Background
My entry into the world of micro-controllers was out of my desire to create a USB device. I wanted to create a general purpose IO framework that could be used to do many useful things, and one of my use cases was to add infra-red capability to my mobile which had none, and use it like a TV remote. While that framework is under way, and i may blog about it later, what i realised was that the runtime overheads of generic code might prevent me from achieving the exact timings that are required for remote protocols like RC6. Hence i fell back to writing a more basic/specific code for this purpose.I have tested this code on the PIC18F4550.
Basics
My work would be in two phases, first find the codes, and second, transmit them. Since i did not find any reference for codes for the tata sky settop box, i had to find out the codes myself. i.e. what was the tata-sky remote transmitting. My starting idea was that it would be a series of ON & OFFs, with specific durations. To capture them, i would need an infra red sensor. i searched the net and found the following links :
This is a project for arduino, and describes how to capture the IR codes for a Nikon camera, and how to play them back. Since i was programming with PICs, this project would not work as is for me and i had to start on my own code.
http://www.sbprojects.com/knowledge/ir/index.php is also a good reference for knowledge of IR protocols.
One basic thing to understand, is that the HIGHs in the code are usually transmitted as a series of pulses at high freq, called the carrier frequency. This is so that ambient light/disturbances are not mistaken for a code. Also, it lets the IR diode cool, since it takes rather high current, 50- 100mA.
There are 2 kinds of sensors, a simple IR sensitive transistor/diode that would capture the signal as is, i.e. even the carrier pulses. This is available for 4-5 Rs. The other filters out the carrier pulses, so we directly get the highs and lows of the code, it costs slightly more, around 25 Rs. i got both, and experimented with them. The plain receiver has low sensitivity, and also, i did not have a oscilloscope to measure the waveform exactly. The one with the filter had good sensitivity so i decided to go with it.
The one i got was TSOP1730( carrier freq 30Khz). Note that its output is inverted, i.e. it is usually HIGH and when it receives IR input, it goes low.
i also got an IR diode for transmitting for 4-5 Rs.
i also got an IR diode for transmitting for 4-5 Rs.
Trial and error
i added a facility to the read code to define an IDLE state and wait until it changes. so we can run the program, it will wait till input is in IDLE state, in our case, HIGH. when we press the remote, the state changes and now it starts taking the reading, i.e. measuring the durations of ones and zeroes.
Why measure durations, why not just measure the sequence of 1s and 0s ? The reason being that in protocols like RC5/6, both 0s and 1s are represented using both the HIGH and LOW states. HIGH first, LOW next could mean 1 , and LOW first, HIGH next could mean 0 etc. The durations are important and need to match the protocol.
One of the problems with the TSOP1730 sensor is that it can get randomly triggered by surrounding disturbances, so sometimes the program would start taking readings without my pressing the remote. Its suggested to tie the TSOP output pin to VDD using a > 10K resistor, and putting a capacitor betw the TSOP's VDD and GND to reduce power-supply interference. i also tried to tie the output to GND instead, thru a 175K resistor, and used a .1uF capacitor betw the TSOP's VDD and GND. None of the methods proved totally fool-proof however, and the random triggering still happens sometimes.
i started to look at the readings i was getting on pressing the remote. then i would try to transmit those durations, inverted now, (since out sensor was inverted ), and see if it worked, but no luck. i did not even know what protocol my remote used, so i had no idea if my readings where right or not.
i searched the net again, came up with link link : http://forum.arduino.cc/index.php?topic=166536.0, that somebody else had built a remote for tata-sky and the protocol was like RC6, tho no details were provided. The guy had built it from the arduino link i have given at the start, so i started looking at it again. one thing i realised was that timing need to be accurate.
at this time, i was using a generic io framework, where one could specify in the request which pin to use, what carrier-freq, the durations etc. i started measuring the timings of my transmit and read using the PIC's timer, and realised that the generic nature of the framework was adding a lot of overhead over direct hardcoding. So i changed the generic code to bare and specific code for the task in hand. One more problem was that my readings did not fit the RC6 protocol exactly; they extended some bits longer. i decided to try with both options, i.e. once stopping at RC6 length, and once with my readings as is.
i started to test again, however, still no success. The settop box links red when it receives a valid code, and this was not happening. i went back to the article to check what else i was missing, and there it was. The article said that we need to send the code twice, not once. i added the repeat with a gap of 65ms, and the set-top box finally responded by changing the channel. yay ! It turned out that the readings that i had got were correct, even tho longer than the RC6 length.
The strange part was that my readings did not show the code being repeated, so how did the remote work ? maybe the remote encodes some more info that i have missed.
The timings of my readings were another issue, they did not match RC6 timings, where one unit is of 444 uS. So i wrote a python script to change my readings to RC6.
def fit_to_rc6( durations) : rc6durations = [] for loop in range( 0,len(durations) ): duration = durations[loop] newduration = duration if duration < 600 : newduration = 444 elif duration < 1000 : newduration = 889 elif duration > 1800 and duration < 3500 : newduration = 2666 rc6durations.append( newduration) return rc6durations arr_durations = fit_to_rc6( [3015,474,435,400,408,400,408,852,408,839,855,407,408,407,408,413,408,407,408,393,428,407,408,413,408,407,408,413,408,407,408,413,408,407,408,407,968,744,848,407,408,400,408,852,408,413,402] )
Here is a sample of the readings i got :
the program has a max wait of 65535 us. after the signal ends,we get all max-durations i.e. 65535. Also the sensor gives inverted output,so S=0 is actually HIGH input to the sensor
Starting.. S:0,D:2662,S:1,D:656,S:0,D:495,S:1,D:265,S:0,D:528,S:1,D:224,S:0,D:535,S:1,D:663,S:0,D:522,S:1,D:656,S:0,D:948,S:1,D:218,S:0,D:542,S:1,D
:224,S:0,D:535,S:1,D:224,S:0,D:542,S:1,D:224,S:0,D:535,S:1,D:224,S:0,D:542,S:1,D:224,S:0,D:535,S:1,D:224,S:0,D:535,S:1,D:224,S:0,D:542,S:1,D:224,S:0,D:535,S:1
,D:224,S:0,D:542,S:1,D:224,S:0,D:535,S:1,D:224,S:0,D:542,S:1,D:224,S:0,D:535,S:1,D:224,S:0,D:542,S:1,D:224,S:0,D:955,S:1,D:656,S:0,D:522,S:1,D:231,S:0,D:535,S
:1,D:224,S:0,D:955,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:6
5535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:655
35,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535
,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S
:1,D:65535,S:1,D:65535,Done..
Starting.. S:0,D:2662,S:1,D:656,S:0,D:495,S:1,D:265,S:0,D:528,S:1,D:224,S:0,D:535,S:1,D:663,S:0,D:522,S:1,D:656,S:0,D:948,S:1,D:218,S:0,D:542,S:1,D
:224,S:0,D:535,S:1,D:224,S:0,D:542,S:1,D:224,S:0,D:535,S:1,D:224,S:0,D:542,S:1,D:224,S:0,D:535,S:1,D:224,S:0,D:535,S:1,D:224,S:0,D:542,S:1,D:224,S:0,D:535,S:1
,D:224,S:0,D:542,S:1,D:224,S:0,D:535,S:1,D:224,S:0,D:542,S:1,D:224,S:0,D:535,S:1,D:224,S:0,D:542,S:1,D:224,S:0,D:955,S:1,D:656,S:0,D:522,S:1,D:231,S:0,D:535,S
:1,D:224,S:0,D:955,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:6
5535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:655
35,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535
,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S:1,D:65535,S
:1,D:65535,S:1,D:65535,Done..
we can see that the reading vary quite a bit, from 224 to 542 are all actually 444 us, and from 656 to 948 are all 889 us ? how do i know ? by getting a bunch of readings, and seeing which part repeats most, and comparing with the Rc6 protocol. The remote_codes.txt file contains codes for some common keys.
Most of them have been implemented in the sample code too.
about the transmitter, its a simple transistor circuit, with the base driven from the PIC, and the IR diode with a 100R resistor in series with the collector. emitter to ground.
about the transmitter, its a simple transistor circuit, with the base driven from the PIC, and the IR diode with a 100R resistor in series with the collector. emitter to ground.
Code
The code is available at https://github.com/manojmo/pic_micro. The read_pin_durations.c is for the reading of the remote codes. The tata_sky_remote_serial.c is for transmitting the codes. it uses a uart to accept a code, and looks it up from an array and transmits it. The array contains the durations of ON/OFFs. Ideally, since we know the protocol and timings, we could use a more compact format to store the codes, rather than actual durations. e.g, we could keep the start-bit of 2666 and 889 separate, and represent the rest with 444 as 0 and 889 as one, and hold the entire code in just an int. Also, no keyboard interfacing is done. One could use a multiplexer, to get 16 lines from 4 pins, or implement it in the PIC. a good link to understand a keyboard is
http://pcbheaven.com/wikipages/How_Key_Matrices_Works/
http://pcbheaven.com/wikipages/How_Key_Matrices_Works/
Other references
Ken sheriff has a IR library for Arduino, which can capture and play-back remote code on the go ! cool, no ? http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html
Another project for PICs is http://www.compendiumarcana.com/irwidget/
Hello,
ReplyDeletei am not sure if TSOP1730 is a correct sensor to decode RC 5/6 protocols.
SM0038 aka TSOP1738 which is set to 38 Khz or TSOP1738 (36 Khz) are the actually sensors should have been used to decode the signals.
Also if you use RC circuit between Vcc and GND, it should stabilize the sensor. R = 100 ohms and C = 4.7 uF
Also tatasky uses RC6-6 protocol (mode 6).
ReplyDeletehi Amitesh,
Deletethanks for your inputs. i did not find much info on tata-sky at that time, so i had to go by trial and error.
Hi Manoj,
ReplyDeletedo you have the codes for buttons 0-9?
no, just the basic nav buttons. was just a proof-of-concept.
Deletei am trying to generate raw codes for tata sky from https://raspinterest.wordpress.com/2017/01/09/tata-sky-lircd-conf-for-lirc-on-raspberry-pi-3/comment-page-1/#comment-125. i found below code from https://github.com/cyborg5/IRLib/blob/master/examples/rcmm/rcmm.ino , but it is not for RC6. do you have any idea how to create code similar to below for RC6?
Deletevoid IRsendRCMM::send(unsigned long data, unsigned char nbits) {
Extent=0;
data = data << (32 - nbits);
nbits=nbits/2;
enableIROut(36);
mark(RCMM_HEAD_MARK); space(RCMM_ZERO);//Send header
for (int i = 0; i < nbits; i++) {
mark(RCMM_DATA_MARK);
switch (data & 0xC0000000UL) {//use the leftmost two bits
case 0x00000000UL: space(RCMM_ZERO); break;
case 0x40000000UL: space(RCMM_ONE); break;
case 0x80000000UL: space(RCMM_TWO); break;
case 0xC0000000UL: space(RCMM_THREE); break;
default: Serial.println("sending error");
}
data <<= 2;
};
mark(RCMM_DATA_MARK);
space(27778-Extent);
};
i found the code for RC6. https://github.com/z3t0/Arduino-IRremote/blob/master/ir_RC5_RC6.cpp. but the raw code generated by me looks slightly different from yours. below code is for KEY_CHANNELUP 0x1DF
Delete2666,889,444,444,444,444,444,444,444,444,444,444,888,888,444,444,444,444,444,444,444,444,444,444,444,444,444,444,444,444,444,444,444,444,444,444,444,444,444,444,444,444,444,444,444,444,444,444,444,444,444,444,0
sorry, these pocs were just for fun when i was on a break. i do not have the time now to delve into it again.
Delete