diff --git a/examples/lorawan/Makefile b/examples/lorawan/Makefile new file mode 100644 index 0000000000000..35851ede74d20 --- /dev/null +++ b/examples/lorawan/Makefile @@ -0,0 +1,40 @@ +# name of your application +APPLICATION = lorawan + +# Use the ST B-L072Z-LRWAN1 board by default: +BOARD ?= b-l072z-lrwan1 + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../.. + +BOARD_INSUFFICIENT_MEMORY := nucleo32-f031 nucleo32-f042 nucleo32-l031 + +DEVEUI ?= 0000000000000000 +APPEUI ?= 0000000000000000 +APPKEY ?= 00000000000000000000000000000000 + +# Default radio driver is Semtech SX1276 (used by the B-L072Z-LRWAN1 board) +DRIVER ?= sx1276 + +# Default region is Europe and default band is 868MHz +REGION ?= EU868 + +# Include the Semtech-loramac package +USEPKG += semtech-loramac + +USEMODULE += $(DRIVER) +USEMODULE += fmt +FEATURES_REQUIRED += periph_rtc + +CFLAGS += -DREGION_$(REGION) +CFLAGS += -DDEVEUI=\"$(DEVEUI)\" -DAPPEUI=\"$(APPEUI)\" -DAPPKEY=\"$(APPKEY)\" + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +DEVELHELP ?= 1 + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +include $(RIOTBASE)/Makefile.include diff --git a/examples/lorawan/README.md b/examples/lorawan/README.md new file mode 100644 index 0000000000000..934a79ce6e3f2 --- /dev/null +++ b/examples/lorawan/README.md @@ -0,0 +1,38 @@ +LoRaWAN +======= + +Description +----------- + +This application shows a simple use case of LoRaWAN 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 devices using RIOT. + +This application is using the Over-The-Air Activation procedure. + +Usage +----- + +Simply build and flash the application for a ST B-L072Z-LRWAN1 board: + + make flash term + +Use the `BOARD`, `DRIVER` and `REGION` make variables to adapt the application +to your hardware setup and region of use: + +- `BOARD` can be one of the nucleo-64 boards +- `DRIVER` can be either `sx1276` or `sx1272` +- `REGION` can be `EU868`, `US915`, etc (see LoRaWAN regional parameters for + details). + +ST Nucleo-64 can be used with an mbed LoRa shield: 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/). + +Finally, to join a LoRaWAN network using OTAA activation, edit the application +`Makefile` and set your device information: + + DEVEUI ?= 0000000000000000 + APPEUI ?= 0000000000000000 + APPKEY ?= 00000000000000000000000000000000 diff --git a/examples/lorawan/main.c b/examples/lorawan/main.c new file mode 100644 index 0000000000000..cb2f6a574a289 --- /dev/null +++ b/examples/lorawan/main.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2018 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup examples + * @{ + * + * @file + * @brief Example demonstrating the use of LoRaWAN with RIOT + * + * @author Alexandre Abadie + * + * @} + */ + +#include +#include +#include +#include + +#include "msg.h" +#include "thread.h" +#include "fmt.h" + +#ifdef MODULE_PM_LAYERED +#include "pm_layered.h" +#endif +#include "periph/rtc.h" + +#include "net/loramac.h" +#include "semtech_loramac.h" + +/* Use the STOP mode to ensure memory retention between each send */ +#define PM_MODE (1) + +/* Messages are sent every 20s to respect the duty cycle on each channel */ +#define PERIOD (20U) + +#define SENDER_PRIO (THREAD_PRIORITY_MAIN - 1) +static kernel_pid_t sender_pid; +static char sender_stack[THREAD_STACKSIZE_MAIN / 2]; + +semtech_loramac_t loramac; + +static const char *message = "This is RIOT!"; + +static uint8_t deveui[LORAMAC_DEVEUI_LEN]; +static uint8_t appeui[LORAMAC_APPEUI_LEN]; +static uint8_t appkey[LORAMAC_APPKEY_LEN]; + +static void rtc_cb(void *arg) +{ + (void) arg; + msg_t msg; + msg_send(&msg, sender_pid); +} + +static void _prepare_next_alarm(void) +{ + struct tm time; + rtc_get_time(&time); + /* set initial alarm */ + time.tm_sec += PERIOD; + while (time.tm_sec > 60) { + time.tm_min++; + time.tm_sec -= 60; + } + while (time.tm_min > 60) { + time.tm_hour++; + time.tm_min -= 60; + } + while (time.tm_hour > 24) { + time.tm_mday++; + time.tm_hour -= 24; + } + rtc_set_alarm(&time, rtc_cb, NULL); +} + +static void _send_message(void) +{ + printf("Sending: %s\n", message); + /* The send call blocks until done */ + semtech_loramac_send(&loramac, (uint8_t *)message, strlen(message)); + /* Wait until the send cycle has completed */ + semtech_loramac_recv(&loramac); +} + +static void *sender(void *arg) +{ + (void)arg; + + msg_t msg; + msg_t msg_queue[8]; + msg_init_queue(msg_queue, 8); + + while (1) { + msg_receive(&msg); + + /* Trigger the message send */ + _send_message(); + + /* Schedule the next wake-up alarm */ + _prepare_next_alarm(); + +#ifdef MODULE_PM_LAYERED + /* enable low-power mode */ + pm_set(PM_MODE); +#endif + } + + /* this should never be reached */ + return NULL; +} + +int main(void) +{ + puts("LoRaWAN Class A low-power application"); + puts("====================================="); + + /* Convert identifiers and application key */ + fmt_hex_bytes(deveui, DEVEUI); + fmt_hex_bytes(appeui, APPEUI); + fmt_hex_bytes(appkey, APPKEY); + + /* Initialize the loramac stack */ + semtech_loramac_init(&loramac); + semtech_loramac_set_deveui(&loramac, deveui); + semtech_loramac_set_appeui(&loramac, appeui); + semtech_loramac_set_appkey(&loramac, appkey); + + /* 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; + } + puts("Join procedure succeeded"); + + /* start the sender thread */ + sender_pid = thread_create(sender_stack, sizeof(sender_stack), + SENDER_PRIO, 0, sender, NULL, "sender"); + + /* trigger the first send */ + msg_t msg; + msg_send(&msg, sender_pid); + return 0; +}