MENU

ダブルドライバー両スロット&ロストドライバー対応 ジョーカーメモリをつくる

前回記事の最後で予告していたとおり、ハザードメモリから無線機能を除いて、「ダブルドライバーの両スロットと、ロストドライバーに対応する(=スロットごとに音声・発光の再生タイミングを最適化する)」という機能に絞ったメモリを作ってみました。この機能が最も活きるメモリと言えば、実際に劇中でも利用シーンのあったこれしかないでしょう、ということで、ジョーカーメモリを作ってみました。

目次

特徴

メインボタンの長押し操作で内部モードが変化し、音声と発光のタイミングが最適化されます。今がどのモードに入っているかはパッと見ではわからないため、モード変化時に劇中の台詞を再生することで、どのモードに入っているかをわかりやすくしてみました。

ジョーカーの正位置であるダブルドライバーのLスロット時は、フィリップがいることが前提なのでこの台詞。

ロストドライバー時。正確にはこの台詞はT2のジョーカーメモリで変身したときのものですが、仮面ライダージョーカーと言えばやっぱりこの台詞が一番カッコ良いと思いましたので、こうしています。

そしてイレギュラーな、ダブルドライバーのRスロット時。ディケイドのファイナルフォームライドでのみ発現する姿になりますので、台詞も通りすがりの仮面ライダーにお願いすることにしました。最初は「ファイナルフォームライド ダ・ダ・ダ・ダブル!」にするつもりだったのですが、ここだけシステムボイスにするのも統一感がないかなと思って、こちらの台詞にしました。

ハードウェア解説

ベースはもちろん、DX版のジョーカーメモリです。

ジョーカーメモリはやはり人気があるのか、中古市場でも他のメモリに比べてちょっと高めで取引されていますね。

ボタンと端子部はそれぞれシルバーとゴールドで塗装しています。遊んでいるうちに塗装が剥がれてくる部分ではあるのですが、やはりここの塗装のあるなしで印象がだいぶ変わるので、ここは塗装しておきました。

塗装は例の如くガンダムマーカーエアブラシで行っていますが、今回は構成を変えて、エア缶ではなく充電式コンプレッサーに繋いで使用してみました。というのも、ガンダムマーカーエアブラシ、毎回塗料カップの掃除をしなくて良くて手軽に使えるという強力なメリットがある一方、エア缶があっという間に冷えてしまってすぐにエアーが弱くなるという、逆に強力なデメリットもあります。これはちょっと何とかしたいなあということで、色々調べてみた結果、少なくとも以下の構成でガンダムマーカーエアブラシを充電式コンプレッサーに繋げて使用できることがわかりました(※もちろん、他の選択肢もあります)。

実際に繋げてみた結果がこちらです。

「よし、これで連続稼働時間が延びるぜ!」と喜び勇んで試してみたところ、「一応使えはするが完璧ではない」という感じがしました。特に、ガンダムマーカーの塗料が充分にあるときは問題なさそうですが、少なくなってくると塗料がかなり飛びにくくなりました。また、マーカーのペン先もエアブラシ用のものに変えていたのですが、逆に元々のペン先に戻した方が上手くいったりもしたので、もうちょっと色々試行錯誤しながら使っていく必要がありそうです。

ハザードメモリのときとの内部構造の一番の違いは、無線通信を使用する必要がなくなったので、マイコンはATOM Liteでなくても良いというところです。従来通りArduino Pro Mini (3.3V)を使用しても良いのですが、同じぐらい小型でかつ安価で扱いやすいマイコンとしてSeeeduino XIAOというものが最近出てきていますので、こちらを使用してみました。

使用感は良好ですので、今後はこちらをメインに使用していくことになると思います。

あとは、基本的にハザードメモリのときに使用したものと同じ具材を使用します。

回路図としてはこんな感じです。

実際に配線すると、こうなります。

マイコンを変えたことで少し配置の自由度が上がりましたので、全体の配置を見直して、電池蓋の中ではありますが電源スイッチを追加して遊びやすくしました。また、ハザードメモリのときは内部スペース確保のためにギリギリまで内部部品を削っていて、結果、蓋は両面テープ留めの形になっていたのですが、今回の部品配置見直しで蓋のネジ留めも可能になりました。

スイッチ部分の配線の仕方はハザードメモリのときと同じですが、改めて説明しておきます。

まず、基板の右上の音声IC部分は切り取ってしまい、

LEDの足を切ってしまって、基板に残った足とスイッチ部分を配線します。

あとは、裏面をこんな感じに配線します。先に載せた回路図と併せて見てもらうとわかりやすいと思います。

ソフトウェア解説

ソースコード的には、前回のハザードメモリからBLE関係の機能を削除して、ATOM Liteマイコンに特化した記述になっていたところ(←analogWrite()の代わりにledcWrite()を使っていたり、ソフトウェアシリアルでなくハードウェアシリアルを使っているところなど)を、一般的なArduino向けの記述に修正しています。また、一部実装がごちゃついていたところも整理しました。

状態遷移自体は、ハザードメモリのときから変わっていません。ただ、「ボタン長押しでモード変更」という仕様にした結果、「ボタンを押すと『ジョーカー!』が鳴る」という仕様ではなく「ボタンを押してすぐに離すと『ジョーカー!』が鳴る」という仕様になっています。前者の仕様のままでの実装も可能なのですが、それだとモード変更のたびに、切り替え台詞の前に「ジョーカー!」が鳴ってしまうので、個人的にはそれがちょっと嫌で後者の仕様にしました。気にならなければ、前者の方が実装としては少しシンプルになると思います。

あと、そんなに難しいことをしているわけではありませんが、「音声と発光のタイミング変更をどう実装しているか?」については少し解説しておきます。

実装の仕方は多分2つあって、シンプルなのは「各スロットに対応した音声ファイルを別々に用意して、モードごとに再生する音声ファイルを変える」というやり方だと思います。ただ、音声としてはどれも結局「ドライバーが開く音」「ジョーカー!」「変身音」の組み合わせで、それぞれの再生までの時間が違うだけになります。

似たような音声ファイルを複数用意するのが個人的に嫌だったのと、あと、発光も併せて再生タイミングを容易に微調整できるようにした方が後々良かろうと思いましたので、音声ファイルとしては「ドライバーが開く音」「ジョーカー!」「変身音」の3つの音声ファイルを用意して、それぞれを再生するまでの時間をプログラム内で変数管理してモード毎に変更する、というやり方にしました。

ソースコードの全文は記事の最後に載せますが、

#define WAIT_FOR_W_R_NAME_MS 1200 // ドライバー開音再生開始後のメモリ名音までの再生待ち時間(WドライバーRスロット側)
#define WAIT_FOR_W_L_NAME_MS 2000 // ドライバー開音再生開始後のメモリ名音までの再生待ち時間(WドライバーLスロット側)
#define WAIT_FOR_LOST_NAME_MS 1200 // ドライバー開音再生開始後のメモリ名音までの再生待ち時間(ロストドライバー)

