Menu

Subscribing to Topics

How to listen for messages from the platform, other devices, or external systems.

How subscriptions work

Subscriptions in the Minimalist SDK are defined as a null-terminated array of topic strings, passed to mqtt.set_subscriptions() before the connection is established. The SDK subscribes to every topic in the list on first connect — and automatically re-subscribes on every reconnect. You set it up once and never think about it again.

static const char * my_topics[] = {
    "~/~/command",
    "~/~/config/setpoint",
    "~/broadcast/alert",
    nullptr           // ← must be null-terminated
};

void setup() {
    // ... wifi and mqtt setup ...

    mqtt.set_subscriptions(my_topics);
    mqtt.set_callback(on_message);
}

Topic prefixing with ~/ and ~/~/

Subscription topics follow the exact same prefix rules as publish topics. The SDK rewrites a leading ~/ or ~/~/ before it hands the topic to the broker.

"~/~/command"
// subscribes to: larry/a8f3k2m1/command
// (only your device receives this)

"~/~/config/setpoint"
// subscribes to: larry/a8f3k2m1/config/setpoint

"~/fleet/alert"
// subscribes to: larry/fleet/alert
// (any device on your account publishing here is heard)

"larry/fleet/alert"
// subscribes to: larry/fleet/alert
// (same result, but written out verbatim)

Use ~/~/ for topics your device alone should receive — commands, config updates, per-device state. Use ~/ for account-wide topics like group broadcasts or shared alerts. Use a verbatim topic when you already have the full string and don't need rewriting.

Only a leading ~/ or ~/~/ is rewritten. A ~ elsewhere in the topic is treated as a literal character.

Handling incoming messages

Register a callback function with mqtt.set_callback(). It receives the full topic string and the payload as null-terminated char * strings:

void on_message(char * topic, char * message) {
    Serial.print("  topic:   ");  Serial.println(topic);
    Serial.print("  message: ");  Serial.println(message);

    // example: react to a command
    if (strstr(topic, "/command") != NULL) {
        if (strcmp(message, "reboot") == 0) {
            ESP.restart();
        }
    }
}

void setup() {
    // ...
    mqtt.set_callback(on_message);
}
The topic your callback receives is the full broker topic, including the username and device ID prefix — e.g. larry/a8f3k2m1/command. Keep this in mind when matching topics in your handler.

Complete example

Here's a full sketch that subscribes to two topics — one device-specific and one shared — and handles messages from both:

#include <Arduino.h>
#include "credentials.h"
#include "device_id.h"
#include "wifi_tools.h"
#include "mqtt.h"

char DEVICE_ID[9];

void on_message(char * topic, char * message) {
    Serial.print("  topic:   ");  Serial.println(topic);
    Serial.print("  message: ");  Serial.println(message);

    if (strstr(topic, "/command") != NULL) {
        if (strcmp(message, "reboot") == 0) {
            Serial.println("  rebooting...");
            ESP.restart();
        }
        if (strcmp(message, "identify") == 0) {
            // flash an LED, beep, or publish a response
            Serial.println("  I'm here!");
        }
    }

    if (strstr(topic, "/alert") != NULL) {
        Serial.print("  got shared alert: ");
        Serial.println(message);
    }
}

static const char * my_topics[] = {
    "~/~/command",      // → larry/a8f3k2m1/command
    "~/fleet/alert",    // → larry/fleet/alert
    nullptr
};

void setup() {
    Serial.begin(115200);
    device_id.get_or_set(DEVICE_ID);
    wifi_tools.begin(WIFI_SSID, WIFI_PASS);
    mqtt.setup(MQTT_HOST, MQTT_PORT, DEVICE_ID, MQTT_USER, MQTT_PASS);
    mqtt.set_subscriptions(my_topics);
    mqtt.set_callback(on_message);
}

void loop() {
    if (wifi_tools.is_connected) {
        mqtt.maintain();
    } else {
        wifi_tools.reconnect();
        mqtt.report_disconnect();
    }
}