利用Chrome Web蓝牙API构建蓝牙应用

3,705 阅读8分钟

如今,浏览器在不断发展,带来了新的API和连接其他设备的方式,并允许访问比以前更多的功能。其中一个API就是网络蓝牙API

截至本文写作时,这个API仍处于测试阶段,但一旦向公众发布,它将为那些想使用蓝牙但又不想为每个平台创建本地应用程序的开发者带来大量的机会。

尽管蓝牙API仍处于测试阶段,我们将试用它,并制作一个简单的网页,与我们的手机配对,给我们提供基本的细节,如电池百分比、设备名称以及设备制造商提供的基本信息。

在这个教程中我们不会使用样式,因为我们只需要了解如何用JavaScript与蓝牙API交互。

记住,并不是所有的浏览器都支持这个API,你也不可能用每部手机来测试。有些手机可能不允许获取设备信息。在本教程中,我将使用苹果iPhone 11,它允许我在浏览器上通过蓝牙获取设备信息,没有任何问题。

要求

  • 一个代码编辑器;我更喜欢VS Code
  • 如果你使用的是VS Code,则需要实时服务器扩展
  • 具有蓝牙功能的笔记本电脑或PC(或即插即用的蓝牙硬件)
  • 一个有蓝牙功能的移动设备(我用的是iPhone 11,你可以用自己的手机试试)
  • 对JavaScript的工作知识
  • 在你的PC或笔记本电脑上安装Chrome Beta。蓝牙API是一个测试版功能,在Chrome Beta上运行效果最好。

请注意,并非所有基于Chromium的浏览器,如Brave,都支持蓝牙API。我尝试在Brave上使用该API,但发现Brave出于安全考虑,故意禁用了该API。

如果你对代码需要任何帮助,这里是GitHub仓库

让我们开始吧

首先,我们需要创建一个文件夹,我们将把它作为工作区。一旦你创建了一个文件夹,使用以下命令打开VS Code。

code .

在本教程中,我们将使用两个文件;命名为index.htmlscript.js 。在index.html 中,我们只需要基本的布局(只有一个按钮),并将该文件链接到我们的JavaScript文件。

下面是index.html 的内容。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <button id="getDetails">Get device details</button>
    <div id="details"></div>
    <script src="script.js"></script>
  </body>
</html>

添加蓝牙功能

让我们从功能开始。转到script.js ,将UI元素存储在变量中,以便我们以后可以访问它们。

const button = document.getElementById("getDetails");
const details = document.getElementById("details");

现在,让我们为我们的按钮创建一个click 监听器,这样一旦用户点击了按钮,我们就可以执行我们的操作。

button.addEventListener("click", async () => {
  try {
  } catch(err) {
    console.error(err);
    alert("An error occured while fetching device details");
  }
});

我们把这个函数做成了async ,因为它让我们的事情更简单,我们不需要做很多回调,使我们的代码看起来更有条理。从现在开始,我们所有的代码都将在try 块内。

请求一个蓝牙设备

接下来,让我们通过浏览器来请求一个蓝牙设备。

// Request the Bluetooth device through browser
const device = await navigator.bluetooth.requestDevice({
  optionalServices: ["battery_service", "device_information"],
  acceptAllDevices: true,
});

在上面的代码中,我们通过navigator.bluetooth ,使用了蓝牙API。在连接到一个设备之前,我们需要向设备提供我们要访问的数据信息。

我们可以使用目标蓝牙设备上的各种服务来访问所需数据。在这种情况下,我们要与电池和设备信息进行交互,所以我们需要battery_servicedevice_information 服务。

一旦用户选择了他想连接的蓝牙设备,我们就会建立一个与GATT服务器的连接,该服务器为我们提供了访问我们先前请求的服务的机会,并且还将设备名称存储在一个变量中,供以后使用。

// Connect to the GATT server
// We also get the name of the Bluetooth device here
let deviceName = device.gatt.device.name;
const server = await device.gatt.connect(); 

现在,我们需要从GATT服务器上单独获取服务,以便我们可以单独访问它们。

// Getting the services we mentioned before through GATT server
const batteryService = await server.getPrimaryService("battery_service");
const infoService = await server.getPrimaryService("device_information");

从设备中获取信息

首先,让我们努力获取目标设备的电池水平。

每个蓝牙设备都有各种服务可以互动。例如,一个移动设备可以有一个电池服务,用于所有的电池活动。也可以有一个电话服务,帮助拨打和接收电话。不同的设备都有不同的蓝牙服务。

