NeoAtlantis应用科学和神秘学实验室 最近迁移到了一个新地址。因为建筑过于陈旧,很多设备开关十分不便,于是负责人安装了一些433MHz上的遥控插座,稍微改善了这一局面。

负责人虽然知道现在早已有借助WIFI的智能插座,也可以通过 Google 助理等控制,甚至通过IFTTT实现复杂的自动化,但是,这些方案对网络的依赖在他看来是个问题。

比较起来,两种方案各有优劣:

  433MHz 无线遥控 WiFi 插座
即插即用 较好(对码简单) 需要配置网络,不太容易
安全可靠性 较差,无加密,无反馈 较好
依赖网络的可靠性 需要网络
扩展性 差,遥控器按钮有限
自动化 不自动 容易自动化

可以看出,使用无线遥控的方案,比起WiFi插座还差的地方在于:无法自动化,且无法获得被控制的设备状态。

自动化的好处是显而易见的:

  • 比如可以节省电力消耗(实验室负责人发现电热水器就是一个很大的耗电来源,尤其在冬季)
  • 比如可以自动规划一些事情(比如使用的高压电饭锅还没有预约功能,但是可以通过智能插座实现)
  • 比如到家自动开灯,白天自动关灯,节约电力

为了达到上述目的,本实验室准备DIY一个方案,实现互联网或者短信到433MHz信号的网关。市场上虽然已经有类似的产品,但是自己动手,仍然是一种不错的训练。

1. 初步分析信号

要了解如何控制433MHz的无线开关,第一步就是捕捉和分析开关的信号。最简单的办法,是使用rtl_433这个程序,配合RTLSDR完成。

编译安装的过程可以在上面这个程序的Github中看到,这里不再详述。运行程序,然后按下遥控器按钮,可以看到如下信息:

rtl-433 capture

可以看到,这个433MHz的遥控器基本上是使用了Nexa或者Proove Security的协议(即使遥控器本身并不是这个牌子)。这个信息比较有用,可以先记住。

图片上打码删去的部分是这个设备的特征编码(House Code),一共有8位。注意到,这个设备的遥控器上有6个按键,对应3个设备的各自开关; 又有2个额外的总开关按键,可以同时控制3个设备。这是怎么做到的呢? 多按几次,就可以看到:所有的3个设备都共享同样的House Code,但是Unit和Group不同:

  1. 当Group不为零的时候,为群组控制,所有共享同样House Code的设备的状态都会变为State的状态(开或关)。
  2. 当Group为零的时候,Unit是被控制的具体设备编号,目前因为遥控器上只有3个设备,这个编号可以发射的值是0,1或2。这个编号具体能到多少的范围,还有待研究。

2. 捕捉信号波形

为了模拟遥控器的输出,我们需要捕捉出这个信号的具体波形。(另一种办法是分析rtl_433这个程序的代码,但是这样似乎难度更高)。办法很简单,使用gqrx这个程序,调到433.92MHz,具体看下波形:

gqrx capture

使用菜单中的Tools->I/Q Recorder这个工具,可以记录一段时间内的SDR原始数据。 这一数据量较大,因此一定要快速操作。IQ数据是将SDR采样得到的结果按照复数形式 \(I+Q\mathbf{i}\) 记录的文件。因为是复数,所以信号中同时具有幅度和相位的信息,可供程序解码使用。

我们感兴趣的是这个信号的波幅与时间的关系。使用如下show.py代码,打开文件,然后用matplotlib绘图:

import matplotlib.pyplot as plt
import scipy
import sys
 
# gqrx_yyyymmdd_hhmmss_freq_samplerate_fc.raw
input_filename = sys.argv[1]
output_suffix = "subset"
 
sample_rate = input_filename.split('_')[4]
 
with open(input_filename, 'rb') as f:
    x = scipy.fromfile(f, dtype=scipy.complex64)
len(x)
 
# Look at the data
plt.plot(abs(x))
plt.show()

运行代码:

$ python3 show.py <gqrx捕捉的raw文件>

就可以看到如下一幅图像:

waveform_overview

3. 具体分析波形细节

上述matplotlib显示波形的窗口可以查看细节。具体方式是在工具栏中选择放大镜形状的图标,然后在需要查看的部分上绘制一个方框。这样显示窗口就会缩放到对应的波形上去了。

