diff --git a/examples/shutdown/CMakeLists.txt b/examples/shutdown/CMakeLists.txt new file mode 100644 index 0000000..b6b2f10 --- /dev/null +++ b/examples/shutdown/CMakeLists.txt @@ -0,0 +1,9 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +get_filename_component(TTN_DIR ../.. ABSOLUTE) +set(EXTRA_COMPONENT_DIRS "${TTN_DIR}") + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(send_recv) diff --git a/examples/shutdown/Makefile b/examples/shutdown/Makefile new file mode 100644 index 0000000..bb74d6f --- /dev/null +++ b/examples/shutdown/Makefile @@ -0,0 +1,5 @@ +PROJECT_NAME := shutdown + +EXTRA_COMPONENT_DIRS := $(abspath ../..) + +include $(IDF_PATH)/make/project.mk diff --git a/examples/shutdown/main/CMakeLists.txt b/examples/shutdown/main/CMakeLists.txt new file mode 100644 index 0000000..e931882 --- /dev/null +++ b/examples/shutdown/main/CMakeLists.txt @@ -0,0 +1,4 @@ +set(COMPONENT_SRCS "main.cpp") +set(COMPONENT_ADD_INCLUDEDIRS "") + +register_component() diff --git a/examples/shutdown/main/component.mk b/examples/shutdown/main/component.mk new file mode 100644 index 0000000..e69de29 diff --git a/examples/shutdown/main/main.cpp b/examples/shutdown/main/main.cpp new file mode 100644 index 0000000..be23730 --- /dev/null +++ b/examples/shutdown/main/main.cpp @@ -0,0 +1,137 @@ +/******************************************************************************* + * + * ttn-esp32 - The Things Network device library for ESP-IDF / SX127x + * + * Copyright (c) 2018 Manuel Bleichenbacher + * + * Licensed under MIT License + * https://opensource.org/licenses/MIT + * + * Sample program showing how to send and receive messages. + *******************************************************************************/ + +#include "freertos/FreeRTOS.h" +#include "esp_event.h" +#include "driver/gpio.h" +#include "nvs_flash.h" + +#include "TheThingsNetwork.h" + +// NOTE: +// The LoRaWAN frequency and the radio chip must be configured by running 'make menuconfig'. +// Go to Components / The Things Network, select the appropriate values and save. + +// Copy the below hex string from the "Device EUI" field +// on your device's overview page in the TTN console. +const char *devEui = "????????????????"; + +// Copy the below two lines from bottom of the same page +const char *appEui = "????????????????"; +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 = 30; +static uint8_t msgData[] = "Hello, world"; + +bool join() +{ + printf("Joining...\n"); + if (ttn.join()) + { + printf("Joined.\n"); + return true; + } + else + { + printf("Join failed. Goodbye\n"); + return false; + } +} + + +void sendMessages(void* pvParameter) +{ + while (1) { + + // Send 2 messages + for (int i = 0; i < 2; i++) + { + printf("Sending message...\n"); + TTNResponseCode res = ttn.transmitMessage(msgData, sizeof(msgData) - 1); + printf(res == kTTNSuccessfulTransmission ? "Message sent.\n" : "Transmission failed.\n"); + + vTaskDelay(TX_INTERVAL * pdMS_TO_TICKS(1000)); + } + + // shutdown + ttn.shutdown(); + + // go to sleep + printf("Sleeping for 30s...\n"); + vTaskDelay(pdMS_TO_TICKS(30000)); + + // startup + ttn.startup(); + + // join again + if (!join()) + return; + } +} + +void messageReceived(const uint8_t* message, size_t length, 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); + + ttn.onMessage(messageReceived); + + if (join()) + { + xTaskCreate(sendMessages, "send_messages", 1024 * 4, (void* )0, 3, nullptr); + } +} diff --git a/include/TheThingsNetwork.h b/include/TheThingsNetwork.h index 1990171..592a4bb 100644 --- a/include/TheThingsNetwork.h +++ b/include/TheThingsNetwork.h @@ -68,7 +68,8 @@ public: /** * @brief Reset the LoRaWAN radio. * - * Does not clear provisioned keys. + * To restart communication, `join()` must be called. + * Neither clears the provisioned keys nor the configured pins. */ void reset(); @@ -76,8 +77,8 @@ public: * @brief Configures the pins used to communicate with the LoRaWAN radio chip. * * - * The SPI bus must be first configured using spi_bus_initialize(). Then it is passed as the first parameter. - * Additionally, 'gpio_install_isr_service()' must be called to initialize the GPIO ISR handler service. + * 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. * * @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) @@ -229,6 +230,7 @@ public: /** * Returns whether Adaptive Data Rate (ADR) is enabled. + * * @return true if enabled * @return false if disabled */ @@ -244,6 +246,21 @@ public: */ void setAdrEnabled(bool enabled); + /** + * @brief Stops all activies and shuts down the RF module and the background tasks. + * + * To restart communication, `startup()` and `join()` must be called. + * Neither clears the provisioned keys nor the configured pins. + */ + void shutdown(); + + /** + * @brief Restarts the background tasks and RF module. + * + * This member function must only be called after a call to `shutdowna()`. + */ + void startup(); + private: TTNMessageCallback messageCallback; diff --git a/src/TTNProvisioning.cpp b/src/TTNProvisioning.cpp index 1bd4a46..b7945c9 100644 --- a/src/TTNProvisioning.cpp +++ b/src/TTNProvisioning.cpp @@ -97,6 +97,7 @@ void ttn_provisioning_task_caller(void* pvParameter) { TTNProvisioning* provisioning = (TTNProvisioning*)pvParameter; provisioning->provisioningTask(); + vTaskDelete(nullptr); } void TTNProvisioning::provisioningTask() @@ -132,7 +133,6 @@ void TTNProvisioning::provisioningTask() free(line_buf); uart_driver_delete(UART_NUM); - vTaskDelete(nullptr); } void TTNProvisioning::addLineData(int numBytes) diff --git a/src/TheThingsNetwork.cpp b/src/TheThingsNetwork.cpp index 8d0dcfa..0aadb87 100644 --- a/src/TheThingsNetwork.cpp +++ b/src/TheThingsNetwork.cpp @@ -111,10 +111,23 @@ void TheThingsNetwork::reset() ttn_hal.enterCriticalSection(); LMIC_reset(); waitingReason = eWaitingNone; - if (lmicEventQueue != nullptr) - { - xQueueReset(lmicEventQueue); - } + ttn_hal.leaveCriticalSection(); +} + +void TheThingsNetwork::shutdown() +{ + ttn_hal.enterCriticalSection(); + LMIC_shutdown(); + ttn_hal.stopLMICTask(); + waitingReason = eWaitingNone; + ttn_hal.leaveCriticalSection(); +} + +void TheThingsNetwork::startup() +{ + ttn_hal.enterCriticalSection(); + LMIC_reset(); + ttn_hal.startLMICTask(); ttn_hal.leaveCriticalSection(); } @@ -194,6 +207,7 @@ bool TheThingsNetwork::joinCore() } ttn_hal.enterCriticalSection(); + xQueueReset(lmicEventQueue); waitingReason = eWaitingForJoin; LMIC_startJoining(); ttn_hal.wakeUp(); diff --git a/src/hal/hal_esp32.cpp b/src/hal/hal_esp32.cpp index 1975a35..4071fa6 100755 --- a/src/hal/hal_esp32.cpp +++ b/src/hal/hal_esp32.cpp @@ -25,6 +25,7 @@ #define NOTIFY_BIT_DIO 1 #define NOTIFY_BIT_TIMER 2 #define NOTIFY_BIT_WAKEUP 4 +#define NOTIFY_BIT_STOP 8 static const char* const TAG = "ttn_hal"; @@ -305,6 +306,9 @@ bool HAL_ESP32::wait(WaitKind waitKind) if (bits == 0) return false; + if ((bits & NOTIFY_BIT_STOP) != 0) + return false; + if ((bits & NOTIFY_BIT_WAKEUP) != 0) { if (waitKind != WAIT_FOR_TIMER) @@ -449,8 +453,12 @@ void HAL_ESP32::leaveCriticalSection() // ----------------------------------------------------------------------------- -void HAL_ESP32::lmicBackgroundTask(void* pvParameter) { - os_runloop(); +void HAL_ESP32::lmicBackgroundTask(void* pvParameter) +{ + HAL_ESP32* instance = (HAL_ESP32*)pvParameter; + while (instance->runBackgroundTask) + os_runloop_once(); + vTaskDelete(nullptr); } void hal_init_ex(const void *pContext) @@ -468,14 +476,25 @@ void HAL_ESP32::init() timerInit(); } -void HAL_ESP32::startLMICTask() { - xTaskCreate(lmicBackgroundTask, "ttn_lmic", 1024 * 4, nullptr, CONFIG_TTN_BG_TASK_PRIO, &lmicTask); +void HAL_ESP32::startLMICTask() +{ + runBackgroundTask = true; + xTaskCreate(lmicBackgroundTask, "ttn_lmic", 1024 * 4, this, CONFIG_TTN_BG_TASK_PRIO, &lmicTask); // enable interrupts gpio_isr_handler_add(pinDIO0, dioIrqHandler, (void *)0); gpio_isr_handler_add(pinDIO1, dioIrqHandler, (void *)1); } +void HAL_ESP32::stopLMICTask() +{ + runBackgroundTask = false; + gpio_isr_handler_remove(pinDIO0); + gpio_isr_handler_remove(pinDIO1); + disarmTimer(); + xTaskNotify(lmicTask, NOTIFY_BIT_STOP, eSetBits); +} + // ----------------------------------------------------------------------------- // Fatal failure diff --git a/src/hal/hal_esp32.h b/src/hal/hal_esp32.h index 2db6140..4eee5fe 100644 --- a/src/hal/hal_esp32.h +++ b/src/hal/hal_esp32.h @@ -39,6 +39,7 @@ public: void configurePins(spi_host_device_t spi_host, uint8_t nss, uint8_t rxtx, uint8_t rst, uint8_t dio0, uint8_t dio1); void init(); void startLMICTask(); + void stopLMICTask(); void wakeUp(); void initCriticalSection(); @@ -84,6 +85,7 @@ private: SemaphoreHandle_t mutex; esp_timer_handle_t timer; int64_t nextAlarm; + volatile bool runBackgroundTask; }; extern HAL_ESP32 ttn_hal;