纳入固件源码、屏幕组件、nand_xfer 工具和文档。忽略本机构建产物、sdkconfig,以及含本机路径的依赖锁。 Co-authored-by: Cursor <[email protected]>
1120 lines
43 KiB
C
1120 lines
43 KiB
C
/*****************************************************include files **************************************************************************/
|
||
#include "LVGL/lvgl_init.h"
|
||
#include "FLASH/flash.h"
|
||
#include <inttypes.h>
|
||
#include "esp_heap_caps.h"
|
||
#include "event.h"
|
||
#include "freertos/timers.h"
|
||
#include "BLE/ble.h"
|
||
#include "BLE/ble_send.h"
|
||
#include "BLE/ble_data_ansy.h"
|
||
#include "nvs_flash.h"
|
||
#include "esp_timer.h"
|
||
#include "ui.h"
|
||
#include "EMOJI_STORAGE/emoji_storage.h"
|
||
#include "ZIP_EXTRACTOR/zip_extractor.h"
|
||
#include "events_init.h"
|
||
#include "WIFI/wifi.h"
|
||
#include "OTA/ota_upgrade.h"
|
||
#include "wifi_prov_ui.h"
|
||
#include "main.h"
|
||
#include "SCREEN/screen_brightness.h"
|
||
#include "SCREEN/screen_sleep.h"
|
||
#include "MOTOR/motor_control.h"
|
||
#include "MOTOR/foc.h"
|
||
#include <sys/stat.h>
|
||
#include <errno.h>
|
||
#include "SC7U22/SC7U22.h"
|
||
#include "soc/io_mux_reg.h"
|
||
#include "driver/pcnt.h"
|
||
#include "esp_netif_sntp.h"
|
||
#include "pet/pet_init.h"
|
||
#include "esp_flash.h"
|
||
|
||
#include "hal/gpio_hal.h"
|
||
#include "soc/gpio_reg.h" // 包含 GPIO 寄存器定义
|
||
#include "soc/gpio_struct.h" // 包含 GPIO 寄存器结构体
|
||
#include "NS4168/ns4168.h"
|
||
#include "NS4168/wav_demux.h"
|
||
// 假设要检查GPIO_NUM (例如:GPIO_NUM_12)
|
||
extern void play_wav2(float volume);
|
||
/**********************************************************task stack *********************************************************************/
|
||
|
||
StaticTask_t lvgl_task_tcb; // 静态内存分配的freeRTOS 任务结构,是高级的任务创建方式
|
||
StackType_t *lvgl_task_stack = NULL; // 栈单元类型 指向任务栈内存的指针
|
||
|
||
StaticTask_t feed_task_tcb;
|
||
StackType_t *feed_task_stack = NULL;
|
||
|
||
StaticTask_t detect_task_tcb;
|
||
StackType_t *detect_task_stack = NULL;
|
||
|
||
StaticTask_t change_task_tcb;
|
||
StackType_t *change_task_stack = NULL;
|
||
|
||
StaticTask_t bt_task_tcb;
|
||
StackType_t *bt_task_stack = NULL;
|
||
|
||
StaticTask_t main_task_tcb;
|
||
StackType_t *main_task_stack = NULL;
|
||
TaskHandle_t main_task_handle = NULL;
|
||
|
||
StaticTask_t bmi_task_tcb;
|
||
StackType_t *bmi_task_stack = NULL;
|
||
|
||
StaticTask_t nst112_task_tcb;
|
||
StackType_t *nst112_task_stack = NULL;
|
||
|
||
StaticTask_t MS523_task_tcb;
|
||
StackType_t *MS523_task_stack = NULL;
|
||
|
||
StaticTask_t pet_task_tcb;
|
||
StackType_t *pet_task_stack = NULL;
|
||
|
||
StaticTask_t extract_task_tcb;
|
||
StackType_t *extract_task_stack = NULL;
|
||
|
||
StaticTask_t write_nvs_task_tcb;
|
||
StackType_t *write_nvs_task_stack = NULL;
|
||
/******************************************************* variable declaration ************************************************************************/
|
||
|
||
|
||
volatile bool g_system_init_done = false; //系统初始化完成标记
|
||
bool is_ui_mode = false; //UI_MODE 标记
|
||
|
||
/* ─── ota ─── */
|
||
static bool g_ota_pre_block_auto_sleep = false;
|
||
static bool g_ota_pre_state_saved = false;
|
||
static bool g_ota_lvgl_deinit_done = false;
|
||
|
||
/* ─── BLE 配网失败恢复(成功不恢复) ─── */
|
||
static bool g_wifi_prov_pre_state_saved = false;
|
||
static bool g_wifi_prov_lvgl_deinit_done = false;
|
||
static bool g_wifi_prov_pre_block_auto_sleep = false;
|
||
|
||
/* ─── 表情包升级:下载成功后的第一次切换“收尾”,不重启,不回到原来 ─── */
|
||
volatile bool g_emoji_download_in_progress = false;
|
||
|
||
|
||
|
||
uint8_t *canvas_fb = NULL;
|
||
|
||
char *lottie_file_path = "/extflash/0/default.json";
|
||
|
||
struct stat file_stat;
|
||
|
||
static esp_websocket_client_handle_t websocket_handle = NULL;
|
||
|
||
static char target_id1[17] = {0};
|
||
char filename_emoji[64] = {0};
|
||
static char client_id[13] = {0};
|
||
static uint64_t chip_id = 0;
|
||
|
||
const char *TAG_MAIN = "example";
|
||
void play_wav(void);
|
||
lv_ui guider_ui;
|
||
// 1. 声明设置亮度的驱动函数 (防止隐式声明警告)
|
||
|
||
/** OTA 下载任务已失败退出:在 WiFi 仍可能重连时,延后恢复 UI,避免早于重连完成 */
|
||
static volatile bool g_ota_recover_pending_after_fail = false;
|
||
/******************************************************* functions declaration ************************************************************************/
|
||
|
||
static void get_boot_address(void); //获取启动地址
|
||
static void show_all_nvs_entries(void); //nvs区域展示
|
||
void set_block_auto_sleep(bool block); //设置自动睡眠
|
||
static void print_memory_usage(const char *step_name);
|
||
/******************************************************* TOUCH ************************************************************************/
|
||
|
||
|
||
|
||
|
||
|
||
/******************************************************* 不清楚是啥 ************************************************************************/
|
||
|
||
void wifi_prov_capture_pre_state(void)
|
||
{
|
||
if (g_wifi_prov_pre_state_saved)
|
||
return;
|
||
g_wifi_prov_pre_block_auto_sleep = block_auto_sleep;
|
||
g_wifi_prov_pre_state_saved = true;
|
||
g_wifi_prov_lvgl_deinit_done = false;
|
||
ESP_LOGI(TAG_MAIN, "已保存 BLE 配网前状态: block_auto_sleep=%d", (int)g_wifi_prov_pre_block_auto_sleep);
|
||
}
|
||
|
||
void wifi_prov_mark_lvgl_deinited(void)
|
||
{
|
||
g_wifi_prov_lvgl_deinit_done = true;
|
||
}
|
||
|
||
static void wifi_prov_restore_pre_state(const char *reason)
|
||
{
|
||
if (g_wifi_prov_pre_state_saved)
|
||
{
|
||
set_block_auto_sleep(g_wifi_prov_pre_block_auto_sleep);
|
||
g_wifi_prov_pre_state_saved = false;
|
||
}
|
||
if (g_wifi_prov_lvgl_deinit_done)
|
||
{
|
||
ESP_LOGW(TAG_MAIN, "BLE 配网失败恢复:重建显示/UI (%s)", reason ? reason : "unknown");
|
||
(void)app_lcd_init();
|
||
(void)app_lvgl_init();
|
||
(void)app_change_init();
|
||
g_wifi_prov_lvgl_deinit_done = false;
|
||
}
|
||
back2default = true;
|
||
ESP_LOGW(TAG_MAIN, "BLE 配网失败:已恢复配网前状态: %s", reason ? reason : "unknown");
|
||
}
|
||
|
||
/******************************************************* 未确定 ************************************************************************/
|
||
|
||
static void ota_capture_pre_command_state(void)
|
||
{
|
||
if (g_ota_pre_state_saved)
|
||
return;
|
||
g_ota_pre_block_auto_sleep = block_auto_sleep;
|
||
g_ota_pre_state_saved = true;
|
||
g_ota_lvgl_deinit_done = false; // wait until OTA start actually de-inits LVGL
|
||
ESP_LOGI(TAG_MAIN, "已保存 OTA 前状态: block_auto_sleep=%d", (int)g_ota_pre_block_auto_sleep);
|
||
}
|
||
|
||
static void ota_restore_pre_command_state(const char *reason)
|
||
{
|
||
if (g_ota_pre_state_saved)
|
||
{
|
||
set_block_auto_sleep(g_ota_pre_block_auto_sleep);
|
||
g_ota_pre_state_saved = false;
|
||
}
|
||
|
||
/* OTA 退出(成功/失败/取消)后:重建 LCD/LVGL/UI,避免黑屏 */
|
||
if (g_ota_lvgl_deinit_done)
|
||
{
|
||
ESP_LOGW(TAG_MAIN, "OTA 恢复:重建显示/UI (%s)", reason ? reason : "unknown");
|
||
(void)app_lcd_init();
|
||
(void)app_lvgl_init();
|
||
(void)app_change_init();
|
||
g_ota_lvgl_deinit_done = false;
|
||
}
|
||
|
||
/* OTA 恢复后强制确保“屏已开 + 亮度非 0”。
|
||
* - 若 OTA 前/过程中进入了休眠并关屏,这里必须唤醒,否则 UI 虽重建但仍黑屏。
|
||
* - 即使未处于休眠态,也补写一次硬件亮度,避免停留在 init 阶段的 0。 */
|
||
toggle_sleep_mode(false); /* 非休眠态会直接 return,无副作用 */
|
||
if (panel_handle)
|
||
{
|
||
panel_qspi_amoled_set_brightness(panel_handle, g_current_brightness);
|
||
}
|
||
|
||
/* OTA 恢复后回到默认表情页,确保屏幕可见且状态可预期 */
|
||
back2default = true;
|
||
ESP_LOGW(TAG_MAIN, "已恢复 OTA 前状态: %s", reason ? reason : "unknown");
|
||
}
|
||
|
||
/******************************************************* 未确定 ************************************************************************/
|
||
|
||
static void ota_try_pending_recover_after_fail(void)
|
||
{
|
||
if (!g_ota_recover_pending_after_fail)
|
||
{
|
||
return;
|
||
}
|
||
if (wifi_is_connected_now())
|
||
{
|
||
g_ota_recover_pending_after_fail = false;
|
||
ota_restore_pre_command_state("OTA failed, WiFi back online");
|
||
return;
|
||
}
|
||
if (wifi_sta_reconnect_exhausted())
|
||
{
|
||
g_ota_recover_pending_after_fail = false;
|
||
ota_restore_pre_command_state("OTA failed, WiFi reconnect exhausted");
|
||
return;
|
||
}
|
||
ESP_LOGW(TAG_MAIN, "OTA 失败恢复暂缓:WiFi 仍在自动重连中");
|
||
}
|
||
|
||
/** 供 ota_upgrade 任务在失败清理后调用:先排队,由 main 在 WiFi 稳定或重试用尽后再恢复 */
|
||
void ota_main_notify_recover_pending_after_fail(void)
|
||
{
|
||
g_ota_recover_pending_after_fail = true;
|
||
set_system_event(OTA_FAIL_RECOVER_PENDING_BIT);
|
||
}
|
||
|
||
void notify_wifi_provision_signal_received(void)
|
||
{
|
||
/* WiFi 配网守卫功能已停用:保留接口兼容调用方 */
|
||
ESP_LOGI(TAG_MAIN, "已收到WiFi配网信号(守卫功能已停用)");
|
||
}
|
||
|
||
/******************************************************* main task ************************************************************************/
|
||
|
||
static void main_task(void *arg) // 主任务
|
||
{
|
||
ESP_LOGI(TAG_MAIN, "[DEBUG] main_task 已启动,等待队列事件 (queue=%p)", (void *)system_event_queue);
|
||
|
||
uint32_t event_bits;
|
||
int heartbeat_counter = 0;
|
||
|
||
while (1)
|
||
{
|
||
//UBaseType_t uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL);
|
||
//ESP_LOGW(TAG_MAIN, "main_task stack high water mark: %u bytes", (unsigned)uxHighWaterMark * sizeof(StackType_t));
|
||
/* 阻塞等待队列事件,BLE 回调通过 xQueueSend 投递,避免回调上下文限制 */
|
||
BaseType_t ret = xQueueReceive(system_event_queue, &event_bits, pdMS_TO_TICKS(5000));
|
||
if (ret != pdPASS)
|
||
{
|
||
heartbeat_counter++;
|
||
if (heartbeat_counter % 6 == 0)
|
||
{
|
||
ESP_LOGI(TAG_MAIN, "[HEARTBEAT] main_task alive, idle=%ds, queue_pending=%d, stack_hwm=%u",
|
||
heartbeat_counter * 5,
|
||
(int)uxQueueMessagesWaiting(system_event_queue),
|
||
(unsigned)uxTaskGetStackHighWaterMark(NULL));
|
||
}
|
||
continue;
|
||
}
|
||
heartbeat_counter = 0;
|
||
ESP_LOGI(TAG_MAIN, "[DEBUG] main_task 收到事件 bits=0x%lX, queue_pending=%d",
|
||
(unsigned long)event_bits, (int)uxQueueMessagesWaiting(system_event_queue));
|
||
|
||
if (event_bits & BLE_CONNECTED_BIT) // BLE 连接成功
|
||
{
|
||
ESP_LOGI(TAG_MAIN, "处理BLE连接事件");
|
||
// 不再自动发送系统信息,改为接收到 get_system_info 请求后再返回
|
||
// 发送正在使用的表情包系列(可选,如不需要也可移除)
|
||
// xTaskNotify(notification_task_handle, BLE_SEND_EMOJI_USING, eSetBits);
|
||
}
|
||
if (event_bits & BLE_DISCONNECTED_BIT) // BLE 断开连接
|
||
{
|
||
ESP_LOGI(TAG_MAIN, "处理BLE断开事件");
|
||
if (ota_upgrade_is_running())
|
||
{
|
||
if (!wifi_is_connected_now())
|
||
{
|
||
if (wifi_sta_still_reconnecting())
|
||
{
|
||
ESP_LOGW(TAG_MAIN, "OTA中 BLE断开且WiFi正在重连,暂不恢复,等待重连结果");
|
||
}
|
||
else
|
||
{
|
||
ESP_LOGW(TAG_MAIN, "OTA中 BLE断开且WiFi已不可用(重连已用尽),准备恢复到OTA前状态");
|
||
set_system_event(OTA_RECOVER_REQUIRED_BIT);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ESP_LOGI(TAG_MAIN, "OTA中 BLE断开但WiFi在线,继续当前OTA流程");
|
||
}
|
||
}
|
||
}
|
||
if (event_bits & WIFI_CONNECTED_BIT) // WiFi 连接成功
|
||
{
|
||
ESP_LOGI(TAG_MAIN, "处理WiFi连接事件");
|
||
// on_wifi_connected();
|
||
ota_try_pending_recover_after_fail();
|
||
}
|
||
if (event_bits & WIFI_DISCONNECTED_BIT) // WiFi 断开连接
|
||
{
|
||
ESP_LOGI(TAG_MAIN, "处理WiFi断开事件");
|
||
if (ota_upgrade_is_running())
|
||
{
|
||
ESP_LOGW(TAG_MAIN, "OTA中 WiFi断开,先走重连流程,等待重连失败再恢复状态");
|
||
}
|
||
ota_try_pending_recover_after_fail();
|
||
}
|
||
if (event_bits & WEBSOCKET_CONNECTED_BIT) // websocket 连接成功
|
||
{
|
||
ESP_LOGI(TAG_MAIN, "处理WebSocket连接事件");
|
||
}
|
||
if (event_bits & WEBSOCKET_DISCONNECTED_BIT) // websocket 断开连接
|
||
{
|
||
ESP_LOGI(TAG_MAIN, "处理WebSocketd断开事件");
|
||
}
|
||
if (event_bits & WEBSOCKET_DATA_BIT) // 接收到 websocket 数据
|
||
{
|
||
ESP_LOGI(TAG_MAIN, "处理WebSocket数据事件");
|
||
}
|
||
if (event_bits & IMU_DATA_READY_BIT) // 读取IMU 的数据
|
||
{
|
||
ESP_LOGI(TAG_MAIN, "处理传感器事件");
|
||
}
|
||
if (event_bits & USER_INPUT_BIT) // 用户输入事件
|
||
{
|
||
ESP_LOGI(TAG_MAIN, "处理用户输入事件");
|
||
}
|
||
if (event_bits & SYSTEM_ERROR_BIT) // 处理系统错误
|
||
{
|
||
ESP_LOGI(TAG_MAIN, "处理系统错误事件");
|
||
}
|
||
if (event_bits & OTA_UPDATE_STARTED_BIT) // OTA 更新开始
|
||
{
|
||
ESP_LOGI(TAG_MAIN, "[DEBUG] 进入 OTA 流程,准备 mqtt_main");
|
||
ESP_LOGI("main_task", "ota start!");
|
||
ota_capture_pre_command_state();
|
||
/* OTA 开始前:释放内部 RAM(LVGL/显示资源),降低 TLS/MQTT 的内部堆失败概率 */
|
||
lvgl_deinit_for_wifi();
|
||
g_ota_lvgl_deinit_done = true;
|
||
set_block_auto_sleep(true);
|
||
if (!Get_MQTT_FLAG())
|
||
mqtt_main();
|
||
|
||
/* 等待 MQTT 连接,带超时(60s),避免无限阻塞导致动画/触屏卡死 */
|
||
const TickType_t mqtt_wait_timeout = pdMS_TO_TICKS(60000);
|
||
TickType_t wait_start = xTaskGetTickCount();
|
||
while (!Get_MQTT_FLAG())
|
||
{
|
||
vTaskDelay(pdMS_TO_TICKS(500));
|
||
if ((xTaskGetTickCount() - wait_start) >= mqtt_wait_timeout)
|
||
{
|
||
ESP_LOGW(TAG_MAIN, "等待MQTT超时,退出OTA流程,恢复主循环");
|
||
break;
|
||
}
|
||
ESP_LOGI("MAIN", "等待MQTT连接...");
|
||
}
|
||
|
||
bool ota_subscribe_ok = false;
|
||
if (Get_MQTT_FLAG())
|
||
{
|
||
ESP_LOGI(TAG_MAIN, "订阅OTA更新主题");
|
||
if (subscribe_ota() == ESP_OK)
|
||
{
|
||
ESP_LOGI(TAG_MAIN, "成功订阅OTA更新主题");
|
||
ota_subscribe_ok = true;
|
||
}
|
||
}
|
||
|
||
/* MQTT 连接/发布失败时:必须把 LVGL/UI 恢复回来,否则会黑屏 */
|
||
if (!ota_subscribe_ok)
|
||
{
|
||
ota_restore_pre_command_state("OTA not started (MQTT/subscribe failed)");
|
||
}
|
||
clear_system_event(OTA_UPDATE_STARTED_BIT);
|
||
}
|
||
if (event_bits & OTA_UPDATE_COMPLETED_BIT) // OTA 更新成功
|
||
{
|
||
/* 成功结果已由 ota_task 经 BLE 发 1.11_A;重启由 ota_task 内 esp_restart() 执行,
|
||
* 此处不恢复 LVGL/UI(避免无意义重建后马上重启) */
|
||
ESP_LOGI(TAG_MAIN, "处理OTA更新完成事件(等待 ota_task 重启,不恢复 UI)");
|
||
clear_system_event(OTA_UPDATE_COMPLETED_BIT);
|
||
}
|
||
if (event_bits & OTA_FAIL_RECOVER_PENDING_BIT)
|
||
{
|
||
clear_system_event(OTA_FAIL_RECOVER_PENDING_BIT);
|
||
ota_try_pending_recover_after_fail();
|
||
}
|
||
if (event_bits & OTA_RECOVER_REQUIRED_BIT)
|
||
{
|
||
ESP_LOGW(TAG_MAIN, "处理 OTA 恢复事件:取消当前OTA并恢复到命令前状态");
|
||
if (ota_upgrade_is_running())
|
||
{
|
||
esp_err_t cancel_ret = ota_upgrade_cancel();
|
||
ESP_LOGW(TAG_MAIN, "ota_upgrade_cancel 返回: %s", esp_err_to_name(cancel_ret));
|
||
}
|
||
ota_restore_pre_command_state("OTA recover required");
|
||
clear_system_event(OTA_RECOVER_REQUIRED_BIT);
|
||
}
|
||
if (event_bits & WIFI_BLE_PROV_FAIL_RECOVER_UI_BIT)
|
||
{
|
||
ESP_LOGW(TAG_MAIN, "处理 BLE 配网失败恢复事件:释放 WiFi 后重建 UI");
|
||
wifi_prov_restore_pre_state("BLE WiFi provision failed");
|
||
clear_system_event(WIFI_BLE_PROV_FAIL_RECOVER_UI_BIT);
|
||
}
|
||
if (event_bits & EMOJI_UPGRADE_FINALIZE_BIT)
|
||
{
|
||
/* 下载成功后的第一次切换:收尾升级;若切到下载包则强制停在该包 default.json */
|
||
char target = g_emoji_upgrade_last_switch_target;
|
||
char downloaded = g_emoji_upgrade_downloaded_pack;
|
||
ESP_LOGW(TAG_MAIN, "[EMOJI_UPGRADE] finalize: downloaded='%c', target='%c'", downloaded, target);
|
||
if (downloaded != '0' && target == downloaded)
|
||
{
|
||
char path_buf[64];
|
||
snprintf(path_buf, sizeof(path_buf), "/extflash/%c/default.json", target);
|
||
ESP_LOGW(TAG_MAIN, "[EMOJI_UPGRADE] 强制停留到目标包默认页: %s", path_buf);
|
||
/* 若此前因配网/OTA 拆过 LVGL,先重建;否则 request_play_lottie_path 会失败并保持黑屏 */
|
||
if (panel_handle == NULL || change_task_handle == NULL)
|
||
{
|
||
ESP_LOGW(TAG_MAIN, "[EMOJI_UPGRADE] LVGL 未就绪(panel=%p, change_task=%p),先重建 UI",
|
||
(void *)panel_handle, (void *)change_task_handle);
|
||
/* 表情包流程已结束:全停网络栈,腾出内部 RAM/DMA 再建 LVGL(与 OTA 前拆 LVGL 对称) */
|
||
mqtt_client_shutdown();
|
||
vTaskDelay(pdMS_TO_TICKS(150));
|
||
wifi_network_shutdown_full();
|
||
vTaskDelay(pdMS_TO_TICKS(150));
|
||
(void)app_lcd_init();
|
||
(void)app_lvgl_init();
|
||
(void)app_change_init();
|
||
}
|
||
/* 即使路径相同也要重播,避免“同路径跳过通知” */
|
||
lvgl_clear_current_lottie_path();
|
||
request_play_lottie_path(path_buf);
|
||
}
|
||
g_emoji_upgrade_downloaded_pack = '0';
|
||
g_emoji_upgrade_last_switch_target = '0';
|
||
clear_system_event(EMOJI_UPGRADE_FINALIZE_BIT);
|
||
}
|
||
if (event_bits & DOWNLOAD_EMOJI_BIT) // 处理下载表情事件
|
||
{
|
||
char pack = ble_get_emoji_download_target_pack();
|
||
snprintf(filename_emoji, sizeof(filename_emoji), "icar/%c.zip", pack != '0' ? pack : 'A');
|
||
ESP_LOGI(TAG_MAIN, "处理下载表情事件: %s", filename_emoji);
|
||
if (!g_emoji_download_in_progress)
|
||
g_emoji_download_in_progress = true;
|
||
if (!Get_MQTT_FLAG())
|
||
mqtt_main();
|
||
TickType_t wait_start = xTaskGetTickCount();
|
||
const TickType_t mqtt_wait_timeout = pdMS_TO_TICKS(60000);
|
||
while (!Get_MQTT_FLAG())
|
||
{
|
||
vTaskDelay(pdMS_TO_TICKS(500));
|
||
if ((xTaskGetTickCount() - wait_start) >= mqtt_wait_timeout)
|
||
{
|
||
ESP_LOGW(TAG_MAIN, "等待MQTT超时,退出表情下载流程");
|
||
break;
|
||
}
|
||
ESP_LOGI("MAIN", "等待MQTT连接...");
|
||
}
|
||
if (Get_MQTT_FLAG())
|
||
publish_json_download(filename_emoji);
|
||
clear_system_event(DOWNLOAD_EMOJI_BIT);
|
||
}
|
||
if (event_bits & DOWNLOAD_SUCCESS_BIT) // 下载表情成功
|
||
{
|
||
ESP_LOGI(TAG_MAIN, "========== [EMOJI_DEBUG] DOWNLOAD_SUCCESS_BIT 收到 ==========");
|
||
xTaskNotify(notification_task_handle, BLE_SEND_DL_EMOJI_SUCCESS, eSetBits);
|
||
|
||
char pack_id = zip_extractor_get_last_downloaded_pack();
|
||
ESP_LOGI(TAG_MAIN, "[EMOJI_DEBUG] zip_extractor_get_last_downloaded_pack() 返回: '%c' (0x%02X)", pack_id, (unsigned char)pack_id);
|
||
|
||
if (pack_id != '0')
|
||
{
|
||
esp_err_t add_ret = emoji_storage_add_downloaded_pack(pack_id);
|
||
ESP_LOGI(TAG_MAIN, "[EMOJI_DEBUG] emoji_storage_add_downloaded_pack('%c') 返回: %s", pack_id, esp_err_to_name(add_ret));
|
||
}
|
||
else
|
||
{
|
||
ESP_LOGW(TAG_MAIN, "[EMOJI_DEBUG] pack_id == '0', 跳过表情替换");
|
||
}
|
||
g_emoji_upgrade_downloaded_pack = pack_id;
|
||
g_emoji_upgrade_pending_finalize = true;
|
||
ESP_LOGI(TAG_MAIN, "[EMOJI_UPGRADE] 下载成功: pack='%c',等待首次切换后收尾(不重启,不回原来)", pack_id);
|
||
/* 托管(A):若 BLE 已断开,则不等待车机下发切换指令,系统直接完成切换到下载包并收尾 */
|
||
if (!ble_is_connected() && pack_id != '0')
|
||
{
|
||
ESP_LOGW(TAG_MAIN, "[EMOJI_UPGRADE] BLE 未连接,触发托管切换到 pack='%c'", pack_id);
|
||
/* 1) 直接保存 using_pack,等同于收到一次切换指令 */
|
||
(void)emoji_storage_save_using_pack(pack_id);
|
||
/* 2) 直接收尾:设置“最后切换目标=下载包”,并触发 finalize 强制停在 default.json */
|
||
g_emoji_upgrade_last_switch_target = pack_id;
|
||
g_emoji_upgrade_pending_finalize = false; /* 防止后续真正收到切换指令时重复 finalize */
|
||
set_system_event(EMOJI_UPGRADE_FINALIZE_BIT);
|
||
}
|
||
g_emoji_download_in_progress = false;
|
||
clear_system_event(DOWNLOAD_SUCCESS_BIT);
|
||
ESP_LOGI(TAG_MAIN, "========== [EMOJI_DEBUG] DOWNLOAD_SUCCESS_BIT 处理完毕 ==========");
|
||
}
|
||
if (event_bits & DOWNLOAD_FAILED_BIT)
|
||
{
|
||
ESP_LOGI(TAG_MAIN, "处理下载表情失败事件");
|
||
xTaskNotify(notification_task_handle, BLE_SEND_DL_EMOJI_FAILED, eSetBits);
|
||
g_emoji_download_in_progress = false;
|
||
clear_system_event(DOWNLOAD_FAILED_BIT);
|
||
}
|
||
clear_system_event(event_bits);
|
||
}
|
||
|
||
ESP_LOGE(TAG_MAIN, "[CRITICAL] main_task while loop exited! This should never happen.");
|
||
main_task_handle = NULL;
|
||
vTaskDelete(NULL);
|
||
}
|
||
|
||
void read_mac_address() {
|
||
uint8_t mac[6];
|
||
// 读取 Wi-Fi Station 接口的 MAC 地址
|
||
esp_read_mac(mac, ESP_MAC_WIFI_STA);
|
||
printf("Wi-Fi Station MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
|
||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||
|
||
// 读取 Wi-Fi SoftAP 接口的 MAC 地址
|
||
esp_read_mac(mac, ESP_MAC_WIFI_SOFTAP);
|
||
printf("Wi-Fi SoftAP MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
|
||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||
}
|
||
void read_flash_unique_id() { //地址检查
|
||
uint64_t unique_id;
|
||
// 使用新API,第一个参数传入默认Flash芯片指针
|
||
esp_err_t err = esp_flash_read_unique_chip_id(esp_flash_default_chip, &unique_id);
|
||
|
||
if (err == ESP_OK) {
|
||
printf("Flash Unique ID: 0x%016llx\n", unique_id);
|
||
} else if (err == ESP_ERR_NOT_SUPPORTED) {
|
||
printf("This flash chip does not support reading Unique ID.\n");
|
||
} else {
|
||
printf("Failed to read Flash Unique ID, error: %d\n", err);
|
||
}
|
||
}
|
||
void list_files_in_dir(const char *path) {
|
||
DIR *dir = opendir(path);
|
||
if (dir == NULL) {
|
||
printf("Failed to open directory: %s\n", path);
|
||
return;
|
||
}
|
||
|
||
struct dirent *entry;
|
||
printf("Files in '%s':\n", path);
|
||
while ((entry = readdir(dir)) != NULL) {
|
||
// 跳过 . 和 ..
|
||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||
continue;
|
||
// 可以进一步判断 entry->d_type == DT_REG 是否为普通文件
|
||
printf(" %s\n", entry->d_name);
|
||
}
|
||
closedir(dir);
|
||
}
|
||
/* //简单文件测试工具
|
||
void get_file_size(const char *path) {
|
||
struct stat st;
|
||
if (stat(path, &st) != 0) {
|
||
ESP_LOGW(TAG_MAIN,"stat failed for %s\n", path);
|
||
return;
|
||
}
|
||
float size_kb = st.st_size / 1024.0;
|
||
ESP_LOGI(TAG_MAIN, "File %s size: %.2f KB", path, size_kb);
|
||
}
|
||
*/
|
||
// 调用示例:假设FAT挂载点为基础路径 "/sdcard"
|
||
void gpio_input_init(void) {
|
||
gpio_config_t io_conf = {
|
||
.pin_bit_mask = (1ULL << 53), // 选择要配置的引脚[reference:2]
|
||
.mode = GPIO_MODE_INPUT, // 设置为输入模式[reference:3][reference:4]
|
||
.pull_up_en = GPIO_PULLUP_ENABLE, // 使能内部上拉电阻[reference:5]
|
||
.pull_down_en = GPIO_PULLDOWN_DISABLE, // 禁用内部下拉电阻[reference:6]
|
||
.intr_type = GPIO_INTR_DISABLE, // 禁用中断[reference:7]
|
||
};
|
||
gpio_config(&io_conf); // 应用配置[reference:8]
|
||
}
|
||
|
||
void gpio_input_init2(void) {
|
||
gpio_config_t io_conf = {
|
||
.pin_bit_mask = (1ULL << 54), // 选择要配置的引脚[reference:2]
|
||
.mode = GPIO_MODE_INPUT, // 设置为输入模式[reference:3][reference:4]
|
||
.pull_up_en = GPIO_PULLUP_ENABLE, // 使能内部上拉电阻[reference:5]
|
||
.pull_down_en = GPIO_PULLDOWN_DISABLE, // 禁用内部下拉电阻[reference:6]
|
||
.intr_type = GPIO_INTR_DISABLE, // 禁用中断[reference:7]
|
||
};
|
||
gpio_config(&io_conf); // 应用配置[reference:8]
|
||
}
|
||
|
||
/******************************************************* app_main ************************************************************************/
|
||
uint32_t get_pin_func(int gpio_num)//直连MUX测试
|
||
{
|
||
// 直接返回 MCU_SEL 值
|
||
return REG_GET_FIELD(IO_MUX_GPIO30_REG + gpio_num * 4, MCU_SEL);
|
||
}
|
||
extern bool g_wifi_state; //wifi状态
|
||
extern void test_decrypt(void); //加解密
|
||
#define ENC_A_GPIO 53
|
||
#define ENC_B_GPIO 54
|
||
#define PCNT_UNIT PCNT_UNIT_0 // 使用PCNT单元0
|
||
#define PCNT_CHANNEL PCNT_CHANNEL_0 // 使用通道0
|
||
|
||
extern void play_wav4(float volume);
|
||
void loop_play_with_decreasing_volume(void)
|
||
{
|
||
// 1. 硬件只初始化一次
|
||
ns4168_i2s_init();
|
||
|
||
float volume = 1.0f; // 起始最大音量
|
||
float step = 0.1f; // 每次递减 0.1(可调)
|
||
const float min_volume = 0.0f; // 静音阈值,小于等于此值停止
|
||
|
||
while (volume > min_volume) {
|
||
// 播放当前音量的音频
|
||
play_wav2(volume);
|
||
ESP_LOGI("MAIN", "播放完成,音量=%.1f,等待3秒", volume);
|
||
|
||
// 延时 3 秒
|
||
vTaskDelay(pdMS_TO_TICKS(3000));
|
||
|
||
// 音量递减
|
||
volume -= step;
|
||
if (volume < min_volume) volume = min_volume; // 避免负数
|
||
}
|
||
|
||
ESP_LOGI("MAIN", "音量已减至零,循环结束");
|
||
}
|
||
|
||
|
||
int64_t init_start_us = 0;
|
||
#define MUSIC_DOWNLOAD 0
|
||
#define MUSIC_TEST 0
|
||
|
||
/* S3:下载工具固件。PC 与板子同一热点后,把 DL_HTTP_BASE 改成 ipconfig 看到的 IPv4。
|
||
* 置 1:下载 EAF 到 /extflash/eaf/。 */
|
||
#define EAF_DOWNLOAD 1
|
||
#define DL_HTTP_BASE "http://10.70.24.125:8000/"
|
||
#define DL_WIFI_SSID "REDMIK80U"
|
||
#define DL_WIFI_PASS "33333333"
|
||
#if EAF_DOWNLOAD
|
||
static const char *const s_eaf_files[] = {
|
||
"maomi_xizao_304x240.eaf",
|
||
"dog_test_304x240.eaf",
|
||
};
|
||
#endif
|
||
void play_wav3(float volume);
|
||
extern void play_wav5(float volume);
|
||
extern void play_wav_p4(float volume);
|
||
extern void play_wav_ring_with_volume(float volume);
|
||
void app_main(void)
|
||
{
|
||
|
||
|
||
//app_sr_init();
|
||
|
||
#if EAF_DOWNLOAD
|
||
ESP_LOGI(TAG_MAIN, "资源下载模式:EAF -> /extflash/eaf/");
|
||
ESP_LOGI(TAG_MAIN, "WiFi SSID=%s HTTP=%s", DL_WIFI_SSID, DL_HTTP_BASE);
|
||
flash_main();
|
||
if (mkdir("/extflash/eaf", 0777) < 0 && errno != EEXIST) {
|
||
ESP_LOGE(TAG_MAIN, "mkdir /extflash/eaf 失败: %s", strerror(errno));
|
||
}
|
||
print_memory_usage("WIFI初始化前");
|
||
wifi_main(DL_WIFI_SSID, DL_WIFI_PASS);
|
||
{
|
||
int waited_ms = 0;
|
||
const int wait_limit_ms = 45000;
|
||
while (!wifi_is_connected_now() && waited_ms < wait_limit_ms) {
|
||
vTaskDelay(pdMS_TO_TICKS(500));
|
||
waited_ms += 500;
|
||
}
|
||
if (!wifi_is_connected_now()) {
|
||
ESP_LOGE(TAG_MAIN, "WiFi 在 %d ms 内未拿到 IP,放弃下载", wait_limit_ms);
|
||
} else {
|
||
for (size_t i = 0; i < sizeof(s_eaf_files) / sizeof(s_eaf_files[0]); i++) {
|
||
char url[192];
|
||
char rel[96];
|
||
snprintf(url, sizeof(url), "%s%s", DL_HTTP_BASE, s_eaf_files[i]);
|
||
snprintf(rel, sizeof(rel), "eaf/%s", s_eaf_files[i]);
|
||
ESP_LOGI(TAG_MAIN, "开始下载 %s -> /extflash/%s", url, rel);
|
||
if (download_file(url, rel) != ESP_OK) {
|
||
ESP_LOGE(TAG_MAIN, "下载失败: %s", s_eaf_files[i]);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
list_files_in_directory("/extflash/eaf");
|
||
ESP_LOGI(TAG_MAIN, "验收:maomi_xizao_304x240.eaf=719328 dog_test_304x240.eaf=1711719");
|
||
while (1) {
|
||
vTaskDelay(pdMS_TO_TICKS(10000));
|
||
}
|
||
#else
|
||
app_lcd_init();
|
||
ESP_ERROR_CHECK(app_lvgl_init());
|
||
#endif
|
||
//MS523_init();
|
||
//NST112_init();
|
||
|
||
//BMI_init();
|
||
|
||
#if MUSIC_TEST
|
||
flash_main();
|
||
ns4168_i2s_init();
|
||
while (1) {
|
||
float volume = 1.0f;
|
||
while (volume > 0.0f) {
|
||
play_wav2(volume);
|
||
vTaskDelay(pdMS_TO_TICKS(3000));
|
||
volume -= 0.1f;
|
||
ESP_LOGW("TEST MODE","当前音量 :%.1f",volume);
|
||
}
|
||
// 一轮结束,下一轮自动从头开始(音量重置)
|
||
}
|
||
|
||
#endif
|
||
|
||
//ns4168_start_white_noise_test();
|
||
//play_wav();
|
||
/*
|
||
flash_main();
|
||
int res = mkdir("/extflash/fonts", 0777);
|
||
if (res < 0) {
|
||
ESP_LOGE("example", "mkdir 失败: %s", strerror(errno));
|
||
} else {
|
||
ESP_LOGI("example", "目录创建成功: /extflash/fonts");
|
||
}
|
||
rename("/extflash/0/MiSans_VF_25.bin", "/extflash/fonts/MiSans_VF_25.bin");
|
||
rename("/extflash/0/MiSans_VF_30.bin", "/extflash/fonts/MiSans_VF_30.bin");
|
||
rename("/extflash/0/MiSans_VF_40.bin", "/extflash/fonts/MiSans_VF_40.bin");
|
||
rename("/extflash/0/MiSans_VF_60.bin", "/extflash/fonts/MiSans_VF_60.bin");
|
||
rename("/extflash/0/MiSans_VF_100.bin", "/extflash/fonts/MiSans_VF_100.bin");
|
||
rename("/extflash/0/MiSans_VF_180.bin", "/extflash/fonts/MiSans_VF_180.bin");
|
||
rename("/extflash/0/iconfont_25.bin", "/extflash/fonts/iconfont_25.bin");
|
||
list_files_in_dir("/extflash/fonts");*/
|
||
#if MUSIC_DOWNLOAD
|
||
flash_main();
|
||
print_memory_usage("WIFI初始化前");
|
||
wifi_main("REDMIK80U","33333333");
|
||
vTaskDelay(pdMS_TO_TICKS(10000));
|
||
download_file("http://10.70.24.125:8000/ring2.wav","ring2.wav");
|
||
list_files_in_dir("/extflash/0/");
|
||
|
||
#endif
|
||
|
||
/*
|
||
mkdir("/extflash/fonts", 0755);
|
||
list_files_in_dir("/extflash/fonts");
|
||
list_files_in_dir("/extflash/0");
|
||
rename("/extflash/0/MiSans_VF_25.bin", "/extflash/fonts/MiSans_VF_25.bin");
|
||
rename("/extflash/0/MiSans_VF_30.bin", "/extflash/fonts/MiSans_VF_30.bin");
|
||
rename("/extflash/0/MiSans_VF_40.bin", "/extflash/fonts/MiSans_VF_40.bin");
|
||
rename("/extflash/0/MiSans_VF_60.bin", "/extflash/fonts/MiSans_VF_60.bin");
|
||
rename("/extflash/0/MiSans_VF_100.bin", "/extflash/fonts/MiSans_VF_100.bin");
|
||
rename("/extflash/0/MiSans_VF_180.bin", "/extflash/fonts/MiSans_VF_180.bin");
|
||
rename("/extflash/0/iconfont_25.bin", "/extflash/fonts/iconfont_25.bin");
|
||
*/
|
||
|
||
|
||
//flash_main();
|
||
//list_files_in_dir("/extflash/fonts");
|
||
//list_files_in_dir("/extflash/0");
|
||
//rename("/extflash/0/MiSans_VF_180.bin", "/extflash/fonts/MiSans_VF_180.bin");
|
||
//app_sr_init();
|
||
//flash_main();
|
||
//ble_main();
|
||
//vTaskDelay(pdMS_TO_TICKS(500));
|
||
//ble_app_advertise_after_system_init();
|
||
//ESP_ERROR_CHECK(appBMI_init();_lcd_init());
|
||
#if EXAMPLE_USE_TOUCH
|
||
//ESP_ERROR_CHECK(app_touch_init());
|
||
|
||
#endif
|
||
//app_touch_init();
|
||
|
||
|
||
//ESP_ERROR_CHECK(app_change_init());
|
||
//NST112_init();
|
||
//app_touch_init();
|
||
//ES7243E_Init();
|
||
|
||
//ESP_ERROR_CHECK(nvs_flash_init());
|
||
|
||
//read_mac_address();
|
||
//read_flash_unique_id();
|
||
//wifi_main("REDMIK80U","33333333");
|
||
//pet_init();
|
||
/*
|
||
while (1) {
|
||
ESP_LOGW("TAG","WIFI_STATE:%d",g_wifi_state);
|
||
vTaskDelay(pdMS_TO_TICKS(500));
|
||
}
|
||
*/
|
||
|
||
//motor_control_init();
|
||
//ESP_ERROR_CHECK(foc_openloop_start());
|
||
//gpio_input_init();
|
||
//gpio_input_init2();
|
||
/*
|
||
pcnt_config_t pcnt_config = {
|
||
.pulse_gpio_num = ENC_A_GPIO, // A相作为脉冲输入
|
||
.ctrl_gpio_num = ENC_B_GPIO, // B相作为控制输入
|
||
|
||
// 关键:设置计数规则[reference:10]
|
||
// 正转(A相超前B相90°):A上升沿时B为低,计数+1;A下降沿时B为高,计数+1
|
||
.pos_mode = PCNT_COUNT_INC, // 上升沿计数增加
|
||
.neg_mode = PCNT_COUNT_INC, // 下降沿计数增加
|
||
.lctrl_mode = PCNT_MODE_REVERSE, // 控制脚为低时,反向计数(即增加)
|
||
.hctrl_mode = PCNT_MODE_KEEP, // 控制脚为高时,保持计数方向(即增加)
|
||
|
||
.counter_h_lim = 32767,
|
||
.counter_l_lim = -32768,
|
||
.unit = PCNT_UNIT,
|
||
.channel = PCNT_CHANNEL,
|
||
};
|
||
pcnt_unit_config(&pcnt_config);
|
||
|
||
pcnt_set_filter_value(PCNT_UNIT, 50); // 设置滤波阈值(APB时钟周期数)
|
||
pcnt_filter_enable(PCNT_UNIT);
|
||
// 3. 启动PCNT
|
||
pcnt_counter_resume(PCNT_UNIT);
|
||
pcnt_counter_clear(PCNT_UNIT);
|
||
|
||
// 4. 读取计数值
|
||
int16_t count = 0;
|
||
while (1) {
|
||
pcnt_get_counter_value(PCNT_UNIT, &count);
|
||
//printf("Encoder Count: %d\n", count);
|
||
if(count>30000||count<-30000){
|
||
count=0;
|
||
}
|
||
ESP_LOGW("TAG","A:%d",gpio_get_level(ENC_A_GPIO));
|
||
ESP_LOGW("TAG","B:%d",gpio_get_level(ENC_B_GPIO));
|
||
vTaskDelay(pdMS_TO_TICKS(50));
|
||
}
|
||
*/
|
||
//test_decrypt();
|
||
//ble_main();
|
||
//wifi_main("REDMIK80U","33333333");
|
||
//vTaskDelay(pdMS_TO_TICKS(10000));
|
||
//obtain_time();
|
||
|
||
//print_date_time();
|
||
/*
|
||
while (1) {
|
||
time_t now;
|
||
struct tm timeinfo;
|
||
time(&now);
|
||
localtime_r(&now, &timeinfo);
|
||
ESP_LOGI("NTP", "当前时间: %02d:%02d:%02d",
|
||
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
|
||
vTaskDelay(10000 / portTICK_PERIOD_MS);
|
||
}
|
||
*/
|
||
/*
|
||
//esp_log_level_set("i2c.master", ESP_LOG_NONE);
|
||
const int64_t init_start_us = esp_timer_get_time();
|
||
|
||
|
||
//flash_main();
|
||
ESP_ERROR_CHECK(nvs_flash_init());
|
||
|
||
if (emoji_storage_load() != ESP_OK)
|
||
{
|
||
ESP_LOGW(TAG_MAIN, "表情包存储加载失败,使用默认");
|
||
}
|
||
|
||
//extract_json_zip("0.zip");
|
||
//list_files_in_dir("/extflash");
|
||
if (!init_event_system())
|
||
{
|
||
ESP_LOGE(TAG_MAIN, "事件系统初始化失败");
|
||
ESP_LOGE(TAG_MAIN, "设备初始化失败前已耗时: %" PRId64 " ms",
|
||
(esp_timer_get_time() - init_start_us) / 1000);
|
||
return;
|
||
}
|
||
|
||
ESP_ERROR_CHECK(app_lcd_init());
|
||
#if EXAMPLE_USE_TOUCH
|
||
//ESP_ERROR_CHECK(app_touch_init());
|
||
#endif
|
||
app_touch_init();
|
||
//NST112_init();
|
||
//print_memory_usage("LCD初始化完成");
|
||
|
||
|
||
//load_brightness_from_nvs();
|
||
|
||
|
||
//BMI_init();
|
||
//vTaskDelay(pdMS_TO_TICKS(10000));
|
||
//app_sr_init();
|
||
|
||
print_memory_usage("APP初始化完成");
|
||
const int64_t init_elapsed_us = esp_timer_get_time() - init_start_us;
|
||
ESP_LOGI(TAG_MAIN, "设备初始化耗时: %" PRId64 " ms (%" PRId64 " us)",
|
||
init_elapsed_us / 1000, init_elapsed_us);
|
||
|
||
|
||
//motor_control_init();
|
||
//ESP_ERROR_CHECK(foc_openloop_start());
|
||
|
||
BaseType_t ret = xTaskCreatePinnedToCore(main_task, "MAIN", MAIN_TASK_STACK_SIZE,
|
||
NULL, MAIN_TASK_PRIORITY, &main_task_handle,1);
|
||
if (ret == pdPASS)
|
||
{
|
||
print_memory_usage("主任务创建(内部RAM)");
|
||
ESP_LOGI(TAG_MAIN, "[DEBUG] main_task_handle=%p (内部RAM栈)", (void *)main_task_handle);
|
||
}
|
||
else
|
||
{
|
||
ESP_LOGW(TAG_MAIN, "内部RAM不足(需%d bytes),尝试SPIRAM创建main_task", MAIN_TASK_STACK_SIZE);
|
||
size_t stack_words = MAIN_TASK_STACK_SIZE / sizeof(StackType_t);
|
||
main_task_stack = heap_caps_malloc(MAIN_TASK_STACK_SIZE, MALLOC_CAP_SPIRAM);
|
||
if (main_task_stack)
|
||
{
|
||
main_task_handle = xTaskCreateStaticPinnedToCore(
|
||
main_task, "MAIN", stack_words,
|
||
NULL, MAIN_TASK_PRIORITY,
|
||
main_task_stack, &main_task_tcb, 1);
|
||
if (main_task_handle)
|
||
{
|
||
// print_memory_usage("主任务创建(SPIRAM)");
|
||
ESP_LOGI(TAG_MAIN, "[DEBUG] main_task_handle=%p (SPIRAM栈)", (void *)main_task_handle);
|
||
}
|
||
else
|
||
{
|
||
ESP_LOGE(TAG_MAIN, "[CRITICAL] main_task SPIRAM创建也失败!");
|
||
heap_caps_free(main_task_stack);
|
||
main_task_stack = NULL;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ESP_LOGE(TAG_MAIN, "[CRITICAL] 无法为main_task分配SPIRAM栈!");
|
||
}
|
||
}
|
||
|
||
//print_memory_usage("MAIN初始化完成");
|
||
ble_main();
|
||
print_memory_usage("蓝牙初始化完成");
|
||
g_system_init_done = true;
|
||
|
||
|
||
ESP_ERROR_CHECK(app_lvgl_init());
|
||
//ESP_ERROR_CHECK(app_change_init());
|
||
const int64_t init_end_us = esp_timer_get_time();
|
||
//下载json压缩包,解压到外部闪存,准备表情包资源
|
||
//remove("/extflash/0.zip");
|
||
//list_files_in_dir("/extflash/0");
|
||
//list_files_in_dir("/extflash/0/A");
|
||
//get_file_size("/extflash/0.zip");
|
||
*/
|
||
/*
|
||
mkdir("/extflash/fonts", 0755);
|
||
rename("/extflash/0/MiSans_VF_25.bin", "/extflash/fonts/MiSans_VF_25.bin");
|
||
rename("/extflash/0/MiSans_VF_30.bin", "/extflash/fonts/MiSans_VF_30.bin");
|
||
rename("/extflash/0/MiSans_VF_40.bin", "/extflash/fonts/MiSans_VF_40.bin");
|
||
rename("/extflash/0/MiSans_VF_60.bin", "/extflash/fonts/MiSans_VF_60.bin");
|
||
rename("/extflash/0/MiSans_VF_100.bin", "/extflash/fonts/MiSans_VF_100.bin");
|
||
rename("/extflash/0/iconfont_25.bin", "/extflash/fonts/iconfont_25.bin");
|
||
*/
|
||
|
||
|
||
//ESP_LOGI("HEX", "文件名长度: %d", strlen("2hours.json"));
|
||
//ESP_LOG_BUFFER_HEX("FILENAME", "2hours.json", strlen("2hours.json"));
|
||
// 1. 检查父路径 /extflash/0
|
||
/*
|
||
struct stat st;
|
||
if (stat("/extflash/0", &st) == 0) {
|
||
if (S_ISDIR(st.st_mode)) {
|
||
ESP_LOGI("DIAG", "/extflash/0 是一个目录 ✓");
|
||
} else if (S_ISREG(st.st_mode)) {
|
||
ESP_LOGE("DIAG", "/extflash/0 是一个普通文件 ✗ 这就是问题!");
|
||
} else {
|
||
ESP_LOGE("DIAG", "/extflash/0 存在但不是目录也不是普通文件");
|
||
}
|
||
} else {
|
||
ESP_LOGE("DIAG", "/extflash/0 不存在,errno=%d", errno);
|
||
}
|
||
|
||
// 2. 尝试在根目录创建同名文件
|
||
FILE *test = fopen("/extflash/text.bin", "wb");
|
||
if (test) {
|
||
ESP_LOGI("DIAG", "在根目录创建文件成功 ✓ 说明文件名本身合法");
|
||
fclose(test);
|
||
unlink("/extflash/text.bin");
|
||
} else {
|
||
ESP_LOGE("DIAG", "在根目录也失败,errno=%d", errno);
|
||
}
|
||
list_files_in_dir("/extflash");
|
||
|
||
|
||
|
||
uint32_t reg_val = REG_READ(IO_MUX_GPIO0_REG);
|
||
ESP_LOGI(TAG_MAIN, "IO_MUX_GPIO0_REG: 0x%d", reg_val);
|
||
reg_val = REG_READ(IO_MUX_GPIO29_REG);
|
||
ESP_LOGI(TAG_MAIN, "IO_MUX_GPIO29_REG: 0x%d", reg_val);
|
||
reg_val = REG_READ(IO_MUX_GPIO30_REG);
|
||
ESP_LOGI(TAG_MAIN, "IO_MUX_GPIO30_REG: 0x%d", reg_val);
|
||
reg_val = REG_READ(IO_MUX_GPIO39_REG);
|
||
ESP_LOGI(TAG_MAIN, "IO_MUX_GPIO39_REG: 0x%d", reg_val);
|
||
*/
|
||
//vTaskDelay(pdMS_TO_TICKS(5000));
|
||
//motor_post_play_sticker_direction(0x02);
|
||
//vTaskDelay(pdMS_TO_TICKS(5000));
|
||
//motor_post_play_sticker_direction(0x0A);
|
||
//vTaskDelay(pdMS_TO_TICKS(5000));
|
||
//motor_post_play_sticker_direction(0x01);
|
||
//vTaskDelay(pdMS_TO_TICKS(2000));
|
||
print_memory_usage("系统初始化完成");
|
||
}
|
||
|
||
static void get_boot_address(void)
|
||
{
|
||
// 1. 获取「当前设置的启动分区」(下次重启会从这个分区启动)
|
||
const esp_partition_t *boot_part = esp_ota_get_boot_partition();
|
||
if (boot_part)
|
||
{
|
||
ESP_LOGE(TAG_MAIN, "===== 当前启动分区信息 =====");
|
||
ESP_LOGE(TAG_MAIN, "分区名称(标识):%s", boot_part->label); // 如 factory/ota_0/ota_1
|
||
ESP_LOGE(TAG_MAIN, "Flash物理起始地址:0x%08X", (unsigned int)boot_part->address); // 核心:启动物理地址
|
||
ESP_LOGE(TAG_MAIN, "分区大小:0x%08X 字节", (unsigned int)boot_part->size);
|
||
ESP_LOGE(TAG_MAIN, "分区类型:%s(App分区=0x00)",
|
||
boot_part->type == ESP_PARTITION_TYPE_APP ? "App分区" : "其他分区");
|
||
}
|
||
|
||
// 2. 获取「当前正在运行的分区」(和启动分区可能一致,OTA后重启才会变)
|
||
const esp_partition_t *running_part = esp_ota_get_running_partition();
|
||
if (running_part)
|
||
{
|
||
ESP_LOGE(TAG_MAIN, "===== 当前运行分区信息 =====");
|
||
ESP_LOGE(TAG_MAIN, "分区名称(标识):%s", running_part->label);
|
||
ESP_LOGE(TAG_MAIN, "Flash物理起始地址:0x%08X", (unsigned int)running_part->address);
|
||
}
|
||
|
||
// 3. 单独获取Factory分区的地址(精准定位出厂分区)
|
||
const esp_partition_t *factory_part = esp_partition_find_first(
|
||
ESP_PARTITION_TYPE_APP,
|
||
ESP_PARTITION_SUBTYPE_APP_FACTORY,
|
||
NULL);
|
||
if (factory_part)
|
||
{
|
||
ESP_LOGE(TAG_MAIN, "===== Factory分区信息 =====");
|
||
ESP_LOGE(TAG_MAIN, "Flash物理起始地址:0x%08X", (unsigned int)factory_part->address);
|
||
}
|
||
}
|
||
|
||
static void show_all_nvs_entries(void)
|
||
{
|
||
nvs_iterator_t iter = NULL;
|
||
esp_err_t err = nvs_entry_find(NVS_DEFAULT_PART_NAME, NULL, NVS_TYPE_ANY, &iter);
|
||
|
||
if (err != ESP_OK || iter == NULL)
|
||
{
|
||
ESP_LOGW("NVS", "NVS 为空,没有数据");
|
||
return;
|
||
}
|
||
|
||
ESP_LOGI("NVS", "=================== NVS 全部内容 ====================");
|
||
|
||
while (err == ESP_OK)
|
||
{
|
||
nvs_entry_info_t info;
|
||
nvs_entry_info(iter, &info);
|
||
|
||
ESP_LOGI("NVS", "命名空间:%-12s key: %-15s 类型 :%d", info.namespace_name, info.key, info.type);
|
||
|
||
err = nvs_entry_next(&iter);
|
||
}
|
||
ESP_LOGI("NVS", "=====================================================");
|
||
|
||
nvs_release_iterator(iter);
|
||
}
|
||
|
||
// 内存监控辅助函数
|
||
static void print_memory_usage(const char *step_name) // 打印内存使用情况
|
||
{
|
||
// 内部SRAM
|
||
uint32_t internal_sram_total = heap_caps_get_total_size(MALLOC_CAP_INTERNAL);
|
||
uint32_t internal_sram_free = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
|
||
uint32_t internal_sram_used = internal_sram_total - internal_sram_free;
|
||
|
||
// PSRAM
|
||
uint32_t psram_total = heap_caps_get_total_size(MALLOC_CAP_SPIRAM);
|
||
uint32_t psram_free = heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
|
||
uint32_t psram_used = psram_total - psram_free;
|
||
|
||
// 总SRAM(内部+PSRAM)
|
||
uint32_t total_ram_total = internal_sram_total + psram_total;
|
||
uint32_t total_ram_free = internal_sram_free + psram_free;
|
||
|
||
ESP_LOGI(TAG_MAIN, "=== %s 后内存使用情况 ===", step_name);
|
||
|
||
ESP_LOGI(TAG_MAIN, "内部SRAM: 已用 %8" PRIu32 " bytes, 剩余 %8" PRIu32 " bytes, 总计 %8" PRIu32 " bytes", internal_sram_used, internal_sram_free, internal_sram_total);
|
||
ESP_LOGI(TAG_MAIN, "PSRAM: 已用 %8" PRIu32 " bytes, 剩余 %8" PRIu32 " bytes, 总计 %8" PRIu32 " bytes", psram_used, psram_free, psram_total);
|
||
ESP_LOGI(TAG_MAIN, "总内存: 已用 %8" PRIu32 " bytes, 剩余 %8" PRIu32 " bytes, 总计 %8" PRIu32 " bytes", internal_sram_used + psram_used, total_ram_free, total_ram_total);
|
||
ESP_LOGI(TAG_MAIN, "====================");
|
||
}
|
||
|
||
|
||
|
||
|
||
|