#define WAIT_FOR_W_R_CHANGE_MS 2200 // メモリ名音再生開始後の変身音までの再生待ち時間(WドライバーRスロット側)
#define WAIT_FOR_W_L_CHANGE_MS 3000 // メモリ名音再音開始後の変身音までの再生待ち時間(WドライバーLスロット側)
#define WAIT_FOR_LOST_CHANGE_MS 1500 // メモリ名音再音開始後の変身音までの再生待ち時間(ロストドライバー)

この部分の値を変更することで、それぞれの音声が再生されるまでの時間を自由に変更することができるようになっています。

まとめ

以上、『ダブルドライバー両スロット&ロストドライバー対応 ジョーカーメモリ』のご紹介でした。ハザードメモリから部品構成を見直してだいぶ遊びやすくなりましたので、実は個人的にはこちらの方でよく遊んでいます。おそらく、無線通信機能付きのハザードメモリより、こちらの方が需要は高いのではないかなと思っています。

動画を観てくださった方はご存知だと思いますが、実は今回ジョーカーメモリの改造と言いながら、スカルメモリにも同様の改造を行いました。スピーカーを交換するついでに、内部の骨のイメージでスピーカーをシルバー塗装してみました。

スカルメモリについては、『仮面ライダーW』の正統続編である『風都探偵』の第6集で色々詳細が判明しましたが、もしシュラウドの想定したとおり『サイクロンスカル』が誕生していたら、もしスカルメモリが壊れずに残っていたら、という「ifの世界」をちょっと見てみたくて、今回スカルメモリも改造してみました。ジョーカーメモリでやり方はだいぶ確立できたので、こちらはそこまで手間はなくできました。

ということで、前回作ったメモリも合わせると、かれこれ5本ほどガイアメモリを改造したことになります。流石にこれだけやれば個人的にはもう充分、という感じなので、心置きなく次の作品作りに移れそうです。

と言っても、具体的に何を作るか決まっているわけではありません。多分『仮面ライダーセイバー』のワンダーライドブックを色々いじりながら考えることになると思いますが、その前に一つレビューを頼まれていることがあるので、それが終わってから取り組むことになると思います。

ソースコード

最後に、今回作ったジョーカーメモリのソースコードの全文を掲載します。

////////// 基本定義 ////////////////////////////////////////////////////////////

#define LOOP_DELAY_MS 20

#define MP3_RX_PIN  0
#define MP3_TX_PIN  1
#define SW_1_PIN    2
#define SW_2_PIN    3
#define SW_3_PIN    4
#define LED_PIN     5

#define ON  LOW
#define OFF HIGH

// 3つのボタン入力の表現
#define N_BUTTON 3
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 MODE_W_R  0 // ダブルドライバーRスロットモード 
#define MODE_W_L  1 // ダブルドライバーLスロットモード
#define MODE_LOST 2 // ロストドライバーモード

#define WAIT_FOR_W_R_NAME_MS  1200 // ドライバー開音再生開始後のメモリ名音までの再生待ち時間(WドライバーRスロット側)
#define WAIT_FOR_W_L_NAME_MS  2000 // ドライバー開音再生開始後のメモリ名音までの再生待ち時間(WドライバーLスロット側)
#define WAIT_FOR_LOST_NAME_MS 1200 // ドライバー開音再生開始後のメモリ名音までの再生待ち時間(ロストドライバー)

#define WAIT_FOR_W_R_CHANGE_MS  2200 // メモリ名音再生開始後の変身音までの再生待ち時間(WドライバーRスロット側)
#define WAIT_FOR_W_L_CHANGE_MS  3000 // メモリ名音再音開始後の変身音までの再生待ち時間(WドライバーLスロット側)
#define WAIT_FOR_LOST_CHANGE_MS 1500 // メモリ名音再音開始後の変身音までの再生待ち時間(ロストドライバー)

#define LONG_PRESS_MS 1500 // モード変更のための長押し時間

#define STATE_INIT           0
#define STATE_TEMP           1
#define STATE_CHANGE_READY   2
#define STATE_CHANGE         3
#define STATE_CRITICAL_READY 4
#define STATE_CRITICAL       5

uint8_t state      = STATE_INIT;
uint8_t prev_state = STATE_INIT;

boolean is_memory_ejected = false; // STATE_INITへの変化経路管理
boolean is_change_off     = false; // STATE_CHANGE_READYへの変化経路管理
boolean is_critical       = false; // STATE_CRITICAL_READYへの変化経路管理

uint8_t mode = MODE_W_L; // デフォルトはダブルドライバーLスロットモードとする
uint8_t prev_mode = mode;
unsigned long long_press_start_time = 0;
boolean is_sw_1_pressing = false;

void print_state(){
  if(prev_state != state){
    switch(state){
    case STATE_INIT:           Serial.println(F("STATE_INIT")); break;
    case STATE_TEMP:           Serial.println(F("STATE_TEMP")); break;
    case STATE_CHANGE_READY:   Serial.println(F("STATE_CHANGE_READY")); break;
    case STATE_CHANGE:         Serial.println(F("STATE_CHANGE")); break;
    case STATE_CRITICAL_READY: Serial.println(F("STATE_CRITICAL_READY")); break;
    case STATE_CRITICAL:       Serial.println(F("STATE_CRITICAL")); break;
    default: ;
    }
  }  
}

////////// 音声処理 /////////////////////////////////////////////////////////////

#include <SoftwareSerial.h>

SoftwareSerial ss_mp3_player(MP3_RX_PIN, MP3_TX_PIN);

#define SOUND_VOLUME_DEFAULT 24 // 0〜30

#define SOUND_MEMORY_NAME     1
#define SOUND_CHANGE_READY    2
#define SOUND_MEMORY_EJECT    3
#define SOUND_DRIVER_OPEN     4
#define SOUND_CHANGE          5
#define SOUND_DRIVER_CLOSE    6
#define SOUND_CRITICAL_READY  7
#define SOUND_CRITICAL        8
#define SOUND_SIDE_L          9
#define SOUND_LOST           10
#define SOUND_SIDE_R         11
#define SOUND_CRITICAL_JOKER 12

#define WAIT_SOUND_STATE_NONE   0
#define WAIT_SOUND_STATE_NAME   1
#define WAIT_SOUND_STATE_CHANGE 2

uint8_t wait_sound_state = WAIT_SOUND_STATE_NONE;
unsigned long sound_wait_start_time = 0;

void mp3_play(uint8_t track){
  // この定義だと再生曲数は255に限定されるので注意。
  // Upper-Byte, Lower-Byteをちゃんと処理してやれば65535曲まで対応可能だが、
  // 容量的にそこまで使うことはほぼないと思われる。

  // Macの場合は、MP3データを全て"ZH"フォルダにコピーした後、
  // ターミナルでZHフォルダに入り、"$ rm ._*"のコマンドで、
  // "._01.mp3"のような名前で裏で勝手に生成されているファイル
  // ("$ ls -a"のコマンドでないと見えない、AppleDouble Header file)
  // を削除しないと、挙動がおかしくなるので注意
 
  uint8_t play[6] = {0xAA,0x07,0x02,0x00,track,track+0xB3};
  ss_mp3_player.write(play,6);
}

void mp3_stop(){
  unsigned char stop[4] = {0xAA,0x04,0x00,0xAE};
  ss_mp3_player.write(stop,4);
}

