Have you ever wondered how TV and VCR remote controls work? Have you ever dreamed of accessing your Atari Classic by remote control? If so this project will be right up your alley -- I'll show you how about ten dollars worth of electronic parts from your local Radio Shack can enable your Atari to receive infrared (IR) signals from most TV, VCR, and stereo remotes, and send IR signals to most any IR remote-controlled appliance. The parts fit in a small box I call an IRG (InfraRed Gateway) which connects to a joystick port on your Atari 400/800/1200/XL/XE.
Please see the hardware instructions for a description of how to build the IRG. This document describes how to control it with some simple machine language subroutines called from BASIC or CC65.
First let's briefly consider how an IR remote works. In the nose
of each of your remotes, there's a bank of infrared LEDs that transmit
either IR modulated at 40kHz, or nothing, these two possibilities
representing logic level 1 or 0. Your TV or VCR listens for a 40kHz IR
signal and demodulates it into a serial pulse stream. (This is
illustrated in the attached PostScript figure "pulses.ps".)
When the proper stream is sent, the TV or VCR executes a remote function.
The key point is that the 40kHz IR signal is either present (logic level 1) or not (logic level 0), so the IR link is like a single (one-way) TTL data line. The protocol each manufacturer uses to send information is up to them, but there are some common standards. Most IR remotes transmit packets of roughly 30 bits using a form of pulse-code modulation (PCM). Within one packet information specifying manufacturer and appliance type is usually given, so that different manufacturer's remotes don't interfere with one another. Some remotes send several different packets even when only one button is being pushed in order to distinguish between a button being held down and one that is being pressed repeatedly. Usually a single packet is enough to execute a function, but at least one of my appliances will only respond to a correct sequence of multiple packets.
Once you've got the IRG hooked up, you are ready to play with it. To do that, you'll need a bit of machine code to send and receive IR signals. The program IRGSCAN.BAS listed with this file is a self-contained example program which contains a short machine language routine that simply digitizes input from the IRG at precisely timed intervals adjustable from 64 to 1339 clock cycles (36 to 748 microseconds). The assembly code contained in IRGSCAN.BAS is also provided (IRGSCAN.M65) so you can get some idea of how it works. Once the IR data is digitized IRGSCAN.BAS can also send it back out over the IRG.
Let's take a closer look at how the machine code IRGSCAN.M65 works. One problem I had to solve was that the pulse width of 500 microseconds used in the IR standard corresponds to only 895 microprocessor cycles. A packet 30 bits long, or 60 pulse widths long, lasts about 1/30 sec, during which time the Atari's 6502 would normally process two vertical blank interrupts, and two screenfuls of DMA cycles would also be stolen from the 6502 by ANTIC. This introduces significant uncertainties into the timing loops I wanted to use in my code, so my solution was to simply turn off DMA and IRQ/NMI interrupts, avoiding vertical blanks, keyboard interrupts, and ANTIC cycle-stealing entirely at the cost of losing the display during IR transmission and reception. I experimented with other approaches based on keeping track of interrupt service routine delays, for example by reading ANTIC's vertical line counter (VCOUNT), but I had little success. The DMA cycles stolen by ANTIC are highly dependent on the display mode, and I wanted this code to work with any display. In my applications, the Atari running the IRG isn't even hooked up to a monitor, so this is no big deal. (Even with a display, you still have a usable picture, since the screen is stable as long as there are no IR transmissions going on.)
IRGSCAN.M65 gets loaded into memory at page six. When this routine is called from BASIC with 4 arguments, it scans the IRG and stores bits collected at precise time intervals into a buffer region. You can set the sampling time interval by changing the variable SAMPLE in IRGSCAN.BAS, which is a single byte. Values of 0 through 255 produce digitization time intervals of 64+5*SAMPLE clock cycles. When called with 3 arguments the routine sends the buffer out over the IRG. Each of the eight bits in the buffer bytes are used, in order from MSB to LSB. This allows ANTIC to display the buffer directly on the screen in a two color graphics mode, assuming screen memory is used for the buffer area. IRGSCAN.BAS does this with the high-resolution graphics mode's (BASIC mode 8) display memory for the buffer. You can scan a remote using IRGSCAN.BAS to see what kind of signals it generates, and play back the sequence to mimic the effects of the remote control. Try changing the timing parameter SAMPLE to see a higher or lower resolution version of the IR signal.
At this point, you can adjust the center frequency of the 555 timer in your IRG to precisely 40kHz, if you haven't already done so. Just scan, say, a TV function like "mute" into memory using IRGSCAN.BAS, then point the IRG at the TV, and play the signal repeatedly. Adjust the trimmer until the TV responds. (The "mute" function is convenient since you won't have to look at the TV to see when it's responding.) My IRG had quite a broad range of acceptable trimmer settings -- it doesn't have to be perfect.
With some more programming effort, IRGSCAN.BAS could be modified so as to serve as a "universal remote," with a library of digitized functions. Unfortunately, IRGSCAN.BAS provides no easy way to *recognize* incoming bit patterns. That problem can be solved with just a little bit more effort.
Let's look at a schematic for a more sophisticated receiving algorithm. The IRG driver, which is provided here in versions suitable for both BASIC and CC65, installs a vertical blank interrupt (VBI) routine that constantly (well, every 1/60 second) checks for IRG activity. When IR input is detected, the driver takes over and scans the IRG, decoding the IR bit pattern on the fly. This allows a BASIC or C program to recognize a particular IR signal, and it solves another problem with digitizing: excessive memory usage. You can see from running IRGSCAN.BAS that an IR packet contains only a few bytes of data, yet IRGSCAN.BAS uses a hundred bytes or so of storage to keep it in digitized form. The IRG driver works with IR signal codes in a packed form, requiring only a few bytes per packet.
The driver uses a few parameters to describe how the incoming
signal should be digitized. These are set by calling the
void IRGinit(int sample, int ton, int toff, int repeat, int tonmin, int toffmax, int qtime, int totime)
Here "sample" controls the sampling rate, or timebase of the digitizer and sending code, using the same delay subroutine IRGSCAN.BAS uses. "ton" ("toff") is the on (off) time for an ideal "1" ("0") pulse. "repeat" is the number of times to repeat the send operation of a single code. Most equipment works better when the control codes are repeated several times. "tonmin" is the minimum values for valid "1" pulses. "toffmax" is the maximum off time for a "0" pulse -- longer than this means end of packet. "qtime" is the required "quiet time" of IRG inactivity before starting to receive a packet. "totime" is the timeout time -- the driver times out if spurious pulses are followed by inactivity of this duration. Good values for these parameters are
sample = 2, ton = 20, toff = 20, repeat = 3, tonmin = 10, toffmax = 30, qtime = 100, totime = 25.
Once installed, the driver can send and receive packets via the
int IRGread(char *buffer, int size) = # bytes returned
int IRGwrite(char *buffer) = 0, or 1 if IRG is active
where "size" is the maximum buffer size, and all strings are null terminated. The receive code has a built-in buffer, so it won't drop packets if you don't read them right away. (The size of this buffer is set inside the driver source code, so you can change it there.) You can look at the driver code ("irg.m65") to see more of the implementation details.
I have provided a simple example program "irgdemo.c" which can be
used as a translator between different sets of IR codes. For example,
it can read remote codes from your VCR remote and map those functions
onto your TV by transmitting new codes to the TV when the proper VCR
codes are received. The demo program reads an init file containing
two types of information. First, lines like
def vcr_chup ab6edf
means that the IR code "ab6edf" corresponds to the named tag "vcr_chup" (VCR channel up). After all of the IR packets are defined in this manner, lines like
map vcr_volup tv_volup end
specify that if the IRG detects the "vcr_volup" packet, it should send the "tv_volup" packet. More than one packet may be output by the IRG, for example
map vcr_qv vcr_n2 vcr_n1 vcr_enter end
maps the "vcr_qv" packet (this is an unused button on my remote) into the three-packet sequence that changes the channel to channel 21.
One last feature is that while the demo program is running, if a
keyboard key is pressed it will simulate reception of the packet ffXX
where XX = the internal Atari code for that key. So
def key_space ff21
defines the "packet" corresponding to pressing the space key. Included with this release is a sample irgdemo.rc file that contains some definitions for a Mitsubishi VCR.
I originally built my gateway in order to understand how IR remotes operate, and because I was interested in controlling home appliances and security systems by IR remote. The Atari was a perfect match because it's so easy to connect to the outside world. But, the applications go further than this. I use my IRG as a "universal" remote interface to my TV, VCRs, and cable box. My Hi-fi VCR has a complicated remote control with many more functions on it than I use, such as control over a TV I don't own. An Atari/IRG translates the codes from my VCR remote to control my TV, backup VCR and cable descrambler. Now I have one remote, without losing the VCR's special features such as jog/shuttle control. The universal remotes sold commercially don't have the mechanical hardware required to emulate these functions.
Other applications of an Atari/IRG also spring to mind:
I hope you enjoy building and using the IRG. Please drop me a line (preferably by e-mail) if you find any unusual applications of the IRG. I'm always interested in what people are doing with the Atari 8-bit machines!
go to the Deaven homepage