En el tercer RETO comenzamos a trabajar con actuadores en Arduino, en este caso con servomotores.
Los actuadores son componentes que, a partir de un valor digital, pueden variar nuestro entorno, por ejemplo, a través de movimientos. Más adelante veremos también cómo producir sonidos, medir temperaturas o la intensidad de la luz.
Un servomotor es un dispositivo similar a un motor que tiene la capacidad de ubicarse en cualquier posición dentro de su rango de operación (en nuestro caso de 0º a 180º, aunque los hay de 360º) y mantenerse en dicha posición. A través de su programación, podremos alterar tanto su velocidad como su ángulo. Una sencilla aplicación a nuestro mundo real sería una barrera en un paso a nivel (parking, vía de tren…), en este caso modificaríamos su valor desde 0º a 90º > 90º a 0º para levantar > bajar la barrera.
.
.
A veces necesitamos librerías (programas ya hechos por terceros) que nos aporten funcionalidades o nos simplifiquen la programación. En definitiva lo que hacen estas librerías es ampliar el “medio ambiente” de actuación de nuestra placa Arduino aportando funcionalidades extra, por ejemplo para actuadores que no viene predefinidos de serie.
Arduino cuenta con un buen número de ellas, pero en ocasiones es necesario descargarlas o crear las nuestras propias. Cuando se da este caso tendremos que instalarlas para poder ejecutarlas. Puedes ampliar esta información visitando el siguiente enlace.
En este RETO vamos a utilizar una librería que viene por defecto en el IDE (Integrated Development Environmet) de Arduino, la denominada librería “Servo“ (Servo.h).
Nuestro servomotor tiene 3 cables: positivo o tensión (rojo), negativo o masa (negro o marrón oscuro) y señal digital, en este caso salida (amarillo) [es importante tener claros los conceptos y no confundir una entrada digital -como el botón- con una salida digital -como el servo-]. Pues bien, esta salida digital recibirá los grados en los que se posicionará la biela de nuestro servo.
El esquema del montaje es como se puede ver a continuación:
.
.
Para comprobar su correcto montaje podemos cargar el ejemplo situado en EJEMPLOS > SERVO > SWEEP y comprobar cómo nuestro servo gira su biela 180º. Podemos ver en el código cómo hace uso de las estructuras de control For para construir bucles de repetición de instrucciones, tal y como ya vimos al programar nuestro coche fantástico (RETO 1):
Prestaremos especial atención a la función myservo.write(pos) de la librería “Servo” que va a ser la encargada de hacer que nuestra biela se posicione en un rango de posibilidad de 0º a 180º, en la posición que determine la variable “pos”.
IMPORTANTE RECORDATORIO:
Indicar que la instrucción: int pos = 0; cumple una doble función, por una parte le está indicando a nuestro microcontorlador que reserve un espacio de memoria para ocuparlo con el dato de esta variable [si hubiera sido un número constante habríamos escrito const int] y por otra parte está inicializando ese valor de tipo número entero y, en este caso concreto, asignándole el valor inicial de cero.
Una vez comprobado que funciona bien, podemos añadir nuevas instrucciones para que nuestra biela vaya directamente a las posiciones 0º, 90º y 180º y entonces produzca un movimiento continuo colocándose en esos 3 ángulos sucesivamente. Bastaría con duplicar la instrucción myservo.write(pos) y variar los valores de la variable “pos”.
El código a cargar sería el siguiente:
.
#include <Servo.h> Servo myservo; // create servo object to control a servo int pausa=500; void setup() { myservo.attach(9); // attaches the servo on pin 9 to the servo object } void loop() { myservo.write(0); delay(pausa); myservo.write(90); delay(pausa); myservo.write(180); delay(pausa); myservo.write(90); delay(pausa); myservo.write(0); delay(pausa); }
.
AMPLIACIONES para el SERVO de 180º:
- Controlar un servo en función del valor de un potenciómetro (resistor variable) como input analógico.
- Controlar un servo a través del Monitor Serial y que se posicione en el ángulo que le indiquemos.
- Controlar el movimiento del servo mediante una estructura de control FOR, tal y como aprendimos aquí.
- Controlar un servo en función de un botón a modo de barrera que sube a 90º desde 0º y viceversa. Al mismo tiempo que haya un semáforo de tres leds (rojo, verde y amarillo). Este reto se explica a continuación:
.
Una vez ya controlado nuestro servo, para seguir con la construcción del PARKING DOMÓTICO, tendremos que realizar el montaje y añadir la programación de los tres leds y dos botones en nuestra placa de prototipado dejando un buen espacio en el medio que lo destinaremos más adelante a conectar y cablear el DISPLAY, tal y como se muestra aquí. El objetivo de nuestro código, de momento, será que nuestros elementos se comporten de la siguiente manera:
Estado normal: Semáforo en rojo y barrera bajada > si pulsamos botón de entrada (o de salida) > el semáforo cambiará de color > la barrera subirá > tiempo de espera para que cruce el vehículo > un parpadeo del led verde avisará de que se termina el tiempo de cruce > la barrera bajará lentamente volviendo a su posición inicial al mismo tiempo que un parpadeo del led amarillo avisa de su bajada > el semáforo cambiará de color volviendo a estar en rojo y recuperando su estado normal.
CÓDIGO para el SERVO de 180º + BOTÓN + 3 LEDS (rojo, verde y amarillo):
// PRIMERA ZONA ******************************** #include <Servo.h> Servo myservo; int i = 0; int ledR = 8; int ledV = 7; int ledA = 6; int pushButton = 2; int buttonState = 0; // SEGUNDA ZONA ******************************** void setup() { myservo.attach(9); pinMode(ledR, OUTPUT); pinMode(ledV, OUTPUT); pinMode(ledA, OUTPUT); Serial.begin(9600); pinMode(pushButton, INPUT); } // TERCERA ZONA ******************************** void loop() { buttonState = digitalRead(pushButton); if (buttonState == HIGH){ delay (2000); digitalWrite( ledR , LOW); digitalWrite( ledV , HIGH); myservo.write(90); delay(4000); digitalWrite( ledV , LOW); for (i = 0 ; i < 5 ; i++) { digitalWrite( ledA , HIGH); delay (300); digitalWrite( ledA , LOW); delay (300); } for (i = 0 ; i < 10 ; i++) { digitalWrite( ledA , HIGH); delay (100); digitalWrite( ledA , LOW); delay (100); } } else { myservo.write(0); digitalWrite( ledR , HIGH); digitalWrite( ledV , LOW); digitalWrite( ledA , LOW); } }
AMPLIACIÓN ÚNICAMENTE DESTINADA A VERDADEROS VALIENTES:
Una ampliación muy interesante sería conseguir que nuestra barrera bajase al mismo tiempo que el semáforo anuncia su cierre, por ejemplo durante una segunda fase de parpadeo más rápido del semáforo verde o un tercer led de color amarillo, como ya os he comentado, ¿no creéis?
Ya que hemos aprendido a usar la estructura de control FOR en la construcción del coche fantástico, vamos a utilizarla para este caso.
También añadiremos dos nuevos conceptos, los OPERADORES GENERALES:
• El módulo %
• La división /
Y el OPERADOR DE COMPARACIÓN:
• La negación !
Los operadores generales, como has podido intuir son operadores aritméticos que nos permiten realizar cálculos matemáticos.
Los operadores aritméticos que se incluyen en el entorno de programación son suma, resta, multiplicación y división. Estos devuelven la suma, diferencia, producto, o cociente (respectivamente) de dos operandos.
Las operaciones se efectúan teniendo en cuanta el tipo de datos que hemos definido para los operandos (int [número entero sin decimales con un rango de -32,767 a 32,768], long [número entero sin decimales con un rango de -2147483648 a 2147483647], float [número decimal], etc..). Por ejemplo, si definimos 9 y 4 como enteros “int”, 9 / 4 devuelve de resultado 2 en lugar de 2,25 ya que el 9 y 4 son valores de tipo entero “int” (enteros) y no se reconocen los decimales con este tipo de datos. Revisa este documento.
Los OPERADORES DE COMPARACIÓN sirven para realizar comparaciones de una variable o constante con otra, se utilizan con frecuencia en las estructuras condicionales del tipo if… para testear si una condición es verdadera.
Existen los siguientes:
x == y // x es igual a y
x != y // x no es igual a y
x < y // x es menor que y
x > y // x es mayor que y
Volviendo a nuestro caso, podremos aprovechar la bajada de la barrera (cambio de posición de 90° a 0°) controlándola con una estructura for de tal forma que en cada resta de 1° grado podamos mover el servo a esa posición y cada 10° también hacer que el led cambie de estado (efecto de parpadeo) de tal forma que parpadee 9 veces. En otras palabras, conseguir que parpadee a una determinada velocidad a la vez que baja la barrera.
Traducido esto a un algoritmo sería de la siguiente forma:
.
.
EXPLICACIÓN de la TABLA:
- ÁNGULO (pos) son todos los ángulos por los que irá pasando y posicionándose el servo gracias a la estructura de control for: for (pos = 90; pos > 0; pos–). En este caso como es de bajada de la barrera del parking irá de 90° > 0°.
- /10 Es el cociente resultado de dividir el ángulo entre 10, despreciando los decimales ya que esta variable como bien recordarás fue declarada de tipo entero. Al despreciar los decimales los desprecia todos, incluso si fuera 0,99
- %10 Es el resto resultado de dividir el ángulo entre 10, por ejemplo 89 % 10 = 8 y de resto 9.
- (pos%10) En esta columna se reinterpretan los valores de la anterior columna aplicando la siguiente premisa: el programa cuando lee cero (0) en realidad lee FALSO; y cuando lee uno u otro valor lee VERDADERO.
- !(pos%10) Esta columna simplemente es la negación de la anterior.
Es importante recordar un momento que las instrucciones de tipo condicional if… se ejecutan cuando la condición es verdadera, es decir su respuesta es cierta, se ejecutará el conjunto de instrucciones siguientes, y si es falsa pasará de largo…
if (v) > ejecuta
if (f) > no ejecuta
Por tanto con el algoritmo !(pos%10) lo que conseguimos es que únicamente en los ángulos múltiplos de 10 (subrayados en amarillo) podamos ejecutar la instrucción que queramos (en este caso el cambio de estado del led) al mismo tiempo que vamos moviendo nuestro servo ángulo a ángulo. Es una de las formas más sencillas de realizar multitareas a pesar de tener un solo microcontrolador, el cual está limitado a leer la secuencia de instrucciones siempre en orden sucesivo y no poder ejecutar otra tarea hasta haber realizado la anterior.
.
for (pos = 90; pos > 0 ; pos--) // Va desde 90 grados a 0 grados. { if (!(pos%10)) { // Yellow LED toggle (invertir estados) sólo una vez cada 10 ejecuciones /* Toggle led status */ if (ledStatusYellow == HIGH) { // Invertir (o toggle) el estado del led amarillo sea cual sea a su llegada, esto es solo a nivel de memoria pero no escribimos nada. ledStatusYellow = LOW; } else { ledStatusYellow = HIGH; } /* Write led status to output */ digitalWrite(ledPinY, ledStatusYellow); // Aquí escribo el valor en el led del estado anteriormente leido (que lo acabamos de invertir). } myservo.write(pos); // BARRERA BAJANDO: comandar al servo posicionarse en el valor de la variable "pos", un grado menos en cada bucle del for(). delay(15); // Esta instrucción myservo.write(pos); se ejecutará 90 veces. }
DOCUMENTO PARA AUTOEVALUAR 1ª FASE
.