バースドライバーL (レジェンド)をつくる

今回はバースドライバーをベースにカプセムとゴチゾウをミックスして、『バースドライバーL』を作ってみました。LはレジェンドのLです。ラージじゃないよ。

開発経緯

前作『ムゲンカプセム』を作成した際には、レジェンドライダー分は省いても作品として成立すると思いましたので、そこを作り込むよりはさっさと完成させてしまおう、と考えて、基本カプセム10種プラス1(コード)のみの対応で完成させました。

ただ、せっかく扱いやすいレジェンドライダーのアニメ素材が出てきたので、これはこれで活かしたいな、と思ったのと、「ガシャポンのライダーって、そもそもバースがおるやん」という気づきがあったので、これを組み合わせて「ガシャポンでレジェンドライダーに変身できるベルトをつくろう」と思い至りました。バースドライバーの中央にディスプレイを配置して、レジェンドライダーに変身すると、そのライダーの画像やアニメが表示されるというもの。

で、ガシャポンなので変身するライダーは基本的にランダムで決まるけど、ムゲンカプセムと違って実際にお金(メダル)を投入できるので、「レジェンドライダーのオーメダルがあればランダムではなくそのライダーに確定で変身できる」という要素を加えれば、玩具としてより面白いかなと。

さらに都合が良かったのが、『ゼッツ』の前作『ガヴ』の玩具、『DXガヴフォン』の存在です。

このガヴフォン、通常版とプレバン版(ラキア&デンデEdition)が存在していて、レジェンドライダーのゴチゾウをセットすることで、通常版だと「番組のキャッチコピー」、プレバン版だと「必殺技名」という貴重な(?)音声を鳴らしてくれる神アイテムです。これらの音声を使えば、変身時と必殺技時にいい感じの音声を鳴らせるな、と考えました。なお、良くも悪くも「ゴチゾウの声」にはなるため、ここだけゴチゾウの声になるのも違和感があるので、音声は全部ゴチゾウに任せることにしました。コンセプトとして、『ゼッツ』との関係性は無視して、『とにかく遊んで楽しいベルト』を目指して作れば、これは全然ありなんじゃないかな、という判断になります。

これで一通りアイデアとしてはまとまったので、ムゲンカプセムの作成が終わってすぐに作成に取り掛かりました。間でちょっといろいろあったのですが、およそ3ヶ月ほどで完成です。

 

特徴

ベースはバースドライバーで、見た目の違いとして、全体をメタリックレッドで塗装してあります。意図せずですが渋めの色になり、なんか番組後半に出てくるボスライダーが使用するベルトっぽくなりました。

通常のバースドライバーと同じくメダルを入れると、リングが七色に光りながら変身待機音が鳴ります。このときの待機音は完全に『変身ベルトガヴ』の音声です。

ハンドルを回してカプセルが開くと、ランダムで選ばれたレジェンドライダーへの変身が始まりますが、このとき、いきなり変身はせず、前口上として「そのライダーの番組キャッチコピー」が流れます。

それからベルト中央のディスプレイにライダーズクレストが表示され、変身開始。

変身が完了すると、そのライダーの画像が現れます。なお、ランダム変身で現れるライダーは、クウガ〜ガヴまでの27人(←リバイスはリバイとバイスで別。ただし、ライダー名以外の音声と画像は共通)と、レジェンド、1号の計29名です。

続けてメダルをもう一枚投入すると、必殺技待機。こちらも音声は『変身ベルトガヴ』のものです。

ハンドルを回すと、必殺技発動。必殺技名が叫ばれると、ライダーのアニメーションが流れます。爆発音は共通。

メダルを取り出すと、変身解除。こちらも解除音は『変身ベルトガヴ』です。

 

クウガ〜ダブル+1号については、対応するオーメダルを使用することで、確定で変身することが可能です。

実はここで使用するオーメダル、製品版のオーメダルとはちょっと違っていまして、後ほど説明します。

 

ハードウェア解説

ますば具材から。

何はともあれバースドライバーですが、これは中古のDX版をフリマアプリで入手しました。

 

それから、オーメダル。これまた、フリマアプリで中古品を買い集めました。なので、どれもそこそこ傷んでいますが、これはしゃーない。

 

続いてマイコン。M5StampS3を使用しています。

私が使用したのは上記の前モデル(M5StampS3)なのですが、生産終了らしいので後継品のリンクを載せています。

後ほど紹介しますが、部品としてディスプレイを使用すると、それだけでピンを大量に使用してしまって、ピンの数が足りなくなります。その点、このM5StampS3はピンの数がかなり多いので、ディスプレイを使用しても比較的余裕で他の部品を使用できるようになります。

ただ、注意点として、使えるピンをフルで使用しようとして場合、1.27mm間隔の半田付けを要求されます。これは私には到底無理、ということで、私の場合は、少し値上がりしますが1.27mmピンヘッダ実装版を購入し、さらに以下の拡張基板をセットで使用しています。

これでだいぶ扱いやすくなります。

 

続いて、先程述べたディスプレイ。以下の円形ディスプレイを使用しました。

今回、タッチ機能は使わないので、本当はタッチパネル機能がないこちらを使用したかったのですが、あいにく品切れ…やむなく、お高いこちらを使用しました。

 

続いて、毎度お馴染みのMP3プレイヤーです。

マイクロSDカードも忘れずに。なお、今回は、スピーカーは玩具に付属のものをそのまま使用しました。

 

続いて、オーメダルを読み込むためのNFCリーダーです。

実はこのNFCリーダー、通常のオーメダルは認識できません。というのも、玩具のオーメダルがそもそもNFCではない別の通信仕様になっているためです。そのため、オーメダル側の中身のチップも、NFC通信に対応したチップに交換する必要があります。ということで、

こちらを使用しました。

ちなみに、以前にプログライズライターを作ったときは、全部のオーメダルの中のチップを入れ替えるのは面倒だったので、逆にオースキャナーの中のリーダーを使って、通常のオーメダルのチップを読み込むようにしていました。こっちの方が正攻法ではあるのですが、プログラムの実装の難易度はかなり高くなるので、今回はチップ入れ替えるにしても12枚だけの話なので、実装がラクな今回のやり方で対応することにしました。

 

あとは、発光用のフルカラーLED。定番のNeoPixel系を一個使用しています。今回作成するにあたって発光は必須でなかったのですが、ディスプレイと開口部の間に隙間があったので、どうせなら光らせることにしました。

 

主要な部品はこんなところです。ちなみに、

  • メダルの挿入
  • ハンドルの回転

これらを認識するためのスイッチは、バースドライバーの中に入っていたスイッチをそのまま流用しました。

画像中央付近やや下、黒と白の線が出ているスイッチがハンドルの回転を認識するスイッチです。ただ、流石に中古品の経年劣化か、反応が悪いことがしばしばありましたので、途中で同型のスイッチに交換しました。たまたま、別の玩具を廃棄した際に分解して回収しておいたものが手元にあったので。

あと、メダルの取り出しを認識するスイッチは付いていなかったので、ここは自分でスイッチを追加しています。

ここ。使用したのはコレです。

 

あとは、配線用の具材と、それから、3Dプリンタでいくつか部品配置用の固定具をプリントしているので、適当なPLAフィラメントと、ディスプレイ周りの部分は発光を透過させるために、こちらのクリアフィラメントを使用しています。

全作の『ムゲンカプセム』でも使用したもので、湿度管理はちゃんとしないといけないですが、クリアにしてはサポートが外しやすくてオススメのフィラメントです。

 

なお、今回は、電源は元々のバースドライバーと同じ、単四電池3本にしています。

スペースに余裕があるときは積極的に電池を使います。リチウムイオンバッテリーは強力ですが、扱いが面倒なので。

 

長くなりましたが、具材はここまで。ここからは制作のポイントです。

まず見た目のところで、本来のバースドライバーは

これなのですが、今回は全体的にメタリックレッドに塗装しています。

これは、今回の改造は、ぱっと見(=YouTubeのサムネ画像など)では通常のバースドライバーとの違いがわかりにくいだろうと思ったので、とりあえず見た目に「なんか違う」と思ってもらうためだけに塗りました。メタリックレッドなのは、とある理由でメタリックレッドのスプレー缶が手元に豊富にあったから、というだけの理由です。

ちなみに、私はエアブラシを持っていないので、基本はスプレー缶塗装なのですが、これまでメタリックの塗装がなかなか上手くいかないのが毎度の悩みでした。初期の頃のスマブラガシャットとか、最近だとライジングヴァレンバスターとか、それが顕著です。もっと上手く塗りたかった。

で、前作のムゲンカプセムではかなり改善されているのですが、それはこちらの本に書いてあった、スプレー缶でのメッキ塗装のやり方を実践したからです。

なんかスプレー缶で上手くメッキ塗装できない、という方は一読の価値ありだと思います。今回も同じやり方で塗装していて、全体的には満足いくものになりました。

ちなみに、金色のラインの部分、ここは玩具で元々塗装されていたので、それをそのまま活かそうと思っていたのですが、メタリックレッドに塗装した後に見てみるとなんだか安っぽい感じになってしまったので、上から筆塗りで金色を重ね塗りしました。そのため、よく見ると塗装があんまり綺麗じゃないのですが、それはご愛嬌ということで。

 

それから肝心の中見。回路図はこんな感じです。

で、実際の配線。まずディスプレイ。

ディスプレイを中央に固定するためのパーツは3Dプリンタで作成しています。併せて、フルカラーLEDもセットできるようしてあります。

ちょっど見づらいですが。これらの配線は、内部で開けた穴を通して裏側に持ってきます。

