简介这是一份面向物联网开发者与无线通信爱好者的LoRa网状网络源码包源自作者在8块LILYGO TTGO T-Beams v0.7硬件上基于LoRa PHY协议设计路由算法的完整实践可用于无中心基站场景下的多跳数据转发与节点入网。包内34个文件以21个Arduino .ino草图为骨干覆盖硬件测试、多发送端单接收、网关双工通信、GPS数据接收等场景另有7个Markdown说明文档、2个socket_server相关脚本及少量C/H文件用于搭建与数据库对接的套接字服务。压缩包仅43KB结构紧凑适合作为野外数据采集或资产跟踪项目的参考起点。内容预览显示资源完整呈现网关节点GW、LoRa节点LN与套接字服务器SS的通信链路示例支持将节点ID、跳数、GPS经纬度及时间戳等有效载荷转发至Express服务器并写入数据库。目前已有1389人浏览学习对有嵌入式基础、希望快速上手LoRa组网的读者尤为实用。1. LoRa 网状网络为什么难从 8 个节点的组网诉求切入lora-mesh 这套源码解决的不是「两个 LoRa 模块能不能互通」这种入门问题而是「8 个 LoRa 节点怎么组出一张能上报 GPS、能多跳、能落库的网状网络」。我拆完这份源码最大的感受是LoRa 的物理层通信门槛低开箱就能收发但一旦涉及多节点交替收发、网关汇聚、跳数路由绝大多数人会在双向通信和参数匹配这两个地方卡死。这套源码的价值就在于它把整个探索过程留了下来——从最基础的硬件测试、互操作实验到网关双向收发、GPS 数据上报数据库每个阶段都有可跑的示例。适合手里有 T-Beam 或者类似 LoRa 开发板、想做资产追踪或传感器网络又不想从零踩坑的开发者。2. 硬件选型与工程目录为什么是 T-Beam v0.7源码包里到底有什么2.1 选 T-Beam v0.7 的三个理由GPS、WiFi、LoRa 三合一做 LoRa 网状网络节点端最麻烦的不是 LoRa 通信而是定位和回传。节点要上报 GPS 坐标那板子上就得有 GPS 模块节点要把数据送到数据库就得有一条 WiFi 或以太网通路。如果分开买模块自己接光是电源和天线就会吃掉大量调试时间。常见做法是直接用集成板。LILYGO TTGO T-Beam v0.7 这个板子把三样东西焊在一起SX1276 LoRa 收发器负责节点间通信NEO-6M GPS 模块负责定位ESP32 负责 WiFi 和 Socket 通信。v0.7 这个版本比早期版本多了一个关键改进——GPS 模块的电源可以单独控制这意味着在室内调试时可以把 GPS 断电省电做低功耗场景时非常有用。当然也有替代方案如果你的节点不需要 GPS只用 LoRa 做数据中继那用普通的 ESP32 SX1276 模块也够但如果要做资产追踪这种带位置上报的网状网络T-Beam 这个集成度能让你少焊一半的线。某开发者当初组这套 8 节点网络时焊线的活基本没干过全程用杜邦线和板载天线就搭起来了。2.2 源码包目录拆解从硬件测试到网关双工的完整链路zip 解压后是lora-mesh-main这个根目录里面全是 Arduino 工程文件夹。我把目录映射成一张表这样你打开源码包时能快速定位到对应实验阶段文件夹阶段定位核心作用01_examples_to_test_hardware硬件验证测试 T-Beam 的 LoRa 收发、GPS 定位、屏幕显示是否正常02_sender-receiver-interoperable互操作验证验证不同板子之间用同一组参数能互通不搞私有协议03_RxGPS_t-beam/04_RxGPS_t-beam_updateGPS 接收接收端解析 LoRa 包里的 GPS 坐标新版修复了旧版的缓冲区问题05_multipleTx_singleRx多对一通信多个 LoRa 节点轮流向同一个接收端发数据验证碰撞场景06_server_clientWiFi 链路T-Beam 作为 TCP 客户端连接 Socket 服务器07_alternating_Rx-Tx/08_alternating_Rx-Tx_callback双向切换同一节点交替收发后者用 callback 事件驱动不放主循环里阻塞09_gateway-node-duplex网关双工网关节点一边收 LoRa 数据一边通过 WiFi 转发到服务器02_random_data_to_db数据落库用随机数据模拟 LoRa 载荷打通 Socket 到 Express 再到数据库全链路GW/SS/LN角色代码分别对应网关、Socket 服务器、LoRa 节点三个角色的独立工程这套目录的设计思路很清晰它不是给你一个「最终成品」让你直接烧录而是把整个探索过程分成多个里程碑。你要做的不是打开一个工程就跑而是从 01 开始一个个往上走。我一般会先把 01 和 02 跑通确认手上板子的 LoRa 物理层没问题再进入网关相关实验。如果你跳过硬件测试直接跑 09出了问题很难判断是 LoRa 链路断了还是 WiFi 链路断了。2.3 GW、SS、LN 三个角色的代码组织逻辑源码包里有三个没有编号的文件夹——GW、SS、LN这三个是整套网状网络最终形态的角色划分。LNLoRa Node是叶子节点也就是那 7 台 T-Beam。它们负责采集 GPS 数据、构造载荷、通过 LoRa 射频把数据发出去本身不连接 WiFi。GWGateway Node是那 1 台特殊的 T-Beam它同时开着 LoRa 接收和 WiFi 连接一边收 7 个 LN 的数据一边通过 Socket 把数据推给 SS。SSSocket Server跑在一台电脑或树莓派上它不是 T-Beam 上跑的代码而是 Node.js 写的服务器脚本负责接收 Socket 数据、通过 HTTP POST 转发给 Express ServerExpress 再把数据写进数据库。这个组织方式给我的启发是网状网络的「网」不一定要求每个节点都对等。你完全可以做成「多节点采集 单网关汇聚」的星型加中继的混合结构。源码里实现的跳数字段已经留在载荷里意味着将来可以让 LN 之间互相转发再由其中一个 LN 统一发给 GW——这才是向真正 mesh 演进的基础。3. 从点对点到双向收发把 LoRa 通信练稳再谈网状组网3.1 互操作示例02_sender-receiver-interoperable的参数对齐原理LoRa 通信不是简单的一发一收收发双方必须在一组射频参数上完全一致才能互通。02_sender-receiver-interoperable这个实验的目的就是验证「多块板子用同一组参数能不能互相通信」。如果两块板子的频率、扩频因子、带宽、编码率、同步字有任何一个不一致接收端就只会收到 CRC 错误或者根本收不到。我在这个工程里核对了参数配置核心配置类似这样// LoRa 参数配置发送端与接收端必须完全一致 #define LORA_FREQUENCY 915.0E6 // 频率915MHz不同地区需调整 #define LORA_SF 7 // 扩频因子 Spreading Factor: 7 #define LORA_BW 125E3 // 带宽 Bandwidth: 125kHz #define LORA_CR 5 // 编码率 Coding Rate: 4/5 #define LORA_SYNC_WORD 0x12 // 同步字用于区分同一频段上的不同网络 void setupLoRa() { LoRa.setPins(SS, RST, DIO0); // T-Beam v0.7 的 SPI 引脚定义 if (!LoRa.begin(LORA_FREQUENCY)) { Serial.println(LoRa init failed!); while (1); } LoRa.setSpreadingFactor(LORA_SF); LoRa.setSignalBandwidth(LORA_BW); LoRa.setCodingRate4(LORA_CR); LoRa.setSyncWord(LORA_SYNC_WORD); LoRa.setTxPower(20); // 发射功率 20dBm实际视当地法规调整 }这段代码的关键在于LoRa.begin()只是初始化频率后面的setSpreadingFactor、setSignalBandwidth、setCodingRate4、setSyncWord是另一套独立的参数维度。很多人只改了频率就以为能通结果 SF 一个用 7 一个用 12根本收不到。setTxPower(20)是发射功率调太高会发热甚至烧射频前端调太低会被环境噪声压掉。互操作实验的另外一个要点是「先测一发一收再测一发多收」。05_multipleTx_singleRx正是这个思路——多个节点向同一个接收端轮流发数据接收端每收到一个包就打印一次。这个实验暴露了 LoRa 的 ALOHA 类碰撞问题如果两个节点同时发接收端会 CRC 失败两个包全丢。解决方向是让节点错开发送时机或者用不同的扩频因子做逻辑分信道。3.2 交替收发07_alternating_Rx-Tx与08_alternating_Rx-Tx_callback的区别网状网络里每个节点既可能是发送方也可能是接收方所以交替收发是绕不开的课题。源码里有 07 和 08 两个版本做法完全不同。07 是「先收后发」的轮询式写法主循环里先打开接收模式等一会然后切到发送模式发数据。这种写法的问题很明显节点在等待接收时占用了主循环它在这段时间里没办法应答别的节点的请求。如果两个节点同时切到发送模式数据就撞了。08 改用 callback 事件驱动LoRa 库在收到数据包时自动触发回调函数主循环可以继续跑自己的逻辑// callback 方式收到 LoRa 数据包时自动进入此函数 void onLoRaReceive(int packetSize) { if (packetSize 0) return; String incoming ; while (LoRa.available()) { incoming (char)LoRa.read(); } Serial.print([RX] RSSI: ); Serial.print(LoRa.packetRssi()); Serial.print( | SNR: ); Serial.print(LoRa.packetSnr()); Serial.print( | Data: ); Serial.println(incoming); // 这里做应答逻辑如果需要回复直接调用 LoRa.beginPacket() LoRa.print() // 注意不要在 callback 里做耗时操作只做轻量处理 } void setup() { // ... 硬件初始化省略 LoRa.onReceive(onLoRaReceive); // 注册回调函数 LoRa.receive(); // 开启持续接收模式 } void loop() { // 主循环可以处理 GPS 读取、WiFi 心跳等任务 // 不会被 LoRa 接收阻塞 }callback 模式的核心是LoRa.onReceive()注册回调函数后调用LoRa.receive()进入持续接收状态。数据包到达时接收中断触发回调函数在中断上下文里执行。所以回调里不能做延时操作也不能做 Serial.print 之外的重活——如果解析 GPS 数据很耗时应该先拷贝到缓冲区在主循环里再处理。我实测下来GPS 解析放 callback 里会导致后续数据包丢失因为中断处理期间新来的包进不了接收队列。3.3 双工网关09_gateway-node-duplex怎么同时处理 LoRa 和 WiFi09 是把 06 的 WiFi 能力和 08 的 LoRa callback 能力合到一起做一个「一边收 LoRa、一边发 WiFi」的双工网关。这个工程是这个项目里最核心的一个示例。实际实现里LoRa 接收和 WiFi 发送是两条独立的事件链LoRa 数据到达后callback 把数据写进一个环形缓冲区主循环检测到缓冲区有新数据就通过 WiFi 发送到 Socket 服务器。这个「缓冲区解耦」是双工网关的关键——如果不加缓冲区LoRa 接收和 WiFi 发送会互相阻塞一个数据包卡在 WiFi 握手时第二个 LoRa 包就丢了。// 简易环形缓冲区用于解耦 LoRa 接收与 WiFi 发送 #define BUF_SIZE 32 char packetBuffer[BUF_SIZE][128]; uint8_t head 0, tail 0; void onLoRaReceive(int packetSize) { if (packetSize 0) return; // 只把数据写入缓冲区不在这里发 WiFi uint8_t next (head 1) % BUF_SIZE; if (next ! tail) { // 缓冲区满则丢弃新包 memset(packetBuffer[head], 0, 128); int i 0; while (LoRa.available() i 127) { packetBuffer[head][i] (char)LoRa.read(); } head next; } } void loop() { // 主循环里检查缓冲区 while (tail ! head) { sendToServer(packetBuffer[tail]); // 通过 WiFi Socket 发到服务器 tail (tail 1) % BUF_SIZE; } // 处理其他任务... }这段代码里head和tail指针的环形缓冲区逻辑很典型onLoRaReceive只写缓冲区loop只读缓冲区两者通过head ! tail判断是否有新数据。如果缓冲区满next ! tail为假直接丢弃新包保证旧数据优先送出去。这样即使 WiFi 偶发延迟LoRa 接收端也不会被卡死。4. 数据链路贯穿GPS 载荷从 LoRa 节点到数据库的完整管道4.1 LN 端的 GPS 采集与载荷构造03_RxGPS_t-beam的载荷格式03_RxGPS_t-beam这个示例展示了 T-Beam 怎么读取 GPS 数据并封装成 LoRa 载荷。我打开源码看载荷格式不是二进制而是一条带分隔符的字符串大约长这样NODE:3|HOP:1|LAT:23.123456|LON:113.654321|T:2024-01-15 10:30:25NODE是节点 IDHOP是跳数当前版本固定为 1为将来多跳预留LAT和LON是经纬度T是日期时间。用字符串做载荷的优点是可以直接用Serial.print调试不需要写二进制编解码缺点是每个包多占十几个字节在 SF7 下 125kHz 带宽时一个包的有效载荷上限大约 200 字节这种 50 字节左右的字符串完全够用。GPS 数据的读取在 T-Beam 上有一个非常容易踩的坑GPS 模块的串口是Serial1波特率 9600而且 v0.7 的 GPS 电源需要通过 GPIO 控制。源码里一般会有类似这样的逻辑// 打开 GPS 电源v0.7 板载 GPS 由 GPIO 控制供电 pinMode(GPS_POWER_ON, OUTPUT); digitalWrite(GPS_POWER_ON, HIGH); // 初始化 GPS 串口 Serial1.begin(9600, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN); // 解析 GPS 数据简化示意 void loop() { while (Serial1.available() 0) { char c Serial1.read(); if (gps.encode(c)) { // 使用 TinyGPS 库解析 if (gps.location.isValid()) { double lat gps.location.lat(); double lng gps.location.lng(); // 构造 LoRa 载荷并发送 String payload NODE: String(nodeId) |HOP:1|; payload LAT: String(lat, 6) |LON: String(lng, 6); payload |T: getDateTimeString(); LoRa.beginPacket(); LoRa.print(payload); LoRa.endPacket(); } } } }参数说明GPS_POWER_ON是 v0.7 板子上 GPS 模块的电源控制引脚必须拉高才能让 GPS 工作否则Serial1里永远收不到数据。gps.encode(c)是 TinyGPS 库的逐字节解析GPS 数据不是一次到位而是要连续喂字节直到解析出完整的 NMEA 语句。location.lat()返回 double 类型用String(lat, 6)保留 6 位小数这大约对应 0.1 米的定位精度对于集装箱追踪这种粒度完全够用。getDateTimeString()这个函数在源码里有它从 TinyGPS 的date和time对象里拼接日期时间字符串。一个值得注意的细节T-Beam 的 GPS 模块没有板载 RTC 电池断电后时间会丢失所以每次上电后要等 GPS 锁定才能拿到正确时间。室内调试时经常出现日期显示 1980 年这是 GPS 没定位的表现不是代码 bug。4.2 SS 端链路Socket 服务器如何把载荷 POST 到 Express 并落库06_server_client和02_random_data_to_db打通了 LoRa 节点到数据库的最后一公里。这里的架构是T-Beam 通过 WiFi 以 TCP Socket 方式连上 Socket 服务器发送原始载荷字符串Socket 服务器收到后通过 HTTP POST 转发给 Express ServerExpress Server 解析字段写入数据库。SS 端代码是 Node.js 写的核心逻辑我用简化版说明// Socket 服务器接收 T-Beam 的 TCP 连接转发数据到 Express const net require(net); const http require(http); const SOCKET_PORT 9000; const EXPRESS_URL http://localhost:3000/api/ingest; const server net.createServer((socket) { console.log([SS] Client connected:, socket.remoteAddress); socket.on(data, (data) { const payload data.toString().trim(); console.log([SS] Received:, payload); // 转发到 Express Server const postData JSON.stringify({ raw: payload }); const options { hostname: localhost, port: 3000, path: /api/ingest, method: POST, headers: { Content-Type: application/json, Content-Length: Buffer.byteLength(postData) } }; const req http.request(options, (res) { res.on(data, (d) { console.log([SS] Express response:, d.toString()); }); }); req.on(error, (e) { console.error([SS] POST failed:, e.message); }); req.write(postData); req.end(); }); socket.on(close, () { console.log([SS] Client disconnected); }); }); server.listen(SOCKET_PORT, () { console.log([SS] Socket server listening on port ${SOCKET_PORT}); });这段代码的关键点在http.request的options里path必须和 Express 路由完全匹配Content-Type必须设置成application/json否则 Express 的 body parser 会拒绝解析。Content-Length要手动算好因为 Node.js 不会自动填。一个实际调试场景如果socket.on(data)里收到的 payload 被 TCP 粘包拆包了那么data.toString().trim()得到的可能是不完整的字符串转发到 Express 后解析会失败。常见做法是给每条消息加一个结束符比如\r\n在 SS 端做缓冲检测到结束符再处理整条消息。源码里02_random_data_to_db用了随机数据模拟 LoRa 载荷来专门测试这条链路目的就是在不依赖真实 LoRa 的情况下把网络管道跑通。Express 端的代码相对标准定义一个 POST 路由api/ingest接收{ raw: NODE:3|HOP:1|... }然后按|分割字符串再按:分割键值对最后插入数据库。这里的数据库用什么都可以源码没限定具体数据库用 MongoDB 或 MySQL 都行只要建一个包含 node_id、hop、lat、lon、timestamp 五个字段的表。4.3 从随机数据到真实数据为什么先跑02_random_data_to_db再跑09很多新手直接烧录 09_gateway-node-duplex结果 LoRa 收不到数据时无法判断是哪一段链路出了问题。源码里 02 这个实验的价值就在这它用随机数据模拟 LoRa 节点的输出把「LoRa 射频链路的变量」排除掉只验证 WiFi Socket 到 Express 再到数据库这一段。正确顺序我一般建议这么走跑01_examples_to_test_hardware确认每块板子 LoRa 收发和 GPS 都正常。跑02_sender-receiver-interoperable确认板子之间的 LoRa 物理层互通记下当前用的 SF、带宽、频率。跑02_random_data_to_db在不接 LoRa 的情况下把 Socket 到数据库的链路打通。跑03_RxGPS_t-beam确认 GPS 能解析出坐标并格式化成NODE:1|HOP:1|...这种字符串。跑09_gateway-node-duplex把 LoRa 收到的数据通过 WiFi 转发出去最终落到数据库。这套顺序本质上就是「先隔离再集成」。每跑通一步你就知道下一步出问题时该怀疑谁。如果第 3 步能跑通但第 5 步收不到数据问题一定在 LoRa 或 GPS 段和服务器端无关。5. 避坑LoRa 网状网络源码复现中的六个典型问题5.1 GPS 上电后一直收不到定位数据Serial1里全是乱码现象烧录 03_RxGPS_t-beam 后串口监视器一直打印乱码或者干脆没有任何输出GPS 的 LED 不闪烁。原因T-Beam v0.7 的 GPS 模块电源不是默认打开的必须用 GPIO 控制拉高。很多人不知道这一点直接Serial1.begin(9600)就以为能收到 NMEA 语句。另外GPS 天线需要放在窗边或室外室内信号被混凝土楼板挡住模块收不到卫星信号自然没有输出。解决在初始化代码里添加pinMode(GPS_POWER_ON, OUTPUT); digitalWrite(GPS_POWER_ON, HIGH);并且把天线靠近窗户。如果还是没有定位等待几分钟GPS 冷启动首次定位可能长达 1 分钟以上。判断 GPS 是否工作的标志是gps.location.isValid()返回true不要看Serial1.available()是否有字节——GPS 模块即使未定位也会持续输出 NMEA 语句。5.2 两块板子参数看着一样但 LoRa 收不到包现象sender 和 receiver 都初始化成功了发送端显示发送成功接收端串口没有任何输出。原因我在 3.1 节提到的那五个参数——频率、SF、带宽、编码率、同步字——有一个不一致就收不到。最常见的翻车点是把LoRa.setSyncWord(0x12)漏了。LoRa 的同步字相当于网络 ID同一个频段上可能有不同设备在通信靠同步字区分。默认值是0x12但如果接收端配置成0x34两端就处于不同虚拟网络。解决把 3.1 节那段配置原封不动复制到收发两端的setup()里。如果还不行用逻辑分析仪或者串口打印LoRa.packetRssi()来判断射频信号是否到达接收端——如果 RSSI 是 -120dBm 左右说明信号被噪声淹没如果 RSSI 低于 -140 说明根本没有射频信号到天线。5.3 callback 里做耗时操作导致数据包连环丢失现象08_alternating_Rx-Tx_callback 跑起来后第一个包能收到后续连续几个包丢失或者收到包内容错乱。原因callback 是中断上下文里执行的。如果在 callback 里做gps.encode(c)这种逐个字符解析、或者调用String拼接大字符串执行时间过长LoRa 接收中断被打断后续数据包溢出被硬件丢弃。解决callback 里只做一件事——把原始字节快速拷贝到缓冲区。数据解析和协议处理全部放到loop()里。我在 3.3 节的环形缓冲区代码就是这个思路。从那以后我每次写 LoRa 业务都会强制问自己一句这个操作能放中断里吗不能就搬走。5.4 多节点同时上报时数据互相碰撞接收端 CRC 持续报错现象5 个节点同时往网关发数据网关串口一直打印 CRC error偶尔能收到一个包。原因LoRa 是 ALOHA 类协议多个节点同时发信号在空中叠加接收端 CRC 校验不过所有并发包都丢。CSMA 监听在 LoRa 里不像 WiFi 那样可靠因为 LoRa 的灵敏度极高远距离的微弱信号也可能干扰本地发送。解决最简单的方式是给每个节点分配不同的时隙用millis()做时间窗口错开发送。常见做法是节点 ID 为 N每 10 秒为一个时隙周期在第 N 秒时发送。再进阶一点可以在发送前先调用LoRa.packetRssi()检测信道是否忙。实测用固定时隙后5 节点的丢包率从 30% 以上降到了 5% 以内。5.5 Socket 服务器偶尔收到半截消息JSON 解析失败现象SS 端打印的 payload 有时候只有半个字段比如NODE:3|HOP:1|LAT:23.1后面一半没了。Express 返回 400。原因TCP 是流式协议不保证一次socket.on(data)事件里拿到完整的一条消息。LoRa 载荷字符串如果刚好跨越了 TCP 分片边界就会在一次 data 事件里出现半条消息。这是网络编程经典坑在这里因为 LoRa 包本身不带结束标记而被很多人忽略。解决在 SS 端做消息缓冲按行分隔符处理。T-Beam 发送时在载荷末尾追加\r\nSS 端把data累加进一个变量检测到\r\n才认为是一条完整消息再进入后续解析和转发逻辑。5.6 数据库连接不释放跑几分钟后写入失败现象系统刚启动时数据正常落库跑 5 分钟左右Express 端开始报数据库连接超时重启后恢复正常。原因Express 的数据库驱动如果用的是连接池默认连接数有限。如果每条 POST 请求都新建一个连接但没释放连接池耗尽后新的请求就会排队直到超时。LoRa 节点上报频率不高但每次连接不释放就像漏水一样慢慢把池子耗干。解决复用数据库连接建一个全局连接池。用 MongoDB 的MongoClient时只初始化一次后续所有写入复用同一个客户端用 MySQL 时用连接池模块设置connectionLimit为 10 左右。另外一个容易被忽略的点检查 Express 路由里有没有await阻塞——如果有多个请求同时进来处理中的请求会拖住事件循环。设计时尽量同步处理单条消息不要让后端做批量等待。6. 进阶验证把随机载荷换成真实节点用日志反推链路拓扑跑通了 09_gateway-node-duplex 之后你手里应该已经有一条「LoRa 节点 → 网关 → Socket 服务器 → Express → 数据库」的完整链路。但这个链路到底稳不稳不能只看数据库里有没有数据。我常用的验证方式是做「连续性测试 跳数还原」两步。连续性测试的目的是验证长时间运行的稳定性方法是让每个节点以固定周期发送数据记录 1 小时内每个节点的收到次数。如果节点 3 的到包率长期低于 80%那就要检查节点 3 的发射功率、天线朝向或者它到网关的距离是否有遮挡。做法是给数据库加一个received_at字段每次写入时记服务器时间再按node_id分组统计。这样你能画出一张「每个节点每分钟上报次数」的热力图快速定位弱节点。跳数还原是为了将来扩展真正 mesh 做准备。当前所有 LN 的HOP字段都是 1意味着所有节点直接和 GW 通信这其实是星型拓扑不是严格意义的 mesh。要做多跳你需要修改 LN 的发送逻辑如果某个节点收不到 GW 的 ACK应答帧它就尝试把数据发给相邻节点由相邻节点代为转发转发时将HOP字段加 1。GW 收到数据后通过HOP字段就能判断数据经过了几跳从而画出全网拓扑。我实际做过的验证脚本很简单把数据库里的HOP字段按节点分组聚合看跳数分布是否合理——如果所有节点都是 HOP1说明近距离都直连如果某个节点经常是 HOP3说明它离网关较远需要中继。这时候你就知道该在哪个位置增加一个中继节点而不是盲目增加发射功率。一个具体的操作步骤是修改 LN 端代码让它在开机前 30 秒发送广播包HELLO|NODE:xGW 收到后回复ACK|NODE:xLN 通过能否收到 ACK 来决定自己往哪个方向上报。这个改动本质上是在物理层之上加了一个非常薄的确认层它不改变 LoRa 参数不会影响底层通信。有个翻车经验值得说某次我为了省电把 GPS 模块在没定位时就断电结果节点上报的坐标全是0.000000数据库里全是无效数据。后来我在 LN 端加了检查gps.location.isValid()为假时直接跳过这一次上报宁可丢一次数据也不上报垃圾坐标。从那以后我每次改完节点代码都会强制走一遍「硬件测试 → 互操作测试 → 随机数据落库 → 真实 GPS 上报」四个阶段再跑 10 分钟的连续性测试看曲线。这套流程帮我在三个项目里避免了「节点端改了一行网关端一天后崩掉」的连锁问题。如果你打算在这套源码基础上做自己的网状网络建议至少抽出一天时间把 01 到 09 全部跑一遍得到的感知比读十篇文档都强。希望帮到你。本文还有配套的精品资源点击获取