(初心者向け)Arduinoで複数LEDを点灯させて&音を鳴らす(欽ちゃんの仮装大賞スコアパネル風)

プログラミング

私は他の人の作ったものを真似ることくらいしかできない電子工作入門者です。
そんな私が社内の勉強会向けに「欽ちゃんの仮装大賞」に出てくるスコアパネルみたいなモノを目指してArduinoで動くものを作ってみました。

試行錯誤しながらひとまず動くものを目指したレベルなので、ブレッドボードむき出しです。ボタンを押すと、音とともに16個のLEDが光っていきます。

この記事では、LEDを光らせるところからボタンと音を使うところまで、順を追って説明します。

■使ったもの

  • Arduino UNO R3 × 1個
  • シフトレジスタ(74HC595) × 2個 (シフトレジスタ1個につきLED8個を光らせることができます)
  • LED × 16個
  • 抵抗 150Ωくらい 輝度調整用(抵抗入りLEDなら無くてもOK) × 16個
  • セラミックコンデンサ 0.1μF × 2個
  • タクトスイッチ × 1個
  • 圧電ブザー × 1個
  • 配線に必要なブレッドボード、ジャンパワイヤ

■大量のLEDを光らせる

今回作るものでは大量のLEDを光らせたいのですが、Arduino UNOのGPIOにLEDを一つずつ接続していては全然ピンが足りません。当初はそんなことも気づかずに作ろうとしてました…(ちなみにArduinoにはいろいろな種類があるのでArduino Megaなら今回の16個くらいならGPIOに一つずつ接続しても足ります。)
ググった結果、こういう場合はシフトレジスタと言うICを使って一気に制御する方法があるようです。

参考ページ

hiramine.com 少ない出力ピンで、大量のLEDを制御する(シフトレジスタ使用)

上記ページを参考にLED16個をどんどん光らせてみます。

下記の配線とプログラムで、LEDが0個から16個まで光っては消え、光っては消えを繰り返します。

配線図1

プログラム1

#define NUMOFLED   16 // LEDの数

#define DATAPIN     9 // 74HC595のDSへ
#define LATCHPIN   11 // 74HC595のST_CPへ
#define CLOCKPIN   12 // 74HC595のSH_CPへ

static int score;

void setup()
{
  pinMode(DATAPIN, OUTPUT);
  pinMode(LATCHPIN, OUTPUT);
  pinMode(CLOCKPIN, OUTPUT);

  score = 0;
}

void loop()
{
  delay(100);

  score = score + 1;

  // scoreがLEDの個数を超えたら0点に戻す
  if (score > NUMOFLED) score = 0;
  
  // 送信中はLATCHPINをLOWにする
  digitalWrite(LATCHPIN, LOW);

  // 点数分だけLEDが光る様にデータ送信
  for( int i = 0; i < NUMOFLED; i++ )
  {
    digitalWrite(DATAPIN, i < score?1:0);
    digitalWrite(CLOCKPIN, HIGH);
    digitalWrite(CLOCKPIN, LOW);
  }

  // 送信後はLATCHPINをHIGHに戻す
  digitalWrite(LATCHPIN, HIGH);
}

ここまででこんな感じです。(画像では、この後で足すタクトスイッチやブザーがつながっています)

■タクトスイッチをつなげる

LEDがいっぱい光っただけでも自分としてはかなり満足感があるのですが、仮装大賞のように「プ、ププ、プ、プププ」って光っていくのをやりたい!
というわけで、タクトスイッチを押したタイミングでLEDが順に光って行くようにします。

下記の配線とプログラムで、タクトスイッチを押したタイミングでLEDが順に光って行くようになります。

配線図2

配線図1から、D3端子、GNDにタクトスイッチを追加しています。

プログラム2

プログラム1ではループ内で無条件にscoreを+1していましたが、タクトスイッチが押されていたら+1するように変更します。

#define NUMOFLED   16 // LEDの数

#define DATAPIN     9 // 74HC595のDSへ
#define LATCHPIN   11 // 74HC595のST_CPへ
#define CLOCKPIN   12 // 74HC595のSH_CPへ
#define BUTTONPIN  2  // タクトスイッチへ

