Re-creating a Lost IR Remote

Re-creating a Lost IR Remote

Most of our everyday household appliances such as fans, air conditioners and TVs use IR remote for easy operation. However, the remote for my wall-mounted fan had gone lost years ago and having to manually press the buttons on the fan is not so convenient.

 

In this post, I will be describing and documenting how I made an IR remote for my electric fan using the output of an IR blaster of a smartphone. This will also work if  you are duplicating a IR remote.

 

Required Materials (for final product):

  1. Arduino Pro Mini 8Mhz 3.3V (Atmega 328p)
  2. 940nm 5mm IR LED
  3. Perforated Board
  4. Rechargeable Lithium Battery
  5. TP4056 Charger Board (with battery protection IC)
  6. Tactile Buttons
  7. 2N2222 or any other similar NPN Transistor
  8. 1kΩ resistor

 

Additional Materials Required:

  1. Arduino Uno/Nano (for prototyping/testing)
  2. Android Smartphone with integrated IR blaster
  3. 38 KHz IR receiver (e.g. TSOP382380 or TSOP38438)
  4. Breadboard with hookup/jumper wires

 

Step 1: Finding the Right App

Now is the time to search Google Play for apps that can support sending IR commands through the smartphone’s IR blaster. Note that not all smartphones have an IR blaster so if you’re unsure, check with the manufacturer.

Some manufacturers include pre-installed app that does the IR commands, so check in that app whether it supports your appliance. For me, I found “Anymote Universal Remote + WiFi Smart Home Control” worked for controlling my fan.

I won’t show the exact steps of how to configure the app as every app is different. Remember to also test all the functions in the app to make sure all of it works properly.

 

Step 2: Capturing the IR Signal from your Smartphone

I used z3t0’s Arduino IR-Remote library to handle the IR commands.

I then launched Arduino IDE and opened the example sketch named “IRrecvDumpV2”.

KDK Remote(IR Receiver)_bb.png

I connected the circuit as above. Note that the pinout for every IR decoder is different, so check on Google for the pinout diagram. The output pin of the IR decoder is connected to digital pin 11 on the Arduino.

Before uploading the example sketch, I changed the baud rate of the sketch to 115200 bps. The default baud rate of  9600 is a little bit too slow for me,  especially when it is dumping the whole IR raw code to the serial monitor. 115200 is easily handled by the Arduino Uno, so it should be of no problem.

void  setup ( )
{
  Serial.begin(115200);   // Status message will be sent to PC at 115200 baud.(Previously 9600)
  irrecv.enableIRIn();  // Start the receiver
}

Once I have uploaded the sketch, open up the serial monitor and set the serial monitor to 115200. Now try pressing the button of an IR remote such as a TV remote. It should dump the output to the serial monitor. If it does, it means everything is working perfectly and you are now ready to capture the IR signal from your phone.

The serial monitor should show something like this,

RemoteDump.PNG

If the sketch outputs something like “IR code too long. Edit IRremoteInt.h and increase RAWBUF”, like mine did, there is an extra step to be done. The buffer length of the library might be too short of the IR raw code, well at least for my fan IR code. To change it, go to the Arduino IRRemote Library folder, and open up IRremoteInt.h. Go to line 43 and change the buffer to something slightly higher like 110. I recommend you not to change this number unless the example sketch specifically tell you to do so.

#define RAWBUF  101  // Maximum length of raw duration buffer

Changed RAWBUF to 110

#define RAWBUF  110  // Maximum length of raw duration buffer (Original = 101)

 

Copy both of the values in “unsigned int” and save it in another blank sketch. Repeat capturing the IR signal until you’ve captured all that you need.

Step 3: Testing the IR Code

We will need to test all of the IR code captured from the above example sketch. For my fan, its original controller had 4 buttons thus I had captured 4 IR codes. To test it, we will need to connect 4 buttons and a 940nm IR LED. I connected the circuit below,
KDK Remote(IR Sender)

Afterwards, I uploaded the sketch below. Basically what it does is to send out a specific IR code for each function if one of the buttons are pressed.


#include IRremote.h;
#include avr/power.h;

IRsend irsend;
byte powerpin = 7;
byte Speedpin = 6;
byte oscilpin = 5;
byte timerpin = 4;
//Power 7, Speed 6, Oscil 5

byte khz = 48; // 48kHz carrier frequency for the protocol

