Linux 的魅力: 这个古老的机器:使用 X10 实现家庭自动化

发表于:2007-05-25来源:作者:点击数: 标签:
80 年代人们的梦想之一是,有朝一日,每个人都拥有火箭汽车,计算机控制着家里的所有东西,比如灯。我们还没有拥有火箭汽车,但是 X10 协议使我们能够远程地开/关设备。在本文中,Peter Seebach 讲解如何使用现有的硬件和几百行简单的代码来设置和驱动 X10
80 年代人们的梦想之一是,有朝一日,每个人都拥有火箭汽车,计算机控制着家里的所有东西,比如灯。我们还没有拥有火箭汽车,但是 X10 协议使我们能够远程地开/关设备。在本文中,Peter Seebach 讲解如何使用现有的硬件和几百行简单的代码来设置和驱动 X10 设备。

理解 X10

X10 协议是用于在电源线上传输数据的相当原始的工具。可能正因为它相当原始,它也相当健壮,尽管好的线过滤器或电压尖峰抑制器可能会干扰它。对于连网来说,X10 的数据传输速度太慢了,但是对于远程开/关设备来说是合适的。

为了发送 X10 信号,需要在电子接线盒上插入某些东西并提供一个计算机接口。这些设备有 USB 和 RS232 串行接口两种形式;我使用串行版本。它们的价格大约是 30 美元,可以从几家厂商那里买到;我使用 Smarthome 的 1132B 型号(参见 参考资料)。真正的主要硬件是一个收发器:它接收 X10 信号并通过串行端口发送信号,还接收串行信号并通过线路发送它们。信号被广播;每个设备都会接收它们。但是,为了防止造成混乱,每个信号包含编码,表明它是发送给哪个或哪些设备的。

一般来说,信号包含房屋编码、单元编码和功能编码。顾名思义,房屋编码用于大的设备组;可以有 16 个房屋,名称为 A 到 P。典型的 X10 控制器单元可能有 4 个摇臂开关(对应单元 1-4)和一个刻度盘(表示要使用哪个房屋编码)。除非要使用大量设备,否则很可能可以为每个设备使用一个房屋代码。单元编码(1-16)指定特定的设备。一些比较老的硬件不支持 1-8 之外的单元编码;同样,一些比较老的设备只支持房屋编码 A-H。

许多设备有某种刻度盘或开关组,用来决定房屋和单元编码。一些比较新的设备可以以电子方式进行编程;以后这样的设备会更多。





回页首


硬件和连接

硬件很容易连接。我使用的设备是一个墙上插座附件,它有点儿像某些便携电子设备的变压器,底部有一个用来传输各种信号的 RJ45 端口。它连接一条信号线,提供标准的 9 针串行端口连接。没有流控制,9600,8N1。非常标准。

在 Linux® 上访问串行端口是相当简单的。默认的设置是访问只限于组 uucp 的成员;我将自己的帐号添加到这个组、注销并再次登录。

对端口进行初始化非常简单。清单 1 中的代码(取自示例程序)显示需要的初始化:


清单 1. 打开串行端口并设置参数
            int fd;
            struct termios t;
            fd = open(dev, O_RDWR);
            if (fd < 0) {
            fprintf(stderr, "Error opening '%s': %s\n",
            dev, strerror(errno));
            return -1;
            }
            tcgetattr(fd, &t);
            cfsetspeed(&t, B9600);
            cfmakeraw(&t);
            t.c_cflag &= ~(CSIZE);
            t.c_cflag |= CS8;
            t.c_cflag &= ~(PARENB);
            tcsetattr(fd, TCSANOW, &t);
            

dev 变量保存设备的路径;在默认情况下,它是 /dev/ttyS0。(在我用来实现这些项目的老式 Gateway 笔记本上,内置的串行端口是 COM1,或 ttyS0。本系列的 第一期 中介绍了这个笔记本。)主例程允许覆盖 dev;另一个可能使用的值是 /dev/ttyUSB0,这是使用 USB 端口时的路径。

tcgetattr/tcsetattr 操作(来自 termios.h)允许修改设备的设置。在几乎所有情况下,最好是获得原来的设置并修改它们,而不是自己完全填写一个 termios 结构。基本规则是设置需要的任何标志并清除不需要的任何标志。其他东西都不变。

这条规则有时候有例外。清单 2 显示第一个代码片段:


清单 2. 配置字符大小的一次失败的尝试
            t.c_cflag |= CS8;
            t.c_cflag &= ~(PARENB | CS6 | CS7);
            

这段看似合理的代码却将字符大小设置为 5 位。为什么呢?CS6 是 0x10,CS7 是 0x20,CS8 是 0x30。设置 CS8,然后清除 CS6 和 CS7,结果的就是 0x00,也就是 CS5 的值。清除 CSIZE 位,然后设置显式地设置需要的值,这样才可以。





回页首


协议

坦白地说,X10 协议是超现实的。RS232 有一个用来描述 X10 消息的协议,它甚至更不可思议。一个消息有 5 字节;0x63(“发送 X10 命令”)、房屋编码、单元编码、功能编码和重复次数。这些都在表中查找。例如,重复编码(表示重复一个信号;例如用于有衰减器的情况)作为字母 A 到 O 发送,A 代表 1,B 代表 2 等等。除了很少的几个例外(比如 “发送命令” 字节)之外,发送的每个值的范围是从 0x40 到 0x5F。

尝试在这个协议中寻找模式似乎很有意思。X10 十六进制编码的主要范围在 0x00-1F;RS232 协议似乎只增加了 0x40 以避免使用控制字符。但是,X10 编码采用的模式对于未经训练的人不容易理解。前几个单元编码(代表 1-4)是 0x0C、0x1C、0x04 和 0x14。(偶数单元编码总是设置了 0x10)。所有单元编码都是偶数;所有命令编码都是奇数。

