Ejercicios de millis y botones

Después de crear un tutorial sobre millis y sobre botones voy a hacer una serie de ejercicios a modo de ejemplos como apoyo a esos post.

Obviamente, como requisito previo conviene haber leido ambos post antes:

Entender millis y no morir en el intento.

Como NO leer un boton y como SI debemos hacerlo.

Aquí iré poniendo una lista de los ejercicios para que sea más fácil de encontrar:

La lista se irá agrandando según se me ocurran ejemplos, o se me propongan algunos que sean interesantes y faciles de desarrollar. Por favor, no vengais a pedirme que os haga los ejercicios de clase, ya que perderéis el tiempo, por qué no los haré.

EJERCICIO 1:

Calcular el tiempo que ha estado un botón pulsado.

Como ya expliqué en el tutorial de los botones, tenemos cuatro estados: suelto, aprentandolo, apretado y soltandolo. Así que se trata ver el tiempo que transcurre entre el apretándolo y el soltándolo. ¿Cómo? mirando el valor que nos marca el reloj millis en cada situación.

/*
 * EJERCICIO1. Calcular el tiempo que un botón ha estado pulsado.
 *

// Libreria del botón.
#include <BotonSimple.h>

// Nuestro botón estará en el pin 2.
BotonSimple boton(2);

// Usamos dos variables para guardar el valor de millis. Una al inicio, cuando "apretamos"
// botón y otra al final cuando lo "soltamos".
unsigned long inicio, fin;

void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT);
}
  
void loop() {
  boton.actualizar();
  // Si estamos apretandolo guardamos el valor al inicio de millis.
  if ( boton.leer()==APRETANDOLO )
    inicio = millis();

  // Si estamos soltandolo tomamos el valor del final. El tiempo transcurrido entre el final 
  // y el inicio nos dará el tiempo que está el botón pulsado. Ese valor lo mostramos por el puerto serie.
  if ( boton.leer()==SOLTANDOLO ) {
    fin = millis();
    Serial.print(F("Has pulsado el botón durante "));
    Serial.print(fin-inicio);
    Serial.println(F(" milisegundos"));
  }
}

Este ejercicio se puede realizar sin libreria, pero ya sabeis como se ha de leer un botón correctamente sin ella. Solo sería cuestión de aplicar lo aprendido. Pero... ¿a que es más fácil asi?.

EJERCICIO 2.
Marcha-Paro con dos botones

La Marcha-Paro es una maniobra muy común en electricidad. Consiste en que mediante pulsadores se ponga en marcha un motor. Uno de los pulsadores "marcha", activa el motor y con mediante otro pulsador "paro" el motor se parará.

Para simular el motor, usaremos el pin 13 que ya sabeis que en la placa arduino tiene un led. Cuando nuestro "motor" esté en marcha se encenderá el led, y cuando esté parado el led se apagará.

Obviamente para ello necesitaremos dos botones y haremos uso de nuestra libreria:

#include <BotonSimple.h>

// Hemos instalado sendos botones en los pins 2 y 3. los llamamos
// de la manera que mejor nos plazca, en este caso "marcha" y
// "paro".
BotonSimple marcha(2);
BotonSimple paro(3);

void setup() {
  pinMode(13,OUTPUT);
}

void loop() {
  // Actualizamos el estado de lo botones.
  marcha.actualizar();
  paro.actualizar();

  // Comprobamos que hemos pulsado el boton de marcha y si lo hemos
  // hecho debemos encender el led.
  if ( marcha.leer()==APRETANDOLO ) digitalWrite(13,HIGH);

  // Comprobamos que hemos pulsado el botón de marcha y si lo hemos
  // hecho hemos de parar el led,
  if ( paro.leer()==APRETANDOLO ) digitalWrite(13,LOW);

}

Ejercicio 3.

Marcha-paro con un solo botón

Antes hicimos un marcha-paro con dos botones: apretabamos el botón de marcha y el motor se ponía en marcha y asi seguía hasta que pulsamos el botón de paro.

Ahora vamos a hacer lo mismo pero con un solo botón. Cuando pulsemos el botón, si el motor está parado se pondrá en marchar, si esta en marcha lo paramos.

Voy a proponer dos soluciones:

  • Usando una variable para controlar el estado del motor.
  • Sin usar una variable, actuando directamente sobre el pin de salida.

Opción 1:

/*
 * Marcha paro con un solo boton usando variable.
 */
