Hướng dẫn dùng MQTT với ESP32: publish dữ liệu cảm biến (DHT11) lên topic và subscribe lệnh điều khiển LED từ server. Có ví dụ mã đầy đủ, cấu trúc topic, JSON payload, và cách tự reconnect.
Mục lục
- Giới thiệu
- MQTT hoạt động thế nào?
- Phần cứng cần chuẩn bị
- Sơ đồ kết nối
- Cài thư viện & cấu hình
- Mã nguồn ví dụ hoàn chỉnh
- Giải thích mã
- Test nhanh bằng MQTT client
- Lỗi thường gặp & cách khắc phục
- Gợi ý mở rộng
1) Giới thiệu
MQTT là giao thức nhẹ cho IoT, tối ưu băng thông và độ trễ thấp. Với ESP32, bạn có thể:
- Publish dữ liệu cảm biến lên topic (telemetry).
- Subscribe lệnh từ server để điều khiển LED/relay.
- Tự reconnect khi WiFi/MQTT bị gián đoạn.
2) MQTT hoạt động thế nào?
- Broker (ví dụ: Mosquitto, EMQX, HiveMQ) làm “trung tâm”.
- Thiết bị publish vào một topic (vd: iot/orgA/dev01/telemetry).
- Ứng dụng khác subscribe topic đó để nhận dữ liệu.
- Có QoS 0/1/2, retained message, Last Will để báo mất kết nối.
Cấu trúc topic khuyến nghị:
iot/<org>/<deviceId>/telemetry
iot/<org>/<deviceId>/cmd/led
Payload JSON mẫu (telemetry):
{"ts": 1731148800000, "temp": 29.5, "hum": 62.1, "device": "dev01"}
3) Phần cứng cần chuẩn bị
| Linh kiện | Số lượng | Ghi chú |
| ESP32 DevKit V1 | 1 | |
| Cảm biến DHT11 | 1 | DATA → GPIO4 (từ Bài 4) |
| LED + điện trở 220Ω | 1 | LED trạng thái, GPIO2 |
| Dây nối, breadboard | Vài sợi |
4) Sơ đồ kết nối (Connection Diagram)

