lunes, 9 de marzo de 2020

Actualización #05

Implementando la librería



Ahora que tengo pruebas de la viabilidad del proyecto, es el momento de comenzar a escribir una librería que simplifique el trabajo.

Abstracción del tipo de serial

La librería debería funcionar igual independientemente de si los datos se reciben por un HardwareSerial o por un SoftwareSerial. Su superclase común más cercana es la clase Stream.
Stream tiene los métodos de lectura, pero hereda de Print, que tiene los métodos de escritura.
Como el compilador para AVR de C++ no tiene dynamic_cast, incluiré un booleano para identificar qué tipo de serial se utiliza y hacer así un casting estático cuando sea necesario.
De momento solo es necesario hacer el casting en la inicialización, porque si Stream ni sus superclases tiene el método begin y de hecho es diferente para los dos tipos de serial. Por lo demás se comportan idénticamente.

Detección de tramas

Las tramas comienzan con un byte 0x00, acaban en un byte 0x00 y tienen 25 bytes. También sabemos que los cuatro últimos bits del penúltimo byte son siempre 0, pero esos 4 bits están reservados y podrían cambiar si fuera necesario, no se tendrán en cuenta.

El código que controla la detección de tramas tiene un contador para saber en qué byte está. Guarda en un buffer temporal la trama cruda. El contador aumenta cuando un byte se transfiere con éxito. Si hay un error de paridad SoftwareSerial ignora todo el byte es transparente para la librería SBUS. Si estamos en el byte 0 y el valor no es 0x0F el contador no aumenta, asume que  hay un descuadre y continua descartando bytes hasta encontrar el inicio de una trama. Si estamos en el byte 24 pero el valor no es 0x00, algún byte se ha perdido, en lugar de actualizar los canales y reiniciar el contador, solo reinicia el contador descartando la trama completa.

Alternativamente se podría asumir que la trama empieza con dos bytes 0x00 y 0x0F y para aceptar una trama se debe empezar con la siguiente para verificar que la trama acaba sin que se pierdan bytes. Pero esta implementación solo resulta más eficiente en un caso muy específico y sería más costosa. Como ese fragmento de código se tiene que ejecutar muchas veces es mejor dejarlo como está.

Esta función se ha puesto a prueba a conciencia , de hecho la versión descrita aquí es la segunda iteración que he realizado. De momento no he logrado hacer fallar a esta nueva revisión.

División de canales

En una actualización anterior publiqué un código que realizaba la división y composición de los 16 canales de 11 bits en 22 bytes. Hacer funcionar la librería ha sido tan sencillo como copiar esos fragmentos de código en las funciones que descomponen y componen las tramas.

Siguiente paso

Dicho todo esto, la recepción de datos está completa. Me gustaría agregar funciones de utilidad para convertir tipos de datos fácilmente. pero eso puede esperar.
El próximo hito a conseguir es la transferencia de telemetría.

viernes, 6 de marzo de 2020

Actualización #04

Recibiendo datos del X8R y R9 por HardwareSerial


Objetivo

Establecer una comunicación por Hardware con el X8R y  R9 y obtener datos significativos.
Usando una placa MEGA 2560.

Resultado

Ante la imposibilidad de usar el módulo R9 por SoftwareSerial, me veo obligado a incorporar una opción en la librería para usar HardwareSerial.

Para conectar el módulo mediante HardwareSerial hay que invertir la señal antes de introducirla a la placa porque la librería de Arduino no permite configurar la lógica inversa como SoftwareSerial.
Comencé usando el circuito integrado séxtuple inversor 7004 de Texas Instrument. El tiempo de propagación es varios órdenes de magnitud inferior al periodo de la señal. Aún así los dos IC que he usado han acabado fallando. Al final he tenido que construir mi propio inversor con un transistor y dos resistencias.

El cable azul es la entrada y el amarillo la salida. La resistencia en horizontal es de 10K y la vertical de 1K. El transistor es un BJT NPN, concretamente he probado con un 2N3904.


Ahora he probado repetido las dos pruebas anteriores pero mediante HardwareSerial en lugar SoftwareSerial.

Finalmente ha funcionado. Logré recibir datos correctamente de los dos módulos.
La calidad ha mejorado. Claramente la velocidad de las placas es demasiado baja para realizar correctamente el timing y acaban fallando muchos bits, gracias al bit de paridad descarta algunos bytes erróneos. Pero si el bit error rate es lo suficientemente alto y aleatorio, un bit de paridad no impedirá recibir tramas con errores ocultos.
Tengo que especificar en la documentación que determinados módulos, como es el caso del R9, solo funcionan por HardwareSerial.

jueves, 5 de marzo de 2020

Actualización #03

Recibiendo datos del R9 por SoftwareSerial


Objetivo

Establecer una comunicación por software con el R9 y obtener datos significativos.
En una placa UNO y MEGA 2560.

Resultado

Habiendo tenido éxito en la prueba del R9, el procedimiento para hacer funcionar el R9 debería reducirse a conectarlo correctamente.

Esta vez no he logrado obtener datos significativos, los bytes son prácticamente aleatorios.
Todo indica a que el módulo R9 envía más tramas por segundo que el X8R, las suficientes para saturar la placa. MEGA tiene la misma velocidad de reloj que UNO, quizás en una placa más rápida funcione.

miércoles, 4 de marzo de 2020

Actualización #02

Recibiendo datos del X8R por SoftwareSerial


Objetivo

Establecer una comunicación por software con el X8R y obtener datos significativos.
Probado en una placa UNO y MEGA 2560.

Resultado

