ハイハイスクールアドベンチャー M5Stack/M5Cardputer版
あらすじ
2019年神奈山県立ハイ高等学校は 地盤が弱く校舎の老朽化も進んだため、 とうとう廃校にする以外方法がなく なってしまった。
ところで大変な情報を手に入れた。 それは、
「ハイ高校にATOMIC BOMBが仕掛けられている。」
と、いうものだ。 どうやらハイ高が廃校になった時、 気が狂った理科の先生がATOMIC BOMBを 学校のどこかに仕掛けてしまったらしい。
お願いだ。我が母校のコナゴナになった 姿を見たくはない。 早くATOMIC BOMBを取り除いてくれ……!!
行動は英語で、“<動詞>” 或いは、“<動詞>”+“<目的語>“のように入れていただきたい。 例えば、”look room”と入れれば部屋の様子を見ることが出来るという訳だ。
それでは Good Luck!!!…………
概要
M5Faces を思い付きで買ったので、キーボードを使う何かを作りたくなって移植を開始しました。 途中でメモリ周りでエラーが多発したため開発を中断1)したものの、M5Unifiedライブラリなる素敵なライブラリがあるらしいことをしって熱が再燃。 今度は最後まで作り切った。 作っている最中に M5Cardputerもリリースされたため、これにも対応を行った。
M5Stack Core (BASIC, Grey, Fire)および Core2、また M5Cardputerに対応しているが、M5Stack Core/Core2については FACESのキーボードモジュールが存在していることが前提である。 入手困難なようなので、見かけたらゲットされたい。
また、データをmicroSDカードにストアして利用するためmicroSDカードも必須である。
ビルド
本アプリケーションはGitHubにてソースを、こちらにてデータファイルを公開している。
データファイルはmicroSDカードに /HHSAdv というディレクトリを作成し、そこに展開したファイルすべてをコピーする。
ソースは、GitHubから取得して、Platform IOのプロジェクトとしてビルドする。
$ git clone https://github.com/wildtree/hhsadv.git
ビルドにはM5Unified 0.1.12, M5GFX 0.1.12, および M5Cardputer 1.0.3以降が必要になる。 それぞれ、platformio.ini に明記されているので、プロジェクトとして開いたら良しなにやってもらえるはずだ。
ターゲットは、M5Stack-grey, M5Stack-core2, M5Cardputerが用意してあるが、M5Stack-core2はPSRAMを使用するケースを想定して定義してあるものなので使う必要がないため、このターゲットは無視して構わない。2) M5Stack Core2であっても M5Stack-grey でビルドしたバイナリを使用可能である。 M5 Cardputer用は M5Cardputerをターゲットとしてビルドしたものを使用する。
あれこれ
M5Cardputerについて
M5Cardputerについては240×135ピクセルの1.14インチ液晶を持つが、これはハイハイスクールアドベンチャーの画像データが想定してる256×152ピクセルよりも小さい。
なので、当初は移植対象から除外していたのだが、GFXライブラリーは、バッファの画像をアフィン変換してLCDに転送する機能を持っていると知ったため、後から対象にした。
256×152の画面を縮小しているが、画像についてはおおむね問題ないレベルで表示できていると思う。
問題は、メッセージエリアである。 勿論ここも8×16/16×16のフォントで描画したものをアフィン変換して転送しているのだが、視力に挑戦といったレベルになっている。 頑張れば読めるというレベルで、これを初見でプレイするのは厳しいだろう。
キーボードもSIOで通信しているFACESのキーボードと違い、キーマトリックスがGPIOにもろに露出した形になっているため、GPIOから得たキーマップをキーコードに変換してやらないと使い物にならない。 幸い、このあたりも M5Cardputerのライブラリがまとめて面倒を見てくれているので、アプリを書くにあたっては困らないが、コードに差異が生じるので理解しておかないといけない。
ピンアサインなど
M5シリーズは、ぽいぽいピンアサインが変わるので、このあたりも留意していなければならない。 SDカードやキーボードの割り当てはちゃんと機種を見て動作を変えないといけない部分だ。
例えば、SDカードのマウントは以下のようにしている。 Core/Core2なら 用意されているSPIを使えば動くが、Cardputerはそこもまとめて面倒をみないといけない。
cfg.clear_display = true; M5.begin(cfg); uint8_t ssPin = M5.getPin(m5::pin_name_t::sd_spi_ss); if (M5.getBoard() == m5::board_t::board_M5Cardputer) { M5Cardputer.begin(cfg); spi.begin( M5.getPin(m5::pin_name_t::sd_spi_sclk), M5.getPin(m5::pin_name_t::sd_spi_miso), M5.getPin(m5::pin_name_t::sd_spi_mosi), M5.getPin(m5::pin_name_t::sd_spi_ss) ); } else { spi = SPI; } M5.Display.setRotation(1); Serial.printf("Free heap size: %6d\r\n", esp_get_free_heap_size()); // mount SD (need for M5Unified library) while (false == SD.begin(ssPin /*GPIO_NUM_4*/, spi, 25000000)) { M5.Display.println("SD Wait ..."); delay(500); }
キーボードも Cardputerはさておき、CoreとCore2とではピンアサインが違うため、コードを変えないといけない。 こっちは、KeyBoardという仮想クラスを作っておいてボードによってインスタンスを変えることで対応している。
CoreはWireでINTR=5だが、Core2はWire1でINTR=33だ。
class M5StackKeyBoard : public KeyBoard { protected: public: M5StackKeyBoard() : KeyBoard(m5::board_t::board_M5Stack) { Wire.begin(); pinMode(INTR, INPUT); digitalWrite(INTR, HIGH); } virtual ~M5StackKeyBoard() { Wire.end(); } virtual bool wait_any_key() override; virtual bool fetch_key(uint8_t &c) override; static const int INTR = 5; }; class M5Core2KeyBoard : public KeyBoard { protected: public: M5Core2KeyBoard() : KeyBoard(m5::board_t::board_M5StackCore2) { Wire1.begin(); pinMode(INTR, INPUT); digitalWrite(INTR, HIGH); } virtual ~M5Core2KeyBoard() { Wire1.end(); } virtual bool wait_any_key() override; virtual bool fetch_key(uint8_t &c) override; static const int INTR = 33; };