LoRaWAN ESP32-S3

1. 产品描述

1.1 产品简介

LoRaWAN ESP32-S3 开发板是一款聚焦远距离无线通讯与便捷开发的专业硬件,凭借全方位LoRa、LoRaWAN 、Meshtastic协议兼容、HomeAssistant生态接入能力以及高易用性的硬件功能设计,为LoRa/LoRaWAN开发者与智能家居用户提供高效解决方案。

全方位支持 LoRa/LoRaWAN/Meshtastic 通讯协议,通讯兼容性强
LoRaWAN ESP32-S3开发板支持LoRa一对一通讯、LoRa一对多通讯,以及标准的LoRaWAN协议和Meshtastic组网协议,可以自由的实现开发板和开发板之间的通讯,或接入到LoRaWAN网络中。开发板采用Arduino编程,提供了易用的库文件和示例代码,无需深入了解LoRa/LoRaWAN 通讯协议协议即可使用,大幅度降低了上手门槛。

支持接入HomeAssistant(ESPHome),扩展远距离通讯能力
LoRaWAN ESP32-S3开发板支持通过ESPHome接入HomeAssistant,成为系统的 “远距离通讯拓展单元”。有效突破传统无线通讯的距离限制,提升复杂环境下(如多层建筑、大户型、户外场景)的信号覆盖与传输稳定性,拓展智能家居设备的部署范围。

功能丰富易用性高,显著提升测试效率
LoRaWAN ESP32-S3开发板在功能设计上以 “高效易用” 为核心,通过高硬件集成,显著减少测试准备时间与设备连接复杂度:

  • 可视化操作与数据查看:集成 0.96 寸LCD屏幕与实体按键,无需依赖外部设备,即可实时查看设备运行数据、通讯状态等关键信息,同时通过按键快速切换显示界面,操作直观便捷。
  • 便携续航,摆脱电源束缚:专门设计锂电池接口,并集成充电功能,支持外接锂电池供电,无论是室内桌面测试,还是户外移动场景使用,都能摆脱固定电源限制,使用场景更灵活。
  • 易用IO接口,轻松连接传感器:配备便捷 IO 接口,无需焊接即可直接连接温湿度、光照、人体感应等各类传感器设备,解决项目搭建过程中繁琐的接线和焊接工作,高效完成项目原型的构建。

1.2 产品特性

  • 支持LoRa、LoRaWAN、Meshtastic远距离通讯协议
  • 支持接入HomeAssistant(ESPHome),使HomeAssistant系统支持远距离通讯
  • 搭载ESP32-S3主控,支持Arduino、yaml编程
  • 功能丰富易用性高,显著提升测试效率
    • 集成0.96寸屏幕、按键,便于查看屏幕数据和切换显示
    • 集成多组电源、I2C接口,便于连接传感器设备
    • 集成锂电池接口和充电功能,便于便携使用

1.3 应用场景

  • LoRa/LoRaWAN网络部署调试:通过开发板屏幕测试查看 LoRaWAN 网络信号、通讯状态,快速搭建和调整网络。
  • 大户型 / 别墅智能家居扩展:在多层别墅或跨庭院住宅中,轻松连接花园传感器、车库设备与室内控制系统,解决墙体阻隔导致的信号断层问题,实现全屋设备统一管理。
  • 户外环境监测场景:搭载温湿度、PM2.5 等传感器,部署于阳台、屋顶或庭院,通过远距离通讯实时回传数据至 HomeAssistant 系统,联动空调、新风设备自动调节室内环境。
  • 农业 / 园艺远程管理:放置于菜园、温室或果园中,连接土壤湿度、光照传感器,即使在数百米外的室内也能实时掌握作物生长环境,触发灌溉系统自动作业。
  • Meshtastic聊天设备:在偏远地区、山区徒步、森林探险、沙漠穿越等信号薄弱或无网络的户外场景中,借助 Meshtastic 协议构建去中心化的无线电通讯网络,可实时发送文字消息。

2. 技术规格

2.1 产品参数

基本参数

  • 工作电压: 3.3V
  • Type-C输入电压: 5V DC
  • 最大充电电流:300mA
  • 屏幕尺寸:0.96寸(160*80)
  • 工作温度:-10~60℃
  • 模块尺寸:73*45mm

LoRa参数

  • 射频芯片:SX1262
  • 工作频段:850~930MHz
  • 发射功率:16dBm(EU868)/22dBm(US915)
  • 接收灵敏度:-137dBm /125kHz SF=12

ESP32-S3参数

  • 处理器:Xtensa® 双核32位LX7微处理器
  • 主频:240MHz
  • SRAM:512KB
  • ROM:384KB
  • Flash:4MB
  • 无线协议:WiFi、Bluetooth 5

