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を作成して制御ができます。

スタックチャンの顔こと moddable-avatarを動かしてみた

github.com

スタックチャンの顔部分はmoddable-avatarというm5stack-avatarのmodabble移植版から構成されています。

ということで単独でリポジトリのmain.ts動かすためのメモです。

構築

Typescriptで実装されているので、Moddableを環境構築の上、Typescriptをインストールしておく。

リポジトリをcloneしたらnpm install で開発ツール類をインストールします。moddableでのビルド自体には必須ではなさそう。

ここでビルドをすると、以下問題が発生します。

  • piuの型定義を解消できない
  • piu/Timelineの定義ファイルが不足している
  • 起動時にmainモジュールが見つからない
    • 2023/8/1時点で本件は解消されました。

manifest.json でincludeとmoduleを修正。

   "build": {},
-  "include": ["$(MODDABLE)/examples/manifest_base.json", "$(MODDABLE)/examples/manifest_piu.json"],
+  "include": ["$(MODDABLE)/examples/manifest_base.json","$(MODDABLE)/examples/manifest_piu.json","$(MODDABLE)/examples/manifest_typings.json"],
   "resources": {
     "*": ["./assets/images/*"],
     "*-alpha": ["./assets/fonts/*"]
   },
   "modules": {
-    "*": ["./main", "./avatar", "./marquee-label", "./balloon", "./emoticon"]
+    "*": ["./src/main", "./src/avatar", "./src/marquee-label", "./src/balloon", "./src/emoticon"]
   },

piu/Timelineの型定義ファイルをstack-chanのリポジトリから$(MODDABLE)/typings/piuに移動します。 https://github.com/meganetaaan/stack-chan/blob/main/firmware/typings/piu/Timeline.d.ts

これで、main.tsが動くようになります。

また、ユーザからのインタラクションも不要であれば、シミュレーターでも動作します。

Avatarクラスのパラメータ

Arguments Type Description
primaryColor color 目の色
secondaryColor color 顔の色

primaryColor : 'white', secondaryColor :'black' とすると、いつもと顔色が変わります。

Arguments Type Default value Description
gaze.x number 0 視線の座標x(setGazeで使う)
gaze.y number 0 視線の座標y(setGazeで使う)
breath number 3 呼吸してるように上下に揺れる動きの距離(実装未使用?)
eyeOpen number 0 未実装?
eyebrowOpen number 0 未実装?
mouthOpen number 0 未実装?
gazeInterval number 4000 眼振動作の有無 (おそらく2回目以降は1~3秒間隔?)
blinkInterval number 4000 瞬き動作の間隔(おそらく2回目以降は1~3秒間隔?)
autoUpdateBlink boolean true 瞬き動作の有無
autoUpdateBreath boolean true 呼吸動作の有無
autoUpdateGaze boolean true 眼振動作の有無
emotion Emotion Emotion.NEUTRAL 喜怒哀楽などの6種定義があるが未実装

delegate event

startSpeech 口をパクパク動かす

stopSpeech startSpeechの動作を止める

setGaze(x, y)  setFocusPoint(x, y)

画面の(x, y)座標方向に視線を向ける

Moddable July 12, 2021がリリースされました

github.com

XSの改善

ECMAScript® Embedded Systems API 仕様対応

SubPlatformのProvider定義追加

esp32/m5stack などSubPlatformに対してもProvider定義が追加されました。ESP32配下のSubPlatformはほとんど対応されているようです。I2CやSPIをはじめとしてSubPlatform固有のピンアサインが定義されています。 

IOクラスを用いたセンサー類のドライバー実装の追加

NTC ThermistorセンサーやAHT10が追加

対応ボード追加

SparkFun Thing Plusが追加となりました。

www.switch-science.com

音声再生

AudioOutでSBCエンコードの再生に対応

SBCとはBluetoothに使用される音声圧縮方法のことだそうです。

AudioOutで矩形波のトーン再生に対応

M5stack本家ライブラリのM5.speaker.tone相当が実現できます。

QRcodeモジュールの改善

TextDecoder, TextEncoderモジュールの追加

UTF-8バイト列と文字列の変換ができる

mcbundleのドキュメント追加

Moddable Store(?!)向けにアプリケーションをパッケージングするためのツールのようです。 パッケージングするための定義はmanifest.jsonに記載する模様。

Windowsには対応してないようなので動作確認は未実施

Moddable June 3, 2021がリリースされました

github.com

ESP32関連

ESP-IDFが4.2から4.2.1にアップデート

ESP32を使っている場合は必要となります。アップデート方法は参照

カスタムブートローダーの使用が可能に

XSの改善

今回改善されたString.fromArrayBuffer はXS独自機能のようで、BTなどの通信時にバイト列から文字列をデコードする用途に使われます。

GIPHYアプリ

contributed にGIPHYというGIF動画共有サービスから検索し表示するアプリが追加されました。動作にはGIPHYのAPIキーが必要となります。また、検索文字入力のためにタッチスクリーンも必要。

画面が少し小さくタッチスクリーンが使いずらい部分やGIF表示時固まるといった不安点な動作が見られましたがM5stackCore2でも確認ができます。元アプリはModdableTwoをベースに画面作成されています。

ECMA TC53のAPI仕様対応

HostProviderインスタンスの名称変更

ボード固有のピンアサイン等を定義したHostProviderインスタンスの名称がHostからdeviceに変わります。このHostProviderインスタンスの具体的な使い方については、後述するIOクラスを用いたドライバ実装中で確認できます。

IOクラスを用いたセンサー類のドライバー実装の追加

