Hướng dẫn MQTT Arduino ESP32 – Xuất bản và đăng ký chủ đề
Trong hướng dẫn này, mình sẽ hướng dẫn bạn cách sử dụng giao thức truyền thông MQTT Arduino ESP32 để xuất bản tin nhắn và đăng ký chủ đề. Cụ thể, chúng ta sẽ xuất bản thông tin từ cảm biến nhiệt độ, độ ẩm BME280 và kiểm soát đầu ra trên ESP32, thông qua phần mềm lập trình Arduino.
Giới thiệu về dự án MQTT Arduino ESP32
Trong bài hướng dẫn MQTT Arduino ESP32 này, chúng ta sẽ sử dụng ứng dụng Node-RED để kiểm soát đầu ra ESP32 và nhận thông tin dữ liệu từ cảm biến, dựa trên giao thức MQTT. Node-Red hiện đang chạy trên mạch Raspberry Pi.
Mình sẽ sử dụng Mosquitto Broker Installed trên cùng một Raspberry Pi. Broker này chịu trách nhiệm nhận các thông tin tin nhắn, lọc và gửi chúng tới những khách hàng đã đăng ký nhận tin nhắn đó. Cụ thể:
- Ứng dụng Node-RED gửi thông báo là on hoặc off trong chủ đề ESP32/output. Mạch ESP32 đăng ký vào chủ đề này để nhận thông tin bật hoặc tắt đèn LED
- Mạch ESP32 xuất bản thông tin nhiệt độ trong chủ đề ESP32.temperature và thông tin độ ẩm trong ESP32/humidity. Ứng dụng Node-RED sẽ đăng ký các chủ đề này để đọc thông tin nhiệt độ, độ ẩm và hiển thị chúng lên biểu đồ hoặc bảng… (tùy vào nhu cầu của người lập trình).
Chuẩn bị
- Mạch Raspberry (mình đang dùng Raspberry 3)
- Mạch ESP32
- Cảm biến nhiệt độ, độ ẩm BME280
- Đèn LED
- Điện trở
- Breadboard
- Dây Jumper
Cảm biến BME280 là gì?
BME280 là một cảm biến giúp đo nhiệt độ, độ ẩm và áp suất. Vì áp suất là khái niệm liên quan đến độ cao, nên chúng ta cũng có thể dùng BME280 để đo và ước lượng độ cao.
Tuy nhiên, trong phạm vi hướng dẫn này thì IoTZone sẽ chỉ hướng dẫn cách đọc nhiệt độ, độ ẩm. Có khá nhiều phiên bản BME khác nhau, nhưng mình chọn dùng loại bên dưới, bạn tham khảo nhé:
Cảm biến BME280 này có thể giao tiếp thông qua giao thức SPI hoặc là I2C. Nếu dùng SPI, bạn hãy dùng các chân sau:
- SCK – chân đồng hồ SPI
- SDO – MISO
- SDI – MOSI
- CS – Chân chọn chip
Nếu dùng I2C, bạn cần dùng các chân:
- SCK – Chân SCL
- SDI – SDA
Kết nối phần cứng
Bạn hãy kết nối mạch ESP32 với cảm biến BME280 theo sơ đồ dưới đây nhé. Ngoài ra, chúng ta cũng cần kết nối một đèn LED với GPIO 4 trên mạch ESP32 để điều khiển:
Dưới đây là hình ảnh thực tế sau khi ban kết nối mạch cho dự án MQTT Arduino ESP32:
Cài đặt thư viện trong Arduino IDE
Đầu tiên, bạn cần cài đặt tiện ích ESP32 trong Arduino IDE nhé! IoTZone đã có hướng dẫn chi tiết cho phần này, bạn xem tại link: Cách lập trình ESP32 bằng Arduino IDE (Windows, Linux, Mac OS X)
Cài thư viện PubSubClient
Đây là thư viện hỗ trợ chúng ta trong việc xuất bản tin nhắn và đăng ký chủ đề đơn giản, thông qua một Server hỗ trợ MQTT. Bạn có thể hiểu đơn giản đây là một thư viện giúp ESP32 giao tiếp được với ứng dụng Node-RED.
Bạn làm theo hướng dẫn sau để cài thư viện:
- Nhấn vào link sau và tải thư viện về dưới dạng .Zip
- Giải nén và đổi tên thư viện thành pubsubclient
- Di chuyển thư viện vào trong folder thư viện của Arduino IDE
- Tắt và mở lại Arduino
Thư viện này có sẵn một số sketch mẫu, bạn có thể truy cập vào File >> Example >> PubSubClient trong Arduino IDE để tham khảo nhé!
Lưu ý: Thư viện PubSubClient không tương thích tốt với ESP32, nhưng trong ví dụ này mình chạy thử thì khá ổn.
Cài thư viện BME280
- Nhấn vào link sau và tải thư viện về dưới dạng .Zip
- Giải nén và đổi tên thư viện thành Adafruit_BME280_Library
- Di chuyển thư viện vào trong folder thư viện của Arduino IDE
- Tắt và mở lại Arduino
Cài thư viện Adafbean_Sensor
- Nhấn vào link sau và tải thư viện về dưới dạng .Zip
- Giải nén và đổi tên thư viện thành Adafruit_Sensor
- Di chuyển thư viện vào trong folder thư viện của Arduino IDE
- Tắt và mở lại Arduino
Chương trình
Mình đã chuẩn bị sẵn code dự án MQTT Arduino ESP32, bạn chỉ cần chỉnh sửa SSID, mật khẩu, địa chỉ IP của Raspberry bên bạn rồi nạp code và demo xem kết quả nhé:
#include <WiFi.h> #include <PubSubClient.h> #include <Wire.h> #include <Adafruit_BME280.h> #include <Adafruit_Sensor.h> // Replace the next variables with your SSID/Password combination const char* ssid = "REPLACE_WITH_YOUR_SSID"; const char* password = "REPLACE_WITH_YOUR_PASSWORD"; // Add your MQTT Broker IP address, example: //const char* mqtt_server = "192.168.1.144"; const char* mqtt_server = "YOUR_MQTT_BROKER_IP_ADDRESS"; WiFiClient espClient; PubSubClient client(espClient); long lastMsg = 0; char msg[50]; int value = 0; //uncomment the following lines if you're using SPI /*#include <SPI.h> #define BME_SCK 18 #define BME_MISO 19 #define BME_MOSI 23 #define BME_CS 5*/ Adafruit_BME280 bme; // I2C //Adafruit_BME280 bme(BME_CS); // hardware SPI //Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI float temperature = 0; float humidity = 0; // LED Pin const int ledPin = 4; void setup() { Serial.begin(115200); // default settings // (you can also pass in a Wire library object like &Wire2) //status = bme.begin(); if (!bme.begin(0x76)) { Serial.println("Could not find a valid BME280 sensor, check wiring!"); while (1); } setup_wifi(); client.setServer(mqtt_server, 1883); client.setCallback(callback); pinMode(ledPin, OUTPUT); } void setup_wifi() { delay(10); // We start by connecting to a WiFi network Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); } void callback(char* topic, byte* message, unsigned int length) { Serial.print("Message arrived on topic: "); Serial.print(topic); Serial.print(". Message: "); String messageTemp; for (int i = 0; i < length; i++) { Serial.print((char)message[i]); messageTemp += (char)message[i]; } Serial.println(); // Feel free to add more if statements to control more GPIOs with MQTT // If a message is received on the topic esp32/output, you check if the message is either "on" or "off". // Changes the output state according to the message if (String(topic) == "esp32/output") { Serial.print("Changing output to "); if(messageTemp == "on"){ Serial.println("on"); digitalWrite(ledPin, HIGH); } else if(messageTemp == "off"){ Serial.println("off"); digitalWrite(ledPin, LOW); } } } void reconnect() { // Loop until we're reconnected while (!client.connected()) { Serial.print("Attempting MQTT connection..."); // Attempt to connect if (client.connect("ESP8266Client")) { Serial.println("connected"); // Subscribe client.subscribe("esp32/output"); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); // Wait 5 seconds before retrying delay(5000); } } } void loop() { if (!client.connected()) { reconnect(); } client.loop(); long now = millis(); if (now - lastMsg > 5000) { lastMsg = now; // Temperature in Celsius temperature = bme.readTemperature(); // Uncomment the next line to set temperature in Fahrenheit // (and comment the previous temperature line) //temperature = 1.8 * bme.readTemperature() + 32; // Temperature in Fahrenheit // Convert the value to a char array char tempString[8]; dtostrf(temperature, 1, 2, tempString); Serial.print("Temperature: "); Serial.println(tempString); client.publish("esp32/temperature", tempString); humidity = bme.readHumidity(); // Convert the value to a char array char humString[8]; dtostrf(humidity, 1, 2, humString); Serial.print("Humidity: "); Serial.println(humString); client.publish("esp32/humidity", humString); } } 1
Như chương trình trên, mình đang dùng chủ đề esp32/temperature và esp32/humidity để gửi thông tin về nhiệt độ, độ ẩm dựa trên giao thức MQTT.
Mạch ESP32 đăng ký chủ đề esp32/output để nhận các tin nhắn từ Node-RED, để ESP32 biết nên điều khiển bật hay tắt đèn LED.
Giải thích chương trình
Đăng ký chủ đề MQTT
Trong phần recoonect(), mình đăng ký chủ đề MQTT cho ESP32 là esp32/output:
client.subscribe("esp32/output");
Trong hàm callback, ESP32 nhận thông báo về các chủ đề đã đăng ký, để có thể điều chỉnh đèn LED sáng hoặc tắt:
// If a message is received on the topic esp32/output, you check if the message is either "on" or "off". // Changes the output state according to the message if (String(topic) == "esp32/output") { Serial.print("Changing output to "); if (messageTemp == "on") { Serial.println("on"); digitalWrite(ledPin, HIGH); } else if (messageTemp == "off") { Serial.println("off"); digitalWrite(ledPin, LOW); } }
Xuất bản tin nhắn trên MQTT
Trong loop(), mình sẽ xuất bản các bài đọc mới sau mỗi 5 giây:
if (now - lastMsg > 5000) { ... }
Ở chế độ mặc định, mạch ESP32 sẽ gửi nhiệt độ dưới dạng độ C. Nếu thích thì bạn có thể bỏ ghi chú ở dòng cuối cùng, để ESP32 gửi dưới dạng độ F nhé:
// Temperature in Celsius temperature = bme.readTemperature(); // Uncomment the next line to set temperature in Fahrenheit // (and comment the previous temperature line) //temperature = 1.8 * bme.readTemperature() + 32; // Temperature in Fahrenheit
Đổi biến nhiệt độ float thành dạng char array để xuất bản nhiệt độ lên chủ đề esp32/temperature:
// Convert the value to a char array char tempString[8]; dtostrf(temperature, 1, 2, tempString); Serial.print("Temperature: "); Serial.println(tempString); client.publish("esp32/temperature", tempString);
Thực hiện tương tự với độ ẩm:
humidity = bme.readHumidity(); // Convert the value to a char array char humString[8]; dtostrf(humidity, 1, 2, humString); Serial.print("Humidity: "); Serial.println(humString); client.publish("esp32/humidity", humString);
Tạo flow trong Node-RED
Trước đó, bạn cần cài đặt Node-RED, Node-RED Dashboard và Mosquitto Broker trên mạch Raspberry của bạn nhé! Phần này mình sẽ hướng dẫn ở một bài viết khác.
Sau khi cài đặt các phần trên, bạn đi tới GitHub hoặc xem trực tiếp tệp thô ở bên dưới, sau đó sao chép mã và nhập flow Node-RED đã được cung cấp:
[{"id":"9e58624.7faaba","type":"mqtt out","z":"c02b79b2.501998","name":"","topic":"esp32/output","qos":"","retain":"","broker":"10e78a89.5b4fd5","x":610,"y":342,"wires":[]},{"id":"abf7079a.653be8","type":"mqtt in","z":"c02b79b2.501998","name":"","topic":"esp32/temperature","qos":"2","broker":"10e78a89.5b4fd5","x":484,"y":249,"wires":[["cc79021b.9a751","21eae8f8.2971b8"]]},{"id":"83cf37cf.c76988","type":"ui_switch","z":"c02b79b2.501998","name":"","label":"Output","group":"61285987.c20328","order":0,"width":0,"height":0,"passthru":true,"decouple":"false","topic":"","style":"","onvalue":"on","onvalueType":"str","onicon":"","oncolor":"","offvalue":"off","offvalueType":"str","officon":"","offcolor":"","x":469,"y":342,"wires":[["9e58624.7faaba"]]},{"id":"cc79021b.9a751","type":"debug","z":"c02b79b2.501998","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":681,"y":216,"wires":[]},{"id":"4aecba01.78ce64","type":"mqtt in","z":"c02b79b2.501998","name":"","topic":"esp32/humidity","qos":"2","broker":"10e78a89.5b4fd5","x":473,"y":133,"wires":[["22efa7b7.544a28","df37e6b7.64c1c8"]]},{"id":"22efa7b7.544a28","type":"debug","z":"c02b79b2.501998","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":100,"wires":[]},{"id":"21eae8f8.2971b8","type":"ui_chart","z":"c02b79b2.501998","name":"","group":"61285987.c20328","order":0,"width":0,"height":0,"label":"Temperature","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":false,"x":681,"y":276,"wires":[[],[]]},{"id":"df37e6b7.64c1c8","type":"ui_gauge","z":"c02b79b2.501998","name":"","group":"61285987.c20328","order":0,"width":0,"height":0,"gtype":"gage","title":"Humidity","label":"%","format":"{{value}}","min":0,"max":"100","colors":["#00b3d9","#0073e6","#001bd7"],"seg1":"33","seg2":"66","x":660,"y":160,"wires":[]},{"id":"10e78a89.5b4fd5","type":"mqtt-broker","z":"","name":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"61285987.c20328","type":"ui_group","z":"","name":"Main","tab":"e7c46d5e.a1283","disp":true,"width":"6","collapse":false},{"id":"e7c46d5e.a1283","type":"ui_tab","z":"","name":"Dashboard","icon":"dashboard"}] 1
Trong cửa sổ Node-RED, bạn click vào Import >> Clipboard:
Sau đó, bạn paste mã đã copy và chọn vào Import.
Khi đó, trên màn hình sẽ tải các nodes như sau:
Sau khi thay đổi chỉnh sửa, bạn hãy nhấn vào nút Deploy để lưu lại các thay đổi.
Node-RED UI
Tới bước này, ứng dung Node-RED của bạn đã sẵn sàng cho dự án MQTT Arduino ESP32 rồi đấy! Để truy cập vào Node-RED UI và xem cách ứng dụng hoạt động, bạn hãy mở bất kỳ trình duyệt Internet nào và nhập URL sau:
http://Your_RPi_IP_address:1880/ui
Ứng dụng Node-RED của bạn sẽ xuất hiện giống như bên dưới, cho phép bạn điều khiển đèn LED bật và tắt, đồng thời bạn cũng quan sát được biểu đồ nhiệt độ và giá trị độ ẩm như bên dưới:
Demo kết quả
Bây giờ, bạn có thể quan sát và theo dõi kết quả dự án MQTT Arduino ESP32 của mình rồi đấy. Bạn có thể mở Serial Monitor trên Arduino để quan sát các tin nhắn đang được xuất bản và nhận trong từng chủ đề:
Lời kết
Trong bài viết trên, mình đã hướng dẫn bạn các khái niệm cơ bản và cách làm một dự án MQTT Arduino ESP32 cơ bản với đèn LED và ccarm biến BME280, thông qua ứng dụng Node-RED và MQTT. Bạn có thể nâng cấp dự án này của mình sao cho sáng tạo hơn, ứng dụng vào tự động hóa trong Smart Home chẳng hạn. Chúc các bạn thành công!
Comments (2)
Tại sao có thư viện cho cảm biến bme280 rồi thì anh lại cài thêm là thư viện adafruit_Sensor ạ, thư viện này có cả sensor nào khác trong bài à a?
Hi bạn. Để sử dụng thư viện BM280 thì mình cần cài thư viện Adafruit_Sensor nha