From the AVR power stage inside a charger, up through the gateway on the wire, across the MQTT broker and cloud backend, into the database and every operator console — the complete data & control path, built ground‑up.
Do this first. Over Bluetooth, the operator picks the site Wi-Fi (scanned on the phone — they only type the password) and the target chargers; the gateway then hops charger-to-charger to hand out the credentials and confirms back. No cables, no cloud. Press play.
Same chargers, same inHand router — but watch what the cloud path looks like before the Spark gateway box (the charger’s stock OCPP link) versus with it. The offline modes are the whole point: native OCPP dies when the internet drops; the gateway keeps charging over BLE.
Tip: hover any box on the map for details. Switch Past / Present / Future above, and toggle online / offline to see what survives an internet outage.
The exact seven-layer signal path — one charge session from a charger in the ground to your browser. Hit Play and it builds itself stage by stage. Cyan streams carry telemetry up; red streams carry commands down.
Pilot & relay control, current setting, live metering (V·A·Wh·W), safety trips (GFCI, ground, over-current/voltage/temp).
WiFi (AP+STA), local HTTP API on port 80, OCPP 1.6J client, logging, config, OTA. Flashes the AVR firmware.
Two independent control planes: a cloud OCPP link to a CSMS, and a local HTTP path the site gateway drives — so charging keeps working even with no CSMS.
The gateway discovers chargers by scanning the subnet (TCP :80 probe → fingerprint /chargerInfo by serial_Number), then caches serial→IP so reboots skip the sweep.
Bare-metal Arduino/FreeRTOS. Core 0 polls the LAN; Core 1 keeps MQTT alive. TLS via SSLClient/BearSSL. Offline buffer on SD card (JSONL).
Same MQTT contract as the ESP32. Polls every 2 s, buffers offline to SQLite (auto-replay at QoS 1), serves a local UI + LAN API, OTA with crash-loop rollback.
Pick a bay, tap to pay, start charging — a local touchscreen wired to the RPi. Works with no internet.
one gateway per site · interchangeable · identical broker contract
Every gateway and every client is just another authenticated MQTT client here. Retained status + heartbeat give any newcomer instant fleet state; last-will marks a gateway offline.
The only MQTT client for the web console — it holds the broker credentials, keeps one always-on connection (no cold-launch node-numbering race), fans commands/OTA out, and mirrors traffic to the DB. Also runs the auth-key rotation and validates logins via Supabase.
Talks straight to the broker (holds creds itself) with three transports and automatic fallback: MQTT → BLE-Op → AP HTTP.
The phone app currently connects to the broker directly (same pattern as the kiosk). The roadmap is to point it at the console backend instead — so broker credentials live in exactly one place and the mobile client becomes a thin, backend-authenticated consumer, just like the web console.
Every MQTT in/out message (except the high-rate telemetry stream) is captured for backtracking; login is validated against Supabase Auth; auth-key issuance and console settings are persisted here.
Login-gated fleet view & control from anywhere: live gateways/nodes, per-node start/stop/set, gateway reboots & factory resets, OTA rollout, live MQTT log, the Supabase database browser, and BLE auth-key rotation — all through the backend over HTTPS + WebSocket.
L:<cmd>:<val> line protocol at 38400./logs as a continuous text stream.Spark_<serial> @ 192.168.4.1./chargerInfo; cache serial→IP; ownership/claim model./wifi_status · /charger_activation_state · /avr_logs · /GetMeterValue (ESP ~sub-second, RPi 2 s).offline_data.sparkcharger/gateway/…; retained status/heartbeat + last-will..bin and RPi .tar.gz release bundles.{"url":"…"} to …/ota.status topic; the node pulls its image over FTP via /esp_OTA / AvrUpdate.The backend generates a fresh random key per online gateway on an interval, pushes
set_auth_key=<key> over MQTT, and records ok/fail in Supabase. That same key is
what BLE-Op and the AP HTTP API validate — so when a gateway drops offline, a phone can still operate it locally with the
last key it successfully received, until it’s back online and rotates again.
A LoRa module at the site power panel streams the building’s live energy-meter readings to a LoRa receiver on the gateway. LoRa’s long range covers the whole site — any LoRa meter or sensor is reachable with no extra wiring.
| Topic | Direction | Retained | Purpose |
|---|---|---|---|
sparkcharger/gateway/status | gw → cloud | retained | Global fleet roster & online/offline status |
sparkcharger/<mac>/telemetry/<serial>/data | gw → cloud | Per-charger live telemetry | |
sparkcharger/<mac>/telemetry/<serial>/info | gw → cloud | Per-charger static/identity info | |
sparkcharger/<mac>/heartbeat | gw → cloud | Gateway liveness | |
sparkcharger/<mac>/response | gw → cloud | Command replies & chunked bodies | |
sparkcharger/<mac>/offline_data | gw → cloud | Buffered SD replay after reconnect | |
sparkcharger/<mac>/cmd | cloud → gw | Control commands (QoS 1) | |
sparkcharger/<mac>/ota | cloud → gw | Targeted firmware OTA | |
sparkcharger/gateway/ota | cloud → fleet | retained | Fleet-wide firmware OTA |