void mp3_set_volume(uint8_t vol){
  uint8_t volume[5] = {0xAA,0x13,0x01,vol,vol+0xBE};
  ss_mp3_player.write(volume,5);
}

void control_sound(){

  unsigned long now_ms = millis();
  
  if(prev_state != state){ // 状態遷移直後の処理
    switch(state){
    case STATE_INIT:
      if(prev_state == STATE_TEMP){
        if(prev_mode == mode && !is_memory_ejected){
          mp3_play(SOUND_MEMORY_NAME);
        }
      }else{
        mp3_play(SOUND_MEMORY_EJECT);
      }
      wait_sound_state = WAIT_SOUND_STATE_NONE;
      break;
    case STATE_TEMP:
      if(prev_state != STATE_INIT){
        mp3_play(SOUND_MEMORY_EJECT);
        wait_sound_state = WAIT_SOUND_STATE_NONE;
      }
      break;
    case STATE_CHANGE_READY:
      if(prev_state == STATE_INIT){
        mp3_play(SOUND_CHANGE_READY);
      }else if(prev_state == STATE_CHANGE){
        mp3_play(SOUND_DRIVER_CLOSE);
        wait_sound_state = WAIT_SOUND_STATE_NONE;
      }
      break;
    case STATE_CHANGE:
      // まずはドライバー開音を鳴らして、変身音鳴らしの待ちに入る
      mp3_play(SOUND_DRIVER_OPEN);
      sound_wait_start_time = now_ms;
      wait_sound_state = WAIT_SOUND_STATE_NAME;
      break;
    case STATE_CRITICAL_READY:
      if(prev_state == STATE_INIT){
        mp3_play(SOUND_CRITICAL_READY);
      }
      break;
    case STATE_CRITICAL:
      if(mode == MODE_LOST){
        mp3_play(SOUND_CRITICAL_JOKER);
      }else{
        mp3_play(SOUND_CRITICAL);
      }
      break;
    default:
      ;
    }
  }else{ // 状態遷移後の時間経過処理
    switch(wait_sound_state){
    case WAIT_SOUND_STATE_NONE:
      break;
    case WAIT_SOUND_STATE_NAME:
      if(mode == MODE_W_R){
        // ダブルドライバーRスロットモードになっているとき
        if(now_ms - sound_wait_start_time >= WAIT_FOR_W_R_NAME_MS){
          mp3_play(SOUND_MEMORY_NAME);
          sound_wait_start_time = now_ms;
          wait_sound_state = WAIT_SOUND_STATE_CHANGE;
        }
      }else if(mode == MODE_W_L){
        // ダブルドライバーLスロットモードになっているとき
        if(now_ms - sound_wait_start_time >= WAIT_FOR_W_L_NAME_MS){
          mp3_play(SOUND_MEMORY_NAME);
          sound_wait_start_time = now_ms;
          wait_sound_state = WAIT_SOUND_STATE_CHANGE;
        }
      }else{
        // ロストドライバーモードになっているとき
        if(now_ms - sound_wait_start_time >= WAIT_FOR_LOST_NAME_MS){
          mp3_play(SOUND_MEMORY_NAME);
          sound_wait_start_time = now_ms;
          wait_sound_state = WAIT_SOUND_STATE_CHANGE;
        }
      }
      break;
    case WAIT_SOUND_STATE_CHANGE:
      if(mode == MODE_W_R){
        // ダブルドライバーRスロットモードになっているとき
        if(now_ms - sound_wait_start_time >= WAIT_FOR_W_R_CHANGE_MS){
          mp3_play(SOUND_CHANGE);
          wait_sound_state = WAIT_SOUND_STATE_NONE;
        }
      }else if(mode == MODE_W_L){
        // ダブルドライバーLスロットモードになっているとき
        if(now_ms - sound_wait_start_time >= WAIT_FOR_W_L_CHANGE_MS){
          mp3_play(SOUND_CHANGE);
          wait_sound_state = WAIT_SOUND_STATE_NONE;
        }
      }else{
        // ロストドライバーモードになっているとき
        if(now_ms - sound_wait_start_time >= WAIT_FOR_LOST_CHANGE_MS){
          mp3_play(SOUND_CHANGE);
          wait_sound_state = WAIT_SOUND_STATE_NONE;
        }
      }
      break;
    default:
      ;
    }
  }
}

////////// 発光処理 /////////////////////////////////////////////////////////////

#define LED_BRIGHTNESS 255 // 255が上限値

boolean is_led_active = false; // 電源投入直後のSTATE_INITのときのLED発光を無効にするためのフラグ

unsigned long led_pattern_start_time = 0;
unsigned long prev_blink_time        = 0;
unsigned long inc_dim_start_time = 0;
boolean is_lighting = false;
boolean is_inc = true;

int  prev_interval_ms = 0;
uint8_t prev_steps = 0;

// デフォルトはダブルドライバーLスロットモードとする
unsigned long memory_name_start_time = WAIT_FOR_W_L_NAME_MS;
unsigned long change_start_time = WAIT_FOR_W_L_NAME_MS + WAIT_FOR_W_L_CHANGE_MS;

void led_base_pattern_on(){
  analogWrite(LED_PIN, LED_BRIGHTNESS);
  prev_interval_ms = 0;
  prev_steps = 0;
}

void led_base_pattern_off(){
  analogWrite(LED_PIN, 0);
  prev_interval_ms = 0;
  prev_steps = 0;
}

void led_base_pattern_blink(unsigned long now_ms, int interval_ms){
  if(now_ms - prev_blink_time >= interval_ms){
    if(is_lighting){
      analogWrite(LED_PIN, 0);
    }else{
      analogWrite(LED_PIN, LED_BRIGHTNESS);
    }
    is_lighting = !is_lighting;
    prev_blink_time = now_ms;
  }

  prev_interval_ms = interval_ms;
  prev_steps = 0;
}

void led_base_pattern_inc(unsigned long now_ms, int interval_ms, uint8_t steps){
  // いずれかの条件が変化していたら、処理をリセットする
  if(interval_ms != prev_interval_ms || steps != prev_steps){
    inc_dim_start_time = now_ms;
  }
  int ms_per_step = interval_ms / steps;
  int current_step = (now_ms - inc_dim_start_time) / ms_per_step;
  if(current_step > steps){
    current_step = steps;
  }
  uint8_t l_step = LED_BRIGHTNESS/steps;
  analogWrite(LED_PIN, l_step*current_step);

  prev_interval_ms = interval_ms;
  prev_steps = steps;
}

void led_base_pattern_dim(unsigned long now_ms, int interval_ms, uint8_t steps){
  // いずれかの条件が変化していたら、処理をリセットする
  if(interval_ms != prev_interval_ms || steps != prev_steps){
    inc_dim_start_time = now_ms;
  }
  int ms_per_step = interval_ms / steps;
  int current_step = (now_ms - inc_dim_start_time) / ms_per_step;
  if(current_step > steps){
    current_step = steps;
  }
  uint8_t l_step = LED_BRIGHTNESS/steps;
  analogWrite(LED_PIN, l_step*(steps-current_step));

  prev_interval_ms = interval_ms;
  prev_steps = steps;
}

