Skip to main content
Associate II
June 10, 2026
Question

STM32F411 USB CDC Host

  • June 10, 2026
  • 6 replies
  • 91 views

I have a laser engraver that runs on an ESP32 S2 Mini microcontroller. It has the following signature: “Espressif CDC Device” and emulates a COM-type serial port. I had two identical cables; if I connect my engraver to my PC via a serial program (NinjaTerm), I can control GRBL. I cut one of these cables, and they have four wires: VBUS, D+, D-, and GND. After crimping some connectors onto the ends of the wires and testing the cable connected to the port to distinguish between my D+ and D-, 

Now I’d like to put an STM32F411 (WeAct BlackPill v3.0) between my engraver and my PC to control my homemade Z-axis. The PC side is on a UART port of the STM32 connected to a CH340, and on the engraver side, a USB (D+/D-) CDC Host (Note: I’m not using the USB-C connector already soldered onto the BlackPill). The UART programming side worked perfectly. Where it’s getting stuck is on the CDC side… no response… 
 

 

#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include "usbh_cdc.h"

/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart2;

void HAL_UART_Printf(UART_HandleTypeDef Uart, const char *Format, ...)
{
unsigned char TmpStr[128];

va_list args;
va_start(args, Format);
vsprintf((char*)TmpStr, Format, args);
va_end(args);

HAL_UART_Transmit(&Uart, TmpStr, strlen((char*)TmpStr), 10);
}

extern USBH_HandleTypeDef hUsbHostFS;
extern ApplicationTypeDef Appli_state;

void ParseCommand(char *line)
{
if (strncmp(line, "M900", 4) == 0)
{
return;
}

if (strncmp(line, "M901", 4) == 0)
{
return;
}

char txbuf[150];
sprintf(txbuf,"%s\r\n",line);

USBH_CDC_Transmit(&hUsbHostFS, (uint8_t*)txbuf, strlen(txbuf));
}

char UartCmd[256];
uint8_t UartCmdCount = 0;
uint8_t UartRx;

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART2)
{
HAL_UART_Printf(*huart, "Uart2Receive\r\n");

if (UartRx == '\n' || UartRx == '\r')
{
UartCmd[UartCmdCount] = '\0';
ParseCommand(UartCmd);
UartCmdCount = 0;
}
else
{
UartCmd[UartCmdCount++] = UartRx;
if (UartCmdCount > 256) UartCmdCount = 0;
}

HAL_UART_Receive_IT(huart, &UartRx, 1);
}
}

#define RX_BUFF_SIZE 64 /* Max Received data 1KB */
uint8_t CDC_RX_Buffer[RX_BUFF_SIZE];

void USBH_CDC_RxCpltCallback(USBH_HandleTypeDef *phost)
{
HAL_UART_Printf(huart2, "CDC Receive\r\n");
/* Get the length of data received */
//uint16_t length = USBH_CDC_GetLastRxSize(phost);

/* Copy data to your FIFO/Ring Buffer for non-blocking processing */
//Process_Rx_Data(rx_buffer, length);

//if (length > 0)
//HAL_UART_Transmit(&huart2, CDC_RX_Buffer, length, 10);

/* Re-arm the CDC receive interrupt for the next packet */
USBH_CDC_Receive(phost, CDC_RX_Buffer, sizeof(CDC_RX_Buffer));
}



/****** MAIN PART *********************/
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_USB_HOST_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Printf(huart2, "\x1b[94mWelcome\r\n");
HAL_UART_Receive_IT(&huart2, &UartRx, 1);
USBH_CDC_Receive(&hUsbHostFS, CDC_RX_Buffer, 1);
static uint8_t once = 0;

/* USER CODE END 2 */

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
MX_USB_HOST_Process();

/* USER CODE BEGIN 3 */
HAL_UART_Printf(huart2, "My name is %s and I`ve %d year old.\r\n", "Nobody", 30);
HAL_Delay(1000);

if (Appli_state == APPLICATION_READY)
{
if (!once)
{
HAL_UART_Printf(huart2,"CDC READY\r\n");

char cmd[] = "$$\r\n";
USBH_CDC_Transmit(&hUsbHostFS, (uint8_t*)cmd, strlen(cmd));
once = 1;
}
}
}
/* USER CODE END 3 */

 

6 replies

ST Technical Moderator
June 10, 2026

Hello ​@Fleuve 

The WeAct BlackPill is not an official ST product.

