你以为接 USB 设备只能靠桌面客户端?今天 Chrome 把这件事用四行代码彻底原生化了
写过硬件通信的人都踩过这个坑——每次要刷 Arduino 固件、调 3D 打印机,都要先装一个桌面客户端。Chrome 61 今天把这件事用一个 API 彻底原生化了。
问题的本质:浏览器一直是孤岛
做前端这么多年,你大概习惯了浏览器只跟网络打交道。但现实里有大量场景需要浏览器跟硬件直接对话:刷固件、调芯片、接仪器、读传感器。以前这些活儿只能靠两件事:要么装一个原生客户端,要么装一个浏览器插件。
以刷 Arduino 固件为例。官方工具 Arduino IDE 要装 200MB,每次写代码还要配驱动,打开一次半天过去了。或者用 Chrome 扩展,但扩展上架流程繁琐,用户还要手动开启开发者模式。两种方案都有一个共同问题:把用户拦在浏览器外面。
这个局面的根源是:浏览器没有直接跟 USB 总线对话的能力。
WebUSB:把 USB 协议直接暴露给 JS
Chrome 61(2017 年)引入的 WebUSB API,就是来解决这个问题的。它把 USB 协议栈直接暴露给 JavaScript,让网页能够:
- 发现并连接 USB 设备
- 读取设备信息(厂商 ID、产品 ID、序列号)
- 进行端到端数据读写
- 监听设备插拔事件
整个过程零安装、零驱动,用户只需要在浏览器里点一下授权就够了。
工作流程:四步完成设备通信
连接一个 USB 设备的核心流程只有四步:
// 第一步:请求设备授权,弹出系统设备选择器
const device = await navigator.usb.requestDevice({
filters: [{ vendorId: 0x2341 }] // 0x2341 = Arduino 的厂商 ID
});
// 第二步:打开设备
await device.open();
// 第三步:选择配置并声明接口
await device.selectConfiguration(1);
await device.claimInterface(0);
// 设备已连接,可以开始通信了
console.log('设备已连接:', device.productName);
数据读写用两种方式:
// 读取设备数据(bulk transfer)
const result = await device.transferIn(1, 64); // 端点 1,每次最多 64 字节
const decoder = new TextDecoder();
const data = decoder.decode(result.data);
// 发送命令到设备(control transfer)
await device.controlTransferOut({
requestType: 'class',
recipient: 'interface',
request: 0x01,
value: 0x22,
index: 2
}, new Uint8Array([0x03]));
四步完成从设备发现到双向通信,没有 npm 包,没有原生代码。
实际在用的场景
WebUSB 不是玩具。几个真实在跑的场景:
刷固件:Arduino 官方出了 WebUSB 库,配合 Leonardo/Zero/MKR 系列板子,用户打开网页就能直接烧录,不需要 Arduino IDE。DAPjs 项目支持 ARM Mbed 系列芯片,包括 BBC micro:bit。
3D 打印机调参:主流 3D 打印机的配套工具正在往 Web 迁移,WebUSB 让浏览器直接连接打印机,读取温度、写配置参数、监控打印进度。avrgirl-arduino 这个开源库就是在做这件事。
仪表仪器:电子工程师用的示波器、逻辑分析仪、编程器,很多支持通过 USB 输出数据。WebUSB 让这些设备的数据直接在浏览器里可视化,不用装厂商专用的桌面软件。
教育场景:学校教嵌入式编程,学生不用配环境,打开网页插上开发板就能开始实验。vectree.io 甚至做了一个 WebUSB 的可视化交互图,把整个协议栈画出来让初学者拖拽理解。
安全模型:浏览器替你把住了门
硬件通信最怕安全问题。WebUSB 在设计上是”保守的”:
- 必须 HTTPS:本地开发用 localhost 除外
- 必须用户手势:设备选择器只能在按钮点击等用户动作里触发,网页无法静默扫描
- 必须显式授权:用户从系统弹窗里手动选中设备,网页只能看到用户选的那个
- 内核驱动的设备不能抢:HID(键盘鼠标)和串口设备已经被系统接管,WebUSB 无法访问
Firefox 和 Safari 暂不支持,原因是 USB 设备指纹追踪风险——USB 设备的序列号在同型号上是唯一的,可以跨网站追踪用户。Chrome/Edge 用户基数大,这个风险相对可控,所以先推进了实现。
三行代码判断浏览器是否支持
在写生产代码之前,先做能力检测:
if ('usb' in navigator) {
// WebUSB 可用
const device = await navigator.usb.requestDevice({ filters: [] });
// 继续连接流程...
} else {
// 不支持,显示替代方案
alert('请使用 Chrome/Edge 访问本页面');
}
实际项目里,可以用这个加一个优雅降级:显示一个引导页,告诉用户用什么浏览器,或者提供一个”下载桌面版”链接。
跟 Web Serial 的区别
很多同学会问:Web Serial API(串口)是不是能做同样的事?两者有分工:
- WebUSB:处理原生 USB 设备,不需要串行化层,适合需要直接控制端点的场景(固件烧录、自定义协议)
- Web Serial:处理串口设备(UART/RS-232),适合 Arduino(通过 USB 转串口芯片)、ESP 系列、开发板调试输出
一个实际项目里经常两者同时用到。
下一步:从零跑通一块 Arduino
手上有一块 Arduino Uno/Nano/Mega?来验证一下 WebUSB 是否在你的浏览器里工作:
- 打开 Chrome,输入
chrome://flags,搜索 WebUSB,开启 Experimental Web Platform Features - 重启浏览器,打开这个示例页面(搜索 WebUSB demo arduino)
- 点击”连接设备”,在弹窗里选中你的 Arduino
- 发送一个字节,看板子上的 LED 有没有反应
整个过程不用装任何软件。如果这步跑通了,一个固件烧录工具的原型,半小时就能出来。
核心结论:USB 设备通信不需要装客户端,已经变成浏览器原生能力。Chrome/Edge 用户覆盖下,这套方案可以直接在生产环境用起来。Firefox/Safari 的缺口,靠”请使用 Chrome 访问”这样的引导在实际项目里完全可行。
评论区
登录后可评论。