【保姆级IDF】ESP32蓝牙之BLE用作HID设备

Tips:

抛砖引玉,本文记录ESP32学习过程中遇到的收获。如有不对的地方,欢迎指正。

目录

1.前言

2.实现功能

3.代码思路

4.HID设备简介

5.代码讲解

        5.1 头文件、全局变量

        5.2 HID设备事件回调函数

        5.3 GAP蓝牙事件回调函数

        5.4  按键初始化及中断处理

        5.5 主函数

        esp_hidd_register_callbacks

        esp_ble_gap_set_security_param

6.成果展示

7.总结


1.前言

        上次写了关于BLE用作服务端的文章,这次准备介绍一下用作HID设备的方法,经过博主对代码的研究发现,其实HID只是在服务端基础上只创建一个电池服务,多创建了一个HID的应用程序,然后就是添加了安全加密方面的东西,可以配对对端设备。详细内容下文介绍。

        这次使用的是ESP32S3-DevKitC-1开发板,见下图:

2.实现功能

        1.使用ESP32的BLE作为HID设备,向外广播,接受其他设备配对连接。

        2.配置GPIO按键中断,用于控制手机音乐的播放暂停。

3.代码思路

        1.以IDF的例程代码:BLE_HIDD_DEMO为基础开发,修改发送的操作指令。

        2.配置GPIO中断模式,注册并编写中断服务函数。

4.HID设备简介

        HID(Human Interface Device,人机接口设备)是一种设备类别,专门用于与用户进行交互,通常是输入设备。HID设备能够使用户通过物理操作(例如按键、触摸或移动)向计算机或其他电子设备传输信息。HID设备的设计使其能够通过标准的接口协议与计算机进行通信,通常不需要安装特定的驱动程序。

        常见的HID设备包括:

        键盘:用于输入文字和命令。

        鼠标:用于移动光标并执行点击操作。

        触摸板和触摸屏:用于通过触摸输入信息。

        游戏控制器:例如游戏手柄、方向盘等。

        扫描仪:例如条形码扫描器。

        手写板:用于数字化手写输入。

        HID的特点:

        标准化协议:HID设备通常使用USB接口或蓝牙等无线方式与计算机或其他设备连接,使用HID协议进行通信。这意味着,设备可以即插即用(即无需额外驱动程序)。

        设备灵活性:HID协议并不限制设备类型,任何符合HID标准的设备都可以作为HID设备。设备可以包含多种输入方式,如按键、触摸、传感器等。

        简单易用:由于HID协议的标准化,用户使用这些设备时通常不需要额外配置,插入设备后,操作系统会自动识别并启用。

        HID协议广泛应用于各种计算机和嵌入式系统中,特别是在用户交互和人机界面设计上。ESP32由于其拥有BLE可实现无线通信的功能,故也能作为一种HID设备。ESP32可以通过蓝牙协议模拟或充当一个HID设备,向其他设备(如智能手机、电脑、平板等)发送输入信号。简单来说,就是ESP32能够通过蓝牙与其他设备进行人机交互,模拟键盘、鼠标、游戏手柄等设备的功能。为了实现蓝牙HID设备功能,ESP32需要通过蓝牙协议栈(通常是ESP32的官方库或SDK提供的蓝牙HID功能)来模拟HID设备的行为。程序员可以通过特定的库(例如ESP-IDF中的hid_device库)来开发此类应用。

5.代码讲解

        5.1 头文件、全局变量

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_bt.h"

#include "esp_hidd_prf_api.h"
#include "esp_bt_defs.h"
#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_gatt_defs.h"
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include "driver/gpio.h"
#include "hid_dev.h"

#define HID_DEMO_TAG "HID_DEMO"


static uint16_t hid_conn_id = 0;        // GATT连接ID
static bool sec_conn = false;           // HID连接状态
//static bool send_volum_up = false;      // 音量加发送状态
#define BUTTON_GPIO GPIO_NUM_9          // 按键GPIO
#define CHAR_DECLARATION_SIZE   (sizeof(uint8_t))   // 特征声明大小

