「M5StackCoreInk」と「toioコアキューブ」と「LEGO」でなにかしたVTuber

© CC BY 4+ visibility363
© CC BY 4+
visibility363

toioコアキューブの上に、M5StackCoreInkを乗せて、Bluetooth接続したうえで、なるべく不安定な状態でいろいろ動かしてみた作品です。LEGOによる姿勢制御に注目です。

link https://youtube.com/shorts/vWzgr72ZSCY?feature=share
動画
開発素材

ガジェット

システム構成
system image

まずはCoreInkの開発環境を整えよう

CoreInk以外の連携の可能性が考えられるので、UIFlow以外の選択肢をとる

ArduinoIDEの場合

久しぶりの起動なので、まずはアップデートが必要そう。 ということで最新版です。

M5StackCoreInk クイックスタートガイドを用意する

https://docs.m5stack.com/en/arduino/arduino_ide 日本語表記も選択できるのでひとあんしん。

ボード管理

リンク押下でページ遷移。 下の図のように、URLをコピーします。 ArduinoIDEで「基本設定」をクリックします。 下の図のように、「追加のボードマネージャのURL」にある右端のアイコンをクリックします。 下の図のようにペーストします。すでにURLがある場合は、改行します。 基本設定から出て、左のサイドバーからボードマネージャをクリックして、下の図のように「m5」などで検索します。M5Stackの項目が出るので、インストールします。 インストール完了。 下の図のように、「ツール」→「ボード」→「M5Stack」を選択します。今回はCoreInkの開発なので、「M5Core」を選択します。

ライブラリ管理

つづいてライブラリ。 下の図のように、サイドバーからライブラリマネージャーをクリックして、「m5core」で検索します。一覧の中から「M5Core-Ink」をインストールします。 依存関係があるものは全てインストールします。

サンプル

サンプルを起動してみます。 下の図のように、「ファイル」→「スケッチ例」→「M5StackCore-Ink」の中からサンプルを選択します。 今回は、「Basics」→「Button」にします。 下の図のように、新しくスケッチが表示されました。

コンパイル

引き続き、サンプルプログラムを使います。

ドライバのインストール

よくわからないので、今回はドキュメントのとおり、下の図のように2種類インストールしました。

書き込み

「ボード」と「ポート」が適切な情報にします。うのっちの環境では、COM6にCoreInkを接続しているので、COM6になっています。 また、UIFlowなど別ツールですでに接続していると、コンパイルエラーが出るので気をつけましょう。(出た)

動かしてみた

「ArduinoからM5StackCoreInkをサンプル起動させてみた」

https://youtube.com/shorts/XJJ-F9IE9-Y

toioのArduino開発環境を整えよう

M5StackToioのサイトを開いておく

https://github.com/futomi/M5StackToio

M5StackToioのダウンロード~インポート

zipファイルでダウンロードする。 下の図のように インストールする。zipファイルは展開しなくてもよい。 ライブラリを追加。

ArduinoIDEの再起動

たぶんこのタイミングで再起動したほうがいいかも。M5StackToioのアップデートがかかりました。

toioコアキューブのアップデート

コンソールおよびコアキューブは2024年にアップデートがありました。

サンプルスケッチで動かす

CoreInkのサンプルスケッチ「HelloWorld」をベースに、M5StackToioのサンプルコードを加えました。 下はスクリプトになります。

*******************************************************************************
* Copyright (c) 2021 by M5Stack
*                  Equipped with CoreInk sample source code
*                          配套  CoreInk 示例源代码
* Visit for more information: https://docs.m5stack.com/en/core/coreink
* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/coreink
*
* Describe: Hello World
* Date: 2021/11/14
*******************************************************************************
*/
#include "M5CoreInk.h"
#include <Toio.h>
// Toio オブジェクト生成
Toio toio;

// ToioCore オブジェクトのポインタ
ToioCore* toiocore = nullptr;

// BLE 接続・切断のタイムスタンプ
unsigned long conn_time = millis();
Ink_Sprite InkPageSprite(&M5.M5Ink);  //创建 M5Ink实例

/* After CoreInk is started or reset
  the program in the setUp () function will be run, and this part will only be
  run once. 在 CoreInk
  启动或者复位后,即会开始执行setup()函数中的程序,该部分只会执行一次。 */
