#include "wifi_board.h"
#include "audio_codecs/es8311_audio_codec.h"
#include "audio_codecs/box_audio_codec.h"
#include "application.h"
#include "button.h"
#include "config.h"
#include "iot/thing_manager.h"
#include "led/single_led.h"
#include "display/display.h"
#include "boards/common/power_save_timer.h"  // 添加电源管理头文件
#include "assets/lang_config.h"              // 引入语音配置头文件  新增
#include "volume_config.h"                   // 引入音量配置头文件
#include "boards/common/qmi8658a.h"          // 引入QMI8658A姿态传感器头文件
#include "boards/common/veml7700.h"          // 引入VEML7700环境光传感器头文件
#include "imu_sensor_thing.h"                // 引入IMU传感器IoT设备头文件
#include "system_info.h"                     // 引入系统信息头文件
#include "settings.h"
#include <cmath>                             // 添加数学函数头文件

#include <wifi_station.h>
#include <ssid_manager.h>
#include <esp_wifi.h>
#include <nvs_flash.h>
#include <esp_log.h>
#include <esp_efuse_table.h>
#include <driver/i2c_master.h>
#include <inttypes.h> // 添加PRIu32宏的定义支持
#include <cstring>
#include "driver/gpio.h"
#include <esp_adc/adc_oneshot.h>
#include <esp_adc/adc_cali_scheme.h>
#include <esp_log.h>
#include <vector>
#include <algorithm>
#include "freertos/queue.h"

#define TAG "Airhub1"
#define Pro_TAG "Airhub"

#include <driver/touch_pad.h>
#include <driver/touch_sensor.h>

// 触摸事件类型
typedef enum {
    TOUCH_EVENT_PRESS = 0,  // 触摸按下事件
    TOUCH_EVENT_RELEASE     // 触摸释放事件
} touch_event_type_t;

// 触摸状态枚举
typedef enum {
    TOUCH_STATE_IDLE,       // 空闲状态 - 未触摸
    TOUCH_STATE_PRESSED,    // 已按下状态 - 已经触发事件，等待释放
    TOUCH_STATE_RELEASED,   // 释放过渡状态 - 确认松手
    TOUCH_STATE_DEBOUNCE    // 去抖状态 - 等待信号稳定
} touch_state_t;

// 触摸事件数据结构
typedef struct {
    int pad_num;              // 触摸板编号
    touch_event_type_t type;  // 事件类型：按下或释放
} touch_event_data_t;

// 前向声明TouchEventTask函数
static void TouchEventTask(void* arg);

class MovecallMojiESP32S3 : public WifiBoard {
private:
    // 触摸状态相关
    touch_state_t touch_states_[4]; // 每个触摸点的状态
    uint32_t touch_last_time_[4];   // 每个触摸点的最后操作时间
    uint32_t raw_touch_values_[4];  // 原始触摸值
    uint32_t touch_thresholds_[4];  // 触摸阈值

    // 去抖动和最短释放时间参数
    const uint32_t DEBOUNCE_TIME_MS = 100;    // 去抖时间(毫秒)
    const uint32_t MIN_RELEASE_TIME_MS = 300; // 最短释放确认时间

    // 添加触摸任务锁定相关变量
    bool touch_task_locked_ = false;          // 触摸任务锁定标志
    int active_touch_pad_ = -1;               // 当前活跃的触摸点编号
    uint32_t touch_task_start_time_ = 0;      // 触摸任务开始时间
    const uint32_t TOUCH_TASK_TIMEOUT_MS = 10000; // 任务超时时间(10秒)

    PowerSaveTimer* power_save_timer_;
    static MovecallMojiESP32S3* instance_;
    static void IRAM_ATTR TouchPadISR(void* arg);
    i2c_master_bus_handle_t codec_i2c_bus_;

    // QMI8658A姿态传感器相关
    QMI8658A* imu_sensor_;
    esp_timer_handle_t imu_timer_handle_;
    qmi8658a_data_t latest_imu_data_;
    bool imu_initialized_;
    const int kImuReadInterval = 160;  // 160ms读取一次IMU数据，匹配125Hz采样率
    iot::ImuSensorThing* imu_thing_;   // IMU传感器IoT设备实例

    // VEML7700环境光传感器相关
    VEML7700* light_sensor_;
    bool light_sensor_initialized_;

    // 石头匹配功能相关
    static constexpr int kDefaultRatioThresholdPercent = 15;  // 光谱比值匹配阈值（%）
    static constexpr int kDefaultLuxThresholdPercent = 50;    // 亮度等级容差阈值（%）
    static constexpr const char* kStoneNvsNamespace = "stone";  // NVS命名空间

    // 亮度等级划分（用于判断是否处于相近光照环境）
    // 0=极暗(<5lux) 1=暗(5-50) 2=中(50-500) 3=亮(500-5000) 4=极亮(>5000)
    static int GetBrightnessLevel(float lux) {
        if (lux < 5.0f)    return 0;
        if (lux < 50.0f)   return 1;
        if (lux < 500.0f)  return 2;
        if (lux < 5000.0f) return 3;
        return 4;
    }

    static const char* GetBrightnessLevelName(int level) {
        static const char* names[] = {"极暗(<5lux)", "暗(5-50)", "中(50-500)", "亮(500-5000)", "极亮(>5000)"};
        if (level >= 0 && level <= 4) return names[level];
        return "未知";
    }

    // 电量检测相关
    adc_oneshot_unit_handle_t adc_handle_;
    adc_cali_handle_t adc_cali_handle_;  // ADC校准句柄
    esp_timer_handle_t battery_timer_handle_;
    std::vector<int> adc_values_;  // ADC采样值队列（存储校准后的mV值）
    uint32_t battery_level_;// 电池电量百分比
    int battery_ticks_;
    int battery_alert_ticks_;
    int status_report_ticks_;  // 设备状态上报计数器
    bool status_report_enabled_;  // 设备状态上报是否启用
    bool rtc_online_;  // RTC火山引擎连接状态（开机默认false，连接成功后设为true）
    const int kBatteryAdcInterval = 10;  // 10秒检测一次
    const int kStatusReportInterval = 30;  // 30秒上报一次
    const int kStatusReportDelay = 3;  // 启动3秒后才开始上报
    const int kBatteryAdcDataCount = 20;  // 保存20个ADC值用于平均（增加采样次数）
    const int kBatteryAdcSampleCount = 10;  // 每次读取采样10次（增加采样次数）
    const char* DEVICE_STATUS_REPORT_URL = CONFIG_DEVICE_STATUS_REPORT_URL;  // 设备状态上报服务器URL
    Button boot_button_{BOOT_BUTTON_GPIO};  // 初始化列表
    Button volume_up_button_{VOLUME_UP_BUTTON_GPIO};
    Button volume_down_button_{VOLUME_DOWN_BUTTON_GPIO};
    Button story_button_{KEY4_GPIO, false, 2000};  // 长按2秒触发石头匹配

    bool production_test_mode_ = false;// 是否开启生产测试模式
    static const int TOUCH_QUEUE_SIZE = 5;// 触摸事件队列大小

    // 生产测试模式触摸检测标志位
    bool touch_detected_flag_ = false;     // 触摸检测标志位
    int touched_pad_index_ = -1;           // 被触摸的触摸板索引

    void EnterProductionTestMode();// 进入生产测试模式函数

    void ReportDeviceStatus(int battery_level);// 上报设备状态到服务器

public:
    // 将静态队列句柄移到public以便静态函数访问
    static QueueHandle_t touch_event_queue_;

    // 触摸事件处理方法
    void HandleTouchEvent(int touch_pad_num, touch_event_type_t event_type);
    // 重置所有触摸状态
    void ResetAllTouchStates();
    // 锁定触摸任务，指定当前活跃的触摸点
    void LockTouchTask(int touch_pad_num);
    // 解锁触摸任务，允许处理新的触摸
    void UnlockTouchTask();

    // 获取电池电量百分比
    bool GetBatteryLevel(int &level, bool& charging, bool& discharging) override {
        // 确保在首次查询时已采样到足够ADC数据，避免返回0导致误判
        if (adc_values_.size() < kBatteryAdcDataCount && adc_handle_ != nullptr) {
            for (int i = 0; i < kBatteryAdcDataCount; ++i) {
                ReadBatteryAdcData();
            }
        }

        level = static_cast<int>(battery_level_);
        charging = false;  // 暂时设为false，可根据需要实现充电检测
        discharging = true; // 暂时设为true，可根据需要实现放电检测
        return true;
    }

public:
    // 构造函数
    MovecallMojiESP32S3() : 
        power_save_timer_(nullptr),
        codec_i2c_bus_(nullptr),
        imu_sensor_(nullptr),
        imu_timer_handle_(nullptr),
        imu_initialized_(false),
        imu_thing_(nullptr),
        light_sensor_(nullptr),
        light_sensor_initialized_(false),
        adc_handle_(nullptr),
        battery_timer_handle_(nullptr),
        battery_level_(0),
        battery_ticks_(0),
        battery_alert_ticks_(0),
        status_report_ticks_(0),
        status_report_enabled_(false),
        rtc_online_(false),
        production_test_mode_(false),
        touch_detected_flag_(false),
        touched_pad_index_(-1)
    {
        // 初始化触摸状态
        for (int i = 0; i < 4; ++i) {
            touch_states_[i] = TOUCH_STATE_IDLE;
            touch_last_time_[i] = 0;
            raw_touch_values_[i] = 0;
            touch_thresholds_[i] = 0;
        }

        // 初始化触摸任务锁
        touch_task_locked_ = false;
        active_touch_pad_ = -1;
        touch_task_start_time_ = 0;

        // 使用240MHz作为CPU最大频率，10秒进入睡眠，-1表示不自动关机
        power_save_timer_ = new PowerSaveTimer(240, 10, -1);

        // 设置低功耗模式回调
        power_save_timer_->OnEnterSleepMode([this]() {
            ESP_LOGI(TAG, "🔋 进入低功耗模式：CPU降频、Light Sleep启用、功放关闭");
            
            // 关闭功放，进一步节省电量
            auto codec = GetAudioCodec();
            if (codec) {
                codec->EnableOutput(false);
                ESP_LOGI(TAG, "🔊 功放已关闭");
            }
        });

        power_save_timer_->OnExitSleepMode([this]() {
            ESP_LOGI(TAG, "🔋 退出低功耗模式：CPU恢复正常、Light Sleep禁用、功放打开");
            
            // 打开功放，恢复正常音频输出
            auto codec = GetAudioCodec();
            if (codec) {
                codec->EnableOutput(true);
                ESP_LOGI(TAG, "🔊 功放已打开");
            }
        });

        // 初始化按钮
        InitializeButtons();
        InitializeStoryButton();

        // 初始化I2C总线（必须在IMU传感器初始化之前）
        InitializeCodecI2c();

        // 初始化IoT功能，启用语音音量控制
        InitializeIot();

        // 初始化电量检测
        InitializeBatteryMonitor();

        // 初始化IMU传感器
        InitializeImuSensor();

        // 初始化VEML7700环境光传感器
        InitializeLightSensor();

        // 启用PowerSaveTimer，启用完整的低功耗管理
        power_save_timer_->SetEnabled(true);
        ESP_LOGI(TAG, "🔋 PowerSaveTimer已启用，20秒无活动将进入低功耗模式");

        // 延迟调用触摸板初始化，避免在构造函数中就调用
        ESP_LOGI(TAG, "在构造函数完成后调用触摸初始化");
                 // 使用task来延迟初始化触摸功能
         xTaskCreate([](void* arg) {
             MovecallMojiESP32S3* board = static_cast<MovecallMojiESP32S3*>(arg);
             // 延迟一段时间，确保其他组件初始化完成
             vTaskDelay(1000 / portTICK_PERIOD_MS);
             ESP_LOGI(TAG, "开始延迟初始化触摸板...");
             if (board) {
                 board->InitializeTouchPads();
             }
             vTaskDelete(NULL);
         }, "touch_init", 4096, this, 5, NULL);
    }