static void hidd_event_callback(esp_hidd_cb_event_t event, esp_hidd_cb_param_t *param);//HID事件回调函数声明

#define HIDD_DEVICE_NAME            "ESP32_HID" // HID设备名称
static uint8_t hidd_service_uuid128[] = {
    /* LSB <--------------------------------------------------------------------------------> MSB */
    //first uuid, 16bit, [12],[13] is the value服务UUID
    0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x12, 0x18, 0x00, 0x00,
};

static esp_ble_adv_data_t hidd_adv_data = {     // HID广播数据
    .set_scan_rsp = false,              //是否是扫描响应数据:否
    .include_name = true,               //是否包含设备名称:是
    .include_txpower = true,            //是否包含发射功率:是
    .min_interval = 0x0006, //广播最小连接间隔, Time = min_interval * 1.25 msec= 7.5ms
    .max_interval = 0x0010, //广播最大连接间隔, Time = max_interval * 1.25 msec= 15 ms
    .appearance = 0x03c0,       //设备外观:键盘
    .manufacturer_len = 0,              // 制造商数据的长度(单位为字节)
    .p_manufacturer_data =  NULL,       // 制造商数据
    .service_data_len = 0,              // 服务数据的长度(单位为字节) 
    .p_service_data = NULL,             // 服务数据
    .service_uuid_len = sizeof(hidd_service_uuid128),   // 服务UUID长度
    .p_service_uuid = hidd_service_uuid128,             // 服务UUID
    .flag = 0x6,                        //广播标志:可连接可扫描
};

static esp_ble_adv_params_t hidd_adv_params = {     // HID广播参数
    .adv_int_min        = 0x20,                     //广播最小间隔, Time = min_interval * 0.625 msec= 20ms
    .adv_int_max        = 0x30,                     //广播最大间隔, Time = max_interval * 0.625 msec= 30ms
    .adv_type           = ADV_TYPE_IND,             //广播类型:指示广播
    .own_addr_type      = BLE_ADDR_TYPE_PUBLIC,     //广播地址类型:公共地址
    //.peer_addr            =                       //使用其他地址类型可能会需要设置这两个成员
    //.peer_addr_type       =
    .channel_map        = ADV_CHNL_ALL,
    .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
};

这部分主要是包含需要用到的头文件,定义一些蓝牙协议栈需要用到的全局变量,比如设备名称、UUID等,esp_ble_adv_data_t类型定义的是蓝牙广播数据的内容,它是用来向其他设备广播自身属性的数据,在其它设备扫描可连接蓝牙时看到的ESP32蓝牙的信息就是由这个类型定义的。esp_ble_adv_params_t类型定义的是广播参数,他决定了ESP32蓝牙的广播方式。

        5.2 HID设备事件回调函数