#include <BotonSimple.h>

// Lo vamos a hacer con solo un botón, asi que lo creamos.
BotonSimple boton(6);

// Usamos una variable para indicar el estado del motor.
// La creamos como un booleano, así si vale TRUE signficará
// que el motor está en marcha, si vale FALSE el motor estará
// parado.
bool motor=false;

void setup() {
  // Simulamos el "motor" con el led de la placa.
  pinMode(13,OUTPUT);

}

void loop() {
  boton.actualizar();
  // Comprobamos el boton y si estamos apretandolo vemos el estado
  // del motor.
  if ( boton.leer()==APRETANDOLO ) {
    if ( motor==true ) { // Si el motor está en marcha lo apagamos.
      motor = false;
      digitalWrite(13,motor);
    }
    else { // El motor está parado lo arrancamos.
      motor = true;
      digitalWrite(13,motor);
    }
  }

}

Opción 2:

/*
 * Marcha paro con un solo boton sin usar variable.
 * 
 * 
 * Cuando un pin lo ponemos como salida podemos saber su estado, ¿cómo? Usando
 * digitalRead. Si un pin lo ponemos como salida usando pinMode(pin,OUTPUT), podemos
 * leer su estado haciendo un simple digitalRead(pin). 
 * 
 */
#include <BotonSimple.h>

// Lo vamos a hacer con solo un botón, asi que lo creamos.
BotonSimple boton(6);

void setup() {
  // Simulamos el "motor" con el led de la placa.
  pinMode(13,OUTPUT);

}

void loop() {
  boton.actualizar();
  // Comprobamos el boton y si estamos apretandolo vemos el estado
  // del motor.
  if ( boton.leer()==APRETANDOLO ) {
    // Leemos el estado de la salida usando digitalRead, con el ! lo negamos
    // Ejemplo: si la salida vale 1, cuando lo leemos nos da 1, y si lo negamos
    // valdrá 0, y ese valor es el que escribimos.
    digitalWrite(13, !digitalRead(13));
  }
}

EJERCICIO 4.

Hacer que un led parpadee al apretar un botón.

Aquí el primer ejemplo con millis.

Ya hemos visto en el tutorial como se ha de hacer un parpadeo: miramos el reloj y si ha transcurrido el tiempo cambiamos el estado del led, anotando el tiempo, para hacer otra pasada.

Y ¿cómo controlamos el parpadeo con un botón? Usando una variable. Si variable es cierta se hará el parpadeo, si es falsa no hará nada. Dicha variable la controlamos con el botón. Si el variable es falsa y hemos apretado el botón debemos: ponerla a cierta para que haga el parpadeo; encender el led; mirar el tiempo actual para tenerlo en cuenta. Si la variable es cierta solo tenemos que apagar el led y poner dicha variable a false para que no parpadee.

/*
 * Controlar el parpadeo de un led con un botón. Si apretamos el botón el
 * led parpadea. Si lo volvemos a apretar, el led se apaga.
 */
#include <BotonSimple.h>

// Nuestro botón.
BotonSimple boton(6);

// Una variable de tiempo.
unsigned long t;

// Una variable para controlar el parpadeo.
bool debeParpadear = false;


void setup() {
  // El led será el de la placa.
  pinMode(13,OUTPUT);

}

