【立创·实战派ESP32-S3】文档教程
第 13 章 蓝牙 HID 设备
HID 全称是 Human Interface Device,即人机交互设备,指键盘、鼠标、游戏手柄等,可以和手机或者 PC 传递数据。
本章实现使用开发板控制手机或 PC 的音量大小。
13.1 使用例程
把开发板提供的【10-ble_hid_device】例程复制到你的实验文件夹当中,并使用 VSCode 打开工程。
连接开发板到电脑,在 VSCode 上选择串口号,选择目标芯片为 esp32s3,串口下载方式,然后点击“一键三联”按钮,等待编译下载打开终端。
开发板开始运行程序后,液晶屏显示两个按键,一个是音量减按键,一个是音量加按键。此时,两个按键可以按下,但是不起作用,因为还没有使用蓝牙连接手机或笔记本电脑。
你可以使用手机或者笔记本,添加蓝牙设备,开发板的蓝牙设备名称是 HID,你的手机或笔记本搜到这个名称后,点击配对即可成功。
配对成功后,点击开发板屏幕上的按键,就可以控制手机和笔记本的音量了。
13.2 例程讲解
本例程,main 文件夹下的文件比较多,如下图所示:
main 文件夹中,一部分文件是我们之前例程都讲过的,剩下的一部分,全部都是蓝牙相关文件,这些蓝牙文件,也都是从官方例程 ble_hid_device_demo 中复制过来的。在第 3 小节,会讲制作过程,这里我们看看程序是怎么运行的。
main 函数内容如下所示:
void app_main(void)
{
bsp_i2c_init(); // I2C初始化
pca9557_init(); // IO扩展芯片初始化
bsp_lvgl_start(); // 初始化液晶屏lvgl接口
app_hid_ctrl(); // 运行蓝牙hid程序
}
2
3
4
5
6
7
一共运行了 4 个函数,前 3 个函数,都已经在之前章节中介绍过,就是初始化了液晶屏显示。
第 4 个运行的函数 app_hid_ctrl()
,就是本例程的应用程序。
app_hid_ctrl()函数
// 运行蓝牙HID控制程序
void app_hid_ctrl(void)
{
lvgl_port_lock(0);
lv_obj_t * label;
lv_obj_t * btn1 = lv_btn_create(lv_scr_act());
lv_obj_add_event_cb(btn1, btn1_event_handler, LV_EVENT_ALL, NULL);
lv_obj_align(btn1, LV_ALIGN_CENTER, -50, 0);
lv_obj_set_size(btn1, 80, 80);
label = lv_label_create(btn1);
lv_label_set_text(label, LV_SYMBOL_VOLUME_MID);
lv_obj_set_style_text_font(label, &lv_font_montserrat_20, 0);
lv_obj_center(label);
lv_obj_t * btn2 = lv_btn_create(lv_scr_act());
lv_obj_add_event_cb(btn2, btn2_event_handler, LV_EVENT_ALL, NULL);
lv_obj_align(btn2, LV_ALIGN_CENTER, 50, 0);
lv_obj_set_size(btn2, 80, 80);
label = lv_label_create(btn2);
lv_label_set_text(label, LV_SYMBOL_VOLUME_MAX);
lv_obj_set_style_text_font(label, &lv_font_montserrat_20, 0);
lv_obj_center(label);
lvgl_port_unlock();
bt_hid_start();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
这个函数比较简单,创建了两个按钮后,最后一行运行蓝牙 HID 程序 bt_hid_start()
。创建两个按钮的同时,创建了两个按钮事件处理函数,分别是音量加和音量减的处理。
bt_hid_start()
函数初始化了一些 HID 函数,创建了 gap 和 hidd 两个事件函数。蓝牙知识比较大,这里就不细讲了。
我们主要看一下按钮处理函数。
static void btn2_event_handler(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
if (sec_conn) {
if(code == LV_EVENT_PRESSING) {
esp_hidd_send_consumer_value(hid_conn_id, HID_CONSUMER_VOLUME_UP, true);
ESP_LOGI(HID_DEMO_TAG, "UP LV_EVENT_CLICKED");
}
else if(code == LV_EVENT_RELEASED) {
esp_hidd_send_consumer_value(hid_conn_id, HID_CONSUMER_VOLUME_UP, false);
ESP_LOGI(HID_DEMO_TAG, "UP LV_EVENT_RELEASED");
}
}
}
static void btn1_event_handler(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
if (sec_conn) {
if(code == LV_EVENT_PRESSING) {
esp_hidd_send_consumer_value(hid_conn_id, HID_CONSUMER_VOLUME_DOWN, true);
ESP_LOGI(HID_DEMO_TAG, "DOWN LV_EVENT_CLICKED");
}
else if(code == LV_EVENT_RELEASED) {
esp_hidd_send_consumer_value(hid_conn_id, HID_CONSUMER_VOLUME_DOWN, false);
ESP_LOGI(HID_DEMO_TAG, "DOWN LV_EVENT_RELEASED");
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
这里面需要搞懂两个知识点。
第一个:sec_conn
变量。当没有连接蓝牙时,sec_conn 是 false,蓝牙连接上后,sec_conn 是 true。
第二个:esp_hidd_send_consumer_value()
函数。这个函数用来发送键盘值。第 2 个参数是键盘值,右键转到定义,可以看所有的键盘值。第 3 个参数表示按键是否按下,true 为按下,false 为放开。btn1 发送“音量减”键值。btn2 发送“音量加”键值。
按键处理函数,在按键按下的时候,发送键值按下,放开按键的时候,发送键值放开。
在 ble_hidd_demo.c 文件的上面,有一个宏定义,可以修改开发板蓝牙设备名称。
#define HIDD_DEVICE_NAME "HID"
13.3 例程制作
本例程是在【08-lcd_lvgl】例程上修改而来。
参考官方例程 ble_hid_device_demo。
参考例程路径:examples\bluetooth\bluedroid\ble\ble_hid_device_demo
你可以单独运行一下此官方例程,把例程复制到你的实验文件夹后,使用 VSCode 打开,只需要设置一下目标芯片为 esp32s3,就可以编译下载到你的开发板。使用手机或笔记本连接名称为 HID 的设备后,就可以在手机或笔记本上看到音量在每个几秒钟自动增加和减小。
复制【08-lcd_lvgl】例程后,把 yinwu.h 和 logo_en_240x240_lcd.h 文件删除,同时把 main.c 文件中包含这两个头文件的语句删除,因为本例程用不着这两个图片了。
把 app_main 函数中,把 lvgl 的 demo 函数都删除,同时把 lv_demos.h 头文件也删除,因为本例程也不用运行 lvgl 的 demo 了。
修改后的 main 函数如下:
#include <stdio.h>
#include "esp32_s3_szp.h"
void app_main(void)
{
bsp_i2c_init(); // I2C初始化
pca9557_init(); // IO扩展芯片初始化
bsp_lvgl_start(); // 初始化液晶屏lvgl接口
}
2
3
4
5
6
7
8
9
10
11
接下来,我们把 ble_hid_device_demo 例程中 main 文件夹下的全部.c 和.h 文件复制粘贴到我们例程的 main 文件夹下。
在 ble_hidd_demo_main.c 文件中,有个 app_main 函数。我们的主函数是在 main.c 文件中,所以需要把 ble_hidd_demo_main.c 文件中的 app_main 函数名称修改一下,把它改成 bt_hid_start。然后把 ble_hidd_demo_main.c 文件名称,重命名为 ble_hidd_demo.c,这样提示主函数不在这个文件中。
在 ble_hidd_demo.c 文件的最后,加入下面的 3 个函数。
static void btn2_event_handler(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
if (sec_conn) {
if(code == LV_EVENT_PRESSING) {
esp_hidd_send_consumer_value(hid_conn_id, HID_CONSUMER_VOLUME_UP, true);
ESP_LOGI(HID_DEMO_TAG, "UP LV_EVENT_CLICKED");
}
else if(code == LV_EVENT_RELEASED) {
esp_hidd_send_consumer_value(hid_conn_id, HID_CONSUMER_VOLUME_UP, false);
ESP_LOGI(HID_DEMO_TAG, "UP LV_EVENT_RELEASED");
}
}
}
static void btn1_event_handler(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
if (sec_conn) {
if(code == LV_EVENT_PRESSING) {
esp_hidd_send_consumer_value(hid_conn_id, HID_CONSUMER_VOLUME_DOWN, true);
ESP_LOGI(HID_DEMO_TAG, "DOWN LV_EVENT_CLICKED");
}
else if(code == LV_EVENT_RELEASED) {
esp_hidd_send_consumer_value(hid_conn_id, HID_CONSUMER_VOLUME_DOWN, false);
ESP_LOGI(HID_DEMO_TAG, "DOWN LV_EVENT_RELEASED");
}
}
}
// 运行蓝牙HID控制程序
void app_hid_ctrl(void)
{
lvgl_port_lock(0);
lv_obj_t * label;
lv_obj_t * btn1 = lv_btn_create(lv_scr_act());
lv_obj_add_event_cb(btn1, btn1_event_handler, LV_EVENT_ALL, NULL);
lv_obj_align(btn1, LV_ALIGN_CENTER, -50, 0);
lv_obj_set_size(btn1, 80, 80);
label = lv_label_create(btn1);
lv_label_set_text(label, LV_SYMBOL_VOLUME_MID);
lv_obj_set_style_text_font(label, &lv_font_montserrat_20, 0);
lv_obj_center(label);
lv_obj_t * btn2 = lv_btn_create(lv_scr_act());
lv_obj_add_event_cb(btn2, btn2_event_handler, LV_EVENT_ALL, NULL);
lv_obj_align(btn2, LV_ALIGN_CENTER, 50, 0);
lv_obj_set_size(btn2, 80, 80);
label = lv_label_create(btn2);
lv_label_set_text(label, LV_SYMBOL_VOLUME_MAX);
lv_obj_set_style_text_font(label, &lv_font_montserrat_20, 0);
lv_obj_center(label);
lvgl_port_unlock();
bt_hid_start();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
app_hid_ctrl()函数就是需要在 app_main 函数中的调用的程序。app_hid_ctrl()函数中最后的调用的 bt_hid_start()函数,就是我们刚才修改函数名称的那个函数,那个函数本来是主函数,里面初始化了 HID 的各个参数。
bt_hid_start()函数最后是创建了一个任务,也就是我们运行官方例程看到的那个效果,每隔几秒钟音量自动增减。现在我们不需要运行这个任务了,因为我们需要用按键控制。所以把这个创建任务删除,同时把任务函数也删除,在最前面,还有一个任务函数中使用的变量 send_volum_up 删除,如果不删除这个变量定义,编译的时候会有警告提醒。
因为我们要在 app_main 函数中调用 app_hid_ctrl()函数,所以需要再在 main 文件夹下新建一个 ble_hidd_demo.h 文件,在这个文件中,声明一下 app_hid_ctrl()函数,然后在 main.c 文件中包含这个头文件,并且把 app_hid_ctrl()函数放到 app_main 函数中。
接下来修改 CMakeLists.txt 文件,在这个文件中,把刚才复制到 main 文件夹下的所有 c 文件都添加进去。
idf_component_register(SRCS "esp32_s3_szp.c"
"main.c"
"ble_hidd_demo.c"
"esp_hidd_prf_api.c"
"hid_dev.c"
"hid_device_le_prf.c"
INCLUDE_DIRS ".")
2
3
4
5
6
7
在 ble_hid_device_demo 例程 main 文件夹下的 CMakeLists.txt 文件中,有一行关于编译的语句,我们把这条语句也复制粘贴到我们自己例程的 CMakeLists.txt 文件中。
idf_component_register(SRCS "esp32_s3_szp.c"
"main.c"
"ble_hidd_demo.c"
"esp_hidd_prf_api.c"
"hid_dev.c"
"hid_device_le_prf.c"
INCLUDE_DIRS ".")
target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-unused-const-variable)
2
3
4
5
6
7
8
9
没有这条语句,编译会报错。
接下来修改 sdkconfig.defaults 文件。
在 ble_hid_device_demo 例程的 sdkconfig.defaults.esp32s3 文件中,有三条关于蓝牙的配置语句,如下所示:
CONFIG_BT_ENABLED=y
# CONFIG_BT_BLE_50_FEATURES_SUPPORTED is not set
CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y
2
3
把这三条语句,复制粘贴到我们例程的 sdkconfig.defaults 文件中。
然后把其中关于 lvgl demo 配置的语句删除,因为不运行那 5 个 demo 了。把字体 12 删除,把字体 16 改成 20,因为我们例程不使用 12 和 16 字体,使用 20 字体。
本例程编译后,应用程序 bin 文件的大小超过了 1M,所以我们需要自定义分区表,在 sdkconfig.defaults 文件中添加“使用自定义分区表”的语句。
最后的 sdkconfig.defaults 文件如下所示:
CONFIG_IDF_TARGET="esp32s3"
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_80M=y
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB=y
CONFIG_ESP32S3_DATA_CACHE_64KB=y
CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y
CONFIG_LV_COLOR_16_SWAP=y
CONFIG_LV_MEM_CUSTOM=y
CONFIG_LV_FONT_MONTSERRAT_20=y
CONFIG_BT_ENABLED=y
# CONFIG_BT_BLE_50_FEATURES_SUPPORTED is not set
CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
把例程 09-wifi_scan_connect 中的 partitons.csv 文件复制粘贴到本例程中,这里的 factory 大小是 7M,可以修改的小一点,也可以不用修改,只要大于 1M 就可以。
到这里,程序就修改好了。
先设置目标芯片,然后设置好串口号和串口方式下载,就可以“一键三联”了。
到这里,你可以看到,我们复制过来的蓝牙文件,只是修改了 ble_hidd_demo_main.c,其它的都没有动。