Skip to content

Connect over MQTT

MQTT is the workhorse protocol for fleets that keep a persistent connection and need both telemetry up and commands down over the same session. CORE-M brokers MQTT through EMQX; the device-link adapter subscribes to the broker, normalizes each message into a TelemetryPoint, and publishes it onto the internal bus — exactly like every other protocol.

BrokerEMQX
Plain MQTT1883
MQTT over TLS8883

Use 8883 in production. Plain 1883 exists for local development and trusted-network deployments only — over 1883 the API key travels in clear text.

Authentication happens in the MQTT CONNECT packet. The device identifies itself with its device_id and authenticates with its API key (or PSK):

CONNECT fieldValue
Client IDthe device’s device_id
Usernamethe device’s device_id
Passwordthe device’s API key (sk_live_...) or PSK

Because the client ID is the device identity, the broker rejects a second connection trying to use the same client ID, and the adapter attributes every message on that session to that device. Identity comes from the authenticated session — never from anything inside the payload.

DirectionTopicPurpose
Device → platformdevices/{device_id}/telemetryPublish telemetry readings.
Platform → devicedevices/{device_id}/commandsSubscribe to receive downlink commands.

Replace {device_id} with the device’s own UUID. A device should publish only to its own telemetry topic and subscribe only to its own command topic.

Publish a JSON object of metric → value pairs. The adapter normalizes it into a TelemetryPoint, mapping numeric fields into numeric_values and string fields into string_values:

{
"temperature": 22.5,
"humidity": 65,
"state": "running"
}

The device_id and tenant_id are taken from the authenticated session, and the timestamp is stamped on arrival unless your payload decoder extracts one. See sending telemetry for the full TelemetryPoint model.

Using the standard mosquitto_pub client over TLS:

  1. Connect and publish one reading to the device’s telemetry topic.

    Terminal window
    mosquitto_pub \
    --cafile /etc/ssl/certs/ca-bundle.crt \
    -h mqtt.kronoxdata.com -p 8883 \
    -i d7b1c0e2-3f44-4a91-9b2e-2c5a1f0e9d33 \
    -u d7b1c0e2-3f44-4a91-9b2e-2c5a1f0e9d33 \
    -P 'sk_live_8f3c2a...' \
    -t 'devices/d7b1c0e2-3f44-4a91-9b2e-2c5a1f0e9d33/telemetry' \
    -m '{"temperature":22.5,"humidity":65,"state":"running"}'
  2. To receive commands, subscribe to the command topic with the same credentials (typically kept open for the life of the device):

    Terminal window
    mosquitto_sub \
    --cafile /etc/ssl/certs/ca-bundle.crt \
    -h mqtt.kronoxdata.com -p 8883 \
    -i d7b1c0e2-3f44-4a91-9b2e-2c5a1f0e9d33 \
    -u d7b1c0e2-3f44-4a91-9b2e-2c5a1f0e9d33 \
    -P 'sk_live_8f3c2a...' \
    -t 'devices/d7b1c0e2-3f44-4a91-9b2e-2c5a1f0e9d33/commands'
sequenceDiagram
    participant Dev as Device
    participant EMQX as EMQX broker :8883
    participant DL as device-link MQTT adapter
    participant RP as Redpanda telemetry.raw.{tenant}

    Dev->>EMQX: CONNECT (clientid=device_id, user=device_id, pass=api_key)
    EMQX-->>Dev: CONNACK
    Dev->>EMQX: PUBLISH devices/{device_id}/telemetry {json}
    EMQX->>DL: Deliver message
    DL->>DL: Resolve identity from session, decode payload
    DL->>RP: Publish normalized TelemetryPoint
    Note over Dev,EMQX: Commands flow back via<br/>devices/{device_id}/commands

The first telemetry message flips the device to online. CORE-M tracks liveness from telemetry arrival, not just the TCP connection: if no telemetry arrives for longer than the offline threshold (default 120 seconds), the device is marked offline even if the MQTT session is technically still open. Devices that report infrequently should either publish a heartbeat reading or have their threshold tuned accordingly.

Reach for MQTT when…

  • The device stays connected and you want low-overhead, frequent publishing.
  • You need reliable server→device commands over the same session.
  • You are running a fleet where a broker’s fan-out and session management help.
  • You want QoS guarantees and a battle-tested ecosystem of clients.

For one-shot or backend-driven sends, HTTP is simpler. For UDP-class constrained hardware, see CoAP.