El objetivo de la lección es comprender la comunicación vía puerto serie, para lo cual hay que utilizar la librería Serial. Veremos también operaciones con enteros, los tipos String y char. Entenderemos cómo operar con Strings y la instrucción while.
Imagen | Descripción |
---|---|
![]() |
PC |
![]() |
Cable USB |
![]() |
Arduino UNO o compatible |
Más tarde o más temprano, necesitaremos 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 debemos 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 eso podemos transmitir información digital. Ahora solo nos falta pactar dos cosas entre quien envía y quien recibe:
El código común que usaremos con Arduino se llama código ASCII y es estándar en todos los PCs. Es una manera de codificar las letras mediante números que representan estos caracteres. Recordemos que solo podemos transmitir unos y ceros.
Así por ejemplo la letra A se representa por el número 65, la B el 66, C el 67… Prácticamente todos los PCs actuales utilizan este código y esto incluye a Windows, Mac y Linux (y por eso podemos leer emails enviados desde diferentes plataformas), pero es importante comprender que este es uno más entre varios códigos de caracteres posibles (EBCDIC por ejemplo).
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 diferente de la velocidad de lectura, el mensaje final será irreconocible.
Buena parte de los errores de comunicación serie programando con Arduino se deben a una diferencia de velocidad entre el emisor y el receptor.
Esta velocidad se mide en bits por segundo (baudios) y veremos que Arduino soporta diferentes velocidades de comunicación serie.
Arduino dispone de una librería serie incluida llamada Serial, que nos permite enviar información al PC y para usarla simplemente debemos pedirle en nuestro setup() que la incluya. La instrucción que se encarga es:
Serial.begin( velocidad ) ;
La velocidad es un valor entre 300 y 115.200 bits por segundo. Y suele ser costumbre establecerla en 9600 (el valor por defecto) pero no hay ninguna razón para esto 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 serie a 9600 bits por segundo
}
void loop()
{
int i = 54 ;
Serial.println( i );
}
El Serial.println()
enviará 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 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 cuales hemos programado nuestra puerto serie, y si lo desplegáis, veréis las diferentes velocidades aceptables para Arduino.
Ahora que sabemos enviar información y resultados al PC, veremos 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):
En C++ debemos 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 dividido 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 qué operaciones se realizarán antes y cuáles después en función de su rango. Para los que se inician en C++ no es fácil saber qué 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 otra manera, 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 número 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 diferentes 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 una manera diferente el Serial.println()
pasándole una String de texto entre comillas. Serial.print()
envía el texto (entre comillas) que le ponemos pero no hace 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("Buen ") ;
Serial.print("Día ") ;
Serial.println("a todos.") ;
}
C++ dispone de una clase de variables llamadas Strings, capaces de contener textos. Podemos operar con ellas simplemente definiéndolas como cualquier otra clase de C++:
void loop()
{
int resultado = 25 ;
String s = " El resultado es: " ; // Nota que la S de String es mayúsc.
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).
Hasta ahora solo hemos enviado mensajes desde Arduino hacia el PC, ¿Pero cómo 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 teclean en la consola (cuando presionamos intro) y si es un texto, lo interpreta como cero.
Hemos utilizado otra función Serial.available()
que es un booleano. Conviene por costumbre comprobar que antes de leer el puerto serie hay algo que nos han enviado. Si hay, available()
es True y en caso contrario es False.
Para leer un String del puerto serie debemos complicarnos un poco más y hablar del tipo char.
Uno de los mayores quebraderos de cabeza al iniciarse en C++ es comprender la diferencia, antiintuitiva, 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 parece lo mismo para C++ son muy diferentes.
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 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 = "" ; //Lo borramos 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 vuelve falsa y sale del while.
Por otro lado, comprobamos si hay algo disponible en la puerta serie y en este caso montamos el mensaje leyendo un char cada vez y sumándolo a mensaje para construir un String que podamos imprimir al salir.