From 97eb2565ead985f844b064204628c6d1db01cebc Mon Sep 17 00:00:00 2001 From: Craig Peacock Date: Thu, 19 Oct 2023 17:16:52 +1100 Subject: [PATCH] Add LoRaWAN Network Time Service Example --- LoRaWAN_NetworkTime/.gitignore | 1 + LoRaWAN_NetworkTime/CMakeLists.txt | 9 + .../boards/lemon_iot_lora_rak3172.conf | 1 + LoRaWAN_NetworkTime/prj.conf | 41 +++++ LoRaWAN_NetworkTime/src/lorawan.h | 9 + LoRaWAN_NetworkTime/src/main.c | 162 ++++++++++++++++++ 6 files changed, 223 insertions(+) create mode 100644 LoRaWAN_NetworkTime/.gitignore create mode 100644 LoRaWAN_NetworkTime/CMakeLists.txt create mode 100644 LoRaWAN_NetworkTime/boards/lemon_iot_lora_rak3172.conf create mode 100644 LoRaWAN_NetworkTime/prj.conf create mode 100644 LoRaWAN_NetworkTime/src/lorawan.h create mode 100644 LoRaWAN_NetworkTime/src/main.c diff --git a/LoRaWAN_NetworkTime/.gitignore b/LoRaWAN_NetworkTime/.gitignore new file mode 100644 index 0000000..28ed25c --- /dev/null +++ b/LoRaWAN_NetworkTime/.gitignore @@ -0,0 +1 @@ +/build* \ No newline at end of file diff --git a/LoRaWAN_NetworkTime/CMakeLists.txt b/LoRaWAN_NetworkTime/CMakeLists.txt new file mode 100644 index 0000000..e3a2dd9 --- /dev/null +++ b/LoRaWAN_NetworkTime/CMakeLists.txt @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(LoRaWAN) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/LoRaWAN_NetworkTime/boards/lemon_iot_lora_rak3172.conf b/LoRaWAN_NetworkTime/boards/lemon_iot_lora_rak3172.conf new file mode 100644 index 0000000..97b3ee9 --- /dev/null +++ b/LoRaWAN_NetworkTime/boards/lemon_iot_lora_rak3172.conf @@ -0,0 +1 @@ +CONFIG_LORA_STM32WL_SUBGHZ_RADIO=y \ No newline at end of file diff --git a/LoRaWAN_NetworkTime/prj.conf b/LoRaWAN_NetworkTime/prj.conf new file mode 100644 index 0000000..6731359 --- /dev/null +++ b/LoRaWAN_NetworkTime/prj.conf @@ -0,0 +1,41 @@ +CONFIG_SERIAL=y +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y +CONFIG_CONSOLE_SUBSYS=y +CONFIG_CONSOLE_GETLINE=y + +# Use Segger RTT Console +#CONFIG_RTT_CONSOLE=y +#CONFIG_USE_SEGGER_RTT=y + +CONFIG_LOG=y + +CONFIG_SPI=y +CONFIG_I2C=y +#CONFIG_LOG_DEFAULT_LEVEL=4 +#CONFIG_LOG_BACKEND_UART=y + +CONFIG_NEWLIB_LIBC=y +CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y +CONFIG_CBPRINTF_FP_SUPPORT=y + +CONFIG_LORA=y +CONFIG_LORA_LOG_LEVEL_DBG=n + +CONFIG_LORAWAN=y +CONFIG_LORAWAN_LOG_LEVEL_DBG=y +CONFIG_LORAMAC_REGION_AU915=y + +CONFIG_MAIN_STACK_SIZE=2048 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 + +CONFIG_FLASH=y +CONFIG_FLASH_PAGE_LAYOUT=y +CONFIG_NVS=y +CONFIG_MPU_ALLOW_FLASH_WRITE=y + +CONFIG_TEST_RANDOM_GENERATOR=y + +CONFIG_LORAWAN_SERVICES=y +CONFIG_LORAWAN_SERVICES_LOG_LEVEL_DBG=y +CONFIG_LORAWAN_APP_CLOCK_SYNC=y \ No newline at end of file diff --git a/LoRaWAN_NetworkTime/src/lorawan.h b/LoRaWAN_NetworkTime/src/lorawan.h new file mode 100644 index 0000000..1caf974 --- /dev/null +++ b/LoRaWAN_NetworkTime/src/lorawan.h @@ -0,0 +1,9 @@ +/* + * LoRaWAN sample application + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define LORAWAN_DEV_EUI { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } // MSB Format! +#define LORAWAN_JOIN_EUI { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } // MSB Format! +#define LORAWAN_APP_KEY { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } \ No newline at end of file diff --git a/LoRaWAN_NetworkTime/src/main.c b/LoRaWAN_NetworkTime/src/main.c new file mode 100644 index 0000000..44bbe97 --- /dev/null +++ b/LoRaWAN_NetworkTime/src/main.c @@ -0,0 +1,162 @@ + +/* + * LoRaWAN Network Time Example + * + * Copyright (c) 2023 Craig Peacock + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include "lorawan.h" + +#define LOG_LEVEL CONFIG_LOG_DBG_LEVEL +#include +LOG_MODULE_REGISTER(main); + +static void dl_callback(uint8_t port, bool data_pending, int16_t rssi, int8_t snr, uint8_t len, const uint8_t *data) +{ + LOG_INF("Port %d, Pending %d, RSSI %ddB, SNR %ddBm", port, data_pending, rssi, snr); + if (data) { + LOG_HEXDUMP_INF(data, len, "Payload: "); + } +} + +static void lorwan_datarate_changed(enum lorawan_datarate dr) +{ + uint8_t unused, max_size; + + lorawan_get_payload_sizes(&unused, &max_size); + LOG_INF("New Datarate: DR_%d, Max Payload %d", dr, max_size); +} + +int main(void) +{ + const struct device *lora_dev; + + struct lorawan_join_config join_cfg; + uint16_t dev_nonce = 0; + + uint8_t dev_eui[] = LORAWAN_DEV_EUI; + uint8_t join_eui[] = LORAWAN_JOIN_EUI; + uint8_t app_key[] = LORAWAN_APP_KEY; + + uint32_t gps_time; + time_t unix_time; + struct tm timeinfo; + char buf[32]; + + int ret; + + LOG_INF("Zephyr LoRaWAN Network Time Server Example, Board: %s", CONFIG_BOARD); + + lora_dev = DEVICE_DT_GET(DT_ALIAS(lora0)); + if (!device_is_ready(lora_dev)) { + LOG_ERR("%s: device not ready.", lora_dev->name); + return(-1); + } + + LOG_INF("Starting LoRaWAN stack."); + ret = lorawan_start(); + if (ret < 0) { + LOG_ERR("lorawan_start failed: %d", ret); + return(-1); + } + + // Enable callbacks + struct lorawan_downlink_cb downlink_cb = { + .port = LW_RECV_PORT_ANY, + .cb = dl_callback + }; + + lorawan_register_downlink_callback(&downlink_cb); + lorawan_register_dr_changed_callback(lorwan_datarate_changed); + + join_cfg.mode = LORAWAN_ACT_OTAA; + join_cfg.dev_eui = dev_eui; + join_cfg.otaa.join_eui = join_eui; + join_cfg.otaa.app_key = app_key; + join_cfg.otaa.nwk_key = app_key; + join_cfg.otaa.dev_nonce = dev_nonce; + + int i = 1; + + do { + LOG_INF("Joining network using OTAA, dev nonce %d, attempt %d", join_cfg.otaa.dev_nonce, i++); + ret = lorawan_join(&join_cfg); + if (ret < 0) { + if ((ret =-ETIMEDOUT)) { + LOG_WRN("Timed-out waiting for response."); + } else { + LOG_ERR("Join failed (%d)", ret); + } + } else { + LOG_INF("Join successful."); + } + + // Increment DevNonce as per LoRaWAN 1.0.4 Spec. + dev_nonce++; + join_cfg.otaa.dev_nonce = dev_nonce; + + if (ret < 0) { + // If failed, wait before re-trying. + k_sleep(K_MSEC(5000)); + } + + } while (ret != 0); + +#ifdef CONFIG_LORAWAN_APP_CLOCK_SYNC + + /* + * lorawan_clock_sync_run() registers a callback on port 202 (LoRaWAN Clock Sync Port) + * and starts a delayable work function to request the time periodically. By default, + * this is every 86400 seconds (24 hours), but can be changed using + * CONFIG_LORAWAN_APP_CLOCK_SYNC_PERIODICITY + * + * If successful, you should recieve a response back from the server similar to that + * below with the time correction (in GPS Time). + * + * main: Port 202, Pending 0, RSSI -66dB, SNR 8dBm + * main: Payload: + * 01 77 7b 5b 52 00 |.w{[R. + * lorawan_clock_sync: clock_sync_package_callback: AppTimeAns time_correction 1381727095 (token 0) + */ + + lorawan_clock_sync_run(); + +#endif + + while (1) { + + /* + * Once time synchronisation has occurred, lorawan_clock_sync_get() can + * be called to populate an uint32_t variable with GPS Time. This is the + * number of seconds since Jan 6th 1980 ignoring leap seconds. + */ + + ret = lorawan_clock_sync_get(&gps_time); + if (ret != 0) { + LOG_ERR("lorawan_clock_sync_get returned %d", ret); + } else { + /* + * The difference in time between UNIX (epoch Jan 1st 1970) and + * GPS (epoch Jan 6th 1980) is 315964800 seconds. This is a bit + * of a fudge as it doesn't take into account leap seconds and + * hence is out by roughly 18 seconds. + * + */ + unix_time = gps_time - 315964800; + localtime_r(&unix_time, &timeinfo); + strftime(buf, sizeof(buf), "%A %B %d %Y %I:%M:%S %p %Z", &timeinfo); + LOG_INF("GPS Time (Seconds since Jan 6th 1980) = %"PRIu32", UTC Time: %s", gps_time, buf); + } + + k_sleep(K_SECONDS(5)); + } +}