Using LoRa (Long Range) communication with the Semtech SX1261/1262/1268 modules

Introduction to LoRa

Beside of the wireless communication systems WiFi, Bluetooth and GSM/LTE, there is a less known technology called LoRa that enables 2 devices to communicate with each other over a longer range, in perfect scenarios over kilometers without the need of WiFi access points,  repeaters or cell towers. Available since quite some time with different protocols, the long range communication system is using a license free (at least in most countries) Sub-1GHz radio band called ISM ( industrial, scientific and medical ).

While these frequencies can basically be used by anyone, there are local regulations and rules that must be applied. These regulations and rules define frequency regions and usage allowances that are defined by different legislation’s for different regions of the world. A good source to find regulations applying to your country and region is the LoRa Alliance® resource HUB. There you can find all kind of information (even if it is mainly for LoRaWan, which I will not touch in this introduction). As a start you can look into my collection of frequencies for LoRa by region here. (written by me, so you will find similar explanations there).

While there are many tutorials and blogs for using LoRa with Arduino existing, most of them are based on the RFM95, RFM96 or RFM69 modules. These modules are using the older generation Semtech SX1276 chips. But for the latest LoRa chip generation from Semtech, the SX1261/2/8 chips are barely any Arduino tutorials written.

The Hardware

As an introduction how to use LoRa I will use the Adafruit HUZZAH32 – ESP32 Feather Board together with the Alora RFM1262 Feather Wing from Circuitrocks. For easy assembly I plug both into an Adafruit FeatherWing Doubler Prototype board.

We will need two of these setups. One will be acting as a sensor node. The second one will be a gateway node that forwards data to the IoT.


Why the Semtech SX1262? And why the Alora RFM1262 board?

Since some time Semtech has a new generation of radio chips, the SX126x series. Compared with the SX127x series this new generation offers firstly lower battery consumption, secondly higher transmit power and thirdly better immunitiy to noise on the radio signal.
Because of these advantages the RFM1262 board utilizes the new Semtech SX1262 chip, moreover it has a continuous frequency coverage from 150MHz to 960MHz. Designed to be compatible with Adafruit Feathers, you can add long range communication features to your project. In addition, it offers the use of different antenna types. You can use a simple wire antenna, plug in an flex antenna into the pre-mounted U.FL connector or solder a SMA connector and screw an antenna directly onto the board.

Circuitrocks has as well a nice tutorial and how-to-use LEARN blog for the module.

For the sensor node I will just use a  temperature and humidity Si7021 sensor to collect data and send it from the sensor node over LoRa to the gateway. For the I2C connection of the Si7021 sensor you can either breakout the 3.3V, GND, SDA and SCL lines on the Feather Doubler prototype to a connector and plug the sensor in. Or you switch to an Adafruit Feather Tripler prototype board and use the Circuitrocks SensoWing Prototyping Add-on board to plug in the sensor there.

The Software

As development platform I personally prefer PlatformIO as plugin to Visual Studio Code. But if you want to use ArduinoIDE that is no problem. The source codes will compile there as well.

As SW interface between our application and the SX1262 module we use the SX126x-Arduino library which is written specifically for this module and supports ESP32, ESP8266 and the nRF52 micro controller platforms. I made this library because there was no other Arduino library available for the SX126x chips, beside of RadioLib, but that didn’t support the LoRaWan protocol (it is on the todo list of Jan, so maybe by the time you read this, LoRaWan is supported as well).

To keep it simple the sensor node will collect temperature and humidity data and send it over LoRa to the gateway. The gateway is collecting sensor data from several sensor nodes and forwarding them to an IoT service. I chose IO.Adafruit which is easy to use. But with an free account you can collect and display only 10 different sensor values.

Battery powered

Both the sensor node and the gateway software are optimized to use a battery as power supply instead of the usual 5V USB chargers.

To minimize power consumption both units are most of the time in deep sleep mode. In deep sleep mode, most of the ESP32 is powered down. Only the RTC logic is awake, waiting for an event to wake up the ESP32. There are 3 options to wake up from a deep sleep, touch pad, external interrupt or timer event. Random Nerd Tutorials has a very good article about the deep sleep and the wake up methods here.

On the sensor node we use the timer as a wake up method. After every reading of the Si7021 sensor we send both the ESP32 and the SX1262 into deep sleep to preserve battery charge. After 30 seconds the RTC timer will wake up the ESP32 which then reads the sensor data, wakes up the LoRa modem and sends the data to the gateway.

