在浏览器中使用 WebBluetooth 操控无人机

我们周围有大量的设备,并且数量还在不断增长。越来越多的设备配备了连接功能。从 行李箱植物 再到 鸡蛋。这带来了新的挑战:我们如何发现我们周围的设备,以及如何与它们交互?

目前,设备交互由在移动电话上运行的独立应用程序处理。但这并不能解决可发现性问题。我需要知道我周围有哪些设备,然后才知道应该安装哪个应用程序。当我站在一个 会议室 前面时,我不关心要安装哪个应用程序,甚至不关心会议室的名称或 ID。我只想尽快进行预订或查看可用性。

蓝牙

来自 Google 的 Scott Jenson 一直在思考可发现性问题,并提出了 物理网络 项目,其前提是

走到哪里,使用什么

这个想法是使用 蓝牙智能,蓝牙的低功耗变体,向世界广播 URL。您的手机接收广告包,对其进行解码,并向用户显示一些信息。单击一下,用户就会被重定向到包含相关内容的网页。这可以用于各种用途

  • 会议室可以广播指向其日历的 URL 以进行安排。
  • 电影海报可以广播一个 URL 来显示放映时间和预告片。
  • 处方药可以广播指向包含药物信息或如何续订的 URL。
  • 环顾四周。其他用例的例子随处可见,等待着被实现。

然而,物质世界不是单行道,这带来了一个问题。广播 URL 非常适合向我告知电影时间之类的事情,但它不允许我与设备进行更深入的交互。如果我想操控一个 无人机,我不仅想发现附近有一个无人机,我还想立即与无人机交互。我们需要一种方法让网页能够回传信息到设备。

于是 Web Bluetooth W3C 小组 开始了工作,该小组包括 Mozilla 的 蓝牙团队 的代表,他们正在努力将蓝牙 API 带入浏览器。如果物理网络允许我们走到任何设备前并获取 Web 应用程序的 URL,那么 WebBluetooth 允许 Web 应用程序连接到设备并与之进行通信。

此时,还有很多工作要做。蓝牙 API 只对 Firefox OS 上的 经过认证的内容 公开,因此目前无法被普通网页内容访问。直到 安全问题得到解决 之前,这种情况将会持续下去。第二个问题是,物理网络信标广播一个 URL。特定的 Web 资源如何知道哪个特定的设备广播了 URL?

如您所见,还有很多工作要做,但这个博客之所以叫 Mozilla Hacks 是有原因的。让我们开始黑客攻击吧!

在 Firefox OS 中添加物理网络支持

由于围绕 WebBluetooth 的大部分工作都是针对 Firefox OS 完成的,所以我选择它作为我的武器。我希望设备发现的过程尽可能地轻松直观。我认为锁屏将是最佳位置。只要您在 Firefox OS 手机上启用了蓝牙,就会弹出一个新的通知,询问您是否要搜索设备 (跟踪错误)。

Tap, tap, tap

navigator.mozBluetooth.defaultAdapter.startLeScan([]).then(handle => {
  handle.ondevicefound = e => {
    console.log('Found', e.device, e.scanRecord);
  };

  setTimeout(() => {
    navigator.mozBluetooth.defaultAdapter.stopLeScan(handle)
  }, 5000);
}, err => console.error(err));

如您在第三行中所见,我们有一个scanRecord。这是设备广播的广告包。它只是一组字节,您可以自由地声明自己的协议。为了实现我们的目的——通过蓝牙广播 URL——Google 已经开发出两种编码方式:UriBeaconEddyStone,这两种方式如今都可以在现实世界中找到。

解析广告包非常简单。以下是我编写的用于 解析 UriBeacon 的代码。解析 UriBeacon 将为您提供一个 URL,该 URL 通常被缩短,因为广告包中的字节有限——这会导致不直观的 UI

So what the hack (pun intended) is this device?

为了获取有关信标背后的网页的一些信息,我们可以执行一个 AJAX 请求并解析网页的内容以增强锁屏上显示的信息

function resolveURI(uri, ele) {
var x = new XMLHttpRequest({ mozSystem: true });
x.onload = e => {
  var h = document.createElement('html');
  h.innerHTML = x.responseText;

  // After following 301/302s, this contains the last resolved URL
  console.log('url is', x.responseURL);

  var titleEl = h.querySelector('title');
  var metaEl = h.querySelector('meta[name="description"]');
  var bodyEl = h.querySelector('body');

  if (titleEl && titleEl.textContent) {
    console.log('title is', titleEl.textContent);
  }

  if (metaEl && metaEl.content) {
    console.log('description is', metaEl.content);
  }
  else if (bodyEl && bodyEl.textContent) {
    console.log('description is', bodyEl.textContent);
  }
};
x.onerror = err => console.error('Loading', uri, 'failed', err);
x.open('GET', uri);
x.send();
};

这将生成一个更友好的通知,实际上描述了信标。

Much nicer

不广播 URL 的无人机

不幸的是,并非所有 BLE 设备都广播 URL。所有这些新技术都是实验性的,而且非常酷,但尚未完全实施。我们对这种情况在不久的将来发生改变抱有很大希望。因为我仍然想现在操控我的无人机,所以我添加了一些代码,将无人机广播的数据 转换为 URL

Web 应用程序

现在我们已经解决了可发现性问题,我们需要一种方法从浏览器控制无人机。由于 Web 内容无法访问蓝牙,我们需要对 Gecko 进行一些更改,Gecko 是 Firefox OS 安全模型的实现所在。如果您对更改感兴趣,请查看提交。我们还需要一个 偷偷摸摸的黑客手段 来确保标签的进程以正确的 Linux 权限运行。