それから、マイコン、MP3プレイヤー、NFCリーダーを固定するためのパーツ。これも3Dプリンタで作成しています。

実際に配線するとこんな感じで、まあなかなか大変です。

一発で完動してくれて良かった。

 

バースドライバー本体はこんな感じで、あとはオーメダル側です。元々はこんな感じで四角い基板が入っているのですが、これがまー目立つ。

ということで、NFCチップに交換するにあたり、もう少しチップを目立たないようにできないかな、と考えました。

結果、こんな感じで3Dプリンタでクリアの中敷き(?)を作りまして。

それを中にはめ込んでから、間にNFCチップを挟むことにしました。

まあ、多少はマシになったかな、ぐらいです。

 

ソフトウェア解説

まず何はともあれ状態遷移。今回はとてもシンプルです。

本来のバースドライバーは、セルメダルを出しては入れを繰り返せば色々武器を装備できたと思いますが、今回はシンプルに、最初に入れたメダルを取り出した時点で変身が解除されるようにしました。なので、必殺技も変身後に一発限りです。

 

それから、音声素材。これは、DXバースドライバーに加え、「開発経緯」で述べたとおり、以下3つの玩具からも音声を録音して使用しています。

音声の録音方法については、先人のこちらの動画をどうぞ。

 

音声に関連して、今回、「番組のキャッチコピー」を前口上として持ってきていますが、後ろに持ってくる選択肢もありました。例えば、

カポーン!→「アギト!」→変身音→「目覚めろ、その魂!」

みたいな感じですね。これはこれで全然ありだと思います。ただ、バースドライバーについてはガシャポンがモチーフなので、カプセルが開いたあと、中見がわかるまでのワクワク感を高めたいなと思って、そうなると、

カポーン!→「目覚めろ、その魂!」→「アギト!」→変身音

こっちの方が良いかなと思ったのでした。前口上の時点でほぼ何が出てくるのかはわかるのですが、それはそれで短時間ですが「アイツが来る!」みたいなワクワク感が出せますし。

あと、丁度これを作っているときに、合間合間で『ファイナルファンタジータクティクス イヴァリース・クロニクルズ』をプレイしていたのも影響しているかもしれません。あれ、技とか魔法を出すときに、前口上があるのです。

「大気満たす力震え、我が腕をして 閃光とならん!無双稲妻突き!」

みたいな。それでこの順番にしてしまった可能性もなくはないです。

 

さて、プログラムについては、全文は最後に載せますので、ここではいくつかポイントだけご紹介します。

まず、変身するライダーの選択。実はメダルを入れた時点でランダムで確定しているのですが、メダルをセットした先にNFCリーダーがあるので、NFCチップを搭載したメダルがセットされると、認識したチップに応じて選択されるライダーが上書きされる、という仕組みになっています。

次に、音声再生、ディスプレイ描画更新、発光タイミングの調整。ここは地味に面倒くさかったところです。ライダーごとに番組キャッチコピーだったり変身音だったりの長さが全然違うので、ここをサボって共通タイミングにしてしまうと、流石に違和感が出ます。例えば、クウガの番組キャッチコピーは「変身。」ですが、ダブルのキャッチコピーは「俺たちは、僕たちは、二人で一人の仮面ライダーさ」です。全然違う。特にセイバーは変身音と必殺技音がエライことになります。超長い。

ということで、変身シーケンスと必殺技シーケンスの一部の長さは変数化して、そこに入る値を、ライダーごとのNFC-IDや発光色を格納している構造体の中に一緒に持たせるようにしています。具体的には以下の長さです:

  • キャッチコピーの長さ(catch_copy_ms)
  • 変身開始〜変身までの長さ(change_ready_ms)
  • 変身〜変身完了(音声終了)までの長さ(change_ms)
  • 必殺技名の長さ(finish_name_ms)

図にした方がわかりやすいですね。

これでまあライダーごとに調整はできるようになったのですが、そのあと、実際にライダーごとにこれらの長さを設定していく必要があり…流石に29人分調整するのは大変面倒くさかったです。

 

で、技術的に今回一番苦労したのは、ディスプレイ周りです。

ライブラリについては、LovyanGFXライブラリがしっかり対応してくださっていたので、それで問題なく動かすことができました。円形ディスプレイを使うのは初めてでしたが、「領域としては四角で、四つの角が実際には表示されないだけ」と考えれば普通に使えました。

ハマったのは、アニメーションのプログラムです。アニメーション自体はデジタルゴチゾウでもムゲンカプセムでも既に実装済みだったのですが、今回、同じように実装しても何故かアニメーションしない。

なんでだろうと思ったら、スプライトの使い過ぎが原因でした。今回、ライダーズクレスト用に1枚、カプセムのアニメーション用に4枚、ディスプレイと同サイズの240 x 240pxのスプライトを計5枚使おうとしたのですが、これだとメモリを食い過ぎなのか、アニメーションが動かない結果になりました。で、いろいろ試して、最終的に、スプライトのサイズを少し小さく210 x 210pxにして、スプライト1枚を使い回すことにして計4枚に減らした結果、無事に動いてくれるようになりました。

あと、今回は1ライダーごとに計5枚、それが28ライダー分 (※リバイとバイスは共用)ということで、140枚もの画像データをマイコンに収められるかが心配でしたが、ChatGPTにサイズの変換とかを色々お願いして、なんとか全部収めることができました。なお、画像をバイナリ形式に変換するにあたっては、こちらのサイトを利用させていただいております。「RAW Dump」すればOK。いつも大変助かっています、ありがとうございます。

 

まとめ

以上、『バースドライバーL (レジェンド)』のご紹介でした。カプセムとゴチゾウのおかげで、玩具としてなかなか楽しいベルトができたのでは、と思っています。あとは、オーズ〜ガヴのオーメダルを新規に自作できたらより楽しかったと思いますが、これは私の手持ちのスキルとツールではちょっと厳しかったです。

これで『ゼッツ』関連アイテムはもう充分かな、という気がしておりますので、次は過去作品の関連アイテム作りを再開しようかなと思います。記念の周年も近いことですし。

あとその前に、ブログの大規模メンテナンスをしようかなと思っています。放置しすぎてバージョンアップが難しくなってきているので。

 

ソースコード

プログラムの全文はこちらになります。ただ、各ライダーのクレストやカプセムの画像データ (img.h) を全文掲載すると、それだけで3万行になりますので、これは流石に省略です。

#define PIN_MP3_RX    3
#define PIN_MP3_TX    4

#define PIN_SW_INSERT 5
#define PIN_SW_EJECT  6
#define PIN_SW_HANDLE 7

#define PIN_LED 9

#define PIN_LCD_BL  11
#define PIN_LCD_RST 12
#define PIN_LCD_DC  14
#define PIN_LCD_CS  46
#define PIN_SCLK    39
#define PIN_MOSI    41
#define PIN_MISO    40

#define ON  LOW
#define OFF HIGH

uint8_t sw_insert = OFF;
uint8_t sw_eject  = OFF;
uint8_t sw_handle = OFF;
uint8_t prev_sw_insert = OFF;
uint8_t prev_sw_eject  = OFF;
uint8_t prev_sw_handle = OFF;

#define STATE_INIT         0
#define STATE_CHANGE_READY 1
#define STATE_CHANGE       2
#define STATE_FINISH_READY 3
#define STATE_FINISH       4

uint8_t state = STATE_INIT;
uint8_t prev_state = STATE_INIT;

void change_state(uint8_t new_state){
  state = new_state;
  Serial.print(F("State: "));
  switch(state){
  case STATE_INIT:          Serial.println(F("INIT"));         break;
  case STATE_CHANGE_READY:  Serial.println(F("CHANGE_READY")); break;
  case STATE_CHANGE:        Serial.println(F("CHANGE"));       break;
  case STATE_FINISH_READY:  Serial.println(F("FINISH_READY")); break;
  case STATE_FINISH:        Serial.println(F("FINISH"));       break;
  default: ;
  }
}

#define MEDAL_INSERT_MS  800
#define CAPSULE_OPEN_MS 1500
#define FINISH_MS       7800

struct color_rgb {
  uint8_t r;
  uint8_t g;
  uint8_t b;
};

color_rgb COLOR_BLACK  = {  0,  0,  0};
color_rgb COLOR_WHITE  = {255,255,255};
color_rgb COLOR_RED    = {255,  0,  0};
color_rgb COLOR_GREEN  = {  0,255,  0};
color_rgb COLOR_BLUE   = {  0,  0,255};
color_rgb COLOR_YELLOW = {255,255,  0};
color_rgb COLOR_PURPLE = {255,  0,255}; // Magenta
color_rgb COLOR_ORANGE = {255,140,  0};
color_rgb COLOR_GOLD   = {255,216,  0};
color_rgb COLOR_CYAN   = {  0, 64,255}; // 定義から変更
color_rgb COLOR_PINK   = {255, 10,148}; // DeepPink

color_rgb rider_color = COLOR_BLACK;
byte rider_tag_id[7] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t rider_id = 0;
unsigned long rider_catch_copy_ms = 0;
unsigned long rider_change_ready_ms   = 0;
unsigned long rider_change_ms         = 0;
unsigned long rider_finish_name_ms    = 0;

typedef struct {
  byte tag_id[7];
  uint8_t id;
  color_rgb color;
  unsigned long catch_copy_ms;
  unsigned long change_ready_ms;
  unsigned long change_ms;
  unsigned long finish_name_ms;
} rider;

