見出し画像

プログラムと電子工作・Lチカ+ボタン

「Lチカ」の状態をボタンで制御します。「ステートマシン」の使用方法を覚えましょう。


目標

  • ブレッドボード上に LED を外付けする回路を配線します。

  • 上ボタン(押しボタンA)を押すごとに、M5StickC Plus に外付けした LED の状態を消灯、点灯、点滅と変化させます。

  • 液晶ディスプレイに LED のの状態をそれぞれ「LED OFF」/「LED ON」/「LED BLINK」と表示します。

部品・機材

使用する部品は次のとおりです。「Lチカ」と全く同じです。

[]内は商品の例です。秋月電子通商の記号は「通販コード」です。
抵抗器は様々な値のものがセットになっている商品を買っておくと、今後の電子工作のときに便利でしょう。

電子部品

  • M5StickC Plus 1台

  • LED 1個[例:秋月電子通商 I-05224]

  • 抵抗器 1個[例:Amazon OSOYOO 金属皮膜抵抗器 抵抗セット]

  • ジャンプワイヤ 複数本[例:秋月電子通商 C-05159]

  • ブレッドボード 1台[例:秋月電子通商 P-05294]

開発用機材

  • PC(Windows10 または 11)、開発環境 Arduino-IDE 導入ずみ

  • USB-A・USB-C ケーブル

開発手順

  1. 回路図(図1)にしたがって、ブレッドボード上に回路を作る。
    抵抗器の値は、150~270Ωのいずれか。

  2. PC と M5StickC Plus を USBケーブルで接続する。

  3. Arduino-IDE でスケッチ mysketch-B.ino を開く

  4. 検証・コンパイルする。

  5. M5StickC Plus に書き込む。

図1 回路図
図2 ブレッドボート、LED の配線

状態遷移

状態遷移を表にして整理します。スケッチの中にコメントで埋め込んでおけば、状態遷移とコードの不一致が防げると思います。

NOW は現在の状態、EVENT は発生するイベント、条件、OPERATION はイベント、条件が発生したときに実行する処理、NEXT は遷移する次の状態です。

/*
 *  状態遷移:
 *  ===========================================================================
 *  NOW     EVENT                           OPERATION                   NEXT
 *  ---------------------------------------------------------------------------
 *  電源ON  --                              LEDをOFFする                OFF
 *  ---------------------------------------------------------------------------
 *  OFF     上ボタンが押された              LEDをONする                 ON
 *  ---------------------------------------------------------------------------
 *  ON      上ボタンが押された              LEDをOFFする                BLINK
 *                                          滅状態にする
 *                                          滅時間を設定する
 *                                          現在時刻を記憶する
 *  ---------------------------------------------------------------------------
 *  BLINK   上ボタンが押された              LEDをOFFする                OFF
 *          -------------------------------------------------------------------
 *          上ボタンが押されていない &
 *          点滅時間が過ぎた &
 *              滅状態                      LEDをONする                 BLINK
 *                                          点状態にする
 *                                          点時間を設定する
 *                                          現在時刻を記憶する
 *              ---------------------------------------------------------------
 *              点状態                      LEDをOFFする                BLINK
 *                                          滅状態にする
 *                                          滅時間を設定する
 *                                          現在時刻を記憶する
 *  ===========================================================================
 */

状態遷移が矛盾なく書けていれば、このとおりにコーディングすると期待どおりに動作するはずです。ちょっと冗長なコードになるのは、分かりやすさを優先してよしとします。

スケッチ

mysketch-C.ino

#include <M5StickCPlus.h>

#define LEDPIN GPIO_NUM_26  /*LEDはG26ピンに接続する*/
#define ON_TIME  200        /*点灯時間(ms)*/
#define OFF_TIME 1000       /*消灯時間(ms)*/
#define LED_ON HIGH         /*LED点灯のロジック*/
#define LED_OFF LOW         /*LED消灯のロジック*/

enum class LED_STATE {OFF, ON, BLINK};
LED_STATE led_state = LED_STATE::OFF;

