M5Stack と toio を組み合わせ：まずは「kenichi884/M5StackToio」のサンプルで toio 4台の同時制御

以下の記事を書いた際に軽い下調べだけはしていた「[kenichi884/M5StackToio](https://github.com/kenichi884/M5StackToio/blob/master/README_jp.md)」を、実際に試してみたという話です。

●M5Stack と toio を組み合わせて使う場合のライブラリの情報を調べる - Qiita
　https://qiita.com/youtoy/items/5fbd3f1e7d98b7bdbf9a

実際に試した内容のの 1つは以下となり、サンプルを利用して toio 4台の同時制御を行いました。

<iframe width="560" height="315" src="https://www.youtube.com/embed/nBXHR1uArSA?si=Esaj1SaTuB7OMmD8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

この後、説明などを書いていきます。

## 今回使うもの
今回のお試しでは、以下を使ってみます。

- 開発環境
   - Arduino IDE
-　デバイス
   -　toio
   -　M5Stack系デバイス
      -　ATOMS3
      -　M5 Din Meter（※ 冒頭の動画で使っているもの）

### 開発を行う前の準備
開発を行う前の準備については、以下に公式の説明がありますので、そちらを参照して進めてください。

●M5StackToio/README_jp.md at master · kenichi884/M5StackToio
　https://github.com/kenichi884/M5StackToio/blob/master/README_jp.md
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/50885371-9b0d-4c5c-23e7-b396c366025e.png)

### 準備の補足：ライブラリのインストール
公式が書いている手順の中にある「2. M5StackToio のインストール」について、ここで少し補足します。

公式の説明にあるように、今回使う「kenichi884/M5StackToio」は Arduino IDE上でインストール可能です。Arduino IDE のライブラリマネージャーで「toio」というキーワードで検索すると、以下のように一覧に出てきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/296e4854-29b1-f5d5-fa4f-15033cc5403e.png)

この時に表示される作者の情報は、[大元のフォーク元](https://github.com/futomi/M5StackToio)の情報になっているようです。
それについて、表示されるライブラリのバージョン 1．0．4 は、今回使う「kenichi884/M5StackToio」に付与されたバージョン番号なので、上記のインストールで「kenichi884/M5StackToio」を使える状態になります。

それと kenichi884/M5StackToio のBluetooth通信は、フォーク元で使われていたものから別のライブラリ「NimBLE-Arduino」になっています。こちらのライブラリのインストールも忘れないように行ってください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/f6f05c23-7f71-4be7-2a81-25ca2bf8e339.png)

あとは必要に応じてになりますが、NimBLE-Arduino で同時接続できる toio の数に影響する設定がありますので、これも適宜変更すると良いです（※ デフォルト設定のままだと、同時接続数が 3台までになるようです）。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/6e9dc4af-c1ef-7fea-28e5-df70361e5f4e.png)

コンフィグファイルが置かれたフォルダは、以下の下を見ていくと発見できます。

●Find sketches, libraries, board cores, and other files on your computer – Arduino Help Center
　https://support.arduino.cc/hc/en-us/articles/4415103213714-Find-sketches-libraries-board-cores-and-other-files-on-your-computer

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/2fe4ca4f-25c1-82bc-a9d2-6d51228121c0.png)

なお、参考として、自分の環境でのコンフィグファイル変更前の状態を掲載しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/5f68a047-f2f1-1d23-36ca-455504830a11.png)

### デバイスへの書き込み
それでは、デバイスへの書き込みを行ってみます。

試す内容は、以下のサンプルとします。
このサンプルは『「M5Stack ATOMS3」で動作確認が行われたものであるものの、「M5Unified」を使っているため、他のボタンが一つ以上ある M5Stack のデバイスでも動作すると思われる』とのことです。

●M5StackToio/examples/multi_corecube_test/multi_corecube_test.ino at master · kenichi884/M5StackToio
　https://github.com/kenichi884/M5StackToio/blob/master/examples/multi_corecube_test/multi_corecube_test.ino

実際、冒頭の動画では「M5 Din Meter」を使って動かすことができました。

