LoginSignup
5
3

More than 3 years have passed since last update.

【JavaScript 2020】 #UIFlow の BLE UART を使ったブラウザから #M5Stack_Core2 ( #M5Stack )への文字の送信

Last updated at Posted at 2020-12-20

この記事で書いている内容は、Web Bluetooth API を使ったブラウザと BLEデバイスのやりとりの話です。
「せっかくなので、どこかのアドベントカレンダーへの登録ができれば」と思い、急遽、JavaScript のアドベントカレンダーの 21日目に登録をしてみました。

少し補足をすると、M5Stackシリーズの M5Stack Core2 と、ビジュアルプログラミング環境の UIFlow を組み合わせて、最近実装された BLE UART を試した話の続きです。

●#UIFlow の BLE UART を使った文字のやりとりを #M5Stack_Core2 で試してみた( #M5Stack ) - Qiita
 https://qiita.com/youtoy/items/0aeac01927d60c33f421

上記の記事では、M5Stack Core2 と BLE通信のやりとりをしていたのは Mac用のアプリの「Bluetooth Explorer」だったのですが、この部分を Web Bluetooth API を使った JavaScript実装にしてみる、という話です。

今回の結果について書いておくと、ブラウザから M5Stack Core2 への文字列の送信は成功しました。

M5Stack側の BLE関連のプログラム

M5Stack側のプログラムですが、UIFlow で作ったものを使います。
冒頭で紹介した記事を書く際に作ったものが、Macアプリとの間での動作確認ができているので、これを用います。

UIFlow.jpeg

Web Bluetooth API での通信

以前、micro:bit や toio といったデバイスを、BLE を使ってブラウザとの間で通信させたことがあったので、「以前と同様に進めれば良いかな」と思っていたのですが、思ったとおりに進みませんでした。ちなみに、その記事の一部が以下のものになります。

●toio を音で制御してみた(Audio用の Teachable Machine でベルやタンバリンの音を機械学習) - Qiita
 https://qiita.com/youtoy/items/37f70bb4ce630e6cbd92
●Web Bluetooth API で BLE(Chrome と micro:bit をつなぐ) - Qiita
 https://qiita.com/youtoy/items/cd2c3d4770d4ad75a321

デバイスのスキャンまわり(おそらく、フィルタ設定まわり)で何か問題が出ていそうな感じがしたので、上記の記事を書いた際に試していたことを見直してみたり、そもそもフィルタしない設定でまずは試したりなど、基本的な部分から見直してみることにしました。

開発者ツールからのスキャン

スキャン関連の話を調べていた時に、Chrome で Web Bluetooth API を試している方の記事で、以下のような記載がありました。
「この方法は、つないでみる部分の試行錯誤をする際には、とても手軽で良いな」と思い、試してみました。

Chromeでペアリング.jpeg

冒頭で「うまくいかない状態になった」という話では、過去に micro:bit や toio をつなげて使っていた際にデバイス名(今回の例では UIFlow上で自分で設定した文字列)でやっていたように思い、デバイス名でフィルタしたところ、スキャンできたデバイスのリストに M5Stack Core2 が出てこない、という状況でした。

Chrome の開発者ツールを開いて、上記の記事に書かれていた以下のコマンドをコンソールで実行してみます。

navigator.bluetooth.requestDevice({ acceptAllDevices: true })

フィルタなしでスキャンすると、リストの中に UIFlow で設定した「m5ble」という名前が出てきました。

BLEでのペアリング.jpg

過去に使っていた Web Bluetooth API のフィルタ設定

上記の画像を見ても分かるとおり、フィルタなしでスキャンすると、BLE を使っている様々なデバイスが一緒にでてきます。そのため、「過去の事例では特定のデバイス名を持つものでフィルタしていたはず」と、あらためて見直してみました。

見直してみると、デバイス名と UUID の一方か両方を使っているようでした。

toio でのフィルタ設定の事例:
toioの事例.jpg

micro:bit でのフィルタ設定の事例:
マイクロビットの事例.jpg

「toio でコメントアウトしてる部分は、何か理由があったんだっけ?」と思い出せない部分もあり、今回の検証のついでに、こちらも開発者ツールのコンソールでの挙動を確認してみます。

micro:bit の検出を試す

micro:bit側のプログラムは、以前書いた記事の内容である以下をそのまま使おうと思います。

BLE_UART_マイクロビット.jpeg

その際に 1点注意があり、以下の設定を適用して、ペアリングなしでもやりとりができるようにしてください。

BLEの設定.jpg

とりあえず、名前をベースにしたフィルタを試してみます。以下を開発者ツールのコンソールで実行します。

