Ingeniería inversa del protocolo chino de transceptor IR USB


Me encontré con un transceptor IR MicroUSB chino, y había un deseo de conectarlo a una computadora con Windows. El transceptor es un dispositivo muy compacto con un conector Micro USB. La única opción "oficial" para trabajar con él es a través de una aplicación de Android llamada ZaZaRemote.

Cuando se conectó a una computadora a través de un adaptador, el dispositivo se definió como un dispositivo USB compatible con HID \ VID_10C4 y PID_8468. Buscar en Google por esta identificación no dio ningún resultado, y tuve que hacer la reversión del protocolo.

HID inválido


La clase de dispositivo se definió como USB \ Class_03 y SubClass_FF y Prot_FF. Clase_03: dispositivo HID, Subclase_FF: específico del proveedor. El controlador instaló automáticamente el controlador hidusb.sys. Puede trabajar con este controlador a través de la API HID .

Después de esbozar un pequeño programa simple, pude obtener información variada sobre el dispositivo:

HidD_GetManufacturerString: "Tiqiaa"
HidD_GetProductString: "Tview"

HidP_GetCaps:
  Usage 1
  UsagePage ff00
  InputReportByteLength 61
  OutputReportByteLength 61
  FeatureReportByteLength 0
  NumberLinkCollectionNodes 1
  NumberInputButtonCaps 0
  NumberInputValueCaps 2
  NumberInputDataIndices 2
  NumberOutputButtonCaps 0
  NumberOutputValueCaps 2
  NumberOutputDataIndices 2
  NumberFeatureButtonCaps 0
  NumberFeatureValueCaps 0
  NumberFeatureDataIndices 0

Resulta que el intercambio se realiza en bloques de 61 bytes como máximo, hay 2 interfaces InputValue y 2 OutputValue. La función HidP_GetValueCaps devuelve información más detallada sobre ellos:

HidP_GetValueCaps(HidP_Input):
    UsagePage ff00
    ReportID fe
    LinkUsage 1
    LinkUsagePage ff00
    BitSize 8
    ReportCount 8

    UsagePage ff00
    ReportID 1
    LinkUsage 1
    LinkUsagePage ff00
    BitSize 8
    ReportCount 60

HidP_GetValueCaps(HidP_Output):
    UsagePage ff00
    ReportID fd
    LinkUsage 1
    LinkUsagePage ff00
    BitSize 8
    ReportCount 8

    UsagePage ff00
    ReportID 2
    LinkUsage 1
    LinkUsagePage ff00
    BitSize 8
    ReportCount 60

De estos datos, los más interesantes son ReportID - la ID del informe (de hecho, el paquete de datos) y ReportCount - su tamaño. Los datos se pueden enviar y recibir utilizando las funciones HidD_SetOutputReport e HidD_GetInputReport, respectivamente. Después de experimentar con estas funciones, con ReportID y tamaños de datos diferentes, no pude lograr un intercambio exitoso. Después del tráfico a través de USB usando USBPcap , descubrí que los datos ni siquiera intentaban transmitirse. Se sospechaba que se trataba de algún tipo de HID incorrecto.


La solicitud SET_REPORT quedó sin respuesta

Invertir la aplicación ZaZaRemote


En el archivo APK de esta aplicación, encontré la biblioteca libtiqiaa_dev_usb.so. Exporta las siguientes funciones:

Java_com_icontrol_dev_TiqiaaUsbController_usbOpen
Java_com_icontrol_dev_TiqiaaUsbController_usbClose
Java_com_icontrol_dev_TiqiaaUsbController_usbSendCmd
Java_com_icontrol_dev_TiqiaaUsbController_usbSendIR
Java_com_icontrol_dev_TiqiaaUsbController_d

A juzgar por el nombre, se dan cuenta del intercambio con el dispositivo. Los fragmentos similares a las funciones virtuales de llamada a menudo se encuentran en su código:

LDR     R3, [R0]
LDR     R3, [R3,#0x18]
BLX     R3

El registro R0 es el primer argumento para esta función. Estas funciones se invocan solo desde el código Java classes.dex. Al descompilar este archivo, obtenemos su tipo:

private native boolean usbOpen(Context context);
private native void usbClose();
private native boolean usbSendCmd(UsbDeviceConnection usbdeviceconnection, UsbEndpoint usbendpoint, int j, int k);
private native boolean usbSendIR(Context context, UsbDeviceConnection usbdeviceconnection, UsbEndpoint usbendpoint, int j, byte abyte0[], int k);
private native IControlIRData d(byte abyte0[]);

El código Java estaba ofuscado, pero algunos nombres aún sobreviven. En relación con esta biblioteca, el ofuscador solo estropeó el nombre de la última función, sin embargo, casi todos los nombres en el código Java principal estaban dañados.

Después de estudiar un poco el código descompilado de Java, encontré las siguientes líneas:

com.tiqiaa.icontrol.e.i.a("DeviceHolder", (new StringBuilder("send......cmdType=")).append(i1).append(",cmdId=").append(j1).toString());
boolean flag1 = a.b(i1, j1);

com.tiqiaa.icontrol.e.i.d("DeviceHolder", (new StringBuilder("send....................freq=")).append(i1).append(",cmdId=").append(j1).append(",buffer.length=").append(abyte0.length).append(" , device = ").append(a).toString());
boolean flag1 = a.a(i1, abyte0, j1);

Los registros de depuración son buenos amigos de un ingeniero inverso.

Para llamar a métodos nativos desde el código Java, se utiliza el mecanismo Java Native Interface . La función exportada debería verse así:

extern "C" JNIEXPORT void JNICALL Java_ClassName_MethodName(JNIEnv *env, jobject obj, <java arguments>)

Ahora puede establecer el tipo de funciones en la IDA:

bool __cdecl Java_com_icontrol_dev_TiqiaaUsbController_usbOpen(JNIEnv *env, jobject obj, struct Context *context);
void __cdecl Java_com_icontrol_dev_TiqiaaUsbController_usbClose(JNIEnv *env, jobject obj);
bool __cdecl Java_com_icontrol_dev_TiqiaaUsbController_usbSendCmd(JNIEnv *env, jobject obj, struct UsbDeviceConnection *usbdeviceconnection, struct UsbEndpoint *usbendpoint, int cmdType, int cmdId);
bool __cdecl Java_com_icontrol_dev_TiqiaaUsbController_usbSendIR(JNIEnv *env, jobject obj, struct Context *context, struct UsbDeviceConnection *usbdeviceconnection, struct UsbEndpoint *usbendpoint, int freq, jbyte buffer, int cmdId);
struct IControlIRData *__cdecl Java_com_icontrol_dev_TiqiaaUsbController_d(JNIEnv *env, jobject obj, jbyteArray buffer);

Ahora el descompilador HexRays reconoce las llamadas JNI y el código se vuelve mucho más comprensible, por ejemplo, la llamada anterior se descompila como:

v5 = ((int (*)(void))(*env)->FindClass)();

Resultado de descompilación de la función UsbSendCmd
bool __cdecl Java_com_icontrol_dev_TiqiaaUsbController_usbSendCmd(JNIEnv *env, jobject obj, struct UsbDeviceConnection *usbdeviceconnection, struct UsbEndpoint *usbendpoint, int cmdType, int cmdId)
{
  char v6; // r5@5
  bool result; // r0@12
  char data[24]; // [sp+8h] [bp-18h]@12

  dword_B57C = 0;
  if ( UsbEndpoint_bulkTransfer )
  {
    if ( usbdeviceconnection )
    {
      if ( usbendpoint )
      {
        switch ( cmdType )
        {
          case 0:
            v6 = 'L';
            goto LABEL_12;
          case 2:
            v6 = 'R';
            goto LABEL_12;
          case 3:
            v6 = 'H';
            goto LABEL_12;
          case 4:
            v6 = 'O';
            goto LABEL_12;
          case 6:
            v6 = 'C';
            goto LABEL_12;
          case 7:
            v6 = 'V';
            goto LABEL_12;
          case 1:
            v6 = 'S';
LABEL_12:
            data[0] = 'S';
            data[1] = 'T';
            data[3] = v6;
            data[2] = cmdId;
            data[4] = 'E';
            data[5] = 'N';
            result = sub_3528(env, usbdeviceconnection, usbendpoint, data, 6);
            break;
          default:
            result = 0;
            break;
        }
      }
      else
      {
        result = 0;
      }
    }
    else
    {
      result = 0;
    }
  }
  else
  {
    result = UsbEndpoint_bulkTransfer;
  }
  return result;
}

El código es elemental, el formato del mensaje es obvio: comienza con la firma "ST", luego viene el byte del tipo de comando: uno de los caracteres {'L', 'R', 'H', 'O', 'C', 'V', 'S '}, luego el byte cmdId (solo un identificador incremental para que coincida con el comando y la respuesta) y termina con la firma "EN". El mensaje generado es enviado por sub_3528.

Código de función sub_3528
int __cdecl sub_3528(JNIEnv *env, struct UsbDeviceConnection *usbdeviceconnection, struct UsbEndpoint *usbendpoint, void *data, int size)
{
  JNIEnv v5; // r1@1
  JNIEnv *v6; // r4@1
  int result; // r0@2
  int v8; // r2@3
  int tsize; // r7@5
  size_t fragmSize; // r5@9
  int v11; // r0@11
  JNIEnv v12; // r3@11
  int v13; // r6@11
  int rdOffs; // [sp+10h] [bp-80h]@7
  int v15; // [sp+14h] [bp-7Ch]@15
  int jbyteArray; // [sp+18h] [bp-78h]@1
  int fragmCnt; // [sp+1Ch] [bp-74h]@7
  char *_data; // [sp+28h] [bp-68h]@1
  char buf[64]; // [sp+34h] [bp-5Ch]@7
  int _stack_chk_guard; // [sp+74h] [bp-1Ch]@1

  _data = (char *)data;
  v5 = *env;
  v6 = env;
  _stack_chk_guard = ::_stack_chk_guard;
  jbyteArray = ((int (*)(void))v5->NewByteArray)();
  if ( jbyteArray )
  {
    v8 = UsbPackCounter + 1;
    if ( UsbPackCounter + 1 > 15 )
      v8 = 1;
    tsize = size;
    UsbPackCounter = v8;
    if ( size > 1024 )
      tsize = 1024;
    j_j_memset(buf, 0, 0x40u);
    buf[0] = 2;
    buf[3] = (tsize / 56 & 0x7F)
           + ((((tsize + -56 * (tsize / 56 & 0x7F)) >> 31) - (tsize + -56 * (tsize / 56 & 0x7Fu))) >> 31);
    rdOffs = 0;
    fragmCnt = 0;
    buf[2] = UsbPackCounter;
    while ( 1 )
    {
      if ( rdOffs >= tsize )
        goto LABEL_25;
      fragmCnt = (fragmCnt + 1) & 0xFF;
      fragmSize = tsize - rdOffs;
      if ( tsize - rdOffs > 56 )
        fragmSize = 56;
      buf[1] = fragmSize + 3;
      buf[4] = fragmCnt;
      j_j_memcpy(&buf[5], &_data[rdOffs], fragmSize);
      ((void (__fastcall *)(JNIEnv *, int, _DWORD, signed int))(*v6)->SetByteArrayRegion)(v6, jbyteArray, 0, 61);
      v11 = ((int (__fastcall *)(JNIEnv *))(*v6)->ExceptionCheck)(v6);
      v12 = *v6;
      v13 = v11;
      if ( v11 )
      {
        ((void (__fastcall *)(JNIEnv *))v12->ExceptionClear)(v6);
        v13 = 0;
        goto return_r6_del;
      }
      if ( !dword_B2A4 )
      {
LABEL_25:
        v13 = 1;
        goto return_r6_del;
      }
      v15 = ((int (__fastcall *)(JNIEnv *))v12->CallIntMethod)(v6);
      if ( ((int (__fastcall *)(JNIEnv *))(*v6)->ExceptionCheck)(v6) )
      {
        ((void (__fastcall *)(JNIEnv *))(*v6)->ExceptionClear)(v6);
        goto return_r6_del;
      }
      if ( v15 < 0 )
        break;
      rdOffs += fragmSize;
    }
    v13 = 0;
return_r6_del:
    ((void (__fastcall *)(JNIEnv *, int))(*v6)->DeleteLocalRef)(v6, jbyteArray);
    result = v13;
  }
  else
  {
    ((void (__fastcall *)(JNIEnv *))(*v6)->ExceptionClear)(v6);
    result = 0;
  }
  if ( _stack_chk_guard != ::_stack_chk_guard )
    j_j___stack_chk_fail(result);
  return result;
}


Esta característica es un poco más complicada. Se puede ver que la longitud máxima del mensaje enviado está limitada a 1024 bytes. El mensaje está dividido en fragmentos. El fragmento consta de 5 bytes del encabezado y un máximo de 56 bytes de datos, un total de 61 bytes. Estructura del encabezado:

  • buf [0] = 2 es una constante. ¿Recuerdas ReportID 2? Parece que esto es todo. Y ReportCount 60, el tamaño de los datos restantes, también es el mismo.
  • buf [1] = fragmSize + 3 - tamaño de datos de fragmento + 3, es decir El tamaño se calcula a partir del byte que sigue a esta variable.
  • buf [2] = UsbPackCounter es solo un contador, 1..15.
  • El valor de buf [3] calculado por la expresión de fantasía es solo el número de fragmentos, puede reescribirlo como:

    buf[3] = tsize / 56;
    if (tsize % 56) buf[3]++;
  • buf [4] = fragmCnt - número de fragmento, 1..buf [3].

Los fragmentos formados se envían a través de una llamada a CallIntMethod. Tiene el siguiente tipo:

jint (JNICALL *CallIntMethod)(JNIEnv *env, jobject obj, jmethodID methodID, ...);

Se puede ver que HexRays no logró esta vez, en los argumentos solo v6 = JNIEnv * env. Sin embargo, en el código del ensamblador todo está en su lugar:

LDR     R2, [R2,#(dword_B2A4 - 0xB284)] ; jmethodID methodID

methodID se almacena en la variable dword_B2A4. Veamos de dónde vino: la



grabación se realiza en las funciones usbOpen y usbClose. Obviamente, estamos interesados ​​en usbOpen.

El fragmento deseado:

v27 = ((int (__fastcall *)(JNIEnv *, int, const char *, const char *))(*v4)->GetMethodID)(
        v4,
        v26,
        "bulkTransfer",
        "(Landroid/hardware/usb/UsbEndpoint;[BII)I");
dword_B2A4 = v27;

Entonces este es el método UsbEndpoint :: bulkTransfer. ¡Y nada, relacionado con HID!

Ahora veamos cómo se transmiten los códigos IR: función usbSendIR.

Es bastante grande, pero el área que forma el mensaje con el equipo es comprensible.

Fragmento de la función usbSendIR
  buf[0] = 'S';
  buf[1] = 'T';
  buf[2] = cmdId;
  buf[3] = 'D';
  if ( freq > 255 )
  {
    LOBYTE(v36) = 0;
    v37 = -1;
    v38 = 0;
    while ( 1 )
    {
      v39 = (freq - IrFreqTable[v38] + ((freq - IrFreqTable[v38]) >> 31)) ^ ((freq - IrFreqTable[v38]) >> 31);
      if ( v37 != -1 && v39 >= v37 )
      {
        v39 = v37;
      }
      else
      {
        LOBYTE(v36) = v38;
        if ( !v39 )
          break;
      }
      if ( ++v38 == 30 )
        break;
      v37 = v39;
    }
  }
  else
  {
    v36 = (unsigned int)(freq - 1) <= 0x1C ? freq : 0;
  }
  buf[4] = v36;
  v40 = &buf[v22];
  v40[5] = 'E';
  v40[6] = 'N';

Al igual que con otros comandos, todo comienza con "ST", seguido de cmdId y el código de comando 'D', luego el byte que determina la frecuencia, si el argumento es freq> 255, se busca en la tabla de frecuencias IrFreqTable, de lo contrario, se copia directamente. Luego los datos se van y todo termina con "EN".

La función con el nombre ofuscado "d" resultó ser un analizador de datos recibidos.

Cambiar el controlador y trabajar con bulkTransfer


Después de estudiar la API HID, descubrí que básicamente no proporciona la capacidad de usar bulkTransfer, por lo que debe cambiar el controlador. El controlador WinUsb es adecuado para trabajar con este dispositivo .
Después de escribir el archivo inf, cambié el controlador a WinUsb e intenté enviar comandos. Todo funcionó y se recibió una reacción del dispositivo: en respuesta al envío de comandos (a través de WinUsb_WritePipe), apareció una respuesta de un formato similar.

Volcado de intercambio con ZaZaRemote


A pesar de los éxitos anteriores, hasta ahora no he podido lograr lo principal: forzar al dispositivo a transmitir comandos IR. La aplicación era demasiado grande y confusa y solo quería volcar el tráfico USB. Sin embargo, ¿cómo hacer esto en el caso de una aplicación de Android? La entrada se encontró como Android-x86 en VirtualBox-e. A pesar de que x86 nunca es ARM, le permite ejecutar binarios ARM nativos a través del mecanismo NativeBridge. Al instalar y configurar el software necesario, pude hacer que esta aplicación funcione en VirtualBox.

Los permisos solicitados definitivamente inspiran confianza en este software.


Al iniciar la aplicación y configurar el reenvío USB, pude detectar el tráfico USB. Entonces obtuve una secuencia de comandos para inicializar la recepción y transmisión de comandos IR, así como datos de muestra de paquetes IR.

Resultó que el dispositivo es capaz no solo de transmitir, sino también de recibir comandos IR, sino que la recepción funcionó de manera regular, desde una distancia de 10 cm, y luego en cualquier otro momento.

Ejemplo de intercambio
- :

OUT:
0040 02 09 01 01 01 53 54 12 56 45 4e .....ST.VEN

IN:
0040 01 30 07 01 01 53 54 12 56 30 01 30 30 30 30 30 .0...ST.V0.00000
0050 30 30 30 2d 30 30 30 30 2d 30 30 30 30 2d 30 30 000-0000-0000-00
0060 30 30 2d 30 30 30 30 30 30 30 30 30 30 30 31 09 00-000000000001.
0070 45 4e ff ff ff df ff f9 ef ff df ff bf fb ff EN.............

OUT:
0040 02 09 02 01 01 53 54 13 53 45 4e .....ST.SEN

IN:
0040 01 0a 08 01 01 53 54 13 53 09 45 4e 30 30 30 30 .....ST.S.EN0000
0050 30 30 30 2d 30 30 30 30 2d 30 30 30 30 2d 30 30 000-0000-0000-00
0060 30 30 2d 30 30 30 30 30 30 30 30 30 30 30 31 09 00-000000000001.
0070 45 4e ff ff ff df ff f9 ef ff df ff bf fb ff EN.............

OUT:
0040 02 3b 03 02 01 53 54 14 44 00 ff ff ff ff b7 7f .;...ST.D.......
0050 7f 1b a3 23 a3 23 a3 69 a3 23 a3 23 a3 23 a3 23 ...#.#.i.#.#.#.#
0060 a3 23 a3 69 a3 69 a3 23 a3 69 a3 69 a3 69 a3 69 .#.i.i.#.i.i.i.i
0070 a3 69 a3 69 a3 23 a3 23 a3 23 a3 69 a3 .i.i.#.#.#.i.

OUT:
0040 02 2f 03 02 02 23 a3 23 a3 23 a3 23 a3 69 a3 69 ./...#.#.#.#.i.i
0050 a3 69 a3 23 a3 69 a3 69 a3 69 a3 7f 7f 7f 7f 7f .i.#.i.i.i......
0060 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 57 45 ..............WE
0070 4e N

IN:
0040 01 0a 09 01 01 53 54 14 4f 09 45 4e 30 30 30 30 .....ST.O.EN0000
0050 30 30 30 2d 30 30 30 30 2d 30 30 30 30 2d 30 30 000-0000-0000-00
0060 30 30 2d 30 30 30 30 30 30 30 30 30 30 30 31 09 00-000000000001.
0070 45 4e ff ff ff df ff f9 ef ff df ff bf fb ff EN.............

- :

OUT:
0040 02 09 01 01 01 53 54 17 56 45 4e .....ST.VEN

IN:
0040 01 30 0c 01 01 53 54 17 56 30 01 30 30 30 30 30 .0...ST.V0.00000
0050 30 30 30 2d 30 30 30 30 2d 30 30 30 30 2d 30 30 000-0000-0000-00
0060 30 30 2d 30 30 30 30 30 30 30 30 30 30 30 31 09 00-000000000001.
0070 45 4e ff ff ff df ff f9 ef ff df ff bf fb ff EN.............

OUT:
0040 02 09 02 01 01 53 54 18 53 45 4e .....ST.SEN

0040 01 0a 0d 01 01 53 54 18 53 09 45 4e 30 30 30 30 .....ST.S.EN0000
0050 30 30 30 2d 30 30 30 30 2d 30 30 30 30 2d 30 30 000-0000-0000-00
0060 30 30 2d 30 30 30 30 30 30 30 30 30 30 30 31 09 00-000000000001.
0070 45 4e ff ff ff df ff f9 ef ff df ff bf fb ff EN.............

OUT:
0040 02 09 03 01 01 53 54 19 52 45 4e .....ST.REN

0040 01 0a 0e 01 01 53 54 19 52 13 45 4e 30 30 30 30 .....ST.R.EN0000
0050 30 30 30 2d 30 30 30 30 2d 30 30 30 30 2d 30 30 000-0000-0000-00
0060 30 30 2d 30 30 30 30 30 30 30 30 30 30 30 31 09 00-000000000001.
0070 45 4e ff ff ff df ff f9 ef ff df ff bf fb ff EN.............

OUT:
0040 02 09 04 01 01 53 54 1a 43 45 4e .....ST.CEN

IN:
0040 01 0a 0f 01 01 53 54 1a 43 13 45 4e 30 30 30 30 .....ST.C.EN0000
0050 30 30 30 2d 30 30 30 30 2d 30 30 30 30 2d 30 30 000-0000-0000-00
0060 30 30 2d 30 30 30 30 30 30 30 30 30 30 30 31 09 00-000000000001.
0070 45 4e ff ff ff df ff f9 ef ff df ff bf fb ff EN.............

OUT:
0040 02 09 05 01 01 53 54 1b 4f 45 4e .....ST.OEN

IN:
0040 01 3b 01 0e 01 53 54 00 44 ff ff ff ff ba 7f 7f .;...ST.D.......
0050 19 a4 21 a4 21 a4 68 a4 22 a4 21 a4 22 a4 22 a4 ..!.!.h.".!.".".
0060 22 a4 68 a4 68 a4 21 a4 68 a4 68 a4 68 a4 68 a4 ".h.h.!.h.h.h.h.
0070 68 a4 68 a4 22 a4 22 a4 22 a4 68 a4 22 fb ff h.h.".".".h."..
.....
0040 01 2f 01 0e 0e 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f ./..............
0050 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f ................
0060 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 3e 13 45 .............>.E
0070 4e 82 02 82 02 82 7f 7f 7f 7f 7f 7f 7f fb ff N..............

Resultó que el dispositivo tiene problemas con el tamaño de los datos a enviar, además de la respuesta, se vierte cualquier basura, generalmente lo que queda del paquete anterior.

Estudiar el vertedero y los experimentos posteriores permitieron descubrir las funciones de todos los equipos:

  • 'V' - Versión - solicitud de una versión - mi dispositivo produce un GUID cero;
  • 'L' - IdleMode - modo de espera - en este modo, el dispositivo se ubica después de que se suministra energía, o pasa por este comando;
  • 'S' — SendMode — — ;
  • 'R' — RecvMode — — ;
  • 'D' — Data — — , — ;
  • 'O' — Output — — , — / ;
  • 'C' — Cancel — — , 'O';
  • 'H' — Unknown — .


Después de recibir un volcado de comandos de control y paquetes IR, pude implementar un control completo del dispositivo, recibiendo y transmitiendo una señal IR. Sin embargo, para sintetizar una señal IR arbitraria, fue necesario determinar el formato en el que está codificada. Para hacer esto, conecté un fotodetector IR a un osciloscopio y comencé a examinar las señales enviadas. A través de la experimentación, descubrí el formato de codificación: el bit alto de cada byte determina si el transmisor está encendido o apagado, y los 7 bits inferiores determinan el tiempo. La unidad de tiempo fue de 16 microsegundos. Por ejemplo: 8A: el transmisor se enciende durante 160 microsegundos; 8A 05 FF 83 - 160 μs encendido, pausa 80 μs, 2.08 ms encendido.

Cuando se enciende el transmisor, el LED pulsa a una frecuencia de ~ 36.64 kHz. Teóricamente, esta frecuencia debería estar determinada por el argumento freq del comando usbSendIR, pero los experimentos han demostrado que el dispositivo no responde a este argumento en absoluto. Sin embargo, mis electrodomésticos normalmente aceptaban las señales de este transceptor.
El formato de los datos grabados por el dispositivo en el modo de recepción resultó ser similar.

TiqiaaUsbIr Class y control IR


Implementé el control del transceptor en forma de una clase TiqiaaUsbIr de C ++ y escribí un simple programa CaptureIR QT. Además de las funciones de recibir y transmitir señales IR, implementé la síntesis y decodificación de señales usando el protocolo NEC. Este protocolo se usa, por ejemplo, en controles remotos de televisores LG. También implementé guardar y cargar señales IR en el formato original y el formato LIRC. Hubo una idea para hacer un módulo para WinLirc, pero resultó ser una API torcida y no completamente implementada, por lo que he pospuesto esta idea por ahora.

Las fuentes y el programa compilado se pueden descargar aquí .

Un ejemplo de uso de la clase TiqiaaUsbIr:

std::vector<std::string> IrDev;
TiqiaaUsbIr::EnumDevices(IrDev); //  
TiqiaaUsbIr Ir;
Ir.Open(IrDev[0].c_str()); //  
Ir.SendNecSignal(0x0408); //   (LG POWER)
Ir.Close();

Señal de encendido capturada:



se sintetiza:



durante la captura, algo salió mal:



Resumen


En el proceso de investigación, el protocolo USB del transceptor IR Tiqiaa Tview fue completamente restaurado, se escribió un archivo inf del controlador y el software para trabajar con él.

El transceptor IR considerado es un dispositivo muy barato, asequible y compacto ($ 5 en Ali, dimensiones 15 x 10 x 5 mm) para controlar electrodomésticos y estudiar sus protocolos IR. Desafortunadamente, el control de la frecuencia del transmisor resultó inoperante, lo que en mi caso no causó problemas, pero es posible que exista una técnica con receptores más delicados.

El modo de recepción, debido al radio exiguo y la baja confiabilidad de captura, no es adecuado para su uso como receptor IR completo: un registro de distancias de captura exitosas de ~ 30 cm, mientras que el control remoto apunta exactamente al receptor, e incluso todas las señales de enfoque normalmente no se capturan. Sin embargo, es útil para capturar señales y estudiar protocolos para controles remotos IR.

Prima


Códigos IR interesantes para televisores LG: PD. Estoy buscando información sobre LG AccessUSB, más detalles aquí .

POWER 0408
POWERON 04C4
POWEROFF 04C5
IN_STOP 04FA
IN_START 04FB
POWERONLY 04FE
EZ_ADJUST 04FF



All Articles