From b179ce6884d02ec8a96d6c4660f74de5d1bda0aa Mon Sep 17 00:00:00 2001 From: Manuel Bleichenbacher Date: Mon, 23 Jul 2018 09:46:45 +0200 Subject: [PATCH 1/9] Add provisioning task, refactor provisioning --- .gitignore | 1 + .vscode/settings.json | 8 + Kconfig | 71 ++++++ include/TheThingsNetwork.h | 11 +- src/TheThingsNetwork.cpp | 252 ++------------------- src/hal.h | 3 + src/provisioning.c | 453 +++++++++++++++++++++++++++++++++++++ src/provisioning.h | 35 +++ 8 files changed, 595 insertions(+), 239 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 src/provisioning.c create mode 100644 src/provisioning.h diff --git a/.gitignore b/.gitignore index 50f8f64..57ace6f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ build/ sdkconfig +sdkconfig.old dev_keys.txt ttn-esp32 diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..248ebc7 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "files.associations": { + "provisioning.h": "c", + "config.h": "c", + "sdkconfig.h": "c", + "oslmic.h": "c" + } +} \ No newline at end of file diff --git a/Kconfig b/Kconfig index c6e7035..f1ef14c 100644 --- a/Kconfig +++ b/Kconfig @@ -68,4 +68,75 @@ config TTN_BG_TASK_PRIO the LoRaWAN radio chip. It needs a high priority as the timing is crucial. Higher numbers indicate higher priority. + +choice TTN_PROVISION_UART + prompt "UART for provisioning" + default TTN_PROVISION_UART_DEFAULT + help + Select whether to use UART for listening for provisioning commands. + + - Default is to use UART0 on pins GPIO1(TX) and GPIO3(RX). + - If "Custom" is selected, UART0 or UART1 can be chosen, + and any pins can be selected. + - If "None" is selected, the feature is not available. + +config TTN_PROVISION_UART_DEFAULT + bool "Default: UART0, TX=GPIO1, RX=GPIO3, 115,200 baud" +config TTN_PROVISION_UART_CUSTOM + bool "Custom" +config TTN_PROVISION_UART_NONE + bool "None" +endchoice + +choice TTN_PROVISION_UART_INIT + prompt "Initialize UART" + default TTN_PROVISION_UART_INIT_NO + depends on !TTN_PROVISION_UART_NONE + help + Select whether to initialize the UART, i.e. set the baud rate, the RX and TX + pins. If the UART is shared with other features (e.g. the console), it + should not be initialized. + +config TTN_PROVISION_UART_INIT_NO + bool "No" +config TTN_PROVISION_UART_INIT_YES + bool "Yes" +endchoice + +choice TTN_PROVISION_UART_NUM + prompt "UART peripheral for provisioning (0-1)" + depends on TTN_PROVISION_UART_CUSTOM + default TTN_PROVISION_UART_CUSTOM_NUM_0 + +config TTN_PROVISION_UART_CUSTOM_NUM_0 + bool "UART0" +config TTN_PROVISION_UART_CUSTOM_NUM_1 + bool "UART1" +endchoice + +config TTN_PROVISION_UART_NUM + int + default 0 if TTN_PROVISION_UART_DEFAULT || TTN_PROVISION_UART_NONE + default 0 if TTN_PROVISION_UART_CUSTOM_NUM_0 + default 1 if TTN_PROVISION_UART_CUSTOM_NUM_1 + +config TTN_PROVISION_UART_TX_GPIO + int "Provisioning UART TX on GPIO#" + depends on TTN_PROVISION_UART_CUSTOM && TTN_PROVISION_UART_INIT_YES + range 0 33 + default 19 + +config TTN_PROVISION_UART_RX_GPIO + int "Provisioning UART RX on GPIO#" + depends on TTN_PROVISION_UART_CUSTOM && TTN_PROVISION_UART_INIT_YES + range 0 39 + default 21 + +config TTN_PROVISION_UART_BAUDRATE + int "Provisioning UART baud rate" + depends on TTN_PROVISION_UART_CUSTOM && TTN_PROVISION_UART_INIT_YES + default 115200 + range 1200 4000000 + + endmenu diff --git a/include/TheThingsNetwork.h b/include/TheThingsNetwork.h index 2d4f906..99c040e 100644 --- a/include/TheThingsNetwork.h +++ b/include/TheThingsNetwork.h @@ -104,6 +104,13 @@ public: */ bool provision(const char *devEui, const char *appEui, const char *appKey); + /** + * @brief Start task that listens on configured UART for provisioning commands. + * + * Run 'make menuconfig' to configure it. + */ + void startProvisioningTask(); + /** * @brief Activate the device via OTAA. * @@ -176,12 +183,8 @@ public: 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 0ca6d79..21b9a7f 100644 --- a/src/TheThingsNetwork.cpp +++ b/src/TheThingsNetwork.cpp @@ -12,13 +12,13 @@ #include "freertos/FreeRTOS.h" #include "esp_event.h" -#include "nvs_flash.h" -#include "TheThingsNetwork.h" #include "esp_log.h" -#include "oslmic.h" +#include "TheThingsNetwork.h" #include "hal.h" #include "hal_esp32.h" #include "lmic.h" +#include "provisioning.h" + enum ClientAction { @@ -28,29 +28,14 @@ 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]; -static uint8_t appEui[8]; -static uint8_t appKey[16]; 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); -static void swapByteOrder(uint8_t* buf, int len); - - TheThingsNetwork::TheThingsNetwork() - : messageCallback(NULL), haveKeys(false) + : messageCallback(NULL) { ASSERT(ttnInstance == NULL); ttnInstance = this; @@ -89,45 +74,23 @@ void TheThingsNetwork::reset() bool TheThingsNetwork::provision(const char *devEui, const char *appEui, const char *appKey) { - if (!decodeKeys(devEui, appEui, appKey)) + if (!provisioning_decode_keys(devEui, appEui, appKey)) return false; - return saveKeys(); + return provisioning_save_keys(); } -bool TheThingsNetwork::decodeKeys(const char *devEui, const char *appEui, const char *appKey) +void TheThingsNetwork::startProvisioningTask() { - haveKeys = false; - - if (strlen(devEui) != 16 || !hexStringToBin(devEui, ::devEui, 8)) - { - ESP_LOGW(TAG, "Invalid device EUI: %s", devEui); - return false; - } - - swapByteOrder(::devEui, 8); - - if (strlen(appEui) != 16 || !hexStringToBin(appEui, ::appEui, 8)) - { - ESP_LOGW(TAG, "Invalid application EUI: %s", appEui); - return false; - } - - swapByteOrder(::appEui, 8); - - if (strlen(appKey) != 32 || !hexStringToBin(appKey, ::appKey, 16)) - { - ESP_LOGW(TAG, "Invalid application key: %s", appEui); - return false; - } - - haveKeys = true; - return true; +#if !defined(CONFIG_TTN_PROVISION_UART_NONE) + provisioning_start_task(); +#endif } + bool TheThingsNetwork::join(const char *devEui, const char *appEui, const char *appKey) { - if (!decodeKeys(devEui, appEui, appKey)) + if (!provisioning_decode_keys(devEui, appEui, appKey)) return false; return joinCore(); @@ -135,9 +98,9 @@ bool TheThingsNetwork::join(const char *devEui, const char *appEui, const char * bool TheThingsNetwork::join() { - if (!haveKeys) + if (!provisioning_have_keys()) { - if (!restoreKeys(false)) + if (!provisioning_restore_keys(false)) return false; } @@ -146,7 +109,7 @@ bool TheThingsNetwork::join() bool TheThingsNetwork::joinCore() { - if (!haveKeys) + if (!provisioning_have_keys()) { ESP_LOGW(TAG, "Device EUI, App EUI and/or App key have not been provided"); return false; @@ -205,118 +168,13 @@ 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) + if (provisioning_have_keys()) 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; + return provisioning_restore_keys(true); } @@ -333,29 +191,6 @@ static const char *eventNames[] = { }; #endif -// This EUI must be in little-endian format, so least-significant-byte first. -// When copying an EUI from ttnctl output, this means to reverse the bytes. -// For TTN issued EUIs the last bytes should be 0xD5, 0xB3, 0x70. -// The order is swapped in TheThingsNetwork::provision(). -void os_getArtEui (u1_t* buf) -{ - memcpy(buf, appEui, 8); -} - -// This should also be in little endian format, see above. -void os_getDevEui (u1_t* buf) -{ - memcpy(buf, devEui, 8); -} - -// This key should be in big endian format (or, since it is not really a number -// but a block of memory, endianness does not really apply). In practice, a key -// taken from ttnctl can be copied as-is. -void os_getDevKey (u1_t* buf) -{ - memcpy(buf, appKey, 16); -} - void onEvent (ev_t ev) { #if CONFIG_LOG_DEFAULT_LEVEL >= 3 ESP_LOGI(TAG, "event %s", eventNames[ev]); @@ -385,56 +220,3 @@ void onEvent (ev_t ev) { clientAction = eActionUnrelated; xQueueSend(resultQueue, &result, 100 / portTICK_PERIOD_MS); } - - -// --- Helper functions --- - -bool hexStringToBin(const char *hex, uint8_t *buf, int len) -{ - const char* ptr = hex; - for (int i = 0; i < len; i++) - { - int val = hexTupleToByte(ptr); - if (val < 0) - return false; - buf[i] = val; - ptr += 2; - } - return true; -} - -int hexTupleToByte(const char *hex) -{ - int nibble1 = hexDigitToVal(hex[0]); - if (nibble1 < 0) - return -1; - int nibble2 = hexDigitToVal(hex[1]); - if (nibble2 < 0) - return -1; - return (nibble1 << 4) | nibble2; -} - -int hexDigitToVal(char ch) -{ - if (ch >= '0' && ch <= '9') - return ch - '0'; - if (ch >= 'A' && ch <= 'F') - return ch + 10 - 'A'; - if (ch >= 'a' && ch <= 'f') - return ch + 10 - 'a'; - return -1; -} - -void swapByteOrder(uint8_t* buf, int len) -{ - uint8_t* p1 = buf; - uint8_t* p2 = buf + len - 1; - while (p1 < p2) - { - uint8_t t = *p1; - *p1 = *p2; - *p2 = t; - p1++; - p2--; - } -} \ No newline at end of file diff --git a/src/hal.h b/src/hal.h index 2acd42b..a1d34c6 100755 --- a/src/hal.h +++ b/src/hal.h @@ -28,6 +28,9 @@ #ifndef _hal_hpp_ #define _hal_hpp_ +#include "oslmic.h" + + #ifdef __cplusplus extern "C" { #endif diff --git a/src/provisioning.c b/src/provisioning.c new file mode 100644 index 0000000..e0879e2 --- /dev/null +++ b/src/provisioning.c @@ -0,0 +1,453 @@ +/******************************************************************************* + * + * 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 + * + * Task listening on a UART port for provisioning commands. + *******************************************************************************/ + +#include "freertos/FreeRTOS.h" +#include "driver/uart.h" +#include "esp_event.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "provisioning.h" + +#define UART_NUM CONFIG_TTN_PROVISION_UART_NUM +#define MAX_LINE_LENGTH 128 + +static const char *TAG = "ttn_prov"; +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 void provisioning_task(void* pvParameter); +static void provisioning_add_line_data(int numBytes); +static void provisioning_detect_line_end(int start_at); +static void provisioning_process_line(); +static bool read_nvs_value(nvs_handle handle, const char* key, uint8_t* data, size_t expected_length, bool silent); +static bool write_nvs_value(nvs_handle handle, const char* key, const uint8_t* data, size_t len); +static bool hex_str_to_bin(const char *hex, uint8_t *buf, int len); +static int hex_tuple_to_byte(const char *hex); +static int hex_digit_to_val(char ch); +static void bin_to_hex_str(const uint8_t* buf, int len, char* hex); +static char val_to_hex_digit(int val); +static void swap_bytes(uint8_t* buf, int len); + + +static QueueHandle_t uart_queue = NULL; +static char* line_buf; +static int line_length; +static uint8_t last_line_end_char = 0; +static uint8_t global_dev_eui[8]; +static uint8_t global_app_eui[8]; +static uint8_t global_app_key[16]; +static bool have_keys = false; + + +#if defined(CONFIG_TTN_PROVISION_UART_INIT_YES) +static void provisioning_init_uart(); +#endif + + +// --- LMIC callbacks + +// This EUI must be in little-endian format, so least-significant-byte first. +// When copying an EUI from ttnctl output, this means to reverse the bytes. +// For TTN issued EUIs the last bytes should be 0xD5, 0xB3, 0x70. +// The order is swapped in provisioning_decode_keys(). +void os_getArtEui (u1_t* buf) +{ + memcpy(buf, global_app_eui, 8); +} + +// This should also be in little endian format, see above. +void os_getDevEui (u1_t* buf) +{ + memcpy(buf, global_dev_eui, 8); +} + +// This key should be in big endian format (or, since it is not really a number +// but a block of memory, endianness does not really apply). In practice, a key +// taken from ttnctl can be copied as-is. +void os_getDevKey (u1_t* buf) +{ + memcpy(buf, global_app_key, 16); +} + + +// --- Provisioning task + +void provisioning_start_task() +{ +#if defined(CONFIG_TTN_PROVISION_UART_INIT_YES) + provisioning_init_uart(); +#endif + + esp_err_t err = uart_driver_install(UART_NUM, 2048, 2048, 20, &uart_queue, 0); + ESP_ERROR_CHECK(err); + + xTaskCreate(provisioning_task, "provisioning", 2048, NULL, 1, NULL); +} + +void provisioning_task(void* pvParameter) +{ + line_buf = (char*)malloc(MAX_LINE_LENGTH + 1); + line_length = 0; + + uart_event_t event; + + ESP_LOGI(TAG, "Provisioning task started"); + + while (true) + { + if (!xQueueReceive(uart_queue, &event, portMAX_DELAY)) + continue; + + switch (event.type) + { + case UART_DATA: + provisioning_add_line_data(event.size); + break; + + case UART_FIFO_OVF: + case UART_BUFFER_FULL: + uart_flush_input(UART_NUM); + xQueueReset(uart_queue); + break; + + default: + break; + } + } +} + +void provisioning_add_line_data(int numBytes) +{ + int n; +top: + n = numBytes; + if (line_length + n > MAX_LINE_LENGTH) + n = MAX_LINE_LENGTH - line_length; + + uart_read_bytes(UART_NUM, (uint8_t*)line_buf + line_length, n, portMAX_DELAY); + int start_at = line_length; + line_length += n; + + provisioning_detect_line_end(start_at); + + if (n < numBytes) + { + numBytes -= n; + goto top; + } +} + +void provisioning_detect_line_end(int start_at) +{ +top: + for (int p = start_at; p < line_length; p++) + { + char ch = line_buf[p]; + if (ch == 0x0d || ch == 0x0a) + { + if (p > 0) + uart_write_bytes(UART_NUM, line_buf + start_at, line_length - start_at - 1); + if (p > 0 || ch == 0x0d || last_line_end_char == 0x0a) + uart_write_bytes(UART_NUM, "\r\n", 2); + + line_buf[p] = 0; + last_line_end_char = ch; + + if (p > 0) + provisioning_process_line(); + + memcpy(line_buf, line_buf + p + 1, line_length - p - 1); + line_length -= p + 1; + start_at = 0; + goto top; + } + } + + if (line_length > 0) + uart_write_bytes(UART_NUM, line_buf + start_at, line_length - start_at); + + if (line_length == MAX_LINE_LENGTH) + line_length = 0; // Line too long; flush it +} + +void provisioning_process_line() +{ + bool is_ok = true; + // Expected format: + // AT+PROV? + // AT+PROV=hex16-hex16-hex32 + if (strcmp(line_buf, "AT+PROV?") == 0) + { + uint8_t binbuf[8]; + char hexbuf[16]; + + memcpy(binbuf, global_dev_eui, 8); + swap_bytes(binbuf, 8); + bin_to_hex_str(binbuf, 8, hexbuf); + uart_write_bytes(UART_NUM, hexbuf, 16); + uart_write_bytes(UART_NUM, "-", 1); + + memcpy(binbuf, global_app_eui, 8); + swap_bytes(binbuf, 8); + bin_to_hex_str(binbuf, 8, hexbuf); + uart_write_bytes(UART_NUM, hexbuf, 16); + + uart_write_bytes(UART_NUM, "-00000000000000000000000000000000\r\n", 35); + } + else if (strncmp(line_buf, "AT+PROV=", 8) == 0) + { + if (strlen(line_buf) == 74 && line_buf[24] == '-' && line_buf[41] == '-') + { + line_buf[24] = 0; + line_buf[41] = 0; + is_ok = provisioning_decode_keys(line_buf + 8, line_buf + 25, line_buf + 42); + } + else + { + is_ok = false; + } + } + else + { + is_ok = false; + } + + uart_write_bytes(UART_NUM, is_ok ? "OK\r\n" : "ERROR\r\n", is_ok ? 4 : 7); +} + +#if defined(CONFIG_TTN_PROVISION_UART_INIT_YES) + +void provisioning_init_uart() +{ + +} + +#endif + + +// --- Key handling + +bool provisioning_have_keys() +{ + return have_keys; +} + +bool provisioning_decode_keys(const char *dev_eui, const char *app_eui, const char *app_key) +{ + have_keys = false; + + if (strlen(dev_eui) != 16 || !hex_str_to_bin(dev_eui, global_dev_eui, 8)) + { + ESP_LOGW(TAG, "Invalid device EUI: %s", dev_eui); + return false; + } + + swap_bytes(global_dev_eui, 8); + + if (strlen(app_eui) != 16 || !hex_str_to_bin(app_eui, global_app_eui, 8)) + { + ESP_LOGW(TAG, "Invalid application EUI: %s", app_eui); + return false; + } + + swap_bytes(global_app_eui, 8); + + if (strlen(app_key) != 32 || !hex_str_to_bin(app_key, global_app_key, 16)) + { + ESP_LOGW(TAG, "Invalid application key: %s", app_key); + return false; + } + + have_keys = true; + return true; +} + + +// --- Non-volatile storage + +bool provisioning_save_keys() +{ + 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 (!write_nvs_value(handle, NVS_FLASH_KEY_DEV_EUI, global_dev_eui, sizeof(global_dev_eui))) + goto done; + + if (!write_nvs_value(handle, NVS_FLASH_KEY_APP_EUI, global_app_eui, sizeof(global_app_eui))) + goto done; + + if (!write_nvs_value(handle, NVS_FLASH_KEY_APP_KEY, global_app_key, sizeof(global_app_key))) + 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 provisioning_restore_keys(bool silent) +{ + have_keys = 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 (!read_nvs_value(handle, NVS_FLASH_KEY_DEV_EUI, global_dev_eui, sizeof(global_dev_eui), silent)) + goto done; + + if (!read_nvs_value(handle, NVS_FLASH_KEY_APP_EUI, global_app_eui, sizeof(global_app_eui), silent)) + goto done; + + if (!read_nvs_value(handle, NVS_FLASH_KEY_APP_KEY, global_app_key, sizeof(global_app_key), silent)) + goto done; + + have_keys = true; + ESP_LOGI(TAG, "Dev and app EUI and app key have been restored from NVS storage"); + +done: + nvs_close(handle); + return have_keys; +} + +bool read_nvs_value(nvs_handle handle, const char* key, uint8_t* data, size_t expected_length, bool silent) +{ + size_t size = expected_length; + esp_err_t res = nvs_get_blob(handle, key, data, &size); + if (res == ESP_OK && size == expected_length) + return true; + + if (res == ESP_OK && size != expected_length) + { + 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 write_nvs_value(nvs_handle handle, const char* key, const uint8_t* data, size_t len) +{ + uint8_t buf[16]; + if (read_nvs_value(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; +} + + +// --- Helper functions --- + +bool hex_str_to_bin(const char *hex, uint8_t *buf, int len) +{ + const char* ptr = hex; + for (int i = 0; i < len; i++) + { + int val = hex_tuple_to_byte(ptr); + if (val < 0) + return false; + buf[i] = val; + ptr += 2; + } + return true; +} + +int hex_tuple_to_byte(const char *hex) +{ + int nibble1 = hex_digit_to_val(hex[0]); + if (nibble1 < 0) + return -1; + int nibble2 = hex_digit_to_val(hex[1]); + if (nibble2 < 0) + return -1; + return (nibble1 << 4) | nibble2; +} + +int hex_digit_to_val(char ch) +{ + if (ch >= '0' && ch <= '9') + return ch - '0'; + if (ch >= 'A' && ch <= 'F') + return ch + 10 - 'A'; + if (ch >= 'a' && ch <= 'f') + return ch + 10 - 'a'; + return -1; +} + +void bin_to_hex_str(const uint8_t* buf, int len, char* hex) +{ + for (int i = 0; i < len; i++) + { + uint8_t b = buf[i]; + *hex = val_to_hex_digit((b & 0xf0) >> 4); + hex++; + *hex = val_to_hex_digit(b & 0x0f); + hex++; + } +} + +char val_to_hex_digit(int val) +{ + return "0123456789ABCDEF"[val]; +} + +void swap_bytes(uint8_t* buf, int len) +{ + uint8_t* p1 = buf; + uint8_t* p2 = buf + len - 1; + while (p1 < p2) + { + uint8_t t = *p1; + *p1 = *p2; + *p2 = t; + p1++; + p2--; + } +} diff --git a/src/provisioning.h b/src/provisioning.h new file mode 100644 index 0000000..3d1900f --- /dev/null +++ b/src/provisioning.h @@ -0,0 +1,35 @@ +/******************************************************************************* + * + * 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 + * + * Task listening on a UART port for provisioning commands. + *******************************************************************************/ + +#ifndef _provision_task_h_ +#define _provision_task_h_ + +#include "oslmic.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +void provisioning_start_task(); +bool provisioning_have_keys(); +bool provisioning_decode_keys(const char *dev_eui, const char *app_eui, const char *app_key); +bool provisioning_save_keys(); +bool provisioning_restore_keys(bool silent); + + +#ifdef __cplusplus +} +#endif + +#endif From 3e084a4ed964c5c89058d4a91d1f67848bc05c32 Mon Sep 17 00:00:00 2001 From: Manuel Bleichenbacher Date: Mon, 23 Jul 2018 14:30:03 +0200 Subject: [PATCH 2/9] provisioning example, waitForProvisioning() --- examples/provisioning/Makefile | 3 + examples/provisioning/main/component.mk | 0 examples/provisioning/main/main.cpp | 91 +++++++++++++++++++++++++ include/TheThingsNetwork.h | 10 +++ src/TheThingsNetwork.cpp | 15 ++++ src/provisioning.c | 35 ++++++---- 6 files changed, 142 insertions(+), 12 deletions(-) create mode 100644 examples/provisioning/Makefile create mode 100644 examples/provisioning/main/component.mk create mode 100644 examples/provisioning/main/main.cpp diff --git a/examples/provisioning/Makefile b/examples/provisioning/Makefile new file mode 100644 index 0000000..5866668 --- /dev/null +++ b/examples/provisioning/Makefile @@ -0,0 +1,3 @@ +PROJECT_NAME := provisioning + +include $(IDF_PATH)/make/project.mk diff --git a/examples/provisioning/main/component.mk b/examples/provisioning/main/component.mk new file mode 100644 index 0000000..e69de29 diff --git a/examples/provisioning/main/main.cpp b/examples/provisioning/main/main.cpp new file mode 100644 index 0000000..9c1cad9 --- /dev/null +++ b/examples/provisioning/main/main.cpp @@ -0,0 +1,91 @@ +/******************************************************************************* + * + * 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 provision the keys via the console. + *******************************************************************************/ + +#include "freertos/FreeRTOS.h" +#include "esp_event.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. + +// 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 33 + +static TheThingsNetwork ttn; + +const unsigned TX_INTERVAL = 30; +static uint8_t msgData[] = "Hello, world"; + + +void sendMessages(void* pvParameter) +{ + while (1) { + printf("Sending message...\n"); + TTNResponseCode res = ttn.transmitMessage(msgData, sizeof(msgData) - 1); + printf(res == kTTNSuccessfulTransmission ? "Message sent.\n" : "Transmission failed.\n"); + + vTaskDelay(TX_INTERVAL * 1000 / portTICK_PERIOD_MS); + } +} + +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); + + ttn.startProvisioningTask(); + + ttn.waitForProvisioning(); + + printf("Joining...\n"); + 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 99c040e..39e57c6 100644 --- a/include/TheThingsNetwork.h +++ b/include/TheThingsNetwork.h @@ -111,6 +111,16 @@ public: */ void startProvisioningTask(); + /** + * @brief Wait until the device EUI, app EUI and app key have been provisioned + * via the provisioning task. + * + * If device is already provisioned (stored data in NVS, call to 'provision()' + * or call to 'join(const char*, const char*, const char*)', this function + * immediately returns. + */ + void waitForProvisioning(); + /** * @brief Activate the device via OTAA. * diff --git a/src/TheThingsNetwork.cpp b/src/TheThingsNetwork.cpp index 21b9a7f..cab4f41 100644 --- a/src/TheThingsNetwork.cpp +++ b/src/TheThingsNetwork.cpp @@ -87,6 +87,21 @@ void TheThingsNetwork::startProvisioningTask() #endif } +void TheThingsNetwork::waitForProvisioning() +{ +#if !defined(CONFIG_TTN_PROVISION_UART_NONE) + if (isProvisioned()) + { + ESP_LOGI(TAG, "Device is already provisioned"); + return; + } + + while (!provisioning_have_keys()) + vTaskDelay(1000 / portTICK_PERIOD_MS); + + ESP_LOGI(TAG, "Device successfully provisioned"); +#endif +} bool TheThingsNetwork::join(const char *devEui, const char *appEui, const char *appKey) { diff --git a/src/provisioning.c b/src/provisioning.c index e0879e2..2c02fa6 100644 --- a/src/provisioning.c +++ b/src/provisioning.c @@ -245,32 +245,38 @@ bool provisioning_have_keys() bool provisioning_decode_keys(const char *dev_eui, const char *app_eui, const char *app_key) { - have_keys = false; + uint8_t buf_dev_eui[8]; + uint8_t buf_app_eui[8]; + uint8_t buf_app_key[16]; - if (strlen(dev_eui) != 16 || !hex_str_to_bin(dev_eui, global_dev_eui, 8)) + if (strlen(dev_eui) != 16 || !hex_str_to_bin(dev_eui, buf_dev_eui, 8)) { ESP_LOGW(TAG, "Invalid device EUI: %s", dev_eui); return false; } - swap_bytes(global_dev_eui, 8); + swap_bytes(buf_dev_eui, 8); - if (strlen(app_eui) != 16 || !hex_str_to_bin(app_eui, global_app_eui, 8)) + if (strlen(app_eui) != 16 || !hex_str_to_bin(app_eui, buf_app_eui, 8)) { ESP_LOGW(TAG, "Invalid application EUI: %s", app_eui); return false; } - swap_bytes(global_app_eui, 8); + swap_bytes(buf_app_eui, 8); - if (strlen(app_key) != 32 || !hex_str_to_bin(app_key, global_app_key, 16)) + if (strlen(app_key) != 32 || !hex_str_to_bin(app_key, buf_app_key, 16)) { ESP_LOGW(TAG, "Invalid application key: %s", app_key); return false; } - have_keys = true; - return true; + memcpy(global_dev_eui, buf_dev_eui, sizeof(global_dev_eui)); + memcpy(global_app_eui, buf_app_eui, sizeof(global_app_eui)); + memcpy(global_app_key, buf_app_key, sizeof(global_app_key)); + + have_keys = provisioning_save_keys(); + return have_keys; } @@ -313,7 +319,9 @@ done: bool provisioning_restore_keys(bool silent) { - have_keys = false; + uint8_t buf_dev_eui[8]; + uint8_t buf_app_eui[8]; + uint8_t buf_app_key[16]; nvs_handle handle = 0; esp_err_t res = nvs_open(NVS_FLASH_PARTITION, NVS_READONLY, &handle); @@ -328,15 +336,18 @@ bool provisioning_restore_keys(bool silent) if (res != ESP_OK) goto done; - if (!read_nvs_value(handle, NVS_FLASH_KEY_DEV_EUI, global_dev_eui, sizeof(global_dev_eui), silent)) + if (!read_nvs_value(handle, NVS_FLASH_KEY_DEV_EUI, buf_dev_eui, sizeof(global_dev_eui), silent)) goto done; - if (!read_nvs_value(handle, NVS_FLASH_KEY_APP_EUI, global_app_eui, sizeof(global_app_eui), silent)) + if (!read_nvs_value(handle, NVS_FLASH_KEY_APP_EUI, buf_app_eui, sizeof(global_app_eui), silent)) goto done; - if (!read_nvs_value(handle, NVS_FLASH_KEY_APP_KEY, global_app_key, sizeof(global_app_key), silent)) + if (!read_nvs_value(handle, NVS_FLASH_KEY_APP_KEY, buf_app_key, sizeof(global_app_key), silent)) goto done; + memcpy(global_dev_eui, buf_dev_eui, sizeof(global_dev_eui)); + memcpy(global_app_eui, buf_app_eui, sizeof(global_app_eui)); + memcpy(global_app_key, buf_app_key, sizeof(global_app_key)); have_keys = true; ESP_LOGI(TAG, "Dev and app EUI and app key have been restored from NVS storage"); From 3a6d4a650180e57f13969062d92eb81f10b84637 Mon Sep 17 00:00:00 2001 From: Manuel Bleichenbacher Date: Mon, 23 Jul 2018 15:29:37 +0200 Subject: [PATCH 3/9] Reset LMIC on AT command provisioning --- src/TheThingsNetwork.cpp | 4 ++-- src/provisioning.c | 11 ++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/TheThingsNetwork.cpp b/src/TheThingsNetwork.cpp index cab4f41..d5e4a37 100644 --- a/src/TheThingsNetwork.cpp +++ b/src/TheThingsNetwork.cpp @@ -222,12 +222,12 @@ void onEvent (ev_t ev) { } else if (clientAction == eActionJoining) { - if (ev != EV_JOINED && EV_REJOIN_FAILED) + if (ev != EV_JOINED && ev != EV_REJOIN_FAILED && ev != EV_RESET) return; } else { - if (ev != EV_TXCOMPLETE && ev != EV_LINK_DEAD) + if (ev != EV_TXCOMPLETE && ev != EV_LINK_DEAD && ev != EV_RESET) return; } diff --git a/src/provisioning.c b/src/provisioning.c index 2c02fa6..56d61ec 100644 --- a/src/provisioning.c +++ b/src/provisioning.c @@ -16,6 +16,8 @@ #include "esp_log.h" #include "nvs_flash.h" #include "provisioning.h" +#include "lmic.h" +#include "hal_esp32.h" #define UART_NUM CONFIG_TTN_PROVISION_UART_NUM #define MAX_LINE_LENGTH 128 @@ -212,13 +214,20 @@ void provisioning_process_line() line_buf[24] = 0; line_buf[41] = 0; is_ok = provisioning_decode_keys(line_buf + 8, line_buf + 25, line_buf + 42); + if (is_ok) + { + hal_enterCriticalSection(); + LMIC_reset(); + hal_leaveCriticalSection(); + onEvent(EV_RESET); + } } else { is_ok = false; } } - else + else if (strcmp(line_buf, "AT") != 0) { is_ok = false; } From 53d4be5de48d8c0961ed8030bd145afa13eddcc5 Mon Sep 17 00:00:00 2001 From: Manuel Bleichenbacher Date: Mon, 23 Jul 2018 16:14:33 +0200 Subject: [PATCH 4/9] AT command for ending provisioning task --- src/provisioning.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/provisioning.c b/src/provisioning.c index 56d61ec..a6b5e82 100644 --- a/src/provisioning.c +++ b/src/provisioning.c @@ -50,6 +50,7 @@ static uint8_t global_dev_eui[8]; static uint8_t global_app_eui[8]; static uint8_t global_app_key[16]; static bool have_keys = false; +static bool quit_task = false; #if defined(CONFIG_TTN_PROVISION_UART_INIT_YES) @@ -106,7 +107,7 @@ void provisioning_task(void* pvParameter) ESP_LOGI(TAG, "Provisioning task started"); - while (true) + while (!quit_task) { if (!xQueueReceive(uart_queue, &event, portMAX_DELAY)) continue; @@ -127,6 +128,10 @@ void provisioning_task(void* pvParameter) break; } } + + free(line_buf); + uart_driver_delete(UART_NUM); + vTaskDelete(NULL); } void provisioning_add_line_data(int numBytes) @@ -227,6 +232,10 @@ void provisioning_process_line() is_ok = false; } } + else if (strcmp(line_buf, "AT+PROVQ") == 0) + { + quit_task = true; + } else if (strcmp(line_buf, "AT") != 0) { is_ok = false; From 585c4b80bb359d1f1848a04b1ef4a3e44145e1a2 Mon Sep 17 00:00:00 2001 From: Manuel Bleichenbacher Date: Mon, 23 Jul 2018 16:59:38 +0200 Subject: [PATCH 5/9] Configuration of UART --- .vscode/c_cpp_properties.json | 2 ++ .vscode/settings.json | 3 ++- Kconfig | 42 +++++++++++++++++------------------ src/provisioning.c | 25 ++++++++++++++++----- 4 files changed, 44 insertions(+), 28 deletions(-) diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 934642c..199c206 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -16,7 +16,9 @@ "${IDF_PATH}/components/soc/include", "${IDF_PATH}/components/soc/esp32/include", "${IDF_PATH}/components/tcpip_adapter/include", + "${workspaceRoot}/examples/provisioning/build/include", "${workspaceRoot}/examples/hello_world/build/include", + "${workspaceRoot}/examples/send_recv/build/include", "${workspaceRoot}/include", "${workspaceRoot}/src" ], diff --git a/.vscode/settings.json b/.vscode/settings.json index 248ebc7..28b96ed 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,7 @@ "provisioning.h": "c", "config.h": "c", "sdkconfig.h": "c", - "oslmic.h": "c" + "oslmic.h": "c", + "hal_esp32.h": "c" } } \ No newline at end of file diff --git a/Kconfig b/Kconfig index f1ef14c..8e02846 100644 --- a/Kconfig +++ b/Kconfig @@ -88,21 +88,6 @@ config TTN_PROVISION_UART_NONE bool "None" endchoice -choice TTN_PROVISION_UART_INIT - prompt "Initialize UART" - default TTN_PROVISION_UART_INIT_NO - depends on !TTN_PROVISION_UART_NONE - help - Select whether to initialize the UART, i.e. set the baud rate, the RX and TX - pins. If the UART is shared with other features (e.g. the console), it - should not be initialized. - -config TTN_PROVISION_UART_INIT_NO - bool "No" -config TTN_PROVISION_UART_INIT_YES - bool "Yes" -endchoice - choice TTN_PROVISION_UART_NUM prompt "UART peripheral for provisioning (0-1)" depends on TTN_PROVISION_UART_CUSTOM @@ -114,6 +99,21 @@ config TTN_PROVISION_UART_CUSTOM_NUM_1 bool "UART1" endchoice +choice TTN_PROVISION_UART_CONFIG + prompt "Configure UART settings" + default TTN_PROVISION_UART_CONFIG_NO + depends on TTN_PROVISION_UART_CUSTOM + help + Select whether to configure the UART, i.e. set the baud rate, the RX and TX + pins. If the UART is shared with other features (e.g. the console), it + should not be configured. + +config TTN_PROVISION_UART_CONFIG_NO + bool "No" +config TTN_PROVISION_UART_CONFIG_YES + bool "Yes" +endchoice + config TTN_PROVISION_UART_NUM int default 0 if TTN_PROVISION_UART_DEFAULT || TTN_PROVISION_UART_NONE @@ -122,21 +122,21 @@ config TTN_PROVISION_UART_NUM config TTN_PROVISION_UART_TX_GPIO int "Provisioning UART TX on GPIO#" - depends on TTN_PROVISION_UART_CUSTOM && TTN_PROVISION_UART_INIT_YES + depends on TTN_PROVISION_UART_CONFIG_YES range 0 33 - default 19 + default 1 config TTN_PROVISION_UART_RX_GPIO int "Provisioning UART RX on GPIO#" - depends on TTN_PROVISION_UART_CUSTOM && TTN_PROVISION_UART_INIT_YES + depends on TTN_PROVISION_UART_CONFIG_YES range 0 39 - default 21 + default 3 config TTN_PROVISION_UART_BAUDRATE int "Provisioning UART baud rate" - depends on TTN_PROVISION_UART_CUSTOM && TTN_PROVISION_UART_INIT_YES - default 115200 + depends on TTN_PROVISION_UART_CONFIG_YES range 1200 4000000 + default 115200 endmenu diff --git a/src/provisioning.c b/src/provisioning.c index a6b5e82..c417151 100644 --- a/src/provisioning.c +++ b/src/provisioning.c @@ -53,8 +53,8 @@ static bool have_keys = false; static bool quit_task = false; -#if defined(CONFIG_TTN_PROVISION_UART_INIT_YES) -static void provisioning_init_uart(); +#if defined(CONFIG_TTN_PROVISION_UART_CONFIG_YES) +static void provisioning_config_uart(); #endif @@ -88,8 +88,8 @@ void os_getDevKey (u1_t* buf) void provisioning_start_task() { -#if defined(CONFIG_TTN_PROVISION_UART_INIT_YES) - provisioning_init_uart(); +#if defined(CONFIG_TTN_PROVISION_UART_CONFIG_YES) + provisioning_config_uart(); #endif esp_err_t err = uart_driver_install(UART_NUM, 2048, 2048, 20, &uart_queue, 0); @@ -244,11 +244,24 @@ void provisioning_process_line() uart_write_bytes(UART_NUM, is_ok ? "OK\r\n" : "ERROR\r\n", is_ok ? 4 : 7); } -#if defined(CONFIG_TTN_PROVISION_UART_INIT_YES) +#if defined(CONFIG_TTN_PROVISION_UART_CONFIG_YES) -void provisioning_init_uart() +void provisioning_config_uart() { + esp_err_t err; + uart_config_t uart_config = { + .baud_rate = CONFIG_TTN_PROVISION_UART_BAUDRATE, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE + }; + err = uart_param_config(UART_NUM, &uart_config); + ESP_ERROR_CHECK(err); + + err = uart_set_pin(UART_NUM, CONFIG_TTN_PROVISION_UART_TX_GPIO, CONFIG_TTN_PROVISION_UART_RX_GPIO, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + ESP_ERROR_CHECK(err); } #endif From 9f0d912758dae49031356c93d3d567bcefedb722 Mon Sep 17 00:00:00 2001 From: Manuel Bleichenbacher Date: Mon, 23 Jul 2018 17:36:28 +0200 Subject: [PATCH 6/9] Ensure library compiles even if no frequency plan was selected --- Kconfig | 8 ++++---- src/TheThingsNetwork.cpp | 8 +++++++- src/config.h | 3 ++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Kconfig b/Kconfig index 8e02846..5850a92 100644 --- a/Kconfig +++ b/Kconfig @@ -2,13 +2,13 @@ menu "The Things Network" choice TTN_LORA_FREQ prompt "TTN LoRa frequency" - default TTN_LORA_FREQ_NONE + default TTN_LORA_FREQ_DISABLED help LoRa frequency must match the geographic region the device is operated in. - Running it with the incorrect frequency most like violates the law. + Running it with the incorrect frequency most likely violates the law. -config TTN_LORA_FREQ_NONE - bool "None" +config TTN_LORA_FREQ_DISABLED + bool "LoRa Disabled" config TTN_LORA_FREQ_EU_868 bool "868 MHz (Europe)" diff --git a/src/TheThingsNetwork.cpp b/src/TheThingsNetwork.cpp index d5e4a37..ea06c64 100644 --- a/src/TheThingsNetwork.cpp +++ b/src/TheThingsNetwork.cpp @@ -14,6 +14,7 @@ #include "esp_event.h" #include "esp_log.h" #include "TheThingsNetwork.h" +#include "config.h" #include "hal.h" #include "hal_esp32.h" #include "lmic.h" @@ -37,10 +38,15 @@ static ClientAction clientAction = eActionUnrelated; TheThingsNetwork::TheThingsNetwork() : messageCallback(NULL) { +#if defined(TTN_IS_DISABLED) + ESP_LOGE(TAG, "TTN is disabled. Configure a frequency plan using 'make menuconfig'"); + ASSERT(0); + esp_restart(); +#endif + ASSERT(ttnInstance == NULL); ttnInstance = this; hal_initCriticalSection(); - } TheThingsNetwork::~TheThingsNetwork() diff --git a/src/config.h b/src/config.h index 478f72b..416d13b 100644 --- a/src/config.h +++ b/src/config.h @@ -24,7 +24,8 @@ extern "C" { #elif defined(CONFIG_TTN_LORA_FREQ_US_915) #define CFG_us915 1 #else -#error TTN LoRa frequency must be configured using 'make menuconfig' +#define TTN_IS_DISABLED 1 +#define CFG_eu868 1 #endif #if defined(CONFIG_TTN_RADIO_SX1272_73) From 9501f7589fe8836af4ed5b97f209536c95466c71 Mon Sep 17 00:00:00 2001 From: Manuel Bleichenbacher Date: Mon, 23 Jul 2018 17:59:07 +0200 Subject: [PATCH 7/9] Fix file description --- include/TheThingsNetwork.h | 2 +- src/TheThingsNetwork.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/TheThingsNetwork.h b/include/TheThingsNetwork.h index 39e57c6..291ca8c 100644 --- a/include/TheThingsNetwork.h +++ b/include/TheThingsNetwork.h @@ -7,7 +7,7 @@ * Licensed under MIT License * https://opensource.org/licenses/MIT * - * This the hardware abstraction layer to run LMIC in on ESP32 using ESP-iDF. + * High-level API for ttn-esp32. *******************************************************************************/ #ifndef _THETHINGSNETWORK_H_ diff --git a/src/TheThingsNetwork.cpp b/src/TheThingsNetwork.cpp index ea06c64..524c9d5 100644 --- a/src/TheThingsNetwork.cpp +++ b/src/TheThingsNetwork.cpp @@ -7,7 +7,7 @@ * Licensed under MIT License * https://opensource.org/licenses/MIT * - * This the hardware abstraction layer to run LMIC in on ESP32 using ESP-iDF. + * High-level API for ttn-esp32. *******************************************************************************/ #include "freertos/FreeRTOS.h" From 78f65901cd9aecdf7ac5be522d6cf2b981747576 Mon Sep 17 00:00:00 2001 From: Manuel Bleichenbacher Date: Sat, 28 Jul 2018 14:26:12 +0200 Subject: [PATCH 8/9] Minor adjustment to SPI timing --- src/hal_esp32.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hal_esp32.c b/src/hal_esp32.c index 00cc96a..065a0f8 100755 --- a/src/hal_esp32.c +++ b/src/hal_esp32.c @@ -182,7 +182,8 @@ static void hal_spi_init() .command_bits = 0, .address_bits = 8, .spics_io_num = lmic_pins.nss, - .queue_size = SPI_QUEUE_SIZE + .queue_size = SPI_QUEUE_SIZE, + .cs_ena_posttrans = 2 }; esp_err_t ret = spi_bus_add_device(lmic_pins.spi_host, &spi_device_intf_config, &spi_handle); From c95ecaab0c5cb5a669e4ab80d6488e5a183dd100 Mon Sep 17 00:00:00 2001 From: Manuel Bleichenbacher Date: Sat, 28 Jul 2018 22:13:59 +0200 Subject: [PATCH 9/9] Check for all zero EUIs and keys --- src/TheThingsNetwork.cpp | 4 +++- src/provisioning.c | 36 +++++++++++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/TheThingsNetwork.cpp b/src/TheThingsNetwork.cpp index 524c9d5..6eff020 100644 --- a/src/TheThingsNetwork.cpp +++ b/src/TheThingsNetwork.cpp @@ -195,7 +195,9 @@ bool TheThingsNetwork::isProvisioned() if (provisioning_have_keys()) return true; - return provisioning_restore_keys(true); + provisioning_restore_keys(true); + + return provisioning_have_keys(); } diff --git a/src/provisioning.c b/src/provisioning.c index c417151..aeedb7c 100644 --- a/src/provisioning.c +++ b/src/provisioning.c @@ -40,6 +40,7 @@ static int hex_digit_to_val(char ch); static void bin_to_hex_str(const uint8_t* buf, int len, char* hex); static char val_to_hex_digit(int val); static void swap_bytes(uint8_t* buf, int len); +static bool is_all_zeroes(const uint8_t* buf, int len); static QueueHandle_t uart_queue = NULL; @@ -306,8 +307,14 @@ bool provisioning_decode_keys(const char *dev_eui, const char *app_eui, const ch memcpy(global_app_eui, buf_app_eui, sizeof(global_app_eui)); memcpy(global_app_key, buf_app_key, sizeof(global_app_key)); - have_keys = provisioning_save_keys(); - return have_keys; + have_keys = !is_all_zeroes(global_dev_eui, sizeof(global_dev_eui)) + && !is_all_zeroes(global_app_eui, sizeof(global_app_eui)) + && !is_all_zeroes(global_app_key, sizeof(global_app_key)); + + if (!provisioning_save_keys()) + return false; + + return true; } @@ -379,12 +386,23 @@ bool provisioning_restore_keys(bool silent) memcpy(global_dev_eui, buf_dev_eui, sizeof(global_dev_eui)); memcpy(global_app_eui, buf_app_eui, sizeof(global_app_eui)); memcpy(global_app_key, buf_app_key, sizeof(global_app_key)); - have_keys = true; - ESP_LOGI(TAG, "Dev and app EUI and app key have been restored from NVS storage"); + + have_keys = !is_all_zeroes(global_dev_eui, sizeof(global_dev_eui)) + && !is_all_zeroes(global_app_eui, sizeof(global_app_eui)) + && !is_all_zeroes(global_app_key, sizeof(global_app_key)); + + if (have_keys) + { + ESP_LOGI(TAG, "Dev and app EUI and app key have been restored from NVS storage"); + } + else + { + ESP_LOGW(TAG, "Dev and app EUI and app key are invalid (zeroes only)"); + } done: nvs_close(handle); - return have_keys; + return true; } bool read_nvs_value(nvs_handle handle, const char* key, uint8_t* data, size_t expected_length, bool silent) @@ -493,3 +511,11 @@ void swap_bytes(uint8_t* buf, int len) p2--; } } + +bool is_all_zeroes(const uint8_t* buf, int len) +{ + for (int i = 0; i < len; i++) + if (buf[i] != 0) + return false; + return true; +} \ No newline at end of file