Skip to main content

MQTT Protocol Overview

:::info Status Implemented. Source: docs/user-device-mqtt-contract.md (stream player, v1.0), docs/transmitter-mqtt-contract.md (transmitter, v1.0). :::

Azan360's two physical device types — the transmitter and the stream player — both speak MQTT against the same self-hosted EMQX broker, using the same JSON envelope shape. They are not the same contract: each is independently versioned v1.0, and the differences below (topic scheme, credential model, entitlement) are permanent design differences between the two device roles, not a version gap waiting to close.

The mobile app holds no MQTT connection at all — it is not a device; see App install / mobile app below.

Two contracts, one broker

TransmitterStream player
Source contractdocs/transmitter-mqtt-contract.mddocs/user-device-mqtt-contract.md
Topic basemasjidconnect/{mosqueId}/device/{deviceId}/ (mosque and device in the path)masjidconnect/rx/{deviceId}/ (device-scoped only, no mosque in the path)
CredentialShared, fleet-wide MQTT username/password (MQTT_DEVICE_USER/MQTT_DEVICE_PASS) — every transmitter authenticates as the same identityPer-device credential, broker-enforced subtree ACL
Broker-side isolationNone — topic-path convention + HTTP-side authorization onlyYes — broker refuses a wildcard or wrong-subtree subscribe outright
Keepalive30 s60 s
Heartbeat default60 s300 s
Bound toOne mosque, for the unit's lifeOne member (owner); mosque binding is mutable, can rebind repeatedly
Client IDazan360-tx-{device_id}azan360-rx-{device_id}

See Topic Structure for the full topic tables and the broker ACL model, and Device Authorization for how a unit earns the right to receive azaan at all — this page and its siblings describe the wire protocol, not the authorization decision behind it.

Envelope conventions (both contracts)

  • Encoding: UTF-8 JSON on every topic.
  • Timestamps: Unix epoch seconds (integer), UTC.
  • Every device-published payload carries "v": "1.0" — the contract version the device implements. A breaking change bumps the major version.
  • Commands (cmd topic) carry a req_id; devices ack with the same req_id on the ack topic. See Commands and Error Handling — idempotency.

QoS and retained-message conventions

TopicQoSRetainedWhy
status1yesLifecycle state must survive a reconnect and a fresh subscriber
heartbeat0noHigh-frequency telemetry; loss is acceptable
config1yesDesired state must be delivered even to a unit that was offline for a month
cmd1noA command is a moment, not durable state — see Error Handling for why it is never retained or queued
ack1noResponse correlated by req_id, not meant to persist
log0noOptional diagnostics, throttled

Session semantics differ deliberately

  • Transmitter: clean_session = true — subscriptions re-established every connect, matching a mosque-bolted asset that's expected to stay connected.
  • Stream player: clean_start = true, Session-Expiry-Interval = 0 — explicitly non-persistent. A stream player's contract is explicit that the broker must never queue a play for later delivery: a missed azaan delivered late is worse than one never delivered. See Error Handling for the full reasoning and the comparison table between persistent and clean sessions.

Broker deployment and operations

How the EMQX broker itself is deployed, secured (TLS, Postgres-backed authn/authz views), rate-limited, monitored, upgraded, and how a device is revoked at the broker level is covered in Device Deployment and Monitoring — not repeated here. This section and its siblings describe what a device sends and receives on the wire.

The mobile app is not on this broker

The Android/iOS app holds no MQTT connection, background or foreground — iOS terminates background sockets and Android Doze does the same within minutes, so an app that tried would drain battery, get killed, and still miss azaan. Its azaan wakeup path is push (APNs VoIP / FCM) plus an HTTP stream-token exchange; see Azaan Broadcast and API Reference (stream-auth, app-installs tags).

Next