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 {
// 将输入数据转换为Uint8Array
const 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;
}