news 2026/10/3 2:02:14

ESP32-Bit-Pirate 仓库中的 SX126x LoRa Ping-Pong 例程深度解析:基于 RAK4631 的双节点测距与低功耗设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ESP32-Bit-Pirate 仓库中的 SX126x LoRa Ping-Pong 例程深度解析:基于 RAK4631 的双节点测距与低功耗设计
  • 嵌入式
  • 硬件开发

【免费下载链接】ESP32-Bit-Pirate

A Hardware Hacking Tool with Web-Based CLI That Speaks Every Protocol

项目地址:https://gitcode.com/GitHub_Trending/es/ESP32-Bit-Pirate
点击查看免费下载

导读:本文以 ESP32-Bit-Pirate 仓库内置的 SX126x-Arduino 库中 PingPong-RAK4631 例程 为绝对主体,完整剖析一个"一端发 PING、另一端回 PONG"的双节点 LoRa 点对点通信程序。读完你将掌握:基于 SX126x Radio API 的收发初始化全流程、用 CAD(信道活动检测)做发射前避让的防碰撞策略、用 FreeRTOS 信号量替代忙轮询实现低功耗的事件驱动模型,以及 RAK4631 上 DFU + BLE-UART 双服务的 BLE 调试通道搭建方法,并能直接将其移植到其他 SX126x 开发板上。

一、例程定位:为什么 LoRa 节点需要 Ping-Pong

该例程(PlatformIO 版本,可在 Atom 或 Microsoft Visual Studio 的 PlatformIO 插件中编译)实现的是两个 LoRa 节点之间的简单 PING / PONG 应答:一个节点发送 PING,另一个节点收到后回复 PONG。它是最基础、也最实用的 LoRa 连通性验证程序——用来测试你的 LoRa 天线、发射功率与频段配置下"能打多远"(range test)非常合适。

程序在setup()中会打印SX126x PingPong test横幅,并通过Serial.printf("RssiValue=%d dBm, SnrValue=%d\n", rx_rssi, rx_snr)输出每次收到包时的 RSSI 与 SNR,配合收发双向报文,即可直观评估链路余量。当前仓库将 SX126x-Arduino 完整收纳在 lib/SX126x-Arduino 下,本仓库的 platformio.ini 亦为各板卡定义了 SX1262 LoRa 引脚(如LORA_SCK_PIN、LORA_MOSI_PIN、LORA_CS_PIN、LORA_RST_PIN、LORA_BUSY_PIN、LORA_DIO1_PIN),说明该库正是仓库 Sub-GHz / LoRa 能力的底层依赖之一,理解本例程即理解这套 SX126x 驱动的核心用法。

二、工作原理:三段式状态机

原文档给出的运行逻辑本质上是一个围绕"听 → 判 → 发"循环的有限状态机,完整流程如下:

  1. 监听:节点进入 RX 监听状态,等待外来报文。
  2. RX 超时(3 秒):若 3 秒内没收到任何包,启动一次CAD(Channel Activity Detection,信道活动检测):
    • 若 CAD 结果显示信道空闲,则发送一个 PING 包;
    • 随后重新进入监听。
  3. 收到 PING:启动 CAD:
    • 若信道空闲,则回复 PONG;
    • 随后重新进入监听。
  4. 收到 PONG:启动 CAD:
    • 若信道空闲,则继续发送下一个 PING;
    • 随后重新进入监听。

可以看到,无论哪个状态,发送动作永远前置一次 CAD 检查——只有确认信道空闲才占用信道发包,这是避免两个节点同时抢发、互相碰撞的关键设计。对应源码位于 PingPong-RAK4631.ino,其中OnRxTimeout回调置位RX_ERR事件,主循环在CAD_FIN分支中根据tx_cadResult决定发包或重新监听(radio.cpp 中的RadioSetCadParams即为底层SX126xSetCadParams的封装,位于第 1160 行)。

三、关键参数表:一份可直接修改的 LoRa 配置清单

例程把所有射频参数集中定义为宏,便于按地区法规与硬件能力调整。以下为源码 PingPong-RAK4631.ino 中的完整参数及含义:

宏默认值含义与取值说明
RF_FREQUENCY916100000 (Hz)载波频率,示例为 916.1 MHz;须符合当地 ISM 频段法规(EU868 / US915 等)
TX_OUTPUT_POWER22 (dBm)发射功率,受 SX126x 硬件与法规上限约束
LORA_BANDWIDTH0带宽索引:0: 125 kHz,1: 250 kHz,2: 500 kHz,3: 保留;带宽越小灵敏度越高、速率越低
LORA_SPREADING_FACTOR7扩频因子,范围SF7..SF12;SF 越大抗干扰越强、速率越慢
LORA_CODINGRATE1编码率索引:1: 4/5,2: 4/6,3: 4/7,4: 4/8;纠错冗余随编号增大而增加
LORA_PREAMBLE_LENGTH8前导码长度(符号数),收发两端必须一致
LORA_SYMBOL_TIMEOUT0RX 符号超时(单位:符号),0 表示由RX_TIMEOUT_VALUE决定
LORA_FIX_LENGTH_PAYLOAD_ONfalse是否固定负载长度;false表示隐式头部(可变长度)
LORA_IQ_INVERSION_ONfalse是否启用 IQ 反转(LoRaWAN 上行常用),P2P 场景保持false
RX_TIMEOUT_VALUE3000 (ms)单次 RX 监听超时,超时后触发OnRxTimeout
TX_TIMEOUT_VALUE5000 (ms)单次 TX 超时保护,防止卡死
BUFFER_SIZE64收发缓冲区与负载长度(字节)

注意:LORA_PREAMBLE_LENGTH、LORA_BANDWIDTH、LORA_SPREADING_FACTOR、LORA_CODINGRATE、LORA_IQ_INVERSION_ON在收发两端必须配置一致,否则无法解调对方报文——这也是调试时最容易踩的坑。

四、初始化流程逐行拆解

4.1 LoRa 硬件初始化:无需手写引脚

例程明确说明代码专为 RAK4631 编写,使用该板卡专用的初始化例程,无需自行定义引脚:

uint32_t init_result = lora_rak4630_init(); Serial.printf("RAK4631 LoRa init %s\r\n", init_result == 0 ? "success" : "failed");

lora_rak4630_init()内部完成 SPI 总线、复位、忙检测等板级配置(相关实现见 src/boards/mcu/board.cpp 与 src/RAK4630_MOD.cpp)。移植到其他开发板时,这一步需替换为对应板卡的初始化函数,或参考本仓库 platformio.ini 中各 env 的LORA_*_PIN引脚宏自行接线。

4.2 注册事件回调

SX126x-Arduino 采用事件驱动模型,先填充RadioEvents_t结构体:

RadioEvents.TxDone = OnTxDone; RadioEvents.RxDone = OnRxDone; RadioEvents.TxTimeout = OnTxTimeout; RadioEvents.RxTimeout = OnRxTimeout; RadioEvents.RxError = OnRxError; RadioEvents.CadDone = OnCadDone;

六个回调覆盖了收发成功、收发超时、接收错误与 CAD 完成等全部关键异步事件,与库中RadioEvents_t的定义一一对应。

4.3 Radio 初始化与参数下发

Radio.Init(&RadioEvents); // 以事件结构体初始化 Radio 实例 Radio.Sleep(); // 先休眠,便于后续逐个配置 Radio.SetChannel(RF_FREQUENCY); // 设置工作信道(载波频率) Radio.SetTxConfig(MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, LORA_CODINGRATE, LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON, true, 0, 0, LORA_IQ_INVERSION_ON, TX_TIMEOUT_VALUE); Radio.SetRxConfig(MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH, LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, 0, true, 0, 0, LORA_IQ_INVERSION_ON, true);
  • MODEM_LORA指明使用 LoRa 调制(区别于 FSK 模式);
  • SetTxConfig的第 3 个参数 0 表示无需频率跳频表,true表示使能 TX 超时保护;
  • SetRxConfig末尾的true表示使能 RX 连续监听模式(single/continuous 选择)。

4.4 进入监听

Radio.Rx(RX_TIMEOUT_VALUE); // 开始监听,超时 3000ms

调用后硬件进入接收状态,之后的一切都由回调驱动,主循环不再做任何忙轮询。

五、低功耗核心:FreeRTOS 信号量事件驱动

原文档专门强调:不要让loop()空转,而是用信号量在 LoRa 事件到来时才唤醒 MCU。

源码中的实现要点:

SemaphoreHandle_t g_task_sem = NULL; // 事件唤醒信号量 volatile uint16_t g_task_event_type = NO_EVENT; // 事件位掩码

