Nefry BTからブラウザにWeb Bluetoothでデータを表示する方法

[IoTLT Advent Calendar 2018](https://qiita.com/advent-calendar/2018/iotlt_neo)の10日目担当の蔵下です。普段はIoTな会社でWeb系の開発を行っています。

のびすけさんの記事「[Nefry BT（ESP32）からBLEでNode.jsにデータを送ってみよう](https://dotstud.io/blog/nefrybt-ble-bluetooth-peripheral/)」で、Nefry BTもいい感じにBLE使えるやん！:clap: ということで、 `Nefry BT ↔ ブラウザ` でデータを表示する方法をまとめてみました。

![web.gif](https://qiita-image-store.s3.amazonaws.com/0/55103/7c9395d8-eade-8e19-2009-5a9aa043b28e.gif)

▲クリスマスっぽさを少しだけ:christmas_tree:

# Nefry BT（BLEデバイス）とブラウザの通信はWeb Bluetoothでできる

Nefry BTとブラウザでのデータ通信は `Web Bluetooth` で実現できます。普段の仕事でもゴニョゴニョ使っているので、詳細は他記事でまとめていますので良かったら見てみてください。

- [Web Bluetoothを実戦投入すると意外と使えたメモ](https://qiita.com/umi_kappa/items/da60e3c8cec19624df91)
- [Web Bluetooth(Read/Write/Notifications)の使い方まとめ](https://qiita.com/umi_kappa/items/45761c7454d5d4c10bf0)

Web Bluetoothは、現在（2018/12/10）Windows、Mac、AndroidのChromeで使えます（詳細は[こちら](https://qiita.com/umi_kappa/items/45761c7454d5d4c10bf0)）。そうです、 `スマホでも使えます！` IoTなデモ作る幅も広がると思うので、ぜひ触ってみてください。

https://dotstud.io/blog/nefrybt-ble-bluetooth-peripheral/

# ソースコード

今回のデモはのびすけさんの記事「[Nefry BT（ESP32）からBLEでNode.jsにデータを送ってみよう](https://dotstud.io/blog/nefrybt-ble-bluetooth-peripheral/)」をベースに一部変更を加えながら進めました（ありがとうございます！）。Nefry BTは初期のR1を使っています。

## スケッチ（Nefry BT側のコード）

```c#:sketch.ino
#include <Nefry.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>

BLECharacteristic *pCharacteristic;
bool deviceConnected = false;
uint8_t value = 0;

// See the following for generating UUIDs:
// https://www.uuidgenerator.net/

#define SERVICE_UUID        "d5875408-fa51-4763-a75d-7d33cecebc31"
#define CHARACTERISTIC_UUID "a4f01d8c-a037-43b6-9050-1876a8c23584"

class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
      deviceConnected = true;
    };

    void onDisconnect(BLEServer* pServer) {
      deviceConnected = false;
    }
};

void setup() {
  Serial.begin(115200);

  // Create the BLE Device
  BLEDevice::init("NefryBT");

  // Create the BLE Server
  BLEServer *pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());

  // Create the BLE Service
  BLEService *pService = pServer->createService(SERVICE_UUID);

  // Create a BLE Characteristic
  pCharacteristic = pService->createCharacteristic(
                      CHARACTERISTIC_UUID,
                      BLECharacteristic::PROPERTY_READ   |
                      BLECharacteristic::PROPERTY_WRITE  |
                      BLECharacteristic::PROPERTY_NOTIFY |
                      BLECharacteristic::PROPERTY_INDICATE
                    );

  // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
  // Create a BLE Descriptor
  pCharacteristic->addDescriptor(new BLE2902());

  // Start the service
  pService->start();

  // Start advertising
  pServer->getAdvertising()->start();
  Serial.println("Waiting a client connection to notify...");
}

void loop() {

  if (deviceConnected) {
    Serial.printf("*** NOTIFY: %d ***\n", value);
    char buffer[32];
    sprintf(buffer, "{\"val\":%d}", value);
    Serial.printf(buffer);
    pCharacteristic->setValue(buffer);
    pCharacteristic->notify();
    //pCharacteristic->indicate();
    value++;
  }
  delay(2000);
}
```

## JavaScript（ブラウザ側のコード）

```js:main.js
const SERVICE_UUID = 'd5875408-fa51-4763-a75d-7d33cecebc31'
const CHARACTERISTIC_UUID = 'a4f01d8c-a037-43b6-9050-1876a8c23584'

const btn = document.getElementById('btn')
const text = document.getElementById('text')

// Web Bluetoothはユーザーアクションをトリガーに処理を始める必要がある
btn.addEventListener('click', (event) => {
  connect()
})

const connect = () => {
  // Scan
  navigator.bluetooth.requestDevice({
    // 'Nefry'というデバイス名でフィルタリング
    acceptAllDevices: false,
    filters: [
      {namePrefix: 'Nefry'}
    ],
    optionalServices: [
      // 使用したいServiceを登録しておく
      SERVICE_UUID
    ]
  })
    // 接続
    .then(device => device.gatt.connect())
    // Service取得
    .then(server => server.getPrimaryService(SERVICE_UUID))
    // Characteristic取得
    .then(service => service.getCharacteristic(CHARACTERISTIC_UUID))
    // Notificationsを開始
    .then(characteristic => setNotifications(characteristic))
    // Errorはこちら
    .catch(error => console.log(error))
}

// Notification設定
const setNotifications = (characteristic) => {

  // Add Event
  characteristic.addEventListener('characteristicvaluechanged', (event) => {
    const value = event.target.value

    // データをパース
    const decoder = new TextDecoder('utf-8')
    const str = decoder.decode(value)
    const json = JSON.parse(str)
    // Nefry BTからのデータを表示
    if (text) text.innerHTML = json.val
  })

  // Notifications開始
  characteristic.startNotifications()
}
```

## HTML（ブラウザ側のコード）

```html:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Web Bluetooth API</title>
  <script src="main.js" defer></script>
  <!-- CSSは省略 -->
</head>
<body>
<button id="btn">Connect</button>
<div id="text"></div>
</body>
</html>
```

# まとめ

特に面白みのないネタになってしまいました(笑)。ですが、みなさんの面白いIoT作品の新たな選択肢になればいいなと思いまとめてみました。最近IoTLT参加できていなかったので、来年からまたちょこちょこ参加したいと思います！ 良いお年を～