大家好,欢迎来到今天的“奇妙的WebHID世界”讲座!
今天咱们要聊的是一个相当硬核,但又充满可能性的东西——WebHID API。 别被“HID”这个缩写吓到,它可不是什么神秘组织,而是“Human Interface Device”的缩写,翻译过来就是“人机接口设备”。 简单说,就是那些让你能跟电脑互动的玩意儿,比如键盘、鼠标、游戏手柄,甚至是更奇葩的东西,比如自定义的按钮盒、扫描仪等等。
WebHID API,简单来说,就是让你的JavaScript代码可以直接跟这些HID设备“对话”。 以前,这事儿只能是桌面应用才能干的,现在浏览器也能插一脚了! 这就打开了很多扇脑洞大开的大门。
为什么要用WebHID?
你可能会问,键盘鼠标不都能用JS监听事件了吗?干嘛还要WebHID? 好问题!
- 标准事件不够用啊! 键盘鼠标的事件,Browser已经定义好了,比如
keydown
,click
,但如果你的HID设备不是标准的键盘鼠标,事件就没法对应了。比如一个自定义的按钮盒,你按下一个特殊的按钮,JS就没法直接知道。WebHID 允许你直接读取设备发来的原始数据,自己解析。 - 独占设备。 有些设备,你希望浏览器独占使用,不要被操作系统拦截。比如,一个专业的游戏手柄,你希望所有的按键和摇杆数据都直接交给游戏处理,而不是被操作系统的一些快捷键给截胡了。
- 性能! 对于一些高频率的设备,比如VR/AR设备,WebHID可以直接读取设备的原始数据,绕过操作系统的驱动层,降低延迟,提高性能。
WebHID 的基本流程
要想用WebHID,大致需要经历以下几个步骤:
- 请求权限: 先跟用户打个招呼,问问能不能访问他们的HID设备。
- 选择设备: 列出所有可用的HID设备,让用户选择要连接哪个。
- 连接设备: 建立连接,准备收发数据。
- 收发数据: 从设备接收数据,或者向设备发送控制指令。
- 断开连接: 用完了,记得礼貌地断开连接。
代码时间!
光说不练假把式,咱们来点实际的。
1. 请求权限和选择设备
async function requestHID() {
try {
const devices = await navigator.hid.requestDevice({
filters: [
{ usagePage: 0x0001, usage: 0x0006 } // 过滤条件:只显示键盘
// 可以添加多个filter,比如
// { vendorId: 0x1234, productId: 0x5678 } // 根据厂商ID和产品ID过滤
]
});
if (devices.length > 0) {
const device = devices[0]; // 选择第一个设备
console.log("已选择设备:", device);
await connectDevice(device); // 连接设备
} else {
console.log("未选择任何设备");
}
} catch (error) {
console.error("请求HID设备失败:", error);
}
}
// 按钮点击事件,触发请求设备
const requestButton = document.getElementById('requestButton');
requestButton.addEventListener('click', requestHID);
这段代码做了几件事:
navigator.hid.requestDevice()
:这是WebHID的核心函数,会弹出一个对话框,让用户选择HID设备。filters
:这是一个过滤器,可以指定要显示的设备类型。usagePage
和usage
是HID的规范,用来描述设备类型。0x0001
代表通用桌面设备,0x0006
代表键盘。 你也可以根据vendorId
(厂商ID) 和productId
(产品ID) 来过滤特定设备。await
:注意这里用了await
,因为requestDevice()
是异步的,需要等待用户选择设备。connectDevice(device)
:用户选择设备后,调用connectDevice()
函数来连接设备。
2. 连接设备和接收数据
async function connectDevice(device) {
try {
await device.open(); // 打开设备连接
console.log("设备已连接");
device.addEventListener("inputreport", event => {
const { data, device, reportId } = event;
const uint8Array = new Uint8Array(data.buffer); // 将数据转换为 Uint8Array
console.log("收到数据:", uint8Array, "Report ID:", reportId);
processInputReport(uint8Array, reportId); // 处理收到的数据
});
device.addEventListener("disconnect", () => {
console.log("设备已断开连接");
});
} catch (error) {
console.error("连接设备失败:", error);
}
}
function processInputReport(data, reportId) {
// 根据 reportId 和 data 的内容,解析HID设备发来的数据
// 具体的解析逻辑需要根据你的设备来定
console.log("处理数据,reportId:", reportId, "data:", data);
// 举例:如果 reportId 是 1, 并且 data[0] 是 0x01,则表示按钮A被按下
if (reportId === 1 && data[0] === 0x01) {
console.log("按钮A被按下");
}
}
这段代码做了这些事:
device.open()
:打开设备连接,这是建立通信的关键一步。device.addEventListener("inputreport", ...)
:监听inputreport
事件,当设备发送数据过来时,这个事件会被触发。event.data
:包含了设备发来的原始数据,是一个DataView
对象。Uint8Array
:为了方便处理,通常会将DataView
转换为Uint8Array
。reportId
:有些HID设备会使用reportId
来区分不同的数据类型。processInputReport(data, reportId)
:这是一个自定义的函数,用来解析收到的数据。 具体的解析逻辑需要根据你的HID设备来定。 这也是最麻烦,也是最有意思的地方!device.addEventListener("disconnect", ...)
:监听disconnect
事件,当设备断开连接时,这个事件会被触发。
3. 发送数据到设备
async function sendOutputReport(device, reportId, data) {
try {
await device.sendReport(reportId, data);
console.log("发送数据成功,Report ID:", reportId, "Data:", data);
} catch (error) {
console.error("发送数据失败:", error);
}
}
// 举例:发送数据给设备,控制LED灯的亮度
const sendButton = document.getElementById('sendButton');
sendButton.addEventListener('click', async () => {
if (device) { // 确保设备已连接
const reportId = 0x02; // Report ID
const brightness = 0x80; // 亮度值 (0-255)
const data = new Uint8Array([brightness]); // 将亮度值转换为 Uint8Array
await sendOutputReport(device, reportId, data);
} else {
console.log("请先连接设备");
}
});
这段代码做了这些事:
device.sendReport(reportId, data)
:向设备发送数据,reportId
和data
的含义取决于你的HID设备。Uint8Array
:发送的数据必须是Uint8Array
类型的。
HID Report 描述符
要想真正玩转WebHID,你需要了解HID Report 描述符。 这玩意儿就像HID设备的“说明书”,描述了设备的功能、数据格式等等。
HID Report 描述符是用一种特殊的语言描述的,叫做“HID Report Descriptor Language”。 听起来很吓人,但其实就是一些预定义的关键字和数值。
举个例子,下面是一个简单的键盘的Report 描述符:
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x05, 0x07, // Usage Page (Key Codes)
0x19, 0xE0, // Usage Minimum (Keyboard LeftControl)
0x29, 0xE7, // Usage Maximum (Keyboard Right GUI)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x06, // Report Count (6)
0x75, 0x08, // Report Size (8)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x65, // Logical Maximum (101)
0x05, 0x07, // Usage Page (Key Codes)
0x19, 0x00, // Usage Minimum (Reserved (no event indicated))
0x29, 0x65, // Usage Maximum (Keyboard Application)
0x81, 0x00, // Input (Data,Ary,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0 // End Collection
是不是看得头皮发麻? 没关系,你不需要记住所有的细节。 你只需要知道,这个描述符告诉了你键盘的按键数量、数据格式等等。
怎么找到HID Report 描述符?
- 设备文档: 有些设备会提供详细的文档,里面包含了Report 描述符。
- 操作系统工具: Windows 下可以用
HIDView
工具,Mac 下可以用IOJones
工具来查看设备的Report 描述符。 - 在线数据库: 有一些在线的HID Report 描述符数据库,比如 usb.org。
实战案例
咱们来几个实战案例,让你感受一下WebHID的威力。
1. 自定义游戏手柄
假设你有一个自定义的游戏手柄,上面有一些特殊的按钮和旋钮。 你可以用WebHID来读取这些按钮和旋钮的状态,然后控制游戏中的角色。
// 假设手柄的 Report 描述符如下:
// Report ID: 0x01
// Button 1: data[0] & 0x01
// Button 2: data[0] & 0x02
// Knob: data[1] (0-255)
function processInputReport(data, reportId) {
if (reportId === 0x01) {
const button1 = data[0] & 0x01;
const button2 = data[0] & 0x02;
const knob = data[1];
console.log("Button 1:", button1 ? "Pressed" : "Released");
console.log("Button 2:", button2 ? "Pressed" : "Released");
console.log("Knob:", knob);
// 根据按钮和旋钮的状态,更新游戏中的角色
updateGameCharacter(button1, button2, knob);
}
}
2. LED 控制器
假设你有一个LED控制器,可以用WebHID来控制LED的颜色和亮度。
// 假设LED控制器的 Report 描述符如下:
// Report ID: 0x02
// Red: data[0] (0-255)
// Green: data[1] (0-255)
// Blue: data[2] (0-255)
async function setLedColor(device, red, green, blue) {
const reportId = 0x02;
const data = new Uint8Array([red, green, blue]);
await sendOutputReport(device, reportId, data);
}
// 设置LED颜色为红色
setLedColor(device, 255, 0, 0);
WebHID 的一些坑
WebHID 虽然强大,但也有些需要注意的地方:
- 兼容性: WebHID 还在发展中,不是所有的浏览器都支持。 目前 Chrome 和 Edge 的支持比较好。
- 安全性: 访问HID设备需要用户授权,浏览器会弹出对话框,询问用户是否允许访问。
- 跨域: 如果你的网页是通过 HTTPS 访问的,那么你的HID设备也必须支持HTTPS,否则可能会出现跨域问题。
- Report 描述符: 解析HID Report 描述符需要一定的功底,需要花时间学习和调试。
WebHID 的未来
WebHID 的潜力是巨大的。 它可以用于各种各样的应用,比如:
- 游戏: 自定义游戏手柄、VR/AR 设备
- 工业控制: 机器人、传感器
- 医疗: 医疗设备
- 教育: STEM 教育
随着WebHID 的不断发展,相信会有越来越多的创新应用涌现出来。
总结
今天咱们聊了WebHID API的基本概念、使用方法和一些实战案例。 希望你能从中受益,开启你的WebHID 探索之旅!
记住,WebHID 是一个强大的工具,但也是一个需要耐心和技巧的工具。 多尝试,多实践,你就能掌握它,创造出属于你的奇妙应用!
好了,今天的讲座就到这里。 谢谢大家! 别忘了,编程的乐趣在于探索和创造! 祝大家编程愉快!