Serial Arduino: Introducción

2
5371
Serial comunicacion
Serial comunicacion
Serial Arduino pretende introducirnos a las nociones básicas de comunicación Serial de los dispositivos y en especial al tratamiento de caracteres y cadenas  en cuanto a su manipilación por parte del Objeto Serial  en esta plataforma. Si bien las funciones básicas como Print heredan del lenguaje «C», tiene ciertas particularidades a tener en cuenta.

La interface básica de comunicación de Arduino con el mundo exterior se lleva a cabo mediante la histórica comunicación Serial asincrónica, que fue y es una de las formas de comunicación más básica  de los últimos tiempos, empleadas en diversos ámbitos. Las comunicaciones seriales han evolucionado, no obstante la forma asincrónica es tan simple y eficaz que se sigue utilizando actualmente. Antiguamente la comunicación serie asincrónica estaba ligada a una interface física del tipo RS232 que es un estándar de interface de comunicación a nivel uno. Las comunicaciones en general entre equipos y dispositivos se analizan mediante “Capas” de comunicación. Estos conceptos se iniciaron con el modelo  de comunicación OSI (Open System InterConnection) Modelo OSI

RS232 definía una interface física, es decir no solo las características eléctricas sino también el conector utilizado en estas interfaces como lo eran el DB09 y el DB25. La comunicación se asociaba a una serie de reglas o “Handshaking” entre dos dispositivos, y además definía si la interface era del tipo DTE (Data Terminal Equipment) o DCE (Data Comunication Equipment), lo cual, a su vez determinaba si el cable debía ser “cruzado o derecho”.  En cada conector se  definían determinados pines que realizaban funciones de Control y de comunicación.

Actualmente estos conceptos se siguen utilizando, la diferencia es que tales conectores y reglas de comunicación se han “Protocolizado”  y resumido a pocos cables en las interface, siendo actualmente mucho más potente el software que el hardware. Antes teníamos por ejemplo el DB25 O DB09 que tenía cables con funciones específicas de protocolo o reglas de intercambio es decir Protocolo de Hardware, pero actualmente se suprimen cables y se habla de Protocolo a nivel de Software, y es allí donde el Modelo OSI cobra sentido dado que se habla de capas y funciones en cada una de ellas.

Cable Serial entre dispositivos
Cable Serial entre dispositivos

En la figura se observa un conexionado típico entre dos equipos con todas sus señales. Hoy en día solo utilizamos un par de cables de comunicación más dos de alimentación como lo es el puerto USB de Arduino. En resumen lo importante es que la comunicación Serial asincrónica utilizada por Arduino hereda de interfaces de Hardware anteriores en tecnología pero no por eso menos efectiva.

La comunicación Serial  en Arduino es asincrónica, esto es no hay un Clock de sincronización de los datos como puede ser la comunicación SPI utilizados actualmente. La ventaja de no usar Clock es la simplicidad, la desventaja es la velocidad máxima de comunicación, entre otras.

Señal Asincrónica básica
Señal Asincrónica básica

La figura muestra una comunicación Serial asincrónica Unipolar (un solo nivel de tensión), los 8 bits de datos (1 Byte) se delimitan con un bit de Start que es un flanco de bajada de la línea de 1 a 0 y un bit de paro o Stop que es la línea en estado alto. El noveno bit ya no se usa y es el de paridad. Es común en las configuraciones de puerto serial o conversores de USB a Serial utilizar la palabra 8N1 que significa 8 bits de datos, No paridad y 1 Bit de Stop.

Otro parámetro es la velocidad de comunicación que va desde 1200 bps hasta 64kbps. Una velocidad de por ejemplo 9600 Baudios significa que el tiempo de duración de cada bit de datos es de 1/9600 = 104 Micro segundos(us).  El receptor lo que debe hacer es escuchar el flanco de bajada del Start y desde allí sabiendo la velocidad de comunicación en Baudios , debe muestrear la línea de recepción en la mitad de cada bit.

