4. LoRa点对点通信实验:从硬件到空中的完整链路
好,咱们进入第四章。这一章我打算带大家亲手搭建一个LoRa点对点通信链路。说白了,就是让两个ESP32通过SX1278模块互相喊话。你想想看,没有网关、没有服务器,两个节点就能在几公里外通信——这就是LoRa最迷人的地方。
我在做第一个LoRa项目时,就是从这个实验开始的。当时在实验室里,一个模块放在桌上,另一个拿到走廊尽头,看着串口打印出"Received: Hello World!",那种感觉,嗯,真的很爽。
4.1 硬件连接图:别小看这几根线
先看硬件。SX1278模块和ESP32的连接,我习惯用SPI接口。为什么?因为SPI速度快,而且LoRa芯片本身就是SPI从设备。
这里给出我常用的接线方案:
| SX1278引脚 | ESP32引脚 | 说明 |
|---|---|---|
| NSS | GPIO5 | 片选,低电平有效 |
| SCK | GPIO18 | SPI时钟 |
| MOSI | GPIO23 | 主机输出,从机输入 |
| MISO | GPIO19 | 主机输入,从机输出 |
| RST | GPIO14 | 复位引脚 |
| DIO0 | GPIO2 | 中断引脚,用于接收完成标志 |
供电方面,SX1278工作电压3.3V,千万别接5V。ESP32的3.3V输出电流足够驱动它。如果你用电池供电,记得加个100μF的电解电容滤波——我在项目中遇到过因为电源纹波导致通信距离缩短的情况。
4.2 SPI总线配置:时钟频率别贪快
SPI配置其实不难,但有个坑——时钟频率。SX1278的数据手册说最高支持10MHz,但我个人建议用1MHz左右。为什么?
你想想看,LoRa通信本身速率就很低(几kbps),SPI再快也快不到哪去。而且ESP32的SPI在高速下容易受干扰,尤其是飞线连接时。我试过用8MHz,结果偶尔出现寄存器读写错误,降到1MHz后稳如老狗。
代码示例:
#include <SPI.h>
#define SS_PIN 5
#define RST_PIN 14
#define DIO0_PIN 2
void setup() {
Serial.begin(115200);
// 配置SPI
SPI.begin(18, 19, 23, 5); // SCK, MISO, MOSI, SS
SPI.setFrequency(1000000); // 1MHz,稳定第一
SPI.setDataMode(SPI_MODE0); // 模式0:极性0,相位0
pinMode(SS_PIN, OUTPUT);
digitalWrite(SS_PIN, HIGH); // 片选默认高电平
pinMode(RST_PIN, OUTPUT);
digitalWrite(RST_PIN, HIGH);
Serial.println("SPI初始化完成");
}
4.3 LoRa寄存器初始化:核心配置三步走
寄存器初始化是LoRa通信的灵魂。SX1278有上百个寄存器,但咱们只需要配置几个关键的。我把它总结为三步:
- 复位模块:拉低RST引脚10ms,再拉高。让芯片回到默认状态。
- 进入LoRa模式:写寄存器RegOpMode(地址0x01),把bit7置1。
- 配置射频参数:频率、扩频因子、带宽、编码率、输出功率。
代码实现:
// 寄存器操作函数
void LoRa_writeReg(uint8_t addr, uint8_t value) {
digitalWrite(SS_PIN, LOW);
SPI.transfer(addr | 0x80); // 写操作,最高位置1
SPI.transfer(value);
digitalWrite(SS_PIN, HIGH);
}
uint8_t LoRa_readReg(uint8_t addr) {
digitalWrite(SS_PIN, LOW);
SPI.transfer(addr & 0x7F); // 读操作,最高位清0
uint8_t value = SPI.transfer(0x00);
digitalWrite(SS_PIN, HIGH);
return value;
}
void LoRa_init() {
// 第一步:复位
digitalWrite(RST_PIN, LOW);
delay(10);
digitalWrite(RST_PIN, HIGH);
delay(10);
// 第二步:进入LoRa模式
LoRa_writeReg(0x01, 0x80); // RegOpMode: LoRa模式,睡眠状态
delay(100);
// 第三步:配置射频参数
// 频率:868MHz(欧洲频段,国内用CN470需调整)
uint64_t freq = 868000000;
uint64_t frf = (uint64_t)freq * 524288 / 32000000;
LoRa_writeReg(0x06, (frf >> 16) & 0xFF); // RegFrMsb
LoRa_writeReg(0x07, (frf >> 8) & 0xFF); // RegFrMid
LoRa_writeReg(0x08, frf & 0xFF); // RegFrLsb
// 扩频因子SF=7,带宽125kHz,编码率4/5
LoRa_writeReg(0x1D, 0x72); // RegModemConfig1: BW=125kHz, CR=4/5
LoRa_writeReg(0x1E, 0x74); // RegModemConfig2: SF=7, 使能CRC
// 输出功率:17dBm(约50mW)
LoRa_writeReg(0x09, 0xFF); // RegPaConfig: 最大功率
LoRa_writeReg(0x4D, 0x87); // RegPaDac: 开启+20dBm模式(慎用)
// 进入待机模式
LoRa_writeReg(0x01, 0x81); // RegOpMode: LoRa模式,待机
Serial.println("LoRa初始化完成");
}
4.4 发送与接收数据:中断驱动更靠谱
发送和接收,我建议用中断方式。轮询虽然简单,但浪费CPU。LoRa模块的DIO0引脚在发送完成或接收到数据时会触发中断。
发送流程:
- 清空FIFO,写入要发送的数据
- 设置发送模式,启动发送
- 等待DIO0中断(或轮询RegIrqFlags)
- 检查发送完成标志
接收流程:
- 设置接收模式,等待数据
- DIO0中断触发,读取FIFO
- 检查CRC,提取有效数据
代码示例:
void LoRa_send(uint8_t *data, uint8_t len) {
// 写入FIFO
LoRa_writeReg(0x0D, 0x00); // RegFifoAddrPtr: 从地址0开始写
for (int i = 0; i < len; i++) {
LoRa_writeReg(0x00, data[i]); // RegFifo
}
LoRa_writeReg(0x22, len); // RegPayloadLength
// 启动发送
LoRa_writeReg(0x01, 0x83); // RegOpMode: LoRa模式,发送
// 等待发送完成(实际项目中用中断)
while (!(LoRa_readReg(0x12) & 0x08)); // RegIrqFlags: TxDone
LoRa_writeReg(0x12, 0x08); // 清除标志
LoRa_writeReg(0x01, 0x81); // 回到待机
}
void LoRa_receive() {
LoRa_writeReg(0x01, 0x85); // RegOpMode: LoRa模式,连续接收
// 等待接收完成(实际项目中用中断)
while (!(LoRa_readReg(0x12) & 0x40)); // RegIrqFlags: RxDone
LoRa_writeReg(0x12, 0x40); // 清除标志
// 读取数据
uint8_t len = LoRa_readReg(0x13); // RegRxNbBytes
LoRa_writeReg(0x0D, LoRa_readReg(0x10)); // RegFifoAddrPtr: 读取当前指针
uint8_t buffer[256];
for (int i = 0; i < len; i++) {
buffer[i] = LoRa_readReg(0x00);
}
// 检查CRC
if (LoRa_readReg(0x12) & 0x20) { // PayloadCrcError
Serial.println("CRC错误,数据丢弃");
} else {
Serial.print("收到数据:");
Serial.write(buffer, len);
Serial.println();
}
}
4.5 空中速率与距离测试:理论vs实际
好了,硬件通了,代码跑了,接下来就是最激动人心的部分——测试。
先看理论速率。LoRa的空中速率计算公式:
BitRate = SF × BW / (2^SF) × CR
其中SF是扩频因子,BW是带宽(Hz),CR是编码率(4/5=0.8)。
举个例子:SF=7,BW=125kHz,CR=4/5
BitRate = 7 × 125000 / 128 × 0.8 ≈ 5468 bps
嗯,你没看错,只有5kbps。但换来的是-130dBm的灵敏度,比WiFi强了30dB。
| 扩频因子 | 速率(bps) | 灵敏度(dBm) | 典型距离(开阔地) |
|---|---|---|---|
| SF7 | 5468 | -123 | 2-3公里 |
| SF9 | 1465 | -129 | 5-8公里 |
| SF12 | 293 | -134 | 10-15公里 |
实际测试时,我建议这样操作:
- 先近距离(10米)验证通信正常
- 然后逐步拉远,每次增加100米
- 记录RSSI(接收信号强度)和SNR(信噪比)
- 当RSSI低于-120dBm时,基本就到极限了
最后说一句:LoRa点对点通信,核心是理解"速率换距离"这个trade-off。你追求距离,就得忍受低速率。反过来,想要高速率,就别指望传太远。这个道理,在后续的组网实验中会反复用到。
好,这一章就到这里。下一章咱们开始玩真正的LoRaWAN协议栈——那才是重头戏。