Skip to content

beegee-tokyo/SX126x-Arduino

Repository files navigation

SX126x-Arduino Build StatusDocumentation


Arduino library for LoRa communication with Semtech SX126x chips. It is based on Semtech's SX126x libraries and adapted to the Arduino framework for ESP32, ESP8266, nRF52832 and RP2040. It will not work with other uC's like AVR.
LoRaWAN version: MAC V1.0.2 and Regional Parameters version: PHY V1.0.2 REV B



IMPORTANT:

RAK11300 module (RP2040) support is only tested with the ArduinoCore Mbed BSP. It will not work with other BSP's for the Raspberry RP2040.

NEWS:

Current testing RAK11300/RAK11310 with the Arduino Pico BSP, still experimental, but promising.



IMPORTANT: READ WHAT'S NEW IN V2 Some major changes are made in V2 of the SX126x-Arduino library:
- The library now supports all LoRaWAN regions without re-compiling
- The interrupt handling for SX126x IRQ's are taken into separate tasks for ESP32, nRF52 and RP2040
This requires some code changes in your existing applications. Please read WHAT'S NEW IN V2 to learn how to migrate your application to use SX126x-Arduino V2


Content

General Info LoRa   LoRaWan
  Based on   Basic LoRa communication     LoRaWAN region definitions
  Licenses     HW structure definition   LoRaWan functions
Changelog     GPIO definitions     Initialize
Features     Example HW configuration     Callbacks
Functions     Initialize the LoRa HW     Join
  Module specific setup     Initialization for specific modules     LoRaWan single channel gateway
  Chip selection     Setup the callbacks for LoRa events     Limit frequency hopping to a sub band
  LoRa parameters     Initialize the radio
  SPI definition     Initialize the radio after CPU woke up from deep sleep Examples
  TXCO and antenna control     Start listening for packets Installation

General info

I stumbled over the SX126x LoRa family in a customer project. Most of the existing Arduino libraries for Semtech's SX127x family are unfortunately not working with this new generation LoRa chip. I found a usefull base library from Insight SIP which is based on the original Semtech SX126x library and changed it to work with the ESP32.
For now the library is tested with an eByte E22-900M22S module connected to an ESP32 and an Insight SIP ISP4520 which combines a Nordic nRF52832 and a Semtech SX1262 in one module. It is as well tested with an RAKwireless WisCore RAK4630 module

Check out the example provided with this library to learn the basic functions.

For the deep sleep support on the ESP32 check out the example DeepSleep.

THIS IS WORK IN PROGRESS AND NOT ALL FUNCTIONS ARE INCLUDED NOR TESTED. USE IT AT YOUR OWN RISK!


Based on


Licenses

Library published under MIT license

Semtech revised BSD license for codeparts used from Semtech S.A.

--- Revised BSD License ---
Copyright (c) 2013, SEMTECH S.A.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the Semtech corporation nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Changelog

