【技术分享】智能家居DIY——如何通过逆向RF协议自制一个智能遥控器

http://p4.qhimg.com/t019a4d6b6f39136a77.jpg

译者:紫曦归来

预估稿费:260RMB

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿


随着家庭生活品质的提高,家用电器设备也越来越多,遥控器逐渐成为了家中不小的负担。厂家本身的想法是配备了遥控器,日常使用这些家电设备更加方便,但是实际却事与愿违,因为生活水平的提高,家中电器及数码设备提供了一堆的遥控器。相信大家都经历过,满屋找遥控器的尴尬。来自澳大利亚的软件工程师尼克·怀特(Nick Whyte)通过逆向遥控器的RF协议,最终DIY了一个可以操控家中所有百叶窗的遥控器。下面小编就带大家看看他是如何进行智能家居DIY的。

我对集成DIY智能家居非常感兴趣,也曾经通过黑进部分家用电器的内部系统,来进行家庭自动化的DIY改造。几个月前,我父亲购买了多个瑞克斯(RAEX)品牌433兆赫射频的电动百叶窗,以取代家里的手动窗帘。

http://p4.qhimg.com/t01b32f710ac5b7b8b0.png

图1:瑞克斯牌433兆赫射频电动百叶窗

注:购买的百叶窗在Spotlight上以“Motion Motorized Roller Blind”的名义销售

百叶窗给这个家带来了非同凡响的感受,尤其是对于比较懒惰的我来说,不用手动开关窗帘,这一点大为省事。但是,遥控百叶窗需要购买瑞克斯品牌遥控器。瑞克斯公司生产了不同型号的遥控器,我挑选了以下两款遥控器:

http://p8.qhimg.com/t0109cb29f7ea3a7715.png

R型遥控器 (YRL2016)

http://p1.qhimg.com/t012ae9275a03c25b0e.png

X型遥控器(YR3144)

每一个房间里都放一个遥控器的做法不可行,因为遥控器的部分功能用不上,购买过多的遥控器就显得非常浪费了。事实上,不同房间的百叶窗可以被编程到同一个遥控器上使用。因此,购买多个遥控器就没有必要了。

解决这一问题就是使用名为“ RM Pro”的App。这款App,可允许用户通过智能手机来操控家电等设备。

http://p5.qhimg.com/t019bfc6353dc0d5949.png

http://p6.qhimg.com/t011b81e324e99976c7.png

但是,在我看来,这个App运行起来非常缓慢,完全不适合家庭自动化系统。我希望家里的百叶窗可以通过苹果智能家居系统(Apple Homekit)进行操控。

如果想要遥控到这些百叶窗,我需要做以下两件事中的任意一件:

1、逆向出RM Pro的应用程序(App)与RM Pro之间是如何实现通信的。

2、逆向出遥控器操控百叶窗的RF协议。

首先,我尝试了第一种选项,但是我无法弄清楚iPhone和集线器之间是如何进行通信的,所以我选择了放弃。此后,我开始逆向RF协议。

我在Ebay网站上购进了一套433兆赫兹Arduino开源电子原型平台的发射器和接收器。以防发射器和接收器之间链接断掉,大家也可以尝试在Ebay上购买一套433兆赫兹Arduino开源电子原型平台的发射器和接收器的链接工具。

http://p7.qhimg.com/t01342c5431202ab330.png


初步研究结果

我在Google上反复搜索RAEX正在使用的协议的技术指标,但没有任何结果:

我通过FCC认证查询网站和专利查询网站找不到协议的任何技术指标。

我向RM Pro发送电子邮件想要获取其技术指标,但依旧毫无所获。

我向瑞克斯公司发送电子邮件想要获取其技术指标,他们表示不会在没有保密协议的情况下给我提供产品技术指标。

我发现RFXTRX可以在百叶窗处于T4模式和Outlook Motion模式下操控百叶窗。

我拆开了一个百叶窗遥控器,发现遥控器内部还装有微控制器(micro-controller)。但我找不到关于通用RF协议如何进行编码的相关说明文档。