每个服务都有特征,而每个特征都有一个值。这个值是一个缓冲区,所以我们需要把它转换成人类可读的形式。

电池电量是一个百分比,所以我们将把缓冲区转换为整数。

// Getting the current battery level
const batteryLevelCharacteristic = await batteryService.getCharacteristic(
  "battery_level"
);
// Convert recieved buffer to number
const batteryLevel = await batteryLevelCharacteristic.readValue();
const batteryPercent = await batteryLevel.getUint8(0);

readValue() 函数返回给我们一个缓冲区,我们需要将其转换为人类可读的形式。

现在,让我们努力获取更多的设备信息。如前所述,每个服务都有一个或多个特征。device_information 服务可能有相当多的特征,这取决于设备,我们不能提取一个具体的特征,因为每个设备都有不同的配置和不同的唯一ID来访问数据。所以我们在这种情况下简单地读取所有的特征。

下面的代码就是这样做的。

// Getting device information
// We will get all characteristics from device_information
const infoCharacteristics = await infoService.getCharacteristics();
console.log(infoCharacteristics);
let infoValues = [];
const promise = new Promise((resolve, reject) => {
  infoCharacteristics.forEach(async (characteristic, index, array) => {
    // Returns a buffer
    const value = await characteristic.readValue();
    console.log(new TextDecoder().decode(value));
    // Convert the buffer to string
    infoValues.push(new TextDecoder().decode(value));
    if (index === array.length - 1) resolve();
  });
});

我们将forEach 包装在一个Promise下,因为父类和forEach 本身是一个异步函数,所以我们需要在继续显示数据之前获取数据。

在这里,当我们使用readValue() 获取数值时,我们使用TextDecoder ,因为我们知道device_information 服务中的大部分数据是字符串类型,而不是整数。

然后,我们将所有的数据推送到一个数组中,这样我们就可以在用户界面上进行渲染,一旦所有的特征被读取,就可以解析Promise。

现在,我们只需将数据渲染到屏幕上。

promise.then(() => {
  // Display all the information on the screen
  // use innerHTML
  details.innerHTML = `
    Device Name - ${deviceName}<br />
    Battery Level - ${batteryPercent}%<br />
    Device Information:
    <ul>
      ${infoValues.map((value) => `<li>${value}</li>`).join("")}
    </ul> 
  `;
});

现在,当你在Chrome Beta上运行我们的网络应用并点击按钮时,你应该看到一个连接蓝牙设备的提示,就像这样。

Bluetooth pairing screen

一旦你选择了你的手机(在我的例子中是Atharva的iPhone)并点击配对,你应该在几秒钟内看到屏幕上的信息,就像这样。

Screenshot of paired Bluetooth device battery level information

信息是正确的,我拍下截图时,我的手机是百分之百开着的。

这里需要注意的一点是,iPhone 12,1 并不意味着我有一个iPhone 12。iPhone 12,1 是iPhone 11的代码名称。因此,如果你看到你的设备有一些奇怪的名字,你应该知道,这可能是制造商的代号或其他东西。

你应该使用蓝牙API吗?

这是最重要的问题。这个功能对大多数浏览器来说还处于测试阶段,即使它向公众开放,也可能存在一些问题,比如硬件不支持蓝牙。如果你想创建一个服务,让别人链接他们的设备,你应该牢记这一点。

另一方面,如果你的组织有正确配置了蓝牙的定制系统,你绝对可以为组织创建一个内部网络应用,可以根据他们的需要与蓝牙设备互动。

我认为你应该在这个API处于测试阶段的时候尝试一下,因为一般来说,当它向公众发布的时候,你会占到上风。没有多少人会知道如何使用这个API,所以对它的了解可以帮助你获得更多的演出机会。

在测试阶段使用它的另一个原因是为了挑战自己。当API被发布后,事情可能会变得更容易。但如果你像我一样喜欢玩API测试版,你可能会有一些乐趣,并在这个过程中学到一些新东西。

一旦API向公众发布,就会产生更多的意识,在我看来,越来越多的蓝牙相关服务将在网络上而不是在本地应用程序中进行。这将使这项技术更容易为网络开发者所接受。

下一步是什么?

我强烈建议阅读目标设备的文档以获得帮助。不是每个设备都有相同的服务;有些设备可能有自定义ID的自定义服务。

对于实践,我建议弄清楚你还能用蓝牙API从你的手机中提取什么。

The postBuild a Bluetooth app with the Chrome Web Bluetooth APIappeared first onLogRocket Blog.