Code releases

  • 2024-02-24
    • Fix callbacks for P2P RX and TX timeout not being called
  • 2023-12-14
    • Seems RAK11300 and arduino-pico BSP is working now. still experimental
  • 2023-12-12
    • Added support for the RAK11300 in arduino-pico BSP still experimental
  • 2023-11-22
    • Change behaviour of IRQ_HEADER_ERROR, thanks to @JeromeBriot
  • 2023-10-01
    • Fix public/private network always public
    • Add option to restart MAC stack to change e.g. region without re-init timers
  • 2023-08-27
    • Add function to reset MAC counters
  • 2023-05-16
    • Fix typo in RadioTimeOnAir for FSK
    • Improve RadioTimeOnAir for FSK, thanks to @mikedupi
  • 2023-04-17
    • Fix runtime problem in LoRaMacHelper, thanks to @avillacis
  • 2023-01-28
    • Update examples, thanks to @DanielBustillos
    • Cleanup RAK4630 initialization
    • Add lmh_getConfRetries() to readback the confirmed package retry setting
    • Make RX timeout configurable with #define RXTIMEOUT_LORA_MAX, thanks to @kisChang
    • Add set default RX gain in SX126xSertRx, thanks to @kisChang
    • Allow change of TCXO control with hwConfig structure, thanks to @dberlin
    • Add confirmed failed callback if the degraded datarate becomes insufficient to transmit the payload length, thanks to @avillacis
    • Fix RX window timeouts, thanks to @battosai30
  • 2022-10-11
    • Fix RP2040 timers, thanks to @kisChang
  • 2022-09-01
    • Fix wrong RX2 frequency in AS923-2, AS923-3, AS923-4
  • 2022-07-13
    • Fix crash when library debug is enabled in AS923 region.
  • 2022-03-21
    • Fix RAK11310 timer problem
  • 2022-03-03
    • Fix AS923 CFlist channel assignment bug
  • 2022-02-09
    • Fix EU868 bug that enabled always all 8 channels
  • 2021-12-18
    • Fix bug in lmh_datarate_set. Function did change only ADR, but did not update the datarate setting
  • 2021-12-09
    • Make antenna switch control compatible with different switches
  • 2021-11-26
    • Improve the examples and correct outdated information
  • 2021-11-01
    • Correct handling of TX timeout in Radio callbacks
  • 2021-09-20
    • Add missing declaration for lora_rak13300_init()
  • 2021-08-11:
    • Correct power regulator setting for RAKwireless RAK11300 module from LDO to DCDC
  • 2021-07-22:
    • Add support for RAKwireless RAK11300 module (Raspberry RP2040 + SX1262 module)
  • 2021-06-30:
    • Add callbacks for LoRaWAN TX finished (both confirmed and unconfirmed)
    • Add addional bandwidths for LoRa transmissions. Breaks Radio.TimeOnAir() for bandwidths other than BW 125, 250 and 500
    • Fix minor problem in CF list handling for AS923-x regions
  • 2021-05-15:
    • Implement new regions AS923-2, AS923-3, AS923-4, RU864
    • Test CF list to add additionals channesl on AS923 and RU864
  • 2021-04-10:
    • Add support for all LoRaWAN regions without recompilation of the code
    • Add background handling of SX126x IRQ's for better performance
    • Read WHAT'S NEW IN V2 to migrate your application to V2
  • 2021-03-10:
    • Fix AS923 OTAA join problem
  • 2021-02-26:
    • Fix join bug when first OTAA join fails
    • When OTAA join failed callback was called, following lmh_join() calls fail always
  • 2021-02-11:
    • Add callback for OTAA join failure
  • 2021-02-02:
    • Fix ADR problem
    • Fix Join problem for some Regions
    • Add some debug output
    • Add option to set node class during initialization. Defaults to CLASS_A for backward compatibility: lmh_error_status lmh_init(lmh_callback_t *callbacks, lmh_param_t lora_param, bool otaa, eDeviceClass class = CLASS_A);
  • 2020-09-29:
    • Fix wrong control of antenna switch for RAK4631
    • Add option to control power of antenna switch by the library with _hwConfig.USE_RXEN_ANT_PWR
  • 2020-08-01:
    • Fixed linker error when header files are included from multiple source files
  • 2020-07-09:
    • Duty cycle and adaptive data rate control moved out of Commissioning.h
  • 2020-06-25:
    • Rework the timer functions for nRF52 family. OTAA now working better
    • Add option to select between OTAA and ABP for LoRaWan when calling lmh_init()
    • Reworked SX126x reset function
    • Support for RAKwireless RAK4630/4631 => lora_rak4630_init()
  • 2020-06-14:
    • Fix Travis CI & documentation
    • Add option to select LDO instead of DCDC for SX126x chip in hwConfig struct
  • 2020-05-22:
    • Fix compiler errors when OTAA is selected
  • 2020-05-20:
    • Add compatibility with nRF52840 (experimental)
    • Fix ArduinoIDE compile problems
    • Fix examples
  • 2020-03-28:
    • Fix bug in LoRaWan Class switch
  • 2020-03-10:
    • Added new SetCadParameter function to Radio class
  • 2020-01-16:
    • Fix bug in receive callbacks in case a CRC error is detected.
    • Added Preamble detection callback
    • Added two more examples for a sensor node and a gateway node with deep sleep usage.
  • 2019-12-28:
    • Updated examples
  • 2019-12-12:
    • Added check if SX126x is really connected
    • Fixed second bug in the definition of the sync word
    • Added IRQ settings in RadioSetRxDutyCycle
  • 2019-12-09:
    • Fixed bug in the definition of the sync word
    • Added possibility to re-init connection to SX1261/2 after CPU wakes up from sleep/deep-sleep
      • lora_hardware_re_init() to re-initialize SX1262 connection without resetting the LoRa chip
      • Radio.ReInit() to re-initialize SX1262 connection without resetting the LoRa chip
      • Radio.IrqProcessAfterDeepSleep() to handle IRQ that woke up the CPU (RX_DONE, TX_DONE, ...)
  • 2019-11-09:
    • Added Workarounds for limitations as written in DS_SX1261-2_V1.2 datasheet
    • Tested with both Single Channel (ESP32) and 8 Channel (Dragino LPS8) LoRaWan gateways
    • Added possibility to force use of sub band of region lmh_setSubBandChannels()
  • 2019-10-12:
    • On PlatformIO no more need to edit Commissioning.h. Everything is done with functions and build flags
    • On ArduinoIDE reduced edititing of Commissioning.h. Only the region has to be setup by #define
    • Replaced LoRaWan definitions by function calls lmh_setDevEui, lmh_setAppEui, lmh_setAppKey, lmh_setNwkSKey, lmh_setAppSKey, lmh_setDevAddr, lmh_setSingleChannelGateway
    • Updated LoRaWan examples
    • Added CHANNEL.MD and DATARATE.MD lists
    • Beautify README.MD
  • 2019-10-11: Added support for LoRaWan connection to single channel Gateway (no frequency hopping)
  • 2019-10-09:
    • Tested LoRaWan with a single channel LoRaWan gateway.
    • Added support for single channel gateways
    • Added support for Insight SIP ISP4520 SoC (nRf52832 + SX1261/2 in one package)
  • 2019-08-01: Added Espressif ESP8266 support
  • 2019-07-31: Added LoRaWan support (only partly tested)
  • 2019-07-28: Restructure of folders, added nRF52832 support
  • 2019-07-26: First commit.

