2021-01-21 11:11:37 +01:00
/**
* @ file Mesh_OTA_Util . c
* @ brief Utility and helper functions to perfrom mesh OTA updates
* @ author Hendrik Schutter
* @ date 21.01 .2021
*/
2021-01-20 20:55:33 +01:00
# include "Mesh_OTA_Util.h"
# include "Mesh_OTA_Globals.h"
static const char * LOG_TAG = " mesh_ota " ;
2021-01-21 11:11:37 +01:00
/**
* @ fn bool bMeshOtaUtilNewerVersion ( const char * cpu8Local , const char * cpu8Remote )
* @ brief compares to version strings
* @ param cpu8Local local image version string
* @ param cpu8Remote remote image version string
* @ return bool
* @ author Hendrik Schutter
* @ date 21.01 .2021
*
* Returns true if remote is newer
*/
2021-01-20 22:39:18 +01:00
bool bMeshOtaUtilNewerVersion ( const char * cpu8Local , const char * cpu8Remote )
2021-01-20 20:55:33 +01:00
{
char u8LocalTmp [ 12 ] ; //local version
char u8RemoteTmp [ 12 ] ; //remote version
2021-01-21 11:11:37 +01:00
char * pu8saveptrLocal = NULL ; //context for strok_r
char * pu8saveptrRemote = NULL ; //context for strok_r
2021-01-20 20:55:33 +01:00
bool bReturn = false ; //flag to stop loop
uint8_t u8Index = 0 ; //numbers counter in version string
2021-01-20 22:39:18 +01:00
strncpy ( u8LocalTmp , cpu8Local , 12 ) ; //copy in tmp
strncpy ( u8RemoteTmp , cpu8Remote , 12 ) ; //copy in tmp
2021-01-20 20:55:33 +01:00
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 ;
}
2021-01-21 11:11:37 +01:00
/**
* @ fn esp_err_t errMeshOtaUtilExtractVersionNumber ( const char * cpu8Data , uint32_t * const cpcu32DataLenght , char * const pc8RemoteVersionNumber )
* @ brief extract version number from image data
* @ param cpu8Data image data buffer
* @ param cpcu32DataLenght pointer to lenght of image data
* @ param pc8RemoteVersionNumber pointer version number
* @ return ESP32 error code
* @ author Hendrik Schutters
* @ date 21.01 .2021
*
* Search version number in raw image data
*/
2021-01-20 22:39:18 +01:00
esp_err_t errMeshOtaUtilExtractVersionNumber ( const char * cpu8Data , uint32_t * const cpcu32DataLenght , char * const pc8RemoteVersionNumber )
2021-01-20 21:40:51 +01:00
{
uint32_t u32StartOffset ;
esp_err_t err = ESP_OK ;
strcpy ( pc8RemoteVersionNumber , " 999.999.999 " ) ; //init value
2021-01-20 22:39:18 +01:00
err = errMeshOtaUtilFindImageStart ( cpu8Data , cpcu32DataLenght , & u32StartOffset ) ; //get image start offset
2021-01-20 21:40:51 +01:00
if ( err = = ESP_OK )
{
//image found
2021-01-20 22:39:18 +01:00
strncpy ( pc8RemoteVersionNumber , cpu8Data + ( u32StartOffset + 48 ) , 11 ) ; //copy version number
2021-01-20 21:40:51 +01:00
pc8RemoteVersionNumber [ 12 ] = ' \0 ' ;
}
return err ;
}
2021-01-21 11:11:37 +01:00
/**
* @ fn esp_err_t errMeshOtaUtilFindImageStart ( const char * const cpu8Data , const uint32_t * const cpcu32DataLenght , uint32_t * const cpu32StartOffset )
* @ brief find start offset from image raw data
* @ param cpu8Data image data buffer
* @ param cpcu32DataLenght pointer to lenght of image data
* @ param cpu32StartOffset pointer to determined offset
* @ return ESP32 error code
* @ author Hendrik Schutter
* @ date 21.01 .2021
*
* Search offset in raw image data from server ( exclude HTTP response )
*/
2021-01-20 22:39:18 +01:00
esp_err_t errMeshOtaUtilFindImageStart ( const char * const cpu8Data , const uint32_t * const cpcu32DataLenght , uint32_t * const cpu32StartOffset )
2021-01-20 20:55:33 +01:00
{
2021-01-21 11:43:09 +01:00
2021-01-21 11:11:37 +01:00
// Offset value
// 0 = 0xE9 (first byte in image --> magic byte)
// 48 = first digit of version number
2021-01-21 11:43:09 +01:00
2021-01-20 20:55:33 +01:00
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 ;
2021-01-20 22:39:18 +01:00
* cpu32StartOffset = 0U ; //reset offset to zero
2021-01-20 20:55:33 +01:00
2021-01-20 22:39:18 +01:00
while ( ( u32DataIndex < * cpcu32DataLenght ) & & ( bImageStartOffsetFound = = false ) )
2021-01-20 20:55:33 +01:00
{
//search for magic byte
2021-01-20 22:39:18 +01:00
if ( cpu8Data [ u32DataIndex ] = = 0xe9 )
2021-01-20 20:55:33 +01:00
{
//magic byte found
while ( ( u8FirstDotIndex < 3 ) & & ( u32FirstDotOffset = = 0 ) )
{
//search first dot in version number
2021-01-20 22:39:18 +01:00
if ( ( u32DataIndex + 49 + u8FirstDotIndex ) < * cpcu32DataLenght )
2021-01-20 20:55:33 +01:00
{
2021-01-20 22:39:18 +01:00
if ( ( cpu8Data [ ( u32DataIndex + 49 + u8FirstDotIndex ) ] = = 0x2e ) )
2021-01-20 20:55:33 +01:00
{
//first dot found
u32FirstDotOffset = ( u32DataIndex + 49 + u8FirstDotIndex ) ;
}
}
u8FirstDotIndex + + ;
}
while ( ( u8SecondDotIndex < 3 ) & & ( u32SecondDotOffset = = 0 ) & & ( u32FirstDotOffset ! = 0 ) )
{
//search first dot in version number
2021-01-20 22:39:18 +01:00
if ( ( u32FirstDotOffset + ( u8SecondDotIndex + 2 ) ) < * cpcu32DataLenght )
2021-01-20 20:55:33 +01:00
{
2021-01-20 22:39:18 +01:00
if ( ( cpu8Data [ ( u32FirstDotOffset + ( u8SecondDotIndex + 2 ) ) ] = = 0x2e ) )
2021-01-20 20:55:33 +01:00
{
//second dot found
u32SecondDotOffset = ( u32FirstDotOffset + ( u8SecondDotIndex + 2 ) ) ;
}
}
u8SecondDotIndex + + ;
}
if ( ( u32FirstDotOffset ! = 0 ) & & ( u32SecondDotOffset ! = 0 ) )
{
//image start found based on magic byte and version number systax
2021-01-20 22:39:18 +01:00
* cpu32StartOffset = u32DataIndex ; //store image start offset
2021-01-20 20:55:33 +01:00
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 ;
}
2021-01-21 11:11:37 +01:00
/**
2021-01-21 11:43:09 +01:00
* @ fn esp_err_t errMeshOtaUtilSendOtaVersionRequest ( const mesh_addr_t * const cpcMeshReceiverAddr )
2021-01-21 11:11:37 +01:00
* @ brief send OTA_Version_Request to node
* @ param cpcMeshReceiverAddr node addr
* @ return ESP32 error code
* @ author Hendrik Schutter
* @ date 21.01 .2021
*/
2021-01-21 11:43:09 +01:00
esp_err_t errMeshOtaUtilSendOtaVersionRequest ( const mesh_addr_t * const cpcMeshReceiverAddr )
2021-01-20 20:55:33 +01:00
{
esp_err_t err = ESP_OK ;
2021-01-20 21:40:51 +01:00
MESH_PACKET_t packet ;
packet . type = OTA_Version_Request ;
2021-01-20 20:55:33 +01:00
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 21:40:51 +01:00
esp_app_desc_t bootPartitionDesc ; //Metadata from boot partition
2021-01-20 20:55:33 +01:00
2021-01-20 21:40:51 +01:00
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
2021-01-20 22:39:18 +01:00
err = errMeshNetworkSendMeshPacket ( cpcMeshReceiverAddr , & packet ) ;
2021-01-20 20:55:33 +01:00
return err ;
}
2021-01-21 11:11:37 +01:00
/**
2021-01-21 11:43:09 +01:00
* @ fn esp_err_t errMeshOtaUtilSendOtaVersionResponse ( const mesh_addr_t * const cpcMeshReceiverAddr )
2021-01-21 11:11:37 +01:00
* @ brief send OTA_Version_Response to node
* @ param cpcMeshReceiverAddr node addr
* @ return ESP32 error code
* @ author Hendrik Schutter
* @ date 21.01 .2021
*/
2021-01-21 11:43:09 +01:00
esp_err_t errMeshOtaUtilSendOtaVersionResponse ( const mesh_addr_t * const cpcMeshReceiverAddr )
2021-01-20 21:40:51 +01:00
{
esp_err_t err = ESP_OK ;
MESH_PACKET_t packet ;
packet . type = OTA_Version_Response ;
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 21:40:51 +01:00
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
2021-01-21 11:11:37 +01:00
ESP_LOGD ( LOG_TAG , " Send OTA_Version_Response to 0x%x " , cpcMeshReceiverAddr - > addr [ 5 ] ) ;
2021-01-20 21:40:51 +01:00
2021-01-20 22:39:18 +01:00
err = errMeshNetworkSendMeshPacket ( cpcMeshReceiverAddr , & packet ) ;
2021-01-20 21:40:51 +01:00
return err ;
}
2021-01-21 11:11:37 +01:00
/**
2021-01-21 11:43:09 +01:00
* @ fn void vMeshOtaUtilPrintOtaProgress ( const uint32_t * const cpcu32TotalImageSize , const uint32_t * const cpcu32BytesWritten , const OTA_MESH_ROLE_t ceRole )
2021-01-21 11:11:37 +01:00
* @ brief print LOG for OTA process progress
* @ param cpcu32TotalImageSize size of OTA partition
* @ param cpcu32BytesWritten actual bytes written
* @ param ceRole role if this OTA process
* @ return void
* @ author Hendrik Schutter
* @ date 21.01 .2021
*/
2021-01-21 11:43:09 +01:00
void vMeshOtaUtilPrintOtaProgress ( const uint32_t * const cpcu32TotalImageSize , const uint32_t * const cpcu32BytesWritten , const OTA_MESH_ROLE_t ceRole )
2021-01-20 20:55:33 +01:00
{
uint32_t u32Percentage = 0U ;
static uint32_t u32LastPercentage = 0U ;
2021-01-20 22:39:18 +01:00
if ( ( * cpcu32BytesWritten ) > = ( * cpcu32TotalImageSize ) )
2021-01-20 20:55:33 +01:00
{
u32Percentage = 100 ;
}
else
{
2021-01-20 22:39:18 +01:00
u32Percentage = ( uint32_t ) ( ( ( float ) ( * cpcu32BytesWritten ) / ( float ) ( * cpcu32TotalImageSize ) ) * 100.0 ) ;
2021-01-20 20:55:33 +01:00
}
if ( ( u32Percentage - u32LastPercentage ) > = OTA_PROGRESS_LOG_INTERVAL )
{
2021-01-20 22:39:18 +01:00
if ( ceRole = = Transmitter )
2021-01-20 20:55:33 +01:00
{
ESP_LOGI ( LOG_TAG , " Transmitting OTA update: %i %% " , u32Percentage ) ;
}
2021-01-20 22:39:18 +01:00
if ( ceRole = = Receiver )
2021-01-20 20:55:33 +01:00
{
ESP_LOGI ( LOG_TAG , " Receiving OTA update: %i %% " , u32Percentage ) ;
}
u32LastPercentage = u32Percentage ;
}
}
2021-01-21 11:11:37 +01:00
/**
* @ fn void vMeshOtaUtilAddAllNeighboursToQueue ( void )
* @ brief add all neigbhours ( children and parent ) to queue
* @ param void
* @ return void
* @ author Hendrik Schutter
* @ date 21.01 .2021
*/
2021-01-20 21:40:51 +01:00
void vMeshOtaUtilAddAllNeighboursToQueue ( void )
2021-01-20 20:55:33 +01:00
{
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
2021-01-20 21:40:51 +01:00
err = errMeshNetworkGetParentNode ( & addrParent ) ;
2021-01-20 20:55:33 +01:00
if ( err = = ESP_OK )
{
2021-01-20 21:40:51 +01:00
vMeshOtaUtilAddNodeToPossibleUpdatableQueue ( addrParent . addr ) ;
2021-01-20 20:55:33 +01:00
}
err = ESP_OK ; //reset error code
2021-01-20 21:40:51 +01:00
ERROR_CHECK ( errMeshNetworkGetChildren ( childrenAddr , & u16ChildrenSize ) ) ; //get all children
2021-01-20 20:55:33 +01:00
for ( uint16_t u16Index = 0 ; ( ( u16Index < u16ChildrenSize ) & & ( err = = ESP_OK ) ) ; u16Index + + )
{
2021-01-20 21:40:51 +01:00
vMeshOtaUtilAddNodeToPossibleUpdatableQueue ( childrenAddr [ u16Index ] . addr ) ;
2021-01-20 20:55:33 +01:00
}
}
2021-01-21 11:11:37 +01:00
/**
* @ fn void vMeshOtaUtilClearOtaMessageQueue ( const mesh_addr_t * const cpcMeshNodeAddr )
* @ brief remode all OTA messages from this node in queue
* @ param cpcMeshNodeAddr node addr
* @ return void
* @ author Hendrik Schutter
* @ date 21.01 .2021
*/
2021-01-20 22:39:18 +01:00
void vMeshOtaUtilClearOtaMessageQueue ( const mesh_addr_t * const cpcMeshNodeAddr )
2021-01-20 20:55:33 +01:00
{
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 )
{
2021-01-21 11:43:09 +01:00
if ( ! ( bMeshNetworkCheckMacEquality ( sMeshPacket . meshSenderAddr . addr , cpcMeshNodeAddr - > addr ) ) )
2021-01-20 20:55:33 +01:00
{
2021-01-20 22:39:18 +01:00
//received OTA message is NOT from cpcMeshNodeAddr --> keep it in queue
2021-01-20 21:40:51 +01:00
vMeshOtaUtilAddOtaMessageToQueue ( & sMeshPacket ) ;
2021-01-20 20:55:33 +01:00
}
}
} //end OTA message loop
}
2021-01-21 11:11:37 +01:00
/**
* @ fn void vMeshOtaUtilClearNeighboursQueue ( const mesh_addr_t * const cpcMeshNodeAddr )
* @ brief remode all instances of this node in queue
* @ param cpcMeshNodeAddr node addr
* @ return void
* @ author Hendrik Schutter
* @ date 21.01 .2021
*/
2021-01-20 23:04:18 +01:00
void vMeshOtaUtilClearNeighboursQueue ( const mesh_addr_t * const cpcMeshNodeAddr )
{
mesh_addr_t sNode ; //packet for sending and receiving
for ( uint32_t u32Index = 0 ; ( u32Index < QUEUE_MESSAGE_OTA_SIZE ) ; u32Index + + ) //loop through all OTA messages
{
if ( xQueueReceive ( queueNodes , & sNode , 0 ) = = pdTRUE )
{
2021-01-21 11:43:09 +01:00
if ( ! ( bMeshNetworkCheckMacEquality ( sNode . addr , cpcMeshNodeAddr - > addr ) ) )
2021-01-20 23:04:18 +01:00
{
//node is NOT cpcMeshNodeAddr --> keep it in queue
vMeshOtaUtilAddNodeToPossibleUpdatableQueue ( cpcMeshNodeAddr - > addr ) ;
}
}
} //end OTA message loop
}
2021-01-21 11:11:37 +01:00
/**
* @ fn void vMeshOtaUtilAddNodeToPossibleUpdatableQueue ( const uint8_t * const cpcu8MAC )
* @ brief add node instance to queue
* @ param cpcu8MAC MAC addr of node
* @ return void
* @ author Hendrik Schutter
* @ date 21.01 .2021
*/
2021-01-20 22:39:18 +01:00
void vMeshOtaUtilAddNodeToPossibleUpdatableQueue ( const uint8_t * const cpcu8MAC )
2021-01-20 20:55:33 +01:00
{
2021-01-20 21:40:51 +01:00
//send payload to node queues
mesh_addr_t addrNode ;
2021-01-20 22:39:18 +01:00
memcpy ( & addrNode . addr , ( uint8_t * ) cpcu8MAC , 6 ) ; //copy MAC
2021-01-20 20:55:33 +01:00
2021-01-20 21:40:51 +01:00
if ( xQueueSend ( queueNodes , & addrNode , portMAX_DELAY ) ! = pdPASS )
{
ESP_LOGE ( LOG_TAG , " Unable to push node into node queue " ) ;
}
else
{
2021-01-21 11:11:37 +01:00
ESP_LOGD ( 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 ] ) ;
2021-01-20 21:40:51 +01:00
}
}
2021-01-20 20:55:33 +01:00
2021-01-21 11:11:37 +01:00
/**
* @ fn void vMeshOtaUtilAddOtaMessageToQueue ( const MESH_PACKET_t * const cpcuMeshPacket )
* @ brief add OTA message to queue
* @ param cpcuMeshPacket OTA message
* @ return void
* @ author Hendrik Schutter
* @ date 21.01 .2021
*/
2021-01-20 22:39:18 +01:00
void vMeshOtaUtilAddOtaMessageToQueue ( const MESH_PACKET_t * const cpcuMeshPacket )
2021-01-20 21:40:51 +01:00
{
//send ota packet to packet queue
2021-01-20 22:39:18 +01:00
if ( xQueueSend ( queueMessageOTA , cpcuMeshPacket , portMAX_DELAY ) ! = pdPASS )
2021-01-20 21:40:51 +01:00
{
ESP_LOGE ( LOG_TAG , " Unable to push ota packet into packet queue " ) ;
}
else
{
2021-01-20 22:39:18 +01:00
switch ( cpcuMeshPacket - > type )
2021-01-20 21:40:51 +01:00
{
case OTA_Abort :
2021-01-21 11:11:37 +01:00
ESP_LOGD ( LOG_TAG , " added ota message to queue: OTA_Abort from 0x%x " , cpcuMeshPacket - > meshSenderAddr . addr [ 5 ] ) ;
2021-01-20 21:40:51 +01:00
break ;
case OTA_Version_Request :
2021-01-21 11:11:37 +01:00
ESP_LOGD ( LOG_TAG , " added ota message to queue: OTA_Version_Request from 0x%x " , cpcuMeshPacket - > meshSenderAddr . addr [ 5 ] ) ;
2021-01-20 21:40:51 +01:00
break ;
case OTA_Version_Response :
2021-01-21 11:11:37 +01:00
ESP_LOGD ( LOG_TAG , " added ota message to queue: OTA_Version Response from 0x%x " , cpcuMeshPacket - > meshSenderAddr . addr [ 5 ] ) ;
2021-01-20 21:40:51 +01:00
break ;
default :
break ;
}
}
2021-01-20 20:55:33 +01:00
}
2021-01-21 11:11:37 +01:00
/**
* @ fn void vMeshOtaUtilChangeStateOfServerWorker ( const bool cbState )
* @ brief callback for mesh network if connectivity to server changed
* @ param cbState boolean state
* @ return void
* @ author Hendrik Schutter
* @ date 21.01 .2021
*/
void vMeshOtaUtilChangeStateOfServerWorker ( const bool cbState )
2021-01-20 21:40:51 +01:00
{
static bool bLastState = false ;
2021-01-20 22:39:18 +01:00
if ( cbState ! = bLastState ) //change only if necessary
2021-01-20 21:40:51 +01:00
{
2021-01-20 22:39:18 +01:00
if ( cbState = = true )
2021-01-20 21:40:51 +01:00
{
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 " ) ;
}
}
2021-01-20 22:39:18 +01:00
bLastState = cbState ;
2021-01-20 21:40:51 +01:00
}
}