iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 26
1
IoT

來與IoT譜寫一首戀愛樂章吧系列 第 26

op.26 《全領域》-全域開發實戰 - 居家植物盆栽 Mvt I (NodeMCU & MQTT)

op.26 打造屬於妳的時空廊道

為你我打造一個專屬妳的自由往返通道
讓妳可以任意地穿越
不再受拘束

今天開始來將前幾天用到的東西進行實作,預計架構會長這樣。
https://ithelp.ithome.com.tw/upload/images/20201011/20129084uszQWipTVD.png

大略地介紹,會經過 NodeMCU 來定時去監測土壤濕度的變化,經由 MQTT 的方式進行回傳,那經由 Broker 處理訊息後,將濕度儲存至資料庫;而手機也可以實際地收到濕度的數值。

今天是一個硬體的實作部分。
需要準備的材料:

NodeMCU
https://ithelp.ithome.com.tw/upload/images/20201011/201290841ovsnniOt7.jpg

土壤濕度感測器
https://ithelp.ithome.com.tw/upload/images/20201011/20129084jna6XySqxw.jpg

從土壤濕度的感測器的參考資料之中,我們可以看到這個模組具有兩種輸出模式,一種為 類比 輸出;另一種是經過類似史密特觸發的電路轉成 數位 的輸出。

既然要監測 濕度 ,那我們就選擇這模組的類比輸出。

從這張圖片我們可以確定可以使用 A0 當輸入腳位,模組的 Vcc 接至 3v3,GND 接到 GND。

那我們就需要先透過 NodeMcu 來讀取類比電壓。

#define Sensor_Pin A0

void setup() {
  Serial.begin(115200);
  pinMode(Sensor_Pin,INPUT);
}

void loop() { 
  Serial.println(analogRead(Sensor_Pin));
  delay(1000);
}

當感測器插入濕潤的土裡時,數據呈現是這樣。
https://ithelp.ithome.com.tw/upload/images/20201011/20129084p0yxQRJyPj.jpg
https://ithelp.ithome.com.tw/upload/images/20201011/20129084nJpf2SXVKr.png

當感測器拔出濕潤的土裡時,數據呈現是這樣。
https://ithelp.ithome.com.tw/upload/images/20201011/20129084gjiasz6yvk.jpg
https://ithelp.ithome.com.tw/upload/images/20201011/20129084jetw3zfuro.png

所以可以知道濕度上升、類比數值下降,接下來就可以準備將這些的資料透過 MQTT 發送囉。


首先還是需要新增新的程式庫,這裡需要新增 PubSubClient ,一樣地可以在 程式庫管理員搜尋到
https://ithelp.ithome.com.tw/upload/images/20201011/20129084oYB2JYWWJD.png

然後開啟之前的連上網路那篇文章裡的程式碼
op.07 《感知層》-連上熟悉的 Wi-Fi 吧

由於需要連接 MQTT 的伺服器,所以一樣也要來撰寫 MQTT 的程式,想必寫過 Flutter 後,下面的程式會比較好懂吧XD

const char* mqttServer = "MQTT Broker 位置";
const char* mqttUserName = "User01"; 
const char* mqttPwd = "MQTT Broker 密碼";
const char* clientID = "ClientID";
const char* topic = "要發布的主題";

另外因為是定時偵測,需要宣告針對偵測相關的變數

unsigned long prevMillis = 0;   // 暫存經過時間(毫秒) 
const long interval = 20000;    // 上傳資料的間隔時間,20秒。 
String msgStr = "";             // 暫存MQTT訊息字串

int Potted_num =0;              // 暫存類比輸入的數值

最後要宣告兩個物件,分別是 WiFiClient 與 PubSubPubSubClient,將 WiFiClient 的參數帶入 PubSubPubSubClient。

WiFiClient NodeMcuClient;
PubSubClient client(NodeMcuClient);

由於受限於韌體限制,這個套件並沒有將保持 MQTT 連線一直占用,過一定時間即會斷線,初始為 15 秒,所以要先來寫一個 reconnect 函式,讓我們可以再長時間使用時可以進行重連後傳送資料。

