Arduino MQTT Protocol
Introduction
MQTT (Message Queuing Telemetry Transport) is a lightweight messaging protocol designed for constrained devices and low-bandwidth, high-latency networks. It's particularly well-suited for Internet of Things (IoT) applications, making it an excellent choice for Arduino projects that need to communicate over networks.
In this tutorial, you'll learn:
- What MQTT is and why it's valuable for IoT projects
- The core concepts of MQTT communication
- How to set up an Arduino to communicate using MQTT
- How to create practical IoT applications using Arduino and MQTT
What is MQTT?
MQTT operates on a publish/subscribe model, which differs from the traditional client-server model. Instead of direct communication between devices, MQTT uses a central broker that manages all messages.
Key MQTT Concepts
- Broker: The central server that receives all messages and routes them to the appropriate subscribers.
- Client: Any device that connects to the broker to publish or subscribe to messages (e.g., your Arduino).
- Topic: A hierarchical string that the broker uses to filter messages for each client (e.g.,
home/livingroom/temperature
). - Publish: Sending a message to a specific topic.
- Subscribe: Registering interest in receiving messages from specific topics.
- QoS (Quality of Service): Determines the guarantee of message delivery (0, 1, or 2).
Getting Started with MQTT on Arduino
Required Hardware
- Arduino board (UNO, Mega, ESP8266, or ESP32)
- Network connectivity (Ethernet shield, WiFi shield, or built-in WiFi for ESP8266/ESP32)
Required Libraries
Before diving into code, you'll need to install the appropriate libraries:
- For Arduino with Ethernet Shield: PubSubClient
- For ESP8266/ESP32: PubSubClient or AsyncMqttClient
You can install these libraries through the Arduino IDE by navigating to:
Sketch > Include Library > Manage Libraries
Basic MQTT Connection with Arduino and Ethernet Shield
Here's a simple example to connect an Arduino with Ethernet shield to an MQTT broker:
#include <SPI.h>
#include <Ethernet.h>
#include <PubSubClient.h>
// Network configuration
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 1, 100);
IPAddress mqttServer(192, 168, 1, 50); // MQTT broker IP address
// MQTT configuration
const char* clientID = "arduinoClient";
const char* topic = "arduino/test";
EthernetClient ethClient;
PubSubClient client(ethClient);
void setup() {
Serial.begin(9600);
// Initialize Ethernet
Ethernet.begin(mac, ip);
Serial.println("Ethernet connected");
// Set MQTT server and callback function
client.setServer(mqttServer, 1883);
client.setCallback(callback);
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
// Publish a message every 5 seconds
static unsigned long lastMsg = 0;
if (millis() - lastMsg > 5000) {
lastMsg = millis();
String message = "Hello from Arduino: " + String(lastMsg);
client.publish(topic, message.c_str());
Serial.println("Message published: " + message);
}
}
void callback(char* topic, byte* payload, unsigned int length) {
// Convert payload to string
char message[length + 1];
memcpy(message, payload, length);
message[length] = '\0';
Serial.print("Message received on topic: ");
Serial.println(topic);
Serial.print("Message: ");
Serial.println(message);
}
void reconnect() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
if (client.connect(clientID)) {
Serial.println("connected");
client.subscribe(topic);
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" retrying in 5 seconds");
delay(5000);
}
}
}
Expected Output:
When running this code, you should see output similar to:
Ethernet connected
Attempting MQTT connection...connected
Message published: Hello from Arduino: 5000
Message published: Hello from Arduino: 10000
Message received on topic: arduino/test
Message: Hello from Arduino: 10000
MQTT with ESP8266
If you're using an ESP8266 or ESP32, which come with built-in WiFi, the setup is slightly different:
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
// WiFi configuration
const char* ssid = "YourWiFiSSID";
const char* password = "YourWiFiPassword";
// MQTT configuration
const char* mqttServer = "broker.example.com";
const int mqttPort = 1883;
const char* mqttUser = "yourusername"; // Leave empty if no authentication
const char* mqttPassword = "yourpassword"; // Leave empty if no authentication
const char* clientID = "ESP8266Client";
const char* topic = "esp8266/test";
WiFiClient espClient;
PubSubClient client(espClient);
void setup() {
Serial.begin(115200);
// Connect to WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("WiFi connected");
// Set MQTT server and callback
client.setServer(mqttServer, mqttPort);
client.setCallback(callback);
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
// Publish a message every 5 seconds
static unsigned long lastMsg = 0;
if (millis() - lastMsg > 5000) {
lastMsg = millis();
String message = "Hello from ESP8266: " + String(lastMsg);
client.publish(topic, message.c_str());
Serial.println("Message published: " + message);
}
}
void callback(char* topic, byte* payload, unsigned int length) {
// Convert payload to string
char message[length + 1];
memcpy(message, payload, length);
message[length] = '\0';
Serial.print("Message received on topic: ");
Serial.println(topic);
Serial.print("Message: ");
Serial.println(message);
}
void reconnect() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
if (client.connect(clientID, mqttUser, mqttPassword)) {
Serial.println("connected");
client.subscribe(topic);
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" retrying in 5 seconds");
delay(5000);
}
}
}
Practical MQTT Examples
Let's explore some practical applications of MQTT with Arduino.
Example 1: Temperature and Humidity Monitoring
This example uses a DHT11 or DHT22 sensor to measure temperature and humidity, then publishes the data to MQTT topics.
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
// WiFi and MQTT configuration
const char* ssid = "YourWiFiSSID";
const char* password = "YourWiFiPassword";
const char* mqttServer = "broker.example.com";
const int mqttPort = 1883;
const char* clientID = "DHT11_Sensor";
// DHT sensor configuration
#define DHTPIN 2 // GPIO pin where the DHT11 is connected
#define DHTTYPE DHT11 // DHT 11 or DHT22
DHT dht(DHTPIN, DHTTYPE);
// MQTT topics
const char* tempTopic = "sensors/temperature";
const char* humidTopic = "sensors/humidity";
WiFiClient espClient;
PubSubClient client(espClient);
void setup() {
Serial.begin(115200);
dht.begin();
// Connect to WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("WiFi connected");
// Set MQTT server
client.setServer(mqttServer, mqttPort);
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
// Read sensor data every 30 seconds
static unsigned long lastMsg = 0;
if (millis() - lastMsg > 30000) {
lastMsg = millis();
// Read temperature and humidity
float temperature = dht.readTemperature();
float humidity = dht.readHumidity();
// Check if reading is valid
if (!isnan(temperature) && !isnan(humidity)) {
// Convert to string
String tempStr = String(temperature);
String humidStr = String(humidity);
// Publish to MQTT
client.publish(tempTopic, tempStr.c_str());
client.publish(humidTopic, humidStr.c_str());
Serial.println("Temperature: " + tempStr + "°C");
Serial.println("Humidity: " + humidStr + "%");
} else {
Serial.println("Failed to read from DHT sensor!");
}
}
}
void reconnect() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
if (client.connect(clientID)) {
Serial.println("connected");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" retrying in 5 seconds");
delay(5000);
}
}
}
Example 2: IoT LED Control
This example allows you to control an LED connected to your Arduino through MQTT messages:
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
// WiFi and MQTT configuration
const char* ssid = "YourWiFiSSID";
const char* password = "YourWiFiPassword";
const char* mqttServer = "broker.example.com";
const int mqttPort = 1883;
const char* clientID = "LED_Controller";
// LED configuration
const int ledPin = 5; // D1 on NodeMCU
// MQTT topics
const char* ledTopic = "arduino/led";
const char* statusTopic = "arduino/led/status";
WiFiClient espClient;
PubSubClient client(espClient);
void setup() {
Serial.begin(115200);
// Initialize LED pin
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
// Connect to WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("WiFi connected");
// Set MQTT server and callback
client.setServer(mqttServer, mqttPort);
client.setCallback(callback);
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
// Publish LED status every 60 seconds (or when changed)
static unsigned long lastStatus = 0;
if (millis() - lastStatus > 60000) {
lastStatus = millis();
publishLedStatus();
}
}
void callback(char* topic, byte* payload, unsigned int length) {
// Convert payload to string
char message[length + 1];
memcpy(message, payload, length);
message[length] = '\0';
Serial.print("Message received on topic: ");
Serial.println(topic);
Serial.print("Message: ");
Serial.println(message);
// Check if message is for LED control
if (strcmp(topic, ledTopic) == 0) {
if (strcmp(message, "ON") == 0) {
digitalWrite(ledPin, HIGH);
Serial.println("LED turned ON");
}
else if (strcmp(message, "OFF") == 0) {
digitalWrite(ledPin, LOW);
Serial.println("LED turned OFF");
}
// Publish updated status
publishLedStatus();
}
}
void publishLedStatus() {
if (digitalRead(ledPin) == HIGH) {
client.publish(statusTopic, "ON");
} else {
client.publish(statusTopic, "OFF");
}
}
void reconnect() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
if (client.connect(clientID)) {
Serial.println("connected");
client.subscribe(ledTopic);
// Publish initial status
publishLedStatus();
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" retrying in 5 seconds");
delay(5000);
}
}
}
MQTT Best Practices
When working with MQTT in Arduino projects, keep these best practices in mind:
-
Topic Structure: Use a hierarchical topic structure like
home/livingroom/temperature
to organize your data logically. -
QoS Levels: Choose the appropriate Quality of Service:
- QoS 0: "At most once" (fire and forget)
- QoS 1: "At least once" (confirmed delivery)
- QoS 2: "Exactly once" (assured delivery)
For most Arduino applications, QoS 0 or 1 is sufficient.
-
Retained Messages: Set the retained flag to true for messages that need to be delivered to new subscribers immediately when they connect.
-
Last Will and Testament (LWT): Use LWT to notify other clients when a device disconnects unexpectedly.
// Example of setting up Last Will and Testament
client.connect(clientID, mqttUser, mqttPassword, "devices/status", 0, 1, "offline");
-
Keep Alive: Set an appropriate keep-alive interval based on your network conditions.
-
Persistent Sessions: Use persistent sessions to receive missed messages after reconnection.
Setting Up an MQTT Broker
For testing or small projects, you can set up a local MQTT broker:
-
Mosquitto: A lightweight open-source MQTT broker
- Installation on Windows: Download from the official Eclipse Mosquitto website
- Installation on Linux:
sudo apt install mosquitto mosquitto-clients
- Installation on macOS:
brew install mosquitto
-
Public MQTT Brokers: For testing, you can use public brokers like:
- test.mosquitto.org
- broker.hivemq.com
- iot.eclipse.org
⚠️ Important: Do not use public brokers for sensitive data or production applications!
Debugging MQTT Communications
When working with MQTT, these tools can help debug your communications:
- MQTT Explorer: A graphical client that lets you visualize and interact with MQTT messages
- Mosquitto Client Tools: Command-line tools to publish and subscribe to topics
bash
# Subscribe to a topic
mosquitto_sub -h broker.example.com -t "arduino/test" -v
# Publish to a topic
mosquitto_pub -h broker.example.com -t "arduino/led" -m "ON"
Summary
MQTT is a powerful protocol for IoT applications with Arduino. Its lightweight nature and publish/subscribe architecture make it ideal for constrained devices and networks.
In this tutorial, you've learned:
- The basic concepts of MQTT
- How to connect Arduino to an MQTT broker
- How to publish messages from Arduino
- How to subscribe to topics and process incoming messages
- Practical examples of MQTT in IoT projects
Additional Resources
-
Libraries:
-
Tools:
Exercises
- Create an MQTT-based weather station that publishes temperature, humidity, and pressure readings.
- Build a home automation system that controls multiple LEDs or relays via MQTT.
- Develop a two-way communication system between two Arduinos using MQTT.
- Create a data logger that subscribes to multiple sensors and saves the data to an SD card.
- Build an MQTT-based alarm system that sends notifications when motion is detected.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)