static void hidd_event_callback(esp_hidd_cb_event_t event, esp_hidd_cb_param_t *param)
{
    // 根据事件类型进行不同的处理
    switch(event) {
        case ESP_HIDD_EVENT_REG_FINISH: {
            // 如果初始化完成
            if (param->init_finish.state == ESP_HIDD_INIT_OK) {
                //esp_bd_addr_t rand_addr = {0x04,0x11,0x11,0x11,0x11,0x05};
                // 设置设备名称
                esp_ble_gap_set_device_name(HIDD_DEVICE_NAME);
                // 配置广播数据
                esp_ble_gap_config_adv_data(&hidd_adv_data);

            }
            break;
        }
        case ESP_BAT_EVENT_REG: {
            // TODO: 处理电池事件
            break;
        }
        case ESP_HIDD_EVENT_DEINIT_FINISH:
	     // TODO: 处理HIDD事件
	     break;
		case ESP_HIDD_EVENT_BLE_CONNECT: {
            // TODO: 处理BLE连接事件
            ESP_LOGI(HID_DEMO_TAG, "ESP_HIDD_EVENT_BLE_CONNECT");
            // 获取连接ID
            hid_conn_id = param->connect.conn_id;
            break;
        }
        case ESP_HIDD_EVENT_BLE_DISCONNECT: {
            // TODO: 处理BLE断开连接事件
            sec_conn = false;
            ESP_LOGI(HID_DEMO_TAG, "ESP_HIDD_EVENT_BLE_DISCONNECT");
            // 开始广播
            esp_ble_gap_start_advertising(&hidd_adv_params);
            break;
        }
        case ESP_HIDD_EVENT_BLE_VENDOR_REPORT_WRITE_EVT: {
            // TODO: 处理BLE供应商报告写入事件
            ESP_LOGI(HID_DEMO_TAG, "%s, ESP_HIDD_EVENT_BLE_VENDOR_REPORT_WRITE_EVT", __func__);
            // 打印数据
            ESP_LOG_BUFFER_HEX(HID_DEMO_TAG, param->vendor_write.data, param->vendor_write.length);
            break;
        }
        case ESP_HIDD_EVENT_BLE_LED_REPORT_WRITE_EVT: {
            // TODO: 处理BLE LED报告写入事件
            ESP_LOGI(HID_DEMO_TAG, "ESP_HIDD_EVENT_BLE_LED_REPORT_WRITE_EVT");
            // 打印数据
            ESP_LOG_BUFFER_HEX(HID_DEMO_TAG, param->led_write.data, param->led_write.length);
            break;
        }
        default:
            break;
    }
    return;
}

        这个函数是为了处理HID设备发生事件时,需要作出的动作。在这个函数中可以看到:在HID设备注册完成时的事件中,设置设备的名称,配置广播数据。在BLE断开连接的状态事件中开启广播,因为无设备连接时需要对外广播提供连接。其他事件只是做打印这些简单处理。该函数在主函数中注册回调。

        5.3 GAP蓝牙事件回调函数

// 静态函数,处理蓝牙事件
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
    // 根据事件类型进行不同的处理
    switch (event) {
    case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
        // 广播数据设置完成,开始广播
        esp_ble_gap_start_advertising(&hidd_adv_params);
        break;
     case ESP_GAP_BLE_SEC_REQ_EVT:
        // 安全请求事件,打印请求地址
        for(int i = 0; i < ESP_BD_ADDR_LEN; i++) {
             ESP_LOGD(HID_DEMO_TAG, "%x:",param->ble_security.ble_req.bd_addr[i]);
        }
        // 发送安全响应
        esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
	 break;
     case ESP_GAP_BLE_AUTH_CMPL_EVT:
        // 认证完成事件,设置安全连接标志
        sec_conn = true;
        esp_bd_addr_t bd_addr;
        // 复制地址
        memcpy(bd_addr, param->ble_security.auth_cmpl.bd_addr, sizeof(esp_bd_addr_t));
        // 打印远程地址
        ESP_LOGI(HID_DEMO_TAG, "remote BD_ADDR: %08x%04x",\
                (bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3],
                (bd_addr[4] << 8) + bd_addr[5]);
        // 打印地址类型
        ESP_LOGI(HID_DEMO_TAG, "address type = %d", param->ble_security.auth_cmpl.addr_type);
        // 打印认证状态
        ESP_LOGI(HID_DEMO_TAG, "pair status = %s",param->ble_security.auth_cmpl.success ? "success" : "fail");
        // 如果认证失败,打印失败原因
        if(!param->ble_security.auth_cmpl.success) {
            ESP_LOGE(HID_DEMO_TAG, "fail reason = 0x%x",param->ble_security.auth_cmpl.fail_reason);
        }
        break;
    default:
        break;
    }
}

        GAP(Generic Access Profile,通用访问配置文件)事件回调函数主要处理与蓝牙设备发现、连接、断开连接和其他设备管理相关的事件。这里列举的事件有限,当广播数据配置完成时发生事件,在这个事件中开启广播,需要向其他设备告知自己的存在以及自身的属性和能力。在遇到安全请求事件时,发送安全响应。通常安全请求由对端设备发起,ESP32来响应,一般在需要配对的时候会发生此事件。当认证完成时也会产生一个事件,我们的处理是取出对端设备地址打印出来,一同打印的还有认证状态,若是失败了也会打印失败原因。该函数在主函数中注册回调。

        5.4  按键初始化及中断处理

