Ingénierie inverse du protocole émetteur-récepteur infrarouge USB chinois


Je suis tombé sur un émetteur-récepteur IR MicroUSB chinois, et il y avait un désir de le connecter à un ordinateur avec Windows. L'émetteur-récepteur est un appareil très compact avec un connecteur Micro USB. La seule option "officielle" pour travailler avec elle est via une application Android appelée ZaZaRemote.

Lorsqu'il est connecté à un ordinateur via un adaptateur, le périphérique a été défini comme un périphérique USB compatible HID \ VID_10C4 & PID_8468. Googler par cette ID n'a donné aucun résultat, et j'ai dû faire une inversion de protocole.

HID non valide


La classe de périphérique a été définie comme USB \ Class_03 & SubClass_FF & Prot_FF. Class_03 - Dispositif HID, SubClass_FF - spécifique au fournisseur. Le pilote hidusb.sys a été automatiquement installé par le système. Vous pouvez travailler avec ce pilote via l' API HID .

Après avoir esquissé un petit programme simple, j'ai pu obtenir diverses informations sur l'appareil:

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

Il s'avère que l'échange s'effectue en blocs de 61 octets maximum, il existe 2 interfaces InputValue et 2 OutputValue. La fonction HidP_GetValueCaps renvoie des informations plus détaillées à leur sujet:

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

Parmi ces données, les plus intéressantes sont ReportID - l'ID du rapport (en fait, le paquet de données) et ReportCount - sa taille. Les données peuvent être envoyées et reçues à l'aide des fonctions HidD_SetOutputReport et HidD_GetInputReport, respectivement. Après avoir expérimenté ces fonctions, avec différents ReportID et tailles de données, je n'ai pas réussi à réussir l'échange. Après le trafic via USB utilisant USBPcap , j'ai constaté que les données n'essayaient même pas d'être transmises. On soupçonnait qu'il s'agissait d'une sorte de DHI incorrect.


SET_REPORT La demande est restée sans réponse

Inverser l'application ZaZaRemote


Dans le fichier APK de cette application, j'ai trouvé la bibliothèque libtiqiaa_dev_usb.so. Il exporte les fonctions suivantes:

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 en juger par le nom, ils réalisent l'échange avec l'appareil. Des fragments similaires à l'appel de fonctions virtuelles se trouvent souvent dans leur code:

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

Le registre R0 est le premier argument de cette fonction. Ces fonctions sont appelées uniquement à partir du code Java classes.dex. En décompilant ce fichier, on obtient leur type:

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

Le code Java a été obscurci, mais certains noms survivent encore. Par rapport à cette bibliothèque, l'obfuscateur n'a gâché que le nom de la dernière fonction, cependant, presque tous les noms du code Java principal ont été corrompus.

Après avoir étudié un peu le code décompilé Java, j'ai trouvé les lignes suivantes:

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

Les journaux de débogage sont les bons amis d'un ingénieur inversé.

Pour appeler des méthodes natives à partir du code Java, le mécanisme Java Native Interface est utilisé . La fonction exportée doit ressembler à:

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

Vous pouvez maintenant définir le type de fonctions dans l'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);

Maintenant, le décompilateur HexRays reconnaît les appels JNI et le code devient beaucoup plus compréhensible, par exemple, l'appel ci-dessus est décompilé comme:

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

Résultat de décompilation de la fonction 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;
}

Le code est élémentaire, le format du message est évident: il commence par la signature "ST", puis vient l'octet du type de commande - l'un des caractères {'L', 'R', 'H', 'O', 'C', 'V', 'S '}, puis l'octet cmdId (juste un identifiant incrémentiel pour faire correspondre la commande et la réponse à celle-ci) et se termine par la signature "EN". Le message généré est envoyé par sub_3528.

Code de fonction 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;
}


