when I connect it to iPad and test it, the basic keyboard function works well, but the Caps Lock function does not work.

Hi, Guys.

I am developing a custom hardware keyboard using the ble_app_hids_keyboard example source code of SDK17.1.0.
The basic keyboard function works well.
And Caps Lock also works well on iPad.

After that, I implemented three additional functions, keyboard + mouse + consumer control, as in the link below.

https://devzone.nordicsemi.com/f/nordic-q-a/86442/ble-integrated-hid-keyboard-and-mouse-not-working-on-android-works-on-ios-pc

However, when I connect it to iPad and test it, the basic keyboard function works well, but the Caps Lock function does not work.
That is, I disabled the iPad's Settings - General - Keyboard - Hardware Keyboard - [Switch Languages using Caps Lock].
Then, the Caps Lock function should work, but the custom board I am developing does not work, so only lowercase letters are entered.
It is the same source as the link above, but I do not know how to make Caps Lock work on iPad. Please help me.

In other words, when I add the mouse and consumer control, Caps Lock does not work on iPad. How can I fix this problem?

My source code is:

#define INPUT_REPORT_COUNT                  4

/* Keyboard - Input */
#define INPUT_REPORT_KEYS_MAX_LEN           8                                          /**< Maximum length of the Input Report characteristic. */
#define INPUT_REPORT_KEYS_INDEX             0                                          /**< Index of Input Report. */
#define INPUT_REPORT_REF_KEYS_ID            1                                          /**< Id of reference to Keyboard Input Report. */

/* Keyboard - Output */
#define OUTPUT_REPORT_MAX_LEN               1                                          /**< Maximum length of Output Report. */
#define OUTPUT_REPORT_INDEX                 0                                          /**< Index of Output Report. */
#define OUTPUT_REPORT_REF_ID                1                                          /**< Id of reference to Keyboard Output Report. */

/* Keyboard - Feature */
#define FEATURE_REPORT_MAX_LEN              2                                          /**< Maximum length of Feature Report. */
#define FEATURE_REPORT_INDEX                0                                          /**< Index of Feature Report. */
#define FEATURE_REPORT_REF_ID               1                                          /**< ID of reference to Keyboard Feature Report. */

/* Mouse - Buttons */
#define INPUT_REPORT_BUTTONS_LEN            3                                          /**< Length of Mouse Input Report containing button data. */
#define INPUT_REPORT_BUTTONS_INDEX          1                                          /**< Index of Mouse Input Report containing button data. */
#define INPUT_REPORT_REF_BUTTONS_ID         2                                          /**< Id of reference to Mouse Input Report containing button data. */

/* Mouse - Movement */
#define INPUT_REP_MOVEMENT_LEN              3
#define INPUT_REP_MOVEMENT_INDEX            2                                          /**< Index of Mouse Input Report containing movement data. */
#define INPUT_REP_REF_MOVEMENT_ID           3

/* Consumer Control */
#define INPUT_REPORT_CONSUMER_MAX_LEN       1
#define INPUT_REPORT_CONSUMER_INDEX         3
#define INPUT_REPORT_REF_CONSUMER_ID        4

static void hids_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;
    ble_hids_feature_rep_init_t * p_feature_report;
    uint8_t                       hid_info_flags;

    static ble_hids_inp_rep_init_t     input_report_array[INPUT_REPORT_COUNT];
    static ble_hids_outp_rep_init_t    output_report_array[1];
    static ble_hids_feature_rep_init_t feature_report_array[1];
    static uint8_t                     report_map_data[] =
    {

//////// Keyboard ///////////////////////////////////////////////////////////
        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)
        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 Size (8 bit)
        0x95, 0x02,       // Report Count (2)
        0xB1, 0x02,       // Feature (Data, Variable, Absolute)

        0xC0,             // End Collection (Application)

/////// Mouse //////////////////////////////////////////////////////////////////
        0x05, 0x01, // Usage Page (Generic Desktop)
        0x09, 0x02, // Usage (Mouse)
        0xA1, 0x01, // Collection (Application)

        // Report ID 1: Mouse buttons + scroll/pan
        0x85, 0x02,       // Report Id 2

        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, 0x03,       // Report Id 3
        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, //0x02,       //0x06      // Input (Data, Variable, Relative)

        0xC0,             // End Collection (Physical)
        0xC0,             // End Collection (Application)

