mirror of
				https://github.com/manuelbl/ttn-esp32.git
				synced 2025-10-31 10:40:35 +01:00 
			
		
		
		
	Store app/dev EUI/key in NVS
This commit is contained in:
		
							
								
								
									
										1
									
								
								.vscode/c_cpp_properties.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.vscode/c_cpp_properties.json
									
									
									
									
										vendored
									
									
								
							| @ -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", | ||||
|  | ||||
| @ -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"); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,3 +1,3 @@ | ||||
| PROJECT_NAME := hello_world | ||||
| PROJECT_NAME := send_recv | ||||
|  | ||||
| include $(IDF_PATH)/make/project.mk | ||||
|  | ||||
| @ -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"); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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 --- | ||||
|  | ||||
|  | ||||
| @ -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); | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user