26/1/15

Componentes electrónicos - El pulsador. Condiciones en lenguaje Arduino

Saludos, lectores. Vuelvo a la carga después de un mes de muchísimos líos, pero con las mismas ganas!

He de confesar que este artículo era uno de los primeros que se me ocurrieron para publicar, pero pensándolo mejor decidí retrasarlo porque ahora podré ejemplificar conceptos vistos en artículos anteriores para que queden más claros, y al mismo tiempo explicar cosas nuevas. En este caso avanzaré un poquito de programación en Arduino e introduciré otro elemento electrónico básico como es el pulsador. Pulsad en "Más información" y descubrid qué es lo que os voy a contar.

El pulsador es simplemente un interruptor momentáneo: dejará pasar la corriente únicamente mientras se mantenga pulsado, aunque se puede utilizar un truco para que se comporte como un interruptor permanente.

Aunque existen también de dos patillas, lo más normal será que los pulsadores tengan 4 patillas. El motivo de que sea así no lo tengo muy claro,aunque yo creo que es para que se pueda fijar más fácilmente en las placas de circuitos. Sea como sea, físicamente son 4 patillas pero realmente son 2, porque están unidas internamente en parejas. En la siguiente imagen (hecha con la cámara de mi móvil que es una puta mierda deja bastante que desear) os marco las patillas que hacen pareja, de tal forma que al presionar el pulsador se cerrará el circuito entre una pareja y otra.


Voy a hacer un paréntesis antes de continuar para explicar algunos conceptos teóricos importantes, como son los conceptos "analógico", "digital", o "lógica digital"...

Para empezar, tenemos que saber lo que es una magnitud: es una propiedad que puede ser medida. La velocidad, el peso, la presión atmosférica... todo eso son magnitudes. Pero una magnitud también puede ser "continua"/"analógica" o "discreta"/"digital".

¿En qué se diferencian un tipo de magnitud de otra? En los valores que puedan tener. Os pongo unos ejemplos para que se entienda mejor:
  • El peso: Una persona puede pesar 80 kg, u 83.2, u 83.25... Hay infinitos valores posibles.
  • El número de personas en una cola: Puede haber 3 personas, o 4, o 17... pero no puede haber 4.34 personas.
Ahí está la diferencia fundamental entre una y otra, entre la continua/analógica y la discreta/digital: en la primera hay infinitos valores posibles entre un valor y otro, mientras que en la segunda sólo son posibles unos valores concretos. Esto último es la clave de la electrónica digital. Sólo hay dos valores posibles: verdadero y falso, alto y bajo, 1 y 0.

¿Y por qué toda esta parrafada...? Porque la tensión de entrada de todo elemento electrónico es analógica, se puede alimentar con 5V, 7, 9.5, 12.3... lo que sea. Pero los componentes electrónicos trabajan con unos y ceros. ¿Cómo se sabe entonces si hay un cero o un uno cuando lo que hay es un cierto número de voltios...?

Para eso nuestra placa Arduino tiene unos circuitos electrónicos que se llaman ADC (Analog-Digital Converter) que se encargan de convertir una magnitud continua como es la alimentación en una magnitud discreta, los unos y ceros. Su funcionamiento se basa en intervalos y según la tecnología que utilicen estos intervalos serán diferentes, aunque de forma aproximada son así:
  • Nivel 0: Si la tensión de entrada está entre 0V y 1.5V, devolverá un 0.
  • Nivel 1: Si la tensión de entrada está entre 2 y 5V, devolverá un 1.
Si os paráis a pensar... ¿qué ocurre si en un determinado momento la tensión de entrada es por ejemplo 0.9V? ¿Es un 0 o es un 1? No es ni una cosa ni otra, sino un estado indeterminado, debido a ruido eléctrico o perturbaciones en la señal. Es algo que hay que evitar a toda costa, pues puede provocar funcionamientos erróneos en nuestro circuito, o en el peor de los casos dañar algún componente de forma irreversible.

Por este motivo he hecho el paréntesis, como introducción al siguiente concepto que voy a mostrar, las resistencias de pull-up.


Una resistencia de pull-up es simplemente una resistencia entre la alimentación (5V normalmente) y el elemento en el que queremos evitar el estado indeterminado. De esta forma se eleva la tensión para asegurar un 1 a la entrada del elemento y así evitar ese estado indeterminado de marras. En cuanto al valor de la resistencia, debe ser un valor alto: en la práctica se usan resistencias de 10K.

De la misma forma, están las resistencias de pull-down cuyo objetivo es justo el contrario: reducir la tensión para asegurar un cero, y para ello se pone la resistencia entre tierra y el elemento en cuestión.


