Zephyr: Integrating a ST STM32U083 and a Semtech SX1262 into a LoRaWAN network (The Thing Network)

What a weekend. After hacking for two long days and nights, I finally was able to see the Join-Request
of my self-developed PCB in the The Things Network (TTN) backend.

What was the most tricky part? To be honest: Bad network coverage in my city resulted in hunting ghosts.
*** Booting Zephyr OS build v4.0.0-3992-gf0d0264c0572 ***
[00:00:00.050,000] <dbg> lorawan: lorawan_set_region: Selected region 5
[00:00:00.109,000] <dbg> lorawan: lorawan_start: LoRaMAC Initialized
[00:00:00.109,000] <inf> lorawan_class_a: Joining network over OTAA
[00:00:00.135,000] <dbg> lorawan: lorawan_join: Network join request sent!
[00:00:06.975,000] <dbg> lorawan: mlme_confirm_handler: Received MlmeConfirm (for MlmeRequest 1)
[00:00:06.975,000] <err> lorawan: MlmeConfirm failed : Rx 2 timeout
[00:00:06.978,000] <err> lorawan_class_a: lorawan_join_network failed: -116
I assumed that lorawan: MlmeConfirm failed : Rx 2
timeout was caused by wrong EUI/APP/JOIN keys or another bug - but it was a bad signal level from the next TTN base station in my city. Opening a window and pointing out a directional antenna solved the problem.
The MSB/LSB story of the keys took some thinkering too, but at least with the latest Zephyr master everything is MSB.
So - how to integrate LoRaWAN into Zephyr? It’s easy.
Step 1: Add the LoRaMAC libs to your west.yml
manifest:
defaults:
remote: zephyrproject-rtos
self:
west-commands: scripts/west-commands.yml
remotes:
- name: zephyrproject-rtos
url-base: https://github.com/zephyrproject-rtos
projects:
- name: zephyr
remote: zephyrproject-rtos
revision: main
import:
# By using name-allowlist we can clone only the modules that are
# strictly needed by the application.
name-allowlist:
- cmsis
- hal_stm32
- loramac-node
Step 2: Add the CONFIG_
parameters to the prj.conf
CONFIG_LORA=y
CONFIG_LORA_SX126X=y
# CONFIG_LORA_LOG_LEVEL_DBG=y
CONFIG_LORAWAN=y
# CONFIG_LORAWAN_LOG_LEVEL_DBG=y
CONFIG_LORAMAC_REGION_EU868=y
CONFIG_LORAWAN_SYSTEM_MAX_RX_ERROR=200
CONFIG_MAIN_STACK_SIZE=2048
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
Step 3: Add the LoRa device to the .dts
of your board.
In my case, it’s a HT-RA62. The DIO1/DIO2/DIO3 configuration can be found in the schematic of your component.

#include <zephyr/dt-bindings/lora/sx126x.h>
(...)
aliases {
lora0 = &lora;
};
&spi1 {
pinctrl-0 = <&spi1_nss_pa4 &spi1_sck_pa5 &spi1_miso_pa6 &spi1_mosi_pa7>;
pinctrl-names = "default";
status = "okay";
lora: lora@0 {
compatible = "semtech,sx1262";
reg = <0>;
reset-gpios = <&gpiob 1 GPIO_ACTIVE_LOW>;
busy-gpios = <&gpiob 0 GPIO_ACTIVE_HIGH>;
dio1-gpios = <&gpiob 2 GPIO_ACTIVE_HIGH>;
dio2-tx-enable;
dio3-tcxo-voltage = <SX126X_DIO3_TCXO_3V3>;
tcxo-power-startup-delay-ms = <3>;
spi-max-frequency = <DT_FREQ_M(1)>;
status = "okay";
};
};
Step 4: Integrate the LoRaWAN application
As a first application, the example project LoRaWAN Class A device from Zephyr should work without further modification. Just enter the keys (once again: all of them are MSB!) and start it.
For the first test run activate both debugging options.
CONFIG_LORA_LOG_LEVEL_DBG=y
CONFIG_LORAWAN_LOG_LEVEL_DBG=y
A successful transmission looks like this.
*** Booting Zephyr OS build v4.0.0-3992-gf0d0264c0572 ***
[00:00:00.050,000] <dbg> lorawan: lorawan_set_region: Selected region 5
[00:00:00.109,000] <dbg> lorawan: lorawan_start: LoRaMAC Initialized
[00:00:00.109,000] <inf> lorawan_class_a: Joining network over OTAA
[00:00:00.135,000] <dbg> lorawan: lorawan_join: Network join request sent!
[00:00:08.450,000] <dbg> lorawan: mlme_confirm_handler: Received MlmeConfirm (for MlmeRequest 1)
[00:00:08.450,000] <inf> lorawan: Joined network! DevAddr: xxx
[00:00:08.453,000] <inf> lorawan_class_a: New Datarate: DR_0, Max Payload 51
[00:00:08.453,000] <inf> lorawan: Datarate changed: DR_0
[00:00:08.453,000] <inf> lorawan_class_a: Sending data...
[00:00:16.128,000] <dbg> lorawan: mcps_indication_handler: Received McpsIndication 0
[00:00:16.128,000] <inf> lorawan_class_a: Port 0, Pending 0, RSSI -107dB, SNR -12dBm, Time 0
[00:00:16.128,000] <dbg> lorawan: mcps_confirm_handler: Received McpsConfirm (for McpsRequest 1)
[00:00:16.128,000] <dbg> lorawan: mcps_confirm_handler: McpsRequest success!
[00:00:16.131,000] <inf> lorawan_class_a: Data sent!
Activating ADR (Adaptive Data Rate) significantly increases the chances of a successful transmission!
ret = lorawan_start();
if (ret < 0) {
LOG_ERR("lorawan_start failed: %d", ret);
return 0;
}
lorawan_enable_adr(true);
The full application can be found on SolarLoRaBox_Firmware GitHub repository. No worries: the keys in that application are dummies and will not work :-)
Step 5: Configure the Things Network
All keys can be generated end exported (nicely C-style-array formatted!) from the TTN backend.
For TTN following network configuration is required
- Frequency Plan: Europe 863-870MHz (SF9 for RX2 - recommended)
- LoRaWAN version: LoRaWAN Specification 1.0.3
- Regional Parameters version: RP001 Regional Parameters 1.0.3 revision A
Following parameter dramatically weakens the encryption but makes things during the development phase a bit easier:
- Settings -> Join Settings -> Reset join nonces
Reason is that the Nonce join_cfg.otaa.dev_nonce
must be unique with every join.
Lessons learned
- Verify the TX of your LoRa modem using an SDR (RTLSDR, etc.).
- Take care that you are 100% able not only to reach a LoRaWAN base station but also to receive it. If your data rate drops to DR_0 with ADR, that’s a bad sign for reliable testing.
- The purpose of DIO1/2/3 of the Semtech modems can be found in the schematics. DIO1 is usually the IRQ line; DIO2 and DIO3 are mostly used for TXEN and switching on the TCXO.
- It’s very helpful to see the debugging output of the LoRaWAN base station. So starting with TTN is a good start - even if you think of not using it for your later applications.