Earthquake Detector

Introduction

Context

Earthquakes, as natural disasters, pose a dual threat—potentially resulting in the loss of human lives and causing substantial material damage. The project we have developed centers around monitoring earthquake occurrences, providing users with timely notifications via phone and web interfaces, and archiving records in a database. This comprehensive approach enables users to promptly assess seismic activity at the sensor's location, empowering them to take immediate and informed measures in response to potential earthquakes.

Project Description

The idea of this project is to gather measurements from a MPU-6050 sensor (with an accelerometer and a gyroscope) and to process the data in order to detect the occurrence of an earthquake. When an earthquake is detected, the following things will happen:

  • on a webpage in the LAN the “Earthquake detected” message will be displayed
  • on the connected LCD the “Earthquake detected” message will be displayed
  • a notification will be sent on the user's phone
  • an entry will be added to the InfluxDB project's readings

Architecture

System overview

Hardware

Components

Circuit Diagram

In order to use the Earthquake Detector system, the ESP32 board has to be connected to a power source. The hardware assembly should be positioned in a place free from unwanted moves (e.g. tram or elevator trepidations). Depending on the needs, the user can access the webpage in the LAN that will signalize the occurrence of an earthquake. Anyway, When an earthquake is detected, the user will receive a notification on the phone and will be able to visualize the moment of occurrence in the database.

The Pinout scheme of the ESP32 board that I used is the following:

Real-life view

Software

Setup function

The first step is to initialize the MPU-6050 sensor, the SPIFFS (since the html code is uploaded on the board's SPIFFS), the LCD and to connect the board to Wi-Fi. Also, because we use a notification system and InluxDB, we had to sync time, then we check the InfluxDB server connection. Then, we start the server for HTTP requests and create a handler for web server events.

void setup() {
  Serial.begin(115200);
  initMPU();
  initSPIFFS();
  initWiFi();
  initLCD();
 
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
 
  sensor.addTag("device", "ESP32");
  sensor.addTag("location", "office");
  sensor.addTag("WiFi_Name", WIFI_NAME);
 
  // Accurate time is necessary for certificate validation and writing in batches
  // For the fastest time sync find NTP servers in your area: https://www.pool.ntp.org/zone/
  // Syncing progress and the time will be printed to Serial.
  timeSync(TZ_INFO, ntpServer, "time.nis.gov");
 
  // Check server connection
  if (client.validateConnection()) {
    Serial.print("Connected to InfluxDB: ");
    Serial.println(client.getServerUrl());
  } else {
    Serial.print("InfluxDB connection failed: ");
    Serial.println(client.getLastErrorMessage());
  }
 
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/index.html", "text/html");
  });
 
  server.serveStatic("/", SPIFFS, "/");
 
  // Handle Web Server Events
  events.onConnect([](AsyncEventSourceClient *client){
 
    if(client->lastId()){
      Serial.printf("Client reconnected! Last message ID that it got is: %u\n", client->lastId());
    }
    client->send("{\"message\": \"hello!\"}", NULL, millis(), 10000);
  });
  server.addHandler(&events);
 
  server.begin();
 
}

Loop function

We start the loop function by making the first read of the MPU-6050 sensor's data. This way we get the data against which we will compare the following readings, always storing data from 2 consecutive readings. If the difference between any of the gyroscope or accelerometer's data is over a threshold, then an earthquake has been detected. In this case the following actions will take place:

  • an event will be sent in order to make a POST request to the webpage with the information “Earthquake detected”:
events.send("Earthquake_detected","eqd",millis());
  • a notification will be sent to the user's phone, to warn him about the occurrence of an earthquake:
sendNotification();
  • the LCD screen will display the message “Earthquake detected”:
    lcd.backlight();
    lcd.setCursor(0, 0);
    lcd.print("   Earthquake   ");
    lcd.setCursor(0, 1);
    lcd.print("    detected    "); 
    //...
    delay(5000);
    lcd.clear();
  • add information to the database:
    // Store measured value into point
    sensor.clearFields();
    // Report RSSI of currently connected network
    sensor.addField("event", "Earthquake occured");
    // Print what are we exactly writing
    Serial.print("Writing: ");
    Serial.println(client.pointToLineProtocol(sensor));
    // If no Wifi signal, try to reconnect it
    // Write point
    if (!client.writePoint(sensor)) {
      Serial.print("InfluxDB write failed: ");
      Serial.println(client.getLastErrorMessage());
    }
  • print on the Serial about the occurrence of the earthquake and show the subtraction's results between old and new MPU's readings.