Features

  • Support SX1261, SX1262 and SX1268 chips
  • Support of LoRa protocol and FSK protocol (theoretical, I did not test FSK at all)
  • Flexible setup for different modules (antenna control, TCXO control)
  • Support LoRaWan node class A and C tested with single channel LoRaWan gateway

Functions

WORK IN PROGRESS
Check out the example provided with this library to learn the basic functions.
See examples


Module specific setup

To adapt the library to different modules and region specific ISM frequencies some defines are used. The following list is not complete yet and will be extended


Chip selection

#define SX1261_CHIP // if your module has a SX1261 chip    
#define SX1262_CHIP // if your module has a SX1262 or SX1268 chip    

MCU to SX126x SPI definition

The hardware configuration is given to the library by a structure with the following elements

  hwConfig.CHIP_TYPE = SX1262_CHIP;         // SX1261_CHIP for Semtech SX1261 SX1262_CHIP for Semtech SX1262/1268
  hwConfig.PIN_LORA_RESET = PIN_LORA_RESET; // GPIO pin connected to NRESET of the SX126x    
  hwConfig.PIN_LORA_NSS = PIN_LORA_NSS;     // GPIO pin connected to NSS of the SX126x    
  hwConfig.PIN_LORA_SCLK = PIN_LORA_SCLK;   // GPIO pin connected to SCK of the SX126x    
  hwConfig.PIN_LORA_MISO = PIN_LORA_MISO;   // GPIO pin connected to MISO of the SX126x    
  hwConfig.PIN_LORA_DIO_1 = PIN_LORA_DIO_1; // GPIO pin connected to DIO 1 of the SX126x    
  hwConfig.PIN_LORA_BUSY = PIN_LORA_BUSY;   // GPIO pin connected to BUSY of the SX126x    
  hwConfig.PIN_LORA_MOSI = PIN_LORA_MOSI;   // GPIO pin connected to MOSI of the SX126x    
  hwConfig.RADIO_TXEN = RADIO_TXEN;         // GPIO pin used to enable the RX antenna of the SX126x    
  hwConfig.RADIO_RXEN = RADIO_RXEN;         // GPIO pin used to enable the TX antenna of the SX126x    
  hwConfig.USE_DIO2_ANT_SWITCH = false;     // True if DIO2 is used to switch the antenna from RX to TX
  hwConfig.USE_DIO3_TCXO = true;            // True if DIO3 is used to control the voltage of the TXCO oscillator
  hwConfig.USE_DIO3_ANT_SWITCH = false;     // True if DIO3 is used to enable/disable the antenna
  hwConfig.USE_LDO = false;                 // False if SX126x DCDC converter is used, true if SX126x LDO is used
  hwConfig.USE_RXEN_ANT_PWR = false;        // If set to true RADIO_RXEN pin is used to control power of antenna switch

Explanation for LDO and DCDC selection

The hardware of the SX126x chips can be designed to use either an internal LDO or an internal DCDC converter. The DCDC converter provides better current savings and will be used in most modules. If there are problems to get the SX126x to work, check which HW configuration is used and set USE_LDO accordingly.
If USE_LDO is not set in the hwConfig, DCDC is used as default.


