Startup guide for your first LoRaWAN steps with an ESP32 and a LoRa module (ABP activation)
Working with LoRaWAN is fascinating, as you don’t need a Wi-Fi or Bluetooth connection to transfer data to a network service. This tutorial…
Startup guide for your first LoRaWAN steps with an ESP32 and a LoRa module (ABP activation)
Working with LoRaWAN is fascinating, as you don’t need a Wi-Fi or Bluetooth connection to transfer data to a network service. This tutorial shows the basics to run this task with a “ready to use” development board from Heltec. If you have ever tried to wire a LoRa module to an ESP32, you know what I’m talking about—there are 6 to 8 connections to get wired and an additional 4 wires for an OLED display.
For that reason, I’m using development boards where all basic connections are implemented on a small board—this is the Heltec LoRa series.
I’m strongly recommending reading my article “**Easy setup of an account, application, and end device on The Things Network (“TTN”)**” first, as it is essential to make the right settings for your device to avoid any disappointments.
Topics of this tutorial
- What are LoRa and LoRaWAN ?
- What is LoRaWAN ?
- The Things Network (“TTN”) overview
- Development environment
- LoRa development boards
- LoRaWAN library
- Short overview about the OTAA and ABP device activation
- Example sketch
- The Things Network console
- Webhooks / Data integration
- Summary
What are LoRa and LoRaWAN ?
This is just a brief description of LoRa and LoRaWAN. LoRa is the technical implementation of a long-range transmitting system. It can send data over a distance of some hundreds of meters up to some kilometers . The maximum distance at your location depends on parameters like the absence of high buildings in the sight range from the sender to the receiver. The second important parameter is the characteristics of the antenna, as there are “better” and “worse” ones available.
Regional LoRa settings
LoRa runs in a regulated frequency range that depends on your country. There are 3 main bands to use without a license:
- Japan/Asia: 433 MHz
- Europe: 866 MHz
- North America: 915 MHz
Before you are going to order a LoRa module, please ensure on your own that you buy the “right” module for your region. As I’m located in Europe, I bought the 866 MHz variants of LoRa modules and boards. Please don’t use modules with different frequencies, as you will get legal problems !
What is LoRaWAN ?
LoRaWAN is a communication protocol that is based on LoRa and describes how the communication is set up between the sender and receiver and how the data gets encrypted while on the air.
There are 2 communication types in use:
Direct communication between two devices
This is the preferred option when you need a private LoRaWAN network, e.g., with a 1:1 (1 sender, 1 receiver) topology. Usually you will not set up the LoRaWAN protocol for this but use generic LoRa communication for this.
Communication over a Gateway
In most of the use cases, you will have several senders (e.g., for temperature and humidity in different rooms) and just one receiver for all sensor data. The gateway is one of the two variants:
- Private gateway: This will accept connections just from predefined LoRaWAN clients
- Public gateway: They accept the communication with every client that meets some parameters. A public gateway is associated with a LoRaWAN service provider, and the gateway will send incoming data to the service provider. There are some providers operating worldwide, and I’m focusing on the “The Things Network” (Internet of Things) service provider (“TTN”).
The Things Network (“TTN”) overview
**The Things Network** provides a set of open tools and a global, open network to build your next IoT application at low cost, featuring maximum security and ready to scale. It is working worldwide and tries to be available everywhere; a map of current coverage is available here. To work with TTN, you need an account that is available for free.
Development environment
I’m running my project on Arduino 2.3.6 (Windows) and ESP32 boards in version 3.3.4. Please note: although this tutorial is based on Heltec development boards, I don’t use the Heltec ESP32 framework. My tutorial is working with the regular framework by Espressif Systems !
As I’m located in Germany/Europe, I need to use the LoRa frequency band 863+ MHz. Please double-check that your devices and the LoRaWAN library are using the frequency band that is allowed in your country !
LoRa development boards
Since the LoRaWAN library used recognizes many Heltec LoRa boards, I tested the tutorial exclusively with the following boards:
- Heltec WiFi LoRa 32 V2 (out phased)
- Heltec WiFi LoRa 32 V3
- Heltec Wireless Stick V3
- Heltec Wireless Stick Lite V3.
On the library’s GitHub repository, you find some explanations of how to use a LoRa module with other pin mappings.
LoRaWAN library
The core of the tutorial is the **EzLoRaWAN library by Francois Riotte (“rgot-org”), version 1.0.1. For your experiments, you can directly use the OTAA example**, but I recommend using the sketch in my GitHub repository (I add a few extra outputs).
Important: This library uses LoRaWAN version 1.0.3 with RP001 Regional Parameters 1.0.3 revision A.
Don’t use any other version or parameter !
The library uses the LoRa frequency band “Europe” by default. If you are living in a different region, you have to modify the library by setting the right region. Open the file “target-config.h” and scroll down to these code lines:
// This defines the region(s) to use. You can enable more than one and
// then select the right region at runtime using os_getRegion() and/or
//
#include <pins_arduino.h>
#ifdef REGION_EU868
#define CFG_eu868 1
#elif defined (REGION_US915)
#define CFG_us915 1
#else
// todo complete region config
////#define CFG_as923 1
////#define CFG_il915 1
////#define CFG_kr920 1
////#define CFG_au915 1
#define CFG_eu868 1
#endif // REGION_EU868
Short overview about the OTAA and ABP device activation
The most used and recommended activation method is the “Over the air activation” (“OTAA”). Over-the-Air Activation (OTAA) is the secure, scalable way to activate LoRaWAN devices. All commercially available LoRaWAN devices support OTAA, and it is selected by default. If your device cannot be activated using the more secure OTAA, you may manually activate it by programming security keys, i.e., using ABP (Activation by Personalization). If you would like to know more about the differences, I’m recommending this overview (“**ABP vs OTAA**”).
There is another article from me available: ”**Startup guide for your first LoRaWAN steps with an ESP32 and a LoRa module (OTAA activation)“**.
Example sketch
Overall it is the original example from the LoRaWAN library (“ttn_example”), but I added some debug output and changed the duration between two transmissions to 120 seconds (2 minutes).
This sketch does not use any other features of your LoRa Dev board (e.g., a display or LED); it is important that the board is connected by USB with your Serial Monitor to follow the outputs.
In the sketch, a float variable, which represents a fictitious temperature value, is increased by 0.1 degrees with each transmission.
Change the configuration
To get a runnable configuration, you need to copy three configuration parameters from the TTN End Device Registration:
const char* config_devEui = "CHANGE_ME"; // Change to TTN Device EUI
const char* config_appEui = "CHANGE_ME"; // Change to TTN Application EUI
const char* config_appKey = "CHANGE_ME"; // Change to TTN Application Key
As described in my setup guide, you can copy and paste the parameter with the default settings.
Are the parameters to be treated confidentially?
In my opinion, you shouldn’t publish the data, as anyone with these parameters can set up a device on TTN and possibly eavesdrop on communication.
To use the auto-detect feature of the LoRaWAN library, you need to add one line to the original sketch:
#define AUTO_PIN_MAP
Compile and Upload the sketch
It is important to choose the matching ESP32 board name for your device; please use these names:
- Heltec WiFi LoRa 32 V2: Heltec WiFi LoRa 32 (V2)
- Heltec WiFi LoRa 32 V3: Heltec WiFi LoRa 32 (V3)
- Heltec Wireless Stick V3: Heltec Wireless Stick(V3)
- Heltec Wireless Stick Lite V3: Heltec Wireless Stick(V3)
Running the sketch on a Heltec WiFi LoRa 32 V2 device gives this log file (again: don’t publish these data as others can decrypt the payload):
LoRaWAN OTAA activation
EzLoRaWan Radio pins: SCLK 5 MISO 19 MOSI 27 CS 18 DIO0 26 DIO1 35 DIO2 34 BUSY 253 RST 14 TCXO 253
TTN begin
Using stored keys to join
Joining TTN ..................................................................................................................................................................................................................................................................................................................
joined !
---------------Status--------------
Device EUI: 70B3D57ED0075358
Application EUI: 1100010003000001
netid: 13
devaddr: 260B1C8A
NwkSKey: 91DF9181BDA4C975F9655DC0AC9D0636
AppSKey: 1FAFF346E74B1D73E48993CFB67925E4
data rate: 2
tx power: 16dB
freq: 867300000Hz
-----------------------------------
Next transmission in 0 seconds
Temp: 18.300001 TTN_CayenneLPP: 1 67 00B7
Next transmission in 119 seconds
...
Next transmission in 0 seconds
Temp: 18.400002 TTN_CayenneLPP: 1 67 00B8
What is the CayenneLPP payload formatter?
In the end we are transmitting just “bytes,” but as the TTN acts as a forwarding gateway to other services, the TTN should be able to separate the received data in data fields. The CayenneLLP payload formatter does this in a very efficient way. In another tutorial, I will explain how to build your own payload encoder and decoder, but for now, you know what this part of the software is for.
Hiccups when using ABP while developing
I experienced some behavior that I didn’t expect. After starting the device, nothing showed up in the TTN console. I think the reason is some settings in the TTN gateway that prevent it from showing new data. I solved it by resetting some data in the console:
- Click on “Settings” while you get the “Live Data” of your device:

