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) {
// 完了
}チャレンジ
中級テストのために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() {
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
delayを使わないタイミング制御のすべてのレッスン
自分で練習してみよう: Arduinoプレイグラウンド