IoT-For-Beginners 实战:用 Wio Terminal 通过 MQTT 发布 DHT11 温度遥测以支撑 GDD 计算
【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners
导读
本篇技术指南聚焦于微软开源课程 IoT-For-Beginners 中「预测植物生长(Predict plant growth)」一课的核心设备端环节:让 Wio Terminal 将 DHT11 温湿度传感器读取到的环境温度,以 JSON 遥测消息的形式通过 MQTT 协议发布到公共 Mosquitto 代理,供服务端代码订阅、落盘,最终用于计算积温(Growing Degree Days,GDD)。读完本文,你将掌握:在 PlatformIO 工程中配置 WiFi/MQTT/JSON/DHT 依赖、编写断线重连的 MQTT 客户端、构造并发布温度遥测、按 10 分钟采样节奏控制发布频率,以及如何用串口监视器验证端到端的数据链路。
为什么要把温度发布到 MQTT?
在农业 IoT 场景中,温度是预测植物生长与成熟的核心指标。植物存在基准温度(base temperature)、最适温度与最高温度,当环境温度高于基准温度时,植物的生长速率由积温决定。本课程采用简化公式计算每日 GDD:
- GDD:当日积累的生长度日数
- Tmax:当日最高温度(°C)
- Tmin:当日最低温度(°C)
- Tbase:植物基准温度(°C)
公式为 GDD = (Tmax + Tmin) / 2 − Tbase,具体推导与玉米、草莓等示例见 2-farm/lessons/1-predict-plant-growth/README.md。
要获得每日的 Tmax 与 Tmin,就必须让温度传感器数据持续流向上游。这正是本文设备端任务的意义:Wio Terminal 周期性地把 DHT11 读到的温度发布到 MQTT 主题,由服务端订阅并打上时间戳存储。
一个重要的设计前提:微控制器本身不具备从互联网获取时间或维护实时时钟的能力(除非额外编程并具备相应硬件)。为简化课程,设备端不随遥测发送时间戳,而是由服务端在收到消息时自行补上——仓库中的服务端实现 code-server/temperature-sensor-server/app.py 正是这样做的:
def handle_telemetry(client, userdata, message): payload = json.loads(message.payload.decode()) print("Message received:", payload) with open(temperature_file_name, mode='a') as temperature_file: temperature_writer = csv.DictWriter(temperature_file, fieldnames=fieldnames) temperature_writer.writerow({'date': datetime.now().astimezone().replace(microsecond=0).isoformat(), 'temperature': payload['temperature']})服务端订阅id + '/telemetry'主题,用datetime.now()为每条温度数据补记时间并写入 CSV,后续可交给 gdd.ipynb 完成 GDD 计算。因此设备端只负责「发数据」,主题名与消息字段的约定必须与服务端保持一致。
前置条件
开始前需要准备:
- 一台 Wio Terminal(搭载 Atmel SAMD51 主控),并已完成第 1 课中 PlatformIO 开发环境与工程的搭建(可参考 1-getting-started/lessons/1-introduction-to-iot/wio-terminal.md);
- 一个 Grove DHT11 温湿度传感器,通过 Grove 线接入 Wio Terminal 右侧(面向屏幕时远离电源键)的 Grove 插座,即数字端口 D0,接线细节见同课前的温度测量文档 2-farm/lessons/1-predict-plant-growth/wio-terminal-temp.md;
- 可用的 WiFi 环境;
- 完成第 4 课「连接互联网」中的 MQTT 连接与遥测发送实验,本文大量复用其中的步骤(详见 wio-terminal-mqtt.md 与 wio-terminal-telemetry.md)。
任务总览:把温度数据发布出去
本任务的核心动作是打开上一小节建立的temperature-sensor工程,依次完成四件事:
- 在
platformio.ini中补充 Seeed WiFi 与 MQTT 等库依赖; - 新增
config.h配置头文件并编写 WiFi 连接代码; - 编写连接 MQTT 代理的代码(含断线重连);
- 修改
loop函数,改为发布温度遥测而非光敏值。
下面结合仓库中可直接运行的完整参考实现逐项展开。完整代码位于 2-farm/lessons/1-predict-plant-growth/code-publish-temperature/wio-terminal/temperature-sensor(src/config.h、src/main.cpp、platformio.ini三个文件)。
第一步:补齐 platformio.ini 的库依赖
在工程根目录的platformio.ini中追加/合并lib_deps。参考实现完整列出了本工程所需全部依赖(@后为已验证的版本号):
[env:seeed_wio_terminal] platform = atmelsam board = seeed_wio_terminal framework = arduino lib_deps = seeed-studio/Grove Temperature And Humidity Sensor @ 1.0.1 knolleary/PubSubClient @ 2.8 bblanchon/ArduinoJson @ 6.17.3 seeed-studio/Seeed Arduino rpcWiFi @ 1.0.5 seeed-studio/Seeed Arduino FS @ 2.1.1 seeed-studio/Seeed Arduino SFUD @ 2.0.2 seeed-studio/Seeed Arduino rpcUnified @ 2.1.3 seeed-studio/Seeed_Arduino_mbedtls @ 3.0.1各依赖职责如下:
seeed-studio/Grove Temperature And Humidity Sensor @ 1.0.1:驱动 DHT11 传感器(上一步测量温度时已引入);knolleary/PubSubClient @ 2.8:Arduino 平台的 MQTT 客户端库;bblanchon/ArduinoJson @ 6.17.3:JSON 文档读写与序列化库;- 其余
seeed-studio系列库:Wio Terminal 的 rpcWiFi(WiFi 芯片驱动)、FS/SFUD/rpcUnified 与 mbedtls(TLS 支持)配套依赖。
这些库以源码形式随 PlatformIO 构建自动下载并编译进固件。注意版本号可以去掉以始终使用最新版,但课程代码仅以所列版本验证过,建议保持锁定。
第二步:config.h 配置头文件
在src目录下创建config.h,集中管理 WiFi 凭据与 MQTT 连接参数。参考实现如下:
#pragma once #include <string> using namespace std; // WiFi credentials const char *SSID = "<SSID>"; const char *PASSWORD = "<PASSWORD>"; // MQTT settings const string ID = "<ID>"; const string BROKER = "test.mosquitto.org"; const string CLIENT_NAME = ID + "temperature_sensor_client"; const string CLIENT_TELEMETRY_TOPIC = ID + "/telemetry";参数含义与取值建议:
| 参数 | 含义 | 说明 |
|---|---|---|
SSID/PASSWORD | WiFi 名称与密码 | 替换<SSID>、<PASSWORD>占位符 |
ID | 设备唯一标识 | 替换<ID>。本课程使用公共代理test.mosquitto.org,大量用户共享,唯一 ID 可避免客户端名与主题互相冲突;后续服务端代码也需要同一个 ID |
BROKER | MQTT 代理地址 | 本课程固定使用公共代理test.mosquitto.org,端口 1883(非加密端口) |
CLIENT_NAME | MQTT 客户端名 | 由ID拼接temperature_sensor_client,须与本工程匹配 |
CLIENT_TELEMETRY_TOPIC | 遥测发布主题 | 由ID拼接/telemetry,与服务端订阅主题一致 |
提示:可以使用 GUID 生成器产生一个足够随机的
ID。
注意CLIENT_NAME必须反映本工程身份,即保持:
const string CLIENT_NAME = ID + "temperature_sensor_client";第三步:main.cpp 中的 WiFi 连接与 MQTT 客户端
main.cpp需要引入相关头文件并实现三层连接逻辑。头部 include 与全局对象声明(参考实现):
#include <Arduino.h> #include <ArduinoJson.h> #include <DHT.h> #include <PubSubClient.h> #include <rpcWiFi.h> #include <SPI.h> #include "config.h" DHT dht(D0, DHT11);DHT dht(D0, DHT11)声明 DHT 传感器实例:D0 对应 Wio Terminal 右侧 Grove 插座,DHT11声明传感器型号(库还支持同系列其他型号)。
WiFi 连接:循环等待直至WiFi.status()变为WL_CONNECTED,每 500ms 重试一次:
void connectWiFi() { while (WiFi.status() != WL_CONNECTED) { Serial.println("Connecting to WiFi.."); WiFi.begin(SSID, PASSWORD); delay(500); } Serial.println("Connected!"); }MQTT 客户端:先用WiFiClient实例化PubSubClient,再实现断线重连与代理配置:
WiFiClient wioClient; PubSubClient client(wioClient); void reconnectMQTTClient() { while (!client.connected()) { Serial.print("Attempting MQTT connection..."); if (client.connect(CLIENT_NAME.c_str())) { Serial.println("connected"); } else { Serial.print("Retying in 5 seconds - failed, rc="); Serial.println(client.state()); delay(5000); } } } void createMQTTClient() { client.setServer(BROKER.c_str(), 1883); reconnectMQTTClient(); }其中client.setServer(BROKER.c_str(), 1883)指定代理地址与端口;reconnectMQTTClient()在未连接时用CLIENT_NAME建立连接,失败则打印client.state()返回码并 5 秒后重试。因为 MQTT 长连接容易因网络波动断开,loop每次迭代都要先调用reconnectMQTTClient()再调用client.loop()处理收发缓冲。
第四步:在 loop 中发布温度遥测
这是本次任务与第 4 课「夜灯」实验的唯一实质差异:不再读取光敏电阻值,而是读取 DHT11 的温度并放入名为temperature的 JSON 字段。参考实现中完整的setup与loop:
void setup() { Serial.begin(9600); while (!Serial) ; // Wait for Serial to be ready delay(1000); dht.begin(); connectWiFi(); createMQTTClient(); } void loop() { reconnectMQTTClient(); client.loop(); float temp_hum_val[2] = {0}; dht.readTempAndHumidity(temp_hum_val); DynamicJsonDocument doc(1024); doc["temperature"] = temp_hum_val[1]; string telemetry; serializeJson(doc, telemetry); Serial.print("Sending telemetry "); Serial.println(telemetry.c_str()); client.publish(CLIENT_TELEMETRY_TOPIC.c_str(), telemetry.c_str()); delay(10 * 60 * 1000); }要点拆解:
- 数组下标约定:
dht.readTempAndHumidity(temp_hum_val)将湿度写入temp_hum_val[0]、温度写入temp_hum_val[1](C++ 数组从 0 开始,见 wio-terminal-temp.md)。因此doc["temperature"] = temp_hum_val[1]取的是温度值; - JSON 构造:
DynamicJsonDocument doc(1024)分配 1024 字节动态缓冲区,serializeJson(doc, telemetry)将文档序列化为字符串; - 发布:
client.publish(CLIENT_TELEMETRY_TOPIC.c_str(), telemetry.c_str())把序列化结果发布到ID/telemetry主题。载荷形如{"temperature":25},与服务端payload['temperature']的解析字段完全对应; - 采样节流:
delay(10 * 60 * 1000)。delay以毫秒为单位,10 × 60 秒/分钟 × 1000 毫秒/秒 = 600,000 ms = 10 分钟。温度在短时间内不会显著变化,10 分钟一次的采样频率既能满足 GDD 计算需求(GDD 以日为粒度),又降低了设备功耗与网络负载。
与第 4 课遥测代码对比可见,读取、序列化、发布的调用链完全一致,区别仅在于「读什么传感器、字段叫什么、多久发一次」。
上传固件并用串口监视器验证
在 VS Code 中通过 PlatformIO 将代码上传到 Wio Terminal,然后打开串口监视器。预期输出如下:
--- Available filters and text transformations: colorize, debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time --- More details at http://bit.ly/pio-monitor-filters --- Miniterm on /dev/cu.usbmodem1201 9600,8,N,1 --- --- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H --- Connecting to WiFi.. Connected! Attempting MQTT connection...connected Sending telemetry {"temperature":25} Sending telemetry {"temperature":25}验证链路的关键点:
Connecting to WiFi..→Connected!:WiFi 连接成功;Attempting MQTT connection...connected:已连上test.mosquitto.org代理;Sending telemetry {"temperature":25}:温度遥测已构造并发布,此时若服务端脚本(订阅同一ID/telemetry主题)在运行,会打印Message received: {'temperature': 25}并写入temperature.csv。
若反复出现failed, rc=...并 5 秒重试,说明设备端无法连接代理,需检查 WiFi 凭据与CLIENT_NAME唯一性;若temperature字段缺失或数值异常,请回查 DHT11 接线与数组下标。
小结与下一步
至此,你的 Wio Terminal 已能每 10 分钟把环境温度以{"temperature":25}形式发布到 MQTT 主题ID/telemetry,完成了 GDD 数据管道的设备端闭环。完整可运行的参考工程在 code-publish-temperature/wio-terminal,服务端订阅代码在 code-server/temperature-sensor-server/app.py。
接下来可以:启动服务端脚本积累若干小时的温度采样,用 code-notebook/gdd.ipynb 计算每日 GDD;若使用树莓派或虚拟设备,可参考同课文档 single-board-computer-temp-publish.md 与 virtual-device-temp.md,其服务端接口完全一致,主题与消息格式约定相同。
免责声明:本文依据仓库中的课程文档与源码编写;课程原文由 Co-op Translator 提供 AI 翻译,涉及关键信息时请以仓库英文原文为准。
【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考