Después del flanco de bajada del Start, 1.5 Bits (156us) aparecerá centrado el primer bit , y a partir de este , cada 104 us aparecerán centrados el resto de los bits. Si cuenta hasta 8 en cada muestreo, el receptor sabrá que ya dispone de los 8 bits y después de 156 us desde el último bit número 8,  sabe que debe esperar el Stop y así validar el byte recibido. El receptor genera su propia señal de muestreo a partir del flanco de bajada del Start. El problema de este esquema sencillo es que bit a bit nunca se muestrea al centro y existe un corrimiento hacia la derecha que va empeorando con la velocidad, la cantidad de bits y la distancia entre los equipos, notar que si el receptor se corre en más de ½ bit estará perdiendo bits y habrá errores. Esta es la razón por la cual en todas las comunicaciones de redes y equipos de radio frecuencia se tiende a utilizar protocolos sincrónicos, es decir que el transmisor no solo envía al receptor la línea de datos sino también el Clock de sincronización  con la misma cadencia de envío para evitar el corrimiento.

Arduino utiliza el Serial asincrónico para comunicarse vía puerto USB. Serial USB es un protocolo serie universal para altas velocidades y donde el sincronismo se logra con la codificación de los bits en forma Polar (2 niveles de tensión +D y –D), esto es  la forma de los pulsos de cada bit, vale decir, no hay una señal dedicada de sincronismo, sino que la forma del pulso de cada bit se utiliza para sincronización. En particular USB utiliza el algoritmo de codificación NRZI («Non Return to Zero Inverted»). La señal de bits es del tipo diferencial.  Un bit=0  se codifica con el mismo nivel que el bit anterior, si el bit=1 este se codifica con el nivel opuesto al bit anterior:

Codificación Serial NRZI
Codificación Serial NRZI

En el esquema Serial asincrónico Unipolar observamos que cada bit se detecta de manera Absoluta, es decir si muestreamos un nivel alto será un 1, caso contrario será un cero. Con NRZI un bit=0 se representa por la ausencia de transmisión ya que mantiene el nivel del estado anterior y un bit=1 se representa como un cambio de bajo a alto o alto a bajo al principio del intervalo. La desventaja de este método es que una cadencia seguidas de bit=0 no provoca flancos en la señal afectando  la sincronización con el receptor.

El hecho que los niveles sean diferenciales (+D y –D caso USB) asegura inmunidad al ruido ya que como el mismo actúa sobre los dos niveles, al hacer la diferencia, siempre permanecerá sin cambio. Las tensiones absolutas no identifican al bit, sino que las transiciones son las que permiten saber cuáles son dichos bits, esa es la diferencia principal de la codificación, a su vez los flancos se utilizan para la sincronización.

Serial Arduino

La placa de Arduino posee un chip que varía según el fabricante y que hace la función de conversión del protocolo USB al Serial asincrónico como por ejemplo el FTDI232. Del lado de nuestra computadora personal habrá que instalar los drivers para que esta conversión se lleve a cabo. Programas como Java, C#, C, etc permiten, a partir de sus entornos de programación emular puertos seriales llamados COM virtuales, es decir desde estos lenguajes vemos al puerto USB como un Serial 8N1 configurable, el resto lo hace el driver. Es este sitio existen muchas aplicaciones realizadas donde se habla de este tema .

https://panamahitek.com/10-cosas-que-necesitas-saber-para-utilizar-arduino-desde-java/ https://panamahitek.com/comunicacion-serial-con-arduino/

La idea es introducir el funcionamiento e interpretación básica  del Serial de  Arduino en lo que hace al  tratamiento de caracteres, variables, código ASCII y cadenas.

Código ASCII

Este código también heredado de los viejos terminales de consola pero que dada su utilidad se va a seguir utilizando por largo rato. Simplemente es una codificación de números y letras para poder ser representados de manera estándar en todo el mundo. Cuando una terminal de datos antigua se comunicaba lo hacía mediante símbolos de letras y números de la misma manera que hacemos uso de nuestro idioma. Cuando decimos “Hola” todos entendemos perfectamente, pero si fuese una terminal deberíamos indicarle que cosa transmitir electrónicamente en lugar de cada letra y allí es donde juega un papel importante el código ASCII.

La información electrónica se codifica en sistema Binario o base 2, solo existe el 0 y el 1. En el sistema decimal la base es 10, probablemente porque el hombre aprendió a contar en base a los 10 dedos de sus manos. Sin extendernos mucho en este tema, existen otros códigos como el Hexadecimal base 16 y el Octal base 8. Los más usados son el binario y Hexadecimal en el mundo de los microcontroladores.

Sistemas de codificación base
Sistemas de codificación base

Si observamos el código ASCII, cada símbolo está codificado con 1 byte, es decir 8 bits, esto quiere decir que podremos codificar 256 símbolos. Cualquier símbolo tendrá su representación en Binario, decimal, Octal y Hexadecimal.

