Dzbj_ESP32-S3_Key/电子吧唧按键功能规划文档.md
Rdzleo 75586b3744 ESP32-S3按键版电子吧唧功能完整实现
1、按键驱动重构:从GPIO中断改为iot_button组件,支持BOOT/KEY2的单击/双击/长按6种事件;
2、新增按键导航管理器(key_nav):9种上下文状态机,统一分发按键事件到对应界面业务逻辑;
3、BLE模块改造:广播默认关闭由按键触发,新增设备间图片传输(GATT Client),支持扫描配对→MTU协商→分包发送;
4、新增6个UI界面:配对(Peiwang)、更新(Update)、发送等待(ImageShar)、接收等待(ImageReception)、发送中(Sharing)、接收中(Receiving);
5、新增电池指示器组件(battery_ui):支持多界面真实电量显示,3秒渐隐效果;
6、Home界面重构:移除手势事件,改为airhub背景图+电池指示器;
7、Img界面重构:移除触摸事件,新增删除二次确认边框机制;
8、禁用ScreenSet/ScreenChar界面(保留文件),禁用触摸初始化(保留代码可恢复);
9、sleep_mgr简化:移除ScreenSet亮度UI依赖,按键唤醒由key_nav统一处理;
10、新增9张图片资源(airhub背景、配对、传输状态、电池图标等);

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 17:35:05 +08:00

