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

how are keyboard key release events sent in example code?

Hello,

I'm trying to understand how the ble_app_hids_keyboard example works. So far, I have hooked up a few more buttons to the board and can type hello.

Now, I would like to have it function more like a real keyboard - including being able to hold down buttons, multiple buttons, etc. I've been going through the example code, and the keys_send() function seems to send both the press notification as well as the release notification via the send_key_scan_press_release() function, but I'm having a hard time understanding how the release notifications are generated and sent.

Thanks! Have pasted the functions in question below. They are unmodified from the original SDK download.

/**@brief Function for sending sample key presses to the peer.
 *
 * @param[in]   key_pattern_len   Pattern length.
 * @param[in]   p_key_pattern     Pattern to be sent.
 */
static void keys_send(uint8_t key_pattern_len, uint8_t * p_key_pattern)
{
uint32_t err_code;
uint16_t actual_len;

err_code = send_key_scan_press_release(&m_hids,
                                       p_key_pattern,
                                       key_pattern_len,
                                       0,
                                       &actual_len);
// An additional notification is needed for release of all keys, therefore check
// is for actual_len <= key_pattern_len and not actual_len < key_pattern_len.
if ((err_code == BLE_ERROR_NO_TX_BUFFERS) && (actual_len <= key_pattern_len))
{
    // Buffer enqueue routine return value is not intentionally checked.
    // Rationale: Its better to have a a few keys missing than have a system
    // reset. Recommendation is to work out most optimal value for
    // MAX_BUFFER_ENTRIES to minimize chances of buffer queue full condition
    (void) buffer_enqueue(&m_hids, p_key_pattern, key_pattern_len, actual_len);
}


if ((err_code != NRF_SUCCESS) &&
    (err_code != NRF_ERROR_INVALID_STATE) &&
    (err_code != BLE_ERROR_NO_TX_BUFFERS) &&
    (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
)
{
    APP_ERROR_HANDLER(err_code);
}
}


/**@brief   Function for transmitting a key scan Press & Release Notification.
 *
 * @warning This handler is an example only. You need to analyze how you wish to send the key
 *          release.
 *
 * @param[in]  p_instance     Identifies the service for which Key Notifications are requested.
 * @param[in]  p_key_pattern  Pointer to key pattern.
 * @param[in]  pattern_len    Length of key pattern. 0 < pattern_len < 7.
 * @param[in]  pattern_offset Offset applied to Key Pattern for transmission.
 * @param[out] actual_len     Provides actual length of Key Pattern transmitted, making  buffering of
 *                            rest possible if needed.
 * @return     NRF_SUCCESS on success, BLE_ERROR_NO_TX_BUFFERS in case transmission could not be
 *             completed due to lack of transmission buffer or other error codes indicating reason
 *             for failure.
 *
 * @note       In case of BLE_ERROR_NO_TX_BUFFERS, remaining pattern that could not be transmitted
 *             can be enqueued \ref buffer_enqueue function.
 *             In case a pattern of 'cofFEe' is the p_key_pattern, with pattern_len as 6 and
 *             pattern_offset as 0, the notifications as observed on the peer side would be
 *             1>    'c', 'o', 'f', 'F', 'E', 'e'
 *             2>    -  , 'o', 'f', 'F', 'E', 'e'
 *             3>    -  ,   -, 'f', 'F', 'E', 'e'
 *             4>    -  ,   -,   -, 'F', 'E', 'e'
 *             5>    -  ,   -,   -,   -, 'E', 'e'
 *             6>    -  ,   -,   -,   -,   -, 'e'
 *             7>    -  ,   -,   -,   -,   -,  -
 *             Here, '-' refers to release, 'c' refers to the key character being transmitted.
 *             Therefore 7 notifications will be sent.
 *             In case an offset of 4 was provided, the pattern notifications sent will be from 5-7
 *             will be transmitted.
 */
static uint32_t send_key_scan_press_release(ble_hids_t *   p_hids,
                                        uint8_t *      p_key_pattern,
                                        uint16_t       pattern_len,
                                        uint16_t       pattern_offset,
                                        uint16_t *     p_actual_len)
{
uint32_t err_code;
uint16_t offset;
uint16_t data_len;
uint8_t  data[INPUT_REPORT_KEYS_MAX_LEN];

// HID Report Descriptor enumerates an array of size 6, the pattern hence shall not be any
// longer than this.
STATIC_ASSERT((INPUT_REPORT_KEYS_MAX_LEN - 2) == 6);

ASSERT(pattern_len <= (INPUT_REPORT_KEYS_MAX_LEN - 2));

offset   = pattern_offset;
data_len = pattern_len;

do
{
    // Reset the data buffer. 
    memset(data, 0, sizeof(data));
    
    // Copy the scan code.
    memcpy(data + SCAN_CODE_POS + offset, p_key_pattern + offset, data_len - offset);
    
    if (is_shift_key_pressed())
    {
        data[MODIFIER_KEY_POS] |= SHIFT_KEY_CODE;
    }

    if (!m_in_boot_mode)
    {
        err_code = ble_hids_inp_rep_send(p_hids, 
                                         INPUT_REPORT_KEYS_INDEX,
                                         INPUT_REPORT_KEYS_MAX_LEN,
                                         data);
    }
    else
    {
        err_code = ble_hids_boot_kb_inp_rep_send(p_hids,
                                                 INPUT_REPORT_KEYS_MAX_LEN,
                                                 data);
    }
    
    if (err_code != NRF_SUCCESS)
    {
        break;
    }
    
    offset++;
} while (offset <= data_len);

*p_actual_len = offset;

return err_code;
}

Thanks!

  • Hi,

    This example is a bit complex, especially when it's only purpose is to say "hello" via button press.

    First off, you will get an interrupt when the button is pressed, triggering function "button_event_handler", which saves which byte is currently being sent (p_key), and passed on to function send_keys().

    Send_keys() will send, over-the-air, the first ASCII letter packed into a HID-acceptable format, which is done by calling "send_key_scan_press_release()".

    If this function returns w/o errors, you will queue a "release key" (all zeros) via function "buffer_enqueue()", and then it'll return.

    Once the initial ASCII byte is successfully transferred over-the-air, you will get a event back from the SoftDevice, BLE_EVT_TX_COMPLETE in function on_ble_evt(), which then sends the previously enqueued release keys.

    If you are developing a keyboard application with the nRF51x22, I would recommend that you look at the nRFready Desktop 2 reference design and how the scanning of the keyboard matrix is done there. The ble_app_hids_keyboard example does not give any insight towards setting up a keyboard matrix. It's purpose is to show the setup of the HoG (HID over GATT) profile as a keyboard.

    Cheers, Håkon

Related