💫
LED Matrixで砂シミュレーション機をつくろう
以前、X(旧twitter)でみた砂シュミュレーション機に一目惚れしました。
@witnessmenowさんの公開されているFalling-Sand-Matrixを参考に、
自分でもLED Matrixと加速度センサーを使った砂シュミュレーション機を作りました。
参照プロジェクト
🔧パーツ一覧
| 機材名 | 備考 | 
|---|---|
| ESP32 DevKitC | 秋月電子 | 
| ledマトリックスモジュールパネル屋内rgb 64 x 64ドット | Amazonで購入, AliExpress等でも販売 | 
| 3軸加速度センサー - MPU6050 | Amazonで購入 | 
| モバイルバッテリ | Amazonで購入(cheero Slim 10000mAh IoT機器対応 大容量 モバイルバッテリ) | 
| ジャンパーケーブル | 適量 | 
| 木材・ネジ | 適量 - 固定用 | 
接続図
- LEDマトリクスは、下記のコネクタがあります
 
ESP32とLEDマトリクス(入力コネクタ、出力コネクタ)
| ESP32 | LED Mat. IN | LED Mat. OUT | 
|---|---|---|
| - | R1 | R0 | 
| - | G0 | R1 | 
| - | G1 | G0 | 
| - | B0 | G1 | 
| - | B1 | B0 | 
| 19 | A | - | 
| 23 | B | - | 
| 18 | C | - | 
| 5 | D | - | 
| 25 | LAT | - | 
| 16 | OE | - | 
| 14 | CLK | - | 
| 13 | R0 | - | 
| GND | GND | - | 

電源周り
モバイルバッテリーからの5V/GNDをLEDマトリクスとESP32に接続します。

ESP32と加速度センサー
| ESP32 | MPU6050 | 
|---|---|
| 3V3 | VCC | 
| 22 | SCL | 
| 21 | SDA | 
| GND | GND | 

💻環境
Arduino IDEを使用します。
- Arduino IDE
- Version:2.1.1
 
 
ライブラリ
LEDマトリクスのライブラリは、下記となります。
また、動作確認した際の各ライブラリのバージョンは以下となります。
| Library | Version | Note | 
|---|---|---|
| PxMatrix LED MATRIX library | 1.8.2 | 上記のライブラリ | 
| Adafruit BusIO | 1.14.3 | - | 
| Adafruit GFX Library | 1.11.7 | - | 
| Arduino-MPU6050 | patch-1 | 
witnessmenow/Arduino-MPU6050 修正は後述  | 
📝作業ログ
いくつかハマったので、作業内容を時系列で記載します。
ピン番号の変更
- fallingSand/fallingSand.inoを上記の接続に修正します
 - ピン番号をこの基板用に変更します
- 
failingSand.ino
#define P_LAT 25 // myboard // #define P_LAT 22 //original #define P_A 19 #define P_B 23 #define P_C 18 #define P_D 5 #define P_E 15 #define P_OE 16 // 2 
 - 
 - また、I2Cのピン設定も接続図に合わせます
- 
failingSand.ino
while (!mpu.begin(MPU6050_SCALE_2000DPS, MPU6050_RANGE_4G, MPU6050_ADDRESS, 21, 22)) //myboard // while (!mpu.begin(MPU6050_SCALE_2000DPS, MPU6050_RANGE_4G, MPU6050_ADDRESS, 27, 33)) //original 
 - 
 
動作確認1 - I2C周りの修正
- 修正自体は、上記で終わりのため、動作を確認しました。
 - 電源を入れたところ、LEDが光りません。
 - print文で追ったところ、上記のMPU6050初期化のmpu.begin()の処理が抜けないことがわかりました
 - 追うと、I2Cのread処理にて停止していることがわかりました
- STOP Conditionが発行されていない???
 
 - Wireライブラリのサンプルコードをもとに修正しました
- 修正前
 
Arduino-MPU6050/MPU6050.cpp(before)// Read 8-bit from register uint8_t MPU6050::readRegister8(uint8_t reg) { uint8_t value; Wire.beginTransmission(mpuAddress); Wire.write(reg); Wire.endTransmission(); Wire.beginTransmission(mpuAddress); Wire.requestFrom(mpuAddress, 1); while (!Wire.available()) { }; value = Wire.read(); Wire.endTransmission(); return value; }- 修正後
 
