Preface
Well I never really liked Infrared Remote Controls as the media to send data. The idea that the transmitter has to be aimed at the reciever really bothered me. I got this chinesse remote and I was supposed to build a decoder module for it. At first some experienced people in the field suggested me that it follows RC-5 prtocol. It's a very easy job... ..." Well as it turned out it isn't an easy job without an oscilloscope to store wave shapes in it's memory. Maybe this blog will help me remeber how stupid I was in May 2011 or it might help me or help others find some help on their endeavours to implement cheap infrared wireless communication modules. This particular chinesse remote costs arround BDT 30 that is less than $0.50.
I am orgranizing this post in accordance to the problems I faced during my struggle.
The IR Receiver I got didnt have any markings on it specifying its model number or anything. Though I knew that among the three pins one would be Vcc, one would be for Ground and one of the pins should be the Output. But Whats what was a mystery till I burned an IR receiver. My BAD!!! It released an awful smell!!
It burned because I guessed that it might have a pinout like TSOP1738. That is GND Vs Out(1,2,3). Well I was damm wrong. Just another guess gone wrong :P!!!
Then I was in great despair... How can I guess the pinouts without burning another IR receiver... Well!! google helped me!!! After kinda giving up to depair and some procrastination I finally thought let's try google. I searched on google image with "IR Receiver pinout" and wollaah-- turned out there are two most common pinouts for IR receivers-- the one I already tried (GND, VS, OUT) and failed. The other common pinout was (OUT, GND, Vs). This pinout actually worked for my unknown IR receiver.
If you work with electronics in Bangladesh or you are using scavenged parts for your project... you should know that most of the time you won't know the actual model number of the part. In this situation the best course of action can be... to look for the most common pinouts for that type of parts... google image does a good job to find the most common pinouts for this sort of sitations.
Now how do I know if the IR receiver is receiving some data? The burning question... Most common output for the IR recivers is logical high that is +5V. Im guessing that when you know the pinout you can always measure the ideal voltage of the IR reciever OUT pin. The one I used to output logical high in its ideal state. I connected a Red LED with a series resitance of arround 2K(It can vary, depends on how bright you want the LED to be) with the output. The following ckt will better describe my connections...
I started the project with the assumption that the remote was following RC-5 protocol. I naively implemented the RC-5 protocol. But when I tried to test it... I failed to get any data... it was disappointing. I was quite baffled because I did manage to test my RC-5 decoder on proteus 7. It worked fine with my RC-5 decoder there. It took me couple days to realize that the remote wasn't using RC-5 protocol. All this because I didnt have confidence in my own instincts and assumptions and relied on other peoples experience too much.
So my advice to you is-- don't always take someone else's advice for granted... they might be wrong... no matter how experienced they are... we are all human.
If you want to know details about the NEC IR protocol I suggest you follow this link http://www.sbprojects.com/knowledge/ir/nec.php
I planned on using the CCP(Capture/Compare/PWM) module of my PIC. Where I used the capture option for Falling Edge.
Okay I think I should put the whole firmware code here for you to better understand. I think I placed enough comments in between... the codes...
////////////////// Chinesse Version NEC Decoder /////////////
////////////////////////////////////////////////
/*
File : NEC_Decoder.c
Author : Md Saiful Islam
Compiler: CCS v4.84
IDE : MPLAB 4.6x
FOSC : 4MHz
*/
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
#include <18F2550.h>
#fuses XT, NOLVP, NOWDT, NOPUT, NODEBUG, NOPROTECT, NOBROWNOUT
#use delay(clock=4000000)
#use rs232 (BAUD=9600, UART1)
volatile unsigned char LastState=0, HalfBit=0, CurrentState=0, necBitNumber=0, RxCompleteFlag=0, ErrorFlag=0;
volatile unsigned int32 necCommand=0, necTempCommand;
volatile unsigned int32 LastTimeStamp=0, CurrentTimeStamp=0, DifferenceTimeStamp;
// used +/- 25% cushion for Ones and Zeros
#define MaxOne (2813)
#define MinOne (1688)
#define MaxZero (1400)
#define MinZero (840)
///////////////////////////////////////////
// Used 10% cushion for the 9ms burst and 4.5ms space--
// but for the rx end the whole thingz supposed to be inverted
#define AGCBurstLong (9900)
#define AGCBurstShort (8100)
#define StartBitLong (4950)
#define StartBitShort (4050)
//////////////////////////////////////////////////////////////
#define RepeatLong (2475)
#define RepeatShort (2025)
//Recommended procedure to change the Edge selection for PIC
void ToRE()
{
DISABLE_INTERRUPTS(INT_CCP1);
CLEAR_INTERRUPT(INT_CCP1);
SETUP_CCP1(CCP_OFF);
setup_ccp1(CCP_CAPTURE_RE);
ENABLE_INTERRUPTS(INT_CCP1);
}
void ToFE()
{
DISABLE_INTERRUPTS(INT_CCP1);
CLEAR_INTERRUPT(INT_CCP1);
SETUP_CCP1(CCP_OFF);
setup_ccp1(CCP_CAPTURE_FE);
ENABLE_INTERRUPTS(INT_CCP1);
}
#int_ccp1
void ccp1_isr()
{
CurrentTimeStamp = ccp_1;
CurrentState = input_state(PIN_C2);
// if the value crosses zero.
if((LastTimeStamp > CurrentTimeStamp) && (LastTimeStamp != 0))
{
CurrentTimeStamp += 65535;
}
DifferenceTimeStamp = CurrentTimeStamp - LastTimeStamp;
// There is going to be a 9ms AGCBurst as the IR Rx device will rx data in inverted form
// there is going to be a falling edge, the space is going to be arround 9ms
if(necBitNumber == 0)
{
if(HalfBit == 0)
{
HalfBit++;
ToRE();
}
else
{
if(DifferenceTimeStamp >= AGCBurstShort && DifferenceTimeStamp <= AGCBurstLong)
{
necBitNumber++;
HalfBit = 0;
ToFE();
}
else
{
HalfBit=0;
ToFE();
}
}
}
else if(necBitNumber == 1)
{
// here the 4.5ms space is implemented in inverted mode
if(DifferenceTimeStamp >= StartBitShort && DifferenceTimeStamp <= StartBitLong)
{
necTempCommand = (necTempCommand<<1);
ToFE();
}
/// If the repeat command is received then repeat the last command
else if(DifferenceTimeStamp >= RepeatShort && DifferenceTimeStamp <= RepeatLong)
{
RxCompleteFlag = 1;
necBitNumber = 0;
LastState=0;
CurrentState=0;
HalfBit=0;
necTempCommand = 0;
LastTimeStamp=0;
CurrentTimeStamp=0;
ToFE();
}
else
{
necBitNumber = 0;
necTempCommand = 0;
HalfBit = 0;
}
}
else if(necBitNumber >1)
{
if(DifferenceTimeStamp >= MinOne && DifferenceTimeStamp <= MaxOne)
{
necTempCommand = (necTempCommand<<1) | 1;
necBitNumber++;
}
else if(DifferenceTimeStamp >= MinZero && DifferenceTimeStamp <= MaxZero)
{
necTempCommand = (necTempCommand<<1);
necBitNumber++;
}
// If alll Timing fails then
else
{
DISABLE_INTERRUPTS(INT_CCP1);
necBitNumber = 0;
LastState=0;
CurrentState=0;
HalfBit=0;
necTempCommand = 0;
LastTimeStamp=0;
CurrentTimeStamp=0;
ErrorFlag=1;
ToFE();
}
}
if(ErrorFlag==0)
{
LastState = CurrentState;
LastTimeStamp = CurrentTimeStamp;
}
if(necBitNumber == 34)
{
RxCompleteFlag = 1;
necCommand = necTempCommand;
necBitNumber = 0;
LastState=0;
CurrentState=0;
HalfBit=0;
necTempCommand = 0;
LastTimeStamp=0;
CurrentTimeStamp=0;
ToFE();
}
ErrorFlag = 0;
}
void main()
{
unsigned int32 x=0;
//setup_oscillator(OSC_4MHZ | OSC_NORMAL);
set_tris_c(0b00000100);
setup_ccp1(CCP_CAPTURE_FE);
// running with 4MHz crystal so the timer1 clock will be at 1MHz that means each tik will be 2us long
SETUP_TIMER_1(T1_INTERNAL | T1_DIV_BY_1);
delay_us(200);
ENABLE_INTERRUPTS(GLOBAL);
ENABLE_INTERRUPTS(INT_CCP1);
printf("\nNEC Decoder\n");
while(1)
{
if(RxCompleteFlag == 1)
{
printf("\nEntireCommand: %lu\n", necCommand);
x = (necCommand>>8 & 0x000000FF);
printf("\nCommand F: %lu\n", x );
x = (necCommand & 0x000000FF);
printf("\nCommand L: %lu\n", x );
x = (necCommand>>24 & 0x000000FF);
printf("\nAddress F: %lu\n", x );
x = (necCommand>>16 & 0x000000FF);
printf("\nAddress L: %lu\n", x );
x=0;
RxCompleteFlag = 0;
}
}
}
I hope the comments on the code here are self explanatory. The circuit diagram for this code would be pretty simple... Just connect the output of the IR receiver to the CCP1 port of the PIC... for this particular PIC18F2550 the pin number is pin 13.
As you can see I have used UART to transmit data that I receive from the IR receiver. I used the UART to communicate with my computer and visualize the corresponding command values for different buttons of the remote.
My final conclusion is that, now that you can map out the command values for each button of the remote, you can do almost anything with it... turn on some motor, switch or do something by some button of the remote... for example if for the button "2" on the remote if you get 192 as a command value... u can put in some code in the microcontroller firmware to do something if the value received is 192.
I hope one day I will have enough time to interface this IR remote decoder to interface with my PC media player software and use the remote to switch musics, star or stop playing them, raise or lower the volume. But a day has only 24hrs... This project is very special to me. Not because it was particularly challenging or it was my first innovation... but because when I was working on it I was very very sad... I dont think I should go into details on a open blog post... :P Just know that this one's special on my list.
Finally if you have trouble understanding any of the code or you have any query dont forget to put your comment below. Oh and you can also put your random opinion there too :P.
![]() |
Photo of the Chinesse Remote |
I am orgranizing this post in accordance to the problems I faced during my struggle.
Guessing The Pin Outs of The IR Receiver
![]() |
Photo of my IR receiver |
![]() |
TSOP1738 Pinout IR Receiver |
![]() |
Another Common Pinout IR Receiver |
If you work with electronics in Bangladesh or you are using scavenged parts for your project... you should know that most of the time you won't know the actual model number of the part. In this situation the best course of action can be... to look for the most common pinouts for that type of parts... google image does a good job to find the most common pinouts for this sort of sitations.
![]() |
The LED is going to blink when the IR Receives Data |
You Should Know That Other People Are Not ALWAYS RIGHT
I started the project with the assumption that the remote was following RC-5 protocol. I naively implemented the RC-5 protocol. But when I tried to test it... I failed to get any data... it was disappointing. I was quite baffled because I did manage to test my RC-5 decoder on proteus 7. It worked fine with my RC-5 decoder there. It took me couple days to realize that the remote wasn't using RC-5 protocol. All this because I didnt have confidence in my own instincts and assumptions and relied on other peoples experience too much.
So my advice to you is-- don't always take someone else's advice for granted... they might be wrong... no matter how experienced they are... we are all human.
Guessing The Infrared Protocol
I think http://www.sbprojects.com/knowledge/ir/index.php is a great web site to learn about all the available infrared protocols. I started my research from this web site. Then I aimed the Chinese remote at the IR receiver and pressed a button. Recorded the output of the IR Receiver output with an oscilloscope... inverted the data... and tried to find the closest match... It kinda coincided with the NEC protocol but not fully. It was missing the last 8bits of the NEC protocol. But it did follow the timing of the pulses as per the NEC protocol for "1"s and "0"s. So, I decided to implement a IR decoder for NEC protocol with 24-bits in mind.If you want to know details about the NEC IR protocol I suggest you follow this link http://www.sbprojects.com/knowledge/ir/nec.php
Implementing The Guess
Well, now that I know what Im expecting to decode... what is "0" and what is "1" can be defined quite easily... I think someone wise once told that knowing what to do is half of the work done.I planned on using the CCP(Capture/Compare/PWM) module of my PIC. Where I used the capture option for Falling Edge.
Okay I think I should put the whole firmware code here for you to better understand. I think I placed enough comments in between... the codes...
////////////////// Chinesse Version NEC Decoder /////////////
////////////////////////////////////////////////
/*
File : NEC_Decoder.c
Author : Md Saiful Islam
Compiler: CCS v4.84
IDE : MPLAB 4.6x
FOSC : 4MHz
*/
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
#include <18F2550.h>
#fuses XT, NOLVP, NOWDT, NOPUT, NODEBUG, NOPROTECT, NOBROWNOUT
#use delay(clock=4000000)
#use rs232 (BAUD=9600, UART1)
volatile unsigned char LastState=0, HalfBit=0, CurrentState=0, necBitNumber=0, RxCompleteFlag=0, ErrorFlag=0;
volatile unsigned int32 necCommand=0, necTempCommand;
volatile unsigned int32 LastTimeStamp=0, CurrentTimeStamp=0, DifferenceTimeStamp;
// used +/- 25% cushion for Ones and Zeros
#define MaxOne (2813)
#define MinOne (1688)
#define MaxZero (1400)
#define MinZero (840)
///////////////////////////////////////////
// Used 10% cushion for the 9ms burst and 4.5ms space--
// but for the rx end the whole thingz supposed to be inverted
#define AGCBurstLong (9900)
#define AGCBurstShort (8100)
#define StartBitLong (4950)
#define StartBitShort (4050)
//////////////////////////////////////////////////////////////
#define RepeatLong (2475)
#define RepeatShort (2025)
//Recommended procedure to change the Edge selection for PIC
void ToRE()
{
DISABLE_INTERRUPTS(INT_CCP1);
CLEAR_INTERRUPT(INT_CCP1);
SETUP_CCP1(CCP_OFF);
setup_ccp1(CCP_CAPTURE_RE);
ENABLE_INTERRUPTS(INT_CCP1);
}
void ToFE()
{
DISABLE_INTERRUPTS(INT_CCP1);
CLEAR_INTERRUPT(INT_CCP1);
SETUP_CCP1(CCP_OFF);
setup_ccp1(CCP_CAPTURE_FE);
ENABLE_INTERRUPTS(INT_CCP1);
}
#int_ccp1
void ccp1_isr()
{
CurrentTimeStamp = ccp_1;
CurrentState = input_state(PIN_C2);
// if the value crosses zero.
if((LastTimeStamp > CurrentTimeStamp) && (LastTimeStamp != 0))
{
CurrentTimeStamp += 65535;
}
DifferenceTimeStamp = CurrentTimeStamp - LastTimeStamp;
// There is going to be a 9ms AGCBurst as the IR Rx device will rx data in inverted form
// there is going to be a falling edge, the space is going to be arround 9ms
if(necBitNumber == 0)
{
if(HalfBit == 0)
{
HalfBit++;
ToRE();
}
else
{
if(DifferenceTimeStamp >= AGCBurstShort && DifferenceTimeStamp <= AGCBurstLong)
{
necBitNumber++;
HalfBit = 0;
ToFE();
}
else
{
HalfBit=0;
ToFE();
}
}
}
else if(necBitNumber == 1)
{
// here the 4.5ms space is implemented in inverted mode
if(DifferenceTimeStamp >= StartBitShort && DifferenceTimeStamp <= StartBitLong)
{
necTempCommand = (necTempCommand<<1);
ToFE();
}
/// If the repeat command is received then repeat the last command
else if(DifferenceTimeStamp >= RepeatShort && DifferenceTimeStamp <= RepeatLong)
{
RxCompleteFlag = 1;
necBitNumber = 0;
LastState=0;
CurrentState=0;
HalfBit=0;
necTempCommand = 0;
LastTimeStamp=0;
CurrentTimeStamp=0;
ToFE();
}
else
{
necBitNumber = 0;
necTempCommand = 0;
HalfBit = 0;
}
}
else if(necBitNumber >1)
{
if(DifferenceTimeStamp >= MinOne && DifferenceTimeStamp <= MaxOne)
{
necTempCommand = (necTempCommand<<1) | 1;
necBitNumber++;
}
else if(DifferenceTimeStamp >= MinZero && DifferenceTimeStamp <= MaxZero)
{
necTempCommand = (necTempCommand<<1);
necBitNumber++;
}
// If alll Timing fails then
else
{
DISABLE_INTERRUPTS(INT_CCP1);
necBitNumber = 0;
LastState=0;
CurrentState=0;
HalfBit=0;
necTempCommand = 0;
LastTimeStamp=0;
CurrentTimeStamp=0;
ErrorFlag=1;
ToFE();
}
}
if(ErrorFlag==0)
{
LastState = CurrentState;
LastTimeStamp = CurrentTimeStamp;
}
if(necBitNumber == 34)
{
RxCompleteFlag = 1;
necCommand = necTempCommand;
necBitNumber = 0;
LastState=0;
CurrentState=0;
HalfBit=0;
necTempCommand = 0;
LastTimeStamp=0;
CurrentTimeStamp=0;
ToFE();
}
ErrorFlag = 0;
}
void main()
{
unsigned int32 x=0;
//setup_oscillator(OSC_4MHZ | OSC_NORMAL);
set_tris_c(0b00000100);
setup_ccp1(CCP_CAPTURE_FE);
// running with 4MHz crystal so the timer1 clock will be at 1MHz that means each tik will be 2us long
SETUP_TIMER_1(T1_INTERNAL | T1_DIV_BY_1);
delay_us(200);
ENABLE_INTERRUPTS(GLOBAL);
ENABLE_INTERRUPTS(INT_CCP1);
printf("\nNEC Decoder\n");
while(1)
{
if(RxCompleteFlag == 1)
{
printf("\nEntireCommand: %lu\n", necCommand);
x = (necCommand>>8 & 0x000000FF);
printf("\nCommand F: %lu\n", x );
x = (necCommand & 0x000000FF);
printf("\nCommand L: %lu\n", x );
x = (necCommand>>24 & 0x000000FF);
printf("\nAddress F: %lu\n", x );
x = (necCommand>>16 & 0x000000FF);
printf("\nAddress L: %lu\n", x );
x=0;
RxCompleteFlag = 0;
}
}
}
Circuit Diagram for IR Receiver/Decoder |
As you can see I have used UART to transmit data that I receive from the IR receiver. I used the UART to communicate with my computer and visualize the corresponding command values for different buttons of the remote.
My final conclusion is that, now that you can map out the command values for each button of the remote, you can do almost anything with it... turn on some motor, switch or do something by some button of the remote... for example if for the button "2" on the remote if you get 192 as a command value... u can put in some code in the microcontroller firmware to do something if the value received is 192.
I hope one day I will have enough time to interface this IR remote decoder to interface with my PC media player software and use the remote to switch musics, star or stop playing them, raise or lower the volume. But a day has only 24hrs... This project is very special to me. Not because it was particularly challenging or it was my first innovation... but because when I was working on it I was very very sad... I dont think I should go into details on a open blog post... :P Just know that this one's special on my list.
Finally if you have trouble understanding any of the code or you have any query dont forget to put your comment below. Oh and you can also put your random opinion there too :P.