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);
}