void loop() {
  boton.actualizar();
  
  // Comprobamos si debe parpadear. Si es que si hemos de controlar
  // el tiempo de parpadeo del led.
  if ( debeParpadear==true ) {
    // Hacemos el control del parpadeo, el tiempo está almacenado en la variable t
    // He optado por que parpadée cada 100 ms.
    if ( millis()-t > 100 ) {
      digitalWrite(13, !digitalRead(13)); // "Negamos" el led.
      t = millis(); // Guardamos el tiempo actual para comprobar en la siguiente pasada.
    }
  }

  // Para controlar el parpadeo debemos ver si pulsamos el boton. Miramos si estamos
  // apretándolo.
  if ( boton.leer()==APRETANDOLO ) {
    // si debeParpadear es falsa, tendremos que hacer que sea cierta y además guardar el
    // tiempo, que usamos para hacer el parpadeo, también encendemos el led.         
    if ( debeParpadear==false ) {
      debeParpadear=true;
      t = millis();
      digitalWrite(13,HIGH);
    }
    else { 
      // debeParpadear es cierto, por lo tanto, debemos apagar el led y decir que
      // que no parpadée mas. Puede que el led esté apagado o encendido al hacerlo,
      // ya que no miramos que estaba haciendo en este momento.
      debeParpadear=false;
      digitalWrite(13,LOW);           
    }
  }
}

Yo Molestando otra vez Me sale este error ? se que es sobre una libreria que tengo que instalar pero la busco y no la encuentro esta en particular tengo una Button pero aun me sigue ese error.

#include <BotonSimple.h>

compilation terminated.
exit status 1

BotonSimple.h: No such file or directory

En el post del tutorial Como No leer un botón y como SI debemos está la libreria como attachment.

Te dejo el enlace aquí tambien: BotonSimple.zip

hola victorjam, no sé como hacerlo, el ejercicio1 quiero que el tiempo que me muestre a cada "soltandolo" sea el incremental (+ el anterior) y no consigo que vaya, tengo esto puesto al final del loop, pero mis conocimientos son minimos, me puedes ayudar?

// * EJERCICIO1. Calcular el tiempo que un botón ha estado pulsado.
// *

// Libreria del botón.
#include <BotonSimple.h>

// Nuestro botón estará en el pin 19.
BotonSimple boton(19);

// Usamos dos variables para guardar el valor de millis. Una al inicio, cuando "apretamos"
// botón y otra al final cuando lo "soltamos".
unsigned long inicio, fin, total;
   
void setup() {
  Serial.begin(9600);
  //pinMode(13, OUTPUT);

}
  
void loop() {
   

  
  boton.actualizar();
  // Si estamos apretandolo guardamos el valor al inicio de millis.
  if ( boton.leer()==APRETANDOLO )
    inicio = millis();

  // Si estamos soltandolo tomamos el valor del final. El tiempo transcurrido entre el final
  // y el inicio nos dará el tiempo que está el botón pulsado. Ese valor lo mostramos por el puerto serie.
  if ( boton.leer()==SOLTANDOLO ) {
    fin = millis();


    total = fin-inicio;

  
    
  }

if ( boton.leer()==SOLTANDOLO ) {
  Serial.print(F("Has pulsado el botón durante "));
    Serial.print((total)/1000);
    
    Serial.println(F(" segundos"));
  
}
total++;
}

Si quieres que te muestre el tiempo "total" vas bien encaminado, pero has olvidado una cosa:

total = fin-inicio;

Con ese código solo almacenas el tiempo de la pulsación actual, porque no sumas. Sustituyelo por:

total = total + (fin-inicio);

Y en cada SOLTANDOLO te sumará lo que ya tenías de antes, con la pulsación actual. No he utilizado el operador += que se usa para hacer esto, por que asi lo entiendes mejor.

Aquí tienes el código, además he añadido un contador de pulsaciones, cada vez que sueltes el botón te dice:
lo que ha estado pulsado ahora, el total de tiempo y el número de veces que has pulsado el botón.

#include <BotonSimple.h>


BotonSimple boton(19);
unsigned long inicio, fin, total;
int contador;
   
