Robot Telemetry System
Distributed environmental monitoring — ESP32 Bluetooth sensor nodes, Raspberry Pi hardware bridge, MQTT telemetry pipeline, and a Next.js control frontend.
Live data from https://iot.optospintronics.org · loading…
System Architecture
ESP32 Sensor Nodes
Each ESP32 samples its sensor every 10 s and stores up to 500 records in a circular buffer. Batches of 25 records are sent over Bluetooth Classic (RFCOMM) with random startup jitter, a 5 s ACK timeout, and exponential retry backoff. Records are only removed after the Pi sends the ACK byte (0x06).
Raspberry Pi — Hardware Bridge
A JDY-31 Bluetooth UART module on GPIO 14/15 (hardware UART — Pi onboard Bluetooth disabled) receives sensor batches. The Pi validates JSON, sends ACK immediately, then publishes each record to MQTT over TLS with QoS-1. A Pololu A-Star on USB handles motor commands. The Pi Camera streams MJPEG via picamera2.
MQTT · Node-RED · Database
Records are published to robot/sensors/<SENSOR_ID> with QoS-1. Node-RED subscribes with a wildcard, routes each payload to the correct sensor table, and exposes HTTP APIs consumed by both the control frontend and this website.
PC Control Frontend
A Next.js app with four pages: Controller (joystick + live camera), Sensors (Bluetooth trigger — timed Start or continuous Listen — with per-sensor cards and live data badge), Data Browser (full history with chart/table toggle), and Settings (Pi IP/port, network scan).
Live Sensor Data
Click a card to view its history chart · auto-refreshes every 30 s
BME680
activeTemperature · Humidity · Pressure · Gas Resistance
No data received yet.
LTR390
activeAmbient Light (Lux) · UV Index
No data received yet.
SCD41
activeCO₂ · Temperature · Humidity
No data received yet.
DHT11
activeTemperature · Humidity
No data received yet.
SGP41
activeVOC index · NOx index (air quality)
No data received yet.
Implementation Notes
Reliable Bluetooth Transfer
- ACK sent before MQTT publish — stays within the 5 s ESP32 timeout
- JDY-31 status lines stripped from the UART buffer before JSON parsing
- Pi onboard Bluetooth disabled (dtoverlay=disable-bt) to free ttyAMA0
- Frontend auto-retries on 409 (stale listener): stop → wait → restart
- Listener skips bad records and continues — thread never killed on a single failure
Sensors Page
- Start: timed Bluetooth window (60 s, auto-extends on each receipt)
- Listen: continuous mode — port stays open indefinitely
- "Latest" badge flashes green on the card that most recently received data
- Sensor history refreshes from Node-RED on every new Bluetooth batch
Controller Page
- Arrow joystick fixed left — sends /motors/<left>,<right> commands
- MJPEG camera feed fixed right — streams directly from /stream.mjpg
- Motor speeds −400 to +400; differential drive direction mapping
- Connection auto-polled every 5 s; controls disabled when Pi unreachable
MQTT Reliability
- loop_start() called before publish, wait_for_publish() per message
- Without loop(), TLS closes before QoS-1 PUBACK — messages silently dropped
- Each record published individually so partial batches are never lost