Arduino入門 第8回 メロディーアラーム

目次

YouTube動画

動画内で作成したスケッチ

音程の周波数定義と本体のスケッチを分けて、#includeを使用したスケッチです。

今回のファイルは2つに分かれています。最初は音程の周波数を定義したヘッダーファイル、次は本体のスケッチファイルです。

音程周波数定義のヘッダーファイル

/* 
 * 音程ごとの周波数
*/

#define DO_3  131
#define DOS_3 139
#define RE_3  147
#define RES_3 156
#define MI_3  165
#define FA_3  175
#define FAS_3 185
#define SO_3  196
#define SOS_3 208
#define RA_3  220
#define RAS_3 233
#define SI_3  247
#define DO_4  262
#define DOS_4 277
#define RE_4  294
#define RES_4 311
#define MI_4  330
#define FA_4  349
#define FAS_4 370
#define SO_4  392
#define SOS_4 415
#define RA_4  440
#define RAS_4 466
#define SI_4  494
#define DO_5  523
#define DOS_5 554
#define RE_5  587
#define RES_5 622
#define MI_5  659
#define FA_5  698
#define FAS_5 740
#define SO_5  784
#define SOS_5 831
#define RA_5  880
#define RAS_5 932
#define SI_5  988
#define DO_6 1047

本体スケッチファイル

 /*
  Arduino入門 第8回 メロディーアラーム
    アラームをメロディーに変更
*/

#include "pitch.h" // 音程周波数定義ファイル

#define TIMER 10      // タイマー時間(秒)
#define HALF_TIMER 5 // 「残り時間が半分」の時間(秒)
#define SOON_TIMER 3  // 「もうすぐ」の時間(秒)

#define SW_PIN A5 // スイッチ接続端子
#define SW_OFF 1  // スイッチOFFのdigitalReadの値

#define LED_PIN 12 // 1秒ごとに点滅するLEDの接続端子
#define LED_ON_INTERVAL   50 // LEDを点灯する時間
#define LED_OFF_INTERVAL 950 // LEDを消灯する時間

#define FULL_PIN 8 // 「残り時間十分」LEDの接続端子
#define HALF_PIN 6 // 「残り時間半分」LEDの接続端子
#define SOON_PIN 4 // 「もうすぐ」LEDの接続端子

#define SPEAKER_PIN A0  // スピーカー接続端子
#define ALARM_TONE 1760 // アラーム音の周波数


void setup() {
  // LED接続端子を出力制御にする
  pinMode(LED_PIN,  OUTPUT);
  pinMode(FULL_PIN, OUTPUT);
  pinMode(HALF_PIN, OUTPUT);
  pinMode(SOON_PIN, OUTPUT);

  // スイッチ接続端子をプルアップ機能有効にする
  pinMode(SW_PIN, INPUT_PULLUP);
}

void loop() {
  // スイッチが押されるまで待つ
  while( digitalRead(SW_PIN) == SW_OFF ) {
  }

  // 「残り時間十分」LEDをON
  digitalWrite(FULL_PIN, HIGH);

  // 指定した時間、LEDを点滅する
  // 処理中は残り時間に応じてLEDの点灯状態を変える
  for(int i=0; i<TIMER; i++) {
    // 残り時間を計算
    int nokori = TIMER - i;

    // 残り時間が半分の時のLED制御
    if(nokori == HALF_TIMER){
      digitalWrite(FULL_PIN, LOW);  // 「残り時間十分」LEDをOFF
      digitalWrite(HALF_PIN, HIGH); // 「残り時間半分」LEDをON
    }

    // もうすぐの時のLED制御
    if(nokori == SOON_TIMER) {
      digitalWrite(HALF_PIN, LOW);  // 「残り時間半分」LEDをOFF
      digitalWrite(SOON_PIN, HIGH); // 「もうすぐ」LEDをON
    }

    // LEDをピカッと1秒間で点滅
    digitalWrite(LED_PIN, HIGH);
    delay(LED_ON_INTERVAL);
    digitalWrite(LED_PIN, LOW);
    delay(LED_OFF_INTERVAL);
  }

  // メロディーを鳴らす
  
  // メロディーの配列
  int melody[]={DO_4, RE_4, MI_4, RE_4, DO_4};

  // メロディー再生
  for(int i=0; i<5; i++) {
    tone(SPEAKER_PIN, melody[i]);
    delay(500);
  }

  // 音を止める
  noTone(SPEAKER_PIN);

  // LEDを消灯
  digitalWrite(SOON_PIN, LOW);
}

