各位观众老爷,欢迎来到今天的 Vue.js 硬件交互特别节目!今天咱们不搞花里胡哨的 UI 动画,来点硬核的——让你的 Vue 应用跟真实世界的硬件设备眉来眼去,哦不,是进行数据交互。
我们今天的主角是 WebUSB 和 Web Bluetooth,这两个浏览器 API 允许你直接从 JavaScript 代码控制连接到电脑的 USB 设备和蓝牙设备。想想就刺激,对不对?
为什么选择 WebUSB 和 Web Bluetooth?
在过去,想让 Web 应用跟硬件打交道,那可是个麻烦事。你得安装各种浏览器插件、ActiveX 控件,或者干脆整个 Native App。这不仅用户体验差,安全性也让人捏把汗。
WebUSB 和 Web Bluetooth 的出现,彻底改变了游戏规则。它们提供了标准化的 API,允许在浏览器中安全地访问硬件设备,而且无需安装任何插件!
特性 | WebUSB | Web Bluetooth |
---|---|---|
连接方式 | USB | Bluetooth |
适用场景 | 打印机、3D 打印机、开发板等需要高速数据传输的设备 | 智能手表、心率带、智能家居设备等低功耗设备 |
安全性 | 基于 HTTPS 协议,用户授权访问 | 基于 HTTPS 协议,用户授权访问 |
兼容性 | Chrome, Edge, Opera 等 | Chrome, Edge, Opera, Safari (部分支持)等 |
准备工作
- HTTPS 环境: 必须使用 HTTPS 协议,否则浏览器会拒绝访问 WebUSB 和 Web Bluetooth。本地开发可以使用
localhost
。 - 现代浏览器: 确保你使用的是支持 WebUSB 和 Web Bluetooth 的浏览器,比如 Chrome, Edge, Opera。Safari 对 Web Bluetooth 的支持还在完善中。
- 硬件设备: 废话,当然得有硬件设备才能玩啊!最好是那种已经支持 WebUSB 或 Web Bluetooth 的设备,或者你能自己写固件控制的设备。
- Vue.js 项目: 我们今天的主战场是 Vue.js,所以你需要一个 Vue 项目。如果你还没有,可以用 Vue CLI 快速搭建一个。
vue create my-hardware-app
WebUSB 实战:点亮你的 LED 灯
咱们先来个简单的 WebUSB 示例,假设你有一个 USB 控制的 LED 灯(或者一个 Arduino 开发板,可以模拟 LED 灯)。我们的目标是:点击 Vue 应用中的按钮,点亮或熄灭 LED 灯。
1. HTML 结构
在你的 App.vue
组件中,添加两个按钮和一个显示设备连接状态的区域:
<template>
<div id="app">
<h1>WebUSB LED 控制器</h1>
<p>设备状态: {{ deviceStatus }}</p>
<button @click="connectDevice" :disabled="isConnecting || isConnected">
{{ isConnecting ? '连接中...' : (isConnected ? '已连接' : '连接设备') }}
</button>
<button @click="toggleLed" :disabled="!isConnected">
{{ ledStatus ? '熄灭 LED' : '点亮 LED' }}
</button>
</div>
</template>
2. JavaScript 逻辑
接下来,在 script
标签中编写 JavaScript 代码:
<script>
export default {
data() {
return {
device: null,
deviceStatus: '未连接',
isConnecting: false,
isConnected: false,
ledStatus: false, // LED 的状态 (true: 亮, false: 灭)
endpointIn: null, // 输入端点
endpointOut: null, // 输出端点
};
},
methods: {
async connectDevice() {
this.isConnecting = true;
try {
// 1. 请求设备
this.device = await navigator.usb.requestDevice({
filters: [], // 可以根据 vendorId 和 productId 过滤设备
});
this.deviceStatus = '已选择设备';
// 2. 打开设备
await this.device.open();
this.deviceStatus = '设备已打开';
// 3. 选择配置 (通常是第一个配置)
await this.device.selectConfiguration(1);
this.deviceStatus = '配置已选择';
// 4. 声明接口
await this.device.claimInterface(0); // 接口编号通常是 0
this.deviceStatus = '接口已声明';
// 5. 找到输入和输出端点
let endpointInfo = this.findEndpoints(this.device.configuration.interfaces[0].alternates[0].endpoints);
if(endpointInfo){
this.endpointIn = endpointInfo.endpointIn;
this.endpointOut = endpointInfo.endpointOut;
} else {
this.deviceStatus = '未找到输入或输出端点';
this.disconnectDevice();
return;
}
this.isConnected = true;
this.isConnecting = false;
this.deviceStatus = '连接成功';
} catch (error) {
this.deviceStatus = '连接失败: ' + error;
this.isConnecting = false;
this.isConnected = false;
console.error('WebUSB 连接错误:', error);
}
},
async toggleLed() {
// 假设你的 USB 设备使用 0x01 控制 LED 的开关
const value = this.ledStatus ? 0x00 : 0x01;
try {
// 发送控制命令
await this.device.transferOut(this.endpointOut.endpointNumber, new Uint8Array([value]));
this.ledStatus = !this.ledStatus; // 切换 LED 状态
this.deviceStatus = `LED 已${this.ledStatus ? '点亮' : '熄灭'}`;
} catch (error) {
this.deviceStatus = '控制 LED 失败: ' + error;
console.error('WebUSB 控制 LED 错误:', error);
}
},
disconnectDevice() {
if (this.device) {
try{
this.device.close();
this.deviceStatus = '设备已断开';
} catch (error){
this.deviceStatus = '断开设备失败: ' + error;
}
this.device = null;
this.isConnected = false;
this.isConnecting = false;
this.ledStatus = false;
this.endpointIn = null;
this.endpointOut = null;
}
},
findEndpoints(endpoints){
let endpointIn = null;
let endpointOut = null;
for(let endpoint of endpoints){
if(endpoint.direction === 'in'){
endpointIn = endpoint;
} else if (endpoint.direction === 'out'){
endpointOut = endpoint;
}
}
if(endpointIn && endpointOut){
return {endpointIn, endpointOut};
} else {
return null;
}
},
},
beforeUnmount() {
this.disconnectDevice();
}
};
</script>
3. 代码解释
navigator.usb.requestDevice()
: 弹出设备选择框,让用户选择 USB 设备。filters
数组可以用来过滤特定厂商和产品的设备。device.open()
: 打开 USB 设备。device.selectConfiguration()
: 选择设备的配置。通常情况下,第一个配置 (配置编号为 1) 就够用了。device.claimInterface()
: 声明要使用的接口。接口编号通常是 0。device.transferOut()
: 向 USB 设备发送数据。第一个参数是输出端点的编号,第二个参数是要发送的数据(必须是Uint8Array
类型)。- 错误处理: 别忘了用
try...catch
捕获错误,并显示给用户。
4. 注意事项
- USB 设备描述符: 你的 USB 设备需要提供正确的设备描述符,包括
vendorId
(厂商 ID)和productId
(产品 ID)。这些信息可以在设备的驱动程序或文档中找到。 - 权限问题: 某些操作系统可能需要额外的权限才能访问 USB 设备。
- 数据格式: 你需要了解你的 USB 设备使用的数据格式,才能正确地发送和接收数据。
Web Bluetooth 实战:读取心率数据
接下来,咱们来挑战一下 Web Bluetooth,读取一个蓝牙心率带的数据。
1. HTML 结构
跟 WebUSB 类似,我们需要一些按钮和显示区域:
<template>
<div id="app">
<h1>Web Bluetooth 心率监测器</h1>
<p>设备状态: {{ deviceStatus }}</p>
<button @click="connectBluetoothDevice" :disabled="isConnecting || isConnected">
{{ isConnecting ? '连接中...' : (isConnected ? '已连接' : '连接设备') }}
</button>
<p v-if="heartRate">心率: {{ heartRate }} BPM</p>
</div>
</template>
2. JavaScript 逻辑
<script>
export default {
data() {
return {
device: null,
server: null,
heartRateService: null,
heartRateCharacteristic: null,
deviceStatus: '未连接',
isConnecting: false,
isConnected: false,
heartRate: null,
};
},
methods: {
async connectBluetoothDevice() {
this.isConnecting = true;
try {
// 1. 请求设备
this.device = await navigator.bluetooth.requestDevice({
filters: [{ services: ['heart_rate'] }], // 只显示支持心率服务的设备
optionalServices: ['heart_rate', 'battery_service'], // 请求访问其他服务
});
this.deviceStatus = '已选择设备: ' + this.device.name;
// 2. 连接 GATT 服务器
this.server = await this.device.gatt.connect();
this.deviceStatus = '已连接 GATT 服务器';
// 3. 获取心率服务
this.heartRateService = await this.server.getPrimaryService('heart_rate');
this.deviceStatus = '已获取心率服务';
// 4. 获取心率测量特征
this.heartRateCharacteristic = await this.heartRateService.getCharacteristic('heart_rate_measurement');
this.deviceStatus = '已获取心率测量特征';
// 5. 启动心率通知
await this.heartRateCharacteristic.startNotifications();
this.deviceStatus = '已启动心率通知';
// 6. 监听心率数据
this.heartRateCharacteristic.addEventListener('characteristicvaluechanged', this.handleHeartRate);
this.isConnected = true;
this.isConnecting = false;
} catch (error) {
this.deviceStatus = '连接失败: ' + error;
this.isConnecting = false;
this.isConnected = false;
console.error('Web Bluetooth 连接错误:', error);
}
},
handleHeartRate(event) {
// 解析心率数据
const value = event.target.value;
const flags = value.getUint8(0);
const rate16Bits = flags & 0x1;
let heartRate;
if (rate16Bits) {
heartRate = value.getUint16(1, /* littleEndian= */ true);
} else {
heartRate = value.getUint8(1);
}
this.heartRate = heartRate;
},
async disconnectBluetoothDevice() {
if (this.device && this.device.gatt.connected) {
try{
await this.heartRateCharacteristic.stopNotifications();
this.heartRateCharacteristic.removeEventListener('characteristicvaluechanged', this.handleHeartRate);
this.device.gatt.disconnect();
this.deviceStatus = '设备已断开';
} catch (error){
this.deviceStatus = '断开设备失败: ' + error;
}
this.device = null;
this.server = null;
this.heartRateService = null;
this.heartRateCharacteristic = null;
this.isConnected = false;
this.isConnecting = false;
this.heartRate = null;
}
},
},
beforeUnmount() {
this.disconnectBluetoothDevice();
}
};
</script>
3. 代码解释
navigator.bluetooth.requestDevice()
: 弹出设备选择框,filters
数组指定要搜索的服务 UUID。optionalServices
数组允许你请求访问其他服务。device.gatt.connect()
: 连接到设备的 GATT 服务器。GATT (Generic Attribute Profile) 是 Bluetooth LE 的核心概念,它定义了设备提供的服务和特征。server.getPrimaryService()
: 获取指定 UUID 的服务。service.getCharacteristic()
: 获取指定 UUID 的特征。特征是 GATT 中最小的数据单元。characteristic.startNotifications()
: 启动特征值的通知。当特征值发生变化时,设备会向你的应用发送通知。characteristic.addEventListener('characteristicvaluechanged', this.handleHeartRate)
: 监听特征值变化的事件,并在handleHeartRate
方法中解析心率数据。- 心率数据解析: 心率数据的格式比较复杂,需要根据 Bluetooth SIG 规范进行解析。这段代码只是一个简单的示例,可能需要根据你的心率带的实际情况进行调整。
4. 注意事项
- Bluetooth UUID: 你需要知道你想要访问的服务和特征的 UUID。这些信息通常可以在设备的文档或 Bluetooth SIG 数据库中找到。
- 数据格式: 不同的 Bluetooth 设备可能使用不同的数据格式。你需要了解你的设备的具体格式,才能正确地解析数据。
- 安全性: 某些 Bluetooth 设备可能需要配对或授权才能访问。
进一步探索
- Web Serial API: 如果你的硬件设备使用串口通信,可以考虑使用 Web Serial API。
- HID API: 如果你的硬件设备是 HID (Human Interface Device) 设备,比如键盘、鼠标、游戏手柄等,可以使用 HID API。
- 封装成 Vue 组件: 为了方便复用和管理,你可以把 WebUSB 和 Web Bluetooth 的代码封装成 Vue 组件。
- 状态管理: 使用 Vuex 或 Pinia 等状态管理库来管理设备连接状态和数据。
- 错误处理和重连机制: 增加更完善的错误处理和自动重连机制,提高应用的健壮性。
总结
WebUSB 和 Web Bluetooth 为 Web 应用打开了通往硬件世界的大门。虽然学习曲线稍微陡峭,但只要你掌握了基本概念和 API,就能创造出令人惊艳的应用。
记住,实践是检验真理的唯一标准。赶紧拿起你的硬件设备,开始你的 Web 硬件之旅吧!
好了,今天的讲座就到这里。希望大家有所收获,下次再见!