Google Chat:---
+86-0755-88291180
sales@spotpear.com
dragon_manager@163.com
tech-support@spotpear.com
zhoujie@spotpear.com
WhatsApp:13246739196
WhatsApp:13424403025


This chapter contains the following sections. Please read as needed:
Connect according to the table below.
| Core2021-XF | ESP32S3 |
|---|---|
| CLK | 40 |
| MISO | 46 |
| MOSI | 45 |
| CS | 42 |
| DIO11 | 38 |
| RESET | 39 |
| BUSY | 41 |
core2021-xf\examples\esp32s3 directory of the example package.| Example | Basic Description | Dependency Library |
|---|---|---|
| 01_lr2021_tx | LR2021 Transmit | RadioLib |
| 02_lr2021_rx | LR2021 Receive | RadioLib |
| 03_lr2021_pingpong | LR2021 Ping‑Pong | RadioLib |
| 04_lr2021_tx_cw | LR2021 CW Mode Transmit | RadioLib |
| 05_lr2021_LoRaWAN | LoRaWAN | RadioLib |
core2021-xf\examples\esp32s3\arduino and select the example program you wish to test.
Example Description
Code Analysis
SPI.begin(...): Initializes ESP32S3 hardware SPI pins with custom CLK/MOSI/MISO.radio.irqDioNum = 11: Configures the LR2021 interrupt mapping pin; must be set before initialization.radio.XTAL = true: Enables the external crystal oscillator to ensure frequency accuracy.setFlag(void): Interrupt callback function triggered automatically after the module finishes transmission, sets a transmission‑complete flag.radio.setPacketSentAction(setFlag): Binds the transmission‑complete interrupt function.radio.startTransmit("content"): Starts asynchronous LoRa transmission (supports string / byte array).radio.finishTransmit(): Cleanup after transmission, turns off the transmit circuit and resets the module state.loop() main logic: Check transmission‑complete flag → print status → delay → send next packet with sequence number.Operation Result
After compiling and uploading, open the serial monitor to see logs indicating transmission completion, as shown below (in combination with 02_lr2021_rx):

Example Description
Code Analysis
SPI.begin(...): Initializes ESP32S3 hardware SPI with custom CLK/MOSI/MISO pins.radio.irqDioNum = 11: Configures the LR2021 interrupt mapping pin; must be set before initialization.radio.XTAL = true: Enables the external crystal oscillator to ensure frequency accuracy and improve reception stability.setFlag(void): Interrupt callback function triggered automatically after a complete packet is received.radio.setPacketReceivedAction(setFlag): Binds the reception‑complete interrupt service function.radio.startReceive(): Starts continuous LoRa reception mode, enters a data‑waiting state.radio.readData(str): Reads the received wireless data (supports string parsing).radio.getRSSI() / radio.getSNR(): Retrieves signal quality parameters for debugging and link evaluation.loop() main logic: Check reception‑complete flag → read data → parse and print → continue listening.Operation Result
After compiling and uploading, open the serial monitor to see real‑time reception logs including data content, RSSI, and SNR, as shown below (in combination with 01_lr2021_tx):

Example Description
INITIATING_NODE sets the module as the initiator; the other module acts as the responder.Code Analysis
SPI.begin(...): Initializes ESP32S3 hardware SPI with custom CLK/MOSI/MISO pins.radio.irqDioNum = 11: Configures the LR2021 interrupt mapping pin; must be set before initialization.radio.XTAL = true: Enables the external crystal oscillator to ensure communication frequency accuracy.setFlag(void): General interrupt callback, triggered on either transmit or receive completion.radio.setIrqAction(setFlag): Binds the shared transmit/receive interrupt function.INITIATING_NODE macro: Used to distinguish the initiating node.radio.startTransmit(): Starts packet transmission.radio.startReceive(): Switches the module to listening (receive) mode.radio.readData(str): Reads the received LoRa packet.loop() main logic: Transmit complete → enter receive; receive complete → delay reply → transmit again.Operation Result
INITIATING_NODE macro on one of them.
Example Description
Code Analysis
SPI.begin(...): Initializes ESP32S3 hardware SPI with custom CLK/MOSI/MISO pins.radio.XTAL = true: Enables the external crystal oscillator to ensure carrier frequency accuracy.OUT_HZ 868000000UL: Defines the direct transmit frequency (868 MHz); can be modified.radio.setOutputPower(22): Sets the transmit power to 22 dBm.radio.transmitDirect(OUT_HZ): Enters continuous direct transmission mode, outputting a fixed‑frequency carrier.loop(): No business logic; the carrier continues to transmit without program intervention.Operation Result
After flashing, the module immediately outputs a fixed‑frequency carrier signal.
The serial port prints initialization and transmission start status; a spectrum analyzer or receiving module can detect the continuous RF signal, as shown below:

Example Description
Code Analysis
SPI.begin(...): Initializes ESP32S3 hardware SPI with custom CLK/MOSI/MISO pins.radio.irqDioNum = 11: Configures the LR2021 interrupt mapping; must be set before initialization.radio.XTAL = true: Enables the external crystal oscillator to ensure LoRaWAN frequency accuracy.prefs.begin(...): Initializes the ESP32 NVS partition for storing LoRaWAN session information.restoreLoRaWANState(): Restores session information from NVS for fast reconnection.node.beginOTAA() / node.activateOTAA(): OTAA join functions.saveLoRaWANState(): Saves session information to NVS after successful join.node.sendReceive(): Sends uplink data and automatically listens for server downlink messages.printHex / printAscii: Formats and prints received data.Operation Result
After flashing, the module automatically completes OTAA join, periodically reports data, and listens for downlink messages.
The serial port prints join status, uplink/downlink messages, signal quality, etc., as shown below:

Example Description
EspHal.h that perfectly adapts to the ESP‑IDF environment.ESP_LOG for stable and reliable operation.Core Files
EspHal.h: ESP‑IDF hardware abstraction layer providing low‑level implementations for GPIO, SPI, delays, and interrupts (common to all examples).main.cpp: LoRa transmit main program.Code Analysis
EspHal* hal = new EspHal(...): Initializes the ESP‑IDF hardware abstraction layer.radio.irqDioNum = 11: Configures the LR2021 interrupt mapping pin.radio.XTAL = true: Enables the external crystal oscillator to ensure frequency accuracy.setFlag(void): Transmit‑complete interrupt callback function.radio.setPacketSentAction(setFlag): Binds the transmit‑complete interrupt.radio.startTransmit(...): Starts asynchronous LoRa transmission.radio.finishTransmit(): Turns off the RF circuit after transmission to ensure low‑power stability.Operation Result

Example Description
EspHal.h for perfect adaptation to the ESP‑IDF environment.Core Files
EspHal.h: Low‑level hardware abstraction (common to all examples).main.cpp: LoRa receive main program.Code Analysis
EspHal* hal: Initializes ESP‑IDF hardware abstraction (GPIO/SPI/interrupts).radio.irqDioNum = 11: Configures the LR2021 interrupt mapping.radio.XTAL = true: Enables the external crystal oscillator to ensure receive frequency accuracy.setFlag(void): Receive‑complete interrupt callback.radio.setPacketReceivedAction(setFlag): Binds the receive‑complete interrupt.radio.startReceive(): Starts continuous LoRa listening.radio.readData(): Reads and parses the received packet.Operation Result

Example Description
EspHal.h for perfect adaptation to the ESP‑IDF environment.Core Files
EspHal.h: Low‑level hardware abstraction (common to all ESP‑IDF examples).main.cpp: LoRa ping‑pong bidirectional communication application.Code Analysis
EspHal* hal: Instantiates the ESP‑IDF hardware abstraction layer (SPI, GPIO, delays, interrupts).radio.irqDioNum = 11: Configures the LR2021 interrupt mapping pin.radio.XTAL = true: Enables the external crystal oscillator to ensure LoRa frequency accuracy.setFlag(void): Common interrupt callback for both transmit and receive events.radio.setIrqAction(setFlag): Binds the module interrupt callback.INITIATING_NODE: Macro switch that defines the device as the active packet‑sending node.radio.startTransmit() / radio.startReceive(): The program automatically switches between transmit and listen modes.radio.readData(): Parses received packets and prints text and signal parameters.Operation Result

Example Description
EspHal.h for perfect adaptation to the ESP‑IDF environment.Core Files
EspHal.h: Low‑level hardware abstraction (common to all ESP‑IDF examples).04_lr2021_direct_transmit.c: LoRa carrier transmission main program.Code Analysis
EspHal* hal: Initializes ESP‑IDF hardware abstraction (SPI, GPIO, delays, interrupts).radio.irqDioNum = 11: Configures the LR2021 interrupt mapping pin.radio.XTAL = true: Enables the external crystal oscillator to ensure carrier frequency accuracy.OUT_HZ 868000000UL: Defines the continuous transmit frequency (868 MHz).radio.setOutputPower(22): Sets the transmit power to 22 dBm.radio.transmitDirect(OUT_HZ): Enters direct carrier transmission mode, starting continuous transmission.Operation Result

Example Description
EspHal.h; runs independently in the pure ESP‑IDF environment.Core Files
EspHal.h: Low‑level hardware abstraction (common to all ESP‑IDF examples).05_lr2021_lorawan_otaa.c: LoRaWAN OTAA communication main program.Code Analysis
EspHal* hal: Initializes ESP‑IDF hardware abstraction (SPI, GPIO, delays, interrupts).radio.irqDioNum = 11: Configures the LR2021 interrupt mapping pin.radio.XTAL = true: Enables the external crystal oscillator to ensure LoRa frequency accuracy.nvs_flash_init(): Enables NVS storage for saving LoRaWAN session information.restoreLoRaWANState(): Recovers session from flash for fast reconnection.node.beginOTAA() / node.activateOTAA(): OTAA join key functions.node.sendReceive(): Sends uplink data and listens for server downlink messages.printHex / printAscii: Formats and prints downlink data.Operation Result

This chapter contains the following sections. Please read as needed:
Connect according to the table below.
| Core2021-XF | Raspberry Pi Pico/Pico2 |
|---|---|
| CLK | 10 |
| MISO | 11 |
| MOSI | 12 |
| CS | 13 |
| DIO11 | 15 |
| RESET | 5 |
| BUSY | 14 |
core2021-xf\examples\pico directory of the example package.| Example | Basic Description | Dependency Library |
|---|---|---|
| 01_lr2021_tx | LR2021 Transmit | RadioLib |
| 02_lr2021_rx | LR2021 Receive | RadioLib |
| 03_lr2021_pingpong | LR2021 Ping‑Pong | RadioLib |
| 04_lr2021_tx_cw | LR2021 CW Mode Transmit | RadioLib |
| 05_lr2021_LoRaWAN | LoRaWAN | RadioLib |
Navigate to core2021-xf\examples\pico\arduino and select the example program you wish to test.
Select the chip model and port.

