From e4c620c9c58c6aede2fccbea207fbc0d87c6b9ca Mon Sep 17 00:00:00 2001 From: localhorst Date: Wed, 20 Jan 2021 20:55:33 +0100 Subject: [PATCH] splitted into several modules; include cleanup --- components/mesh_ota/CMakeLists.txt | 2 +- .../{HTTPS_client.c => HTTPS_Client.c} | 2 +- .../{Mesh_network.c => Mesh_Network.c} | 2 +- ...twork_handler.c => Mesh_Network_Handler.c} | 2 +- components/mesh_ota/Mesh_OTA.c | 637 +----------------- components/mesh_ota/Mesh_OTA_Globals.c | 10 + .../mesh_ota/Mesh_OTA_Partition_Access.c | 323 +++++++++ components/mesh_ota/Mesh_OTA_Util.c | 314 +++++++++ .../{HTTPS_client.h => HTTPS_Client.h} | 9 - .../{Mesh_network.h => Mesh_Network.h} | 11 +- components/mesh_ota/include/Mesh_OTA.h | 39 +- components/mesh_ota/include/Mesh_OTA_Access.h | 28 + .../mesh_ota/include/Mesh_OTA_Globals.h | 19 + components/mesh_ota/include/Mesh_OTA_Util.h | 35 + main/Blinky_LED.c | 2 +- main/Blinky_LED.h | 1 + main/Main.c | 18 +- 17 files changed, 757 insertions(+), 697 deletions(-) rename components/mesh_ota/{HTTPS_client.c => HTTPS_Client.c} (99%) rename components/mesh_ota/{Mesh_network.c => Mesh_Network.c} (99%) rename components/mesh_ota/{Mesh_network_handler.c => Mesh_Network_Handler.c} (99%) create mode 100644 components/mesh_ota/Mesh_OTA_Globals.c create mode 100644 components/mesh_ota/Mesh_OTA_Partition_Access.c create mode 100644 components/mesh_ota/Mesh_OTA_Util.c rename components/mesh_ota/include/{HTTPS_client.h => HTTPS_Client.h} (91%) rename components/mesh_ota/include/{Mesh_network.h => Mesh_Network.h} (78%) create mode 100644 components/mesh_ota/include/Mesh_OTA_Access.h create mode 100644 components/mesh_ota/include/Mesh_OTA_Globals.h create mode 100644 components/mesh_ota/include/Mesh_OTA_Util.h diff --git a/components/mesh_ota/CMakeLists.txt b/components/mesh_ota/CMakeLists.txt index 8d5c8bc..78a3b4c 100644 --- a/components/mesh_ota/CMakeLists.txt +++ b/components/mesh_ota/CMakeLists.txt @@ -1,4 +1,4 @@ -idf_component_register(SRCS "HTTPS_client.c" "Mesh_network_handler.c" "Mesh_network.c" "Mesh_OTA.c" +idf_component_register(SRCS "Mesh_OTA_Util.c" "Mesh_Network.c" "Mesh_Network_Handler.c" "HTTPS_Client.c" "Mesh_OTA_Partition_Access.c" "Mesh_OTA_Globals.c" "Mesh_OTA.c" INCLUDE_DIRS "include" REQUIRES nvs_flash esp_http_client diff --git a/components/mesh_ota/HTTPS_client.c b/components/mesh_ota/HTTPS_Client.c similarity index 99% rename from components/mesh_ota/HTTPS_client.c rename to components/mesh_ota/HTTPS_Client.c index 6518925..41d847b 100644 --- a/components/mesh_ota/HTTPS_client.c +++ b/components/mesh_ota/HTTPS_Client.c @@ -1,4 +1,4 @@ -#include "HTTPS_client.h" +#include "HTTPS_Client.h" static const char *TAG = "https_client"; diff --git a/components/mesh_ota/Mesh_network.c b/components/mesh_ota/Mesh_Network.c similarity index 99% rename from components/mesh_ota/Mesh_network.c rename to components/mesh_ota/Mesh_Network.c index 4217dd5..f04fc46 100644 --- a/components/mesh_ota/Mesh_network.c +++ b/components/mesh_ota/Mesh_Network.c @@ -1,5 +1,5 @@ -#include "Mesh_OTA.h" +#include "Mesh_Network.h" static const char *LOG_TAG = "mesh_network"; static uint8_t tx_buf[CONFIG_MESH_MESSAGE_SIZE] = { 0, }; diff --git a/components/mesh_ota/Mesh_network_handler.c b/components/mesh_ota/Mesh_Network_Handler.c similarity index 99% rename from components/mesh_ota/Mesh_network_handler.c rename to components/mesh_ota/Mesh_Network_Handler.c index 3f5e2f7..791cd71 100644 --- a/components/mesh_ota/Mesh_network_handler.c +++ b/components/mesh_ota/Mesh_Network_Handler.c @@ -1,5 +1,5 @@ -#include "Mesh_OTA.h" +#include "Mesh_Network.h" static const char *LOG_TAG = "mesh_network_handler"; diff --git a/components/mesh_ota/Mesh_OTA.c b/components/mesh_ota/Mesh_OTA.c index bef1e0b..6292756 100644 --- a/components/mesh_ota/Mesh_OTA.c +++ b/components/mesh_ota/Mesh_OTA.c @@ -1,16 +1,10 @@ #include "Mesh_OTA.h" +#include "Mesh_OTA_Util.h" +#include "Mesh_OTA_Globals.h" +#include "Mesh_OTA_Access.h" static const char *LOG_TAG = "mesh_ota"; -xQueueHandle queueNodes; //nodes that should be checked for ota update (contains children and parent) -xQueueHandle queueMessageOTA; //mesh ota controll messages like "OTA_Version_Response" "OTA_ACK" - -SemaphoreHandle_t bsStartStopServerWorker; //binary semaphore -SemaphoreHandle_t bsOTAProcess; //binary semaphore - -const esp_partition_t* pOTAPartition; //pointer to ota partition -bool bWantReboot; //flag to signal pending reboot - esp_err_t errMeshOTAInitialize() { esp_err_t err = ESP_OK; @@ -105,75 +99,6 @@ esp_err_t errMeshOTAInitialize() return err; } -void vAddNodeToPossibleUpdatableQueue(uint8_t* pu8MAC) -{ - //send payload to node queues - mesh_addr_t addrNode; - memcpy(&addrNode.addr, (uint8_t *)pu8MAC, 6); //copy MAC - - if (xQueueSend(queueNodes, &addrNode, portMAX_DELAY) != pdPASS) - { - ESP_LOGE(LOG_TAG, "Unable to push node into node queue"); - } - else - { - ESP_LOGI(LOG_TAG, "added node \"%x:%x:%x:%x:%x:%x\" to possible updatable queue", addrNode.addr[0], addrNode.addr[1], addrNode.addr[2], addrNode.addr[3], addrNode.addr[4], addrNode.addr[5]); - } -} - -void vAddOtaMessageToQueue(MESH_PACKET_t* puMeshPacket) -{ - //send ota packet to packet queue - if (xQueueSend(queueMessageOTA, puMeshPacket, portMAX_DELAY) != pdPASS) - { - ESP_LOGE(LOG_TAG, "Unable to push ota packet into packet queue"); - } - else - { - switch (puMeshPacket->type) - { - case OTA_Abort: - ESP_LOGI(LOG_TAG, "added ota message to queue: OTA_Abort from 0x%x", puMeshPacket->meshSenderAddr.addr[5]); - break; - case OTA_Version_Request: - ESP_LOGI(LOG_TAG, "added ota message to queue: OTA_Version_Request from 0x%x", puMeshPacket->meshSenderAddr.addr[5]); - break; - - case OTA_Version_Response: - ESP_LOGI(LOG_TAG, "added ota message to queue: OTA_Version Response from 0x%x", puMeshPacket->meshSenderAddr.addr[5]); - break; - default: - break; - } - } -} - -void vChangeStateOfServerWorker(bool bState) //allow access via function ptn to networl_handler -{ - static bool bLastState = false; - - if(bState != bLastState) //change only if necessary - { - ESP_LOGI(LOG_TAG, "server worker change handler"); - - if(bState == true) - { - if (xSemaphoreGive(bsStartStopServerWorker) != pdTRUE) - { - ESP_LOGE(LOG_TAG, "Unable to give mutex to activate the server worker"); - } - } - else - { - if (xSemaphoreTake(bsStartStopServerWorker,( TickType_t ) 10 ) != pdTRUE) - { - ESP_LOGE(LOG_TAG, "Unable to obtain mutex to deactivate the server worker"); - } - } - bLastState = bState; - } -} - void vTaskServerWorker(void *arg) { esp_err_t err; @@ -272,84 +197,6 @@ void vTaskOTAWorker(void *arg) } } -esp_err_t errOTAHTTPS(bool* pbNewOTAImage) -{ - esp_err_t err = ESP_OK; - char u8OTABuffer[OTA_HTTPS_SEGMENT_SIZE]; //store image segment from server before ota write - uint32_t u32BufferLenght = OTA_HTTPS_SEGMENT_SIZE; //size of buffer - uint32_t u32BytesRead = 0; //number of bytes that are read from server, <= u32BufferLenght - char pcRemoteVersionNumber[12]; //string for version number in server image - const esp_partition_t* pBootPartition; //pointer to boot partition (that will booted after reset) - static esp_ota_handle_t otaHandle; //OTA process handle - uint32_t u32StartOffset = 0U; //start offset for image (exclude the http response data) - esp_app_desc_t bootPartitionDesc; //Metadate from boot partition - uint32_t u32OTABytesWritten = 0U; //counter unsed for progress log - - ERROR_CHECK(errHTTPSClientRetrieveData(u8OTABuffer, &u32BufferLenght, &u32BytesRead)); //read first bytes if image, including the version - - ERROR_CHECK(errExtractVersionNumber(u8OTABuffer, &u32BytesRead, pcRemoteVersionNumber)); //extract version numbers - - if(err == ESP_OK) //check if version number is found - { - xSemaphoreTake(bsOTAProcess, portMAX_DELAY); //wait for binary semaphore that allows to start the OTA process - - pBootPartition = esp_ota_get_boot_partition(); //get boot partition (that will booted after reset), not the running partition - ERROR_CHECK(esp_ota_get_partition_description(pBootPartition, &bootPartitionDesc)); //get metadata of partition - - if(bNewerVersion((bootPartitionDesc).version, pcRemoteVersionNumber)) //compare local and remote version - { - // server image is newer --> OTA update required - ESP_LOGI(LOG_TAG, "Server: image is newer --> OTA update required"); - - ERROR_CHECK(errFindImageStart(u8OTABuffer, &u32BufferLenght, &u32StartOffset)); //get image start offset - - ERROR_CHECK(esp_ota_begin(pOTAPartition, OTA_SIZE_UNKNOWN, &otaHandle)); //start ota update process - if(err == ESP_OK) - { - //image download and ota partition write - ESP_LOGI(LOG_TAG, "start OTA download via HTTPS"); - do - { - vPrintOTAProgress(&(pOTAPartition->size), &u32OTABytesWritten, Receiver); - ERROR_CHECK(esp_ota_write(otaHandle, (const void*) u8OTABuffer+u32StartOffset, (u32BytesRead-u32StartOffset))); - - if(err == ESP_OK) - { - //write was succsesfull - u32StartOffset = 0U; //reset the offset for next download - ERROR_CHECK(errHTTPSClientRetrieveData(u8OTABuffer, &u32BufferLenght, &u32BytesRead)); //download next data segment - u32OTABytesWritten = u32OTABytesWritten + u32BytesRead; //update counter - } - } - while ((u32BytesRead > 0) && (err == ESP_OK) && (u32OTABytesWritten <= pOTAPartition->size)); //loop until error or complete image downloaded - } - - if(err == ESP_OK) - { - //no error occurred --> finish ota update process - ERROR_CHECK(esp_ota_end(otaHandle)); //finish process - ERROR_CHECK(esp_ota_set_boot_partition(pOTAPartition)); //set new image as boot - if(err == ESP_OK) - { - *pbNewOTAImage = true; //image validated - } - } - else - { - //error occurred --> abort ota update process - ESP_LOGE(LOG_TAG, "abort ota process due to error 0x%x -> %s", err, esp_err_to_name(err)); - ERROR_CHECK(esp_ota_abort(otaHandle)); - *pbNewOTAImage = false; //ota update failed - } - } - else - { - ESP_LOGI(LOG_TAG, "server image is NOT newer --> OTA update NOT required"); - } - xSemaphoreGive(bsOTAProcess); //free binary semaphore, this allows other tasks to start the OTA process - } //end version number extracted - return err; -} esp_err_t errOTAMeshSlave(bool* pbNewOTAImage) { @@ -476,481 +323,3 @@ esp_err_t errOTAMeshMaster(bool* pbNewOTAImage, mesh_addr_t* pMeshNodeAddr) } return err; } - -bool bNewerVersion(const char* pu8Local, const char* pu8Remote) -{ - /* - * Return true if remote version is newer (higher) than local version - */ - char u8LocalTmp[12]; //local version - char u8RemoteTmp[12]; //remote version - char* pu8saveptrLocal; //context for strok_r - char* pu8saveptrRemote; //context for strok_r - bool bReturn = false; //flag to stop loop - uint8_t u8Index = 0; //numbers counter in version string - - strncpy(u8LocalTmp, pu8Local, 12); //copy in tmp - strncpy(u8RemoteTmp, pu8Remote, 12); //copy in tmp - - char* pu8TokenLocal = strtok_r(u8LocalTmp, ".", &pu8saveptrLocal); //split tokens - char* pu8TokenRemote = strtok_r(u8RemoteTmp, ".", &pu8saveptrRemote); //split tokens - - while( (u8Index <= 2) && (bReturn == false)) //loop through tokens - { - u8Index++; - if(atoi(pu8TokenLocal) < atoi(pu8TokenRemote)) - { - bReturn = true; //version number difference --> stop loop - } - pu8TokenLocal = strtok_r(NULL, ".", &pu8saveptrLocal); //split tokens - pu8TokenRemote = strtok_r(NULL, ".", &pu8saveptrRemote); //split tokens - } - return bReturn; -} - -esp_err_t errFindImageStart(const char* pu8Data, uint32_t* pu32DataLenght, uint32_t* pu32StartOffset) -{ - /* - Offset value - 0 = 0xE9 (first byte in image --> magic byte) - 48 = first digit of version number - */ - - esp_err_t errReturn = ESP_OK; - bool bImageStartOffsetFound = false; - uint32_t u32DataIndex = 0; - uint32_t u32FirstDotOffset = 0; - uint32_t u32SecondDotOffset = 0; - uint8_t u8FirstDotIndex = 0; - uint8_t u8SecondDotIndex = 0; - - *pu32StartOffset = 0U; //reset offset to zero - - while((u32DataIndex < *pu32DataLenght) && (bImageStartOffsetFound == false)) - { - //search for magic byte - if(pu8Data[u32DataIndex] == 0xe9) - { - //magic byte found - while ((u8FirstDotIndex < 3) && (u32FirstDotOffset == 0)) - { - //search first dot in version number - if((u32DataIndex+49+u8FirstDotIndex) < *pu32DataLenght) - { - if((pu8Data[(u32DataIndex+49+u8FirstDotIndex)] == 0x2e)) - { - //first dot found - u32FirstDotOffset = (u32DataIndex+49+u8FirstDotIndex); - } - } - u8FirstDotIndex++; - } - - while ((u8SecondDotIndex < 3) && (u32SecondDotOffset == 0) && (u32FirstDotOffset != 0)) - { - //search first dot in version number - if((u32FirstDotOffset+(u8SecondDotIndex+2)) < *pu32DataLenght) - { - if((pu8Data[(u32FirstDotOffset+(u8SecondDotIndex+2))] == 0x2e)) - { - //second dot found - u32SecondDotOffset = (u32FirstDotOffset+(u8SecondDotIndex+2)); - } - } - u8SecondDotIndex++; - } - - if((u32FirstDotOffset != 0) && (u32SecondDotOffset != 0)) - { - //image start found based on magic byte and version number systax - *pu32StartOffset = u32DataIndex; //store image start offset - bImageStartOffsetFound = true; - } - else - { - // this is propably not the magic byte --> reset - u32FirstDotOffset = 0; - u32SecondDotOffset = 0; - u8FirstDotIndex = 0; - u8SecondDotIndex = 0; - } - } - u32DataIndex++; - } - - if(bImageStartOffsetFound == false) - { - errReturn = ESP_ERR_NOT_FOUND; - } - - return errReturn; -} - -esp_err_t errExtractVersionNumber(const char* pu8Data, uint32_t* pu32DataLenght, char* pc8RemoteVersionNumber) -{ - uint32_t u32StartOffset; - esp_err_t err = ESP_OK; - - strcpy(pc8RemoteVersionNumber, "999.999.999"); //init value - err = errFindImageStart(pu8Data, pu32DataLenght, &u32StartOffset); //get image start offset - - if(err == ESP_OK) - { - //image found - strncpy(pc8RemoteVersionNumber, pu8Data+(u32StartOffset+48), 11); //copy version number - pc8RemoteVersionNumber[12] = '\0'; - } - return err; -} - -void vPrintOTAProgress(const uint32_t* const pu32TotalImageSize, const uint32_t* const pu32BytesWritten, OTA_MESH_ROLE_t eRole) -{ - uint32_t u32Percentage = 0U; - static uint32_t u32LastPercentage = 0U; - - if((*pu32BytesWritten) >= (*pu32TotalImageSize)) - { - u32Percentage = 100; - } - else - { - u32Percentage = (uint32_t) (((float) (*pu32BytesWritten)/(float) (*pu32TotalImageSize)) * 100.0); - } - - if((u32Percentage-u32LastPercentage) >= OTA_PROGRESS_LOG_INTERVAL) - { - if(eRole == Transmitter) - { - ESP_LOGI(LOG_TAG, "Transmitting OTA update: %i %%", u32Percentage); - } - - if(eRole == Receiver) - { - ESP_LOGI(LOG_TAG, "Receiving OTA update: %i %%", u32Percentage); - } - - - - u32LastPercentage = u32Percentage; - } -} - -void vAddAllNeighboursToQueue(void) -{ - esp_err_t err = ESP_OK; - - mesh_addr_t addrParent; //addr of parent node - mesh_addr_t childrenAddr[CONFIG_MESH_ROUTE_TABLE_SIZE]; //array of children attached to this node - uint16_t u16ChildrenSize = 0U; //number of children attached to this node - - err = errGetParentNode(&addrParent); - - if(err == ESP_OK) - { - vAddNodeToPossibleUpdatableQueue(addrParent.addr); - ESP_LOGI(LOG_TAG, "added parent"); - } - - err = ESP_OK; //reset error code - - ERROR_CHECK(errGetChildren(childrenAddr, &u16ChildrenSize)); //get all children - - for (uint16_t u16Index = 0; ((u16Index < u16ChildrenSize) && (err == ESP_OK)); u16Index++) - { - vAddNodeToPossibleUpdatableQueue(childrenAddr[u16Index].addr); - ESP_LOGI(LOG_TAG, "added child"); - } -} - -esp_err_t errSendOTAVersionResponse(mesh_addr_t* pMeshReceiverAddr) -{ - esp_err_t err = ESP_OK; - MESH_PACKET_t packet; - packet.type = OTA_Version_Response; - - const esp_partition_t* pBootPartition; //pointer to boot partition (that will booted after reset) - esp_app_desc_t bootPartitionDesc; //Metadata from boot partition - - pBootPartition = esp_ota_get_boot_partition(); //get boot partition (that will booted after reset), not the running partition - ERROR_CHECK(esp_ota_get_partition_description(pBootPartition, &bootPartitionDesc)); //get metadate of partition - memcpy(&packet.au8Payload, &bootPartitionDesc.version, 12); //copy local version to OTA_Version_Response packet - - ESP_LOGI(LOG_TAG, "Send OTA_Version_Response to 0x%x", pMeshReceiverAddr->addr[5]); - - err = errSendMeshPacket(pMeshReceiverAddr, &packet); - return err; -} - -void vClearOtaMessageQueue(mesh_addr_t* pMeshNodeAddr) -{ - MESH_PACKET_t sMeshPacket; //packet for sending and receiving - for (uint32_t u32Index = 0; (u32Index < QUEUE_MESSAGE_OTA_SIZE); u32Index++) //loop through all OTA messages - { - if (xQueueReceive(queueMessageOTA, &sMeshPacket, 0) == pdTRUE) - { - if(!(bCheckMACEquality(sMeshPacket.meshSenderAddr.addr, pMeshNodeAddr->addr))) - { - //received OTA message is NOT from pMeshNodeAddr --> keep it in queue - vAddOtaMessageToQueue(&sMeshPacket); - } - else - { - ESP_LOGI(LOG_TAG, "Removed type %i from node 0x%x", sMeshPacket.type, pMeshNodeAddr->addr[5]); - } - } - }//end OTA message loop -} - -esp_err_t errSendOTAVersionRequest(mesh_addr_t* pMeshReceiverAddr) -{ - esp_err_t err = ESP_OK; - MESH_PACKET_t packet; - packet.type = OTA_Version_Request; - - const esp_partition_t* pBootPartition; //pointer to boot partition (that will booted after reset) - esp_app_desc_t bootPartitionDesc; //Metadata from boot partition - - pBootPartition = esp_ota_get_boot_partition(); //get boot partition (that will booted after reset), not the running partition - ERROR_CHECK(esp_ota_get_partition_description(pBootPartition, &bootPartitionDesc)); //get metadate of partition - memcpy(&packet.au8Payload, &bootPartitionDesc.version, 12); //copy local version to OTA_Version_Request packet - err = errSendMeshPacket(pMeshReceiverAddr, &packet); - return err; -} - -esp_err_t errOTAMeshTransmit(mesh_addr_t* pMeshNodeAddr) -{ - esp_err_t err = ESP_OK; - const esp_partition_t* pBootPartition; //pointer to boot partition (that will booted after reset) - MESH_PACKET_t sMeshPacket; //packet for sending and receiving - // uint32_t u32Index = 0U; //index for partition read offset - bool bAbort = false; //abort the OTA process - bool bNodeIsResponding = false; //remote node is still active - uint32_t u32OTABytesWritten = 0U; - uint32_t u32SegmentCounter = 0U; - - pBootPartition = esp_ota_get_boot_partition(); //get boot partition (that will booted after reset), not the running partition - - //loop through partition to read in segmensts until end or error or abort called - while( ((OTA_MESH_SEGMENT_SIZE * u32SegmentCounter) < pBootPartition->size) && (err == ESP_OK) && (bAbort == false)) - { - bNodeIsResponding = false; //reset to default - - // read partition with offset based in index - ERROR_CHECK(esp_partition_read(pBootPartition, (OTA_MESH_SEGMENT_SIZE * u32SegmentCounter), sMeshPacket.au8Payload, OTA_MESH_SEGMENT_SIZE)); - u32OTABytesWritten = ((u32SegmentCounter+1) * OTA_MESH_SEGMENT_SIZE); - vPrintOTAProgress(&(pBootPartition->size), &u32OTABytesWritten, Transmitter); - - if(err == ESP_OK) - { - //no error while read --> send OTA_DATA packet - sMeshPacket.type = OTA_Data; - - if((OTA_MESH_SEGMENT_SIZE * (u32SegmentCounter+1)) >= pBootPartition->size) //check if last segment - { - //last partition image segment --> send OTA_Complete - ESP_LOGI(LOG_TAG, "OTA-TX: last segment--> send Complete"); - sMeshPacket.type = OTA_Complete; - } - //ESP_LOGI(LOG_TAG, "OTA-TX: send packet"); - err = errSendMeshPacket(pMeshNodeAddr, &sMeshPacket); - } - else - { - // error while read --> send OTA_ABORT and abort this OTA process - sMeshPacket.type = OTA_Abort; - bAbort = true; - ESP_LOGI(LOG_TAG, "OTA-TX: error while read --> send ABORT"); - errSendMeshPacket(pMeshNodeAddr, &sMeshPacket); - } - - // loop through all OTA messages or until abort is called or error - for (uint32_t u32Index = 0; ((u32Index < QUEUE_MESSAGE_OTA_SIZE) && (bAbort == false) && (err == ESP_OK)); u32Index++) //loop through all OTA messages - { - // if(uxQueueSpacesAvailable(queueMessageOTA) < QUEUE_MESSAGE_OTA_SIZE) - // { - //queue not empty - if (xQueueReceive(queueMessageOTA, &sMeshPacket, ((OTA_MESH_TIMEOUT) / portTICK_PERIOD_MS)) != pdTRUE) - { - ESP_LOGE(LOG_TAG, "Unable to receive OTA Messages from queue"); - err = ESP_FAIL; - } - - if((err == ESP_OK) && (bCheckMACEquality(sMeshPacket.meshSenderAddr.addr, pMeshNodeAddr->addr))) //if OTA_Version_Request - { - //packet from node received - switch (sMeshPacket.type) - { - case OTA_ACK: //increase index for next round - u32Index++; - bNodeIsResponding = true; - u32Index = QUEUE_MESSAGE_OTA_SIZE;//this will end the loop through all OTA messages - break; - case OTA_Abort: //abort this OTA process - bAbort = true; - bNodeIsResponding = true; - break; - default: - //receives wrong OTA message type from node --> back to queue - //vAddOtaMessageToQueue(&sMeshPacket); - break; - } - } - else if (err == ESP_OK) - { - //received from wrong node --> back to queue - vAddOtaMessageToQueue(&sMeshPacket); - } - /* - } - else - { - // OTA Message queue is empty --> wait some time - ESP_LOGI(LOG_TAG, "OTA-TX: ota message queue empty --> wait"); - vTaskDelay( (OTA_MESH_TIMEOUT) / portTICK_PERIOD_MS); - } - */ - - }//end OTA message loop - - if(bNodeIsResponding == false) - { - //no abort was called but node didn’t responded - ESP_LOGE(LOG_TAG, "OTA-TX: no abort was called but node didn’t responded --> error"); - bAbort = true; - err = ESP_FAIL; //this OTA process failed with error - } - u32SegmentCounter++; - }//end of partition segment loop - vClearOtaMessageQueue(pMeshNodeAddr); - return err; -} - -esp_err_t errOTAMeshReceive(bool* pbNewOTAImage, mesh_addr_t* pMeshNodeAddr) -{ - esp_err_t err = ESP_OK; - MESH_PACKET_t sMeshPacket; //packet for sending and receiving - bool bComplete = false; //complete the OTA process - bool bAbort = false; //abort the OTA process - bool bNodeIsResponding = false; //remote node is still active - uint32_t u32OTABytesWritten = 0U; //counter unsed for progress log - static esp_ota_handle_t otaHandle; //OTA process handle - *pbNewOTAImage = false; - uint32_t u32SegmentCounter = 0U; - - ERROR_CHECK(esp_ota_begin(pOTAPartition, OTA_SIZE_UNKNOWN, &otaHandle)); //start ota update process - - //partition segement loop through partition to read in segmensts until end or error or abort called - while((bComplete == false) && (err == ESP_OK) && (bAbort == false) && (u32OTABytesWritten <= pOTAPartition->size)) - { - bNodeIsResponding = false; //reset to default - - // loop through all OTA messages or until abort is called - for (uint32_t u32Index = 0; ((u32Index < QUEUE_MESSAGE_OTA_SIZE) && (bAbort == false)); u32Index++) //loop through all OTA messages - { - //if(uxQueueSpacesAvailable(queueMessageOTA) < QUEUE_MESSAGE_OTA_SIZE) - // { - //queue not empty - if (xQueueReceive(queueMessageOTA, &sMeshPacket, ((OTA_MESH_TIMEOUT) / portTICK_PERIOD_MS)) != pdTRUE) - { - ESP_LOGE(LOG_TAG, "Unable to receive OTA Messages from queue"); - err = ESP_FAIL; - } - - if((err == ESP_OK) && (bCheckMACEquality(sMeshPacket.meshSenderAddr.addr, pMeshNodeAddr->addr))) //if OTA_Version_Request - { - //packet from node received - switch (sMeshPacket.type) - { - case OTA_Complete: //signal end of this OTA process, fall through because same behavior as OTA_Data - bComplete = true; - ESP_LOGI(LOG_TAG, "OTA-RX: rec Complete --> last segment"); - //fall through - case OTA_Data: //data segement received - bNodeIsResponding = true; - ERROR_CHECK(esp_ota_write(otaHandle, sMeshPacket.au8Payload, OTA_MESH_SEGMENT_SIZE)); - u32OTABytesWritten = ((u32SegmentCounter+1) * OTA_MESH_SEGMENT_SIZE); - vPrintOTAProgress(&(pOTAPartition->size), &u32OTABytesWritten, Receiver); - u32Index = QUEUE_MESSAGE_OTA_SIZE; //this will end the loop through all OTA messages - break; - case OTA_Abort: //abort this OTA process - bAbort = true; - bNodeIsResponding = true; - ESP_LOGI(LOG_TAG, "OTA-RX: rec Abort"); - //this will end the loop through all OTA messages - break; - default: - //receives wrong OTA message type from node --> back to queue - //vAddOtaMessageToQueue(&sMeshPacket); - break; - } - } - else if (err == ESP_OK) - { - //received from wrong node --> back to queue - vAddOtaMessageToQueue(&sMeshPacket); - } - - /* } - else - { - ESP_LOGI(LOG_TAG, "OTA-RX: ota message queue empty --> wait"); - // OTA Message queue is empty --> wait some time - vTaskDelay( (OTA_MESH_TIMEOUT) / portTICK_PERIOD_MS); - } - */ - - }//end of OTA message loop - - if(bNodeIsResponding == false) - { - //no abort was called but node didn’t responded --> error - ESP_LOGI(LOG_TAG, "OTA-RX: no abort was called but node didn’t responded --> error"); - bAbort = true; //this will stop the partition segement loop - err = ESP_FAIL; //this OTA process failed with error - } - else - { - //node has responded with OTA_DATA or OTA_Complete or OTA_ABORT - if(err == ESP_OK) - { - - if(bAbort == false) - { - //no error while ota write --> send OTA_ACK packet - //ESP_LOGI(LOG_TAG, "OTA-RX: no error while ota write --> send OTA_ACK packet"); - sMeshPacket.type = OTA_ACK; - err = errSendMeshPacket(pMeshNodeAddr, &sMeshPacket); - } - } - else - { - // error while read --> send OTA_ABORT and abort this OTA process - sMeshPacket.type = OTA_Abort; - bAbort = true; - ESP_LOGI(LOG_TAG, "OTA-RX: abort --> send ABORT"); - errSendMeshPacket(pMeshNodeAddr, &sMeshPacket); - } - } - u32SegmentCounter++; - }//end of partition segement loop - - if(bComplete == true) - { - //all OTA segments received --> validate - ESP_LOGI(LOG_TAG, "OTA-RX: validate image "); - ERROR_CHECK(esp_ota_end(otaHandle)); - ERROR_CHECK(esp_ota_set_boot_partition(pOTAPartition)); - if(err == ESP_OK) - { - //successfully updated OTA partition - *pbNewOTAImage = true; - } - } - else - { - //not all OTA segments received --> abort this OTA process - ERROR_CHECK(esp_ota_abort(otaHandle)); - } - vClearOtaMessageQueue(pMeshNodeAddr); - return err; -} diff --git a/components/mesh_ota/Mesh_OTA_Globals.c b/components/mesh_ota/Mesh_OTA_Globals.c new file mode 100644 index 0000000..890a888 --- /dev/null +++ b/components/mesh_ota/Mesh_OTA_Globals.c @@ -0,0 +1,10 @@ +#include "Mesh_OTA_Globals.h" + +xQueueHandle queueNodes; //nodes that should be checked for ota update (contains children and parent) +xQueueHandle queueMessageOTA; //mesh ota controll messages like "OTA_Version_Response" "OTA_ACK" + +SemaphoreHandle_t bsStartStopServerWorker; //binary semaphore +SemaphoreHandle_t bsOTAProcess; //binary semaphore + +const esp_partition_t* pOTAPartition; //pointer to ota partition +bool bWantReboot; //flag to signal pending reboot diff --git a/components/mesh_ota/Mesh_OTA_Partition_Access.c b/components/mesh_ota/Mesh_OTA_Partition_Access.c new file mode 100644 index 0000000..6776c26 --- /dev/null +++ b/components/mesh_ota/Mesh_OTA_Partition_Access.c @@ -0,0 +1,323 @@ +#include "Mesh_OTA.h" +#include "Mesh_OTA_Util.h" +#include "Mesh_OTA_Globals.h" +#include "Mesh_OTA_Access.h" + +static const char *LOG_TAG = "mesh_ota_access"; + +esp_err_t errOTAMeshTransmit(mesh_addr_t* pMeshNodeAddr) +{ + esp_err_t err = ESP_OK; + const esp_partition_t* pBootPartition; //pointer to boot partition (that will booted after reset) + MESH_PACKET_t sMeshPacket; //packet for sending and receiving + // uint32_t u32Index = 0U; //index for partition read offset + bool bAbort = false; //abort the OTA process + bool bNodeIsResponding = false; //remote node is still active + uint32_t u32OTABytesWritten = 0U; + uint32_t u32SegmentCounter = 0U; + + pBootPartition = esp_ota_get_boot_partition(); //get boot partition (that will booted after reset), not the running partition + + //loop through partition to read in segmensts until end or error or abort called + while( ((OTA_MESH_SEGMENT_SIZE * u32SegmentCounter) < pBootPartition->size) && (err == ESP_OK) && (bAbort == false)) + { + bNodeIsResponding = false; //reset to default + + // read partition with offset based in index + ERROR_CHECK(esp_partition_read(pBootPartition, (OTA_MESH_SEGMENT_SIZE * u32SegmentCounter), sMeshPacket.au8Payload, OTA_MESH_SEGMENT_SIZE)); + u32OTABytesWritten = ((u32SegmentCounter+1) * OTA_MESH_SEGMENT_SIZE); + vPrintOTAProgress(&(pBootPartition->size), &u32OTABytesWritten, Transmitter); + + if(err == ESP_OK) + { + //no error while read --> send OTA_DATA packet + sMeshPacket.type = OTA_Data; + + if((OTA_MESH_SEGMENT_SIZE * (u32SegmentCounter+1)) >= pBootPartition->size) //check if last segment + { + //last partition image segment --> send OTA_Complete + ESP_LOGI(LOG_TAG, "OTA-TX: last segment--> send Complete"); + sMeshPacket.type = OTA_Complete; + } + //ESP_LOGI(LOG_TAG, "OTA-TX: send packet"); + err = errSendMeshPacket(pMeshNodeAddr, &sMeshPacket); + } + else + { + // error while read --> send OTA_ABORT and abort this OTA process + sMeshPacket.type = OTA_Abort; + bAbort = true; + ESP_LOGI(LOG_TAG, "OTA-TX: error while read --> send ABORT"); + errSendMeshPacket(pMeshNodeAddr, &sMeshPacket); + } + + // loop through all OTA messages or until abort is called or error + for (uint32_t u32Index = 0; ((u32Index < QUEUE_MESSAGE_OTA_SIZE) && (bAbort == false) && (err == ESP_OK)); u32Index++) //loop through all OTA messages + { + // if(uxQueueSpacesAvailable(queueMessageOTA) < QUEUE_MESSAGE_OTA_SIZE) + // { + //queue not empty + if (xQueueReceive(queueMessageOTA, &sMeshPacket, ((OTA_MESH_TIMEOUT) / portTICK_PERIOD_MS)) != pdTRUE) + { + ESP_LOGE(LOG_TAG, "Unable to receive OTA Messages from queue"); + err = ESP_FAIL; + } + + if((err == ESP_OK) && (bCheckMACEquality(sMeshPacket.meshSenderAddr.addr, pMeshNodeAddr->addr))) //if OTA_Version_Request + { + //packet from node received + switch (sMeshPacket.type) + { + case OTA_ACK: //increase index for next round + u32Index++; + bNodeIsResponding = true; + u32Index = QUEUE_MESSAGE_OTA_SIZE;//this will end the loop through all OTA messages + break; + case OTA_Abort: //abort this OTA process + bAbort = true; + bNodeIsResponding = true; + break; + default: + //receives wrong OTA message type from node --> back to queue + //vAddOtaMessageToQueue(&sMeshPacket); + break; + } + } + else if (err == ESP_OK) + { + //received from wrong node --> back to queue + vAddOtaMessageToQueue(&sMeshPacket); + } + /* + } + else + { + // OTA Message queue is empty --> wait some time + ESP_LOGI(LOG_TAG, "OTA-TX: ota message queue empty --> wait"); + vTaskDelay( (OTA_MESH_TIMEOUT) / portTICK_PERIOD_MS); + } + */ + + }//end OTA message loop + + if(bNodeIsResponding == false) + { + //no abort was called but node didn’t responded + ESP_LOGE(LOG_TAG, "OTA-TX: no abort was called but node didn’t responded --> error"); + bAbort = true; + err = ESP_FAIL; //this OTA process failed with error + } + u32SegmentCounter++; + }//end of partition segment loop + vClearOtaMessageQueue(pMeshNodeAddr); + return err; +} + +esp_err_t errOTAMeshReceive(bool* pbNewOTAImage, mesh_addr_t* pMeshNodeAddr) +{ + esp_err_t err = ESP_OK; + MESH_PACKET_t sMeshPacket; //packet for sending and receiving + bool bComplete = false; //complete the OTA process + bool bAbort = false; //abort the OTA process + bool bNodeIsResponding = false; //remote node is still active + uint32_t u32OTABytesWritten = 0U; //counter unsed for progress log + static esp_ota_handle_t otaHandle; //OTA process handle + *pbNewOTAImage = false; + uint32_t u32SegmentCounter = 0U; + + ERROR_CHECK(esp_ota_begin(pOTAPartition, OTA_SIZE_UNKNOWN, &otaHandle)); //start ota update process + + //partition segement loop through partition to read in segmensts until end or error or abort called + while((bComplete == false) && (err == ESP_OK) && (bAbort == false) && (u32OTABytesWritten <= pOTAPartition->size)) + { + bNodeIsResponding = false; //reset to default + + // loop through all OTA messages or until abort is called + for (uint32_t u32Index = 0; ((u32Index < QUEUE_MESSAGE_OTA_SIZE) && (bAbort == false)); u32Index++) //loop through all OTA messages + { + //if(uxQueueSpacesAvailable(queueMessageOTA) < QUEUE_MESSAGE_OTA_SIZE) + // { + //queue not empty + if (xQueueReceive(queueMessageOTA, &sMeshPacket, ((OTA_MESH_TIMEOUT) / portTICK_PERIOD_MS)) != pdTRUE) + { + ESP_LOGE(LOG_TAG, "Unable to receive OTA Messages from queue"); + err = ESP_FAIL; + } + + if((err == ESP_OK) && (bCheckMACEquality(sMeshPacket.meshSenderAddr.addr, pMeshNodeAddr->addr))) //if OTA_Version_Request + { + //packet from node received + switch (sMeshPacket.type) + { + case OTA_Complete: //signal end of this OTA process, fall through because same behavior as OTA_Data + bComplete = true; + ESP_LOGI(LOG_TAG, "OTA-RX: rec Complete --> last segment"); + //fall through + case OTA_Data: //data segement received + bNodeIsResponding = true; + ERROR_CHECK(esp_ota_write(otaHandle, sMeshPacket.au8Payload, OTA_MESH_SEGMENT_SIZE)); + u32OTABytesWritten = ((u32SegmentCounter+1) * OTA_MESH_SEGMENT_SIZE); + vPrintOTAProgress(&(pOTAPartition->size), &u32OTABytesWritten, Receiver); + u32Index = QUEUE_MESSAGE_OTA_SIZE; //this will end the loop through all OTA messages + break; + case OTA_Abort: //abort this OTA process + bAbort = true; + bNodeIsResponding = true; + ESP_LOGI(LOG_TAG, "OTA-RX: rec Abort"); + //this will end the loop through all OTA messages + break; + default: + //receives wrong OTA message type from node --> back to queue + //vAddOtaMessageToQueue(&sMeshPacket); + break; + } + } + else if (err == ESP_OK) + { + //received from wrong node --> back to queue + vAddOtaMessageToQueue(&sMeshPacket); + } + + /* } + else + { + ESP_LOGI(LOG_TAG, "OTA-RX: ota message queue empty --> wait"); + // OTA Message queue is empty --> wait some time + vTaskDelay( (OTA_MESH_TIMEOUT) / portTICK_PERIOD_MS); + } + */ + + }//end of OTA message loop + + if(bNodeIsResponding == false) + { + //no abort was called but node didn’t responded --> error + ESP_LOGI(LOG_TAG, "OTA-RX: no abort was called but node didn’t responded --> error"); + bAbort = true; //this will stop the partition segement loop + err = ESP_FAIL; //this OTA process failed with error + } + else + { + //node has responded with OTA_DATA or OTA_Complete or OTA_ABORT + if(err == ESP_OK) + { + + if(bAbort == false) + { + //no error while ota write --> send OTA_ACK packet + //ESP_LOGI(LOG_TAG, "OTA-RX: no error while ota write --> send OTA_ACK packet"); + sMeshPacket.type = OTA_ACK; + err = errSendMeshPacket(pMeshNodeAddr, &sMeshPacket); + } + } + else + { + // error while read --> send OTA_ABORT and abort this OTA process + sMeshPacket.type = OTA_Abort; + bAbort = true; + ESP_LOGI(LOG_TAG, "OTA-RX: abort --> send ABORT"); + errSendMeshPacket(pMeshNodeAddr, &sMeshPacket); + } + } + u32SegmentCounter++; + }//end of partition segement loop + + if(bComplete == true) + { + //all OTA segments received --> validate + ESP_LOGI(LOG_TAG, "OTA-RX: validate image "); + ERROR_CHECK(esp_ota_end(otaHandle)); + ERROR_CHECK(esp_ota_set_boot_partition(pOTAPartition)); + if(err == ESP_OK) + { + //successfully updated OTA partition + *pbNewOTAImage = true; + } + } + else + { + //not all OTA segments received --> abort this OTA process + ERROR_CHECK(esp_ota_abort(otaHandle)); + } + vClearOtaMessageQueue(pMeshNodeAddr); + return err; +} + +esp_err_t errOTAHTTPS(bool* pbNewOTAImage) +{ + esp_err_t err = ESP_OK; + char u8OTABuffer[OTA_HTTPS_SEGMENT_SIZE]; //store image segment from server before ota write + uint32_t u32BufferLenght = OTA_HTTPS_SEGMENT_SIZE; //size of buffer + uint32_t u32BytesRead = 0; //number of bytes that are read from server, <= u32BufferLenght + char pcRemoteVersionNumber[12]; //string for version number in server image + const esp_partition_t* pBootPartition; //pointer to boot partition (that will booted after reset) + static esp_ota_handle_t otaHandle; //OTA process handle + uint32_t u32StartOffset = 0U; //start offset for image (exclude the http response data) + esp_app_desc_t bootPartitionDesc; //Metadate from boot partition + uint32_t u32OTABytesWritten = 0U; //counter unsed for progress log + + ERROR_CHECK(errHTTPSClientRetrieveData(u8OTABuffer, &u32BufferLenght, &u32BytesRead)); //read first bytes if image, including the version + + ERROR_CHECK(errExtractVersionNumber(u8OTABuffer, &u32BytesRead, pcRemoteVersionNumber)); //extract version numbers + + if(err == ESP_OK) //check if version number is found + { + xSemaphoreTake(bsOTAProcess, portMAX_DELAY); //wait for binary semaphore that allows to start the OTA process + + pBootPartition = esp_ota_get_boot_partition(); //get boot partition (that will booted after reset), not the running partition + ERROR_CHECK(esp_ota_get_partition_description(pBootPartition, &bootPartitionDesc)); //get metadata of partition + + if(bNewerVersion((bootPartitionDesc).version, pcRemoteVersionNumber)) //compare local and remote version + { + // server image is newer --> OTA update required + ESP_LOGI(LOG_TAG, "Server: image is newer --> OTA update required"); + + ERROR_CHECK(errFindImageStart(u8OTABuffer, &u32BufferLenght, &u32StartOffset)); //get image start offset + + ERROR_CHECK(esp_ota_begin(pOTAPartition, OTA_SIZE_UNKNOWN, &otaHandle)); //start ota update process + if(err == ESP_OK) + { + //image download and ota partition write + ESP_LOGI(LOG_TAG, "start OTA download via HTTPS"); + do + { + vPrintOTAProgress(&(pOTAPartition->size), &u32OTABytesWritten, Receiver); + ERROR_CHECK(esp_ota_write(otaHandle, (const void*) u8OTABuffer+u32StartOffset, (u32BytesRead-u32StartOffset))); + + if(err == ESP_OK) + { + //write was succsesfull + u32StartOffset = 0U; //reset the offset for next download + ERROR_CHECK(errHTTPSClientRetrieveData(u8OTABuffer, &u32BufferLenght, &u32BytesRead)); //download next data segment + u32OTABytesWritten = u32OTABytesWritten + u32BytesRead; //update counter + } + } + while ((u32BytesRead > 0) && (err == ESP_OK) && (u32OTABytesWritten <= pOTAPartition->size)); //loop until error or complete image downloaded + } + + if(err == ESP_OK) + { + //no error occurred --> finish ota update process + ERROR_CHECK(esp_ota_end(otaHandle)); //finish process + ERROR_CHECK(esp_ota_set_boot_partition(pOTAPartition)); //set new image as boot + if(err == ESP_OK) + { + *pbNewOTAImage = true; //image validated + } + } + else + { + //error occurred --> abort ota update process + ESP_LOGE(LOG_TAG, "abort ota process due to error 0x%x -> %s", err, esp_err_to_name(err)); + ERROR_CHECK(esp_ota_abort(otaHandle)); + *pbNewOTAImage = false; //ota update failed + } + } + else + { + ESP_LOGI(LOG_TAG, "server image is NOT newer --> OTA update NOT required"); + } + xSemaphoreGive(bsOTAProcess); //free binary semaphore, this allows other tasks to start the OTA process + } //end version number extracted + return err; +} \ No newline at end of file diff --git a/components/mesh_ota/Mesh_OTA_Util.c b/components/mesh_ota/Mesh_OTA_Util.c new file mode 100644 index 0000000..7a955a3 --- /dev/null +++ b/components/mesh_ota/Mesh_OTA_Util.c @@ -0,0 +1,314 @@ +#include "Mesh_OTA_Util.h" +#include "Mesh_OTA_Globals.h" + +static const char *LOG_TAG = "mesh_ota"; + +void vAddNodeToPossibleUpdatableQueue(uint8_t* pu8MAC) +{ + //send payload to node queues + mesh_addr_t addrNode; + memcpy(&addrNode.addr, (uint8_t *)pu8MAC, 6); //copy MAC + + if (xQueueSend(queueNodes, &addrNode, portMAX_DELAY) != pdPASS) + { + ESP_LOGE(LOG_TAG, "Unable to push node into node queue"); + } + else + { + ESP_LOGI(LOG_TAG, "added node \"%x:%x:%x:%x:%x:%x\" to possible updatable queue", addrNode.addr[0], addrNode.addr[1], addrNode.addr[2], addrNode.addr[3], addrNode.addr[4], addrNode.addr[5]); + } +} + +void vAddOtaMessageToQueue(MESH_PACKET_t* puMeshPacket) +{ + //send ota packet to packet queue + if (xQueueSend(queueMessageOTA, puMeshPacket, portMAX_DELAY) != pdPASS) + { + ESP_LOGE(LOG_TAG, "Unable to push ota packet into packet queue"); + } + else + { + switch (puMeshPacket->type) + { + case OTA_Abort: + ESP_LOGI(LOG_TAG, "added ota message to queue: OTA_Abort from 0x%x", puMeshPacket->meshSenderAddr.addr[5]); + break; + case OTA_Version_Request: + ESP_LOGI(LOG_TAG, "added ota message to queue: OTA_Version_Request from 0x%x", puMeshPacket->meshSenderAddr.addr[5]); + break; + + case OTA_Version_Response: + ESP_LOGI(LOG_TAG, "added ota message to queue: OTA_Version Response from 0x%x", puMeshPacket->meshSenderAddr.addr[5]); + break; + default: + break; + } + } +} + +void vChangeStateOfServerWorker(bool bState) //allow access via function ptn to networl_handler +{ + static bool bLastState = false; + + if(bState != bLastState) //change only if necessary + { + ESP_LOGI(LOG_TAG, "server worker change handler"); + + if(bState == true) + { + if (xSemaphoreGive(bsStartStopServerWorker) != pdTRUE) + { + ESP_LOGE(LOG_TAG, "Unable to give mutex to activate the server worker"); + } + } + else + { + if (xSemaphoreTake(bsStartStopServerWorker,( TickType_t ) 10 ) != pdTRUE) + { + ESP_LOGE(LOG_TAG, "Unable to obtain mutex to deactivate the server worker"); + } + } + bLastState = bState; + } +} + +bool bNewerVersion(const char* pu8Local, const char* pu8Remote) +{ + /* + * Return true if remote version is newer (higher) than local version + */ + char u8LocalTmp[12]; //local version + char u8RemoteTmp[12]; //remote version + char* pu8saveptrLocal; //context for strok_r + char* pu8saveptrRemote; //context for strok_r + bool bReturn = false; //flag to stop loop + uint8_t u8Index = 0; //numbers counter in version string + + strncpy(u8LocalTmp, pu8Local, 12); //copy in tmp + strncpy(u8RemoteTmp, pu8Remote, 12); //copy in tmp + + char* pu8TokenLocal = strtok_r(u8LocalTmp, ".", &pu8saveptrLocal); //split tokens + char* pu8TokenRemote = strtok_r(u8RemoteTmp, ".", &pu8saveptrRemote); //split tokens + + while( (u8Index <= 2) && (bReturn == false)) //loop through tokens + { + u8Index++; + if(atoi(pu8TokenLocal) < atoi(pu8TokenRemote)) + { + bReturn = true; //version number difference --> stop loop + } + pu8TokenLocal = strtok_r(NULL, ".", &pu8saveptrLocal); //split tokens + pu8TokenRemote = strtok_r(NULL, ".", &pu8saveptrRemote); //split tokens + } + return bReturn; +} + +esp_err_t errFindImageStart(const char* pu8Data, uint32_t* pu32DataLenght, uint32_t* pu32StartOffset) +{ + /* + Offset value + 0 = 0xE9 (first byte in image --> magic byte) + 48 = first digit of version number + */ + + esp_err_t errReturn = ESP_OK; + bool bImageStartOffsetFound = false; + uint32_t u32DataIndex = 0; + uint32_t u32FirstDotOffset = 0; + uint32_t u32SecondDotOffset = 0; + uint8_t u8FirstDotIndex = 0; + uint8_t u8SecondDotIndex = 0; + + *pu32StartOffset = 0U; //reset offset to zero + + while((u32DataIndex < *pu32DataLenght) && (bImageStartOffsetFound == false)) + { + //search for magic byte + if(pu8Data[u32DataIndex] == 0xe9) + { + //magic byte found + while ((u8FirstDotIndex < 3) && (u32FirstDotOffset == 0)) + { + //search first dot in version number + if((u32DataIndex+49+u8FirstDotIndex) < *pu32DataLenght) + { + if((pu8Data[(u32DataIndex+49+u8FirstDotIndex)] == 0x2e)) + { + //first dot found + u32FirstDotOffset = (u32DataIndex+49+u8FirstDotIndex); + } + } + u8FirstDotIndex++; + } + + while ((u8SecondDotIndex < 3) && (u32SecondDotOffset == 0) && (u32FirstDotOffset != 0)) + { + //search first dot in version number + if((u32FirstDotOffset+(u8SecondDotIndex+2)) < *pu32DataLenght) + { + if((pu8Data[(u32FirstDotOffset+(u8SecondDotIndex+2))] == 0x2e)) + { + //second dot found + u32SecondDotOffset = (u32FirstDotOffset+(u8SecondDotIndex+2)); + } + } + u8SecondDotIndex++; + } + + if((u32FirstDotOffset != 0) && (u32SecondDotOffset != 0)) + { + //image start found based on magic byte and version number systax + *pu32StartOffset = u32DataIndex; //store image start offset + bImageStartOffsetFound = true; + } + else + { + // this is propably not the magic byte --> reset + u32FirstDotOffset = 0; + u32SecondDotOffset = 0; + u8FirstDotIndex = 0; + u8SecondDotIndex = 0; + } + } + u32DataIndex++; + } + + if(bImageStartOffsetFound == false) + { + errReturn = ESP_ERR_NOT_FOUND; + } + + return errReturn; +} + +esp_err_t errExtractVersionNumber(const char* pu8Data, uint32_t* pu32DataLenght, char* pc8RemoteVersionNumber) +{ + uint32_t u32StartOffset; + esp_err_t err = ESP_OK; + + strcpy(pc8RemoteVersionNumber, "999.999.999"); //init value + err = errFindImageStart(pu8Data, pu32DataLenght, &u32StartOffset); //get image start offset + + if(err == ESP_OK) + { + //image found + strncpy(pc8RemoteVersionNumber, pu8Data+(u32StartOffset+48), 11); //copy version number + pc8RemoteVersionNumber[12] = '\0'; + } + return err; +} + +void vPrintOTAProgress(const uint32_t* const pu32TotalImageSize, const uint32_t* const pu32BytesWritten, OTA_MESH_ROLE_t eRole) +{ + uint32_t u32Percentage = 0U; + static uint32_t u32LastPercentage = 0U; + + if((*pu32BytesWritten) >= (*pu32TotalImageSize)) + { + u32Percentage = 100; + } + else + { + u32Percentage = (uint32_t) (((float) (*pu32BytesWritten)/(float) (*pu32TotalImageSize)) * 100.0); + } + + if((u32Percentage-u32LastPercentage) >= OTA_PROGRESS_LOG_INTERVAL) + { + if(eRole == Transmitter) + { + ESP_LOGI(LOG_TAG, "Transmitting OTA update: %i %%", u32Percentage); + } + + if(eRole == Receiver) + { + ESP_LOGI(LOG_TAG, "Receiving OTA update: %i %%", u32Percentage); + } + + + + u32LastPercentage = u32Percentage; + } +} + +void vAddAllNeighboursToQueue(void) +{ + esp_err_t err = ESP_OK; + + mesh_addr_t addrParent; //addr of parent node + mesh_addr_t childrenAddr[CONFIG_MESH_ROUTE_TABLE_SIZE]; //array of children attached to this node + uint16_t u16ChildrenSize = 0U; //number of children attached to this node + + err = errGetParentNode(&addrParent); + + if(err == ESP_OK) + { + vAddNodeToPossibleUpdatableQueue(addrParent.addr); + ESP_LOGI(LOG_TAG, "added parent"); + } + + err = ESP_OK; //reset error code + + ERROR_CHECK(errGetChildren(childrenAddr, &u16ChildrenSize)); //get all children + + for (uint16_t u16Index = 0; ((u16Index < u16ChildrenSize) && (err == ESP_OK)); u16Index++) + { + vAddNodeToPossibleUpdatableQueue(childrenAddr[u16Index].addr); + ESP_LOGI(LOG_TAG, "added child"); + } +} + +esp_err_t errSendOTAVersionResponse(mesh_addr_t* pMeshReceiverAddr) +{ + esp_err_t err = ESP_OK; + MESH_PACKET_t packet; + packet.type = OTA_Version_Response; + + const esp_partition_t* pBootPartition; //pointer to boot partition (that will booted after reset) + esp_app_desc_t bootPartitionDesc; //Metadata from boot partition + + pBootPartition = esp_ota_get_boot_partition(); //get boot partition (that will booted after reset), not the running partition + ERROR_CHECK(esp_ota_get_partition_description(pBootPartition, &bootPartitionDesc)); //get metadate of partition + memcpy(&packet.au8Payload, &bootPartitionDesc.version, 12); //copy local version to OTA_Version_Response packet + + ESP_LOGI(LOG_TAG, "Send OTA_Version_Response to 0x%x", pMeshReceiverAddr->addr[5]); + + err = errSendMeshPacket(pMeshReceiverAddr, &packet); + return err; +} + +void vClearOtaMessageQueue(mesh_addr_t* pMeshNodeAddr) +{ + MESH_PACKET_t sMeshPacket; //packet for sending and receiving + for (uint32_t u32Index = 0; (u32Index < QUEUE_MESSAGE_OTA_SIZE); u32Index++) //loop through all OTA messages + { + if (xQueueReceive(queueMessageOTA, &sMeshPacket, 0) == pdTRUE) + { + if(!(bCheckMACEquality(sMeshPacket.meshSenderAddr.addr, pMeshNodeAddr->addr))) + { + //received OTA message is NOT from pMeshNodeAddr --> keep it in queue + vAddOtaMessageToQueue(&sMeshPacket); + } + else + { + ESP_LOGI(LOG_TAG, "Removed type %i from node 0x%x", sMeshPacket.type, pMeshNodeAddr->addr[5]); + } + } + }//end OTA message loop +} + +esp_err_t errSendOTAVersionRequest(mesh_addr_t* pMeshReceiverAddr) +{ + esp_err_t err = ESP_OK; + MESH_PACKET_t packet; + packet.type = OTA_Version_Request; + + const esp_partition_t* pBootPartition; //pointer to boot partition (that will booted after reset) + esp_app_desc_t bootPartitionDesc; //Metadata from boot partition + + pBootPartition = esp_ota_get_boot_partition(); //get boot partition (that will booted after reset), not the running partition + ERROR_CHECK(esp_ota_get_partition_description(pBootPartition, &bootPartitionDesc)); //get metadate of partition + memcpy(&packet.au8Payload, &bootPartitionDesc.version, 12); //copy local version to OTA_Version_Request packet + err = errSendMeshPacket(pMeshReceiverAddr, &packet); + return err; +} + diff --git a/components/mesh_ota/include/HTTPS_client.h b/components/mesh_ota/include/HTTPS_Client.h similarity index 91% rename from components/mesh_ota/include/HTTPS_client.h rename to components/mesh_ota/include/HTTPS_Client.h index 0150363..91baafd 100644 --- a/components/mesh_ota/include/HTTPS_client.h +++ b/components/mesh_ota/include/HTTPS_Client.h @@ -5,19 +5,10 @@ #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#include "esp_wifi.h" #include "esp_event.h" #include "esp_log.h" #include "esp_system.h" -#include "nvs_flash.h" #include "esp_netif.h" - -#include "lwip/err.h" -#include "lwip/sockets.h" -#include "lwip/sys.h" -#include "lwip/netdb.h" -#include "lwip/dns.h" - #include "mbedtls/platform.h" #include "mbedtls/net_sockets.h" #include "mbedtls/esp_debug.h" diff --git a/components/mesh_ota/include/Mesh_network.h b/components/mesh_ota/include/Mesh_Network.h similarity index 78% rename from components/mesh_ota/include/Mesh_network.h rename to components/mesh_ota/include/Mesh_Network.h index f2c0421..e448e36 100644 --- a/components/mesh_ota/include/Mesh_network.h +++ b/components/mesh_ota/include/Mesh_Network.h @@ -8,7 +8,7 @@ #include "esp_log.h" #include "esp_mesh.h" #include "esp_mesh_internal.h" -#include +#include "nvs_flash.h" #ifndef CONFIG_MESH_MESSAGE_SIZE #define CONFIG_MESH_MESSAGE_SIZE 1500 @@ -67,6 +67,15 @@ struct meshPacket typedef struct meshPacket MESH_PACKET_t; +#define ERROR_CHECK(x) if (err == ESP_OK) \ + { \ + err = (x); \ + if (err != ESP_OK) \ + { \ + ESP_LOGE(LOG_TAG, "%s failed with error: 0x%x -> %s", #x, err, esp_err_to_name(err)); \ + } \ + } \ + extern bool bIsMeshConnected; extern int32_t i32MeshLayer; extern mesh_addr_t meshParentAddr; diff --git a/components/mesh_ota/include/Mesh_OTA.h b/components/mesh_ota/include/Mesh_OTA.h index 075c8f5..24a2e41 100644 --- a/components/mesh_ota/include/Mesh_OTA.h +++ b/components/mesh_ota/include/Mesh_OTA.h @@ -1,20 +1,14 @@ #ifndef H_MESH_OTA #define H_MESH_OTA -#include -#include "esp_wifi.h" #include "esp_system.h" #include "esp_event.h" #include "esp_log.h" -#include "esp_mesh.h" -#include "esp_mesh_internal.h" -#include "nvs_flash.h" -#include "driver/gpio.h" #include "esp_ota_ops.h" #include "esp_partition.h" -#include "Mesh_network.h" -#include "HTTPS_client.h" +#include "Mesh_Network.h" +#include "HTTPS_Client.h" #define ERASE_NVS //erase non volatile storage if full #define QUEUE_NODES_SIZE 10 @@ -34,42 +28,15 @@ } \ } \ -enum otaMeshRole -{ - Transmitter, - Receiver -}; - -typedef enum otaMeshRole OTA_MESH_ROLE_t; - esp_err_t errMeshOTAInitialize(); -esp_err_t errOTAHTTPS(bool* pbNewOTAImage); + esp_err_t errOTAMeshSlave(bool* pbNewOTAImage); esp_err_t errOTAMeshMaster(bool* pbNewOTAImage, mesh_addr_t* pMeshNodeAddr); -esp_err_t errOTAMeshTransmit(mesh_addr_t* pMeshNodeAddr); -esp_err_t errOTAMeshReceive(bool* pbNewOTAImage, mesh_addr_t* pMeshNodeAddr); -//helper functions -bool bNewerVersion(const char* pu8Local, const char* pu8Remote); -esp_err_t errExtractVersionNumber(const char* pu8Data, uint32_t* pu32DataLenght, char* pc8RemoteVersionNumber); -esp_err_t errFindImageStart(const char* pu8Data, uint32_t* pu32DataLenght, uint32_t* pu32StartOffset); -void vPrintOTAProgress(const uint32_t* const pu32TotalImageSize, const uint32_t* const pu32BytesWritten, OTA_MESH_ROLE_t eRole); -void vAddAllNeighboursToQueue(void); -esp_err_t errSendOTAVersionResponse(mesh_addr_t* meshReceiverAddr); -esp_err_t errSendOTAVersionRequest(mesh_addr_t* meshReceiverAddr); -void vClearOtaMessageQueue(mesh_addr_t* pMeshNodeAddr); - -//Handler -void vAddNodeToPossibleUpdatableQueue(uint8_t* pu8MAC); -void vAddOtaMessageToQueue(MESH_PACKET_t* puMeshPacket); -void vChangeStateOfServerWorker(bool state); - //Tasks void vTaskServerWorker(void *arg); void vTaskOTAWorker(void *arg); - - #endif /* H_MESH_OTA */ diff --git a/components/mesh_ota/include/Mesh_OTA_Access.h b/components/mesh_ota/include/Mesh_OTA_Access.h new file mode 100644 index 0000000..2336d1a --- /dev/null +++ b/components/mesh_ota/include/Mesh_OTA_Access.h @@ -0,0 +1,28 @@ +#ifndef H_MESH_OTA_ACCESS +#define H_MESH_OTA_ACCESS + +#include "esp_system.h" +#include "esp_event.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "esp_ota_ops.h" +#include "esp_partition.h" + +#include "Mesh_Network.h" +#include "HTTPS_Client.h" + +#define ERROR_CHECK(x) if (err == ESP_OK) \ + { \ + err = (x); \ + if (err != ESP_OK) \ + { \ + ESP_LOGE(LOG_TAG, "%s failed with error: 0x%x -> %s", #x, err, esp_err_to_name(err)); \ + } \ + } \ + +esp_err_t errOTAMeshTransmit(mesh_addr_t* pMeshNodeAddr); +esp_err_t errOTAMeshReceive(bool* pbNewOTAImage, mesh_addr_t* pMeshNodeAddr); + +esp_err_t errOTAHTTPS(bool* pbNewOTAImage); + +#endif /* H_MESH_OTA_ACCESS */ diff --git a/components/mesh_ota/include/Mesh_OTA_Globals.h b/components/mesh_ota/include/Mesh_OTA_Globals.h new file mode 100644 index 0000000..63f67e3 --- /dev/null +++ b/components/mesh_ota/include/Mesh_OTA_Globals.h @@ -0,0 +1,19 @@ +#ifndef H_MESH_OTA_GLOBALS +#define H_MESH_OTA_GLOBALS + +#include "esp_system.h" +#include "esp_partition.h" + +#include "Mesh_Network.h" +#include "HTTPS_Client.h" + +extern xQueueHandle queueNodes; //nodes that should be checked for ota update (contains children and parent) +extern xQueueHandle queueMessageOTA; //mesh ota controll messages like "OTA_Version_Response" "OTA_ACK" + +extern SemaphoreHandle_t bsStartStopServerWorker; //binary semaphore +extern SemaphoreHandle_t bsOTAProcess; //binary semaphore + +extern const esp_partition_t* pOTAPartition; //pointer to ota partition +extern bool bWantReboot; //flag to signal pending reboot + +#endif /* H_MESH_OTA_GLOBALS */ diff --git a/components/mesh_ota/include/Mesh_OTA_Util.h b/components/mesh_ota/include/Mesh_OTA_Util.h new file mode 100644 index 0000000..cbcafc8 --- /dev/null +++ b/components/mesh_ota/include/Mesh_OTA_Util.h @@ -0,0 +1,35 @@ +#ifndef H_MESH_OTA_UTIL +#define H_MESH_OTA_UTIL + +#include "esp_system.h" +#include "esp_event.h" +#include "esp_log.h" + +#include "Mesh_Network.h" +#include "HTTPS_Client.h" +#include "Mesh_OTA.h" + +enum otaMeshRole +{ + Transmitter, + Receiver +}; + +typedef enum otaMeshRole OTA_MESH_ROLE_t; + +//helper functions +bool bNewerVersion(const char* pu8Local, const char* pu8Remote); +esp_err_t errExtractVersionNumber(const char* pu8Data, uint32_t* pu32DataLenght, char* pc8RemoteVersionNumber); +esp_err_t errFindImageStart(const char* pu8Data, uint32_t* pu32DataLenght, uint32_t* pu32StartOffset); +void vPrintOTAProgress(const uint32_t* const pu32TotalImageSize, const uint32_t* const pu32BytesWritten, OTA_MESH_ROLE_t eRole); +void vAddAllNeighboursToQueue(void); +esp_err_t errSendOTAVersionResponse(mesh_addr_t* meshReceiverAddr); +esp_err_t errSendOTAVersionRequest(mesh_addr_t* meshReceiverAddr); +void vClearOtaMessageQueue(mesh_addr_t* pMeshNodeAddr); + +//Handler +void vAddNodeToPossibleUpdatableQueue(uint8_t* pu8MAC); +void vAddOtaMessageToQueue(MESH_PACKET_t* puMeshPacket); +void vChangeStateOfServerWorker(bool state); + +#endif /* H_MESH_OTA_UTIL */ diff --git a/main/Blinky_LED.c b/main/Blinky_LED.c index 384d4bb..6317f82 100644 --- a/main/Blinky_LED.c +++ b/main/Blinky_LED.c @@ -183,4 +183,4 @@ void vTaskReceiveData(void *arg) } vTaskDelay(200 / portTICK_PERIOD_MS); } -} \ No newline at end of file +} diff --git a/main/Blinky_LED.h b/main/Blinky_LED.h index a5ec54e..a807322 100644 --- a/main/Blinky_LED.h +++ b/main/Blinky_LED.h @@ -11,6 +11,7 @@ #include "Mesh_OTA.h" + #define GPIO_BOOT_BTN 0 //GPIO0 (Boot BTN) #define GPIO_LED 2 //GPIO2 (internal blue LED in DevKit V1.0) diff --git a/main/Main.c b/main/Main.c index 50164a3..eabab6c 100644 --- a/main/Main.c +++ b/main/Main.c @@ -1,15 +1,9 @@ - -#include -#include "esp_wifi.h" +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" #include "esp_system.h" -#include "esp_event.h" -#include "esp_log.h" -#include "esp_mesh.h" -#include "esp_mesh_internal.h" -#include "nvs_flash.h" -#include "driver/gpio.h" -#include "esp_ota_ops.h" -#include "esp_partition.h" +#include "esp_spi_flash.h" #include "Mesh_OTA.h" #include "Blinky_LED.h" @@ -19,7 +13,7 @@ static const char *LOG_TAG = "esp_main"; void app_main(void) { esp_err_t err = ESP_OK; - ESP_LOGI(LOG_TAG, "hardcoded: 0.0.1"); + ESP_LOGI(LOG_TAG, "hardcoded version: 0.0.1"); ESP_LOGI(LOG_TAG, "start mesh network"); err = errMeshNetworkInitialize();