This is the body of loop function:

void loop() {
 
  old_a = a;
  old_g = g;
  old_temp = temp;
 
  mpu.getEvent(&a, &g, &temp);
 
  if((old_a.acceleration.x == 0) && (old_a.acceleration.y == 0) && (old_a.acceleration.z == 0) && 
     (old_g.gyro.x == 0) && (old_g.gyro.y == 0) && (old_g.gyro.z == 0)) {
 
    Serial.println("a.acceleration.x = " + String(a.acceleration.x));
    Serial.println("a.acceleration.y = " + String(a.acceleration.y));
    Serial.println("a.acceleration.z = " + String(a.acceleration.z));
    Serial.println("g.gyro.x = " + String(g.gyro.x));
    Serial.println("g.gyro.y = " + String(g.gyro.y));
    Serial.println("g.gyro.z = " + String(g.gyro.z));
 
    return;
  }
 
  if((abs(g.gyro.x - old_g.gyro.x) >= gyroTh) || 
     (abs(g.gyro.y - old_g.gyro.y) >= gyroTh) ||
     (abs(g.gyro.z - old_g.gyro.z) >= gyroTh) ||
     (abs(a.acceleration.x - old_a.acceleration.x) >= accTh) || 
     (abs(a.acceleration.y - old_a.acceleration.y) >= accTh) || 
     (abs(a.acceleration.z - old_a.acceleration.z) >= accTh)) {
 
 
    events.send("Earthquake_detected","eqd",millis());
    sendNotification();
 
 
    lcd.backlight();
    lcd.setCursor(0, 0);
    lcd.print("   Earthquake   ");
    lcd.setCursor(0, 1);
    lcd.print("    detected    "); 
 
    // Store measured value into point
    sensor.clearFields();
    // Report RSSI of currently connected network
    sensor.addField("event", "Earthquake occured");
    // Print what are we exactly writing
    Serial.print("Writing: ");
    Serial.println(client.pointToLineProtocol(sensor));
    // If no Wifi signal, try to reconnect it
    // Write point
    if (!client.writePoint(sensor)) {
      Serial.print("InfluxDB write failed: ");
      Serial.println(client.getLastErrorMessage());
    }
 
    Serial.println("Earthquake detected.");
    Serial.println("g.gyro.x - old_g.gyro.x = " + String(g.gyro.x - old_g.gyro.x));
    Serial.println("g.gyro.y - old_g.gyro.y = " + String(g.gyro.y - old_g.gyro.y));
    Serial.println("g.gyro.z - old_g.gyro.z = " + String(g.gyro.z - old_g.gyro.z));
 
    Serial.println("a.acceleration.x - old_a.acceleration.x = " + String(a.acceleration.x - old_a.acceleration.x));
    Serial.println("a.acceleration.y - old_a.acceleration.y = " + String(a.acceleration.y - old_a.acceleration.y));
    Serial.println("a.acceleration.z - old_a.acceleration.z = " + String(a.acceleration.z - old_a.acceleration.z));
 
    a.acceleration.x = old_a.acceleration.x;
    a.acceleration.y = old_a.acceleration.y;
    a.acceleration.z = old_a.acceleration.z;
    g.gyro.x = old_g.gyro.x;
    g.gyro.y = old_g.gyro.y;
    g.gyro.z = old_g.gyro.z;
 
    delay(5000);
    lcd.clear();
  } else {
    events.send("No_earthquake_detected","neqd",millis());
    delay(100);
  }
}

sendNotification() function

For sending notifications, we used the Pushover App, which allows you to receive notifications from custom applications. It offers a free 30-day trial period, so it was just enough for developing this project. I created a Pushover account and then a new application. With the generated API Token and user key we were able to send notifications to users' devices.

