通过Linux::IRPulses解析红外代码来控制疯狂

使用脉冲式不可见光发送信息可能非常复杂。使用LIRC和Linux::IRPulses来解开这个问题。

红外遥控器是那些制造商都认为他们有“唯一真正的方法™”去做的事情之一。你可能认为只有一种或两种简单的方式来脉冲一点红外光。显然,我们都错了,因为家庭娱乐行业一直在发明新的方法。这还不包括其他行业和爱好项目,它们提出了完全不同的方法。

Linux红外遥控 (LIRC) 项目为很多遥控器提供了映射。但这对于一些边缘设备没有帮助。此外,堆栈顶部的某些组件更倾向于在检测到有效的脉冲序列后执行程序。

如果我们更愿意在我们的程序中处理脉冲,那么我们需要忽略LIRC的上层,并直接解析脉冲数据。这正是Linux::IRPulses所做的事情。

我们首先需要硬件来检测脉冲。在普通电脑上,有许多模块可供使用,可以插入USB端口。在像树莓派这样的单板电脑上,我们有通用输入/输出(GPIO)引脚,可以读取脉冲的定时。

设置树莓派

如果你使用的是常规红外设备,请跳过此部分。如果你想设置树莓派GPIO引脚上的模块,请继续阅读。

首先,你需要一个正确频率的模块来接收你正在尝试接收的红外数据。如果你使用的是旧电视的遥控器,那么搜索“红外协议”应该会得到正确的答案。38 KHz是一种常见频率,但这只是制造商最初做的不同之处。

TSOP38138是一个38KHz的红外遥控接收器。它是不同频率设备家族的一部分,其中任何一个都可能足够。

用于拾取遥控数据的红外接收器有三个引脚:电源、接地和数据。将电源连接到树莓派的+3.3V引脚,将接地连接到接地,将数据连接到GPIO 23。有关引脚位置,请参阅树莓派GPIO文档

现在我们需要配置LIRC。首先,使用apt-get install lirc进行简单安装。接下来,我们需要加载内核模块,告诉LIRC引脚的位置,并配置一些树莓派启动选项。

/etc/modules-load.d/modules.conf中,放入以下内容:

lirc_dev
lirc_rpi gpio_in_pin=23 gpio_out_pin=22

这将使GPIO 23成为你的输入引脚。LIRC也可以设置来发送红外数据,所以我们在这里将GPIO 22设置为该目的。接下来,修改/etc/lirc/hardware.conf

# /etc/lirc/hardware.conf
#
# Arguments which will be used when launching lircd
LIRCD_ARGS="--uinput"

#Don't start lircmd even if there seems to be a good config file
#START_LIRCMD=false

#Don't start irexec, even if a good config file seems to exist.
#START_IREXEC=false

#Try to load appropriate kernel modules
LOAD_MODULES=true

# Run "lircd --driver=help" for a list of supported drivers.
DRIVER="default"
# usually /dev/lirc0 is the correct setting for systems using udev
DEVICE="/dev/lirc0"
MODULES="lirc_rpi"

# Default configuration files for your hardware if any
LIRCD_CONF=""
LIRCMD_CONF=""

这里特别重要的是DEVICE(设备路径)和MODULES(树莓派GPIO驱动程序)。

最后,编辑/boot/config.txt并在文件中添加以下内容:

dtoverlay=lirc-rpi,gpio_in_pin=23,gpio_out_pin=22

然后重启。一旦重启,你可以通过将红外模块插入正确的引脚并对准它来测试它。使用mode2 -d /dev/lirc0,你应该会看到发送的pulsespace数据。

解码不可解码的内容

索尼的遥控器使用40KHz的频率。它通过发送2400μs的脉冲和600μs的空间作为头部开始。在头部之后,通过1200μs的脉冲发送1位,通过600μs的空间发送0。这些1和0之间是600μs的空间。代码的长度可能是12、15或20位,具体取决于遥控器。这已经是相当直接的了。

NEC使用38KHz的载波频率。头部后面跟着9000μs的空间。1位通过562.5μs的脉冲发送。0位也通过562.5μs的脉冲发送。等等,这是什么?不,这不是打字错误。NEC通过脉冲后的空间长度来区分1和0:1为1687.5μs,0为562.5μs。

EasyRaceLapTimer(一个开源的多旋翼赛车计时系统)使用38KHz的频率。它发送300μs的脉冲和300μs的空间。然后交替发送脉冲和空间,1位为600μs,0位为300μs。

上面所有的计时数字都是大错特错。这个嘈杂的、模拟的世界性质意味着来自红外接收器的实际值将与指定值不同,可能多达15%。可以安全地假设反向工程规格只是对制造商意图的实际值进行猜测。

总之,我们面前有一项复杂的工作,上面的内容只是涵盖了其中的一些例子。

Linux::IRPulses

该模块的目标是简化读取这些脉冲和空间的过程,同时容忍数值的不准确。

目前,该模块通过解析LIRC的mode2程序输出工作。这可能会在未来改为直接从/dev/lirc0读取。现在,我们将从打开到mode2的管道开始。

open( my $in, '-|', 'mode2 -d /dev/lirc0' ) or die "Can't exec mode2: $!\n";

我们现在需要定义Linux::IRPulses构造函数的协议。为此,添加use Linux::IRPulses将导出子例程pulse()space()pulse_or_space()。这些用于指定您期望接收的脉冲或空间。

例如,我们知道NEC发送9000μs的脉冲和4500μs的空间作为其头部。我们通过以下方式告诉构造函数:

my $ir = Linux::IRPulses->new({
    header => [ pulse 9000, space 4500 ],
    ...
});

解析器遍历数组的每个条目,检查给定的脉冲或空间数据是否符合预期。一旦它到达头部数组的末尾,它将头部标记为良好,然后寻找1和0的有效数据。我们以相同的方式指定这些。我们还将添加其他构造函数参数。

my $ir = Linux::IRPulses->new({
    header => [ pulse 9000, space 4500 ],
    zero => [ pulse 563, space 563 ],
    one => [ pulse 563, space 1688 ],
    bit_count => 32,
    callback => sub {
        my ($args) = @_;
        my $code = $args->{code};
        say "Received code $code";
    },
});

解析器将继续寻找1和0,直到收集到足够多的bit_count。一旦达到正确的数量,它将调用在callback中指定的子例程,并传递一个散列引用。散列引用包含code(检测到的红外代码)和pulse_obj(Linux::IRPulses对象)键。所有长度数字都检查20%的容差。

我们不敢保证您在处理红外数据后仍能保持理智,但希望Linux::IRPulses可以帮助您有尊严地发疯。

(原始照片CC-BY 2.0由Stefanus Ming在https://flic.kr/p/7djHYP提供)


本文最初发表在PerlTricks.com上。

标签

Timm Murray

Timm Murray是一位Perl程序员和硬件专家。他是事实上的标准Perl无人机库UAV::Pilot的创建者。他经常写博客关于Perl和硬件黑客技术。

浏览他们的文章

反馈

这篇文章有什么问题吗?请帮助我们通过在GitHub上创建问题或提交拉取请求来解决。