Introduction
At the end of our 4th year in the HTL (technical college) we were looking for an interesting project. We decided to develop our own "Universal Remote Control for the PDA." With this program it is possible to control your TV. During our internet research, we didn't find much information about this topic, but we found some useful information on codeproject.com and therefore we decided to publish our own solution here.
About the program
General:
- application for PocketPC (Windows Mobile 3.0 and later)
- Windows CE operating system
- code in C# (Visual Studio 2005)
Implementation requires:
- opening COM Ports on the PDA (COM1, COM2,…)
- creating code to send to the PDA (RC5-Code)
- creating pauses (range in µs)
Transmission
The existing infrared port on the PDA uses the standard IrDA protocol. The problem is that although the IrDA class in C# is able to open the port, an endpoint is required for successful transmission. This means that the class needs a connection between two intelligent devices (PDA-PDA or PDA-Handy). Therefore it is not possible to use the IrDA for a connection-oriented transmission to a dumb device such as a TV.
There are three types of infrared transmission:
- Standard IR (SIR 115Kbps)
- Fast IR (FIR 4Mbps)
- Consumer IR (CIR 115Kbps)
CIR is the type you find in remote controls with a wide and long range. The wavelength of infrared light is between 940 and 950 nm. How the commands are coded depends on the manufacturer of the device.
![Screenshot - one.jpg](/KB/miscctrl/UniversalRemoteControlPDA/one.jpg)
We use the serial port and can generally create every code on the basis of the UART protocol. The first step is to find out which of the COM-ports on the PDA is responsible for the IR-port.
COM1 | RS232 connector on DS, if DS is in normal (not transparent) mode |
COM2 | Raw IR, used internally by IrDA protocol (If you really need Raw IrDA, use this port in the same manner as for RS232 communication.) |
COM3 | Used internally by IrDA protocol (Use IrDA sockets for communication with IrDA) |
COM4 | GPS module in the device |
COM5 | GSM module |
COM6 | GPS module in DS, if DS in normal state (read-only access) |
COM7 | RS232 connector on the device (Lemo connector on H41RS device) |
COM8 | RS232 connector on DS, if DS in transparent mode |
COM9 | Free |
The COM2-port makes it possible to send serial data through the IR-port. The SerialPort-class in C# provides methods of accessing the serial driver properties. We wrote a program which could display all available COM-ports on the PDA. The initial problem was that only COM3-port was shown. The solution was to install an update for the SerialPort-class. The update is called Microsoft .NET Compact Framework 2.0 Service Pack 1 and repairs some of the faulty methods in the SerialPort-class.
public void GetName()
{
StringBuilder s = new StringBuilder();
String s1;
string[] ports = SerialPort.GetPortNames();
foreach (string port in ports)
{
s.Append(port+" ");
}
s1 = s.ToString();
label1.Text = "Port Names: "+s1;
}
RC5-code
For further steps, basics of RC5-code are required. First we must initialize all parameters of the serial port:
public static String portName = "COM2";
static int baudRate = 115200;
public static Parity parity = Parity.None;
static int dataBits = 7;
public static StopBits stopBits = StopBits.One;
SerialPort IR_COM = new SerialPort(portName, baudRate, parity,
dataBits, stopBits);
Manchester code
The next step is to initialise the parameters to generate the Manchester code. In RohCodeToManchester()
, the raw data array will be passed to ManchesterCode()
. ManchesterCode()
generates of 1 a change from 1 to 0 and of 0 a change from 0 to 1.
char[] rohData = new char[14] {
'1','1','0','0','0','0','0','0','0','0','0','1','0','0' };
public char[] manchesterData = new char[29];
int index = 0, md = 0;
public void ManchesterCode(char c)
{
if (c == '1')
{
manchesterData[index] = '1';
manchesterData[++index] = '0';
}
if (c == '0')
{
manchesterData[index] = '0';
manchesterData[++index] = '1';
}
index++;
}
public void RohCodeToManchester()
{
for (int i=0; i < rohData.Length; i++)
{
ManchesterCode(rohData[i]);
}
manchesterData[28] = 'e';
}
Generating burst and pause
To guarantee a safe data transmission, a 38 kHz modulated signal is transferred. The signal is then demodulated by the IR-receiver. The signal looks like this:
![Screenshot - two.jpg](data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==)
In the following calculations, you have to consider that the IR-transmitter inverts the signal and the LSB will be transmitted first. The following steps will be explained from this point of view, which is also the view of the program.
A low bit will be burst-modulated and a high bit will be a pause. So every 0 in the Manchester code is burst-modulated and every 1 stands for a pause of 889µs. What we know is that the baud rate must be 115 kBaud and the signal has to be modulated with 38 kHz.
The period length of 26µs accords to the baud rate of 38.4kBaud.
Because 38.4kBaud is not a standard baud rate, we have to bring it up to 115.2kBaud. One bit will take 8.7µs.
![Screenshot - three.jpg](data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==)
Where
- Ts is the period length
- ts is the duration of a bit
- Vs is the baud rate
- f is the frequency
Our desired burst should look like 100100100, including start- and stop-bit. This accords to the presentation bellow. From our starting base of 100100100, we calculate back and consider that the IR-transmitter will invert the signal and send the LSB first.
100100100 | with start- and stop-bit |
0010010 | without start- and stop-bit |
1101101 | inverted |
1011011 | LSB at first |
0x5B | hexadecimal |
![Screenshot - four.jpg](data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==)
The word length is set to 1 start-bit, 7 data-bits and 1 stop-bit. The image on the top shows us that the start- and stop-bit is ideally integrated into the burst. Therefore one burst takes 79.7µs. Because the burst stands for the logical 0 in the data stream, we have to bring the burst up to the RC5 data length of 889µs. For one bit we have to send the burst 0x5B 11 times.
![Screenshot - five.jpg](data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==)
Where
- tb is the duration of a burst
- z is the number of bursts
A logical way to generate the pause would be to send 0x00 11 times, but this is not the solution because with each byte the stop-bit is part of the bit stream. So we had to find a timer that could create a pause of 889µs. There is a counter called QueryPerformanceCounter, which is a function in the coredll.dll on the PDA. Through the QueryPerformanceCounter, we could achieve an exact time measurement. This is because the QueryPerformanceCounter has direct access to the processor clock.
In the following code, we import the coredll.dll and make the initialization for the QueryPerformanceCounter and the BurstOut
function. With DllImport you can import the .dll and after this you can use QueryPerformanceCounter()
as a normal function in your program. The byte-array called BufferBurst include 22 times 0x5B. This is the result of the calculation above, as we had to send 11 times 0x5B for one RC5 bit. Because two logical 0s could appear in rapid succession in the data stream, the BufferBurst has double length.
>
[DllImport("coredll.dll")]
extern static int QueryPerformanceCounter(ref long perfCounter);
[DllImport("coredll.dll")]
extern static int QueryPerformanceFrequency(ref long frequency);
long ctrStart = 0, ctrAkt = 0, ctrEnd = 0;
BurstOut()
passes a variable of the type long
to the QPC()
method. This variable sets the time interval for the QueryPerformanceCounter. In QPC()
we stay in the while-loop until we have reached the specified time.
public void QPC(long data)
{
QueryPerformanceCounter(ref ctrStart);
ctrEnd = ctrStart + data;
while (ctrAkt < ctrEnd)
{
QueryPerformanceCounter(ref ctrAkt);
}
}
The modulation is done in the BurstOut()
function, where the Manchester code array is passed by a while-loop. There we proof the current and following bit. If the bit sequence is 10, a pause of 889µs is made and if the bit sequence is 11, we wait twice as long. When the bit sequence is 01, we send the burst 11 times and by a sequence of 00, 22 times.
public void BurstOut()
{
while (manchesterData[md] != 'e')
{
if (manchesterData[md] == '1' && manchesterData[md + 1] == '0')
{
QPC(319);
}
if (manchesterData[md] == '1' && manchesterData[md + 1] == '1')
{
QPC(766);
md++;
}
if (manchesterData[md] == '0' && manchesterData[md + 1] == '1')
{
IR_COM.Write(bufferBurst, 0, 10);
}
if (manchesterData[md] == '0' && manchesterData[md + 1] == '0')
{
IR_COM.Write(bufferBurst, 0, 22);
md++;
}
if (manchesterData[md] == '0' && manchesterData[md + 1] == 'e')
{
IR_COM.Write(bufferBurst, 0, 10);
}
md++;
}
}
History
- May 10, 2007 - Original version posted
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.