2021-01-11 22:56:39 +01:00
# include "Mesh_OTA.h"
2021-01-20 20:55:33 +01:00
# include "Mesh_OTA_Util.h"
# include "Mesh_OTA_Globals.h"
# include "Mesh_OTA_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 ;
2021-01-18 19:03:32 +01:00
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 ;
}
}
2021-01-18 19:03:32 +01:00
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-17 23:27:01 +01:00
ERROR_CHECK ( errMeshNetworkSetChildConnectedHandle ( vAddNodeToPossibleUpdatableQueue ) ) ;
2021-01-20 15:18:22 +01:00
ERROR_CHECK ( errMeshNetworkSetOTAMessageHandleHandle ( vAddOtaMessageToQueue ) ) ;
2021-01-17 23:27:01 +01:00
ERROR_CHECK ( errMeshNetworkSetChangeStateOfServerWorkerHandle ( vChangeStateOfServerWorker ) ) ;
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-17 23:47:59 +01:00
xReturned = xTaskCreate ( vTaskServerWorker , " vTaskServerWorker " , 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 ;
}
}
2021-01-18 22:56:42 +01:00
if ( err = = ESP_OK )
{
xReturned = xTaskCreate ( vTaskOTAWorker , " vTaskOTAWorker " , 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 ;
}
void vTaskServerWorker ( void * arg )
{
2021-01-18 12:49:52 +01:00
esp_err_t err ;
2021-01-18 19:03:32 +01:00
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 ;
2021-01-18 19:03:32 +01:00
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
{
2021-01-18 19:03:32 +01:00
ESP_LOGI ( LOG_TAG , " Checking firmware image on server " ) ;
2021-01-18 17:38:08 +01:00
if ( bFirstRun = = true )
{
2021-01-18 19:03:32 +01:00
ERROR_CHECK ( errHTTPSClientInitialize ( ) ) ;
2021-01-18 17:38:08 +01:00
bFirstRun = false ;
}
2021-01-18 12:49:52 +01:00
2021-01-18 19:03:32 +01:00
ERROR_CHECK ( errHTTPSClientConnectToServer ( ) ) ;
ERROR_CHECK ( errHTTPSClientValidateServer ( ) ) ;
ERROR_CHECK ( errHTTPSClientSendRequest ( ) ) ;
2021-01-18 12:49:52 +01:00
2021-01-18 17:38:08 +01:00
ERROR_CHECK ( errOTAHTTPS ( & bNewOTAImage ) ) ;
2021-01-18 19:03:32 +01:00
errHTTPSClientReset ( ) ;
2021-01-18 12:49:52 +01:00
if ( bNewOTAImage = = true )
{
//set want reboot
2021-01-18 19:03:32 +01:00
ESP_LOGI ( LOG_TAG , " Updated successfully via HTTPS, set pending reboot " ) ;
bWantReboot = true ;
2021-01-18 22:56:42 +01:00
vAddAllNeighboursToQueue ( ) ; //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
}
2021-01-18 22:56:42 +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-19 12:36:21 +01:00
void vTaskOTAWorker ( void * arg )
{
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
2021-01-20 13:43:41 +01:00
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();
}
ERROR_CHECK ( errOTAMeshSlave ( & bNewOTAImage ) ) ;
}
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 ;
}
ERROR_CHECK ( errOTAMeshMaster ( & bNewOTAImage , & meshNodeAddr ) ) ;
2021-01-19 22:19:30 +01:00
if ( err ! = ESP_OK )
{
//OTA process faild --> add back to queue
vAddNodeToPossibleUpdatableQueue ( meshNodeAddr . addr ) ;
}
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 ;
vAddAllNeighboursToQueue ( ) ; //add all existing neighbours to queue
}
vTaskDelay ( ( 1000 ) / portTICK_PERIOD_MS ) ;
}
}
2021-01-17 23:27:01 +01:00
2021-01-19 12:36:21 +01:00
esp_err_t errOTAMeshSlave ( bool * pbNewOTAImage )
{
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
* pbNewOTAImage = false ; //set default false
//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-19 12:36:21 +01:00
ERROR_CHECK ( errSendOTAVersionResponse ( & sOTAMessage . meshSenderAddr ) ) ;
if ( ( bNewerVersion ( ( bootPartitionDesc ) . version , ( char * ) sOTAMessage . au8Payload ) ) & & ( err = = ESP_OK ) ) //compare local and remote version
{
//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
2021-01-19 22:19:30 +01:00
ERROR_CHECK ( errOTAMeshReceive ( pbNewOTAImage , & sOTAMessage . meshSenderAddr ) ) ;
2021-01-19 12:36:21 +01:00
}
if ( ( bNewerVersion ( ( char * ) sOTAMessage . au8Payload , ( bootPartitionDesc ) . version ) ) & & ( err = = ESP_OK ) ) //compare remote and local version
{
//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-19 22:19:30 +01:00
ERROR_CHECK ( errOTAMeshTransmit ( & 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 ;
}
esp_err_t errOTAMeshMaster ( bool * pbNewOTAImage , mesh_addr_t * pMeshNodeAddr )
{
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-19 12:36:21 +01:00
* pbNewOTAImage = false ; //set default false
2021-01-19 17:20:16 +01:00
if ( bIsNodeNeighbour ( pMeshNodeAddr ) = = true ) //check if node is still connected
{
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 13:43:41 +01:00
ESP_LOGI ( LOG_TAG , " Mesh-Master: send Version_Request to 0x%x " , pMeshNodeAddr - > addr [ 5 ] ) ;
2021-01-19 17:20:16 +01:00
ERROR_CHECK ( errSendOTAVersionRequest ( pMeshNodeAddr ) ) ; //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 ;
}
if ( ( err = = ESP_OK ) & & ( sOTAMessage . type = = OTA_Version_Response ) & & ( bCheckMACEquality ( sOTAMessage . meshSenderAddr . addr , pMeshNodeAddr - > addr ) ) ) //if OTA_Version_Request
{
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
if ( ( bNewerVersion ( ( bootPartitionDesc ) . version , ( char * ) sOTAMessage . au8Payload ) ) & & ( err = = ESP_OK ) ) //compare local and remote version
{
//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
2021-01-19 22:19:30 +01:00
ERROR_CHECK ( errOTAMeshReceive ( pbNewOTAImage , & sOTAMessage . meshSenderAddr ) ) ;
2021-01-19 17:20:16 +01:00
}
if ( ( bNewerVersion ( ( char * ) sOTAMessage . au8Payload , ( bootPartitionDesc ) . version ) ) & & ( err = = ESP_OK ) ) //compare remote and local version
{
//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-19 22:19:30 +01:00
ERROR_CHECK ( errOTAMeshTransmit ( & 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 15:18:22 +01:00
vAddOtaMessageToQueue ( & 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
2021-01-20 13:43:41 +01:00
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
2021-01-20 13:43:41 +01:00
ESP_LOGI ( LOG_TAG , " OTA-Master: connected and NOT responding --> add node back to queue " ) ;
2021-01-19 17:20:16 +01:00
vAddNodeToPossibleUpdatableQueue ( pMeshNodeAddr - > addr ) ;
}
2021-01-19 12:36:21 +01:00
return err ;
}