拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于W55MH32-ADK的室内外气象监测系统:传感器选型、数据链路与低功耗实践

基于W55MH32-ADK的室内外气象监测系统:传感器选型、数据链路与低功耗实践

去年冬天,小区物业那块LED屏上的温度计成功把我骗了一整个星期——屏幕显示7℃,外面草地上已经结霜,每天出门穿多厚全靠体感在赌。后来实在忍不了,我决定自己搭一套室内/室外气象监测系统:室内机放客厅,室外机挂阳台外墙,温度、湿度、气压、风速、雨量、光照六个数据全部采回来,既能本地显示,也能通过 MQTT 上报到云端自动画曲线。整套系统的主控选了一块 32 位开发板——W55MH32-ADK。

这篇文章把整个实现过程拆开讲清楚。既适合正在选主控板的人参考,也适合手头有类似开发板、想搭环境监测却不知道从哪下手的新手。我会把传感器接线、滤波标定、通信协议、低功耗设计以及调试中踩过的坑都记录下来,能直接复制的直接复制,能避开的坑尽量帮你避开。

1. 为什么选 W55MH32-ADK 做中枢:选型对比与角色分工

1.1 树莓派被淘汰:嵌入式方案才是气象站的常态

刚开始我也动过直接买成品智能气象站的念头,搜了一圈发现要么数据封闭不开放,要么传感器精度差,而且你只能被动看它家APP里的图。自己动手做,最关键的一步是选主控。

我第一批对比方案里就有树莓派 Zero 2W。它的优点是 Python 生态好、写图表方便,但缺点同样明显:Linux 启动慢、掉电容易损坏 SD 卡、整板功耗按瓦算,放室外长期通电心里没底。工业气象站没人用树莓派做传感器节点,原因不是性能不够,而是稳定性和功耗都撑不住。

ESP32 也考虑过。它便宜、社区资料海量、自带 Wi-Fi 和蓝牙,缺点是想要多串口同时挂传感器、再开 Web Server 和 MQTT 客户端时,可用外设和内存比较紧张。做一个单节点传感器它很合适,做"室内外多节点汇聚+网关"就有点憋屈。

1.2 W55MH32-ADK 的接口完整度恰好卡在需求上

W55MH32-ADK 是围绕 W55MH32 主控的一套评估/开发套件,板载调试接口和各种常用外设接口。我选择它的理由很朴素:这块板子的接口完整度恰好卡在我的需求上。

做气象站这种项目,主控要同时面对三类东西:传感器需要 I2C/SPI/ADC/定时器输入捕获,室内外通信需要至少两个串口(一个跑 RS485,一个跑无线模块),对外上网还需要网络或串口转以太网/无线模组。很多板子能干活,但要么串口不够用,要么得自己飞线改板。W55MH32-ADK 把这些外设接口集中在一块评估板上,面包板和杜邦线就能完成初期原型验证,不用一上来就画 PCB,这一点对快速试错帮助很大。

另外,它作为 32 位 MCU,处理速度和处理气象数据这种低频、低数据量的任务绰绰有余。很多人一上来就想着上 RTOS、上大内存,实际上气象站每秒产生几十个字节,整个固件跑裸机加几个中断就能稳稳当当。

1.3 它在系统里的三层角色:采集、汇聚、网关

在整套系统里,W55MH32-ADK 承担了三个角色。

第一是采集层。室内机的温湿度、气压、光照直接挂在板子的 I2C 总线上,室外机的风速和雨量通过脉冲计数器接入定时器输入捕获引脚,风向传感器用 ADC 读取。所有传感器都由这颗主控周期轮询,不需要额外的小单片机。

第二是汇聚层。室外机采集的数据通过 RS485 或 LoRa 传到室内机,室内机把六类数据统一成 JSON 格式,再转发给显示和上云模块。这个中间层非常关键,它让整套系统不依赖某个传感器的 I2C 地址或某种通信协议,换传感器时只需要改一小段解析代码。

第三是网关层。板子本地挂了一块 SPI 屏幕做实时显示,同时自己跑一个极简 HTTP 服务,局域网内任何设备都能访问/api/weather拿到 JSON 数据。此外还接了一个 MQTT 客户端,把数据推送到 Broker,云端画曲线和手机查看都走这条路。

