ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

基于W55MH32-ADK与MicroPython的室内外气象站实战

2026/10/7 7:43:49 拓冰建站 浏览量
基于W55MH32-ADK与MicroPython的室内外气象站实战 1. 项目缘起与整体设计思路1.1 为什么要在 W55MH32-ADK 上折腾一个气象站手里这块 W55MH32-ADK 开发板主控是带以太网外设的 Cortex-M 内核 MCU板载 RJ45 网口和一颗 AHT20 温湿度传感器。它天生适合做“联网 采集”这类活儿有线网络稳定、供电简单、传感器走 I2C 就近挂在板子上不用额外飞线。我最初的想法很朴素——让板子每隔几分钟把室内温湿度读出来再顺手从公网拉一份室外天气把两组数据并排显示这样一眼就能看出“屋里屋外差多少”决定要不要开窗、要不要加衣服。这个项目解决的核心问题有三个。第一是数据来源割裂室内数据靠本地传感器室外数据靠网络接口两套东西要在一个固件里协同工作。第二是长期运行的稳定性设备一旦上电就应该自己跑下去断网要能自愈不能读一次就卡死。第三是开发效率用 MicroPython 而不是裸机 C改一行逻辑重新烧录几秒钟就能验证调试成本极低。适合谁来参考如果你手上有任意一块带网口、能跑 MicroPython 的开发板想做一个“联网采集 云端数据融合”的小项目这篇内容基本可以照搬。哪怕你用的是别的传感器、别的天气接口思路和踩坑经验也是通用的。全文我会把选型理由、接线、代码、参数计算、故障排查都摊开讲尽量做到你拿着板子就能复现。1.2 整体架构本地采集 云端拉取 双通道输出整个系统的数据流其实就一条主线我把它拆成四段来看采集段AHT20 通过 I2C 总线把室内温度、湿度读进 MCU这是纯本地操作不依赖网络。拉取段MCU 通过以太网口向 Open-Meteo API 发起 HTTP 请求拿到指定经纬度的室外温度、湿度、风速等字段。融合段把两组数据按统一格式整理计算温差、湿度差判断“室内外差异等级”。输出段一路通过串口打印给人看一路可以推到本地 Web 页面或者 MQTT 主题方便远程查看。为什么选 Open-Meteo 而不是别的天气服务这是我在选型时反复权衡的点。很多天气 API 要注册账号、申请 Key、有调用次数限制个人项目跑着跑着就被限流。Open-Meteo 的特点是免注册、免密钥、有免费额度返回的是标准 JSON字段命名清晰对嵌入式设备非常友好。你只需要拼一个带经纬度的 URL用 GET 请求就能拿到数据省掉了鉴权这一整块复杂度。为什么用以太网而不是无线有线连接在长期运行场景下优势明显不涉及配网流程、不受信号强弱影响、延迟稳定。W55MH32-ADK 板载的网口配合 MicroPython 的网络栈初始化之后就是一个标准的 socket 接口写起来和 PC 上没区别。对于“插上电就扔在角落跑几个月”的设备有线是最省心的选择。1.3 技术栈选型的取舍逻辑MicroPython 在这个项目里扮演的是“胶水 逻辑”的角色。有人会问这种活儿用 C 写不是更省资源吗确实C 的固件更小、启动更快。但代价是HTTP 客户端、JSON 解析、I2C 驱动这些都要自己啃调试一次要重新编译烧录迭代速度慢一个数量级。MicroPython 把这些都封装好了machine.I2C、socket、json都是现成的我改一行业务逻辑几秒钟就能看到结果。对于原型验证和小批量部署这个效率优势是压倒性的。AHT20 的选择理由也很直接它是 AHT10 的升级款出厂校准、I2C 接口、温湿度一体精度在 ±0.3°C / ±2%RH 这个量级做室内监测完全够用。它比 DHT 系列强的地方在于不需要严格的时序延时DHT 那种单总线协议对微秒级延时很敏感MicroPython 下经常读失败AHT20 走标准 I2C读失败的概率低得多代码也干净。提示AHT20 上电后需要约 100ms 的稳定时间第一次读取前建议先发初始化命令并等待否则可能读到全 0 或者 0xFFFF 这种无效值。2. 硬件连接与核心细节解析2.1 板载资源盘点与接线确认W55MH32-ADK 这块板子拿到手先别急着写代码把资源摸清楚。我习惯先做三件事确认主控型号和主频、确认网口对应的 PHY 芯片、确认 I2C 引脚位置。主控决定了你能跑多重的协议栈PHY 决定了网络初始化的方式I2C 引脚决定了传感器怎么接。AHT20 的接线只有四根VCC、GND、SDA、SCL。供电用 3.3V千万别接 5VAHT20 的绝对最大供电电压是 3.6V接 5V 大概率直接烧掉。SDA 和 SCL 分别接到板子的 I2C 数据线和时钟线上具体引脚号要看板子的丝印或者原理图。我一般会选一组默认带内部上拉的 I2C 引脚如果板子没有内部上拉就得在 SDA、SCL 上各加一个 4.7kΩ 到 3.3V 的上拉电阻否则总线拉不起来扫描不到设备。项目参数说明传感器供电3.3V严禁 5V会损坏芯片I2C 地址0x38AHT20 固定地址7 位上拉电阻4.7kΩ板载无上拉时需外接网口速率10/100Mbps自适应插上网线即可供电方式DC 或 PoE视板子版本而定接线完成后第一件事不是写业务代码而是扫描 I2C 总线。这一步能帮你排除 80% 的硬件问题。如果扫描不到 0x38那后面所有代码都是白搭先查线、查供电、查上拉。2.2 AHT20 驱动原理与读取流程AHT20 的读取流程分三步理解了这个流程你就能自己写驱动不用到处找库。第一步是初始化。上电后传感器处于休眠状态需要发送初始化命令0xBE带上两个参数字节 0x08 和 0x00。发完之后等 10ms 左右让内部校准完成。这一步很多人会漏掉结果读出来的数据一直是错的。第二步是触发测量。发送测量命令0xAC同样带两个参数字节 0x33 和 0x00。发完之后传感器开始转换典型转换时间是 75ms 到 80ms。这段时间你必须等不能立刻去读否则读到的是上一次的旧数据或者忙状态。第三步是读取数据。AHT20 返回 6 个字节加 1 个状态字节。前两个字节是湿度的高位和低位中间两个字节是温度的高位和低位最后是 CRC 校验。原始数据是 20 位的需要做位运算拼接再换算。换算公式我直接给出来这是从数据手册里抠出来的湿度%RH (原始湿度值 / 2^20) × 100温度°C (原始温度值 / 2^20) × 200 - 50这里的 2^20 就是 1048576。为什么是 20 位因为 AHT20 的 ADC 是 20 位精度原始值范围是 0 到 1048575。温度公式里的“×200 - 50”是因为传感器输出的是相对值需要线性映射到 -50°C 到 150°C 这个量程。注意读取时如果状态字节的 bit7 是 1说明传感器还在忙需要等待后重读。这个判断一定要加否则会读到脏数据。2.3 网络初始化与 Open-Meteo 接口解析网络这块MicroPython 的network模块提供了 LAN 类初始化流程是创建 LAN 对象、激活、配置 DHCP或者静态 IP、等待连接、获取 IP 地址。整个过程通常几秒钟内完成。我建议用 DHCP省去手动配 IP 的麻烦路由器会自动分配。拿到 IP 之后就可以发 HTTP 请求了。Open-Meteo 的接口 URL 结构是这样的https://api.open-meteo.com/v1/forecast?latitude39.9longitude116.4currenttemperature_2m,relative_humidity_2m,wind_speed_10m参数说明latitude和longitude是经纬度current后面跟你要的字段用逗号分隔。返回的 JSON 里current对象下就是你要的数据。字段名temperature_2m表示 2 米高度的气温relative_humidity_2m是相对湿度wind_speed_10m是 10 米风速。为什么用 HTTPS 而不是 HTTP因为很多公共 API 已经强制 HTTPS 了而且 HTTPS 能防止数据被中间篡改。MicroPython 的ssl模块支持 TLS但要注意TLS 握手会消耗较多内存和时间如果你的板子内存紧张可能会失败。这时候可以退而求其次用 HTTP但要确认接口支持。字段含义单位temperature_2m2 米气温°Crelative_humidity_2m相对湿度%wind_speed_10m10 米风速km/hweather_code天气代码整数天气代码是个整数对应不同的天气状况比如 0 是晴天3 是阴天61 是小雨。你可以自己建一个映射表把代码翻译成中文描述显示出来更直观。3. 实操过程与核心环节实现3.1 环境搭建与固件准备第一步是把 MicroPython 固件烧进板子。不同板子的烧录方式不一样有的是拖拽 UF2 文件有的是用 esptool 之类的工具。W55MH32-ADK 具体用哪种看官方文档。烧录完成后用串口工具连上去应该能看到 MicroPython 的 REPL 提示符。看到这个符号说明固件跑起来了。第二步是准备开发环境。我习惯用 Thonny 或者 mpremote 这类工具能直接编辑文件、上传到板子、运行脚本。Thonny 的好处是有文件管理界面能看到板子上的文件系统改代码很方便。mpremote 更适合命令行党一条命令就能把本地文件推到板子上。第三步是确认网络。插上网线在 REPL 里跑几行代码看能不能拿到 IP。如果拿不到先查网线、查路由器 DHCP、查板子网口指示灯。这一步通了后面才有意义。import network lan network.LAN() lan.active(True) # 等待 DHCP 分配 for _ in range(20): if lan.isconnected(): break time.sleep(0.5) print(lan.ifconfig())这段代码跑完如果打印出 IP、掩码、网关、DNS说明网络通了。如果一直是0.0.0.0那就是没连上别往下走。3.2 AHT20 读取代码逐行拆解下面这段是我实际用的 AHT20 读取函数我把它拆开讲。from machine import I2C, Pin import time AHT20_ADDR 0x38 def aht20_init(i2c): # 发送初始化命令 0xBE, 参数 0x08 0x00 i2c.writeto(AHT20_ADDR, bytes([0xBE, 0x08, 0x00])) time.sleep_ms(20) def aht20_read(i2c): # 触发测量 0xAC, 参数 0x33 0x00 i2c.writeto(AHT20_ADDR, bytes([0xAC, 0x33, 0x00])) time.sleep_ms(80) data i2c.readfrom(AHT20_ADDR, 7) # 检查忙状态 if data[0] 0x80: return None raw_h (data[1] 12) | (data[2] 4) | (data[3] 4) raw_t ((data[3] 0x0F) 16) | (data[4] 8) | data[5] humidity raw_h / 1048576.0 * 100 temperature raw_t / 1048576.0 * 200 - 50 return temperature, humidity逐行看writeto是向指定地址写字节第一个字节是命令后面是参数。readfrom是读指定长度的字节。位运算那两行是关键湿度占 20 位跨了 data[1]、data[2]、data[3] 的高 4 位温度也占 20 位从 data[3] 的低 4 位开始接 data[4]、data[5]。这种“跨字节拼接”在传感器驱动里非常常见理解了一次就通用了。1048576.0就是 2 的 20 次方。用浮点数除法是为了保留小数精度。如果你想要整数运算省资源可以先乘 100 再除但 MicroPython 下浮点开销可以接受直接写更清晰。实操心得time.sleep_ms(80)这个延时不能省。我试过改成 50ms读出来的湿度偶尔会跳变改成 80ms 之后就稳定了。数据手册写的是典型 75ms留点余量更保险。3.3 HTTP 请求与 JSON 解析完整流程网络请求这块MicroPython 的urequests库用起来最顺手。它模仿了 requests 的接口get一下就能拿到响应对象.json()直接解析成字典。import urequests def fetch_weather(lat, lon): url https://api.open-meteo.com/v1/forecast?latitude{}longitude{}currenttemperature_2m,relative_humidity_2m,wind_speed_10m.format(lat, lon) try: resp urequests.get(url, timeout10) data resp.json() resp.close() cur data[current] return { temp: cur[temperature_2m], humidity: cur[relative_humidity_2m], wind: cur[wind_speed_10m] } except Exception as e: print(fetch error:, e) return None这里有几个细节值得说。timeout10是必须加的否则网络卡住时程序会一直等整个循环就僵死了。resp.close()也很重要MicroPython 的 socket 资源有限不关闭会泄漏跑久了就报内存错误。异常捕获是兜底网络请求失败是常态不能让一次失败把整个程序搞崩。JSON 解析后data[current]就是当前天气对象。字段名要和请求参数里写的一致写错了会 KeyError。我建议先把返回的原始 JSON 打印出来看一眼确认结构再写解析代码比对着文档猜要快得多。3.4 主循环设计与数据融合逻辑主循环的设计原则是采集和拉取解耦各自有各自的节奏。室内传感器可以每 5 秒读一次室外天气每 10 分钟拉一次就够了没必要频繁请求。用一个计数器或者时间戳来判断是否到了拉取时机。import time last_fetch 0 FETCH_INTERVAL 600 # 10 分钟 while True: temp_in, hum_in aht20_read(i2c) now time.time() if now - last_fetch FETCH_INTERVAL: weather fetch_weather(39.9, 116.4) last_fetch now if weather and temp_in is not None: diff_t temp_in - weather[temp] diff_h hum_in - weather[humidity] print(室内 {:.1f}C {:.1f}% | 室外 {:.1f}C {:.1f}% | 温差 {:.1f}C.format( temp_in, hum_in, weather[temp], weather[humidity], diff_t)) time.sleep(5)温差和湿度差是这个小项目最有价值的输出。温差为正说明室内比室外热为负说明室内更凉。湿度差能帮你判断室内是不是太干或者太潮。我一般会把温差超过 5°C 或者湿度差超过 20% 的情况标记出来提示“建议通风”或者“注意保湿”。提示time.time()在没有 RTC 的板子上可能返回的是开机后的秒数不是真实时间。如果你需要真实时间戳得加一个 RTC 模块或者开机时从网络同步时间。对于判断“是否到了拉取时机”这个用途开机秒数完全够用。4. 常见问题与排查技巧实录4.1 传感器读不到数据的排查路径这是新手最容易卡住的地方。现象是 I2C 扫描不到 0x38或者读出来全是 0。排查顺序我总结成一张表现象可能原因排查方法扫描不到设备接线错误万用表测 SDA/SCL 通断扫描不到设备供电不足测 VCC 是否 3.3V扫描不到设备缺上拉电阻测 SDA/SCL 空闲电平读出全 0未初始化检查是否发了 0xBE读出全 0读取太快增加转换等待时间数据跳变延时不足把 80ms 加到 100ms我踩过最坑的一次是 SDA 和 SCL 接反了。I2C 总线接反不会烧芯片但就是扫不到设备查了半天才发现。所以接线之后第一件事就是对照原理图确认引脚别凭感觉。另一个坑是上拉电阻。有些板子的 I2C 引脚内部有弱上拉但阻值很大长导线或者多设备时拉不起来。这时候外接 4.7kΩ 上拉就能解决。判断方法很简单用示波器或者逻辑分析仪看空闲时的电平如果不到 3.3V就是上拉不够。4.2 网络请求失败的典型场景网络这块的问题比传感器更隐蔽因为失败原因多。我遇到过的典型场景有这么几个。DNS 解析失败。现象是urequests.get报错说找不到主机。原因是板子没拿到 DNS 服务器地址或者 DNS 服务器不可达。解决办法是在ifconfig里手动指定 DNS或者检查路由器的 DHCP 配置。TLS 握手超时。HTTPS 请求在内存紧张的板子上容易失败因为 TLS 握手需要分配缓冲区。如果反复失败可以试试减少同时打开的 socket 数量或者改用 HTTP前提是接口支持。响应体太大导致内存溢出。Open-Meteo 返回的 JSON 如果字段要得太多可能几百 KBMicroPython 的堆扛不住。解决办法是只请求你需要的字段别一股脑全要。我在 URL 里只写了三个字段返回体就很小解析毫无压力。请求频率过高被限流。免费接口都有隐形限流请求太频繁会被暂时拒绝。我的做法是室外数据 10 分钟拉一次既够用又不会触发限制。实操心得给网络请求加一个重试机制。第一次失败后等 5 秒重试连续失败 3 次就跳过这一轮等下一个周期再试。这样偶发的网络抖动不会影响整体运行。4.3 长期运行的稳定性经验这个项目我连续跑了两周中间遇到过几次问题总结出来供参考。内存泄漏。MicroPython 的垃圾回收不是实时的长时间运行后如果频繁创建对象内存会碎片化。我的对策是把能复用的对象提到循环外面比如 URL 字符串、socket 缓冲区别在循环里反复创建。另外可以定期手动调用gc.collect()强制回收。网络断线不自愈。网线被碰掉或者路由器重启后lan.isconnected()会变成 False但程序如果没检查这个状态就会一直发请求失败。我在主循环里加了连接检查一旦发现断开就重新初始化网络等连上再继续。传感器偶发读失败。AHT20 偶尔会返回忙状态这时候我的代码返回 None主循环里判断 None 就跳过这一轮不更新数据。这样不会因为一次读失败就崩溃。看门狗。如果板子支持硬件看门狗一定要开。程序跑飞时看门狗会自动复位比人工去拔电重启强太多。MicroPython 的machine.WDT就是干这个的喂狗间隔设成几秒主循环里定期喂。问题根因对策内存不足对象频繁创建复用对象 定期 gc网络断线物理断开状态检查 重连读传感器失败忙状态返回 None 跳过程序跑飞未知开启看门狗4.4 数据展示与后续扩展方向数据读出来了怎么展示是个加分项。最简单的就是串口打印调试够用。想好看一点可以在板子上跑一个极简的 HTTP 服务器把数据渲染成网页手机浏览器打开就能看。MicroPython 起一个 socket 服务器不难几十行代码的事。再进一步可以把数据推到 MQTT 主题接上本地的智能家居系统或者存到数据库里做趋势分析。我试过把数据写进一个 CSV 文件存在板子的 flash 里跑一周后导出来画曲线能看出室内温湿度的日变化规律挺有意思。经纬度怎么定如果你不知道自己的经纬度可以用地图工具查一下精确到小数点后两位就够了天气接口对精度要求不高。我一般用城市中心的坐标反正室外天气本来就是大范围的差几公里没影响。最后分享一个小技巧把常用的配置项经纬度、拉取间隔、I2C 引脚号单独放在文件开头用常量定义。这样换地方部署时只改这几行不用翻遍整个代码。我吃过这个亏之前把经纬度硬编码在函数里换个城市测试时找了半天才找到在哪改。