> For the complete documentation index, see [llms.txt](https://docs.bituo-technik.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.bituo-technik.com/device-api/spm-sdm-open-wi-fi-api-overview/mqtt-api-reference.md).

# MQTT API

{% hint style="info" %}
**Status:** Active| **Applies to:** all SPM / SDM Open Wi-Fi branches (TLS support: see the overview)  |  **Updated:** 2026-10-17
{% endhint %}

The meter can connect to your own MQTT broker: it publishes telemetry periodically, subscribes to a command topic, and publishes each result to a response topic. The commands are the same set as on BLE, HTTP and WebSocket. This page covers only what is specific to MQTT: topics, connection behavior, legacy commands and MQTTS setup.

### 1. Introduction

* The meter is an MQTT **client**. You provide the broker.
* Command and response structure: Business Command Reference. Telemetry fields: Telemetry Payload. Error codes: Error Codes.
* An inbound command JSON must not exceed **1024 bytes**. A message that is too long or fails to parse is dropped and **gets no response**.
* No usable broker is configured at the factory. The device starts connecting once a broker address is written with the BLE `mqtt` command or the Web page.
* New firmware answers commands in the unified `ok/msg/event/d` structure and the `seq` field is deprecated. Legacy commands are still recognized; see Section 5.3.

### 2. Topics

The default topic is `{Model}/{SerialNumber}/{suffix}`; the serial number is 12 uppercase hex characters.

<table><thead><tr><th width="113.22216796875">Suffix</th><th>Direction</th><th width="188.77783203125">Content</th><th>QoS</th><th>Retain</th></tr></thead><tbody><tr><td><code>/data</code></td><td>Device → server</td><td>Periodic telemetry</td><td>0</td><td>No</td></tr><tr><td><code>/cmd</code></td><td>Server → device</td><td>Commands</td><td>0</td><td>No</td></tr><tr><td><code>/cdata</code></td><td>Device → server</td><td>Command responses</td><td>0</td><td>No</td></tr><tr><td><code>/mdata</code></td><td>Device → server</td><td>Device metadata, published every time the broker connection is made</td><td>0</td><td><strong>Yes</strong></td></tr></tbody></table>

Example (model `SPM02`, serial `AABBCCDDEEFF`): `SPM02/AABBCCDDEEFF/data`, `SPM02/AABBCCDDEEFF/cmd`.

Notes:

* The device subscribes to `/cmd` at QoS 0, so the broker delivers to it at QoS 0. You may publish commands at QoS 1, but the last hop to the device is not retransmitted.
* The model comes from the device (for example `SPM01`, `SPM02`, `SDM01_1PN`, `SDM01_3PN`, `SDM02`). The exact string for a batch is whatever the device reports in `/mdata` or the `model` response.
* All four topics can be changed. Use `topic1` (`topic_post`, `topic_set`) and `topic2` (`topic_response`, `topic_metadata`); each topic is at most 63 characters. Restart afterwards so the new `/cmd` is subscribed. Read the current values with `mqttdata`; `TP/TC/TR/TM` are the full `/data`, `/cmd`, `/cdata`, `/mdata` topics in that order.
* Some customized firmware uses a different topic prefix. **Use what `mqttdata` returns**; do not build the topic yourself.
* The device sets no Last Will. To detect it going offline, watch whether `/data` keeps updating (use `Post` and `Time`).

### 3. Telemetry (`/data`)

At each MQTT report period the device publishes one JSON to `/data`. Fields, per-model differences and examples are on the Telemetry Payload page. Only the MQTT-specific parts are here:

* The period is set by the fifth segment of the `mqtt` command (in **seconds**, minimum 1 s). `MI` in the `model` response is in **milliseconds**. The factory default on the device is 5 s; the online documentation says 30 s. Trust the `MI` you read from the device.
* It is published only while connected to the broker; data from an offline period is **not replayed**.
* With `commonmode` on, `/data` carries the generic format; see Section 6 of the telemetry page.

### 4. Metadata (`/mdata`)

Every time the device connects (including reconnects) it publishes one retained message to `/mdata`, so a new subscriber gets it immediately.

```json
{
  "product": "EnergySensor-SPM01-AABBCCDDEEFF",
  "Serialnumber": "AABBCCDDEEFF",
  "fwVer": "5.1.16",
  "wifiname": "MyRouter"
}
```

<table><thead><tr><th width="209">Field</th><th>Description</th></tr></thead><tbody><tr><td><code>product</code></td><td>Product name, usually <code>EnergySensor-Model-Serial</code></td></tr><tr><td><code>Serialnumber</code></td><td>Serial number</td></tr><tr><td><code>fwVer</code></td><td>Wi-Fi module firmware version</td></tr><tr><td><code>wifiname</code></td><td>SSID of the connected router (absent on some firmware)</td></tr></tbody></table>

### 5. Commands and responses

#### 5.1 Message format

Publish a JSON object with at least `cmd` to `/cmd`:

```json
{"cmd":"location"}
```

The device publishes the response to `/cdata`:

```json
{"ok":true,"msg":"location received","event":"location"}
```

<table><thead><tr><th width="194.5555419921875">Field</th><th>Description</th></tr></thead><tbody><tr><td><code>ok</code></td><td><code>true</code> on success</td></tr><tr><td><code>msg</code></td><td>Result text or the original error text</td></tr><tr><td><code>event</code></td><td>Echo of the command name; use it to pair a request with its response</td></tr><tr><td><code>d</code></td><td>Optional returned data</td></tr><tr><td><code>code</code></td><td>Only on failure; see Error Codes (on MQTT it is <code>3000 + subcode</code>)</td></tr></tbody></table>

There is no `seq`, so send one command at a time and match the response by `event`.

#### 5.2 Which commands are available

Apart from the BLE-only handshake commands (`pass`, `auth`, the secured handshake and so on), all BLE business commands can be sent over MQTT in the same format. The common ones:

<table><thead><tr><th width="254.5555419921875">Command</th><th>Purpose</th></tr></thead><tbody><tr><td><code>getFunctionMap</code> / <code>model</code> / <code>mqttdata</code></td><td>Read the capability bitmap, device configuration, MQTT topics and connection status</td></tr><tr><td><code>location</code></td><td>Flash or beep for 15 s to find the device</td></tr><tr><td><code>zero</code></td><td>Reset energy counters; <strong>irreversible</strong></td></tr><tr><td><code>restart</code></td><td>Reboot</td></tr><tr><td><code>wifi</code></td><td>Configure Wi-Fi (a wrong value drops the device and MQTT cannot fix it)</td></tr><tr><td><code>mqtt</code> / <code>mqtr</code></td><td>Configure the broker; <code>mqtr</code> saves then reboots; see Section 7.1</td></tr><tr><td><code>udp</code> / <code>deleteudp</code> / <code>udpdata</code></td><td>UDP report targets</td></tr><tr><td><code>enablehttp</code> and others</td><td>Turn other channels on or off; restart afterwards</td></tr></tbody></table>

The full commands, parameters and responses are in the Business Command Reference. Differences on MQTT:

* MQTT needs no `pass` / `auth`. **Anyone who can publish to `/cmd` controls the device.** Protect it with broker accounts and TLS; see Section 7.3.
* `setInterval` / `getInterval` control the WebSocket push period, not the MQTT period.
* A command the model does not support returns a failure; see the business command reference.

#### 5.3 Legacy commands

These commands remain from older firmware. The device still handles them and the responses use the old format. Use the unified commands from Section 5.2 for new development.

<table><thead><tr><th width="147.888916015625">Command</th><th>Parameters</th><th>Behavior</th><th>Response</th></tr></thead><tbody><tr><td><code>rdata</code></td><td>none</td><td>Publish <code>/data</code> once immediately</td><td><code>{"result":"success","event":"rdata","seq":"&#x3C;SN>"}</code>; on failure <code>result</code> is <code>failed</code> and <code>code</code> is 3001</td></tr><tr><td><code>rint</code></td><td>none</td><td>Read the report period</td><td><code>{"result":"success","event":"rint","seq":"&#x3C;SN>","INTV":5}</code>, <code>INTV</code> in seconds</td></tr><tr><td><code>wint</code></td><td><code>ReportFrequency</code> (seconds)</td><td>Set the report period; <strong>reboots on success</strong></td><td><code>event</code> is <code>wint</code>; if <code>ReportFrequency</code> is missing or 0 it fails with <code>code</code> 3002</td></tr><tr><td><code>mqstep1</code></td><td><code>data</code>: <code>host｜clientid｜ssl｜port</code></td><td>Save the broker address</td><td><code>event</code> is <code>mqstep1</code></td></tr><tr><td><code>mqstep2</code></td><td><code>data</code>: <code>interval｜username｜password</code></td><td>Save period and account; <strong>reboots on success</strong></td><td><code>event</code> is <code>mqstep2</code></td></tr><tr><td><code>tp1</code></td><td><code>data</code>: <code>topic_post｜topic_set</code></td><td>Change the publish and command topics; <strong>reboots</strong></td><td><code>event</code> is <code>tp1</code></td></tr><tr><td><code>tp2</code></td><td><code>data</code>: <code>topic_response｜topic_metadata</code></td><td>Change the response and metadata topics</td><td><code>event</code> is <code>tp2</code></td></tr><tr><td><code>topic</code></td><td><code>topic_post</code>, <code>topic_set</code>, <code>topic_response</code>, <code>topic_metadata</code></td><td>Change all four topics and re-subscribe <code>/cmd</code></td><td><code>event</code> is <code>topic</code></td></tr><tr><td><code>otaurl</code></td><td><code>url</code></td><td>Upgrade firmware from the given URL</td><td><code>{"type":"ACK","seq":"&#x3C;SN>","flag":&#x3C;n>}</code></td></tr><tr><td><code>ota</code></td><td>none</td><td>Upgrade from the default URL</td><td>Same as above</td></tr><tr><td><code>cafile</code></td><td><code>cadata</code>, optional <code>filename</code>, <code>writeLen</code></td><td>Write the MQTT CA certificate</td><td><code>event</code> is <code>cafile</code></td></tr></tbody></table>

Notes:

* The `｜` in the table stands for the ASCII pipe `|` in a real command; segments are positional.
* In a legacy response `seq` is the **device serial number**, not a request counter.
* `cafile` overwrites when `writeLen` is 0 and appends otherwise, so one certificate can be sent in several pieces. Keep each whole command within 1024 bytes. For new work use `cafilestart` / `cafiledata` over BLE or HTTP.
* `otaurl` / `ota` make the device download and write firmware, and anyone able to publish to `/cmd` can trigger them. Restrict publish rights on `/cmd` as in Section 7.3.

### 6. Connection behavior

These behaviors explain most "why did the device drop" questions:

1. **It connects only once enabled.** The device tries to connect only after a broker is configured. A successful `mqtt` / `mqtr` also sets the MQTT bit (bit 9) in the function map, so no separate `enable*` is needed.
2. **Automatic reconnect.** Every 5 s the device checks the link and, if it is down, reconnects with the saved address, ClientId and account. After connecting it re-subscribes `/cmd` and republishes `/mdata`.
3. **Repeated failures cause a reboot.** After 11 consecutive failed connections, and with no phone connected over BLE, the device restarts. In weak-signal mode with TLS the threshold is 1. It also restarts when there is not enough memory to set up TLS.
4. **ClientId.** The default is `{Model}-{SerialNumber}`. A broker allows only one connection per ClientId, so do not let two devices or a test script share one, or they will kick each other off.
5. **Report cadence.** When the period timer fires the device publishes if connected; `rdata` adds one more at once.
6. **During an upgrade.** MQTT handling pauses while firmware is being upgraded.
7. **Restart after changes.** `mqtt` saves without restarting. `mqtr`, `wint`, `mqstep2` and `tp1` restart after saving; for others you send `restart` yourself.

### 7. Configuring the broker and MQTTS

#### 7.1 Configure with a command

Send over BLE (or any channel that already works):

```json
{"cmd":"mqtr","data":"broker.example.com|dev01|1|8883|30|user|pwd"}
```

The seven `data` segments are `host｜clientid｜ssl｜port｜interval｜username｜password`. `ssl` is `1` for TLS and `0` for plaintext. `interval` is in seconds; 0 or non-numeric becomes 5 s. A `port` of 0 or non-numeric becomes 1883. A successful response has `msg` `MQTT successfully`. `mqtt` only saves; `mqtr` saves and reboots. Each segment must be 1 to 63 characters; an **empty segment** or a too-long one returns `Error length!`, so username and password cannot be left blank either.

#### 7.2 MQTTS (TLS)

The device supports TLS 1.2 / 1.3 and verifies the server with the broker's root certificate.

1. **Turn TLS on.** Put `1` in the third segment of the `mqtt` command; the port is usually 8883.
2. **Write the CA certificate.** It must be PEM (`.pem` or `.crt`).
   * BLE: send `cafilestart`, then `cafiledata` in several pieces; see the Business Command Reference.
   * Web page: open the device IP, go to Edit → Forwarding → Upload MQTT CA certificate and upload the file. The Web page needs HTTP turned on first with BLE `enablehttp`.
   * Without an uploaded certificate the device uses the root certificate built into the firmware, which only works for brokers signed by that root. A self-hosted broker or another cloud needs its own CA uploaded.
3. **Restart.** Save with `mqtr`, or send `restart`, for the TLS settings to apply.
4. **Check.** `d.MF` in the `model` response is `true` once connected; `MF` in `mqttdata` is a number where `0` means connected.

TLS uses one of the device's TLS slots (Basic+ one, Pro two). MQTTS has the highest priority; see Overview Section 3.1.

#### 7.3 Security advice

* Give each device its own broker account and ClientId, and use ACLs so it can publish only `{Model}/{SerialNumber}/data`, `/cdata`, `/mdata` and subscribe only to its own `/cmd`.
* Only your control backend may publish to `/cmd`.
* Use MQTTS in production and upload the matching CA.
* Do not send a Wi-Fi password in `/cmd` unless the link is TLS.
* `zero`, `restart`, `wifi` and `reset` have a large effect; add a confirmation step in the backend.

### 8. Migrating from the legacy API

| Item                 | Legacy (5.1.13 and earlier)        | New (5.1.14 and later)                         |
| -------------------- | ---------------------------------- | ---------------------------------------------- |
| Topic prefix         | `bituotechnik/{Model}/...`         | `{Model}/...`, prefix removed                  |
| Command style        | Uppercase, such as `RDATA`, `WINT` | Lowercase, same as BLE, such as `location`     |
| Request tracking     | `seq`                              | Deprecated; use `event` in the response        |
| Response format      | `{"result":"SUCCESS","seq":...}`   | `{"ok":true,"msg":"...","event":"...","d":{}}` |
| Phase indicator      | Implicit, via `DeviceType`         | Explicit `TP` field                            |
| New telemetry fields | none                               | `Time`, `IP`, `MCUVersion`                     |
| Metadata             | `macAddr`, `fwVer`                 | `product`, `Serialnumber`, `fwVer`             |

The three-phase `TotalActivePower`, `TotalForwardEnergy` and `TotalReverseEnergy` are included in the payload again from 5.1.14.

### 9. Debugging examples

With the Mosquitto command line (replace the address and account with yours):

```bash
# Subscribe to every topic of one device
mosquitto_sub -h broker.example.com -p 1883 -u user -P pwd -t "SPM02/AABBCCDDEEFF/#" -v

# Make the device flash
mosquitto_pub -h broker.example.com -p 1883 -u user -P pwd -t "SPM02/AABBCCDDEEFF/cmd" -m '{"cmd":"location"}'
```

The MQTT monitor tool in this repository can subscribe, send commands and match responses by `event`.

***

### Appendix-1:  How to Configure MQTTS (TLS/SSL)

The device supports secure MQTT connections over **TLS 1.2** and **TLS 1.3** to ensure data encryption and prevent man-in-the-middle attacks. Follow these steps to configure MQTTS via the embedded Web UI.

#### Step 1: Access the Embedded Web UI

1. Open a web browser and enter the device's IP address (e.g., `http://192.168.50.208`).
2. **Troubleshooting:** If the web page is inaccessible, the HTTP service might be disabled on the device. You can enable it using the **BT Powerlink APP** or the **WeChat Mini APP**:
   * Open the App and connect to the device via Bluetooth.
   * Navigate to **Settings** -> **Forwarding**.
   * Turn on the **HTTP** toggle.

#### Step 2: Upload the CA Certificate

To establish a trusted TLS connection, the device needs the Root CA certificate of your MQTT broker.

1. In the Web UI, navigate to **Edit** -> **Forwarding** -> **Upload MQTT CA certificate**.
2. Click **Choose File** and select your CA certificate file (must be in PEM format, usually `.pem` or `.crt`).
3. Click **Upload**.

#### Step 3: Configure MQTT Broker Settings

1. Navigate to **Edit** -> **Forwarding** -> **MQTT broker**.
2. **Check the `SSL / TLS` box** at the top.
3. Fill in your broker details.

**Example using EMQX Public Broker:**

* **Host:** `broker.emqx.io`
* **Port:** `8883` *(Standard secure MQTT port)*
* **Client ID / Username / Password:** Fill according to your needs.
* **ReportFrequency:** Set your desired interval (e.g., `30` seconds).
* Click **Save MQTT**.

> 💡 **Tip: How to get the EMQX CA Certificate?** If you are using `broker.emqx.io`, you can download their Root CA certificate directly from the EMQX official website (search for "EMQX Public Broker CA download"). Alternatively, you can visit `https://broker.emqx.io` in your browser, click the "Lock" icon in the address bar, view the certificate details, and export the Root CA as a Base64 encoded `.cer` or `.crt` file.

#### Step 4: Reboot to Apply Changes

After saving the configuration, the device must be rebooted for the new TLS settings and certificates to take effect.

1. Look at the top right corner of the Web UI dashboard.
2. You will see a notification: *"Configuration was sent. Enable restart when you are ready to reboot the device."*
3. Check the **"Send restart command"** box. The device will reboot immediately and attempt to connect to your broker using MQTTS.
4. Once rebooted, you can verify the connection status at the top right of the dashboard (it should display `MQTT: Online`).

***

### Appendix-2: Major Changes from Legacy API (v5.1.14-)

For developers migrating from the older firmware (e.g., v1.1.8) to the new Basic Firmware (5.1.12+), please note the following breaking changes. The architecture has been unified with the BLE standard to improve efficiency and reduce payload sizes.

#### 1 Quick Comparison Table

<table><thead><tr><th width="233">Feature / Concept</th><th>Legacy API (v5.1.13-)</th><th>Basic Firmware (v5.1.14+)</th></tr></thead><tbody><tr><td><strong>Topic Prefix</strong></td><td><code>bituotechnik/{Model}/...</code></td><td><code>{Model}/...</code> <em>(Prefix removed)</em></td></tr><tr><td><strong>Command Format</strong></td><td>Uppercase (e.g., <code>RDATA</code>, <code>WINT</code>)</td><td>Lowercase / BLE semantics (e.g., <code>location</code>)</td></tr><tr><td><strong>Request Tracking</strong></td><td>Uses <code>seq</code> (Sequence Number)</td><td><strong>Deprecated</strong>. Uses <code>event</code> field in response</td></tr><tr><td><strong>Response Format</strong></td><td><code>{"result": "SUCCESS", "seq": ...}</code></td><td><code>{"ok": true, "msg": "...", "event": "...", "d": {...}}</code></td></tr><tr><td><strong>Phase Indicator</strong></td><td>Implicit (relies on <code>DeviceType</code>)</td><td>Explicit <code>TP</code> field (<code>1</code> = Single, <code>3</code> = Three-Phase)</td></tr><tr><td><strong>New Telemetry Fields</strong></td><td>N/A</td><td><code>Time</code>, <code>IP</code>, <code>MCUVersion</code></td></tr><tr><td><strong>Metadata (<code>/mdata</code>)</strong></td><td><code>macAddr</code>, <code>fwVer</code></td><td><code>product</code>, <code>Serialnumber</code>, <code>fwVer</code></td></tr></tbody></table>

#### 2 Detailed Change Notes

* **Command Architecture (BLE Unification):** The `seq` field is completely removed. Request/response matching is now handled via the `event` field in the response payload. Commands now strictly follow the BLE API semantics.
* **Telemetry Payload (`/data`):** Added the `TP` field to explicitly indicate phase type, replacing the old `DeviceType` logic. Payloads now include `Time` (Unix timestamp), `IP`, and `MCUVersion`. *(Note: Starting from v5.1.14, calculated totals like `TotalActivePower`, `TotalForwardEnergy`, and `TotalReverseEnergy` have been re-introduced to the 3-phase payload).*
* **Metadata (`/mdata`):** The JSON schema has been updated. The legacy `macAddr` field is replaced by `Serialnumber`, and a new `product` string is included.
