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

[nRF52840][usbd_hid_composite] How does Windows transmit CAPS LOCK state to the device without output endpoint?

Hello, I am a new bee in using nRF52840. I have looked into the given example in SDK called usbd_hid_composite (infocenter.nordicsemi.com/index.jsp. It worked well in my computer. I can use the four buttons and see the LEDs turning on and off when I press CAPS LOCK. I noticed in the codes two input endpoint are set to transmit the button states, but I did not see any output endpoint from Windows to transmit CAPS LOCK state. Then how does the board know the CAPS LOCK state and reflect it with LEDs?

Also, I want to change the original code later. The default keyboard output letter is "g" when the button 4 is pressed. I want to have a output endpoint from PC and then I can change the letter from the PC side. Is this realizable? Does anyone know how to do that?

Thank you for your support in advance.

Kind regards,

Louis

  • Hi Louis,

    HID specification says:  "The Interrupt Out pipe is optional. If a device declares an Interrupt Out endpoint then Output reports are transmitted by the host to the device through the Interrupt Out endpoint. If no Interrupt Out endpoint is declared then Output reports are transmitted to a device through the Control endpoint, using Set_Report(Output) requests".

    You can see that transfer is going through NRF_DRV_USBD_EPOUT0 in app_usbd_hid_kbd.c.

    Also, I want to change the original code later. The default keyboard output letter is "g" when the button 4 is pressed. I want to have a output endpoint from PC and then I can change the letter from the PC side. Is this realizable? Does anyone know how to do that?

    I think the right way would be to implement a feature report, but this requires changing SDK code (there's no handling for feature reports). A quick and dirty way is to write a report handler (case APP_USBD_HID_USER_EVT_OUT_REPORT_READY) that will parse 5 bits of LEDs as a command for your application. From Windows side, call HidD_SetOutputReport.

  • Dear Dmitry,

    Thank you for your kind support again.

    HID specification says:  "The Interrupt Out pipe is optional. If a device declares an Interrupt Out endpoint then Output reports are transmitted by the host to the device through the Interrupt Out endpoint. If no Interrupt Out endpoint is declared then Output reports are transmitted to a device through the Control endpoint, using Set_Report(Output) requests".

    OK, I understand now.

    A quick and dirty way is to write a report handler (case APP_USBD_HID_USER_EVT_OUT_REPORT_READY) that will parse 5 bits of LEDs as a command for your application. From Windows side, call HidD_SetOutputReport.

    What do you mean by parsing 5 bits of LEDs as a command for my application? Could you be more specific? (Sorry I am a new bee to IOT)

  • Here is a USB HID keyboard report descriptor from app_usbd_hid_kbd_desc.h:

    0x95, 0x05, /* REPORT_COUNT (5) */\
    0x75, 0x01, /* REPORT_SIZE (1) */\
    0x05, 0x08, /* USAGE_PAGE (LEDs) */\
    0x19, 0x01, /* USAGE_MINIMUM (Num Lock) */\
    0x29, 0x05, /* USAGE_MAXIMUM (Kana) */\
    0x91, 0x02, /* OUTPUT (Data,Var,Abs) */\
    0x95, 0x01, /* REPORT_COUNT (1) */\
    0x75, 0x03, /* REPORT_SIZE (3) */\
    0x91, 0x03, /* OUTPUT (Cnst,Var,Abs) */\

    You can see that it declares 5 bits for LED state and three bits of padding in output report. I mean that without modification of SDK code, you can receive only these 5 bits and treat them on your own (as LED states, as 5-bit command to change a letter, etc...)
    Of course, you can increase output report size (with some trick, see this thread).

  • OK, I see what you meant, but how do I extract the 5-bit LED states in order to use them as conditions in main.c and how do I switch the letter from "g" to other letters, say "h"?

    case APP_USBD_HID_USER_EVT_OUT_REPORT_READY:
        /* Only one output report IS defined for HID keyboard class. Update LEDs state. */
    
        if(LED == 5b'00000) letter = "h"; // I got the spirit but do not know how to implement it. :(
        
        bsp_board_led_invert(LED_HID_REP);
        kbd_status();
        break;

  • Someway like this:

    const void * app_usbd_hid_kbd_out_report_get(app_usbd_hid_kbd_t const * p_kbd,
    size_t * p_size);
    
    size_t size;
    uint8_t *report = (uint8_t *) app_usbd_hid_kbd_out_report_get(&m_app_hid_kbd, &size);
    uint8_t LED= report[1];
    if (LED == 0x5) letter = APP_USBD_HID_KBD_H;

    To change letter, search for CONFIG_KBD_LETTER and replace it with a global variable holding your letter. Note that keyboard codes are not ASCII codes (look at app_usbd_hid_kbd.h)

Related