Explanation for TXCO and antenna control

  • RADIO_TXEN and RADIO_RXEN are used on eByte E22-900M22S module to switch the antenna between RX and TX
  • DIO2 as antenna switch is used in the example Semtech design as default and might be used by many modules
  • DIO3 as antenna switch is used by e.g. Insight SIP ISP4520 module which integrates a nRF52832 and a SX126x chip
  • Some modules use DIO3 to control the power supply of the TXCO.
  • Some modules use DIO2 to switch the antenna between RX and TX and a separate GPIO to power the antenna switch on or off. Switching the antenna switch off can reduce the power consumption. The GPIO used to control the antenna power is defined as RADIO_RXEN. LOW == power off, HIGH == power on.

Usage

See examples.
There is one example for ArduinoIDE and one example for PlatformIO available.
The PingPong examples show how to define the HW connection between the MCU and the SX126x chip/module.
Another example is for LoRaWan and is tested with a Single Channel (ESP32) and a 8 Channel (Dragino LPS8) LoRaWan gateways. The examples can be found here: ArduinoIDE and one example for PlatformIO


Basic LoRa communication


HW structure definition

Structure to define the connection between the MCU and the SX126x

hw_config hwConfig;

GPIO definitions

GPIO definitions for an ESP32. Change it to the connections between the ESP32 and the SX126x in your specific HW design

// ESP32 - SX126x pin configuration
int PIN_LORA_RESET = 4;  // LORA RESET
int PIN_LORA_NSS = 5;    // LORA SPI CS
int PIN_LORA_SCLK = 18;  // LORA SPI CLK
int PIN_LORA_MISO = 19;  // LORA SPI MISO
int PIN_LORA_DIO_1 = 21; // LORA DIO_1
int PIN_LORA_BUSY = 22;  // LORA SPI BUSY
int PIN_LORA_MOSI = 23;  // LORA SPI MOSI
int RADIO_TXEN = 26;     // LORA ANTENNA TX ENABLE
int RADIO_RXEN = 27;     // LORA ANTENNA RX ENABLE

LoRa definitions

Check the SX126x datasheet for explanations
The bandwidth can be set to any bandwidth supported by the SX126x:

Index Bandwidth Index Bandwidth
0 125 kHz 5 31.25 kHz
1 250 kHz 6 20.83 kHz
2 500 kHz 7 15.63 kHz
3 62.5 kHz 8 10.42 kHz
4 41.67 kHz 9 7.81 kHz
// Define LoRa parameters
#define RF_FREQUENCY 868000000  // Hz
#define TX_OUTPUT_POWER 22      // dBm
#define LORA_BANDWIDTH 0        // [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3 ... 9 see table]
#define LORA_SPREADING_FACTOR 7 // [SF7..SF12]
#define LORA_CODINGRATE 1       // [1: 4/5, 2: 4/6,  3: 4/7,  4: 4/8]
#define LORA_PREAMBLE_LENGTH 8  // Same for Tx and Rx
#define LORA_SYMBOL_TIMEOUT 0   // Symbols
#define LORA_FIX_LENGTH_PAYLOAD_ON false
#define LORA_IQ_INVERSION_ON false
#define RX_TIMEOUT_VALUE 3000
#define TX_TIMEOUT_VALUE 3000

#define BUFFER_SIZE 64 // Define the payload size here

Example HW configuration

Fill the structure with the HW configuration

  // Define the HW configuration between MCU and SX126x
  hwConfig.CHIP_TYPE = SX1262_CHIP;         // Example uses an eByte E22 module with an SX1262
  hwConfig.PIN_LORA_RESET = PIN_LORA_RESET; // LORA RESET
  hwConfig.PIN_LORA_NSS = PIN_LORA_NSS;     // LORA SPI CS
  hwConfig.PIN_LORA_SCLK = PIN_LORA_SCLK;   // LORA SPI CLK
  hwConfig.PIN_LORA_MISO = PIN_LORA_MISO;   // LORA SPI MISO
  hwConfig.PIN_LORA_DIO_1 = PIN_LORA_DIO_1; // LORA DIO_1
  hwConfig.PIN_LORA_BUSY = PIN_LORA_BUSY;   // LORA SPI BUSY
  hwConfig.PIN_LORA_MOSI = PIN_LORA_MOSI;   // LORA SPI MOSI
  hwConfig.RADIO_TXEN = RADIO_TXEN;         // LORA ANTENNA TX ENABLE
  hwConfig.RADIO_RXEN = RADIO_RXEN;         // LORA ANTENNA RX ENABLE
  hwConfig.USE_DIO2_ANT_SWITCH = false;     // Example uses an eByte E22 module which uses RXEN and TXEN pins as antenna control
  hwConfig.USE_DIO3_TCXO = true;            // Example uses an eByte E22 module which uses DIO3 to control oscillator voltage
  hwConfig.USE_DIO3_ANT_SWITCH = false;     // Only Insight ISP4520 module uses DIO3 as antenna control
  hwConfig.USE_LDO = false;                 // Set to true if SX126x uses LDO instead of DCDC converter
  hwConfig.USE_RXEN_ANT_PWR = false;        // Antenna power is not controlled by a GPIO

