2
0

ブラウザの Web Bluetooth API を使って toio の時間指定付きモーター制御を扱う(環境は p5.js Web Editor を利用)

Last updated at Posted at 2024-03-17

はじめに

ブラウザの Web Bluetooth API と toio を組み合わせたお試しについての記事です。

toio の通信仕様は公開されていて、それに従った BLE での処理を行うと、toio を様々なプログラミング言語で制御することができます。

今回の記事では、ブラウザ上で実行する JavaScript の処理で、以下の toio の時間指定付きモーター制御を扱います(その際に、ブラウザの API である「Web Bluetooth API」を使っています)。

●モーター | toio™コア キューブ 技術仕様 > 「時間指定付きモーター制御」
 https://toio.github.io/toio-spec/docs/ble_motor/#%E6%99%82%E9%96%93%E6%8C%87%E5%AE%9A%E4%BB%98%E3%81%8D%E3%83%A2%E3%83%BC%E3%82%BF%E3%83%BC%E5%88%B6%E5%BE%A1

その際に、時間指定付きモーター制御の時間指定の値などを、処理の途中で書きかえつつ toio を動かすということも試しています。

直近で書いた記事について

直近では、Web Bluetooth API で以下のサウンド機能を扱う話を記事にしていました。その記事の内容と今回の内容は、基本的な流れは同じです。

●ブラウザの Web Bluetooth API を使って toio で使える効果音のいくつかをセットで鳴らす(環境は p5.js Web Editor を利用) - Qiita
 https://qiita.com/youtoy/items/00abe2b06e23bf9e0f09

今回の内容を試した時の様子

実装内容の話に入る前に、先に今回の内容を試した時の様子(以下の動画)を紹介します。

こちらでは、「画面をクリックした際に toio のモーター制御を 3回実行する」ということをやっています。

また、モーター制御に関する少し変則的な処理や、p5.js のちょっとした描画処理を入れています。
それらについて、具体的には「3回目の動作を 1/2 の確率で異なる回転方向・回転時間となるようする」「一連のモーター制御を行っている間、動画の左の方にうつっている p5.js の描画用キャンバスの、背景色を青系の色に変化させる」というものです。

今回の実装

それでは、今回実装した内容の話に入っていきます。

まずは、プログラム全体を示します。ちょっと雑な部分があります・・・。
※ 開発・実行に p5.js Web Editor を用いており、その環境内の sketch.js のみ書きかえて使っています(HTML や CSS はそのまま)。

sketch.js
const TOIO_SERVICE_UUID = "10b20100-5b3b-4571-9508-cf3efcd7bbae";
const TOIO_MOTOR_CHARACTERISTIC_UUID = "10b20102-5b3b-4571-9508-cf3efcd7bbae";

const sleep = (msec) => new Promise((resolve) => setTimeout(resolve, msec));

let characteristic;
let isConnected = false;

async function controlMotor() {
  try {
    if (!isConnected) {
      const device = await navigator.bluetooth.requestDevice({
        filters: [{ services: [TOIO_SERVICE_UUID] }],
        optionalServices: [TOIO_MOTOR_CHARACTERISTIC_UUID],
      });
      const server = await device.gatt.connect();
      const service = await server.getPrimaryService(TOIO_SERVICE_UUID);
      characteristic = await service.getCharacteristic(
        TOIO_MOTOR_CHARACTERISTIC_UUID
      );
      isConnected = true;
      console.log("接続しました");
    } else {
      background(100, 140, 190);
      console.log("モーター制御を開始");
      const data = new Uint8Array([
        0x02,
        0x01,
        1,
        0x64,
        0x02,
        2,
        0x14,
        10, // 0x0a を数値指定、10倍した値が時間(単位 ミリ秒)となる
      ]);

      await characteristic.writeValueWithoutResponse(data);

      await sleep(800);
      toggleValues(data, [2, 5]);
      data[7] = 20;
      await characteristic.writeValueWithoutResponse(data);
      await sleep(600);
      let t;
      if (random() < 0.5) {
        toggleValues(data, [2, 5]);
        t = 90;
      } else {
        t = 30;
      }
      data[7] = t;
      await characteristic.writeValueWithoutResponse(data);
      await sleep(t * 10);
      console.log("モーター制御完了");
      background(220);
    }
  } catch (error) {
    console.error(`Error: ${error}`);
  }
}

function toggleValues(data, indices) {
  indices.forEach((index) => {
    data[index] = data[index] === 1 ? 2 : 1;
  });
}

function setup() {
  createCanvas(550, 450);
  background(220);
}

function mouseClicked() {
  controlMotor();
}

実装内容の補足: 大まかな部分

上記で実装している内容は、おおまかには以下のとおりです。

  • 画面をクリックすると controlMotor() を実行する
    • controlMotor() の処理は以下のとおり
      • toio との接続が行われていなかったら、toio との接続を行う: if (!isConnected) {} の中の処理
      • 既に toio と接続されていたら効果音を鳴らす: background(100, 140, 190)background(220) までの処理

また、複数回のモーター制御を実行する際に、モーター制御の上書きを防ぐため、冒頭に書いている以下の処理を用いています(※ モーター制御が完了する前に、次の別のモーター制御を実行すると、完了していなかったモーター制御は途中までしか実行されません)。

const sleep = (msec) => new Promise((resolve) => setTimeout(resolve, msec));

なお、冒頭に書いている 2つの UUID は、toio の仕様のページに記載があるものを用いています。

実装内容の補足: モーター制御

モーター制御の処理は、writeValueWithoutResponse() を使っている部分です。その中で、Uint8Array() で toio に送るデータを作っています。

※ 前のサウンド機能を使った時は writeValueWithResponse() でしたが、今回は writeValueWithoutResponse() を使うことになります(違いは with か without か)

Uint8Array() の括弧の中の内容

Uint8Array() の括弧の中の内容は、以下となっています。

  • [0x02, 0x01, 1, 0x64, 0x02, 2, 0x14, 10]
  • [0x02, 0x01, 2, 0x64, 0x02, 1, 0x14, 20]
  • 1/2 の確率で以下のどちらかになる
    • [0x02, 0x01, 2, 0x64, 0x02, 1, 0x14, 30]
    • [0x02, 0x01, 1, 0x64, 0x02, 2, 0x14, 30]

今回、const data = new Uint8Array() で定義した値を、途中で書きかえる実験をしてみたかったため、background(100, 140, 190)background(220) までの処理はごちゃごちゃした内容になっています。

なお、ここで指定している数値などの意味は、toio公式の仕様で書かれている以下の内容に従ったものです。

image.png

回転方向を反転させる処理

今回、3回の時間指定付きモーター制御を行う際に、回転方向が反転する部分があります。

その処理では、Uint8Array() の「3番目」「6番目」の両方の値を「1 ⇒ 2、または 2 ⇒ 1」に変化させています。それを実行するために以下の toggleValues() を用いています。

function toggleValues(data, indices) {
  indices.forEach((index) => {
    data[index] = data[index] === 1 ? 2 : 1;
  });
}
2
0
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
2
0