The first version of the seismometer is based on the micro controller ESP32. The ADC is a ADS1115 analog-to-digital converter with a resolution of 16 bit. The geophon is a SM-24 geophon from Sensor Nederland B.V..


The ESP32 is a 32 bit micro controller loaded with a lot of utilities. The version used here is a board based on the SX1276 LoRa chip produced by Heltec Automation. It contains WiFi (802.11 b/g/n), Bluetooth and LoRa (2.6 km distance range) communication, onboard 32 MByte Flash, WLAN antenna, blue OLED display, CP2102 USB/serial chip and some sensors.
Price for ESP32 SX1276 LoRa version with OLED is around 24 Euro (AMAZON).


The ADS1115 from Texas Instruments is a 16 bit, 4 channel analog-to-digital converter. Here an Arduino shield version is used, which is easy to connect and to program.


The SM-24 is a low-distortion geophone, which offers very high performance in seismic exploration with an extended bandwidth from 10 Hz up to 240 Hz, allowing full bandwidth at 2 ms sampling, and with one of the lowest lifecycle cost of ownership in the industry, installed base of over 14 million worldwide.

Termination of SM-24

For the connection between the SM-24 and the ADS1115 the following resistor circuit is used. The 1kΩ resistor in the middle is a calibration resistor according to its data sheet in order to flatten the response curve. The other two 1kΩ resistors, leading to the ADS1115, are for current limiting, in case that the output voltage is larger than the maximum input voltage of the ADC. Even if the output voltage is 10V, which is much higher than the usual output of several mV, the current would be limited to 5mA, which doesn’t destroy the electrostatic discharge diodes (ESD) of the ADS111x analog inputs.

Is the ESP32 the best solution?

In principle, one could develop the seismometer with the ESP32 as the main control unit. There are mainly three reasons, why in the next approach a Raspberry Pi was used, and the ESP32 was no longer used:


Neither the Free Pascal development team (fpc) nor Embarcadero (Delphi) offers a PASCAL compiler for the ESP32 system. Since the main routines for interrupt programming, SQL handling, image processing for live data and spectral analysis should be handled in PASCAL, the absence of a PASCAL compiler or a PASCAL IDE is hampering the project.

No SQL server

The is no SQL server, especially no PostgreSQL available for the ESP32 system. For this project a data base, which contains the data for one month, is targeted. If the geophone is sampled with 500 Hz (2 ms sampling) this makes around 43 million records per day or around 1.5 billion records a month. It makes no sense to save such an amount of data in an ASCII, HTML or XML file, as we want to be able to abstract data for a given range quickly. So an SQL server is a reasonable solution.

No SSD support

The SQL database for one month of data has a size of around 120 GB, and the SQL server writes every second data in the database. So an SD card would be to small, slow and permanent writing is harming the card. A good solution is a 256 GB SSD, which is fast, needs very few power, has no rotating parts (which would disturb the measurements of the geophone) and is big enough. Unfortunatly the ESP32 system has no support for an SSD.

If you still would like to test the ESP32 system by yourself, you need to install the ARDUINO IDE for the ESP32, here you will find an instruction. After installing you can simply use the following  sketch (sketch = Arduino name for a program):

#include "WiFi.h"

const char* NETWORK_SSID        = "SEISMOMETER_WIFI";
const char* NETWORK_PASSWORD    = "test123";
const char* NTP_ADRESS          = "";
const char* NTP_TIMEZONE        = "CET-1CEST,M3.5.0/2,M10.5.0/3";
const int   OLED_SCL            = 15;
const int   OLED_SDA            = 4;
const int   OLED_RST            = 16;

U8X8_SSD1306_128X64_NONAME_SW_I2C   display_SSD1306(OLED_SCL, OLED_SDA, OLED_RST);

// ADS1115
Adafruit_ADS1115                    adc_ADS1115;
const float ADS1115_VOLTAGE_FACTOR  = 125.0 / 1000 / 1000;

// SM24
const float SM24_V_M_S_FACTOR       = 28.8;

time_t                              current_time;
struct timeval                      time_info;
struct tm                           local_time;
struct tm*                          p_local_time;
char                                buffer [64];
int                                 previous_second;
int                                 frame_counter;
int                                 fps;

void setup()
  int         i;

  // Init display
  display_SSD1306.begin ();
  display_SSD1306.setFont (u8x8_font_chroma48medium8_r);
  display_SSD1306.clear ();
  display_SSD1306.setCursor (0, 0);

  // Init WiFi
  WiFi.mode (WIFI_STA);
  display_SSD1306.print ("Starting wifi\n");

  // Connect WiFi
  i = 0;
  while (WiFi.status () != WL_CONNECTED)
    delay (500);
    display_SSD1306.setCursor (0, 1);
    if ((i % 2) == 0)
      display_SSD1306.print("/ ");
      display_SSD1306.print("\\ ");
    i = i + 1;
  display_SSD1306.print("WiFi connected\n");

  //Init ntp time
  display_SSD1306.print ("Getting time from\n");
  display_SSD1306.print (NTP_ADRESS);
  display_SSD1306.print ("\n");
  display_SSD1306.print ("NTP ready.\n");

  if (!getLocalTime (&local_time))
    display_SSD1306.print ("Failed to obtain time\n");
    display_SSD1306.print (asctime (&local_time));
    display_SSD1306.print ("\n");

    display_SSD1306.clear ();

   adc_ADS1115.begin ();
   adc_ADS1115.setGain (GAIN_ONE);

void loop()
  int   adc_Value;
  float voltage;
  float m_s;

  gettimeofday (&time_info, NULL);
  current_time    = time_info.tv_sec;
  p_local_time    = localtime (&current_time);

  if (p_local_time->tm_sec > previous_second)
    previous_second = p_local_time->tm_sec;
    fps             = frame_counter;
    frame_counter   = 0;
  frame_counter = frame_counter + 1;

//  snprintf (buffer, sizeof (buffer), "%d" ":" "%02d" ":" "%02d" "." "%03d" "\n" "%d fps  ", p_local_time->tm_hour, p_local_time->tm_min, p_local_time->tm_sec, time_info.tv_usec / 1000, fps);
  snprintf (buffer, sizeof (buffer), "Seismometer");
  display_SSD1306.setCursor (0, 0);
  display_SSD1306.print (buffer);

  adc_Value = adc_ADS1115.readADC_Differential_0_1 ();
  if (adc_Value < 0)
    adc_Value = -adc_Value;

  snprintf (buffer, sizeof (buffer), "ADC: %d    ", adc_Value);
//  display_SSD1306.setCursor (0, 4);
//  display_SSD1306.print (buffer);

  voltage = adc_Value * ADS1115_VOLTAGE_FACTOR;
  snprintf (buffer, sizeof (buffer), "V  : %7.4f ", voltage);
  display_SSD1306.setCursor (0, 5);
  display_SSD1306.print (buffer);

  m_s = voltage * SM24_V_M_S_FACTOR;
  snprintf (buffer, sizeof (buffer), "m/s: %7.4f ", m_s);
  display_SSD1306.setCursor (0, 6);
  display_SSD1306.print (buffer);

  delay (500);