1. Product Description
1.1 Product Overview
The LoRaWAN ESP32-S3 Development Board is a professional hardware focusing on long-range wireless communication and convenient development. With comprehensive compatibility for LoRa, LoRaWAN, and Meshtastic protocols, integration capability with the HomeAssistant ecosystem, and user-friendly hardware function design, it provides an efficient solution for LoRa/LoRaWAN developers and smart home users.
Comprehensive Support for LoRa/LoRaWAN/Meshtastic Communication Protocols with Strong Communication Compatibility
The LoRaWAN ESP32-S3 Development Board supports LoRa one-to-one communication, LoRa one-to-many communication, as well as the standard LoRaWAN protocol and Meshtastic networking protocol. It enables flexible communication between development boards or integration into LoRaWAN networks. The board uses Arduino for programming and provides easy-to-use library files and sample code. There is no need to have in-depth knowledge of LoRa/LoRaWAN communication protocols to use it, which greatly lowers the entry barrier.
Support for Integration with HomeAssistant (ESPHome) to Expand Long-Range Communication Capabilities
The LoRaWAN ESP32-S3 Development Board can be integrated into HomeAssistant via ESPHome, serving as a "long-range communication expansion unit" for the system. It effectively breaks through the distance limitations of traditional wireless communication, enhances signal coverage and transmission stability in complex environments (such as multi-story buildings, large residences, and outdoor scenarios), and expands the deployment range of smart home devices.
Rich Functions and High Usability, Significantly Improving Testing Efficiency
The functional design of the LoRaWAN ESP32-S3 Development Board centers on "high efficiency and ease of use". Through high hardware integration, it significantly reduces test preparation time and device connection complexity:
- Visual Operation and Data Viewing: It integrates a 0.96-inch LCD screen and physical buttons. Without relying on external devices, you can view key information such as device operation data and communication status in real time. At the same time, you can quickly switch display interfaces through the buttons, making the operation intuitive and convenient.
- Portable Battery Life, Free from Power Constraints: A dedicated lithium battery interface is designed, and charging function is integrated to support external lithium battery power supply. Whether it is used for indoor desktop testing or outdoor mobile scenarios, it can get rid of the limitation of fixed power supply, making the application scenarios more flexible.
- Easy-to-Use IO Interfaces for Effortless Sensor Connection: It is equipped with convenient IO interfaces, allowing direct connection to various sensor devices such as temperature and humidity sensors, light sensors, and human motion sensors without soldering. This solves the tedious wiring and soldering work during project setup and enables efficient construction of project prototypes.
1.2 Product Features
- Supports LoRa, LoRaWAN, and Meshtastic long-range communication protocols
- Supports integration with HomeAssistant (ESPHome), enabling the HomeAssistant system to support long-range communication
- Equipped with ESP32-S3 main controller, supporting Arduino and YAML programming
- Rich functions and high usability, significantly improving testing efficiency
- Integrates a 0.96-inch screen and buttons for easy data viewing and display switching
- Integrates multiple sets of power supplies and I2C interfaces for easy sensor device connection
- Integrates a lithium battery interface and charging function for convenient portable use
1.3 Application Scenarios
- LoRa/LoRaWAN Network Deployment and Debugging: View LoRaWAN network signals and communication status through the development board screen for testing, enabling rapid network setup and adjustment.
- Smart Home Expansion for Large Residences/Villas: In multi-story villas or houses with cross-courtyards, it easily connects garden sensors, garage devices, and indoor control systems, solving the problem of signal interruption caused by wall barriers and realizing unified management of all household devices.
- Outdoor Environmental Monitoring Scenarios: Equipped with sensors such as temperature and humidity sensors and PM2.5 sensors, it can be deployed on balconies, rooftops, or courtyards. It transmits data back to the HomeAssistant system in real time through long-range communication, and links with air conditioners and fresh air devices to automatically adjust the indoor environment.
- Agricultural/Horticultural Remote Management: Placed in vegetable gardens, greenhouses, or orchards, it connects soil moisture and light sensors. Even indoors hundreds of meters away, you can grasp the crop growth environment in real time and trigger the irrigation system for automatic operation.
- Meshtastic Chat Device: In outdoor scenarios with weak or no network signals, such as remote areas, mountain hiking, forest expeditions, and desert crossings, it uses the Meshtastic protocol to build a decentralized radio communication network, enabling real-time text message sending.
2. Technical Specifications
2.1 Product Parameters
Basic Parameters
- Operating Voltage: 3.3V
- Type-C Input Voltage: 5V DC
- Maximum Charging Current: 300mA
- Screen Size: 0.96 inches (160×80 pixels)
- Operating Temperature: -10~60℃
- Module Size: 73×45mm
LoRa Parameters
- RF Chip: SX1262
- Operating Frequency Band: 850~930MHz
- Transmission Power: 16dBm (EU868) / 22dBm (US915)
- Receiver Sensitivity: -137dBm / 125kHz SF=12
ESP32-S3 Parameters
- Processor: Xtensa® dual-core 32-bit LX7 microprocessor
- Clock Speed: 240MHz
- SRAM: 512KB
- ROM: 384KB
- Flash: 4MB
- Wireless Protocols: WiFi, Bluetooth 5
2.2 Onboard Function Diagram