// 初始化按键
void KEY_Init(gpio_num_t pin)
{

    // 定义按键的GPIO配置
    gpio_set_direction(pin, GPIO_MODE_INPUT);
    gpio_set_intr_type(pin, GPIO_INTR_POSEDGE);  // 上升沿触发中断
}

// 读取按键状态
void IRAM_ATTR KEY_read()
{
    static bool botton_init = false; // 定义一个静态变量,用于记录按键的状态
    if(gpio_get_level(GPIO_NUM_45) == 1){ // 如果按键引脚的电平为高电平
        if(botton_init == false){ // 如果按键状态为未按下
                esp_hidd_send_consumer_value(hid_conn_id, HID_CONSUMER_PLAY, true); // 发送播放命令按下
                vTaskDelay(3 / portTICK_PERIOD_MS); // 延时3ms
                esp_hidd_send_consumer_value(hid_conn_id, HID_CONSUMER_PLAY, false); // 发送播放命令松开
                botton_init = true; // 更新按键状态为按下
            }
            else if(botton_init == true){ // 如果按键状态为按下
                esp_hidd_send_consumer_value(hid_conn_id, HID_CONSUMER_PAUSE, true); // 发送暂停命令按下
                vTaskDelay(3 / portTICK_PERIOD_MS); // 延时3ms
                esp_hidd_send_consumer_value(hid_conn_id, HID_CONSUMER_PAUSE, false); // 发送暂停命令松开
                botton_init = false; // 更新按键状态为未按下
            }
    }
}

        对IO的配置其中一个就是选择哪种中断触发方式,具体有:上升沿触发、下降沿触发、上升下降沿触发、输入电平高触发、输入电平低触发。作为中断IO使用我们需要配置为输入模式,然后选择要使用的IO的端口号,最后配置为上升沿触发中断。

        在中断服务函数中,我们定义一个静态布尔变量表示按键状态,由于硬件有做消抖处理,所以我们没有做软件消抖,无非就是检测到按下之后多等待一会检测是否真的按下,然后做进一步处理。实现的按键功能就是按一下播放再按一下暂停。读者可以自行理解代码,且代码中有注释,在此不多解释了。esp_hidd_send_consumer_value函数最后一个参数介绍一下:他是一个布尔值,表示按键是否被按下,如果为真,则把命令填充到buffer中,如果为假,则不处理,默认为处初始化的0。意思就是:这个布尔值决定按键是否按下,完整的流程是需要按下后松开,所以需要两个esp_hidd_send_consumer_value函数,仅仅最后一个参数不同,表示按下松开。下面贴一下这个函数的内部处理流程:

void esp_hidd_send_consumer_value(uint16_t conn_id, uint8_t key_cmd, bool key_pressed)
{
    uint8_t buffer[HID_CC_IN_RPT_LEN] = {0, 0};
    if (key_pressed) {
        ESP_LOGD(HID_LE_PRF_TAG, "hid_consumer_build_report");
        hid_consumer_build_report(buffer, key_cmd);
    }
    ESP_LOGD(HID_LE_PRF_TAG, "buffer[0] = %x, buffer[1] = %x", buffer[0], buffer[1]);
    hid_dev_send_report(hidd_le_env.gatt_if, conn_id,
                        HID_RPT_ID_CC_IN, HID_REPORT_TYPE_INPUT, HID_CC_IN_RPT_LEN, buffer);
    return;
}

        还有一点要注意!!中断服务函数里面禁用printf!!否则程序会崩溃。这里ESP_LOGD函数在中断中也没有作用!

        5.5 主函数

