Comunicación Serie con otro dispositivo

Muy buenas,

En el trabajo utilizamos habitualmente un dispositivo que registra unos parámetros de transmisión de microondas (no procede entrar en detalles de qué son esos parámetros), y actualmente visualizamos los valores a través de una aplicación de PC proporcionada por el propio fabricante.

Para hacer unas pruebas con unos prototipos, quiero que el arduino lea esos parámetros para intentar automatizar algunas acciones, aprovechando que en el manual de instrucciones viene más o menos detallado el protocolo de comunicación serie.

Me surgen algunas dudas para empezar a abordar la rutina de arduino, ya que no he lidiado antes con protocolos de comunicación, y quería ver si alguien me podía echar un cable

La comunicación es serie a través de RS232 y lo llamma SCI (Serial Command Interface).

El Arduino deberá enviar una solicitud al dispositivo, que es un caracter. Se pueden enviar distintas solicitudes, y en función de ésta el dispositivo envía distintos frames de datos en forma de caracteres ASCII.
Cada frame empieza con el carácter ‘$’ seguido de dos caracteres ‘0’. El siguiente carácter corresponde al tipo de frame de datos (hay ter tipos distintos), y a partir de ahí viene la confusión.
Los siguientes bytes contienen la información numérica que interesa (las variables que quiero usar) agrupadas en 4 caracteres cada una.
Si el frame se transmite como cadena de caracteres, me surge la duda de como unir dos caracteres en números. Por ejemplo si leo los caracteres son “ABCD”, para expresarlo como número decimal entiendo que no debo concatenar los números decimales correspondiente a A, B, C y D (65-66-67-68) ya que en ese caso lo máximo que podría representar es 127127127127…(o en la práctica 127999999, lo cual sería más que suficiente en este caso en concreto).

¿Qué función sería la más adecuada para sacar el valor numérico a una cadena de 4 caracteres con Arduino? En el apartado Reference de este sitio web sí que encuentro la función word (x,y) que entiendo que concatena dos bytes consecutivos, pero no encuentro qué función podría usar para concatenar 4 bytes o 2 words en un unsigned long.

¿Existe alguna librería que facilite este tipo de comunicación?

Otra duda que tengo de la comunicación serie es qué ocurre si mientras los dos equipos intentan enviar al mismo tiempo. Es decir, si el Arduino intenta enviar una solicitud mientras el otro dispositivo está enviando datos, ¿se interrumpe la comunicación?

Perdonar por el rollo y gracias de antemano.

saludos

La solución pasa por máquinas de estados y el uso de millis(). La máquina de estado te sirve para saber qué representa el cada byte que recibes. Mientras que millis() te sirve para controlar los timeout.

El timeout más que nada es por si se produce algún corte en la comunicación.

En cuanto a las máquinas de estado, depende mucho de si tienes memoria suficiente como para "cargar" una trama entera de datos y procesarla una vez recibida completamente, o si la memoria "escasea" y quieres ir procesando las tramas según van llegando los datos.

En el caso de tratar los datos una vez se tenga el paquete completo en memoria, básicamente lo que ha de controlar la máquina de estados es el inicio de la transmisión, la recepción y almacenamiento de cada byte, el final de la transmisión y si hay algún timeout o error en la comunicación. Un posible error importante que hay que controlar, es un posible "desbordamiento del buffer". Esto es que se reciban más datos de los que se pueden almacenar. Una vez recibido un paquete completamente, se procesa extrayendo los datos que se quieran, y se hace con esos datos lo que se desee. Para luego pasar a esperar la recepción del siguiente paquete. Tal vez nos valgamos de otras máquinas de estados para extraer cierto tipo de información del paquete.

En el caso de querer procesar el paquete "al vuelo", obteniendo los datos según nos van llegando, antes de completar toda la trama: sería más compleja la máquina de estados ya que ha de controlar tantos estados como datos posibles nos pueda llegar y más. Además de que, tal vez habría que controlar que se recibe bien todo el paquete antes de "validar" toda la información recibida. Esto último sería aconsejable si al final de cada trama nos llega algún tipo de CRC LRC o cualquier datos que nos indique que toda la información nos ha llegado correctamente.

Eso sí, es importante conocer exactamente cómo nos llega codificada la información. Por ejemplo: si nos viene un campo numérico, hemos de saber cómo nos viene. Parece ser que en este caso la transmisión no es "binaria", ¿pero está en BCD o hexadecimal? En el caso de venir en hexadecimal ¿que vamos a recibir primero el byte más significativo o el menos significativo? etc.

Desde mi pinte de vista, otra cosa importante es no usar String, sino la cadenas de C (char[]). Las String son muy cómodas, pero gastan memoria sin ningún control por nuestra parte y en ocasiones puede ralentizar el proceso puntualmente. Incluso en los ordenadores más potentes se utilizan buffers de char[] en lugar de cadenas de tipo String.

Para ser más específico en la ayuda, necesitaría saber con más exactitud como son las tramas que quieres procesar y qué es lo que quieres hacer con los datos recibidos. No sé si te interesa "guardar" todos o sólo obtener y tratas parte de ellos.

Por cierto, el envío de la "solicitud" y espera de recepción también se soluciona con máquinas de estados y millis().

Digamos que para cualquier programa "medianamente decente/complejo" en Arduino: la solución pasa siempre por máquinas de estados y millis(). Cualquier otra cosa, para mi, es una pérdida de tiempo.

