Arduino(8pino)でゼルダの伝説のごまだれ宝箱(ファミコン風)を作る

突然ですが、ゼルダの伝説のごまだれ宝箱を作りたいと思います。「『ごまだれ』ってなんじゃい?」という方は、こちらとかこちらとかを見て頂ければ良いかと思います。リンクが宝箱からアイテムを手に入れた時の、あのメロディーです。

実際にごまだれのメロディーが流れる宝箱は、実はWii U版『ゼルダ無双』の初回限定版特典として既に製品化されています。

実物はこんな感じですね。

これはすごく欲しかったのですが、残念ながら『ゼルダ無双』そのものには全然興味がなかったので、結局買わずじまいでした。

でもやっぱり欲しいものは欲しい。ということで、作ってしまいましょう。

 

この取り組みについては、すでに海外に先人がおられます。

  1. I Built a Legend of Zelda Themed Treasure Chest!
  2. “Interactive Zelda Treasure Chest” by nerdtinkerer.com (YouTube)

本気度が半端ねえです。特に2.の方がArduinoを使っていて参考になりそうですが、これは2つのArduino + RFIDシールド + MP3シールドを使用した超豪華仕様です。これを作れれば理想ですが、そんな予算も時間も技術もありません。

ということで、今回使うのはコレです。

 th_zelda-treasure-chest-10

タカラトミーアーツのガチャガチャ『伝説の宝箱』です。

とても都合の良いことに、ドット風の宝箱(写真左)まで用意されています。これとArduinoの圧電ブザーのチープなサウンドを組み合わせれば、上記のような豪華なものはできなくても、ファミコン風の味のあるごまだれ宝箱が作成できるのではないかと。

しかしこのサイズ(40mm x 35mm x 32mmぐらい)なので、Arduino UNO + シールドなんて構成は不可能です。Arduino Pro Miniならギリギリ入りますが、今回は今まで使ったことのないArduino互換機を使ってみたいと思います。

自分が知る限り最小のArduino互換機、8pinoです。2年前のMaker Faireで2個買ったままずっと放置していたものを引っ張り出してきました。

th_zelda-treasure-chest-12

小さい!

実際はArduino互換機というよりはAdafruit社のTrinketとやらの互換機という方が正確なのだそうですが、開発自体はArduino IDEで行えるので、特に違いは意識しなくて良さそうです(。。。と、タカを括り過ぎて、後でエラい目に遭いました。後述。)

使い方は公式の日本語マニュアルを参照してください。基本的に標準のArduino IDEのままでは8pinoへのソフトの書き込みはできず、adafruitのページに書かれているインストール方法に従って追加で8pino (Trinket)用の設定を標準IDEに追加するか、Trinket用の設定があらかじめ組み込まれたIDEを同じページからダウンロードしてきます。なお、Windows環境だとドライバのインストールも必要になります。

 

準備ができたら、まずはサクッとプロトタイプを作ります。マニュアルページにも書かれていますが、8pinoは頻繁なUSBケーブルへの抜き差しには耐えられない(数百回程度)そうなので、まずは8pinoのオリジナル?である、Trinket(3.3V)とブレッドボードを使ってソフトのデバッグと諸々のハードの接続を確認します。

こんな感じです。

th_zelda-treasure-chest-2

蓋が開いたことを検知するリードスイッチと、宝箱の内側を光らせるためのLED、それから肝心のごまだれ音を鳴らすための圧電ブザーです。これを、3Vのコイン電池(CR2032)で駆動させる。とてもシンプル。ソースコードについては、最終版を最後に載せます。

こんな感じでハード側の接続はとてもシンプルなのですが、Trinketへのプログラムのアップロードでどエラくハマってしまいました。。。

Trinketへプログラムをアップロードするときは、通常のArduinoと違って、以下の2点に注意する必要があります。

  1. プログラムをアップロードできるのは、PCとTrinketを繋いでから10秒の間(赤色LEDが点滅している間)だけ
  2. プログラムのアップロードは、シリアルポートではなく書込装置によって行われる

まず1.についての補足です。これは正確には、リセットボタンを押してから10秒以内でもOKです。そしてWindowsの場合、10秒が過ぎたらWindowsのデバイスマネージャでもTrinketが見えなくなります。これが正常な動作だということに気がつかなくて、ずっと接続不具合の原因を探してしまいました。。。

