Compare commits
7 Commits
55bbe951fe
...
68dd7e95ab
| Author | SHA1 | Date | |
|---|---|---|---|
| 68dd7e95ab | |||
| 52546e35fe | |||
| 64e207df72 | |||
| b6a871a3b9 | |||
| 6c348c600c | |||
| ebe62c5284 | |||
| 73e62c4dfd |
Vendored
+8
-6
@@ -1,19 +1,21 @@
|
||||
{
|
||||
"C_Cpp.intelliSenseEngine": "default",
|
||||
"idf.extensionActivationMode": "always",
|
||||
"idf.openOcdConfigs": [
|
||||
"board/esp32s3-builtin.cfg"
|
||||
],
|
||||
"idf.openOcdConfigs": ["board/esp32s3-builtin.cfg"],
|
||||
"idf.port": "/dev/ttyACM0",
|
||||
"idf.currentSetup": "C:\\esp\\v6.0.1\\esp-idf",
|
||||
"idf.toolsPath": "C:\\Espressif",
|
||||
"idf.pythonBinPathWin": "C:\\Espressif\\tools\\python\\v6.0.1\\venv\\Scripts\\python.exe",
|
||||
"idf.customExtraVars": {
|
||||
"IDF_TARGET": "esp32s3"
|
||||
"IDF_TARGET": "esp32s3",
|
||||
"IDF_TOOLS_PATH": "C:\\Espressif",
|
||||
"ESP_IDF_VERSION": "6.0.1"
|
||||
},
|
||||
"clangd.path": "C:\\Espressif\\tools\\xtensa-esp-elf\\esp-15.2.0_20251204\\esp-clang/bin/clangd",
|
||||
"clangd.path": "C:\\Espressif\\tools\\esp-clang\\esp-20.1.1_20250829\\esp-clang\\bin\\clangd.exe",
|
||||
"clangd.arguments": [
|
||||
"--background-index",
|
||||
"--query-driver=**",
|
||||
"--compile-commands-dir=C:\\Users\\yulinling\\work\\work-hardware\\HeavenlyHeroStar\\build"
|
||||
"--compile-commands-dir=d:\\work\\work-hardware\\HeavenlyHeroStar\\build"
|
||||
],
|
||||
"idf.flashType": "UART",
|
||||
"idf.portWin": "COM5"
|
||||
|
||||
@@ -0,0 +1,473 @@
|
||||
# BLE HID 键盘实现规格
|
||||
|
||||
> 项目:HeavenlyHeroStar
|
||||
> 目标芯片:ESP32-S3
|
||||
> ESP-IDF:6.0.1
|
||||
> BLE 栈:NimBLE(外设模式)
|
||||
> 状态:待实现
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
### 1.1 现状
|
||||
|
||||
当前 `main/services/ble_manager.c` 实现为自定义 GATT 外设:
|
||||
|
||||
- 服务 UUID:`0xFFF0`
|
||||
- 特征 UUID:`0xFFF1`(Read / Write)
|
||||
- 无 Notify / HID 能力
|
||||
- 电脑端无法以系统键盘方式接收按键
|
||||
|
||||
### 1.2 目标
|
||||
|
||||
将开发板实现为标准 **BLE HID 键盘(HOGP)**,使 Windows / macOS / Linux 在蓝牙配对后,将设备识别为键盘,**无需安装 PC 端接收程序**。
|
||||
|
||||
扩展目标(后续阶段):
|
||||
|
||||
- 快捷键映射保存在设备 NVS
|
||||
- 通过网页或 Web BLE 配置快捷键
|
||||
- 物理按键(BOOT / KEY)及触摸屏 UI 触发 HID 报告
|
||||
|
||||
### 1.3 方案选型结论
|
||||
|
||||
| 方案 | 说明 | 结论 |
|
||||
|------|------|------|
|
||||
| A | 自定义 GATT Notify + PC Python 脚本 | 适合快速验证,需 PC 常驻程序 |
|
||||
| **C** | **BLE HID 键盘** | **采用**:OS 原生输入,适合 macropad 产品形态 |
|
||||
| C + 配置通道 | HID 执行 + GATT/WiFi 写 NVS | 网页配置快捷键时采用 |
|
||||
|
||||
配置与执行分离:
|
||||
|
||||
```text
|
||||
配置阶段:网页 / Web BLE / 屏上 UI → 写入 NVS 快捷键表
|
||||
使用阶段:按键 / 触摸 → 读 NVS → 发送 BLE HID Report → OS 输入
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 系统架构
|
||||
|
||||
### 2.1 模块划分
|
||||
|
||||
```text
|
||||
main/services/
|
||||
├── ble_manager.c # GAP:广播、连接、LED、bonding
|
||||
├── ble_manager.h
|
||||
├── ble_hid.c # HID GATT 服务、Report Map、发送报告
|
||||
├── ble_hid.h
|
||||
└── hid_keymap.c # 扫描码表、shortcut → modifier + keycode(可选拆分)
|
||||
```
|
||||
|
||||
### 2.2 数据流
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User as 用户
|
||||
participant Btn as BOOT/KEY/UI
|
||||
participant FW as 固件
|
||||
participant NVS as NVS
|
||||
participant HID as BLE HID
|
||||
participant OS as 操作系统
|
||||
|
||||
User->>FW: 网页/GATT 配置快捷键
|
||||
FW->>NVS: 保存映射表
|
||||
|
||||
User->>Btn: 按下物理键或 UI 按钮
|
||||
Btn->>FW: 触发事件
|
||||
FW->>NVS: 查 shortcut
|
||||
FW->>HID: keyboard report (press)
|
||||
FW->>HID: keyboard report (release)
|
||||
HID->>OS: Notify Input Report
|
||||
OS->>User: 当前焦点窗口收到快捷键
|
||||
```
|
||||
|
||||
### 2.3 GATT 服务设计
|
||||
|
||||
#### 阶段一(M2):仅 HID
|
||||
|
||||
| 服务 / 特征 | UUID | 属性 | 用途 |
|
||||
|-------------|------|------|------|
|
||||
| Human Interface Device | `0x1812` | Primary | 标准 HID 服务 |
|
||||
| HID Information | `0x2A4A` | Read | 版本信息 |
|
||||
| Report Map | `0x2A4B` | Read | USB HID 描述符 |
|
||||
| HID Control Point | `0x2A4C` | Write w/o resp | Suspend / Exit |
|
||||
| Report | `0x2A4D` | Read, Notify | **键盘 Input Report** |
|
||||
| Protocol Mode | `0x2A4E` | Read, Write | Boot / Report 协议 |
|
||||
|
||||
Report 特征需附带 **Report Reference** 描述符(Report ID = 1, Input)。
|
||||
|
||||
#### 阶段二(M5,可选):配置服务
|
||||
|
||||
| 服务 / 特征 | UUID | 属性 | 用途 |
|
||||
|-------------|------|------|------|
|
||||
| HHS Config | `0xFFF0` | Primary | 厂商配置 |
|
||||
| Config Write | `0xFFF1` | Write | 接收 shortcut blob / JSON |
|
||||
| Config Read | `0xFFF2` | Read | 读回当前配置(可选) |
|
||||
|
||||
HID 与配置服务可 **并存** 于同一 GATT 表。
|
||||
|
||||
### 2.4 广播数据
|
||||
|
||||
除设备名 `HeavenlyHeroStar`(`CONFIG_ESP_BLE_DEVICE_NAME`)外,广播包需包含 HID 服务 UUID,否则 Windows 可能无法识别为键盘:
|
||||
|
||||
```c
|
||||
static ble_uuid16_t adv_uuids16[] = { BLE_UUID16_INIT(0x1812) };
|
||||
fields.uuids16 = adv_uuids16;
|
||||
fields.num_uuids16 = 1;
|
||||
fields.uuids16_is_complete = 1;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. HID 键盘报告格式
|
||||
|
||||
### 3.1 Input Report(8 字节)
|
||||
|
||||
与 USB HID 标准键盘 boot/report 格式一致:
|
||||
|
||||
| 偏移 | 字段 | 说明 |
|
||||
|------|------|------|
|
||||
| 0 | modifier | 修饰键位图 |
|
||||
| 1 | reserved | 保留,填 0 |
|
||||
| 2–7 | keycodes[6] | 最多 6 个同时按下的键,通常仅用 keycodes[0] |
|
||||
|
||||
```c
|
||||
#define HID_MOD_LCTRL 0x01
|
||||
#define HID_MOD_LSHIFT 0x02
|
||||
#define HID_MOD_LALT 0x04
|
||||
#define HID_MOD_LGUI 0x08 /* Win / Command */
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t modifier;
|
||||
uint8_t reserved;
|
||||
uint8_t keycodes[6];
|
||||
} hid_keyboard_report_t;
|
||||
```
|
||||
|
||||
### 3.2 发送组合键流程
|
||||
|
||||
组合键必须发送 **按下** 与 **释放** 两帧,否则 OS 认为键仍按住:
|
||||
|
||||
```text
|
||||
1. report = { modifier, 0, keycode, 0, 0, 0, 0, 0 }
|
||||
2. delay 20–50 ms
|
||||
3. report = { 0, 0, 0, 0, 0, 0, 0, 0 } /* 全零 = 释放 */
|
||||
```
|
||||
|
||||
示例:`Ctrl+C`
|
||||
|
||||
```text
|
||||
按下:modifier=0x01, keycode=0x06
|
||||
释放:全零
|
||||
```
|
||||
|
||||
### 3.3 常用扫描码(Keyboard/Keypad Page 0x07)
|
||||
|
||||
| 键 | keycode |
|
||||
|----|---------|
|
||||
| C | `0x06` |
|
||||
| S | `0x16` |
|
||||
| D | `0x07` |
|
||||
| F5 | `0x3E` |
|
||||
| Enter | `0x28` |
|
||||
|
||||
完整表见 [USB HID Usage Tables](https://www.usb.org/sites/default/files/documents/hut1_3_0.pdf)。
|
||||
|
||||
### 3.4 Report Map
|
||||
|
||||
**不要手写**。从 ESP-IDF 官方 HID 示例复制 `report_map[]` 字节数组:
|
||||
|
||||
```powershell
|
||||
cd C:\esp\v6.0.1\esp-idf\examples\bluetooth\nimble
|
||||
dir /s /b *hid*
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 对外 API 规格
|
||||
|
||||
### 4.1 `ble_hid.h`
|
||||
|
||||
```c
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
int ble_hid_init(void);
|
||||
bool ble_hid_ready(void);
|
||||
|
||||
int ble_hid_key_press(uint8_t modifier, uint8_t keycode);
|
||||
int ble_hid_key_release(void);
|
||||
int ble_hid_send_combo(uint8_t modifier, uint8_t keycode);
|
||||
```
|
||||
|
||||
| 函数 | 说明 |
|
||||
|------|------|
|
||||
| `ble_hid_init` | 注册 HID GATT 服务,在 `ble_manager_init` 中调用 |
|
||||
| `ble_hid_ready` | 已连接 **且** Host 已订阅 Report Notify |
|
||||
| `ble_hid_send_combo` | 发送按下 → 延迟 → 释放,供按键/UI 调用 |
|
||||
|
||||
### 4.2 `ble_manager.h`(保留)
|
||||
|
||||
```c
|
||||
void ble_manager_init(void);
|
||||
void ble_manager_set_status_cb(ble_status_cb_t cb);
|
||||
bool ble_manager_is_connected(void);
|
||||
```
|
||||
|
||||
连接状态与 LED(`BOARD_PIN_LED`,GPIO16)逻辑保持不变。
|
||||
|
||||
### 4.3 就绪条件
|
||||
|
||||
在 `gap_event_handler` 中处理 `BLE_GAP_EVENT_SUBSCRIBE`:
|
||||
|
||||
```c
|
||||
case BLE_GAP_EVENT_SUBSCRIBE:
|
||||
if (event->subscribe.attr_handle == s_hid_report_handle) {
|
||||
s_notify_enabled = event->subscribe.cur_notify;
|
||||
}
|
||||
break;
|
||||
```
|
||||
|
||||
```c
|
||||
bool ble_hid_ready(void)
|
||||
{
|
||||
return s_connected && s_notify_enabled;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. sdkconfig 配置
|
||||
|
||||
在 `sdkconfig.defaults` 或 `idf.py menuconfig` 中确认:
|
||||
|
||||
```text
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BT_NIMBLE_ENABLED=y
|
||||
CONFIG_BT_NIMBLE_ROLE_PERIPHERAL=y
|
||||
|
||||
# Windows 重连稳定性
|
||||
CONFIG_BT_NIMBLE_SM_LEGACY=y
|
||||
CONFIG_BT_NIMBLE_SM_SC=y
|
||||
CONFIG_BT_NIMBLE_NVS_PERSIST=y
|
||||
```
|
||||
|
||||
`ble_manager_init` 中 Security Manager:
|
||||
|
||||
```c
|
||||
ble_hs_cfg.sm_io_cap = BLE_HS_IO_NO_INPUT_OUTPUT; /* Just Works */
|
||||
ble_hs_cfg.sm_bonding = 1;
|
||||
ble_hs_cfg.sm_sc = 1;
|
||||
```
|
||||
|
||||
保留 `ble_store_config_init()` 以持久化 bonding 信息。
|
||||
|
||||
可选(配置通道 / 较大 payload):
|
||||
|
||||
```text
|
||||
CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU=256
|
||||
```
|
||||
|
||||
若后续启用 WiFi + BLE 共存:
|
||||
|
||||
```text
|
||||
CONFIG_ESP_COEX_ENABLED=y
|
||||
```
|
||||
|
||||
修改后执行:
|
||||
|
||||
```powershell
|
||||
idf.py fullclean
|
||||
idf.py reconfigure
|
||||
idf.py build
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 实现步骤
|
||||
|
||||
### 阶段 0:准备与基线
|
||||
|
||||
| 步骤 | 内容 |
|
||||
|------|------|
|
||||
| 0.1 | 在 `$IDF_PATH/examples/bluetooth/nimble/` 找到 HID demo,单独编译烧录 |
|
||||
| 0.2 | Windows 配对 demo,在记事本验证能输入字符 |
|
||||
| 0.3 | 确认本工程 ESP-IDF 6.0.1、`IDF_TARGET=esp32s3`、NimBLE 已启用 |
|
||||
|
||||
**策略选择:**
|
||||
|
||||
- **阶段一**:删除现有 `0xFFF0/0xFFF1` 自定义服务,仅保留 HID(最快跑通)
|
||||
- **阶段二**:HID + 恢复 `0xFFF0` 配置服务(网页 / Web BLE)
|
||||
|
||||
### 阶段 1:新增 `ble_hid.c`
|
||||
|
||||
1. 从 IDF demo 迁移 Report Map、HID GATT 服务定义
|
||||
2. 实现 `ble_hid_init()`:注册 GATT 服务,保存 `s_hid_report_handle`
|
||||
3. 实现 `ble_hid_send_report()`:`ble_gatts_notify_custom()`
|
||||
4. 实现 `ble_hid_send_combo()`:press → `vTaskDelay(20ms)` → release
|
||||
5. 在 `CMakeLists.txt` 添加 `"services/ble_hid.c"`
|
||||
|
||||
### 阶段 2:改造 `ble_manager.c`
|
||||
|
||||
1. 移除现有 `0xFFF0/0xFFF1` 自定义 GATT(阶段一)
|
||||
2. `gatt_svc_init()` 替换为 `ble_hid_init()`
|
||||
3. 广播增加 `0x1812` 服务 UUID
|
||||
4. `gap_event_handler` 增加 `BLE_GAP_EVENT_SUBSCRIBE` 处理
|
||||
5. 保留连接 / 断开 / 重广播 / LED 逻辑
|
||||
|
||||
初始化顺序:
|
||||
|
||||
```c
|
||||
nimble_port_init();
|
||||
ble_svc_gap_init();
|
||||
ble_svc_gap_device_name_set(BLE_DEVICE_NAME);
|
||||
ble_hid_init();
|
||||
ble_hs_cfg.sync_cb = on_stack_sync;
|
||||
ble_hs_cfg.reset_cb = on_stack_reset;
|
||||
ble_hs_cfg.sm_io_cap = BLE_HS_IO_NO_INPUT_OUTPUT;
|
||||
ble_hs_cfg.sm_bonding = 1;
|
||||
ble_hs_cfg.sm_sc = 1;
|
||||
ble_store_config_init();
|
||||
nimble_port_freertos_init(nimble_host_task);
|
||||
```
|
||||
|
||||
### 阶段 3:按键触发(最小验证)
|
||||
|
||||
在 `app_main.c` 中:
|
||||
|
||||
```c
|
||||
#include "boot_button.h"
|
||||
#include "ble_hid.h"
|
||||
|
||||
static void on_boot_press(void)
|
||||
{
|
||||
if (ble_hid_ready()) {
|
||||
ble_hid_send_combo(HID_MOD_LCTRL, HID_KEY_C);
|
||||
} else {
|
||||
ESP_LOGW("app", "HID not ready");
|
||||
}
|
||||
}
|
||||
|
||||
/* ble_manager_init() 之后 */
|
||||
boot_button_init(on_boot_press);
|
||||
```
|
||||
|
||||
**验证顺序:**
|
||||
|
||||
1. 烧录,串口确认 `advertising as "HeavenlyHeroStar"`
|
||||
2. Windows:设置 → 蓝牙 → 添加设备 → 配对键盘
|
||||
3. 打开记事本并聚焦
|
||||
4. 按 BOOT:验证快捷键(建议先用 F5 等可见效果)
|
||||
|
||||
### 阶段 4:NVS 快捷键表
|
||||
|
||||
#### 4.1 数据结构
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
uint8_t source; /* 0=BOOT, 1=KEY(GPIO4), 2+=UI slot */
|
||||
uint8_t modifier;
|
||||
uint8_t keycode;
|
||||
uint8_t reserved;
|
||||
} shortcut_entry_t;
|
||||
|
||||
#define SHORTCUT_MAX 16
|
||||
```
|
||||
|
||||
#### 4.2 存储
|
||||
|
||||
```text
|
||||
NVS namespace: "shortcuts"
|
||||
Key: "map"
|
||||
Format: shortcut_entry_t[SHORTCUT_MAX] blob
|
||||
```
|
||||
|
||||
启动时加载;无有效数据时使用默认映射(例如 BOOT → F5)。
|
||||
|
||||
#### 4.3 触发
|
||||
|
||||
```c
|
||||
shortcut_entry_t *e = shortcut_find(SOURCE_BOOT);
|
||||
if (e && ble_hid_ready()) {
|
||||
ble_hid_send_combo(e->modifier, e->keycode);
|
||||
}
|
||||
```
|
||||
|
||||
### 阶段 5:网页 / 配置通道(后续)
|
||||
|
||||
| 方式 | 通道 | 说明 |
|
||||
|------|------|------|
|
||||
| WiFi SoftAP + HTTP | `esp_http_server` | PC/手机浏览器 POST 配置,需 `BOARD_WIFI_ENABLED=1` |
|
||||
| Web Bluetooth | 自定义 GATT `0xFFF0` Write | 无需 WiFi,需 Chrome/Edge |
|
||||
| 屏上 LVGL UI | 本地 | 已有触摸屏,可不依赖网页 |
|
||||
|
||||
网页仅写入 NVS;运行时仍通过 HID 发送,PC 无需安装程序。
|
||||
|
||||
---
|
||||
|
||||
## 7. 与现有代码对应关系
|
||||
|
||||
| 现有 | HID 实施后 |
|
||||
|------|------------|
|
||||
| `0xFFF0/0xFFF1` Read/Write | 阶段一删除;阶段二改为配置专用 |
|
||||
| `chr_access()` | 移至配置服务或删除 |
|
||||
| `ble_manager_is_connected()` | 保留 |
|
||||
| `notify_status()` + LED | 保留 |
|
||||
| Notify 方案 `ble_manager_send()` | 替换为 `ble_hid_send_combo()` |
|
||||
| `boot_button.c` | 已具备框架,`app_main` 中注册回调 |
|
||||
| `BOARD_WIFI_ENABLED=0` | 网页配置阶段再启用 |
|
||||
|
||||
---
|
||||
|
||||
## 8. Windows 联调清单
|
||||
|
||||
| 现象 | 可能原因 | 处理 |
|
||||
|------|----------|------|
|
||||
| 搜不到设备 | 广播未含 `0x1812` | 检查 adv fields |
|
||||
| 能连上但无按键 | 未订阅 Notify | 查 `BLE_GAP_EVENT_SUBSCRIBE`、`ble_hid_ready()` |
|
||||
| 键一直按住 | 未发 release 报告 | 发送全零 8 字节 report |
|
||||
| 配对后难重连 | bonding 未持久化 | 确认 `ble_store_config_init()`、NVS |
|
||||
| 部分键无效 | 扫描码错误 | 对照 HID Usage Table |
|
||||
| 连接后很快断开 reason=531 | 远端主动断开 | 531 = Remote User Terminated;完成配对后再测 |
|
||||
| 安全软件拦截 | 蓝牙键盘权限 | 检查杀毒 / 企业策略 |
|
||||
|
||||
---
|
||||
|
||||
## 9. 里程碑
|
||||
|
||||
| 编号 | 交付物 | 验收标准 |
|
||||
|------|--------|----------|
|
||||
| **M1** | IDF HID demo 独立跑通 | Windows 配对,记事本可输入 |
|
||||
| **M2** | `ble_hid.c` 迁入工程 | 替换自定义 GATT,编译通过 |
|
||||
| **M3** | BOOT → `ble_hid_send_combo` | 记事本 / 浏览器验证 F5 或 Ctrl+C |
|
||||
| **M4** | NVS 快捷键表 | 多按键 / UI 槽位可配置 |
|
||||
| **M5** | 配置通道(GATT 或 WiFi 网页) | 浏览器修改映射,重启后仍生效 |
|
||||
|
||||
建议严格按 M1 → M5 顺序推进,避免同时调试 HID、NVS、网页。
|
||||
|
||||
---
|
||||
|
||||
## 10. 风险与约束
|
||||
|
||||
1. **HID 不能承载配置协议**:网页改映射必须走 NVS + 独立配置通道。
|
||||
2. **配对与配置是两件事**:用户需先在 OS 中配对键盘,配置页单独访问。
|
||||
3. **安全**:配对后设备可向任意焦点窗口输入;丢失设备需用户在系统中删除配对。
|
||||
4. **资源**:ESP32-S3 + LVGL + BLE HID 可行;若 NimBLE host 任务栈不足需调大。
|
||||
5. **WiFi + BLE 共存**:启用网页配置时需额外验证射频共存与内存。
|
||||
|
||||
---
|
||||
|
||||
## 11. 参考
|
||||
|
||||
- ESP-IDF 路径:`C:\esp\v6.0.1\esp-idf\examples\bluetooth\nimble\*hid*`
|
||||
- 工程 BLE 入口:`main/services/ble_manager.c`
|
||||
- 板级 GPIO:`main/bsp/board_config.h`(BOOT=GPIO0, KEY=GPIO4, LED=GPIO16)
|
||||
- USB HID Usage Tables:Keyboard/Keypad Page (0x07)
|
||||
|
||||
---
|
||||
|
||||
## 修订记录
|
||||
|
||||
| 版本 | 日期 | 说明 |
|
||||
|------|------|------|
|
||||
| 1.0 | 2026-07-01 | 初版:BLE HID 实现步骤与架构规格 |
|
||||
+7
-2
@@ -4,14 +4,19 @@ idf_component_register(SRCS
|
||||
"bsp/display.c"
|
||||
"bsp/touch.c"
|
||||
"bsp/lvgl_port.c"
|
||||
"bsp/boot_button.c"
|
||||
"services/wifi_manager.c"
|
||||
"services/ble_manager.c"
|
||||
"services/ble_hid.c"
|
||||
"services/esp_hid_gap.c"
|
||||
"ui/ui.c"
|
||||
"ui/ui_helpers.c"
|
||||
"ui/screens/ui_Screen1.c"
|
||||
"ui/screens/ui_Screen2.c"
|
||||
"ui/widgets/ui_comp_hook.c"
|
||||
PRIV_REQUIRES
|
||||
esp_wifi
|
||||
esp_netif
|
||||
bt
|
||||
esp_hid
|
||||
nvs_flash
|
||||
esp_driver_ledc
|
||||
esp_driver_spi
|
||||
|
||||
@@ -66,4 +66,10 @@ menu "Example Configuration"
|
||||
bool "WAPI PSK"
|
||||
endchoice
|
||||
|
||||
config ESP_BLE_DEVICE_NAME
|
||||
string "BLE device name"
|
||||
default "HeavenlyHeroStar"
|
||||
help
|
||||
GAP device name advertised over BLE.
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -6,7 +6,12 @@
|
||||
#include "lvgl_port.h"
|
||||
#include "touch.h"
|
||||
#include "ui.h"
|
||||
#include "ui_handlers.h"
|
||||
#include "ble_manager.h"
|
||||
|
||||
#if BOARD_WIFI_ENABLED
|
||||
#include "wifi_manager.h"
|
||||
#endif
|
||||
|
||||
static const char *TAG = "app";
|
||||
|
||||
@@ -35,6 +40,13 @@ void app_main(void)
|
||||
ui_init();
|
||||
lvgl_port_unlock();
|
||||
|
||||
ui_handlers_init();
|
||||
|
||||
#if BOARD_WIFI_ENABLED
|
||||
ESP_LOGI(TAG, "Starting WiFi");
|
||||
wifi_manager_init();
|
||||
#endif
|
||||
|
||||
ESP_LOGI(TAG, "Starting BLE");
|
||||
ble_manager_init();
|
||||
}
|
||||
|
||||
+41
-1
@@ -1,4 +1,44 @@
|
||||
#include "ui_handlers.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "ble_hid.h"
|
||||
#include "boot_button.h"
|
||||
#include "esp_log.h"
|
||||
#include "ui_events.h"
|
||||
|
||||
/* Add callback implementations here when SquareLine Studio generates events. */
|
||||
static const char *TAG = "ui_handlers";
|
||||
|
||||
static void on_boot_press(void)
|
||||
{
|
||||
if (ble_hid_ready()) {
|
||||
ble_hid_send_combo(0, HID_KEY_C);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "HID not ready");
|
||||
}
|
||||
}
|
||||
|
||||
void ui_handlers_init(void)
|
||||
{
|
||||
boot_button_init(on_boot_press);
|
||||
}
|
||||
|
||||
void update_year_action(lv_event_t * e)
|
||||
{
|
||||
(void)e;
|
||||
}
|
||||
|
||||
void click_button_two(lv_event_t * e)
|
||||
{
|
||||
(void)e;
|
||||
}
|
||||
|
||||
void click_button_three(lv_event_t * e)
|
||||
{
|
||||
(void)e;
|
||||
}
|
||||
|
||||
void click_button_one(lv_event_t * e)
|
||||
{
|
||||
(void)e;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
/* UI event callbacks declared in ui_events.h are implemented in ui_handlers.c */
|
||||
void ui_handlers_init(void);
|
||||
|
||||
@@ -41,9 +41,16 @@
|
||||
#define BOARD_TOUCH_ENABLED 1
|
||||
|
||||
/* User IO */
|
||||
#define BOARD_PIN_BOOT 0
|
||||
#define BOARD_PIN_KEY 4
|
||||
#define BOARD_PIN_LED 16
|
||||
|
||||
/* BLE (ESP32-S3 built-in radio) */
|
||||
#define BOARD_BLE_ENABLED 1
|
||||
|
||||
/* WiFi */
|
||||
#define BOARD_WIFI_ENABLED 0
|
||||
|
||||
/* LVGL port */
|
||||
#define BOARD_LVGL_DRAW_BUF_LINES 20
|
||||
#define BOARD_LVGL_TICK_PERIOD_MS 2
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
#include "boot_button.h"
|
||||
|
||||
#include "board_config.h"
|
||||
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
static const char *TAG = "boot_btn";
|
||||
|
||||
#define BOOT_DEBOUNCE_MS 50
|
||||
|
||||
static boot_button_cb_t s_on_press;
|
||||
static QueueHandle_t s_gpio_evt_queue;
|
||||
|
||||
static void IRAM_ATTR gpio_isr_handler(void *arg)
|
||||
{
|
||||
uint32_t gpio_num = (uint32_t)arg;
|
||||
xQueueSendFromISR(s_gpio_evt_queue, &gpio_num, NULL);
|
||||
}
|
||||
|
||||
static void boot_button_task(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
uint32_t io_num;
|
||||
|
||||
while (1) {
|
||||
if (xQueueReceive(s_gpio_evt_queue, &io_num, portMAX_DELAY) != pdTRUE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(BOOT_DEBOUNCE_MS));
|
||||
|
||||
if (gpio_get_level(BOARD_PIN_BOOT) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
while (gpio_get_level(BOARD_PIN_BOOT) == 0) {
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
if (s_on_press != NULL) {
|
||||
s_on_press();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void boot_button_init(boot_button_cb_t on_press)
|
||||
{
|
||||
s_on_press = on_press;
|
||||
|
||||
gpio_config_t io_conf = {
|
||||
.pin_bit_mask = 1ULL << BOARD_PIN_BOOT,
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pull_up_en = GPIO_PULLUP_ENABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_NEGEDGE,
|
||||
};
|
||||
ESP_ERROR_CHECK(gpio_config(&io_conf));
|
||||
|
||||
s_gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
|
||||
esp_err_t err = gpio_install_isr_service(0);
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
ESP_ERROR_CHECK(err);
|
||||
}
|
||||
ESP_ERROR_CHECK(gpio_isr_handler_add(BOARD_PIN_BOOT, gpio_isr_handler,
|
||||
(void *)BOARD_PIN_BOOT));
|
||||
|
||||
xTaskCreate(boot_button_task, "boot_btn", 2048, NULL, 5, NULL);
|
||||
ESP_LOGI(TAG, "BOOT button ready on GPIO%d", BOARD_PIN_BOOT);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
typedef void (*boot_button_cb_t)(void);
|
||||
|
||||
void boot_button_init(boot_button_cb_t on_press);
|
||||
@@ -0,0 +1,255 @@
|
||||
#include "ble_hid.h"
|
||||
|
||||
#include "board_config.h"
|
||||
|
||||
#if BOARD_BLE_ENABLED
|
||||
|
||||
#include "esp_hidd.h"
|
||||
#include "esp_hid_common.h"
|
||||
#include "esp_hid_gap.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static const char *TAG = "ble_hid";
|
||||
|
||||
#define HID_KEYBOARD_REPORT_ID 1
|
||||
#define HID_KEYBOARD_REPORT_LEN 7
|
||||
|
||||
static const unsigned char s_keyboard_report_map[] = {
|
||||
0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x85, 0x01, 0x05, 0x07, 0x19, 0xE0,
|
||||
0x29, 0xE7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02,
|
||||
0x95, 0x01, 0x75, 0x08, 0x81, 0x03, 0x95, 0x05, 0x75, 0x01, 0x05, 0x08,
|
||||
0x19, 0x01, 0x29, 0x05, 0x91, 0x02, 0x95, 0x01, 0x75, 0x03, 0x91, 0x03,
|
||||
0x95, 0x05, 0x75, 0x08, 0x15, 0x00, 0x25, 0x65, 0x05, 0x07, 0x19, 0x00,
|
||||
0x29, 0x65, 0x81, 0x00, 0xC0,
|
||||
};
|
||||
|
||||
static esp_hid_raw_report_map_t s_report_maps[] = {
|
||||
{
|
||||
.data = s_keyboard_report_map,
|
||||
.len = sizeof(s_keyboard_report_map),
|
||||
},
|
||||
};
|
||||
|
||||
static esp_hid_device_config_t s_hid_config = {
|
||||
.vendor_id = 0x16C0,
|
||||
.product_id = 0x05DF,
|
||||
.version = 0x0100,
|
||||
.device_name = CONFIG_ESP_BLE_DEVICE_NAME,
|
||||
.manufacturer_name = "HeavenlyHeroStar",
|
||||
.serial_number = "1234567890",
|
||||
.report_maps = s_report_maps,
|
||||
.report_maps_len = 1,
|
||||
};
|
||||
|
||||
static esp_hidd_dev_t *s_hid_dev;
|
||||
static ble_hid_status_cb_t s_status_cb;
|
||||
static bool s_connected;
|
||||
|
||||
static void ble_hid_log_buffer(const char *label, const uint8_t *data, size_t len)
|
||||
{
|
||||
char hex[32];
|
||||
size_t pos = 0;
|
||||
|
||||
for (size_t i = 0; i < len && pos + 3 < sizeof(hex); i++) {
|
||||
pos += (size_t)snprintf(hex + pos, sizeof(hex) - pos, "%02x ", data[i]);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "%s len=%u: %s", label, (unsigned)len, hex);
|
||||
}
|
||||
|
||||
static void ble_hid_send_idle_report(void)
|
||||
{
|
||||
uint8_t buffer[HID_KEYBOARD_REPORT_LEN] = {0};
|
||||
|
||||
if (!ble_hid_ready()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ble_hid_log_buffer("idle", buffer, sizeof(buffer));
|
||||
(void)esp_hidd_dev_input_set(s_hid_dev, 0, HID_KEYBOARD_REPORT_ID, buffer,
|
||||
sizeof(buffer));
|
||||
}
|
||||
|
||||
static void ble_hid_idle_task(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
/* Windows often enables notify a few seconds after connect. */
|
||||
vTaskDelay(pdMS_TO_TICKS(3500));
|
||||
ble_hid_send_idle_report();
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void ble_hid_hidd_event(void *handler_args, esp_event_base_t base, int32_t id,
|
||||
void *event_data)
|
||||
{
|
||||
esp_hidd_event_t event = (esp_hidd_event_t)id;
|
||||
esp_hidd_event_data_t *param = (esp_hidd_event_data_t *)event_data;
|
||||
|
||||
(void)handler_args;
|
||||
(void)base;
|
||||
|
||||
switch (event) {
|
||||
case ESP_HIDD_START_EVENT:
|
||||
ESP_LOGI(TAG, "HID stack started");
|
||||
esp_hid_ble_gap_adv_start();
|
||||
break;
|
||||
|
||||
case ESP_HIDD_CONNECT_EVENT:
|
||||
if (param->connect.status == 0) {
|
||||
s_connected = true;
|
||||
ESP_LOGI(TAG, "HID connected");
|
||||
if (s_status_cb != NULL) {
|
||||
s_status_cb(true);
|
||||
}
|
||||
xTaskCreate(ble_hid_idle_task, "ble_hid_idle", 2048, NULL, 5, NULL);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "HID connect failed status=%d", param->connect.status);
|
||||
}
|
||||
break;
|
||||
|
||||
case ESP_HIDD_DISCONNECT_EVENT:
|
||||
s_connected = false;
|
||||
ESP_LOGI(TAG, "HID disconnected reason=%d", param->disconnect.reason);
|
||||
if (s_status_cb != NULL) {
|
||||
s_status_cb(false);
|
||||
}
|
||||
esp_hid_ble_gap_adv_start();
|
||||
break;
|
||||
|
||||
case ESP_HIDD_PROTOCOL_MODE_EVENT:
|
||||
ESP_LOGI(TAG, "protocol mode map=%u mode=%s",
|
||||
param->protocol_mode.map_index,
|
||||
param->protocol_mode.protocol_mode ? "REPORT" : "BOOT");
|
||||
break;
|
||||
|
||||
case ESP_HIDD_CONTROL_EVENT:
|
||||
ESP_LOGI(TAG, "control map=%u %sSUSPEND",
|
||||
param->control.map_index,
|
||||
param->control.control ? "EXIT_" : "");
|
||||
break;
|
||||
|
||||
case ESP_HIDD_OUTPUT_EVENT:
|
||||
ESP_LOGD(TAG, "output map=%u id=%u len=%d",
|
||||
param->output.map_index, param->output.report_id, param->output.length);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int ble_hid_init(ble_hid_status_cb_t status_cb)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
s_status_cb = status_cb;
|
||||
s_connected = false;
|
||||
|
||||
ret = esp_hidd_dev_init(&s_hid_config, ESP_HID_TRANSPORT_BLE, ble_hid_hidd_event,
|
||||
&s_hid_dev);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_hidd_dev_init failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "esp_hidd keyboard ready (report payload=%u bytes)",
|
||||
(unsigned)HID_KEYBOARD_REPORT_LEN);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void ble_hid_on_input_notify(bool enabled)
|
||||
{
|
||||
if (enabled) {
|
||||
ble_hid_send_idle_report();
|
||||
}
|
||||
}
|
||||
|
||||
bool ble_hid_ready(void)
|
||||
{
|
||||
return s_connected && s_hid_dev != NULL;
|
||||
}
|
||||
|
||||
int ble_hid_key_press(uint8_t modifier, uint8_t keycode)
|
||||
{
|
||||
uint8_t buffer[HID_KEYBOARD_REPORT_LEN] = {0};
|
||||
esp_err_t ret;
|
||||
|
||||
if (!ble_hid_ready()) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
buffer[0] = modifier;
|
||||
buffer[2] = keycode;
|
||||
ble_hid_log_buffer("press", buffer, sizeof(buffer));
|
||||
|
||||
ret = esp_hidd_dev_input_set(s_hid_dev, 0, HID_KEYBOARD_REPORT_ID, buffer,
|
||||
sizeof(buffer));
|
||||
return (ret == ESP_OK) ? 0 : ret;
|
||||
}
|
||||
|
||||
int ble_hid_key_release(void)
|
||||
{
|
||||
uint8_t buffer[HID_KEYBOARD_REPORT_LEN] = {0};
|
||||
esp_err_t ret;
|
||||
|
||||
if (!ble_hid_ready()) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
ble_hid_log_buffer("release", buffer, sizeof(buffer));
|
||||
ret = esp_hidd_dev_input_set(s_hid_dev, 0, HID_KEYBOARD_REPORT_ID, buffer,
|
||||
sizeof(buffer));
|
||||
return (ret == ESP_OK) ? 0 : ret;
|
||||
}
|
||||
|
||||
int ble_hid_send_combo(uint8_t modifier, uint8_t keycode)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = ble_hid_key_press(modifier, keycode);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
return ble_hid_key_release();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int ble_hid_init(ble_hid_status_cb_t status_cb)
|
||||
{
|
||||
(void)status_cb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ble_hid_ready(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int ble_hid_key_press(uint8_t modifier, uint8_t keycode)
|
||||
{
|
||||
(void)modifier;
|
||||
(void)keycode;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ble_hid_key_release(void)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ble_hid_send_combo(uint8_t modifier, uint8_t keycode)
|
||||
{
|
||||
(void)modifier;
|
||||
(void)keycode;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif /* BOARD_BLE_ENABLED */
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define HID_MOD_LCTRL 0x01
|
||||
#define HID_MOD_LSHIFT 0x02
|
||||
#define HID_MOD_LALT 0x04
|
||||
#define HID_MOD_LGUI 0x08
|
||||
|
||||
#define HID_KEY_C 0x06
|
||||
#define HID_KEY_F5 0x3E
|
||||
|
||||
typedef void (*ble_hid_status_cb_t)(bool connected);
|
||||
|
||||
int ble_hid_init(ble_hid_status_cb_t status_cb);
|
||||
bool ble_hid_ready(void);
|
||||
|
||||
int ble_hid_key_press(uint8_t modifier, uint8_t keycode);
|
||||
int ble_hid_key_release(void);
|
||||
int ble_hid_send_combo(uint8_t modifier, uint8_t keycode);
|
||||
|
||||
#if defined(BOARD_BLE_ENABLED) && BOARD_BLE_ENABLED
|
||||
void ble_hid_on_input_notify(bool enabled);
|
||||
#endif
|
||||
@@ -0,0 +1,104 @@
|
||||
#include "ble_manager.h"
|
||||
|
||||
#include "board_config.h"
|
||||
#include "ble_hid.h"
|
||||
#include "esp_log.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if BOARD_BLE_ENABLED
|
||||
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_hid_gap.h"
|
||||
#include "esp_hid_common.h"
|
||||
#include "host/ble_hs.h"
|
||||
#include "nimble/nimble_port.h"
|
||||
#include "nimble/nimble_port_freertos.h"
|
||||
#include "store/config/ble_store_config.h"
|
||||
|
||||
static const char *TAG = "ble_manager";
|
||||
|
||||
#define BLE_DEVICE_NAME CONFIG_ESP_BLE_DEVICE_NAME
|
||||
|
||||
static ble_status_cb_t s_status_cb;
|
||||
static bool s_connected;
|
||||
|
||||
static void on_hid_status(bool connected)
|
||||
{
|
||||
s_connected = connected;
|
||||
gpio_set_level(BOARD_PIN_LED, connected ? 1 : 0);
|
||||
if (s_status_cb != NULL) {
|
||||
s_status_cb(connected);
|
||||
}
|
||||
}
|
||||
|
||||
static void nimble_host_task(void *param)
|
||||
{
|
||||
(void)param;
|
||||
nimble_port_run();
|
||||
nimble_port_freertos_deinit();
|
||||
}
|
||||
|
||||
void ble_store_config_init(void);
|
||||
|
||||
#endif /* BOARD_BLE_ENABLED */
|
||||
|
||||
void ble_manager_set_status_cb(ble_status_cb_t cb)
|
||||
{
|
||||
#if BOARD_BLE_ENABLED
|
||||
s_status_cb = cb;
|
||||
#else
|
||||
(void)cb;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ble_manager_is_connected(void)
|
||||
{
|
||||
#if BOARD_BLE_ENABLED
|
||||
return s_connected;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ble_manager_init(void)
|
||||
{
|
||||
#if BOARD_BLE_ENABLED
|
||||
esp_err_t ret;
|
||||
|
||||
gpio_reset_pin(BOARD_PIN_LED);
|
||||
gpio_set_direction(BOARD_PIN_LED, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(BOARD_PIN_LED, 0);
|
||||
|
||||
ESP_LOGI(TAG, "init esp_hid_gap (NimBLE)");
|
||||
ret = esp_hid_gap_init(HIDD_BLE_MODE);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_hid_gap_init failed: %d", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = esp_hid_ble_gap_adv_init(ESP_HID_APPEARANCE_KEYBOARD, BLE_DEVICE_NAME);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_hid_ble_gap_adv_init failed: %d", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = ble_hid_init(on_hid_status);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "ble_hid_init failed: %d", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ble_store_config_init();
|
||||
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
||||
|
||||
ret = esp_nimble_enable(nimble_host_task);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_nimble_enable failed: %d", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "BLE HID keyboard started as \"%s\"", BLE_DEVICE_NAME);
|
||||
#else
|
||||
ESP_LOGI("ble_manager", "BLE disabled by board config");
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef void (*ble_status_cb_t)(bool connected);
|
||||
|
||||
void ble_manager_init(void);
|
||||
void ble_manager_set_status_cb(ble_status_cb_t cb);
|
||||
bool ble_manager_is_connected(void);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#ifndef _ESP_HID_GAP_H_
|
||||
#define _ESP_HID_GAP_H_
|
||||
|
||||
#define HIDD_IDLE_MODE 0x00
|
||||
#define HIDD_BLE_MODE 0x01
|
||||
#define HIDD_BT_MODE 0x02
|
||||
#define HIDD_BTDM_MODE 0x03
|
||||
|
||||
#if CONFIG_BT_HID_DEVICE_ENABLED
|
||||
#if CONFIG_BT_BLE_ENABLED
|
||||
#define HID_DEV_MODE HIDD_BTDM_MODE
|
||||
#else
|
||||
#define HID_DEV_MODE HIDD_BT_MODE
|
||||
#endif
|
||||
#elif CONFIG_BT_BLE_ENABLED
|
||||
#define HID_DEV_MODE HIDD_BLE_MODE
|
||||
#elif CONFIG_BT_NIMBLE_ENABLED
|
||||
#define HID_DEV_MODE HIDD_BLE_MODE
|
||||
#else
|
||||
#define HID_DEV_MODE HIDD_IDLE_MODE
|
||||
#endif
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "esp_bt.h"
|
||||
#if !CONFIG_BT_NIMBLE_ENABLED
|
||||
#include "esp_bt_defs.h"
|
||||
#include "esp_bt_main.h"
|
||||
#include "esp_gap_bt_api.h"
|
||||
#endif
|
||||
#include "esp_hid_common.h"
|
||||
#if CONFIG_BT_BLE_ENABLED
|
||||
#include "esp_gattc_api.h"
|
||||
#include "esp_gatt_defs.h"
|
||||
#include "esp_gap_ble_api.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if !CONFIG_BT_NIMBLE_ENABLED
|
||||
typedef struct esp_hidh_scan_result_s {
|
||||
struct esp_hidh_scan_result_s *next;
|
||||
|
||||
esp_bd_addr_t bda;
|
||||
const char *name;
|
||||
int8_t rssi;
|
||||
esp_hid_usage_t usage;
|
||||
esp_hid_transport_t transport; //BT, BLE or USB
|
||||
union {
|
||||
struct {
|
||||
esp_bt_cod_t cod;
|
||||
esp_bt_uuid_t uuid;
|
||||
} bt;
|
||||
struct {
|
||||
esp_ble_addr_type_t addr_type;
|
||||
uint16_t appearance;
|
||||
} ble;
|
||||
};
|
||||
} esp_hid_scan_result_t;
|
||||
|
||||
esp_err_t esp_hid_scan(uint32_t seconds, size_t *num_results, esp_hid_scan_result_t **results);
|
||||
void esp_hid_scan_results_free(esp_hid_scan_result_t *results);
|
||||
const char *ble_addr_type_str(esp_ble_addr_type_t ble_addr_type);
|
||||
void print_uuid(esp_bt_uuid_t *uuid);
|
||||
#endif
|
||||
|
||||
esp_err_t esp_hid_gap_init(uint8_t mode);
|
||||
esp_err_t esp_hid_gap_deinit(void);
|
||||
|
||||
esp_err_t esp_hid_ble_gap_adv_init(uint16_t appearance, const char *device_name);
|
||||
esp_err_t esp_hid_ble_gap_adv_start(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ESP_HIDH_GAP_H_ */
|
||||
@@ -1,5 +1,9 @@
|
||||
#include "wifi_manager.h"
|
||||
|
||||
#include "board_config.h"
|
||||
|
||||
#if BOARD_WIFI_ENABLED
|
||||
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
@@ -114,3 +118,14 @@ void wifi_manager_init(void)
|
||||
|
||||
ESP_LOGI(TAG, "WiFi STA started, connecting in background...");
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include "esp_log.h"
|
||||
|
||||
void wifi_manager_init(void)
|
||||
{
|
||||
ESP_LOGI("wifi_manager", "WiFi disabled by board config");
|
||||
}
|
||||
|
||||
#endif /* BOARD_WIFI_ENABLED */
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// This file was generated by SquareLine Studio
|
||||
// SquareLine Studio version: SquareLine Studio 1.6.1
|
||||
// LVGL version: 9.2.2
|
||||
// Project name: HeavenlyHeroStar
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
// This file was generated by SquareLine Studio
|
||||
// SquareLine Studio version: SquareLine Studio 1.6.1
|
||||
// LVGL version: 9.2.2
|
||||
// Project name: HeavenlyHeroStar
|
||||
|
||||
#include "../ui.h"
|
||||
|
||||
lv_obj_t * ui_Screen2 = NULL;
|
||||
lv_obj_t * ui_Button1 = NULL;
|
||||
lv_obj_t * ui_Button2 = NULL;
|
||||
lv_obj_t * ui_Button3 = NULL;
|
||||
lv_obj_t * ui_Label2 = NULL;
|
||||
lv_obj_t * ui_Label4 = NULL;
|
||||
lv_obj_t * ui_Label5 = NULL;
|
||||
// event funtions
|
||||
void ui_event_Button1(lv_event_t * e)
|
||||
{
|
||||
lv_event_code_t event_code = lv_event_get_code(e);
|
||||
|
||||
if(event_code == LV_EVENT_CLICKED) {
|
||||
click_button_two(e);
|
||||
}
|
||||
}
|
||||
|
||||
void ui_event_Button2(lv_event_t * e)
|
||||
{
|
||||
lv_event_code_t event_code = lv_event_get_code(e);
|
||||
|
||||
if(event_code == LV_EVENT_CLICKED) {
|
||||
click_button_three(e);
|
||||
}
|
||||
}
|
||||
|
||||
void ui_event_Button3(lv_event_t * e)
|
||||
{
|
||||
lv_event_code_t event_code = lv_event_get_code(e);
|
||||
|
||||
if(event_code == LV_EVENT_CLICKED) {
|
||||
click_button_one(e);
|
||||
}
|
||||
}
|
||||
|
||||
// build funtions
|
||||
|
||||
void ui_Screen2_screen_init(void)
|
||||
{
|
||||
ui_Screen2 = lv_obj_create(NULL);
|
||||
lv_obj_remove_flag(ui_Screen2, LV_OBJ_FLAG_SCROLLABLE); /// Flags
|
||||
|
||||
ui_Button1 = lv_button_create(ui_Screen2);
|
||||
lv_obj_set_width(ui_Button1, 100);
|
||||
lv_obj_set_height(ui_Button1, 80);
|
||||
lv_obj_set_align(ui_Button1, LV_ALIGN_CENTER);
|
||||
lv_obj_add_flag(ui_Button1, LV_OBJ_FLAG_SCROLL_ON_FOCUS); /// Flags
|
||||
lv_obj_remove_flag(ui_Button1, LV_OBJ_FLAG_SCROLLABLE); /// Flags
|
||||
|
||||
ui_Button2 = lv_button_create(ui_Screen2);
|
||||
lv_obj_set_width(ui_Button2, 100);
|
||||
lv_obj_set_height(ui_Button2, 80);
|
||||
lv_obj_set_x(ui_Button2, 0);
|
||||
lv_obj_set_y(ui_Button2, 100);
|
||||
lv_obj_set_align(ui_Button2, LV_ALIGN_CENTER);
|
||||
lv_obj_add_flag(ui_Button2, LV_OBJ_FLAG_SCROLL_ON_FOCUS); /// Flags
|
||||
lv_obj_remove_flag(ui_Button2, LV_OBJ_FLAG_SCROLLABLE); /// Flags
|
||||
|
||||
ui_Button3 = lv_button_create(ui_Screen2);
|
||||
lv_obj_set_width(ui_Button3, 100);
|
||||
lv_obj_set_height(ui_Button3, 80);
|
||||
lv_obj_set_x(ui_Button3, 0);
|
||||
lv_obj_set_y(ui_Button3, -100);
|
||||
lv_obj_set_align(ui_Button3, LV_ALIGN_CENTER);
|
||||
lv_obj_add_flag(ui_Button3, LV_OBJ_FLAG_SCROLL_ON_FOCUS); /// Flags
|
||||
lv_obj_remove_flag(ui_Button3, LV_OBJ_FLAG_SCROLLABLE); /// Flags
|
||||
|
||||
ui_Label2 = lv_label_create(ui_Screen2);
|
||||
lv_obj_set_width(ui_Label2, LV_SIZE_CONTENT); /// 1
|
||||
lv_obj_set_height(ui_Label2, LV_SIZE_CONTENT); /// 1
|
||||
lv_obj_set_align(ui_Label2, LV_ALIGN_CENTER);
|
||||
lv_label_set_text(ui_Label2, "Ctrl+C");
|
||||
|
||||
ui_Label4 = lv_label_create(ui_Screen2);
|
||||
lv_obj_set_width(ui_Label4, LV_SIZE_CONTENT); /// 1
|
||||
lv_obj_set_height(ui_Label4, LV_SIZE_CONTENT); /// 1
|
||||
lv_obj_set_x(ui_Label4, 0);
|
||||
lv_obj_set_y(ui_Label4, 100);
|
||||
lv_obj_set_align(ui_Label4, LV_ALIGN_CENTER);
|
||||
lv_label_set_text(ui_Label4, "Ctrl+V");
|
||||
|
||||
ui_Label5 = lv_label_create(ui_Screen2);
|
||||
lv_obj_set_width(ui_Label5, LV_SIZE_CONTENT); /// 1
|
||||
lv_obj_set_height(ui_Label5, LV_SIZE_CONTENT); /// 1
|
||||
lv_obj_set_x(ui_Label5, 0);
|
||||
lv_obj_set_y(ui_Label5, -100);
|
||||
lv_obj_set_align(ui_Label5, LV_ALIGN_CENTER);
|
||||
lv_label_set_text(ui_Label5, "Ctrl+X");
|
||||
|
||||
lv_obj_add_event_cb(ui_Button1, ui_event_Button1, LV_EVENT_ALL, NULL);
|
||||
lv_obj_add_event_cb(ui_Button2, ui_event_Button2, LV_EVENT_ALL, NULL);
|
||||
lv_obj_add_event_cb(ui_Button3, ui_event_Button3, LV_EVENT_ALL, NULL);
|
||||
|
||||
}
|
||||
|
||||
void ui_Screen2_screen_destroy(void)
|
||||
{
|
||||
if(ui_Screen2) lv_obj_del(ui_Screen2);
|
||||
|
||||
// NULL screen variables
|
||||
ui_Screen2 = NULL;
|
||||
ui_Button1 = NULL;
|
||||
ui_Button2 = NULL;
|
||||
ui_Button3 = NULL;
|
||||
ui_Label2 = NULL;
|
||||
ui_Label4 = NULL;
|
||||
ui_Label5 = NULL;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// This file was generated by SquareLine Studio
|
||||
// SquareLine Studio version: SquareLine Studio 1.6.1
|
||||
// LVGL version: 9.2.2
|
||||
// Project name: HeavenlyHeroStar
|
||||
|
||||
#ifndef UI_SCREEN2_H
|
||||
#define UI_SCREEN2_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// SCREEN: ui_Screen2
|
||||
extern void ui_Screen2_screen_init(void);
|
||||
extern void ui_Screen2_screen_destroy(void);
|
||||
extern lv_obj_t * ui_Screen2;
|
||||
extern void ui_event_Button1(lv_event_t * e);
|
||||
extern lv_obj_t * ui_Button1;
|
||||
extern void ui_event_Button2(lv_event_t * e);
|
||||
extern lv_obj_t * ui_Button2;
|
||||
extern void ui_event_Button3(lv_event_t * e);
|
||||
extern lv_obj_t * ui_Button3;
|
||||
extern lv_obj_t * ui_Label2;
|
||||
extern lv_obj_t * ui_Label4;
|
||||
extern lv_obj_t * ui_Label5;
|
||||
// CUSTOM VARIABLES
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
+3
-1
@@ -31,11 +31,13 @@ void ui_init(void)
|
||||
false, LV_FONT_DEFAULT);
|
||||
lv_disp_set_theme(dispp, theme);
|
||||
ui_Screen1_screen_init();
|
||||
ui_Screen2_screen_init();
|
||||
ui____initial_actions0 = lv_obj_create(NULL);
|
||||
lv_disp_load_scr(ui_Screen1);
|
||||
lv_disp_load_scr(ui_Screen2);
|
||||
}
|
||||
|
||||
void ui_destroy(void)
|
||||
{
|
||||
ui_Screen1_screen_destroy();
|
||||
ui_Screen2_screen_destroy();
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ extern "C" {
|
||||
///////////////////// SCREENS ////////////////////
|
||||
|
||||
#include "screens/ui_Screen1.h"
|
||||
#include "screens/ui_Screen2.h"
|
||||
|
||||
///////////////////// VARIABLES ////////////////////
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// This file was generated by SquareLine Studio
|
||||
// SquareLine Studio version: SquareLine Studio 1.6.1
|
||||
// LVGL version: 9.2.2
|
||||
// Project name: HeavenlyHeroStar
|
||||
|
||||
#include "ui.h"
|
||||
|
||||
void update_year_action(lv_event_t * e)
|
||||
{
|
||||
// Your code here
|
||||
}
|
||||
|
||||
void click_button_two(lv_event_t * e)
|
||||
{
|
||||
// Your code here
|
||||
}
|
||||
|
||||
void click_button_three(lv_event_t * e)
|
||||
{
|
||||
// Your code here
|
||||
}
|
||||
|
||||
void click_button_one(lv_event_t * e)
|
||||
{
|
||||
// Your code here
|
||||
}
|
||||
@@ -10,6 +10,13 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "lvgl.h"
|
||||
|
||||
void update_year_action(lv_event_t * e);
|
||||
void click_button_two(lv_event_t * e);
|
||||
void click_button_three(lv_event_t * e);
|
||||
void click_button_one(lv_event_t * e);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
+20
-1
@@ -1,2 +1,21 @@
|
||||
CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n
|
||||
# CONFIG_ESP_WIFI_ENABLED is not set
|
||||
CONFIG_ESP_LCD_TOUCH_CST816S_DISABLE_READ_ID=y
|
||||
|
||||
# App partition: BLE + LVGL exceed default 1MB
|
||||
CONFIG_PARTITION_TABLE_SINGLE_APP=n
|
||||
CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y
|
||||
|
||||
# BLE (NimBLE peripheral + HID keyboard)
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BT_NIMBLE_ENABLED=y
|
||||
CONFIG_BT_NIMBLE_50_FEATURE_SUPPORT=n
|
||||
CONFIG_BT_NIMBLE_ROLE_PERIPHERAL=y
|
||||
CONFIG_BT_NIMBLE_SM_LEGACY=y
|
||||
CONFIG_BT_NIMBLE_SM_SC=y
|
||||
CONFIG_BT_NIMBLE_NVS_PERSIST=y
|
||||
CONFIG_BT_NIMBLE_HID_SERVICE=y
|
||||
CONFIG_BT_NIMBLE_SVC_HID_MAX_RPTS=3
|
||||
CONFIG_BT_NIMBLE_BAS_SERVICE=y
|
||||
CONFIG_BT_NIMBLE_DIS_SERVICE=y
|
||||
CONFIG_BT_NIMBLE_SVC_DIS_PNP_ID=y
|
||||
CONFIG_BT_NIMBLE_SVC_DIS_MANUFACTURER_NAME=y
|
||||
|
||||
Reference in New Issue
Block a user