今更ながら、マジョカアイリスハックに挑戦しました
どういうものかは Twitter で #マジョカアイリスハック
などで検索したら出てくると思います
雑に言うと、マジョカアイリスというおもちゃをハックして遊ぶことです
特に、 640 x 48 という珍しい液晶が対象になっているようです
今回は、 元のデータから JPEG を抽出し、抽出した JPEG を SD カード に保存し、それを ESP32 で読み込んでムービーを再生するようにします
ハックする
分解や解析はだいぶ進んでおり、検索すればたいていの情報は出てくると思います
ここに詳しい解析結果がまとまっています
JPEG を抽出する
抽出にはこちらを使わせていただきました
一部抽出できないものもありましたが、バイナリを直接いじって、(おそらく)すべての JPEG を抽出することができました
JPEG を変換する
JPEG には、大きく分けてベースラインとプログレッシブという2つのフォーマットがあるそうです
今回抽出した JPEG はプログレッシブのようでした
プログラムの都合上、ベースラインである必要があるので、変換します
変換には jpegtran を使用しました
jpegtran -copy none input.jpg output.jpg
のように使います
使用機器
ESP32 DevKit v1 互換品
SD カードモジュール
FFC 変換基板
プログラムを書く
今回のプログラムは、こちらを大いに参考にさせていただきました
というか、ほぼコピペと言っても差し支えないですね
画像の表示は、サンプルコードと同じく LovyanGFX を使用させていただきました
また、ツイッターにて LovyanGFX の作者である らびやん(@lovyan03) 氏に多くのアドバイスをいただきました
ここに、感謝申し上げます
#include <WiFi.h> #include <SD.h> #include <LovyanGFX.hpp> // SD.h より後に include する // SD #define SD_FREQ 8000000 // Image int counter = 0; #define IMAGE_DIR "/image" struct LGFX_Config { static constexpr int gpio_wr = 4; //to LCD WR(14) static constexpr int gpio_rd = 2; //to LCD RD(13) static constexpr int gpio_rs = 15; //to LCD DC(16) static constexpr int gpio_d0 = 12; //to LCD D0(4) static constexpr int gpio_d1 = 13; //to LCD D1(5) static constexpr int gpio_d2 = 26; //to LCD D2(6) static constexpr int gpio_d3 = 25; //to LCD D3(7) static constexpr int gpio_d4 = 17; //to LCD D4(8) static constexpr int gpio_d5 = 16; //to LCD D5(9) static constexpr int gpio_d6 = 27; //to LCD D6(10) static constexpr int gpio_d7 = 14; //to LCD D7(11) }; static lgfx::LGFX_PARALLEL<LGFX_Config> lcd; static lgfx::Panel_ILI9342 panel; static lgfx::LGFX_Sprite buf; // [640 * 48] Buffer constexpr int32_t panel_width = 640; void setup() { // Wifi off WiFi.mode(WIFI_OFF); // SD mount SD.begin(SS, SPI, SD_FREQ); // 設定できるクロックは20MHz~6.7MHzです。また、80MHzを整数で割った値に調整されます。 // 20MHz, 16MHz, 13.4MHz, 11.5MHz, 10MHz, 88.9MHz, 8MHz, 7.3MHz, 6.7MHz panel.freq_write = 16000000; panel.len_dummy_read_pixel = 8; panel.spi_cs = 33; //to LCD CS(15) panel.spi_dc = -1; panel.gpio_rst = 32; //to LCD RST(2) panel.gpio_bl = -1; panel.pwm_ch_bl = -1; panel.backlight_level = true; panel.memory_width = 320; panel.memory_height = 240; panel.panel_width = 320; panel.panel_height = 96; panel.offset_x = 0; panel.offset_y = 144; panel.rotation = 0; panel.offset_rotation = 0; lcd.setPanel(&panel); lcd.init(); lcd.setColorDepth(16); lcd.setRotation(0); // 0 or 2 buf.setColorDepth(16); buf.createSprite(640, 48); buf.setSwapBytes(true); } // ===== MAIN LOOP ===== // void loop() { char fileName[128]; sprintf(fileName, "%s/%03d.jpg", IMAGE_DIR, counter); if (buf.drawJpgFile(SD, fileName)) { lcd_buffer_write(); counter++; } else { counter = 0; } } // ===== 640*48px Buffer to 320*96px LCD ===== // inline void lcd_buffer_write() { lcd.setAddrWindow(0, 0, 320, 96); lcd.pushPixels((lgfx::swap565_t*)buf.getBuffer(), 640 * 48); }
バッファに JPEG を読み込んで LCD に送る を繰り返しています
注意点としては、 LovyanGFX.hpp
を SD.h
より後に include すること です
この順番で include することで、 LovyanGFX の SD 用の機能が有効になる、そうです
順番を守らないと buf.drawJpgFile(SD, fileName)
でコンパイル時に「そんな関数ねーぞ」と怒られます
また、 LovyanGFX では現時点でベースラインの JPEG のみに対応しているため、変換を行ったのでした
配線する
以下のように配線します
わかりにくいかもしれないので、回路図も用意しました(わかりやすいとは言っていない)
なお、 VIN
から SD カードモジュールの電源を取っているため、 USB 接続が必要です
9V電池は液晶のバックライトのみに使用しています
再生する
実際に再生したものがこちらです
やったー! pic.twitter.com/zyTy7Of5E6
— アーサー㌠ (@KokToH_kuro) 2021年4月22日
途中白飛びしてわかりにくいですが、レインボーダイヤモンドを実行したときのムービーが流れています
だいたい 10fps くらいかと思います
もともともそれくらいだったと思うので、あまり違和感はありません
まとめ
以上、マジョカアイリスの液晶にムービーを表示してみる、でした
実は、液晶を使うのはこれが初めてでした
なかなか攻めたことしてる気がします
今後は、もう少し fps を上げたいところです
SD との通信周波数を上げればいいと思ったのですが、どうも 8MHz を超えるとマウントに失敗してしまうようです
SD カードとかモジュールの問題なのか、よくわかりません
あとは、一気に JPEG を読み込んでしまうとか、 MJPEG を使ってみるとか、いろいろ考えています
最終的にはキーボードに付けるとかやってみたいですね
いつになるかわかりませんが……
ここまでお読みいただき、ありがとうございました