这样分层之后,整个项目从"一块板子连一堆传感器"变成"三层各司其职",调试起来思路非常清晰。

2. 室内外分离的采集架构:传感器配置与接线

2.1 室内机和室外机的职责边界

很多 DIY 气象站项目最后做成一锅粥,问题出在职责边界没划清楚。室内机和室外机不是简单地在不同位置放两组传感器,而是各自有明确分工。

室外机负责测得"环境真实值",包括温度、湿度、风速、风向、雨量、气压。它的环境最恶劣,要面对太阳辐射、雨水、凝露、电线感应雷和各种小飞虫,所以原则是传感器尽可能少,通信尽可能省电,不跑 LCD,不跑 Web Server,只把数据发给室内机。

室内机负责"展示和联网"。它把室内温度和室外数据合并成统一格式,驱动本地屏幕、提供 HTTP 接口、推送 MQTT。室内机对体积和功耗不敏感,可以插电运行,唯一要求是稳定不死机。这样划分后,室外机做到极简低功耗,室内机则可以做得很"豪华"。

我的实际做法是:室外机有一个塑料防水盒,里面只放主控、传感器、RS485 收发器或 LoRa 模块,以及电源管理部分;室内机放在客厅电视柜上,屏幕朝外,后面拖一根网线或 USB 电源线,完全本地供电。

2.2 温湿度传感器:优先 I2C,避开单总线的坑

温湿度传感器我最推荐 SHT30 或 BME280,原因就一条:它们走 I2C,稳定性和精度都远好于 DHT11/DHT22 这类单总线传感器。

DHT22 虽然是新手最爱,但实际工程里很容易出问题。单总线时序要求非常严格,主控稍被中断打扰一次就可能读到全 0xFF,所以必须用 GPIO 模拟时序并且关闭中断,这在复杂固件里很别扭。SHT30 就简单得多,写两个字节的 I2C 命令然后等一小段时间再读结果,出错时还能通过 CRC 校验判断数据是否有效。

接线方面有个容易被忽略的点:传感器的 VCC 和 GND 一定要先接,再接 SCL/SDA,不然热插拔可能把芯片搞死。另外,I2C 总线上挂多个传感器时,每个传感器的地址要靠 ADDR 引脚区分,比如 SHT30 常用地址是 0x44 和 0x45,BME280 是 0x76 和 0x77。我把室内和室外各放一个 SHT30,地址错开,用一根 I2C 总线还分不出来,后来干脆让室外机独立跑,室内 SHT30 挂在主控总线上,省去了一堆地址冲突问题。

2.3 风速、风向与雨量:脉冲信号接法

温湿度这类数字传感器接线简单,真正考验硬件的是风速、风向和雨量这类"机械传感器"。

我用的是常见的三杯式风速计,内部霍尔器件或干簧管输出脉冲信号。风速和脉冲频率成正比,不同型号系数差别很大,常见风速计大概是每转输出一个脉冲,风速约风速(m/s) = 频率(Hz) × 0.75左右,具体参数要查型号或自己实测。接线时信号线要加上拉电阻,因为干簧管和霍尔输出大多是开漏,不加上拉的话信号全是毛刺。

雨量桶更直接——翻斗式雨量计,每翻斗一次输出一个脉冲,一般代表 0.2mm 降雨量。它的坑在于翻斗动作瞬间会产生抖动,一个斗可能输出两三个脉冲,必须在固件里做消抖处理:连续检测到两个沿的时间间隔小于某个阈值,就只算一次。

风速计和雨量计的输出都接在主控的定时器捕获引脚上,用输入捕获模式统计脉冲数和脉冲间隔,这样 CPU 不用一直瞪着电平变化。风向传感器我简化成 8 方向磁簧开关,8 根线接 GPIO,哪个方向导通就读哪个值;如果换成分压式风向标,直接接 ADC 即可。

2.4 气压、光照等扩展传感器的取舍

气象站除了温湿度,气压和光照也能带来不少信息。气压我用 BME280,顺便还能把湿度一起读了,一颗搞定三个参数。光照用 BH1750,I2C 直接读 Lux 值,非常省事,放室内和放室外含义不同:室外光照反映太阳辐射强度,室内光照更多是判断窗帘要不要拉。

