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服务器
今天就到这里啦~明天又要继续上班了呢!明天的部分我们来玩玩 不一样 的吧!
<<: 利用 Grafana Operator 部署 Grafana 到 OpenShift,并建立客制化的 Dashboard。
>>: JavaScript 之旅 (26):String.prototype.replaceAll()
第13天,表格不就是栏加列吗,还能变出什麽花样? 没错,tableau public 就是可以~呈现...
今日我们要来使用Kibana内的警报功能,看如何设定Alert让我们能收到异常的通知。 设定Aler...
前情提要 在D特的帮助下,试着穿越时空,但时间碎片凑齐了却还是出不去,现在该怎办? 我:...所以我...
上篇component元件有说到每个元件范围都应该是独立的,更不应该发生子元件直接改变根元件的情况,...
前言 上一篇结束後已经将型别部分,大致上了解差不多了,铁人文章也来到一半了。今天开始下半部的铁人,会...