Click here to Skip to main content
6,822,123 members and growing! (17,645 online)
Email Password   helpLost your password?
Platforms, Frameworks & Libraries » Mobile Development » Applications     Intermediate

Pocket PC TV Remote Control

By Nick Deacon

An article describing how to use the IR port on a pocket PC to control a TV.
C++, eVC, Windows, Mobile, Visual-Studio, Dev
Posted:6 May 2003
Views:654,479
Bookmarked:196 times
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
100 votes for this article.
Popularity: 9.33 Rating: 4.66 out of 5
2 votes, 2.0%
1
1 vote, 1.0%
2
8 votes, 8.1%
3
16 votes, 16.2%
4
72 votes, 72.7%
5

Introduction

Have you ever wanted to be able to control your TV, Hi-Fi, or Video using the IR port on your pocket PC? Here's how to do it.

Background

I recently lost the TV remote for my old Sony TV. In itself that was no problem, as I bought a replacement remote which did the job. However, when the TV lost its colour setting, I had a problem as it could only show pictures in black and white, and the replacement remote didn't have the buttons for colour adjustment. I decided to write a program on my old Jornada 525 Pocket PC to send the correct codes to the TV using the IR port.

There appears to be three main protocols for sending IR codes to devices. Sony uses the 'Pulse Coded' method which entails sending a steam of data containing header bits, '1' bits and '0' bits separated by spaces. These bits modulate a carrier of 40KHz, and are of different lengths, 2200 us for the header, 110 us for a 1 bit and 550 us for a 0 bit. The spaces are 550 us of silence. Most Sony equipment uses 12 bits of data, which is separated into 6 bits of address (the device type) and 6 bits of command. So the data looks like this: hxxxxxxyyyyyy where h is the header bit, xxxxxx is the 6 bits of the command (msb first) and yyyyyy is the 6 bits of address. I won't go into any further details on this, as there are many sources on the internet that describe the protocol and list the codes for the different devices. Some newer Sony equipment use 19 bit codes, and I believe that other manufacturers use the same format that I have described. It should also be possible to write similar classes for devices that use 'Space Coded' or 'Shift Coded' protocols.

I have written a class called CIrPulse using Embedded C++, which encapsulates the functionality to control Sony and compatible devices from a Jornada 525 PC running Windows CE 3.0. It should work with other devices and Operating Systems, but you will need to try it!

Using the code

The CIrPulse class exposes a number of functions which makes sending IR codes as easy as possible. On declaring a CIrPulse class you should call FindIrPort() once. This returns a UINT which represents the port number for the IrDA port, which it gets by looking in the registry. This port number is used in all subsequent calls to open the IrDA port for serial comms.

UINT CIrPulse::FindIrPort()
{
    // Look into the registry for the IR port number


    HKEY hKey = NULL;

    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Comm\\IrDA"), 
        0, 0, &hKey) == ERROR_SUCCESS)
    {
        DWORD dwType = 0;
        DWORD dwData = 0;
        DWORD dwSize = sizeof(dwData);

        if (RegQueryValueEx(hKey, _T("Port"), NULL, &dwType, 
            (LPBYTE) &dwData, &dwSize) == ERROR_SUCCESS)
        {
            if (dwType == REG_DWORD && dwSize == sizeof(dwData))
            {
                RegCloseKey(hKey);
                                
                return (UINT) dwData;
            }
        }

        RegCloseKey(hKey);
    }

    return 0;
}

Having got the port number, you can call the Open(UINT) function, passing the port number received from the call to FindIrPort(). This opens the port and sets the serial parameters, returning true if successful. The port is set to 115200 baud, 8 data bits, 2 stop bits and even parity. A discussion of how the carrier is produced, and why I have used these settings appears later in the article.