void reconnect() {
  while (!client.connected()) {
    if (client.connect(clientID, mqttUserName, mqttPwd)) {
      Serial.println("MQTT connected");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      delay(5000);  // 等5秒之後再重試
    }
  }
}

接下來需要在 setup() 裡加入設定 MQTT 伺服器的參數

client.setServer(mqttServer, 1883);    //對應 IP 與 連接埠

進到 loop() 的部分,我們必須先在偵測數據前,確認 MQTT 是否連接,如果沒有擇要進行連接,有的話則讓 MQTT 保持連線。

if (!client.connected()) {
    reconnect();
  }
  client.loop();

接著準備進行土壤濕度的收集。

if (millis() - prevMillis > interval) {
    prevMillis = millis();
    Potted_num =analogRead(Sensor_Pin);
  }

當數據收集完成時,便可以透過 MQTT 來發送,所以必須將數值轉成 MQTT 的形式發送。

msgStr=String(Potted_num);
byte arrSize = msgStr.length() + 1;
char msg[arrSize];
msgStr.toCharArray(msg, arrSize); // 把String字串轉換成字元陣列格式
client.publish(topic, msg);       // 發布MQTT主題與訊息
msgStr = "";

首先需要先將 int 型態轉成 String 並存入 msgStr 裡;由於 publish() 的訊息格式為 字元陣列,所以需要將字串轉成字元陣列,而根據 C 的特性,字串轉成字元陣列的長度要加上 1 (因為字元陣列需要一個空間儲存結束字元),最後清空暫存字串。

到這邊即可以燒錄至 NodeMCU裡,並且執行之前寫的 C# Broker ,正確的執行畫面應該會長這樣!
https://ithelp.ithome.com.tw/upload/images/20201011/20129084Vv4DtKiO8p.png

好啦今天就到此結束,結束前附上今天完整的程式碼:

#include<PubSubClient.h>
#include<ESP8266WiFi.h>

#define Sensor_Pin A0

const char* ssid = "SSID";
const char* password = "password";

const char* mqttServer = "Mqtt Server IP";
const char* mqttUserName = "User01"; 
const char* mqttPwd = "Mqtt Server Password";
const char* clientID = "User01";
const char* topic = "Data";

unsigned long prevMillis = 0;   // 暫存經過時間(毫秒) 
const long interval = 20000;    // 上傳資料的間隔時間,20秒。 
String msgStr = "";             // 暫存MQTT訊息字串

int Potted_num =0;

WiFiClient NodeMcuClient;
PubSubClient client(NodeMcuClient);

void setup(){
  Serial.begin(115200);
  pinMode(Sensor_Pin,INPUT);
  
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.println(".");
  }
  Serial.println("WiFi connected");
  client.setServer(mqttServer, 1883);
}

void reconnect() {
  while (!client.connected()) {
    if (client.connect(clientID, mqttUserName, mqttPwd)) {
      Serial.println("MQTT connected");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      delay(5000);  // 等5秒之後再重試
    }
  }
}

void loop(){
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
  if (millis() - prevMillis > interval) {
    prevMillis = millis();
    Potted_num =analogRead(Sensor_Pin);
    
    msgStr=String(Potted_num);
    byte arrSize = msgStr.length() + 1;
    char msg[arrSize];
    msgStr.toCharArray(msg, arrSize); // 把String字串轉換成字元陣列格式
    client.publish(topic, msg);       // 發布MQTT主題與訊息
    msgStr = "";
  }
}

參考來源:
MQTT教學(九):使用ESP8266上傳資料到ThingSpeak MQTT伺服器

今天就到這裡啦~明天又要繼續上班了呢!明天的部分我們來玩玩 不一樣 的吧!

今日的曲子:<<瑤族舞曲>>茅沅、劉鐵山/曲 彭修文/改編

Yes


上一篇
op.25 《應用層》-Flutter 資料視覺化呈現
下一篇
op.27 《全領域》-全域開發實戰 - 居家植物盆栽 Mvt II (C# Broker & Mysql)
系列文
來與IoT譜寫一首戀愛樂章吧30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言