次に2.についての補足です。ほとんどのArduinoは”ツール”→”シリアルポート”でUSB接続されているArduinoのポートを指定して書込みを行いますが、TrinketをPCに挿しても”ツール”→”シリアルポート”には何も表示されません。下手にArduino歴が長かったせいで、「シリアルポートを指定しないとプログラムのアップロードは不可能」と思い込んでいたので、これまたずっと接続不具合の原因を探してしまいました。。。

実はTrinketはシリアルポートではなく「書込装置」というものを使ってプログラムをアップロードします。確かにマニュアル通り”書込装置”に”USBtinyISP”は指定していましたが、これがシリアルポートに置き換わるものだという認識を全然持っていませんでした。。。Arduino IDEの「マイコンボードを書き込む」のボタンは、シリアルポートを使うか書込装置を使うかは勝手に判断してアップロードしてくれるみたいですが、意識的に使い分けたい場合は、”スケッチ”→”書込装置を使って書き込む”を選択すればOKです。

ということで、サクッとプロトを作るはずが2〜3時間潰してしまったところで、今度は8pino本体を使ってソースと配線を確認します。数百回抜き差しするほど8pinoのソースを頻繁に書き換えないのであれば、いきなり8pinoでプロトを作ってしまっても良いと思います。

th_zelda-treasure-chest-8

これは個人的にオススメしたいのですが、8pinoを使って何かしら作りたい場合には、上記のようにソケットを半田付けしたものを、本番に使うのとは別にもう一つ用意しておいた方がいいと思います。

理由は2つです。まず、本家のTrinket 3.3Vでうまく動いても、8pinoで同様に動く保証はないので、必ず8pino本体を使ったテストが必要になること。後述しますが、実際今回、Trinketと全く同じように配線したのに、動きませんでした。

そしてこれが一番大きな理由ですが、8pinoに何かしらの配線をしてしまった状態では、プログラムのアップロードが出来なくなってしまう可能性が高いということ。これは日本語マニュアルにも書かれていますが、気づくのが遅れてしまいました。

8pinoという製品の特徴から、8pino本体に直接半田付けして配線するようなケースは多いと思いますが、一度半田付けした後にプログラムをアップロードしようとすると高確率で失敗する(エラーになる)ので、プログラムをアップロードするために配線を全部外してもう一度半田付け、という事態が発生してしまいます(発生しました)。

ということで、8pinoを使って何かしら作る時の理想的な手順は以下のようになります。

  1. Trinket 3.3Vを使って、プログラムと配線の確認(これは、8pinoの寿命が気にならないなら飛ばしてもよい)
  2. ソケットをつけたテスト用8pinoを使って、プログラムと配線の確認
  3. 実際に使用する8pinoに、最終版のソフトをアップロード
  4. 最終版のソフトをアップロードした8pinoに、半田付けで配線

間違っても、半田付けで配線した後に8pinoにソフトをアップロードしようとしないこと!自分は数十回ほど書込み失敗を経験した後、この間違いに気がついて、配線の半田付けを全部やり直しました。。。

ちなみに、配線後もどうしてもプログラムのデバッグ・再アップロードが発生しそうなら、多少大きさが犠牲になりますが、ソケット付きの8pinoをそのまま使うか、いっそTrinket 3.3Vをそのまま使うのも良いと思います。あれも十分小さいですし、リセットボタンがあるのでUSBケーブルを抜き差ししなくてよいのでラクです。

 

ということで、だいぶ本筋じゃないところでハマってしまいましたが、8pino自体を使ったテストはこんな感じです。

th_zelda-treasure-chest-6

しかし、ここで問題発生。ちゃんと動きません。試しにコイン電池(CR2032, 3.0V)じゃなくてUSB電源を使ってみると、ちゃんと動きます。ということは、電圧の問題?

th_zelda-treasure-chest-7

コイン電池(CR2032)を直列に2個繋いでみると、USB接続と同様に動作しました。うーん、Trinket3.3Vの時はコイン電池1個で動いたのになあ。。。とりあえずは、当初の予定よりだいぶ図体がでかくなってしまいますが、CR2032電池2個を直列に繋いで使うことにします。

 

というわけで、ここまで8pinoの使い方講座みたいになってしまいましたが、ここからようやく、ごまだれ宝箱の作成に入ります。

th_zelda-treasure-chest-3

材料を改めて整理しておきます。8pinoに加えて、宝箱本体として、タカラトミーアーツのガチャガチャ『伝説の宝箱』の、ドットの宝箱。自分はこれのガチャガチャが近所になかったので、メルカリで出品されていたものを買いました。

