Ayuda o sugerencias para decodificar un protocolo IR

Hace algún tiempo compré un pequeño y simple helicóptero de juguete controlado por infrarrojos (igual que este , se llama "Diamond Gyro" o "Diamond Force"). Por diversión, he estado buscando controlarlo a través de un Arduino.

Actualización: tengo el protocolo resuelto; Ver respuesta

Otros ya han compartido sus resultados sobre la piratería de un helicóptero de juguete IR diferente y la decodificación de su protocolo IR. Realmente genial, pero desafortunadamente mi helicóptero usa un protocolo diferente. Uno que no puedo entender. (Debo agregar que la electrónica es puramente un pasatiempo ocasional para mí, por lo que es posible que haya pasado por alto algo obvio).

Al igual que en el segundo enlace anterior, desmonté el controlador, localicé el pin del IC que controla los LED (por cierto, las marcas del IC se han borrado) y conecté un analizador lógico.

Tengo muchos buenos datos, pero todavía no puedo descifrar el protocolo. Este sitio es un gran recurso, pero ninguno de los protocolos enumerados parece encajar. Y nada más que he encontrado parece encajar con la señal que he capturado tampoco. Sin embargo, tengo que imaginar que es un protocolo simple y listo para usar, solo porque es un pequeño juguete barato.

Así que agradecería cualquier idea que pueda tener. Tal vez solo lo estoy viendo mal.
(Más información debajo de la imagen)

Muestras del canal A

Características de la señal/protocolo

Capturé esto a 16 MHz con el controlador configurado en el canal A; debe ser preciso, en cuanto al tiempo. (Hay 3 canales IR entre los que puede elegir, pero el uso de los otros dos canales no cambia las características, solo partes del paquete en sí). Los tiempos son muy consistentes (+/- 10 µs máx.). Los paquetes se repiten con intervalos variables pero, como mínimo, están separados por unos 100 ms.

Portador: 38kHz @ 50% ciclo de trabajo

Bajos:
- Corto: 285µs
- Largo: 795µs

Máximos:
- Corto: 275µs
- Largo: 855µs

Siempre 17 colmos por paquete.

Controles/entradas

El heli tiene 3 controles: "acelerador" (es decir, elevación/velocidad del rotor), cabeceo (adelante/atrás) y guiñada (rotación alrededor del eje del rotor), todos controlados con 2 palancas de mando. Todos tienen algún tipo de rango (no solo encendido/apagado) y, por lo que puedo decir, todos se transmiten en un solo paquete. Las entradas izquierda/derecha solo se envían si se envía algo más, por lo que apliqué el acelerador máximo al muestrear eso. Acelerador y entrada de tono en sus propios paquetes de activación que se envían, tan pronto como empuje las palancas de mando más allá de algún umbral/banda muerta (en el gráfico a continuación, la etiqueta "min" es para el primer paquete enviado cuando empuja lentamente un control más allá de su banda muerta).

También tiene botones para recortar a izquierda y derecha, ya que el heli no es un instrumento de precisión ( en absoluto ) y tiende a girar lentamente de lo contrario. Desafortunadamente, los botones de recorte izquierdo/derecho no parecen enviar una señal que incremente/reduzca algo con cada pulsación (lo que sería útil para calcular el protocolo); parece ser solo un comando único, que le dice al helicóptero que se ajuste a la izquierda/derecha, y luego lo sigue.

¿Por qué no usar los rastros de señal que ya tiene para escribir los paquetes sin procesar?
@IgnacioVazquez-Abrams ¿Quiere decir simplemente reproducir las señales grabadas en el helicóptero?
Por supuesto. No es como si el helicóptero fuera capaz de notar la diferencia...
@IgnacioVazquez-Abrams Cierto , pero por lo que puedo decir, el paquete contiene los 3 controles (acelerador/cabeceo/guiñada) y los controles del helicóptero, ninguno está solo encendido/apagado. Para dirigir la cosa mediante la reproducción, tendría que capturar todas las configuraciones... Además, quiero entender el protocolo.
@IgnacioVazquez-Abrams Vaya, de alguna manera confundí mi último comentario. Quería decir: "... el paquete contiene los 3 controles (acelerador/cabeceo/guiñada) y ninguno de ellos solo está encendido/apagado".

Respuestas (2)

