Understanding and using infrared signals from a remote control via an Arduino and an IR sensor

The used infrared light sensor
In the previous articles on measuring IR pulses and controlling equipment with an IR LED we learnt how our Arduino can help us retreiving the pulses from a remote control and using in later together with an infrared light source. Now we want to understand how the code works, so that we can set up our own machine that can be controlled with an infrared remote control. For this we have to have a closer look on the pulse sequence and how infrared equipment communicates.




A usual remote control is sending the signal in three components: a starting block, middle and ending block with very typical pulse lengths. Let's look at an example IR pulse sequence, measured with the arduino using this tutorial:

| ON | OFF |          (whereby OFF was calculated with the micros() command)

8960,4507,
537,570,
554,580,
524,608,
528,582,
554,583,
557,569,
535,573,
583,550,
594,1658,
562,1714,
522,1769,
503,1678,
562,1709,
535,1714,
566,1678,
554,1714,
554,1682,
566,572,
536,570,
562,1717,
527,591,
545,586,
578,557,
523,584,
552,587,
549,1683,
561,1709,
531,580,
556,1718,
526,1773,
523,1719,
497,1711,
533,40054,
8950,2247,
553,1194672,
   Listing has ended, amount of pairs: 36
-------- New record: --------

| ON | OFF |          (whereby OFF was calculated with the micros() command)

8957,4504,
536,574,
558,577,
587,547,
533,582,
554,578,
586,552,
528,579,
553,581,
583,1656,
560,1711,
533,1715,
581,1663,
553,1717,
527,1704,
592,1651,
565,1716,
532,578,
554,579,
581,556,
528,1714,
582,540,
536,575,
565,625,
535,552,
532,1713,
579,1663,
557,1716,
528,572,
560,1708,
536,1715,
581,1658,
558,1716,
528,40055,
8929,2259,
565,1194672,
   Listing has ended, amount of pairs: 36
We see two sequences from different buttons of the remote. The amount of pairs is equal, as well as beginning and ending pulses: The beginning looks like 8957 us, 4507 us and the ending is obviously something like 40055 us, 8929 us, 2259 us (where us = microseconds, what equals one millionth second). What is between differs, related to the different button. 
Important is that we cannot say the pulses are exactly the same all the time - pulses like 580 us and 540 us are equal in the range of imprecision. With this information we can shorten, or better to say smoothen the code in blocks of around 560 us, thus we have blocks of 4x, 8x and 16x this value plus one of about 40 milliseconds (8960 is 16 times 560 f.ex.). These general pulse blocks are important when we want to use a remote control to operate with the IR sensor and the Arduino, because we need general pulse schemes, as single pulses differ in the microseconds range but nevertheless contain the same information (they seem to differ, because the measuring is not 100 % accurate, what is no more a problem when we understand the codeblocks).

Now we tell the Arduino to output the simplified sequence in addition to the normal output by using a program like this: 

// THIS CODE WAS IMPLEMENTED BY PATRICK BAYER, patriq@web.de IN THE YEAR 2014. DO NOT CLAIM THIS CODE FOR YOUR OWN IN ANY WAY AND QUOTE THE SOURCE WHEN YOU USE IT.

#define valuepairs 100     // amount of rows in the matrix; int is not possible because the matrix definition below doesn't allow it.
int measuremethod1 = LOW;  // the value is LOW, so pulseIn() waits for the pin to go LOW, starts timing, then waits for the pin to go HIGH and stops timing.
int measuremethod2 = LOW;  // set this to HIGH if you want so see the result measuring only HIGH pulses; you loose the first pulse by doing this by the way
int measureoutput;         // placeholder for serial output
byte irpin=2;              // IR Sensor position on Arduino
long matrix[valuepairs][2];    // definition of the matrix -> 100 y rows and 2 x culumns, resulting in 200 storable values in total; se
int ende;                  // variable integer for ending, cause matrix is normally not filled totally
byte changemethods=1;      // starting mode; in the loop sequence always HIGH,LOW,HIGH,LOW,... measure
int blocklength=556;       // code block length, differs with different remote control
int imprecisionrange=150;  // the range we look up- and downwards the defined blocklength
                
//----------- The program begins here

void setup() {   Serial.begin(9600); }

void loop()                             {
 
  savePulseSequence();  
  if(ende>0) {serialOutput();}
                                        }


//----------- We define the smoothening of our output values by assigning a multiplier in respect to the defined blocklength
byte multiplier (long rawblocklength){byte ausgabe;{for(int j=1;j<100;j++){if(rawblocklength*10/blocklength<=j*10+2 && rawblocklength*10/blocklength>=j*10-2){ausgabe=j;}}}return(ausgabe); }

//----------- We define the rounding of our output values; change rd=10 to f.ex. 100 to round on full hundreds and so on
long roundvalues(long rundwert){byte rd=10; if(rundwert-rundwert/rd*rd>(rd/2)){return(rundwert/rd*rd+rd);}else{return(rundwert/rd*rd);}}                   

