もう少し音楽プレーヤーっぽくしてみる

th_push_led-8

前回前々回と、ボタン操作で再生する音楽を選択可能なArduino音楽プレーヤーを作成しました。ここから、もうちょっとカッコ良くしていきたいと思います。

th_push-led-4

まず、ボタンです。上の写真のようなタクトスイッチだと電子工作感(?)がとても強いので、もう少しカッコ良いボタンを買ってきました。

 th_push-led-5

これです。正式型番はHB-15CKS2-4RBRS。押したら光るタイプのスイッチで、これはモーメンタリ(押している間だけ通電)ですが、オルタネイト(通電状態がロック/アンロックされる)版もあります。カッコ良いんだけど、高い。。。(1個600円ぐらい)

この手のタイプのスイッチを使うのは初めてなのですが、何か端子がいっぱいあってよくわかりません。「C」「L」「L」「NO」「NC」という刻印があるのはわかっていて、Cはおそらく何かの共通端子、LはおそらくLED関係なのでしょうが、どっちのLがプラスでマイナスなのかわかりません。

とりあえず仕様を探してみたところ、共立エレショップさんの方で仕様が公開されていました。それを見ると、

  • 押している時は「C」と「NO」が通電
  • 押していない時は「C」と「NC」が通電

となっています。こちらも参考にさせていただくと、「NO」は「常時開(Normally Open)」なので、押すと通電。「NC」は「常時閉(Normally Close)」なので、押していない時は通電、ということのようです。ただ、LEDの極性が相変わらずはっきりしません。

 th_push-led-2

クリヤパーツを上に引っこ抜くとLEDが見えるので、それをさらに引き抜くと極性がわかるのかもしれませんが、もう面倒なので直接通電させて調べることにします。5V以下の電圧なら逆に繋いでもつかないだけで壊れないハズ。

 th_push-led-6

とりあえず音楽再生ボタンをモーメンタリスイッチに置き換えてみました。GNDから伸びているリード線をスイッチの「NO」に繋ぎ、スイッチの「C」とArduinoのA0番ピンを繋いでいます(A0番ピンは内部プルアップ有効済みで、デジタル入力として使用)。LED発光については、間に220Ωをかまして5V電源とGNDをスイッチの「L」に繋いでいます。最初繋いだら光りませんでしたが、「L」の接続を入れ替えたら光りました。うん、やっぱり試してみる方が早い。

 th_push-led-3

ちなみに、2個ほど試したところ、どちらも型番が書いてある方がマイナスでした。調べた後は黒線でマーキングしておくと、後々わかりやすいです。

これでちゃんと光るし、音楽再生/停止も機能しています。クリック感も良い感じです。が、常時光りっぱなしです。このスイッチのLEDの系統は、メインのスイッチの回路とは完全に独立しているので、「L」に繋ぐのを5V電源じゃなくてArduinoのIOピンの出力に変更してやれば、好きなように光らせることができます。

th_push-led-7

ここではシンプルに、SDカードの初期化が無事に済んだ時点で、確認用に2つのLEDを点灯させています。

 th_push_led-8

さらに、前にちょっと触った7セグLEDで曲番号を表示するようにすると、だいぶそれっぽくなります。前に7セグLEDを触ったときは二桁表示をダイナミック制御にすることで制御ピン数を少なくしていたのですが、いざそのまま組み合わせてみると、普段の表示は問題ないのですが、ボタンを押した時に一瞬表示が崩れてしまう現象が発生してしまいました。

そのため、ここでは表示の綺麗さを優先して、Arduino Mega (Seeeduino Mega)のIOピンの数に物を言わせて、各桁の7セグLEDを個別に制御しています。

一応、ソースコードはこんな感じです。

#include <WaveHC.h>
#include <WaveUtil.h>

#define PLAY_PIN   A0
#define SELECT_PIN A1
#define PLAY_LED_PIN A2
#define SELECT_LED_PIN A3

