Engenharia reversa do protocolo de transceptor USB USB IR


Me deparei com um transceptor de microUSB IR chinês e havia um desejo de conectá-lo a um computador com Windows. O transceptor é um dispositivo muito compacto com um conector Micro USB. A única opção "oficial" para trabalhar é através de um aplicativo Android chamado ZaZaRemote.

Quando conectado a um computador por um adaptador, o dispositivo era definido como um dispositivo USB compatível com HID \ VID_10C4 & PID_8468. A busca por esse ID no Google não deu nenhum resultado e eu tive que fazer a reversão do protocolo.

HID inválido


A classe do dispositivo foi definida como USB \ Class_03 & SubClass_FF & Prot_FF. Class_03 - Dispositivo HID, SubClass_FF - específico do fornecedor. O driver hidusb.sys foi instalado automaticamente pelo sistema. Você pode trabalhar com este driver através da API HID .

Tendo esboçado um pequeno programa simples, pude obter várias informações sobre o 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

Acontece que a troca é realizada em blocos de 61 bytes no máximo, existem 2 interfaces InputValue e 2 OutputValue. A função HidP_GetValueCaps retorna informações mais detalhadas sobre eles:

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

Desses dados, os mais interessantes são ReportID - o ID do relatório (na verdade, o pacote de dados) e ReportCount - seu tamanho. Os dados podem ser enviados e recebidos usando as funções HidD_SetOutputReport e HidD_GetInputReport, respectivamente. Tendo experimentado essas funções, com ID de relatório e tamanho de dados diferentes, não consegui obter uma troca bem-sucedida. Após o tráfego via USB usando o USBPcap , descobri que os dados nem tentavam ser transmitidos. Havia uma suspeita de que isso é algum tipo de HID incorreto.


A solicitação SET_REPORT ficou sem resposta

Estornando o aplicativo ZaZaRemote


No arquivo APK deste aplicativo, encontrei a biblioteca libtiqiaa_dev_usb.so. Exporta as seguintes funções:

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 julgar pelo nome, eles percebem a troca com o dispositivo. Fragmentos semelhantes à chamada de funções virtuais são frequentemente encontrados em seu código:

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

O registro R0 é o primeiro argumento para esta função. Essas funções são chamadas apenas do código Java classes.dex. Ao descompilar esse arquivo, obtemos seu 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[]);

O código Java foi ofuscado, mas alguns nomes ainda sobrevivem. Em relação a essa biblioteca, o ofuscador estragou apenas o nome da última função; no entanto, quase todos os nomes no código Java principal foram corrompidos.

Tendo estudado um pouco o código descompilado Java, encontrei as seguintes linhas:

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);

Os logs de depuração são bons amigos de um engenheiro reverso.

Para chamar métodos nativos a partir do código Java, o mecanismo Java Native Interface é usado . A função exportada deve se parecer com:

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

Agora você pode definir o tipo de funções no 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);

Agora, o descompilador HexRays reconhece chamadas JNI e o código se torna muito mais compreensível, por exemplo, a chamada acima é descompilada como:

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

Resultado da descompilação da função 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;
}

O código é elementar, o formato da mensagem é óbvio: começa com a assinatura "ST" e, em seguida, vem o byte do tipo de comando - um dos caracteres {'L', 'R', 'H', 'O', 'C', 'V', 'S '}, o byte cmdId (apenas um identificador incremental para corresponder ao comando e a resposta) e termina com a assinatura "EN". A mensagem gerada é enviada por sub_3528.

Código de Função 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;
}


Esse recurso é um pouco mais complicado. Pode-se observar que o comprimento máximo da mensagem enviada é limitado a 1024 bytes. A mensagem é dividida em fragmentos. O fragmento consiste em 5 bytes do cabeçalho e no máximo 56 bytes de dados - um total de 61 bytes. Estrutura do cabeçalho:

  • buf [0] = 2 é uma constante. Lembre-se do ReportID 2? Parece que é isso. E o ReportCount 60 - o tamanho dos dados restantes - também é o mesmo.
  • buf [1] = fragmSize + 3 - tamanho dos dados do fragmento + 3, ou seja o tamanho é calculado a partir do byte após esta variável.
  • buf [2] = UsbPackCounter é apenas um contador, 1..15.
  • O valor de buf [3] calculado pela expressão de fantasia é apenas o número de fragmentos, você pode reescrevê-lo como:

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

Fragmentos formados são enviados por meio de uma chamada para CallIntMethod. Tem o seguinte tipo:

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

Pode-se observar que o HexRays não conseguiu esse tempo - apenas nos argumentos v6 = JNIEnv * env. No entanto, no código do assembler, tudo está no lugar:

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

methodID é armazenado na variável dword_B2A4. Vamos ver de onde veio: A



gravação é feita nas funções usbOpen e usbClose. Obviamente, estamos interessados ​​em usbOpen.

O fragmento desejado:

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

Portanto, este é o método UsbEndpoint :: bulkTransfer. E nada - relacionado ao HID!

Agora vamos ver como os códigos IR são transmitidos - função usbSendIR.

É bastante grande, mas a área que forma a mensagem com a equipe é compreensível.

Fragmento da função 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';

Como em outros comandos, tudo começa com "ST", seguido por cmdId e o código de comando 'D', o byte que determina a frequência - se o argumento for freq> 255 - é pesquisado na tabela de frequência IrFreqTable, caso contrário, é copiado diretamente. Então os dados vão e tudo termina com "EN".

A função com o nome ofuscado “d” acabou sendo um analisador de dados recebidos.

Alterar o driver e trabalhar com bulkTransfer