void led_base_pattern_blink_slowly(unsigned long now_ms, int interval_ms, uint8_t steps){
  // いずれかの条件が変化していたら、処理をリセットする
  if(interval_ms != prev_interval_ms || steps != prev_steps){
    inc_dim_start_time = now_ms;
    is_inc = true;
  }
  int ms_per_step = interval_ms / steps;
  int current_step = (now_ms - inc_dim_start_time) / ms_per_step;
  if(current_step > steps){
    current_step = steps;
  }
  uint8_t l_step = LED_BRIGHTNESS/steps;
  if(is_inc){
    analogWrite(LED_PIN, l_step*current_step);
  }else{
    analogWrite(LED_PIN, l_step*(steps-current_step));
  }
  if(now_ms - inc_dim_start_time >= interval_ms){
    is_inc = !is_inc;
    inc_dim_start_time = now_ms;
  }

  prev_interval_ms = interval_ms;
  prev_steps = steps;
}



void led_pattern_memory_name(unsigned long passed_ms, unsigned long now_ms){
  if(passed_ms <= 500){                          led_base_pattern_dim(now_ms, 500, 10);}
  else if(500 < passed_ms && passed_ms <= 1300){ led_base_pattern_dim(now_ms, 800, 20);}
  else{                                          led_base_pattern_off();}
}

void led_pattern_memory_ejected(unsigned long passed_ms, unsigned long now_ms){
  if(passed_ms <= 300){                          led_base_pattern_on();}
  else if(300 < passed_ms && passed_ms <= 1000){ led_base_pattern_dim(now_ms, 700, 20);}
  else{                                          led_base_pattern_off();} 
}

void led_pattern_change_ready(unsigned long passed_ms, unsigned long now_ms){
  if(passed_ms <= 525){                         led_base_pattern_on();}
  else if(525 < passed_ms && passed_ms <= 800){ led_base_pattern_dim(now_ms, 275, 10);}
  else{                                         led_base_pattern_blink_slowly(now_ms, 550, 10);}  
}

void led_pattern_change(unsigned long passed_ms, unsigned long now_ms){
  // ドライバー開
  if(passed_ms <= 600){
    led_base_pattern_inc(now_ms, 600, 10);
  } else if(600 < passed_ms && passed_ms <=  1100){
    led_base_pattern_dim(now_ms, 500, 10);}
  else if(1100 < passed_ms && passed_ms <=  memory_name_start_time){
    led_base_pattern_off();
  }
  // メモリ名
  else if(memory_name_start_time < passed_ms && passed_ms <= memory_name_start_time+500){
    led_base_pattern_dim(now_ms, 500, 10);
  }else if(memory_name_start_time+500  < passed_ms && passed_ms <= memory_name_start_time+1300){
    led_base_pattern_dim(now_ms, 800, 20);
  }else if(memory_name_start_time+1300 < passed_ms && passed_ms <= change_start_time){
    led_base_pattern_off();
  } 
  // 変身
  else if(change_start_time < passed_ms && passed_ms <=  change_start_time+450){
    led_base_pattern_dim(now_ms, 450, 10);
  }else if(change_start_time+450 < passed_ms && passed_ms <= change_start_time+800){
    led_base_pattern_dim(now_ms, 350, 10);
  }else if(change_start_time+800 < passed_ms && passed_ms <= change_start_time+1050){
    led_base_pattern_dim(now_ms, 250, 10);
  }else if(change_start_time+1050 < passed_ms && passed_ms <= change_start_time+6050){
    led_base_pattern_on();
  }else if(change_start_time+6050 < passed_ms && passed_ms <= change_start_time+7550){
    led_base_pattern_dim(now_ms, 1500, 20);
  }else{
    led_base_pattern_off();
  } 
}

void led_pattern_change_off(unsigned long passed_ms, unsigned long now_ms){
  if(passed_ms <= 300){                          led_base_pattern_on();}
  else if(300 < passed_ms && passed_ms <= 1000){ led_base_pattern_dim(now_ms, 700, 20);}
  else{                                          led_base_pattern_off();} 
}

void led_pattern_critical_ready(unsigned long passed_ms, unsigned long now_ms){
  if(passed_ms <= 525){                           led_base_pattern_on();}                 // 装填
  else if( 525 < passed_ms && passed_ms <=  800){ led_base_pattern_dim(now_ms, 275, 10);}
  else if( 800 < passed_ms && passed_ms <= 1200){ led_base_pattern_dim(now_ms, 400, 10);} // ジョーカー
  else if(1200 < passed_ms && passed_ms <= 2000){ led_base_pattern_dim(now_ms, 800, 20);}
  else if(2000 < passed_ms && passed_ms <= 2300){ led_base_pattern_off();}
  else if(2300 < passed_ms && passed_ms <= 2500){ led_base_pattern_on();}                 // マキシマムドライブ
  else if(2500 < passed_ms && passed_ms <= 2900){ led_base_pattern_dim(now_ms, 400, 10);}
  else if(2900 < passed_ms && passed_ms <= 3000){ led_base_pattern_on();}
  else if(3000 < passed_ms && passed_ms <= 3700){ led_base_pattern_dim(now_ms, 700, 20);}
  else if(3700 < passed_ms && passed_ms <= 4000){ led_base_pattern_off();}
  else if(4000 < passed_ms && passed_ms <= 4500){ led_base_pattern_dim(now_ms, 500, 10);} // 待機音
  else if(4500 < passed_ms && passed_ms <= 6000){ led_base_pattern_inc(now_ms, 1500, 20);}
  else{                                           led_base_pattern_blink_slowly(now_ms, 170, 10);}  
}

void led_pattern_critical(unsigned long passed_ms, unsigned long now_ms){
  if(passed_ms <= 300){                            led_base_pattern_blink(now_ms, 80);}
  else if(  300 < passed_ms && passed_ms <= 1000){ led_base_pattern_dim(now_ms, 700, 20);}
  else if( 1000 < passed_ms && passed_ms <= 1600){ led_base_pattern_inc(now_ms, 600, 10);}
  else if( 1600 < passed_ms && passed_ms <= 1700){ led_base_pattern_off();}
  else if( 1700 < passed_ms && passed_ms <= 2200){ led_base_pattern_blink(now_ms, 60);}
  else if( 2200 < passed_ms && passed_ms <= 3000){ led_base_pattern_dim(now_ms, 800, 20);}
  else if( 3000 < passed_ms && passed_ms <= 3700){ led_base_pattern_blink(now_ms, 60);}
  else if( 3700 < passed_ms && passed_ms <= 5000){ led_base_pattern_dim(now_ms, 1300, 20);}
  else{                                            led_base_pattern_off();}  
}

