RCProxy is a a proxy for IR signals.
It is capable of detecting IR signals is various protocols and act accordingly - for example, send out IR signals in other protocols.
The motive for this project is a situation i encountered at home:
I have my samsung TV and cable box hooked up via IR blasters to my PC, and an IR reciver as well.
I'm using LIRC and setup'd irexec to do various tasks when some buttons are pushed on my remote.
Among others, those tasks involve sending out IR signals via the IR blasters to my cable box and my TV. One example
is the "Live TV" and "Recorded TV" buttons which we're used to switch between cable and computer views. when "Live
TV" was selected, the TV should be switched to the correct source. When "Recorded TV" was selected, the TV would
switch to computer view and the media player software would be launched.
The probelm arises because the computer must be turned ON all the time in order to be able to do that.
It distupts my sleep and wastes a lot of electricity. It's just ain't right to drive a RC proxy using a dual-core
The solution? Use a low-cost processor and low-cost IR reciever and transmitter\s to do exactly that job -
Detect IR signals and send new ones instead.
The architecure of the system is as follows:
The entire dance is mastered by a centeral framework which is able to detect / send IR carrier signal at 36Khz.
When a carrier signal change is detected (Low->High, High->Low) it will automatically notify several decoders. each decoder's task is to
decode some protocol, and it must do so by analyzing the time between edges in the carrier signal.
Whenever a decoder picks up a signal, it notifies a centeral managment unit called action_manager
about it's findings.
Each decoder has a special table which translates a key into a "user's intention" (for example : 0x381B => "volume Up").
The code is looked-up in all the tables and the "user's intention" is notified in the system.
Each intention is then matched against several actions, where each (at least currently)
means sending an IR signal to a different device (using a remote ID and key) (Remote ID corresponds to a an encoder of some sort).
On the other hand we have several encoders which defines many keys that each is capable to encode, and actions are mapped to them according
to the remote ID. Thus, each key in the main (theoritcally there can be many) remote is translated to zero or more signals of other remotes.
(At lease currently. it is possible to add different kind of actions if needed - GPIO or anything else).
This project uses a PIC18F2550 processor from microchip. This chip has a usb module, 4 timers and 2 ccp modules. It also has 24Kbytes of
program memory, 2Kbytes of RAM and 256 bytes of Non-volatile memory. This project requires the USB module (if you want debugging),
3 Timers, and 2 ccp modules. it doesn't use the Non-volatile memory, and program memory usage depends on the version of software and the
the type of compiler (optimizations).
In order to be able to use it efficiently, I've programmed it with "usb HID bootloader" from microchip. It's a bootloader,
which allows me to use any pc to program the chip (without special hardward). It located in the first 8Kbytes of the program memory and
thus any application which i wish to program the chip with must be specially compiled, so that it will begin from location 0x1000
(including the reset vector). the main.c
file in the RCPROXY is implementing this solution.
In order to build applications for this processor, one must download MPLAB IDE, and MPLAB C18 (both available from microchip.com), and
install them. Then, when creating a new project in MPLAB IDE, you should configure the project options to fit your installation of the
C18 compiler, linker, assembler and include paths/libraries. You can then use the HID bootloader executable to program your newly created
project to the chip:
- Press the reset and SW1 buttons on the board
- Let go of the reset button
- Let go of the SW1 button
And you should now see "Device attached" in the HID bootloader program.
LIRC (and a compatable reciever / transmitter) is a must in terms of debugging such a project. Lirc can offer you 2 things:
The remote learning capability which allows you to understand how is the protocol used.
Being able to see exactly the timings of the IR signals you decode / encode is a good help. In many cases i've compared
Them to mine, realized that some sort of linear relation exists, and calculated it based on empirical data.
Currently the only things implemented are the framework, RC6 Mode 6A decoder, and a JVC-like protocol as encoder.
The main application file is main.c
and it's built around microchip's USB examples, it's in turn uses ir.c
to do all the IR processing (the framework).
At the initialization routine in ir.c
we initialize some more modules: yes.c, samsung.c, mce.c, jvce_encoder.c
each module registers itself in the appropriate framework module.
we use CC1 in in capture mode to capture the voltage going low->high or vice versa, and record the time passed (using the ccp1 value).
we then inform this value to the decoder, which update his state accordingly.
we use Timer3 to wait for events. When we receive signals we always wait for a gap period of no change to signal the end of the command,
and thus we use Timer3 to wait for no change (we re-initialize it at each change). when it's done, we notify the decoders.
We also use the CCP2 module as PWM to generate a 36Khz carrier for outbound signals.
It's always in pause mode, meaning, Timer2 (which CCP2 as PWM uses) is not running, until we need to.
When we want to generate a signal, we set Timer2 status accordingly (ON/OFF) and set Timer3 to overflow in a known time. It then flips
Timer2 status and sets the timer again.
All the information (time to overflow) is given via the encoders (currently: single encoder, easy to add more).
The decoder keeps an internal state which is made out of a status (current location inside the signal), and a few other data pieces which
he must keep track of. When a signal is detected, It notifies the action_manager, and the "acting" process begins.
The encoder is simplier, and it's internal state is much smaller, and again, it uses a callback function to tell the framework how much
time should it wait before flipping the carrier signal state.
Currently, i've noticed that if i send an IR signal, and then appends it with some gap, and then another signal, the recievers won't notice
it. They expect the signal to begin from the first IR carrier transmittion. Meaning, unless my hardware will be changed, i cannot act and
send a signal for each signal i recieve and recognize, but i'm forced to wait a while beforehand.
Thus, i'm currently not supporting more than one action per "user's intention" altough the code has this option (it's commented).
If i'll get 2 (or more) transmitters and place them directly on top of the recievers i would be able to change this comment and send out
a signal as soon as i receive a signal i recognize.
Moreover, for debugging reasons i'm currently recording the entire timings of the signal and then send it out to analyze_signal() which
makes sense of it, but the code is built (espcially the decoder) so that i won't be needing to keep track of all the signal, but rather
one period of time at a time. It could be changed very very easily (call rc6_add_data immediatly instead of holding the data in a buffer
for later analysis).
I've encountered many difficulties during the making of this project.
The first problem was being new to microchip products. I've never used this chip before, and neither did any of my classmates.
Using it required me to install windows xp on a virtual machine (i'm using linux), and set up the development enviroment necessary.
Getting to create my first program was very hard, as the hardware i've used contained no debugging instruments (leds..) of any kind.
I've created a program which alternates a gpio port from low to high and vice versa, and sleeping for some time in the between.
I then used a multi-meter to mesure the voltage of the chip's leg and verified that it was working. it took some time to get it done.
The second problem i've faced was to transfer data using the USB module. At this stage i've yet known nothing about the chip and it's
internals, and reviewing the USB code was hard. Another problem arised later on when i needed to use interrupts. I needed to write debugging
information from the ISR, and the way to do it is write to a global buffer, and wait for a signal which tells you that the USB module is
free to send more data, and then use data from this buffer. The problem arised when the code which implemented this behaviour doesn't run
in the ISR, but the ISR must be able to write to the global buffer. Because the interrupt can happen at any time, and locks cannot be used,
i had to implement a ring buffer. thus, writers can write while a single reader reads. Understanding the problem was very hard, and it was
very hard to debug.
The most time-consuming problem i've faced was due to the combination of the short timings of the project (IR signals), and the lack
of RAM. For example, i wanted to debug the timings i'm getting when hitting a button on the remote, and the most obvious solution is to
just print it out. BUT then the usb module dispatches a number of interrupts of his own, causing the cpu to work for too much time, and for
me to miss out some other interrupts. (and for some reason the USB module cannot work without interrupts, even though it has official
support for "polling-mode"). Debugging this problem was very hard, and required by to use several tricks in some places of the code (for
example, the IR signal decoders can only use a single timing value at a time, instead of being able to review a buffer holding the entire
Another problem i've faced was the lack of documentation for the IR protocols i've used. For example, the RC6 Mode 6A is correctly
documented in just one place, many more give a wrong descriptions of the packet (very very confusing!). The protocols implemented for my
other remotes (yes and samsung) are not documented at all, and i've figured out the details myself.
The lack of RAM has also forced me to be using program memory instead of ordinary RAM. Thus, all the configuration data (IE: the
"user's intention" to actions is done i code, and is compiled with the program. This is bad from the user's prespective, because it means
he cannot change configuration without re-compiling and re-programming the device.
The high periods of the IR carrier signal tends to be longer than that of the low period.
Meaning -> the high periods are usually longer than the low ones of equivalnt length.
USB pooling mode in microchip's examples just doesn't work for some versions of the code (seen in some forums).
1Kbyte out of the 2Kbytes of RAM in the PIC18F2550 is dedicated to the USB module.
I takes some work getting C18 to link a program where one file (one obj) uses more than 1 bank of memory (256 bytes).
C18 has a trial license which has optimizations option enabled, while some licenses' types have them disabled.
The USB stack of VMWARE is not very stable, i got it crashing (and crashing my pc) a few times during development.
C18 produces a warning when a function's type is unkown (not decalred). usually, it causes catastophic crashes on rutime
even though it's just a warning...
This project is published under GPLv3.
Shahar zini <shshzi at g-m-a-i-l> (replace g-m-a-i-l with gmail.com)