op.26 打造屬於妳的時空廊道
為你我打造一個專屬妳的自由往返通道
讓妳可以任意地穿越
不再受拘束
今天開始來將前幾天用到的東西進行實作,預計架構會長這樣。
大略地介紹,會經過 NodeMCU 來定時去監測土壤濕度的變化,經由 MQTT 的方式進行回傳,那經由 Broker 處理訊息後,將濕度儲存至資料庫;而手機也可以實際地收到濕度的數值。
今天是一個硬體的實作部分。
需要準備的材料:
NodeMCU
土壤濕度感測器
從土壤濕度的感測器的參考資料之中,我們可以看到這個模組具有兩種輸出模式,一種為 類比 輸出;另一種是經過類似史密特觸發的電路轉成 數位 的輸出。
既然要監測 濕度 ,那我們就選擇這模組的類比輸出。
從這張圖片我們可以確定可以使用 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);
}
當感測器插入濕潤的土裡時,數據呈現是這樣。
當感測器拔出濕潤的土裡時,數據呈現是這樣。
所以可以知道濕度上升、類比數值下降,接下來就可以準備將這些的資料透過 MQTT 發送囉。
首先還是需要新增新的程式庫,這裡需要新增 PubSubClient ,一樣地可以在 程式庫管理員搜尋到
然後開啟之前的連上網路那篇文章裡的程式碼
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 ,正確的執行畫面應該會長這樣!
好啦今天就到此結束,結束前附上今天完整的程式碼:
#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伺服器
今天就到這裡啦~明天又要繼續上班了呢!明天的部分我們來玩玩 不一樣 的吧!