- Scroll down to “Network layer” and click on “Expand”:

- Scroll down to “Session and MAC state reset” and click on “Reset…”, then click on “Save changes”:

This should resolve the issue after a restart of your device. But it could take some minutes to get effective.
The Things Network console
As we received “joined” information from the gateway, the TTN console gave us way more data. The “End device overview” informs us about the last active communication with a device:

After clicking on the device, you get a complete overview of the options with this device:

Most interesting is the Live Data tab, as it shows the most recent communication with the device. Here is the typical start of the communication:
16:50:24 Forward uplink data message DevAddr 260B1C8A 016700BB FPort 1 Data rate SF10BW125 SNR -5.5 RSSI -110
...
16:44:24 Forward uplink data message DevAddr 260B1C8A Payload temperature_1 18.4 016700B8 FPort 1 Data rate SF10BW125 SNR -5.5 RSSI -108
16:44:24 Successfully processed data message DevAddr 260B1C8A
16:42:24 Forward uplink data message DevAddr 260B1C8A 00 FPort 1 Data rate SF10BW125 SNR -5.25 RSSI -110
16:42:24 Decode uplink data message failure invalid output
16:42:24 Successfully processed data message DevAddr 260B1C8A
16:42:20 Forward join-accept message DevAddr 260B1C8A JoinEUI 1100010003000001 DevEUI 70B3D57ED0075358
16:42:18 Successfully processed join-request DevAddr 260B54E9 JoinEUI 1100010003000001 DevEUI 70B3D57ED0075358
16:42:18 Accept join-request DevAddr 260B1C8A JoinEUI 1100010003000001 DevEUI 70B3D57ED0075358
16:41:37 Forward join-accept message DevAddr 260B33B1 JoinEUI 1100010003000001 DevEUI 70B3D57ED0075358
16:41:35 Successfully processed join-request DevAddr 260B54E9 JoinEUI 1100010003000001 DevEUI 70B3D57ED0075358
16:41:35 Accept join-request DevAddr 260B33B1 JoinEUI 1100010003000001 DevEUI 70B3D57ED0075358
16:41:04 Join-request to cluster-local Join Server failed DevNonce has already been used
16:40:48 Join-request to cluster-local Join Server failed DevNonce has already been used
16:40:18 Join-request to cluster-local Join Server failed DevNonce has already been used
16:40:00 Join-request to cluster-local Join Server failed DevNonce has already been used
There are three lines of special interest to us:
- 16:40:00 … DevNonce has already been used: this is shown because TTN notices an increasing message number that doesn’t match the last transmission. Simply wait for some minutes, and all is working as expected (this happens after a reboot of the device).
- 16:50:24 Forward uplink data message DevAddr 260B1C8A 016700BB: The last 4 bytes are the data bytes that were transmitted by our device. The temperature value was encoded by the CayenneLPP encoder, so TTN just reads the pure data.
- 16:44:24 Forward uplink data message DevAddr 260B1C8A Payload temperature_1 18.4 016700B8: this time we used the CayenneLPP payload formatter in TTN, and TTN can interpret the 4 data bytes as temperature value.
Select a Payload Formatter
- Click on the “Payload Formatter” tab:

