Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Universal Remote Control with PDA

0.00/5 (No votes)
10 May 2007 1  
Developing a �Universal Remote Control for the PDA�

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

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;

            //get a list of port names

            string[] ports = SerialPort.GetPortNames();

            //display port names

            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:

//initialize the parameters for the SerialPort object

        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.

//initialize the parameters for the function ManchesterCode and 

//RohCodeToManchester

        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')
             {  // 1 -> 10

                 manchesterData[index] = '1';
                 manchesterData[++index] = '0';
             }
             if (c == '0')
             {  // 0 -> 01

                 manchesterData[index] = '0';
                 manchesterData[++index] = '1';
             }
             index++;    
        }

        public void RohCodeToManchester()
        {   
            for (int i=0; i < rohData.Length; i++)
            {   //the array rohData is passed to the function ManchesterCode

                ManchesterCode(rohData[i]);
            }

            //mark the end

            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

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

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

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

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.

>//import the coredll.dll with the functions QueryPerformanceCounter

//and QueryPerformanceFrequency

        [DllImport("coredll.dll")]
        extern static int QueryPerformanceCounter(ref long perfCounter);
        [DllImport("coredll.dll")]
        extern static int QueryPerformanceFrequency(ref long frequency);

        //initialize the parameters for the QueryPerformanceCounter

        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)
        {   //start the counter

            QueryPerformanceCounter(ref ctrStart);
            
            //specified the end time

            ctrEnd = ctrStart + data;

            while (ctrAkt < ctrEnd)
            {   //wait until the counter reaches the end time

                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')
           {   
               //pause of 889�s

               if (manchesterData[md] == '1' && manchesterData[md + 1] == '0')
               {
                   QPC(319);
               }

               //pause of 1,778ms

               if (manchesterData[md] == '1' && manchesterData[md + 1] == '1')
               {
                   QPC(766);
                   md++;
               }

               //burst of 889�s

               if (manchesterData[md] == '0' && manchesterData[md + 1] == '1')
               {
                   IR_COM.Write(bufferBurst, 0, 10);
               }

               //pause of 1,778ms

               if (manchesterData[md] == '0' && manchesterData[md + 1] == '0')
               {
                   IR_COM.Write(bufferBurst, 0, 22);
                   md++;
               }

               //the end

               if (manchesterData[md] == '0' && manchesterData[md + 1] == 'e')
               {
                   IR_COM.Write(bufferBurst, 0, 10);
               }

               md++;
           }
       } 

History

  • May 10, 2007 - Original version posted

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here