2.2 板载功能示意

  • Key1:按键1,连接到GPIO18
  • Key2:按键2,连接到GPIO0,按下按键并复位可进入boot模式,系统启动后可作为普通按键使用
  • LED:LED灯,连接到GPIO21
  • RST:复位按键
  • 0.96' LCD:0.96寸彩色LCD屏幕,分辨率160*80
  • Charge:充电指示灯
    • 熄灭:未接入电源或已充满
    • 常亮:充电中
  • Type-C:代码烧录、供电接口
  • Li-ino:3.7V锂电池接口
  • BAT-ADC:锂电池电压检测(GPIO1)
  • I2C:I2C接口,用于连接I2C传感器
  • GPIO:GPIO接口,可作为SPI、ADC、I2S、UART、PWM等功能
  • ESP32-S3:ESP32-S3-WROOM-1-N4模组
  • SX1262: LoRa收发器
  • IPEX1:IPEX 1代天线座,用于连接LoRa天线

2.3 板载功能引脚定义

2.4 IO功能

3. 首次使用

3.1 添加板卡教程

ESP32 Arduino IDE添加板卡教程

3.2 选择开发板

3.3 下载代码

  • 将代码复制到窗口内,点击"Upload"上传代码
  • 等待烧录完成,即可看见板载LED灯开始闪烁

若无法烧录、LED未闪烁,请查看常见问题

int led = 21;
void setup() {
  pinMode(led,OUTPUT);
}

void loop() {
  digitalWrite(led,HIGH);
  delay(1000);
  digitalWrite(led,LOW);
  delay(1000);
}

4. ESP32通用教程

ESP32通用教程

5. 开发板功能示例

5.1 LoRaWAN教程

5.1.1 OTAA入网

功能描述及结果展示

该示例代码通过 OTAA 模式接入 LoRaWAN 网络。每 10 秒发送一次数据(“DFRobot”字符串)。支持接收下行数据并打印。若加入失败,会自动重试。

注意:需要在网关将解析数据设置为text


代码

#include "DFRobot_LoRaWAN.h"

// Data packet transmission interval
#define APP_INTERVAL_MS 10000

const uint8_t DevEUI[8] = {0xDF, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11};
const uint8_t AppEUI[8] = {0xDF, 0xB7, 0xB7, 0xB7, 0xB7, 0x00, 0x00, 0x00};
const uint8_t AppKey[16] = {
    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 
    0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10};
uint8_t port = 2;

uint8_t buffer[255];
LoRaWAN_Node node(DevEUI, AppEUI, AppKey, CLASS_A);
TimerEvent_t appTimer;

void joinCb(bool isOk, int16_t rssi, int8_t snr)
{
    if(isOk){
        printf("JOIN SUCCESS\n");
        TimerSetValue(&appTimer, APP_INTERVAL_MS);
        TimerStart(&appTimer);
    }else{
        printf("OTAA connection error. Restart the connection request packet after 5 seconds.\n");
        delay(5000);
        node.join(joinCb);      // Rejoin the LoRaWAN network        
    }
}

void userSendUnConfirmedPacket(void)
{
    TimerSetValue(&appTimer, APP_INTERVAL_MS);
    TimerStart(&appTimer);    

    const char * data = "DFRobot"; 
    uint32_t datalen = strlen(data);
    memcpy(buffer, data, datalen);
    node.sendUnconfirmedPacket(port, buffer, /*size=*/datalen);

    printf("Sending Unconfirmed Packet...\n");
}

// Receive data callback function
void rxCb(void *buffer, uint16_t size, uint8_t port, int16_t rssi, int8_t snr, bool ackReceived, uint16_t uplinkCounter, uint16_t downlinkCounter)
{
    if(size != 0){
        printf("data:%s\n", (uint8_t*)buffer);
    }
}

void setup()
{
    Serial.begin(115200);
    delay(5000); // Open the serial port within 5 seconds after uploading to view full print output
     
    if(!(node.init(/*dataRate=*/DR_4, /*txEirp=*/16))){     // Initialize the LoRaWAN node, set the data rate and Tx Eirp
        printf("LoRaWAN Init Failed!\nPlease Check: DR or Region\n");
        while(1);
    }
    TimerInit(&appTimer, userSendUnConfirmedPacket); // Initialize timer event
    node.setRxCB(rxCb);                            // Set the callback function for receiving data
    node.join(joinCb);                             // Join the LoRaWAN network
    printf("Join Request Packet\n");
}

void loop()
{
    delay(1000);
}

5.1.2 ABP入网

功能描述及结果展示

该示例代码通过 ABP 模式接入 LoRaWAN 网络(添加设备时填写任意DEVEUI即可)。每 10 秒发送一次数据(“DFRobot”字符串)。支持接收下行数据并打印。

注意:需要在网关将解析数据设置为text。


代码

#include "DFRobot_LoRaWAN.h"
#define APP_INTERVAL_MS 10000

const uint32_t nodeDevAddr = 0xDF666666;
const uint8_t nodeNwsKey[16] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
};
const uint8_t nodeAppsKey[16] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10
};

uint8_t buffer[255];
uint8_t port = 2;

LorawanNode node(nodeDevAddr, nodeNwsKey, nodeAppsKey, CLASS_A);
TimerEvent_t appTimer;

void userSendConfirmedPacket(void) 
{    
    TimerSetValue(&appTimer, APP_INTERVAL_MS);
    TimerStart(&appTimer);

    const char * data = "DFRobot"; 
    uint32_t datalen = strlen(data);
    memcpy(buffer, data, datalen);
    node.sendConfirmedPacket(port, buffer, /*size=*/datalen);

    printf("Sending Confirmed Packet...\n");
}

