前面的文章中我们已经知道如何获取遥控器的红外编码,以及通过单片机重放红外信号,来遥控空调。但是这样是远远不够的,要想让家电智能起来,我们需要把模块接入一些高级点的系统中,比如树莓派,然后通过Linux系统来控制红外发射。下面我们将介绍如何使用Linux与红外模块交互。

1.通信协议的制定

就目前而言,模块与外界通信的渠道只有串口可以使用,所以我们决定使用串口与树莓派进行通信。但是只要制定好双方通信的协议,两者才能合理正确地通信,不然就像你说英语我说中文,鸡同鸭讲。

通信协议采用比较简单的方式,包括起始位数据个数数据三个部分组成。

  • 起始位:占一个字节,用来标识报文的开始,数值为0x2B
  • 数据个数:占一个字节,用来标识之后数据的数量,最大为0XFF,即后面的数据最长为255个
  • 数据:传输的有效载荷,实际的数据
举个例子

2B 03 11 22 33

有效数据位 11 22 33

2.单片机端解析报文

单片机端需要在串口接收中断中根据协议来处理报文,处理的代码如下。


/*用来标识是否正在使用缓存区*/
bit buf_empty = 0;

/*报文起始位有效,接收后续报文*/
bit rec_start = 0;

/*该则报文接收到的数据个数*/
unsigned char rec_counter = 0;

/*该则报文需要接收的数据个数*/
unsigned char rec_sum = 0;

/*需要处理的数据个数*/
unsigned char snd_sum = 0;

/*接收缓存区*/
unsigned char idata recv_buff[MAX_BUF];

/*protocol: | start | byte count | byte0 | byte1 | ...*/
void Uart_Isr() interrupt 4 using 1
{
    /*receive data*/
    if (RI && buf_empty)
    {
        unsigned char ch = SBUF;
        RI = 0;          

        /*normal bytes*/
        if(rec_start && rec_sum != 0)
        {
            recv_buff[rec_counter++] = ch;
        }

        /*the amount of all bytes*/
        if(rec_start && 0 == rec_sum)
        {
            snd_sum = rec_sum = ch;
        } 

        /*start byte*/
        if(!rec_start && DATA_PREFIX == ch)
        {
             rec_start = 1; 
        }
        
        if(MAX_BUF == rec_counter || (rec_sum != 0 && rec_sum <= rec_counter))
        {
               /*buff is full*/
            rec_counter = 0;
            buf_empty = 0;
            rec_sum = 0;
            rec_start = 0;
        }       
    }

    /*send data*/
    if (TI)
    {
        TI = 0;             
        busy = 0;           
    }
}

3.树莓派端报文发送程序

树莓派端需要实现串口的读写即可,代码如下。

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <termios.h>

#define MAX_SIZE 256

void init_series(int fd)
{
    struct termios t ;

    tcgetattr( fd , &t) ;

    t.c_cflag &=~CSIZE ;

    t.c_cflag &=~CSTOPB;

    t.c_cflag |= CREAD ;

    t.c_cflag |= CLOCAL ;

    t.c_cflag &= ~CRTSCTS;

    t.c_cflag |= CS8;

    cfsetispeed( &t , B9600);
    cfsetospeed( &t , B9600);

    tcflush( fd , TCIOFLUSH);

    tcsetattr( fd , TCSANOW ,&t);
}

/*这里没有写协议封装函数*/
char air_on[] =  {0x2B, 104, 01, 01, 00, 00, 00, 00, 01, 01, 01, 01, 01, 00, 01,
            00, 00, 01, 00, 00, 00, 00, 00, 01, 01, 01, 00, 00, 00, 00, 00, 00,
            00, 00, 00, 00, 00, 00, 00, 01, 00, 01, 00, 00, 00, 00, 00, 00, 00,
            00, 00, 00, 00, 00, 00, 01, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
            00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 01, 00, 00, 00,
            00, 00, 00, 00, 00, 00, 00, 01, 00, 01, 00, 00, 00, 00, 00, 01, 01,
            01, 01, 01, 00, 00, 00};

char air_off[] = {0x2B, 104, 01, 01, 00, 00, 00, 00, 01, 01, 01, 01, 01, 00, 00,
            00, 00, 00, 00, 00, 00, 00, 00, 01, 01, 01, 00, 00, 00, 00, 00, 00, 
            00, 00, 00, 00, 00, 00, 00, 01, 00, 01, 00, 00, 00, 00, 00, 00, 00, 
            00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
            00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
            00, 00, 00, 00, 00, 00, 00, 01, 00, 01, 00, 00, 00, 00, 00, 01, 01,
            01, 01, 00, 00, 01, 00};

int main(int argc, char **argv)
{
    int fd ;
    int count ;

    char *buff_send = NULL;

    if(argc <2)
    {
        printf("please input args!\n");
        return 0;
    }

    fd = open("/dev/ttyUSB0" , O_RDWR  | O_NOCTTY );

    if(fd<0)
    {
        printf("open series error\n");
        return 0 ;
    }

    init_series(fd);

    switch(argv[1][0])
    {
        case 'n':
        case 'N': buff_send = air_on; break;

        case 'f':
        case 'F': buff_send = air_off; break;

        default: break;
    }

    if(buff_send != NULL)
    {
        write(fd , buff_send , 104+2);
    }

    close(fd);

    return 0 ;
}

OK啦,现在可以通过Linux来控制空调啦!但是这样貌似还不是很方便,如果想远程通过手机来控制的话可能会更方便,后面的文章将介绍搭建简单的物联网云平台,然后将手机和树莓派接入云平台实现远程控制空调。