一、参考

  • Arduino基础入门篇25—红外遥控
  • Arduino与红外遥控握手

二、目标

在日常生活中我们会接触到各式各样的遥控器,电视机、空调、机顶盒等都有专用的遥控器,很多智能手机也在软硬件上对红外遥控做了支持,可以集中遥控绝大部分家用电器。
当按下遥控器上某个按键,串口输出该按键的名称。

三、理论学习

红外遥控主要由红外发射和红外接收两部分组成。

红外发射和接收的信号其实都是一连串的二进制脉冲码,高低电平按照一定的时间规律变换来传递相应的信息。为了使其在无线传输过程中免受其他信号的干扰,通常都将信号调制在特定的载波频率上(38K红外载波信号),通过红外发射二极管发射出去,而红外接收端则要将信号进行解调处理,还原成二进制脉冲码进行处理。

红外接收头有三个引脚,如上图从左到右依次为VOUT、GND、VCC。红外遥控器发射的38K红外载波信号由遥控器里的编码芯片对其进行编码,具体编码方式和协议可在网上获取,这里不再展开。当按下遥控器按键时,遥控器发出红外载波信号,红外接收器接收到信号,程序对载波信号进行解码,通过数据码的不同来判断按下的是哪个键。

机顶盒android system recovery 3e 机顶盒遥控器学习方法_红外遥控

  • 本实验采用红外接收头VS1838+红外遥控器。VS1838使用NEC码编码格式
  • NEC码格式:
    1、使用38kHz载波频率
    2、引导码间隔9ms+4.5ms
    3、使用16位客户代码
    4、使用8位数据代码和8位取反的数据代码
    下面的波形是从红外接收头上得到的波形:(调制信号转换为高低电平了)

    5、二进制0和1的表示方法

四、接线图

机顶盒android system recovery 3e 机顶盒遥控器学习方法_#include_02


红外接收头左侧VOUT连接开发板数字引脚11,GND和VCC分别连接开发板的GND和5V。

实验原理图如下图所示:

机顶盒android system recovery 3e 机顶盒遥控器学习方法_嵌入式硬件_03

实物连接图

机顶盒android system recovery 3e 机顶盒遥控器学习方法_红外_04


两种规格遥控

机顶盒android system recovery 3e 机顶盒遥控器学习方法_红外遥控_05

五、代码

库安装

在IDE中点击「项目」—「加载库」—「管理库」,查找「IRremote」进行安装。

机顶盒android system recovery 3e 机顶盒遥控器学习方法_#include_06

测试按下按键通过窗口输出

我们演示右侧遥控器效果

机顶盒android system recovery 3e 机顶盒遥控器学习方法_红外遥控_07

#include <IRremote.h>

int RECV_PIN = 11;//定义红外接收器的引脚为11

IRrecv irrecv(RECV_PIN);
long ir_item; // 解码之后的值
decode_results results;//注意这个数据类型

void setup()
{
  Serial.begin(9600);
  irrecv.enableIRIn(); // 初始化红外接收器
}

void loop() {
  if (irrecv.decode(&results)) {  //注意这里取解码结果的方法,传递的是变量指针。
    ir_item = results.value;
    print(results);
    irrecv.resume(); // 给红外传送指令,让其继续接收下一个值。同315M无线接收一个道理。
  }
}

void print(decode_results result)
{
  long value = results.value;
  int type = results.decode_type;
  String typeStr = "UNKNOWN";
  String typelist[14]={"UNKNOWN", "NEC", "SONY", "RC5", "RC6", "DISH", "SHARP", "PANASONIC", "JVC", "SANYO", "MITSUBISHI", "SAMSUNG", "LG", "WHYNTER"};
  if(type>=1 && type<=13){
    typeStr=typelist[type];
  }
  Serial.print(ir_item, HEX); //以16进制换行输出接收代码。注意取值方法【results.value】。
  Serial.print(" = ");
  Serial.print("IR TYPE:"+typeStr+"  ");
  Serial.print(" = ");
  if(value==0xFF38C7)
  {
    Serial.println("ok");
  }
  else if(value==0xFFA25D)
  {
    Serial.println("1");
  }
  else if(value==0xFF629D)
  {
    Serial.println("2");
  }
  else if(value==0xFFE21D)
  {
    Serial.println("3");
  }
  else if(value==0xFF22DD)
  {
    Serial.println("4");
  }
  else if(value==0xFF02FD)
  {
    Serial.println("5");
  }
  else if(value==0xFFC23D)
  {
    Serial.println("6");
  }
  else if(value==0xFFE01F)
  {
    Serial.println("7");
  }
  else if(value==0xFFA857)
  {
    Serial.println("8");
  }
  else if(value==0xFF906F)
  {
    Serial.println("9");
  }
  else if(value==0xFF9867)
  {
    Serial.println("0");
  }
  else if(value==0xFF6897)
  {
    Serial.println("*");
  }
  else if(value==0xFFB04F)
  {
    Serial.println("#");
  }
  else if(value==0xFF18E7)
  {
    Serial.println("up");
  }
  else if(value==0xFF4AB5)
  {
    Serial.println("down");
  }
  else if(value==0xFF10EF)
  {
    Serial.println("left");
  }
  else if(value==0xFF5AA5)
  {
    Serial.println("right");
  }
  else{
    Serial.println("unKnown");    
  }  
}

机顶盒android system recovery 3e 机顶盒遥控器学习方法_红外_08

结合之前LED显示温度试验,按下按键把按键值显示到第一行

#include <IRremote.h>

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>

// #include <OneWire.h> //可以不引入,因为DallasTemperature.h中已经引入了OneWire.h
#include <DallasTemperature.h>

