直接把一个ESP32C6刷成Zigbee空气质量监测仪,再直连Home Assistant,这套组合我已经稳定跑了两周,家里客厅和书房的空气数据都在HA面板上实时展示,中间还触发过几次自动化,比如PM2.5突增时提醒我开净化器,效果挺直观。
如果你也想在Home Assistant里加一个低成本、低功耗、不依赖云的空气质量监测节点,又不想花大几百买成品,那这个项目非常适合抄作业。核心思路很简单:用ESP32C6这颗自带802.15.4物理层的SoC跑Zigbee协议栈,把温湿度、TVOC/CO2当量、PM2.5等数据本地采集,然后通过Zigbee直接进HA,全程没有WiFi中转,没有云平台。
这篇文章会完整拆解我的选型思路、传感器组合、固件开发、Zigbee组网,以及HA里的接入配置和自动化。更重要的是,我会把调试过程中踩过的坑单独列出来,比如设备频繁离线、数据跳变、信道干扰、颗粒物传感器功耗控制等,这些经验比教程里的步骤更有价值。
1. 项目整体设计与硬件选型
1.1 为什么用ESP32C6做Zigbee设备
先说结论:如果你手里已经有一块ESP32C6开发板,这个项目基本零成本起步。
ESP32C6和早期常用的ESP32相比,最核心的区别是多了一个802.15.4无线电接口。802.15.4是Zigbee和Thread的物理层基础,意味着这颗芯片原生就能跑Zigbee协议栈,不需要像以前那样给ESP32外挂一颗CC2530或者别的Zigbee协处理器芯片。这个差异非常关键:Zigbee功能是芯片硬件上支持的,不是靠软件模拟,稳定性和时序都有保证。
Zigbee相比WiFi,最突出的优势是低功耗、网状组网和本地化。空气质量监测这类设备的数据量很小,每5分钟上报一次温湿度和空气数据,每次的字节数可能就几十个。WiFi在这种场景下不仅大材小用,还常年维持着高功耗,做成电池供电根本撑不住。Zigbee终端设备可以进入休眠模式,由父节点暂时缓存数据,需要上报时再唤醒,平均功耗能压到很低。
另外一个容易被忽略的点是,Zigbee网络是独立于WiFi的。家里WiFi波动、路由器重启、运营商光猫出问题,都不会直接影响Zigbee设备的数据上报。对玩HA的人来说,“系统越不依赖路由器越好”是常识,尤其是把传感器作为自动化触发源时,链路的可靠性比什么都重要。
当然,ESP32C6也不是没有短板。它的802.15.4射频性能和专业Zigbee协处理器相比,发射功率和接收灵敏度在极限场景下有差距,穿墙能力会弱一些。对于桌面摆放、柜顶安装这种中短距离场景完全够用,但如果想装到房屋偏远角落,就需要考虑设备位置或者中间增加Zigbee路由节点。
1.2 传感器组合方案
空气质量监测这个概念范围很大。我做的是一个介于“温湿度计”和“专业空气检测仪”之间的版本,重点覆盖三个维度:温湿度、室内空气质量(TVOC和CO2当量)、颗粒物(PM1.0/PM2.5/PM10)。
温湿度传感器用的是SHTC3,I2C接口,精度典型值±2%相对湿度和±0.2摄氏度,比很多开发板上自带的温湿度传感器靠谱太多。SHTC3封装很小,功耗也低,测量完成后可以直接进入掉电模式,非常契合Zigbee的休眠周期。
TVOC和CO2当量用的是SGP30,一款很经典的金属氧化物气体传感器。它出厂前做过校准,能输出总挥发性有机化合物TVOC和二氧化碳当量CO2eq两路数据。但有一点必须提醒:SGP30测出来的CO2eq不是真实CO2浓度,而是根据TVOC模型推算出的等效值,它适合做趋势监测,不适合当精确的二氧化碳检测仪。如果非要测真实CO2,得上NDIR红外传感器,比如SCD40或SCD41这类,但价格一下就上去了,基本是SGP30的五到十倍。我这个项目的定位是“低成本趋势监测”,所以选SGP30不亏。想升级也很容易,I2C总线上多挂一个设备而已。
颗粒物传感器用的是激光散射原理的PMS系列,UART串口输出,能同时给出PM1.0、PM2.5、PM10的质量浓度和计数浓度。这类传感器内部有风扇,功耗比前两个传感器大不少,工作电流在几十毫安级别,而且上电后需要预热稳定。固件里不能让它一直跑,我设计成“周期性采样”:设备每次唤醒后,先给颗粒物传感器上电,等风扇转起来并稳定读数后采集一帧数据,然后立刻断电,再执行Zigbee上报。这样既保证数据有效,又不会把电池容量白白吃掉。
1.3 物料清单与成本
这套系统我做了两版,一版是USB供电的桌面版,另一版是预留了低功耗设计的电池版。桌面版物料清单如下:
| 物料 | 说明 | 参考成本 |
|---|---|---|
| ESP32C6开发板 | 板载天线,带USB转串口 | 约30元 |
| SHTC3模块 | I2C温湿度传感器 | 约10元 |
| SGP30模块 | I2C空气质量传感器 | 约20元 |
| PMS系列颗粒物传感器 | UART接口,激光散射 | 约60元 |
| 3D打印外壳 | 带通风孔 | 约10元 |
| 5V/1A电源 | USB供电 | 约10元 |
总物料成本大概在120到150元。市面上支持Zigbee的成品空气质量检测仪动辄四五百,相比之下这个方案便宜不少,而且固件上报频率、校准策略、数据格式全部自己控制,不会被厂商锁定。
2. Zigbee 协议栈与 Home Assistant 集成方案
2.1 ESP32C6 的 Zigbee 能力
现在很多人提到ESP32C6,第一反应是WiFi 6。WiFi 6确实是它的卖点,但真正特殊的是它做到了“双协议”:除了2.4GHz WiFi,还有一个802.15.4无线电,既能跑Zigbee也能跑Thread。也就是说,这颗芯片未来既能做Zigbee终端设备,也能做Matter设备,只是目前Matter over Thread在HA生态里的成熟度还远不如Zigbee。
固件开发上,我最初试过Arduino环境里的一些Zigbee库,但最后还是换成了官方IDF框架里的Zigbee协议栈,并且基于厂商提供的SDK做应用。这套协议栈在角色管理、休眠控制、重连机制上比社区移植的库完整很多,尤其如果你以后想做路由设备或者协调器,强烈建议别绕开官方SDK。做终端设备虽然相对简单,但同样涉及大量的回调函数和网络事件处理,调试信息是否完整直接影响排错效率。
2.2 接入 HA 的路线:ZHA 还是 Zigbee2MQTT
HA接入Zigbee设备的方案主流就两条:ZHA和Zigbee2MQTT,简称Z2M。
ZHA是HA自带的Zigbee集成,通过串口连接Zigbee协调器使用。优点是没额外依赖、配置简单、HA重启后会自动重连。缺点是对非标准设备的兼容性稍弱,调试信息不如Z2M直观。
Zigbee2MQTT是独立的Zigbee网关服务,通过MQTT把设备数据转发给HA。优点是设备兼容库非常庞大,几乎市面上主流的Zigbee设备都能识别,而且有很多高级配置项,比如绑定、重发机制、上报频率限制。缺点是多了一层MQTT架构,需要额外跑一个消息代理。
我最后选择的是Zigbee2MQTT,理由有三个:
- 调试阶段日志友好。Z2M的Web控制台能看到“设备加入网络”“接收到上报”“OTA可用更新”这些事件,ZHA需要翻HA系统日志才能找到类似信息。
- 可以精细控制设备配置。比如Zigbee终端设备的“保持连接”间隔、上报频率、单位转换,Z2M都能在配置里调整,ZHA要改得靠折腾。
- 后续扩展性好。如果以后加其他品牌的Zigbee设备,Z2M的兼容性和社区支持都会省很多事。
当然,如果不想多装任何东西,只想最快看到数据,用ZHA也完全可行。我后面接入部分会附带提两句ZHA的路径。
2.3 为什么“直连”是这个项目的加分项
这里说的直连,是指Zigbee设备直接加入本地的Zigbee网络,而不是像很多WiFi传感器那样,先连路由器、再连云、最后HA再从云端拉数据。
直连带来的好处很实在:
- 本地优先。家里断外网时,传感器上报照常,HA自动化照常触发。
- 数据链路短。从传感器到协调器再到HA,全程局域网,没有第三方服务器中转,延迟低。
- 隐私安全。家里的空气质量数据不会跑到厂商云服务器上,也不需要为了一个传感器注册什么账号。
对追求“不依赖云”的智能家居玩家来说,直连是基本要求,也是这套方案最让我满意的地方。
3. 固件开发与数据采集实现
3.1 开发环境搭建与工程结构
这个项目的固件我最终保留了基于官方IDF框架的C工程,原因前面说过,官方Zigbee协议栈对休眠、重连、集群绑定这些底层控制更完整。开发环境是在Linux下搭建的,安装过程不复杂:克隆IDF仓库、运行安装依赖脚本、导出一份环境变量,然后就能编译。第一次接触的话直接看官方文档的快速开始章节,跟着走一遍就行,大概一两个小时能搞定。
工程结构上,我把传感器驱动独立成两个目录,SHTC3和SGP30共用一套I2C驱动,PMS颗粒物传感器单独一个UART驱动。Zigbee应用逻辑单独拆成文件,这样后面要换传感器型号或者改上报策略,不用动Zigbee层的代码。所有驱动模块通过一个简单的数据采集接口对外提供数据,方便调试时单独测试。
3.2 传感器驱动:I2C与UART的配合
SHTC3和SGP30都走I2C,挂在同一根I2C总线上即可,设备地址不冲突。物理接线也就四根线:电源、地、SDA、SCL。
固件里我写了一个I2C总线扫描函数,上电时把总线上所有设备地址打印出来。这一步看起来简单,但非常实用,能快速确认两个传感器都被系统识别到,少走很多弯路。
SGP30有个特性必须注意:上电后需要约15秒的预热时间才能输出有效数据。如果开机马上读取,读到的TVOC和CO2eq都是0,或者读数要很久才能稳定。我第一次调试时在这个坑里卡了一个晚上,一直怀疑传感器坏了,后来用逻辑分析仪抓了I2C波形才发现是预热时间不够。所以我在驱动里加了一个启动延时,确认上电15秒后才开始读数据。
PMS颗粒物传感器用的是UART串口,波特率9600。它支持两种模式:主动查询和被动接收。被动模式下传感器一直往外发数据包,功耗高;主动查询模式下发一条指令才回一次数据。我在低功耗设计里选了主动查询方式,每次采样时才发送0x42 0x4D查询指令,传感器返回32字节数据包,包含帧头、校验码和PM1.0、PM2.5、PM10等数据。
颗粒物传感器刚上电时读数有爬坡过程,前几十秒的数据会偏低或者波动明显。我加了一个“启动丢弃”逻辑:上电后前30秒的采样结果直接丢弃,避免把异常值上报给HA。这个逻辑虽然简单,但能显著减少后期在HA里查数据瞬跳的烦恼。
3.3 Zigbee 终端设备的组网与上报逻辑
ESP32C6的Zigbee协议栈里,设备角色选择是第一个关键配置。做空气质量监测仪,正确的角色是终端设备,英文缩写ZED,而不是路由器。路由器和终端设备的区别在于:终端设备大部分时间可以休眠,数据包由父节点代为缓存;路由器则需要一直保持在线,转发网络里其他设备的数据。
选用终端设备能把平均功耗降下来,代价是每次上报前需要先唤醒,发数据,等待父节点确认后,再重新进入休眠。这个时序必须处理好:如果上报后立刻休眠,父节点还没回确认帧,数据就会丢失。我在协议栈回调里加了“发送完成确认”逻辑,确保收到确认帧后才允许进入睡眠。
Zigbee规范里没有一个标准的“空气质量cluster”,所以数据格式需要自己定义。我把温度、湿度、TVOC、CO2eq、PM2.5分别放到一个自定义cluster的不同attribute里,然后通过Zigbee2MQTT的配置做数据映射。具体方式是在Z2M的external converter里声明这些attribute和数据单位,HA实体就能正常识别。
上报逻辑采用定时唤醒方式,默认每5分钟唤醒一次,读取所有传感器数值,通过report方式发给父节点,然后立刻休眠。每次唤醒的总运行时间控制在2秒以内,绝大部分时间都在深睡眠,电池供电版本的平均功耗算下来非常可观。如果想更快响应空气质量突变,可以在后续版本做“事件触发上报”:比如检测到TVOC突发升高就主动唤醒上报,不需要等5分钟。
3.4 关键参数的计算与校准
SGP30的原始数据是TVOC指数和CO2eq浓度,单位分别是ppb和ppm。很多教程直接透传原始值,但如果你用的传感器存放了一段时间,最好先做一次基线校准和湿度补偿。
基线校准的目的是消除电化学传感器的长时漂移。SGP30内部有一个基线算法,会随环境污染物积累缓慢变化,官方建议定期把基线保存到非易失存储,下次上电时加载。我的做法是每天固定时间保存一次基线,而不是关机时触发,因为USB供电的设备关机不可控,关机时保存基线经常来不及。
湿度补偿方面,SGP30的TVOC测量结果受空气湿度影响明显,直接用SHTC3读到的湿度值做补偿计算,效果比裸读取好很多。在厨房、浴室这种高湿环境下,潮湿环境容易让传感器读数偏高,补偿后曲线明显平滑。
PM2.5的计算则是直接从PMS数据包里解析质量浓度字段,单位是μg/m³。注意,激光散射式传感器测出的PM2.5和标准称重法存在系统性差异,尤其在潮湿空气中会偏高。这类低成本传感器普遍存在这个通病,固件里加一个湿度补偿系数能缓解,但无法彻底消除。这也是这类传感器的物理特性决定的,别指望百元级的模块能对标工业级仪器。
4. 组装与部署实录
4.1 硬件装配与走线
我用的是ESP32C6开发板加一块洞洞板,先把传感器模块焊上去,再用杜邦线连接。接线图很简单:SHTC3的SDA和SCL接ESP32C6的I2C引脚,SGP30并联在同一根I2C总线上,PMS传感器接UART的TX和RX。
这步最容易踩的坑是传感器供电电压。SHTC3和SGP30都支持1.8V到3.6V,但市面上很多模块板载了稳压,输入可以接5V也能接3.3V,丝印上标注最好看一眼。我最开始把一块SGP30模块接到了5V排针上,结果读回的I2C数据全是乱码,后来意识到模块板载稳压芯片已经把电压抬到5V,又串联了电平转换才解决。
颗粒物传感器是供电大客户,上电瞬间冲击电流不小。我曾经用一个劣质USB口供电,PMS传感器上电瞬间压降导致整个系统反复重启,后来在颗粒物传感器电源端加了一个100uF电解电容,并且让ESP32C6的一个GPIO控制它的供电,采样时才上电。这个方法同时解决了两个问题:上电瞬间压降和非采样时段的静态功耗。
4.2 Zigbee 配对:让设备自己找网络
配对过程不复杂,但有个细节容易忽略:Zigbee终端设备加入网络时,需要让协调器开放“允许加入”窗口。我在Zigbee2MQTT控制台上点了一下Permit Join按钮,然后给设备上电,按一下配对按键,设备会在空气中扫描所有可用Zigbee信道,选择信号最强且允许加入的网络请求入网。
当时遇到一个很有意思的问题:设备不小心加入到了邻居家的Zigbee网络。因为我家Z2M协调器信道是11,邻居家路由器的信道可能是25,设备扫描时先扫到了信号更强的25,就直接加入过去了。排查方法是通过串口把Zigbee日志打出来,看PAN ID是否和自己家的PAN ID一致。这个问题虽然不常遇到,但如果住在公寓楼里,周围Zigbee网络很多,非常值得注意。
加入成功后,Zigbee2MQTT会自动识别设备类型并生成一个默认名称。我在Z2M里把它改成了living_room_air_quality,然后重启设备确认数据上报正常。
4.3 HA 中的实体配置与自动化
设备接入Zigbee2MQTT后,HA的MQTT集成会自动发现设备并创建实体。我在HA里看到的是这样一组实体:
| 实体 | 数据来源 | 单位 |
|---|---|---|
| temperature | SHTC3 | °C |
| humidity | SHTC3 | %RH |
| tvoc | SGP30 | ppb |
| co2_equivalent | SGP30 | ppm |
| pm1_0 | PMS颗粒物传感器 | μg/m³ |
| pm2_5 | PMS颗粒物传感器 | μg/m³ |
| pm10 | PMS颗粒物传感器 | μg/m³ |
有了这些实体,我做了两个自动化逻辑。第一个是空气质量恶化提醒:当PM2.5在5分钟内从低于35持续上升且超过75,触发手机通知,让我去处理厨房油烟或者开净化器。第二个是温湿度联动:当湿度超过70%且温度低于20度时,自动开启除湿机。这些自动化全部在HA本地执行,Zigbee设备只负责上报数据,云挂了也不会影响判断。
如果你是用的ZHA集成,步骤也差不多:加入协调器后,ZHA会自动发现设备并创建实体,只是在自定义cluster的映射上不如Z2M灵活,可能需要写一点额外的模板转换。
5. 常见问题与排查技巧
5.1 设备频繁离线或重新加入
我遇到最典型的场景是:设备休眠一段时间后,HA里的实体变成不可用,重新上电又好。
这个问题大概率出在父节点选择上。终端设备上电时会通过“重新加入”流程找到父节点,如果父节点本身也是一个经常休眠的路由设备,那它就没法为终端设备缓存数据,导致终端设备失联。我的解决方法是,把HA附近一个常电的Zigbee路由器设置为该终端设备的固定父节点。Zigbee2MQTT里可以配置设备的parent参数,或者通过调整设备位置来强制它选择更稳定的信号源。
5.2 数据突然变成0或跳变
如果SGP30读到的数据全是0,先别怀疑传感器坏了,大概率是预热时间不足。我当时加长预热后问题立刻消失。
如果是颗粒物数据突然跳变,原因可能有几个:传感器风扇堵转、镜头被灰尘覆盖、电源不稳定导致内部激光器闪烁。我的经验是在固件侧加一层数据校验:连续两次采样值突变超过阈值就忽略第二次,仍沿用前一次值,并在日志里打上“数据异常”标记。这个方法不算高科技,但能避免HA自动化因为一个尖峰数据误报。
5.3 Zigbee 距离和穿墙能力不理想
ESP32C6的射频性能确实和专用Zigbee协处理器有差距,如果你把监测仪放在离协调器两堵墙以外,丢包率会明显上升。
我做了两件事缓解:第一,确保天线区域没有被金属外壳包裹。3D打印的外壳如果开孔位置没对准天线,信号强度能差出10dBm以上,这个损失非常可惜。第二,在房屋中部放一个常电的Zigbee插座作为路由节点,让终端设备通过路由多跳通信。Zigbee网状路由是标准功能,只要网络里有常电路由器,距离问题就能迎刃而解。
5.4 Zigbee 与 WiFi 的 2.4GHz 干扰问题
Zigbee和WiFi同在2.4GHz频段,信道划分有重叠,选不好就会相互干扰。我犯过的错误是把WiFi信道选到了6,同时Zigbee信道开到了18,结果数据重传频繁,上报延迟很大。
后来我把Z2M协调器信道固定到和常用WiFi信道错开的频点上,同时在路由器WiFi配置里避开了最拥挤的几个信道,干扰问题基本消失。如果你家周边的WiFi信号密集,可以先扫描一下哪个WiFi信道最空,再反推Zigbee信道,这个步骤真的能省下大量排查时间。
5.5 问题排查速查表
| 症状 | 可能原因 | 处理方式 |
|---|---|---|
| 设备频繁离线 | 终端设备父节点休眠 | 固定一个常电路由器作为父节点 |
| SGP30读数为0 | 预热时间不足 | 上电后延时15秒再读取 |
| PM2.5读数突跳 | 风扇堵转或电源不稳 | 固件侧加数据校验,硬件侧加电容 |
| 上报延迟大 | Zigbee和WiFi信道冲突 | 调整Zigbee信道避开WiFi频段 |
| 信号距离短 | 天线被金属遮挡 | 外壳开孔对准天线位置 |
| 设备加入邻居网络 | PAN ID不匹配 | 串口日志检查PAN ID,重新配对 |
6. 做完之后的几点体会
这个项目走完“感知层、网络层、集成层”整条链路后,我觉得最有价值的不是最终那几个数据,而是对Zigbee生态有了比较完整的理解。从设备端看,Zigbee终端设备的功耗管理比WiFi传感器复杂得多,但一旦调好,可靠性和功耗表现都非常让人满意。从系统集成看,HA加Zigbee2MQTT组合几乎把所有非标传感器的兼容问题都消化掉了,剩下的就是你自己的传感器驱动逻辑。
一个小建议是:如果你想复刻这个项目,别在刚开始纠结传感器精度。先花一版固件把“数据能上HA”这件事打通,然后再慢慢调校准、加补偿。一步步替换比一上来就追求完美更容易落地。我当时就是太早陷入SGP30基线校准的细节,结果主流程拖了两天才跑通,回头想其实可以先跳过。
我目前还在做两件事:一是把固件里的数据上报改成“阈值触发加周期兜底”,让空气污染瞬间的响应更快;二是给设备加一块小屏幕显示实时空气质量等级,这样离开电脑时也能扫一眼房间状态。等这两块都稳定了,我会再更新一篇,把更完整的代码和配置放出来。最后如果遇到具体问题,多翻Zigbee2MQTT的日志,那里面写的信息量远超你的想象,很多看着像“玄学”的问题其实都有明确答案。