Full-Stack IoT Architecture for Real-Time Earthquake Detection
📚 Technical Specification: A comprehensive architecture whitepaper is available in the
docs/directory, compiled via Typst. For a browsable, topic-by-topic reference, check out the project Wiki.
QuakeGuard is a full-stack IoT architecture for real-time detection, analysis, and reporting of seismic events. The system transforms everyday household appliances — washing machines, TVs, refrigerators — into a distributed seismic sensor network, each node capable of detecting and reporting earthquake activity autonomously.
Intelligent edge sensors (ESP32-C3 + ADXL345) analyze vibrations locally using professional-grade algorithms and transmit cryptographically signed data to an asynchronous cloud backend. The backend is engineered to handle the massive traffic spikes — the Thundering Herd effect — typical during widespread seismic events, ensuring reliable alarm delivery without bottlenecking. A React Native mobile app receives real-time haptic and visual alerts via WebSocket.
ESP32-C3 + ADXL345
│
│ MQTT (signed payload)
▼
MQTT Bridge ──► POST /readings/ ──► ECDSA Verification
│
▼
Redis Queue
│
▼
Background Worker ────►┌───────────┐
│ │ (Queue) │ AI Worker │
▼ ▼ │ (Ollama) │
PostgreSQL Redis Pub/Sub ◄─└─────┬─────┘
+ PostGIS │
▼
WebSocket Broadcast
│
▼
React Native Mobile App
(Haptic + Visual Alert)
The project follows Microservices and Event-Driven Design principles across three fully independent layers.
| Feature | Detail |
|---|---|
| Hardware | ESP32-C3 SuperMini + ADXL345 Accelerometer |
| Sampling Rate | 100 Hz |
| Detection Algorithm | STA/LTA (Short Term / Long Term Average) |
| Signal Processing | Digital High-Pass Filter (HPF) to remove gravity |
| Data Structure | Statically allocated Ring Buffers (RingBuffer<100> STA, RingBuffer<1000> LTA) |
| Security | ECDSA NIST256p cryptographic signing on every payload |
| Transmission | MQTT publish to quakeguard/telemetry |
| Provisioning | Automated device handshake on first boot via POST /devices/register |
| Secret Injection | Compile-time ENROLLMENT_TOKEN via PlatformIO pre-script + #error fail-fast |
| Feature | Detail |
|---|---|
| Framework | FastAPI (Python 3.11), fully async |
| Security | API Key auth, ECDSA signature verification, Anti-Replay (60s window) |
| Message Broker | Redis — decouples ingestion from processing |
| Rate Limiting | Fixed-window 50 req/s per IP via Redis |
| Alert Engine | ML-like magnitude proxy (M = log10(PGA_calib) + b), threshold M ≥ 4.5 |
| Deduplication | Redis TTL cooldown lock per zone (60s), prevents alert storms |
| Persistence | PostgreSQL + PostGIS with recorded_at timestamps |
| Zone Assignment | Automatic via PostGIS ST_Contains spatial query, ordered by ST_Area ascending |
| Zone Seeding | 8 pre-populated global macro-regions + "Unknown Region" fallback |
| Observability | GET /health — concurrent PostgreSQL + Redis ping |
| Secrets | Fail-fast RuntimeError on missing env vars at startup |
| MQTT Bridge | mqtt_subscriber.py — forwards MQTT payloads to the secure HTTP pipeline |
| Edge AI | Asynchronous emergency report generation via local Ollama (Llama 3.2) |
| Feature | Detail |
|---|---|
| Framework | React Native (Expo) with TypeScript |
| Navigation | Expo Router — 3-tab Bottom Navigator (Monitor, Sensors Map, Settings) |
| State Management | Zustand slices (usePreferencesStore, useAlertStore) |
| Server State | TanStack Query + Axios — caching, background refetch, retry |
| Real-Time | WebSocket context with exponential backoff reconnection |
| Alert Delivery | SOS haptic vibration pattern + OS push notification via expo-notifications |
| Alert History | In-session feed of last 10 critical events |
| AI Report Banner | Inline AI-generated emergency report (summary + recommendations) for the latest alert, with "Report unavailable" badge on failures |
| Offline Mode | Toggle silences WebSocket, halts all TanStack Query polling |
| Notifications | notificationsEnabled toggle gates haptics and push notifications |
| Safe Areas | react-native-safe-area-context — Dynamic Island and punch-hole compatible |
Data integrity is paramount in an emergency system. Every telemetry packet is cryptographically secured end-to-end.
ESP32 signs payload with ECDSA NIST256p (SHA256)
↓
Backend verifies signature against registered public key
↓
Timestamp validated within 60-second window (Anti-Replay)
↓
API Key checked on every request (X-API-Key header)
↓
Payload accepted → Redis Queue
Threat model coverage:
- ✅ Man-in-the-Middle (MitM) — ECDSA signature verification
- ✅ Spoofing — public key registration + signature check
- ✅ Replay attacks — 60-second timestamp window
- ✅ Brute force — rate limiting 50 req/s per IP
- ✅ Unauthorized access — API Key + enrollment token fail-fast
- Docker Desktop & Docker Compose
- PlatformIO (VS Code Extension)
- Node.js 18+ & Expo Go (mobile)
- A mobile hotspot or shared WiFi network for ESP32 + backend connectivity
cd backend
cp .env.example .envEdit .env and set the required secrets:
# --- Application Secrets ---
IOT_API_KEY=your_secret_key
MOBILE_WS_TOKEN=your_ws_token
ENROLLMENT_TOKEN=your_enrollment_token
# --- Database ---
POSTGRES_DB=quakeguard_db
POSTGRES_USER=developer
POSTGRES_PASSWORD=your_db_password
API_PORT=8000
# --- Cloud MQTT (HiveMQ) ---
MQTT_BROKER=your-cluster-id.s1.eu.hivemq.cloud
MQTT_PORT=8883
MQTT_USERNAME=your_mqtt_username
MQTT_PASSWORD=your_mqtt_password
# --- AI Emergency Reports (optional, local Ollama) ---
AI_REPORT_ENABLED=true
OLLAMA_HOST=http://ollama:11434
OLLAMA_MODEL=llama3.2:1b
⚠️ The backend will refuse to start if any of these are missing — this is intentional fail-fast behavior.
💡 Optional: to enable on-premise AI emergency reports, start the stack with the
aiprofile —docker compose --profile ai up --build -d. This launches the local Ollama service (auto-pullsllama3.2:1bon first boot) plus the dedicatedai-worker. Reports are generated entirely on your machine: telemetry never leaves the host.
cd backend
docker compose up --build -d| Endpoint | URL |
|---|---|
| API | http://localhost:8000 |
| Swagger UI | http://localhost:8000/docs |
| Health Check | http://localhost:8000/health |
cd firmware
cp esp32_config.env.example esp32_config.env
# Edit esp32_config.env with your network IP and ENROLLMENT_TOKENFlash via PlatformIO. On first boot the device will:
- Open a WiFi captive portal (
QuakeGuard-Setup) - Connect to your network
- Automatically register with the backend and receive a
sensor_id
💡 If the sensor registers with
latitude=0.0, longitude=0.0it will be assigned to "Unknown Region". Hardcode your coordinates inmain.cppfor correct zone assignment until GPS integration is complete.
cd mobile
npm installUpdate constants/config.ts with your machine's local IP:
export const API_BASE_URL = "http://YOUR_LOCAL_IP:8000";Create .env in the mobile root:
EXPO_PUBLIC_IOT_API_KEY=your_secret_key
EXPO_PUBLIC_MOBILE_WS_TOKEN=your_ws_tokennpx expo startScan the QR code with Expo Go. Ensure your phone is on the same WiFi network as the backend machine.
Validates the full pipeline: ingestion → Redis → worker → PostGIS → WebSocket alerts.
cd backend
export API_URL="http://localhost:8000"
export NUM_SENSORS=150
export CONCURRENCY_LIMIT=50
python -m tests.stress_testThree phases:
| Phase | What it tests |
|---|---|
| 🔥 Phase 1 — Firehose | 150 concurrent sensors, rate limiter validation |
| ⚔️ Phase 2 — Security | Bad signature blocked (401), Replay attack blocked (403) |
| 🔍 Phase 3 — E2E | DB persistence verified via polling GET /sensors/{id}/statistics |
A successful run ends with 🏆 SYSTEM CERTIFIED.
Trigger a simulated earthquake instantly from Swagger UI without running the stress test:
POST /demo/trigger-earthquake
Default payload (works with empty {}):
{
"zone_id": 1,
"magnitude": 7.5,
"message": "Simulated Critical Event"
}This publishes directly to the Redis quake_alerts channel, bypassing the IoT pipeline entirely and triggering the mobile app alert UI within milliseconds.
The database is pre-seeded with 8 global macro-regions. Sensors are automatically assigned to the correct zone via PostGIS spatial query at registration time.
| Zone | Coverage |
|---|---|
| Italy - North | Lombardy, Veneto, Piedmont |
| Italy - Center | Tuscany, Lazio, Umbria |
| Italy - South & Islands | Campania, Sicily, Sardinia |
| Western Europe | France, Spain, Germany, UK |
| North America | USA, Canada, Mexico |
| South America | Brazil, Argentina, Chile |
| East Asia | China, Japan, India |
| Unknown Region | Fallback for unmapped coordinates |
| Workflow | Trigger | Checks |
|---|---|---|
backend-ci.yml |
backend/** |
Bandit, Safety, stress test |
frontend-ci.yml |
mobile/** |
ESLint, npm audit |
iot-ci.yml |
firmware/** |
PlatformIO compilation |
pr-lint.yml |
All PRs | Semantic PR title (type(scope): message) |
devops-ci.yml |
.github/workflows/** |
Actionlint workflow validation |
QuakeGuard/
├── backend/
│ ├── src/
│ │ ├── main.py # FastAPI gateway + REST endpoints
│ │ ├── security.py # ECDSA, API Key, Anti-Replay
│ │ ├── worker.py # Redis consumer + magnitude + alert engine + AI enqueue
│ │ ├── ai_report_worker.py # Dedicated AI report consumer (Ollama + state machine)
│ │ ├── ollama_client.py # Deterministic LLM client (anti-hallucination)
│ │ ├── mqtt_subscriber.py # MQTT to HTTP bridge
│ │ ├── seed.py # Geographic zone seeder
│ │ ├── models.py # SQLAlchemy ORM models (+ EmergencyReport)
│ │ ├── schemas.py # Pydantic request/response schemas
│ │ └── database.py # DB engine and session factory
│ ├── tests/
│ │ ├── stress_test.py # Critical E2E stress test suite
│ │ └── unit/ # Unit tests (worker, ollama client, AI worker, models, ...)
│ ├── init-scripts/
│ │ └── ollama-entrypoint.sh # Auto-pulls the Ollama model on startup
│ ├── build.ps1 # Automatic container publish
│ ├── docker-compose.yml
│ ├── Dockerfile
│ ├── mosquitto.conf
│ ├── requirements.txt # Python requirements for backend development
│ └── .env.example
├── mobile/
│ ├── app/ # Expo Router screens
│ │ └── (tabs)/
│ │ ├── index.tsx # Monitor / Dashboard
│ │ ├── map.tsx # Sensor Network Map
│ │ └── settings.tsx # User Preferences
│ ├── api/ # Axios client + TanStack Query hooks
│ ├── components/ # Shared UI components
│ ├── store/ # Zustand state slices
│ ├── context/
│ │ └── WebSocketContext.tsx # Real-time alert context
│ └── constants/
│ └── config.ts # Centralized configuration
├── firmware/
│ ├── src/
│ │ ├── main.cpp # FreeRTOS tasks, STA/LTA, MQTT, provisioning
│ │ ├── DetectionCore.h # Pure-C++ STA/LTA core (shared firmware/host, R1)
│ │ └── RingBuffer.h # Statically allocated circular buffer
│ ├── tools/
│ │ └── detect_cli.cpp # Native host CLI (SIL replay, same core)
│ ├── test/ # Test scripts to insert into the ESP32
│ ├── key-generator/ # ECDSA key generator for backend testing
│ ├── esp32_config.env.example
│ ├── extra_script.py # ENV variables injector
│ └── platformio.ini
├── research/ # SIL validation (ROADMAP R1)
│ ├── fetch_itaca.py # ITACA download (graceful degradation) / synthetic fallback
│ ├── synthetic.py # Realistic synthetic dataset generator
│ ├── calibrate_io.py # Dataset layout I/O + path-injection guard
│ ├── orchestrator.py # Compile & run the host C++ CLI (subprocess bridge)
│ ├── metrics.py # Sensitivity, False-Alarm Rate, latency, ROC
│ ├── calibrate.py # TRIGGER_RATIO x NOISE_FLOOR sweep (F1 maximization)
│ ├── plot_roc.py # ROC curve figure for the paper
│ ├── requirements.txt # matplotlib (optional, plotting)
│ └── README.md # Dataset layout, unit conversion, licensing policy
└── docs/ # Technical Documentation
├── main.typ # Typst Whitepaper Entrypoint
├── 01-architecture.typ # System Architecture & Overview
├── 02-hardware.typ # Hardware & Edge Computing (ESP32-C3)
├── 03-security.typ # Cryptographic Security & Provisioning
├── 04-broker.typ # Data Plane & Message Broker (MQTT)
├── 05-backend.typ # Backend Services & Event Processing
├── 06-mobile.typ # Mobile Client & Live Telemetry
├── 07-deployment.typ # Deployment & CI/CD
└── 08-ai.typ # AI Emergency Report Service (v1.2.0)
| Version | Focus |
|---|---|
| v1.0 | ✅ Released — edge seismic detection on ESP32, local alerts |
| v1.1 | ✅ Released — HiveMQ Cloud MQTT (TLS), ngrok HTTPS tunnel, security hardening |
| v1.2 | ✅ Current — On-Premise AI Worker (Local Ollama / Llama 3.2) for privacy-preserving emergency reports |
| v1.2.1 | Zero-Trust Serial Fallback — signed telemetry over USB CDC (serial) when MQTT is unreachable |
| v1.3 | GNSS sync — accurate node timestamps, GPS coordinate resolution, ADXL345 calibration |
| v2.0 | Triangulation — multi-node spatial correlation + AI reports for epicenter calculation |
| v2.1 | Data Dashboards — Grafana dashboards for real-time visualization of seismic telemetry |
| v2.1.1 | Timeseries DB & Mobile Fix — migration to TimescaleDB/InfluxDB; per-sensor chart isolation in React Native mobile |
| v2.2 | Heterogeneous Edge Intelligence — hybrid Tier A (STA/LTA) + Tier B (quantized CNN) decision fusion |
| Future | Cloud IaC — Kubernetes + Terraform auto-scaling platform (see ROADMAP.md) |
| Node | Focus |
|---|---|
| #Research | Parallel ongoing node — SIL cross-validation: replay of the exact production C++ STA/LTA core (DetectionCore.h) on the host via the same C++ source, Python-only as orchestrator, ROC metrics + AI benchmarking (latency P50/P99, hallucination rate). R1 pipeline (core isolation, host CLI, orchestrator, metrics, calibration) implemented; see ROADMAP.md |
QuakeGuard's architecture and real-world applicability have been recognized in the following academic and industrial contexts:
- 🥇 1st Place Overall - Institute Project Day (2025): Awarded best technical project out of ~20 prototypes across four engineering disciplines (Computer Science, Automation, Mechanics, Chemistry). The full-stack architecture was evaluated and awarded by an industrial jury featuring technical representatives from Siemens, ABB, SORINT.lab, SAME, Ferrero, and Confindustria.
- 🎓 Academic Origin & Consultation: The system's conceptualization originated during the 2025 CQIIA-MatNet Summer School (Università di Bergamo). The distributed network logic and spatial deployment strategy were subsequently refined following technical consultations with Prof. F. Finazzi.
- 🏅 GF Marilli Competition: [Currently competing - Pending evaluation].
This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0). See the LICENSE file for details.
Developed by GiZano and riccardo0731
Open Source — AGPL-3.0 License