bool led_on_f;              /*LED ON フラグ:true ON、false OFF*/
uint32_t led_chg_time;      /*LED ON/OFFを切り替えた時刻(ms)*/
uint32_t led_period;        /*LED ON/OFF時間(ms)*/

void setup() {
    //  電源ON時に 1回だけ実行する処理をここに書く。
    M5.begin();             /*M5を初期化する*/
    M5.Lcd.setTextSize(2);  /*文字サイズはちょっと大きめ*/
    M5.Lcd.setRotation(3);  /*上スイッチが左になる向き*/

    //  最初の状態は LED消灯。
    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setCursor(0, 0);
    M5.Lcd.print("LED OFF  ");

    //  LEDを消灯し、LEDピンを出力モードに設定する。
    digitalWrite(LEDPIN, LED_OFF);
    pinMode(LEDPIN, OUTPUT);
}

void loop() {
    //  自動的に繰り返し実行する処理をここに書く。

    //  ボタンの状態を更新する。
    M5.update();

    //  現在のLED状態に応じて、LEDを点灯/消灯/点滅する。
    switch (led_state) {
    case LED_STATE::OFF:  /*消灯*/
        //  上ボタン(ボタンA)をチェックする。
        if (M5.BtnA.wasPressed()) {
            //  --- 上ボタンが押された。
            //  LEDをONする。
            M5.Lcd.setCursor(0, 0);
            M5.Lcd.print("LED ON   ");
            digitalWrite(LEDPIN, LED_ON);

            led_state = LED_STATE::ON;
        }
        break;
    case LED_STATE::ON:  /*点灯*/
        //  上ボタン(ボタンA)をチェックする。
        if (M5.BtnA.wasPressed()) {
            //  --- 上ボタンが押された。
            //  LEDをOFFする。
            M5.Lcd.setCursor(0, 0);
            M5.Lcd.print("LED BLINK");
            digitalWrite(LEDPIN, LED_OFF);

            led_on_f = false;
            led_period = OFF_TIME;
            led_chg_time = millis();

            led_state = LED_STATE::BLINK;
        }
        break;
    case LED_STATE::BLINK:  /*点滅*/
        //  上ボタン(ボタンA)をチェックする。
        if (M5.BtnA.wasPressed()) {
            //  --- 上ボタンが押された。
            //  LEDをOFFする。
            M5.Lcd.setCursor(0, 0);
            M5.Lcd.print("LED OFF  ");
            led_state = LED_STATE::OFF;
        }
        else {
            //  LED点滅時間が過ぎたか?
            uint32_t now = millis();
            if (now - led_chg_time >= led_period) {
                //  --- 切替えるタイミングが来た。
                if (!led_on_f) {
                    //  --- LED滅
                    digitalWrite(LEDPIN, LED_ON);
                    led_on_f = true;
                    led_period = ON_TIME;
                    led_chg_time = now;
                }
                else {
                    //  --- LED点
                    digitalWrite(LEDPIN, LED_OFF);
                    led_on_f = false;
                    led_period = OFF_TIME;
                    led_chg_time = now;
                }
            }
        }
        break;
    }
}

blinky.ino での点滅は、①LED を点灯、②点灯時間だけ待つ(delay)、③LED を消灯、④消灯時間だけ待つ(delay)を繰返すことで実現しました。

このスケッチのどこかに、上ボタン(押しボタンA)を検出する機能を追加するのだけでは、②④の待っている間はボタンが押されたことを検出できません。②④の待つ機能を delay() 関数以外の別の仕組みで実現する必要があります。

ここでは millis() 関数で現在の CPU時間を求め、点灯あるいは消灯時間が経過したときに、LED の状態を切り替えるようにしています。

結果

  • 電源ONで LED が消灯し、上ボタン(押しボタンA)を押すと点灯し、さらに押すと点滅に変化したらOKです。

  • 液晶ディスプレイに「LED ON」「LED OFF」「LED BLINK」と表示されるのに、LED が点灯、消灯、点滅しなかったら、外付けの回路に誤りがあります。

mysketch-A.ino の実行結果の写真
写真 mysketch-B.ino の実行結果

練習問題

  1. 点滅パターンを変更してみましょう。

いいなと思ったら応援しよう!