Entender millis y no morir en el intento

Al final me he animado y he escrito un tutorial sobre millis. He intentado que sea comprensible y lo he hecho con ejemplos para que sea más fácil de entender.

Dada la extensión del tema lo he divido en varias partes, así que disculpad, pero tardo un poco ponerlas todas.

Allá vamos.

2 Likes

Para explicar el proceso de como funciona millis, como usarlo, y como crear temporizadores vamos a montar un pequeño circuito de pruebas:

Usaremos el led de la placa que está conectado al pin 13 para ver el efecto visual, el puerto serie para mostrar cosas (habrá que abrir la consola desde el IDE) y un pequeño botón, no hará falta una resistencia ya que utilizaremos la interna del pin de arduino.

Empecemos analizando este pequeño código:

/*
 * blink.
 */
void setup() {
  pinMode(13, OUTPUT); // Usamos el led de la placa.
}

void loop() {
  digitalWrite(13, HIGH); // Encendemos el led.
  delay(1000); // Esperamos 1 segundo.
  digitalWrite(13, LOW); // Apagamos el led.
  delay(1000); // Esperamos 1 segundo.
}

Primero encendemos el led, esperamos 1 segundo, apagamos el led, esperamos un segundo y volvemos a empezar. Recordad que loop es un bucle infinito y siempre que termine vuelve a empezar.

¿Qué hemos conseguido? ¡¡El led parpadea!! ¡¡Milagro, ya sé programar!! Mas lejos de la realidad. Hemos conseguido un efecto que puede valer para algo, pero que nos complicará la existencia de cara al futuro.

Por ejemplo ahora queremos conectar un botón al pin dos y queremos hacer que cuando lo pulsemos muestre el texto “hola mundo” por el puerto serie:

/*
 * blink con botón.
 */
void setup() {
  pinMode(13, OUTPUT); // Usamos el led de la placa.
  // Usaremos la resistencia pull-up interna de arduino para
  // establecer el valor de la entrada a HIGH si no hay nada
  // conectado. El pulsador lo colocaremos al pin 2 y a GND,
  // de forma que cuando lo pulsemos el pin valga 0.
  pinMode(2, INPUT_PULLUP); 
  Serial.begin(9600); // Iniciamos el puerto serie.
}

void loop() {
  digitalWrite(13, HIGH); // Encendemos el led.
  delay(1000); // Esperamos 1 segundo.
  digitalWrite(13, LOW); // Apagamos el led.
  delay(1000); // Esperamos 1 segundo.
  if ( digitalRead(2)==LOW ) {
    Serial.println("Hola mundo");
  }
}

¿Qué ocurre ahora? El led sigue parpadeando, pero cuando pulsamos el botón no se muestra nada, sólo muestra el mensaje manteniendo pulsado el botón, y no lo muestra una vez detrás de otra, sino cada dos segundos, ¿casualidad?, realmente no.

Delay es una función que detiene el programa, si le ponemos que espere un segundo, el programa se para un segundo, no hace nada y si en ese tiempo queremos interactuar con Arduino no podremos: no leemos pines, no leemos entradas analógicas, no escribimos en pantalla, no hacemos nada de nada solo esperar a que pase el tiempo.

Una manera de resolver problemas es usar interrupciones. Una interrupción es una porción de código que se ejecuta cuando se produce un evento externo, independientemente de lo que se esté haciendo en el loop. Efectivamente, por ejemplo si en el loop estamos imprimiento algo por el puerto serie, si se produce una interrupción, deja de imprimir, ejecuta el código asociado a la interrupción, y sigue por donde se quedó imprimiendo.

/*
 * blink con interrupción.
 */

// Este código se ejecutará cuando se produzca la interrupción.
void codigoInterrupcion() {
  Serial.println("Hola mundo");
}
 