事件位定义:

宏值含义
NO_EVENT0b0000000000000000无事件
TX_FIN0b0000000000000001TX 完成
TX_ERR0b0000000000000010TX 超时
RX_FIN0b0000000000000100RX 完成
RX_ERR0b0000000000001000RX 超时/错误
CAD_FIN0b0000000000010000CAD 完成

工作机理:

  • setup()中xSemaphoreCreateBinary()创建信号量并先 Give 一次(确保初始可用);
  • loop()首行xSemaphoreTake(g_task_sem, portMAX_DELAY)——没有事件时任务永久阻塞,MCU 可进入低功耗;
  • 六个回调(如OnTxDone)只做两件事:g_task_event_type |= TX_FIN置位事件位,再xSemaphoreGive(g_task_sem)唤醒任务;
  • 被唤醒后,loop()进入while (g_task_event_type != NO_EVENT)循环,用位掩码&判定事件类型,处理完后用&= N_TX_FIN等方式清除对应位。

这种"中断置位 + 信号量唤醒 + 主循环统一处理"的模式,比在loop()里轮询标志位功耗更低,也让事件处理逻辑集中在单线程中,避免了并发竞争。

六、主从角色自动协商:Master / Slave 逻辑

例程用一个bool isMaster标志实现角色自协商,无需人工配置哪端先发:

  • 上电默认isMaster = true,等待 3 秒 RX 超时后由 Master 主动发 PING;
  • Master 收到 PONG:说明远端在线,delay(500)后执行 CAD,信道空闲则发下一个 PING;
  • Master 收到 PING:说明对方已是 Master,把自己降级为 Slave(isMaster = false),此后负责回 PONG;
  • Slave 收到 PING:执行 CAD,空闲则回复 PONG;
  • Slave 收到其他数据或 RX 超时:重新升级为 Master(isMaster = true)并重新发起 PING。

OnRxDone回调中的报文识别使用strncmp((const char *)RcvBuffer, (const char *)PingMsg, 4) == 0逐字节比对前 4 个字符"PING"/"PONG",其余字节由