void setup() {
  M5.begin();                // Initialize CoreInk. 初始化 CoreInk
  if (!M5.M5Ink.isInit()) {  // check if the initialization is successful.
                             // 检查初始化是否成功
    Serial.printf("Ink Init faild");
    while (1) delay(100);
  }
  M5.M5Ink.clear();  // clear the screen. 清屏
  delay(1000);
  // creat ink Sprite. 创建图像区域
  if (InkPageSprite.creatSprite(0, 0, 200, 200, true) != 0) {
    Serial.printf("Ink Sprite creat faild");
  }
  InkPageSprite.drawString(35, 50, "Hello World!");  // draw string.
                                                     // 绘制字符串
  InkPageSprite.pushSprite();                        // push the sprite.  推送图片
                                                     // toio コア キューブのスキャン開始
  std::vector<ToioCore*> toiocore_list = toio.scan(3);
  if (toiocore_list.size() > 0) {
    toiocore = toiocore_list.at(0);
  }

  // BLE 接続状態イベントのコールバックをセット
  toiocore->onConnection([](bool state) {
    Serial.println(state ? "接続" : "切断");
  });
}

void loop() {
  // コールバックを使う場合には必ず Toio オブジェクトの loop() を呼び出す
  toio.loop();

  if (toiocore) {
    // 10 秒おきに BLE 接続・切断を繰り返す
    unsigned long now = millis();
    if (now - conn_time >= 10000) {
      if (toiocore->isConnected()) {
        toiocore->disconnect();
      } else {
        toiocore->connect();
      }
      conn_time = now;
    }
  }
}

プロトタイプをつくってみよう

7/21時点

setupメソッドでtoioのサンプルスクリプトが起動。loopメソッドでcoreinkのボタン判定。loop内でtoioの機能を使うと切断されるので、たぶん書き方がまずい気がする。(toio.loop();は入れている)

7/22

CoreInkを頑丈にしました。 コアキューブにも手を加えました。(デコっているだけ) 動きました!! https://youtube.com/shorts/WnDf-RaaKSg?feature=share この日のスクリプト

/* ----------------------------------------------------------------
  Toio Core Cube が発見された後、M5Stack の A ボタンを押すと Toio Core
  Cube と BLE 接続します。接続が完了すると、ジョイスティックで Toio Core
  Cute を運転することができます。

  BLE 接続中、ジョイスティックの z 軸を押すと、チャルメロが再生されます。
  また、M5Stack の B ボタンを押すと、Toio Core Cube の LED が白で点灯し
  ます。
  -------------------------------------------------------------- */
#include "M5CoreInk.h"
#include <Toio.h>
Ink_Sprite InkPageSprite(&M5.M5Ink); 

// ライト ON/OFF 状態
static bool light_on = false;

// MIDI データ (チャルメラ)
static const uint8_t CHARUMERA_LEN = 39;
static uint8_t CHARUMERA_DATA[CHARUMERA_LEN] = {
  3,             // Type of Sound control (MIDI)
  1,             // Repeat count
  12,            // Number of operations
  14,  69, 255,  // 140ms, A5
  14,  71, 255,  // 140ms, B5
  56,  73, 255,  // 560ms, C#6
  14,  71, 255,  // 140ms, B5
  14,  69, 255,  // 140ms, A5
  114, 128, 255, // no sound
  14,  69, 255,  // 140ms, A5
  14,  71, 255,  // 140ms, B5
  14,  73, 255,  // 560ms, C#6
  14,  71, 255,  // 140ms, B5
  14,  69, 255,  // 140ms, A5
  56,  71, 255   // 560ms, B5
};

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

// 発見された ToioCore オブジェクトのポインタ変数を定義
ToioCore* toiocore = nullptr;

void ButtonTest(char* str) {
    InkPageSprite.clear();                  // clear the screen.  清屏
    InkPageSprite.drawString(35, 59, str);  // draw the string.  绘制字符串
    InkPageSprite.pushSprite();             // push the sprite.  推送图片
    delay(2000);
}

void setup() {
  //coreinkの初期化ここから
  M5.begin(); 
  if (!M5.M5Ink.isInit()) { 
    Serial.printf("Ink Init faild");
    while (1) delay(100);
  }
  M5.M5Ink.clear(); 
  delay(1000);
  if (InkPageSprite.creatSprite(0, 0, 200, 200, true) != 0) {
      Serial.printf("Ink Sprite create faild");
  }
  InkPageSprite.drawString(5, 20, "Hello toio");
  //coreinkの初期化ここまで

  ButtonTest("HelloCoreInk");
  // 3 秒間 Toio Core Cube をスキャン
  std::vector<ToioCore*> toiocore_list = toio.scan(3);

  // 最初に見つかった Toio Core Cube の ToioCore オブジェクト
  toiocore = toiocore_list.at(0);
  // toiocore->onConnection([](bool state) {
  //   InkPageSprite.drawString(5, 20, "Hello Core-INK toio");
  // });
  ButtonTest("toio OK");
  Serial.printf("toio OK");
  delay(1000);
  M5.M5Ink.clear();  // Clear the screen. 
}