After uploading, open the serial port monitor, and the relevant information will be output.
Example Description
Code Analysis
SPI1.setSCK / setRX / setTX: Remaps pins for RP2040 hardware SPI1.SPI1.begin(): Starts the Raspberry Pi Pico hardware SPI1.radio.irqDioNum = 11: Configures the LR2021 interrupt mapping pin; must be set before initialization.radio.XTAL = true: Enables the external crystal oscillator to ensure frequency accuracy.setFlag(void): Interrupt callback function triggered automatically after the module finishes transmission, sets a transmission‑complete flag.radio.setPacketSentAction(setFlag): Binds the transmission‑complete interrupt function.radio.startTransmit("content"): Starts asynchronous LoRa transmission (supports string / byte array).radio.finishTransmit(): Cleanup after transmission, turns off the transmit circuit and resets the module state.loop() main logic: Check transmission‑complete flag → print status → delay → send next packet with sequence number.Operation Result
After compiling and uploading, open the serial monitor to see logs indicating transmission completion, as shown below (in combination with 02_lr2021_rx):

Example Description
Code Analysis
SPI1.setSCK / setRX / setTX: Remaps pins for RP2040 hardware SPI1.SPI1.begin(): Initializes the Raspberry Pi Pico hardware SPI1 bus.radio.irqDioNum = 11: Configures the LR2021 interrupt mapping pin; must be set before initialization.radio.XTAL = true: Enables the external crystal oscillator to ensure frequency accuracy and improve reception stability.setFlag(void): Interrupt callback function triggered automatically after a complete packet is received.radio.setPacketReceivedAction(setFlag): Binds the reception‑complete interrupt service function.radio.startReceive(): Starts continuous LoRa reception mode, enters a data‑waiting state.radio.readData(str): Reads the received wireless data (supports string parsing).radio.getRSSI() / radio.getSNR(): Retrieves signal quality parameters for debugging and link evaluation.loop() main logic: Check reception‑complete flag → read data → parse and print → continue listening.Operation Result
After compiling and uploading, open the serial monitor to see real‑time reception logs including data content, RSSI, and SNR, as shown below (in combination with 01_lr2021_tx):

Example Description
INITIATING_NODE sets the module as the initiator; the other module acts as the responder.Code Analysis
SPI1.setSCK / setRX / setTX: Remaps pins for Raspberry Pi Pico SPI1.SPI1.begin(): Initializes the hardware SPI1 bus.radio.irqDioNum = 11: Configures the LR2021 interrupt mapping pin; must be set before initialization.radio.XTAL = true: Enables the external crystal oscillator to ensure communication frequency accuracy.setFlag(void): General interrupt callback, triggered on either transmit or receive completion.radio.setIrqAction(setFlag): Binds the shared transmit/receive interrupt function.INITIATING_NODE macro: Used to distinguish the initiating node.radio.startTransmit(): Starts packet transmission.radio.startReceive(): Switches the module to listening (receive) mode.radio.readData(str): Reads the received LoRa packet.loop() main logic: Transmit complete → enter receive; receive complete → delay reply → transmit again.Operation Result
INITIATING_NODE macro on one of them.
Example Description
Code Analysis
SPI1.setSCK / setRX / setTX: Remaps pins for Raspberry Pi Pico SPI1.SPI1.begin(): Initializes the hardware SPI1 bus.radio.XTAL = true: Enables the external crystal oscillator to ensure carrier frequency accuracy.OUT_HZ 868000000UL: Defines the direct transmit frequency (868 MHz); can be modified.radio.setOutputPower(22): Sets the transmit power to 22 dBm.radio.transmitDirect(OUT_HZ): Enters continuous direct transmission mode, outputting a fixed‑frequency carrier.loop(): No business logic; the carrier continues to transmit without program intervention.Operation Result
After flashing, the module immediately outputs a fixed‑frequency carrier signal.
The serial port prints initialization and transmission start status; a spectrum analyzer or receiving module can detect the continuous RF signal, as shown below:

Example Description
Code Analysis
SPI1.setSCK/setRX/setTX: Remaps pins for RP2040 hardware SPI1.SPI1.begin(): Initializes the Raspberry Pi Pico hardware SPI1 bus.radio.irqDioNum = 11: Configures the LR2021 interrupt mapping pin; must be set before initialization.radio.XTAL = true: Enables the external crystal oscillator to ensure LoRaWAN frequency accuracy.EEPROM.begin(256): Initializes the RP2040 on‑chip Flash to emulate EEPROM.restoreLoRaWANState(): Recovers session information from EEPROM for fast reconnection.node.beginOTAA()/activateOTAA(): OTAA join key functions.saveLoRaWANState(): Saves session information to EEPROM after successful join.node.sendReceive(): Sends uplink data and automatically listens for downlink messages.printHex / printAscii: Formats and prints downlink data.Operation Result
After flashing, the module automatically completes OTAA join, periodically reports data, and listens for downlink messages.
The serial port prints join status, uplink/downlink data, signal quality, etc., as shown below:

Navigate to core2021-xf\examples\pico\c and select the example program you wish to test.
Select the chip model and port.