void app_main(void)
{
    KEY_Init(GPIO_NUM_45);// 初始化按键,IO45
    gpio_install_isr_service(0);// 安装GPIO中断服务
    gpio_isr_handler_add(GPIO_NUM_45,KEY_read,NULL);// 添加GPIO中断处理函数
    esp_err_t ret;
    // Initialize NVS.
    ret = nvs_flash_init();// 初始化NVS
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {// 如果NVS没有足够的空间,则擦除NVS
        ESP_ERROR_CHECK(nvs_flash_erase());// 如果NVS没有足够的空间,则擦除NVS
        ret = nvs_flash_init();// 重新初始化NVS
    }
    ESP_ERROR_CHECK( ret );



    ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));// 释放经典蓝牙内存

    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
    ret = esp_bt_controller_init(&bt_cfg);// 初始化蓝牙控制器
    if (ret) {
        ESP_LOGE(HID_DEMO_TAG, "%s initialize controller failed", __func__);
        return;
    }

    ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);// 启用蓝牙控制器
    if (ret) {
        ESP_LOGE(HID_DEMO_TAG, "%s enable controller failed", __func__);
        return;
    }

    esp_bluedroid_config_t bluedroid_cfg = BT_BLUEDROID_INIT_CONFIG_DEFAULT();// 初始化蓝牙配置
    ret = esp_bluedroid_init_with_cfg(&bluedroid_cfg);// 初始化蓝牙
    if (ret) {
        ESP_LOGE(HID_DEMO_TAG, "%s init bluedroid failed", __func__);
        return;
    }

    ret = esp_bluedroid_enable();// 启用蓝牙
    if (ret) {
        ESP_LOGE(HID_DEMO_TAG, "%s init bluedroid failed", __func__);
        return;
    }

    if((ret = esp_hidd_profile_init()) != ESP_OK) {// 初始化HID
        ESP_LOGE(HID_DEMO_TAG, "%s init bluedroid failed", __func__);
    }

    //注册回调函数到GAP模块
    esp_ble_gap_register_callback(gap_event_handler);// 注册GAP回调函数
    esp_hidd_register_callbacks(hidd_event_callback);// 注册HID回调函数

    /* set the security iocap & auth_req & key size & init key response key parameters to the stack*/
    esp_ble_auth_req_t auth_req = ESP_LE_AUTH_BOND;     //设置认证后与对端设备绑定
    esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE;           //设置IO能力为 No output No input
    uint8_t key_size = 16;      //密钥大小应为7~16 bytes
    uint8_t init_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;//设置初始密钥为加密密钥和身份密钥
    uint8_t rsp_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;//设置响应密钥为加密密钥和身份密钥
    esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &auth_req, sizeof(uint8_t));//设置认证模式
    esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));//设置IO能力
    esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &key_size, sizeof(uint8_t));//设置密钥大小
    /* If your BLE device act as a Slave, the init_key means you hope which types of key of the master should distribute to you,
    and the response key means which key you can distribute to the Master;
    If your BLE device act as a master, the response key means you hope which types of key of the slave should distribute to you,
    and the init key means which key you can distribute to the slave. */
    esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &init_key, sizeof(uint8_t));//设置初始密钥
    esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &rsp_key, sizeof(uint8_t));//设置响应密钥

}

        主函数中首先初始化了IO中断,然后就是NVS初始化,这个是每个例程都有的。接着是初始化蓝牙步骤。

        esp_hidd_register_callbacks

        这个函数可以深究一下,有点特殊。他将HID事件回调函数当成参数传入,首先一个功能就是注册HID回调函数,如果进去它的定义中查看,可以发现它内部执行了服务注册的函数,见下图:

        hidd_register_cb函数中就是执行了esp_ble_gatts_register_callback函数用于在GATTS模块中注册应用程序回调函数。见下图:

        gatts_event_handler 函数主要用于将新添加的服务配置文件赋予gatt_if,并为配置文件中的GATT服务器gatts_cb注册回调函数:esp_hidd_prf_cb_hdl,在这个函数中的注册应用程序ID事件里调用了hidd_le_create_service函数,见下图:

        配置文件表中分配 esp_hidd_prf_cb_hdl函数为回调函数。

         在已注册的gatts_event_handler函数中,当GATTS服务器注册事件到来时执行了esp_hidd_prf_cb_hdl函数并将相关参数传入。

        在这个函数(hidd_le_create_service)中调用了GATTS模块的API:esp_ble_gatts_create_attr_tab函数注册了服务程序。见下图:

         第一个参数bas_att_db便是服务列表,创建了电池服务。

        由此可见,当ESP32的BLE用于HID设备时,虽然使用的是HID协议,但是其本质上在HID协议内部也会创建一个GATTS服务器应用程序, 随后再创建另外一个HID应用程序。当然,如果你不希望有这个电池服务也可以选择不创建。

        esp_ble_gap_set_security_param

        这个函数用来设置GAP安全参数值,覆盖掉默认值。它是 BLE 配对和加密过程中的一个关键函数,能够控制蓝牙设备在不同安全等级下的行为。通过配置此函数,你可以指定设备的配对模式、加密级别、IO能力(如是否要求用户输入配对码)等安全参数。