unsigned int power[51] = {3400,3400, 900,2550, 900,2550, 850,900, 850,2550, 850,2600, 850,2550, 900,800, 900,850, 850,850, 850,900, 850,850, 850,2600, 850,850, 850,850, 850,2600, 850,850, 850,850, 900,850, 850,2600, 850,2600, 850,2600, 800,2600, 850,2600, 850,850, 850}; // SANYO DC123E
unsigned int oscil[51] = {3450,3450, 850,2600, 900,2550, 900,850, 850,2550, 900,2600, 850,2600, 850,2600, 850,850, 900,2550, 900,2600, 900,2550, 850,900, 850,850, 900,850, 850,2600, 900,850, 850,850, 900,850, 900,800, 900,2600, 900,800, 900,850, 850,850, 900,2550, 900}; // SANYO DEE211
unsigned int Speed[51] = {3450,3400, 900,2550, 900,2500, 900,850, 850,2550, 900,2550, 900,2550, 850,2550, 850,2600, 900,2500, 900,2550, 850,2600, 900,800, 900,800, 900,850, 900,2550, 850,850, 850,850, 900,800, 900,850, 850,850, 900,850, 900,800, 900,850, 850,2550, 850}; // SANYO DFE201

unsigned int delimiter[51] = {3400,3400, 900,2550, 900,2550, 900,800, 900,2550, 900,2550, 900,2550, 850,850, 900,2550, 900,2550, 850,2550, 900,2550, 900,800, 900,800, 950,800, 900,2550, 850,850, 950,800, 900,800, 900,2550, 900,800, 900,800, 900,850, 900,800, 900,2550, 900}; // SANYO FFFFFFFF
//delimiter is also used for Timer code

void setup()
{
 pinMode(powerpin,INPUT_PULLUP);
 pinMode(Speedpin,INPUT_PULLUP);
 pinMode(oscilpin,INPUT_PULLUP);
 pinMode(timerpin,INPUT_PULLUP);
 //Serial.begin(9600);
}

void loop() {

if(digitalRead(powerpin) == LOW){
 Serial.println("Sending Power");
 irsend.sendRaw(power, sizeof(power) / sizeof(power[0]), khz); //Note the approach used to automatically calculate the size of the array.
 irsend.sendRaw(delimiter, sizeof(delimiter) / sizeof(delimiter[0]), khz); //Note the approach used to automatically calculate the size of the array.
 delay(50);
 }

 if(digitalRead(oscilpin) == LOW){
 Serial.println("Sending Oscil");
 irsend.sendRaw(oscil, sizeof(oscil) / sizeof(oscil[0]), khz);
 irsend.sendRaw(delimiter, sizeof(delimiter) / sizeof(delimiter[0]), khz);
 delay(50);
 }

if(digitalRead(Speedpin) == LOW){
 Serial.println("Sending Speed");
 irsend.sendRaw(Speed, sizeof(Speed) / sizeof(Speed[0]), khz);.
 irsend.sendRaw(delimiter, sizeof(delimiter) / sizeof(delimiter[0]), khz);
 delay(50);
 }

if(digitalRead(timerpin) == LOW){
 Serial.println("Sending Timer");
 irsend.sendRaw(delimiter, sizeof(delimiter) / sizeof(delimiter[0]), khz);
 delay(50);
 }

 delay(10);
}

Once the sketch has been uploaded, I used my smartphone camera to see whether the arduino is turning on the IR LED through the 2N2222 transistor. After that, I tried pointing the LED at the fan and it worked! With the IR code verified, I can move on to making the arduino code more power efficient.

 

Step 4: Adapting Code for Power Saving and Interrupts

I read up on Nick Gammon’s  guide on Arduino Power Saving, since I am intending to run it on a rechargeable Lithium Polymer Battery. The programming and code adapting part probably took the longest, as this was the first time that I used Arduino’s Sleep Modes and adding interrupts into  it. My plan is to make the Arduino Pro Mini sleep  indefinitely until a specific button is pressed then it will wake up and send out the IR code. There was a lot of times that Arduino simply refused to wake up after it went into sleep mode as I had set up the interrupts wrongly.

There are only 2 external hardware interrupt on the Arduino, and they are on Digital Pin 2 & 3. Digital Pin 3 is used up by the IRremote Library as it uses the Timer 2. I have not found a way for the library use another timer, as it requires changing the whole library. Thus I utilized the Pin Change Interrupts (with a library) to get extra 3 interrupt pins.

Initially, my plan was to use a dedicated button to wake the Arduino up, meaning that there will be 5 buttons. There will then be a timeout where if there is no button pressed within that period, it will go to sleep. However, I thought that that would consume more battery life as the Arduino is running at full speed during that period. Plus it would be a hassle having to keep pressing a wake up button if the remote has gone to sleep.

My second idea was to use one of the existing buttons to wake the Arduino up, for example the power button. Once the power button is pressed, it’ll wake up the CPU and in order to prevent the arduino sending out the power IR code while  the button is pressed (when waking up the arduino), there will be a delay before it actually registers the button press as to send out the IR code.  However, I found it to be too slow and therefore abandoned  this idea.

