1、按键驱动重构:dzbj_button改为iot_button组件,支持BOOT/KEY2单击/双击/长按,回调通过xTaskCreate派发避免阻塞esp_timer; 2、新增按键导航管理器(key_nav):9种上下文状态机,统一分发按键事件到对应界面; 3、BLE改造:广播默认关闭由按键触发启停,新增设备间GATT Client图片传输(扫描→连接→MTU协商→分包发送),WRITE_EVT区分APP/设备传图跳转不同界面; 4、新增模式切换按键抑制:NVS标志+2秒时间窗,防止AI↔吧唧切换时幽灵按键触发配网或白屏; 5、AI模式BOOT单击回调添加抑制检查,吧唧模式key_nav_boot_click同步添加; 6、新增6个UI界面:配对(Peiwang)、更新(Update)、发送等待/接收等待/发送中/接收中; 7、新增电池指示器组件(battery_ui):多界面真实电量显示+3秒渐隐; 8、Home/Img界面重构:移除触摸手势事件,改为airhub背景+按键导航; 9、禁用触摸(DZBJ_ENABLE_TOUCH=0),保留代码可恢复; 10、sleep_mgr简化:按键唤醒由key_nav统一处理; Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
310 lines
9.6 KiB
C
310 lines
9.6 KiB
C
// 按键导航管理器
|
||
// 根据当前界面上下文分发BOOT/KEY2的单击/双击/长按事件
|
||
// 所有回调通过xTaskCreate派发到独立任务,避免阻塞esp_timer
|
||
|
||
#include "key_nav.h"
|
||
#include "dzbj_button.h"
|
||
#include "dzbj_ble.h"
|
||
#include "ble_transfer.h"
|
||
#include "device_mode.h"
|
||
#include "esp_log.h"
|
||
#include "freertos/FreeRTOS.h"
|
||
#include "freertos/task.h"
|
||
#include "lvgl.h"
|
||
#include "esp_lvgl_port.h"
|
||
|
||
// UI 头文件
|
||
#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_ScreenUpdate.h"
|
||
#include "ui/screens/ui_ScreenImageShar.h"
|
||
#include "ui/screens/ui_ScreenImageReception.h"
|
||
#include "ui/screens/ui_ScreenSharing.h"
|
||
#include "ui/screens/ui_ScreenReceiving.h"
|
||
#include "ui/battery_ui.h"
|
||
#include "sleep_mgr/include/sleep_mgr.h"
|
||
#include "driver/gpio.h"
|
||
|
||
// 图片管理函数(pages.c)
|
||
extern void init_spiffs_image_list(void);
|
||
extern const char* get_next_image(void);
|
||
extern const char* get_prev_image(void);
|
||
extern const char* get_current_image(void);
|
||
extern bool update_ui_ImgBle(const char *img_name);
|
||
extern bool delete_current_image(void);
|
||
|
||
static const char *TAG = "KEY_NAV";
|
||
|
||
static nav_context_t current_ctx = NAV_CTX_HOME;
|
||
|
||
void key_nav_set_context(nav_context_t ctx) {
|
||
current_ctx = ctx;
|
||
ESP_LOGI(TAG, "导航上下文切换: %d", ctx);
|
||
}
|
||
|
||
nav_context_t key_nav_get_context(void) {
|
||
return current_ctx;
|
||
}
|
||
|
||
// === 界面切换辅助(需要LVGL锁) ===
|
||
|
||
static void nav_switch_screen(lv_obj_t **screen, void (*init_func)(void), nav_context_t new_ctx) {
|
||
if (lvgl_port_lock(100)) {
|
||
_ui_screen_change(screen, LV_SCR_LOAD_ANIM_NONE, 0, 0, init_func);
|
||
lvgl_port_unlock();
|
||
}
|
||
key_nav_set_context(new_ctx);
|
||
}
|
||
|
||
// === BOOT按键处理 ===
|
||
|
||
void key_nav_boot_click(void) {
|
||
// 模式切换重启后按键抑制(组合键释放产生的幽灵单击)
|
||
if (device_mode_in_switch_suppress()) {
|
||
ESP_LOGI(TAG, "模式切换按键抑制期,忽略BOOT单击");
|
||
return;
|
||
}
|
||
if (sleep_mgr_is_screen_off()) {
|
||
sleep_mgr_notify_activity();
|
||
return;
|
||
}
|
||
sleep_mgr_notify_activity();
|
||
|
||
switch (current_ctx) {
|
||
case NAV_CTX_HOME:
|
||
// Home界面BOOT单击 → Img界面
|
||
nav_switch_screen(&ui_ScreenImg, &ui_ScreenImg_screen_init, NAV_CTX_IMG);
|
||
break;
|
||
|
||
case NAV_CTX_IMG:
|
||
// Img界面BOOT单击 → 下一张图片
|
||
if (lvgl_port_lock(100)) {
|
||
for (int try = 0; try < 10; try++) {
|
||
const char *next_img = get_next_image();
|
||
if (!next_img) break;
|
||
if (update_ui_ImgBle(next_img)) break;
|
||
ESP_LOGW(TAG, "跳过无效图片,继续下一张");
|
||
}
|
||
battery_ui_show_briefly();
|
||
lvgl_port_unlock();
|
||
}
|
||
break;
|
||
|
||
case NAV_CTX_IMG_DELETE:
|
||
// 第一次按BOOT → 显示白色边框进入二次确认
|
||
if (lvgl_port_lock(100)) {
|
||
ui_ScreenImg_show_delete_confirm_border();
|
||
lvgl_port_unlock();
|
||
}
|
||
key_nav_set_context(NAV_CTX_IMG_DELETE_CONFIRM);
|
||
break;
|
||
|
||
case NAV_CTX_IMG_DELETE_CONFIRM:
|
||
// 第二次按BOOT → 执行删除
|
||
if (lvgl_port_lock(100)) {
|
||
if (delete_current_image()) {
|
||
ui_ScreenImg_hide_delete_container();
|
||
const char *next_img = get_current_image();
|
||
if (next_img) {
|
||
update_ui_ImgBle(next_img);
|
||
}
|
||
} else {
|
||
ui_ScreenImg_hide_delete_container();
|
||
}
|
||
lvgl_port_unlock();
|
||
}
|
||
key_nav_set_context(NAV_CTX_IMG);
|
||
break;
|
||
|
||
case NAV_CTX_PEIWANG:
|
||
// 配对界面任意键 → 关蓝牙返回Home
|
||
dzbj_ble_stop();
|
||
nav_switch_screen(&ui_ScreenHome, &ui_ScreenHome_screen_init, NAV_CTX_HOME);
|
||
break;
|
||
|
||
case NAV_CTX_IMAGE_SHAR:
|
||
case NAV_CTX_IMAGE_RECEPTION:
|
||
// 等待配对界面任意键 → 取消传输 + 返回Home
|
||
ble_transfer_cancel();
|
||
nav_switch_screen(&ui_ScreenHome, &ui_ScreenHome_screen_init, NAV_CTX_HOME);
|
||
break;
|
||
|
||
case NAV_CTX_UPDATE:
|
||
case NAV_CTX_SHARING:
|
||
case NAV_CTX_RECEIVING:
|
||
// 这些界面暂不响应BOOT单击(等待自动跳转)
|
||
break;
|
||
}
|
||
}
|
||
|
||
void key_nav_boot_double_click(void) {
|
||
if (sleep_mgr_is_screen_off()) {
|
||
sleep_mgr_notify_activity();
|
||
return;
|
||
}
|
||
sleep_mgr_notify_activity();
|
||
|
||
switch (current_ctx) {
|
||
case NAV_CTX_IMG:
|
||
// Img界面BOOT双击 → 上一张图片
|
||
if (lvgl_port_lock(100)) {
|
||
for (int try = 0; try < 10; try++) {
|
||
const char *prev_img = get_prev_image();
|
||
if (!prev_img) break;
|
||
if (update_ui_ImgBle(prev_img)) break;
|
||
ESP_LOGW(TAG, "跳过无效图片,继续上一张");
|
||
}
|
||
battery_ui_show_briefly();
|
||
lvgl_port_unlock();
|
||
}
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
void key_nav_boot_long_press(void) {
|
||
if (sleep_mgr_is_screen_off()) {
|
||
sleep_mgr_notify_activity();
|
||
return;
|
||
}
|
||
sleep_mgr_notify_activity();
|
||
|
||
// 组合键保护:如果KEY2同时按下,说明是模式切换组合键,跳过删除逻辑
|
||
if (gpio_get_level(PIN_BTN_KEY2) == 0) {
|
||
ESP_LOGI(TAG, "BOOT+KEY2组合键检测,跳过BOOT长按业务");
|
||
return;
|
||
}
|
||
|
||
switch (current_ctx) {
|
||
case NAV_CTX_IMG:
|
||
// Img界面BOOT长按 → 显示删除确认
|
||
if (lvgl_port_lock(100)) {
|
||
ui_ScreenImg_show_delete_container();
|
||
lvgl_port_unlock();
|
||
}
|
||
key_nav_set_context(NAV_CTX_IMG_DELETE);
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
// === KEY2按键处理 ===
|
||
|
||
void key_nav_key2_click(void) {
|
||
if (sleep_mgr_is_screen_off()) {
|
||
sleep_mgr_notify_activity();
|
||
return;
|
||
}
|
||
sleep_mgr_notify_activity();
|
||
|
||
switch (current_ctx) {
|
||
case NAV_CTX_HOME:
|
||
// Home界面KEY2单击 → 开蓝牙 + 配对界面
|
||
dzbj_ble_start();
|
||
nav_switch_screen(&ui_ScreenPeiwang, &ui_ScreenPeiwang_screen_init, NAV_CTX_PEIWANG);
|
||
break;
|
||
|
||
case NAV_CTX_IMG:
|
||
// Img界面KEY2单击 → 返回Home
|
||
if (lvgl_port_lock(100)) {
|
||
ui_ScreenImg_hide_delete_container();
|
||
lvgl_port_unlock();
|
||
}
|
||
nav_switch_screen(&ui_ScreenHome, &ui_ScreenHome_screen_init, NAV_CTX_HOME);
|
||
break;
|
||
|
||
case NAV_CTX_IMG_DELETE:
|
||
case NAV_CTX_IMG_DELETE_CONFIRM:
|
||
// 删除确认/二次确认 KEY2单击 → 取消
|
||
if (lvgl_port_lock(100)) {
|
||
ui_ScreenImg_hide_delete_container();
|
||
lvgl_port_unlock();
|
||
}
|
||
key_nav_set_context(NAV_CTX_IMG);
|
||
break;
|
||
|
||
case NAV_CTX_PEIWANG:
|
||
// 配对界面KEY2单击 → 关蓝牙返回Home
|
||
dzbj_ble_stop();
|
||
nav_switch_screen(&ui_ScreenHome, &ui_ScreenHome_screen_init, NAV_CTX_HOME);
|
||
break;
|
||
|
||
case NAV_CTX_IMAGE_SHAR:
|
||
case NAV_CTX_IMAGE_RECEPTION:
|
||
// 等待配对界面KEY2单击 → 取消传输 + 返回Home
|
||
ble_transfer_cancel();
|
||
nav_switch_screen(&ui_ScreenHome, &ui_ScreenHome_screen_init, NAV_CTX_HOME);
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
void key_nav_key2_double_click(void) {
|
||
if (sleep_mgr_is_screen_off()) {
|
||
sleep_mgr_notify_activity();
|
||
return;
|
||
}
|
||
sleep_mgr_notify_activity();
|
||
|
||
switch (current_ctx) {
|
||
case NAV_CTX_HOME:
|
||
case NAV_CTX_IMG:
|
||
// KEY2双击 → 接收方等待配对界面 + 启动接收模式
|
||
nav_switch_screen(&ui_ScreenImageReception, &ui_ScreenImageReception_screen_init, NAV_CTX_IMAGE_RECEPTION);
|
||
ble_transfer_start_receive();
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
void key_nav_key2_long_press(void) {
|
||
if (sleep_mgr_is_screen_off()) {
|
||
sleep_mgr_notify_activity();
|
||
return;
|
||
}
|
||
sleep_mgr_notify_activity();
|
||
|
||
// 组合键保护:如果BOOT同时按下,说明是模式切换组合键,跳过分享逻辑
|
||
if (gpio_get_level(PIN_BTN_BOOT) == 0) {
|
||
ESP_LOGI(TAG, "BOOT+KEY2组合键检测,跳过KEY2长按业务");
|
||
return;
|
||
}
|
||
|
||
switch (current_ctx) {
|
||
case NAV_CTX_HOME:
|
||
// Home界面KEY2长按 → 初始化图片列表 + 直接进分享等待
|
||
init_spiffs_image_list();
|
||
nav_switch_screen(&ui_ScreenImageShar, &ui_ScreenImageShar_screen_init, NAV_CTX_IMAGE_SHAR);
|
||
ble_transfer_start_send();
|
||
break;
|
||
|
||
case NAV_CTX_IMG:
|
||
// Img界面KEY2长按 → 发送方等待配对(分享当前图片)
|
||
nav_switch_screen(&ui_ScreenImageShar, &ui_ScreenImageShar_screen_init, NAV_CTX_IMAGE_SHAR);
|
||
ble_transfer_start_send();
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
// === 初始化(注册KEY2按键回调,BOOT由board类注册) ===
|
||
|
||
void key_nav_init(void) {
|
||
// KEY2按键回调由dzbj_button模块注册
|
||
// BOOT按键回调由board类的InitializeBadgeModeButtons注册
|
||
key_nav_set_context(NAV_CTX_HOME);
|
||
ESP_LOGI(TAG, "按键导航初始化完成");
|
||
}
|