阐述 Web Bluetooth API 如何让网页与低功耗蓝牙 (BLE) 设备交互,例如智能穿戴设备。

各位同学,掌声欢迎来到今天的“Web Bluetooth API:让你的网页和智能穿戴设备谈恋爱”讲座! 我是你们的老朋友,今天咱们就来聊聊如何用几行代码,让你的网页直接和智能手表、手环、甚至智能体重秤眉来眼去。

开场白:蓝牙,不再是遥远的传说

以前,网页想和蓝牙设备打交道?那简直比登天还难!你得写 Native 应用,用各种复杂的 SDK,费时费力。但 Web Bluetooth API 的出现,就像一道闪电,劈开了这片混沌,让一切变得简单起来。现在,只需要你的浏览器支持(Chrome、Edge、Opera 等主流浏览器),你就能在网页里直接控制蓝牙设备,是不是想想都觉得刺激?

Web Bluetooth API 的核心概念

Web Bluetooth API 主要围绕着以下几个核心概念展开:

  • navigator.bluetooth.requestDevice(): 这是整个 API 的入口,负责弹出一个设备选择窗口,让用户选择要连接的蓝牙设备。
  • BluetoothDevice: 代表一个蓝牙设备,包含了设备的信息(例如名称、UUID 等)和连接状态。
  • BluetoothGATTServer: GATT(Generic Attribute Profile)服务器,负责提供设备的各种服务和特性。
  • BluetoothGATTService: GATT 服务,是对设备功能的抽象,例如心率监测、电池电量等。
  • BluetoothGATTCharacteristic: GATT 特性,是服务的具体属性,例如心率值、电池电量值等。

简单来说,你可以把蓝牙设备想象成一栋大楼,BluetoothGATTServer 是大楼的管理员,BluetoothGATTService 是大楼里的不同房间(比如健身房、餐厅),BluetoothGATTCharacteristic 则是房间里的具体物品(比如跑步机、餐桌)。

实战演练:连接你的心率带

光说不练假把式,咱们直接上代码,以连接一个心率带为例,一步步探索 Web Bluetooth API 的奥秘。

1. 启动设备选择器

首先,我们需要调用 navigator.bluetooth.requestDevice(),让浏览器弹出设备选择窗口。这个函数接收一个 filters 参数,用于指定要搜索的设备类型。

async function connectHeartRateSensor() {
  try {
    const device = await navigator.bluetooth.requestDevice({
      filters: [{ services: ['heart_rate'] }] // 指定只搜索心率服务
    });

    console.log('设备名称:' + device.name);
    console.log('设备 ID:' + device.id);

    // 连接 GATT 服务器
    await connectGattServer(device);

  } catch (error) {
    console.log('连接失败:' + error);
  }
}

这段代码会弹出一个窗口,列出所有提供 "heart_rate" 服务的蓝牙设备。用户选择设备后,requestDevice() 函数会返回一个 BluetoothDevice 对象。

小贴士:filters 参数的妙用

filters 参数非常强大,你可以用它来精确控制要搜索的设备类型。除了指定服务 UUID,还可以指定设备名称或名称前缀。

参数 描述 示例
services 一个包含 GATT 服务 UUID 的数组。只搜索提供这些服务的设备。 [{ services: ['heart_rate', 'battery_service'] }]
name 设备名称。只搜索名称完全匹配的设备。 [{ name: 'MyHeartRateSensor' }]
namePrefix 设备名称前缀。只搜索名称以此前缀开头的设备。 [{ namePrefix: 'HRM-' }]
deviceId 蓝牙设备的device ID。 [{ deviceId: '39c50141-1057-456f-a0f4-a822349f744a' }]
manufacturerData 制造商数据。如果设备广播中包含了特定的制造商数据,你可以使用此过滤器。 [{ manufacturerData: [{ companyIdentifier: 0x004C /* Apple's Company Identifier */ }] }]

2. 连接 GATT 服务器

拿到 BluetoothDevice 对象后,下一步是连接它的 GATT 服务器。

