USB Config Descriptor -> Interface/Endpoint Descriptor

本文介绍了一个用于USB设备描述符解析的实现方案,包括配置描述符、接口描述符及端点描述符的设置方法。通过这些函数,可以将USB设备的各种描述符信息正确地解析并保存到内存中。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

USB_COMMON_DESCRIPTOR * USBH_X_GetNextDescriptor(
  USB_COMMON_DESCRIPTOR * pbCommDescriptor,
  U32 * wNextDescriptorOffset )
{
  USB_COMMON_DESCRIPTOR * pNextCommDescriptor;
  U8 bLength = pbCommDescriptor->bLength;
  *wNextDescriptorOffset += bLength;
  pNextCommDescriptor = (USB_COMMON_DESCRIPTOR *) ( ( (U8 *) pbCommDescriptor ) + bLength );
  return pNextCommDescriptor;
}

void USBH_X_SetEpDescriptor(
  TUSBH_X_DEVICE * pDevice,
  U8 bIfNumber,
  U8 bEpNumber,
  USB_ENDPOINT_DESCRIPTOR * pEpDescriptor )
{
  pDevice->pEndpointDescriptorArray[ bIfNumber ][ bEpNumber ] = pEpDescriptor;
}

void USBH_X_SetIfDescriptor(
  TUSBH_X_DEVICE * pDevice,
  U8 bIfNumber,
  USB_INTERFACE_DESCRIPTOR * pIfDescriptor )
{
  pDevice->pInterfaceDescriptorArray[ bIfNumber ] = pIfDescriptor;
}