void rxCb(void *buffer,uint16_t size,uint8_t port,int16_t rssi,int8_t snr,bool ackReceived,uint16_t uplinkCounter ,uint16_t downlinkCounter)
{
    if(size != 0){
        printf("data:%s\n", (uint8_t*)buffer);
    }
}


void setup()
{
    Serial.begin(115200);
    delay(5000); // Open the serial port within 5 seconds after uploading to view full print output
        
    if(!(node.init(/*dataRate=*/DR_4, /*txEirp=*/16))){     // Initialize the LoRaWAN node, set the data rate and Tx Eirp
        printf("LoRaWAN Init Failed!\nPlease Check: DR or Region\n");
        while(1);
    }
    TimerInit(&appTimer, userSendConfirmedPacket); // Initialize timer event
    node.setTxCB(txCb);                             // Set the callback function for sending data
    node.setRxCB(rxCb);                             // Set the callback function for receiving data
    printf("ABP Test\n");
    TimerSetValue(&appTimer, APP_INTERVAL_MS);
    TimerStart(&appTimer);                         // Start a timer to send data
}

void loop()
{
    delay(1000);
}

5.1.3 LCD_OTAA

功能描述及结果展示
该示例代码通过 OTAA 模式接入 LoRaWAN 网络,每 10 秒发送一次数据(“DFRobot”字符串),使用屏幕显示网络连接、发送和接收状态。

代码

#include "DFRobot_LoRaWAN.h"

LCD_OnBoard screen;

#define BG_COLOR        COLOR_RGB565_BLACK      // Screen background color
#define TEXT_COLOR      COLOR_RGB565_GREEN      // Screen font color

#define TEXT_FONT       &FreeMono9pt7b          // font
#define TEXT_SIZE       1                       // Screen font size
#define LINE_HEIGHT     18                      // Line height
#define POX_X           0                       // Screen print position X coordinate
#define POX_Y           15                      // Screen print position Y coordinate
#define LINE_1          0                       // Line number
#define LINE_2          1
#define LINE_3          2
#define LINE_4          3

#define APP_INTERVAL_MS 10000

const uint8_t DevEUI[8] = {0xDF, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11};
const uint8_t AppEUI[8] = {0xDF, 0xB7, 0xB7, 0xB7, 0xB7, 0x00, 0x00, 0x00};
const uint8_t AppKey[16] = {
    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 
    0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10};

uint8_t buffer[255];
uint8_t port = 2;
uint32_t counter = 0;

LoRaWAN_Node node(DevEUI, AppEUI, AppKey, CLASS_A);
TimerEvent_t appTimer;

void joinCb(bool isOk, int16_t rssi, int8_t snr)
{
    screen.fillScreen(BG_COLOR);    
    screen.setTextColor(TEXT_COLOR);
    screen.setFont(TEXT_FONT);
    screen.setTextSize(TEXT_SIZE);

    if(isOk){
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
        screen.printf("JOIN SUCCESS");
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_2);
        screen.printf("Accept Packet");
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_3);
        screen.printf("Rssi = %d", rssi);
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_4);
        screen.printf("Snr = %d", snr);

        TimerSetValue(&appTimer, APP_INTERVAL_MS);
        TimerStart(&appTimer);
    }else{
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
        screen.printf("OTAA join Err!");

        delay(5000);
        screen.fillScreen(BG_COLOR);    
        screen.setTextColor(TEXT_COLOR);
        screen.setFont(TEXT_FONT);
        screen.setTextSize(TEXT_SIZE);
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
        screen.printf("Restart");
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_2);
        screen.printf("Join Request");
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_3);
        screen.printf("Packet");
        node.join(joinCb);           // Rejoin the LoRaWAN network
    }
}

void userSendUnConfirmedPacket(void)
{
    TimerSetValue(&appTimer, APP_INTERVAL_MS);
    TimerStart(&appTimer);

    const char * data = "DFRobot"; 
    uint32_t datalen = strlen(data);
    memcpy(buffer, data, datalen);
    node.sendUnconfirmedPacket(port, buffer, /*size=*/datalen);

    screen.fillScreen(BG_COLOR);    
    screen.setTextColor(TEXT_COLOR);
    screen.setFont(TEXT_FONT);
    screen.setTextSize(TEXT_SIZE);
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
    screen.printf("Sending %dst", counter++);
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_2);
    screen.printf("UnConfirmed");
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_3);
    screen.printf("packet ...");
}

void rxCb(void *buffer, uint16_t size, uint8_t port, int16_t rssi, int8_t snr, bool ackReceived, uint16_t uplinkCounter, uint16_t downlinkCounter)
{
    screen.fillScreen(BG_COLOR);    
    screen.setTextColor(TEXT_COLOR);
    screen.setFont(TEXT_FONT);
    screen.setTextSize(TEXT_SIZE);

    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
    screen.printf("Rssi = %d", rssi);
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_2);
    screen.printf("Snr = %d", snr);
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_3);
    screen.printf("UpCount = %d", uplinkCounter);
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_4);
    screen.printf("DownCount = %d", downlinkCounter);
}

