From 57c71c401c2ae2173848a07757d76c3392d09aea Mon Sep 17 00:00:00 2001 From: localhorst Date: Tue, 19 Jan 2021 22:19:30 +0100 Subject: [PATCH] OTA Tx and Rx over Mesh --- components/mesh_ota/Mesh_OTA.c | 341 ++++++++++++++------- components/mesh_ota/Mesh_network.c | 1 + components/mesh_ota/include/Mesh_OTA.h | 14 +- components/mesh_ota/include/Mesh_network.h | 6 +- 4 files changed, 253 insertions(+), 109 deletions(-) diff --git a/components/mesh_ota/Mesh_OTA.c b/components/mesh_ota/Mesh_OTA.c index fe2f2d8..4bc55d3 100644 --- a/components/mesh_ota/Mesh_OTA.c +++ b/components/mesh_ota/Mesh_OTA.c @@ -130,7 +130,7 @@ void vAddOTAControllMessageToQueue(MESH_PACKET_t* puMeshPacket) } else { - ESP_LOGI(LOG_TAG, "added ota controll message to queue"); + ESP_LOGI(LOG_TAG, "added ota message to queue: %i (type)", puMeshPacket->type); } } @@ -239,6 +239,12 @@ void vTaskOTAWorker(void *arg) } ERROR_CHECK(errOTAMeshMaster(&bNewOTAImage, &meshNodeAddr)); + + if (err != ESP_OK) + { + //OTA process faild --> add back to queue + vAddNodeToPossibleUpdatableQueue(meshNodeAddr.addr); + } } if(bNewOTAImage == true) @@ -290,7 +296,7 @@ esp_err_t errOTAHTTPS(bool* pbNewOTAImage) ESP_LOGI(LOG_TAG, "start OTA download via HTTPS"); do { - vPrintOTAProgress(&(pOTAPartition->size), &u32OTABytesWritten); + vPrintOTAProgress(&(pOTAPartition->size), &u32OTABytesWritten, Receiver); ERROR_CHECK(esp_ota_write(otaHandle, (const void*) u8OTABuffer+u32StartOffset, (u32BytesRead-u32StartOffset))); if(err == ESP_OK) @@ -364,6 +370,7 @@ esp_err_t errOTAMeshSlave(bool* pbNewOTAImage) //remote newer as local ESP_LOGI(LOG_TAG, "remote image on node is newer --> OTA update required"); // --> this version older --> start OTA_Rx --> set pbNewOTAImage true + ERROR_CHECK(errOTAMeshReceive(pbNewOTAImage, &sOTAMessage.meshSenderAddr)); } if((bNewerVersion((char*) sOTAMessage.au8Payload, (bootPartitionDesc).version)) && (err == ESP_OK)) //compare remote and local version @@ -371,6 +378,7 @@ esp_err_t errOTAMeshSlave(bool* pbNewOTAImage) //local newer as remote ESP_LOGI(LOG_TAG, "remote image on node is older --> OTA send required"); // --> this version newer --> start OTA_Tx + ERROR_CHECK(errOTAMeshTransmit(&sOTAMessage.meshSenderAddr)); } xSemaphoreGive(bsOTAProcess); //free binary semaphore, this allows other tasks to start the OTA process } @@ -396,10 +404,6 @@ esp_err_t errOTAMeshMaster(bool* pbNewOTAImage, mesh_addr_t* pMeshNodeAddr) ERROR_CHECK(errSendOTAVersionRequest(pMeshNodeAddr)); //send OTA_VERSION_REQUEST with local version in payload - //read OTA_Version_Response (if from this node) (all other OTA message add again) - // if(uxQueueSpacesAvailable(queueMessageOTA) < QUEUE_MESSAGE_OTA_SIZE) - //{ - for (uint32_t u32Index = 0; u32Index < QUEUE_MESSAGE_OTA_SIZE; u32Index++) //loop through all OTA messages { if(uxQueueSpacesAvailable(queueMessageOTA) < QUEUE_MESSAGE_OTA_SIZE) @@ -422,6 +426,7 @@ esp_err_t errOTAMeshMaster(bool* pbNewOTAImage, mesh_addr_t* pMeshNodeAddr) //remote newer as local ESP_LOGI(LOG_TAG, "Mesh: remote image on node is newer --> OTA update required"); // --> this version older --> start OTA_Rx --> set pbNewOTAImage true + ERROR_CHECK(errOTAMeshReceive(pbNewOTAImage, &sOTAMessage.meshSenderAddr)); } if((bNewerVersion((char*) sOTAMessage.au8Payload, (bootPartitionDesc).version)) && (err == ESP_OK)) //compare remote and local version @@ -429,14 +434,20 @@ esp_err_t errOTAMeshMaster(bool* pbNewOTAImage, mesh_addr_t* pMeshNodeAddr) //local newer as remote ESP_LOGI(LOG_TAG, "Mesh: remote image on node is older --> OTA send required"); // --> this version newer --> start OTA_Tx + ERROR_CHECK(errOTAMeshTransmit(&sOTAMessage.meshSenderAddr)); } } - else + else if (err == ESP_OK) { - //send wrong OTA message back to queue + //received from wrong node or type --> back to queue vAddOTAControllMessageToQueue(&sOTAMessage); } } + else + { + // OTA Message queue is empty --> wait some time + vTaskDelay( (1000/QUEUE_MESSAGE_OTA_SIZE) / portTICK_PERIOD_MS); + } }//end loop xSemaphoreGive(bsOTAProcess); //free binary semaphore, this allows other tasks to start the OTA process } @@ -575,7 +586,7 @@ esp_err_t errExtractVersionNumber(const char* pu8Data, uint32_t* pu32DataLenght, return err; } -void vPrintOTAProgress(const uint32_t* const pu32TotalImageSize, const uint32_t* const pu32BytesWritten) +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; @@ -591,7 +602,18 @@ void vPrintOTAProgress(const uint32_t* const pu32TotalImageSize, const uint32_t* if((u32Percentage-u32LastPercentage) >= OTA_PROGRESS_LOG_INTERVAL) { - ESP_LOGI(LOG_TAG, "OTA update progress: %i %%", u32Percentage); + 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; } } @@ -655,109 +677,216 @@ esp_err_t errSendOTAVersionRequest(mesh_addr_t* pMeshReceiverAddr) return err; } -/* -esp_err_t esp_mesh_ota_send(mesh_addr_t* dest) +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; - static uint32_t u32index; + pBootPartition = esp_ota_get_boot_partition(); //get boot partition (that will booted after reset), not the running partition - const esp_partition_t * pBootPartition = esp_ota_get_boot_partition(); - - if((*pBootPartition).subtype == 0) - { - - int data_read = 0; - - struct ota_mesh_packet packet; - packet.type=OTA_Data; - - if(u32index == 1024) + //loop through partition to read in segmensts until end or error or abort called + while( ((OTA_MESH_SEGMENT_SIZE * u32Index) < pBootPartition->size) && (err == ESP_OK) && (bAbort == false)) { - //all data read - data_read = 0; - u32index = 0; - } - else - { - ESP_LOGI(MESH_TAG, "OTA-Data read: %i", u32index); - err = esp_partition_read(pBootPartition, (1024*u32index), packet.au8Payload, 1024 ); - ESP_ERROR_CHECK(err); - data_read = 1024; - u32index++; - } + bNodeIsResponding = false; //reset to default - if (data_read > 0) - { - //send ota fragemnt to node - esp_mesh_send_packet(dest, &packet); - } + // read partition with offset based in index + ERROR_CHECK(esp_partition_read(pBootPartition, (OTA_MESH_SEGMENT_SIZE * u32Index), sMeshPacket.au8Payload, OTA_MESH_SEGMENT_SIZE)); + u32OTABytesWritten = ((u32Index+1) * OTA_MESH_SEGMENT_SIZE); + vPrintOTAProgress(&(pBootPartition->size), &u32OTABytesWritten, Transmitter); - ESP_ERROR_CHECK(err); - } + if(err == ESP_OK) + { + //no error while read --> send OTA_DATA packet + sMeshPacket.type = OTA_Data; + + if((OTA_MESH_SEGMENT_SIZE * (u32Index+1)) >= pBootPartition->size) //check if last segment + { + //last partition image segment --> send OTA_Complete + sMeshPacket.type = OTA_Complete; + } + + err = errSendMeshPacket(pMeshNodeAddr, &sMeshPacket); + } + else + { + // error while read --> send OTA_ABORT and abort this OTA process + sMeshPacket.type = OTA_Abort; + bAbort = true; + 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, ((3000) / 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; + break; + case OTA_Abort: //abort this OTA process + bAbort = true; + bNodeIsResponding = true; + break; + default: + //receives wrong OTA message type from node --> back to queue + vAddOTAControllMessageToQueue(&sMeshPacket); + break; + } + } + else if (err == ESP_OK) + { + //received from wrong node --> back to queue + vAddOTAControllMessageToQueue(&sMeshPacket); + } + } + else + { + // OTA Message queue is empty --> wait some time + vTaskDelay( (1000/QUEUE_MESSAGE_OTA_SIZE) / portTICK_PERIOD_MS); + } + + }//end OTA message loop + + if(bNodeIsResponding == false) + { + //no abort was called but node didn’t responded + bAbort = true; + err = ESP_FAIL; //this OTA process failed with error + } + }//end of partition segment loop + 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; + + 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, ((3000) / 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; + //fall through + case OTA_Data: //data segement received + bNodeIsResponding = true; + ERROR_CHECK(esp_ota_write(otaHandle, sMeshPacket.au8Payload, OTA_MESH_SEGMENT_SIZE)); + u32OTABytesWritten = ((u32Index+1) * OTA_MESH_SEGMENT_SIZE); + vPrintOTAProgress(&(pOTAPartition->size), &u32OTABytesWritten, Receiver); + break; + case OTA_Abort: //abort this OTA process + bAbort = true; + bNodeIsResponding = true; + break; + default: + //receives wrong OTA message type from node --> back to queue + vAddOTAControllMessageToQueue(&sMeshPacket); + break; + } + } + else if (err == ESP_OK) + { + //received from wrong node --> back to queue + vAddOTAControllMessageToQueue(&sMeshPacket); + } + } + else + { + // OTA Message queue is empty --> wait some time + vTaskDelay( (1000/QUEUE_MESSAGE_OTA_SIZE) / portTICK_PERIOD_MS); + } + + }//end of OTA message loop + + if(bNodeIsResponding == false) + { + //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 + 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; + errSendMeshPacket(pMeshNodeAddr, &sMeshPacket); + } + } + }//end of partition segement loop + + if(bComplete == true) + { + //all OTA segments received --> validate + 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 - { - ESP_LOGI(MESH_TAG, "Subtype: %d", (*pBootPartition).subtype); - } + { + //not all OTA segments received --> abort this OTA process + ERROR_CHECK(esp_ota_abort(otaHandle)); + } return err; } - -esp_err_t esp_mesh_ota_receive(mesh_addr_t* dest, struct ota_mesh_packet* packet) -{ - esp_err_t err = ESP_OK; - static esp_ota_handle_t otaHandle; - static uint32_t u32index; - - const esp_partition_t * pBootPartition = esp_ota_get_boot_partition(); - const esp_partition_t * pOTAPartition = esp_ota_get_next_update_partition(pBootPartition); - - if(u32index == 0) - { - //first run - - err = esp_ota_begin(pOTAPartition, OTA_SIZE_UNKNOWN, &otaHandle); - ESP_ERROR_CHECK(err); - } - - ESP_LOGI(MESH_TAG, "OTA-Data write: %i", u32index); - err = esp_ota_write(otaHandle, packet->au8Payload, 1024); - - if(err != ESP_OK) - { - ESP_LOGE(MESH_TAG, "OTA-Data write error: %i at %i", err, u32index); - } - - ESP_ERROR_CHECK(err); - - - - if(u32index >= 1023) - { - //ota update complete - - ESP_LOGI(MESH_TAG, "OTA-Data complete arrived: %i", u32index); - err = esp_ota_end(otaHandle); - ESP_ERROR_CHECK(err); - esp_app_desc_t otaPartitionDesc; - err = esp_ota_get_partition_description(pOTAPartition, &otaPartitionDesc); - ESP_ERROR_CHECK(err); - ESP_LOGI(MESH_TAG, "pOTAPartition project_name: %s", (otaPartitionDesc).project_name); - - err = esp_ota_set_boot_partition(pOTAPartition); - ESP_ERROR_CHECK(err); - - struct ota_mesh_packet retPacket; - retPacket.type=OTA_Complete; - ESP_ERROR_CHECK (esp_mesh_send_packet(dest, &retPacket)); //send back to parent - - //check if this node has children --> Update them - - esp_restart(); - } - - u32index++; - - - return err; -} -*/ diff --git a/components/mesh_ota/Mesh_network.c b/components/mesh_ota/Mesh_network.c index 054b8b6..e28c1c8 100644 --- a/components/mesh_ota/Mesh_network.c +++ b/components/mesh_ota/Mesh_network.c @@ -305,6 +305,7 @@ void vTaskReceiveMeshData(void *arg) case OTA_Data: case OTA_ACK: case OTA_Complete: + case OTA_Abort: //call the rx handle from OTA if(pOTAMessageHandle) { diff --git a/components/mesh_ota/include/Mesh_OTA.h b/components/mesh_ota/include/Mesh_OTA.h index 7883271..d83710d 100644 --- a/components/mesh_ota/include/Mesh_OTA.h +++ b/components/mesh_ota/include/Mesh_OTA.h @@ -22,6 +22,7 @@ #define SERVER_CHECK_INTERVAL 30 //in seconds #define OTA_HTTPS_SEGMENT_SIZE 2048U #define OTA_PROGRESS_LOG_INTERVAL 7U +#define OTA_MESH_SEGMENT_SIZE MESH_NETWORK_PAYLOAD_SIZE #define ERROR_CHECK(x) if (err == ESP_OK) \ { \ @@ -32,17 +33,28 @@ } \ } \ +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); +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); diff --git a/components/mesh_ota/include/Mesh_network.h b/components/mesh_ota/include/Mesh_network.h index f69706f..b4c2d13 100644 --- a/components/mesh_ota/include/Mesh_network.h +++ b/components/mesh_ota/include/Mesh_network.h @@ -46,6 +46,7 @@ #define CONFIG_MESH_ROUTE_TABLE_SIZE 50 #endif +#define MESH_NETWORK_PAYLOAD_SIZE 1024U struct meshPacket { @@ -56,9 +57,10 @@ struct meshPacket OTA_Version_Response, //send own version in payload OTA_Data, //send image segment OTA_ACK, //ack image segment - OTA_Complete //signal end of image + OTA_Complete, //signal end of image + OTA_Abort //abort OTA process } type; - uint8_t au8Payload[1024]; + uint8_t au8Payload[MESH_NETWORK_PAYLOAD_SIZE]; mesh_addr_t meshSenderAddr; };