1414
1515#include <string.h>
1616#include <freertos/FreeRTOS.h>
17+ #include <freertos/timers.h>
1718#include <freertos/task.h>
19+ #include <esp_event.h>
1820#include <esp_log.h>
1921#include <esp_ota_ops.h>
2022#include <esp_partition.h>
2123#include <esp_https_ota.h>
2224#include <esp_wifi_types.h>
2325#include <esp_wifi.h>
26+ #include <nvs.h>
2427#if CONFIG_BT_ENABLED
2528#include <esp_bt.h>
2629#endif /* CONFIG_BT_ENABLED */
2730
2831#include <esp_rmaker_utils.h>
32+ #include <esp_rmaker_common_events.h>
33+
34+ #include "esp_rmaker_internal.h"
2935#include "esp_rmaker_ota_internal.h"
3036
3137#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL (4 , 4 , 0 )
4450
4551#endif /* !IDF4.4 */
4652static const char * TAG = "esp_rmaker_ota" ;
53+ static TimerHandle_t s_ota_rollback_timer ;
4754
4855#define OTA_REBOOT_TIMER_SEC 10
4956#define DEF_HTTP_TX_BUFFER_SIZE 1024
5057#define DEF_HTTP_RX_BUFFER_SIZE CONFIG_ESP_RMAKER_OTA_HTTP_RX_BUFFER_SIZE
51-
58+ #define RMAKER_OTA_ROLLBACK_WAIT_PERIOD CONFIG_ESP_RMAKER_OTA_ROLLBACK_WAIT_PERIOD
5259extern const char esp_rmaker_ota_def_cert [] asm("_binary_rmaker_ota_server_crt_start" );
5360const char * ESP_RMAKER_OTA_DEFAULT_SERVER_CERT = esp_rmaker_ota_def_cert ;
5461char * esp_rmaker_ota_status_to_string (ota_status_t status )
@@ -71,6 +78,8 @@ char *esp_rmaker_ota_status_to_string(ota_status_t status)
7178}
7279esp_err_t esp_rmaker_ota_report_status (esp_rmaker_ota_handle_t ota_handle , ota_status_t status , char * additional_info )
7380{
81+ ESP_LOGI (TAG , "Reporting %s: %s" , esp_rmaker_ota_status_to_string (status ), additional_info );
82+
7483 if (!ota_handle ) {
7584 return ESP_FAIL ;
7685 }
@@ -267,7 +276,19 @@ esp_err_t esp_rmaker_ota_default_cb(esp_rmaker_ota_handle_t ota_handle, esp_rmak
267276 ota_finish_err = esp_https_ota_finish (https_ota_handle );
268277 if ((err == ESP_OK ) && (ota_finish_err == ESP_OK )) {
269278 ESP_LOGI (TAG , "OTA upgrade successful. Rebooting in %d seconds..." , OTA_REBOOT_TIMER_SEC );
279+ #ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
280+ nvs_handle handle ;
281+ esp_err_t err = nvs_open_from_partition (ESP_RMAKER_NVS_PART_NAME , RMAKER_OTA_NVS_NAMESPACE , NVS_READWRITE , & handle );
282+ if (err == ESP_OK ) {
283+ uint8_t ota_update = 1 ;
284+ nvs_set_blob (handle , RMAKER_OTA_UPDATE_FLAG_NVS_NAME , & ota_update , sizeof (ota_update ));
285+ nvs_close (handle );
286+ }
287+ /* Success will be reported after a reboot since Rollback is enabled */
288+ esp_rmaker_ota_report_status (ota_handle , OTA_STATUS_IN_PROGRESS , "Rebooting into new firmware" );
289+ #else
270290 esp_rmaker_ota_report_status (ota_handle , OTA_STATUS_SUCCESS , "OTA Upgrade finished successfully" );
291+ #endif
271292 esp_rmaker_reboot (OTA_REBOOT_TIMER_SEC );
272293 return ESP_OK ;
273294 } else {
@@ -284,6 +305,101 @@ esp_err_t esp_rmaker_ota_default_cb(esp_rmaker_ota_handle_t ota_handle, esp_rmak
284305 return ESP_FAIL ;
285306}
286307
308+
309+ static void event_handler (void * arg , esp_event_base_t event_base ,
310+ int32_t event_id , void * event_data )
311+ {
312+ esp_rmaker_ota_t * ota = (esp_rmaker_ota_t * )arg ;
313+ esp_event_handler_unregister (RMAKER_COMMON_EVENT , RMAKER_MQTT_EVENT_CONNECTED , & event_handler );
314+ esp_rmaker_ota_report_status ((esp_rmaker_ota_handle_t )ota , OTA_STATUS_SUCCESS , "OTA Upgrade finished and verified successfully" );
315+ esp_ota_mark_app_valid_cancel_rollback ();
316+ ota -> ota_in_progress = false;
317+ if (s_ota_rollback_timer ) {
318+ xTimerStop (s_ota_rollback_timer , portMAX_DELAY );
319+ xTimerDelete (s_ota_rollback_timer , portMAX_DELAY );
320+ s_ota_rollback_timer = NULL ;
321+ }
322+ if (ota -> type == OTA_USING_TOPICS ) {
323+ esp_rmaker_ota_fetch ();
324+ }
325+ }
326+
327+ static void esp_ota_rollback (TimerHandle_t handle )
328+ {
329+ ESP_LOGE (TAG , "Could not verify firmware even after %d seconds since boot-up. Rolling back." ,
330+ RMAKER_OTA_ROLLBACK_WAIT_PERIOD );
331+ esp_ota_mark_app_invalid_rollback_and_reboot ();
332+ }
333+
334+ static esp_err_t esp_ota_check_for_mqtt (esp_rmaker_ota_t * ota )
335+ {
336+ s_ota_rollback_timer = xTimerCreate ("ota_rollback_tm" , (RMAKER_OTA_ROLLBACK_WAIT_PERIOD * 1000 ) / portTICK_PERIOD_MS ,
337+ pdTRUE , NULL , esp_ota_rollback );
338+ if (s_ota_rollback_timer ) {
339+ xTimerStart (s_ota_rollback_timer , 0 );
340+ } else {
341+ ESP_LOGW (TAG , "Could not create rollback timer. Will require manual reboot if firmware verification fails" );
342+ }
343+
344+ return esp_event_handler_register (RMAKER_COMMON_EVENT , RMAKER_MQTT_EVENT_CONNECTED , & event_handler , ota );
345+ }
346+
347+ static void esp_rmaker_ota_manage_rollback (esp_rmaker_ota_config_t * ota_config , esp_rmaker_ota_t * ota )
348+ {
349+ const esp_partition_t * running = esp_ota_get_running_partition ();
350+ esp_ota_img_states_t ota_state ;
351+ if (esp_ota_get_state_partition (running , & ota_state ) == ESP_OK ) {
352+ ESP_LOGI (TAG , "OTA state = %d" , ota_state );
353+ /* Not checking for CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE here because the firmware may have
354+ * it disabled, but bootloader may have it enabled, in which case, we will have to
355+ * handle this state.
356+ */
357+ if (ota_state == ESP_OTA_IMG_PENDING_VERIFY ) {
358+ ESP_LOGI (TAG , "First Boot after an OTA" );
359+ /* Run diagnostic function */
360+ bool diagnostic_is_ok = true;
361+ if (ota_config -> ota_diag ) {
362+ diagnostic_is_ok = ota_config -> ota_diag ();
363+ }
364+ if (diagnostic_is_ok ) {
365+ ESP_LOGI (TAG , "Diagnostics completed successfully! Continuing execution ..." );
366+ /* Will not mark the image valid here immediately, but instead will wait for
367+ * MQTT connection. The below flag will tell the OTA functions that the earlier
368+ * OTA is still in progress.
369+ */
370+ ota -> ota_in_progress = true;
371+ esp_ota_check_for_mqtt (ota );
372+ } else {
373+ ESP_LOGE (TAG , "Diagnostics failed! Start rollback to the previous version ..." );
374+ esp_ota_mark_app_invalid_rollback_and_reboot ();
375+ }
376+ #ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
377+ } else {
378+ /* If rollback is enabled, and the ota update flag is found, it means that the firmware was rolled back
379+ */
380+ nvs_handle handle ;
381+ esp_err_t err = nvs_open_from_partition (ESP_RMAKER_NVS_PART_NAME , RMAKER_OTA_NVS_NAMESPACE , NVS_READWRITE , & handle );
382+ if (err == ESP_OK ) {
383+ uint8_t ota_update = 0 ;
384+ size_t len = sizeof (ota_update );
385+ if ((err = nvs_get_blob (handle , RMAKER_OTA_UPDATE_FLAG_NVS_NAME , & ota_update , & len )) == ESP_OK ) {
386+ ota -> rolled_back = true;
387+ nvs_erase_key (handle , RMAKER_OTA_UPDATE_FLAG_NVS_NAME );
388+ if (ota -> type == OTA_USING_PARAMS ) {
389+ /* Calling this only for OTA_USING_PARAMS, because for OTA_USING_TOPICS,
390+ * the work queue function will manage the status reporting later.
391+ */
392+ esp_rmaker_ota_report_status ((esp_rmaker_ota_handle_t )ota ,
393+ OTA_STATUS_REJECTED , "Firmware rolled back" );
394+ }
395+ }
396+ nvs_close (handle );
397+ }
398+ #endif
399+ }
400+ }
401+ }
402+
287403static const esp_rmaker_ota_config_t ota_default_config = {
288404 .server_cert = esp_rmaker_ota_def_cert ,
289405};
@@ -307,26 +423,6 @@ esp_err_t esp_rmaker_ota_enable(esp_rmaker_ota_config_t *ota_config, esp_rmaker_
307423 ESP_LOGE (TAG , "Failed to allocate memory for esp_rmaker_ota_t" );
308424 return ESP_ERR_NO_MEM ;
309425 }
310- const esp_partition_t * running = esp_ota_get_running_partition ();
311- esp_ota_img_states_t ota_state ;
312- if (esp_ota_get_state_partition (running , & ota_state ) == ESP_OK ) {
313- ESP_LOGI (TAG , "OTA state = %d" , ota_state );
314- if (ota_state == ESP_OTA_IMG_PENDING_VERIFY ) {
315- ESP_LOGI (TAG , "First Boot after an OTA" );
316- /* Run diagnostic function */
317- bool diagnostic_is_ok = true;
318- if (ota_config -> ota_diag ) {
319- diagnostic_is_ok = ota_config -> ota_diag ();
320- }
321- if (diagnostic_is_ok ) {
322- ESP_LOGI (TAG , "Diagnostics completed successfully! Continuing execution ..." );
323- esp_ota_mark_app_valid_cancel_rollback ();
324- } else {
325- ESP_LOGE (TAG , "Diagnostics failed! Start rollback to the previous version ..." );
326- esp_ota_mark_app_invalid_rollback_and_reboot ();
327- }
328- }
329- }
330426 if (ota_config -> ota_cb ) {
331427 ota -> ota_cb = ota_config -> ota_cb ;
332428 } else {
@@ -342,6 +438,7 @@ esp_err_t esp_rmaker_ota_enable(esp_rmaker_ota_config_t *ota_config, esp_rmaker_
342438 err = esp_rmaker_ota_enable_using_topics (ota );
343439 }
344440 if (err == ESP_OK ) {
441+ esp_rmaker_ota_manage_rollback (ota_config , ota );
345442 ota_init_done = true;
346443 } else {
347444 free (ota );
0 commit comments