要点まとめ

ヘッダーファイル

スケッチの先頭部分に#defineなどで準備をしていますが、この部分の行数が多くなることがよくあります。この先頭部分を「ヘッダー」と呼んでいます。

ヘッダー部分の行数が多くなると、肝心のスケッチの処理部分であるsetupやloopがスケッチのかなり下の方にいってしまいます。

そこで、C++では#includeというヘッダー部分に他のファイルの内容を読み込む機能があります。#includeは次のように書きます。

このように書くと、#includeと書いたところに、指定したファイルの内容を読み込んでくれます。

配列

同じようなデータをまとめて扱うために、C++では「配列」というものが用意されています。

配列は変数を束ねたようなものです。今までは変数を「メモ用紙」に例えて説明しました。これに対して、配列は「メモ帳」のようなものになります。ちょうど「メモ用紙(変数)」を束ねたようなものと考えられます。

配列をメモ帳に例えると、次のような特徴があります。

  • 変数と同様にサイズがある
  • Arduinoに用意してもらうとき、「サイズ」「タイトル」に加えて「枚数」を指定する
  • 読み書きする場合は「タイトル」と「ページ番号」を指定する
  • 用紙してもらったメモ帳の各ページには適当な数字が書かれている

次に、配列の基本的な扱いをまとめます。

配列の宣言

Arduinoに配列を用意してもらうことを「配列の宣言」と呼んでいます。配列は次のように宣言します。

配列の代入

配列に値を代入をする場合、各要素に対して変数と同じ扱いをします。

配列の操作

配列の各要素は、変数と同じように比較や計算などができます。

配列の初期化

配列を宣言した場合、各要素に適当な数値が代入された状態になります。

そこで、特定の値で配列を準備したい場合、次のように配列を初期化して宣言することができます。

チャレンジ課題解答例

チャレンジ課題❶の解答例

メロディーの音の長さを個別に変えられるようにします。

現在のスケッチでは次のようなスケッチで、音程の配列 melody[ ] を用意して、for文繰り返し処理でメロディーを再生しています。

// メロディー再生
for(int i=0; i<5; i++) {
  tone(SPEAKER_PIN, melody[i]);
  delay(500);
}

音の長さも毎回個別に変更する場合、音程と同じように配列を用意して指定すれば実現できます。

例えば、durationという名前の配列(durationは「長さ」の意味)を、音のデータ分、用意します。

int duration[5];

この配列宣言だけの場合、それぞれの要素は適当な数字になっていますので、それぞれの音の長さで初期化しておきます。音の長さは「ド〜レミ〜レド〜」と、短音と長い音がありますが、短い音は500ms、長い音は1000msにしてみます。

int duration[] = {1000, 500, 1000, 500, 1000};

これでそれぞれの音の長さの配列も準備できました。

次のようにdelayの引数に指定すると、それぞれの音の長さを個別に指定できるようになります。

// メロディー配列準備(それぞれの音程と音の長さ)
int melody[] =   {DO_4, RE_4, MI_4, RE_4, DO_4};
int duration[] = {1000,  500, 1000,  500, 1000};

// メロディー再生
for(int i=0; i<5; i++) {
  tone(SPEAKER_PIN, melody[i]);
  delay( duration[i] );
}

ところで上のスケッチの場合、音の長さの指定で500や1000と入力するのは効率が悪いので、500を1単位として、次のように指定することにします。

int duration[] = {2, 1, 2, 1, 2};

その代わりに、delayの引数で1が500になるように配列に500を掛けます。

delay( duration[i]*500 );

このような考え方で、次のようなスケッチにしてみました。ヘッダーファイル「pitch.h」は変更ありません。

 /*
  Arduino入門 第8回 メロディーアラーム
    チャレンジ課題(1)解答例
*/

#include "pitch.h" // 音程周波数定義ファイル

#define TIMER 10      // タイマー時間(秒)
#define HALF_TIMER 5 // 「残り時間が半分」の時間(秒)
#define SOON_TIMER 3  // 「もうすぐ」の時間(秒)

#define SW_PIN A5 // スイッチ接続端子
#define SW_OFF 1  // スイッチOFFのdigitalReadの値

#define LED_PIN 12 // 1秒ごとに点滅するLEDの接続端子
#define LED_ON_INTERVAL   50 // LEDを点灯する時間
#define LED_OFF_INTERVAL 950 // LEDを消灯する時間

