From ba481ceac5d9245110e93db9097e3c76b0cffa16 Mon Sep 17 00:00:00 2001 From: Manuel Bl <10954524+manuelbl@users.noreply.github.com> Date: Wed, 29 Sep 2021 18:02:52 +0200 Subject: [PATCH] Power off for C++ --- examples/power_off/CMakeLists.txt | 10 ++ examples/power_off/main/CMakeLists.txt | 4 + examples/power_off/main/main.cpp | 126 +++++++++++++++++++++++++ include/TheThingsNetwork.h | 48 ++++++++++ include/ttn.h | 2 +- 5 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 examples/power_off/CMakeLists.txt create mode 100644 examples/power_off/main/CMakeLists.txt create mode 100644 examples/power_off/main/main.cpp diff --git a/examples/power_off/CMakeLists.txt b/examples/power_off/CMakeLists.txt new file mode 100644 index 0000000..802785b --- /dev/null +++ b/examples/power_off/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.5) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +# Update the below line to match the path to the ttn-esp32 library, +# e.g. list(APPEND EXTRA_COMPONENT_DIRS "/Users/me/Documents/ttn-esp32") +list(APPEND EXTRA_COMPONENT_DIRS "../..") + +#add_definitions(-DLMIC_ENABLE_event_logging=1) + +project(power_off) diff --git a/examples/power_off/main/CMakeLists.txt b/examples/power_off/main/CMakeLists.txt new file mode 100644 index 0000000..8aa291e --- /dev/null +++ b/examples/power_off/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRCS "main.cpp" + INCLUDE_DIRS "." + REQUIRES ttn-esp32) diff --git a/examples/power_off/main/main.cpp b/examples/power_off/main/main.cpp new file mode 100644 index 0000000..a5c4ed8 --- /dev/null +++ b/examples/power_off/main/main.cpp @@ -0,0 +1,126 @@ +/******************************************************************************* + * + * ttn-esp32 - The Things Network device library for ESP-IDF / SX127x + * + * Copyright (c) 2021 Manuel Bleichenbacher + * + * Licensed under MIT License + * https://opensource.org/licenses/MIT + * + * Sample program sending messages and going to deep sleep in-between. + *******************************************************************************/ + +#include "driver/gpio.h" +#include "esp_event.h" +#include "esp_sleep.h" +#include "freertos/FreeRTOS.h" +#include "nvs_flash.h" + +#include "TheThingsNetwork.h" + +// NOTE: +// The LoRaWAN frequency and the radio chip must be configured by running 'idf.py menuconfig'. +// Go to Components / The Things Network, select the appropriate values and save. + +// Copy the below hex strings from the TTN console (Applications > Your application > End devices +// > Your device > Activation information) + +// AppEUI (sometimes called JoinEUI) +const char *appEui = "????????????????"; +// DevEUI +const char *devEui = "????????????????"; +// AppKey +const char *appKey = "????????????????????????????????"; + +// Pins and other resources +#define TTN_SPI_HOST HSPI_HOST +#define TTN_SPI_DMA_CHAN 1 +#define TTN_PIN_SPI_SCLK 5 +#define TTN_PIN_SPI_MOSI 27 +#define TTN_PIN_SPI_MISO 19 +#define TTN_PIN_NSS 18 +#define TTN_PIN_RXTX TTN_NOT_CONNECTED +#define TTN_PIN_RST 14 +#define TTN_PIN_DIO0 26 +#define TTN_PIN_DIO1 35 + +static TheThingsNetwork ttn; + +const unsigned TX_INTERVAL = 60; +static uint8_t msgData[] = "Hello, world"; + +void messageReceived(const uint8_t *message, size_t length, ttn_port_t port) +{ + printf("Message of %d bytes received on port %d:", length, port); + for (int i = 0; i < length; i++) + printf(" %02x", message[i]); + printf("\n"); +} + +extern "C" void app_main(void) +{ + esp_err_t err; + // Initialize the GPIO ISR handler service + err = gpio_install_isr_service(ESP_INTR_FLAG_IRAM); + ESP_ERROR_CHECK(err); + + // Initialize the NVS (non-volatile storage) for saving and restoring the keys + err = nvs_flash_init(); + ESP_ERROR_CHECK(err); + + // Initialize SPI bus + spi_bus_config_t spi_bus_config; + spi_bus_config.miso_io_num = TTN_PIN_SPI_MISO; + spi_bus_config.mosi_io_num = TTN_PIN_SPI_MOSI; + spi_bus_config.sclk_io_num = TTN_PIN_SPI_SCLK; + spi_bus_config.quadwp_io_num = -1; + spi_bus_config.quadhd_io_num = -1; + spi_bus_config.max_transfer_sz = 0; + err = spi_bus_initialize(TTN_SPI_HOST, &spi_bus_config, TTN_SPI_DMA_CHAN); + ESP_ERROR_CHECK(err); + + // Configure the SX127x pins + ttn.configurePins(TTN_SPI_HOST, TTN_PIN_NSS, TTN_PIN_RXTX, TTN_PIN_RST, TTN_PIN_DIO0, TTN_PIN_DIO1); + + // The below line can be commented after the first run as the data is saved in NVS + ttn.provision(devEui, appEui, appKey); + + // Register callback for received messages + ttn.onMessage(messageReceived); + + // ttn.setAdrEnabled(false); + // ttn.setDataRate(kTTNDataRate_US915_SF7); + // ttn.setMaxTxPower(14); + + if (ttn.resumeAfterPowerOff(60)) + { + printf("Resumed from power off.\n"); + } + else + { + printf("Joining...\n"); + if (ttn.join()) + { + printf("Joined.\n"); + } + else + { + printf("Join failed. Goodbye\n"); + return; + } + } + + printf("Sending message...\n"); + TTNResponseCode res = ttn.transmitMessage(msgData, sizeof(msgData) - 1); + printf(res == kTTNSuccessfulTransmission ? "Message sent.\n" : "Transmission failed.\n"); + + // Wait until TTN communication is idle and save state + ttn.waitForIdle(); + ttn.prepareForPowerOff(); + + printf("Power off...\n"); + // Do whatever is needed to power off the device. + // For testing, press reset button to simulate power cycle. + while (true) + vTaskDelay(pdMS_TO_TICKS(1000)); +} diff --git a/include/TheThingsNetwork.h b/include/TheThingsNetwork.h index 0c6d9ea..2452344 100644 --- a/include/TheThingsNetwork.h +++ b/include/TheThingsNetwork.h @@ -619,6 +619,32 @@ class TheThingsNetwork return ttn_resume_after_deep_sleep(); } + /** + * @brief Resumes TTN communication after power off. + * + * The communcation state is restored from data previously saved in NVS (non-volatile storage). + * 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. + * + * In order to advance the clock, the estimated duration the device was powered off has to + * be specified. As the exact duration is probably not known, an estimation of the shortest + * duration between power-off and next power-on can be used instead. + * + * If the device has access to the real time, set the system time (using `settimeofday()`) + * before calling this function (and before @ref join()) and pass 0 for `off_duration`. + * + * Before this function is called, `nvs_flash_init()` must have been called once. + * + * @param off_duration duration the device was powered off (in minutes) + * @return `true` if the device was able to resume, `false` otherwise. + */ + bool resumeAfterPowerOff(int off_duration) + { + return ttn_resume_after_power_off(off_duration); + } + /** * @brief Stops all activies and prepares for deep sleep. * @@ -639,6 +665,28 @@ class TheThingsNetwork ttn_prepare_for_deep_sleep(); } + /** + * @brief Stops all activies and prepares for power off. + * + * This function is called before powering off the device. It saves the current + * communication state in NVS (non-volatile storage) 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 is powered off. + * + * Before calling this function, use @ref ttn_busy_duration() to check + * that the TTN device is idle and ready to be powered off. + * + * To restart communication, @ref resumeAfterPowerOff(int) must be called. + * + * Before this function is called, `nvs_flash_init()` must have been called once. + */ + void prepareForPowerOff() + { + ttn_prepare_for_power_off(); + } + /** * @brief Waits until the TTN device is idle. * diff --git a/include/ttn.h b/include/ttn.h index a41b491..ca36586 100644 --- a/include/ttn.h +++ b/include/ttn.h @@ -598,7 +598,7 @@ extern "C" * * In order to advance the clock, the estimated duration the device was powered off has to * be specified. As the exact duration is probably not known, an estimation of the shortest - * duration between power off and on can be used instead. + * duration between power-off and next power-on can be used instead. * * If the device has access to the real time, set the system time (using `settimeofday()`) * before calling this function (and before @ref join()) and pass 0 for `off_duration`.