MQTT Và ESP8266 | Học ARM
MQTT là gì ? Cách sử dụng MQTT như thế nào ? Các ví dụ về MQTT với ESP8266 được thực hiện ra sao ? Trong bài viết này mình sẽ trình bày một số khái niệm và các bước thực hiện MQTT với ví dụ minh họa cơ bản trên ESP8266 cho mọi người cùng tham khảo.
Trước khi bắt đầu mình cùng xem qua một hình ảnh cơ bản về MQTT
Với hình ảnh trên thì ta có thể thấy MQTT Broker là một trạm trung chuyển, nói đơn giản dễ hiểu thì coi như nó là một trung tâm thông tin, có nhiệm vụ nhận thông tin từ rất nhiều các thiết bị khác nhau và chuyển các thông tin này tới từng thiết bị riêng biệt.
Sẽ có một câu hỏi được đặt ra là Broker MQTT này sẽ lấy ở đâu ra ? Chúng ta sẽ có 2 cách
- Tự tạo Broker MQTT trên máy tính, raspberry, server,.. tùy vào điều kiện hiện có: cách này tốn kém thêm chi phí, phức tạp, không dành cho người mới bắt đầu.
- Sử dụng các dịch vụ MQTT broker có sẵn: cách này dễ thở hơn chút, sử dụng cái sẵn có, thao tác cấu hình lấy thông tin để sử dụng, phù hợp với những người muốn thử nghiệm, tham khảo, tận dụng cái có sẵn. Trong bài viết này mình sẽ sử dụng cách này.
Để hiểu hơn một chút về cách thức hoạt động của MQTT Broker và ESP8266 ta sẽ đi vào ví dụ cụ thể
Chuẩn bị
Phần cứng
- ESP8266 (NodeMCU)
Phần mềm
- Arduino IDE
- Thư viện Pubsubclient
- MQTTlens cho Chrome
Cách kết nối nối cũng như dùng Arduino cho ESP8266 và các vấn đề liên quan các bạn tham khảo ở đây giúp mình nếu các bạn mới bắt đầu tìm hiểu.
Thực hành 1
Sau khi chuẩn bị đầy đủ đồ nghề thì mình bắt tay vào thử chút thôi. Về cách làm mình sẽ dùng ESP8266 (NodeMCU) làm Client để kết nối lên một dịch vụ MQTT Broker là CloudMQTT với 2 nhiệm vụ chính, nhiệm vụ 1 là tham khảo từ Phan Tiến Sang, nhiệm vụ 2 là thay đổi 1 chút để điều khiển LED
- Nhiệm vụ 1: Gửi (publish) dữ liệu lên broker, và nhận thông tin (subcribe) từ broker, mục đích chính là kiểm tra publish và subcribe giữa ESP8266 và MQTT Broker.
- Nhiệm vụ 2: Nhận(subcribe) thông tin từ broker, kiểm tra dữ liệu và thực hiện bật tắt LED
Tạo tài khoản và cấu hình CloudMQTT
Bước 1: Bạn vào trực tiếp trang CloudMQTT để tạo một tài khoản cho mình, sẽ có một đường dẫn để kích hoạt thông tin và cấu hình cho tài khoản của bạn, trừ email và mật khẩu ra thì các ô còn lại các bạn nhập gì tùy thích.
Bước 2: Tạo MQTT Broker bằng cách ấn vào nút Create màu xanh, nhập các thông tin vào như hình.
Bước 3: Chọn Details để cấu hình thông tin
Trong manage User điền tên user là esp8266, password là 123456, sau đó Save
Trong New Rule chọn user như hình, tên topic đặt demo, tick chọn Read,Write
Vậy là xong bước cấu hình broker, kết quả, ta sẽ quan tâm tới 3 ô khoanh đỏ
Lập trình ESP8266
Publish thông tin lên topic
Để ESP8266 có thể publish và subcribe dữ liệu lên MQTT broker thì cần phải có thư viện MQTT, ở đây mình dùng thư viện sẵn có là PubSubClient để thử nghiệm cho nhanh
#include <ESP8266WiFi.h> #include <PubSubClient.h> // Cập nhật thông tin // Thông tin về wifi #define ssid "ten_wifi" #define password "password" // Thông tin về MQTT Broker #define mqtt_server "m12.cloudmqtt.com" // Thay bằng thông tin của bạn #define mqtt_topic_pub "demo" //Giữ nguyên nếu bạn tạo topic tên là demo #define mqtt_topic_sub "demo" #define mqtt_user "esp8266" //Giữ nguyên nếu bạn tạo user là esp8266 và pass là 123456 #define mqtt_pwd "123456" const uint16_t mqtt_port = 10769; //Port của CloudMQTT WiFiClient espClient; PubSubClient client(espClient); long lastMsg = 0; char msg[50]; int value = 0; void setup() { Serial.begin(115200); setup_wifi(); client.setServer(mqtt_server, mqtt_port); client.setCallback(callback); } // Hàm kết nối wifi void setup_wifi() { delay(10); 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()); } // Hàm call back để nhận dữ liệu void callback(char* topic, byte* payload, unsigned int length) { Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] "); for (int i = 0; i < length; i++) { Serial.print((char)payload[i]); } Serial.println(); } // Hàm reconnect thực hiện kết nối lại khi mất kết nối với MQTT Broker void reconnect() { // Chờ tới khi kết nối while (!client.connected()) { Serial.print("Attempting MQTT connection..."); // Thực hiện kết nối với mqtt user và pass if (client.connect("ESP8266Client",mqtt_user, mqtt_pwd)) { Serial.println("connected"); // Khi kết nối sẽ publish thông báo client.publish(mqtt_topic_pub, "ESP_reconnected"); // ... và nhận lại thông tin này client.subscribe(mqtt_topic_sub); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); // Đợi 5s delay(5000); } } } void loop() { // Kiểm tra kết nối if (!client.connected()) { reconnect(); } client.loop(); // Sau mỗi 2s sẽ thực hiện publish dòng hello world lên MQTT broker long now = millis(); if (now - lastMsg > 2000) { lastMsg = now; ++value; snprintf (msg, 75, "hello world #%ld", value); Serial.print("Publish message: "); Serial.println(msg); client.publish(mqtt_topic_pub, msg); } }Mình giải thích qua một chút về chương trình, chương trình này sẽ thực hiện kết nối với mạng wifi trước để có kết nối tới internet, sau đó thực hiện gửi thông tin lên broker bằng lệnh publish, sau đó lắng nghe thông tin có trên Broker(ở đây là thông tin do chính nó gửi lên) và in thông tin này ra terminal của Arduino thông qua hàm callback.
Kết quả
Log Arduino
Log Websocket UI
Bật tắt LED
Coi như đã giao tiếp thực hiện publish và subcribe trên MQTT Broker. Giờ thực hiện cải tiến thêm một chút, là từ giao diện Websocket UI Send message xuống, ở dưới khi nhận được message thì ESP8266 thực hiện bật/tắt LED có sẵn trên kit.
#include <ESP8266WiFi.h> #include <PubSubClient.h> // Cập nhật thông tin // Thông tin về wifi #define ssid "ten_wifi" #define password "password" // Thông tin về MQTT Broker #define mqtt_server "m12.cloudmqtt.com" // Thay bằng thông tin của bạn #define mqtt_topic_pub "demo" //Giữ nguyên nếu bạn tạo topic tên là demo #define mqtt_topic_sub "demo" #define mqtt_user "esp8266" //Giữ nguyên nếu bạn tạo user là esp8266 và pass là 123456 #define mqtt_pwd "123456" const uint16_t mqtt_port = 10769; //Port của CloudMQTT const byte ledPin = D0; WiFiClient espClient; PubSubClient client(espClient); long lastMsg = 0; char msg[50]; int value = 0; void setup() { Serial.begin(115200); setup_wifi(); client.setServer(mqtt_server, mqtt_port); client.setCallback(callback); pinMode(ledPin, OUTPUT); } // Hàm kết nối wifi void setup_wifi() { delay(10); 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()); } // Hàm call back để nhận dữ liệu void callback(char* topic, byte* payload, unsigned int length) { Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] "); for (int i = 0; i < length; i++) { char receivedChar = (char)payload[i]; Serial.print(receivedChar); if (receivedChar == '1') // Kiểm tra nếu tin nhận được là 1 thì bật LED và ngược lại digitalWrite(ledPin, HIGH); if (receivedChar == '0') digitalWrite(ledPin, LOW); } Serial.println(); } // Hàm reconnect thực hiện kết nối lại khi mất kết nối với MQTT Broker void reconnect() { // Chờ tới khi kết nối while (!client.connected()) { Serial.print("Attempting MQTT connection..."); // Thực hiện kết nối với mqtt user và pass if (client.connect("ESP8266Client",mqtt_user, mqtt_pwd)) { Serial.println("connected"); // Khi kết nối sẽ publish thông báo client.publish(mqtt_topic_pub, "ESP_reconnected"); // ... và nhận lại thông tin này client.subscribe(mqtt_topic_sub); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); // Đợi 5s delay(5000); } } } void loop() { // Kiểm tra kết nối if (!client.connected()) { reconnect(); } client.loop(); }Với chương trình này mình sẽ thay đổi và thực hiện kiểm tra gói tin từ Broker gửi về để điều khiển LED trong hàm callback
Kết quả
Các khái niệm trong MQTT ?
Một số ví dụ trên có thể cho chúng ta phần nào hình dung được về MQTT, tất nhiên nó vẫn dừng lại ở mức độ cơ bản. Nói lại cách giao tiếp thông thường khi một device kết nối với server, sẽ gửi POST request với giá trị của sensor(ở đây ví dụ ta sẽ gửi dữ liệu từ sensor lên server), sau đó sẽ là ngắt kết nối với server, đợi khoảng 1 thời gian nào đó, sau đó lặp lại các bước trên. Vậy mỗi lần cần gửi giá trị của sensor thì device lại phải thực hiện kết nối với server lại, việc kết nối nhiều lần ở đây ta thấy là không cần thiết. Ngược lại, nếu chúng ta muốn gửi data từ server về device, thì chỉ có cách duy nhất là device phải kết nối với server và phải thực hiện kiểm tra nếu có update dữ liệu mới. Đây rõ ràng không phải là cách hiệu quả nhất để gửi dữ liệu. Giao thức HTTP sẽ sử dụng phương thức giao tiếp là request-response, phương thức này thì tốt cho việc transfer các dữ liệu lớn nhưng nó không phải là protocol hữu hiệu nhất khi chúng ta chỉ cần gửi vài byte dữ liệu(giá trị sensor), và đó là lý do cần phải dùng MQTT
MQTT (Message Queuing Telemetry Transport) là một giao thức gởi dạng publish/subscribe sử dụng cho các thiết bị Internet of Things với băng thông thấp, độ tin cậy cao và khả năng được sử dụng trong mạng lưới không ổn định. Bởi vì giao thức này sử dụng băng thông thấp trong môi trường có độ trễ cao nên nó là một giao thức lý tưởng cho các ứng dụng M2M. MQTT cũng là giao thức sử dụng trong Facebook Messager.
Các định nghĩa “subscribe”, “publish”, “qos”, “retain”, “last will and testament (lwt)” trong giao thức MQTT là gì ?
Publish, subscribe
Trong một hệ thống sử dụng giao thức MQTT, nhiều node trạm (gọi là mqtt client – gọi tắt là client) kết nối tới một MQTT server (gọi là broker). Mỗi client sẽ đăng ký một vài kênh (topic), ví dụ như “/client1/channel1”, “/client1/channel2”. Quá trình đăng ký này gọi là “subscribe”, giống như chúng ta đăng ký nhận tin trên một kênh Youtube vậy. Mỗi client sẽ nhận được dữ liệu khi bất kỳ trạm nào khác gởi dữ liệu và kênh đã đăng ký. Khi một client gởi dữ liệu tới kênh đó, gọi là “publish”.
QoS
Ở đây có 3 tuỳ chọn *QoS (Qualities of service) * khi “publish” và “subscribe”:
- QoS0 Broker/client sẽ gởi dữ liệu đúng 1 lần, quá trình gởi được xác nhận bởi chỉ giao thức TCP/IP, giống kiểu đem con bỏ chợ.
- QoS1 Broker/client sẽ gởi dữ liệu với ít nhất 1 lần xác nhận từ đầu kia, nghĩa là có thể có nhiều hơn 1 lần xác nhận đã nhận được dữ liệu.
- QoS2 Broker/client đảm bảm khi gởi dữ liệu thì phía nhận chỉ nhận được đúng 1 lần, quá trình này phải trải qua 4 bước bắt tay.
Bạn có thể tham khảo thêm về QoS
Một gói tin có thể được gởi ở bất kỳ QoS nào, và các client cũng có thể subscribe với bất kỳ yêu cầu QoS nào. Có nghĩa là client sẽ lựa chọn QoS tối đa mà nó có để nhận tin. Ví dụ, nếu 1 gói dữ liệu được publish với QoS2, và client subscribe với QoS0, thì gói dữ liệu được nhận về client này sẽ được broker gởi với QoS0, và 1 client khác đăng ký cùng kênh này với QoS 2, thì nó sẽ được Broker gởi dữ liệu với QoS2.
Một ví dụ khác, nếu 1 client subscribe với QoS2 và gói dữ liệu gởi vào kênh đó publish với QoS0 thì client đó sẽ được Broker gởi dữ liệu với QoS0. QoS càng cao thì càng đáng tin cậy, đồng thời độ trễ và băng thông đòi hỏi cũng cao hơn.
Retain
Nếu RETAIN được set bằng 1, khi gói tin được publish từ Client, Broker PHẢI lưu trữ lại gói tin với QoS, và nó sẽ được gởi đến bất kỳ Client nào subscribe cùng kênh trong tương lai. Khi một Client kết nối tới Broker và subscribe, nó sẽ nhận được gói tin cuối cùng có RETAIN = 1 với bất kỳ topic nào mà nó đăng ký trùng. Tuy nhiên, nếu Broker nhận được gói tin mà có QoS = 0 và RETAIN = 1, nó sẽ huỷ tất cả các gói tin có RETAIN = 1 trước đó. Và phải lưu gói tin này lại, nhưng hoàn toàn có thể huỷ bất kỳ lúc nào.
Khi publish một gói dữ liệu đến Client, Broker phải se RETAIN = 1 nếu gói được gởi như là kết quả của việc subscribe mới của Client (giống như tin nhắn ACK báo subscribe thành công). RETAIN phải bằng 0 nếu không quan tâm tới kết quả của viẹc subscribe.
LWT
Gói tin LWT (last will and testament) không thực sự biết được Client có trực tuyến hay không, cái này do gói tin KeepAlive đảm nhận. Tuy nhiên gói tin LWT như là thông tin điều gì sẽ xảy đến sau khi thiết bị ngoại tuyến.
MQTT hoạt động như thế nào ?
MQTT rất nhẹ và nhanh, chỉ cần vài byte để kết nối tới server và kết nối này được giữ mọi lúc. Kết nối của nó sẽ dùng ít dữ liệu và thời gian hơn giao thức HTTP. Vậy MQTT hoạt động ra sao ?
Toàn bộ hệ thống của chúng ta sẽ bao gồm rất nhiều clients và 1 broker duy nhất, tất cả các device của chúng ta được gọi chung là clients, client cũng có thể là điện thoại, là laptop, là pc hoặc là esp8266. Mỗi device này chỉ kết nối với một broker duy nhất và chúng không kết nối với nhau.
Toàn bộ hệ thống sẽ dựa trên phương thức kết nối là publish – subcribe, mỗi client có thể là một publisher – gửi messages, hoặc là subcriber – lắng nghe message tới, hoặc đồng thời là cả 2 trong cùng 1 thời điểm.
Broker ở đây là loại server với nhiệm vụ là cho phép publish mesage từ publisher và chuyển tiếp nó tới subcriber.
Vậy làm thế nào để broker hiểu được để chuyển đúng message tới subscriber ? Chúng ta thử nghĩ nếu mỗi subscriber đều nhận được mọi message, trong khi đó có rất nhiều publisher gửi message trong hệ thống, nghĩa là subscriber sẽ nhận được 1 đống tin nhắn rác, để giải quyết vấn đề này thì cần có các topic.
Mỗi publisher sẽ gửi tới một hoặc nhiều topic và mỗi subscriber sẽ subscribe từ một hoặc nhiều topic có liên quan. Các topic này sẽ có dạng cấu trúc cây, và được phân chia bởi dấu /
Ví dụ 1: Chúng ta có nhiệt độ môi trường ở một số nơi khác nhau và tôi thì thích nhiệt độ ở trong vườn nhà mình. Tôi sẽ dùng smartphone để subscribe topic là Sensors/Garden/Temperature. Còn device dùng để đo nhiệt độ môi trường là ESP8266 sẽ được đặt ở trong vườn, kết nối với internet và publish giá trị nhiệt độ lên topic Sensors/Garden/Temperature, và lúc này thì smartphone của tôi sẽ nhận được thông tin giá trị nhiệt độ hiện tại.
Ví dụ 2: Một ví dụ khác khi bạn dùng với LWT và keep alive là ta có 1 cảm biến, nó gửi những dữ liệu quan trọng và rất không thường xuyên. Nó có publish trước với Broker một tin nhắn lwt ở topic /node/gone-offline với tin nhắn id của nó. Và tôi cũng subscribe topic /node/gone-offline, sẽ gởi SMS tới điện thoại thôi mỗi khi nhận được tin nhắn nào ở kênh mà tôi subscribe.Trong quá trình hoạt động, cảm biến luôn giữ kết nối với Broker bởi việc luôn gởi gói tin keepAlive. Nhưng nếu vì lý do gì đó, cảm biến này chuyển sang ngoại tuyến, kết nối tới Broker timeout do Broker không còn nhận được gói keepAlive.Lúc này, do cảm biến của tôi đã đăng ký LWT, do vậy broker sẽ đóng kết nối của Cảm biến, đồng thời sẽ publish một gói tin là Id của cảm biến vào kênh /node/gone-offline, dĩ nhiên là tôi cũng sẽ nhận được tin nhắn báo rằng cảm biến yêu quý của mình đã ngoại tuyến.
Tóm gọn: Ngoài việc đóng kết nối của Client đã ngoại tuyến, gói tin LWT có thể được định nghĩa trước và được gởi bởi Broker tới kênh nào đó khi thiết bị đăng ký LWT ngoại tuyến.
Một ví dụ minh họa sống động về MQTT pub sub lên các topic bạn có thể xem tại đây
Thực hành 2
Tiếp tục từ bài thực hành 1 thì chúng ta sẽ phát triển thêm một chút nữa, xây dựng hệ thống với 2 client là eps8266 và laptop(pub sub lên broker dùng mqttlen) và broker là cloudMQTT
ESP8266 sẽ thực hiện publish data lên topic esp/test sau mỗi 10s và subcribe data từ broker
#include <ESP8266WiFi.h> #include <PubSubClient.h> const char* ssid = "wifi_name"; const char* password = "pass"; const char* mqttServer = "m12.cloudmqtt.com"; const int mqttPort = 10769; const char* mqttUser = "your_username"; const char* mqttPassword = "your_password"; long lastMsg = 0; char msg[50]; int value = 0; WiFiClient espClient; PubSubClient client(espClient); void setup() { Serial.begin(115200); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.println("Connecting to WiFi.."); } Serial.println("Connected to the WiFi network"); client.setServer(mqttServer, mqttPort); client.setCallback(callback); while (!client.connected()) { Serial.println("Connecting to MQTT..."); if (client.connect("ESP8266Client", mqttUser, mqttPassword )) { Serial.println("connected"); } else { Serial.print("failed with state "); Serial.print(client.state()); delay(2000); } } //Pub Hello to esp/test topic and sub this topic client.publish("esp/test", "Hello from ESP8266"); client.subscribe("esp/test"); } void callback(char* topic, byte* payload, unsigned int length) { Serial.print("Message arrived in topic: "); Serial.println(topic); Serial.print("Message:"); for (int i = 0; i < length; i++) { Serial.print((char)payload[i]); } Serial.println(); Serial.println("-----------------------"); } void loop() { client.loop(); long now = millis(); if (now - lastMsg > 8000) { lastMsg = now; ++value; snprintf (msg, 75, "hello world #%ld", value); // client.publish(mqtt_topic_pub, msg); if (value < 10) { Serial.print("Publish message: "); Serial.println(msg); client.publish("esp/test", msg); } } }PC thực hiện publish/subscribe data lên topic esp/test bằng mqttlen
- Cấu hình MQTTlens, bạn tạo mới một connection với tên bất kỳ, hostname là tên server cloudmqtt với port là 10769, lưu ý phần credentials nhập username và password trên cloudmqtt của bạn vào
- Publish/Subcribe với MQTTlen: để nhận được message đưa lên broker tại topic esp/test thì ta nhập topic này tại phần subscribe, sau đó ấn nút subscribe, và để publish message lên topic esp/test trên broker thì ta nhập topic và nội dung message, sau đó ấn publish.
Phần subscriptions sẽ là thông tin của các message gửi lên topic, ở đây ta có thể thấy thông tin từ esp8266 gửi lên broker thông qua topic là esp/test
Kết quả sau khi publish và subcribe với ESP8266: ta thấy các message publish lên sẽ có dạng hello world #x, khi có một message được publish từ mqttlens thì ESP sẽ nhận được tin với nội dung Xin chao hocARM
Tạm kết
Vậy là mình đã đi từ thực hành trước để các bạn có thể hình dung được sơ bộ MQTT, sau đó là một ít khái niệm cơ bản, và cuối cùng lại quay về với thực hành trên 2 device pub/sub lên 1 topic qua broker. Ta có thể nhận thấy với thư viện PubsubClient thì chỉ thấy được pub, sub, mình cũng có kiểm tra xem có Qos, LWT không nhưng chưa thấy trong thư viện. Vẫn còn rất nhiều điều phải làm với MQTT, còn một cách hay ho hơn là kết hợp MQTT và tạo giao diện điều khiển với nodered thông qua bài viết sau.
Còn nếu bạn muốn chi tiết hơn về cách tự tạo MQTT Broker và Client thì sao ? Mời các bạn theo dõi bài viết sau
Tham khảo
[1][2][3]
Từ khóa » Mqtt Với Esp8266
-
[IoT] Bài 7: ESP8266 - Arduino Ide Và Giao Thức MQTT
-
ESP8266 – MQTT - Unicloud Blogs
-
ESP8266 Phần 3: MQTT Và Esp8266 - STKT Blog
-
Node-red MQTT Và ESP8266 - Duy Đặng - Robot And Technology
-
Thiết Kế Hệ Thống IoT đơn Giản Dùng Giao Thức MQTT Kết Hợp PHP ...
-
ESP8266 Và Giao Thức MQTT Cơ Bản | MCA - MicroController Academy
-
KẾT NỐI VỚI MQTT SỬ DỤNG ESP8266/ESP32 - P1 - YouTube
-
ESP8266 Connects To MQTT Broker With Arduino - EMQ
-
Connect ESP8266 + Local MQTT On Window Using Mosquitto #2
-
Giao Tiếp ESP32 Với ESP8266 Qua Giao Thức MQTT | HTT Offical
-
Vấn đề Về MQTT Và ESP8266 - Programming - Dạy Nhau Học
-
Thiminhnhut/esp8266-cloud-mqtt - GitHub