这里要提醒一句:传感器不是越多越好。每增加一个传感器,就多一路供电、一份接线、一段调试时间,还可能多一个故障点。我对扩展传感器的取舍原则是:优先选 I2C 接口、3.3V 供电的设备,因为这样可以共用电源域和总线,电路和固件复杂度最低。模拟输出的传感器如果非用不可,一定要确认主控 ADC 的输入范围和传感器输出范围匹配,不然又要加分压电阻又要算偏移,很麻烦。

3. 模拟量采集与滤波:ADC 出来的数据为什么不能直接用

3.1 分压电路与参考电压的坑

风向标、光电池、部分风速计模块输出的是模拟电压,ADC 采集前要先过一遍脑子,不能直接把线怼上去。

第一个坑是参考电压。很多开发板的 3.3V 基准电压精度并不高,如果板子供电来自 USB 口或开关电源,Vref 可能有 2% 以上的偏差,直接导致 ADC 读数系统性偏移。我的做法是先测一下实际 Vref,比如用 1.024V 的外部基准模块校准,或者直接用万用表量 3.3V 引脚电压,把真实值写进固件做比例换算。

第二个坑是分压电阻。有些传感器输出范围超过 MCU 的 ADC 最大量程,比如 0~5V 的输出,直接进 0~3.3V 的 ADC 必然削顶。用两只电阻分压时要注意等效输出阻抗,阻值太大(比如 100kΩ 对 100kΩ)会导致 ADC 采样保持电容充不进电,读数漂得离谱。我一般控制在 10kΩ 级别,必要时加一级电压跟随器(运放)隔离。

第三个坑是防护。室外模拟传感器线路很长,信号线上难免感应雷电共模电压,虽然不会打雷直接命中,但夏季雷雨天气读数偶尔会跳变。我的临时做法是在 ADC 引脚前并联一个小电容(比如 0.1uF)和两个对地的保护二极管,至少能滤掉大部分毛刺。

3.2 平均滤波与窗口选择

传感器原始数据即便接对了,读数也往往是抖的,尤其是温度、湿度这种缓慢变化的量。ADC 采一次 0.34V,下一次 0.36V,直接显示温度就变成 25.1℃、25.7℃ 来回跳,看着特别不专业。

我的滤波策略是中值滤波 + 滑动平均两段式:先对一组 5 个原始采样取中值,去掉异常尖刺;再把中值结果放入一个长度为 10~20 的滑动窗口求平均,用来展示和上报。

滤波窗口长度需要根据量测对象调。温度变化很慢,窗口拉长到 20 也没问题,曲线会很顺滑;雨量脉冲则完全不能滤波,要的是实时计数;风速介于两者之间,窗口取 5~10 秒平均风速,比瞬时风速更有参考价值。有一个细节值得注意:气象数据普遍按 1 分钟或 10 分钟上报一次,滑动窗口长度千万别拉到 60 秒以上,否则你看到的是"几十秒前的平均值",极端天气反应不过来。

3.3 标定:用一个温度计做差值补偿的经验

传感器出厂精度再高,经过分压电阻、参考电压、接线电阻几层损耗后,读数也会出现固定偏差。标定这件事没那么玄乎,我的方法是家里准备好一支已经校准过的工业温度计,把传感器和它放在同一个环境里稳定 2 小时后,读取差值,然后在固件里加一个线性补偿:真实值 = 读数 × 斜率 + 偏移。

湿度标定难度高一些,可以用饱和盐溶液做参考,比如氯化钠饱和溶液在 25℃ 时的相对湿度约 75.3%,把传感器放在密封罐里隔 12 小时再看偏差。气压传感器一般不需要手动标定,BME280 出厂校准系数都烧在芯片内部,读出来的已经是补偿后的值。

风速标定同样可以自己来:用电风扇开到固定档位,用手持风速计在旁边测,记录风速和脉冲频率的对应关系,多点拟合出系数。这一步不做的话,风速计读出来的值只能算"趋势",不能算"准确风速"。

4. 数据链路设计:串口、RS485 与无线方案

4.1 有线 RS485 方案:稳定但施工麻烦