函数原型

esp_err_t esp_ble_gap_set_security_param(esp_ble_sm_param_t param, void *value, uint16_t len);

参数解析:

        --param:该参数指定了要设置的安全参数的类型,类型为esp_ble_sm_param_t,它是一个枚举类型,表示不同的安全参数。

        --value:指定对应的安全参数值。根据param的不同,value的的类型和内容会有所不同。

        --len:value的长度,即传入的参数值的字节数。

        esp_ble_sm_param_t是一个枚举类型,定义了可以设置的不同安全参数。不同的参数类型会影响 BLE 设备的安全行为,如加密强度、认证方式、输入输出能力等。下面是常见的参数类型及其详细说明。

1.ESP_BLE_SM_AUTHEN_REQ_MODE

类型:uint8_t

描述:设置设备配对时的认证要求。控制配对过程中是否启用 MITM 保护、设备是否需要绑定等。

值的选项:

        ESP_LE_AUTH_NO_BOND:不绑定

        ESP_LE_AUTH_BOND:与对端设备绑定

        ESP_LE_AUTH_REQ_MITM:防止中间人攻击

        ESP_LE_AUTH_REQ_BOND_MITM:启用绑定并防止中间人攻击

        ESP_LE_AUTH_REQ_SC_ONLY:启用安全连接

        ESP_LE_AUTH_REQ_SC_BOND:启用安全连接绑定

        ESP_LE_AUTH_REQ_SC_MITM:启用安全连接并防止中间人攻击

        ESP_LE_AUTH_REQ_SC_MITM_BOND:启用安全连接、防止中间人攻击、并且启用设备绑定。

2.ESP_BLE_SM_IOCAP_MODE

类型:uint8_t

描述:设置设备的输入输出能力,决定设备配对时如何与用户交互(是否显示配对码、是否需要输入 PIN 码等)。

值的选项:

        ESP_IO_CAP_OUT:表示设备仅具有输出能力,即设备能够显示配对信息,但不具有输入能力,它意味着设备能显示配对码或认证信息,但无法进行键盘输入或其他形式的用户交互。

        ESP_IO_CAP_IO:表示设备同时支持输入和输出能力。设备既可以显示配对信息(如配对码),也可以接收用户输入(如键盘输入或 PIN 码输入)。

        ESP_IO_CAP_IN:表示设备只有输入能力,即设备不能显示任何信息(如配对码),但可以接收用户输入。例如,用户可以通过设备的按钮或键盘输入配对密码。

        ESP_IO_CAP_NONE:表示设备既没有输入能力,也没有输出能力。设备无法与用户进行任何交互,无法显示配对信息或接收用户输入。

        ESP_IO_CAP_KBDISP:表示设备同时具有键盘输入能力和显示输出能力。设备可以显示配对信息(如配对码),同时也能接收用户输入(如通过键盘输入 PIN 码)。

3.ESP_BLE_SM_MAX_KEY_SIZE

类型:uint8_t

描述:设置配对时的最大加密密钥长度。通常情况下,配对过程中使用的加密密钥长度会在最小和最大值之间自动协商。

值的范围:通常为16字节

4.ESP_BLE_SM_SET_INIT_KEY

类型:uint8_t

描述:启动器密钥分发/生成