Muchas gracias, me es muy útil tu aportación de cara a abordar este problema y otros que también tengo en mente (aunque aún tengo bastante que aprender para poder hacerlo bien).

Pongo un ejemplo de lo que viene en la documentación para que veas cómo viene estructurado.

Solicitud de frame SCIS11:

MSB B6 B5 B4 B3 B2 B1 LSB
byte 0 'K'

Yo intuyo que en la tabla anterior la letra "B" se refiere a bit y no a byte, y que se envía un único byte correspondiente al caracter K, ¿no?

frame de respuesta SCIS11

B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 B10 B11 B12 B13 B14
'$' '0' '0' 'K' M3 M2 M1 M0 signo F4 F3 F2 F1 F0 '/n

traducción literal de lo que se indica a continuación del frame

- Mx, 4 caracteres indicando 100 veces el valor de la magnitud S11 expresada en dB
- Fx, 4 caracteres indicando 100 veces el valor de la fase S11 expresada en grados
- signo, caracter individual que determina el signo de magnitud y de fase según la siguiente tabla

Entiendo que el dispositivo sólo envía 1 frame de datos cuando recibe la solicitud correspondiente, de manera que mi programa de arduino sería el que marcara la frecuencia con que se envían los datos.

A parte, el dispositivo se puede poner en modo ráfaga ("burst mode") si recibe una solicitud determinada, de manera que empieza a enviar 1 frame cada milisegundo de manera continua.
En ese modo, el frame enviado es bastante más pequeño ya que cada dato viene representado por 2 bytes 'signed integer' en vez de por 4 caracteres, además de prescindir de un caracter para el signo y de los dos '0' del encabezamiento.

Decir que en ese frame de 8 bytes se envían los 3 parámetros que quiero usar, mientras que en el modo petición-respuesta se envian 2 frames de 15 y 18 caracteres respectivamente para enviar 5 parámetros (2 de los cuales en principio no necesito)

Imagino que el modo ráfaga puede ser algo más dificil de gestionar para el Arduino a pesar de ser el frame bastante más pequeño ¿no?

No tengo muy claro aún cómo es el formato de los paquetes que se han de enviar y los que se reciben. Lo primero que yo haría sería conectar el Arduino a ese puerto serie y tratar de mandarle "a mano" una petición para obtener una respuesta "real".

¿Tienes hardware para conectar el Arduino al "dispositivo"? ¿Es RS232 de los de señales a ±12 voltios o es un RS232 a 5 voltios? ¿Sabes la velocidad y paridad de la comunicación?

Porque si no puedes "conectarlo" al "dispositivo", poco podrás hacer.

Si lo que tienes es un Arduino UNO o similar, de los que sólo tienen un puerto serie, yo utilizaría un puerto serie extra por sofware. Para poder tener conectado el Arduino al PC y al dispositivo a la vez. Y desde el monitor serie del IDE mandar cadenas al dispositivo y ver su respuesta en el motitor serie. Adaptaría el programa de ejemplo de https://www.arduino.cc/en/Tutorial/SoftwareSerialExample adaptando las velocidades de transmisión (y la paridad si fuera necesario).

Si dispones de un Arduino MEGA o de los que tienen más de un puerto serie, adaptaría las velocidades del ejemplo de https://www.arduino.cc/en/Tutorial/MultiSerialMega adaptando igualmente las velocidades de transmisión (y la paridad si fuera necesario).

Supongo que para que te responda el "paquete K" te bastaría mandarle una K desde el monitor serie. Lo que no sé si sólo la K o también has de mandar un retorno de carro a continuación.

También puedes dejar pendiente lo de conectar el Arduino al dispositivo y ahora para analizar sus paquetes te bastaría con conectarlo a un puerto serie del PC (valdría con un adaptador RS232-USB) y con el mismo monitor serie del IDE de Arduino hacer las pruebas de mandarle una K y ver lo que responde. Pero tarde o temprano has de "enchufar" el dispositivo al Arduino. Y si quieres depurar fácilmente lo mejor es el montaje que te sugerí antes, el de usar un puerto serie más en el Arduino.

Una vez tengas una respuesta con datos "reales" ya es cuestión de verlos y analizar qué es lo que responde. Porque por lo que has puesto, aún no tengo claro si la respuesta está en BCD, en "presunto hexadecimal" o qué. Ni tan siquiera el "signo" sé con qué lo representa.

Lo que sí que parece es que no tiene ningún tipo de CRC o LRC (suma de chequeo para verificar que no ha habido error en la comunicación) y que los paquetes parecen pequeños, simples y de tamaño prefijado. Siendo así, lo haría leyendo todo el paquete y una vez recibido completamente, verificaría el tipo y tamaño del paquete y lo procesaría para sacarle la información que quiero.

No veo práctico especular sin más. Lo mejor es obtener algo de información e ir avanzando poco a poco.

Mucha gracias de nuevo,

En cuanto tenga el dispositivo a mano haré la prueba que comentas (tengo un adaptador de RS232 pendiente de probar) y me volveré a pasar por aquí.
Entonces, ¿con el monitor Serie del IDE de Arduino también podría enviar y recibir paquetes con ese dispositivo?

Si fuera posible compartir el puerto serie del PC con dos aplicaciones, algo útil sería usar "simultáneamente" (con poco tiempo de desfase) el monitor serie del Arduino IDE y el software propio de ese dispositivo para visualizar datos numéricos ya extraídos.