树莓派3使用wiringPI,模拟空调开关,实现空调遥控

1.背景

由于树莓派3无法安装LIRC,所以决定直接使用IO模拟NEC,对红外遥控进行捕获,存储,以及发送

前言

使用的板子是树莓派3,红外接收管为HS0038B,发射管为TSAL6200,对应的空调是海尔,遥控用的是手机模拟遥控

一、NEC是啥

红外协议,建议百度

二、处理思路

1.原理图

使用树莓派的P17作为发射脚,P18作为接收脚,调用树莓派3.3V作为发射电源,5V作为红外接收电源,限流电阻100R,发射电流最大为3.3*10=33mA

android仿空调调温效果 手机空调模拟_#include

PCB绘制后长这样,注意mos管的封装,之前画错了。

android仿空调调温效果 手机空调模拟_#include_02

如果觉得麻烦,可以直接飞线,用杜邦线/面包板直连,实验成功后再弄板子

2.代码流程

2.1.头文件

#ifndef TEST_H
#define TEST_H
#include <time.h>
#include <sys/time.h>
#include <fstream>
#include <map>
#include <iterator>
#define test_printf(format,...)  do{    \
        printf("LINE:%d "format,__LINE__,##__VA_ARGS__);        \
}while(0);


#define RECV_PINNUM 1  //gpio 18
#define SEND_PINNUM 0  //gpio 17


#define LOADWAVE(PIN,TL,TH)                                                     \
        digitalWrite((PIN),HIGH);                                               \
        delayMicroseconds((TL));                                                \
        digitalWrite((PIN),LOW);                                                \
        delayMicroseconds((TH));

#define LOADWAVECS(PIN,TL,TH,TIMES)                     \
        for(int ts=0;ts<(int)((TIMES));ts++){   \
                LOADWAVE(PIN,TL,TH)                                     \
        }



class NECWave{
public:
        NECWave(){
                LowTimeUs = 0;
                HightTimeUs = 0;
        };
        int LowTimeUs;
        int HightTimeUs;
};
void test(int choice,string filePath);

void initNecINPins(int inPin);
void initNecOUTPins(int outPin);
NECWave catchSignalWave(int inPin);             //捕获单个上升下降
void catchlirc(int inPin,vector<NECWave> &NecTimeCat,string savePath);  //捕获总函数
int caculateDepTime(struct timeval startT,struct timeval endT); // 计算时间差值
void sendWave(int outPin,vector<NECWave> NecTimeCat,string ifilePath);           //发送捕获的NecTimeCat
void VtNECWave2File(string path,vector<NECWave> NecTimeCat);
void File2VtNECWave(string path,vector<NECWave> &NecTimeCat);
#endif

源文件

#include "test.h"
#include "wiringPi.h"
#define OVERTIME_SECOND_NUM  15 //最大超时时间
#define LOADWAVELEVEL   0.35    //占空比(反)
#define LOADFREQ 38000          //载波频率
void test(int choice,string filePath)
{
        vector<NECWave> NecTimeCat;
        test_printf("test funs\r\n");
        //initNecPins(RECV_PINNUM,SEND_PINNUM);
        wiringPiSetup();
        if(choice == 0){
                initNecINPins(RECV_PINNUM);
                catchlirc(RECV_PINNUM,NecTimeCat,filePath);
        }else if(choice==1){
                initNecOUTPins(SEND_PINNUM);
                sendWave(SEND_PINNUM,NecTimeCat,filePath);
        }else if(choice==2){
                wiringPiSetup();
                pinMode(RECV_PINNUM,INPUT);
                pinMode(SEND_PINNUM,INPUT);     //输出设置为输入
        }else{
                cout<<"Err filename"<<endl;
        }
}

void initNecINPins(int inPin)
{
        pinMode(inPin,INPUT);
}

void initNecOUTPins(int outPin)
{
        pinMode(outPin,OUTPUT);
        digitalWrite(outPin,HIGH);
}