Module specific initialization


  • If you use a microcontroller and a separate board with the SX126x transceiver you need to define the hwConfig structure to define the GPIO's used to connect the two chips.
  • If you use the Insight SIP4520 or the RAKwireless RAK4630/4631 modules the connections between the chips are fixed. In this case you do not need the hwConfig structure and can instead use simplified initialzation functions as shown below.

Module specific header files


  • If you use a microcontroller and a separate board with the SX126x transceiver use the generic header files SX126x-Arduino.h and LoRaWan-Arduino.h
  • If you use the RAKwireless RAK4630/4631 modules use the module specific header files SX126x-RAK4630.h and LoRaWan-RAK4630.h
  • If you use the Insight SIP4520 modules use the module specific header files SX126x-ISP4520.h and LoRaWan-ISP4520.h

Initialize the LoRa HW

  lora_hardware_init(hwConfig);

Simplified LoRa HW initialization for specific modules

Some modules integrate an MCU and the SX126x LoRa transceiver and have a fixed connection between them. In these cases a simplified initialization can be used.

Simplified LoRa HW initialization for ISP4520 module

The ISP4520 module has the nRF52832 and SX1261 or SX1262 chips integrated in a module. Therefore the hardware configuration is fixed. To initialize the LoRa chip you need only to specify if the module is based on a SX1261 (ISP4520 EU version) or on a SX1262 (ISP4520 US version).

  lora_isp4520_init(SX1262);

Simplified LoRa HW initialization for RAK4630/4631 module

The RAK4630/4631 module has the nRF52840 and SX1262 chips integrated in a module. Therefore the hardware configuration is fixed.

  lora_rak4630_init();

Simplified LoRa HW initialization for RAK11300/11310 module

The RAK11300/11310 module has the RP2040 and SX1262 chips integrated in a module. Therefore the hardware configuration is fixed.

  lora_rak11300_init();

Simplified LoRa HW initialization for RAK13300 module

The RAK13300 module is an IO module that has a LoRa SX1262 LoRa transceiver. It is made for the RAK11200 ESP32 module and the hardware configuration is fixed.

  lora_rak13300_init();

Initialize the LoRa HW after CPU woke up from deep sleep

When you want to use the deep sleep function of the ESP32 with external wake up source, you do not want to reset and reconfigure the SX126x chip after its IRQ woke up the ESP32. This re-init function sets up only the required definitions for the communication without resetting the SX126x

  lora_hardware_re_init(hwConfig);

Setup the callbacks for LoRa events

  RadioEvents.TxDone = OnTxDone;
  RadioEvents.RxDone = OnRxDone;
  RadioEvents.TxTimeout = OnTxTimeout;
  RadioEvents.RxTimeout = OnRxTimeout;
  RadioEvents.RxError = OnRxError;
  RadioEvents.CadDone = OnCadDone;

Initialize the radio

Initialize the radio and set the TX and RX parameters

  Radio.Init(&RadioEvents);

  Radio.SetChannel(RF_FREQUENCY);

  Radio.SetTxConfig(MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH,
                    LORA_SPREADING_FACTOR, LORA_CODINGRATE,
                    LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON,
                    true, 0, 0, LORA_IQ_INVERSION_ON, TX_TIMEOUT_VALUE);

  Radio.SetRxConfig(MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR,
                    LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH,
                    LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON,
                    0, true, 0, 0, LORA_IQ_INVERSION_ON, true);

Initialize the radio after CPU woke up from deep sleep

When you want to use the deep sleep function of the ESP32 with external wake up source, you do not want to reset and reconfigure the SX126x chip after its IRQ woke up the ESP32. Radio.ReInit() sets up only the required communication without resetting the SX1262. Radio.IrqProcessAfterDeepSleep() is checking the reason for the wake-up IRQ and calls the event handler

  Radio.ReInit(&RadioEvents);

  Radio.IrqProcessAfterDeepSleep();

