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. Debes cuidar la polaridad del LED de forma que el ánodo sea mas positivo que el cátodo, pero no importa en que parte pongas la resistencia. El otro tema a tener en cuanta es si quieres que el LED se prenda con un HIGH (circuito actual), donde el LED esta entre el pin de salida y GND o con un LOW en cuyo caso el LED estaría entre +5V y el pin de salida.

En esta oportunidad usaremos una resistencia en pull-down (resistencia a GND) en el lado sensado del botón pulsador, es decir el pin de entrada estará normalmente en LOW y cuando pulsamos pasa a HIGH. Si lo quisieras al revés solo tienes que invertir el botón y la resistencia que ahora se llamara pull-up (resistencia a +5V), es decir el pin de entrada estará normalmente en HIGH y cuando pulsamos pasa a LOW.

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 la sección setup(). En el sección 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 errática verdad ? Inclui 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 código 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 ?

5. Solución 2

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. Para mejorar este código usare una interrupción externa para poder detectar solamente el flanco de subida (RISING).

void setup(){
   Serial.begin(9600); //Inicia monitor serie
   pinMode(4, OUTPUT); //LED
   pinMode(2, INPUT);  //Pulsador
   digitalWrite(4, LOW);  //Apaga LED
   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
   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 final

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
   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 leídas por el Arduino. Es por eso que la solución 2 mato 2 pájaros de un tiro y sin saberlo al añadir el delay(), por eso si comentas la instrucción delay() observaras nuevamente el comportamiento aparentemente es errático. Consulta Electrónica Práctica Aplicada.

Si puedes mejorar o simplificar mas aun este código de ejemplo dímelo aquí