rider rider_list[] = {
  {{0x5A, 0xC1, 0x30, 0x08, 0x4F, 0x41, 0x89},  1, COLOR_RED,    1500,  7800,  3000,  3300}, // クウガ
  {{0x5A, 0x01, 0xFA, 0x04, 0x4F, 0x41, 0x89},  2, COLOR_GOLD,   2800,  8500,  3000,  3300}, // アギト
  {{0x5A, 0xB1, 0x30, 0x08, 0x4F, 0x41, 0x89},  3, COLOR_RED,    4000,  3200,  3000,  3000}, // 龍騎
  {{0x5A, 0x11, 0xA9, 0x05, 0x4F, 0x41, 0x89},  4, COLOR_RED,    2500,  8000,  3000,  2600}, // ファイズ
  {{0x5A, 0x51, 0xE0, 0x0B, 0x4F, 0x41, 0x89},  5, COLOR_BLUE,   3200,  5500,  2500,  4800}, // ブレイド
  {{0x5A, 0xB1, 0xB4, 0x01, 0x4F, 0x41, 0x89},  6, COLOR_PURPLE, 3300,  7500,  3000,  4800}, // 響鬼
  {{0x5A, 0xE1, 0x3C, 0x0B, 0x4F, 0x41, 0x89},  7, COLOR_RED,    2000,  6300,  6500,  5100}, // カブト
  {{0x5A, 0xA1, 0xB4, 0x01, 0x4F, 0x41, 0x89},  8, COLOR_RED,    3800,  9200,  5000,  2800}, // 電王
  {{0x5A, 0x91, 0x64, 0x02, 0x4F, 0x41, 0x89},  9, COLOR_RED,    5000,  8000,  3500,  3000}, // キバ
  {{0x5A, 0xD1, 0x3C, 0x0B, 0x4F, 0x41, 0x89}, 10, COLOR_PINK,   4200,  9200,  3500,  5100}, // ディケイド
  {{0x5A, 0xB1, 0xD5, 0x08, 0x4F, 0x41, 0x89}, 11, COLOR_PURPLE, 5200,  9500,  3500,  4800}, // ダブル
  {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 12, COLOR_GREEN,  2600,  6000,  5000,  3200}, // オーズ
  {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 13, COLOR_WHITE,  3000,  9000,  3000,  6000}, // フォーゼ
  {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 14, COLOR_RED,    4500,  6500,  7000,  5800}, // ウィザード
  {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 15, COLOR_ORANGE, 3400,  8500,  6000,  4800}, // 鎧武
  {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 16, COLOR_RED,    3200,  9000,  3000,  6200}, // ドライブ
  {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 17, COLOR_ORANGE, 2700,  7800,  6200,  5900}, // ゴースト
  {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 18, COLOR_PURPLE, 3400,  9800,  3500,  6300}, // エグゼイド
  {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 19, COLOR_RED,    3000,  9800,  5000,  8400}, // ビルド
  {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 20, COLOR_PURPLE, 3300,  9000,  3000,  5900}, // ジオウ
  {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 21, COLOR_YELLOW, 5200,  8000,  6200,  4300}, // ゼロワン
  {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 22, COLOR_RED,    3400,  8200, 13000, 11500}, // セイバー
  {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 23, COLOR_PINK,   4800,  9500,  5000,  7300}, // リバイ
  {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 24, COLOR_WHITE,  4300, 10500,  8500,  6200}, // ギーツ
  {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 25, COLOR_CYAN,   3800,  9000,  5000,  8300}, // ガッチャード
  {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 26, COLOR_PURPLE, 4500,  5800,  5000,  4500}, // ガヴ
  {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 27, COLOR_PINK,   4800,  9500,  5000,  7300}, // バイス
  {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 28, COLOR_GOLD,   4000,  6500,  4000,  5000}, // レジェンド
  {{0x5A, 0x41, 0xE0, 0x0B, 0x4F, 0x41, 0x89}, 29, COLOR_GREEN,  3300,  7000,  7000,  4000}  // 1号
};

uint8_t num_riders = sizeof(rider_list) / sizeof(rider);

//////////////////////////////////////////////////////////////////////////////////////

#include "MFRC522_I2C.h"

MFRC522 mfrc522(0x28);

//////////////////////////////////////////////////////////////////////////////////////
#include <DFPlayerMini_Fast.h>
#include <SoftwareSerial.h>

#define SOUND_VOLUME 20 // 0〜30

SoftwareSerial ss_mp3_player(PIN_MP3_RX, PIN_MP3_TX);
DFPlayerMini_Fast mp3_player;

#define SOUND_STATE_1ST 0
#define SOUND_STATE_2ND 1
#define SOUND_STATE_3RD 2
uint8_t sound_state = SOUND_STATE_1ST;
unsigned long sound_wait_start_point_ms = 0;

#define SOUND_FOLDER_COMMON     1
#define SOUND_FOLDER_CATCH_COPY 2
#define SOUND_FOLDER_CHANGE     3
#define SOUND_FOLDER_FINISH     4

#define SOUND_POWER_ON     1
#define SOUND_MEDAL_INSERT 2
#define SOUND_CAPSULE_OPEN 3
#define SOUND_MEDAL_EJECT  4
#define SOUND_CHANGE_READY 5
#define SOUND_FINISH_READY 6
#define SOUND_FINISH       7

void play_sound(uint8_t folder_num, uint8_t track_num){
  mp3_player.playFolder(folder_num, track_num);
  Serial.print(F("Play Folder: "));
  Serial.print(folder_num);
  Serial.print(F(", Track: "));
  Serial.println(track_num);
}

void pause_sound(){
  mp3_player.pause();
}

void control_sound(unsigned long now_ms){
  ///////////// 状態変化直後の音声処理 /////////////
  switch(state){
  case STATE_INIT:
    switch(prev_state){
    case STATE_CHANGE_READY:
    case STATE_CHANGE:
    case STATE_FINISH_READY:
    case STATE_FINISH:
      play_sound(SOUND_FOLDER_COMMON, SOUND_MEDAL_EJECT);
      sound_state = SOUND_STATE_1ST;
    default:
      ;
    }
    break;
  case STATE_CHANGE_READY:
    if(prev_state == STATE_INIT){
      play_sound(SOUND_FOLDER_COMMON, SOUND_MEDAL_INSERT);
      sound_state = SOUND_STATE_2ND;
      sound_wait_start_point_ms = now_ms;
    }
    break;
  case STATE_CHANGE:
    if(prev_state == STATE_CHANGE_READY){
      play_sound(SOUND_FOLDER_COMMON, SOUND_CAPSULE_OPEN);
      sound_state = SOUND_STATE_2ND;
      sound_wait_start_point_ms = now_ms;
    }
    break;
  case STATE_FINISH_READY:
    if(prev_state == STATE_CHANGE){
      play_sound(SOUND_FOLDER_COMMON, SOUND_MEDAL_INSERT);
      sound_state = SOUND_STATE_2ND;
      sound_wait_start_point_ms = now_ms;
    }
    break;
  case STATE_FINISH:
    if(prev_state == STATE_FINISH_READY){
      play_sound(SOUND_FOLDER_COMMON, SOUND_CAPSULE_OPEN);
      sound_state = SOUND_STATE_2ND;
      sound_wait_start_point_ms = now_ms;
    }
    break;
  default:
    ;
  }

  ///////////// 音が連続するときの時間経過処理 /////////////

  if(sound_state == SOUND_STATE_1ST){
    ;
  }else if(sound_state == SOUND_STATE_2ND){
    switch(state){
    case STATE_INIT:
      break;
    case STATE_CHANGE_READY:
      if(now_ms - sound_wait_start_point_ms >= MEDAL_INSERT_MS){
        play_sound(SOUND_FOLDER_COMMON, SOUND_CHANGE_READY);
        sound_state = SOUND_STATE_1ST;
      }
      break;
    case STATE_CHANGE:
      if(now_ms - sound_wait_start_point_ms >= CAPSULE_OPEN_MS){
        play_sound(SOUND_FOLDER_CATCH_COPY, rider_id);
        sound_state = SOUND_STATE_3RD;
        sound_wait_start_point_ms = now_ms;
      }
      break;
    case STATE_FINISH_READY:
      if(now_ms - sound_wait_start_point_ms >= MEDAL_INSERT_MS){
        play_sound(SOUND_FOLDER_COMMON, SOUND_FINISH_READY);
        sound_state = SOUND_STATE_1ST;
      }
      break;
    case STATE_FINISH:
      if(now_ms - sound_wait_start_point_ms >= CAPSULE_OPEN_MS){
        play_sound(SOUND_FOLDER_FINISH, rider_id);
        sound_state = SOUND_STATE_3RD;
        sound_wait_start_point_ms = now_ms;
      }
      break;
    default:
      ;
    }
  }else if(sound_state == SOUND_STATE_3RD){
    switch(state){
      case STATE_INIT:
        break;
      case STATE_CHANGE_READY:
        break;
      case STATE_CHANGE:
        if(now_ms - sound_wait_start_point_ms >= rider_catch_copy_ms){
          play_sound(SOUND_FOLDER_CHANGE, rider_id);
          sound_state = SOUND_STATE_1ST;
        }
        break;
      case STATE_FINISH_READY:
        break;
      case STATE_FINISH:
        if(now_ms - sound_wait_start_point_ms >= rider_finish_name_ms){
          play_sound(SOUND_FOLDER_COMMON, SOUND_FINISH);
          sound_state = SOUND_STATE_1ST;
        }
        break;
      default:
        ;
    }
  }
}

//////////////////////////////////////////////////////////////////////////////////////
#include <Adafruit_NeoPixel.h>
#define N_LED 1

