並讓 Raspberry pi 與 NodeMCU 做為發佈者(Publisher)或訂閱者(Subscriber)
雙向操控(或告知) NodeMCU 上的 LED 燈
MQTT 在訊息傳遞的運作模式可參考此文,簡單的來說就是在一個架構中會存在三種角色
1. Broker(轉送訊息者)
2. 發佈者(Publisher)
3. 訂閱者(Subscriber)
以及一個 "基於目錄架構的主題(topic)"
作為一個訂閱者可以使用
mosquitto_sub -t "/leds/esp8266"
來訂閱一個主題,上述的語句代表讓 Rpi 作為一個訂閱者,訂閱的主題為目錄 leds 下的 esp8266
mosquitto_pub -t "/leds/esp8266" -m "TOOGLE"
作為一個發佈者可以使用
mosquitto_pub -t "/leds/esp8266" -m "TOOGLE"
意謂著將訊息 TOOGLE 發佈到 leds 下的 esp8266 主題
這邊只是先展示關係而已,詳細如何使用還得先建立 MQTT 服務才行,見下文
-------------------------------------------------------------------------------------------
首先 Rpi 的部份要先進行 MQTT 服務的搭建,可參考此文、此文以及此文
sudo wget http://repo.mosquitto.org/debian/mosquitto-repo.gpg.key
sudo apt-key add mosquitto-repo.gpg.key
cd /etc/apt/sources.list.d/
sudo wget http://repo.mosquitto.org/debian/mosquitto-wheezy.list
sudo apt-get update
Install Mosquitto Broker
sudo apt-get install mosquitto
Install Mosquitto Client
sudo apt-get install mosquitto-clients
查看 MQTT 狀態
systemctl status mosquitto.service
圖中的綠色部分 active(rinning) 就表示 Rpi 上的 MQTT 服務已經啟動
順帶一提, pi@WYJ-raspi 之中的 'WYJ-raspi' 為你在 Rpi 設的 Host name ,先記起來後面會用到
Hint : 若想在 Python 上面使用 MQTT 服務可以輸入以下
sudo pip install paho-mqtt
但此部分在本文中並不會使用到,可以略過
當我們建立起來 MQTT 的服務之後
首先我們試試看使用 Rpi 來訂閱一個主題
mosquitto_sub -t "/leds/esp8266"
上述的語句代表讓 Rpi 作為一個訂閱者,訂閱的主題為目錄 leds 下的 esp8266
此時你的終端機會卡住,因為它正在聆聽這個主題的最新發佈訊息
接著我們開啟一個新的終端機,同樣進入 Rpi 後輸入
mosquitto_pub -t "/leds/esp8266" -m "TOGGLE"
意謂著將訊息 TOOGLE 發佈到 leds 下的 esp8266 主題
對照兩個終端機的畫面可以看到如下圖
發布訊息 TOOGLE 到 leds 下的 esp8266 主題
訂閱者這邊接收到 TOOGLE 訊息
Raspberry pi 這部分就暫時完成了,將終端機介面留著,拿出 NodeMCU
首先在 Arduino IDE 安裝三個 Library
硬體接線的部分,在 NodeMCU 的 D3 腳位接上 LED ; D2 腳位接上一個按鈕(低位觸發)
圖我就不給了,很簡單的接線
NodeMCU 程式碼的部分為以下
#include "ESP8266WIFI.h"
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"
// **************** 程式碼修改自 **********************
// http://www.instructables.com/id/Raspberry-Pi-Talking-to-ESP8266-Using-MQTT/
/************************* WiFi Access Point *********************************/
#define WLAN_SSID "WiFi帳號"
#define WLAN_PASS "WiFi密碼"
// 若你的 NodeMCU 與 Rpi 同個網域,可以使用內網的方式連到 Rpi 的 MQTT Broker
#define MQTT_SERVER "192.168.0.105" // MQTT Broker 所在的 ip 位置
#define MQTT_PORT 1883 // mosquitto 預設的 port 1883
// 若 NodeMCU 與 Rpi 不同網域,則可以將 Rpi 網域的分享器設置虛擬伺服器
// 從外部的某個 port 連到 Rpi 網域的內部 1883 port
//#define MQTT_SERVER "實體ip 或網域名稱"
//#define MQTT_PORT 外部port
#define MQTT_USERNAME "" // 預設不需使用帳號密碼來登入 MQTT
#define MQTT_PASSWORD ""
#define LED_PIN D3
#define BUTTON_PIN D2
uint32_t x=0;
void MQTT_connect();
/************ Global State ******************/
WiFiClient client; // Create an WiFiClient class
// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
Adafruit_MQTT_Client mqtt(&client, MQTT_SERVER, MQTT_PORT, MQTT_USERNAME, MQTT_PASSWORD);
// 實體化esp8266_led_P 用以對 '/leds/esp8266' 發布訊息
Adafruit_MQTT_Publish esp8266_led_P = Adafruit_MQTT_Publish(&mqtt, MQTT_USERNAME "/leds/esp8266");
// 實體化 esp8266_led 用以對 '/leds/esp8266' 接收訊息
Adafruit_MQTT_Subscribe esp8266_led = Adafruit_MQTT_Subscribe(&mqtt, MQTT_USERNAME "/leds/esp8266");
/*************************** Sketch Code ************************************/
// ===== SETUP =====
// =================
void setup() {
Serial.begin(115200);
delay(10);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
pinMode(BUTTON_PIN, INPUT);
Serial.println(); Serial.println();
Serial.print("Connecting to ");
Serial.println(WLAN_SSID);
WiFi.begin(WLAN_SSID, WLAN_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.println("WiFi connected");
Serial.println("Local IP address: "); Serial.println(WiFi.localIP());
// Setup MQTT subscription for esp8266_led.
mqtt.subscribe(&esp8266_led);
}
// ===== LOOP =====
// ================
void loop() {
// 讀取當前 BUTTON 狀態
int button_first = digitalRead(BUTTON_PIN);
// 不斷確定有連接到 MQTT Server 若斷線則自動重連
MQTT_connect();
// 聆聽各種訂閱的主題
Adafruit_MQTT_Subscribe *subscription;
while ((subscription = mqtt.readSubscription())) {
if (subscription == &esp8266_led) { //如果有來自 '/leds/esp8266' 的訊息
char *message = (char *)esp8266_led.lastread; //抓取訊息
Serial.print("Got: ");
Serial.println(message); // 印出接收到的訊息
// 藉由訊息,來使 LED 做相應的動作
if (strncmp(message, "ON", 2) == 0) {
digitalWrite(LED_PIN, HIGH);
}
else if (strncmp(message, "OFF", 3) == 0) {
digitalWrite(LED_PIN, LOW);
}
else if (strncmp(message, "TOGGLE", 6) == 0) {
digitalWrite(LED_PIN, !digitalRead(LED_PIN));
}
}
}// 聆聽各種訂閱的主題 END
delay(20);
int button_second = digitalRead(BUTTON_PIN); // 讀取當前 BUTTON 狀態
if ((button_first == HIGH) && (button_second == LOW)) { //代表按鈕被按下
Serial.println("Button is pressed!");
digitalWrite(LED_PIN,!digitalRead(LED_PIN)); //反轉 LED 燈狀態
//讀取目前 LED 燈狀態
if (digitalRead(LED_PIN) == HIGH){
esp8266_led_P.publish("Led trun ON by NodeMCU"); //若目前 LED 亮
}
else{
esp8266_led_P.publish("Led trun OFF by NodeMCU"); //若目前 LED 滅
}
}
}// void loop() END
// ===== MQTT_connect =====
// =========================
void MQTT_connect() {
int8_t ret;
// 如果目前已經連上 MQTT Server 則直接返回
if (mqtt.connected()) {
return;
}
Serial.print("Connecting to MQTT... ");
uint8_t retries = 3;
while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
Serial.println(mqtt.connectErrorString(ret));
Serial.println("Retrying MQTT connection in 5 seconds...");
mqtt.disconnect();
delay(5000); // wait 5 seconds
retries--;
if (retries == 0) {
// 連接至 MQTT 失敗三次,則基本上認為版子掛了
while (1); // 故意用一個 while(1) 死迴圈來觸發 NodeMCU 的 Watchdog 來重啟版子
}
}
Serial.println("MQTT Connected!");
}
關於設定連接 port 的部分請參考
一起學 Python 110 : 外部網路連回家中網路 遠端操控 Raspberry pi (使用 No-ip )
結果展示
關於手機的部分,加入傳參數到 MQTT 的功能,請參考下一章
一起學 Python 114 : Rapsberry pi 與 NodeMCU (ESP8266) 溝通 基於 MQTT - 1
補充指令
mosquitto 設定檔
sudo nano /etc/mosquitto/mosquitto.conf
mosquitto 重啟
service mosquitto restart
mosquitto 刪除
sudo apt-get remove --purge --auto-remove mosquitto
查看連接埠
netstat -a
查看錯誤記錄
nano /var/log/mosquitto/mosquitto.log
ps : 前提是在 /etc/mosquitto/mosquitto.conf 裡面有設定
log_dest file /var/log/mosquitto/mosquitto.log
[20200726 更新]
某次想查閱 /var/log/mosquitto/mosquitto.log 的 Log 時發現怎樣都找不到這個文件
後來發現,我們必須要自己手動產生出這個文件後, mosquitto 才會把 Log 存放到這個文件內
可能是這個套件的一個 bug
正常來說這個文件如果不存在的話應該要由它自動產生的才是
參考連結
RASPBERRY PI TALKING TO ESP8266 USING MQTT
樹莓派安裝 Mosquitto 輕量級 MQTT Broker 教學,連接各種物聯網設備
Mosquitto MQTT 安裝