$ duy_
cd ../projects

Apr 2025 — July 2025 · Team of 2 — backend and infrastructure

IoT Accident Monitoring

ESP8266 telemetry to MQTT to InfluxDB, with Grafana alerting for near-instant incident response.

stack

  • ESP8266
  • MPU6050
  • GPS L80
  • ASP.NET 8
  • MQTT (Mosquitto)
  • InfluxDB
  • Redis
  • Grafana
  • Traefik
  • Bind9
  • OpenRouteService
  • Docker Compose
  • SQL Server

what it does

  • Real-time crash detection from an ESP8266 + MPU6050 + GPS unit, streaming telemetry over MQTT.
  • Time-series pipeline into InfluxDB with a .NET background worker doing detection and notification.
  • Clean-architecture ASP.NET 8 API for incident history, with Bicep IaC for the Azure deployment path.
  • Grafana dashboards and alerts over live device data, plus a multi-device Python simulator for load testing.
architecture — iot accident monitoring

On the vehicle: an MPU6050 inertial unit and a GPS L80 both feed an ESP8266 running the firmware; a Python simulator publishes the same payloads for testing. Ingest and detection: both publish over MQTT to Mosquitto with TLS and auth; a .NET iot-monitor worker consumes the stream, runs crash detection, writes telemetry to InfluxDB and keeps dedupe state in Redis. API and operations: the worker raises incidents into the accident-monitor ASP.NET 8 API built on clean architecture, which persists history to SQL Server and calls OpenRouteService for nearest-responder routing, while Grafana dashboards and alerts read directly from InfluxDB. Everything runs as containers on one Docker Compose host behind Traefik, with Bind9 for internal DNS.

The simulator on the right is not decoration: it publishes the same MQTT payloads as the real unit, which is how the detection path was tested without crashing a vehicle.

Vehicle crash detection, end to end: a microcontroller on the vehicle, an MQTT broker, a time-series store, and dashboards that alert while the incident is still happening.

Device

An ESP8266 reads an MPU6050 accelerometer/gyro and an L80 GPS module. When the acceleration signature crosses the crash threshold it publishes a telemetry frame — impact magnitude, coordinates, device id — over MQTT. A second sketch drives a warning car unit over RF for the demo rig.

Ingest and detection

Mosquitto brokers the device traffic. iot-monitor, a .NET 8 background service, subscribes, writes every frame into InfluxDB as a time series, and runs the detection pass in-line — so the same message that lands in storage also decides whether to raise a notification. Redis holds hot state so repeated frames from one incident collapse into a single alert instead of a storm.

API and history

accident-monitor is an ASP.NET 8 clean-architecture application — Domain, Application, Infrastructure, WebApi — exposing incident history over REST on SQL Server. It ships with a Dockerfile, a devcontainer, Cake build scripts and Bicep templates under infra/ for the Azure deployment path.

Operations

Everything runs from one Compose file: Mosquitto, InfluxDB, Grafana, Traefik as the reverse proxy, Bind9 for internal service resolution, and OpenRouteService for map and routing lookups against the incident coordinates. Grafana carries the live dashboards and the alert rules.

Testing without a vehicle

Field-testing a crash detector is awkward, so the device side is simulated. Python scripts under scripts-testing/ generate realistic single-device and multi-device telemetry patterns, feed them through the real MQTT path, and verify what came out the other end of InfluxDB — which made the detection thresholds tunable from a laptop.

Decisions and trade-offs

DecisionThe obvious alternativeWhy, and what it cost
MQTT from the deviceHTTP polling to the APIA crash notification must be pushed, not discovered on the next poll, and an ESP8266 on a vehicle has neither the power budget nor the connection quality for chatty HTTP. The cost is a broker to run, secure and keep alive.
Detection inside the ingest workerDetect later, in the API or a batch jobThe same message that lands in storage decides whether to alert, so the alert can never lag the write. The cost is that the worker now has two jobs, and a slow detection pass would back up ingest — which is exactly why the heuristic stays cheap.
InfluxDB for telemetryPut the frames in SQL Server with everything elseTelemetry is append-heavy time series with a retention policy; a relational table for it is a bill and an index-maintenance problem waiting to happen. Incident records still live in SQL Server, where relational queries belong.
Redis for dedupe stateAlert on every qualifying frameOne crash emits many frames. Without collapsing them, the first real incident buries the operator in notifications and trains them to ignore the channel. The cost is a stateful dependency in the hot path.
A simulator that speaks real MQTTField testing, or unit tests with mocked payloadsYou cannot crash a car to test a threshold, and a mocked payload proves nothing about the broker, the worker or InfluxDB. The simulator publishes identical frames through the real path, which is what made the thresholds tunable from a laptop.
Bicep templates for an Azure pathShip only the Compose stackIt was a hedge for the coursework, and in hindsight the wrong call: two deployment targets for a two-person project doubled the surface area without doubling the value. The Compose path is the one that actually ran.

decisions

4 calls
  1. Detection inside the ingest worker over a scheduled query over stored telemetry

    A crash alert that arrives a minute late is not an alert. The same message that gets written to InfluxDB decides whether to notify.

  2. InfluxDB for telemetry over putting everything in SQL Server

    The device stream is append-heavy time series with a retention policy. Incident records are relational and long-lived. Those are two different storage problems.

  3. Redis for hot device state over deduplicating on read

    One crash produces many frames. Without hot state the operator gets an alert storm at the exact moment they can least afford one.

  4. A Python device simulator over field testing the detector

    Thresholds need dozens of iterations and you cannot get those by crashing a real vehicle. The simulator drives the real MQTT path, so only the sensor is fake.