void setup()
{
    Serial.begin(115200);
    screen.begin();

    screen.fillScreen(BG_COLOR);    
    screen.setTextColor(TEXT_COLOR);
    screen.setFont(TEXT_FONT);
    screen.setTextSize(TEXT_SIZE);
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
    screen.printf("LoRaWAN Node");
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_3);
    screen.printf("Unconfirmed");
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_4);
    screen.printf("Packet");
    delay(2000);
      
    if(!(node.init(/*dataRate=*/DR_4, /*txEirp=*/16))){     // Initialize the LoRaWAN node, set the data rate and Tx Eirp
        screen.fillScreen(BG_COLOR);        
        screen.setTextColor(TEXT_COLOR);
        screen.setFont(TEXT_FONT);
        screen.setTextSize(TEXT_SIZE);
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
        screen.printf("LoRaWAN Init");
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_2);
        screen.printf("Failed!");
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_3);
        screen.printf("Please Check:");
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_4);
        screen.printf("DR or Region");
        while(1);
    }
    TimerInit(&appTimer, userSendUnConfirmedPacket);   // Initialize timer event
    node.setRxCB(rxCb);                                 // Set the callback function for receiving data
    node.join(joinCb);                                      // Join the LoRaWAN network

    screen.fillScreen(BG_COLOR);
    screen.setTextColor(TEXT_COLOR);
    screen.setFont(TEXT_FONT);
    screen.setTextSize(TEXT_SIZE);
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
    screen.printf("Join Request");
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_2);
    screen.printf("Packet");
}

void loop()
{
    delay(100);
}

5.1.4 休眠深度

功能描述及结果展示
该示例代码通过 OTAA 模式接入 LoRaWAN 网络。每 10 秒发送一次数据(“DFRobot”字符串)。接收下行消息(带有 ACK 或数据)。使用屏幕显示网络连接、发送和接收状态。支持唤醒源(按键唤醒)识别和处理。

代码

#include "DFRobot_LoRaWAN.h"

#define BTN_PIN 18  // GPIO2, 3, 11, 12, 13 can all trigger external wake-up

LCD_OnBoard screen;

#define BG_COLOR        COLOR_RGB565_BLACK      // Screen background color
#define TEXT_COLOR      COLOR_RGB565_GREEN      // Screen font color

#define TEXT_FONT       &FreeMono9pt7b          // font
#define TEXT_SIZE       1                       // Screen font size
#define LINE_HEIGHT     18                      // Line height
#define POX_X           0                       // Screen print position X coordinate
#define POX_Y           15                      // Screen print position Y coordinate
#define LINE_1          0                       // Line number
#define LINE_2          1
#define LINE_3          2
#define LINE_4          3

#define APP_INTERVAL_MS 10000
const uint8_t DevEUI[8] = {0xDF, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11};
const uint8_t AppEUI[8] = {0xDF, 0xB7, 0xB7, 0xB7, 0xB7, 0x00, 0x00, 0x00};
const uint8_t AppKey[16] = {
  0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 
  0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10};

uint8_t buffer[255];
uint8_t port = 2;
LoRaWAN_Node node(DevEUI, AppEUI, AppKey, CLASS_A);
// Rejoin count
RTC_DATA_ATTR uint8_t CurReJoinTimes = 0;
// Downlink Reception Success Flag
uint8_t rxFlag = 1;
uint32_t prevTimeStamp = 0;

const char* wakeup_reason_strings[] = 
{
    "ESP_SLEEP_WAKEUP_UNDEFINED",
    "ESP_SLEEP_WAKEUP_ALL",
    "ESP_SLEEP_WAKEUP_EXT0",
    "ESP_SLEEP_WAKEUP_EXT1",
    "ESP_SLEEP_WAKEUP_TIMER",
    "ESP_SLEEP_WAKEUP_TOUCHPAD",
    "ESP_SLEEP_WAKEUP_ULP",
    "ESP_SLEEP_WAKEUP_GPIO",
    "ESP_SLEEP_WAKEUP_UART",
    "ESP_SLEEP_WAKEUP_WIFI",
    "ESP_SLEEP_WAKEUP_COCPU",
    "ESP_SLEEP_WAKEUP_COCPU_TRAP_TRIG",
    "ESP_SLEEP_WAKEUP_BT"
};

