最終回 チャレンジ課題

基礎編パート1の最終回として、チャレンジ課題に挑戦してみたいと思います。

目次

プログラミングの習得?

基礎編パート1では、Arduino Microでキッチンタイマーを作りながらC++言語の基本的な文法を習得してきました。

プログラミング言語の文法を一通り覚えるだけで、すぐにプログラムが作れるようになる、というのはなかなか難しいです。

プログラミングの習得は、自分で作りたいものを考え、それを実現するプログラムを作成することを何度も繰り返すのが効果的です。

今回製作したキッチンタイマーで、自分で考えた機能を実現するスケッチをたくさん作ってみてください。


そこで基礎編パート1の最後として、チャレンジ課題を用意しました。

他の人が書いたプログラムを解読する、というのもとても参考になります。

課題の解答例も掲載していますので、参考にしてみてください。(あまり参考にならないかもしれませんが…)

チャレンジ課題

チャレンジ課題1(動作開始時にLEDとスピーカーのテストをする)

キッチンタイマーの回路はブレッドボード上に組み立てています。

ブレッドボードは部品を穴に差し込んでいるだけですので、何かにぶつけた時に部品が外れてしまうことがあります。

ブレッドボードで電子工作をするとこのようなことが起こりやすいので、部品の接続が悪い状態ではスピーカーがならなかったり、LEDが光らなかったりします。

そこで、Arduino Microが動作を開始したとき、全てのLEDとスピーカーが正常に動作するか、確認できるような動作を追加してみてください。

動作仕様もご自身で考えていただければと思います。

チャレンジ課題2(LEDの点滅パターンを途中で変える)

キッチンタイマーのスケッチは、スタートスイッチを押すとLEDが1秒間に1回点滅を開始します。

このLEDの点滅パターンを次のように変えてみてください。

  • スイッチを押してタイマーをスタートすると最初は青色LEDを2秒に1回点滅
  • 残り時間が少なくなったら1秒に1回点滅するように点滅パターンを変える

「残り時間が少なくなったら」というのは、例えばタイマー時間30秒前になったら、点滅パターンを変えるような感じです。

ところで、現在のスケッチではタイマー時間を計測するforの処理は、1秒に1回としています。

「2秒に1回LEDを点滅する」という処理はちょっと難しそうですよね。そこで、次のように考えてみてください。

2秒に1回点滅する考え方

タイマー時間計測をするために、1秒に1回、forの処理をする必要があります。つまり1秒に1回、必ずforの中の処理は実行されてしまいます。

そこで、forの中の処理では「残り時間が偶数であればLEDを点滅処理、奇数であれば何もしない」というように処理を分ける方法があります。

ある数値が偶数か奇数か判定するには、数値を2で割ったあまりが0か1か判断できます。例えば偶数の30を2で割るとあまりは0なので偶数、25を2で割るとあまりが1なので奇数になります。

数値を割ったあまりは%という演算子で計算することができます。

例えば、30を2で割った余りは30 % 2で計算できます。

この考え方で、残り時間が偶数か奇数かで処理を分けるには、次のようにifを使えばできそうです。

残り時間が偶数の時のみLEDを点滅処理をする、というようにすれば、2秒に1回LEDを点滅することができます。

// 残り時間が偶数か奇数かで処理を分ける
if (TIMER_JIKAN-count) % 2 == 0 ) {
  // 残り時間が偶数のとき処理される
} else {
  // 残り時間が奇数のとき処理される
}

チャレンジ課題3(タイマー時間を選択できるようにする)

キッチンタイマーにはスイッチが一つありますので、このスイッチを使用してタイマー時間を選択できるようにしてみようと思います。

具体的な動作仕様としては、次のようにしてみます。

  • Arduinoボードの動作開始時にスイッチが押されていない場合、3分タイマーに設定する
  • Arduinoボードの動作開始時にスイッチを押された状態の場合、1分タイマーに設定して、次の動作をする
    • タイマー時間が変更されたことを表現するために、スイッチが押されている間、青色LEDを点滅する(人はこの点滅を見て、タイマー時間が変更されたことを認識する)
    • スイッチが離されたら、現在のスケッチの動作と同様に、スタートボタンが押されたらタイマー計測を開始する