void setup() {
  pinMode(13, OUTPUT); // Usamos el led de la placa.
  // Usaremos la resistencia pull-up interna de arduino para
  // establecer el valor de la entrada a HIGH si no hay nada
  // conectado. El pulsador lo colocaremos al pin 2 y a GND,
  // de forma que cuando lo pulsemos el pin valga 0.
  pinMode(2, INPUT_PULLUP); 
  Serial.begin(9600); // Iniciamos el puerto serie.
  
  // Creamos la interrupción, diciendole al arduino que cuando el
  // pin2 pase de HIGH a LOW ("FALLING") ejecute nuestro código.
  attachInterrupt(digitalPinToInterrupt(2), codigoInterrupcion, FALLING);
}

// En el loop no hay que tratar el pin 2, ya que al tratarse de 
// una interrupción Arduino lo detectará por su cuenta y ejecutará
// el código.
void loop() {
  digitalWrite(13, HIGH); // Encendemos el led.
  delay(1000); // Esperamos 1 segundo.
  digitalWrite(13, LOW); // Apagamos el led.
  delay(1000); // Esperamos 1 segundo.
}

En el ejemplo anterior, cuando pulsamos el botón si se muestra el mensaje, de hecho varios de ellos debido al rebote (debounce) del pulsador. Hemos “conseguido” el efecto deseado pero no es la mejor forma de hacerlo, ¿por qué?

  • Tenemos escasez de interrupciones. En un arduino uno solo tenemos dos que estan presentes en los pins 2 y 3. (*)
  • Las interrupciones están pensadas para eventos criticos que precisan una rapida respuesta del arduino, pulsar un botón no es algo critico. Un ejemplo válido: leer las revoluciones de un motor, cuya velocidad es muy alta y si usamos digitalRead perderemos vueltas.
  • El código de la interrupción debe ser simple, conciso y rápido. No se debe poner código que haga que el Arduino se bloquee o tarde mucho, en el ejemplo anterior, aunque funciona, usar println no es una buena idea. Lo suyo es un conjunto básico de operaciones (sumar, restar, etc…).
  • Seguramente el profesor te suspenderá si lo haces y en el foro te diremos que no lo hagas. Para muchos nuevos quizás sea esta más importante que las anteriores.

Y ¿qué podemos hacer? Usar la función millis.

(*)En realidad podemos tener más interrupciones, pero no de forma nativa con el
IDE de Arduino y hay que buscar librerias alternativas. Si el concepto de interrupción ya es díficil de entender, imaginad si teneis mas.

Arduino tiene una "especie" de reloj interno que se pone en marcha desde punto y hora que lo encendemos hasta que lo apagamos (o hacemos un reset de la placa). Ese reloj va contando en milisegundos, asi que empieza contando 0, 1, 2, 3, 4, 5... y subiendo cada milisegundo. Cuando hacemos una llamada a millis esta nos devuelve el valor dicho reloj.

El reloj funciona de manera independiente de nuestro código. Esta montado de tal manera que funciona como una interrupción (ya lo vimos antes) que se ejecuta cada milisegundo y solo hace eso, incrementar el número. Así que aunque hagamos un delay se seguirá ejecutando.

Imaginad que tenemos un reloj de pulsera, este va a estar contando todo el rato para saber en que hora estamos. De vez en cuando miramos la hora y "¡uy! las 5, hora del café".

Tu te levantas, desayunas, te vas a trabajar/estudiar, comes, cenas, etc y haces una vida normal, pero el reloj sigue contando las horas, de vez en cuando lo miras porque tienes que hacer algo a una hora.

Pues bien, ese reloj de pulsera es el reloj de arduino y ese "mirar la hora" es llamar a la función millis.

Vamos a ver un ejemplo sencillo:

/*
* Ejemplo de millis con delay.
*/

void setup() {
 Serial.begin(9600);
}

void loop() {
 Serial.println(millis());
 delay(1000); // Oh! Dios! Estas usando delay!
}

