Kick Out the World

技術的なメモとかポエムを書きます。

ModdableのModでスタックチャンの拡張機能を作る

本家スタックチャンのファームウェアはModdableを使って作成されていますが、独自機能の実装はModという仕組みを使って追加することができます。 Modを使うことでファームウェア自体の再焼き込みを行わずに独自機能のみを差し込むことができます。

github.com

今回はスタックチャンのModに必要な実装を確認してきます。

とりあえずModを動かしてみる

公式のリポジトリにいくつかModが用意されていますが、特に対向サーバ等の準備が不要な look_at_target を動かしてみます。 ※筆者はM5Gobottom版のケース+M5satck を使用していますが、ソフトウェア的には違いは無いです。

cd /path/to/stack-chan/firmware/mods/look_at_target
mcrun -d -m -p esp32/m5stack

mcconfigを使ったファームウェアビルド+焼き込みと異なりあっという間にInstalling mod...complete と表示され、スタックチャンが左右に一定周期で首振りするようになります。

最小構成で動かしてみる

適当にディレクトリを作成し、必要なファイルは以下2つを用意します。

通常のModdableアプリケーション同様に使用するModuleやResourceの定義をしますが、今回は特に何も足さないので、Modとして動かすための最小の以下とします。

{
    "modules": {
        "*": "./mod"
    }
}
  • mod.js

ここにModのロジックを記載します。まずは、スタックチャンのファームウェア(main.ts)から呼び出される関数を用意します。 これらの関数を用意しなければmain.ts側でデフォルトの動作が設定されます。

// Modの読み込み後に呼ばれる
function onLaunch() {
  trace(`[Mod_test]onLaunch\n`);
  // Avatarを含むApplicaitonを返す(返さない場合はmain.ts側でデフォルトで生成される)
}

// Robot(Servo等の制御モジュール)の初期化後に呼ばれる
function onRobotCreated(r) {
  trace(`[Mod_test]onRobotCreated\n`);
}

// M5stackの物理ボタンが謳歌されると呼ばれる(Core2は非対応)
function onButtonChange(btn,released) {
  trace(`[Mod_test]onButtonChange btn:${btn} / pressed:${released}\n`);
}

export default {
  onLaunch,
  onRobotCreated,
  onButtonChange,
  autoLoop: false
}

ディレクトリに移動し、同様にmcrun でインストールすると起動します。 物理ボタンA,B,Cを順番に押下したときのxsbugのログ。

modules of mod: ["mod","check","mod/config"]
modules of host: ["pins/digital","ili9341","m5button","time","timer","Resource","mdns","dns","modules","wifi","socket","net","sntp","websocket","base64","logical","crypt","tts-remote","tts-local","http","commodetto/Bitmap","pins/digital/monitor","pins/audioout","setup/target","m5stack/screen","commodetto/Poco","commodetto/parseBMP","commodetto/parseRLE","piu/All","piu/Timeline","piu/CombTransition","piu/MC","piu/WipeTransition","setup/piu","dns/parser","dns/serializer","pins/servo","serial","init-network","speeches","mc/config","avatar","balloon","emoticon","marquee-label","rs30x","main","robot","rs30x-driver","sg90-driver"]
[Mod_test]onLaunch
[Mod_test]onRobotCreated
[Mod_test]onButtonChange btn:A / released:0
[Mod_test]onButtonChange btn:A /released:1
[Mod_test]onButtonChange btn:B / released:0
[Mod_test]onButtonChange btn:B / released:1
[Mod_test]onButtonChange btn:C / released:0
[Mod_test]onButtonChange btn:C /released:1
ちょっと改良してスタックチャンを動かす
import { Target } from 'robot'

let robot;
let panAngle;
let tiltAngle;

// Modの読み込み後に呼ばれる
function onLaunch() {
  trace(`[Mod_test]onLaunch\n`);
  // Avatarを含むApplicaitonを返す(返さない場合はmain.ts側でデフォルトで生成される)
}

// Robot(Servo等の制御モジュール)の初期化後に呼ばれる
function onRobotCreated(r) {
  trace(`[Mod_test]onRobotCreated\n`);
  panAngle = 90;
  tiltAngle = 90;

  robot = r;
  robot._driver._pan.write(panAngle)
  robot._driver._tilt.write(tiltAngle)
}

// M5stackの物理ボタンが謳歌されると呼ばれる(Core2は非対応)
function onButtonChange(btn, released) {
  trace(`[Mod_test]onButtonChange btn:${btn} / pressed:${released}\n`);

  if (released) {
    switch (btn) {
      case "A":
        panAngle -= 10;
        break;
      case "B":
        panAngle += 10;
        break;
      case "C":
        panAngle = 90;
        break;
    }
    robot._driver._pan.write(panAngle)
    robot._driver._tilt.write(tiltAngle)

    trace(`panAngle:${panAngle} / tiltAngle:${tiltAngle}\n`);
  }
}

export default {
  onLaunch,
  onRobotCreated,
  onButtonChange,
  autoLoop: false
}

物理ボタン少し押しづらいですが、押下することで左右方向に動くようになりました。 顔の部分(Avatar)の制御をする場合は、onLaunch内でApplicationを作成して制御ができます。