static int score;

void setup()
{
  pinMode(DATAPIN, OUTPUT);
  pinMode(LATCHPIN, OUTPUT);
  pinMode(CLOCKPIN, OUTPUT);

  score = 0;
}

void loop()
{
  // タクトスイッチが押されているときだけscoreをカウントアップする
  if(digitalRead(BUTTONPIN)==LOW) {
    score = score + 1;
    
    // scoreがLEDの個数を超えたら0点に戻す
    if (score > NUMOFLED) score = 0;

    // 送信中はLATCHPINをLOWにする
    digitalWrite(LATCHPIN, LOW);
    
    // 点数分だけLEDが光る様にデータ送信
    for( int i = 0; i < NUMOFLED; i++ )
    {
      digitalWrite(DATAPIN, i < score?1:0);
      digitalWrite(CLOCKPIN, HIGH);
      digitalWrite(CLOCKPIN, LOW);
    }
    
    // 送信後はLATCHPINをHIGHに戻す
    digitalWrite(LATCHPIN, HIGH);

    // タクトスイッチが離されるまで待つ
    while(digitalRead(BUTTONPIN)==LOW) {}
  }
}

■音を鳴らす

ボタンを押してLEDを光らせていくことはできましたが、やっぱり音がないと味気ない・・・
なので次はタクトスイッチを押したタイミングで音が鳴るようにします。

配線図3

配線図2から、D13、D14端子に圧電ブザーを追加します。

プログラム3

タクトスイッチが押されるごとに、ブザーを鳴らす処理を追加します。音程が半音ずつ上がっていくよう予め配列に周波数を定義しておきます。

#define NUMOFLED   16 // LEDの数
#define BEATTIME  120 //音を出している時間(msec)

#define DATAPIN     9 // 74HC595のDSへ
#define LATCHPIN   11 // 74HC595のST_CPへ
#define CLOCKPIN   12 // 74HC595のSH_CPへ
#define BUTTONPIN   2 // タクトスイッチへ
#define SPEAKERPIN 13 // スピーカーの出力ピン番号

//20個分の音程(今回使用するのは16個まで)
int sounds[] = {262,277,294,311,330,349,370,392,415,440,466,494,523,554,587,622,659,698,740,784};

static int score;

void setup()
{
  pinMode(DATAPIN, OUTPUT);
  pinMode(LATCHPIN, OUTPUT);
  pinMode(CLOCKPIN, OUTPUT);

  score = 0;
}

void loop()
{
  // タクトスイッチが押されているときだけscoreをカウントアップする
  if(digitalRead(BUTTONPIN)==LOW) {
    score = score + 1;
    
    if (score > NUMOFLED) {
      // scoreがLEDの個数を超えたら0点に戻す
      // このときは音を鳴らさない
      score = 0;
    } else {
      tone(SPEAKERPIN, sounds[score-1], BEATTIME);
    }

    // 送信中はLATCHPINをLOWにする
    digitalWrite(LATCHPIN, LOW);
  
    // 点数分だけLEDが光る様にデータ送信
    for( int i = 0; i < NUMOFLED; i++ )
    {
      digitalWrite(DATAPIN, i < score?1:0);
      digitalWrite(CLOCKPIN, HIGH);
      digitalWrite(CLOCKPIN, LOW);
    }
  
    // 送信後はLATCHPINをHIGHに戻す
    digitalWrite(LATCHPIN, HIGH);
  
    // タクトスイッチが離されるまで待つ
    while(digitalRead(BUTTONPIN)==LOW) {}
  }
}

これで、欽ちゃんの仮装大賞っぽい雰囲気を再現できました。あとは自分のこだわりを盛り込んで行くと良いでしょう。

参考までに、下記の処理を追加したプログラムも載せておきます。(動画の状態のもの)

  1. 一定時間経っても点数が上がらなかった場合には0点に戻る。
  2. 満点になったときに特別なサウンドを流す。

参考プログラム

変更内容の説明は省略します。
タイマー処理のため、MsTimer2というライブラリーを追加しています。

#include <MsTimer2.h>

