Cart 0

Bat Listener, Arduino and display!

Posted by Matthew Little on

Following on from our previous post on connecting the Bat Listener kit to and Arduino, here we show you how to add a small LCD screen to display frequency data and a 'sonograph'.

Hopefully you have the Bat listener connected to the Arduino and are reading data onto the serial port. The next step is to add a small display so we can have a portable display of the most interesting data.

What is a 'sonograph'?

A sonograph (sometimes called a 'sonogram') is a graph of frequency against time. Displaying the frequency measured by the Bat Listener against time allows us to see the pulses generated.

Each bat species has a characteristic sonogram which, along with other information, can be used to help identify the various bat species.

Here is an example bat sonogram:

Here are some interesting links to more detailed information:

Build your own

Parts required

Connecting the LCD

The connections for the LCD are as follows:

Pin Name LCD Pin Arduino Pin
A0  D9
Chip Select CS D10
SPI Data Out SDA D11
SPI Reset RESET +3.3V
SPI Clock SCLK D13
Backlight Power LED +3.3V
LCD Power Vcc +5V


The connections for the Bat Listener are:

Pin Name Bat Listener Pin Arduino Pin
Digital pulse output uP O/P 1  D2
Power +9V Battery + Vin


I wired these first using breadboard: 

Then I soldered these onto a small piece of strip-board to make a more permanent unit:


Display the frequency

To measure the frequency I used the same procedure as in part one of this system.

We care counting pulses using an interrupt on pin D2.

This time we set up two counters: one is for the 1Hz frequency measurements (displayed as numbers on the screen), the other is for the graph display.

The graph display counter is checked every 10mS and the results are plotted on a small graph. The graph is big enough to show just over 1 second of data (1.17 seconds to be precise).

We are measuring a few interested parameters such as:

  • Average frequency over 1 second
  • Maximum frequency seen in the 1 second sample period
  • Average non-zero frequency - this is the average frequency when the unit is detecting anything. Any zero frequency measurements are not used in this calculation

Please check the arduino code for the calculations performed. When checked with an adjustable frequency meter this seemed to measure accurately.

Here is the graph in action, showing a 40kHz test frequency:

Photos of the unit in action

The pulses in these photos were produced by squishing my fingers together in front of the sensor. I'll test this unit with real bats when they wake up in the Spring!


This project has a number of limitations including:

  • Speed of updating to the LCD reduces the resolution of the device
  • Resolution of the display means we do not get very fine data resolution
  • Frequency range of the detector - this is limited to around 60kHz and means that we cannot accurately detect frequencies higher than this
  • Recording the display - at present the display is constantly written to. A future update could be to only display the image when an ultrasound is detected.

This project is meant as inspiration only and is not suitable for accurate bat species identification.

Arduino Code

Here is the code I used for both the Arduino Uno and Nano versions.

You will need to download and install both:

Here is the code I wrote to do the display. Please feel free to use and update as required. Your mileage may vary...

  /****** Bat Listener Frequency Measurement **************
  /****** by Matt Little **********************************
  /****** Date: 10/01/2017 ********************************
  /****** *********************
  /****** *************************

  See for information and construction details

  /*************Details of Code*****************************

  This code has been written to measure the frequency from the bat listener.
  Bat listener is available from:

  The O/P Terminals are connected to GND and D2 of an Arduino Uno.
  D2 is also the interrupt pin.
  Each time the pulse arrives then a counter is incremented
  Every 1 second this is displayed on the serial output and converted into a frequency.
  Pulses per second are converted to the actual frequency, as there is a divide by 16 in the circuit.

  A 1.44" SPI LCD has been added to display the information.
  Details of the display are here:
  The pin connections are:

  10/01/2017 Code written - Matt Little
  10/01/2017 Added LCD display - Matt Little
  11/1/2017 Added pixel display for freq - Matt Little

  Libraries used:
  Adafruit TFT GFX library
  Guide for using the Library is here:


#include <SPI.h>
#include <Adafruit_GFX.h>
#include <TFT_ILI9163C.h> // Color definitions #define BLACK 0x0000 #define BLUE 0x001F #define RED 0xF800 #define GREEN 0x07E0 #define CYAN 0x07FF #define MAGENTA 0xF81F #define YELLOW 0xFFE0 #define WHITE 0xFFFF /* Teensy3.x and Arduino's You are using 4 wire SPI here, so: MOSI: 11//Teensy3.x/Arduino UNO (for MEGA/DUE refere to arduino site) MISO: 12//Teensy3.x/Arduino UNO (for MEGA/DUE refere to arduino site) SCK: 13//Teensy3.x/Arduino UNO (for MEGA/DUE refere to arduino site) the rest of pin below: */ #define __CS 10 #define __DC 9 /* Teensy 3.x can use: 2,6,9,10,15,20,21,22,23 Arduino's 8 bit: any DUE: check arduino site If you do not use reset, tie it to +3V3 */ TFT_ILI9163C tft = TFT_ILI9163C(__CS, __DC); // Variables for the Pulse Counter int batinterrupt = 0; // Pulse Counter Interrupt - This is pin D2 of arduino - which is INT1 long int pulseCounter = 0; // Holds the number of pulses float previousfrequency; // Holds the oprevious pulse counter for blanking on LCD float frequency = 0; float maxFrequency = 0; // Holds the max frequency value float previousMaxFrequency = 0; // Holds the max frequency value float aveNonZeroFreq = 0; // Holds the average non-xero freq float previousAveNonZeroFreq = 0; // Holds the average non-xero freq int aveNonZeroIndex = 0; // Holds the number of samples in the nonZeroFreq value (for averaging) long int previousMillis = 0; // Holds the previous millis value for timing int sampleTime = 1000; // This is the sample period in milliseconds long int graphCounter = 0; // Holds the number of pulses float frequencyGraph = 0; float previousFrequencyGraph = 0; long int previousMillisGraph = 0; // Holds the previous millis value for graphing int sampleTimeGraph = 10; // This is the sample period in milliseconds for graphing int graphIndex = 0; //****************INITIALISE ROUTINE****************************** void setup() { Serial.begin(115200); // Set up a serial output for data display and changing parameters attachInterrupt(batinterrupt, pulse, RISING); // This sets up our Interrupt Service Routine (ISR) for flow tft.begin(); tft.setRotation(0); // Set up the display tft.setTextColor(GREEN); tft.setTextSize(0); tft.setCursor(95, 9); tft.println("kHz"); tft.setTextColor(GREEN); tft.setTextSize(0); tft.setCursor(0, 0); tft.println("AVE:"); tft.setTextColor(RED); tft.setTextSize(0); tft.setCursor(0, 20); tft.println("MAX:"); tft.setTextColor(BLUE); tft.setTextSize(0); tft.setCursor(64, 20); tft.println("NZA:"); // Initialise the graph display tft.drawFastHLine(10, 118, 118, YELLOW); tft.drawFastVLine(10, 38, 85, YELLOW); // Add x axis segements tft.drawFastVLine(30, 118, 5, YELLOW); tft.drawFastVLine(50, 118, 5, YELLOW); tft.drawFastVLine(70, 118, 5, YELLOW); tft.drawFastVLine(90, 118, 5, YELLOW); tft.drawFastVLine(110, 118, 5, YELLOW); // Add y axis segements tft.drawFastHLine(5, 118, 5, YELLOW); tft.drawFastHLine(5, 108, 5, YELLOW); tft.drawFastHLine(5, 98, 5, YELLOW); tft.drawFastHLine(5, 88, 5, YELLOW); tft.drawFastHLine(5, 78, 5, YELLOW); tft.drawFastHLine(5, 68, 5, YELLOW); tft.drawFastHLine(5, 58, 5, YELLOW); tft.drawFastHLine(5, 48, 5, YELLOW); tft.drawFastHLine(5, 38, 5, YELLOW); tft.setTextColor(WHITE); tft.setTextSize(0); tft.setCursor(2, 70); tft.println("f"); tft.setCursor(60, 120); tft.println("t"); } // //*********** The flow interrupt************** void pulse() { // Increment the pulse counter pulseCounter++; graphCounter++; } void loop() { if (millis() >= (previousMillis + sampleTime)) { previousMillis = millis(); // The parameters we are interested in are: // Average Frequency // Max Frequency // Average non-zero frequency // AVERAGE FREQUENCY //frequency = ((pulsecounter*16)*(1000/sampleTime))/1000; // This is the frequency in kHz frequency = (((float)pulseCounter * 16.0) / (float)sampleTime); // This is the frequency in kHz (Quicker algorithm) if(aveNonZeroIndex>0) { aveNonZeroFreq = aveNonZeroFreq/(float)aveNonZeroIndex; } else { aveNonZeroFreq = 0; } updateFreq(); // Disply the frequency on the LCD previousMaxFrequency = maxFrequency; maxFrequency = 0; // Reset the max freq for next reading previousAveNonZeroFreq = aveNonZeroFreq; aveNonZeroFreq = 0; // Reset the freq for next reading aveNonZeroIndex = 0; // Reset index for next reading previousfrequency = frequency; // Output Freq to serial port for monitoring (if required) Serial.println(frequency); pulseCounter = 0; // Reset the pulse counter resetGraphFreq(); } if (millis() >= (previousMillisGraph + sampleTimeGraph)) { updateGraph(); } } void updateFreq() { // The parameters we are interested in are: // Average Frequency // Max Frequency // Average non-zero frequency // AVERAGE FREQUENCY // This subroutine displays the average frequency on the LCD display // Display the frequency on the LCD tft.setCursor(30, 0); tft.setTextColor(BLACK); tft.setTextSize(2); tft.println(previousfrequency); tft.setCursor(30, 0); tft.setTextColor(GREEN); tft.setTextSize(2); tft.println(frequency); // MAX FREQUENCY // This subroutine displays the average frequency on the LCD display // Display the frequency on the LCD tft.setCursor(30, 20); tft.setTextColor(BLACK); tft.setTextSize(0); tft.println(previousMaxFrequency); tft.setCursor(30, 20); tft.setTextColor(RED); tft.setTextSize(0); tft.println(maxFrequency); // AVERAGE NON-ZERO FREQUENCY // This subroutine displays the average frequency on the LCD display // Display the frequency on the LCD tft.setCursor(94, 20); tft.setTextColor(BLACK); tft.setTextSize(0); tft.println(previousAveNonZeroFreq); tft.setCursor(94, 20); tft.setTextColor(BLUE); tft.setTextSize(0); tft.println(aveNonZeroFreq); } void updateGraph() { // This subroutine displays a graph of the frequency against time previousMillisGraph = millis(); frequencyGraph = (((float)graphCounter * 16.0) / (float)sampleTimeGraph); // This is the frequency in kHz // // PIXEL PLOT // // Here we want to plot a pixel to indicate the frequency // tft.drawPixel((10+graphIndex),(117-frequencyGraph),BLUE); // LINE PLOT // We plot a line from the prefvious freqneucy value to the new frequency value // A line is drawn from x0,y0 to x1,y1 graphIndex++; tft.drawLine((10 + graphIndex), (117 - previousFrequencyGraph), (11 + graphIndex), (117 - frequencyGraph), BLUE); // Here we calculate the max and the non-zero average // MAX FREQUENCY if (frequencyGraph > maxFrequency) { maxFrequency = frequencyGraph; } // AVERAGE NON-ZERO FREQUENCY if(frequencyGraph>0.00) { aveNonZeroFreq = aveNonZeroFreq+frequencyGraph; aveNonZeroIndex++; // Increment the number of points } previousFrequencyGraph = frequencyGraph; // Store the previous graph freq graphCounter = 0; // Reset the pulse counter // We can show over 1 second of data, actually around 1.17 seconds if (graphIndex >= 117) { // Blank graph screen and reset graph index // fillRect(uint16_t x0, uint16_t y0, uint16_t w, uint16_t h, uint16_t color); tft.fillRect(11, 30, 117, 88, BLACK); graphIndex = 0; resetGraphFreq(); } } void resetGraphFreq() { // This subroutine resets the graphing parameters to remove timing errors that occur from slow writes to LCD // The time taken for resetting the graph causes an error with the display (more pulses come in) // So here we reset the graph gounter and graph time to adjust for this graphCounter = 0; // Reset the pulse counter previousMillisGraph = millis(); // Resets the update }



Share this post

← Older Post Newer Post →

Leave a comment

Please note, comments must be approved before they are published.