完成这些更改后,我们将navigator.mozBluetooth 对所有内容开放,并在 Firefox 中以一个属于“bluetooth”Linux 组的进程运行每个标签,确保可以访问硬件。如果您稍后玩弄此构建,请注意,在我的“偷偷摸摸的”黑客手段实现后,您现在运行的构建不再保证安全性。使用这种禁用安全性的构建黑客手段对于物联网实验来说是可以的,但绝对不建议作为生产解决方案。当 Web Bluetooth 规范最终确定,并且官方支持在 Gecko 中发布时,将实施适当的安全性 进行实施

有了 API,我们可以开始编写应用程序。当您点击锁屏上的物理网络通知时,我们传递设备地址作为参数。这可能会发生变化。有关正在进行的讨论,请查看 Eddystone -> Web Bluetooth 交接

var address = 'aa:bb:cc:dd:ee'; // parsed from URL
var counter = 0;
navigator.mozBluetooth.defaultAdapter.startLeScan([]).then(handle => {
  handle.ondevicefound = e => {
    if (e.device.address !== address) return;

    navigator.mozBluetooth.defaultAdapter.stopLeScan(handle);

    // write some code to fly the drone
  };
}, err => console.error(err));

现在我们有了对设备地址的引用,可以建立连接。我们用来与设备相互通信的协议称为 GATT,即通用属性配置文件。GATT 背后的理念是,设备可以拥有多个标准服务。例如,心率传感器可以实现电池服务和心率服务。由于这些服务是标准化的,因此使用应用程序只需要编写一次实现逻辑,就可以与任何心率监测器进行通信。

特征是给定服务的一部分。例如,心率服务将实现 心率测量心率最大值。特征可以是可读的和可写的,具体取决于它们的定义方式。无人机也是如此。它有一个用于操控无人机的服务,以及用于让您从手机控制无人机的特征。

幸运的是,Martin Dlouhý(据我所知,他是第一个)已经对 Rolling Spider 无人机的通信协议进行了解码,因此我们可以使用他的工作和新的蓝牙 API 开始操控……

// Have a way of knowing when the connection drops
e.device.gatt.onconnectionstatechanged = cse => {
  console.log('connectionStateChanged', cse);
};
// Receive events (battery change f.e.) from device
e.device.gatt.oncharacteristicchanged = cce => {
  console.log('characteristicChanged', cce);
};

// Set up the connection
e.device.gatt.connect().then(() => {
  return e.device.gatt.discoverServices();
}).then(() => {
  // devices have services, and services have characteristics
  var services = e.device.gatt.services;
  console.log('services', services);

  // find the characteristic that handles flying the drone
  var c = services.reduce((curr, f) => curr.concat(f.characteristics), [])
    .filter(c => c.uuid === '9a66fa0b-0800-9191-11e4-012d1540cb8e')[0];

  // take off instruction!
  var buffer = new Uint8Array(0x04, counter++, 0x02, 0x00, 0x01, 0x00]);
  c.writeValue(buffer).then(() => {
    console.log('take off successful!');
  });
});

台北的 Mozilla 团队使用它为 Firefox OS 创建了一个 演示应用程序,在去年 6 月的惠斯勒 Mozilla 工作周期间,演示了新 API 的功能。现在 API 在浏览器中可用,我们可以使用这项工作,将其托管为一个网页,稍微加强一下图形,然后就拥有一个网站来操控无人机了!

Such amaze

太棒了,无人机真多。

https://www.youtube.com/watch?t=33&v=yILD_ZdXJW4

结论

网络的时代真是激动人心!随着越来越多的设备接入互联网,我们需要一种方法来发现和交互这些设备,而不会遇到太多麻烦。物理网络和 WebBluetooth 的结合,使我们能够为愿意与现实世界中的设备交互的用户创造无缝体验。虽然我们还有很长的路要走,但我们正朝着正确的方向前进。Google 和 Mozilla 正在积极开发这项技术;我非常希望这篇博文中的所有内容都将在一年内成为常识!

如果你觉得这还不够快,你可以尝试使用 Firefox OS 的实验版本,它支持这篇博文中提到的所有功能。这个版本可以在 Flame 开发者设备上运行。首先,升级到 nightly_v3 基本镜像,然后刷入 这个版本

致谢

感谢 Tzu-Lin HuangSean Lee 开发了最初的无人机代码;感谢 Mozilla 台北的 WebBluetooth 团队(尤其是 Jocelyn Liu)在我抱怨 API 时提供的快速反馈和补丁;感谢 Chris Williams 将无人机放入了我的 JSConf.us 礼品袋中;感谢 Scott Jenson 回答了我关于物理网络的无数问题;感谢 Telenor Digital 让我玩了两周的无人机。

关于 Jan Jongboom

Jan Jongboom 是 Telenor Digital 的战略工程师,从事物联网工作。

更多 Jan Jongboom 的文章...


3 条评论

  1. Mindaugas J.

    作为一个附加组件开发者,每当我看到 innerHTML 被使用时,都会有一种条件反射,让我“坐直”。

    这里面的安全模型是什么?
    > var h = document.createElement(‘html’);
    > h.innerHTML = x.responseText;
    这是在特权上下文中创建的吗?

    你肯定不希望随机的无线热点入侵你的手机。

    2015 年 8 月 20 日 凌晨 2:00

    1. Jan Jongboom

      Google 有一个物理网络代理,它可以从网站读取这些数据,所以如果我们要将它投入生产,我们会使用类似的东西。

      据我所知,在将元素添加到 DOM 之前,它不会执行任何脚本,但小心驶得万年船。

      2015 年 8 月 28 日 凌晨 5:36

  2. matt holevinski

    听起来像一个新的攻击向量,会让我日益增长的漏洞更加脆弱。

    2015 年 8 月 30 日 凌晨 7:55

这篇文章的评论已关闭。