Me tomo la libertad de responder mi propia pregunta, ya que tengo la mayor parte descifrada y esta es una buena manera de compartir mis hallazgos. Agradezco a Olin Lathrop por brindarme un punto de partida y algunas ideas para probar, pero, en última instancia, el protocolo resultó bastante diferente de la suposición de Olin, por lo que publiqué esta respuesta.


Actualización: publiqué una pregunta de seguimiento con respecto a los últimos 8 bits, que no entendí completamente, y Dave Tweed lo resolvió . Incluiré los detalles aquí, por lo que esta respuesta puede funcionar como una especificación de protocolo completa, pero consulte la respuesta de Dave.


Tuve que probar algunas cosas diferentes para resolver esto, pero estoy bastante seguro de que lo conseguí. Curiosamente, no he encontrado nada parecido a este protocolo en ningún otro lugar, pero es muy posible que sea un protocolo común que simplemente no conozco.

De todos modos, esto es lo que he encontrado:

Protocolo/codificación

Tanto los pulsos como los espacios intermedios se utilizan para codificar los datos. Un pulso/espacio largo es un uno binario (1), y un pulso/espacio corto es un cero binario (0). Los pulsos se envían usando una modulación de 38 kHz de infrarrojos de consumo estándar al 50 % del ciclo de trabajo.

Los tiempos de pulso/espacio están en la pregunta original, pero los repetiré aquí para completar:

 Bit    Pulse     Space
-----+---------+---------
  0  |  275µs  |  285µs
  1  |  855µs  |  795µs

Todo ±10µs máx., ±5µs típ.. Esto se basa en muestras capturadas con un analizador lógico a 16MHz; No tengo un osciloscopio, así que no sé el perfil exacto (es decir, tiempos de subida/bajada).

Los paquetes se repiten siempre que se apliquen las entradas de control y parezcan estar separados por un mínimo de 100 ms.

La transmisión de paquetes comienza con un preámbulo de "pulso 1", que es fijo y no forma parte de los datos. El siguiente espacio codifica el primer bit de datos del paquete y el último pulso codifica el último bit.

Cada paquete tiene una longitud de 32 bits y contiene todas las entradas que puede proporcionar el control remoto. Los valores se leen como little endian, es decir, MSB primero.

Estructura de datos

A continuación se muestra la estructura básica de los paquetes individuales. Los últimos 8 bits me confundieron, pero eso ya se ha resuelto (ver más abajo).

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2
--+---------------------------+-----------+---+-------+-----------
 P|    Yaw    |   Throttle    |   Pitch   | T | Chan. |   Check

P: Preamble (always a pulse-1), T: Trim, Chan.: Channel

Bit    Length    Description (see note below)
-----------------------------------------------
0      1         Preamble. High 1
1-6    6         Yaw. Range 0-36 for left-right, 17 being neutral
7-14   8         Throttle. Range 0-134
15-20  6         Pitch. Range 0-38 for forward-back, 17 being neutral
21-22  2         Trim. Left = 1, right = 2, no trim = 0
23-26  4         Channel. A = 5, B = 2, C = 8
27-32  6         Check bits

Nota: Los rangos se basan en las lecturas más altas que obtuve. El protocolo es capaz de rangos más grandes, hasta 255 para aceleración, 63 para cabeceo/guiñada, pero alcanza aproximadamente la mitad de eso.
El valor de tono parece tener una banda muerta de 14 a 21 (inclusive); solo los valores por encima o por debajo hacen que el helicóptero reaccione. No sé si es lo mismo para la guiñada (difícil de decir, ya que el helicóptero es inestable de todos modos y puede girar ligeramente por sí solo).

Aquí está en términos gráficos (compárelo con el gráfico en la pregunta original)

estructura del paquete

Los 6 bits de verificación se calculan haciendo XOR en todos los valores anteriores. Cada valor se trata como 6 bits. Esto significa que los 2 MSB del valor de aceleración de 8 bits simplemente se ignoran. Es decir

check = yaw ^ (throttle & 0x3F) ^ pitch ^ trim ^ channel

notas practicas

Los tiempos y la modulación de la señal no necesitan ser muy precisos. Incluso la temporización nada precisa de mi Arduino funciona bien a pesar de la modulación dudosa y un poco de imprecisión en las duraciones de pulso/espacio en comparación con el control remoto real.