最後に

長いシリーズの記事でしたが、これで基礎編パート1は終わりです。本当にお疲れさまでした!

振り返るとずいぶん長い道のりでしたよね。

ここまで読まれた方は、とても勉強熱心な方だと思います!

今後も電子工作、プログラミングに限らず、世の中まだまだ興味深いことがたくさんあります。ぜひ、いろいろなことに興味を持って、さまざまなツール(道具)を身につけていってくださればと思います。

きっと人生がもっと楽しくなると思いますよ!

基礎編はパート2も用意しています。パート2では、今まで習得したC++言語の文法を使って、いろいろな動作を実現するスケッチを作成していきます。

ぜひパート1に懲りずに、パート2もお読みいただければと思います。

チャレンジ課題解答例

チャレンジ課題の解答例です。あくまでも一例として参考にしていただければと思います。

なお、音程の周波数を定義するヘッダファイルはすべてのスケッチに共通しますので、このセクションの最後に掲載しています。

チャレンジ課題1(動作開始時にLEDとスピーカーのテストをする)

動作開始時に、LEDを2回点滅、同時にスピーカーもLEDに同期して鳴らすようにしてみました。

また、この動作は処理が多くなりますのでled_speader_ckeckという名前の関数を作成してみました。

/*
  最終回 チャレンジ課題1
    動作開始時にLEDとスピーカーをテスト
*/

// 各音程の周波数定義読み込み
#include "onkai.h"

// 秒を表現するLED関連の定義
#define BYOU_LED 12  // 秒を表現するLEDの接続端子
#define BYOU_ON  50 // 秒を表現するLEDを点灯している時間(単位:ミリ秒)
#define BYOU_OFF (1000 - BYOU_ON) // 秒を表現するLEDを消している時間(単位:ミリ秒)

// 残り時間を表現するLED関連(緑色、黄色、赤色LED)
#define LED_MIDORI 8 // 緑色LEDの端子番号
#define LED_KIIRO  6 // 黄色LEDの端子番号
#define LED_AKA    4 // 赤色LEDの端子番号

// タイマースタートスイッチ関連の定義
#define SWITCH A5 // スイッチを接続している端子名
#define SWITCH_OFF 1 // スイッチOFFの時のdigitalReadの値
#define SWITCH_ON  0 // スイッチONの時のdigitalReadの値

// タイマー時間設定(LEDの点滅回数)
#define TIMER_JIKAN 30

// 残り時間を表現するLEDの制御時間
#define KIIRO_JIKAN 10  // 残り時間3分の2
#define AKA_JIKAN   20  // 残り時間3分の1

// アラーム音関連
#define SPEAKER A0  // スピーカーの端子番号
#define ALARM   880 // アラーム音の音程

// 音データの構造体
struct Note {
  uint16_t ontei;
  uint16_t length;
};

#define MELODY_LENGTH 13 // アラームメロディーの音の数
Note melody[] = {
  {DO_4, 200},
  {MI_4, 200},
  {SO_4, 400},
  {MI_4, 400},
  {DO_4, 200},
  {MI_4, 200},
  {SO_4, 400},
  {MI_4, 400},
  {DO_4, 200},
  {MI_4, 200},
  {SO_4, 400},
  {SI_4, 400},
  {DO_5, 800}};

// 残り時間の列挙体宣言
//            十分    半分以下  もうすぐ
enum Nokori {JYUUBUN, HANBUN, MOUSUGU};

// 残り時間表示用LEDの制御関数
//   引数: HIGHまたはLOW
//   返り値: なし
void controlTimeLed(Nokori nokori_jikan) {

  switch(nokori_jikan) {  // 残り時間に応じた処理
    case JYUUBUN:  // 残り時間十分
      digitalWrite(LED_MIDORI, HIGH);
      digitalWrite(LED_KIIRO,  LOW);
      digitalWrite(LED_AKA,    LOW);
      break;

    case HANBUN:  // 残り時間半分以下
      digitalWrite(LED_MIDORI, LOW);
      digitalWrite(LED_KIIRO,  HIGH);
      digitalWrite(LED_AKA,    LOW);
      break;

    case MOUSUGU:  // もうすぐ設定時刻
      digitalWrite(LED_MIDORI, LOW);
      digitalWrite(LED_KIIRO,  LOW);
      digitalWrite(LED_AKA,    HIGH);
      break;
  }

}

