Tutorial Arduino
Tutorial 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.
Por ello se desarrollan los lenguajes de ordenador, para dar instrucciones a una máquina de
forma:
Precisa: Sin ambigüedades inherentes a la comunicación humana.
Univoca: Solo se puede interpretar de una manera.
Concisa: Preferiblemente ordenes cortas.
El IDE de Arduino se programa en una variante de C++ , que es un lenguaje muy extendido
por sus características, aunque no es un lenguaje sencillo. C++, que fija reglas estrictas de
cómo escribir estas instrucciones.
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.
Funcion del compilador
El compilador convierte nuestras instrucciones (código fuente) en instrucciones del procesador
(código ejecutable).
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 “ } “.
Es imperativo que a cada apertura de una llave corresponda un cierre de llave. En sucesivos
capítulos ampliaremos este concepto.
Por ahora resaltar las líneas que aparecen dentro de los bloques principales:
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.
Parece obligado en el mundo Arduino, que el primer programa que hagamos sea el blinking
LED, y está bien porque ilustra algunas ideas interesantes en cuanto a sus posibilidades:
La capacidad de Arduino para interactuar con el mundo externo. Algo bastante inusitado
para quienes estén acostumbrados a la informática tradicional, donde la potencia de cálculo ha
crecido de forma espectacular, pero sigue siendo imposible (o casi), influir en el mundo exterior.
Entradas: Para leer información digital del mundo exterior.
Arduino dispone de 14 pines que pueden ser usados de este modo, numerados del 0 al 13:
pines del 0 al 13
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 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.
void setup()
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.
digitalWrite( 13 , HIGH) ;
digitalWrite( 13 , LOW) ;
El 13 indica el pin a utilizar y HIGH, 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.
Para hacer este retraso de, digamos, un segundo, utilizaremos:
Por tanto para programar una luz que se enciende y se apaga, tendríamos que generar una
secuencia de órdenes (Como en una receta e cocina) que hicieran:
1. Informar a Arduino de que vamos a utilizar el pin13 para escribir valores( en el Setup).
2. Encender el LED : Poner valor alto ( 5V) en dicho pin.
3. Esperar un segundo.
4. Apagar el LED: Poner valor bajo (0V) en dicho pin.
5. Volver a esperar un segundo.
o Si omitiéramos este segundo retraso, apagaría la luz y volvería a empezar
encontrándose la orden de volver a encender. No apreciaríamos que se había apagado.(No
espero que me creáis. Comprobadlo).
o El procesador de Arduino UNO es muy lento desde el punto de vista
electrónico, pero es capaz de conmutar la luz( pasar de encendido a apagado y vuelta a
encender) unas 15.000 veces por segundo.
El primer concepto que tenéis que fijar, es que los ordenadores procesan las ordenes en
secuencia, una instrucción después de otra y en el orden en que se las dais. Nuestro
programa instruye al ordenador para que ejecute esas instrucciones y fija el orden en el que
se ejecutan.
La forma de escribir un programa en Arduino C++ que haga lo anteriormente descrito es algo
parecido a esto Descargar:
void setup()
void loop()
}
Nótese el sangrado de las líneas para destacar los bloques de código. Esto se
considera buena práctica y os lo recomendamos encarecidamente, porque facilita mucho la
comprensión del programa.
Cuando os equivoquéis ( y creadme, os vais a equivocar) el sangrado ayuda,y mucho,
a visualizar el programa.
Solo hay dos tipos de programadores. Los que se equivocan y los que se van a
equivocar
Solo nos falta ya, comprobar si hay errores y para ello pulsamos el icono en amarillo:
Si todo va bien,( si no hay errores en rojo) podemos compilar y volcar con la siguiente flecha,
En caso contrario ( y creedme que os pasará con frecuencia) habrá que revisar los posibles
errores y corregirlos. Volveremos sobre esto en el futuro.
La flecha en amarillo volcara nuestro programa al Arduino y podremos comprobar que la luz
del pin 13 parpadea con un retraso de un segundo entre encendido y apagado.
Sugerencia: Si modificamos los valores del delay, modificaremos la cadencia del parpadeo.
Nota: Esto no funcionara con ningún otro Pin del Arduino UNO, porque solo el 13 tiene
un LED conectado.
RESUMEN DE LA SESIÓN
En esta sesión hemos aprendido varias cosas importantes:
MATERIAL REQUERIDO.
Una Protoboard.
Un diodo LED
Cuando dejamos fluir agua de un sitio alto a otro más bajo, el agua corre libremente mientras
no se lo impidamos, y siempre de arriba abajo. Decimos que las diferentes alturas suponen
una diferencia de potencial entre ambos puntos que puede ser transformada en trabajo útil.
Cuando existe una diferencia de tensión eléctrica (o diferencia de potencial) entre dos puntos
con conexión, la electricidad fluye del positivo (o de mas carga) hacia el negativo o menos, y
también podemos obtener trabajo útil de este principio.
Aunque la física detrás de estos dos ejemplos es diferente, conceptualmente son bastante
parecidos y por ello hablamos de:
La idea es que la corriente eléctrica fluye del positivo al negativo porque hay una diferencia de
tensión (que medimos en Voltios de símbolo V) pero esto no es una medida absoluta sino la
diferencia que hay entre los puntos en que lo medimos.
De la misma manera, la diferencia de altura entre dos puntos solo representa eso,
una diferencia y no indica a qué altura se encuentran con respecto a una referencia más o
menos arbitraria.
V=RxI
I=V/R
Lo que implica que si la resistencia del circuito es nula (o casi, como en el caso de un cable de
cobre) la intensidad de la corriente se dispara y puede llegar a fundir el cable o componente
que encuentre.
Vemos a la izquierda el símbolo del diodo LED que es emisor de luz y por eso tiene esas
flechitas salientes para indicarlo (LED viene del inglés Light Emitting Diode, o diodo emisor de
luz).
A su vez vemos a la izquierda las letras GND para indicar que es el negativo. Tiene muchos
nombres: Masa, El símbolo –, Tierra( aunque no es lo mismo), Ground, Negativo, cátodo.
Por último a la derecha el símbolo de +5V indica el extremo de tensión positiva o positivo y a
veces se representa como Vcc. Las líneas rectas y negras indican conexión eléctrica mediante
cables conductores.
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.
Una vez comprendido el esquema eléctrico del circuito, veamos la conexión en la Protoboard:
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.
Podemos ahora volcar el programa que hicimos en la sesión 2 (o simplemente cargar el
ejemplo Blink), siguiendo el procedimiento que definimos allí, 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.
RESUMEN DE LA SESIÓN
.
MATERIAL REQUERIDO.
8 x diodos LED.
Algunos cables de Protoboard.
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()
…………………………
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.
Esta solución es la que podríamos describir como de fuerza bruta, pero no es muy elegante,
es trabajosa y probablemente cometeríamos más de un error al escribirla, porque las
personas tendemos a equivocarnos haciendo tareas repetitivas aburridas (y esta lo es
mortalmente, imaginad un circuito de de 16 LEDs).
En cambio los ordenadores no se aburren y además C++ nos ofrece un medio cómodo de
indicarle que debe repetir algo un número definido de veces. Este medio es la instrucción For
que podemos usar en combinación con una variable.
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 variables es numérica y
entera, sin decimales.
Iremos viendo que existen otros tipos de variables. Volveremos sobre este tema en
próximas sesiones.
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()
pinMode( i , OUTPUT) ;
}
Aunque la sintaxis parece complicada al principio, uno se acostumbra con rapidez. Aquí lo
importante es que for necesita 3 parámetros separados por un carácter de punto y coma.
Una variable que ira 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++ que es pedirle a C++
que incremente en uno la variable i, al final de cada iteración.
Con el mismo criterio podríamos escribir la función loop() así Descargar:
void loop()
digitalWrite( i , HIGH) ;
delay (500) ;
digitalWrite( i , LOW);
delay (500) ;
En la sesión 3 el código era muy similar excepto en que escribíamos el valor 13 para el único
pin que tenía un LED conectado. Aquí asignamos el pin con una variable i , que va tomando
los valores de 6 hasta el 13 para el pin.
Nótese que la instrucción for no lleva un punto y coma al final. Esto es porque se
aplica al bloque de instrucciones que le siguen entre llaves, como es el caso del loop() La
iteración realiza las cuatro instrucciones que siguen a la línea del for, porque están dentro de
un bloque de instrucciones.
Las instrucciones que se aplican a bloques de código, no llevan punto y coma al final.
En el caso de particular de que el bloque lleve una única línea de código, las llaves
pueden ser omitidas, como en el caso de la instrucción for en la función setup() de arriba.
ESQUEMA ELECTRÓNICO DEL CIRCUITO
.
El esquema del circuito es muy similar al de la sesión 3, salvo por el hecho de que colocamos
en la Protoboard 8 LEDs.
Cuando nuestro programa levante el pin correspondiente a valor a HIGH, se cerrar el circuito
iluminándose el LED asociado.
Con este circuito, y con el programa 4.1 descrito en las páginas anteriores, tendremos un
efecto de luces similar al del “coche fantástico” (O de los Zylon para los aficionados a la
ciencia ficción).
En general, se considera buena costumbre (la recomendamos), montar los circuitos
que veamos a partir del esquema electrónico del mismo, más que a partir del diagrama de
conexiones de la Protoboard.
La razón es que con el esquema, la comprensión del circuito es completa y se evita la
tentación de copiar la práctica sin necesidad de entenderla.
Además, el diagrama electrónico del circuito es su completa descripción y suele
resultar más sencillo comprender la función del mismo. En cambio a medida que los circuitos
se hacen más complejos, comprender su función desde un esquema de Protoboard puede
complicarse mucho, y peor aún llevar a una interpretación errónea.
VARIANTES DEL PROGRAMA CON EL MISMO CIRCUITO
.
Este montaje nos permite jugar con las luces y se presta a varios programas diferentes para
conseguir distintos efectos.
Por ejemplo, con el programa anterior 4.1, el efecto no es exactamente el del coche 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.
Desde luego que sí, bastaría con usar dos ciclos for, similar a lo siguiente Descargar:
void loop() // Prog_4_2
digitalWrite( i , HIGH) ;
delay (500) ;
digitalWrite( i , LOW);
delay (500) ;
digitalWrite( i , HIGH) ;
delay (500) ;
digitalWrite( i , LOW);
delay (500) ;
}
El primer ciclo for hace que las luces se encienda en secuencia desde la 6 hasta la 13.
El segundo bucle 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 vuelta a empezar.
En el segundo bucle hemos hecho una cuenta atrás diciéndole a la variable i que
se decrcmentara 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.
Otra variante seria, hacer un efecto de ola en al que las luces subieran dejando encendidos
los LEDs previos hasta alcanzar el máximo y ahora descender apagando los LEDs superiores.
Os recomendamos intentar resolver el problema como desafío, antes de buscar una solución.
Programar es en parte aprender las instrucciones de un lenguaje (la parte fácil), y otra
más difícil que es aprender a resolver los problemas de un modo que nos permita darle
instrucciones a un ordenador para que lo lleve a cabo.
Estos procedimientos secuenciales de cómo resolver un cierto tipo de problemas es lo
que se conoce como un algoritmo.
Según el problema que abordemos el algoritmo será más o menos complicado pero
aprender a programar tiene más que ver con desarrollar esta capacidad de resolver
problemas lógicos en una secuencia de pasos que podamos codificar en un ordenador.
Por cierto, cualquiera puede aprender a programar. No lo dudéis. Solo que como en
todo, a unos les lleva más tiempo que a otros desarrollar la habilidad necesaria. Al principio
muchos me dicen que les duele la cabeza de pensar en este tipo de cosas, pero os animo a
continuar (poco a poco si es preciso) porque os encontrareis que vale la pena.
RESUMEN DE LA SESIÓN
.
En esta sesión hemos aprendido varias cosas importantes:
La instrucción For, nos permite iterar un bloque de instrucciones tantas veces le
indiquemos.
Hemos visto uno de los tipos de variables que C++ acepta: los enteros.
Hemos introducido el concepto de algoritmo, como un procedimiento secuencial para
resolver un problema concreto y lo hemos aplicado a varios ejemplos de programas sencillos
con luces.
MATERIAL REQUERIDO.
Arduino Uno o similar. Un PC con el entorno de Arduin
configurado.
Una Protoboard.
Un diodo LED.
Un pulsador.
Algunos cables de Protoboard.
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 todo o nada para
encender un LED. De la misma manera podemos leer valores, todo o nada, 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).
Y aquí tenemos el esquema para protoboard del circuito.
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;
void setup()
Atención: C++ diferencia entre mayúsculas y minúsculas y por tanto LED, Led y led no
son lo mismo en absoluto. Del mismo modo, pinMode es correcto y en cambio pinmode
generará un error de compilador fulminante.
He usado la variable boton sin acento porque no es recomendable usarlos ni la ñ en
los nombres de variables, porque pueden pasar cosas extrañas.
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()
¿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 una valor dado
x es HIGH, entonces !x es LOW y viceversa.
Un operador es un símbolo que relaciona varios valores entre sí, o que modifica el
valor de una variable de un modo previsible.
Ejemplos de operadores en C++ son los matemáticos como +,-,* , / ; y hay otros como
la negación ! o el cambio de signo de una variable : – x. Iremos viendo más.
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
void loop()
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 el D6 leerá TRUE y por tanto pondrá LED a FALSE. En caso
contrario encenderá el LED.
De hecho podríamos escribir una variante curiosa del blinking LED usando el operador
negación:
void loop()
{
bool valor = digitalRead (LED) ;
delay ( 1000) ;
Podemos leer la situación actual de un pin (nos devuelve su estado actual), aún
cuando lo hayamos definido como salida, En cambio no podemos escribir en un pin definido
como entrada.
La primera linea lee la situación del LED y la invierte en la segunda línea, después escribe
esto en LED. Y puestos a batir algún record, podemos escribir el blinking led en solo dos
líneas:
void loop()
delay ( 1000) ;
Las instrucciones dentro de los paréntesis se ejecutan antes que las que están fuera
de ellos. Por eso el digitalRead se ejecuta antes que el digitaWrite..
RESUMEN DE LA SESIÓN
.
o Hemos visto una forma de leer señales digitales del mundo exterior además de
poder enviarlas:
digitalRead( pin)
digitalWrite( pin , valor)
o Hemos conocido un nuevo componente: el pulsador.
o Conocemos un nuevo tipo en C++, el booleano y un nuevo operador de
negación.
MATERIAL REQUERIDO.
Una Protoboard.
Un diodo LED.
Un pulsador.
Algunos cables de Protoboard..
LA INSTRUCCIÓN IF
En este capítulo vamos a presentar unas instrucciones nuevas de C++, que nos permitan
tomar decisiones para hacer una cosa u otra.
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 esle, 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 ;
..............
void loop()
if ( valor)
else
Leemos primero el botón a una variable bool y después decidimos si encender o apagar el
LED dependiendo de qué 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.
Se puede utilizar una instrucción if omitiendo la cláusula de else si se desea, pues
esta es opcional.
VOLVIENDO CON LOS BOTONES
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í y os
recomiendo que lo probéis en vuestro Arduino:
void loop()
{
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.
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 varios 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.
void loop()
{
delay(250) ;
Muy importante: Nótese que la condición es (valor == false), con doble = . En C++ la
comparación de dos valores usa ==, la asignación de valor a una variable solo uno. Esto es
fuente de errores frecuentes al principio (entre novicios inexpertos).
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.
Si queremos poder mantener pulsado sin que se produzca este efecto hay que sofisticar un
poco más el programa:
void setup()
pinMode(LED, OUTPUT);
void loop()
estado = digitalRead(boton);
digitalWrite(LED, !digitalRead(LED));
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: <=
Vale la pena comentar aquí que, a pesar de su aparente inocencia, los botones tienen una
sorprendente habilidad para complicarnos la vida, y que en la práctica la combinación de
rebotes y la necesidad de corregirlos, junto al uso de pullups que garanticen la correcta
lectura, pueden hacer que su uso se pueda complicar mucho más de lo que parece, sino se
estudia el problema con calma.
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 !
If ( boton1 && boton2) Que ambos botones estén sin pulsar
If ( !( boton1 && boton2)) Que ambos estén pulsados.
If( boton1 || boton2 ) Que al menos uno este sin pulsar, o ambos.
RESUMEN DE LA SESIÓN
.
o Hemos visto la instrucción if / else.
o Vimos varios programas con pulsadores y como hacer el debouncing.
o Hemos presentado los operadores lógicos de relación y comparación.
o Continuamos escribiendo pequeños programas para desarrollar la forma de
pensar necesaria para escribir nuestras propias aplicaciones.
MATERIAL REQUERIDO.
Más antes que después, vamos a necesitar comunicar nuestro Arduino con nuestro PC. Las
razones son varias, enviarle órdenes o recibir información o señales por ejemplo.
Los PCs disponen de teclados, pantallas y adaptadores de red, pero con Arduino tenemos que
usar el puerto USB que establecerá una conexión en serie con nuestro PC.
La comunicación en serie es muy sencilla, bastan dos hilos para enviar una diferencia de
tensión entre ellos y poder marcar niveles alto (5V) y bajo(0V) y con esto podemos transmitir
información digital. Ahora solo nos falta pactar dos cosas entre quien envía y quien recibe:
Un código común para codificar los caracteres que enviamos.
Un acuerdo de velocidad para saber a qué ritmo hay que leer los datos.
El código común que vamos a usar con Arduino se llama código ASCII y es estándar en
todos los PCs. Es una forma de codificar las letras mediantes números que representas estos
caracteres. Recordad que solo podemos transmitir unos y ceros.
Así por ejemplo la letra A se representa por el numero 65, la B el 66, C el 67… Prácticamente
todos los PCs actuales utilizan este código y eso incluye a Windows, Mac y Linux (y por eso
podemos leer emails enviados desde distintas plataformas), pero es importante comprender
que esté es uno más entre varios códigos de caracteres posibles (EBCDIC por ejemplo).
Actualmente, en realidad, se suele usar una extensión del código
ASCII (llamada Unicode) que permita el uso de caracteres no incluidos en la tabla original, y
que permita representar caracteres como las Ñ, o acentos para el español, pero también
alfabetos distintos como el Kanji chino o el alfabeto cirílico. Y este es el motivo por el que
podéis leer las letras chinas o rusas en las páginas de internet de estos países..
El otro factor a pactar para realizar una comunicación serie es la velocidad. Dado que solo
disponemos de dos hilos para transmitir, necesitamos saber cuándo hay que leer la línea y
esto se hace estableciendo un acuerdo de velocidad. Si la velocidad de envío es distinta de la
velocidad de lectura, el mensaje final será irreconocible.
Buena parte de los errores de comunicación serie programando con Arduino se suelen deber
a una diferencia de velocidad entre el emisor y el receptor.
Esta velocidad se mide en bits por segundo y vamos a ver que Arduino soporta diferentes
velocidades de comunicación serie.
Serial.begin( velocidad ) ;
Nótese que Serial tiene la S mayúsculas y que C++ diferencia entre mayúsculas y
minúsculas
La velocidad es una 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:
void setup()
{
}
void loop()
{
int i = 54 ;
Serial.println( i );
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 115200, 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. 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:
int i = 4 * 2 resultado = 8
Los paréntesis fuerzan las operaciones de una forma clara y conviene utilizarlos ante la duda
porque de otro modo, detectar los errores de operación puede volverse muy difícil
especialmente cuando uno empieza a programar.
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.
void setup()
void loop()
{
if ( i % 2 == 0)
Serial.println("Es par.") ;
else
Serial.println("Es impar");
En este programa hemos usado de un modo diferente el Serial.println() pasándole una String
de texto entrecomillada. Serial.print() envía el texto ( entrecomillado) 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()
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 ;
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()
Serial.println( a + b);
void loop()
int resultado = 25 ;
}
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).
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 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.
char c = ‘a’ ;
String s =”a” ;
void setup()
{ Serial.begin(9600); }
void loop ()
delay(25);
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 intro 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 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 os 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, comprobareis
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.
RESUMEN DE LA SESIÓN
.
o Hemos visto como establecer la comunicación con el PC externo, tanto para
enviar como para recibir mensajes enteros y de texto.
o Hemos presentado los tipos String y char.
o Hemos visto las reglas básicas para operar con enteros y con Strings.
o Presentamos una nueva instrucción: while.
MATERIAL REQUERIDO.
Arduino Uno o similar. Un PC con el entorno de A
configurado.
Ya hemos utilizado muchas veces el monitor serie del IDE para mostrar valores de las
variables de nuestro programa. Pero en ciertas ocasiones puede ser útil verlo en forma
de gráfica en vez de en datos numéricos, por ejemplo, para ver la tendencia de los datos de
una forma sencilla y clara.
Pues resulta que el IDE de Arduino incorpora desde la versión 1.6.6 una herramienta
llamada Serial Plotter que nos permite precisamente crear gráficas a partir de las variables
que le indiquemos. Es muy sencillita de usar y por el momento no ofrece muchas opciones,
pero seguramente vayan añadiendo características nuevas con nuevas versiones.
void setup()
Serial.begin(9600);
void loop()
int valor;
valor = random(0,100);
Serial.println(valor);
delay(250);
Ahora en vez de abrir el Monitor Serie, abriremos el Serial Plotter, que está en la barra de
herramientas, justo debajo.
CÓMO INCLUIR MÁS VARIABLES
void setup()
Serial.begin(9600);
void loop()
int valor1;
int valor2;
valor1 = random(0,100);
valor2 = random(0,100);
Serial.print(valor1);
Serial.print(",");
Serial.println(valor2);
delay(250);
}
RESUMEN DE LA SESIÓN
.
o Hemos descubierto una nueva herramienta que nos permite
hacer gráficas fácilmente desde el mismo IDE de Arduino.
o Sabemos incorporar varias variables a la gráfica.
o Ahora que sabemos que existe esta herramienta y cómo se utiliza, podéis
incorporarla a vuestros proyectos para realizar gráficas de las medidas de sensores, la
velocidad de un motor, o lo que se os pase por la cabeza.
MATERIAL REQUERIDO.
LA PRIMERA FUNCIÓN: CALCULANDO SI UN NÚMERO ES
PRIMO
.
Ya hemos comentado antes, que programar es un poco como andar en bici, se aprende
pedaleando y a programar… programando. Hay que ir aprendiendo la sintaxis del lenguaje,
C++ en nuestro caso, pero también aprendiendo a resolver problemas lógicos y partirlos en
instrucciones.
Hacer cursos de programación (o de andar en bici) está bien, pero al final hay que ponerse a
programar y tener problemas, porque solo teniéndolos y resolviéndolos, solo o con ayuda, se
aprende. No se puede aprender a nadar sólo estudiando.
Con un cierto temblor de manos, 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.
Esta sesion va a requerir un esfuerzo un poco mayor que las anteriores porque vamos a
empezar a entrenar un musculo poco usado,el cerebro, en una tarea poco frecuente, pensar.
Y esto es algo que exige un poco e esfuerzo, pero es necesario para avanzar.
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.
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
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()
Veamos cómo podría ser el código de la función Primo():
return(false) ;
return (true) ;
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. Recordad que al definir la función dijimos bool
Primo (int n) donde n representa el valor que queremos probar. Así pues Descargar:
void loop() // Prog_8_1
bool p = Primo(x);
if (p )
else
void loop()
bool p = Primo(x);
control = false ;
return(false) ;
int contador = 1 ;
void loop()
{
if (Primo(x) )
if ( contador++ % 8 == 0)
Serial.println( String(x)+"," ) ;
else
}
control = false ;
Ahora el programa formatea la salida de una forma un poco más presentable y cómoda de
leer.
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 tenemosuna 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, ene este caso en el if, y después se incremente
en 1 su valor.
Si hubiéramos escrito:
if ( ++contador % 8 == 0)
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:
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 anda con tiento.
int i = 32767 ;
Serial.println ( i+1);
int i = 32767 ;
Esto no es un error, sino que se decidió así en su día y C++ no controla los
desbordamientos, así que mucho cuidado, porque este tipo de errores pueden ser muy
complicados de detectar
Instrucción Significa
Aquí declaramos que vamos a pasar a Funcion5, tres argumentos en el orden definido, un
entero un String y por ultimo un long.
RESUMEN DE LA SESIÓN
.
o Hemos definido una función propia para saber si un número es primo.
o Vimos que el tipo entero tiene un límite de tamaño.
o Conocimos tipos con mayor y menor capacidad para manejar números enteros
mas o menos grandes, pero que todos siguen teniendo un límite de tamaño.
o El efecto de desbordamiento de tipos es clave y debe ser tenido muy en
cuanta cuando operamos con enteros.
o Hemos ido jugando con problemas lógicos y hemos visto algunas soluciones
que os pueden ayudar a plantear las vuestras propias.
MATERIAL REQUERIDO.
Hemos visto ya como definir funciones. En esta sesión vamos a plantear un programa que
acepte un número desde la consola y compruebe si es o no primo. Y en caso de no serlo, nos
calcule cuales son los divisores primos de este.
Primo() – Calcula si un número dado es primo, devuelve true y en caso contrario false.
Divisores() – Para un número dado nos devuelve sus divisores primos.
GetLine() – Vamos a definir una función genérica que recoja una cadena de texto de la
puerta serie, para procesarla a posteriori. En este caso recogerá el número a probar.
La idea es, que nuestro programa empiece comprobando si un numero es primo. Si lo es, bien
por el. SI no llamaremos a una función que calcule cuales son sus divisores. Y por ultimo
necesitamos de algo que nos pueda dar un número desde la consola para probarlo y por eso
escribimos otro programa que nos permita recibir cómodamente este numero de entrada.
Fijaros que casi sin haber escrito una linea de programa, ya he decidido como partirlo en
bloques mas sencillos de manejar y programar. En otras palabras, he buscado una estrategia,
de resolución
Esto es un poco trampa. Parece que se me acaba de ocurrir instantáneamente pero
no (Aunque parezca increíble).
Después de pensar un rato y pasar un rato mayor escribiendo y afinando el programa,
da gusto presentarlo como si fuera fácil. Que lo es, pero lo que no ves, es la de horas que hay
que estar haciendo pruebas hasta todo va encajando y queda afinado.
Con tiempo y práctica ( No, no mucha) iréis mejorando con rapidez, en vuestra
habilidad de romper problemas en pedazos manejables, simplemente requiere tiempo y
entrenamiento, y esto es algo que os será útil no solo para programar.
OPERANDO CON ARRAYS.
Con la función Primo() que vimos en la sesión anterior, a medida que el tamaño del número a
probar, crece, el tiempo que tarda en determinar si es primo también, ya que dividimos por
todos los números que le preceden.
Una manera más eficaz de calcular si un número es primo, es dividirlo solo por los números
primos menores que el. Pero para esto necesitaríamos un modo de archivar estos primos.
Podríamos ejecutar primero el programa Prog_8.3 para hallar los N primeros números primos,
y si dispusiéramos de algún medio para guardarlos, tendríamos un sistema más eficaz para
decidir si un número es o no primo.
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 el primer caso definimos un array de enteros, de una sola dimensión con 5 elementos, sin
asignar valores de momento.
En el segundo caso 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 Descargar:
int serie2[] = { 3,5,6,12, 23} ; // Prog_9_1
void setup()
Serial.begin(9600) ;
}
void loop()
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.
Un error muy peligroso, y difícil de detectar sería algo así (Prog_9.2):
Uno esperaría que C++ generase un error, ya que definimos un array de 5 elementos y
hacemos referencia a 100, pero no. Nuevamente C++ nos sorprende devolviendo
correctamente los 5 primeros valores y luego sigue leyendo posiciones de memoria
consecutivas tan tranquilo, como si tuvieran sentido.
C++ espera que seamos nosotros quienes controlemos esto, así que mucho cuidado
Por último, mencionar que podemos manejar arrays de varias dimensiones:
Imaginad 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.
} ;
Hemos definido un array enumerando sus elementos, entre llaves y separados por comas.
Es importante percatarse de que después de copiar y pegar las salida de Prog_8.3
hemos borrado la coma después del 1021, porque si no daría un error de sintaxis al definir el
array.
Obsérvese que hay un punto y coma después de la llave de cierre del array. Aunque
está entre llaves es una instrucción de asignación y no una definición de función.
Al definir el array por enumeración, si el número es alto podemos perder de vista
cuantos elementos contiene. Si necesitamos calcular el número de miembros podemos utilizar
la función sizeof():
int size = sizeof(P) / sizeof(int);
Donde P es nuestro array y dividimos por sizeof(int) porque definimos P como int. Y
para este caso devuelve un valor de 172 elementos
Ahora bastaría dividir el número a probar por aquellos elementos del array P, menores que él:
bool Primo(int x)
{
int index = 0 ;
{ if ( x % P[index++] == 0)
return(false);
}
return(true);
Recorremos los valores almacenados en el array P[] mediante index al que vamos
incrementando en cada iteración, hasta que nos toque probar un primo mayor que el valor que
comprobamos.
FUNCIÓN DIVISORES ()
.
Esta función va a recorrer los elementos del array en tanto en cuanto sean menores (posibles
divisores) que el número que probamos. Si encontramos divisores primos los guardamos en
un array que hemos llamado Div[] al que le asignamos un máximo de 32 divisores:
int Div[32] ;
int Divisores(int x)
{
int k = P[index++] ;
if ( x % k == 0)
Div[pos++]= k ; //Guardamos el divisor en en el array
Div[].
Es importante entender que tanto la función Primo() como Divisores() recorren el array
de números primos hasta que sobrepasan el valor del numero a probar. Si el número que
probamos es mayor que el máximo primo que contiene P[], Podemos obtener resultados
extraños, ya que leeremos más elementos de los que hemos definido.
Este método de buscar divisores es válido solo para números inferiores a 1024( o en
su caso, al máximo número hasta el que hayamos calculado primos), porque un número no
será primo si Primo() lo afirma, ya que encontrará divisores. Pero puede afirmar que un
numero es primo erróneamente si sus divisores son superiores al máximo primo en P[].
LA FUNCIÓN GETLINE()
.
Aunque ya comentamos que podemos usar una función parseInt () incluida en Arduino para
recoger un valor del puerto serie, tiene el inconveniente de que si no recibe una entrada salta
al cabo de un tiempo ( muy escasito) y devuelve 0, por lo que tendríamos que controlar el
valor devuelto para que no se repitiese continuamente.
Por eso vamos a escribir una función de uso general que nos permita recoger una cadena de
texto de la puerta serie sin que salga hasta que reciba un String que vamos a hacer finalice en
intro. De hecho ya vimos este programa, aunque no como función en la sesión Comunicación
con el exterior
String GetLine()
String S = "" ;
if (Serial.available())
{
char c = Serial.read(); ;
{
S = S + c ;
delay(25) ;
c = Serial.read();
return(S) ;
Definimos Getline() de tipo String, porque queremos que nos devuelva un texto.
Comprobamos que hay algo disponible en la puerta serie, y en caso afirmativo construimos un
String S añadiéndole cada uno de los caracteres que leemos del puerto serie, hasta que
encontremos un intro.
Normalmente convendrá comprobar si hay algo disponible en la puerta serie antes de
llamar a GetLine(), y si es así, la comprobación que hace GetLine() de tener algo disponible
en el Serial seria redundante.
Pero si llamáramos a GetLine() sin comprobarlo y esta no lo controlase, quedaríamos
atrapados en esta función hasta que alguien escribiera algo finalizado con intro para poder
salir y podría no ser sencillo comprender el problema.
Nuevamente hemos incluido un delay de 25 ms en el while para asegurarnos de que
Arduino no puede volver a leer mas caracteres antes de que a la velocidad de 9600 bps haya
llegado el próximo carácter. Si la velocidad de comunicación es de 115200 bits por segundo o
más, se puede suprimir este retraso.
EL PROGRAMA PRINCIPAL
.
Podemos ya escribir nuestra función principal loop(), que llame a las funciones que hemos
definido a lo largo de esta sesión, para determinar si un numero que le pasamos por la puerta
serie es primo o no y en caso negativo que nos muestre los divisores primos encontrados.
if (Serial.available())
String s = GetLine();
if ( Primo(i))
else
Serial.print(String(Div[n]) + ",\t");
}
}
Empezamos comprobando si hay algo sin leer en la puerta serie y si es asi llamamos a
GetLine() para que nos consiga lo que hay.
Como GetLine() nos devuelve un tipo String() usamos la función estándar de Arduino C++,
s.toInt() que convierte el contenido String a tipo numérico int.
Después llamamos a Primo() para que compruebe este número. Si es primo, simplemente
imprime un mensaje para confirmarlo. En caso contrario llamamos a Divisores() que busca y
almacena en el array Div[] los divisores primos que encuentra.
RESUMEN DE LA SESIÓN
.
o Hemos operado con arrays de una única dimensión tanto para leer su
contenido como para modificarlo.
o Hemos utilizado el programa de primos como excusa para mostrar cómo se
pueden resolver problemas complejos, dividiéndolos en otros más sencillos que podamos
resolver con funciones simples.
o Esta es, sin duda, una de las habilidades clave para ser un programador
competente, y como todo en la vida requiere practica e ir trabajando.
o También hemos definido una función de propósito general GetLine() que
utilizaremos más veces en el futuro.
MATERIAL REQUERIDO.
Un diodo LED.
Algunos cables de Protoboard..
ANALÓGICO Y DIGITAL
Todas las señales que hemos manejado hasta ahora con nuestro Arduino , de entrada o de
salida, comparten una característica común: Son digitales, es decir que pueden tomar un valor
HIGH o LOW pero no valores intermedios.
Si representamos una el valor de una señal digital a lo largo del tiempo veríamos algo así:
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.
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 como los chicos de Arduino son listos, decidieron emplear 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.
Fijaros en la anchura del pulso cuadrado de arriba. Cuanto mas ancho es,
mas tensión promedio hay presente entre los pines, y esto en el mundo exterior
es equivalente a un valor analógico de tensión comprendido entre 0 y 5V. Al 50% es
equivalente a una señal analogica del 50% de 5V, es decir 2,5. Si mantenemos los 5V un 75%
del tiempo, será el equivalente a una señal analógica de 75% de 5V = 3,75 V.
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( 9, V) ;
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. Fijaros en la
numeración de los pines de la imagen:
{
pinMode( 9, OUTPUT) ;
void loop()
analogWrite (9, i) ;
delay( 10);
{
pinMode( 9, OUTPUT) ;
void loop()
delay( 10);
Aquí aprovecho ( por pura vagancia) 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 os parece el truco?
RESUMEN DE LA SESIÓN
.
o Describimos a grandes rasgos la diferencia ente valores digitales y valores
analógicos.
o Hemos visto como simular valores analógicos en una salida digital de Arduino.
Solo con las salidas que lo aceptan: pines 3, 5, 6, 9, 10 y 1.
Podemos asignar valores entre 0 y 255.
MATERIAL REQUERIDO.
Una Protoboard.
Un diodo LED RGB, independiente, o bien, con montur
Algunos cables de Protoboard..
Hasta ahora hemos usado varias combinaciones de LEDS, pero siempre de un color definido.
Habitualmente los rojos y amarillos son los más fáciles de conseguir, pero se pueden comprar
también en tonos azules, verdes y hasta blancos. No suele haber grandes diferencias entre
ellos excepto en el color.
Pero a veces es interesante disponer de una luz piloto que cambie de color según las
condiciones. Por ejemplo, todos identificamos el verde como una señal de OK, mientras que el
rojo indica problemas y el amarillo… bueno pues algo intermedio.
Poner varios diodos para hacer esto es engorroso y complica el diseño, así que estaría bien
disponer de un diodo al que podamos indicar que color queremos que muestre. Esto es
un LED RGB.
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
Es decir RGB, uno de esos acrónimos que surgen continuamente en imagen, TV, etc.
Un LED RGB es en realidad la unión de tres LEDs de los colores básicos, en un encapsulado
común, compartiendo el Ground (cátodo es otro nombre más para el negativo).
En función de la tensión que pongamos en cada pin podemos conseguir la mezcla de color que
deseemos con relativa sencillez
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.
El pin más largo en estos LED es el GND.
Al lado de GND hay dos pines a un lado y uno solitario al otro. Por lo normal el
solitario es el rojo R.
Así pues el pin out (patillaje) de un RGB LED suele ser R, GND, G, B.
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 como -.
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 aseguraros
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).
void setup()
{
for (int i =9 ; i<12 ; i++)
pinMode(i, OUTPUT);
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(255 ,0 ,0) ;
delay(500);
Color(0,255 ,0) ;
delay(500);
Color(0 ,0 ,255) ;
delay(500);
Color(0,0,0);
delay(1000);
}
Este programa debería producir una secuencia de rojo, verde, azul, 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 uséis Windows disponéis del programa Paint incluido (en el menú
de accesorios) y para quienes uséis Mac o Linux tenéis programas similares.
Así pues para conseguir ese tono de azulito de la imagen basta con que llaméis a
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 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. Probad esto:
pinMode(i, OUTPUT);
void loop()
delay(500);
RESUMEN DE LA SESIÓN
.
o Ya conocemos la utilidad y manejo de un LED RGB.
o Hemos insistido con la programación de los pines de Arduino como salidas
analogías (PWM).
o Hemos introducido la función random() que suele ser sorprendentemente útil
en un montón de situaciones diversas.
MATERIAL REQUERIDO.
Una Protoboard.
Un diodo LED.
Un potenciómetro de 10KΩ
Algunos cables de Protoboard..
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.
Imaginad el volumen de un equipo de música, o el dial que sintoniza una emisora en una radio
FM.
Hay potenciómetros de tantos tamaños, formas y colore,s como podáis imaginar, pero al final
son una resistencia fija de un valor dado (10 kΩ en nuestro caso actual) y un mecanismo que
permita deslizar un dial conductor sobre esa resistencia, que nos permita tomar una parte de
ese valor.
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.
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.
CIRCUITO PARA PROTOBOARD
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.
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.
void setup()
void loop()
Serial.println( Lectura);
delay(200) ;
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.
Al no estar el A5 conectado a ninguna referencia válida, está flotando y los valores
que captura son muestra de esa incoherencia. En realidad lo que está haciendo tu Duino es
captar ruido aleatorio de radiofrecuencia e intentar darle sentido, pero lo tiene mal, como
podeis ver.
No obstante en condiciones normales los valores que leerá seran relativamente bajos.
¿Quieres que las oscilaciones crezcan en valor?. Fácil. Ponle una antena. Vale un simple
cable de protoboard conectado desde el A5 a nada (O si coges el otro extremo entre los
dedos, tu mismo haras de antena). Acabas de construir el receptor de Radio frecuencia mas
inutil del mundo
UN ÚLTIMO COMENTARIO
Decíamos en una sección anterior, que la fidelidad con que podemos muestrear una señal
analógica dependía, básicamente, de la resolución de la muestra y de la velocidad a la que
podíamos muestrear la señal (Sample Rate en inglés).
Ya dijimos que la familia Arduino, dispone de convertidores de 10 bits por lo que nuestra
resolución es de 210 = 1.024 y en el caso del DUE de 212 = 4.096. Pero hasta ahora no hemos
visto a qué velocidad podemos tomar muestras con nuestro Arduino. Vamos a comprobarlo,
con este mismo circuito.
Tenemos una función llamada millis() que nos indica en milisegundos el tiempo transcurrido
desde que iniciamos Arduino y la podemos usar para ver cuantas muestras podemos tomar
por segundo.
void setup()
{ Serial.begin(9600); }
void loop()
{ unsigned long T ;
int n = 0 ;
T = millis();
{ analogRead( A5) ;
Serial.println(n);
Hemos usado un unsigned long para guardar millis porque es el tipo que Arduino usa
internamente para su reloj. Sería un error manejar millis con un int porque su valor máximo es
32.767 y midiendo milisegundos el contador desbordaría en poca más de 32 segundos.
SI corréis este programa en un Arduino UNO os dará, poco más o menos, un resultado de
8.940 muestras o lecturas por segundo. No está mal.
Es adecuado para muestrear señales que no varíen demasiado rápido con el tiempo, como
son casi todos los sensores habituales en la industria, pero que se quedará corto si queréis
muestrear señales de audio.
Para jugar con audio es mejor usar un Arduino DUE. Tiene una velocidad de reloj 4
veces más rápida(os hará falta), capacidad de muestreo a velocidad de audio (40Khz) y
auténticos convertidores DAC (digital to analog converters).
De hecho no es complicado aumentar la velocidad de muestreo hasta unas 20.000
muestras por segundo con un Arduino UNO, pero para eso tenemos que puentear Arduino y
saltar a programar el chip interior Atmega 328. No es momento para ello, pero hay formas.
RESUMEN DE LA SESIÓN
o Ya conocemos el uso del potenciómetro.
o Hemos presentado los conceptos básicos en la conversión analógica to digital.
o Aprendimos a leer las puertas analógicas de Arduino.
o Sabemos que podemos leer las puertas analógicas unas 8.900 veces por
segundo con una resolución de 10 bits, o sea entre 0 y 1.024.
o Conocimos la función millis().
MATERIAL REQUERIDO.
Una Protoboard más cables.
En las sesiones previas hasta ahora, hemos influido en el mundo exterior poniendo una orden
en los pines digitales de Arduino. Por ejemplo poniendo HIGH en un pin y confiando en que
esto sea suficiente para gobernar lo que haya detrás, que hasta ahora han sido diodos LEDs.
Pero la capacidad de Arduino para alimentar ese elemento externo es limitada, y con mucha
frecuencia insuficiente para excitar un actuador exterior.Por eso necesitamos elementos
externos que reciban la señal de control de nuestro Arduino y adapten este control a las
necesidades del mundo exterior.
Para ello vamos a tener que utilizar elementos como transistores, relés, servos y pequeños
motores de diferentes tipos.
Los pines digitales de Arduino permiten un máximo de 40 mA o lo que es lo mismo 5V
x 40mA = 0,2 Watt.
Esto es suficiente para iluminar LEDs o incluso mover un pequeño servo pero es
insuficiente para mover un motorcito de corriente continua o de paso a paso.
Es importante asegurarnos de que lo que conectemos a los pines no sobrepasen las
especificaciones de nuestros Arduinos, porque si los forzamos la cosa acabara oliendo a
plástico quemado y un viaje a la tienda de Arduinos a por otro.
A lo largo de las sesiones anteriores hemos ido conociendo componentes electrónicos de
distintos tipos: Diodos, LEDS, Resistencias, Potenciómetros… y ahora vamos a presentaros
otro que es la base fundamental de toda la electrónica moderna, incluyendo la informática, se
llama transistor.
¡Que no cunda el pánico! Somos conscientes del terror reverencial que el transistor inspira
entre los aficionados novatos a la electrónica y los novicios de Arduino, pero tranquilos, es
mucho más fácil de lo que parece y es un componente muy agradecido en casi cualquier
proyecto que podáis imaginar.
Así que por favor seguid leyendo. Y confio en que acabareis cogiéndole gusto a los
transistores.
EL TRANSISTOR
Siguiendo con las analogías que vimos en la sesión 3, entre el flujo de corriente eléctrica y
flujo de agua, resulta que con el agua tenemos una cosa muy similar al transistor. Se
llama grifo.
Si, uno de esos grifos que abrimos y cerramos para ducharnos o lavarnos las manos y que
cumplen básicamente dos funciones:
Permitir abrir o cortar el flujo de agua (digitalmente SI/NO).
Regular la intensidad de dicho flujo, abriendo más o menos el paso.
Pues básicamente un transistor es exactamente lo mismo pero construido de otro modo, con
diferentes materiales y un poco más rápido de accionar. Un transistor puede trabajar de dos
maneras:
Permitir o cortar el flujo de corriente.
Amplificando una señal de entrada (enseguida volvemos)
Cuando un transistor funciona de la primera manera, en modo pasa o no pasa, pero sin
medias tintas, decimos que funciona al corte (no pasa) o a saturación (pasa sin
restricciones). Y esto es el fundamento de toda tecnología digital moderna: Ordenadores,
teléfonos, consolas, relojes digitales. De hecho un transistor en corte es un 0 y en saturación
es un 1, (o TRUE / FALSE si preferís).
Los grifos, tiene tres partes: entrada, salida y control. Los transistores igual, pero se llaman
emisor, colector y base (E, C, B).
Si hacemos funcionar un grifo y vamos moviendo sin parar el control en ambas direcciones, el
flujo de salida de agua es proporcional al Angulo del mando en cada momento. Y si la tubería
fuese lo bastante enorme estaríamos amplificando enormemente nuestro movimiento manual.
Cuando hacemos eso con un transistor poniendo en la Base una señal eléctrica variable, el
flujo de corriente entre el Emisor y el Colector sigue la señal de la base pero amplificándola.
Se pueden conseguir ganancias enormes con este sistema y es la base de todos los
amplificadores electrónicos modernos.
Cada vez que escuchas música, hay un transistor (o varios) amplificando una señal débil
para que puedas escucharla.
Todos tenemos clara la idea de lo que es un conductor y un aislante.
Los semiconductores son un tipo de materiales que podemos convertir en conductores o
aislantes a voluntad, mediante una señal eléctrica. Y que presentan ciertas propiedades
interesantes bajo las condiciones adecuadas.
Los semiconductores mas típicos y mas extendidos en la fabricación electrónica son el
Silicio y el arseniuro de Galio, pero son muchos los materiales, que en mayor o menor medida,
presenta un efecto semiconductor
La mayor parte de los componentes que conocemos en electrónica, como LEDs,
transistores y diodos son semiconductores, y muchos otros menos conocidos como tiristores y
hasta láseres de estado solido
NUESTRO PRIMER CIRCUITO CON UN TRANSISTOR
Vamos a empezar con un transistor de uso general, que podemos encontrar con facilidad en
cualquier sitio: P2N2222. Todos los circuitos que incluyen un transistor se suelen parecer a
este:
Una ventaja de usar un transistor es que aísla eficazmente el circuito de control de la base de
la carga entre Emisor y Colector, haciendo casi imposible que queméis un Arduino con un
circuito como este.
El número de transistores comerciales es ilimitado (y sus precios también)
dependiendo de su función y de su capacidad para soportar diferentes tensiones, mayores
cargas, disipar más calor o hacer poco ruido electrónico.
No es buena idea buscar transistores raros y caros diseñados para tareas específicas.
Mientras no tengáis muy claro porque compráis un transistor caro, es mejor pegarse a los
modelos baratos de toda la vida.
El P2N2222 lleva muchos años en el mercado por algo. Empezad con él y ya iremos
hablando de otros.
Vamos a empezar por lo sencillo, simplemente, arrancando y apagando el motor sin variar la
velocidad.
const int control = 9 ;
void setup()
{
pinMode(control, OUTPUT) ;
void loop()
digitalWrite(control, HIGH);
delay(2000);
digitalWrite(control, LOW);
delay(1000);
Ya va siendo hora de que empecemos a coger buenas costumbres, y por eso en la primera
línea definimos una constante de tipo entero llamada control, que usaremos en lugar el 9,
cuando queramos referirnos al pin de control.
A medida que el tamaño de los programas crecen, un error en un numero de pin
puede ser muy difícil de detectar, pero en cambio dándole un nombre, no solo ayuda a que
sea más fácil de leer, sino que además, si por cualquier motivo queremos cambiar el pin de
control, basta con cambiarlo en un único sitio sin necesidad de recorrer el programa buscando
un número concreto.
void setup()
{ pinMode(control, OUTPUT) ; }
void loop()
delay(15) ;
Donde escucharemos como va aumentando la velocidad del motor hasta pararse y volver a
empezar. La razón es que al variar la tensión en la base del transistor, este limita la corriente
que lo atraviesa modificando así la velocidad del motor al que esta conectado. Sería bastante
sencillo añadir al ejemplo un potenciómetro, de modo que usemos su valor para variar la
velocidad del motor. Animo os lo dejo como ejercicio.
RESUMEN DE LA SESIÓN
o Confío en que los transistores den un poco menos de miedo ahora. La imagen
del grifo es cómoda para imaginar la operación del transistor (aunque tiene sus limitaciones).
o Hemos presentado el P2N2222 un transistor típico para aplicaciones en donde
la intensidad de la corriente a regular no pasa de medio amperio y la tensión de emisor no es
demasiado alta, hasta 40V.
o Hemos usado un circuito típico con un transistor para arrancar y parar un
motor DC. Pero que también os servirá para otras aplicaciones como manejar tiras de LED de
12V por ejemplo.
o Cuidado: Una típica tira de LEDs suele moverse sobre 18 Watios con 12V de
alimentación, o sea 18W / 12V =1,5 Amperios, más que de sobra para freír nuestro P2N2222.
Para esto hace falta otro modelo de transistor (ya hablaremos).
o Usamos las salidas PWM de Arduino para variar la velocidad de giro del motor.
MATERIAL REQUERIDO.
Una Protoboard más cables.
Un transistor 2N2222
Un pequeño Relé.
Un relé es un interruptor que podemos activar mediante una señal eléctrica. En su versión
más simple es un pequeño electro-imán que cuando lo excitamos mueve la posición de un
contacto eléctrico de conectado a desconectado o viceversa.
El símbolo del relé muestra la bobina y en este caso, un accionador que conmuta entre dos
contactos, pero también existen relés de múltiples contactos. Mediante una señal de control
de poca intensidad que excite la bobina podemos conmutar grandes tensiones o intensidades.
Hemos visto cómo usar un transistor para hacer lo mismo, ¿Porque entonces usar relés?
En primer lugar, los relés llevan entre nosotros desde finales del siglo 19 (y nadie ha
sido todavía capaz de convertirlos en obsoletos), es una tecnología muy probada y bien
establecida en la industria y además sirve para cosas que son problemáticas para los
transistores.
Hay límites en la corriente que un transistor puede aceptar, pero un relé se puede
diseñar para que aguante cualquier carga, porque basta con los extremos metálicos de los
contactos lo soporten.
Aísla completamente el circuito de control del de potencia, lo que tiene su importancia
especialmente en líneas de media y alta tensión.
En la práctica, con Arduino es más sencillo utilizar un relé para encender una luz fluorescente
o la calefacción, que buscar un transistor de características adecuadas.
Aunque hay relés que necesitan muy poca potencia para excitar la bobina, por regla general
Arduino se quedará corto y vamos a tener que usar un transistor que nos resuelva la papeleta.
El ejemplo que veremos a continuación incluye un circuito de transistor / relé completo que
nos permitirá atacar cualquier proyecto casero que nos propongamos.
Por una vez, y sin que sirva de precedente, no vamos a incluir un esquema de Protoboard
para el circuito porque el pinout (patillaje) de un relé depende del fabricante y del modelo y
sería poco práctico establecer múltiples diagramas de protoboard.
Además, el circuito básico del transistor de la sesión anterior es exactamente el mismo que
este y bastaría reemplazar el motor por los contactos de control del relé por una parte, y por la
otra buscar en el relé cual son los pines de normalmente abierto y normalmente cerrado, y
conectar un LED a cada uno con una resistencia común.
El diagrama de la protoboard no haría sino complicar las cosas y ya va siendo hora de que
tratemos de montar el circuito a partir del esquema electrónico directamente. Animo
Para probar que todo está correctamente conectado, bastaría con correr el mismo programa
que para el motor:
void setup()
pinMode(control, OUTPUT) ;
void loop()
digitalWrite(control, HIGH);
delay(1000);
digitalWrite(control, LOW);
delay(1000);
Este programa causará que el relé conmute cada segundo y los LEDs se encenderán
alternativamente.
Para convertir este circuito en unas luces de emergencia bastaría con poner una batería o
pilas en el común del relé en lugar de los 5V de Arduino. De ese modo al desconectar Arduino
la luz de emergencia se activaría sola.
RESUMEN DE LA SESIÓN
MATERIAL REQUERIDO.
Una Protoboard más cables.
Vamos a ver como los usamos. Lo primero, tenéis que buscar la hoja de especificaciones del
fabricante. (Busca TMP36 o LM35DZ data sheet)Aunque al principio no es fácil
acostumbrarse a leer este tipo de documentación (y que encima vienen en inglés), en último
término es a donde hay que ir cuando queráis saber exactamente las características de su
funcionamiento.
Pero el resumen podría ser más o menos así:
SI conectáis la tensión al revés, Tendréis tiempo de reaccionar y cambiarla, pero
mucho cuidado porque se calentará más que suficiente para haceros una quemadura
dolorosa.
Si veis que está caliente no tratéis de sacarlo con los dedos, simplemente
desconectad el Arduino y dadle un rato para que se enfríe.
ESQUEMA ELECTRÓNICO DEL CIRCUITO.
.
Vamos a montar un pequeño circuito que lea la temperatura de un sensor, imprima el valor en
la consola y encienda un diodo cuando esta sobrepase un cierto umbral. Aquí tenemos el
esquema electrónico:
Y el
circuito para protoboard sería muy sencillo también:
CALCULANDO LA TEMPERATURA.
.
El fabricante del TMP36 y del LM35DZ nos dice que la salida de tensión será de 10 mV (mili
voltios) por cada grado de temperatura y además sabemos que nuestro Arduino mide en las
puertas analógicas una máximo de 1.024 para 5V (y 0 para 0V), por tanto, para una lectura
dada, el valor en voltios de la medida será:
Y como cada voltio equivale a 100ºC ( 1V / 10mv = 100), la temperatura en grados Celsius es
resultado de multiplicar esto por 100.
Pero para que la cosa no sea tan fácil el fabricante del TMP36 nos dice que el 0V no es 0º
sino -50º (y así poder leer valores bajo cero), así que al total hay que restarle 50. En cambio
com el LM35DZ empieza en 0º, no hay que restarle nada, es más cómodo de manejar.
Resumiendo. Para calcular la temperatura en ºC a partir de la lectura de Arduino:
Ya sé que la tendencia natural es usar int para todo, pero normalmente en cuanto
haya una división de por medio os conviene usar un float hasta que tengáis muy claro porque
usáis otro tipo.
Cada vez que tomemos una muestra de nuestra entrada analógica vamos a calcular la
temperatura y si sobrepasa el umbral definido, damos orden de activar la alarma, en caso
contrario la apagamos.
Como la temperatura es algo que varía lentamente usaremos un delay para que solo nos dé
una medida por segundo.
EL PROGRAMA DE CONTROL.
int Sensor = 0 ;
int umbral = 25 ;
Sensor es el pin analógico (A0) al que conectamos el sensor de temperatura y umbral el valor
a partir del cual disparamos la alarma de temperatura.
El valor de umbral habrá que ajustarlo en función de la temperatura ambiente donde
estéis montando el circuito.
En principio 25ºC son buenos para que si queréis subir la temperatura, lo podáis hacer
simplemente soplando o manteniendo entre los dedos el sensor. Otro sistema de subir la
temperatura rápido, es poner el sensor al lado de la salida de refrigeración de un portátil.
Y pasando los cálculos de la página anterior a C++ nos quedaría algo parecido a:
void loop ()
int umbral = 25 ;
void setup()
Serial.begin(9600);
pinMode(11,OUTPUT);
void loop()
Fijando un umbral superior y otro inferior podemos escribir un programa que arranque o pare
el ventilador cuando la temperatura se sale de los márgenes fijados. Habríais conseguido, de
un modo muy sencillo, una regulación de temperatura con un sensor y un ventilador.
RESUMEN DE LA SESIÓN
Hemos visto lo que son y cómo funciona un sensor de temperatura básico como el TMP35.
Hay muchos por ahí, pero suelen ser parecidos a este.
Hemos aprovechado para meter el tipo float y ver como forzar las operaciones con ellos y
lo que es más importante prevenir posibles malas pasadas con ellos.
MATERIAL REQUERIDO.
Arduino Uno o similar.
Esta sesión acepta cualquier otro modelo de Ard
Una Protoboard mas cables.
Un Joystick.
QUE ES UN JOYSTICK
Un joystick suele estar formado por dos potenciómetros a 90º que transforman el movimiento
en X e Y del mando en una señal eléctrica proporcional a su posición y que además suele
incluir un botón.
Vamos a montar un circuito con un servo como en la sesión previa y usaremos uno de los ejes
del joystick para posicionar un servo, y si pulsamos el botón encendemos un LED.
(Ignoraremos el otro eje Y, aunque podríamos usarlo para posicionar un segundo servo).
El pin correspondiente al botón suele venir marcado como SW de Switch.
EL PROGRAMA DE CONTROL
int angulo = 0 ;
int Eje_X = A1 ;
int Eje_Y = A2 ;
void setup()
{
void loop()
servo1.write(angulo);
if ( ! digitalRead(boton))
digitalWrite(LED, HIGH);
else
digitalWrite(LED, LOW);
delay(250) ;
La parte que corresponde al servo es exactamente lo mismo que en la sesión 16. Por
lo demás hemos incluido unas definiciones al principio indicando que hemos conectado el eje
X a la entrada analógica A1 y el eje Y a la entrada A2.
El pulsador está conectado al pin digital 4 y el LED al 12, de forma que si queremos
cambiar de pines, por la razón que sea, bastara con actualizar esta lista al principio del
programa.
Insistir en que hemos definido la entrada correspondiente al boto del joystick como
INPUT_PULLUP y no como INPUT, porque de este modo no necesitamos incluir una
resistencia, sino que Arduino conecta un pullup internamente
Por eso leeremos LOW al pulsarlo y entre tanto será HIGH, por ese motivo invertimos la
condición en el if. Encenderemos el botón solo cuando pulsemos.
El servo sigue la posicion del joystick y cuando soltamos vuelve al centro.
Pero hay algo de epiléptico en el movimiento del servo (y más con uno barato como éste que
estoy usando). Recibe muchas interferencias y el movimiento es muy convulso, porque, aún si
el servo tuviera una cierta precisión (lo que es una suposición entre optimista y muy optimista)
los potenciómetros y los convertidores analógicos a digital siempre tienen un margen de ruido.
Seguro que se nos puede ocurrir una forma de mejorar ese movimiento. Pensadlo, ¿Que
podemos hacer para filtrar ese ruido?
Vale. En el mundo real, las cosas no son nunca blancas o negras, sino más bien en tonos
grises (o muy frecuentemente chocolates), por eso no es buena idea enviar las lecturas
directamente al control el servo, o de lo que sea que estamos moviendo.
Hay que filtrar un poco la señal. Sin entrar mucho en este tema (sobre el que hay
enciclopedias),vamos a usar una técnica muy básica, pero muy eficaz en muchas ocasiones y
que os conviene conocer.
Vamos a leer el potenciómetro para decidir si subimos o bajamos el valor del Angulo. No para
calcular Angulo directamente.
Como el potenciómetro nos da valores entre 0 y 10000, cuando está centrado o suelto, leerá
sobre 500, poco más o menos (aunque bailará). Así que le vamos a dar un margen de
tolerancia. Solo aumentaremos el Angulo, un valor dado, si la lectura del potenciómetro
sobrepasa el valor de 600 y lo disminuiremos cuando baje de 400.
De este modo pequeñas oscilaciones alrededor del punto medio, no nos afectarán. Es decir
las hemos filtrado. Esto reflejado en el programa, podría ser algo así: Prog_17_2
#include <Servo.h> // Incluir la librería Servo
int Eje_X = A1 ;
int Eje_Y = A2 ;
void setup()
{
servo1.attach(6) ; // Conectar servo1 al pin 6
}
void loop()
Creo que comprobareis que el movimiento es más fluido y uniforme, y que prácticamente
elimina las convulsiones del servo. Además usamos este método para dejar clavado al servo
en la posición que nos interesa (Aunque soltemos el mando), algo que del otro modo sería
imposible.
RESUMEN DE LA SESIÓN
o Hemos visto que un joystick son dos potenciómetros a 90º mas un pulsador,
ambos normales y corrientes.
o Aprovechamos el montaje anterior para mover el servo con el joystick y
mapear su valor entre 0 y 180º
o Hemos introducido el concepto de filtrado de señales, que de por si es una
rama específica de la electrónica y la computación, y una, que cada día es mas importante en
todos los ámbitos de la tecnología.
MATERIAL REQUERIDO.
Una Protoboard mas cables.
Sensor de distancia HC-SR04
Hemos visto, en los documentales, que los murciélagos son capaces de volar en completa
oscuridad y sin embargo, sortear obstáculos o atrapar insectos en vuelo. Sabemos que lo
hacen, pero rara vez pensamos como.
Tenemos una vaga idea de que se llama ecolocalización y que más o menos tiene que ver
con unos sonidos agudos que emiten y que después recogen con esas enormes orejas que
Dios les ha dado, pero rara vez nos planteamos cómo es esto posible.
Delfines y ballenas utilizan un sistema similar para atrapar a sus presas, y hasta hemos visto
que, en cualquier película de submarinos, en el momento álgido el capitán ordena emitir un
pulso único de sonar para localizar al enemigo.
El radar funciona de modo similar aunque usando ondas de radio frecuencia muy
cortasy con una problemática propia descomunal. Un pulso de radiofrecuencia se emite desde
la antena y se recoge el eco que vuelve a la velocidad de la luz.
Lo que haremos en esta sesión es utilizar un sensor de distancia sencillo HC-SR04 (y muy
parecido a los sensores de aparcamiento de los coches modernos), que nos permite enviar
estos pulsos ultrasónicos y escuchar el eco de retorno. Midiendo este tiempo, podemos
calcular la distancia hasta el obstáculo.
El oído humano no percibe sonidos por encima de 20kHz. Por eso, a las ondas de
mayor frecuencia las llamamos ultrasonidos ( mas allá del sonido). Los sensores de
ultrasonidos funcionan sobre los 40 kHz.
No son perfectos, les influye la temperatura ambiente, la humedad y los materiales en
los que reflejan, lo que genera una cierta incertidumbre. Pero a cambio son baratos y efectivos
hasta un poco más de 3 metros en condiciones normales si la precisión no es un problema
determinante
DIAGRAMA DE CONEXIÓN
Veamos como conectar uno de esto detectores a nuestros Duinos. Aquí está el esquema
eléctrico y de protoboard por cortesía de Fritzing:
Y de nuevo, el diagrama de conexión de la protoboard
EL PROGRAMA DE CONTROL
#define trigPin 13
#define echoPin 12
#define led 2
Hasta ahora habíamos visto que podíamos definir una variable como int, por ejemplo, y
también como una constante (const int pin). Aquí utilizamos otro método, el #define que es
una directiva para el compilador.
Esto solo significa que el compilador (en rigor el pre procesador) cambiará todas las
ocurrencias de estos #define en nuestro programa por su valor antes de compilar. Esta es la
forma clásica de C de hacer esto y tiene la virtud de que no ocupa memoria definiendo una
variable (y con un Arduino UNO, que va muy corto de memoria, esto puede ser crítico en
ocasiones).
void setup()
{
Serial.begin (9600);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
pinMode(led, OUTPUT);
Ya estamos más que habituados a la función delay(milis), pero el reloj interno de Arduino mide
en microsegundos y tenemos otra función parecida delayMicroseconds(µs) que simplemente
congela Arduino el número especificado de microsegundos.
Para dar un pulso ultrasónico lo que hacemos es activar el pin Trigger durante unos
microsegundos y para ello lo ponemos en HIGH, antes de escuchar el eco:
Para escuchar el pulso vamos a usar otra función, pulseIn() ( Oh sí, hay muchas,
muchísimas). Para leer el manual de pulseIn() buscad en google Arduino pulseIn y vereis que
pronto lo encontrais.
Básicamente lo que hace es escuchar el pin que le pasamos, buscando una señal que pase
de LOW a HIGH ( si le pasamos HIGH como parámetro) y cuenta el tiempo que tarda en
volver a bajar desde que sube.
Ahora ya sabemos el tiempo que tarda en volver el eco en µs. Como la velocidad del sonido
es de 343 metros / segundo, Necesitamos 1/343 = 0,00291 segundos para recorrer un metro.
Para usar una medida más cómoda podemos pasar esto a microsegundos por centímetro:
Como nuestro eco mide el tiempo que tarda el pulso en ir y venir la distancia recorrida será la
mitad:
Así que el programa queda parecido a esto ( Prog_18_1) : Descargar ejemplo 18_1:
#define trigPin 13
#define echoPin 12
#define led 2
void setup()
{ Serial.begin (9600);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
pinMode(led, OUTPUT);
void loop()
else
Para convertir esto en un detector de movimiento hemos creado una variable un poco menor
de la medida que el sensor recibe en vacio (en mi caso unos 200 cm). Si la distancia medida
cae por debajo este valor es que algo se ha interpuesto y por tanto encendemos una alarma,
en nuestro caso un humilde LED.
Después de este ejercicio de física y matemáticas, que sin duda causará furor entre los
estudiantes aplicados, vamos a hacer el mismo programa pero usando una librería externa,
que alguien se ha molestado en escribir, paras esas pocas personas que no disfrutan de los
problemas de ciencias y que así, podamos ver la diferencia.
Ya está. Arduino ha importado la librería y los ejemplos que incluye. Si ahora volvéis a
Programa\ImportarLibrería, veréis que al final de la lista ya está disponible como NewPing, y
además el zip incluye varios ejemplos de uso. Vamos a cargar el equivalente del programa
anterior. Haced :
Arduino cargara un programa de ejemplo. Las instrucciones claves son primero inicializar la
librería con:
#include <NewPing.h>
#define TRIGGER_PIN 12 // Arduino pin tied to trigger pin on the ultrasonic
sensor.
#define ECHO_PIN 11 // Arduino pin tied to echo pin on the ultrasonic sensor.
void setup()
void loop()
delay(50);
Serial.print("Ping: ");
Serial.print(uS / US_ROUNDTRIP_CM);
Serial.println("cm");
}
Como veis la librería se encarga de inicializar los pines necesarios, enviar los pulsos, escuchar
el eco de retorno y de hacer los cálculos. No está mal.
Fijaros, que el ejemplo, utiliza diferentes pines a los que nosotros hemos usado,así
que tendreís que modificarlos. Igualmente, el ejemplo inicializa la puerta serie a 115.200. Es
imprescindible igualar esta velocidad con la que recibe la consola o veréis muchas cosas raras
en pantalla.
Los alumnos avispados se habrán dado cuenta de que Arduino viene forrado de
ejemplos que pueden cargar y usar. Os invito a que investiguéis y juguéis con estos ejemplos
cuanto queráis.
RESUMEN DE LA SESIÓN
o Hemos conocido los sensores ultrasónicos de distancia.
Sabemos que sirven para medir algo menos de 3 0 4 metros
Sabemos que no son perfectos pero son útiles y baratos.
o Más sobre medir el tiempo: delayMicroseconds ().
o Más funciones disponibles: PulseIn ().
o Importando una librería externa, de nuevo,algo que en las próximas sesiones
iremos haciendo cada vez más, a medida que los elementos a usar se vayan sofisticando.
MATERIAL REQUERIDO.
Un teclado no es más que una colección de botones, a cada uno de los cuales le asignamos
un símbolo o una función determinada. Pero botones al fin y al cabo.
Leer botones es algo que ya no tiene secretos para nosotros, pero si conectáramos cada tecla
a un pin digital de nuestro Arduino, pronto estaríamos en apuros.
El teclado de nuestro ordenador suele ser de alrededor de 106 teclas, así que el método de
fuerza bruta va a entrar en apuros rápidamente. Necesitamos otra solución.
Y como el mundo está lleno de gente ingeniosa se les ocurrió una solución de lo más
elegante, una matriz de teclas.
Vamos a ver un ejemplo con un pequeño teclado numérico de 16 teclas tipo los de los
teléfonos móviles o los de los cajeros automáticos.
Para que nuestro Arduino pueda saber que tecla se pulsa, basta con poner tensión en las filas
de forma secuencial y luego leer las columnas para ver cuál de ellas tiene HIGH.Los teclados
matriciales usan una combinación de filas y columnas para conocer el estado de los botones.
Cada tecla es un pulsador conectado a una fila y a una columna. Cuando se pulsa una de las
teclas, se cierra una conexión única entre una fila y una columna.
Por ejemplo, ponemos HIGH en la primera fila (hilo 8 en el diagrama de la derecha) y después
leemos sucesivamente los hilos correspondientes a las columnas (hilos 4, 3, 2,1). Si ninguno
está en HIGH es que no se ha pulsado ninguna tecla de la primera fila.
Pasamos a la segunda fila (hilo 7) y ponemos HIGH, si al leer los hilos 4, 3, 2,1 encontramos
que el hilo 1 está en HIGH, es que se ha pulsado la tecla correspondiente a la “B”.
De este modo, para leer un teclado matricial de 4×4 necesitamos 8 hilos en lugar de 16,
aunque nos dará un poco más de guerra a la hora de programar. Para un teclado de PC 106
teclas bastaría una matriz de 10×11 o sea 21 hilos en vez de 106.
DIAGRAMA DE CONEXIÓN
Un apunte rápido: Por comodidad en el diagrama, y porque en los ejemplos que
vienen con Arduino utilizan estos mismos números de pines del 0 al 7, los hemos mantenido.
Pero conviene saber que Arduino usa los pines 0 y 1 para comunicarse a través del USB, por
lo que no es buena usarlos para otra cosa cuando vayamos a usar el puerto serie.
EL PROGRAMA DE LECTURA
No es un programa complicado, pero antes de que el pánico se apodere de los que siguen la
sesión con creciente temor (y recuerden que tienen algo urgente e ineludible que hacer)
comentaremos que alguien ha tenido este problema antes que nosotros y amablemente ha
puesto a nuestra disposición una librería llamada KeyPad ideal para leer este tipo de matrices
sin complicaciones,.
Lo que no quita para que podáis escribir un programa que lea directamente la matriz y
reconozcáis las pulsaciones. Animo, no es difícil y el ejercicio siempre es bueno.
Lo primero es descargar la librería e instalarla. La tenéis aquí Descargar, o bien en la página
de Arduino
http://playground.arduino.cc/Code/Keypad#Download
Una vez instalada para usarla basta con que le indiquéis que deseáis usarla. SI hacéis
Programa\Importar Librería \ Keypad veréis que incluye la siguiente línea en vuestro
programa:
#include <Keypad.h>
Vamos a ver como usamos esta librería. Lo primero es definir un par de contantes
Y ahora una par de arrays para indicarle a la librería que pines de Arduino corresponden a las
filas y cuales a las columnas del keypad:
Recordad que un array es una colección de elementos que pasamos entre llaves y separados
por comas. Es más cómodo que andar definiendo 8 variables.
Recordad lo que vimos en la sesión 9, que cuando definimos una array por enumeración de
sus elementos, como en este caso, no necesitamos pasar entre corchetes el número de
elementos (Los cuenta el mismo C++ solito).
Tendremos que definirle, además, que símbolos corresponden a cada posición de las teclas.
Una nueva oportunidad para disfrutar de los arrays, pero esta vez de 2 dimensiones en vez
de una.
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
Definimos el array de 4×4 pasándole las dimensiones entre corchetes. Sus miembros
van a ser 4 filas, cada una de las cuales es un array de 4 elementos, y después enumeramos
los cuatro arrays de 4 miembros.
Fijaros en que cada array va entre llaves. Los elementos de un array se separan por
comas, pero al final de las llaves exteriores va un punto y coma porque toda el conjunto es
una asignación.
Los arrays siempre parecen complicados hasta que te acostumbras pero no es para
tanto y este es un buen ejemplo para que comprendéis la idea de un array de 2 dimensiones.
Una de las curiosidades de leer una matriz de teclas y después asignar un carácter contenido
en un array es que podemos redefinir el valor de las teclas sin más que cambiar el contenido
del array.
Si por lo que sea hubiéramos conectado mal los pines del keypad al Arduino,
podríamos redefinir los valores del array para que coincidan con lo que pone en las teclas.
Puede ser más fácil que mover de sitio los cables.
De hecho es esa capacidad de mapear la representación de un carácter a una
posición en una tabla, es lo que permite que podamos reconfigurar el mismo teclado a
diferentes lenguas, sin más que cambiar los valores del array (aunque también ayudará
mucho luego cambiar los símbolos rotulados en el teclado).
Por ultimo tendremos que crear una instancia de Keypad que llamaremos Teclado1
Que leído así asusta, pero que en realidad es una tontería. Traduciendo sería: Crea una
instancia del tipo Keypad que llamaras Teclado1 y al que asignarás las teclas que tenemos en
el array Teclas, y le decimos que hemos conectado las filas del keypad a los números de
pines que te indico en el array Pins_Filas y las columnas al array Pins_Cols.
MakeKeymap (Teclas) es una función disponible en la librería y que nos permite
asignar un array de valores a una matriz. Se hace solo una vez al principio salvo que por lo
que sea nos interese cambiarlo sobre la marcha.
Por ultimo para leer el keypad hacemos una llamada a otra función de la librería:
char pulsacion = Teclado1.getKey() ;
Por ultimo enviamos a la consola el carácter pulsado. Aquí tenéis el programa en versión
completa:Descargar
#include <Keypad.h> // Prog_19_1
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
void setup()
{ Serial.begin(9600) ; }
void loop()
if (pulsación != 0)
RESUMEN DE LA SESIÓN
MATERIAL REQUERIDO.
Arduino Uno o similar.
Una Protoboard más Cables .
PIEZOELECTRICIDAD
Este fenómeno también ocurre a la inversa: se deforman bajo la acción de fuerzas internas al
ser sometidos a un campo eléctrico. El efecto piezoeléctrico es normalmente reversible: al
dejar de someter los cristales a un voltaje exterior o campo eléctrico, recuperan su forma.
Es decir, que son materiales (el cuarzo es el más conocido) que si los sometemos a una
tensión eléctrica variable (como una señal PWM, que ya nos son familiares) vibran.
Es un fenómeno bastante conocido y muchos encendedores domésticos de gas
funcionan bajo este principio. Un resorte golpea un cuarzo y como resultado tenemos la
chispa que enciende el gas o el calentador de agua con un característico click).
En otro orden de cosas, los circuitos electrónicos digitales, suelen disponer de un reloj
interno que vibra a una velocidad patrón, basados en cristales de cuarzo piezoeléctrico. El
cristal de Arduino late a 16Mhz por segundo y la flecha indica su posición.
En una sesión próxima utilizaremos uno de estos cristales para sincronizar un circuito
discreto con el corazón de un Arduino, el ATmega 328.
Si conectamos un piezo con una señal digital, vibran a una frecuencia sigue bastante
fielmente la variación eléctrica con que los excita, y si vibran a la frecuencia audible, oiremos
el sonido que producen. A un componente que hace esto, le llamamos Buzzer o zumbador.
Naturalmente, la calidad del sonido que producen dista bastante de lo que podríamos
denominar alta fidelidad. Pero es suficiente para generar tonos audibles (como la típica alarma
de los despertadores digitales) e incluso tonos musicales reconocibles que podemos
secuenciar, hasta en piezas musicales (por más que uno quisiera estar en otro lugar cuando
las oyes).
Como antes o después, disponer de una señal acústica en vuestros proyectos, acaba siendo
útil, vamos a ver cómo podemos montar estos elementos, y que tipo de opciones tenemos
disponibles.
ESQUEMA ELECTRÓNICO
xx
La conexión es tan simple como conectar negativo a GND y positivo al pin 9. De todas
maneras hay que tener cuidado. Los piezos tienen polaridad y hay que asegurarse de
conectarlos correctamente.
Si los conectáis al revés, simplemente no sonará, y tendréis que dar la vuelta.
Además para este primer montaje, necesitamos usar un pin PWM (como el 9) porque
es la alternancia entre HIGH y LOW lo que produce el efecto piezoeléctrico (recordad que una
señal PWM envía un tren de ondas cuadradas de amplitud variable), por lo que lo más
cómodo es usar un pin PWM en lugar de programar un efecto equivalente en un pin normal.
EL PROGRAMA
Vamos a empezar creando una función, Beep(), que haga ruido simplemente:
analogWrite(9, 20);
Y ahora prueba
void setup()
pinMode(9, OUTPUT);
beep(50);
beep(50);
beep(50);
delay(1000);
void loop()
{ beep(200);
Lo único que beep () hace es poner una señal PWM en el pin 9 de 20 sobre 255. Podéis
varias el valor, pero el tono de audio no cambiará gran cosa porque está controlado por la
señal de base. Esto es suficiente para generar una de las molestas señales acústicas de un
despertador barato.
¿Qué pasa si quiero generar señales de tono variable para hacer una melodía? Bueno pues
Arduino dispone de la función tone() que genera una señal de la frecuencia indicada, y
notone() que la corta:
void setup()
int pinOut = 8;
Podemos definir una matriz con las frecuencias de las notas a utilizar y lee las
correspondencias con las notas musicales. Veamos cómo generar una escala con un
zumbador:
int speakerPin = 9;
int tones[ ] = {261, 277, 294, 311, 330, 349, 370, 392, 415, 440,466, 494};
// mid C C# D D# E F F# G G# A
void setup()
{ }
void loop()
tone(speakerPin, tones[i]);
delay(500);
noTone(speakerPin);
}
Dada la afición reinante a la física recreativa, me aterra la idea de ponerme a perorar
sobre la relación entre las notas musicales y la frecuencia correspondiente, así como los
distintos métodos de calcularlas y no digamos ya pretender hablar de las escalas temperadas.
Así que vamos a mencionar simplemente que existe una relación directa entre la
frecuencia de una nota y su posición, en digamos el teclado de un piano.
Para aquellos con formación musical e interés en como calcular la frecuencia de una
nota dada podeís hacer una búsqueda en Google, que enseguida encontrareis de todo.
Por último, y para cerrar esta sesión, os diré que internet está lleno de gente a quien le sobra
tiempo y lo dedica a las extravagancias más inverosímiles. Basta con indicaros aquí la página
de alguien que ha dedicado un número indecente de horas en transcribir la música de la
guerra de las galaxias a orquesta de cámara, compuesta por Arduino y zumbador.Descargar
ejemplo:
RESUMEN DE LA SESIÓN
MATERIAL REQUERIDO.
Una Protoboard.
Cables de Protoboard.
LOS FOTOSENSORES
Se les suele utilizar como sensores de luz, para arrancar luces automáticamente cuando la
oscuridad sobrepasa un cierto umbral, o como detectores de movimiento próximo ( Cuando
algo se interpone).
Vamos a utilizar en esta sesión un típico LDR, que es bastante fácil de conseguir y es sensible
a los cambios de luz ambiente. Montaremos un circuito con un LDR y el zumbador que vimos
en la última sesión, para construir un theremin rudimentario, que espero que os haga pasar un
rato entretenido.
El circuito utiliza un LDR como señal de control y calcularemos una frecuencia en función de la
caída de tensión que leamos en nuestra fotorresistencia.
Recordad que los convertidores ADC como los de Arduino no pueden leer resistencia
sino tensión.
Los LDR no son precisamente rápidos en reaccionar a la luz, y puedan tardar hasta
algunas décimas de segundo en reaccionar. Esto no es importante para una alarma de luz,
pero hace imposible que se puedan utilizar para enviar información mediante la luz.
Pero antes de entrar en materia necesitamos hacer un inciso para conocer lo que es
un divisor de tensión.
DIVISORES DE TENSIÓN
V=R*I
EN SERIE EN P
Cuando ponemos las dos resistencias en serie, la resistencia resultante es la suma de ambas:
Si todavía queda alguien despierto, se habrá dado cuenta que si R1 y R2 son iguales Vout será
exactamente la mitad de Vin pero si R1 o R2, fuese un potenciómetro (o un LDR) cualquier
variación en el ajuste, causaría una modificación en el valor de salida de tensión Vout.
Esto es lo que se conoce como un divisor de tensión y es un circuito de lo más práctico para
rebajar una señal de entrada, y podéis apostar a que lo usareis mas de una vez.
Por ejemplo, los convertidores analógicos de Arduino aceptan un máximo de 5V, pero muchas
señales industriales son de entre 0 y 12V.Si lo conectas sin más al A0, por ejemplo, freirás el
chip de largo.
Pero con el truco del divisor de tensión y calculando adecuadamente las resistencias (Que sí,
que tú puedes con lo que hay en la página anterior) puedes adaptarlo tranquilamente para que
tu Arduino viva feliz con una señal que originalmente le hubiera chamuscado.
Los divisores de tensión son un circuito muy sencillo y que conviene que sea parte de
vuestro arsenal electrónico. Resuelven cantidad de problemas con una resistencia y un
potenciómetro y son ideales para tratar señales, que por exceso de tensión, quedarían fuera
del alcance de tu Arduino.
El programa es muy sencillo. Leemos la caída de tensión en A0 y lo usamos para mapear una
frecuencia entre 20 y 5.000 Hz para llamar a la función tone() y eso es todo.
void setup()
void loop()
Para probar el circuito os recomiendo que pongáis un foco potente a 50 cm por encima
del LDR y probéis a mover la mano por delante y especialmente de arriba abajo y viceversa.
Escuchareis un tono continuo más o menos agudo, que ira variando su frecuencia en
función de la luz que incida en el LDR.
Se acepta que el oído humano se mueve ente 20 Hz y 20Khz (aunque esto es para
algún adolescente de oído muy fino) para los que peinan canas entre 40Hz y 10Khz ya es un
rango optimista. Por eso, mapeamos los valores del potenciómetro, que van de 0 a 1024,
entre 20 y 5Khz, pero os recomiendo que cambiéis estos valores y veáis lo que pasa.
Creo que convendréis conmigo, en que ha sido tan fácil, que no puedo dejaros marchar, sin
poneros antes algún otro problema. Veamos.
El sonido que obtenemos es de espectro continuo, es decir, que reproduce frecuencias continuas en
el margen que es capaz. Pero nuestro oído está acostumbrado a escuchar las notas en tonos y
semitonos de frecuencia dada, como veíamos en el programa de las sesión 20. ¿Cómo haríais para
conseguir que el resultado de este theremin, produjera, las notas que definimos allí?
int tono[ ] = {261, 277, 294, 311, 330, 349, 370, 392, 415, 440,466, 494};
// mid C C# D D# E F F# G G# A
void setup()
}
void loop()
int p = analogRead(A0) ;
int n = map (p, 500,1024, 0, 12) ; // Ell array solo tiene 12 notas
tone(pinBuzzer, tono[n]);
delay(300);
He usado un array con las frecuencias temperadas de una octava. Después, mapeamos las lectura de
la puerta A0 a un entero entre 0 y 12, porque el array e arriba solo tiene 12 notas, y usamos su valor
para leer la frecuencia correspondiente.
En mi caso, además, A0 solo daba valores entre 500 y 1024, así que por eso he corregido la escala.
Por último el delay impide que el cambio de notas sea instantáneo, porque de lo contrario no
notaríamos mucho cambio el programa anterior.
RESUMEN DE LA SESIÓN
MATERIAL REQUERIDO.
Arduino UNO o equivalente.
Una Protoboard más cables.
Ya sabemos que Arduino UNO dispone de 14 pines digitales que podríamos usar para entrada
y salida y que además disponemos de 6 puertas de entrada analógicas de A0 hasta A5. Lo
que no suele ser muy conocido es que las puertas analógicas también se pueden usar como
pines digitales en caso necesario. Podemos leer y escribir valores digitales exactamente como
lo hacemos con los pines digitales normales:
digitalWrite(A1, HIGH) ;
Nuestras puertas analógicas se comportarán gentilmente como puertas digitales (Si, también
podemos hacer lo mismo para la lectura). Os animo a que hagáis el blinking LED con una
puerta analógica.
Aqui teneis que hace el parpadeo de un LED con la puerta A1 Blinking LED y el
Prog_20_2 que lee en digital el pin A1 Lectura digital
Así pues, en realidad, disponemos de 20 puertas digitales para nuestras cosas. Pero en la
vida de toda persona, siempre hay un momento en que esto nos suficiente.
Podemos pasarnos a un Arduino Mega que con 54 pines digitales más 16 puertas analógicas
hacen el impresionante numero de 60 puertas disponibles. Pero al final, la combinación entre
la ley de Murphy y la segunda ley de la termodinámica (La de que todo tiende al caos)
garantizan que aparezca alguien que quiere un cuadro de luces con 64 LEDs (o 128 ya
puestos) y la catástrofe nos acecha porque no hay Arduinos con más pines.
Afortunadamente la industria electrónica nos provee con una forma sencilla de aumentar el
número de salidas digitales de nuestros Arduinos sin demasiada complicación. Unos
pequeños chips llamados Shift Registers fáciles de encontrar como el 74HC595.
Si lo que necesitas es aumentar los pines digitales de entrada, prueba a usar
74HC165 que es un shift register de entrada
Si lo que necesitas es aumentar los pines analógicos puedes usar un multiplexor /
demultiplexor como el 74HC4051.
Aunque ahora todo parezca un poco confuso (no me extraña) son bastante sencillos de
manejar una vez que entiendes lo que hacen y son sorprendentemente útiles en cantidad de
situaciones.
En la Sesión 7 vimos que para comunicar dos puntos con una conexión serie necesitábamos
pactar una velocidad de envío para saber cuándo hay que leer los datos que llegan.
Cuando los 8 bits se han leído en el registro un tercer pin (Latch pin) escribe estos bits en los
pines de salida del chip y los mantiene hasta que se reciban nuevos datos.
Fíjate en el gráfico
superior. Nuestro Arduino envía la señal de Clock Pin de modo regular para indicar cuando
hay que leer (Se lee en el flanco de subida, cuando Clock sube de 0 a 1).
Cada uno de estos valores se van pasando en orden a los pines de salida de Q0 hasta Q7,
pero aún no se activan. Cuando el Latch Pin se activa (también por flanco de subida) los
valores pasan a los pines de salida y se memorizan.
Vamos a montar un circuito que use estas salidas para gobernar un conjunto de 8 LEDs,
usando solo 3 pines de Arduino que corresponden a Clock, Data y Latch.
Y aquí
tenemos el esquema de protoboard.
Y despues el setup
void setup()
Las comunicaciones síncronas son tan frecuentes, que nuestro Arduino las soporta de fábrica
con una serie de funciones. La que nos interesa, se llama shiftOut(). (Podeis Goglear el
manual) Para iniciar la comunicación síncrona basta poner el Latch en LOW
DigitalWrite(latchPin, LOW);
Y ahora enviar los 8 bits llamando a shiftOut() y pasándole que pines usamos para Data y
Clock además indicándole el valor que queremos enviar a la salida:
Lo de MSBFIRST significa Most Significant Bit First, o sea, enviar el número binario
empezando por la izquierda. También podría ser al revés como LSBFIRST (Least Significant
Bit First) . Solo es una forma de que los informáticos se hagan los interesantes y en este
caso nos da absolutamente lo mismo.
Y por último fijar los valores en la salida poniendo HIGH el Latch de nuevo.
void loop()
{
digitalWrite(latchPin, LOW) ; // Latch a LOW para que no varíe la salida
delay(500);
Veremos que los LEDs irán contando mostrando los valores de Num en binario. Aquí tenéis
otro ejemplo con este mismo circuito en el que leemos la puerta serie para recibir un número
entre 0 y 9 y lo pasamos a los pines LEDs de salida: https://www.youtube.com/watch?v=7lk-
gNXV1Y0
Una curiosidad del 74HC595, es que si le metemos un tren de más bits de los 8 que puede
almacenar, sencillamente los va empujando hacia la salida por su pin 9 (que os habréis fijado
no hemos utilizado en el ejemplo anterior) y los va dejando caer al olvido.
Hemos conseguido una ampliación de otras 8 salidas digitales sin utilizar pines adicionales de
nuestro Arduino. Y naturalmente una vez visto el truco, no hay límite en principio al número
de chips que podemos concatenar.
Si ese que quería un cuadro de luces de 128 LEDS vuelve, lo podríamos arreglar con 128/8 =
16 chips. O si preferís, también existen estos chips en 16 bits: SN74LS674, 74F675A que son
16-Bit Serial-In, Serial/Parallel-Out Shift Register, y lo arreglaríamos con solo 8 chips.
Y ya que estamos ¿Existen Shift registers de 32 o de 64 bits? Pues sí.
Si buscáis en internet encontrareis que existe casi cualquier cosa que os imaginéis en chips
digitales, pero tomároslo con precaución porque mientras que un 74HC595 lo compras por
1€, el SN74LS674 de 16 bits se va a 4€ y los demás no he mirado pero me lo imagino.
RESUMEN DE LA SESIÓN
o Hemos visto que los pines analógicos también se pueden usar como digitales.
o Hemos presentado nuestro primer chip comercial, el 74HC595, que es un shift
register serial-in, parallel-out de 8 bits, y que se puede conectar en cascada.
o Hemos apuntado muy brevemente a que existen otros chips en el mercado,
literalmente miles o decenas de miles.
o Vimos la diferencia entre comunicación serie asíncrona y síncrona y vimos un
ejemplo de cómo se sincronizan los datos.
o Hemos conocido otra función de Arduino shiftOut().
MATERIAL REQUERIDO.
Paciencia y 20 minutos de
EL PROBLEMA
En la sesión anterior presentamos un circuito con un Shift Register para gobernar 8 salidas
digitales (o más) con solo 3 pines de Arduino y montamos un circuito de 8 LEDS y un primer
programa en el que iterábamos un número de 0 a 255 y cada vez lo enviábamos a la salida
del registro.
Vagamente dijimos que los LED irían contando en binario y mostrando esos valores en el
circuito. Pero hasta ahora no hemos hablado en absoluto de que es el sistema binario o
porque construimos ordenadores basados en él. ¿Es que nuestro viejo y familiar sistema
decimal no es lo bastante bueno para los informáticos? ¿Se trata de una conspiración
internacional para torturar a los estudiantes de informática, o para defender, los inconfesables
intereses de las universidades de ciencias?
En mi experiencia, la gente que ha estudiado electrónica digital o informática, suele tener unas
ideas más o menos claras de estos temas, pero el resto de universitarios y de gente más
joven, rara vez tienen una comprensión mínima básica a pesar de que vivimos un mundo en
el que a diario manejamos tecnología y términos informáticos sin tener un mínimo de
comprensión de la base en que se apoya.
Como en estos tutoriales pretendemos no dar por supuesto ningún conocimiento previo, ha
llegado el momento de introducir algunos conceptos e ideas básicas sobre tecnología digital.
Somos muy conscientes de la desbandada que semejante pretensión puede producir entre los
esforzados seguidores de estos tutoriales, así que vamos a prometerles en primer lugar, que
trataremos de hacerlo fácil y hasta agradable y en segundo lugar que merece la pena, porque
no se puede entender la tecnología moderna sin unas nociones mínimas de sus fundamentos
lógicos.
EL SISTEMA DECIMAL
Estamos tan acostumbrados al sistema decimal, que cualquier idea de que se pueda usar
otro, al principio suena absurdo o hasta malintencionado. Y sin embargo este es solo uno más
entre un ilimitado número de posibilidades.
Desde pequeños, los colegios nos han entrenado en su uso y al final lo tenemos tan
interiorizado que sencillamente, hemos incluso olvidado las reglas básicas que aplicamos,
simplemente lo hacemos y ya está.
Vamos atener que recordar esas reglas. La primera es que se llama sistema decimal porque
utilizamos diez dígitos, del 0 al 9 (No señor, no usamos del 1 al 10). ¿Por qué?
Pues, probablemente, porque tenemos 10 dedos en las manos, y recordemos que las manos
fueron la primera calculadora. De hecho a la representación simbólica de un numero la
llamamos digito que viene del latín digitus (dedo), (y por eso tendemos a pensar que usamos
del 1 al 10…dedos), porque, aunque hemos asumido el sistema de numeración que nos
enseñaron de niños, no es fácil comprender el salto conceptual que significa representar nada
con un 0. Y esta es la clave.
Los antiguos griegos y romanos, por ejemplo, tenían un sistema de numeración que
funcionaba malamente, y que era demencial porque no era posicional sino simbólico, y
además no incluía ni la noción ni la notación del 0. Sumar ya era complicado pero multiplicar o
dividir era casi imposible.
Hizo falta que surgiera un matemático indio, allá por el siglo 3, que comprendió el concepto
del 0 y su importancia como digito, y estableció el primer sistema de numeración moderno.
Desde allí pasaron a los árabes, grandes comerciantes, que enseguida comprendieron la
ventaja de esta numeración para sus negocios( Pues cuando hay por el medio piastras o
euros el ingenio se agudiza mucho), y a través de los árabes llegaron a occidente en épocas
muy posteriores y con la notación actual que nos es familiar. Por eso quizás hayáis oído decir
que en occidente usamos los números arábigos.
¿Qué reglas aplicamos para contar? (Lo domináis, lo sé, pero recordemos el procedimiento).
Pues solamente dos:
Primera: usamos los números en orden creciente 0, 1, 2,3,….9 y cuando se nos
acaban los dígitos de base volvemos a 0 e incrementamos en 1 el digito a la izquierda,
aplicando la misma regla.
Segunda: Si no hay ningún digito a la izquierda entendemos que es un cero y
aplicamos la regla primera para incrementarlo.
Así pues, el siguiente al 9 va a ser un 0 a la derecha (ya que el siguiente al 9 es 0) y a la
izquierda como no hay nada lo consideramos un 0 que al incrementarlo deviene en un 1, o
sea el 10. Lo siguientes números son fáciles:
11, 12,13,….. 19
20, 21,22…..29
……………
90, 91,92…..99
¿Qué ocurre ahora? Pues lo mismo. Aplicamos la regla primera al 9 de la derecha, 9 à 0, e
incrementamos el 9 a su derecha, 9 à 0 y aplicamos la regla al inexistente cero a la izquierda
que deviene en 0 à 1. Por tanto el siguiente número es el 100. Este es un procedimiento para
crear números ilimitadamente grandes y que simplifica enormemente el cálculo.
Pero la elección de 10 dígitos como sistema de numeración es una decisión arbitraria (que por
cierto no ha sido la única en la historia de la humanidad) y no está justificada por ninguna
razón lógica. Es simplemente una costumbre.
Y como los matemáticos son gente que odia las cosas sencillas, a la que les gusta
complicarse la vida y amargárnosla a los demás, empezaron a hacerse preguntas del tipo
¿Existe algún límite, superior o inferior en el número de dígitos de un sistema de
numeración? ¿Son equivalentes? ¿Se puede traducir de un sistema de numeración a otro? Y
aquí es donde se abrieron las puertas del infierno (o del paraíso digital, según se mire).
EL SISTEMA BINARIO
El sistema más sencillo de numeración que podemos organizar se llama binario y contiene
solo dos dígitos: 0 y 1. Con este sistema se puede escribir cualquier número por
ilimitadamente grande que sea y se puede además contar, calcular y hacer cualquier cosa que
se pueda hacer es el sistema decimal, sin excepción.
¿Cómo contamos con un sistema tan raro? Pues en realidad, aplicando exactamente las
mismas reglas que con el decimal, pero limitados a dos únicos dígitos:
0 - 0
1 - 1
10 - 2
11 - 3
100 - 4
101 - 5
110 - 6
111 - 7
1000 - 8
La notación binaria hace que la representación sea mucho más larga ¿y qué?, pero no tiene
otro inconveniente. Además las reglas para sumar y multiplicar harían las delicias de los
escolares.
0+0=0 0*0=0
0+1=1 0*1=0
1+0=1 1*0=0
Y ahora viene la cuestión clave, ¿Por qué usar el sistema binario en los ordenadores en
lugar del familiar sistema decimal?
Pues sencillamente por una cuestión e tecnología: No disponemos de ningún material que
presente 10 estados diferenciados estables que podamos utilizar como fundamento para la
construcción de ordenadores decimales.
Pero sin embargo, si disponemos de materiales (los semiconductores) que presentan dos
estados estables fácilmente diferenciables, los semiconductores nos permiten construir
transistores que mediante una señal de control en la Base, permiten la conducción entre
emisor y colector( cuando funciona en saturación) o la corta por completo ( cuando funciona
al corte).Y es esto lo que reconocemos como 0 y 1.
O lo que es lo mismo con un 1 en la base (HIGH o 5V) podemos leer la tensión de salida
como un 1 o HIGH. Y para un LOW en la base leeremos un LOW en la salida del
transistor. Construimos computadores binarios porque disponemos de una tecnología
estable que se presta a ello.
¿Es el sistema binario superior, o mejor que el decimal, en cualquier aspecto retorcido de la
teoría de algo? Para nada, son completamente equivalentes.
¿De disponer de una tecnología con 10 estados estables, que nos permitiera construir
ordenadores basados en el sistema decimal, seguiríamos usando el sistema binario? Ni
hablar, y el binario se relegaría a las escuelas de historia solamente (y a los matemáticos
claro).
Muy a principios del siglo 19, Charles Babbage ya construía maquinas mecánicas en
Inglaterra, que calculaban basados en una lógica parecida, pero la tecnología disponible
entonces solo permitía usar la mecánica y lamentablemente la velocidad de computo que se
obtenía era un asco comparado con la electrónica actual.
De hecho es factible construir computadores con engranajes mecánicos, sistemas
neumáticos, relés y hasta grifos de agua, pero el coste y la velocidad de cómputo que se
obtiene no compensan el esfuerzo.
Aunque IBM ya construía computadoras e los años 40 del siglo 20, eran caras y
problemáticas porque se construían a base de válvulas termoiónicas de vacío ( las famosas
lámparas, o válvulas de la radio de la abuela). Fue con el advenimiento del transistor en 1947
(que supuso el Nobel de física para sus descubridores) que empezó la carrera tecnológica
actual.
Al principio los transistores se fabricaban de uno en uno, pero la gente (que no es tonta), en
seguida se dio cuenta de que podía fabricar varios transistores en una oblea de silicio,
interconectados entre sí, además de todos los componentes necesarios para integrar un
circuito electrónico en un único chip.
Y aquí empezó una carrera por ir metiendo más transistores y más componentes en un chip.
Montar las fábricas es caro pero lo que fabricas es casi gratis. (El Silicio sale de la arena de
playa) así que cuanto más componentes integras, más cosas hace el circuito y más caro se
puede vender.
A finales de 2014, el circuito comercial que más transistores integra es un procesador de Intel
con alrededor de 4.300.000.000 transistores (Si, está bien, Unos cuatro mil trescientos
millones).
Y para cerrar este capítulo mencionaremos que la famosa ley de Moore dice que el número de
transistores que somos capaces de integrar en un chip se dobla cada dos años (más o
menos) y que se ha cumplido bastante fielmente en los últimos 50 años, aunque parece haber
dudas de que esto vaya a seguir siendo así.
Si alguien pensaba que una vez establecido el sistema binario además del decimal, los
matemáticos nos iban a dejar en paz de una vez, se ve que es nuevo y no conoce a esa
gente.
Un momento ¿16? ¡ Si no hay tantos números ! ¡Pues se inventan los que haga falta!
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F
Os acordáis de que cuando vimos la sesión del keypad matricial, contenía todos esos
símbolos? ¿Creíais que era casualidad?
Vuestro humilde Duino, soporta todos estas notaciones de números, y ya buscaremos
algun ejemplo para ilustrarlo.
¿Porque usar otro par de sistemas de numeración tan extraños? Porque son potencias de 2,
23 y 24, y son especialmente fáciles de pasar de uno al otro, especialmente al hexadecimal
Ignoraremos el octal, porque se usa más bien poco, pero el hexadecimal, sí que se usa
bastante. Para entender porque, tenemos que dar un rodeo.
En el sistema decimal, a llamamos dígitos a cada uno de los símbolos que representan los
números de la base aunque su nombre correcto sería digito decimal.
En el sistema binario llamamos dígitos binarios a cada uno de sus símbolos 0 y 1, en inglés se
dice BInary digiT, o sea bit, una de las palabras mágicas de la informática.
En el sistema decimal, de un numero como 4.327 decimos que tiene cuatro dígitos y en el
sistema binario de algo como 1100 0101 decimos que es de 8 dígitos, o sea de 8 bits.
En el mundo real, además de los diez dígitos usamos veintitantas letras mayúsculas y otras
tantas minúsculas, adema de símbolos especiales como +, -, *, /, € ….
Pero con la tecnología digital, sencillamente no hay más numero posibles,¿ Así que, como
representamos todo este caos? Pues solo hay una solución: Creando un código de
representación de caracteres, de forma que cada símbolo se represente por una combinación
de bits en una tabla.
Algo que ya habíamos visto cuando hablamos en la sesión 7 del código ASCII. Lo que no
dijimos allí es que para poder representar todos los caracteres del alfabeto occidental (y
reservar alguno para sorpresas como el €) necesitamos utilizar paquetes de 8 bits porque con
ello podemos codificar 28 = 256 caracteres.
Parecía que había que bautizar a los paquetes de 8 bits. Alguien dijo Byte, y mira, hasta hoy.
Así que un bit es un simple digito binario y la información que puede codificar es 0 o 1. Es
difícil codificar un elemento de información más pequeño.
Si alguien piensa que solo con el 1, sería menor, se olvida de que no hay manera de
librarse del 0 y con un solo dígito, todos los números serian iguales, piénsalo, habríamos
vuelto a las cavernas, poniendo un palito por cada bisonte.
Un paquete de 8 bits, que puede llevar información de texto codificada se llama byte. Pero con
la tecnología moderna la capacidad de las memorias ha crecido tanto que necesitamos
unidades mayores así que tiramos del griego:
Peta 1015
Exa 1018
Zetta 1021
¿A que no os sabíais estos últimos?
Los prefijos decimales son las potencias de la base 10, pero por aprovechamiento, en un
sistema binario conviene usar las potencias de 2 y aprovecharnos de lo redonda que queda la
numeración, pero recordando que la Mega en binario son 1024×1024 aunque luego nadie
recordemos cuanto exactamente.
Ahora si podemos entender porque el sistema hexadecimal es útil en informática. Un carácter
se codifica con 8 bits y un número hexadecimal representa 4 bits exactamente por lo que
cualquier paquete de 8 bits se traduce en dos dígitos hexadecimales. La conversión es
automática y el empaquetado más conciso:
A 65 0100 0001
B 66 0100 0010
C 67 0100 0011
D 68 0100 0100
Fíjate en la numeración e las primeras letras ( en ASCII). El binario corresponde a sus 8 bits y
en hexadecimal solo dos. Convertir de binario a ex consiste en tomar los números de 4 en 4 y
asignar el valor hex equivalente, para la D son dos cuatros.
Creo que ya hemos abusado bastante de la buena voluntad de nuestros resignados
lectores y por tanto, evitaré entrar en cómo se pasa un número de un sistema a otro.
Todos los lenguajes de programación incluyen notación para introducir número binarios,
decimales, octales y hexadecimales y C++ no es la excepción. En las próximas sesiones
tendremos ocasión de verlo ( No, no es una amenaza).
RESUMEN DE LA SESIÓN
o Hemos recordado los fundamentos del sistema decimal.
o Presentamos el sistema binario.
Que no es mejor ni peor que el decimal, sino distinto..
Que lo usamos porque se adapta brillantemente a la tecnología
disponible.
o Para no aburrirnos, también hemos presentado el sistema hexadecimal, que se
emplea abunda mente en las ciencias de computación.
o Vimos de donde vienen los términos bit, byte, KiloByte o MegaByte.
MATERIAL REQUERIDO.
Arduino UNO o equivalente.
Una Protoboard más cables.
Una resistencia de 330Ω.
Por eso se desarrollaron los sensores de la familia DHT. Nos proporcionan de forma digital
la temperatura y la humedad, con diferente precisión según el modelo.
Barato, entre 4 y 5 €
Funciona con 3,3 y %V de alimentación
Rango de temperatura: de -40º a 125º ±0.5°C
Rango de humedad: de 0% al 100% con 5% de precisión.
Lee 2 veces por segundo.
Bajo consumo.
Devuelva la medida en ºC
En cuanto a la forma de conectarlos y programarlos es la misma para ambos modelos y
veremos que hay desarrolladas librerías para Arduino que soportan los dos de una forma
sencilla.
La conexión es trivial, pero cabe destacar que se vende en dos encapsulados, uno de tres
pines que son GND, Data y Vcc, y otro 4 pines y uno de ellos, sencillamente sobra y no se
conecta. Normalmente viene rotulado en el sensor el nombre de cada pin, y si no ya sabeis, a
buscar el manual en Google
Vamos con el esquema por cortesía de Fritzing:
En primer lugar, tenemos que descargar una librería para manejarlos cómodamente, DX11.zip
e importarla. Aquí teneís la librería DHT11.zip
Hacemos el importar la librería DHT11 que nos pondrá:
#include <DHT11.h>
Y definimos una instancia del sensor donde declaramos el pin al que esta conectado.
int pin=2;
DHT11 dht11(pin);
int error ;
Basta con hacer dht11.read pasandole las variables donde queremos el resultado, y
comprobamos que no haya errors (Siempre es buena idea comprobar que no hay error
cuando hacemos una llamda). El programa completo seria más o menos algo así: Prog_24_1
#include <DHT11.h>
int pin=2;
DHT11 dht11(pin);
void setup()
Serial.begin(9600);
void loop()
int err;
Serial.print("Temperatura: ");
Serial.print(temp);
Serial.println();
else
Serial.println();
Serial.print(err);
Serial.println();
Como vereris, son de lo mas sencillos de utilizar y un componente habitual de vuestro arsenal
de sensores
RESUMEN DE LA SESIÓN
Hemos visto los sensores DHT11, y montado un circuito de prueba con el
Los DHT22, usan el mismo circuito y pines, pero su resolución y velocidad es bastante
mejor, aunque para muchas aplicaciones el DHT11 es mas que suficiente.
MATERIAL REQUERIDO.
Paciencia y 20 minutos de
Y como este es un curso pensado para iniciarse desde cero, ha llegado el momento de hacer
un pequeño resumen de procesadores y memorias, sus diferencias y el porqué de unas y
otras. Poneros cómodos.
Haciendo un poco de historia (poco, tranquilos), el primer micro procesador con derecho a
llamarse así, fue el Intel 8080 allá por los finales de los años 70.
Fue el primer chip, que integraba todo lo necesario para construir una unidad central de
proceso (o CPU por sus siglas en ingles), en un único chip, y a su alrededor se construyeron
los primeros ordenadores personales sin más que añadir memoria y elementos de entrada
/salida).
Para no ponerme pesado, diré simplemente, que aquel modelo basado en CPU, memoria e
I/O (Input Output) ha seguido vigente hasta hoy, en lo que conocemos como PCs.
Los números han crecido exponencialmente, claro, pero el modelo conceptual sigue siendo el
mismo, desde el 8080 hasta los modernos Intel I7.
Bus datos 8 64
Los procesadores han tenido un periodo de expansión infinita, pero siguen teniendo los
mismos problemas que el primer día:
No tiene conexión, ni influencia en el mundo físico. ¿Te imaginas encender unos
LEDs, o mover un motor con tu PC? No es imposible, pero no sabríamos por dónde empezar.
Podemos guardar datos en los cada día más enormes discos duros, pero los
programas deben ejecutarse desde memoria RAM, y esto ha sido un cuello de botella, con los
modernos sistemas operativos como Windows u OSX, que devoran ansiosamente, cuanta
memoria tengas instalada.
En cuanto pierdes la alimentación, la RAM vuela y tus datos salen por el desagüe, si
no has tenido la precaución de salvar a disco periódicamente.
Los microprocesadores modernos se han diseñado para ser rapidísimos haciendo
cálculos complejos, y han alcanzado una capacidad tal, que sencillamente hace mucho que se
sobrepasó lo que un usuario normal necesita, y les ha costado algunos años darse cuenta.
Entre tanto y en paralelo, pequeños fabricantes, a los que difícilmente conoce el público, han
ido generando pequeños procesadores, menos ambiciosos, pero más dirigidos a resolver un
tipo de problemas que Intel, AMD y alguno otro ignoraban. (Evito conscientemente el tema de
los procesadores de teléfonos móviles, porque abriríamos una puerta que se sale de lo que
nos atañe ahora mismo)
Uno de esos fabricantes es ATMega, y nuestros Arduinos están construidos a partir de uno de
sus procesadores: UN AT328 para el Arduino UNO. Y que se vende en dos encapsulados:
Podéis encontrar Arduinos UNO con ambos modelos y su única diferencia es el encapsulado
plástico que los recubre, aunque el del primer tipo se suele montar en un zócalo, para poderlo
reemplazar cuando le hagáis algo que no deberías.
En una sesión futura tengo previsto que montemos un Arduino en una protoboard,
para que veáis lo sencillo que es.
Sus especificaciones técnicas son en principio modestas, comparándolos con los últimos
procesadores de Intel,
Pero tienen la curiosidad de incorporar pines digitales que pueden sentir y comandar el
mundo exterior, (a los que los seguidores de estas sesiones están ya más que habituados)
además de convertidores analógicos a digital (ADC)
Es un modelo útil imaginar la memoria del ordenador, como en una hilera de cajas o cajones,
todos del mismo tamaño que están numerados de 0 a la n, donde n es el tamaño de nuestra
RAM. Cuando enciendes el ordenador lo primero que hace nuestra CPU en cuanto se
despierta es salir corriendo al cajón 0, ver que instrucción tiene allí y ejecutarla. Después con
el deber cumplido salta al siguiente cajón, el 1, lee la orden y la ejecuta, después salta al cajón
2 y……
Las primeras instrucciones que tiene en el cajón 0, se llaman boot strap (que es algo así como
el cordón de arranque de un motor), en nuestro caso son las instrucciones que le enseñan a
interpretar el modelo Arduino, para aceptar nuestros programas. Cuando termina con esto, el
boot strap le dirige directamente a nuestras instrucciones y por eso los ordenadores ejecutan
las órdenes en secuencia, porque sencillamente no saben hacerlo de otro modo, a no ser que
nosotros les ordenemos saltar a otros cajones en función de las condiciones que les
especifiquemos.
En los primeros ordenadores, solo había dos tipos de memoria: RAM y ROM, pero ya sabéis
que las cosas tienden al caos y las todo se han ido complicando desde entonces.
Todo lo que ejecutamos en nuestros PCs debe estar almacenado en memoria RAM o Random
Access Memory. Se suele traducir por memorias de acceso aleatorio. . Se pueden leer y
escribir (lo ideal, vamos) pero tienen la mala costumbre de borrase cuando les quitan la
corriente.
Cuando arrancas tu PC ya sabe hacer algunas, como leer el boot strap y por tanto, esto no
puede estar en RAM (Se habría perdido al apagar), eso está en ROM.
Las memorias ROM, o Read Only Memories, como su nombre indica son de solo lectura y no
se pueden escribir más que por el fabricante cuando las construye.
Tradicionalmente una sabia combinación de memorias RAM y ROM han permitido todo el
desarrollo de la microinformática, pero pronto empezaron las variedades:
MEMORIA CARACTERÍSTICAS
ROM Read Only Memory, son no volátiles, pero solo se pueden leer, no escribir.
Programable Read Only MemoryNo son volátiles y se pueden escribir y reescribir en unos
PROM
prácticamente están en desuso
Electically Erasable Programable Read only MemorySon igual que las PROM, pero con la
EEPROM eléctricamente lo que evita el engorro de llevarlas a reprogramar a maquinas raras.A camb
número limitado de escrituras antes de morir.
Pines digitales 14 54
Pines PWM 6 16
Convertidores ADC 6 16
Convertidores DAC 0 0
Interrupciones 2 24
Tenéis que saber que nuestros programas se graban en la memoria Flash (que es bastante
lentora de escribir), pero las variables que definamos en nuestro programa se guardan en
memoria SRAM (que es mucho más rápida para escribir en ella)
Por eso, nuestro familiar Arduino UNO, se os va a quedar corto enseguida porque va muy
escaso de memoria. En cuanto empecéis a incluir librerías en vuestros programas el margen
de memoria flash se reducirá peligrosamente y entre tanto, la parte de SRAM dedicada a
manejar las variables se puede llenar con facilidad, sino controláis los tamaños de estas o de
los arrays que creéis.
De hecho, quizás no os hayáis fijado, pero cada vez que compiláis y volcáis un programa
vuestro Arduino, el IDE os informa de como andáis de memoria, de uno y otro tipo:
Así que, a medida que vuestros programas y proyectos vayan creciendo es interesante que
tengáis en reserva un Arduino Mega para lo que sea menester. Que desde luego, es el
siguiente Arduino que os interesa añadir a vuestra colección.
Si necesitas potencia de cálculo, para procesar audio, hacer transformadas de Fourier FFT, o
filtros digitales, en los que lo que prima es la potencia y la velocidad, el DUE es tu candidato,
sin duda.
Pero si quieres ponerle algún shield (ya hablaremos) o enganchar con integrados TTL y
similares, los 3 voltios del DUE te van a complicar la vida, en seguida. Si quieres un Arduino
UNO a lo bestia para lo que haga falta, compra el Mega (que además es bastante más barato)
y estarás encantado.
Además con la evolución de precios que todo esto esta teniendo (a la baja) es probable que te
acabes comprando uno de cada.
Hay mas modelos en camino, pero de momento no conviene complicarnos mas.
Por eso hay ocasiones, en las que es interesante poder conservar opciones de configuración
del usuario, sin tener que preguntarle lo mismo cada vez que reinicia.
Vamos a escribir un pequeño programa que muestre como almacenar valores en la EEPROM,
de forma que podamos recuperarlos aun cuando se vaya la luz.
#include <EEPROM.h>
EEPROM.write(i, i);
#include <EEPROM.h>
value = EEPROM.read(pos);
Donde pos, es la posición de EEPROM que queremos escribir. Para un UNO pos puede llegar
hasta un máximo de 1.024, que es la longitud de EEPROM disponible. Para un Mega puede
alcanzar 4k = 4.096
Y poco más se puede decir de este asunto. Que está disponible una especie de mini disco
duro secuencial, dentro de vuestro Arduino, y que es bueno que sepáis que podéis usarlo
cuando lo necesitéis.
En cuanto al tema de que esta memoria se degrada con el uso y acaba volviéndose
inservible, vale la pena revisar la documentación del fabricante (Si, el dichoso manual que hay
que leerse siempre) confirma este punto y dicen que solo aguanta 100.000 reescrituras antes
de degradarse.
Así que si grabas 4 datos diarios, se estropeara al cabo de, veamos, 100.000/4 /365 =
algo más de 68 años, así que ya sabéis, mucho cuidado al escribir la EEPROM, no vayáis a
gastarla.
Parece existir un absurdo temor a usar las EEPROM en la comunidad Arduino. Os
recomiendo que las escribais sin miedo. Hay muchas posiciones y si alguna se estropea,
siempre podéis escribir en otras posiciones. El UNO tiene 1024 diferentes.
RESUMEN DE LA SESIÓN
Hemos hablado un poco de los diferentes tipos de memoria y de sus motivos de ser.
Hemos visto las características de diferentes procesadores, al redor de donde se con
construyen los modelos de Arduino.
Hemos presentado varios modelos diferentes de Arduino.
MATERIAL REQUERIDO.
Un sensor de agua
SENSORES DE AGUA
Por diferentes razones, no es raro necesitar detectar si hay presencia de agua en un lugar.
Puede ser porque queremos saber si ha habido un escape de agua, o para saber si hay
condensación en una cierta zona, y muy frecuentemente nos viene bien conocer el nivel de un
líquido en un depósito
En cualquier caso como son bastante baratos, he pensado en incluir un ejemplo sencillo de su
uso, y quien sabe, quizás puedan seros útiles.
Son muy simples y lo único que hay que hacer es conectar a tensión y GND y el tercer pin es
una señal analógica, proporcional a la cantidad de agua que detecta.
DIAGRAMA DE CONEXIÓN
En este diagrama he pintado unos cables cortos por necesidad, pero cuando montéis
este ejemplo, os recomiendo que pongáis los cables más largos que tengáis, porque la
electrónica y el agua no se llevan bien.
Tened cuidado de no volcar agua en vuestro Arduino, o morirá en acto de servicio.
Si insertáis el sensor en agua, para usarlo como medidor de nivel, tened mucho
cuidado de no sumergir los pines de conexión a Arduino, o provocareis un corto.
No hay suficiente tensión como para provocar un accidente, pero siempre es
recomendable impedir un cortocircuito.
PROGRAMAMA DE CONTROL
El programa es de lo más simple y sabrá a poco después de las últimas sesiones que hemos
tenido.
Vamos a leer la señal del sensor con nuestro A0 y enviaremos la lectura a la consola serie
para ver la medida. Jugando a mojar más o menos el sensor veremos cómo los valores van
cambiando a más cuanto mayor sea la cantidad de agua que detecta.
void setup()
{
Serial.begin(9600);
void loop()
Serial.println(analogRead(A0));
Como no he sido capaz de grabar un vídeo decente del sensor, el agua, y la salida serie de la
consola Arduino, he optado, por incluir un display en el vídeo, que nos permita ver la lectura
de la puerta A0.
Como a pesar de todo alguno me seguis pidiendo el programa con el display os lo
pongo aqui por si quereis echarle una ojeada: Prog_26_1
El vídeo pretende simplemente, mostraros la utilidad del sensor de agua y no del display. Así
que aquí tenemos ese mini video con el resultado:
RESUMEN DE LA SESIÓN
.
o Hemos presentado un sensor muy sencillo de agua, muy útil si quereis
detectar inundación o simplemente gotas de lluvia.
o No es un sensor de humedad, que es un tipo diferente de sensor.
o Se puede utilizar como un sensor de nivel de agua relativamente pobre.
MATERIAL REQUERIDO.
Arduino UNO o similar.
Un buzzer piezoeléctrico
Un sensor de llama
SENSORES DE LLAMA
No resulta extraño que los sensores de llamas se encuentren entre los más vendidos. La
industria los suministra de todos los tipos, tamaños y precios, porque la idea de que se te
queme la casa o la empresa es algo que ayuda mucho a sacar la cartera y buscar un detector
que sea capaz de avisarte con tiempo de que hay llamas cerca de tus cosas.
En realidad, es muy probable que tengas varios de estos detectores en tu casa. Imagínate,
por ejemplo, los calentadores de gas, los hornos domésticos de gas, o un sencillo calentador
de agua y calefacción.
Todos ellos requieren una llama quemando gas. Si por cualquier motivo, la llama se extingue y
no lo detectases, el gas seguiría fluyendo y causando un embalsamiento, listo para causar un
disgusto explosivo.
Por eso, todos estos electrodomésticos, y sus equivalentes industriales, incorporan por ley, un
detector de llama, que pueda cortar el gas en cuanto detecte falta de llama.
Lo sensores más sencillos (y baratos), suelen ser del tipo de detectores de ultravioletas, o
sensores de infrarrojos.
El mercado ofrece, también, un amplio espectro de sensores del tipo de detectores de
humo y de gases nocivos como monóxido de Carbono (CO) típico de la combustión,Dioxido
de Carbono (CO2), detectores de humo, o de gases peligrosos libres, como hidrocarburos
(Propano, Butano y demás).
Si tenemos ocasión en el futuro hablaremos de ellos.
En un entorno normal, la ignición de una llama provoca una ionización potente de gases
comunes (En realidad un plasma ionizado, similar al que ilumina una lámpara halógena), que
generan un patrón típico de frecuencia en el rango del ultravioleta. El detector es un
semiconductor sensible a este rango de frecuencias.
Nosotros vamos a usar un sensor muy sencillo y fácil de manejar que es un sensor infrarrojo
de llamas. Funcionan detectando una longitud de onda especifica (de unos 760 nm) que son
características de las llamas, aunque son relativamente fáciles de engañar y pueden dar
falsos positivos con ciertas luces.
Como todo en la vida, es cuestión de precio, y el nuestro es barato, así que nos tendremos
que apañar, pero a pesar de todo, confio, en que el resultado de la sesión, sea satisfactorio.
ESQUEMA DE CONEXIÓN
Lo normal para leer este sensor de llamas, seria usar un divisor de tensión como este:
Donde R1 debería tomar valores sobre 500k y como no tenía nada parecido, he usado 4
resistencias de 100k en serie, para tener lecturas claras en la puerta A0 de Arduino. EL
esquema eléctrico es algo así:
PROGRAMA DE CONTROL
El programa es de lo más simple. Solo tenemos que leer la caída de tensión en el divisor y
mandar una señal acústica con el buzzer en cuanto el valor de la lectura en A0 sobrepase un
cierto umbral.
void setup()
pinMode(9, OUTPUT);
}
void loop()
int v = analogRead(A0) ;
if ( v > 10)
beep(200) ;
analogWrite(9, 20);
Lo que hace el programa es leer el pin A0 y si su valor sobrepasa un mínimo (para evitar los
falsos positivos) llama a la función beep() para dar la alarma.
Nota: Normalmente hay que calibrar la sensibilidad del sensor. Para ello hay que ver
que valores te devuelve en tu caso concreto y afinar este valor umbral, para que no esté
pitando continuamente.
Los sensores en placa, suelen venir con un potenciómetro, que hay que ajustar para
calibrar la sensibilidad el detector.
Tambien convendría modificar el programa, para que el tono de la señal se hiciese
mas agudo cuanto mas alto sea el valor de lectura en A0
Aquí os dejo un mini video con un ejemplo:
RESUMEN DE LA SESIÓN
o Hemos visto que estos pequeños sensores de llamas son simples y
razonablemente eficaces.
o Dan bastante falsos positivos especialmente si les acercáis una luz potente.
o Os recomiendo la versión con montura por la comodidad de uso.
o Hemos creado una pequeña alarma de incendios con unos materiales de muy
poco coste
MATERIAL REQUERIDO.
Una Protoboard.
Un diodo LED.
En el centro tenemos la conexión a 5V y a GND (+ y G).
D0 es una salida digital que actúa a modo de comparador. Si el sonido captado por
el micrófono supera un determinado nivel se pone a HIGH.
A0 es una salida analógica que nos da un valor entre 0 y 1023 en función del volumen
del sonido.[/fancy-ul]
Además tenemos dos LEDs, uno que nos indica si hay alimentación en el sensor y otro que se
ilumina si D0 está a HIGH.
Primero vamos a usar la salida digital D0 como señal para encender un LED, de forma que
cuando demos una palmada, un silbido o hagamos algún ruido un poco alto, se encienda o se
apague un LED.
Sólo necesitamos conectar el pin D0 y los dos pines de alimentación, tal como se ve en el
diagrama electrónico y el montaje en la protoboard:
Si hemos conectado bien el sensor, se debería iluminar el LED de alimentación. El de salida
digital accionada puede o no estar encendido.
AJUSTANDO EL LÍMITE DE DISPARO
Esta es seguramente la parte más complicada de esta sesión. Para ajustar el límite de disparo
lo que hacemos es girar el potenciómetro con un destornillador. Tenemos que dejarlo de tal
forma que el LED que marca si está accionada la salida digital esté apagado, pero lo más
próximo posible al límite en el que se enciende.
Si lo ajustamos mal y el LED se está encendido, no detectaremos ningún cambio y no
podremos reaccionar a ningún estímulo sonoro.
Si lo ajustamos de forma que esté apagado pero demasiado lejos del límite en el que
se enciende, habrá que llamar al increíble Hulk para que dé una palmada por nosotros.
PROGRAMANDO EL SENSOR
El programa para controlarlo es muy sencillo y es el mismo que hemos utilizado varias veces
para controlar un interruptor. La diferencia es que en vez de utilizar un pulsador, si el sensor
detecta un sonido por encima del límite enviará una señal que recogeremos en la entrada
digital 2.
int LED = 13 ;
int sensor = 6 ;
void setup()
}
void loop()
delay (1000);
Si no sois capaces de encender el LED de una palmada, probad a pegar un silbido, un
soplido o a hablar cerca del micrófono.
Podéis descargar el programa completo aquí: Sensor_sonido_D0.
Como hemos explicado al principio, el pin A0 nos devuelve un valor entre 0 y 1023 en función
del volumen del sonido que esté registrando.
Las conexiones serían las mismas pero conectando la salida A0 del sensor a cualquier
entrada analógica del Arduino. Podéis desconectar la salida D0 o dejarla conectada y seguir
usándola. El potenciómetro del sensor serviría en este caso para fijar la sensibilidad del
sensor.
El programa para mostrar las medidas del sensor en el monitor serie sería muy sencillito y ya
lo hemos utilizado varias veces:
void setup()
void loop()
{
int Lectura = analogRead(A5) ;
Serial.println( Lectura);
delay(200) ;
RESUMEN DE LA SESIÓN
Cómo funciona el sensor de sonido KY-038 y a utilizar tanto su salida digital como
analógica.
Podemos interaccionar con nuestro Arduino a través del sonido.
MATERIAL REQUERIDO.
Hemos ido viendo hasta ahora como utilizar un sensor de temperatura y también como usar
un pequeño transistor para mover un motor de CC, pero no los habíamos mezclado.
Como no disponemos de ningún sistema a refrigerar, nos conformaremos con enfriar el propio
sensor, lo que no resulta especialmente provechoso, pero sin duda, nos servirá para
mostraros un ejemplo de control y reacción, en el mejor espíritu de un sistema automático, tal
como un regulador de temperatura con nuestros Duinos.
Veremos que la regulación de un sistema tan sencillo no tiene grandes problemas y por eso
esta sesión esta mas pensada como un ejercicio de control sencillo y que como una
regulación en toda regla.
Además el parámetro que buscamos controlar, la temperatura, tiene la virtud de variar con
lentitud, lo que nos deja mucho tiempo para reaccionar y no tener que preocuparnos por
cuestión de reaccionar con precisión en tiempo real.
Hace mucho tiempo, cuando empecé a trabajar en cosas de estas, y alguien
preguntaba qué era eso de trabajar en tiempo real, definíamos el tiempo real como el margen
que tenías para reaccionar y controlar el parámetro díscolo antes de que algo caro se fuera al
guano.
Cuando estás en un marrón así, las técnicas que vamos a aplicar aquí no valdrían y
tendríamos que empezar a hablar de cosas como el control PID, pero por ahora es pronto, así
que mejor lo dejamos aquí.
Vamos por tanto a montar un circuito sencillo con el sensor de temperatura y otro
independiente con un transistor para controlar el motor del ventilador en la misma protoboard.
Vamos a montar un pequeño circuito que lea la temperatura de un sensor, imprima el valor en
la consola y encienda un diodo cuando esta sobrepase un cierto umbral.
Ya hemos visto en sesiones previas como usar un sensor LM35DZ, pero si alguno queréis
revisarlo podéis hacerlo aquí: Sensor de temperatura.
Vamos a empezar probando el sensor para comprobar que funciona correctamente, y para
ello vamos a usar este programa:
void setup()
void loop()
Serial.println(temp) ;
delay(200);
Usamos la puerta Analog0 que definimos como una constante sensor, y en el loop leemos la
puerta y convertimos el valor de la lectura a grados centígrados (Recordad que la razón de
hacerlo así la vimos en la sesión de los sensores de temperatura) e imprimimos sin más el
valor de la temperatura. Deberías ver algo parecido a esto:
El sensor LM35DZ tiene un margen de error de alrededor de ± 0,5 grados y por eso las
lecturas tienen oscilación, dando diferencias de hasta un grado.
El baile de las lecturas se debe a que el mundo real nunca es perfecto y hay cosas que van
desde la incertidumbre de la medida causada por el propio sensor hasta los errores en los
convertidores de nuestro Arduino (Que no son los mejores del mundo) e incluso interferencias
electromagnéticas espurias.
Todo ello se confabula para que tengamos ese molesto baile de cifras en la medida y aunque
en este caso no tiene importancia puede convenirnos filtrar este tipo de errores y para ello
podemos promediar las n ultimas lecturas y presentar esto como el valor de la temperatura.
Para ello podemos guardarlas digamos 8 últimas lecturas y promediarlas, con lo que
aplanaremos fuertemente los errores, y la manera más sencilla es usar un buffer circular (O
Ring Buffer)
La forma de apuntar a la posición a escribir es una variable llamada Índex que empieza en 0 y
cada vez que escribe en la posición correspondiente se incrementa en 1 ¿Y cómo evitamos
que pase del valor máximo aceptable? Pues tomando el resto del valor de Índex con respecto
al número de posiciones del array.
index = ++index % 8 ;
Una vez que apuntamos correctamente a la posición a escribir y después de los N valores
iniciales en los que la temperatura promedio será falsa (Porque aún no hemos leído N valores)
tendremos en Buffer las ultimas N lecturas y el resto es aritmética:
int index = 0 ; // Posicion a escribir
index = ++index % N ;
float Tmedia = 0 ;
Serial.println( Tmedia / N) ;
Aquí os dejo el programa complete, por si quieres jugar con el: Prog_130_2. Para N = 32
podéis ver algo así:
Podéis ver que la temperatura baila mucho menos y el baile desciende a medida que
aumentáis el número de muestras que promediáis, pero cuidado, cuanto más crece N también
crece el tiempo que tardáis en responder a los cambios ( Para N = 32 a 5 muestras por
segundo hay más de 6 segundos de tiempo de reacción).
Aunque con una temperatura ambiente eso no es grave, puede haber ocasiones en
que lo sea, por ejemplo porque un quemador arranca, y en 6 segundos le sobra tiempo para
chamuscar lo que sea que estemos controlando, así que ojo.
INCLUYENDO EL VENTILADOR.
void setup()
{ Serial.begin(115200);
pinMode(control, OUTPUT) ;
void loop()
Serial.println(temp) ;
digitalWrite(control, HIGH);
else
digitalWrite(control, LOW);
delay(200);
Fijaros que al alcanzar el umbral, disparamos el ventilador a tope de modo digital. Es
decir todo o nada. En una situación real probablemente convendría hacer un control
proporcional de la velocidad del ventilador en función de lo lejos que esta del punto que
buscamos ( Aquí es donde entran las cuestiones de control PID)
Os propongo que hagáis un ejemplo en el que la velocidad del ventilador sea
proporcional a la diferencia entre la temperatura umbral y la temperatura medida.
Aquí tenéis un pequeño vídeo mostrando el montaje:
RESUMEN DE LA SESIÓN
Una Protoboard.
Una resistencia.
En cuanto empecéis a plantearos hacer un circuito que realice alguna función práctica, hay un
50% de probabilidades de que acabéis necesitando enviar información al mundo exterior (y
claro no vais a dejar vuestro portátil pegado al invento).
Así que es el momento de ver que nos ofrece el mercado para mostrar información y para
empezar, nada mejor que un sencillo display LED de 1 digito.
Los diodos LED ya no guardan secretos para los que nos siguen, pero esto de un display
parece otra cosa.
Pues no. Es lo mismo pero en lugar de ser un punto luminoso como los familiares LED, tiene
forma de barra: 7 barras más 1 punto.
Estos displays nos son bastante familiares en despertadores y similares, están formados por 7
líneas, que a dependiendo de qué segmentos se iluminan, muestran los dígitos del 0 al 9.
A cada uno de los 7 segmentos se les adjudica una letra para representarlos y suele ser a, b,
c, e, f, g y aparte un punto (o dos, o dos puntos según el fabricante) y es un buen momento
para echar un vistazo a las especificaciones de cada fabricante para estar seguros de que pin
es cual.
Todos los circuitos, los displays incluidos, llevan un código de fabricante rotulado. Es buena
política buscar en internet la hoja de especificaciones(o data sheet) y echarle un ojo. Si no
entendéis nada, es lo normal de momento, pero desde luego habrá un dibujo diciéndoos que
es cada pin. Yo he buscado el modelo que tengo delante, (un Kingbright SC08-11HWA ), pero
no os fieis, el vuestro puede ser distinto.
Desde que existe Internet, localizar una hoja de características es algo trivial (No
como antes, que para conseguirlas había que pedir favores y mostrar las debidas muestras de
respeto y sumisión a quien disponía de catálogos de componentes).
Probad a buscar en Google algo así como XXXXX data sheet, donde XXXXX son los
códigos que habéis encontrado rotulado en el display o componente que sea.
Lo importante es que sepáis que es cada pin, y que veáis que segmento es cual. En realidad
es bastante más fácil de lo que parece, localizad primero un GND. En el de arriba son el 3, 5,
11 y 16.
Poned una resistencia (de 330Ω como siempre) de la salida del display a GND. Este es un
display de cátodo común, o sea que todos los LEDS comparten la masa, pero necesitamos
poner una resistencia de limitación o fundiréis el display, y este vale ya lo suficiente, como
para que os cueste verle la gracia al tema.
Una vez que hayáis conectado la masa común con una resistencia, empezad cableando solo
un LED, el a por ejemplo y conectadlo al pin 2 de Arduino. Escribid un programa en Arduino
para iluminar el LED en el pin 2 y veréis como se enciende el segmento a.
Podéis ir conectando y probando el resto de los pines poco a poco, asegurando las
conexiones. Es muy fácil equivocarse si no andáis con tiento.
Para saber cual es el pin 1, poner el display de modo que viendo los numeros el punto decimal
quede abajo a vuestra 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.
El circuito no puede ser más sencillo, son como cuando hicimos un circuito con 8 diodos, solo
que esta vez los 8 diodos están en una cajita y con forma para dibujar números.
Es importante que sigáis el esquema de aquí encima para que luego el programa
coincida con vuestras conexiones.
Por eso no es importante que pin de vuestro display es el segmento C (Que
dependerá del fabricante y modelo), pero si que es importante que el segmento C vaya al pin
digital 4 de Arduino y lo mismo para los demás.
No vamos a incluir el diagrama de protoboard, porque es difícil dejarlo más claro
PROGRAMA DE CONTROL
Tenemos que comprobar que las conexiones son correctas lo primero. Probad esto:
void setup()
pinMode(i, OUTPUT);
void loop()
digitalWrite(j, HIGH);
delay(400) ;
digitalWrite(j, LOW);
delay(400) ;
}
En el setup, inicializamos los 8 pines de salida con for (recordar que somos vagos) y en el
loop usamos una primera iteración que va iluminando los 8 segmentos con un delay, y en la
segunda los borra y vuelta a empezar. La idea es comprobar que las conexiones son
correctas.Vereis algo así:
Para ello, lo más rápido es hacerte una tabla para ver que segmentos tienes que iluminar para
cada digito decimal, y vas apuntando cuales son 1 (encendido) o 0 (apagado).
DIGITO A B C D E
0 1 1 1 1 1
1 0 1 1 0 0
2 1 1 0 1 1
3 1 1 1 1 0
4 0 1 1 0 0
5 1 0 1 1 0
6 1 0 1 1 1
7 1 1 1 0 0
8 1 1 1 1 1
9 1 1 1 0 0
Aunque os damos los deberes hechos, haced la prueba con alguno de los dígitos,
simplemente, para aseguraros de que entendéis la idea
Ahora solo queda escribir el programa que dibuja los números en el display, así que vamos a
ello.
La primera idea que se nos ocurre a todos es hacer una función por cada digito que queramos
representar. Por ejemplo podríamos hacer:
void Display7()
digitalWrite(3,1);
digitalWrite(4,1);
digitalWrite(5,0);
…………..
El problema de este enfoque es que hay que hacer 10 funciones como esta y luego escribir en
el programa principal algo que llame a la función correspondiente según el número que
queramos mostrar.
Esta es, de nuevo, la solución de fuerza bruta y no, aquí no permitimos esas cosas, nos
gustan las soluciones elegantes (y porque, además, somos muy vagos y nos cansamos solo
de pensar en hacerlo así).
Por cierto, para ser alguien en la comunidad freaky, hay que hablar de la elegancia de
tu código siempre que surja la ocasión, si queréis ser programadores es muy importante que
empecéis a adoptar las poses del gremio.
Así que vamos a buscar otra solución más elegante. Para empezar, todos los que al ver la
última tabla, con los segmentos que hay que iluminar, hayáis pensado que se parece
sospechosamente a un array de 10 x 8 podéis anotaros un punto por listos.
Porque por supuesto vamos a crear una array con todo eso (No, no tenemos comisiones por
vender los arrays) y veréis que el programa se simplifica mucho.
De nuevo, como con todo en la vida, es mucho más fácil de lo que parece. Vamos a empezar
definiendo el array de dos dimensiones (porque es una tabla, otra cosa son el número de
elementos por dimensión):
byte Digit[10][8] = // Arduino UNO va muy justo de memoria. Por eso lo
{ 1,1,0,1,1,0,1,0 }, // 2
{ 1,1,1,1,0,0,1,0 }, // 3
{ 0,0,1,0,0,1,1,0 }, // 4
{ 1,0,1,1,0,1,1,0 }, // 5
{ 1,0,1,1,1,1,1,0 }, // 6
{ 1,1,1,0,0,0,0,0 }, // 7
{ 1,1,1,1,1,1,1,0 }, // 8
{ 1,1,1,0,0,1,1,0 } // 9
};
Podemos ahora crear una función, que pasándole un número de 0 a 9, elija la fila del array en
función de ese mismo número y busque el array interno correspondiente.
void Display(int N)
digitalWrite(pin , valor) ;
}
Buscamos valor en la tabla cuyo índice principal N apunta al array interno correspondiente. El
for i, va leyendo los 8 elementos del array para escribirlos en el pin correspondiente de salida,
que lo calculamos como i +2, (Porque usamos los pines 2 al 10 para conectar los segmentos
del display y así unos valores de 1 de 0 a 8 se convierten en pines de 2 al 10).
void setup()
pinMode(i, OUTPUT);
void loop()
for ( int k=0 ; k<10 ; k++) // Llama a Display para k de 0 a 9, los digitos
{ Display(k);
delay(1000);
void Display(int N)
}
}
Fijaros que podíamos haber escrito así (El código compacto también puntúa con los colegas):
void Display(int N)
digitalWrite(i+2 , Digit[N][i] ) ;
He aprovechado este ejemplo para insistir con los arrays, porque son muy útiles para resolver
problemas que de otro modo se pueden complicar bastante.
Un problema que a priori, era muy pesado, o complejo, se transforma en una nimiedad
recorriendo la tabla. He visto varios ejemplos por internet de este programa y la mayor parte
de ellos me ha dado dolor de cabeza (Con algunas muy honrosas excepciones).
La cosa ha ido bastante bien con un digito, pero… ¿Y si queremos mostrar 4 dígitos? ¿Vamos
a multiplicar 8 cables x 4 dígitos más tensión y GND = 34 pines?
En la próxima sesión veremos cómo manejar un display de 4 dígitos, porque para eso los
venden hechos, y no es necesario volvernos locos enchufando no sé cuántos cables para
hacer un reloj.
Como esta sesión ya es suficientemente densa, no incluiré el ejemplo de conectar un
único digito a un shift register para gobernarlo, pero os recomiendo que lo hagáis como
práctica, y si es preciso, colgamos el ejemplo en la página web.
RESUMEN DE LA SESIÓN
o Hemos presentado los displays LED de 7 segmentos y un dígito.
o Son LEDs completamente normales, solo que en forma de líneas y no de
puntos.
o Algunas de las funciones que hemos definido nos seran útiles en los siguientes
capítulos para gobernar mas display
MATERIAL REQUERIDO.
Arduino Uno o similar. Esta sesión acepta cualquier ot
Una Protoboard .
Cables de protoboard
4 x resistencias de 330Ω.
4 x Transistor 2N2222
En la sesión anterior, la cosa ha ido bastante bien con un dígito, pero ahora que le hemos
cogido el puntillo, queremos poder usar 4 dígitos para, digamos dar la hora.
La cosa va por ahí, pero para eso habría que poner unas memorias y latch como en el shift
register y alguien dijo: No, no, más barato aun. Y un ingeniero creativo dijo: pues si hacemos
que esos 4 pines para indicar la posición vayan a tierra pasando por un transistor cada uno
podemos hacer que se ilumine solo uno cada vez. Y si hacemos el ciclo lo bastante rápido,
pasando de uno a otro, ni se enteran. Ni que decir tiene que el ingeniero creativo pasó a jefe
de proyectos.
Y eso es todo. Simplemente seguimos utilizando 8 pines para iluminar los segmentos, pero
solo hay uno activo en un momento concreto. Y haciendo un ciclo rápido, iluminamos uno
cada vez rápidamente y pasamos al siguiente y luego al siguiente y problema resuelto.
Así que lo primero que vamos a hacer es conectar los 8 segmentos del display a los pines del
2 al 10 de nuestro Arduino, para probarlos. Conectad GND al cátodo del primer digito con una
resistencia y comprobad las conexiones.
CONEXIONES INICIALES
De nuevo, tengo que insistiros en que leáis la hoja de características de vuestro display y no
os fieis del diagrama de abajo, porque el vuestro puede ser distinto. Mirad el código de vuestro
display y bajaros la data sheet de Internet. Leedla, aunque solo sea para comprobar si
vuestros pines coinciden con el diagrama que os pongo debajo (Que es del manual de
Parallax).
Este diagrama es para el display que tengo entre manos (5461AS) y el vuestro no tiene por
qué usar los mismos pines ( Aunque la probabilidad de que sea así es alta, y en caso negativo
tampoco es grave porque basta con que conectéis los pines correctos al mismo orden de
vuestro Arduino).
Lo que yo he hecho (porque mi display solo tiene 12 pines), es conectar los 8 segmentos y
enviar el cátodo a masa mediante una resistencia, para probar el primer digito y ya me
preocupare del resto luego. Es imperativo que os aseguréis de conectar correctamente los
segmentos antes que nada.
Podeís correr un programa simple como este, para comprobar la conexión de los segmentos,
que los va encendiendo y apagando en secuencia: Prog_31_1
void setup()
pinMode( i, OUTPUT);
void loop()
{ digitalWrite( j, HIGH);
delay(300);
digitalWrite( j, LOW) ;
delay(300);
DIAGRAMA DE CONEXIÓN
Ahora que estamos seguros de que nuestros 7 segmentos están enchufados donde
corresponde viene la cuestión de utilizar 4 pines de Arduino para elegir cual se ilumina.
Nuestra primera idea sería conectar esas salidas del display, 12, 9, 8 y 6 a 4 pines de nuestro
Arduino. Si mantenemos esos pines en HIGH excepto el que queremos iluminar, que
ponemos en LOW, parece que cerraría el circuito a GND y ya está.
Pero no. El consumo típico de cada segmento, (como de cada LED) es de entre 15 y 20mA.
Como son 8 segmentos cuando escribamos el 8 más el punto, el consumo será de 8 x 15 =
120 mA y pudiendo llegar a 160mA. Muy por encima de los 40 mA que un pin de nuestro
Arduino puede suministrar o drenar, así que mejor que pensemos otra cosa antes de que
huela a quemado.
Y la solución, queridos amigos, es, naturalmente, utilizar un transistor para permitir cerrar el
circuito de cada dígito.
El 2N2222 es ya un viejo conocido (No os olvidéis de la resistencia de la base, es
imprescindible), poniendo a LOW los pines 9, 10, 11, y 12 los transistores están al corte, y
ningún digito puede iluminarse. Si ponemos tensión en el pin 12, su transistor se satura y
permite la salida a Ground del primer digito que encenderá los segmentos que le indiquen los
pines 2 al 8.
Ahora ponemos en LOW el pin 12 y en HIGH el 11. Los valores de los segmentos iluminaran
ahora el segundo digito. Y luego el terceo y el cuarto. El único trucos es hacerlo rápido, y eso
se le da muy bien a nuestro Duino.
EL PROGRAMA DE CONTROL
Vamos a reaprovechar la función Display () que escribimos en la sesión anterior para dibujar
los dígitos en el display, que si recordáis se apoyaba en un array para saber que segmentos
encender: Prog_31_2
byte Digit[10][8] =
{
{ 1,1,1,1,1,1,0,0 }, // 0
{ 0,1,1,0,0,0,0,0 }, // 1
{ 1,1,0,1,1,0,1,0 }, // 2
{ 1,1,1,1,0,0,1,0 }, // 3
{ 0,0,1,0,0,1,1,0 }, // 4
{ 1,0,1,1,0,1,1,0 }, // 5
{ 1,0,1,1,1,1,1,0 }, // 6
{ 1,1,1,0,0,0,0,0 }, // 7
{ 1,1,1,1,1,1,1,0 }, // 8
{ 1,1,1,0,0,1,1,0 } // 9
};
void setup()
pinMode(i, OUTPUT);
Pero vamos a extender Display para que escriba un digito en la posición que queramos,
donde la posición 0 son las unidades, 1 las decenas, 2 las centenas y 3 los miles.
{
digitalWrite(10,LOW);
digitalWrite(11,LOW);
digitalWrite(12,LOW);
digitalWrite(i+2 , Digit[N][i]) ;
Como veis, es la misma función en la parte de iluminar segmentos, pero hemos hecho que al
entrar los apague todos, luego levanta los pines que queremos y solo luego activamos el
transistor de salida de la posición que queremos, lo que hace que ilumine la posición pedida
en pos.
Calculamos el pin de salida como pos + 9, porque a las posiciones 0, 1, 2, 3 les corresponden
los pines 9, 10, 11, 12, también aquí, la elección del orden de los pines juega a nuestro favor.
Una vez que tenemos resuelto dibujar el número buscado en la posición elegida tenemos que
escribir otra función que descomponga un número dado en unidades, decenas, centenas y
miles.
Display(3 , Digit3);
Display(2 , Digit2);
Display(1 , Digit1);
Display(0 , Digit0);
Por si acaso alguien faltó el día que hablamos de las operaciones con enteros haremos un
caso concreto. Supongamos un número como 2.386. ¿Cómo obtenemos sus dígitos
separados?
2386 % 100 = 86 Ahora hacemos la división entera por 10 o sea 8,los decimales
se tiran
Por ultimo para la función principal, empecemos pidiéndole que cuente décimas de segundo
(La cuenta atrás la hacemos, luego)
void loop()
¿Creías que iba a ser difícil? Pues lo siento. Es así de sencillo. Ya conocíamos millis(), el reloj
interno de nuestro Duino en milisegundos. Pues haciendo la división entera (otra vez) nos da
un contador de hasta 999 segundos.
¿Y si queremos hacer una cuenta atrás? Venga pensad un momento antes de mirar la
solución.
Muy fácil también. Como nuestro contador va hasta 9999 basta con restarle a esto el
cálculo de arriba, o simplemente poner en lugar del 9999 la cifra desde la que deseamos
descontar.
Más interesante, y práctico supone convertir esa cifra a minutos y segundos sexagesimales
para hacer un reloj.
void loop()
CalculaDigitos(k) ;
De nuevo a partir del reloj interno de nuestro Duino, millis(),(que cuenta en milisegundos por lo
que lo dividimos por mil para pasarlo a segundos), calculamos los segundos y minutos, para
luego llamar a CalculaDigitos() que espera un numero centesimal y por eso multiplicamos por
100 los minutos para que cuadre el display.
Os dejo como ejercicio que hagáis un reloj que efectúa una cuenta atrás desde un número
dado.
El circuito que hemos visto, es el correcto para garantizar que el display tenga una
vida larga y pacífica, de hecho es el que recomienda el fabricante.En internet encontrareis
bastantes ejemplos en los que, en vez de transistores usan resistencias de un cierto valor
para limitar la intensidad en los controles de salida. He preferido no hacerlo así, a sabiendas
de que el montaje que os he propuesto es bastante más pesado.
ALGUNOS COMENTARIOS FINALES
Cablear este montaje es un asco y si fuera un circuito comercial seria caro (Porque aunque los
componentes sean baratos, montarlos cuesta pasta, más que los componentes en este caso).
Así que en la práctica os desaconsejo que uséis estos displays de por si, en vuestros
proyectos. Hay demasiados cables y siempre hay alguno dando guerra. Lo normal es usar un
display tipo I2C o similar que lleven un circuito de control interno, o bien usar un integrado de
control BCD como el Max7219 (Volveremos a hablar del asunto en un futuro próximo).
Y ¿Por qué a pesar de todo hemos hecho esta práctica? Pues porque es importante que
entendáis cómo funcionan las cosas y el concepto de multiplexado es uno de los claves
básicas para entender la electrónica digital,
Por cierto, ¿Os habéis dado cuenta de que, al hacer un multiplexado de los dígitos basado en
que nuestro Duino recorra el ciclo completo en menos tiempo de lo que nuestro ojo percibe,
cualquier uso de un delay() dará al traste con el invento?. Meted uno por donde queráis y ved
lo que ocurre.
Aunque hasta ahora hemos usado y abusado de los delays, son algo poco práctico y
potencialmente muy peligroso a medida que los proyectos que presentemos se vayan
complicando, y este es uno de esos casos. Ya hablaremos de las soluciones.
RESUMEN DE LA SESIÓN
MATERIAL REQUERIDO.
Una Protoboard.
Cables de protoboard
En la sesión anterior vimos cómo usar un display de 4 dígitos montando nosotros toda la
electrónica. Vimos que la cosa funcionaba bastante bien, y vimos que no se notaba el truco de
multiplexar los dígitos, porque en general los Duinos son lo suficientemente rápidos para que
no lo percibamos.
Pero lo que desde luego si que percibimos, fue lo pesado que es montar aquel circuito en una
protoboard, por la cantidad de cables que supone conectar y por lo fácil que es equivocarse o
soltar uno inadvertidamente.
En la práctica ese sistema solo es para los estudiantes (por aquello de que aprendan
sufriendo), pero en la realidad es mucho mejor utilizar un display con sistema anexo de
control, que son un poco más caros, pero no tienen ninguno de los problemas que vimos.
En realidad son tan sencillos que no suelen tener más allá de 4 pines y se controlan por
software directamente.
Hoy os voy a presentar un display chiquitín, pero muy resultón, que tengo rodando por la
mesa ya un tiempo y que me encanta.
Es un display de Catalex, un fabricante chino, de 4 dígitos que se gobierna con esos 4 pines
que decíamos (y dos son Vcc y GND), tiene una librería muy simple y funciona tanto a 5V como
a 3,3V.
Espero recibir en breve uno de estos displays en versión I2C (Aaún no hemos hablado
del I2C, pero todo llegará) y entonces haremos un sesión sobre el tema..
DIAGRAMA DE CONEXIÓN
Conectamos tensión y Ground y solo nos quedan 2 hilos: DIO o Data Input Output y Clock,
que conectamos a los pinos 2 y 3 respectivamente. Y eso es todo.
EL PROGRAMA DE CONTROL
El fabricante del display nos proporciona una librería para que podamos olvidarnos del
multiplexado y demás historias: DigitalTube
La descargamos e instalamos como vimos en la sesión Conociendo los servos, Seleccionad
Programa – Añadir librería e indicadle el fichero que acabáis e descargar y se instalará.
Y en la lista de librerías que sale en el desplegable elegid DigitalTube . Veréis que os incluye
una línea como esta:
#include <TM1637.h>
Que es un nombre impresentable, pero que es lo que hay. Para usarla, definimos un par de
pines para la señal CLK y DIO:
#define CLK 3//pins definitions for TM1637 and can be changed to other ports
#define DIO 2
Ahora tenemos que crear una instancia del tipo TM1637 que llamaremos Display1 y le
pasamos los pines que controlan la comunicación:
TM1637 Display1(CLK,DIO);
Y ahora el fabricante nos pide que le pasemos los dígitos a representar en un array de 4
unsigned byte definidos con un tipo raro int8_t, del que no teneís que preocuparos:
En este caso el display mostrara 3212. Y ahora hay que inicializar la librería y el display
void setup()
Display1.set();
Display1.init() ;
Display1.display(Digits);
Y listo. Por pura vagancia os pongo el programa completo para que apreciéis la diferencia:
#include "TM1637.h"
#define CLK 3
#define DIO 2
TM1637 Display1(CLK,DIO);
void setup()
Display1.set();
Display1.init() ;
void loop()
Display1.display(Digits);
Vamos ahora a hacer el ejemplo del contador de la sesión anterior para que veamos cómo
queda. Vamos a empezar por las definiciones
#include "TM1637.h"
#define CLK 3
#define DIO 2
TM1637 Display1(CLK,DIO);
void setup()
Display1.set();
Display1.init() ;
Vamos a usar un contador para que veamos su valor en el display, así que vamos a necesitar
separar los dígitos del valor y meterlos en el array Digits. Para ello vamos a recuperar la
función CalculaDigitos () de la sesión anterior con alguna pequeña modificación:
Digits[3] = Digit0 ;
Digits[2] = Digit1 ;
Digits[1] = Digit2 ;
Digits[0] = Digit3 ;
Display1.display(Digits);
Calculamos los dígitos y los metemos en el array, después basta con llamar al método display
(en minúsculas) del objeto Display (en mayúsculas). Y para calcular el número:
void loop()
CalculaDigitos(i);
delay(100);
RESUMEN DE LA SESIÓN
MATERIAL REQUERIDO.
Arduino Uno o similar. Esta sesión acepta cualquier ot
Una 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 lunes).
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:
Son baratos.
Están disponibles en varios tamaños y configuraciones.
Son de bajo consumo.
Muy prácticos si te basta con mostrar solo texto (y algunos caracteres especiales).
En esta sesión veremos cómo conectarlos a vuestros Duinos y cómo usarlos para sacar
mensajes al exterior.
ANTES DE EMPEZAR
Aunque, últimamente, estos displays suelen venir con los pines soldados, todavía hay muchos
sitios que cuando compras uno de estos, te lo envían en kit, con los pines aparte para que los
sueldes
En algún momento tendremos que hablar de cómo soldar componentes, pero ese día,
aún no ha llegado.
Lo primero que tienes que saber, es que tienes que soldarlos, y que no vale sujetar los pines
de algún modo más o menos extravagante. Los sueldas y punto. Hazte a la idea. Cualquier
otra solución acabara funcionando mal, o directoramente con el display quemado.
Cuando lo tengas listo, pincha el display en la protoboard, dejando sitio para el resto de
componentes y cables, recuerda que habrá muchos, así que se generoso con el espacio que
dejas.
DIAGRAMA DE CONEXIÓN
Si conectas ahora el cable USB a tu Duino, el LCD debería iluminarse, si no, revisa tus cables
antes de seguir.
Vamos a conectar ahora, el potenciómetro de ajuste. Para ello conecta uno de los extremos
del pot a GND y el otro a 5V. El centro al pin 3 del LCD.
Aprovechemos también para dar tensión al panel LCD, El pin 1 va a GND y el 2 a tensión:
Si todo ha ido bien, ya podemos encender el dispay y probarlo. Conecta el USB a tu Arduino y
veamos. Si vas girando el potenciómetro, en algún momento tienes que ver unos cuadraditos
en la pantalla, en caso contrario revisa las conexiones. No sigas, si no ves esto.
Vamos a conectar ya los pines de datos y control. Sin entrar en muchos detallas, no vamos a
usar todos los pines disponibles, porque no los necesitamos. Solo usaremos dos pines de
control, RS y EN y los 4 pines de datos D7, D6, D5, y D4 . No necesitamos mas por ahora.
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, EN, d4, d5, d6, d7)
Tened cuidado porque los pines que hemos usado, no corresponden a los ejemplos
de Arduino, así que podéis cargarlos, peroaseguraros de cambiar la línea de definición de los
pines, o no correrán.
El resto es sencillo
void setup()
void loop()
Cuidado: Como siempre la primera linea, la superior, es la linea 0 y la segunda la linea
1.
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>
void setup()
void loop()
String s = reloj() ;
lcd.print(s) ;
String reloj()
return (S);
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 fijaros 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).
Fijaros que definimos dentro de la función un string llamado s:
En esta línea no hay que confundir (aunque se escriben exactamente igual), el tipo String para
definir S, con la función String(n) que convierte un numero n en un string de texto para que
pueda mezclar el número de los minutos y los segundos separados por un símbolo de “:”.
Quita los espacios y miro lo que pasa en el cambio de minuto. ¿Cómo lo arreglarías,
sin el truco de poner esos espacios al final? Piensalo.
En la función loop, hemos usado
lcd.print(s) ;
Para sacar el mensaje de texto. Todo lo que ya sabéis de Serial.print() se usa exactamente
igual con esta instrucción. Y por último, tenemos una línea como esta:
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. Aquí os dejo un mini video con el resultado.
La librería LCD, viene con varios ejemplos muy interesantes, que os conviene probar.
Recordad, simplemente, que tenéis que cambiar las definiciones de los pines para que corran.
Sin llegar a tanta sofisticación, es muy interesante que veamos cómo definir algunos
caracteres especiales, porque en la tabla base del LCD, no están incluidas ñ, acentos, grados,
o €. Así que dependiendo del uso que le deis pude seros de interés saber cómo definir tus
propios símbolos.
Vamos a definir un carácter propio, para digamos, el símbolo de grados centígrados, por
ejemplo.
Lo primero que tenéis que saber, es que los caracteres se definen con un array ( si, de nuevo)
de 8×8, como si los dibujarais en una cuadricula de ese tamaño, y rellenando el cuadradito
completo.
byte grado[8] =
0b00010010,
0b00010010,
0b00001100,
0b00000000,
0b00000000,
0b00000000,
0b00000000
};
lcd.createChar(0, euro);
lcd.createChar(1, grado);
Y ahora ya estan disponibles. Tened 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).
#include <LiquidCrystal.h>
byte grado[8] =
0b00001100,
0b00010010,
0b00010010,
0b00001100,
0b00000000,
0b00000000,
0b00000000,
0b00000000
};
void setup()
lcd.createChar(1, grado);
lcd.setCursor(0, 0);
lcd.print("Estamos a 25");
lcd.write(1);
void loop()
Por último, y para cerrar ya la sesión (Todo llega en la vida), Deciros que de nuevo, hemos montado
este display a la brava, conectando un buen número de cables.
Esto solo se puede justificar, porque en su día piqué y me compre un display de estos ( en realidad,
dos. Tengo otro de 16×4), pero si vais a compraros un LCD, por Dios, compraros uno que sea I2C o
algo así, vuestra salud mental mejorará mucho y solo usa 4 cables.
RESUMEN DE LA SESIÓN