arrow-righthamburgerlogo-marksocial-facebooksocial-githubsocial-twitter
2018.01.12

Nefry BT(ESP32)からBLEでNode.jsにデータを送ってみよう

のびすけ

Nefry BT
   このエントリーをはてなブックマークに追加  

こんにちは、代表ののびすけ(@n0bisuke)です。Nefry BTを使ってBluetooth / BLEを利用する方法を紹介します。

ほぼESP32のコードなので、検証してませんが他のESP32系のボードでも動作すると思います。

BLEについて

BLEには大きく分けPeripheral (ペリフェラル)Central(セントラル)という二つの役割があります。

Nefry BTは書き込むコードによって、PeripheralにもCentralにもなることができます。

環境

Peripheralの作成

情報発信側のPeripheralをNefry BTで作成します。

UUIDの作成

BLEを利用するためにはSERVICE(サービス)CHARACTERISTIC(キャラクタリスティック)という各機能を司るUUIDを設定する必要があります。プログラミングでいうクラスとメソッドの関係だと思うと良いかもしれません。

BLEデバイスは部屋の中やカフェ、駅などいたるところに存在するのでそれらのデバイスと自分が所持しているデバイスのIDが競合しないようにユニークな値にする必要があります。そこでUUIDを作成し、設定する必要があります。

https://www.uuidgenerator.net/

このサイトでUUIDが作成出きるので各自作成しましょう。

Nefry BTのスケッチ

大元のコードはnkolban氏のこちらのコードです。

#define SERVICE_UUIDの箇所と#define CHARACTERISTIC_UUIDの箇所に先ほど作成したUUIDを指定します。

またBLEDevice::init("");の箇所にBLEデバイスの名前を設定できます。 以下のコードではNefryBT-n0bisukeという名前を指定しています。

#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-n0bisuke");

  // 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[10];
    sprintf(buffer, "{\"val\":%d}", value);
    Serial.printf(buffer);
    pCharacteristic->setValue(buffer);
    pCharacteristic->notify();
    //pCharacteristic->indicate();
    value++;
  }
  delay(2000);
}

コンパイルエラーが出ないで書き込みが出来ればOKです。

確認

デバッグにはLightBlueなどのBLEデバッグ用のアプリケーションを利用することをお勧めします。

Nefry BTが起動すると先ほど指定したNefryBT-n0bisukeという名前でBLEデバイスが検出されます。

SERVICEのUUIDやCHARACTERISTICのUUIDも先ほど指定したものが表示されていると思います。

これで問題なく、Nefry BTから情報が発信されていることが確認出来ました。

Centralの作成

次は受信側のCentralを作成していきます。 データの確認だけであれば先ほどのLightBlueなどのアプリで確認でも良いのですが、自分のサービスに組み込む際には何かしらのプログラミング言語でアクセスできた方が都合が良いです。

nobleの利用

nobleはNode.js向けのBLEライブラリです。MacやWindows、Raspberry PiなどのデバイスをBLEのCentralにすることができます。

mkdir ble_central
cd ble_central
touch app.js
npm init -y
npm i --save noble

これで準備とnobleのインストールが完了しました。

app.jsに以下を記述します。SERVICE_UUIDやCHARACTERISTIC_UUIDは自分で作成したNefry BT側に書き込んだUUIDと同様のものを指定しましょう。

[WIP] Async/Awaitに書き換えたい。

'use strict';

const noble = require('noble');
const serviceuuid = `d5875408fa514763a75d7d33cecebc31`;
const charauuid = `a4f01d8ca03743b690501876a8c23584`;

//キャラクタリスティックにアクセスしてデータやりとり
const accessChara = (chara) => {
    console.log('-----Start GATT Access-----')
    chara.notify(true, (err) => {
        if (err) {
          console.log('listen notif error', err)
        } else {
          console.log('listen notif')
        }
    });
    chara.on('data', (data, isNotif) => {
        const jsonStr = data.toString('utf-8');
        const jsonData = JSON.parse(jsonStr);
        console.log(jsonData);
    });
}


