Arduino Apunte Imprimir Copia1
Arduino Apunte Imprimir Copia1
Introducción
Arduino es una compañía de hardware libre y una comunidad tecnológica que diseña y manufactura placas de
desarrollo de hardware y software, compuesta respectivamente por circuitos impresos que integran un
microcontrolador y un entorno de desarrollo (IDE), en donde se programa cada placa.
El hardware consiste en una placa de circuito impreso con un microcontrolador, usualmente Atmel AVR, puertos
digitales y analógicos de entrada/salida, los cuales pueden conectarse a placas de expansión (shields), que
amplían las características de funcionamiento de la placa Arduino. Asimismo, posee un puerto de conexión USB
desde donde se puede alimentar la placa y establecer comunicación con el computador.
El microcontrolador de la placa se programa mediante un computador, usando una comunicación serial mediante
un convertidor de niveles RS-232 a TTL serial.
El lenguaje de programación Arduino se realiza mediante el uso de un lenguaje propio basado en el lenguaje de
programación de alto nivel Processing que es similar a C++.
Existen múltiples variantes del Arduino. En este caso, usaremos el Arduino UNO que es el más común.
LEDs RX TX (12)
TX es la abreviatura de transmisión, RX es la abreviatura de recibir. Estas marcas aparecen un poco en la
electrónica para indicar los pasadores responsables de la comunicación en serie. En nuestro caso, hay dos lugares
en la Arduino UNO donde aparecen TX y RX - una vez por pines digitales 0 y 1, y por segunda vez junto a los
indicadores LED de TX y RX (12). Estos LEDs nos darán algunas buenas indicaciones visuales siempre nuestro
Arduino está recibiendo o transmitiendo datos (como cuando nos estamos cargando un nuevo programa en el
tablero).
Microcontrolador (13)
Lo negro con todas las patas de metal es un circuito integrado (IC, por sus siglas en Ingles). Piense en ello como el
cerebro de nuestro Arduino. La principal IC en el Arduino es ligeramente diferente del tipo de placa a placa tipo,
pero es por lo general de la línea de ATmega de CI de la empresa ATMEL. Esto puede ser importante, ya que
puede necesitar para saber el tipo de IC (junto con su tipo de tarjeta) antes de cargar un nuevo programa desde el
software de Arduino. Esta información se puede encontrar en la escritura en la parte superior de la IC. Si quieres
saber más acerca de la diferencia entre diversos circuitos integrados, la lectura de las hojas de datos suele ser una
buena idea.
Y bajamos la versión más reciente del IDE (Entorno de desarrollo de Arduino). Elegir la versión
correspondiente a nuestro sistema (En Windows recomendamos la versión Installer)
Una vez finalizado, ejecutar el fichero descargado, e ir respondiendo a las opciones de instalación.
Al cabo de unos minutos finalizara la instalación, y en el área de trabajo del equipo aparecerá el
icono de Arduino
Comprobación de la instalación.
Una vez instalado el IDE, vamos a comprobar que reconoce nuestro Arduino correctamente y que podemos
programarlo. Para ello, Conecta tu Arduino a tu ordenador mediante el USB (Comprueba que las luces del Arduino
se iluminan indicando que tiene alimentación.)
Al hacerlo nuestro PC debe detectar el nuevo dispositivo USB y montar el driver adecuado.
Atención, el puerto serie en que se instala puede variar, dependiendo de las características del equipo.
Ahora, ya podemos arrancar el icono de Arduino del escritorio de trabajo y configurar el modelo de Arduino y
confirmar el puerto serie al que se conecta. En [Menú]\Herramientas\Placa Elegir el modelo exacto de nuestro
Arduino:
En [Menú]\Herramientas\Port es necesario comprobar que tenemos asignado un puerto y que tiene la marca
de selección.
Es importante asignar el puerto y el modelo de Arduino para garantizar el correcto funcionamiento del IDE. La
marca de selección debe estar con el tick. Vamos ahora a volcar un programa de ejemplo:
Al pulsar [Menú]\Archivo\Ejemplos\01.Basics\Blink, aparecerá una serie de textos en el entorno de trabajo, que
ignoraremos por ahora. Pulsar el botón Marcado en amarillo, Veremos una línea de progreso verde avanzando.
Si todo va correctamente veremos un mensaje en la parte inferior del IDE:
Este mensaje en color blanco indica que hemos volcado correctamente el programa y ya deberíamos ver una luz
que parpadea en nuestra placa Arduino.
Un programa de ordenador es básicamente el equivalente a una receta de cocina… pero destinado a un público
distinto.
Mientras que las personas somos razonablemente buenas interpretando las instrucciones, generalmente vagas,
de una receta de cocina, cuando programamos quien debe entendernos es un ordenador que espera
instrucciones precisas respecto a lo que debe hacer y que además carece por completo de la imaginación o
capacidad de improvisación humana.
El IDE de Arduino se programa en una variante de C++, que es un lenguaje muy extendido por sus características.
Un programa es una serie de instrucciones que se ejecutan en secuencia.
Un programa interno comprueba que la sintaxis de nuestro programa es acorde a la norma de C++, y si hay
cualquier cosa que no le convence dará un error y finalizará la comprobación obligándonos a revisar lo que hemos
escrito.
Cuando el comprobador acepta nuestro programa, invoca otro programa que traduce lo que hemos escrito a
instrucciones comprensibles para el procesador de nuestro Arduino. A este nuevo programa se le llama
compilador. La funcion del compilador es conviertir nuestras instrucciones (código fuente) en instrucciones del
procesador (código ejecutable).
Estructura de un programa Arduino.
void setup()
{
instrucciones;
}
void loop()
{
instrucciones;
}
Setup: Sus instrucciones se ejecutan solo una vez, cuando se arranca el programa al encender Arduino o
cuando pulsamos el botón de reset. Generalmente incluye definiciones e inicializaciones de ahí su nombre.
Loop: Sus instrucciones se van ejecutando en secuencia hasta el final…. Y cuando acaba, vuelve a empezar
desde el principio haciendo un ciclo sin fin.
Cuando abrimos el IDE de Arduino (o hacemos [Menú]\Archivo\nuevo) él nos escribe ya estas dos funciones (en
color cobre).
Nótese que el principio de cada función es indicado por la apertura de llave “ { “ y el fin de la misma corresponde
al símbolo de cerrar llaves “ } “.
De hecho el conjunto de instrucciones contenidas entre una apertura y cierre de llaves se llama bloque y es de
mucha importancia a la hora de que nuestro Arduino interprete de una u otra manera las instrucciones que le
damos. Es imperativo que a cada apertura de una llave corresponda un cierre de llave.
Cualquier cosa que escribamos precedido por “ // “ son comentarios, y serán ignorados. Es decir podemos
dejarnos mensajes dentro del código, (que de otro modo darían errores). El compilador ignorará cualquier cosa
entre // y el fin de línea.
Arduino dispone de 14 pines que pueden ser usados de este modo, numerados del 0 al 13:
En la sesión anterior cargamos un programa de ejemplo que hacía parpadear un LED en la placa con una
frecuencia definida. Veamos como programar esto.
Pediremos a Arduino que active su pin 13 como de salida digital y después encenderemos y apagaremos esta
señal lo que hará que el LED que tiene conectado de serie se encienda o apague al ritmo que marquemos.
Para indicar al sistema que deseamos usar el pin 13 como salida digital utilizamos la instrucción:
El primer parámetro indica el pin a usar (en éste ejemplo el 13) y “OUTPUT” es para usarlo como salida, y también
podría usarse el valor “INPUT” para indicar que vamos a leer de este pin.
Estas definiciones se harán solo una vez al principio, en la función setup(). La nuestra quedará, con una única
instrucción que declara que vamos a usar el pin 13 como salida digital:
void setup()
{
pinMode( 13, OUTPUT) ;
}
Es importante fijarse en que a pesar de ser una única instrucción, hemos delimitado el bloque de esta función
mediante abrir y cerrar llaves.
Obsérvese que la instrucción finaliza en “ ; ” . C++ obliga a acabar las instrucciones con un punto y coma que
delimite la orden. Si se omite generará un error.
digitalWrite( 13 , HIGH) ;
digitalWrite( 13 , LOW) ;
El 13 indica el pin a utilizar y HIGH o LOW indican el valor que deseamos poner en esa salida, que en Arduino
corresponden a 5V para HIGH y 0V para LOW.
Si en la función loop() escribiéramos estas dos instrucciones seguidas, Arduino cambiaría estos valores tan
deprisa que no percibiríamos cambios, así que necesitamos frenarle un poco para que podamos percibir el
cambio.
En esta entrada presentaré de forma muy sencilla una novedosa y muy práctica herramienta que nos ofrece
Autodesk: ni más ni menos que un simulador (muy elemental) de Arduino. En él podremos hacer cosas más que
suficientes como para desarrollar aprendizajes básicos debido a que dispondremos de pulsadores, diodos LED,
resistencias y potenciometros.
Recomiendo esta aplicación para quien desee iniciarse en el manejo de entradas y salidas tanto digitales como
analógicas.
Este simulador se programa de forma muy sencilla mediante el mismo lenguaje del entorno Arduino IDE y,
además, el código se compila y "carga" a la placa hasta más rápido que en una netbook.
¿Cómo empezar a desarrollar en 123D Circuits?
Vamos a el sitio 123d.Circuits.io y nos registramos, lo cual es muy fácil y rápido. Después cuando iniciemos, nos
solicitará el usuario (nuestro correo electrónico) y la contraseña.
Una vez que ingresemos a nuestra cuenta la pantalla principal será esta:
Para crear un proyecto ir a “+New”, y cuando aparecen las opciones seleccionar el cuadro “New Electronics Lab”.
En la pantalla que aparece hay por defecto un protoboard. ¿Qué es un Protoboard? , es un tablero con orificios
conectados eléctricamente entre sí, habitualmente siguiendo patrones de líneas, en el cual se pueden insertar
componentes electrónicos y cables para el armado y prototipado de circuitos electrónicos y sistemas similares.
Está hecho de dos materiales, un aislante, generalmente un plástico, y un conductor que conecta los diversos
orificios entre sí. Uno de sus usos principales es la creación y comprobación de prototipos de circuitos
electrónicos antes de llegar a la impresión mecánica del circuito en sistemas de producción comercial.
La placa Arduino no requiere de alimentación, sí la protoboard, la cual se alimentará desde la placa así como los
componentes que requieran de alimentación.
Los materiales son seleccionados desde "Components [+]", se arrastran, y se sueltan. El código se escribe desde
"Code Editor [</]". Y una vez hayamos terminado el código, vamos a "upload & run" para empezar a ver como tu
creación cobra vida. La única contra que tiene la plataforma es que si tu código no compila no te va a decir donde
está el error, aunque este sea pura sintaxis. Si no encuentras tu error, puedes dar a "download code" o un simple
copiar y pegar en nuestro IDE arduino para verificar, corregir y volver a copiar. Como vemos simula muchos
componentes y además también simula una terminal serie.
Nuestro primer circuito
Una luz (LED) que se enciende y se apaga, necesitamos lo siguiente:
Material requerido.
Una Protoboard.
Un diodo LED
Veamos como es el circuito del LED conectado al pin 13 de nuestro Arduino. Su esquema eléctrico sería:
Un diodo, es un componente electrónico que solo permite pasar la corriente en una dirección. En la dirección
del positivo al negativo (la parte ancha del triángulo) al negativo, la punta del triángulo (que indica la
dirección).
Las resistencias en cambio no diferencian un extremos del otro, decimos que no tienen polaridad.
Existen tres formas principales de conocer la polaridad de un led:
La pata más larga siempre va a ser el ánodo.
En el lado del cátodo, la base del LED tiene un borde plano.
Dentro del LED la plaqueta indica el ánodo. Se puede reconocer porque es más pequeña
que el yunque que indica el cátodo.
Es importante entender los esquemas electrónicos porque permiten comprender con rapidez cualquier circuito.
Vale la pena dedicarle un poco de esfuerzo porque son el lenguaje de la electrónica.
Este esquema sigue una pauta de marcar los cables que van a positivo en rojo y los que van a GND en negro.
Recomendamos encarecidamente se siga esta norma en la práctica porque ayuda a identificar posibles problemas
y evita errores.
La Protoboard une los puntos de la línea azul entre si y los de encima de la línea roja entre sí, (se les llama
raíles), pero no conecta el raíl rojo positivo con el raíl negro negativo.
A su vez existen dos zonas de líneas verticales en la Protoboard. Estas líneas verticales están unidas entre sí
internamente, para facilitar la conexión de los componentes, pero no se unen las líneas paralelas.
Nótese el sangrado de las líneas para destacar los bloques de código. Esto se considera buena práctica y lo
recomendamos encarecidamente, porque facilita mucho la comprensión del programa.
Podemos ahora cargar nuevamente el ejemplo Blink, siguiendo el procedimiento que ya definimos, y veremos
cómo ésta vez, además del LED propio de Arduino, nuestro LED exterior parpadea siguiendo el mismo ciclo de
encendido y apagado.
Una Protoboard.
8 x diodos LED.
El esquema del circuito es muy similar al de la sesión anterior, salvo por el hecho de que colocamos en la
Protoboard 8 LEDs.
La única novedad es que dado que la función de la resistencia es limitar la intensidad de la corriente que circula
por el circuito, y puesto que todos los diodos tienen masa común, basta una única resistencia entre este punto y
Ground.
Cuando nuestro programa levante el pin correspondiente a valor a HIGH, se cerrará el circuito iluminándose el
LED asociado.
Si quisiéramos montar un circuito que tuviera 8 LEDs y en el que la luz se desplazara de uno a otro, una
posibilidad sería repetir varias veces las mismas secuencias de instrucciones que ya conocemos.
Por ejemplo si conectamos distintos LEDs a distintos pines digitales de Arduino, deberíamos declararlo en nuestra
Función de setup() que podría ser:
void setup()
{
// Inicializar los pines digitales como una salida
pinMode( 13, OUTPUT) ;
pinMode( 12, OUTPUT) ;
pinMode( 11, OUTPUT) ;
…………………………
pinMode( 6, OUTPUT) ;
}
Y a su vez nuestro loop() debería repetir tantas veces como LEDs tengamos el juego de encender y apagar cada
uno de los LEDs en secuencia desde el pin 13 hasta el 6. Podríamos reemplazar todos éstas repeticiones con la
instrucción For que podemos usar en combinación con una variable.
Recordemos que una variable es un contenedor que puede tomar varios valores, en nuestro caso aceptará
todos los valores entre 6 y 13.
C++ nos exige declarar el tipo de las variables antes de usarlas. En nuestro caso usaremos el tipo entero que
se escribe int para indicar que esta variable es numérica y entera, sin decimales.
Así por ejemplo, para inicializar en nuestro setup() los pines desde el 13 hasta el 6 como salidas (requerido por
nuestro Arduino) podríamos usar la instrucción for de la siguiente manera:
void setup()
{
int i = 0 ; // Inicializamos la variable i como un entero
for ( i = 6 ; i < 14 ; i++)
pinMode( i , OUTPUT) ;
}
Aquí lo importante es que for necesita 3 parámetros separados por un carácter de punto y coma. Estos
parámetros son y en éste orden:
Una variable que irá tomando valores según una cierta regla, y a la que asignamos un valor inicial. En este
caso: i = 6 .
El ciclo continúa mientras se cumpla esta condición. En nuestro caso mientras la i sea menor que 14, o sea
hasta el 13: i <14
Como cambia la variable en cada iteración. En nuestro caso i++ (o i = i + 1) que es pedirle a C++ que
incremente en uno la variable i, al final de cada iteración.
Este montaje nos permite jugar con las luces y se presta a varios programas diferentes para conseguir distintos
efectos.
Por ejemplo, con el programa 2, el efecto no es exactamente el del “auto fantástico” porque cuando acabamos
de iterar el for, el programa vuelve a empezar desde el principio, lo que hace que la luz salte desde el pin 6 hasta
la del pin 13.
Así pues ¿Podríamos hacer que la luz rebotara?. Desde luego que sí, bastaría con usar dos ciclos for, similar a lo
siguiente:
El primer ciclo for hace que las luces se encienda en secuencia desde la 6 hasta la 13. El segundo ciclo for
entra a continuación empezando con la luz 12 (para no repetir la 13) y finalizando con la 7(para no repetir la
6), y vuelve a empezar.
En el segundo bucle hemos hecho una cuenta atrás diciéndole a la variable i que se decrementara en uno en
cada iteración mediante la instrucción i - - .
También nos hemos aprovechado de que C++ nos permite definir variables sobre la marcha dentro de la
propia instrucción for, sin necesidad de dedicarle una línea completa a la declaración e inicialización.
Un diodo LED.
Un pulsador.
Entradas digitales
Con frecuencia en electrónica necesitamos saber si una luz está encendida o apagada, si alguien ha pulsado un
botón o si una puerta ha quedado abierta o está cerrada.
A este tipo de señales todo/nada, SI/NO, TRUE/FALSE, 0/1 se les llama digitales, y podemos manejarlas con los
pines de 0 al 13 de Arduino y por eso hablamos de pines digitales.
Muchos de los sensores y actuadores que vemos en el mundo real son digitales:
Como actuadores digitales, tenemos luces, alarmas, sirenas, desbloqueo de puertas, etc.
Como sensores digitales podemos mencionar botones y pulsadores, Finales de carrera, desbordamiento de
nivel, sensores de llamas, humo o gases tóxicos.
Hemos visto que Arduino pueden usar los pines digitales como salidas verdadero/falso para encender un LED. De
la misma manera podemos leer valores, verdadero/falso, del mundo exterior.
En esta sesión veremos que los pines digitales de Arduino pueden ser usados tanto de entrada como de salida.
Vamos a leer un botón o pulsador externo y vamos a encender o apagar un LED en función de que el botón se
pulse o no.
A esta resistencia que fuerza el valor alto en vacio se le conoce como pullup Si la conectáramos a masa para
forzar una lectura a Ground se le llamaría pulldown resistor.
Esta resistencia es clave para que las lecturas del pulsador sean consistentes. El circuito, simplemente, no
funcionará bien si se omite (volveremos sobre esto).
En este esquema hemos seguido la práctica habitual de usar cables negros para conectar a masa y cables
rojos para conectar a tensión (5V).
Obsérvese que el pulsador S1 tiene cuatro pines (el que está sobre la resistencia horizontal). Esto es porque
cada entrada del interruptor tiene dos pines conectados. En nuestro circuito simplemente ignoramos los pines
secundarios.
Empecemos haciendo un programa que haga que el LED se encienda cuando pulsamos el botón y se apague
cuando lo soltamos. Para ello pediremos a Arduino que configure el pin digital 10 (D10) como salida para manejar
el LED, y el pin digital 6 (D6) como entrada para leer el botón.
Normalmente en programas sencillos basta con poner el número de pin en las instrucciones. Pero a medida que
el programa se complica esto tiende a provocar errores difíciles de detectar.
Por eso es costumbre definir variables con los números de pin que usamos, de forma que podamos modificarlos
tocando en un solo lugar (y no teniendo que buscar a lo largo del programa). Vamos a escribir esto un poco más
elegantemente:
int LED = 10 ;
int boton = 6;
Vimos que para encender el LED bastaba usar digitalWrite( LED, HIGH). Para leer un botón se puede hacer algo
similar: digitalRead( botón). Veamos cómo podría ser nuestro loop:
void loop()
{
int valor = digitalRead(boton) ; // leemos el valor de boton en valor
digitalWrite( LED, valor) ;
}
¿Fácil no? Aunque el LED está encendido hasta que pulsamos el botón y se apaga al pulsar.
¿Cómo podríamos hacer lo contrario, que el LED se encienda al pulsar y se apague si no? Bastaría con escribir en
LED lo contrario de lo que leamos en el botón.
Existe un operador que hace eso exactamente el operador negación “ ! “ . Si un valor dado x es HIGH, entonces !x
es LOW y viceversa.
Recordemos que un operador es un símbolo que relaciona varios valores entre sí, o que modifica el valor de
una variable de un modo previsible.
De hecho este tipo de operaciones son tan frecuentes que C++ incorpora un tipo llamado bool o booleano que
solo acepta dos valores TRUE (cierto) y FALSE y son completamente equivalentes al 1 / 0, y al HIGH / LOW
Este nuevo programa sería algo así:
void loop()
{
int valor = digitalRead(boton) ; // leemos el valor de boton en valor
digitalWrite( LED, !valor) ; //Escribimos valor en LED
}
Hemos definido valor como bool, porque podemos usar el valor de tensión alto como TRUE y el valor bajo como
FALSE.
Si el botón no está pulsado leerá TRUE y por tanto pondrá LED a FALSE. En caso contrario encenderá el LED.
Condicionales y botones
Material requerido.
Una Protoboard.
Un diodo LED.
Un pulsador.
El tipo de variable bool solo puede tomar dos valores: True o False. Con frecuencia hay que tomar decisiones para
seguir un camino u otro en función de que se cumpla una condición dada; Esta condición se debe evaluar
necesariamente, a True o False para tomar una decisión sin duda posible.
Por ejemplo, antes utilizamos la instrucción for y comentamos que la iteración se mantiene mientras se cumpla
una cierta condición. Esta condición debe ser evaluable a True o False, es decir es un booleano.
Existen otras muchas instrucciones que se apoyan en los valores booleanos, (como los condicionales if que
veremos en esta sesión) pero en un modo muy explicito toda la computación actual se basa en la lógica digital de
solo dos valores que solemos llamar 1 y 0, pero que con todo derecho podemos llamar a estos valores True y
False.
Los ordenadores modernos funcionan mediante la aplicación del algebra de bool a variables booleanas y con
un juego completo de operadores lógicos como la negación (NOT), que vimos anteriormente, mas operadores
lógicos como AND, OR, + y – .
La instrucción if
Repasemos… La instrucción if es muy sencilla de usar, basta con pasarle entre paréntesis una variable o condición
que se evalúe a true o false. Si el resultado es true se hace el bloque que viene a continuación y en caso contrario
se ejecuta el bloque que hay detrás del else, si existe. Si no existe la clausula del else, entonces el bloque que
sigue al if, se ejecuta o no, en función de la condición y luego sigue con la secuencia de instrucciones a
continuación.
if ( condición)
{
instrucción 1 ;
instrucción 2 ;
................
}
else
{
instruccion20 ;
instruccion21 ;
..............
}
Recordemos que en el circuito de la sesión anterior disponíamos de un pulsador y de un LED, en esta sesión
vamos a continuar con el mismo circuito y para conseguir que el LED se encienda o apague al pulsar el botón. Para
ello podríamos mantener la misma función setup() y escribir el loop() diferente:
void loop()
{
bool valor = digitalRead(boton) ;
if ( valor)
digitalWrite( LED, HIGH) ;
else
digitalWrite( LED, LOW) ;
}
Leemos primero el botón a una variable bool y después decidimos si encender o apagar el LED dependiendo de
que su valor sea True o False.
Recordemos que un bloque es un conjunto de instrucciones encerrados entre llaves y que hay un caso
particular en el que se pueden omitir, si y solo si, el bloque consta de una única instrucción como es nuestro
caso.
Vamos con un programa diferente. Queremos que el botón actúe como un interruptor, que al pulsarlo una vez se
encienda, y la próxima vez lo apague. Podríamos plantear algo así:
int LED = 10 ;
int boton = 6 ;
bool estado = false ;
void setup() //Programa 5
{
pinMode( LED, OUTPUT) ;
pinMode( boton , INPUT_PULLUP) ;
digitalWrite(LED , LOW) ; // Apagamos el LED al empezar
}
void loop()
{
bool valor = digitalRead(boton) ; //leemos el botón: false = LOW
if ( valor == false ) // esto es que han pulsado el botón
{
estado = ! estado ; // cambiamos el estado
digitalWrite(LED, estado) ; // escribimos el nuevo valor
}
}
La idea es definir una variable llamada estado al principio para guardar la situación del LED. El loop comprueba si
se ha pulsado el botón, y de ser así invierte su estado, y después escribe el valor de estado en el LED. Si estaba
encendido lo apaga. Si estaba apagado se enciende.
Aunque parece un plan perfecto, en la práctica no va a funcionar. En el tiempo que nosotros tardamos entre
pulsar y liberar el botón, nuestro humilde Arduino es capaz de leer unos cuantos miles de veces el pulsador e
invertir el valor del LED otras tantas.
Por eso, si lee un número par de veces dejara el LED como estaba y si lo lee un número impar de veces lo
invertirá. En la práctica la situación del LED se torna aleatoria, y si pulsáis repetidamente el botón veréis que el
resultado es impredecible.
Otra fuente de problemas es que en el mundo real un interruptor no cambia de un estado a otro de forma
perfecta, sino que suele rebotar y causar varias conexiones y desconexiones muy rápidas antes de quedar en un
valor estable. A esto se le llaman rebotes (bouncing) y al procedimiento para eliminar estos rebotes se le llama
debouncing en la jerga electrónica.
El debouncing se puede hacer por hardware con un conjunto de resistencia y condensador, o por software,
mucho más frecuentemente (por más barato) y para esto una solución es nuevamente frenar a Arduino y hacerle
esperar un tiempo entre 50 y 250 mili-segundos (ms) una vez que detecta que se ha pulsado el botón, de modo
que nos dé tiempo a liberar el pulsador:
void loop()
{
bool valor = digitalRead(boton) ; //leemos el botón: false = LOW
if ( valor == false ) // esto es que han pulsado el botón
{
estado = ! estado ; // cambiamos el estado
digitalWrite(LED, estado) ; // escribimos el nuevo valor
delay(250) ;
}
}
Muy importante: Nótese que la condición es (valor == false), con doble =. RECORDEMOS que en C++ la
comparación de dos valores usa ==, la asignación de valor a una variable solo uno.
Este lapso de 250 ms es suficiente para pulsar y liberar el botón cómodamente. Si probáis esta variante veréis que
ahora el LED invierte su valor cada vez que pulsas, siempre y cuando no te demores demasiado en liberar el
botón.
Pero… ¿Qué pasa cuando dejas el botón pulsado? Pues sencillamente que el LED invierte su estado cada 250 ms
(milisegundos) y tenemos otra variante del blinking LED.
Si queremos poder mantener pulsado sin que se produzca este efecto hay que sofisticar un poco más el
programa:
void loop()
{
estado = digitalRead(boton);
if (estado != estado_anterior) //hay cambio : Han pulsado o soltado
{
if (estado == LOW) //Al pulsar botón cambiar LED, NO al soltar
digitalWrite(LED, !digitalRead(LED));
estado_anterior = estado; // Para recordar el ultimo valor
}
}
REPASEMOS… Ya dijimos que para comprobar si dos valores son iguales usamos ==, Para comprobar si son
diferentes usamos != , y existen otros operadores relacionales
Igual que: ==
Distinto de: !=
Mayor que: >
Mayor o igual: >=
Menor que: <
Menor o igual: <=
Por último, una condición lógica se puede construir mediante los operadores lógicos AND, OR, y NOT cuyos
símbolos son respectivamente: &&, || y !
Si usáramos un circuito dos pulsadores con pullups (True, si no se pulsa) y un LED, dependiendo del
comportamiento que se busque podemos especificar diferentes condiciones:
Arduino dispone de una librería serie incluida llamada Serial, que nos permite enviar información al PC y para
usarla simplemente tenemos que pedirle en nuestro setup() que la incluya. La instrucción que se encarga es:
Serial.begin( velocidad ) ;
Nótese que Serial tiene la S mayúscula y que C++ diferencia entre mayúsculas y minúsculas
La velocidad es un valor entre 300 y 115.200 bits por segundo. Y suele ser costumbre establecerla en 9600 (el
valor por defecto) pero no hay ninguna razón para ello y esta no es una velocidad especialmente alta.
Para enviar un mensaje desde Arduino a nuestro PC podemos usar las funciones Serial.print() y
Serial.println().Veamos un ejemplo:
El println() enviara el valor de i al puerto serie de Arduino (repetidamente). Para leerlo en nuestro PC
necesitamos un monitor de puerto serie. El IDE de Arduino incluye uno muy sencillo, pero suficiente que se invoca
con el botón del monitor:
Necesitamos además asegurarnos de que la velocidad de conexión es la misma en ambos extremos. Fíjate en la
parte inferior derecha del monitor serie:
Normalmente la velocidad por defecto son los 9600 bits por segundo o baudios en los que hemos programado
nuestra puerta serie, y si lo desplegáis, veréis las diferentes velocidades aceptables para Arduino.
Estrictamente hablando, bits por segundo y baudios no son exactamente lo mismo salvo bajo ciertas
condiciones particulares que en Arduino se cumplen, por lo que aquí podemos usarlos como sinónimos.
En el mundo Arduino parece haber un acuerdo de usar velocidades bajas como 9600 en lugar de más altas
como 115.200, para evitar problemas. Esto es algo que hace años estaba justificado por problemas de
transmisión, pero con la tecnología actual no hay motivo para ello. Es más, en cuanto necesitemos utilizar
dispositivos de comunicaciones como adaptadores Ethernet o BlueTooth para comunicarnos, la velocidad
tendrá que subir necesariamente.
Ahora que sabemos enviar información y resultados al PC, vamos a ver cómo podemos operar con enteros y
mostrar el resultado en la puerta serie.
Recordemos: en C++ los operadores numéricos son los normales en cálculo (y algunos menos frecuentes):
Adición: +
Resta: –
Multiplicación: *
División entera: / Cociente sin decimales (puesto que operamos con enteros
Resto: % Devuelve el resto de una división.
En C++ tenemos que expresar las operaciones matemáticas en una sola línea y utilizar paréntesis para garantizar
que se opera como necesitamos. Vamos con algunos ejemplos:
Dada una expresión, la precedencia de operadores indica que operaciones se realizaran antes y cuales después en
función de su rango. Es más seguro usar paréntesis. Los paréntesis fuerzan las operaciones de una forma clara y
conviene, ya que detectar errores de operación puede volverse muy difícil.
El operador resto es más útil de lo que parece a primera vista porque nos permite saber si un numero es múltiplo
de otro. Supongamos que queremos saber si un número dado es par.
Podríamos escribir un programa como este:
void setup()
{
Serial.begin(9600) ; // Inicializa el Puerto serie
}
void loop()
{
int i = 27 ; //El número en cuestión
if ( i % 2 == 0)
Serial.println("Es par.") ;
else
Serial.println("Es impar");
}
Dando a i distintos valores podemos comprobar cómo funciona el operador resto %. Volveremos sobre esto
cuando veamos algunos ejemplos de cómo calcular números primos.
En este programa hemos usado de un modo diferente el Serial.println() pasándole una String de texto entre
comillas. Serial.print() envía el texto (entre comillas) que le pongamos pero no da salto de línea cuando
termina. En cambio Serial.println() hace lo mismo e incluye al final ese salto de línea.
void setup()
{
Serial.begin(9600) ; // Inicializa el Puerto serie
}
void loop()
{
Serial.print("Buenos ") ;
Serial.print("Dias ") ;
Serial.println("a todos.") ;
}
C++ dispone de un tipo de variables llamadas Strings, capaces de contener textos. Podemos operar con ellas
simplemente definiéndolas como cualquier otro tipo de C++:
void loop()
{
int resultado = 25 ;
String s = "El resultado es: " ; //Ver la S de string es mayúscula.
Serial.print( s) ;
Serial.println( resultado);
}
Un tipo String se define simplemente poniendo entre comillas dobles un texto, y se puede operar con ellas de
una forma similar a como operamos con enteros. Prueba:
void loop()
{
String a = "hola " ;
String b = "a todos." ;
Serial.println( a + b);
}
void loop()
{
int resultado = 25 ;
String s = "El resultado es: " ;
Serial.println( s + String( resultado ));
}
Donde imprimimos el resultado de concatenar s String, y la conversión de un int a String (El operador + añade
un String al final de otro).
Hasta ahora solo hemos enviado mensajes desde Arduino hacia el PC, ¿Pero como recibimos mensajes en
Arduino?
En primer lugar disponemos de una función llamada Serial.parseInt() que nos entrega lo que se escribe en el
monitor serie convertido a entero:
void setup() //Programa 7
{
Serial.begin(9600) ; // Inicializa el Puerto serie
void loop()
{
if (Serial.available() > 0)
{
int x = Serial.parseInt();
Serial.println ( x) ;
}
}
Este programa simplemente recibe en x los números que nos tecleen en la consola (cuando pulsemos intro) y si
es un texto, lo interpreta como cero.
Hemos utilizado otra función de Serial: Available() que es un booleano. Conviene por costumbre comprobar que
antes de leer el puerto serie hay algo que nos han enviado. Si lo hay Available() es True y en caso contrario es
False.
Para leer un String del puerto serie tenemos que complicarnos un poco más y hablar del tipo char.
Uno de las mayores problemas al iniciarse en C++ es comprender la diferencia, anti-intuitiva, entre char y String.
char es un tipo que representa un único carácter y se define con comillas simples, a diferencia de String que
necesita comillas dobles:
char c = ‘a’ ;
String s =”a” ;
void setup()
{ Serial.begin(9600); }
void loop ()
{
char c = ' ' ;
String mensaje ="" ;
if (Serial.available()) //Comprobamos si hay algo esperando
{
while( c != '\n') //Si lo hay, lo leemos hasta el enter
{
mensaje = mensaje + c ; // Añadimos lo leído al mensaje
c = Serial.read(); //Leer 1 carácter
delay(25);
}
Serial.println( mensaje); //Al salir imprimir el mensaje
//mensaje = "" ; //Bórralo para la próxima vez
}
}
Aquí usamos otra instrucción de C++ llamada while. Es similar a if, Ejecuta repetidamente el bloque que le sigue
mientras se cumpla la condición que le pasamos entre paréntesis:
while ( condición)
{ ……… }
Cuando lee el enter final de lo que escribimos, La condición c != ‘\n’ se torna falso y sale del while.
Por lo demás, comprobamos si hay algo disponible en la puerta serie y de ser así montamos el mensaje leyendo
un char (carácter) cada vez y sumándoselo a mensaje para construir un String que podamos imprimir al salir.
El motivo del delay(25) es que a una velocidad tan lenta, enviar un char de 8 bits por la puerta serie, tarda
mucho más de lo que tarda Arduino en ejecutar las instrucciones del while y volver a empezar. Por eso si se
suprime el delay (y les recomiendo la prueba) leerá un carácter bueno (de la palabra escrita y como 10
caracteres basura para un Arduino UNO o Mega).
Si subimos la velocidad de comunicación a 115200 bits por segundo, comprobaran que no hay este problema
ya que al multiplicar la velocidad de envío por más de 10 Arduino ya no tiene tiempo de volver a por más
caracteres antes de que lleguen.
Funciones y enteros
Material requerido.
Vamos a centrarnos en esta sesión en algunos ejemplos clásicos de programación, como son el cálculo de
números primos para entrenar esta capacidad de búsqueda de algoritmos prácticos para resolver problemas más
o menos abstractos y para presentar algunos conceptos adicionales.
Es importante destacar que no existe una forma única de resolver un problema concreto y que una no tiene
porque ser mejor que otra, aunque con frecuencia se aplican criterios de eficiencia o elegancia para
seleccionar una solución.
Supongamos que queremos crear un programa que nos devuelva true o false según que el número que le
pasamos sea primo o no y a la que podamos llamar varias veces sin copiar el código una y otra vez. La llamaremos
Primo() y queremos utilizarla de la siguiente manera: Si el numero n que le pasamos es primo nos tiene que
devolver true y en caso contrario que devuelva false, o sea queremos que nos devuelva un valor bool. Esto es lo
que llamamos una función.
En realidad, ya hemos utilizado varias funciones que Arduino trae predefinidas como el Serial.print() o abs() , o
Serial.available() y se las reconoce por esa apertura y cierre de paréntesis.
C++ nos ofrece todas las herramientas para crear nuestras propias funciones y es algo muy útil porque nos ayuda
a organizar un problema general en trozos o funciones más pequeñas y más fáciles de manejar.
Para definir una función así, tenemos que declararla primero y describirle a C++ que hacer:
bool Primo( int x) // int x representa el parámetro que pasaremos a esta función
{
Aquí va lo que tiene que hacer
…………
return( bool);
}
Declaramos la función Primo() como bool, o sea va a devolver un valor bool y por eso en algún punto tendremos
que usar la instrucción return(true) o return(false) para devolver un resultado a quien la llame. Si devolviera un
entero habría que definirla como int Primo(int x).
Si una función no va a devolver ningún valor, sino que simplemente realiza su trabajo y finaliza sin mas
entonces hay que declararla como void (vacía). Ya cononocemos dos funciones así : setup() y loop()
Para saber si un número es o no primo basta con dividirlo por todos los números positivos menores que él y
mayores que 1. En el ejemplo dividimos el número n empezando en 2 y finalizando en n-1.
Si encontramos un valor de i que devuelve resto 0, entonces es divisible (no es primo), devolvemos false con
return y volvemos a la intruccion que llamo a la función. Si no hallamos ningún divisor, al finalizar el for
devolvemos true y listo. Este es el método de fuerza bruta y sin duda es mejorable pero de momento nos sirve.
Para usar Primo hay que pasarle un entero. Recuerden que al definir la función dijimos bool Primo (int n) donde n
representa el valor que queremos probar. Así pues:
void loop()
{
int x = 427 ; // El número a probar
bool p = Primo(x);
if (p )
Serial.print( String(x) + " Es primo.") ;
else
Serial.print( String(x) + " No es primo." ) ;
}
Veamos cuantos primos hay hasta el, digamos 1024 y vamos a formatear la salida. Para ello usaremos el
caracter tabulador que se representa como ‘ \t ’ y una coma después, asi los números quedarán presentados en
una forma de tabla:
void loop()
{
if ( control) // Solo es para que no repita una y otra vez lo mismo
{
Serial.println( "Los numeros primos hasta el " + String( maximo)) ;
for ( int x = 2 ; x < maximo ; x++)
{
if (Primo(x) )
{
if ( contador++ % 8 == 0)
Serial.println( String(x)+"," ) ;
else
Serial.print( String(x) +","+ '\t') ;
}
}
}
control = false ;
}
Para conseguirlo, hemos añadido una coma y un tabulador a cada número excepto a uno de cada 8 que añadimos
intro. También tenemos una línea que conviene comentar: if ( contador++ % 8 == 0)
Cuando a una variable se le añaden dos símbolos mas al nombre, significa que primero se use su valor actual en la
instrucción en curso, en este caso en el if, y después se incremente en 1 su valor.
Querría decir que queremos incrementar su valor antes de utilizarlo. Esta notación es muy habitual en C++ y
conviene reconocerla. También podemos usar contador-- y --contador para decrementar.
El tipo entero
Este sería un buen momento para preguntarnos hasta donde podría crecer máximo en el programa anterior. Le
asignamos un valor de 1024, pero ¿Tiene un entero límite de tamaño?
La respuesta es afirmativa. Los enteros int en Arduino C++ utilizan 16 bits por lo que el máximo seria en principio
216 = 65.536, Pero como el tipo int usa signo, su valor está comprendido entre
-32.768 y +32.767.
De hecho en Arduino C++ hay varios tipos de distintos tamaños para manejar enteros… Repasemos
Tipo Descripción Valor
int Entero con signo, 16 bits entre -32,768 y 32,767
unsigned int Entero sin signo, 16 bits 216 – 1 ; de 0 hasta 65.535
long Entero con signo, 32 bits 232 – 1 ,Desde -2.147.483,648 hasta 2.147.483.647
unsigned long Entero sin signo, 32 bits Desde 232 – 1 ; 0 a 4.294.967.295
byte Entero sin signo, 8 bits 28 de 0 hasta 255
Todos estos tipos representan enteros con y sin signo y se pueden utilizar para trabajar con números realmente
grandes pero no sin límite.
De hecho C++ tiene la fea costumbre de esperar que nosotros llevemos el cuidado de no pasarnos metiendo un
valor que no cabe en una variable. Cuando esto ocurre se le llama desbordamiento (overflow) y C++ ignora
olímpicamente el asunto, dando lugar a problemas difíciles de detectar si uno no tiene cuidado.
Cuando se declara una función se debe especificar que parámetro va a devolver. Así:
Instrucción Significa
int Funcion1() Indica que va a devolver un entero
String Funcion2() Indica que va a devolver un String.
unsigned long Funcion3() Indica que va a devolver un long sin signo
void Funcion4() No va a devolver valores en absoluto
Una función puede devolver cualquier tipo posible en C++, pero sólo puede devolver un único valor mediante la
instrucción return(). Expresamente se impide devolver más de un parámetro. Este problema se puede resolver
usando variables globales o pasando valores por referencia.
Lo que sí está permitido es pasar varios argumentos a una función:
int Funcion5 ( int x , String s , long y)
Aquí declaramos que vamos a pasar a Funcion5, tres argumentos en el orden definido, un entero, un String y por
ultimo un long.
Utilizando Arrays
Material requerido.
Repasemos… Un array es simplemente una colección de elementos organizados como una matriz, y pueden
definirse con varias dimensiones. Empecemos con un array de una sola dimensión. Para definirlo podemos optar
por dos maneras:
En este caso definimos un array de enteros, de una sola dimensión con 5 elementos, sin asignar valores de
momento.
Aquí asignamos un array de enteros a los valores que le pasamos entre llaves, sin especificar cuantos, porque le
dejamos a C++ la tarea de contarlos. Decimos que definimos el array por enumeración.
Para asignar o leer los valores de un array se utiliza un índice entre corchetes. Veamos este programa:
Atención: la primera posición del un array es la 0 y la última el número de elementos – 1. Así serie2 [0]
devuelve el primer elemento 3, y serie2[4] el último 23.
Int Tablero[ 8, 8 ] ;
Imaginen que Tablero representa las posiciones de una partida de ajedrez y cada valor que contiene esa posición
corresponde a una pieza que se encuentra en esa casilla.
Un diodo LED.
Analógico y digital
En la vida muchas cosas son así, apruebas o suspendes, enciendes la luz o la apagas, pero muchas otras son
variables mensurables continuas y pueden tomar cualquier valor que imaginemos, como el ángulo del reloj o la
temperatura, que aun dentro de valores finitos pueden
tomar tantos valores intermedios como podamos
imaginar,
A esta clase de variables las llamamos analógicas y una
representación por contraposición a lo digital, sería
algo como esto:
No es raro que queramos controlar algo del mundo exterior con una señal analógica de forma que el
comportamiento del sistema siga esa señal. Podemos por ejemplo querer variar la luminosidad de un diodo LED y
no simplemente apagarlo o encenderlo
En esta sesión aprenderemos a enviar señales analógicas a los pines de salida de Arduino.
Hasta ahora hemos visto como activar las salidas digitales de Arduino, para encender y apagar un LED por
ejemplo. Pero no hemos visto como modificar la intensidad del brillo de ese LED. Para ello, tenemos que
modificar la tensión de salida de nuestro Arduino, o en otras palabras tenemos que poder presentar un valor
analógico de salida.
Para empezar tenemos que dejar claro que los Arduino carecen de salidas analógicas puras que puedan hacer
esto (con la notable excepción del Arduino DUE).
Pero podemos realizar un truco, para que con una salida digital podamos conseguir que casi parezca una salida
analógica.
A este truco se le llama PWM, siglas de Pulse Width Modulation, o modulación de ancho de pulsos. La idea
básica es poner salidas digitales que varían de forma muy rápida de modo que el valor eficaz de la señal de salida
sea equivalente a una señal analógica de menor voltaje.
Lo sorprendente es que el truco funciona.
Para poder usar un pin digital de Arduino como salida analógica, lo declaramos en el Setup() igual que si fuera
digital:
pinMode( 9, OUTPUT) ;
analogWrite escribe en el pin de salida un valor entre 0 y 5V, dependiendo de V (que debe estar entre 0 y 255).
De este modo si conectamos un LED a una de estas salidas PWM podemos modificar su brillo sin más que variar el
valor que escribimos en el pin.
Pero hay una restricción. No todos los pines digitales de Arduino aceptan poner valores PWM en la salida.
Solamente aquellos que tienen un símbolo ~ delante del número. Observen la numeración de los pines de la
imagen:
Solamente los pines 3, 5, 6, 9, 10 y 11 pueden hacer PWM y simular un valor analógico en su salida.
Si intentas hacer esto con un pin diferente, Arduino acepta la orden tranquilamente, sin error, pero para
valores de 0 a 127 entiende que es LOW y para el resto pone HIGH.
Vamos a hacer el típico montaje de una resistencia y un diodo LED, similar al que ya hicimos, pero asegurándonos
de usar uno de los pines digitales que pueden dar señales PWM. En la imagen en lugar de usar el pin 13 utilizar el
pin 9. Podemos escribir un programa parecido a esto:
Con el siguiente código el LED va aumentando el brillo hasta un máximo y vuelve a empezar la transición:
void loop()
{
for ( int i= -255 ; i<255 ; i++)
{
analogWrite (9, abs(i)) ;
delay( 10);
}
}
Aquí aprovecho para hacer el ciclo de subir y bajar el brillo del LED con un único bucle. La función abs(num),
devuelve el valor absoluto o sin signo de un número num, y por eso mientras que i viaja de -255 a 255, abs(i) va
de 255 a 0 y vuelta a subir a 255. ¿Que les parece el truco?
Una Protoboard.
Para quien este acostumbrado al diseño por ordenador ya está familiarizado con la idea de que podemos generar
cualquier color en la pantalla con la mezcla, en diferentes grados de tres colores básicos:
Red : Rojo
Green: Verde
Blue: Azul
Para quien haya dibujado con lápices de colores o acuarelas, las mezclas de colores de arriba les resultará
extraña. Esto es porque cuando pintamos en un papel blanco, la mezcla de colores es substractiva: Si
mezclamos los tres colores obtenemos negro, o por lo menos algo oscuro
En cambio cuando pintamos con luz directamente, la mezcla es aditiva y obtenemos blanco al mezclar los tres
colores básicos. Las reglas de mezcla de color en ambos casos son opuestas.
Vamos a montar un pequeño circuito que nos permita gobernar el color que emite uno de éstos LEDs de RGB.
De todos modos conviene asegurarse leyendo las especificaciones del fabricante, o bien identificando cada
PIN. Para identificarlos basta conectar el GND a nuestro Arduino e ir probando cada una de las patas
independientemente para ver qué color producen.
Si tu RGB tiene una montura Keyes, no tendrás que hacer esto, porque los pines vienen marcados y GND
viene rotulado.
Atención, en contra de la norma habitual, en este caso el cable rojo no indica la tensionVcc, sino el pin de gobierno
del LED rojo.
En este esquema hemos utilizado los pines 9, 10 y 11. Podemos usar otros pero asegurense de que puedan hacer
PWM(los que tienen ~) para poder poner distintas intensidades.
Dado que nuestra idea es poder mezclar las tonalidades de los componentes RGB para generar diferentes matices
de colores, parece buena idea escribir una función que haga esta mezcla de colores y a la que podamos recurrir
de forma abstracta y práctica (además de para encapsular una utilidad curiosa, a la que podremos recurrir en
futuros ejemplos y de paso insistir en el concepto de función).
De este modo tendríamos fácil llamar a Color ( 0, 255, 0) para el verde. De hecho vamos a empezar
asegurándonos de que tenemos identificados correctamente los pines, escribiendo un sketch como este:
void loop()
{ Color(0,255 ,0) ; //verde
delay(500);
Color(0 ,0 ,255) ; //azul
delay(500);
Color(255 ,0 ,0) ; //rojo
delay(500);
Color(0,0,0); //Apagado
delay(1000);
}
Este programa debería producir una secuencia de verde, azul, rojo, apagado y vuelta a empezar.
Conviene asegurarse de que hemos identificado correctamente los pines del RGB, porque de lo contrario, las
mezclas posteriores de colores no serán lo que esperamos.
Vamos a ver como averiguar qué mezcla de RGB necesitamos para conseguir un color determinado. Para quienes
usen Windows éste dispone del programa Paint incluido (en el menú de accesorios) y para quienes usan Mac o
Linux tienen programas similares.
Si ejecutan el Paint (o equivalente) suele tener un selector de colores:
Si van pinchando en la zona de colores de la derecha, en la barra vertical aparecen los matices próximos al que
han pinchado y pueden elegir el que más les guste. Debajo pueden ver la separación en RGB precisa para
conseguir un tono determinado.
Así pues para conseguir ese tono de azulito de la imagen basta con que llamen a
Dado que Arduino nos permite escribir valores de 0 a 255 en los pines digitales, cuando utilizamos
analogWrite(), en la práctica tendremos 255 x 255 x 255 colores diferentes o lo que es igual: 16.581.375
colores posibles.
La función Color() que hemos creado en esta sesión es muy sencilla pero se va añadiendo a otras que hemos ido
creando en sesiones anteriores con lo que vamos haciendo una pequeña colección de ellas.
El grupo de desarrollo de Arduino ha ido creando también muchas funciones que están disponibles para
incorporar en nuestros programas y que por razones de espacio resultan imposibles de ver más que muy por
encima.
Solo como ejemplo introduciremos una de ellas. La función random(N) devuelve un valor al azar, comprendido
entre 0 y N y en este caso, se presta especialmente bien para generar colores aleatorios en nuestro LED
RGB. Prueben esto:
Una Protoboard.
Un diodo LED.
Un potenciómetro de 10KΩ
Los potenciómetros
Hasta ahora hemos usado siempre resistencias fijas, de un valor dado. Pero a veces es conveniente disponer de
una señal variable para controlar el circuito que nos interesa. Imaginen el volumen de un equipo de música, o el
dial que sintoniza una emisora en una radio FM.
Un potenciómetro es, simplemente, un mecanismo para proporcionar una
resistencia variable.
Por eso un potenciómetro siempre tiene 3 pines en fila. Los del extremo se
comportan como una resistencia del valor de fondo de escala del
potenciómetro, y un pin central que va tomando valores de resistencia en
función del movimiento que hagamos con el ajuste.
Vamos a montar un circuito como este (en el que el potenciómetro esta
rotulado Pot1):
La idea es conectar 5V y GND a los extremos del Potenciómetro (no importa cual es uno y otro) y luego conectar
el pin central al positivo de un LED y el negativo a GND directo, pasando por una resistencia de limitación.
De este modo cuando giremos el potenciómetro estaremos modificando la tensión que aplicamos a la entrada del
LED, que variará entre 0 y 5V (Aunque ahora parezca extraño es muy sencillo) y habremos conseguido un
regulador de intensidad del LED.
Con una resistencia de 10k la intensidad en el circuito será de: 5V / 10.000Ω = 0,5 mA Muy poco para
conseguir iluminar el LED que requiere unos 20 mA. Así que durante la mayor parte del giro del potenciómetro
el LED estará apagado.
Importante: No olvides la resistencia R1. Aunque el potenciómetro limite la intensidad, hay un momento en
que llegara a cero y ahí y tu LED fallecerá en acto de servicio.
El montaje en la protoboard sería similar a esto ya que vamos a utilizar el Arduino simplemente para dar tensión
al circuito y nada más, Verán que la intensidad de la luz varía de forma continua al girar el potenciómetro.
Recuerda que debido al exceso de resistencia del potenciómetro de prueba, durante la mayor parte del giro
del ajuste el LED estará apagado.
Nótese que en este caso utilizamos nuestro Arduino simplemente como fuente de alimentación para dar
tensión al circuito.
void loop ()
{
brillo = analogRead (pot) / 4; //leemos el valor del potenciometro divididos entre 4 ya que
// solo se pueden usar valores entre 0 y 255 en analog Write
analogWrite(led, brillo); //analogWrite recibe dos valores, el pin a usar y la intensidad
// del voltaje (los valores de voltaje van de 0 a 255)
}
Con Arduino hemos visto que podemos influir en el mundo exterior aplicando salidas todo / nada en los pines
digitales y también que usando PWM podemos simular bastante satisfactoriamente señales analógicas en algunos
de esos pines.
También hemos visto cómo detectar pulsaciones de botones, definiendo como entradas los pines digitales. Pero
en muchas ocasiones los sensores que usamos para supervisar el mundo exterior, nos entregan una señal
analógica. Es el caso de los sensores de temperatura o distancia, de presión o PH, de intensidad de corriente en
un circuito o de caudal de agua en una tubería.
Para leer este tipo de señales continuas necesitamos un convertidor analógico a digital (o ADC por sus siglas en
ingles) y que nos permite leer el valor de una señal analógica en un momento dado.
Estos convertidores toman una muestra del valor actual de la señal y nos entregan su valor instantáneo, medido
en Voltios.
Mediante la lectura repetida de muestras a lo largo del tiempo podemos reconstruir la señal original con mayor o
menor precisión, dependiendo de la exactitud de nuestra medida y de la velocidad a la que pueda tomar esas
muestras.
Arduino UNO dispone de seis convertidores analógico a digital, nominados de A0 hasta A5, rotuladas como
ANALOG IN:
Veamos cómo usar las entradas analógicas con un circuito como este, en el que damos tensión a los extremos de
un potenciómetro y conectamos el pin central (el variable) a la entrada de la puerta A5 de Arduino:
Parece buen momento para destacar que los convertidores ADC leen valores de tensión y no resistencia, por lo
tanto, lo que vamos a leer es la caída de tensión en el potenciómetro a medida que giramos el ajuste.
La primera curiosidad es que no necesitamos declarar en el setup() que vamos a usar una puerta analógica. Y la
segunda es que para tomar una muestra (leer) del pin A5, usaremos la instrucción:
Los convertidores de Arduino UNO y Mega son de 10 bits de resolución por lo que nos devolverá valores entre
0 y 210 = 1.024 para tensiones entre 0 y 5V. En cambio el Arduino DUE dispone de convertidores de 12 bits por
lo que el valor de sus lecturas estará entre 0 y 1012 o sea 4.096, es decir tiene mejor resolución (pero sólo
puede leer hasta 3,3V).
Asegúrate de no usar sensores que puedan dar más de 5V máximo (con Arduino UNO y Mega), ya que
dañarías el chip principal de Arduino.
Vamos a escribir un programa que lea el valor del pin A5 y lo envíe a la consola para que podamos visualizarlo.
Cuando lo vuelques, arranca la consola y veras que a medida que giras el ajuste las lecturas varían de forma
continua reflejando la posición del potenciómetro, las lecturas reflejan la caida en voltios en el.
Display de 7 segmentos
Material requerido.
Cables de protoboard
Una resistencia.
Un display de 1 dígito.
Un display de segmentos (o visualizador) es un componente electrónico que se utiliza para representar números.
Como su propio nombre indica y, como se puede observar en la imagen siguiente, el display está compuesto por 7
segmentos, los cuales se encenderán y/o apagarán en función del número a representar. De forma interna, se
asemeja a siete LEDs conectados estratégicamente formando el número 8, aunque externamente dicha
semejanza no se observa, de ahí su simplicidad.
Para saber cual es el pin 1, poner el display de modo que viendo los numeros el punto decimal quede abajo a su
derecha. El pin numero 1 es en la parte inferior el primero por la izquierda, y luego ir contando en el sentido
contrario a las agujas del reloj
Cada uno de los segmentos que componen este display se denominan a, b, c, d, e, f y g, tal y como se muestra en
la figura anterior. El P simboliza el punto decimal.
En lo que se refiere a las conexiones, tenemos que tener en cuenta cada segmento a qué pin lo vamos a conectar,
para poder efectuar una llamada a los pines correcta. En nuestro caso, hemos hecho las siguientes conexiones
(puede variar la designación según convenga):
Segmento a - pin 7
Segmento b - pin 8
Segmento c - pin 9
Segmento d - pin 10
Segmento e - pin 11
Segmento f - pin 12
Segmento g - pin 13
Programa de control
Tenemos que comprobar que las conexiones son correctas lo primero. Prueben esto:
DIGITO a b c d e f g
0 0 0 0 0 0 1
1 0 0 1 1 1 1
0 0 1 0 0 1 0
0 0 0 0 1 1 0
1 0 0 1 1 0 0
0 1 0 0 1 0 0
0 1 0 0 0 0 0
0 0 0 1 1 1 1
0 0 0 0 0 0 0
0 0 0 0 1 0 0
Ahora Vamos a hacer que nuestro display muestre de forma descendente todos los números (de 9 a 0) con un
intervalo de separación de 1 segundo. Recuerden que como es ANODO COMUN se encienden en 0V (LOW).
void loop() {
// 9
digitalWrite(7, LOW);
digitalWrite(8, LOW);
digitalWrite(9, LOW);
digitalWrite(10, HIGH);
digitalWrite(11, HIGH);
digitalWrite(12, LOW);
digitalWrite(13, LOW);
delay(1000); // espera 1 segundo
// 8
digitalWrite(7, LOW);
digitalWrite(8, LOW);
digitalWrite(9, LOW);
digitalWrite(10, LOW);
digitalWrite(11, LOW);
digitalWrite(12, LOW);
digitalWrite(13, LOW);
delay(1000);
// 7
digitalWrite(7, LOW);
digitalWrite(8, LOW);
digitalWrite(9, LOW);
digitalWrite(10, HIGH);
digitalWrite(11, HIGH);
digitalWrite(12, HIGH);
digitalWrite(13, HIGH);
delay(1000);
// 6
digitalWrite(7, LOW);
digitalWrite(8, HIGH);
digitalWrite(9, LOW);
digitalWrite(10, LOW);
digitalWrite(11, LOW);
digitalWrite(12, LOW);
digitalWrite(13, LOW);
delay(1000);
// 5
digitalWrite(7, LOW);
digitalWrite(8, HIGH);
digitalWrite(9, LOW);
digitalWrite(10, LOW);
digitalWrite(11, HIGH);
digitalWrite(12, LOW);
digitalWrite(13, LOW);
delay(1000);
// 4
digitalWrite(7, HIGH);
digitalWrite(8, LOW);
digitalWrite(9, LOW);
digitalWrite(10, HIGH);
digitalWrite(11, HIGH);
digitalWrite(12, LOW);
digitalWrite(13, LOW);
delay(1000);
// 3
digitalWrite(7, LOW);
digitalWrite(8, LOW);
digitalWrite(9, LOW);
digitalWrite(10, LOW);
digitalWrite(11, HIGH);
digitalWrite(12, HIGH);
digitalWrite(13, LOW);
delay(1000);
// 2
digitalWrite(7, LOW);
digitalWrite(8, LOW);
digitalWrite(9, HIGH);
digitalWrite(10, LOW);
digitalWrite(11, LOW);
digitalWrite(12, HIGH);
digitalWrite(13, LOW);
delay(1000);
// 1
digitalWrite(7, HIGH);
digitalWrite(8, LOW);
digitalWrite(9, LOW);
digitalWrite(10, HIGH);
digitalWrite(11, HIGH);
digitalWrite(12, HIGH);
digitalWrite(13, HIGH);
delay(1000);
// 0
digitalWrite(7, LOW);
digitalWrite(8, LOW);
digitalWrite(9, LOW);
digitalWrite(10, LOW);
digitalWrite(11, LOW);
digitalWrite(12, LOW);
digitalWrite(13, HIGH);
delay(1000);
}
Vamos a simplificar nuestro código y realizarlo para que muestre de forma ascendente (de 0 a 9) todos los
números con un intervalo de separación de 1 segundo.
int pausa=1000; // Variable que define el intervalo de tiempo entre cada digito
void display (int a, int b, int c, int d, int e, int f, int g) // Funcion del display
{
digitalWrite (7,a); //Se reciben 7 variables y se asignan a cada una de las salidas
digitalWrite (8,b);
digitalWrite (9,c);
digitalWrite (10,d);
digitalWrite (11,e);
digitalWrite (12,f);
digitalWrite (13,g);
}
Material requerido.
Una Protoboard.
Cables de protoboard
Un Potenciómetro.
Los displays LEDs de 7 segmentos, que vimos en las sesiones anteriores, están muy bien, son baratos y prácticos,
pero tienen el inconveniente de que no pueden mostrar mensajes de texto, sino solo números.
Se echa de menos algún sistema para mostrar mensajes de texto sencillos, y por eso se comercializan los displays
LCD. Son faciles de encontrar en diversos formatos: 16×2 (16 caracteres x 2 líneas) o LCD 16×4 (16 caracteres x4
líneas).
LCD viene del inglés Liquid Crystal Display, o sea Pantalla de cristal liquido.
Son una opción muy sencilla de usar, y además, dan un toque muy pro a vuestros proyectos, y por eso, en los
últimos años los displays LCD han ganado mucha aceptación en productos comerciales de todo tipo.
Básicamente porque:
o Son baratos.
o Están disponibles en varios tamaños y configuraciones.
o Son de bajo consumo.
o Muy prácticos si te basta con mostrar solo texto (y algunos caracteres especiales).
Este es el esquema para potoboard:
El programa de control
Vamos a usar una librería de control del panel LCD, que viene incluida en nuestro Arduino. Pinchad en:
\\Programa\Importar Libreria\LiquidCrystal
Y ahora podemos importar uno de los ejemplos o escribir el nuestro, comentando el código. Lo primero es que al
importar la librería nos ha escrito esto:
#include <LiquidCrystal.h>
Despues, hay que inicializar la librería. Creamos una instancia llamada lcd, de la clase LiquidCrystal y le pasamos
como parámetros los pines que hemos usado:
LiquidCrystal lcd(7, 8, 9, 10, 11, 12); // ( RS, E, d4, d5, d6, d7)
Tengan cuidado porque los pines que hemos usado, no corresponden a los ejemplos de Arduino, así que
pueden cargarlos, pero asegúrense de cambiar la línea de definición de los pines, o no correrán.
El resto es sencillo:
void setup()
{
lcd.begin(16, 2); // Fijar el numero de caracteres y de filas
lcd.print("Prometec.net"); // Enviar el mensaje
}
void loop()
{
lcd.setCursor(0, 8); // set the cursor to column 0, line 1
lcd.print(millis() / 1000); // print the number of seconds since reset:
}
Estos display son muy pesados de cablear, pero muy sencillos de utilizar.
Vamos a probar sacando un reloj (muy sencillo de momento). Si recordáis las funciones que usamos en las ultimas
sesiones, podemos recuperar alguna para presentar el valor de millis() como un reloj:
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
void setup()
{
lcd.begin(16, 2); // Fijamos el numero de caracteres y filas
lcd.print("Prometec.net"); // Aqui va el mensaje
}
void loop()
{
lcd.setCursor(6, 1); // Nos ponemos en la Línea 1, posicion 6
String s = reloj() ;
lcd.print(s) ;
}
String reloj()
{
int n = millis() / 1000 ; // Lo pasamos a segundos
int segundos = n % 60 ;
int minutos = n / 60 ;
Merece la pena, comentar algunas cosas de este código. En primer lugar en la función reloj, calculamos los
minutos y segundos a partir del reloj interno de Arduino en milisegundos, no hay nada nuevo en esto. Pero vean
que hemos definido reloj como String:
String reloj()
Eso significa que vamos a devolver un parámetro tipo String a quien nos haya llamado. En algún punto de la
función habrá que hacer un return( String).
Para sacar el mensaje de texto. Todo lo que ya saben de Serial.print() se usa exactamente igual con esta
instrucción. Y por último, tenemos una línea como esta:
lcd.setCursor(6, 1); // Nos ponemos en la Línea 1, posicion 6
Que lo que hace es posicionar el cursor del panel, en la posición 6 de la segunda línea, para escribir la hora
centrada.
Vamos a definir un carácter propio, para digamos, el símbolo de grados centígrados, por ejemplo.
Lo primero que tienen que saber, es que los caracteres se definen con un array ( si, de nuevo) de 8×8, como si los
dibujaran en una cuadricula de ese tamaño, y rellenando el cuadradito completo.
byte grado[8] =
{
0b00001100, // Los definimos como binarios 0bxxxxxxx
0b00010010,
0b00010010,
0b00001100,
0b00000000,
0b00000000,
0b00000000,
0b00000000
};
Y ahora ya estan disponibles. Tengan en cuenta que solo podemos definir 8 caracteres especiales en un momento
dado (Aunque podemos definir 30 arrays, de caracteres y crearlos y matarlos sobre la marcha).
Aqui tenemos un ejemplo del programa:
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
byte grado[8] =
{
0b00001100,
0b00010010,
0b00010010,
0b00001100,
0b00000000,
0b00000000,
0b00000000,
0b00000000
};
void setup()
{
lcd.begin(16, 2); // Hay que inicializar el LCD
lcd.createChar(1, grado);
lcd.setCursor(0, 0);
lcd.print("Estamos a 25");
lcd.write(1);
}
void loop()
{
}