Hasta aquí las resistencias de pull-up/pull-down, que resumiendo se puede decir que sirven para asegurar un nivel lógico cuando el sistema está en reposo. Ahora me toca hablar de otro concepto teórico, esta vez de programación: las condiciones.

Todo lenguaje de programación dispone de un mecanismo para decidir hacer una cosa u otra según se cumpla o no una condición. Sólo las condiciones podrían dar para escribir mil artículos, pues con un poco de habilidad y creatividad se puede hacer auténticas virguerías...pero eso lo dejo para otro artículo xD. De momento iré explicando cosas sencillitas, cuando haya alguna cosa más complicada ya me preocuparé de explicarla cuando surja.

Si tienes frío te pones un abrigo, si te pica te rascas, si no tienes dinero no te puedes comprar cosas... Siempre es lo mismo: tiene que ocurrir (o no) una cosa para que ocurra (o no) otra... ¿Qué utilidad tiene esto en el mundo Arduino? Por ejemplo sirve para encender un ventilador si la temperatura supera cierto nivel, o hacer una foto si se detecta movimiento. Como veis las posibilidades son muy amplias.

Para poner una condición en lenguaje Arduino se utiliza la palabra if. Veamos unos códigos de ejemplo...
int variable1 = 3;
int variable2 = 0;

if(variable1 > 0) {
  variable2 = -1;
}
 
Después de la palabra if se abren unos paréntesis, dentro de los que se escribe la condición a evaluar. En el código anterior se pregunta si el valor de variable1 es mayor que cero, y en caso de ser así entrará dentro y establecerá a -1 el valor de variable2. Si no se cumple, pues simplemente ignorará lo que hay dentro del if y continuará ejecutando el código que le siga.

También se puede establecer qué hacer si la condición no se cumple, con la cláusula else. Veamos...
int variable1 = 3;
int variable2 = 0;

if(variable1 > 0) {
  variable2 = -1;
}
else {
  variable2 = 3;
}
 
Si variable1 es mayor que cero, establece el valor de variable2 como -1. Si no, establece el valor de variable2 como 3.

Se puede incluso encadenar varias condiciones...
int variable1 = 3;
int variable2 = 0;

if(variable1 > 0) {
  variable2 = -1;
}
else if(variable1 < 0) {
  variable2 = 3;
}
else if(variable1 > 5) {
  variable2 = 0;
}
else {
  variable2 = -6;
}
Así especificamos alternativas para que el programa vaya por un lado u otro... Si se no se cumple la primera condición comprueba la segunda , si falla comprueba la tercera y si también falla ejecutará el código alternativo del último else. Pero si se cumpliera alguna de las tres, entraría dentro de ella y se terminaría el código, porque son varias alternativas pero sólo una se ejecutará.

Hasta aquí las condiciones por hoy, más adelante hablaré en más profundidad sobre ellas. Ahora vamos a cacharrear...

En primer lugar os voy a presentar el esquema electrónico de lo que voy a hacer, mediante el software libre y gratuito Fritzing, (que además también está para Linux), que sirve para montar circuitos electrónicos:

En electrónica se suele utilizar cables de color rojos la alimentación y cables negros para la tierra, y es el esquema de colores que yo utilizaré. El cable rosa va al pin 47 que será el que encienda o apague el LED al pulsar el botón, y el cable amarillo va al pin 49 que será el que lea el estado del pulsador (pulsado o no, cerrado o abierto). Como queremos que el circuito haga cosas cuando se pulse el botón, se ha puesto una resistencia de pull-down para asegurar que el Arduino lea un nivel lógico bajo cuando no esté pulsado.

Vamos con el código...
int pinPulsador = 49;
int pinLED = 47;
boolean estadoPulsador;

void setup() {
  pinMode(pinPulsador, INPUT);
  pinMode(pinLED, OUTPUT);
}

void loop() {
  estadoPulsador = digitalRead(pinPulsador);
  
  digitalWrite(pinLED,estadoPulsador);
}