//discovered BLE device
const discovered = (peripheral) => {
    console.log(`BLE Device Found: ${peripheral.advertisement.localName}(${peripheral.uuid}) RSSI${peripheral.rssi}`);

    if(peripheral.advertisement.localName === 'NefryBT-n0bisuke'){
        noble.stopScanning();
        console.log('device found');
        console.log(`service discover...`);

        peripheral.connect(error => {
            if (error) {
                console.log("connection error:", error)
            } else {
                console.log("device connected");
            }

            peripheral.discoverServices([],(err, services) => {
                if (error) {
                    console.log("discover service error", error)
                }
                console.log('discover service');               
                services.forEach(service => {
                    if(service.uuid === serviceuuid){
                        service.discoverCharacteristics([], (error, charas) => {
                            console.log('discover chara');
                            charas.forEach(chara => {
                                if(chara.uuid === charauuid){
                                    console.log("found chara: ", chara.uuid)
                                    accessChara(chara);
                                }
                            });
                        });
                    }
                });
            });
        });
    }
}

//BLE scan start
const scanStart = () => {
    noble.startScanning();
    noble.on('discover', discovered);
}

if(noble.state === 'poweredOn'){
    scanStart();
}else{
    noble.on('stateChange', scanStart);
}

実行

node app.js

で実行します。この時、先ほどのLightBlueなどのアプリでNefryBTにBLEアクセスしていると上手くいかないのでアプリ側の接続は解除しましょう。

Mac側のNode.jsのログはこんな感じで表示されます。

BLE Device Found: LED(59aa15c3a3274ed7b11d334b5c0d0900) RSSI-68
BLE Device Found: NefryBT-n0bisuke(d0b77d4611f54380b8b63e6d05765ad6) RSSI-49
device found
service discover...
device connected
discover service
discover chara
found chara:  a4f01d8ca03743b690501876a8c23584
-----Start GATT Access-----
listen notif
{ val: 147 }
{ val: 148 }
・
・
・

解説

NefryBT側ではデバイスにアクセスがありCentralとのコネクションが確立すると、変数valueの値をセット(pCharacteristic->setValue)して送信(pCharacteristic->notify)し、valueの値をインクリメントします。

これを2秒ごとに行うので2秒間隔でNefryBTからMacのNode.jsに情報が送信されます。

・
(省略)
・
・
void loop() {

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

Node.js(noble)側では、PERIPHERAL -> SERVICE -> CHARACTERISTICと階層的にアクセスしていき、CHARACTERISTICまでアクセスが出きると、accessChara関数が呼ばれます。

この中のchara.on('data')の箇所でデータが送られてくるたびにイベントが発火して、データの中身を確認できます。

・
・
(省略)
・
const accessChara = (chara) => {
    console.log('-----Start GATT Access-----')
    chara.notify(true, (err) => {
        if (err) {
          console.log('listen notif error', err)
        } else {
          console.log('listen notif')
        }
    });
    chara.on('data', (data, isNotif) => {
        const jsonStr = data.toString('utf-8');
        const jsonData = JSON.parse(jsonStr);
        console.log(jsonData);
    });
}
・
・
(省略)
・

まとめ

Nefry BTでBLEを利用する方法を紹介しました。 Nefry BTでセンサーのデータを取得し、Centralに送信する方法なども応用して作れそうですね。

これを参考にNefry BTとBLEデバイスの連携などに活用していきましょう。

今回はNefry BTをPeripheralにする実装でしたが、別の機会でCentralにする方法も紹介できればと思っています。

それでは!

所感

今回の実装ですが色々と途中でのハマりが多いかつ、調べてもまだまだESP32のBLE利用をArduinoでやってる事例は少なくけっこう大変でした。この辺の大変だった知見はQiitaなどでまとめらたらと思っています。お疲れ様です笑

   このエントリーをはてなブックマークに追加