for (int i = 4; i < BufferSize; i++) { TxdBuffer[i] = i - 4; // 用递增数字填充负载,方便观察 payload }

填充为递增序列,便于在串口或 BLE-UART 上肉眼核对数据完整性。

七、CAD:发包前的"先听后发"

每次发包前,代码统一执行:

Radio.Sleep(); Radio.SetCadParams(LORA_CAD_08_SYMBOL, LORA_SPREADING_FACTOR + 13, 10, LORA_CAD_ONLY, 0); cadTime = millis(); Radio.StartCad();
  • LORA_CAD_08_SYMBOL:CAD 检测窗口为 8 个符号;
  • 第 2 个参数LORA_SPREADING_FACTOR + 13:CAD 检测峰值阈值(detection peak),随扩频因子联动调整;
  • 第 3 个参数 10:CAD 检测最小值(detection minimum);
  • LORA_CAD_ONLY:检测后不自动切换 RX,交由代码显式控制;
  • 底层实现可在 radio.cpp 找到:RadioSetCadParams直接转发给SX126xSetCadParams((RadioLoRaCadSymbols_t)cadSymbolNum, cadDetPeak, cadDetMin, (RadioCadExitModes_t)cadExitMode, cadTimeout)。

OnCadDone(bool cadResult)返回后,主循环打印CAD returned channel busy/free after %ldms(用millis() - cadTime计算检测耗时)。信道忙则退回Radio.Rx(RX_TIMEOUT_VALUE)继续监听;信道空闲则按当前角色组装TxdBuffer并Radio.Send(TxdBuffer, BufferSize)发出。

八、BLE 调试通道:DFU + BLE-UART 双服务

例程同时启用了 Nordic nRF52 的 BLE(ble.ino,受#ifdef NRF52_SERIES保护,仅 nRF52 编译时生效):

  • DFU 服务(BLEDfu):Nordic 官方的 OTA 升级服务,用于在芯片上无线更新固件,先于其他服务bledfu.begin()以保证一致性;
  • BLE-UART 服务(BLEUart bleuart):把调试日志通过 BLE 转发出去,可配合 Android 上的 Serial Bluetooth Terminal 等 BLE-UART App 查看——这正是原文档中提到的调试方式。

初始化要点:

Bluefruit.begin(); Bluefruit.setTxPower(4); // 合法值:-40..4 dBm Bluefruit.setName("PPG_LORA_SX126x_TEST"); Bluefruit.Periph.setConnectCallback(connect_callback); Bluefruit.Periph.setDisconnectCallback(disconnect_callback); bleuart.begin();

广播参数:restartOnDisconnect(true)断线自动重播,setInterval(32, 244)(单位 0.625 ms,即快速模式约 20 ms、慢速模式约 152.5 ms),setFastTimeout(30)快速模式 30 秒超时,start(0)表示持续广播直到连接。

连接状态由bleUARTisConnected全局标志跟踪:connect_callback置true,disconnect_callback置false。主程序在每次事件分支中都会if (bleUARTisConnected) bleuart.print(...),把与串口相同的日志镜像到 BLE 通道,实现在不插线的情况下观察 LoRa 通信过程。

九、运行与测试:在两块 RAK4631 上验证

  1. 准备两块 RAK4631(或任意 SX126x 开发板,替换板级初始化函数并核对platformio.ini中LORA_*_PIN接线)。
  2. 用 PlatformIO 打开 PingPong-RAK4631 目录,编译并烧录到两块板。
  3. 串口以 115200 baud 打开(源码Serial.begin(115200)),两块板均能看到SX126x PingPong test与BoardId: xx-xx-...打印。
  4. 观察日志:Master 每 3 秒超时后经 CAD 发 PING;Slave 收到后回 PONG;每次接收打印RssiValue=xx dBm, SnrValue=xx,可作为测距与天线效果的直接依据。
  5. 如需无线调试,用 BLE-UART App 连接PPG_LORA_SX126x_TEST,即可看到与串口同步的日志。

十、常见问题与调参建议

  • 两端收不到对方:优先检查频率、SF、带宽、编码率、前导码、IQ 反转是否一致(见第三节参数表);
  • 距离不够:在法规允许范围内提高TX_OUTPUT_POWER,或将LORA_SPREADING_FACTOR从 7 调高到 9/12(速率变慢但灵敏度显著提升),或将带宽从 125 kHz 换到更窄配置;
  • 频繁丢包:确认 CAD 参数(LORA_CAD_08_SYMBOL、LORA_SPREADING_FACTOR + 13阈值、10 的最小检测值)是否与当前 SF 匹配;两节点间距过近导致接收饱和时可适当降低功率;
  • 低功耗无效:确认loop()确实阻塞在xSemaphoreTake上,且没有任何delay()长驻路径(Master 分支的delay(500)仅在收到 PONG 后短暂执行,属有意为之的节奏控制)。

结语

通过本例程,可以完整掌握 SX126x-Arduino 库的事件驱动编程范式:RadioEvents回调 + 信号量唤醒 + 位掩码事件分发,加上 CAD 先听后发与主从自协商,构成了一个稳健、省电、可实际用于链路评估的 LoRa P2P 通信骨架。它也是理解本仓库 Sub-GHz/LoRa 能力入口的最佳范例——将这套初始化与事件处理模式迁移到其他板卡与协议场景,仅需替换板级初始化和回调内容即可。相关源码与库均可在仓库内继续深入研读:例程目录、Radio 底层实现、板级初始化。

  • 嵌入式
  • 硬件开发

【免费下载链接】ESP32-Bit-Pirate

A Hardware Hacking Tool with Web-Based CLI That Speaks Every Protocol

项目地址:https://gitcode.com/GitHub_Trending/es/ESP32-Bit-Pirate
点击查看免费下载
上一篇:三步搞定智能聊天机器人:麦麦AI助手QQ群管理实战指南
下一篇:革命性AI图像翻译工具CUT:快速实现无配对图像转换的终极指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 2:00:24

如何搭建自己的开源法律助手:3 款中文法律大模型选型与部署指南

如何搭建自己的开源法律助手&#xff1a;3 款中文法律大模型选型与部署指南 【免费下载链接】Awesome-Chinese-LLM 整理开源的中文大语言模型&#xff0c;以规模较小、可私有化部署、训练成本较低的模型为主&#xff0c;包括底座模型&#xff0c;垂直领域微调及应用&#xff0c…

作者头像 李华
网站建设 2026/10/3 1:58:39

STM32F1自定义HID实战:寄存器级USB协议栈开发

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华