After uploading, open the serial port monitor, and the relevant information will be output.
Example Description
Code Analysis
PicoHal* hal = new PicoHal(SPI_PORT, SPI_MISO, SPI_MOSI, SPI_SCK): Creates a Pico hardware abstraction layer instance and binds the SPI port.LR2021 radio = new Module(hal, RFM_NSS, RFM_IRQ, RFM_RST, RFM_BUSY): Initializes the LR2021 module object.radio.irqDioNum = 11: Configures the interrupt mapping pin.radio.XTAL = true: Enables the external crystal oscillator for better frequency accuracy.radio.begin(): Initializes the module and returns an error code.radio.setPacketSentAction(setFlag): Binds the transmission‑complete callback function setFlag().radio.setFrequency(868.0), setOutputPower(22), setBandwidth(125.0), setSpreadingFactor(7), setCodingRate(5), setSyncWord(...), setPreambleLength(8): Configures LoRa parameters.radio.startTransmit("Hello World!"): Starts the first packet transmission.setFlag(): Sets transmittedFlag = true when transmission is complete; this flag is polled in the main loop.for(;;):transmittedFlagradio.finishTransmit() to clean up the transmissionOperation Result
After startup, the serial port prints initialization status (paired with 02_lr2021_rx):

Example Description
Code Analysis
PicoHal* hal = new PicoHal(SPI_PORT, SPI_MISO, SPI_MOSI, SPI_SCK): Creates a Pico hardware abstraction layer instance and binds the SPI port.LR2021 radio = new Module(hal, RFM_NSS, RFM_IRQ, RFM_RST, RFM_BUSY): Initializes the LR2021 module object.radio.irqDioNum = 11: Configures the interrupt mapping pin.radio.XTAL = true: Enables the external crystal oscillator for better frequency accuracy.radio.begin(): Initializes the module and returns an error code.radio.setPacketSentAction(setFlag): Binds the transmission‑complete callback function setFlag().radio.setFrequency(868.0), setOutputPower(22), setBandwidth(125.0), setSpreadingFactor(7), setCodingRate(5), setSyncWord(...), setPreambleLength(8): Configures LoRa parameters.radio.startReceive(): Starts receive mode and enters listening state.setFlag(): Sets receivedFlag = true when a packet is received; this flag is polled in the main loop.for(;;):receivedFlagradio.getPacketLength() and call radio.readData(rxBuf, len)radio.startReceive()Operation Result
After startup, the serial port prints initialization status (paired with 01_lr2021_tx):

Example Description
#define INITIATING_NODE macro; the other module automatically enters receive mode.Code Analysis
PicoHal* hal = new PicoHal(SPI_PORT, SPI_MISO, SPI_MOSI, SPI_SCK): Creates a Pico hardware abstraction layer instance and binds the SPI port.LR2021 radio = new Module(hal, RFM_NSS, RFM_IRQ, RFM_RST, RFM_BUSY): Initializes the LR2021 module object.radio.irqDioNum = 11: Configures the interrupt mapping pin.radio.XTAL = true: Enables the external crystal oscillator for better frequency accuracy.radio.begin(): Initializes the module and returns an error code.radio.setPacketSentAction(setFlag): Binds the transmit/receive complete callback function.INITIATING_NODE determines whether the node first sends data.for(;;):operationDone flag.radio.startReceive()radio.readData(rxBuffer, rxLen)Operation Result
INITIATING_NODE.
Example Description
Code Analysis
PicoHal* hal = new PicoHal(SPI_PORT, SPI_MISO, SPI_MOSI, SPI_SCK): Creates a Pico hardware abstraction layer instance and binds the SPI port.LR2021 radio = new Module(hal, RFM_NSS, RFM_IRQ, RFM_RST, RFM_BUSY): Initializes the LR2021 module object.radio.irqDioNum = 11: Configures the interrupt mapping pin.radio.XTAL = true: Enables the external crystal oscillator for better frequency accuracy.radio.begin(): Initializes the module and returns an error code.radio.setOutputPower(22): Sets the transmit power to 22 dBm.radio.transmitDirect(OUT_HZ): Enters continuous direct transmission mode, outputting a fixed‑frequency carrier.Operation Result
After flashing, the module immediately outputs a fixed‑frequency carrier signal.
The serial port prints initialization and transmission start status; a spectrum analyzer or receiving module can detect the continuous RF signal, as shown below:

Example Description
uplinkIntervalSeconds).Code Analysis
PicoHal* hal = new PicoHal(SPI_PORT, SPI_MISO, SPI_MOSI, SPI_SCK): Creates a Pico hardware abstraction layer instance and binds the SPI port.LR2021 radio = new Module(hal, RFM_NSS, RFM_IRQ, RFM_RST, RFM_BUSY): Initializes the LR2021 module object.LoRaWANNode node(&radio, &Region, subBand): Creates a LoRaWAN node instance.FLASH_TARGET_OFFSET: storage offsetFLASH_EMULATE_EEPROM_SIZE: emulation sizesaveLoRaWANState() / restoreLoRaWANState(): saves/restores LoRaWAN session statenode.beginOTAA(joinEUI, devEUI, nwkKey, appKey): Initializes OTAA parameters.joinLoRaWAN(restored): Attempts to join the network and saves DevNonce/Session.node.sendReceive(tx, sizeof(tx), 1, rx, &rxLen): Sends uplink data and receives downlink.uplinkIntervalSeconds seconds before the next transmission.Operation Result
After flashing, the module automatically completes OTAA join, periodically reports data, and listens for downlink messages.
The serial port prints join status, uplink/downlink data, signal quality, etc., as shown below:

This chapter contains the following sections. Please read as needed:
| Image | Description |
|---|---|
| Arduino UNO R3 |
| Arduino UNO R4 |
| Arduino UNO R4 WIFI |
Connect according to the table below.
| Core2021-XF | Arduino UNO |
|---|---|
| CLK | 13 |
| MISO | 12 |
| MOSI | 11 |
| CS | 10 |
| DIO11 | 2 |
| RESET | 3 |
| BUSY | 9 |
core2021-xf\examples\arduino directory of the example package.| Example | Basic Description | Dependency Library |
|---|---|---|
| 01_lr2021_tx | LR2021 Transmit | RadioLib |
| 02_lr2021_rx | LR2021 Receive | RadioLib |
| 03_lr2021_pingpong | LR2021 Ping‑Pong | RadioLib |
| 04_lr2021_tx_cw | LR2021 CW Mode Transmit | RadioLib |
| 05_lr2021_LoRaWAN | LoRaWAN (not available on Arduino R3) | RadioLib |
Select the development board:
| Image | Description |
|---|---|
![]() | Arduino UNO R3 |
![]() | Arduino UNO R4 |
Select the port for the development board, then compile and upload.
After the upload is completed, open the serial port monitor, and the relevant information will be output.
Example Description
Code Analysis
radio.irqDioNum = 11: Configures the LR2021 interrupt mapping pin; must be set before initialization.radio.XTAL = true: Enables the external crystal oscillator to ensure frequency accuracy.setFlag(void): Interrupt callback function triggered automatically after the module finishes transmission, sets a transmission‑complete flag.radio.setPacketSentAction(setFlag): Binds the transmission‑complete interrupt function.radio.startTransmit("content"): Starts asynchronous LoRa transmission (supports string / byte array).radio.finishTransmit(): Cleanup after transmission, turns off the transmit circuit and resets the module state.loop() main logic: Check transmission‑complete flag → print status → delay → send next packet with sequence number.Operation Result
After compiling and uploading, open the serial monitor to see logs indicating transmission completion, as shown below (in combination with 02_lr2021_rx):

Example Description
Code Analysis
radio.irqDioNum = 11: Configures the LR2021 interrupt mapping pin; must be set before initialization.radio.XTAL = true: Enables the external crystal oscillator to ensure frequency accuracy and improve reception stability.setFlag(void): Interrupt callback function triggered automatically after a complete packet is received, sets a reception‑complete flag.radio.setPacketReceivedAction(setFlag): Binds the reception‑complete interrupt service function.radio.startReceive(): Starts continuous LoRa reception mode, enters a data‑waiting state.radio.readData(str): Reads the received wireless data (supports string parsing).radio.getRSSI() / radio.getSNR(): Retrieves signal quality parameters for debugging and link evaluation.loop() main logic: Check reception‑complete flag → read data → parse and print → continue listening.Operation Result
After compiling and uploading, open the serial monitor to see real‑time reception logs including data content, RSSI, and SNR, as shown below (in combination with 01_lr2021_tx):

Example Description
INITIATING_NODE sets the module as the initiator; the other module acts as the responder.Code Analysis
radio.irqDioNum = 11: Configures the LR2021 interrupt mapping pin; must be set before initialization.radio.XTAL = true: Enables the external crystal oscillator to ensure communication frequency accuracy.setFlag(void): General interrupt callback, triggered on either transmit or receive completion.radio.setIrqAction(setFlag): Binds the shared transmit/receive interrupt function.INITIATING_NODE macro: Used to distinguish the initiating node.radio.startTransmit(): Starts packet transmission.radio.startReceive(): Switches the module to listening (receive) mode.radio.readData(str): Reads the received LoRa packet.loop() main logic: Transmit complete → enter receive; receive complete → delay reply → transmit again.Operation Result
INITIATING_NODE macro on one of them.
Example Description
Code Analysis
radio.XTAL = true: Enables the external crystal oscillator to ensure carrier frequency accuracy.OUT_HZ 868000000UL: Defines the direct transmit frequency (868 MHz); can be modified.radio.setOutputPower(22): Sets the transmit power to 22 dBm.radio.transmitDirect(OUT_HZ): Enters continuous direct transmission mode, outputting a fixed‑frequency carrier.loop(): No business logic; the carrier continues to transmit without program intervention.Operation Result
After flashing, the module immediately outputs a fixed‑frequency carrier signal.
The serial port prints initialization and transmission start status; a spectrum analyzer or receiving module can detect the continuous RF signal, as shown below:

Example Description
Code Analysis
radio.irqDioNum = 11: Configures the LR2021 interrupt mapping pin; must be set before initialization.radio.XTAL = true: Enables the external crystal oscillator to ensure LoRaWAN frequency accuracy.EEPROM.begin(): Initializes the on‑chip EEPROM for storing LoRaWAN session information.restoreLoRaWANState(): Restores join session and random values from EEPROM to achieve fast reconnection.node.beginOTAA(): Initializes OTAA join parameters (JoinEUI, DevEUI, AppKey, NwkKey).node.activateOTAA(): Initiates an OTAA join request to the server.saveLoRaWANState(): Saves session information to EEPROM after successful join.node.sendReceive(): Sends uplink data and automatically opens receive windows to listen for downlink messages.printHex / printAscii: Prints the downlink data in hexadecimal and ASCII formats.Operation Result
Refer to the LoRaWAN environment setup document for environment configuration.
After flashing, the module automatically performs OTAA join; once joined successfully, it periodically uploads data.
The serial port prints join status, uplink/downlink messages, RSSI, SNR, etc., as shown below:

This chapter contains the following sections. Please read as needed:
sudo apt install cmake -y
sudo apt install -y liblgpio-dev
cd ~
# Download from github
git clone https://github.com/waveshare/core2021-xf.git
cd core2021-xf/examples/raspberrypi
sudo raspi-config nonint do_spi 0Connect according to the table below.
| Core2021-XF | Raspberry Pi (BCM) |
|---|---|
| CLK | 11 |
| MISO | 9 |
| MOSI | 10 |
| CS | 25 |
| DIO11 | 17 |
| RESET | 22 |
| BUSY | 24 |
core2021-xf/examples/raspberrypi directory of the example package.| Example | Basic Description | Dependency Library |
|---|---|---|
| 01_lr2021_tx | LR2021 Transmit | RadioLib |
| 02_lr2021_rx | LR2021 Receive | RadioLib |
| 03_lr2021_pingpong | LR2021 Ping‑Pong | RadioLib |
| 04_lr2021_tx_cw | LR2021 CW Mode Transmit | RadioLib |
| 05_lr2021_LoRaWAN | LoRaWAN | RadioLib |
Example Description
Script Description
build.sh: Build script that automatically creates a build directory, generates a Makefile, compiles the project, and outputs the executable file.clean.sh: Clean script that quickly removes generated files and restores the project directory.Code Analysis
PiHal* hal = new PiHal(0, SPI_FREQ_HZ): Raspberry Pi hardware initialization, configures SPI bus and clock.radio.irqDioNum = 11: Configures the LR2021 interrupt mapping pin.radio.XTAL = true: Enables the external crystal oscillator to ensure communication frequency accuracy.setFlag(void): Interrupt callback function that automatically sets a flag when transmission is complete.radio.setPacketSentAction(setFlag): Binds the transmit‑complete interrupt.radio.startTransmit(): Starts asynchronous LoRa packet transmission.radio.finishTransmit(): Turns off the RF circuit after transmission to ensure stable low‑power operation.** Program Usage**
lr2021_tx directory and execute the following commands:cd ~/core2021-xf/examples/raspberrypi/lr2021_tx
chmod +x *
./build.sh
./build/01_lr2021_tx
Operation Result

Example Description
Script Description
build.sh: Build script that automatically creates a build directory, generates a Makefile, compiles the project, and outputs the executable file.clean.sh: Clean script that quickly removes generated files and restores the project directory.Code Analysis
PiHal* hal = new PiHal(0, SPI_FREQ_HZ): Raspberry Pi hardware initialization, configures SPI bus and clock.radio.irqDioNum = 11: Configures the LR2021 interrupt mapping pin.radio.XTAL = true: Enables the external crystal oscillator to ensure communication frequency accuracy.setFlag(void): Interrupt callback function that automatically sets a flag when reception is complete.radio.setPacketReceivedAction(setFlag): Binds the receive‑complete interrupt.radio.startReceive(): Starts asynchronous LoRa listening mode.radio.readData(): Reads the received packet, supports HEX and string parsing.radio.getRSSI() / radio.getSNR(): Retrieves signal quality parameters.** Program Usage**
lr2021_rx directory and execute the following commands:cd ~/core2021-xf/examples/raspberrypi/lr2021_rx
chmod +x *
./build.sh
./build/02_lr2021_rx
Operation Result

Example Description
Script Description
build.sh: Build script that automatically creates a build directory, generates a Makefile, compiles the project, and outputs the executable file.clean.sh: Clean script that quickly removes generated files and restores the project directory.Code Analysis
PiHal* hal = new PiHal(0, SPI_FREQ_HZ): Raspberry Pi hardware initialization, configures SPI bus and clock.radio.irqDioNum = 11: Configures the LR2021 interrupt mapping pin.radio.XTAL = true: Enables the external crystal oscillator to ensure communication frequency accuracy.setFlag(void): Common interrupt callback triggered on either transmit or receive completion.INITIATING_NODE: Macro to distinguish the initiating node.radio.startTransmit(): Starts packet transmission.radio.startReceive(): Switches the module to listening mode.radio.readData(): Reads the received LoRa packet and parses it.** Program Usage**
lr2021_pingpong directory and execute the following commands:cd ~/core2021-xf/examples/raspberrypi/lr2021_pingpong
chmod +x *
./build.sh
./build/03_lr2021_pingpong
Operation Result

Example Description
Script Description
build.sh: Build script that automatically creates a build directory, generates a Makefile, compiles the project, and outputs the executable file.clean.sh: Clean script that quickly removes generated files and restores the project directory.Code Analysis
PiHal* hal = new PiHal(0, SPI_FREQ_HZ): Raspberry Pi hardware initialization, configures SPI bus and clock.radio.XTAL = true: Enables the external crystal oscillator to ensure carrier frequency accuracy.OUT_HZ 868000000UL: Defines the continuous transmit frequency (868 MHz).radio.setOutputPower(22): Sets the transmit power to 22 dBm.radio.transmitDirect(OUT_HZ): Enters continuous direct transmission mode, outputting a fixed‑frequency carrier.** Program Usage**
04_lr2021_tx_cw directory and execute the following commands:cd ~/core2021-xf/examples/raspberrypi/04_lr2021_tx_cw
chmod +x *
./build.sh
./build/04_lr2021_tx_cw
Operation Result