#define FULL_PIN 8 // 「残り時間十分」LEDの接続端子
#define HALF_PIN 6 // 「残り時間半分」LEDの接続端子
#define SOON_PIN 4 // 「もうすぐ」LEDの接続端子

#define SPEAKER_PIN A0  // スピーカー接続端子
#define ALARM_TONE 1760 // アラーム音の周波数


void setup() {
  // LED接続端子を出力制御にする
  pinMode(LED_PIN,  OUTPUT);
  pinMode(FULL_PIN, OUTPUT);
  pinMode(HALF_PIN, OUTPUT);
  pinMode(SOON_PIN, OUTPUT);

  // スイッチ接続端子をプルアップ機能有効にする
  pinMode(SW_PIN, INPUT_PULLUP);
}

void loop() {
  // スイッチが押されるまで待つ
  while( digitalRead(SW_PIN) == SW_OFF ) {
  }

  // 「残り時間十分」LEDをON
  digitalWrite(FULL_PIN, HIGH);

  // 指定した時間、LEDを点滅する
  // 処理中は残り時間に応じてLEDの点灯状態を変える
  for(int i=0; i<TIMER; i++) {
    // 残り時間を計算
    int nokori = TIMER - i;

    // 残り時間が半分の時のLED制御
    if(nokori == HALF_TIMER){
      digitalWrite(FULL_PIN, LOW);  // 「残り時間十分」LEDをOFF
      digitalWrite(HALF_PIN, HIGH); // 「残り時間半分」LEDをON
    }

    // もうすぐの時のLED制御
    if(nokori == SOON_TIMER) {
      digitalWrite(HALF_PIN, LOW);  // 「残り時間半分」LEDをOFF
      digitalWrite(SOON_PIN, HIGH); // 「もうすぐ」LEDをON
    }

    // LEDをピカッと1秒間で点滅
    digitalWrite(LED_PIN, HIGH);
    delay(LED_ON_INTERVAL);
    digitalWrite(LED_PIN, LOW);
    delay(LED_OFF_INTERVAL);
  }

  // メロディーを鳴らす
  
  // メロディーの配列
  int melody[] =   {DO_4, RE_4, MI_4, RE_4, DO_4};
  int duration[] = {   2,    1,    2,    1,    2};

  // メロディー再生
  for(int i=0; i<5; i++) {
    tone(SPEAKER_PIN, melody[i]);
    delay( duration[i]*500 );
  }

  // 音を止める
  noTone(SPEAKER_PIN);

  // LEDを消灯
  digitalWrite(SOON_PIN, LOW);
}

チャレンジ課題❷の解答例

さらに休符に対応します。

メロディーの音程データはすでに次のように準備しています。

int melody[] =   {DO_4, RE_4, MI_4, MI_4, RE_4, DO_4};

この配列で休符を表現したいので、無理やり休符の概念の導入をしてみます。

このデータは音の周波数のデータです。振動しないという音は無音ですので、周波数が0Hzを休符と決めることにします。

上の配列で、「ドレミ」の後に休符を入れたい場合、次のような配列を用意します。

int melody[] =   {DO_4, RE_4, MI_4, 0, MI_4, RE_4, DO_4};

すると、melody配列の要素が0の場合は休符、それ以外は音のデータ、ということになります。

あとは次のようにif文を使ってmelody配列の要素が0かどうかで処理を分ければ実現できます。

// メロディー再生
for(int i=0; i<7; i++) {
  // melody[i]の値を調べて、0であれば休符、そうでなければ音を出す処理をする
  if( melody[i] == 0 ) {
    // この処理はmelody[i]がゼロの場合なので、
    // 休符の処理をする
  } else {
    // この処理はmelody[i]がゼロ以外、つまり音のデータの場合なので
    // 音を出す処理をする
  }
}

このような考え方のもと、スケッチは次のようにしてみました。なお、休符の0はわかりやすいように#defineでRESTという名前にして、pitch.hで定義を追加しました。

以下はヘッダーファイルです。

/* 
 * 音程ごとの周波数
*/

// 休符
#define REST 0