void loop() {
  // イベントを扱う場合は、必ずここで Toio オブジェクトの
  // loop() メソッドを呼び出すこと
  toio.loop();

  // Toio core Cube が見つかっていなければ loop () を抜ける
  if (!toiocore) {
    M5.M5Ink.clear();  // Clear the screen. 
    delay(1000);
    ButtonTest("toio lost");
    Serial.printf("toio lost");
    return;
  }

  if (M5.BtnUP.wasPressed())
  {
    ButtonTest("Btn UP Pressed"); 
    Serial.printf("Btn UP Pressed");
    // Toio Core Cube と BLE 接続中かどうかをチェック
    if (toiocore->isConnected()) {
      // toiocore->disconnect(); // 接続中なら切断
    } else {
      toiocore->connect(); // 切断中なら接続
    }
    // return;
  }

  // // Toio core Cube が未接続なら loop () を抜ける
  // if (!toiocore->isConnected()) {
  //   return;
  // }

  // if (M5.BtnUP.wasPressed())
  // {
  //     ButtonTest("Btn UP Pressed");  // Scroll wheel up. 
  //     Serial.printf("Btn UP Pressed");
  // }
  if (M5.BtnMID.wasPressed())
  {
    ButtonTest("Btn MID Pressed"); 
    Serial.printf("Btn MID Pressed");
    if (light_on) {
      toiocore->turnOffLed(); // 点灯中なら消灯
      toiocore->controlMotor(true, 40, false, 40, 4500);
    } else {
      // 2 秒間だけ右に曲がりながら進む (左右のモーターを個別に制御)
      toiocore->controlMotor(true, 40, false, 40, 4500);
      delay(5000);
      toiocore->turnOnLed(255, 0, 0); // 消灯中なら赤で点灯
    }
    light_on = !light_on;
  }
  if (M5.BtnDOWN.wasPressed())
  {
    ButtonTest("Btn DOWN Pressed"); 
    Serial.printf("Btn DOWN Pressed");
    toiocore->playSoundRaw(CHARUMERA_DATA, CHARUMERA_LEN);
  }
  if (M5.BtnPWR.wasPressed()) {  // Right button press.  
      ButtonTest("Btn PWR Pressed");
      Serial.printf("Btn PWR Pressed");
      M5.shutdown();  // Turn off the power, restart it, you need to wake up
                      // through the PWR button.
  }
  // // スロットル
  // int8_t throttle = jy;
  // if (throttle > 100) {
  //   throttle = 100;
  // }
  // if (throttle < -100) {
  //   throttle = -100;
  // }
  // // 最大速度が速すぎて操作しづらいので値を 2/3 にする
  // throttle = (float)throttle * 2 / 3;

  // // ステアリング
  // int8_t steering = (abs(jx) < 10) ? 0 : jx; // 10 ほど遊びを入れる
  // if (steering > 100) {
  //   steering = 100;
  // }
  // if (steering < -100) {
  //   steering = -100;
  // }
  // // 左右の動きの反応が敏感すぎるので値を 2/3 にする
  // steering = (float)steering * 2 / 3;

  // toiocore->drive(throttle, steering);
  M5.update(); 
}

7/31 完成しよう

構成図

スクリプト

/* ----------------------------------------------------------------
  Toio Core Cube が発見された後、M5Stack の A ボタンを押すと Toio Core
  Cube と BLE 接続します。接続が完了すると、ジョイスティックで Toio Core
  Cute を運転することができます。

  BLE 接続中、ジョイスティックの z 軸を押すと、チャルメロが再生されます。
  また、M5Stack の B ボタンを押すと、Toio Core Cube の LED が白で点灯し
  ます。
  -------------------------------------------------------------- */
#include "M5CoreInk.h"
#include <Toio.h>
Ink_Sprite InkPageSprite(&M5.M5Ink); 

// ライト ON/OFF 状態
static bool light_on = false;

// MIDI データ (チャルメラ)
static const uint8_t CHARUMERA_LEN = 39;
static uint8_t CHARUMERA_DATA[CHARUMERA_LEN] = {
  3,             // Type of Sound control (MIDI)
  1,             // Repeat count
  12,            // Number of operations
  14,  69, 255,  // 140ms, A5
  14,  71, 255,  // 140ms, B5
  56,  73, 255,  // 560ms, C#6
  14,  71, 255,  // 140ms, B5
  14,  69, 255,  // 140ms, A5
  114, 128, 255, // no sound
  14,  69, 255,  // 140ms, A5
  14,  71, 255,  // 140ms, B5
  14,  73, 255,  // 560ms, C#6
  14,  71, 255,  // 140ms, B5
  14,  69, 255,  // 140ms, A5
  56,  71, 255   // 560ms, B5
};

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