BOOL CIrPulse::Open(UINT uiPort)
{
    ASSERT(uiPort > 0 && uiPort <= 255);

    Close(); 

    // Open the IRDA port


    CString strPort;
    strPort.Format(_T("COM%d:"), uiPort);
    
    m_irPort = CreateFile((LPCTSTR) strPort, GENERIC_READ | GENERIC_WRITE, 
                          0, NULL, OPEN_EXISTING, 0, NULL);
    if (m_irPort == INVALID_HANDLE_VALUE)
    {
        return FALSE;
    }

    // Set the size of input and output buffers


    VERIFY(SetupComm(m_irPort, 2048, 2048));

    // clear the read and write buffers


    VERIFY(PurgeComm(m_irPort, PURGE_TXABORT | PURGE_RXABORT | 
                               PURGE_TXCLEAR | PURGE_RXCLEAR));

    // Reinitializes all IRDA port settings


    DCB dcb;

    dcb.DCBlength = sizeof(DCB);

    VERIFY(GetCommState(m_irPort, &dcb));

    dcb.BaudRate          = CBR_115200;
    dcb.fBinary           = TRUE;
    dcb.fParity           = TRUE;
    dcb.fOutxCtsFlow      = FALSE;
    dcb.fOutxDsrFlow      = FALSE;
    dcb.fDtrControl       = DTR_CONTROL_DISABLE;
    dcb.fDsrSensitivity   = FALSE;
    dcb.fTXContinueOnXoff = FALSE;
    dcb.fOutX             = FALSE;
    dcb.fInX              = FALSE;
    dcb.fErrorChar        = FALSE;  
    dcb.fNull             = FALSE;
    dcb.fRtsControl       = RTS_CONTROL_DISABLE;
    dcb.fAbortOnError     = FALSE;
    dcb.ByteSize          = 8; 
    dcb.Parity            = EVENPARITY; 
    dcb.StopBits          = TWOSTOPBITS;
 
    
    VERIFY(SetCommState(m_irPort, &dcb));

    // Set the timeouts for all read and write operations


    COMMTIMEOUTS timeouts;

    VERIFY(GetCommTimeouts(m_irPort, &timeouts));

    timeouts.ReadIntervalTimeout         = MAXDWORD;
    timeouts.ReadTotalTimeoutMultiplier  = 0;
    timeouts.ReadTotalTimeoutConstant    = 0;
    timeouts.WriteTotalTimeoutMultiplier = 0;
    timeouts.WriteTotalTimeoutConstant   = 0;

    VERIFY(SetCommTimeouts(m_irPort, &timeouts));

    DWORD dwEvent=EV_TXEMPTY;
    SetCommMask(m_irPort,dwEvent);

    return TRUE;
}

Call the function SetCodeSize(DWORD) to set the number of bits to transmit (eg 12). This can be done at any time, and only needs to be done once. It remains in effect until a subsequent call to change it.

Finally call SendCode(long) passing the actual code to send.

BOOL CIrPulse::SendCode(DWORD lValue)
{

DWORD dwCount;
int i=0;

ASSERT(iDataLength>0);


//purge the transmit buffer 

VERIFY(PurgeComm(m_irPort, PURGE_TXABORT | PURGE_RXABORT | 
                           PURGE_TXCLEAR | PURGE_RXCLEAR));

// send the code 6 times for each button press

for(int x=0;x<6;x++) {
    MakeStream(lValue); //send the code

    dwCount=GetTickCount();
    while(GetTickCount()<dwCount+26) //delay for 26 ms

        i++;

}



return true;
}

Note that this function calls another function MakeStream(long) 6 times, pausing for 26ms between each call. I have found that the code has to be sent a number of times for the receiving device to respond, presumably to prevent spurious activation. The delay of 26ms is necessary for the receiving device to register the code, before the next one appears.

The function MakeStream(long) writes the stream of bytes to the IrPort, and ensures that the correct length of packet is sent depending on whether a start bit, '1' bit or '0' bit is to be sent. The buffer containing the bytes of data (0xdb) is in the form of a ByteArray.

The function Close() naturally enough closes the IrPort after use.

The function works fine on my Jornada, but see the discussion below to see what changes you might have to make.

 BOOL CIrPulse::MakeStream(DWORD lValue) {
    DWORD dwStreamLength;
    

    //make the start pulse

    dwStreamLength=iHPulse/charWidth;
    ASSERT(Write((const char *)bPulseStream.GetData(),
dwStreamLength)==dwStreamLength); // ******************************************** // ***** The Delay before the next pulse goes here // ******************************************** //loop through the bits in the Code sending the pulse for(int i=0;i<iDataLength;i++) { if(lValue & 1) { //make the 1 pulse dwStreamLength=i1Pulse/charWidth; ASSERT(Write((const char *)bPulseStream.GetData(),
dwStreamLength)==dwStreamLength); // ******************************************** // ***** The Delay before the next pulse goes here // ******************************************** } else { //make the 0 pulse dwStreamLength=i0Pulse/charWidth; ASSERT(Write((const char *)bPulseStream.GetData(),
dwStreamLength)==dwStreamLength); // ******************************************** // ***** The Delay before the next pulse goes here // ******************************************** } lValue >>= 1; } return TRUE; }

I have included a simple application, which uses CIrPulse to create a remote control for a Sony TV. This has the basic functions of channel selection, volume control and On/Off.

Points of Interest

Because the CIrPort class uses a serial comms interface to the IR port, a 40KHz carrier has to be generated by sending appropriate characters out of the serial port. Fortunately if we send the character 0xdb at 115200 baud with 8 data bits, 2 stop bits and even parity, this has the effect of producing a 38.4 KHz carrier which is close enough. All my Sony equipment accept this without problems.

