DX-LR22-433T22S

元器件
通信模块
库存 500

介绍

大夏龙雀(DX) LoRa扩频透传模块,基于SX1262射频芯片,433-532MHz频段,最大发射功率22dBm(160mW),UART串口AT指令透传,支持点对点/定点/广播模式,通信距离可达8km(空旷),支持LBT监听、ADR自适应速率、CAD唤醒。适用于物联网远距离低速数据传输。

规格参数

参数
尺寸约20×14×3mm
缓存1KB收发FIFO
休眠电流<3uA
发射功率最大22dBm (约160mW)
发射电流约130mA @22dBm
天线接口IPEX-1 / 半孔焊盘
射频芯片SX1262
工作温度-40°C~+85°C
工作电压2.8V~5.5V (典型3.3V)
接口类型UART TTL (3.3V电平)
接收电流约12mA
空中速率0.3kbps~62.5kbps (LoRa), 最高300kbps (FSK)
组网方式点对点/定点/广播
调制方式LoRa/FSK/GFSK
通信距离空旷>5km @SF12 0.3kbps
频率范围410-441MHz (默认433MHz)
高级功能LBT, CAD, ADR, RSSI上报
接收灵敏度-148dBm @ SF12 BW125kHz

代码例程

DX-LR22-433T22S LoRa模块驱动代码(ESP32/Arduino).md

# DX-LR22-433T22S LoRa 模块驱动代码

以下代码基于 **ESP32 (Arduino框架)** 编写,使用 HardwareSerial2 连接 DX-LR22 模块,实现透传收发、帧同步状态机、AT 指令配置及诊断功能。

---

## 1. 头文件 `dx_lr22.h`

```cpp
/**
 * @file    dx_lr22.h
 * @brief   DX-LR22-433T22S 大夏龙雀 LoRa 透传模块驱动
 * @note    基于 SX1262,UART AT 透传,433MHz / +22dBm
 */

#ifndef DX_LR22_H__
#define DX_LR22_H__

#include <Arduino.h>

// ===================== 硬件引脚定义 (按实际接线修改) =====================
#ifndef LORA_UART_RX
#define LORA_UART_RX     16    // ESP32 GPIO16 → 模块 TXD
#endif
#ifndef LORA_UART_TX
#define LORA_UART_TX     17    // ESP32 GPIO17 → 模块 RXD
#endif
#ifndef LORA_AUX_PIN
#define LORA_AUX_PIN     4     // ESP32 GPIO4  ← 模块 AUX (可选, 状态检测)
#endif
#ifndef LORA_MD0_PIN
#define LORA_MD0_PIN     18    // 模式配置脚 MD0
#endif
#ifndef LORA_MD1_PIN
#define LORA_MD1_PIN     19    // 模式配置脚 MD1
#endif

// ===================== 常量配置 =====================
#define LORA_UART_BAUD   9600          // 默认透传波特率
#define LORA_SERIAL      Serial2      // ESP32 HardwareSerial2
#define LORA_RX_BUF_SIZE 1024         // 接收缓冲区大小

// ===================== 帧协议常量 =====================
#define FRAME_SYNC0      0xA5         // 同步字第一字节
#define FRAME_SYNC1      0x5A         // 同步字第二字节
#define FRAME_PREAMBLE   0xAA         // 前导填充字节

// ===================== 返回码 =====================
enum {
    LORA_OK       = 0,
    LORA_TIMEOUT  = -1,
    LORA_BUSY     = -2,
    LORA_ERROR    = -3,
};

// ===================== 工作模式 =====================
enum LoraMode : uint8_t {
    MODE_TRANSPARENT = 0x00,  // MD1=0 MD0=0  透传
    MODE_AT          = 0x01,  // MD1=0 MD0=1  AT配置
    MODE_CONFIG2     = 0x02,  // MD1=1 MD0=0  保留
    MODE_DEEPSLEEP   = 0x03,  // MD1=1 MD0=1  深度休眠
};

// ===================== 帧接收状态机状态 =====================
enum RxState : int {
    ST_SYNC0,   // 等待 0xA5
    ST_SYNC1,   // 等待 0x5A
    ST_BODY,    // 接收帧体
};

// ===================== API 声明 =====================

// 初始化
void dx_lr22_init(void);
void dx_lr22_setMode(LoraMode mode);

// 透传发送
void dx_lr22_send(const uint8_t *data, uint16_t len);
void dx_lr22_sendStr(const char *str);

// 透传接收 (非阻塞)
bool dx_lr22_recv(uint8_t *frame, uint16_t frame_size);

// AT 指令
int dx_lr22_sendAT(const char *cmd, uint32_t timeout_ms = 500);
int dx_lr22_checkResponse(const char *expected, uint32_t timeout_ms = 500);
int dx_lr22_atCmd(const char *cmd, char *resp, uint16_t respMax, uint32_t timeout_ms = 500);

// 状态与诊断
bool dx_lr22_isIdle(void);
uint32_t dx_lr22_diag_bytes(void);
int dx_lr22_getRSSI(void);

// 循环回调 (放在 loop() 中用于异步处理)
void dx_lr22_task(void);

#endif // DX_LR22_H__
```