En este ejemplo, miro el contador millis y lo envio por el puerto serie. Luego hago un delay. Como el loop es un bucle infinito el proceso se repite eternamente. Y la salida es esta:

0
999
1999
3000
3999
5000
6000
7001
8000
9000
10001
...
...

Como veis, el reloj sigue contando aunque tu detengas el programa haciendo un delay (recordad, un delay es hacer una parada de programa). Como veis la salida no es exactamente 1000 milisegundos, casi siempre hay una diferencia de 1 ms, pero no es preocupante, tanto millis como delay no son exactos al 100%.

Veamos como hacer el parpadeo del led cada segundo. Parece lógico que tenemos que ver lo que marca millis (la "hora" que es) en este momento, guardar su valor, e ir mirando en cada pasada hasta que haya pasado un segundo, es decir millis nos da 1000 mas de lo que tenemos guardado. En ese momento cambiamos el led, volvemos a anotar el tiempo y seguimos mirando millis.

/*
 * blink sin delay.
 */

// En la variable t guardamos el valor de millis para ir comprobando
// en cada pasada de loop si han pasado 1000 ms.
unsigned long t;

// En esta variable guardamos el estado del led: encendido (HIGH) o 
// apagado (LOW).
int estadoLed;

void setup() {
 // El led está en el pin 13, hay que ponerlo como salida.
 pinMode(13,OUTPUT);
 // Miramos el "reloj" por primera vez.
 t = millis();
 // Encendemos el led.
 estadoLed = HIGH;
 digitalWrite(13,estadoLed);
}

void loop() {
 // Aquí la comparación mágica.
 if ( millis() - t >= 1000 ) {
   // Cambiamos el estado del led.
   if ( estadoLed==HIGH ) 
     estadoLed=LOW;
   else
     estadoLed=HIGH;
   digitalWrite(13,estadoLed);
   // Guardamos el tiempo de nuevo para comprobar en las siguientes
   // pasadas.
   t = millis();
 }
}

et voilá! un led que parpadea sin usar delay.

Aquí el problema radica en entender el if o la comparación mágica. Recordad: loop es un bucle inficito, cuando termina vuelve a empezar y millis siempre está contando.

Al principio (setup) hemos leido el valor de millis y lo hemos guardado en t, supongamos que se ha guardado un cero. El reloj de millis sigue contando, pongamos que al entrar en el loop ya vale 10. ¿Cómo sabemos el tiempo que ha pasado? fácil, sabemos que el reloj actual vale 10, y que cuando lo miramos (lo guardamos en la variable t) valia 0. Así que 10-0 significa que han transcurrido 10 ms.

Comparamos ese tiempo transcurrido con el tiempo del parpado. Si el tiempo transcurrido fuera mayor que los 1000 ms del parpadeo deberemos cambiar el pin. De momento esa condición no se cumple, así que no hacemos nada.

Pero ¿qué ocurre cuando han pasado 1000 ms desde que iniciamos el arduino? Millis nos devolverá 1000. Volvemos a comparar, millis ahora vale 1000 y t (nuestro tiempo de inicio) vale 0, restamos y vemos el tiempo que ha transcurrido 1000-0 = 1000. Y cuando lo comparamos con 1000 resulta que es igual. Entonces cambiamos el estado del led, si estaba encendido lo apagamos y viceversa. Y ahora lo importante, si queremos volver a contar desde este punto que ya hemos cambiado el led, tendremos que guardar la "hora" actual, el valor de millis, para seguir comparando después. Hacemos t = millis() = 1000.

Nota: En este momento t = millis() podría ser 1001 o mas, el código que pongamos dentro del if tarda un tiempo en ejecutarse y no lo controlamos. En este caso el tiempo que tarda en ejecutarse el cambio del led es ridiculo, asi que vale con un valor de 1000.

A partir de aquí el proceso sigue. En la siguiente pasada millis puede valer 1001 y t vale 1000, el tiempo transcurrido será de 1001-1000=1. 1 es mayor que 1000 obvio que no, por lo tanto no hace nada. Y seguimos contando...

