/* * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "app_composition.h" #include #include "control_plane.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "sdkconfig.h" #ifndef CONFIG_TQ_BOOT_SEQUENCE_DRY_RUN #define CONFIG_TQ_BOOT_SEQUENCE_DRY_RUN 0 #endif #ifndef CONFIG_TQ_BOOT_SOUND_TIMEOUT_MS #define CONFIG_TQ_BOOT_SOUND_TIMEOUT_MS 3000 #endif static const char *TAG = "app_comp_boot"; typedef enum { APP_COMPOSITION_BOOT_STAGE_POWER_HOLD = 1, APP_COMPOSITION_BOOT_STAGE_STORAGE_PREPARE, APP_COMPOSITION_BOOT_STAGE_AUDIO_INIT, APP_COMPOSITION_BOOT_STAGE_BOOT_SOUND, APP_COMPOSITION_BOOT_STAGE_AUDIO_READY, APP_COMPOSITION_BOOT_STAGE_LCD_DEVICE, APP_COMPOSITION_BOOT_STAGE_LCD_RENDER_LOOP, APP_COMPOSITION_BOOT_STAGE_PLATFORM_DOMAIN, APP_COMPOSITION_BOOT_STAGE_TURNTABLE_CALIBRATION, APP_COMPOSITION_BOOT_STAGE_WIFI_BOOTSTRAP, APP_COMPOSITION_BOOT_STAGE_RUNTIME_WORKERS, APP_COMPOSITION_BOOT_STAGE_NETWORK_SERVICES, } app_composition_boot_stage_t; static void app_composition_log_stage(app_composition_boot_stage_t stage, const char *name, const char *event) { ESP_LOGI(TAG, "boot stage %02d %-24s event=%s", (int)stage, name, event); } static void app_composition_log_stage_result(app_composition_boot_stage_t stage, const char *name, esp_err_t result) { ESP_LOGI(TAG, "boot stage %02d %-24s result=%s", (int)stage, name, esp_err_to_name(result)); } static void app_composition_log_stage_skipped(app_composition_boot_stage_t stage, const char *name, const char *reason) { ESP_LOGI(TAG, "boot stage %02d %-24s skipped=%s", (int)stage, name, reason); } static esp_err_t app_composition_run_plan_a_boot_sequence(void) { app_composition_log_stage(APP_COMPOSITION_BOOT_STAGE_POWER_HOLD, "power_hold", "configured_or_disabled_by_platform_core"); app_composition_log_stage(APP_COMPOSITION_BOOT_STAGE_STORAGE_PREPARE, "storage_prepare", "control_plane_prepare_runtime_storage"); esp_err_t err = control_plane_prepare_runtime_storage(); if (err != ESP_OK) { ESP_LOGE(TAG, "Runtime storage prepare failed: %s", esp_err_to_name(err)); return err; } esp_err_t battery_guard_err = control_plane_battery_apply_boot_guard(); if (battery_guard_err != ESP_OK && battery_guard_err != ESP_ERR_NOT_SUPPORTED) { ESP_LOGW(TAG, "Battery boot guard failed, continue boot: %s", esp_err_to_name(battery_guard_err)); } app_composition_log_stage(APP_COMPOSITION_BOOT_STAGE_AUDIO_INIT, "audio_init", "boot_sound_policy_begin"); app_composition_log_stage(APP_COMPOSITION_BOOT_STAGE_BOOT_SOUND, "boot_sound", "play_boot_sound_with_timeout"); esp_err_t boot_sound_err = control_plane_play_boot_sound(CONFIG_TQ_BOOT_SOUND_TIMEOUT_MS); if (boot_sound_err != ESP_OK) { ESP_LOGW(TAG, "Boot sound unavailable, continue boot: %s", esp_err_to_name(boot_sound_err)); } app_composition_log_stage(APP_COMPOSITION_BOOT_STAGE_AUDIO_READY, "audio_ready", "boot_sound_policy_end_keep_runtime"); #if CONFIG_TQ_SCREEN_ENABLE app_composition_log_stage(APP_COMPOSITION_BOOT_STAGE_LCD_DEVICE, "lcd_device", "control_plane_init_lcd_device"); esp_err_t lcd_err = control_plane_init_lcd_device(); app_composition_log_stage_result(APP_COMPOSITION_BOOT_STAGE_LCD_DEVICE, "lcd_device", lcd_err); bool lcd_ready = (lcd_err == ESP_OK); if (lcd_ready) { app_composition_log_stage(APP_COMPOSITION_BOOT_STAGE_LCD_RENDER_LOOP, "lcd_render_loop", "control_plane_start_lcd_render_worker"); esp_err_t lcd_worker_err = control_plane_start_lcd_render_worker(); app_composition_log_stage_result(APP_COMPOSITION_BOOT_STAGE_LCD_RENDER_LOOP, "lcd_render_loop", lcd_worker_err); if (lcd_worker_err != ESP_OK) { lcd_ready = false; } } else { app_composition_log_stage_skipped(APP_COMPOSITION_BOOT_STAGE_LCD_RENDER_LOOP, "lcd_render_loop", "lcd_device_unavailable"); } #else app_composition_log_stage_skipped(APP_COMPOSITION_BOOT_STAGE_LCD_DEVICE, "lcd_device", "disabled_by_config"); app_composition_log_stage_skipped(APP_COMPOSITION_BOOT_STAGE_LCD_RENDER_LOOP, "lcd_render_loop", "disabled_by_config"); #endif app_composition_log_stage(APP_COMPOSITION_BOOT_STAGE_PLATFORM_DOMAIN, "platform_domain", "control_plane_init"); err = control_plane_init(); if (err != ESP_OK) { ESP_LOGE(TAG, "control_plane_init failed: %s", esp_err_to_name(err)); return err; } esp_err_t battery_err = control_plane_battery_service_start(); if (battery_err != ESP_OK && battery_err != ESP_ERR_NOT_SUPPORTED) { ESP_LOGW(TAG, "Early battery service start failed, continue boot: %s", esp_err_to_name(battery_err)); } #if CONFIG_TQ_FACTORY_CLI_ENABLE err = control_plane_factory_cli_start(); if (err != ESP_OK) { ESP_LOGE(TAG, "Factory CLI start failed: %s", esp_err_to_name(err)); return err; } #endif #if CONFIG_TQ_SCREEN_ENABLE if (lcd_ready) { esp_err_t expression_err = control_plane_request_boot_expression(); if (expression_err != ESP_OK) { ESP_LOGW(TAG, "Boot LCD expression request failed: %s", esp_err_to_name(expression_err)); } } #endif #if CONFIG_TQ_BOOT_TURNTABLE_CALIBRATION_ENABLE app_composition_log_stage(APP_COMPOSITION_BOOT_STAGE_TURNTABLE_CALIBRATION, "turntable_calibration", "control_plane_calibrate"); esp_err_t calibration_err = control_plane_calibrate(); if (calibration_err != ESP_OK) { ESP_LOGW(TAG, "Boot turntable calibration failed, continue degraded: %s", esp_err_to_name(calibration_err)); } #else app_composition_log_stage_skipped(APP_COMPOSITION_BOOT_STAGE_TURNTABLE_CALIBRATION, "turntable_calibration", "disabled_by_config"); #endif app_composition_log_stage(APP_COMPOSITION_BOOT_STAGE_WIFI_BOOTSTRAP, "wifi_bootstrap", "control_plane_bootstrap_connectivity"); esp_err_t wifi_err = control_plane_bootstrap_connectivity(CONFIG_MULTIMODAL_WIFI_SSID, CONFIG_MULTIMODAL_WIFI_PASSWORD, CONFIG_MULTIMODAL_CONNECT_TIMEOUT_MS); if (wifi_err != ESP_OK) { ESP_LOGW(TAG, "Connectivity bootstrap failed: %s", esp_err_to_name(wifi_err)); } bool provisioning_active = control_plane_is_mini_program_provisioning_running(); app_composition_log_stage(APP_COMPOSITION_BOOT_STAGE_RUNTIME_WORKERS, "runtime_workers", provisioning_active ? "deferred_until_ble_provisioning_completes" : "control_plane_start_runtime_workers"); if (provisioning_active) { ESP_LOGI(TAG, "Full runtime workers deferred while BLE provisioning is active"); } else { err = control_plane_start_runtime_workers(); if (err != ESP_OK) { ESP_LOGE(TAG, "Runtime worker start failed: %s", esp_err_to_name(err)); return err; } } app_composition_log_stage(APP_COMPOSITION_BOOT_STAGE_NETWORK_SERVICES, "network_services", provisioning_active ? "deferred_with_runtime_workers" : "started_by_runtime_workers_after_readiness"); return ESP_OK; } esp_err_t app_composition_start_default_runtime(void) { setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); ESP_LOGI(TAG, "========================================"); ESP_LOGI(TAG, " App Composition Boot Plan A"); ESP_LOGI(TAG, " dry_run=%d", CONFIG_TQ_BOOT_SEQUENCE_DRY_RUN); ESP_LOGI(TAG, "========================================"); #if CONFIG_TQ_BOOT_SEQUENCE_DRY_RUN ESP_LOGE(TAG, "Plan A dry-run is enabled; disable CONFIG_TQ_BOOT_SEQUENCE_DRY_RUN"); return ESP_ERR_NOT_SUPPORTED; #else return app_composition_run_plan_a_boot_sequence(); #endif }