void led_pattern_critical_joker(unsigned long passed_ms, unsigned long now_ms){
  if(passed_ms <= 300){                            led_base_pattern_blink(now_ms, 80);}
  else if(  300 < passed_ms && passed_ms <= 1000){ led_base_pattern_dim(now_ms, 700, 20);}
  else if( 1000 < passed_ms && passed_ms <= 2000){ led_base_pattern_dim(now_ms,1000, 20);}
  else if( 2000 < passed_ms && passed_ms <= 2600){ led_base_pattern_inc(now_ms, 600, 10);}
  else if( 2600 < passed_ms && passed_ms <= 2700){ led_base_pattern_off();}
  else if( 2700 < passed_ms && passed_ms <= 3200){ led_base_pattern_blink(now_ms, 60);}
  else if( 3200 < passed_ms && passed_ms <= 4000){ led_base_pattern_dim(now_ms, 800, 20);}
  else if( 4000 < passed_ms && passed_ms <= 4700){ led_base_pattern_blink(now_ms, 60);}
  else if( 4700 < passed_ms && passed_ms <= 6000){ led_base_pattern_dim(now_ms, 1300, 20);}
  else{                                            led_base_pattern_off();}   
}

void control_led(){

  unsigned long now_ms = millis();

  if(prev_state != state){
    // 電源ON直後はLEDをOFFにしているので、最初の状態遷移以降にLEDを有効化させるための処理
    is_led_active = true;
    if(state == STATE_INIT){
      if(prev_state == STATE_TEMP){   
        if(prev_mode != mode){
          // モード変更した直後は発光処理はさせないようにする
          is_led_active = false;
        }else if(is_memory_ejected){
          // このときは発光パターンをリセットさせない
          ;
        }else{
          led_pattern_start_time = now_ms;
        }
      }else{
        led_pattern_start_time = now_ms;
      }
    }else if(state == STATE_CHANGE){
      // モードごとで発光タイミングを切り替えるための処理
      switch(mode){
      case MODE_LOST:
        memory_name_start_time = WAIT_FOR_LOST_NAME_MS;
        change_start_time = WAIT_FOR_LOST_NAME_MS + WAIT_FOR_LOST_CHANGE_MS;
        break;
      case MODE_W_R:
        memory_name_start_time = WAIT_FOR_W_R_NAME_MS;
        change_start_time = WAIT_FOR_W_R_NAME_MS + WAIT_FOR_W_R_CHANGE_MS;
        break;
      case MODE_W_L:
        memory_name_start_time = WAIT_FOR_W_L_NAME_MS;
        change_start_time = WAIT_FOR_W_L_NAME_MS + WAIT_FOR_W_L_CHANGE_MS;
        break;
      }
      led_pattern_start_time = now_ms;
    }else if(state == STATE_CRITICAL_READY){
      if(!is_critical){
        // is_criticalがtrueのときは、発光パターンをリセットさせない
        led_pattern_start_time = now_ms;
      }
    }else{
      // 上記以外の状態遷移では、発光パターンのリセットのみ実施
      led_pattern_start_time = now_ms;
    }
  }

  unsigned long passed_ms = now_ms - led_pattern_start_time;

  switch(state){
  case STATE_INIT:
    if(is_led_active){
      if(is_memory_ejected){
        led_pattern_memory_ejected(passed_ms, now_ms);
      }else{
        led_pattern_memory_name(passed_ms, now_ms);
      }
    }
    break;
  case STATE_TEMP:
    if(is_memory_ejected){
      led_pattern_memory_ejected(passed_ms, now_ms);
    }
    break;
  case STATE_CHANGE_READY:
    if(is_change_off){
      led_pattern_change_off(passed_ms, now_ms);
    }else{
      led_pattern_change_ready(passed_ms, now_ms);
    }
    break;
  case STATE_CHANGE:
    led_pattern_change(passed_ms, now_ms);
    break;
  case STATE_CRITICAL_READY:
    if(is_critical){
      if(mode == MODE_LOST){
       led_pattern_critical_joker(passed_ms, now_ms);
      }else{
       led_pattern_critical(passed_ms, now_ms);
      }
    }else{
      led_pattern_critical_ready(passed_ms, now_ms);
    }
    break;
  case STATE_CRITICAL:
    if(mode == MODE_LOST){
     led_pattern_critical_joker(passed_ms, now_ms);
    }else{
     led_pattern_critical(passed_ms, now_ms);
    }
    break;
  default:
    ;    
  }
}

////////// メイン処理 ////////////////////////////////////////////////////////////

void setup(){  
  Serial.begin(115200);
  pinMode(SW_1_PIN, INPUT_PULLUP);
  pinMode(SW_2_PIN, INPUT_PULLUP);
  pinMode(SW_3_PIN, INPUT_PULLUP);
  pinMode(LED_PIN,  OUTPUT);

  // MP3プレイヤーセットアップ
  ss_mp3_player.begin(9600);
  mp3_set_volume(SOUND_VOLUME_DEFAULT);

  led_base_pattern_off();
}

