Skip to main content
Visitor II
December 27, 2025
Question

USB Printer with STM32

  • December 27, 2025
  • 2 replies
  • 74 views

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?

    This topic has been closed for replies.

    2 replies

    Super User
    December 28, 2025

    Which kind of libraries? Do you want to print simple text or graphics? Do you need to deal with fonts, formatting languages etc.? 

     

    Visitor II
    December 28, 2025

    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?

    Graduate
    December 28, 2025

    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.

    Visitor II
    January 2, 2026

    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);
    }