Ahora vamos a ver el código poco a poco en cada una de sus partes.
int pinPulsador = 49;
int pinLED = 47;
boolean estadoPulsador;
Lo primero de todo es crearnos las variables necesarias para que el Arduino interactúe con nuestro circuito, que no son más que unos int que servirán para indicar con qué pin van a interactuar el pulsador y el LED, y un boolean para almacenar el estado del pulsador.
void setup() {
  pinMode(pinPulsador, INPUT);
  pinMode(pinLED, OUTPUT);
} 
Aquí tan simple como decir que el pin 47(el que va a leer el estado del pulsador) va a ser de entrada y el 49(el que enciende el LED) va a ser de salida.
void loop() {
  estadoPulsador = digitalRead(pinPulsador);
  
  digitalWrite(pinLED,estadoPulsador);
}
La función loop, el corazón de nuestro programa, y que ejecutará una y otra vez mientras el Arduino tenga alimentación. En primer lugar invoca a la función digitalRead para guardar el nivel lógico que hay en el pin que recibe como parámetro, pinPulsador en este caso: si cuando se llame a esta función el pulsador está pulsado, guardará un true y en caso contrario un false. Después simplemente invocará a la función digitalWrite que ya conocéis, pasándole el nivel lógico recién leído.

Este código a pesar de ser tan sencillo aún se puede mejorar, debido a que la variable que guarda el estado del pulsador no es ni necesaria:
int pinPulsador = 49;
int pinLED = 47;

void setup() {
  pinMode(pinPulsador, INPUT);
  pinMode(pinLED, OUTPUT);
}

void loop() {
  digitalWrite(pinLED,digitalRead(pinPulsador));
}
¿Para qué voy a guardar continuamente el estado del pulsador si directamente puedo recogerlo y sobre la marcha utilizarlo para pasarlo a la función digitalWrite? En vez de llamar a la función digitalRead, guardar su resultado en una variable y utilizar esa variable en la llamada a digitalWrite, puedo invocarla directamente en la llamada, y me ahorro esa variable.

¿Y qué hace este circuito? Mantener el LED encendido mientras esté pulsado el pulsador:

Pero... ¿Qué hacemos si queremos que el LED se encienda o apague y mantenga ese estado sin necesidad de mantener el pulsador pulsado? Se complica un poco el código, pero poco ;)
int pinPulsador = 49;
int pinLED = 47;
boolean estadoPulsador = false;
boolean ultimoEstado = false;
boolean estadoLED = false;

void setup() {
  pinMode(pinPulsador, INPUT);
  pinMode(pinLED, OUTPUT);
}

void loop() {
  //Averiguar el estado en este momento del pulsador
  estadoPulsador = digitalRead(pinPulsador); //1
  
  //Si es diferente del ultimo estado conocido
  if(estadoPulsador != ultimoEstado) { //2
    //Si se ha pulsado el boton
    if(estadoPulsador == HIGH) { //3
      //Se invierte el estado del LED y se manda la señal
      estadoLED = !estadoLED; //4
      digitalWrite(pinLED, estadoLED);
    };    
  }
  
  //Se actualiza el ultimo estado conocido
  ultimoEstado = estadoPulsador; //5
  
}

Como veis, se han añadido tres variables booleanas más, y el código dentro del loop ahora tiene más cosas. ¿Cómo funciona esto? He marcado algunas líneas del código con números, para que sea más entendible lo que se hace en cada paso:
  1. Primero se averigua el estado del pulsador (si está pulsado o no).
  2. Se compara con el último estado conocido del pulsador: si son diferentes quiere decir que el botón se ha pulsado. De esta forma se consigue que el programa entre por aquí solamente si se ha pulsado el botón. Como se ve, el operador para comprobar si dos variables contienen distintos valores es !=.
  3. Ha habido un cambio de estado, y si ese cambio es que el nuevo estado es el interruptor pulsado, entonces se hará algo.
  4. Se invierte el estado del LED, si antes estaba apagado ahora estará encendido, y viceversa. Como es una variable booleana, con el operador NOT (!) se invierte su valor, para a continuación mandar ese estado al LED en la línea siguiente.
  5. Esta es una línea fundamental para el buen funcionamiento del programa. ¿Recordáis que en la línea 2 se comparaba el estado recién leído con el último guardado? Aquí es donde se establece ese último estado guardado. Paraos a pensar: si no se ha pulsado el interruptor devolverá un false, la variable ultimoEstado estará a false, y por tanto no entrará dentro de la condición
Listo, ya tenemos un circuito en el que cuando se pulse el botón se apagará o encenderá el LED y se mantendrá así. Un vídeo para comprobarlo:

Con este vídeo ya me despido. Ha sido bastante denso este artículo, pero considero que es algo básico para que vayáis aprendiendo, y teniendo en cuenta todo el tiempo que estado sin escribir, tenía que ser algo más tocho xD.

Nos vemos en la próxima entrada, y sentíos libres de escribir los comentarios que os venga en gana.

¡Sed buenos!

No hay comentarios:

Publicar un comentario