简介面向物联网与嵌入式开发者的智慧农业温室暖棚控制与监测系统源码基于STM32主控芯片实现适合在校学生、工程师用于课程设计、毕业设计或项目初期验证。工程由传感器采集、设备控制、云平台通信和微信小程序远程监测构成使用DHT11温湿度传感器、BH1750光照传感器采集环境数据通过ESP8266接入OneNET平台配合OLED显示与继电器控制实现暖棚环境的自动化调节。资源包共四十五个文件大小约六百六十六KB主体为十五个C语言源文件另有微信小程序前后端描述文件、电路原理图PDF、Gerber PCB制板文件及README说明目录划分为主控程序、小程序、硬件设计三部分便于按模块查阅与二次开发。所有核心代码均经过运行测试功能稳定作者提供远程讲解支持下载后如遇编译或调试问题可私信咨询。这套源码已有166人学习下载可直接作为毕设演示或在此基础上增加更多农业场景功能。1. 基于物联网 STM32 的温室暖棚系统一整套能打板、能上云、能远程控制的毕设级源码做温室暖棚最怕的从来不是热而是半夜温度骤降、通风不及时、光照不足导致作物徒长。这套基于物联网 STM32 的智慧农业源码解决的正是「感知—联网—控制—远程操作」全链路问题底层用 STM32 读取 DHT11 温湿度和 BH1750 光照强度按阈值逻辑驱动继电器控制加热、加湿、补光和通风设备联网走 ESP8266 MQTT 协议上报云平台手机端用微信小程序实时查看数据、远程下发控制指令。它不是「点个灯」级别的单片机 demo而是从 PCB 打板文件、电路原理图、固件源码到小程序端的完整毕设级工程。适合物联网、自动化、电子信息的毕设、课设也适合想从裸机开发跨到物联网全栈的从业者拿来当学习底板。整套源码自带工程目录和硬件资料边看边跑比对着开发板手册东拼西凑要高效得多。2. 固件主控STM32 外设驱动与工程结构拆解拿到压缩包先别急着编译先看它背后的代码组织方式。这套工程把「硬件驱动、协议封装、业务逻辑」分得很清楚后面改传感器、换平台、加功能都是围绕这个结构展开的。2.1 从 dht11.c / bh1750.c / oled.c 看外设选型逻辑先说传感器为什么选这几种。DHT11 是典型的单总线数字温湿度传感器精度是 ±2℃ 和 ±5%RH放在温室暖棚这个场景完全够用——你要的不是实验室级测量而是「温度低于 15℃ 就开加热、湿度低于 40% 就开加湿」这种趋势判断。价格便宜、接口简单、代码成熟是毕设和课设最常见的选择。BH1750 是 I2C 接口的数字光照传感器内部自带 16 位 ADC直接把光照强度换算成 lux 输出不需要自己搭模拟放大电路接两根线就能读。至于 OLED它在这套系统里的角色不只是显示数据更是调试工具——在没有串口线或者现场接电调试的时候看一眼屏幕就知道当前温湿度、联网状态和继电器动作比猜代码强得多。选型上有个容易被忽略的点DHT11 的响应速度和精度都不高但它的时序是公开且固定的BH1750 的 I2C 地址取决于 ADDR 引脚电平默认是 0x23硬件上如果拉高就变成 0x5C。这些细节在阅读源码时都会体现出来后面接线和移植时尤其要注意。2.2 固件工程结构驱动层、协议层与应用层把压缩包里的 C 文件按职责分类结构非常清晰表格如下层级对应源文件职责基础服务层delay.c、sys.c、usart.c延时、系统时钟、串口收发与中断处理硬件驱动层dht11.c、bh1750.c、oled.c、key.c具体外设的初始化、读取与显示协议/通信层esp8266.c、MqttKit.c、onenet.cAT 指令封装、MQTT 报文构造、平台数据点对接应用层main.c、exti.c、timer.c、led.c、beep.c业务流程、外部中断、定时任务、指示与告警这种分层最大的好处是解耦。比如你想把 DHT11 换成 SHT30只需要改驱动层上层控制逻辑完全不用动想把上报目标从 OneNET 换成其他物联网平台只需要重写 onenet.c 里的数据点组装函数业务层的阈值判断、继电器控制不受影响。实际做项目时我一般会在这种结构上再加一个config.h把所有引脚定义、阈值参数、云端鉴权信息集中放改配置不动逻辑代码后期调参方便很多。2.3 DHT11 单总线时序读取与校验DHT11 的数据线是单总线主机先拉低 20ms 左右发起读取然后释放总线DHT11 会拉低响应信号之后连续输出 40 bit 数据8 bit 湿度整数、8 bit 湿度小数、8 bit 温度整数、8 bit 温度小数、8 bit 校验和。典型驱动代码如下uint8_t dht11_read_data(uint8_t *hum, uint8_t *temp) { uint8_t data[5] {0}; uint8_t i, j, tmp; // 主机拉低总线 18-30ms 触发读取 DHT_GPIO_MODE_OUT(); DHT_GPIO_RESET(); delay_ms(20); DHT_GPIO_SET(); delay_us(30); DHT_GPIO_MODE_IN(); // 等待 DHT11 响应先拉低 80us再拉高 80us if (!DHT_GPIO_READ()) { while (!DHT_GPIO_READ()); // 等待拉高 while (DHT_GPIO_READ()); // 等待响应结束 for (i 0; i 5; i) { for (j 0; j 8; j) { while (!DHT_GPIO_READ()); // 等待 50us 低电平结束 delay_us(40); // 40us 后采样 tmp 0; if (DHT_GPIO_READ()) tmp 1; while (DHT_GPIO_READ()); data[i] (data[i] 1) | tmp; } } } // 校验前 4 字节之和等于第 5 字节 if ((uint8_t)(data[0] data[1] data[2] data[3]) data[4]) { *hum data[0]; *temp data[2]; return 0; } return 1; }逻辑上最关键的坑在采样点每一位数据的时间结构是 50us 低电平 26~28us逻辑 0或 70us逻辑 1的高电平。上面这段代码的做法是等低电平结束后延时 40us 再读电平这个 40us 就是区分 0 和 1 的分水岭。参数上需要注意两点起始低电平时间要在 18~30ms太短 DHT11 不响应太长会拉长整个读取周期采样间隔建议大于 1s否则传感器自身来不及刷新读出来的永远是旧值。2.4 BH1750 光照读取与测量模式BH1750 走 I2C写入测量指令后需要等待内部转换完成再读数据。常见的连续高分辨率模式指令是 0x10转换时间约 120ms读出来的两个字节拼成 16 位数据再除以 1.2 就是实际 lux 值。核心代码如下uint16_t bh1750_read_lux(void) { uint8_t buf[2]; uint16_t lux; // 发送 0x10连续高分辨率模式 i2c_start(); i2c_write(BH1750_ADDR 1); // 地址 0x23写方向 i2c_write(0x10); i2c_stop(); delay_ms(180); // 等待转换完成高分辨率模式约 120ms // 读取 2 字节大端拼接 i2c_start(); i2c_write((BH1750_ADDR 1) | 0x01); // 读方向 buf[0] i2c_read_ack(); buf[1] i2c_read_nack(); i2c_stop(); lux (buf[0] 8) | buf[1]; return lux / 1.2; }这里 180ms 的延时是保守值实测高分辨率模式下 120ms 就能读到稳定数据但如果测量模式改成一字节能更快代价是分辨率减半。参数说明BH1750 的地址由 ADDR 引脚决定接地是 0x23接高是 0x5C如果你的板子和源码里不一致改宏定义就行不需要动电路。读回来的原始值如果接近 65535说明光照超出量程需要换低分辨率模式或者增加遮光处理。3. 联网链路ESP8266 与 MQTT 上云从 AT 指令到数据上报温室监控的价值在于「不在现场也能知道棚内状态」。这一章把 STM32 如何通过 ESP8266 接入云平台的完整链路拆开讲清楚。整条链路是STM32 串口发 AT 指令 → ESP8266 连 WiFi → 建立 MQTT 连接 → 订阅/发布主题 → 云平台转发给微信小程序。3.1 STM32 与 ESP8266 的串口分工ESP8266 模块在整套系统里承担 WiFi 和 MQTT 协议栈的运算STM32 只需要通过 USART 和它对话。固件里 esp8266.c 负责拼装和发送 AT 指令usart.c 负责接收模块返回的响应MqttKit.c 负责把 MQTT 报文按协议格式填字节流。这三个文件的配合关系就像「命令发出、响应接收、协议编解码」三层流水线。串口参数上ESP8266 模块出厂波特率常见 115200 或 9600固件里大概率会有一段ATUART_DEF的初始化把模块配置到目标波特率。调试时要注意如果模块当前波特率和源码配置不一致你会发现发指令没任何回应这是最常见的翻车点之一后面避坑章会专门讲。3.2 AT 指令配置流程入网到建立 MQTT 连接模块上电后需要依次完成设置 Station 模式、连接 WiFi、配置 MQTT 用户名密码、建立连接。这套序列在 esp8266.c 里体现得比较完整核心流程如下// 1. 设置 Station 模式 esp8266_send_cmd(ATCWMODE1, OK, 3000); // 2. 连接 WiFiSSID 和密码从配置项读取 esp8266_send_cmd(ATCWJAP\MyGreenHouse\,\12345678\, WIFI GOT IP, 15000); // 3. 配置 MQTT 参数clientid, username, password esp8266_send_cmd(ATMQTTUSERCFG0,1,\greenhouse01\,\productID\,\auth_token\,0,0,\\, OK, 2000); // 4. 建立 MQTT 连接服务器地址和端口 esp8266_send_cmd(ATMQTTCONN0,\iot-platform.example.com\,1883,1, OK, 5000);参数说明第 2 步的ATCWJAP要等「WIFI GOT IP」而不是「OK」因为入网成功的标志是拿到 IP第 3 步的MQTTUSERCFG中第一个参数 0 是连接 ID1 表示使能 SSL后面依次是 clientid、用户名、密码第 4 步MQTTCONN里的参数是连接 ID、服务器域名、端口、是否使能重连。每个指令都要设置超时时间WiFi 连接给 15sMQTT 建连给 5s响应超时做重试。实际经验是ESP8266 模块偶尔会吞指令或者返回乱码所以每条指令发出后要清空接收缓冲区再等待目标关键字否则容易出现「上次的 OK 和这次的 OK 混在一起」的错乱。3.3 MQTT 报文结构与 MqttKit.c 的封装逻辑MQTT 报文分为固定头、可变头和载荷三部分。固定头第一个字节的高 4 位表示报文类型1 是 CONNECT3 是 PUBLISH8 是 SUBSCRIBE低 4 位是标志位剩余长度用变长编码表示。MqttKit.c 做的事情就是把协议字段填进字节流。下面是构造一个 CONNECT 报文的典型代码void mqtt_connect_packet(uint8_t *buf, const char *client_id) { uint8_t idx 0; uint8_t payload_len; const char *proto MQTT; const char *cid client_id; // 固定头类型 1 (CONNECT)剩余长度暂填 0 buf[idx] 0x10; buf[idx] 0x00; // 稍后回填 // 可变头协议名 MQTT级别 4 buf[idx] 0x00; buf[idx] 0x04; memcpy(buf[idx], proto, 4); idx 4; buf[idx] 0x04; // protocol level buf[idx] 0xC2; // 连接标志用户名密码清理会话 buf[idx] 0x00; buf[idx] 0x3C; // keepalive 60s // 载荷client_id 长度前缀 字符串 uint16_t cid_len strlen(cid); buf[idx] cid_len 8; buf[idx] cid_len 0xFF; memcpy(buf[idx], cid, cid_len); idx cid_len; // 回填剩余长度 buf[1] idx - 2; }逻辑说明里最重要的一点是连接标志0xC2二进制是 1100 0010表示启用密码和用户名、并请求清理会话。如果你的设备端证书鉴权方式不同这个字节要相应变化。keepalive 设置为 60s意味着 60s 内没有业务报文设备必须发心跳包 PINGREQ 保持连接。MqttKit.c 的本质就是把这些字节编排逻辑从 main 函数的业务代码里剥离出来避免满屏都是十六进制魔法数。3.4 云平台接入与数据点设计设备端把数据上传到云平台不是直接把温湿度明文发上去而是按照平台定义的数据流格式组装 JSON。常见做法是上报到数据流主题核心负载结构如下{ datastreams: [ { id: temp, datapoints: [ { value: 25.3 } ] }, { id: humi, datapoints: [ { value: 62.0 } ] }, { id: lux, datapoints: [ { value: 1200 } ] } ] }代码里的 onenet.c 实际上就是把你读到的传感器值填进这个 JSON 模板然后通过 MQTT 的 PUBLISH 报文发出去。数据点的 id 要在云平台「产品—设备—数据流」里预先创建好名称必须和代码里一致否则平台会直接丢弃这条数据。另外设备端和平台的鉴权信息一般分为用户名和密码密码往往是设备的 access token烧录前要确认固定设备和 token 的对应关系很多人换了设备但没换 token导致连不上网。3.5 心跳、掉线检测与自动重连温室现场的网络不稳定是常态所以链路设计里必须有掉线重连机制。实际工程里常见的做法是主循环里定时发布一条心跳数据当发现 MQTT 连接断开串口被动的收到模块上报的MQTTDISCONNECTED延迟 5s 后重新执行 MQTTUSERCFG 和 MQTTCONN并增加失败后的重试退避逻辑连续失败 5 次后把模块复位重新初始化。重连参数建议心跳间隔 30~60s 比较合适太短消耗流量和模块资源太长容易被服务器踢掉。退避策略上第一次失败等 5s第二次 10s最多等 60s 封顶。这套在 onenet.c 的底层驱动里如果没实现你可以自己在 main 循环里用状态机补上。4. 控制逻辑与调参阈值判断、状态切换与手动/自动模式硬件驱动读到了数据联网链路把数据送上了云平台接下来是整套系统真正干活的部分怎么根据温湿度光照决定打开还是关掉加热、加湿、补光和通风设备。4.1 main.c 顶层循环与业务状态流主循环的设计思路是「采集—上报—控制—响应按键—等待」的流水线。由于 DHT11 需要 1s 以上的采样间隔整个循环一般用 1s 作为节拍光照传感器每隔几个节拍读一次。伪代码如下while (1) { // 1. 读取传感器 if (dht11_read_data(humi, temp) 0) { lux bh1750_read_lux(); } // 2. 上报云平台仅在成功读取后上报 if (link_ok need_report) { mqtt_publish_status(temp, humi, lux); } // 3. 自动模式下执行阈值控制 if (mode MODE_AUTO) { climate_control(temp, humi, lux); } // 4. 按键和外部事件响应 key_scan(); exti_process(); delay_ms(500); }这个结构最大的好处是每一轮都有明确的入口和出口出了故障可以直接定位到对应环节。注意need_report这个标志位我一般不会每个循环都上报而是每 5s 一次一方面省流量另一方面云平台的图表曲线也不会被刷得太密。上报频率每 5~10s 一次在农业场景完全够用如果要做实时性更强的控制把控制判断放在上报前面即可。4.2 阈值控制与滞回区间防止继电器频繁吸合直接拿传感器值和一个固定阈值比较是新手最常见的控制写法但实际运行几天后就会发现问题温度在 20℃ 附近抖动继电器跟着反复吸合释放加热器寿命堪忧。正确的做法是加入滞回区间也就是「低于下限启动高于上限停止中间维持现状」。核心控制逻辑如下void climate_control(uint8_t temp, uint8_t humi, uint16_t lux) { // 温度控制低于 20℃ 开加热高于 24℃ 关加热 if (temp TEMP_LOW) heater_ctrl(ON); else if (temp TEMP_HIGH) heater_ctrl(OFF); // 湿度控制低于 40% 开加湿高于 60% 关加湿 if (humi HUMI_LOW) humidifier_ctrl(ON); else if (humi HUMI_HIGH) humidifier_ctrl(OFF); // 光照控制低于 2000lux 且温度允许时补光 if (lux LUX_LOW temp TEMP_LOW) light_ctrl(ON); else if (lux LUX_HIGH) light_ctrl(OFF); }参数说明TEMP_LOW、TEMP_HIGH之间就是滞回区间温差 4℃ 是一个比较保守的数值。如果设置成同样的 20/20控温设备会在 1℃ 范围内的抖动中反复通断这个现象在继电器咔嗒声里听得特别明显。湿度控制同理40%~60% 的区间既保证作物生长环境又给执行器足够的休息时间。光照控制额外加了温度判断是为了避免补光灯在高温天同时启动进一步加剧棚内升温。4.3 手动/自动切换与按键中断处理现场调试时不会永远都希望系统自动控制。按键模块承担手动/自动模式切换和执行器的手动启停。key.c 负责扫描按键状态并去抖exti.c 负责响应外部中断比如紧急停止按钮beep.c 在模式切换和执行器动作时发出提示音。典型的状态机逻辑是短按模式键在「自动」和「手动」之间切换在手动模式下短按某个执行器对应的按键就翻转该执行器开关状态。代码层处理如下void key_scan(void) { if (KEY_MODE_PRESSED) { delay_ms(20); // 消抖 if (KEY_MODE_PRESSED) { g_mode !g_mode; beep_on(50); } } }外部中断配合蜂鸣器还有一个用途当温度超过安全上限比如 35℃时强制打开通风并报警这类保护逻辑不应该依赖云平台下发指令必须在设备端独立完成相当于给系统上了最后一道保险。4.4 控制参数调整建议参数建议值影响TEMP_LOW18~20℃过低的启动阈值会导致加热过晚作物受冻TEMP_HIGH24~26℃过高的关闭阈值会让加热器持续工作浪费能源HUMI_LOW40~45%大棚内湿度过低叶片容易失水HUMI_HIGH60~65%高于 65% 容易诱发病害LUX_LOW1500~2500 lux补光启动点阴天和夜间适用滞回区间4~6℃数值越小控制越精准但执行器磨损越大参数调整的核心原则是「先保安全再谈节能」。整套系统的控制闭环完全可以在设备端独立运转即使云平台断开、小程序连不上棚内的温控和通风依然正常这点务必在答辩或验收时讲清楚它是整套设计的可靠性格栅。5. 避坑指南ESP8266 静默失败、DHT11 时序翻车与供电玄学整个工程跑通不难但「能编译通过」和「在现场稳定运行一周」是两码事。以下这几条坑基本覆盖了接手这类项目时最容易浪费时间的排查点。5.1 DHT11 读出来的数据全是 0xFF 或长时间无响应现象串口打印温湿度显示 255、85 之类的固定值或者 OLED 上数据完全不刷新。原因大概率是单总线上拉电阻缺失导致信号边沿变缓DHT11 的低电平响应无法被 STM32 正确识别另一可能是中断被高频触发导致 40us 采样时序被打乱。解决数据线上加 4.7kΩ~10kΩ 上拉电阻到 3.3V这是最有效的修正。代码层面在读取 DHT11 的完整时序函数前后关闭中断__disable_irq()和__enable_irq()防止定时器或串口中断打断采样窗口。注意 DHT11 采样间隔至少 1s如果你在 while 循环里连续读取第二个周期失败是因为传感器还没准备好不是硬件坏了。5.2 ESP8266 连接云平台后几分钟就掉线再也连不上现象MQTT 连接刚建立时上报正常约 3~5 分钟后串口出现MQTTDISCONNECTED重启模块又能恢复但几分钟后再次掉线。原因设备没有在 keepalive 时间内发送心跳包被服务器判定为死连接。部分 ESP8266 AT 固件版本在配置了ATMQTTCONN后不会自动发心跳必须由 MCU 周期性地发送 PINGREQ即ATMQTTPING。解决在 main 循环里用计数器统计距离上次上报的时间如果超过 30s 没有业务数据发送就主动执行一次ATMQTTPING并检查返回值是否为 OK。同时确认 keepalive 参数和实际心跳间隔匹配例如 keepalive 设为 60s心跳周期应设为 30s 左右留一半余量给网络延迟。5.3 ESP8266 串口收到的响应是乱码或完全沉默现象向模块发送AT没收到预期OK收到的是#或乱码字符。原因模块当前波特率和源码配置不一致。常见出厂固件波特率是 115200但如果模块经过了二次配置或被刷过非标准固件实际的波特率可能是 9600 或 74880固件启动日志专用。解决先用 USB-TTL 直连模块逐个测试 115200、9600、74880 等常见波特率确认能收到OK后发送ATUART_DEF115200,8,1,0,0把模块默认波特率固定为和源码一致的速率然后断电重启再接到 STM32 上。这个操作要记住改完默认波特率后旧波特率就失效了相当于给模块换了一套「语言」。5.4 继电器频繁通断加热器发出高频咔嗒声现象控制器处于自动模式温度在阈值附近波动时继电器 1~2 秒切换一次。原因代码里只做了单点阈值比较没有滞回处理。20℃ 开加热、20℃ 关加热传感器数值在 19.8~20.2℃ 抖动执行器必然跟着抖动。解决把控制逻辑改成temp 20℃启动、temp 24℃停止的滞回区间中间状态维持上一次的输出。继电器驱动电路上还可以并接续流二极管防止感性负载产生反向电动势干扰 MCU 复位。这个改动同时也是答辩时的加分点因为体现了「工程思维」而不只是「抄代码」。5.5 OLED 花屏、闪烁或亮度过低现象屏幕显示混乱字符或者内容能看清但整体发虚偶发白屏。原因I2C 地址不匹配会直接白屏电源纹波过大导致屏幕初始化失败是更隐蔽的原因尤其是继电器吸合的瞬间大电流导致 3.3V 供电跌落超过 100mV。解决先确认屏幕实际地址是 0x3C 还是 0x3D这和屏幕模组上的拉高/拉低电阻有关。电源侧在 OLED 的 VCC 和 GND 之间就近加 100nF 去耦电容不要把继电器供电和模块供电接在同一组杜邦线上用独立电源给执行器供电这是现场稳定运行的基本功。5.6 云平台不更新数据但设备端串口显示上报成功现象OLED 显示数据在变串口日志显示 publish 返回 OK但手机端小程序页面数值长期不变。原因上报的数据流 id 和云平台产品里创建的 id 不一致或 JSON 格式里 datapoints 的 value 类型不匹配字符串 vs 数字。解决在 onenet.c 的数据组装函数里把设备端发送的完整 JSON 字符串通过串口原样打印出来然后在平台调试页手动模拟同一条数据推送。对比后通常是 id 拼写错误或漏了引号。有一类情况是云平台把重复 value 值合并了比如连续两次上报 25.3平台只记录变化值界面不更新是正常的。6. 进阶玩法从阈值开关到策略调度把毕设做成可落地的系统前面几章已经把全套源码跑通了但从「会跑」到「跑得好」之间还有一段路。这章分享几个提升方向都是我在类似项目上验证过成本不高、收益明显的改法。把固定阈值升级成分时段策略。温室暖棚温控和光照策略白天和夜间应该完全不同。白天有太阳辐射棚内温度可能短时间冲到 35℃这时加热器必须锁死夜间失去辐射热源20℃ 启动加热就是合理的。做法很简单在控制函数入口加一个依据系统时间的判断分支白天使用一套参数夜间使用另一套参数。参数表如下时段温度下限温度上限补光启动 lux06:00~18:0022℃30℃150018:00~06:0016℃24℃2000增加趋势判断提前动作。单纯靠阈值判断是「事后补偿」靠趋势判断可以做「事前干预」。连续采集 5 分钟温度如果每分钟下降超过 0.5℃说明棚体保温性能在变差可以提前启动加热而不必等到跌破下限。参考逻辑float temp_slope(float *buf, uint8_t len) { // 用最后 5 个采样点做线性斜率估计 float sum_x 0, sum_y 0, sum_xy 0, sum_xx 0; for (uint8_t i 0; i len; i) { sum_x i; sum_y buf[i]; sum_xy i * buf[i]; sum_xx i * i; } float n len; return (n * sum_xy - sum_x * sum_y) / (n * sum_xx - sum_x * sum_x); } // 主循环内趋势为负且斜率绝对值超过阈值时提前启动加热 if (temp_slope(history, 5) -0.5f) { heater_ctrl(ON); }这段代码里 5 个点的窗口是经验值窗口太短斜率被传感器噪声淹没太长则提前量落后于实际变化0.5℃/min 的触发阈值也需要按自家棚体的保温能力标定。迭代方式很简单先记录三天数据把斜率打印出来找出「温度自然下降但还不需要加热」的区间把阈值设在那之上。离线验收三板斧。验收这套系统时不需要真的去大棚里冻一晚上。用一块湿毛巾包住 DHT11 的探头湿度数值应该快速上升到 90% 以上用电吹风在探头附近吹热风温度上升后加热器应关闭、通风设备应启动用手机热点给 ESP8266 配网测试联网链路比蹭现场 WiFi 靠谱得多。整个过程焊个转接板放在桌面上就能完成花不了半小时。把调试信息变成你的后悔药。整个系统跑起来后我强烈建议保底做一件事串口日志分级。普通信息打印温度、湿度、光照和继电器状态警告信息打印 MQTT 重连、DHT11 读取失败的事件错误信息只打印无法恢复的故障。同时在 OLED 上留一页「系统状态页」标准展示当前模式自动/手动、WiFi 是否连接、云平台是否在线、剩余可用内存。这样现场跑飞了你不用猜是传感器挂了还是网络断了看屏幕就好。这套源码的价值不在代码本身而在它把传感器、主控、通信、平台、小程序这几层之间的接口关系完整地串起来了。我接手类似项目时第一件事就是先看它的消息流连接关系再决定改哪里。从那以后我每次给这类系统重新布线或换传感器都会强制自己先画一遍「数据从传感器到手机屏幕走了哪几跳」把每一跳的串口日志和状态输出提前埋好。做到这一步后面所有调试都是在填参数而不是在猜逻辑。希望帮到你。本文还有配套的精品资源点击获取