void USBH_X_SetCfgDescriptor(
  TUSBH_X_DEVICE * pDevice )
{
  U32 interfaceIndex = 0;
  U32 endpointIndex = 0;

// Points to the current configuration descriptor
  USB_COMMON_DESCRIPTOR * pNextDescriptor;
  USB_CONFIGURATION_DESCRIPTOR * pCfgDescriptor;
  USB_INTERFACE_DESCRIPTOR * pIfDescriptor;
  USB_ENDPOINT_DESCRIPTOR * pEpDescriptor;

  pCfgDescriptor = (USB_CONFIGURATION_DESCRIPTOR*) ( ( pDevice->pUsbDevice->pConfigDescriptor ) );
  pNextDescriptor = (USB_COMMON_DESCRIPTOR*) ( pCfgDescriptor );

  U32 wTotalLength = pCfgDescriptor->wTotalLength;
  U32 wNextDescriptorOffset = USB_CONFIGURATION_DESCRIPTOR_LENGTH;

  pDevice->pCurrentConfigurationDescriptor = pCfgDescriptor;

  interfaceIndex = 0;
  while ( wNextDescriptorOffset < wTotalLength )
  {
    pNextDescriptor = USBH_X_GetNextDescriptor( pNextDescriptor, &wNextDescriptorOffset );
    if ( pNextDescriptor->bDescriptorType == USB_DESC_TYPE_INTERFACE )
    {
      pIfDescriptor = (USB_INTERFACE_DESCRIPTOR*) pNextDescriptor;
      if ( pIfDescriptor->bAlternateSetting == 0 )
      {
        USBH_X_SetIfDescriptor( pDevice, interfaceIndex, pIfDescriptor );
        // Parse Ep descriptors relative to the current interface
        endpointIndex = 0;
        while ( endpointIndex < pIfDescriptor->bNumEndpoints )
        {
          pNextDescriptor = USBH_X_GetNextDescriptor( pNextDescriptor, &wNextDescriptorOffset );
          if ( pNextDescriptor->bDescriptorType == USB_DESC_TYPE_ENDPOINT )
          {
            pEpDescriptor = (USB_ENDPOINT_DESCRIPTOR *) pNextDescriptor;
            USBH_X_SetEpDescriptor( pDevice, interfaceIndex, endpointIndex, pEpDescriptor );
            endpointIndex++;
            if ( ( endpointIndex == USBH_X_DEV_MAX_ENDPOINT )|| ( endpointIndex == pIfDescriptor->bNumEndpoints) )
break; } } interfaceIndex++; if ( ( interfaceIndex == USBH_X_DEV_MAX_IF )||( interfaceIndex == pDevice->pUsbDevice->InterfaceCount) )
return; } } } } void USBH_X_SetDevDescriptor( TUSBH_X_DEVICE * pDevice ) { pDevice->pDeviceDescriptor = (USBH_DEVICE_DESCRIPTOR*) ( &( pDevice->pUsbDevice->DeviceDescriptor ) ); }
#include <EspUsbHost.h> #ifndef __EspUsbHostPrinter_H__ #define __EspUsbHostPrinter_H__ class EspUsbHostPrinter : public EspUsbHost { public: bool isReady = false; uint8_t interval; unsigned long lastCheck; usb_transfer_t *usbTransfer = NULL; // 添加初始化 /** * @brief 处理USB配置描述符的回调函数 * * 该函数在USB主机枚举设备时被调用,用于识别打印机类接口并分配端点。 * * @param bDescriptorType 描述符类型 * @param p 指向描述符数据的指针 */ void onConfig(const uint8_t bDescriptorType, const uint8_t *p) { switch (bDescriptorType) { case USB_B_DESCRIPTOR_TYPE_INTERFACE: { const usb_intf_desc_t *intf = (const usb_intf_desc_t *)p; // 识别打印机类接口 if ((intf->bInterfaceClass == USB_CLASS_PRINTER) && (intf->bInterfaceSubClass == 1)) { esp_err_t err = usb_host_interface_claim(this->clientHandle, this->deviceHandle, intf->bInterfaceNumber, intf->bAlternateSetting); if (err != ESP_OK) { ESP_LOGI("EspUsbHostPrinter", "usb_host_interface_claim() err=%x", err); } } } break; case USB_B_DESCRIPTOR_TYPE_ENDPOINT: if (this->usbTransfer == NULL) { const usb_ep_desc_t *endpoint = (const usb_ep_desc_t *)p; esp_err_t err; // 检查是否为中断传输 if ((endpoint->bmAttributes & USB_BM_ATTRIBUTES_XFERTYPE_MASK) != USB_BM_ATTRIBUTES_XFER_INT) { return; } // 检查端点方向是否为输入(IN) if (endpoint->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_DIR_MASK) { // 分配传输结构体 err = usb_host_transfer_alloc(8, 0, &this->usbTransfer); if (err != ESP_OK) { this->usbTransfer = NULL; return; } // 初始化传输结构体参数 this->usbTransfer->device_handle = this->deviceHandle; this->usbTransfer->bEndpointAddress = endpoint->bEndpointAddress; this->usbTransfer->callback = this->_onStatus; this->usbTransfer->context = this; interval = endpoint->bInterval; isReady = true; } } break; } }; /** * @brief USB打印机状态传输回调函数 * * @param transfer 指向USB传输结构体的指针 */ static void _onStatus(usb_transfer_t *transfer) { if (transfer->status == 0) { EspUsbHostPrinter *usbHost = (EspUsbHostPrinter*)transfer->context; usbHost->onStatus(transfer); } }; /** * @brief 处理接收到的打印机状态数据 * * @param transfer USB传输结构体指针 */ virtual void onStatus(usb_transfer_t *transfer) { // 子类需要实现此方法来处理接收到的数据 }; /** * @brief 发送打印数据到USB打印机 * * @param data 要发送的数据指针 * @param length 数据长度 * @return esp_err_t 发送结果 */ esp_err_t printData(const uint8_t *data, size_t length) { // 实现数据发送逻辑 // 这里需要创建输出端点传输并发送数据 return ESP_OK; }; /** * @brief USB打印机任务处理函数 */ void task(void) { EspUsbHost::task(); if (this->isReady) { unsigned long now = millis(); if ((now - this->lastCheck) > this->interval) { this->lastCheck = now; this->usbTransfer->num_bytes = 8; esp_err_t err = usb_host_transfer_submit(this->usbTransfer); if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) { ESP_LOGI("EspUsbHostPrinter", "usb_host_transfer_submit() err=%x", err); } } } } }; #endif class MyEspUsbHostPrinter : public EspUsbHostPrinter { public: /** * @brief 处理接收到的USB数据 * @param transfer 指向USB传输数据结构的指针 */ void onStatus(usb_transfer_t *transfer) override { uint8_t *const p = transfer->data_buffer; // 打印接收到的数据 Serial.println("Received: "); for (int i = 0; i < transfer->num_bytes; i++) { Serial.print(p[i], HEX); //Serial.print(" "); } }; /** * @brief 发送数据到USB设备 * @param data 要发送的数据 * @param length 数据长度 */ esp_err_t sendData(const uint8_t *data, size_t length) { return printData(data, length); } }; MyEspUsbHostPrinter usbHost; void setup() { Serial.begin(115200); Serial.println("USB Host Printer Example"); usbHost.begin(); } void loop() { usbHost.task(); // 示例:发送数据 uint8_t testData[] = "?C+ASK\r\n\r\n"; // 打印机初始化命令 esp_err_t err = usbHost.sendData(testData, sizeof(testData)); /* if (err != ESP_OK) { ESP_LOGI("EspUsbHostPrinter", "sendData() err=%x", err); } else { ESP_LOGI("EspUsbHostPrinter", "sendData() success"); }*/ } 简化、优化。接口只保留对USB数据处理(包括发送和接收),各种判断放在主程序处理
08-16
#include <EspUsbHost.h> class EspUsbHostPrinter : public EspUsbHost { public: bool outputEndpointAvailable = false; uint8_t outputEndpointAddress = 0; bool inputEndpointAvailable = false; uint8_t inputEndpointAddress = 0; bool dataReceived = false; uint8_t receivedData[512]; size_t receivedLength = 0; // 添加传输状态跟踪和超时机制 bool sendTransferPending = false; unsigned long sendTransferStartTime = 0; const unsigned long sendTransferTimeout = 5000; // 5秒超时 // 传输统计 unsigned long transfersSubmitted = 0; unsigned long transfersCompleted = 0; unsigned long transfersFailed = 0; // 设备信息 uint16_t vendorId = 0; uint16_t productId = 0; uint8_t deviceClass = 0; uint8_t deviceSubClass = 0; uint8_t deviceProtocol = 0; // 设备连接状态跟踪 bool deviceInfoReceived = false; unsigned long deviceConnectedTime = 0; bool enumerationAttempted = false; /** * @brief 检查USB设备是否已连接 */ bool isDeviceConnected() { return deviceHandle != nullptr; } /** * @brief USB传输完成回调函数(用于发送数据) */ static void transferCallback(usb_transfer_t *transfer) { if (transfer) { EspUsbHostPrinter *host = (EspUsbHostPrinter *)transfer->context; host->sendTransferPending = false; host->sendTransferStartTime = 0; host->transfersCompleted++; ESP_LOGI("EspUsbHostPrinter", "Send Transfer completed, status: %d, bytes: %d", transfer->status, transfer->actual_num_bytes); // 打印传输的详细信息 if (transfer->status != USB_TRANSFER_STATUS_COMPLETED) { ESP_LOGW("EspUsbHostPrinter", "Send transfer not completed successfully, status: %d", transfer->status); host->transfersFailed++; } usb_host_transfer_free(transfer); } } /** * @brief USB接收传输完成回调函数 */ static void receiveCallback(usb_transfer_t *transfer) { EspUsbHostPrinter *host = (EspUsbHostPrinter *)transfer->context; if (transfer) { ESP_LOGI("EspUsbHostPrinter", "Receive Transfer status: %d, bytes: %d", transfer->status, transfer->actual_num_bytes); if (transfer->status == USB_TRANSFER_STATUS_COMPLETED && transfer->actual_num_bytes > 0) { size_t actual_bytes = transfer->actual_num_bytes; host->dataReceived = true; host->receivedLength = actual_bytes; if (host->receivedLength > sizeof(host->receivedData) - 1) { host->receivedLength = sizeof(host->receivedData) - 1; } memcpy(host->receivedData, transfer->data_buffer, host->receivedLength); host->receivedData[host->receivedLength] = '\0'; ESP_LOGI("EspUsbHostPrinter", "Data received: %d bytes", host->receivedLength); // 打印接收到的原始数据 ESP_LOG_BUFFER_HEX("USB_RX", host->receivedData, host->receivedLength); } else if (transfer->status != USB_TRANSFER_STATUS_COMPLETED) { ESP_LOGW("EspUsbHostPrinter", "Receive transfer not completed, status: %d", transfer->status); } } // 重新提交接收传输以继续监听数据 if (transfer) { esp_err_t err = usb_host_transfer_submit(transfer); if (err != ESP_OK) { ESP_LOGE("EspUsbHostPrinter", "Failed to re-submit receive transfer: %d", err); usb_host_transfer_free(transfer); } } } /** * @brief 处理USB设备连接事件 */ void onEvent(const usb_host_client_event_msg_t *event) { if (event->event == USB_HOST_CLIENT_EVENT_NEW_DEV) { ESP_LOGI("EspUsbHostPrinter", "New USB device connected"); deviceInfoReceived = false; // 重置设备信息接收状态 enumerationAttempted = false; deviceConnectedTime = millis(); } else if (event->event == USB_HOST_CLIENT_EVENT_DEV_GONE) { ESP_LOGI("EspUsbHostPrinter", "USB device disconnected"); resetEndpoint(); } else { ESP_LOGI("EspUsbHostPrinter", "USB Event: %d", event->event); } } /** * @brief 处理USB配置描述符的回调函数 */ void onConfig(const uint8_t bDescriptorType, const uint8_t *p) override { switch (bDescriptorType) { case USB_B_DESCRIPTOR_TYPE_DEVICE: { if (p == nullptr) { ESP_LOGW("EspUsbHostPrinter", "Device descriptor pointer is null"); break; } const usb_device_desc_t *device_desc = (const usb_device_desc_t *)p; vendorId = device_desc->idVendor; productId = device_desc->idProduct; deviceClass = device_desc->bDeviceClass; deviceSubClass = device_desc->bDeviceSubClass; deviceProtocol = device_desc->bDeviceProtocol; deviceInfoReceived = true; // 标记已接收到设备信息 ESP_LOGI("EspUsbHostPrinter", "Device descriptor - Class: 0x%02x, SubClass: 0x%02x, Protocol: 0x%02x, Vendor: 0x%04x, Product: 0x%04x", device_desc->bDeviceClass, device_desc->bDeviceSubClass, device_desc->bDeviceProtocol, device_desc->idVendor, device_desc->idProduct); break; } case USB_B_DESCRIPTOR_TYPE_CONFIGURATION: { if (p == nullptr) { ESP_LOGW("EspUsbHostPrinter", "Configuration descriptor pointer is null"); break; } const usb_config_desc_t *config_desc = (const usb_config_desc_t *)p; ESP_LOGI("EspUsbHostPrinter", "Configuration descriptor - Interfaces: %d, ConfigValue: %d", config_desc->bNumInterfaces, config_desc->bConfigurationValue); break; } case USB_B_DESCRIPTOR_TYPE_INTERFACE: { if (p == nullptr) { ESP_LOGW("EspUsbHostPrinter", "Interface descriptor pointer is null"); break; } const usb_intf_desc_t *intf = (const usb_intf_desc_t *)p; ESP_LOGI("EspUsbHostPrinter", "Interface descriptor - Class: 0x%02x, Subclass: 0x%02x, Protocol: 0x%02x, Number: %d", intf->bInterfaceClass, intf->bInterfaceSubClass, intf->bInterfaceProtocol, intf->bInterfaceNumber); // 支持多种设备类型,包括所有可能的CDC类设备 bool isCompatible = (intf->bInterfaceClass == USB_CLASS_PRINTER) || (intf->bInterfaceClass == 0x0A) || // CDC数据类 (intf->bInterfaceClass == 0x02) || // CDC控制类 (intf->bInterfaceClass == 0xFF) || // Vendor specific (intf->bInterfaceClass == 0x00); // 设备特定类 if (isCompatible) { ESP_LOGI("EspUsbHostPrinter", "Compatible interface found (Class: 0x%02x)", intf->bInterfaceClass); esp_err_t err = usb_host_interface_claim(clientHandle, deviceHandle, intf->bInterfaceNumber, intf->bAlternateSetting); if (err != ESP_OK) { ESP_LOGW("EspUsbHostPrinter", "Claim interface err=%x", err); } } else { ESP_LOGI("EspUsbHostPrinter", "Non-compatible interface found, class: 0x%02x", intf->bInterfaceClass); } break; } case USB_B_DESCRIPTOR_TYPE_ENDPOINT: { if (p == nullptr) { ESP_LOGW("EspUsbHostPrinter", "Endpoint descriptor pointer is null"); break; } const usb_ep_desc_t *endpoint = (const usb_ep_desc_t *)p; ESP_LOGI("EspUsbHostPrinter", "Endpoint - Addr: 0x%02x, Attr: 0x%02x, MaxPacket: %d, Interval: %d", endpoint->bEndpointAddress, endpoint->bmAttributes, endpoint->wMaxPacketSize, endpoint->bInterval); if ((endpoint->bmAttributes & USB_BM_ATTRIBUTES_XFERTYPE_MASK) == USB_BM_ATTRIBUTES_XFER_BULK) { ESP_LOGI("EspUsbHostPrinter", "Bulk endpoint detected"); if (!(endpoint->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_DIR_MASK)) { outputEndpointAddress = endpoint->bEndpointAddress; outputEndpointAvailable = true; ESP_LOGI("EspUsbHostPrinter", "Bulk OUT endpoint configured: 0x%02x", outputEndpointAddress); } else { inputEndpointAddress = endpoint->bEndpointAddress; inputEndpointAvailable = true; ESP_LOGI("EspUsbHostPrinter", "Bulk IN endpoint configured: 0x%02x", inputEndpointAddress); // 为输入端点启动接收传输 startReceiveTransfer(); } } else if ((endpoint->bmAttributes & USB_BM_ATTRIBUTES_XFERTYPE_MASK) == USB_BM_ATTRIBUTES_XFER_INT) { ESP_LOGI("EspUsbHostPrinter", "Interrupt endpoint detected"); if (endpoint->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_DIR_MASK) { inputEndpointAddress = endpoint->bEndpointAddress; inputEndpointAvailable = true; ESP_LOGI("EspUsbHostPrinter", "Interrupt IN endpoint configured: 0x%02x", inputEndpointAddress); // 为输入端点启动接收传输 startReceiveTransfer(); } } else { uint8_t transferType = endpoint->bmAttributes & USB_BM_ATTRIBUTES_XFERTYPE_MASK; switch(transferType) { case USB_BM_ATTRIBUTES_XFER_CONTROL: ESP_LOGI("EspUsbHostPrinter", "Control endpoint: 0x%02x", endpoint->bEndpointAddress); break; case USB_BM_ATTRIBUTES_XFER_ISOC: ESP_LOGI("EspUsbHostPrinter", "Isochronous endpoint: 0x%02x", endpoint->bEndpointAddress); break; } } break; } default: ESP_LOGD("EspUsbHostPrinter", "Unhandled descriptor type: %d", bDescriptorType); break; } } /** * @brief 启动接收传输 */ esp_err_t startReceiveTransfer() { if (!inputEndpointAvailable) { ESP_LOGW("EspUsbHostPrinter", "Cannot start receive transfer: input endpoint not available"); return ESP_ERR_NOT_FOUND; } usb_transfer_t *transfer = nullptr; esp_err_t err = usb_host_transfer_alloc(512, 0, &transfer); if (err != ESP_OK) { ESP_LOGE("EspUsbHostPrinter", "Failed to allocate receive transfer: %d", err); return err; } transfer->device_handle = deviceHandle; transfer->bEndpointAddress = inputEndpointAddress; transfer->num_bytes = 512; transfer->callback = receiveCallback; transfer->context = this; ESP_LOGI("EspUsbHostPrinter", "Starting receive transfer on endpoint 0x%02x", inputEndpointAddress); err = usb_host_transfer_submit(transfer); if (err != ESP_OK) { ESP_LOGE("EspUsbHostPrinter", "Failed to submit receive transfer: %d", err); usb_host_transfer_free(transfer); } return err; } /** * @brief 向USB设备发送数据 */ esp_err_t sendData(const uint8_t *data, size_t length) { if (data == nullptr || length == 0) { ESP_LOGW("EspUsbHostPrinter", "SendData failed: Invalid arguments"); return ESP_ERR_INVALID_ARG; } if (!outputEndpointAvailable) { ESP_LOGW("EspUsbHostPrinter", "SendData failed: Endpoint not available"); return ESP_ERR_NOT_FOUND; } // 检查超时,强制清除挂起状态 if (sendTransferPending) { unsigned long currentTime = millis(); if ((currentTime - sendTransferStartTime) > sendTransferTimeout) { ESP_LOGW("EspUsbHostPrinter", "Send transfer timeout (%lu ms), forcing clear", (currentTime - sendTransferStartTime)); sendTransferPending = false; sendTransferStartTime = 0; } else { ESP_LOGW("EspUsbHostPrinter", "SendData failed: Previous transfer still pending (%lu ms)", (currentTime - sendTransferStartTime)); return ESP_ERR_NOT_FINISHED; } } usb_transfer_t *transfer = nullptr; esp_err_t err = usb_host_transfer_alloc(length, 0, &transfer); if (err != ESP_OK) { ESP_LOGE("EspUsbHostPrinter", "Transfer alloc failed: %d", err); return err; } transfer->device_handle = deviceHandle; transfer->bEndpointAddress = outputEndpointAddress; transfer->num_bytes = length; transfer->callback = transferCallback; transfer->context = this; memcpy(transfer->data_buffer, data, length); ESP_LOGI("EspUsbHostPrinter", "Sending %d bytes to endpoint 0x%02x", length, outputEndpointAddress); ESP_LOG_BUFFER_HEX("USB_TX", data, length); sendTransferPending = true; sendTransferStartTime = millis(); transfersSubmitted++; err = usb_host_transfer_submit(transfer); if (err != ESP_OK) { ESP_LOGE("EspUsbHostPrinter", "Transfer submit failed: %d", err); sendTransferPending = false; sendTransferStartTime = 0; transfersFailed++; usb_host_transfer_free(transfer); } return err; } /** * @brief 执行USB主机任务 */ void task() { EspUsbHost::task(); } /** * @brief 重置端点状态 */ void resetEndpoint() { outputEndpointAvailable = false; outputEndpointAddress = 0; inputEndpointAvailable = false; inputEndpointAddress = 0; dataReceived = false; receivedLength = 0; sendTransferPending = false; sendTransferStartTime = 0; deviceInfoReceived = false; enumerationAttempted = false; } /** * @brief 获取端点状态信息 */ void printEndpointStatus() { ESP_LOGI("EspUsbHostPrinter", "Endpoints - OUT: %s (0x%02x), IN: %s (0x%02x)", outputEndpointAvailable ? "Available" : "Not available", outputEndpointAddress, inputEndpointAvailable ? "Available" : "Not available", inputEndpointAddress); } /** * @brief 获取设备信息 */ void printDeviceInfo() { ESP_LOGI("EspUsbHostPrinter", "Device Info - Vendor: 0x%04x, Product: 0x%04x, Class: 0x%02x, SubClass: 0x%02x, Protocol: 0x%02x", vendorId, productId, deviceClass, deviceSubClass, deviceProtocol); // 根据设备信息提供可能的建议 if (vendorId == 0x10C4 && productId == 0xEA60) { ESP_LOGI("EspUsbHostPrinter", "Detected CP210x device - common USB-to-Serial converter"); } else if (vendorId == 0x0403 && productId == 0x6001) { ESP_LOGI("EspUsbHostPrinter", "Detected FTDI device - common USB-to-Serial converter"); } else if (vendorId == 0x2341 || vendorId == 0x1A86) { ESP_LOGI("EspUsbHostPrinter", "Detected Arduino/CH340 device"); } } /** * @brief 打印完整的设备描述符信息 */ void printFullDeviceInfo() { Serial.println("=== 完整设备信息 ==="); Serial.printf("设备连接时间: %lu ms\n", deviceConnectedTime); Serial.printf("Vendor ID: 0x%04X\n", vendorId); Serial.printf("Product ID: 0x%04X\n", productId); Serial.printf("Device Class: 0x%02X\n", deviceClass); Serial.printf("Device SubClass: 0x%02X\n", deviceSubClass); Serial.printf("Device Protocol: 0x%02X\n", deviceProtocol); Serial.printf("设备信息接收: %s\n", deviceInfoReceived ? "是" : "否"); Serial.printf("枚举尝试: %s\n", enumerationAttempted ? "是" : "否"); Serial.printf("输出端点: %s (地址: 0x%02X)\n", outputEndpointAvailable ? "可用" : "不可用", outputEndpointAddress); Serial.printf("输入端点: %s (地址: 0x%02X)\n", inputEndpointAvailable ? "可用" : "不可用", inputEndpointAddress); // 根据设备类型提供信息 if (deviceClass == 0x00 && deviceSubClass == 0x00) { Serial.println("设备类型: 设备特定类设备"); } else if (deviceClass == 0xFF) { Serial.println("设备类型: 厂商特定设备"); } else if (deviceClass == 0x07) { Serial.println("设备类型: 打印机类设备"); } else if (deviceClass == 0x02) { Serial.println("设备类型: 通讯设备类(CDC)"); } else if (deviceClass == 0x0A) { Serial.println("设备类型: CDC数据类设备"); } else if (deviceClass == 0x03) { Serial.println("设备类型: HID设备"); } else if (deviceClass == 0x08) { Serial.println("设备类型: 存储设备"); } Serial.println("=================="); } /** * @brief 详细分析设备类型 */ void analyzeDeviceType() { Serial.println("=== 设备类型分析 ==="); if (!deviceInfoReceived) { Serial.println("警告: 尚未接收到设备信息,可能设备未正确枚举"); unsigned long timeSinceConnect = millis() - deviceConnectedTime; Serial.printf("自连接以来时间: %lu ms\n", timeSinceConnect); Serial.println("=================="); return; } // 根据Vendor ID和Product ID判断设备类型 if (vendorId == 0x046D) { Serial.println("可能设备: Logitech设备"); } else if (vendorId == 0x0951) { Serial.println("可能设备: Kingston存储设备"); } else if (vendorId == 0x0781) { Serial.println("可能设备: SanDisk存储设备"); } else if (vendorId == 0x1949) { Serial.println("可能设备: Kindle设备"); } else if (vendorId == 0x05AC) { Serial.println("可能设备: Apple设备"); } else if (vendorId == 0x04F2) { Serial.println("可能设备: Webcam设备"); } else if (vendorId == 0x045E) { Serial.println("可能设备: Microsoft设备"); } else if (vendorId == 0x04A9) { Serial.println("可能设备: Canon设备"); } else if (vendorId == 0x04B3) { Serial.println("可能设备: IBM设备"); } else if (vendorId == 0x0000 && productId == 0x0000) { Serial.println("错误: 无效的设备ID,设备可能未正确枚举"); } else { Serial.printf("未知设备: Vendor ID 0x%04X, Product ID 0x%04X\n", vendorId, productId); } // 根据设备类判断 switch(deviceClass) { case 0x00: Serial.println("设备类: 设备特定类"); break; case 0x01: Serial.println("设备类: 音频设备"); break; case 0x02: Serial.println("设备类: 通讯设备(CDC)"); break; case 0x03: Serial.println("设备类: HID设备"); if (deviceSubClass == 0x01 && deviceProtocol == 0x02) { Serial.println("具体类型: 鼠标"); } else if (deviceSubClass == 0x01 && deviceProtocol == 0x01) { Serial.println("具体类型: 键盘"); } break; case 0x05: Serial.println("设备类: 物理设备"); break; case 0x06: Serial.println("设备类: 图像设备"); break; case 0x07: Serial.println("设备类: 打印机设备"); break; case 0x08: Serial.println("设备类: 存储设备"); break; case 0x09: Serial.println("设备类: 集线器设备"); break; case 0x0A: Serial.println("设备类: CDC数据设备"); break; case 0xFF: Serial.println("设备类: 厂商特定设备"); break; default: Serial.printf("设备类: 未知 (0x%02X)\n", deviceClass); break; } Serial.println("=================="); } /** * @brief 检查接收传输状态 */ void checkReceiveStatus() { Serial.println("=== 接收状态检查 ==="); Serial.printf("输入端点可用: %s\n", inputEndpointAvailable ? "是" : "否"); Serial.printf("输入端点地址: 0x%02X\n", inputEndpointAddress); if (inputEndpointAvailable) { Serial.println("尝试启动接收传输..."); esp_err_t err = startReceiveTransfer(); if (err == ESP_OK) { Serial.println("接收传输启动成功"); } else { Serial.printf("接收传输启动失败: %d\n", err); } } Serial.println("=================="); } /** * @brief 检查是否有接收到数据 */ bool hasDataReceived() { return dataReceived; } /** * @brief 获取接收到的数据 */ void getReceivedData(uint8_t *data, size_t &length) { if (dataReceived && data && length >= receivedLength) { memcpy(data, receivedData, receivedLength); length = receivedLength; dataReceived = false; } else { length = 0; } } /** * @brief 清除接收数据标志 */ void clearReceivedData() { dataReceived = false; receivedLength = 0; } /** * @brief 尝试发送不同长度的数据包来测试设备 */ void testDevice() { Serial.println("Running device test..."); printTransferStats(); // 测试1: 发送单个字节 Serial.println("Test 1: Sending single byte 0x55"); uint8_t singleByte = 0x55; esp_err_t res = sendData(&singleByte, 1); printResult("Single byte test", res); delayAndCheck(3000); // 测试2: 发送短数据包 Serial.println("Test 2: Sending short data packet"); const uint8_t shortPacket[] = {0x00, 0x01, 0x02, 0x03}; res = sendData(shortPacket, sizeof(shortPacket)); printResult("Short packet test", res); delayAndCheck(3000); // 测试3: 发送较长数据包 Serial.println("Test 3: Sending longer data packet"); const uint8_t longPacket[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F }; res = sendData(longPacket, sizeof(longPacket)); printResult("Long packet test", res); delayAndCheck(3000); // 测试4: 发送AT命令 Serial.println("Test 4: Sending AT command"); const char* atCommand = "AT\r\n"; res = sendData((const uint8_t*)atCommand, strlen(atCommand)); printResult("AT command test", res); delayAndCheck(3000); Serial.println("Device test completed."); printTransferStats(); } /** * @brief 尝试发送打印机特定命令 */ void tryPrinterSpecificCommands() { Serial.println("尝试打印机特定命令..."); // 尝试标准打印机命令 const uint8_t printerCommands[][10] = { {0x1B, 0x40}, // ESC @ - 初始化打印机 {0x1B, 0x64, 0x05}, // ESC d 5 - 打印并走纸5行 {0x0C}, // FF - 进纸到页首 {0x1B, 0x76, 0x00}, // ESC v 0 - 打印光栅位图(测试) }; for (int i = 0; i < sizeof(printerCommands)/sizeof(printerCommands[0]); i++) { Serial.printf("发送打印机命令 %d\n", i+1); esp_err_t res = sendData(printerCommands[i], sizeof(printerCommands[i])); printResult("打印机命令测试", res); delayAndCheck(3000); } } /** * @brief 尝试CDC类设备命令 */ void tryCDCCommands() { Serial.println("尝试CDC设备命令..."); // 发送一些常见的串口命令 const char* cdcCommands[] = { "\r\n", // 空行 "+++", // 进入命令模式(某些设备) "AT\r", // 基本AT命令 "ATI\r", // 设备信息 "ATE1\r", // 开启回显 }; for (int i = 0; i < sizeof(cdcCommands)/sizeof(cdcCommands[0]); i++) { Serial.printf("发送CDC命令: %s", cdcCommands[i]); esp_err_t res = sendData((const uint8_t*)cdcCommands[i], strlen(cdcCommands[i])); printResult("CDC命令测试", res); delayAndCheck(3000); } } /** * @brief 尝试HID设备命令 */ void tryHIDCommands() { Serial.println("尝试HID设备命令..."); // HID设备标准命令 - 获取报告 const uint8_t getReport[] = {0xA1, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; esp_err_t res = sendData(getReport, sizeof(getReport)); printResult("HID Get Report", res); delayAndCheck(3000); } /** * @brief 尝试存储设备命令 */ void tryStorageCommands() { Serial.println("尝试存储设备命令..."); // SCSI测试单元就绪命令 const uint8_t testUnitReady[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; esp_err_t res = sendData(testUnitReady, sizeof(testUnitReady)); printResult("SCSI Test Unit Ready", res); delayAndCheck(3000); } /** * @brief 发送厂商特定命令 */ void tryVendorSpecificCommands() { Serial.println("尝试厂商特定命令..."); // 发送一些常见的厂商命令模式 const uint8_t vendorCommands[][8] = { {0x00}, // 空命令 {0xFF, 0xFF, 0xFF}, // 广播命令 {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // 标准请求 }; for (int i = 0; i < sizeof(vendorCommands)/sizeof(vendorCommands[0]); i++) { Serial.printf("发送厂商命令 %d\n", i+1); esp_err_t res = sendData(vendorCommands[i], sizeof(vendorCommands[i])); printResult("厂商命令测试", res); delayAndCheck(3000); } } /** * @brief 打印测试结果 */ void printResult(const char* testName, esp_err_t result) { switch(result) { case ESP_OK: Serial.printf("%s: SUCCESS\n", testName); break; case ESP_ERR_TIMEOUT: Serial.printf("%s: TIMEOUT - Device not responding\n", testName); break; case ESP_ERR_INVALID_ARG: Serial.printf("%s: INVALID ARGUMENTS\n", testName); break; case ESP_ERR_NOT_FINISHED: Serial.printf("%s: PREVIOUS TRANSFER PENDING\n", testName); break; case ESP_ERR_NOT_FOUND: Serial.printf("%s: ENDPOINT NOT AVAILABLE\n", testName); break; default: Serial.printf("%s: ERROR 0x%x\n", testName, result); break; } } /** * @brief 延迟并检查传输状态 */ void delayAndCheck(unsigned long ms) { unsigned long start = millis(); while (millis() - start < ms) { task(); // 继续处理USB任务 delay(10); } } /** * @brief 强制清除传输状态 */ void forceClearTransferStatus() { if (sendTransferPending) { sendTransferPending = false; sendTransferStartTime = 0; transfersFailed++; ESP_LOGW("EspUsbHostPrinter", "Force cleared transfer status"); } } /** * @brief 打印传输统计信息 */ void printTransferStats() { Serial.printf("Transfer Stats - Submitted: %lu, Completed: %lu, Failed: %lu, Pending: %s\n", transfersSubmitted, transfersCompleted, transfersFailed, sendTransferPending ? "YES" : "NO"); ESP_LOGI("EspUsbHostPrinter", "Transfer Stats - Submitted: %lu, Completed: %lu, Failed: %lu, Pending: %s", transfersSubmitted, transfersCompleted, transfersFailed, sendTransferPending ? "YES" : "NO"); } /** * @brief 重置传输统计 */ void resetTransferStats() { transfersSubmitted = 0; transfersCompleted = 0; transfersFailed = 0; } /** * @brief 检查设备是否已完全枚举 */ bool isDeviceFullyEnumerated() { return deviceInfoReceived && (inputEndpointAvailable || outputEndpointAvailable); } /** * @brief 尝试重新枚举设备 */ void tryReenumerate() { if (!enumerationAttempted && deviceConnectedTime > 0) { unsigned long timeSinceConnect = millis() - deviceConnectedTime; if (timeSinceConnect > 5000) { // 连接5秒后仍未枚举 Serial.println("尝试重新枚举设备..."); enumerationAttempted = true; // 这里可以添加重新枚举的逻辑,但在当前API下比较困难 } } } }; EspUsbHostPrinter usbHost; void setup() { Serial.begin(115200); Serial.println("USB Host Printer Test"); // 初始化 USB 主机 usbHost.begin(); } void loop() { usbHost.task(); if (!usbHost.isDeviceConnected()) { static bool wasConnected = true; if (wasConnected) { Serial.println("USB设备断开连接"); usbHost.resetEndpoint(); usbHost.resetTransferStats(); wasConnected = false; } return; } else { static bool wasConnected = false; if (!wasConnected) { Serial.println("USB设备已连接"); wasConnected = true; delay(1000); usbHost.printFullDeviceInfo(); usbHost.analyzeDeviceType(); usbHost.checkReceiveStatus(); } } // 检查是否有接收到的数据并通过串口发送至电脑 if (usbHost.hasDataReceived()) { uint8_t buffer[512]; size_t len = sizeof(buffer); usbHost.getReceivedData(buffer, len); if (len > 0) { Serial.printf("[USB接收到数据] %d 字节:\n", len); // 显示十六进制和ASCII格式 for (size_t i = 0; i < len; i++) { if (i % 16 == 0) { Serial.printf("\n%04X: ", (uint16_t)i); } Serial.printf("%02X ", buffer[i]); } Serial.println(); // 显示ASCII可打印字符 Serial.print("ASCII: "); for (size_t i = 0; i < len; i++) { if (buffer[i] >= 32 && buffer[i] <= 126) { Serial.printf("%c", buffer[i]); } else { Serial.print("."); } } Serial.println(); Serial.println("--- End of USB data ---"); } } else { // 定期检查接收状态 static unsigned long lastCheck = 0; if (millis() - lastCheck > 10000) { // 每10秒检查一次 lastCheck = millis(); Serial.println("检查接收状态..."); usbHost.checkReceiveStatus(); } // 检查是否需要重新枚举 usbHost.tryReenumerate(); } static unsigned long lastSend = 0; unsigned long currentMillis = millis(); if (currentMillis - lastSend > 30000) { // 增加到30秒间隔 lastSend = currentMillis; if (!usbHost.outputEndpointAvailable) { Serial.println("端点不可用 - 等待设备枚举"); usbHost.printFullDeviceInfo(); return; } Serial.println("=== 发送测试数据 ==="); // 检查设备是否完全枚举 if (!usbHost.isDeviceFullyEnumerated()) { Serial.println("警告: 设备可能未完全枚举,跳过测试"); usbHost.printFullDeviceInfo(); usbHost.analyzeDeviceType(); } else { // 根据设备类型运行不同的测试 if (usbHost.deviceClass == 0x07) { // 打印机类设备 usbHost.tryPrinterSpecificCommands(); } else if (usbHost.deviceClass == 0x02 || usbHost.deviceClass == 0x0A) { // CDC类设备 usbHost.tryCDCCommands(); } else if (usbHost.deviceClass == 0x03) { // HID设备 usbHost.tryHIDCommands(); } else if (usbHost.deviceClass == 0x08) { // 存储设备 usbHost.tryStorageCommands(); } else if (usbHost.deviceClass == 0xFF) { // 厂商特定设备 usbHost.tryVendorSpecificCommands(); } else { // 默认测试 usbHost.testDevice(); } } // 分析设备类型 usbHost.analyzeDeviceType(); // 每次测试后强制清除传输状态,防止挂起 usbHost.forceClearTransferStatus(); Serial.println("=================="); } }这个程序能发送但不能接收USB数据
最新发布
08-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值