我发现似乎可以通过I2C工程远程固件上的ROM芯片逆向出遥控器的固件。

获取信号数据

在收到网购的Arduino开源电子原型平台的发射器和接收器后,我将接收器与Arduino开源电子原型平台相连接,并开始尝试进行信号数据搜索。我反复进行了多次尝试,最后我找到了一条可以获取数据的路径。

获取了足够的信号数据信息后,我开始进行数据分析。我发现这些数据颇难理解,我甚至不能确定这些数据的准确性。

我进行了延伸学习,阅读了如何进行RF 逆向的相关书籍和文档。这些书本里介绍了一种方法,就是将接收器插入电脑的麦克风端口来捕捉信号,之后通过Audacity音频软件进行分析。(如下图所示)我想,何不尝试一下呢?

http://p8.qhimg.com/t01bc8c42badb92ed62.png

http://p8.qhimg.com/t018b11c6b13d897897.png

这一办法帮助我搜集了许多的信号数据。我搜集到了4个不同的R型遥控器和2个不同的X型遥控器的信号数据。意外的是,我还搜集到了与Broadlink RM Pro(B型)配对的8种不同设备的信号数据。

通过分析这些信号数据,我可以得出以下结论:

1、 设备之间传输的信号没有使用滚动码(rolling code)。因此,我可以通过重播捕获的信号,使百叶窗进行重复动作。

2、 设备之间传输的信号要重复至少3遍(根据所使用的遥控器类型不同而有所区别)。

缩小信号的波形,我们可以看到捕获到的信号的不同部分。下图展示了遥控器与百叶窗配对状态下1号遥控器,在1频道时的信号波形:

http://p9.qhimg.com/t01c401809ed409e11c.png

放大信号波形:

http://p8.qhimg.com/t01d36579be98083183.png

在缩放图像中,不难看出信号首先以振荡0101 AGC模式(oscillating 0101 AGC pattern)进行传输,此后以双宽度前导码模式传输,再之后以较长的header模式传输,此后转为data模式。

R型遥控器的传输信号中,Header和data模式要重复三边,AGC模式仅在开头出现过一次。

纯粹研究这些信号并不会有太大的帮助。我需要将这些信号数字化,并对其进行分析,找出不同遥控器和频道之间信号传输模式的差异。


解码信号波形

我们需要确定信号波形是如何编码的。这一类硬件应用通常会使用下列方法之一:

曼彻斯特编码(又称相位编码)

三态/三位编码,附加信息

PWM编码

原始数据?high long = 11,high short = 1,low long = 00,low short = 0?

通过一些研究,我能够确定其最有可能使用的是曼彻斯特编码。这一点需要我们谨记!


信号数字化

我开始按照上述原始方案处理信号(即使我认为其采用了曼彻斯特编码)。这样做的理由是,如果编码方式并不是曼彻斯特编码,我还可以尝试通过另一种方案解码。(另外,手抄原始信号的方式总比我通过思考进行曼彻斯特解码更容易)。

我将每次捕获的信号写入Google Sheets电子表格。写下每个频道的每次动作大概需要5分钟时间,每个遥控器都有6个频道。我开始认为这将需要一段时间才能真正得到足够的数据进行分析。(考虑到我需要对160条捕获信号进行数字化)

在从2个遥控器中收集8个不同频道的所有动作后,我选择了暂停。我获得了32条捕获信号用于分析。从这些数据中,我能够推断出一些关于原始bits的特点:

不同频道会有一些bits改变。

不同遥控器会有一些bits改变。

对于不同频道/遥控器/动作组合,有些bits似乎会随机变化。

我仍然需要更多的数据,但这也使得我必须手动解码大量捕获信号。为了能够在任意地点实现操作,我还需要一个脚本来帮助处理这些通过Audacity软件捕获的WAV文件。我编写了一个脚本,使其可以通过原始编码方式检测标头和提取数据(就是我一直手动在做的)。该脚本生成了JSON格式的输出文件,我可以添加额外的元数据,并反复检查捕获信号的波形:

