A real distributed systems project โ not a toy script.
Built with the actual MQTT protocol used by AWS IoT, Tesla, and billions of embedded devices worldwide.
๐ฌ What's happening under the hood
Every 3โ5 seconds, each node wakes up, generates a reading, and shouts it
into the network. The hub is always listening. The dashboard is always watching.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ LIVE SENSOR NETWORK (simulated) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฃ
โ โ
โ ๐ก node_1 temp=22.4ยฐC โโโ โ
โ ๐ก node_2 temp=19.8ยฐC โโโค โ
โ ๐ง node_3 humi=58.3% โโโผโโโบ [ Mosquitto Broker :1883 ] โ
โ ๐ง node_4 humi=61.1% โโโค โ โ
โ ๐ node_5 motion=0 โโโ โ subscribe sensors/+/+ โ
โ โผ โ
โ [ hub.py ] โ
โ โโ logs readings to SQLite โ
โ โโ tracks heartbeats per node โ
โ โโ 15s silence = โ OFFLINE โ
โ โ โ
โ โผ โ
โ [ Streamlit Dashboard ] โ
โ โโ ๐ข node_1 22.4ยฐC 2s ago โ
โ โโ ๐ข node_2 19.8ยฐC 4s ago โ
โ โโ ๐ด node_3 OFFLINE 23s ago โ ALERT โ
โ โโ ๐ข node_4 61.1% 1s ago โ
โ โโ ๐ข node_5 motion=0 3s ago โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Kill any node with one command. The hub detects the silence. The dashboard goes red. Revive it. It comes back online. The whole thing is live.
๐ฅ Demo
โจ Why this project exists
Most IoT tutorials fake it โ one script, fake pub/sub, no real network, no real protocol.
This project uses real MQTT over a real broker with real distributed processes. The same architecture pattern behind:
| Real System | What it shares with this project |
|---|---|
| ๐ Smart home hubs (Google Home, Amazon Echo) | MQTT pub/sub between devices and hub |
| ๐ญ Industrial SCADA systems | Sensor nodes โ central aggregator โ dashboard |
| ๐ Tesla vehicle telemetry | Heartbeat-based liveness detection |
| โ๏ธ AWS IoT Core | Topic-based routing, wildcard subscriptions |
| โ Kubernetes | Heartbeat timeouts โ node marked NotReady |
๐งฉ Core concepts demonstrated
๐จ MQTT Publish / Subscribe โ click to expand
MQTT is a messaging protocol designed in 1999 for oil pipeline monitoring over satellite โ low bandwidth, unreliable connections, constrained devices. Those same properties make it the dominant IoT protocol today.
How it works here:
node_1 publishes โโโบ sensors/node_1/temperature โโโบ broker โโโบ hub receives
node_1 publishes โโโบ sensors/node_1/heartbeat โโโบ broker โโโบ hub receives
hub subscribes โโโบ sensors/+/+ (+ matches any single level)
Publishers and subscribers never connect to each other โ the broker decouples them entirely. A node doesn't know or care that the hub exists.
๐ฅ๏ธ Distributed Process Architecture โ click to expand
Each sensor node is a completely independent OS process with:
- Its own MQTT client connection
- Its own data generation loop
- No shared memory with any other node
- No knowledge that other nodes exist
PID 21564 sensor_node.py node_1 temperature
PID 11088 sensor_node.py node_2 temperature
PID 7896 sensor_node.py node_3 humidity
PID 4208 sensor_node.py node_4 humidity
PID 3892 sensor_node.py node_5 motion
PID 9100 hub.py โ subscribes to all of them
Kill PID 7896 โ the others keep running. The hub notices the silence. This is how real embedded hardware behaves.
๐ Heartbeat-based Fault Detection โ click to expand
A classic distributed systems pattern used in Kubernetes, ZooKeeper, Consul, and every serious distributed database.
The algorithm:
Every 5 seconds:
node โ publishes heartbeat to sensors/{node_id}/heartbeat
Every 5 seconds (hub fault-detection thread):
for each known node:
if (now - last_heartbeat) > 15 seconds:
mark node OFFLINE
log alert
When heartbeat resumes:
if node was OFFLINE:
mark node ONLINE
log recovery alert
Sleep-mode awareness: Nodes can publish their expected sleep schedule before going dormant. The hub uses this to avoid false-alarming โ it knows the difference between "dead" and "intentionally sleeping."
๐ Real-time Monitoring Dashboard โ click to expand
The Streamlit dashboard polls SQLite every 3 seconds and shows:
- Node status grid โ per-node cards with color-coded online/offline status, current reading, last-seen timestamp
- Time-series charts โ rolling Plotly charts for temperature, humidity, and motion
- Alert log โ timestamped log of every offline event, recovery, and anomalous reading
SQLite is used as a lightweight shared state store โ the hub writes, the dashboard reads. No additional infrastructure needed.
๐ Project structure
iot-sensor-network/
โ
โโโ src/
โ โโโ sensor_node.py # ๐ก Independent sensor device โ MQTT publisher
โ โ # Generates realistic temperature / humidity / motion
โ โ # Publishes readings + heartbeats
โ โ
โ โโโ hub.py # ๐ฅ Central hub โ MQTT subscriber
โ โ # Logs all readings to SQLite
โ โ # Fault-detection thread watches heartbeats
โ โ
โ โโโ launch_nodes.py # ๐ Spawns all 5 nodes as separate OS processes
โ โ # Writes PID records so fault_injector can find them
โ โ
โ โโโ fault_injector.py # ๐ Kill / revive individual nodes on demand
โ # Demonstrates fault detection live
โ
โโโ app.py # ๐ Streamlit dashboard โ live monitoring UI
โโโ mqtt_test.py # โ
Connectivity verification script
โโโ requirements.txt # ๐ฆ Pinned dependencies
โโโ .gitignore
โโโ README.md
โ๏ธ Tech stack
| Layer | Technology | Version | Role |
|---|---|---|---|
| Message broker | Mosquitto | 2.x | Routes all MQTT messages |
| MQTT client | paho-mqtt | 2.1.0 | Python MQTT pub/sub |
| Database | SQLite | stdlib | Time-series + state store |
| Dashboard | Streamlit | 1.36.0 | Live web UI |
| Charts | Plotly | 5.22.0 | Interactive time-series |
| Data wrangling | pandas | 2.2.2 | DB โ DataFrame queries |
๐ Getting started
Prerequisites
- Python 3.9 โ 3.12
- Mosquitto MQTT broker โ this is a system service, not a pip package
Step 1 โ Install Mosquitto
โ ๏ธ This step is required. The hub and nodes will fail with
ConnectionRefusedErrorif Mosquitto isn't running.
- Download the installer from https://mosquitto.org/download/ (64-bit
.exe) - Run it โ installs as a Windows service automatically
- Start the service (PowerShell as Administrator):
net start mosquitto
- Verify it's listening:
netstat -an | findstr 1883
# Should show: 0.0.0.0:1883 LISTENING
macOS:
brew install mosquitto
brew services start mosquitto
Linux:
sudo apt install mosquitto mosquitto-clients
sudo systemctl start mosquitto
Step 2 โ Python environment
# Windows
py -3.11 -m venv venv
venv\Scripts\python.exe -m pip install -r requirements.txt
# macOS / Linux
python3.11 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
Step 3 โ Verify connectivity
Run this before anything else. It connects to Mosquitto, publishes a test message, and confirms it arrives back.
# Windows
venv\Scripts\python.exe mqtt_test.py
# macOS / Linux
python mqtt_test.py
Expected output:
โ
All Python imports OK
โ
Connected to Mosquitto at localhost:1883
โ
Received message: {"test": true, ...}
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
๏ฟฝ๏ฟฝ๏ฟฝ MQTT CONNECTIVITY TEST PASSED
You can now start the full simulation.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Step 4 โ Run the simulation
Open three terminal windows in the project root.
Terminal 1 โ Start the hub first:
venv\Scripts\python.exe src/hub.py
[hub] Connected to broker at localhost:1883
[hub] Subscribed to sensors/+/+
[hub] ๐ก node_1/temperature: 22.4ยฐC โ data starts flowing
[hub] ๐ก node_3/humidity: 58.1%
Terminal 2 โ Launch all sensor nodes:
venv\Scripts\python.exe src/launch_nodes.py
โ
node_1 (temperature) โ PID 21564
โ
node_2 (temperature) โ PID 11088
โ
node_3 (humidity ) โ PID 7896
โ
node_4 (humidity ) โ PID 4208
โ
node_5 (motion ) โ PID 3892
Terminal 3 โ Open the dashboard:
venv\Scripts\streamlit.exe run app.py
Opens at http://localhost:8501 โ auto-refreshes every 3 seconds.
Step 5 โ Demo fault detection ๐ฅ
This is the interesting part. Open a fourth terminal:
# Kill node_3 โ simulates a dead battery or lost connection
venv\Scripts\python.exe src/fault_injector.py --kill node_3
Watch what happens:
# After ~15 seconds silence, the hub fires:
[hub] โ Node node_3 OFFLINE (no heartbeat for 15s)
[hub] ๐ ALERT [offline] node_3: Node node_3 went offline
The dashboard card for node_3 turns ๐ด RED.
# Bring it back
venv\Scripts\python.exe src/fault_injector.py --revive node_3
# Hub immediately detects the returning heartbeat:
[hub] โ
Node node_3 came back ONLINE
[hub] ๐ ALERT [online] node_3: Node node_3 reconnected
Card goes ๐ข GREEN. The full cycle is logged in the alerts panel.
๐ก Sensor simulation details
| Sensor | Behaviour | Range |
|---|---|---|
| Temperature | Slow random walk ยฑ 0.3ยฐC per step, 3% chance of a sudden spike (+3โ8ยฐC) | 15ยฐC โ 35ยฐC |
| Humidity | Slow random walk ยฑ 0.5% per step | 20% โ 90% |
| Motion | Binary trigger, ~5% probability per reading | 0 or 1 |
The hub flags anomalies automatically:
- Temperature > 30ยฐC or < 15ยฐC โ alert logged
- Humidity > 80% โ alert logged
- Motion triggered โ alert logged
๐ง Troubleshooting
| Problem | Fix |
|---|---|
ConnectionRefusedError on port 1883 |
Run net start mosquitto as Administrator |
| Dashboard shows "waiting for data" | Start hub.py before opening the dashboard |
| Nodes exit immediately | Run mqtt_test.py first to verify broker is reachable |
ModuleNotFoundError |
Run pip install -r requirements.txt inside your venv |
| Port 1883 already in use | Another Mosquitto instance is running โ check Services |
๐บ Future work
This is a v1 local simulation. The path to production:
- ๐ TLS + Auth โ production MQTT uses port 8883 with client certificates or username/password. Noted in code comments throughout.
- โ๏ธ Cloud broker โ swap
localhost:1883for AWS IoT Core, HiveMQ Cloud, or EMQX. The node/hub code is broker-agnostic. - ๐ Real hardware โ
sensor_node.pyruns unmodified on a Raspberry Pi. PointBROKER_HOSTat a real broker and connect actual sensors. - ๐ Time-series DB โ replace SQLite with InfluxDB or TimescaleDB for production-scale workloads and Grafana dashboards.
- ๐ด Sleep mode โ already partially implemented. Nodes publish their sleep schedule; the hub suppresses false-alarm timeouts during intentional dormant periods.
๐ Why MQTT?
MQTT was designed in 1999 for monitoring oil pipelines over satellite links โ high latency, low bandwidth, unreliable connections, constrained devices. Those exact constraints describe IoT hardware.
Compared to HTTP:
| MQTT | HTTP | |
|---|---|---|
| Header overhead | 2 bytes fixed | ~800 bytes minimum |
| Connection model | Persistent | Request/response |
| Broker decoupling | โ Yes | โ No |
| QoS guarantees | 3 levels | None built-in |
| Idle detection | Last Will & Testament | Polling |
Today MQTT is the default protocol for AWS IoT Core, Azure IoT Hub, Google Cloud IoT, and most commercial smart home platforms.
Built by Eddie ยท Python ยท MQTT ยท Distributed Systems
If this was useful, leave a โญ

Comments