Menu
CoddyTech

millis()がオーバーフローするとき

CoddyのArduinoジャーニー「delayを使わないタイミング制御」セクションの一部。レッスン 17/18。

unsigned long は 4,294,967,295 で上限に達します。約 49.7 日後、millis() はこの値に達し、再び 0 から始まります。これをロールオーバーと呼びます。

より小さい型でも同じことが起こります。unsigned int は 65535 で上限に達します。

unsigned int a = 65530;
a = a + 10;   // 65540は収まらないため、aは現在4

減算も同じようにラップアラウンドし、それによって安全に扱えます。65530 から始まった時計が今 4 を示しているなら、10 ステップ進んだことになります。また、unsigned int に格納した 4 から 65530 を引いた値は、ちょうど 10 です。同様に、ロールオーバーをまたいでも、millis() - start は常に start からの経過時間を表します。

加算は安全ではありません。now >= start + interval では、合計値が上限を超えて小さな数にラップすることがあり、その場合タイマーはすぐに完了したことになります。また、now がラップしたのに合計値がまだラップしていない場合、タイマーはいつまでも完了しません。必ず減算を使いましょう。

if (now - start >= interval) {
  // 完了
}
challenge icon

チャレンジ

中級

テストのために49日も待てる人はいないため、このスケッチではロールオーバーに近い時刻をisDone()に渡し、その答えを出力します。「done」と早すぎる段階で表示され、「waiting」と永遠に表示されます。isDone()内の1行を修正し、startからの経過時間をintervalと比較するようにしてください。

Runを押して、4つの答えを読み、行を修正してからCheckを押してください。

自分で試してみよう

const unsigned long interval = 2000;

// start で開始したタイマーは、now の時点で終了しているか?
bool isDone(unsigned long start, unsigned long now) {
  return now >= start + interval;   // この行を修正する
}

// 経過時間と isDone() の結果を出力する。
void test(unsigned long start, unsigned long now) {
  Serial.print(now - start);
  Serial.print(" ms: ");
  if (isDone(start, now)) {
    Serial.println("done");
  } else {
    Serial.println("waiting");
  }
}

void setup() {
  Serial.begin(9600);
  // millis() は 4294967295 で止まり、その後 0 から再び始まる
  test(4294966000, 4294966500);   // 値が一周する直前
  test(4294966000, 4294967200);   // まだ一周する前
  test(4294966000, 900);          // 時計が 0 を通過した
  test(4294964000, 500);          // もっと前に開始したタイマー
}

void loop() {
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

delayを使わないタイミング制御のすべてのレッスン

自分で練習してみよう: Arduinoプレイグラウンド