#### ATOMS3 で試す
とりあえず ATOMS3 で試します。
試し方はコード内にコメントで書かれているので、書き込みなどが終わった後は、以下に従って試していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/77bbc4e2-f720-8644-c33c-c51887d63e57.png)

以下が、今回 ATOMS3 に書きこんだものです。上で掲載していたサンプルをそのまま用いています。
書き込みをする前には、Arduino IDE でボードの選択などを適宜行ってください。

```cpp
// https://github.com/kenichi884/M5StackToio/blob/master/examples/multi_corecube_test/multi_corecube_test.ino

#include <M5Unified.h>
#include <Toio.h>

// Declare a toio object
// Toio オブジェクト生成
Toio toio;

// Set this value or higher.  in nimconfig.h  "#define CONFIG_BT_NIMBLE_MAX_CONNECTIONS" 
#define MAX_TOIOCORECUBE_NUM 6

ToioCoreTargetPos targetPos[MAX_TOIOCORECUBE_NUM];

// Decreare a pointer variable for the discoverd ToioCore object.
// 発見された ToioCore オブジェクトのポインタ変数を定義
ToioCore* toiocore[MAX_TOIOCORECUBE_NUM];
size_t toiocore_num = 0;

// Simple play mat included with the toio Core Cube (single package)
// toio コア キューブ（単体）付属の簡易プレイマット
const int MAT_CENTER_X = 98 + (402 - 98) / 2;
const int MAT_CENTER_Y = 142 + (358 - 142) / 2;
// Play mat included with the toio COLLECTION (circle and two lines side)
// トイオ・コレクション付属のプレイマット(円周と二本線側)
//const int MAT_CENTER_X = 45 + (455 - 45) / 2;
//const int MAT_CENTER_Y = 45 + (455 - 45) / 2;
const int RADIUS = 60;
#define PI 3.141592653589793

// Calculate coordinates of polygon vertices
// 頂点の座標を計算
void calcTargetPos()
{
  if(toiocore_num > 0) {
    float angleDelta = 2.0 * PI / toiocore_num;
    for(int i = 0 ; i < toiocore_num ; i++){
      targetPos[i].angleAndRotation = 0;
      targetPos[i].angleDegree = (i * angleDelta) * 180 / PI + 90;
      targetPos[i].posX = MAT_CENTER_X + RADIUS * cos(i * angleDelta);
      targetPos[i].posY = MAT_CENTER_Y + RADIUS * sin(i * angleDelta);;
      M5.Log.printf("preset Target %d, %d, (%d %d)\n", targetPos[i].posX, targetPos[i].posY, 
        targetPos[i].angleDegree, targetPos[i].angleAndRotation );
    }
  }
}

// Move Toio Core Cubes to next positions.
// 次の頂点位置に移動
void moveToNextTarget()
{
  if(toiocore_num < 1) return;
  ToioCoreTargetPos p = targetPos[0];
  // rotate target positions
  for(int i = 0 ; i < (toiocore_num -1) ; i++){
    targetPos[i] = targetPos[i + 1];
  }
  targetPos[toiocore_num -1] = p;
  for(int i = 0 ; i < toiocore_num ; i++){
    toiocore[i]->controlMotorWithTarget(0, 5, 0, 80, 0, 
      targetPos[i].posX, targetPos[i].posY, 
      targetPos[i].angleDegree, targetPos[i].angleAndRotation);
  }

}

void setup() {
  M5.begin();
  M5.Log.printf("M5StackToio multi_corecube_test\n");

  // Scan Toio Core Cubes in 5 seconds.
  // 5 秒間 Toio Core Cube をスキャン
  M5.Log.printf("Scanning your toio core...\n");
  std::vector<ToioCore*> toiocore_list = toio.scan(3);

  // Exit if could not found any cubes.
  // 見つからなければ終了
  toiocore_num = toiocore_list.size();
  if (toiocore_num == 0) {
    M5.Log.printf("No toio Core Cube was found. Turn on your Toio Core Cube, then press the reset button of your Toio Core Cube.\n");
    return;
  }
  M5.Log.printf("found %u toio core cubes.\n", toiocore_num);
  for(uint i = 0 ; i < toiocore_num ; i++){
    // Assign the ToioCore object of the Toio Core Cubes found.
    // 見つかった Toio Core Cube の ToioCore オブジェクト
    toiocore[i] = toiocore_list.at(i);
    // Print the Toio Core cube's device name and MAC address.
    // Toio Core のデバイス名と MAC アドレスを表示
    M5.Log.printf("%u: %s (%s)\n", i, toiocore[i]->getName().c_str(), toiocore[i]->getAddress().c_str());
    targetPos[i]= {0, 0, 0, 0};

    // Start BLE connection.
    // BLE 接続開始
    M5.Log.printf("Connecting...\n");

    if (!toiocore[i]->connect()) {
      M5.Log.printf("%u: Failed to establish a BLE connection.\n", i);
      return;
    }
    M5.Log.printf("%u: Connected.\n", i);
    // Set Connection event callback.
   // Connection イベントのコールバックをセット
   M5.Log.printf("set cannection callback %u %p\n", i, toiocore[i]);
    toiocore[i]->onConnection([i](bool state) {
      M5.Log.printf("%u: Connection Event %s\n", i, state ? "Connected   " : "Disconnected");
    });

    /// Button Event callback
    toiocore[i]->onButton([i](bool state){
      M5.Log.printf("%u: Button Event %d\n", i, state);
      // Play sound effect
      toiocore[i]->playSoundEffect(i);
      for(int j = 0 ; j < toiocore_num; j++){
        // Spin other than yourself for 300ms.
        // 自分以外を300ms回転
        if(j != i)
          toiocore[j]->controlMotor(true, 80, false, 80, 300);
      }
    });

  // Set indicator color.
  // LED色設定
  uint j = i + 1;
  toiocore[i]->turnOnLed((j & 0x01) ? 255:0, (j & 0x02) ? 255:0, (j & 0x04) ? 255:0 );

  }

  calcTargetPos();
  // Print free heap memory size.
  // 空きヒープメモリサイズの確認
  //M5.Log.printf("esp_get_free_heap_size(): %6d\n", esp_get_free_heap_size() );
}

void loop() {
  M5.update();

  // If you want to handle event callbacks, you shoud call loop() method  of Toio Object here.
  // イベントを扱う場合は、必ずここで Toio オブジェクトの
  // loop() メソッドを呼び出すこと
  toio.loop();

  // M5Stack のボタン A が2秒長押しされたときの処理
  // When button A of M5Stack is pressed for 2 seconds
  if (M5.BtnA.wasReleaseFor(2000)) { 
    // Check the Toio Core Cube was connected or disconnected.
    // Toio Core Cube と BLE 接続中かどうかをチェック
    for(uint i = 0; i < toiocore_num ; i++){
      if (toiocore[i]->isConnected()) {
        M5.Log.printf("%u: try disconnect\n", i);
        toiocore[i]->disconnect(); // If connected, disconnect. 接続中なら切断
      } else {
        M5.Log.printf("%u: try connect\n", i);
        toiocore[i]->connect(); // If not connected, connect. 接続していないなら接続
      }
    }
  // When button A of M5Stack was pressed.
  // M5Stack のボタン A が押されたときの処理
  } else if(M5.BtnA.wasReleased()) { 
    M5.Log.printf("press/release A btn\n");
  // Move Toio Core Cubes to next positions.
  // 次の位置に移動
    moveToNextTarget();
  }
  yield();
}
```

#### 動作確認： ATOMS3
以下は、実際に ATOMS3 を使って動作させた時の様子で、意図通りの動作をさせられました。

<iframe width="560" height="315" src="https://www.youtube.com/embed/CEplmVaaL3Q?si=cXMMkBlsfPnRRlMj" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

#### M5 Din Meter を使う
今回、冒頭に動画を掲載していた M5 Din Meter を使ったお試しも行いました。

その際、コードは全く変更しておらず、対応したことといえば Arduino IDE でボードの選択を変更したくらいです。

## おわりに
今回、「kenichi884/M5StackToio」の簡単なお試しとして、サンプルの 1つを使ってみました。お試しでは、M5Stack から toio 4台の同時制御が行えました。

今後は、別のサンプルを試したり、コードを書きかえたものを試したりなどできればと思います。