//
// 動作開始時のLEDとスピーカーの動作確認関数
//
void led_speader_ckeck(void) {

  // 「100msだけLEDを点灯してスピーカーを鳴らす」を2回繰り返す
  for(int i=0; i<2; i++) {
    //LEDを点灯
    digitalWrite(BYOU_LED,   HIGH);
    digitalWrite(LED_MIDORI, HIGH);
    digitalWrite(LED_KIIRO,  HIGH);
    digitalWrite(LED_AKA,    HIGH);
    //スピーカーを鳴らす
    tone(SPEAKER, ALARM);
    
    delay(100);  // 100msそのまま

    //LEDを消す
    digitalWrite(BYOU_LED,   LOW);
    digitalWrite(LED_MIDORI, LOW);
    digitalWrite(LED_KIIRO,  LOW);
    digitalWrite(LED_AKA,    LOW);
    //スピーカーを止める
    noTone(SPEAKER);

    delay(300);  // 300ms待つ
  }

}

void setup() {
  // 端子の設定
  // スピーカー接続端子はtoneの指示で自動的に出力設定になるので設定していません
  pinMode(BYOU_LED,   OUTPUT); // 秒表現のLED接続端子の出力設定
  pinMode(SWITCH,     INPUT_PULLUP); // スイッチ接続端子をプルアップ設定
  pinMode(LED_MIDORI, OUTPUT); // 緑色LED接続端子設定
  pinMode(LED_KIIRO,  OUTPUT); // 緑色LED接続端子設定
  pinMode(LED_AKA,    OUTPUT); // 緑色LED接続端子設定

  // LEDとスピーカーのテスト
  led_speader_ckeck();

  // 秒のLEDを点灯するまでちょっと時間をあける
  delay(500);

  // 秒のLED(青色LED)を点灯する
  digitalWrite(BYOU_LED, HIGH);

  // スイッチが押されるまで何もしないで待つ
  while( digitalRead(SWITCH) == SWITCH_OFF ) {
  }

  // タイマー開始時に残り時間十分の表示にする
  controlTimeLed(JYUUBUN);
}

void loop() {

  // TIMER_JIKAN分の回数を数える
  for(uint8_t count=0;  count<TIMER_JIKAN; count++) {

    // 残り時間が3分の2のとき、緑色をOFF、黄色をONにする
    if( count == KIIRO_JIKAN ) {
      controlTimeLed(HANBUN);
    }

    // 残り時間が3分の1のとき、黄色をOFF、赤色をONにする
    if( count == AKA_JIKAN ){
      controlTimeLed(MOUSUGU);
    }

    // 残り時間が5秒以下かそうでないかで処理を変える
    if( (TIMER_JIKAN-count) <= 5 ) {
      // 残り時間が5秒以下のときは、1秒に1回青色LEDを点滅すると同時に音を鳴らす
      digitalWrite(BYOU_LED, HIGH);
      tone(SPEAKER, ALARM);
      delay(BYOU_ON);
      digitalWrite(BYOU_LED, LOW);
      noTone(SPEAKER);
      delay(BYOU_OFF);
    } else {
      // そうでなければ、1秒に1回青色LEDを点滅する(音は鳴らさない)
      digitalWrite(BYOU_LED, HIGH);
      delay(BYOU_ON);
      digitalWrite(BYOU_LED, LOW);
      delay(BYOU_OFF);
    }
  
  }

  // 時間になったので秒のLED(青色LED)を点灯する
  digitalWrite(BYOU_LED, HIGH);

  // メロディーを3回演奏する
  for(uint8_t count=0; count<3; count++) {
  
    // 1回分のメロディー演奏
    for(uint8_t i=0; i<MELODY_LENGTH; i++ ) {
      tone(SPEAKER, melody[i].ontei);
      delay(melody[i].length);
    }
    // 音を消す
    noTone(SPEAKER);

    // メロディーを区切るために少し時間待ちをする
    delay(500);
  }

  // 何もしないで待つ
  while( true ) {
  }

}

