diff --git a/main/application.cc b/main/application.cc index 90c481a..5b77a42 100644 --- a/main/application.cc +++ b/main/application.cc @@ -83,6 +83,8 @@ static const char* const STATE_STRINGS[] = { Application::Application() { event_group_ = xEventGroupCreate(); // 吧唧模式不需要后台任务(节省32KB栈内存) + // ⚠️ 不能在配网模式下置 nullptr: OnAudioOutput 无判空, 直接调 + // background_task_->Schedule() 会 std::mutex::lock 异常 → abort reboot loop #ifdef CONFIG_BAJI_BADGE_MODE if (!device_mode_is_badge()) { background_task_ = new BackgroundTask(4096 * 8); diff --git a/main/boards/common/wifi_board.cc b/main/boards/common/wifi_board.cc index 36b2422..85abb32 100644 --- a/main/boards/common/wifi_board.cc +++ b/main/boards/common/wifi_board.cc @@ -424,10 +424,10 @@ bool WifiBoard::StartBleProvisioning() { // 播放配网提示音 auto& application = Application::GetInstance(); if(strcmp(CONFIG_DEVICE_ROLE, "KAKA") == 0){ - application.Alert("BLE配网模式", "请使用手机APP搜索Airhub_开头的蓝牙设备", "happy", Lang::Sounds::P3_KAKA_WIFICONFIG); + application.Alert("BLE配网模式", "请使用手机APP搜索设备连接WI-FI", "happy", Lang::Sounds::P3_KAKA_WIFICONFIG); } else if(strcmp(CONFIG_DEVICE_ROLE, "RTC_Test") == 0){ - application.Alert("BLE配网模式", "请使用手机APP搜索Airhub_开头的蓝牙设备", "happy", Lang::Sounds::P3_LALA_WIFICONFIG); + application.Alert("BLE配网模式", "请使用手机APP搜索设备连接WI-FI", "happy", Lang::Sounds::P3_LALA_WIFICONFIG); } @@ -468,9 +468,9 @@ bool WifiBoard::StartBleProvisioning() { // } // auto& application = Application::GetInstance(); // if (strcmp(CONFIG_DEVICE_ROLE, "KAKA") == 0) { -// application.Alert("BLE配网模式", "请使用手机APP搜索Airhub_开头的蓝牙设备", "", Lang::Sounds::P3_KAKA_WIFICONFIG); +// application.Alert("BLE配网模式", "请使用手机APP搜索设备连接WI-FI", "", Lang::Sounds::P3_KAKA_WIFICONFIG); // } else if (strcmp(CONFIG_DEVICE_ROLE, "RTC_Test") == 0) { -// application.Alert("BLE配网模式", "请使用手机APP搜索Airhub_开头的蓝牙设备", "", Lang::Sounds::P3_LALA_WIFICONFIG); +// application.Alert("BLE配网模式", "请使用手机APP搜索设备连接WI-FI", "", Lang::Sounds::P3_LALA_WIFICONFIG); // } // while (true) { // int free_sram = heap_caps_get_free_size(MALLOC_CAP_INTERNAL); diff --git a/main/boards/movecall-moji-esp32s3/movecall_moji_esp32s3.cc b/main/boards/movecall-moji-esp32s3/movecall_moji_esp32s3.cc index bad7a1b..0196a7f 100644 --- a/main/boards/movecall-moji-esp32s3/movecall_moji_esp32s3.cc +++ b/main/boards/movecall-moji-esp32s3/movecall_moji_esp32s3.cc @@ -16,9 +16,14 @@ #include "settings.h" #include "dzbj/dzbj_init.h" // dzbj 显示模块初始化(公共: dzbj_hw_display_init;吧唧专用 dzbj_display_init 在头内 #ifdef 包裹) #include "display/ai_chat_display.h" // AI 对话模式专用显示 +#include "dzbj/fatfs.h" // SPIFFS DecodeImg (配网模式背景图也用) +#include "esp_spiffs.h" // 配网模式 SPIFFS 挂载 +#include "esp_lcd_panel_ops.h" // esp_lcd_panel_draw_bitmap (配网模式直接画 bitmap) +// ⚠️ 不 include "dzbj/lcd.h" 因为它会 include esp_lvgl_port.h → 触发 lv_font_t 跟 display.h 冲突 +// panel_handle 是 lcd.c 暴露的全局 C 变量, 直接 extern 声明: +extern "C" esp_lcd_panel_handle_t panel_handle; #ifdef CONFIG_BAJI_BADGE_MODE #include "dzbj/device_mode.h" // 设备模式管理(AI/吧唧) -#include "dzbj/fatfs.h" // SPIFFS 文件系统 #include "dzbj/dzbj_ble.h" // BLE 图传服务 #include "dzbj/dzbj_battery.h" // 电池监测 #include "dzbj/dzbj_button.h" // 按键驱动 @@ -54,6 +59,8 @@ extern "C" void init_spiffs_image_list(void); // AI 对话屏幕初始化(纯 C,避免 lv_font_t 冲突) extern "C" void ai_chat_screen_init(void); extern "C" void ai_chat_resume_animation(void); +// 配网模式专用最小化初始化 (只显示文字, 不加载 EAF 资源 / 不创建 anim) +extern "C" void ai_chat_screen_init_provisioning(const char* hint_text); // 背光初始化(pages_pwm.h 包含 LVGL 头文件,不能直接 include) extern "C" void pwm_init(void); @@ -248,8 +255,21 @@ public: // ===== AI 对话模式 ===== // 仅硬件+LVGL 初始化(不加载 SquareLine UI) dzbj_hw_display_init(codec_i2c_bus_); - // 加载 AI 对话专用屏幕 - ai_chat_screen_init(); + // 配网模式跳过 EAF 数字人初始化, 节省 ~30 KB DRAM + 4.32 MB PSRAM, + // 让 BLE Bluedroid stack 有足够 DRAM 完成 advertising data malloc. + // 配网期间用户操作只需要听音效 + 看简单提示, 不需要数字人界面. + if (WifiBoard::NeedsProvisioning()) { + // 配网模式: EAF 最小化初始化 (仅显示文字, 不加载 8 张 EAF 资源 + 不创建 anim) + // 省 4.32 MB PSRAM (EAF 资源) + 数字人 anim 的 DRAM 占用 + // 保留 ~30 KB DRAM 给 gfx flush buffer (启动时预分配, 不抢 BLE 初始化) + // 显示"请使用APP\n蓝牙配网~"提示 + // 跟 adaptation_dzbjImg_shar (用 LVGL 显示 GIF + 配网) 同思路, 用 EAF 替代 LVGL + ESP_LOGI(TAG, "🔵 配网模式: EAF 最小化初始化, 显示配网提示文字"); + ai_chat_screen_init_provisioning("请使用APP\n蓝牙配网~"); + } else { + // 加载 AI 对话专用屏幕 + ai_chat_screen_init(); + } vTaskDelay(pdMS_TO_TICKS(100)); // 等待首帧渲染 pwm_init(); // 点亮背光 ESP_LOGI(TAG, "🤖 AI对话模式启动"); diff --git a/main/dzbj/ai_chat_ui.h b/main/dzbj/ai_chat_ui.h index 6a1eba8..0fdd30a 100644 --- a/main/dzbj/ai_chat_ui.h +++ b/main/dzbj/ai_chat_ui.h @@ -8,6 +8,13 @@ extern "C" { // 创建并加载 AI 对话屏幕 void ai_chat_screen_init(void); +// 配网模式专用 - 最小化 EAF 初始化, 只显示文字提示 +// 不加载 8 张 EAF 资源 (省 4.32 MB PSRAM) +// 不创建数字人 anim (省 DRAM) +// 不加载背景图 (省 DRAM + SPI buffer) +// 只启用 gfx renderer + label, 显示静态文字 +void ai_chat_screen_init_provisioning(const char* hint_text); + // 更新状态文本(如 "Listening...", "Speaking...") void ai_chat_set_status(const char* status); diff --git a/main/dzbj/ai_chat_ui_eaf.c b/main/dzbj/ai_chat_ui_eaf.c index 9ce5fb4..542a609 100644 --- a/main/dzbj/ai_chat_ui_eaf.c +++ b/main/dzbj/ai_chat_ui_eaf.c @@ -541,3 +541,92 @@ void ai_chat_resume_animation(void) { // EAF 动画由 gfx_anim_start 持续播放,无需手动 resume ESP_LOGD(TAG, "resume_animation(EAF 模式下自动循环,无需操作)"); } + +// ========================================================== +// 配网模式专用 - 最小化 EAF 初始化, 只显示文字提示 +// 设计目的: 配网模式下 DRAM 紧张 (RTC SDK + 应用 .bss 占 30-50KB), +// 不能完整初始化 EAF (加载 4.32 MB EAF 资源 + 数字人 anim). +// 只启用 gfx renderer + 单个 label, 在启动早期预分配 flush buffer, +// 避免跟后续 BLE Bluedroid 初始化抢动态分配的 DRAM. +// 跟 adaptation_dzbjImg_shar (用 LVGL 显示 GIF) 同思路, 用 EAF 替代 LVGL 省 .bss +// ========================================================== +void ai_chat_screen_init_provisioning(const char* hint_text) { + if (s_initialized) { + ESP_LOGW(TAG, "[配网] EAF 已初始化, 跳过"); + return; + } + ESP_LOGI(TAG, "============================"); + ESP_LOGI(TAG, "=== EAF 最小化 (配网模式) ==="); + ESP_LOGI(TAG, "============================"); + + // 1. 初始化 gfx 核心 (Core 0, 跟原版一致, 不抢音频 Core 1) + gfx_core_config_t gfx_cfg = { + .fps = 25, + .task = GFX_EMOTE_INIT_CONFIG(), + }; + gfx_cfg.task.task_priority = 4; + gfx_cfg.task.task_affinity = 0; + gfx_cfg.task.task_stack = 8 * 1024; + s_emote_handle = gfx_emote_init(&gfx_cfg); + if (!s_emote_handle) { + ESP_LOGE(TAG, "[配网] gfx_emote_init 失败"); + return; + } + + // 2. 添加 display (接管 panel_handle, 启动时预分配 flush buffer ~30KB DRAM) + gfx_disp_config_t disp_cfg = { + .h_res = LCD_W, + .v_res = LCD_H, + .flush_cb = eaf_disp_flush_cb, + .update_cb = NULL, + .user_data = (void *)panel_handle, + .flags = { + .swap = true, + .buff_dma = true, + .buff_spiram = false, + .double_buffer = true, + }, + .buffers = { .buf1 = NULL, .buf2 = NULL, .buf_pixels = LCD_W * 20 }, + }; + s_disp = gfx_disp_add(s_emote_handle, &disp_cfg); + if (!s_disp) { + ESP_LOGE(TAG, "[配网] gfx_disp_add 失败"); + gfx_emote_deinit(s_emote_handle); + s_emote_handle = NULL; + return; + } + + const esp_lcd_panel_io_callbacks_t cbs = { .on_color_trans_done = eaf_flush_io_ready }; + esp_lcd_panel_io_register_event_callbacks(lcd_io_handle, &cbs, s_disp); + + // 3. 设置背景色 = 黑色 + gfx_disp_set_bg_color(s_disp, GFX_COLOR_HEX(0x000000)); + + s_initialized = true; + + // 4. 创建文字 label - 不加载 EAF 资源 + 不创建 anim + // 上下/左右居中: label height 恰好包文字 (避免 gfx_label 内部顶对齐导致视觉偏上), + // 配合 GFX_ALIGN_CENTER 整体居中到屏幕中央 + gfx_emote_lock(s_emote_handle); + s_chat_label = gfx_label_create(s_disp); + if (s_chat_label) { + gfx_label_set_font(s_chat_label, (gfx_font_t)&font_puhui_20_4); + // 字体 20px + line_spacing 8 = 28px/行, 2 行文字 = 56px, 留 8px 余白 = 64px + gfx_obj_set_size(s_chat_label, 300, 64); + gfx_label_set_long_mode(s_chat_label, GFX_LABEL_LONG_WRAP); + gfx_label_set_text_align(s_chat_label, GFX_TEXT_ALIGN_CENTER); // 文字水平居中 + gfx_label_set_color(s_chat_label, GFX_COLOR_HEX(0xFFFFFF)); // 白字 (黑底背景) + gfx_label_set_bg_enable(s_chat_label, false); + gfx_label_set_line_spacing(s_chat_label, 8); + // label 整体居中到屏幕正中 → 文字视觉上上下左右居中 + gfx_obj_align(s_chat_label, GFX_ALIGN_CENTER, 0, 0); + gfx_label_set_text(s_chat_label, hint_text ? hint_text : "请使用APP\n蓝牙配网~"); + gfx_obj_set_visible(s_chat_label, true); + ESP_LOGI(TAG, "[配网] 文字 label 创建成功 (居中显示)"); + } else { + ESP_LOGE(TAG, "[配网] gfx_label_create 失败"); + } + gfx_emote_unlock(s_emote_handle); + + ESP_LOGI(TAG, "=== EAF 最小化初始化完成 ==="); +}