How sensor data flows
Here is the path a single temperature reading will take:
laptop ─[publish]─→ Mosquitto ─[subscribe]─→ API ─[WebSocket]─→ dashboard
sensor broker (Pi) (browser)
Three jobs. Each one is small, each one is useful on its own:
The publisher doesn’t know who’s listening. The dashboard doesn’t know which Micro:bit sent the data. Both only know the broker. That’s called decoupling — you can add a second sensor, or a third dashboard, without changing any of the others.
Open the dashboard
From your laptop, open the dashboard:
http://pi-iot-XX.local/
It should say “No sensor data yet.” with a connection pill in the top-right that reads connected. That’s the dashboard reporting that it has an open WebSocket to the API. It just hasn’t seen any sensor messages yet — because nothing is publishing.
disconnectedThe API isn’t reachable. SSH into the Pi and run docker compose ps — the api service should be Up. If not, check the logs: docker compose logs api.
Look at a real message
Before we send any messages, here’s the shape they’ll all use. Every reading published to the platform looks like this:
classroom/sensor/microbit-test-01/temperature
{
"ts": "2026-05-10T13:45:12.123Z",
"source": "microbit-test-01",
"schema": "v1",
"data": {
"value": 24.3,
"unit": "celsius",
"quality": "good"
}
}
The topic is structured: <domain>/sensor/<device>/<measurement>. Every part is lowercase, every part is meaningful. The API subscribes to +/sensor/+/+, where + means “match any one segment” — so it picks up every sensor on every domain.
schema: "v1"?JSON is human-readable and trivially decoded everywhere. The schema field is a promise: “this message has the v1 shape”. When we change the shape later we’ll bump it to v2, and the API will be able to handle both.
Run the test publisher
The platform repo includes a small Python script that publishes fake temperature and humidity readings once a second. We’ll run it on your laptop — it talks to the Pi over Wi-Fi.
If you don’t already have the repo on your laptop, clone it. Then in a terminal:
cd /path/to/iot-platform
pip install aiomqtt
If pip install complains about “externally-managed-environment”, your system Python is locked. Create an isolated one:
python3 -m venv .venv source .venv/bin/activate pip install aiomqtt
Now run the publisher, pointing it at your Pi:
python3 scripts/publish-test-sensor.py \
--host pi-iot-XX.local \
--password "<your MQTT password>"
You should see lines tick by:
→ connecting to pi-iot-XX.local:1883 as platform
→ publishing as microbit-test-01 on classroom/sensor/microbit-test-01/{temperature,humidity}
✓ connected. Publishing 1 Hz. Ctrl-C to stop.
t= 0.0s temperature= 22.05 °C humidity= 60.4 %
t= 1.0s temperature= 22.18 °C humidity= 60.5 %
t= 2.0s temperature= 21.93 °C humidity= 60.6 %
Leave it running. Don’t close the terminal yet. We’ll come back to it.
Same fixes as Phase 2: check the password matches .env on the Pi, and that your laptop is on the same Wi-Fi.
Watch the cards update
Switch back to the dashboard tab. Within one second of starting the publisher, you should see two cards appear:
- microbit-test-01 / temperature with a value like
22.1 °C. - microbit-test-01 / humidity with a value like
60.4 %.
Each card’s number should change every second — the value goes up and down a bit because the publisher’s fake signal is a slow sine wave plus noise. Below the value you’ll see “just now” or “1 s ago”, ticking up.
Try this:
- Open a second tab on the same dashboard URL. The same cards appear instantly. Both tabs are subscribed to the same WebSocket; both get the same updates.
- Run a second publisher in another terminal with a different device name:
terminal 2 · laptop
python3 scripts/publish-test-sensor.py \ --host pi-iot-XX.local \ --password "<your MQTT password>" \ --device microbit-test-02Two more cards appear. The dashboard didn’t need to be told — it just received new sensor IDs from the API.
The platform doesn’t hard-code which sensors exist. New ones appear when they show up. Old ones stick around showing their last value. This is how every IoT system you’ll ever build should behave.
Peek at MQTT in Node-RED
Node-RED is your X-ray machine for the bus. Let’s use it to see every message flying through the broker, in real time.
Open Node-RED:
http://pi-iot-XX.local:1880/
Build a tiny three-node flow:
- From the left sidebar, drag an mqtt in node onto the canvas.
- Drag a debug node next to it.
- Connect the output of the mqtt node to the input of the debug node (click and drag from the right edge of one to the left edge of the other).
- Double-click the mqtt node:
- Server: click the pencil and add a new server.
- Server:
mosquitto - Port:
1883 - On the Security tab, set
UsernameandPasswordto match your.env. - Click Add.
- Server:
- Topic:
+/sensor/+/+ - Output: a parsed JSON object
- Click Done.
- Server: click the pencil and add a new server.
- Click the red Deploy button (top-right).
- On the right, click the bug-shaped tab to open the Debug pane.
You should see a new debug entry appear roughly every half-second — one per published topic. Click any of them to expand the JSON. Compare it to the shape from Step 2.
Later in the course, when something doesn’t show up on the dashboard, this Node-RED flow tells you instantly whether the message reached the broker at all. If you see it here but not on the dashboard, the bug is in the API. If you don’t see it here, the bug is in the publisher.
Stop the publisher — watch it go stale
Go back to the publisher terminal and press Ctrl+C. The script stops; no more messages are sent.
Watch the dashboard. The cards keep their last value — that’s correct, the API still has the most recent reading in memory. After about 30 seconds, each card should grow a yellow STALE badge, and the timestamp underneath ticks up: “30 s ago”, “1 min ago”.
Restart the publisher. The badges disappear immediately as fresh readings arrive.
A real classroom sensor might lose Wi-Fi, run out of battery, or just disconnect. Showing the last value is fine; showing it without telling the user it’s old is dangerous — teachers might think the room is still 22 °C when actually the sensor died an hour ago. Always surface freshness.
Final verification
Tick everything below before declaring Phase 3 done. Phase 4 will assume readings make it from MQTT to the API reliably.