This is an old revision of the document!
MQTT (Message Queuing Telemetry Transport) is a lightweight, publish-subscribe network protocol designed for efficient communication in constrained environments. Originally developed by IBM in the late 1990s, MQTT has become a standard for IoT (Internet of Things) systems due to its low bandwidth requirements and minimal overhead. It is mostly used in Home Automation systems, Industrial IoT applications and Mobile Messaging and Telemetry.
sensors/temperature/room1
).sensors/+/room1
) to match multiple topics.In this lab, you'll interact with an MQTT broker, publish sensor data from your Sparrow sensor node, and visualize real-time telemetry. Familiarity with topics and QoS settings will be essential to design robust IoT communication systems.
Goal: connect to Wi-Fi, connect to MQTT, then publish a heartbeat every 5s to iot/<yourname>/heartbeat.
// src/main.cpp #include <Arduino.h> #include <WiFi.h> #include <PubSubClient.h> //////////////// EDIT THESE //////////////// const char* WIFI_SSID = "TP-Link_2A64"; const char* WIFI_PASSWORD = "99481100"; const char* MQTT_HOST = "test.mosquitto.org"; // or your lab broker const uint16_t MQTT_PORT = 1883; const char* BASE_TOPIC = "iot/dantudose"; // change per student //////////////////////////////////////////// WiFiClient espClient; PubSubClient mqtt(espClient); void ensureWiFi() { if (WiFi.status() == WL_CONNECTED) return; WiFi.mode(WIFI_STA); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); Serial.print("WiFi connecting"); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.printf("\nWiFi OK, IP: %s\n", WiFi.localIP().toString().c_str()); } void ensureMQTT() { if (mqtt.connected()) return; String clientId = String("sparrow-c6-") + String((uint32_t)ESP.getEfuseMac(), HEX); Serial.println("MQTT connecting..."); Serial.println("Client ID:"); Serial.print(clientId); while (!mqtt.connect(clientId.c_str())) { Serial.print("."); delay(1000); } Serial.println("\nMQTT connected"); } unsigned long lastPub = 0; void setup() { Serial.begin(115200); delay(1000); WiFi.useStaticBuffers(true); // small memory win on C6 mqtt.setServer(MQTT_HOST, MQTT_PORT); } void loop() { ensureWiFi(); ensureMQTT(); mqtt.loop(); if (millis() - lastPub > 5000) { lastPub = millis(); String topic = String(BASE_TOPIC) + "/heartbeat"; String payload = String("{\"uptime_ms\":") + millis() + "}"; bool ok = mqtt.publish(topic.c_str(), payload.c_str()); Serial.printf("Publish %s => %s (%s)\n", topic.c_str(), payload.c_str(), ok ? "OK" : "FAIL"); } }
Test it out using this link.
Goal: subscribe to iot/<yourname>/led and set the Sparrow’s single WS2812 LED color from JSON payloads like {“r”:255,”g”:0,”b”:64}.
#include <Arduino.h> #include <WiFi.h> #include <PubSubClient.h> #include <ArduinoJson.h> #include <Adafruit_NeoPixel.h> //////////////// EDIT THESE //////////////// const char* WIFI_SSID = "TP-Link_2A64"; const char* WIFI_PASSWORD = "99481100"; const char* MQTT_HOST = "test.mosquitto.org"; // or your lab broker const uint16_t MQTT_PORT = 1883; const char* BASE_TOPIC = "iot/dantudose"; // change per student //////////////////////////////////////////// #define NEOPIXEL_PIN 3 Adafruit_NeoPixel pixel(1, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800); WiFiClient espClient; PubSubClient mqtt(espClient); void ensureWiFi() { if (WiFi.status() == WL_CONNECTED) return; WiFi.mode(WIFI_STA); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); while (WiFi.status() != WL_CONNECTED) { delay(400); Serial.print("."); } Serial.printf("\nIP: %s\n", WiFi.localIP().toString().c_str()); } void onMessage(char* topic, byte* payload, unsigned int len) { Serial.printf("MQTT msg on %s: %.*s\n", topic, len, (char*)payload); StaticJsonDocument<128> doc; DeserializationError err = deserializeJson(doc, payload, len); if (err) return; int r = doc["r"] | 0, g = doc["g"] | 0, b = doc["b"] | 0; pixel.setPixelColor(0, pixel.Color(r, g, b)); pixel.setBrightness(doc["brightness"] | 50); // optional 0..255 pixel.show(); // Acknowledge String ackTopic = String(BASE_TOPIC) + "/led/ack"; StaticJsonDocument<96> ack; ack["ok"] = true; ack["r"] = r; ack["g"] = g; ack["b"] = b; char buf[96]; size_t n = serializeJson(ack, buf); mqtt.publish(ackTopic.c_str(), buf, n); } void ensureMQTT() { if (mqtt.connected()) return; while (!mqtt.connected()) { String cid = String("sparrow-c6-led-") + String((uint32_t)ESP.getEfuseMac(), HEX); if (mqtt.connect(cid.c_str())) break; delay(1000); } String sub = String(BASE_TOPIC) + "/led"; mqtt.subscribe(sub.c_str()); Serial.printf("Subscribed: %s\n", sub.c_str()); } void setup() { Serial.begin(115200); pixel.begin(); pixel.clear(); pixel.show(); mqtt.setServer(MQTT_HOST, MQTT_PORT); mqtt.setCallback(onMessage); } void loop() { ensureWiFi(); ensureMQTT(); mqtt.loop(); }
Test it out using this link.
Goal: read temperature, humidity, pressure (and gas resistance) from the BME688 over I²C and publish every 10s to iot/<yourname>/bme688.
#include <Arduino.h> #include <WiFi.h> #include <Wire.h> #include <PubSubClient.h> #include <ArduinoJson.h> #include <Adafruit_BME680.h> //////////////// EDIT THESE //////////////// const char* WIFI_SSID = "TP-Link_2A64"; const char* WIFI_PASSWORD = "99481100"; const char* MQTT_HOST = "test.mosquitto.org"; // or your lab broker const uint16_t MQTT_PORT = 1883; const char* BASE_TOPIC = "iot/dantudose"; // change per student //////////////////////////////////////////// // Sparrow I2C: SDA=21, SCL=22 #define SDA_PIN 21 #define SCL_PIN 22 Adafruit_BME680 bme; // I2C WiFiClient espClient; PubSubClient mqtt(espClient); void ensureWiFi() { if (WiFi.status() == WL_CONNECTED) return; WiFi.mode(WIFI_STA); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); while (WiFi.status() != WL_CONNECTED) { delay(400); Serial.print("."); } Serial.printf("\nIP: %s\n", WiFi.localIP().toString().c_str()); } void ensureMQTT() { if (mqtt.connected()) return; while (!mqtt.connected()) { String cid = String("sparrow-c6-sense-") + String((uint32_t)ESP.getEfuseMac(), HEX); if (mqtt.connect(cid.c_str())) break; delay(1000); } } unsigned long lastPub = 0; void setup() { Serial.begin(115200); Wire.begin(SDA_PIN, SCL_PIN); mqtt.setServer(MQTT_HOST, MQTT_PORT); if (!bme.begin(0x76)) { // Sparrow uses 0x76 Serial.println("BME688 not found!"); for(;;) { delay(1000); } } // Reasonable oversampling; heater off for simplicity bme.setTemperatureOversampling(BME680_OS_8X); bme.setHumidityOversampling(BME680_OS_2X); bme.setPressureOversampling(BME680_OS_4X); bme.setIIRFilterSize(BME680_FILTER_SIZE_3); bme.setGasHeater(0, 0); // off } void loop() { ensureWiFi(); ensureMQTT(); mqtt.loop(); if (millis() - lastPub > 10000) { lastPub = millis(); if (!bme.performReading()) { Serial.println("BME read failed"); return; } StaticJsonDocument<192> doc; doc["ts"] = (uint32_t)(millis()/1000); doc["temp_c"] = bme.temperature; doc["hum_pct"] = bme.humidity; doc["press_hpa"] = bme.pressure / 100.0; doc["gas_ohm"] = bme.gas_resistance; char payload[192]; size_t n = serializeJson(doc, payload); String topic = String(BASE_TOPIC) + "/bme688"; bool ok = mqtt.publish(topic.c_str(), payload, n); Serial.printf("Pub %s => %s (%s)\n", topic.c_str(), payload, ok ? "OK" : "FAIL"); } }
Test it out using this link.
Goal here is to add production-style robustness: MQTT session properties (LWT, retain, QoS), back-off reconnects, and how to flip to TLS.
#include <Arduino.h> #include <WiFi.h> #include <WiFiClientSecure.h> #include <MQTT.h> #include <Wire.h> #include <Adafruit_BME680.h> #include <Adafruit_NeoPixel.h> #include <ArduinoJson.h> //////////////// EDIT THESE //////////////// const char* WIFI_SSID = "TP-Link_2A64"; const char* WIFI_PASSWORD = "99481100"; const char* MQTT_HOST = "test.mosquitto.org"; // or your lab broker const uint16_t MQTT_PORT = 1883; const char* BASE_TOPIC = "iot/dantudose"; // change per student //////////////////////////////////////////// #define SDA_PIN 21 #define SCL_PIN 22 #define NEOPIXEL_PIN 3 WiFiClient net; MQTTClient mqtt(2048); Adafruit_BME680 bme; Adafruit_NeoPixel pixel(1, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800); unsigned long nextConnTry = 0; uint32_t backoffMs = 1000; unsigned long lastHeartbeat = 0; unsigned long lastSensorPub = 0; void ensureWiFi() { if (WiFi.status() == WL_CONNECTED) return; WiFi.mode(WIFI_STA); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); Serial.print("WiFi connecting"); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.printf("\nWiFi OK, IP: %s, RSSI: %d dBm\n", WiFi.localIP().toString().c_str(), WiFi.RSSI()); } void setLED(uint8_t r, uint8_t g, uint8_t b, uint8_t brightness) { pixel.setBrightness(brightness); pixel.setPixelColor(0, pixel.Color(r, g, b)); pixel.show(); } void handleLEDJson(const String& payload) { JsonDocument doc; DeserializationError err = deserializeJson(doc, payload); if (err) { Serial.printf("LED JSON parse error: %s\n", err.c_str()); return; } uint8_t r = doc["r"].isNull() ? 0 : doc["r"].as<uint8_t>(); uint8_t g = doc["g"].isNull() ? 0 : doc["g"].as<uint8_t>(); uint8_t b = doc["b"].isNull() ? 0 : doc["b"].as<uint8_t>(); uint8_t br = doc["brightness"].isNull() ? 100 : doc["brightness"].as<uint8_t>(); setLED(r,g,b,br); JsonDocument ack; ack["ok"] = true; ack["r"] = r; ack["g"] = g; ack["b"] = b; ack["brightness"] = br; String out; serializeJson(ack, out); mqtt.publish((String(BASE_TOPIC)+"/led/ack").c_str(), out.c_str(), false, 1); } void messageReceived(String &topic, String &payload) { Serial.printf("MSG [%s]: %s\n", topic.c_str(), payload.c_str()); if (topic == String(BASE_TOPIC) + "/led") handleLEDJson(payload); } bool connectMQTT() { String cid = String("sparrow-c6-qos1-") + String((uint32_t)ESP.getEfuseMac(), HEX); mqtt.setCleanSession(true); mqtt.setKeepAlive(30); String willTopic = String(BASE_TOPIC) + "/status"; mqtt.setWill(willTopic.c_str(), "{\"status\":\"offline\"}", true, 1); Serial.printf("MQTT connecting to %s:%u as %s ...\n", MQTT_HOST, MQTT_PORT, cid.c_str()); bool ok = mqtt.connect(cid.c_str(), nullptr, nullptr); if (!ok) { Serial.printf("MQTT connect failed, error=%d\n", mqtt.lastError()); return false; } mqtt.subscribe((String(BASE_TOPIC)+"/led").c_str(), 1); mqtt.publish(willTopic.c_str(), "{\"status\":\"online\"}", true, 1); Serial.println("MQTT connected"); return true; } void ensureMQTT() { if (mqtt.connected()) return; if (millis() < nextConnTry) return; if (!connectMQTT()) { backoffMs = min<uint32_t>(backoffMs * 2, 30000); nextConnTry = millis() + backoffMs; Serial.printf("Retry in %lu ms\n", (unsigned long)backoffMs); } else { backoffMs = 1000; } } bool readBME(float& tC, float& hPct, float& pHpa, float& gas) { if (!bme.performReading()) return false; tC = bme.temperature; hPct = bme.humidity; pHpa = bme.pressure / 100.0; gas = bme.gas_resistance; return true; } void setup() { Serial.begin(115200); pixel.begin(); setLED(0,0,0,10); Wire.begin(SDA_PIN, SCL_PIN); if (!bme.begin(0x76)) { Serial.println("BME688/BME680 not found at 0x76!"); } else { bme.setTemperatureOversampling(BME680_OS_8X); bme.setHumidityOversampling(BME680_OS_2X); bme.setPressureOversampling(BME680_OS_4X); bme.setIIRFilterSize(BME680_FILTER_SIZE_3); bme.setGasHeater(0, 0); } mqtt.begin(MQTT_HOST, MQTT_PORT, net); mqtt.onMessage(messageReceived); } void loop() { ensureWiFi(); ensureMQTT(); mqtt.loop(); if (mqtt.connected() && millis() - lastHeartbeat > 15000) { lastHeartbeat = millis(); JsonDocument hb; hb["uptime_s"] = (uint32_t)(millis()/1000); hb["ip"] = WiFi.localIP().toString(); hb["rssi"] = WiFi.RSSI(); String out; serializeJson(hb, out); mqtt.publish((String(BASE_TOPIC)+"/heartbeat").c_str(), out.c_str(), true, 1); Serial.printf("Heartbeat -> %s\n", out.c_str()); } if (mqtt.connected() && millis() - lastSensorPub > 10000) { lastSensorPub = millis(); float tC, hPct, pHpa, gas; if (readBME(tC, hPct, pHpa, gas)) { JsonDocument s; s["ts"] = (uint32_t)(millis()/1000); s["temp_c"] = tC; s["hum_pct"] = hPct; s["press_hpa"] = pHpa; s["gas_ohm"] = gas; String out; serializeJson(s, out); mqtt.publish((String(BASE_TOPIC)+"/bme688").c_str(), out.c_str(), false, 1); Serial.printf("Sensor -> %s\n", out.c_str()); } else { Serial.println("BME read failed"); } } }
Test it out using this link.