He realizado una modificación de la librería SoftwareSerial para que funcione con 8E2 en lugar de 8E1.  Antes de intentar la conexión con el módulo receptor, he probado a usar dos arduinos para ver cómo responden a 100000 baudios. Si se introducen pausas, funciona sin problemas. Pero cuando se usa la línea a plena capacidad la placa se satura y se vuelve inútil. Sabiendo esto, la viabilidad de SoftwareSerial dependerá de que el receptor tenga un tiempo entre tramas lo suficientemente largo.

Ahora sí, lo enlazo con el receptor. El Arduino se conecta a las señales SBUS OUT y SBUS IN por pines digitales usados como RX y TX respectivamente.
Para mostrar de forma más visible los datos, he escrito un pequeño sketch que muestra los bytes recibidos de 25 en 25. La trama comienza en un byte con el valor 15 y termina 25 bytes más a delante en 0.

El resultado ha sido todo un éxito, cuando el transmisor varía el primer canal se observa un cambio en el segundo byte y parte del siguiente como cabría esperar. Aunque ocurren problemas, cada cierto tiempo la posición del inicio de la trama retrocede, eso indica que se pierden bytes. Pese a esto, que es fácilmente corregible en la librería SBUS, el resultado es muy bueno.

miércoles, 26 de febrero de 2020

Actualización #01

El protocolo SBUS


Muchos modelos son compatibles con los receptores FrSky porque se conectan a las salidas PWM.
Pero no todos los receptores tienen PWM y los que lo tienen solo pueden sacar 8 canales en lugar de 16. Incluso recomiendan usar dos receptores para lograr 16 canales. Pero la interfaz principal de comunicación es SBUS.

Comunicación

SBUS es un protocolo serial derivado de RS-232 con las siguientes características:

  • Baudrate 100000 no estándar.
  • Lógica invertida.
  • Configuración 8E2
    • 1 bit de inicio
    • 8 bits de datos
    • 1 bit de paridad par
    • 2 bits de parada
El baudrate es muy alto para SoftwareSerial, HardwareSerial no tendría problema en un Arduino.
La lógica invertida significa que los niveles de tensión funcionan al contrario que en RS-232, no es solo que los datos estén invertidos, sino que la forma de señalizar una ráfaga es completamente opuesta. SoftwareSerial permite lógica inversa pero HardwareSerial no, requeriría hardware adicional para invertir la señal.
La configuración 8E2 es una de las posibles en HardwareSerial pero SoftwareSerial está implementado únicamente con 8N1, aunque eso podría cambiar se modifico la librería.

La trama

La información se encapsula en tramas de 25 bytes. Con 1 byte cabecera fijo, 22 bytes de datos, 1 byte de flags y 1 byte final de valor fijo.
Se transmiten 16 canales de 11 bits, eso hace que se mezclen canales en un byte. La estructura resumida es la siguiente. En los flags se incluyen dos canales extra de un bit cada uno, un bit para activar el modo seguro y un bit para indicar trama perdida.

  • Byte 0 : Cabecera valor fijo 0x0F
  • Byte 1 - Byte 22 : Canales 0 a 15
  • Byte 23 : Flags, canales 16 y 17.
  • Byte 24 : Marca de final valor fijo 0x00
Toda esta información está explicada con mayor profundidad en la documentación de GitHub.

Conclusiones y viabilidad

Aquí tenemos el siguiente dilema. SoftwareSerial no soportará un baudrate tan alto y no tiene configuración 8E2 pero sí permite lógica invertida. HardwareSerial no tendría problema con el baudrate ni con la configuración 8E2 pero requiere invertir la señal mediante un circuito.

La hoja de ruta será intentar hacer funcionar SoftwareSerial con 8E2 y el baudrate alto y si no es posible pasar a HarwareSerial diseñando un pequeño inversor.

No soy un experto en operaciones a nivel de bit pero he escrito en C++ el siguiente código demostrando como guardar 16 canales de 11 bits en 22 bytes separados y sacarlos en el proceso inverso.
En un principio hice unas operaciones por cada canal pero en el tercero me dí cuenta del patrón que seguía y creé un pequeño procedimiento estándar que resuelve este problema con cualquier número de canales, bytes por canal y bytes por carácter. (Por defecto: 16, 11, 8)
Adjunto el archivo de prueba.

martes, 25 de febrero de 2020

Actualización #00

#00 : Iniciando el terreno


A la izquierda pueden verse dos receptores FrSky, R9 y X8R. A la derecha dos placas de desarrollo Arduino Uno y Mega. En el centro un mando FrSky X9D.

El objetivo de este proyecto es poder usar cómodamente ese mando para controlar los Arduinos.
Como los receptores tienen al menos una salida SBUS, una librería capaz de comunicarse por ese protocolo podría establecer un enlace con ese mando o cualquiera conectado al receptor.

Los mandos de FrSky utilizan OpenTX que es un firmware libre para transmisores de radio.

Dispositivos

Idealmente me gustaría ser capaz de comunicar el receptor con la placa Arduino mediante un serial software. No todas las placas tienen puertos seriales extras, la mayoría solo tiene el utilizado para la conexión USB.
Todos los receptores actuales de FrSky incorporan un puerto SBUS para comunicarse con el modelo y el protocolo ACCST D16 para comunicarse con el mando.
La mayoría de mando de FrSky acepta ACCST D16, por lo que la compatibilidad es prácticamente completa. 

Existen diversas marcas que comercializan alternativas compatibles con FrSky, por lo que logrando comunicación software serial con un receptor FrSky podría afirmar que Casi cualquier Arduino es compatible con casi cualquier mando radio control.

Planificación

  1. Estudio del protocolo SBUS y viabilidad
  2. Implementación básica de recepción
  3. Recepción y decodificación fiable
  4. Envío de telemetría  fiable
  5. Estandarización en una librería formal
En el proceso se generará la documentación oportuna y una vez terminado la librería será publicada.