#define PIN_1_1  38
#define PIN_1_2  39
#define PIN_1_4  40
#define PIN_1_5  41
#define PIN_1_6  42
#define PIN_1_7  43
#define PIN_1_9  44
#define PIN_1_10 45

#define PIN_10_1  26
#define PIN_10_2  27
#define PIN_10_4  28
#define PIN_10_5  29
#define PIN_10_6  30
#define PIN_10_7  31
#define PIN_10_9  32
#define PIN_10_10 33

#define error(msg) SerialPrint_P(PSTR(msg))

SdReader  cardReader;
FatVolume volume;
FatReader root;
FatReader playFile;
WaveHC    waveFile;

const char game_0[] PROGMEM = "maX.wav";
const char game_1[] PROGMEM = "zeX.wav";
const char game_2[] PROGMEM = "moX.wav";
const char* const games[] PROGMEM = {game_0, game_1, game_2};
const PROGMEM char number[] = {'0','1','2','3'};

#define ON  LOW
#define OFF HIGH

// PIN_1~PIN_10(3,8除く)で点灯箇所を示す配列を作成
const uint8_t seg[][8] PROGMEM = {
//Seg:  1   2   4   5   6   7   9   10
      {ON , ON, ON,OFF, ON, ON, ON,OFF}, // 0
      {OFF,OFF, ON,OFF, ON,OFF,OFF,OFF}, // 1
      {ON,  ON,OFF,OFF, ON, ON,OFF, ON}, // 2
      {OFF, ON, ON,OFF, ON, ON,OFF, ON}, // 3
      {OFF,OFF, ON,OFF, ON,OFF, ON, ON}, // 4
      {OFF, ON, ON,OFF,OFF, ON, ON, ON}, // 5
      {ON,  ON, ON,OFF,OFF, ON, ON, ON}, // 6
      {OFF,OFF, ON,OFF, ON, ON, ON,OFF}, // 7
      {ON,  ON, ON,OFF, ON, ON, ON, ON}, // 8
      {OFF, ON, ON,OFF, ON, ON, ON, ON}  // 9
};

char name[8];
uint8_t index = 1;
uint8_t lastPlayState    = HIGH;
uint8_t lastSelectState  = HIGH;
boolean isPlaying = false;

void setup(){
  Serial.begin(9600);
  pinMode(PLAY_PIN, INPUT_PULLUP);
  pinMode(SELECT_PIN, INPUT_PULLUP);
  pinMode(PLAY_LED_PIN, OUTPUT);
  pinMode(SELECT_LED_PIN, OUTPUT);

  digitalWrite(PLAY_LED_PIN, LOW);
  digitalWrite(SELECT_LED_PIN, LOW);

  pinMode(PIN_1_1, OUTPUT);
  pinMode(PIN_1_2, OUTPUT);
  pinMode(PIN_1_4, OUTPUT);
  pinMode(PIN_1_5, OUTPUT);
  pinMode(PIN_1_6, OUTPUT);
  pinMode(PIN_1_7, OUTPUT);
  pinMode(PIN_1_9, OUTPUT);
  pinMode(PIN_1_10, OUTPUT);
  pinMode(PIN_10_1, OUTPUT);
  pinMode(PIN_10_2, OUTPUT);
  pinMode(PIN_10_4, OUTPUT);
  pinMode(PIN_10_5, OUTPUT);
  pinMode(PIN_10_6, OUTPUT);
  pinMode(PIN_10_7, OUTPUT);
  pinMode(PIN_10_9, OUTPUT);
  pinMode(PIN_10_10, OUTPUT);

  // カードリーダの初期化
  if(!cardReader.init()){
    error("Can't init the card.");
    while(1){}
  }

  // FATパーティションを検索
  uint8_t partition = 0;
  for(partition=0; partition<5;partition++){
    if(volume.init(cardReader, partition)){
      break;  
    }  
  }

  // FATパーティションが見つからなければエラー
  if(partition == 5){
    error("Can't find a FAT partition.");
    while(1){}
  }

  // ボリュームのルートディレクトリをオープン
  if(!root.openRoot(volume)){
    error("Can't open the root.");
    while(1){}
  }

  digitalWrite(PLAY_LED_PIN, HIGH);
  digitalWrite(SELECT_LED_PIN, HIGH);
  pattern_on(index);
}

