M5Scratchに関するなにか
https://gyazo.com/40d5e93fe01a61260924513c4085c1b0
はじめに
背景は…
金のやつ、欲しかったorz
お品書き
M5Scratchの拡張
イケてるデモを作る
LovyanGFX対応
ボード選択の結果を利用した多機種対応
M5StickC
ATOM Matrix
色々な無線LANの設定方法
LovyanLauncher
SmartConfig
ソースに直書き(network.h)
M5Stack GrayでIMUの地磁気センサーを使う
自己紹介
以下を参照してください。
M5Scratch=M5S*+Scratch
M5Scratchは、M5ファミリーのボード(M5S*)と、Scratch1.4でインタラクションするためのArduino IDEプログラムです。
https://www.youtube.com/watch?v=zzp7oRjX_WU
table:ボードサポート状況
Support LCD LED IMU Keyboard 備考
M5Stack Basic o o None x x
M5Stack Gray (Faces) o o None o (9axis) o (Faces)
M5StickC o o o (Red LED) o (6axis) x
M5StickC plus o o o o (6axis) x
ATOM Matrix o x o (Matrix) o (6axis) x
(ATOM Lite) △ x - (RGB LED) x x headerの切り分け不可
Wio Terminal x ? ? ? x
みんなのM5Stack自慢大会発表時点で残っていた問題点
みんなのM5Stack自慢大会発表時点で残っていた問題点には、以下のようなものがあります。
ネットワーク関連(無線LAN設定とScratch動作PCのIPアドレス)の設定をnetwork.hに書かなければならない
code:network.h
const char* ssid = "SSID";
const char* password = "PASSWORD";
const char* host = "Scratch Host IP";
無線LANの設定がいけてない
Scratchが動いているホストIPの設定がいけてない
デモ環境で、無線LAN環境が普段と異なり、DHCPでホストIPが変わる場合、Arudino IDEで再コンパイルが必要
コンパクトな無線LAN AP機器などを使えば吸収はできなくもない (おまけ参照)
新しい機器への対応
M5StickC/Plus
ATOM Matrix/Lite
Wio Terminal
しょぼいデモをなんとかしたい
M5Scratchを拡張する
M5Scratchを拡張するためには、Arduino IDEでコードを変更する必要があります。
M5S*側からScratchへ情報を送るには、以下の関数を使います。
それぞれ、Scratch Remote Sensor Protocolのbroadcast(broadcast())とsensor-update(sensor_update())に対応しています。
broadcast(String msg):メッセージstringを送る
sensor_update(String varName, String varValue): 変数varNameの値varValueを送る
code:M5Scratch.ino
// ボタンAが押されたら、メッセージBtnAを送る
if (M5.BtnA.isPressed()) {
broadcast(client, "BtnA");
}
// 0-255の乱数を変数vとして送る
sensor_update(client, "v", String(random(0, 255)));
逆に、Scratchから送った情報をM5S*側で受け取るためには、以下のようにします。
現在は、switch文で場合分けをしているため、Scratch側からの変数名は1文字しか使えません。
code:M5Scratch.ino
if (msg.startsWith("broadcast") == true) {
// broadcast
msg.replace("broadcast ", "");
msg.replace("\"", "");
M5.Lcd.println("broadcast:\"" + msg + "\""); // LCDにメッセージ内容を表示
} else if (msg.startsWith("sensor-update")) {
(snip)
// sensor-update
if (msg.startsWith("r") == true) {
r = constrain(int(getValue('r', msg).toFloat()), 0, 255);
} else if (msg.startsWith("g") == true) {
(snip)
LovyanGFXでイケてるデモ
これまでのデモでは、M5S*のディスプレイに表示しているのは、Scratch側から送られた(r,g,b)値に基づいて、M5S*の背景色を変えるだけの地味なものでした。
これを、さらにx,y座標や向いている方法t、キャラクタの拡大率zなどを追加して、画面にキャラクタが表示されるようにしてみました。
LovyanGFXは、ESP32用のイケてるグラフィックスライブラリです。
以下のような特徴があります。
高速
高機能
たくさんのデバイスに対応
このサイトには、に、任意の大きさのBMP,JPEG,PNGから、C用のイメージデータを作ってくれる機能もあります。 LovyanGFXを使ってM5S*側でのイケてるデモを作ります。
キャラクタを動かす例は、サンプル例 → LovyanGFX → Sprite → MovingIconsを使うのが一番良いと思います。 今回は、スプライトをScratchCatに、ScratchCatの(x,y)座標と角度(t)とズーム倍率(z)、右上の丸の色(r,g,b)をM5S*側で受け取るように作りました。 https://gyazo.com/aa0275f36c90597d0752f850be7a00d2
https://www.youtube.com/watch?v=Y_04pz9JAas
無線LANの設定について
無線LANの設定ですが、以下のような方法が考えられます。
LovyanLauncher対応にする
SDなどの不揮発性の領域に設定情報を書き込んでおく
プログラム中に直書きする
LovyanLauncherによる方法では、LovyanLauncherの無線LAN設定で設定した無線LAN設定(SSID/PASSWORD)が、アプリケーション側にも渡されるようになっています。
このため、LovyanLauncherの無線LAN設定が一度すんでしまうと、再起動後も自動的にこの設定が渡されます。
SmartConfigを使う方法では、スマートフォンから、設定用アプリでWiFi情報を設定することになります。
設定した情報をどこかにキャッシュしていない状況では、起動時に毎回設定を行う必要があります。
SDなどを利用する方法は、上記の方法と併用して使うことができます。
ただ、SDが存在しないデバイスもあるため、一概にこの方法が取れるとは限りません。
M5S*の不揮発性領域に書き込んでおく方法も考えられますが、これを変更するには他のコンピュータで書き換えればいいSDよりも敷居が上がってしまいます。
LovyanLauncher対応
以下のような特徴があります。
SDカードに格納されたアプリを実行する(Launcher)
WiFiの設定機能: アプリにもこの情報が提供される
バイナリのダンプなどなど
以下のような3ステップで、対応は完了です。
#include <M5StackUpdater.h>
WiFi設定で、ssidとpasswordを渡さないように変更
BUTTON Aを押しながら起動すると、LovyanLauncherを読み出すように変更
code:M5Scratch.ino.diff
--- a/M5Scratch/M5Scratch.ino
+++ b/M5Scratch/M5Scratch.ino
@@ -46,6 +46,7 @@
+#include <M5StackUpdater.h>
#if defined(M5STACK_MPU9250) @@ -60,8 +61,6 @@ MPU9250 IMU;
/*
network.h contains network information below:
- const char* ssid = "SSID";
- const char* password = "PASSWORD";
const char* host = "Scratch Host IP";
*/
@@ -73,7 +72,7 @@ void WiFiSetup() {
wifisetup:
Serial.println("Wifi begin.");
WiFi.disconnect();
- WiFi.begin(ssid, password);
+ WiFi.begin();
Serial.println("End of Wifi begin.");
int c = 0;
@@ -93,6 +92,15 @@ void setup() {
M5.begin();
delay(100);
+#if defined(ARDUINO_M5Stack_Core_ESP32)
+ // for LovyanLauncher
+ if (digitalRead(BUTTON_A_PIN) == 0) {
+ Serial.println("Will Load menu binary");
+ updateFromFS(SD);
+ ESP.restart();
+ }
+#endif
+
// Init Serial
Serial.begin(115200);
delay(10);
無線LANの設定のためのSmartConfig化
設定情報をどこかにキャッシュしていない状況では、毎回SmartConfigする必要があるために、それはそれでめんどくさいことになります。
各種M5Stackシリーズ対応
Arduino IDEでは、ツール→ボードを選択すると自動的にARDUINO_*マクロが定義されます。
これを使って、#ifdefやdefined()などで場合わけが可能になります。
現状では、以下のようにboards.txtに、*.build.boardとして定義されています。
code:shell
% grep build.board ~/Library/Arduino15/packages/m5stack/hardware/esp32/1.0.7/boards.txt|grep M5
m5stack-core-esp32.build.board=M5Stack_Core_ESP32
m5stack-fire.build.board=M5STACK_FIRE
m5stick-c.build.board=M5Stick_C
m5stick-c-plus.build.board=M5Stick_C_Plus
m5stack-atom.build.board=M5Stack_ATOM
m5stack-core2.build.board=M5STACK_Core2
m5stack-tough.build.board=M5STACK_TOUGH
m5stack-timer-cam.build.board=M5Stack-Timer-CAM
m5stack-coreink.build.board=M5Stack_CoreInk
m5stack-m5paper.build.board=M5STACK_Paper
% grep build.board ~/Library/Arduino15/packages/Seeeduino/hardware/samd/1.8.1/boards.txt|grep TERMINAL
seeed_wio_terminal.build.board=WIO_TERMINAL
この情報をまとめると、以下の表のようになります。
table:ボード選択で定義されるARDUINOマクロ
M5Stack Basic, Gray ARDUINO_M5Stack_Core_ESP32 M5Stack.h
M5Stack Fire ARDUINO_M5STACK_FIRE M5Stack.h
M5StickC ARDUINO_M5Stick_C M5StickC.h
M5StickC plus ARDUINO_M5Stick_C_Plus M5StickCPlus.h
ATOM Matrix ARDUINO_M5Stack_ATOM M5Atom.h
ATOM Lite (ARDUINO_M5Stack_ATOM) M5Atom.h ATOM Matrixと切り分けできない
M5Stack Core 2 ARDUINO_M5STACK_Core2 M5Core2.h
Wio Terminal ARDUINO_WIO_TERMINAL
例えば、ヘッダファイルの読み込み部分では、以下のようなコードを書くことになります。
code:M5Scratch.ino
#if defined(ARDUINO_M5Stick_C) // M5StickCの場合 #elif defined(ARDUINO_M5Stack_Core_ESP32) // M5Stackの場合 #elif defined(ARDUINO_M5Stack_ATOM) // ATOM Matrixの場合 画面まわりのコードを共通化する
画面まわりのコードでは、LCDの横と縦のピクセル数を表すM5.Lcd.width()とM5.Lcd.height()を使って画面の大きさの違いを吸収可能です。
画面の向きをLandscapeやPortrateに変更する場合には、M5.Lcd.setRotation()を適切に設定します。
フォントのサイズの吸収に関しては、良い方法がわからないので、固定値で行っています。
M5StickC対応
M5StickCに対応するためには、以下のようなことを行いました。
マクロARDUINO_M5Stick_Cで場合分けを行う
#include <M5StickC.h>
BUTTONスイッチ関連の処理がM5Stackとは違う
LCDの出力方向を考えて、必要であれば変更する(M5.Lcd.setRotation())
フォントの大きさを変更(M5.Lcd.setTextSize())
LEDの対応の追加(digitalWrite(M5_LED, led))
code:M5Scratch.ino.diff
// M5StickC用ヘッダーファイルの読み込み
+#if defined(ARDUINO_M5Stick_C)
+#include <M5StickC.h>
(snip)
+#endif
(snip)
// BUTTONスイッチの初期化
+#if defined(ARDUINO_M5Stick_C)
pinMode(M5_BUTTON_HOME, INPUT);
pinMode(M5_BUTTON_RST, INPUT);
+#endif
(snip)
// BUTTONスイッチの利用
+#if defined(ARDUINO_M5Stick_C)
if (digitalRead(M5_BUTTON_HOME) == LOW) {
broadcast(client, "BtnA");
}
if (digitalRead(M5_BUTTON_RST) == LOW) {
broadcast(client, "BtnB");
}
+#endif
// LCDの出力方向変更
#if defined(ARDUINO_M5Stick_C) + M5.Lcd.setRotation(3);
M5.Lcd.setTextSize(1);
#elif defined(ARDUINO_M5Stack_Core_ESP32) (snip)
// 出力する文字の大きさを小さく
+#if defined(ARDUINO_M5Stick_C)
+ M5.Lcd.setTextSize(1);
+#elif defined(ARDUINO_M5Stack_Core_ESP32)
+ M5.Lcd.setTextSize(2);
+#endif
(snip)
// 変数lによるLEDの点灯/消灯
case 'l':
int led = int(getValue('l', msg).toFloat());
+#if defined(ARDUINO_M5Stick_C)
digitalWrite(M5_LED, led);
+#endif
M5StickC plusは、ARDUINO_M5Stick_Cのような専用のマクロ定義が無いので場合分けできませんが、ヘッダとしてM5StickCPlus.hを使う必要があります。
ATOM Matrix対応
ATOM Matrixへの対応は以下のように行いました。
マクロARDUINO_M5Stack_ATOMで場合分けを行う
#include <M5Atom.h>
M5.begin(true, false, true)でないとLEDは使えない(LED利用選択の第3引数デフォルトはfalse)
ディスプレイはないので当たり前だけど、M5.Lcd.*は使えない
LEDの輝度はあまり高くしない方が良いらしい
とりあえず、Max0x20にした
code:M5Scratch.ino.diff
// ヘッダファイルの切り替え
+#elif defined(ARDUINO_M5Stack_ATOM)
+#include <M5Atom.h>
+#endif
(snip)
// 初期化
+#if defined(ARDUINO_M5Stack_ATOM)
+ M5.begin(true, false, true);
+#else
M5.begin();
+#endif
(snip)
// LCD(は無いので)に出力しない
+#if !defined(ARDUINO_M5Stack_ATOM)
M5.Lcd.println("WiFi connected.");
+#endif
(snip)
// 全てのLEDに、LED保護のためにRGBを抑制した出力を行う
+#if defined(ARDUINO_M5Stack_ATOM)
+ // Conver raw rgb(0-255) to led rgb(0-0x20)
+ int rl = constrain(int((r / 255.0) * 0x20), 0, 0x20);
+ int gl = constrain(int((g / 255.0) * 0x20), 0, 0x20);
+ int bl = constrain(int((b / 255.0) * 0x20), 0, 0x20);
+ Serial.println("LED RGB:(" + String(rl) + ", " + String(gl) + ", " + String(bl) + ")");
+ setBuff(rl, gl, bl);
+ M5.dis.displaybuff(DisBuff);
+#else
M5.Lcd.fillScreen(uint16_t (((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3) ));
M5.Lcd.println("RGB:(" + String(r) + ", " + String(g) + ", " + String(b) + ")");
+#endif
(snip)
ATOM Liteも、専用のマクロ定義が無いので、場合分けできません。
M5Stack Grayで地磁気センサーを使う
M5Stack Grayなどの9軸IMUモデルでは、3軸の地磁気センサーが利用可能です。
6軸IMUのMPU6886互換で利用する時は、M5StickCと同じM5.Imu.*インタフェース経由で6軸のIMUが利用可能ですが、このインタフェースでは地磁気センサーを扱うことができません。
地磁気センサーを使う場合は、MPU9250を使って自力で頑張る必要があります。
code:M5Scratch.ino
// 必要な.hファイルを読みこむ
MPU9250 IMU;
(snip)
// 初期化
IMU.MPU9250SelfTest(IMU.SelfTest);
IMU.calibrateMPU9250(IMU.gyroBias, IMU.accelBias);
IMU.initMPU9250();
IMU.initAK8963(IMU.magCalibration);
(snip)
// 地磁気データの読み込み
IMU.readMagData(IMU.magCount);
IMU.getMres();
mx = (float)IMU.magCount0 * IMU.mRes; my = (float)IMU.magCount1 * IMU.mRes; mz = (float)IMU.magCount2 * IMU.mRes; 実際に地磁気センサーを利用するためには、初期化時にキャリブレーション(補正)が必要となります。
M5Scratchでキャリブレーションの実装をどのようにするのかは、考えているところです。
TODOとか、いくすえについて
これから作業する必要があるのは、以下のようなことです。
なんか、ヒントがあったら教えてください(_o_)
ScratchホストのIP設定の改善
ソースファイルに書くのはいけてない(host変数)
code:network.h
const char* ssid = "SSID";
const char* password = "PASSWORD";
const char* host = "Scratch Host IP";
初期化時に入力する?: 入力がめんどくさそう
SDに保存した設定見る?: 対応できない機種がある
LovyanLauncher使ってる場合、どうせSDはあるから/M5Scratch.txtなんかで設定する
他の不揮発性メモリを使う?
無線LAN設定をどうするのがいいのか?
現状では、LovyanLauncher、SmartConfig、直書きの方法が取れる
他の方法は? 初期化時にUI使って手入力? 不揮発性メモリに記録?
他ボード対応
Wio Terminal対応
ATOM Lite対応 (vs ATOM Matrix): どうやって、ATOM Matrixの場合と切り分けるか?
2021/02/24時点で、boards.txtにM5StickC Plus対応が入っているので、場合分けが可能になりました。M5StickC Plus対応 (vs M5StickC): どうやって、M5StickCの場合と切り分けるか?
なんちゃってheadingの改善: 初期化時に地磁気のカリブレーションが必要
M5Scratch側では、1文字の変数名しか使えない: switch文を使って分岐しているためでifで分岐すればいいだけ
ScratchからM5S*へのデータのパース部分がイケてない
Scratch 3.0対応
おわりに
何回か発表とかしていたら、みなさまのおかげで、えらく遠いところにきてしまいました。
これからも、懲りずに色々教えてください(_o_)
M5Scratchとむとうのこれからにご期待ください。
おまけ:きしかた
おまけ:デモ用のネットワーク環境
PQI Air Penみたいな、上流用WiFi接続と、無線LAN APとして働くものがあれば、インターネット接続もでき、Scratch側のIPを固定した構成が可能です。 https://gyazo.com/88b845a823538f452fb7513b2bd4b05a