2025-12-26 7:05 PM
How would I go about implementing a USB host for a usb printer. I would like to send things through my STM32 to be printed. I’ve seen adaptations or some work done on the USB printer class as a device but I am unsure how I would do this as a host? Any ideas or already implemented libraries?
2025-12-27 4:43 PM
Which kind of libraries? Do you want to print simple text or graphics? Do you need to deal with fonts, formatting languages etc.?
2025-12-27 9:53 PM
Well, im wondering if I need a special USB printer host class to send anything including text and graphics. Is there just a bulk endpoint I can send whatever print data to?
2025-12-27 11:41 PM
Why don't you start by reading USB printer class specification document?
You need to know something about the printer you want to use. Simple printers use simple protocol with usually one OUT endpoint and simple command language. Modern laser and inkjet printers are much more sophisticated and may be hard to interact with.
2026-01-02 2:26 PM - edited 2026-01-02 3:00 PM
Hi. So I started writing a Printer Host Class based of the already implemented CDC class. I so far have just implemented sending stuff through the Bulk OUT endpoint. I wanted to know if I am on the right track. Here is what I have so far. Or should I just use the already existing MSC and adapt it for printer to make it easy?
#ifndef __USBH_PRINTER_H
#define __USBH_PRINTER_H
#ifdef __cplusplus
extern "C" {
#endif
#include "usbh_core.h"
typedef struct _PRINTER_Process
{
uint8_t interface;
uint8_t InEp;
uint8_t OutEp;
uint8_t InPipe;
uint8_t OutPipe;
uint16_t InEpSize;
uint16_t OutEpSize;
uint8_t *pTxData;
uint32_t TxDataLength;
uint8_t *pRxData;
uint32_t RxDataLength;
PRINTER_StateTypeDef state;
}
PRINTER_HandleTypeDef;
typedef enum
{
PRINTER_IDLE = 0U,
PRINTER_SEND_DATA,
PRINTER_SEND_DATA_WAIT,
PRINTER_ERROR
}
PRINTER_StateTypeDef;
extern USBH_ClassTypeDef PRINTER_Class;
#define USBH_PRINTER_CLASS &PRINTER_Class
USBH_StatusTypeDef USBH_PRINTER_IOProcess(USBH_HandleTypeDef *phost);
USBH_StatusTypeDef USBH_PRINTER_Init(USBH_HandleTypeDef *phost);
#ifdef __cplusplus
}
#endif
#endif#include "usbh_printer.h"
static USBH_StatusTypeDef USBH_PRINTER_InterfaceInit(USBH_HandleTypeDef *phost);
static USBH_StatusTypeDef USBH_PRINTER_InterfaceDeInit(USBH_HandleTypeDef *phost);
static USBH_StatusTypeDef USBH_PRINTER_Process(USBH_HandleTypeDef *phost);
static USBH_StatusTypeDef USBH_PRINTER_ClassRequest(USBH_HandleTypeDef *phost);
USBH_ClassTypeDef PRINTER_Class =
{
"PRINTER",
USB_PRINTER_CLASS,
USBH_PRINTER_InterfaceInit,
USBH_PRINTER_InterfaceDeInit,
USBH_PRINTER_ClassRequest,
USBH_PRINTER_Process
};
static USBH_StatusTypeDef USBH_PRINTER_InterfaceInit(USBH_HandleTypeDef *phost)
{
USBH_StatusTypeDef status;
uint8_t interface;
PRINTER_HandleTypeDef *PRINTER_Handle;
interface = USBH_FindInterface(phost, 0x07, 0x01, 0x02); //Get interface index for specified printer values
if ((interface == 0xFFU) || (interface >= USBH_MAX_NUM_INTERFACES)) {
USBH_DbgLog("Cannot Find the interface for Printer Class.", phost->pActiveClass->Name);
return USBH_FAIL;
}
status = USBH_SelectInterface(phost, interface); //Change phost selected interface
if (status != USBH_OK)
{
return USBH_FAIL;
}
phost->pActiveClass->pData = (PRINTER_HandleTypeDef *)USBH_malloc(sizeof(PRINTER_HandleTypeDef));
PRINTER_Handle = (PRINTER_HandleTypeDef *) phost->pActiveClass->pData;
if (PRINTER_Handle == NULL)
{
USBH_DbgLog("Cannot allocate memory for Printer Handle");
return USBH_FAIL;
}
(void)USBH_memset(PRINTER_Handle, 0, sizeof(PRINTER_HandleTypeDef));
if ((phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress & 0x80U) != 0U){
PRINTER_Handle->InEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress;
PRINTER_Handle->InEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].wMaxPacketSize;
} else {
PRINTER_Handle->OutEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress;
PRINTER_Handle->OutEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].wMaxPacketSize;
}
if ((phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].bEndpointAddress & 0x80U) != 0U){
PRINTER_Handle->InEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].bEndpointAddress;
PRINTER_Handle->InEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].wMaxPacketSize;
} else {
PRINTER_Handle->OutEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].bEndpointAddress;
PRINTER_Handle->OutEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].wMaxPacketSize;
}
PRINTER_Handle->OutPipe = USBH_AllocPipe(phost, PRINTER_Handle->OutEp);
PRINTER_Handle->InPipe = USBH_AllocPipe(phost, PRINTER_Handle->InEp);
(void)USBH_OpenPipe(phost, PRINTER_Handle->OutPipe, PRINTER_Handle->OutEp, phost->device.address, phost->device.speed, USB_EP_TYPE_BULK, PRINTER_Handle->OutEpSize);
(void)USBH_OpenPipe(phost, PRINTER_Handle->InPipe, PRINTER_Handle->InEp, phost->device.address, phost->device.speed, USB_EP_TYPE_BULK, PRINTER_Handle->InEpSize);
(void)USBH_LL_SetToggle(phost, PRINTER_Handle->OutPipe, 0U);
(void)USBH_LL_SetToggle(phost, PRINTER_Handle->InPipe, 0U);
return USBH_OK;
}
USBH_StatusTypeDef USBH_PRINTER_InterfaceDeInit(USBH_HandleTypeDef *phost)
{
PRINTER_HandleTypeDef *PRINTER_Handle = (PRINTER_HandleTypeDef *) phost->pActiveClass->pData;
if ((PRINTER_Handle->InPipe) != 0U) {
(void)USBH_ClosePipe(phost, PRINTER_Handle->InPipe);
(void)USBH_FreePipe(phost, PRINTER_Handle->InPipe);
PRINTER_Handle->InPipe = 0U;
}
if ((PRINTER_Handle->OutPipe) != 0U) {
(void)USBH_ClosePipe(phost, PRINTER_Handle->OutPipe);
(void)USBH_FreePipe(phost, PRINTER_Handle->OutPipe);
PRINTER_Handle->OutPipe = 0U;
}
if ((phost->pActiveClass->pData) != NULL) {
USBH_free(phost->pActiveClass->pData);
phost->pActiveClass->pData = 0U;
}
return USBH_OK;
}
static USBH_StatusTypeDef USBH_PRINTER_ClassRequest(USBH_HandleTypeDef *phost)
{
phost->pUser(phost, HOST_USER_CLASS_ACTIVE);
return USBH_OK;
}
USBH_StatusTypeDef USBH_PRINTER_Transmit(USBH_HandleTypeDef *phost, uint8_t *pbuff, uint32_t length)
{
USBH_StatusTypeDef Status = USBH_BUSY;
PRINTER_HandleTypeDef *PRINTER_Handle = (PRINTER_HandleTypeDef *) phost->pActiveClass->pData;
if ((PRINTER_Handle->state == PRINTER_IDLE))
{
PRINTER_Handle->pTxData = pbuff;
PRINTER_Handle->TxDataLength = length;
PRINTER_Handle->state = PRINTER_SEND_DATA;
Status = USBH_OK;
#if (USBH_USE_OS == 1U)
USBH_OS_PutMessage(phost, USBH_CLASS_EVENT, 0U, 0U);
#endif /* (USBH_USE_OS == 1U) */
}
return Status;
}
static USBH_StatusTypeDef USBH_PRINTER_Process(USBH_HandleTypeDef *phost)
{
USBH_StatusTypeDef status = USBH_BUSY;
USBH_StatusTypeDef req_status = USBH_OK;
USBH_URBStateTypeDef URB_Status = USBH_URB_IDLE;
PRINTER_HandleTypeDef *PRINTER_Handle = (PRINTER_HandleTypeDef *) phost->pActiveClass->pData;
switch (PRINTER_Handle->state) {
case PRINTER_IDLE:
status = USBH_OK;
break;
case PRINTER_SEND_DATA:
// submit one chunk
if (PRINTER_Handle->TxDataLength > PRINTER_Handle->OutEpSize)
USBH_BulkSendData(phost, PRINTER_Handle->pTxData, PRINTER_Handle->OutEpSize,
PRINTER_Handle->OutPipe, 1U);
else
USBH_BulkSendData(phost, PRINTER_Handle->pTxData, PRINTER_Handle->TxDataLength,
PRINTER_Handle->OutPipe, 1U);
PRINTER_Handle->state = PRINTER_SEND_DATA_WAIT;
break;
case PRINTER_SEND_DATA_WAIT:
URB_Status = USBH_LL_GetURBState(phost, PRINTER_Handle->OutPipe);
if (URB_Status == USBH_URB_DONE)
{
if (PRINTER_Handle->TxDataLength > PRINTER_Handle->OutEpSize)
{
PRINTER_Handle->TxDataLength -= PRINTER_Handle->OutEpSize;
PRINTER_Handle->pTxData += PRINTER_Handle->OutEpSize;
PRINTER_Handle->state = PRINTER_SEND_DATA;
}
else
{
PRINTER_Handle->TxDataLength = 0;
PRINTER_Handle->state = PRINTER_IDLE;
USBH_PRINTER_TransmitCallback(phost);
}
}
else if (URB_Status == USBH_URB_NOTREADY)
{
// NAK
PRINTER_Handle->state = PRINTER_SEND_DATA;
}
else if (URB_Status == USBH_URB_ERROR || URB_Status == USBH_URB_STALL)
{
PRINTER_Handle->state = PRINTER_ERROR;
}
break;
case PRINTER_ERROR:
req_status = USBH_ClrFeature(phost, 0x00U);
if (req_status == USBH_OK)
{
PRINTER_Handle->state = PRINTER_IDLE;
}
break;
default:
break;
}
return USBH_OK;
}
USBH_StatusTypeDef USBH_PRINTER_Init(USBH_HandleTypeDef *phost)
{
USBH_StatusTypeDef Status = USBH_BUSY;
#if (USBH_USE_OS == 1U)
osEvent event;
event = osMessageGet(phost->class_ready_event, osWaitForever);
if (event.status == osEventMessage)
{
if (event.value.v == USBH_CLASS_EVENT)
{
#else
while ((Status == USBH_BUSY) || (Status == USBH_FAIL))
{
/* Host background process */
USBH_Process(phost);
if (phost->gState == HOST_CLASS)
{
#endif
Status = USBH_OK;
}
}
return Status;
}
USBH_StatusTypeDef USBH_PRINTER_IOProcess(USBH_HandleTypeDef *phost)
{
if (phost->device.is_connected == 1U)
{
if (phost->gState == HOST_CLASS)
{
USBH_PRINTER_Process(phost);
}
}
return USBH_OK;
}
__weak void USBH_PRINTER_TransmitCallback(USBH_HandleTypeDef *phost)
{
UNUSED(phost);
}
2026-01-09 1:29 AM
Hi @PeppersONLY
We do not provide a direct example for hosting a printer using the classic middleware library or USBX. However, if you plan to use the USBX stack, this thread might be helpful for your implementation.
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-01-09 7:26 AM
Does this mean that for USBX this won’t work on bare metal only a RTOS?
2026-01-09 8:16 AM
Not specifically @PeppersONLY. This implementation should work on both bare metal and in RTOS context.
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-01-09 8:25 AM - edited 2026-01-09 8:27 AM
@PeppersONLY wrote:I wanted to know if I am on the right track.
As @gbm said, have you looked at the documentation for the printer(s) you're thinking of using?
No use diving into this Printer Host Class if your intended printer(s) don't use it !
You still haven't given any indication of what type(s) of printers you want to use ...
Also, what STM32 ?
How to write your question to maximize your chances to find a solution
2026-01-10 1:07 PM
I don't understand how I could use the USBX stack on bare metal? Doesn't USBX rely on the handling of a RTOS like Azure.