チャレンジ課題2(LEDの点滅パターンを途中で変える)

チャレンジ課題1のスケッチにさらに処理を追加してみました。課題のところで説明したように、残り時間が偶数か奇数かで処理を分けるようにしてみました。

なお、既存のスケッチではすでに5秒以下かそうでないかでスピーカーを鳴らすか鳴らさないか処理を分けています。次のスケッチでは、その処理に加えて課題の内容を追加してみました。

ちょっと複雑ですね…

/*
  最終回 チャレンジ課題2
    残り時間に応じて点滅パターンを変える
      残り時間が一定時間より大きい: 2秒に1回点滅
      残り時間が一定時間以下: 1秒に1回点滅
*/

// 各音程の周波数定義読み込み
#include "onkai.h"

// 秒を表現するLED関連の定義
#define BYOU_LED 12  // 秒を表現するLEDの接続端子
#define BYOU_ON  50 // 秒を表現するLEDを点灯している時間(単位:ミリ秒)
#define BYOU_OFF (1000 - BYOU_ON) // 秒を表現するLEDを消している時間(単位:ミリ秒)

// 残り時間を表現するLED関連(緑色、黄色、赤色LED)
#define LED_MIDORI 8 // 緑色LEDの端子番号
#define LED_KIIRO  6 // 黄色LEDの端子番号
#define LED_AKA    4 // 赤色LEDの端子番号

// タイマースタートスイッチ関連の定義
#define SWITCH A5 // スイッチを接続している端子名
#define SWITCH_OFF 1 // スイッチOFFの時のdigitalReadの値
#define SWITCH_ON  0 // スイッチONの時のdigitalReadの値

// タイマー時間設定(LEDの点滅回数)
#define TIMER_JIKAN 30

// 点滅パターンを変えるタイミングの残り時間
#define CHANGE_PATTERN 10

// 残り時間を表現するLEDの制御時間
#define KIIRO_JIKAN 10  // 残り時間3分の2
#define AKA_JIKAN   20  // 残り時間3分の1

// アラーム音関連
#define SPEAKER A0  // スピーカーの端子番号
#define ALARM   880 // アラーム音の音程

// 音データの構造体
struct Note {
  uint16_t ontei;
  uint16_t length;
};

#define MELODY_LENGTH 13 // アラームメロディーの音の数
Note melody[] = {
  {DO_4, 200},
  {MI_4, 200},
  {SO_4, 400},
  {MI_4, 400},
  {DO_4, 200},
  {MI_4, 200},
  {SO_4, 400},
  {MI_4, 400},
  {DO_4, 200},
  {MI_4, 200},
  {SO_4, 400},
  {SI_4, 400},
  {DO_5, 800}};

// 残り時間の列挙体宣言
//            十分    半分以下  もうすぐ
enum Nokori {JYUUBUN, HANBUN, MOUSUGU};

// 残り時間表示用LEDの制御関数
//   引数: HIGHまたはLOW
//   返り値: なし
void controlTimeLed(Nokori nokori_jikan) {

  switch(nokori_jikan) {  // 残り時間に応じた処理
    case JYUUBUN:  // 残り時間十分
      digitalWrite(LED_MIDORI, HIGH);
      digitalWrite(LED_KIIRO,  LOW);
      digitalWrite(LED_AKA,    LOW);
      break;

    case HANBUN:  // 残り時間半分以下
      digitalWrite(LED_MIDORI, LOW);
      digitalWrite(LED_KIIRO,  HIGH);
      digitalWrite(LED_AKA,    LOW);
      break;

    case MOUSUGU:  // もうすぐ設定時刻
      digitalWrite(LED_MIDORI, LOW);
      digitalWrite(LED_KIIRO,  LOW);
      digitalWrite(LED_AKA,    HIGH);
      break;
  }

}

