obniz BLE/Wi-Fi GATEWAY Gen 2.0 で toio を動かしたメモ
この記事は IoTLT Advent Calendar 2022 の 9 日目の記事です。
obniz BLE/Wi-Fi GATEWAY Gen 2.0 で toio を動かしたメモです。
できあがったもの
やったー。ずっとやりたかった #obniz BLE/Wi-Fi GATEWAY Gen 2.0 と #toio の連携ができました!
— Tanaka Seigo (@1ft_seabass) December 7, 2022
toio ボタンのクリック取得によるインタラクティブに動きが変わる仕組みで、時計回り・反時計回り・直進に変化します! pic.twitter.com/44cIqVuxtA
obniz BLE/Wi-Fi GATEWAY Gen 2.0

もともと Gen 1.0 を持っていたのですが、やはりディスプレイがあるといろいろ挙動が見えて安心ですし、電子インク的なディスプレイも興味があったので購入しました。
obniz BLE/Wi-Fi Gateway Gen2.0 - obniz Docs
リファレンスも充実していて分かりやすいです。もちろん obniz Board でも BLE デバイスへの接続はできるのですが、ゲートウェイですとコンセントに直接挿せて電源を取れますし、ピン穴がないぶん「BLE につなぐぞ!」という強い意志で対峙できるので好きです。
イベント会場での BLE 運用を想像すると、コンセントにガッと挿して使えるのはきっとやりやすいでしょう。こういったユースケースを狙っているところもあります。

ということで開封し、自分のアカウントにデバイス登録を済ませて使えるようになりました。ドキュメントに沿って進めれば、大丈夫でした。
toio の準備
それでは toio の準備です。

toio は混乱のないように、どこともペアリングしていない状態で電源を ON しておきましょう。わたしは別のデバイスでうっかりペアリングしてしまっていたために、つながらなくてハマりました。注意しましょう。
このように青ランプが点滅していると接続待ちの状態です。
obniz Cloud で動かすソースコード

obniz Cloud でアプリ開発を選んで新規アプリを作成、今回の obniz BLE_Wi-Fi GATEWAY Gen 2.0 に関連付けます。
本来であれば toio の BLE 仕様に合わせて地道に頑張っていくところですが obniz では toio_corecube としてパーツライブラリがあります。
それを元に作ったものがこちらです。
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" />
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="https://unpkg.com/obniz@3.x/obniz.js" crossorigin="anonymous" ></script>
</head>
<body>
<div id="obniz-debug"></div>
<script>
const obniz = new Obniz("OBNIZ_ID_HERE");
let currentMode = 1;
let previousMode = 0;
let flagToioConnected = false;
let device;
obniz.onconnect = async function() {
await obniz.ble.initWait();
// toio の検出→接続
const Toio_CoreCube = Obniz.getPartsClass("toio_CoreCube");
obniz.ble.scan.onfind = async (peripheral) => {
if (Toio_CoreCube.isDevice(peripheral)) {
console.log(`toio find ${peripheral.address}`);
device = new Toio_CoreCube(peripheral);
device.ondisconnect = (reason) => {
console.log(reason);
flagToioConnected = false;
}
console.log(`toio connecting ${peripheral.address}`);
await device.connectWait();
console.log(`toio connected ${peripheral.address}`);
// 接続フラグを true
flagToioConnected = true;
}
};
await obniz.ble.scan.startWait();
obniz.onloop = async function(){
// toio 接続時だけ動作
if(flagToioConnected){
// ボタン状態の判定
const isButtonPush = await device.getButtonStateWait();
// ボタンが押されるとモードが移動 0 → 1 → 2 → 0 ... と回る
if(isButtonPush){
console.log("isButtonPush");
currentMode += 1;
currentMode %= 3;
}
// モードに応じて動作が変わる
if(currentMode != previousMode){
console.log(`currentMode ${currentMode}`);
if(currentMode == 0){
// まっすぐ進む
await device.moveAroundWait(10, 10);
} else if(currentMode == 1){
// 時計回り
await device.moveAroundWait(20, -20);
} else if(currentMode == 2){
// 反時計回り
await device.moveAroundWait(-20, 20);
}
previousMode = currentMode;
}
}
}
};
// called on offline
obniz.onclose = async function() {
};
</script>
</body>
</html>
こちらを起動すると、

