linux解析lirc信号
网络上找了下linux解析红外信号的方法,大多提到使用lirc程序,但使用后发现很不好用,mode2可以顺利看到lirc信号,但如果想解析为实际的code就有点麻烦了。irrecord是真的难用,于是想可不可以只使用最简单的设备读取操作来解析红外控制器的按键码拉。经过实际观察和测试,是可行的,而且方式也不复杂。只需要稍对nec编码格式有个了解就可以做到了。
简单介绍一下NEC编码
NEC编码协议可以算红外控制器最通用的一个格式了。只需要一个引脚就可以实现红外信号的发送与接收。信号发送要麻烦点,除了基本的信号格式外还有一个加38K方波过程。我的目标只是信号的解析因此不用管这里的38K方波。
每次按下一个红外按键后,控制器都会以一个NEC格式的编码发送一串信号。红外接收头收到这一串信号后会将其转换为发送时的原始信号。这个原始信号一般就8个字节,也可能会多1,2个验证字节。实际我们只需要关心前8个字节内容就可以了。
开始信号是一个9mS高电平信号,接着是4.5mS低电平信号,再后面就是数据了。高电平是0.5mS低电平和1.69mS高电平。低电平是0.56mS低电平和0.56mS低电平。最后一般再来一个2,3ms的结束电平信号。
看看从lirc设备读取的第个按键信号
将下面代码与入一个C文件中编译运行,就可以看到读取到的lirc设备数据是什么样子的了。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
// 自己的lirc0设备名,可能需要改变
#define IR_DEVICE "/dev/lirc0"
int main(int argc, char **argv)
{
int i, size;
int fd = -1;
unsigned short buff[50];
fd = open(IR_DEVICE, O_RDONLY);
if(fd < 0)
{
fprintf(stderr, "Open event1 error\n");
return -1;
}
while(1)
{
size = read(fd, buff, sizeof(buff));
if(size)
{
/* 因为buff是short型(16位),因此显示时长度只有一半 */
for(i = 0; i < size / 2; i++)
{
if(i == size/2 - 1)
printf("%x\n", buff[i]);
else
printf("%x ", buff[i]);
}
}
}
close(fd);
return 0;
}
下面是运行结果,按下同的按键会出现不同的数据,注意这里的数据是以16进制的方式显示的。
ffff ff 23a8 100 1198 0 250 100 230 0 250 100 698 0 250 100 230 0 248 100 238 0 248 100 230 0 250 100 698 0 250 100 230 0 248 100
238 0 248 100 6a0 0 250 100 228 0 250 100 698 0 250 100 698 0 250 100 698 0 250 100 230 0 248 100 698 0 250 100 698 0 250 100 698 0 250 100 698 0
250 100 230 0 248 100
238 0 248 100 6a0 0 250 100 228 0 250 100 230 0 250 100 698 0 250 100 230 0 248 100 238 0 248 100 698 0 258 100 690 0 250 100 248 0 238 100 698 0 250 100
698 0 250 100 230 0 248 100
5400 300
- 使用自己的控制器多按几个按键观察,可以发现第一个数字一般都比较大FFFF,或其它比较大的数字,第三个数字很稳定23a8左右变化不大,第五个也变化不大。前面介绍NEC协议时说过有9mS和4.5mS的开始信号,可以猜测这里的FFFF,23A8,1198都是开始信号。
- 后面出现比较多的就是0,100,250,698左右的数据。也是根据NEC协议可以猜测250表示数字信号0,698表示数字信号1。而它们之间出现的0,100全可以看作是过程信号,直接忽略。
- 每个按键后面都会出现一个5400 300。这两个数字一出现后面基本都没有什么有用信息了。因此将5400作为信号结束标志。
编程测试
将下面代码保存为一个C文件,同样编译运行,查看结果。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/input.h>
#define IR_DEVICE "/dev/lirc0"
int fd = -1;
/*
* @brife : 将数据转换为0,1
* @param : value实际值, 250或698左右
* @return : 0-250,1-698
*/
unsigned char getByte(unsigned short value)
{
if(value > 0x0500)
return 1;
else
return 0;
}
/*
* @brife : 获取一个控制器的解析数据
* @param : 解析数据保存地址
* @return : 获取到的解析数据长度
*/
unsigned char getOneKey(unsigned short *parseWord)
{
unsigned short buff[50];
unsigned char ret;
unsigned char start = 0;
unsigned char i;
unsigned char wordSize, bitIndex;
while(1)
{
// 读取数据,这里会多次读取
ret = read(fd, buff, sizeof(buff));
if(ret > 0)
{
for(i = 0; i < ret/2; i=i+2)
{
/* 还没有检测到开始信号 */
if(!start)
{
/* 当前数据就是开始信号 */
if(buff[i] > 0x1000 && buff[i + 2] > 0x2200 && buff[i + 2] < 0x2500)
{
start = 1;
i = i + 4;
wordSize = 0;
bitIndex = 0;
parseWord[0] = 0x0000;
}
// 忽略
else
{
}
}
else
{
/* 找到结束依赖0x5400 */
if(buff[i] > 0x5000 && buff[i] < 0x5500)
{
return (wordSize);
}
/* 数据解析 */
else
{
parseWord[wordSize] = parseWord[wordSize] | getByte(buff[i]);
parseWord[wordSize] = parseWord[wordSize] << 1;
bitIndex++;
if(bitIndex > 15)
{
bitIndex = 0;
wordSize++;
parseWord[wordSize] = 0x0000;
}
}
}
}
}
}
}
int main(int argc, char **argv)
{
int i;
// 用于保存解析好后的数据信息
unsigned short parseData[10];
unsigned char len;
fd = open(IR_DEVICE, O_RDONLY);
if(fd < 0)
{
fprintf(stderr, "Open event1 error\n");
return -1;
}
while(1)
{
len = getOneKey(&parseData[0]);
/* 排除数据太短的无用信息 */
if(len > 1)
{
printf("Get data: ");
for(i = 0; i < len; i++)
{
if(i != len - 1)
printf("%x, ", parseData[i]);
else
printf("%x\n", parseData[i]);
}
}
}
close(fd);
return 0;
}
下面是我使用2种不同的红外控制器测试以上程序的结果
Get data: 2020, 8a8a, 2a0a, 80a0
Get data: 2020, 8a8a, 208a, 8a20
Get data: 2020, 8a8a, 8282, 2828
Get data: 2020, 8a8a, 800a, 2aa0
Get data: 2020, 8a8a, 202, a8a8
Get data: 2020, 8a8a, 8882, 2228
Get data: 2020, 8a8a, 2, aaa8
Get data: 2020, 8a8a, 8a8a, 2020
Get data: 2020, 8a8a, 8802, 22a8
Get data: 2020, 8a8a, 8002, 2aa8
Get data: 2020, 8a8a, a02, a0a8
Get data: 2020, 8a8a, 2802, 82a8
Get data: 0, aaa2, a880, 22a
Get data: 0, aaa2, 8280, 282a
Get data: 0, aaa2, 280, a82a
Get data: 0, aaa2, 208, a8a2
前面的2020是一个控制器,后面0开头的又是另外一个控制器。可以明显看到规律。同一个控制器前2个数据都是一样的,后两个值就是按键信息。
第一个控制器的data[0] | data[1] = 0xAAAA, data[2] | data[3] = 0xAAAA。明显0xAAAA可以作为数据接收是否合法的验证码。如果有出现不是这个规律的数据直接丢弃就可以了。
第二个控制器前两个数据也是固定的,但不满足0xAAAA的规律,但后面二个数据还是满足此规律的,因此后面一个控制器可以直接验证前2位数据值,后两位同样求或后是否满足0xAAAA作为验证条件。
多按几个按键可以看到第个按键后2位数据都是不一样的,因此满足按键唯一性要求。从以上数据也完全可以说,使用此方法解析lirc信号也可行的。