From bd728887cf35378427fca1c74c72eb72fa4c44c8 Mon Sep 17 00:00:00 2001 From: Manuel Bl <10954524+manuelbl@users.noreply.github.com> Date: Sun, 26 Sep 2021 16:35:42 +0200 Subject: [PATCH] Prepare for deep sleep functions --- include/TheThingsNetwork.h | 118 ++++++++++++++++++++++++++----------- include/ttn.h | 98 +++++++++++++++++++++--------- src/ttn.c | 51 +++++++++------- 3 files changed, 182 insertions(+), 85 deletions(-) diff --git a/include/TheThingsNetwork.h b/include/TheThingsNetwork.h index be18f1b..8d996cc 100644 --- a/include/TheThingsNetwork.h +++ b/include/TheThingsNetwork.h @@ -436,17 +436,6 @@ class TheThingsNetwork { } - /** - * @brief Resets the LoRaWAN radio. - * - * To restart communication, @ref join() must be called. - * Clears neither the provisioned keys nor the configured pins. - */ - void reset() - { - ttn_reset(); - } - /** * @brief Configures the pins used to communicate with the LoRaWAN radio chip. * @@ -553,11 +542,14 @@ class TheThingsNetwork } /** - * @brief Activates the device via OTAA. + * @brief Activates the device via OTAA using previously provisioned keys. * - * The DevEUI, AppEUI/JoinEUI and AppKey must have already been provisioned by a call to provision(). + * The DevEUI, AppEUI/JoinEUI and AppKey must have already been provisioned by a call + * to @ref provision() or @ref provisionWithMAC(). * Before this function is called, `nvs_flash_init()` must have been called once. * + * The RF module is initialized and the TTN background task is started. + * * The function blocks until the activation has completed or failed. * * @return `true` if the activation was succesful, `false` if the activation failed @@ -568,9 +560,12 @@ class TheThingsNetwork } /** - * @brief Sets the DevEUI, AppEUI/JoinEUI and AppKey and activate the device via OTAA. + * @brief Activates the device via OTAA using the provided keys. * - * The DevEUI, AppEUI/JoinEUI and AppKey are NOT saved in non-volatile memory. + * For the activation, the provided DevEUI, AppEUI/JoinEUI and AppKey are used. + * They are NOT saved in non-volatile memory. + * + * The RF module is initialized and the TTN background task is started. * * The function blocks until the activation has completed or failed. * @@ -584,6 +579,78 @@ class TheThingsNetwork return ttn_join(devEui, appEui, appKey); } + /** + * @brief Resumes TTN communication after deep sleep. + * + * The communcation state is restored from data previously saved in RTC memory. + * The RF module and the TTN background task are started. + * + * This function is called instead of @ref join() or @ref join(const char*, const char*, const char*) + * to continue with the established communication and to avoid a further join procedure. + * + * @return `true` if the device was able to resume, `false` otherwise. + */ + bool resumeAfterDeepSleep() + { + return ttn_resume_after_deep_sleep(); + } + + /** + * @brief Stops all activies and prepares for deep sleep. + * + * This function is called before entering deep sleep. It saves the current + * communication state in RTC memory and shuts down the RF module and the + * TTN background task. + * + * It neither clears the provisioned keys nor the configured pins + * but they will be lost if the device goes into deep sleep. + * + * Before calling this function, use @ref busyDuration() to check + * that the TTN device is idle and ready to go to deep sleep. + * + * To restart communication, @ref resumeAfterDeepSleep() must be called. + */ + void prepareForDeepSleep() + { + ttn_prepare_for_deep_sleep(); + } + + /** + * @brief Returns the minimum duration the TTN device is busy. + * + * This function can be called to check whether the TTN device is + * still involved in communication or ready to go to deep sleep or + * to be powered off. + * + * If it returns 0, the TTN communication is idle and the device can go + * to deep sleep or can be powered off. + * + * If it returns a value different from 0, the value indicates the duration + * the device will be certainly busy. After that time, this function must be + * called again. It might still return a value different from 0. + * + * @return busy duration (in FreeRTOS ticks) + */ + TickType_t busyDuration() + { + return ttn_busy_duration(); + } + + /** + * @brief Stops all activies. + * + * This function shuts down the RF module and the TTN background task. It neither clears the + * provisioned keys nor the configured pins. The currentat device state (and activation) + * are lost. + * + * To restart communication, @ref join() or @ref join(const char*, const char*, const char*) + * must be called. + */ + void shutdown() + { + ttn_shutdown(); + } + /** * @brief Transmits a message * @@ -697,27 +764,6 @@ class TheThingsNetwork ttn_set_max_tx_pow(tx_pow); } - /** - * @brief Stops all activies and shuts down the RF module and the background tasks. - * - * To restart communication, @ref startup() and @ref join() must be called. - * it neither clears the provisioned keys nor the configured pins. - */ - void shutdown() - { - ttn_shutdown(); - } - - /** - * @brief Restarts the background tasks and RF module. - * - * This member function must only be called after a call to shutdowna(). - */ - void startup() - { - ttn_startup(); - } - /** * @brief Gets current RX/TX window * @return window diff --git a/include/ttn.h b/include/ttn.h index bdf83fc..cba5406 100644 --- a/include/ttn.h +++ b/include/ttn.h @@ -432,19 +432,13 @@ extern "C" */ void ttn_init(void); - /** - * @brief Resets the LoRaWAN radio. - * - * To restart communication, @ref ttn_join() or @ref ttn_join_provisioned() must be called. - * It neither clears the provisioned keys nor the configured pins. - */ - void ttn_reset(void); - /** * @brief Configures the pins used to communicate with the LoRaWAN radio chip. * * Before calling this member function, the SPI bus needs to be configured using `spi_bus_initialize()`. * Additionally, `gpio_install_isr_service()` must have been called to initialize the GPIO ISR handler service. + * + * Call this function after @ref ttn_init() and before all other TTN functions. * * @param spi_host The SPI bus/peripherial to use (`SPI_HOST`, `HSPI_HOST` or `VSPI_HOST`). * @param nss The GPIO pin number connected to the radio chip's NSS pin (serving as the SPI chip select) @@ -528,11 +522,14 @@ extern "C" void ttn_wait_for_provisioning(void); /** - * @brief Activates the device via OTAA. + * @brief Activates the device via OTAA using previously provisioned keys. * - * The DevEUI, AppEUI/JoinEUI and AppKey must have already been provisioned by a call to provision(). + * The DevEUI, AppEUI/JoinEUI and AppKey must have already been provisioned by a call + * to @ref ttn_provision() or @ref ttn_provision_with_mac(). * Before this function is called, `nvs_flash_init()` must have been called once. * + * The RF module is initialized and the TTN background task is started. + * * The function blocks until the activation has completed or failed. * * @return `true` if the activation was succesful, `false` if the activation failed @@ -540,9 +537,12 @@ extern "C" bool ttn_join_provisioned(void); /** - * @brief Sets the DevEUI, AppEUI/JoinEUI and AppKey and activate the device via OTAA. + * @brief Activates the device via OTAA using the provided keys. + * + * For the activation, the provided DevEUI, AppEUI/JoinEUI and AppKey are used. + * They are NOT saved in non-volatile memory. * - * The DevEUI, AppEUI/JoinEUI and AppKey are NOT saved in non-volatile memory. + * The RF module is initialized and the TTN background task is started. * * The function blocks until the activation has completed or failed. * @@ -553,6 +553,65 @@ extern "C" */ bool ttn_join(const char *dev_eui, const char *app_eui, const char *app_key); + /** + * @brief Resumes TTN communication after deep sleep. + * + * The communcation state is restored from data previously saved in RTC memory. + * The RF module and the TTN background task are started. + * + * This function is called instead of @ref ttn_join() or @ref ttn_join_provisioned() + * to continue with the established communication and to avoid a further join procedure. + * + * @return `true` if the device was able to resume, `false` otherwise. + */ + bool ttn_resume_after_deep_sleep(void); + + /** + * @brief Stops all activies and prepares for deep sleep. + * + * This function is called before entering deep sleep. It saves the current + * communication state in RTC memory and shuts down the RF module and the + * TTN background task. + * + * It neither clears the provisioned keys nor the configured pins + * but they will be lost if the device goes into deep sleep. + * + * Before calling this function, use @ref ttn_busy_duration() to check + * that the TTN device is idle and ready to go to deep sleep. + * + * To restart communication, @ref ttn_resume_after_deep_sleep() must be called. + */ + void ttn_prepare_for_deep_sleep(void); + + /** + * @brief Returns the minimum duration the TTN device will be busy. + * + * This function can be called to check whether the TTN device is + * still involved in communication or ready to go to deep sleep or + * to be powered off. + * + * If it returns 0, the TTN communication is idle and the device can go + * to deep sleep or can be powered off. + * + * If it returns a value different from 0, the value indicates the duration + * the device will be certainly busy. After that time, this function must be + * called again. It might still return a value different from 0. + * + * @return busy duration (in FreeRTOS ticks) + */ + TickType_t ttn_busy_duration(); + + /** + * @brief Stops all activies. + * + * This function shuts down the RF module and the TTN background task. It neither clears the + * provisioned keys nor the configured pins. The currentat device state (and activation) + * are lost. + * + * To restart communication, @ref ttn_join() and @ref ttn_join_provisioned() must be called. + */ + void ttn_shutdown(void); + /** * @brief Transmits a message * @@ -642,21 +701,6 @@ extern "C" */ void ttn_set_max_tx_pow(int tx_pow); - /** - * @brief Stops all activies and shuts down the RF module and the background tasks. - * - * To restart communication, @ref ttn_startup() and @ref ttn_join() must be called. - * it neither clears the provisioned keys nor the configured pins. - */ - void ttn_shutdown(void); - - /** - * @brief Restarts the background tasks and RF module. - * - * This member function must only be called after a call to shutdowna(). - */ - void ttn_startup(void); - /** * @brief Gets current RX/TX window * @return window diff --git a/src/ttn.c b/src/ttn.c index c94c8d9..695a208 100644 --- a/src/ttn.c +++ b/src/ttn.c @@ -58,6 +58,7 @@ typedef struct } ttn_lmic_event_t; static bool is_started; +static bool has_joined; static QueueHandle_t lmic_event_queue; static ttn_message_cb message_callback; static ttn_waiting_reason_t waiting_reason = TTN_WAITING_NONE; @@ -67,6 +68,8 @@ static int subband = 2; static ttn_data_rate_t join_data_rate = TTN_DR_JOIN_DEFAULT; static int max_tx_power = DEFAULT_MAX_TX_POWER; +static void start(void); +static void stop(void); static bool join_core(void); static void config_rf_params(void); static void event_callback(void *user_data, ev_t event); @@ -93,16 +96,6 @@ void ttn_configure_pins(spi_host_device_t spi_host, uint8_t nss, uint8_t rxtx, u #if LMIC_ENABLE_event_logging ttn_log_init(); #endif - - LMIC_registerEventCb(event_callback, NULL); - LMIC_registerRxMessageCb(message_received_callback, NULL); - - os_init_ex(NULL); - ttn_reset(); - - lmic_event_queue = xQueueCreate(4, sizeof(ttn_lmic_event_t)); - ASSERT(lmic_event_queue != NULL); - hal_esp32_start_lmic_task(); } void ttn_set_subband(int band) @@ -110,17 +103,32 @@ void ttn_set_subband(int band) subband = band; } -void ttn_reset(void) +void start(void) { + if (is_started) + return; + + LMIC_registerEventCb(event_callback, NULL); + LMIC_registerRxMessageCb(message_received_callback, NULL); + + os_init_ex(NULL); hal_esp32_enter_critical_section(); LMIC_reset(); LMIC_setClockError(MAX_CLOCK_ERROR * 4 / 100); waiting_reason = TTN_WAITING_NONE; hal_esp32_leave_critical_section(); + + lmic_event_queue = xQueueCreate(4, sizeof(ttn_lmic_event_t)); + ASSERT(lmic_event_queue != NULL); + hal_esp32_start_lmic_task(); + is_started = true; } -void ttn_shutdown(void) +void stop(void) { + if (!is_started) + return; + hal_esp32_enter_critical_section(); LMIC_shutdown(); hal_esp32_stop_lmic_task(); @@ -128,12 +136,9 @@ void ttn_shutdown(void) hal_esp32_leave_critical_section(); } -void ttn_startup(void) +void ttn_shutdown(void) { - hal_esp32_enter_critical_section(); - LMIC_reset(); - hal_esp32_start_lmic_task(); - hal_esp32_leave_critical_section(); + stop(); } bool ttn_provision(const char *dev_eui, const char *app_eui, const char *app_key) @@ -226,7 +231,9 @@ bool join_core(void) return false; } - is_started = true; + start(); + + has_joined = true; hal_esp32_enter_critical_section(); xQueueReset(lmic_event_queue); waiting_reason = TTN_WAITING_FOR_JOIN; @@ -239,8 +246,8 @@ bool join_core(void) ttn_lmic_event_t event; xQueueReceive(lmic_event_queue, &event, portMAX_DELAY); - is_started = event.event == TTN_EVNT_JOIN_COMPLETED; - return is_started; + has_joined = event.event == TTN_EVNT_JOIN_COMPLETED; + return has_joined; } ttn_response_code_t ttn_transmit_message(const uint8_t *payload, size_t length, ttn_port_t port, bool confirm) @@ -317,7 +324,7 @@ void ttn_set_adr_enabled(bool enabled) void ttn_set_data_rate(ttn_data_rate_t data_rate) { - if (is_started) + if (has_joined) { hal_esp32_enter_critical_section(); LMIC_setDrTxpow(data_rate, LMIC.adrTxPow); @@ -331,7 +338,7 @@ void ttn_set_data_rate(ttn_data_rate_t data_rate) void ttn_set_max_tx_pow(int tx_pow) { - if (is_started) + if (has_joined) { hal_esp32_enter_critical_section(); LMIC_setDrTxpow(LMIC.datarate, tx_pow);