#define ONE_WIRE_BUS A0               //1-wire数据总线连接在IO4
OneWire oneWire(ONE_WIRE_BUS);       //声明
DallasTemperature sensors(&oneWire); //声明

LiquidCrystal_I2C lcd(0x27,16,2);  // set the LCD address to 0x27 for a 16 chars and 2 line display

int RECV_PIN = 11;//定义红外接收器的引脚为11

IRrecv irrecv(RECV_PIN);
long ir_item; // 解码之后的值
decode_results results;//注意这个数据类型

char lcdStr[16] = "hello, world!   ";
int index = 0;

void setup()
{
  Serial.begin(115200);
  irrecv.enableIRIn(); // 初始化红外接收器


  sensors.begin(); //初始化总线
  sensors.setWaitForConversion(false); //设置为非阻塞模式

  lcd.init();                      // initialize the lcd 
 
  // Print a message to the LCD.
  lcd.backlight();
  lcd.print(lcdStr);
}

void loop() {
  // 显示温度
  displayTemp();
  if (irrecv.decode(&results)) {  //注意这里取解码结果的方法,传递的是变量指针。
    ir_item = results.value;
    print(results);
    irrecv.resume(); // 给红外传送指令,让其继续接收下一个值。同315M无线接收一个道理。
  }
}

void print(decode_results result)
{
  long value = results.value;
  int type = results.decode_type;
  String typeStr = "UNKNOWN";
  String typelist[14]={"UNKNOWN", "NEC", "SONY", "RC5", "RC6", "DISH", "SHARP", "PANASONIC", "JVC", "SANYO", "MITSUBISHI", "SAMSUNG", "LG", "WHYNTER"};
  if(type>=1 && type<=13){
    typeStr=typelist[type];
  }
  Serial.print(ir_item, HEX); //以16进制换行输出接收代码。注意取值方法【results.value】。
  Serial.print(" = ");
  Serial.print("IR TYPE:"+typeStr+"  ");
  Serial.print(" = ");
  lcd.setCursor(0,0);
  if(value==0xFF38C7)
  {
    Serial.println("ok");
    displayValue(lcdStr,"ok",2);
  }
  else if(value==0xFFA25D)
  {
    Serial.println("1");
    displayValue(lcdStr,"1",1);
  }
  else if(value==0xFF629D)
  {
    Serial.println("2");
    displayValue(lcdStr,"2",1);
  }
  else if(value==0xFFE21D)
  {
    Serial.println("3");
    displayValue(lcdStr,"3",1);
  }
  else if(value==0xFF22DD)
  {
    Serial.println("4");
    displayValue(lcdStr,"4",1);
  }
  else if(value==0xFF02FD)
  {
    Serial.println("5");
    displayValue(lcdStr,"5",1);
  }
  else if(value==0xFFC23D)
  {
    Serial.println("6");
    displayValue(lcdStr,"6",1);
  }
  else if(value==0xFFE01F)
  {
    Serial.println("7");
    displayValue(lcdStr,"7",1);
  }
  else if(value==0xFFA857)
  {
    Serial.println("8");
    displayValue(lcdStr,"8",1);
  }
  else if(value==0xFF906F)
  {
    Serial.println("9");
    displayValue(lcdStr,"9",1);
  }
  else if(value==0xFF9867)
  {
    Serial.println("0");
    displayValue(lcdStr,"0",1);
  }
  else if(value==0xFF6897)
  {
    Serial.println("*");
    displayValue(lcdStr,"*",1);
  }
  else if(value==0xFFB04F)
  {
    Serial.println("#");
    displayValue(lcdStr,"#",1);
  }
  else if(value==0xFF18E7)
  {
    Serial.println("up");
    displayValue(lcdStr,"up",2);
  }
  else if(value==0xFF4AB5)
  {
    Serial.println("down");
    displayValue(lcdStr,"down",4);
  }
  else if(value==0xFF10EF)
  {
    Serial.println("left");
  }
  else if(value==0xFF5AA5)
  {
    Serial.println("right");
  }
  else{
    Serial.println("unKnown");    
  }  
}

// 将按键值显示在LED,并向右位移
void displayValue(char* buffer, char* value, int length)
{
  // LCD只能显示16位
  int size = 16;
  for(int i = size ; i > length ; i--)
	{
		buffer[i-1] = buffer[i-length-1] ;
	}
  memcpy(buffer, value, length);
  
  Serial.println(buffer);
  Serial.println(lcdStr);
  lcd.print(buffer);
}


unsigned long previousMillis = 0; //毫秒时间记录
const long interval = 1000;       //时间间隔
void displayTemp()
{
  //以下段落相当于每秒读取前次温度,并发起新一次温度转换
  unsigned long currentMillis = millis();         //读取当前时间
  if (currentMillis - previousMillis >= interval) //如果和前次时间大于等于时间间隔
  {
    previousMillis = currentMillis; //更新时间记录

    float tempC = sensors.getTempCByIndex(0); //获取索引号0的传感器摄氏温度数据
    if (tempC != DEVICE_DISCONNECTED_C)       //如果获取到的温度正常
    {
      Serial.print("\n当前温度是: ");
      Serial.print(tempC);
      Serial.println(" ℃");
      Serial.println(" ");

      String val = "temp:"+ String(tempC) + String("℃");
      
      lcd.setCursor(0,1);
      //lcd.print(val);
      // lcd.print((int)tempC/10);
      // lcd.print(".");
      // lcd.print((int)tempC%10);
      lcd.print("temp:");
      lcd.print(tempC);
      // 打印摄氏度
      lcd.print((char)223);
      lcd.print("C");
      
    }
    sensors.requestTemperatures(); //发起新的温度转换
  }
}