趣味の開発ノート

ITの学習やプログラミング・ノーコードアプリ開発のことなど。

【M5Stack】時刻に合わせて一定間隔(分)で処理を定期実行するスケッチ

M5Stackでの電子工作の学習中。

こちらの続きで、一定間隔でセンサ値をデータベース(Spreadsheet)に送るような仕組みを作っていく。

nouka-it.hatenablog.com

具体的には「30分おきに定期的に処理を行う」ようにしていきたい。
単純に経過時間を使うのではなく「xx:00、xx:30きっかりに処理を行う」というように、時刻に合わせた処理を行いたい。

時刻と連動させるにはNTPサーバを使うという方法があるが、通信を使わず簡単に実現できる方法はないのかな?と思って探していた。
そしておそらく、この辺りのロジックを組み合わせたらできそうだな、というのがわかってきた。

nouka-it.hatenablog.com

nouka-it.hatenablog.com

実行確認のしやすい形でロジックを考える

ちょっと複雑なのでミニマムに実装していこう。
まずは実行確認がしやすいように 「毎分00秒きっかりに1回だけ「Send data!」を表示させる」 というスケッチを作成することにする。

時間の経過をどうやってとるか?

ここでは__TIME__からコンパイル時点での時hh、分mm、秒ssを算出して、1秒ずつ加算しながらこれらの変数hh, mm, ssで時間を管理する。
簡単に考えてloop()関数の中でdelay(1000)で1秒ずつ進めて、都度時間合わせをするようにした。
ただ、おそらくこれだと実行に1秒ちょいかかって少しずつ時間がずれていく気がするけど・・・ひとまず良しとする。

分が変わった時に1回だけ処理をするのはどうしたら良いか?

別の変数ommに現在の分を記録しておいて、if文でその変数ommと変化後の分mmを使って切り替わりタイミングを検知するようにした。 (ちょっと説明が上手くできない。)

作成したスケッチ

ともあれ、このようなスケッチを作成した。

▼毎分00秒の時に1回だけ「Send data!」を表示させる

#include <M5Stack.h>

static uint8_t conv2d(const char* p); // タイムスタンプを時、分、秒に変換する関数

uint8_t hh = conv2d(__TIME__), mm = conv2d(__TIME__ + 3), ss = conv2d(__TIME__ + 6); // コンパイル時刻
uint8_t omm = mm;

void setup() {
    // M5の初期化
    M5.begin();         
    M5.Power.begin();

    // 画面の初期設定
    M5.Lcd.fillScreen(TFT_BLACK);
    M5.Lcd.setTextSize(3);
    M5.Lcd.setTextColor(TFT_YELLOW, TFT_BLACK);
}

void loop() {
    // 画面の初期化
    M5.Lcd.clear();

    // 1秒進めて時間合わせ
    ss++; // 
    if(ss == 60){
      ss = 0;
      mm++;
      if(mm > 59){
        mm = 0;
        hh++;
        if(hh > 23){
          hh = 0;
        }  
      }        
    }

    if(mm != omm){  // 分の切り替わりを検知してこの中身を実行

        omm = mm;     // 現在の分を記録

        // 1回だけ実行される処理
        M5.Lcd.setCursor(0, 180);
        M5.Lcd.printf("Send data!\n");
    }

    // 画面の表示テスト
    M5.Lcd.setCursor(0, 0);
    M5.Lcd.printf("compile time\n");
    M5.Lcd.setCursor(0, 30);
    M5.Lcd.printf("%u", __TIME__);

    M5.Lcd.setCursor(0, 90);
    M5.Lcd.printf("hh, mm, ss\n");
    M5.Lcd.setCursor(0, 120);
    M5.Lcd.printf("%u, %u, %u", hh, mm, ss);

    delay(1000);
}


// Function to extract numbers from compile time string
static uint8_t conv2d(const char* p) {
  uint8_t v = 0;
  if ('0' <= *p && *p <= '9')
    v = *p - '0';
  return 10 * v + *++p - '0';
}

これで動画のように、分が切り替わって秒数が00きっかりになった時に「Send data!」が1回だけ表示されて、1秒後には表示されないような実装ができた。
良い感じ。

www.youtube.com

手直しして実際のロジックに当てはめる

先程のスケッチを踏まえて 「毎時00分、30分の時に処理を行う」 ための骨組みを書いた。

作成したスケッチ

先程のスケッチからディスプレイへの表示の処理を省いたので、現段階ではこれをM5Stackに書き込んでも何も起こらない。
あとは(※)に定期実行したい処理(データを送信する処理)を入れ込むことで、定期実行ができる。

▼毎時00分、30分の時に処理を行うスケッチ

#include <M5Stack.h>

static uint8_t conv2d(const char* p);

uint8_t hh = conv2d(__TIME__), mm = conv2d(__TIME__ + 3), ss = conv2d(__TIME__ + 6); // Get H, M, S from compile time
uint8_t omm = mm;

void setup() {
    // M5の初期化
    M5.begin();         
    M5.Power.begin();

    // 画面の初期設定
    M5.Lcd.fillScreen(TFT_BLACK);
    M5.Lcd.setTextSize(3);
    M5.Lcd.setTextColor(TFT_YELLOW, TFT_BLACK);
}

void loop() {

    // 1秒進めて時間合わせ
    ss++; // 
    if(ss == 60){
      ss = 0;
      mm++;
      if(mm > 59){
        mm = 0;
        hh++;
        if(hh > 23){
          hh = 0;
        }  
      }        
    }

    if(mm == 0 || mm == 30) {  // 毎時00分、30分の時に実行
      if(mm != omm){  // 分の切り替わりを検知して一回だけこの中身が実行される

        omm = mm;     // 現在の分を保管

        /* (※) ここでデータ送信処理を行う */
      }
    }

    delay(1000);
}


// Function to extract numbers from compile time string
static uint8_t conv2d(const char* p) {
  uint8_t v = 0;
  if ('0' <= *p && *p <= '9')
    v = *p - '0';
  return 10 * v + *++p - '0';
}

今回のまとめ

良さそうなスケッチができた!

前回のセンサーデータをSpreadsheetへ送る処理と組み合わせて、いける気がする。