Was that an earthquake, or did I just stand up too fast? If you’ve ever asked that question — then this project’s for you.
We’re building an ESP32 Earthquake Monitor that fetches live data from the USGS Earthquake API, locates your position via NEO-6M GPS, and alerts you through an LCD, buzzer, and RGB LED. It even logs quake data to an SD card and hosts a mini web dashboard for quake history — because if you’re going to feel the earth move, you might as well have the data to prove it.
Project Overview
This project combines IoT, geolocation, and real-time data processing in one neat ESP32 package.
Here’s what happens behind the scenes:
- The ESP32 connects to Wi-Fi and syncs its clock with an NTP (Network Time Protocol) server.
- Using the current date, it requests the day’s earthquakes in a given location via the USGS Earthquake API.
- The NEO-6M GPS module provides your current coordinates.
- The ESP32 computes which earthquakes are closest to you.
- If a quake is detected within ~200 km:
- All quake data is logged to the SD card, and you can view it later through the ESP32’s built-in web page.
Components Needed
| Component | Description |
|---|---|
| ESP32 Dev Board | Main microcontroller with Wi-Fi |
| NEO-6M GPS Module | Provides latitude & longitude |
| 16x2 I²C LCD | Displays quake status |
| Active Buzzer | Alarm sound for nearby quakes |
| RGB LED | Visual indicator (green = calm, red = alert) |
| MicroSD Module | Stores quake history |
| Breadboard + wires | For connections |
Fetching Data from the USGS Earthquake API
We’ll use this endpoint to get the latest quakes in the Philippines (my country):
https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson &starttime=2025-10-17 &endtime=2025-10-18 &minmagnitude=4.5 &minlatitude=5.0 &maxlatitude=20.0 &minlongitude=115.0 &maxlongitude=130.0 &orderby=time
But instead of fixed dates, we’ll dynamically replace starttime and endtime each day using the ESP32’s NTP-synced clock.
That means the device automatically fetches today’s earthquakes every 24 hours — no manual editing required.
GPS and Connections
| Module | ESP32 Pin | Notes |
|---|---|---|
| NEO-6M TX | GPIO16 (RX2) | GPS data to ESP32 |
| NEO-6M RX | GPIO17 (TX2) | Optional |
| LCD SDA | GPIO21 | I²C |
| LCD SCL | GPIO22 | I²C |
| Buzzer + | GPIO25 | Output |
| RGB LED R | GPIO26 | Red pin |
| RGB LED G | GPIO27 | Green pin |
| SD CS | GPIO5 | SD card chip select |
| SD MOSI | GPIO23 | SPI data (ESP32 → SD) |
| SD MISO | GPIO19 | SPI data (SD → ESP32) |
| SD SCK | GPIO18 | SPI clock |
Code Overview
Here’s the project flow summarized:
- Connect to Wi-Fi and get time from NTP.
- Format today’s date into YYYY-MM-DD.
- Construct the API URL dynamically.
- Fetch and parse the GeoJSON data.
- Get your GPS location (latitude & longitude).
- Compute the distance between your location and each quake’s epicenter using the Haversine formula.
- Update the LCD, RGB LED, and buzzer accordingly.
- Log quake data to the SD card.
- Host a simple web page displaying recent logs.
Example Code Snippet (Simplified)
#include <WiFi.h> #include <HTTPClient.h> #include <ArduinoJson.h> #include <Wire.h> #include <LiquidCrystal_I2C.h> #include <TinyGPSPlus.h> #include <HardwareSerial.h> #include <SD.h> #include <NTPClient.h> #include <WiFiUdp.h> #define BUZZER_PIN 25 #define LED_R 26 #define LED_G 27 #define SD_CS 5 LiquidCrystal_I2C lcd(0x27, 16, 2); HardwareSerial gpsSerial(2); TinyGPSPlus gps; WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, "pool.ntp.org", 28800); // +8 GMT (Philippines) const char* ssid = "YOUR_WIFI_SSID"; const char* password = "YOUR_WIFI_PASSWORD"; void setup() { Serial.begin(115200); lcd.init(); lcd.backlight(); pinMode(BUZZER_PIN, OUTPUT); pinMode(LED_R, OUTPUT); pinMode(LED_G, OUTPUT); gpsSerial.begin(9600, SERIAL_8N1, 16, 17); WiFi.begin(ssid, password); lcd.print("Connecting WiFi..."); while (WiFi.status() != WL_CONNECTED) delay(500); timeClient.begin(); while(!timeClient.update()) timeClient.forceUpdate(); if(!SD.begin(SD_CS)) lcd.print("SD Fail!"); else lcd.print("SD Ready"); delay(2000); lcd.clear(); fetchEarthquakes(); } void fetchEarthquakes() { // Get today's date time_t epoch = timeClient.getEpochTime(); struct tm *tm_info = localtime(&epoch); char date_str[11]; strftime(date_str, 11, "%Y-%m-%d", tm_info); String url = "https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson"; url += "&starttime=" + String(date_str); url += "&endtime=" + String(date_str); url += "&minmagnitude=4.5&minlatitude=5.0&maxlatitude=20.0&minlongitude=115.0&maxlongitude=130.0&orderby=time"; HTTPClient http; http.begin(url); int code = http.GET(); if(code == 200) { DynamicJsonDocument doc(16384); deserializeJson(doc, http.getString()); for(JsonObject f : doc["features"].as<JsonArray>()) { double lon = f["geometry"]["coordinates"][0]; double lat = f["geometry"]["coordinates"][1]; double mag = f["properties"]["mag"]; const char* place = f["properties"]["place"]; double myLat = gps.location.lat(); double myLon = gps.location.lng(); double dist = TinyGPSPlus::distanceBetween(myLat, myLon, lat, lon) / 1000.0; logToSD(place, mag, dist); if(dist < 200) alertUser(place, mag); } } http.end(); } void alertUser(const char* place, double mag) { lcd.clear(); lcd.print("ALERT!"); lcd.setCursor(0,1); lcd.print(place); digitalWrite(LED_R, HIGH); digitalWrite(LED_G, LOW); tone(BUZZER_PIN, 1000, 2000); delay(5000); noTone(BUZZER_PIN); digitalWrite(LED_R, LOW); digitalWrite(LED_G, HIGH); } void logToSD(const char* place, double mag, double dist) { File f = SD.open("/quakes.csv", FILE_APPEND); if(f) { f.printf("%s,%.1f,%.1f km\n", place, mag, dist); f.close(); } }
Adding a Web Dashboard
With the ESP32’s Wi-Fi capability, it’s easy to serve quake logs over a local webpage.
The idea:
- The ESP32 starts a web server.
- When a user visits the ESP32’s IP address (e.g., http://192.168.1.25), the page displays the contents of quakes.csv from the SD card.
- The page auto-refreshes every 60 seconds, so you always see the latest events.
#include <WebServer.h> WebServer server(80); void handleRoot() { if (!SD.exists("/quakes.csv")) { server.send(200, "text/html", "<h3>No quake data logged yet.</h3>"); return; } File file = SD.open("/quakes.csv"); if (!file) { server.send(500, "text/html", "Error opening quake log."); return; } String html = "<!DOCTYPE html><html><head><meta charset='UTF-8'>"; html += "<meta http-equiv='refresh' content='60'>"; html += "<title>ESP32 Earthquake Monitor</title>"; html += "<style>body{font-family:Arial;background:#f8f9fa;color:#333;text-align:center;}"; html += "table{margin:auto;border-collapse:collapse;width:80%;}"; html += "th,td{border:1px solid #aaa;padding:8px;}th{background:#004b87;color:white;}</style></head><body>"; html += "<h1>ESP32 Earthquake Log</h1><table><tr><th>Location</th><th>Magnitude</th><th>Distance (km)</th></tr>"; while (file.available()) { String line = file.readStringUntil('\n'); int firstComma = line.indexOf(','); int secondComma = line.indexOf(',', firstComma + 1); if (firstComma > 0 && secondComma > 0) { String place = line.substring(0, firstComma); String mag = line.substring(firstComma + 1, secondComma); String dist = line.substring(secondComma + 1); html += "<tr><td>" + place + "<td><td>" + mag + "</td><td>" + dist + "</td></tr>"; } } file.close(); html += "</table><p><em>Auto-refreshes every 60 seconds</em></p><body></html>"; server.send(200, "text/html", html); } void startWebServer() { server.on("/", handleRoot); server.begin(); Serial.println("Web server started. Access via http://" + WiFi.localIP().toString()); } void loop() { gpsSerial.listen(); while (gpsSerial.available()) gps.encode(gpsSerial.read()); server.handleClient(); }
How it works
- WebServer server(80); creates a basic web server on port 80.
- The handleRoot() function reads quakes.csv and formats it into an HTML table.
- The meta refresh tag reloads the page every minute automatically.
- The startWebServer() function launches the web interface after Wi-Fi connects and SD initializes.
To start the dashboard, just call this inside setup() after SD initialization:
When you connect to your ESP32’s IP (check Serial Monitor), you’ll see a simple HTML table listing quake records stored on the SD card:
| Date | Location | Magnitude | Distance |
|---|---|---|---|
| 2025-10-18 | Davao Region | 5.7 | 120 km |
| 2025-10-18 | Mindoro Strait | 4.9 | 450 km |
The full code can be downloaded here: github repo
Notes
- You can open the page from any device on the same Wi-Fi network (phone, laptop, tablet).
- To clear old logs, simply remove or format the SD card.
- You can expand this with JavaScript charting (like Chart.js) for visual magnitude trends later.
Visual Indicators
- RGB LED Green — No nearby quakes.
- RGB LED Red + Buzzer — Earthquake within ~200 km radius.
You can tweak the threshold or add yellow for “moderate distance.”
Bonus Improvements
- Add a real-time clock (RTC) backup if NTP isn’t reachable.
- Include magnitude filtering (e.g., only warn for M≥5.0).
- Replace the buzzer with a relay and a siren for stronger sound alerts
- Create a map visualization using Google Maps API for stored quakes.
Final Thoughts
With the NTP-based auto date update, GPS-based distance calculation, SD card logging, RGB alert LED, and now this web dashboard, your ESP32 Earthquake Monitor is a full-fledged, connected device worthy of a spot on your desk.
It not only helps answer “Was that an earthquake?” —it lets you prove it with data, logs, and a neat little web interface.