Letra DEC OCT BIN HEXA
H 72 110 00101010 78
@ 64 100 00100000 40

No obstante para el procesamiento electrónico siempre son binarios, son bits, lo que cambia es cómo vamos a interpretar la información y como codificar esos bits de información. Por ejemplo el símbolo @ es bien conocido por todos y al enviarse por medios electrónicos viajará su código en binario, en este caso 00100000, independientemente del resto de los códigos.

Las terminales, como el monitor Serial de Arduino que siempre lo trabajamos a 9600 baudios (por default),  cuando reciba este binario lo va a presentar como su código ASCII, @, porque el señor ASCII así lo dispuso. Si yo quisiera podría inventar mi propio código de símbolos pero seguramente no sería nunca un estándar. La terminal o monitor del Arduino siempre va a interpretar el código ASCII.

Cadenas y caracteres

Es muy común utilizar cadenas y caracteres, la diferencia es que lo que comúnmente nosotros entendemos por una letra, en informática es un carácter, y lo que entendemos por una oración es una cadena o variable alfanumérica. Así una cadena es un conjunto de caracteres. El carácter o variable tipo char se codifica con las comillas simples , ejemplo ‘@’ y lo que es cadena o variable tipo String lo codificamos con comillas dobles , ejemplo “Hola”. Lo importante aquí es que cualquier dispositivo debe saber dónde termina la cadena y para ello en la codificación, el compilador que estemos usando va a agregar un carácter de fin de cadena ‘\0’ invisible para nosotros pero está.

La esencia de todo esto (lo importante de comprender):

Serial.print(var) y Serial.println(var) Estas funciones imprimen cualquier tipo de (var) indicada pero en el caso especial que sea un número, lo toma siempre como decimal con 2 decimales.

Va a tomar la variable (var) y la va a transmitir en código ASCII carácter a carácter.

  • Serial.print (45)

Toma el ASCII (4)=decimal 52 y el ASCII (5)=decimal 53 y los transmite uno a uno . Para probar esto utilice una aplicación de consola Terminal.exe para poder analizar mas en profundidad

Serial.print(45)
Serial.print(45)

El 34 y el 35 son los Hexadecimales del 52 y 53. Podemos observar que se transmiten el 52 y el 53. También dijimos que en el fondo la comunicación es electrónica BINARIA, es decir lo que realmente transmite es el 52(binario)= 00110100 y luego el 53(binario)=00110101

  • Serial.println(45)

Va a agregar a lo ya visto dos caracteres adicionales que son ‘\r’ Carriage Return ASCII (13) o retorno de carro y ‘\n’ new line ASCII(10) o nueva línea.

Serial.println(45)
Serial.println(45)

Observamos nuevamente el 52-53-13-10 que son los ASCII de 45  con retorno + nueva línea, y en binario (lo que realmente se transmite): 00110100-00110101-00001101-00001010. La otra columna que se ve 34-35-0D-0A son los ASCII pero en Hexadecimal. Si comprendimos esto podemos entender ahora que pasa si imprimimos una cadena.

  • Serial.println(“HOLA”);
Serial.println("HOLA")
Serial.println(«HOLA»)

72 ASCII decimal, 48 ASCII Hexa , 01001000 Binario de 72      ‘H’ 79 ASCII decimal, 4F ASCII Hexa,  01001111 Binario de 79        ‘O’ 76 ASCII decimal, 4C ASCII Hexa,  01001100 Binario de 76       ‘L’ 65 ASCII decimal, 41 ASCII Hexa,  01000001 Binario de 65       ‘A’ 13 ASCII decimal, 0D Ascii Hexa, 00001101  Binario de Carriage R. CR 10 Ascii decimal, 0A Ascii Hexa, 00001010  Binario de Line Feed LF

  • Serial.println(‘H’) 

Intentamos aquí transmitir un simple carácter y observamos lo esperable, el ASCII decimal de ‘H’ seguidos por CR y LF 72-13-10(decimal)    ,  48- 0D- 0A (Hexadecimal)

Serial.println('H')
Serial.println(‘H’)

Veamos ahora que pasa si intentamos hacer lo siguiente:

  • Serial.Print(‘11’)  

Obviamente 11 no es un carácter , la expresión correcta seria “11” como cadena, pero Arduino lo compila igual y lo que imprime es:

12593, parece absurdo pero no lo es . Si vemos el ASCII decimal del carácter ‘1’ es el 49 y su binario 00110001. Como Arduino trata  lo que está entre comillas como caracteres, va a intentar averiguar su ASCII , que es un valor entero, pero como cada carácter dijimos que es de 1 Byte, va a necesitar 2 Bytes , uno para cada 49, entonces armará una variable de 2 Bytes (16 bits) de la siguiente manera:

4949 = 00110001 00110001 = 12593 luego va a tomar cada digito y transmitirá su ASCII.

Serial.println('11')
Serial.println(’11’)

¡¡Increible… pero cierto Serial.println(var, format)  . Esta función toma la variable, la formatea a cualquiera de los 4 permitidos BIN(binario),HEX(hexadecimal),OCT(octal),DEC(decimal por default) y luego transmite los ASCII respectivos ya formateados.

  • Serial.println(65,BIN)  

Toma el 65 y lo pasa a binario 01000001 y luego transmite los ASCII decimales del 1-0-0-0-0-0-1 49-48-48-48-48-48-49-13-10 , la excepción es que el “0” más significativo no lo incluye debido a que el binario puede entenderse con o sin ese cero. Mostrará 1000001 en el monitor.

Serial.println(65,BIN)
Serial.println(65,BIN)

Serial.write (var)

En esta función el tema cambia, toma la variable var y la transmite como 1 byte de 8 bits en  BINARIO tal cual es, NO ASCII.

  • Serial.write(123)

El binario de 133 = 01111011 , como el monitor serial solo interpreta ASCII , cualquier terminal trabaja así, debe recibir el 123 y presentar el ASCII del decimal 133 = ‘} 

Serial.write(123)
Serial.write(123)

Dijimos que con 1 Byte puedo representar hasta 256 símbolos .Pero que pasa si var>255

  • Int var=9766;Serial.write(var)

Como solo transmite 1 Byte y necesita 2 Bytes, en este caso ya que un Int posee 2 bytes en Arduino, lo primero que hace es pasarlo a binario : 0010011000100110 , el Byte más significativo MSB=00100110 , y el byte menos significativo será LSB=00100110 . Como Write transmite solo y solo un byte , toma siempre el LSB . en este caso 00100110 cuyo decimal es el 38, Luego el monitor recibe el 38 y lo interpreta como dijimos, comoASCII mostrando el ASCII(38)= ‘&’

Int var=9766; Serial.write(var)
Int var=9766;
Serial.write(var)

Conclusión: Write siempre transmite en formato binario el LSB de la variable. Toda transmisión es siempre BINARIA, lo que se modifica es como los diferentes dispositivos la interpretan. En al caso de los monitores de Arduino y cualquier software Serial Terminal siempre van a interpretar ASCII para representar visualmente los mismos.

Char caso especial

Char presenta confusiones en determinados momentos ya que en realidad char admite definir y asignar variables de estas dos maneras: Char A=43; Char B=’+’;   En este caso y a propósito he definido B como el carácter cuyo ASCII decimal es el 43.

Char es en realidad un entero y si decimos char A=43 el compilador entiende que 43 es en realidad el Ascii de un carácter el cual tampoco le interesa. En el caso char B=’+’, la interpretación es obvia, y será el carácter definido. Como Write transmite en formato binario , en el caso A toma el binario de 43 y en el caso B también así que en el monitor veremos lo mismo , el ‘+’.

Serial.write(A); Serial.write(B);
Serial.write(A);
Serial.write(B);

Y por último usando println: char A=43 char B=’+’

  • Serial.println(B);
  • Serial.println(A);

Ambas formas son similares y print transmitirá el ASCII 43 que será intrepretado con ‘+’.

Serial.println(B); Serial.println(A);
Serial.println(B);
Serial.println(A);

Espero que sea de utilidad este análisis, no hemos inventado nada nuevo, solo se pretende conocer un poco mas en detalle como funciona el Serial de Arduino. El tema es extenso puesto que el Serial tiene diversas funciones, que espero poder abordar en un futuro. Gracias  

0 0 votes
Article Rating
Suscríbete
Notify of
guest

2 Comments
newest
oldest most voted
Inline Feedbacks
View all comments
Gustavo
Gustavo
2 years ago

Si lo haces con serialprint cada caracter se transmitirá en ascci, el asscii del 1 , luego del 0….etc
Del otro lado tendrás que hacer la conversión. Otra manera es transmitir en binario con serialwrite

Pablo Largacha
Pablo Largacha
4 years ago

Una consulta, y si quisiera transmitir una variable int cuyo valor es 1023, cómo debería hacerlo?