Hi guys, help me please.....
I want to build a device act like both keyboards and mouse. I've made some progress that the device can connect to my PC(win10) successfully and I can send keys to my PC, but there are two problems:
1, The output report is abnormal, when I get the report type in the callback function(on_hid_rep_char_write), I get 1(BLE_HIDS_REP_TYPE_INPUT)and it should be 2(BLE_HIDS_REP_TYPE_OUTPUT)。
2,I send a right-click action to PC but nothing happens.
My codes:
#include "HIDService.h"
#include "app_error.h"
#include "nrf_log.h"
#define BASE_USB_HID_SPEC_VERSION 0x0101 /**< Version number of base USB HID Specification implemented by this application. */
#define MODIFIER_KEY_POS 0 /**< Position of the modifier byte in the Input Report. */
#define SCAN_CODE_POS 2 /**< The start position of the key scan code in a HID Report. */
#define INPUT_REPORT_KEYS_MAX_LEN 8 /**< Maximum length of the Input Report characteristic. */
#define OUTPUT_REPORT_MAX_LEN 1 /**< Maximum length of Output Report. */
bool m_in_boot_mode = false; /**< Current protocol mode. */
extern uint16_t m_conn_handle; /**< Handle of the current connection. */
BLE_HIDS_DEF(m_hids, /**< Structure used to identify the HID service. */
NRF_SDH_BLE_TOTAL_LINK_COUNT,
INPUT_REPORT_KEYS_MAX_LEN,
OUTPUT_REPORT_MAX_LEN,
4);
static uint8_t report_map_data[] = {
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x01, // REPORT_ID (1)
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)
0x95, 0x08, // Report Count (8)
0x75, 0x01, // Report Size (1)
0x81, 0x02, // Input (Data, Variable, Absolute)
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8)
0x81, 0x01, // Input (Constant) reserved byte(1)
0x05, 0x08, // Usage Page (Page for LEDs)
0x19, 0x01, // Usage Minimum (1)
0x29, 0x05, // Usage Maximum (5)
0x95, 0x05, // Report Count (5)
0x75, 0x01, // Report Size (1)
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
0x05, 0x07, // Usage Page (Key codes)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x65, // Logical Maximum (101)
0x19, 0x00, // Usage Minimum (0)
0x29, 0x65, // Usage Maximum (101)
0x95, 0x06, // Report Count (6)
0x75, 0x08, // Report Size (8)
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
0x09, 0x02, // USAGE (Mouse)
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x02, // REPORT_ID (2)
0x09, 0x01, // USAGE (Pointer)
0xA1, 0x00, // COLLECTION (Physical)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM
0x29, 0x03, // USAGE_MAXIMUM
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x03, // REPORT_COUNT (3)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x05, // REPORT_SIZE (5)
0x81, 0x03, // INPUT (Const,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x09, 0x38, // USAGE (Wheel)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7F, // LOGICAL_MAXIMUM (127)
0x95, 0x03, // REPORT_COUNT (3)
0x75, 0x08, // REPORT_SIZE (8)
0x81, 0x06, // INPUT (Data,Var,Rel)
0xC0, // END_COLLECTION
0xC0, // END COLLECTION
};
/**@brief Function for handling the HID Report Characteristic Write event.
*
* @param[in] p_evt HID service event.
*/
static void on_hid_rep_char_write(ble_hids_evt_t * p_evt){
if (p_evt->params.char_write.char_id.rep_type != BLE_HIDS_REP_TYPE_OUTPUT){
//!!!!!!!!! should not enter this branch
NRF_LOG_INFO("rep_type: %d", p_evt->params.char_write.char_id.rep_type);
return;
}
ret_code_t err_code;
uint8_t report_val;
uint8_t report_index = p_evt->params.char_write.char_id.rep_index;
if (report_index != 1){
NRF_LOG_INFO("report_index: %d", report_index);
return;
}
// This code assumes that the output report is one byte long. Hence the following
// static assert is made.
STATIC_ASSERT(OUTPUT_REPORT_MAX_LEN == 1);
err_code = ble_hids_outp_rep_get(&m_hids, report_index, OUTPUT_REPORT_MAX_LEN, 0, m_conn_handle, &report_val);
APP_ERROR_CHECK(err_code);
if (((report_val & 0x02) != 0)){
NRF_LOG_INFO("Caps Lock is turned On!");
}else{
NRF_LOG_INFO("Caps Lock is turned Off!");
}
}
/**@brief Function for handling HID events.
*
* @details This function will be called for all HID events which are passed to the application.
*
* @param[in] p_hids HID service structure.
* @param[in] p_evt Event received from the HID service.
*/
static void on_hids_evt(ble_hids_t * p_hids, ble_hids_evt_t * p_evt)
{
switch (p_evt->evt_type)
{
case BLE_HIDS_EVT_BOOT_MODE_ENTERED:
m_in_boot_mode = true;
break;
case BLE_HIDS_EVT_REPORT_MODE_ENTERED:
m_in_boot_mode = false;
break;
case BLE_HIDS_EVT_REP_CHAR_WRITE:
on_hid_rep_char_write(p_evt);
break;
case BLE_HIDS_EVT_NOTIF_ENABLED:
break;
default:
// No implementation needed.
break;
}
}
/**@brief Function for handling Service errors.
*
* @details A pointer to this function will be passed to each service which may need to inform the
* application about an error.
*
* @param[in] nrf_error Error code containing information about what went wrong.
*/
static void service_error_handler(uint32_t nrf_error)
{
APP_ERROR_HANDLER(nrf_error);
}
/**@brief Function for initializing HID Service.
*/
void hid_init(void){
ret_code_t err_code;
ble_hids_init_t hids_init_obj;
ble_hids_inp_rep_init_t * p_input_report;
ble_hids_outp_rep_init_t * p_output_report;
uint8_t hid_info_flags;
static ble_hids_inp_rep_init_t input_report_array[2];
static ble_hids_outp_rep_init_t output_report_array[1];
memset((void *)input_report_array, 0, sizeof(input_report_array));
memset((void *)output_report_array, 0, sizeof(input_report_array));
// Initialize HID Service
p_input_report = &input_report_array[0];
p_input_report->max_len = 8;
p_input_report->rep_ref.report_id = 1;
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[0];
p_output_report->max_len = 1;
p_output_report->rep_ref.report_id = 1;
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);
p_input_report = &input_report_array[1];
p_input_report->max_len = 4;
p_input_report->rep_ref.report_id = 2;
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);
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 = true;
hids_init_obj.inp_rep_count = 2;
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_boot_mouse_inp_rep.cccd_write_perm);
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&hids_init_obj.security_mode_boot_mouse_inp_rep.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&hids_init_obj.security_mode_boot_mouse_inp_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);
}
static void handle_hid_error(ret_code_t err_code){
if ((err_code != NRF_SUCCESS) &&
(err_code != NRF_ERROR_INVALID_STATE) &&
(err_code != NRF_ERROR_RESOURCES) &&
(err_code != NRF_ERROR_BUSY) &&
(err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
)
{
NRF_LOG_INFO("handle_hid_error err_code: %d", err_code);
APP_ERROR_HANDLER(err_code);
}
}
void send_keys(uint8_t * p_keys, uint8_t size, uint8_t modifier){
ret_code_t err_code;
uint8_t data[INPUT_REPORT_KEYS_MAX_LEN];
ASSERT(size <= INPUT_REPORT_KEYS_MAX_LEN - SCAN_CODE_POS);
memset(data, 0, sizeof(data));
memcpy(data + SCAN_CODE_POS, p_keys, size);
data[MODIFIER_KEY_POS] = modifier;
if (m_in_boot_mode){
err_code = ble_hids_boot_kb_inp_rep_send(&m_hids, INPUT_REPORT_KEYS_MAX_LEN, data, m_conn_handle);
}else{
err_code = ble_hids_inp_rep_send(&m_hids, 0, INPUT_REPORT_KEYS_MAX_LEN, data, m_conn_handle);
}
handle_hid_error(err_code);
}
void send_mouse_movement(uint8_t btns, uint8_t x, uint8_t y, uint8_t wheel){
ret_code_t err_code;
uint8_t data[4] = {btns, x, y, wheel};
NRF_LOG_INFO("send_mouse_movement btns: %d, x: %d, y: %d, wheel:%d", btns, x, y, wheel);
if (m_in_boot_mode){
err_code = ble_hids_boot_mouse_inp_rep_send(&m_hids, btns, (int8_t)x, (int8_t)y, 0, NULL, m_conn_handle);
}else{
err_code = ble_hids_inp_rep_send(&m_hids, 1, 4, data, m_conn_handle);
}
handle_hid_error(err_code);
}
void send_mouse_moving(uint8_t x, uint8_t y, uint8_t wheel){
send_mouse_movement(0, x, y, wheel);
}
void send_mouse_btns(bool left_btn, bool right_btn, bool middle_btn){
uint8_t state = 0x00;
state = left_btn ? (state + 0x01) : state;
state = middle_btn ? (state + 0x02) : state;
state = right_btn ? (state + 0x04) : state;
send_mouse_movement(state, 0, 0, 0);
}
void test_hid_send_keys(uint8_t key){
NRF_LOG_INFO("send key begin");
send_keys(&key, 1, 0x00);
send_keys(NULL, 0, 0x00);
NRF_LOG_INFO("send key end");
//this test succeed.
}
void test_hid_send_mouse(void){
NRF_LOG_INFO("send mouse begin");
send_mouse_btns(false, true, false);
send_mouse_btns(false, false, false);
NRF_LOG_INFO("send mouse end");
//this test failed, nothing happens in PC.
}
Thanks!