#define NUMOFLED   16 // LEDの数
#define BEATTIME  120 //音を出している時間(msec)
#define TIMEOVER_MSEC 3000 // スコアリセットまでの制限時間

#define DATAPIN     9 // 74HC595のDSへ
#define LATCHPIN   11 // 74HC595のST_CPへ
#define CLOCKPIN   12 // 74HC595のSH_CPへ
#define BUTTONPIN   2 // タクトスイッチへ
#define SPEAKERPIN 13 //スピーカーの出力ピン番号

//20個分の音程(今回使用するのは16個まで)
int sounds[] = {262,277,294,311,330,349,370,392,415,440,466,494,523,554,587,622,659,698,740,784};

static int score;

// 一定時間スコアが増えなければスコアをリセットする
void resetScore() {
  MsTimer2::stop();

  tone(SPEAKERPIN,100, BEATTIME * 2) ;
  delay(BEATTIME * 2) ;

  score = 0;
  //LEDをリセット
  sendData();

}

// LEDの点灯内容を送信する
void sendData() {
  // 送信中はLATCHPINをLOWにする
  digitalWrite(LATCHPIN, LOW);

  // 点数分だけLEDが光る様にデータ送信
  for( int i = 0; i < NUMOFLED; i++ )
  {
    digitalWrite(DATAPIN, i < score?1:0);
    digitalWrite(CLOCKPIN, HIGH);
    digitalWrite(CLOCKPIN, LOW);
  }

  // 送信後はLATCHPINをHIGHに戻す
  digitalWrite(LATCHPIN, HIGH);  
}

// 満点になったときのサウンドを鳴らす
void playFanfare() {
  delay(BEATTIME) ;
  tone(SPEAKERPIN, sounds[13]*2, BEATTIME) ;
  delay(BEATTIME) ;
  tone(SPEAKERPIN, sounds[14]*2, BEATTIME) ;
  delay(BEATTIME) ;
  tone(SPEAKERPIN, sounds[15]*2, BEATTIME) ;
  delay(BEATTIME) ;
  tone(SPEAKERPIN, sounds[13]*2, BEATTIME) ;
  delay(BEATTIME) ;
  tone(SPEAKERPIN, sounds[14]*2, BEATTIME) ;
  delay(BEATTIME) ;
  tone(SPEAKERPIN, sounds[15]*2, BEATTIME) ;
  delay(BEATTIME) ;
}

void setup()
{
  pinMode(DATAPIN, OUTPUT);
  pinMode(LATCHPIN, OUTPUT);
  pinMode(CLOCKPIN, OUTPUT);

  //起動音を鳴らす
  tone(SPEAKERPIN, 2000, BEATTIME) ;
  delay(BEATTIME) ;
  tone(SPEAKERPIN, 1000, BEATTIME) ;
  delay(BEATTIME) ;

  //一定時間でスコアをリセットするタイマーをセット(まだタイマーは開始しない)
  MsTimer2::set(TIMEOVER_MSEC, resetScore);

  score = 0;

  //LEDをリセット
  sendData();
}

void loop()
{
  // タクトスイッチが押されているときだけscoreをカウントアップする
  if(digitalRead(BUTTONPIN)==LOW) {
    // タクトスイッチが押されたらタイマーをリセットするために止める
    MsTimer2::stop();

    score = score + 1;

    if (score > NUMOFLED) {
      // scoreがLEDの個数を超えたら0点に戻す
      // このときは音を鳴らさない
      score = 0;
    } else {
      tone(SPEAKERPIN, sounds[score-1], BEATTIME);
    }

    //LEDの点灯を変更
    sendData();
    
    // 点数が上がって満点になったのならサウンドを鳴らす
    if (score == NUMOFLED) playFanfare();

    // タクトスイッチが離されるまで待つ
    while(digitalRead(BUTTONPIN)==LOW) {}
    
    // 点数が 1点以上満点未満 なら、ボタンが押されてから一定間隔後に点数リセットを呼び出す
    if(0 < score && score < NUMOFLED) MsTimer2::start();
  }
}

なお、欽ちゃんの仮装大賞パネルは、過去におもちゃが製品化されていたようですね。今はプレミア価格になっているようです。

created by Rinker
タカラトミー(TAKARA TOMY)