Tau.Acuvim/docs/mqtt-protocol.md
Renier Forster 84a0668c54 Initial commit: Tau Acuvim IoT monitoring system
Complete IoT monitoring platform for Acuvim II power meters via ESP32.

Firmware (Phases 1-7):
- ESP32-WROVER-B (TTGO T-Call v1.4) with RS485 Modbus RTU
- WiFi STA+AP concurrent mode with GSM/GPRS failover
- Transport abstraction layer with 4 priority modes
- MQTT protocol with 20 commands, LWT, QoS, exponential backoff
- SD card offline buffering with JSONL rotation and non-blocking drain
- OTA firmware updates with dual partition rollback protection
- Watchdog timer, crash loop detection, Acuvim health monitoring
- Captive portal provisioning with AP mode

Console backend (Phase 8):
- .NET 10 minimal API with PostgreSQL + EF Core
- JWT authentication, SignalR real-time updates
- MQTTnet 5.x bridge service with health monitoring
- Device, telemetry, firmware, alert, group management
- Rate limiting, security headers, Swagger/OpenAPI

Frontend (Phase 9):
- React 18 + TypeScript + Vite with Ant Design 5
- ECharts telemetry visualization, TanStack Query
- SignalR live updates, device management UI
- Dashboard, fleet management, firmware deployment

Testing & Production (Phase 10):
- 28 firmware unit tests (Modbus, JSON, config, version)
- 23 xUnit backend tests (device, telemetry, command, alert)
- Docker Compose with nginx, TLS MQTT, PostgreSQL
- Production deployment, commissioning, and troubleshooting docs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-16 19:05:32 +02:00

26 KiB

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

{
  "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

{
  "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

{
  "cmd": "<command_name>",
  "request_id": "<unique_identifier>",
  "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

{
  "request_id": "<matching_identifier>",
  "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:

{
  "cmd": "ping",
  "request_id": "ping-001"
}

Response:

{
  "request_id": "ping-001",
  "status": "success",
  "data": { "pong": true },
  "message": "pong"
}

5.2 get_config

Returns the full device configuration (passwords are masked).

Request:

{
  "cmd": "get_config",
  "request_id": "cfg-001"
}

Response:

{
  "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:

{
  "cmd": "get_status",
  "request_id": "stat-001"
}

Response:

{
  "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:

{
  "cmd": "get_telemetry",
  "request_id": "tel-001"
}

Response:

{
  "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:

{
  "cmd": "restart",
  "request_id": "rst-001"
}

Response:

{
  "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:

{
  "cmd": "factory_reset",
  "request_id": "fr-001",
  "params": {
    "confirm": true
  }
}

Response:

{
  "request_id": "fr-001",
  "status": "success",
  "message": "Factory reset complete, restarting into AP mode"
}

If confirm is missing or false:

{
  "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:

{
  "cmd": "wifi_scan",
  "request_id": "scan-001"
}

Response:

{
  "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:

{
  "cmd": "wifi_set",
  "request_id": "wset-001",
  "params": {
    "ssid": "NewNetwork",
    "password": "newpassword123"
  }
}

Response:

{
  "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:

{
  "cmd": "wifi_disconnect",
  "request_id": "wdisc-001"
}

Response:

{
  "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:

{
  "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:

{
  "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:

{
  "cmd": "gsm_set",
  "request_id": "gset-001",
  "params": {
    "apn": "internet",
    "username": "",
    "password": "",
    "enabled": true
  }
}

Response:

{
  "request_id": "gset-001",
  "status": "success",
  "message": "GSM configuration updated"
}

5.12 transport_priority

Sets the transport priority order for network failover.

Request:

{
  "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:

{
  "request_id": "tp-001",
  "status": "success",
  "message": "Transport priority set to wifi_first"
}

5.13 sleep_set

Configures deep sleep mode.

Request:

{
  "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:

{
  "request_id": "slp-001",
  "status": "success",
  "message": "Sleep mode configured: 15min sleep, 60s wake"
}

5.14 modbus_set

Updates Modbus RS485 communication parameters.

Request:

{
  "cmd": "modbus_set",
  "request_id": "mb-001",
  "params": {
    "slave_addr": 1,
    "baud_rate": 9600,
    "poll_interval": 5
  }
}

Response:

{
  "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:

{
  "cmd": "console_set",
  "request_id": "curl-001",
  "params": {
    "url": "https://console.example.com"
  }
}

Response:

{
  "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:

{
  "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):

{
  "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:

{
  "cmd": "ota_check",
  "request_id": "ochk-001"
}

Response:

{
  "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:

{
  "cmd": "set_heartbeat",
  "request_id": "hb-001",
  "params": {
    "interval_sec": 30
  }
}

Response:

{
  "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:

{
  "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:

{
  "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):

{
  "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

{
  "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

{
  "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)

{
  "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:

{
  "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