/////// Consumer Control //////////////////////////////////////////////////////////////////////
        // Report ID 3: Advanced buttons
        0x05, 0x0C,       // Usage Page (Consumer)
        0x09, 0x01,       // Usage (Consumer Control)
        0xA1, 0x01,       // Collection (Application)

        0x85, 0x04,       // Report Id (4)

        0x15, 0x00,       // Logical minimum (0)
        0x25, 0x01,       // Logical maximum (1)
        0x75, 0x01,       // Report Size (1)
        0x95, 0x01,       // Report Count (1)

        0x09, 0x70,       // Usage (Display Brightness Decremanr)
        0x81, 0x06,       // Input (Data,Value,Relative,Bit Field)
        0x09, 0x6F,       // Usage (Display Brightness Incremanr)
        0x81, 0x06,       // Input (Data,Value,Relative,Bit Field)
        0x09, 0xB6,       // Usage (Scan Previous Track)
        0x81, 0x06,       // Input (Data,Value,Relative,Bit Field)
        0x09, 0xCD,       // Usage (Play/Pause)
        0x81, 0x06,       // Input (Data,Value,Relative,Bit Field)
        0x09, 0xB5,       // Usage (Scan Next Track)
        0x81, 0x06,       // Input (Data,Value,Relative,Bit Field)
        0x09, 0xE2,       // Usage (Volume Mute)
        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)
        0xC0,              // End Collection
    };

    memset((void *)input_report_array, 0, sizeof(ble_hids_inp_rep_init_t) * INPUT_REPORT_COUNT);
    memset((void *)output_report_array, 0, sizeof(ble_hids_outp_rep_init_t));
    memset((void *)feature_report_array, 0, sizeof(ble_hids_feature_rep_init_t));

    // Initialize HID Service

    /* Keyboard */
    p_input_report                      = &input_report_array[INPUT_REPORT_KEYS_INDEX];     //0
    p_input_report->max_len             = INPUT_REPORT_KEYS_MAX_LEN;    //8
    p_input_report->rep_ref.report_id   = INPUT_REPORT_REF_KEYS_ID;     //1
    p_input_report->rep_ref.report_type = BLE_HIDS_REP_TYPE_INPUT;      //1

    p_input_report->sec.cccd_wr = SEC_JUST_WORKS;
    p_input_report->sec.wr      = SEC_JUST_WORKS;
    p_input_report->sec.rd      = SEC_JUST_WORKS;

    p_output_report                      = &output_report_array[OUTPUT_REPORT_INDEX];       //0
    p_output_report->max_len             = OUTPUT_REPORT_MAX_LEN;       //1
    p_output_report->rep_ref.report_id   = OUTPUT_REPORT_REF_ID;        //1
    p_output_report->rep_ref.report_type = BLE_HIDS_REP_TYPE_OUTPUT;    //2

    p_output_report->sec.wr = SEC_JUST_WORKS;
    p_output_report->sec.rd = SEC_JUST_WORKS;

    p_feature_report                      = &feature_report_array[FEATURE_REPORT_INDEX];    //0
    p_feature_report->max_len             = FEATURE_REPORT_MAX_LEN;     //2
    p_feature_report->rep_ref.report_id   = FEATURE_REPORT_REF_ID;      //1
    p_feature_report->rep_ref.report_type = BLE_HIDS_REP_TYPE_FEATURE;  //3

    p_feature_report->sec.rd              = SEC_JUST_WORKS;
    p_feature_report->sec.wr              = SEC_JUST_WORKS;

    /* Mouse */
    p_input_report                      = &input_report_array[INPUT_REPORT_BUTTONS_INDEX];  //1
    p_input_report->max_len             = INPUT_REPORT_BUTTONS_LEN;     //3
    p_input_report->rep_ref.report_id   = INPUT_REPORT_REF_BUTTONS_ID;  //2
    p_input_report->rep_ref.report_type = BLE_HIDS_REP_TYPE_INPUT;      //1

    p_input_report->sec.cccd_wr = SEC_JUST_WORKS;
    p_input_report->sec.wr      = SEC_JUST_WORKS;
    p_input_report->sec.rd      = SEC_JUST_WORKS;

    p_input_report                      = &input_report_array[INPUT_REP_MOVEMENT_INDEX];    //2
    p_input_report->max_len             = INPUT_REP_MOVEMENT_LEN;       //3
    p_input_report->rep_ref.report_id   = INPUT_REP_REF_MOVEMENT_ID;    //3
    p_input_report->rep_ref.report_type = BLE_HIDS_REP_TYPE_INPUT;      //1

    p_input_report->sec.cccd_wr = SEC_JUST_WORKS;
    p_input_report->sec.wr      = SEC_JUST_WORKS;
    p_input_report->sec.rd      = SEC_JUST_WORKS;

    /* Consumer Control */
    p_input_report                      = &input_report_array[INPUT_REPORT_CONSUMER_INDEX]; //3
    p_input_report->max_len             = INPUT_REPORT_CONSUMER_MAX_LEN;    //1 //2 ***
    p_input_report->rep_ref.report_id   = INPUT_REPORT_REF_CONSUMER_ID;     //4
    p_input_report->rep_ref.report_type = BLE_HIDS_REP_TYPE_INPUT;          //1

    p_input_report->sec.cccd_wr = SEC_JUST_WORKS;
    p_input_report->sec.wr      = SEC_JUST_WORKS;
    p_input_report->sec.rd      = SEC_JUST_WORKS;

    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              = 1;
    hids_init_obj.p_feature_rep_array            = feature_report_array;
    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;

    hids_init_obj.rep_map.rd_sec         = SEC_JUST_WORKS;
    hids_init_obj.hid_information.rd_sec = SEC_JUST_WORKS;

    hids_init_obj.boot_kb_inp_rep_sec.cccd_wr = SEC_JUST_WORKS;
    hids_init_obj.boot_kb_inp_rep_sec.rd      = SEC_JUST_WORKS;

    hids_init_obj.boot_kb_outp_rep_sec.rd = SEC_JUST_WORKS;
    hids_init_obj.boot_kb_outp_rep_sec.wr = SEC_JUST_WORKS;

    hids_init_obj.boot_mouse_inp_rep_sec.cccd_wr = SEC_JUST_WORKS;
    hids_init_obj.boot_mouse_inp_rep_sec.wr      = SEC_JUST_WORKS;
    hids_init_obj.boot_mouse_inp_rep_sec.rd      = SEC_JUST_WORKS;

    hids_init_obj.protocol_mode_rd_sec = SEC_JUST_WORKS;
    hids_init_obj.protocol_mode_wr_sec = SEC_JUST_WORKS;
    hids_init_obj.ctrl_point_wr_sec    = SEC_JUST_WORKS;

    err_code = ble_hids_init(&m_hids, &hids_init_obj);
    APP_ERROR_CHECK(err_code);
}

Best Regards,

Related