Start listening for packets

  Radio.Rx(RX_TIMEOUT_VALUE);

LoRaWan

YOU NEED BELOW STEPS ONLY IF YOU WANT TO IMPLEMENT THE LORAWAN FUNCTIONALITY, IT IS NOT REQUIRED FOR BASIC LORA COMMUNICATION
If you want to use LoRaWan communication some additional steps are required.
You need to define a region. The defined region tells the library which frequency and which channels should be used. Valid regions are:

  • LORAMAC_REGION_AS923 -> Asia 923 MHz
  • LORAMAC_REGION_AU915 -> Australia 915 MHz
  • LORAMAC_REGION_CN470 -> China 470 MHz
  • LORAMAC_REGION_CN779 -> China 779 MHz
  • LORAMAC_REGION_EU433 -> Europe 433 MHz
  • LORAMAC_REGION_EU868 -> Europe 868 MHz
  • LORAMAC_REGION_IN865 -> India 865 MHz
  • LORAMAC_REGION_KR920 -> Korea 920 MHz
  • LORAMAC_REGION_US915 -> US 915 MHz
  • LORAMAC_REGION_AS923_2 -> Asia 923 MHz with frequency shift of -1.8MHz (not tested)
  • LORAMAC_REGION_AS923_3 -> Asia 923 MHz with frequency shift of -6.6MHz (e.g. Philippines) (in use)
  • LORAMAC_REGION_AS923_4 -> Asia 923 MHz with frequency shift of -5.9MHz (Israel) (not tested)
  • LORAMAC_REGION_RU864 -> Russia 864 MHz (not tested)

More information:

In addition you need

  • Device EUI if you want to use ABP registration of the device
  • Application EUI
  • Application Key, the AES encryption/decryption cipher application key
  • Device address
  • Network Session Key
  • App Session Key

for your node.

Sparkfun has a nice tutorial how to get these requirements from TheThingsInternet

In addition you must define several LoRaWan parameters.

  • Enable or disable adaptive data rate
  • Set the default or a specific data rate
  • Define if you want to connect to a public or private network
  • Specify the number of join trials in case you use OTAA
  • Specify the TX power
  • Enable or disable the duty cycle transmissions. For EU retion the ETSI mandates duty cycled transmissions.

You can find a lot of information about LoRaWan on the LoRa Alliance website.


LoRaWan region definitions

The LoRaWAN region is set during the lmh_init() call.
See lmh_init() for details.


LoRaWan functions


Set EUIs and keys

To be able to send data over a gateway to an IoT application like TheThingsNetwork you need to set the EUIs and Keys for the device, the application and the sessions. If you are using ABP activation all 6 values need to be set. If you are using OTAA activation, only the device EUI, the application EUI and the application key are required.
For the difference between ABP and OTAA activation read the TheThingsNetwork Wiki.
The EUIs, keys and address should be defined in your code like this:

// Device EUI
uint8_t nodeDeviceEUI[8] = {0x00, 0x95, 0x64, 0x1F, 0xDA, 0x91, 0x19, 0x0B};
// Application EUI
uint8_t nodeAppEUI[8] = {0x70, 0xB3, 0xD5, 0x7E, 0xD0, 0x02, 0x01, 0xE1};
// Application key for AES encryption
uint8_t nodeAppKey[16] = {0x07, 0xC0, 0x82, 0x0C, 0x30, 0xB9, 0x08, 0x70, 0x0C, 0x0F, 0x70, 0x06, 0x00, 0xB0, 0xBE, 0x09};
// Device address
uint32_t nodeDevAddr = 0x260116F8;
// Network session key for AES encryption
uint8_t nodeNwsKey[16] = {0x7E, 0xAC, 0xE2, 0x55, 0xB8, 0xA5, 0xE2, 0x69, 0x91, 0x51, 0x96, 0x06, 0x47, 0x56, 0x9D, 0x23};
// Application session key for AES encryption
uint8_t nodeAppsKey[16] = {0xFB, 0xAC, 0xB6, 0x47, 0xF3, 0x58, 0x45, 0xC7, 0x50, 0x7D, 0xBF, 0x16, 0x8B, 0xA8, 0xC1, 0x7C};

Then, just before initializing the library set these values with

// Setup the EUIs and Keys
lmh_setDevEui(nodeDeviceEUI);
lmh_setAppEui(nodeAppEUI);
lmh_setAppKey(nodeAppKey);
lmh_setNwkSKey(nodeNwsKey);
lmh_setAppSKey(nodeAppsKey);
lmh_setDevAddr(nodeDevAddr);