//----------- We define the measuring and saving of our pulse sequence
void savePulseSequence()
{
  ende=valuepairs;  // for resetting
  if(changemethods==1)                                     {
    for(int i=0;i<valuepairs;i++)                                                             {
      matrix[i][0]=pulseIn(irpin, measuremethod1);
      matrix[i][1]=micros();
      if(matrix[i][0]==0){ende=i; i=valuepairs; if(matrix[0][0]>1){changemethods=0;}}     }
    measureoutput=measuremethod1;                          }
  else                               { 
    for(int i=0;i<valuepairs;i++)                                                             {
      matrix[i][0]=pulseIn(irpin, measuremethod2);
      matrix[i][1]=micros();
      if(matrix[i][0]==0){ende=i; i=valuepairs; if(matrix[0][0]>1){changemethods=1;}}     }
    measureoutput=measuremethod2;    }
}

//----------- This is the function needed for the output of the measured signal pulses
void serialOutput()
{
  if(measureoutput==0){Serial.println("-------- New record: --------");Serial.println(); Serial.println("| ON | OFF |          (whereby OFF was calculated with the micros() command)");Serial.println();}
  else              {Serial.println("-------- New record: --------");Serial.println(); Serial.println("| OFF | ON |          (whereby ON was calculated with the micros() command)");Serial.println();}
   
  for(int i=0;i<ende;i++)                    {     // Output of the raw pulse sequence
      Serial.print(matrix[i][0]);
      Serial.print(",");
      Serial.print(matrix[i+1][1]-matrix[i][1]-matrix[i+1][0]);              
      Serial.print(",");
      Serial.println();                      }
  Serial.print("   Listing has ended, amount of pairs: ");Serial.println(ende);
   
  Serial.print("   CSV: ");                        // Output values as comma separated values
    for(int i=0;i<ende;i++)                   {
      Serial.print(matrix[i][0]);
      Serial.print(",");
      Serial.print(matrix[i+1][1]-matrix[i][1]-matrix[i+1][0]);              
      Serial.print(",");                      }
  Serial.println(); 
 
  Serial.print("   CSV smooth: ");                 //  Now we output the smooth values
    for(int i=0;i<ende;i++)                   {
      Serial.print(roundvalues(matrix[i][0]));
      Serial.print(",");
      Serial.print(roundvalues(matrix[i+1][1]-matrix[i][1]-matrix[i+1][0]));              
      Serial.print(",");                      }
  Serial.println();
 
  Serial.print("   Multiplier: ");                 // Output the calculated multiplier, with respect to the defined blocklength
    for(int i=0;i<ende;i++)                   {
      Serial.print(multiplier(matrix[i][0]));
      Serial.print(",");
      Serial.print(multiplier(matrix[i+1][1]-matrix[i][1]-matrix[i+1][0]));              
      Serial.print(",");                      }
  Serial.println(); 
   
  Serial.print("   Method for measuring: (1=HIGH, 0=LOW):"); Serial.println(measureoutput);
  Serial.println();
}
The multiplier() function does exactly what was explained in the text above. Download the program as .ino here if you like, for better visibility. 
So now we have the pulse sequence in a very smooth and - what is most important - always the same way, the corresponding output line looks like this:

Multiplier: 16,8,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,1,1,3,1,3,1,1,1,1,1,1,1,1,1,1,1,3,1,1,1,1,1,3,1,3,1,3,1,3,1,72,16,4,1,0

So this is the "code" from a special button. The last values 1 and 0 can be deleted, they are not important and may not be checked. Let's now have a look at the output of the program that checks for pulses and compares them with one or more stored pulses:

Now checking for pulses
. . .

Pulse sequence received.
A button was recognized: number 1.

. . .

Pulse sequence received.
I am sorry, no stored button matches

. .

Pulse sequence received.
I am sorry, no stored button matches

. .

Pulse sequence received.
A button was recognized: number 1.

. . . . . . . . . . . . .
.
The program leading to this output:

// THIS CODE WAS IMPLEMENTED BY PATRICK BAYER, patriq@web.de IN THE YEAR 2014. DO NOT CLAIM THIS CODE FOR YOUR OWN IN ANY WAY AND QUOTE THE SOURCE WHEN YOU USE IT.

#define valuepairs 100     // amount of rows in the matrix; int is not possible because the matrix definition below doesn't allow it.
boolean realpulse;         // with this we decide if we've got a real pulse in the loop section, or just zero signal (it may not be a correct pulse);
byte irpin=2;              // IR Sensor position on Arduino
long matrix[valuepairs][2];         // definition of the raw data matrix -> y-rows and 2 x-culumns
int ende;                  // variable integer for ending, cause matrix is normally not filled totally
byte changemethods=1;      // starting mode; in the loop sequence always HIGH,LOW,HIGH,LOW,... measure
int blocklength=556;       // code block length, differs with different remote control
int imprecisionrange=150;  // the range we look up- and downwards the defined blocklength