void VtNECWave2File(string path,vector<NECWave> NecTimeCat)
{
        ofstream ofs;
        ofs.open(path,ios::out|ios::binary);
        if(!ofs){
                cout<<exec("pwd")<<endl;
                printf("Err fileOpen write in path:%s\r\n",path.c_str());
                return ;
        }
        for(int num =0 ;num<NecTimeCat.size();num++){
                ofs.write((char*)&NecTimeCat[num].LowTimeUs,4);
                ofs.write((char*)&NecTimeCat[num].HightTimeUs,4);
                //ofs<<NecTimeCat[num].LowTimeUs;
                //ofs<<NecTimeCat[num].HightTimeUs;
        }
        ofs.close();
}
void File2VtNECWave(string path,vector<NECWave> &NecTimeCat)
{
        char buff[4];
        ifstream ifs;
        ifs.open(path,ios::in|ios::binary);
        if(!ifs){
                cout<<exec("pwd")<<endl;
                printf("Err fileOpen read in path:%s\r\n",path.c_str());
                return ;
        }
        while(!ifs.eof()){
                NECWave NecTwave;

                //ifs>>buff;
                ifs.read(buff,4);
                NecTwave.LowTimeUs = *((int *)buff);
                //ifs>>buff;
                ifs.read(buff,4);
                NecTwave.HightTimeUs = *((int *)buff);

                if(ifs.eof()){
                break;
                }
                //ifs>>NecTwave.LowTimeUs;
                //ifs>>NecTwave.HightTimeUs;
                NecTimeCat.push_back(NecTwave);
        }
        ifs.close();

}
void catchlirc(int inPin,vector<NECWave> &NecTimeCat,string savePath)
{
        bool flag=false;
        double val;
        int index = 0;
        //NECWave singleWave;
        int usCountsLow,usCountsHIGH;
        struct timeval startT,endT;
        test_printf("start catch\r\n");
        int cntTs = 0;
        while(1){
                NECWave singleWave = catchSignalWave(inPin);
                if(singleWave.LowTimeUs==0 && singleWave.HightTimeUs==0){
                        if(index==0){
                                test_printf("End wave Or Over time!\r\n");
                                break;
                        }else{
                                test_printf("recv data\r\n");
                                break;
                        }
                }
                if(singleWave.HightTimeUs > OVERTIME_SECOND_NUM*1000000){ // Second
                        test_printf("OVER TIME BREAK\r\n");
                        break;
                }
                if(abs(singleWave.LowTimeUs- singleWave.HightTimeUs)>100000){ //100ms
                        if(index==0){
                                continue;
                        }
                        test_printf("END TIME BREAK\r\n");
                        cout<<"LowTimeUs:"<<singleWave.LowTimeUs<<endl;
                        cout<<"HightTimeUs:"<<singleWave.HightTimeUs<<endl;
                        index++;
                        singleWave.HightTimeUs = 4000;
                        NecTimeCat.push_back(singleWave);
                        break;
                }
                if(index==0){
                        if(singleWave.HightTimeUs>400){
                                        flag = true;
                        }else{
                                if(cntTs==3){
                                        break;
                                }
                                cout<<"Times"<<endl;

                                cout<<"LowTimeUs:"<<singleWave.LowTimeUs<<endl;
                                cout<<"HightTimeUs:"<<singleWave.HightTimeUs<<endl;
                                delay(1000);
                                cntTs ++;
                                index = 0;
                        }
                }
                if(flag==true){
                        index++;
                        NecTimeCat.push_back(singleWave);
                }
        }
        if(flag==true){
                for(int num=0;num<NecTimeCat.size();num++){
                        test_printf("num:%d Low=%d Hight=%d\r\n",num,NecTimeCat[num].LowTimeUs,\
                        NecTimeCat[num].HightTimeUs);
                }
                VtNECWave2File(savePath,NecTimeCat);
        }else{
                printf("unfound signal in\r\n");
        }


}
NECWave catchSignalWave(int inPin)
{
        NECWave singleWave;
        struct timeval startT,endT,OverTime,OverTst;



again:
        gettimeofday(&OverTst,NULL);
        while(digitalRead(inPin)==HIGH){
                gettimeofday(&OverTime,NULL);
                if((OverTime.tv_sec-OverTst.tv_sec)>OVERTIME_SECOND_NUM){       //连续持续两秒的NEC不存在!
                        printf("cattime over1!\r\n");
                        return singleWave;
                }
        }
        gettimeofday(&startT,NULL);     //获取起始时间  下降沿时刻
        while(digitalRead(inPin)==LOW);
        gettimeofday(&endT,NULL);       //获取结束时间  上升沿时刻
        singleWave.LowTimeUs = caculateDepTime(startT,endT);

        if(singleWave.LowTimeUs<450){
                goto again;
        }


        while(digitalRead(inPin)==HIGH){
                gettimeofday(&OverTime,NULL);
                if((OverTime.tv_sec-OverTst.tv_sec)>(OVERTIME_SECOND_NUM+1)){   //连续持续两秒的NEC不存在!
                        printf("cattime over2!\r\n");
                        singleWave.LowTimeUs = 0;
                        singleWave.HightTimeUs = 0;
                        return singleWave;
                }
        }
        gettimeofday(&startT,NULL);     //获取起始时间  下降沿
#if     0
        while(digitalRead(inPin)==LOW);
        gettimeofday(&endT,NULL);       //获取结束时间
        singleWave.HightTimeUs = caculateDepTime(startT,endT);
#else
        singleWave.HightTimeUs = caculateDepTime(endT,startT);
#endif
        return singleWave;
}