// Join network callback function
void joinCb(bool isOk, int16_t rssi, int8_t snr)
{
    screen.fillScreen(BG_COLOR);    
    screen.setTextColor(TEXT_COLOR);
    screen.setFont(TEXT_FONT);
    screen.setTextSize(TEXT_SIZE);

    if(isOk){
        CurReJoinTimes = 0;
        printf("JOIN SUCCESS\n");
        printf("JoinAccept Packet rssi = %d snr = %d\n", rssi, snr);
        printf("NetID = %06X\n", node.getNetID());
        printf("DevAddr = %08X\n", node.getDevAddr());
        uint8_t * NwkSKey = node.getNwkSKey();
        uint8_t * AppSKey = node.getAppSKey();
        printf("NwkSKey=0X");
        for(uint8_t i= 0; i < 16; i++){
            printf("%02X", NwkSKey[i]);
        }
        printf("\n");
        printf("AppSKey=0X");
        for(uint8_t i = 0; i < 16; i++){
            printf("%02X", AppSKey[i]);
        }
        printf("\n");

        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
        screen.printf("JOIN SUCCESS");
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_2);
        screen.printf("Accept Packet");
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_3);
        screen.printf("Rssi = %d", rssi);
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_4);
        screen.printf("Snr = %d", snr);
        delay(5000);

        node.deepSleepMs(APP_INTERVAL_MS);  // Deep sleep after successful network join        

    }else{
        printf("OTAA join error\n");
        printf("Check Whether the device has been registered on the gateway!\n");
        printf("deviceEUI and appKey are the same as the devices registered on the gateway\n");
        printf("Ensure that there is a gateway nearby\n");
        printf("Check whether the antenna is normal\n");
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
        screen.printf("OTAA join Err!");
        delay(2000);    

        // Backoff join procedure
        CurReJoinTimes++; 
        // printf("\n\n------CurReJoinTimes = %d------\n\n", CurReJoinTimes);
        uint64_t backoff_time_ms = 5000 * (1ULL << (CurReJoinTimes - 1));
        backoff_time_ms = (backoff_time_ms > 300000) ? 300000 : backoff_time_ms;
        node.deepSleepMs(backoff_time_ms);
    }  
}

void userSendConfirmedPacket(void)
{    
    const char * data = "DFRobot"; 
    uint32_t datalen = strlen(data);
    memcpy(buffer, data, datalen);    
    node.sendConfirmedPacket(port, buffer, /*size=*/datalen);
    rxFlag = 0;
    
    screen.fillScreen(BG_COLOR);    
    screen.setTextColor(TEXT_COLOR);
    screen.setFont(TEXT_FONT);
    screen.setTextSize(TEXT_SIZE);
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
    screen.printf("Sending...");
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_2);
    screen.printf("Confirmed");
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_3);
    screen.printf("Packet");
}

// Receive data callback function
void rxCb(void *buffer, uint16_t size, uint8_t port, int16_t rssi, int8_t snr, bool ackReceived, uint16_t uplinkCounter, uint16_t downlinkCounter)
{
    rxFlag = 1;
    screen.fillScreen(BG_COLOR);    
    screen.setTextColor(TEXT_COLOR);
    screen.setFont(TEXT_FONT);
    screen.setTextSize(TEXT_SIZE);
    if(ackReceived == true){
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
        screen.printf("this is a ACK");
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_2);
        screen.printf("Rssi = %d", rssi);
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_3);
        screen.printf("Snr = %d", snr);
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_4);
        screen.printf("DownCount = %d", downlinkCounter);
    }
    delay(3000);

    node.deepSleepMs(APP_INTERVAL_MS);      // MCU sleep for a specified duration
}

// Handle button-triggered wakeup: display node info and return to sleep
void buttonWakeupHandler()
{    
    screen.fillScreen(BG_COLOR);    
    screen.setTextColor(TEXT_COLOR);
    screen.setFont(TEXT_FONT);
    screen.setTextSize(TEXT_SIZE);
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
    screen.printf("buttonCB");
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_2);
    screen.printf("dataRate: %d\n", node.getDataRate());
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_3);
    screen.printf("txEirp: %d\n", node.getEIRP());
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_4);
    screen.printf("netID: %d\n", node.getNetID());
    printf("LastDownlinkCounter = %d\n", node.getLastDownCounter());
    printf("LastUplinkCounter = %d\n", node.getLastUplinkCounter());
    
    delay(5000);
    node.deepSleepMs(APP_INTERVAL_MS);      // MCU sleep for a specified duration
}

void setup()
{
    Serial.begin(115200);
    screen.begin(); 

    screen.fillScreen(BG_COLOR);    
    screen.setTextColor(TEXT_COLOR);
    screen.setFont(TEXT_FONT);
    screen.setTextSize(TEXT_SIZE);
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
    screen.printf("WakeUp");
    delay(2000); 

    // Set to wake up using a button press
    esp_sleep_enable_ext0_wakeup((gpio_num_t )BTN_PIN, LOW);
    esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
    if (wakeup_reason >= ESP_SLEEP_WAKEUP_UNDEFINED && wakeup_reason <= ESP_SLEEP_WAKEUP_BT) {
        printf("\n\n------Wakeup reason: [%s]------\n\n", wakeup_reason_strings[wakeup_reason]);
    } else {
        printf("\n\n------Wakeup reason: [UNKNOWN]------\n\n");
    }
    if (wakeup_reason == ESP_SLEEP_WAKEUP_EXT0) {
        buttonWakeupHandler();
    }
  
    if(!(node.init(/*dataRate=*/DR_4, /*txEirp=*/16))){     // Initialize the LoRaWAN node, set the data rate and Tx Eirp
        screen.fillScreen(BG_COLOR);        
        screen.setTextColor(TEXT_COLOR);
        screen.setFont(TEXT_FONT);
        screen.setTextSize(TEXT_SIZE);
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
        screen.printf("LoRaWAN Init");
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_2);
        screen.printf("Failed!");
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_3);
        screen.printf("Please Check:");
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_4);
        screen.printf("DR or Region");
        while(1);
    }
    node.setRxCB(rxCb);                                 // Set the callback function for receiving data

    if(!node.isJoined()) {
        screen.fillScreen(BG_COLOR);        
        screen.setTextColor(TEXT_COLOR);
        screen.setFont(TEXT_FONT);
        screen.setTextSize(TEXT_SIZE);
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
        screen.printf("Join Request");
        node.join(joinCb);                                  // Join the LoRaWAN network 
    } else {
        userSendConfirmedPacket();                          // Send data
    }    
}