室内机和室外机的距离通常不远,从几米到几十米,如果布线方便,我首选 RS485 而不是普通 TTL 串口。原因很简单:TTL 电平是 3.3V/0V,线一长就容易衰减和受干扰;RS485 用差分信号,两根绞线可以跑几百米,抗干扰能力强很多。

RS485 接线只有两根线,A 和 B,半双工通信。连接时要注意两端各接一个 120Ω 终端电阻,否则高速收发会出现反射,实测波形会像台阶一样。用主从轮询模式最简单:室内机为主机,定时发送查询指令,室外机作为从机,收到后立刻回复当前数据。

方向切换有一个容易踩的坑:RS485 收发器在发送时要控制 DE/RE 引脚拉到发送态,发完必须马上切回接收态,否则总线上会一直占着不放。我一开始用延时 10ms 再切换,短距离没问题;后来线延长到几十米,就需要改成发完一帧、等发送完成中断、再切接收,否则最后几个字节会被自己的回环数据干扰。

4.2 无线方案:LoRa 是比 WiFi 更合适的选择

如果不想拉线,无线方案里我推荐 LoRa,而不是 WiFi。室外机如果内置 WiFi 模块,单次连接功耗高,断网后重连逻辑又繁琐,放室外电池供电完全扛不住。LoRa 的优势是低功耗、远距离、穿墙能力强,433MHz 模块在普通住宅环境穿三四堵墙还能稳定通信,单次发射耗电大约 120mA 只持续零点几秒。

LoRa 模块配置时要注意频率、扩频因子、带宽三个参数。我用的是 433MHz 频段,扩频因子 SF7,带宽 125kHz,实测室内外相距 20 米左右延迟很低。不建议在市区直接调 SF12 追求极限距离,发射时间太长反而更容易被同频干扰。室外机平时让 LoRa 模块进入休眠,每 10 分钟唤醒一次发数据,发完立刻睡,这样模块耗电几乎可以忽略。

另外,LoRa 和数据帧格式要提前想清楚,它带宽很窄,不适合一把 JSON 直接怼上去,最好用紧凑二进制帧传输,到室内机再解析展示。

4.3 数据帧格式:简单但够用

无论 RS485 还是 LoRa,室内外通信的帧格式我统一设计成如下结构:

帧头(0xAA 0x55) + 数据长度 + 类型 + 数据 + CRC16

类型字段区分帧是"风速数据""环境数据"还是"心跳",数据区按固定字节序排列。这样做的好处是解析端不用猜,收到帧先查帧头、再验 CRC,数据出错直接丢弃,不会把坏数据当真实数据上报。

温度传递我统一用放大 10 倍的整数,比如 25.3℃ 传 253,客户端再缩小回去。气压传整数 Pa,光照传整数 Lux。这样固件里不用浮点运算,内存占用更低,串口调起来也更直观。数据帧里别忘了带序列号,接收端可以判断是否丢帧,丢帧太多就该提醒检查通信链路。

5. 数据出口:本地显示、Web 页面与 MQTT 上云

5.1 LCD 本地显示

室内机主控直接挂了一片 SPI 接口的 LCD,不需要跑触摸,只做展示。刷屏逻辑非常简单:每秒重新刷新一次所有数值,温度保留一位小数,气压和气概取整,风速保留一位小数。

刷新 LCD 有个常见坑:不要在传感器读取和数据处理的中断里做绘图,SPI 刷屏很占时间,容易把中断逻辑拖垮。正确做法是主循环里标记一个display_dirty标志,等传感器数据都更新完,再一次性刷新屏幕。屏幕放在室内机,本身就是插电状态,不需要考虑低功耗,把刷新率从每秒 1 次降到每 2 秒 1 次,肉眼几乎无区别,还能降低发热。

5.2 板端内置 Web Server

本地屏幕只是入口,真正方便的是在局域网里用手机随时看。我在 W55MH32-ADK 上跑了一个极简 HTTP 服务,只做两件事:提供静态页面和返回 JSON 接口。

/api/weather返回当前所有数据:

{ "indoor_temp": 25.3, "indoor_hum": 55, "outdoor_temp": 12.8, "outdoor_hum": 68, "pressure": 101325, "wind_speed": 3.2, "wind_dir": 135, "rain": 0.2, "lux": 3200, "timestamp": 1734567890 }