Llega un momento que cuando llamamos a millis nos devuelve 2000. Y al hacer la comparación 2000-1000=1000, resulta que ya han transcurrido los 1000 ms y al comparar con 1000, volvemos a cambiar el estado del led y cambiamos el valor t a 2000. Y seguimos contado...

Ocurrirá lo mismo cuando lleguemos a 3000, 4000, 5000, 6000, ..., es decir cada segundo le led cambiará de estado.

Bien, ya tenemos nuestro led parpadeando, ahora interactuaremos con arduino añadiendo que cuando el botón este pulsado muestre la cade de texto "hola mundo" al igual que lo hacimos con delay.

/*
 * blink sin delay.
 */

// En la variable t guardamos el valor de millis para ir comprobando
// en cada pasada de loop si han pasado 1000 ms.
unsigned long t;

// En esta variable guardamos el estado del led: encendido (HIGH) o 
// apagado (LOW).
int estadoLed;
 void setup() {
  // Iniciamos el puerto serie.
  Serial.begin(9600);
  // El led está en el pin 13, hay que ponerlo como salida.
  pinMode(13,OUTPUT);
  // El botón lo ponemos como entrada.
  pinMode(2,INPUT_PULLUP);
  // Miramos el "reloj" por primera vez.
  t = millis();
  // Encendemos el led.
  estadoLed = HIGH;
  digitalWrite(13,estadoLed);
  
 }

 void loop() {
  // Aquí la comparación mágica.
  if ( millis() - t >= 1000 ) {
    // Cambiamos el estado del led.
    if ( estadoLed==HIGH ) 
      estadoLed=LOW;
    else
      estadoLed=HIGH;
    digitalWrite(13,estadoLed);
    // Guardamos el tiempo de nuevo para comprobar en las siguientes
    // pasadas.
    t = millis();
  }
  // Miramos el estado del botón y si está pulsado mostramos el mensaje.
  if ( digitalRead(2)==LOW ) {
    Serial.println("Hola Mundo");
  }
 }

¿Qué pasa ahora? Cada vez que pulsamos el botón ¡zás! un montón de mensajes. Cuando usabamos delay teniamos que manterner el botón pulsado para que lo mostrará una vez, recordad siempre que delay detiene el programa. En cambio, usando millis conseguimos el mismo efecto del parpadeo pero el programa nunca se detiene.

Preguntaréis ¿por qué tanto mensaje si yo solo lo quiero mostrar una vez? En realidad el programa hace lo que tiene que hacer, solo que esa no es la manera correcta de leer un botón. Para leer un botón hay que ver el cambio de estado (de no pulsado a pulsado) y tener en cuenta el rebote. Es tema para otro tutorial.

En este caso solo miramos que en el pin haya un estado bajo (digitalRead(2)==LOW) y lo hace muchas veces por que no bloqueamos el programa: cuando termina de leer con millis sigue con la siguiente instrucción que es el digitalRead, vuelve a mirar el reloj y sigue. Y esto a una rapidez tal que lo hace varias veces por milisegundo.

Con esta técnica hemos conseguido algo simple: hacer cosas periodicamente. Cada x tiempo hacemos una cosa, en este caso, cambiar un led. Pero podemos cambiar el tiempo y hacer otra cosa: leer un sensor, mandar un menaje, ...

Veamos otro ejemplo: una luz de escalera.

Los que han vivido en un piso saben que en las escaleras hay un pulsador que cuando lo activamos se enciende la luz, esta luz sigue encendida durante un tiempo en el cual tu subes al siguiente piso. Obviamente, si usas el ascensor, cuando salgas de él tendrás que volver a darle para que se encienda, pero si has usado la escalera habrás hecho lo lógico: volver a darle otra vez mientras sigue encendida para que vuelva a contar y no se te apague a medio subir. ¿Cómo lo hacemos?