void loop()
{
    // Prevent prolonged downlink waiting from blocking deep sleep and increasing power consumption
    uint32_t currTimeStamp = TimerGetCurrentTime();
    if (currTimeStamp - prevTimeStamp >= APP_INTERVAL_MS * 2) {
        prevTimeStamp = currTimeStamp;    
        if (!rxFlag) {
            node.deepSleepMs(APP_INTERVAL_MS);
        } 
    }
    delay(1000);
}

5.2 LoRa教程

该部分仅演示部分示例,更多示例代码请在"DFRobot_LoRaWAN\examples\DFRobot_LoRaRadio"中查看

5.2.1 LoRa收发数据

功能描述及结果展示
发送端:向接收端发送数据,并在屏幕上显示发送次数
接收端:接收发送端的数据,并在屏幕上显示接收数据次数

代码
发送端

#include "DFRobot_LoRaRadio.h"

LCD_OnBoard screen;

#define BG_COLOR        COLOR_RGB565_BLACK      // Screen background color
#define TEXT_COLOR      COLOR_RGB565_GREEN      // Screen font color

#define TEXT_FONT       &FreeMono9pt7b          // font
#define TEXT_SIZE       1                       // Screen font size
#define LINE_HEIGHT     18                      // Line height
#define POX_X           0                       // Screen print position X coordinate
#define POX_Y           15                      // Screen print position Y coordinate
#define LINE_1          0                       // Line number
#define LINE_2          1
#define LINE_3          2
#define LINE_4          3

#ifdef REGION_EU868
#define RF_FREQUENCY 868000000  // Hz
#define TX_EIRP 16    // dBm 
#endif
#ifdef REGION_US915
#define RF_FREQUENCY 915000000  // Hz
#define TX_EIRP 22    // dBm 
#endif
#define LORA_SPREADING_FACTOR 7

DFRobot_LoRaRadio radio;
uint8_t buffer[4] = {1, 2, 3, 4};
uint32_t counter = 0;

// Transmission complete callback function
void loraTxDone(void)
{
    printf("-------------------------LoRa Tx done-----------------------------\n");
    screen.fillScreen(BG_COLOR);    
    screen.setTextColor(TEXT_COLOR);
    screen.setFont(TEXT_FONT);
    screen.setTextSize(TEXT_SIZE);
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
    screen.printf("Send %dst", counter);
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_2);
    screen.printf("done");
}

void setup()
{
    Serial.begin(115200); // Initialize serial communication with a baud rate of 115200
    screen.begin();

    screen.fillScreen(BG_COLOR);    
    screen.setTextColor(TEXT_COLOR);
    screen.setFont(TEXT_FONT);
    screen.setTextSize(TEXT_SIZE);
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
    screen.printf("LoRa Send Test");

    delay(5000);   // Open the serial port within 5 seconds after uploading to view full print output
    radio.init();  // Initialize the LoRa node with a default bandwidth of 125 KHz    
    radio.setTxCB(loraTxDone);                  // Set the transmission complete callback function
    radio.setFreq(RF_FREQUENCY);                // Set the communication frequency
    radio.setEIRP(TX_EIRP);                     // Set the Tx Eirp
    radio.setSF(LORA_SPREADING_FACTOR);         // Set the spreading factor
    radio.setBW(BW_125);                        // Set the bandwidth
}

void loop()
{
    printf("statistics: send %d packet\n", ++counter);  // Print the prompt message
    radio.sendData(buffer, /*size=*/4);                 // Send data
    delay(3 * 1000);                                    // Delay 3 seconds before sending next data
}

接收端

#include "DFRobot_LoRaRadio.h"

LCD_OnBoard screen;

#define BG_COLOR        COLOR_RGB565_BLACK      // Screen background color
#define TEXT_COLOR      COLOR_RGB565_GREEN      // Screen font color

#define TEXT_FONT       &FreeMono9pt7b          // font
#define TEXT_SIZE       1                       // Screen font size
#define LINE_HEIGHT     18                      // Line height
#define POX_X           0                       // Screen print position X coordinate
#define POX_Y           15                      // Screen print position Y coordinate
#define LINE_1          0                       // Line number
#define LINE_2          1
#define LINE_3          2
#define LINE_4          3

#ifdef REGION_EU868
#define RF_FREQUENCY 868000000  // Hz
#define TX_EIRP 16    // dBm 
#endif
#ifdef REGION_US915
#define RF_FREQUENCY 915000000  // Hz
#define TX_EIRP 22    // dBm 
#endif
#define LORA_SPREADING_FACTOR 7