//
// 動作開始時のLEDとスピーカーの動作確認関数
//
void led_speader_ckeck(void) {

  // 「100msだけLEDを点灯してスピーカーを鳴らす」を2回繰り返す
  for(int i=0; i<2; i++) {
    //LEDを点灯
    digitalWrite(BYOU_LED,   HIGH);
    digitalWrite(LED_MIDORI, HIGH);
    digitalWrite(LED_KIIRO,  HIGH);
    digitalWrite(LED_AKA,    HIGH);
    //スピーカーを鳴らす
    tone(SPEAKER, ALARM);
    
    delay(100);  // 100msそのまま

    //LEDを消す
    digitalWrite(BYOU_LED,   LOW);
    digitalWrite(LED_MIDORI, LOW);
    digitalWrite(LED_KIIRO,  LOW);
    digitalWrite(LED_AKA,    LOW);
    //スピーカーを止める
    noTone(SPEAKER);

    delay(300);  // 300ms待つ
  }

}

void setup() {
  // 端子の設定
  // スピーカー接続端子はtoneの指示で自動的に出力設定になるので設定していません
  pinMode(BYOU_LED,   OUTPUT); // 秒表現のLED接続端子の出力設定
  pinMode(SWITCH,     INPUT_PULLUP); // スイッチ接続端子をプルアップ設定
  pinMode(LED_MIDORI, OUTPUT); // 緑色LED接続端子設定
  pinMode(LED_KIIRO,  OUTPUT); // 緑色LED接続端子設定
  pinMode(LED_AKA,    OUTPUT); // 緑色LED接続端子設定

  // LEDとスピーカーのテスト
  led_speader_ckeck();

  // 秒のLEDを点灯するまでちょっと時間をあける
  delay(500);

  // 秒のLED(青色LED)を点灯する
  digitalWrite(BYOU_LED, HIGH);

  // スイッチが押されるまで何もしないで待つ
  while( digitalRead(SWITCH) == SWITCH_OFF ) {
  }

  // タイマー開始時に残り時間十分の表示にする
  controlTimeLed(JYUUBUN);
}

void loop() {

  // TIMER_JIKAN分の回数を数える
  for(uint8_t count=0;  count<TIMER_JIKAN; count++) {

    // 残り時間が3分の2のとき、緑色をOFF、黄色をONにする
    if( count == KIIRO_JIKAN ) {
      controlTimeLed(HANBUN);
    }

    // 残り時間が3分の1のとき、黄色をOFF、赤色をONにする
    if( count == AKA_JIKAN ){
      controlTimeLed(MOUSUGU);
    }

    // 点滅パターンを変更する残り時間なのか判断する
    if( (TIMER_JIKAN-count) <= CHANGE_PATTERN ) {
      // 点滅パターンを変える時間の時の処理
      // ここではさらに残り時間が5秒以下かそうでないかで処理を変える
      if( (TIMER_JIKAN-count) <= 5 ) {
        // 残り時間が5秒以下のときは、1秒に1回青色LEDを点滅すると同時に音を鳴らす
        digitalWrite(BYOU_LED, HIGH);
        tone(SPEAKER, ALARM);
        delay(BYOU_ON);
        digitalWrite(BYOU_LED, LOW);
        noTone(SPEAKER);
        delay(BYOU_OFF);
      } else {
        // そうでなければ、1秒に1回青色LEDを点滅する(音は鳴らさない)
        digitalWrite(BYOU_LED, HIGH);
        delay(BYOU_ON);
        digitalWrite(BYOU_LED, LOW);
        delay(BYOU_OFF);
      }
    } else {
      // このブロックは2秒に1回LEDを点滅する
      // 考え方としては、残り時間が偶数のとき点滅、奇数の時は点滅なし、と判断する
      // 偶数か奇数か判定するには2で割った余りが0か1かで判断する
      // つまり、残り時間を2で割ったあまりが0の時のみ点滅処理をする
      // 残り時間が奇数の場合、1秒待つだけの処理をする
      if( (TIMER_JIKAN-count) % 2 == 0 ) {
        digitalWrite(BYOU_LED, HIGH);
        delay(BYOU_ON);
        digitalWrite(BYOU_LED, LOW);
        delay(BYOU_OFF);
      } else {
        delay(1000);
      }
    }
  
  }

  // 時間になったので秒のLED(青色LED)を点灯する
  digitalWrite(BYOU_LED, HIGH);

  // メロディーを3回演奏する
  for(uint8_t count=0; count<3; count++) {
  
    // 1回分のメロディー演奏
    for(uint8_t i=0; i<MELODY_LENGTH; i++ ) {
      tone(SPEAKER, melody[i].ontei);
      delay(melody[i].length);
    }
    // 音を消す
    noTone(SPEAKER);

    // メロディーを区切るために少し時間待ちをする
    delay(500);
  }

  // 何もしないで待つ
  while( true ) {
  }

}

