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

 Date: January 26, 2025
First prototype of my self-developed wirelesssensor PCB. Design of the second version is already finished.
First prototype of my self-developed sensor PCB. Design of the second version is already finished.

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.

Finally: the join message from TTN
Finally: the join message from TTN

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.

DIO1/2/3 configuration of the HT-RA62
DIO1/2/3 configuration of the HT-RA62
#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.

Previous
⏪ Zephyr: Missing USART4 definition in .dtsi files of the STM32U0

Next
Zephyr: State of LoRa(WAN) ⏩