team M and Aの部屋

ゆるーい電子工作好きな夫婦のブログです。

micro:bitからBLE通信で送られたデータをWebブラウザで表示する

今更ながらBLE通信のお勉強のため、眠っていたmicro:bitを引っ張り出してきました。micro:bitとPCをBLE接続し、micro:bitからNotifyで送信されたデータをWebブラウザで取得するところまでできたので、備忘録として残します。

環境

micro:bitArduino IDEでプログラミングするための準備

以下のページを参考にしました。

sanuki-tech.net

Arduino IDEのボードマネージャーで「nRF5 boards」を追加します。この中にmicro:bitが含まれています。今回使うのはV1.5なので、「BBC micro:bit」を選択します。

 

BLEPeripheralライブラリの追加

BLE通信用に以下のライブラリを使用します。

github.com

  1. 上記リンクからzipファイルをダウンロード。
  2. Arduino IDEの[スケッチ]-[ライブラリをインクルード]-[ZIP形式のライブラリのインストール]をクリック。
  3. 1でダウンロードしたZIPファイルを選択。
  4. [ファイル]-[スケッチ例]で「BLEPeripheral」のサンプルを選択できることを確認。

 

LED表示用Screenライブラリの追加

BLEPeripheralと同様に下記のライブラリも追加します。

github.com

 

micro:bitソースコード (Arduino)

ベースのプログラムは下記を参考にさせてもらいました。

tomosoft.jp

動作はざっくり以下のようになっています。

  • Custom service (testService)を設定
  • Custom Characteristic (testReadWriteCharacteristic, testNotifyCharacteristic)を設定
  • LED表示
    • 接続前:ひし形(IconNames::Diamond)
    • 接続中:笑顔(IconNames::Happy)
    • 接続断:悲しい顔(IconNames::Sad)
  • 接続中かつボタンAを押しているとき、Notify発行

#include <bleperipheral.h>
#include <microbit_screen.h>
 
BLEPeripheral blePeripheral = BLEPeripheral();

// create service
//uuidgen
BLEService testService = BLEService("19b10010-e8f2-537e-4f6c-d104768a1214");
 
char defaultVal[] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00};
 
BLECharacteristic testReadWriteCharacteristic = BLECharacteristic("19b10011-e8f2-537e-4f6c-d104768a1214",
                                       BLERead | BLEWrite,
                                       11);
BLECharacteristic testNotifyCharacteristic = BLECharacteristic("19b10012-e8f2-537e-4f6c-d104768a1214",
                                       BLERead | BLENotify,
                                       20);
char* receivedBuf = "          ";
 
void setup() {
  Serial.begin(115200);
  SCREEN.begin();
 
  pinMode(PIN_BUTTON_A, INPUT_PULLUP);
  pinMode(PIN_BUTTON_B, INPUT_PULLUP);
 
  blePeripheral.setDeviceName("BBC micro:bit");
  blePeripheral.setLocalName("BBC micro:bit");
 
  blePeripheral.setAdvertisedServiceUuid(testService.uuid());
  testReadWriteCharacteristic.setValue(defaultVal);
 
  blePeripheral.addAttribute(testService);
  blePeripheral.addAttribute(testReadWriteCharacteristic);
  blePeripheral.addAttribute(testNotifyCharacteristic);

  blePeripheral.setConnectionInterval(0x0006, 0x0028);
 
  // BLE init
  blePeripheral.begin();
  Serial.println(F("BLE HOGE Peripheral"));
}


void loop() {
  SCREEN.showIcon(IconNames::Diamond);
 
  // Blocking wait...
  BLECentral central = blePeripheral.central();
  if (central) {
    // central connected to peripheral
    Serial.print(F("Connected to central: "));
    Serial.println(central.address());

    while (central.connected()) {
      SCREEN.showIcon(IconNames::Happy);
      if (digitalRead(PIN_BUTTON_A) == LOW) {
        unsigned char temp[20] = {};
        uint32_t time_us = micros();
        memcpy(temp, &time_us, 4);
        for(int i = 4; i < 20; i++) {
          temp[i] = i;
        }
        if(testNotifyCharacteristic.canNotify()){
          testNotifyCharacteristic.setValue(temp, 20);
          Serial.println(time_us, DEC);
        } else {
          Serial.println("wait");
        }
      }
    }
    Serial.print(F("Disconnected from central: "));
    Serial.println(central.address());
    SCREEN.showIcon(IconNames::Sad);
    delay(2000);
  }
}

Webブラウザの準備

chromeを起動し、下記をURLのところに入力する。

chrome://flags/#enable-experimental-web-platform-features