All of the Pin Change Interrupts go back to sleep mode automatically once it has done sending out the IR code, no matter if the button is still pressed or not. Only the hardware pin interrupt (Pin 2) does not go to sleep once it is done sending out the code. I found out that if the Arduino went to sleep with that pin on ‘LOW’, it will not wake up. It is the limitation that the ATMega328p can only wake up on a logic ‘LOW’ signal, rather than a edge triggered ‘FALLING’ signal of the other buttons. It only goes to sleep when the button is released. I had to add in a ‘While Loop’ to make sure that the CPU only sleeps when the button goes back to  logic HIGH. This is to prevent the CPU from locking up as I have found out when pressing the button rapidly.


case powerpin:

irsend.sendRaw(power, sizeof(power) / sizeof(power[0]), khz);
irsend.sendRaw(delimiter, sizeof(delimiter) / sizeof(delimiter[0]), khz);
while(digitalRead(2) == LOW)
{delay(10);}

break;

I would say that the final Arduino code is quite easy and simple to understand. I also added in code that disables the unneeded peripherals like ADC, SPI and TWI/I2C as it not needed at all. To save more power, I also enabled the internal pull ups to prevent any stray voltage leakage.  I know that  there are actually more ways to save power, but I have not applied them as I didn’t have  much time to experiment yet. Maybe I will do it in the future. To see the full code, expand the code below.


/*
* IR Remote for KDK Fan
* https://hassanulmakers.wordpress.com/?p=1277
* Coded by hassanul
*/

#include
#include
#include
#include "Arduino.h"
#include 

IRsend irsend;
const byte powerpin = 2;
const byte speedpin = 7; //PCINT2
const byte oscilpin = 8; //PCINT0
const byte timerpin = A0; //PCINT1

volatile byte WhichInterruptPin = 0;

byte khz = 48; // 48kHz carrier frequency for the protocol

const unsigned int power[51] = {3400,3400, 900,2550, 900,2550, 850,900, 850,2550, 850,2600, 850,2550, 900,800, 900,850, 850,850, 850,900, 850,850, 850,2600, 850,850, 850,850, 850,2600, 850,850, 850,850, 900,850, 850,2600, 850,2600, 850,2600, 800,2600, 850,2600, 850,850, 850}; // SANYO DC123E
const unsigned int oscil[51] = {3450,3450, 850,2600, 900,2550, 900,850, 850,2550, 900,2600, 850,2600, 850,2600, 850,850, 900,2550, 900,2600, 900,2550, 850,900, 850,850, 900,850, 850,2600, 900,850, 850,850, 900,850, 900,800, 900,2600, 900,800, 900,850, 850,850, 900,2550, 900}; // SANYO DEE211
const unsigned int Speed[51] = {3450,3400, 900,2550, 900,2500, 900,850, 850,2550, 900,2550, 900,2550, 850,2550, 850,2600, 900,2500, 900,2550, 850,2600, 900,800, 900,800, 900,850, 900,2550, 850,850, 850,850, 900,800, 900,850, 850,850, 900,850, 900,800, 900,850, 850,2550, 850}; // SANYO DFE201
const unsigned int delimiter[51] = {3400,3400, 900,2550, 900,2550, 900,800, 900,2550, 900,2550, 900,2550, 850,850, 900,2550, 900,2550, 850,2550, 900,2550, 900,800, 900,800, 950,800, 900,2550, 850,850, 950,800, 900,800, 900,2550, 900,800, 900,800, 900,850, 900,800, 900,2550, 900}; // SANYO FFFFFFFF
//delimiter is also used for Timer code

void setup()
{
power_adc_disable();
power_spi_disable();
power_twi_disable();
DDRD &= B00001011; // set Arduino pins 2 to 7 as inputs, leaves 0 & 1 (RX & TX) as is
DDRB = B00000000; // set pins 8 to 13 as inputs
PORTD |= B11110101; // enable pullups on pins 2 to 7
PORTB |= B11111111; // enable pullups on pins 8 to 13
pinMode(13,OUTPUT);
pinMode(2,INPUT_PULLUP);
pinMode(7,INPUT_PULLUP);
pinMode(8,INPUT_PULLUP);
pinMode(A0,INPUT_PULLUP);
}
void Power(){
detachInterrupt(digitalPinToInterrupt(2));
digitalWrite(13,HIGH);
WhichInterruptPin = powerpin;
}

void SpeedInt (void)
{
PCintPort::detachInterrupt(speedpin);
digitalWrite(13,HIGH);
WhichInterruptPin = speedpin;
}

void OscilInt (void)
{
PCintPort::detachInterrupt(oscilpin);
digitalWrite(13,HIGH);
WhichInterruptPin = oscilpin;
}

