diff --git a/examples/lorawan/Makefile b/examples/lorawan/Makefile index 513490347fd1..10438ba18100 100644 --- a/examples/lorawan/Makefile +++ b/examples/lorawan/Makefile @@ -7,9 +7,25 @@ BOARD ?= b-l072z-lrwan1 # This has to be the absolute path to the RIOT base directory: RIOTBASE ?= $(CURDIR)/../.. -DEVEUI ?= 0000000000000000 -APPEUI ?= 0000000000000000 -APPKEY ?= 00000000000000000000000000000000 +# Change this to abp to enable Activation By Personnalization mode +ACTIVATION_MODE ?= otaa + +ifeq (otaa,$(ACTIVATION_MODE)) + DEVEUI ?= 0000000000000000 + APPEUI ?= 0000000000000000 + APPKEY ?= 00000000000000000000000000000000 +else ifeq (abp,$(ACTIVATION_MODE)) + DEVADDR ?= 00000000 + NWKSKEY ?= 00000000000000000000000000000000 + APPSKEY ?= 00000000000000000000000000000000 + RX2_FREQ ?= 869525000 + RX2_DR ?= 3 +else + $(error Unsupported activation mode '$(ACTIVATION_MODE)') +endif + +# Send a message every 20s after joining the network +SEND_PERIOD_S ?= 20 # Pass these enviroment variables to docker DOCKER_ENV_VARS += DEVEUI @@ -28,6 +44,18 @@ USEPKG += semtech-loramac USEMODULE += $(DRIVER) USEMODULE += fmt FEATURES_OPTIONAL += periph_rtc +FEATURES_OPTIONAL += periph_eeprom + +CFLAGS += -DSEND_PERIOD_S=$(SEND_PERIOD_S) +ifeq (otaa,$(ACTIVATION_MODE)) + CFLAGS += -DUSE_OTAA +else ifeq (abp,$(ACTIVATION_MODE)) + CFLAGS += -DUSE_ABP +endif + +# Enable deep sleep power mode (e.g. STOP mode on STM32) which +# in general provides RAM retention after wake-up. +CFLAGS += -DPM_BLOCKER_INITIAL=0x00000001 # Comment this out to disable code in RIOT that does safety checking # which is not needed in a production environment but helps in the @@ -52,8 +80,16 @@ endif include $(RIOTBASE)/Makefile.include ifndef CONFIG_KCONFIG_USEMODULE_LORAWAN - # OTAA compile time configuration keys - CFLAGS += -DCONFIG_LORAMAC_APP_KEY_DEFAULT=\"$(APPKEY)\" - CFLAGS += -DCONFIG_LORAMAC_APP_EUI_DEFAULT=\"$(APPEUI)\" - CFLAGS += -DCONFIG_LORAMAC_DEV_EUI_DEFAULT=\"$(DEVEUI)\" + ifeq (otaa,$(ACTIVATION_MODE)) + # OTAA compile time configuration keys + CFLAGS += -DCONFIG_LORAMAC_APP_KEY_DEFAULT=\"$(APPKEY)\" + CFLAGS += -DCONFIG_LORAMAC_APP_EUI_DEFAULT=\"$(APPEUI)\" + CFLAGS += -DCONFIG_LORAMAC_DEV_EUI_DEFAULT=\"$(DEVEUI)\" + else ifeq (abp,$(ACTIVATION_MODE)) + CFLAGS += -DCONFIG_LORAMAC_DEV_ADDR_DEFAULT=\"$(DEVADDR)\" + CFLAGS += -DCONFIG_LORAMAC_APP_SKEY_DEFAULT=\"$(APPSKEY)\" + CFLAGS += -DCONFIG_LORAMAC_NWK_SKEY_DEFAULT=\"$(NWKSKEY)\" + CFLAGS += -DCONFIG_LORAMAC_DEFAULT_RX2_FREQ=$(RX2_FREQ) + CFLAGS += -DCONFIG_LORAMAC_DEFAULT_RX2_DR=$(RX2_DR) + endif endif diff --git a/examples/lorawan/README.md b/examples/lorawan/README.md index 66cf1c343eef..f6e0420cf259 100644 --- a/examples/lorawan/README.md +++ b/examples/lorawan/README.md @@ -1,22 +1,39 @@ -LoRaWAN -======= +## LoRaWAN -Description ------------ +### Description -This application shows a simple use case of LoRaWAN with RIOT. +This application shows a basic LoRaWAN use-case with RIOT. By using the real time clock and low-power capabilities of a board, this -application shows how to program a LoRaWAN Class A device using RIOT. +application also shows how to program a LoRaWAN Class A device using RIOT. -This application is using the Over-The-Air Activation procedure. +By default, the application uses the Over-The-Air Activation (OTAA) procedure. -Usage ------ +### Configuration -Simply build and flash the application for a ST B-L072Z-LRWAN1 board: +To join a LoRaWAN network using OTAA activation, edit the application +`Makefile` and set your device information: - make flash term + ACTIVATION_MODE ?= otaa + DEVEUI ?= 0000000000000000 + APPEUI ?= 0000000000000000 + APPKEY ?= 00000000000000000000000000000000 + +To join a LoRaWAN network using ABP activation, edit the application +`Makefile` and set your device and LoRaWAN application information: + + ACTIVATION_MODE ?= abp + DEVADDR ?= 00000000 + NWKSKEY ?= 00000000000000000000000000000000 + APPSKEY ?= 00000000000000000000000000000000 + RX2_FREQ ?= 869525000 + RX2_DR ?= 3 + +Note that rx2 frequency (`RX2_FREQ`) and datarate (`RX2_DR`) variables must be +set explicitly at compile time when using ABP activation because they are +supposed to be known in advance by the network and the device. In this example, +the values used are compatible with TheThingsNetwork provider network. +They might change depending on the network provider used. Use the `BOARD`, `DRIVER` and `LORA_REGION` make variables to adapt the application to your hardware setup and region of use: @@ -26,17 +43,18 @@ to your hardware setup and region of use: - `LORA_REGION` can be `EU868`, `US915`, etc (see LoRaWAN regional parameters for details). -ST Nucleo-64 can be used with mbed LoRa shields: there's one based on -[the sx1276 radio](https://os.mbed.com/components/SX1276MB1xAS/) and one based -on the [the sx1272 radio](https://os.mbed.com/components/SX1272MB2xAS/). +The `SEND_PERIOD_S` variable can also be adapted to change the time period (in +seconds) between each message sent by the device. -Finally, to join a LoRaWAN network using OTAA activation, use `make menuconfig` -inside the application and edit the configuration or edit the application -`Makefile` : +### Usage - DEVEUI ?= 0000000000000000 - APPEUI ?= 0000000000000000 - APPKEY ?= 00000000000000000000000000000000 +Simply build and flash the application for a ST B-L072Z-LRWAN1 board: + + make flash term + +ST Nucleo-64 can be used as-is with mbed LoRa shields: there's one based on +[the sx1276 radio](https://os.mbed.com/components/SX1276MB1xAS/) and one based +on the [the sx1272 radio](https://os.mbed.com/components/SX1272MB2xAS/). ## Automatic test diff --git a/examples/lorawan/main.c b/examples/lorawan/main.c index 5a8a5e31b2f6..34ca9f929ff2 100644 --- a/examples/lorawan/main.c +++ b/examples/lorawan/main.c @@ -49,8 +49,14 @@ #include "sx126x_params.h" #endif -/* Messages are sent every 20s to respect the duty cycle on each channel */ -#define PERIOD_S (20U) +/* By default, messages are sent every 20s to respect the duty cycle + on each channel */ +#ifndef SEND_PERIOD_S +#define SEND_PERIOD_S (20U) +#endif + +/* Low-power mode level */ +#define PM_LOCK_LEVEL (1) #define SENDER_PRIO (THREAD_PRIORITY_MAIN - 1) static kernel_pid_t sender_pid; @@ -69,9 +75,17 @@ static ztimer_t timer; static const char *message = "This is RIOT!"; +#ifdef USE_OTAA static uint8_t deveui[LORAMAC_DEVEUI_LEN]; static uint8_t appeui[LORAMAC_APPEUI_LEN]; static uint8_t appkey[LORAMAC_APPKEY_LEN]; +#endif + +#ifdef USE_ABP +static uint8_t devaddr[LORAMAC_DEVADDR_LEN]; +static uint8_t nwkskey[LORAMAC_NWKSKEY_LEN]; +static uint8_t appskey[LORAMAC_APPSKEY_LEN]; +#endif static void _alarm_cb(void *arg) { @@ -86,12 +100,12 @@ static void _prepare_next_alarm(void) struct tm time; rtc_get_time(&time); /* set initial alarm */ - time.tm_sec += PERIOD_S; + time.tm_sec += SEND_PERIOD_S; mktime(&time); rtc_set_alarm(&time, _alarm_cb, NULL); #else timer.callback = _alarm_cb; - ztimer_set(ZTIMER_MSEC, &timer, PERIOD_S * MS_PER_SEC); + ztimer_set(ZTIMER_MSEC, &timer, SEND_PERIOD_S * MS_PER_SEC); #endif } @@ -134,11 +148,6 @@ int main(void) puts("LoRaWAN Class A low-power application"); puts("====================================="); - /* Convert identifiers and application key */ - fmt_hex_bytes(deveui, CONFIG_LORAMAC_DEV_EUI_DEFAULT); - fmt_hex_bytes(appeui, CONFIG_LORAMAC_APP_EUI_DEFAULT); - fmt_hex_bytes(appkey, CONFIG_LORAMAC_APP_KEY_DEFAULT); - /* Initialize the radio driver */ #if IS_USED(MODULE_SX127X) sx127x_setup(&sx127x, &sx127x_params[0], 0); @@ -154,6 +163,12 @@ int main(void) /* Initialize the loramac stack */ semtech_loramac_init(&loramac); + +#ifdef USE_OTAA /* OTAA activation mode */ + /* Convert identifiers and keys strings to byte arrays */ + fmt_hex_bytes(deveui, CONFIG_LORAMAC_DEV_EUI_DEFAULT); + fmt_hex_bytes(appeui, CONFIG_LORAMAC_APP_EUI_DEFAULT); + fmt_hex_bytes(appkey, CONFIG_LORAMAC_APP_KEY_DEFAULT); semtech_loramac_set_deveui(&loramac, deveui); semtech_loramac_set_appeui(&loramac, appeui); semtech_loramac_set_appkey(&loramac, appkey); @@ -161,15 +176,49 @@ int main(void) /* Use a fast datarate, e.g. BW125/SF7 in EU868 */ semtech_loramac_set_dr(&loramac, LORAMAC_DR_5); - /* Start the Over-The-Air Activation (OTAA) procedure to retrieve the - * generated device address and to get the network and application session - * keys. - */ - puts("Starting join procedure"); - if (semtech_loramac_join(&loramac, LORAMAC_JOIN_OTAA) != SEMTECH_LORAMAC_JOIN_SUCCEEDED) { - puts("Join procedure failed"); - return 1; + /* Join the network if not already joined */ + if (!semtech_loramac_is_mac_joined(&loramac)) { + /* Start the Over-The-Air Activation (OTAA) procedure to retrieve the + * generated device address and to get the network and application session + * keys. + */ + puts("Starting join procedure"); + if (semtech_loramac_join(&loramac, LORAMAC_JOIN_OTAA) != SEMTECH_LORAMAC_JOIN_SUCCEEDED) { + puts("Join procedure failed"); + return 1; + } + +#ifdef MODULE_PERIPH_EEPROM + /* Save current MAC state to EEPROM */ + semtech_loramac_save_config(&loramac); +#endif } +#endif + +#ifdef USE_ABP /* ABP activation mode */ + /* Convert identifiers and keys strings to byte arrays */ + fmt_hex_bytes(devaddr, CONFIG_LORAMAC_DEV_ADDR_DEFAULT); + fmt_hex_bytes(nwkskey, CONFIG_LORAMAC_NWK_SKEY_DEFAULT); + fmt_hex_bytes(appskey, CONFIG_LORAMAC_APP_SKEY_DEFAULT); + semtech_loramac_set_devaddr(&loramac, devaddr); + semtech_loramac_set_nwkskey(&loramac, nwkskey); + semtech_loramac_set_appskey(&loramac, appskey); + + /* Configure RX2 parameters */ + semtech_loramac_set_rx2_freq(&loramac, CONFIG_LORAMAC_DEFAULT_RX2_FREQ); + semtech_loramac_set_rx2_dr(&loramac, CONFIG_LORAMAC_DEFAULT_RX2_DR); + +#ifdef MODULE_PERIPH_EEPROM + /* Store ABP parameters to EEPROM */ + semtech_loramac_save_config(&loramac); +#endif + + /* Use a fast datarate, e.g. BW125/SF7 in EU868 */ + semtech_loramac_set_dr(&loramac, LORAMAC_DR_5); + + /* ABP join procedure always succeeds */ + semtech_loramac_join(&loramac, LORAMAC_JOIN_ABP); +#endif puts("Join procedure succeeded"); /* start the sender thread */