esp32-c3 主控作为 BLE keyboard 的使用介绍
- 本文默认你已经了解了ESP-IDF开发环境的基本操作,本文只描述思路,不讲操作细节
- 本文实现了使用esp32c3将来自串口的键码使用BLE蓝牙发送到Win电脑的功能。在windows下进行了测试,使用PC自带的蓝牙和esp32c3配对之后,PC可以收到来自其键盘输入
1 准备材料
- esp32c3: 这里使用的是合宙的开发板,淘宝9.9包邮,便宜好用。
- 开发环境:ESP-IDF v4.4.1-dirty
- 其他:我这里使用的是Ubuntu读取了键盘输入,将数据从串口转发到esp32c3
2 步骤
- 将
~/esp/esp-idf/examples/bluetooth/bluedroid/ble/ble_hid_device_demo
复制到工作目录
- 用下面的代码将工程里的
ble_hidd_demo_main.c
全部替换。
- 编译下载
- 在windows里可以搜索名为test-keyboard的设备,并将其连接。连接成功之后,LED5 会有呼吸灯效果。esp32c3若从uart0接收到特定的数据包,则会将其通过蓝牙转发到PC。
3 其他
- CYPRESS BLE_HID协议文档。CYPRESS BLE代码示例。esp32c3 串口数据包协议为HID键盘协议包,帧尾部添加0xff作为分割符。
- 本设计不在意功耗,调整了蓝牙的上报率
min_interval=0x0002
,降低蓝牙链路上面的延时,减少使用键盘时的不跟手的感觉。
4 代码 ble_hidd_demo_main.c
- 呼吸灯里的
#include "breath_led.h"
led_duty
没有提供,请自行实现
#include
#include
#include
#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"
#include "esp_vfs.h"
#include "esp_vfs_dev.h"
#include "driver/uart.h"
#include "breath_led.h"
#include "driver/ledc.h"
#include "esp_err.h"
void led4_trg (void);
void led4_off (void);
void led5_on();
void led5_off();
#define BUTTON_PIN 9
#define HID_DEMO_TAG "HID_DEMO"
static uint16_t hid_conn_id = 0;
static bool sec_conn = false;
static bool send_volum_up = false;
#define CHAR_DECLARATION_SIZE (sizeof(uint8_t))
static void hidd_event_callback(esp_hidd_cb_event_t event, esp_hidd_cb_param_t *param);
#define HIDD_DEVICE_NAME "test-Keyboard"
static uint8_t hidd_service_uuid128[] = {
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 = {
.set_scan_rsp = false,
.include_name = true,
.include_txpower = true,
.min_interval = 0x0002,
.max_interval = 0x0006,
.appearance = 0x03c0,
.manufacturer_len = 0,
.p_manufacturer_data = NULL,
.service_data_len = 0,
.p_service_data = NULL,
.service_uuid_len = sizeof(hidd_service_uuid128),
.p_service_uuid = hidd_service_uuid128,
.flag = 0x6,
};
static esp_ble_adv_params_t hidd_adv_params = {
.adv_int_min = 0x20,
.adv_int_max = 0x30,
.adv_type = ADV_TYPE_IND,
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
.channel_map = ADV_CHNL_ALL,
.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
};
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_ble_gap_set_device_name(HIDD_DEVICE_NAME);
esp_ble_gap_config_adv_data(&hidd_adv_data);
}
break;
}
case ESP_BAT_EVENT_REG: {
break;
}
case ESP_HIDD_EVENT_DEINIT_FINISH:
break;
case ESP_HIDD_EVENT_BLE_CONNECT: {
ESP_LOGI(HID_DEMO_TAG, "ESP_HIDD_EVENT_BLE_CONNECT");
hid_conn_id = param->connect.conn_id;
break;
}
case ESP_HIDD_EVENT_BLE_DISCONNECT: {
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: {
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);
}
default:
break;
}
return;
}
int usr_hidd_input (void *dev, size_t map_index, size_t report_id, uint8_t *data, size_t length)
{
if (sec_conn) {
esp_hidd_send_keyboard_value(hid_conn_id, data[0], &data[2], 6);
return ESP_OK;
}
return !ESP_OK;
}
void newaction ()
{
static uint8_t uartrec[64] = {0};
uint8_t rec = 0;
int iret = 0;
static int idx = 0;
uart_read_bytes(0, &rec, 1, (portTickType)portMAX_DELAY);
uartrec[idx++] = rec;
led4_trg();
if (uartrec[idx-1] == 0xff) {
led4_off();
if (idx == 9) {
usr_hidd_input(hid_conn_id, 1, 2, uartrec, 8);
}
idx = 0;
}
}
void hid_main(void *pvParameters)
{
while (1) {
newaction();
}
}
void button_init (int gpio)
{
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pin_bit_mask = (1ULL<<gpio);
io_conf.pull_down_en = 0;
io_conf.pull_up_en = 0;
gpio_config(&io_conf);
}
void Led_init (int gpio)
{
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = (1ULL<<12 );
io_conf.pull_down_en = 0;
io_conf.pull_up_en = 0;
gpio_config(&io_conf);
}
void led4_trg (void)
{
static int i = 0;
gpio_set_level(12, (++i)&0x01);
}
void led4_off (void)
{
gpio_set_level(12, 0);
}
void led5_on (void)
{
gpio_set_level(13, 1);
}
void led5_off (void)
{
gpio_set_level(13, 0);
}
#define LEDC_TIMER LEDC_TIMER_0
#define LEDC_MODE LEDC_LOW_SPEED_MODE
#define LEDC_OUTPUT_IO (13)
#define LEDC_CHANNEL LEDC_CHANNEL_0
#define LEDC_DUTY_RES LEDC_TIMER_13_BIT
#define LEDC_DUTY (4095)
#define LEDC_FREQUENCY (5000)
void uart_init (void)
{
uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_APB,
};
uart_driver_install(0, 2*1024, 0, 0, NULL, 0);
uart_param_config(0, &uart_config);
ESP_ERROR_CHECK(uart_set_pin(0, 21, 20, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
}
void example_ledc_init (void)
{
ledc_timer_config_t ledc_timer = {
.speed_mode = LEDC_MODE,
.timer_num = LEDC_TIMER,
.duty_resolution = LEDC_DUTY_RES,
.freq_hz = LEDC_FREQUENCY,
.clk_cfg = LEDC_AUTO_CLK
};
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
ledc_channel_config_t ledc_channel = {
.speed_mode = LEDC_MODE,
.channel = LEDC_CHANNEL,
.timer_sel = LEDC_TIMER,
.intr_type = LEDC_INTR_DISABLE,
.gpio_num = LEDC_OUTPUT_IO,
.duty = 0,
.hpoint = 0
};
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
}
void led_breath_task (void)
{
#if 1
int cnt = 0;
example_ledc_init();
ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, LEDC_DUTY));
ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL));
while(1) {
if(sec_conn) {
ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, led_duty[cnt++]));
ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL));
if(cnt > sizeof(led_duty)/sizeof(led_duty[0])-1) cnt = 0;
} else {
cnt = 0;
ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, cnt++));
ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL));
}
vTaskDelay(pdMS_TO_TICKS(10));
}
#endif
}
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;
}
}
void usr_init(void)
{
esp_log_level_set("*", ESP_LOG_ERROR);
button_init(BUTTON_PIN);
Led_init(0);
uart_init();
xTaskCreate(&led_breath_task, "led_task", 2048, NULL, 5, NULL);
}
void app_main(void)
{
esp_err_t ret;
ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
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\n", __func__);
return;
}
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
if (ret) {
ESP_LOGE(HID_DEMO_TAG, "%s enable controller failed\n", __func__);
return;
}
ret = esp_bluedroid_init();
if (ret) {
ESP_LOGE(HID_DEMO_TAG, "%s init bluedroid failed\n", __func__);
return;
}
ret = esp_bluedroid_enable();
if (ret) {
ESP_LOGE(HID_DEMO_TAG, "%s init bluedroid failed\n", __func__);
return;
}
if((ret = esp_hidd_profile_init()) != ESP_OK) {
ESP_LOGE(HID_DEMO_TAG, "%s init bluedroid failed\n", __func__);
}
esp_ble_gap_register_callback(gap_event_handler);
esp_hidd_register_callbacks(hidd_event_callback);
esp_ble_auth_req_t auth_req = ESP_LE_AUTH_BOND;
esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE;
uint8_t key_size = 16;
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));
esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &key_size, sizeof(uint8_t));
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));
usr_init();
xTaskCreate(&hid_main, "hid_task", 2048, NULL, 5, NULL);
}