Skip to content

MQTT Message Transmission

MQTT Message Transmission

This section covers the MQTT message transmission from the IoT button — publishing a button press event to the MQTT broker and receiving acknowledgments. By the end of this section, you will be able to:

  • Configure the PubSubClient library for MQTT on ESP32
  • Publish button press events with appropriate payload structure
  • Handle MQTT connection failures gracefully
  • Ensure message delivery with appropriate QoS settings

Before starting this section, please ensure:

The IoT button publishes a simple message when pressed:

Publisher (Button) Broker Subscribers
│ │ │
│ ──► Topic: "factory/button/01" │ │
│ Payload: {"button":"01", │ │
│ "action":"toggle", │ │
│ "battery":3.85} │ │
│ │──► Subscribe ──────► Node-RED
│ │ │
│ │ │
│ ◄── (QoS 1 acknowledgment) │ (Toggle action)

For the work order button system, use a consistent topic hierarchy:

factory/button/{button_id}/{action}
Examples:
- factory/button/01/press — Button 01 pressed
- factory/button/02/press — Button 02 pressed
- factory/button/status/01 — Button 01 status (battery level, online)

Topic design considerations:

  • Unique per button: Each button has a unique ID in the topic
  • Action-based: The last level indicates the action type
  • Wildcard subscribable: Node-RED can subscribe to factory/button/#
{
"button_id": "BTN-001",
"action": "toggle",
"timestamp": 1715901234,
"battery_voltage": 3.85,
"battery_percent": 85,
"rssi": -65,
"firmware": "v1.0"
}

Step 1: Install and Configure PubSubClient

Section titled “Step 1: Install and Configure PubSubClient”
#include <WiFi.h>
#include <PubSubClient.h>
// MQTT Broker configuration
const char* MQTT_BROKER = "192.168.1.100"; // Mosquitto server IP
const int MQTT_PORT = 1883;
const char* MQTT_USER = ""; // Leave empty if no authentication
const char* MQTT_PASS = "";
const char* MQTT_TOPIC = "factory/button/01/press";
// Button identification
const char* BUTTON_ID = "BTN-001";
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
bool connectMQTT() {
mqttClient.setServer(MQTT_BROKER, MQTT_PORT);
// Generate unique client ID
String clientId = "iot_button_";
clientId += String((uint32_t)ESP.getEfuseMac(), HEX);
Serial.print("Connecting to MQTT broker: ");
Serial.println(MQTT_BROKER);
if (mqttClient.connect(clientId.c_str(), MQTT_USER, MQTT_PASS)) {
Serial.println("MQTT connected");
return true;
} else {
Serial.print("MQTT connection failed, state: ");
Serial.println(mqttClient.state());
return false;
}
}

Step 3: Publish Button Press with Battery Status

Section titled “Step 3: Publish Button Press with Battery Status”
#include <ArduinoJson.h>
float readBatteryVoltage() {
int adcValue = analogRead(BATTERY_ADC_PIN);
return (adcValue / 4095.0) * 3.3 * 2; // Voltage divider ×2
}
bool publishButtonPress() {
// Create JSON payload
StaticJsonDocument<256> doc;
doc["button_id"] = BUTTON_ID;
doc["action"] = "toggle";
// Add battery status
float voltage = readBatteryVoltage();
doc["battery_voltage"] = voltage;
doc["battery_percent"] = (int)((voltage - 3.3) / (4.2 - 3.3) * 100);
doc["rssi"] = WiFi.RSSI();
doc["timestamp"] = time(nullptr);
// Serialize to string
char payload[256];
serializeJson(doc, payload);
Serial.print("Publishing: ");
Serial.println(payload);
// Publish with QoS 1 (at least once delivery)
bool success = mqttClient.publish(MQTT_TOPIC, payload, true);
if (success) {
Serial.println("Published successfully");
} else {
Serial.println("Publish failed");
}
return success;
}
void performButtonAction() {
// Step 1: Connect WiFi (from Section 04-07)
if (!connectWiFiWithRetry(2)) {
Serial.println("WiFi failed - cannot publish");
return;
}
// Step 2: Connect MQTT
if (!connectMQTT()) {
Serial.println("MQTT failed - sleeping");
WiFi.disconnect(true);
return;
}
// Step 3: Publish message
publishButtonPress();
// Step 4: Allow MQTT to flush
mqttClient.loop();
delay(100);
// Step 5: Disconnect cleanly
mqttClient.disconnect();
WiFi.disconnect(true);
Serial.println("Button action complete");
}
void setup() {
Serial.begin(115200);
delay(100);
// Button was pressed (wake from deep sleep)
performButtonAction();
// Go back to sleep
Serial.println("Entering deep sleep...");
Serial.flush();
esp_deep_sleep_start();
}

To verify the button messages are being received, use MQTT Explorer or mosquitto_sub:

Terminal window
# Subscribe to all button topics
mosquitto_sub -h 192.168.1.100 -t "factory/button/#" -v
# Expected output:
# factory/button/01/press {"button_id":"BTN-001","action":"toggle","battery_voltage":3.85,...}
  • Button publishes MQTT message when pressed
  • Message is received by the MQTT broker
  • Payload contains correct JSON structure
  • Battery voltage is included in the message
  • Device disconnects MQTT cleanly before sleep

Symptom: mqttClient.connect() returns false

MQTT State Codes:

State CodeMeaningSolution
-4Connection timeoutCheck broker IP and network
-3Connection lostBroker not running
-2Connect failedWrong broker address
-1DisconnectedNormal state before connect
1Protocol errorCheck MQTT version compatibility
2Client ID rejectedCheck for duplicates
3Server unavailableBroker overloaded or not running
4Bad username/passwordCheck credentials
5Not authorizedCheck broker ACL

Solution:

void debugMQTTState() {
int state = mqttClient.state();
switch (state) {
case MQTT_CONNECT_UNAUTHORIZED:
Serial.println("MQTT: Unauthorized - check credentials");
break;
case MQTT_CONNECT_TIMEOUT:
Serial.println("MQTT: Timeout - check broker IP and port");
break;
case MQTT_CONNECT_FAILED:
Serial.println("MQTT: Failed - is broker running?");
break;
default:
Serial.print("MQTT state: ");
Serial.println(state);
}
}

Symptom: Button reports successful publish, but Node-RED doesn’t trigger

Possible Causes:

  • Topic mismatch between button and Node-RED MQTT In node
  • MQTT In node subscribed to wrong topic
  • QoS mismatch (button publishes QoS 1, Node-RED subscribes QoS 0)

Solution: Verify with MQTT Explorer or mosquitto_sub before checking Node-RED.

  • Use QoS 1 for button messages — ensures delivery without excessive overhead
  • Include battery voltage in every message for health monitoring
  • Use unique client IDs based on MAC address to avoid conflicts
  • Disconnect MQTT cleanly before deep sleep
  • Include firmware version for update tracking
  • Do not publish large payloads — keep under 512 bytes for reliability
  • Avoid retained messages for button presses (each press is a new event)
  1. MQTT publish after WiFi connect — sequential, not parallel
  2. JSON payload includes button ID, action, battery level, RSSI, and timestamp
  3. QoS 1 ensures delivery without the overhead of QoS 2
  4. Total active time: ~4-5 seconds (WiFi 2-3s + MQTT 1s + cleanup)
  5. Battery voltage monitoring is critical for proactive maintenance

Target Audience: Alibaba.com IoT Pre-sales Engineers
Status: ✅ Completed