[
  {
    "filename": "/Users/nickw/Dropbox/RF_Blinds/Export_Audio2/tracks2/R1_CH1.wav",
    "captures": [
      {
        "data": "01100101100110011001100101101001011010010110011010011010101010101010101010011001101010101010101010101010101",
        "header_pos": 15751,
        "preamble_pos": 15071
      },
      {
        "data": "01100101100110011001100101101001011010010110011010100110101010101001101010011001101010101010101010101010101",
        "header_pos": 46307,
        "preamble_pos": 45628
      },
      {
        "data": "01100101100110011001100101101001011010010110011010010110101010101010011010011001101010101010101010101010101",
        "header_pos": 73514,
        "preamble_pos": 72836
      },
      {
        "data": "01100101100110011001100101101001011010010110011010101010101010100101010101101001011010101010101010101010101",
        "header_pos": 103575,
        "preamble_pos": 102895
}
    ]
  }
]

一旦确认,我就会对这些数据进行列表,并将其插入我的电子表格进行进一步处理。不幸的是,每次捕获信号的 bits量已经大到让我失去理智:

http://p7.qhimg.com/t01622387a888216fee.png

我认为如果以曼彻斯特编码方式进行解码可能会有更好的效果。为此,我又写了一个脚本,将原始捕获数据按照曼彻斯特编码进行处理(或其他编码类型)。在将这些数据计入电子表格时,我逐渐得到了更加合理的答案。

http://p0.qhimg.com/t01276413ba53605257.png

观察这些数据,我们立即可以得到这些bits和其用途之间所存在的一些联系:

6bits用于频道(C)

2bits用于动作(A)

6bits用于某些校验和,似乎是动作和频道的一个函数。 F(A, C)

行动改变时发生变化

频道改变时发生变化

没有频道是相同的,因此不能确定是否会因遥控器不同而发生变化。

1bit似乎是动作的一个函数 F(A)

1bit似乎是F(A)的一个函数,于是,G(F(A))。它取决于F(A)的值,有时是1对1映射,有时是反映射。

经过进一步调查,我确定对于同一个遥控器和频道来说,每个不同的动作,会使F(A, C)增加1.(如果你将这些bits看作大端格式)。

http://p7.qhimg.com/t01d12bb73f60d0a747.png

再仔细观察这些数据,我同时能够确定,对于相邻的频道,bits与C(Channel)存在递加/递减算法关联(X型遥控器递加计算,R型遥控器递减计算)。另外,F(C)还会一起增加/减少。注意C栏。

http://p6.qhimg.com/t01ae1b54cb508a39c8.png

由此,我可以确认F(A, C)和C之间的关系,即F(A, C) = F(PAIR, C0) == F(PAIR, C1) ± 1。在有了这个发现之后,我确定F(A, C)和A之间也存在其他的数学联系。


制作更多数据

根据我们所收集的现有信息,我们似乎可以通过改变6 bits频道数据,以及按照我们上面发现的数学联系改写校验和,制作新的遥控器。这意味着我们可以从单一源频道生成64个频道。而这已足够控制房间里的所有百叶窗了。但我还是想完全解码校验和字段,从而无限量地制作遥控器。

我编写了一个工具来通过元捕获数据输出所有频道:

./remote-gen generate 01000110110100100001010110111111111010101
...

我想要生成更多数据的原因是,如果我们可以在同一个频道上查看不同的遥控器,也许我们可以确定校验和的构成方式。即R0CH0,R1CH0,X1CH0,等…

实际上,我想做的是解出下列方程式的函数G:

F(ACTION_PAIR, CH0) == G(F(ACTION_PAIR, CH0))

然而,在查看所有频道0的PAIR捕获数据后,校验和似乎仍然是以完全混乱和随机的形式表达:

http://p1.qhimg.com/t012bf08cd3b32a6076.png