navigator.bluetooth.requestDevice({filters:[{namePrefix: 'BBC'}]})

無事、想定通りの結果となりました。周囲には別の BLEデバイスもありますが、micro:bit のみがリストに出てきました。

マイクロビットのペアリング.jpg

toio の検出を試す

toio は、電源をいれただけで BLE通信ができる状態になるので、toioの電源をいれた後にスキャンを試してみます。以下を開発者ツールのコンソールで実行します。

navigator.bluetooth.requestDevice({filters:[{namePrefix: 'toio'}]})

無事、想定通りの結果となりました。

toioのペアリング.jpg

M5Stack Core2 の検出を試す

それでは M5Stack Core2 で試してみます。

いったん、フィルタの設定なしでスキャンしてみます。

navigator.bluetooth.requestDevice({ acceptAllDevices: true })

上記を試した結果、以下のとおり検出できました。

M5Stackのスキャン.jpg

次はフィルタを設定した以下を試します。

navigator.bluetooth.requestDevice({filters:[{namePrefix: 'm5ble'}]})

そして上記を試すと、micro:bit や toio の時よりも少し時間がかかった気もしましたが、以下のとおり検出できました。

M5Stackのスキャン.jpg

JavaScript のプログラムでの処理: M5Stack Core2 との通信

次に、以下の HTML+JavaScript のプログラムで接続等を試してみます。
(※ Notify用の UUID を書いているものの、それを使った処理は未実装)

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>UIFlow と BLE</title>
</head>

<body>
  <h1>UIFlow と BLE</h1>
  <button onclick="onStartButtonClick()">接続</button>
  <button onclick="sendMessage()">テキスト書き込み</button>

<script>
const UUID_1 = '6e400001-b5a3-f393-e0a9-e50e24dcca9e';
const UUID_2 = '6e400002-b5a3-f393-e0a9-e50e24dcca9e'; // Write
const UUID_3 = '6e400003-b5a3-f393-e0a9-e50e24dcca9e'; // Notify

var bluetoothDevice;
var characteristic;

async function onStartButtonClick() {

  navigator.bluetooth.requestDevice({filters:[{namePrefix: 'm5ble'}]})
  .then(device => {
    bluetoothDevice = device;
    console.log("device", device);
    return device.gatt.connect();
  })
  .then(server =>{
    console.log("server", server)
    return server.getPrimaryService(UUID_1);
  })
  .then(service => {
    console.log("service", service)
    return service.getCharacteristic(UUID_2)
  })
  .then(chara => {
    console.log("characteristic", chara)
    alert("BLE接続が完了しました。");
    characteristic = chara;
  })
  .catch(error => {
    console.log(error);
  });
}

function sendMessage() {
  if (!bluetoothDevice || !bluetoothDevice.gatt.connected || !characteristic) return ;
  var text = "aaa";
  var arrayBuffe = new TextEncoder().encode(text);
  characteristic.writeValue(arrayBuffe);
}
</script>
</body>
</html>

上記のプログラムで接続処理を実行すると、コンソールに以下のメッセージが表示されました。

BLEで接続.jpg

接続はできたものの、以下のメッセージ・URL がでてきました。

DOMException: Origin is not allowed to access any service. Tip: Add the service UUID to 'optionalServices' in requestDevice() options.

https://webbluetoothcg.github.io/web-bluetooth/#dom-requestdeviceoptions-optionalservices

表示された URL のページを見てみたところ、このあたりが関係しそうです。

Bluetooth_requestDevice___-_Web_APIs___MDN.jpg

先ほどのメッセージの内容からすると、「filters」と合わせて「optionalServices」も設定し、「optionalServices」にはサービスの UUID を指定する、ということを行えば良さそうです。

上で掲載していたプログラムの一部を書きかえます。以下に、変更をした部分のみ抜粋し、変更前後の内容を掲載します。

こちらが変更前、「navigator.bluetooth.requestDevice」の後の部分が変更対象です。

async function onStartButtonClick() {

  navigator.bluetooth.requestDevice({filters:[{namePrefix: 'm5ble'}]})
  .then(device => {
    bluetoothDevice = device

以下が変更後の内容です。

async function onStartButtonClick() {

  navigator.bluetooth.requestDevice({
    filters:[{namePrefix: 'm5ble'}],
    optionalServices: [UUID_1]
  })
  .then(device => {

これで再度、試してみます。
その結果、無事に以下のツイートの動画にあるように、ブラウザから M5Stack側への通信を行うことができました。

思ったより時間がかかったため、本当は M5Stack Core2 からブラウザへの通信の部分も試したかったのですが、今回はここまでにします。やり残した部分は、別途記事にできればと思います。

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3