    // 发送触摸消息
    void SendTouchMessage(int touch_pad_num) {
        const char* message = nullptr;
        power_save_timer_->WakeUp();

        // 获取当前应用状态
        auto& app = Application::GetInstance();
        auto current_state = app.GetDeviceState();

        // 仅在 Dialog 对话状态且内部 listening 开启时有效
        if (!(current_state == kDeviceStateDialog && app.IsDialogUploadEnabled())) {
            ESP_LOGI(TAG, "触摸事件无效：仅在Dialog+listening内部状态下有效");
            if (touch_task_locked_ && active_touch_pad_ == touch_pad_num) {
                xTaskCreate([](void* arg) {
                    MovecallMojiESP32S3* board = static_cast<MovecallMojiESP32S3*>(arg);
                    if (board) {
                        board->UnlockTouchTask();
                    }
                    vTaskDelete(NULL);
                }, "unlock_invalid_state", 4096, this, 5, NULL);
            }
            return;
        }

        // 根据流程图中的情况处理触摸事件:
        // 1. 如果当前是Speaking状态，触摸事件不生效
        if (current_state == kDeviceStateSpeaking) {
            ESP_LOGI(TAG, "当前处于Speaking状态，触摸事件被忽略");
            // 由于任务未能执行，立即解锁触摸任务
            if (touch_task_locked_ && active_touch_pad_ == touch_pad_num) {
                ESP_LOGI(TAG, "触摸任务无法执行，创建任务来解锁");
                // 创建任务来解锁，避免直接调用可能导致栈溢出的操作
                xTaskCreate([](void* arg) {
                    MovecallMojiESP32S3* board = static_cast<MovecallMojiESP32S3*>(arg);
                    if (board) {
                        board->UnlockTouchTask();
                    }
                    vTaskDelete(NULL);
                }, "unlock_failed", 4096, this, 5, NULL);
            }
            return;
        }

        // 2. 如果当前是Listening状态且已检测到语音输入，触摸事件不生效
        if (current_state == kDeviceStateListening && app.IsVoiceDetected()) {
            ESP_LOGI(TAG, "当前处于Listening状态且已检测到语音输入，触摸事件被忽略");
            // 由于任务未能执行，立即解锁触摸任务
            if (touch_task_locked_ && active_touch_pad_ == touch_pad_num) {
                ESP_LOGI(TAG, "触摸任务无法执行，创建任务来解锁");
                // 创建任务来解锁，避免直接调用可能导致栈溢出的操作
                xTaskCreate([](void* arg) {
                    MovecallMojiESP32S3* board = static_cast<MovecallMojiESP32S3*>(arg);
                    if (board) {
                        board->UnlockTouchTask();
                    }
                    vTaskDelete(NULL);
                }, "unlock_failed", 4096, this, 5, NULL);
            }
            return;
        }

        // 根据触摸点选择消息
        switch (touch_pad_num) {
            case 0: message = "有人在摸摸你的脑袋"; break;
            case 1: message = "有人在摸摸你的肚子"; break;
            case 2: message = "有人在摸摸你的下巴"; break;
            case 3: message = "有人在摸摸你的后背"; break;
        }

        // 发送消息
        if (message != nullptr) {
            ESP_LOGI(TAG, "发送触摸消息: \"%s\"", message);

			// 仅在 Dialog+内部listening 下发送；其他状态在前面已返回

            // SendTextMessage内部会自动检查协议是否初始化
            app.SendTextMessage(message);
            ESP_LOGI(TAG, "消息已发送");

            // 消息已发送，开始监听语音回复
            // 任务将在收到回复或超时后结束
            // 通过TaskStateMonitor监听设备状态变化

            // 创建一个任务来监控设备状态变化
            if (touch_task_locked_ && active_touch_pad_ == touch_pad_num) {
                ESP_LOGI(TAG, "创建任务状态监控");

                xTaskCreate([](void* arg) {
                    MovecallMojiESP32S3* board = static_cast<MovecallMojiESP32S3*>(arg);
                    auto& app = Application::GetInstance();
                    uint32_t start_time = esp_timer_get_time() / 1000;

                    // 等待设备状态变为Speaking或超时
                    // 如果超时或设备重新回到Idle状态，则解锁触摸任务
                    while (true) {
                        auto state = app.GetDeviceState();
                        uint32_t current_time = esp_timer_get_time() / 1000;
                        uint32_t elapsed = current_time - start_time;

                        // 如果设备开始说话，等待它说完
                        if (state == kDeviceStateSpeaking) {
                            ESP_LOGI(TAG, "检测到设备进入Speaking状态，等待说话完成");
                            // 等待设备回到Idle状态
                            while (app.GetDeviceState() == kDeviceStateSpeaking) {
                                vTaskDelay(100 / portTICK_PERIOD_MS);

                                // 检查超时
                                uint32_t now = esp_timer_get_time() / 1000;
                                if (now - start_time > 30000) { // 30秒超时
                                    ESP_LOGW(TAG, "等待说话完成超时");
                                    break;
                                }
                            }
                            ESP_LOGI(TAG, "设备说话已完成，解锁触摸任务");
                            board->UnlockTouchTask();
                            break;
                        } 
                        // 如果设备回到Idle状态，可能是消息被忽略
                        else if (state == kDeviceStateIdle && elapsed > 1000) {
                            ESP_LOGW(TAG, "设备回到Idle状态，消息可能被忽略");
                            board->UnlockTouchTask();
                            break;
                        }
                        // 如果等待太久，自动解锁
                        else if (elapsed > 10000) { // 10秒超时
                            ESP_LOGW(TAG, "等待回复超时，解锁触摸任务");
                            board->UnlockTouchTask();
                            break;
                        }

                        vTaskDelay(200 / portTICK_PERIOD_MS);
                    }
                    vTaskDelete(NULL);
                }, "task_monitor", 8192, this, 5, NULL);
            }
        } else {
            // 无效的触摸点或消息，自动解锁
            if (touch_task_locked_ && active_touch_pad_ == touch_pad_num) {
                // 创建任务来解锁
                xTaskCreate([](void* arg) {
                    MovecallMojiESP32S3* board = static_cast<MovecallMojiESP32S3*>(arg);
                    if (board) {
                        board->UnlockTouchTask();
                    }
                    vTaskDelete(NULL);
                }, "unlock_invalid", 4096, this, 5, NULL);
            }
        }
    }

    // 析构函数
    ~MovecallMojiESP32S3() {
        delete power_save_timer_;

        // 清理IMU传感器资源
        if (imu_timer_handle_) {
            esp_timer_stop(imu_timer_handle_);
            esp_timer_delete(imu_timer_handle_);
        }
        if (imu_sensor_) {
            delete imu_sensor_;
        }
        if (imu_thing_) {
            delete imu_thing_;
        }

        // 清理VEML7700环境光传感器资源
        if (light_sensor_) {
            delete light_sensor_;
        }

        // 清理电量检测资源
        if (battery_timer_handle_) {
            esp_timer_stop(battery_timer_handle_);
            esp_timer_delete(battery_timer_handle_);
        }
        if (adc_handle_) {
            adc_oneshot_del_unit(adc_handle_);
        }
    }