void sendNotification() {
  // Make HTTPS POST request to send notification
  if (WiFi.status() == WL_CONNECTED) {
    // Create a JSON object with notification details
    // Check the API parameters: https://pushover.net/api
    StaticJsonDocument<512> notification; 
    notification["token"] = apiToken; //required
    notification["user"] = userToken; //required
    notification["message"] = "Earthquake detected at " + getLocalTime(); //required
    notification["title"] = "BAD NEWS"; //optional
    notification["url"] = ""; //optional
    notification["url_title"] = ""; //optional
    notification["html"] = ""; //optional
    notification["priority"] = ""; //optional
    notification["sound"] = ""; //optional
    notification["timestamp"] = ""; //optional
 
    // Serialize the JSON object to a string
    String jsonStringNotification;
    serializeJson(notification, jsonStringNotification);
 
    // Create a WiFiClientSecure object
    WiFiClientSecure client;
    // Set the certificate
    client.setCACert(PUSHOVER_ROOT_CA);
 
    // Create an HTTPClient object
    HTTPClient https;
 
    // Specify the target URL
    https.begin(client, pushoverApiEndpoint);
 
    // Add headers
    https.addHeader("Content-Type", "application/json");
 
    // Send the POST request with the JSON data
    int httpResponseCode = https.POST(jsonStringNotification);
 
    // Check the response
    if (httpResponseCode > 0) {
      Serial.printf("HTTP response code: %d\n", httpResponseCode);
      String response = https.getString();
      Serial.println("Response:");
      Serial.println(response);
    } else {
      Serial.printf("HTTP response code: %d\n", httpResponseCode);
    }
 
    // Close the connection
    https.end();
  }
}

Web app

The times when I was running away from web developing have finally come to an end. With a very simple interface and some JavaScript code, we managed to send the information to the webpage in real time and show a message accordingly to the occurrence of an earthquake.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="icon" href="/favicon.ico" type="image/x-icon">
 
    <title>ESP32 MPU-6050 Earthquake Detection</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            text-align: center;
            margin: 50px;
        }
 
        h1 {
            color: #333;
        }
 
        #status {
            font-size: 24px;
            margin-top: 20px;
        }
 
        .earthquake {
            color: red;
        }
 
        .no-earthquake {
            color: green;
        }
    </style>
</head>
<body>
    <h1>ESP32 MPU-6050 Earthquake Detection</h1>
    <div id="status" class="no-earthquake">No earthquake detected</div>
 
    <script>
        if (!!window.EventSource) {
         var source = new EventSource('/events');
 
         source.addEventListener('open', function(e) {
          console.log("Events Connected");
         }, false);
         source.addEventListener('error', function(e) {
          if (e.target.readyState != EventSource.OPEN) {
            console.log("Events Disconnected");
          }
         }, false);
 
         source.addEventListener('message', function(e) {
          console.log("message", e.data);
         }, false);
 
         source.addEventListener('eqd', function(e) {
          console.log("eqd", e.data);
          document.getElementById('status').innerText = 'Earthquake detected';
          document.getElementById('status').classList.add('earthquake');
          document.getElementById('status').classList.remove('no-earthquake');
         }, false);
 
         source.addEventListener('neqd', function(e) {
          console.log("neqd", e.data);
          document.getElementById('status').innerText = 'No earthquake detected';
          document.getElementById('status').classList.remove('earthquake');
          document.getElementById('status').classList.add('no-earthquake');
         }, false);
        }
        </script>
 
</body>
</html>

Results

Web interface

When no earthquake is detected, this is how the webpage looks like:

When an earthquake is detected, this is how the webpage looks like:

Notifications

In the notification bar, the notification about the occurrence of an earthquake will look like this:

In the Pushover App, the notification history will be stored like this:

InfluxDB

This is how are stored the events in InfluxDB:

Conclusion and Future Work

This project can accomplish its purpose if there are some others conditions met. For example, the board with the sensor must be placed in a stable place, free from unwanted moves (e.g. tram or elevator trepidations). Otherwise, it is possible to return false positive results. However, the algorithm used in earthquake detection is a simplistic one and is also prone to wrong results.

I tried to add MQTT to this project, in order to make the results more accessible for a large number of clients. I will keep this in mind as a future development for this project. Also, I would like to improve the detection algorithm, so that it will take into account readings from the IMU sensor over a larger amount of time (2 seconds, so we can compare the mean of these readings with a threshold).

References

iothings/proiecte/2023/earthquakedetector.txt · Last modified: 2024/01/16 00:07 by stefania.frunza
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0