Device Protocol
MQTT message formats, topic structure, HMAC signing, and protocol specification for CPI device communication.
Topic Structure
All MQTT topics follow the prefix pattern cpi/{deviceId}/:
| Topic | Direction | Description |
|---|---|---|
cpi/{deviceId}/telemetry | Device → Platform | Sensor data readings |
cpi/{deviceId}/status | Device → Platform | Device online/offline status |
cpi/{deviceId}/commands | Platform → Device | Remote commands |
cpi/{deviceId}/ack | Device → Platform | Command acknowledgments |
The deviceId is a UUID assigned during device registration.
Protocol Version
Current protocol version: 1.0
All messages are JSON-encoded UTF-8 strings published with QoS 1. Maximum message size: 8KB (enforced by Mosquitto).
Message Formats
Telemetry
Published by the device to report sensor readings.
{
"ts": 1700000000000,
"nonce": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"data": {
"temperature": 22.5,
"humidity": 45.2,
"pressure": 1013.25
},
"sig": "a1b2c3d4...64-hex-chars"
}| Field | Type | Description |
|---|---|---|
ts | integer | Millisecond epoch timestamp |
nonce | string | UUID v4, unique per message |
data | object | Key-value sensor readings (string keys, numeric values) |
sig | string | HMAC-SHA256 hex signature |
The data object supports arbitrary keys. Common examples: temperature, humidity, pressure, voltage, current, rpm.
Status
Published by the device to report connectivity state.
{
"ts": 1700000000000,
"nonce": "d1e2f3a4-b5c6-7890-d1e2-f3a4b5c67890",
"status": "ONLINE",
"sig": "b2c3d4e5...64-hex-chars"
}| Status Value | When to Publish |
|---|---|
ONLINE | On successful MQTT connection |
OFFLINE | As MQTT Last Will and Testament (LWT) |
MAINTENANCE | During firmware update or calibration |
ERROR | On device error requiring attention |
Set OFFLINE as the MQTT LWT message so it's automatically published if the device disconnects unexpectedly.
Command (Platform → Device)
Received by the device on the commands topic.
{
"cmdId": "e5f6a7b8-c9d0-1234-e5f6-a7b8c9d01234",
"ts": 1700000000000,
"action": "REBOOT",
"payload": {},
"sig": "c3d4e5f6...64-hex-chars"
}| Action | Payload | Description |
|---|---|---|
REBOOT | {} | Restart the device |
SET_CONFIG | { "interval": 5000 } | Update device configuration |
FIRMWARE_UPDATE | { "url": "...", "version": "2.0" } | Trigger OTA update |
IDENTIFY | {} | Blink LED or signal physically |
Command ACK (Device → Platform)
Sent by the device to acknowledge command receipt and completion.
{
"cmdId": "e5f6a7b8-c9d0-1234-e5f6-a7b8c9d01234",
"status": "COMPLETED",
"ts": 1700000000000,
"sig": "d4e5f6a7...64-hex-chars"
}| ACK Status | Meaning |
|---|---|
RECEIVED | Command received, queued for execution |
IN_PROGRESS | Command is being executed |
COMPLETED | Command executed successfully |
FAILED | Command execution failed |
HMAC Signing
Every message must include an HMAC-SHA256 signature computed with the device's hmacSecret.
Signing Process
- Construct the signing string by joining fields with
|(pipe) - Compute HMAC-SHA256 of the string using the
hmacSecret - Encode as lowercase hex (64 characters)
- Include as the
sigfield
Signature Strings
| Message Type | Signing String |
|---|---|
| Telemetry | {deviceId}|{ts}|{nonce}|{JSON.stringify(data)} |
| Status | {deviceId}|{ts}|{nonce}|{JSON.stringify({status})} |
| Command | {deviceId}|{cmdId}|{ts}|{action}|{JSON.stringify(payload)} |
| ACK | {deviceId}|{cmdId}|{ts}|{status} |
JSON serialization must be compact — no extra whitespace. JavaScript JSON.stringify() is compact by default. Python requires json.dumps(data, separators=(',', ':')).
Validation Rules
- Timestamp tolerance: ±5 minutes (300,000 ms) from server time
- Nonce uniqueness: Duplicate nonces rejected within 5-minute window
- HMAC enforcement: In production (
REQUIRE_HMAC_SIGNATURES=true), unsigned messages from non-PENDING devices are rejected
Rate Limiting
10 authentication failures in 5 minutes triggers automatic device suspension. The device must be reactivated by an admin.
Connection Limits
Enforced by Mosquitto:
| Parameter | Value |
|---|---|
| Max connections | 500 |
| Max message size | 8 KB |
| Max inflight messages | 20 |
| Max queued messages | 1000 |