The biggest problem is how to achieve the periods of silence which separate each pulse. It is not possible to produce pure silence out of the serial port, as even if you send a 0x0 character, you still get pulses on the IR due to start and stop bits. I experimented with sending different characters on the assumption that if you can send a carrier at frequencies other than 40KHz, this might fool the device into accepting this as a silence. This has the advantage in that you can produce one byteArray containing the data for the entire code, ensuring that the timing is accurate. The results however were not consistent, and I rejected this method in favour of pausing between sending the groups of 0xdb characters out of the serial port. Because the delay needed is in the order of 550 us, there is no way (that I found) of consistently achieving this delay which is independent of processor speed. On my Jornada I didn't need to create a delay at all, as each call to the Write function seemed to take the correct amount of time. Anyway, I am afraid to say that you may have to fiddle about to create a delay that works with your Pocket PC. If any one can work out a fool-proof way of achieving the correct delay for any device, please let me know!

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

About the Author

Nick Deacon


Member

Occupation: Web Developer
Location: United Kingdom United Kingdom

Other popular Mobile Development articles:

  • Writing Your Own GPS Applications: Part 2
    In part two of the series, the author of "GPS.NET" teaches developers how to write GPS applications suitable for the real world by mastering GPS precision concepts. Source code includes a working NMEA interpreter and sample high-precision application in C# and VB.NET.
  • Writing Your Own GPS Applications: Part I
    What is it that GPS applications need to be good enough to use for in-car navigation? Also, how does the process of interpreting GPS data actually work? In this three-part series, I will cover both topics and give you the skills you need to write a commercial-grade GPS application.
  • Learn How to Find GPS Location on Any SmartPhone, and Then Make it Relevant
    A step by step tutorial for getting GPS from any SmartPhone, even without GPS built in, and then making location useful.
  • Windows Mobile, iPhone, Android - Marketplace Comparison
    Detailed comparison between Windows Mobile Marketplace, Apple's iPhone AppStore and Android Market from developer point of view.
  • iPhone UI in Windows Mobile
    It's an interface that works with transparency effects. As a sample I used an interface just like the iPhone one. In this tutorial I am explaining how simple is working with transparency on Windows Mobile.
Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 25 of 197 (Total in Forum: 197) (Refresh)FirstPrevNext
GeneralCOM port name PinmemberAtion4:53 18 May '09  
GeneralSending / Receiving Raw IR Data Using VB.Net Compact Framwork PinmemberCodeMonkey8519:51 10 Mar '09  
AnswerAutomatic Delay Calculation Solved [modified] Pinmembermalcolmst22:22 14 Feb '09  
GeneralRe: Automatic Delay Calculation Solved Pinmembermalcolmst23:03 14 Feb '09  
GeneralOnline Examination PinmemberPervaze0:54 25 Sep '08  
QuestionCAN THE Pocket PC recieve and learn infrared signals from a remote control ? PinmemberSHAMS BOB1123:10 7 Sep '08  
AnswerRe: CAN THE Pocket PC recieve and learn infrared signals from a remote control ? Pinmemberniaussat9:47 27 Nov '08  
GeneralRe: CAN THE Pocket PC recieve and learn infrared signals from a remote control ? PinmemberIkarevska13:08 4 Dec '08  
GeneralRe: CAN THE Pocket PC recieve and learn infrared signals from a remote control ? Pinmembermalcolmst1:29 15 Feb '09  
Question.net Pinmemberprasath6719:21 6 Aug '08  
QuestionMove the code for another TV Pinmemberkrafes1:44 1 Jun '08  
GeneralWireless robotic interfacing PinmemberAlfie Gonzales21:19 30 Mar '08  
GeneralSending pulsed message Pinmemberkardath4:33 31 Jan '08  
Questionabout Pinmembercashmeriprince1:38 5 Nov '07  
General0xdb issue Pinmemberagg.varun9:57 14 Oct '07  
GeneralRe: 0xdb issue PinmemberMulleDK1314:14 10 Mar '08  
Generalfinding serial com port no. on PDA Pinmembereshanichande6:32 16 Aug '07  
GeneralRe: finding serial com port no. on PDA PinmemberImperatorn21:29 21 Apr '08  
Generalexe or cab download ? Pinmemberpcplague4:22 16 Jul '07  
QuestionJ2ME PinmemberDinofx23:56 23 Dec '06  
GeneralHow about Java PinmemberJose Cruz13:04 13 Dec '06  
Generalerror Pinmemberbp_htl23:38 4 Dec '06  
AnswerRe: error Pinmemberfarmyob8:33 16 Jan '07  
Generalwhat to do? Pinmemberbp_htl22:22 4 Dec '06  
Generalany luck Pinmembert_barbz4:20 31 Oct '06  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads.

PermaLink | Privacy | Terms of Use
Last Updated: 6 May 2003
Editor: Chris Maunder
Copyright 2003 by Nick Deacon
Everything else Copyright © CodeProject, 1999-2010
Web18 | Advertise on the Code Project