チャレンジ課題3(タイマー時間を選択できるようにする)

Arduino動作開始時のスイッチ状態に応じて、2種類のタイマー時間を設定することになります。

現在のスケッチではタイマー時間は#defineで固定で定義していますので、固定値を定義することはできません。

そこで、setup関数の中でスイッチ状態を調べて、その状態に応じたタイマー時間を変数に代入しておくことにします。この変数は、setup関数で値を設定してloop関数で参照するため、グローバル変数にする必要あがあります。

また、タイマー時間が2種類ありますので、残り時間の判定も固定値ではできなくなります。

ということで、いろいろ変更点が出てきますね。

完成したスケッチを掲載しますが、このスケッチでは次のような内容の変更を加えています。

スケッチ変更内容
  • 2種類のタイマー時間を定義
    通常のタイマー時間をTIMER_JIKAN_NORMAL、短いタイマー時間をTIMER_JIKAN_SHORTという名前で#define定義しておきます(スケッチ28・29行目)
  • タイマー時間を保存する変数を用意
    スイッチ状態に応じてタイマー時間が変わりますので、タイマー時間はtimer_jikanという変数に代入しておくことにします。この変数はsetup関数とloop関数で使用するためグローバル変数として宣言します(スケッチ41行目)
  • スイッチ状態に応じてタイマー時間を設定
    LEDとスピーカーの動作確認テスト終了後、スイッチの状態を調べてタイマー時間を次のように設定します。(スケッチ144〜158行目)
     ・スイッチOFF:通常のタイマー時間を設定 timer_jikan = TIMER_JIKAN_NORMAL;
     ・スイッチON:短いタイマー時間を設定 timer_jikan = TIMER_JIKAN_SHORT;
      さらに、スイッチがOFFになるまでLEDを点滅
  • 残り時間LEDの判定時間変更
    タイマー時間が2種類になりすまので、残り時間の判定時間はタイマー時間に応じて変わります。そこで、timer_jikanから計算するようにしました(スケッチ178・183行目)
/*
  最終回 チャレンジ課題3
      タイマー時間を選択できるようにする
      Arduino動作開始時にスイッチが...
        押されていない: 3分タイマー
        押されている: 1分タイマー
      A
*/

// 各音程の周波数定義読み込み
#include "onkai.h"

// 秒を表現するLED関連の定義
#define BYOU_LED 12  // 秒を表現するLEDの接続端子
#define BYOU_ON  50 // 秒を表現するLEDを点灯している時間(単位:ミリ秒)
#define BYOU_OFF (1000 - BYOU_ON) // 秒を表現するLEDを消している時間(単位:ミリ秒)

// 残り時間を表現するLED関連(緑色、黄色、赤色LED)
#define LED_MIDORI 8 // 緑色LEDの端子番号
#define LED_KIIRO  6 // 黄色LEDの端子番号
#define LED_AKA    4 // 赤色LEDの端子番号

// タイマースタートスイッチ関連の定義
#define SWITCH A5 // スイッチを接続している端子名
#define SWITCH_OFF 1 // スイッチOFFの時のdigitalReadの値
#define SWITCH_ON  0 // スイッチONの時のdigitalReadの値

// タイマー時間設定(LEDの点滅回数)
#define TIMER_JIKAN_NORMAL 180  // 通常の設定時間(3分)
#define TIMER_JIKAN_SHORT  60   // スイッチが押されている時の設定時間(1分)

// 点滅パターンを変えるタイミングの残り時間
#define CHANGE_PATTERN 30