// 各音程の周波数
#define DO_3  131
#define DOS_3 139
#define RE_3  147
#define RES_3 156
#define MI_3  165
#define FA_3  175
#define FAS_3 185
#define SO_3  196
#define SOS_3 208
#define RA_3  220
#define RAS_3 233
#define SI_3  247
#define DO_4  262
#define DOS_4 277
#define RE_4  294
#define RES_4 311
#define MI_4  330
#define FA_4  349
#define FAS_4 370
#define SO_4  392
#define SOS_4 415
#define RA_4  440
#define RAS_4 466
#define SI_4  494
#define DO_5  523
#define DOS_5 554
#define RE_5  587
#define RES_5 622
#define MI_5  659
#define FA_5  698
#define FAS_5 740
#define SO_5  784
#define SOS_5 831
#define RA_5  880
#define RAS_5 932
#define SI_5  988

以下は本体のスケッチファイルです。

 /*
  Arduino入門 第8回 メロディーアラーム
    チャレンジ課題(2)解答例
*/

#include "pitch.h" // 音程周波数定義ファイル

#define TIMER 10      // タイマー時間(秒)
#define HALF_TIMER 5 // 「残り時間が半分」の時間(秒)
#define SOON_TIMER 3  // 「もうすぐ」の時間(秒)

#define SW_PIN A5 // スイッチ接続端子
#define SW_OFF 1  // スイッチOFFのdigitalReadの値

#define LED_PIN 12 // 1秒ごとに点滅するLEDの接続端子
#define LED_ON_INTERVAL   50 // LEDを点灯する時間
#define LED_OFF_INTERVAL 950 // LEDを消灯する時間

#define FULL_PIN 8 // 「残り時間十分」LEDの接続端子
#define HALF_PIN 6 // 「残り時間半分」LEDの接続端子
#define SOON_PIN 4 // 「もうすぐ」LEDの接続端子

#define SPEAKER_PIN A0  // スピーカー接続端子
#define ALARM_TONE 1760 // アラーム音の周波数


void setup() {
  // LED接続端子を出力制御にする
  pinMode(LED_PIN,  OUTPUT);
  pinMode(FULL_PIN, OUTPUT);
  pinMode(HALF_PIN, OUTPUT);
  pinMode(SOON_PIN, OUTPUT);

  // スイッチ接続端子をプルアップ機能有効にする
  pinMode(SW_PIN, INPUT_PULLUP);


}

void loop() {
  // スイッチが押されるまで待つ
  while( digitalRead(SW_PIN) == SW_OFF ) {
  }

  // 「残り時間十分」LEDをON
  digitalWrite(FULL_PIN, HIGH);

  // 指定した時間、LEDを点滅する
  // 処理中は残り時間に応じてLEDの点灯状態を変える
  for(int i=0; i<TIMER; i++) {
    // 残り時間を計算
    int nokori = TIMER - i;

    // 残り時間が半分の時のLED制御
    if(nokori == HALF_TIMER){
      digitalWrite(FULL_PIN, LOW);  // 「残り時間十分」LEDをOFF
      digitalWrite(HALF_PIN, HIGH); // 「残り時間半分」LEDをON
    }

    // もうすぐの時のLED制御
    if(nokori == SOON_TIMER) {
      digitalWrite(HALF_PIN, LOW);  // 「残り時間半分」LEDをOFF
      digitalWrite(SOON_PIN, HIGH); // 「もうすぐ」LEDをON
    }

    // LEDをピカッと1秒間で点滅
    digitalWrite(LED_PIN, HIGH);
    delay(LED_ON_INTERVAL);
    digitalWrite(LED_PIN, LOW);
    delay(LED_OFF_INTERVAL);
  }

  // メロディーを鳴らす
  
  // メロディーの配列
  int melody[] =   {DO_4, RE_4, MI_4, REST, MI_4, RE_4, DO_4};
  int duration[] = {   2,    1,    2,    2,    2,    1,    2};

  // メロディー再生
  for(int i=0; i<7; i++) {
    // melody[i]の値がREST(=0)の場合、音を止めて指定時間待つ
    // それ以外の場合は、通常通りtoneで音を出す
    if( melody[i] == REST ) {
      noTone(SPEAKER_PIN);
      delay( duration[i]*500 );
    } else {
      tone(SPEAKER_PIN, melody[i]);
      delay( duration[i]*500 );
    }
  }

  // 音を止める
  noTone(SPEAKER_PIN);

  // LEDを消灯
  digitalWrite(SOON_PIN, LOW);
}
通知の設定
通知タイミング
guest
0 コメント
本文中にフィードバック
全てのコメントを見る
目次