For the gateway it is a little bit more difficult. Because we do not know at what time a sensor node is sending a data package, we cannot use the timer to wake up from deep sleep. Instead we will use a special mode of the SX1262 chip called RX Duty Cycle. We initialize the SX1262, start the RX Duty Cycle and then send the ESP32 into deep sleep, this time with an external interrupt as wake up event. Once the SX1262 has received a data package it will raise an interrupt which will wake up the ESP32. Then the ESP32 can read the received data package and process it.

The SX1262 will be in low power mode and wake up only for a short period of time to listen if any LoRa packages are about to be sent. Semtech has a good application note how this works, the document SX1261_AN1200.36_SX1261-2_RxDutyCycle_V1.0. I strongly recommend that you read this document before falling into a trap and your ESP32 is forever in deep sleep (unless you push the reset button). For those who don’t want to go through the whole document, I use some excerpts from the application to show you how RX Duty Cycle works and what settings need your special attention.

The principle of RX Duty Cycle

RxDutyCycle is a special RX mode that allows decreasing power consumption by putting periodically the radio in reception, while keeping the microcontroller in sleep mode. When a packet is received by the SX1261/2 it triggers an interrupt for the microcontroller in order to wake it up.

This image has an empty alt attribute; its file name is RxDutyCycle-1.png

Calculation of sleep and receive time length

The important stuff is to calculate the “Rx duration” and the “Sleep duration” times. If you set your sleep time too long, the SX1262 might miss a preamble which is the beginning of a data package.

This image has an empty alt attribute; its file name is Missed-preample.png

But how do we know the preamble length? Semtech has a nice tool, the SX1261 Calculator Tool. You definitely needto download and install this tool, because you need it to calculate the length of the sleep period.

Once you have the tool installed start it. In order to get the length of the preamble (which determines the length of the sleep period) you have to enter 4 parameters in the tool.

  • Spreading factor
  • Bandwidth
  • Code Rate
  • Preamble Length

This image has an empty alt attribute; its file name is LoRa-calculator-output.png

These parameters define the length of a single symbol.

Check the screenshot above where I marked in yellow the parameters you need to enter. The red circled numbers are what we need to calculate the preamble length.

In above example I set the SF to 7, the bandwidth to 125kHz, the CR to 4/5 and the preamble length to 8. That is what I will use later in the code.

You can see that with this settings, a symbol has a length of 1024ms and the preamble is 12.25 symbols long. That means that our preamble length is 1024ms * 12.25 = 12544ms long. So we know we have to set our sleep time shorter than that. To be able to receive at least 2 symbols when trying to catch the beginning of a data packet, I defined the sleep time to 10 symbols length which is 1024ms * 10 = 10240ms.

Ok, so now we know how long we can put the SX126x into sleep without missing a package. For the receive window, the time the SX126x is actually awake and listening, I chose a time of 2 symbols which gives us 1024ms * 2 = 2048ms.

If we calculated the sleep and receive times correct, we will be able to catch any incoming data packet, even in the worst case that we see only the last two symbols of the preamble.

This image has an empty alt attribute; its file name is Captured-preamble.png

Now if you look on these two numbers, sleep time 10ms and awake (receive) time 2ms, that means that the SX126x is basically sleeping 8/10th of the time. And the ESP32 will be in deep sleep as well most of the time and only wake up after the SX1262 received a data package. That should give us a good power saving.

Additional methods to save battery

Beside of Deep Sleep and RX Duty Cycle there are 2 more things that should be done to reduce power consumption.

a) Once you have debugged your code and it is running well, remove (disable) all usage of the Serial port. In the code example this is done with the #define BATT_SAVE_ON 1. when you look through the code you can see that every usage of Serial is fenced by a check if BATT_SAVE_ON is defined 0 or not

This is a compiler directive which tells the compiler whether to include the Serial commands in the code or not. If BATT_SAVE_ON is set to 0, the compiler will include all Serial commands into the generated code. Otherwise all Serial commands will be removed.

b) Reduce the CPU frequency with the simple function setCpuFrequencyMhz(). By default the ESP32 is running on a 240MHz clock. This can be reduced to

  • 160MHz (WiFi and Bluetooth ok)
  • 80MHz (WiFi and Bluetooth ok)
  • 40MHz (no WiFi and no Bluetooth)
  • 26MHz (no WiFi and no Bluetooth)
  • 20MHz (no WiFi and no Bluetooth)
  • 13MHz (no WiFi and no Bluetooth)
  • 10MHz (no WiFi and no Bluetooth)