在看这些数据的同时,我又发现了另一种模式。G(F(A))的位置相比F(A)出现了整个字节的偏移(8 bits)。另外,前2 bits的F(A, C)位于字节边界,并与A(Action)对齐。随着行动增加,F(A, C)也是如此。我们再将所有处于字节边界的bits进行排列,看看其有什么特征:

http://p0.qhimg.com/t01c7bae59b22a2da74.png

http://p9.qhimg.com/t014af3b1c5c94c1089.png

在这里,我们需要确定基于前4个字节产生的已知校验和的函数。最初,我尝试通过字节进行异或(XOR)运算:

http://p0.qhimg.com/t011c436e89018acc5e.png

但结果并不成功,输出结果是随机形式的数据,使用校验和与输出结构异或运算后,并没有产生常数键。因此,我推断出校验和不是通过异或运算生成的。那会不会是采用的加法呢?我们从上面已经能看到加法/减法的运算关系。

http://p9.qhimg.com/t01f1806ad936fec80d.png

我们的推测看起来越来越有希望——相同类型遥控器的频道之间存在一个常数差异。因为我制作的项目存在漏洞,会不会不同类型遥控器的常数也存在不同?在频道或校验和变化时,我们是否没有打包正确的位数,或使用错误的字节边界?

事实证明,这就是原因所在。


分析校验和

查看原始捕获数据,并执行相同的模加法,我们确定校验和是通过添加前导的4个字节和加3来计算的。我无法确定为什么在这里使用3,除了瑞克斯公司(购买百叶窗的品牌)想要使其校验和的解码过程变得更多困难,或者为了确保正确的传输模式。

我改装了我的遥控器应用来处理这些刚刚识别的边界:

type RemoteCode struct {
    LeadingBit uint // Single bit
    Channel    uint8
    Remote     uint16
    Action     uint8
    Checksum   uint8
}

这样的一组数据可以解释我们的问题。事实证明, F(A)不是A(Action)的一个函数,它实际上是被传输的动作数据(action data)的一部分:

type BlindAction struct {
    Name  string
    Value uint8
}
var validActions = []BlindAction{
    BlindAction{Value: 127, Name: "PAIR"},
    BlindAction{Value: 252, Name: "DOWN"},
    BlindAction{Value: 253, Name: "STOP"},
    BlindAction{Value: 254, Name: "UP"},
}

另外,频道和遥控器之间的拆分或许是没有必要的。相反,这可能只是一个任意的24位整数,但是更容易的是将其拆分为8位int和16位int。基于此,我可以推断,协议可以容纳2 ^ 24个遥控器(约1670万)!这将是很多的百叶窗!

最终,我在这里正式写出了校验和的函数:

func (r *RemoteCode) GuessChecksum() uint8 {
    return r.Channel + r.Remote.GetHigh() + r.Remote.GetLow() + r.Action.Value + 3
}


附加工具

我的“衍生遥控器”(remote-gen)项目是为了通过原有遥控器生成代码,但现在需要一些其他功能。

我需要一种从捕获数据中提取信息的方法,并验证在生成校验和期间,其校验和是否与我们的规则集一致。我写了一个info命令:

./remote-gen info 00010001110001001101010111011111101010100 --validate
Channel:    196
Remote:     54673
Action:     STOP
Checksum:          42
Guessed Checksum:  42

运行—validate后,如果猜测的校验和!=校验和,则将显示错误并退出。在我们的所有捕获数据中运行这一进程,可以证明我们的校验和函数是正确的。

该工具所需的另一个功能是能够生成任意代码来创建我们自己的遥控器:

好了到这里,我已经可以使用这个工具来生成任何我所需要的遥控器了。

./remote-gen create --channel=196 --remote=54654 --verbose
00010001101111110101010111111111010011001    Action: PAIR
00010001101111110101010110011111101101000    Action: DOWN
00010001101111110101010111011111111101000    Action: STOP
00010001101111110101010110111111100011000    Action: UP

结语

这就是我如何逆向出一个完全模式的协议的整个过程。我未来还将和大家分享更多智能家居DIY的文章。

(完)