async function connectGattServer(device) {
  try {
    const server = await device.gatt.connect();
    console.log('GATT 服务器已连接');

    // 获取心率服务
    await getHeartRateService(server);

  } catch (error) {
    console.log('GATT 连接失败:' + error);
  }
}

device.gatt.connect() 返回一个 BluetoothGATTServer 对象,代表设备的 GATT 服务器。

注意:gatt.connect() 的玄机

gatt.connect() 是一个异步操作,如果设备已经连接,它会立即返回;如果设备未连接,它会尝试建立连接。

3. 获取心率服务

连接 GATT 服务器后,我们需要获取心率服务,才能读取心率数据。

async function getHeartRateService(server) {
  try {
    const service = await server.getPrimaryService('heart_rate');
    console.log('心率服务已获取');

    // 获取心率测量特性
    await getHeartRateMeasurementCharacteristic(service);

  } catch (error) {
    console.log('获取心率服务失败:' + error);
  }
}

server.getPrimaryService('heart_rate') 返回一个 BluetoothGATTService 对象,代表心率服务。

服务 UUID 的重要性

每个 GATT 服务都有一个唯一的 UUID(Universally Unique Identifier),用于标识服务的类型。心率服务的 UUID 是 "heart_rate"。

4. 获取心率测量特性

获取心率服务后,我们需要获取心率测量特性,才能读取心率数据。

async function getHeartRateMeasurementCharacteristic(service) {
  try {
    const characteristic = await service.getCharacteristic('heart_rate_measurement');
    console.log('心率测量特性已获取');

    // 启动心率数据通知
    await startHeartRateNotifications(characteristic);

  } catch (error) {
    console.log('获取心率测量特性失败:' + error);
  }
}

service.getCharacteristic('heart_rate_measurement') 返回一个 BluetoothGATTCharacteristic 对象,代表心率测量特性。

特性 UUID 的重要性

每个 GATT 特性也有一个唯一的 UUID,用于标识特性的类型。心率测量特性的 UUID 是 "heart_rate_measurement"。

5. 启动心率数据通知

获取心率测量特性后,我们需要启动数据通知,才能实时接收心率数据。

async function startHeartRateNotifications(characteristic) {
  try {
    await characteristic.startNotifications();
    console.log('心率数据通知已启动');

    // 监听心率数据变化事件
    characteristic.addEventListener('characteristicvaluechanged', handleHeartRateData);

  } catch (error) {
    console.log('启动心率数据通知失败:' + error);
  }
}

characteristic.startNotifications() 启动数据通知。当心率数据发生变化时,characteristicvaluechanged 事件会被触发。

6. 处理心率数据

最后,我们需要编写 handleHeartRateData 函数,处理接收到的心率数据。

function handleHeartRateData(event) {
  const value = event.target.value;
  const heartRate = value.getUint8(1); // 获取心率值

  console.log('心率:' + heartRate + ' bpm');

  // 将心率值显示在网页上
  document.getElementById('heartRate').textContent = heartRate;
}

event.target.value 是一个 DataView 对象,包含了心率数据的原始字节。我们需要根据数据的格式,从 DataView 对象中提取心率值。

完整代码示例

<!DOCTYPE html>
<html>
<head>
  <title>Web Bluetooth 心率监测</title>
</head>
<body>
  <h1>心率:<span id="heartRate">--</span> bpm</h1>
  <button onclick="connectHeartRateSensor()">连接心率带</button>

  <script>
    async function connectHeartRateSensor() {
      try {
        const device = await navigator.bluetooth.requestDevice({
          filters: [{ services: ['heart_rate'] }]
        });

        console.log('设备名称:' + device.name);
        console.log('设备 ID:' + device.id);

        const server = await device.gatt.connect();
        console.log('GATT 服务器已连接');

        const service = await server.getPrimaryService('heart_rate');
        console.log('心率服务已获取');

        const characteristic = await service.getCharacteristic('heart_rate_measurement');
        console.log('心率测量特性已获取');

        await characteristic.startNotifications();
        console.log('心率数据通知已启动');

        characteristic.addEventListener('characteristicvaluechanged', handleHeartRateData);

      } catch (error) {
        console.log('连接失败:' + error);
      }
    }

    function handleHeartRateData(event) {
      const value = event.target.value;
      const heartRate = value.getUint8(1); // 获取心率值

      console.log('心率:' + heartRate + ' bpm');

      document.getElementById('heartRate').textContent = heartRate;
    }
  </script>
