最近在搞一个锁的项目,使用NRF51822芯片。基本功能,就是手机端与锁第一次配对绑定以后,在手机端不取消配对的情况,每次手机靠近锁,BLE自动回连,然后开锁,锁与手机远离以后,断开连接,锁自动锁上。功能很简单,第一眼就想到使用hids服务,因为之前也没有用过,这里也是初学,把自己的心得体会分享一下,以便大家相互学习。
本次是在NRF52832的Demo板上测试的 ,使用的是SDK12.3的hids_keyboard 例程,这次主要讲流程。我们从主函数一步一步分析讲起。
1.0 配对管理初始化。
配对信息初始化,主要是对开关机以后,软件对之前是否存在绑定设备的信息校验和初始化。因为,如果之前存在绑定信息,在绑定回调里面,会将绑定信息和手机MAC 地址保存在芯片内部flash里面(再次之前,会加入白名单里面) 。如下图:
1.2 GAP 外观设置
在GAP 初始化里面,增加一个外观设置,这个就是我们使用手机蓝牙搜索时候,出现的键盘图标,没什么特别的东西,大家在使用其他东西时候,也可以通过这个函数进行更改自己的图标,
1.3 广播初始化
在广播初始化里面,增加了定向广播,这个目的就是为了断开连接(不是取消配对造成的断开)之后,回连手机的操作。
1.4服务初始化
服务初始化里面,增加了 HIDS的keyboard 初始化,这个里面,最重要的就是键盘配置表,也是最难得的,对于我这种菜鸡,目前还没整明白,不过,大家如果有时间,可以自己看看,这里介绍一款软件,可以帮助学习,软件名字叫做DT
链接:https://www.usb.org/sites/default/files/documents/dt2_4.zip
还有一个文档,就是这些USB HID 所遵循的协议,也是够难的,大家自己看 。有明白和知道的评论区分享出来。
链接:https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf
static void hids_init(void)
{
uint32_t err_code;
ble_hids_init_t hids_init_obj;
ble_hids_inp_rep_init_t input_report_array[1];
ble_hids_inp_rep_init_t * p_input_report;
ble_hids_outp_rep_init_t output_report_array[1];
ble_hids_outp_rep_init_t * p_output_report;
uint8_t hid_info_flags;
memset((void *)input_report_array, 0, sizeof(ble_hids_inp_rep_init_t));
memset((void *)output_report_array, 0, sizeof(ble_hids_outp_rep_init_t));
static uint8_t report_map_data[] =
{
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x05, 0x07, // Usage Page (Key Codes)
0x19, 0xe0, // Usage Minimum (224)
0x29, 0xe7, // Usage Maximum (231)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input (Data, Variable, Absolute)
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8)
0x81, 0x01, // Input (Constant) reserved byte(1)
0x95, 0x05, // Report Count (5)
0x75, 0x01, // Report Size (1)
0x05, 0x08, // Usage Page (Page# for LEDs)
0x19, 0x01, // Usage Minimum (1)
0x29, 0x05, // Usage Maximum (5)
0x91, 0x02, // Output (Data, Variable, Absolute), Led report
0x95, 0x01, // Report Count (1)
0x75, 0x03, // Report Size (3)
0x91, 0x01, // Output (Data, Variable, Absolute), Led report padding
0x95, 0x06, // Report Count (6)
0x75, 0x08, // Report Size (8)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x65, // Logical Maximum (101)
0x05, 0x07, // Usage Page (Key codes)
0x19, 0x00, // Usage Minimum (0)
0x29, 0x65, // Usage Maximum (101)
0x81, 0x00, // Input (Data, Array) Key array(6 bytes)
0x09, 0x05, // Usage (Vendor Defined)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Count (2)
0x95, 0x02, // Report Size (8 bit)
0xB1, 0x02, // Feature (Data, Variable, Absolute)
0xC0 // End Collection (Application)
};
// Initialize HID Service
p_input_report = &input_report_array[INPUT_REPORT_KEYS_INDEX];
p_input_report->max_len = INPUT_REPORT_KEYS_MAX_LEN;
p_input_report->rep_ref.report_id = INPUT_REP_REF_ID;
p_input_report->rep_ref.report_type = BLE_HIDS_REP_TYPE_INPUT;
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&p_input_report->security_mode.cccd_write_perm);
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&p_input_report->security_mode.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&p_input_report->security_mode.write_perm);
p_output_report = &output_report_array[OUTPUT_REPORT_INDEX];
p_output_report->max_len = OUTPUT_REPORT_MAX_LEN;
p_output_report->rep_ref.report_id = OUTPUT_REP_REF_ID;
p_output_report->rep_ref.report_type = BLE_HIDS_REP_TYPE_OUTPUT;
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&p_output_report->security_mode.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&p_output_report->security_mode.write_perm);
hid_info_flags = HID_INFO_FLAG_REMOTE_WAKE_MSK | HID_INFO_FLAG_NORMALLY_CONNECTABLE_MSK;
memset(&hids_init_obj, 0, sizeof(hids_init_obj));
hids_init_obj.evt_handler = on_hids_evt;
hids_init_obj.error_handler = service_error_handler;
hids_init_obj.is_kb = true;
hids_init_obj.is_mouse = false;
hids_init_obj.inp_rep_count = 1;
hids_init_obj.p_inp_rep_array = input_report_array;
hids_init_obj.outp_rep_count = 1;
hids_init_obj.p_outp_rep_array = output_report_array;
hids_init_obj.feature_rep_count = 0;
hids_init_obj.p_feature_rep_array = NULL;
hids_init_obj.rep_map.data_len = sizeof(report_map_data);
hids_init_obj.rep_map.p_data = report_map_data;
hids_init_obj.hid_information.bcd_hid = BASE_USB_HID_SPEC_VERSION;
hids_init_obj.hid_information.b_country_code = 0;
hids_init_obj.hid_information.flags = hid_info_flags;
hids_init_obj.included_services_count = 0;
hids_init_obj.p_included_services_array = NULL;
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&hids_init_obj.rep_map.security_mode.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hids_init_obj.rep_map.security_mode.write_perm);
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&hids_init_obj.hid_information.security_mode.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hids_init_obj.hid_information.security_mode.write_perm);
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(
&hids_init_obj.security_mode_boot_kb_inp_rep.cccd_write_perm);
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&hids_init_obj.security_mode_boot_kb_inp_rep.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hids_init_obj.security_mode_boot_kb_inp_rep.write_perm);
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&hids_init_obj.security_mode_boot_kb_outp_rep.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&hids_init_obj.security_mode_boot_kb_outp_rep.write_perm);
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&hids_init_obj.security_mode_protocol.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&hids_init_obj.security_mode_protocol.write_perm);
BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hids_init_obj.security_mode_ctrl_point.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&hids_init_obj.security_mode_ctrl_point.write_perm);
err_code = ble_hids_init(&m_hids, &hids_init_obj);
APP_ERROR_CHECK(err_code);
}
1.5 广播开始
广播开始时,会进行白名单获取和设置绑定参数信息
在 ble_advertising_start 里面 ,最主要的就是下面两处,这两个地方直接决定了广播的模式是快速还是定向
我们来看一下定向广播里面的设置,是把手机的Mac地址赋值过去 ,就可以了 !不知道我的操作有问题,还是其他的原因,我把广播初始化直接设置成定向广播,然后强制修改成我自己的手机Mac地址,它是无法回连到我的手机上,必须经过一次配对绑定才行。
2.0绑定信息存储
在绑定管理会呆里面,最重要的两个事件 :
PM_EVT_BONDED_PEER_CONNECTED :连接上已经绑定的设备。(这个事件是在回连成功以后,触发的)
PM_EVT_CONN_SEC_SUCCEEDED : 连接到绑定设备。(这个是在第一次绑定触发的)
看一下PM_EVT_CONN_SEC_SUCCEEDED 里面的操作,就是判断绑定链接属性,然后满足判断,就把绑定的信息保存下来,当然,这里还有一些链接秘钥等信息。
2.2 广播回调事件
在on_adv_evt 里面,关注两个事件 :BLE_ADV_EVT_WHITELIST_REQUEST 和 BLE_ADV_EVT_PEER_ADDR_REQUEST
这两个回调事件基本功能就是把绑定信息读取出来,把手机mac地址赋值给m_peer_address,这两个事件的触发也是在ble_start里面触发的,在上面已经介绍过了。这里就是为了定向广播的数据赋值做准备的。
3.0 结束
到这里整个过程已近介绍完了 ,如果还想看keyboard 发送数据到手机端的 ,可以自己看一下bsp_event_handler 连的发送,其他不介绍,配对绑定的过程很复杂,先要有配对才行,绑定在一些情况下会出现。这个过程大家自己网络搜索资料 或者去nrf 官网看看 。
我这边发现有两个问题!
1、Hids_keyboard 和 mousce 例程,使用过程中会使手机端出现自动打字和鼠标图案,想了很久也没解决掉,不知道其他的hid 服务 比如 :电子笔,扫描枪,游戏柄等是否可以。需要验证一下。这些例程官网上资料少,个人还没有去实践操作。
2、不管是Hids_keyboard 还是 mousce 例程,如果手机端和 ble进行了配对绑定成功,此时,把ble关闭,手机蓝牙取消配对。然后,在打开ble和手机蓝牙,虽然ble的log显示是进行的定向广播,但是,却无法回连。我手动去连接ble(不使用nrf connect 工具),也无法连接成功。这个地方不知道是不是一个 bug。