DFRobot_LoRaRadio radio;
uint8_t buffer[4] = {1, 2, 3, 4};
uint32_t counter = 0;

void loraRxDone(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr)
{
    uint8_t i = 0;
    printf("LoRa data received on channel %ld, SF=%d Rssi=%d Snr=%d \nData={",RF_FREQUENCY, LORA_SPREADING_FACTOR, rssi, snr);
    for(; i < size-1; i++){
        printf("0x%02x, ", payload[i]);
    }
    printf("0x%02x}\n\n", payload[i]);

    screen.fillScreen(BG_COLOR);    
    screen.setTextColor(TEXT_COLOR);
    screen.setFont(TEXT_FONT);
    screen.setTextSize(TEXT_SIZE);
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
    screen.printf("Recv %dst", ++counter);
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_2);
    screen.printf("packet");
}

void loraRxError(void)
{
    printf("LoRaRxError\n");
    screen.fillScreen(BG_COLOR);    
    screen.setTextColor(TEXT_COLOR);
    screen.setFont(TEXT_FONT);
    screen.setTextSize(TEXT_SIZE);
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
    screen.printf("LoRaRxError");
}

void setup()
{
    Serial.begin(115200);       // Initialize serial communication with a baud rate of 115200
    screen.begin();

    screen.fillScreen(BG_COLOR);    
    screen.setTextColor(TEXT_COLOR);
    screen.setFont(TEXT_FONT);
    screen.setTextSize(TEXT_SIZE);
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
    screen.printf("LoRa Recv Test");

    delay(5000);                                  // Open the serial port within 5 seconds after uploading to view full print output
    radio.init();                                 // Initialize the LoRa node with a default bandwidth of 125 KHz    
    radio.setRxCB(loraRxDone);                    // Set the receive complete callback function
    radio.setRxErrorCB(loraRxError);              // Set the receive error callback function
    radio.setFreq(RF_FREQUENCY);                  // Set the communication frequency
    radio.setSF(LORA_SPREADING_FACTOR);           // Set the spreading factor
    radio.setBW(BW_125);                          // Set the bandwidth
    radio.startRx();                              // Start receiving
}

void loop()
{
    delay(3000);
}

6. 项目应用示例

6.1 上传温湿度数据到LoRaWAN网关

功能描述及结果展示
该示例代码通过 OTAA 模式接入 LoRaWAN 网络,每 30 秒读取一次SHT31(I2C地址0x45)温湿度传感器数据显示在屏幕上并发送到网关,然后休眠。可以通过按键(IO18),主动唤醒主控采集数据并发送。

代码

#include "DFRobot_LoRaWAN.h"
#include <DFRobot_SHT3x.h>

// Button pin
#define BTN_PIN 18  // GPIO2, 3, 11, 12, 13 can all trigger external wake-up

LCD_OnBoard screen;

#define BG_COLOR        COLOR_RGB565_BLACK      // Screen background color
#define TEXT_COLOR      COLOR_RGB565_GREEN      // Screen font color

#define TEXT_FONT       &FreeMono9pt7b          // font
#define TEXT_SIZE       1                       // Screen font size
#define LINE_HEIGHT     18                      // Line height
#define POX_X           0                       // Screen print position X coordinate
#define POX_Y           15                      // Screen print position Y coordinate
#define LINE_1          0                       // Line number
#define LINE_2          1
#define LINE_3          2
#define LINE_4          3

// Data packet transmission interval
#define APP_INTERVAL_MS 30000
const uint8_t DevEUI[8] = {0xDF, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11};
const uint8_t AppEUI[8] = {0xDF, 0xB7, 0xB7, 0xB7, 0xB7, 0x00, 0x00, 0x00};
const uint8_t AppKey[16] = {
  0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 
  0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10};

uint8_t buffer[255];
uint8_t port = 2;
LoRaWAN_Node node(DevEUI, AppEUI, AppKey, CLASS_A);

DFRobot_SHT3x sht3x;

// Rejoin count
RTC_DATA_ATTR uint8_t CurReJoinTimes = 0;

// Join network callback function
void joinCb(bool isOk, int16_t rssi, int8_t snr)
{
    screen.fillScreen(BG_COLOR);    
    screen.setTextColor(TEXT_COLOR);
    screen.setFont(TEXT_FONT);
    screen.setTextSize(TEXT_SIZE);

    if(isOk){
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
        screen.printf("JOIN SUCCESS");
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_2);
        screen.printf("Accept Packet");
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_3);
        screen.printf("Rssi = %d", rssi);
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_4);
        screen.printf("Snr = %d", snr);
        delay(1000);
        node.deepSleepMs(APP_INTERVAL_MS);  // Deep sleep after successful network join        
    }else{
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
        screen.printf("OTAA join Err!");
        delay(2000);
        // Backoff join procedure
        CurReJoinTimes++; 
        uint64_t backoff_time_ms = 5000 * (1ULL << (CurReJoinTimes - 1));
        backoff_time_ms = (backoff_time_ms > 300000) ? 300000 : backoff_time_ms;
        node.deepSleepMs(backoff_time_ms);
    }  
}