值的选项:

        ESP_BLE_ENC_KEY_MASK:加密密钥

        ESP_BLE_ID_KEY_MASK:身份密钥

        ESP_BLE_CSR_KEY_MASK:CSR密钥

        ESP_BLE_LINK_KEY_MASK:链接密钥

5.ESP_BLE_SM_SET_RSP_KEY

类型:uint8_t

描述:启动器密钥分发/生成

值的选项:同上

6.成果展示

        设置安全参数的作用就在这里,手机会提示配对选项,点击配对即可。

        在电池服务中,我将电量的值设为了100,手机上同样可以看到设备电量显示。

        按下板子上的按键,即可控制手机音乐的播放和暂停。 

7.总结

        以上就是关于BLE用作HID设备的内容,使用它可以让ESP32模拟HID设备的行为,通过蓝牙对其他设备实现控制或数据传输的功能。

        本文对BLE本身没有过多的展开介绍,如果想了解可以移步至我的主页查看上一期文章。大家如果觉得有用,还请多多点赞收藏关注,你的支持就是对我最大的肯定。谢谢大家!

        如有疑问欢迎留言交流。以上观点为个人理解,只想抛砖引玉,如有不对的地方欢迎指正,不喜轻喷。


2024.11.19-14:54

### 回答1: 抱歉,作为一名语言模型,我无法绘制图像。但我可以为你描述一个简单的网络拓扑图,它可能类似于这样: 在中心,有一个圆形的节点,代表服务器或路由器。从该节点向外伸出几条线,每条线代表一个网络连接。每个连接的末端可能是另一个圆形节点,也可能是一个小圆点,代表计算机或其他设备。 在实际的网络拓扑图中,可能还会有更多的节点、连接和子网。但是这个简单的示例应该足以帮助你理解网络拓扑的基本概念。 ### 回答2: 网络拓扑图是一种用图形表示网络结构的工具。它展示了网络中各个节点和它们之间的连接关系。一个简单的网络拓扑图可以如下所示: 在这张图中,我们有四个节点,分别用A、B、C、D表示。这四个节点可以代表计算机、服务器或其他网络设备。这些节点之间的连接关系是通过连线表示的,箭头表示了连接的方向。 在这个例子中,A节点连接到B和C节点,B节点连接到C和D节点,C节点连接到D节点。这种连接方式可以是有线连接,比如以太网线,或者是无线连接,比如Wi-Fi。 这个简单的网络拓扑图展示了一个典型的星型拓扑结构,其中A节点是中心节点,其他节点都与它相连。这种拓扑结构通常用于小型网络,例如家庭网络或办公室网络。 通过网络拓扑图,我们可以清楚地看到网络中各个节点之间的连接关系,帮助我们理解和管理网络。此外,它还能够帮助我们确定网络中的瓶颈和故障点,以便更好地优化网络性能或解决问题。 总之,网络拓扑图是一种简单而重要的工具,可以帮助我们理解和管理网络。它可以根据实际情况进行扩展和调整,以适应更复杂的网络结构。 ### 回答3: 网络拓扑图是用于描述计算机网络中各个设备之间连接关系的图形化表示。以下是一个简单的网络拓扑图的描述: 这个网络拓扑图描述了一个小型办公室网络,其中有5台设备,包括1台路由器、2台台式电脑、1台打印机和1台无线接入点。所有设备都通过以太网连接。 在图的左侧,有一台标有“路由器”的设备,它有多个以太网接口,用于与其他设备连接。从路由器分别连接到两台台式电脑和无线接入点。无线接入点放置在办公室中心,它通过无线信号与其他无线设备通信。 两台台式电脑位于网络中间部分,它们分别通过以太网电缆与路由器相连。这些电脑可以通过路由器互相通信,并与其他设备进行数据交换。 在图的右侧,有一台打印机,它通过以太网连接到路由器。这将使所有网络中的设备都能够与打印机进行通信和共享打印资源。 整个网络拓扑图简单明了,清晰地展示了设备之间的连接关系。它可以帮助人们更好地理解网络结构,诊断和解决网络问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_山岚_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值