协议还允许一些例外。一些命令(比如 “关闭所有单元”)不需要使用单元编码。对于这些命令,省略单元编码。不像您可能猜测的那样,设置为零;而是省略。次序就变成了 0x63、房屋编码、功能编码、0x00和重复次数。但是,因为所有单元编码都是偶数,所有命令编码都是奇数,所以这不会引起误解。





回页首


设计软件接口

驱动 X10 所用的代码并不太复杂,但是到处重复编写这些代码也很麻烦。UNIX® 的解决方案是一个实用程序,它可以从命令行发送 X10 命令。其他程序可以根据需要运行这个实用程序。执行并调试这个实用程序之后,可以将这个程序的组改为 uucp 并给它设置 setgid 位,从而限制对 X10 硬件的访问。

发送 X10 命令的次序在编写时没有二义性,但是对它进行解析很麻烦。我并不试图查明哪个部分是哪个参数,而是编写了一个实用程序来接收命令编码、房屋编码和单元编码。如果指定的命令不需要单元,就不读取单元。在表中查找命令、房屋编码和单元;用户以象征性方式指定它们。例如,命令 “x10 on a 1” 会打开房屋 A 中的单元 1。-h 选项或在一个字段中放上 “help” 会列出可用编码以及传输给 X10 适配器的原始编码。

我的初始设计忽略了重复编码,因为我觉得不太需要它们。X10 协议很慢,所以多次调用这个程序的开销不会导致显著的效率下降。

用来进行编码查找的结构将一个字符串和一个数值关联起来。可以添加一个 “标志” 字段,但是我在编写好接口之后才想到这一点,所以只能将标志放在无符号字符范围外的位上。这导致只有一个标志 SUPPRESS_UNIT,它用在不需要单元的命令上。特殊命令 none 用来发送没有命令的消息;一些可编程 X10 硬件可以使用这些消息配置房屋和单元的设置。

扩展软件

程序可以打开和关闭各种设备是很棒,但是它不够灵活;可以以几种方式改进这种状况。

首先,给设备分配符号名会很方便。在测试期间,我一共使用了三个设备(其中两个放在我家里),有几次我忘了哪个编码代表哪个设备。合理的做法是创建一个配置文件。我选择使用一种传统的相当典型的配置文件格式;每行有三个以空白分隔的字段,包含设备的符号名、房屋编码和单元编码。以 # 开头的行是注释。例如,清单 3 中的文件描述了几个设备:


清单 3. 将设备名映射到房屋和单元编码
            # the big lamp by the couch
            tvroom		 		  a		 		  1
            # I hooked up my little brother's nintendo so I can mess with him
            n64		 		  a		 		  2
            

按照与命令行相同的方式查找房屋和单元编码。

对衰减器的支持是另一个可以添加的特性;我还没有实现它,但是协议支持这么做。但是要注意一点:不要同时发送大量明/灭消息;接口太慢了。





回页首


提供 Web 界面

远程访问自己的灯是非常方便的,但是其实在大多数时候用处不大,通过 Web 界面打开灯也没什么意义。Web 页面是很容易使用,但是电灯开关更简单。这并不意味着 Web 界面没有用。通过 Web 界面控制家里的复杂电器可能是有意义的,例如打开另一间屋子中的咖啡壶。如果您习惯于总是在咖啡壶中放上咖啡粉和水,那么点击一下按钮,就可以开始煮咖啡。

另一个可以添加的特性是配置新的 X10 设备。我使用的灯控制器预先设置为设备 A1,没有开关或按钮。为了对它进行编程,插入它,然后在 30 秒内发送 3 个包含特定房屋和单元编码的 X10 消息;它就会设置成这个房屋和单元编码。从一个简单的 Web 界面执行这种操作是很棒的,尽管这种任务不需要频繁地执行。以下脚本的效果就很好:


清单 4. 设置设备的房屋和单元编码
            #!/bin/sh
            x10 none $*
            x10 none $*
            x10 none $*
            

用房屋和单元编码或 x10.conf 中的设备名进行调用,它就会对设备进行编程。





回页首


使用 cron 调度任务

使用 cron 调度某些操作(比如开灯)的执行时间非常简单。如果在渡假期间希望自己的房屋在晚上开灯,装出房子有人住的样子,那么在每天晚上同一时间开灯可能不太好。需要有点儿随机性。下面的简单脚本会在一小时内的某一时间执行操作:


清单 5. 延迟一小时内的随机时间
            #!/bin/bash
            sleep $((RANDOM / 10))
            $*
            

将这个脚本保存为 /usr/local/bin/soonish,然后可以使用下面这样的 cron 项:


清单 6. 在晚上开灯
            0 18 * * *		 		  /usr/local/bin/soonish x10 on tvroom
            0 21 * * *		 		  /usr/local/bin/soonish x10 off tvroom
            





回页首


结束语

这个月,我在送货费上超过了预算(我编写这个系列的动机和约束见 “Linux 的魅力:让古老的机器重获新生”)。X10 收发器、灯模块和文档的总价格是 49.97 美元。不幸的是,要加 8 美元的送货费。文档根本没什么价值;不但可以下载更好的文档,而且不知道为什么,送来的文档是 5 本同样的书,其中全是关于 X10 协议的技术信息,根本没有关于将什么信号发送到收发器的参考资料。所以,我原本可以省下为文档支付的 5 美元。

从长远来看,有多个模块可以控制是非常有好处的,但是这要花钱。我要注意控制预算;我有两个 1997 年买的 X10 模块,它们和新模块一样工作得很好。

原文转自:http://www.ltesting.net