    void InitializeCodecI2c() {
        ESP_LOGI(TAG, "Initializing I2C master bus for audio codec...");//
        // 初始化I2C外设 编解码器
        i2c_master_bus_config_t i2c_bus_cfg = {
            // .i2c_port = I2C_NUM_0,
            .i2c_port = I2C_NUM_1,
            .sda_io_num = AUDIO_CODEC_I2C_SDA_PIN,
            .scl_io_num = AUDIO_CODEC_I2C_SCL_PIN,
            .clk_source = I2C_CLK_SRC_DEFAULT,
            .glitch_ignore_cnt = 7,
            .intr_priority = 0,
            .trans_queue_depth = 0,
            .flags = {
                .enable_internal_pullup = 1,
            },
        };
        ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &codec_i2c_bus_));

        ScanI2cDevices();   // 新增 扫描I2C总线上的设备  新增陀螺仪/姿态传感器 业务代码
    }

    // 新增 扫描I2C总线上的设备 函数
    // ==============================================================================
    void ScanI2cDevices() {
        ESP_LOGI(TAG, "Scanning I2C bus for devices...");

        int devices_found = 0;
        // 只扫描指定的三个设备地址
        uint8_t target_addresses[] = {
            0x18,  // ES8311音频编解码器地址
            0x6A,  // QMI8658A姿态传感器地址
            0x6B,  // QMI8658A姿态传感器备用地址
            0x40,  // ES7210 ADC地址
            0x10,  // VEML7700环境光传感器地址
        };

        size_t addr_count = sizeof(target_addresses) / sizeof(target_addresses[0]);

        for (size_t i = 0; i < addr_count; i++) {
            uint8_t addr = target_addresses[i];
            i2c_device_config_t dev_cfg = {
                .dev_addr_length = I2C_ADDR_BIT_LEN_7,
                .device_address = addr,
                .scl_speed_hz = 100000,  // 使用较低的速度进行扫描
            };

            i2c_master_dev_handle_t dev_handle;
            esp_err_t ret = i2c_master_bus_add_device(codec_i2c_bus_, &dev_cfg, &dev_handle);
            if (ret == ESP_OK) {
                // 尝试读取一个字节来检测设备是否响应
                uint8_t dummy_data;
                ret = i2c_master_receive(dev_handle, &dummy_data, 1, 100);
                if (ret == ESP_OK || ret == ESP_ERR_TIMEOUT) {
                    ESP_LOGI(TAG, "I2C设备在线: 0x%02X", addr);
                    devices_found++;
                }
                i2c_master_bus_rm_device(dev_handle);
            }
        }

        ESP_LOGI(TAG, "I2C scan completed. Found %d devices", devices_found);

        if (devices_found == 0) {
            ESP_LOGW(TAG, "No I2C devices found. Check hardware connections.");
        }
    }
    // ==============================================================================

    // VEML7700环境光传感器初始化
    // ==============================================================================
    void InitializeLightSensor() {
        ESP_LOGI(TAG, "初始化VEML7700环境光传感器...");

        light_sensor_ = new VEML7700(codec_i2c_bus_);
        esp_err_t ret = light_sensor_->Init();
        if (ret != ESP_OK) {
            ESP_LOGW(TAG, "VEML7700初始化失败，传感器可能未连接");
            delete light_sensor_;
            light_sensor_ = nullptr;
            light_sensor_initialized_ = false;
            return;
        }

        light_sensor_initialized_ = true;

        // 使用自动量程读取一次数据验证传感器工作正常
        veml7700_auto_data_t als_result, white_result;
        ret = light_sensor_->ReadAutoAll(&als_result, &white_result);
        if (ret == ESP_OK) {
            ESP_LOGI(TAG, "VEML7700 验证成功(自动量程) - ALS: %u (%.2f lux, gain=%d, it=%d), White: %u (%.2f lux)",
                     als_result.raw, als_result.lux, als_result.gain, als_result.it,
                     white_result.raw, white_result.lux);
        }
    }
    // ==============================================================================

    // 双击KEY4：录入本命石头光源信息到NVS
    // 存储光谱比值（ALS/White）和亮度等级，而非绝对Lux值
    // ==============================================================================
    void RegisterMyStone() {
        ESP_LOGI(TAG, "=== 开始录入本命石头 ===");

        if (!light_sensor_initialized_ || !light_sensor_) {
            ESP_LOGE(TAG, "VEML7700传感器未初始化，无法录入石头");
            return;
        }

        // 等待2秒，让用户将石头放到传感器上
        ESP_LOGI(TAG, "请将石头放到传感器上方，2秒后开始检测...");
        vTaskDelay(pdMS_TO_TICKS(2000));

        // 连续采样3次取中位数，过滤偶发异常
        ESP_LOGI(TAG, "正在测量石头光源信息（3次采样取中位数）...");
        float als_samples[3], white_samples[3];
        for (int i = 0; i < 3; i++) {
            veml7700_auto_data_t als_r, white_r;
            esp_err_t ret = light_sensor_->ReadAutoAll(&als_r, &white_r);
            if (ret != ESP_OK) {
                ESP_LOGE(TAG, "石头光源测量失败(第%d次): %s", i + 1, esp_err_to_name(ret));
                return;
            }
            als_samples[i] = als_r.lux;
            white_samples[i] = white_r.lux;
            ESP_LOGI(TAG, "  采样%d: ALS=%.2f lux, White=%.2f lux", i + 1, als_r.lux, white_r.lux);
            if (i < 2) vTaskDelay(pdMS_TO_TICKS(200));
        }

        // 简单排序取中位数
        for (int i = 0; i < 2; i++) {
            for (int j = i + 1; j < 3; j++) {
                if (als_samples[i] > als_samples[j]) { float t = als_samples[i]; als_samples[i] = als_samples[j]; als_samples[j] = t; }
                if (white_samples[i] > white_samples[j]) { float t = white_samples[i]; white_samples[i] = white_samples[j]; white_samples[j] = t; }
            }
        }
        float als_lux = als_samples[1];   // 中位数
        float white_lux = white_samples[1];

        // 计算光谱比值（石头的固有光学特征）
        float ratio = 0.0f;
        if (white_lux > 0.01f) {
            ratio = als_lux / white_lux;
        }

        int brightness_level = GetBrightnessLevel(als_lux);

        // 比值乘10000转整数存储（保留4位小数精度）
        int32_t ratio_x10000 = (int32_t)(ratio * 10000.0f);
        int32_t als_lux_x100 = (int32_t)(als_lux * 100.0f);
        int32_t white_lux_x100 = (int32_t)(white_lux * 100.0f);

        // 存入NVS
        Settings stone_settings(kStoneNvsNamespace, true);
        stone_settings.SetInt("ratio", ratio_x10000);
        stone_settings.SetInt("als_lux", als_lux_x100);
        stone_settings.SetInt("white_lux", white_lux_x100);
        stone_settings.SetInt("br_level", brightness_level);
        stone_settings.SetInt("valid", 1);
        stone_settings.SetInt("ratio_th", kDefaultRatioThresholdPercent);
        stone_settings.SetInt("lux_th", kDefaultLuxThresholdPercent);

        ESP_LOGI(TAG, "========================================");
        ESP_LOGI(TAG, "  本命石头录入成功！");
        ESP_LOGI(TAG, "  ALS:   %.2f lux", als_lux);
        ESP_LOGI(TAG, "  White: %.2f lux", white_lux);
        ESP_LOGI(TAG, "  光谱比值(ALS/White): %.4f", ratio);
        ESP_LOGI(TAG, "  亮度等级: %d - %s", brightness_level, GetBrightnessLevelName(brightness_level));
        ESP_LOGI(TAG, "  比值阈值: %d%%, 亮度容差阈值: %d%%", kDefaultRatioThresholdPercent, kDefaultLuxThresholdPercent);
        ESP_LOGI(TAG, "========================================");
    }
    // ==============================================================================

    // 长按KEY4 2秒：识别他人石头并与本命石比对
    // 双维度匹配：光谱比值（石头固有属性）+ 亮度等级（环境相近性）
    // ==============================================================================
    void MatchStone() {
        ESP_LOGI(TAG, "=== 开始石头匹配 ===");

        if (!light_sensor_initialized_ || !light_sensor_) {
            ESP_LOGE(TAG, "VEML7700传感器未初始化，无法匹配");
            return;
        }

        // 读取NVS中的本命石数据
        Settings stone_settings(kStoneNvsNamespace, false);
        int32_t valid = stone_settings.GetInt("valid", 0);
        if (valid != 1) {
            ESP_LOGW(TAG, "尚未录入本命石头！请先双击KEY4录入本命石");
            return;
        }

        int32_t my_ratio_x10000 = stone_settings.GetInt("ratio", 0);
        int32_t my_als_lux_x100 = stone_settings.GetInt("als_lux", 0);
        int32_t my_white_lux_x100 = stone_settings.GetInt("white_lux", 0);
        int32_t my_br_level = stone_settings.GetInt("br_level", 0);
        int32_t ratio_threshold = stone_settings.GetInt("ratio_th", kDefaultRatioThresholdPercent);
        int32_t lux_threshold = stone_settings.GetInt("lux_th", kDefaultLuxThresholdPercent);

        float my_ratio = (float)my_ratio_x10000 / 10000.0f;
        float my_als_lux = (float)my_als_lux_x100 / 100.0f;
        float my_white_lux = (float)my_white_lux_x100 / 100.0f;

        ESP_LOGI(TAG, "本命石数据 - 比值: %.4f, ALS: %.2f, White: %.2f, 亮度等级: %d(%s)",
                 my_ratio, my_als_lux, my_white_lux, (int)my_br_level, GetBrightnessLevelName(my_br_level));

        // 等待2秒，让用户将对方石头放到传感器上
        ESP_LOGI(TAG, "请将对方石头放到传感器上方，2秒后开始检测...");
        vTaskDelay(pdMS_TO_TICKS(2000));

        // 连续采样3次取中位数
        ESP_LOGI(TAG, "正在测量对方石头光源信息（3次采样取中位数）...");
        float als_samples[3], white_samples[3];
        for (int i = 0; i < 3; i++) {
            veml7700_auto_data_t als_r, white_r;
            esp_err_t ret = light_sensor_->ReadAutoAll(&als_r, &white_r);
            if (ret != ESP_OK) {
                ESP_LOGE(TAG, "对方石头测量失败(第%d次): %s", i + 1, esp_err_to_name(ret));
                return;
            }
            als_samples[i] = als_r.lux;
            white_samples[i] = white_r.lux;
            ESP_LOGI(TAG, "  采样%d: ALS=%.2f lux, White=%.2f lux", i + 1, als_r.lux, white_r.lux);
            if (i < 2) vTaskDelay(pdMS_TO_TICKS(200));
        }

        // 排序取中位数
        for (int i = 0; i < 2; i++) {
            for (int j = i + 1; j < 3; j++) {
                if (als_samples[i] > als_samples[j]) { float t = als_samples[i]; als_samples[i] = als_samples[j]; als_samples[j] = t; }
                if (white_samples[i] > white_samples[j]) { float t = white_samples[i]; white_samples[i] = white_samples[j]; white_samples[j] = t; }
            }
        }
        float other_als = als_samples[1];
        float other_white = white_samples[1];

        // 计算对方石头的光谱比值
        float other_ratio = 0.0f;
        if (other_white > 0.01f) {
            other_ratio = other_als / other_white;
        }
        int other_br_level = GetBrightnessLevel(other_als);

        ESP_LOGI(TAG, "对方石数据 - 比值: %.4f, ALS: %.2f, White: %.2f, 亮度等级: %d(%s)",
                 other_ratio, other_als, other_white, other_br_level, GetBrightnessLevelName(other_br_level));

        // ======== 维度1：光谱比值匹配（石头固有光学特征）========
        float ratio_max = (my_ratio > other_ratio) ? my_ratio : other_ratio;
        float ratio_diff_percent = 0.0f;
        if (ratio_max > 0.001f) {
            float ratio_diff = (my_ratio > other_ratio) ? (my_ratio - other_ratio) : (other_ratio - my_ratio);
            ratio_diff_percent = (ratio_diff / ratio_max) * 100.0f;
        }
        bool ratio_match = (ratio_diff_percent <= (float)ratio_threshold);

        // ======== 维度2：亮度等级匹配（环境相近性）========
        // 允许相差1个等级（如"暗"和"中"可以匹配）
        int level_diff = (my_br_level > other_br_level) ? (my_br_level - other_br_level) : (other_br_level - my_br_level);
        bool level_match = (level_diff <= 1);

        // 如果亮度等级相同，再用Lux值做细粒度容差检查
        float lux_diff_percent = 0.0f;
        float lux_max = (my_als_lux > other_als) ? my_als_lux : other_als;
        if (lux_max > 0.01f) {
            float lux_diff = (my_als_lux > other_als) ? (my_als_lux - other_als) : (other_als - my_als_lux);
            lux_diff_percent = (lux_diff / lux_max) * 100.0f;
        }

        // 综合判定：比值匹配 AND 亮度等级相近
        bool overall_match = ratio_match && level_match;

        ESP_LOGI(TAG, "========================================");
        ESP_LOGI(TAG, "  匹配分析:");
        ESP_LOGI(TAG, "  [维度1] 光谱比值: 本命=%.4f 对方=%.4f 差异=%.1f%% %s (阈值 %" PRId32 "%%)",
                 my_ratio, other_ratio, ratio_diff_percent, ratio_match ? "PASS" : "FAIL", ratio_threshold);
        ESP_LOGI(TAG, "  [维度2] 亮度等级: 本命=%d(%s) 对方=%d(%s) 相差%d级 %s",
                 (int)my_br_level, GetBrightnessLevelName(my_br_level),
                 other_br_level, GetBrightnessLevelName(other_br_level),
                 level_diff, level_match ? "PASS" : "FAIL");
        ESP_LOGI(TAG, "  [参考] ALS亮度差异: %.1f%%（仅参考，不参与判定）", lux_diff_percent);
        ESP_LOGI(TAG, "  ----------------------------------------");
        if (overall_match) {
            ESP_LOGI(TAG, "  *** 同频匹配成功！交友成功！ ***");
        } else {
            if (!ratio_match) {
                ESP_LOGI(TAG, "  匹配失败：光谱特征不同（石头材质/颜色差异）");
            }
            if (!level_match) {
                ESP_LOGI(TAG, "  匹配失败：光照环境差异过大（相差%d个等级）", level_diff);
            }
        }
        ESP_LOGI(TAG, "========================================");
    }
    // ==============================================================================

    // 按钮初始化 函数
    void InitializeButtons() {
        ESP_LOGI(TAG, "初始化按钮...");// 初始化按钮...

        // BOOT按键单击事件 - 用于WiFi重置和触摸解锁
        boot_button_.OnClick([this]() {
            static uint32_t last_click_time = 0;
            uint32_t current_time = esp_timer_get_time() / 1000; // 当前时间(毫秒)

            // 防抖动处理：如果距离上次点击时间太短(小于500毫秒)，则忽略此次点击
            if (last_click_time > 0 && current_time - last_click_time < 500) {
                ESP_LOGI(TAG, "BOOT 按钮点击过于频繁，忽略此次点击");// BOOT 按钮点击过于频繁，忽略此次点击
                return;
            }

            last_click_time = current_time;
            ESP_LOGI(TAG, "BOOT button clicked");

            // 创建一个单独的任务来处理触摸解锁，避免在按钮回调中执行复杂操作
            xTaskCreate([](void* arg) {
                MovecallMojiESP32S3* board = static_cast<MovecallMojiESP32S3*>(arg);
                if (board) {
                    board->UnlockTouchTask();
                }
                vTaskDelete(NULL);
            }, "boot_unlock", 4096, this, 5, NULL);

            // 获取当前应用实例和状态
            auto &app = Application::GetInstance();
            auto current_state = app.GetDeviceState();

            // RTC 空闲软退出状态下，BOOT 单击优先唤醒并重连 RTC。
            if (app.IsHibernating()) {
                ESP_LOGI(TAG, "🔵 BOOT in hibernate → 唤醒（重连 RTC）");
                xTaskCreate([](void* arg) {
                    (void)arg;
                    Application::GetInstance().WakeFromHibernate();
                    vTaskDelete(NULL);
                }, "wake_hibernate", 4096, NULL, 5, NULL);
                return;
            }

            // 检查是否处于BLE配网状态，如果是则屏蔽按键响应（生产测试模式下除外）
            auto* wifi_board = dynamic_cast<WifiBoard*>(this);
            if (wifi_board && wifi_board->IsBleProvisioningActive() && !production_test_mode_) {
                ESP_LOGI(Pro_TAG, "🔵 当前为蓝牙配网模式,[BOOT按键]被按下，长按BOOT按键5秒可进入生产测试模式!");// 生产测试打印
                ESP_LOGI("BluetoothMAC", "Bluetooth MAC Address: %s", SystemInfo::GetBleMacAddress().c_str());// 生产测试打印
                ESP_LOGI(Pro_TAG, "当前电量值: %" PRIu32 "%%", battery_level_);// 生产测试打印
                return;
            }

            // 如果处于生产测试模式，记录按键测试并播放音频-生产测试模式 新增代码
            // ==============================================================================
            if (production_test_mode_) {
                ESP_LOGI(Pro_TAG, "🔧 生产测试模式：BOOT按键已被按下!");// 生产测试打印
                ESP_LOGI(Pro_TAG, "当前电量值: %" PRIu32 "%%", battery_level_);// 生产测试打印
                ESP_LOGI("BluetoothMAC", "Bluetooth MAC Address: %s", SystemInfo::GetBleMacAddress().c_str());// 生产测试打印
                // 播放BOOT按键测试音频
                auto& app = Application::GetInstance();

                // 确保音频输出已启用
                auto* codec = GetAudioCodec();// 获取音频编解码器
                if (codec) {
                    codec->EnableOutput(true);
                    ESP_LOGI(TAG, "🔊 测试模式：已启用音频输出");
                }

                // 播放测试音频
                // app.PlaySound(Lang::Sounds::P3_PUTDOWN_BOOT);
                app.PlaySound(Lang::Sounds::P3_1);

                ESP_LOGI(TAG, "🎵 测试模式：开始播放BOOT按键测试音频");

                // 改进的音频播放完成等待逻辑
                int wait_count = 0;
                const int max_wait_cycles = 100; // 最多等待10秒 (100 * 100ms)

                // 等待音频队列开始处理（非空状态）
                while (app.IsAudioQueueEmpty() && wait_count < 20) { // 最多等待2秒音频开始
                    vTaskDelay(pdMS_TO_TICKS(100));
                    wait_count++;
                }

                if (!app.IsAudioQueueEmpty()) {
                    ESP_LOGI(Pro_TAG, "🎵 测试模式：音频开始播放，等待播放完成"); // 生产测试打印
                    wait_count = 0;

                    // 等待音频播放完成（队列变空）
                    while (!app.IsAudioQueueEmpty() && wait_count < max_wait_cycles) {
                        vTaskDelay(pdMS_TO_TICKS(100));
                        wait_count++;
                    }

                    if (app.IsAudioQueueEmpty()) {
                        ESP_LOGI(Pro_TAG, "✅ 测试模式：音频播放完成");// 生产测试打印
                    } else {
                        ESP_LOGW(Pro_TAG, "⚠️ 测试模式：音频播放超时，强制清空队列");
                        app.ClearAudioQueue();
                    }
                } else {
                    ESP_LOGW(Pro_TAG, "⚠️ 测试模式：音频未能开始播放");// 生产测试打印
                }

                // 额外等待100ms确保音频完全结束
                vTaskDelay(pdMS_TO_TICKS(100));

                return;
            }
            // ==============================================================================

            if (current_state == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {

                // 设备启动且wifi未连接时，重置wifi配置
                ESP_LOGI(TAG, "🔄 BOOT按键触发：设备状态=%d，WiFi连接状态=%s", current_state, WifiStation::GetInstance().IsConnected() ? "已连接" : "未连接");
                ESP_LOGI(TAG, "🔄 开始重置WiFi配置，清除已保存的WiFi凭据");

                // 清除已保存的WiFi配置，阻止自动连接
                esp_wifi_restore();
                ESP_LOGI(TAG, "✅ 已清除所有WiFi凭据，设备将进入配网模式");

                ResetWifiConfiguration();//进入Blufi配网模式
                // 唤醒设备，防止立即进入睡眠
                power_save_timer_->WakeUp();
            }
            else {
                // 检查是否在BLE配网模式下，如果是则屏蔽单独的BOOT按键功能
                auto* wifi_board = dynamic_cast<WifiBoard*>(this);
                if (wifi_board && wifi_board->IsBleProvisioningActive() && !production_test_mode_) {
                    ESP_LOGI(TAG, "🔵 BLE配网模式下，屏蔽单独BOOT按键功能");
                    return;
                }

                ESP_LOGI(TAG, "当前设备状态: %d", current_state);

                if (current_state == kDeviceStateIdle) {
                    // 如果当前是待命状态，切换到聆听状态
                    ESP_LOGI(TAG, "从待命状态切换到聆听状态");

                    auto codec = GetAudioCodec();   // 🔧 修复：强制重新初始化音频输出，确保硬件状态正确
                    ESP_LOGI(TAG, "强制重新初始化音频输出");
                    codec->EnableOutput(false);    // 先关闭音频输出
                    vTaskDelay(pdMS_TO_TICKS(50)); // 短暂延迟让硬件复位
                    codec->EnableOutput(true);     // 再开启，强制硬件重新初始化

                    // 🔧 检查音频资源是否可用
                    if (codec->output_enabled()) {
                        ESP_LOGI(TAG, "播放提示音：卡卡在呢");
                        app.ResetDecoder();     // 🔧 关键修复：重置解码器状态，清除残留
                        // PlaySound(Lang::Sounds::P3_KAKAZAINNE);  原有蜡笔小新 音色播报
                        if(strcmp(CONFIG_DEVICE_ROLE, "KAKA") == 0){
                            app.PlaySound(Lang::Sounds::P3_KAKA_ZAINNE);
                        }
                        else if(strcmp(CONFIG_DEVICE_ROLE, "RTC_Test") == 0){
                            app.PlaySound(Lang::Sounds::P3_LALA_ZAINNE);
                        }

                        // 🔧 修复：使用改进的等待逻辑，确保音频真正播放完成
                        ESP_LOGI(TAG, "等待音频播放完成...");
                        vTaskDelay(pdMS_TO_TICKS(100));  // 给音频足够的时间开始播放

                        // 等待音频队列清空 + 额外缓冲时间确保I2S硬件完成输出
                        int timeout_count = 0;
                        const int max_timeout = 150; // 3秒超时

                        while (timeout_count < max_timeout) {
                            if (app.IsAudioQueueEmpty()) {
                                // 队列清空后，再等待500ms确保I2S硬件完成输出
                                ESP_LOGI(TAG, "音频队列已清空，等待硬件输出完成...");
                                vTaskDelay(pdMS_TO_TICKS(500));
                                ESP_LOGI(TAG, "音频播放完成");
                                break;
                            }
                            vTaskDelay(pdMS_TO_TICKS(20));
                            timeout_count++;
                        }

                        if (timeout_count >= max_timeout) {
                            ESP_LOGW(TAG, "等待音频播放超时，继续状态切换");
                        }
                    } else {
                        ESP_LOGW(TAG, "音频输出无法启用，跳过提示音播放");
                    }

                    app.ToggleChatState(); // 切换到聆听状态
                } else if (current_state == kDeviceStateListening) {
                    // 如果当前是聆听状态，切换到待命状态
                    ESP_LOGI(TAG, "🔵 BOOT button pressed in Listening state - switching to idle");
                    ESP_LOGI(TAG, "从聆听状态切换到待命状态");
                    app.ToggleChatState(); // 切换到待命状态
                } else if (current_state == kDeviceStateSpeaking) {
                    // 如果当前是说话状态，终止说话并切换到待命状态
                    ESP_LOGI(TAG, "🔴 BOOT button pressed in Speaking state - initiating abort sequence");
                    ESP_LOGI(TAG, "从说话状态切换到聆听状态");
                    //app.AbortSpeakingAndReturnToIdle(); // 专门处理从说话状态到idle状态的转换
                    app.AbortSpeakingAndReturnToListening(); // 专门处理从说话状态到聆听状态的转换
                } 
                else if(current_state == kDeviceStateDialog) {
                    // Application::GetInstance().ToggleChatState();// 切换对话状态
                    app.ToggleChatState(); // 切换对话状态
                }
                else {
                    // 其他状态下只唤醒设备
                    ESP_LOGI(TAG, "唤醒设备");
                    power_save_timer_->WakeUp();
                }
            }
        });

        // 配网模式下长按 BOOT 按键5秒进入 生产测试模式 新增代码
        // ==============================================================================
        // 添加BOOT按键长按事件处理 - 仅在配网模式下长按5秒进入测试模式
        boot_button_.OnLongPress([this]() {
            //ESP_LOGI(TAG, "🔧 BOOT button long pressed - checking if in provisioning mode");

            // 检查是否处于BLE配网状态，只有在配网模式下才允许进入测试模式
            auto* wifi_board = dynamic_cast<WifiBoard*>(this);
            if (wifi_board && wifi_board->IsBleProvisioningActive()) {
                // ESP_LOGI(TAG, "🔧 设备正在进行BLE配网，长按5秒进入生产测试模式");
                EnterProductionTestMode();
            } else {
                ESP_LOGI(TAG, "🔵 非配网模式下，BOOT长按被屏蔽，无法进入测试模式");
                return;
            }
        });
        // ==============================================================================

        ESP_LOGI(TAG, "Boot button initialized on GPIO%d", BOOT_BUTTON_GPIO);

        volume_up_button_.OnClick([this]() {
            ESP_LOGI(TAG, "Volume up button clicked!");

            // 检查是否处于BLE配网状态，如果是则屏蔽按键响应（生产测试模式下除外）
            auto* wifi_board = dynamic_cast<WifiBoard*>(this);
            if (wifi_board && wifi_board->IsBleProvisioningActive() && !production_test_mode_) {
                ESP_LOGI(TAG, "🔵 设备正在进行BLE配网，音量加按键被屏蔽");
                return;
            }

            // 如果处于生产测试模式，记录按键测试
            if (production_test_mode_) {
                ESP_LOGI(TAG, "🔧 生产测试模式：音量加按键点击测试");
                return;
            }

            auto codec = GetAudioCodec();
            // 将当前硬件音量转换为用户音量，增加10%，再转换回硬件音量
            auto current_hw_volume = codec->output_volume();
            auto current_user_volume = HARDWARE_TO_USER_VOLUME(current_hw_volume);
            auto new_user_volume = current_user_volume + 10;
            if (new_user_volume > 100) {
                new_user_volume = 100;
            }
            auto new_hw_volume = USER_TO_HARDWARE_VOLUME(new_user_volume);
            codec->SetOutputVolume(new_hw_volume);
            ESP_LOGI(TAG, "Volume up: User %d%% -> Hardware %d%% (Range: %d%%-%d%%)", 
                     new_user_volume, new_hw_volume, MIN_VOLUME_PERCENT, MAX_VOLUME_PERCENT);
        });

        volume_up_button_.OnLongPress([this]() {
            ESP_LOGI(TAG, "Volume up button long pressed!");
            auto codec = GetAudioCodec();
            // 设置为用户音量100%，对应硬件最高音量
            auto hw_volume = USER_TO_HARDWARE_VOLUME(100);
            codec->SetOutputVolume(hw_volume);
            ESP_LOGI(TAG, "Volume set to maximum: User 100%% -> Hardware %d%%", hw_volume);
        });
        ESP_LOGI(TAG, "Volume up button initialized on GPIO%d", VOLUME_UP_BUTTON_GPIO);

        volume_down_button_.OnClick([this]() {
            ESP_LOGI(TAG, "Volume down button clicked!");

            // 检查是否处于BLE配网状态，如果是则屏蔽按键响应（生产测试模式下除外）
            auto* wifi_board = dynamic_cast<WifiBoard*>(this);
            if (wifi_board && wifi_board->IsBleProvisioningActive() && !production_test_mode_) {
                ESP_LOGI(TAG, "🔵 设备正在进行BLE配网，音量减按键被屏蔽");
                return;
            }

            // 如果处于生产测试模式，记录按键测试
            if (production_test_mode_) {
                ESP_LOGI(TAG, "🔧 生产测试模式：音量减按键点击测试");
                return;
            }

            auto codec = GetAudioCodec();
            // 将当前硬件音量转换为用户音量，减少10%，再转换回硬件音量
            auto current_hw_volume = codec->output_volume();
            auto current_user_volume = HARDWARE_TO_USER_VOLUME(current_hw_volume);
            auto new_user_volume = current_user_volume - 10;
            if (new_user_volume < 0) {
                new_user_volume = 0;
            }
            auto new_hw_volume = USER_TO_HARDWARE_VOLUME(new_user_volume);
            codec->SetOutputVolume(new_hw_volume);
            ESP_LOGI(TAG, "Volume down: User %d%% -> Hardware %d%% (Range: %d%%-%d%%)", 
                     new_user_volume, new_hw_volume, MIN_VOLUME_PERCENT, MAX_VOLUME_PERCENT);
        });

        volume_down_button_.OnLongPress([this]() {
            ESP_LOGI(TAG, "Volume down button long pressed!");
            auto codec = GetAudioCodec();
            // 设置为用户音量0%，对应硬件最低音量
            auto hw_volume = USER_TO_HARDWARE_VOLUME(0);
            codec->SetOutputVolume(hw_volume);
            ESP_LOGI(TAG, "Volume set to minimum: User 0%% -> Hardware %d%%", hw_volume);
        });
        ESP_LOGI(TAG, "Volume down button initialized on GPIO%d", VOLUME_DOWN_BUTTON_GPIO);
    }

    void InitializeBatteryMonitor() {
        ESP_LOGI(TAG, "Initializing battery monitor...");

        // 初始化 ADC
        adc_oneshot_unit_init_cfg_t init_config = {
            .unit_id = BATTERY_ADC_UNIT,
            .ulp_mode = ADC_ULP_MODE_DISABLE,
        };
        ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config, &adc_handle_));

        adc_oneshot_chan_cfg_t chan_config = {
            .atten = ADC_ATTEN_DB_12,  // 12dB衰减，测量范围0-3.3V
            .bitwidth = ADC_BITWIDTH_12,  // 12位精度
        };
        ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle_, BATTERY_ADC_CHANNEL, &chan_config));

         // 🔧 添加ADC校准
        adc_cali_curve_fitting_config_t cali_config = {
            .unit_id = BATTERY_ADC_UNIT,
            .atten = ADC_ATTEN_DB_12,
            .bitwidth = ADC_BITWIDTH_12,
        };
        ESP_ERROR_CHECK(adc_cali_create_scheme_curve_fitting(&cali_config, &adc_cali_handle_));
        ESP_LOGI(TAG, "ADC calibration initialized");

        // 创建电池电量检查定时器
        esp_timer_create_args_t timer_args = {
            .callback = [](void* arg) {
                MovecallMojiESP32S3* self = static_cast<MovecallMojiESP32S3*>(arg);
                self->CheckBatteryStatus();
            },
            .arg = this,
            .dispatch_method = ESP_TIMER_TASK,
            .name = "battery_check_timer",
            .skip_unhandled_events = true,
        };
        ESP_ERROR_CHECK(esp_timer_create(&timer_args, &battery_timer_handle_));
        ESP_ERROR_CHECK(esp_timer_start_periodic(battery_timer_handle_, 1000000)); // 每秒检查一次

        ESP_LOGI(TAG, "电池状态监控已初始化，GPIO:%d", BATTERY_ADC_GPIO);// 电池状态监控已初始化，GPIO:%d
    }

    // 初始化IMU传感器（QMI8658A 陀螺仪）
    void InitializeImuSensor() {

        auto& app = Application::GetInstance();// 获取当前应用状态
        auto current_state = app.GetDeviceState();// 获取当前设备状态
        
        // 在生产测试模式下或在对话状态下启用姿态传感器
    if (!production_test_mode_ && current_state != kDeviceStateDialog) {
        ESP_LOGI(TAG, "非生产测试模式且不在对话状态，姿态传感器业务已禁用以节约资源");
        imu_initialized_ = false;// 非生产测试模式且不在对话状态，姿态传感器业务已禁用以节约资源
        imu_sensor_ = nullptr;// 姿态传感器实例指针
        return;
    }
    
    const char* log_tag = production_test_mode_ ? Pro_TAG : TAG;
    
    if (current_state == kDeviceStateDialog) {
        ESP_LOGI(log_tag, "对话状态下启用姿态传感器");
    } else {
        ESP_LOGI(log_tag, "生产测试模式下启用姿态传感器");// 生产测试模式下启用姿态传感器
    }

    ESP_LOGI(log_tag, "初始化IMU传感器 QMI8658A...");// 初始化IMU传感器（QMI8658A 陀螺仪）

        // 初始化状态为false，确保系统在IMU不可用时仍能正常运行
        imu_initialized_ = false;// 初始化状态为false，确保系统在IMU不可用时仍能正常运行
        imu_sensor_ = nullptr;// 姿态传感器实例指针

        if (!codec_i2c_bus_) {
        ESP_LOGI(log_tag, "I2C总线未初始化，IMU传感器将被禁用");// I2C总线未初始化，IMU传感器将被禁用
        ESP_LOGI(log_tag, "系统将继续运行，不启用运动检测功能");// 系统将继续运行，不启用运动检测功能
        return;
    }

    ESP_LOGI(log_tag, "I2C总线已初始化，创建IMU传感器实例");// I2C总线已初始化，创建IMU传感器实例
    ESP_LOGI(log_tag, "使用I2C地址: 0x6A");// 使用I2C地址: 0x6A

        
        vTaskDelay(pdMS_TO_TICKS(100));// 添加延迟，确保I2C总线完全稳定

        // 创建IMU传感器实例 (使用I2C地址0x6A)
        uint8_t working_address = 0x6A;
        try {
            imu_sensor_ = new QMI8658A(codec_i2c_bus_, 0x6A);
            ESP_LOGI(log_tag, "IMU传感器实例创建成功，I2C地址: 0x6A");// IMU传感器实例创建成功，I2C地址: 0x6A

            // 测试I2C通信 - 尝试读取芯片ID
            ESP_LOGI(log_tag, "测试I2C通信，读取QMI8658A芯片ID...");// 测试I2C通信，读取QMI8658A芯片ID...
            uint8_t chip_id = imu_sensor_->GetChipId();
            ESP_LOGI(log_tag, "读取到的芯片ID: 0x%02X (预期: 0x05)", chip_id);// 读取到的芯片ID: 0x%02X (预期: 0x05)

            if (chip_id == 0xFF) {
                ESP_LOGI(log_tag, "I2C通信失败 - 读取到的芯片ID为0xFF");// I2C通信失败 - 读取到的芯片ID为0xFF
                ESP_LOGI(log_tag, "尝试备用I2C地址 0x6B...");// 尝试备用I2C地址 0x6B...

                // 尝试使用备用地址0x6B
                delete imu_sensor_;
                imu_sensor_ = new QMI8658A(codec_i2c_bus_, 0x6B);
                working_address = 0x6B;
                chip_id = imu_sensor_->GetChipId();
                ESP_LOGI(log_tag, "读取到的芯片ID (0x6B): 0x%02X", chip_id);// 读取到的芯片ID (0x6B): 0x%02X

                if (chip_id == 0xFF) {
                    ESP_LOGI(log_tag, "I2C通信失败 - 读取到的芯片ID (0x6B)为0xFF");// I2C通信失败 - 读取到的芯片ID (0x6B)为0xFF
                    ESP_LOGI(log_tag, "可能原因：1) 硬件连接问题 2) 错误的I2C引脚 3) 电源供应问题");// 可能原因：1) 硬件连接问题 2) 错误的I2C引脚 3) 电源供应问题
                    delete imu_sensor_;
                    imu_sensor_ = nullptr;
                    return;
                }
            }

            if (chip_id != 0x05) {
                ESP_LOGI(log_tag, "读取到的芯片ID (0x%02X)与预期的0x05不符", chip_id);// 读取到的芯片ID (0x6A)与预期的0x05不符
                ESP_LOGI(log_tag, "这可能不是QMI8658A传感器，或存在通信问题");// 这可能不是QMI8658A传感器，或存在通信问题
                // 继续尝试初始化，可能是兼容的传感器
            }

            ESP_LOGI(log_tag, "成功建立I2C通信，使用地址: 0x%02X", working_address);// 成功建立I2C通信，使用地址: 0x6A

        } catch (...) {
            ESP_LOGI(log_tag, "创建IMU传感器实例时发生异常");// 创建IMU传感器实例时发生异常
            ESP_LOGI(log_tag, "系统将继续运行，不启用运动检测功能");// 系统将继续运行，不启用运动检测功能
            imu_sensor_ = nullptr;
            return;
        }

        // 配置传感器参数
        qmi8658a_config_t config = {
            .acc_range = QMI8658A_ACC_RANGE_4G,   // 加速度计量程±4g
            .gyro_range = QMI8658A_GYRO_RANGE_512DPS, // 陀螺仪量程±512dps
            .acc_odr = QMI8658A_ODR_125HZ,        // 加速度计采样率125Hz
            .gyro_odr = QMI8658A_ODR_125HZ,       // 陀螺仪采样率125Hz
            .mode = QMI8658A_MODE_DUAL,           // 同时启用加速度计和陀螺仪
            .enable_interrupt = false,            // 不启用中断
            .interrupt_pin = 0,                   // 中断引脚
            .auto_calibration = true,             // 启用自动校准
            .acc_offset = {0.0f, 0.0f, 0.0f},     // 加速度计偏移校准
            .gyro_offset = {0.0f, 0.0f, 0.0f}     // 陀螺仪偏移校准
        };

        ESP_LOGI(log_tag, "开始初始化IMU传感器...");// 开始初始化IMU传感器...

        // 初始化传感器 - 修复逻辑错误：QMI8658A_OK = 0 表示成功
        qmi8658a_error_t init_result = imu_sensor_->Initialize(&config);
        if (init_result == QMI8658A_OK) {
            imu_initialized_ = true;
            ESP_LOGI(log_tag, "QMI8658A传感器初始化成功");// QMI8658A传感器初始化成功
            
            if (config.auto_calibration) {
                qmi8658a_error_t calib_buf = imu_sensor_->StartBufferedReading(20);
                if (calib_buf == QMI8658A_OK) {
                    imu_sensor_->StartCalibration(6000);
                    bool running = false;
                    float progress = 0.0f;
                    do {
                        imu_sensor_->GetCalibrationStatus(&running, &progress);
                        vTaskDelay(pdMS_TO_TICKS(200));
                    } while (running);
                    qmi8658a_calibration_t calib;
                    imu_sensor_->GetCalibrationData(&calib);
                    imu_sensor_->ApplyCalibration(&calib);
                    imu_sensor_->StopBufferedReading();
                }
            }

            // 创建IMU数据读取定时器
            esp_timer_create_args_t timer_args = {
                .callback = [](void* arg) {
                    MovecallMojiESP32S3* self = static_cast<MovecallMojiESP32S3*>(arg);
                    self->ReadImuData();
                },
                .arg = this,
                .dispatch_method = ESP_TIMER_TASK,
                .name = "imu_read_timer",
                .skip_unhandled_events = true,
            };

            // 使用错误处理而不是ESP_ERROR_CHECK，避免系统崩溃
            esp_err_t err = esp_timer_create(&timer_args, &imu_timer_handle_);
            if (err != ESP_OK) {
                ESP_LOGI(log_tag, "创建IMU定时器失败: %s", esp_err_to_name(err));// 创建IMU定时器失败: %s
                ESP_LOGI(log_tag, "IMU传感器将被禁用，系统继续正常运行");// IMU传感器将被禁用，系统继续正常运行
                delete imu_sensor_;
                imu_sensor_ = nullptr;
                imu_initialized_ = false;
                return;
            }

            err = esp_timer_start_periodic(imu_timer_handle_, kImuReadInterval * 1000);
            if (err != ESP_OK) {
                ESP_LOGI(log_tag, "启动IMU定时器失败: %s", esp_err_to_name(err));// 启动IMU定时器失败: %s
                ESP_LOGI(log_tag, "IMU传感器将被禁用，系统继续正常运行");// IMU传感器将被禁用，系统继续正常运行
                esp_timer_delete(imu_timer_handle_);
                imu_timer_handle_ = nullptr;
                delete imu_sensor_;
                imu_sensor_ = nullptr;
                imu_initialized_ = false;
                return;
            }

            ESP_LOGI(log_tag, "IMU数据读取定时器已启动，采样间隔: %dms", kImuReadInterval);// IMU数据读取定时器已启动，采样间隔: %dms
        } else {
            ESP_LOGI(log_tag, "QMI8658A传感器初始化失败，错误码: %d", init_result);// QMI8658A传感器初始化失败，错误码: %d
            ESP_LOGI(TAG, "可能原因：1) 硬件连接问题 2) 传感器未响应 3) I2C通信错误");// 可能原因：1) 硬件连接问题 2) 传感器未响应 3) I2C通信错误
            ESP_LOGI(TAG, "系统将继续运行，不启用运动检测功能");// 系统将继续运行，不启用运动检测功能

            // 清理资源
            if (imu_sensor_) {
                delete imu_sensor_;// 清理IMU传感器实例
                imu_sensor_ = nullptr;// 重置IMU传感器指针
            }
            imu_initialized_ = false;// 重置IMU初始化状态
        }
    }

    // 读取IMU数据的方法
    void ReadImuData() {
        // ESP_LOGI(Pro_TAG, "读取IMU数据，是否初始化 =%d, 传感器指针 =%p", imu_initialized_, imu_sensor_);// 读取IMU数据，是否初始化：%d，传感器指针：%p
        
        if (!imu_initialized_ || !imu_sensor_) {
            ESP_LOGI(Pro_TAG, "IMU未初始化，跳过数据读取");// IMU未初始化，跳过数据读取
            return;
        }

        // 实现重试机制，最多尝试3次读取
        const int kMaxRetries = 3;
        const int kRetryDelayMs = 5; // 重试间隔5ms
        qmi8658a_error_t result = QMI8658A_ERROR_TIMEOUT;
        int retry_count = 0;
        
        do {
            // 读取传感器数据
            // ESP_LOGI(Pro_TAG, "尝试读取IMU传感器数据(第%d次尝试)", retry_count + 1);// 尝试读取IMU传感器数据
            result = imu_sensor_->ReadSensorData(&latest_imu_data_);
            
            if (result == QMI8658A_OK) {
                // ESP_LOGI(Pro_TAG, "成功读取IMU数据，正在处理...");// 成功读取IMU数据，正在处理...
                // 可以在这里添加数据处理逻辑
                // 例如：检测姿态变化、计算角度等
                ProcessImuData(&latest_imu_data_);
                return; // 读取成功，直接返回
            } else if (result == QMI8658A_ERROR_DATA_NOT_READY && retry_count < kMaxRetries - 1) {
                // 数据未就绪但还有重试次数，等待后重试
                // ESP_LOGI(Pro_TAG, "IMU数据未就绪，%dms后重试(剩余%d次)", kRetryDelayMs, kMaxRetries - retry_count - 1);
                esp_rom_delay_us(kRetryDelayMs * 1000); // 延时
                retry_count++;
            } else {
                // 其他错误或重试次数用尽
                ESP_LOGI(Pro_TAG, "读取IMU数据失败，错误码 = %d，已尝试%d次", result, retry_count + 1);
                break;
            }
        } while (retry_count < kMaxRetries);
        
        // 如果执行到这里，说明所有尝试都失败了
        ESP_LOGI(Pro_TAG, "所有尝试都失败，放弃本次IMU数据读取");// 读取IMU数据失败，错误码 = %d
    }

    // 处理IMU数据的方法
    void ProcessImuData(const qmi8658a_data_t* data) {
        // 处理IMU数据的逻辑
        // 可以根据需要实现姿态检测、运动检测等功能
        // 示例：检测是否有显著的加速度变化（可能表示设备被移动）
        float accel_magnitude = sqrt(data->acc_x * data->acc_x + 
                                   data->acc_y * data->acc_y + 
                                   data->acc_z * data->acc_z);

        // 如果加速度幅值超过阈值，可能需要唤醒设备或触发某些功能
        const float MOTION_THRESHOLD = 1.5f; // g
        if (accel_magnitude > MOTION_THRESHOLD) {
            // 检测到运动，可以在这里添加相应的处理逻辑
            // ESP_LOGI(TAG, "Motion detected: accel_magnitude = %.2f g", accel_magnitude);// 检测到运动：加速度幅值 = %.2f g
            ESP_LOGI(Pro_TAG, "检测到运动: 加速度幅值 = %.2f g", accel_magnitude);// 检测到运动：加速度幅值 = %.2f g
        }

        // 更新IMU IoT设备的数据
        if (imu_thing_) {
            imu_thing_->UpdateData(*data);
        }

        // // 记录详细的传感器数据（调试用）
        // ESP_LOGI(TAG, "IMU Data - Accel: (%.2f, %.2f, %.2f) g, Gyro: (%.2f, %.2f, %.2f) dps, Temp: %.1f°C",
        //         data->acc_x, data->acc_y, data->acc_z,
        //         data->gyro_x, data->gyro_y, data->gyro_z,
        //         data->temperature);
        // 记录详细的传感器数据（调试用）
        ESP_LOGI(Pro_TAG, "IMU Data - Accel: (%.2f, %.2f, %.2f) g, Gyro: (%.2f, %.2f, %.2f) dps, Temp: %.1f°C",
                data->acc_x, data->acc_y, data->acc_z,
                data->gyro_x, data->gyro_y, data->gyro_z,
                data->temperature);
    }

    // 检查电池状态的方法
    void CheckBatteryStatus() {
        // 如果电池电量数据不足，则读取电池电量数据
        if (adc_values_.size() < kBatteryAdcDataCount) {
            ReadBatteryAdcData();
            return;
        }

        // 检查是否启用设备状态上报（启动3秒后）
        if (!status_report_enabled_ && battery_ticks_ >= kStatusReportDelay) {
            status_report_enabled_ = true;
            ESP_LOGI(TAG, "📤 设备状态上报已启用，每%d秒上报一次", kStatusReportInterval);
        }

        // 如果电池电量数据充足，则每 kBatteryAdcInterval 个 tick 读取一次电池电量数据
        battery_ticks_++;
        if (battery_ticks_ % kBatteryAdcInterval == 0) {
            ReadBatteryAdcData();
            Application::GetInstance().Schedule([this]() {
                auto codec = GetAudioCodec();
                if (!codec) {
                    return;
                }
                // 如果电池电量低于25%，则将输出音量设置为0（静音）
                if (battery_level_ <= 25) {
                    if (codec->output_volume() != 0) {
                        codec->SetOutputVolumeRuntime(0);
                    }
                } else {
                Settings s("audio", false);
                int vol = s.GetInt("output_volume", AudioCodec::default_output_volume());
                if (vol <= 0) {
                    vol = AudioCodec::default_output_volume();
                }
                if (codec->output_volume() != vol) {
                    codec->SetOutputVolumeRuntime(vol);
                }
            }
            });
        }

        // 设备状态上报逻辑：每30秒上报一次（启动3秒后），配网模式下跳过
        if (status_report_enabled_) {
            status_report_ticks_++;
            if (status_report_ticks_ % kStatusReportInterval == 0) {
                auto& wifi_station = WifiStation::GetInstance();
                if (wifi_station.IsConnected()) {
                    ReportDeviceStatus(battery_level_);
                }
            }
        }

        battery_alert_ticks_++;
        auto& app = Application::GetInstance();
        auto state = app.GetDeviceState();

        // 检测RTC火山引擎连接状态（进入dialog状态说明RTC已连接、音频通道已打开）
        if (!rtc_online_ && state == kDeviceStateDialog) {
            rtc_online_ = true;
            ESP_LOGI(TAG, "🔗 检测到RTC火山引擎已连接");
        }

        if (battery_level_ <= 30 && battery_level_ > 25) {
            if (battery_alert_ticks_ % 10 == 0) {
                if (state == kDeviceStateIdle) {
                    app.Schedule([]() {
                        auto& board = Board::GetInstance();
                        auto codec = board.GetAudioCodec();
                        if (codec) { codec->EnableOutput(true); }
                        if (Application::GetInstance().IsAudioQueueEmpty()) {
                            if(strcmp(CONFIG_DEVICE_ROLE, "KAKA") == 0){
                                Application::GetInstance().PlaySound(Lang::Sounds::P3_KAKA_BATTERY_L);
                            } else if(strcmp(CONFIG_DEVICE_ROLE, "LALA") == 0){
                                Application::GetInstance().PlaySound(Lang::Sounds::P3_LALA_BATTERY_L);
                            }
                            ESP_LOGI(TAG, "电量值低警告音已播放！！");
                        }
                    });
                } else if (state == kDeviceStateSpeaking) {
                    app.Schedule([]() {
                        auto& a = Application::GetInstance();
                        a.SetLowBatteryTransition(true);
                        a.AbortSpeakingAndReturnToIdle();
                        vTaskDelay(pdMS_TO_TICKS(500));
                        a.ClearAudioQueue();
                        auto& board = Board::GetInstance();
                        auto codec = board.GetAudioCodec();
                        if (codec) { codec->EnableOutput(true); }
                        if(strcmp(CONFIG_DEVICE_ROLE, "KAKA") == 0){
                            a.PlaySound(Lang::Sounds::P3_KAKA_BATTERY_L);
                        } else if(strcmp(CONFIG_DEVICE_ROLE, "LALA") == 0){
                            a.PlaySound(Lang::Sounds::P3_LALA_BATTERY_L);
                        }
                        ESP_LOGI(TAG, "电量值低警告音已播放！！");
                        a.SetLowBatteryTransition(false);
                    });
                } else if (state == kDeviceStateListening) {
                    app.Schedule([]() {
                        auto& a = Application::GetInstance();
                        a.SetLowBatteryTransition(true);
                        a.SetDeviceState(kDeviceStateIdle);
                        vTaskDelay(pdMS_TO_TICKS(500));
                        auto& board = Board::GetInstance();
                        auto codec = board.GetAudioCodec();
                        if (codec) { codec->EnableOutput(true); }
                        if (a.IsAudioQueueEmpty()) {
                            if(strcmp(CONFIG_DEVICE_ROLE, "KAKA") == 0){
                                a.PlaySound(Lang::Sounds::P3_KAKA_BATTERY_L);
                            } else if(strcmp(CONFIG_DEVICE_ROLE, "LALA") == 0){
                                a.PlaySound(Lang::Sounds::P3_LALA_BATTERY_L);
                            }
                            ESP_LOGI(TAG, "电量值低警告音已播放！！");
                        }
                        a.SetLowBatteryTransition(false);
                    });
                }
            }
        }
    }

    void ReadBatteryAdcData() {
        std::vector<int> adc_samples;
        for (int i = 0; i < kBatteryAdcSampleCount; i++) {
            int adc_value;
            esp_err_t ret = adc_oneshot_read(adc_handle_, BATTERY_ADC_CHANNEL, &adc_value);
            if (ret != ESP_OK) {
                ESP_LOGE(TAG, "ADC read failed: %s", esp_err_to_name(ret));
                return;
            }
            adc_samples.push_back(adc_value);
            vTaskDelay(pdMS_TO_TICKS(10));  // 每次采样间隔10ms
        }

        std::sort(adc_samples.begin(), adc_samples.end());
        int adc_value = adc_samples[adc_samples.size() / 2];  // 中位数滤波

        int adc_voltage_mv;
        esp_err_t ret = adc_cali_raw_to_voltage(adc_cali_handle_, adc_value, &adc_voltage_mv);
        if (ret != ESP_OK) {
            ESP_LOGE(TAG, "ADC calibration failed: %s", esp_err_to_name(ret));
            adc_voltage_mv = adc_value * 3300 / 4095;
        }

        adc_values_.push_back(adc_voltage_mv);
        if (adc_values_.size() > kBatteryAdcDataCount) {
            adc_values_.erase(adc_values_.begin());
        }

        int average_voltage_mv = 0;
        for (auto value : adc_values_) {
            average_voltage_mv += value;
        }
        average_voltage_mv /= adc_values_.size();

        // 修正分压系数：根据满电电池电压4.2V和ADC读数967mV计算得出
        float battery_voltage = average_voltage_mv / 1000.0f * 4.34f;

        // 使用锂电池典型电压阈值
        const float kVoltage100Percent = 4.2f;  // 锂电池满电电压
        const float kVoltage75Percent = 3.9f;   // 75%电量电压
        const float kVoltage50Percent = 3.7f;   // 50%电量电压
        const float kVoltage25Percent = 3.5f;   // 25%电量电压
        const float kVoltage0Percent = 3.2f;    // 0%电量电压（保护电压）

        // 基于锂电池典型电压计算电量百分比
        int battery_percentage;
        if (battery_voltage >= kVoltage100Percent) {
            battery_percentage = 100;
        } else if (battery_voltage >= kVoltage75Percent) {
            battery_percentage = 75 + (battery_voltage - kVoltage75Percent) * 25 / (kVoltage100Percent - kVoltage75Percent);
        } else if (battery_voltage >= kVoltage50Percent) {
            battery_percentage = 50 + (battery_voltage - kVoltage50Percent) * 25 / (kVoltage75Percent - kVoltage50Percent);
        } else if (battery_voltage >= kVoltage25Percent) {
            battery_percentage = 25 + (battery_voltage - kVoltage25Percent) * 25 / (kVoltage50Percent - kVoltage25Percent);
        } else if (battery_voltage >= kVoltage0Percent) {
            battery_percentage = 0 + (battery_voltage - kVoltage0Percent) * 25 / (kVoltage25Percent - kVoltage0Percent);
        } else {
            battery_percentage = 0;
        }

        battery_level_ = battery_percentage;

        ESP_LOGI(TAG, "ADC: %d, 原始电压: %.2fV, 计算电池电压: %.2fV, 电量: %d%%, 满电电压: %.2fV", 
                 average_voltage_mv, average_voltage_mv / 1000.0f, battery_voltage, battery_percentage, kVoltage100Percent);

        // 打印蓝牙MAC地址
        ESP_LOGI("BluetoothMAC", "Bluetooth MAC Address: %s", SystemInfo::GetBleMacAddress().c_str());// 生产测试打印
    }

    void InitializeStoryButton() {
        story_button_.OnClick([this]() {
            ESP_LOGI(TAG, "Story button clicked!");

            // 检查是否处于BLE配网状态，如果是则屏蔽按键响应（生产测试模式下除外）
            auto* wifi_board = dynamic_cast<WifiBoard*>(this);
            if (wifi_board && wifi_board->IsBleProvisioningActive() && !production_test_mode_) {
                //ESP_LOGI(Pro_TAG, "🔵 设备正在进行蓝牙配网，长按BOOT按键5秒可进入生产测试模式!");
                ESP_LOGI(Pro_TAG, "🔵 当前为蓝牙配网模式,[故事按键]被按下，长按BOOT按键5秒可进入生产测试模式!");
                ESP_LOGI("BluetoothMAC", "Bluetooth MAC Address: %s", SystemInfo::GetBleMacAddress().c_str());// 生产测试打印
                ESP_LOGI(Pro_TAG, "当前电量值: %" PRIu32 "%%", battery_level_);// 生产测试打印
                return;
            }

            // 如果处于生产测试模式，记录按键测试并播放音频
            if (production_test_mode_) {
                ESP_LOGI(Pro_TAG, "🔧 生产测试模式：故事按键已被按下!");// 生产测试打印
                ESP_LOGI("BluetoothMAC", "Bluetooth MAC Address: %s", SystemInfo::GetBleMacAddress().c_str());// 生产测试打印
                ESP_LOGI(Pro_TAG, "当前电量值: %" PRIu32 "%%", battery_level_);// 生产测试打印
                // 播放故事按键测试音频
                auto& app = Application::GetInstance();
                // app.PlaySound(Lang::Sounds::P3_PUTDOWN_STORY);
                app.PlaySound(Lang::Sounds::P3_2);

                // 等待音频播放完成后清空队列
                vTaskDelay(pdMS_TO_TICKS(700)); // 等待3秒确保音频播放完成

                // 清空音频播放队列，避免残留
                app.ClearAudioQueue();

                return;
            }

            auto& app = Application::GetInstance();// 获取应用实例
            auto current_state = app.GetDeviceState();// 获取当前设备状态

            // 如果当前状态为对话状态
            if (current_state == kDeviceStateDialog) {
                if (!app.IsDialogUploadEnabled()) {// 如果音频采集上传未启用
                    app.ToggleChatState();// 切换聊天状态
                    vTaskDelay(300 / portTICK_PERIOD_MS);// 等待300ms确保状态切换完成
                }
                app.AbortSpeaking(kAbortReasonVoiceInterrupt);// 🔊 发送中止通话请求时，加锁保护RTC句柄
                vTaskDelay(pdMS_TO_TICKS(120));// 等待120ms确保中止通话请求发送完成
                // app.SendTextMessage("给我讲个小故事");// 发送“给我讲个小故事”消息
                app.SendStoryRequest();// 发送故事请求
            } 
            // else if (current_state == kDeviceStateSpeaking) {
            //     app.AbortSpeaking(kAbortReasonNone);
            //     vTaskDelay(300 / portTICK_PERIOD_MS);
            //     if (!app.IsDialogUploadEnabled()) {
            //         app.ToggleChatState();// 切换聊天状态
            //         vTaskDelay(300 / portTICK_PERIOD_MS);// 等待300ms确保状态切换完成
            //     }
            //     app.SendTextMessage("给我讲个小故事");// 发送“给我讲个小故事”消息
            // } else {
            //     app.ToggleChatState();// 切换聊天状态
            //     vTaskDelay(300 / portTICK_PERIOD_MS);// 等待300ms确保状态切换完成
            //     app.SendTextMessage("给我讲个小故事");// 发送“给我讲个小故事”消息
            // }
            // 唤醒设备，防止立即进入睡眠
            power_save_timer_->WakeUp();
        });
        ESP_LOGI(TAG, "故事按键单击已初始化，GPIO引脚 =%d", KEY4_GPIO);

        // 双击KEY4：录入本命石头
        // 注意：iot_button回调运行在esp_timer任务中，禁止vTaskDelay
        // 必须用xTaskCreate派发到独立任务执行传感器读取
        story_button_.OnDoubleClick([this]() {
            ESP_LOGI(TAG, "KEY4 双击 - 录入本命石头");
            power_save_timer_->WakeUp();
            xTaskCreate([](void* arg) {
                auto* board = static_cast<MovecallMojiESP32S3*>(arg);
                board->RegisterMyStone();
                vTaskDelete(NULL);
            }, "stone_reg", 4096, this, 5, NULL);
        });
        ESP_LOGI(TAG, "故事按键双击(录入本命石)已初始化");

        // 长按KEY4 2秒：识别他人石头并匹配
        story_button_.OnLongPress([this]() {
            ESP_LOGI(TAG, "KEY4 长按2秒 - 石头匹配");
            power_save_timer_->WakeUp();
            xTaskCreate([](void* arg) {
                auto* board = static_cast<MovecallMojiESP32S3*>(arg);
                board->MatchStone();
                vTaskDelete(NULL);
            }, "stone_match", 4096, this, 5, NULL);
        });
        ESP_LOGI(TAG, "故事按键长按(石头匹配)已初始化");

        ESP_LOGI(TAG, "所有按键已成功初始化！");
    }

    // 物联网初始化，添加音频设备和IMU传感器
    void InitializeIot() {
        auto& thing_manager = iot::ThingManager::GetInstance();// 获取物联网管理器实例
        thing_manager.AddThing(iot::CreateThing("Speaker")); // 添加音频设备到物联网管理器

        // 创建并添加IMU传感器IoT设备 陀螺仪/姿态传感器业务 新增代码
        // ==============================================================================
        const char* log_tag = production_test_mode_ ? Pro_TAG : TAG;
        if (imu_sensor_ && imu_initialized_) {
            imu_thing_ = new iot::ImuSensorThing(imu_sensor_);
            thing_manager.AddThing(imu_thing_);
            ESP_LOGI(log_tag, "IMU传感器已添加到IoT管理器");// IMU传感器已添加到IoT管理器
        } else {
            ESP_LOGI(log_tag, "IMU传感器未初始化，跳过IoT注册");// IMU传感器未初始化，跳过IoT注册
        }
        // ==============================================================================
    }

    // 唤醒词方案配置
    void InitializeWakeWordSchemes() {
        ESP_LOGI(TAG, "Wake word schemes initialized");
    }



    // 设置触摸阈值
    void CalibrateTouchThresholds() {
        touch_pad_t touch_pads[4] = {TOUCH_PAD_NUM1, TOUCH_PAD_NUM2, TOUCH_PAD_NUM3, TOUCH_PAD_NUM7};

        // 使用固定阈值5000
        uint32_t default_threshold = 5000;  // 设置为5000，降低灵敏度减少误触发

        // 为每个触摸板设置阈值
        for (int i = 0; i < 4; ++i) {
            // 先读取原始值作为参考
            uint32_t touch_value = 0;
            esp_err_t ret = touch_pad_read_raw_data(touch_pads[i], &touch_value);

            if (ret == ESP_OK) {
                ESP_LOGI(TAG, "触摸板 %d 初始原始值: %" PRIu32, i, touch_value);
                // 根据实际读数动态调整阈值
                if (touch_value > 8000) {
                    // 未触摸状态，使用固定阈值而非动态计算
                    raw_touch_values_[i] = touch_value;  // 存储当前值作为参考
                    touch_thresholds_[i] = default_threshold;  // 使用固定的5000阈值

                    ESP_ERROR_CHECK(touch_pad_set_thresh(touch_pads[i], touch_thresholds_[i]));
                    ESP_LOGI(TAG, "触摸板 %d 设置固定阈值: %" PRIu32, i, (uint32_t)touch_thresholds_[i]);
                } else {
                    // 可能已经在触摸状态，使用固定阈值
                    raw_touch_values_[i] = 10000;  // 假设一个高值
                    touch_thresholds_[i] = default_threshold;

                    ESP_ERROR_CHECK(touch_pad_set_thresh(touch_pads[i], default_threshold));
                    ESP_LOGI(TAG, "触摸板 %d 设置固定阈值(触摸中): %" PRIu32, i, default_threshold);
                }
            } else {
                // 读取失败，使用默认值
                raw_touch_values_[i] = 10000;
                touch_thresholds_[i] = default_threshold;

                ESP_ERROR_CHECK(touch_pad_set_thresh(touch_pads[i], default_threshold));
                ESP_LOGI(TAG, "触摸板 %d 无法读取原始值，使用固定阈值: %" PRIu32, i, default_threshold);
            }
        }

        // 使用滤波器提高稳定性
        ESP_LOGI(TAG, "启用触摸传感器滤波器");
        touch_filter_config_t filter_info = {
            .mode = TOUCH_PAD_FILTER_IIR_16,  // IIR滤波器，16采样
            .debounce_cnt = 1,                // 消抖计数
            .noise_thr = 0,                   // 噪声阈值(不使用)
            .jitter_step = 4,                 // 抖动步长
            .smh_lvl = TOUCH_PAD_SMOOTH_IIR_2 // 平滑级别
        };
        touch_pad_filter_set_config(&filter_info);
        touch_pad_filter_enable();

        ESP_LOGI(TAG, "触摸阈值校准完成，使用固定阈值: %" PRIu32, default_threshold);
    }

    // 重置触摸状态的方法（可用于外部强制重置）
    void ResetTouchState(int touch_pad_num) {
        if (touch_pad_num >= 0 && touch_pad_num < 4) {
            touch_states_[touch_pad_num] = TOUCH_STATE_IDLE;
            ESP_LOGI(TAG, "触摸板 %d 状态已手动重置为空闲", touch_pad_num);
        }
    }