---

## 2. 驱动实现 `dx_lr22.cpp`

```cpp
/**
 * @file    dx_lr22.cpp
 * @brief   DX-LR22-433T22S 驱动实现
 */

#include "dx_lr22.h"

// ===================== 内部状态 =====================
static RxState    rx_state   = ST_SYNC0;
static int        rx_idx     = 0;
static uint32_t   raw_bytes  = 0;
static uint8_t    rx_buf[256];       // AT响应缓存
static uint16_t   rx_buf_len = 0;

// ===================== 初始化 =====================

void dx_lr22_init(void)
{
    // 配置模式引脚
    pinMode(LORA_MD0_PIN, OUTPUT);
    pinMode(LORA_MD1_PIN, OUTPUT);
    
    // 默认进入透传模式
    dx_lr22_setMode(MODE_TRANSPARENT);
    
    // 初始化 AUX 引脚 (输入上拉)
    pinMode(LORA_AUX_PIN, INPUT_PULLUP);
    
    // 配置 UART
    LORA_SERIAL.setRxBufferSize(LORA_RX_BUF_SIZE);
    LORA_SERIAL.begin(LORA_UART_BAUD, SERIAL_8N1, LORA_UART_RX, LORA_UART_TX);
    
    Serial.printf("[LORA] Serial2 initialized: %u baud, RX=GPIO%d, TX=GPIO%d\n",
                  (unsigned)LORA_UART_BAUD, LORA_UART_RX, LORA_UART_TX);
    
    // 排空上电残留字节
    delay(100);
    while (LORA_SERIAL.available()) {
        LORA_SERIAL.read();
    }
    
    raw_bytes  = 0;
    rx_buf_len = 0;
    rx_state   = ST_SYNC0;
    rx_idx     = 0;
    
    Serial.println("[LORA] DX-LR22 initialized OK");
}

// ===================== 模式切换 =====================

void dx_lr22_setMode(LoraMode mode)
{
    digitalWrite(LORA_MD0_PIN, (mode & 0x01) ? HIGH : LOW);
    digitalWrite(LORA_MD1_PIN, (mode & 0x02) ? HIGH : LOW);
    delay(20);  // 模式切换稳定时间
}

// ===================== AUX 状态读取 =====================

bool dx_lr22_isIdle(void)
{
    // AUX 高电平 = 空闲可写
    return digitalRead(LORA_AUX_PIN) == HIGH;
}

// ===================== 透传发送 =====================

void dx_lr22_send(const uint8_t *data, uint16_t len)
{
    // 4 字节前导 → 帮助对端 LoRa 模块 RF 链路训练
    static const uint8_t preamble[4] = { 0xAA, 0xAA, 0xAA, 0xAA };
    
    // 等待 AUX 空闲 (简单超时保护)
    uint32_t t0 = millis();
    while (!dx_lr22_isIdle()) {
        if (millis() - t0 > 100) {
            Serial.println("[LORA] WARNING: AUX timeout, force send");
            break;
        }
        delay(1);
    }
    
    LORA_SERIAL.write(preamble, sizeof(preamble));
    LORA_SERIAL.write(data, len);
    LORA_SERIAL.flush();
}

void dx_lr22_sendStr(const char *str)
{
    dx_lr22_send((const uint8_t *)str, strlen(str));
}

// ===================== 透传接收 (帧同步状态机) =====================

bool dx_lr22_recv(uint8_t *frame, uint16_t frame_size)
{
    while (LORA_SERIAL.available()) {
        uint8_t b = (uint8_t)LORA_SERIAL.read();
        raw_bytes++;
        
        switch (rx_state) {
        case ST_SYNC0:
            if (b == FRAME_SYNC0) {
                rx_state = ST_SYNC1;
            }
            break;
            
        case ST_SYNC1:
            if (b == FRAME_SYNC1) {
                // 同步字匹配, 开始接收帧体
                frame[0] = FRAME_SYNC0;
                frame[1] = FRAME_SYNC1;
                rx_idx   = 2;
                rx_state = ST_BODY;
            } else if (b == FRAME_SYNC0) {
                // 保持 ST_SYNC1, 可能连续收到同步字
                rx_state = ST_SYNC1;
            } else {
                rx_state = ST_SYNC0;
            }
            break;
            
        case ST_BODY:
            frame[rx_idx++] = b;
            if (rx_idx >= frame_size) {
                rx_state = ST_SYNC0;
                return true;  // 帧接收完成
            }
            break;
            
        default:
            rx_state = ST_SYNC0;
            break;
        }
    }
    return false;
}

// ===================== AT 指令 =====================

int dx_lr22_sendAT(const char *cmd, uint32_t timeout_ms)
{
    rx_buf_len = 0;
    
    // 确保在 AT 模式
    dx_lr22_setMode(MODE_AT);
    delay(10);
    
    LORA_SERIAL.print(cmd);
    LORA_SERIAL.print("\r\n");
    LORA_SERIAL.flush();
    
    (void)timeout_ms;
    return LORA_OK;
}

int dx_lr22_checkResponse(const char *expected, uint32_t timeout_ms)
{
    uint32_t start = millis();
    
    while ((millis() - start) < timeout_ms) {
        while (LORA_SERIAL.available()) {
            char c = (char)LORA_SERIAL.read();
            if (rx_buf_len < sizeof(rx_buf) - 1) {
                rx_buf[rx_buf_len++] = (uint8_t)c;
            }
        }
        rx_buf[rx_buf_len] = '\0';
        
        if (rx_buf_len > 0 && strstr((char *)rx_buf, expected) != NULL) {
            return LORA_OK;
        }
        delay(10);
    }
    return LORA_TIMEOUT;
}

int dx_lr22_atCmd(const char *cmd, char *resp, uint16_t respMax, uint32_t timeout_ms)
{
    // 切换到 AT 模式
    dx_lr22_setMode(MODE_AT);
    delay(10);
    
    // 排空缓冲区
    while (LORA_SERIAL.available()) LORA_SERIAL.read();
    rx_buf_len = 0;
    
    // 发送指令
    LORA_SERIAL.print(cmd);
    LORA_SERIAL.print("\r\n");
    LORA_SERIAL.flush();
    
    // 等待响应
    uint32_t start = millis();
    while ((millis() - start) < timeout_ms) {
        while (LORA_SERIAL.available()) {
            char c = (char)LORA_SERIAL.read();
            if (rx_buf_len < sizeof(rx_buf) - 1) {
                rx_buf[rx_buf_len++] = (uint8_t)c;
            }
        }
        rx_buf[rx_buf_len] = '\0';
        
        // 检测结束标志 (+OK 或 +ERR)
        if (strstr((char *)rx_buf, "+OK") || strstr((char *)rx_buf, "+ERR")) {
            if (resp) {
                uint16_t copyLen = min(rx_buf_len, (uint16_t)(respMax - 1));
                memcpy(resp, rx_buf, copyLen);
                resp[copyLen] = '\0';
            }
            return (strstr((char *)rx_buf, "+OK")) ? LORA_OK : LORA_ERROR;
        }
        delay(10);
    }
    
    // 超时
    if (resp && rx_buf_len > 0) {
        uint16_t copyLen = min(rx_buf_len, (uint16_t)(respMax - 1));
        memcpy(resp, rx_buf, copyLen);
        resp[copyLen] = '\0';
    }
    return LORA_TIMEOUT;
}

// ===================== RSSI 查询 =====================

int dx_lr22_getRSSI(void)
{
    char resp[64];
    int ret = dx_lr22_atCmd("AT+RSSI?", resp, sizeof(resp), 500);
    if (ret == LORA_OK) {
        // 解析 "+OK=RSSI:-85dBm" 之类
        int rssi = 0;
        if (sscanf(resp, "+OK=RSSI:%d", &rssi) == 1) {
            return rssi;
        }
    }
    return 0;  // 无效值
}

// ===================== 诊断 =====================

uint32_t dx_lr22_diag_bytes(void)
{
    uint32_t n = raw_bytes;
    raw_bytes = 0;
    return n;
}

// ===================== 异步任务 (周期性调用) =====================

void dx_lr22_task(void)
{
    // 在透传模式下, 这里可以处理帧分发逻辑
    // 例: 收帧 → 校验 → 回调上层
    // 具体实现取决于应用帧格式
}
```