このようにコンソールに toio を検索し接続するログが出てきます。接続がうまくいくと toio からかわいい音が鳴ります。
まれによくあるのですが connecting から進まないこともあるので、そのときは obniz のアプリ実行をいったん止めてまた再開というのを繰り返すと成功すると思います。toio の再起動や obniz ゲートウェイ自体の再起動のような重い操作をしなくても、この操作でうまくいくということです。
実装的にうまくできたところ
// toio の検出→接続
const Toio_CoreCube = Obniz.getPartsClass("toio_CoreCube");
obniz.ble.scan.onfind = async (peripheral) => {
if (Toio_CoreCube.isDevice(peripheral)) {
console.log(`toio find ${peripheral.address}`);
device = new Toio_CoreCube(peripheral);
device.ondisconnect = (reason) => {
console.log(reason);
flagToioConnected = false;
}
console.log(`toio connecting ${peripheral.address}`);
await device.connectWait();
console.log(`toio connected ${peripheral.address}`);
// 接続フラグを true
flagToioConnected = true;
}
};
まず接続面ですが、サンプルのコードでほぼ構成されていますが、await async で非同期に動作しているので、接続して全て終わったら flagToioConnected を true にして接続状態をつくっています。
こうすることで、以降の obniz.onloop 内で flagToioConnected を使うことで、接続後に動作するループが実現できました。もしかすると、接続状態を検知できるメソッドがあるかもですが、いったんこれでもうまくいっています。
obniz.onloop = async function(){
// toio 接続時だけ動作
if(flagToioConnected){
// ボタン状態の判定
const isButtonPush = await device.getButtonStateWait();
// ボタンが押されるとモードが移動 0 → 1 → 2 → 0 ... と回る
if(isButtonPush){
console.log("isButtonPush");
currentMode += 1;
currentMode %= 3;
}
// モードに応じて動作が変わる
if(currentMode != previousMode){
console.log(`currentMode ${currentMode}`);
if(currentMode == 0){
// まっすぐ進む
await device.moveAroundWait(10, 10);
} else if(currentMode == 1){
// 時計回り
await device.moveAroundWait(20, -20);
} else if(currentMode == 2){
// 反時計回り
await device.moveAroundWait(-20, 20);
}
previousMode = currentMode;
}
}
}
};
ボタンの状態は onchange のようにイベントでは検知できませんが、getButtonStateWait というボタン状態を知れるメソッドがあるので obniz.onloop でチェックしています。押されたときに、動作モードを 1 増やして余剰 % 使ってモードが移動 0 → 1 → 2 → 0 ... と回るようにしています。
モードを現在のモード currentMode と過去のモード previousMode で管理して違いがあった時だけ動作させているので、わりときびきび反応してくれています。
getButtonStateWait を高頻度でチェックするのは負荷がかかったりバッテリー消費が大きいかもしれないので当初は心配なところはありましたが、いまのところうまく回っています。もしかすると 200 ミリ秒くらいに遅延を入れて優しいループにしてもいいかもしれないので、今度やってみます。
もちろん M5Stack や Raspberry Pi 、obniz Board でもつながりますが、展示のネタ作りや試行錯誤をしていると 1 大消費するのがやりづらかったりします、このように obniz BLE/Wi-Fi GATEWAY Gen 2.0 にような BLE 接続に特化したデバイスがあると、toio の操作はこちらに任せて、他の仕組みは分離して作れるといったことができるので、引き続き、試していこうと思います。