void setup() {
  Serial.begin(9600);
  total = 0;
  contador = 0;
}
 
void loop() {
  boton.actualizar();
  if ( boton.leer()==APRETANDOLO )
    inicio = millis();
  if ( boton.leer()==SOLTANDOLO ) {
    fin = millis();
    total += (fin-inicio);
    contador++;
    Serial.print("El boton ha estado pulsado: "); Serial.println(fin-inicio);
    Serial.print("Total de tiempo que el botón ha estado pulsado: "); Serial.println(total);
    Serial.print("El número de pulsaciones es ahora: "); Serial.println(contador);
  }
}

EJERCICIO 5.

Marcha-paro con un botón y tiempo de encendido.

En este ejercicio vamos a realizar la marcha-paro de un motor con un solo botón, es decir, si el motor está parado se pondrá en marcha al apretar el botón, y se parará si estaba en marcha. La diferencia es que ahora el motor funcionará solo durante un determinado tiempo cuando se ponga en marcha y se apagará solo.

Para ello usaremos un temporizador y la función millis. Usaremos una variable boolena para saber el estado del motor (true, encendido; false, apagado).

/*
 * EJERCICIO 5
 * 
 * Marcha-Paro con tiempo.
 * 
 * Al pulsar el botón el motor se encenderá. El motor se apagará si volvemos
 * a pulsar el botón o cuando transcurra un tiempo de marcha.
 * 
 */
#include <BotonSimple.h>

// El tiempo de marcha lo definimos como una constante en este caso, si el tiempo
// fuera variable (cambiando el tiempo con otro botón por ejemplo) lo declarariamos
// como una variable normal (sin el const).
const unsigned long TIEMPO_MARCHA=5000;

BotonSimple boton(5); // El botón
unsigned long t;      // El temporizador.
bool motor = false;   // Estado del motor, inicialmente apagado.

   
void setup() {
  Serial.begin(9600);
  pinMode(13,OUTPUT);
  digitalWrite(13, LOW);
}
 
void loop() {
  // Leemos el botón.
  boton.actualizar();
  // Comprobamos que estamos soltando el botón.
  if ( boton.leer()==SOLTANDOLO ) {
    // Si el motor está apagado debemos encenderlo, de paso tenemos que guardar
    // el tiempo actual para controlar despues el intervalo de apagado.
    if ( motor==false ) {
      motor = true;
      t = millis();
      digitalWrite(13, HIGH);
    }
    else {
      // Si el motor está en marchar solo debemos apagarlo.
      motor = false;
      digitalWrite(13, LOW);
    }
  }

  // Comprobamos que el motor está en marcha y miramos el tiempo que ha
  // transcurrido desde que lo encendimos, si el tiempo es mayor debemos
  // apagar el motor.
  if ( motor && millis()-t > TIEMPO_MARCHA ) {
    motor = false;
    digitalWrite(13, LOW);
  }
 
}

EJERCICIO 6.

Parpadeo con tiempo de actividad y apagado

Este fué una pregunta que hizo el usuario Salvador_22 y me lo anoté como ejercicio ya que lo vi interesante ya que mezcla dos temporizadores. Se trata de que un led parpadee a una frecuencia durante un tiempo y esté apagado durante otro tiempo distinto.

Obviamente harán falta dos temporizadores: una para el parpado del led y otro para controlar el tiempo de encendido/apagado. También habrá que tener una variable de estado que te indique en que momento de actividad estamos (si el parpadeo está activo o no).

