1. LoRa 自组网到底在解决什么问题
LoRa 自组网这个词,刚接触的朋友容易把它和“LoRa 网关+云平台”那套架构搞混。我先把边界划清楚:LoRa 自组网指的是终端节点之间不依赖公网、不依赖中心网关,自己组织成一张能互相转发数据的网状网络。它解决的核心问题是——在完全没有蜂窝信号、没有宽带、甚至没有固定供电的现场,让几十到几百个传感器节点把数据一跳一跳地传回汇聚点。
我做过几个典型的现场:山区果园的土壤墒情监测、地下管廊的温湿度采集、厂区外围的周界报警。这些地方的共同点是拉线成本极高、公网覆盖稀烂、设备靠电池撑。LoRa 的物理层特性——扩频调制、-137dBm 级别的接收灵敏度、单包几十到两百多字节——天然适合这种“低速率、长距离、低功耗”的活。但物理层只解决了“点对点能通”,真正让一张网活起来的是自组网协议:谁跟谁连、数据怎么绕、节点掉线了怎么办、新节点怎么入网。
这篇文章我会从原理讲到实操,重点拆 RS485 在 LoRa 自组网里的角色、IAP 远程升级怎么做、嵌入式端协议栈怎么落地。适合已经会点 STM32、玩过串口、想把这套东西真正部署到现场的人。如果你只是想知道“LoRa 能不能传 10 公里”,那看完第一节就够了;如果你想自己搭一张能跑起来的网,后面的内容建议逐段对照着做。
2. 核心架构拆解:为什么是 LoRa + RS485 + IAP 这套组合
2.1 LoRa 自组网的拓扑选型逻辑
自组网拓扑常见三种:星型、树型、网状(Mesh)。很多人一上来就想上 Mesh,觉得“多跳最牛”,但实际选型要看现场。
星型最简单,所有节点直连汇聚点。优点是协议极简、延迟确定、功耗最低;缺点是汇聚点覆盖半径就是全网半径,一般城区 1-2 公里、郊区 3-5 公里、视距开阔能到 10 公里以上。如果现场是个直径 2 公里的果园,星型完全够用,别折腾 Mesh。
树型是星型的扩展,中间加一级中继。适合狭长地形,比如沿着一条河道或公路布点。中继节点需要常供电或大容量电池,因为它要一直开着接收。
Mesh 是真正的多跳自组网,节点既能当终端也能当路由器。优点是覆盖可以无限延伸、有冗余路径;缺点是协议复杂、路由维护开销大、功耗高、延迟不可控。我踩过的坑是:在一个 60 节点的 Mesh 网里,路由表收敛要几十秒,期间数据丢得厉害。后来改成“分簇+两级中继”,稳定性立刻上来了。
选型的判断标准我总结成一张表:
| 拓扑 | 适用半径 | 节点数 | 功耗 | 协议复杂度 | 典型场景 |
|---|---|---|---|---|---|
| 星型 | <5km | <50 | 最低 | 低 | 小园区、单栋楼 |
| 树型 | 5-15km | 50-200 | 中 | 中 | 河道、公路沿线 |
| Mesh | 不限 | 100+ | 高 | 高 | 大范围、有冗余要求 |
提示:不要为了“技术先进”选 Mesh。现场能星型就星型,能树型就树型,Mesh 是最后的手段。
2.2 RS485 在自组网里的真实定位
这里要澄清一个常见误解:RS485 不是 LoRa 的替代品,两者是互补的。LoRa 负责“无线远距离”,RS485 负责“有线短距离、多设备、抗干扰”。
实际部署里,RS485 通常出现在两个位置:
第一,汇聚点侧。LoRa 汇聚节点收到无线数据后,通过 RS485 总线把数据转给本地工控机或 PLC。为什么不用 USB 或网口?因为工业现场 RS485 是标配,一根双绞线能挂 32 个设备、跑 1200 米、抗共模干扰强,接线端子一拧就行,比网口皮实得多。
第二,节点侧。一个 LoRa 节点盒子往往要接多个 RS485 传感器。比如一个环境监测节点,下面挂温湿度、CO2、光照三个 RS485 传感器,节点用 Modbus RTU 轮询采集,再打包通过 LoRa 发出去。这样节点数量少、无线信道占用少,成本也低。
RS485 的电气特性决定了它的边界:差分信号、共模范围 -7V 到 +12V、终端匹配 120Ω、总线型手拉手拓扑。这些细节后面第 4 节会展开。
2.3 IAP 为什么是自组网的刚需
IAP(In-Application Programming)指的是程序在运行中自己给自己刷固件。对于部署在山上、井下、厂区外围的 LoRa 节点,你不可能每次都爬上去插下载器。没有 IAP,一个 bug 就能让整个项目变成“一次性工程”。
LoRa 自组网 + IAP 的组合逻辑是:汇聚点通过 LoRa 把固件分包广播/组播下去,节点收到后写入 Flash 的备份区,校验通过后跳转 Bootloader 完成自更新。整个过程不需要人到现场。
但 IAP 有几个硬骨头:分包传输的可靠性、断电续传、版本回滚、Boot 里变量复位问题。热搜词里“iap boot里面定义的变量复位后会怎样”就是典型困惑——Bootloader 和 App 是两个独立的程序,复位后 RAM 里的变量当然全没了,跨程序传参只能靠 Flash 里的标志位或备份寄存器。这个后面第 5 节详细讲。
3. 嵌入式端协议栈的核心实现要点
3.1 物理层参数怎么定
LoRa 的物理层参数直接决定通信距离、速率和抗干扰能力。核心四个参数:扩频因子 SF、带宽 BW、编码率 CR、发射功率。
SF 从 7 到 12,每加 1,灵敏度提升约 2.5dB,但空中时间翻倍。BW 常用 125kHz、250kHz、500kHz,带宽越大速率越高但灵敏度越低。CR 一般 4/5 到 4/8,抗干扰换速率。
我一般这样定:
- 城区/干扰大:SF9、BW125、CR4/5、功率 17dBm
- 郊区/视距:SF7、BW125、CR4/5、功率 14dBm
- 山区/远距离:SF12、BW125、CR4/8、功率 20dBm
空中时间要算清楚。以 SF12、BW125、CR4/5、负载 32 字节为例,单包空中时间约 1.5 秒。如果 100 个节点每 5 分钟上报一次,信道占用率 = 100 × 1.5 / 300 = 50%,已经很高了。这时候要么降 SF,要么拉长上报周期,要么上 TDMA。
注意:国内 470-510MHz 频段有功率和占空比限制,实际项目要按当地规定来,别照搬国外参数。
3.2 自组网协议栈的分层设计
我在嵌入式端一般分四层:
物理层驱动:封装 SX1276/SX1262/LLCC68 的寄存器操作,提供 send/recv 接口。这层要处理 DIO 中断、FIFO 读写、CRC 校验。
MAC 层:负责信道接入。简单场景用 CSMA/CA(先听后发),复杂场景用 TDMA(时隙分配)。TDMA 需要全网时间同步,一般由汇聚点周期性发信标,节点收到后校准本地时钟。
网络层:负责路由。星型不需要路由;树型维护父子关系;Mesh 需要路由表。我倾向用“按需路由 + 邻居表”的简化方案,避免维护全网路由表。
应用层:定义数据帧格式、命令字、上报周期、传感器采集逻辑。
数据帧我习惯这样设计:
typedef struct { uint8_t magic; // 0xA5 帧头 uint8_t version; // 协议版本 uint16_t src_addr; // 源地址 uint16_t dst_addr; // 目的地址 uint8_t hop_count; // 跳数 uint8_t cmd; // 命令字 uint8_t seq; // 序列号 uint8_t payload_len; // 负载长度 uint8_t payload[64]; // 负载 uint16_t crc16; // 校验 } lora_frame_t;帧头 magic 用来快速同步,seq 用来去重,hop_count 用来防环。这些字段看着简单,但现场调试时能省大量时间。
3.3 地址分配与入网流程
节点地址我一般用 2 字节,高字节表示簇号,低字节表示簇内编号。比如 0x0105 表示 1 号簇第 5 个节点。这样路由时先看簇号,再看节点号,逻辑清晰。
入网流程设计成三步:
- 新节点上电后广播入网请求(dst_addr = 0xFFFF)
- 汇聚点或中继收到后回复入网响应,分配地址和时隙
- 节点保存地址,进入正常工作状态
入网请求要带节点唯一 ID(比如 MCU 的 UID 或 Flash 里的序列号),防止地址冲突。我见过一个项目因为没做唯一 ID,两个节点拿到同一地址,数据互相覆盖,查了三天才发现。
4. RS485 电路设计与组网实操
4.1 RS485 典型电路的关键细节
RS485 收发器常用 MAX485、SP3485、ADM2483(隔离型)。典型电路看着简单,但有几个坑:
终端匹配电阻。总线两端各接一个 120Ω 电阻,中间节点不接。很多人图省事每个节点都焊 120Ω,结果总线负载过重,通信距离大幅缩短。判断方法:用万用表量 A-B 之间电阻,应该是 60Ω 左右(两个 120Ω 并联)。
偏置电阻。总线空闲时差分电压不确定,容易误触发。需要在 A 上拉、B 下拉,一般 4.7kΩ 到 10kΩ。偏置电阻和终端电阻配合,保证空闲时 A-B 电压大于 200mV。
保护电路。现场雷击、浪涌、静电是常态。TVS 管(如 SMBJ6.5CA)、自恢复保险丝、共模电感这三件套建议都加上。我有个项目在厂区,没加 TVS,一个雷雨天烧了 8 个节点。
自动换向电路。RS485 半双工需要控制 DE/RE 引脚。用 MCU 的 GPIO 控制最简单,但软件要精确控制换向时机。也可以用 555 或三极管做自动换向,省一个 GPIO,但时序不好调。热搜词里“rs485自动换向电路”和“用反向器设计rs485”都是这个需求。
4.2 RS485 总线型串联的接线规范
总线型拓扑是 RS485 的标准接法,手拉手串联,不能星型或树型分支。分支长度超过 30cm 就会引起反射,导致误码。
接线步骤:
- 主干线用 120Ω 特性阻抗的双绞线(如 RVSP 2×0.5)
- 所有节点的 A 接 A、B 接 B,不能交叉
- 终端两个节点各接 120Ω 匹配电阻
- 屏蔽层单端接地,不要两端都接,否则形成地环流
- 节点分支线尽量短,最好直接从主线上焊接
提示:A/B 线序没有绝对标准,但同一总线必须统一。我习惯 A 接正、B 接负,接反了通信全乱。
4.3 Modbus RTU 轮询的实操技巧
RS485 上跑 Modbus RTU 是最常见的组合。节点作为主机轮询多个传感器,要点:
轮询周期。每个传感器响应时间不同,一般 10-50ms。如果挂 10 个传感器,一轮至少 500ms。加上 LoRa 发送时间,整个上报周期要留够余量。
超时重试。单次超时设 200ms,重试 2 次。连续 3 轮失败标记传感器离线。
寄存器映射。把每个传感器的数据映射到统一的内部结构体,方便打包发送。
typedef struct { uint16_t temp; // 温度 ×10 uint16_t humidity; // 湿度 ×10 uint16_t co2; // CO2 ppm uint16_t light; // 光照 lux uint8_t status; // 在线状态 } sensor_data_t;CRC 校验。Modbus RTU 用 CRC16,多项式 0xA001。别自己写,用查表法,快且不易错。
5. IAP 远程升级的完整实现
5.1 Flash 分区规划
以 STM32F103 为例,128KB Flash 这样分:
| 区域 | 起始地址 | 大小 | 用途 |
|---|---|---|---|
| Bootloader | 0x08000000 | 16KB | 引导程序 |
| App | 0x08004000 | 80KB | 应用程序 |
| Backup | 0x08018000 | 16KB | 固件备份 |
| Flag | 0x0801C000 | 2KB | 升级标志 |
| Config | 0x0801C800 | 2KB | 配置参数 |
Bootloader 只做三件事:检查升级标志、决定跳转 App 还是进入升级模式、执行固件搬运。App 负责正常业务和接收升级数据。
5.2 Boot 与 App 之间的变量传递
热搜词里“iap boot里面定义的变量复位后会怎样”这个问题,答案是:复位后 RAM 全部重新初始化,Boot 里定义的变量在 App 里根本访问不到。两个程序是独立编译的,链接地址不同,变量地址也不同。
跨程序传参只有三个可靠途径:
- Flash 标志区:在固定地址写标志字,Boot 和 App 都按这个地址读写
- 备份寄存器:STM32 的 BKP 寄存器,掉电不丢,但数量有限
- No-init 段:把变量放到不初始化的 RAM 段,复位后保留,但断电丢失
我一般用 Flash 标志区,简单可靠。标志字设计:
#define FLAG_ADDR 0x0801C000 #define FLAG_UPGRADE 0x5A5A5A5A #define FLAG_NORMAL 0x00000000 void set_upgrade_flag(void) { HAL_FLASH_Unlock(); HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, FLAG_ADDR, FLAG_UPGRADE); HAL_FLASH_Lock(); }Boot 启动时读这个地址,等于 0x5A5A5A5A 就进升级模式,否则跳 App。
5.3 LoRa 分包传输与断电续传
固件一般几十 KB,LoRa 单包最多 255 字节,要分成几百包。设计要点:
分包协议。每包带固件总长度、包序号、包内数据、CRC。接收方按序号写入 Backup 区。
确认与重传。每包发完等 ACK,超时重传,最多 3 次。连续失败则暂停升级,等下一轮。
断电续传。每收到一包,把当前包序号写入 Flash。重新上电后从上次序号继续。这个功能看着简单,但现场断电是常态,没有续传就得从头再来。
整体校验。所有包收完后,对 Backup 区做 CRC32 校验,通过才置升级标志,否则丢弃。
typedef struct { uint32_t total_len; uint16_t pkt_index; uint16_t pkt_total; uint8_t data[200]; uint16_t crc16; } fw_packet_t;注意:升级过程中要关闭看门狗或及时喂狗,否则搬运固件时超时复位,前功尽弃。
6. 常见问题与排查技巧实录
6.1 通信距离不达标
这是最高频的问题。排查顺序:
- 看天线。是不是 470MHz 专用天线?有没有拧紧?天线周围有没有金属遮挡?
- 看参数。SF 是不是太低?BW 是不是太宽?功率是不是没设满?
- 看环境。有没有同频干扰?用频谱仪扫一下。
- 看电源。发射瞬间电流能到 120mA,电源带不动会导致功率下降。
我遇到过一次,距离只有 200 米,最后发现是天线座虚焊。所以硬件问题永远先查。
6.2 RS485 通信时好时坏
典型原因是总线反射或地环流。排查:
- 量 A-B 电阻,应该是 60Ω 左右
- 检查终端电阻是不是只装了两个
- 检查屏蔽层是不是两端都接地了
- 用示波器看 A-B 波形,有没有过冲或振铃
热搜词里“rs485的ab波形哪种才是正确的”,正确波形应该是干净的差分方波,上升沿陡峭、无振铃、空闲时电压差大于 200mV。
6.3 IAP 升级失败
常见原因和解决:
| 现象 | 可能原因 | 解决 |
|---|---|---|
| 跳转 App 后死机 | 中断向量表没重映射 | App 里设置 SCB->VTOR |
| 升级后不启动 | 标志字没清 | Boot 跳转前清标志 |
| 升级中途卡死 | 看门狗复位 | 升级时喂狗或关狗 |
| 固件校验失败 | 分包丢包 | 加重传和整体校验 |
| 断电后从头开始 | 没做续传 | 每包写序号到 Flash |
6.4 节点功耗超标
电池节点要求待机微安级。常见漏电点:
- LoRa 模块没进 Sleep,还在接收
- RS485 收发器没关,静态电流几毫安
- 传感器一直供电,没做电源开关
- MCU 没进 Stop 模式,跑着主循环
我的做法是:采集完立刻关传感器电源、关 RS485、LoRa 进 Sleep、MCU 进 Stop,用 RTC 定时唤醒。这样整机待机能做到 10μA 以下。
7. 现场部署的经验与参数速查
7.1 部署前的检查清单
- 频段和功率是否符合当地规定
- 每个节点的地址是否唯一
- 天线是否匹配、朝向是否合理
- RS485 终端电阻和偏置电阻是否到位
- 固件版本是否统一
- IAP 升级通道是否测试通过
- 电池电压是否充足
7.2 关键参数速查表
| 参数 | 推荐值 | 说明 |
|---|---|---|
| SF | 7-12 | 越大越远越慢 |
| BW | 125kHz | 平衡距离和速率 |
| CR | 4/5 | 抗干扰够用 |
| 功率 | 14-20dBm | 按规定和距离调 |
| 上报周期 | 5-30min | 按业务需求 |
| RS485 波特率 | 9600/19200 | 工业常用 |
| 终端电阻 | 120Ω × 2 | 只在两端 |
| 偏置电阻 | 4.7kΩ | A 上拉 B 下拉 |
7.3 我踩过的几个坑
第一个坑:Mesh 网络里节点频繁掉线。原因是路由表老化时间设太短,节点刚建立的路由很快失效。后来把老化时间从 30 秒改成 5 分钟,稳定了。
第二个坑:RS485 和 LoRa 共电源,LoRa 发射瞬间拉低电压导致 RS485 误码。解决方法是电源加 1000μF 大电容,或者 LoRa 和 RS485 分开供电。
第三个坑:IAP 升级时没关看门狗,搬运 80KB 固件要几秒,看门狗 2 秒就复位了。后来在 Boot 里先关狗,搬运完再开。
第四个坑:节点地址用随机数生成,结果两个节点撞地址。改成用 MCU UID 哈希,彻底解决。
这套 LoRa 自组网方案我从最早的星型单跳,做到现在的分簇两级中继,前后迭代了四五个版本。核心体会是:协议越简单越可靠,现场环境永远比实验室恶劣,能有线就别无线,能单跳就别多跳。RS485 和 IAP 这两个看似边缘的技术点,实际上是决定项目能不能长期稳定运行的关键。后面如果要做更大规模,可以考虑上 TDMA 时隙和自适应速率,但那是另一个话题了。