Creo, pero no lo he probado, que el helicóptero simplemente se enganchará al canal de la primera señal que encuentre. Si se deja sin señal durante demasiado tiempo (un par de segundos), parece volver a su modo de "búsqueda", hasta que vuelve a adquirir una señal.

El helicóptero ignorará los valores de cabeceo y guiñada si el acelerador es cero.

Los comandos de recorte se envían solo una vez por cada botón que se presiona en el control remoto. Presumiblemente, el valor de compensación simplemente incrementa/disminuye un valor en el propio controlador del helicóptero; no es algo de lo que el control remoto realiza un seguimiento. Por lo tanto, cualquier implementación de esto probablemente debería apegarse a ese esquema, y ​​solo enviar el valor de recorte izquierdo/derecho ocasional, pero de lo contrario, de forma predeterminada, un valor de recorte cero en los paquetes.

Recomiendo tener un interruptor de apagado que simplemente ponga el acelerador a cero. Esto hará que el helicóptero caiga del cielo, pero sufrirá menos daño cuando sus motores no estén girando. Entonces, si está a punto de estrellarse o golpear algo, presione el interruptor de apagado para evitar dañar los engranajes o romper las cuchillas.

Los LED IR del control remoto original parecen tener una longitud de onda de >900 nm, pero no tengo problemas para usar un LED de ~850 nm.

El receptor de infrarrojos del helicóptero está bien, pero no es muy sensible, por lo que cuanto más brillante sea la fuente de infrarrojos, mejor. El control remoto usa 3 LED en serie, ubicados en el riel de 9V en lugar del riel de 5V que usa la lógica. No he comprobado su consumo actual con mucha precisión, pero apuesto a que son 50 mA.

Data de muestra

Aquí hay un montón de paquetes, para cualquier persona interesada (sí, escribí un decodificador; no decodifiqué todo esto a mano). Los paquetes del canal A provienen de las mismas capturas que los gráficos en la pregunta original.

Channel A                                                       
Yaw     Throttle  Pitch   Tr  Chan  Check     Description
-----------------------------------------------------------
000100  10000100  000000  00  0101  000101    Left Mid + Throttle
000000  10000110  010001  00  0101  010010    Left Max + Throttle 
100001  10000110  000000  00  0101  100010    Right Mid + Throttle 
100100  10000100  010001  00  0101  110100    Right Max + Throttle
010001  00000000  001011  00  0101  011111    Forward Min 
010001  00000000  000000  00  0101  010100    Forward Max 
010001  00000000  011000  00  0101  001100    Back Min 
010001  00000000  100101  00  0101  110001    Back Max
010001  00000000  010001  01  0101  010101    Left Trim 
010001  00000000  010001  10  0101  100101    Right Trim 
010001  00000011  010001  00  0101  000110    Throttle 01 (min)
010001  00010110  010001  00  0101  010011    Throttle 02
010001  00011111  010001  00  0101  011010    Throttle 03
010001  00101111  010001  00  0101  101010    Throttle 04
010001  00111110  010001  00  0101  111011    Throttle 05
010001  01010101  010001  00  0101  010000    Throttle 06
010001  01011111  010001  00  0101  011010    Throttle 07
010001  01101100  010001  00  0101  101001    Throttle 08
010001  01111010  010001  00  0101  111111    Throttle 09
010001  10000101  010001  00  0101  000000    Throttle 10 (max)

Channel B
Yaw     Throttle  Pitch   Tr  Chan  Check     Description
-----------------------------------------------------------
000000  10000110  010001  00  0010  010101    Left Max + Throttle 
100100  10000110  010001  00  0010  110001    Right Max + Throttle 
010001  00000000  001001  00  0010  011010    Forward Min 
010001  00000000  000000  00  0010  010011    Forward Max 
010001  00000000  010111  00  0010  000100    Back Min 
010001  00000000  100110  00  0010  110101    Back Max
010001  00000000  010001  01  0010  010010    Left Trim 
010001  00000000  010001  10  0010  100010    Right Trim 
010001  00000001  010001  00  0010  000011    Throttle Min 
010001  00110100  010001  00  0010  110110    Throttle Mid 
010001  01100111  010001  00  0010  100101    Throttle High 
010001  10001111  010001  00  0010  001101    Throttle Max 

