ARTICLE DETAIL

资讯详情

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

LoRa与LoRaWAN实战:空气监测设备接入TTN全攻略

LoRa与LoRaWAN实战:空气监测设备接入TTN全攻略 去年我把手上的空气质量监测设备从 Wi-Fi 方案整个切到了 LoRa并且成功接入了 The Things NetworkTTN这个公共 LoRaWAN 网络。整个过程踩了不少坑也总结出一套可以复用的思路。如果你也在做环境监测类的物联网项目或者正想把设备从 Wi-Fi 路由器里解放出来这篇记录应该能给你一点参考。我会把改造动机、硬件选型、入网配置、数据接收、问题排查全链路讲清楚尽量说人话能直接抄作业的地方绝不藏着掖着。1. 改造背景为什么放着 Wi-Fi 不用非要去搞 LoRa1.1 Wi-Fi 方案的三个硬伤Atmosphere IoT 最初用的是 ESP32 自带 Wi-Fi设备通过家里或办公室的路由器联网把温湿度、PM2.5、CO2 浓度这些数据上传到平台。这个方案在样机阶段没有任何问题因为样机就放在开发台上路由器在五米范围内信号满格数据更新速度也很快。但当设备真的部署出去问题很快就暴露了。首先是覆盖问题。Wi-Fi 穿墙能力有限尤其是 2.4GHz 频段在承重墙、金属门、玻璃幕墙面前衰减非常明显。我这边有个场景是三层办公楼设备放在一楼地下车库入口附近路由器在二楼直线距离没多远但数据就是传不回来延迟从几秒变成几分钟最后干脆掉线。第二个问题是功耗。ESP32 的 Wi-Fi 保持连接时电流长期在 80mA 到 120mA 之间如果用电池供电基本上几天就得充一次。而空气质量监测设备本身就要求 7x24 小时无人值守运行频繁换电池或者拖一个电源适配器都是不可接受的。第三个问题是依赖路由器。路由器一重启、一断电、一升级固件所有设备全部失联整个监测系统就瘫痪了。你可能要说蜂窝网络4G/NB-IoT不是也能解决这些问题吗确实NB-IoT 覆盖和功耗都很好但问题在于它依赖运营商网络有些地方 NB-IoT 信号弱而且要插卡、要选套餐、要付流量费终端成本也高。这个项目希望做到零通信成本、自主可控所以最终综合考虑LoRa 是更合适的选择。1.2 LoRa 和 LoRaWAN 到底是什么关系很多刚接触 LoRa 的朋友会把 LoRa 和 LoRaWAN 混为一谈这里先花两分钟把概念理清。LoRa 是 Semtech 公司推出的一种 chirp 扩频调制技术解决的是物理层怎么把信号传得更远、更抗干扰的问题。它跟传统的 FSK 调制不太一样LoRa 通过线性调频扩频把数据在更宽的频带上铺开从而换取了极高的接收灵敏度。同样 20dBm 发射功率FSK 可能传几百米LoRa 在空旷环境能传几公里甚至十几公里这就是扩频带来的增益。LoRaWAN 则是基于 LoRa 物理层的一套完整网络协议。它定义了设备如何入网、如何分配信道、如何加密数据、如何与网关通信。简单类比一下LoRa 相当于一条能传很远的路LoRaWAN 是这条路上的交规和管理系统。没有 LoRaWANLoRa 只是两个点之间的无线串口有了 LoRaWAN才形成了设备、网关、网络服务器、应用服务器能够协同工作的完整体系。Atmosphere IoT 这次升级实际上是同时引入了这两层硬件上用 LoRa 模组做射频收发软件协议栈用 LoRaWAN 规范网络平台接入 TTN。1.3 选择 The Things Network 的核心理由The Things Network 是一个由全球社区驱动的开源 LoRaWAN 网络。它不像运营商网络那样由某个公司统一建设而是由全世界无数个第三方网关Gateway拼接成一张开放网络。只要你的设备所在位置附近有 TTN 网关就能入网并上传数据不需要自己去买基站、建基站。在项目选型时TTN 有几点让我很有好感。第一它完全开放不锁定厂商任何符合 LoRaWAN 标准的设备都能接入。第二数据通路透明TTN 只负责接收和转发不会劫持你的数据去做分析设备上报的数据最终通过 MQTT 直接推到你自己的服务器。第三零成本LoRa 的免授权频段本身不需要缴费TTN 网络服务也不收费。当然公共网络的覆盖有一定不确定性这就要在部署前先查看你所在区域的网关分布。不过像 TTN 这样的社区网络在国内外很多城市都有热心玩家部署的网关覆盖密度比想象中好。2. 硬件选型与电路改造的关键细节2.1 主控保留 ESP32外挂 SX126x LoRa 模组改造时我没有更换主控还是用 ESP32 继续跑传感器逻辑和应用层代码只在其基础上外挂了一个 LoRa 模组。这个决定的出发点很现实原有所有传感器驱动、数据采集逻辑、显示逻辑全部复用只是把网络出口从 Wi-Fi 换成 LoRa 模组。代码改动的范围被压缩到最小出问题的概率也低很多。LoRa 模组我选了基于 Semtech SX126x 芯片的 E22-400M30S。SX126x 是 SX127x 的下一代接收灵敏度提升了大概 3dB发射电流也优化了而且还支持更宽的频率范围。模组本身集成了 PA 放大电路最高可以把发射功率推到 22dBm在城市楼宇环境里穿两三堵墙完全够用。选模组时有一个需要注意的点频段版本必须跟当地免授权频段匹配。国内使用的是 470-510MHz欧洲是 863-870MHz北美是 902-928MHz。不同地区的 LoRa 模组在硬件上就是按频段区分的选错了要么入不了网要么干扰其他业务这点大家在采购时必须确认清楚。2.2 传感器数据读取与接口设计Atmosphere IoT 的传感器部分包括温湿度、PM2.5、CO2 这三类核心数据。温湿度用的是 Sensirion SHT30I2C 接口大概两行代码就能读出来PM2.5 用的是攀藤 PMS5003 激光传感器UART 接口主动上报数据帧解析起来也很简单CO2 用的是 SenseAir S8同样走 UART 或者 PWM测的是真正的 NDIR 非色散红外原理精度比电化学传感器靠谱得多。LoRa 改造对传感器这一层的影响几乎为零因为传感器数据读取和通信方式是解耦的。唯一要注意的是LoRa 的数据包很小不能像 Wi-Fi 那样直接用 JSON 上报。所以在代码层面我加了一层传感器数据打包模块把所有测量值转成紧凑的二进制格式再交给 LoRaWAN 协议栈发送。这部分我在第 3 章会详细展开。2.3 低功耗设计让电池真能用五个月LoRa 虽然以低功耗著称但很多人的实际续航跟预期差得很远问题往往出在峰值功耗和平均功耗的关系上。SX126x 在 22dBm 发射功率下瞬时电流大约 90mA 到 120mA单次发送时间虽然只有几十到几百毫秒但如果发送频率控制不好平均功耗一样降不下来。我的策略是上报间隔设为 5 分钟每次发送前先唤醒 LoRa 模组、初始化射频然后发送一个约 12 到 16 字节的 payload发送完成后立刻让模组进入睡眠模式。同时给模组的电源加了一个 MOS 管开关平时整个射频部分完全断电只有 ESP32 在睡眠。这样整机待机电流可以压到 50μA 以下加上传感器定时采样的功耗平均电流大概在 8mA 左右。电池方面我用了两节 18650 锂电池并联实测容量约 3000mAh。按 8mA 平均电流算理论续航是 375 小时也就是大约 15 天。等等这里我重新算一下——如果只靠这个功耗3000mAh 除以 8mA 确实是 375 小时不到 16 天。但我前面提到的 8mA 是包含传感器常开的情况。实际项目中PM2.5 激光传感器不是一直开着的我让它每 30 秒启动一次、转 10 秒后关闭这样 PM2.5 传感器的平均电流能从 100mA 降到 33mA 左右。整套系统的真实平均电流在 40mA 到 50mA 之间因为 PM2.5 是耗电大户两节 18650 电池大约能撑 60 到 75 小时——这个续航并不理想。后来我把上报周期调到 10 分钟同时把 PM2.5 采样频率降到每分钟一次整机平均电流压到 12mA 左右3000mAh 的电池组能跑 250 到 300 小时也就是 10 到 12 天。看到这里你应该明白了LoRa 低功耗不是无条件的必须和传感器策略、上报频率一起整体设计。空气质量监测这类场景对时效性要求不高10 分钟一个数据点完全够用。想要更长续航还可以选择 18650 容量更大的版本或者用一次性锂电池组续航能到半年以上非常适合无人值守部署。3. 接入 The Things Network 的完整实操流程3.1 在 TTN Console 创建应用与注册设备接入 TTN 的第一步是在 TTN Console 里创建一个 Application。在网页端登录后点击Applications然后选择Register application。填写应用 ID 后系统会生成对应的 Application ID 和 App EUI。这个 App EUI 从 8 字节的十六进制字符串可以看到它是应用层面的标识。创建好 Application 后在End devices选项卡下Register end device按照提示填入设备的相关参数。TTN 支持手动输入和自动生成两种方式。我建议让 TTN 帮忙自动生成 DevEUI 和 AppKey这样避免了随机数熵不足导致的密钥安全问题。生成的这三个参数要用好——它们是设备入网的唯一凭证。TTN 界面上会有Copy按钮点击即可复制。3.2 OTAA 激活参数烧录与入网原理设备激活方式我推荐 OTAAOver-The-Air Activation而不是 ABPActivation By Personalization。OTAA 每次入网都会动态生成会话密钥安全性更好而且设备移动、重新入网时不需要重新烧录参数。ABP 是静态密钥入网速度快但密钥一旦泄露就永远暴露了。OTAA 需要三个参数DevEUI、AppEUITTN 里显示为 JoinEUI、AppKey。其中 DevEUI 是设备唯一标识AppEUI 标识应用AppKey 是根密钥。设备上电后发送 Join Request网关转发到 TTN 网络服务器服务器校验通过后返回 Join Accept并分发两个会话密钥NwkSKey 和 AppSKey。NwkSKey 用于网络层的消息完整性校验AppSKey 用于应用层数据的加解密。整个握手过程叫三步入网整个流程看起来简单但每一步都有坑。在 ESP32 代码里我用的是 MCCI LoRaWAN 协议栈。配置核心就是把这几个参数写进去static const u1_t PROGMEM DEVEUI[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; static const u1_t PROGMEM APPEUI[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const u1_t PROGMEM APPKEY[16] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 }; void setup_lora(void) { lmh_setDevEui(DEVEUI); lmh_setAppEui(APPEUI); lmh_setAppKey(APPKEY); lmh_setFrequencyPlan(FP_470_510); lmh_setOTAA(1); }这里有一个特别容易踩的坑字节序。TTN Console 上显示的 DevEUI 是 MSB 优先格式也就是最左边的字节是最先发送的。但很多 LoRaWAN 协议栈内部是按 LSB 优先来处理的所以你在代码里填的数组顺序可能正好要反过来。我一开始没注意这个问题结果设备 Forever 卡在入网阶段TTN 后台完全看不到 join 事件。后来在协议栈文档里看到一行注释TTN uses little endian才恍然大悟把数组顺序反转后立刻入网成功。3.3 紧凑的 Payload 格式设计与打包LoRa 的 payload 空间非常小单包上限取决于扩频因子和带宽配置。以 SF9、125kHz 带宽为例单包最多能传 100 多字节但实际上为了可靠性我一般只用 12 到 16 字节。所以绝对不能用 JSON 明文传输必须设计一套紧凑的二进制协议。我采用的是 TLVType-Length-Value格式。每个数据项由三个部分组成1 字节类型Type、1 字节长度Length、实际数据字节Value。比如温度数据项Type 0x01表示温度 Length 0x02数据长度 2 字节 Value 0x1F 0x40大端整数 8000表示 80.00℃数值除以 100依次把温度、湿度、CO2 浓度、PM2.5、电池电压、RSSI、状态位这些数据打包好最后组成一个 14 字节左右的 payload。这样既保证了数据完整性也最大程度节约空口时间。打包代码结构如下void pack_sensor_payload(uint8_t *buf, uint8_t *len) { uint8_t idx 0; // 温度单位 0.01℃ int16_t temp read_temp_x100(); buf[idx] 0x01; buf[idx] 0x02; buf[idx] (temp 8) 0xFF; buf[idx] temp 0xFF; // 湿度单位 0.01% int16_t hum read_hum_x100(); buf[idx] 0x02; buf[idx] 0x02; buf[idx] (hum 8) 0xFF; buf[idx] hum 0xFF; // CO2单位 ppm uint16_t co2 read_co2_ppm(); buf[idx] 0x03; buf[idx] 0x02; buf[idx] (co2 8) 0xFF; buf[idx] co2 0xFF; // PM2.5单位 μg/m3 uint16_t pm25 read_pm25(); buf[idx] 0x04; buf[idx] 0x02; buf[idx] (pm25 8) 0xFF; buf[idx] pm25 0xFF; *len idx; }TTN 只是透传 payload它不会解析里面的内容所以格式完全由自己定。只要保证设备端打包和后端解析逻辑一致就行。上行数据默认是加密的TTN 转发到你后端时已经解密为明文不用担心中间环节泄露。4. 数据接收链路搭建与平台对接4.1 用 MQTT 实时接收设备数据设备成功入网上报后数据到了 TTN 网络服务器接下来关键的一步是把数据取到自己的后端。TTN 提供了多种集成方式包括 MQTT、HTTP Webhooks、Storage Integration 等最常用的是 MQTT。在 TTN Console 的 Integrations 页面添加 MQTT 集成后系统会生成 MQTT 地址、用户名和密码。TLS 端口是 8883这里推荐直接启用 TLS因为 MQTT 用户名密码虽然有校验但明文传输在任何场景下都不够安全。配好之后用任意 MQTT 客户端订阅以下主题即可接收设备上行数据mosquitto_sub -h eu1.cloud.thethings.network -p 8883 --cafile ca.pem -u app-namettn -P your-password -t v3/app-namettn/devices//up -vTTN 上行主题的通配符是devices//up其中匹配任意设备。每收到一条消息就是该设备上报的一个数据帧。消息体本身是 JSON 格式里面包含 device_id、received_at、uplink_message 等字段其中uplink_message.frm_payload是一个 base64 编码的字符串decode 之后就是我在设备端打包的那串二进制数据。4.2 后端解析、入库与可视化我这边后端用 Python 写了一个 MQTT 订阅服务收到 TTN 推送的 JSON 后先取出frm_payload字段做 base64 解码再按 TLV 协议解析成具体的传感器数值。解析代码很简单import base64, json def parse_payload(payload_b64): raw base64.b64decode(payload_b64) result {} i 0 while i len(raw): typ raw[i] length raw[i 1] value_bytes raw[i 2 : i 2 length] value int.from_bytes(value_bytes, big) result[typ] value i 2 length return result解析完成后按 type 字段对应到具体指标0x01 温度、0x02 湿度、0x03 CO2、0x04 PM2.5。存储我用了 InfluxDB这是一款时序数据库专门用来存监控数据。每条记录用 device_eui 作为 tag各传感器值作为 field这样在 Grafana 里就能按设备、按时间范围直接绘制曲线。整个可视化链路是LoRa 设备 - TTN 网关 - TTN 网络服务器 - MQTT - Python 消费者 - InfluxDB - Grafana。中间任何一环断了数据就不会出现在最终的仪表盘上。所以调试时要有一个分层验证的意识先看设备有没有上报再看 TTN Console 有没有 uplink 记录然后订阅 MQTT 看有没有消息最后看数据库里的新记录。一层一层排查基本十分钟以内能定位问题。4.3 实际运行效果与数据表现完成全链路搭建后我实际运行了两个月。设备部署在一栋三层办公楼里一楼、二楼、三楼各放一台附近有一个社区 TTN 网关直线距离约 1.5 公里中间有不少建筑物遮挡。这段时间的统计结果是每台设备每天上报 144 条10 分钟间隔平均丢包率在 3% 以内没有出现连续丢包的情况。信号强度大约在 -115dBm 到 -105dBm 之间这个水平对 LoRa 来说属于正常范围。有意思的是CO2 数据和人员活动有明显的相关性。工作日上午九点以后三楼的 CO2 浓度会稳步上升到下午三点左右达到峰值明显高于二楼和一楼的读数。这类数据通过 LoRa 传回来之后可以在 Grafana 上实时展示也能用来联动新风系统这就是空气质量监测物联网一个非常典型的落地场景。5. 碰到的问题与排查实录5.1 入网失败先查频段、再查参数、最后查网关入网失败是我在整个项目中遇到最多的问题也是 LoRaWAN 新手最容易卡住的地方。症状表现为设备一直发 Join Request但 TTN Console 后台看不到任何 join 事件。排查顺序应该是这样第一步确认频段配置。模块是 470M 还是 868M代码里 Frequency Plan 是否匹配这两者任何一个不匹配设备发出的射频信号就不在网关监听的频点上网关根本收不到TTN 自然没有任何痕迹。第二步检查 DevEUI/AppEUI/AppKey 三个参数重点看字节序。TTN 显示的是 MSB 格式很多协议栈要求 LSB必须逐字节核对。第三步确认你所在位置真的有网关覆盖。TTN 的官网覆盖地图可以查但地图上的网关可能离线或覆盖不到你那里。我就碰到过一次地图上显示附近有网关但实际是停机状态设备自然连不上。5.2 丢包率突然升高来自环境的干扰入网成功后长期运行最怕的问题就是丢包。设备部署固定信号强度变化不大但某一天开始丢包率从 2% 涨到 20%。这种问题往往不是设备故障而是环境变化导致。我遇到的一个典型案例是二楼办公室的设备某天之后丢包率突然飙升。我远程检查信号强度发现 RSSI 没有明显变化但 RSSI 并不能完全反映信噪比情况。后来派同事到现场看发现办公室窗户外面新装了一扇金属防盗窗正好挡在天线朝向网关的方向。金属对无线电信号的衰减非常大尤其对低频段信号这种反射和吸收效应很致命。如果你部署的设备也有类似情况建议在固定设备前先做一个现场勘察看看朝向网关的方向有没有金属障碍物比如防盗窗、铝合金门框、玻璃幕墙里的金属网格。天线的位置也尽量调整到开阔方向不要贴着墙面或者金属管道。5.3 电池续航不如预期平均功耗算错了很多人觉得 LoRa 本身就低功耗选好模块就完事了。但实际上整机的功耗由传感器 主控 射频三部分共同决定任何一块没有优化好续航都会打折扣。我一开始的 PM2.5 传感器是常开的PMS5003 工作时电流标称 100mA 左右虽然实际读数是间歇性的但硬件上它一直在运转。后来我给它加了 MOS 管供电开关每 60 秒通电 10 秒读取数据然后彻底断电。仅这一项就把 PM2.5 传感器的平均电流从 100mA 降到了 16mA 左右。LoRa 模组也做了同样的处理平时掉电发送前上电。这样整套系统在 10 分钟上报间隔下的平均电流从最初的 40mA 降到了 10mA 左右电池续航延长了三四倍。还有一个细节就是发射功率。SX126x 默认的 22dBm 发射功率在信号强的时候毫无必要。实测信号在 -100dBm 以上时把发射功率降到 14dBm 完全不影响接收但发射电流可以降低将近一半。建议根据实际信号质量动态调节既能省电还能减少对周围射频环境的干扰。5.4 免授权频段的占空比限制必须心中有数LoRa 用的免授权频段不是随意发多少都行很多地区对单台设备在某个频点的发射占空比有明确限制。国内 470MHz 频段的法规要求设备在一个小时内在同一个频点上的发射时间不能超过总时间的 1%。对于 10 分钟上报一次、单次发送约 100ms 的设备来说占空比约为 0.017%远低于 1%完全没有问题。但如果你把上报间隔调到 1 分钟一次占空比就会达到 0.17%仍然合规如果改成 20 秒一次就逼近 0.5% 了要小心。这个参数在规划上报频率时务必要算清楚。写在最后这次帮 Atmosphere IoT 完成 LoRa 改造并接入 TTN我最大的体会是通信方案的选择永远不是技术越新越好而是要和部署场景严格匹配。Wi-Fi 覆盖好、带宽大、配置成熟适合室内供电充足的场景LoRa 的优势在远距离、低功耗、抗干扰适合电池供电、无人值守、数据量小的场景。把这两者的边界想清楚后面做的每步决策都会顺很多。如果你也准备做类似的改造我建议按这个顺序走先查好所在区域的网关覆盖再选对频段版本的模组然后花时间把串口/SPI 驱动调通最后才是接入 TTN 和做后端。每个环节都用最小的代码量验证通了再往下走特别是 payload 格式一定要在设备端和后端同时定义好否则后面联调会非常痛苦。另外说一句LoRaWAN 的吞吐能力有限不要指望它传图片、传日志、传大文件它天生就是为小数据、大覆盖、长续航设计的。认清楚这一点你就知道怎么用好它了。
返回列表