#include <Arduino.h>
#include <usb/usb_host.h>
// USB标准描述符类型
#define USB_DEVICE_DESC 1
#define USB_CONFIGURATION_DESC 2
#define USB_INTERFACE_DESC 4
#define USB_ENDPOINT_DESC 5
// USB传输类型
//#define USB_BM_ATTRIBUTES_XFER_BULK 2
#define USB_B_ENDPOINT_ADDRESS_EP_DIR_MASK 0x80
class MyUsbHost {
private:
usb_host_client_handle_t clientHandle = nullptr;
bool currentInterfaceIsPrinter = false;
uint8_t currentInterfaceNumber = 0;
bool isTransferInProgress = false;
uint8_t printerDeviceAddress = 0;
public:
bool printerReady = false;
uint8_t printerEndpointOut = 0;
uint8_t printerEndpointIn = 0;
uint16_t printerVid = 0;
uint16_t printerPid = 0;
usb_device_handle_t printerDeviceHandle = nullptr;
bool printerInterfaceFound = false;
bool interfaceClaimed = false;
void begin() {
const usb_host_config_t config = {
.skip_phy_setup = false,
.intr_flags = ESP_INTR_FLAG_LEVEL1
};
esp_err_t err = usb_host_install(&config);
if (err != ESP_OK) {
Serial.printf("usb_host_install failed: %d\n", err);
return;
}
Serial.println("USB Host initialized");
}
void task() {
// 处理USB主机事件
uint32_t event_flags;
usb_host_lib_handle_events(0, &event_flags);
if (clientHandle == nullptr) {
usb_host_client_config_t client_config = {
.is_synchronous = false,
.max_num_event_msg = 10,
.async = {
.client_event_callback = client_event_callback,
.callback_arg = this,
}
};
Serial.println("USB Host client config set");
esp_err_t err = usb_host_client_register(&client_config, &clientHandle);
if (err != ESP_OK) {
Serial.printf("usb_host_client_register failed: %d\n", err);
return;
}
Serial.println("USB Host client registered");
}
if (clientHandle != nullptr) {
usb_host_client_handle_events(clientHandle, 0);
}
}
private:
static void client_event_callback(const usb_host_client_event_msg_t* event_msg, void* arg) {
MyUsbHost* host = (MyUsbHost*)arg;
Serial.printf("USB_HOST_CLIENT_EVENT: %d\n", event_msg->event);
switch (event_msg->event) {
case USB_HOST_CLIENT_EVENT_NEW_DEV:
Serial.printf("USB_HOST_CLIENT_EVENT_NEW_DEV: address=%d\n", event_msg->new_dev.address);
host->handleNewDevice(event_msg->new_dev.address);
break;
case USB_HOST_CLIENT_EVENT_DEV_GONE:
Serial.println("USB_HOST_CLIENT_EVENT_DEV_GONE");
host->handleDeviceGone();
break;
default:
Serial.printf("Unknown event: %d\n", event_msg->event);
break;
}
}
void handleNewDevice(uint8_t dev_addr) {
printerDeviceAddress = dev_addr;
Serial.printf("New device connected with address: %d\n", dev_addr);
// 根据设备地址打开设备以获取设备句柄
esp_err_t err = usb_host_device_open(clientHandle, dev_addr, &printerDeviceHandle);
if (err != ESP_OK) {
Serial.printf("Failed to open device: %d (%s)\n", err, esp_err_to_name(err));
return;
}
if (printerDeviceHandle == nullptr) {
Serial.println("Device handle is null");
return;
}
Serial.printf("Device opened successfully. Address: %d, Handle: %p\n", dev_addr, printerDeviceHandle);
// 获取设备描述符
const usb_device_desc_t* dev_desc;
err = usb_host_get_device_descriptor(printerDeviceHandle, &dev_desc);
if (err != ESP_OK) {
Serial.printf("Failed to get device descriptor: %d (%s)\n", err, esp_err_to_name(err));
usb_host_device_close(clientHandle, printerDeviceHandle);
printerDeviceHandle = nullptr;
return;
}
printerVid = dev_desc->idVendor;
printerPid = dev_desc->idProduct;
Serial.printf("Device found: VID=0x%04x, PID=0x%04x\n", printerVid, printerPid);
// 获取配置描述符
const usb_config_desc_t* config_desc;
err = usb_host_get_active_config_descriptor(printerDeviceHandle, &config_desc);
if (err != ESP_OK) {
Serial.printf("Failed to get config descriptor: %d (%s)\n", err, esp_err_to_name(err));
usb_host_device_close(clientHandle, printerDeviceHandle);
printerDeviceHandle = nullptr;
return;
}
Serial.printf("Config descriptor length: %d\n", config_desc->wTotalLength);
// 解析配置描述符
parseConfigDescriptor((uint8_t*)config_desc, config_desc->wTotalLength);
// 如果找到了打印机接口,尝试claim接口
if (printerInterfaceFound && printerDeviceHandle != nullptr) {
Serial.printf("Attempting to claim interface %d\n", currentInterfaceNumber);
err = usb_host_interface_claim(clientHandle, printerDeviceHandle, currentInterfaceNumber, 0);
if (err != ESP_OK) {
Serial.printf("Failed to claim interface: %d (%s)\n", err, esp_err_to_name(err));
printerReady = false;
} else {
Serial.println("Interface claimed successfully");
interfaceClaimed = true;
printerReady = true;
}
}
}
void handleDeviceGone() {
Serial.println("Device disconnected");
resetPrinterState();
}
void resetPrinterState() {
printerReady = false;
printerEndpointOut = 0;
printerEndpointIn = 0;
interfaceClaimed = false;
printerInterfaceFound = false;
currentInterfaceNumber = 0;
currentInterfaceIsPrinter = false;
isTransferInProgress = false;
printerVid = 0;
printerPid = 0;
// 只在设备句柄有效时关闭设备
if (printerDeviceHandle != nullptr) {
// 先释放声明的接口
if (interfaceClaimed) {
usb_host_interface_release(clientHandle, printerDeviceHandle, currentInterfaceNumber);
}
usb_host_device_close(clientHandle, printerDeviceHandle);
printerDeviceHandle = nullptr;
}
}
void parseConfigDescriptor(uint8_t* desc, uint16_t len) {
uint8_t* p = desc;
uint16_t offset = 0;
Serial.printf("Parsing config descriptor, total length: %d\n", len);
// 重置打印机端点信息
printerEndpointOut = 0;
printerEndpointIn = 0;
printerInterfaceFound = false;
currentInterfaceIsPrinter = false;
uint8_t printerInterfaceNumber = 0; // 保存打印机接口编号
while (offset < len) {
if (offset + 2 > len) {
Serial.printf("End of descriptor at offset %d\n", offset);
break;
}
uint8_t length = p[0];
uint8_t type = p[1];
if (length == 0) {
Serial.println("Invalid descriptor length: 0");
break;
}
if (offset + length > len) {
Serial.printf("Descriptor length %d exceeds total length at offset %d\n", length, offset);
break;
}
Serial.printf("Descriptor: type=0x%02x, length=%d\n", type, length);
if (type == USB_INTERFACE_DESC) {
if (length < sizeof(usb_intf_desc_t)) {
Serial.printf("Interface descriptor too short: %d\n", length);
} else {
usb_intf_desc_t* intf = (usb_intf_desc_t*)p;
currentInterfaceNumber = intf->bInterfaceNumber;
// 检查是否为标准打印机类
currentInterfaceIsPrinter = (intf->bInterfaceClass == 0x07);
Serial.printf("Interface descriptor found: Interface=%d, Class=0x%02x, SubClass=0x%02x, Protocol=0x%02x\n",
intf->bInterfaceNumber, intf->bInterfaceClass,
intf->bInterfaceSubClass, intf->bInterfaceProtocol);
if (currentInterfaceIsPrinter) {
printerInterfaceFound = true;
printerInterfaceNumber = currentInterfaceNumber; // 保存打印机接口编号
Serial.printf("Printer interface found: Interface=%d, Class=0x%02x, SubClass=0x%02x, Protocol=0x%02x\n",
intf->bInterfaceNumber, intf->bInterfaceClass,
intf->bInterfaceSubClass, intf->bInterfaceProtocol);
}
}
}
else if (type == USB_ENDPOINT_DESC) {
if (length < sizeof(usb_ep_desc_t)) {
Serial.printf("Endpoint descriptor too short: %d\n", length);
} else {
usb_ep_desc_t* ep_desc = (usb_ep_desc_t*)p;
Serial.printf("Endpoint descriptor found for interface %d: Address=0x%02x, Attributes=0x%02x\n",
currentInterfaceNumber, ep_desc->bEndpointAddress, ep_desc->bmAttributes);
if ((ep_desc->bmAttributes & 0x03) == USB_BM_ATTRIBUTES_XFER_BULK) {
if (currentInterfaceIsPrinter) {
if (ep_desc->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_DIR_MASK) {
printerEndpointIn = ep_desc->bEndpointAddress;
Serial.printf("Printer IN endpoint found: 0x%02x\n", printerEndpointIn);
} else {
printerEndpointOut = ep_desc->bEndpointAddress;
Serial.printf("Printer OUT endpoint found: 0x%02x\n", printerEndpointOut);
}
} else {
Serial.printf("BULK endpoint for non-printer interface: 0x%02x\n", ep_desc->bEndpointAddress);
}
}
}
}
p += length;
offset += length;
}
// 检查是否找到了打印机接口和至少一个端点
if (printerInterfaceFound && (printerEndpointOut != 0 || printerEndpointIn != 0)) {
currentInterfaceNumber = printerInterfaceNumber; // 使用打印机接口编号
printerReady = true;
Serial.println("Printer is ready!");
if (printerEndpointOut != 0) {
Serial.printf("Printer OUT endpoint: 0x%02x\n", printerEndpointOut);
}
if (printerEndpointIn != 0) {
Serial.printf("Printer IN endpoint: 0x%02x\n", printerEndpointIn);
}
} else if (printerInterfaceFound) {
Serial.println("Printer interface found but no BULK endpoints detected");
} else {
Serial.println("No printer interface found");
}
}
static void transferCallback(usb_transfer_t *transfer) {
MyUsbHost* host = (MyUsbHost*)transfer->context;
host->isTransferInProgress = false;
if (transfer->status == USB_TRANSFER_STATUS_COMPLETED) {
Serial.printf("Transfer completed, %d bytes transferred\n", transfer->actual_num_bytes);
} else {
Serial.printf("Transfer failed with status: %d\n", transfer->status);
}
host->isTransferInProgress = false;
usb_host_transfer_free(transfer);
}
public:
// 打印数据到打印机(带重试机制)
bool printData(const uint8_t* data, size_t length, int maxRetries = 3) {
for (int attempt = 0; attempt < maxRetries; attempt++) {
if (attempt > 0) {
Serial.printf("Retry attempt %d/%d\n", attempt, maxRetries);
delay(100 * attempt);
}
// 检查设备状态
if (!printerReady || printerEndpointOut == 0) {
Serial.println("Printer not ready for printing");
Serial.printf("Printer ready: %s, Device handle: %p, Endpoint OUT: 0x%02x\n",
printerReady ? "YES" : "NO", printerDeviceHandle, printerEndpointOut);
// 尝试重新打开设备
if (printerDeviceHandle == nullptr && printerDeviceAddress != 0) {
Serial.println("Attempting to re-open device...");
esp_err_t open_err = usb_host_device_open(clientHandle, printerDeviceAddress, &printerDeviceHandle);
if (open_err != ESP_OK) {
Serial.printf("Failed to re-open device: %d (%s)\n", open_err, esp_err_to_name(open_err));
// 如果无法重新打开设备,重置状态并返回
if (open_err == ESP_ERR_NOT_FOUND) {
resetPrinterState();
}
continue;
}
// 重新声明接口
if (!interfaceClaimed && printerDeviceHandle != nullptr) {
Serial.println("Re-claiming interface...");
esp_err_t claim_err = usb_host_interface_claim(clientHandle, printerDeviceHandle, currentInterfaceNumber, 0);
if (claim_err != ESP_OK) {
Serial.printf("Failed to re-claim interface: %d (%s)\n", claim_err, esp_err_to_name(claim_err));
// 如果无法声明接口,关闭设备并继续尝试
usb_host_device_close(clientHandle, printerDeviceHandle);
printerDeviceHandle = nullptr;
continue;
} else {
Serial.println("Interface re-claimed successfully");
interfaceClaimed = true;
printerReady = true;
}
}
}
// 如果仍然没有有效的设备句柄,返回失败
if (printerDeviceHandle == nullptr) {
Serial.println("No valid device handle available");
continue;
}
}
if (isTransferInProgress) {
Serial.println("Transfer already in progress, please wait");
continue;
}
// 确保USB主机栈已处理所有事件
task();
// 检查设备是否仍然连接
if (printerDeviceHandle == nullptr) {
Serial.println("Device handle became null before transfer");
continue;
}
usb_transfer_t* transfer;
esp_err_t err = usb_host_transfer_alloc(length, 0, &transfer);
if (err != ESP_OK) {
Serial.printf("Failed to allocate transfer: %d (%s)\n", err, esp_err_to_name(err));
continue;
}
// 配置传输前再次检查设备句柄
if (printerDeviceHandle == nullptr) {
Serial.println("Device handle became null before transfer configuration");
usb_host_transfer_free(transfer);
continue;
}
// 配置传输
transfer->device_handle = printerDeviceHandle;
transfer->bEndpointAddress = printerEndpointOut;
transfer->num_bytes = length;
transfer->callback = transferCallback;
transfer->context = this;
transfer->timeout_ms = 5000;
// 复制数据到传输缓冲区
memcpy(transfer->data_buffer, data, length);
isTransferInProgress = true;
// 提交传输前再次检查设备句柄
if (printerDeviceHandle == nullptr) {
Serial.println("Device handle became null before transfer submission");
isTransferInProgress = false;
usb_host_transfer_free(transfer);
continue;
}
// 提交传输
err = usb_host_transfer_submit(transfer);
if (err != ESP_OK) {
Serial.printf("Failed to submit transfer: %d (%s)\n", err, esp_err_to_name(err));
isTransferInProgress = false;
usb_host_transfer_free(transfer);
// 根据错误类型采取不同措施
switch (err) {
case ESP_ERR_NOT_FOUND:
Serial.println("Device not found during transfer submit");
// 设备可能已断开,重置状态
resetPrinterState();
return false;
case ESP_ERR_INVALID_STATE:
Serial.println("Invalid state during transfer submit");
// 尝试重置接口声明状态
if (interfaceClaimed && printerDeviceHandle != nullptr) {
usb_host_interface_release(clientHandle, printerDeviceHandle, currentInterfaceNumber);
interfaceClaimed = false;
}
// 重新声明接口
if (printerDeviceHandle != nullptr) {
esp_err_t claim_err = usb_host_interface_claim(clientHandle, printerDeviceHandle, currentInterfaceNumber, 0);
if (claim_err == ESP_OK) {
Serial.println("Interface re-claimed successfully");
interfaceClaimed = true;
} else {
Serial.printf("Failed to re-claim interface: %d (%s)\n", claim_err, esp_err_to_name(claim_err));
}
}
continue;
default:
Serial.println("Other error during transfer submit");
continue;
}
}
// 等待传输完成
uint32_t startTime = millis();
while (isTransferInProgress && (millis() - startTime < 6000)) {
task();
delay(10);
}
if (!isTransferInProgress) {
return true;
} else {
Serial.println("Transfer timeout");
isTransferInProgress = false;
}
}
Serial.println("All print attempts failed");
return false;
}
// HP打印机初始化
bool hpInitialize() {
// 发送HP标准重置命令
const uint8_t initCmd[] = "x1B@"; // HP Reset
Serial.println("Sending HP reset command...");
if (printData(initCmd, sizeof(initCmd)-1)) {
delay(100);
return true;
}
return false;
}
// 打印文本
bool printText(const char* text) {
return printData((const uint8_t*)text, strlen(text));
}
// 打印简单文本页
bool printSimpleTestPage() {
Serial.println("Sending simple test page...");
const char* testPage =
"HP Deskjet 1212 Test Page\r\n"
"=========================\r\n\r\n"
"This is a simple test print from ESP32.\r\n"
"Printer Model: HP Deskjet 1212\r\n"
"Connection: USB\r\n"
"Status: Connected and Ready\r\n\r\n"
"If you can read this text, the test was successful!\r\n\r\n"
"Timestamp: ";
if (!printData((const uint8_t*)testPage, strlen(testPage))) return false;
// 添加时间戳
char timestamp[20];
sprintf(timestamp, "%lu\r\n\r\n", millis());
if (!printData((const uint8_t*)timestamp, strlen(timestamp))) return false;
// 表单进纸命令
const char* formFeed = "\r\n\r\n\x0C";
return printData((const uint8_t*)formFeed, strlen(formFeed));
}
};
MyUsbHost usbHost;
void setup() {
Serial.begin(115200);
Serial.println("HP Deskjet 1212 USB Printer Test");
Serial.println("=================================");
Serial.println("Waiting for printer connection...");
usbHost.begin();
}
void loop() {
usbHost.task();
static bool testStarted = false;
static uint32_t testStartTime = 0;
if (usbHost.printerReady && !testStarted) {
testStarted = true;
testStartTime = millis();
Serial.println("=== PRINTER READY FOR TESTING ===");
// 初始化打印机
Serial.println("Initializing printer...");
if (usbHost.hpInitialize()) {
Serial.println("Printer initialized successfully");
} else {
Serial.println("Failed to initialize printer");
}
delay(1000);
// 打印简单测试页
Serial.println("Printing simple test page...");
if (usbHost.printSimpleTestPage()) {
Serial.println("Simple test page sent to printer");
} else {
Serial.println("Failed to send simple test page");
}
Serial.println("=== PRINT TESTING COMPLETED ===");
}
delay(100);
}
运行结果如下:
11:33:54.911 -> Descriptor: type=0x04, length=9
11:33:54.911 -> Interface descriptor found: Interface=2, Class=0xff, SubClass=0x04, Protocol=0x01
11:33:54.945 -> Descriptor: type=0x05, length=7
11:33:54.945 -> Endpoint descriptor found for interface 2: Address=0x0a, Attributes=0x02
11:33:54.945 -> BULK endpoint for non-printer interface: 0x0a
11:33:54.945 -> Descriptor: type=0x05, length=7
11:33:54.945 -> Endpoint descriptor found for interface 2: Address=0x8b, Attributes=0x02
11:33:54.945 -> BULK endpoint for non-printer interface: 0x8b
11:33:54.945 -> Printer is ready!
11:33:54.945 -> Printer OUT endpoint: 0x08
11:33:54.945 -> Printer IN endpoint: 0x89
11:33:54.988 -> Attempting to claim interface 0
11:33:54.988 -> Interface claimed successfully
11:33:54.988 -> === PRINTER READY FOR TESTING ===
11:33:54.988 -> Initializing printer...
11:33:54.988 -> Sending HP reset command...
11:33:54.988 -> Transfer completed, 4 bytes transferred
11:33:55.108 -> Printer initialized successfully
11:33:56.121 -> Printing simple test page...
11:33:56.121 -> Sending simple test page...
11:33:56.121 -> Transfer completed, 243 bytes transferred
11:33:56.121 -> Transfer completed, 8 bytes transferred
11:33:56.167 -> Transfer completed, 5 bytes transferred
11:33:56.167 -> Simple test page sent to printer
11:33:56.167 -> === PRINT TESTING COMPLETED ===
优化、简化一下
最新发布