/*
 *  Luz de escalera.
 *  
 *  Cuando pulsamos el botón la luz se enciende pero permanecerá
 *  encendida durante un minuto cuando la soltemos. Si volvemos 
 *  a pulsar volverá a empezar a contar otra vez.
 *  
 *  Simulamos la luz con el led de la placa.
 */

unsigned long t;

void setup() {
  pinMode(2, INPUT_PULLUP);
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);
}

void loop() {
  // Comprobamos que el botón está pulsado.
  if ( digitalRead(2)==LOW ) {
    digitalWrite(13,HIGH); // Encendemos la luz.
    t = millis(); // Guardamos CUANDO ha ocurrido.
  }
  // Si el botón NO está pulsado.
  else {
    // Queremos que la luz siga encendida durante un minuto
    // asi que tenemos que comprobar que ha transcurrido ese
    // minuto.
    if ( millis()-t > 60000 ) {
      digitalWrite(13,LOW); // Ha pasado el minuto, apagamos la luz.
    }
  }
}

Cuando detectamos el pulsador activado tenemos que encender la luz y guardamos el tiempo de cuando ha ocurrido. Cuando el pulsador ya no está, no apagamos la luz, si no que comprobamos que el tiempo que ha transcurrido es el de un minuto, si es asi la apagamos.

Podríamos haber hecho lo mismo con delay, la respuesta es NO. Si cuando detectamos que hemos pulsado hacemos un delay(60000), durante un minuto arduino se congela y tendremos que esperar a que la luz se apague para volver a repetir el proceso. Así que ya sabes, si eres lento subiendo la escalera quizás no tengas muchos problemas, pero si eres rápido entre piso y piso vas a tener que esperar a que apague o quedarte oscuras por la mitad.

Con este ejemplo podéis apreciar lo que os dije en el primer post: con delay hacéis cosas, pero a la larga os dará dolores de cabeza.

En el siguiente post os daré unas nociones a tener en cuenta cuando usamos millis.

1 Like

reservado para la parte 4Esta parte quizás sea un poco "técnica", tenéis dos opciones: leerla y entenderla, o quedaremos con las moralejas que dejo. Desde mi punto de vista lo lógico es leerlo todo y tratar de entederlo.

¿Por qué usas unsigned long?

El contador que usa millis es del tipo unsigned long. En Arduino este tipo de datos tiene un tamaño de 32 bits. Lo cual nos da un total de valores entre 0 y 4294967296 todo en milisegundos.

No voy a entrar en la temática de números, ya que da juego para otro tutorial. Basta decir que si millis vale 4294967000 y lo guardamos en un unsigned int que lo máximo que almacena es 65534 entonces el valor se truncará y no será el valor que deseamos.

Uno de los errores que más he visto es usar float, pensando que los float son más grandes, cosa que no es asi. En Arduino un float tiene el mismo tamaño que un unsigned long, es decir, 32 bits, y tiene que guardar el tamaño de los decimales y el signo.

Para convenceros os dejo este pequeño programa.

/*
 * Demostración de como se asignan mal los números.  
 */

// en N tenemos un valor de millis relativamente alto.
unsigned long n=4294967295;
float x; // un valor float.
int   y; // un valor entero.

void setup() {
  Serial.begin(9600);
  x = n;
  y = n;
  Serial.println(x); // La salida es "ovf" overflow.
  Serial.println(y); // La salida es -1.
  // La salida no es el número grande que esperabamos.
}

void loop() {
  
}

Moraleja: usad siempre UNSIGNED LONG con millis.

Entonces, ¿Si el máximo valor que cuenta es 4294967296 que ocurre despues de todos esos milisegundos?

Que el contador se desborda, pasa a valer 0 y sigue contando. Más de una vez habréis oido que millis a los 49 dias se desborda. Efectivamente para ser exactos se desborda a los 49,710269629 días.