On the gateway node we will need WiFi, so a reduction to 80MHz is the right choice. On the sensor node we could theoretically go lower, but I never tested it and there might be a problem with the I2C bus to the sensor.

General Software (both Sensor and Gateway node)

The code should be more or less self explaining, but here a few important details.


LoRa settings and the pin configuration for the hardware connection between the ESP32 and the Alora RFM1262

In the first part you setup the LoRa communication definitions. It is important that you choose a transmission frequency that is within the legal ISM frequencies of your country. For the settings it is important to use the same values on the sensor node and the gateway node.

By changing the bandwidth, spreading factor, coding rate and preamble length, you can increase or decrease your data rate. Higher data rate means lower range. Mark Zachman wrote a very nice explanation how these settings influence both the data rate and range in The Best LoRa Settings for Range and Reliability

LoRa event callbacks

The SX126x library is interrupt driven and you need to define functions that handle different events. For the sensor node there are only 3 events that need to be taken care of.

At the end of a successful transmission OnTxDone will be called. At this point the sensor node is ready to back into deep sleep.

In case an error occurred during the transmission OnTxTimeout will be called. In this case you might consider to resend the data package, but in this tutorial I just ignore it and send the node back into deep sleep.

Before sending a package a Channel Activity Detection (CAD) should be started. At the end OnCadDone is called with the result.

About Channel Activity Detection

To avoid collision of data packages sent by different LoRa nodes, the SX1262 has a feature called Channel Activity Detection, short CAD. It is strongly recommended to use this feature before starting to send a data package. If you just start sending and at the same time another LoRa node is already sending data your activity will not only destroy your own data package, but as well the data of the other LoRa node.

So before sending a data package, the SX1262 is set into CAD mode.

Radio.StartCad will check for a defined time for any activity on the selected frequency. If it detects any other LoRa node sending data, OnCadDone will be called with a return value of TRUE. In this case you should not start any transmission. If no activity is detected, OnCadDone will be called with the return value of FALSE. In this case you should immediately start sending your data package.

The time the SX1262 is checking is defined by the given number of symbols (LORA_CAD_08_SYMBOL) and the length of a single symbol (See SX1261 Calculator Tool above how to know the length of a symbol).

For the gateway node there are as well 3 LoRa events that needs to be handled.

OnRxDone is called when a package was received successfully. In this case we get pointers to the data, the size of the received data and information about the signal itself (Signal strength and Signal to Noise Ratio).

In case of a timeout while waiting for a package, onRxTimeout is called.

And if the received package is invalid (CRC checksum is not correct), OnRxError is called. In this case the data package should be ignored.

The Adafruit IO stuff

Kindly Adafruit others a free IoT visualization tool. With this website and the provided Adafruit MQTT library it is quite easy to send your sensor data to the cloud and visualize it.

To be able to send data to the io.adafruit IoT cloud service you will have to sign up for an Adafruit IO account. What we need here in the software are
the AIO_USERNAME and AIO_KEY to be able to send data to
While logged into your Adafruit IO account click on the AIO Key button to get these two values.

This image has an empty alt attribute; its file name is io-adafruit-key.png

After setting up the account details in the gateway software the sensor data can be sent to Adafruit IO

Each feed to Adafruit IO needs a unique name, which should identify the sensor. To avoid changing the source code for each single sensor node you want to setup, I create a unique 4 byte identifier from the ESP32’s MAC address. The SX126x-Arduino library provides a function to get this MAC address and we just have to copy the id into an uint32_t variable.

The sensor node will include its unique ID into the data package together with the sensor data.

The gateway node will extract the unique ID from the data package and use it to send the data to a unique feed on Adafruit IO.

The feed we publish to has the format <USERNAME>/feeds/<FEEDNAME>. We create this feed name with the sprintf command sprintf(devTopic, “%s/feeds/%08X_t”, AIO_USERNAME, dataMsg.nodeId); At the end we add either a _t for the temperature data or a _h for the humidity value.




The full source code can be found in my Github repo for the SX126x-Arduino library.

But just in case you want to copy and paste, here it is as well.

The Sensor Node source code

The Gateway Node source code



Leave a Reply

Your email address will not be published. Required fields are marked *

Free Link Directory