Using Arduino to control IR Remote consumer electronics

Thread Starter

Josh Kaufman

Joined Aug 24, 2016
31
Hello Gang!

I am trying to clone the IR signal of consumer electronics to give IR control to my Arduino.
(I wish to give the Arduino the ability to turn on and off a TV)
This seems simple enough, and there are several tutorials online, so, easy, right?

I've found several examples of how to copy the IR signal into the Serial Monitor my problem is re-transmitting them with my IR Led from my arduino.

Demo Hardware:


My breadboard example:
I'm using an detector to collect the IR signals, and a 940nm IR LED to transmit.



Most of the tutorials I find on the internet are based on outdated IR Libraries.
(I don't mind this, as long as I can get them to work)

After failing to transmit the captured codes from my IR Led I tired the examples from the new IRlib2 library.

I've tried the "rawRecv" example sketch and successfully captured the timing data.
Code:
/* rawR&cv.ino Example sketch for IRLib2
*  Illustrate how to capture raw timing values for an unknow protocol.
*  You will capture a signal using this sketch. It will output data the
*  serial monitor that you can cut and paste into the "rawSend.ino"
*  sketch.
*/
// Recommend only use IRLibRecvPCI or IRLibRecvLoop for best results
#include <IRLibRecvPCI.h>

IRrecvPCI myReceiver(2);//pin number for the receiver

void setup() {
  Serial.begin(9600);
  delay(2000); while (!Serial); //delay for Leonardo
  myReceiver.enableIRIn(); // Start the receiver
  Serial.println(F("Ready to receive IR signals"));
}

void loop() {
  //Continue looping until you get a complete signal received
  if (myReceiver.getResults()) {
    Serial.println(F("Do a cut-and-paste of the following lines into the "));
    Serial.println(F("designated location in rawSend.ino"));
    Serial.print(F("\n#define RAW_DATA_LEN "));
    Serial.println(recvGlobal.recvLength,DEC);
    Serial.print(F("uint16_t rawData[RAW_DATA_LEN]={\n\t"));
    for(bufIndex_t i=1;i<recvGlobal.recvLength;i++) {
      Serial.print(recvGlobal.recvBuffer[i],DEC);
      Serial.print(F(", "));
      if( (i % 8)==0) Serial.print(F("\n\t"));
    }
    Serial.println(F("1000};"));//Add arbitrary trailing space
    myReceiver.enableIRIn();      //Restart receiver
I get this code from my samsung remote:


Code:
Do a cut-and-paste of the following lines into the
designated location in rawSend.ino

#define RAW_DATA_LEN 68
uint16_t rawData[RAW_DATA_LEN]={
    4530, 4514, 562, 1686, 562, 1682, 566, 1682,
    562, 590, 534, 590, 534, 590, 534, 590,
    534, 590, 534, 1686, 558, 1686, 562, 1686,
    562, 590, 534, 590, 534, 590, 530, 594,
    530, 594, 530, 590, 534, 1686, 562, 590,
    534, 590, 534, 590, 534, 590, 534, 594,
    526, 594, 530, 1686, 562, 590, 534, 1690,
    558, 1682, 562, 1686, 562, 1686, 562, 1686,
    562, 1682, 562, 1000};
Here is the 'rawSend' example sketch:
Code:
/* rawSend.ino Example sketch for IRLib2
 *  Illustrates how to send a code Using raw timings which were captured
 *  from the "rawRecv.ino" sample sketch.  Load that sketch and
 *  capture the values. They will print in the serial monitor. Then you
 *  cut and paste that output into the appropriate section below.
 */
#include <IRLibSendBase.h>    //We need the base code
#include <IRLib_HashRaw.h>    //Only use raw sender

IRsendRaw mySender;

void setup() {
  Serial.begin(9600);
  delay(2000); while (!Serial); //delay for Leonardo
  Serial.println(F("Every time you press a key is a serial monitor we will send."));
}
/* Cut and paste the output from "rawRecv.ino" below here. It will 
 * consist of a #define RAW_DATA_LEN statement and an array definition
 * beginning with "uint16_t rawData[RAW_DATA_LEN]= {…" and concludes
 * with "…,1000};"
 */





/*
 * Cut-and-paste into the area above.
 */
   
void loop() {
  if (Serial.read() != -1) {
    //send a code every time a character is received from the 
    // serial port. You could modify this sketch to send when you
    // push a button connected to an digital input pin.
    mySender.send(rawData,RAW_DATA_LEN,36);//Pass the buffer,length, optionally frequency
    Serial.println(F("Sent signal."));
  }
}
Hardware


I'm using a 940nm IR LED driven by a 2222a BJT.
I used the diagram from a tutorial that used a 4.6K resistor on the base.
(should I have a current limiter for the IR LED?)

Here is my question:


After I paste the timing code into "rawSend" How do I code the LED to turn on and off with the appropriate timing?

I've tried many tutorials I found online, but perhaps because they use a different library, I'm not having success.

I've tried examples like these:


https://learn.adafruit.com/using-an-infrared-library/hardware-needed

http://www.instructables.com/id/Arduino-Infrared-Remote-tutorial/

Using IR commands to control consumer electronics is something I'd like to use a lot once I figure it out.
Any tips would be greatly appreciated!

Thanks!
 

Thread Starter

Josh Kaufman

Joined Aug 24, 2016
31
Thank you for the tip shteii,

The first example shown there admits that it does not work for 2 of 3 remotes.
Moving on, I see that much of this work is based on the work of Ken Shirriff and his library.
I've passed on this before in favor of the newer IRLib2, however, since I'm not finding tutorials on this new library, I decided to go ahead and install the "Arduino-IRremote-master library"

I am not trying out the capture example, and immediately the IDE encounters an error in compiling "IRrercevDump".
Undeterred, I try the seemingly updated "IRrecevDumpV2" = same error:
'class decode_results' has no member named 'address'



Any advice on how to get arround this?
 

Thread Starter

Josh Kaufman

Joined Aug 24, 2016
31
Remind me which ATmel chip you are using?
I'm using the Nano. ( I believe it's 5v @16Mhz based on the resonator and regulator)


Here are the error messages:

IrrecevDump:

Code:
Arduino: 1.8.3 (Windows Store 1.8.6.0) (Windows 10), Board: "Arduino Nano, ATmega328"

C:\Users\Josh\Dropbox\INFO\ELECTRONICS\Electronics Programs\Arduino\libraries\Arduino-IRremote-master\examples\IRrecvDump\IRrecvDump.ino: In function 'void dump(decode_results*)':

IRrecvDump:53: error: 'class decode_results' has no member named 'address'

     Serial.print(results->address, HEX);

                           ^

IRrecvDump:56: error: 'LG' was not declared in this scope

   else if (results->decode_type == LG) {

                                    ^

IRrecvDump:62: error: 'AIWA_RC_T501' was not declared in this scope

   else if (results->decode_type == AIWA_RC_T501) {

                                    ^

IRrecvDump:65: error: 'WHYNTER' was not declared in this scope

   else if (results->decode_type == WHYNTER) {

                                    ^

Multiple libraries were found for "IRremote.h"
Used: C:\Program Files\WindowsApps\ArduinoLLC.ArduinoIDE_1.8.6.0_x64__mdqgnx93n4wtt\libraries\RobotIRremote
Not used: C:\Users\Josh\Dropbox\INFO\ELECTRONICS\Electronics Programs\Arduino\libraries\Arduino-IRremote-master
exit status 1
'class decode_results' has no member named 'address'

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.
IRrecevDumpV2 Error Messages:

Code:
Arduino: 1.8.3 (Windows Store 1.8.6.0) (Windows 10), Board: "Arduino Nano, ATmega328"

C:\Users\Josh\Dropbox\INFO\ELECTRONICS\Electronics Programs\Arduino\libraries\Arduino-IRremote-master\examples\IRrecvDumpV2\IRrecvDumpV2.ino: In function 'void ircode(decode_results*)':

IRrecvDumpV2:28: error: 'class decode_results' has no member named 'address'

     Serial.print(results->address, HEX);

                           ^

C:\Users\Josh\Dropbox\INFO\ELECTRONICS\Electronics Programs\Arduino\libraries\Arduino-IRremote-master\examples\IRrecvDumpV2\IRrecvDumpV2.ino: In function 'void encoding(decode_results*)':

IRrecvDumpV2:53: error: 'SAMSUNG' was not declared in this scope

     case SAMSUNG:      Serial.print("SAMSUNG");       break ;

          ^

IRrecvDumpV2:54: error: 'LG' was not declared in this scope

     case LG:           Serial.print("LG");            break ;

          ^

IRrecvDumpV2:55: error: 'WHYNTER' was not declared in this scope

     case WHYNTER:      Serial.print("WHYNTER");       break ;

          ^

IRrecvDumpV2:56: error: 'AIWA_RC_T501' was not declared in this scope

     case AIWA_RC_T501: Serial.print("AIWA_RC_T501");  break ;

          ^

IRrecvDumpV2:58: error: 'DENON' was not declared in this scope

     case DENON:        Serial.print("Denon");         break ;

          ^

C:\Users\Josh\Dropbox\INFO\ELECTRONICS\Electronics Programs\Arduino\libraries\Arduino-IRremote-master\examples\IRrecvDumpV2\IRrecvDumpV2.ino: In function 'void dumpInfo(decode_results*)':

IRrecvDumpV2:68: error: 'class decode_results' has no member named 'overflow'

   if (results->overflow) {

                ^

C:\Users\Josh\Dropbox\INFO\ELECTRONICS\Electronics Programs\Arduino\libraries\Arduino-IRremote-master\examples\IRrecvDumpV2\IRrecvDumpV2.ino: In function 'void dumpCode(decode_results*)':

IRrecvDumpV2:152: error: 'class decode_results' has no member named 'address'

       Serial.print(results->address, HEX);

                             ^

Multiple libraries were found for "IRremote.h"
Used: C:\Program Files\WindowsApps\ArduinoLLC.ArduinoIDE_1.8.6.0_x64__mdqgnx93n4wtt\libraries\RobotIRremote
Not used: C:\Users\Josh\Dropbox\INFO\ELECTRONICS\Electronics Programs\Arduino\libraries\Arduino-IRremote-master
exit status 1
'class decode_results' has no member named 'address'

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.
 

Thread Starter

Josh Kaufman

Joined Aug 24, 2016
31
It is the one on the other side.
The resonator says: "A B" I read that the prefix "A.x" means 16Mhz.
Also, I can load other sketches onto this board with no problem.

In the mean time I've been working on the new library files (In my first post)

I have been successful in capturing consistent codes from the IRlib2 examples.
First I used the "rawRecv" in the first post, I coppied the timing and script over to the "rawSend"

The result is:

Code:
 rawSend.ino Example sketch for IRLib2
*  Illustrates how to send a code Using raw timings which were captured
*  from the "rawRecv.ino" sample sketch.  Load that sketch and
*  capture the values. They will print in the serial monitor. Then you
*  cut and paste that output into the appropriate section below.
*/
#include <IRLibSendBase.h>    //We need the base code
#include <IRLib_HashRaw.h>    //Only use raw sender

IRsendRaw mySender;

void setup() {
  Serial.begin(9600);
  delay(2000); while (!Serial); //delay for Leonardo
  Serial.println(F("Every time you press a key is a serial monitor we will send."));
}
/* Cut and paste the output from "rawRecv.ino" below here. It will
* consist of a #define RAW_DATA_LEN statement and an array definition
* beginning with "uint16_t rawData[RAW_DATA_LEN]= {…" and concludes
* with "…,1000};"
*/

#define RAW_DATA_LEN 26
uint16_t rawData[RAW_DATA_LEN]={
  2430, 602, 1162, 638, 562, 634, 1162, 638,
  538, 658, 1166, 634, 538, 662, 562, 634,
  1166, 634, 538, 662, 538, 658, 538, 662,
  538, 1000};


/*
* Cut-and-paste into the area above.
*/
  
void loop() {
  if (Serial.read() != -1) {
    //send a code every time a character is received from the
    // serial port. You could modify this sketch to send when you
    // push a button connected to an digital input pin.
    mySender.send(rawData,RAW_DATA_LEN,36);//Pass the buffer,length, optionally frequency
    Serial.println(F("Sent signal."));
  }
}

How hard would it be to translate this code into the IR LED output?
It seems I'm 90% there, and all of the hard parts are finished.
The comments in the code even say:

Quote
You could modify this sketch to send when you
// push a button connected to an digital input pin.

My problem is that I don't understand WHERE this code is "sending" the signal to.

Can anyone tell me how to "send" to the LED with this code?
(and trigger it with a button)
Or Am I wrong about this being possible?

Do you have to be an expert at the library to make it work?
 

shteii01

Joined Feb 19, 2010
4,644
You need IR led to transmit.
You need IR detector to receive.

In your first post you show some kind of IR device on the breadboard. I assume it is IR detector.
Do you have IR led for transmitting?
 

jjw

Joined Dec 24, 2013
551
IR remotes modulate a 38kHz ( may be +- 2kHz ) carrier on and off.
The raw values you got are the on/off times in usecs.
You could set the pwm output to drive the IR Led ( with transistor switch ) at 38kHz and set it on/off in software.
 

Thread Starter

Josh Kaufman

Joined Aug 24, 2016
31
You need IR led to transmit.
You need IR detector to receive.

In your first post you show some kind of IR device on the breadboard. I assume it is IR detector.
Do you have IR led for transmitting?
The led driven by the BJT is IR.
It peaks at frequency of 38KHz
https://www.amazon.com/gp/product/B00EFOQEUM/ref=oh_aui_detailpage_o00_s00?ie=UTF8&psc=1

IR remotes modulate a 38kHz ( may be +- 2kHz ) carrier on and off.
The raw values you got are the on/off times in usecs.
You could set the pwm output to drive the IR Led ( with transistor switch ) at 38kHz and set it on/off in software.
Post #3 in video below had the same idea! (see video below)
http://www.eevblog.com/forum/blog/eevblog-506-ir-remote-control-arduino-protocol-tutorial/

I'm sure that the library covers all of that, but since I can't get it working, the next logical step is to program what I know.

These libraries are made to do all of this with minimal code, but this is just one way to "skin the cat"
Another way is to decode the bits manually, then re-transmit
(I can have access to an oscilloscope tomorrow, hopefully I can line up all of my O-Scope work while I'm there.)

Backwards Engineering video:
This is the process Dave from the EEVBlog did for his remote, using his scope (and later a logic analyzer for finer measurement of the carrier)
his code is here:
(Note: Dave takes in consideration the delay time for the "digital write" command and also the "stop Bit" at the end.

Code:
//*****************************************
// NEC (Japanese) Infrared code sending library for the Arduino
// Send a standard NEC 4 byte protocol direct to an IR LED on the define pin
// Assumes an IR LED connected on I/O pin to ground, or equivalent driver.
// Tested on a Freetronics Eleven Uno compatible
// Written by David L. Jones www.eevblog.com
// Youtube video explaining this code: http://www.youtube.com/watch?v=BUvFGTxZBG8
// License: Creative Commons CC BY
//*****************************************

#define IRLEDpin  2              //the arduino pin connected to IR LED to ground. HIGH=LED ON
#define BITtime   562            //length of the carrier bit in microseconds
//put your own code here - 4 bytes (ADDR1 | ADDR2 | COMMAND1 | COMMAND2)
unsigned long IRcode=0b11000001110001111100000000111111;

// SOME CODES:
// Canon WL-D89 video remote START/STOP button = 0b11000001110001111100000000111111

void setup()
{
}

void IRsetup(void)
{
  pinMode(IRLEDpin, OUTPUT);
  digitalWrite(IRLEDpin, LOW);    //turn off IR LED to start
}

// Ouput the 38KHz carrier frequency for the required time in microseconds
// This is timing critial and just do-able on an Arduino using the standard I/O functions.
// If you are using interrupts, ensure they disabled for the duration.
void IRcarrier(unsigned int IRtimemicroseconds)
{
  for(int i=0; i < (IRtimemicroseconds / 26); i++)
    {
    digitalWrite(IRLEDpin, HIGH);   //turn on the IR LED
    //NOTE: digitalWrite takes about 3.5us to execute, so we need to factor that into the timing.
    delayMicroseconds(9);          //delay for 13us (9us + digitalWrite), half the carrier frequnecy
    digitalWrite(IRLEDpin, LOW);    //turn off the IR LED
    delayMicroseconds(9);          //delay for 13us (9us + digitalWrite), half the carrier frequnecy
    }
}

//Sends the IR code in 4 byte NEC format
void IRsendCode(unsigned long code)
{
  //send the leading pulse
  IRcarrier(9000);            //9ms of carrier
  delayMicroseconds(4500);    //4.5ms of silence

  //send the user defined 4 byte/32bit code
  for (int i=0; i<32; i++)            //send all 4 bytes or 32 bits
    {
    IRcarrier(BITtime);               //turn on the carrier for one bit time
    if (code & 0x80000000)            //get the current bit by masking all but the MSB
      delayMicroseconds(3 * BITtime); //a HIGH is 3 bit time periods
    else
      delayMicroseconds(BITtime);     //a LOW is only 1 bit time period
     code<<=1;                        //shift to the next bit for this byte
    }
  IRcarrier(BITtime);                 //send a single STOP bit.
}

void loop()                           //some demo main code
{
  IRsetup();                          //Only need to call this once to setup
  IRsendCode(IRcode);              
  delay(5000);
  IRsendCode(IRcode);
  while(1);
}
Here is an explanation of 2 data formats RC5 and NEC. (just the sort of thing I was trying to avoid getting involved in lol)
http://www.vishay.com/docs/80071/dataform.pdf

This post is from the "IRremote library" author (not the IRlib2 library mentioned in my original post)
"Using arbitrary remotes with the Arduino IRremote library" (protocol agnostic)
http://www.righto.com/2010/01/using-arbitrary-remotes-with-arduino.html

This is the general overview of the entire library:
http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html

This guy was able to use a backwards photodiode to reverse engineer with his O-scope!
http://www.zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/

Here is his code:

Code:
/* Control a Lutron Maestro light dimmer */
#define BIT_IS_SET(i, bits)  (1 << i & bits)

// LED connected to digital pin 13
const int LED_PIN = 13;
// Width of a pulse, in microseconds
const int PULSE_WIDTH = 2300;
// # of bytes per command
const int COMMAND_LENGTH = 4; 

const int UP[]     = {255, 136, 130, 34};
const int DOWN[]   = {255, 136, 130, 20};
const int ON[]     = {255, 136, 132, 184};
const int OFF[]    = {255, 136, 189, 18};
const int RECALL[] = {255, 136, 132, 183};

void setup()
{
  pinMode(LED_PIN, OUTPUT);
}

/* Modulate pin at 39 kHz for give number of microseconds */
void on(int pin, int time) {
  static const int period = 25;
  // found wait_time by measuring with oscilloscope
  static const int wait_time = 9;

  for (time = time/period; time > 0; time--) {
    digitalWrite(pin, HIGH);
    delayMicroseconds(wait_time);
    digitalWrite(pin, LOW);
    delayMicroseconds(wait_time);
  }
}

/* Leave pin off for time (given in microseconds) */
void off(int pin, int time) {
  digitalWrite(pin, LOW);
  delayMicroseconds(time);
}

/* Send a byte over the IR LED */
void send_byte(int bits) {
  for (int i = 7; i >= 0; i--)
  {
    if (BIT_IS_SET(i, bits)) {
      on(LED_PIN, PULSE_WIDTH);
    } else {
      off(LED_PIN, PULSE_WIDTH);
    }
  }
}

/* Send a full command */
void command(const int bytes[]) {
  for (int i = 0; i < COMMAND_LENGTH; i++) {
    send_byte(bytes[i]);
  }
  off(LED_PIN, 4 * PULSE_WIDTH);
}

void loop()
{
  command(UP);
  delay(1000);
  command(DOWN);
  delay(1000);
}
I am trying to build off of the techniques already available.
But I'm not quite successful in adapting these projects for my purposes.
Small (& large) traps keep getting me! lol
 
Top