Remote Control IR Transmission Using IOIO
Nadav Burstein & Yanir Gam Ze Letova
IOIO is a piece of electronics coupled with a firmware and software stack, created by Itay Ben-Zvi, which allows an Android device to connect to electronic circuits. The board is equipped with a Microchip PIC microprocessor, and the firmware and software stack allows application developers to operate various features of the MCU, such as digital IO, UART, SPI etc.
Our project's goal was to extend the IOIO framework to support sending arbitrary remote control IR signals. This can be used, for example, to develop customizable and universal remote controls in the form of an Android application, allowing users to control their household IR-operated appliances from a mobile phone.
An IR signal is a modulated beam of infrared light. When the beam is “on”, it actually blinks rapidly at a certain frequency, called the carrier frequency. This frequency varies between different IR protocols, but is generally around 38 kHz. Each IR receiver are is tuned to this frequency, and only interprets an infrared beam as “on” if it blinks in that frequency. This is done to reduce noise, as infrared light is everywhere, radiating from the sun, ordinary light bulbs, and even human bodies.
IR protocols encode different device commands in a series of bursts – on and off cycles – which usually represent the signal's start, a sequence of 0s and 1s representing the requested command, and the signal's end. Each protocol has its own different way of encoding these commands. To be able to support as many protocols as possible, we chose to represent an IR signal by a sequence of “burst pairs”, where a pair is an “on” time and an “off” time, measured in microseconds. This allows us to emulate virtually all IR protocols, and is actually the method used by LIRC to represent raw codes.
The IOIO framework contains infrastructure enabling message transfer between the Android device and the IOIO board. We added a couple of message types that are used by the IR module to transfer our representation of an IR signal from the device to the board. We had two factors to consider: one, the messages are limited in size, and are smaller than an entire IR signal represented in our scheme; two, the entire IR signal needs to be available to the IR module in the board before transmission begins, because the signal is rapid. To this end send the signal in chunks to board, where it is buffered until it arrives in full, and only then transmitted.
To control an IR transmitter, use the IrTransmitter interface. For example, to have an IR LED connected at pin number 10 act as an IR transmitter:
This will cause pin number 10 to act as an IR transmitter. Note that since the IR transmitter uses a PWM module to generate the IR signal, the pin has to be PPS capable, and a PWM module is allocated for the transmitter's use. To send an IR signal, have an n-by-2 two-dimensional int array, which contains burst pairs to be transmitted, and use the irTransmit() method:
Where 38000 is the carrier frequency in Hz. Once you are finished with the IR transmitter, call the close() method to free resources, such as the allocated PWM module.
To demonstrate the use of the IR transmitter module, we create a simple Android application – a customized remote control that can perform simple functions on a Pioneer DVD, a Toshiba TV and an Innova DVB-T receiver. The code can be obtained at the bottom of the page.
Since IOIO code base is quite large, we decided to start with a simple implementation: a stand-alone program to run on the PIC and generate a hard-coded IR signal. Once we got this simple IR transmitter working, we moved on to merge it into the IOIO, integrating it with the existing code and extending the Java API to support IR transmission. When we came across bugs in actually making the signal, we’d go back to our stand-alone transmitter and try to fix it there. Among the difficulties we encountered, were these:
1. Our first challenge was becoming familiar with the different technologies involved. The PIC architecture is different from the one we used during the semester, and neither of us had any experience developing for the Android platform. As mentioned we initially built a stand-alone PIC program to generate an IR signal, which eased our way a bit into understanding the IOIO code base.
2. Another issue we had was how to debug our code. At first, we used only the IOIO yellow LED to signal different stages of the execution, and at one point we even wrote a debug function to flash the LED with 0s (blinking) and 1s (continuous light) according to a given memory address contents, wrtiting down the result. Eventually we encountered issues that were extremely difficult, if not impossible, to debug this way. We then consulted Sivan, and he provided us with an LPC board to serve as a UART proxy between the IOIO and a computer terminal. This enabled us to use the IOIO's debug logging functions and have a proper printf().
Also related to debugging and
testing was the issue of obtaining valid IR codes for devices we had available.
The internet is filled with various IR codes in different formats for a huge
variety of devices. However, it was imperative that the codes we obtain be
reliable, so that in case the device we're testing against isn’t responding,
we'll know that the problem is with our program, rather than with the IR code
we have. In this respect, most codes obtained from internet forums proved
unreliable in retrospect. Luckily, we had a Pioneer DVD available, and Pioneer
IR codes for its devices. The codes are provided in ProntoHex
format, which is different from what we use. However, the LIRC project has a
script called pronto2lirc that transforms a ProntoHex code to a LIRC raw code, which are the actual
burst times of the IR signal used by us.
Later on, to obtain IR codes for two other devices, we used an oscilloscope to record the signal generated by the remote controls for those devices.
4. While IR receivers are reported to be rather tolerant with regard to incoming signal accuracy, sending IR signals still requires achieving timing accuracy of about a few tens of microseconds. Since we wanted to integrate with the IOIO with as little disruption as possible, we wanted to avoid having to hog a timer for accurate delayed execution. We thus resorted to using dead loops to generate delays. This proved to be not so easy when trying to achieve find-grained timing, as described in a bit more detail here.