// 吧唧模式按键驱动 // BOOT(GPIO0): iot_button(由board类管理),此模块处理业务分发 // KEY2(GPIO4): iot_button(此模块管理),支持单击/双击/长按 // 界面上下文状态机决定按键行为 #include "dzbj_button.h" #include "device_mode.h" #include "driver/gpio.h" #include "esp_log.h" #include "esp_timer.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "iot_button.h" #include "lvgl.h" #include "esp_lvgl_port.h" #include "sleep_mgr/include/sleep_mgr.h" #include "dzbj_ble.h" #include "ble_transfer.h" #include "pages.h" #include "ui/ui.h" #include "ui/screens/ui_ScreenHome.h" #include "ui/screens/ui_ScreenImg.h" #include "ui/screens/ui_ScreenPeiwang.h" #include "ui/screens/ui_ScreenImageShar.h" #include "ui/screens/ui_ScreenImageReception.h" static const char *TAG = "DZBJ_BTN"; // === 界面上下文状态 === static btn_context_t current_ctx = BTN_CTX_HOME; // === KEY2 iot_button 实例 === static button_handle_t key2_handle = NULL; // === 外部UI函数声明 === extern void ui_ScreenImg_hide_delete_container(void); extern bool flashlight_is_active(void); extern uint8_t flashlight_get_saved_brightness(void); extern void flashlight_exit(void); extern void pwm_set_brightness(uint8_t percent); // === 上下文管理 === void dzbj_button_set_context(btn_context_t ctx) { current_ctx = ctx; ESP_LOGI(TAG, "按键上下文切换: %d", ctx); } btn_context_t dzbj_button_get_context(void) { return current_ctx; } // === 退出到Home的通用逻辑(在独立任务中执行,可调用vTaskDelay) === static void go_home_task(void *arg) { sleep_mgr_notify_activity(); // 退出手电筒 bool was_flashlight = flashlight_is_active(); uint8_t saved_brightness = 0; if (was_flashlight) { saved_brightness = flashlight_get_saved_brightness(); flashlight_exit(); vTaskDelay(pdMS_TO_TICKS(80)); } if (lvgl_port_lock(100)) { _ui_screen_change(&ui_ScreenHome, LV_SCR_LOAD_ANIM_NONE, 0, 0, &ui_ScreenHome_screen_init); lvgl_port_unlock(); } current_ctx = BTN_CTX_HOME; if (was_flashlight) { vTaskDelay(pdMS_TO_TICKS(150)); pwm_set_brightness(saved_brightness); } vTaskDelete(NULL); } // === 退出配对/等待界面 → Home === static void exit_peiwang_task(void *arg) { ESP_LOGI(TAG, "退出配对界面,关闭蓝牙"); dzbj_ble_stop(); sleep_mgr_notify_activity(); if (lvgl_port_lock(100)) { _ui_screen_change(&ui_ScreenHome, LV_SCR_LOAD_ANIM_NONE, 0, 0, &ui_ScreenHome_screen_init); lvgl_port_unlock(); } current_ctx = BTN_CTX_HOME; vTaskDelete(NULL); } static void exit_transfer_wait_task(void *arg) { ESP_LOGI(TAG, "退出传输等待,取消传输"); ble_transfer_cancel(); sleep_mgr_notify_activity(); if (lvgl_port_lock(100)) { _ui_screen_change(&ui_ScreenHome, LV_SCR_LOAD_ANIM_NONE, 0, 0, &ui_ScreenHome_screen_init); lvgl_port_unlock(); } current_ctx = BTN_CTX_HOME; vTaskDelete(NULL); } // === KEY2 进入功能界面的任务 === static void key2_enter_peiwang_task(void *arg) { ESP_LOGI(TAG, "KEY2单击:开蓝牙 → 配对界面"); sleep_mgr_notify_activity(); dzbj_ble_start(); if (lvgl_port_lock(100)) { _ui_screen_change(&ui_ScreenPeiwang, LV_SCR_LOAD_ANIM_NONE, 0, 0, &ui_ScreenPeiwang_screen_init); lvgl_port_unlock(); } current_ctx = BTN_CTX_PEIWANG; vTaskDelete(NULL); } static void key2_enter_reception_task(void *arg) { ESP_LOGI(TAG, "KEY2双击:接收模式 → 等待配对"); sleep_mgr_notify_activity(); ble_transfer_start_receive(); if (lvgl_port_lock(100)) { _ui_screen_change(&ui_ScreenImageReception, LV_SCR_LOAD_ANIM_NONE, 0, 0, &ui_ScreenImageReception_screen_init); lvgl_port_unlock(); } current_ctx = BTN_CTX_IMAGE_RECEPTION; vTaskDelete(NULL); } static void key2_enter_share_task(void *arg) { ESP_LOGI(TAG, "KEY2长按:发送模式 → 等待配对"); sleep_mgr_notify_activity(); init_spiffs_image_list(); ble_transfer_start_send(); if (lvgl_port_lock(100)) { _ui_screen_change(&ui_ScreenImageShar, LV_SCR_LOAD_ANIM_NONE, 0, 0, &ui_ScreenImageShar_screen_init); lvgl_port_unlock(); } current_ctx = BTN_CTX_IMAGE_SHAR; vTaskDelete(NULL); } // === KEY2 事件回调(在esp_timer中执行,不能vTaskDelay,派发到独立任务) === static void key2_click_cb(void *handle, void *usr_data) { // 模式切换重启后按键抑制 if (device_mode_in_switch_suppress()) { ESP_LOGI(TAG, "模式切换按键抑制期,忽略KEY2单击"); return; } switch (current_ctx) { case BTN_CTX_HOME: case BTN_CTX_IMG: // 开蓝牙 → 配对界面 xTaskCreate(key2_enter_peiwang_task, "k2_pw", 3072, NULL, 5, NULL); break; case BTN_CTX_PEIWANG: // 退出配对 → Home xTaskCreate(exit_peiwang_task, "k2_exit", 3072, NULL, 5, NULL); break; case BTN_CTX_IMAGE_SHAR: case BTN_CTX_IMAGE_RECEPTION: // 取消传输 → Home xTaskCreate(exit_transfer_wait_task, "k2_exit", 3072, NULL, 5, NULL); break; default: // SHARING/RECEIVING/UPDATE 不响应 break; } } static void key2_double_click_cb(void *handle, void *usr_data) { if (device_mode_in_switch_suppress()) return; switch (current_ctx) { case BTN_CTX_HOME: case BTN_CTX_IMG: // 接收模式 xTaskCreate(key2_enter_reception_task, "k2_recv", 3072, NULL, 5, NULL); break; default: break; } } static void key2_long_press_cb(void *handle, void *usr_data) { if (device_mode_in_switch_suppress()) return; // 组合键保护:如果BOOT同时按下,说明是模式切换组合键,跳过 if (gpio_get_level(PIN_BTN_BOOT) == 0) { ESP_LOGI(TAG, "BOOT+KEY2组合键检测,跳过KEY2长按业务"); return; } switch (current_ctx) { case BTN_CTX_HOME: case BTN_CTX_IMG: // 发送模式 xTaskCreate(key2_enter_share_task, "k2_shar", 3072, NULL, 5, NULL); break; default: break; } } // === 初始化 === esp_err_t dzbj_button_init(void) { // KEY2 使用 iot_button(支持单击/双击/长按) button_config_t key2_cfg = { .type = BUTTON_TYPE_GPIO, .long_press_time = 1200, .short_press_time = 300, // 双击检测窗口300ms,与按键版对齐 .gpio_button_config = { .gpio_num = PIN_BTN_KEY2, .active_level = 0, // 低电平有效 }, }; key2_handle = iot_button_create(&key2_cfg); if (!key2_handle) { ESP_LOGE(TAG, "KEY2 iot_button创建失败"); return ESP_FAIL; } // 注册KEY2事件 iot_button_register_cb(key2_handle, BUTTON_SINGLE_CLICK, key2_click_cb, NULL); iot_button_register_cb(key2_handle, BUTTON_DOUBLE_CLICK, key2_double_click_cb, NULL); iot_button_register_cb(key2_handle, BUTTON_LONG_PRESS_START, key2_long_press_cb, NULL); ESP_LOGI(TAG, "按键初始化完成 (KEY2=GPIO%d iot_button,BOOT由board类管理)", PIN_BTN_KEY2); return ESP_OK; } // === BOOT+KEY2 组合键模式切换 === static void mode_switch_task(void *arg) { ESP_LOGI(TAG, "执行模式切换..."); if (device_mode_is_badge()) { ESP_LOGI(TAG, "吧唧模式 → AI模式"); device_mode_set(DEVICE_MODE_AI); } else { ESP_LOGI(TAG, "AI模式 → 吧唧模式"); device_mode_set(DEVICE_MODE_BADGE); } vTaskDelete(NULL); } static void mode_switch_combo_cb(void *handle, void *data) { if (gpio_get_level(PIN_BTN_KEY2) == 0) { ESP_LOGI(TAG, "BOOT+KEY2组合键2秒:触发模式切换"); xTaskCreate(mode_switch_task, "mode_sw", 2048, NULL, 5, NULL); } } void dzbj_button_register_mode_switch_combo(void *boot_handle) { if (!boot_handle) return; button_event_config_t evt_cfg = { .event = BUTTON_LONG_PRESS_START, .event_data = { .long_press = { .press_time = 2000, }, }, }; esp_err_t ret = iot_button_register_event_cb((button_handle_t)boot_handle, evt_cfg, mode_switch_combo_cb, NULL); if (ret == ESP_OK) { ESP_LOGI(TAG, "BOOT+KEY2组合键模式切换注册成功(2000ms)"); } else { ESP_LOGE(TAG, "组合键注册失败: %s", esp_err_to_name(ret)); } } // === BOOT 单击处理(根据上下文分发) === void dzbj_boot_click_handler(void) { if (device_mode_in_switch_suppress()) { ESP_LOGI(TAG, "模式切换按键抑制期,忽略BOOT单击"); return; } bool screen_was_off = sleep_mgr_is_screen_off(); if (screen_was_off) { ESP_LOGI(TAG, "BOOT:低功耗唤醒"); sleep_mgr_notify_activity(); return; } switch (current_ctx) { case BTN_CTX_HOME: // Home界面 BOOT单击:无额外操作(已在Home) sleep_mgr_notify_activity(); break; case BTN_CTX_IMG: case BTN_CTX_SET: // Img/Set界面 BOOT单击:返回Home xTaskCreate(go_home_task, "boot_home", 4096, NULL, 5, NULL); break; case BTN_CTX_PEIWANG: // 配对界面 BOOT单击:关蓝牙 → Home xTaskCreate(exit_peiwang_task, "boot_exit", 3072, NULL, 5, NULL); break; case BTN_CTX_IMAGE_SHAR: case BTN_CTX_IMAGE_RECEPTION: // 传输等待界面 BOOT单击:取消 → Home xTaskCreate(exit_transfer_wait_task, "boot_exit", 3072, NULL, 5, NULL); break; case BTN_CTX_SHARING: case BTN_CTX_RECEIVING: case BTN_CTX_UPDATE: // 传输中/更新中不响应 break; } }