Add provisioning task, refactor provisioning

This commit is contained in:
Manuel Bleichenbacher 2018-07-23 09:46:45 +02:00
parent 2dd5b05891
commit b179ce6884
8 changed files with 595 additions and 239 deletions

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
build/
sdkconfig
sdkconfig.old
dev_keys.txt
ttn-esp32

8
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,8 @@
{
"files.associations": {
"provisioning.h": "c",
"config.h": "c",
"sdkconfig.h": "c",
"oslmic.h": "c"
}
}

71
Kconfig
View File

@ -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

View File

@ -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

View File

@ -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;
#if !defined(CONFIG_TTN_PROVISION_UART_NONE)
provisioning_start_task();
#endif
}
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;
}
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--;
}
}

View File

@ -28,6 +28,9 @@
#ifndef _hal_hpp_
#define _hal_hpp_
#include "oslmic.h"
#ifdef __cplusplus
extern "C" {
#endif

453
src/provisioning.c Normal file
View File

@ -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--;
}
}

35
src/provisioning.h Normal file
View File

@ -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