# MQTT Protocol Reference Complete reference for the MQTT topic structure, payload formats, command/response protocol, and quality-of-service settings used between ESP32 devices and the console application. --- ## 1. Topic Structure All topics use the following prefix convention: ``` {prefix}/{device_id}/{message_type} ``` - **prefix**: Configurable, default `acuvim` - **device_id**: Unique device identifier, format `ACV-AABBCCDDEEFF` (derived from MAC address) - **message_type**: One of the types listed below ### Topic Map | Topic | Direction | QoS | Retain | Description | |-------|-----------|-----|--------|-------------| | `acuvim/{device_id}/telemetry` | Device -> Broker | 0 | No | Acuvim II power meter readings | | `acuvim/{device_id}/heartbeat` | Device -> Broker | 1 | No | Device health and status | | `acuvim/{device_id}/cmd` | Console -> Device | 1 | No | Commands to the device | | `acuvim/{device_id}/resp` | Device -> Console | 1 | No | Command responses | | `acuvim/{device_id}/status` | Device / Broker (LWT) | 1 | Yes | Online/offline status | | `acuvim/{device_id}/alerts` | Device -> Broker | 1 | No | Threshold violation alerts | | `devices/register` | Device -> Broker | 1 | No | Device self-registration | ### Wildcard Subscriptions (Console) The console application subscribes to the following wildcard topics to receive messages from all devices: ``` acuvim/+/telemetry acuvim/+/heartbeat acuvim/+/resp acuvim/+/status acuvim/+/alerts devices/register ``` --- ## 2. Telemetry ### Topic ``` acuvim/{device_id}/telemetry ``` ### QoS and Delivery - **QoS 0** (at most once): Telemetry is high-frequency data. Occasional message loss is acceptable. Using QoS 0 reduces overhead, especially over GSM. - **Retain:** No - **Publish interval:** Configurable via `poll_interval_sec`, default 5 seconds on WiFi, 30 seconds on GSM. ### Payload Format ```json { "ts": 1716000000, "dev": "ACV-AABBCCDDEEFF", "v": { "a": 230.1, "b": 231.4, "c": 229.8, "ab": 399.2, "bc": 400.1, "ca": 398.7 }, "i": { "a": 15.2, "b": 14.8, "c": 15.5 }, "p": { "total": 10.5, "a": 3.5, "b": 3.4, "c": 3.6, "reactive": 2.1, "apparent": 10.7, "pf": 0.98 }, "f": 50.01, "e": { "imp_act": 12345.6, "exp_act": 0.0, "imp_react": 1234.5, "exp_react": 0.0 }, "d": { "act": 10.2, "max_act": 15.0, "react": 2.0 }, "thd": { "va": 2.1, "vb": 2.3, "vc": 2.0, "ia": 5.4, "ib": 5.1, "ic": 5.6 }, "conn": "wifi", "rssi": -45, "src": "live" } ``` ### Field Reference | Field Path | Type | Unit | Description | |------------|------|------|-------------| | `ts` | integer | epoch seconds (UTC) | Timestamp of the reading | | `dev` | string | - | Device ID | | `v.a` | float | V | Phase A voltage (L-N) | | `v.b` | float | V | Phase B voltage (L-N) | | `v.c` | float | V | Phase C voltage (L-N) | | `v.ab` | float | V | Line voltage A-B | | `v.bc` | float | V | Line voltage B-C | | `v.ca` | float | V | Line voltage C-A | | `i.a` | float | A | Phase A current | | `i.b` | float | A | Phase B current | | `i.c` | float | A | Phase C current | | `p.total` | float | kW | Total active power | | `p.a` | float | kW | Phase A active power | | `p.b` | float | kW | Phase B active power | | `p.c` | float | kW | Phase C active power | | `p.reactive` | float | kVAR | Total reactive power | | `p.apparent` | float | kVA | Total apparent power | | `p.pf` | float | - | Total power factor (-1.0 to 1.0) | | `f` | float | Hz | Line frequency | | `e.imp_act` | float | kWh | Import active energy (accumulated) | | `e.exp_act` | float | kWh | Export active energy (accumulated) | | `e.imp_react` | float | kVARh | Import reactive energy (accumulated) | | `e.exp_react` | float | kVARh | Export reactive energy (accumulated) | | `d.act` | float | kW | Active power demand | | `d.max_act` | float | kW | Maximum active power demand | | `d.react` | float | kVAR | Reactive power demand | | `thd.va` | float | % | Voltage THD Phase A | | `thd.vb` | float | % | Voltage THD Phase B | | `thd.vc` | float | % | Voltage THD Phase C | | `thd.ia` | float | % | Current THD Phase A | | `thd.ib` | float | % | Current THD Phase B | | `thd.ic` | float | % | Current THD Phase C | | `conn` | string | - | Active transport: `"wifi"` or `"gsm"` | | `rssi` | integer | dBm | Signal strength (WiFi RSSI or GSM equivalent) | | `src` | string | - | Data source: `"live"` (real-time) or `"sd"` (buffered from SD card) | ### Notes - All floating-point values are IEEE 754 single-precision, read from Modbus as big-endian 32-bit floats across two consecutive registers. - Short JSON keys are used to minimize payload size, which is important when transmitting over GSM. - The `thd` group may be omitted when transmitting over GSM to reduce payload size. - When replaying buffered data from the SD card, `src` is set to `"sd"` and the `ts` reflects the original measurement time. --- ## 3. Heartbeat ### Topic ``` acuvim/{device_id}/heartbeat ``` ### QoS and Delivery - **QoS 1** (at least once): Heartbeats are critical for device health monitoring. Guaranteed delivery ensures the console can accurately track device status. - **Retain:** No - **Publish interval:** Configurable via `heartbeat_interval_sec`, default 60 seconds. - Sent immediately on boot, on transport switch, and on demand via the `set_heartbeat` command. ### Payload Format ```json { "ts": 1716000000, "dev": "ACV-AABBCCDDEEFF", "fw": "1.2.0", "up": 86400, "boot": 3, "hb": 1440, "conn": { "type": "wifi", "wifi": { "ssid": "SiteNetwork", "rssi": -45, "ip": "192.168.1.100" }, "gsm": { "enabled": true, "connected": false, "signal": 0, "operator": "" }, "mqtt": true }, "health": { "heap_free": 120000, "heap_min": 95000, "psram_free": 3800000, "cpu_temp": 52.3, "reset_reason": "POWER_ON", "uptime_sec": 86400 }, "modbus": { "connected": true, "success": 17280, "errors": 12, "error_rate": 0.07, "last_error": 0, "last_read_ms": 45 }, "sd": { "available": true, "queued": 0, "free_mb": 3800 }, "ota": { "version": "1.2.0", "partition": "app0", "update_available": false } } ``` ### Field Reference | Field Path | Type | Description | |------------|------|-------------| | `ts` | integer | UTC epoch timestamp | | `dev` | string | Device ID | | `fw` | string | Current firmware version (semver) | | `up` | integer | Uptime in seconds since last boot | | `boot` | integer | Total boot count (persisted in NVS, increments each restart) | | `hb` | integer | Heartbeat sequence number since last boot | | `conn.type` | string | Active transport: `"wifi"` or `"gsm"` | | `conn.wifi.ssid` | string | Connected WiFi SSID | | `conn.wifi.rssi` | integer | WiFi signal strength in dBm | | `conn.wifi.ip` | string | Assigned IP address | | `conn.gsm.enabled` | boolean | Whether GSM is enabled in config | | `conn.gsm.connected` | boolean | Whether GSM is currently connected | | `conn.gsm.signal` | integer | GSM signal quality (0-31 CSQ scale) | | `conn.gsm.operator` | string | Mobile network operator name | | `conn.mqtt` | boolean | Whether MQTT is currently connected | | `health.heap_free` | integer | Current free heap memory in bytes | | `health.heap_min` | integer | Minimum free heap since boot (leak detection) | | `health.psram_free` | integer | Free PSRAM in bytes | | `health.cpu_temp` | float | ESP32 internal temperature in Celsius | | `health.reset_reason` | string | Last reset reason (see table below) | | `health.uptime_sec` | integer | Uptime in seconds | | `modbus.connected` | boolean | Whether last Modbus read succeeded | | `modbus.success` | integer | Total successful Modbus reads since boot | | `modbus.errors` | integer | Total failed Modbus reads since boot | | `modbus.error_rate` | float | Error percentage (errors / total * 100) | | `modbus.last_error` | integer | Last Modbus error code (0 = none) | | `modbus.last_read_ms` | integer | Duration of last complete read cycle in ms | | `sd.available` | boolean | Whether an SD card is detected | | `sd.queued` | integer | Number of buffered telemetry records awaiting drain | | `sd.free_mb` | integer | Free space on SD card in megabytes | | `ota.version` | string | Currently running firmware version | | `ota.partition` | string | Active OTA partition (`"app0"` or `"app1"`) | | `ota.update_available` | boolean | Whether a newer firmware version is available | ### Reset Reason Values | Value | Meaning | |-------|---------| | `POWER_ON` | Normal power-on reset | | `EXTERNAL` | External reset pin | | `SW_RESET` | Software restart (e.g., `ESP.restart()`) | | `PANIC` | Guru Meditation / exception handler | | `INT_WDT` | Interrupt watchdog timeout | | `TASK_WDT` | Task watchdog timeout | | `WDT` | Other watchdog timeout | | `DEEP_SLEEP` | Wake from deep sleep | | `BROWNOUT` | Brownout reset (low voltage) | | `UNKNOWN` | Unrecognized reset reason | ### Console Health Detection The console uses heartbeat timing to determine device status: | Condition | Status | |-----------|--------| | Heartbeat received within 1x interval | Online | | No heartbeat for 3x interval | Degraded | | No heartbeat for 5x interval | Offline | --- ## 4. Command / Response Protocol ### Command (Console to Device) **Topic:** `acuvim/{device_id}/cmd` **QoS:** 1 **Retain:** No ```json { "cmd": "", "request_id": "", "params": { } } ``` - `cmd`: The command name (see complete list below). - `request_id`: A unique string generated by the console to correlate the response. Format is typically a UUID or short identifier like `"cmd-a1b2c3"`. - `params`: Command-specific parameters. Omit or pass `{}` for commands with no parameters. ### Response (Device to Console) **Topic:** `acuvim/{device_id}/resp` **QoS:** 1 **Retain:** No ```json { "request_id": "", "status": "success", "data": { }, "message": "Human-readable description" } ``` - `request_id`: Must match the `request_id` from the originating command. - `status`: Either `"success"` or `"error"`. - `data`: Command-specific response data. May be omitted for simple acknowledgements. - `message`: Human-readable status message. ### Command Timeout If the console does not receive a response within 60 seconds, the command is marked as `"timeout"`. The device should respond as quickly as possible; most commands respond in under 2 seconds. --- ## 5. Complete Command Reference ### 5.1 ping Connectivity test. The device responds with a pong. **Request:** ```json { "cmd": "ping", "request_id": "ping-001" } ``` **Response:** ```json { "request_id": "ping-001", "status": "success", "data": { "pong": true }, "message": "pong" } ``` --- ### 5.2 get_config Returns the full device configuration (passwords are masked). **Request:** ```json { "cmd": "get_config", "request_id": "cfg-001" } ``` **Response:** ```json { "request_id": "cfg-001", "status": "success", "data": { "device_id": "ACV-AABBCCDDEEFF", "wifi": { "ssid": "SiteNetwork", "enabled": true }, "mqtt": { "broker": "console.example.com", "port": 8883, "tls": true, "topic_prefix": "acuvim" }, "gsm": { "apn": "internet", "enabled": true }, "modbus": { "slave_addr": 1, "baud_rate": 9600, "poll_interval": 5 }, "heartbeat_interval": 60, "console_url": "https://console.example.com", "firmware_version": "1.2.0" } } ``` --- ### 5.3 get_status Returns the current device status including health metrics. **Request:** ```json { "cmd": "get_status", "request_id": "stat-001" } ``` **Response:** ```json { "request_id": "stat-001", "status": "success", "data": { "uptime": 86400, "heap_free": 120000, "heap_min": 95000, "wifi_connected": true, "wifi_rssi": -45, "gsm_connected": false, "mqtt_connected": true, "modbus_connected": true, "modbus_errors": 12, "sd_available": true, "sd_queued": 0, "boot_count": 3, "reset_reason": "POWER_ON" } } ``` --- ### 5.4 get_telemetry Returns the most recent Acuvim II reading. **Request:** ```json { "cmd": "get_telemetry", "request_id": "tel-001" } ``` **Response:** ```json { "request_id": "tel-001", "status": "success", "data": { "ts": 1716000000, "v": { "a": 230.1, "b": 231.4, "c": 229.8 }, "i": { "a": 15.2, "b": 14.8, "c": 15.5 }, "p": { "total": 10.5, "pf": 0.98 }, "f": 50.01 } } ``` --- ### 5.5 restart Restarts the ESP32. The device publishes the response before restarting. **Request:** ```json { "cmd": "restart", "request_id": "rst-001" } ``` **Response:** ```json { "request_id": "rst-001", "status": "success", "message": "Restarting in 2 seconds" } ``` --- ### 5.6 factory_reset Clears all NVS configuration and restarts the device into AP mode. Requires explicit confirmation. **Request:** ```json { "cmd": "factory_reset", "request_id": "fr-001", "params": { "confirm": true } } ``` **Response:** ```json { "request_id": "fr-001", "status": "success", "message": "Factory reset complete, restarting into AP mode" } ``` If `confirm` is missing or `false`: ```json { "request_id": "fr-001", "status": "error", "message": "Factory reset requires params.confirm = true" } ``` --- ### 5.7 wifi_scan Scans for available WiFi networks and returns the results. **Request:** ```json { "cmd": "wifi_scan", "request_id": "scan-001" } ``` **Response:** ```json { "request_id": "scan-001", "status": "success", "data": { "networks": [ { "ssid": "SiteNetwork", "rssi": -40, "enc": "WPA2", "ch": 6 }, { "ssid": "GuestWifi", "rssi": -65, "enc": "WPA2", "ch": 11 }, { "ssid": "OpenNet", "rssi": -78, "enc": "OPEN", "ch": 1 } ] } } ``` --- ### 5.8 wifi_set Updates WiFi credentials. The device will disconnect from the current network and connect to the new one. **Request:** ```json { "cmd": "wifi_set", "request_id": "wset-001", "params": { "ssid": "NewNetwork", "password": "newpassword123" } } ``` **Response:** ```json { "request_id": "wset-001", "status": "success", "message": "WiFi credentials updated, reconnecting" } ``` --- ### 5.9 wifi_disconnect Disconnects from the current WiFi network without clearing credentials. **Request:** ```json { "cmd": "wifi_disconnect", "request_id": "wdisc-001" } ``` **Response:** ```json { "request_id": "wdisc-001", "status": "success", "message": "WiFi disconnected" } ``` --- ### 5.10 mqtt_set Updates the MQTT broker configuration. The device will disconnect and reconnect with the new settings. **Request:** ```json { "cmd": "mqtt_set", "request_id": "mset-001", "params": { "broker": "mqtt.newserver.com", "port": 8883, "username": "ACV-AABBCCDDEEFF", "password": "newpassword", "topic_prefix": "acuvim", "tls": true } } ``` **Response:** ```json { "request_id": "mset-001", "status": "success", "message": "MQTT configuration updated, reconnecting" } ``` **Warning:** This command can render the device unreachable via MQTT if incorrect values are provided. Ensure fallback access via the captive portal. --- ### 5.11 gsm_set Updates GSM/cellular configuration. **Request:** ```json { "cmd": "gsm_set", "request_id": "gset-001", "params": { "apn": "internet", "username": "", "password": "", "enabled": true } } ``` **Response:** ```json { "request_id": "gset-001", "status": "success", "message": "GSM configuration updated" } ``` --- ### 5.12 transport_priority Sets the transport priority order for network failover. **Request:** ```json { "cmd": "transport_priority", "request_id": "tp-001", "params": { "priority": "wifi_first" } } ``` Valid values for `priority`: | Value | Behaviour | |-------|-----------| | `wifi_first` | Prefer WiFi, fall back to GSM (default) | | `gsm_first` | Prefer GSM, fall back to WiFi | | `wifi_only` | WiFi only, no GSM fallback | | `gsm_only` | GSM only, no WiFi | **Response:** ```json { "request_id": "tp-001", "status": "success", "message": "Transport priority set to wifi_first" } ``` --- ### 5.13 sleep_set Configures deep sleep mode. **Request:** ```json { "cmd": "sleep_set", "request_id": "slp-001", "params": { "enabled": true, "sleep_min": 15, "wake_sec": 60 } } ``` - `sleep_min`: Duration of deep sleep in minutes. - `wake_sec`: Duration the device stays awake to transmit data. **Response:** ```json { "request_id": "slp-001", "status": "success", "message": "Sleep mode configured: 15min sleep, 60s wake" } ``` --- ### 5.14 modbus_set Updates Modbus RS485 communication parameters. **Request:** ```json { "cmd": "modbus_set", "request_id": "mb-001", "params": { "slave_addr": 1, "baud_rate": 9600, "poll_interval": 5 } } ``` **Response:** ```json { "request_id": "mb-001", "status": "success", "message": "Modbus configuration updated" } ``` --- ### 5.15 console_set Sets the console application URL used for OTA firmware downloads. **Request:** ```json { "cmd": "console_set", "request_id": "curl-001", "params": { "url": "https://console.example.com" } } ``` **Response:** ```json { "request_id": "curl-001", "status": "success", "message": "Console URL updated" } ``` --- ### 5.16 ota_update Triggers an OTA firmware update. The device will download the binary from the specified URL, verify the checksum, and install it. **Request:** ```json { "cmd": "ota_update", "request_id": "ota-001", "params": { "url": "https://console.example.com/api/firmware/download/1.3.0", "version": "1.3.0", "checksum": "a1b2c3d4e5f6..." } } ``` **Response (immediate acknowledgement):** ```json { "request_id": "ota-001", "status": "success", "message": "OTA update started, downloading 1.3.0" } ``` The device will reboot after successful installation. The console should monitor for the device to come back online with the new firmware version in its next heartbeat. --- ### 5.17 ota_check Checks with the console whether a firmware update is available. **Request:** ```json { "cmd": "ota_check", "request_id": "ochk-001" } ``` **Response:** ```json { "request_id": "ochk-001", "status": "success", "data": { "update_available": true, "current_version": "1.2.0", "available_version": "1.3.0", "url": "https://console.example.com/api/firmware/download/1.3.0", "size": 1048576, "checksum": "a1b2c3d4e5f6...", "mandatory": false } } ``` --- ### 5.18 set_heartbeat Changes the heartbeat reporting interval. **Request:** ```json { "cmd": "set_heartbeat", "request_id": "hb-001", "params": { "interval_sec": 30 } } ``` **Response:** ```json { "request_id": "hb-001", "status": "success", "message": "Heartbeat interval set to 30 seconds" } ``` --- ### 5.19 set_alerts Configures alert thresholds for the Acuvim II health monitoring. **Request:** ```json { "cmd": "set_alerts", "request_id": "alt-001", "params": { "thresholds": { "overvoltage": 260.0, "undervoltage": 200.0, "overcurrent_factor": 1.2, "voltage_imbalance_pct": 10.0, "current_imbalance_pct": 20.0, "frequency_low": 49.5, "frequency_high": 50.5, "low_power_factor": 0.85, "high_thd": 8.0 } } } ``` **Response:** ```json { "request_id": "alt-001", "status": "success", "message": "Alert thresholds updated" } ``` --- ### 5.20 register_ack Sent by the console to acknowledge a device registration. This is the only command initiated by the console without a prior user action. **Request (Console to Device):** ```json { "cmd": "register_ack", "request_id": "reg-001", "params": { "status": "accepted", "device_name": "Building A - Main Incomer" } } ``` The device saves the acknowledgement to NVS and uses `device_name` for display in the captive portal. --- ## 6. Alert Payload ### Topic ``` acuvim/{device_id}/alerts ``` ### QoS and Delivery - **QoS 1**: Alerts represent important events that should not be lost. - **Retain:** No ### Payload Format ```json { "ts": 1716000000, "dev": "ACV-AABBCCDDEEFF", "alert": "overvoltage", "severity": "warning", "message": "Phase A voltage 265.3V exceeds 260V threshold", "phase": "A", "value": 265.3, "threshold": 260.0 } ``` ### Field Reference | Field | Type | Description | |-------|------|-------------| | `ts` | integer | UTC epoch timestamp | | `dev` | string | Device ID | | `alert` | string | Alert type identifier (see table below) | | `severity` | string | `"info"`, `"warning"`, or `"critical"` | | `message` | string | Human-readable description | | `phase` | string | Affected phase (`"A"`, `"B"`, `"C"`, or `null` for non-phase-specific alerts) | | `value` | float | Measured value that triggered the alert | | `threshold` | float | Configured threshold that was exceeded | ### Alert Types | Alert Type | Default Threshold | Severity | Description | |------------|-------------------|----------|-------------| | `overvoltage` | > 260 V | warning | Phase voltage exceeds upper limit | | `undervoltage` | < 200 V | warning | Phase voltage below lower limit | | `overcurrent` | > CT rated x 1.2 | warning | Phase current exceeds rated capacity | | `voltage_imbalance` | > 10% difference | info | Excessive voltage imbalance between phases | | `current_imbalance` | > 20% difference | info | Excessive current imbalance between phases | | `frequency_deviation` | < 49.5 or > 50.5 Hz | warning | Frequency outside normal range | | `low_power_factor` | < 0.85 | info | Poor power factor | | `high_thd` | > 8% | info | High total harmonic distortion | | `modbus_loss` | 5 consecutive errors | critical | Lost communication with Acuvim II | | `modbus_high_error_rate` | > 5% over 1 hour | warning | Elevated Modbus error rate | ### Alert Deduplication The device does not re-publish the same alert while the condition persists. An alert is considered cleared when the value returns within the threshold minus a hysteresis margin (e.g., overvoltage triggers at 260V, clears at 255V). After clearing, the alert can trigger again if the condition reoccurs. --- ## 7. Device Registration ### Topic ``` devices/register ``` ### QoS and Delivery - **QoS 1**: Registration must be reliably delivered. - **Retain:** No ### Payload Format ```json { "device_id": "ACV-AABBCCDDEEFF", "mac": "AA:BB:CC:DD:EE:FF", "firmware": "1.2.0", "hardware": "TTGO T-Call v1.4", "chip": "ESP32-WROVER-B", "imei": "123456789012345", "capabilities": ["wifi", "gsm", "sd", "modbus", "ota"], "ts": 1716000000 } ``` ### Field Reference | Field | Type | Description | |-------|------|-------------| | `device_id` | string | Unique device ID derived from MAC address | | `mac` | string | WiFi MAC address | | `firmware` | string | Current firmware version | | `hardware` | string | Board/hardware identification | | `chip` | string | ESP32 chip model | | `imei` | string | GSM modem IMEI (empty string if no GSM module) | | `capabilities` | array | List of available features, dynamically detected at boot | | `ts` | integer | UTC epoch timestamp | ### Capabilities | Capability | Meaning | |------------|---------| | `wifi` | WiFi radio available (always present) | | `gsm` | GSM modem detected and operational | | `sd` | SD card detected and mounted | | `modbus` | Modbus RS485 interface available (always present) | | `ota` | OTA update support (always present) | ### Registration Triggers The device publishes a registration message: 1. On first boot (no registration acknowledgement stored in NVS). 2. After a factory reset (NVS cleared). 3. After a firmware update (re-register with new version). 4. Every 24 hours (periodic re-registration to confirm presence). --- ## 8. Last Will and Testament (LWT) ### Topic ``` acuvim/{device_id}/status ``` ### QoS, Retain, and Behaviour - **QoS 1** - **Retain: Yes** (so new subscribers immediately know the device state) The LWT is configured at MQTT connect time. If the device disconnects unexpectedly (network loss, crash, power failure), the broker publishes the LWT message on the device's behalf. ### LWT Payload (Published by Broker on Unexpected Disconnect) ```json { "status": "offline", "timestamp": 0 } ``` The `timestamp` in the LWT is set to `0` because the broker publishes it and does not know the actual time. The console uses the message receipt time instead. ### Online Payload (Published by Device on Connect) Immediately after connecting, the device publishes to the same topic: ```json { "status": "online", "timestamp": 1716000000, "ip": "192.168.1.100", "fw": "1.2.0" } ``` This overwrites the retained LWT message, so any new subscriber will see the device as online. --- ## 9. QoS Summary | Message Type | QoS | Rationale | |--------------|-----|-----------| | Telemetry | 0 | High-frequency, loss tolerable, reduces broker and network overhead | | Heartbeat | 1 | Critical for status tracking, must be reliably delivered | | Command (cmd) | 1 | Must reach the device to trigger actions | | Response (resp) | 1 | Console needs to confirm command execution | | Status (LWT) | 1 | Online/offline state must be reliably propagated | | Alerts | 1 | Important events that should not be missed | | Registration | 1 | Device must be registered in the console | --- ## 10. MQTT Client Configuration ### Device (ESP32) | Parameter | Value | |-----------|-------| | Client ID | Device ID (e.g., `ACV-AABBCCDDEEFF`) | | Keep Alive | 60 seconds | | Clean Session | `false` (broker retains subscriptions across reconnects) | | Max Payload Size | 1024 bytes (PubSubClient default, increase if needed) | | Reconnect Backoff | 10s, 20s, 40s, 60s (max) | ### Console (.NET) | Parameter | Value | |-----------|-------| | Client ID | `acuvim-console` | | Keep Alive | 60 seconds | | Clean Session | `false` | | Protocol | MQTT v5 (via MQTTnet) | | Auto Reconnect | Yes | --- ## 11. Topic Examples For a device with ID `ACV-A1B2C3D4E5F6`: ``` acuvim/ACV-A1B2C3D4E5F6/telemetry <- Device publishes meter readings acuvim/ACV-A1B2C3D4E5F6/heartbeat <- Device publishes health data acuvim/ACV-A1B2C3D4E5F6/cmd <- Console publishes commands acuvim/ACV-A1B2C3D4E5F6/resp <- Device publishes command responses acuvim/ACV-A1B2C3D4E5F6/status <- Online/offline (LWT) acuvim/ACV-A1B2C3D4E5F6/alerts <- Device publishes threshold alerts devices/register <- Device publishes registration ```