Cette fonctionnalité est un peu plus compliquée. On peut voir que la longueur maximale du message envoyé est limitée à 1024 octets. Le message est divisé en fragments. Le fragment se compose de 5 octets de l'en-tête et d'un maximum de 56 octets de données - un total de 61 octets. Structure d'en-tête:

  • buf [0] = 2 est une constante. Rappelez-vous ReportID 2? Il semble que ce soit ça. Et ReportCount 60 - la taille des données restantes - est également la même.
  • buf [1] = fragmSize + 3 - taille des données du fragment + 3, c.-à-d. la taille est calculée à partir de l'octet suivant cette variable.
  • buf [2] = UsbPackCounter n'est qu'un compteur, 1..15.
  • La valeur de buf [3] calculée par l'expression fantaisie est juste le nombre de fragments, vous pouvez le réécrire comme:

    buf[3] = tsize / 56;
    if (tsize % 56) buf[3]++;
  • buf [4] = fragmCnt - numéro de fragment, 1..buf [3].

Les fragments formés sont envoyés via un appel à CallIntMethod. Il a le type suivant:

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

On peut voir que HexRays n'a pas réussi cette fois - dans les arguments seulement v6 = JNIEnv * env. Cependant, dans le code assembleur, tout est en place:

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

methodID est stocké dans la variable dword_B2A4. Voyons d'où cela vient: L'



enregistrement se fait dans les fonctions usbOpen et usbClose. De toute évidence, nous sommes intéressés par usbOpen.

Le fragment souhaité:

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

Il s'agit donc de la méthode UsbEndpoint :: bulkTransfer. Et rien - lié à HID!

Voyons maintenant comment les codes IR sont transmis - fonction usbSendIR.

Il est assez vaste, mais la zone qui forme le message avec l'équipe est compréhensible.

Fragment de la fonction 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';

Comme pour les autres commandes, tout commence par "ST", puis cmdId et le code de commande 'D' suivent, suivis d'un octet qui détermine la fréquence - si l'argument est freq> 255 - il est recherché dans la table de fréquences IrFreqTable, sinon il est copié directement. Ensuite, les données vont et tout se termine par "EN".

La fonction portant le nom obscurci «d» s'est avérée être un analyseur des données reçues.

Modification du pilote et utilisation de bulkTransfer


Après avoir étudié l'API HID, j'ai découvert qu'elle ne permet essentiellement pas d'utiliser bulkTransfer - vous devez donc changer de pilote. Le pilote WinUsb convient pour travailler avec cet appareil .
Après avoir écrit le fichier inf, j'ai changé le pilote en WinUsb et essayé d'envoyer des commandes. Tout a fonctionné et une réaction a été reçue de l'appareil - en réponse à l'envoi de commandes (via WinUsb_WritePipe), une réponse d'un format similaire est venue.

Échange de vidage avec ZaZaRemote


Malgré les succès précédents, jusqu'à présent, je n'ai pas pu réaliser l'essentiel - forcer l'appareil à transmettre des commandes IR. L'application était trop grande et déroutante et voulait juste vider le trafic USB. Cependant, comment faire cela dans le cas d'une application Android? L'entrée a été trouvée sous Android-x86 sur VirtualBox-e. Bien que x86 ne soit jamais ARM, il vous permet néanmoins d'exécuter des binaires ARM natifs via le mécanisme NativeBridge. En installant et en configurant le logiciel nécessaire, j'ai pu faire fonctionner cette application dans VirtualBox.

Les autorisations demandées inspirent certainement confiance en ce logiciel.


En lançant l'application et en configurant le transfert USB, j'ai pu renifler le trafic USB. J'ai donc obtenu une séquence de commandes pour initialiser la réception et la transmission de commandes IR, ainsi que des exemples de données de paquets IR.

Il s'est avéré que l'appareil est capable non seulement de transmettre, mais également de recevoir des commandes IR, mais la réception fonctionnait assez bien - à une distance de 10 cm, puis toutes les deux fois.

Exemple d'échange
- :

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

Il s'est avéré que l'appareil a des problèmes avec la taille des données à envoyer - en plus de la réponse, tout déchet est versé - généralement ce qui reste du paquet précédent.

