做智能井盖这个项目,最初是被一个很笨逼的场景打动的:城市里井盖数量巨大,丢了、被撬、被水冲开、井下气体超标,全靠人工巡查,效率低还容易漏。我们团队用AIR780E Cat.1 4G模块加微信小程序,配合腾讯云MQTT,做了一套轻量级智能井盖监测系统。这个连载想完整记录这套方案,从硬件选型、云平台配置到小程序开发,把每一步的决策原因、踩坑过程和可用代码都摊开讲。如果你正在做类似的物联网项目,或者刚接触Cat.1模块和MQTT,这篇可以当作一份直接能抄作业的参考。
这期连载01先把整个系统的骨架搭起来:为什么选Cat.1而不是NB-IoT,为什么用腾讯云MQTT,传感器和电源怎么设计,小程序端如何展示和告警,最后是联调阶段最常见的几个坑。后面几期会深入AT指令、设备接入的具体代码、小程序完整工程结构。
1. 项目整体架构与技术选型:为什么是AIR780E+小程序+腾讯云MQTT
1.1 场景里的真实痛点:井盖管理到底难在哪
井盖管理不是“盖子盖好就行”这么简单。城市里路灯井、排水井、燃气井、通信井分布在主干道、人行道、绿化带甚至河道边,数量动辄几十万。传统巡检是一组人开着车沿路看,井盖有没有破损、有没有被偷、井内有没有积水,全靠肉眼。暴雨天水位上涨,井下管道倒灌,井盖被顶开,现场如果没人及时发现,就是行人和车辆的安全隐患。
另一个痛点是“事后追溯难”。井盖丢失或被非法开启,往往过了很久才发现,等找到现场,重要线缆可能已经被破坏。如果有一套系统能在井盖被掀开、倾斜、水淹、气体异常时几秒内产生告警,再联动地图定位,就能把问题从“事后处置”变成“事中干预”。
还有一个现实约束:井盖分布极广,不能依赖有线供电和有线网络,绝大多数位置没有稳定的外部电源,也没有Wi-Fi。设备必须靠电池或低功耗策略运行,通信网络必须覆盖广、功耗可控、部署简单。这些约束条件决定了技术选型的大方向。
1.2 Cat.1、AIR780E和腾讯云MQTT各自解决什么问题
第一版方案考虑过多种通信方式,最终选择了LTE Cat.1。Cat.1是基于4G LTE网络的一种低速率物联网通信标准,理论下行速率10Mbps、上行5Mbps,实际跑个几十Kbps就足够用。它最大的优势是直接复用现有4G基站,不用自建网关,也基本不存在“信号死角比NB-IoT多”的尴尬。NB-IoT在很多地下场景信号覆盖确实好,但是速率低、时延偏大,有些地区还受运营商频段和策略影响;LoRa需要自建网关,井盖分布广,网关成本一下子上来了。
AIR780E是合宙推出的一款Cat.1模块,支持LTE Cat.1 bis,尺寸很小,LGA封装,内置丰富的AT指令和低功耗PSM模式。选它有三个理由:一是模块本身功耗控制得好,待机可以到微安级别,配合电池供电能跑很久;二是开发方式灵活,既可以用AT指令快速调通网络和MQTT,也可以用Open方式在模块内部跑自己的逻辑,适合不同水平的开发者;三是社区资料和示例代码多,遇到问题基本能搜到解决方案。
小程序作为用户端,解决了“要不要单独做个App”的问题。井盖巡检人员、市政管理人员、应急调度员分布在多个部门,让他们各自安装一个App,光版本更新就够折腾。微信小程序点开即用,扫码就能看到井盖状态,还能接收告警通知,学习和维护成本低很多。
腾讯云MQTT承担了设备与云端、设备与小程序之间的消息枢纽。设备端通过MQTT协议连上腾讯云IoT Explorer,上报状态和传感器数据;小程序端通过云函数或者WebSocket订阅需要的主题,拿到数据后展示在地图和列表里。选择腾讯云而不是自建MQTT服务器,最直接的原因是省掉了一套要维护的服务器集群,同时腾讯云IoT Explorer本身有设备管理、Topic管理、规则引擎,设备数量上来之后不用自己写管理后台。
2. 硬件核心拆解:AIR780E与传感器、电源的低功耗设计
2.1 AIR780E模块到底要接哪些东西
AIR780E作为主通信模块,硬件上除了模块本体,还需要外围电路配合。供电是最容易出问题的一环。模块的VBAT工作电压范围一般在3.4V到4.2V之间,典型值是3.8V,最大峰值电流可能到2A级别。这个特性决定了不能用普通LDO直接供电,LDO在大压差下发热严重且带不动瞬时大电流。我实测下来,用支持2A以上输出的DC-DC降压电路,或者直接锂电池供电,再配合大容量钽电容和瓷片电容做滤波,是最稳定的方案。
SIM卡电路虽然看起来简单,但接触不良、ESD防护不到位会导致设备频繁离线。建议使用推入式Micro SIM卡座或Nano SIM卡座,卡座旁边加ESD防护器件,数据线走线尽量短。AIR780E还支持eSIM方案,量产时可以减少卡座成本,但调试阶段还是实体SIM卡方便。
天线部分不能省。Cat.1模块需要LTE主天线,如果带定位功能还需要GNSS天线。天线的位置很讲究,装在井盖内部金属腔体里会严重屏蔽信号,最好把天线延伸到井盖侧面的非金属区域,或者使用外置天线引出到井口。实测中,天线贴在金属井盖内部时RSRP能差20dBm以上,直接导致联网失败。
模块与MCU之间通过UART串口通信,标准做法是AT指令。如果需要直接驱动传感器,也可以用模块的GPIO和ADC,不过为了后续扩展,我习惯在AIR780E外面再接一颗低功耗MCU(比如STM32L系列或合宙自家的低功耗方案),把传感器采集、数据处理、状态判断放在MCU里,AIR780E只负责网络通信和MQTT上报。
2.2 井盖状态监测到底需要哪些传感器
智能井盖不是只有一个“开/关”状态。实际场景里,我们最关心的几类事件包括:井盖被非法开启或移位、井内水位过高、井下可燃或有毒气体超标、井盖发生小角度倾斜但未完全掀开。
对于“井盖被打开”这种最核心的事件,最简单可靠的方案是干簧管加磁铁。把磁铁装在井盖边缘,干簧管装在井座,井盖闭合时磁铁吸合干簧管,井盖被掀开时干簧管断开,产生中断信号唤醒设备上报。这种方案零功耗、抗干扰强,成本很低。如果还想检测“倾斜但没有完全打开”的中间状态,可以再加一颗三轴加速度计,比如MPU6050或更低功耗的LIS3DH。通过计算重力加速度在三轴上的投影,判断井盖是否倾斜,角度阈值可以按场景标定。
水浸检测用的是电极式传感器,两根电极暴露在井内底部,水位上升接触到电极后电阻变化,MCU通过ADC采集到电压跳变。这里要注意电极的防腐蚀处理,长期泡在污水里,普通铜电极几个月就会氧化失效,建议使用不锈钢电极并定期校准。
气体检测要按井的类型区分。燃气井和污水井对甲烷、硫化氢比较敏感,可选催化燃烧式或电化学式传感器。这类传感器的通病是功耗高,不能一直通电,我通常用MOS管控制供电,只在采样时刻开启,采样完立即断电,能省掉大量功耗。
还有一个传感器容易被忽略:电池电压检测。通过MCU的ADC分压采集电池电压,每次上报时把电量一起发到云端,这样后台能看到哪些设备快没电了,安排人员集中更换电池,而不是等设备离线才被动响应。
2.3 电池寿命怎么估算,低功耗策略怎么落地
井盖埋在路边,不能三天两头换电池,所以功耗是硬指标。AIR780E有PSM(Power Saving Mode)模式,进入PSM后模块几乎不耗电,但代价是网络连接会断开,需要唤醒后重新附着。我用的是“事件唤醒+周期心跳”双策略:平时MCU和模块都睡死,干簧管或者加速度计发生中断时立刻唤醒,上报一条告警;同时每24小时或者12小时主动唤醒一次,上报心跳和电池电量,让云端知道设备还活着。
传感器供电用MOS管控制,采样前才打开。以水浸传感器为例,每次采样通电500ms,采样完断电,静态功耗可以忽略不计。MCU选用低功耗型号,休眠时电流控制在10uA以内。整机实测下来,静态电流大约在20uA左右,一次完整上报(包含入网、建链、发数据)平均消耗约30mAh,如果按一天一次心跳加偶尔告警来算,一块4000mAh的锂电池理论上可以支撑数月到一年以上,具体要看告警频率。
不过这里有个坑要提醒:入网和建立MQTT连接是耗电大户,一次入网可能消耗几十毫安时的电量。如果设备频繁掉线重连,电池寿命会急剧缩短。所以不要盲目缩短心跳间隔,也不要让设备在信号差的地方反复重试。我后来给代码加了一个“失败退避”逻辑:连续N次入网失败后,延长下次尝试间隔到10分钟、30分钟、1小时,避免在弱信号区死循环耗电。
3. 云端链路设计:腾讯云MQTT与数据协议的完整方案
3.1 为什么直接选腾讯云IoT Explorer而不是自建MQTT
物联网项目一旦设备量超过几十台,自建MQTT的成本和维护压力会突然变大。要处理的不仅有服务器本身,还有证书管理、设备鉴权、Topic权限控制、离线消息存储、规则引擎等。腾讯云IoT Explorer把这些能力以产品化方式提供,设备端只要拿到三元组(ProductID、DeviceName、DeviceSecret),用MQTT协议接入就能完成认证和通信。
另一个加分项是它的腾讯生态。小程序云开发、云函数和移动端SDK都有现成对接,省去了“云平台只提供原始MQTT,上层要自己写一堆胶水代码”的麻烦。而且腾讯云IoT Explorer有免费额度,对于原型验证和中小规模项目非常友好,具体免费策略以官网最新说明为准。
3.2 Topic怎么划分,数据格式怎么定
Topic设计直接决定后续的扩展性。我的做法是分三类:上行数据、下行命令、系统事件。
设备上报状态数据,用一个专门的主题,所有传感器数据打包成JSON一次上报,而不是一个传感器一个主题。这样减少网络交互次数,也方便云端规则引擎用一条规则处理。数据格式大概是这样的:
{ "deviceId": "cover_001", "timestamp": 1717390800, "type": "report", "position": { "lng": 116.397, "lat": 39.908 }, "status": { "lidOpen": false, "tiltAngle": 3.2, "waterLevel": 0, "gasPpm": 12 }, "battery": 86, "signal": 22 }deviceId是业务编号,便于后台显示;position字段由设备端或云端根据井盖编号补充;status里每个字段对应一组传感器的状态。这里有一个细节:字段命名尽量统一用驼峰,小程序端拿到之后不用做太多转换。如果是告警事件,比如井盖被打开,type字段改成alert,同时云端规则引擎会触发告警推送。
下行控制用另一个主题,比如远程控制设备重启、校准传感器阈值。控制指令下发后,设备端返回执行结果,格式类似:
{ "deviceId": "cover_001", "type": "response", "cmd": "restart", "result": "ok" }系统事件主要指设备上下线通知。腾讯云IoT Explorer本身就支持上下线Topic,后台可以订阅这些事件,当设备离线超过阈值时自动生成工单。这部分我建议不要自己造轮子,直接使用平台提供的系统Topic。
3.3 设备认证、心跳保活和离线判断
设备认证使用腾讯云IoT Explorer的密钥认证。设备端需要计算的MQTT三要素是ClientId、Username、Password,其中Password是用DeviceSecret对特定字符串做HMAC-SHA256后得到的。直接用官方SDK或者合宙的AT指令固件,它会自动计算,不需要自己手动实现加密算法。但如果你用的是AT指令手动连接MQTT,就需要在代码或服务端实现一遍签名算法,注意密钥不要硬编码明文,尽量放在安全存储区域。
心跳保活对井盖这种低功耗设备尤其重要。MQTT本身的KeepAlive机制是在连接层发PINGREQ,腾讯云一般要求KeepAlive的间隔在30到120秒之间。但井盖设备平时处于PSM省电状态,不可能每30秒发一次心跳。所以我的方案是:设备在线时用MQTT KeepAlive,进入PSM前主动发送一条offline准备消息,同时让平台基于“最后一次心跳”时间判断离线。简单说,不要只依赖协议层心跳,要在业务层设计“状态过期时间”。后台如果超过15分钟没有收到某设备任何消息,就判定离线。
离线判断的阈值要结合上报策略调整。如果设备设计成30分钟上报一次心跳,后端15分钟判定就会误报。我最终用的是“上报周期乘2加5分钟”的动态阈值,既不会漏报,也不会频繁误报。
4. 小程序端开发:从地图展示到告警中心的完整实现
4.1 小程序直连MQTT还是通过API转发
很多人一上来就想着小程序直接通过MQTT over WebSocket连腾讯云,这样消息可以实时到手机。想法没错,但实际会碰到几个麻烦:一是设备密钥不应该下发到小程序端,会泄露;二是小程序在弱网环境下长连接不稳定,一旦断线重连逻辑没写好,用户会一直看到“连接中”;三是微信小程序对网络连接有并发限制,长连接数量太多会影响其他请求。
所以我的架构是“设备上报到云端,云端存库,小程序通过云函数/API读取,同时监听告警事件”。具体路径是:AIR780E通过MQTT上报数据,腾讯云IoT Explorer规则引擎把数据写入云开发数据库,小程序通过云函数查询数据。需要实时告警时,小程序端订阅云开发数据库的watch事件,或者用定时器轮询最近的告警记录。这个方案牺牲了一点点实时性,但换来的是稳定、安全、易维护。
4.2 用uni-app开发小程序,导航栏和页面结构怎么设计
开发小程序我选了uni-app,用HBuilderX创建项目。选uni-app的好处是以后如果想出App端,代码可以复用很大一部分,不必从头再来。整个小程序的页面分成四块:首页地图总览、井盖列表、告警中心、我的。
首页地图是核心。用腾讯地图小程序SDK,把井盖位置渲染成自定义Marker,正常状态用绿色,告警状态用红色。点击Marker跳转到井盖详情页。列表页则按区域、状态筛选项展示井盖,支持搜索井盖编号。告警中心按时间倒序展示所有告警,未读的加红点提示。
页面标题这里有个小程序开发的常见细节:不同井盖的详情页标题如果都叫“井盖详情”,用户根本分不清。我在井盖详情页onLoad里拿到设备编号后,用uni.setNavigationBarTitle动态修改标题,比如“井盖详情:市政路12号井”,这样分享到微信群里时,别人一眼就能看出是哪个井盖。如果你用的是微信原生小程序,对应的接口是wx.setNavigationBarTitle,效果一样。
顶部导航栏高度的问题也值得一提。不同机型状态栏高度不一样,有些还带灵动岛,如果自定义导航栏,需要获取系统信息里的safe area,再把胶囊按钮的位置考虑进去,否则自定义按钮会被刘海遮挡。我的做法是直接保留微信原生导航栏,不再自定义,省掉大量适配工作。只有在需要顶部放自定义金额条或扫描入口的页面才自定义,并且用uni.getSystemInfoSync()拿状态栏高度去做占位。
4.3 告警推送和通知逻辑怎么做
告警通知设计了两个渠道。第一是微信“订阅消息”,用户主动订阅一次,平台就能推送一次消息。这个接口的限制是一次订阅只能推送一条,所以我在用户点击订阅时,默认让他订阅“告警通知”模板,每次有新告警,通过云函数调用subscribeMessage.send发送。对于市政管理人员,这个通知够用了;如果需要多次通知,就得引导用户每次收到后再次订阅。
第二是企业微信群机器人。井盖告警很多情况下需要在工作群里同步,我在云函数里加上webhook推送,告警产生时往指定的企业微信群发一条带地图链接的消息。这个对现场调度特别管用,因为值班人员通常都盯着工作群。
小程序端还需要处理“告警风暴”的问题。如果某个井盖因为传感器误报,一分钟发10条告警,用户会被打扰疯掉。我在云端加了一层合并逻辑:同一个设备在5分钟内只产生一条告警,后续同类告警只累加次数,不重复推送。设备恢复后再发送“已恢复”状态通知。这需要在规则引擎或云函数里做状态机判断,不要指望设备端来解决。
5. 联调过程中踩过的坑与排查方法实录
5.1 AIR780E注网和数据上报失败,先查这4个位置
联调第一步是让模块能正常入网。用串口把AIR780E连接到电脑,发送AT指令,最常用的是:
AT+CGATT? // 查询是否附着4G网络 AT+COPS? // 查询当前运营商 AT+CEREG? // 查询注册状态,0或1表示未注册,5表示已注册 AT+CSQ // 查询信号强度,值越大越好如果AT+CGATT返回0,先看SIM卡是否插好、是否有欠费停机。我遇到过一个很隐蔽的问题:SIM卡没有开通物联网卡的专用APN,导致附着失败。合宙模块一般默认自动选APN,但如果失败,可以手动配置:
AT+CGDCONT=1,"IP","CMNBIOT" // 以联通物联网卡为例接入腾讯云MQTT失败时,首先要检查MQTT三元组签名是否和后台一致。一种常见情况是设备端用了Wi-Fi网络的时钟和当前时间不匹配,导致HMAC签名的有效期校验失败。AIR780E内部没有RTC电池,断电后时间复位,必须在每次上报前通过NTP或基站时间校准。这个问题排查了整整一晚上,最后发现模块时间停在1970年,签名当然验不过。
如果模块频繁重启,多半是供电不够。你可以在VBAT引脚示波器抓波形,正常供电应该在掉电时有平滑下降曲线,而瞬时跌落会有明显毛刺。换大容量电容或调整DC-DC输出电压能解决。还有一点,SIM卡座和数据线尽量不要飞线连接,我用杜邦线连SIM卡时,信号稍弱就掉线,改成PCB直焊后稳定很多。
5.2 小程序连接后端和MQTT的典型异常
小程序端最常见的坑出现在“不校验合法域名”和“实际真机请求”之间的差异。开发工具里勾选“不校验合法域名”,能请求到接口,但真机一点就报“request:fail url not in domain list”。解决办法是在微信公众平台后台配置request合法域名,必须是HTTPS,且域名备案过。如果你的小程序只是体验版,也要在后台临时配置,不然真机照样访问不了。
第二个高频问题是订阅消息授权失败。在开发者工具里点击订阅能弹窗,但真机上如果用户之前拒绝过授权,再次调用subscribeMessage会直接走fail回调,不会弹窗。接口文档没有明确说这个逻辑,我是踩了坑才发现的。解决办法是在页面里放一个引导按钮,如果用户拒绝过,就跳转到“设置”页,让他手动打开订阅消息权限。
如果你坚持在小程序端直连腾讯云MQTT over WebSocket,要注意不能用http明文,必须用wss,并且“socket合法域名”也需要配置。小程序从基础库2.x开始,WebSocket连接数量也有限制,多个页面同时连接会被断开。这再次验证了我之前说的:能走后端API就走后端API,长连接这个口子尽量少开。
5.3 调试工具怎么配合用,才能快速定位问题
我调试AIR780E用的是合宙的串口调试软件,输出AT指令和模块日志。只要开了Modem日志,模块内部的注册流程、网络附着状态、TCP连接情况都会打出来,定位问题非常快。云端的排查用腾讯云IoT Explorer控制台,左侧在线调试里有设备日志,能查到设备每一次上行消息,如果设备上报了但控制台没记录,那问题一定在设备端发送环节。
小程序端我用Charles做代理抓包,查看云函数的请求和返回。只要装上Charles的HTTPS证书,就能看到小程序的网络请求体。注意,真机调试时需要在手机上配置代理,并且确保手机和电脑在同一局域网。抓包不是用来做违规事情,而是看接口返回和请求参数是否正常,这个方法在正式开发调试里非常常见。
联调阶段最容易出现“设备上行成功,小程序看不到数据”的割裂情况。我的排查顺序是:设备日志确认MQTT发送成功,云端控制台确认数据进入规则引擎,数据库确认数据已经写入,云函数确认返回结果,最后小程序确认渲染逻辑正确。逐层排查,比在一堆日志里瞎猜高效得多。
6. 连载01收尾:目前跑通的效果和下一期预告
6.1 当前原型系统跑通了什么
到这期结束,我们已经有一个能跑的原型:井盖设备通过AIR780E上报状态到腾讯云IoT Explorer,规则引擎把数据写入云开发数据库,小程序展示井盖列表、地图和告警中心。测试环境下,设备模拟开盖事件,从传感器动作到小程序告警中心出现记录,实测耗时为1到2秒。调度群收到企业微信推送也在同一时间范围。
电池方面,用4000mAh锂电池供电的实验板,在一天3次心跳、偶尔模拟告警的情况下,运行两周电压下降约6毫伏,按这个趋势估算,使用大半年问题不大。当然这还只是原型数据,真正量产还要做高低温、振动、防水等可靠性测试。
6.2 下一期连载准备深入什么
下一期(连载02)会直接把AIR780E的AT指令流程和腾讯云MQTT的完整接入代码贴出来,包括设备端状态机的设计思路、PSM唤醒和心跳上报的具体代码写法。如果你正在卡在“模块能上网但MQTT连不上”或“上报了几条数据就再也连不上”这两类问题上,下一期应该能帮你直接解决。
另外我打算在后续连载里补充一个真实部署案例:市区一条主干道装了30个井盖设备的试运行数据,包括信号覆盖统计、报警准确率、电池消耗趋势,以及那些在大雨天被水淹没后依然能上报的设备表现。物联网项目只有经过真实环境反复捶打,才算真正“能用”。
最后分享一个我在实际项目里最深的体会:不要一开始就追求把所有功能做完,先把“一个井盖异常打开后,从设备到小程序到通知群,整条链路能不能跑通”跑出来,再逐步加智能化。链路通了,后面所有优化才有意义。这个项目当前版本最大的功臣反而是那些不起眼的地方:干簧管加磁铁的开关检测、失败退避联网策略、告警合并逻辑。这些细节看着简单,但对系统的稳定性影响最大,也最值得花时间去打磨。