</body>
</html>

将这段代码保存为 HTML 文件,然后在支持 Web Bluetooth API 的浏览器中打开,点击“连接心率带”按钮,就可以连接你的心率带,并在网页上实时显示心率数据了。

进阶技巧:读取和写入数据

除了监听数据通知,Web Bluetooth API 还支持读取和写入数据。

  • 读取数据: 使用 characteristic.readValue() 函数可以读取特性的当前值。
  • 写入数据: 使用 characteristic.writeValue() 函数可以向特性写入新的值。

示例:读取电池电量

假设你的设备提供了一个电池服务,你可以使用以下代码读取电池电量:

async function getBatteryLevel(service) {
  try {
    const characteristic = await service.getCharacteristic('battery_level');
    const value = await characteristic.readValue();
    const batteryLevel = value.getUint8(0); // 获取电池电量值

    console.log('电池电量:' + batteryLevel + '%');

  } catch (error) {
    console.log('获取电池电量失败:' + error);
  }
}

示例:写入数据

假设你的设备提供了一个控制 LED 灯的服务,你可以使用以下代码控制 LED 灯的开关:

async function setLedState(characteristic, state) {
  try {
    const value = new Uint8Array([state]); // 1 表示开,0 表示关
    await characteristic.writeValue(value);
    console.log('LED 灯状态已设置');

  } catch (error) {
    console.log('设置 LED 灯状态失败:' + error);
  }
}

安全性 considerations

Web Bluetooth API 涉及用户隐私和安全,因此浏览器对 API 的使用做了一些限制:

  • HTTPS Only: Web Bluetooth API 只能在 HTTPS 协议下使用,确保数据传输的安全性。
  • User Gesture Required: 启动设备扫描必须由用户主动触发(例如点击按钮),防止恶意网站未经用户允许扫描蓝牙设备。
  • Permissions: 浏览器会询问用户是否允许网站访问蓝牙设备,用户可以随时撤销权限。

Web Bluetooth API 的应用场景

Web Bluetooth API 的应用场景非常广泛,例如:

  • 智能穿戴设备: 监测心率、步数、睡眠质量等数据。
  • 智能家居: 控制灯泡、插座、窗帘等设备。
  • 医疗健康: 远程监测血糖、血压等数据。
  • 工业自动化: 控制机器人、传感器等设备。
  • 教育: 开发互动式教学工具。

Web Bluetooth API 的优势

  • 跨平台: 可以在支持 Web Bluetooth API 的任何浏览器中使用,无需针对不同平台开发不同的应用。
  • 易于开发: 使用 JavaScript 编写,学习成本低,开发效率高。
  • 安全可靠: 受浏览器安全机制保护,用户可以控制网站对蓝牙设备的访问权限。
  • 无需安装: 用户只需打开网页即可使用,无需安装任何插件或应用。

Web Bluetooth API 的局限性

  • 浏览器支持: 并非所有浏览器都支持 Web Bluetooth API。
  • 设备兼容性: 并非所有蓝牙设备都支持 Web Bluetooth API。
  • 安全性限制: 受浏览器安全机制的限制,无法访问某些底层蓝牙功能。

总结:拥抱 Web Bluetooth API 的未来

Web Bluetooth API 是一个强大而灵活的工具,它为 Web 应用打开了通往蓝牙世界的大门。虽然目前还存在一些局限性,但随着浏览器支持的不断完善和蓝牙技术的不断发展,Web Bluetooth API 的应用前景将更加广阔。

希望今天的讲座能帮助大家更好地理解和使用 Web Bluetooth API。下次有机会,咱们再一起探索更多有趣的 Web 技术!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注