---

## 3. 完整应用示例 `main.ino`

```cpp
/**
 * @file    main.ino
 * @brief   DX-LR22-433T22S 完整使用示例
 * 
 * 硬件连接 (ESP32):
 *   GPIO16 ← 模块 TXD
 *   GPIO17 → 模块 RXD
 *   GPIO4  ← 模块 AUX
 *   GPIO18 → 模块 MD0
 *   GPIO19 → 模块 MD1
 * 
 * 流程:
 *   1. 上电后进入 AT 模式, 查询/配置模块参数
 *   2. 切换到透传模式
 *   3. 周期性发送传感器数据
 *   4. 非阻塞接收远端数据
 */

#include "dx_lr22.h"

// ===================== 应用帧格式 =====================
#pragma pack(push, 1)
typedef struct {
    uint8_t  sync0;        // 0xA5
    uint8_t  sync1;        // 0x5A
    uint8_t  type;         // 帧类型: 0x01=传感器上报, 0x02=控制指令
    uint16_t seq;          // 序列号
    float    temperature;  // 温度 °C
    float    humidity;     // 湿度 %
    uint16_t battery_mv;   // 电池电压 mV
    uint16_t crc16;        // CRC16 校验
} sensor_frame_t;
#pragma pack(pop)

#define SENSOR_FRAME_SIZE sizeof(sensor_frame_t)
static_assert(SENSOR_FRAME_SIZE == 17, "Frame size mismatch");

// CRC16-CCITT 查表实现
static uint16_t crc16_ccitt(const uint8_t *data, uint16_t len)
{
    uint16_t crc = 0xFFFF;
    for (uint16_t i = 0; i < len; i++) {
        crc ^= (uint16_t)data[i] << 8;
        for (uint8_t j = 0; j < 8; j++) {
            crc = (crc & 0x8000) ? (crc << 1) ^ 0x1021 : crc << 1;
        }
    }
    return crc;
}

// ===================== 全局变量 =====================
static sensor_frame_t rx_frame;
static uint16_t       tx_seq = 0;

// ===================== 模拟传感器读取 =====================
static float read_temperature(void) { return 25.6f + (random(-50, 50) / 100.0f); }
static float read_humidity(void)    { return 58.2f + (random(-30, 30) / 100.0f); }
static uint16_t read_battery(void)  { return 3300; }

// ===================== 发送传感器帧 =====================
static void send_sensor_frame(void)
{
    sensor_frame_t tx;
    tx.sync0       = FRAME_SYNC0;
    tx.sync1       = FRAME_SYNC1;
    tx.type        = 0x01;               // 传感器上报
    tx.seq         = tx_seq++;
    tx.temperature = read_temperature();
    tx.humidity    = read_humidity();
    tx.battery_mv  = read_battery();
    
    // 计算 CRC (跳过 CRC 字段自身)
    tx.crc16 = crc16_ccitt((uint8_t *)&tx, SENSOR_FRAME_SIZE - 2);
    
    dx_lr22_send((uint8_t *)&tx, SENSOR_FRAME_SIZE);
    
    Serial.printf("[TX] seq=%u temp=%.1f°C hum=%.1f%% batt=%umV\n",
                  tx.seq, tx.temperature, tx.humidity, tx.battery_mv);
}

// ===================== 处理接收帧 =====================
static void handle_frame(const sensor_frame_t *f)
{
    // CRC 校验
    uint16_t crc_calc = crc16_ccitt((const uint8_t *)f, SENSOR_FRAME_SIZE - 2);
    if (crc_calc != f->crc16) {
        Serial.printf("[RX] CRC FAIL: calc=0x%04X got=0x%04X\n", crc_calc, f->crc16);
        return;
    }
    
    Serial.printf("[RX] seq=%u type=%u temp=%.1f°C hum=%.1f%% batt=%umV\n",
                  f->seq, f->type, f->temperature, f->humidity, f->battery_mv);
}

// ===================== AT 配置流程 =====================
static void configure_module(void)
{
    char resp[128];
    int ret;
    
    Serial.println("=== DX-LR22 AT Configuration ===");
    
    // 1. 测试通信
    ret = dx_lr22_atCmd("AT", resp, sizeof(resp), 500);
    Serial.printf("AT test: %s → %s\n", (ret == LORA_OK) ? "OK" : "FAIL", resp);
    
    // 2. 查询版本
    ret = dx_lr22_atCmd("AT+VER?", resp, sizeof(resp), 500);
    Serial.printf("Firmware: %s\n", resp);
    
    // 3. 查询当前频率
    ret = dx_lr22_atCmd("AT+FREQ?", resp, sizeof(resp), 500);
    Serial.printf("Frequency: %s\n", resp);
    
    // 4. 查询扩频因子
    ret = dx_lr22_atCmd("AT+SF?", resp, sizeof(resp), 500);
    Serial.printf("Spreading Factor: %s\n", resp);
    
    // 5. 查询带宽
    ret = dx_lr22_atCmd("AT+BW?", resp, sizeof(resp), 500);
    Serial.printf("Bandwidth: %s\n", resp);
    
    // 6. 查询功率
    ret = dx_lr22_atCmd("AT+POWER?", resp, sizeof(resp), 500);
    Serial.printf("TX Power: %s\n", resp);
    
    // 7. 查询 RSSI (当前环境底噪)
    ret = dx_lr22_atCmd("AT+RSSI?", resp, sizeof(resp), 500);
    Serial.printf("RSSI: %s\n", resp);
    
    Serial.println("=== Configuration Done ===");
}

// ===================== Setup =====================
void setup()
{
    Serial.begin(115200);
    delay(500);
    Serial.println("\n===================================");
    Serial.println("  DX-LR22-433T22S LoRa Demo");
    Serial.println("===================================");
    
    // 随机种子
    randomSeed(analogRead(0));
    
    // 初始化 LoRa 模块 (默认进入透传模式)
    dx_lr22_init();
    
    // 进入 AT 模式进行配置
    configure_module();
    
    // 切回透传模式
    dx_lr22_setMode(MODE_TRANSPARENT);
    Serial.println("[MAIN] Enter transparent mode, ready to communicate.");
}

// ===================== Loop =====================
void loop()
{
    static uint32_t last_tx_time = 0;
    static uint32_t last_diag_time = 0;
    
    // ---- 非阻塞接收 ----
    if (dx_lr22_recv((uint8_t *)&rx_frame, SENSOR_FRAME_SIZE)) {
        handle_frame(&rx_frame);
    }
    
    // ---- 周期性发送 (每 5 秒) ----
    if (millis() - last_tx_time >= 5000) {
        last_tx_time = millis();
        send_sensor_frame();
    }
    
    // ---- 诊断打印 (每 10 秒) ----
    if (millis() - last_diag_time >= 10000) {
        last_diag_time = millis();
        uint32_t bytes = dx_lr22_diag_bytes();
        Serial.printf("[DIAG] UART raw bytes in 10s: %u\n", (unsigned)bytes);
    }
    
    // ---- 异步任务 ----
    dx_lr22_task();
    
    delay(10);  // 避免空转占满 CPU
}
```