ST resources support genuine ST products. ST does not guarantee that clones or counterfeit products operate correctly with the firmware that ST provides.

Thank you for your understanding.

If work with this board must continue, use one of the ST USB CDC host examples as a comparison with the project and check for any differences.
BR
Gyessine

In order to give better visibility on the answered topics, please click on 'Best answer' on the reply which solved your issue or answered your question.
Andrew Neil
Super User
June 10, 2026

As ​@Gyessine said, the Black Pill is not an ST Product, and might not have a genuine STM32.

 

Therefore it would be best to first get this working on a genuine ST board:

https://www.st.com/en/microcontrollers-microprocessors/stm32f411ce.html#tools-software

Once you’ve got it working on a genuine ST board, you could then move on to the Black Pill

 

Note that connecting to USB via flying leads is not going to give great results;  you’d be better using a board with a dedicated USB host socket - looks like the 32F411EDISCOVERY is the best bet for that ...

A complex system that works is invariably found to have evolved from a simple system that worked.A complex system designed from scratch never works and cannot be patched up to make it work.
Ozone
Principal
June 10, 2026

> Where it’s getting stuck is on the CDC side… no response… 

Not quite sure what you mean, supposedly not the CH340 converter, but the “Falcon” engraver side.

BTW, I have two BluePill boards, and no indication the F411 MCUs on those are not genuine. Just saying.


But the Bluepill boards have a debug connector that works fine, I had tried a ST-Link and a JLink.
So, you could debug your code, and check the USB-related part.

Pavel A.
June 10, 2026

So, the CH340 and UART part is working fine, and the problem is the USB CDC host function, correct?

Does your board provide power to the connected USB device or is it self-powered?  Does the device detect and start enumerating - or no response at all, to any host request?

 

 

 

FleuveAuthor
Associate II
June 11, 2026

@Gyessine  ​@Andrew Neil 

Thank you for your reply. When I chose my microcontroller last year for my LCD project, I selected it for its high clock speed. So I bought a BlackPill for development and five STM32F411CEU6 chips. I prototype everything on a breadboard and always manufacture my own final PCBs. So I used one for the LCD screen project, and the other was a relay controller with serial control. I had three left over. I’ll probably try soldering one onto a picnic table for a breadboard. I have nothing against an official development board. As far as I’m concerned, a NUCLEO-F411RE with its STM32F411RET6 chip could do the trick… but first I’d like to analyze two schematics to determine the bare minimum requirements for the MCU. I already have an Altium version of the other MCU.


Next part below :D

Andrew Neil
Super User
June 16, 2026

@Fleuve 

 I prototype everything on a breadboard

Breadboards aren’t great for high-speed digital circuits - such as USB.

You will be far better off using a dev board with known-good USB hardware.

 

NUCLEO-F411RE with its STM32F411RET6 chip could do the trick

It does not have any hardware for the microcontroller’s USB.

As previously noted, the 32F411EDISCOVERY would be far more appropriate ...

A complex system that works is invariably found to have evolved from a simple system that worked.A complex system designed from scratch never works and cannot be patched up to make it work.
FleuveAuthor
Associate II
June 11, 2026

@Ozone  ​@Pavel A. 

Yeah Uart part working great with NinjaTerm. 
 

I've had time to run a few tests... the code currently generated by CubeMX seems to be doing something. I stepped through it in the debugger, and it passes MX_USB_HOST_Init.

if (USBH_Init(&hUsbHostFS, USBH_UserProcess, HOST_FS) != USBH_OK)
{
Error_Handler();
}
if (USBH_RegisterClass(&hUsbHostFS, USBH_CDC_CLASS) != USBH_OK)
{
Error_Handler();
}
if (USBH_Start(&hUsbHostFS) != USBH_OK)
{
Error_Handler();
}

No Error!!

I also modified my USBH_UserProcess to include UART outputs. Here are the IDs detected...

#define HOST_USER_CONNECTION                    0x04U
#define HOST_USER_DISCONNECTION 0x05U
#define HOST_USER_UNRECOVERED_ERROR 0x06U

but never HOST_USER_CLASS_ACTIVE... it’s as if I have a HOST but no CDC connection... I have two things to try.

1. I need to put my connector back on and not use my modified cable. With the connector, I can connect my shield case to ground.
 

2. I also have a BluePill... I should program it as a USB CDC device. Maybe it’s the CDC on my ESP32 that’s acting up.