waveform_single

这是第6号波形,即第3个按钮“关闭”按键的信号。从图中我们可以得出第二个重要信息:每个按键的传送分为6段。(实际上如果仔细分析,可以看到这6段是重复发送6次)

而具体每段发送的波形,就是我们需要了解的信号了:

waveform_overview

4. 脉冲位置编码

下面展示波形的前几个脉冲。在matplotlib的绘图窗口中移动鼠标,可以找到鼠标光标所指的信号序号。下图中已经标注了各个脉冲的上升(->)和下降(<-)沿对应的序号。

waveform_timing

根据信号序号换算到时间,需要知道采样率。此次录制的信号,采样率为1.8MHz,因此每个采样样本对应的时间为:

\[ \frac{1s}{1.8 \times 10^{6}} = 5.55 \times 10^{-7} s = 0.555 \mu s \]

据此可以计算上述脉冲的时长和间隔,列表如下:

脉冲编号 前一个脉冲下降沿序号 上升沿序号 下降沿序号 距离前一个脉冲的序号差 距离前一个脉冲的时长(us) 脉冲长度 脉冲时长(us)
1 - 26506929 26507427 - - 498 277
2 26507427 26512218 26512716 4791 2661 498 277
3 26512716 26513179 26513677 463 257 498 277
4 26513677 26516115 26516630 2438 1354 515 286
5 26516630 26519051 26519549 2421 1345 498 277
6 26519549 26520013 26520511 464 258 498 277
7 26520511 26522966 26523447 2455 1364 481 267
8 26523447 26523928 26524391 481 267 463 257
9 26524391 26524855 26525336 464 258 481 267
10 26525336 26527791 26528272 2455 1364 481 267
11 26528272 26528753 26529216 481 267 463 257
我们能据此得出什么结论呢?

为了获取一点提示,我们可以参考https://github.com/merbanan/rtl_433/blob/master/src/devices/nexa.c,即rtl_433这个程序源码中对应nexa系列信号的解码参数。

r_device nexa = {
        .name        = "Nexa",
        .modulation  = OOK_PULSE_PPM,
        .short_width = 270,  // 1:1
        .long_width  = 1300, // 1:5
        .sync_width  = 2700, // 1:10
        .tolerance   = 200,
        .gap_limit   = 1500,
        .reset_limit = 2800,
        .decode_fn   = &nexa_callback,
        .fields      = output_fields,
};

可以看出:

  1. 我们得到的结果,脉冲长度大约在257到286微秒之间,与程序中的short_width给的数值270相符。
  2. 而除了第1个脉冲外,其余脉冲距离前一个脉冲的时间也只有两种情况:
    1. 一个脉冲长度,约270微秒;
    2. 一个长脉冲长度,约1360微秒。这就是程序中备注的 1:5 或 1300微秒的来源。
  3. 第一个脉冲与后续脉冲间隔较长,为2661微秒。对应程序中备注的 1:10 或 2700 微秒。根据sync_width这项的名字,可以认为这是某种前导信号,用于激活或者同步接收设备。

此外,另一个重要的分析线索是程序中modulation这项的值PPM。经过Google查询,得知PPM全称为Pulse Position Modulation,脉冲位置调制。具体在德语维基中有一个说明示意图:

waveform_timing

可以看出,这个与我们的分析是一致的。可以想象,如果要模仿遥控器控制我们的433MHz设备,需要:

  1. 根据遥控器的编码规则构建编码(本文尚未分析到这一步,但是可以根据RTLSDR的捕获结果直接得到)
  2. 以基础的270us为时间单位,控制433Mhz发射模块的开关,产生信号。具体步骤如下:
    1. 发射1个时间单位的同步信号
    2. 等待10个时间单位
    3. 根据要发射1还是0, 要么发射1个单位的信号后等待1个单位的时间,或者等待5个单位的时间
  3. 将上一步的信号重复6次发射。(重复间隔经过类似的分析,应该在10毫秒以上)

5. 接下去的步骤

接下来,为了实现控制,还要做的事情有:

  1. 研究遥控器发射信号的编码规则。包括House Code、单元或群组的控制信息。
  2. 研究如何使用单片机发射信号。
  3. 尝试使用我们的程序,给遥控插座重新设定House Code(即重新对码)。