public:
    // 触摸板初始化
    void InitializeTouchPads() {
        ESP_LOGI(TAG, "初始化触摸板...");

        // 初始化触摸模块
        esp_err_t ret = touch_pad_init();
        if (ret != ESP_OK) {
            ESP_LOGE(TAG, "触摸板初始化失败，错误代码: 0x%x", ret);
            return;
        }

        // 设置工作模式
        touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER);

        // 配置触摸传感器
        ESP_LOGI(TAG, "配置触摸传感器...");
        touch_pad_t touch_pads[4] = {TOUCH_PAD_NUM1, TOUCH_PAD_NUM2, TOUCH_PAD_NUM3, TOUCH_PAD_NUM7};

        for (int i = 0; i < 4; ++i) {
            touch_pad_config(touch_pads[i]);
        }

        // 先读取基准值，然后设置阈值
        ESP_LOGI(TAG, "校准触摸阈值...");
        CalibrateTouchThresholds();

        // 创建触摸事件队列
        ESP_LOGI(TAG, "创建触摸事件队列...");
        touch_event_queue_ = xQueueCreate(TOUCH_QUEUE_SIZE, sizeof(touch_event_data_t));
        if (!touch_event_queue_) {
            ESP_LOGE(TAG, "创建触摸事件队列失败");
            return;
        }

        // 注册触摸中断
        ESP_LOGI(TAG, "注册触摸中断处理程序...");
        // 仅启用按下中断，由触摸任务处理释放
        touch_pad_isr_register(TouchPadISR, nullptr, TOUCH_PAD_INTR_MASK_ACTIVE);
        touch_pad_intr_enable(TOUCH_PAD_INTR_MASK_ACTIVE);

        // 创建处理触摸事件的任务
        ESP_LOGI(TAG, "创建触摸事件任务...");
        xTaskCreate(TouchEventTask, "touch_event_task", 4096, this, 10, NULL);

        // 确保所有触摸状态初始为空闲
        ResetAllTouchStates();

        // 开启触摸监控定时器，用于定期检查触摸状态是否正常
        ESP_LOGI(TAG, "设置触摸监控...");

        instance_ = this;
        touch_pad_fsm_start();
        ESP_LOGI(TAG, "触摸板初始化完成");
    }

    AudioCodec* GetAudioCodec() {
        // 使用延迟初始化模式，确保I2C总线和编解码器按正确顺序初始化
        static AudioCodec* audio_codec = nullptr;
        static bool init_attempted = false;

        if (audio_codec == nullptr && !init_attempted) {
            init_attempted = true;  // 标记为已尝试初始化

            ESP_LOGI(TAG, "Initializing audio codec...");
            // 确保I2C总线已初始化
            if (codec_i2c_bus_ == nullptr) {
                ESP_LOGI(TAG, "Initializing I2C bus for audio codec...");
                InitializeCodecI2c();
            }

            if (codec_i2c_bus_ != nullptr) {
                try {
                    ESP_LOGI(TAG, "Creating BoxAudioCodec (ES8311+ES7210, %s reference) ...", AUDIO_INPUT_REFERENCE ? "with" : "without");
                    audio_codec = new BoxAudioCodec(
                        codec_i2c_bus_,
                        AUDIO_INPUT_SAMPLE_RATE,
                        AUDIO_OUTPUT_SAMPLE_RATE,
                        AUDIO_I2S_GPIO_MCLK,
                        AUDIO_I2S_GPIO_BCLK,
                        AUDIO_I2S_GPIO_WS,
                        AUDIO_I2S_GPIO_DOUT,
                        AUDIO_I2S_GPIO_DIN,
                        AUDIO_CODEC_PA_PIN,
                        AUDIO_CODEC_ES8311_ADDR,
                        AUDIO_CODEC_ES7210_ADDR,
                        AUDIO_INPUT_REFERENCE
                    );
                    ESP_LOGI(TAG, "Audio codec initialized successfully");
                } catch (const std::exception& e) {
                    ESP_LOGE(TAG, "Exception during audio codec initialization: %s", e.what());
                } catch (...) {
                    ESP_LOGE(TAG, "Unknown exception during audio codec initialization");
                }
            } else {
                ESP_LOGE(TAG, "Failed to initialize I2C bus for audio codec");
            }
        }

        return audio_codec;
    }

    virtual Led* GetLed() override {
        static SingleLed led_strip(BUILTIN_LED_GPIO);// 初始化单灯条对象
        return &led_strip;
    }

    virtual Display* GetDisplay() override {
        static Display display;  // 空显示器对象，所有方法都是空实现
        return &display;
    }

    virtual Backlight* GetBacklight() override {
        return nullptr;
    }

    // 获取IMU传感器数据
    bool GetImuData(qmi8658a_data_t* data) {
        if (!imu_initialized_ || !imu_sensor_ || !data) {
            return false;
        }

        // 复制最新的IMU数据
        *data = latest_imu_data_;
        return true;
    }

    // 检查IMU传感器是否已初始化
    bool IsImuInitialized() const {
        return imu_initialized_;
    }

    // 获取生产测试模式状态
    bool IsProductionTestMode() const {
        return production_test_mode_;
    }

    // 唤醒PowerSaveTimer，从低功耗模式恢复到正常模式
    void WakeUp() override {
        if (power_save_timer_) {
            power_save_timer_->WakeUp();
            ESP_LOGI(TAG, "🔋 PowerSaveTimer已唤醒，从低功耗模式恢复到正常模式");
        }
    }

    // 重启前回调：上报设备离线状态
    void OnBeforeRestart() override {
        ESP_LOGI(TAG, "📤 设备即将重启，上报离线状态");
        rtc_online_ = false;
        auto& wifi_station = WifiStation::GetInstance();
        if (wifi_station.IsConnected()) {
            ReportDeviceStatus(battery_level_);
        }
    }
};