void loop(){

  unsigned long now_ms = millis();

  //////////////////// 状態遷移管理 ////////////////////

  sw[0] = digitalRead(SW_1_PIN);
  sw[1] = digitalRead(SW_2_PIN);
  sw[2] = digitalRead(SW_3_PIN);
  
  switch(state){
  case STATE_INIT:
    if(memcmp(prev_sw, OFF_OFF_OFF, N_BUTTON) == 0){
      if(memcmp(sw, ON_OFF_OFF, N_BUTTON) == 0){
        state = STATE_TEMP;
        prev_mode = mode;
        is_memory_ejected = false;
        is_sw_1_pressing = true; // モード変更長押し認識開始
        long_press_start_time = now_ms;
      }else if(memcmp(sw, OFF_ON_OFF, N_BUTTON) == 0){
        state = STATE_CHANGE_READY;
        is_change_off = false;
      }else if(memcmp(sw, ON_ON_OFF, N_BUTTON) == 0){
        state = STATE_CHANGE;
      }else if(memcmp(sw, OFF_OFF_ON, N_BUTTON) == 0){
        state = STATE_CRITICAL_READY;
        is_critical = false;
      }else if(memcmp(sw, ON_OFF_ON, N_BUTTON) == 0){
        state = STATE_CRITICAL;
      }
    }
    break;
  case STATE_TEMP:
    if(memcmp(prev_sw, ON_OFF_OFF, N_BUTTON) == 0){
      if(memcmp(sw, OFF_OFF_OFF, N_BUTTON) == 0){
        state = STATE_INIT;
        is_sw_1_pressing = false; // モード変更長押し認識終了
      }else if(memcmp(sw, ON_ON_OFF, N_BUTTON) == 0){
        state = STATE_CHANGE;
        is_sw_1_pressing = false; // モード変更長押し認識終了
      }else if(memcmp(sw, ON_OFF_ON, N_BUTTON) == 0){
        state = STATE_CRITICAL;
        is_sw_1_pressing = false; // モード変更長押し認識終了
      }     
    }
    break;
  case STATE_CHANGE_READY:
    if(memcmp(prev_sw, OFF_ON_OFF, N_BUTTON) == 0){
      if(memcmp(sw, ON_ON_OFF, N_BUTTON) == 0){
        state = STATE_CHANGE;
      }else if(memcmp(sw, OFF_OFF_OFF, N_BUTTON) == 0){
        state = STATE_INIT;
        is_memory_ejected = true;
      }
    }
    break;
  case STATE_CHANGE:
    if(memcmp(prev_sw, ON_ON_OFF, N_BUTTON) == 0){
      if(memcmp(sw, OFF_ON_OFF, N_BUTTON) == 0){
        state = STATE_CHANGE_READY;
        is_change_off = true;
      }else if(memcmp(sw, ON_OFF_OFF, N_BUTTON) == 0){
        state = STATE_TEMP;
        is_memory_ejected = true;
      }else if(memcmp(sw, OFF_OFF_OFF, N_BUTTON) == 0){
        state = STATE_INIT;
        is_memory_ejected = true;
      }
    }
    break;
  case STATE_CRITICAL_READY:
    if(memcmp(prev_sw, OFF_OFF_ON, N_BUTTON) == 0){
      if(memcmp(sw, ON_OFF_ON, N_BUTTON) == 0){
        state = STATE_CRITICAL;
      }else if(memcmp(sw, OFF_OFF_OFF, N_BUTTON) == 0){
        state = STATE_INIT;
        is_memory_ejected = true;
      }
    }
    break;
  case STATE_CRITICAL:
    if(memcmp(prev_sw, ON_OFF_ON, N_BUTTON) == 0){
      if(memcmp(sw, OFF_OFF_ON, N_BUTTON) == 0){
        state = STATE_CRITICAL_READY;
        is_critical = true;
      }else if(memcmp(sw, ON_OFF_OFF, N_BUTTON) == 0){
        state = STATE_TEMP;
        is_memory_ejected = true;
      }else if(memcmp(sw, OFF_OFF_OFF, N_BUTTON) == 0){
        state = STATE_INIT;
        is_memory_ejected = true;
      }
    }
    break;
  default:
    ;
  }

  ////////// SW1長押し認識処理 ///////////////
  if(is_sw_1_pressing && now_ms - long_press_start_time >= LONG_PRESS_MS){
    prev_mode = mode;
    switch(mode){
    case MODE_W_L:
      mode = MODE_LOST;
      mp3_play(SOUND_LOST);
      break;
    case MODE_W_R:
      mode = MODE_W_L;
      mp3_play(SOUND_SIDE_L);
      break;
    case MODE_LOST:
      mode = MODE_W_R;
      mp3_play(SOUND_SIDE_R);
      break;
    default:
      ;
    }
    is_sw_1_pressing = false;
  }

  ////////// 音声再生処理 ////////////////////
  control_sound();

  ////////// LED発光処理 ////////////////////
  control_led();

  ////////// デバッグ処理 ////////////////////
  print_state();

  ////////// 処理状態の保持 //////////////////
  prev_sw[0] = sw[0];
  prev_sw[1] = sw[1];
  prev_sw[2] = sw[2];
  prev_state = state;

  delay(LOOP_DELAY_MS);
}

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメント一覧 (44件)

  • コメント失礼いたします。
    こちらのソースコードで検証を行う際に、124行目の部分にwarningが発生するとはあるでしょうか?

    • GL様

      うーん、そんな変なことをしているわけではないと思うのですが。。。試しにその部分の関数(mp3_set_volume)を丸ごと全部コメントアウトしてみてください。それでエラーが解決すればそこの関数が原因ですが、コメントアウトしてもエラーが出るようであれば、他の部分が原因のエラーが124行目のエラーとして表示されてしまっている気がします。

  • ありがとうございます。試してみます。
    ちなみにこのdfr0534を使用する際に必要な設定やarduinoのライブラリなどはありますでしょうか。

    • Arduinoで標準で使用できるSoftwareSerialだけで動くと思うので、特に特別なものは必要ないと思います。

  • dfr0534のサンプルプログラムを実行した際にも同じようなエラーが出てしまいました。

  • warning: narrowing conversion of ‘(((int)Track) + 179)’ from ‘int’ to ‘unsigned char’ inside { } [-Wnarrowing]
    連投すいません、このようなwarningです。

    • 素直に読むと、2byteで表現されるintの数を、1byteで表現されるunsigned char型に入れようとしているように見えます。
      あと、ErrorではなくWarningであるなら、一応プログラムとしては動作しないでしょうか?

  • たびたびすみません。ハードの質問なのですが、スイッチ部分の配線はどのようになってるでしょうか。

    • 申し訳ないのですが、配線については既にブログ記事に記載している以上の情報は特にありません。。。

  • 一つ質問です。
    ハザードメモリの記事も読みましたが電池に関しては必要なのでしょうか?
    近々T2ウェザーメモリ(カプセル版)をCSM風に改造するので細かく知りたいと願います。

    • 黒鶴 様

      すみません、何をされたいのかがハッキリとはわからないのですが、発光や音声が必要なら電池は絶対に必要ですし、さらに私の作例のようにマイコンやMP3プレイヤーを使うつもりなら、ボタン電池では電力が足りないので、リチウムイオンポリマー充電池が必要だと思います。

  • コメント失礼します。MP3モジュールは、DFPlayer MiniのようなSDカード式の物でも大丈夫でしょうか?
    また、SDカード式の場合コマンド等に変更すべき箇所はありますか?

    • 匿名様
      > MP3モジュールは、DFPlayer MiniのようなSDカード式の物でも大丈夫でしょうか?
      はい、もちろん大丈夫です。

      > SDカード式の場合コマンド等に変更すべき箇所はありますか?
      SDカード式どうこうというよりは、使用するモジュールに合わせて適切なライブラリを使用するようにしてください。例えば、DFPlayerであれば、以下のようなライブラリがあります。
      DFPlayer – A Mini MP3 Player For Arduino
      DFPlayer Mini Fast
      一応公式?のライブラリは前者だと思いますが、後者を使っても良いと思います。

  • 私も成功しましたが、メインボタンを押すと音が出なくなり、残りのボタンは別の音がします、どうすればいいですか?

    • ロケット様

      うーん、原因を特定できる情報がほぼないので判断できません。。。プログラムか配線を間違えているとしか。。。

  • コメント失礼します。
    『LEDの足を切ってしまって、基板に残った足とスイッチ部分を配線します。』
    と書かれていますが、ここはリード線をハンダでつけている感じになるのでしょうか?
    基盤を弄るのは初めてで、変な質問だと思われたなら申し訳ございません。

    • 匿名 様

      だいぶトリッキーな配線でわかりにくくて申し訳ありません。そうですね、リード線でも良いですし、確か私は抵抗器の足の余りとかを繋ぎに使って配線してたと思います。

      • 返信ありがとうございます。
        抵抗機の足の余りは思いつきませんでした、教えてくださってありがとうございます。

    • k様

      すみません、覚えていないのですが、そんなに大きな電圧の話でもないので、適当です。
      私が普段使っている範囲内なので、100Ω〜1kΩの範囲内だったと思います。
      写真に写っている抵抗のカラーコードを頑張って読めば正確な値はわかりますが。。。。

  • コメント失礼いたします。
    こちらの方法でエターナルメモリを製作中なのですが、変身時の発光パターンを
    DXのエターナルメモリと同じにするにはどの部分をどのように変更すればいいのか
    可能でしたら教えていただけないでしょうか?

    また、ロストドライバーを展開した状態でメモリを指すとメモリ装填音だけ鳴る(変身音は鳴らない)といった書き換えは可能でしょうか?

    突然で大変失礼かとも思いましたが、どうかお教えいただきたく思います。

    • 匿名様

      すみません、全てを手取り足取り教えることはできないので、ヒントだけ。。。

      > 変身時の発光パターンをDXのエターナルメモリと同じにするには
      > どの部分をどのように変更すればいいのか可能でしたら教えていただけないでしょうか?

      359行目の、led_pattern_change関数ですね。
      コメントにあるとおり、「ドライバー開く」「メモリ名」「変身音」の3ステップの発光パターンをこの中で定義しています。
      この中では、基本的に以下の4パターンを組み合わせて光り方を調整しているので、
      それぞれの順番と、継続時間、変化時間間隔を調整すれば、大体の発光パターンは作り出せると思います。
      ・led_base_pattern_on … LEDをつける
      ・led_base_pattern_off … LEDを消す
      ・led_base_pattern_inc … 徐々にLEDをつける
      ・led_base_pattern_dim … 徐々にLEDを消す

      > また、ロストドライバーを展開した状態でメモリを指すとメモリ装填音だけ鳴る(変身音は鳴らない)といった書き換えは可能でしょうか?

      多分できると思います。記事中の状態遷移図を見ていただくと、STATE_INITからSTATE_CHANGEに行く経路は3つあることがわかると思います。
      ①STATE_INIT → STATE_CHANGE_READY → STATE_CHANGE
      ②STATE_INIT → STATE_CHANGE
      ③STATE_INIT → STATE_TEMP → STATE_CHANGE
      このうち、①が通常の変身動作(メモリを挿して、ドライバーを開く)で、
      ②と③が、仰られている「ドライバーを展開した状態でメモリを挿す」に相当すると思います。
      ②になるか③になるかはやってみないとわかりませんが、両方に同じコードを書いておけば済む話だと思います。
      現状、私は②③の動作については発光と音声を何も定義していない気がしますので(※忘れているだけならすみません)、
      この部分の遷移で、ご自身がやりたい発光や音声再生のコードを独自に追加頂くと良いと思います。

  • コメント失礼します
    この記事を読んでガイアメモリを作成中です
    https://youtu.be/LQRUi_2mjIs?t=275

    ロストドライバーモードで、こちらの動画の4:35秒のようにドライバーが開いた状態から、高速で「閉じる→開く→閉じる」といったようなことをすると、RスロットモードでもLスロットモードでもない別のモード(待機音、変身音、必殺音も異なる)
    に派生させたいです
    具体的に言うと、

    (ロストドライバーモード)
    ①ドライバーを閉める
      ↓2秒以内に  
    ②ドライバーを開ける
      ↓2秒以内に   
    ③ドライバーを閉める→
      ↓
     {別のモード(別の待機音、変身音、必殺音)}
    ④メモリをドライバーから抜く
      ↓
    (ロストドライバーモード)に戻る

    ①~④のようなことをしたい場合、スケッチのどこをどうすればいいでしょうか?
    お仕事で忙しいと思いますが、お暇な時でいいので教えて頂けたら嬉しいです

    • 匿名 様

      うーん、提示頂いたものは私が作成したものではないので、作られた方に聞いて欲しいところではありますが。。。
      すみません、元より丸々コードを書くことはできませんので、考え方を述べるに留めさせてください。

      私の状態遷移図で言うなら、STATE_CHANGE→STATE_CHANGE_READY→STATE_CHANGE→STATE_CHANGE_READYの状態遷移に、それぞれタイマーをつける、ということになると思います。
      例えば、STATE_CHANGE→STATE_CHANGE_READYの変化が起こる部分は611〜614行目当たりですが、ここで、タイマーのスタート時間を変数(仮に、start_time)に保存します。
      そして、次にSTATE_CHANGE_READY→STATE_CHANGEが起こる部分は600〜603行目あたりですが、ここで、millis() – start_time <= 2000 みたいな判定式を入れる。 こんな感じのタイマーセット→判定を繰り返して、条件を満たせば特殊モードフラグを立てる(例えば、special_mode = true)、みたいなことをすれば良いと思います。 で、音声再生の部分で、このフラグがtrueかfalseかで、再生する音声ファイルを変える、ということになると思います。 大筋こんな感じになると思いますが、フラグをいつ戻すとかの処理も追加する必要があります。 これについては、上記のように「どこの状態遷移で処理を入れれば良いか」を考えていただくと良いと思います。

  • 質問があります。
    同じ回路を組み、同じプログラムにしましたが、sw_1を押しても発光せず、音声に至っては全て機械音(ジーという音)しか鳴りません。
    このことに関して2点ほど心当たりがあります。

    一つ目は、20th ダブルドライバーに付属しているガイアメモリを使っていることです。
    ダブルドライバーに付属しているガイアメモリはこのサイトで使っているガイアメモリとやや基板がやや異なっている(swが付いている面の裏側の左上にあるBAT+が無く、その代わりにSPとU(Y?)DOがある等)ので、もしかしたら配線を変える必要があるということ。
    因みにsw_2とsw_3は問題なく発光しました。

    二つ目は、MP3 Playerモジュールに「DFPlayer HW-247A」を使っていることです。
    sdカードには「0001.mp3」という風にファイルを置きました。

    「こうすれば不具合が改善されるんじゃないか」というヒダカヒロジさんのアドバイスが欲しいです。

    • 匿名様

      一点目に関しては、すみません、20thダブルドライバー付属のメモリを持っていないため、はっきりしたことは言えません。
      ただ、仰るとおり基板が変わっているのであれば、SW_1を押したとき/押さないときでどこの電位が変化するかを確認して、配線を見直さなければならないかもしれません。

      二点目に関しては、DFPlayerを使っているのであれば、「mp3/0001.mp3」のような感じでファイルを置いているのではないかと思いますが、原因の切り分けが必要かと思います。
      まず、今回使っているマイコン基板+バッテリー+DFPlayerのみの、最も簡単な配線&サンプルスケッチなどで、きちんと音が鳴るかを確認した方が良いと思います。
      そこで音が鳴らないようなら、そもそもの配線・基本のプログラムが誤っていることになります。
      簡単な配線&シンプルなスケッチなら音が鳴るが、今回のガイアメモリの配線になると鳴らない、ということであれば、
      簡単な配線&シンプルなスケッチの状態から少しずつ配線とプログラムを足しながら動作確認していき、何を足したところでおかしくなったかで、原因を特定することになると思います。

      • 返信ありがとうございます。
        質問の捕捉になりますが、音が鳴らない原因として、DFPlayerのライブラリ?を使っていなかったことも原因かもしれません。
        マイコンはseeeduino XIAOを使っています。
        あと確かにsdカードには「mp3/0001.mp3」と置いていました。

        何はともあれまずは「マイコン基板+バッテリー+DFPlayer」のみでしっかり音が鳴るか、試したみたいと思います。

        追加で2点質問があります。
        変な質問だと思われたならすいません。

        一点目
        sw_1の不具合の解決策として「SW_1を押したとき/押さないときでどこの電位が変化するかを確認して、配線を見直す」とありますが、
        電位の変化は具体的にどうやって確認すればいいですか?
        そもそも「電位」が何なのか分からなくて…

        二点目
        回路図から下にスクロールしていくと、「LEDの足を切ってしまって、基板に残った足とスイッチ部分を配線します。」とありますが、
        このLEDの足とスイッチ部分を繋げたものに、本体のLEDを繋げればいいですか?
        それとも、LEDの足とスイッチ部分だけを繋げて、本体のLEDは繋げない、ということですか?
        回路図を見る限り後者だと思いますが、確認がしたくて…

        教えてほしいです。

        • 匿名 様
          > 音が鳴らない原因として、DFPlayerのライブラリ?を使っていなかったことも原因かもしれません。

          なるほどです。ライブラリなしでも頑張れば鳴らせると思いますが、車輪の再発明みたいなことになってしまいますので、素直にライブラリを使った方が簡単だと思います。

          > 電位の変化は具体的にどうやって確認すればいいですか? そもそも「電位」が何なのか分からなくて…

          基本的には、テスターという計測器具を使って測ることになると思います。テスターとか電位とかがよくわからなければ、
          例えば以下のような記事を見て頂くと良いかもしれません(適当に検索したものですが)
          https://www.diylabo.jp/column/column-985.html

          > このLEDの足とスイッチ部分を繋げたものに、本体のLEDを繋げればいいですか?
          > それとも、LEDの足とスイッチ部分だけを繋げて、本体のLEDは繋げない、ということですか?

          はい、回路図の通り、ここにはLEDは繋げません。LEDは抵抗を挟んで、直接マイコンと繋ぎます。

          • ヒダカヒロジさんのアドバイスを頂いて、
            何とかsw_1を押すとLEDが発光するようになりました。
            また、サンプルスケッチを使うとDFPlayer miniから音声が鳴りました。

            他の方の返信に

            「> SDカード式の場合コマンド等に変更すべき箇所はありますか?
             SDカード式どうこうというよりは、使用するモジュールに合わせて適切なライブラリを使用するようにしてください。」

            とありましたので、このサイトのプログラムの頭に
            「#include 」を追加しました。

            しかし、やはりどのスイッチを押しても発光はしますが音声が鳴りません。

            スイッチを押すと、スピーカーから「カチッ」と音がするので、信号が届いていないというわけではなさそうです。
            そうなるとプログラムが原因だと思いますが、
            「#include 」以外に何かを追加する必要があるのでしょうか?

          • 匿名 様

            繰り返しになりますが、以下が対応方針になります。

            > 簡単な配線&シンプルなスケッチなら音が鳴るが、今回のガイアメモリの配線になると鳴らない、ということであれば、
            > 簡単な配線&シンプルなスケッチの状態から少しずつ配線とプログラムを足しながら動作確認していき、何を足したところでおかしくなったかで、原因を特定することになると思います。

            個別にソースコードを見て回答を示す、ということには対応致しかねますので、ご了承ください。

  • 数年前のブログなのにいきなりコメントしてすみません

    私も好きな音声をガイアメモリで鳴らしてみたい!
    の一心で調べた所この記事を見つけて拝見させて頂きました。
    もしベルト装填時の遅延の制御などをなくして、いわばサウンドガイアメモリ模様な仕様にしたい場合はマイコンを除いてSDを入れるプレイヤーに直で繋いでも大丈夫なのでしょうか?
    はんだとかは持っているので繋げたりとかは大丈夫なんですけどいかんせん配線の知識が皆無の学生な者でMP3プレイヤーの購入に割り出せずにいます。

    もしよろしければアドバイスを頂けると非常にありがたいです!

    • コメントありがとうございます。ただ申し訳ありません、「マイコンを除いてSDを入れるプレイヤーに直で繋ぐ」というのが、何をされたいのかが読み取れず。。。少なくとも、何をプレイヤーに繋ぐのかが、記載がないのでわからないです。

      もし、マイコン(Arduino)を使わずに、DFPlayer(←「SDを入れるプレイヤー」というのは、これのことを指していますか?)にガイアメモリのボタンを直接繋いで音を鳴らしたい、ということでしたら、私はやったことがないですが、おそらくできると思います。
      以下の仕様をご参照ください。 https://wiki.dfrobot.com/DFPlayer_Mini_SKU_DFR0299

      • 返信ありがとうございます!

        すみません伝え方がかなりアバウトでした。
        そうですね、DFプレイヤーのことを指していました。
        Amazonとかで適当に調べたら超小型のMP3再生電子基板モジュールなどがあり、同じなのかなと思い質問しました。DFプレイヤーの方がコストいいですね

    • 音角Ver2のソースコードを見に来たら気になるコメントがあったので、横から失礼します。

      ドーパント型のガイアメモリ(ガイアウィスパーと変身音の2音のみ)であれば、↓の私の約5年前のブログ記事のように、マイコンを使わず、DFplayer+αの部品で作れます。
      https://chitakekoubou.blogspot.com/2019/02/blog-post.html
      ですが、上記の方法ではダブルドライバorロストドライバに対応できません。

      一応、ご参考までに。

  • ヒダカヒロジさんに質問があります。
    もしマイコンはArduinoプロミニとMP3モジュールは、DFPlayer Miniを使っているならコードのプログラムにどこかの部分変える必要ありますか?

    • ノブ様
      ここだけと断言はできませんが、104〜127行目と、547〜549行目あたりは変える必要があると思います。

  • 質問失礼します。
    配線の質問なのですが、コネクタとスイッチ部分の配線がどうしても理解できず教えていただきたいです。またスイッチの製品名なども教えていただきたいです。コネクタはURLのものを使用したのですが、スイッチはURLが記載されていなかったのでスライドスイッチを購入しています。

    • だいぶ昔のことなので私もだいぶうろ覚えですが…確かに分かりにくいというか、だいぶトリッキーな配線になっていますね。今の私が見てわかりにくなと思ったのは、黒い線、GNDの配線です。
      バッテリーやSeeeduino XIAOのGNDから伸びている線が抵抗Rに直結するような絵になってしまっていますが、これは直結しているのではなくて、見にくいですが、プリント基板の「BAT+」と書かれている部分の端子の半田付けを中継して配線されています。この中継が大事で、これをやっておかないと、他は正しく配線していても、プリント基板上の各スイッチが反応しません。

      あと、仰られているスイッチというのがどれを指しているのかがよくわかっていないのですが、回路図上で”Power SW”となっている部分のことを指しているのでしたら、これは、ガイアメモリに入るサイズの小型のスライドスイッチであればなんでも良いです。例えばこういうものとかですね。

      • 返信ありがとうございます。BAT+に接続すればいいというのはわかりました。
        もう一つ質問があるのですが「コネクタ」と「スライドスイッチ」の配線、はんだ付けする箇所が分からっておりません。コネクタの先端の突起やスイッチの先に直にはんだ付けをしてもいいのでしょうか?

        • > コネクタの先端の突起やスイッチの先に直にはんだ付けをしてもいいのでしょうか?

          はい、構いません。ただ、失礼ながら、今の状態でいきなりガイアメモリの基板に直接はんだ付けを始めるのは少々難しい印象を受けました。可能なら、基板にはんだ付けする前に、よくあるブレッドボードやピンなどを使って、コネクタやスライドスイッチのそれぞれに直接はんだ付けした場合にどういう動作になるかを確認してから、ガイアメモリの基板の半田付けにチャレンジした方が良いのでは、と思いました。

コメントする

目次