Objetivos
Material requerido.
Kit inicio UNO | |
Kit Inicio Mega |
Comunicación Serie con el mundo exterior
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.
Estableciendo la comunicación Serie
.
Arduino dispone de una librería serie incluida llamada Serial, que nos permite envía información al PC y para usarla simplemente tenemos que pedirle en nuestro setup() que la incluya. La instrucción que se encarga es:
Serial.begin( velocidad ) ;
- Nótese que Serial tiene la S mayú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:
int LED = 10 ; int boton = 6 ; bool estado = false ; void setup() { Serial.begin(9600) ; // Inicializa el Puerto seria 9600 bits por segundo } void loop() { int i = 54 ; Serial.println( i ); }
El println() enviara el valor de i al puerto serie de Arduino (repetidamente). Para leerlo en nuestro PC necesitamos un monitor de puerto serie. El IDE de Arduino incluye uno muy sencillo, pero suficiente que se invoca con el botón del monitor:
Necesitamos además asegurarnos de que la velocidad de conexión es la misma en ambos extremos. Fíjate en la parte inferior derecha del monitor serie:
Normalmente la velocidad por defecto son los 9600 bits por segundo o baudios en los que hemos programado nuestra puerta serie, y si lo desplegáis, veréis las diferentes velocidades aceptables para Arduino.
- Estrictamente hablando, bits por segundo y baudios no son exactamente lo mismo salvo bajo ciertas condiciones particulares que en Arduino se cumplen, por lo que aquí podemos usarlos como sinónimos.
- En el mundo Arduino parece haber un acuerdo de usar velocidades bajas como 9600 en lugar de más altas como 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:
Operación | Resultado | Comentario |
---|---|---|
int i = 4 * 2 | resultado = 8 | |
int i = 4 * 2 / 3 | resultado = 2 | Porque desprecia los decimales al ser entero |
int i = 14 % 3 | resultado = 2 | El resto de 14 entre 3 |
Int i = 2 + 8 / 2 | resultado = 6 | Calcula primero la división. |
Int i = (2+8) / 2 | resultado = 5 | El paréntesis fuerza a que se realice primero la suma |
Dada una expresión, la precedencia de operadores indica que operaciones se realizaran antes y cuales después en función de su rango. Para los que se inician en C++ no es fácil saber que operadores tienen preferencia, por lo que es más seguro que ante la duda uséis paréntesis.
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.
Podríamos escribir un programa como este:
void setup() { Serial.begin(9600) ; // Inicializa el Puerto serie } void loop() { int i = 27 ; //El número en cuestión if ( i % 2 == 0) Serial.println("Es par.") ; else Serial.println("Es impar"); }
Dando a i distintos valores podemos comprobar cómo funciona el operador resto %. Volveremos sobre esto cuando veamos algunos ejemplos de cómo calcular números primos.
En este programa hemos usado de un modo diferente el Serial.println() pasándole una String de texto 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() { Serial.begin(9600) ; // Inicializa el Puerto serie } void loop() { Serial.print("Buenos ") ; Serial.print("Dias ") ; Serial.println("a todos.") ; }
C++ dispone de un tipo de variables llamadas Strings, capaces de contener textos. Podemos operar con ellas simplemente definiéndolas como cualquier otro tipo de C++:
void loop() { int resultado = 25 ; String s = “ El resultado es: ” ; // Nótese que la S de string es mayúscula. Serial.print( s) ; Serial.println( resultado); }
Un tipo String se define simplemente poniendo entre comillas dobles un texto, y se puede operar con ellas de una forma similar a como operamos con enteros. Prueba:
void loop() { String a = "hola " ; String b = "a todos." ; Serial.println( a + b); }
Y también podemos construir un String sobre la marcha así:
void loop() { int resultado = 25 ; String s = "El resultado es: " ; Serial.println( s + String( resultado )); }
Donde imprimimos el resultado de concatenar s String, y la conversión de un int a String (El operador + añade un String al final de otro).
Recibiendo mensajes a través del puerto Serie
.
Hasta ahora solo hemos enviado mensajes desde Arduino hacia el PC, ¿Pero como recibimos mensajes en Arduino?
En primer lugar disponemos de una función llamada Serial.parseInt() que nos entrega lo que se escribe en el monitor serie convertido a entero:
void loop() { if (Serial.available() > 0) { int x = Serial.parseInt(); Serial.println ( x) ; } }
Este programa simplemente recibe en x los números que nos tecleen en la consola (cuando pulsemos intro) y si es un texto, lo interpreta como cero.
Hemos utilizado otra función de Serial : Available() que es un booleano. Conviene por costumbre comprobar que antes de leer el puerto serie hay algo que nos han enviado. Si lo hay Available() es True y en caso contrario es False.
Para leer un String del puerto serie tenemos que complicarnos un poco más y hablar del tipo char.
Uno de de las mayores quebradero de cabeza al iniciarse en C++ es comprender la diferencia, anti-intuitiva, entre char y String. char es un tipo que representa un único carácter y se define con comillas simples, a diferencia de String que necesita comillas dobles:
char c = ‘a’ ; String s =”a” ;
Aunque parezca lo mismo para C++ son muy distintos.
Para leer una cadena desde el puerto serie necesitamos leer un carácter cada vez y después montar un String a partir de ellos, pero antes, asegúrate de seleccionar ambos NL & CR en la parte inferior del monitor serie, para garantizar que se envía el carácter de fin de línea:
Un programa para leer la consola sería algo así:
void setup() { Serial.begin(9600); } void loop () { char c = ' ' ; String mensaje ="" ; if (Serial.available()) //Comprobamos si hay algo esperando { while( c != '\n') //Si lo hay, lo leemos hasta el intro { mensaje = mensaje + c ; // Añadimos lo leído al mensaje c = Serial.read(); //Leer 1 carácter delay(25); } Serial.println( mensaje); //Al salir imprimir el mensaje mensaje = "" ; //Bórralo para la próxima vez } }
Aquí usamos otra instrucción de C++ llamada while. Es similar a if, Ejecuta repetidamente el bloque que le sigue mientras se cumpla la condición que le pasamos entre paréntesis:
while ( condición) { ……… }
Cuando lee el 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.