void MovecallMojiESP32S3::ReportDeviceStatus(int battery_level) {
    ESP_LOGI(TAG, "📤 准备上报设备状态，电量: %d%%", battery_level);

    // 获取当前音量（语音调整后的音量优先，否则为系统默认音量）
    auto codec = GetAudioCodec();
    int volume = codec ? codec->output_volume() : AudioCodec::default_output_volume();

    // 构造JSON数据
    char json_buffer[512];
    snprintf(json_buffer, sizeof(json_buffer),
             "{\"mac_address\":\"%s\",\"is_online\":%s,"
             "\"battery\":%d,\"volume\":%d,\"brightness\":%d}",
             SystemInfo::GetBleMacAddress().c_str(),
             rtc_online_ ? "true" : "false",
             battery_level,
             volume,
             50);  // brightness默认50，后续根据实际调整

    ESP_LOGI(TAG, "📤 上报数据: %s", json_buffer);

    // 创建HTTP客户端
    auto http = Board::GetInstance().CreateHttp();
    if (!http) {
        ESP_LOGE(TAG, "❌ 创建HTTP客户端失败");
        return;
    }

    // 设置请求头
    http->SetHeader("Content-Type", "application/json");

    // 打开连接
    if (!http->Open("POST", DEVICE_STATUS_REPORT_URL, json_buffer)) {
        ESP_LOGE(TAG, "❌ 连接服务器失败: %s", DEVICE_STATUS_REPORT_URL);
        delete http;
        return;
    }

    // 获取响应
    auto response = http->GetBody();
    ESP_LOGI(TAG, "📥 服务器响应: %s", response.c_str());

    // 关闭连接
    http->Close();
    delete http;

    ESP_LOGI(TAG, "✅ 设备状态上报完成");
}