それから、コイン電池CR2032と電池ホルダ。上の写真ではそれぞれ1個ずつですが、最終的に電圧の関係で2個ずつ必要になってしまいました。電池ホルダは、あまり高さが出ないものを選んだ方が良いと思います。電源をON/OFFするためのスライドスイッチも忘れずに。

続いて、ドアの開閉を検知するためのリードスイッチとマグネット。自分はよくネオジム磁石を使っています。

あと、宝箱の中を光らせるための高輝度タイプのLED。8pinoの出力程度なので、直で8pinoに繋いでしまっても大丈夫だと思いますが、申し訳程度に1.5Ωの抵抗を挟んで接続します。必要に応じて、光拡散用キャップもつけます。

最後に、ごまだれ音を鳴らすための圧電ブザー。極性のない、シンプルなもので良いと思います。

 

これらを頑張って宝箱の中に収められるサイズにまとめていくわけですが、先に述べた通り、実際に半田付けしていく前に、先に8pinoにプログラムをアップロードしておくこと!というわけで、以下のソースを先に書き込んでおきます。

int SPEAKER_PIN = 3; 
int LED_PIN     = 2;
int SWITCH_PIN  = 1; 
bool isOpened = false;

char notes[] = "gabygabyxzCDxzCDabywabywzCDEzCDEbywFCDEqywFGDEqi        azbC"; // a space represents a rest
int length = sizeof(notes); // the number of notes
int beats[] = { 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 2,3,3,16,};
int tempo = 75;

void playTone(int tone, int duration) {
  for (long i = 0; i < duration * 1000L; i += tone * 2) {
    digitalWrite(SPEAKER_PIN, HIGH);
    delayMicroseconds(tone);
    digitalWrite(SPEAKER_PIN, LOW);
    delayMicroseconds(tone);
  }
}

void playNote(char note, int duration) {
  char names[] = { 'c', 'd', 'e', 'f', 'g', 'x', 'a', 'z', 'b', 'C', 'y', 'D', 'w', 'E', 'F', 'q', 'G', 'i' };
  // c=C4, C = C5. These values have been tuned.
  int tones[] = { 1898, 1690, 1500, 1420, 1265, 1194, 1126, 1063, 1001, 947, 893, 843, 795, 749, 710, 668, 630, 594 };

  // play the tone corresponding to the note name
  for (int i = 0; i < 18; i++) {
    if (names[i] == note) {
      playTone(tones[i], duration);
    }
  }
}

void setup() {
  // put your setup code here, to run once:
  pinMode(SPEAKER_PIN, OUTPUT);  
  pinMode(LED_PIN, OUTPUT);  
  pinMode(SWITCH_PIN, INPUT_PULLUP); 

  digitalWrite(SPEAKER_PIN, LOW);
  digitalWrite(LED_PIN, LOW);
}

void loop() {

  if(digitalRead(SWITCH_PIN) == HIGH){
    if(!isOpened){
      digitalWrite(LED_PIN, HIGH);
      for (int i = 0; i < length; i++) {
        if (notes[i] == ' ') {
          delay(beats[i] * tempo); // rest
        }else {
          playNote(notes[i], beats[i] * tempo);
        }    
        // pause between notes
        delay(tempo / 2); 
      }
      digitalWrite(LED_PIN, LOW);
      isOpened = true;
    }
  }else{
    isOpened = false;   
  }

  delay(100);
}

なお、ごまだれ音を鳴らす部分のソースは、こちらの方が作成して公開してくださったものをそのまま使わせていただいています。ありがとうございます。

 

 プログラムを書き込んだら、あとは組み付けです。

th_zelda-treasure-chest-13

上から見た図。

th_zelda-treasure-chest-14

下から見た図。

th_zelda-treasure-chest-15

横から見た図。。。全体的に配線が汚いですが、これは自分の技術もさることながら、先に述べた8pinoのプログラムアップロードやら電圧の問題やらで何度も配線をやり直したためです。。。

th_zelda-treasure-chest-9

宝箱にはなんとか治まりましたが、電池の厚さが当初の2倍になったこともあり、何か他のものを入れる余裕は全くありません。天井には、リードスイッチを反応させるためのネオジム磁石を両面テープで貼り付けています。

 

ということで、一応完成しました。この記事のトップにも載せていますが、以下にも再掲しておきます。

 

というわけで、なんとか目的通りのものを作ることができましたが、8pinoの扱い方に不慣れだったせいで、だいぶ作りが荒くなってしまいました。時間ができたら、もう少し綺麗に作り直したいと思います。