Example Description
lorawan_state.bin to persistently store session information, allowing fast reconnection after power‑off reboot without re‑joining.Script Description
build.sh: Build script that automatically creates a build directory, generates a Makefile, compiles the project, and outputs the executable file.clean.sh: Clean script that quickly removes generated files and restores the project directory.Code Analysis
PiHal* hal = new PiHal(0, SPI_FREQ_HZ): Raspberry Pi hardware initialization, SPI0 + 8 MHz clock.radio.irqDioNum = 11: Configures the LR2021 interrupt mapping to ensure stable communication.radio.XTAL = true: Enables the external crystal oscillator to ensure LoRaWAN frequency accuracy.saveLoRaWANState(): Saves session data to a local file for persistence.restoreLoRaWANState(): Recovers session from the file, supports fast reconnection.node.beginOTAA() / node.activateOTAA(): OTAA join key functions.node.sendReceive(): Sends uplink data and listens for server downlink messages.printHex / printAscii: Formats and prints downlink data.** Program Usage**
05_lr2021_LoRaWAN directory and execute the following commands:cd ~/core2021-xf/examples/raspberrypi/05_lr2021_LoRaWAN
chmod +x *
./build.sh
./build/05_lr2021_LoRaWAN
Operation Result

Connect according to the table below:
| Core2021-XF | STM32L476RG |
|---|---|
| CLK | PA5 |
| MISO | PA6 |
| MOSI | PA7 |
| CS | PA8 |
| DIO8 | PA1 |
| RESET | PA0 |
| BUSY | PB3 |
examples/main_examples directory of Lora-net/usp.Example Description
Code Analysis
smtc_modem_hal.h, smtc_hal_mcu.h, smtc_hal_gpio.h, smtc_hal_watchdog.hradio_tx_irq_callback, radio_rx_irq_callback, timer_irq_callbackralf_params_lora_t rx_lora_param / tx_lora_param: simulated RX/TX configurations for porting testsporting_test_spi(): Verifies SPI port and chip firmware versionporting_test_radio_irq(): Verifies TX/RX interrupt triggeringporting_test_get_time(): Checks HAL time‑getting functionporting_test_timer_irq(): Verifies timer interruptsporting_test_stop_timer(): Verifies timer stop functionalityporting_test_disable_enable_irq(): Verifies interrupt disable/enable logicporting_test_random(): Verifies random number generation and distributionporting_test_config_rx_radio() / porting_test_config_tx_radio(): Checks RX/TX configuration timeporting_test_sleep_ms(): Checks MCU sleep timeporting_test_timer_irq_low_power(): Verifies timer interrupts in low‑power modeporting_test_flash() / test_context_store_restore()main_porting_tests():ENABLE_TEST_FLASHOperation Result

Example Description
"LoRa" every 10 seconds by default)Code Analysis
hal_mcu_init(): Initializes MCU peripheralssmtc_rac_init(): Initializes the Radio Abstraction Layeruser_button_t stores button state and press durationuser_button_callback(): Debounces the button and triggers Manager modeSMTC_LED_TX / SMTC_LED_RXping_pong_init(): Initializes Ping‑Pong transactionsperiodic_uplink_init(): Initializes periodic uplinkhal_watchdog_reload()smtc_rac_run_engine()ping_pong_on_button_press()hal_mcu_set_sleep_for_ms(SLEEP_DELAY)ping_pong_init(): Sets transaction context, payload, and modulation parametersping_pong_tx() / ping_pong_rx(): Sends/receives PING or PONGpre_ping_pong_callback() / post_ping_pong_callback():Operation Result

Example Description
example_options.h to provide LoRaWAN credentials:USER_LORAWAN_DEVICE_EUIUSER_LORAWAN_JOIN_EUIUSER_LORAWAN_GEN_APP_KEYUSER_LORAWAN_APP_KEYMODEM_EXAMPLE_REGION (EU868 or WW_2G4)Code Analysis
hal_mcu_init(), hal_gpio_init_in() configure peripheralssmtc_rac_init() and smtc_modem_init() initialize the modem, with callback modem_event_callback()user_button_callback() handles button events and triggers an immediate uplinkperiodical_uplink_init() initializes the transactionpost_periodic_callback() schedules the next uplinkmodem_event_callback() handles various modem events:RESET: sets credentials and region, initiates JoinJOINED: successful join, starts the first periodic uplinkALARM: periodic uplink triggerTXDONE: uplink transmission completedDOWNDATA: downlink data receivedLINK_CHECK, CLASS_B_PING_SLOT_INFO, etc.) print debug informationsmtc_modem_run_engine() and smtc_rac_run_engine() to process transactionsOperation Result