Tóm tắt dây:
- DHT11: VCC → 3V3, GND → GND, DATA → GPIO4 (+ 10kΩ kéo lên 3V3 nếu cảm biến rời).
- LED: Anode → GPIO2 qua 220Ω, Cathode → GND.
5) Cài thư viện & cấu hình
- PubSubClient (by Nick O’Leary) – Tools → Manage Libraries…
- DHT sensor library + Adafruit Unified Sensor (nếu chưa cài từ Bài 4)
Khai báo broker:
- Nếu có broker riêng: mqtt_host = “mqtt.yourdomain.com” (TLS khuyến nghị).
- Nếu test nhanh: dùng broker public (không bảo mật – chỉ dùng thử), ví dụ broker.hivemq.com.
6) Mã nguồn ví dụ hoàn chỉnh
#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
// ==== WiFi & MQTT ====
const char* ssid = "YourWiFiName";
const char* password = "YourPassword";
// Dùng broker riêng nếu có (khuyến nghị). Public chỉ để demo.
const char* mqtt_host = "broker.hivemq.com";
const uint16_t mqtt_port = 1883; // TLS thường 8883 (cần thêm WiFiClientSecure)
const char* org = "orgA";
const char* deviceId = "dev01";
String topicTelemetry = String("iot/") + org + "/" + deviceId + "/telemetry";
String topicCmdLed = String("iot/") + org + "/" + deviceId + "/cmd/led";
String lastWillTopic = String("iot/") + org + "/" + deviceId + "/status";
// ==== DHT & LED ====
#define DHTPIN 4
#define DHTTYPE DHT11
#define LED_PIN 2
DHT dht(DHTPIN, DHTTYPE);
WiFiClient espClient;
PubSubClient mqtt(espClient);
// Forward declare
void ensureMqtt();
void onMqttMessage(char* topic, byte* payload, unsigned int length);
void setup() {
Serial.begin(115200);
delay(500);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
// WiFi
Serial.print("Connecting WiFi");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
Serial.println("\n✅ WiFi connected: " + WiFi.localIP().toString());
// MQTT
mqtt.setServer(mqtt_host, mqtt_port);
mqtt.setCallback(onMqttMessage);
dht.begin();
}
unsigned long lastPub = 0;
void loop() {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("⚠️ WiFi lost. Reconnecting...");
WiFi.reconnect();
delay(1000);
}
ensureMqtt();
mqtt.loop();
// Publish telemetry mỗi 5 giây
unsigned long now = millis();
if (now - lastPub > 5000) {
lastPub = now;
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(h) || isnan(t)) {
Serial.println("❌ DHT read failed");
return;
}
// JSON đơn giản (có thể dùng ArduinoJson cho payload lớn)
String payload = String("{\"ts\":") + String((unsigned long)(millis())) +
",\"temp\":" + String(t, 2) +
",\"hum\":" + String(h, 2) +
",\"device\":\"" + deviceId + "\"}";
boolean ok = mqtt.publish(topicTelemetry.c_str(), payload.c_str(), true); // retained
Serial.print(ok ? "✅ Published: " : "❌ Publish failed: ");
Serial.println(payload);
}
}
// Đảm bảo MQTT luôn kết nối, có Last Will & subscribe lệnh
void ensureMqtt() {
while (!mqtt.connected()) {
Serial.print("Connecting MQTT...");
String clientId = "esp32-" + String(deviceId) + "-" + String(random(0xffff), HEX);
// Last Will: báo "offline" khi mất kết nối đột ngột
if (mqtt.connect(clientId.c_str(), NULL, NULL,
lastWillTopic.c_str(), 0, true, "offline")) {
Serial.println(" connected.");
// Đánh dấu online
mqtt.publish(lastWillTopic.c_str(), "online", true);
// Nhận lệnh LED
mqtt.subscribe(topicCmdLed.c_str());
Serial.print("Subscribed: "); Serial.println(topicCmdLed);
} else {
Serial.print(" failed, rc="); Serial.print(mqtt.state());
Serial.println(" | retry in 2s");
delay(2000);
}
}
}
// Xử lý lệnh: iot/<org>/<deviceId>/cmd/led -> payload: "ON" / "OFF" / "TOGGLE"
void onMqttMessage(char* topic, byte* payload, unsigned int length) {
String msg; msg.reserve(length);
for (unsigned int i = 0; i < length; i++) msg += (char)payload[i];
Serial.print("? ["); Serial.print(topic); Serial.print("] "); Serial.println(msg);
if (String(topic) == topicCmdLed) {
if (msg == "ON") digitalWrite(LED_PIN, HIGH);
else if (msg == "OFF") digitalWrite(LED_PIN, LOW);
else if (msg == "TOGGLE") digitalWrite(LED_PIN, !digitalRead(LED_PIN));
}
}
7) Giải thích mã
- PubSubClient xử lý MQTT; setServer() chỉ định broker; setCallback() nhận tin.
- Last Will: lastWillTopic mang trạng thái “offline” khi thiết bị rớt kết nối đột ngột.
- Retained: publish retained giúp client mới subscribe sẽ nhận trạng thái mới nhất.
- ensureMqtt(): tự kết nối lại & subscribe topic lệnh.
- onMqttMessage(): nhận lệnh “ON” | “OFF” | “TOGGLE” để điều khiển LED.
8) Test nhanh bằng MQTT client
Bạn có thể dùng:
- MQTTX (GUI)
- MQTT Explorer
- Hoặc mosquitto_pub/sub (CLI)
Ví dụ (CLI):
# Subscribe telemetry
mosquitto_sub -h broker.hivemq.com -t "iot/orgA/dev01/telemetry" -v
# Gửi lệnh bật LED
mosquitto_pub -h broker.hivemq.com -t "iot/orgA/dev01/cmd/led" -m "ON" -q 1
9) Lỗi thường gặp & cách khắc phục
- Không kết nối được broker → Kiểm tra host/port, tường lửa router, hoặc đổi mạng.
- Mất kết nối liên tục → Tăng keepAlive, kiểm tra nguồn ESP32, bắt WiFi yếu.
- DHT đọc lỗi → Tăng delay giữa các lần đọc; kiểm tra điện trở kéo lên cho DATA.
- Topic sai chính tả → Dùng biến topicTelemetry, topicCmdLed thống nhất.
10) Gợi ý mở rộng
- Dùng TLS (8883) với WiFiClientSecure + chứng chỉ CA.
- Thêm QoS 1 cho gói lệnh quan trọng.
- Lưu local buffer (SPIFFS/LittleFS) để retry khi mất mạng.
- Gửi batched JSON (nhiều mẫu/gói) để tối ưu băng thông.
- Tạo dashboard bằng Node-RED / Grafana / Home Assistant.


Để lại một bình luận