JS `Web Bluetooth` `GATT Profile` 与 `Characteristic` `Properties` 探索

好嘞,各位听众老爷们,今天咱们来聊聊Web Bluetooth这玩意儿,特别是关于GATT Profile、Characteristic以及Properties这些个听起来高大上,实际上也没那么难的东西。准备好了吗?咱们开讲!

Web Bluetooth:让浏览器也能“蓝牙一下”

Web Bluetooth API,顾名思义,就是让你的网页能直接跟蓝牙设备对话。想想看,以后刷手环、控制智能家居,甚至给你的智能玩具写个控制界面,都不需要装APP,直接用浏览器就能搞定,是不是有点小激动?

当然,Web Bluetooth也不是万能的,它需要用户的许可,而且不是所有浏览器都支持(支持度最好的肯定是Chrome系了)。但总的来说,它开辟了一个新的天地,让Web应用有了更多想象空间。

GATT:蓝牙世界的“接口规范”

要理解Web Bluetooth,就不得不提GATT (Generic Attribute Profile)。你可以把它想象成蓝牙设备之间的“通用语”,或者说是“接口规范”。所有的蓝牙设备都必须遵循这个规范,才能互相交流。

GATT定义了一套结构化的数据组织方式,让设备可以发布自己的服务(Services)和特性(Characteristics),就像一个餐厅的菜单一样,告诉你它能提供什么。

GATT的层级结构

GATT的层级结构主要由以下几个部分组成:

  • Profile (配置文件): 简单来说,Profile 就是一个“应用场景”或者“使用案例”。比如,心率 Profile 就定义了如何通过蓝牙传输心率数据。一个设备可以支持多个 Profile。
  • Service (服务): Service 是 Profile 的组成部分,它是一组相关 Characteristics 的集合。比如,心率 Service 就包含心率测量、电量等等 Characteristics。每个 Service 都有一个唯一的 UUID (Universally Unique Identifier)。
  • Characteristic (特性): Characteristic 是 GATT 中最基本的数据单元,它代表设备可以提供或接受的特定数据。比如,心率测量值就是一个 Characteristic。每个 Characteristic 也有一个唯一的 UUID。
  • Descriptor (描述符): Descriptor 是对 Characteristic 的补充说明,它提供关于 Characteristic 的元数据,比如数据的格式、单位、权限等等。

可以用一个表格来更清晰地表示这个层级结构:

层次 描述 例子
Profile 应用场景,定义一组相关服务和特性 心率 Profile, 血压 Profile
Service 服务的集合,包含一组相关特性 心率服务, 电量服务
Characteristic 数据的最小单元,代表一个特定的数据 心率测量值, 电量百分比
Descriptor 对 Characteristic 的补充说明 心率测量值的单位(BPM), 电量百分比的范围(0-100)

Characteristic:数据的“容器”

Characteristic 是 GATT 中最关键的概念之一。它就像一个数据的“容器”,用来存放设备可以提供或接受的特定数据。

每个 Characteristic 都有以下几个重要的属性:

  • UUID (Universally Unique Identifier): 一个128位的唯一标识符,用来区分不同的 Characteristic。有些常用的 Characteristic 有标准的 UUID,比如心率测量值的 UUID 是 0x2A37
  • Value (值): Characteristic 实际存储的数据。值的类型可以是各种各样的,比如整数、浮点数、字符串等等。
  • Properties (属性): 定义了 Characteristic 的操作权限,比如是否可读、是否可写、是否可订阅等等。

Characteristic Properties:控制数据的“开关”

Characteristic Properties 定义了你可以对 Characteristic 进行哪些操作。它们决定了数据的流动方向和方式。常见的 Properties 包括:

  • Read (可读): 允许你从 Characteristic 中读取数据。
  • Write (可写): 允许你向 Characteristic 中写入数据。
  • Write Without Response (无应答写): 允许你向 Characteristic 中写入数据,但设备不会返回任何响应。这种方式速度更快,但可靠性较低。
  • Notify (通知): 允许设备在 Characteristic 的值发生变化时主动通知客户端。客户端需要先订阅才能收到通知。
  • Indicate (指示): 类似于 Notify,但设备在发送通知后会等待客户端的确认。这种方式可靠性更高,但速度较慢。
  • Broadcast (广播): 允许设备广播 Characteristic 的值。这种方式不需要客户端连接,但数据是公开的。

可以用一张表格来总结一下这些 Properties:

Property 描述 适用场景
Read 允许客户端读取 Characteristic 的值 获取设备的当前状态、配置信息等等
Write 允许客户端向 Characteristic 写入值 设置设备的参数、控制设备的行为等等
Write Without Response 允许客户端向 Characteristic 写入值,但不要求设备响应 对实时性要求较高,但对可靠性要求不高的场景,比如快速控制
Notify 允许设备在 Characteristic 的值发生变化时主动通知客户端 实时监控设备的状态变化,比如心率、温度等等
Indicate 允许设备在 Characteristic 的值发生变化时主动通知客户端,并等待客户端确认 对可靠性要求较高的场景,比如关键数据的传输
Broadcast 允许设备广播 Characteristic 的值 不需要客户端连接,但数据是公开的场景,比如广告信息

