This project contains:
- ESP32 firmware (
src/main.cpp) that publishes distance sensor readings to MQTT. - A Python CLI backend (
backend.py) to monitor all devices and set per-device thresholds. - A Flask web dashboard (
dashboard.py) for a live UI and threshold control. - A simulator (
simulator.py) to test everything without physical ESP32 hardware.
The system uses MQTT topics to exchange data and configuration.
- Sensor data topic format:
sensors/<device_id>/<sensor_name>- Current firmware publishes distance to
sensors/esp32_1/distance.
- Threshold configuration topic format:
config/<device_id>/threshold- Example:
config/esp32_1/threshold
How data flows:
- Device (real or simulated) publishes sensor readings to
sensors/.... - Backend CLI and Dashboard subscribe to
sensors/+/+to see all devices. - CLI/Dashboard publish threshold values to
config/<device_id>/threshold. - Device (real or simulated) receives new threshold and updates its behavior.
src/main.cpp: ESP32 firmware (Wi-Fi + MQTT + HC-SR04 logic).platformio.ini: PlatformIO build config.backend.py: Terminal backend with commands and live table view.dashboard.py: Flask app with live web dashboard.templates/dashboard.html: Dashboard UI template.publish.py: Simple MQTT publisher example (sends test messages).subscribe.py: Simple MQTT subscriber example (prints received messages).simulator.py: Fake ESP32 devices for local testing.run_demo.sh: One-command launcher for simulator + backend + dashboard.stop_demo.sh: Stops all services started byrun_demo.sh.workshop.conf: Example Mosquitto config.requirements.txt: Python dependencies for backend/dashboard/simulator.
- Python 3.10+ (tested with Python 3.12).
- An MQTT broker (Mosquitto recommended).
- For firmware deployment only: PlatformIO + ESP32 board.
From the project root:
python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txtTo leave the environment:
deactivateIf you already have a broker, skip this section.
Use the broker IP/host with all scripts via --broker.
Install and start Mosquitto (example):
sudo apt update
sudo apt install -y mosquitto mosquitto-clientsThis repo includes a ready-to-use Mosquitto config for workshops:
workshop.conf(in the project root)- Listens on port 1883
- Allows anonymous connections (no password needed)
To enable it:
sudo cp workshop.conf /etc/mosquitto/conf.d/workshop.conf
sudo systemctl restart mosquitto
sudo systemctl status mosquittoYou can now use localhost:1883 as your broker for all scripts and ESP32 devices.
Publishes 5 test messages to a topic on the MQTT broker (default: localhost:1883).
Usage:
python publish.pyEdit the BROKER, PORT, and TOPIC variables in the script as needed.
Subscribes to a topic on the MQTT broker (default: localhost:1883) and prints received messages.
Usage:
python subscribe.pyEdit the BROKER, PORT, and TOPIC variables in the script as needed.
Simulates one or more ESP32-style devices.
What it does:
- Publishes distance to
sensors/<device_id>/distance. - Subscribes to
config/+/threshold. - Updates each device threshold when config messages arrive.
Common commands:
python simulator.py --broker 192.168.1.100
python simulator.py --broker 192.168.1.100 --devices esp32_1,esp32_2,esp32_3
python simulator.py --broker 192.168.1.100 --count 5 --interval 0.5Useful options:
--devices: comma-separated IDs.--count: auto-createesp32_1..esp32_Nwhen--devicesis not set.--interval: publish period in seconds.--min-distance,--max-distance,--jitter: control value range and movement.--default-threshold: starting threshold for all simulated devices (default20.0).
Subscribes to all sensor data and lets you set thresholds from terminal.
Run:
python backend.py --broker 192.168.1.100Optional auth:
python backend.py --broker 192.168.1.100 --username myuser --password mypassDefault threshold behavior:
- Backend assumes
20.0for a device until you explicitly set one. - Override with
--default-thresholdif needed.
CLI commands:
help: show command list.list: print latest readings JSON.live [seconds]: live table view in terminal (default refresh1.0s).set <device_id> <threshold>: publish threshold toconfig/<device_id>/threshold.quitorexit: close backend.
Background mode:
--no-cli: run backend without interactive prompt (used by demo launcher).
Example:
set esp32_2 35
Flask dashboard with live updates and threshold form.
Run:
python dashboard.py --broker 192.168.1.100Open in browser:
http://127.0.0.1:5000
Features:
- Live stream of readings from all devices.
- Table view: device, sensor, value, threshold, time, topic.
- Form to set threshold for a selected device.
Useful options:
--host 0.0.0.0to access from other machines on LAN.--web-port 5001to change web port.--sensor-topicto customize topic filter.--default-thresholdsets fallback threshold shown for devices (default20.0).
Example:
python dashboard.py --broker 192.168.1.100 --host 0.0.0.0 --web-port 5001Open 2-3 terminals in project root and activate the same venv in each.
Terminal 1: start simulator
source venv/bin/activate
python simulator.py --broker 127.0.0.1 --count 3Terminal 2: start backend CLI
source venv/bin/activate
python backend.py --broker 127.0.0.1Then in backend CLI:
live
set esp32_1 30
Terminal 3 (optional): start dashboard
source venv/bin/activate
python dashboard.py --broker 127.0.0.1Open http://127.0.0.1:5000 and use the form to set thresholds.
If you want to launch everything at once, use:
./run_demo.shBy default, this starts:
simulator.py(unless--count 0)dashboard.py(web UI)
The backend CLI (backend.py) is NOT started by default.
If you want to run it in the background for extra logging or CLI testing, add --with-backend:
./run_demo.sh --with-backendDefault values used by the launcher:
- broker:
127.0.0.1 - port:
1883 - simulated devices:
3 - dashboard:
http://127.0.0.1:5000
Notes:
--count 0disablessimulator.pyinrun_demo.shso only real ESP32 publishers are used.simulator.pyby itself still expects--count >= 1unless--devicesis provided.--with-backendstartsbackend.pyin background (optional, default: off).
Common options:
# Simulate 5 devices, dashboard on all interfaces
./run_demo.sh --broker 192.168.1.100 --port 1883 --count 5 --web-host 0.0.0.0 --web-port 5001
# Real ESP32 only (no simulator)
./run_demo.sh --broker 192.168.1.100 --port 1883 --count 0 --web-host 0.0.0.0 --web-port 5001
# With backend CLI in background
./run_demo.sh --with-backendStop everything:
./stop_demo.shLogs are written to:
.demo/logs/simulator.log(if simulator enabled).demo/logs/backend.log(if --with-backend).demo/logs/dashboard.log
Firmware file: src/main.cpp
Current firmware behavior:
- Connects to Wi-Fi using values in code (
ssid,password). - Publishes distance every second to
sensors/esp32_1/distance. - Subscribes to
config/esp32_1/threshold. - Turns LED on when
distance < threshold.
Before flashing:
- Set Wi-Fi credentials in
src/main.cpp. - Set broker IP in
src/main.cpp(mqtt_server). - Optionally change
device_idand matching topics.
Build/upload with PlatformIO (example):
pio run
pio run --target upload
pio device monitor -b 115200- Device publishes:
sensors/<device_id>/distance-> numeric payload (cm)
- Backend/dashboard subscribe:
sensors/+/+
- Backend/dashboard publish config:
config/<device_id>/threshold-> numeric payload
- Device/simulator subscribe config:
- real firmware:
config/<device_id>/threshold - simulator:
config/+/threshold
- real firmware:
Import "paho.mqtt.client" could not be resolved:- Activate venv and run
pip install -r requirements.txt.
- Activate venv and run
- No data appears in backend/dashboard:
- Verify broker is running and reachable.
- Ensure all scripts use the same
--brokerand--port. - Confirm simulator/ESP32 is publishing to
sensors/...topics.
- Threshold updates do not apply:
- Confirm exact
device_id(for exampleesp32_1). - Check that config topic matches
config/<device_id>/threshold.
- Confirm exact
- Dashboard not reachable from another device:
- Run with
--host 0.0.0.0and allow firewall access to chosen port.
- Run with
# Setup
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
# Simulate devices
python simulator.py --broker 127.0.0.1 --count 3
# CLI backend
python backend.py --broker 127.0.0.1
# Web dashboard
python dashboard.py --broker 127.0.0.1
# One-command demo
./run_demo.sh
./stop_demo.sh