解码 IR 协议的帮助或提示

电器工程 信号 红外线的 遥控
2022-01-07 03:45:07

前段时间我买了一架简单便宜的红外线控制的小型玩具直升机(和这个一样——它叫“钻石陀螺”或“钻石力量”)。为了好玩,我一直在研究通过 Arduino 控制它。

更新:弄清楚了协议;看答案

其他人已经分享了他们在破解不同的 IR 玩具直升机并解码其 IR 协议方面的结果。真的很酷,但不幸的是我的直升机使用了不同的协议。一个我想不通的。(我应该补充一点,电子产品对我来说纯粹是一种有时的爱好,所以我可能忽略了一些明显的东西)。

就像上面的第二个链接一样,我把控制器拆开,找到控制 LED 的 IC 引脚(顺便说一下,IC 的标记已被擦除),并连接了一个逻辑分析仪。

有很多好的数据,但我仍然无法弄清楚协议。该站点是一个很好的资源,但列出的协议似乎都不适合。我发现的其他任何东西似乎都与我捕获的信号不符。不过,我不得不想象它是一个简单的、现成的协议,只是因为它是一个便宜的小玩具。

所以我很感激你有任何想法。也许我只是看错了。
(图片下方有更多信息)

来自通道 A 的样本

信号/协议特性

我在控制器设置为通道 A 的情况下以 16MHz 捕获了这个;应该是准确的,时间方面的。(您可以选择 3 个 IR 通道,但使用另外两个通道不会改变特性,只会改变数据包本身的一部分。)时间非常一致(最大 +/- 10µs)。数据包以不同的间隔重复,但至少相隔约 100 毫秒。

载波:38kHz @ 50% 占空比

低点:
- 短:285µs
- 长:795µs

高:
- 短:275µs
- 长:855µs

每包总是 17 个高点。

控制/输入

直升机有 3 个控件:“油门”(即升力/旋翼速度)、俯仰(前进/后退)和偏航(围绕旋翼轴旋转)均由 2 个摇杆控制。它们都有某种范围(不仅仅是开/关),据我所知,它们都在一个数据包中传输。仅在发送其他内容时才发送左/右输入,因此我在采样时应用了最大油门。当您将摇杆推过某个阈值/死区时,在发送自己的触发数据包时进行油门和俯仰输入(在“min”标签下方的图表中,当将控件缓慢推过其死区时发送的第一个数据包)。

