M5Stack で millis 関数で簡単なタイマーを作るメモ

M5Stack で millis 関数で簡単なタイマーを作るメモです。

このように動きます。

シンプルに 5 秒数えます。A ボタンでカウント開始 B ボタンでカウントダウン中にストップします。

背景

Arduino の loop 関数で delay(1000) とか入れておいて 1 秒ごとに定期実行させるの、とっても便利ですよね。私も良く使います。小さい仕組みなら、これでだいたい何とかなります。

ただ、弱点もあります。

たとえば、MQTT をつなぐ PubSubClient の mqtt.loop であったり、何かしらの再接続、その他にもセンサー計測まわりで loop 関数の中でずっと動かしつづけたほうがいい系の処理があるとき。

このあたりの処理がいるのに、うっかり処理を待つ delay 関数で待ち処理を入れてしまうと、delay で待っている間に大切な処理が働かないので不安定になったり、そうでなくても思ったような処理にならない場合があります。こんな感じで、ある程度、仕組みを作って積んでいくと delay 関数が足かせになる場合があるんですよね。

なので、loop 関数は常時動かし続けるが、処理を止める delay 関数を使わないで、なにかしら定期実行をさせたい処理を millis 関数で簡単なタイマーを作ってみて、自分のためにメモしておきます。

millis 関数は リファレンス にあるとおりで、Arduinoボードがプログラムの実行を開始した時から現在までの時間をミリ秒単位で返します。これを使えば、ある時に時間を記録して時間経過を計測した上で、指定の時間で止めるようなタイマーがつくれます。

もちろん、めっちゃ正確にタイマーを作るのであればリアルタイムクロック(RTC)は必要ですが、今回は普段づかいでざっくり時間を取りたいので、これでオッケーとします。

ソースコード

出来上がったコードはこちらです。M5Stack V2.6 で動かしてみました。

A ボタンが押された時間を記録しておいて、loop 関数内でカウントダウン中は、さっきの押された時間から現在時間 millis() から経過時間を計算してます。また、残り時間も制限時間(今回は 5 秒)から経過時間を引いて計算後にディスプレイ表示しています。

ディスプレイ表示は loop 実行でめっちゃ細かく実行してるのでチラつきが目立ちますが、ひとまず動いたので、今回はこれで。

#include <M5Stack.h>

// カウントダウン開始フラグ
int flagCountdown = 0;

// A ボタンが押された時間
long timePushedMillis = 0;

// 制限時間 秒 5秒で設定
int timeWaitSeconds = 5;

// 制限時間 ミリ秒
long timeWaitMillis = timeWaitSeconds * 1000;

void setup() {

  // ディスプレイとシリアルとON
  M5.begin(true, false, true);
  
  // スタート
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.setTextSize(3);

  // タイトル
  M5.Lcd.setCursor(10, 80);
  M5.Lcd.println("Millis Timer");
}

void loop() {
  M5.update();

  // カウントダウンフラグが 1 の時だけ動作
  if(flagCountdown == 1){
    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setCursor(10, 110);
    // 経過時間 ミリ秒
    // A ボタンが押された時間から現在時間 millis() を引くと得られる
    float timeElaspledMillis = millis() - timePushedMillis;
    // 残り時間 ミリ秒
    // 制限時間から経過時間を引くと得られる
    float timeLeftMillis = timeWaitMillis - timeElaspledMillis;
    // 残り時間 秒
    // 表示用の秒数
    float timeLeftSeconds = timeLeftMillis / 1000;
    // 表示
    M5.Lcd.print(timeLeftSeconds);
    M5.Lcd.print(" sec / ");
    M5.Lcd.print(timeWaitSeconds);
    M5.Lcd.print(" sec");
    // 0 になったら終了
    if( timeLeftSeconds <= 0 ){
      // カウントダウン終了
      flagCountdown = 0;
      // タイトルに戻る
      M5.Lcd.fillScreen(BLACK);
      M5.Lcd.setCursor(10, 80);
      M5.Lcd.println("Millis Timer");
    }
  }
  
  if (M5.BtnA.wasReleased()) {
    // A ボタン
    // カウントダウン開始
    flagCountdown = 1;
    // A ボタンが押された時間の記録
    timePushedMillis = millis();
  } else if (M5.BtnB.wasReleased()) {
    // B ボタン
    // カウントダウン終了
    flagCountdown = 0;
    // タイトル
    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setCursor(10, 80);
    M5.Lcd.println("Millis Timer");
  } else if (M5.BtnC.wasReleased()) {
    // C ボタン
  }
}