Thư viện PubSubClient: subscribe/publish MQTT trên ESP32/Arduino
PubSubClient của Nick O'Leary là thư viện MQTT phổ biến nhất cho Arduino/ESP32 — gọn nhẹ, ổn định, hỗ trợ MQTT 3.1.1. Bài này liệt kê toàn bộ API kèm pattern cho production.
1. Cài đặt
Library Manager → "PubSubClient" by Nick O'Leary. Cài tự động dependency: không có. Yêu cầu một Client (vd WiFiClient, WiFiClientSecure, EthernetClient).
2. Khởi tạo
#include
#include
WiFiClient wifiClient;
PubSubClient mqtt(wifiClient);
void setup() {
mqtt.setServer("192.168.1.100", 1883);
mqtt.setCallback(onMessage);
}
3. Connect — có và không auth
mqtt.connect("client-id");
mqtt.connect("client-id", "username", "password");
mqtt.connect("client-id", "user", "pass",
"home/status", 0, true, "offline");
// Last Will: nếu disconnect bất thường, broker tự publish "offline"
Client ID phải unique trên broker — trùng = thiết bị kia bị đá. Best practice: dùng MAC address:
String cid = "esp-" + String((uint32_t)ESP.getEfuseMac(), HEX);
mqtt.connect(cid.c_str());
4. Publish
mqtt.publish("home/temp", "25.4");
mqtt.publish("home/temp", "25.4", true); // retain
mqtt.publish("home/raw", payload, length); // raw bytes
Trả về bool — false khi disconnect hoặc payload > buffer (mặc định 256 byte).
5. Subscribe
mqtt.subscribe("home/livingroom/cmd");
mqtt.subscribe("home/+/temp"); // wildcard 1 cấp
mqtt.subscribe("home/#"); // wildcard nhiều cấp
mqtt.subscribe("home/cmd", 1); // QoS 1
mqtt.unsubscribe("home/cmd");
6. Callback nhận message
void onMessage(char* topic, byte* payload, unsigned int len) {
String s;
for (unsigned i = 0; i < len; i++) s += (char)payload[i];
Serial.printf("[%s] %s\n", topic, s.c_str());
if (strcmp(topic, "home/cmd") == 0) {
if (s == "on") digitalWrite(LED, HIGH);
if (s == "off") digitalWrite(LED, LOW);
}
}
Quan trọng: payload KHÔNG null-terminated, chỉ có len byte. Đừng dùng strcmp(payload, "on") trực tiếp — đọc rác.
7. Loop — bắt buộc
void loop() {
if (!mqtt.connected()) reconnect();
mqtt.loop(); // xử lý keepalive, callback
// ... code khác
}
Không gọi mqtt.loop() = không nhận message, disconnect sau 15s.
8. Reconnect with backoff
void reconnect() {
uint32_t delay_ms = 500;
while (!mqtt.connected()) {
String cid = "esp-" + String((uint32_t)ESP.getEfuseMac(), HEX);
if (mqtt.connect(cid.c_str(), USER, PASS,
"home/status", 0, true, "offline")) {
mqtt.publish("home/status", "online", true);
mqtt.subscribe("home/cmd");
return;
}
Serial.print("rc="); Serial.println(mqtt.state());
delay(delay_ms);
delay_ms = min((uint32_t)10000, delay_ms * 2); // backoff
}
}
9. Buffer size — gửi JSON lớn
Mặc định payload tối đa 256 byte (header + topic + data). JSON dài 500 byte sẽ fail. Tăng buffer:
mqtt.setBufferSize(1024); // gọi trước connect()
Hoặc set qua define MQTT_MAX_PACKET_SIZE khi compile.
10. Keepalive
mqtt.setKeepAlive(60); // mặc định 15s
Tăng nếu network chậm, giảm nếu cần phát hiện disconnect nhanh.
11. Sockets timeout
mqtt.setSocketTimeout(10); // giây, mặc định 15
12. State codes — debug fail connect
| state() | Ý nghĩa |
|---|---|
| 0 | MQTT_CONNECTED |
| -1 | MQTT_DISCONNECTED |
| -2 | MQTT_CONNECT_FAILED — TCP fail (broker không reach) |
| -3 | MQTT_CONNECTION_LOST |
| -4 | MQTT_CONNECTION_TIMEOUT |
| 2 | MQTT_CONNECT_BAD_PROTOCOL |
| 3 | MQTT_CONNECT_BAD_CLIENT_ID |
| 4 | MQTT_CONNECT_BAD_CREDENTIALS |
| 5 | MQTT_CONNECT_UNAUTHORIZED |
13. TLS / mTLS
WiFiClientSecure secureClient;
secureClient.setCACert(rootCA);
// hoặc skip verify (chỉ debug):
// secureClient.setInsecure();
PubSubClient mqtt(secureClient);
mqtt.setServer("broker.hivemq.com", 8883);
14. Async — khi cần hiệu năng cao
PubSubClient là synchronous, block khi gửi. Cần xử lý nhanh và đa connection: chuyển sang AsyncMqttClient (Marvin Roger) — based on AsyncTCP, không block, callback-driven.
Liên quan
Hướng dẫn bối cảnh đầy đủ: ESP32 + MQTT với PubSubClient từ A-Z. Dùng cùng ArduinoJson để truyền JSON. Tham khảo dự án Smart home MQTT + Home Assistant với ESP32.