2026-03-14 8:42 PM
Hello, I've been trying to setup bi-directional communication with USB-HID on a custom board with STM32WB55CEU. I can send data to the PC from the STM just fine. My usb stack runs, enumerates and reports correctly. However, when trying to send Data from the PC back to the STM, I always receive garbage packages.
Here's my device descriptor for the Outreport:
0x09, 0x03, // USAGE (Vendor Usage 3)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x04, // REPORT_COUNT (4)
0x91, 0x02, // OUTPUT (Data,Var,Abs)So 4 Bytes. I also tried this with and without adding a Report ID, which did not change the results whatsoever.
I have also tried running the code with defining USB_CUSTOMHID_REPORT_BUFFER_EVENT_ENABLED; both in main.h and in usbd_customhid.c and usbd_customhid.h to no changes.
I also tried changing CUSTOM_HID_EPOUT_SIZE to 0x04 and 0x05, same with USBD_CUSTOMHID_OUTREPORT_BUF_SIZE. Including all of the combinations with and without report ID and with and without enabling the buffer define.
Here is my OutReport callback:
static int8_t CUSTOM_HID_OutEvent_FS(uint8_t event_idx, uint8_t state)
{
/* USER CODE BEGIN 6 */
/* Start next USB packet transfer once data processing is completed */
USBD_CUSTOM_HID_ReceivePacket(&hUsbDeviceFS);
uint8_t *buffer = (uint8_t*) ((uint32_t) event_idx);
uint8_t a = buffer[0];
uint8_t b = buffer[1];
uint8_t c = buffer[2];
uint8_t d = buffer[3];
USBD_CUSTOM_HID_HandleTypeDef* hhid = (USBD_CUSTOM_HID_HandleTypeDef*)hUsbDeviceFS.pClassData;
uint8_t *buffer2 = hhid->Report_buf;
uint8_t e = buffer2[0];
uint8_t f = buffer2[1];
uint8_t g = buffer2[2];
uint8_t h = buffer2[3];
if(buffer[0] ==0x01|| buffer2[0]==0x01){
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
}
return (USBD_OK);
/* USER CODE END 6 */
}I tried putting both USBD_CUSTOM_HID_ReceivePacket(), before AND after handling the data, but this did not change anything. The callback does trigger correctly, so the stack works for triggering OutReports, and I would inspect the contents of the buffers with breakpoint. The first method of accessing buffer I saw from this: https://stm32world.com/wiki/STM32_USB_Custom_HID. This method however, always produces the same seemingly random bytes. The second buffer2 on the other hand, accessing it the "proper" way, would always just mirror the contents of event_idx and state, which leads me to believe the hhid->Report_buf is never initialized and is just reading the first memory in stack. The contents of the 2 buffers never were even close to the bytes I was sending.
I send the HID report on my Linux computer through <hidapi/hidapi.h>, compiled with hidapi-hidraw and this code:
char report[4] = { 0x01,0x02,0x03,0x04};
int res = hid_write(handle, report, 4*sizeof(char));
I honestly don't know what else to try or where else to look. This seems like a hyperspecific issue and I have no clue why it's happening. I find it odd that the callback triggers but without the correct data, how does that even happen? I would appreciate the help.
Solved! Go to Solution.
2026-04-02 8:26 AM
Thanks for the detailed feedback.
What you observed is consistent with how the OS HID stack treats your interface: since the HID type you defined normally doesn’t have an interrupt OUT EP / report, the OS effectively ignores or filters those OUT reports instead of delivering the payload to the interrupt OUT endpoint. That’s why your CUSTOM_HID_OutEvent_FS was only seeing garbage and not the bytes you sent.
By switching the channel to a Feature report, the data is now carried over the standard HID control path (SET_REPORT/GET_REPORT on EP0), which your OS accepts without applying those usage specific restrictions.
So, for your current use case, using a Feature report is valid.
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2026-03-15 4:09 AM
Okay Update: I think I know in which function it's going wrong, but I don't know why. In this function: USBD_StatusTypeDef USBD_LL_DataOutStage(USBD_HandleTypeDef *pdev, uint8_t epnum, uint8_t *pdata).
USBD_StatusTypeDef USBD_LL_DataOutStage(USBD_HandleTypeDef *pdev,uint8_t epnum, uint8_t *pdata)
{
//...
if (epnum == 0U)
{
pep = &pdev->ep_out[0];
if (pdev->ep0_state == USBD_EP0_DATA_OUT) // <- NEVER hits breakpoint, pdev->ep0_state is always 5
{
//...
}
else
{
// my OutReport gets handled as handshake(?) i think = garbage data.
}
//...
}I don't know why ep0_state is incorrect, however. Is this an issue on the PC side and how I send the packet?
2026-03-16 2:19 AM
First, I suggest you to add logging message instead of inserting breakpoint. You can't step through every USB stage to debug this;
Second, event_idx and state are decoded values from the OUT report, not a pointer to the raw buffer. That’s why casting event_idx to an address gives you random data.
Here is a reference example to use CUSTOM_HID_OutEvent_FS
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2026-03-16 6:06 AM
Thank you for the reply,
I can't use logging because neither UART nor SWDO Pins are available. It is irrelevant in this case anyways, because I can still breakpoint and inspect the bytes.
When defining USB_CUSTOMHID_REPORT_BUFFER_EVENT_ENABLED the signature of the callback changes:
#ifdef USBD_CUSTOMHID_REPORT_BUFFER_EVENT_ENABLED
((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData[pdev->classId])->OutEvent(hhid->Report_buf);
#else
((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData[pdev->classId])->OutEvent(hhid->Report_buf[0],
hhid->Report_buf[1]);
Which does not matter either anyhow, because I tested the data through both methods, because both of them contained the identical bytes matching event_idx and state anyways. Both of the methods of derefercing the data, produced the SAME result.
The reference isn't very useful, because as I have stated clearly, the custom sent bytes are not present anywhere in the data. It doesn't matter if I only use the first 2 bytes, because they are still just random garbage data.
2026-03-18 6:29 AM
Can you confirm an interrupt OUT endpoint really exists and is opened using USBD_LL_OpenEP()? Can you provide a trace using USB analyzer or Wireshark/USBPCap sniffer? Maybe the host is not sending an Interrupt OUT ?
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2026-03-20 4:49 AM
Yes, USBD_LL_OpenEP() happens.
The OS should be sending an Interrupt Out, unless CUSTOM_HID_OutEvent_FS still triggers on control node actions? I am not familiar enough with the STM architecture to know that. Regardless, I ended up changing it from an OutReport to a FeatureReport, and that works as expected now and I am able to send custom data.
I think the OS is eating the custom bytes that I am sending because it refuses to send an OutReport to the HID type that I defined which typically has no OutReport. I suspect making it a composite device would also fix my issue, but because it works with a FeatureReport right now, I don't want to try, unless there is a significant drawback of a FeatureReport that I haven't run into yet.
2026-04-02 8:26 AM
Thanks for the detailed feedback.
What you observed is consistent with how the OS HID stack treats your interface: since the HID type you defined normally doesn’t have an interrupt OUT EP / report, the OS effectively ignores or filters those OUT reports instead of delivering the payload to the interrupt OUT endpoint. That’s why your CUSTOM_HID_OutEvent_FS was only seeing garbage and not the bytes you sent.
By switching the channel to a Feature report, the data is now carried over the standard HID control path (SET_REPORT/GET_REPORT on EP0), which your OS accepts without applying those usage specific restrictions.
So, for your current use case, using a Feature report is valid.
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.