int caculateDepTime(struct timeval startT,struct timeval endT)
{
        int usCountsStart,usCountsEnd;
        usCountsStart = startT.tv_sec * 1000000 + startT.tv_usec;
        usCountsEnd   = endT.tv_sec * 1000000 + endT.tv_usec;
        return  (usCountsEnd - usCountsStart);
}

void sendWave(int outPin,vector<NECWave> NecTimeCat,string ifilePath)           //发送捕获的NecTimeCat
{
        File2VtNECWave(ifilePath,NecTimeCat);
        for(int num=0;num<NecTimeCat.size();num++){
                test_printf("num:%d Low=%d Hight=%d\r\n",num,NecTimeCat[num].LowTimeUs, \
                NecTimeCat[num].HightTimeUs);
        }
        vector<int> loadcycleVts;
        int index=0;
        float fPeriodUs = 1000000 / LOADFREQ;
        uint32_t u32PeriodUs = fPeriodUs;
        uint32_t u32LoadHeightTime = u32PeriodUs * LOADWAVELEVEL;
        uint32_t u32LoadLowTime    = u32PeriodUs - u32LoadHeightTime;
        uint32_t loadcycleTs;
        cout<<"load Wave HeightTimeUs"<<u32LoadHeightTime<<endl;
        cout<<"load Wave LowTimeUs"<<u32LoadLowTime<<endl;
        for(auto wavePtr=NecTimeCat.begin();wavePtr!=NecTimeCat.end();wavePtr++){
        loadcycleTs = (wavePtr->LowTimeUs+u32PeriodUs) / u32PeriodUs;           //预先把轮次获取   除法吃时间
                loadcycleVts.push_back(loadcycleTs);
        }
        for(auto wavePtr=NecTimeCat.begin();wavePtr!=NecTimeCat.end();wavePtr++){

                if(1){
                loadcycleTs = loadcycleVts[index];
                        LOADWAVECS(outPin,u32LoadLowTime,u32LoadHeightTime,loadcycleTs);
                }else{
                        delayMicroseconds(wavePtr->LowTimeUs);
                }


                digitalWrite(outPin,LOW);
                delayMicroseconds(wavePtr->HightTimeUs - u32PeriodUs*3);
                index++;
        }
        digitalWrite(outPin,HIGH);
}

main文件

#include <public.h>
#include <tool.h>
#include <test.h>

int main(int argc,char *argv[])
{
        cout<<"main funs"<<endl;
        cout<<exec("pwd")<<endl;
        if(argc<2){
                printf("less args\r\n");
                printf("para as : ./run.sh \"-s\" \"../data/outfile.bin\"\r\n");
                printf("para as : ./run.sh \"-r\" \"../data/outfile.bin\"\r\n");
                printf("para as : ./run.sh \"rest\" \" \"\r\n");
//#define DEBUG
#ifndef DEBUG
//              test("../data/open.txt","../data/open.txt");
#else
//              test("./data/open.txt","./data/open.txt");
#endif
        }else{
                string fsname="";
                printf("filename= %s para = %s\r\n",argv[2],argv[1]);
                if(!strcmp(argv[1],"-r")){
                        fsname.assign(argv[2]);
                        test(0,fsname);
                }else if(!strcmp(argv[1],"-s")){
                        fsname.assign(argv[2]);
                        test(1,fsname);
                }else if(!strcmp(argv[1],"rest")){
                        test(2,fsname);
                }else{
                        cout << " err io files "<<endl;
                }
        }
        return 0;
}

简要说明
传参0 “-r”,捕获红外遥控通信,参数1 表示捕获到的红外文件存储路径;
传参0 “-s”,将存储的文件转换为红外发射出去,参数1 表示红外文件路径
传参0"-rest",复位

./bin/wiringPi_T1 "-r" "./data/open.bin"   \\读取捕获红外,存入open.bin
 ./bin/wiringPi_T1 "-s" "./data/open.bin"   \\发射红外文件open.bin

总结

1.红外发射的管子最小电流需要在20mA左右,不可以直接使用树莓派的IO直接驱动
2.红外接收器,默认高电平自带38KHz的载波信号.无载波信号,红外接收器无法接收到载波.
3.如果一直无法成功通信,那么可以做自收自发测试,用树莓派的红外发射器发射,再用树莓派的红外接收器接收. 比对时间
4.归根结底,还是由于有的树莓派不支持lirc_rpi,需要更换版本啥的,很麻烦
5.单片机实现原理大致差不多.

6.树莓派实现了这个功能有啥用
(1)夏/冬天天下班前半小时可以提前开空调
(2)每天/周,周期性的开关空调(空调频繁开关不节约电费,详情自个百度)
(3)加装一个温度传感器,可以进行闭环温度调控.