// 発見された ToioCore オブジェクトのポインタ変数を定義
ToioCore* toiocore = nullptr;

void ButtonTest(char* str) {
    InkPageSprite.clear();                  // clear the screen.  清屏
    InkPageSprite.drawString(35, 59, str);  // draw the string.  绘制字符串
    InkPageSprite.pushSprite();             // push the sprite.  推送图片
    delay(2000);
}

void setup() {
  //coreinkの初期化ここから
  M5.begin(); 
  if (!M5.M5Ink.isInit()) { 
    Serial.printf("Ink Init faild");
    while (1) delay(100);
  }
  M5.M5Ink.clear(); 
  delay(1000);
  if (InkPageSprite.creatSprite(0, 0, 200, 200, true) != 0) {
      Serial.printf("Ink Sprite create faild");
  }
  InkPageSprite.drawString(5, 20, "Hello toio");
  //coreinkの初期化ここまで

  ButtonTest("HelloCoreInk");
  // 3 秒間 Toio Core Cube をスキャン
  std::vector<ToioCore*> toiocore_list = toio.scan(3);

  // 最初に見つかった Toio Core Cube の ToioCore オブジェクト
  toiocore = toiocore_list.at(0);
  // toiocore->onConnection([](bool state) {
  //   InkPageSprite.drawString(5, 20, "Hello Core-INK toio");
  // });
  ButtonTest("toio OK");
  Serial.printf("toio OK");
  delay(1000);
  M5.M5Ink.clear();  // Clear the screen. 
}