// 残り時間を表現するLEDの制御時間
// タイマー時間が2種類あるため、スケッチ内でそれぞれの残り時間を計算するように変更
// そのため、次の定義は必要なくなったのでコメント
// #define KIIRO_JIKAN 10  // 残り時間3分の2
// #define AKA_JIKAN   20  // 残り時間3分の1

// 設定時間を保存するグローバル変数
uint16_t timer_jikan;

// アラーム音関連
#define SPEAKER A0  // スピーカーの端子番号
#define ALARM   880 // アラーム音の音程

// 音データの構造体
struct Note {
  uint16_t ontei;
  uint16_t length;
};

#define MELODY_LENGTH 13 // アラームメロディーの音の数
Note melody[] = {
  {DO_4, 200},
  {MI_4, 200},
  {SO_4, 400},
  {MI_4, 400},
  {DO_4, 200},
  {MI_4, 200},
  {SO_4, 400},
  {MI_4, 400},
  {DO_4, 200},
  {MI_4, 200},
  {SO_4, 400},
  {SI_4, 400},
  {DO_5, 800}};

// 残り時間の列挙体宣言
//            十分    半分以下  もうすぐ
enum Nokori {JYUUBUN, HANBUN, MOUSUGU};

// 残り時間表示用LEDの制御関数
//   引数: HIGHまたはLOW
//   返り値: なし
void controlTimeLed(Nokori nokori_jikan) {

  switch(nokori_jikan) {  // 残り時間に応じた処理
    case JYUUBUN:  // 残り時間十分
      digitalWrite(LED_MIDORI, HIGH);
      digitalWrite(LED_KIIRO,  LOW);
      digitalWrite(LED_AKA,    LOW);
      break;

    case HANBUN:  // 残り時間半分以下
      digitalWrite(LED_MIDORI, LOW);
      digitalWrite(LED_KIIRO,  HIGH);
      digitalWrite(LED_AKA,    LOW);
      break;

    case MOUSUGU:  // もうすぐ設定時刻
      digitalWrite(LED_MIDORI, LOW);
      digitalWrite(LED_KIIRO,  LOW);
      digitalWrite(LED_AKA,    HIGH);
      break;
  }

}

//
// 動作開始時のLEDとスピーカーの動作確認関数
//
void led_speader_ckeck(void) {

  // 「100msだけLEDを点灯してスピーカーを鳴らす」を2回繰り返す
  for(int i=0; i<2; i++) {
    //LEDを点灯
    digitalWrite(BYOU_LED,   HIGH);
    digitalWrite(LED_MIDORI, HIGH);
    digitalWrite(LED_KIIRO,  HIGH);
    digitalWrite(LED_AKA,    HIGH);
    //スピーカーを鳴らす
    tone(SPEAKER, ALARM);
    
    delay(100);  // 100msそのまま

    //LEDを消す
    digitalWrite(BYOU_LED,   LOW);
    digitalWrite(LED_MIDORI, LOW);
    digitalWrite(LED_KIIRO,  LOW);
    digitalWrite(LED_AKA,    LOW);
    //スピーカーを止める
    noTone(SPEAKER);

    delay(300);  // 300ms待つ
  }

}

void setup() {
  // 端子の設定
  // スピーカー接続端子はtoneの指示で自動的に出力設定になるので設定していません
  pinMode(BYOU_LED,   OUTPUT); // 秒表現のLED接続端子の出力設定
  pinMode(SWITCH,     INPUT_PULLUP); // スイッチ接続端子をプルアップ設定
  pinMode(LED_MIDORI, OUTPUT); // 緑色LED接続端子設定
  pinMode(LED_KIIRO,  OUTPUT); // 緑色LED接続端子設定
  pinMode(LED_AKA,    OUTPUT); // 緑色LED接続端子設定

  // LEDとスピーカーのテスト
  led_speader_ckeck();

  // スイッチの状態に応じてタイマー設定時間を変える
  if( digitalRead(SWITCH) == SWITCH_OFF ) {
    // スイッチがOFFの時は通常のタイマー時間に設定する
    // この後の動作は通常通り
    timer_jikan = TIMER_JIKAN_NORMAL;
  } else {
    // スイッチがONの場合は、短いタイマー時間に設定する
    // さらに設定時間が変更になったことを表現するために、スイッチがONの間、LEDの点滅を続ける
    timer_jikan = TIMER_JIKAN_SHORT;
    while( digitalRead(SWITCH) == SWITCH_ON ) {
      digitalWrite(BYOU_LED, HIGH);
      delay(300);
      digitalWrite(BYOU_LED,LOW);
      delay(300);
    }
  }


  // 秒のLED(青色LED)を点灯する
  digitalWrite(BYOU_LED, HIGH);

  // スイッチが押されるまで何もしないで待つ
  while( digitalRead(SWITCH) == SWITCH_OFF ) {
  }

  // タイマー開始時に残り時間十分の表示にする
  controlTimeLed(JYUUBUN);
}