¿Y qué pasa si se desborda?

Nada. El reloj de pulsera ¿al pasar media noche se bloquea? no, sigue contando como si nada. En arduino ocurre igual.

¿Y qué pasa con mi temporizador si se desborda millis mientras tanto?

Bien. Esto es interesante.

Hasta ahora habeis visto que he usado una regla simple: siempre he calculado el intervalo de tiempo que ha transcurrido viendo el tiempo actual (millis) restando el tiempo de inicio (t) y comparando con el tiempo que queremos (duración).

Pero también podriamos calcular el valor del reloj al que queremos que haga algo. Por ejemplo cuando guardamos millis vale 10, y queremos que dentro de 30 milisegundos haga algo. Eso significa que cuando milis valga 40 (10+30) queremos que haga algo. Asi que si comparo el valor de millis con 40 y tenemos el mismo resultado.

Por ejemplo:

/*
 * blink sin delay, pero sumando.
 */

// En la variable t guardamos el valor de millis para ir comprobando
// en cada pasada de loop si han pasado 1000 ms.
unsigned long t;

// En esta variable guardamos el estado del led: encendido (HIGH) o 
// apagado (LOW).
int estadoLed;

void setup() {
 // El led está en el pin 13, hay que ponerlo como salida.
 pinMode(13,OUTPUT);
 // Miramos el "reloj" por primera vez.
 t = millis();
 // Encendemos el led.
 estadoLed = HIGH;
 digitalWrite(13,estadoLed);
}

void loop() {
 // Aquí sumando, hacemos la suma de cuando miramos el tiempo con el
 // tiempo de nuestro intervalo y lo comparamos con millis.
 if ( millis() >= t + 1000 ) {
   // Cambiamos el estado del led.
   if ( estadoLed==HIGH ) 
     estadoLed=LOW;
   else
     estadoLed=HIGH;
   digitalWrite(13,estadoLed);
   // Guardamos el tiempo de nuevo para comprobar en las siguientes
   // pasadas.
   t = millis();
 }
}

Oye! También funciona. Pero ¿qué ocurre cuando estamos a punto de desbordarnos?.

Supongamos que cuando tomamos el valor de t inicial faltan 500 ms para desbordarnos, t valdrá: 4294966796 (en hexadecimal 0xfffffe0c). Si le sumamos 1000 (0x000003e8) que es el tiempo del parpadeo, el resultado será 500 (0x000001f4).

Obviamente la suma está bien, piénsalo, faltaban 500 para desbordarse, si le sumamos 500 llegamos al 0, otros 500 y ya tenemos los 1000.

Pero que ocurre cuando comparamos, millis todavia no se ha desbordado, sigue siendo un valor alto (recordad que lo anotamos cuando valia 4294966796), y si han transcurridos unos pocos milisegundos, la condición es cierta, entramos en la condición y ya no hemos esperado los 1000 segundos,

¿Cuando restamos pasa lo mismo? No. Analicemos, la misma situación. Guardamos t cuando faltan 500ms, es decir, al igual que antes 4294966796.

Si solo han pasado dos milisegundos cuando volvemos a comparar millis nos dará 4294966798, hacemos la resta y nos da 2. Bien, por ahora bien.

Pasa el tiempo y millis se desborda, con lo vale 0. Si hacemos la resta 0-4294966796 nos da: -4294966796, pero ojo, estamos tratando enteros sin signo, asi que no hay numeros negativos, con lo que el resultado es ¡¡ 500 !!, que es el tiempo que ha transcurrido.

Vale, poca fé tenéis. Coged la calculadora de windows, en modo programador, para poder convertir en hexadecimal. Ahora teclead 0-4294966796, el resultado que aparece será -4294966796. Convertirlo en hexadecimal. El valor que os da es: FFFFFFFF000001F4, pero arduino trabaja con 32 bits, asi que teneis coger solo 8 digitos (1 digito son 4 bits). Si lo haceis queda 0x000001f4 que son 500.

