ESP32-Mesh-OTA/components/mesh_ota/Mesh_OTA.c

325 lines
14 KiB
C
Raw Normal View History

#include "Mesh_OTA.h"
#include "Mesh_OTA_Util.h"
#include "Mesh_OTA_Globals.h"
2021-01-20 21:40:51 +01:00
#include "Mesh_OTA_Partition_Access.h"
2021-01-02 00:30:13 +01:00
2021-01-17 23:27:01 +01:00
static const char *LOG_TAG = "mesh_ota";
esp_err_t errMeshOTAInitialize()
{
esp_err_t err = ESP_OK;
BaseType_t xReturned;
bWantReboot = false;
2021-01-17 23:27:01 +01:00
//create queue to store nodes for ota worker task
queueNodes = xQueueCreate(QUEUE_NODES_SIZE, sizeof(mesh_addr_t));
if (queueNodes == 0) // Queue not created
{
ESP_LOGE(LOG_TAG, "Unable to create Queue for Nodes");
err = ESP_FAIL;
}
if(err == ESP_OK)
{
//create queue to store ota messages
queueMessageOTA = xQueueCreate(QUEUE_MESSAGE_OTA_SIZE, sizeof(MESH_PACKET_t));
if (queueMessageOTA == 0) // Queue not created
{
ESP_LOGE(LOG_TAG, "Unable to create Queue for OTA Messages");
err = ESP_FAIL;
}
}
if(err == ESP_OK)
{
bsStartStopServerWorker = xSemaphoreCreateBinary();
if( bsStartStopServerWorker == NULL )
{
ESP_LOGE(LOG_TAG, "Unable to create Mutex to represent state of Server worker");
err = ESP_FAIL;
}
}
if(err == ESP_OK)
{
bsOTAProcess = xSemaphoreCreateBinary();
if( bsOTAProcess == NULL )
{
ESP_LOGE(LOG_TAG, "Unable to create Mutex to grant access to OTA Process");
err = ESP_FAIL;
}
}
if(err == ESP_OK)
{
xSemaphoreGive(bsOTAProcess); //unlock binary semaphore
if( bsOTAProcess == NULL )
{
ESP_LOGE(LOG_TAG, "Unable to unlock Mutex to grant access to OTA Process");
err = ESP_FAIL;
}
}
2021-01-20 21:40:51 +01:00
ERROR_CHECK(errMeshNetworkSetChildConnectedHandle(vMeshOtaUtilAddNodeToPossibleUpdatableQueue));
ERROR_CHECK(errMeshNetworkSetOTAMessageHandleHandle(vMeshOtaUtilAddOtaMessageToQueue));
ERROR_CHECK(errMeshNetworkSetChangeStateOfServerWorkerHandle(vMeshOtaUtilChangeStateOfServerWorker));
2021-01-17 23:27:01 +01:00
2021-01-18 17:38:08 +01:00
if(err == ESP_OK)
{
pOTAPartition = esp_ota_get_next_update_partition(NULL); //get ota partition
if(pOTAPartition == NULL)
{
err = ESP_FAIL;
ESP_LOGE(LOG_TAG, "unable to get next ota partition");
}
}
2021-01-17 23:27:01 +01:00
if(err == ESP_OK)
{
2021-01-20 21:40:51 +01:00
xReturned = xTaskCreate(vMeshOtaTaskServerWorker, "vMeshOtaTaskServerWorker", 8192, NULL, 5, NULL);
2021-01-17 23:27:01 +01:00
if(xReturned != pdPASS)
{
ESP_LOGE(LOG_TAG, "Unable to create the server worker task");
err = ESP_FAIL;
}
}
if(err == ESP_OK)
{
2021-01-20 21:40:51 +01:00
xReturned = xTaskCreate(vMeshOtaTaskOTAWorker, "vMeshOtaTaskOTAWorker", 8192, NULL, 5, NULL);
if(xReturned != pdPASS)
{
ESP_LOGE(LOG_TAG, "Unable to create the OTA worker task");
err = ESP_FAIL;
}
}
2021-01-17 23:27:01 +01:00
return err;
}
2021-01-20 21:40:51 +01:00
void vMeshOtaTaskServerWorker(void *arg)
2021-01-17 23:27:01 +01:00
{
2021-01-18 12:49:52 +01:00
esp_err_t err;
bool bNewOTAImage; //true if a new ota image was downloaded and validated
2021-01-18 17:38:08 +01:00
bool bFirstRun = true;
2021-01-17 23:47:59 +01:00
2021-01-18 12:49:52 +01:00
while(true)
{
err = ESP_OK;
bNewOTAImage = false;
2021-01-18 12:49:52 +01:00
xSemaphoreTake(bsStartStopServerWorker, portMAX_DELAY); //wait for binary semaphore that allows to start the worker
xSemaphoreGive(bsStartStopServerWorker); //free binary semaphore, this allows the handler to change is to taken
if (esp_mesh_is_root()) //check again that this node is the root node
{
ESP_LOGI(LOG_TAG, "Checking firmware image on server");
2021-01-18 17:38:08 +01:00
if(bFirstRun == true)
{
ERROR_CHECK(errHTTPSClientInitialize());
2021-01-18 17:38:08 +01:00
bFirstRun = false;
}
2021-01-18 12:49:52 +01:00
ERROR_CHECK(errHTTPSClientConnectToServer());
ERROR_CHECK(errHTTPSClientValidateServer());
ERROR_CHECK(errHTTPSClientSendRequest());
2021-01-18 12:49:52 +01:00
2021-01-20 21:40:51 +01:00
ERROR_CHECK(errMeshOtaPartitionAccessHttps(&bNewOTAImage));
errHTTPSClientReset();
2021-01-18 12:49:52 +01:00
if(bNewOTAImage == true)
{
//set want reboot
ESP_LOGI(LOG_TAG, "Updated successfully via HTTPS, set pending reboot");
bWantReboot = true;
2021-01-20 21:40:51 +01:00
vMeshOtaUtilAddAllNeighboursToQueue(); //add all existing neighbours to queue (aparent will not be added because this node is the root)
2021-01-18 12:49:52 +01:00
}
vTaskDelay( (SERVER_CHECK_INTERVAL*1000) / portTICK_PERIOD_MS); //sleep till next server checks
2021-01-18 12:49:52 +01:00
}
}
}
2021-01-20 21:40:51 +01:00
void vMeshOtaTaskOTAWorker(void *arg)
2021-01-19 12:36:21 +01:00
{
esp_err_t err = ESP_OK;
bool bNewOTAImage; //true if a new ota image was downloaded and validated
mesh_addr_t meshNodeAddr; //node that should be checked for ota update
while(true)
{
err = ESP_OK;
bNewOTAImage = false;
if((uxQueueSpacesAvailable(queueNodes) - QUEUE_NODES_SIZE) == 0)
{
//nodes queue is empty
ESP_LOGI(LOG_TAG, "nodes queue is empty");
2021-01-19 12:36:21 +01:00
if(bWantReboot == true)
{
2021-01-20 11:20:35 +01:00
//ESP_LOGI(LOG_TAG, "ESP32 Reboot ...");
2021-01-19 12:36:21 +01:00
//vTaskDelay( (1000) / portTICK_PERIOD_MS);
//esp_restart();
}
2021-01-20 21:40:51 +01:00
ERROR_CHECK(errMeshOtaSlaveEndpoint(&bNewOTAImage));
2021-01-19 12:36:21 +01:00
}
else
{
//queue not empty
ESP_LOGI(LOG_TAG, "nodes queue not empty: %i", (QUEUE_NODES_SIZE - uxQueueSpacesAvailable(queueNodes)));
if (xQueueReceive(queueNodes, &meshNodeAddr, ((100) / portTICK_PERIOD_MS)) != pdTRUE)
{
ESP_LOGE(LOG_TAG, "Unable to receive OTA Messages from Queue");
err = ESP_FAIL;
}
2021-01-20 21:40:51 +01:00
ERROR_CHECK(errMeshOtaMasterEndpoint(&bNewOTAImage, &meshNodeAddr));
2021-01-19 22:19:30 +01:00
if (err != ESP_OK)
{
//OTA process faild --> add back to queue
2021-01-20 21:40:51 +01:00
vMeshOtaUtilAddNodeToPossibleUpdatableQueue(meshNodeAddr.addr);
2021-01-19 22:19:30 +01:00
}
2021-01-19 12:36:21 +01:00
}
if(bNewOTAImage == true)
{
//set want reboot
ESP_LOGI(LOG_TAG, "Updated successfully via Mesh, set pending reboot");
bWantReboot = true;
2021-01-20 21:40:51 +01:00
vMeshOtaUtilAddAllNeighboursToQueue(); //add all existing neighbours to queue
2021-01-19 12:36:21 +01:00
}
vTaskDelay( (1000) / portTICK_PERIOD_MS);
}
}
2021-01-20 22:39:18 +01:00
esp_err_t errMeshOtaSlaveEndpoint(bool* const cpbNewOTAImage)
2021-01-19 12:36:21 +01:00
{
esp_err_t err = ESP_OK;
MESH_PACKET_t sOTAMessage;
const esp_partition_t* pBootPartition; //pointer to boot partition (that will booted after reset)
esp_app_desc_t bootPartitionDesc; //Metadate from boot partition
2021-01-20 22:39:18 +01:00
*cpbNewOTAImage = false; //set default false
2021-01-19 12:36:21 +01:00
//read OTAMessages queue
if(uxQueueSpacesAvailable(queueMessageOTA) < QUEUE_MESSAGE_OTA_SIZE)
{
//queue not empty
if (xQueueReceive(queueMessageOTA, &sOTAMessage, ((100) / portTICK_PERIOD_MS)) != pdTRUE)
{
ESP_LOGE(LOG_TAG, "Unable to receive OTA Messages from Queue");
err = ESP_FAIL;
}
if((err == ESP_OK) && (sOTAMessage.type == OTA_Version_Request)) //if OTA_Version_Request
{
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
2021-01-19 17:20:16 +01:00
//send OTA_Version_Response to sender of OTA_Version_Request packet wirh version in payload
2021-01-20 21:40:51 +01:00
ERROR_CHECK(errMeshOtaUtilSendOTAVersionResponse(&sOTAMessage.meshSenderAddr));
2021-01-19 12:36:21 +01:00
2021-01-20 21:40:51 +01:00
if((bMeshOtaUtilNewerVersion((bootPartitionDesc).version, (char*) sOTAMessage.au8Payload)) && (err == ESP_OK)) //compare local and remote version
2021-01-19 12:36:21 +01:00
{
//remote newer as local
ESP_LOGI(LOG_TAG, "remote image on node is newer --> OTA update required");
2021-01-20 22:39:18 +01:00
// --> this version older --> start OTA_Rx --> set cpbNewOTAImage true
ERROR_CHECK(errMeshOtaPartitionAccessMeshReceive(cpbNewOTAImage, &sOTAMessage.meshSenderAddr));
2021-01-19 12:36:21 +01:00
}
2021-01-20 21:40:51 +01:00
if((bMeshOtaUtilNewerVersion((char*) sOTAMessage.au8Payload, (bootPartitionDesc).version)) && (err == ESP_OK)) //compare remote and local version
2021-01-19 12:36:21 +01:00
{
//local newer as remote
ESP_LOGI(LOG_TAG, "remote image on node is older --> OTA send required");
// --> this version newer --> start OTA_Tx
2021-01-20 21:40:51 +01:00
ERROR_CHECK(errMeshOtaPartitionAccessMeshTransmit(&sOTAMessage.meshSenderAddr));
2021-01-19 12:36:21 +01:00
}
xSemaphoreGive(bsOTAProcess); //free binary semaphore, this allows other tasks to start the OTA process
}
}
return err;
}
2021-01-20 22:39:18 +01:00
esp_err_t errMeshOtaMasterEndpoint(bool* const cpbNewOTAImage, const mesh_addr_t* const cpcMeshNodeAddr)
2021-01-19 12:36:21 +01:00
{
esp_err_t err = ESP_OK;
MESH_PACKET_t sOTAMessage;
const esp_partition_t* pBootPartition; //pointer to boot partition (that will booted after reset)
2021-01-19 17:20:16 +01:00
esp_app_desc_t bootPartitionDesc; //Metadata from boot partition
bool bNodeIsConnected = false;
bool bNodeIsResponding = false;
2021-01-20 22:39:18 +01:00
*cpbNewOTAImage = false; //set default false
2021-01-19 12:36:21 +01:00
2021-01-20 22:39:18 +01:00
if(bMeshNetworkIsNodeNeighbour(cpcMeshNodeAddr) == true) //check if node is still connected
2021-01-19 17:20:16 +01:00
{
bNodeIsConnected = true; //node is one of the neighbours
xSemaphoreTake(bsOTAProcess, portMAX_DELAY); //wait for binary semaphore that allows to start the OTA process
2021-01-20 22:39:18 +01:00
ESP_LOGI(LOG_TAG, "Mesh-Master: send Version_Request to 0x%x", cpcMeshNodeAddr->addr[5]);
ERROR_CHECK(errMeshOtaUtilSendOTAVersionRequest(cpcMeshNodeAddr)); //send OTA_VERSION_REQUEST with local version in payload
2021-01-19 12:36:21 +01:00
2021-01-19 17:20:16 +01:00
for (uint32_t u32Index = 0; u32Index < QUEUE_MESSAGE_OTA_SIZE; u32Index++) //loop through all OTA messages
{
if(uxQueueSpacesAvailable(queueMessageOTA) < QUEUE_MESSAGE_OTA_SIZE)
{
//queue not empty
if (xQueueReceive(queueMessageOTA, &sOTAMessage, ((3000) / portTICK_PERIOD_MS)) != pdTRUE)
{
ESP_LOGE(LOG_TAG, "Unable to receive OTA Messages from queue");
err = ESP_FAIL;
}
2021-01-20 22:39:18 +01:00
if((err == ESP_OK) && (sOTAMessage.type == OTA_Version_Response) && (bMeshNetworkCheckMACEquality(sOTAMessage.meshSenderAddr.addr, cpcMeshNodeAddr->addr))) //if OTA_Version_Request
2021-01-19 17:20:16 +01:00
{
bNodeIsResponding = true;
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
2021-01-20 21:40:51 +01:00
if((bMeshOtaUtilNewerVersion((bootPartitionDesc).version, (char*) sOTAMessage.au8Payload)) && (err == ESP_OK)) //compare local and remote version
2021-01-19 17:20:16 +01:00
{
//remote newer as local
ESP_LOGI(LOG_TAG, "Mesh: remote image on node is newer --> OTA update required");
2021-01-20 22:39:18 +01:00
// --> this version older --> start OTA_Rx --> set cpbNewOTAImage true
ERROR_CHECK(errMeshOtaPartitionAccessMeshReceive(cpbNewOTAImage, &sOTAMessage.meshSenderAddr));
2021-01-19 17:20:16 +01:00
}
2021-01-20 21:40:51 +01:00
if((bMeshOtaUtilNewerVersion((char*) sOTAMessage.au8Payload, (bootPartitionDesc).version)) && (err == ESP_OK)) //compare remote and local version
2021-01-19 17:20:16 +01:00
{
//local newer as remote
ESP_LOGI(LOG_TAG, "Mesh: remote image on node is older --> OTA send required");
// --> this version newer --> start OTA_Tx
2021-01-20 21:40:51 +01:00
ERROR_CHECK(errMeshOtaPartitionAccessMeshTransmit(&sOTAMessage.meshSenderAddr));
2021-01-19 17:20:16 +01:00
}
}
2021-01-19 22:19:30 +01:00
else if (err == ESP_OK)
2021-01-19 17:20:16 +01:00
{
2021-01-19 22:19:30 +01:00
//received from wrong node or type --> back to queue
2021-01-20 21:40:51 +01:00
vMeshOtaUtilAddOtaMessageToQueue(&sOTAMessage);
2021-01-19 17:20:16 +01:00
}
}
2021-01-19 22:19:30 +01:00
else
{
// OTA Message queue is empty --> wait some time
ESP_LOGI(LOG_TAG, "OTA-Master: OTA Message queue is empty --> wait some time");
2021-01-19 22:19:30 +01:00
vTaskDelay( (1000/QUEUE_MESSAGE_OTA_SIZE) / portTICK_PERIOD_MS);
}
2021-01-19 17:20:16 +01:00
}//end loop
xSemaphoreGive(bsOTAProcess); //free binary semaphore, this allows other tasks to start the OTA process
}
if((bNodeIsResponding == false) && (bNodeIsConnected == true))
{
//add node back to queue if connected and NOT responding
ESP_LOGI(LOG_TAG, "OTA-Master: connected and NOT responding --> add node back to queue ");
2021-01-20 22:39:18 +01:00
vMeshOtaUtilAddNodeToPossibleUpdatableQueue(cpcMeshNodeAddr->addr);
2021-01-19 17:20:16 +01:00
}
2021-01-19 12:36:21 +01:00
return err;
}