Hello Guys,
I successfully used ble_hids_mouse and ble_hids_keyboard on my hardware but my final solution is a mouse + keyboard combo.
I merged the mouse and keyboard functions but when I pair with Windows it shows driver error. I suspect my problem has to do with the HID init because it is the only section of the merge I had to guess.
If anyone who has done this before is willing to help, you will have my eternal gratitude.
I'm not an expert on HID configuration so I tried the following approach:
#define MOVEMENT_SPEED 5 // Number of pixels by which the cursor is moved each time a button is pushed
#define INPUT_REPORT_COUNT 4 // Number of input reports in this application.
#define INPUT_REP_BUTTONS_LEN 3 // Length of Mouse Input Report containing button data.
#define INPUT_REP_MOVEMENT_LEN 3 // Length of Mouse Input Report containing movement data.
#define INPUT_REP_MEDIA_PLAYER_LEN 1 // Length of Mouse Input Report containing media player data.
#define INPUT_REP_BUTTONS_INDEX 1 // Index of Mouse Input Report containing button data.
#define INPUT_REP_MOVEMENT_INDEX 2 // Index of Mouse Input Report containing movement data.
#define INPUT_REP_MPLAYER_INDEX 3 // Index of Mouse Input Report containing media player data.
#define INPUT_REP_REF_BUTTONS_ID 1 // Id of reference to Mouse Input Report containing button data.
#define INPUT_REP_REF_MOVEMENT_ID 2 // Id of reference to Mouse Input Report containing movement data.
#define INPUT_REP_REF_MPLAYER_ID 3 // Id of reference to Mouse Input Report containing media player data.
#define OUTPUT_REPORT_INDEX 0 // Index of Output Report.
#define OUTPUT_REPORT_MAX_LEN 1 // Maximum length of Output Report.
#define INPUT_REPORT_KEYS_INDEX 0 // Index of Input Report.
#define OUTPUT_REPORT_BIT_MASK_CAPS_LOCK 0x02 // CAPS LOCK bit in Output Report (based on 'LED Page (0x08)' of the Universal Serial Bus HID Usage Tables).
#define INPUT_REP_REF_ID 0 // Id of reference to Keyboard Input Report.
#define OUTPUT_REP_REF_ID 0 // Id of reference to Keyboard Output Report.
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[INPUT_REPORT_COUNT];
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;
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)
0x09, 0x02, // Usage (Mouse)
0xA1, 0x01, // Collection (Application)
// Report ID 1: Mouse buttons + scroll/pan
0x85, 0x01, // Report Id 1
0x09, 0x01, // Usage (Pointer)
0xA1, 0x00, // Collection (Physical)
0x95, 0x05, // Report Count (3)
0x75, 0x01, // Report Size (1)
0x05, 0x09, // Usage Page (Buttons)
0x19, 0x01, // Usage Minimum (01)
0x29, 0x05, // Usage Maximum (05)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x81, 0x02, // Input (Data, Variable, Absolute)
0x95, 0x01, // Report Count (1)
0x75, 0x03, // Report Size (3)
0x81, 0x01, // Input (Constant) for padding
0x75, 0x08, // Report Size (8)
0x95, 0x01, // Report Count (1)
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x38, // Usage (Wheel)
0x15, 0x81, // Logical Minimum (-127)
0x25, 0x7F, // Logical Maximum (127)
0x81, 0x06, // Input (Data, Variable, Relative)
0x05, 0x0C, // Usage Page (Consumer)
0x0A, 0x38, 0x02, // Usage (AC Pan)
0x95, 0x01, // Report Count (1)
0x81, 0x06, // Input (Data,Value,Relative,Bit Field)
0xC0, // End Collection (Physical)
// Report ID 2: Mouse motion
0x85, 0x02, // Report Id 2
0x09, 0x01, // Usage (Pointer)
0xA1, 0x00, // Collection (Physical)
0x75, 0x0C, // Report Size (12)
0x95, 0x02, // Report Count (2)
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x16, 0x01, 0xF8, // Logical maximum (2047)
0x26, 0xFF, 0x07, // Logical minimum (-2047)
0x81, 0x06, // Input (Data, Variable, Relative)
0xC0, // End Collection (Physical)
0xC0, // End Collection (Application)
// Report ID 3: Advanced buttons
0x05, 0x0C, // Usage Page (Consumer)
0x09, 0x01, // Usage (Consumer Control)
0xA1, 0x01, // Collection (Application)
0x85, 0x03, // Report Id (3)
0x15, 0x00, // Logical minimum (0)
0x25, 0x01, // Logical maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x01, // Report Count (1)
0x09, 0xCD, // Usage (Play/Pause)
0x81, 0x06, // Input (Data,Value,Relative,Bit Field)
0x0A, 0x83, 0x01, // Usage (AL Consumer Control Configuration)
0x81, 0x06, // Input (Data,Value,Relative,Bit Field)
0x09, 0xB5, // Usage (Scan Next Track)
0x81, 0x06, // Input (Data,Value,Relative,Bit Field)
0x09, 0xB6, // Usage (Scan Previous Track)
0x81, 0x06, // Input (Data,Value,Relative,Bit Field)
0x09, 0xEA, // Usage (Volume Down)
0x81, 0x06, // Input (Data,Value,Relative,Bit Field)
0x09, 0xE9, // Usage (Volume Up)
0x81, 0x06, // Input (Data,Value,Relative,Bit Field)
0x0A, 0x25, 0x02, // Usage (AC Forward)
0x81, 0x06, // Input (Data,Value,Relative,Bit Field)
0x0A, 0x24, 0x02, // Usage (AC Back)
0x81, 0x06, // Input (Data,Value,Relative,Bit Field)
0xC0 // End Collection
};
// 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);
// Mouse
// Initialize HID Service.
p_input_report = &input_report_array[INPUT_REP_BUTTONS_INDEX];
p_input_report->max_len = INPUT_REP_BUTTONS_LEN;
p_input_report->rep_ref.report_id = INPUT_REP_REF_BUTTONS_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_input_report = &input_report_array[INPUT_REP_MOVEMENT_INDEX];
p_input_report->max_len = INPUT_REP_MOVEMENT_LEN;
p_input_report->rep_ref.report_id = INPUT_REP_REF_MOVEMENT_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_input_report = &input_report_array[INPUT_REP_MPLAYER_INDEX];
p_input_report->max_len = INPUT_REP_MEDIA_PLAYER_LEN;
p_input_report->rep_ref.report_id = INPUT_REP_REF_MPLAYER_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);
// === mouse
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 = INPUT_REPORT_COUNT;
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);
// Mouse
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);
// === mouse
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);
}