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.
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.
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
| Decision | The obvious alternative | Why, and what it cost |
|---|---|---|
| MQTT from the device | HTTP polling to the API | A 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 worker | Detect later, in the API or a batch job | The 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 telemetry | Put the frames in SQL Server with everything else | Telemetry 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 state | Alert on every qualifying frame | One 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 MQTT | Field testing, or unit tests with mocked payloads | You 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 path | Ship only the Compose stack | It 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-
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.
-
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.
-
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.
-
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.