M5StackとScratchであそぼ
https://gyazo.com/b622ce93c74c05f56a953c98c5b7ad33
今日のデモ
https://www.youtube.com/watch?v=VftcIWEwbqA
M5Stackに顔を出して、ボタンで操作してみる
M5StackとScratch 1.4をつないで、データをやり取りしてみる
Scratch→M5Stack
RGBの色データ(r,g,b)を、M5Stackの画面に表示してみる
文字列sを、M5Stackの画面に表示してみる
M5Stack→Scratch
testメッセージを受け取る
乱数vをScratchでキャラクターの大きさに使う
M5Stackってなあに?
https://gyazo.com/ff5e6a7ed02467a951894afad7b307c1
M5Stack = Modular 5×5cm Stackable
Arduinoの仲間(ESP32)で、M5Stack Basicの場合、 + バッテリー : 別に電源を用意しなくて良い
+ カラーLCD画面(320x240): 画像を表示できる
+ 3つのボタン 色々な機能を作れる
+ 無線通信(WiFi, Bluetooth): 無線通信を使ってパソコンなどとデータのやりとりができる
+ microSDスロット: データを保存したり、読み込んだりできる
+ スピーカー: 音が出せる
+ かっこいいケース: 5x5cmの大きさのケース
(+商品が入っているケース) ケーブルなども含めて、ピッタリのサイズで、片付けにも最適
拡張性がある
様々なモジュール(拡張ボード)がある
どんどん積んでいくことができる
簡単につながるユニットもある
プログラミング
Arduino IDE
M5Cloud (MicroPython, Lua)
https://gyazo.com/1bda3f7c3106b86afb6f0e14b9c91c29
M5Stackいろいろ
https://gyazo.com/0526fd5004b2e79f8448a2433e1bb7e5
左:M5Stack Gray, 右:M5Stack Basic
9軸(加速度,ジャイロ,磁気)のセンサーが追加
9軸IMU+充電用ポッド
LEGOとつながる
Grayにキーボード、電卓キー、ゲームキーの3種のフェース(face)と充電ドック付き
LEGOとつながる
センサーが6ユニット付属
M5Stick(C)ももうすぐ発売
Arduino IDEでのM5Stackのプログラミングの最初の一歩
初期化は、M5.begin()するだけ
code:setup.ino
void setup() {
M5.begin();
}
loopの度にM5.update()する
code:update.ino
void loop() {
:
M5.update();
}
画面の背景色を変えて、画面をクリアしたい
code:fillscreen.ino
M5.Lcd.fillScreen(WHITE);
好きな場所に文字を出したい
code:print.ino
M5.Lcd.setCursor(10, 10);
M5.Lcd.setTextSize(2);
M5.Lcd.println("Welcome to M5Stack!!");
microSDに記録されている画像を表示したい
画像フォーマットに注意が必要: Jpgの場合、320x240のtrue color
code:jpg.ino
M5.Lcd.drawJpg(SD, "/NARA.jpg");
ボタンAから入力する
isPressed(), isReleased(), wasPressed(), wasReleased()などがある
code:button.ino
if (M5.BtnA.isPressed()) {
M5.Lcd.println("Button A pressed");
}
例:表情が変わる顔を作ろう!!
https://gyazo.com/c82558578379ce47934622c47c1d7347
操作
左右のボタンで、目がそっち方向に動く
真ん中ボタンで、口の上下が変わる
code:face.ino
void setup() {
M5.begin();
delay(100);
M5.Lcd.fillScreen(WHITE);
M5.Lcd.println("Welcome to my face!!");
}
int offset = 0;
int laugh = 1;
int lastLaugh = laugh;
void loop() {
M5.update();
int isUpdate = 0;
if (M5.BtnA.isPressed()) {
offset -= 1;
isUpdate = 1;
}
if (M5.BtnB.isPressed())
laugh = 0;
else
laugh = 1;
if (!(lastLaugh == laugh)) isUpdate = 1;
if (M5.BtnC.isPressed()) {
offset += 1;
isUpdate = 1;
}
if (isUpdate) M5.Lcd.fillScreen(WHITE);
M5.Lcd.fillCircle(EYE_X + offset * EYE_STEP, EYE_Y, EYE_R, BLACK);
M5.Lcd.fillCircle(LCD_WIDTH - EYE_X + offset * EYE_STEP, EYE_Y, EYE_R, BLACK);
if (laugh)
M5.Lcd.fillTriangle(MOUSE_X, MOUSE_Y, MOUSE_X - MOUSE_SIZE, MOUSE_Y + MOUSE_SIZE, MOUSE_X + MOUSE_SIZE, MOUSE_Y + MOUSE_SIZE, RED);
else
M5.Lcd.fillTriangle(MOUSE_X, MOUSE_Y + MOUSE_SIZE, MOUSE_X - MOUSE_SIZE, MOUSE_Y, MOUSE_X + MOUSE_SIZE, MOUSE_Y, RED);
M5.Lcd.setCursor(0, 0);
M5.Lcd.print("offset:" + String(offset));
lastLaugh = laugh;
}
例:画像を表示する
https://gyazo.com/28b41e173679529fd796b1730576af3f
microSDに入った画像を表示する
画像ファイルフォーマットは、JPG, BMPが使える
code:nara.ino
void setup() {
// Init M5
M5.begin();
M5.Lcd.clear();
M5.Lcd.drawJpgFile(SD, "/NARA.jpg");
}
例:ScratchからM5Stackにデータを送る
https://gyazo.com/ceb5747d053aad963f8e7cf65563b458
Scratch側からM5Stack側へデータを送ります
Scratch側では…
変数r,g,bを0-255の乱数にします
変数vを0-1000の乱数にします
https://gyazo.com/1d3f89e5d8de7cde769757f4a8319c86
M5Stack側では…
色指定の赤(r:red), 緑(g:green), 青(b:blue)を受け取って背景の色をその色にして、その値を文字で表示します
文字列(s:string)を受け取って、それを画面の真ん中ほどに表示します
code:初期化;M5Stack_ScratchRemoteSensor_receive_only.ino
const char* ssid = "SSID";
const char* password = "PASSWORD";
const char* host = "Scratch_Host_IP";
const int Port = 42001;
void setup() {
// Init M5
M5.begin();
delay(100);
// Init Serial
Serial.begin(115200);
delay(10);
// We start by connecting to a WiFi network
M5.Lcd.println("Welcome to Scratch Remoto Sensor!!");
M5.Lcd.print("Connecting to ");
M5.Lcd.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
M5.Lcd.print(".");
}
M5.Lcd.println("");
M5.Lcd.println("WiFi connected.");
M5.Lcd.print("My IP:" + WiFi.localIP());
M5.Lcd.println("Host IP:" + String(host));
delay(5000);
}
code:getValue.ino
String getValue(char name, String msg) {
msg.replace(String(name) + " ", "");
Serial.println("{" + String(name) + ":" + String(msg) + "}");
return msg;
}
code:ループ;M5Stack_ScratchRemoteSensor_receive_only.ino
void loop() {
int r = 0, g = 0, b = 0;
String s;
char* str;
M5.update();
delay(10);
// Use WiFiClient class to create TCP connections
WiFiClient client;
if (!client.connect(host, Port)) {
Serial.println("connection failed");
return;
}
Serial.print("create tcp ok\r\n");
// Read all from server and print them to Serial.
uint32_t len = 0;
String msg = "";
char *c;
//// Receive msg
client.setTimeout(100);
len = client.readBytes(buffer, sizeof(buffer));
while (len > 0) {
M5.Lcd.setCursor(0, 0);
Serial.print("Received:[");
for (uint32_t i = 0; i < len; i++) {
Serial.print((char)bufferi); if (i >= 4) { // Skip 4 byte message header
}
}
Serial.print("]\r\n");
while ((!msg.startsWith("broadcast") && !msg.startsWith("sensor-update")) && msg.length() > 0 ) {
msg.substring(1);
}
if (msg.startsWith("broadcast") == true) {
// message
msg.replace("broadcast ", "");
msg.replace("\"", "");
Serial.println("{broadcast:" + msg + "}");
M5.Lcd.setTextSize(3);
M5.Lcd.println("{broadcast:" + msg + "}");
} else if (msg.startsWith("sensor-update")) {
// value
msg.replace("sensor-update ", "");
msg.replace("\"", "");
msg.trim();
while (msg.length() > 0) {
msg.trim();
switch (msg.charAt(0)) {
case 'r':
r = int(getValue('r', msg).toFloat());
break;
case 'g':
g = int(getValue('g', msg).toFloat());
break;
case 'b':
b = int(getValue('b', msg).toFloat());
break;
case 's':
s = getValue('s', msg);
break;
}
Serial.println("{{msg:" + msg + "}}");
// Skip var_value
while (msg.charAt(0) != ' ' && msg.length() > 0) {
msg = msg.substring(1);
}
Serial.println("{{msg2:" + msg + "}}");
}
//// Output
// RGB background
M5.Lcd.fillScreen(uint16_t (((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3) ));
M5.Lcd.setTextSize(2);
M5.Lcd.println("{RGB:(" + String(r) + ", " + String(g) + ", " + String(b) + ")}");
Serial.println("{RGB:(" + String(r) + ", " + String(g) + ", " + String(b) + ")}");
// msg
M5.Lcd.setCursor(0, 120);
M5.Lcd.setTextSize(5);
M5.Lcd.println("{s:" + s + "}");
} else {
Serial.println("NOP");
}
len = msg.length();
}
}
例:M5StackからScratchにデータを送る
https://gyazo.com/b932ca6865f6e4acd5da5b6f5121fb98
M5StackからScratchにデータを送ります
M5Stack側では…
メッセージtestを送り続ける
センサvに0-255の乱数を送り続ける
Scratch側では…
メッセージtestを受け取ると…
コスチュームを歩いているように変える
センサvの値で、Scratch Catの大きさを変える
https://gyazo.com/86b01538202c0384c248d75eb1adeb44
初期化部分は一つ前と同じ
code:tmp.ino
void loop() {
M5.update();
delay(10);
// Use WiFiClient class to create TCP connections
WiFiClient client;
if (!client.connect(host, Port)) {
Serial.println("connection failed");
return;
}
Serial.print("create tcp ok\r\n");
// broadcast
strcpy(scmd + 4, "broadcast \"test\"");
scmd3 = (uint8_t)strlen(scmd + 4); if (client.write((const uint8_t*)scmd, 4 + strlen(scmd + 4))) {
Serial.println("send ok");
} else {
Serial.println("send err");
}
// sensor-update
sprintf(scmd + 4, "sensor-update \"v\" %d", random(0, 255));
scmd3 = (uint8_t)strlen(scmd + 4); if (client.write((const uint8_t*)scmd, 4 + strlen(scmd + 4))) {
Serial.println("send ok");
} else {
Serial.println("send err");
}
}
これからどうする?
しばらくすると、ScratchとM5Stackとの間でデータのやり取りができなくなるのを直す
M5Stack Grayを使って、加速度データなどもScratchへ送りたい
micro:bitみたいに、ゲームのコントローラとして使えるかも
おわりに
M5Stackは、ただのArduinoでしょと思ってたら…
面白いデバイスが最初からついているのでけっこう遊べます
ケースなどのデザインもかわいいです
追加のセンサーなどを使うと、さらに色々できます
Scratch 1.4 遠隔センサー(Remote Sensor)と遊ぶと、もっとたのしい!!
発表やデモ、展示の感想や反省点など
まさかのデモができない
WiFiの設定がうまくいかなかった
会場WiFi: WiFi接続ができない上に、DHCPを使わずに固定でIPを設定する必要があった
スマホのデザリング: WiFi接続が確立すると、M5Stackが再起動する
やっぱり、動作確認ができたMy無線LAN環境を持参すべきだった
とりあえず、事前に用意した動画を流したが、手ブレが酷かった 発表時に、緊張からか、結構震えた