找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3726

积分

1

好友

513

主题
发表于 4 天前 | 查看: 17| 回复: 0

大家周二早。如果你用过智能家居设备或者运动手环,是不是对“扫码下载App,打开蓝牙连接”的流程感到一丝厌烦?作为程序员,你可能会想:为什么不能直接在浏览器里打开一个URL搞定?

其实,完全可以。这得益于 Web Bluetooth API,它允许浏览器直接与低功耗蓝牙(BLE)设备进行交互。如今,共享单车开锁、乐心编程积木,甚至是某些医疗设备,都在尝试使用 PWA 结合蓝牙技术来替代笨重的原生 App。

今天,我们就用原生 JavaScript,连接一个标准的心率广播设备(市面上大部分手环、手表都支持),将实时心跳数据显示在网页上。

01. 核心概念:GATT 协议栈

蓝牙开发有一套自己的“术语”,别被它吓到,本质上和我们调用API接口差不多。

我们可以把一个蓝牙设备想象成一栋“大楼”:

  1. GATT Server:整栋大楼,也就是设备本身。
  2. Service (服务):大楼里的某一层。例如,设备可能提供“心率服务层”、“电池服务层”等。
  3. Characteristic (特征值):楼层里的某个房间。例如,在“心率服务层”里,有“心率数值房间”、“传感器位置房间”等。

我们的工作流程可以简化为:找到大楼 -> 坐电梯去“心率服务”那一层 -> 敲开“心率数值”那个房间的门 -> 接收并解析房间(特征值)里传出来的数据。

02. 实战:连接与授权

请注意,出于安全和隐私考虑,Web Bluetooth API 的使用有两个硬性前提:

  1. 网站必须运行在 HTTPS 协议下(localhost 本地开发环境除外)。
  2. 必须由用户的手势操作(例如点击按钮)来触发连接流程,代码不能自动扫描。

接下来,我们通过一个按钮点击事件来启动连接流程。代码如下:

async function connectBluetooth() {
  try {
    // 1. 🔍 扫描并请求设备
    // 我们通过指定服务UUID来筛选,这里只请求标准的“心率服务”(UUID: 0x180D)
    const device = await navigator.bluetooth.requestDevice({
      filters: [{ services: ['heart_rate'] }],
      // 或者也可以接受所有设备(不推荐,用户体验较差)
      // acceptAllDevices: true,
      // optionalServices: ['heart_rate']
    });

    console.log('连接到设备:', device.name);

    // 2. 🤝 连接设备的 GATT 服务器
    const server = await device.gatt.connect();

    // 3. 🏢 找到“心率服务”层 (UUID: 0x180D)
    const service = await server.getPrimaryService('heart_rate');

    // 4. 🚪 找到“心率测量”特征值房间 (UUID: 0x2A37)
    const characteristic = await service.getCharacteristic('heart_rate_measurement');

    // 5. 👂 开始监听该特征值的通知(Notify)
    await characteristic.startNotifications();

    // 绑定数据变更的回调函数
    characteristic.addEventListener('characteristicvaluechanged', handleHeartRateChange);

    console.log('开始监听心率...');
  } catch (error) {
    console.error('连接失败:', error);
  }
}

03. 难点:解析二进制数据 (DataView)

蓝牙设备传回的不是我们熟悉的 JSON 或字符串,而是原始的 ArrayBuffer(二进制数据流)

我们需要对照 Bluetooth SIG 官方标准文档 中定义的格式,使用位操作来解码。心率数据(特征值 UUID 0x2A37)的格式大致如下:

  • 第 1 个字节 (Flags - 标志位)
    • 第 0 位:心率值格式标志(0 表示心率值为 8 位,1 表示心率值为 16 位)。
  • 后续字节:根据标志位,从第 2 个字节开始,存储着实际的心率数值。

解码函数 handleHeartRateChange 的实现如下:

function handleHeartRateChange(event) {
  const value = event.target.value; // 这是一个 DataView 对象

  // 1. 读取第一个字节 (Flags)
  const flags = value.getUint8(0);

  // 2. 判断第 0 位 (心率值格式位)
  // 如果为 1,说明心率值是 16 位的 (占2个字节)
  // 如果为 0,说明心率值是 8 位的 (占1个字节)
  const rate16Bits = flags & 0x1;

  let heartRate;
  if (rate16Bits) {
    // 读取一个 16 位的无符号整数 (小端字节序)
    heartRate = value.getUint16(1, true);
  } else {
    // 读取一个 8 位的无符号整数
    heartRate = value.getUint8(1);
  }

  console.log(`当前心率: ${heartRate} BPM`);
  updateUI(heartRate); // 更新页面UI的函数
}

04. 写入数据:控制设备

除了“读”,我们自然也能“写”。例如,控制一个智能灯泡的颜色。其思路与读取类似,只是最后调用的是 writeValue 方法。

// 假设我们已经连接并获取了控制颜色的特征值
const colorChar = await service.getCharacteristic('light_color');

// 准备要写入的数据:红色 (RGB: 255, 0, 0)
const data = new Uint8Array([255, 0, 0]);
await colorChar.writeValue(data);

05. 2026 兼容性现状

一项技术的实用性离不开浏览器支持。截至目前,Web Bluetooth API 的兼容性情况如下:

  • Chrome / Edge (桌面版) / Android WebView:提供完整且稳定的支持。
  • iOS Safari老大难问题。直到2026年,Apple 出于其严格的隐私保护策略(实质上也是为了维护 App Store 生态),仍未完全开放 Web Bluetooth API 的支持。
    • 替代方案:iOS 用户需要使用如 BluefyWebBLE 这类专门支持 Web Bluetooth 的第三方浏览器 App 来访问你的网页。另一种思路是将网页打包为 PWA 应用时进行“套壳”处理。

结语

Web Bluetooth 是迈向 Web of Things (WoT) 的重要基石。它极大地拓展了前端工程师的能力边界,让我们编写的代码能够与物理世界进行交互。

想象一下未来的场景:在医院,你无需排队领取纸质报告,只需用手机贴近医疗设备,健康数据便能通过网页同步展示;在健身房,扫描跑步机上的二维码,你的实时配速和心率就直接呈现在浏览器页面中。这些并非遥不可及的幻想,而是正在发生的技术演进。如果你对这类连接物理世界的 前端 开发感兴趣,可以在 云栈社区前端 & 移动 等板块找到更多相关资源和深度讨论,探索更多关于 JavaScript 和物联网结合的实战技巧。




上一篇:Linux免费又好用,为何大多数人还是离不开Windows?
下一篇:Linux发行版选择指南:初学者入门、桌面环境与热门版本推荐
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-23 10:26 , Processed in 0.597346 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表