Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

displaying numeric's characters with HID Keyboard in BLE, azerty Keyboard

Hi,

I would like display all keycode azerty Keyboard, I started to use uart_event_handle() and I modified send_key_scan_press_release() for the modifier position key [MODIFIER_KEY_POS]. When I connected to smartphone and I receive the character 'c', I display 5 keys: 12345 and when I receive the character 'd', I display 5 keys : &é"'( it's OK. but when I receive the character 'e' , I display: 123'( . I just added for case 'e' the line kb_key_shift_ctrl_alt_altgr = NO_SPECIFIC_KEY_CODE; after while(size < MAX_KEYS_IN_ONE_REPOR)
{
...
}

I can't understand why the SHIFT key seems to be released on the fourth character. 123' (normally number 4) then 5?

static uint8_t m_sample_key_press_scan_str_number[] = /**< Key pattern to be sent when the key press button has been pushed. */
{
    0x1E,      
    0x1F,      
    0x20,      
    0x21,      
    0x22,      
    0x28        /* Key Return */
};

uint8_t kb_key_shift_ctrl_alt_altgr;  // variable for input report MODIFER_KEY_POS, see macro SHIFT_KEY_CODE, CTRL_KEY_CODE....


void uart_event_handle(app_uart_evt_t * p_event)					//$at_06_04_22
{
	uint8_t message[UARTE_MAX_DATA_LEN] = "UART OK";
	uint8_t message_erase_bonds[] = "wait cmd Erase bonds"; 
	uint8_t i;
	
	static uint8_t * p_key = m_sample_key_press_scan_str;
    static uint8_t   size  = 0;
	
    static uint8_t data_array[UARTE_MAX_DATA_LEN];
    static uint8_t index = 0;
    //uint32_t       err_code;
	
    switch (p_event->evt_type)
    {
        case APP_UART_DATA_READY:
            UNUSED_VARIABLE(app_uart_get(&data_array[index]));
			
			switch(data_array[0])
			{
				case 'a':
					for (i = 0; i < strlen((const char*)message_erase_bonds); i++)
					{
						app_uart_put(message_erase_bonds[i]);
					}
					break;
					
				case 'b':
					advertising_start(true);
					break;
					
				case 'c':
					p_key = m_sample_key_press_scan_str_number;
					kb_key_shift_ctrl_alt_altgr = SHIFT_KEY_CODE;
					size = 0;
					if (m_conn_handle != BLE_CONN_HANDLE_INVALID)
					{
						while (size < MAX_KEYS_IN_ONE_REPORT)
						{
							keys_send(1, p_key);
							p_key++;
							size++;
						}
					}
					break;		

				case 'd':
					p_key = m_sample_key_press_scan_str_number;
					kb_key_shift_ctrl_alt_altgr = NO_SPECIFIC_KEY_CODE;
					size = 0;
					if (m_conn_handle != BLE_CONN_HANDLE_INVALID)
					{
						while (size < MAX_KEYS_IN_ONE_REPORT)
						{
							keys_send(1, p_key);
							p_key++;
							size++;
						}
					}
					break;		

				case 'e':
					p_key = m_sample_key_press_scan_str_number;
					kb_key_shift_ctrl_alt_altgr = SHIFT_KEY_CODE;
					size = 0;
					if (m_conn_handle != BLE_CONN_HANDLE_INVALID)
					{
						while (size < MAX_KEYS_IN_ONE_REPORT)
						{
							keys_send(1, p_key);
							p_key++;
							size++;
						}
					}
					kb_key_shift_ctrl_alt_altgr = NO_SPECIFIC_KEY_CODE;
					break;		
					
				default:
					for (i = 0; i < strlen((const char*)message); i++)
					{
						app_uart_put(message[i]);
					}
					break;
			}
            break;

        case APP_UART_COMMUNICATION_ERROR:
            APP_ERROR_HANDLER(p_event->data.error_communication);
            break;

        case APP_UART_FIFO_ERROR:
            APP_ERROR_HANDLER(p_event->data.error_code);
            break;

        default:
            break;
    }
}

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)
{
    ret_code_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 (bsp_button_is_pressed(SHIFT_BUTTON_ID))
        {
            data[MODIFIER_KEY_POS] |= SHIFT_KEY_CODE;
        }
		*/
		
		data[MODIFIER_KEY_POS] |= kb_key_shift_ctrl_alt_altgr;			//$at_12_04_22
		
        if (!m_in_boot_mode)
        {
            err_code = ble_hids_inp_rep_send(p_hids,
                                             INPUT_REPORT_KEYS_INDEX,
                                             INPUT_REPORT_KEYS_MAX_LEN,
                                             data,
                                             m_conn_handle);
        }
        else
        {
            err_code = ble_hids_boot_kb_inp_rep_send(p_hids,
                                                     INPUT_REPORT_KEYS_MAX_LEN,
                                                     data,
                                                     m_conn_handle);
        }

        if (err_code != NRF_SUCCESS)
        {
            break;
        }

        offset++;
    }
    while (offset <= data_len);

    *p_actual_len = offset;

    return err_code;
}

