It seems to me that this document is compliant :
UNO Q Sensors Dashboard – Observed End-to-End Data Flow
This document describes the complete data path of the project, from Modulino sensors connected to the STM32U585 (MCU) to the Web dashboard displayed in a browser.
1) Components and roles
-
STM32U585 (MCU / Arduino sketch)
-
Reads Modulino sensors.
-
Sends sensor values and presence events to the Linux side using Bridge.call().
-
-
Linux side (Python / App Lab runtime)
-
Receives Bridge calls (callbacks registered via Bridge.provide()).
-
Stores the latest values in a shared in-memory state.
-
Exposes a JSON endpoint for the dashboard.
-
-
WebUI (assets/index.html)
-
Fetches the JSON endpoint periodically.
-
Updates the displayed values in the browser.
-
2) Startup sequence
-
Arduino App Lab starts the project
-
Triggers the build and deployment of the MCU sketch (handled by Arduino App CLI running on the UNO Q Linux system).
-
Starts the Python application.
-
Starts the WebUI server (serving assets/index.html).
-
-
Python initializes the WebUI and API
-
-
web = WebUI() serves ./assets/index.html.
-
web.expose_api("GET", "/api/state", api_state) exposes the JSON endpoint.
-
-
STM32 initializes Bridge and Modulino sensors
-
-
Bridge.begin() initializes MCU↔Linux Bridge communication.
-
Modulino.begin() initializes the Modulino framework.
-
light.begin(), thermo.begin(), distance.begin() initialize each Modulino module.
-
Note: a delay(5000) was used during early tests, but the system works correctly without it (it is currently commented out in the sketch).
3) Periodic sensor acquisition (1 Hz)
Every second, the STM32:
-
Updates and reads sensor values:
-
light.update() + light.getAL() → lux
-
thermo.getTemperature() → temp
-
thermo.getHumidity() → hum
-
-
Sends them to Python via Bridge:
-
- Bridge.call("update_sensors", lux, temp, hum)
Python receives the call because it registered:
- Bridge.provide("update_sensors", update_sensors)
Then Python updates the shared state:
- _state["lux"], _state["temp"], _state["hum"]
4) Presence detection event (distance sensor)
The STM32 continuously polls the distance sensor:
-
If a new distance value is available:
- mm = distance.get()
-
Presence condition:
-
- presence = (mm > 0 && mm < SEUIL_MM)
-
Rate limiting (cooldown):
-
- At most one presence notification every COOLDOWN_MS (10 seconds)
-
If triggered, send to Python:
-
- Bridge.call("presence_mm", mm)
Python receives it via:
- Bridge.provide("presence_mm", presence_mm)
Then Python stores:
-
_state["last_presence_time"] = now_str()
-
_state["last_presence_mm"] = mm
5) Web API: exposing the current state
Python exposes the current dashboard state through:
- GET /api/state
When the endpoint is called, Python returns a JSON payload containing:
-
current time (now)
-
latest sensor values (lux, temp, hum)
-
last presence info (last_presence_time, last_presence_mm)
Thread-safety note:
- Access to _state is protected by a threading.Lock().
6) Web dashboard refresh (1 second)
In assets/index.html:
-
setInterval(refresh, 1000) runs refresh() once per second.
-
refresh() performs:
-
fetch("/api/state", { cache: "no-store" })
-
parses JSON
-
updates the HTML elements:
- #now, #temp, #hum, #lux, #presence
-
Result: the browser shows near real-time sensor values.
7) Key design decisions (educational)
-
Separation of concerns
-
MCU: sensor reading + event detection
-
Python: state aggregation + JSON API
-
WebUI: visualization only
-
-
Thread safety
- Python protects _state with a lock.
-
Rate limiting
- Presence events are throttled using COOLDOWN_MS to avoid spamming.
Best regards