Tendo estudado a API HID, descobri que ela basicamente não fornece a capacidade de usar bulkTransfer - então você precisa alterar o driver. O driver WinUsb é adequado para trabalhar com este dispositivo .
Depois de escrever o arquivo inf, mudei o driver para WinUsb e tentei enviar comandos. Tudo funcionou e uma reação foi recebida do dispositivo - em resposta ao envio de comandos (via WinUsb_WritePipe), uma resposta de um formato semelhante veio.

Troca de despejo com ZaZaRemote


Apesar dos sucessos anteriores, até agora não consegui alcançar a principal coisa - forçar o dispositivo a transmitir comandos de IR. O aplicativo era muito grande e confuso e só queria despejar o tráfego USB. No entanto, como fazer isso no caso de um aplicativo Android? A entrada foi encontrada como Android-x86 no VirtualBox-e. Apesar de o x86 nunca ser ARM, ele permite executar binários nativos do ARM através do mecanismo NativeBridge. Ao instalar e configurar o software necessário, consegui fazer com que esse aplicativo funcionasse no VirtualBox.

As permissões solicitadas definitivamente inspiram confiança neste software.


Ao iniciar o aplicativo e configurar o encaminhamento USB, consegui detectar o tráfego USB. Então, recebi uma sequência de comandos para inicializar a recepção e transmissão de comandos IR, bem como dados de amostra de pacotes IR.

Descobriu-se que o dispositivo é capaz não apenas de transmitir, mas também de receber comandos de infravermelho, mas a recepção funcionou quase tão - a uma distância de 10 cm e depois todas as outras vezes.

Exemplo de troca
- :

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..............

Verificou-se que o dispositivo tem problemas com o tamanho dos dados a enviar - além da resposta, qualquer lixo é derramado - geralmente o que resta do pacote anterior.

O estudo do despejo e os experimentos subsequentes tornaram possível descobrir as funções de todas as equipes:

  • 'V' - Versão - solicitação de uma versão - meu dispositivo produz um GUID zero;
  • 'L' - IdleMode - modo de espera - nesse modo, o dispositivo fica localizado após o fornecimento de energia ou segue esse comando;
  • 'S' — SendMode — — ;
  • 'R' — RecvMode — — ;
  • 'D' — Data — — , — ;
  • 'O' — Output — — , — / ;
  • 'C' — Cancel — — , 'O';
  • 'H' — Unknown — .


Depois de receber um despejo de comandos de controle e parcelas de IR, pude implementar o controle completo do dispositivo - recebendo e transmitindo um sinal de IR. No entanto, para sintetizar um sinal IR arbitrário, foi necessário determinar o formato em que ele é codificado. Para fazer isso, conectei um fotodetector de infravermelho a um osciloscópio e comecei a examinar os sinais enviados. Através da experimentação, descobri o formato de codificação: o bit alto de cada byte determina se o transmissor está ligado ou desligado, e os 7 bits inferiores determinam o tempo. A unidade de tempo foi de 16 microssegundos. Por exemplo: 8A - o transmissor é ligado por 160 microssegundos; 8A 05 FF 83 - 160 μs ativado, pausa 80 μs e 2,08 ms ativado.

Quando o transmissor é ligado, o LED pulsa a uma frequência de ~ 36,64 kHz. Teoricamente, essa frequência deve ser determinada pelo argumento freq do comando usbSendIR, mas experimentos demonstraram que o dispositivo não responde a esse argumento. No entanto, meus eletrodomésticos normalmente aceitavam os sinais desse transceptor.
O formato dos dados gravados pelo dispositivo no modo de recebimento mostrou-se semelhante.

Classe TiqiaaUsbIr e controle IR


Eu implementei o controle do transceptor na forma de uma classe C ++ TiqiaaUsbIr e escrevi um programa simples do CaptureIR QT. Além das funções de recepção e transmissão de sinais IR, implementei a síntese e decodificação de sinais usando o protocolo NEC. Este protocolo é usado, por exemplo, em controles remotos de TVs LG. Também implementei salvar e carregar sinais de IR no formato original e no formato LIRC. Havia uma idéia de criar um módulo para o WinLirc, mas ele era uma API torta e não totalmente implementada, então adiei essa idéia por enquanto.

Fontes e programa compilado podem ser baixados aqui .

Um exemplo de uso da classe TiqiaaUsbIr:

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

Sinal de inicialização capturado:



É sintetizado:



Durante a captura, algo deu errado:



Sumário


No processo de pesquisa, o protocolo USB do transceptor IR Tiqiaa Tview foi completamente restaurado, um arquivo inf do driver e o software para trabalhar com ele foram gravados.

O transceptor de infravermelho considerado é um dispositivo muito barato, acessível e compacto (US $ 5 na Ali, dimensões 15 x 10 x 5 mm) para controlar eletrodomésticos e estudar seus protocolos de infravermelho. Infelizmente, o controle da frequência do transmissor acabou sendo inoperante, o que, no meu caso, não causou problemas, mas é possível que exista uma técnica com receptores mais exigentes.

O modo de recepção, devido ao raio escasso e à baixa confiabilidade de captura, não é adequado para uso como um receptor infravermelho completo - um registro de distâncias de captura bem-sucedidas de ~ 30 cm, enquanto o controle remoto é direcionado exatamente para o receptor e nem todos os sinais de foco são normalmente capturados. No entanto, é útil para capturar sinais e estudar protocolos para controles remotos de IR.

Bônus


Códigos IR interessantes para TVs LG: PS Estou procurando informações sobre o LG AccessUSB, mais detalhes aqui .

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



All Articles