- Select the CayenneLPP Formatter in the Uplink tab and press “Save changes”:

Without a payload formatter, only the pure data is shown in the console:
Press enter or click to view the image in full size

After selecting the CayenneLPP formatter, you can see the real data:
Press enter or click to view the image in full size

Select a Uplink data entry in the console
After clicking on an entry in the console, you get the complete log file of this transmission:
{
"name": "as.up.data.forward",
"time": "2026-01-16T10:06:22.343360515Z",
"identifiers": [
{
"device_ids": {
"device_id": "hel-wsl-v3-dev1",
"application_ids": {
"application_id": "esp32-b1"
},
"dev_eui": "70B3D57ED007537D",
"dev_addr": "260B35D2"
}
}
],
"data": {
"@type": "type.googleapis.com/ttn.lorawan.v3.ApplicationUp",
"end_device_ids": {
"device_id": "hel-wsl-v3-dev1",
"application_ids": {
"application_id": "esp32-b1"
},
"dev_eui": "70B3D57ED007537D",
"dev_addr": "260B35D2"
},
"correlation_ids": [
"gs:uplink:01KF34ACDJ2EAWGNJG7HXB929W"
],
"received_at": "2026-01-16T10:06:22.336607520Z",
"uplink_message": {
"f_port": 1,
"f_cnt": 25,
"frm_payload": "AWcAzg==",
"decoded_payload": {
"temperature_1": 20.6
},
"rx_metadata": [
{
"gateway_ids": {
"gateway_id": "eui-50313953746d4750",
"eui": "50313953746D4750"
},
"time": "2026-01-16T10:06:22.017465Z",
"timestamp": 914878404,
"rssi": -110,
"channel_rssi": -110,
"snr": -4.75,
"location": {
"latitude": 51.2966332954312,
"longitude": 6.8672400935079,
"altitude": 77,
"source": "SOURCE_REGISTRY"
},
"uplink_token": "CiIKIAoUZXVpLTUwMzEzOTUzNzQ2ZDQ3NTASCFAxOVN0bUdQEMTfn7QDGgsInpuoywYQ95mNPiCgq4KY0Icw",
"channel_index": 4,
"received_at": "2026-01-16T10:06:22.107167941Z"
}
],
"settings": {
"data_rate": {
"lora": {
"bandwidth": 125000,
"spreading_factor": 7,
"coding_rate": "4/5"
}
},
"frequency": "867300000",
"timestamp": 914878404,
"time": "2026-01-16T10:06:22.017465Z"
},
"received_at": "2026-01-16T10:06:22.131427599Z",
"consumed_airtime": "0.051456s",
"packet_error_rate": 0.53846157,
"network_ids": {
"net_id": "000013",
"ns_id": "EC656E0000000181",
"tenant_id": "ttn",
"cluster_id": "eu1",
"cluster_address": "eu1.cloud.thethings.network"
}
}
},
"correlation_ids": [
"gs:uplink:01KF34ACDJ2EAWGNJG7HXB929W"
],
"origin": "ip-10-100-6-120.eu-west-1.compute.internal",
"context": {
"tenant-id": "CgN0dG4="
},
"visibility": {
"rights": [
"RIGHT_APPLICATION_TRAFFIC_READ"
]
},
"unique_id": "01KF34ACM7DKJWYD9TS6Q6KBX2"
}
There is tons of technical data, but I want to draw your attention to a specific position.
In the lower part is one line that you need to obey:
”consumed_airtime”: “0.051456s”,
This line means the transmission of 4 bytes of data (plus some header bytes for LoRaWAN) took around 0.05 seconds. This is much lower than a transmission by using the OTAA activation.
Airtime calculator — Duty Cycles
When using LoRa, you have to follow the duty cycles that allow you to use 1% per hour for all of your transmissions. In most cases, no one will claim any “overtime” usage.
Using public gateways for TTN is more restrictive:
The Things Network (TTN) duty cycle combines regulatory limits (like 1% for most European LoRaWAN channels) with a strict Fair Access Policy (FAP) of 30 seconds of uplink airtime and 10 downlink messages per device, per 24 hours, to prevent network congestion, with lower limits (0.1%) on specific channels, requiring careful device configuration for time-on-air to avoid exceeding these rules.
This means in practice: If you are allowed to use 30 seconds per day and a single transmission will cost you 0.051456 seconds, you can send 30 / 0.329728 = 583.02 messages a day. If a day has 24 60 60 = 8,640 seconds, you can send a new message every 8,640 / 90.98 = 14 seconds ! In other words, you are allowed to send a message every 14 seconds !
For that reason, the simple “every byte counts” is important for your work with TTN.
You can find an Airtime Calculator, e.g., here.

