中文USB IR收发器协议的逆向工程


我遇到了一个中文MicroUSB IR收发器,希望将其连接到装有Windows的计算机。收发器是带有Micro USB连接器的非常紧凑的设备。唯一可以使用的“官方”选项是通过一个名为ZaZaRemote的Android应用程序。

通过适配器连接到计算机时,该设备被定义为HID兼容USB设备\ VID_10C4&PID_8468。使用该ID进行谷歌搜索没有任何结果,因此我必须进行协议逆转。

无效的HID


设备类别定义为USB \ Class_03&SubClass_FF&Prot_FF。Class_03-HID设备,SubClass_FF-供应商特定。hidusb.sys驱动程序已由系统自动安装。您可以通过HID API使用此驱动程序

绘制了一个简单的小程序后,我可以在设备上获取各种信息:

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

事实证明,交换是在最大61个字节的块中进行的,有2个InputValue和2个OutputValue接口。HidP_GetValueCaps函数返回有关它们的更多详细信息:

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

在此数据中,最有趣的是ReportID-报告的ID(实际上是数据包)和ReportCount-其大小。可以分别使用HidD_SetOutputReport和HidD_GetInputReport函数发送和接收数据。在尝试了这些功能以及不同的ReportID和数据大小之后,我无法成功进行交换。使用USBPcap通过USB进行通信之后,我发现数据甚至都没有尝试传输。有人怀疑这是某种不正确的HID。


SET_REPORT请求未答复

反转ZaZaRemote应用程序


在此应用程序的APK文件中,我找到了库libtiqiaa_dev_usb.so。它导出以下功能:

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

从名称来看,他们实现了与设备的交换。类似于调用虚拟函数的片段通常在其代码中找到:

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

寄存器R0是此函数的第一个参数。这些函数仅从Java classes.dex代码中调用。通过反编译此文件,我们得到它们的类型:

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

Java代码被混淆了,但是有些名称仍然存在。关于该库,混淆器仅破坏了最后一个函数的名称,但是,几乎所有主要Java代码中的名称都被破坏了。

在研究了Java反编译代码之后,我发现了以下几行:

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

调试日志是逆向工程师的好朋友。

要从Java代码调用本机方法,请使用Java本机接口机制导出的函数应如下所示:

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

现在,您可以在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);

现在,HexRays反编译器可以识别JNI调用,并且代码变得更加容易理解,例如,上述调用反编译为:

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

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

代码很基本,消息格式很明显:它以签名“ ST”开头,然后是命令类型的字节-字符{'L','R','H','O','C','V','S '},然后是字节cmdId(只是一个增量标识符,用于匹配命令及其响应),并以签名“ EN”结尾。生成的消息由sub_3528发送。

功能代码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;
}


此功能有点复杂。可以看出,发送消息的最大长度被限制为1024个字节。该消息分为多个片段。该片段由5个字节的标头和最多56个字节的数据组成-总共61个字节。标题结构:

  • buf [0] = 2是一个常数。还记得ReportID 2吗?看来就是这样。而ReportCount 60(剩余数据的大小)也相同。
  • buf [1] = fragmSize + 3-片段数据大小+ 3,即 大小是从该变量后面的字节计算得出的。
  • buf [2] = UsbPackCounter只是一个计数器1..15。
  • 花式表达式计算的buf [3]的值仅是片段数,您可以将其重写为:

    buf[3] = tsize / 56;
    if (tsize % 56) buf[3]++;
  • buf [4] = fragmCnt-片段编号1..buf [3]。

形成的片段通过对CallIntMethod的调用发送。它具有以下类型:

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

可以看出HexRays这次没有管理-仅在参数v6 = JNIEnv * env中。但是,在汇编代码中,所有内容均已就绪:

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

methodID存储在dword_B2A4变量中。让我们看看它



的来历记录是在usbOpen和usbClos​​e函数中完成的。显然,我们对usbOpen感兴趣。

所需的片段:

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

这就是UsbEndpoint :: bulkTransfer方法。没什么-与HID有关!

现在,让我们看看如何传输IR代码-usbSendIR函数。

它很大,但是与团队形成信息的区域是可以理解的。

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

与其他命令一样,所有内容均以“ ST”开头,后跟cmdId和命令代码“ D”,然后是确定频率的字节(如果参数为freq> 255)在频率表IrFreqTable中查找,否则直接复制。然后数据走了,一切都以“ EN”结尾。

带有混淆名称“ d”的函数原来是接收数据的解析器。