En el código encontraréis los comentarios de como usarlos.

  /*
   *  Parpadeo con tiempo de actividad y apagado.
   *  
   *  Necesitamos un temporizador para el parpadeo y otro para comprobar el tiempo que 
   *  debe estar activo o apagado. Para controlar si está activo o no se necesita de 
   *  una variable auxiliar para indicarlo.
   *  
   *  Dichar variable será "estado". Si estado es cierta (true) significa que el led debe
   *  parpadear. Si es falsa (false) no debe parpadear.
   *  
   *  Si "estado" es cierta debemos comparar el tiempo transcurrido en el temporizador con
   *  el tiempo de de encendido, si ha pasado dicho tiempo, entonces apagamos el led y 
   *  cambiamos de estado a false. Aquí también debemos hacer el parpadeo, asi que tenemos
   *  que comparar el tiempo transcurrido con el tiempo de parpadeo.
   *  
   *  Si "estado" es false, solo debemos comparar el tiempo con el el tiempo que debe estar
   *  apagado, si ha pasado, estado volverá a valer true.
   *
   */
  const unsigned long tiempo_encendido = 10000;
  const unsigned long tiempo_apagado = 20000;
  const unsigned long tiempo_parpadeo = 500;
  
  bool estado;                // falso significa apagado, cierto significa parpadeando;
  unsigned long t;            // una variable donde guadaremos millis para el parpadeo
  unsigned long t2;           // en esta variable guardaremos el tiempo para el cambio de estado;
  
  void setup() {
    pinMode(13,OUTPUT);
    estado=true;
    t2 = t = millis();
  }
  
  void loop() {
    // Si estado es true significa que debemos hacer el parpadeo.
    if ( estado==true ) {
      // Miramos el tiempo de parpadeo, si ha pasado el tiempo debemos apagar o 
      // encender el led.
      if ( millis()-t > tiempo_parpadeo ) {
        digitalWrite(13, !digitalRead(13));
        t=millis();
      }
      // Hemos de comprobar también si ya ha pasado el tiempo que debe estar
      // parapadeando, si ha pasado, tenemos que apagar el led y cambiar el
      // estado a falso.
      if ( millis()-t2 > tiempo_encendido ) {
        t2=millis(); // Guardamos el valor.
        digitalWrite(13, LOW); // Apagamos el led.
        estado=false;
      }
    }
    // estado es false, asi que no parpadea solo tenemos que comprobar que transcurre
    // el tiempo de apagado.
    else { 
      // Aqui no parpardeamos, solo comprobamos que transcurra el tiempo que debe 
      // estar apagado. Si ha transcurrido debemos guardar el tiempo y cambiar el
      // estado a true para que de nuevo parpadee.
      if ( millis()-t2 > tiempo_apagado ) {
        t2=millis(); // Lo guardamos.
        estado=true;
      }
    }
  }

EJERCICIO 7

Luces del coche fantástico.

Este ejercicio es muy básico y se puede realizar usando delay, pero en él se notará muy claramente el efecto de usar delay y no millis. Si quereis podéis intentar hacerlo usando delay (antes de usar este código) e intentad cambiar la velocidad, veréis como resulta imposible hacerlo y que como mucho conseguiréis cambiar la velocidad cada 8 segundos.

Se trata de hacer que una luz se mueva de izquierda a derecha, y de derecha a izquierda. Para ello realizaremos este montaje:

lucesfan.gif

(ampliad la imagen para verla mejor).

Montaremos 8 leds y le añadiremos dos botones: uno para subir la velocidad del barrido y otro para bajarla.

/*
 * EJERCICIO 7.
 * 
 * Luces del coche fantástico.
 * 
 * Vamos a conectar unos leds a los pines del Arduino, no olvidar de poner su resistencia
 * limiatadora, y crear las luces del coche fantastico. Añadiremos dos botones: subir y 
 * bajar, para aumentar/disminuir la velocidad. Usaremos 8 leds.
 * 
 * Cada led irá en un pin del arduino, para no tener que escribir un montón usaremos un
 * array "pins" donde se guarda el número de pin. Podremos acceder a cada pin del array
 * usando pins[x] (x, es un indice del array).
 * 
 * Usaremos la variable "secuencia" para determinar que led se debe encender en cada 
 * momento: si secuencia es 0, se enciende el led 0, si 1 el led 1, hasta el 7, que se
 * encenderá el led 7.Observamos que en este caso la secuencia coincide con el valor  
 * del array (el pin) a encender.
 * 
 * Existe otra variable "incremento" que nos servirá para aumentar/disminuir la secuencia.
 * Asi si incremento vale uno, secuencia en cada paso irá aumentando en uno: 0, 1, 2, 3, 4,
 * 5, 6, 7. Cuando la secuencia llegue a 7 no nos interesa que sigua incrementado sino que
 * haga todo los contrario, con lo que hacemos que incremento valga -1. Asi en cada paso
 * irá restando: 7, 6, 5, 4, 3, 2, 1, 0. Cuando lleguemos a 0 volvemos a poner la secuencia
 * a +1 y la variable se irá incrementando.
 * 
 */

