From 2c998f5accc00b3212585434e4c5a2c517bedfe5 Mon Sep 17 00:00:00 2001 From: Manuel Bleichenbacher Date: Fri, 20 Jul 2018 21:19:53 +0200 Subject: [PATCH] Store app/dev EUI/key in NVS --- .vscode/c_cpp_properties.json | 1 + examples/hello_world/main/main.cpp | 32 +++--- examples/send_recv/Makefile | 2 +- examples/send_recv/main/main.cpp | 34 ++++--- include/TheThingsNetwork.h | 22 ++++- src/TheThingsNetwork.cpp | 151 ++++++++++++++++++++++++++++- src/hal_esp32.c | 12 +-- 7 files changed, 218 insertions(+), 36 deletions(-) diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 1c5a1d7..934642c 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -12,6 +12,7 @@ "${IDF_PATH}/components/log/include", "${IDF_PATH}/components/lwip/include/lwip", "${IDF_PATH}/components/lwip/include/lwip/port", + "${IDF_PATH}/components/nvs_flash/include", "${IDF_PATH}/components/soc/include", "${IDF_PATH}/components/soc/esp32/include", "${IDF_PATH}/components/tcpip_adapter/include", diff --git a/examples/hello_world/main/main.cpp b/examples/hello_world/main/main.cpp index 0fe985d..68237f1 100644 --- a/examples/hello_world/main/main.cpp +++ b/examples/hello_world/main/main.cpp @@ -11,6 +11,7 @@ #include "freertos/FreeRTOS.h" #include "esp_event.h" +#include "nvs_flash.h" #include "TheThingsNetwork.h" @@ -33,15 +34,12 @@ const unsigned TX_INTERVAL = 30; static uint8_t msgData[] = "Hello, world"; -void send_messages(void* pvParameter) +void sendMessages(void* pvParameter) { while (1) { printf("Sending message...\n"); TTNResponseCode res = ttn.transmitBytes(msgData, sizeof(msgData) - 1); - if (res == kTTNSuccessfulTransmission) - printf("Message sent.\n"); - else - printf("Transmission failed.\n"); + printf(res == kTTNSuccessfulTransmission ? "Message sent.\n" : "Transmission failed.\n"); vTaskDelay(TX_INTERVAL * 1000 / portTICK_PERIOD_MS); } @@ -49,7 +47,13 @@ void send_messages(void* pvParameter) extern "C" void app_main(void) { - gpio_install_isr_service(ESP_INTR_FLAG_IRAM); + 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; @@ -61,15 +65,21 @@ extern "C" void app_main(void) spi_bus_config.max_transfer_sz = 0; esp_err_t ret = spi_bus_initialize(HSPI_HOST, &spi_bus_config, 1); - assert(ret == ESP_OK); + ESP_ERROR_CHECK(err); ttn.configurePins(HSPI_HOST, 18, TTN_NOT_CONNECTED, 14, 26, 33); + // The below line can be commented after the first run as the data is saved in NVS ttn.provision(devEui, appEui, appKey); printf("Joining...\n"); - ttn.join(); - printf("Joined.\n"); - - xTaskCreate(send_messages, "send_messages", 1024 * 4, (void* )0, 3, NULL); + if (ttn.join()) + { + printf("Joined.\n"); + xTaskCreate(sendMessages, "send_messages", 1024 * 4, (void* )0, 3, NULL); + } + else + { + printf("Join failed. Goodbye\n"); + } } diff --git a/examples/send_recv/Makefile b/examples/send_recv/Makefile index 1038c2d..96ee4b2 100644 --- a/examples/send_recv/Makefile +++ b/examples/send_recv/Makefile @@ -1,3 +1,3 @@ -PROJECT_NAME := hello_world +PROJECT_NAME := send_recv include $(IDF_PATH)/make/project.mk diff --git a/examples/send_recv/main/main.cpp b/examples/send_recv/main/main.cpp index 59d34b9..0f175bc 100644 --- a/examples/send_recv/main/main.cpp +++ b/examples/send_recv/main/main.cpp @@ -6,11 +6,12 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * - * Sample program showing how to send a test message every 30 second. + * Sample program showing how to send and receive messages. *******************************************************************************/ #include "freertos/FreeRTOS.h" #include "esp_event.h" +#include "nvs_flash.h" #include "TheThingsNetwork.h" @@ -38,10 +39,7 @@ void sendMessages(void* pvParameter) while (1) { printf("Sending message...\n"); TTNResponseCode res = ttn.transmitBytes(msgData, sizeof(msgData) - 1); - if (res == kTTNSuccessfulTransmission) - printf("Message sent.\n"); - else - printf("Transmission failed.\n"); + printf(res == kTTNSuccessfulTransmission ? "Message sent.\n" : "Transmission failed.\n"); vTaskDelay(TX_INTERVAL * 1000 / portTICK_PERIOD_MS); } @@ -57,7 +55,13 @@ void messageReceived(const uint8_t* message, size_t length, port_t port) extern "C" void app_main(void) { - gpio_install_isr_service(ESP_INTR_FLAG_IRAM); + 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; @@ -68,18 +72,24 @@ extern "C" void app_main(void) spi_bus_config.quadhd_io_num = -1; spi_bus_config.max_transfer_sz = 0; - esp_err_t ret = spi_bus_initialize(HSPI_HOST, &spi_bus_config, 1); - assert(ret == ESP_OK); + err = spi_bus_initialize(HSPI_HOST, &spi_bus_config, 1); + ESP_ERROR_CHECK(err); ttn.configurePins(HSPI_HOST, 18, TTN_NOT_CONNECTED, 14, 26, 33); + // 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); printf("Joining...\n"); - ttn.join(); - printf("Joined.\n"); - - xTaskCreate(sendMessages, "send_messages", 1024 * 4, (void* )0, 3, NULL); + if (ttn.join()) + { + printf("Joined.\n"); + xTaskCreate(sendMessages, "send_messages", 1024 * 4, (void* )0, 3, NULL); + } + else + { + printf("Join failed. Goodbye\n"); + } } diff --git a/include/TheThingsNetwork.h b/include/TheThingsNetwork.h index e31cc6e..49b8b26 100644 --- a/include/TheThingsNetwork.h +++ b/include/TheThingsNetwork.h @@ -90,7 +90,10 @@ public: /** * @brief Sets the information needed to activate the device via OTAA, without actually activating. * - * The provided EUIs and key are saved in non-volatile memory. Call join() without arguments to activate. + * The provided device EUI, app EUI and app key are saved in non-volatile memory. Before + * this function is called, 'nvs_flash_init' must have been called once. + * + * Call join() without arguments to activate. * * @param devEui Device EUI (16 character string with hexadecimal data) * @param appEui Application EUI of the device (16 character string with hexadecimal data) @@ -101,9 +104,9 @@ public: bool provision(const char *devEui, const char *appEui, const char *appKey); /** - * @brief Set the EUIs and keys and activate the device via OTAA. + * @brief Set the device EUI, app EUI and app key and activate the device via OTAA. * - * The EUIs and key are NOT saved in non-volatile memory. + * The device EUI, app EUI and app key are NOT saved in non-volatile memory. * * The function blocks until the activation has completed or failed. * @@ -119,6 +122,7 @@ public: * @brief Activate the device via OTAA. * * The app EUI, app key and dev EUI must already have been provisioned by a call to 'provision()'. + * Before this function is called, 'nvs_flash_init' must have been called once. * * The function blocks until the activation has completed or failed. * @@ -160,11 +164,23 @@ public: */ void onMessage(TTNMessageCallback callback); + /** + * @brief Checks if device EUI, app EUI and app key have been stored in non-volatile storage + * or have been provided as by a call to 'join(const char*, const char*, const char*)'. + * + * @return true if they are stored, complete and of the correct size + * @return false otherwise + */ + bool isProvisioned(); private: TTNMessageCallback messageCallback; + bool haveKeys; + bool joinCore(); bool decodeKeys(const char *devEui, const char *appEui, const char *appKey); + bool saveKeys(); + bool restoreKeys(bool silent); }; #endif diff --git a/src/TheThingsNetwork.cpp b/src/TheThingsNetwork.cpp index a205268..52e8169 100644 --- a/src/TheThingsNetwork.cpp +++ b/src/TheThingsNetwork.cpp @@ -11,6 +11,7 @@ #include "freertos/FreeRTOS.h" #include "esp_event.h" +#include "nvs_flash.h" #include "TheThingsNetwork.h" #include "esp_log.h" #include "oslmic.h" @@ -26,6 +27,10 @@ enum ClientAction }; static const char *TAG = "ttn"; +static const char *NVS_FLASH_PARTITION = "ttn"; +static const char *NVS_FLASH_KEY_DEV_EUI = "devEui"; +static const char *NVS_FLASH_KEY_APP_EUI = "appEui"; +static const char *NVS_FLASH_KEY_APP_KEY = "appKey"; static TheThingsNetwork* ttnInstance; static uint8_t devEui[8]; @@ -35,6 +40,8 @@ static QueueHandle_t resultQueue; static ClientAction clientAction = eActionUnrelated; +static bool readNvsValue(nvs_handle handle, const char* key, uint8_t* data, size_t expectedLength, bool silent); +static bool writeNvsValue(nvs_handle handle, const char* key, const uint8_t* data, size_t len); static bool hexStringToBin(const char *hex, uint8_t *buf, int len); static int hexTupleToByte(const char *hex); static int hexDigitToVal(char ch); @@ -42,6 +49,7 @@ static void swapByteOrder(uint8_t* buf, int len); TheThingsNetwork::TheThingsNetwork() + : messageCallback(NULL), haveKeys(false) { ASSERT(ttnInstance == NULL); ttnInstance = this; @@ -67,7 +75,7 @@ void TheThingsNetwork::configurePins(spi_host_device_t spi_host, uint8_t nss, ui reset(); resultQueue = xQueueCreate(12, sizeof(int)); - assert(resultQueue != NULL); + ASSERT(resultQueue != NULL); hal_startBgTask(); } @@ -80,11 +88,16 @@ void TheThingsNetwork::reset() bool TheThingsNetwork::provision(const char *devEui, const char *appEui, const char *appKey) { - return decodeKeys(devEui, appEui, appKey); + if (!decodeKeys(devEui, appEui, appKey)) + return false; + + return saveKeys(); } bool TheThingsNetwork::decodeKeys(const char *devEui, const char *appEui, const char *appKey) { + haveKeys = false; + if (strlen(devEui) != 16 || !hexStringToBin(devEui, ::devEui, 8)) { ESP_LOGW(TAG, "Invalid device EUI: %s", devEui); @@ -107,6 +120,7 @@ bool TheThingsNetwork::decodeKeys(const char *devEui, const char *appEui, const return false; } + haveKeys = true; return true; } @@ -115,11 +129,28 @@ bool TheThingsNetwork::join(const char *devEui, const char *appEui, const char * if (!decodeKeys(devEui, appEui, appKey)) return false; - return join(); + return joinCore(); } bool TheThingsNetwork::join() { + if (!haveKeys) + { + if (!restoreKeys(false)) + return false; + } + + return joinCore(); +} + +bool TheThingsNetwork::joinCore() +{ + if (!haveKeys) + { + ESP_LOGW(TAG, "Device EUI, App EUI and/or App key have not been provided"); + return false; + } + hal_enterCriticalSection(); clientAction = eActionJoining; LMIC_startJoining(); @@ -173,6 +204,120 @@ void TheThingsNetwork::onMessage(TTNMessageCallback callback) messageCallback = callback; } +bool TheThingsNetwork::saveKeys() +{ + bool result = false; + + nvs_handle handle = 0; + esp_err_t res = nvs_open(NVS_FLASH_PARTITION, NVS_READWRITE, &handle); + if (res == ESP_ERR_NVS_NOT_INITIALIZED) + { + ESP_LOGW(TAG, "NVS storage is not initialized. Call 'nvs_flash_init()' first."); + goto done; + } + ESP_ERROR_CHECK(res); + if (res != ESP_OK) + goto done; + + if (!writeNvsValue(handle, NVS_FLASH_KEY_DEV_EUI, ::devEui, sizeof(::devEui))) + goto done; + + if (!writeNvsValue(handle, NVS_FLASH_KEY_APP_EUI, ::appEui, sizeof(::appEui))) + goto done; + + if (!writeNvsValue(handle, NVS_FLASH_KEY_APP_KEY, ::appKey, sizeof(::appKey))) + goto done; + + res = nvs_commit(handle); + ESP_ERROR_CHECK(res); + + result = true; + ESP_LOGI(TAG, "Dev and app EUI and app key saved in NVS storage"); + +done: + nvs_close(handle); + return result; +} + +bool TheThingsNetwork::restoreKeys(bool silent) +{ + haveKeys = false; + + nvs_handle handle = 0; + esp_err_t res = nvs_open(NVS_FLASH_PARTITION, NVS_READONLY, &handle); + if (res == ESP_ERR_NVS_NOT_FOUND) + return false; // partition does not exist yet + if (res == ESP_ERR_NVS_NOT_INITIALIZED) + { + ESP_LOGW(TAG, "NVS storage is not initialized. Call 'nvs_flash_init()' first."); + goto done; + } + ESP_ERROR_CHECK(res); + if (res != ESP_OK) + goto done; + + if (!readNvsValue(handle, NVS_FLASH_KEY_DEV_EUI, ::devEui, sizeof(::devEui), silent)) + goto done; + + if (!readNvsValue(handle, NVS_FLASH_KEY_APP_EUI, ::appEui, sizeof(::appEui), silent)) + goto done; + + if (!readNvsValue(handle, NVS_FLASH_KEY_APP_KEY, ::appKey, sizeof(::appKey), silent)) + goto done; + + haveKeys = true; + ESP_LOGI(TAG, "Dev and app EUI and app key have been restored from NVS storage"); + +done: + nvs_close(handle); + return haveKeys; +} + +bool TheThingsNetwork::isProvisioned() +{ + if (haveKeys) + return true; + + return restoreKeys(true); +} + +bool readNvsValue(nvs_handle handle, const char* key, uint8_t* data, size_t expectedLength, bool silent) +{ + size_t size = expectedLength; + esp_err_t res = nvs_get_blob(handle, key, data, &size); + if (res == ESP_OK && size == expectedLength) + return true; + + if (res == ESP_OK && size != expectedLength) + { + if (!silent) + ESP_LOGW(TAG, "Invalid size of NVS data for %s", key); + return false; + } + + if (res == ESP_ERR_NVS_NOT_FOUND) + { + if (!silent) + ESP_LOGW(TAG, "No NVS data found for %s", key); + return false; + } + + ESP_ERROR_CHECK(res); + return false; +} + +bool writeNvsValue(nvs_handle handle, const char* key, const uint8_t* data, size_t len) +{ + uint8_t buf[16]; + if (readNvsValue(handle, key, buf, len, true) && memcmp(buf, data, len) == 0) + return true; // unchanged + + esp_err_t res = nvs_set_blob(handle, key, data, len); + ESP_ERROR_CHECK(res); + + return res == ESP_OK; +} + // --- LMIC functions --- diff --git a/src/hal_esp32.c b/src/hal_esp32.c index abc96a4..af3cc62 100755 --- a/src/hal_esp32.c +++ b/src/hal_esp32.c @@ -84,7 +84,7 @@ static void hal_io_init() } dio_queue = xQueueCreate(12, sizeof(queue_item_t)); - assert(dio_queue != NULL); + ASSERT(dio_queue != NULL); gpio_pad_select_gpio(lmic_pins.dio0); gpio_set_direction(lmic_pins.dio0, GPIO_MODE_INPUT); @@ -151,8 +151,8 @@ static void collect_spi_result() spi_transaction_t* trx; esp_err_t err = spi_device_get_trans_result(spi_handle, &trx, 100 / portTICK_PERIOD_MS); - assert(err == ESP_OK); - assert(trx == spi_trx_queue + tail); + ESP_ERROR_CHECK(err); + ASSERT(trx == spi_trx_queue + tail); spi_num_outstanding_trx--; } @@ -163,7 +163,7 @@ static void submit_spi_trx() int head = spi_trx_queue_head; esp_err_t err = spi_device_queue_trans(spi_handle, spi_trx_queue + head, 100 / portTICK_PERIOD_MS); - assert(err == ESP_OK); + ESP_ERROR_CHECK(err); spi_num_outstanding_trx++; head++; @@ -185,7 +185,7 @@ static void hal_spi_init() }; esp_err_t ret = spi_bus_add_device(lmic_pins.spi_host, &spi_device_intf_config, &spi_handle); - assert(ret == ESP_OK); + ESP_ERROR_CHECK(ret); ESP_LOGI(TAG, "SPI initialized"); } @@ -459,5 +459,5 @@ void hal_startBgTask() { void hal_failed(const char *file, u2_t line) { ESP_LOGE(TAG, "%s:%d", file, line); - assert(0); + ASSERT(0); }