Channel C
Yaw     Throttle  Pitch   Tr  Chan  Check     Description
-----------------------------------------------------------
000000  10000101  010001  00  1000  011100    Left Max + Throttle 
100100  10000101  010001  00  1000  111000    Right Max + Throttle 
010001  00000000  001010  00  1000  010011    Forward Min 
010001  00000000  000000  00  1000  011001    Forward Max 
010001  00000000  010111  00  1000  001110    Back Min 
010001  00000000  100110  00  1000  111111    Back Max
010001  00000000  010001  01  1000  011000    Left Trim 
010001  00000000  010001  10  1000  101000    Right Trim 
010001  00000001  010001  00  1000  001001    Throttle Min 
010001  00110100  010001  00  1000  111100    Throttle Mid 
010001  01100110  010001  00  1000  101110    Throttle High 
010001  10000101  010001  00  1000  001101    Throttle Max

Como se mencionó anteriormente, se han resuelto los últimos 8 bits, pero solo para la posteridad, aquí están mis pensamientos originales. Siéntase libre de ignorarlo por completo, ya que estaba bastante equivocado en mis conjeturas.

Los últimos 8 bits

Los últimos 8 bits del paquete siguen siendo un poco misteriosos.

Los 4 bits del bit 23 al 26 parecen estar completamente determinados por la configuración del canal del control remoto. Cambiar el canal en el control remoto no altera el protocolo o la modulación de ninguna manera; solo cambia esos 4 bits.

Pero 4 bits es el doble de lo que realmente se necesita para codificar la configuración del canal; solo hay tres canales, por lo que 2 bits son suficientes. Por lo tanto, en la descripción de la estructura anterior, solo etiqueté los primeros 2 bits como "Canal" y dejé los otros dos etiquetados como "X", pero esto es una suposición.

A continuación se muestra una muestra de los bits relevantes para la configuración de cada canal.

Chan.   Bits 23-26
-----+-------------
  A  |  0  1  0  1
  B  |  0  0  1  0
  C  |  1  0  0  0

Básicamente, hay 2 bits más de los necesarios para transmitir la configuración del canal. Tal vez el protocolo tiene 4 bits reservados para permitir más canales más tarde, o el protocolo se puede usar en juguetes completamente diferentes, pero simplemente no lo sé. Para los valores más grandes, el protocolo usa bits adicionales que podrían omitirse (yaw/throttle/pitch podrían funcionar con un poco menos cada uno), pero para el recorte, que también tiene 3 estados, solo se usan 2 bits. Entonces, uno podría sospechar que el canal también tiene solo 2 bits, pero eso deja a los siguientes 2 sin contabilizar.

La otra posibilidad es que la suma de verificación del paquete tenga una longitud de 8 bits, comenzando con los "bits X" y, a través de la magia de la suma de verificación, de alguna manera siempre reflejan la configuración del canal. Pero de nuevo: no lo sé.

Y hablando de: no tengo idea de cómo se forman esos bits de verificación. Quiero decir, son bits de verificación, ya que no corresponden a ninguna entrada de control única, y el helicóptero no parece responder si juego con ellos. Supongo que es un CRC de algún tipo, pero no he podido descifrarlo. La verificación tiene una longitud de 6 a 8 bits, dependiendo de cómo interprete los "X bits", por lo que hay muchas formas de combinarlos.

Esto no se ve tan mal. Primero observe que todos los mensajes contienen exactamente 17 pulsos. Esto nos da inmediatamente una fuerte pista de que los espacios cortos dentro de un mensaje son irrelevantes. Parece que los datos están codificados por pulsos que son cortos o largos, y que es aceptable cierto rango de espacio entre estos pulsos.

Obviamente, cada mensaje comienza con un pulso largo como bit de inicio. Eso deja 16 bits de datos. Probablemente algunos de los primeros bits sean un código de operación, posiblemente de longitud variable. Si estuviera haciendo esto, algunos de los bits finales serían una suma de verificación. Imagínese que los ingenieros que escribieron el firmware querían mantener las cosas simples para ellos, por lo que puede comenzar suponiendo que hay 8 bits de datos en alguna parte. Ahora vea si alguno de los mensajes tiene sentido.

Llamemos 1 a un largo y 0 a un corto. Podría ser al revés, pero tenemos que empezar por alguna parte. Quitando las hojas de bits de inicio:

1010001101011010 minutos del acelerador
1010011101011000 aceleración máxima
1010000001011111 min adelante
1010000000011110 máx adelante
1010000011011101 máximo atrás
1010000100011010 minutos atrás
0000010101011100 máx. izquierda + aceleración máx.
0100010101011110 máx derecho + acelerador máx.
1010000101111111 moldura izquierda
1010000101011011 ajuste derecho