Connection to a single channel gateway

If the node talks to a single channel gateway you can fix the frequency and data rate and avoid frequency hopping. See more info in LoRaWan single channel gateway

 lmh_setSingleChannelGateway(uint8_t userSingleChannel, int8_t userDatarate)

Initialize

Initialize LoRaWan.

/**@brief Lora Initialisation
 *
 * @param callbacks   Pointer to structure containing the callback functions
 * @param lora_param  Pointer to structure containing the parameters
 * @param otaa        Choose OTAA (true) or ABP (false) activation
 * @param nodeClass   Choose node class CLASS_A, CLASS_B or CLASS_C, default to CLASS_A
 * @param region      Choose LoRaWAN region to set correct region parameters, defaults to EU868
 *
 * @retval error status
 */
	lmh_error_status lmh_init(lmh_callback_t *callbacks, lmh_param_t lora_param, bool otaa, 
	                          eDeviceClass nodeClass = CLASS_A, 
	                          LoRaMacRegion_t region = LORAMAC_REGION_EU868);

Valid regions are: LoRaMacRegion_t region This parameter selects the LoRaWAN region for your application. Allowed values for the region are:

  • LORAMAC_REGION_AS923
  • LORAMAC_REGION_AU915
  • LORAMAC_REGION_CN470
  • LORAMAC_REGION_CN779
  • LORAMAC_REGION_EU433
  • LORAMAC_REGION_EU868
  • LORAMAC_REGION_IN865
  • LORAMAC_REGION_KR920
  • LORAMAC_REGION_US915
  • LORAMAC_REGION_AS923_2
  • LORAMAC_REGION_AS923_3
  • LORAMAC_REGION_AS923_4
  • LORAMAC_REGION_RU864

REMARK

CN779-787 devices may not be produced, imported or installed after 2021-01-01; deployed devices may continue to operate through their normal end-of-life.


Specifiy sub bands

For some regions and some gateways you need to specifiy a sub band to be used. See more info in Limit frequency hopping to a sub band

 lmh_setSubBandChannels(uint8_t subBand)

RAKwireless RAK4630/RAK4631
The subbands for each region are automatically preset to match with the RAKwireless gateways default settings. In this case you do not need to define the sub bands.


Callbacks

LoRaWan needs callbacks and parameters defined before initialization

/** Lora user application data buffer. */ 
static uint8_t m_lora_app_data_buffer[LORAWAN_APP_DATA_BUFF_SIZE];
/** Lora user application data structure. */
static lmh_app_data_t m_lora_app_data = {m_lora_app_data_buffer, 0, 0, 0, 0};

/**@brief Structure containing LoRaWan parameters, needed for lmh_init()
 * 
 * Set structure members to
 * LORAWAN_ADR_ON or LORAWAN_ADR_OFF to enable or disable adaptive data rate
 * LORAWAN_DEFAULT_DATARATE OR DR_0 ... DR_5 for default data rate or specific data rate selection
 * LORAWAN_PUBLIC_NETWORK or LORAWAN_PRIVATE_NETWORK to select the use of a public or private network
 * JOINREQ_NBTRIALS or a specific number to set the number of trials to join the network
 * LORAWAN_DEFAULT_TX_POWER or a specific number to set the TX power used
 * LORAWAN_DUTYCYCLE_ON or LORAWAN_DUTYCYCLE_OFF to enable or disable duty cycles
 *                   Please note that ETSI mandates duty cycled transmissions. 
 */
static lmh_param_t lora_param_init = {LORAWAN_ADR_ON, 
			LORAWAN_DEFAULT_DATARATE, LORAWAN_PUBLIC_NETWORK, 
			JOINREQ_NBTRIALS, LORAWAN_DEFAULT_TX_POWER};

/**@brief Structure containing LoRaWan callback functions, needed for lmh_init() */

static lmh_callback_t lora_callbacks = {BoardGetBatteryLevel, BoardGetUniqueId, BoardGetRandomSeed,
			lorawan_rx_handler, lorawan_has_joined_handler, lorawan_confirm_class_handler,
			lorawan_join_failed_handler, lorawan_unconfirmed_finished, lorawan_confirmed_finished};