L'étude du dépotoir et des expériences qui ont suivi ont permis de connaître les fonctions de toutes les équipes:

  • 'V' - Version - demande de version - mon appareil produit un GUID zéro;
  • «L» - IdleMode - mode veille - dans ce mode, l'appareil est localisé après la mise sous tension ou passe par cette commande;
  • 'S' — SendMode — — ;
  • 'R' — RecvMode — — ;
  • 'D' — Data — — , — ;
  • 'O' — Output — — , — / ;
  • 'C' — Cancel — — , 'O';
  • 'H' — Unknown — .


Après avoir reçu une décharge de commandes de contrôle et de colis IR, j'ai pu mettre en œuvre un contrôle d'appareil à part entière - recevoir et transmettre un signal IR. Cependant, afin de synthétiser un signal IR arbitraire, il a fallu déterminer le format dans lequel il est codé. Pour ce faire, j'ai connecté un photodétecteur IR à un oscilloscope et j'ai commencé à examiner les signaux envoyés. Grâce à l'expérimentation, j'ai découvert le format de codage: le bit haut de chaque octet détermine si l'émetteur est allumé ou éteint, et les 7 bits inférieurs déterminent l'heure. L'unité de temps était de 16 microsecondes. Par exemple: 8A - l'émetteur est allumé pendant 160 microsecondes; 8A 05 FF 83-160 μs allumé, pause 80 μs, 2,08 ms allumé.

Lorsque l'émetteur est allumé, la LED clignote à une fréquence de ~ 36,64 kHz. Théoriquement, cette fréquence devrait être déterminée par l'argument freq de la commande usbSendIR, mais des expériences ont montré que l'appareil ne répond pas du tout à cet argument. Néanmoins, mes appareils électroménagers acceptaient normalement les signaux de cet émetteur-récepteur.
Le format des données enregistrées par l'appareil en mode réception s'est avéré similaire.

Classe TiqiaaUsbIr et contrôle IR


J'ai implémenté le contrôle de l'émetteur-récepteur sous la forme d'une classe C ++ TiqiaaUsbIr et écrit un simple programme CaptureIR QT. En plus des fonctions de réception et de transmission de signaux IR, j'ai implémenté la synthèse et le décodage de signaux en utilisant le protocole NEC. Ce protocole est utilisé, par exemple, dans les télécommandes des téléviseurs LG. J'ai également implémenté l'enregistrement et le chargement des signaux IR dans le format d'origine et le format LIRC. Il y avait une idée de créer un module pour WinLirc, mais il s'est avéré qu'il s'agissait d'une API tordue et non entièrement implémentée, j'ai donc reporté cette idée pour l'instant.

Les sources et le programme compilé peuvent être téléchargés ici .

Un exemple d'utilisation de la 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();

Signal de mise sous tension capturé:



Il est synthétisé:



Pendant la capture, quelque chose s'est mal passé:



Sommaire


En cours de recherche, le protocole USB de l'émetteur-récepteur IR Tiqiaa Tview a été complètement restauré, un fichier inf du pilote et le logiciel pour travailler avec lui ont été écrits.

L'émetteur-récepteur IR considéré est un appareil très bon marché, abordable et compact (5 $ sur Ali, dimensions 15 x 10 x 5 mm) pour contrôler les appareils électroménagers et étudier ses protocoles IR. Malheureusement, le contrôle de la fréquence de l'émetteur s'est avéré inopérant, ce qui dans mon cas n'a pas causé de problèmes, mais il est possible qu'il existe une technique avec des récepteurs plus pointilleux.

Le mode de réception, en raison du faible rayon et de la faible fiabilité de capture, ne convient pas pour une utilisation comme récepteur infrarouge à part entière - un enregistrement des distances de capture réussies de ~ 30 cm, tandis que la télécommande vise exactement le récepteur, et même tous les signaux de mise au point ne sont pas normalement capturés. Cependant, il est utile pour capturer des signaux et étudier les protocoles des télécommandes IR.

Prime


Codes IR intéressants pour les téléviseurs LG: PS Je recherche des informations sur LG AccessUSB, plus de détails ici .

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



All Articles