更改驱动程序并完成bulkTransfer


研究了HID API之后,我发现它基本上不提供使用bulkTransfer的功能-因此您必须更改驱动程序。WinUsb驱动程序适合与此设备一起使用
编写完inf文件后,我将驱动程序更改为WinUsb,并尝试发送命令。一切正常,并且从设备接收到响应-响应发送命令(通过WinUsb_WritePipe),出现了类似格式的响应。

与ZaZaRemote进行转储交换


尽管取得了先前的成功,但到目前为止,我还无法实现主要任务-强制设备发送IR命令。该应用程序太大且令人困惑,只想转储USB流量。但是,如果是Android应用程序,该怎么做?在VirtualBox-e上找到的条目为Android-x86。尽管x86从来都不是ARM,但是它仍然允许您通过NativeBridge机制运行本机ARM二进制文件。通过安装和配置必要的软件,我能够使该应用程序在VirtualBox中运行。

所需的权限肯定会激发人们对该软件的信心。


通过启动应用程序并设置USB转发,我能够嗅探USB流量。因此,我得到了一系列命令,用于初始化IR命令的接收和发送以及IR数据包的样本数据。

事实证明,该设备不仅能够发送,而且能够接收IR命令,但是接收工作相当正常-从10厘米的距离开始,然后每隔一段时间发送一次。

交流范例
- :

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

事实证明,该设备在要发送的数据大小方面存在问题-除了答案之外,还会倒入任何垃圾-通常是前一包的剩余内容。

通过研究转储和随后的实验,可以找出所有团队的职能:

  • 'V'-版本-请求版本-我的设备产生的GUID为零;
  • “ L”-空闲模式-待机模式-在此模式下,设备在通电后就处于定位状态,或者通过此命令进入;
  • 'S' — SendMode — — ;
  • 'R' — RecvMode — — ;
  • 'D' — Data — — , — ;
  • 'O' — Output — — , — / ;
  • 'C' — Cancel — — , 'O';
  • 'H' — Unknown — .


接收到控制命令和IR数据包的转储后,我便能够实现全面的设备控制-接收和发送IR信号。但是,为了合成任意的IR信号,必须确定其编码格式。为此,我将红外光电探测器连接到示波器,并开始检查发送的信号。通过实验,我发现了编码格式:每个字节的高位确定发送器是打开还是关闭,而低7位确定时间。时间单位为16微秒。例如:8A-发射器开启160微秒; 8A 05 FF 83-160μs开启,暂停80μs,2.08毫秒开启。

发射器打开时,LED以〜36.64 kHz的频率脉动。从理论上讲,该频率应由usbSendIR命令的freq参数确定,但实验表明该设备完全不响应该参数。但是,我的家用电器通常接受此收发器的信号。
事实证明,设备在接收模式下记录的数据格式是相似的。

TiqiaaUsbIr类和IR控制


我以C ++ TiqiaaUsbIr类的形式实现了收发器控件,并编写了一个简单的CaptureIR QT程序。除了接收和发送IR信号的功能外,我还使用NEC协议实现了信号的合成和解码。例如,此协议用于LG电视的遥控器中。我还实现了以原始格式和LIRC格式保存和加载IR信号。曾经有一个为WinLirc制作模块的想法,但事实证明这是一个弯曲的,尚未完全实现的API,因此我暂时推迟了这个想法。

源码和编译程序可以在这里下载

使用TiqiaaUsbIr类的示例:

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

捕获的开机信号:



合成:



捕获期间出了点问题:



摘要


在研究过程中,Tiqiaa Tview红外收发器的USB协议已完全恢复,并编写了一个驱动程序的inf文件以及用于该文件的软件。

所考虑的红外收发器是一种非常便宜,负担得起且紧凑的设备(在Ali上售价5美元,尺寸15 x 10 x 5 mm),用于控制家用电器和研究其IR协议。不幸的是,控制发射机的频率被证明是行不通的,在我看来,这并不会引起问题,但是有可能存在一种技术更复杂的接收机。

由于半径较小且捕获可靠性低,因此接收模式不适合用作完整的IR接收器-成功捕获距离约为30 cm的记录,而遥控器恰好对准接收器,即使所有聚焦信号也无法正常捕获。但是,它对于捕获信号和研究IR遥控器的协议很有用。

奖金


LG电视的有趣IR代码: PS我正在寻找有关LG AccessUSB的信息,更多详细信息在这里

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



All Articles