unsigned long led_pattern_start_point_ms = 0;
int prev_interval_ms = 0;
uint8_t prev_steps = 0;
unsigned long prev_action_point_ms = 0;
unsigned long inc_dim_start_point_ms = 0;
bool is_blink_on = false;
bool is_inc = true;

int rainbow_hue = 0;

// 本来はNEO_RGB指定が正しいはずだが、Color(r,g,b)の色指定でなぜかRとGが入れ替わってしまうため、NEO_GRB指定にしている
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(N_LED, PIN_LED, NEO_GRB);

void led_base_pattern_on(struct color_rgb *color){
  pixels.setPixelColor(0, pixels.Color(color->r, color->g, color->b));
}

void led_base_pattern_off(){
  pixels.setPixelColor(0, pixels.Color(0,0,0));
}

void led_base_pattern_inc(struct color_rgb *color, unsigned long now_ms, int interval_ms, uint8_t steps){
  if(inc_dim_start_point_ms == 0 || interval_ms != prev_interval_ms || steps != prev_steps){
    inc_dim_start_point_ms = now_ms;
    prev_interval_ms = interval_ms;
    prev_steps = steps;
  }

  int ms_per_step = interval_ms / steps;
  int current_step = (now_ms - inc_dim_start_point_ms) / ms_per_step;
  if(current_step > steps){
    current_step = steps;
  }
  uint8_t r_step = color->r/steps;
  uint8_t g_step = color->g/steps;
  uint8_t b_step = color->b/steps;

  pixels.setPixelColor(0, pixels.Color(r_step*current_step, g_step*current_step, b_step*current_step));
}

void led_base_pattern_dim(struct color_rgb *color, unsigned long now_ms, int interval_ms, uint8_t steps){
  if(inc_dim_start_point_ms == 0 || interval_ms != prev_interval_ms || steps != prev_steps){
    inc_dim_start_point_ms = now_ms;
    prev_interval_ms = interval_ms;
    prev_steps = steps;
  }

  int ms_per_step = interval_ms / steps;
  int current_step = (now_ms - inc_dim_start_point_ms) / ms_per_step;
  if(current_step > steps){
    current_step = steps;
  }
  uint8_t r_step = color->r/steps;
  uint8_t g_step = color->g/steps;
  uint8_t b_step = color->b/steps;

  pixels.setPixelColor(0, pixels.Color(r_step*(steps-current_step), g_step*(steps-current_step), b_step*(steps-current_step)));
}

void led_base_pattern_blink(struct color_rgb *color, unsigned long now_ms, int inverval_ms){
  if(now_ms - prev_action_point_ms >= inverval_ms){
    if(is_blink_on){
      pixels.setPixelColor(0, pixels.Color(0,0,0));
    }else{
      pixels.setPixelColor(0, pixels.Color(color->r,color->g,color->b));
    }
    is_blink_on = !is_blink_on;
    prev_action_point_ms = now_ms;
  }
}

void led_base_pattern_blink_slowly(struct color_rgb *color, unsigned long now_ms, int interval_ms, uint8_t steps){
  if(inc_dim_start_point_ms == 0 || interval_ms != prev_interval_ms || steps != prev_steps){
    inc_dim_start_point_ms = now_ms;
    prev_interval_ms = interval_ms;
    prev_steps = steps;
  }

  int ms_per_step = interval_ms / steps;
  int current_step = (now_ms - inc_dim_start_point_ms) / ms_per_step;
  if(current_step > steps){
    current_step = steps;
  }
  uint8_t r_step = color->r/steps;
  uint8_t g_step = color->g/steps;
  uint8_t b_step = color->b/steps;

  if(is_inc){
    pixels.setPixelColor(0, pixels.Color(r_step*current_step, g_step*current_step, b_step*current_step));
  }else{
    pixels.setPixelColor(0, pixels.Color(r_step*(steps-current_step), g_step*(steps-current_step), b_step*(steps-current_step)));
  }

  if(now_ms - inc_dim_start_point_ms >= interval_ms){
    is_inc = !is_inc;
    inc_dim_start_point_ms = 0;
  }
}

void led_base_battern_rainbow(){
  rainbow_hue += 1024; // 256の倍数で設定
  if(rainbow_hue == 65536){
    rainbow_hue = 0;
  }
  pixels.setPixelColor(0, pixels.ColorHSV(rainbow_hue));
}

void led_pattern_change_ready(unsigned long passed_ms, unsigned long now_ms){
  if(passed_ms < MEDAL_INSERT_MS){
      led_base_pattern_dim(&COLOR_WHITE, now_ms, MEDAL_INSERT_MS, 20);
  }else if(MEDAL_INSERT_MS <= passed_ms){
      led_base_battern_rainbow();
  }
}

void led_pattern_change(unsigned long passed_ms, unsigned long now_ms){
  if(passed_ms < CAPSULE_OPEN_MS){
      led_base_pattern_dim(&COLOR_WHITE, now_ms, CAPSULE_OPEN_MS, 20);
  }else if(CAPSULE_OPEN_MS <= passed_ms && passed_ms < CAPSULE_OPEN_MS + rider_catch_copy_ms){
      led_base_pattern_off();
  }else if(CAPSULE_OPEN_MS + rider_catch_copy_ms <= passed_ms &&
           passed_ms < CAPSULE_OPEN_MS + rider_catch_copy_ms + rider_change_ready_ms + rider_change_ms * 3/4){
      led_base_pattern_on(&rider_color);
  }else if(CAPSULE_OPEN_MS + rider_catch_copy_ms + rider_change_ready_ms + rider_change_ms * 3/4 <= passed_ms &&
           passed_ms < CAPSULE_OPEN_MS + rider_catch_copy_ms + rider_change_ready_ms + rider_change_ms){
      led_base_pattern_dim(&rider_color, now_ms, rider_change_ms/4, 20);
  }else if(CAPSULE_OPEN_MS + rider_catch_copy_ms + rider_change_ready_ms + rider_change_ms <= passed_ms){
      led_base_pattern_off();
  }
}

void led_pattern_finish_ready(unsigned long passed_ms, unsigned long now_ms){
  if(passed_ms < MEDAL_INSERT_MS){
      led_base_pattern_dim(&rider_color, now_ms, MEDAL_INSERT_MS, 20);
  }else if(MEDAL_INSERT_MS <= passed_ms){
      led_base_pattern_blink_slowly(&rider_color, now_ms, 200, 10);
  }
}

void led_pattern_finish(unsigned long passed_ms, unsigned long now_ms){
  if(passed_ms < CAPSULE_OPEN_MS){
      led_base_pattern_dim(&rider_color, now_ms, CAPSULE_OPEN_MS, 20);
  }else if(CAPSULE_OPEN_MS <= passed_ms && passed_ms < CAPSULE_OPEN_MS + rider_finish_name_ms){
      led_base_pattern_on(&rider_color);
  }else if(CAPSULE_OPEN_MS + rider_finish_name_ms <= passed_ms && passed_ms < CAPSULE_OPEN_MS + rider_finish_name_ms + 1000){
      led_base_pattern_inc(&rider_color, now_ms, 1000, 20);
  }else if(CAPSULE_OPEN_MS + rider_finish_name_ms + 1000 <= passed_ms && passed_ms < CAPSULE_OPEN_MS + rider_finish_name_ms + 4000){
      led_base_pattern_on(&rider_color);
  }else if(CAPSULE_OPEN_MS + rider_finish_name_ms + 4000 <= passed_ms && passed_ms < CAPSULE_OPEN_MS + rider_finish_name_ms + 5000){
      led_base_pattern_blink(&rider_color, now_ms, 50);
  }else if(CAPSULE_OPEN_MS + rider_finish_name_ms + 5000 <= passed_ms && passed_ms < CAPSULE_OPEN_MS + rider_finish_name_ms + 7800){
      led_base_pattern_dim(&rider_color, now_ms, 2800, 20);
  }else if(CAPSULE_OPEN_MS + rider_finish_name_ms + 7800 <= passed_ms){
      led_base_pattern_off();
  }
}

void control_led(unsigned long now_ms){
  if(prev_state != state){
    led_pattern_start_point_ms = now_ms;
    inc_dim_start_point_ms= 0;
    led_base_pattern_off();
  }

  unsigned long passed_ms = now_ms - led_pattern_start_point_ms;

  switch(state){
  case STATE_INIT:
    // 上記の初期化でOFFになっている
    break;
  case STATE_CHANGE_READY:
    led_pattern_change_ready(passed_ms, now_ms);
    break;
  case STATE_CHANGE:
    led_pattern_change(passed_ms, now_ms);
    break;
  case STATE_FINISH_READY:
    led_pattern_finish_ready(passed_ms, now_ms);
    break;
  case STATE_FINISH:
    led_pattern_finish(passed_ms, now_ms);
    break;
  default:
    ;
  }

  pixels.show();
}

//////////////////////////////////////////////////////////////////////////////////////

#include <pgmspace.h>
#include <LovyanGFX.hpp>

#include "img.h"

#define DISP_WIDTH  240
#define DISP_HEIGHT 240
#define SPRITE_WIDTH  210
#define SPRITE_HEIGHT 210

uint8_t SPRITE_X = (DISP_WIDTH  - SPRITE_WIDTH) / 2;
uint8_t SPRITE_Y = (DISP_HEIGHT - SPRITE_HEIGHT) / 2; 