Semtech's LoRa is a long-range, low-power wireless platform for the Internet of Things (IoT). Generally, it refers to radio frequency chips using LoRa technology. Its main features are as follows:
LoRa (short for long range) uses a spread spectrum modulation technology derived from Chirp Spread Spectrum (CSS) technology. It is a type of long-distance wireless transmission technology and LPWAN communication technology. Spread spectrum technology trades bandwidth for sensitivity. Technologies like Wi-Fi and ZigBee also use spread spectrum, but LoRa modulation is characterized by approaching the Shannon-Hartley theorem limit, maximizing sensitivity improvement. Compared to traditional FSK technology, at the same communication rate, LoRa has 8~12 dBm better sensitivity than FSK. Currently, LoRa primarily operates in the Sub-GHz ISM bands.
LoRa technology integrates digital spread spectrum, digital signal processing, and forward error correction coding, significantly improving long-distance communication performance. LoRa's link budget is superior to any other standardized communication technology; the link budget is the main factor determining distance in a given environment.
LoRa RF chips mainly include the SX127X series, SX126X series, and SX130X series. The SX127X and SX126X series are used for LoRa nodes, while the SX130X series is used for LoRa gateways. For details, refer to Semtech's product list.
LoRaWAN is an open protocol for Low-Power Wide-Area Networks (LPWAN) built on top of the LoRa radio modulation technique. It is designed to wirelessly connect battery-powered "things" to the internet in regional, national, or global networks, targeting key IoT requirements such as bidirectional end-to-end communication, end-to-end security, mobility, and localization services. Nodes require network join authentication to connect wirelessly to the internet, establishing an encrypted communication channel between the node and the server. The LoRaWAN protocol layers are shown in the figure below.
The Class A/B/C node device types in the MAC layer cover almost all IoT application scenarios. The difference between them lies in the transmission and reception time slots of the nodes.
In the Modulation layer, parameters like EU868 and AS430 indicate different frequency bands used in different countries. Please refer to the link for regional parameters.

Implementing a LoRaWAN network covering a city or other area requires four components: nodes (LoRa node RF chips), gateways (or base stations, LoRa gateway RF chips), servers, and the cloud, as shown in the figure below.
A DEVICE (node device) must first initiate a join request packet to the GATEWAY and then to the server. Only after successful authentication can it normally send and receive application data with the server.
The GATEWAY can communicate with the server via wired networks or 3/4/5G wireless networks.
Major server-side operators include TTN, etc. For setting up your own cloud service, please refer to lorawan-stack or chirpstack.

ModemE_application_examples and only demonstrates the basic LoRaWAN Class A application. Other advanced examples can be ported from the official repository, including: Join Request, LoRaWAN Class B application, LoRaWAN Multicast Class B/C examples, and FUOTA examples.Raspberry Pi 4B (with compatible power supply)
TF card (TF card with a capacity greater than 8GB is recommended)
Card reader
Gateway device
Node device
Development board (optional models): ESP32, Raspberry Pi, STM32, and Raspberry Pi Pico

This example uses ChirpStack as the LoRaWAN network server. Please follow the official Raspberry Pi installation steps for configuration.
First, download the ChirpStack Gateway OS image, extract it, and use Win32DiskImager to write the image to the TF card.
Downloading the Image
Writing the Image
After writing, please refer to the official documentation for detailed configuration. This document only provides a brief installation guide. For details, see: ChirpStack Gateway OS Getting Started Guide.
Insert the TF card into the Raspberry Pi and power it on. After booting, your computer's Wi-Fi should detect a wireless hotspot named ChirpStackAP-XXXXXX with the password ChirpStackAP. After connecting successfully, access 192.168.0.1 in a browser to open the ChirpStack management interface. No password is required for the first login.
Connecting to Wi-Fi
Accessing the Web Interface
After booting, you can connect to an external network via Ethernet or Wi-Fi. Here, connecting via Ethernet is used as an example. To configure Wi-Fi, please refer to: Wi-Fi Configuration. After connecting to the network, you can view the current IP address in the web management interface.

After the server configuration is complete and the IP address is obtained, power off the Raspberry Pi and disconnect the power. Connect the SX1303-868M-LoRaWAN-Gateway-HAT (gateway device) to the Raspberry Pi and attach the antenna. Power on the Raspberry Pi. In your browser, navigate to the previously obtained IP address to enter the ChirpStack management interface. Click ChirpStack -> Concentratord and enable the gateway function. Using the SX1303 (868 MHz) as an example, configure it as shown below, then click "Save & Apply":

Enabling the Gateway

Configuring Gateway Parameters
Use the IP address obtained earlier to access the device remotely via an SSH tool (e.g., MobaXterm). The default username is root. After a successful connection, enter the following command in the terminal to get the gateway ID: gateway-id. The system will output the current device's gateway ID. Make a note of this ID; it will be needed when adding the gateway later.

Navigate to Applications -> ChirpStack. The first time you enter, you need to log in. The default username and password are both admin. After logging in, click Gateways -> Add gateway, fill in the gateway-id obtained earlier on the Add page, and save it. Return to the Gateway page to see if the gateway has been successfully launched.

Adding Gateway to Server

Checking if Gateway is Online
This example uses the Core1121‑XF as the node.
First, add a device profile in the web interface: Device Profiles -> Add device profile. Configure as shown in the figure below:

Then, add an application: Applications -> Add application, fill in the relevant information, and save:


Setting EUI

Setting Key
Use the 05_lr2021_LoRaWAN example program. Open it and go to the directory: core2021-xf\examples\arduino\05_lr2021_LoRaWAN. Edit the config.h file and fill in the EUI and keys generated earlier in the corresponding fields. Then compile and flash.

After flashing, the node will automatically request to join the LoRaWAN network. Upon successful joining, the node will periodically send uplink data. You can view device events and communication status through the web interface:
①. Click Events to view the node's operating status.
②. Check for join failures.
③. If joining is successful, you can see the join event.
④. View the data reported by the node.
⑤. View debug information via the serial port.

The server also supports sending downlink data to the node:
①. Click Queue.
②. Enter the hexadecimal data to be sent.
③. Click Send.
④. The node receives the data and prints it on the serial port.

Development Board Design Files
Monday-Friday (9:30-6:30) Saturday (9:30-5:30)
Email: services01@spotpear.com