当前版本:v2.6.0
把 USB 打印机变成网络打印机。Windows / macOS / Linux 装原厂驱动后,通过标准 Raw Socket 9100 直接打印, 不需要装任何打印服务器软件。
[电脑] ──WiFi── [ESP32-S3] ──USB OTG── [打印机]
核心特性一句话:固件对打印数据完全透明,不理解任何页面描述语言, 所以 PCL / PostScript / ESC-P / ESC/POS / PJL / PCLm / GDI(主机型)全部可用 —— 渲染由电脑端驱动完成,板子只负责把字节搬到 USB 上。
| 场景 | 说明 |
|---|---|
标准 USB Printer Class(接口类 0x07) |
最常见情况,插上就能用 |
| GDI / 主机型打印机 | 渲染在电脑端驱动完成,板子只透明转发。实测 Epson Stylus Photo 1390、HP LaserJet MFP |
厂商私有接口(0xFF) |
机型库放行后接管。已放行 HP(含非标准 255/9/1)、Canon |
IPP-over-USB(7/1/4) |
能识别并接管,目前仍透传不代理 |
| 热敏票据机 / 标签机 | Zebra、TSC、Star、Gprinter、QinHeng 已在库中 |
| 场景 | 原因 |
|---|---|
| 高速打印机(High-Speed 480Mbps 独占) | ESP32-S3 只有 Full-Speed 12Mbps,实测 0.6–1 MB/s。A3/2880dpi 大图会明显慢 |
| 一体机的扫描 / 传真 | 只转发打印通道 |
| 手机端免驱打印 | iOS/Android 免驱发的是 PDF / URF,主机型(GDI)根本不认识。要在设备端做 PDF→原机语言的栅格化,MCU 做不到。主机型请在 Windows / macOS / Linux 装原厂驱动使用 |
| 打印中需要双向状态查询 | 支持回读(见下),但同一时刻只受理一个连接,打印期间的状态查询连接会被拒绝 |
固件在 Bulk IN 端点常挂一条读传输,把打印机吐出来的字节原样写回 TCP:
- Windows 端口设置里可以真正勾上「启用双向支持」
- PJL 状态查询、缺纸/卡纸应答、票据机 ACK 都能回传
- 这是后续做 IPP 代理的前置能力
注意:实测 HP 机型在常驻回读下会拒收打印数据,机型库已对 HP 整族关闭回读(Q_NO_BIDIR)。
若你的机器被回读干扰,在 printer_db.cpp 里给它加 Q_NO_BIDIR。
| 项目 | 规格 |
|---|---|
| 开发板 | ESP32-S3 开发板(必须带 USB OTG) |
| Flash | 4MB |
| PSRAM | 2MB 或以上(实测 XMC 4MB Flash + 2MB PSRAM 可用) |
| USB OTG 线 | Type-C / Micro-USB 转 USB-A 母口,必须是 OTG 线,普通充电线不行 |
| 打印机线 | USB-A 公转 USB-B 公(打印机原装线) |
| 供电 | 5V / 1A 以上 |
- 标「USB」的接口 → 接 OTG 线 → 打印机(Host 模式)
- 标「UART / COM」的接口 → 接电脑烧录,烧录后作为供电口
⚠️ 板载 USB-Serial/JTAG 口(VID:PID303A:1001)不是 OTG 口。接错口枚举不到打印机。
板管理器搜索 esp32,安装版本 3.3.10(IDF v5.5.4)。
🔴 必须钉死 3.3.10,绝对不要升级。 Arduino IDE / arduino-cli 会自动升级开发板包。实测升到 3.3.11 后 USB Host 不再枚举打印机: 网页显示「未连接」、
enumCount=0、vid=0000,断电重启打印机、重插 USB 全都无效 —— 看起来像硬件坏了,其实只是核心版本变了。判别信号:编译突然整机重编(数分钟)+ 产物体积明显变化 (3.3.10 ≈ 1,089,824 字节;3.3.11 ≈ 1,076,736 字节)。
退回:
arduino-cli core uninstall esp32:esp32 # 注意:不能带 @版本号 arduino-cli core install esp32:esp32@3.3.10装完需整机重编译再 OTA。装/卸都很慢(10 分钟量级),期间低 CPU 属正常。
| 设置项 | 选择 | 说明 |
|---|---|---|
| Board | ESP32S3 Dev Module |
|
| USB CDC On Boot | Disabled | 必须 Disabled,否则 USB Host 不可用 |
| USB Mode | USB-OTG (TinyUSB) |
|
| USB DFU On Boot | Disabled | |
| USB MSC On Boot | Disabled | |
| PSRAM | OPI PSRAM |
|
| Flash Size | 4MB |
按实际 Flash 选 |
| Partition Scheme | Minimal SPIFFS (1.9MB APP / 190KB SPIFFS) |
必须选含 OTA 的方案 |
对应 FQBN:
esp32:esp32:esp32s3:USBMode=default,CDCOnBoot=default,PSRAM=enabled,FlashSize=4M,PartitionScheme=min_spiffsArduino IDE:打开 ESP32S3_PrintServer_v2.ino → 选择串口 → 点上传。
arduino-cli:
arduino-cli compile --fqbn "esp32:esp32:esp32s3:USBMode=default,CDCOnBoot=default,PSRAM=enabled,FlashSize=4M,PartitionScheme=min_spiffs" .编译时只给
--build-path,不要同时给--output-dir(指向同一目录会报are the same file, 是假错,.bin其实已经正确生成)。
按住 BOOT → 按一下 RST → 松开 RST → 松开 BOOT,进入下载模式后重试。
- 烧录完成,接好 USB OTG 线
- 串口监视器(115200)应看到
[USB] 打印机就绪 - 手机或电脑连 WiFi
PrintServer-Setup,密码12345678 - 浏览器打开
http://192.168.4.1 - 用出厂账号登录:
admin/admin - 扫描 → 选 WiFi → 输密码 → 连接
- 成功后设备切换到新 IP,页面自动跳转,用同一套账号密码再登录一次
- 进 系统 → 修改管理员密码(不改的话,除配网外的所有写操作都会被
403挡住)
ESP32 只支持 2.4GHz WiFi。
忘记管理员密码:长按板子 BOOT 键约 10 秒(串口会打印提示),密码清空回 admin。
没有实体按键的板子把 config.h 的 AUTH_FACTORY_RESET_PIN 改成 -1 关闭此功能
(同时也就失去了这个救场手段)。
设备连不上网、网页打不开:等 3 分钟。固件判定长时间离线后会自动开出
PrintServer-Setup 热点,连上去重新配置即可,不用拆机重刷。期间它一直在后台尝试连回原 WiFi。
- 设置 → 蓝牙和其他设备 → 打印机和扫描仪 → 添加设备
- 搜索完成后点 手动添加
- 选 使用 TCP/IP 地址或主机名添加打印机
- 设备类型
TCP/IP 设备,地址填板子 IP(或printserver.local) - 系统查询大概率超时 → 直接点 自定义 手动设置
- 协议 Raw,端口
9100 - 取消勾选「SNMP 状态已启用」(重要,否则 Windows 判定打印机离线)
- 选打印机驱动
- 用真实打印作业验证(不是固件测试页,见故障排查)
系统设置 → 打印机与扫描器 → 添加打印机 → 切到 IP 标签页 → 协议 HP Jetdirect - Socket → 地址填 IP → 使用 → 选择软件 搜型号。
# Web: http://localhost:631 → Administration → Add Printer
sudo lpadmin -p WiFiPrinter -E -v socket://192.168.1.50:9100 -m everywhere访问 http://printserver.local。未登录时整页盖登录框;登录后 5 个页签,每 2 秒自动刷新:
| 页签 | 内容 |
|---|---|
| 概览 | 打印机状态与型号、当前作业(已接收 / 已发往 USB / 回读已回收 / 速率 / 用时)、累计统计、运行时长、内存 |
| 打印机 | USB 链路全量信息(VID:PID、接口、协议、端点、包长、是否 IPP-over-USB、重枚举次数)、Device ID、序列号;「打印测试页」「软复位」按钮(异步,结果以 toast 弹出) |
| 网络 | WiFi 状态、SSID、IP、当前 / 近 1 分钟最差 / 均值信号、离线时长、断线与重连次数、当前重试间隔、后备 AP 是否已开;扫描并切换网络 |
| 系统 | 修改管理员密码、OTA 升级、重启、恢复出厂 |
| 添加打印机 | Windows / macOS 图文步骤,IP 自动填入 |
顶部在默认密码未改时挂醒目横幅;打印机进入「无响应保护期」也会提示。
除状态和登录外,所有接口都需要登录(Cookie PSID 或 Authorization: Bearer <token>)。
跨站请求会被拒绝(校验 Origin 与 Host 一致)。
| 端点 | 方法 | 登录 | 说明 |
|---|---|---|---|
/api/auth/state |
GET | 否 | 鉴权状态:authenticated / needChange / 是否被锁定 |
/api/auth/login |
POST | — | 表单 user pass,成功返回会话 Cookie |
/api/auth/logout |
POST | 是 | 销毁当前会话 |
/api/auth/passwd |
POST | 是 | oldpass newpass newpass2;成功后自动重签会话 |
/api/status |
GET | 是 | 完整状态 JSON(打印机 / USB / 作业 / 统计 / 网络质量 / 异步操作结果) |
/api/debug |
GET | 是 | 崩溃日志、boot 计数、泄漏传输计数 |
/api/wifi/scan |
GET | 是 | 后台扫描,返回上次结果(非阻塞) |
/api/wifi/connect |
POST | 是¹ | ssid password,返回新 IP |
/api/testpage |
POST | 是² | 提交测试页,立刻返回,轮询 /api/status 的 ops.testpage |
/api/printer/reset |
POST | 是² | 同上,结果看 ops.usbReset |
/api/restart |
POST | 是² | 重启 ESP32 |
/api/factory-reset |
POST | 是¹ | 清除 WiFi + 管理员密码并重启 |
/api/ota |
POST multipart | 是² | OTA 升级,字段名 firmware;可加 ?pw= 传 OTA_PASSWORD |
¹ 允许在「还没改掉出厂默认密码」时调用 —— 配网和救场是基础能力,不能被锁死。
² needChange 状态下返回 403 {"error":"default-password"}。
ops 状态码:0 无请求 / 1 执行中 / 2 成功 / 3 失败。
异步化是因为这些操作要发 USB 控制或批量传输,遇到不应答的打印机会阻塞几十秒。
兼容性的关键不在「认不认得这台打印机」,而在知不知道它的坏脾气。
固件对数据是透明的,PCL / PS / ESC-POS / GDI 天然都能打。真正决定「某一台打印机能不能用、好不好用」 的从来不是打印语言,而是它在 USB 层的一些怪癖:
- 对所有控制传输长时间 NAK(爱普生 1390)→ 不能发 Device ID
- 作业开头被塞
ESC @就卡住不出纸(HP 走@PJL流) - 作业结束补 FF 反而让它永久等待下一页(PCLm 流尾部自带
%%EOF) - 暴露的根本不是标准打印机类接口,而是厂商私有接口(部分惠普 MFP)
这些无法从描述符推断,只能一台台实测积累。printer_db.cpp 维护 VID:PID → 行为特征位 的表,
命中的机型会覆盖 config.h 的全局默认值。
| 标志 | 含义 |
|---|---|
Q_WANT_ESC_RESET |
作业开头要发 ESC @(多数 ESC/P 爱普生、针式机) |
Q_NO_ESC_RESET |
千万别发 ESC @(HP 等 @PJL 流机型:发了会卡「正在打印文档」不出纸) |
Q_NO_CTRL |
完全不应答控制传输,禁发 Device ID / 字符串描述符 / 端口状态(Epson 1390) |
Q_TAIL_NONE |
作业结束什么都不补(PCLm / PDF / PS / PJL 系 —— 自带收尾标记,补任何东西都是又开一页) |
Q_TAIL_FF |
作业结束无条件补 FF(ESC/P、ESC/POS —— 流式格式没有收尾标记) |
Q_NO_FAIL_RECOVER |
禁止故障自愈序列(少数机型被收尾序列打死) |
Q_NO_TEXT_TESTPAGE |
禁用固件自带的纯文本测试页(主机型 PCLm / PCL3GUI / GDI 收下但既不渲染也不结束,会卡死到必须断电) |
Q_VENDOR_IFACE |
允许接管厂商私有(0xFF)批量接口(HP / Canon 已放行) |
Q_NO_BIDIR |
不回读 Bulk IN |
Q_IPP_OVER_USB |
已知 IPP-over-USB 设备(描述符哪怕不是标准 7/1/4) |
Q_WANT_FORMFEED / Q_NO_FORMFEED |
已废弃,见 Q_TAIL_NONE / Q_TAIL_FF |
Q_NO_ZLP |
不补零长度包(极少数老机) |
两者都没写 = Q_TAIL_AUTO:按作业末尾字节自动判断(安全兜底)。
-
插上打印机,访问
/api/status,记下usb.vid/usb.pid/usb.protocol -
在
printer_db.cpp的PROFILES里加一行:{0x04B8, 0x9999, "Epson 某某机型", Q_NO_CTRL | Q_WANT_ESC_RESET | Q_TAIL_FF},PID填PID_ANY表示对该厂商所有产品生效(厂商级行为,如 HP 全线禁ESC @) -
只填有实测依据的标志。没证据的机型不要乱标,让它走全局默认值更安全
-
改完重新编译 + OTA
⚠️ 补录时不要改config.h的全局默认值去迁就一台机器 —— 那会影响所有未登记的机型。 全局值只是「查不到该机型时的兜底」。
这张表是这个方案里最不容易被复制的部分:代码能开源,几百台真机的调校数据不行。
:9100 同时只受理一个连接(有意为之:半路的 foreign language 数据流混进来会打印错位)。
客户端连接
├─ 15s 内没收到任何数据 → 判定空连接,丢弃(免重启自愈)
├─ 收到数据
│ ├─ 全部字节成功下发 + TCP 静默 1.5s → 主动关闭,作业完成
│ ├─ 传输失败 / 打印机断连 → _abortJob():标记失败 + 干净关连接 + 按机型复位
│ ├─ 120s 没成功下发任何字节 → 判定打印机失联,释放通道
│ ├─ 10 分钟未结束 → 强制回收通道
│ └─ 60s 双向都无收发 → 最终兜底断开
└─ 通道被占用时的连接 → 立即拒绝并计数(stats.rejected)
为什么要「主动关闭」:RAW 9100 是字节流协议,没有长度字段也没有 EOJ 握手。
Windows 发完作业后会等服务端关闭 TCP,而旧固件在等 Windows 先关闭 —— 双方互等到
系统级超时(实测约 60 秒)。表现是队列长时间停在「正在打印」,后续作业全堵在后面。
v2.6.0 起,数据全量下发后 TCP 静默 JOB_DATA_IDLE_END_MS(默认 1500ms)就由服务端主动 FIN,
Windows 队列约 2 秒内结束。
作业收尾策略按机型区分:驱动流的收尾标记各不相同,固件「好心」补的换页符反而会让打印机 永久等待下一页。
| 打印语言 | 驱动自带收尾 | 固件是否补 FF |
|---|---|---|
| PCLm / PDF | %%EOF |
不补 |
| PCL5/6 + PJL | UEL + @PJL EOJ |
不补 |
| PostScript | 0x04 (Ctrl-D) |
不补 |
| ESC/P(爱普生 1390) | 无 | 补 |
| ESC/POS(票据机) | 无 | 补 |
| 现象 | 原因与处理 |
|---|---|
串口无 [USB] 输出 |
检查是不是用了普通充电线而不是 OTG 线;确认接在「USB」口不是「UART」口 |
打印机一直「未连接」、enumCount=0 |
先确认 ESP32 核心是 3.3.10(见安装核心)。升到 3.3.11 会这样,断电重启打印机、重插 USB 都无效。然后断电重启打印机、重插 USB |
未找到可用打印接口 |
描述符里既没有标准打印机类也没有被放行的私有接口。到 /api/status 记 vid/pid,在 printer_db.cpp 补一行并加 Q_VENDOR_IFACE |
型号显示为 VID:xxxx |
Device ID 读失败(该机型在库里配了 Q_NO_CTRL)。不影响打印 |
| Windows 打印机显示离线 | 端口设置里取消勾选 SNMP |
| Windows 添加卡在查询中 | 直接点自定义,指定 Raw / 9100 |
| macOS 队列暂停 | 手动点恢复,确认协议为 HP Jetdirect |
| 打印乱码 | 驱动选错;或 TESTPAGE_PREPEND_ESC_RESET 与机型不匹配 |
| 打印用着用着失灵,重启才好 | 空连接占住了作业通道(Windows 双向探测、被取消作业留下的半开连接)。固件会自动回收,无需重启 |
| 固件测试页打不出来 | 主机型(GDI / PCL3GUI / PCLm)打印机只认官方驱动生成的数据流,纯文本测试页不会出纸 —— 这不算故障。这类机型网页上测试页按钮已自动隐藏。请用真实打印作业验证链路 |
| HP 卡在「正在打印文档」不出纸 | 作业开头硬塞 ESC @ 会打乱 PJL 解析。JOB_RESET_ON_CONNECT 和 TESTPAGE_PREPEND_ESC_RESET 必须是 0(默认已是 0),且机型库已对 HP 全线置 Q_NO_ESC_RESET |
| 打印机打完一直不结束,下一份要等很久 | v2.6.0 已修(数据下发完 1.5 秒主动断连)。若仍出现,看串口有没有 [Print] 数据已全量下发,TCP 静默 XXXms,主动结束作业;另外检查 Windows 的「保留打印的文档」是否开着(会让已结束记录视觉上保留) |
sent=0、USB 传输失败 status=0 |
打印机自身在拒收数据:缺粉 / 卡纸 / 残留卡住的作业 / 面板报错。断电重启打印机并检查耗材。固件侧只能快速失败保护,无法替打印机复位 |
| 打印到一半重复了好多份 | Windows 后台在重试失败的作业。看 /api/status 的 stats.failedJobs 和 job.bytesSent 是否对不上 —— 一般是打印机侧先出问题,不是固件 |
| 打印机出问题时网页/API 卡住 | |
bootCount 持续增长 |
板子在崩溃循环。先把打印机物理拔掉,等 uptime 稳定上涨后再 OTA。/api/debug 能看到 lastCrashStage / stageLine |
OTA 失败 / 403 default-password |
还在用出厂密码 admin/admin,先登录改密码 |
| 忘了我设的管理员密码 | 长按 BOOT 键 10 秒清空密码(串口会打印提示) |
| 换路由器 / 改 WiFi 密码后失联 | 等 3 分钟,设备自动开 PrintServer-Setup 热点,连上去重新配置 |
| WiFi 连着但打印很卡、时断时续 | 看「网络 → 近 1 分钟最差信号」。长期低于 -75 dBm 请挪近或加中继;WIFI_DISABLE_SLEEP 保持 1 |
| 重新连上 WiFi 后打印机变离线 | 固件会自动重建 mDNS 与 :9100 监听。仍异常就看 wifi.disconnects 是否增长,必要时手动重启 |
printserver.local 无法访问 |
Windows 缺 mDNS,直接用 IP |
| Arduino IDE 看不到网络端口 | 装 Bonjour Print Services |
编辑 config.h。
⚠️ 下面带JOB_/ENABLE_/USB_前缀的打印相关项只是「查不到该机型时的全局默认值」。printer_db.cpp里登记过的机型以库中的Q_*标志为准。要改某台机器的行为,改库不要改这里。
| 参数 | 默认 | 说明 |
|---|---|---|
FW_VERSION |
2.6.0 |
固件版本 |
PRINTER_NAME |
ESP32_PrintServer |
设备标识名 |
MDNS_HOSTNAME |
printserver |
mDNS 主机名,浏览器用 http://printserver.local |
RAW_SOCKET_PORT |
9100 |
JetDirect / AppSocket 打印端口 |
AP_SSID / AP_PASSWORD |
PrintServer-Setup / 12345678 |
配网热点 |
| 参数 | 默认 | 说明 |
|---|---|---|
USB_XFER_CHUNK |
8192 |
单次 USB 批量传输大小 |
USB_XFER_TIMEOUT_MS |
30000 |
单块传输等待上限(打印机走纸时会 NAK,要留余量) |
PRINT_CHUNK |
16384 |
每次从 TCP 取多少字节交给 USB |
PRINT_BUFFER_SIZE |
256 * 1024 |
PSRAM 打印缓冲 |
STATUS_POLL_MS |
2000 |
IEEE 1284 端口状态轮询间隔 |
ENABLE_BIDIR_PASSTHROUGH |
1 |
读 Bulk IN 并把应答回写 TCP |
USB_RX_BUFFER_SIZE |
4 * 1024 |
IN 端点回读环形缓冲 |
ENABLE_PORT_STATUS_POLL |
0 |
IEEE1284 端口状态查询。保持 0:不少 GDI 机型对它长时间 NAK,而本 IDF 版本 timeout_ms 无效、EP0 无取消接口,会留下永久悬挂的传输 |
| 参数 | 默认 | 说明 |
|---|---|---|
USB_RECOVER_ENABLED |
1 |
传输失败时自动尝试拉回打印机(halt 端点 → SOFT_RESET → 收尾序列) |
USB_RECOVER_AFTER |
1 |
连续几次无回调就触发。必须为 1:某块写失败后立刻 break 结束作业,"连续 2 次"在同一份作业里永远凑不满 |
USB_RECOVER_MAX_PER_JOB |
2 |
单份作业最多自愈几次 |
USB_RECOVER_MAX_WAIT_MS |
1200 |
自愈各步骤等待上限 |
USB_RECOVER_RETRY |
1 |
自愈后原地重试失败的那块 |
USB_STALL_MAX_TIMEOUTS |
3 |
连续几次无回调判定打印机失联 |
USB_STALL_COOLDOWN_MS |
15000 |
冷却时长,期间新作业立刻失败返回,保住网页响应 |
| 参数 | 默认 | 说明 |
|---|---|---|
JOB_NODATA_TIMEOUT_MS |
15000 |
连上后一直没数据 → 判定空连接自动断开 |
JOB_DATA_IDLE_END_MS |
1500 |
数据全量下发后 TCP 静默多久即判定作业结束并主动断连(v2.6.0 新增,修 Windows 队列 60 秒不结束) |
JOB_IDLE_TIMEOUT_MS |
60000 |
异常连接最终兜底。必须大于 USB_XFER_TIMEOUT_MS |
JOB_TX_STALL_MS |
120000 |
作业期内这么久没成功下发过任何字节 → 释放通道 |
JOB_MAX_DURATION_MS |
600000 |
单作业最长占用通道 10 分钟 |
JOB_RESET_ON_CONNECT |
0 |
作业开头发 ESC @。Epson 可设 1,HP 必须 0 |
JOB_APPEND_FORMFEED |
1 |
作业结束补 FF。PCLm / PS / PJL 系必须 0 |
TESTPAGE_PREPEND_ESC_RESET |
0 |
ESC/P 机型可设 1;PCL / PS / HP 必须 0 |
| 参数 | 默认 | 说明 |
|---|---|---|
PRINT_TASK_CORE |
0 |
打印任务跑在哪个核心(loop() 在 core1,分离才真正并行) |
PRINT_TASK_PRIORITY / _STACK_SIZE |
2 / 8192 |
|
USB_HOST_TASK_PRIORITY / _STACK_SIZE |
5 / 10240 |
栈不能低于 10240 |
WDT_TIMEOUT_SEC |
60 |
|
LED_PIN |
2 |
无外接 LED 也不影响功能 |
| 参数 | 默认 | 说明 |
|---|---|---|
WIFI_DISABLE_SLEEP |
1 |
关掉 modem sleep。强烈建议保持 1,节能模式会造成数百毫秒延迟抖动 |
WIFI_RETRY_MIN_MS / _MAX_MS |
5000 / 60000 |
重连退避上下限 |
WIFI_REASSOC_AFTER |
3 |
失败几次后升级为完整重新关联 |
WIFI_DRIVER_RESET_AFTER |
8 |
再失败几次后复位 WiFi 驱动 |
WIFI_FALLBACK_AP_MS |
180000 |
离线多久后开后备配网 AP(0 关闭) |
WIFI_RSSI_SAMPLE_MS / _HISTORY |
2000 / 30 |
信号采样间隔与保留点数(约 1 分钟) |
| 参数 | 默认 | 说明 |
|---|---|---|
AUTH_ENABLED |
1 |
调试时可临时置 0 |
AUTH_DEFAULT_USER / _PASS |
admin / admin |
出厂账号密码,登录后强制修改 |
AUTH_PASS_MIN_LEN / _MAX_LEN |
4 / 32 |
|
AUTH_SESSION_TIMEOUT_MS |
2h |
会话空闲过期 |
AUTH_MAX_SESSIONS |
6 |
并发会话上限。槽位满时淘汰最久没活动的那个,不会被拒绝 |
AUTH_MAX_FAILS |
5 |
连续输错几次开始锁定(指数退避,上限 30 分钟) |
AUTH_FACTORY_RESET_PIN |
0 |
长按该 GPIO 10 秒清空管理员密码,-1 关闭 |
OTA_PASSWORD |
空 | 建议设置,否则局域网内谁都能刷机 |
| 参数 | 默认 | 说明 |
|---|---|---|
HEAP_GUARD_ENABLE |
1 |
|
HEAP_GUARD_MIN_KB / _HOLD_MS |
24 / 30000 |
堆低于该值并持续这么久 → 空闲时自动重启,防碎片化变永久故障 |
ESP32S3_PrintServer_v2/
├── ESP32S3_PrintServer_v2.ino 主程序入口(setup / loop / 全局对象)
├── config.h 配置参数(机型库未命中时的全局默认值)
├── printer_db.h / .cpp 机型行为特征库(VID:PID → Q_* 标志)
├── usb_printer.h / .cpp USB Host 打印机驱动(枚举、匹配、传输、自愈、回读)
├── print_server.h / .cpp Raw Socket 9100 服务器(独立 FreeRTOS 任务)
├── web_ui.h / .cpp 网页管理界面 + HTTP API + 登录鉴权接入
├── wifi_manager.h / .cpp WiFi 配网 / 三级重连 / 退避 / 后备 AP / 信号采样
├── auth.h / .cpp 管理员鉴权(SHA-256 加盐、会话、暴力破解锁定)
├── crashlog.h / .cpp 崩溃日志(RTC 内存,重启后经 /api/debug 读出)
├── LICENSE MIT
└── README.md
- 单作业通道。
:9100同时只受理一个连接(有意设计)。打印期间状态查询连接会被拒绝。 排 N 份作业时建议在 Windows 端口里取消勾选「启用双向支持」。 - 打印机内部死锁无法软件复位。当打印机自身的固件 / FIFO 卡死(
sent=0、USB 传输失败 status=0且面板报错),固件能做的只是快速失败保护 —— 端点 halt/flush/clear、SOFT_RESET、收尾序列 对这类情况实测无效。目前只能断电重启打印机。 要根治需要 USB 设备句柄重枚举 或可控 VBUS 的硬件兜底,尚未实现。 - ESP32-S3 只有 Full-Speed 12Mbps,实测吞吐 0.6–1 MB/s。高速打印机 / 大幅面照片会很慢。 升级路径是 ESP32-P4(内置 USB High-Speed PHY,480Mbps)。
- 主机型打印机不支持手机免驱。见先看这个。
- IPP-over-USB 只识别不代理。能认出
7/1/4接口,但不做 IPP 请求代理。
- 打印队列秒级结束。数据全量下发后 TCP 静默 1.5s 即主动断连,根治 Windows/macOS 队列「打完半天不结束、下一份要等很久」(原先双方互等约 60 秒)
- 作业收尾策略按机型区分(
Q_TAIL_NONE/Q_TAIL_FF) - 异常路径不再发禁忌的
ESC @(_resetPrinter()统一遵守机型库) - 主机型打印机禁用纯文本测试页
- HP 加
Q_NO_FAIL_RECOVER,停止执行实测无效、反而污染打印机状态的恢复序列
- 作业收尾策略从「一刀切补 FF」改为按机型 + 作业尾部字节自描述判断
- 登录会话不再会把管理员关在门外:槽位满时淘汰最久未活动的会话,登录永不被拒绝
- 打印通道看门狗:120s 无成功下发 → 释放通道;单作业 10 分钟强制回收
- 打印任务独立成 FreeRTOS 任务后再无 WDT 重启
- 管理员登录:所有写操作都要登录,SHA-256 加盐存 NVS,HttpOnly Cookie,会话令牌,指数退避锁定
- 打印服务搬出
loop(),独立 FreeRTOS 任务 + 独立核心。打印机「叫不应」不再锁死管理页 - 网页触发的阻塞操作(测试页 / 软复位)改为异步
- WiFi 三级重连 + 指数退避 + 关闭 modem sleep
- 长时间离线自动开后备 AP
- 断链自愈:WiFi 重连后自动重建 mDNS 与 :9100
- 双向回读写保护、内存自愈
- 机型行为特征库(
printer_db.cpp)—— 本方案商业化的核心资产 - USB 接口匹配改为多级打分,放行惠普 MFP 非标准
255/9/1(依据 Linuxipp-usb源码), 显式排除 HID / MSC / Imaging 等搭车接口 - 双向回读:常驻 Bulk IN + 环形缓冲 → 写回 TCP
- 打印机快速失败保护(
USB_STALL_*)
见 git log。
MIT License — 详见 LICENSE
基于 ESP-IDF USB Host 栈与 Arduino-ESP32 核心实现。
接口打分策略参考了 Linux ipp-usb 项目对惠普 MFP 非标准接口组合的处理。