/// 独自の設定を行うクラスを、LGFX_Deviceから派生して作成します。
class LGFX : public lgfx::LGFX_Device
{
// 接続するパネルの型にあったインスタンスを用意します。
  lgfx::Panel_GC9A01      _panel_instance;
// パネルを接続するバスの種類にあったインスタンスを用意します。
  lgfx::Bus_SPI       _bus_instance;   // SPIバスのインスタンス
// バックライト制御が可能な場合はインスタンスを用意します。(必要なければ削除)
  lgfx::Light_PWM     _light_instance;
// タッチスクリーンの型にあったインスタンスを用意します。(必要なければ削除)
  lgfx::Touch_CST816S          _touch_instance;

public:
  LGFX(void)
  {
    { // バス制御の設定を行います。
      auto cfg = _bus_instance.config();    // バス設定用の構造体を取得します。

  // SPIバスの設定
      cfg.spi_host = SPI3_HOST;     // 使用するSPIを選択  ESP32-S2,C3 : SPI2_HOST or SPI3_HOST / ESP32 : VSPI_HOST or HSPI_HOST
      cfg.spi_mode = 0;             // SPI通信モードを設定 (0 ~ 3)
      cfg.freq_write = 40000000;    // 送信時のSPIクロック (最大80MHz, 80MHzを整数で割った値に丸められます)
      cfg.freq_read  = 16000000;    // 受信時のSPIクロック
      cfg.spi_3wire  = true;        // 受信をMOSIピンで行う場合はtrueを設定
      cfg.use_lock   = true;        // トランザクションロックを使用する場合はtrueを設定
      cfg.dma_channel = SPI_DMA_CH_AUTO; // 使用するDMAチャンネルを設定 (0=DMA不使用 / 1=1ch / 2=ch / SPI_DMA_CH_AUTO=自動設定)
      cfg.pin_sclk = PIN_SCLK;   // SPIのSCLKピン番号を設定
      cfg.pin_mosi = PIN_MOSI;   // SPIのMOSIピン番号を設定
      cfg.pin_miso = PIN_MISO;   // SPIのMISOピン番号を設定 (-1 = disable)
      cfg.pin_dc   = PIN_LCD_DC; // SPIのD/Cピン番号を設定  (-1 = disable)
      // SDカードと共通のSPIバスを使う場合、MISOは省略せず必ず設定してください。

      _bus_instance.config(cfg);    // 設定値をバスに反映します。
      _panel_instance.setBus(&_bus_instance);      // バスをパネルにセットします。
    }

    { // 表示パネル制御の設定を行います。
      auto cfg = _panel_instance.config();    // 表示パネル設定用の構造体を取得します。

      cfg.pin_cs   = PIN_LCD_CS;  // CSが接続されているピン番号   (-1 = disable)
      cfg.pin_rst  = PIN_LCD_RST;  // RSTが接続されているピン番号  (-1 = disable)
      cfg.pin_busy = -1;  // BUSYが接続されているピン番号 (-1 = disable)

      // ※ 以下の設定値はパネル毎に一般的な初期値が設定されていますので、不明な項目はコメントアウトして試してみてください。

      cfg.panel_width      =   DISP_WIDTH;  // 実際に表示可能な幅
      cfg.panel_height     =   DISP_HEIGHT;  // 実際に表示可能な高さ
      cfg.offset_x         =     0;  // パネルのX方向オフセット量
      cfg.offset_y         =     0;  // パネルのY方向オフセット量
      cfg.offset_rotation  =     1;  // 回転方向の値のオフセット 0~7 (4~7は上下反転) 1で右90°回転
      cfg.dummy_read_pixel =     8;  // ピクセル読出し前のダミーリードのビット数
      cfg.dummy_read_bits  =     1;  // ピクセル以外のデータ読出し前のダミーリードのビット数
      cfg.readable         =  true;  // データ読出しが可能な場合 trueに設定
      cfg.invert           =  true;  // パネルの明暗が反転してしまう場合 trueに設定
      cfg.rgb_order        = false;  // パネルの赤と青が入れ替わってしまう場合 trueに設定
      cfg.dlen_16bit       = false;  // 16bitパラレルやSPIでデータ長を16bit単位で送信するパネルの場合 trueに設定
      cfg.bus_shared       = false;  // SDカードとバスを共有している場合 trueに設定(drawJpgFile等でバス制御を行います)

      _panel_instance.config(cfg);
    }

    { // バックライト制御の設定を行います。(必要なければ削除)
      auto cfg = _light_instance.config();    // バックライト設定用の構造体を取得します。

      cfg.pin_bl = PIN_LCD_BL;      // バックライトが接続されているピン番号
      cfg.invert = false;           // バックライトの輝度を反転させる場合 true
      cfg.freq   = 44100;           // バックライトのPWM周波数
      cfg.pwm_channel = 7;          // 使用するPWMのチャンネル番号

      _light_instance.config(cfg);
      _panel_instance.setLight(&_light_instance);  // バックライトをパネルにセットします。
    }

    setPanel(&_panel_instance); // 使用するパネルをセットします。
  }
};

LGFX display;
// 大きなスプライトを多く用意すると、正常に動作しない。210 x 210 の場合、描画するPNGファイル次第では4枚が限界
LGFX_Sprite sprite_1(&display);
LGFX_Sprite sprite_2(&display);
LGFX_Sprite sprite_3(&display);
LGFX_Sprite sprite_4(&display);

#define IMG_STATE_INIT 0
#define IMG_STATE_1ST  1
#define IMG_STATE_2ND  2
uint8_t img_state = IMG_STATE_INIT;
unsigned long img_wait_start_point_ms = 0;

#define ANIMATION_SPEED_MS 100

void clear_sprite(){
  sprite_1.fillSprite(TFT_WHITE); // ここは最初はクレストを描画する
  sprite_2.fillSprite(TFT_BLACK);
  sprite_3.fillSprite(TFT_BLACK);
  sprite_4.fillSprite(TFT_BLACK);
}

uint8_t frame_index = 0;
unsigned long prev_frame_change_point_ms = 0;

void animate_rider(unsigned long now_ms, int inverval_ms){
  if(prev_frame_change_point_ms == 0){
    prev_frame_change_point_ms = now_ms;
  }

  if(now_ms - prev_frame_change_point_ms >= inverval_ms){
    switch(frame_index){
    case 0: sprite_1.pushSprite(SPRITE_X, SPRITE_Y); break;
    case 1: sprite_2.pushSprite(SPRITE_X, SPRITE_Y); break;
    case 2: sprite_3.pushSprite(SPRITE_X, SPRITE_Y); break;
    case 3: sprite_4.pushSprite(SPRITE_X, SPRITE_Y); break;
    default: ;
    }
    frame_index++;
    if(frame_index > 3){
      frame_index = 0;
    }
    prev_frame_change_point_ms = now_ms;
  }
}

void control_img(unsigned long now_ms){
  // 状態遷移時処理
  switch(state){
  case STATE_INIT:
    switch(prev_state){
    case STATE_CHANGE_READY:
    case STATE_CHANGE:
    case STATE_FINISH_READY:
    case STATE_FINISH:
      clear_sprite();
      display.fillScreen(TFT_BLACK);
      sprite_4.pushSprite(SPRITE_X, SPRITE_Y); // sprite_1以外ならなんでも良い
      break;
    default:
      ;
    }
    break;
  case STATE_CHANGE_READY:
    break;
  case STATE_CHANGE:
    if(prev_state == STATE_CHANGE_READY){
      display.fillScreen(TFT_WHITE);
      img_state = IMG_STATE_1ST;
      img_wait_start_point_ms = now_ms;
    }
    break;
  case STATE_FINISH_READY:
    break;
  case STATE_FINISH:
    if(prev_state == STATE_FINISH_READY){
      img_state = IMG_STATE_1ST;
      img_wait_start_point_ms = now_ms;
      // アニメーション用変数の初期化。メインのアニメーション処理は永続処理の方で記述
      prev_frame_change_point_ms = 0;
      frame_index = 0;
    }
    break;
  default:
    ;
  }

  // 時間経過処理
  switch(state){
  case STATE_INIT:
    break;
  case STATE_CHANGE_READY:
    break;
  case STATE_CHANGE:
    switch(img_state){
    case IMG_STATE_INIT:
      break;
    case IMG_STATE_1ST:
      if(now_ms - img_wait_start_point_ms >= CAPSULE_OPEN_MS + rider_catch_copy_ms){
        sprite_1.pushSprite(SPRITE_X, SPRITE_Y); // クレスト表示
        img_wait_start_point_ms = now_ms;
        img_state = IMG_STATE_2ND;
        update_img_crest2rider(); // クレスト画像はすぐさま差し替えの準備
      }
      break;
    case IMG_STATE_2ND:
      if(now_ms - img_wait_start_point_ms >= rider_change_ready_ms){
        display.fillScreen(TFT_BLACK);
        sprite_1.pushSprite(SPRITE_X, SPRITE_Y); // 立ち絵表示
        img_state = IMG_STATE_INIT;
      }
      break;
    default:
      ;
    }
    break;
  case STATE_FINISH_READY:
    break;
  case STATE_FINISH:
    switch(img_state){
    case IMG_STATE_INIT:
      break;
    case IMG_STATE_1ST:
      if(now_ms - img_wait_start_point_ms < CAPSULE_OPEN_MS + rider_finish_name_ms){
      }else if(CAPSULE_OPEN_MS + rider_finish_name_ms <= now_ms - img_wait_start_point_ms &&
                now_ms - img_wait_start_point_ms < CAPSULE_OPEN_MS + rider_finish_name_ms + FINISH_MS){
        animate_rider(now_ms, ANIMATION_SPEED_MS);// アニメーション
      }else if(CAPSULE_OPEN_MS + rider_finish_name_ms + FINISH_MS <= now_ms - img_wait_start_point_ms){
        img_state = IMG_STATE_2ND;
      }
      break;
    case IMG_STATE_2ND:
      sprite_1.pushSprite(SPRITE_X, SPRITE_Y); // 立ち絵表示;
      img_state = IMG_STATE_INIT;
      break;
    default:
      ;
    }
    break;
  default:
    ;
  }

}