I2CやSMBusを使ったドライバ実装modules/drivers/sensors に追加され、examplesにも対応するプログラムがあるので、M5stackのENVⅡUnitとかでサッと動作確認が可能です。

SMBusのメソッド名がTC53の仕様に合ってないとdicussionに書いた一方で、既存のままで実装されてるけど、どうするのだろ。

新しくドライバー類を作成ないし、コントリビュートするに当たっては、IOクラスを用いたほうがよさそうだけど、まだまだ過渡期の入り口な印象です。以下感想

CRCモジュールの追加

上記のドライバー追加に合わせてチェックサムのためのモジュールが追加されています。使い方はドライバーの実装内で確認できます。

REPL機能の改善

よりNode.jsに近づいたことですが、まだ触ったことがないので試してみたい。

暗号化やTLSの改善

特になし

Moddable May 4, 2021がリリースされました

ほんの思い付きですが、月1,2回ほどあるリリースノートについて簡単にトピックスの紹介や補足でもしていこうかと思います。 あまり詳しくないor関心の薄い部分については適当な紹介となることはご了承ください

続きを読む

「リモートワークの達人」と「思考の整理学」を読んだ

「小さなチーム、大きな仕事」は読んだことがあったけど、こちらは読んだことがなかったので、読んでみた。

 

パフォーマンスを発揮して働くという点において「小さなチーム…」と共通する部分が多く、読み始めから既視感があっだけど、最後まで読み進めていくと、読んだことがないことが分かって安心?した。

 

確かに必要なことは全部書いてあった。現実に実践できるかどうかは別にして…な気持ちもあるけど、やれることからやってみたらよい、というマインド。

 

今回物理文庫本を買うに当たって、たまたま近くに陳列されていた「思考の整理学」も買って読んでみた。

 

文章を書く上で都合のよいとされる場面に「三上」という言葉があり、朝一が最も頭が整理された状態であるし、気分を変えていつもと違う場所で考えてみる、といった部分はリモートワークにもリンクする部分があり面白かった。

 

もちろん、これ以外にも創造的な思考をするために必要なことが書いてあるので、リモートワーク関係なく読んでよかった。

【Moddable】manifest.jsonで定義した値が適用されているか確認する

manifest.jsonconfigdefine といった設定値が定義されていますが、Exampleのアプリケーションを動かしてたり、新しいボード対応とかをしていると、ここで定義した値通りに動いてないのでは、みたいな場面に遭遇することが稀によくあります。

ということで、いくつか確認できたものを例に紹介する

確認する場所

mcconfig コマンドでビルドされた生成物は以下フォルダに格納されている。 ${MODDABLE}/build/tmp/$(platform)/$(subplatform)/debug/$(Application)

esp32/m5stackhelloworldアプリを動かしたなら moddable/build/tmp/esp32/m5stack/debug/helloworld

結論としてはここにあるヘッダファイルが自動生成され、ビルド時に参照される。

define

例えば以下のようにI2Cのデフォルトsda,sclピンとかが定義されていると、

"defines": {
    "i2c": {
        "sda_pin": 21,
        "scl_pin": 22
    }

mc.defines.hのヘッダファイル内で以下のような定義に自動生成される。

#define MODDEF_I2C_SDA_PIN (21)
#define MODDEF_I2C_SCL_PIN (22)

この定義はmodules/pins/i2c/esp/modI2C.cで参照されており、I2Cコンストラクタでsda,sclが指定されていない場合のデフォルト値として利用されていることがわかる。

#if defined(MODDEF_I2C_SDA_PIN) && defined(MODDEF_I2C_SCL_PIN)
    conf.sda_io_num = (-1 == config->sda) ? MODDEF_I2C_SDA_PIN : config->sda;
    conf.scl_io_num = (-1 == config->scl) ? MODDEF_I2C_SCL_PIN : config->scl;
#else

config

アプリケーションで利用する際にはimport config from "mc/config";でインポートして参照する設定値。

"config": {
    "screen": "m5stack/screen",
    "rotation": 90,
    "touch": "",
}

mc.config.jsを見たらだいだい見たまんま

mc.rotation.h

画面出力する場合は本ファイルで定義されるkPocoRotationが画面の向きを決定する。実際この値が変わると参照しているcommodettoやpiuは再ビルドが走る。

ここに適用される値は、config.rotaionが適用されるが、mcconfig-rオプションを指定していた場合はその値が優先される。

複数のmanifest.jsonで同じ定義が衝突したとき

ApplicationやPlatformやModuleごとに定義されるmanifest.jsonファイルで同じ定義がされていた場合は、(恐らく)定義が読み込まれてた順で後勝ちになっていそう。

例えばM5stackのアプリケーションでexamples/manifest_base.jsonを読み込んだ場合は、 includeで以下のように辿って読み込まれていく。

(アプリケーションのmanifest.json)
┗ $(MODDABLE)/examples/manifest_base.json
  ┗$(BUILD)/devices/esp32/manifest.json
  ┗$(BUILD)/devices/esp32/targets/m5stack/manifest.json
   ┗(以下略)

ただし、includeで先読みしてくため、下から上に向かってdeinfeやconfigは読み込まれていく(アプリケーションのmanifest.jsonでで定義したものが最終的に後勝ちになる)ので、意図しない定義値が適用されているときは、manifestの読み込み順にも注意したほうがよい。

まとめ

Platformで定義した値をSubPlatformで上書きできない点がモヤモヤする(嵌った)ところですが、意図する値となってないと思ったら値を確認してみましょう。