UNO Q – Modulino sensors dashboard using App Lab, Bridge and WebUI

@GolamMostafa

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

  1. 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).

  2. Python initializes the WebUI and API

    • web = WebUI() serves ./assets/index.html.

    • web.expose_api("GET", "/api/state", api_state) exposes the JSON endpoint.

  3. 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:

  1. Updates and reads sensor values:

    • light.update() + light.getAL() → lux

    • thermo.getTemperature() → temp

    • thermo.getHumidity() → hum

  2. 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:

  1. If a new distance value is available:

    • mm = distance.get()
  2. Presence condition:

    • presence = (mm > 0 && mm < SEUIL_MM)
  3. Rate limiting (cooldown):

    • At most one presence notification every COOLDOWN_MS (10 seconds)
  4. 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:

  1. setInterval(refresh, 1000) runs refresh() once per second.

  2. 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