Menu
CoddyTech

Cuando millis() se desborda

Parte de la sección Temporización sin delay del Journey de Arduino de Coddy. Lección 17 de 18.

Un unsigned long se detiene en 4,294,967,295. Después de unos 49.7 días, millis() lo alcanza y vuelve a empezar en 0. Esto se llama desbordamiento.

Un tipo más pequeño muestra lo mismo. Un unsigned int se detiene en 65535:

unsigned int a = 65530;
a = a + 10;   // 65540 no cabe: a ahora vale 4

La resta se desborda de la misma manera, y eso la hace segura: un reloj que empezó en 65530 y ahora marca 4 ha contado 10 pasos, y 4 menos 65530, almacenado en un unsigned int, es exactamente 10. Del mismo modo, millis() - start siempre representa el tiempo transcurrido desde start, incluso si ocurre un desbordamiento.

La suma no es segura. En now >= start + interval, la suma puede superar el máximo y desbordarse hasta un número pequeño, por lo que el temporizador termina de inmediato. Y cuando now se ha desbordado, pero la suma no, nunca termina. Resta siempre:

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

Desafío

Intermedio

Nadie puede esperar 49 días para hacer una prueba, así que este sketch proporciona a isDone() tiempos cercanos al desbordamiento e imprime sus respuestas. Dice "done" demasiado pronto y "waiting" para siempre. Corrige la línea de isDone() para que compare el tiempo transcurrido desde start con interval.

Pulsa Run, lee las cuatro respuestas, corrige la línea y después pulsa Check.

Pruébalo tú mismo

const unsigned long interval = 2000;

// ¿Ha terminado el temporizador que comenzó en start cuando el tiempo es now?
bool isDone(unsigned long start, unsigned long now) {
  return now >= start + interval;   // corrige esta línea
}

// Imprime cuánto tiempo ha transcurrido y qué devuelve 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() se detiene en 4294967295 y luego vuelve a empezar en 0
  test(4294966000, 4294966500);   // justo antes del desbordamiento
  test(4294966000, 4294967200);   // todavía antes del desbordamiento
  test(4294966000, 900);          // el reloj pasó de 0
  test(4294964000, 500);          // un temporizador que comenzó antes
}

void loop() {
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Temporización sin delay

Practica por tu cuenta: Playground de Arduino