void loop() {

  // TIMER_JIKAN分の回数を数える
  for(uint16_t count=0;  count<timer_jikan; count++) {

    // 残り時間が3分の2、つまり経過時間が3分の1のとき、緑色をOFF、黄色をONにする
    if( count == timer_jikan/3 ) {
      controlTimeLed(HANBUN);
    }

    // 残り時間が3分の1、つまり経過時間が3分の2のとき、黄色をOFF、赤色をONにする
    if( count == timer_jikan*2/3 ) {
      controlTimeLed(MOUSUGU);
    }

    // 点滅パターンを変更する残り時間なのか判断する
    if( (timer_jikan-count) <= CHANGE_PATTERN ) {
      // 点滅パターンを変える時間の時の処理
      // ここではさらに残り時間が5秒以下かそうでないかで処理を変える
      if( (timer_jikan-count) <= 5 ) {
        // 残り時間が5秒以下のときは、1秒に1回青色LEDを点滅すると同時に音を鳴らす
        digitalWrite(BYOU_LED, HIGH);
        tone(SPEAKER, ALARM);
        delay(BYOU_ON);
        digitalWrite(BYOU_LED, LOW);
        noTone(SPEAKER);
        delay(BYOU_OFF);
      } else {
        // そうでなければ、1秒に1回青色LEDを点滅する(音は鳴らさない)
        digitalWrite(BYOU_LED, HIGH);
        delay(BYOU_ON);
        digitalWrite(BYOU_LED, LOW);
        delay(BYOU_OFF);
      }
    } else {
      // このブロックは2秒に1回LEDを点滅する
      // 考え方としては、残り時間が偶数のとき点滅、奇数の時は点滅なし、と判断する
      // 偶数か奇数か判定するには2で割った余りが0か1かで判断する
      // つまり、残り時間を2で割ったあまりが0の時のみ点滅処理をする
      // 残り時間が奇数の場合、1秒待つだけの処理をする
      if( (timer_jikan-count) % 2 == 0 ) {
        digitalWrite(BYOU_LED, HIGH);
        delay(BYOU_ON);
        digitalWrite(BYOU_LED, LOW);
        delay(BYOU_OFF);
      } else {
        delay(1000);
      }
    }
  
  }

  // 時間になったので秒のLED(青色LED)を点灯する
  digitalWrite(BYOU_LED, HIGH);

  // メロディーを3回演奏する
  for(uint8_t count=0; count<3; count++) {
  
    // 1回分のメロディー演奏
    for(uint8_t i=0; i<MELODY_LENGTH; i++ ) {
      tone(SPEAKER, melody[i].ontei);
      delay(melody[i].length);
    }
    // 音を消す
    noTone(SPEAKER);

    // メロディーを区切るために少し時間待ちをする
    delay(500);
  }

  // 何もしないで待つ
  while( true ) {
  }

}

各音程の周波数を定義したヘッダファイル(課題1〜3共通)

// メロディー演奏用の音の周波数
#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

更新履歴

日付内容
2020.2.1新規投稿
2021.8.27新サイトデザイン対応
2022.2.1一部記述内容変更。チャレンジ課題の内容には変更なし
2022.2.18チャレンジ課題の内容一部変更
2025.1.25チャレンジ課題内容一部変更、解答例追加
通知の設定
通知タイミング
guest
0 コメント
新しい準
古い順 一番投票が多い
本文中にフィードバック
全てのコメントを見る
目次