From 7f0e07797248654729261519a7f2a813a21538c3 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Wed, 4 Mar 2020 16:46:48 +0530 Subject: [PATCH 01/26] drivers: lora: Prepare the SX1276 driver for LoRaWAN For using this driver with LoRaWAN, following changes were done: - Removed the use of counter and adapted system timer APIs for tick counting. This is required because most of the counter drivers run at very low tick rate like 1Hz and will not work with loramac-node library. - Removed the use of counter and adapted timer APIs for alarm functionality. - Added few more function definitions and callbacks required by the stack Signed-off-by: Manivannan Sadhasivam --- drivers/lora/CMakeLists.txt | 9 ++- drivers/lora/Kconfig.sx1276 | 1 - drivers/lora/sx1276.c | 124 ++++++++++++++++++++++++------------ 3 files changed, 93 insertions(+), 41 deletions(-) diff --git a/drivers/lora/CMakeLists.txt b/drivers/lora/CMakeLists.txt index 4686147fa24975..3b39c0555153e5 100644 --- a/drivers/lora/CMakeLists.txt +++ b/drivers/lora/CMakeLists.txt @@ -1,5 +1,12 @@ # SPDX-License-Identifier: Apache-2.0 -zephyr_library_named(loramac-node) +# LoRa drivers depend on the include directories exposed by the loramac-node +# library. Hence, if it exists then the source files are added to that otherwise +# a library with same name is created. +if(TARGET loramac-node) + set(ZEPHYR_CURRENT_LIBRARY loramac-node) +else() + zephyr_library_named(loramac-node) +endif() zephyr_library_sources_ifdef(CONFIG_LORA_SX1276 sx1276.c) diff --git a/drivers/lora/Kconfig.sx1276 b/drivers/lora/Kconfig.sx1276 index 4a1447a595d9ab..a8853f12054cb1 100644 --- a/drivers/lora/Kconfig.sx1276 +++ b/drivers/lora/Kconfig.sx1276 @@ -10,7 +10,6 @@ menuconfig LORA_SX1276 select HAS_SEMTECH_LORAMAC select HAS_SEMTECH_SX1276 depends on SPI - depends on COUNTER help Enable LoRa driver for Semtech SX1276. diff --git a/drivers/lora/sx1276.c b/drivers/lora/sx1276.c index 36efb8393eb288..b6a5357e87feca 100644 --- a/drivers/lora/sx1276.c +++ b/drivers/lora/sx1276.c @@ -4,13 +4,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include #include #include #include #include +/* LoRaMac-node specific includes */ #include +#include #define LOG_LEVEL CONFIG_LORA_LOG_LEVEL #include @@ -24,6 +25,9 @@ LOG_MODULE_REGISTER(sx1276); #define SX1276_REG_PA_DAC 0x4d #define SX1276_REG_VERSION 0x42 +#define BOARD_TCXO_WAKEUP_TIME 5 + +static u32_t saved_time; extern DioIrqHandler *DioIrq[]; struct sx1276_dio { @@ -41,31 +45,28 @@ static struct sx1276_dio sx1276_dios[] = ; #define SX1276_MAX_DIO ARRAY_SIZE(sx1276_dios) -struct sx1276_data { - struct device *counter; +static struct sx1276_data { struct device *reset; struct device *spi; struct spi_config spi_cfg; struct device *dio_dev[SX1276_MAX_DIO]; struct k_sem data_sem; + struct k_timer timer; RadioEvents_t sx1276_event; + /* TODO: Use Non-volatile memory for backup */ + volatile u32_t backup_reg[2]; u8_t *rx_buf; u8_t rx_len; s8_t snr; s16_t rssi; } dev_data; -bool SX1276CheckRfFrequency(uint32_t frequency) +bool SX1276CheckRfFrequency(u32_t frequency) { /* TODO */ return true; } -void RtcStopAlarm(void) -{ - counter_stop(dev_data.counter); -} - void SX1276SetAntSwLowPower(bool status) { /* TODO */ @@ -76,6 +77,11 @@ void SX1276SetBoardTcxo(u8_t state) /* TODO */ } +u32_t SX1276GetBoardTcxoWakeupTime(void) +{ + return BOARD_TCXO_WAKEUP_TIME; +} + void SX1276SetAntSw(u8_t opMode) { /* TODO */ @@ -93,61 +99,100 @@ void SX1276Reset(void) k_sleep(6); } -void BoardCriticalSectionBegin(uint32_t *mask) +void BoardCriticalSectionBegin(u32_t *mask) { *mask = irq_lock(); } -void BoardCriticalSectionEnd(uint32_t *mask) +void BoardCriticalSectionEnd(u32_t *mask) { irq_unlock(*mask); } -uint32_t RtcGetTimerElapsedTime(void) +u32_t RtcGetTimerValue(void) { - u32_t ticks; - int err; - - err = counter_get_value(dev_data.counter, &ticks); - if (err) { - LOG_ERR("Failed to read counter value (err %d)", err); - return 0; - } + return k_ms_to_ticks_ceil32(k_uptime_get_32()); +} - return ticks; +u32_t RtcGetTimerElapsedTime(void) +{ + return (k_ms_to_ticks_ceil32(k_uptime_get_32()) - saved_time); } u32_t RtcGetMinimumTimeout(void) { - /* TODO: Get this value from counter driver */ - return 3; + return 1; } -void RtcSetAlarm(uint32_t timeout) +void RtcStopAlarm(void) { - struct counter_alarm_cfg alarm_cfg; + k_timer_stop(&dev_data.timer); +} - alarm_cfg.flags = 0; - alarm_cfg.ticks = timeout; +#ifdef CONFIG_LORAWAN +static void timer_callback(struct k_timer *_timer) +{ + ARG_UNUSED(_timer); - counter_set_channel_alarm(dev_data.counter, 0, &alarm_cfg); + TimerIrqHandler(); } +#endif -uint32_t RtcSetTimerContext(void) +void RtcSetAlarm(u32_t timeout) { - return 0; + k_timer_start(&dev_data.timer, k_ticks_to_ms_floor32(timeout), + K_NO_WAIT); } -uint32_t RtcMs2Tick(uint32_t milliseconds) +u32_t RtcSetTimerContext(void) { - return counter_us_to_ticks(dev_data.counter, (milliseconds / 1000)); + saved_time = k_ms_to_ticks_ceil32(k_uptime_get_32()); + + return saved_time; } -void DelayMsMcu(uint32_t ms) +u32_t RtcGetTimerContext(void) +{ + return saved_time; +} + +u32_t RtcMs2Tick(uint32_t milliseconds) +{ + return k_ms_to_ticks_ceil32(milliseconds); +} + +u32_t RtcTick2Ms(uint32_t tick) +{ + return k_ticks_to_ms_floor32(tick); +} + +void DelayMsMcu(u32_t ms) { k_sleep(ms); } +u32_t RtcGetCalendarTime(uint16_t *milliseconds) +{ + u32_t now = k_uptime_get_32(); + + *milliseconds = now; + + /* Return in seconds */ + return now / MSEC_PER_SEC; +} + +void RtcBkupWrite(u32_t data0, uint32_t data1) +{ + dev_data.backup_reg[0] = data0; + dev_data.backup_reg[1] = data1; +} + +void RtcBkupRead(u32_t *data0, uint32_t *data1) +{ + *data0 = dev_data.backup_reg[0]; + *data1 = dev_data.backup_reg[1]; +} + static void sx1276_irq_callback(struct device *dev, struct gpio_callback *cb, u32_t pins) { @@ -426,6 +471,7 @@ const struct Radio_s Radio = { .Random = SX1276Random, .SetRxConfig = SX1276SetRxConfig, .SetTxConfig = SX1276SetTxConfig, + .TimeOnAir = SX1276GetTimeOnAir, .Send = SX1276Send, .Sleep = SX1276SetSleep, .Standby = SX1276SetStby, @@ -435,6 +481,8 @@ const struct Radio_s Radio = { .WriteBuffer = SX1276WriteBuffer, .ReadBuffer = SX1276ReadBuffer, .SetMaxPayloadLength = SX1276SetMaxPayloadLength, + .SetPublicNetwork = SX1276SetPublicNetwork, + .GetWakeupTime = SX1276GetWakeupTime, .IrqProcess = NULL, .RxBoosted = NULL, .SetRxDutyCycle = NULL, @@ -491,14 +539,12 @@ static int sx1276_lora_init(struct device *dev) return -EIO; } - dev_data.counter = device_get_binding(DT_RTC_0_NAME); - if (!dev_data.counter) { - LOG_ERR("Cannot get pointer to %s device", DT_RTC_0_NAME); - return -EIO; - } - k_sem_init(&dev_data.data_sem, 0, UINT_MAX); +#ifdef CONFIG_LORAWAN + k_timer_init(&dev_data.timer, timer_callback, NULL); +#endif + dev_data.sx1276_event.TxDone = sx1276_tx_done; dev_data.sx1276_event.RxDone = sx1276_rx_done; Radio.Init(&dev_data.sx1276_event); From 1ca70590a38d66d2bcd5bc9e8abae6258c5c52a3 Mon Sep 17 00:00:00 2001 From: Kuba Sanak Date: Wed, 4 Mar 2020 16:53:15 +0530 Subject: [PATCH 02/26] drivers: lora: Add CheckRfFrequency callback to SX1276 driver CheckRfFrequency was not assigned correctly to the Radio. This lead to the system hang when the loramac library tries to call a relevant Radio method when processing a join-accept message Signed-off-by: Kuba Sanak Signed-off-by: Manivannan Sadhasivam --- drivers/lora/sx1276.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/lora/sx1276.c b/drivers/lora/sx1276.c index b6a5357e87feca..c234ead533f2ef 100644 --- a/drivers/lora/sx1276.c +++ b/drivers/lora/sx1276.c @@ -471,6 +471,7 @@ const struct Radio_s Radio = { .Random = SX1276Random, .SetRxConfig = SX1276SetRxConfig, .SetTxConfig = SX1276SetTxConfig, + .CheckRfFrequency = SX1276CheckRfFrequency, .TimeOnAir = SX1276GetTimeOnAir, .Send = SX1276Send, .Sleep = SX1276SetSleep, From 08e023531279e42db15e3569a7c8cdaf5f398ff0 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Wed, 4 Mar 2020 19:52:59 +0530 Subject: [PATCH 03/26] include: Add LoRaWAN API Add LoRaWAN API for Zephyr. Only config, join and send methods are implemented. Signed-off-by: Manivannan Sadhasivam --- include/net/lorawan.h | 113 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 include/net/lorawan.h diff --git a/include/net/lorawan.h b/include/net/lorawan.h new file mode 100644 index 00000000000000..40588b1c269f00 --- /dev/null +++ b/include/net/lorawan.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2019 Manivannan Sadhasivam + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_NET_LORAWAN_H_ +#define ZEPHYR_INCLUDE_NET_LORAWAN_H_ + +/** + * @file + * @brief Public LoRaWAN APIs + */ + +#include +#include + +/** + * @brief LoRaWAN class types. + */ +enum lorawan_class { + LORAWAN_CLASS_A = 0x00, + LORAWAN_CLASS_B = 0x01, + LORAWAN_CLASS_C = 0x02, +}; + +/** + * @brief LoRaWAN activation types. + */ +enum lorawan_act_type { + LORAWAN_ACT_OTAA = 0, + LORAWAN_ACT_ABP, +}; + +/** + * @brief LoRaWAN datarate types. + */ +enum lorawan_datarate { + LORAWAN_DR_0 = 0, + LORAWAN_DR_1, + LORAWAN_DR_2, + LORAWAN_DR_3, + LORAWAN_DR_4, + LORAWAN_DR_5, + LORAWAN_DR_6, + LORAWAN_DR_7, + LORAWAN_DR_8, + LORAWAN_DR_9, + LORAWAN_DR_10, + LORAWAN_DR_11, + LORAWAN_DR_12, + LORAWAN_DR_13, + LORAWAN_DR_14, + LORAWAN_DR_15, +}; + +struct lorawan_mib_config { + enum lorawan_class lw_class; + u8_t *dev_eui; + u8_t *join_eui; + u8_t *app_eui; + u8_t *app_key; + u8_t *nwk_key; + bool pub_nw; + bool adr_enable; +}; + +/** + * @brief Configure the LoRaWAN stack + * + * Configure the LoRaWAN stack using MIB (Mac Information Base) parameters. + * + * @param mib_config MIB configuration + * + * @return 0 if successful, negative errno code if failure + */ +int lorawan_config(struct lorawan_mib_config *mib_config); + +/** + * @brief Join the LoRaWAN network + * + * Join the LoRaWAN network using either OTAA or AWB. + * + * @param datarate Datarate to be used for network join + * @param mode Activation mode + * + * @return 0 if successful, negative errno code if failure + */ +int lorawan_join_network(enum lorawan_datarate datarate, + enum lorawan_act_type mode); + +/** + * @brief Send data to the LoRaWAN network + * + * Send data to the connected LoRaWAN network. + * + * @param port Port to be used for sending data. Must be set if the + * payload is not empty. + * @param datarate Datarate to be used for sending data + * @param data Data buffer to be sent + * @param len Length of the buffer to be sent. Maximum length of this + * buffer is 255 bytes but the actual payload size varies with + * region and datarate. + * @param confirm Use confirmed messages + * @param tries Number of tries needed for sending the data in case the Ack + * from server is not received + * + * @return 0 if successful, negative errno code if failure + */ +int lorawan_send(u8_t port, enum lorawan_datarate datarate, + u8_t *data, u8_t len, bool confirm, u8_t tries); + +#endif /* ZEPHYR_INCLUDE_NET_LORAWAN_H_ */ From 010f460345c37c75562d54de1d3db0cb7a407f53 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Wed, 4 Mar 2020 19:54:09 +0530 Subject: [PATCH 04/26] lorawan: Add initial support for LoRaWAN Add initial support for LoRaWAN based on Semtech's loramac-node library. Current implementation only supports OTAA config and sending data to LoRaWAN server like ThingsNetwork. While at it, this commit also moves the "loramac-node" library definition from drivers/lora to subsys/lorawan. This is required because, subsys/lorawan gets processed before drivers/lora and that creates issue while building. Signed-off-by: Manivannan Sadhasivam --- modules/Kconfig.loramac-node | 6 + subsys/CMakeLists.txt | 1 + subsys/Kconfig | 2 + subsys/lorawan/CMakeLists.txt | 34 +++ subsys/lorawan/Kconfig | 58 +++++ subsys/lorawan/lorawan.c | 422 ++++++++++++++++++++++++++++++++++ 6 files changed, 523 insertions(+) create mode 100644 subsys/lorawan/CMakeLists.txt create mode 100644 subsys/lorawan/Kconfig create mode 100644 subsys/lorawan/lorawan.c diff --git a/modules/Kconfig.loramac-node b/modules/Kconfig.loramac-node index fc775d30b25ec7..0b004df7959193 100644 --- a/modules/Kconfig.loramac-node +++ b/modules/Kconfig.loramac-node @@ -9,6 +9,12 @@ config HAS_SEMTECH_LORAMAC help This option enables the use of Semtech's LoRaMac stack +config HAS_SEMTECH_SOFT_SE + bool "Semtech Secure Element software implementation" + help + This option enables the use of Semtech's Secure Element + software implementation + config HAS_SEMTECH_RADIO_DRIVERS bool "Semtech LoRa Radio Drivers" help diff --git a/subsys/CMakeLists.txt b/subsys/CMakeLists.txt index a203f5bd34db58..a92b3b1fbd54d3 100644 --- a/subsys/CMakeLists.txt +++ b/subsys/CMakeLists.txt @@ -2,6 +2,7 @@ add_subdirectory(debug) add_subdirectory(logging) +add_subdirectory_ifdef(CONFIG_LORAWAN lorawan) add_subdirectory_ifdef(CONFIG_BT bluetooth) add_subdirectory_ifdef(CONFIG_CONSOLE_SUBSYS console) add_subdirectory_ifdef(CONFIG_SHELL shell) diff --git a/subsys/Kconfig b/subsys/Kconfig index 615ae9762b6fec..6531ff9f307fe2 100644 --- a/subsys/Kconfig +++ b/subsys/Kconfig @@ -17,6 +17,8 @@ source "subsys/fs/Kconfig" source "subsys/logging/Kconfig" +source "subsys/lorawan/Kconfig" + source "subsys/mgmt/Kconfig" source "subsys/net/Kconfig" diff --git a/subsys/lorawan/CMakeLists.txt b/subsys/lorawan/CMakeLists.txt new file mode 100644 index 00000000000000..141eb972b73106 --- /dev/null +++ b/subsys/lorawan/CMakeLists.txt @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: Apache-2.0 + +# lorawan.c depend on the include directories exposed by the loramac-node +# library. Hence, if it exists then the source files are added to that otherwise +# a library with same name is created. +if(TARGET loramac-node) + set(ZEPHYR_CURRENT_LIBRARY loramac-node) +else() + zephyr_library_named(loramac-node) +endif() + +if(CONFIG_LORAMAC_REGION_AS923) + zephyr_compile_definitions(-DREGION_AS923) +elseif(CONFIG_LORAMAC_REGION_AU915) + zephyr_compile_definitions(-DREGION_AU915) +elseif(CONFIG_LORAMAC_REGION_CN470) + zephyr_compile_definitions(-DREGION_CN470) +elseif(CONFIG_LORAMAC_REGION_CN779) + zephyr_compile_definitions(-DREGION_CN779) +elseif(CONFIG_LORAMAC_REGION_EU433) + zephyr_compile_definitions(-DREGION_EU433) +elseif(CONFIG_LORAMAC_REGION_EU868) + zephyr_compile_definitions(-DREGION_EU868) +elseif(CONFIG_LORAMAC_REGION_KR920) + zephyr_compile_definitions(-DREGION_KR920) +elseif(CONFIG_LORAMAC_REGION_IN865) + zephyr_compile_definitions(-DREGION_IN865) +elseif(CONFIG_LORAMAC_REGION_US915) + zephyr_compile_definitions(-DREGION_US915) +elseif(CONFIG_LORAMAC_REGION_RU864) + zephyr_compile_definitions(-DREGION_RU864) +endif() + +zephyr_library_sources_ifdef(CONFIG_LORAWAN lorawan.c) diff --git a/subsys/lorawan/Kconfig b/subsys/lorawan/Kconfig new file mode 100644 index 00000000000000..b9bf82eea2bde3 --- /dev/null +++ b/subsys/lorawan/Kconfig @@ -0,0 +1,58 @@ +# LoRaWAN configuration options + +# Copyright (c) 2019 Manivannan Sadhasivam +# SPDX-License-Identifier: Apache-2.0 + +menuconfig LORAWAN + bool "LoRaWAN support [EXPERIMENTAL]" + depends on LORA + select HAS_SEMTECH_LORAMAC + select HAS_SEMTECH_SOFT_SE + help + This option enables LoRaWAN support. + +if LORAWAN + +module = LORAWAN +module-str = lorawan +source "subsys/logging/Kconfig.template.log_config" + +choice + prompt "LoRaWAN Region Selection" + default LORAMAC_REGION_IN865 + help + Select the LoRaWAN region. + +config LORAMAC_REGION_AS923 + bool "Asia 923MHz Frequency band" + +config LORAMAC_REGION_AU915 + bool "Australia 915MHz Frequency band" + +config LORAMAC_REGION_CN470 + bool "China 470MHz Frequency band" + +config LORAMAC_REGION_CN779 + bool "China 779MHz Frequency band" + +config LORAMAC_REGION_EU433 + bool "Europe 433MHz Frequency band" + +config LORAMAC_REGION_EU868 + bool "Europe 868MHz Frequency band" + +config LORAMAC_REGION_KR920 + bool "South Korea 920MHz Frequency band" + +config LORAMAC_REGION_IN865 + bool "India 865MHz Frequency band" + +config LORAMAC_REGION_US915 + bool "North America 915MHz Frequency band" + +config LORAMAC_REGION_RU864 + bool "Russia 864MHz Frequency band" + +endchoice + +endif # LORAWAN diff --git a/subsys/lorawan/lorawan.c b/subsys/lorawan/lorawan.c new file mode 100644 index 00000000000000..7b1be3b9b16f77 --- /dev/null +++ b/subsys/lorawan/lorawan.c @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2019 Manivannan Sadhasivam + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include + +#ifdef CONFIG_LORAMAC_REGION_AS923 + #define LORAWAN_REGION LORAMAC_REGION_AS923 +#elif CONFIG_LORAMAC_REGION_AU915 + #define LORAWAN_REGION LORAMAC_REGION_AU915 +#elif CONFIG_LORAMAC_REGION_CN470 + #define LORAWAN_REGION LORAMAC_REGION_CN470 +#elif CONFIG_LORAMAC_REGION_CN779 + #define LORAWAN_REGION LORAMAC_REGION_CN779 +#elif CONFIG_LORAMAC_REGION_EU433 + #define LORAWAN_REGION LORAMAC_REGION_EU433 +#elif CONFIG_LORAMAC_REGION_EU868 + #define LORAWAN_REGION LORAMAC_REGION_EU868 +#elif CONFIG_LORAMAC_REGION_KR920 + #define LORAWAN_REGION LORAMAC_REGION_KR920 +#elif CONFIG_LORAMAC_REGION_IN865 + #define LORAWAN_REGION LORAMAC_REGION_IN865 +#elif CONFIG_LORAMAC_REGION_US915 + #define LORAWAN_REGION LORAMAC_REGION_US915 +#elif CONFIG_LORAMAC_REGION_RU864 + #define LORAWAN_REGION LORAMAC_REGION_RU864 +#elif + #error "Atleast one LoRaWAN region should be selected" +#endif + +#define LOG_LEVEL CONFIG_LORAWAN_LOG_LEVEL +#include +LOG_MODULE_REGISTER(lorawan); + +K_SEM_DEFINE(lorawan_config_sem, 0, 1); +K_SEM_DEFINE(lorawan_tx_sem, 0, 1); + +K_MUTEX_DEFINE(lorawan_join_mutex); +K_MUTEX_DEFINE(lorawan_send_mutex); + +static volatile bool join_status = false; +static volatile bool send_status = false; + +const char *status2str(int status) +{ + switch (status) { + case LORAMAC_STATUS_OK: + return "OK"; + case LORAMAC_STATUS_BUSY: + return "Busy"; + case LORAMAC_STATUS_SERVICE_UNKNOWN: + return "Service unknown"; + case LORAMAC_STATUS_PARAMETER_INVALID: + return "Parameter invalid"; + case LORAMAC_STATUS_FREQUENCY_INVALID: + return "Frequency invalid"; + case LORAMAC_STATUS_DATARATE_INVALID: + return "Datarate invalid"; + case LORAMAC_STATUS_FREQ_AND_DR_INVALID: + return "Frequency or datarate invalid"; + case LORAMAC_STATUS_NO_NETWORK_JOINED: + return "No network joined"; + case LORAMAC_STATUS_LENGTH_ERROR: + return "Length error"; + case LORAMAC_STATUS_REGION_NOT_SUPPORTED: + return "Region not supported"; + case LORAMAC_STATUS_SKIPPED_APP_DATA: + return "Skipped APP data"; + case LORAMAC_STATUS_DUTYCYCLE_RESTRICTED: + return "Duty-cycle restricted"; + case LORAMAC_STATUS_NO_CHANNEL_FOUND: + return "No channel found"; + case LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND: + return "No free channel found"; + case LORAMAC_STATUS_BUSY_BEACON_RESERVED_TIME: + return "Busy beacon reserved time"; + case LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME: + return "Busy ping-slot window time"; + case LORAMAC_STATUS_BUSY_UPLINK_COLLISION: + return "Busy uplink collision"; + case LORAMAC_STATUS_CRYPTO_ERROR: + return "Crypto error"; + case LORAMAC_STATUS_FCNT_HANDLER_ERROR: + return "FCnt handler error"; + case LORAMAC_STATUS_MAC_COMMAD_ERROR: + return "MAC command error"; + case LORAMAC_STATUS_CLASS_B_ERROR: + return "ClassB error"; + case LORAMAC_STATUS_CONFIRM_QUEUE_ERROR: + return "Confirm queue error"; + case LORAMAC_STATUS_MC_GROUP_UNDEFINED: + return "Multicast group undefined"; + case LORAMAC_STATUS_ERROR: + return "Unknown error"; + default: + return NULL; + } +} + +const char *eventinfo2str(int status) +{ + switch (status) { + case LORAMAC_EVENT_INFO_STATUS_OK: + return "OK"; + case LORAMAC_EVENT_INFO_STATUS_ERROR: + return "Error"; + case LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT: + return "Tx timeout"; + case LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT: + return "Rx 1 timeout"; + case LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT: + return "Rx 2 timeout"; + case LORAMAC_EVENT_INFO_STATUS_RX1_ERROR: + return "Rx1 error"; + case LORAMAC_EVENT_INFO_STATUS_RX2_ERROR: + return "Rx2 error"; + case LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL: + return "Join failed"; + case LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED: + return "Downlink repeated"; + case LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR: + return "Tx DR payload size error"; + case LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS: + return "Downlink too many frames loss"; + case LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL: + return "Address fail"; + case LORAMAC_EVENT_INFO_STATUS_MIC_FAIL: + return "MIC fail"; + case LORAMAC_EVENT_INFO_STATUS_MULTICAST_FAIL: + return "Multicast fail"; + case LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED: + return "Beacon locked"; + case LORAMAC_EVENT_INFO_STATUS_BEACON_LOST: + return "Beacon lost"; + case LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND: + return "Beacon not found"; + default: + return NULL; + } +} + +static LoRaMacPrimitives_t macPrimitives; +static LoRaMacCallback_t macCallbacks; + +void OnMacProcessNotify(void) +{ + LoRaMacProcess(); +} + +static void McpsConfirm(McpsConfirm_t *mcpsConfirm) +{ + if (mcpsConfirm->Status != LORAMAC_EVENT_INFO_STATUS_OK) { + LOG_ERR("McpsRequest failed : %s", + log_strdup(eventinfo2str(mcpsConfirm->Status))); + } else { + LOG_DBG("McpsRequest success!"); + send_status = true; + } + + k_sem_give(&lorawan_tx_sem); +} + +static void McpsIndication(McpsIndication_t *mcpsIndication) +{ + if (mcpsIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK) { + LOG_ERR("McpsIndication failed : %s", + log_strdup(eventinfo2str(mcpsIndication->Status))); + return; + } + + /* TODO: Check MCPS Indication type */ + if (mcpsIndication->RxData == true) { + if (mcpsIndication->BufferSize != 0) { + LOG_DBG("Rx Data: %s", + log_strdup(mcpsIndication->Buffer)); + } + } + + /* TODO: Compliance test based on FPort value*/ +} + +static void MlmeConfirm(MlmeConfirm_t *mlmeConfirm) +{ + MibRequestConfirm_t mibGet; + + if (mlmeConfirm->Status != LORAMAC_EVENT_INFO_STATUS_OK) { + LOG_ERR("MlmeConfirm failed : %s", + log_strdup(eventinfo2str(mlmeConfirm->Status))); + goto out_sem; + } + + switch (mlmeConfirm->MlmeRequest) { + case MLME_JOIN: + mibGet.Type = MIB_DEV_ADDR; + LoRaMacMibGetRequestConfirm(&mibGet); + LOG_INF("Joined network! DevAddr: %08x", mibGet.Param.DevAddr); + join_status = true; + break; + case MLME_LINK_CHECK: + /* Not implemented */ + break; + default: + break; + } + +out_sem: + k_sem_give(&lorawan_config_sem); +} + +static void MlmeIndication(MlmeIndication_t *mlmeIndication) +{ + LOG_DBG("%s", __func__); +} + +int lorawan_config(struct lorawan_mib_config *mib_config) +{ + MibRequestConfirm_t mibReq; + + mibReq.Type = MIB_NWK_KEY; + mibReq.Param.NwkKey = mib_config->nwk_key; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_DEV_EUI; + mibReq.Param.DevEui = mib_config->dev_eui; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_JOIN_EUI; + mibReq.Param.JoinEui = mib_config->join_eui; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_DEVICE_CLASS; + mibReq.Param.Class = mib_config->lw_class; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_ADR; + mibReq.Param.AdrEnable = mib_config->adr_enable; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_PUBLIC_NETWORK; + mibReq.Param.EnablePublicNetwork = mib_config->pub_nw; + LoRaMacMibSetRequestConfirm(&mibReq); + + return 0; +} + +static LoRaMacStatus_t lorawan_join_otaa(enum lorawan_datarate datarate) +{ + MlmeReq_t mlmeReq; + + mlmeReq.Type = MLME_JOIN; + mlmeReq.Req.Join.Datarate = datarate; + + return LoRaMacMlmeRequest(&mlmeReq); +} + +int lorawan_join_network(enum lorawan_datarate datarate, + enum lorawan_act_type mode) +{ + LoRaMacStatus_t status; + int ret; + + k_mutex_lock(&lorawan_join_mutex, K_FOREVER); + + if (mode == LORAWAN_ACT_OTAA) { + join_status = false; + status = lorawan_join_otaa(datarate); + if (status != LORAMAC_STATUS_OK) { + LOG_ERR("OTAA join failed: %s", + log_strdup(status2str(status))); + ret = -EINVAL; + goto out; + } + + LOG_DBG("Network join request sent!"); + + /* + * We can be sure that the semaphore will be released for + * both success and failure cases after a specific time period. + * So we can use K_FOREVER and no need to check the return val. + */ + k_sem_take(&lorawan_config_sem, K_FOREVER); + } else { + ret = -EINVAL; + goto out; + } + + if (join_status) { + ret = 0; + } else { + /* TODO: Return the exact error code */ + ret = -EINVAL; + } + +out: + k_mutex_unlock(&lorawan_join_mutex); + return ret; +} + +int lorawan_send(u8_t port, enum lorawan_datarate datarate, u8_t *data, + u8_t len, bool confirm, u8_t tries) +{ + LoRaMacStatus_t status; + McpsReq_t mcpsReq; + LoRaMacTxInfo_t txInfo; + int ret = 0; + bool empty_frame = false; + + if (data == NULL) { + return -EINVAL; + } + + k_mutex_lock(&lorawan_send_mutex, K_FOREVER); + + status = LoRaMacQueryTxPossible(len, &txInfo); + if (status != LORAMAC_STATUS_OK) { + /* + * If this returns false, then most likely the payload has + * exceeded the maximum possible length for the current region + * and datarate. We can't do much other than sending empty + * frame in order to flush MAC commands in stack and hoping the + * application to lower the payload size for next try. + */ + LOG_ERR("LoRaWAN Send failed: %s", + log_strdup(status2str(status))); + empty_frame = true; + mcpsReq.Type = MCPS_UNCONFIRMED; + mcpsReq.Req.Unconfirmed.fBuffer = NULL; + mcpsReq.Req.Unconfirmed.fBufferSize = 0; + mcpsReq.Req.Unconfirmed.Datarate = DR_0; + } else { + if (confirm == false) { + mcpsReq.Type = MCPS_UNCONFIRMED; + mcpsReq.Req.Unconfirmed.fPort = port; + mcpsReq.Req.Unconfirmed.fBuffer = data; + mcpsReq.Req.Unconfirmed.fBufferSize = len; + mcpsReq.Req.Unconfirmed.Datarate = datarate; + } else { + mcpsReq.Type = MCPS_CONFIRMED; + mcpsReq.Req.Confirmed.fPort = port; + mcpsReq.Req.Confirmed.fBuffer = data; + mcpsReq.Req.Confirmed.fBufferSize = len; + mcpsReq.Req.Confirmed.NbTrials = tries; + mcpsReq.Req.Confirmed.Datarate = datarate; + send_status = false; + } + } + + status = LoRaMacMcpsRequest(&mcpsReq); + if (status != LORAMAC_STATUS_OK) { + LOG_ERR("LoRaWAN Send failed: %s", + log_strdup(status2str(status))); + ret = -EINVAL; + goto out; + } + + /* + * Indicate the application that the current packet is not sent and + * it has to resend the packet + */ + if (empty_frame) { + ret = -EAGAIN; + goto out; + } + + /* Wait for send confirmation */ + if (confirm) { + /* + * We can be sure that the semaphore will be released for + * both success and failure cases after a specific time period. + * So we can use K_FOREVER and no need to check the return val. + */ + k_sem_take(&lorawan_tx_sem, K_FOREVER); + + if (send_status) { + ret = 0; + } else { + /* TODO: Return the exact error code */ + ret = -EINVAL; + } + } + +out: + k_mutex_unlock(&lorawan_send_mutex); + return ret; +} + +static int lorawan_init(struct device *dev) +{ + LoRaMacStatus_t status; + + macPrimitives.MacMcpsConfirm = McpsConfirm; + macPrimitives.MacMcpsIndication = McpsIndication; + macPrimitives.MacMlmeConfirm = MlmeConfirm; + macPrimitives.MacMlmeIndication = MlmeIndication; + macCallbacks.GetBatteryLevel = NULL; + macCallbacks.GetTemperatureLevel = NULL; + macCallbacks.NvmContextChange = NULL; + macCallbacks.MacProcessNotify = OnMacProcessNotify; + + status = LoRaMacInitialization(&macPrimitives, &macCallbacks, + LORAWAN_REGION); + if (status != LORAMAC_STATUS_OK) { + LOG_ERR("LoRaMacInitialization failed: %s", + log_strdup(status2str(status))); + return -EINVAL; + } + + LoRaMacStart(); + + LOG_DBG("LoRaMAC Initialized"); + + return 0; +} + +SYS_INIT(lorawan_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE); From 7f040e4b20ea725d4e59ba75392af3c20a699a45 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Wed, 4 Mar 2020 19:59:49 +0530 Subject: [PATCH 05/26] samples: lorawan: Add Class-A LoRaWAN sample application This sample application shows how to configure an end node in Class-A mode and to send data to network server via gateway. Signed-off-by: Manivannan Sadhasivam --- samples/lorawan/class_a/CMakeLists.txt | 9 ++ .../lorawan/class_a/boards/96b_wistrio.conf | 1 + samples/lorawan/class_a/prj.conf | 7 ++ samples/lorawan/class_a/sample.yaml | 8 ++ samples/lorawan/class_a/src/main.c | 97 +++++++++++++++++++ 5 files changed, 122 insertions(+) create mode 100644 samples/lorawan/class_a/CMakeLists.txt create mode 100644 samples/lorawan/class_a/boards/96b_wistrio.conf create mode 100644 samples/lorawan/class_a/prj.conf create mode 100644 samples/lorawan/class_a/sample.yaml create mode 100644 samples/lorawan/class_a/src/main.c diff --git a/samples/lorawan/class_a/CMakeLists.txt b/samples/lorawan/class_a/CMakeLists.txt new file mode 100644 index 00000000000000..7938d79d8c8cca --- /dev/null +++ b/samples/lorawan/class_a/CMakeLists.txt @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) + +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(lorawan) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/samples/lorawan/class_a/boards/96b_wistrio.conf b/samples/lorawan/class_a/boards/96b_wistrio.conf new file mode 100644 index 00000000000000..be022a25e699d3 --- /dev/null +++ b/samples/lorawan/class_a/boards/96b_wistrio.conf @@ -0,0 +1 @@ +CONFIG_SPI_1=y diff --git a/samples/lorawan/class_a/prj.conf b/samples/lorawan/class_a/prj.conf new file mode 100644 index 00000000000000..62263c39a03d04 --- /dev/null +++ b/samples/lorawan/class_a/prj.conf @@ -0,0 +1,7 @@ +CONFIG_LOG=y +CONFIG_SPI=y +CONFIG_GPIO=y +CONFIG_NEWLIB_LIBC=y +CONFIG_LORA=y +CONFIG_LORA_SX1276=y +CONFIG_LORAWAN=y diff --git a/samples/lorawan/class_a/sample.yaml b/samples/lorawan/class_a/sample.yaml new file mode 100644 index 00000000000000..768c9d1d27dfee --- /dev/null +++ b/samples/lorawan/class_a/sample.yaml @@ -0,0 +1,8 @@ +common: + tags: lorawan +sample: + description: Demonstration of Class-A LoRaWAN functionality + name: LoRaWAN Class-A +tests: + samples.lorawan.class_a: + platform_whitelist: 96b_wistrio diff --git a/samples/lorawan/class_a/src/main.c b/samples/lorawan/class_a/src/main.c new file mode 100644 index 00000000000000..8d4ee53c0b8031 --- /dev/null +++ b/samples/lorawan/class_a/src/main.c @@ -0,0 +1,97 @@ +/* + * Class A LoRaWAN sample application + * + * Copyright (c) 2019 Manivannan Sadhasivam + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +/* Customize based on network configuration */ +#define LORAWAN_DEV_EUI { 0xDD, 0xEE, 0xAA, 0xDD, 0xBB, 0xEE,\ + 0xEE, 0xFF } +#define LORAWAN_JOIN_EUI { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ + 0x00, 0x00 } +#define LORAWAN_APP_EUI { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ + 0x00, 0x00 } +#define LORAWAN_APP_KEY { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE,\ + 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88,\ + 0x09, 0xCF, 0x4F, 0x3C } +#define LORAWAN_DEFAULT_DATARATE LORAWAN_DR_0 + +#define DELAY 5000 + +#define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL +#include +LOG_MODULE_REGISTER(lorawan_class_a); + +char data[] = {'h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd'}; + +void main(void) +{ + struct device *lora_dev; + struct lorawan_mib_config mib_config; + u8_t dev_eui[] = LORAWAN_DEV_EUI; + u8_t join_eui[] = LORAWAN_JOIN_EUI; + u8_t app_eui[] = LORAWAN_APP_EUI; + u8_t app_key[] = LORAWAN_APP_KEY; + int ret; + + lora_dev = device_get_binding(DT_INST_0_SEMTECH_SX1276_LABEL); + if (!lora_dev) { + LOG_ERR("%s Device not found", DT_INST_0_SEMTECH_SX1276_LABEL); + return; + } + + mib_config.lw_class = LORAWAN_CLASS_A; + mib_config.dev_eui = dev_eui; + mib_config.join_eui = join_eui; + mib_config.app_eui = app_eui; + mib_config.app_key = app_key; + mib_config.nwk_key = app_key; + mib_config.pub_nw = true; /* Connecting to a public network */ + mib_config.adr_enable = true; + + LOG_INF("Configuring LoRaWAN stack"); + ret = lorawan_config(&mib_config); + if (ret < 0) { + LOG_ERR("lorawan_config failed: %d", ret); + return; + } + + LOG_INF("Joining network over OTAA"); + ret = lorawan_join_network(LORAWAN_DEFAULT_DATARATE, LORAWAN_ACT_OTAA); + if (ret < 0) { + LOG_ERR("lorawan_join_network failed: %d", ret); + return; + } + + LOG_INF("Sending data..."); + while (1) { + ret = lorawan_send(2, LORAWAN_DEFAULT_DATARATE, data, + sizeof(data), true, 1); + + /* + * Note: The stack may return -EAGAIN if the provided data + * length exceeds the maximum possible one for the region and + * datarate. But since we are just sending the same data here, + * we'll just continue. + */ + if (ret == -EAGAIN) { + LOG_ERR("lorawan_send failed: %d. Continuing...", ret); + k_sleep(DELAY); + continue; + } + + if (ret < 0) { + LOG_ERR("lorawan_send failed: %d", ret); + return; + } + + LOG_INF("Data sent!"); + k_sleep(DELAY); + } +} From 303e759f55e388d5437ecba7206eb34eb859d5c9 Mon Sep 17 00:00:00 2001 From: Kuba Sanak Date: Wed, 4 Mar 2020 20:14:56 +0530 Subject: [PATCH 06/26] lorawan: Add support for system_max_rs_error system_max_rs_error is used to negotiate overall timing error for Rx in the loramac-node library. Hence, add support for configuring this from sample application. Signed-off-by: Kuba Sanak Signed-off-by: Manivannan Sadhasivam --- include/net/lorawan.h | 1 + samples/lorawan/class_a/src/main.c | 1 + subsys/lorawan/lorawan.c | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/include/net/lorawan.h b/include/net/lorawan.h index 40588b1c269f00..a4db5ee1a8197c 100644 --- a/include/net/lorawan.h +++ b/include/net/lorawan.h @@ -61,6 +61,7 @@ struct lorawan_mib_config { u8_t *app_eui; u8_t *app_key; u8_t *nwk_key; + u32_t system_max_rs_error; bool pub_nw; bool adr_enable; }; diff --git a/samples/lorawan/class_a/src/main.c b/samples/lorawan/class_a/src/main.c index 8d4ee53c0b8031..aa3847d6ce627a 100644 --- a/samples/lorawan/class_a/src/main.c +++ b/samples/lorawan/class_a/src/main.c @@ -54,6 +54,7 @@ void main(void) mib_config.nwk_key = app_key; mib_config.pub_nw = true; /* Connecting to a public network */ mib_config.adr_enable = true; + mib_config.system_max_rs_error = 20; LOG_INF("Configuring LoRaWAN stack"); ret = lorawan_config(&mib_config); diff --git a/subsys/lorawan/lorawan.c b/subsys/lorawan/lorawan.c index 7b1be3b9b16f77..569a625c0d0ed0 100644 --- a/subsys/lorawan/lorawan.c +++ b/subsys/lorawan/lorawan.c @@ -247,6 +247,10 @@ int lorawan_config(struct lorawan_mib_config *mib_config) mibReq.Param.EnablePublicNetwork = mib_config->pub_nw; LoRaMacMibSetRequestConfirm(&mibReq); + mibReq.Type = MIB_SYSTEM_MAX_RX_ERROR; + mibReq.Param.SystemMaxRxError = mib_config->system_max_rs_error; + LoRaMacMibSetRequestConfirm(&mibReq); + return 0; } From 7bb80fa8b9ec169b0b653a1be0e2facc654954ef Mon Sep 17 00:00:00 2001 From: Kuba Sanak Date: Thu, 12 Mar 2020 23:12:44 +0530 Subject: [PATCH 07/26] lorawan: Rework error handling code For consistency with other parts of Zephyr, the public APIs available in lorawan subsystem now returns error codes from the set defined in errno.h. Signed-off-by: Kuba Sanak [mani: reworked the code and commit a bit for upstream] Signed-off-by: Manivannan Sadhasivam --- subsys/lorawan/lorawan.c | 121 ++++++++++++++++++++++++++++----------- 1 file changed, 89 insertions(+), 32 deletions(-) diff --git a/subsys/lorawan/lorawan.c b/subsys/lorawan/lorawan.c index 569a625c0d0ed0..8813c4afbb5b7d 100644 --- a/subsys/lorawan/lorawan.c +++ b/subsys/lorawan/lorawan.c @@ -39,15 +39,12 @@ #include LOG_MODULE_REGISTER(lorawan); -K_SEM_DEFINE(lorawan_config_sem, 0, 1); -K_SEM_DEFINE(lorawan_tx_sem, 0, 1); +K_SEM_DEFINE(mlme_confirm_sem, 0, 1); +K_SEM_DEFINE(mcps_confirm_sem, 0, 1); K_MUTEX_DEFINE(lorawan_join_mutex); K_MUTEX_DEFINE(lorawan_send_mutex); -static volatile bool join_status = false; -static volatile bool send_status = false; - const char *status2str(int status) { switch (status) { @@ -146,29 +143,92 @@ const char *eventinfo2str(int status) } } +/* + * MAC status and Event status to Zephyr error code conversion. + * Direct mapping is not possible as statuses often indicate the domain from + * which the error originated rather than its cause or meaning. -EINVAL has been + * used as a general error code because those usually result from incorrect + * configuration. + */ +const int mac_status_to_errno[] = { + 0, /* LORAMAC_STATUS_OK */ + -EBUSY, /* LORAMAC_STATUS_BUSY */ + -ENOPROTOOPT, /* LORAMAC_STATUS_SERVICE_UNKNOWN */ + -EINVAL, /* LORAMAC_STATUS_PARAMETER_INVALID */ + -EINVAL, /* LORAMAC_STATUS_FREQUENCY_INVALID */ + -EINVAL, /* LORAMAC_STATUS_DATARATE_INVALID */ + -EINVAL, /* LORAMAC_STATUS_FREQ_AND_DR_INVALID */ + -ENOTCONN, /* LORAMAC_STATUS_NO_NETWORK_JOINED */ + -EMSGSIZE, /* LORAMAC_STATUS_LENGTH_ERROR */ + -EPFNOSUPPORT, /* LORAMAC_STATUS_REGION_NOT_SUPPORTED */ + -EMSGSIZE, /* LORAMAC_STATUS_SKIPPED_APP_DATA */ + -ECONNREFUSED, /* LORAMAC_STATUS_DUTYCYCLE_RESTRICTED */ + -ENOTCONN, /* LORAMAC_STATUS_NO_CHANNEL_FOUND */ + -ENOTCONN, /* LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND */ + -EBUSY, /* LORAMAC_STATUS_BUSY_BEACON_RESERVED_TIME */ + -EBUSY, /* LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME */ + -EBUSY, /* LORAMAC_STATUS_BUSY_UPLINK_COLLISION */ + -EINVAL, /* LORAMAC_STATUS_CRYPTO_ERROR */ + -EINVAL, /* LORAMAC_STATUS_FCNT_HANDLER_ERROR */ + -EINVAL, /* LORAMAC_STATUS_MAC_COMMAD_ERROR */ + -EINVAL, /* LORAMAC_STATUS_CLASS_B_ERROR */ + -EINVAL, /* LORAMAC_STATUS_CONFIRM_QUEUE_ERROR */ + -EINVAL /* LORAMAC_STATUS_MC_GROUP_UNDEFINED */ +}; + +const int mac_event_info_to_errno[] = { + 0, /* LORAMAC_EVENT_INFO_STATUS_OK */ + -EINVAL, /* LORAMAC_EVENT_INFO_STATUS_ERROR */ + -ETIMEDOUT, /* LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT */ + -ETIMEDOUT, /* LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT */ + -ETIMEDOUT, /* LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT */ + -EINVAL, /* LORAMAC_EVENT_INFO_STATUS_RX1_ERROR */ + -EINVAL, /* LORAMAC_EVENT_INFO_STATUS_RX2_ERROR */ + -EINVAL, /* LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL */ + -ECONNRESET, /* LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED */ + -EMSGSIZE, /* LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR */ + -ECONNRESET, /* LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS */ + -EACCES, /* LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL */ + -EACCES, /* LORAMAC_EVENT_INFO_STATUS_MIC_FAIL */ + -EINVAL, /* LORAMAC_EVENT_INFO_STATUS_MULTICAST_FAIL */ + -EINVAL, /* LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED */ + -EINVAL, /* LORAMAC_EVENT_INFO_STATUS_BEACON_LOST */ + -EINVAL /* LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND */ +}; + static LoRaMacPrimitives_t macPrimitives; static LoRaMacCallback_t macCallbacks; -void OnMacProcessNotify(void) +static LoRaMacEventInfoStatus_t last_mcps_confirm_status; +static LoRaMacEventInfoStatus_t last_mlme_confirm_status; +static LoRaMacEventInfoStatus_t last_mcps_indication_status; +static LoRaMacEventInfoStatus_t last_mlme_indication_status; + +static void OnMacProcessNotify(void) { LoRaMacProcess(); } static void McpsConfirm(McpsConfirm_t *mcpsConfirm) { + LOG_DBG("Received McpsConfirm (for McpsRequest %d)", + mcpsConfirm->McpsRequest); + if (mcpsConfirm->Status != LORAMAC_EVENT_INFO_STATUS_OK) { LOG_ERR("McpsRequest failed : %s", log_strdup(eventinfo2str(mcpsConfirm->Status))); } else { LOG_DBG("McpsRequest success!"); - send_status = true; } - k_sem_give(&lorawan_tx_sem); + last_mcps_confirm_status = mcpsConfirm->Status; + k_sem_give(&mcps_confirm_sem); } static void McpsIndication(McpsIndication_t *mcpsIndication) { + LOG_DBG("Received McpsIndication %d", mcpsIndication->McpsIndication); + if (mcpsIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK) { LOG_ERR("McpsIndication failed : %s", log_strdup(eventinfo2str(mcpsIndication->Status))); @@ -183,6 +243,8 @@ static void McpsIndication(McpsIndication_t *mcpsIndication) } } + last_mcps_indication_status = mcpsIndication->Status; + /* TODO: Compliance test based on FPort value*/ } @@ -190,6 +252,9 @@ static void MlmeConfirm(MlmeConfirm_t *mlmeConfirm) { MibRequestConfirm_t mibGet; + LOG_DBG("Received MlmeConfirm (for MlmeRequest %d)", + mlmeConfirm->MlmeRequest); + if (mlmeConfirm->Status != LORAMAC_EVENT_INFO_STATUS_OK) { LOG_ERR("MlmeConfirm failed : %s", log_strdup(eventinfo2str(mlmeConfirm->Status))); @@ -201,7 +266,6 @@ static void MlmeConfirm(MlmeConfirm_t *mlmeConfirm) mibGet.Type = MIB_DEV_ADDR; LoRaMacMibGetRequestConfirm(&mibGet); LOG_INF("Joined network! DevAddr: %08x", mibGet.Param.DevAddr); - join_status = true; break; case MLME_LINK_CHECK: /* Not implemented */ @@ -211,12 +275,14 @@ static void MlmeConfirm(MlmeConfirm_t *mlmeConfirm) } out_sem: - k_sem_give(&lorawan_config_sem); + last_mlme_confirm_status = mlmeConfirm->Status; + k_sem_give(&mlme_confirm_sem); } static void MlmeIndication(MlmeIndication_t *mlmeIndication) { - LOG_DBG("%s", __func__); + LOG_DBG("Received MlmeIndication %d", mlmeIndication->MlmeIndication); + last_mlme_indication_status = mlmeIndication->Status; } int lorawan_config(struct lorawan_mib_config *mib_config) @@ -268,17 +334,16 @@ int lorawan_join_network(enum lorawan_datarate datarate, enum lorawan_act_type mode) { LoRaMacStatus_t status; - int ret; + int ret = 0; k_mutex_lock(&lorawan_join_mutex, K_FOREVER); if (mode == LORAWAN_ACT_OTAA) { - join_status = false; status = lorawan_join_otaa(datarate); if (status != LORAMAC_STATUS_OK) { LOG_ERR("OTAA join failed: %s", log_strdup(status2str(status))); - ret = -EINVAL; + ret = mac_status_to_errno[status]; goto out; } @@ -289,16 +354,12 @@ int lorawan_join_network(enum lorawan_datarate datarate, * both success and failure cases after a specific time period. * So we can use K_FOREVER and no need to check the return val. */ - k_sem_take(&lorawan_config_sem, K_FOREVER); - } else { - ret = -EINVAL; - goto out; - } - - if (join_status) { - ret = 0; + k_sem_take(&mlme_confirm_sem, K_FOREVER); + if (last_mlme_confirm_status != LORAMAC_EVENT_INFO_STATUS_OK) { + ret = mac_event_info_to_errno[last_mlme_confirm_status]; + goto out; + } } else { - /* TODO: Return the exact error code */ ret = -EINVAL; } @@ -331,7 +392,7 @@ int lorawan_send(u8_t port, enum lorawan_datarate datarate, u8_t *data, * frame in order to flush MAC commands in stack and hoping the * application to lower the payload size for next try. */ - LOG_ERR("LoRaWAN Send failed: %s", + LOG_ERR("LoRaWAN Query Tx Possible Failed: %s", log_strdup(status2str(status))); empty_frame = true; mcpsReq.Type = MCPS_UNCONFIRMED; @@ -352,7 +413,6 @@ int lorawan_send(u8_t port, enum lorawan_datarate datarate, u8_t *data, mcpsReq.Req.Confirmed.fBufferSize = len; mcpsReq.Req.Confirmed.NbTrials = tries; mcpsReq.Req.Confirmed.Datarate = datarate; - send_status = false; } } @@ -360,7 +420,7 @@ int lorawan_send(u8_t port, enum lorawan_datarate datarate, u8_t *data, if (status != LORAMAC_STATUS_OK) { LOG_ERR("LoRaWAN Send failed: %s", log_strdup(status2str(status))); - ret = -EINVAL; + ret = mac_status_to_errno[status]; goto out; } @@ -380,13 +440,10 @@ int lorawan_send(u8_t port, enum lorawan_datarate datarate, u8_t *data, * both success and failure cases after a specific time period. * So we can use K_FOREVER and no need to check the return val. */ - k_sem_take(&lorawan_tx_sem, K_FOREVER); + k_sem_take(&mcps_confirm_sem, K_FOREVER); - if (send_status) { - ret = 0; - } else { - /* TODO: Return the exact error code */ - ret = -EINVAL; + if (last_mcps_confirm_status != LORAMAC_EVENT_INFO_STATUS_OK) { + ret = mac_event_info_to_errno[last_mcps_confirm_status]; } } From f425e69bc5cb3a059505a62675804cbf65982b26 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Wed, 4 Mar 2020 22:24:40 +0530 Subject: [PATCH 08/26] CODEOWNERS: Add entry for LoRaWAN Add CODEOWNERS entry for LoRaWAN Signed-off-by: Manivannan Sadhasivam --- CODEOWNERS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CODEOWNERS b/CODEOWNERS index e94dbc5c7390f9..1a00e3a019edfa 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -328,6 +328,7 @@ /include/logging/ @nordic-krch /include/net/ @jukkar @tbursztyka @pfalcon /include/net/buf.h @jukkar @jhedberg @tbursztyka @pfalcon +/include/net/lorawan.h @Mani-Sadhasivam /include/posix/ @pfalcon /include/power/power.h @wentongwu @nashif /include/ptp_clock.h @jukkar @@ -361,6 +362,7 @@ /samples/drivers/display/ @vanwinkeljan /samples/drivers/ht16k33/ @henrikbrixandersen /samples/drivers/lora/ @Mani-Sadhasivam +/samples/lorawan/ @Mani-Sadhasivam /samples/net/ @jukkar @tbursztyka @pfalcon /samples/net/dns_resolve/ @jukkar @tbursztyka @pfalcon /samples/net/lwm2m_client/ @mike-scott @@ -418,6 +420,7 @@ /subsys/fs/nvs/ @Laczen /subsys/logging/ @nordic-krch /subsys/logging/log_backend_net.c @nordic-krch @jukkar +/subsys/lorawan/ @Mani-Sadhasivam /subsys/mgmt/ @carlescufi @nvlsianpu /subsys/net/buf.c @jukkar @jhedberg @tbursztyka @pfalcon /subsys/net/ip/ @jukkar @tbursztyka @pfalcon From 768a68862e18c1e2ffd069ce353b87826a2877ce Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Sun, 22 Mar 2020 10:36:57 +0530 Subject: [PATCH 09/26] west: Update loramac-node repo Update loramac-node repo to support LoRaWAN. Signed-off-by: Manivannan Sadhasivam --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index adf2ece86b8d32..d1dc3f25f77516 100644 --- a/west.yml +++ b/west.yml @@ -98,7 +98,7 @@ manifest: revision: 9b591b289e1f37339bd038b5a1f0e6c8ad39c63a path: modules/lib/open-amp - name: loramac-node - revision: 29e516ec585b1a909af2b5f1c60d83e7d4d563e3 + revision: 85f21c03ed765ce36ac917978a2eff5fae1dbccf path: modules/lib/loramac-node - name: openthread revision: 46194ba9053d71966ddbf7c159c0ff68f93d6497 From 076a769fbb0e160e8791d9ac887f0409577a7f17 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Wed, 4 Mar 2020 16:46:48 +0530 Subject: [PATCH 10/26] drivers: lora: Prepare the SX1276 driver for LoRaWAN For using this driver with LoRaWAN, following changes were done: - Removed the use of counter and adapted system timer APIs for tick counting. This is required because most of the counter drivers run at very low tick rate like 1Hz and will not work with loramac-node library. - Removed the use of counter and adapted timer APIs for alarm functionality. - Added few more function definitions and callbacks required by the stack Signed-off-by: Manivannan Sadhasivam --- drivers/lora/CMakeLists.txt | 9 ++++++++- drivers/lora/sx1276.c | 40 ++++++++++++++++++++++++++++++++++--- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/drivers/lora/CMakeLists.txt b/drivers/lora/CMakeLists.txt index 1a359072b4f3db..8adf4405869f3f 100644 --- a/drivers/lora/CMakeLists.txt +++ b/drivers/lora/CMakeLists.txt @@ -1,6 +1,13 @@ # SPDX-License-Identifier: Apache-2.0 -zephyr_library_named(loramac-node) +# LoRa drivers depend on the include directories exposed by the loramac-node +# library. Hence, if it exists then the source files are added to that otherwise +# a library with same name is created. +if(TARGET loramac-node) + set(ZEPHYR_CURRENT_LIBRARY loramac-node) +else() + zephyr_library_named(loramac-node) +endif() zephyr_library_sources_ifdef(CONFIG_LORA_SHELL shell.c) zephyr_library_sources_ifdef(CONFIG_LORA_SX1276 sx1276.c) diff --git a/drivers/lora/sx1276.c b/drivers/lora/sx1276.c index 134549564d3db9..6884de9d950fb2 100644 --- a/drivers/lora/sx1276.c +++ b/drivers/lora/sx1276.c @@ -27,6 +27,8 @@ LOG_MODULE_REGISTER(sx1276); #define SX1276_REG_PA_DAC 0x4d #define SX1276_REG_VERSION 0x42 +#define BOARD_TCXO_WAKEUP_TIME 5 + static u32_t saved_time; extern DioIrqHandler *DioIrq[]; @@ -60,13 +62,15 @@ struct sx1276_data { struct k_sem data_sem; struct k_timer timer; RadioEvents_t sx1276_event; + /* TODO: Use Non-volatile memory for backup */ + volatile u32_t backup_reg[2]; u8_t *rx_buf; u8_t rx_len; s8_t snr; s16_t rssi; } dev_data; -bool SX1276CheckRfFrequency(uint32_t frequency) +bool SX1276CheckRfFrequency(u32_t frequency) { /* TODO */ return true; @@ -82,6 +86,11 @@ void SX1276SetBoardTcxo(u8_t state) /* TODO */ } +u32_t SX1276GetBoardTcxoWakeupTime(void) +{ + return BOARD_TCXO_WAKEUP_TIME; +} + void SX1276SetAntSw(u8_t opMode) { /* TODO */ @@ -99,12 +108,12 @@ void SX1276Reset(void) k_sleep(K_MSEC(6)); } -void BoardCriticalSectionBegin(uint32_t *mask) +void BoardCriticalSectionBegin(u32_t *mask) { *mask = irq_lock(); } -void BoardCriticalSectionEnd(uint32_t *mask) +void BoardCriticalSectionEnd(u32_t *mask) { irq_unlock(*mask); } @@ -176,6 +185,28 @@ static void sx1276_dio_work_handle(struct k_work *work) (*DioIrq[dio])(NULL); } +u32_t RtcGetCalendarTime(uint16_t *milliseconds) +{ + u32_t now = k_uptime_get_32(); + + *milliseconds = now; + + /* Return in seconds */ + return now / MSEC_PER_SEC; +} + +void RtcBkupWrite(u32_t data0, uint32_t data1) +{ + dev_data.backup_reg[0] = data0; + dev_data.backup_reg[1] = data1; +} + +void RtcBkupRead(u32_t *data0, uint32_t *data1) +{ + *data0 = dev_data.backup_reg[0]; + *data1 = dev_data.backup_reg[1]; +} + static void sx1276_irq_callback(struct device *dev, struct gpio_callback *cb, u32_t pins) { @@ -463,6 +494,7 @@ const struct Radio_s Radio = { .Random = SX1276Random, .SetRxConfig = SX1276SetRxConfig, .SetTxConfig = SX1276SetTxConfig, + .TimeOnAir = SX1276GetTimeOnAir, .Send = SX1276Send, .Sleep = SX1276SetSleep, .Standby = SX1276SetStby, @@ -472,6 +504,8 @@ const struct Radio_s Radio = { .WriteBuffer = SX1276WriteBuffer, .ReadBuffer = SX1276ReadBuffer, .SetMaxPayloadLength = SX1276SetMaxPayloadLength, + .SetPublicNetwork = SX1276SetPublicNetwork, + .GetWakeupTime = SX1276GetWakeupTime, .IrqProcess = NULL, .RxBoosted = NULL, .SetRxDutyCycle = NULL, From 1acab10ecfec55787b2be320cf5aac5b473ae4a5 Mon Sep 17 00:00:00 2001 From: Kuba Sanak Date: Wed, 4 Mar 2020 16:53:15 +0530 Subject: [PATCH 11/26] drivers: lora: Add CheckRfFrequency callback to SX1276 driver CheckRfFrequency was not assigned correctly to the Radio. This lead to the system hang when the loramac library tries to call a relevant Radio method when processing a join-accept message Signed-off-by: Kuba Sanak Signed-off-by: Manivannan Sadhasivam --- drivers/lora/sx1276.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/lora/sx1276.c b/drivers/lora/sx1276.c index 6884de9d950fb2..05e5306659dcc3 100644 --- a/drivers/lora/sx1276.c +++ b/drivers/lora/sx1276.c @@ -494,6 +494,7 @@ const struct Radio_s Radio = { .Random = SX1276Random, .SetRxConfig = SX1276SetRxConfig, .SetTxConfig = SX1276SetTxConfig, + .CheckRfFrequency = SX1276CheckRfFrequency, .TimeOnAir = SX1276GetTimeOnAir, .Send = SX1276Send, .Sleep = SX1276SetSleep, From 393d076748dc48ed560812daaac21be55b3899c7 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Wed, 4 Mar 2020 19:52:59 +0530 Subject: [PATCH 12/26] include: Add LoRaWAN API Add LoRaWAN API for Zephyr. Only config, join and send methods are implemented. Signed-off-by: Manivannan Sadhasivam --- include/net/lorawan.h | 113 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 include/net/lorawan.h diff --git a/include/net/lorawan.h b/include/net/lorawan.h new file mode 100644 index 00000000000000..40588b1c269f00 --- /dev/null +++ b/include/net/lorawan.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2019 Manivannan Sadhasivam + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_NET_LORAWAN_H_ +#define ZEPHYR_INCLUDE_NET_LORAWAN_H_ + +/** + * @file + * @brief Public LoRaWAN APIs + */ + +#include +#include + +/** + * @brief LoRaWAN class types. + */ +enum lorawan_class { + LORAWAN_CLASS_A = 0x00, + LORAWAN_CLASS_B = 0x01, + LORAWAN_CLASS_C = 0x02, +}; + +/** + * @brief LoRaWAN activation types. + */ +enum lorawan_act_type { + LORAWAN_ACT_OTAA = 0, + LORAWAN_ACT_ABP, +}; + +/** + * @brief LoRaWAN datarate types. + */ +enum lorawan_datarate { + LORAWAN_DR_0 = 0, + LORAWAN_DR_1, + LORAWAN_DR_2, + LORAWAN_DR_3, + LORAWAN_DR_4, + LORAWAN_DR_5, + LORAWAN_DR_6, + LORAWAN_DR_7, + LORAWAN_DR_8, + LORAWAN_DR_9, + LORAWAN_DR_10, + LORAWAN_DR_11, + LORAWAN_DR_12, + LORAWAN_DR_13, + LORAWAN_DR_14, + LORAWAN_DR_15, +}; + +struct lorawan_mib_config { + enum lorawan_class lw_class; + u8_t *dev_eui; + u8_t *join_eui; + u8_t *app_eui; + u8_t *app_key; + u8_t *nwk_key; + bool pub_nw; + bool adr_enable; +}; + +/** + * @brief Configure the LoRaWAN stack + * + * Configure the LoRaWAN stack using MIB (Mac Information Base) parameters. + * + * @param mib_config MIB configuration + * + * @return 0 if successful, negative errno code if failure + */ +int lorawan_config(struct lorawan_mib_config *mib_config); + +/** + * @brief Join the LoRaWAN network + * + * Join the LoRaWAN network using either OTAA or AWB. + * + * @param datarate Datarate to be used for network join + * @param mode Activation mode + * + * @return 0 if successful, negative errno code if failure + */ +int lorawan_join_network(enum lorawan_datarate datarate, + enum lorawan_act_type mode); + +/** + * @brief Send data to the LoRaWAN network + * + * Send data to the connected LoRaWAN network. + * + * @param port Port to be used for sending data. Must be set if the + * payload is not empty. + * @param datarate Datarate to be used for sending data + * @param data Data buffer to be sent + * @param len Length of the buffer to be sent. Maximum length of this + * buffer is 255 bytes but the actual payload size varies with + * region and datarate. + * @param confirm Use confirmed messages + * @param tries Number of tries needed for sending the data in case the Ack + * from server is not received + * + * @return 0 if successful, negative errno code if failure + */ +int lorawan_send(u8_t port, enum lorawan_datarate datarate, + u8_t *data, u8_t len, bool confirm, u8_t tries); + +#endif /* ZEPHYR_INCLUDE_NET_LORAWAN_H_ */ From 1c7ca9f3b009e30790b08a7c84726b1341c084cf Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Wed, 4 Mar 2020 19:54:09 +0530 Subject: [PATCH 13/26] lorawan: Add initial support for LoRaWAN Add initial support for LoRaWAN based on Semtech's loramac-node library. Current implementation only supports OTAA config and sending data to LoRaWAN server like ThingsNetwork. While at it, this commit also moves the "loramac-node" library definition from drivers/lora to subsys/lorawan. This is required because, subsys/lorawan gets processed before drivers/lora and that creates issue while building. Signed-off-by: Manivannan Sadhasivam --- modules/Kconfig.loramac-node | 6 + subsys/CMakeLists.txt | 1 + subsys/Kconfig | 2 + subsys/lorawan/CMakeLists.txt | 34 +++ subsys/lorawan/Kconfig | 58 +++++ subsys/lorawan/lorawan.c | 422 ++++++++++++++++++++++++++++++++++ 6 files changed, 523 insertions(+) create mode 100644 subsys/lorawan/CMakeLists.txt create mode 100644 subsys/lorawan/Kconfig create mode 100644 subsys/lorawan/lorawan.c diff --git a/modules/Kconfig.loramac-node b/modules/Kconfig.loramac-node index fc775d30b25ec7..0b004df7959193 100644 --- a/modules/Kconfig.loramac-node +++ b/modules/Kconfig.loramac-node @@ -9,6 +9,12 @@ config HAS_SEMTECH_LORAMAC help This option enables the use of Semtech's LoRaMac stack +config HAS_SEMTECH_SOFT_SE + bool "Semtech Secure Element software implementation" + help + This option enables the use of Semtech's Secure Element + software implementation + config HAS_SEMTECH_RADIO_DRIVERS bool "Semtech LoRa Radio Drivers" help diff --git a/subsys/CMakeLists.txt b/subsys/CMakeLists.txt index a203f5bd34db58..a92b3b1fbd54d3 100644 --- a/subsys/CMakeLists.txt +++ b/subsys/CMakeLists.txt @@ -2,6 +2,7 @@ add_subdirectory(debug) add_subdirectory(logging) +add_subdirectory_ifdef(CONFIG_LORAWAN lorawan) add_subdirectory_ifdef(CONFIG_BT bluetooth) add_subdirectory_ifdef(CONFIG_CONSOLE_SUBSYS console) add_subdirectory_ifdef(CONFIG_SHELL shell) diff --git a/subsys/Kconfig b/subsys/Kconfig index 615ae9762b6fec..6531ff9f307fe2 100644 --- a/subsys/Kconfig +++ b/subsys/Kconfig @@ -17,6 +17,8 @@ source "subsys/fs/Kconfig" source "subsys/logging/Kconfig" +source "subsys/lorawan/Kconfig" + source "subsys/mgmt/Kconfig" source "subsys/net/Kconfig" diff --git a/subsys/lorawan/CMakeLists.txt b/subsys/lorawan/CMakeLists.txt new file mode 100644 index 00000000000000..141eb972b73106 --- /dev/null +++ b/subsys/lorawan/CMakeLists.txt @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: Apache-2.0 + +# lorawan.c depend on the include directories exposed by the loramac-node +# library. Hence, if it exists then the source files are added to that otherwise +# a library with same name is created. +if(TARGET loramac-node) + set(ZEPHYR_CURRENT_LIBRARY loramac-node) +else() + zephyr_library_named(loramac-node) +endif() + +if(CONFIG_LORAMAC_REGION_AS923) + zephyr_compile_definitions(-DREGION_AS923) +elseif(CONFIG_LORAMAC_REGION_AU915) + zephyr_compile_definitions(-DREGION_AU915) +elseif(CONFIG_LORAMAC_REGION_CN470) + zephyr_compile_definitions(-DREGION_CN470) +elseif(CONFIG_LORAMAC_REGION_CN779) + zephyr_compile_definitions(-DREGION_CN779) +elseif(CONFIG_LORAMAC_REGION_EU433) + zephyr_compile_definitions(-DREGION_EU433) +elseif(CONFIG_LORAMAC_REGION_EU868) + zephyr_compile_definitions(-DREGION_EU868) +elseif(CONFIG_LORAMAC_REGION_KR920) + zephyr_compile_definitions(-DREGION_KR920) +elseif(CONFIG_LORAMAC_REGION_IN865) + zephyr_compile_definitions(-DREGION_IN865) +elseif(CONFIG_LORAMAC_REGION_US915) + zephyr_compile_definitions(-DREGION_US915) +elseif(CONFIG_LORAMAC_REGION_RU864) + zephyr_compile_definitions(-DREGION_RU864) +endif() + +zephyr_library_sources_ifdef(CONFIG_LORAWAN lorawan.c) diff --git a/subsys/lorawan/Kconfig b/subsys/lorawan/Kconfig new file mode 100644 index 00000000000000..b9bf82eea2bde3 --- /dev/null +++ b/subsys/lorawan/Kconfig @@ -0,0 +1,58 @@ +# LoRaWAN configuration options + +# Copyright (c) 2019 Manivannan Sadhasivam +# SPDX-License-Identifier: Apache-2.0 + +menuconfig LORAWAN + bool "LoRaWAN support [EXPERIMENTAL]" + depends on LORA + select HAS_SEMTECH_LORAMAC + select HAS_SEMTECH_SOFT_SE + help + This option enables LoRaWAN support. + +if LORAWAN + +module = LORAWAN +module-str = lorawan +source "subsys/logging/Kconfig.template.log_config" + +choice + prompt "LoRaWAN Region Selection" + default LORAMAC_REGION_IN865 + help + Select the LoRaWAN region. + +config LORAMAC_REGION_AS923 + bool "Asia 923MHz Frequency band" + +config LORAMAC_REGION_AU915 + bool "Australia 915MHz Frequency band" + +config LORAMAC_REGION_CN470 + bool "China 470MHz Frequency band" + +config LORAMAC_REGION_CN779 + bool "China 779MHz Frequency band" + +config LORAMAC_REGION_EU433 + bool "Europe 433MHz Frequency band" + +config LORAMAC_REGION_EU868 + bool "Europe 868MHz Frequency band" + +config LORAMAC_REGION_KR920 + bool "South Korea 920MHz Frequency band" + +config LORAMAC_REGION_IN865 + bool "India 865MHz Frequency band" + +config LORAMAC_REGION_US915 + bool "North America 915MHz Frequency band" + +config LORAMAC_REGION_RU864 + bool "Russia 864MHz Frequency band" + +endchoice + +endif # LORAWAN diff --git a/subsys/lorawan/lorawan.c b/subsys/lorawan/lorawan.c new file mode 100644 index 00000000000000..7b1be3b9b16f77 --- /dev/null +++ b/subsys/lorawan/lorawan.c @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2019 Manivannan Sadhasivam + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include + +#ifdef CONFIG_LORAMAC_REGION_AS923 + #define LORAWAN_REGION LORAMAC_REGION_AS923 +#elif CONFIG_LORAMAC_REGION_AU915 + #define LORAWAN_REGION LORAMAC_REGION_AU915 +#elif CONFIG_LORAMAC_REGION_CN470 + #define LORAWAN_REGION LORAMAC_REGION_CN470 +#elif CONFIG_LORAMAC_REGION_CN779 + #define LORAWAN_REGION LORAMAC_REGION_CN779 +#elif CONFIG_LORAMAC_REGION_EU433 + #define LORAWAN_REGION LORAMAC_REGION_EU433 +#elif CONFIG_LORAMAC_REGION_EU868 + #define LORAWAN_REGION LORAMAC_REGION_EU868 +#elif CONFIG_LORAMAC_REGION_KR920 + #define LORAWAN_REGION LORAMAC_REGION_KR920 +#elif CONFIG_LORAMAC_REGION_IN865 + #define LORAWAN_REGION LORAMAC_REGION_IN865 +#elif CONFIG_LORAMAC_REGION_US915 + #define LORAWAN_REGION LORAMAC_REGION_US915 +#elif CONFIG_LORAMAC_REGION_RU864 + #define LORAWAN_REGION LORAMAC_REGION_RU864 +#elif + #error "Atleast one LoRaWAN region should be selected" +#endif + +#define LOG_LEVEL CONFIG_LORAWAN_LOG_LEVEL +#include +LOG_MODULE_REGISTER(lorawan); + +K_SEM_DEFINE(lorawan_config_sem, 0, 1); +K_SEM_DEFINE(lorawan_tx_sem, 0, 1); + +K_MUTEX_DEFINE(lorawan_join_mutex); +K_MUTEX_DEFINE(lorawan_send_mutex); + +static volatile bool join_status = false; +static volatile bool send_status = false; + +const char *status2str(int status) +{ + switch (status) { + case LORAMAC_STATUS_OK: + return "OK"; + case LORAMAC_STATUS_BUSY: + return "Busy"; + case LORAMAC_STATUS_SERVICE_UNKNOWN: + return "Service unknown"; + case LORAMAC_STATUS_PARAMETER_INVALID: + return "Parameter invalid"; + case LORAMAC_STATUS_FREQUENCY_INVALID: + return "Frequency invalid"; + case LORAMAC_STATUS_DATARATE_INVALID: + return "Datarate invalid"; + case LORAMAC_STATUS_FREQ_AND_DR_INVALID: + return "Frequency or datarate invalid"; + case LORAMAC_STATUS_NO_NETWORK_JOINED: + return "No network joined"; + case LORAMAC_STATUS_LENGTH_ERROR: + return "Length error"; + case LORAMAC_STATUS_REGION_NOT_SUPPORTED: + return "Region not supported"; + case LORAMAC_STATUS_SKIPPED_APP_DATA: + return "Skipped APP data"; + case LORAMAC_STATUS_DUTYCYCLE_RESTRICTED: + return "Duty-cycle restricted"; + case LORAMAC_STATUS_NO_CHANNEL_FOUND: + return "No channel found"; + case LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND: + return "No free channel found"; + case LORAMAC_STATUS_BUSY_BEACON_RESERVED_TIME: + return "Busy beacon reserved time"; + case LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME: + return "Busy ping-slot window time"; + case LORAMAC_STATUS_BUSY_UPLINK_COLLISION: + return "Busy uplink collision"; + case LORAMAC_STATUS_CRYPTO_ERROR: + return "Crypto error"; + case LORAMAC_STATUS_FCNT_HANDLER_ERROR: + return "FCnt handler error"; + case LORAMAC_STATUS_MAC_COMMAD_ERROR: + return "MAC command error"; + case LORAMAC_STATUS_CLASS_B_ERROR: + return "ClassB error"; + case LORAMAC_STATUS_CONFIRM_QUEUE_ERROR: + return "Confirm queue error"; + case LORAMAC_STATUS_MC_GROUP_UNDEFINED: + return "Multicast group undefined"; + case LORAMAC_STATUS_ERROR: + return "Unknown error"; + default: + return NULL; + } +} + +const char *eventinfo2str(int status) +{ + switch (status) { + case LORAMAC_EVENT_INFO_STATUS_OK: + return "OK"; + case LORAMAC_EVENT_INFO_STATUS_ERROR: + return "Error"; + case LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT: + return "Tx timeout"; + case LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT: + return "Rx 1 timeout"; + case LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT: + return "Rx 2 timeout"; + case LORAMAC_EVENT_INFO_STATUS_RX1_ERROR: + return "Rx1 error"; + case LORAMAC_EVENT_INFO_STATUS_RX2_ERROR: + return "Rx2 error"; + case LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL: + return "Join failed"; + case LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED: + return "Downlink repeated"; + case LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR: + return "Tx DR payload size error"; + case LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS: + return "Downlink too many frames loss"; + case LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL: + return "Address fail"; + case LORAMAC_EVENT_INFO_STATUS_MIC_FAIL: + return "MIC fail"; + case LORAMAC_EVENT_INFO_STATUS_MULTICAST_FAIL: + return "Multicast fail"; + case LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED: + return "Beacon locked"; + case LORAMAC_EVENT_INFO_STATUS_BEACON_LOST: + return "Beacon lost"; + case LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND: + return "Beacon not found"; + default: + return NULL; + } +} + +static LoRaMacPrimitives_t macPrimitives; +static LoRaMacCallback_t macCallbacks; + +void OnMacProcessNotify(void) +{ + LoRaMacProcess(); +} + +static void McpsConfirm(McpsConfirm_t *mcpsConfirm) +{ + if (mcpsConfirm->Status != LORAMAC_EVENT_INFO_STATUS_OK) { + LOG_ERR("McpsRequest failed : %s", + log_strdup(eventinfo2str(mcpsConfirm->Status))); + } else { + LOG_DBG("McpsRequest success!"); + send_status = true; + } + + k_sem_give(&lorawan_tx_sem); +} + +static void McpsIndication(McpsIndication_t *mcpsIndication) +{ + if (mcpsIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK) { + LOG_ERR("McpsIndication failed : %s", + log_strdup(eventinfo2str(mcpsIndication->Status))); + return; + } + + /* TODO: Check MCPS Indication type */ + if (mcpsIndication->RxData == true) { + if (mcpsIndication->BufferSize != 0) { + LOG_DBG("Rx Data: %s", + log_strdup(mcpsIndication->Buffer)); + } + } + + /* TODO: Compliance test based on FPort value*/ +} + +static void MlmeConfirm(MlmeConfirm_t *mlmeConfirm) +{ + MibRequestConfirm_t mibGet; + + if (mlmeConfirm->Status != LORAMAC_EVENT_INFO_STATUS_OK) { + LOG_ERR("MlmeConfirm failed : %s", + log_strdup(eventinfo2str(mlmeConfirm->Status))); + goto out_sem; + } + + switch (mlmeConfirm->MlmeRequest) { + case MLME_JOIN: + mibGet.Type = MIB_DEV_ADDR; + LoRaMacMibGetRequestConfirm(&mibGet); + LOG_INF("Joined network! DevAddr: %08x", mibGet.Param.DevAddr); + join_status = true; + break; + case MLME_LINK_CHECK: + /* Not implemented */ + break; + default: + break; + } + +out_sem: + k_sem_give(&lorawan_config_sem); +} + +static void MlmeIndication(MlmeIndication_t *mlmeIndication) +{ + LOG_DBG("%s", __func__); +} + +int lorawan_config(struct lorawan_mib_config *mib_config) +{ + MibRequestConfirm_t mibReq; + + mibReq.Type = MIB_NWK_KEY; + mibReq.Param.NwkKey = mib_config->nwk_key; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_DEV_EUI; + mibReq.Param.DevEui = mib_config->dev_eui; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_JOIN_EUI; + mibReq.Param.JoinEui = mib_config->join_eui; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_DEVICE_CLASS; + mibReq.Param.Class = mib_config->lw_class; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_ADR; + mibReq.Param.AdrEnable = mib_config->adr_enable; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_PUBLIC_NETWORK; + mibReq.Param.EnablePublicNetwork = mib_config->pub_nw; + LoRaMacMibSetRequestConfirm(&mibReq); + + return 0; +} + +static LoRaMacStatus_t lorawan_join_otaa(enum lorawan_datarate datarate) +{ + MlmeReq_t mlmeReq; + + mlmeReq.Type = MLME_JOIN; + mlmeReq.Req.Join.Datarate = datarate; + + return LoRaMacMlmeRequest(&mlmeReq); +} + +int lorawan_join_network(enum lorawan_datarate datarate, + enum lorawan_act_type mode) +{ + LoRaMacStatus_t status; + int ret; + + k_mutex_lock(&lorawan_join_mutex, K_FOREVER); + + if (mode == LORAWAN_ACT_OTAA) { + join_status = false; + status = lorawan_join_otaa(datarate); + if (status != LORAMAC_STATUS_OK) { + LOG_ERR("OTAA join failed: %s", + log_strdup(status2str(status))); + ret = -EINVAL; + goto out; + } + + LOG_DBG("Network join request sent!"); + + /* + * We can be sure that the semaphore will be released for + * both success and failure cases after a specific time period. + * So we can use K_FOREVER and no need to check the return val. + */ + k_sem_take(&lorawan_config_sem, K_FOREVER); + } else { + ret = -EINVAL; + goto out; + } + + if (join_status) { + ret = 0; + } else { + /* TODO: Return the exact error code */ + ret = -EINVAL; + } + +out: + k_mutex_unlock(&lorawan_join_mutex); + return ret; +} + +int lorawan_send(u8_t port, enum lorawan_datarate datarate, u8_t *data, + u8_t len, bool confirm, u8_t tries) +{ + LoRaMacStatus_t status; + McpsReq_t mcpsReq; + LoRaMacTxInfo_t txInfo; + int ret = 0; + bool empty_frame = false; + + if (data == NULL) { + return -EINVAL; + } + + k_mutex_lock(&lorawan_send_mutex, K_FOREVER); + + status = LoRaMacQueryTxPossible(len, &txInfo); + if (status != LORAMAC_STATUS_OK) { + /* + * If this returns false, then most likely the payload has + * exceeded the maximum possible length for the current region + * and datarate. We can't do much other than sending empty + * frame in order to flush MAC commands in stack and hoping the + * application to lower the payload size for next try. + */ + LOG_ERR("LoRaWAN Send failed: %s", + log_strdup(status2str(status))); + empty_frame = true; + mcpsReq.Type = MCPS_UNCONFIRMED; + mcpsReq.Req.Unconfirmed.fBuffer = NULL; + mcpsReq.Req.Unconfirmed.fBufferSize = 0; + mcpsReq.Req.Unconfirmed.Datarate = DR_0; + } else { + if (confirm == false) { + mcpsReq.Type = MCPS_UNCONFIRMED; + mcpsReq.Req.Unconfirmed.fPort = port; + mcpsReq.Req.Unconfirmed.fBuffer = data; + mcpsReq.Req.Unconfirmed.fBufferSize = len; + mcpsReq.Req.Unconfirmed.Datarate = datarate; + } else { + mcpsReq.Type = MCPS_CONFIRMED; + mcpsReq.Req.Confirmed.fPort = port; + mcpsReq.Req.Confirmed.fBuffer = data; + mcpsReq.Req.Confirmed.fBufferSize = len; + mcpsReq.Req.Confirmed.NbTrials = tries; + mcpsReq.Req.Confirmed.Datarate = datarate; + send_status = false; + } + } + + status = LoRaMacMcpsRequest(&mcpsReq); + if (status != LORAMAC_STATUS_OK) { + LOG_ERR("LoRaWAN Send failed: %s", + log_strdup(status2str(status))); + ret = -EINVAL; + goto out; + } + + /* + * Indicate the application that the current packet is not sent and + * it has to resend the packet + */ + if (empty_frame) { + ret = -EAGAIN; + goto out; + } + + /* Wait for send confirmation */ + if (confirm) { + /* + * We can be sure that the semaphore will be released for + * both success and failure cases after a specific time period. + * So we can use K_FOREVER and no need to check the return val. + */ + k_sem_take(&lorawan_tx_sem, K_FOREVER); + + if (send_status) { + ret = 0; + } else { + /* TODO: Return the exact error code */ + ret = -EINVAL; + } + } + +out: + k_mutex_unlock(&lorawan_send_mutex); + return ret; +} + +static int lorawan_init(struct device *dev) +{ + LoRaMacStatus_t status; + + macPrimitives.MacMcpsConfirm = McpsConfirm; + macPrimitives.MacMcpsIndication = McpsIndication; + macPrimitives.MacMlmeConfirm = MlmeConfirm; + macPrimitives.MacMlmeIndication = MlmeIndication; + macCallbacks.GetBatteryLevel = NULL; + macCallbacks.GetTemperatureLevel = NULL; + macCallbacks.NvmContextChange = NULL; + macCallbacks.MacProcessNotify = OnMacProcessNotify; + + status = LoRaMacInitialization(&macPrimitives, &macCallbacks, + LORAWAN_REGION); + if (status != LORAMAC_STATUS_OK) { + LOG_ERR("LoRaMacInitialization failed: %s", + log_strdup(status2str(status))); + return -EINVAL; + } + + LoRaMacStart(); + + LOG_DBG("LoRaMAC Initialized"); + + return 0; +} + +SYS_INIT(lorawan_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE); From 2b27ea82ba4aa22ff92d961b19a45d6a6baf30ca Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Wed, 4 Mar 2020 19:59:49 +0530 Subject: [PATCH 14/26] samples: lorawan: Add Class-A LoRaWAN sample application This sample application shows how to configure an end node in Class-A mode and to send data to network server via gateway. Signed-off-by: Manivannan Sadhasivam --- samples/lorawan/class_a/CMakeLists.txt | 9 ++ .../lorawan/class_a/boards/96b_wistrio.conf | 1 + samples/lorawan/class_a/prj.conf | 7 ++ samples/lorawan/class_a/sample.yaml | 8 ++ samples/lorawan/class_a/src/main.c | 102 ++++++++++++++++++ 5 files changed, 127 insertions(+) create mode 100644 samples/lorawan/class_a/CMakeLists.txt create mode 100644 samples/lorawan/class_a/boards/96b_wistrio.conf create mode 100644 samples/lorawan/class_a/prj.conf create mode 100644 samples/lorawan/class_a/sample.yaml create mode 100644 samples/lorawan/class_a/src/main.c diff --git a/samples/lorawan/class_a/CMakeLists.txt b/samples/lorawan/class_a/CMakeLists.txt new file mode 100644 index 00000000000000..7938d79d8c8cca --- /dev/null +++ b/samples/lorawan/class_a/CMakeLists.txt @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) + +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(lorawan) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/samples/lorawan/class_a/boards/96b_wistrio.conf b/samples/lorawan/class_a/boards/96b_wistrio.conf new file mode 100644 index 00000000000000..be022a25e699d3 --- /dev/null +++ b/samples/lorawan/class_a/boards/96b_wistrio.conf @@ -0,0 +1 @@ +CONFIG_SPI_1=y diff --git a/samples/lorawan/class_a/prj.conf b/samples/lorawan/class_a/prj.conf new file mode 100644 index 00000000000000..62263c39a03d04 --- /dev/null +++ b/samples/lorawan/class_a/prj.conf @@ -0,0 +1,7 @@ +CONFIG_LOG=y +CONFIG_SPI=y +CONFIG_GPIO=y +CONFIG_NEWLIB_LIBC=y +CONFIG_LORA=y +CONFIG_LORA_SX1276=y +CONFIG_LORAWAN=y diff --git a/samples/lorawan/class_a/sample.yaml b/samples/lorawan/class_a/sample.yaml new file mode 100644 index 00000000000000..768c9d1d27dfee --- /dev/null +++ b/samples/lorawan/class_a/sample.yaml @@ -0,0 +1,8 @@ +common: + tags: lorawan +sample: + description: Demonstration of Class-A LoRaWAN functionality + name: LoRaWAN Class-A +tests: + samples.lorawan.class_a: + platform_whitelist: 96b_wistrio diff --git a/samples/lorawan/class_a/src/main.c b/samples/lorawan/class_a/src/main.c new file mode 100644 index 00000000000000..69eeefaf5c15ae --- /dev/null +++ b/samples/lorawan/class_a/src/main.c @@ -0,0 +1,102 @@ +/* + * Class A LoRaWAN sample application + * + * Copyright (c) 2019 Manivannan Sadhasivam + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#define DEFAULT_RADIO_NODE DT_ALIAS(lora0) +BUILD_ASSERT(DT_HAS_NODE_STATUS_OKAY(DEFAULT_RADIO_NODE), + "No default LoRa radio specified in DT"); +#define DEFAULT_RADIO DT_LABEL(DEFAULT_RADIO_NODE) + +/* Customize based on network configuration */ +#define LORAWAN_DEV_EUI { 0xDD, 0xEE, 0xAA, 0xDD, 0xBB, 0xEE,\ + 0xEE, 0xFF } +#define LORAWAN_JOIN_EUI { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ + 0x00, 0x00 } +#define LORAWAN_APP_EUI { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ + 0x00, 0x00 } +#define LORAWAN_APP_KEY { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE,\ + 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88,\ + 0x09, 0xCF, 0x4F, 0x3C } +#define LORAWAN_DEFAULT_DATARATE LORAWAN_DR_0 + +#define DELAY K_MSEC(5000) + +#define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL +#include +LOG_MODULE_REGISTER(lorawan_class_a); + +char data[] = {'h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd'}; + +void main(void) +{ + struct device *lora_dev; + struct lorawan_mib_config mib_config; + u8_t dev_eui[] = LORAWAN_DEV_EUI; + u8_t join_eui[] = LORAWAN_JOIN_EUI; + u8_t app_eui[] = LORAWAN_APP_EUI; + u8_t app_key[] = LORAWAN_APP_KEY; + int ret; + + lora_dev = device_get_binding(DEFAULT_RADIO); + if (!lora_dev) { + LOG_ERR("%s Device not found", DEFAULT_RADIO); + return; + } + + mib_config.lw_class = LORAWAN_CLASS_A; + mib_config.dev_eui = dev_eui; + mib_config.join_eui = join_eui; + mib_config.app_eui = app_eui; + mib_config.app_key = app_key; + mib_config.nwk_key = app_key; + mib_config.pub_nw = true; /* Connecting to a public network */ + mib_config.adr_enable = true; + + LOG_INF("Configuring LoRaWAN stack"); + ret = lorawan_config(&mib_config); + if (ret < 0) { + LOG_ERR("lorawan_config failed: %d", ret); + return; + } + + LOG_INF("Joining network over OTAA"); + ret = lorawan_join_network(LORAWAN_DEFAULT_DATARATE, LORAWAN_ACT_OTAA); + if (ret < 0) { + LOG_ERR("lorawan_join_network failed: %d", ret); + return; + } + + LOG_INF("Sending data..."); + while (1) { + ret = lorawan_send(2, LORAWAN_DEFAULT_DATARATE, data, + sizeof(data), true, 1); + + /* + * Note: The stack may return -EAGAIN if the provided data + * length exceeds the maximum possible one for the region and + * datarate. But since we are just sending the same data here, + * we'll just continue. + */ + if (ret == -EAGAIN) { + LOG_ERR("lorawan_send failed: %d. Continuing...", ret); + k_sleep(DELAY); + continue; + } + + if (ret < 0) { + LOG_ERR("lorawan_send failed: %d", ret); + return; + } + + LOG_INF("Data sent!"); + k_sleep(DELAY); + } +} From 67063356e679fc910ccc34df584c20986f40fb40 Mon Sep 17 00:00:00 2001 From: Kuba Sanak Date: Wed, 4 Mar 2020 20:14:56 +0530 Subject: [PATCH 15/26] lorawan: Add support for system_max_rs_error system_max_rs_error is used to negotiate overall timing error for Rx in the loramac-node library. Hence, add support for configuring this from sample application. Signed-off-by: Kuba Sanak Signed-off-by: Manivannan Sadhasivam --- include/net/lorawan.h | 1 + samples/lorawan/class_a/src/main.c | 1 + subsys/lorawan/lorawan.c | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/include/net/lorawan.h b/include/net/lorawan.h index 40588b1c269f00..a4db5ee1a8197c 100644 --- a/include/net/lorawan.h +++ b/include/net/lorawan.h @@ -61,6 +61,7 @@ struct lorawan_mib_config { u8_t *app_eui; u8_t *app_key; u8_t *nwk_key; + u32_t system_max_rs_error; bool pub_nw; bool adr_enable; }; diff --git a/samples/lorawan/class_a/src/main.c b/samples/lorawan/class_a/src/main.c index 69eeefaf5c15ae..be414332bc14ec 100644 --- a/samples/lorawan/class_a/src/main.c +++ b/samples/lorawan/class_a/src/main.c @@ -59,6 +59,7 @@ void main(void) mib_config.nwk_key = app_key; mib_config.pub_nw = true; /* Connecting to a public network */ mib_config.adr_enable = true; + mib_config.system_max_rs_error = 20; LOG_INF("Configuring LoRaWAN stack"); ret = lorawan_config(&mib_config); diff --git a/subsys/lorawan/lorawan.c b/subsys/lorawan/lorawan.c index 7b1be3b9b16f77..569a625c0d0ed0 100644 --- a/subsys/lorawan/lorawan.c +++ b/subsys/lorawan/lorawan.c @@ -247,6 +247,10 @@ int lorawan_config(struct lorawan_mib_config *mib_config) mibReq.Param.EnablePublicNetwork = mib_config->pub_nw; LoRaMacMibSetRequestConfirm(&mibReq); + mibReq.Type = MIB_SYSTEM_MAX_RX_ERROR; + mibReq.Param.SystemMaxRxError = mib_config->system_max_rs_error; + LoRaMacMibSetRequestConfirm(&mibReq); + return 0; } From 87c79d62aa5d78d3a5e031b01057df92c16dd807 Mon Sep 17 00:00:00 2001 From: Kuba Sanak Date: Thu, 12 Mar 2020 23:12:44 +0530 Subject: [PATCH 16/26] lorawan: Rework error handling code For consistency with other parts of Zephyr, the public APIs available in lorawan subsystem now returns error codes from the set defined in errno.h. Signed-off-by: Kuba Sanak [mani: reworked the code and commit a bit for upstream] Signed-off-by: Manivannan Sadhasivam --- subsys/lorawan/lorawan.c | 121 ++++++++++++++++++++++++++++----------- 1 file changed, 89 insertions(+), 32 deletions(-) diff --git a/subsys/lorawan/lorawan.c b/subsys/lorawan/lorawan.c index 569a625c0d0ed0..8813c4afbb5b7d 100644 --- a/subsys/lorawan/lorawan.c +++ b/subsys/lorawan/lorawan.c @@ -39,15 +39,12 @@ #include LOG_MODULE_REGISTER(lorawan); -K_SEM_DEFINE(lorawan_config_sem, 0, 1); -K_SEM_DEFINE(lorawan_tx_sem, 0, 1); +K_SEM_DEFINE(mlme_confirm_sem, 0, 1); +K_SEM_DEFINE(mcps_confirm_sem, 0, 1); K_MUTEX_DEFINE(lorawan_join_mutex); K_MUTEX_DEFINE(lorawan_send_mutex); -static volatile bool join_status = false; -static volatile bool send_status = false; - const char *status2str(int status) { switch (status) { @@ -146,29 +143,92 @@ const char *eventinfo2str(int status) } } +/* + * MAC status and Event status to Zephyr error code conversion. + * Direct mapping is not possible as statuses often indicate the domain from + * which the error originated rather than its cause or meaning. -EINVAL has been + * used as a general error code because those usually result from incorrect + * configuration. + */ +const int mac_status_to_errno[] = { + 0, /* LORAMAC_STATUS_OK */ + -EBUSY, /* LORAMAC_STATUS_BUSY */ + -ENOPROTOOPT, /* LORAMAC_STATUS_SERVICE_UNKNOWN */ + -EINVAL, /* LORAMAC_STATUS_PARAMETER_INVALID */ + -EINVAL, /* LORAMAC_STATUS_FREQUENCY_INVALID */ + -EINVAL, /* LORAMAC_STATUS_DATARATE_INVALID */ + -EINVAL, /* LORAMAC_STATUS_FREQ_AND_DR_INVALID */ + -ENOTCONN, /* LORAMAC_STATUS_NO_NETWORK_JOINED */ + -EMSGSIZE, /* LORAMAC_STATUS_LENGTH_ERROR */ + -EPFNOSUPPORT, /* LORAMAC_STATUS_REGION_NOT_SUPPORTED */ + -EMSGSIZE, /* LORAMAC_STATUS_SKIPPED_APP_DATA */ + -ECONNREFUSED, /* LORAMAC_STATUS_DUTYCYCLE_RESTRICTED */ + -ENOTCONN, /* LORAMAC_STATUS_NO_CHANNEL_FOUND */ + -ENOTCONN, /* LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND */ + -EBUSY, /* LORAMAC_STATUS_BUSY_BEACON_RESERVED_TIME */ + -EBUSY, /* LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME */ + -EBUSY, /* LORAMAC_STATUS_BUSY_UPLINK_COLLISION */ + -EINVAL, /* LORAMAC_STATUS_CRYPTO_ERROR */ + -EINVAL, /* LORAMAC_STATUS_FCNT_HANDLER_ERROR */ + -EINVAL, /* LORAMAC_STATUS_MAC_COMMAD_ERROR */ + -EINVAL, /* LORAMAC_STATUS_CLASS_B_ERROR */ + -EINVAL, /* LORAMAC_STATUS_CONFIRM_QUEUE_ERROR */ + -EINVAL /* LORAMAC_STATUS_MC_GROUP_UNDEFINED */ +}; + +const int mac_event_info_to_errno[] = { + 0, /* LORAMAC_EVENT_INFO_STATUS_OK */ + -EINVAL, /* LORAMAC_EVENT_INFO_STATUS_ERROR */ + -ETIMEDOUT, /* LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT */ + -ETIMEDOUT, /* LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT */ + -ETIMEDOUT, /* LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT */ + -EINVAL, /* LORAMAC_EVENT_INFO_STATUS_RX1_ERROR */ + -EINVAL, /* LORAMAC_EVENT_INFO_STATUS_RX2_ERROR */ + -EINVAL, /* LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL */ + -ECONNRESET, /* LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED */ + -EMSGSIZE, /* LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR */ + -ECONNRESET, /* LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS */ + -EACCES, /* LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL */ + -EACCES, /* LORAMAC_EVENT_INFO_STATUS_MIC_FAIL */ + -EINVAL, /* LORAMAC_EVENT_INFO_STATUS_MULTICAST_FAIL */ + -EINVAL, /* LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED */ + -EINVAL, /* LORAMAC_EVENT_INFO_STATUS_BEACON_LOST */ + -EINVAL /* LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND */ +}; + static LoRaMacPrimitives_t macPrimitives; static LoRaMacCallback_t macCallbacks; -void OnMacProcessNotify(void) +static LoRaMacEventInfoStatus_t last_mcps_confirm_status; +static LoRaMacEventInfoStatus_t last_mlme_confirm_status; +static LoRaMacEventInfoStatus_t last_mcps_indication_status; +static LoRaMacEventInfoStatus_t last_mlme_indication_status; + +static void OnMacProcessNotify(void) { LoRaMacProcess(); } static void McpsConfirm(McpsConfirm_t *mcpsConfirm) { + LOG_DBG("Received McpsConfirm (for McpsRequest %d)", + mcpsConfirm->McpsRequest); + if (mcpsConfirm->Status != LORAMAC_EVENT_INFO_STATUS_OK) { LOG_ERR("McpsRequest failed : %s", log_strdup(eventinfo2str(mcpsConfirm->Status))); } else { LOG_DBG("McpsRequest success!"); - send_status = true; } - k_sem_give(&lorawan_tx_sem); + last_mcps_confirm_status = mcpsConfirm->Status; + k_sem_give(&mcps_confirm_sem); } static void McpsIndication(McpsIndication_t *mcpsIndication) { + LOG_DBG("Received McpsIndication %d", mcpsIndication->McpsIndication); + if (mcpsIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK) { LOG_ERR("McpsIndication failed : %s", log_strdup(eventinfo2str(mcpsIndication->Status))); @@ -183,6 +243,8 @@ static void McpsIndication(McpsIndication_t *mcpsIndication) } } + last_mcps_indication_status = mcpsIndication->Status; + /* TODO: Compliance test based on FPort value*/ } @@ -190,6 +252,9 @@ static void MlmeConfirm(MlmeConfirm_t *mlmeConfirm) { MibRequestConfirm_t mibGet; + LOG_DBG("Received MlmeConfirm (for MlmeRequest %d)", + mlmeConfirm->MlmeRequest); + if (mlmeConfirm->Status != LORAMAC_EVENT_INFO_STATUS_OK) { LOG_ERR("MlmeConfirm failed : %s", log_strdup(eventinfo2str(mlmeConfirm->Status))); @@ -201,7 +266,6 @@ static void MlmeConfirm(MlmeConfirm_t *mlmeConfirm) mibGet.Type = MIB_DEV_ADDR; LoRaMacMibGetRequestConfirm(&mibGet); LOG_INF("Joined network! DevAddr: %08x", mibGet.Param.DevAddr); - join_status = true; break; case MLME_LINK_CHECK: /* Not implemented */ @@ -211,12 +275,14 @@ static void MlmeConfirm(MlmeConfirm_t *mlmeConfirm) } out_sem: - k_sem_give(&lorawan_config_sem); + last_mlme_confirm_status = mlmeConfirm->Status; + k_sem_give(&mlme_confirm_sem); } static void MlmeIndication(MlmeIndication_t *mlmeIndication) { - LOG_DBG("%s", __func__); + LOG_DBG("Received MlmeIndication %d", mlmeIndication->MlmeIndication); + last_mlme_indication_status = mlmeIndication->Status; } int lorawan_config(struct lorawan_mib_config *mib_config) @@ -268,17 +334,16 @@ int lorawan_join_network(enum lorawan_datarate datarate, enum lorawan_act_type mode) { LoRaMacStatus_t status; - int ret; + int ret = 0; k_mutex_lock(&lorawan_join_mutex, K_FOREVER); if (mode == LORAWAN_ACT_OTAA) { - join_status = false; status = lorawan_join_otaa(datarate); if (status != LORAMAC_STATUS_OK) { LOG_ERR("OTAA join failed: %s", log_strdup(status2str(status))); - ret = -EINVAL; + ret = mac_status_to_errno[status]; goto out; } @@ -289,16 +354,12 @@ int lorawan_join_network(enum lorawan_datarate datarate, * both success and failure cases after a specific time period. * So we can use K_FOREVER and no need to check the return val. */ - k_sem_take(&lorawan_config_sem, K_FOREVER); - } else { - ret = -EINVAL; - goto out; - } - - if (join_status) { - ret = 0; + k_sem_take(&mlme_confirm_sem, K_FOREVER); + if (last_mlme_confirm_status != LORAMAC_EVENT_INFO_STATUS_OK) { + ret = mac_event_info_to_errno[last_mlme_confirm_status]; + goto out; + } } else { - /* TODO: Return the exact error code */ ret = -EINVAL; } @@ -331,7 +392,7 @@ int lorawan_send(u8_t port, enum lorawan_datarate datarate, u8_t *data, * frame in order to flush MAC commands in stack and hoping the * application to lower the payload size for next try. */ - LOG_ERR("LoRaWAN Send failed: %s", + LOG_ERR("LoRaWAN Query Tx Possible Failed: %s", log_strdup(status2str(status))); empty_frame = true; mcpsReq.Type = MCPS_UNCONFIRMED; @@ -352,7 +413,6 @@ int lorawan_send(u8_t port, enum lorawan_datarate datarate, u8_t *data, mcpsReq.Req.Confirmed.fBufferSize = len; mcpsReq.Req.Confirmed.NbTrials = tries; mcpsReq.Req.Confirmed.Datarate = datarate; - send_status = false; } } @@ -360,7 +420,7 @@ int lorawan_send(u8_t port, enum lorawan_datarate datarate, u8_t *data, if (status != LORAMAC_STATUS_OK) { LOG_ERR("LoRaWAN Send failed: %s", log_strdup(status2str(status))); - ret = -EINVAL; + ret = mac_status_to_errno[status]; goto out; } @@ -380,13 +440,10 @@ int lorawan_send(u8_t port, enum lorawan_datarate datarate, u8_t *data, * both success and failure cases after a specific time period. * So we can use K_FOREVER and no need to check the return val. */ - k_sem_take(&lorawan_tx_sem, K_FOREVER); + k_sem_take(&mcps_confirm_sem, K_FOREVER); - if (send_status) { - ret = 0; - } else { - /* TODO: Return the exact error code */ - ret = -EINVAL; + if (last_mcps_confirm_status != LORAMAC_EVENT_INFO_STATUS_OK) { + ret = mac_event_info_to_errno[last_mcps_confirm_status]; } } From cbbc0430c4a8cd316712643de6484f9a1967c3bb Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Wed, 4 Mar 2020 22:24:40 +0530 Subject: [PATCH 17/26] CODEOWNERS: Add entry for LoRaWAN Add CODEOWNERS entry for LoRaWAN Signed-off-by: Manivannan Sadhasivam --- CODEOWNERS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CODEOWNERS b/CODEOWNERS index 7b4cdf65485e6b..92e3313387a1a5 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -344,6 +344,7 @@ /include/logging/ @nordic-krch /include/net/ @jukkar @tbursztyka @pfalcon /include/net/buf.h @jukkar @jhedberg @tbursztyka @pfalcon +/include/net/lorawan.h @Mani-Sadhasivam /include/posix/ @pfalcon /include/power/power.h @wentongwu @nashif /include/ptp_clock.h @jukkar @@ -379,6 +380,7 @@ /samples/drivers/ht16k33/ @henrikbrixandersen /samples/drivers/lora/ @Mani-Sadhasivam /samples/drivers/counter/maxim_ds3231/ @pabigot +/samples/lorawan/ @Mani-Sadhasivam /samples/net/ @jukkar @tbursztyka @pfalcon /samples/net/dns_resolve/ @jukkar @tbursztyka @pfalcon /samples/net/lwm2m_client/ @rlubos @@ -442,6 +444,7 @@ /subsys/fs/nvs/ @Laczen /subsys/logging/ @nordic-krch /subsys/logging/log_backend_net.c @nordic-krch @jukkar +/subsys/lorawan/ @Mani-Sadhasivam /subsys/mgmt/ @carlescufi @nvlsianpu /subsys/mgmt/smp_udp.c @aunsbjerg /subsys/net/buf.c @jukkar @jhedberg @tbursztyka @pfalcon From 2b2d81e802fc4c1f7d2dd95bef4a1f7a8eb6543f Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Sun, 22 Mar 2020 10:36:57 +0530 Subject: [PATCH 18/26] west: Update loramac-node repo Update loramac-node repo to support LoRaWAN. Signed-off-by: Manivannan Sadhasivam --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index fb15152c283a52..0edef9e485b427 100644 --- a/west.yml +++ b/west.yml @@ -98,7 +98,7 @@ manifest: revision: 724f7e2a4519d7e1d40ef330042682dea950c991 path: modules/lib/open-amp - name: loramac-node - revision: 29e516ec585b1a909af2b5f1c60d83e7d4d563e3 + revision: 85f21c03ed765ce36ac917978a2eff5fae1dbccf path: modules/lib/loramac-node - name: openthread revision: a83d18cf18bc8cd431ad6704ad0bf0e6d08c99d7 From d1a2b8e37eb40d9824d82ab8e617bf426e34e0dd Mon Sep 17 00:00:00 2001 From: Kuba Sanak Date: Tue, 12 May 2020 12:45:47 +0100 Subject: [PATCH 19/26] Add downlink latest message read functionality. Currently only the latest downlink message can be read - using Zephyr ring buffer generates hard faults when sending a message after DL have been received for 2nd/3rd times... Signed-off-by: Kuba Sanak --- include/net/lorawan.h | 25 ++++ samples/lorawan/class_a/src/main.c | 36 ++++-- subsys/lorawan/Kconfig | 9 +- subsys/lorawan/lorawan.c | 196 ++++++++++++++++++++++++++++- 4 files changed, 255 insertions(+), 11 deletions(-) diff --git a/include/net/lorawan.h b/include/net/lorawan.h index a4db5ee1a8197c..04b16bc1a6c217 100644 --- a/include/net/lorawan.h +++ b/include/net/lorawan.h @@ -110,5 +110,30 @@ int lorawan_join_network(enum lorawan_datarate datarate, */ int lorawan_send(u8_t port, enum lorawan_datarate datarate, u8_t *data, u8_t len, bool confirm, u8_t tries); +/** + * @brief Check if there is uncollected received data from the LoRaWAN network + * + * Check if there is uncolllected received data from the LoRaWAN network + * + * @return 0 or positive number indicating the number of received messages + * waiting to be read from the buffer, or negative errno code if failure + */ +int lorawan_receive_available(); + +/** + * @brief Read uncollected received data from the LoRaWAN network + * + * Read uncollected received data from the LoRaWAN network. This function will + * return each received message only once - each read opration removes the + * oldest downlink measage from the buffer of received unread messages. + * + * @param port Port at which the message arrived + * @param data Data buffer where received data shall be placed + * @param len Size of the \p data buffer available. Messages with + * payloads larger than the buffer will be truncated + * + * @return 0 if successful, negative errno code if failure + */ +int lorawan_receive_read(u8_t *port, u8_t *data, u8_t len); #endif /* ZEPHYR_INCLUDE_NET_LORAWAN_H_ */ diff --git a/samples/lorawan/class_a/src/main.c b/samples/lorawan/class_a/src/main.c index be414332bc14ec..6dd6de47ef917d 100644 --- a/samples/lorawan/class_a/src/main.c +++ b/samples/lorawan/class_a/src/main.c @@ -28,6 +28,7 @@ BUILD_ASSERT(DT_HAS_NODE_STATUS_OKAY(DEFAULT_RADIO_NODE), #define LORAWAN_DEFAULT_DATARATE LORAWAN_DR_0 #define DELAY K_MSEC(5000) +#define DL_MSG_LEN 256 #define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL #include @@ -39,11 +40,15 @@ void main(void) { struct device *lora_dev; struct lorawan_mib_config mib_config; + int ret; + int dl_msgs_no; + int i; + uint8_t port; + uint8_t dl_msg[DL_MSG_LEN] = {'\0'}; u8_t dev_eui[] = LORAWAN_DEV_EUI; u8_t join_eui[] = LORAWAN_JOIN_EUI; u8_t app_eui[] = LORAWAN_APP_EUI; u8_t app_key[] = LORAWAN_APP_KEY; - int ret; lora_dev = device_get_binding(DEFAULT_RADIO); if (!lora_dev) { @@ -75,11 +80,11 @@ void main(void) return; } - LOG_INF("Sending data..."); while (1) { + k_sleep(DELAY); + LOG_INF("Sending data..."); ret = lorawan_send(2, LORAWAN_DEFAULT_DATARATE, data, sizeof(data), true, 1); - /* * Note: The stack may return -EAGAIN if the provided data * length exceeds the maximum possible one for the region and @@ -87,17 +92,30 @@ void main(void) * we'll just continue. */ if (ret == -EAGAIN) { - LOG_ERR("lorawan_send failed: %d. Continuing...", ret); - k_sleep(DELAY); + LOG_ERR("lorawan_send failed: %d. Retrying...", ret); continue; } if (ret < 0) { - LOG_ERR("lorawan_send failed: %d", ret); - return; + LOG_ERR("lorawan_send failed: %d. Retrying...", ret); + continue; } LOG_INF("Data sent!"); - k_sleep(DELAY); + k_sleep(1000); + + dl_msgs_no = lorawan_receive_available(); + if (dl_msgs_no > 0) { + LOG_INF("%d downlink msgs available", dl_msgs_no); + for (i=0; i= 0 ) { + LOG_INF("Msg at port %d of length %d: %s", + port, + ret, + log_strdup(dl_msg)); + } + } + } } -} +} \ No newline at end of file diff --git a/subsys/lorawan/Kconfig b/subsys/lorawan/Kconfig index b9bf82eea2bde3..1f0da2bc867bf4 100644 --- a/subsys/lorawan/Kconfig +++ b/subsys/lorawan/Kconfig @@ -5,7 +5,7 @@ menuconfig LORAWAN bool "LoRaWAN support [EXPERIMENTAL]" - depends on LORA + depends on LORA && RING_BUFFER select HAS_SEMTECH_LORAMAC select HAS_SEMTECH_SOFT_SE help @@ -55,4 +55,11 @@ config LORAMAC_REGION_RU864 endchoice +config LORAWAN_RX_RING_BUFFER_MAX_SIZE_WORDS + int "Downlink Ring Buffer Size in 32-bit words [disfunctional]" + default 0 + help + This value defines the maximum number 32-bit words used in a buffer + for downlink messages. Set to zero to disable usage of a ring buffer. + endif # LORAWAN diff --git a/subsys/lorawan/lorawan.c b/subsys/lorawan/lorawan.c index 8813c4afbb5b7d..2222c05364c8a1 100644 --- a/subsys/lorawan/lorawan.c +++ b/subsys/lorawan/lorawan.c @@ -9,6 +9,10 @@ #include #include +#if CONFIG_LORAWAN_RX_RING_BUFFER_MAX_SIZE_WORDS != 0 +#include +#endif // CONFIG_LORAWAN_RX_RING_BUFFER_MAX_SIZE_WORDS != 0 + #include #ifdef CONFIG_LORAMAC_REGION_AS923 @@ -35,6 +39,12 @@ #error "Atleast one LoRaWAN region should be selected" #endif +#define CEIL_32(x) ((unsigned int)(((((x))+31U)/32U)*32U)) + +#define LORAWAN_PLD_MAX_SIZE 242U +#define LORAWAN_PLD_MAX_SIZE_CEIL_32_B CEIL_32(LORAWAN_PLD_MAX_SIZE) +#define LORAWAN_PLD_MAX_SIZE_CEIL_32 (LORAWAN_PLD_MAX_SIZE_CEIL_32_B/4) + #define LOG_LEVEL CONFIG_LORAWAN_LOG_LEVEL #include LOG_MODULE_REGISTER(lorawan); @@ -45,6 +55,29 @@ K_SEM_DEFINE(mcps_confirm_sem, 0, 1); K_MUTEX_DEFINE(lorawan_join_mutex); K_MUTEX_DEFINE(lorawan_send_mutex); +#if CONFIG_LORAWAN_RX_RING_BUFFER_MAX_SIZE_WORDS != 0 + +#define RX_RING_BUF_SIZE_WORDS (CONFIG_LORAWAN_RX_BUFFER_MAX_SIZE_WORDS) +struct rx_ring_buf { + struct ring_buf rb; + u32_t buffer[RX_RING_BUF_SIZE_WORDS]; +}; + +struct rx_ring_buf rx_buf; + +#else + +uint8_t rx_buffer[LORAWAN_PLD_MAX_SIZE]; +uint8_t rx_buffer_stored_len; +uint8_t rx_buffer_port; + +#endif // CONFIG_LORAWAN_RX_RING_BUFFER_MAX_SIZE_WORDS != 0 + +static uint16_t rx_buf_avail_elem; +static uint16_t rx_buf_discarded_elem; +static uint32_t payload_tmp[LORAWAN_PLD_MAX_SIZE_CEIL_32]; +static uint8_t payload_tmp_size = LORAWAN_PLD_MAX_SIZE_CEIL_32; + const char *status2str(int status) { switch (status) { @@ -204,6 +237,132 @@ static LoRaMacEventInfoStatus_t last_mlme_confirm_status; static LoRaMacEventInfoStatus_t last_mcps_indication_status; static LoRaMacEventInfoStatus_t last_mlme_indication_status; +/** + * @brief Read a downlink message from the ring buffer. + * + * Read a downlink message from the ring buffer and place its payload in the + * provided container. The message is consumed in the process. If the container + * is shorter than the available payload any excess data will be discarded and + * only as much of the payload as possible will be output. + * + * @param port Container for the message's port + * @param payload Container for the message's payload + * @param len Lenght in bytes of the \p payload container. + * + * @retval non-negative integer indicating the number of read payload bytes + * successfully copied to the provided container + * @retval -EAGAIN No messages in the buffer. + */ +static int rx_buf_get(u8_t *port, u8_t *payload, u16_t len) +{ + int len_to_copy; + +#if CONFIG_LORAWAN_RX_RING_BUFFER_MAX_SIZE_WORDS != 0 + int ret; + u16_t rx_buffer_stored_len; + + payload_tmp_size = LORAWAN_PLD_MAX_SIZE_CEIL_32; + ret = ring_buf_item_get(&rx_buf.rb, &rx_buffer_stored_len, + port, payload_tmp, &payload_tmp_size); + payload_tmp_size = LORAWAN_PLD_MAX_SIZE_CEIL_32; + + if ( ret < 0 ) { + return ret; + } + + rx_buf_avail_elem--; + len_to_copy = rx_buffer_stored_len < len ? rx_buffer_stored_len : len; + memcpy(payload, payload_tmp, len_to_copy); +#else + len_to_copy = rx_buffer_stored_len < len ? rx_buffer_stored_len : len; + rx_buf_avail_elem = 0; + *port = rx_buffer_port; + rx_buffer_port = 0; + memcpy(payload, rx_buffer, len_to_copy); +#endif + return len_to_copy; +} + +/** + * @brief Put a downlink message into the ring buffer. + * + * Put a downlink message intothe ring buffer. If the buffer doesn not have + * sufficient free space oldest messages will be discarded for as long as + * possible until sufficient space is available. + * + * @param port Message's port number + * @param payload Container for the message's payload + * @param len \p payload size in bytes. + * + * @retval 0 On success + * @retval -EMSGSIZE Message too large for the buffer +*/ +static int rx_buf_put(u8_t port, u8_t *payload, u16_t len) +{ + int ret; + +#if CONFIG_LORAWAN_RX_RING_BUFFER_MAX_SIZE_WORDS != 0 + memcpy(payload_tmp, payload, len); + + ret = ring_buf_item_put(&rx_buf.rb, len, port, payload_tmp, + CEIL_32(len)/4); + while (ret == -EMSGSIZE) { + /* not enough room for the data item -> remove the oldes one */ + payload_tmp_size = LORAWAN_PLD_MAX_SIZE_CEIL_32; + ret = ring_buf_item_get(&rx_buf.rb, NULL, NULL, payload_tmp, + &payload_tmp_size); + payload_tmp_size = LORAWAN_PLD_MAX_SIZE_CEIL_32; + if (ret == -EAGAIN) { + /* Buffer doesn't have any items -> the one we are + * trying to add won't fit any way. */ + return -EMSGSIZE; + } + rx_buf_discarded_elem++; + + memcpy(payload_tmp, payload, len); + + ret = ring_buf_item_put(&rx_buf.rb, len, port, payload_tmp, + CEIL_32(len)/4); + } + + rx_buf_avail_elem++; +#else + memcpy(rx_buffer, payload, len); + rx_buf_avail_elem = 1; + rx_buffer_port = port; + rx_buffer_stored_len = len; +#endif + + return 0; +} + +/** + * @brief Return the number of downlink messages available. + * + * Return the number of downlink messages available in the buffer + * + * @retval number of downlink messages available in the buffer + */ +static int rx_buf_avail() +{ + return rx_buf_avail_elem; +} + +/** + * @brief Return the number of discarded downlink messages since last call. + * + * Returns the number of discarded downlink messages since this funcion has been + * called last. + * + * @retval number of discarded characters + */ +static int rx_buf_discarded() +{ + int retval = rx_buf_discarded_elem; + rx_buf_discarded_elem = 0; + return retval; +} + static void OnMacProcessNotify(void) { LoRaMacProcess(); @@ -237,15 +396,28 @@ static void McpsIndication(McpsIndication_t *mcpsIndication) /* TODO: Check MCPS Indication type */ if (mcpsIndication->RxData == true) { + LOG_DBG("RxData set"); if (mcpsIndication->BufferSize != 0) { - LOG_DBG("Rx Data: %s", + LOG_DBG("Rx %dB Data: %s", + mcpsIndication->BufferSize, log_strdup(mcpsIndication->Buffer)); + + rx_buf_put(mcpsIndication->Port, mcpsIndication->Buffer, + mcpsIndication->BufferSize); } } last_mcps_indication_status = mcpsIndication->Status; /* TODO: Compliance test based on FPort value*/ + LOG_DBG("Multicast: %d (0x%x)", mcpsIndication->Multicast, mcpsIndication->Multicast); + LOG_DBG("Port: %d (0x%x)", mcpsIndication->Port, mcpsIndication->Port); + LOG_DBG("FramePending: %d (0x%x)", mcpsIndication->FramePending, mcpsIndication->FramePending); + LOG_DBG("RxData: %d (0x%x)", mcpsIndication->RxData, mcpsIndication->RxData); + LOG_DBG("Rssi: %d (0x%x)", mcpsIndication->Rssi, mcpsIndication->Rssi); + LOG_DBG("Snr: %d (0x%x)", mcpsIndication->Snr, mcpsIndication->Snr); + LOG_DBG("AckReceived: %d (0x%x)", mcpsIndication->AckReceived, mcpsIndication->AckReceived); + LOG_DBG("DeviceTimeAnsReceived: %d (0x%x)", mcpsIndication->DeviceTimeAnsReceived, mcpsIndication->DeviceTimeAnsReceived); } static void MlmeConfirm(MlmeConfirm_t *mlmeConfirm) @@ -452,10 +624,24 @@ int lorawan_send(u8_t port, enum lorawan_datarate datarate, u8_t *data, return ret; } +int lorawan_receive_available() +{ + return rx_buf_avail(); +} + +int lorawan_receive_read(u8_t *port, u8_t *data, u8_t len) +{ + return rx_buf_get(port, data, len); +} + static int lorawan_init(struct device *dev) { LoRaMacStatus_t status; + uint8_t test[] = {0xaa, 0x55, 0x11}; + uint32_t test32; + memcpy(&test32, test, sizeof(uint32_t)); + macPrimitives.MacMcpsConfirm = McpsConfirm; macPrimitives.MacMcpsIndication = McpsIndication; macPrimitives.MacMlmeConfirm = MlmeConfirm; @@ -473,6 +659,14 @@ static int lorawan_init(struct device *dev) return -EINVAL; } + +#if CONFIG_LORAWAN_RX_RING_BUFFER_MAX_SIZE_WORDS != 0 + ring_buf_init(&rx_buf.rb, sizeof(rx_buf.buffer), rx_buf.buffer); +#endif + + rx_buf_avail_elem = 0; + rx_buf_discarded_elem = 0; + LoRaMacStart(); LOG_DBG("LoRaMAC Initialized"); From 2c91d4cd509e8fd95b99f71db566e78bf0fc80bd Mon Sep 17 00:00:00 2001 From: Kuba Sanak Date: Wed, 3 Jun 2020 17:22:35 +0100 Subject: [PATCH 20/26] Reshuffled Kconfig to make usage of ring buffer optional (as it causes crashes). Also added board overlay file for Feather M). --- .../adafruit_feather_m0_basic_proto.overlay | 27 +++++++++++++++++++ samples/lorawan/class_a/prj.conf | 1 + subsys/lorawan/Kconfig | 20 +++++++++++--- subsys/lorawan/lorawan.c | 20 ++++++-------- 4 files changed, 52 insertions(+), 16 deletions(-) create mode 100644 samples/lorawan/class_a/boards/adafruit_feather_m0_basic_proto.overlay diff --git a/samples/lorawan/class_a/boards/adafruit_feather_m0_basic_proto.overlay b/samples/lorawan/class_a/boards/adafruit_feather_m0_basic_proto.overlay new file mode 100644 index 00000000000000..8d4c778000c7ca --- /dev/null +++ b/samples/lorawan/class_a/boards/adafruit_feather_m0_basic_proto.overlay @@ -0,0 +1,27 @@ +/* + * This overlay suits Adafrut Feather M0 LoRa edition. + * + * Copyritght (c) 2020: R3-IoT Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&sercom4 { + status = "okay"; + compatible = "atmel,sam0-spi"; + + cs-gpios = <&porta 6 0>; + + dipo = <0>; + dopo = <1>; + + sx1276@0 { + compatible = "semtech,sx1276"; + reg = <0>; + label = "sx1276"; + reset-gpios = <&portb 13 0>; + dio-gpios = <&porta 9 0>; + + spi-max-frequency = <1000000>; + }; +}; \ No newline at end of file diff --git a/samples/lorawan/class_a/prj.conf b/samples/lorawan/class_a/prj.conf index 62263c39a03d04..271a0f1f966108 100644 --- a/samples/lorawan/class_a/prj.conf +++ b/samples/lorawan/class_a/prj.conf @@ -5,3 +5,4 @@ CONFIG_NEWLIB_LIBC=y CONFIG_LORA=y CONFIG_LORA_SX1276=y CONFIG_LORAWAN=y +CONFIG_LORAWAN_USE_RX_RING_BUFFER=n \ No newline at end of file diff --git a/subsys/lorawan/Kconfig b/subsys/lorawan/Kconfig index 1f0da2bc867bf4..e7e8916e08f4c4 100644 --- a/subsys/lorawan/Kconfig +++ b/subsys/lorawan/Kconfig @@ -5,7 +5,7 @@ menuconfig LORAWAN bool "LoRaWAN support [EXPERIMENTAL]" - depends on LORA && RING_BUFFER + depends on LORA select HAS_SEMTECH_LORAMAC select HAS_SEMTECH_SOFT_SE help @@ -55,11 +55,23 @@ config LORAMAC_REGION_RU864 endchoice +config LORAWAN_USE_RX_RING_BUFFER + bool "Use Ring Buffer for Downlink [NOT YET FUNCTIONAL]" + default False + depends on RING_BUFFER + help + This value enables a ring buffer used for storing incoming downlink + messages instead of only having the latest message available. + +if LORAWAN_USE_RX_RING_BUFFER + config LORAWAN_RX_RING_BUFFER_MAX_SIZE_WORDS - int "Downlink Ring Buffer Size in 32-bit words [disfunctional]" - default 0 + int "Downlink Ring Buffer Size in 32-bit words [NOT YET FUNCTIONAL]" + default 8 help This value defines the maximum number 32-bit words used in a buffer - for downlink messages. Set to zero to disable usage of a ring buffer. + for downlink messages. + +endif # LORAWAN_USE_RX_RING_BUFFER endif # LORAWAN diff --git a/subsys/lorawan/lorawan.c b/subsys/lorawan/lorawan.c index 2222c05364c8a1..39db386c3b5a07 100644 --- a/subsys/lorawan/lorawan.c +++ b/subsys/lorawan/lorawan.c @@ -9,9 +9,9 @@ #include #include -#if CONFIG_LORAWAN_RX_RING_BUFFER_MAX_SIZE_WORDS != 0 +#if CONFIG_LORAWAN_USE_RX_RING_BUFFER #include -#endif // CONFIG_LORAWAN_RX_RING_BUFFER_MAX_SIZE_WORDS != 0 +#endif //def CONFIG_LORAWAN_USE_RX_RING_BUFFER #include @@ -55,9 +55,9 @@ K_SEM_DEFINE(mcps_confirm_sem, 0, 1); K_MUTEX_DEFINE(lorawan_join_mutex); K_MUTEX_DEFINE(lorawan_send_mutex); -#if CONFIG_LORAWAN_RX_RING_BUFFER_MAX_SIZE_WORDS != 0 +#if CONFIG_LORAWAN_USE_RX_RING_BUFFER -#define RX_RING_BUF_SIZE_WORDS (CONFIG_LORAWAN_RX_BUFFER_MAX_SIZE_WORDS) +#define RX_RING_BUF_SIZE_WORDS (CONFIG_LORAWAN_RX_RING_BUFFER_MAX_SIZE_WORDS) struct rx_ring_buf { struct ring_buf rb; u32_t buffer[RX_RING_BUF_SIZE_WORDS]; @@ -71,7 +71,7 @@ uint8_t rx_buffer[LORAWAN_PLD_MAX_SIZE]; uint8_t rx_buffer_stored_len; uint8_t rx_buffer_port; -#endif // CONFIG_LORAWAN_RX_RING_BUFFER_MAX_SIZE_WORDS != 0 +#endif //def CONFIG_LORAWAN_USE_RX_RING_BUFFER static uint16_t rx_buf_avail_elem; static uint16_t rx_buf_discarded_elem; @@ -257,7 +257,7 @@ static int rx_buf_get(u8_t *port, u8_t *payload, u16_t len) { int len_to_copy; -#if CONFIG_LORAWAN_RX_RING_BUFFER_MAX_SIZE_WORDS != 0 +#if CONFIG_LORAWAN_USE_RX_RING_BUFFER int ret; u16_t rx_buffer_stored_len; @@ -301,7 +301,7 @@ static int rx_buf_put(u8_t port, u8_t *payload, u16_t len) { int ret; -#if CONFIG_LORAWAN_RX_RING_BUFFER_MAX_SIZE_WORDS != 0 +#if CONFIG_LORAWAN_USE_RX_RING_BUFFER memcpy(payload_tmp, payload, len); ret = ring_buf_item_put(&rx_buf.rb, len, port, payload_tmp, @@ -638,10 +638,6 @@ static int lorawan_init(struct device *dev) { LoRaMacStatus_t status; - uint8_t test[] = {0xaa, 0x55, 0x11}; - uint32_t test32; - memcpy(&test32, test, sizeof(uint32_t)); - macPrimitives.MacMcpsConfirm = McpsConfirm; macPrimitives.MacMcpsIndication = McpsIndication; macPrimitives.MacMlmeConfirm = MlmeConfirm; @@ -660,7 +656,7 @@ static int lorawan_init(struct device *dev) } -#if CONFIG_LORAWAN_RX_RING_BUFFER_MAX_SIZE_WORDS != 0 +#if CONFIG_LORAWAN_USE_RX_RING_BUFFER ring_buf_init(&rx_buf.rb, sizeof(rx_buf.buffer), rx_buf.buffer); #endif From 2f677e73cb9bc568dcc1b17a922160162a3733dc Mon Sep 17 00:00:00 2001 From: Kuba Sanak Date: Wed, 3 Jun 2020 17:43:12 +0100 Subject: [PATCH 21/26] Get rid of warnings. Signed-off-by: Kuba Sanak --- subsys/lorawan/lorawan.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/subsys/lorawan/lorawan.c b/subsys/lorawan/lorawan.c index 39db386c3b5a07..ce01e70fbb9741 100644 --- a/subsys/lorawan/lorawan.c +++ b/subsys/lorawan/lorawan.c @@ -75,9 +75,10 @@ uint8_t rx_buffer_port; static uint16_t rx_buf_avail_elem; static uint16_t rx_buf_discarded_elem; +#if CONFIG_LORAWAN_USE_RX_RING_BUFFER static uint32_t payload_tmp[LORAWAN_PLD_MAX_SIZE_CEIL_32]; static uint8_t payload_tmp_size = LORAWAN_PLD_MAX_SIZE_CEIL_32; - +#endif const char *status2str(int status) { switch (status) { @@ -299,9 +300,9 @@ static int rx_buf_get(u8_t *port, u8_t *payload, u16_t len) */ static int rx_buf_put(u8_t port, u8_t *payload, u16_t len) { - int ret; #if CONFIG_LORAWAN_USE_RX_RING_BUFFER + int ret; memcpy(payload_tmp, payload, len); ret = ring_buf_item_put(&rx_buf.rb, len, port, payload_tmp, From 65903c1ded76acddff9d4f456891bf5fbdcaf75b Mon Sep 17 00:00:00 2001 From: Kuba Sanak Date: Tue, 9 Jun 2020 19:16:02 +0100 Subject: [PATCH 22/26] Update with main lorawan-upstream branch Signed-off-by: Kuba Sanak --- drivers/lora/sx1276.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/drivers/lora/sx1276.c b/drivers/lora/sx1276.c index d9782281eebbaa..05e5306659dcc3 100644 --- a/drivers/lora/sx1276.c +++ b/drivers/lora/sx1276.c @@ -144,7 +144,6 @@ static void timer_callback(struct k_timer *_timer) TimerIrqHandler(); } -#endif void RtcSetAlarm(u32_t timeout) { @@ -158,7 +157,6 @@ u32_t RtcSetTimerContext(void) return saved_time; } - /* For us, 1 tick = 1 milli second. So no need to do any conversion here */ u32_t RtcGetTimerContext(void) { @@ -209,28 +207,6 @@ void RtcBkupRead(u32_t *data0, uint32_t *data1) *data1 = dev_data.backup_reg[1]; } -u32_t RtcGetCalendarTime(uint16_t *milliseconds) -{ - u32_t now = k_uptime_get_32(); - - *milliseconds = now; - - /* Return in seconds */ - return now / MSEC_PER_SEC; -} - -void RtcBkupWrite(u32_t data0, uint32_t data1) -{ - dev_data.backup_reg[0] = data0; - dev_data.backup_reg[1] = data1; -} - -void RtcBkupRead(u32_t *data0, uint32_t *data1) -{ - *data0 = dev_data.backup_reg[0]; - *data1 = dev_data.backup_reg[1]; -} - static void sx1276_irq_callback(struct device *dev, struct gpio_callback *cb, u32_t pins) { From 708392502c400d7cb28b1c8036624fbe6530a2a6 Mon Sep 17 00:00:00 2001 From: Kuba Sanak Date: Fri, 15 May 2020 14:23:39 +0100 Subject: [PATCH 23/26] Revert "boards: arm: adafruit_feather_m0_basic_proto: Set flash offset" This reverts commit 61aff640f90a2c33d603b08ef7143758dc53cbe7. Signed-off-by: Kuba Sanak --- boards/arm/adafruit_feather_m0_basic_proto/board.cmake | 2 -- 1 file changed, 2 deletions(-) diff --git a/boards/arm/adafruit_feather_m0_basic_proto/board.cmake b/boards/arm/adafruit_feather_m0_basic_proto/board.cmake index cb8bfd7c096599..05695a86d92a9a 100644 --- a/boards/arm/adafruit_feather_m0_basic_proto/board.cmake +++ b/boards/arm/adafruit_feather_m0_basic_proto/board.cmake @@ -2,6 +2,4 @@ # # SPDX-License-Identifier: Apache-2.0 -board_runner_args(bossac "--offset=0x2000") - include(${ZEPHYR_BASE}/boards/common/bossac.board.cmake) From 1fae38a6799fedf24c2a252c21539db960aaf1aa Mon Sep 17 00:00:00 2001 From: Kuba Sanak Date: Thu, 11 Jun 2020 13:52:35 +0100 Subject: [PATCH 24/26] Make the Feather M0 overlay work with new device referencing style Signed-off-by: Kuba Sanak --- .../boards/adafruit_feather_m0_basic_proto.overlay | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/samples/lorawan/class_a/boards/adafruit_feather_m0_basic_proto.overlay b/samples/lorawan/class_a/boards/adafruit_feather_m0_basic_proto.overlay index 8d4c778000c7ca..490b65735b52c6 100644 --- a/samples/lorawan/class_a/boards/adafruit_feather_m0_basic_proto.overlay +++ b/samples/lorawan/class_a/boards/adafruit_feather_m0_basic_proto.overlay @@ -15,7 +15,7 @@ dipo = <0>; dopo = <1>; - sx1276@0 { + lora: sx1276@0 { compatible = "semtech,sx1276"; reg = <0>; label = "sx1276"; @@ -24,4 +24,11 @@ spi-max-frequency = <1000000>; }; -}; \ No newline at end of file +}; + +/ { + + aliases { + lora0 = &lora; + }; +}; From 4b2d7f352fe1546364df6938c150f5df44d877b6 Mon Sep 17 00:00:00 2001 From: Kuba Sanak Date: Thu, 11 Jun 2020 13:57:34 +0100 Subject: [PATCH 25/26] Fix problems with ring buffer use. Signed-off-by: Kuba Sanak --- samples/lorawan/class_a/prj.conf | 5 ++- samples/lorawan/class_a/src/main.c | 10 ++++-- subsys/lorawan/Kconfig | 13 ++++--- subsys/lorawan/lorawan.c | 57 +++++++++++++++++------------- 4 files changed, 52 insertions(+), 33 deletions(-) diff --git a/samples/lorawan/class_a/prj.conf b/samples/lorawan/class_a/prj.conf index 271a0f1f966108..06c22c37928507 100644 --- a/samples/lorawan/class_a/prj.conf +++ b/samples/lorawan/class_a/prj.conf @@ -5,4 +5,7 @@ CONFIG_NEWLIB_LIBC=y CONFIG_LORA=y CONFIG_LORA_SX1276=y CONFIG_LORAWAN=y -CONFIG_LORAWAN_USE_RX_RING_BUFFER=n \ No newline at end of file +CONFIG_LORAWAN_USE_RX_RING_BUFFER=n + +CONFIG_MAIN_STACK_SIZE=2048 +CONFIG_LORAWAN_USE_RX_RING_BUFFER=y diff --git a/samples/lorawan/class_a/src/main.c b/samples/lorawan/class_a/src/main.c index 6dd6de47ef917d..3b48a138051c5f 100644 --- a/samples/lorawan/class_a/src/main.c +++ b/samples/lorawan/class_a/src/main.c @@ -73,11 +73,17 @@ void main(void) return; } + ret = -1; + + while (ret < 0) { LOG_INF("Joining network over OTAA"); ret = lorawan_join_network(LORAWAN_DEFAULT_DATARATE, LORAWAN_ACT_OTAA); if (ret < 0) { LOG_ERR("lorawan_join_network failed: %d", ret); - return; + k_sleep(K_MSEC(3000)); + } else { + break; + } } while (1) { @@ -105,7 +111,7 @@ void main(void) k_sleep(1000); dl_msgs_no = lorawan_receive_available(); - if (dl_msgs_no > 0) { + if (dl_msgs_no >= 0) { LOG_INF("%d downlink msgs available", dl_msgs_no); for (i=0; i @@ -67,7 +70,7 @@ struct rx_ring_buf rx_buf; #else -uint8_t rx_buffer[LORAWAN_PLD_MAX_SIZE]; +uint8_t rx_buffer[LORAWAN_PLD_MAX_SIZE_BYTES]; uint8_t rx_buffer_stored_len; uint8_t rx_buffer_port; @@ -76,8 +79,8 @@ uint8_t rx_buffer_port; static uint16_t rx_buf_avail_elem; static uint16_t rx_buf_discarded_elem; #if CONFIG_LORAWAN_USE_RX_RING_BUFFER -static uint32_t payload_tmp[LORAWAN_PLD_MAX_SIZE_CEIL_32]; -static uint8_t payload_tmp_size = LORAWAN_PLD_MAX_SIZE_CEIL_32; +static uint32_t payload_tmp[LORAWAN_PLD_MAX_SIZE_WORDS]; +static uint8_t payload_tmp_size = LORAWAN_PLD_MAX_SIZE_WORDS; #endif const char *status2str(int status) { @@ -262,10 +265,10 @@ static int rx_buf_get(u8_t *port, u8_t *payload, u16_t len) int ret; u16_t rx_buffer_stored_len; - payload_tmp_size = LORAWAN_PLD_MAX_SIZE_CEIL_32; + payload_tmp_size = LORAWAN_PLD_MAX_SIZE_WORDS; ret = ring_buf_item_get(&rx_buf.rb, &rx_buffer_stored_len, port, payload_tmp, &payload_tmp_size); - payload_tmp_size = LORAWAN_PLD_MAX_SIZE_CEIL_32; + payload_tmp_size = LORAWAN_PLD_MAX_SIZE_WORDS; if ( ret < 0 ) { return ret; @@ -300,35 +303,41 @@ static int rx_buf_get(u8_t *port, u8_t *payload, u16_t len) */ static int rx_buf_put(u8_t port, u8_t *payload, u16_t len) { - #if CONFIG_LORAWAN_USE_RX_RING_BUFFER int ret; + u16_t type_tmp; + u8_t val_tmp; memcpy(payload_tmp, payload, len); ret = ring_buf_item_put(&rx_buf.rb, len, port, payload_tmp, - CEIL_32(len)/4); + SIZE_BYTES_TO_WORDS(len)); + while (ret == -EMSGSIZE) { /* not enough room for the data item -> remove the oldes one */ - payload_tmp_size = LORAWAN_PLD_MAX_SIZE_CEIL_32; - ret = ring_buf_item_get(&rx_buf.rb, NULL, NULL, payload_tmp, + payload_tmp_size = LORAWAN_PLD_MAX_SIZE_WORDS; + ret = ring_buf_item_get(&rx_buf.rb, &type_tmp, &val_tmp, payload_tmp, &payload_tmp_size); - payload_tmp_size = LORAWAN_PLD_MAX_SIZE_CEIL_32; if (ret == -EAGAIN) { /* Buffer doesn't have any items -> the one we are * trying to add won't fit any way. */ return -EMSGSIZE; } + LOG_DBG("Discarded ring buff elemem size %d", payload_tmp_size); + rx_buf_avail_elem--; rx_buf_discarded_elem++; + payload_tmp_size = LORAWAN_PLD_MAX_SIZE_WORDS; memcpy(payload_tmp, payload, len); ret = ring_buf_item_put(&rx_buf.rb, len, port, payload_tmp, - CEIL_32(len)/4); + SIZE_BYTES_TO_WORDS(len)); } + rx_buf_avail_elem++; #else memcpy(rx_buffer, payload, len); + rx_buf_avail_elem = 1; rx_buffer_port = port; rx_buffer_stored_len = len; @@ -387,6 +396,7 @@ static void McpsConfirm(McpsConfirm_t *mcpsConfirm) static void McpsIndication(McpsIndication_t *mcpsIndication) { + int ret; LOG_DBG("Received McpsIndication %d", mcpsIndication->McpsIndication); if (mcpsIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK) { @@ -403,22 +413,19 @@ static void McpsIndication(McpsIndication_t *mcpsIndication) mcpsIndication->BufferSize, log_strdup(mcpsIndication->Buffer)); - rx_buf_put(mcpsIndication->Port, mcpsIndication->Buffer, - mcpsIndication->BufferSize); + ret = rx_buf_put(mcpsIndication->Port, + mcpsIndication->Buffer, + mcpsIndication->BufferSize); + if (ret == -EMSGSIZE) { + LOG_WRN("Rx buff too small for DL size %d", + mcpsIndication->BufferSize); + } } } last_mcps_indication_status = mcpsIndication->Status; /* TODO: Compliance test based on FPort value*/ - LOG_DBG("Multicast: %d (0x%x)", mcpsIndication->Multicast, mcpsIndication->Multicast); - LOG_DBG("Port: %d (0x%x)", mcpsIndication->Port, mcpsIndication->Port); - LOG_DBG("FramePending: %d (0x%x)", mcpsIndication->FramePending, mcpsIndication->FramePending); - LOG_DBG("RxData: %d (0x%x)", mcpsIndication->RxData, mcpsIndication->RxData); - LOG_DBG("Rssi: %d (0x%x)", mcpsIndication->Rssi, mcpsIndication->Rssi); - LOG_DBG("Snr: %d (0x%x)", mcpsIndication->Snr, mcpsIndication->Snr); - LOG_DBG("AckReceived: %d (0x%x)", mcpsIndication->AckReceived, mcpsIndication->AckReceived); - LOG_DBG("DeviceTimeAnsReceived: %d (0x%x)", mcpsIndication->DeviceTimeAnsReceived, mcpsIndication->DeviceTimeAnsReceived); } static void MlmeConfirm(MlmeConfirm_t *mlmeConfirm) @@ -658,7 +665,7 @@ static int lorawan_init(struct device *dev) #if CONFIG_LORAWAN_USE_RX_RING_BUFFER - ring_buf_init(&rx_buf.rb, sizeof(rx_buf.buffer), rx_buf.buffer); + ring_buf_init(&rx_buf.rb, RX_RING_BUF_SIZE_WORDS, rx_buf.buffer); #endif rx_buf_avail_elem = 0; From 078ff36896837df46cee4fd5877a92bd0453d088 Mon Sep 17 00:00:00 2001 From: Kuba Sanak Date: Thu, 11 Jun 2020 16:03:08 +0100 Subject: [PATCH 26/26] Fix build warnings. Signed-off-by: Kuba Sanak --- include/net/lorawan.h | 10 ++++++++++ samples/lorawan/class_a/src/main.c | 2 +- subsys/lorawan/lorawan.c | 7 +++++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/include/net/lorawan.h b/include/net/lorawan.h index 04b16bc1a6c217..bce2e81b073064 100644 --- a/include/net/lorawan.h +++ b/include/net/lorawan.h @@ -120,6 +120,16 @@ int lorawan_send(u8_t port, enum lorawan_datarate datarate, */ int lorawan_receive_available(); +/** + * @brief Check if any uncollected received incoming data was discarded + * + * Check if any uncollected received incoming data was discarded due to + * insufficient free space in the RX buffer. + * + * @return 0 or positive number indicating the number of discarded messages + */ +int lorawan_receive_discarded(); + /** * @brief Read uncollected received data from the LoRaWAN network * diff --git a/samples/lorawan/class_a/src/main.c b/samples/lorawan/class_a/src/main.c index 3b48a138051c5f..68f540b5d5b70f 100644 --- a/samples/lorawan/class_a/src/main.c +++ b/samples/lorawan/class_a/src/main.c @@ -108,7 +108,7 @@ void main(void) } LOG_INF("Data sent!"); - k_sleep(1000); + k_sleep(K_MSEC(1000)); dl_msgs_no = lorawan_receive_available(); if (dl_msgs_no >= 0) { diff --git a/subsys/lorawan/lorawan.c b/subsys/lorawan/lorawan.c index 2903c1cb8d94e7..dcd6de9773de07 100644 --- a/subsys/lorawan/lorawan.c +++ b/subsys/lorawan/lorawan.c @@ -39,8 +39,6 @@ #error "Atleast one LoRaWAN region should be selected" #endif -#define MIN(a, b) ((a)<=(b)?(a):(b)) - #define CEIL_32(x) ((unsigned int)(((((x))+31U)/32U)*32U)) #define SIZE_BYTES_TO_WORDS(x) (CEIL_32(x*8)/32) @@ -642,6 +640,11 @@ int lorawan_receive_read(u8_t *port, u8_t *data, u8_t len) return rx_buf_get(port, data, len); } +int lorawan_receive_discarded() +{ + return rx_buf_discarded(); +} + static int lorawan_init(struct device *dev) { LoRaMacStatus_t status;