Arduino-MPU6050/MPU6050.cpp(after)// Read 8-bit from register uint8_t MPU6050::readRegister8(uint8_t reg) { uint8_t value; Wire.beginTransmission(mpuAddress); Wire.write(reg); Wire.endTransmission(); Wire.beginTransmission(mpuAddress); Wire.endTransmission(true); uint8_t bytesReceived = Wire.requestFrom(mpuAddress, 1); if( bytesReceived ) { Wire.readBytes(&value, bytesReceived); } Wire.endTransmission(); return value; }- 同じ処理が MPU6050::fastRegister8(uint8_t reg)にもあるため、同じように修正しました
 
 - 上記の修正に伴い、MPU6050.cpp/hをローカルに移動し、各インクルード文を修正しますMPU6050.cpp/fallingSandRep.ino
// #include <MPU6050.h> #include "MPU6050.h" 
動作確認2-加速度センサー軸の修正
- 上記の修正を行い、再度動作を確認しました
 - 無事にLEDが点灯しましたが、傾けた方向と逆に砂が転がります
 - 加速度センサーの取り付け方にて、転がる向きが変わってしまうため、下記のコードを追加しましたfallingSandRep.ino
ImgBufferGFX imgWrapper(img, WIDTH, HEIGHT); #define MPU6050_CHANGE_X_Y 1 //add - X/Y軸の入れ替え #define MPU6050_CHANGE_SIGN_X 1 //add - X方向の反転 #define MPU6050_CHANGE_SIGN_Y 0 //add - Y方向の反転 float xOffset = -1350; float yOffset = -2590; 略 void pixelTask(void *param) { 略 #if MPU6050_CHANGE_SIGN_X float xOffset = (accelVector.XAxis) * -1; #else float xOffset = (accelVector.XAxis * -1) * -1; #endif #if MPU6050_CHANGE_SIGN_Y float yOffset = (accelVector.YAxis) * -1; #else float yOffset = (accelVector.YAxis * -1) * -1; #endif #if MPU6050_CHANGE_X_Y float tmpOffset = xOffset; xOffset = yOffset; yOffset = tmpOffset; #endif while (true) { 略 #if MPU6050_CHANGE_SIGN_X float accelX = (accelVector.XAxis) + xOffset; #else float accelX = (accelVector.XAxis * -1) + xOffset; #endif #if MPU6050_CHANGE_SIGN_Y float accelY = (accelVector.YAxis) + yOffset; #else float accelY = (accelVector.YAxis * -1) + yOffset; #endif float accelZ = accelVector.ZAxis; #if MPU6050_CHANGE_X_Y float accelTmp = accelX; accelX = accelY; accelY = accelTmp; #endif - 上記の場合、LEDパネルと加速度センサーの位置関係は以下となります
 - 最終的なファイル構成は、下記となります
- ./fallingSandRep/
- fallingSandRep.ino
 - imgBufferGFX.h
 - MPU6050.cpp
 - MPU6050.h
 
 
 - ./fallingSandRep/
 
以上で、うまく動作することができました🎉。
📄メモ
- 当たり判定や表示の仕組みは、コメントに詳しく記載されています
 - 気になったことを列挙します
 
64x32に変更する場合
- 64x32のLEDパネルにする場合、下記の修正が必要です
 - 該当箇所は、以下です
- 表示サイズの定義fallingSand.ino
// 64x64 #define WIDTH 64 // Display width in pixels #define HEIGHT 64 // Display height in pixels // 64x32 #define WIDTH 64 // Display width in pixels #define HEIGHT 32 // Display height in pixels - PxMATRIXの初期化fallingSand.ino
// 64x64 PxMATRIX display(64, 64, P_LAT, P_OE, P_A, P_B, P_C, P_D, P_E); // 64x32 PxMATRIX display(64,32,P_LAT, P_OE,P_A,P_B,P_C,P_D); - 行指定fallingSand.ino
// 64x64 display.begin(32); // 64x32 display.begin(16); 
 - 表示サイズの定義
 
RGBの順番変更
- メーカによってRGBの順番が異なるようです
 - 下記にて変更できますfallingSand.ino
// RGB display.setColorOrder(RRGGBB); // GBR display.setColorOrder(GGBBRR); 
mapと当たり判定
- img[]がmapとなっている
- uint16_t img[WIDTH * HEIGHT]; // Internal 'map' of pixels
 - 本配列が衝突の判定のmapとなっている
 - imgWrapper経由で描画した文字列を描画した場合、その部分は壁になる
- 壁 : imgWrapper.print("HI");
 - 表示 : display.print("HI");
 
 - 例:HIを描画した場合の動作
- 動画を取る
 
 
 
さいごに
久しぶりに電子工作をして楽しかったです。
目に見えるものは、わかりやすくていいですね。



Discussion