- Key1: Button 1, connected to GPIO18
- Key2: Button 2, connected to GPIO0. Press this button and reset the device to enter boot mode; it can be used as a normal button after the system starts.
- LED: LED light, connected to GPIO21
- RST: Reset button
- 0.96' LCD: 0.96-inch color LCD screen with a resolution of 160×80
- Charge: Charging indicator light
- Off: No power connected or fully charged
- Steady on: Charging in progress
- Type-C: Interface for code burning and power supply
- Li-ino: 3.7V lithium battery interface
- BAT-ADC: Lithium battery voltage detection (GPIO1)
- I2C: I2C interface, used to connect I2C sensors
- GPIO: GPIO interface, which can function as SPI, ADC, I2S, UART, PWM, etc.
- ESP32-S3: ESP32-S3-WROOM-1-N4 module
- SX1262: LoRa transceiver
- IPEX1: IPEX Generation 1 antenna connector, used to connect a LoRa antenna
2.3 Onboard Function Pin Definition

2.4 IO Functions

3. First-Time Use
3.1 Board Addition Tutorial
Tutorial for Adding ESP32 Board to Arduino IDE
3.2 Select the Development Board

3.3 Code Download
- Copy the code into the window and click "Upload" to upload the code.
- Wait for the burning process to complete, and you will see the onboard LED start blinking.
If burning fails or the LED does not blink, please refer to the Frequently Asked Questions
int led = 21;
void setup() {
pinMode(led,OUTPUT);
}
void loop() {
digitalWrite(led,HIGH);
delay(1000);
digitalWrite(led,LOW);
delay(1000);
}
4. ESP32 General Tutorial
5. Development Board Function Examples
5.1 LoRaWAN Tutorial
- Please install the DFRobot_LoRaWAN_ESP32S3 library before use.
- The LoRaWAN examples require collaboration with a gateway:
- LoRaWAN protocol stack version: 1.0.3
- This section only demonstrates partial examples. For more sample code, please refer to the "DFRobot_LoRaWAN_ESP32S3\examples" directory.
5.1.1 OTAA Network Access
Function Description and Result Display
This sample code connects to the LoRaWAN network in OTAA (Over-The-Air Activation) mode. It sends data (the string "DFRobot") every 10 seconds, supports receiving downlink data and printing it, and automatically retries if the connection fails.
Note: You need to set the data parsing format to "text" on the gateway.


Code
#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 Network Access
Function Description and Result Display
This sample code connects to the LoRaWAN network in ABP (Activation By Personalization) mode (you can enter any DEVEUI when adding the device). It sends data (the string "DFRobot") every 10 seconds and supports receiving downlink data for printing.
Note: You need to set the data parsing format to "text" on the gateway.