Algunas cosas saltan de inmediato. Obviamente, el bit 0 es un bit de paridad. De lo contrario, parece haber un campo de 3 bits <15:13>, un valor de datos de 8 bits <12:5> y otro campo de 4 bits <4:1>.

Parece que el valor de los datos se envía en orden de bits de menor a mayor, por lo que probablemente tenga más sentido interpretar los 16 bits completos invertidos de lo que muestro.

No tengo ganas de dedicar más tiempo a esto, pero espero que esto te haya dado un comienzo. Procedería reescribiendo la lista anterior con el bit de paridad quitado, el número completo cambiado de LSB a MSB, y cada campo asumido se muestra por separado con un espacio entre él y el campo contiguo. Eso puede permitir que te salgan más cosas. También tenga en cuenta que podemos tener el sentido 1/0 de cada bit al revés. Tal vez escriba la nueva tabla en cada sentido y vea si algo tiene más sentido de una forma.

¡Gracias, esto es excelente! Me pondré manos a la obra, a ver qué encuentro. Después de mirar otros protocolos, comencé a pensar que tal vez los espacios eran irrelevantes, pero como tenían dos tiempos muy consistentes, no estaba tan seguro. Pensé que variarían más si no fueran importantes. De todos modos, lo intentaré. Gracias de nuevo
Eh... por lo que puedo decir, los espacios importan. Me concentré en el acelerador y capturé algunas muestras más en 10 posiciones diferentes del acelerador. La exclusión de espacios no me dio números significativos, independientemente de cómo hice las conversiones. Pero incluirlos como largo = 1, corto = 0 produce una progresión suave del valor del acelerador de 1 a 134 (little endian). Todavía trabajando en los otros parámetros.
Tengo el protocolo casi completamente resuelto, pero todavía hay un poco de misterio. Agregué un montón de cosas a mi pregunta, si quieres intentarlo. De todos modos, ¡gracias por tu ayuda hasta ahora! Me hizo trabajar en la dirección correcta.
@Flambino: Parece que estás muy por delante de lo que hice al principio, que resultó ser en su mayoría conjeturas erróneas en retrospectiva. Leí su pregunta actualizada pero todavía no entiendo cómo se usa exactamente la longitud de los espacios. ¿Fue solo una coincidencia que todos los patrones que mostró tenían exactamente 17 pulsos y que el último indicaba paridad si solo los pulsos se toman como 0 o 1?
Honestamente, fue principalmente prueba y error de mi parte. Dado que los 2 tiempos utilizados para los espacios son tan exactos como los tiempos de pulso, pensé que podrían ser significativos. Y, cuando ignorar los espacios no arrojó datos binarios útiles, simplemente asumí pulso largo = 1 y espacio largo = 1 (y espacio/pulso corto = 0), lo que inmediatamente me dio datos muy útiles. Entonces, el primer espacio después del pulso del preámbulo es el primer bit (el gráfico de máxima derecha + aceleración máxima muestra un "espacio 1" como el primer bit) seguido de 16 pulsos, con 15 espacios más en el medio; 32 bits
Estoy pensando que podría responder mi propia pregunta aquí y hacer una nueva sobre qué es esa suma de verificación (o cómo "descifrarla"). Mi pregunta original sobre el protocolo general es algo discutible ya que estoy bastante seguro de haberlo resuelto, así que creo que es mejor hacer una nueva. De todos modos, espero que no le importe que responda mi propia pregunta aquí.
@Flambino: responder a su propia pregunta es totalmente apropiado si tiene información sobre la respuesta que aún no se ha publicado, lo que ciertamente es el caso aquí. Lo intenté, pero este tipo de cosas requiere prueba y error. Resulta que mi primer intento fue más un error que un resultado útil. Bueno, así es como funciona este proceso.
En realidad, su respuesta fue de gran ayuda, independientemente de los detalles. Esperaba que alguien dijera "oh, esto es solo el protocolo xyz", así que ni siquiera pensé en sentarme e intentar algo . Además, estuve mirando todos estos protocolos y tratando (sin éxito) de relacionarlos con mis datos capturados, en lugar de solo mirar los datos en sí. Tu respuesta me hizo hacer eso, y todo resultó ser bastante fácil.