byte remotecode1[][2]={16,8,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,3,1,3,1,3,1,3,1,72,16,4,1,0};
byte pulsestocheck=35;      // amount of pulse-pairs (ON+OFF counts as one pulse here) to be checked;
                          

               
//----------- The program begins here

void setup() {   Serial.begin(9600); delay(1000); Serial.println("Now checking for pulses"); }

void loop()                            
{
  savePulseSequence();
  Serial.print(". ");
  if(realpulse==true)                                                               {
    Serial.println();Serial.println();
    Serial.println("Pulse sequence received.");
    if(checkformatch()>0)                                   {
      Serial.print("A button was recognized: number ");
      Serial.print(checkformatch());Serial.println(".");
      Serial.println();                                     }
    else{Serial.println("I am sorry, no stored button matches");Serial.println();}  }
}


//----------- We define the smoothening of our output values by assigning a multiplier in respect to the defined blocklength
byte multiplier (long rawblocklength){byte ausgabe;{for(int j=1;j<100;j++){if(rawblocklength*10/blocklength<=j*10+2 && rawblocklength*10/blocklength>=j*10-2){ausgabe=j;}}}return(ausgabe); }

//----------- We define the rounding of our output values; change rd=10 to f.ex. 100 to round on full hundreds and so on
long roundvalues(long rundwert){byte rd=10; if(rundwert-rundwert/rd*rd>(rd/2)){return(rundwert/rd*rd+rd);}else{return(rundwert/rd*rd);}}                   

//----------- We define the measuring and saving of our pulse sequence
void savePulseSequence()
{
  ende=valuepairs;  // for resetting
  realpulse=false;  // for resetting
  for(int i=0;i<valuepairs;i++)                      {
      matrix[i][0]=pulseIn(irpin, LOW);
      matrix[i][1]=micros();
      if(matrix[i][0]==0){ende=i; i=valuepairs;}     }
  if(ende>0)                                                         {
    realpulse=true;
    for(int i=0;i<ende;i++)                                    {
      matrix[i][1]=matrix[i+1][1]-matrix[i][1]-matrix[i+1][0]; }     }
}                                                         

//----------- This is the function needed for the output of the measured signal pulses
void serialOutput()
{
  Serial.println("-------- New record: --------");Serial.println(); Serial.println("| ON | OFF |          (whereby OFF was calculated with the micros() command)");Serial.println();
 
   
  for(int i=0;i<ende;i++)                    {     // Output of the raw pulse sequence
      Serial.print(matrix[i][0]);
      Serial.print(",");
      Serial.print(matrix[i+1][1]-matrix[i][1]-matrix[i+1][0]);              
      Serial.print(",");
      Serial.println();                      }
  Serial.print("   Listing has ended, amount of pairs: ");Serial.println(ende);
   
  Serial.print("   CSV: ");                        // Output values as comma separated values
    for(int i=0;i<ende;i++)                   {
      Serial.print(matrix[i][0]);
      Serial.print(",");
      Serial.print(matrix[i+1][1]-matrix[i][1]-matrix[i+1][0]);              
      Serial.print(",");                      }
  Serial.println(); 
 
  Serial.print("   CSV smooth: ");                 //  Now we output the smooth values
    for(int i=0;i<ende;i++)                   {
      Serial.print(roundvalues(matrix[i][0]));
      Serial.print(",");
      Serial.print(roundvalues(matrix[i+1][1]-matrix[i][1]-matrix[i+1][0]));              
      Serial.print(",");                      }
  Serial.println();
 
  Serial.print("   Multiplier: ");                 // Output the calculated multiplier, with respect to the defined blocklength
    for(int i=0;i<ende;i++)                   {
      Serial.print(multiplier(matrix[i][0]));
      Serial.print(",");
      Serial.print(multiplier(matrix[i+1][1]-matrix[i][1]-matrix[i+1][0]));              
      Serial.print(",");                      }
  Serial.println(); 
   
}

byte checkformatch()
{
  byte ausgabe=0;
  for(int i=0;i<pulsestocheck;i++)      {                  
    if(multiplier(matrix[i][0])!=remotecode1[i][0] || multiplier(matrix[i][1])!=remotecode1[i][1]){i=pulsestocheck;}
    if(i==pulsestocheck-1){ausgabe=1;}  }
  return(ausgabe);
}
   
A new command is included in this version: checkformatch(); It will return a value above zero, when we match the pulse sequence stored at the beginning, in the matrix pulsestocheck. Download this program here.
Now we've got a clear output value (1 in this case) we can work with, being able to control the outputs of our Arduino and thousand other things ...

Kommentare

Beliebte Posts aus diesem Blog

Die Hinterradachse beim Fahrrad aus- und einbauen - Reparatur z.B. nach einem Achsbruch

Erfahrungen mit Springlane.de - einmal und sicher nie wieder!