Prender y apagar LED

Se trata de un simple proyecto en el que al pulsar el botón el LED deberá prender y al volver a pulsar el botón deberá apagar el LED. Muy simple no ?

1. Circuito propuesto

Siempre debes usar una resistencia en serie con el LED para limitar su corriente, tipicamente de 220~330 ohms. En esta oportunidad estamos usaremos una resistencia en pull-down (resistencia a GND) en el lado sensado del botón pulsador. Es decir el pin de entrada estara normalmente en LOW y cuando pulsamos pasa a HIGH.

Prende y apagar LED

2. Componentes

Los componentes son muy simples y no requieren mayor comentario.

3. Código Arduino

Primero debemos inicializar los pines que usaras en setup(). En el loop() nada ocurre si el pulsador no esta presionado (HIGH). Si lo esta se consulta si el LED esta prendido, si lo esta se apaga de lo contrario se prende. El código Arduino es el siguiente.

void setup(){
   Serial.begin(9600);        //Inicia monitor serie
   pinMode(4, OUTPUT);        //LED
   pinMode(2, INPUT);         //Pulsador
   digitalWrite(4, LOW);      //Apaga LED
}

void loop(){
   if (digitalRead(2) == HIGH){
      if (digitalRead(4) == HIGH){
         digitalWrite(4, LOW); //Apaga LED
      }else{
         digitalWrite(4, HIGH); //Prende LED
      }
	   Serial.println(digitalRead(4));
   } 
}

Se comporta de manera erratica verdad ? Incluí el Serial.println(digitalRead(4)); para darte una pista del problema. Recuerda que Arduino ejecuta la sección loop() miles de veces por segundo, de modo que todo el tiempo que tengas pulsado el estado del LED cambiara. Ver monitor serie muchos 0 y 1s.

4. Solución 1

Para mejorar este circuito introduciré un retardo suficiente para que se detenga el loop() y darte tiempo de soltar el botón. Ademas aprovechare de contarte que en los if(condición) hay una redundancia. Para que una condición sea valida su resultado debe ser TRUE o FALSE, pero recuerda que son sinónimos de TRUE el HIGH y cualquier numero distinto de 0, así mismo son sinónimos de FALSE el LOW y el 0. Así que la condición digitalRead(2) == HIGH es verdadera si el pin 2 tiene un HIGH, pero resulta que digitalRead(2) por si misma es verdadera si el pin 2 esta en HIGH y no requiere preguntar por su equivalencia. Prueba el siguiente código:

void setup(){
   Serial.begin(9600);      //Inicia monitor serie
   pinMode(4, OUTPUT);      //LED
   pinMode(2, INPUT);       //Pulsador
   digitalWrite(4, LOW);    //Apaga LED
}

void loop(){
   if (digitalRead(2)){
      if (digitalRead(4)){
         digitalWrite(4, LOW);  //Apaga LED
      }else{
         digitalWrite(4, HIGH); //Prende LED
      }
	   Serial.println(digitalRead(4));
	   delay(3000);              //Retardo de 3 segundos
   } 
}

Mucho mejor verdad ? Pero y que pasa si en lugar de usar el HIGH que no sabemos cuanto durara tratamos de usar el flanco de subida que debería ser uno solo sin importar el tiempo que dure el pulso.

5. Solución 2

Para mejorar este circuito usare una interrupción externa para poder detectar solamente el flanco de subida.

void setup(){
   Serial.begin(9600);      //Inicia monitor serie
   pinMode(4, OUTPUT);      //LED
   pinMode(2, INPUT);       //Pulsador
   digitalWrite(4, LOW);    //Apaga LED
   digitalPinToInterrupt(2);
   attachInterrupt(digitalPinToInterrupt(2), pulsa, RISING);
}

//Función de interrupción
void pulsa() {
   if (digitalRead(4)){
      digitalWrite(4, LOW);  //Apaga LED
   }else{
      digitalWrite(4, HIGH); //Prende LED
   }
	Serial.println(digitalRead(4));
}

void loop() {
	//Nada
}

6. Solución 3

Ahora que les parece si retiramos la resistencia de 10K que estamos usando como pull-down y la cambiamos por un pull-up por software al declarar el pinMode() para el pulsador.

Prende y apagar LED
void setup(){
   Serial.begin(9600);           //Inicia monitor serie
   pinMode(4, OUTPUT);           //LED
   pinMode(2, INPUT_PULLUP);     //Pulsador
   digitalWrite(4, LOW);         //Apaga LED
   digitalPinToInterrupt(2);
   attachInterrupt(digitalPinToInterrupt(2), pulsa, RISING);
}

//Función de interrupción
void pulsa() {
   if (digitalRead(4)){
      digitalWrite(4, LOW);  //Apaga LED
   }else{
      digitalWrite(4, HIGH); //Prende LED
   }
	Serial.println(digitalRead(4));
}

void loop() {
	//Nada
}

Te hago notar que ahora el pin 2 esta normalmente en HIGH y que al pulsar pasara a LOW produciendo lo que se llama el flanco de bajada (FALLING) y luego al soltar se producirá el flanco de subida (RISING).

7. Solución 4

Ahora simplificaremos aun mas nuestro código evitando preguntas estúpidas y haciendo que Arduino actúe sin consultar.

void setup(){
   Serial.begin(9600);
   pinMode(4, OUTPUT);        //LED
   pinMode(2, INPUT_PULLUP);  //Pulsador
   digitalWrite(4, LOW);      //Apaga LED
   digitalPinToInterrupt(2);
   attachInterrupt(digitalPinToInterrupt(2), pulsa, RISING);
}

//Función de interrupción
void pulsa() {
   digitalWrite(4, !digitalRead(4));    //conmuta LED
   Serial.println(digitalRead(4));
}

void loop() {
	//Nada
}

Ademas cada vez que pulsas el botón se generan unas micro-chispas que son leidas por el Arduino. Es por eso que la solucion 2 soluciono 2 pajaros de un tiro y sin saberlo al añadir el dalay(), por eso si comentas la instrucción delay() observaras nuevamente el comportamiento aparentemente es erratico. Consulta Electrónica Práctica Aplicada.

8. Solución final

void setup(){
   Serial.begin(9600);      //Inicia monitor serie
	DDRD = 1<<PD4;     //LED, pone D4 como salida
   pinMode(2, INPUT);       //Pulsador
   PORTD = 0<<PD4;    //Apaga LED
}

void loop(){
   if (digitalRead(2)){
      if (digitalRead(4)){
		   PORTD = PORTD ^ 32;
         digitalWrite(4, LOW);  //Apaga LED
      }else{
         digitalWrite(4, HIGH); //Prende LED
      }
	   Serial.println(digitalRead(4));
	   delay(3000);              //Retardo de 3 segundos
   } 
}