thanks for help

Alexandre

Parents
  • good morning Terje,

    II understood well keys_send() function and buffer_emqueue() and the buffer_dequeue(). I increase the MAX_BUFFER_ENTRIES at 254 for my tests. I can send the string: 0123456789 aabbccddee AABBCCDDEE ffgghhiijj and receive on HID keyboard smartphone the same string But sometime one character adds at 4th place --> space or 'j' . 

    0123456789 aabbccddee AABBCCDDEE ffgghhiijj
    keycode = 27 1E 1F 20 21 22 23 24 25 26 2C 14 14 05 05 06 06 07 07 08 08 2C 14 14 05 05 06 06 07 07 08 08 2C 09 09 0A 0A 0B 0B 0C 0C 0D 0D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00


    count_trace_send_key_scan = A6
    scan = [00]27 00 1E 00 1F 00 20 21 22 23 24 25 26 2C 14 14 05 05 06 06 07 07 08 08 2C 14 14 05 05 06 06 07 07 08 08 2C 09 09 0A 0A 0B 0B 0C 0C 0D 0D 00 20 00 21 00 00 22 00 00 23 00 00 24 00 00 25 00 00 26 00 00 2C 00 00 14 00 00 14 00 00 05 00 00 05 00 00 06


    count_trace_buffer_enqueue = 29
    enqueue = [00]1F 20 21 22 23 24 25 26 2C 14 14 05 05 06 06 07 07 08 08 2C 14 14 05 05 06 06 07 07 08 08 2C 09 09 0A 0A 0B 0B 0C 0C 0D 0D

    012j3456789 aabbccddee AABBCCDDEE ffgghhiijj
    keycode = 27 1E 1F 20 21 22 23 24 25 26 2C 14 14 05 05 06 06 07 07 08 08 2C 14 14 05 05 06 06 07 07 08 08 2C 09 09 0A 0A 0B 0B 0C 0C 0D 0D 2C 09 09 0A 0A 0B 0B 0C 0C 0D 0D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00


    count_trace_send_key_scan = A6
    scan = [00]27 00 1E 00 1F 00 20 21 22 23 24 25 26 2C 14 14 05 05 06 06 07 07 08 08 2C 14 14 05 05 06 06 07 07 08 08 2C 09 09 0A 0A 0B 0B 0C 0C 0D 0D 00 00 00 20 00 00 21 00 00 22 00 23 00 00 24 00 00 25 00 00 26 00 00 2C 00 00 14 00 00 14 00 00 05 00 00 05 00

    count_trace_buffer_enqueue = 29
    enqueue = [00]1F 20 21 22 23 24 25 26 2C 14 14 05 05 06 06 07 07 08 08 2C 14 14 05 05 06 06 07 07 08 08 2C 09 09 0A 0A 0B 0B 0C 0C 0D 0D

    Do you have a maximum characters to send ? because at each key scan the input report take 8 bytes. Maybe it's the sending 43 characters successive because 43 * 8 bytes = 344 bytes ?

    Thanks 

    Alexandre

    static uint8_t m_sample3_str_ascii_keyboard[] = "0123456789 aabbccddee AABBCCDDEE ffgghhiijj";	
    static uint8_t m_sample5_str_ascii_keyboard[] = "0123456789 aabbccddee AABBCCDDEE ";	
    
    /**@brief   Function for handling app_uart events.
     *
     * @details This function will receive a single character from the app_uart module and append it to
     *          a string. 
     */
    /**@snippet [Handling the data received over UART] */
    #ifdef AT_DBG
    void uart_event_handle(app_uart_evt_t * p_event)					//$at_06_04_22
    {
    	uint8_t message[UARTE_MAX_DATA_LEN] = "UART OK";
    	
    	uint16_t tmp;
    	static uint8_t buf_keycode[256];
    	static uint8_t buf_modifier_keycode[256];
        	
        static uint8_t data_array[UARTE_MAX_DATA_LEN];
        static uint8_t index = 0;
    	
    	static int16_t i = 0;
    			
        switch (p_event->evt_type)
        {
            case APP_UART_DATA_READY:
                UNUSED_VARIABLE(app_uart_get(&data_array[index]));
    			
    			switch(data_array[0])
    			{
    					
    				case 'c':
    					for (i = 0; i < sizeof(trace_buffer_enqueue); i++)
    					{
    						trace_buffer_enqueue[i] = 0;
    					}
    					if (m_conn_handle != BLE_CONN_HANDLE_INVALID)
    					{
    						if (BUFFER_LIST_EMPTY())
    						{
    							count_trace_buffer_enqueue = count_trace_send_key_scan = 0;
    							for (i = 0; i < strlen((const char *)m_sample3_str_ascii_keyboard); i++)
    							{
    								conv_Ascii_to_keycode(TABLE_FRENCH_KEYBOARD_ID189, m_sample3_str_ascii_keyboard[i], &buf_keycode[i], &buf_modifier_keycode[i]);
    								keys_send(1, &buf_keycode[i], &buf_modifier_keycode[i]);
    							}
    						}
    						else
    						{
    							send_string_uart((uint8_t *)" NO EMPTY ", strlen(" NO EMPTY ")); 
    						}
    					}
    					break;			
                    
                    case 'e':
    					for (i = 0; i < sizeof(trace_buffer_enqueue); i++)
    					{
    						trace_buffer_enqueue[i] = 0;
    					}
    					if (m_conn_handle != BLE_CONN_HANDLE_INVALID)
    					{
    						if (BUFFER_LIST_EMPTY())
    						{
    							count_trace_buffer_enqueue = count_trace_send_key_scan = 0;
    							for (i = 0; i < strlen((const char *)m_sample5_str_ascii_keyboard); i++)
    							{
    								conv_Ascii_to_keycode(TABLE_FRENCH_KEYBOARD_ID189, m_sample5_str_ascii_keyboard[i], &buf_keycode[i], &buf_modifier_keycode[i]);
    								keys_send(1, &buf_keycode[i], &buf_modifier_keycode[i]);
    							}
    						}
    						else
    						{
    							send_string_uart((uint8_t *)" NO EMPTY ", strlen(" NO EMPTY ")); 
    						}
    					}
    					break;			
    			
    				case '0':
    					send_string_uart((uint8_t *)" keycode = ", strlen(" keycode = ")); 
    					for (i = 0; i < strlen((const char *)m_sample1_str_ascii_keyboard); i++)
    					{
    						tmp = conv_hexa_to_ascii(buf_keycode[i]);
    						app_uart_put(tmp >> 8);
    						app_uart_put(tmp & 0x00FF);
    						app_uart_put(' ');
    					}
    					send_string_uart((uint8_t *)" END ", strlen(" END ")); 
    					app_uart_put('\x0D');
    					break;
    				
    				case '1':
    					send_string_uart((uint8_t *)" count_trace_send_key_scan = ", strlen(" count_trace_send_key_scan = "));
    					tmp = conv_hexa_to_ascii(count_trace_send_key_scan);
    					app_uart_put(tmp >> 8);
    					app_uart_put(tmp & 0x00FF);
    					app_uart_put('\x0D');	
    					break;
    				
    				case '2':
    					send_string_uart((uint8_t *)" count_trace_buffer_enqueue = ", strlen(" count_trace_buffer_enqueue = "));
    					tmp = conv_hexa_to_ascii(count_trace_buffer_enqueue);
    					app_uart_put(tmp >> 8);
    					app_uart_put(tmp & 0x00FF);
    					app_uart_put('\x0D');	
    					break;
    
    				case '3':
    					send_string_uart((uint8_t *)" scan = ", strlen("  scan = ")); 
    					for (i = 0; i < count_trace_send_key_scan; i++)
    					{
    						tmp = conv_hexa_to_ascii(trace_send_key_scan[i]);
    						app_uart_put(tmp >> 8);
    						app_uart_put(tmp & 0x00FF);
    						app_uart_put(' ');
    					}
    					app_uart_put('\x0D');
    					break;
    				
    				case '4':
    					send_string_uart((uint8_t *)" enqueue = ", strlen("  enqueue = ")); 
    					for (i = 0; i < count_trace_buffer_enqueue; i++)
    					{
    						tmp = conv_hexa_to_ascii(trace_buffer_enqueue[i]);
    						app_uart_put(tmp >> 8);
    						app_uart_put(tmp & 0x00FF);
    						app_uart_put(' ');
    					}
    					app_uart_put('\x0D');
    					break;
    
    				default:
    					send_string_uart(message, strlen((const char *)message));
    					break;
    			}
                break;
    
            case APP_UART_COMMUNICATION_ERROR:
                APP_ERROR_HANDLER(p_event->data.error_communication);
                break;
    
            case APP_UART_FIFO_ERROR:
                APP_ERROR_HANDLER(p_event->data.error_code);
                break;
    
            default:
                break;
        }
    }
    #endif
    
    static void keys_send(uint8_t key_pattern_len, uint8_t * p_key_pattern, uint8_t * p_modifier_key)
    {
        ret_code_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,
    											p_modifier_key);
        // 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 == NRF_ERROR_RESOURCES) && (actual_len <= key_pattern_len))
        {
            // Buffer enqueue routine return value is not intentionally checked.
            // Rationale: Its better to have 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
            UNUSED_VARIABLE(buffer_enqueue(&m_hids, p_key_pattern, key_pattern_len, actual_len, p_modifier_key));
    		//APP_ERROR_CHECK(buffer_enqueue(&m_hids, p_key_pattern, key_pattern_len, actual_len));		//$at_13_04_22
        }
    
    
        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)
           )
        {
            APP_ERROR_HANDLER(err_code);
        }
    }
    
    static uint32_t buffer_enqueue(ble_hids_t * p_hids,
                                   uint8_t    * p_key_pattern,
                                   uint16_t     pattern_len,
                                   uint16_t     offset,
    								uint8_t * p_modifier_key)
    {
        buffer_entry_t * element;
        uint32_t         err_code = NRF_SUCCESS;
    
        if (BUFFER_LIST_FULL())
        {
            // Element cannot be buffered.
            err_code = NRF_ERROR_NO_MEM;
        }
        else
        {
            // Make entry of buffer element and copy data.
            element              = &buffer_list.buffer[(buffer_list.wp)];
            element->p_instance  = p_hids;
            element->p_data      = p_key_pattern;
            element->data_offset = offset;
            element->data_len    = pattern_len;
    		element->modifier_key = *p_modifier_key;
    
            buffer_list.count++;
            buffer_list.wp++;
    
            if (buffer_list.wp == MAX_BUFFER_ENTRIES)
            {
                buffer_list.wp = 0;
            }
    		trace_buffer_enqueue[count_trace_buffer_enqueue] = *p_key_pattern; // dbg
    		count_trace_buffer_enqueue++;
        }
    
        return err_code;
    }
    
    
    

  • Hi,

    I am sorry for the delay. Are you still seeing issues, or have you figured it out on your own in the mean time?

    I still recommend that you replace UNUSED_VARIABLE with APP_ERROR_CHECK as mentioned in my previous reply, for debugging this. Then you will get an error when buffer_enqueue fails, instead of it silently failing. That will help you pinpoint where the issue is. Similarly, for any other function calls returning an error code, you should check the return value. For some such calls the value is already checked, as some values can be handled programmatically.

    Regards,
    Terje

  • Dear Terje,

    1) Sorry for my delay, I will onto another problem and I'm going to vacation in 2 days. I can't get it to work  APP_ERROR_CHECK, I don't fit into the function. Maybe be a flag enable into sdk_config.h ?

    I have made good progress on my application with the nrf52840 module by limiting the number of characters to 32.


    **@brief Function for error handling, which is called when an error has occurred.
     *
     * @warning This handler is an example only and does not fit a final product. You need to analyze
     *          how your product is supposed to react in case of error.
     *
     * @param[in] error_code  Error code supplied to the handler.
     * @param[in] line_num    Line number where the handler is called.
     * @param[in] p_file_name Pointer to the file name.
     */
    void app_error_handler_bare(ret_code_t error_code)
    {
        error_info_t error_info =
        {
            .line_num    = 0,
            .p_file_name = NULL,
            .err_code    = error_code,
        };
    
        app_error_fault_handler(NRF_FAULT_ID_SDK_ERROR, 0, (uint32_t)(&error_info));
    
        UNUSED_VARIABLE(error_info);
    }

    2) I'm looking for a solution to know when I'm dis-apparaired to my smartphone ? I modified the following function "static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)"  to receive character 'C' for connected and 'D' for disconnected.


    /**@brief Function for handling BLE events.
     *
     * @param[in]   p_ble_evt   Bluetooth stack event.
     * @param[in]   p_context   Unused.
     */
    static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
    {
        ret_code_t err_code;
    
        switch (p_ble_evt->header.evt_id)
        {
            case BLE_GAP_EVT_CONNECTED:
                NRF_LOG_INFO("Connected");
    			app_uart_put('C');																			//$at_06_04_22
                err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
                APP_ERROR_CHECK(err_code);
                m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
                err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GAP_EVT_DISCONNECTED:
                NRF_LOG_INFO("Disconnected");
    			app_uart_put('D');																			//$at_06_04_22		
                // Dequeue all keys without transmission.
                (void) buffer_dequeue(false);
    
                m_conn_handle = BLE_CONN_HANDLE_INVALID;
    
                // Reset m_caps_on variable. Upon reconnect, the HID host will re-send the Output
                // report containing the Caps lock state.
                m_caps_on = false;
                // disabling alert 3. signal - used for capslock ON
                err_code = bsp_indication_set(BSP_INDICATE_ALERT_OFF);
                APP_ERROR_CHECK(err_code);
    
                break; // BLE_GAP_EVT_DISCONNECTED
    
            case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
            {
                NRF_LOG_DEBUG("PHY update request.");
                ble_gap_phys_t const phys =
                {
                    .rx_phys = BLE_GAP_PHY_AUTO,
                    .tx_phys = BLE_GAP_PHY_AUTO,
                };
                err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
                APP_ERROR_CHECK(err_code);
            } break;
    
            case BLE_GATTS_EVT_HVN_TX_COMPLETE:
                // Send next key event
                (void) buffer_dequeue(true);
                break;
    
            case BLE_GATTC_EVT_TIMEOUT:
                // Disconnect on GATT Client timeout event.
                NRF_LOG_DEBUG("GATT Client Timeout.");
                err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
                                                 BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GATTS_EVT_TIMEOUT:
                // Disconnect on GATT Server timeout event.
                NRF_LOG_DEBUG("GATT Server Timeout.");
                err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
                                                 BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                APP_ERROR_CHECK(err_code);
                break;
    
            default:
                // No implementation needed.
                break;
        }
    }
    

    Good day

    Alexandre

  • Dear Terje,

    I have made 2 prototypes in BLE which seems to work but when I move away from a PC I can't send anything, the BLE module tells me that I am connected ? I will switch to the nrf Connect SDK solution  + IDE from segger SES.

    Good day

    alexandre

Reply Children
No Data
Related