2021-01-11 22:56:39 +01:00
# include "Mesh_OTA.h"
2021-01-02 00:30:13 +01:00
2021-01-17 23:27:01 +01:00
static const char * LOG_TAG = " mesh_ota " ;
xQueueHandle queueNodes ; //nodes that should be checked for ota update (contains children and parent)
xQueueHandle queueMessageOTA ; //mesh ota controll messages like "OTA_Version_Response" "OTA_ACK"
SemaphoreHandle_t bsStartStopServerWorker ; //binary semaphore
2021-01-18 17:38:08 +01:00
const esp_partition_t * pOTAPartition ; //pointer to ota partition
2021-01-17 23:27:01 +01:00
esp_err_t errMeshOTAInitialize ( )
{
esp_err_t err = ESP_OK ;
BaseType_t xReturned ;
//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 ;
}
}
ERROR_CHECK ( errMeshNetworkSetChildConnectedHandle ( vAddNodeToPossibleUpdatableQueue ) ) ;
ERROR_CHECK ( errMeshNetworkSetOTAMessageHandleHandle ( vAddOTAControllMessageToQueue ) ) ;
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 ;
}
}
return err ;
}
void vAddNodeToPossibleUpdatableQueue ( uint8_t * pu8Data )
{
//send payload to node queue
mesh_addr_t addrNode ;
memcpy ( & addrNode . addr , ( uint8_t * ) pu8Data , 6 ) ; //copy MAC
if ( xQueueSend ( queueNodes , & addrNode , portMAX_DELAY ) ! = pdPASS )
{
ESP_LOGE ( LOG_TAG , " Unable to push node into node queue " ) ;
}
else
{
ESP_LOGI ( 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 ] ) ;
}
}
void vAddOTAControllMessageToQueue ( MESH_PACKET_t * puMeshPacket )
{
//send ota packet to packet queue
if ( xQueueSend ( queueMessageOTA , puMeshPacket , portMAX_DELAY ) ! = pdPASS )
{
ESP_LOGE ( LOG_TAG , " Unable to push ota packet into packet queue " ) ;
}
else
{
ESP_LOGI ( LOG_TAG , " added ota controll message to queue " ) ;
}
}
void vChangeStateOfServerWorker ( bool bState ) //allow access via function ptn to networl_handler
{
static bool bLastState = false ;
if ( bState ! = bLastState ) //change only if necessary
{
ESP_LOGI ( LOG_TAG , " server worker change handler " ) ;
if ( bState = = true )
{
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 " ) ;
}
}
bLastState = bState ;
}
}
void vTaskOTAWorker ( void * arg )
{
}
void vTaskServerWorker ( void * arg )
{
2021-01-18 12:49:52 +01:00
esp_err_t err ;
bool bNewOTAImage = false ; // 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 ;
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 17:38:08 +01:00
if ( bFirstRun = = true )
{
2021-01-18 12:49:52 +01:00
ERROR_CHECK ( https_clientInitialize ( ) ) ;
2021-01-18 17:38:08 +01:00
bFirstRun = false ;
}
2021-01-18 12:49:52 +01:00
2021-01-18 17:38:08 +01:00
ERROR_CHECK ( https_clientConnectToServer ( ) ) ;
ERROR_CHECK ( https_clientValidateServer ( ) ) ;
ERROR_CHECK ( https_clientSendRequest ( ) ) ;
2021-01-18 12:49:52 +01:00
2021-01-18 17:38:08 +01:00
ERROR_CHECK ( errOTAHTTPS ( & bNewOTAImage ) ) ;
https_clientReset ( ) ;
2021-01-18 12:49:52 +01:00
if ( bNewOTAImage = = true )
{
//set want reboot
ESP_LOGI ( LOG_TAG , " Updated successfully via HTTPS, will reboot if possible " ) ;
}
vTaskDelay ( ( SERVER_CHECK_INTERVAL * 1000 ) / portTICK_PERIOD_MS ) ; //sleep till next server check
}
}
}
esp_err_t errOTAHTTPS ( bool * pbNewOTAImage )
{
2021-01-17 23:47:59 +01:00
esp_err_t err = ESP_OK ;
2021-01-18 17:38:08 +01:00
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
2021-01-17 23:27:01 +01:00
2021-01-18 17:38:08 +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
2021-01-17 23:27:01 +01:00
2021-01-18 17:38:08 +01:00
ERROR_CHECK ( https_clientRetrieveData ( u8OTABuffer , & u32BufferLenght , & u32BytesRead ) ) ; //read first bytes if image, including the version
2021-01-17 23:47:59 +01:00
2021-01-18 17:38:08 +01:00
ERROR_CHECK ( errExtractVersionNumber ( u8OTABuffer , & u32BytesRead , pcRemoteVersionNumber ) ) ; //extract version numbers
2021-01-17 23:47:59 +01:00
2021-01-18 17:38:08 +01:00
if ( err = = ESP_OK ) //check if version number is found
2021-01-18 12:49:52 +01:00
{
2021-01-18 17:38:08 +01:00
if ( bNewerVersion ( ( bootPartitionDesc ) . version , pcRemoteVersionNumber ) ) //compare local and remote version
2021-01-18 12:49:52 +01:00
{
// server image is newer --> OTA update required
ESP_LOGI ( LOG_TAG , " server image is newer --> OTA update required " ) ;
2021-01-18 17:38:08 +01:00
ERROR_CHECK ( errFindImageStart ( u8OTABuffer , & u32BufferLenght , & u32StartOffset ) ) ; //get image start offset
2021-01-17 23:47:59 +01:00
2021-01-18 12:49:52 +01:00
//TODO lock ota mutex
2021-01-17 23:47:59 +01:00
2021-01-18 17:38:08 +01:00
ERROR_CHECK ( esp_ota_begin ( pOTAPartition , OTA_SIZE_UNKNOWN , & otaHandle ) ) ; //start ota update process
2021-01-18 12:49:52 +01:00
if ( err = = ESP_OK )
{
//image download and ota partition write
2021-01-18 17:38:08 +01:00
ESP_LOGI ( LOG_TAG , " start ota https download " ) ;
2021-01-18 12:49:52 +01:00
do
{
2021-01-18 17:38:08 +01:00
//TODO progress log
ERROR_CHECK ( esp_ota_write ( otaHandle , ( const void * ) u8OTABuffer + u32StartOffset , ( u32BytesRead - u32StartOffset ) ) ) ;
2021-01-17 23:47:59 +01:00
2021-01-18 12:49:52 +01:00
if ( err = = ESP_OK )
{
//write was succsesfull
u32StartOffset = 0U ; //reset the offset for next download
2021-01-18 17:38:08 +01:00
ERROR_CHECK ( https_clientRetrieveData ( u8OTABuffer , & u32BufferLenght , & u32BytesRead ) ) ; //download next data segment
2021-01-18 12:49:52 +01:00
}
2021-01-17 23:47:59 +01:00
}
2021-01-18 17:38:08 +01:00
while ( ( u32BytesRead > 0 ) & & ( err = = ESP_OK ) ) ; //loop until error or complete image downloaded
2021-01-18 12:49:52 +01:00
}
if ( err = = ESP_OK )
{
//no error occurred --> finish ota update process
ERROR_CHECK ( esp_ota_end ( otaHandle ) ) ; //finish process
2021-01-18 17:38:08 +01:00
ERROR_CHECK ( esp_ota_set_boot_partition ( pOTAPartition ) ) ; //set new image as boot
2021-01-18 12:49:52 +01:00
if ( err = = ESP_OK )
2021-01-17 23:47:59 +01:00
{
2021-01-18 12:49:52 +01:00
* pbNewOTAImage = true ; //image validated
2021-01-17 23:47:59 +01:00
}
}
else
{
2021-01-18 17:38:08 +01:00
//error occurred --> abort ota update process
ESP_LOGE ( LOG_TAG , " abort ota process due to error 0x%x -> %s " , err , esp_err_to_name ( err ) ) ;
2021-01-18 12:49:52 +01:00
ERROR_CHECK ( esp_ota_abort ( otaHandle ) ) ;
* pbNewOTAImage = false ; //ota update failed
2021-01-17 23:47:59 +01:00
}
2021-01-18 12:49:52 +01:00
//TODO unlock ota mutex
} //end newer version on server
} //end version number extracted
return err ;
2021-01-17 23:27:01 +01:00
}
2021-01-02 00:30:13 +01:00
/*
* Return true if remote version is newer ( higher ) than local version
*/
2021-01-16 18:23:10 +01:00
bool bNewerVersion ( const char * pu8Local , const char * pu8Remote )
2021-01-09 17:41:40 +01:00
{
char u8LocalTmp [ 12 ] ; //local version
char u8RemoteTmp [ 12 ] ; //remote version
2021-01-16 18:23:10 +01:00
char * pu8saveptrLocal ; //context for strok_r
char * pu8saveptrRemote ; //context for strok_r
2021-01-09 17:41:40 +01:00
bool bReturn = false ; //flag to stop loop
uint8_t u8Index = 0 ; //numbers counter in version string
2021-01-06 18:10:12 +01:00
2021-01-09 17:41:40 +01:00
strcpy ( u8LocalTmp , pu8Local ) ; //copy in tmp
strcpy ( u8RemoteTmp , pu8Remote ) ; //copy in tmp
2021-01-06 18:10:12 +01:00
2021-01-09 17:41:40 +01:00
char * pu8TokenLocal = strtok_r ( u8LocalTmp , " . " , & pu8saveptrLocal ) ; //split tokens
char * pu8TokenRemote = strtok_r ( u8RemoteTmp , " . " , & pu8saveptrRemote ) ; //split tokens
2021-01-06 18:10:12 +01:00
2021-01-09 17:41:40 +01:00
while ( ( u8Index < = 2 ) & & ( bReturn = = false ) ) //loop through tokens
2021-01-02 00:30:13 +01:00
{
2021-01-17 23:27:01 +01:00
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
2021-01-02 00:30:13 +01:00
}
return bReturn ;
}
2021-01-06 18:10:12 +01:00
2021-01-07 23:10:36 +01:00
esp_err_t errFindImageStart ( const char * pu8Data , uint32_t * pu32DataLenght , uint32_t * pu32StartOffset )
2021-01-06 18:10:12 +01:00
{
/*
Offset value
2021-01-09 17:41:40 +01:00
0 = 0xE9 ( first byte in image - - > magic byte )
2021-01-06 18:10:12 +01:00
48 = first digit of version number
*/
esp_err_t errReturn = ESP_OK ;
2021-01-07 17:10:25 +01:00
bool bImageStartOffsetFound = false ;
2021-01-06 18:10:12 +01:00
uint32_t u32DataIndex = 0 ;
uint32_t u32FirstDotOffset = 0 ;
uint32_t u32SecondDotOffset = 0 ;
uint8_t u8FirstDotIndex = 0 ;
uint8_t u8SecondDotIndex = 0 ;
2021-01-09 17:41:40 +01:00
* pu32StartOffset = 0U ; //reset offset to zero
2021-01-16 18:23:10 +01:00
2021-01-07 17:10:25 +01:00
while ( ( u32DataIndex < * pu32DataLenght ) & & ( bImageStartOffsetFound = = false ) )
2021-01-17 23:27:01 +01:00
{
//search for magic byte
if ( pu8Data [ u32DataIndex ] = = 0xe9 )
2021-01-06 18:10:12 +01:00
{
2021-01-17 23:27:01 +01:00
//magic byte found
while ( ( u8FirstDotIndex < 3 ) & & ( u32FirstDotOffset = = 0 ) )
{
//search first dot in version number
if ( ( u32DataIndex + 49 + u8FirstDotIndex ) < * pu32DataLenght )
{
if ( ( pu8Data [ ( u32DataIndex + 49 + u8FirstDotIndex ) ] = = 0x2e ) )
{
//first dot found
u32FirstDotOffset = ( u32DataIndex + 49 + u8FirstDotIndex ) ;
}
}
u8FirstDotIndex + + ;
}
2021-01-06 18:10:12 +01:00
2021-01-17 23:27:01 +01:00
while ( ( u8SecondDotIndex < 3 ) & & ( u32SecondDotOffset = = 0 ) & & ( u32FirstDotOffset ! = 0 ) )
{
//search first dot in version number
if ( ( u32FirstDotOffset + ( u8SecondDotIndex + 2 ) ) < * pu32DataLenght )
{
if ( ( pu8Data [ ( u32FirstDotOffset + ( u8SecondDotIndex + 2 ) ) ] = = 0x2e ) )
{
//second dot found
u32SecondDotOffset = ( u32FirstDotOffset + ( u8SecondDotIndex + 2 ) ) ;
}
}
u8SecondDotIndex + + ;
}
if ( ( u32FirstDotOffset ! = 0 ) & & ( u32SecondDotOffset ! = 0 ) )
{
//image start found based on magic byte and version number systax
* pu32StartOffset = u32DataIndex ; //store image start offset
bImageStartOffsetFound = true ;
}
else
{
// this is propably not the magic byte --> reset
u32FirstDotOffset = 0 ;
u32SecondDotOffset = 0 ;
u8FirstDotIndex = 0 ;
u8SecondDotIndex = 0 ;
}
2021-01-06 18:10:12 +01:00
}
2021-01-17 23:27:01 +01:00
u32DataIndex + + ;
2021-01-06 18:10:12 +01:00
}
2021-01-07 17:10:25 +01:00
if ( bImageStartOffsetFound = = false )
2021-01-17 23:27:01 +01:00
{
errReturn = ESP_ERR_NOT_FOUND ;
}
2021-01-06 18:10:12 +01:00
return errReturn ;
}
2021-01-07 23:10:36 +01:00
esp_err_t errExtractVersionNumber ( const char * pu8Data , uint32_t * pu32DataLenght , char * pc8RemoteVersionNumber )
2021-01-06 18:10:12 +01:00
{
uint32_t u32StartOffset ;
2021-01-09 17:41:40 +01:00
esp_err_t err = ESP_OK ;
2021-01-06 18:10:12 +01:00
2021-01-09 17:41:40 +01:00
strcpy ( pc8RemoteVersionNumber , " 999.999.999 " ) ; //init value
err = errFindImageStart ( pu8Data , pu32DataLenght , & u32StartOffset ) ; //get image start offset
2021-01-06 18:10:12 +01:00
if ( err = = ESP_OK )
2021-01-17 23:27:01 +01:00
{
//image found
strncpy ( pc8RemoteVersionNumber , pu8Data + ( u32StartOffset + 48 ) , 11 ) ; //copy version number
pc8RemoteVersionNumber [ 12 ] = ' \0 ' ;
}
2021-01-06 18:10:12 +01:00
return err ;
2021-01-07 17:10:25 +01:00
}
2021-01-11 15:22:45 +01:00
/*
esp_err_t esp_mesh_ota_send ( mesh_addr_t * dest )
{
esp_err_t err = ESP_OK ;
static uint32_t u32index ;
2021-01-18 17:38:08 +01:00
const esp_partition_t * pBootPartition = esp_ota_get_boot_partition ( ) ;
2021-01-11 15:22:45 +01:00
2021-01-18 17:38:08 +01:00
if ( ( * pBootPartition ) . subtype = = 0 )
2021-01-11 15:22:45 +01:00
{
int data_read = 0 ;
struct ota_mesh_packet packet ;
packet . type = OTA_Data ;
if ( u32index = = 1024 )
{
//all data read
data_read = 0 ;
u32index = 0 ;
}
else
{
ESP_LOGI ( MESH_TAG , " OTA-Data read: %i " , u32index ) ;
2021-01-18 17:38:08 +01:00
err = esp_partition_read ( pBootPartition , ( 1024 * u32index ) , packet . au8Payload , 1024 ) ;
2021-01-11 15:22:45 +01:00
ESP_ERROR_CHECK ( err ) ;
data_read = 1024 ;
u32index + + ;
}
if ( data_read > 0 )
{
//send ota fragemnt to node
esp_mesh_send_packet ( dest , & packet ) ;
}
ESP_ERROR_CHECK ( err ) ;
}
else
{
2021-01-18 17:38:08 +01:00
ESP_LOGI ( MESH_TAG , " Subtype: %d " , ( * pBootPartition ) . subtype ) ;
2021-01-11 15:22:45 +01:00
}
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 ;
2021-01-18 17:38:08 +01:00
const esp_partition_t * pBootPartition = esp_ota_get_boot_partition ( ) ;
const esp_partition_t * pOTAPartition = esp_ota_get_next_update_partition ( pBootPartition ) ;
2021-01-11 15:22:45 +01:00
if ( u32index = = 0 )
{
//first run
2021-01-18 17:38:08 +01:00
err = esp_ota_begin ( pOTAPartition , OTA_SIZE_UNKNOWN , & otaHandle ) ;
2021-01-11 15:22:45 +01:00
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 ;
2021-01-18 17:38:08 +01:00
err = esp_ota_get_partition_description ( pOTAPartition , & otaPartitionDesc ) ;
2021-01-11 15:22:45 +01:00
ESP_ERROR_CHECK ( err ) ;
2021-01-18 17:38:08 +01:00
ESP_LOGI ( MESH_TAG , " pOTAPartition project_name: %s " , ( otaPartitionDesc ) . project_name ) ;
2021-01-11 15:22:45 +01:00
2021-01-18 17:38:08 +01:00
err = esp_ota_set_boot_partition ( pOTAPartition ) ;
2021-01-11 15:22:45 +01:00
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 ;
}
*/