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.
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:
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:
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(); }
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:
events.send("Earthquake_detected","eqd",millis());
sendNotification();
lcd.backlight(); lcd.setCursor(0, 0); lcd.print(" Earthquake "); lcd.setCursor(0, 1); lcd.print(" detected "); //... delay(5000); lcd.clear();
// 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()); }
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); } }
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(); } }
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>
When no earthquake is detected, this is how the webpage looks like:
When an earthquake is detected, this is how the webpage looks like:
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:
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).
Hardware components documentation and datasheets
ESP32 I2C Communication: Set Pins, Multiple Bus Interfaces and Peripherals
ESP32 with MPU-6050 Accelerometer, Gyroscope and Temperature Sensor
How to Use I2C LCD with ESP32 on Arduino IDE
ESP32 Web Server using Server-Sent Events (Update Sensor Readings Automatically)
ESP32: Send Pushover Notifications (Arduino IDE)
ESP32 NTP Client-Server: Get Date and Time