void TimerInt (void)
{
PCintPort::detachInterrupt(timerpin);
digitalWrite(13,HIGH);
WhichInterruptPin = timerpin;
}
void enterSleep(void)
{
// SLEEP_MODE_IDLE - the lowest power saving mode
// SLEEP_MODE_ADC
// SLEEP_MODE_PWR_SAVE
// SLEEP_MODE_STANDBY
// SLEEP_MODE_PWR_DOWN - the highest power saving mode
WhichInterruptPin = 0;
attachInterrupt(digitalPinToInterrupt(2), Power, LOW); //Interrupt 0 is pin 2 on Arduino
PCintPort::attachInterrupt(7,SpeedInt,FALLING);
PCintPort::attachInterrupt(8,OscilInt,FALLING);
PCintPort::attachInterrupt(A0,TimerInt,FALLING);

set_sleep_mode(SLEEP_MODE_PWR_DOWN);

sleep_enable();

digitalWrite(13,LOW);
sleep_mode(); //Enter Sleep

sleep_disable(); //Processor will continue here after waking up

}

void loop() {

switch (WhichInterruptPin) {
case powerpin:
irsend.sendRaw(power, sizeof(power) / sizeof(power[0]), khz);
irsend.sendRaw(delimiter, sizeof(delimiter) / sizeof(delimiter[0]), khz);
while(digitalRead(2) == LOW)
{delay(10);}
break;

case speedpin:
irsend.sendRaw(Speed, sizeof(Speed) / sizeof(Speed[0]), khz);
irsend.sendRaw(delimiter, sizeof(delimiter) / sizeof(delimiter[0]), khz);
//delay(100);
break;

case oscilpin:
irsend.sendRaw(oscil, sizeof(oscil) / sizeof(oscil[0]), khz);
irsend.sendRaw(delimiter, sizeof(delimiter) / sizeof(delimiter[0]), khz);
//delay(100);
break;

case timerpin:
irsend.sendRaw(delimiter, sizeof(delimiter) / sizeof(delimiter[0]), khz);
//delay(100);
break;

default:
//No action needed here
break;
}

enterSleep(); //Sleep processor
} //End of loop

Once I have verified that the code works and there are no bugs, I uploaded the program sketch to my 3.3V 8MHz Arduino Pro Mini using my USB to TTL Serial Adapter.

 

Step 5: Soldering The Final Circuit

I first began by soldering the pin headers for the Arduino Pro Mini. Afterwards, I tried to layout the components on a piece of uncut perf board. I also drew lines using a marker for cutting it later when I have finished soldering.

                          

I used a  mixture of the rainbow ribbon cable wires and hookup wires. The ribbon cables are great for connecting signal wires as they are flexible and have a small gauge so that it won’t clutter up the circuit board, while I  like to use hookup  wire for making small and short connections, e.g connecting ground connections for the 4 tactile buttons. However one thing I hated about my rainbow ribbon cable is that the insulation tends to melt and contract back easily with just me tinning the wire with my Antex 25W soldering iron.

For the TP4056 LiPo Charger Board, I changed the Rprog (Charge rate) from the default 1000mA to something around 270 mA by changing the 1.2kΩ resistor to a 4.7kΩ resistor. I am using a 600 mAh battery, so I am charging the battery at about 0.5C and it should take a little more than 2 hours to charge from empty.

The resistor R3 circled above is the RProg resistor. I didn’t have any SMD resistors, so I used the through hole 1/4 W resistor and soldered it on the pads.

I also removed the power led on the arduino to save power. I used my fine tip for my soldering iron and heated for quite some time but I still could not get the LED to desolder. (Probably due to the LED connected to the ground plane? Or due to my tip becoming rusty in some parts?). In the end, I decided to remove the 1KΩ (102) resistor connected to the LED

 

After I had completed soldering the parts, I then cut off the extra space of the perf board using my dremel. Afterwards I added wires to the TP4056 charger board and the LiPo battery and used a jumper link as a switch so that I can measure the current consumption without desoldering the wires.

I added a layer of plastic sheet and paper masking tape and hot glued it onto the back side of the board so that the TP4056 and LiPo battery can be mounted at the back without the risk of being shorted out. I then taped the battery to the board.

Step 7: Testing

With that all of the fabrication done, I tested the remote and it worked!

Video will be up soon!

 

8 thoughts on “Re-creating a Lost IR Remote

    1. Hello Hassan, so shall we catch up over the next several weeks maybe?.. Find me on facebook.com/arsenyspb let’s have a coffee and talk?

      Like

  1. Hey Hassan, do you still have an IR receiver set-up to analyse the fan working? I live in Simei and try to convince american startup ‘Bond’ to add support of KDK fans (read: do pull request with protocol details). It would be great if we collaborate to analyse the IR protocol of the remote I have and then we commit it to them! PM me maybe?..

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s