浏览器里打开同一 IP 的/路径,就是一张自动刷新数据的简单 HTML 页面。做这一步的关键是 HTTP 解析不要太复杂,固件里只处理 GET 请求,解析 URL 路径,对未知路径统一返回 404。千万别在一开始就给板子加 HTTP 长连接、分块传输这些功能,内存和复杂度都会失控。

5.3 MQTT 上云与按主题拆数据

本地 Web 够用了,但要做历史曲线还是得上云。我的做法是让室内机主控定时把 JSON 数据 publish 到 MQTT Broker,按主题拆分:

  • weather/indoor/temperature
  • weather/outdoor/temperature
  • weather/outdoor/wind
  • weather/outdoor/rain

每条主题的 payload 就是对应数值,云端订阅这些主题后写入时序数据库,再用 Grafana 或者 Home Assistant 画面板。这样拆主题的好处是,以后想只显示温度曲线,不用解析整个 JSON。

MQTT 客户端在 32 位 MCU 上跑需要注意重连逻辑。家里路由器重启、宽带断线,TCP 连接会被静默切掉,客户端容易一直以为还连着。我设置了每 60 秒主动 ping 一次 Broker,连续三次 ping 失败就断开重连,实测稳定性提升非常明显。

6. 功耗核算、太阳能供电与外壳防水的工程细节

6.1 室外机低功耗模式与唤醒节奏

室外机如果拉网线供电,那所有功耗优化都可以跳过;但想放阳台外、屋檐下甚至院子里,就必须做电池供电。我用的是 3.7V 锂电池 + 低功耗主控 + LoRa 的组合。

低功耗的核心设计是按需唤醒。室外机默认进入深度睡眠模式,只有定时器在跑,每 10 分钟唤醒一次,做以下事情:读取传感器、计算平均风速和雨量累加、打包成帧、通过 LoRa 发送到室内机、再次睡去。发送完立即关掉传感器电源和 LoRa 模块电源,通过 MOSFET 切电,比软件关闭外设时钟更实在。

MCU 深度睡眠电流一般在微安级别,整个室外机真正耗电的不是主控,而是传感器和通信模块。所以电源切换电路很关键:平时把所有传感器和 LoRa 的 VCC 断开,唤醒瞬间再打开,等模块上电稳定几十毫秒后再读取数据。

6.2 太阳能+电池容量核算

室外机如果只靠电池,几个月就得换一次。有些位置实在不方便换电,我就加了太阳能板。选型前先做一次功耗估算,千万不能拍脑袋。

我给的典型场景是:室外机传感器采集阶段电流约 10mA,持续 1 秒;LoRa 发送阶段约 120mA,持续 0.5 秒;其他时间睡眠。10 分钟一个周期,平均电流大概在几十微安到 0.5mA 之间,再加上电源转换损耗和传感器上电损耗,我给整机预算 5mA 平均电流。

用 2600mAh 锂电池算,不考虑太阳能时能撑约 20 多天,这个余量已经够应付连续阴雨。太阳能板选 6V/2W,晴天日均发电按 4 小时有效日照折算,一天大约 8Wh 左右,而负载一天耗电不到 0.5Wh,充电量富余非常多。配合充电管理模块,要特别注意不能用太阳能板直连电池,必须经过带防反充的充电管理芯片,否则晚上太阳能板反而变成负载把电池抽干。

我实际测试下来的经验是,这套组合在广东这种冬天阴雨天多的地方也能连续维持,最差情况下半个月不见太阳,电池电量也只掉一部分。

6.3 室外机外壳的坑:防晒、防水、防结露

室外机外壳我选用 IP65 以上等级的防水接线盒,但只用防水盒还远远不够。

第一个坑是太阳辐射。黑色或深色外壳被阳光直射后,内部温度会比真实气温高好几度,温度传感器测出来根本不是气温,而是"盒子里的热空气"。解决办法是把盒子刷成白色或银色,放在屋檐下避免直射,同时温度传感器尽量外置到盒子外面,至少也要让测温探头紧贴外壳外侧。