// 初始化静态成员变量
MovecallMojiESP32S3* MovecallMojiESP32S3::instance_ = nullptr;
QueueHandle_t MovecallMojiESP32S3::touch_event_queue_ = nullptr;

// 处理触摸事件的任务
static void TouchEventTask(void* arg) {
    MovecallMojiESP32S3* board = (MovecallMojiESP32S3*)arg;
    touch_event_data_t touch_event;

    ESP_LOGI(TAG, "触摸事件任务启动");

    // 检查board指针
    if (board == nullptr) {
        ESP_LOGE(TAG, "触摸事件任务收到无效的board指针");
        vTaskDelete(NULL);
        return;
    }

    // 检查触摸队列是否有效
    if (MovecallMojiESP32S3::touch_event_queue_ == nullptr) {
        ESP_LOGE(TAG, "触摸事件队列未初始化");
        vTaskDelete(NULL);
        return;
    }

    ESP_LOGI(TAG, "触摸事件任务开始主循环");

    // 用于跟踪每个触摸点的状态
    bool is_touch_active[4] = {false, false, false, false};
    uint32_t touch_start_time[4] = {0, 0, 0, 0};
    uint32_t last_press_time[4] = {0, 0, 0, 0}; // 记录最后一次按下时间，用于防抖
    const uint32_t RELEASE_DELAY_MS = 300; // 触摸释放延迟(毫秒)
    const uint32_t PRESS_IGNORE_MS = 200;  // 忽略连续按压的时间窗口(毫秒)

    while (1) {
        if (xQueueReceive(MovecallMojiESP32S3::touch_event_queue_, &touch_event, 20 / portTICK_PERIOD_MS)) {
            // 收到实际触摸事件(应该都是按下事件)
            uint32_t current_time = esp_timer_get_time() / 1000; // 当前时间(毫秒)

            if (touch_event.pad_num >= 0 && touch_event.pad_num < 4) {
                int pad = touch_event.pad_num;

                // 记录详细的调试信息
                ESP_LOGI(TAG, "TouchEventTask收到触摸事件 - 触摸板: %d, 事件类型: %s", 
                         pad, touch_event.type == TOUCH_EVENT_PRESS ? "按下" : "释放");

                // 过滤连续的按压事件，避免抖动
                if (touch_event.type == TOUCH_EVENT_PRESS) {
                    if (!is_touch_active[pad] || 
                        (current_time - last_press_time[pad] > PRESS_IGNORE_MS)) {
                        // 设置该触摸点为激活状态
                        is_touch_active[pad] = true;
                        touch_start_time[pad] = current_time;
                        last_press_time[pad] = current_time;

                        // 处理按下事件
                        board->HandleTouchEvent(pad, TOUCH_EVENT_PRESS);
                    } else {
                        ESP_LOGD(TAG, "忽略过于频繁的触摸事件 - 触摸板: %d", pad);
                    }
                }
            } else {
                ESP_LOGW(TAG, "收到无效的触摸板编号: %d", touch_event.pad_num);
            }
        } else {
            // 检查是否需要生成释放事件
            uint32_t current_time = esp_timer_get_time() / 1000; // 毫秒

            // 检查每个触摸点
            for (int i = 0; i < 4; ++i) {
                if (is_touch_active[i]) {
                    // 如果触摸点处于激活状态并超过释放延迟
                    if (current_time - touch_start_time[i] >= RELEASE_DELAY_MS) {
                        // 生成释放事件
                        ESP_LOGI(TAG, "生成触摸释放事件 - 触摸板: %d", i);
                        is_touch_active[i] = false;
                        board->HandleTouchEvent(i, TOUCH_EVENT_RELEASE);
                    }
                }
            }

            // 检查触摸状态 (使用touch_pad_read_raw_data直接读取触摸值)
            touch_pad_t touch_pads[4] = {TOUCH_PAD_NUM1, TOUCH_PAD_NUM2, TOUCH_PAD_NUM3, TOUCH_PAD_NUM7};
            for (int i = 0; i < 4; i++) {
                if (is_touch_active[i]) {
                    // 尝试读取当前触摸值，如果大于阈值，则触摸已释放
                    uint32_t touch_value = 0;
                    esp_err_t ret = touch_pad_read_raw_data(touch_pads[i], &touch_value);
                    if (ret == ESP_OK && touch_value > 8000) { // 较大的值表示未触摸，保持高于阈值检测释放
                        ESP_LOGI(TAG, "检测到触摸释放(传感器读数) - 触摸板: %d, 值: %" PRIu32, i, touch_value);
                        is_touch_active[i] = false;
                        board->HandleTouchEvent(i, TOUCH_EVENT_RELEASE);
                    }
                }
            }
        }
    }
}

