本文將示範使用Arduino Uno控制板搭載乙太網路擴展板,藉由Nick O’Leary先生開發的MQTT前端程式庫,叫做
PubSubClient
,從Arduino發送MQTT主題訊息給Mosquitto伺服器。
PubSubClient程式庫
相容於下列擴展板(shield)和控制板,完整說明請參閱此程式庫的網頁說明。
Arduino Ethernet
Arduino Ethernet Shield
Arduino YUN
Arduino WiFi Shield
Sparkfun WiFly Shield
TI CC3000 WiFi
Intel Galileo/Edison
ESP8266
此程式庫有一些功能上的限制:
只能發布QoS 0訊息
,但可以訂閱QoS 0或QoS 1的主題。
最大訊息長度(含標頭)預設為128位元組
,可透過PubSubClient.h裡的MQTT_MAX_PACKET_SIZE常數值調整。
keepalive(保持連線)
簡隔時間預設為15秒,可透過PubSubClient.h裡的MQTT_KEEPALIVE常數值調整。
用戶端預設採用MQTT 3.1.1標準
,如果你的MQTT伺服器不支援(Mosquitto有支援),可將PubSubClient.h裡的MQTT_VERSION值改成3.1。
安裝PubSubClient程式庫
Arduino IDE(整合開發工具)從1.6.2版開始,支援從「
程式庫管理員(Library Manager)
」新增與更新程式庫的功能。選擇Arduino IDE裡的「
草稿碼→匯入程式庫→管理程式庫
」,開啟「程式庫管理員」。在搜尋欄位輸入關鍵字“mqtt”,可找到許多相關程式庫,請安裝PubSubClient。
如果你使用的是舊版的IDE,需要手動下載安裝程式庫,請到
PubSubClient專案網頁下載
.zip壓縮格式檔,或者直接
按此連結下載
。
下載之後,將它解壓縮存入「
文件\Arduino\libraries
」路徑。
PubSubClient程式庫提供的函式指令介紹
本節介紹稍後將使用的PubSubClient程式庫函式,完整的指令請參閱
官方API文件
,請先略讀本節再閱讀下一節的程式碼。
MQTT的相關指令都要透過PubSubClient物件操作,因此MQTT程式最重要的一步是
建立PubSubClient物件
,程式指令如下:
PubSubClient 物件名稱(網路用戶端物件)
由於MQTT協定基於TCP/IP,因此網路層要透過其他程式庫實作。
採用W5100乙太網路卡的場合,使用官方Ethernet程式庫建立TCP/IP連線
,因此這裡的「網路用戶端」指的是
EthernetClinet類型
的物件。基於乙太網路卡,建立PubSubClient物件的敘述如下:
底下是本文使用的函式指令:
setServer(MQTT伺服器, 埠號)
:指定欲連接的MQTT伺服器的IP位址或網域名稱,以及埠號。
connect(用戶端ID)
:連線到MQTT伺服器,並傳入
自訂的唯一識別碼
。
每個MQTT用戶端都需要一個唯一的識別碼(Client ID,以下稱「用戶端ID」)
,MQTT伺服器透過用戶端ID來識別用戶並且紀錄個別用戶的狀態,像是訂閱的主題和通訊品質設定。根據
MQTT 3.1.1規格書
Client Identifier單元的說明,
用戶端ID的長度為1~23個字元,並且只允許數字和英文字母。
但實際的狀況視伺服器和用戶端使用的軟體而定,例如,HiveMQ公司的MQTT伺服器軟體允許用戶端ID最大長度為65535字元(參閱該公司的
這篇文件
說明),而MQTTLens軟體自動產生的用戶端ID長度則是32個字元(參閱下圖)。話說回來,寫程式的時候還是盡量遵循規格書訂定的規範,以減少相容性的問題。
connected()
:檢查用戶端
是否和伺服器連線
,傳回true代表仍處於連線狀態;false代表已斷線。
publish(主題, 內容)
:發布主題和內容,「主題」與「內容」參數值都是
字元陣列
類型。此函數會傳回一個布林值,true代表發布成功,false代表不成功,可能是斷線或者訊息內容太長。
loop()
:
程式應該要定期呼叫loop()函式
,以便和伺服器保持連線並且處理接收到的訊息。loop()函式會傳回一個布林值,true代表仍與伺服器相連;false代表與伺服器斷線。
Arudino Uno搭載乙太網路擴展板發布MQTT主題訊息
本單元將以Arduino充當MQTT發布者,每隔5秒發布一則隨機溫度和濕度值(JSON格式)的“home/yard/DHT11”主題。
實驗材料:
Arduino Uno控制板 × 1
採用W5100晶片的乙太網路擴展板 × 1
實驗程式:
本實驗程式修改自PubSubClient程式庫內建的mqtt_basic範例,底下是程式的處理流程以及相關PubSubClient指令:
此程式基於Ethernet程式庫,所以寫過Arduino HTTP伺服器或前端程式的讀者應該會感到熟悉,這是主程式部份:
#include <SPI.h>
#include <Ethernet.h>
#include <PubSubClient.h>
// 設定MAC(實體)位址
const byte mac[] = {0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED};
// 設定用戶端和伺服器的IP位址,請自行修改成你的設備的IP位址。
const IPAddress ip(192, 168, 1, 25);
const IPAddress server(192, 168, 1, 19);
// 設定用戶端ID
const char clientID[] = "yard001";
// 設定主題名稱
const char topic[] = "home/yard/DHT11";
// 儲存訊息的字串變數
String msgStr = "";
// 儲存字元陣列格式的訊息字串(參閱下文說明)
char json[25];
EthernetClient ethClient; // 建立乙太網路前端物件
PubSubClient client(ethClient); // 基於乙太網路物件,建立MQTT前端物件
void setup(){
Serial.begin(9600);
// 設定MQTT代理人的網址和埠號
client.setServer(server, 1883);
Ethernet.begin(mac, ip);
// 留點時間給乙太網路卡進行初始化
delay(1500);
void loop(){
// 確認用戶端是否已連上伺服器
if (!client.connected()) {
// 若沒有連上,則執行此自訂函式。
reconnect();
// 更新用戶端狀態
client.loop();
// 建立MQTT訊息(JSON格式的字串)
msgStr = msgStr + "{\"temp\":" + (19 + random(10)) + ",\"humid\":" + 20 + "}";
// 把String字串轉換成字元陣列格式
msgStr.toCharArray(json, 25);
// 發布MQTT主題與訊息
client.publish(topic, json);
// 清空MQTT訊息內容
msgStr = "";
delay(5000);
底下連結MQTT伺服器的reconnect()自訂函式。
void reconnect() {
// 若目前沒有和伺服器相連,則反覆執行直到連結成功…
while (!client.connected()) {
// 指定用戶端ID並連結MQTT伺服器
if (client.connect(clientID)) {
// 若連結成功,在序列埠監控視窗顯示「已連線」。
Serial.println("connected");
} else {
// 若連線不成功,則顯示錯誤訊息
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// 等候5秒,再重新嘗試連線。
delay(5000);
請
按此下載本單元的範例程式碼(.ZIP壓縮格式)
。
實驗結果:
我們將
使用MQTTLens程式訂閱home/yard/DHT11主題
,如果你之前沒有透過它訂閱過這個主題,請先訂閱,然後再把上面的程式上傳到Arduino控制板。每隔5秒,MQTTLens將顯示從Mosquitto伺服器轉送過來的訂閱訊息。
使用String資料類型動態建立字串並轉換成字元陣列
補充說明一下建立MQTT訊息(JSON格式的字串)的敘述。這部份採用String資料類型動態建立字串,子字串和數字之間用“+”運算子相連。
連接字串的最前面要加上這個String類型變數(msgStr),否則編譯過程將引發資料類型錯誤。
由於publish()函式的參數值為字元陣列格式,因此程式要先透過String物件的toCharArray()方法,把String類型的字串轉換成字元陣列格式。
筆者將字元陣列的長度(元素數量)設定成25,足夠此範例的JSON字串使用,如有需要,請自行調整json陣列變數的長度。
MQTT教學(五):「保留」發布訊息以及QoS品質設定
MQTT教學(四):使用MQTTLens訂閱與發布MQTT訊息
MQTT教學(一):認識MQTT
HiveMQ公司的
MQTT Essentials系列文件
Category:
教學文件
硬體與DIY
Tagged:
arduino
ESP8266
由Arduino 發佈 MQTTLens訂閱,MQTTLens可收到訊息
但由MQTTLens發佈 Arduino訂閱,Arduino無法收到訊息
由Arduino發佈 Arduino自己訂閱,Arduino可收到訊息
可以確定Arduino收發正常,但為什麼由MQTTLens發佈訊息Arduino收不到?
可否麻煩您解惑,並告之解決方法,感謝!
EthernetClient ethClient; // 建立乙太網路前端物件
PubSubClient client(ethClient); // 基於乙太網路物件,建立MQTT前端物件
不知為何你要連結兩個server,不過,你也可以寫個伺服器端程式,從一個server傳遞訊息給另一個server。
thanks,
jeffrey
老師您好,請問在樹莓派上安裝了mqtt伺服器 並使用arduino uno +esp8266做連線一開始有回傳數值到樹莓派終端介面,但運作一陣子後就無回傳數值了是與keepalive 有關係嗎?
MQTT是為了窄頻寬、網路通訊品質不佳的環境而設計的協定,前後端斷線是很正常的情況。
如果你需要前後端始終保持連線並即時通訊,可使用WebSocket技術,像Node.js有個socket.io模組,可容易達成這項需求。
thanks,
jeffrey
老師您好,我使用arduino uno +esp8266做連線一開始有回傳數值到AWS雲端BROKER,但運作一陣子後(大概12hr~18hr)就無回傳數值了是與keepalive 有關係嗎?
或者會有其他原因嗎,非常需要您的幫助,謝謝~~!
請查看AWS伺服器的log,了解斷線的原因。
問題應該不是出在keepAlive設定,因為pubsubclient程式預設每15秒會向MQTT broker發出ping訊息來維持連線狀態(參閱
API文件的Configuration Options
裡的MQTT_KEEPALIVE常數)。
你也可以查看pubsubclient的state()方法的傳回值對照錯誤訊息。
thanks,
jeffrey
老師您好,想請問一下我看了「MQTT教學(二)」和「MQTT教學(四)」的介紹,還是搞不懂為甚麼出現failed, rc=-2,可以請老師解答嗎。
另外想問,如果照著您的步驟做,一開始的:
// 設定MAC(實體)位址
const byte mac[] = {0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED};
是要從哪裡查看自己的實體位址呢
// 設定用戶端和伺服器的IP位址,請自行修改成你的設備的IP位址。
const IPAddress ip(192,168,1,25);
const IPAddress server(172,168,1,19);
IP指的是利用CMD–>ipconfig後查到的本機地址
那server呢?
老師您好:
請問mqtt server 的 ip是怎麼決定或設置的? 之前試過localhost and 127.0.0.1都能正常連接,若單純只有伺服器的話,如果從外部像是esp8266進行mqtt 連線,我總是失敗,ipv4位址也不例外,連伺服器都架不成。請教一下關於ip and server 問題,謝謝
老師您好,
最近在開發ESP32連上AWS IOT,如果是以WIFI進行連線,與AWS IOT的連線狀況都很穩定。
但如果是由SIM7020提供網路訊號的話,與AWS IOT的連線狀況就會變得非常不穩定,大約30-40分鐘就會斷線,重新連線一次,不過因為我們的需求需要ESP32時時刻刻在監聽的狀態,所以這麼頻繁的斷線會影響到正常運行。
所以不曉得可否請教老師遇到此類問題,可從何下手改善呢?
非常謝謝!
Ethernet.begin(mac); // 以DHCP方式連線
Serial.print(Ethernet.localIP()); // 顯示分配到的IP位址
void loop() {
三星S22 Ultra手機(三):DIY 58mm相機濾鏡保護殼
三星S22 Ultra手機(二):DIY USB Type-C轉3.5mm耳機DAC音頻轉接線
三星S22 Ultra手機(一):替換藍牙耳機的電池
MQTT教學(十一):上傳資料到ThingSpeak MQTT伺服器的Arduino與MicroPython程式
替MSI電競筆電散熱風扇上潤滑油
搭載Wi-Fi與藍牙通訊晶片的Raspberry Pi Pico W微控制板(二)
搭載Wi-Fi與藍牙通訊晶片的Raspberry Pi Pico W微控制板(一)
更換飛利浦電動刮鬍刀的鎳氫充電電池
安裝macOS版的CH340 USB轉TTL序列通訊IC驅動程式
Arduino IDE 2.0(三):編輯器的新功能介紹
最新回應