//////////////////////////////////////////////////////////////////////////////////////

void set_rider(uint8_t index){
  memcpy(rider_tag_id, rider_list[index].tag_id, 7);
  rider_color           = rider_list[index].color;
  rider_id              = rider_list[index].id;
  rider_catch_copy_ms   = rider_list[index].catch_copy_ms;
  rider_change_ready_ms = rider_list[index].change_ready_ms;
  rider_change_ms       = rider_list[index].change_ms;
  rider_finish_name_ms  = rider_list[index].finish_name_ms;

  clear_sprite();
  switch(rider_id){
  case 1:
    sprite_1.drawPng((std::uint8_t*)kuuga_0,  892, (SPRITE_WIDTH - 180)/2, (SPRITE_HEIGHT - 180)/2 + 10);
    sprite_2.drawPng((std::uint8_t*)kuuga_2, 3124, (SPRITE_WIDTH - 285)/2 - 10, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)kuuga_3, 3138, (SPRITE_WIDTH - 285)/2 - 10, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)kuuga_4, 3630, (SPRITE_WIDTH - 285)/2 - 10, (SPRITE_HEIGHT - 180)/2);
    break;
  case 2:
    sprite_1.drawPng((std::uint8_t*)agito_0, 1039, (SPRITE_WIDTH - 200)/2, (SPRITE_HEIGHT - 200)/2);
    sprite_2.drawPng((std::uint8_t*)agito_2, 2742, (SPRITE_WIDTH - 400)/2 - 50, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)agito_3, 4724, (SPRITE_WIDTH - 400)/2 - 50, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)agito_4, 4872, (SPRITE_WIDTH - 400)/2 - 50, (SPRITE_HEIGHT - 180)/2);
    break;
  case 3:
    sprite_1.drawPng((std::uint8_t*)ryuki_0, 1000, (SPRITE_WIDTH - 200)/2, (SPRITE_HEIGHT - 200)/2);
    sprite_2.drawPng((std::uint8_t*)ryuki_2, 3114, (SPRITE_WIDTH - 459)/2 - 40, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)ryuki_3, 4930, (SPRITE_WIDTH - 459)/2 - 40, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)ryuki_4, 5926, (SPRITE_WIDTH - 459)/2 - 40, (SPRITE_HEIGHT - 180)/2);
    break;
  case 4:
    sprite_1.drawPng((std::uint8_t*)faiz_0, 1020, (SPRITE_WIDTH - 180)/2, (SPRITE_HEIGHT - 180)/2);
    sprite_2.drawPng((std::uint8_t*)faiz_2, 2796, (SPRITE_WIDTH - 243)/2 - 15, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)faiz_3, 3049, (SPRITE_WIDTH - 243)/2 - 15, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)faiz_4, 2534, (SPRITE_WIDTH - 243)/2 - 15, (SPRITE_HEIGHT - 180)/2);
    break;
  case 5:
    sprite_1.drawPng((std::uint8_t*)blade_0, 1207, (SPRITE_WIDTH - 200)/2, (SPRITE_HEIGHT - 200)/2);
    sprite_2.drawPng((std::uint8_t*)blade_2, 3910, (SPRITE_WIDTH - 459)/2 + 15, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)blade_3, 4831, (SPRITE_WIDTH - 459)/2 + 15, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)blade_4, 5617, (SPRITE_WIDTH - 459)/2 + 15, (SPRITE_HEIGHT - 180)/2);
    break;
  case 6:
    sprite_1.drawPng((std::uint8_t*)hibiki_0, 1505, (SPRITE_WIDTH - 200)/2, (SPRITE_HEIGHT - 200)/2);
    sprite_2.drawPng((std::uint8_t*)hibiki_2, 3769, (SPRITE_WIDTH - 378)/2 - 20, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)hibiki_3, 3948, (SPRITE_WIDTH - 378)/2 - 20, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)hibiki_4, 3896, (SPRITE_WIDTH - 378)/2 - 20, (SPRITE_HEIGHT - 180)/2);
    break;
  case 7:
    sprite_1.drawPng((std::uint8_t*)kabuto_0, 1104, (SPRITE_WIDTH - 200)/2, (SPRITE_HEIGHT - 200)/2);
    sprite_2.drawPng((std::uint8_t*)kabuto_2, 2836, (SPRITE_WIDTH - 405)/2 - 10, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)kabuto_3, 3075, (SPRITE_WIDTH - 405)/2 - 10, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)kabuto_4, 3489, (SPRITE_WIDTH - 405)/2 - 10, (SPRITE_HEIGHT - 180)/2);
    break;
  case 8:
    sprite_1.drawPng((std::uint8_t*)den_o_0, 1947, (SPRITE_WIDTH - 200)/2, (SPRITE_HEIGHT - 200)/2);
    sprite_2.drawPng((std::uint8_t*)den_o_2, 3930, (SPRITE_WIDTH - 306)/2 + 25, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)den_o_3, 3731, (SPRITE_WIDTH - 306)/2 + 25, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)den_o_4, 3859, (SPRITE_WIDTH - 306)/2 + 25, (SPRITE_HEIGHT - 180)/2);
    break;
  case 9:
    sprite_1.drawPng((std::uint8_t*)kiva_0, 1461, (SPRITE_WIDTH - 200)/2, (SPRITE_HEIGHT - 200)/2);
    sprite_2.drawPng((std::uint8_t*)kiva_2, 5107, (SPRITE_WIDTH - 459)/2 - 40, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)kiva_3, 4573, (SPRITE_WIDTH - 459)/2 - 40, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)kiva_4, 3615, (SPRITE_WIDTH - 459)/2 - 40, (SPRITE_HEIGHT - 180)/2);
    break;
  case 10:
    sprite_1.drawPng((std::uint8_t*)decade_0,  523, (SPRITE_WIDTH - 200)/2, (SPRITE_HEIGHT - 200)/2);
    sprite_2.drawPng((std::uint8_t*)decade_2, 2823, (SPRITE_WIDTH - 351)/2 + 15, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)decade_3, 3419, (SPRITE_WIDTH - 351)/2 + 15, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)decade_4, 3579, (SPRITE_WIDTH - 351)/2 + 15, (SPRITE_HEIGHT - 180)/2);
    break;
  case 11:
    sprite_1.drawPng((std::uint8_t*)w_0,  837, (SPRITE_WIDTH - 180)/2, (SPRITE_HEIGHT - 180)/2);
    sprite_2.drawPng((std::uint8_t*)w_2, 3749, (SPRITE_WIDTH - 459)/2 + 52, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)w_3, 4413, (SPRITE_WIDTH - 459)/2 + 52, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)w_4, 3847, (SPRITE_WIDTH - 459)/2 + 52, (SPRITE_HEIGHT - 180)/2);
    break;
  case 12:
    sprite_1.drawPng((std::uint8_t*)ooo_0, 2497, (SPRITE_WIDTH - 200)/2, (SPRITE_HEIGHT - 200)/2);
    sprite_2.drawPng((std::uint8_t*)ooo_2, 3445, (SPRITE_WIDTH - 387)/2 - 30, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)ooo_3, 4123, (SPRITE_WIDTH - 387)/2 - 30, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)ooo_4, 2842, (SPRITE_WIDTH - 387)/2 - 30, (SPRITE_HEIGHT - 180)/2);
    break;
  case 13:
    sprite_1.drawPng((std::uint8_t*)fourze_0, 1233, (SPRITE_WIDTH - 180)/2, (SPRITE_HEIGHT - 180)/2);
    sprite_2.drawPng((std::uint8_t*)fourze_2, 3267, (SPRITE_WIDTH - 306)/2 + 8, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)fourze_3, 1678, (SPRITE_WIDTH - 306)/2 + 8, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)fourze_4, 2240, (SPRITE_WIDTH - 306)/2 + 8, (SPRITE_HEIGHT - 180)/2);
    break;
  case 14:
    sprite_1.drawPng((std::uint8_t*)wizard_0, 2611, (SPRITE_WIDTH - 200)/2, (SPRITE_HEIGHT - 200)/2);
    sprite_2.drawPng((std::uint8_t*)wizard_2, 5065, (SPRITE_WIDTH - 459)/2 + 70, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)wizard_3, 4058, (SPRITE_WIDTH - 459)/2 + 70, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)wizard_4, 4058, (SPRITE_WIDTH - 459)/2 + 70, (SPRITE_HEIGHT - 180)/2);
    break;
  case 15:
    sprite_1.drawPng((std::uint8_t*)gaim_0, 1437, (SPRITE_WIDTH - 180)/2, (SPRITE_HEIGHT - 180)/2);
    sprite_2.drawPng((std::uint8_t*)gaim_2, 3030, (SPRITE_WIDTH - 448)/2 - 60, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)gaim_3, 3052, (SPRITE_WIDTH - 448)/2 - 60, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)gaim_4, 3320, (SPRITE_WIDTH - 448)/2 - 60, (SPRITE_HEIGHT - 180)/2);
    break;
  case 16:
    sprite_1.drawPng((std::uint8_t*)drive_0, 1170, (SPRITE_WIDTH - 200)/2, (SPRITE_HEIGHT - 200)/2);
    sprite_2.drawPng((std::uint8_t*)drive_2, 3854, (SPRITE_WIDTH - 427)/2 + 50, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)drive_3, 3410, (SPRITE_WIDTH - 427)/2 + 50, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)drive_4, 3851, (SPRITE_WIDTH - 427)/2 + 50, (SPRITE_HEIGHT - 180)/2);
    break;
  case 17:
    sprite_1.drawPng((std::uint8_t*)ghost_0, 1197, (SPRITE_WIDTH - 200)/2, (SPRITE_HEIGHT - 200)/2);
    sprite_2.drawPng((std::uint8_t*)ghost_2, 3601, (SPRITE_WIDTH - 459)/2 - 40, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)ghost_3, 4178, (SPRITE_WIDTH - 459)/2 - 40, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)ghost_4, 4541, (SPRITE_WIDTH - 459)/2 - 40, (SPRITE_HEIGHT - 180)/2);
    break;
  case 18:
    sprite_1.drawPng((std::uint8_t*)exaid_0, 1491, (SPRITE_WIDTH - 200)/2, (SPRITE_HEIGHT - 200)/2);
    sprite_2.drawPng((std::uint8_t*)exaid_2, 3699, (SPRITE_WIDTH - 459)/2 - 35, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)exaid_3, 5152, (SPRITE_WIDTH - 459)/2 - 35, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)exaid_4, 6232, (SPRITE_WIDTH - 459)/2 - 35, (SPRITE_HEIGHT - 180)/2);
    break;
  case 19:
    sprite_1.drawPng((std::uint8_t*)build_0, 1295, (SPRITE_WIDTH - 200)/2, (SPRITE_HEIGHT - 200)/2);
    sprite_2.drawPng((std::uint8_t*)build_2, 3987, (SPRITE_WIDTH - 459)/2 - 15, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)build_3, 4433, (SPRITE_WIDTH - 459)/2 - 15, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)build_4, 4467, (SPRITE_WIDTH - 459)/2 - 15, (SPRITE_HEIGHT - 180)/2);
    break;
  case 20:
    sprite_1.drawPng((std::uint8_t*)zio_0, 1006, (SPRITE_WIDTH - 200)/2 -  3, (SPRITE_HEIGHT - 200)/2);
    sprite_2.drawPng((std::uint8_t*)zio_2, 4622, (SPRITE_WIDTH - 405)/2 + 47, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)zio_3, 5203, (SPRITE_WIDTH - 405)/2 + 47, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)zio_4, 5117, (SPRITE_WIDTH - 405)/2 + 47, (SPRITE_HEIGHT - 180)/2);
    break;
  case 21:
    sprite_1.drawPng((std::uint8_t*)zeroone_0, 1494, (SPRITE_WIDTH - 200)/2, (SPRITE_HEIGHT - 200)/2);
    sprite_2.drawPng((std::uint8_t*)zeroone_2, 3934, (SPRITE_WIDTH - 400)/2 - 115, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)zeroone_3, 3267, (SPRITE_WIDTH - 400)/2 - 115, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)zeroone_4, 3572, (SPRITE_WIDTH - 400)/2 - 115, (SPRITE_HEIGHT - 180)/2);
    break;
  case 22:
    sprite_1.drawPng((std::uint8_t*)saber_0, 1794, (SPRITE_WIDTH - 200)/2, (SPRITE_HEIGHT - 200)/2);
    sprite_2.drawPng((std::uint8_t*)saber_2, 6180, (SPRITE_WIDTH - 459)/2 - 20, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)saber_3, 7909, (SPRITE_WIDTH - 459)/2 - 20, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)saber_4, 6945, (SPRITE_WIDTH - 459)/2 - 20, (SPRITE_HEIGHT - 180)/2);
    break;
  case 23:
    sprite_1.drawPng((std::uint8_t*)revice_0, 1688, (SPRITE_WIDTH - 180)/2, (SPRITE_HEIGHT - 180)/2);
    sprite_2.drawPng((std::uint8_t*)revice_2, 5044, (SPRITE_WIDTH - 237)/2 - 0, (SPRITE_HEIGHT - 120)/2);
    sprite_3.drawPng((std::uint8_t*)revice_3, 5581, (SPRITE_WIDTH - 237)/2 - 0, (SPRITE_HEIGHT - 120)/2);
    sprite_4.drawPng((std::uint8_t*)revice_4, 4891, (SPRITE_WIDTH - 237)/2 - 0, (SPRITE_HEIGHT - 120)/2);
    break;
  case 24:
    sprite_1.drawPng((std::uint8_t*)geats_0, 1859, (SPRITE_WIDTH - 200)/2, (SPRITE_HEIGHT - 200)/2);
    sprite_2.drawPng((std::uint8_t*)geats_2, 3180, (SPRITE_WIDTH - 392)/2 - 45, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)geats_3, 3766, (SPRITE_WIDTH - 392)/2 - 45, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)geats_4, 4189, (SPRITE_WIDTH - 392)/2 - 45, (SPRITE_HEIGHT - 180)/2);
    break;
  case 25:
    sprite_1.drawPng((std::uint8_t*)gotchard_0, 2112, (SPRITE_WIDTH - 200)/2, (SPRITE_HEIGHT - 200)/2);
    sprite_2.drawPng((std::uint8_t*)gotchard_2, 4099, (SPRITE_WIDTH - 459)/2 - 35, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)gotchard_3, 4153, (SPRITE_WIDTH - 459)/2 - 35, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)gotchard_4, 4564, (SPRITE_WIDTH - 459)/2 - 35, (SPRITE_HEIGHT - 180)/2);
    break;
  case 26:
    sprite_1.drawPng((std::uint8_t*)gavv_0, 1520, (SPRITE_WIDTH - 180)/2, (SPRITE_HEIGHT - 180)/2);
    sprite_2.drawPng((std::uint8_t*)gavv_2, 3838, (SPRITE_WIDTH - 394)/2 - 30, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)gavv_3, 5042, (SPRITE_WIDTH - 394)/2 - 30, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)gavv_4, 5031, (SPRITE_WIDTH - 394)/2 - 30, (SPRITE_HEIGHT - 180)/2);
    break;
  case 27:
    sprite_1.drawPng((std::uint8_t*)revice_0, 1688, (SPRITE_WIDTH - 180)/2, (SPRITE_HEIGHT - 180)/2);
    sprite_2.drawPng((std::uint8_t*)revice_2, 5044, (SPRITE_WIDTH - 237)/2 - 0, (SPRITE_HEIGHT - 120)/2);
    sprite_3.drawPng((std::uint8_t*)revice_3, 5581, (SPRITE_WIDTH - 237)/2 - 0, (SPRITE_HEIGHT - 120)/2);
    sprite_4.drawPng((std::uint8_t*)revice_4, 4891, (SPRITE_WIDTH - 237)/2 - 0, (SPRITE_HEIGHT - 120)/2);
    break;
  case 28:
    sprite_1.drawPng((std::uint8_t*)legend_0, 1808, (SPRITE_WIDTH - 200)/2, (SPRITE_HEIGHT - 200)/2);
    sprite_2.drawPng((std::uint8_t*)legend_2, 3746, (SPRITE_WIDTH - 459)/2 - 53, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)legend_3, 5036, (SPRITE_WIDTH - 459)/2 - 53, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)legend_4, 3415, (SPRITE_WIDTH - 459)/2 - 53, (SPRITE_HEIGHT - 180)/2);
    break;
  case 29:
    sprite_1.drawPng((std::uint8_t*)rider_1_0, 1368, (SPRITE_WIDTH - 180)/2, (SPRITE_HEIGHT - 168)/2 + 20);
    sprite_2.drawPng((std::uint8_t*)rider_1_2, 5346, (SPRITE_WIDTH - 459)/2 + 55, (SPRITE_HEIGHT - 180)/2);
    sprite_3.drawPng((std::uint8_t*)rider_1_3, 2701, (SPRITE_WIDTH - 459)/2 + 55, (SPRITE_HEIGHT - 180)/2);
    sprite_4.drawPng((std::uint8_t*)rider_1_4, 2599, (SPRITE_WIDTH - 459)/2 + 55, (SPRITE_HEIGHT - 180)/2);
    break;
  default:
    ;
  }
}