Code
#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
Function Description and Result Display
This sample code connects to the LoRaWAN network in OTAA mode, sends data (the string "DFRobot") every 10 seconds, and uses the screen to display network connection, transmission, and reception status.
Code
#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 Sleep Depth
Function Description and Result Display
This sample code connects to the LoRaWAN network in OTAA mode. It sends data (the string "DFRobot") every 10 seconds, receives downlink messages (with ACK or data), uses the screen to display network connection, transmission and reception status, and supports the identification and processing of wake-up sources (button wake-up).
Code
#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 Tutorial
- Please install the DFRobot_LoRaWAN_ESP32S3 library before use
- This section only demonstrates partial examples. For more sample code, please refer to the "DFRobot_LoRaWAN_ESP32S3\examples\DFRobot_LoRaRadio" directory.
5.2.1 LoRa Data Transmission and Reception
Function Description and Result Display
- Transmitter Side: Sends data to the receiver and displays the number of transmissions on the screen.
- Receiver Side: Receives data from the transmitter and displays the number of data receptions on the screen.
Code
Transmitter Side
#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
}
Receiver Side
#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. Project Application Examples
6.1 Uploading Temperature and Humidity Data to LoRaWAN Gateway
Function Description and Result Display
This sample code connects to the LoRaWAN network in OTAA mode. It reads data from the SHT31 temperature and humidity sensor (I2C address: 0x45) every 30 seconds, displays the data on the screen, sends the data to the gateway, and then enters sleep mode. The main controller can be proactively woken up via a button (connected to IO18) to collect and send data.
Code
#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); //A minimum delay of 4 seconds is required
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 Communication
Meshtastic is an off-grid communication platform based on LoRa technology. It enables long-range communication using low-cost, low-power radio devices. Whether in areas lacking communication infrastructure or in situations where existing networks are down, Meshtastic can provide a reliable solution. This project is fully community-driven and open-source, suitable for scenarios such as outdoor adventures, hiking, emergency communication, and public welfare rescue.

Firmware
You can directly download the firmware to the development board using the FLASH Download Tool for a quick experience.
Usage Method
- Download the Meshtastic Mobile App.
- After burning the firmware, reset the device.
- Open the Meshtastic mobile app, click "Scan", find the device named "Meshtastic_xxxx", and connect to it (the pairing password is "123456").

- Once the device is successfully connected, enter the channel sharing page. You will see the QR code of your own channel. Click "Scan" to scan others' QR codes and join their channels.

- Go to the chat page and click the corresponding channel to start chatting.

More Links
6.3 Integration with HomeAssistant
The LoRaWAN ESP32-S3 Development Board can be integrated with the HomeAssistant gateway, enabling the gateway to receive sensor data over long distances.
- Go to Settings -> Add-ons -> Add-on Store -> Search for "ESPHome" -> Select "ESPHome Device Builder (beta)" -> Click Install.


- Click ESPHome Builder in the sidebar -> Click NEW DEVICE -> Click CONTINUE in the pop-up window -> Enter a name and click NEXT -> Select ESP32-S3 -> Click SKIP in the pop-up window.

- Click EDIT to enter the code editing page -> Add the following code below the "captive_portal" section.


Code
# 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
For more configuration parameters, please refer to: https://beta.esphome.io/components/sx126x
- Burn the code to the development board.
7. FLASH Download Tool Usage Tutorial
FLASH Download Tool Usage Tutorial
8. Common Problems
- Unable to download the code to the development board / Cannot find the COM port / The COM port appears and disappears continuously / The serial port continuously prints reset information
- Please hold down the BOOT button, then click RST, and then download the code.
9. Data Download
- Hardware schematic diagram
- Hardware size and component location diagram
- ESP32-S3-WROOM-1-N4
- Information of other important components
- Antenna specification
- Shell file
- 2D files
- 3D model files
- KICAD files