Web Bluetooth 实战:读取心率数据

光说不练假把式,咱们来写个小例子,演示如何使用 Web Bluetooth 读取心率数据。

1. 连接设备

首先,我们需要连接到支持心率 Profile 的蓝牙设备。这里我们使用 navigator.bluetooth.requestDevice() 方法来请求用户选择设备。

async function connectDevice() {
  try {
    const device = await navigator.bluetooth.requestDevice({
      filters: [{ services: ['heart_rate'] }] // 指定我们只搜索支持心率服务的设备
    });

    console.log('连接设备:', device.name);
    const server = await device.gatt.connect();
    console.log('连接 GATT server');
    await getHeartRateData(server); // 连接成功后,获取心率数据
  } catch (error) {
    console.error('连接设备失败:', error);
  }
}

2. 获取心率服务和 Characteristic

连接成功后,我们需要获取心率服务和心率测量 Characteristic。

async function getHeartRateData(server) {
  try {
    const service = await server.getPrimaryService('heart_rate'); // 获取心率服务
    console.log('获取心率服务');
    const characteristic = await service.getCharacteristic('heart_rate_measurement'); // 获取心率测量 Characteristic
    console.log('获取心率测量 Characteristic');
    await startHeartRateNotifications(characteristic); // 开始监听心率数据
  } catch (error) {
    console.error('获取心率服务或 Characteristic 失败:', error);
  }
}

3. 监听心率数据

最后,我们需要监听心率测量 Characteristic 的值,并在值发生变化时更新 UI。

async function startHeartRateNotifications(characteristic) {
  try {
    await characteristic.startNotifications(); // 开启监听
    console.log('开始监听心率数据');

    characteristic.addEventListener('characteristicvaluechanged', (event) => {
      const heartRate = event.target.value.getUint8(1); // 从数据中提取心率值 (心率值的格式需要参考具体的蓝牙设备文档)
      console.log('心率:', heartRate);
      // 在页面上显示心率值
      document.getElementById('heartRate').innerText = heartRate;
    });
  } catch (error) {
    console.error('开启监听失败:', error);
  }
}

完整代码示例

<!DOCTYPE html>
<html>
<head>
  <title>Web Bluetooth 心率示例</title>
</head>
<body>
  <h1>心率: <span id="heartRate">--</span> BPM</h1>
  <button onclick="connectDevice()">连接设备</button>

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

        console.log('连接设备:', device.name);
        const server = await device.gatt.connect();
        console.log('连接 GATT server');
        await getHeartRateData(server);
      } catch (error) {
        console.error('连接设备失败:', error);
      }
    }

    async function getHeartRateData(server) {
      try {
        const service = await server.getPrimaryService('heart_rate');
        console.log('获取心率服务');
        const characteristic = await service.getCharacteristic('heart_rate_measurement');
        console.log('获取心率测量 Characteristic');
        await startHeartRateNotifications(characteristic);
      } catch (error) {
        console.error('获取心率服务或 Characteristic 失败:', error);
      }
    }

    async function startHeartRateNotifications(characteristic) {
      try {
        await characteristic.startNotifications();
        console.log('开始监听心率数据');

        characteristic.addEventListener('characteristicvaluechanged', (event) => {
          const heartRate = event.target.value.getUint8(1);
          console.log('心率:', heartRate);
          document.getElementById('heartRate').innerText = heartRate;
        });
      } catch (error) {
        console.error('开启监听失败:', error);
      }
    }
  </script>
</body>
</html>

注意事项:

  • 这个例子只是一个简单的演示,实际应用中需要处理各种错误情况,比如设备断开连接、权限被拒绝等等。
  • 心率数据的格式可能因设备而异,你需要参考具体的蓝牙设备文档来正确解析数据。
  • 需要在支持 Web Bluetooth 的浏览器上运行,并且需要用户授权才能访问蓝牙设备。

踩坑指南:Web Bluetooth 的那些坑

Web Bluetooth 虽然强大,但也有些坑需要注意:

  • 权限问题: 用户必须明确授权才能访问蓝牙设备。如果用户拒绝授权,你的代码就无法工作。
  • 浏览器兼容性: 不是所有浏览器都支持 Web Bluetooth。目前 Chrome 系的浏览器支持最好。
  • 设备兼容性: 不是所有蓝牙设备都支持 Web Bluetooth。你需要确认你的设备是否支持,并且了解它的 GATT Profile。
  • 数据格式: 不同的蓝牙设备使用不同的数据格式。你需要仔细阅读设备的文档,才能正确解析数据。
  • 安全性: Web Bluetooth 涉及到用户的隐私数据,你需要采取措施保护用户的数据安全。

总结

Web Bluetooth 为 Web 应用打开了一扇新的大门,让我们可以直接与蓝牙设备交互。理解 GATT Profile、Characteristic 和 Properties 是使用 Web Bluetooth 的关键。希望今天的讲解能帮助你入门 Web Bluetooth,开启你的蓝牙之旅!

最后,记住一点:实践是检验真理的唯一标准。多写代码,多踩坑,才能真正掌握 Web Bluetooth。

今天的讲座就到这里,谢谢大家!

发表回复

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