void update_img_crest2rider(){
  // クレストを一度表示した後は、ライダーの1枚目の画像に差し替える準備をする
  sprite_1.fillSprite(TFT_BLACK);
  switch(rider_id){
  case  1: sprite_1.drawPng((std::uint8_t*)kuuga_1,    3630, (SPRITE_WIDTH - 285)/2 -  10, (SPRITE_HEIGHT - 180)/2); break;
  case  2: sprite_1.drawPng((std::uint8_t*)agito_1,    3307, (SPRITE_WIDTH - 400)/2 -  50, (SPRITE_HEIGHT - 180)/2); break;
  case  3: sprite_1.drawPng((std::uint8_t*)ryuki_1,    3242, (SPRITE_WIDTH - 459)/2 -  40, (SPRITE_HEIGHT - 180)/2); break;
  case  4: sprite_1.drawPng((std::uint8_t*)faiz_1,     2534, (SPRITE_WIDTH - 243)/2 -  15, (SPRITE_HEIGHT - 180)/2); break;
  case  5: sprite_1.drawPng((std::uint8_t*)blade_1,    3000, (SPRITE_WIDTH - 459)/2 +  15, (SPRITE_HEIGHT - 180)/2); break;
  case  6: sprite_1.drawPng((std::uint8_t*)hibiki_1,   4042, (SPRITE_WIDTH - 378)/2 -  20, (SPRITE_HEIGHT - 180)/2); break;
  case  7: sprite_1.drawPng((std::uint8_t*)kabuto_1,   3950, (SPRITE_WIDTH - 405)/2 -  10, (SPRITE_HEIGHT - 180)/2); break;
  case  8: sprite_1.drawPng((std::uint8_t*)den_o_1,    3091, (SPRITE_WIDTH - 306)/2 +  25, (SPRITE_HEIGHT - 180)/2); break;
  case  9: sprite_1.drawPng((std::uint8_t*)kiva_1,     4443, (SPRITE_WIDTH - 459)/2 -  40, (SPRITE_HEIGHT - 180)/2); break;
  case 10: sprite_1.drawPng((std::uint8_t*)decade_1,   2620, (SPRITE_WIDTH - 351)/2 +  15, (SPRITE_HEIGHT - 180)/2); break;
  case 11: sprite_1.drawPng((std::uint8_t*)w_1,        2787, (SPRITE_WIDTH - 459)/2 +  52, (SPRITE_HEIGHT - 180)/2); break;
  case 12: sprite_1.drawPng((std::uint8_t*)ooo_1,      2842, (SPRITE_WIDTH - 387)/2 -  30, (SPRITE_HEIGHT - 180)/2); break;
  case 13: sprite_1.drawPng((std::uint8_t*)fourze_1,   3271, (SPRITE_WIDTH - 306)/2 +   8, (SPRITE_HEIGHT - 180)/2); break;
  case 14: sprite_1.drawPng((std::uint8_t*)wizard_1,   3912, (SPRITE_WIDTH - 459)/2 +  70, (SPRITE_HEIGHT - 180)/2); break;
  case 15: sprite_1.drawPng((std::uint8_t*)gaim_1,     5065, (SPRITE_WIDTH - 448)/2 -  60, (SPRITE_HEIGHT - 180)/2); break;
  case 16: sprite_1.drawPng((std::uint8_t*)drive_1,    4006, (SPRITE_WIDTH - 427)/2 +  50, (SPRITE_HEIGHT - 180)/2); break;
  case 17: sprite_1.drawPng((std::uint8_t*)ghost_1,    2827, (SPRITE_WIDTH - 459)/2 -  40, (SPRITE_HEIGHT - 180)/2); break;
  case 18: sprite_1.drawPng((std::uint8_t*)exaid_1,    3272, (SPRITE_WIDTH - 459)/2 -  35, (SPRITE_HEIGHT - 180)/2); break;
  case 19: sprite_1.drawPng((std::uint8_t*)build_1,    3342, (SPRITE_WIDTH - 459)/2 -  15, (SPRITE_HEIGHT - 180)/2); break;
  case 20: sprite_1.drawPng((std::uint8_t*)zio_1,      4686, (SPRITE_WIDTH - 405)/2 +  47, (SPRITE_HEIGHT - 180)/2); break;
  case 21: sprite_1.drawPng((std::uint8_t*)zeroone_1,  3091, (SPRITE_WIDTH - 400)/2 - 115, (SPRITE_HEIGHT - 180)/2); break;
  case 22: sprite_1.drawPng((std::uint8_t*)saber_1,    4911, (SPRITE_WIDTH - 459)/2 -  20, (SPRITE_HEIGHT - 180)/2); break;
  case 23: sprite_1.drawPng((std::uint8_t*)revice_1,   5097, (SPRITE_WIDTH - 237)/2 -   0, (SPRITE_HEIGHT - 120)/2); break;
  case 24: sprite_1.drawPng((std::uint8_t*)geats_1,    3023, (SPRITE_WIDTH - 392)/2 -  45, (SPRITE_HEIGHT - 180)/2); break;
  case 25: sprite_1.drawPng((std::uint8_t*)gotchard_1, 3886, (SPRITE_WIDTH - 459)/2 -  35, (SPRITE_HEIGHT - 180)/2); break;
  case 26: sprite_1.drawPng((std::uint8_t*)gavv_1,     3170, (SPRITE_WIDTH - 394)/2 -  30, (SPRITE_HEIGHT - 180)/2); break;
  case 27: sprite_1.drawPng((std::uint8_t*)revice_1,   5097, (SPRITE_WIDTH - 237)/2 -   0, (SPRITE_HEIGHT - 120)/2); break;
  case 28: sprite_1.drawPng((std::uint8_t*)legend_1,   3461, (SPRITE_WIDTH - 459)/2 -  53, (SPRITE_HEIGHT - 180)/2); break;
  case 29: sprite_1.drawPng((std::uint8_t*)rider_1_1,  3684, (SPRITE_WIDTH - 459)/2 +  55, (SPRITE_HEIGHT - 180)/2); break;
  default: ;
  }
}

