2021-01-21 11:11:37 +01:00
/**
* @ file Mesh_OTA_Partition_Access . c
* @ brief Write and read partition if requested from Mesh_OTA
* @ author Hendrik Schutter
* @ date 21.01 .2021
*
* Additional Infos : Write image via HTTPS
* Receive or transmit via Mesh
*/
2021-01-20 20:55:33 +01:00
# 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-20 20:55:33 +01:00
2021-01-20 21:40:51 +01:00
static const char * LOG_TAG = " mesh_ota_partition_access " ;
2021-01-20 20:55:33 +01:00
2021-01-21 11:11:37 +01:00
/**
* @ fn esp_err_t errMeshOtaPartitionAccessHttps ( bool * const cpbNewOTAImage )
* @ brief Downloads and writes the image from the server to partition
* @ param cpbNewOTAImage pointer to boolean to signal if a new image was successfully received
* @ return ESP32 error code
* @ author Hendrik Schutter
* @ date 21.01 .2021
*
* Checks if the image on server is newer
* Downloads the image in segements
* Handles OTA process
*/
2021-01-20 22:39:18 +01:00
esp_err_t errMeshOtaPartitionAccessHttps ( bool * const cpbNewOTAImage )
2021-01-20 21:40:51 +01:00
{
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 ( errMeshOtaUtilExtractVersionNumber ( u8OTABuffer , & u32BytesRead , pcRemoteVersionNumber ) ) ; //extract version numbers
2021-01-21 11:11:37 +01:00
//check if version number is found
2021-01-21 11:43:09 +01:00
if ( err = = ESP_OK )
2021-01-20 21:40:51 +01:00
{
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 ( bMeshOtaUtilNewerVersion ( ( 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 ( errMeshOtaUtilFindImageStart ( u8OTABuffer , & u32BufferLenght , & u32StartOffset ) ) ; //get image start offset
ERROR_CHECK ( esp_ota_begin ( pOTAPartition , OTA_SIZE_UNKNOWN , & otaHandle ) ) ; //start ota update process
2021-01-21 11:11:37 +01:00
2021-01-20 21:40:51 +01:00
if ( err = = ESP_OK )
{
//image download and ota partition write
ESP_LOGI ( LOG_TAG , " start OTA download via HTTPS " ) ;
do
{
2021-01-21 11:43:09 +01:00
vMeshOtaUtilPrintOtaProgress ( & ( pOTAPartition - > size ) , & u32OTABytesWritten , Receiver ) ;
2021-01-20 21:40:51 +01:00
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
}
}
2021-01-21 11:11:37 +01:00
//loop until error or complete image downloaded
while ( ( u32BytesRead > 0 ) & & ( err = = ESP_OK ) & & ( u32OTABytesWritten < = pOTAPartition - > size ) ) ;
2021-01-20 21:40:51 +01:00
}
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 )
{
2021-01-20 22:39:18 +01:00
* cpbNewOTAImage = true ; //image validated
2021-01-26 23:55:53 +01:00
vMeshOtaUtilAddAllNeighboursToQueue ( NULL ) ; //add all existing neighbours to queue (aparent will not be added because this node is the root)
2021-01-20 21:40:51 +01:00
}
}
else
{
//error occurred --> abort ota update process
2021-01-21 11:11:37 +01:00
ESP_LOGE ( LOG_TAG , " abort OTA process due to error 0x%x -> %s " , err , esp_err_to_name ( err ) ) ;
2021-01-20 21:40:51 +01:00
ERROR_CHECK ( esp_ota_abort ( otaHandle ) ) ;
2021-01-20 22:39:18 +01:00
* cpbNewOTAImage = false ; //ota update failed
2021-01-20 21:40:51 +01:00
}
}
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 ;
}
2021-01-21 11:11:37 +01:00
/**
* @ fn esp_err_t errMeshOtaPartitionAccessMeshTransmit ( const mesh_addr_t * const cpcMeshNodeAddr )
* @ brief Reads the local image and sends it to node
* @ param cpcMeshNodeAddr pointer to mesh node addr to send the image segments to
* @ return ESP32 error code
* @ author Hendrik Schutter
* @ date 21.01 .2021
*
* Reads the newest OTA image in segments
* Sends the image to mesh node in segments
* Handles OTA process
*/
2021-01-20 22:39:18 +01:00
esp_err_t errMeshOtaPartitionAccessMeshTransmit ( const mesh_addr_t * const cpcMeshNodeAddr )
2021-01-20 20:55:33 +01:00
{
esp_err_t err = ESP_OK ;
2021-01-21 11:11:37 +01:00
const esp_partition_t * pBootPartition = NULL ; //pointer to boot partition (that will booted after reset)
2021-01-20 20:55:33 +01:00
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
2021-01-21 11:11:37 +01:00
uint32_t u32OTABytesWritten = 0U ; //counter of bytes unsed for progress log
uint32_t u32SegmentCounter = 0U ; //counter of segments unsed for progress log
2021-01-20 20:55:33 +01:00
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 ) )
{
2021-01-21 11:11:37 +01:00
bNodeIsResponding = false ; //reset to default for this loop
2021-01-20 20:55:33 +01:00
// read partition with offset based in index
ERROR_CHECK ( esp_partition_read ( pBootPartition , ( OTA_MESH_SEGMENT_SIZE * u32SegmentCounter ) , sMeshPacket . au8Payload , OTA_MESH_SEGMENT_SIZE ) ) ;
2021-01-21 11:11:37 +01:00
u32OTABytesWritten = ( ( u32SegmentCounter + 1 ) * OTA_MESH_SEGMENT_SIZE ) ; //calc bytes that are written in this ota process
2021-01-21 11:43:09 +01:00
vMeshOtaUtilPrintOtaProgress ( & ( pBootPartition - > size ) , & u32OTABytesWritten , Transmitter ) ;
2021-01-20 20:55:33 +01:00
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
2021-01-26 23:55:53 +01:00
ESP_LOGI ( LOG_TAG , " OTA-TX: last segment--> send Complete " ) ;
2021-01-20 20:55:33 +01:00
sMeshPacket . type = OTA_Complete ;
}
2021-01-27 16:47:25 +01:00
2021-01-20 22:39:18 +01:00
err = errMeshNetworkSendMeshPacket ( cpcMeshNodeAddr , & sMeshPacket ) ;
2021-01-20 20:55:33 +01:00
}
else
{
// error while read --> send OTA_ABORT and abort this OTA process
sMeshPacket . type = OTA_Abort ;
bAbort = true ;
2021-01-21 11:11:37 +01:00
ESP_LOGE ( LOG_TAG , " OTA-TX: error while read --> send ABORT " ) ;
2021-01-20 22:39:18 +01:00
errMeshNetworkSendMeshPacket ( cpcMeshNodeAddr , & sMeshPacket ) ;
2021-01-20 20:55:33 +01:00
}
// 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
{
2021-01-21 11:11:37 +01:00
//get OTA message from queue
2021-01-20 20:55:33 +01:00
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 ;
}
2021-01-21 11:11:37 +01:00
//check if from correct node
2021-01-21 11:43:09 +01:00
if ( ( err = = ESP_OK ) & & ( bMeshNetworkCheckMacEquality ( sMeshPacket . meshSenderAddr . addr , cpcMeshNodeAddr - > addr ) ) )
2021-01-20 20:55:33 +01:00
{
2021-01-21 11:11:37 +01:00
//packet from node received --> handle it
2021-01-20 20:55:33 +01:00
switch ( sMeshPacket . type )
{
2021-01-21 11:11:37 +01:00
case OTA_ACK : //start next loop for segment
2021-01-20 20:55:33 +01:00
bNodeIsResponding = true ;
2021-01-21 11:11:37 +01:00
u32Index = QUEUE_MESSAGE_OTA_SIZE ; //this will end the loop through all OTA messages
2021-01-20 20:55:33 +01:00
break ;
case OTA_Abort : //abort this OTA process
bAbort = true ;
bNodeIsResponding = true ;
break ;
default :
2021-01-26 23:55:53 +01:00
ESP_LOGI ( LOG_TAG , " OTA-TX: no ACK or ABORT message received " ) ;
2021-01-20 20:55:33 +01:00
break ;
}
}
} //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
2021-01-21 11:11:37 +01:00
vMeshOtaUtilClearOtaMessageQueue ( cpcMeshNodeAddr ) ; //remove all OTA messages from remote node
2021-01-20 20:55:33 +01:00
return err ;
}
2021-01-21 11:11:37 +01:00
/**
* @ fn esp_err_t errMeshOtaPartitionAccessMeshReceive ( bool * const cpbNewOTAImage , const mesh_addr_t * const cpcMeshNodeAddr )
2021-01-21 11:43:09 +01:00
* @ brief Downloads and writes the image from the remote node
2021-01-21 11:11:37 +01:00
* @ param cpbNewOTAImage pointer to boolean to signal if a new image was successfully received
* @ param cpcMeshNodeAddr pointer to mesh node addr to receive the image segments from
* @ return ESP32 error code
* @ author Hendrik Schutter
* @ date 21.01 .2021
*
* Receives the images segments from remote node
* Writtes segments to OTA partition
* Handles OTA process
*/
2021-01-20 22:39:18 +01:00
esp_err_t errMeshOtaPartitionAccessMeshReceive ( bool * const cpbNewOTAImage , const mesh_addr_t * const cpcMeshNodeAddr )
2021-01-20 20:55:33 +01:00
{
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
2021-01-21 11:11:37 +01:00
* cpbNewOTAImage = false ; //set default to false
uint32_t u32SegmentCounter = 0U ; //counter of segments unsed for progress log
2021-01-20 20:55:33 +01:00
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
{
//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 ;
}
2021-01-21 11:43:09 +01:00
if ( ( err = = ESP_OK ) & & ( bMeshNetworkCheckMacEquality ( sMeshPacket . meshSenderAddr . addr , cpcMeshNodeAddr - > addr ) ) ) //if OTA_Version_Request
2021-01-20 20:55:33 +01:00
{
//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 ;
2021-01-21 11:43:09 +01:00
//fall through
2021-01-20 20:55:33 +01:00
case OTA_Data : //data segement received
bNodeIsResponding = true ;
ERROR_CHECK ( esp_ota_write ( otaHandle , sMeshPacket . au8Payload , OTA_MESH_SEGMENT_SIZE ) ) ;
2021-01-21 11:11:37 +01:00
u32OTABytesWritten = ( ( u32SegmentCounter + 1 ) * OTA_MESH_SEGMENT_SIZE ) ; //calc bytes that are written in this ota process
2021-01-21 11:43:09 +01:00
vMeshOtaUtilPrintOtaProgress ( & ( pOTAPartition - > size ) , & u32OTABytesWritten , Receiver ) ;
2021-01-20 20:55:33 +01:00
u32Index = QUEUE_MESSAGE_OTA_SIZE ; //this will end the loop through all OTA messages
break ;
case OTA_Abort : //abort this OTA process
2021-01-26 23:55:53 +01:00
ESP_LOGE ( LOG_TAG , " OTA-RX: receives abort " ) ;
2021-01-20 20:55:33 +01:00
bAbort = true ;
bNodeIsResponding = true ;
2021-01-21 11:11:37 +01:00
ESP_LOGE ( LOG_TAG , " OTA-RX: receives abort --> abort this OTA process on this node " ) ;
2021-01-20 20:55:33 +01:00
//this will end the loop through all OTA messages
break ;
default :
break ;
}
}
else if ( err = = ESP_OK )
{
//received from wrong node --> back to queue
2021-01-20 21:40:51 +01:00
vMeshOtaUtilAddOtaMessageToQueue ( & sMeshPacket ) ;
2021-01-20 20:55:33 +01:00
}
} //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
sMeshPacket . type = OTA_ACK ;
2021-01-20 22:39:18 +01:00
err = errMeshNetworkSendMeshPacket ( cpcMeshNodeAddr , & sMeshPacket ) ;
2021-01-20 20:55:33 +01:00
}
}
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 " ) ;
2021-01-20 22:39:18 +01:00
errMeshNetworkSendMeshPacket ( cpcMeshNodeAddr , & sMeshPacket ) ;
2021-01-20 20:55:33 +01:00
}
}
u32SegmentCounter + + ;
} //end of partition segement loop
if ( bComplete = = true )
{
//all OTA segments received --> validate
ESP_LOGI ( LOG_TAG , " OTA-RX: validate image " ) ;
2021-01-21 11:11:37 +01:00
ERROR_CHECK ( esp_ota_end ( otaHandle ) ) ; //validate image
2021-01-20 20:55:33 +01:00
ERROR_CHECK ( esp_ota_set_boot_partition ( pOTAPartition ) ) ;
if ( err = = ESP_OK )
{
//successfully updated OTA partition
2021-01-20 22:39:18 +01:00
* cpbNewOTAImage = true ;
2021-01-20 20:55:33 +01:00
}
}
else
{
//not all OTA segments received --> abort this OTA process
ERROR_CHECK ( esp_ota_abort ( otaHandle ) ) ;
}
2021-01-20 22:39:18 +01:00
vMeshOtaUtilClearOtaMessageQueue ( cpcMeshNodeAddr ) ;
2021-01-20 20:55:33 +01:00
return err ;
}