The following callbacks are implemented in the library, but you can override them in your application code:
BoardGetBatteryLevel is an empty pre-defined callback in the library. Every board has a different method to read the battery level (or none at all). If you want the LoRaWAN node to report it's battery level, you should write your own function to read and return the battery level. Keep in mind that the LoRaWAN server expects the battery level as a value between 0 and 255. 100% battery level equals 255.
BoardGetUniqueId is a pre-defined callback used by the library to get a unique ID of the board. This is used when the device EUI is assigned automatically. In most use cases this is not used.
BoardGetRandomSeed is used together with BoardGetUniqueId. In most use cases this is not used.
The following callbacks have to be implemented in your application code:
lorawan_rx_handler is called when a Downlink was received from the LoRaWAN server. See examples how to implement it.
lorawan_has_joined_handler is called after the node has successfully joined the network. Keep in mind that when ABP join method is used, this callback is called immediately after lmh_join().
lorawan_confirm_class_handler is called if you change the nodes class with lmh_class_request().
lorawan_join_failed_handler is called if the join process failed. Failing te join process can have multiple reasons. A few can be

  • No gateway in range
  • Gateway not connected to a LoRaWAN server
  • Wrong DevEUI, AppEUI or AppKey

lorawan_unconfirmed_finished is called after a unconfirmed packet send is finished. It is called after RX1 window and RX2 window timed out or after a downlink from the LoRaWAN server was received.
lorawan_confirmed_finished is called after a confirmed packet send is finished. It has a paramter that tells if a confirmation (ACK) was received from the LoRaWAN server or not.


Join

Join the LoRaWan network to be able to send and receive data. Default connection type is

void lmh_join(void)

LoRaWan single channel gateway

By default when using LoRaWan communication, the node is using frequency hoping. That means that for each package to be sent a random frequency is chosen out of the predefined frequencies for a region. The frequency (== channels) for each region can be found in the file CHANNELS.MD.
If connecting the node to a single channel gateway this is a problem, because a single channel gateway can receive only on one channel (== frequency). To get around this problem the channel hoping can be disabled and a fixed frequency (channel) and datarate can be set by the function

void lmh_setSingleChannelGateway(uint8_t userSingleChannel, int8_t userDatarate);

The first paramenter is the channel (frequency) to be used to communicate with the single channel gateway.
Check the specification of your single channel gateway to find out on which channel (frequency) it is listening and then get the channel number from the file CHANNELS.MD.
The second parameter selects the datarate for the communication. Again check the specification of your single channel gateway to find out what datarate it is using and use it in the function call. It might be that instead of the datarate the spreading factor SF and bandwidth BW are documented. In this case you need to check the file DATARATE.MD to find out which datarate to choose.
E.g. the things4u ESP-1ch-Gateway-v5.0 single channel gateway when setup to US915 region is listening on 902.30 Mhz with a bandwidth of 125kHz and a spreading factor of 7. In CHANNEL.MD you can find that 902.30 MHz is channel 0 and in DATARATE.MD you can find that SF7 and BW 125 kHz would be for region US915 the data rate DR_3. In this example we fix the communication to the channel 0 with the datarate DR_3 (SF7 and BW125);

// Setup connection to a single channel gateway
lmh_setSingleChannelGateway(0, DR_3);

Limit frequency hopping to a sub band

While testing the LoRaWan functionality I discovered that for some regions and some LoRaWan gateways it is required to limit the frequency hopping to a specific sub band of the region.
E.g. in the settings of the LoRaWan gateway I bought for testing (Dragino LPS8) you have not only to define the region, but as well one of 8 sub bands. The gateway will listen only on the selected sub band.
The problem is that if the LoRa node uses all available frequencies for frequency hopping, then for sure some of the packets will be lost, because they are sent on frequencies outside of the sub band on which the gateway is listening.
Depending on the region, there could be between 2 and 12 sub bands to select from. Each sub band consists of 8 frequencies with a fixed distance between each. The sub bands are selected by numbers starting with 1 for the first sub band of 8 frequencies.
You have to check with your LoRaWan gateway if you need to setup a sub band
Example to limit the frequency hopping to sub band #1

// For some regions we might need to define the sub band the gateway is listening to
/// \todo This is for Dragino LPS8 gateway. How about other gateways???
if (!lmh_setSubBandChannels(1))
{
	Serial.println("lmh_setSubBandChannels failed. Wrong sub band requested?");
}

Installation

In Arduino IDE open Sketch->Include Library->Manage Libraries then search for SX126x-Arduino
In PlatformIO open PlatformIO Home, switch to libraries and search for SX126x-Arduino. Or install the library in the terminal with platformio lib install SX126x-Arduino

For manual installation download the archive, unzip it and place the SX126x-Android folder into the library directory.
In Arduino IDE this is usually <arduinosketchfolder>/libraries/
In PlatformIO this is usually <user/.platformio/lib>