-
Notifications
You must be signed in to change notification settings - Fork 2
Hardware and Edge Computing
GiZano edited this page Sep 3, 2026
·
3 revisions
The edge layer operates on resource-constrained microcontrollers, specifically the ESP32-C3 SuperMini (RISC-V architecture).
Due to the specific physical layout, the I2C bus is software-mapped to non-standard GPIO pins:
- SDA (Data): GPIO 7 (requires internal pull-up).
- SCL (Clock): GPIO 8 (requires internal pull-up).
- Power: The ADXL345 is powered strictly via the 3.3V rail.
The sensor operates at a 100Hz sampling rate (ADXL345_DATARATE_100_HZ) with a measurement range of ±16G.
-
High-Pass Filter (HPF): A digital filter (
HPF_ALPHA = 0.9f) isolates dynamic vibration data by subtracting the static DC component (Earth's gravity). - Noise Gate: Micro-vibrations below the empirical threshold of 0.04G are clamped to zero to prevent false positives from electrical noise.
-
Dropout Protection: The firmware automatically drops frames reporting near-zero absolute acceleration (< 2.0
$m/s^2$ prior to filtering), mitigating corrupted readings from I2C disconnects.
The implementation utilizes a custom, memory-efficient RingBuffer template class in C++ to maintain rolling sums for
- Short-Term Window (STA): 100 samples (1 second).
- Long-Term Window (LTA): 1000 samples (10 seconds).
-
Trigger Condition: An earthquake is registered when the STA/LTA ratio exceeds
1.8f, provided the STA absolute value is above the noise floor. (Note: The firmware deployed on edge nodes uses this more sensitive threshold of1.8fto ensure no P-waves are missed in the field, whereas a stricter ratio of2.4is used specifically in the offline SIL calibration to certify a theoretical 0% False Alarm Rate bound.)
The firmware decouples acquisition from network latency via FreeRTOS:
-
sensorTask(priority 5): Wakes every 10 ms to poll ADXL345, runsSeismicDetector(pure C++DetectionCore.hshared with host SIL validation), pushesSeismicEventtoeventQueueon trigger. -
networkTask(priority 1): ConsumeseventQueue, syncs time via NTP (pool.ntp.org), signs payload, dispatches via MQTT or serial fallback. Never blockssensorTask. -
gnssTask(optional, priority 2): WhenGNSS_ENABLED=1, continuously feeds NMEA frames.
Since v1.2.2 the node tolerates complete MQTT/WiFi loss:
-
First-available-path dispatcher:
MQTT → USB CDC Serial → Retention RingviadecidePath(mqttReachable, usbHostPresent, timeValid)(pure, shared withtest_serial_fallback.cpp). -
Framing:
SerialFallback.hbuilds[QG:FB]{"value":..,"sensor_id":..,"device_timestamp":..,"signature_hex":".."}— byte-identical to MQTT JSON; marker filters CDC boot noise. -
Host-aware routing:
Serial.isConnected()(HWCDC,ARDUINO_USB_CDC_ON_BOOT=1) distinguishes a real USB host from a charger; with no host, events are retained, not written to a dead port. -
Retention ring: Bounded
RetentionRing<100>FIFO (oldest overwritten on overflow). On path recovery, events are drained in order and re-signed with current wall time so backend ±300 s replay window accepts them. -
Offline wall clock: NTP is opportunistic (
configTime); at first syncepochAtSync+millis()anchors a software clock. No frame is emitted beforetimeValid. -
Host bridge:
firmware/tools/serial_bridge.pytails/dev/ttyACM0, filters[QG:FB], SSRF-validates URL, POSTs to/readings/withX-API-Key— same gate as MQTT bridge. Smoke-tested iniot-ci.yml.
An optional u-blox / NEO-6M / M8N on secondary UART (RX GPIO 0, TX GPIO 1, 9600 baud), compiled only with GNSS_ENABLED=1:
-
Last-known fix in NVS (
quake-gnss, ≤1 write/60 s): provisioning reports real coords even after cold boot. -
Staleness: Fix >10 s old → fallback to last-known; if none, coordinates omitted → backend assigns
Unknown Region. - Scope: NTP+PPS discipline deferred to v1.3; this module only pipes coordinates.
- Previous: System Architecture & Overview
- Next: Cryptographic Security & Provisioning
- Back to: Home