Arduino Arduino es una plataforma de desarrollo en base a un microcontrolador ATMEL de código abierto y hardware libre. Por hardware libre se entiende a todos los dispositivos cuyas especificaciones y diagramas son de acceso público, de manera que cualquiera puede replicarlos. Esto quiere decir que Arduino ofrece las bases de información para que cualquier otra persona o empresa pueda crear sus propias placas, pudiendo ser diferentes entre ellas pero igualmente funcionales al partir de la misma base. El software libre son los programas informáticos cuyo código está disponible para que cualquiera pueda utilizarlo y modificarlo. Una plataforma de desarrollo es una plaqueta con un uC a la que puedo cargarle un programa para que ejecute y desarrolle ciertas tareas. (básicamente un prototipo) Actualmente hay varias placas de desarrollo; Arduino y Raspberry Pi son las más conocidas. A diferencia de Raspberry, que es sistema similar a una microcomputadora del tamaño de una tarjeta de crédito, Arduino es una plataforma que cuenta con varias opciones de placas y microcontroladores, aunque todos de la línea ATMEL. Los tres tipos de Arduino más populares son: NANO, UNO y MEGA. Las dos primeras versiones cuentan con un microcontrolador (uC) ATMEGA328 mientras que el Arduino MEGA cuenta con un ATMEGA2560 (mucho más potente que el anterior). Veamos la placa de un Arduino UNO, que es prácticamente un estándar en la industria. 1 A primera vista se destaca el microcontrolador. Hay otras versiones de Arduino UNO en los que el uC es de montaje superficial, sin zócalo. Obviamente funciona igual, el inconveniente es que si se daña ese circuito integrado, al no poder reemplazarlo hay que tirar la placa. Hay un conector para la alimentación y otro conector para la conexión a una PC de forma de cargarle el programa. Este es un conector USB (microUSB en otros tipos de Arduino). Hay un segundo microcontrolador en la placa, está indicado como ATmega16U2, este es siempre de montaje superficial y permite la comunicación entre la placa y la computadora que le carga el programa. (algunas placas de Arduino UNO pueden tener otros circuitos integrados para cumplir con esta función) Ambos microcontroladores tienen los cristales que les dan la señal de sincronismo (reloj/ clock). En el caso del ATmega16U2 se lo ve a simple vista, está a su izquierda en la foto. El cristal del microcontrolador principal es mucho más chico, difícil de distinguir a simple vista. A la derecha del conector de alimentación hay un regulador serie del tipo 7805 que baja la tensión de alimentación a los 5 volts que requieren los microcontroladores para operar sin correctamente. A la derecha del conector USB hay un pulsador que es el Reset. Al oprimirlo el sistema se reinicia y comienza a ejecutar el programa cargado en el ATmega328 desde cero. Finalmente a los costados de la placa están dispuestas las entradas y salidas del sistema. Son pines numerados que permitirán enviar señales a perisféricos (leds, motores, válvulas, reles, display, etc.) si se los configura como salida o bien leer datos (sensores, pulsadores, interruptores, teclados, etc.) si se los configura como entradas. Algunos de estos pines también llevan tensiones para alimentar componentes externos, tanto de 5 como de 3,3 v. Hay otros pines especiales que veremos al momento de necesitarlos. Arduino además de la placa ofrece la plataforma Arduino IDE (Entorno de Desarrollo Integrado), que es un software de programación con el que cualquiera puede crear aplicaciones para las placas Arduino. Este entorno de programación es una aplicación que se instala en la PC y permite escribir los programas en un lenguaje similar al C++, compilarlo (transformar las instrucciones entendibles para el ser humano en un archivo binario ejecutable) y luego conectarse con la placa para “subirle el programa”. Esta interfase de programación también cuenta con una cantidad de librerías y ejemplos básicos. Las librerías son pequeños programas que permiten realizar operaciones tales como conectar la placa Arduino con un módulo LCD sin ninguna programación adicional por parte del programador más que aclarar que quiere que se vea en el display. La carga del programa desde la PC al Arduino es posible porque el uC ya cuenta con un programa cargado que permite la comunicación. A ese programa se lo llama Bootloader. Para aprovechar la enorme potencialidad de estas plataformas de desarrollo lo primero es comprender el lenguaje que permite programarlas, y eso es lo que haremos a continuación. Por empezar diremos que un programa es una secuencia ordenada de instrucciones, que el microcontrolador ejecutará una por una y en órden. Dada la velocidad a la que el uC ejecuta instrucciones (puede ejecutar cada instrucción en algunos microsegundos), al ser humano le da la sensación de que muchas ocurren al mismo tiempo pero, realmente el controlador ejecuta una luego de haber ejecutado la anterior. 2 A los programas de Arduino se los llama scketch, y son los que luego de compilados se cargarán en la plaqueta Arduino para que esta los ejecute. Todo scketch constará al menos de dos funciones: void setup () { } Entre las llaves se colocan todas las instrucciones de configuración del programa, que se ejecutarán una sola vez ni bien se alimente al Arduino. Un ejemplo de instrucciones que se colocan en la función setup (configuración) son las que configuran los pines para que trabajen como entrada o como salida. Una vez definidas dentro del setup, el programa las podrá utilizar convenientemente. Otro ejemplo podría ser configurar la velocidad a la que se comunica el Arduino con un módulo Bluetooth. Una vez configurada en el setup, esa velocidad se mantendrá siempre constante. La segunda función será el programa propiamente dicho void loop () { } En este caso, las instrucciones constituyen el programa que queremos desarrollar y se ejecutarán siempre desde la primera a la última para luego volver a la primera y ejecutarse nuevamente. Este ciclo se repite indefinidamente mientras el Arduino se encuentre alimentado. Esta es la razón por la que esta función se llama “loop”. Primeras Instrucciones: El lenguaje de programación es “case sensitive”, es decir que diferencia entre minúsculas y mayúsculas. Esto quiere decir que hay que respetar exactamente la escritura de las instrucciones o los programas no se compilaran e indicarán errores. pinMode: Instrucción que se escribe dentro de la función setup (eso quiere decir entre sus llaves), con el objetivo de configurar los pines del Arduino para que trabajen como entrada o como salida 3 Por ejemplo, si voy a conectar un LED en el pin 10 del Arduino con la intención de encenderlo y/o apagarlo , ese pin se debe configurar como salida (ya que el programa tendrá que escribir un 1 o un 0 en cada caso). La instrucción será: pinMode (10, OUTPUT); La instrucción genérica sería pinMode (número de Pin, estado) Nótese que detrás de cada instrucción hay que escribir punto y coma (;). Si por el contrario, el Arduino tiene que “leer” el estado de un sensor que por ejemplo le indique si una ventana está abierta o cerrada (algo que se haría con un pulsador o con un sensor magnético), el pin que recibe esa información se debe configurar como entrada. Supongamos que conectemos el sensor en el pin 4, la instrucción correcta será: pinMode (4,INPUT); digitalWrite: Esta instrucción se utiliza para “escribir” un pin del Arduino que previamente haya sido configurado en el setup como salida. Se puede escribir un “cero” o un “uno”. Escribir un uno significa que luego de ejecutar la instrucción, en ese pin habrá 5 volts mientras que luego de escribir un “cero”, el pin tendrá 0 volts. Por ejemplo, si tengo conectado un LED en el pin 8 y quiero encenderlo, tengo que escribir un “uno” en ese pin. La instrucción será: digitalWrite (8, HIGH); Como vemos se utiliza el término “HIGH”, en lugar de “uno”, significa que escribo un “estado alto”. Si luego quiero apagar el LED conectado en ese pin, la instrucción será: digitalWrite(8, LOW); En este caso se utiliza el término “LOW”, en lugar de “cero”. En este caso escribo un “estado bajo”. digitalRead: Esta instrucción se utiliza para “leer” el estado de un Pin del Arduino que antes fue configurado como entrada. Como resultado de la lectura se puede obtener un “uno” o un “cero”. Se obtendrá un “uno” si la tensión en ese Pin es de 5 volts, o sea un estado alto (HIGH). Se obtendrá un “cero” si la tensión en el Pin al momento de leerlo es de 0 volts, es decir un estado bajo (LOW). El estado de la señal colocada en el Pin por medio de algún tipo de sensor (podría ser un pulsador, un sensor de temperatura, de iluminación, etc.) puede ir cambiando con el 4 tiempo por lo que cuando se utiliza esta instrucción, el valor del estado que se lee debe ser almacenado para utilizarlo más adelante. Supongamos que se deben leer varios pulsadores y tomar una decisión al final de acuerdo a su estado. Cómo el Arduino lee uno por vez, cada vez que lo hace debe almacenar el estado y al final analizar a todos juntos. La forma de almacenar el estado de una entrada al momento de leerla lo que se debe hacer es guardar el dato en una variable, que no es más que una posición en la memoria interna del microcontrolador. La instrucción general sería Variable = digitalRead (Pin); El ejemplo típico sería estado = digitalRead (7); En este caso, el Arduino leerá el Pin 7 y guardará el valor leído (ya sea un “uno” o un “cero” en la variable que llamamos estado. La variable lógicamente puede tener el nombre que querramos. Es conveniente dar a las variables nombres asociados a las informaciones que manejan para luego identificarlas facilemente. Cuando un programa tiene pocas variables pero, a medida que aumente su complejidad y con ello la cantidad de variables que utilicemos, identificarlas fácilmente será de gran ayuda. Por ejemplo si quisiéramos saber si en un habitación hay oscuridad o claridad podríamos escribir la siguiente instrucción luminosidad = digitalRead (5); O en el caso de estar desarrollando una alarma, si tenemos el sensor de presión del asiento del conductor en la entreda 9 podríamos escribir peso = digitalRead(9); Las variables que vayamos usando en un programa se deben definir al comienzo del mismo. Estas variables toman valores binarios (1 o 0) así que al comienzo del programa (antes de la función “setup”) debemos escribir: boolean luminosidad; boolean peso; Aunque 1 y 0 también son enteros por lo que es frecuente definirlas como: int estado; int luminosidad; Donde int identifica a las variables que toman valores enteros. Existen otros tipos de variables como char, byte, long, word, float, y otras que veremos en breve. 5 Alias: Los Alias nos permiten identificar fácilmente, por medio de “etiquetas” o “alias” los distintos elementos del hardware del Arduino. Es decir, les podemos dar un nombre. Supongamos por ejemplo que en el Pin 3 tenemos instalado un led azul. Si a lo largo de un programa tengo muchas instrucciones que encienden o apagan ese led nos encontraremos con instrucciones del tipo: digitalWrite (3, HIGH); .............. .... . . . . . . . . . . .. . . . . . . . .. . . . . . .. . . . . . .. . . digitalWrite (3, LOW); . .. . . . . . . . .. . . . . .. . . . .. . . . . .. . . . .. . . . .. . . . .. . . . . .. . . . .. . . . digitalWrite (3, HIGH); ……………………….. ……………………….. Podría haber muchas instrucciones de este tipo a lo largo del programa. Si por alguna razón (y ocurre con frecuencia) quisiera cambiar el led de Pin, digamos pasarlo del Pin 3 al 5, tendría que ubicar todas las instrucciones donde aparezca y cambiar el 3 por 5. Esto quiere decir que tendría hacer una gran cantidad de operaciones. Si en lugar de hacer eso declaro un Alias al principio del programa escribiendo: azul= 3; (lo que digo es que cada vez que aparezca la palabra azul, el progra ma lo considerará como un 3) ahora el programa quedaría: digitalWrite (azul, HIGH); . . . . . . . . . . . . . . . .. . . . . . . .. . . . . . . . . . . . . . .. . . . . . .. . . . . . .. . . digitalWrite (azul, LOW); . .. . . . . . . . .. . . . . .. . . . .. . . . . .. . . . .. . . . .. ………………………….. . . . .. . . . . .. . . . .. . . . … digitalWrite (azul, HIGH); ………………………….. ………………………….. 6 y si por alguna razón tengo que cambiar de Pin, lo único que tendría que hacer es ir al Alias y cambiar por: azul= 5 Es decir, resuelvo el problema cambiando una sola instrucción que se donde se encuentra delay( ): Esta instrucción detiene al procesador un tiempo expresado en milisegundos que se escribe entre los paréntesis. Supongamos que quiero que el microcontrolador no haga nada por el término de 2 segundos, escribiría: delay(2000); Hola Mundo Se suele definir de esta forma, “hola mundo”, al primer programa que se desarrolla en un lenguaje de programación. La IDE de Arduino ya viene con un ejemplo que obra de Hola Mundo. Es un programa que sirve para verificar que la placa está funcionando correctamente, sin necesidad de instalar ningún componente adicional, ya que tiene un led instalado en el Pin 13 visible al usuario. El programa se llama blink, y es el siguiente: int led=13; void setup() { pinMode(led, OUTPUT); } void loop() { digitalWrite(led, HIGH); delay(1000); digitalWrite(led, LOW); delay(1000); } 7 Como vimos en la página 4, hay una instrucción que permite leer el estado de una entrada del Arduino, esa instrucción es digitalRead Esta instrucción le permite al Arduino conocer datos del mundo exterior, es decir, recibir información de hechos que ocurren fuera de él. Por ejemplo, podemos colocar un pulsador en una entrada del arduino y esta instrucción nos permitiría saber si alguien lo está oprimiendo o no. De igual forma podríamos conectar algún tipo de sensor a una entrada del arduino. Por ejemplo supongamos querer saber si se abre una ventana para activar una alarma. Una forma de hacerlo es colocar un sensor magnético (un pulsador que se activa por medio de un imán) en una puerta o ventana y, cuando se abren cambiarían su estado, de cerrado a abierto, y esto sería registrado por el arduino. El sensor sería del tipo Una de las dos partes tiene un iman en su interior y la otra parte un interruptor magnético del tipo Que tiene dos tiras metálicas que se pegan cuando se acerca el imán y se despegan cuando se aleja. 8 Este es sólo un ejemplo, hay muchos tipos de sensores como para determinar si es de día o de noche, si llueve o no, si alguien aplaude, si hay gas en un ambiente, etc., que se pueden conectar a una entrada del arduino para que éste los lea y de acuerdo a que estén abiertos o cerrados realice algún tipo de acción. Veamos como se conectaría el pulsador a un arduino En este circuito vemos un pulsador conectado a la entrada 10 del arduino. Como funciona? Si el pulsador está abierto (no están conectados sus terminales por lo que es lo mismo que si no existiera) los 5 volts de la fuente llegan a la entrada 10 a través de la resistencia R2. Las entradas del arduino no toman corriente por lo que no hay caída de tensión en la resistencia, o sea que la tensión en el pin de entrada van a ser los 5 volts. Si alguien oprime el pulsador, sus terminales se tocan y la entrada 10 queda conectada a masa, es decir que la tensión en la entrada se va a 0 volts. Circulará corriente desde la fuente de 5volts a masa a través de R2 pero, como la resistencia se elije de un valor alto (10 KΩ es un valor típico), su valor es mínimo. Resumiendo, cuando el pulsador está abierto hay un “1” en la entrada y cuando el pulsador está oprimido hay un “0”. 9 Para poder aprovechar esa información, tenemos que ver una nueva instrucción que es posiblemente la instrucción más poderosa de los sistemas digitales. Esto es porque les permite a los microcontroladores tomar decisiones, es decir actuar como los seres humanos. If – Else: (traducido significaría si – sino ) Esta instrucción permite tomar decisiones a partir del estado de una variable. Si la variable tiene el valor de la lectura de un Pin del Arduino, la decisión que se termina tomando depende del estado del Pin leído. El formato de la instrucción es: If (condición) { Entre estas llaves se escriben las instrucciones (puede ser una sola o muchas) que el Arduino ejecutará SI se cumple la condición } else { Entre estas llaves se escriben las instrucciones (puede ser una sola o muchas) que el Arduino ejecutará si NO se cumple la condición } Siguiente instrucción a ejecutar luego de ejecutar el if-else (A) Es decir, Si se cumple la condición escrita entre paréntesis luego del If, el Arduino ejecutará las instrucciones que están entre las llaves que le siguen. Si no se cumple la condición que se encuentra entre paréntesis, el Arduino ejecutará las instrucciones que se encuentran debajo de la palabra else. Luego de ejecutar las instrucciones (ya haya sido que se encuentran entre las primeras llaves o entre las segundas llaves), el arduino continuará ejecutando las instrucciones que aparezcan luego de las llaves del else, es decir donde se encuentra la letra A. 10 Veamos un ejemplo con el circuito que acabamos de ver, donde hay un pulsador en el Pin 10 del Arduino y un LED en el Pin 7. Supongamos que queremos que al oprimir el pulsador se encienda el Led y cuando no está oprimido que el Led esté apagado… Un posible programa puede ser (siempre hay más de una forma de escribir un programa que realice una tarea)… int led=7; int pulsador=10; int estado; void setup() { pinMode (led, OUTPUT); pinMode (pulsador, INPUT); } void loop() { pulsador = digitalRead(pulsador) If ( pulsador == LOW) { digitalWrite (led, HIGH); } else { digitalWrite (led, LOW); } } En este programa, al comienzo se escribieron los Alias, del led y del pulsador. Se definió luego la variable donde se va a guardar la información de la lectura. A esta variable se la llamó “estado” (porque guarda el estado de la lectura, pero podría tener cualquier nombre. Se la definió como variable entera (int) ya que va a tener valores enteros (0 y 1) pero también se podría haber definido como variable boolean. Luego se definieron los pines que se van a utilizar como entrada y como salida en la función setup. El pulsador envía información al Arduino así que el Pin que recibe la información debe definirse como una entrada. 11 Finalmente, en el programa principal ( loop, que se repetirá indefinidamente) primero se lee el estado del pulsador con la función digitalRead y se guarda el estado leído (0 si el pulsador está abierto y 1 si está cerrado) y se guarda ese valor en la variable estado. En la instrucción siguiente (if) se compara la lectura para saber si es 0. Nótese que la comparación se hace con LOW, no se escribe 0. Es decir se pregunta si el valor leído es LOW, que significa preguntar si el valor es “bajo”. Si se quisiera preguntar si el estado de la variable es 1 se preguntaría if(pulsador==HIGH) Otra cosa que se debe notar es que, para comparar se repite el signo =, es decir se escribe dos veces. Si se escribe una vez, la comparación no funcionará, es decir que está mal. Si se cumple la condición, es decir si el estado de la variable es 0, es decir si al leer el Pin del arduino el pulsador estaba oprimido, el Arduino ejecutará las instrucciones que se encuentran entre las llaves que están debajo. En este caso eso significa escribir un estado alto (un 1) en el Pin 7, es decir encender el Led. Si no se cumple la condición, es decir si el estado de la variable es 1, es decir si al leer el Pin del arduino el pulsador estaba abierto, el Arduino ejecutará las instrucciones que se encuentran entre las llaves que están debajo del else. Ya sea que el arduino haya ejecutado las instrucciones de las primeras llaves luego del IF o de las segundas, luego continuá el programa. En este caso no hay más instrucciones así que se vuelve al comienzo del loop, es decir a leer de nuevo el Pin de entrada y nuevamente a tomar la decisión de encender o apagar el led. Este ciclo se realizará eternamente a menos que se interrumpa la energía. for : Hemos visto que la función “loop” en un bucle infinito o ciclo infinito. Esto quiere decir que todas las instrucciones que se encuentren dentro de esta función se ejecutarán desde la primera a la última para luego retornar a la primera y volver a ejecutarse y este proceso continuará mientras el Arduino esté alimentado. Esta no es la única forma de generar bucles o ciclos. Existen instrucciones que nos permiten realizar tareas en forma repetitiva, ya sea una cantidad determinada como una cantidad indeterminada de ocasiones. A estas instrucciones también se las llama bucles o ciclos. Ciclos determinandos. Son aquellos que nos permiten realizar un grupo de operaciones repetitivas una determinada cantidad de veces. El ejemplo más simple de este tipo de bucles es la función for. 12 Esta instrucción tiene el siguiente formato for ( variable = valor inicial; valor final; incremento) { Instrucción 1 Instrucción 2 ……………. ……………. ……………. Instrucción N } El encontrar una instrucción de este tipo, el Arduino ejecutará en forma ordenada, de la primera a la última, las instrucciones que se encuentren entre las llaves inmediatamente posteriores a la sentencia for. La cantidad de veces que las ejecute estas instrucciones dependerá de los valores que tome la variable. Veamos un ejemplo for ( int x=1 ; x <10 ; i++) { } Supongamos como ejemplo que en un proyecto necesito definir los pines que van del 0 al 13 como salida dentro de la función setup. Una opción sería escribir la siguiente secuencia de instrucciones: void setup () { digitalWrite (0, OUTPUT); digitalWrite (1, OUTPUT); digitalWrite (2, OUTPUT); digitalWrite (3, OUTPUT); digitalWrite (4, OUTPUT); digitalWrite (5, OUTPUT); digitalWrite (6, OUTPUT); digitalWrite (7, OUTPUT); digitalWrite (8, OUTPUT); digitalWrite (9, OUTPUT); digitalWrite (10, OUTPUT); digitalWrite (11, OUTPUT); digitalWrite (12, OUTPUT); digitalWrite (13, OUTPUT); } 13 Una forma mucho más eficiente de resolver este problema sería: void setup () { for ( int i = 0; i<14 ; i++) { digitalWrite (i, OUTPUT); } } Cuando llegue a la sentencia for, el Arduino asignará el valor inicial, 0 en este caso, a la variable i, por lo que la primera instrucción que ejecutará luego será: digitalWrite (0, OUTPUT); Luego de ejecutarla volverá al for (ya que es la única instrucción incluida en el ciclo y se fijará si llegó al límite superior, 14 en este caso. Como no llegó, le sumará 1 por lo que i ahora valdrá 1 (0 + 1). Luego ejecutará la instrucción entre llaves. digitalWrite (1, OUTPUT) Esta secuencia se repetirá hasta que i llegue a 14 entonces, en lugar de ejecutar la instrucción entre llaves debajo del for, continuará el programa por la siguiente instrucción. Resolver Para el siguiente circuito, escribir el programa que, al presionar el pulsador haga que los 10 leds titilen a la vez (enciendan y apaguen todos juntos) 5 veces. Una vez que dejaron de titilar, si se vuelve a oprimir el pulsador nuevamente deben titilar los 10 leds. Mientras no están titilando los leds debe encontrarse el led del Pin13 (incluido en el Arduino) encendido. 14 While () Vimos que la función for nos permite hacer ciclos o bucles determinados y de esa forma ejecutar secuencias repetitivas de instrucciones sin tener que escribirlas más que una vez. Esa sentencia no es la única para realizar este tipo de operación. Un bucle del tipo while es un bucle de ejecución continua que se repite mientras se cumpla la expresión colocada entre paréntesis en la cabecera del bucle. En esa expresión existirá una variable que irá cambiando su valor hasta que la condición deje de cumplirse y entonces el Arduino abandonará el ciclo y seguirá por la instrucción siguiente. Las instrucciones que se repiten serán las que se encuentren entre las llaves que se escribirán inmediatamente debajo del While. En un diagrama de flujo se vería así: Mientras se cumpla la condición A se realiza la tarea B, cuando no se cumpla termina el bucle. Veamos un ejemplo int cuenta=0; while ( cuenta < 200) // el ciclo se a repetir mientras cuenta sea menor a 200 { digitalWrite (13,HIGH); // enciende el led del Arduino delay (500); digitalWrite (13,LOW); delay (500); cuenta ++; // incrementa la variable en 1 } digitalWrite (4,HIGH); ………………………. ………………………. ………………………. 15 Este pequeño programa se repite mientras (while en inglés) la variable cuenta valga menos de 200. Al entrar al bucle la variable cuenta vale 1 ya que ese valor se le cargó previamente. Luego de encender y apagar el Led, el Arduino le suma 1 al valor de la variable cuenta de forma tal que luego de la primera pasada cuenta valdrá 2. Como todavía es menor que 200 volverá a ejecutar las instrucciones que se encuentran entre las llaves. Al final de esta segunda pasada cuenta valdrá 2 por lo que se volverá a repetir. Luego de haber encendido y apagado el led 200 veces , el Arduino abandonará el lazo y seguirá por la instrucción que continúe debajo de la llave de cierre, en este caso es colocar un 1 en el Pin 4. Si al momento en el que el Arduino ejecuta la sentencia While, la condición no se cumple, las instrucciones que se encuentran en el bucle no se ejecutarán ninguna vez. Esto podría ocurrir si dentro del paréntesis, la variable que se compara tuviese el valor de una entrada del Arduino. Esto quiere decir que la variable se comprar con una lectura digital (digitalRead). De esta forma podemos hacer que una o varias instrucciones se ejecuten según sea el estado de un elemento externo al Arduino como podría ser una llave, un pulsador, un sensor de cualquier tipo, que una fuente de tensión esté encendida o apagada, etc. Veamos un caso con un pulsador Según como instale el pulsador se podrían dar cualquiera de las dos condiciones: Caso 1: Cuando presiono el pulsador leo un 1, mientras no lo presiono leo un 0 A la resistencia que se encuentra entre la entrada y masa (generalmente de 10K) se la denomina Pull-Down, ya que tira hacia abajo (a 0 volts) el valor de la tensión de entrada. 16 Caso 2. Cuando presiono el pulsador leo un 0, mientras no lo presiono leo un 1 A la resistencia que se encuentra entre la entrada y el positivo (generalmente de 10K) se la denomina Pull-Up, ya que tira hacia arriba (a 5 volts) el valor de la tensión de entrada. Usando el primer esquema (leyendo un 1 al oprimir el pulsador), vamos a escribir un programa que parpadee un led mientras el pulsador está oprimido y deje de hacerlo cuando no lo está. Supongamos que el pulsador está conectado al Pin 5 del Arduino y el Led al Pin 3. El sketch sería: Vemos que luego de configurar los pines en el setup, el Arduino entra a la función loop. Lo primero que hace es leer el pulsador y salvar el dato que lee en la variable pulsador. 17 Luego, ejecuta el bucle (ciclo o lazo) si al leer la entrada el pulsador estaba oprimido, es decir si pulsador es igual a HIGH (valor alto, 1) Dentro del lazo enciende y apaga el led y luego chequea el pulsador para ver si todavía sigue oprimido, para continuar en el bucle. En caso de que no, abandona el bucle. Si el pulsador no estaba oprimido, la variable pulsador tendrá un 0 (LOW), por lo que no ejecutará las instrucciones, solo continuará pero como ya no hay programa volverá al inicio, por lo que volverá a leer el pulsador y decidirá nuevamente si ejecuta las instrucciones del bucle o no. Dentro del While, al momento de la comparación, no es necesario usar una variable para guardar la lectura, ya que el dato se utiliza en ese preciso momento y tomando la decisión. Por este motivo este programa también podría ser escrito de la siguiente manera. Si quiero crear un bucle ininterrumpido, es decir que se repita una indeterminada cantidad de veces, podría escribir while (1) { Instrucción 1 Instrucción 2 Instrucción 3 ……………. ……………. Instrucción N } Ya que 1 siempre es igual a 1, este ciclo se repetirá por siempre. 18 do – While El bucle do while funciona de la misma manera que el bucle while, con la salvedad de que la condición se prueba al final del bucle, por lo que el bucle siempre se ejecutará al menos una vez. do { digitalWrite (13,HIGH); delay (500); digitalWrite (13,LOW); delay (500); cuenta ++; // enciende el led del Arduino // incrementa la variable en 1 } while (cuenta <200); Resolver 1 - Realizar el programa escrito para resolver el último ejercicio (el de los 10 leds que titilan) utilizando la función While. 2 – Para el siguiente circuito, escribir el sketch que: cuando se oprima el pulsador P1 el dígito muestre la letra H y al oprimir el pulsador P2 muestre la letra L. Utilice la instrucción While en lugar de IF 19 Switch – case (break, default) Al igual que if, la sentencia “ switch – case” controla el flujo del programa especificando en el programa que código se debe ejecutar en función del valor que toma una variables. En este caso en la instrucción switch se compara el valor de una variable sobre los valores especificados en la instrucción case. Esta instrucción es como realizar una secuencia de preguntas (una secuencia de instrucciones If, pero mucho más eficiente) En el siguiente diagrama de flujo, de acuerdo a que la variable tome como valores la “opción 1“, “la opción 2”, etc., el arduino realizará una determinada operación (instrucción 1, o instrucción 2 o …….., instrucción N). Si la variable no toma como valor ninguna de las opciones, el arduino realizará la acción por default. Luego continuará por la siguiente instrucción. Este esquema puede ser reemplazado por una instrucción switch, cuyo diagrama de flujo se vería como en el siguiente dibujo : 20 La sintaxis de esta instrucción sería: switch (var) { case Opc 1: instrucción/es 1; break; case Opc 2: instrucción/es 2; break; case Opc 3: instrucción/es 3; break; default: instrucción/es por default; break; } Siguiente instrucción; Default indica las instrucciones que se deben realizar cuando la variable no tome como valor ninguna de las opciones. El comando break indica el fin de cada opción para el que el arduino continue ejecutando la siguiente instrucción 21 Resolver Tenemos tres leds de distinto color conectados a un Arduino. El led rojo está conectado al Pin 8 del Arduino, el led verde al Pin 9 y el led azul al terminal 10. El pulsador se encuentra conectado al Pin 7 del Arduino de forma que al oprimirlo genera un estado LOW (0 volt), mientras se encuentra inactivo el estado en ese pin es HIGH (5volts). Utilizando la función Switch, desarrollar el sketch que permita que cada vez que oprima el pulsador cambie el led que está encendido, los otros dos deben permanecer apagados. AnalogRead Hemos visto que para leer el estado de un pin que ha sido configurado como entrada en un Arduino existe la función DigitalRead. La placa Arduino Uno contiene 6 canales (8 canales en el Mini y Nano, 16 en el Mega), analógicos con convertidor analógico a digital de 10-bit. Eso significa que, mediante la instrucción AnalogRead, el Arduino puede leer la señal que se encuentra en una de esas 6 entradas (A0, A1, A2, A3, A4 y A5) y devolver un número proporcional a esa tensión en una palabra digital de 10 bits. En su expresión más simple podemos decir que, a tensiones de entrada entre 0 y 5 volts le corresponderán lecturas entre 0 y 1023 0 volt 0000000000 0 ´´´´´´´´´´´´´´´´ 5 volts 1111111111 1023 22 Si tengo 0 volt en la entrada al momento de leerla, el resultado de la lectura me dará 0. Si tengo 5 volts en la entrada al momento de leerla, el resultado de la lectura me dará 1023. Aplicando una simple regla de 3 puedo ver que entre cada bit y el siguiente habrá una diferencia de 0,0049 volts (4,9 mV). A este valor se lo define como la resolución, que corresponde a entradas entre 0 volts y 5 volts. Veremos más adelante que, esos valores se pueden cambiar con analogReference(). Todo esto quiere decir que: Tensión de entrada = 0v lectura = 0000000000 0 4,9mV 0000000001 1 9,8mV 0000000010 2 14,6mV 0000000011 3 19,5mV 0000000100 4 ………. ……………. . .. ………. …………….. .. 2500mV (2,5v) 1000000000 512 ……….. ……………. ….. ……….. ……………. ….. ……….. ……………. ….. ……….. ……………. ….. 5000mV (5v) 1111111111 1023 Al igual que ocurría cuando usábamos la instrucción DigitalRead, luego de la lectura analógica lo habitual es guardar el valor en una variable que por los valores que tomará será del tipo int (entero). El proceso de conversión de la señal analógica a la información digital, que se produce al leer una entrada analógica, tarda unos 100 microsegundos (0.0001 s), por lo que la velocidad de lectura máxima es de alrededor de 10.000 veces por segundo. Entonces: La instrucción será: AnalogRead (Pin) Donde el argumento será el número de Pin que estoy leyendo (A0 a A5 en un Arduino Uno) El valor que se obtendrá de la lectura será un número entero entre 0 y 1023, proporcional a la tensión de entrada. 23 Veamos un ejemplo que además incluye otra nueva instrucción: int entrada = 3; int lectura = 0; // Genero un Alias para el pin al que se le conecta las señal a leer // variable para almacenar el valor leído void setup() { Serial.begin(57600); // configuración serie } void loop() { lectura = analogRead(entrada); // lee el pin de entrada Serial.println(lectura); // imprime el valor } En este programa se lee la entrada analógica 3 y el valor leído se guarda en una variable a la que llamamos “lectura”. Para poder ver el valor leído se podría recurrir a varios métodos. Se podría por ejemplo poner un grupo de dígitos de 7 segmentos, o bien un grupo de leds, o tal vez un módulo LCD. La IDE de Arduino nos da una excelente herramienta para verificar el valor que van tomando las variables. Esta herramienta se llama Monitor y se activa clickeando en la parte superior derecha de la IDE de Arduino. 24 Al clickear sobre el ícono del Monitor aparecerá una pantalla de monitor que mostrará todo aquello que le enviemos. En el área Recibir del monitor irán apareciendo los datos que envíe desde el programa. En el programa (función loop) encontramos la función: Serial.println(lectura); Veremos más en detalle esta instrucción pero, en este momento lo que debemos saber es que lo que hace es imprimir (print) los datos que se encuentran entre paréntesis en el monitor. Si lo que tengo entre paréntesis se encuentra entre comillas lo imprimirá textalmente. Serial.println(“señal de entrada”); Esta instrucción haría que aparezca en el monitor la oración: señal de entrada Si lo que se coloca entre paréntesis es una variable, lo que ocurrirá es que en el monitor aparecerá el contenido de esa variable. En nuestro ejemplo, el valor leído en la entrada 3 (como dijimos un valor entre 0 y 1023) aparecerá en el monitor. Como esta instrucción se encuentra dentro de la función loop, la lectura se irá repitiendo indefinidamente. Para usar el monitor hay que definir en la función setup la velocidad de envío de datos. En este caso la instrucción es Serial.begin (57600); donde 56700 es dicha velocidad. 25 Ejercicio: Lo que queremos es escribir un programa que lea la entrada A0 donde vamos a colocar una batería de control remoto de 3,2v y nos indique que valor tiene. Para expresar la tensión de la batería será necesario efectuar una operación matemática Una regla de 3 1…………………4,9 mV valor leido……… X y luego pasar el resultado a volts, ya que va a dar en mV. Como la operación incluye un número no entero, el tipo de variable que va a almacenar el resultado de la operación no debe ser int (entero) sino float (con coma, flotante). Al momento de definirla, la expresión será: float tension; En este momento la información leída la vamos a presentar en el monitor. [ En breve vamos a realizar el mismo programa pero presentando la información en un módulo LCD y encendiendo un led si está debajo de un valor de 2,8v.] 26 Volver a realizar un programa que determine el estado de la batería pero en este caso, el arduino deberá: Encender el led verde si la batería tiene más de 3 volts Encender el led amarillo si la batería tiene entre 2,5 y 3 volts. Encender el led rojo si la batería tiene menos de 2,5 volts En este programa podremos utilizar la sentencia if para averiguar si la tensión de entrada se encuentra por encima, por debajo o entre dos valores determinados. Por ejemplo, si la lectura de la tensión de entrada se guarda en una variable que se llame tension, las instrucciones podrían ser: If (tension > =3) { } o, por ejemplo if (tension <3 && tension >= 2.5) { } En este lenguaje de programación && significa “and” (y) y || significa “or” (o). La expresión anterior se leería como: Si la tensión es menor que 3v y mayor que 2,5v. 27 Veamos un ejemplo más: Desarrollar el sketch que: Al cerrar el interruptor S el Arduino mida la tensión de la batería. Si la batería tiene menos de 3 volts encienda el led rojo Si la tensión se encuentra entre 3 volts y 3,5 volts enciende el led amarillo Si la tensión supera los 3,5 volts enciende el led verde. En los tres casos presenta en el monitor el valor de la tensión de la batería Luego, si la tensión fue inferior o igual a 3,5 volts (quiere decir que encendió el amarillo o el rojo) el Arduino cierra el relé para que la batería se cargue. Por último va leyendo la tensión sobre la batería y cuando llega a 4,2 volts desactiva el relé y deja titilando el led verde indicando carga plena. Cada vez que lee la presenta el valor de la tensión en el monitor. 28 analogWrite() Esta función escribe un valor analógico ( realmente una señal PWM ) en un pin. Entre otras aplicaciones se puede utilizar para encender un LED con distintos brillos o para impulsar un motor a distintas velocidades. Después de una instrucción analogWrite(), el pin generará una onda rectangular constante del ciclo de trabajo especificado hasta que se vuelva a ejecutar otra función analogWrite(), (o bien instrucción digitalRead() o digitalWrite()), en el mismo pin. La instrucción tiene el formato: analogWrite(pin,valor) Donde pin indica el número de pin del Arduino donde se encontrá la señal de PWM y valor es un número entre 0 y 255 que indica el ciclo de trabajo (relación entre el tiempo en el que la señal va a estar en estado alto y el tiempo en el que va a estar en estado bajo) No todos los pines de un Arduino pueden generar una señal PWM, y dependen del modelo. Los pines PWM están indicados en la plaqueta Arduino con un tilde similar a la de la letra ñ . Debajo una tabla que muestra los datos para los modelos de Arduino más comunes. JUNTA PINES PWM FRECUENCIA PWM Uno, Nano, Mini 3, 5, 6, 9, 10, 11 490 Hz (pines 5 y 6: 980 Hz) Mega 2 al 13, 44 al 46 490 Hz (pines 4 y 13: 980 Hz) Leonardo, Micro, Yún 3, 5, 6, 9, 10, 11, 13 490 Hz (pines 3 y 11: 980 Hz) Señal de PWM Lo primero que tenemos que entender es que la mayoría de automatismos (y Arduino no es una excepción) no son capaces de proporcionar una auténtica salida analógica. Ni siquiera pueden suministrar una salida analógica discretizada (es decir, a saltos) de tensión. Lo único que pueden proporcionar es una salida digital, es decir una señal que varía entre 0V y 5V, sin pasar por valores intermedios. Para salvar esta limitación y simular una salida analógica la mayoría de los automatismos emplean un "truco", que consiste en activar una salida digital durante un tiempo y mantenerla apagada durante el resto. El promedio de la tensión de salida, a lo largo del 29 tiempo, será igual al valor analógico deseado. A la relación entre ambos tiempos (de encendido y apagado) se lo denomina ciclo de trabajo (duty cycle) Veamos algunos ejemplos. PWM NO ES UNA SEÑAL ANALÓGICA Es importante recordar en todo momento que en una salida PWM el valor máximo de tensión realmente es Vcc. Por ejemplo, si estamos alimentando un dispositivo que necesita 3V, y usamos una pulsada, en realidad estaremos suministrando 5V durante un 60% del tiempo y 0V durante el 40%. Pero si el dispositivo, por ejemplo, soporta como máximo 3V, podemos dañarlo si lo alimentamos mediante un PWM. Una señal pulsada es suficiente para emular una señal analógica en muchas aplicaciones. Por ejemplo, podemos variar la intensidad luminosa en un LED mediante un PWM. El LED realmente se enciende y apaga varias veces por segundo, pero este parpadeo es tan rápido que el ojo no lo aprecia. El efecto global percibido es que el LED brilla con menor intensidad. 30 Otro ejemplo, al variar la velocidad de un motor DC con un PWM, en la mayoría de los casos la inercia del motor se encargará de que el efecto del PWM sea despreciable. No obstante, en función de la frecuencia podemos notar vibraciones o ruidos, en cuyo caso deberemos variar la frecuencia del PWM. Por otro lado, debemos tener en cuenta los efectos que provoca la rápida conexión y desconexión de la señal pulsada en el dispositivo/componente electrónico al que se aplica. Por ejemplo, en el caso de cargas inductivas (motores, relés, o electroimanes) la desconexión supondrá la generación de voltaje inducido que puede dañar la salida digital o el propio dispositivo, por lo que será necesario disponer de las protecciones oportunas. En cuanto a transistores, en general, los de tipo BJT resultan apropiados para funcionar como amplificación de señales PWM. Esto no suele ser así en los transistores MOS, donde los efectos capacitivos del mismo, unidos a la limitación de corriente de las salidas digitales, frecuentemente harán que necesitemos un driver de amplificación previo para evitar que el transistor trabaje en zona activa. LOS TIMER EN PWM POR HARDWARE Las funciones PWM emplean los Timer para generar la onda de salida. Cada Timer da servicio a 2 o 3 salidas PWM. Para ello dispone de un registro de comparación por cada salida. Cuando el tiempo alcanza el valor del registro de comparación, la salida invierte su valor. Cada salida conectada a un mismo temporizador comparte la misma frecuencia, aunque pueden tener distintos Duty cycles, dependiendo del valor de su registro de comparación. ASOCIACIÓN DE TIMERS Y PWM En el caso de Arduino Uno, Mini y Nano El Timer0 controla las salidas PWM 5 y 6. El Timer1 controla las salidas PWM 9 y 10. El Timer2 controla las salidas PWM 3 y 11. Mientras que en Arduino Mega El Timer0 controla las salidas PWM 4 y 13. El Timer1 controla las salidas PWM 11 y 12. El Timer2 controla las salidas PWM 9 y 10. El Timer3 controla las salidas PWM 2, 3 y 5. El Timer4 controla las salidas PWM 6, 7 y 8. El Timer5 controla las salidas PWM 44, 45 y 46. 31 INCOMPATIBILIDADES El uso de los Timer no es exclusivo de las salidas PWM, si no que es compartido con otras funciones. Emplear funciones que requieren el uso de estos Timer supondrá que no podremos emplear de forma simultánea alguno de los pines PWM. SERVO La librería servo hace un uso intensivo de temporizadores por lo que, mientras la estemos usando, no podremos usar algunas de las salidas PWM. En el caso de Arduino Uno, Mini y Nano, la librería servo usa el Timer 1, por lo que no podremos usar los pines 9 y 10 mientras usemos un servo. En el caso de Arduino Mega, dependerá de la cantidad de servos que empleemos. Si usamos menos de 12 servos, usa el Timer 5, por lo que perderemos los pin 44, 45 y 46. Para 24 servos, usa los Timer 1 y 5, por lo que perderemos los pin 11, 12, 44, 45 y 45. Para 36 servos, usa los Timer 1,3 y 5, perdiendo los pin 2, 3, 5, 11, 12, 44, 45, 46. Para 48 servos, usa los Timer 1, 3, 4 y 5, perdiendo todos los pin PWM. COMUNICACIÓN SPI En Arduino Uno, Mini y Nano, el pin 11 se emplea también para la función MOSI de la comunicación SPI. Por lo tanto, no podremos usar ambas funciones de forma simultánea en este pin. Arduino Mega no tiene este problema, ya que figuran en pines distintos. FUNCIÓN TONE La función Tone emplea el Timer 2, por lo que no podremos usar los pines 3 y 11, y en Arduino mega los pines 9 y 10. Ejemplo de aplicación : Programa “fade” de la lista de ejemplos de la IDE. El programa hace que un led instalado en una salida “analógica” encienda y apague gradualmente, es decir subiendo y bajando su brillo. 32 int led = 9; // Pin PWM donden se conecta el LED int brightness = 0; // Brillo del LED int fadeAmount = 5; // Incremento del brillo del LED void setup() { pinMode(led, OUTPUT); } void loop() { analogWrite(led, brightness); brightness = brightness + fadeAmount; if (brightness <= 0 || brightness >= 255) { fadeAmount = -fadeAmount; } delay(30); } Desarrollar un proyecto con un arduino que controle la velocidad de un motor de corriente continua. 33 El objetivo es que el arduino sense la temperatura del ambiente por medio de un termistor y cuando: La temperatura sea superior a 30 °C el arduino encienda el ventilador (motor) a un 10% de su máxima velocidad. Si la temperatura sigue subiendo, por cada grado que aumente el arduino subirá la velocidad en 10%, es decir que cuando la temperatura supere los 31°C la velocidad será 20% de la máxima, cuando supere los 32°C será 30% del máximo y así hasta llegar al 100% de la velocidad del ventilador. (más adelante usaremos un sensor digital para determinar la temperatura con mayor precisión y tomaremos decisiones adicionales si al llegar al 100% de la velocidad la temperatura sigue subiendo). El termistor que compramos tiene la siguiente curva de respuesta: El circuito electrónico podría ser: 34 Librerías de Arduino Módulo LCD . Veamos un módulo estándar de 16 columnas y dos filas El módulo se comunica con el microcontrolador por medio de 8 líneas de datos (D0-D7) y 3 líneas de control (RS – R/W – E). El pin E realmente habilita todo el módulo (enable) aunque suele considerarse una señal de control. 35 Según la operación que se desee realizar en el módulo LCD, los pines de control E, RS#, RW# deben tener un estado determinado. Además debe tener en el bus de datos un código que indique un carácter para mostrar en la pantalla o una instrucción de control para el display. Los módulos LCD responden a un conjunto especial de instrucciones, estas deben ser enviadas por el microcontrolador o sistema de control al display, según la operación que se requiera. Se muestran a continuación el conjunto de instrucciones del modulo LCD. 36 Generar todos los cambios de estado en las señales de control y de datos para que el Arduino se comunique con el módulo LCD es una tarea compleja que lleva bastante tiempo e implica un gran conocimiento del hardware de ambos sistemas. Para hacer más fácil la tarea se pueden emplear librerías. Las librerías son programas que han sido escrito por otros usuarios y a veces por las mismas empresas que desarrollan los módulos (este como cualquier otro: Sensor de temperatura, sensor de humedad, sensor de color, etc.). Estos programas están publicados en distintas páginas web y hay que buscarlos en la red. Luego de cargar una librería en un sketch se la puede utilizar pero hay que revisar los requerimientos de la misma. En el caso de la librería que nos permite comunicarnos con un módulo LCD y viene por Default en la IDE de Arduino, deberemos incluir un comando que identifica los pines de comunican entre el módulo y el arduino y luego indicar en la función “set-up” el modelo de módulo que estamos usando. Recién luego se podrán utilizar los comandos que permite la librería tales como, borrar la información del display, posicionar el cursor, escribir un grupo de caracteres, escribir el contenido de una variable, etc. El ejemplo de utilización básica que viene incluida en los ejemplos en la IDE de Arduino es: #include <LiquidCrystal.h> const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2; LiquidCrystal lcd(rs, en, d4, d5, d6, d7); void setup() { lcd.begin(16, 2); lcd.print("hello, world!"); } void loop() { lcd.setCursor(0, 1); lcd.print(millis() / 1000); } Explicar que función cumple cada línea de programación y que hace el programa. 37