1. 简介
WebHID API允许网页应用直接与HID(人机接口设备)进行通信。本教程将演示如何创建一个基础的WebHID应用,实现以下功能:
- 显示和获取HID设备列表
- 连接/断开HID设备
- 读取设备数据
- 向设备发送数据
2. 兼容性和前提条件
2.1 浏览器支持
- 主要支持Chrome浏览器
- 需要在安全上下文中运行(HTTPS或localhost)
2.2 权限要求
- 需要用户明确授权才能访问HID设备
- 某些操作系统可能需要额外的权限设置
3. 项目结构
项目包含两个主要文件:
├── index.html // 页面结构和样式
└── hid-demo.js // WebHID功能实现
4. 实现步骤
4.1 创建基础HTML结构
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>HID Device Communication</title><style>#output {width: 100%;height: 200px;margin: 10px 0;padding: 5px;border: 1px solid #ccc;overflow-y: auto;}/* ... 其他样式 ... */</style>
</head>
<body><h1>WebHID API 演示</h1><button id="connectButton">连接新HID设备</button><h2>已连接设备列表:</h2><ul id="deviceList"></ul><div class="input-group"><input type="text" id="sendData" placeholder="输入要发送的数据(用逗号分隔的数字)"><button id="sendButton">发送数据</button></div><h2>输出日志:</h2><pre id="output"></pre><script src="hid-demo.js"></script>
</body>
</html>
4.2 实现WebHID核心功能
4.2.1 初始化和获取设备列表
let currentDevice = null;document.addEventListener('DOMContentLoaded', () => {// 获取已授权的设备navigator.hid.getDevices().then(devices => {updateDeviceList(devices);}).catch(error => {console.error('Error getting devices:', error);});
});
4.2.2 连接新设备
document.getElementById('connectButton').addEventListener('click', async () => {try {const devices = await navigator.hid.requestDevice({filters: [] // 空过滤器显示所有设备});if (devices.length > 0) {updateDeviceList(devices);}} catch (error) {console.error('Error connecting to device:', error);}
});
4.2.3 设备连接/断开处理
async function toggleConnect(device) {try {if (device.opened) {await device.close();currentDevice = null;appendToOutput(`设备已断开: ${device.productName}`);} else {await device.open();currentDevice = device;appendToOutput(`设备已连接: ${device.productName}`);// 监听设备输入报告device.addEventListener('inputreport', event => {const {data, reportId} = event;const value = new Uint8Array(data.buffer);appendToOutput(`收到数据 (报告ID ${reportId}): ${Array.from(value)}`);});}// 刷新设备列表显示const devices = await navigator.hid.getDevices();updateDeviceList(devices);} catch (error) {console.error('Error toggling device connection:', error);appendToOutput(`操作失败: ${error.message}`);}
}
4.2.4 数据发送功能
document.getElementById('sendButton').addEventListener('click', async () => {if (!currentDevice || !currentDevice.opened) {alert('请先连接设备!');return;}const data = document.getElementById('sendData').value;try {const dataArray = new Uint8Array(data.split(',').map(x => parseInt(x.trim())));await currentDevice.sendReport(0, dataArray);appendToOutput('已发送数据: ' + data);} catch (error) {console.error('Error sending data:', error);appendToOutput('发送数据失败: ' + error.message);}
});
5. 使用说明
5.1 连接设备
- 点击"连接新HID设备"按钮
- 在弹出的系统对话框中选择要连接的设备
- 设备将显示在已连接设备列表中
5.2 数据收发
- 连接设备后,设备发送的数据会自动显示在输出日志中
- 在输入框中输入要发送的数据(格式:逗号分隔的数字,如
1,2,3,4
) - 点击"发送数据"按钮发送数据
6. 注意事项
-
数据格式:
- 发送数据需要使用逗号分隔的数字格式
- 不同设备可能需要特定的数据格式,请参考设备文档
-
报告ID:
- 当前示例使用默认报告ID (0)
- 某些设备可能需要特定的报告ID,需要相应修改代码
-
错误处理:
- 所有操作都包含错误处理
- 错误信息会显示在输出日志中
-
安全性:
- 必须在HTTPS或localhost环境下运行
- 需要用户明确授权才能访问设备
7. 调试建议
- 使用Chrome开发者工具监控控制台输出
- 检查设备连接状态和错误信息
- 验证数据格式是否符合设备要求
- 确保设备驱动正确安装
8. 扩展建议
- 添加设备过滤器,只显示特定类型的设备
- 实现自定义数据格式转换
- 添加数据可视化功能
- 实现设备自动重连机制
9. 参考资源
- WebHID API MDN文档
- HID接口规范
- HIDDevice接口
完整Demo
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>HID Device Communication</title><style>#output {width: 100%;height: 200px;margin: 10px 0;padding: 5px;border: 1px solid #ccc;overflow-y: auto;}#deviceList {margin: 10px 0;}#deviceList li {margin: 5px 0;padding: 5px;border: 1px solid #eee;display: flex;justify-content: space-between;align-items: center;}.input-group {margin: 10px 0;}</style>
</head>
<body><h1>WebHID API 演示</h1><button id="connectButton">连接新HID设备</button><h2>已连接设备列表:</h2><ul id="deviceList"></ul><div class="input-group"><input type="text" id="sendData" placeholder="输入要发送的数据(用逗号分隔的数字)"><button id="sendButton">发送数据</button></div><h2>输出日志:</h2><pre id="output"></pre><script src="hid-demo.js"></script>
</body>
</html>
let currentDevice = null;document.addEventListener('DOMContentLoaded', () => {// 获取已授权的设备navigator.hid.getDevices().then(devices => {updateDeviceList(devices);}).catch(error => {console.error('Error getting devices:', error);});// 连接新设备按钮事件document.getElementById('connectButton').addEventListener('click', async () => {try {// 请求连接HID设备const devices = await navigator.hid.requestDevice({filters: [] // 空过滤器显示所有设备});if (devices.length > 0) {updateDeviceList(devices);}} catch (error) {console.error('Error connecting to device:', error);}});// 发送数据按钮事件document.getElementById('sendButton').addEventListener('click', async () => {if (!currentDevice || !currentDevice.opened) {alert('请先连接设备!');return;}const data = document.getElementById('sendData').value;console.log('发送数据: ' + data);try {// 将输入数据转换为Uint8Arrayconst dataArray = new Uint8Array(data.split(',').map(x => parseInt(x.trim())));await currentDevice.sendReport(5, dataArray);appendToOutput('已发送数据: ' + data);console.log('已发送数据: ' + data);} catch (error) {console.error('Error sending data:', error);appendToOutput('发送数据失败: ' + error.message);}});
});// 更新设备列表显示
function updateDeviceList(devices) {const deviceList = document.getElementById('deviceList');deviceList.innerHTML = '';devices.forEach(device => {const li = document.createElement('li');li.textContent = `${device.productName} (VID: ${device.vendorId}, PID: ${device.productId})`;const connectBtn = document.createElement('button');connectBtn.textContent = device.opened ? '断开' : '连接';connectBtn.addEventListener('click', () => toggleConnect(device));li.appendChild(connectBtn);deviceList.appendChild(li);});
}// 连接/断开设备
async function toggleConnect(device) {try {if (device.opened) {await device.close();currentDevice = null;appendToOutput(`设备已断开: ${device.productName}`);} else {await device.open();currentDevice = device;appendToOutput(`设备已连接: ${device.productName}`);// 监听设备输入报告device.addEventListener('inputreport', event => {const {data, reportId} = event;const value = new Uint8Array(data.buffer);appendToOutput(`收到数据 (报告ID ${reportId}): ${Array.from(value)}`);});}// 刷新设备列表显示const devices = await navigator.hid.getDevices();updateDeviceList(devices);} catch (error) {console.error('Error toggling device connection:', error);appendToOutput(`操作失败: ${error.message}`);}
}// 添加输出信息
function appendToOutput(message) {const output = document.getElementById('output');output.textContent += message + '\n';output.scrollTop = output.scrollHeight;
}