Webhooks / Data integration
It is, of course, nice to be able to view your data on a portal, but the actual purpose of LoRa transmission should be to be able to process the sensor data in other systems (e.g., in home automation systems). For this purpose, TTN therefore includes webhooks and other integrators to forward the sensor data to other systems. By clicking on “Webhooks” on the left side of the menu, you get access to several predefined services. Some other data forwarding is available when clicking on “other integrators.”
However, data forwarding is not the focus of this tutorial and will not be explained further here.
Summary
I know this tutorial is longer than usual, but I didn’t want to artificially split it into several parts. Using LoRaWAN is a very exciting way to utilize LoRa communication, provided a public gateway is available in your area. Even with simple and cheap (Heltec) devices, communication over several kilometers is possible.
Source code of the app
You find the complete code of the app in my GitHub repository.
Happy coding !
메타데이터
- post_id
- a8574d7c4b1f
- slug
- startup-guide-for-your-first-lorawan-steps-with-an-esp32-and-a-lora-module-abp-activation-a8574d7c4b1f
- url
- https://medium.com/@androidcrypto/startup-guide-for-your-first-lorawan-steps-with-an-esp32-and-a-lora-module-abp-activation-a8574d7c4b1f
- canonical_url
- https://medium.com/@androidcrypto/startup-guide-for-your-first-lorawan-steps-with-an-esp32-and-a-lora-module-abp-activation-a8574d7c4b1f
- author_url
- https://medium.com/@androidcrypto
- status
- ok
- fetched_at
- 2026-07-13 08:59:31