它还有用于左右修剪的按钮,因为直升机不是精密仪器(根本不是,否则往往会缓慢旋转。不幸的是,左/右微调按钮似乎没有发送一个信号,每次按下都会增加/减少一些东西(这对于确定协议很方便);它似乎只是一个命令,告诉直升机向左/向右修剪,然后它会跟踪它。

2个回答

我冒昧地回答我自己的问题,因为我已经弄清楚了大部分问题,这是分享我的发现的好方法。感谢 Olin Lathrop 给了我一个开始的地方和一些尝试的想法,但最终,该协议与 Olin 的猜测完全不同,因此我发布了这个答案。


更新:我发布了一个关于最后 8 位的后续问题,我并不完全理解,Dave Tweed 弄明白了我将在此处包含详细信息,因此此答案可以作为完整的协议规范使用,但请务必查看 Dave 的答案。


我不得不尝试一些不同的事情来解决这个问题,但我很有信心我明白了。奇怪的是,我在其他地方没有发现任何类似这个协议的东西,但它很可能是一个我不知道的通用协议。

无论如何,这就是我发现的:

协议/编码

脉冲和其间的空间都用于对数据进行编码。长脉冲/间隔是二进制一 (1),短脉冲/间隔是二进制零 (0)。脉冲使用标准消费红外 38kHz 调制 @ 50% 占空比发送。

脉冲/空间时序在原始问题中,但为了完整起见,我将在这里重复它们:

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

所有 ±10µs 最大值,±5µs 典型值。这是基于用逻辑分析仪在 16MHz 捕获的样本;我没有示波器,所以我不知道确切的配置文件(即上升/下降时间)。

只要应用了控制输入,数据包就会重复,并且看起来至少相隔 100 毫秒。

数据包传输以“脉冲 1”前导码开始,它是固定的,不是数据的一部分。下面的空间编码数据包的第一个数据位,最后一个脉冲编码最后一个位。

每个数据包长 32 位,包含遥控器可以提供的每个输入。值以小端方式读取,即 MSB 在前。

数据结构

下面是各个数据包的基本结构。最后 8 位让我感到困惑,但现在已经弄清楚了(见下文)。

 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

注意:范围是基于我得到的最高读数。该协议能够实现更大的范围——油门最高 255,俯仰/偏航 63——但上限约为一半。
音高值似乎有一个从 14 到 21(含)的死区;只有高于或低于的值才会真正使直升机做出反应。我不知道偏航是否相同(很难说,因为直升机无论如何都不稳定,并且可能会自行轻微旋转)。

这是图形术语(与原始问题中的图形比较)

包结构

6 个校验位是通过对所有前面的值进行异或来计算的。每个值被视为 6 位。这意味着 8 位限制值的 2 个 MSB 被简单地忽略。IE

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

实用笔记

信号时序和调制不需要非常精确。与真正的遥控器相比,即使是我的 Arduino 的完全不准确的计时也能正常工作,尽管调制很狡猾,脉冲/空间持续时间有一点点命中和失误。

我相信 - 但尚未测试 - 直升机将简单地锁定它发现的第一个信号的通道。如果它长时间没有信号(几秒钟),它似乎会回到它的“搜索”模式,直到它再次获得信号。

如果油门为零,直升机将忽略俯仰和偏航值。

每次按下遥控器上的按钮,修剪命令仅发送一次。据推测,微调值只是增加/减少直升机自身控制器中的值;这不是遥控器跟踪的东西。因此,任何对此的实现都应该遵循该方案,并且只发送偶尔的修剪左/右值,否则默认为数据包中的零修剪值。

我建议有一个简单地将油门设置为零的终止开关。这将导致直升机从天上掉下来,但当它不旋转电机时,它受到的伤害会更小。因此,如果您即将坠毁或撞到什么东西,请按下终止开关以避免剥落齿轮或折断刀片。

原始遥控器的 IR LED 的波长似乎 >900nm,但我使用 ~850nm LED 没有问题。

直升机的红外接收器还可以,但不是超级敏感,所以你的红外源越亮越好。遥控器使用 3 个串联的 LED,位于 9V 导轨上,而不是逻辑使用的 5V 导轨上。还没有非常精确地检查他们的电流消耗,但我敢打赌它是 50mA。

样本数据

这是一堆数据包,供任何感兴趣的人使用(是的,我编写了一个解码器;我没有手动解码所有这些)。通道 A 数据包来自与原始问题中的图表相同的捕获。

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

如上所述,最后8位已经弄清楚了,但只是为了后人,这里是我的原始想法。随意完全忽略它,因为我的猜测非常错误。

最后 8 位

数据包的最后 8 位仍然有点神秘。

第 23 位到第 26 位这 4 位似乎完全由遥控器的频道设置决定。更改遥控器上的频道不会以任何方式改变协议或调制;它只会改变那 4 位。

但是 4 位是编码通道设置实际需要的两倍;只有三个通道,所以 2 位就足够了。因此,在上面的结构描述中,我只将前 2 位标记为“通道”,而将其他两个标记为“X”,但这是一个猜测。

下面是每个通道设置的相关位示例。

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

基本上,传输通道设置需要多出 2 位。也许该协议预留了 4 位以便稍后允许更多通道,或者该协议可以用于完全不同的玩具,但我根本不知道。对于较大的值,该协议确实使用了可以省略的额外位(偏航/油门/俯仰每个都可以少一点),但是对于修剪 - 它也有 3 个状态 - 只使用了 2 个位。因此,人们可能会怀疑该通道也只有 2 位,但这使得接下来的 2 位下落不明。

另一种可能性是数据包的校验和是 8 位长,从“X 位”开始,并且 - 通过校验和魔法 - 它们恰好以某种方式总是反映通道设置。但再说一遍:我不知道。

说到:我不知道这些校验位是如何形成的。我的意思是,它们校验位,因为它们不对应于任何单个控制输入,而且如果我摆弄它们,直升机似乎没有响应。我猜这是某种CRC,但我无法弄清楚。检查的长度为 6-8 位,具体取决于您如何解释“X 位”,因此有很多方法可以放在一起。

这看起来还不错。首先请注意,所有消息都恰好包含 17 个脉冲。这立即为我们提供了一个强有力的线索,即消息中的短空格是无关紧要的。似乎数据是由短脉冲或长脉冲编码的,并且这些脉冲之间的一定范围的间隔是可以接受的。

显然,每条消息都以一个长脉冲作为起始位。剩下 16 个数据位。可能早期的一些位是操作码,可能是可变长度。如果我这样做,一些结束位将是校验和。假设编写固件的工程师希望自己保持简单,因此您可以先假设某处有 8 个数据位。现在看看是否有任何消息有意义。

让我们称多头为 1,空头为 0。也可以反过来,但我们必须从某个地方开始。剥离起始位叶:

1010001101011010 分钟油门
1010011101011000 最大油门
1010000001011111 分钟前进
1010000000011110 最大前进
1010000011011101 最大返回
1010000100011010 分钟返回
0000010101011100 最大左侧 + 最大油门
0100010101011110 最大右 + 最大油门
1010000101111111 左修剪
1010000101011011 右修剪

有几样东西马上就出来了。显然位 0 是奇偶校验位。否则,似乎有一个 3 位字段 <15:13>、一个 8 位数据值 <12:5> 和另一个 4 位字段 <4:1>。

看起来数据值是以低位到高位的顺序发送的,因此解释从我显示的内容翻转的整个 16 位可能更有意义。

我不想花更多时间在这上面,但希望这给了你一个开始。我将重写上面的列表,去掉奇偶校验位,整数将 LSB 翻转到 MSB,每个假设字段分别显示,在它和邻接字段之间有一个空格。这可能会让更多的东西突然出现在你身上。还要记住,我们可能对每一位都有 1/0 的感觉。也许以每一种方式写出新表,看看是否某种方式更有意义。