This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

The output report used for capslock LEDs is abnormal when I combine HID keyboard and mouse to one device(nrf52840, s140).

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!

Parents
  • Hi,

    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)。

    I suspect that this is the report_index, right? Here's the function you use:

    /**@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!");
        }
    }

    This is what I get when I (hackishly) add your code to the ble_app_hids_keyboard from SDK v15:

    <info> app: report_index: 0

    This means that the type is "BLE_HIDS_REP_TYPE_OUTPUT".

    This seems correct, as the keyboard descriptor, located at index 0 (with reportid = 1), is the one that has the output report.

     

    2,I send a right-click action to PC but nothing happens.

     Do the other functions work, like x/y/scroll? Do you see different behavior on other OSes (android or similar)?

     

    Kind regards,

    Håkon

  • Hi Håkon,appreciate for your prompt reply. The micro "BLE_HIDS_REP_TYPE_OUTPUT" and "BLE_HIDS_REP_TYPE_INPUT"  are not report_index,they are rep_type, actually. I use the on_hid_rep_char_write function to get the state of capslock led. But this function returns at the beging because the value of "p_evt->params.char_write.char_id.rep_type"  is not equal BLE_HIDS_REP_TYPE_OUTPUT, so the ble_hids_outp_rep_get function can not be excuted.I have tried to excute the ble_hids_outp_rep_get function directly
    in this function(on_hid_rep_char_write), it triggered a fatal error. 

    And the second problem,I tried other mouse actions but all failed. When I check the attribute page of my device in win10-Control panel->device and printer, I can not find the infomation about mouse——it seems that my device is only a keyboard, not a composite equipment.

    I think there are something wrong in the report descriptor or hids_init_obj,but I can’t figure it out :-(

    Looking forward to your reply.

    Best regards,

    Martin

  • Hi,

     

    I couldn't get your mouse descriptor to properly work. I took parts of the descriptor in ble_app_hids_mouse, and it seems to be functional. Note that it's only your button functions that maps up to the usb descriptor. I'd recommend trying to combine the two examples bit-by-bit and remember to delete your device in the bluetooth settings for every time you change the USB descriptor.

     

    For debug reasons, you should delete the bonding information on reset via calling the function advertising_start(true); in main().

     

    Here's the ble_app_hids_keyboard from SDK 15 modified with parts of your functions: main.c

    The output report also seems to work now.

     

    Kind regards,

    Håkon

  • Hi Håkon, Thanks for your advise. I remove the mouse report descriptor from report_map_data and change some arguments, then the output report works well: <info> app: Caps Lock is turned Off! 

    My code: 

    #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
    			 );
    
    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
    	/*
    	0x05, 0x01,       // USAGE_PAGE (Generic Desktop)
    	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 != 0){
    		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[1];
        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   = 0;
        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   = 0;
        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                       = 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_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);
    	NRF_LOG_INFO("ble_hids_init ret: %d", err_code);
        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");
    }
    
    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");
    }
    

    But, I lost the mouse functionality......

  • Did you try adding the USB descriptor that is included in the ble_app_hids_mouse instead of the one you're using? In the main.c file I added, parts of the mouse (the buttons + wheel) seems to coexist fine with the keyboard descriptor. Its then just a matter of adding the X/Y descriptor.

     

    Kind regards,

    Håkon

  • Hi Håkon,I tried and it works!Now I can send the keyboard data and the mouse data to my PC, it's perfect, Thank you!

    Best regards,

    Martin

  • Glad to hear that it works! Have a nice weekend Slight smile

    Kind regards,

    Håkon

Reply Children
No Data
Related