---

## 4. 使用说明

### 4.1 编译与烧录

1. 将 `dx_lr22.h` 和 `dx_lr22.cpp` 放入 Arduino 项目的 `src/` 目录
2. `main.ino` 放在项目根目录
3. 根据实际接线修改 `dx_lr22.h` 中的 GPIO 宏定义
4. 选择 ESP32 Dev Module → 编译上传

### 4.2 两模块对传测试

- 准备两套 ESP32 + DX-LR22 模块
- 两模块参数保持一致:频率 433MHz、SF12、BW125kHz
- 烧录同一份固件
- 打开串口监视器,观察每 5 秒收发一次传感器帧

### 4.3 关键调试点

| 现象 | 排查方向 |
|------|----------|
| 收不到数据 | 检查天线是否接好;双方频率/SF/BW是否一致;AUX是否正常 |
| 帧 CRC 频繁失败 | 空中误码率高 → 降低SF或减小距离再测 |
| AT 指令无响应 | 确认 MD1=0 MD0=1;波特率9600;模块供电正常 |
| AUX 始终为低 | 模块可能处于繁忙/死锁 → 硬件复位后再试 |

### 4.4 前导码 + 同步字设计说明

本驱动采用 **4字节前导(0xAA) + 2字节同步字(0xA5 0x5A)** 的帧格式。设计考量:

- **0xAA 前导:** 0xAA = 0b10101010,提供丰富的边沿跳变,帮助对端 LoRa 模块的 PLL 和 AGC 快速锁定。
- **0xA5 0x5A 同步字:** 互补字节对,汉明距离大,误同步概率低。状态机在 ST_SYNC0→ST_SYNC1→ST_BODY 三级跳转中能正确处理同步字内嵌和噪声干扰场景。
- **帧体 CRC:** 应用层做 CRC16 校验,确保数据完整性(LoRa 物理层虽有 CRC,但端到端校验更可靠)。

参考资料

暂无参考文献