第二个坑是结露。盒内空气湿度大、温度变化剧烈时,内部会凝露,传感器直接短路。我的做法是盒内放两三包硅胶干燥剂,并在盒子底部开一个小呼吸孔(孔径几毫米,内部贴防水透气膜),让内外气压平衡又不进水。

第三个坑是防风。雨量桶和风速计都是用支架固定在盒子外面的,常规 L 型支架在台风天容易被吹歪。我用两个固定点把风速计支架夹在阳台栏杆上,又加了一根钢丝备用,这套装置经历过两次台风,目前还没被吹跑。

7. 固件架构和踩坑实录

7.1 裸机调度:几个定时器加一个状态机足够

室外机和室内机的固件我都没有上 RTOS,用裸机前后台架构跑得很稳。道理很简单:任务少、周期固定、每个任务的耗时都可控,RTOS 带来的线程切换反而引入不确定性。

主循环里我是这样安排的:

  • 一个毫秒级定时器产生时基,每 10ms 处理一次按键和 Led 刷新;
  • 每秒检查一次是否到了传感器采集时间;
  • 每 10 分钟触发室外机唤醒,完成采集和发送;
  • 每 60 秒触发室内机 MQTT 数据上报。

每个任务只是一个函数,主循环轮询标志位,到了就执行。这套结构最大好处是出问题好定位,哪怕某个传感器卡住,也不会影响其他任务,因为每个任务都会在开头检查一下"上一个周期是否正常完成"。

7.2 I2C 总线卡死:一次把 SDA 拉死的故障排查

踩坑实录里最典型的就是 I2C 卡死。某天室外机突然收不到 SHT30 的读数,把传感器换到室内机单独测试,发现每次读到一半,I2C 总线就卡死,SCL 正常跳,SDA 一直拉低,后续命令全部无响应。

排查下来,问题出在长线供电和接线毛刺上。室外传感器的线接近两米,风吹日晒后接线端子氧化,接触电阻变大,供电瞬时跌落,传感器内部逻辑混乱,最后把 SDA 线拉在低电平不放。真正解决问题的办法有两步:

第一,固件里加 I2C 总线恢复代码。当一次通信超时后,把 SDA 和 SCL 引脚临时配置成普通 GPIO 输出,手动翻转 SCL 九个时钟周期,让卡死的从机释放 SDA,再恢复正常 I2C 模式重试。这一步我实现成独立函数,每次 I2C 失败都会调用。

第二,硬件上把所有接线端子换成压接端子并灌胶固定,传感器供电端加一个 100uF 电解电容和 0.1uF 陶瓷电容,保证电压稳定。加了这两个措施后,再没有出现过 I2C 卡死。

7.3 传感器失效与数据异常的判读

室外设备长期风吹雨淋,传感器一定会出问题,所以要提前设计"数据可信度"判断逻辑,而不是傻乎乎地一直显示失效值。

我的规则很简单:每次传感器读回来先做合理性检查。温度超过 -30℃~60℃ 直接判无效;湿度和光照超过量程清零;气压变化率一小时内超过 5 hPa 也标记异常。一旦判定异常,该通道的值就不参与上报,屏幕上会显示--,云端曲线会断点而不是出现离谱的尖峰。

这个逻辑在雨季特别有用。有一阵雨量计被落叶卡住翻斗,误报了几十毫米降雨,就是因为没做连续降雨合理性判断。后来加了规则:连续 24 小时降雨超过 300mm,系统自动提醒检查雨量计。虽然这个阈值过于严苛,但至少把机械故障和真实暴雨区别开,省掉了不少误报清洗工作。

另外,室外机和室内机之间如果连续 3 轮通信都失败,室内机要主动标记"室外机离线",而不是继续展示上一份缓存数据,这个状态提示能让用户第一时间发现链路故障,而不是对着过时数据做判断。

整套系统从画草图到跑稳定大概花了两个周末,现在已经在阳台外连续运行了几个月。如果让我重做一遍,我会第一版就直接上太阳能+低功耗方案,而不是先用有线调试、之后再改无线供电,后者反而花掉更多调试时间。最后留一条建议:气象站这类项目,稳定性和长期运行比功能炫酷重要得多,先跑通一个最简链路,再一步步加传感器,否则很容易卡在"什么都想做、什么都没做稳"的状态。

返回列表