IoT何をいまさら(62) Wio Terminal、RTC(実時間タイマ)を使う

JosephHalfmoon
Joseph Halfmoon

前回WioTerminalの画面に文字列をスクロール出力できるようになったので、今度はRTC(実時間タイマ)を使えるようにしたいと思います。RTCが使えれば、何時何分の計測値はいくら、という記録がとれますので。今回はまず手動で現在時刻を設定できるようにしてみます。

まずWio Terminalの中心であるマイクロチップのArm Cortex-M4Fマイコン、SAMD51P19AのRTCについて見てみます。マイコン内蔵のRTCには3モードあり、10ビットプリスケーラ付きの単純な32ビットまたは16ビットカウンタとしてタイムアウトの監視などにも使える2つのモードと、カレンダ時計として閏年を含む秒単位の時間計測を行うモードです。ライブラリ関数はカレンダ時計としてのインタフェースであるので、多分3番目のモードなのだろうと想像しますが、確認したわけではありません。なお、Wio Terminalの回路をみると32.768kHzの振動子が原クロックに使われているように見えます。

この手のマイコンの通例にしたがって、SAMD51P19AのRTCもバッテリでバックアップ可能な電源ドメインに配置されています。しかし、Wio Terminalの回路図をみると、バッテリバックアップ用にも使える端子は別な目的に使われてしまっていてバックアップはされていないように見えます。このため、電源を落とすとRTCの内容は消えてしまうので、再度、時刻設定が必要になります。なお、USBケーブルを接続したまま、本体横の電源兼RESETスイッチを電源OFFにしても(一番上のポジション)電源は切れるので時刻は失われます。

RTC使用のための、Arduino環境へのライブラリのセットアップ方法と基本的な使用方法は、Seeed社の以下のページを参照すれば分かります。

RTC Overview

RTCの時刻初期化については、「ハードコードされたキメウチ時刻」と「ネットワーク使ったNTP時刻」の初期化のみ説明されています。まあ、NTP使えば正確な時刻が得られるので、それがあれば良いだろうという思し召しなんだと思います。でも、

どんな時計でも手動の時刻設定くらいあるわなあ

ということで、手動で時刻設定できるようにしてみました。そのためには、年、月、日、時、分、秒を手動で入力できなければならないです。

5Way Switchつかった数字の入力関数

前回のText出力と5 way switch入力のファイルに以下の関数を追加してみました。呼び出すときに “20210107123300” のような数字の文字列を与えると、最下行にそれが表示されます。そして5 way switchの左右でカーソル(反転文字)が移動でき、上下で数字を大きくしたり、小さくしたりできる。そしてPressで決定、という具合。なお、ボタンの感度は最後の待ちのdelay()関数次第。300は鈍重な感じだが年寄りには良い?

#define TXT_LM (4)
#define TXT_H (16)
#define TXT_W (12)
~途中略~

void statusEditNum(char *stat) {
  int maxPos = strlen(stat);
  int currentPos = 0;
  statusLine(stat);
  while (digitalRead(WIO_5S_PRESS) != LOW) {
    tft.drawChar(TXT_LM+currentPos*TXT_W, 224, stat[currentPos], txtBGcolor, txtFGcolor,2);
    if (digitalRead(WIO_5S_LEFT) == LOW) {
      tft.drawChar(TXT_LM+currentPos*TXT_W, 224, stat[currentPos], txtFGcolor, txtBGcolor,2);
      currentPos = (currentPos > 0) ? (currentPos - 1) : 0;
    } else if (digitalRead(WIO_5S_RIGHT) == LOW) {
      tft.drawChar(TXT_LM+currentPos*TXT_W, 224, stat[currentPos], txtFGcolor, txtBGcolor,2);
      currentPos = (currentPos < (maxPos - 1)) ? (currentPos + 1) : maxPos - 1;
    } else if (digitalRead(WIO_5S_UP) == LOW) {
      stat[currentPos] = (stat[currentPos] < 0x39) ? (stat[currentPos] + 1) : 0x39;
    } else if (digitalRead(WIO_5S_DOWN) == LOW) {
      stat[currentPos] = (stat[currentPos] > 0x30) ? (stat[currentPos] - 1) : 0x30;      
    }
    delay(300);
  }
}

実際、上記の関数を使って現在時刻を修正しているところが下に。ちょっと見難いですが、末尾の「0」のところが反転表示になっていてカーソル位置であるとことが分かります。ここでPressすれば、その数字文字列確定と。Wio Terminal, Set RTC init-value

さて、RTCの操作自体は、提供されたRTC_SAMD51クラスのインスタンスに一皮被せて行いました。使い回しやすいように別な .ino ファイルとしておきました。

  • rtcInit()で上の数字文字列使った時刻の初期化
  • rtcNow()で、RTCからの時刻の読み出し

rtcInit()に空文字列など与えると初期化せず、成り行きの時刻のままで動作しますが、その場合、2000年1月1日0時0分0秒からのスタートになるようです。

rtcNow()の第2に引数に1を与えると、人間が読みやすいフォーマット、それ以外だと、上の数字文字列の修正関数で処理できるフォーマットで返されます。

RTCの設定、読み出し
RTC_SAMD51 rtc;

int s2int(char *s, size_t n) {
  char work[5];
  if (*s == '0') {
    s++;
    n--;
  }
  strlcpy(work, s, n+1);
  return atoi(work);
}

void rtcInit(char *initStr) {
  int yr, mo, dy, hr, mi, sc;
  char buf[28];
  
  rtc.begin();
  if (strlen(initStr) == 14) {
    yr = s2int(initStr, 4);
    initStr += 4;
    mo = s2int(initStr, 2);
    initStr += 2;
    dy = s2int(initStr, 2);
    initStr += 2;
    hr = s2int(initStr, 2);
    initStr += 2;
    mi = s2int(initStr, 2);
    initStr += 2;
    sc = s2int(initStr, 2);
    DateTime now = DateTime(yr, mo, dy, hr, mi, sc);
    rtc.adjust(now);
  }
}

void rtcNow(char *buf, int opt) {
  int yr, mo, dy, hr, mi, sc;
  DateTime now;

  now = rtc.now();
  yr = now.year();
  mo = now.month();
  dy = now.day();
  hr = now.hour();
  mi = now.minute();
  sc = now.second();
  if (opt == 1) {
    sprintf(buf, "%04d/%02d/%02d %02d:%02d:%02d", yr, mo, dy, hr, mi, sc);
  } else {
    sprintf(buf, "%04d%02d%02d%02d%02d%02d", yr, mo, dy, hr, mi, sc);    
  }
}

最後、前回のコードをベースにRTCの初期化と、現在時刻の読み出しをメニューに仕込んだものがこちら。

loop()関数内のMenuへのRTC設定、読み出しの追加部分
void loop() {
~途中略~
  char buf2[28];

  switch (fiveWayPrompt()) {
~途中略~
    case 3:
      rtcNow(buf2, 0);
      statusEditNum(buf2);
      rtcInit(buf2);
      rtcNow(buf2, 1);
      txtPrint(buf2);
      statusLine("L) Initialize RTC");
      break;
    case 4:
      rtcNow(buf2, 1);
      txtPrint(buf2);
      statusLine("R) current Time");
      break;
~以下略~

これでタイムスタンプを打つことが出来るようになりました。次は長時間の「測定」をやって、時刻と測定値をダラダラとSDカードに記録してみる予定であります。

IoT何をいまさら(61) Wio Terminal、スクロールするテキスト表示 へ戻る

IoT何をいまさら(63) Wio Terminal、時刻と温度をSDカードに記録 へ進む