¡Tachan!¡Magia!. No, simple matemática binaria. Por eso aunque millis se desborde no ocurre nada si hacemos la resta.

Moraleja: Siempre haced millis()-tiempo >= duración, restad!

1 Like

Hasta aquí lo que llevo hecho, seguro que algún error he cometido y es de sabios aprender de los errores, así que si veis algo mal o que no se entienda bien, me lo decís he intento subsanarlo.

1 Like

Pues millones de gracias por dedicar tú tiempo a aclarar las dudad de los que no nos enteramos, como es mi caso. Realmente sigo sin saber como aplicarlo de una forma correcta, pero con lo que has escrito, sólo es ponerse las pilas y trastear.

Muchas gracias por tú tiempo.

Dejo un programa "chorra" que he desarrollado para que jugueis un rato

captura.jpg

En el podeis experimentar lo que se hace con millis.

Veréis cuatro cosas:

millis() que se incrementa cada segundo (a modo de millis, pero mas lento para que se vea).

t=millis(), cada vez que pulseis el botón, millis se guarda en la variable t.

millis()-t, cuando pulseis el botón empezara a realizar la operación millis()-t, es decir, calcula el tiempo que ha transcurrido desde que pulsamos el botón.

Un botón, simula cuando guardamos millis.

Recomiendo, jugar al menos 5 minutos, viendo que ocurre cada vez que guardamos millis.

PD. El programa lo he hecho con Lazarus, que es un compilador de Pascal, obsoleto ya lo sé, pero es de la pocas herramientas que permiten hacer aplicaciones rápidas sin tener que quebrarse la cabeza, así que deduzco que puede que a alguien no le vaya en su ordenador.

captura.jpg

entrenador.zip (795 KB)

Post genial y muy claro. Empezare mi codigo de nuevo con tus consejos.

¡Muchísimas gracias!

Muy interesante…

Ya me has hecho polvo… yo que acabo de empezar en arduino y estaba tan contento con mi delay

muchas gracias

millis() es un quebradero de cabeza, pero delay() es mucho mas ya que no funcionan las cosas, y creo que en Cuenca nos gustan las cosas fáciles.

Hola muy claro y didáctico tu tutorial. Si entendi bien es como correr el inicio del conteo a un punto de donde lo querramos?. Por lo menos eso veo en el entrenador. Seria como abrir intervalos de tiempo donde el programa se ejecuta o no en esos intervalos?. Si es asi quiero resolver que mi ESP8266 01s me permita encender una luz de forma manual mientras trata de conectarse a internet (por ejempli si se perdio la señal en un interruptor wifi).Vere de postear el pedido de ayuda con el codigo. Saludos

Muchísimas gracias victorjam, creo que mejor explicado es imposible, me ha servido de gran ayuda para la comprensión de millis, sobre todo ahora que estoy iniciándome en este tipo de programación. Voy a ponerlo en práctica.

Muchas gracias Victorjam

Estoy haciendo un probador de disyuntores diferenciales; en realidad con el arduino quiero hacer la medición, (metrología) del mismo y bueno, probaré con las dos interrupciones y con millis para medir el instante de inicio y el de final.
Gracias

Excelente! buscaba una guía para entender mejor este tema, gracias por tomarte el tiempo de explicarlo detalladamente.

Muy claro victorjam. Muchas gracias por tu tiempo.

victorjam:
Dejo un programa "chorra" que he desarrollado para que jugueis un rato

captura.jpg

Hola:

Muy bien explicado eso de los milis.

¿Qué programa o qué lenguaje hiciste esta aplicación?

Saludos.

Hay un PD en el post que lo pone: free pascal, usando el IDE lazarus.

Hola:

¿Puedes poner el código fuente de pascal?

Gracias. lo intentaré adaptarlo al Delphi 10, ya que lo tengo instalado.

Saludos.