#include <BotonSimple.h>

const int pins[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };

BotonSimple botonSubir(8); // El botón que subirá la velocidad.
BotonSimple botonBajar(9); // El botón que bajará la velocidad.

unsigned long tiempoSecuencia;         // La variable de tiempo que nos indica cuando cambiar de secuencia.
unsigned long duracionSecuencia = 100; // La duración de la secuencia.

int secuencia;  // Secuencia actual.
int incremento; // Incremento.

int i; // Una variable auxiliar para los bucles.

void setup() {
  // Iniciamos las salidas.
  for (i=0; i<8; i++) {
    pinMode(pins[i], OUTPUT);
    digitalWrite(pins[i], LOW);
  }
  // Empezamos la secuencia en 0.
  secuencia=0;
  // El incremento es 1 (de pin a pin).
  incremento = +1;
}

void loop() {
  // Leemos los botones.
  botonSubir.actualizar();
  botonBajar.actualizar();

  // Si el tiempo trasncurrido desde que tomamos el valor de tiempoSecuencia es
  // mayor que lo que debe durar la secuencia, tenemos que cambiar la secuencia
  // y apagar encender los leds correspondientes a la secuencia.
  if  ( millis()-tiempoSecuencia> duracionSecuencia ) {
    for (i=0; i<8; i++) {
      if ( secuencia==i ) {
        digitalWrite(pins[i], HIGH);
      }
      else {
        digitalWrite(pins[i], LOW);
      }
    }
    secuencia+=incremento; // Pasamos a la siguiente secuencia.
    if ( secuencia==0 ) incremento=1;   // Cuando la secuencia es cero debemos sumar
    if ( secuencia==7 ) incremento=-1;  // Cuando la secuencia es siete debemos restar.
    tiempoSecuencia = millis(); // Volvemos a guardar el tiempo.
  }

  // Leemos el boton y aumentamos la velocidad, es decir, disminuimos la
  // duracion de la secuencia. Controlamos que no valga menos de 10.
  // Si la duración de la secuencia fuera 0, daria la sensación de que todos los
  // leds están encendidos.
  if ( botonSubir.leer()==APRETANDOLO ) {
    if ( duracionSecuencia>10 ) duracionSecuencia-=10;
  }
  // Leemos el boton y disminuimos la velocidad, es decir, aumentamos la 
  // duracion de la secuencia, la limitamos a 2 segundos entre leds.
  if ( botonBajar.leer()==APRETANDOLO ) {
    if ( duracionSecuencia<2000 ) duracionSecuencia+=10;
  }
  
}

Para ver el resultado: VIDEO
PD. Este ha costado mas trabajo explicarlo, espero haberlo logrado.

lucesfan.gif

muchisimas gracias, pero ahora, si quiero darle una vuelta de más, si instalo un reloj rtc, el DS3231 por ejemplo, debo seguir usando la funcion millis para seguir ejecutando codigo, de tal manera que me gustaría poder hacer foto del momento justo cuando es presionado el pulsador, no se si me explico... intento hacerlo y te comento por aqui si te parece bien, o mejor en el foro de software?

Por no desvirtuar el tutorial y dado que es para resolver una duda de tu programa continualo en la sección de software.