// 修改ISR函数处理触摸事件
void IRAM_ATTR MovecallMojiESP32S3::TouchPadISR(void* arg) {
    // 获取触摸状态
    uint32_t pad_intr = touch_pad_get_status();
    touch_pad_clear_status();

    // 处理触摸事件
    touch_pad_t touch_pads[4] = {TOUCH_PAD_NUM1, TOUCH_PAD_NUM2, TOUCH_PAD_NUM3, TOUCH_PAD_NUM7};

    for (int i = 0; i < 4; ++i) {
        // 检查按下事件
        if (pad_intr & (1 << touch_pads[i])) {
            // 生产测试模式：独立处理，不影响正常业务逻辑
            // 生产测试模式下触摸按键业务处理 新增代码
            // ==============================================================================
            if (instance_->production_test_mode_) {
                // 获取当前时间用于防抖
                uint32_t current_time = esp_timer_get_time() / 1000; // 转换为毫秒

                // 检查防抖时间（500ms防抖间隔，避免重复触发）
                if (current_time - instance_->touch_last_time_[i] > 500) {
                    // 设置触摸检测标志位
                    instance_->touch_detected_flag_ = true;
                    instance_->touched_pad_index_ = i;
                    ESP_EARLY_LOGI(Pro_TAG, "🔧 检测到触摸事件，设置标志位 (触摸板%d)", i);
                    // 生产测试触摸音效
                    const char* pad_names[4] = {"脑袋", "肚子", "下巴", "后背"};
                    ESP_EARLY_LOGI(Pro_TAG, "生产测试：触摸板%d(%s)被触摸", i, pad_names[i]);
                    ESP_EARLY_LOGI(Pro_TAG, "生产测试：触摸板%d(%s)被触摸", i, pad_names[i]);
                    ESP_EARLY_LOGI(Pro_TAG, "生产测试：触摸板%d(%s)被触摸", i, pad_names[i]);
                    // // 通过Application播放音效（非阻塞）- 已禁用
                    // auto& app = Application::GetInstance();
                    // app.PlaySound(Lang::Sounds::P3_PUTDOWN_TOUCH);
                    // 更新最后触摸时间
                    instance_->touch_last_time_[i] = current_time;

                    // 重置标志位，为下次触摸做准备
                    instance_->touch_detected_flag_ = false;
                    instance_->touched_pad_index_ = -1;
                } else {
                    // 在防抖时间内，忽略触摸事件
                    ESP_EARLY_LOGD(Pro_TAG, "触摸板%d防抖中，忽略触摸事件", i);
                }

                // 生产测试模式下直接返回，不执行后续的正常业务逻辑
                return;
            }
            // ==============================================================================

            // 正常模式：保持原有的触摸处理逻辑
            // 创建按下事件
            touch_event_data_t event = {
                .pad_num = i,
                .type = TOUCH_EVENT_PRESS
            };

            // 发送到队列 - 只关注状态变化，消息处理由HandleTouchEvent负责
            if (MovecallMojiESP32S3::touch_event_queue_) {
                BaseType_t xHigherPriorityTaskWoken = pdFALSE;
                BaseType_t result = xQueueSendFromISR(MovecallMojiESP32S3::touch_event_queue_, 
                                 &event, &xHigherPriorityTaskWoken);

                if (result == pdTRUE) {
                    ESP_EARLY_LOGI(TAG, "触摸事件已发送到队列");
                } else {
                    ESP_EARLY_LOGE(TAG, "触摸事件发送到队列失败");
                }

                if (xHigherPriorityTaskWoken) {
                    portYIELD_FROM_ISR();
                }
            } else {
                ESP_EARLY_LOGE(TAG, "触摸事件队列为空");// 队列为空是严重错误，无论什么模式都要记录
            }
        }
    }
}