void loop() {
  // イベントを扱う場合は、必ずここで Toio オブジェクトの
  // loop() メソッドを呼び出すこと
  toio.loop();

  // Toio core Cube が見つかっていなければ loop () を抜ける
  if (!toiocore) {
    M5.M5Ink.clear();  // Clear the screen. 
    delay(1000);
    ButtonTest("toio lost");
    Serial.printf("toio lost");
    return;
  }

  if (M5.BtnUP.wasPressed())
  {
    ButtonTest("Btn UP Pressed"); 
    Serial.printf("Btn UP Pressed");
    // Toio Core Cube と BLE 接続中かどうかをチェック
    if (toiocore->isConnected()) {
      // toiocore->disconnect(); // 接続中なら切断
    } else {
      toiocore->connect(); // 切断中なら接続
      toiocore->playSoundRaw(CHARUMERA_DATA, CHARUMERA_LEN);

    }
    // return;
  }

  // // Toio core Cube が未接続なら loop () を抜ける
  // if (!toiocore->isConnected()) {
  //   return;
  // }

  // if (M5.BtnUP.wasPressed())
  // {
  //     ButtonTest("Btn UP Pressed");  // Scroll wheel up. 
  //     Serial.printf("Btn UP Pressed");
  // }
  if (M5.BtnMID.wasPressed())
  {
    ButtonTest("Btn MID Pressed"); 
    Serial.printf("Btn MID Pressed");
    if (light_on) {
      toiocore->turnOffLed(); // 点灯中なら消灯
      toiocore->controlMotor(false, 10, false, 10, 1000);
      delay(1000);
      toiocore->controlMotor(false, 20, false, 20, 1000);
      delay(1000);
      toiocore->controlMotor(false, 30, false, 30, 1000);
      delay(1000);
      toiocore->controlMotor(false, 40, false, 40, 1000);
      delay(1000);
      toiocore->controlMotor(false, 50, false, 50, 1000);
      delay(1000);
      toiocore->controlMotor(false, 60, false, 60, 1000);
      delay(1000);
      toiocore->controlMotor(false, 70, false, 70, 1000);
      delay(1000);
      toiocore->controlMotor(false, 80, false, 80, 1000);
      delay(1000);
      toiocore->controlMotor(false, 90, false, 90, 1000);
      delay(1000);
      toiocore->controlMotor(false, 100, false, 100, 1000);
      delay(1000);
      toiocore->controlMotor(false, 10, false, 100, 1000);
      delay(1000);
      toiocore->controlMotor(false, 100, false, 100, 1000);
      delay(1000);
      toiocore->controlMotor(false, 10, false, 100, 1000);
      delay(1000);
      toiocore->controlMotor(false, 100, false, 100, 1000);
      delay(1000);
      toiocore->controlMotor(false, 100, true, 100, 4500);
      delay(3000);
      toiocore->controlMotor(true, 100, false, 100, 4500);
    } else {
      // 2 秒間だけ右に曲がりながら進む (左右のモーターを個別に制御)
      toiocore->turnOnLed(255, 0, 0); // 消灯中なら赤で点灯
      toiocore->controlMotor(true, 10, true, 10, 1000);
      delay(1000);
      toiocore->controlMotor(true, 20, true, 20, 1000);
      delay(1000);
      toiocore->controlMotor(true, 30, true, 30, 1000);
      delay(1000);
      toiocore->controlMotor(true, 40, true, 40, 1000);
      delay(1000);
      toiocore->controlMotor(true, 50, true, 50, 1000);
      delay(1000);
      toiocore->controlMotor(true, 60, true, 60, 1000);
      delay(1000);
      toiocore->controlMotor(true, 70, true, 70, 1000);
      delay(1000);
      toiocore->controlMotor(true, 80, true, 80, 1000);
      delay(1000);
      toiocore->controlMotor(true, 90, true, 90, 1000);
      delay(1000);
      toiocore->controlMotor(true, 100, true, 100, 1000);
      delay(1000);
      toiocore->controlMotor(true, 100, true, 10, 1000);
      delay(1000);
      toiocore->controlMotor(true, 100, true, 100, 1000);
      delay(1000);
      toiocore->controlMotor(true, 100, true, 10, 1000);
      delay(1000);
      toiocore->controlMotor(true, 100, true, 100, 1000);
      delay(1000);
    }
    light_on = !light_on;
  }
  if (M5.BtnDOWN.wasPressed())
  {
    ButtonTest("Btn DOWN Pressed"); 
    Serial.printf("Btn DOWN Pressed");
    if (light_on) {
      toiocore->controlMotor(false, 100, false, 100, 500);
      delay(500);
      toiocore->controlMotor(true, 100, true, 5, 1000);
    } else {
      // 2 秒間だけ右に曲がりながら進む (左右のモーターを個別に制御)
      toiocore->controlMotor(true, 100, true, 100, 500);
      delay(500);
      toiocore->controlMotor(false, 5, false, 100, 1000);
    }
    light_on = !light_on;
  }
  if (M5.BtnPWR.wasPressed()) {  // Right button press.  
      ButtonTest("Btn PWR Pressed");
      Serial.printf("Btn PWR Pressed");
      delay(3000);
      M5.shutdown();  // Turn off the power, restart it, you need to wake up
                      // through the PWR button.
  }
  // // スロットル
  // int8_t throttle = jy;
  // if (throttle > 100) {
  //   throttle = 100;
  // }
  // if (throttle < -100) {
  //   throttle = -100;
  // }
  // // 最大速度が速すぎて操作しづらいので値を 2/3 にする
  // throttle = (float)throttle * 2 / 3;

  // // ステアリング
  // int8_t steering = (abs(jx) < 10) ? 0 : jx; // 10 ほど遊びを入れる
  // if (steering > 100) {
  //   steering = 100;
  // }
  // if (steering < -100) {
  //   steering = -100;
  // }
  // // 左右の動きの反応が敏感すぎるので値を 2/3 にする
  // steering = (float)steering * 2 / 3;

  // toiocore->drive(throttle, steering);
  M5.update(); 
}

外観

全体像まえがわ

全体像うしろがわ

後ろ

ひだり

みぎ

見やすく

動いているようす

https://youtube.com/shorts/vWzgr72ZSCY?feature=share

ストーリー
  • もともとM5Stack CoreInk と toioは接続したいと思っていましたが、技術的なスキルが足りずに時間だけが過ぎていました。
  • 今回、このコンテストを機に、ArduinoIDEで制御することで、2機の接続が可能になった次第です。
  • そのため、スクリプト的なものはサンプルの組み合わせでしかありませんが、toioコアキューブのLEGOによる物理的な姿勢制御は、取り組んでいて非常に楽しいものでした。
  • LEGOによる物理的な制御は、正解はないものなので、一期一会な感覚でこの作品をつくりました。
メンバー
  • user
    うのっち🍝 @sanpeita

関連イベント
  • event M5Stack Japan Creativity Contest 20242024-06-01 開催
関連リンク

同じニオイがする作品
  • event 喋ると光るヘルメット
  • event M5bitLess label & data extension
  • event PAINTAR
  • event 陣痛共有デバイス「Happy Pain」

Proto lovers ♥
user

イベントまとめ

コンテストまとめ

作品を登録しよう

モノづくりしている人に、つくった作品を見てもらえ、リアクションがもらえるかも?

close

目次


Proto lovers ♥
user