iot
architecture
mqtt
timescaledb
TSI-Telemetry Architecture
System Overview
┌─────────────┐ BLE ┌─────────────┐ WiFi ┌─────────────┐
│ Car ECU │◄────────►│ ESP32 │◄────────►│Phone Hotspot│
│ (OBD-II) │ │ (firmware) │ └──────┬──────┘
└─────────────┘ └─────────────┘ │
Internet
│
┌───────▼───────┐
│ HiveMQ Cloud │
│ (MQTT Broker) │
└───────┬───────┘
│
Internet
│
┌────────────────────────────────┴───┐
│ Mac Mini (Home) │
│ │
│ ┌──────────────┐ ┌───────────┐ │
│ │ Python Bridge│─►│TimescaleDB│ │
│ │ (MQTT sub) │ │ (Docker) │ │
│ └──────────────┘ └─────┬─────┘ │
│ │ │
│ ┌─────▼─────┐ │
│ │ Grafana │ │
│ │(dashboard)│ │
│ └───────────┘ │
└───────────────────────────────────┘
Why Cloud MQTT Broker?
The ESP32 connects to your phone's hotspot while driving. Your Mac Mini is at home behind NAT. They can't reach each other directly.
Solution: Use HiveMQ Cloud as an intermediary:
- ESP32 publishes to cloud broker
- Mac Mini bridge subscribes to cloud broker
- No NAT/firewall issues
- Free tier is sufficient
Data Flow
- Car ECU exposes data via OBD-II port
- ELM327 adapter translates OBD-II protocol to Bluetooth LE
- ESP32 connects to ELM327, requests PIDs, parses responses
- ESP32 connects to phone hotspot WiFi
- ESP32 publishes telemetry to HiveMQ Cloud topic
car/telemetry - Python Bridge (on Mac Mini) subscribes to HiveMQ Cloud
- Bridge writes data to TimescaleDB
- Grafana queries TimescaleDB, displays real-time dashboard
Why This Stack?
Why MQTT?
- Lightweight protocol designed for IoT
- Low bandwidth overhead
- Pub/Sub model decouples producers and consumers
- QoS levels for reliability
- Works well over unreliable mobile connections
Why TimescaleDB over InfluxDB?
We switched from InfluxDB to TimescaleDB because:
- Built on PostgreSQL (familiar SQL syntax)
- Easier setup and querying
- Better for complex analytics later
- Hypertables handle time-series optimization automatically
- Can mix relational and time-series data
Why HiveMQ Cloud over local Mosquitto?
- ESP32 can't reach home network from mobile hotspot
- No port forwarding or VPN setup needed
- Free tier (10 GB/month) is plenty for telemetry
- TLS encryption included
- Reliable cloud infrastructure
Components
ESP32 Firmware Modules
| Module | Responsibility |
|---|---|
BLEManager |
BLE connection to ELM327 |
OBDParser |
Parse OBD-II responses |
CarData |
Data structure for telemetry |
DisplayManager |
Serial output formatting |
Config |
Configuration constants |
WiFiManager |
(TODO) WiFi connection |
MQTTManager |
(TODO) MQTT publishing to HiveMQ |
Server Components
| Component | Port | Purpose |
|---|---|---|
| TimescaleDB (Docker) | 5433 | Time-series database |
| Grafana | 3000 | Visualization |
| Python Bridge | - | MQTT to TimescaleDB |
| HiveMQ Cloud | 8883 | Cloud MQTT broker |
OBD-II Protocol
Request Format
01 0C\r <- Request PID 0C (RPM)
Response Format
41 0C 1A 2B <- Response
│ │ │ │
│ │ └──┴── Data bytes (A=0x1A, B=0x2B)
│ └──────── PID (0C = RPM)
└─────────── Mode 41 = positive response to mode 01
PID Conversion Formulas
| PID | Name | Formula | Unit |
|---|---|---|---|
| 0x0C | RPM | (A*256 + B) / 4 | rpm |
| 0x0D | Speed | A | km/h |
| 0x05 | Coolant Temp | A - 40 | °C |
| 0x0F | Intake Temp | A - 40 | °C |
| 0x11 | Throttle | A * 100 / 255 | % |
| 0x04 | Engine Load | A * 100 / 255 | % |
| 0x0B | MAP | A | kPa |
| 0x2F | Fuel Level | A * 100 / 255 | % |
| 0x0E | Timing Advance | A / 2 - 64 | degrees |
| 0x42 | Battery Voltage | (A*256 + B) / 1000 | V |
Network Topology
┌─────────────────────────────────────────────────────────────────┐
│ WHILE DRIVING │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Car Phone Cloud │
│ ┌─────┐ ┌───────┐ ┌─────────┐ │
│ │ESP32│───WiFi────────►│Hotspot│───Cellular──►│ HiveMQ │ │
│ └─────┘ └───────┘ │ Cloud │ │
│ └────┬────┘ │
│ │ │
├─────────────────────────────────────────────────────┼──────────┤
│ HOME │ │
├─────────────────────────────────────────────────────┼──────────┤
│ │ │
│ ┌────▼────┐ │
│ │ Bridge │ │
│ └────┬────┘ │
│ │ │
│ ┌────▼────┐ │
│ │Timescale│ │
│ └────┬────┘ │
│ │ │
│ ┌────▼────┐ │
│ │ Grafana │ │
│ └─────────┘ │
└─────────────────────────────────────────────────────────────────┘