// 添加锁定触摸任务的方法实现
void MovecallMojiESP32S3::LockTouchTask(int touch_pad_num) {
    // 在生产测试模式下，不锁定触摸任务- 新增代码
    // ================================================================
    if (production_test_mode_) {
        ESP_LOGI(TAG, "🔧 生产测试模式：跳过触摸任务锁定，保持连续测试能力");
        return;
    }
    // ================================================================

    touch_task_locked_ = true;
    active_touch_pad_ = touch_pad_num;
    touch_task_start_time_ = esp_timer_get_time() / 1000; // 记录任务开始时间(毫秒)
    ESP_LOGI(TAG, "触摸任务已锁定，活跃触摸点：%d", touch_pad_num);
}

// 添加解锁触摸任务的方法实现
void MovecallMojiESP32S3::UnlockTouchTask() {
    // 先清除锁定状态和活跃触摸点
    touch_task_locked_ = false;
    active_touch_pad_ = -1;
    ESP_LOGI(TAG, "触摸任务已解锁，可以接收新的触摸");

    // 重置所有触摸状态，但不调用其他复杂操作
    uint32_t current_time = esp_timer_get_time() / 1000;
    for (int i = 0; i < 4; i++) {
        touch_states_[i] = TOUCH_STATE_IDLE;
        touch_last_time_[i] = current_time;
    }
    ESP_LOGI(TAG, "所有触摸状态已重置");
}

void MovecallMojiESP32S3::HandleTouchEvent(int touch_pad_num, touch_event_type_t event_type) {
    if (touch_pad_num < 0 || touch_pad_num >= 4) return;

    // 获取当前时间
    uint32_t current_time = esp_timer_get_time() / 1000; // 毫秒

    // 获取触摸点状态
    touch_state_t current_state = touch_states_[touch_pad_num];
    uint32_t time_elapsed = current_time - touch_last_time_[touch_pad_num];

    // 添加更详细的调试信息
    const char* pad_names[4] = {"脑袋", "肚子", "下巴", "后背"};
    const char* state_names[4] = {"空闲", "按下", "释放", "去抖"};

    ESP_LOGI(TAG, "[调试] 触摸事件处理 - 触摸板: %d(%s), 事件类型: %s, 当前状态: %s, 间隔: %" PRIu32 "ms",
         touch_pad_num,
         pad_names[touch_pad_num],
         event_type == TOUCH_EVENT_PRESS ? "按下" : "释放",
         state_names[current_state],
         time_elapsed);

    // 检查触摸任务是否已锁定，如果锁定且不是活跃触摸点的事件，忽略该事件
    if (touch_task_locked_) {
        // 在生产测试模式下，不锁定触摸任务，允许连续测试
        if (production_test_mode_) {
            ESP_LOGI(TAG, "🔧 生产测试模式：忽略触摸任务锁定，允许连续测试");
            // 直接解锁并继续处理
            UnlockTouchTask();
        } else {
            // 检查是否超时
            if (current_time - touch_task_start_time_ > TOUCH_TASK_TIMEOUT_MS) {
                ESP_LOGW(TAG, "触摸任务超时(%" PRIu32 "ms)，自动解锁", 
                        current_time - touch_task_start_time_);
                UnlockTouchTask();
                // 继续处理当前事件
            } 
            // 如果不是活跃触摸点的事件，忽略
            else if (touch_pad_num != active_touch_pad_) {
                ESP_LOGI(TAG, "触摸任务已锁定，忽略非活跃触摸点(%d)的事件", touch_pad_num);
                return;
            }
        }
    }

    // 根据当前状态和事件类型进行状态转换
    switch (current_state) {
        case TOUCH_STATE_IDLE:
            if (event_type == TOUCH_EVENT_PRESS) {
                // 从空闲转为按下状态 - 这是第一次触摸
                touch_states_[touch_pad_num] = TOUCH_STATE_PRESSED;
                touch_last_time_[touch_pad_num] = current_time;

                // 锁定任务，确保只有当前触摸点的任务被执行
                if (!touch_task_locked_) {
                    LockTouchTask(touch_pad_num);

                    // 触发一次消息
                    SendTouchMessage(touch_pad_num);
                    power_save_timer_->WakeUp();
                }

                ESP_LOGI(TAG, "[状态] 触摸板 %d(%s) 状态: IDLE -> PRESSED (首次触摸)", 
                    touch_pad_num, pad_names[touch_pad_num]);
            }
            break;

        case TOUCH_STATE_PRESSED:
            if (event_type == TOUCH_EVENT_PRESS) {
                // 已经处于按下状态，忽略连续的按下事件
                ESP_LOGD(TAG, "忽略持续按下事件 - 触摸板: %d(%s)", touch_pad_num, pad_names[touch_pad_num]);

            } else if (event_type == TOUCH_EVENT_RELEASE) {
                // 从按下转为释放状态
                touch_states_[touch_pad_num] = TOUCH_STATE_RELEASED;
                touch_last_time_[touch_pad_num] = current_time;

                // 如果是活跃触摸点释放，解锁触摸任务
                if (touch_task_locked_ && touch_pad_num == active_touch_pad_) {
                    // 根据实际需求决定是否立即解锁
                    // 这里我们延迟解锁，等待任务完成
                    // 实际应用中，可能需要在任务完成后主动调用UnlockTouchTask

                    // 示例：延迟1秒解锁，等待任务完成
                    xTaskCreate([](void* arg) {
                        MovecallMojiESP32S3* board = static_cast<MovecallMojiESP32S3*>(arg);
                        vTaskDelay(1000 / portTICK_PERIOD_MS);
                        if (board) {
                            board->UnlockTouchTask();
                        }
                        vTaskDelete(NULL);
                    }, "unlock_touch", 4096, this, 5, NULL);
                }

                ESP_LOGI(TAG, "[状态] 触摸板 %d(%s) 状态: PRESSED -> RELEASED", 
                    touch_pad_num, pad_names[touch_pad_num]);
            }
            break;

        case TOUCH_STATE_RELEASED:
            if (event_type == TOUCH_EVENT_PRESS) {
                // 如果释放后很快又被按下，可能是抖动，进入去抖状态
                if (time_elapsed < DEBOUNCE_TIME_MS) {
                    touch_states_[touch_pad_num] = TOUCH_STATE_DEBOUNCE;
                    touch_last_time_[touch_pad_num] = current_time;

                    ESP_LOGI(TAG, "[状态] 触摸板 %d(%s) 状态: RELEASED -> DEBOUNCE (可能抖动)", 
                        touch_pad_num, pad_names[touch_pad_num]);
                } else {
                    // 如果释放时间足够长，则认为是新的有效按下
                    touch_states_[touch_pad_num] = TOUCH_STATE_PRESSED;
                    touch_last_time_[touch_pad_num] = current_time;

                    // 触发一次新的消息
                    SendTouchMessage(touch_pad_num);
                    power_save_timer_->WakeUp();

                    ESP_LOGI(TAG, "[状态] 触摸板 %d(%s) 状态: RELEASED -> PRESSED (新的按下)", 
                        touch_pad_num, pad_names[touch_pad_num]);
                }
            } else if (time_elapsed > MIN_RELEASE_TIME_MS) {
                // 如果释放状态持续足够长，回到空闲状态
                touch_states_[touch_pad_num] = TOUCH_STATE_IDLE;
                touch_last_time_[touch_pad_num] = current_time;

                ESP_LOGI(TAG, "[状态] 触摸板 %d(%s) 状态: RELEASED -> IDLE (完全释放)", 
                    touch_pad_num, pad_names[touch_pad_num]);
            }
            break;

        case TOUCH_STATE_DEBOUNCE:
            if (event_type == TOUCH_EVENT_RELEASE) {
                // 去抖动完成，回到释放状态
                touch_states_[touch_pad_num] = TOUCH_STATE_RELEASED;
                touch_last_time_[touch_pad_num] = current_time;

                ESP_LOGI(TAG, "[状态] 触摸板 %d(%s) 状态: DEBOUNCE -> RELEASED", 
                    touch_pad_num, pad_names[touch_pad_num]);
            } else if (event_type == TOUCH_EVENT_PRESS && time_elapsed > DEBOUNCE_TIME_MS) {
                // 如果在去抖状态接收到新的按压且时间足够长，认为是有效按下
                touch_states_[touch_pad_num] = TOUCH_STATE_PRESSED;
                touch_last_time_[touch_pad_num] = current_time;

                ESP_LOGI(TAG, "[状态] 触摸板 %d(%s) 状态: DEBOUNCE -> PRESSED (确认按下)", 
                    touch_pad_num, pad_names[touch_pad_num]);
            }
            break;
    }
}

// 添加一个新方法用于重置所有触摸状态
void MovecallMojiESP32S3::ResetAllTouchStates() {
    uint32_t current_time = esp_timer_get_time() / 1000;

    ESP_LOGI(TAG, "所有触摸状态已重置");

    // 重置所有触摸点状态，简化日志输出
    for (int i = 0; i < 4; i++) {
        touch_states_[i] = TOUCH_STATE_IDLE;
        touch_last_time_[i] = current_time;
    }

    // 清除触摸中断状态
    touch_pad_clear_status();
}

// 进入生产测试模式- 新增代码
// ==============================================================================
void MovecallMojiESP32S3::EnterProductionTestMode() {
    if (production_test_mode_) {
        ESP_LOGI(TAG, "已经处于生产测试模式，忽略重复进入");
        return;
    }

    production_test_mode_ = true;
    esp_log_level_set("*", ESP_LOG_INFO);
    esp_log_level_set("MovecallMojiESP32S3", ESP_LOG_INFO);
    esp_log_level_set("Airhub1", ESP_LOG_INFO);
    esp_log_level_set("AFE", ESP_LOG_ERROR);
    
    ESP_LOGI(Pro_TAG, "🔧 已进入生产测试模式,可以开始测试!");// 生产测试打印
    
    auto& app = Application::GetInstance();
    auto* codec = GetAudioCodec();
    if (codec) {
        codec->EnableOutput(true);
        ESP_LOGI(TAG, "🔊 测试模式：已启用音频输出");
    }
    app.PlaySound(Lang::Sounds::P3_TEST_MODAL);
    ESP_LOGI(TAG, "🎵 测试模式：开始播放进入测试模式音频");

    ESP_LOGI(Pro_TAG, "🔧 生产测试模式：重新初始化IMU传感器");
    InitializeImuSensor();

    // 检查IMU传感器初始化状态
    ESP_LOGI(Pro_TAG, "🔧 生产测试：IMU传感器初始化状态: %s", imu_initialized_ ? "成功" : "失败");
    
    if (imu_initialized_ && imu_sensor_) {
        ESP_LOGI(Pro_TAG, "🔧 姿态传感器已初始化成功! 可以开始测试运动检测功能");
        xTaskCreate(
            [](void* arg) {
                vTaskDelay(pdMS_TO_TICKS(1500));
                auto& app_local = Application::GetInstance();
                app_local.PlaySound(Lang::Sounds::P3_TUOLUOYI);
                ESP_LOGI(Pro_TAG, "🎵 播放陀螺仪检测成功音频");
                vTaskDelete(NULL);
            },
            "play_tuoluo_audio",
            4096,
            nullptr,
            5,
            nullptr
        );
    } else {
        ESP_LOGI(Pro_TAG, "姿态传感器初始化失败或未连接!");
        
        // 尝试再次检测连接
        if (codec_i2c_bus_) {
            uint8_t detected_address = 0;
            bool sensor_connected = QMI8658A::CheckConnection(codec_i2c_bus_, &detected_address);
            if (sensor_connected) {
                ESP_LOGI(Pro_TAG, "🔧 姿态传感器物理连接存在! 设备地址:0x%02X，但初始化失败", detected_address);
            }
        }
    }

    // 非阻塞式触摸检测 - 触摸事件将通过现有的触摸处理机制来处理
    ESP_LOGI(Pro_TAG, "🔧 生产测试模式已启用，触摸检测已激活，其他按键功能正常可用");
    ESP_LOGI(Pro_TAG, "🔧 提示：现在可以测试触摸板、BOOT按键和讲故事按键");
}
// ==============================================================================

DECLARE_BOARD(MovecallMojiESP32S3);