//////////////////////////////////////////////////////////////////////////////////////

void setup(void){
  Serial.begin(115200);

  pinMode(PIN_SW_INSERT, INPUT_PULLUP);
  pinMode(PIN_SW_EJECT,  INPUT_PULLUP);
  pinMode(PIN_SW_HANDLE, INPUT_PULLUP);
  pinMode(PIN_LED, OUTPUT);

  // 省電力化のため、CPU周波数を少し落とす場合に有効化。最大240MHz。よく設定するのは160MHz
  setCpuFrequencyMhz(160);

  pixels.begin();
  pixels.clear();

  // NFCリーダー
  Wire.begin();
  mfrc522.PCD_Init();
  mfrc522.PCD_AntennaOff(); // 最初はアンテナOFFにする(省エネ目的)

  // MP3プレイヤーセットアップ
  ss_mp3_player.begin(9600);
  if(!mp3_player.begin(ss_mp3_player)) {
    Serial.println(F("Unable to begin music_player:"));
    Serial.println(F("1.Please recheck the connection!"));
    Serial.println(F("2.Please insert the SD card!"));
    while(true);
  }
  Serial.println(F("dfplayer online."));
  delay(800); // 間を開けるのが短すぎるとコマンドが有効にならないので注意
  mp3_player.volume(SOUND_VOLUME);
  delay(500);
  play_sound(SOUND_FOLDER_COMMON, SOUND_POWER_ON);

  display.init();
  display.fillScreen(TFT_BLACK);
  sprite_1.createSprite(SPRITE_WIDTH, SPRITE_HEIGHT);
  sprite_2.createSprite(SPRITE_WIDTH, SPRITE_HEIGHT);
  sprite_3.createSprite(SPRITE_WIDTH, SPRITE_HEIGHT);
  sprite_4.createSprite(SPRITE_WIDTH, SPRITE_HEIGHT);
}

void loop(void){

  unsigned long now_ms = millis();

  ////////////////// オーメダル読取 //////////////////
  if(state == STATE_CHANGE_READY){
    if(mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()){
      // NFCタグがあり、かつ、そのUIDが読めた
      // オーメダルがセットされていない状態からセットされた
      for(uint8_t i=0; i<mfrc522.uid.size; i++) {  // Output the stored UID data.
        Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
        Serial.print(mfrc522.uid.uidByte[i], HEX);
      }
      Serial.println("");

      for(uint8_t i=0; i<num_riders; i++){
        if (memcmp(rider_list[i].tag_id, mfrc522.uid.uidByte, mfrc522.uid.size) == 0){
          // 読み込んだNFCタグのIDが先の読み込みと異なっていれば更新する
          if(memcmp(rider_list[i].tag_id, rider_tag_id,7) != 0){
            set_rider(i);
          }
          break;
        }
      }
    }
  }

  sw_insert = digitalRead(PIN_SW_INSERT);
  sw_eject  = digitalRead(PIN_SW_EJECT);
  sw_handle = digitalRead(PIN_SW_HANDLE);

  if(prev_sw_insert == OFF && sw_insert == ON){
    switch(state){
    case STATE_INIT:
      set_rider(random(num_riders)); // オーメダルの種類に依らず、まずはランダム選択する
      change_state(STATE_CHANGE_READY);
      mfrc522.PCD_AntennaOn(); // NFCリーダーアンテナON
      break;
    case STATE_CHANGE:
      change_state(STATE_FINISH_READY);
      break;
    break;
    case STATE_CHANGE_READY:
    case STATE_FINISH_READY:
    case STATE_FINISH:
    default: ;
    }
  }

  if(prev_sw_eject == OFF && sw_eject == ON){
    switch(state){
    case STATE_CHANGE:
    case STATE_CHANGE_READY:
    case STATE_FINISH_READY:
    case STATE_FINISH:
      change_state(STATE_INIT);
      for(uint8_t i=0;i<7;i++){
        rider_tag_id[i] = 0x00;
      }
      break;
    case STATE_INIT:
    default: ;
    }
  }

  if(prev_sw_handle == OFF && sw_handle == ON){
    switch(state){
    case STATE_CHANGE_READY:
      change_state(STATE_CHANGE);
      mfrc522.PCD_AntennaOff(); // NFCリーダーアンテナOFF(省エネ目的)
      break;
    case STATE_FINISH_READY:
      change_state(STATE_FINISH);
      break;
    case STATE_INIT:
    case STATE_CHANGE:
    case STATE_FINISH:
    default: ;
    }
  }

  // -------- 音声処理 --------
  control_sound(now_ms);

  // -------- 発光処理 --------
  control_led(now_ms);

  // -------- 画像表示処理 --------
  control_img(now_ms);

  prev_sw_insert = sw_insert;
  prev_sw_eject  = sw_eject;
  prev_sw_handle = sw_handle;

  prev_state = state;

  delay(1);
}