void loop(){
  uint8_t selectState = digitalRead(SELECT_PIN);
  if(lastSelectState == HIGH && selectState == LOW){
    soundSelect();    
    if(waveFile.isplaying){
      soundStop();
      soundPlay();
    }
  }
  lastSelectState = selectState;

  uint8_t playState = digitalRead(PLAY_PIN);
  if(lastPlayState == HIGH && playState == LOW){
    if(waveFile.isplaying){
      soundStop();
    }else{
      soundPlay();
    }    
  }
  lastPlayState = playState;

  if(isPlaying  && !waveFile.isplaying){
    // 曲の再生が終わったら自動的に次の曲を再生する
    soundSelect();
    soundPlay();
  }

  delay(10);  
}

void soundPlay(){
  // ファイル名のセット
  strcpy_P(name, (char*)pgm_read_word(&(games[2])));
  name[2] = pgm_read_byte(&number[index]); 
  // ルートディレクトリにあるファイルを名前指定でオープン
  if(!playFile.open(root, name)){
    error("Can't open the file to play.");
  }  
  // プレーヤーにファイルをセット
  if(!waveFile.create(playFile)){
    error("Not a valid WAV file.");
  }
  // 再生開始
  waveFile.play();
  // 再生用にオープンしたファイルをクローズ
  playFile.close();

  isPlaying = true;
}

void soundStop(){
  waveFile.stop();
  isPlaying = false;
}

void soundSelect(){
  index == 3 ? index = 1: index++;
  pattern_on(index);
}

void pattern_on(uint8_t num){
  uint8_t disp_num_1  = num % 10;
  uint8_t disp_num_10 = num / 10;
  digitalWrite(PIN_1_1,  pgm_read_byte(&(seg[disp_num_1][0])));
  digitalWrite(PIN_1_2,  pgm_read_byte(&(seg[disp_num_1][1])));
  digitalWrite(PIN_1_4,  pgm_read_byte(&(seg[disp_num_1][2])));
  digitalWrite(PIN_1_5,  pgm_read_byte(&(seg[disp_num_1][3])));
  digitalWrite(PIN_1_6,  pgm_read_byte(&(seg[disp_num_1][4])));
  digitalWrite(PIN_1_7,  pgm_read_byte(&(seg[disp_num_1][5])));
  digitalWrite(PIN_1_9,  pgm_read_byte(&(seg[disp_num_1][6])));
  digitalWrite(PIN_1_10, pgm_read_byte(&(seg[disp_num_1][7]))); 
  digitalWrite(PIN_10_1,  pgm_read_byte(&(seg[disp_num_10][0])));
  digitalWrite(PIN_10_2,  pgm_read_byte(&(seg[disp_num_10][1])));
  digitalWrite(PIN_10_4,  pgm_read_byte(&(seg[disp_num_10][2])));
  digitalWrite(PIN_10_5,  pgm_read_byte(&(seg[disp_num_10][3])));
  digitalWrite(PIN_10_6,  pgm_read_byte(&(seg[disp_num_10][4])));
  digitalWrite(PIN_10_7,  pgm_read_byte(&(seg[disp_num_10][5])));
  digitalWrite(PIN_10_9,  pgm_read_byte(&(seg[disp_num_10][6])));
  digitalWrite(PIN_10_10, pgm_read_byte(&(seg[disp_num_10][7]))); 
}

これで、機能的に自分が必要だったものは揃いました。が、相変わらず電子工作感が半端ないです。これは、おいおいちゃんとした形に仕上げていきます。これはあくまでプロト、ということで。