以下の画像のように、「Experimental Web Platform features」を「Enabled」に変更する。

chromeを再起動する。

htmlファイルの作成

WebブラウザからBLE接続、Notifyデータの取得をするためのhtmlファイルを作成しました。普段はまったくコーディングすることのない不慣れなhtmlとjavascriptを色んなサイトを参考にさせてもらいながら書きました。

一応、動作するようになったものが下記です。(再接続処理は未完成)


<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>BLE Test</title>
</head>

<body>
<form name="js">
  <div>
    <input type="button" value="接続" onclick="connect();"/>
    <input type="button" value="切断" onclick="disconnect();" />
  </div>
  <div>
    <p id="notify">Send</p>
    <p>
      <input type="text" name="y" value="" />
      <input type="button" value="送信" onclick="send();" />
    </p>
  </div>
  <div>
    <p id="notify">Notify</p>
    <p>
      <textarea cols="50" rows="10" id="notify_msg"></textarea>
    </p>
  </div>
</form>

<script>
var connect_try = true;

var ble_device;
var ble_server;
var gatt_service;
var send_characteristic;
var notify_characteristic;

//micro:bit BLE UUID
var TEST_SERVICE_UUID            = '19b10010-e8f2-537e-4f6c-d104768a1214';
var SEND_CHARACTERISTIC_UUID     = '19b10011-e8f2-537e-4f6c-d104768a1214';
var NOTIFY_CHARACTERISTIC_UUID   = '19b10012-e8f2-537e-4f6c-d104768a1214';

function connect() {
  navigator.bluetooth.requestDevice({
    filters: [{
      namePrefix: 'BBC micro:bit',
    }],
    optionalServices: [TEST_SERVICE_UUID]
  })
  .then(device => {
    ble_device = device;
    console.log("device", device);
    return device.gatt.connect();
  })
  .then(server =>{
    ble_server = server;
    console.log("server", ble_server)
    return ble_server.getPrimaryService(TEST_SERVICE_UUID);
  })
  .then(service => {
    gatt_service = service
    console.log("service", service)
    return gatt_service.getCharacteristic(NOTIFY_CHARACTERISTIC_UUID)
  })
  .then(chara => {
    console.log("Notify:", chara)
    notify_characteristic = chara;
    notify_characteristic.startNotifications();
    notify_characteristic.addEventListener('characteristicvaluechanged',onNotification);  
    return gatt_service.getCharacteristic(SEND_CHARACTERISTIC_UUID)
  })
  .then(chara => {
    alert("BLE接続が完了しました。");
    console.log("Send:", chara)
    send_characteristic = chara;
    // 切断時に実行される関数
    ble_device.addEventListener('gattserverdisconnected', onDisconnected);
    connect_try = true;
    return;
  })
  .catch(error => {
    alert("BLE接続に失敗しました。");
    console.log(error);
  });    
}

function onNotification(event) {
  let notify_element = document.getElementById('notify_msg');
  console.log(event.target.value);
  
  let timestamp = event.target.value.getUint32(0, littleEndian = true);
  notify_element.value = notify_element.value + timestamp + ":";

  for(let i = 4; i < event.target.value.byteLength; i++){
    let temp = event.target.value.getUint8(i);
    notify_element.value = notify_element.value + temp;
  }
  notify_element.value = notify_element.value + "\r\n";
}

function disconnect() {
  if (!ble_device || !ble_device.gatt.connected) return ;
  ble_device.gatt.disconnect();
  connect_try = false;
  alert("BLE接続を切断しました。")
}

function send() {
  temp = Uint8Array.from(document.js.y.value);
  console.log('y:' + temp);
  return (send_characteristic.writeValue(temp))
  .catch(error => {
    console.log('Error : ' + error);
    this.onError(error);
  });
}

function onDisconnected() {
  console.log('Disconnected');
  let timerId = setInterval(function() {
    if(connect_try){
      if(ble_device.gatt.connect()) {
        clearInterval(timerId);
        console.log('Connected');
      }
    } else {
      clearInterval(timerId);
    }
  }, 1000);
}
</script>    
</body>
</html>

下記が動作時の画面です。

「接続」ボタンをクリックすると、BLEデバイスを検索します。

Notifyが届くと、1行ずつ表示されます。最初の10桁の数字(:まで)はmicro:bitで付加したタイムスタンプ(us単位)です。どの程度の通信速度が出せるか調べてみたかったので、タイムスタンプ4byte+16byteの合計20byteを送信しています。

Notifyを連続送信させたところ、45msで20Byte×6回=120Byte送信できていたので、20kbpsくらい出ていた計算になります。ちょっとしたログを取るのに使えそうな感じ。