477 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Dzbj_ESP32_S3 纯电子吧唧 — 两键实现三键功能规划
> 更新日期2026-03-25
> 目标项目Dzbj_ESP32_S3纯电子吧唧无 AI 对话模式)
> 参考实现Baji_Rtc_Toy_Key宝石角按键版的 key_nav 模块
> 硬件平台ESP32-S3-N16R8与 Baji_Rtc_Toy 开发板相同
---
## 一、硬件现状
### 1.1 物理按键
| 按键 | GPIO | 电气特性 | 可用于业务控制 |
|------|------|---------|:---:|
| **BOOT** | GPIO0 | 低电平有效,内部上拉 | ✅ |
| **KEY2** | GPIO4 | 低电平有效,内部上拉 | ✅ |
| **SW1** | — | 物理电源开关,未接 MCU GPIO | ❌ |
> SW1 为纯硬件断电开关,无法被软件检测,不参与业务逻辑。
### 1.2 当前按键驱动(待重构)
当前使用 GPIO ISR + 手动去抖200ms 时间戳),**只支持"按下"一种事件**
```c
// 当前驱动button.c
gpio_isr_handler() xQueueSend btn_task() 200ms去抖 回调
```
**问题**2 个按键 × 1 种事件 = **只有 2 种操作**,无法覆盖宝石角的全部功能。
---
## 二、宝石角3 键)功能分析
宝石角按键版有 3 个按键:**BOOT**、**KEY1**、**KEY2**。通过 `iot_button` 组件支持单击/双击/长按,加上 key_nav 导航管理器,实现了完整的无触摸交互。
### 2.1 宝石角各界面按键行为
| 界面 | BOOT 单击 | KEY 单击 | BOOT 双击 | KEY 长按 |
|------|----------|---------|----------|---------|
| **Home** | → Set | → Img | — | — |
| **Img浏览** | → Home | 下一张图片 | → Home | — |
| **Img删除确认** | 确认删除 | 取消(退出删除模式) | → Home | — |
| **Set无焦点** | → Home | 选中第一个图标 | → Home | — |
| **Set有焦点** | 执行选中功能 | 切换到下一个焦点 | → Home | — |
| **Set亮度调节** | 亮度 +10% | 亮度 -10% | → Home | 退出调节模式 |
| **应援灯** | 切换颜色 | 退出 → Set | → Home | — |
### 2.2 宝石角 Set 界面焦点系统
焦点通过蓝色边框高亮(`#2196F3`3pxKEY 单击循环切换:
```
节能 → 应援灯 → 删除 → 亮度 → 节能 → ...
```
BOOT 单击执行当前焦点对应的功能(进入应援灯、进入亮度调节、进入删除模式等)。
---
## 三、两键方案设计
### 3.1 核心思路
`iot_button` 组件替换当前 GPIO ISR 驱动,为 2 个按键获得 **6 种事件**
| 按键 | 单击 | 双击 | 长按 |
|------|------|------|------|
| **BOOT (GPIO0)** | ✅ | ✅ | ✅ |
| **KEY2 (GPIO4)** | ✅ | ✅ | ✅ |
2 键 × 3 种事件 = **6 种操作**,完全覆盖宝石角 3 键的功能需求。
### 3.2 按键角色定义
| 按键 | 角色 | 助记 |
|------|------|------|
| **BOOT** | 确认 / 执行 / 返回 | 右手拇指(主操作) |
| **KEY2** | 导航 / 切换 / 浏览 | 左手拇指(辅助操作) |
### 3.3 iot_button 时间参数
```c
button_config_t btn_cfg = {
.long_press_time = 2000, // 长按阈值 2 秒
.short_press_time = 0, // 双击检测窗口 180ms默认值
};
```
| 事件 | 触发条件 |
|------|---------|
| 单击 | 按下后 180ms 内无第二次按下 |
| 双击 | 180ms 内连续按下两次 |
| 长按 | 持续按住超过 2 秒 |
---
## 四、完整按键映射表
### 4.1 屏幕关闭(低功耗模式)
| 按键 | 任何操作 | 行为 |
|------|---------|------|
| BOOT | 按下 | 唤醒屏幕,恢复亮度,不触发业务 |
| KEY2 | 按下 | 唤醒屏幕,恢复亮度,不触发业务 |
> 唤醒后回到休眠前的界面。
### 4.2 Home 界面
| 按键 | 事件 | 行为 | 说明 |
|------|------|------|------|
| BOOT | 单击 | → **Set** 界面 | 进入设置 |
| KEY2 | 单击 | → **Img** 界面 | 进入图片浏览 |
| BOOT | 长按 | — | 预留(可用于关机提示/重置等) |
| KEY2 | 长按 | — | 预留 |
### 4.3 Img 界面(图片浏览)
| 按键 | 事件 | 行为 | 说明 |
|------|------|------|------|
| KEY2 | 单击 | **下一张**图片 | 自动跳过解码失败的无效图片 |
| KEY2 | 双击 | **上一张**图片 | 自动跳过无效图片 |
| BOOT | 单击 | → **Home** 界面 | 返回主界面 |
| BOOT | 长按 | 进入 **删除确认** 模式 | 替代宝石角的 Set→删除→Img 路径 |
> 宝石角用左右两个按键分别切换上/下一张。本方案用 KEY2 单击=下一张、双击=上一张,一个按键两个方向。
### 4.4 Img 界面(删除确认模式)
BOOT 长按触发后,显示删除确认 UIContainerDle
| 按键 | 事件 | 行为 | 说明 |
|------|------|------|------|
| BOOT | 单击 | **确认删除**当前图片 | 删除后自动显示下一张 |
| KEY2 | 单击 | **取消**,退出删除模式 | 隐藏 ContainerDle |
### 4.5 Set 界面(焦点导航模式)
进入 Set 界面后默认无焦点。KEY2 单击开始焦点循环:
```
无焦点 → 节能 → 应援灯 → 亮度 → (无焦点) → 节能 → ...
```
> 注:相比宝石角去掉了"删除"焦点项。删除功能已移到 Img 界面的 BOOT 长按中,避免 Set→Img 的复杂跳转。
| 按键 | 事件 | 行为 | 说明 |
|------|------|------|------|
| KEY2 | 单击 | **切换焦点**到下一个图标 | 蓝色边框高亮 |
| BOOT | 单击(无焦点时) | → **Home** 界面 | 返回 |
| BOOT | 单击(有焦点时) | **执行**选中功能 | 进入节能/应援灯/亮度调节 |
| BOOT | 单击(在焦点项上) | → 对应功能 | 详见下方 |
**各焦点项的执行行为**
| 焦点项 | BOOT 单击执行 |
|--------|-------------|
| 节能 | 切换节能模式开/关(亮度降至 10% + 10s 超时熄屏) |
| 应援灯 | 进入应援灯全屏模式 |
| 亮度 | 进入亮度调节模式 |
### 4.6 Set 界面(亮度调节模式)
从焦点"亮度"按 BOOT 进入,滑块高亮显示:
| 按键 | 事件 | 行为 | 说明 |
|------|------|------|------|
| BOOT | 单击 | 亮度 **+10%** | 上限 100% |
| KEY2 | 单击 | 亮度 **-10%** | 下限 10% |
| KEY2 | 长按 | **退出**亮度调节模式 | 回到焦点导航 |
### 4.7 应援灯全屏模式
从焦点"应援灯"按 BOOT 进入:
| 按键 | 事件 | 行为 | 说明 |
|------|------|------|------|
| BOOT | 单击 | **切换颜色**(红→绿→蓝→循环) | LCD 硬件级写 GRAM |
| KEY2 | 单击 | **退出** → Set 界面 | 恢复原亮度 |
### 4.8 ScreenChar 角色动画界面(如有)
| 按键 | 事件 | 行为 | 说明 |
|------|------|------|------|
| BOOT | 单击 | 切换动画播放/暂停 | 保持原有逻辑 |
---
## 五、与宝石角功能对照
### 5.1 功能覆盖对比
| 功能 | 宝石角3 键) | 本方案2 键) | 实现方式 |
|------|:---:|:---:|------|
| 界面导航Home/Img/Set | ✅ | ✅ | BOOT=确认/返回KEY2=切换 |
| 下一张图片 | ✅ KEY 单击 | ✅ KEY2 单击 | 相同 |
| 上一张图片 | ✅ 另一个KEY | ✅ KEY2 双击 | 双击替代第三键 |
| 删除图片 | ✅ Set→删除→Img | ✅ Img 中 BOOT 长按 | 更直接,少一步跳转 |
| Set 焦点导航 | ✅ | ✅ | 相同 |
| 亮度调节 | ✅ | ✅ | 相同 |
| 应援灯 | ✅ | ✅ | 相同 |
| 节能开关 | ✅ | ✅ | 相同 |
| 低功耗唤醒 | ✅ | ✅ | 两键均可唤醒 |
| 跳过无效图片 | ✅ | ✅ | 自动跳过(已实现) |
### 5.2 操作差异
| 操作 | 宝石角 | 本方案 | 差异说明 |
|------|--------|--------|---------|
| 上一张图片 | 独立按键单击 | KEY2 双击 | 需要快速双击,操作稍慢 |
| 删除图片入口 | Set 界面焦点 | Img 界面 BOOT 长按 | 更直接,浏览时即可删除 |
| 退出亮度调节 | KEY 长按 | KEY2 长按 | 相同 |
---
## 六、界面导航图
```
┌──────────────────┐
│ Home主界面
│ │
│ BOOT→Set │
│ KEY2→Img │
└───┬──────────┬───┘
│ │
BOOT单击 │ │ KEY2单击
▼ ▼
┌─────────────────┐ ┌──────────────────────┐
│ Set设置界面 │ │ Img图片浏览界面
│ │ │ │
│ KEY2焦点切换 │ │ KEY2单击下一张 │
│ BOOT执行/返回 │ │ KEY2双击上一张 │
│ │ │ BOOT单击返回Home │
│ ┌────────────┐ │ │ BOOT长按删除确认 │
│ │焦点项: │ │ │ │
│ │ 节能 │ │ │ ┌──────────────────┐ │
│ │ 应援灯 ──┐ │ │ │ │ 删除确认模式 │ │
│ │ 亮度 ──┐ │ │ │ │ │ BOOT确认删除 │ │
│ └───────┼─┼─┘ │ │ │ KEY2取消 │ │
└──────────┼─┼───┘ │ └──────────────────┘ │
│ │ └──────────────────────┘
│ │
┌─────────┘ └──────────┐
▼ ▼
┌─────────────────┐ ┌──────────────────┐
│ 亮度调节模式 │ │ 应援灯全屏模式 │
│ │ │ │
│ BOOT亮度+10% │ │ BOOT切换颜色 │
│ KEY2亮度-10% │ │ KEY2退出→Set │
│ KEY2长按退出 │ │ │
└─────────────────┘ └──────────────────┘
```
---
## 七、技术实现要点
### 7.1 按键驱动重构GPIO ISR → iot_button
**当前**button.c
```c
// GPIO 中断 + 手动 200ms 去抖,只支持"按下"
gpio_isr_handler_add(PIN_BTN_BOOT, gpio_isr_handler, ...);
```
**目标**(使用 iot_button 组件):
```c
#include "iot_button.h"
button_config_t btn_cfg = {
.long_press_time = 2000,
.short_press_time = 0, // 默认 180ms 双击窗口
};
button_gpio_config_t gpio_cfg = {
.gpio_num = PIN_BTN_BOOT,
.active_level = 0, // 低电平有效
};
button_handle_t boot_handle;
iot_button_new_gpio_device(&btn_cfg, &gpio_cfg, &boot_handle);
// 注册三种事件
iot_button_register_cb(boot_handle, BUTTON_SINGLE_CLICK, NULL, boot_click_cb, NULL);
iot_button_register_cb(boot_handle, BUTTON_DOUBLE_CLICK, NULL, boot_dblclick_cb, NULL);
iot_button_register_cb(boot_handle, BUTTON_LONG_PRESS_START, NULL, boot_longpress_cb, NULL);
```
**依赖添加**idf_component.yml
```yaml
dependencies:
button: ">=3.2.0"
```
### 7.2 key_nav 导航管理器
从宝石角移植 key_nav 模块,核心结构:
```c
// 导航上下文(根据当前界面和模式决定按键行为)
typedef enum {
NAV_CTX_HOME, // Home 界面
NAV_CTX_IMG, // Img 浏览
NAV_CTX_IMG_DELETE, // Img 删除确认
NAV_CTX_SET, // Set 焦点导航
NAV_CTX_SET_BRIGHTNESS, // 亮度调节
NAV_CTX_FLASHLIGHT, // 应援灯
} nav_context_t;
// Set 焦点项(比宝石角少了"删除"项)
typedef enum {
SET_FOCUS_NONE = -1,
SET_FOCUS_LOW_POWER = 0,
SET_FOCUS_FLASHLIGHT,
SET_FOCUS_BRIGHTNESS,
SET_FOCUS_COUNT,
} set_focus_item_t;
```
### 7.3 回调中禁止 vTaskDelay关键约束
`iot_button` 回调在 `esp_timer` 任务中执行,必须派发到独立任务:
```c
static void boot_click_cb(void *arg, void *data) {
// ❌ 禁止vTaskDelay(pdMS_TO_TICKS(100));
// ✅ 正确:派发到独立任务
xTaskCreate(nav_boot_click_task, "nav_boot", 3072, NULL, 5, NULL);
}
static void nav_boot_click_task(void *arg) {
// 这里可以安全地 vTaskDelay、修改 LVGL 等
if (sleep_mgr_is_screen_off()) {
sleep_mgr_notify_activity();
} else {
// 根据当前 nav_context 执行对应操作
handle_boot_click();
}
vTaskDelete(NULL);
}
```
### 7.4 焦点高亮样式
```c
#define FOCUS_BORDER_COLOR 0x2196F3 // Material Blue
#define FOCUS_BORDER_WIDTH 3
static void set_focus_border(lv_obj_t *obj, bool active) {
lvgl_port_lock(0);
if (active) {
lv_obj_set_style_border_color(obj, lv_color_hex(FOCUS_BORDER_COLOR), LV_PART_MAIN);
lv_obj_set_style_border_width(obj, FOCUS_BORDER_WIDTH, LV_PART_MAIN);
lv_obj_set_style_border_opa(obj, LV_OPA_COVER, LV_PART_MAIN);
} else {
lv_obj_set_style_border_opa(obj, LV_OPA_TRANSP, LV_PART_MAIN);
}
lvgl_port_unlock();
}
```
### 7.5 应援灯颜色切换优化
绕过 LVGL 分band渲染直接写 LCD GRAM瞬间切换无从上到下刷新感
```c
void flashlight_switch_color(void) {
lcd_disp_on_off(false); // DISPOFFLCD 停止输出
lcd_fill_color(new_color); // 直接写 GRAM~35ms
lcd_disp_on_off(true); // DISPON瞬间恢复画面已完整
}
```
---
## 八、文件变更清单
### 8.1 需要重构的文件
| 文件 | 变更内容 |
|------|---------|
| `main/button/button.c` | GPIO ISR → iot_button 组件,支持单击/双击/长按 |
| `main/button/include/button.h` | 新增事件类型枚举,回调签名变更 |
| `main/main.c` | 移除 `boot_btn_handler()`~65 行),集成 `key_nav_init()` |
| `main/ui/screens/ui_ScreenHome.c` | 移除手势事件函数 |
| `main/ui/screens/ui_ScreenImg.c` | 移除手势/点击事件,保留 SCREEN_LOADED |
| `main/ui/screens/ui_ScreenSet.c` | 移除手势/滑块/点击事件 |
| `main/sleep_mgr/sleep_mgr.c` | 移除按键回调注册,由 key_nav 统一处理 |
| `main/CMakeLists.txt` | 添加 key_nav 源文件和头文件路径 |
| `main/idf_component.yml` | 添加 `button: ">=3.2.0"` 依赖 |
### 8.2 需要新增的文件
| 文件 | 功能 |
|------|------|
| `main/key_nav/key_nav.c` | 按键导航管理器(从宝石角移植,适配两键方案) |
| `main/key_nav/include/key_nav.h` | 导航上下文枚举 + 焦点状态 |
### 8.3 需要新增的函数
| 函数 | 文件 | 功能 |
|------|------|------|
| `lcd_fill_color(uint32_t color)` | `main/lcd/lcd.c` | 直接写 GRAM 填充颜色(应援灯优化) |
| `flashlight_switch_color()` | `ui_ScreenSet.c` | 应援灯颜色切换(调用 lcd_fill_color |
---
## 九、实施步骤
1. **添加 iot_button 依赖**`idf_component.yml`
2. **重构 button 模块** → 替换 GPIO ISR 为 iot_button支持 6 种事件
3. **新建 key_nav 模块** → 从宝石角移植调整为两键方案KEY2 双击=上一张BOOT 长按=删除)
4. **移除触摸事件** → 各 Screen 文件中删除手势/点击回调和 `lv_obj_add_event_cb` 注册
5. **适配 main.c** → 删除旧的 `boot_btn_handler()`,添加 `key_nav_init()`
6. **适配 sleep_mgr** → 唤醒逻辑由 key_nav 统一处理
7. **添加 lcd_fill_color()** → 应援灯硬件级颜色切换
8. **更新 CMakeLists.txt** → 添加新源文件和头文件路径
9. **编译测试**`idf.py build`
10. **逐功能验证** → 按第十章清单测试
---
## 十、测试验证清单
### 10.1 基础功能
- [ ] BOOT 单击 / 双击 / 长按 事件均可正确触发
- [ ] KEY2 单击 / 双击 / 长按 事件均可正确触发
- [ ] 屏幕关闭时任意按键唤醒,不触发业务
### 10.2 Home 界面
- [ ] BOOT 单击 → 进入 Set
- [ ] KEY2 单击 → 进入 Img
### 10.3 Img 界面
- [ ] KEY2 单击 → 下一张图片(自动跳过无效图片)
- [ ] KEY2 双击 → 上一张图片(自动跳过无效图片)
- [ ] BOOT 单击 → 返回 Home
- [ ] BOOT 长按 → 显示删除确认
- [ ] 删除确认中 BOOT 单击 → 删除成功
- [ ] 删除确认中 KEY2 单击 → 取消
### 10.4 Set 界面
- [ ] KEY2 单击 → 焦点循环切换(蓝色边框)
- [ ] BOOT 单击(无焦点)→ 返回 Home
- [ ] BOOT 单击(节能焦点)→ 切换节能模式
- [ ] BOOT 单击(应援灯焦点)→ 进入应援灯
- [ ] BOOT 单击(亮度焦点)→ 进入亮度调节
### 10.5 亮度调节
- [ ] BOOT 单击 → 亮度 +10%(上限 100%
- [ ] KEY2 单击 → 亮度 -10%(下限 10%
- [ ] KEY2 长按 → 退出亮度调节模式
### 10.6 应援灯
- [ ] BOOT 单击 → 颜色切换(红→绿→蓝→红)
- [ ] KEY2 单击 → 退出回到 Set
### 10.7 低功耗
- [ ] 10 秒无操作 → 屏幕熄灭
- [ ] 任意按键 → 唤醒,显示熄屏前的界面
- [ ] 唤醒时不闪烁(先渲染再恢复亮度)
### 10.8 稳定性
- [ ] 快速连续按键不崩溃(防抖正常)
- [ ] 连续切换 20+ 张图片无内存泄漏
- [ ] 长时间运行 1 小时无崩溃