void userSendConfirmedPacket(void)
{    
    float temperature = sht3x.getTemperatureC();
    float humidity = sht3x.getHumidityRH();
    screen.fillScreen(BG_COLOR);    
    screen.setTextColor(TEXT_COLOR);
    screen.setFont(TEXT_FONT);
    screen.setTextSize(TEXT_SIZE);
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
    screen.printf("Temp: %.2f C", temperature);
    screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_2);
    screen.printf("Humi: %.2f %%", humidity);
    char data[64];
    snprintf((char*)data, sizeof(data), "T:%.2f,H:%.2f", temperature, humidity);
    uint32_t datalen = strlen(data);
    memcpy(buffer, data, datalen);
    node.sendUnconfirmedPacket(port, buffer, datalen);
    delay(4000);  //最少需要4S延时
    node.deepSleepMs(APP_INTERVAL_MS);
}

void setup()
{
    Serial.begin(115200);

    screen.begin(); 
    screen.fillScreen(BG_COLOR);    
    screen.setTextColor(TEXT_COLOR);
    screen.setFont(TEXT_FONT);
    screen.setTextSize(TEXT_SIZE);
    while (sht3x.begin() != 0) {
        screen.fillScreen(BG_COLOR);  
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
        screen.printf("SHT3x Err");
        delay(1000);
    }
    delay(1000);

    // Set to wake up using a button press
    esp_sleep_enable_ext0_wakeup((gpio_num_t )BTN_PIN, LOW);
    esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
    if(!(node.init(/*dataRate=*/DR_4, /*txEirp=*/16))){     // Initialize the LoRaWAN node, set the data rate and Tx Eirp
        screen.fillScreen(BG_COLOR);        
        screen.setTextColor(TEXT_COLOR);
        screen.setFont(TEXT_FONT);
        screen.setTextSize(TEXT_SIZE);
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
        screen.printf("LoRaWAN Init Failed!");
        while(1);
    }

    if(!node.isJoined()) {
        screen.fillScreen(BG_COLOR);        
        screen.setTextColor(TEXT_COLOR);
        screen.setFont(TEXT_FONT);
        screen.setTextSize(TEXT_SIZE);
        screen.setCursor(POX_X, POX_Y + LINE_HEIGHT * LINE_1);
        screen.printf("Join Request");
        node.join(joinCb);                                  // Join the LoRaWAN network 
    } else {
        userSendConfirmedPacket();                          // Send data
    }    
}

void loop()
{
    delay(1000);
}

6.2 Meshtastic通讯

Meshtastic是一种基于LoRa技术的离网通信平台。它通过低成本、低功耗的无线电设备,实现远距离的通信。无论是在通信基础设施缺失的地区,还是在现有网络瘫痪的情况下,Meshtastic 都能提供可靠的解决方案。这个项目完全由社区驱动,并且开源,适用于户外探险、徒步旅行,应急通信、公益救援等场景。

固件

通过FLASH下载工具可以直接将固件下载到开发板中,快速体验

固件

使用方法

  1. 下载Meshtastic手机APP
  2. 烧录固件复位设备后
  3. 打开Meshtastic手机APP,点击"Scan",找到"Meshtastic_xxxx",并连接(配对密码为"123456")

  1. 设备成功连接后,进入频道分享页面,可看到自己频道的二维码,点击"Scan"可扫描其他人的二维码进入频道

  1. 点击聊天页面,点击对应频道即可聊天

更多链接

6.3 接入HomeAssistant

LoRaWAN ESP32-S3开发板可接入HomeAssistant网关,可以使网关可以远距离接收到传感器数据

  1. 设置 -> 加载项 -> 加载项商店 -> 搜索"ESPHome" -> 选择"ESPHome Device Builder(beta)" -> 点击安装

  1. 在侧边栏点击"ESPHome Builder" -> 点击"NEW DEVICE" -> 在弹出窗口点击"CONTINUE" -> 输入名称,点击NEXT -> 选择ESP32-S3 -> 在弹出窗口点击"SKIP"

  1. 点击"EDIT"进入代码编辑页面 -> 在"captive_portal"下方添加代码

代码

# Example configuration entry
spi:
  clk_pin: GPIO7
  mosi_pin: GPIO6
  miso_pin: GPIO5

sx126x:
  dio1_pin: GPIO4
  cs_pin: GPIO10
  busy_pin: GPIO40
  rst_pin: GPIO41
  pa_power: 3
  bandwidth: 125_0kHz
  crc_enable: true
  frequency: 433920000
  modulation: LORA
  hw_version: sx1262
  rf_switch: true
  sync_value: [0x14, 0x24]
  preamble_size: 8
  spreading_factor: 7
  coding_rate: CR_4_6
  tcxo_voltage: 1_8V
  tcxo_delay: 5ms

更多配置参数可参考:https://beta.esphome.io/components/sx126x

  1. 烧录代码

7. FLASH下载工具使用教程

FLASH Download Tool使用教程

8. 常见问题

  • 无法将代码下载到开发板 / 找不到COM口 / COM口不断出现又消失 / 串口不断打印复位信息
    • 请按住BOOT按键,然后点击RST,再下载代码即可

9. 资料下载