オリジナルライドウォッチをつくる 〜ZX(ゼクロス)ライドウォッチ〜
今回ご紹介するのは、自分にとって最後のオリジナルライドウォッチ『ZX(ゼクロス)ライドウォッチ』、またの名を『10号誕生!仮面ライダー全員集合!!ライドウォッチ』、あるいは『仮面ライダーSPIRITSライドウォッチ』です。
開発経緯
開発のきっかけは、『グランドジオウライドウォッチ』の変身音です。『クウガ』から『ビルド』までの名前を、歌に乗せて読み上げるやつですね。自分がこれを聞いたときに思ったのは、「昭和ライダーでも同じように、ライダーの名前を読み上げる歌があったな」ということでした。
その歌というのは、水木一郎さんの『九人ライダー永遠に』という歌で、この歌のサビの部分です。この歌が世に出たのは自分が生まれる前だったりするのですが、なぜ自分がこの歌のことを知っていたかというと、1984年に放送されたテレビ特番『10号誕生!仮面ライダー全員集合!!』内にて10人ライダーが集結するときにこの歌が流れていて、この番組の録画を兄が何度も何度も繰り返し見ていたからです。それで、自分の中にも記憶として残っていました。
さて、『グランドジオウライドウォッチ』の変身音を聞きながらそんなことを考えていたので、「こんな昭和の歌もあるよ」とネタ的にツイートしようかなと思ったときに「ちょっと待てよ」と。「そういう歌があるのなら、それを変身音にした昭和ライダーのライドウォッチを作れば良いのでは」と、そこで思い至りました。
そんなわけで、「『九人ライダー永遠に』を変身音にする」というところから開発がスタートしているため、この曲が劇中で使用され、また10号ライダーという一つの区切りである『ZX(ゼクロス)』をテーマにまとめ上げるのが一番収まりが良いだろうと考え、今回この形になりました。「どうせ昭和ライダーのウォッチを作るなら、BLACK (RX)、真、ZO、Jまで含めれば良いのに」と思われた方もいるかもしれませんが、彼ら「便宜上は昭和ライダーだが生まれは平成(BLACKはRXとして)」というライダーは、皆さまご存知の通り、劇場版『Over Quartzer』でフォローされましたので、言ってしまえば『ジオウ』という作品にとって「蛇足」ではあるのかもしれませんが、劇中で出ることもないであろう昭和10人ライダーのライドウォッチを作っておくことは補完的には意義のあることかなと思い、ZXをテーマに押し切ることにしました。この決断によって、設計においても色々うまくまとまる部分が出てきたのですが、それは追い追いご紹介します。
作品内容
この『ZXライドウォッチ』ですが、前述のテレビ特番『10号誕生!仮面ライダー全員集合!!』の内容を強く意識したものになっています。そのため、そちらを視聴頂いた後に改めてこのウォッチを見て頂くと、「ああ、なるほど」と思うところが出てくると思います。東映特撮ファンクラブに入会されている方は、そちらからご視聴頂くことが可能ですので、宜しければ是非。
まず、ウォッチ非展開時の外観です。この状態では、ただの『ZXライドウォッチ』としてしか機能しません。まだ『仮面ライダー』ではないのです。
電源スイッチは背面下部で、起動音は上記番組のオープニングと同じになっています。スイッチ部の緑色はZXのマフラーのイメージです。今回は珍しく、塗装が全体的にうまくいったと思います。
ウォッチを展開すると、発光と共に9人ライダーが出現し、『10号誕生!仮面ライダー全員集合!!ライドウォッチ』として機能するようになります。ZXが10人目の仮面ライダーとなった瞬間です。この状態でジクウドライバーにウォッチを装填すると、ドライバーの表示が『ZX』から『SHOWA』に切り替わります。
9人ライダーの発光については、「あの歌に合わせてライダーが順に光っていったらカッコ良いよな!」という思いから入れ込もうと思ったのですが、これが後で説明するタッチ召喚機能の実装を一段と難しくしてしまいました。詳細は後述します。
そして、ウォッチの展開によるドライバーの表示変更ですが、動画にあるようにジクウドライバーに挿した状態で展開しても、ドライバーの認識が変化するようになっています。これも、「一回ウォッチを外して展開して挿し直すより、ウォッチをつけたまま展開して変化した方が絶対カッコ良いしビックリするよな!」という思いから実装しました。発想が小学生です。
ちなみに、昭和10人ライダー状態になると、Rスロットに挿している『ジオウ』の認識も、『2018』から『1971』に変化します。これは、「せっかくだからジオウの表示も昭和っぽくした方が良いかな!せっかくドライバー側に表示が仕込まれてることだし!」ぐらいの気持ちで実装したもので、それ以上の深い意味はありません。
昭和10人ライダー状態で発動するアーマータイムの音声は、前述のとおり『九人ライダー永遠に』のサビを使用しています。ただ、『九人ライダー』の名の通りで、元々はスーパー1の劇場版のために作られた曲のため、ZXの読み上げ音声は入っていません。そのため、ZXだけは『10号誕生!仮面ライダー全員集合!!』の番組内でのZX自身の名乗りを合成しています。
昭和10人ライダー状態になると、さらにフィニッシュタイムが発動可能になります。このときのウォッチの音声は、ちょっと聞き取りにくいですが、「ライダーシンドローム!」になっています。これも、ZXライドウォッチを作るからには是非やっておきたいことでした。「ライダーシンドローム」については、2014年公開の映画『平成ライダー対昭和ライダー 仮面ライダー大戦 feat.スーパー戦隊』の最後でも昭和ライダー達が使用していたので、そちらでご存知の方もおられるかもしれません。
ちなみに、フィニッシュタイムの最後でZXだけ赤く発光しているのは、わかる人にはわかるかもしれませんが、「ZXは必殺のZXキックを放つときには体が赤く発光する」という設定に基づいています。トドメの一撃、というイメージですね。漫画『仮面ライダーSPIRITS』でもよく言及されていたと思います。
そして、タッチ操作による昭和ライダー達の召喚機能です。これについては、『ジオウ』本編でグランドジオウが平成ライダー達のレリーフをタッチして平成ライダーを召喚しているのを見て、それと同様の機能を玩具的に再現してみたくて入れ込んでみました。最初は召喚音声が鳴るだけでも良いかな、と思っていたのですが、「せっかくジクウドライバーに昭和ライダーの表示が仕込まれているんだから、これも一緒に表示させた方が絶対カッコ(以下略)」ということで、頑張って入れ込んでみました。
また、せっかくライダーごとのタッチ操作を組み込んだので、ジクウドライバーとの連動だけでなく単独でも遊べる機能として、各ライダーのテーマ曲を一曲、フルコーラスで再生できる機能も追加してみました。
ハードウェア解説
まず、「商品化されていないのに、この1号からZXまでのライドウォッチのシールはどこから出てきたのか?」について、最初にご説明しておきます。これらは全て、知り合いの方に新規に描き下ろして頂きました。先方のご希望により紹介は控えさせて頂きますが、この方のご協力なしには今回のZXライドウォッチは成立しませんでした。改めましてお礼申し上げます、ありがとうございました。
それでは、いつものように必要な具材を最初にザッと紹介してしまいます。ベースとなっているのは、今更ですが『DXグランドジオウライドウォッチ』です。
そして中の電子部品は、自分にとって定番の以下の具材です。
Arduino Pro miniは、今回はウォッチ側とジクウドライバー側で計2つ使用することになります。
フルカラーLEDのNeoPixelですが、いつも使用しているスイッチサイエンス取り扱いのものではなく、共立エレショップ取り扱いのものを使用しました。こちらの方がやや省スペースで配線可能なためです。今回はほぼ白色でしか発光させないため、別にフルカラーLEDである必要性はなかったのですが、やはり配線の扱いやすさのため、NeoPixelを使用しました。
今回独自の機能であるタッチ操作は、以下の5つの具材を使用して実現しています。
やり方は色々あると思うのですが、自分の場合は試行錯誤の末、こうなりました。詳細は後述します。
もう一つの機能であるジクウドライバーの表示切替ですが、これはウォッチとドライバー間の赤外線通信によって実現しており、そのため以下の具材を使用しています。詳しくは後述します。
後は、配線に必要なコードとかです。今回はウォッチの展開ギミックがあるので、できるだけ柔らかいコードを使用する必要があります。
それでは、実装上のポイントを簡単にご紹介していきます。まずはライダー召喚のためのタッチ機能の実装について。
タッチ機能についてはカイザフォンXのときに既に実装しており、メインとなるモジュールは同じものを使っています。今回厄介だったのは、各ライダーを発光させるために、透過性を持たせたままタッチできるようにする必要があった、というところです。カイザフォンXのときはその必要がなかったので、銅テープの上にラベルを貼り付ける形でタッチの電極を作れば良かったのですが、今回はそれだと光が通りません。
この部分は色々試行錯誤したのですが、最終的に静電容量フィルムと導電性の両面テープ、銅テープを組み合わせる方式で何とか形にできました。
まず、静電容量フィルムを使えば、透過性という点ではクリアできます。ただ、静電容量フィルムにはコードを半田付けすることができません。今回フィルムを貼り付ける部分はウォッチの展開ギミックの影響を最も強く受ける部分であり、コードの固定が弱くなってしまうと、ウォッチの展開を繰り返すとすぐに壊れてしまう可能性があります。
そこで、「コードを半田付けで固定するための銅テープと静電容量フィルムを導電性の両面テープで貼り付けて電荷の通り道を作る」という、かなり強引な手法でクリアしました。なお、静電容量フィルム自体に粘着性はないので、フィルムとラベルシールの間は普通の両面テープシートで接着させています。
もちろん、最後はグルーガンでガチガチに固めています。
ちなみに、この発光&タッチ機能は、対象を10人ライダーに絞ったことによって実現できた機能と言えます。というのも、本来『グランドジオウライドウォッチ』は4枚のプレートが展開することで、前面のジオウを含め計20人のライダーを扱うことができるのですが、今回10人に絞ることで、前面の2枚のプレートのみを展開すれば数とすれば間に合うことになり、後ろの2枚のプレートを土台としてLEDを固定することができました。昭和ライダー15人を扱っても、とても無茶をすればできたかもしれませんが、見栄えは決して良いものにはならなかったと思います。また、今回用いたタッチセンサが同時に扱えるタッチ数は12個までなので、もし昭和ライダー15人を扱うなら、もう一つタッチセンサを追加しなければならなくなり、そうなるとさすがに筐体に部品を押し込むのがかなり難しくなっていたと思います。
続いて、ジクウドライバーの表示変更の方法についてです。これは、ウォッチとジクウドライバーの間で赤外線通信を行うことによって実現しているのですが、このアイデア自体は私が考えたものではありません。Twitterで相互フォローさせて頂いているかっぱ次郎様が『オールインワンライドウォッチ』を作成する際に考案された方法であり、今回はその方式を自分なりにアレンジして使用させて頂きました。ここではハードウェアの解説に留め、詳しいことは次のソフトウェア解説で説明させて頂きます。
まずはジクウドライバー側ですが、回路としてはこんな感じになっています。
点線赤枠で囲んでいる部分が元々のジクウドライバーの配線で、それにArduinoからの配線を後付けするような形になっています。電源はジクウドライバー本体の電池からとっています。
配線はこんな感じで、ドライバー本体の基板とArduino基板はすべてピン/ソケットで接続するようにしています(Arduinoは電池ボックスの裏に貼り付けています)。Arduinoを配線している間は普通のジクウドライバーのようには動作しない(←ライドウォッチを挿しても認識しないし、回転をさせても認識しない)のですが、配線を全て取ってしまえば元のジクウドライバーとして遊べるようになるので、いつでも戻せるようにこうしています。
なお、ジクウドライバー本体の基板に半田付け・基板接続するときは、必ずドライバー側の電池は抜いておきましょう。念のため。
赤外線受信モジュールはここ、Lスロット側から顔を出すようになっています。幸いなことにクリアパーツなので、穴開けは不要です。
先ほど述べた通り、上図の配線をすると、通常のジクウドライバーとしては機能しなくなってしまい、R/Lスロットにウォッチを装填しても反応してくれません。しかし動画の中では、Rスロットに挿したジオウライドウォッチが反応しているように見えます。
実はこれはフェイクみたいなもので、Rスロットのこの部分にスイッチを追加しており、ここを通過するものは全て『ジオウ 2018』として認識されるようになっています。その代わり、Rスロット側も、ウォッチからの赤外線通信によって、後から認識を自由に変えることができるようになっています。
ライドウォッチの認識ピンによってジクウドライバー側の認識スイッチが押されてしまうとややこしいことになってしまうので、ジオウ・ZXライドウォッチ共に、認識ピンは全て削っています。
ジクウドライバーの回転認識スイッチも、同様の理由で取り外しています。ジクウドライバーの制御は全て、赤外線通信を通してソフト的に行います。
続いてZXライドウォッチ側ですが、回路図を載せるのを忘れていたので、ここで載せておきます。
配線自体は数が多くて大変ですが、やればどうにかできるレベルでした。意外なところで躓いたのが、赤外線LEDの位置です。
最初は「できるだけ赤外線受信モジュールに近い位置で光らせないとダメだろう」と思って、ライドウォッチの認識プレートの穴からちょっとだけ顔を出すぐらいの位置で固定してみたのですが、実際ジクウドライバーとの連携をテストしてみると、これが全然うまくいかず。逆にドライバーとウオッチの間の距離をある程度離してみるとうまく動いてくれたので、赤外線LEDはドライバーの赤外線受信モジュールからできるだけ距離を空けるよう、なるべくウォッチ本体の奥まった位置で固定することにしました。これで、ドライバーとの連携動作がうまく動いてくれるようになりました。
ソフトウェア解説
最後に、ソフトウェア解説です。
ベースは以前万丈ライドウォッチをご紹介する際に公開したコードの通りなのですが、今回ライダーの召喚用に新たな状態遷移を追加しています。簡単に表すと、こんな感じになります。
一応ライダータイムの遷移も残していますが、今回はこのときは音が鳴らないようにしています。
続いて赤外線通信によるジクウドライバーの表示変化の方法についてですが、まず前提として、ジクウドライバーをどうやってソフト的に制御できるようにするかを説明します。
上図はドライバーのRスロットを内側から見た図です。動画内でも同様の説明をしていますが、ジクウドライバーは結局のところ、赤枠部分の電位の変化(HIGH/LOW)でウォッチを認識し、黄枠部分の電位の変化で回転を認識しています。つまり、この部分の電位を物理スイッチの操作に依らず、マイコンで直接操作してやれば、ドライバーの基板にウォッチの認識とドライバーの回転を錯覚させることができるようになります。Lスロット側も同様です。
ジクウドライバーにおいては、何もスイッチが押されていない状態では電位はHIGHになります。これをArduino側でGNDに落としてやればLOWになります。
ドライバーの回転については単純にHIGH/LOWを繰り返すだけで認識されますが、ウォッチの認識はきちんと理屈を考える必要があります。
ウォッチを装填すると、ドライバー側の認識スイッチが連続でON/OFFされて、上図①〜⑦のような電位変化が連続で発生します。この順番を再現する形で電位変化を発生させれば、ウォッチを認識させることが可能になります。認識後に逆順で電位を変化させれば、ウォッチを外すことに相当します。各ウォッチごとに異なる電位変化を発生させる必要がありますが、これについてほ、クレーンの丈様が公開してくださっている識別パターン一覧を参考にしてプログラムを作成させて頂きました。ありがとうございました。
これでジクウドライバーの操作をソフト的に制御できることがわかったので、最後にウォッチとドライバーの間の赤外線通信の方法の説明です。
先にも述べましたとおり、この方法自体は私が考え出したものではなく、かっぱ次郎様が考案されたものです。その内容についてはこちらで公開してくださっていますが、今回はそちらをベースに少しアレンジさせて頂きました。具体的には、Lスロットの操作だけでなく、Rスロットとベルトの回転も操作できるように拡張しています。
この二つの操作を追加したのは、タッチ操作によるライダー召喚をスムーズに行うためです。まず、RスロットではなくLスロットに昭和ライダーを認識させてしまうと、召喚のたびにRスロット側の『ZI-O』が表示されてから各昭和ライダーの表示がなされる(あるいは、Rスロットをリセットしても表示までに時間がかかる)ことになり、ちょっと間延びしてしまう感じがししました。また、召喚のたびに毎回ベルトを回転させる動作も、あっても良いかもしれませんが、個人的にはちょっと億劫な感じがしましたので、これも省略できるようにしたいと思いました。
そんなわけで、ウォッチ側からは、「RとLのどちらのスロットを操作するか」「どのライドウォッチを認識させるか」「ウォッチの認識後にドライバーの回転を認識させるか」の3つの情報を赤外線に乗せて通信させることにしました。イメージとしては、こんな感じです。
3つの情報の前後に、送信開始認識用と送信終了認識用の情報を合わせて送信しています。この赤外線送受信プログラムについては、かっぱ次郎様の記事内でも紹介されていた以下の本の内容を参照して作成致しました。
ちょっと躓いたところとしては、赤外線LEDの点滅間隔の調整です。詳しくは上記の本を読んで頂きたいですが、赤外線信号で「1(HIGH)」を表したいときには、その期間ずっと赤外線LEDを点灯させておけばよいということはなく、対向の赤外線受信モジュールの中心周波数(キャリア周波数)に合わせて一定間隔の細かい点滅を繰り返すことで「1」を認識させる必要があります。その「細かい点滅」の調整係数が、使用するArduinoの動作周波数によって異なるということに気づくのが遅れて、ちょっとハマってしまいました。手持ちのArduinoの都合で、最初の検証では16MHzで動作するArduino UNO、本番では8MHzで動作するArduino Pro miniを使っていたので。具体的にどういう値を設定したかは、最後に掲載するソースコードをご参照ください。
それから、ライドウォッチの識別情報には1byteを割り当てているので、理屈では0〜255までの数字を扱うことができるはずなのですが、255を送ったときには何故か赤外線受信モジュールが反応してくれませんでした。そのため、「ウォッチの認識はさせずにドライバーの回転だけ認識させたい」というときには、ウォッチの識別情報は255ではなく121(←正規のライドウォッチの認識番号の上限+1)を送るようにしています。
だいぶ長くなってしまいましたが、解説は以上になります。お疲れ様でした。
まとめ
ということで、ZXライドウォッチのご紹介でした。前作のクウガライドウォッチから二ヶ月弱での制作になりましたが、これは会社の夏期休暇をうまく挟んで時間をとれたからで、通常なら三ヶ月かかってもおかしくないぐらい大変でした。その分やり遂げた時にはかなりの達成感がありましたが、正直、二度と作りたくないライドウォッチです。
余談ですが、今回ZXライドウォッチを作成するにあたり、「昭和ライダーのことをある程度ちゃんと知らなくちゃいけんよな」と思い、「仮面ライダーSPIRITS」の既刊(「新」含む) を全巻購入・読破し、また通勤のときにひたすら昭和ライダーの主題歌を聞き込んでいました。結果、これまで正直、昭和ライダーには特に強い思い入れがあるというわけでもなかったのですが、「やっぱり昭和もカッコ良いなあ」と思うに至りました。ライダーのカッコ良さは時代を超えて不変です。
『ジオウ』については元々がレジェンドライダーが題材ということで、「今回はレジェンド題材とかで特に自分が何か作るということはしなくて良いかなあ」と放映当初は思っていましたが、終わって見れば例年通り色々作ってしまいました。今回も一年間楽しませて頂き、制作スタッフの方々に改めて感謝申し上げます。ありがとうございました。
(↑これまで作ってきた『ジオウ』関連の作品達。ネオディエンドライドウォッチは#Rin_chのK様の元にあります)
今回のZXライドウォッチで、自分の『ジオウ』関連の作品作りは最後になります。自分としては結構やりきった感がありますので、心置きなく『ゼロワン』へと移行できそうです。具体的に何を作るかが決まっているわけではありませんが、今後も折を見て何か作っていきたいと思いますので、今後とも宜しくお願い致します。
ソースコード
最後に、参考としてソースコードの全文を掲載しておきます。まずはジクウドライバー側です。
#define R_SW_A_PIN 2
#define R_SW_B_PIN 3
#define R_SW_C_PIN 4
#define L_SW_A_PIN 5
#define L_SW_B_PIN 6
#define L_SW_C_PIN 7
#define ROLL_SW_PIN 8
#define POWER_LED_PIN 9
#define IR_RX_PIN 10
#define R_SLOT_SW_PIN 11
#define SW_TEST_PIN_1 11
#define SW_TEST_PIN_2 12
#define SW_TEST_PIN_3 13
#define SIDE_R 0
#define SIDE_L 1
#define ROLL_OFF 0
#define ROLL_ON 1
uint8_t r_slot_sw = HIGH;
uint8_t prev_r_slot_sw = HIGH;
////////// IR信号の受信 ////////////////////////////////////////////////////////////
#define IR_ON LOW
#define IR_OFF HIGH
#define LEN_IR_DATA 8
// 使用する赤外線受信モジュール(OSRB38C9AA)の中心周波数
// (キャリア周波数)が37.9kHzなので、それに応じて定数を定める
#define IR_LONG_DURATION_MICRO_SEC 600
////////// ジクウドライバーの制御 ////////////////////////////////////////////////////////////
#define NUM_INPUT_CODE 7
#define SET_INTERVAL_MS 20 // これより短くすると動作が安定しない
const uint8_t INIT_CODE = B11111100; // ドライバーの両側に何も挿さっていない状態を想定
const uint8_t R_DELIMITER_CODE = B00011100; // Rスロット側のピン認識区切り用コード
const uint8_t L_DELIMITER_CODE = B11100000; // Lスロット側のピン認識区切り用コード
const uint8_t R_MASK_CODE = B00011100; // Rスロット側の電位の状態を保持するためのコード
const uint8_t L_MASK_CODE = B11100000; // Lスロット側の電位の状態を保持するためのコード
const uint8_t END_CODE = B00000000; // 両スロット共通のコード送信完了用コード
const uint8_t RIDER_CODES[][3] PROGMEM = {
// ピンありを"0"、ピンなしを"1"として、スイッチCBAの順に01を並べる
{B110,B101,B100}, // No.001 ZI-O 2018
{B110,B101,B011}, // No.002 ZI-O 2019
{B110,B101,B101}, // No.003 ZI-O 2038
{B110,B101,B001}, // No.004 ZI-O 2058
{B110,B100,B101}, // No.005 ZI-O ****
{B110,B100,B011}, // No.006 ZI-O 2000
{B110,B100,B010}, // No.007 ZI-O 0000
{B110,B100,B001}, // No.008 ZI-O 1971
{B110,B011,B101}, // No.009 ZI-O 1989
{B110,B011,B100}, // No.010 RIDER NEXT
{B110,B011,B010}, // No.011 ZI-O ライダ
{B110,B011,B001}, // No.012 GEIZ 2068
{B110,B010,B101}, // No.013 GEIZ 2019
{B110,B010,B100}, // No.014 GEIZ 2038
{B110,B010,B011}, // No.015 GEIZ 2058
{B110,B010,B001}, // No.016 GEIZ ****
{B110,B001,B101}, // No.017 GEIZ 2000
{B110,B001,B100}, // No.018 GEIZ 1010
{B110,B001,B011}, // No.019 GEIZ 1971
{B110,B001,B010}, // No.020 GEIZ 1989
{B101,B110,B100}, // No.021 GEIZ 2020
{B101,B110,B011}, // No.022 GEIZ ライダ
{B101,B110,B010}, // No.023 TSUKUYOMI 2068
{B101,B110,B001}, // No.024 RIDER NEXT
{B101,B100,B110}, // No.025 RIDER ****
{B101,B100,B011}, // No.026 RIDER 2019
{B101,B100,B010}, // No.027 RIDER ライダ
{B101,B100,B001}, // No.028 RIDER 0000
{B101,B011,B110}, // No.029 NEXT 0000
{B101,B011,B100}, // No.030 FUTURE 0000
{B101,B011,B010}, // No.031 NEW 0000
{B101,B011,B001}, // No.032 KUUGA 2000
{B101,B010,B110}, // No.033 AGITO 2001
{B101,B010,B100}, // No.034 RYUKI 2002
{B101,B010,B011}, // No.035 FAIZ 2003
{B101,B010,B001}, // No.036 BLADE 2004
{B101,B001,B110}, // No.037 HIBIKI 2005
{B101,B001,B100}, // No.038 KABUTO 2006
{B101,B001,B011}, // No.039 DEN-O 2007
{B101,B001,B010}, // No.040 KIVA 2008
{B100,B110,B101}, // No.041 DECADE 2009
{B100,B110,B011}, // No.042 W 2009
{B100,B110,B010}, // No.043 OOO 2010
{B100,B110,B001}, // No.044 FOURZE 2011
{B100,B101,B110}, // No.045 WIZARD 2012
{B100,B101,B011}, // No.046 GAIM 2013
{B100,B101,B010}, // No.047 DRIVE 2014
{B100,B101,B001}, // No.048 GHOST 2015
{B100,B011,B110}, // No.049 EX-AID 2016
{B100,B011,B101}, // No.050 BUILD 2017
{B100,B011,B010}, // No.051 CROSS-Z 2017
{B100,B011,B001}, // No.052 GREASE 2017
{B100,B010,B110}, // No.053 ROGUE 2017
{B100,B010,B101}, // No.054 BRAVE 2016
{B100,B010,B011}, // No.055 SNIPE 2016
{B100,B010,B001}, // No.056 LAZER 2016
{B100,B001,B110}, // No.057 PARA-DX 2016
{B100,B001,B101}, // No.058 POPPY 2016
{B100,B001,B011}, // No.059 RIDER1 1971
{B100,B001,B010}, // No.060 RIDER2 1971
{B011,B110,B101}, // No.061 V3 1973
{B011,B110,B100}, // No.062 RIDERMAN 1973
{B011,B110,B010}, // No.063 X 1974
{B011,B110,B001}, // No.064 AMAZON 1974
{B011,B101,B110}, // No.065 STRONGER 1975
{B011,B101,B100}, // No.066 SKYRIDER 1979
{B011,B101,B010}, // No.067 SUPER-1 1980
{B011,B101,B001}, // No.068 ZX 1984
{B011,B100,B110}, // No.069 BLACK 1987
{B011,B100,B101}, // No.070 BLACK RX 1988
{B011,B100,B010}, // No.071 SHIN 1992
{B011,B100,B001}, // No.072 ZO 1993
{B011,B010,B110}, // No.073 J 1994
{B011,B010,B101}, // No.074 GENM 2016
{B011,B010,B100}, // No.075 CRONUS 2016
{B011,B010,B001}, // No.076 EVOL 2017
{B011,B001,B110}, // No.077 SHOCKER 1971
{B011,B001,B101}, // No.078 ALFA 2016
{B011,B001,B100}, // No.079 OMEGA 2016
{B011,B001,B010}, // No.080 NEO 2017
{B010,B110,B101}, // No.081 RIDE STRIKER
{B010,B110,B100}, // No.082 ALL RIDERS ライダ
{B010,B110,B011}, // No.083 SUPER SENTAI -SS-
{B010,B110,B001}, // No.084 HEISEI -HR-
{B010,B101,B110}, // No.085 SHOWA -SR-
{B010,B101,B100}, // No.086 KAMEN RIDER ライダ
{B010,B101,B011}, // No.087 EVIL EVIL
{B010,B101,B001}, // No.088 ANCIENT 0000
{B010,B100,B110}, // No.089 GAME GAME
{B010,B100,B101}, // No.090 FUTURE 0000
{B010,B100,B011}, // No.091 X MAS 1225
{B010,B100,B001}, // No.092 SPECIAL -SP-
{B010,B011,B110}, // No.093 RIDER 0000
{B010,B011,B101}, // No.094 ZI-O ライダ
{B010,B011,B100}, // No.095 SINGULAR 0000
{B010,B011,B001}, // No.096 NOTHING 0000
{B010,B001,B110}, // No.097 MILLENIUM 2000
{B010,B001,B101}, // No.098 MAX MAX
{B010,B001,B100}, // No.099 DIEND 2009
{B010,B001,B011}, // No.100 GENIUS FIN
{B001,B110,B101}, // No.101 MUTEKI FIN
{B001,B110,B100}, // No.102 MUGEN FIN
{B001,B110,B011}, // No.103 TRIDRON FIN
{B001,B110,B010}, // No.104 KIWAMI FIN
{B001,B101,B110}, // No.105 INFINITY FIN
{B001,B101,B100}, // No.106 COSMIC FIN
{B001,B101,B011}, // No.107 PTOTYRA FIN
{B001,B101,B010}, // No.108 XTREME FIN
{B001,B100,B110}, // No.109 COMPLETE FIN
{B001,B100,B101}, // No.110 EMPEROR FIN
{B001,B100,B011}, // No.111 LINER FIN
{B001,B100,B010}, // No.112 HYPER FIN
{B001,B011,B110}, // No.113 ARMED FIN
{B001,B011,B101}, // No.114 KING FIN
{B001,B011,B100}, // No.115 BLASTER FIN
{B001,B011,B010}, // No.116 SURVIVE FIN
{B001,B010,B110}, // No.117 SHINING FIN
{B001,B010,B101}, // No.118 ULTIMATE FIN
{B001,B010,B100}, // No.119 RIDE GADGET
{B001,B010,B011} // No.120 RIDER ライダ
};
#define NONE 0
uint8_t r_rider = NONE;
uint8_t l_rider = NONE;
uint8_t r_reset_code[3] = {R_DELIMITER_CODE, R_DELIMITER_CODE, R_DELIMITER_CODE}; // 入力と逆順でコードを保持
uint8_t l_reset_code[3] = {L_DELIMITER_CODE, L_DELIMITER_CODE, L_DELIMITER_CODE}; // 入力と逆順でコードを保持
void generate_set_code(uint8_t set_side, uint8_t* code){
// 逆サイドの電位状態を保持する形のコードに書き換える
if(set_side == SIDE_R){
for(uint8_t i=0; i<NUM_INPUT_CODE; i++){
code[i] = (PORTD & L_MASK_CODE) | code[i];
}
}else if(set_side == SIDE_L){
for(uint8_t i=0; i<NUM_INPUT_CODE; i++){
code[i] = (PORTD & R_MASK_CODE) | code[i];
}
}
}
void reset_rider_code(uint8_t side){
// 入力時と逆順のコードを入力する
uint8_t code[NUM_INPUT_CODE] = {0,0,0,0,0,0,0};
if(side == SIDE_R){
code[0] = R_DELIMITER_CODE;
code[1] = r_reset_code[0];
code[2] = R_DELIMITER_CODE;
code[3] = r_reset_code[1];
code[4] = R_DELIMITER_CODE;
code[5] = r_reset_code[2];
code[6] = R_DELIMITER_CODE;
generate_set_code(SIDE_R, code);
r_rider = NONE;
}else if(side == SIDE_L){
code[0] = L_DELIMITER_CODE;
code[1] = l_reset_code[0];
code[2] = L_DELIMITER_CODE;
code[3] = l_reset_code[1];
code[4] = L_DELIMITER_CODE;
code[5] = l_reset_code[2];
code[6] = L_DELIMITER_CODE;
generate_set_code(SIDE_L, code);
l_rider = NONE;
}
for(uint8_t i=0; i<NUM_INPUT_CODE; i++){
PORTD = code[i];
delay(SET_INTERVAL_MS);
}
};
void write_rider_code(uint8_t side, uint8_t rider_number){
uint8_t code[NUM_INPUT_CODE] = {0,0,0,0,0,0,0};
if(side == SIDE_R){
// 既にRスロットに何か挿さっている想定の場合は、Rスロットをリセットする
if(r_rider != NONE){
reset_rider_code(SIDE_R);
}
if(rider_number == 0){
// 0が送られてきたときは、スロットのリセットだけして終了する
return;
}
// Rスロット用コード作成。操作するのは2,3,4ピンのみなので、シフト演算でB000CBA00の形にする
code[0] = pgm_read_byte(&(RIDER_CODES[rider_number-1][0])) << 2;
code[1] = R_DELIMITER_CODE;
code[2] = pgm_read_byte(&(RIDER_CODES[rider_number-1][1])) << 2;
code[3] = R_DELIMITER_CODE;
code[4] = pgm_read_byte(&(RIDER_CODES[rider_number-1][2])) << 2;
code[5] = R_DELIMITER_CODE;
code[6] = END_CODE;
// リセット用にコードを逆順に保持する
r_reset_code[0] = code[4];
r_reset_code[1] = code[2];
r_reset_code[2] = code[0];
// Lスロット側の状態を保持するコードに書き換える
generate_set_code(SIDE_R, code);
// 現在セットされているライダーの識別番号を保持
r_rider = rider_number;
}else if(side == SIDE_L){
// 既にLスロットに何か挿さっている想定の場合は、Lスロットをリセットする
if(l_rider != NONE){
reset_rider_code(SIDE_L);
}
if(rider_number == 0){
// 0が送られてきたときは、スロットのリセットだけして終了する
return;
}
// Lスロット用コード作成。操作するのは5,6,7ピンのみなので、シフト演算でBCBA00000のCBAの形にする
code[0] = pgm_read_byte(&(RIDER_CODES[rider_number-1][0])) << 5;
code[1] = L_DELIMITER_CODE;
code[2] = pgm_read_byte(&(RIDER_CODES[rider_number-1][1])) << 5;
code[3] = L_DELIMITER_CODE;
code[4] = pgm_read_byte(&(RIDER_CODES[rider_number-1][2])) << 5;
code[5] = L_DELIMITER_CODE;
code[6] = END_CODE;
// リセット用にコードを逆順に保持する
l_reset_code[0] = code[4];
l_reset_code[1] = code[2];
l_reset_code[2] = code[0];
// Rスロット側の状態を保持するコードに書き換える
generate_set_code(SIDE_L, code);
// 現在セットされているライダーの識別番号を保持
l_rider = rider_number;
}
// 最終的なコードをセットする
for(uint8_t i=0; i<NUM_INPUT_CODE; i++){
PORTD = code[i];
delay(SET_INTERVAL_MS);
}
}
void roll_driver(){
digitalWrite(ROLL_SW_PIN, HIGH); // 回転認識スイッチが離された想定
delay(SET_INTERVAL_MS);
digitalWrite(ROLL_SW_PIN, LOW); // 回転認識スイッチが押された想定
}
////////// メイン処理 ////////////////////////////////////////////////////////////
void setup() {
Serial.begin(115200);
pinMode(R_SW_A_PIN, OUTPUT);
pinMode(R_SW_B_PIN, OUTPUT);
pinMode(R_SW_C_PIN, OUTPUT);
pinMode(L_SW_A_PIN, OUTPUT);
pinMode(L_SW_B_PIN, OUTPUT);
pinMode(L_SW_C_PIN, OUTPUT);
pinMode(ROLL_SW_PIN, OUTPUT);
pinMode(POWER_LED_PIN, OUTPUT);
pinMode(SW_TEST_PIN_1, INPUT_PULLUP);
pinMode(SW_TEST_PIN_2, INPUT_PULLUP);
pinMode(SW_TEST_PIN_3, INPUT_PULLUP);
pinMode(IR_RX_PIN, INPUT_PULLUP);
pinMode(R_SLOT_SW_PIN, INPUT_PULLUP);
// 初期状態
PORTD = INIT_CODE; // ライドウォッチが挿さっていない状態を想定
digitalWrite(ROLL_SW_PIN, LOW); // 回転認識スイッチが押されている状態を想定
digitalWrite(POWER_LED_PIN, HIGH); // Arduinoが動作中であることを示すため、通電中は常時ON
}
void loop() {
while(1){ // 常にIR信号の受信待ちにする
// スタートビット(1)チェック
if(digitalRead(IR_RX_PIN) == IR_ON){ // とりあえず何かしらのIR信号を受信した
//Serial.print(F("Start"));
delayMicroseconds(400);
}else{
break; // 何もIR信号を受信していないければ、ここで終了
}
if(digitalRead(IR_RX_PIN) == IR_ON){ // 400μs待ってまたスタートビットの信号が続いているかチェックする
delayMicroseconds(IR_LONG_DURATION_MICRO_SEC);
}else{
break; // 受信した信号はスタートビットではなかったので、ここで終了
}
// スタートビットが確認できたので、
// スロット情報(1ビット), 識別番号情報(8ビット), ベルト回転要否情報を読み取る
uint8_t side = !digitalRead(IR_RX_PIN); // HIGHが送信されていれば受信機の出力はLOWなので、反転させる
delayMicroseconds(IR_LONG_DURATION_MICRO_SEC);
uint8_t rider_number = 0;
for(uint8_t i=0;i<LEN_IR_DATA;i++){
rider_number = rider_number | (!digitalRead(IR_RX_PIN) << i); // 8bitの右端から受信する
delayMicroseconds(IR_LONG_DURATION_MICRO_SEC);
}
uint8_t roll = !digitalRead(IR_RX_PIN); // HIGHが送信されていれば受信機の出力はLOWなので、反転させる
delayMicroseconds(IR_LONG_DURATION_MICRO_SEC);
// ストップビット(1010)チェック
if(digitalRead(IR_RX_PIN) == IR_ON){
delayMicroseconds(IR_LONG_DURATION_MICRO_SEC);
}else{
break; // ストップビットが不正なので、ここで終了
}
if(digitalRead(IR_RX_PIN) == IR_OFF){
delayMicroseconds(IR_LONG_DURATION_MICRO_SEC);
}else{
break; // ストップビットが不正なので、ここで終了
}
if(digitalRead(IR_RX_PIN) == IR_ON){
delayMicroseconds(IR_LONG_DURATION_MICRO_SEC);
}else{
break; // ストップビットが不正なので、ここで終了
}
if(digitalRead(IR_RX_PIN) == IR_OFF){
delayMicroseconds(IR_LONG_DURATION_MICRO_SEC);
}else{
break; // ストップビットが不正なので、ここで終了
}
// ストップビットまで確認できたので、読み取った値を確定させて後続の処理を続ける
Serial.print(F("Side:"));
Serial.println(side);
Serial.print(F("Rider Number:"));
Serial.println(rider_number);
Serial.print(F("Roll:"));
Serial.println(roll);
// ジクウドライバーにライダー識別信号を送信。CODE_121が送られてきたときは送信しない
if(rider_number != 121){
write_rider_code(side, rider_number);
}
// ジクウドライバーにドライバー回転信号を送信
if(roll == ROLL_ON){
if(rider_number != 121){
delay(1000); // ライダー識別信号送信から連続するときのみ、ディレイを設ける
}
roll_driver();
}
}
r_slot_sw = digitalRead(R_SLOT_SW_PIN);
if(prev_r_slot_sw == HIGH && r_slot_sw == LOW){ // OFFからONに変わればジオウ装填とみなす
Serial.println(F("Set ZI-O."));
write_rider_code(SIDE_R, 1);
}else if(prev_r_slot_sw == LOW && r_slot_sw == HIGH){ // ONからOFFに変わればジオウ解除とみなす
Serial.println(F("Remove ZI-O."));
reset_rider_code(SIDE_R);
}
prev_r_slot_sw = r_slot_sw;
}
続いてZXライドウォッチ側です。
////////// 基本定義 ////////////////////////////////////////////////////////////
#define SW_C_PIN 4
#define SW_1_PIN 5
#define SW_2_PIN 6
#define SW_OPEN_CLOSE_PIN 7
#define LOOP_INTERVAL_MS 20
// ライドウォッチの識別コード
#define CODE_000 0 // 便宜上の定義。スロットをリセットするときに指定する
#define CODE_001_ZIO_2018 1
#define CODE_002_ZIO_2019 2
#define CODE_003_ZIO_2038 3
#define CODE_004_ZIO_2058 4
#define CODE_005_ZIO_____ 5
#define CODE_006_ZIO_2000 6
#define CODE_007_ZIO_0000 7
#define CODE_008_ZIO_1971 8
#define CODE_009_ZIO_1989 9
#define CODE_010_RIDER_NEXT 10
#define CODE_011_ZIO_RIDER 11
#define CODE_012_GEIZ_2068 12
#define CODE_013_GEIZ_2019 13
#define CODE_014_GEIZ_2038 14
#define CODE_015_GEIZ_2058 15
#define CODE_016_GEIZ_____ 16
#define CODE_017_GEIZ_2000 17
#define CODE_018_GEIZ_1010 18
#define CODE_019_GEIZ_1971 19
#define CODE_020_GEIZ_1989 20
#define CODE_021_GEIZ_2020 21
#define CODE_022_GEIZ_RIDER 22
#define CODE_023_TSUKUYOMI_2068 23
#define CODE_024_RIDER_NEXT 24
#define CODE_025_RIDER_____ 25
#define CODE_026_RIDER_2019 26
#define CODE_027_NEXT_0000 27
#define CODE_028_RIDER_0000 28
#define CODE_029_NEXT_0000 29
#define CODE_030_FUTURE_0000 30
#define CODE_031_NEW_0000 31
#define CODE_032_KUUGA_2000 32
#define CODE_033_AGITO_2001 33
#define CODE_034_RYUKI_2002 34
#define CODE_035_FAIZ_2003 35
#define CODE_036_BLADE_2004 36
#define CODE_037_HIBIKI_2005 37
#define CODE_038_KABUTO_2006 38
#define CODE_039_DENO_2007 39
#define CODE_040_KIVA_2008 40
#define CODE_041_DECADE_2009 41
#define CODE_042_W_2009 42
#define CODE_043_OOO_2010 43
#define CODE_044_FOURZE_2011 44
#define CODE_045_WIZARD_2012 45
#define CODE_046_GAIM_2013 46
#define CODE_047_DRIVE_2014 47
#define CODE_048_GHOST_2015 48
#define CODE_049_EXAID_2016 49
#define CODE_050_BUILD_2017 50
#define CODE_051_CROSSZ_2017 51
#define CODE_052_GREASE_2017 52
#define CODE_053_ROGUE_2017 53
#define CODE_054_BRAVE_2016 54
#define CODE_055_SNIPE_2016 55
#define CODE_056_LAZER_2016 56
#define CODE_057_PARADX_2016 57
#define CODE_058_POPPY_2016 58
#define CODE_059_RIDER1_1971 59
#define CODE_060_RIDER2_1971 60
#define CODE_061_V3_1973 61
#define CODE_062_RIDERMAN_1973 62
#define CODE_063_X_1974 63
#define CODE_064_AMAZON_1974 64
#define CODE_065_STRONGER_1975 65
#define CODE_066_SKYRIDER_1979 66
#define CODE_067_SUPER1_1980 67
#define CODE_068_ZX_1984 68
#define CODE_069_BLACK_1987 69
#define CODE_070_BLACKRX_1988 70
#define CODE_071_SHIN_1992 71
#define CODE_072_ZO_1993 72
#define CODE_073_J_1994 73
#define CODE_074_GENM_2016 74
#define CODE_075_CRONUS_2016 75
#define CODE_076_EVOL_2017 76
#define CODE_077_SHOCKER_1971 77
#define CODE_078_ALFA_2016 78
#define CODE_079_OMEGA_2016 79
#define CODE_080_NEO_2017 80
#define CODE_081_RIDE_STRIKER 81
#define CODE_082_ALLRIDERS_RIDER 82
#define CODE_083_SUPERSENTAI_SS 83
#define CODE_084_HEISEI_SS 84
#define CODE_085_SHOWA_SR 85
#define CODE_086_KAMENRIDER_RIDER 86
#define CODE_087_EVIL_EVIL 87
#define CODE_088_ANCIENT_0000 88
#define CODE_089_GAME_GAME 89
#define CODE_090_FUTURE_0000 90
#define CODE_091_XMAS_1225 91
#define CODE_092_SPECIAL_SP 92
#define CODE_093_RIDER_0000 93
#define CODE_094_ZIO_RIDER 94
#define CODE_095_SINGULAR_0000 95
#define CODE_096_NOTHING_0000 96
#define CODE_097_MILLENIUM_2000 97
#define CODE_098_MAX_MAX 98
#define CODE_099_DIEND_2009 99
#define CODE_100_GENIUS_FIN 100
#define CODE_101_MUTEKI_FIN 101
#define CODE_102_MUGEN_FIN 102
#define CODE_103_TRIDRON_FIN 103
#define CODE_104_KIWAMI_FIN 104
#define CODE_105_INFINITY_FIN 105
#define CODE_106_COSMIC_FIN 106
#define CODE_107_PTOTYRA_FIN 107
#define CODE_108_XTREME_FIN 108
#define CODE_109_COMPLETE_FIN 109
#define CODE_110_EMPEROR_FIN 110
#define CODE_111_LINER_FIN 111
#define CODE_112_HYPER_FIN 112
#define CODE_113_ARMED_FIN 113
#define CODE_114_KING_FIN 114
#define CODE_115_BLASTER_FIN 115
#define CODE_116_SURVIVE_FIN 116
#define CODE_117_SHINING_FIN 117
#define CODE_118_ULTIMATE_FIN 118
#define CODE_119_RIDE_GADGET 119
#define CODE_120_RIDER_RIDER 120
#define CODE_121 121 // 便宜上の定義。ジクウドライバーの回転のみを発生させるときに指定する
uint8_t selected_rider_code = CODE_000;
uint8_t prev_selected_rider_code = CODE_000;
// 3つのボタン入力の表現
#define N_BUTTON 3
#define ON LOW
#define OFF HIGH
const uint8_t OFF_OFF_OFF[] = {OFF, OFF, OFF};
const uint8_t OFF_OFF_ON[] = {OFF, OFF, ON};
const uint8_t OFF_ON_OFF[] = {OFF, ON, OFF};
const uint8_t OFF_ON_ON[] = {OFF, ON, ON};
const uint8_t ON_OFF_OFF[] = {ON, OFF, OFF};
const uint8_t ON_OFF_ON[] = {ON, OFF, ON};
const uint8_t ON_ON_OFF[] = {ON, ON, OFF};
const uint8_t ON_ON_ON[] = {ON, ON, ON};
uint8_t prev_sw[] = {OFF, OFF, OFF};
uint8_t sw[] = {OFF, OFF, OFF};
// 展開状態の表現
#define RIDERS_OPEN LOW
#define RIDERS_CLOSE HIGH
uint8_t open_close_state = RIDERS_CLOSE;
// 状態の定義
#define STATE_SINGLE_A 1
#define STATE_SINGLE_B 2
#define STATE_READY_WP 3
#define STATE_WEAPON 4
#define STATE_READY_CH 5
#define STATE_CHANGED 6
#define STATE_READY_CR 7
#define STATE_CRITICAL 8
#define STATE_SUMMON 9
#define SIDE_NONE 0
#define SIDE_ARMOR_TIME 1
#define SIDE_RIDER_TIME 2
uint8_t prev_state = STATE_SINGLE_A;
uint8_t state = STATE_SINGLE_A;
uint8_t prev_side = SIDE_NONE;
uint8_t side = SIDE_NONE;
uint8_t rider_time_counter = 0;
uint8_t armor_time_counter = 0;
// メインテーマ再生状態管理用
boolean is_playing_song = false;
////////// IR信号の送信 ////////////////////////////////////////////////////////////
#define IR_TX_PIN 9
#define LEN_IR_DATA 8
#define SIDE_R 0
#define SIDE_L 1
#define ROLL_OFF 0
#define ROLL_ON 1
// 対向の赤外線受信モジュール(OSRB38C9AA)の中心周波数
// (キャリア周波数)が37.9kHzなので、それに応じて定数を定める
#define IR_SHORT_DURATION_MICRO_SEC 5 // 16MHzのArduinoなら10、8MHzのArduinoなら5ぐらい
#define IR_LONG_DURATION_MICRO_SEC 600
#define NUM_IR_ON_OFF 23 // 26μs/回 * 23回 = 598μs
#define IR_SEND_INTERVAL_MS 1020
void send_bit_0(){
digitalWrite(IR_TX_PIN, LOW);
delayMicroseconds(IR_LONG_DURATION_MICRO_SEC);
//Serial.println(F("0")); // デバッグ用
}
void send_bit_1(){
// 1回のループでおよそ26μsになるようにIR_SHORT_DURATION_MICRO_SECを設定。
// (←うまく動かないときはまずこの値を調整する)
// ループの全体でおよそIR_LONG_DURATION_MICRO_SEC(=600)になるようにする
for(uint8_t i=0;i<NUM_IR_ON_OFF;i++){
digitalWrite(IR_TX_PIN, HIGH);
delayMicroseconds(IR_SHORT_DURATION_MICRO_SEC);
digitalWrite(IR_TX_PIN, LOW);
delayMicroseconds(IR_SHORT_DURATION_MICRO_SEC);
}
//Serial.println(F("1")); // デバッグ用
}
void send_ir(uint8_t side, uint8_t rider_number, uint8_t roll){
// スタートビットの送信
send_bit_1();
// スロット情報(R/L)の送信
if(side == SIDE_R){
send_bit_0();
}else{
send_bit_1();
}
// 識別番号情報の送信
for(uint8_t i=0;i<LEN_IR_DATA;i++){
uint8_t send_bit = bitRead(rider_number, i); // 8bitの右端から送信する
if(send_bit == 1){
send_bit_1();
}else{
send_bit_0();
}
}
// ベルト回転要否情報の送信
if(roll == ROLL_ON){
send_bit_1();
}else{
send_bit_0();
}
// ストップビットの送信
send_bit_1();
send_bit_0();
send_bit_1();
send_bit_0();
// デバッグ用
Serial.println(F("IR Send:"));
Serial.println(side);
Serial.println(rider_number);
Serial.println(roll);
}
////////// 音声処理 ////////////////////////////////////////////////////////////////
#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>
#define SOUND_POWER_ON 1
#define SOUND_EJECT 2
#define SOUND_ZX_RIDER_TIME 3
#define SOUND_ZX_ARMOR_TIME 4
#define SOUND_SHOWA_SINGLE 5
#define SOUND_SHOWA_RIDER_ARMOR_WAITING 6
#define SOUND_SHOWA_INIT 7
#define SOUND_SHOWA_ARMOR_TIME 8
#define SOUND_SHOWA_FINISH_READY_AND_WAITING 9
#define SOUND_SHOWA_FINISH_TIME 10
#define SOUND_SUMMON_1ST_READY 11
#define SOUND_SUMMON_2ND_READY 12
#define SOUND_SUMMON_V3_READY 13
#define SOUND_SUMMON_RIDERMAN_READY 14
#define SOUND_SUMMON_X_READY 15
#define SOUND_SUMMON_AMAZON_READY 16
#define SOUND_SUMMON_STRONGER_READY 17
#define SOUND_SUMMON_SKYRIDER_READY 18
#define SOUND_SUMMON_SUPER1_READY 19
#define SOUND_SUMMON_ZX_READY 20
#define SOUND_SUMMON_1ST 21
#define SOUND_SUMMON_2ND 22
#define SOUND_SUMMON_V3 23
#define SOUND_SUMMON_RIDERMAN 24
#define SOUND_SUMMON_X 25
#define SOUND_SUMMON_AMAZON 26
#define SOUND_SUMMON_STRONGER 27
#define SOUND_SUMMON_SKYRIDER 28
#define SOUND_SUMMON_SUPER1 29
#define SOUND_SUMMON_ZX 30
#define SONG_1ST 31
#define SONG_2ND 32
#define SONG_V3 33
#define SONG_RIDERMAN 34
#define SONG_X 35
#define SONG_AMAZON 36
#define SONG_STRONGER 37
#define SONG_SKYRIDER 38
#define SONG_SUPER1 39
#define SONG_ZX 40
#define READY_CHANGE_WAIT 1000
#define ARMOR_TIME_WAIT 6500
#define RIDER_TIME_WAIT 1000
#define FINISH_TIME_WAIT_SHORT 1000
#define FINISH_TIME_WAIT_LONG 2000
#define SOUND_VOLUME_DEFAULT 22
unsigned long sound_wait_start_time = 0;
boolean is_armor_time_waiting = false;
boolean is_rider_time_waiting = false;
boolean is_armor_time_finish_waiting = false;
boolean is_rider_time_finish_waiting = false;
boolean is_ready_change_waiting = false;
uint8_t volume = SOUND_VOLUME_DEFAULT;
SoftwareSerial ss_mp3_player(2, 3); // RX, TX
DFRobotDFPlayerMini mp3_player;
void control_sound(){
if(prev_state != state){
switch(state){
case STATE_SINGLE_A:
switch(prev_state){
case STATE_SINGLE_B:
mp3_player.playMp3Folder(SOUND_SHOWA_SINGLE);
break;
case STATE_READY_WP:
case STATE_WEAPON:
case STATE_READY_CH:
case STATE_CHANGED:
case STATE_READY_CR:
case STATE_CRITICAL:
case STATE_SUMMON:
mp3_player.playMp3Folder(SOUND_EJECT);
break;
default:
;
}
break;
case STATE_SINGLE_B:
mp3_player.playMp3Folder(SOUND_SHOWA_SINGLE);
break;
case STATE_READY_WP:
if(prev_state != STATE_WEAPON){
mp3_player.pause();
}
break;
case STATE_WEAPON:
// 本来は武器サウンドが鳴るが、今回は対応させない
break;
case STATE_READY_CH:
if(prev_state != STATE_CHANGED){
if(open_close_state == RIDERS_CLOSE){ // ZX
mp3_player.pause();
}else{ // 10人ライダー
sound_wait_start_time = millis();
is_ready_change_waiting = true;
}
}else{ // ZXモードから昭和ライダーモードに移行したとき
mp3_player.playMp3Folder(SOUND_SHOWA_RIDER_ARMOR_WAITING);
}
break;
case STATE_CHANGED:
mp3_player.pause();
if(prev_state != STATE_SUMMON){ // STATE_SUMMONのときの音声は、処理の都合上状態遷移の中で鳴らす
if(side == SIDE_RIDER_TIME){
sound_wait_start_time = millis();
is_rider_time_waiting = true;
}else if(side == SIDE_ARMOR_TIME){
sound_wait_start_time = millis();
is_armor_time_waiting = true;
}
}
break;
case STATE_READY_CR:
mp3_player.playMp3Folder(SOUND_SHOWA_FINISH_READY_AND_WAITING);
break;
case STATE_CRITICAL:
mp3_player.pause();
if(side == SIDE_RIDER_TIME){
sound_wait_start_time = millis();
is_rider_time_finish_waiting = true;
}else if(side == SIDE_ARMOR_TIME){
sound_wait_start_time = millis();
is_armor_time_finish_waiting = true;
}
break;
default:
;
}
}else{
unsigned long now = millis();
if(is_ready_change_waiting){
if(now - sound_wait_start_time >= READY_CHANGE_WAIT){
mp3_player.playMp3Folder(SOUND_SHOWA_RIDER_ARMOR_WAITING);
is_ready_change_waiting = false;
}
}else if(is_rider_time_waiting){
if(now - sound_wait_start_time >= RIDER_TIME_WAIT){
// mp3_player.playMp3Folder(SOUND_RIDER_TIME); // 本来はライダータイムが鳴るが、今回は対応させない
is_rider_time_waiting = false;
}
}else if(is_armor_time_waiting){
if(now - sound_wait_start_time >= ARMOR_TIME_WAIT){
if(open_close_state == RIDERS_CLOSE){
mp3_player.playMp3Folder(SOUND_ZX_ARMOR_TIME); // ZX
}else{
mp3_player.playMp3Folder(SOUND_SHOWA_ARMOR_TIME); // 10人ライダー
}
is_armor_time_waiting = false;
}
}else if(is_rider_time_finish_waiting){
if(now - sound_wait_start_time >= FINISH_TIME_WAIT_LONG){
mp3_player.playMp3Folder(SOUND_SHOWA_FINISH_TIME);
is_rider_time_finish_waiting = false;
}
}else if(is_armor_time_finish_waiting){
if(now - sound_wait_start_time >= FINISH_TIME_WAIT_SHORT){
mp3_player.playMp3Folder(SOUND_SHOWA_FINISH_TIME);
is_armor_time_finish_waiting = false;
}
}
}
}
////////// 発光処理 ////////////////////////////////////////////////////////////////
#define LED_COLOR_PIN 8
#include <Adafruit_NeoPixel.h>
#define N_COLOR_LED 10
#define LED_1ST 0
#define LED_2ND 1
#define LED_V3 2
#define LED_RIDERMAN 3
#define LED_X 4
#define LED_AMAZON 6
#define LED_STRONGER 7
#define LED_SKYRIDER 8
#define LED_SUPER1 9
#define LED_ZX 5
#define LUMINANCE_BASE 127
#define LUMINANCE_MAX 255
#define LED_COUNT_MAX 2000 // LOOP_INTERVAL_MSが20msで、40sまでの発光定義を想定
struct color_rgb {
uint8_t r;
uint8_t g;
uint8_t b;
};
struct color_rgb COLOR_WHITE = {LUMINANCE_MAX,LUMINANCE_MAX,LUMINANCE_MAX};
struct color_rgb COLOR_RED = {LUMINANCE_MAX,0,0};
uint16_t led_counter = LED_COUNT_MAX;
unsigned long prev_blink_time = 0;
unsigned long inc_dim_start_time = 0;
boolean is_lighting = false;
boolean is_inc = false;
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(N_COLOR_LED, LED_COLOR_PIN, NEO_GRB);
void led_base_pattern_all_on(struct color_rgb *color){
inc_dim_start_time = 0;
for(uint8_t i=0;i<N_COLOR_LED;i++){
pixels.setPixelColor(i, pixels.Color(color->r,color->g,color->b));
}
}
void led_base_pattern_all_off(){
inc_dim_start_time = 0;
for(uint8_t i=0;i<N_COLOR_LED;i++){
pixels.setPixelColor(i, pixels.Color(0,0,0));
}
}
void led_base_pattern_all_blink(struct color_rgb *color, int interval_ms){
inc_dim_start_time = 0;
unsigned long now = millis();
if(now - prev_blink_time >= interval_ms){
if(is_lighting){
for(uint8_t i=0;i<N_COLOR_LED;i++){
pixels.setPixelColor(i, pixels.Color(0,0,0));
}
}else{
for(uint8_t i=0;i<N_COLOR_LED;i++){
pixels.setPixelColor(i, pixels.Color(color->r, color->g, color->b));
}
}
is_lighting = !is_lighting;
prev_blink_time = now;
}
}
void led_base_pattern_all_dim(struct color_rgb *color, int interval_ms, uint8_t steps){
unsigned long now = millis();
if(inc_dim_start_time == 0){
inc_dim_start_time = now;
}
int ms_per_step = interval_ms / steps;
int current_step = (now - inc_dim_start_time) / 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;
for(uint8_t i=0;i<N_COLOR_LED;i++){
pixels.setPixelColor(i, pixels.Color(r_step*(steps-current_step), g_step*(steps-current_step), b_step*(steps-current_step)));
}
}
void led_base_pattern_zx_blink(struct color_rgb *color, int interval_ms){
inc_dim_start_time = 0;
unsigned long now = millis();
if(now - prev_blink_time >= interval_ms){
if(is_lighting){
pixels.setPixelColor(LED_ZX, pixels.Color(0,0,0));
}else{
pixels.setPixelColor(LED_ZX, pixels.Color(color->r, color->g, color->b));
}
is_lighting = !is_lighting;
prev_blink_time = now;
}
}
void led_base_pattern_zx_inc(struct color_rgb *color, int interval_ms, uint8_t steps){
unsigned long now = millis();
if(inc_dim_start_time == 0){
inc_dim_start_time = now;
}
int ms_per_step = interval_ms / steps;
int current_step = (now - inc_dim_start_time) / 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(LED_ZX, pixels.Color(r_step*current_step, g_step*current_step, b_step*current_step));
}
void led_base_pattern_zx_dim(struct color_rgb *color, int interval_ms, uint8_t steps){
unsigned long now = millis();
if(inc_dim_start_time == 0){
inc_dim_start_time = now;
}
int ms_per_step = interval_ms / steps;
int current_step = (now - inc_dim_start_time) / 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(LED_ZX, pixels.Color(r_step*(steps-current_step), g_step*(steps-current_step), b_step*(steps-current_step)));
}
void led_base_pattern_zx_blink_slowly(struct color_rgb *color, int interval_ms, uint8_t steps){
unsigned long now = millis();
if(inc_dim_start_time == 0){
inc_dim_start_time = now;
}
int ms_per_step = interval_ms / steps;
int current_step = (now - inc_dim_start_time) / 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(LED_ZX, pixels.Color(r_step*current_step, g_step*current_step, b_step*current_step));
}else{
pixels.setPixelColor(LED_ZX, pixels.Color(r_step*(steps-current_step), g_step*(steps-current_step), b_step*(steps-current_step)));
}
if(now - inc_dim_start_time >= interval_ms){
is_inc = !is_inc;
inc_dim_start_time = 0;
}
}
void led_base_pattern_rider(struct color_rgb *color, uint8_t rider){
// 特定のライダーを点灯させる
inc_dim_start_time = 0;
pixels.setPixelColor(rider, pixels.Color(color->r,color->g,color->b));
}
void led_base_pattern_rider_seq(struct color_rgb *color, uint8_t rider){
// 指定したライダーまでをすべて点灯させる
inc_dim_start_time = 0;
if(rider <= LED_ZX){ // 1号、2号、V3、ライダーマン、X、ZX
for(uint8_t i=0;i<=rider;i++){
pixels.setPixelColor(i, pixels.Color(color->r,color->g,color->b));
}
}else{ // アマゾン、ストロンガー、スカイライダー、スーパー1
for(uint8_t i=0;i<=4;i++){
pixels.setPixelColor(i, pixels.Color(color->r,color->g,color->b));
}
for(uint8_t i=6;i<=rider;i++){
pixels.setPixelColor(i, pixels.Color(color->r,color->g,color->b));
}
}
}
void led_base_pattern_rider_only(struct color_rgb *color, uint8_t rider_code){
// 特定のライダーのみを点灯させ、他は消灯させる。
// これのみタッチ処理と関連して呼び出されるため、rider_codeを各LEDの番号に対応させる必要がある
uint8_t rider = 0;
if(CODE_059_RIDER1_1971 <= rider_code && rider_code <= CODE_063_X_1974){
rider = rider_code - 59;
}else if(CODE_064_AMAZON_1974 <= rider_code && rider_code <= CODE_067_SUPER1_1980){
rider = rider_code - 58;
}else{
rider = LED_ZX;
}
inc_dim_start_time = 0;
pixels.setPixelColor(rider, pixels.Color(color->r,color->g,color->b));
for(uint8_t i=0;i<rider;i++){
pixels.setPixelColor(i, pixels.Color(0,0,0));
}
for(uint8_t i=rider+1;i<N_COLOR_LED;i++){
pixels.setPixelColor(i, pixels.Color(0,0,0));
}
}
/////////////////////////////////////////////////////////////////////////
void led_pattern_power_on(uint16_t led_counter_ms){
if(led_counter_ms <= 3500){
// led_base_pattern_all_on(&COLOR_WHITE);
led_base_pattern_rider_only(&COLOR_WHITE, CODE_068_ZX_1984);
}else{
led_base_pattern_all_off();
}
}
void led_pattern_single(uint16_t led_counter_ms){ // 起動音+「昭和ライダー!」
if(led_counter_ms <= 600){ led_base_pattern_zx_blink(&COLOR_WHITE, 80);}
else if( 600 < led_counter_ms && led_counter_ms <= 1400){ led_base_pattern_zx_dim(&COLOR_WHITE, 800, 10);}
else if(1400 < led_counter_ms && led_counter_ms <= 3000){ led_base_pattern_all_on(&COLOR_WHITE);}
else{ led_base_pattern_all_off();}
}
void led_pattern_ready_change_zx(uint16_t led_counter_ms){
led_base_pattern_zx_blink(&COLOR_WHITE, 800);
}
void led_pattern_ready_change_showa(uint16_t led_counter_ms){
if(led_counter_ms <= 1000){ led_base_pattern_all_off();}
else if(1000 < led_counter_ms && led_counter_ms <= 21000){ led_base_pattern_zx_blink_slowly(&COLOR_WHITE, 500, 10);}
else{ led_base_pattern_all_off();}
}
void led_pattern_armor_time_zx(uint16_t led_counter_ms){
if(led_counter_ms <= 6200){ led_base_pattern_all_off();}
else if( 6200 < led_counter_ms && led_counter_ms <= 7000){ led_base_pattern_zx_dim(&COLOR_WHITE, 800, 10);}
else if( 7000 < led_counter_ms && led_counter_ms <= 7100){ led_base_pattern_rider(&COLOR_WHITE, LED_ZX);}
else if( 7100 < led_counter_ms && led_counter_ms <= 7900){ led_base_pattern_zx_dim(&COLOR_WHITE, 800, 10);}
else if( 7900 < led_counter_ms && led_counter_ms <= 20000){ led_base_pattern_rider(&COLOR_WHITE, LED_ZX);}
else{ led_base_pattern_all_off();}
}
void led_pattern_armor_time_showa(uint16_t led_counter_ms){
if(led_counter_ms <= 6200){ led_base_pattern_all_off();}
else if( 6200 < led_counter_ms && led_counter_ms <= 7000){ led_base_pattern_zx_dim(&COLOR_WHITE, 800, 10);}
else if( 7000 < led_counter_ms && led_counter_ms <= 7100){ led_base_pattern_rider(&COLOR_WHITE, LED_ZX);}
else if( 7100 < led_counter_ms && led_counter_ms <= 7900){ led_base_pattern_zx_dim(&COLOR_WHITE, 800, 10);}
else if( 7900 < led_counter_ms && led_counter_ms <= 9600){ led_base_pattern_rider(&COLOR_WHITE, LED_1ST);}
else if( 9600 < led_counter_ms && led_counter_ms <= 11300){ led_base_pattern_rider(&COLOR_WHITE, LED_2ND);}
else if(11300 < led_counter_ms && led_counter_ms <= 14600){ led_base_pattern_rider(&COLOR_WHITE, LED_V3);}
else if(14600 < led_counter_ms && led_counter_ms <= 16300){ led_base_pattern_rider(&COLOR_WHITE, LED_RIDERMAN);}
else if(16300 < led_counter_ms && led_counter_ms <= 17200){ led_base_pattern_rider(&COLOR_WHITE, LED_X);}
else if(17200 < led_counter_ms && led_counter_ms <= 18000){ led_base_pattern_rider(&COLOR_WHITE, LED_AMAZON);}
else if(18000 < led_counter_ms && led_counter_ms <= 19700){ led_base_pattern_rider(&COLOR_WHITE, LED_STRONGER);}
else if(19700 < led_counter_ms && led_counter_ms <= 21300){ led_base_pattern_rider(&COLOR_WHITE, LED_SKYRIDER);}
else if(21300 < led_counter_ms && led_counter_ms <= 25200){ led_base_pattern_rider(&COLOR_WHITE, LED_SUPER1);}
else if(25200 < led_counter_ms && led_counter_ms <= 25400){ led_base_pattern_zx_blink(&COLOR_WHITE, 40);}
else if(25400 < led_counter_ms && led_counter_ms <= 30600){ led_base_pattern_rider(&COLOR_WHITE, LED_ZX);}
else if(30600 < led_counter_ms && led_counter_ms <= 30800){ led_base_pattern_all_blink(&COLOR_WHITE, 40);}
else if(30800 < led_counter_ms && led_counter_ms <= 34400){ led_base_pattern_all_on(&COLOR_WHITE);}
else{ led_base_pattern_all_off();}
}
//void led_pattern_showa_init(uint16_t led_counter_ms){ // 起動音+「昭和ライダー!」
// if(led_counter_ms <= 20){ led_base_pattern_all_off();}
// else if( 20 < led_counter_ms && led_counter_ms <= 600){ led_base_pattern_zx_blink(&COLOR_WHITE, 80);}
// else if( 600 < led_counter_ms && led_counter_ms <= 1400){ led_base_pattern_zx_dim(&COLOR_WHITE, 800, 10);}
// else if(1400 < led_counter_ms && led_counter_ms <= 3000){ led_base_pattern_all_on(&COLOR_WHITE);}
// else{ led_base_pattern_all_off();}
//}
void led_pattern_ready_finish_time(uint16_t led_counter_ms){ // 起動音+「昭和ライダー!」+必殺待機
if(led_counter_ms <= 600){ led_base_pattern_zx_blink(&COLOR_WHITE, 80);}
else if( 600 < led_counter_ms && led_counter_ms <= 1400){ led_base_pattern_zx_dim(&COLOR_WHITE, 800, 10);}
else if(1400 < led_counter_ms && led_counter_ms <= 3000){ led_base_pattern_all_on(&COLOR_WHITE);}
else if(3000 < led_counter_ms && led_counter_ms <=28000){ led_base_pattern_zx_blink_slowly(&COLOR_WHITE, 500, 10);}
else{ led_base_pattern_all_off();}
}
void led_pattern_finish_time(uint16_t led_counter_ms){ // 「ライダーシンドローム!」
if(led_counter_ms <= 1000){ led_base_pattern_all_off();}
else if(1000 < led_counter_ms && led_counter_ms <= 1080){ led_base_pattern_all_blink(&COLOR_WHITE, 40);}
else if(1080 < led_counter_ms && led_counter_ms <= 1880){ led_base_pattern_all_dim(&COLOR_WHITE,800, 10);}
else if(1880 < led_counter_ms && led_counter_ms <= 2380){ led_base_pattern_all_blink(&COLOR_WHITE, 40);}
else if(2380 < led_counter_ms && led_counter_ms <= 3880){ led_base_pattern_all_dim(&COLOR_WHITE, 1500, 10);}
else if(3880 < led_counter_ms && led_counter_ms <=14080){ led_base_pattern_all_on(&COLOR_WHITE);
led_base_pattern_rider(&COLOR_RED, LED_ZX);}
else{ led_base_pattern_all_off();}
}
/////////////////////////////////////////////////////////////////////////
// 設定したLEDパターンを実行する。この間、スイッチの操作などは受け付けられないので注意
void execute_led_pattern(void (*func)(uint16_t), uint16_t duration_ms){
for(uint16_t i=0;i<duration_ms;i+=LOOP_INTERVAL_MS){
func(i);
pixels.show();
delay(LOOP_INTERVAL_MS);
}
}
void control_led(){
if(prev_state != state){
if(prev_state != STATE_SINGLE_B && state == STATE_SINGLE_A){
led_counter = LED_COUNT_MAX; // 発光させないように、カウントの上限値にする
}else if((prev_state == STATE_SINGLE_A || prev_state == STATE_SINGLE_B) && state == STATE_READY_WP){
led_counter = LED_COUNT_MAX; // 発光させないように、カウントの上限値にする
}else if(prev_state == STATE_WEAPON && state == STATE_READY_WP){
; // 発光を継続させるためにカウントをリセットしない
}else{
led_counter = 0; // 基本的に状態遷移が発生するとカウントをリセットする
}
}else if(prev_side != side){
led_counter = 0;
}
uint16_t led_counter_ms = led_counter * LOOP_INTERVAL_MS; // LEDはms単位で制御
switch(state){
case STATE_SINGLE_A:
if(selected_rider_code == CODE_000){
led_pattern_single(led_counter_ms);
}else{
if(is_playing_song){
led_base_pattern_rider_only(&COLOR_WHITE, selected_rider_code);
}else{
led_base_pattern_all_off();
}
}
break;
case STATE_SINGLE_B:
if(selected_rider_code == CODE_000){
led_pattern_single(led_counter_ms);
}else{
if(is_playing_song){
led_base_pattern_rider_only(&COLOR_WHITE, selected_rider_code);
}else{
led_base_pattern_all_off();
}
}
break;
case STATE_READY_WP:
case STATE_WEAPON:
// led_pattern_weapon(led_counter_ms); // 本来は武器用発光だが、今回は対応させない
break;
case STATE_READY_CH:
if(open_close_state == RIDERS_CLOSE){ // ZX
led_pattern_ready_change_zx(led_counter_ms);
}else{ // 10人ライダー
led_pattern_ready_change_showa(led_counter_ms);
}
break;
case STATE_CHANGED:
if(side == SIDE_NONE){
// led_base_pattern_zx_blink(&COLOR_WHITE, 800); // ややこしくなるので今回は省略
}else if(side == SIDE_ARMOR_TIME){
if(open_close_state == RIDERS_CLOSE){
led_pattern_armor_time_zx(led_counter_ms);
}else{
led_pattern_armor_time_showa(led_counter_ms);
}
}else if(side == SIDE_RIDER_TIME){
// 本来はライダータイム用だが、今回は対応させない
}
break;
case STATE_READY_CR:
led_pattern_ready_finish_time(led_counter_ms);
break;
case STATE_CRITICAL:
if(side == SIDE_ARMOR_TIME){
led_pattern_finish_time(led_counter_ms);
}else if(side == SIDE_RIDER_TIME){
// 本来はライダータイム用だが、今回は対応させない
}
break;
case STATE_SUMMON:
led_base_pattern_rider_only(&COLOR_WHITE, selected_rider_code);
break;
default:
;
}
if(led_counter < LED_COUNT_MAX){
led_counter++;
}
pixels.show();
}
////////// タッチ処理 ////////////////////////////////////////////////////////////
#define NUM_TOUCH_PIN 12
#define TOUCH_INTERVAL_MS 500
#include "Adafruit_MPR121.h"
#ifndef _BV
#define _BV(bit) (1 << (bit)) // ただのビットシフトの簡略表示
#endif
// You can have up to 4 on one i2c bus but one is enough for testing!
Adafruit_MPR121 cap = Adafruit_MPR121();
uint16_t lasttouched = 0;
uint16_t currtouched = 0;
unsigned long last_touch_time = 0;
void play_song(uint8_t rider_code){
if(prev_selected_rider_code != selected_rider_code){
// 選択したライダーが変わったときは、必ず曲を再生する
mp3_player.playMp3Folder(rider_code - 48); // 1号:59〜ZX:68で、召喚準備音声は11〜20
delay(2500);
mp3_player.playMp3Folder(rider_code - 28); // 1号:59〜ZX:68で、テーマ曲音声は31〜40
is_playing_song = true;
}else{
// 同じライダーが選択されたときは、再生中なら停止、停止中なら再生
if(is_playing_song){
mp3_player.pause();
}else{
mp3_player.playMp3Folder(rider_code - 48); // 1号:59〜ZX:68で、召喚準備音声は11〜20
delay(2500);
mp3_player.playMp3Folder(rider_code - 28); // 1号:59〜ZX:68で、テーマ曲音声は31〜40
}
is_playing_song = !is_playing_song;
}
}
void summon_rider(uint8_t side, uint8_t rider_code, uint8_t roll){
send_ir(SIDE_L, CODE_000, ROLL_OFF); // Lスロットのリセット信号を送る
mp3_player.playMp3Folder(rider_code - 48); // 1号:59〜ZX:68で、召喚準備音声は11〜20
delay(2700);
send_ir(side, rider_code, roll);
delay(3200);
mp3_player.playMp3Folder(rider_code - 38); // 1号:59〜ZX:68で、召備音声は21〜30
}
void touch_rider(uint8_t rider_code){
Serial.print(F("Touch:"));
Serial.println(rider_code);
selected_rider_code = rider_code;
switch(state){
case STATE_SINGLE_A:
case STATE_SINGLE_B:
led_base_pattern_rider_only(&COLOR_WHITE, rider_code); // まず発光させる
pixels.show();
play_song(rider_code);
break;
case STATE_CHANGED:
case STATE_CRITICAL:
case STATE_SUMMON:
led_base_pattern_rider_only(&COLOR_WHITE, rider_code); // まず発光させる
pixels.show();
summon_rider(SIDE_R, rider_code, ROLL_ON);
state = STATE_SUMMON;
break;
case STATE_READY_WP:
case STATE_WEAPON:
case STATE_READY_CH:
case STATE_READY_CR:
default:
;
}
prev_selected_rider_code = selected_rider_code;
}
////////// ユーティリティ関数 //////////////////////////////////////////////////
void reset_rider_time_counter(){
rider_time_counter = 0;
}
void reset_armor_time_counter(){
armor_time_counter = 0;
}
void reset_all(){
led_base_pattern_all_off();
reset_rider_time_counter();
reset_armor_time_counter();
is_armor_time_waiting = false;
is_rider_time_waiting = false;
is_armor_time_finish_waiting = false;
is_rider_time_finish_waiting = false;
is_ready_change_waiting = false;
led_counter = LED_COUNT_MAX;
selected_rider_code = CODE_000;
prev_selected_rider_code = CODE_000;
}
////////// メイン処理 ////////////////////////////////////////////////////////////
void setup(){
Serial.begin(115200);
pinMode(SW_C_PIN, INPUT_PULLUP);
pinMode(SW_1_PIN, INPUT_PULLUP);
pinMode(SW_2_PIN, INPUT_PULLUP);
pinMode(SW_OPEN_CLOSE_PIN, INPUT_PULLUP);
pinMode(LED_COLOR_PIN, OUTPUT);
pinMode(IR_TX_PIN, OUTPUT);
// ---------- MP3プレイヤーセットアップ ----------
ss_mp3_player.begin(9600);
if (!mp3_player.begin(ss_mp3_player)) { //Use softwareSerial to communicate with mp3.
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("mp3_player online."));
mp3_player.setTimeOut(500); //Set serial communictaion time out 500ms
mp3_player.volume(volume); //Set volume value (0~30).
// ---------- タッチセンサセットアップ ----------
if (!cap.begin(0x5A)) {
Serial.println(F("MPR121 not found, check wiring?"));
while (1);
}
// 赤外線LEDセットアップ
digitalWrite(IR_TX_PIN, LOW);
// 開閉の正気状態の取得
open_close_state = digitalRead(SW_OPEN_CLOSE_PIN);
// 起動エフェクト
mp3_player.playMp3Folder(SOUND_POWER_ON);
execute_led_pattern(led_pattern_power_on, 4000);
// 起動直後は発光させないように、カウントの上限値にする
led_counter = LED_COUNT_MAX;
}
void loop(){
////////// 状態遷移管理 ////////////////////
uint8_t input_port = PIND; // ピン0〜7のデジタル入力の同時読み取り
sw[0] = bitRead(input_port, SW_C_PIN);
sw[1] = bitRead(input_port, SW_1_PIN);
sw[2] = bitRead(input_port, SW_2_PIN);
open_close_state = bitRead(input_port, SW_OPEN_CLOSE_PIN);
prev_state = state;
prev_side = side;
if(memcmp(prev_sw, OFF_OFF_OFF, N_BUTTON) == 0){
if(memcmp(sw, ON_OFF_OFF, N_BUTTON) == 0){
if(prev_state == STATE_SINGLE_A){
reset_all();
state = STATE_SINGLE_B;
}else if(prev_state == STATE_SINGLE_B){
reset_all();
state = STATE_SINGLE_A;
}
}else if(memcmp(sw, OFF_ON_OFF, N_BUTTON) == 0){
if(prev_state == STATE_SINGLE_A || prev_state == STATE_SINGLE_B){
state = STATE_READY_WP;
}
}else if(memcmp(sw, OFF_OFF_ON, N_BUTTON) == 0){
;
}else if(memcmp(sw, ON_ON_OFF, N_BUTTON) == 0){
;
}else if(memcmp(sw, ON_OFF_ON, N_BUTTON) == 0){
;
}else if(memcmp(sw, OFF_ON_ON, N_BUTTON) == 0){
if(prev_state == STATE_SINGLE_A || prev_state == STATE_SINGLE_B){
state = STATE_READY_CH;
if(open_close_state == RIDERS_CLOSE){
send_ir(SIDE_L, CODE_068_ZX_1984, ROLL_OFF);
}else{
send_ir(SIDE_R, CODE_008_ZIO_1971, ROLL_OFF);
delay(IR_SEND_INTERVAL_MS);
send_ir(SIDE_L, CODE_085_SHOWA_SR, ROLL_OFF);
}
}
}else if(memcmp(sw, ON_ON_ON, N_BUTTON) == 0){
;
}
}else if(memcmp(prev_sw, ON_OFF_OFF, N_BUTTON) == 0){
if(memcmp(sw, OFF_OFF_OFF, N_BUTTON) == 0){
;
}else if(memcmp(sw, ON_ON_OFF, N_BUTTON) == 0){
if(prev_state == STATE_SINGLE_A || prev_state == STATE_SINGLE_B){
state = STATE_READY_WP;
}
}else if(memcmp(sw, ON_OFF_ON, N_BUTTON) == 0){
;
}else if(memcmp(sw, OFF_ON_OFF, N_BUTTON) == 0){
;
}else if(memcmp(sw, OFF_OFF_ON, N_BUTTON) == 0){
;
}else if(memcmp(sw, ON_ON_ON, N_BUTTON) == 0){
if(prev_state == STATE_SINGLE_A || prev_state == STATE_SINGLE_B){
state = STATE_READY_CH;
if(open_close_state == RIDERS_CLOSE){
send_ir(SIDE_L, CODE_068_ZX_1984, ROLL_OFF);
}else{
send_ir(SIDE_R, CODE_008_ZIO_1971, ROLL_OFF);
delay(IR_SEND_INTERVAL_MS);
send_ir(SIDE_L, CODE_085_SHOWA_SR, ROLL_OFF);
}
}
}else if(memcmp(sw, OFF_ON_ON, N_BUTTON) == 0){
;
}
}else if(memcmp(prev_sw, OFF_ON_OFF, N_BUTTON) == 0){
if(memcmp(sw, ON_ON_OFF, N_BUTTON) == 0){
if(prev_state == STATE_CHANGED){
reset_all();
state = STATE_READY_CR;
}else if(prev_state == STATE_READY_CR){
reset_all();
prev_state = STATE_CHANGED; // 擬似的に状態変化を起こす
state = STATE_READY_CR;
}
}else if(memcmp(sw, OFF_OFF_OFF, N_BUTTON) == 0){
if(prev_state == STATE_CHANGED || prev_state == STATE_READY_WP || prev_state == STATE_READY_CH || prev_state == STATE_READY_CR || prev_state == STATE_SUMMON){
reset_all();
state = STATE_SINGLE_A;
}
}else if(memcmp(sw, OFF_ON_ON, N_BUTTON) == 0){
if(prev_state == STATE_READY_WP){
state = STATE_WEAPON;
}else if(prev_state == STATE_READY_CH || prev_state == STATE_CHANGED){
rider_time_counter++;
if(rider_time_counter == 2){
prev_state = STATE_READY_CH; // 擬似的に状態変化を起こす
state = STATE_CHANGED;
side = SIDE_RIDER_TIME;
send_ir(SIDE_L, CODE_121, ROLL_ON);
}
}else if(prev_state == STATE_READY_CR){
rider_time_counter++;
if(rider_time_counter == 2){
state = STATE_CRITICAL;
side = SIDE_RIDER_TIME;
send_ir(SIDE_L, CODE_121, ROLL_ON);
}
}
}else if(memcmp(sw, ON_OFF_OFF, N_BUTTON) == 0){
;
}else if(memcmp(sw, ON_ON_ON, N_BUTTON) == 0){
;
}else if(memcmp(sw, OFF_OFF_ON, N_BUTTON) == 0){
;
}else if(memcmp(sw, ON_OFF_ON, N_BUTTON) == 0){
;
}
}else if(memcmp(prev_sw, OFF_OFF_ON, N_BUTTON) == 0){
if(memcmp(sw, ON_OFF_ON, N_BUTTON) == 0){
if(prev_state == STATE_SINGLE_A){
state = STATE_SINGLE_B;
}else if(prev_state == STATE_SINGLE_B){
state = STATE_SINGLE_A;
}else if(prev_state == STATE_CHANGED){
reset_all();
state = STATE_READY_CR;
}else if(prev_state == STATE_READY_CR){
reset_all();
prev_state = STATE_CHANGED; // 擬似的に状態変化を起こす
state = STATE_READY_CR;
}
}else if(memcmp(sw, OFF_ON_ON, N_BUTTON) == 0){
if(prev_state == STATE_SINGLE_A || prev_state == STATE_SINGLE_B){
state = STATE_READY_CH;
if(open_close_state == RIDERS_CLOSE){
send_ir(SIDE_L, CODE_068_ZX_1984, ROLL_OFF);
}else{
send_ir(SIDE_R, CODE_008_ZIO_1971, ROLL_OFF);
delay(IR_SEND_INTERVAL_MS);
send_ir(SIDE_L, CODE_085_SHOWA_SR, ROLL_OFF);
}
}else if(prev_state == STATE_READY_CH || prev_state == STATE_CHANGED){
armor_time_counter++;
if(armor_time_counter == 2){
prev_state = STATE_READY_CH; // 擬似的に状態変化を起こす
state = STATE_CHANGED;
side = SIDE_ARMOR_TIME;
send_ir(SIDE_L, CODE_121, ROLL_ON);
}
}else if(prev_state == STATE_READY_CR){
armor_time_counter++;
if(armor_time_counter == 2){
state = STATE_CRITICAL;
side = SIDE_ARMOR_TIME;
send_ir(SIDE_L, CODE_121, ROLL_ON);
}
}
}else if(memcmp(sw, OFF_OFF_OFF, N_BUTTON) == 0){
if(prev_state == STATE_WEAPON || prev_state == STATE_CHANGED || prev_state == STATE_READY_CH || prev_state == STATE_READY_CR || prev_state == STATE_SUMMON){
reset_all();
state = STATE_SINGLE_A;
}
}else if(memcmp(sw, ON_ON_ON, N_BUTTON) == 0){
;
}else if(memcmp(sw, ON_OFF_OFF, N_BUTTON) == 0){
;
}else if(memcmp(sw, OFF_ON_OFF, N_BUTTON) == 0){
;
}else if(memcmp(sw, ON_ON_OFF, N_BUTTON) == 0){
;
}
}else if(memcmp(prev_sw, ON_ON_OFF, N_BUTTON) == 0){
if(memcmp(sw, OFF_ON_OFF, N_BUTTON) == 0){
;
}else if(memcmp(sw, ON_OFF_OFF, N_BUTTON) == 0){
if(prev_state == STATE_CHANGED || prev_state == STATE_READY_WP || prev_state == STATE_READY_CH || prev_state == STATE_READY_CR || prev_state == STATE_SUMMON){
reset_all();
state = STATE_SINGLE_A;
}
}else if(memcmp(sw, ON_ON_ON, N_BUTTON) == 0){
if(prev_state == STATE_READY_WP){
state = STATE_WEAPON;
}else if(prev_state == STATE_READY_CH || prev_state == STATE_CHANGED){
rider_time_counter++;
if(rider_time_counter == 2){
prev_state = STATE_READY_CH; // 擬似的に状態変化を起こす
state = STATE_CHANGED;
side = SIDE_RIDER_TIME;
send_ir(SIDE_L, CODE_121, ROLL_ON);
}
}else if(prev_state == STATE_READY_CR){
rider_time_counter++;
if(rider_time_counter == 2){
state = STATE_CRITICAL;
side = SIDE_RIDER_TIME;
send_ir(SIDE_L, CODE_121, ROLL_ON);
}
}
}else if(memcmp(sw, OFF_OFF_OFF, N_BUTTON) == 0){
;
}else if(memcmp(sw, OFF_ON_ON, N_BUTTON) == 0){
;
}else if(memcmp(sw, ON_OFF_ON, N_BUTTON) == 0){
;
}else if(memcmp(sw, OFF_OFF_ON, N_BUTTON) == 0){
;
}
}else if(memcmp(prev_sw, ON_OFF_ON, N_BUTTON) == 0){
if(memcmp(sw, OFF_OFF_ON, N_BUTTON) == 0){
;
}else if(memcmp(sw, ON_ON_ON, N_BUTTON) == 0){
if(prev_state == STATE_SINGLE_A || prev_state == STATE_SINGLE_B){
state = STATE_READY_CH;
if(open_close_state == RIDERS_CLOSE){
send_ir(SIDE_L, CODE_068_ZX_1984, ROLL_OFF);
}else{
send_ir(SIDE_R, CODE_008_ZIO_1971, ROLL_OFF);
delay(IR_SEND_INTERVAL_MS);
send_ir(SIDE_L, CODE_085_SHOWA_SR, ROLL_OFF);
}
}else if(prev_state == STATE_READY_CH || prev_state == STATE_CHANGED){
armor_time_counter++;
if(armor_time_counter == 2){
prev_state = STATE_READY_CH; // 擬似的に状態変化を起こす
state = STATE_CHANGED;
side = SIDE_ARMOR_TIME;
send_ir(SIDE_L, CODE_121, ROLL_ON);
}
}else if(prev_state == STATE_READY_CR){
armor_time_counter++;
if(armor_time_counter == 2){
state = STATE_CRITICAL;
side = SIDE_ARMOR_TIME;
send_ir(SIDE_L, CODE_121, ROLL_ON);
}
}
}else if(memcmp(sw, ON_OFF_OFF, N_BUTTON) == 0){
if(prev_state == STATE_WEAPON || prev_state == STATE_CHANGED || prev_state == STATE_READY_CH || prev_state == STATE_READY_CR || prev_state == STATE_SUMMON){
reset_all();
state = STATE_SINGLE_A;
}
}else if(memcmp(sw, OFF_ON_ON, N_BUTTON) == 0){
;
}else if(memcmp(sw, OFF_OFF_OFF, N_BUTTON) == 0){
;
}else if(memcmp(sw, ON_ON_OFF, N_BUTTON) == 0){
;
}else if(memcmp(sw, OFF_ON_OFF, N_BUTTON) == 0){
;
}
}else if(memcmp(prev_sw, OFF_ON_ON, N_BUTTON) == 0){
if(memcmp(sw, ON_ON_ON, N_BUTTON) == 0){
reset_all();
if(prev_state == STATE_CHANGED){
if(open_close_state == RIDERS_CLOSE){ // これから10人ライダーが揃う状態
state = STATE_READY_CH;
led_base_pattern_all_on(&COLOR_WHITE); // これだけ特別にここで光らせる
pixels.show();
mp3_player.playMp3Folder(SOUND_SHOWA_SINGLE); // これだけ特別にここで鳴らす
delay(2700);
send_ir(SIDE_R, CODE_008_ZIO_1971, ROLL_OFF);
delay(IR_SEND_INTERVAL_MS);
send_ir(SIDE_L, CODE_085_SHOWA_SR, ROLL_OFF);
}else{ // すでに10人ライダーが集合している状態
state = STATE_READY_CR;
}
}else if(prev_state == STATE_READY_CR || prev_state == STATE_CRITICAL){
prev_state = STATE_CHANGED; // 擬似的に状態変化を起こす
state = STATE_READY_CR;
}else if(prev_state == STATE_SUMMON){
state = STATE_CHANGED;
led_base_pattern_all_on(&COLOR_WHITE); // これだけ特別にここで光らせる
pixels.show();
mp3_player.playMp3Folder(SOUND_SHOWA_INIT); // これだけ特別にここで鳴らす
delay(2700);
send_ir(SIDE_R, CODE_008_ZIO_1971, ROLL_OFF);
delay(IR_SEND_INTERVAL_MS);
send_ir(SIDE_L, CODE_085_SHOWA_SR, ROLL_ON);
}
}else if(memcmp(sw, OFF_OFF_ON, N_BUTTON) == 0){
if(prev_state == STATE_CHANGED){
side = SIDE_NONE;
if(armor_time_counter == 2){
reset_armor_time_counter();
}
}else if(prev_state == STATE_CRITICAL){
state = STATE_CHANGED;
side = SIDE_NONE;
if(armor_time_counter == 2){
reset_armor_time_counter();
}
}
}else if(memcmp(sw, OFF_ON_OFF, N_BUTTON) == 0){
if(prev_state == STATE_WEAPON){
state = STATE_READY_WP;
}else if(prev_state == STATE_CHANGED){
side = SIDE_NONE;
if(rider_time_counter == 2){
reset_rider_time_counter();
}
}else if(prev_state == STATE_CRITICAL){
state = STATE_CHANGED;
side = SIDE_NONE;
if(rider_time_counter == 2){
reset_rider_time_counter();
}
}
}else if(memcmp(sw, ON_OFF_ON, N_BUTTON) == 0){
;
}else if(memcmp(sw, ON_ON_OFF, N_BUTTON) == 0){
;
}else if(memcmp(sw, OFF_OFF_OFF, N_BUTTON) == 0){
if(prev_state == STATE_WEAPON || prev_state == STATE_CHANGED || prev_state == STATE_READY_CH || prev_state == STATE_READY_CR || prev_state == STATE_CRITICAL || prev_state == STATE_SUMMON){
reset_all();
state = STATE_SINGLE_A;
}
}else if(memcmp(sw, ON_OFF_OFF, N_BUTTON) == 0){
;
}
}else if(memcmp(prev_sw, ON_ON_ON, N_BUTTON) == 0){
if(memcmp(sw, OFF_ON_ON, N_BUTTON) == 0){
;
}else if(memcmp(sw, ON_OFF_ON, N_BUTTON) == 0){
if(prev_state == STATE_CHANGED){
side = SIDE_NONE;
if(armor_time_counter == 2){
reset_armor_time_counter();
}
}else if(prev_state == STATE_CRITICAL){
state = STATE_CHANGED;
side = SIDE_NONE;
if(armor_time_counter == 2){
reset_armor_time_counter();
}
}
}else if(memcmp(sw, ON_ON_OFF, N_BUTTON) == 0){
if(prev_state == STATE_WEAPON){
state = STATE_READY_WP;
}else if(prev_state == STATE_CHANGED){
side = SIDE_NONE;
if(rider_time_counter == 2){
reset_rider_time_counter();
}
}else if(prev_state == STATE_CRITICAL){
state = STATE_CHANGED;
side = SIDE_NONE;
if(rider_time_counter == 2){
reset_rider_time_counter();
}
}
}else if(memcmp(sw, OFF_OFF_ON, N_BUTTON) == 0){
;
}else if(memcmp(sw, OFF_ON_OFF, N_BUTTON) == 0){
;
}else if(memcmp(sw, ON_OFF_OFF, N_BUTTON) == 0){
if(prev_state == STATE_WEAPON || prev_state == STATE_CHANGED || prev_state == STATE_READY_CH || prev_state == STATE_READY_CR || prev_state == STATE_CRITICAL || prev_state == STATE_SUMMON){
reset_all();
state = STATE_SINGLE_A;
}
}else if(memcmp(sw, OFF_OFF_OFF, N_BUTTON) == 0){
;
}
}
prev_sw[0] = sw[0];
prev_sw[1] = sw[1];
prev_sw[2] = sw[2];
////////// タッチ処理 ////////////////////
// 本来ここに状態遷移は含むべきではないが、タッチ操作は例外として扱う。
// touch_rider関数の中で状態遷移を発生させる
currtouched = cap.touched(); // 現在の0〜11ピンのタッチ状態を確認
// 連続反応を避けるため、一度タッチを検出したら500msはタッチ処理を行わない
unsigned long now = millis();
if(now - last_touch_time > TOUCH_INTERVAL_MS){
for(uint8_t i;i<NUM_TOUCH_PIN;i++){
if((currtouched & _BV(i)) && !(lasttouched & _BV(i))){
switch(i){
case 1: touch_rider(CODE_059_RIDER1_1971); break;
case 2: touch_rider(CODE_060_RIDER2_1971); break;
case 3: touch_rider(CODE_061_V3_1973); break;
case 4: touch_rider(CODE_062_RIDERMAN_1973); break;
case 5: touch_rider(CODE_063_X_1974); break;
case 6: touch_rider(CODE_064_AMAZON_1974); break;
case 7: touch_rider(CODE_065_STRONGER_1975); break;
case 8: touch_rider(CODE_066_SKYRIDER_1979); break;
case 9: touch_rider(CODE_067_SUPER1_1980); break;
case 10: touch_rider(CODE_068_ZX_1984); break;
default: ;
}
last_touch_time = now;
break; // 同時押しの場合は、数字の小さいものを優先して終了する
}
}
}
lasttouched = currtouched;
////////// 音声再生処理 ////////////////////
control_sound();
////////// LED発光処理 ////////////////////
control_led();
delay(LOOP_INTERVAL_MS);
}
前回に引き続き、ライドウォッチについては状態遷移関係でちょっとバグが残っているかもしれませんが、ご了承くださいませ。
ディスカッション
コメント一覧
おー、なんか私の名前がいっぱい載ってる!ちょっと嬉しいです。
私は子供が遊んでいた為、通常動作の維持プラスアルファにこだわりましたが突き詰めた赤外線での制御はエグいですね。ジクウドライバーがバシュバシュ切り替わるのは凄い通り越してちょっと引くレベルですw
凄い物を見せて頂きありがとうございました。ちょっとでもお役に立てて嬉しいです。
さすがにこの作品は真似できませんw
かっぱ次郎 様
いえいえ、こちらこそありがとうございます。今回のライドウォッチは、絵を描いてくれた方と、次郎様のアイデアがあってこそ、たくさんの人に驚きかつ楽しんで頂けたのだと思っています。おそらく次郎様の赤外線通信方式なしでも形にはできたたと思いますが、その場合は単に音声が鳴るだけで、「なるほど、すごいね」程度で終わってしまっていたと思います。やっぱり視覚的に大きく変わっていくというのはすごくインパクトあるなと、今回改めて思いました。
5Vのprominiを使っているのですが、そうするとフルカラーシリアルテープが真っ白にただ光り続けるだけで制御が効きません。
どうしたらいいですか
うーん、情報が少なすぎて回答に困りますが。。。
少なくとも光っているということは、5VとGNDの接続はうまくいっているのだと思います。となると、まず疑うのは、残るコントロール用の配線がきちんと接続されているかどうか。それも正しく配線されているということであれば、あとはもうプログラムのどこかにミスがあるということだと思います。ただ、稀にLEDの方が壊れているケースもあるかもしれないので、プログラミングも絶対に合っているはずということであれば、もう一つ用意して確かめてみてもよいかもしれません。
hidaka_hirojiさんのブログにもあるフルカラーリングのスケッチを試したところ
それも同じ現象に陥ってしまいました。
ですが、3.3Vで流すと綺麗に光ってくれます。
自分のスケッチでも同様です。
そして、neopixelのライブラリにあるスケッチも試しましたが5vでもしっかり動きました。
返信が遅くなってしまい申し訳ありません。またすみません、長らく5VのArduino Pro Miniを使っていないこともあって、原因がわかりません。。。一律5Vで動かないならまだしも、サンプルの方では5Vで動くいうのが不可解で、原因の切り分けができないのです。申し訳ありません。