News | Articles | Libraries | Developer Tools | Books | Forum Links | Search   
Sections:
 

IRDA

By Daniel Strigl, August 26, 2003.
Print version

Introduction

This article describes working with IrDA in details. You will find working client/server samples and a lot of information about IrDA protocol and how to program for this protocol.

IrDA data transfer between Windows devices

Can you imagine for a while a cosy television evening without an infrared TV remote control? Constant rising, in order to switch between the innumerable channels, increasing or decreasing volume, etc... Not very comfortably, is it?

However, infrared remote controls are not the only devices of this kind in our daily life: Ever more devices with infrared support - Notebooks or Handies come into market bringing along the new infrared standard. By means of IrDA, in the meantime, data transfer between two Pocket PC devices became standard. But what's the use of the infrared interface, if it is not supported by the software?

The following article is going to demonstrate, how simply you can implement the infrared support into your Windows applications. I provide a small Server/Client example, with the help of which you can send messages from your Pocket PC application to a desktop Windows application. You can use this example as a base for your own applications, which will provide ability to exchange data between different devices right from your application.

This Server/Client application consists of two parts: a server application that waits for messages, and a client application that sends messages to the server. Both applications are quite simple and work with data transfer between Windows devices by means of IrDA, which corresponds to the actual topic of the article.

Software requirements: eMbedded Visual Tools 3.0; Visual Studio 6.0 or higher. Both products are available on the Microsoft homepage. eMbedded Visual Tools 3.0, which is necessary for the client application that runs on Pocket PC, is available free of charge. As for the server application that runs under Windows, you will need Microsoft Visual studio 6.0 or higher.

First, a couple of words about "data transfer via IrDA".

What is IrDA?

IrDA stands for Infrared Data Association - an organization of over 150 companies that provide common standard for infrared communication between different devices. The IrDA standard contains all fundamental specifications for wireless data communication via infrared. Thus the greatest possible compatibility between devices of different manufacturers can be reached. By this moment the IrDA has published a set of standards that define the physical and software-specific layers necessary for smooth communication between two infrared devices. In the meantime the infrared interface of many devices is also called the IrDA interface. The structure of this IrDA stack is illustrated below (see Fig. 1).


Figure 1. IrDA stack

The single layers of this IrDA stack describe the real structure very briefly and simply, since the detailed description of all these layers and protocols would break the framework of this article.

Physical Layer: specifies the optical characteristics, data coding and framework size for different speeds.

IrLAP (Link Access Protocol): provides a reliable connection between two infrared devices of different manufactures.

IrLMP (Link Management Protocol): makes multiplex services available over the IrLAP, i.e. it makes possibile to maintain several parallel connections with different applications.

IAS (Information Access Service): enables the exchange of the information about the existing equipment services.

Tiny TP: enables the flow control for a channel

IrOBEX: describes the simple transfer of files and other data objects between two devices.

IrCOMM: emulation of the serial and parallel ports; the existing applications that use serial or parallel communication get allowed to use infrared without software changes.

IrLAN (Local Area Network): this protocol describes the local area network for wireless connection via infrared.

Detailed knowledge about the structure of these layers is necessary if you want to understand the IrDA programming more precisely. You can find more information on the official homepage of the IrDA (http://www.irda.org).

IrDA programming under Windows

In Windows the infrared interface can be fully described just by means of the IrSock extension to the existing WinSock2 Standards. With the IrSock extension it acts strictly speaking around no extension of the existing WinSock2 Standard, but only around the support for TinyTP/IrLMP as some further transportation protocols, which can be headed for like TCP/IP over Sockets. They as programmers work thus only with the conventional Socket functions, as it admits for example from TCP/IP programming be might. The following illustration (See Fig. 2) is to demonstrate how your application accesses the IrDA interface over the WinSock interface.


Figure 2. Access to the IrDA interface via WinSock

However, there are some little differences in Socket programming for the infrared interface. Some functions must be adapted because of special characteristics of infrared communication (like missing fixed addresses, short-time existing connections, etc.). Some Socket functions are therefore not supported under infrared communication, e.g. the "gethostbyname" function. In addition there are some restrictions and changes peculiar to infrared communication compared to conventional Socket programming, like for example with TCP/IP. IrSockets can work only as Stream Sockets, i.e. they do not act as datagram Sockets.

The addressing of the IrDA devices was also adapted specially for the infrared connection. Instead of the TCP port and the IP number used in TCP/IP programming there come a so-called LSAP selector and a service name used for the infrared connection. The LSAP selector can be compared with a port number under TCP/IP and corresponds to a number between 0 and 127. If one wants to address the IrDA equipment, he can select either a desired LSAP Selector or indicate a service name, whereby a free LSAP Selector is selected automatically.

With the simple implementation of the infrared connection it is enough to know the following: each IrDA server application that maintains the infrared connection, enables such an LSAP selector and a service name (as in our example); each Client application uses this kind of the addressing to build up a connection to the server application. All well-known Socket functions (like "send" or "recv") can be used in their usual way accordingly to these restrictions and changes.

This kind of addressing during the infrared connection allows to run several applications that use the same infrared port, as shown in the following illustration (see Fig. 3).


Figure 3. Addressing infrared connection

The server application

As mentioned above, you need Visual Studio 6.0 or higher for the server application. These tools allow to build a dialog-based application, using MFC (Microsoft Foundation Classes) Frameworks.

The user interface of the server dialog (see Fig. 4) is very simple. As the most important element beside the "Exit" button there is a text field that contains a message received from the Client application.


Figure 4. Screenshot of the server application with a message received

In order to be able to use the Socket functionality in your project, you must link the "Ws2_32.lib" library to your project and merge the "af_irda.h" include file that goes along with the library. To do so, first open the "Project Settings" dialog by selecting Project -> Settings from the main menu. Then select the Links tab sheet and from the Category manu select General. Then type "Ws2_32.lib" in the "object/library modules" input field and click Ok. The following illustration (see Fig. 5) demonstrates this process.


Pic 5. Screenshot of the Visual C++ project settings

Now you can use a Socket function, but first you must initialize the WinSock DLL, which operates by means of the "WSAStartup" function. This function presents you a version of Windows Sockets that you need. Here we have a function for version 1.1. The function provides you with a detailed description of Windows Sockets implementation in the "WSADATA" structure. When the program ends you must call the complementary function "WSACleanup" to "WSAStartup", which releases all open resources and then logs out.

The Socket functions work normally in blocking mode, in the backround task, waiting for or receiving messages produced by the client. For example the "recv" function returns only if data was first read by the Socket, that's why this mode is called blocking here. Of course there are also Socket functions working by means of the "WSAAsyncSelect" functions (this is not available under Windows CE) and "ioctlsocket" functions that allow to switch into the non-blocking mode, however we must demostrate the way the data reaches the background task. The functioning of message sending in the non-blocking mode is described later, on the basis the Client application.

As it is already mentioned, as the program starts, the WinSock DLL is initialized within the "OnInitDialog" function and a background task "WaitForMessage" starts, too.

WSADATA wsaData; VERIFY(WSAStartup(MAKEWORD(1,1), &wsaData) == 0); // Start the thread for the IrDA messages m_pWaitForMessageThread = AfxBeginThread(WaitForMessage, this); ASSERT_VALID(m_pWaitForMessageThread);

Next we want to look at the background task, which is responsible for Client messages receiving.

Within the "WaitForMessage" function (this very background task) now the Socket is produced, which waits for detailed Client connections. This Socket is produced by a "socket" function. Here, the first parameter of the "socket" function indicates the address family, which corresponds to the "AF_IRDA" constant on the IrDA connection. The second parameter indicates the connection type, whereby the only "SOCK_STREAM" constant is applicable here, since (as it is already mentioned) only Stream Sockets are supported with the IrDA connection.

Subsequently, this Socket is connected with our "MySampleIrDAService" service name by means of the "bind" command and shifted into the listening state by the "listen" instruction, waiting for a connection. The "listen" instruction expects the length of the list of the lining up connections with the Socket as the second parameter, i.e. maximally this number of connections can be set using the "accept" command. All other call attempts are rejected thereby by the server. If you wish to permit only a single Client and expect for only one Client connection, set this value to 1.

// Create the server socket m_serverSocket = socket(AF_IRDA, SOCK_STREAM, 0); if (m_serverSocket == INVALID_SOCKET) { return; } // Associate the server service with the socket SOCKADDR_IRDA serverSocketAddr = { AF_IRDA, 0, 0, 0, 0, "MySampleIrDAService" }; if (bind(m_serverSocket, reinterpret_cast<struct sockaddr*>(&serverSocketAddr), sizeof(serverSocketAddr)) == SOCKET_ERROR) { closesocket(m_serverSocket); return; } // Listen for an incoming connection if (listen(m_serverSocket, 1) == SOCKET_ERROR) { closesocket(m_serverSocket); return; }

A Client tries to announce itself at the server and manages to do so by means of the "accept" function. If a connection is successful, the "accept" function provides a Socket, which describes this Server-Client connection.

Now before the actual message is read from the Client, we determine the length of the message the Client provides. This is very useful, since we can know how much memory to reserve and how many bytes to read while receiving the message. In addition to the message the Client first sends us the length of the message in form of a 32 bit number and afterwards the message itself. When determining the message length it must be mentioned that this 32 bit number has also the correct "byte order". In order to guarantee that the 32 bit number on both systems is interpreted in the same way, the Socket functions "htonl" and "ntohl" are used. These two functions convert a 32 bit number into a so-called "TCP/IP network byte order" and back again. The Client converts here the length of the message into the "TCP/IP network byte order" and afterwards sends this converted number to the server. The server at its turn converts the received number from the "TCP/IP network byte order" into the "host byte order" (byte sequence of the target system), whereby it is guaranteed that both systems interpret this number the same way.

m_clientSocket = accept(m_serverSocket, 0, 0); if (m_bWaitForMessage == FALSE && m_clientSocket == INVALID_SOCKET) { break; } else if (m_clientSocket == INVALID_SOCKET) { continue; } // Receive the size of the message u_long ulSize = 0; int nResult = recv(m_clientSocket, reinterpret_cast<char*>(&ulSize), sizeof(ulSize), 0); if (m_bWaitForMessage == FALSE && nResult != sizeof(ulSize)) { break; } else if (nResult != sizeof(ulSize)) { closesocket(m_clientSocket); continue; } // Convert network byte order to host byte order ulSize = ntohl(ulSize); if (ulSize > 0) // Receive the message { LPSTR pszMessage = new CHAR[ulSize + 1]; nResult = recv(m_clientSocket, pszMessage, ulSize, 0); if (m_bWaitForMessage == FALSE && nResult != static_cast<int>(ulSize)) { delete [] pszMessage; break; } else if (nResult != static_cast<int>(ulSize)) { delete [] pszMessage; closesocket(m_clientSocket); continue; } pszMessage[nResult] = '\0'; SendMessage(MY_WM_MSG_RECEIVED, reinterpret_cast<WPARAM>(pszMessage)); delete [] pszMessage; } // Close the client socket closesocket(m_clientSocket);

So, now the memory for the actual message is reserved and the server tries to read the message in. As the reading is complete, this message is sent to the actual GUI task, which opens a dialog with a text field containing this message and provides a beep, in order to inform the user about the arrival of a message. Sending a message from the background task to GUI task is performed by means of the user-defined "MY_WM_MSG_RECEIVED" Windows message, which was defined before by the "RegisterWindowMessage" API function and hung up afterwards into the communication network of the MFC dialog.

If a message was read and transfered to the text field of the dialogue, the Socket connector with the client can be closed again by means of the "closesocket" function. Subsequently, the server keeps waiting for another connection and another message from the cClient.

///////////////////////////////////////////////////////////////////////////// // Message table const UINT CIrDAServerDlg::MY_WM_MSG_RECEIVED = RegisterWindowMessage(_T("MY_WM_MSG_RECEIVED-{A0422CA6-D03D-466c-A1DC- 0DF8D4E89386}")); BEGIN_MESSAGE_MAP(CIrDAServerDlg, CDialog) //{{AFX_MSG_MAP(CIrDAServerDlg) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_WM_DESTROY() //}}AFX_MSG_MAP ON_REGISTERED_MESSAGE(MY_WM_MSG_RECEIVED, OnMyMessage) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // User-defined message handler LRESULT CIrDAServerDlg::OnMyMessage(WPARAM wParam, LPARAM lParam) { LPSTR pszMessage = reinterpret_cast<LPSTR>(wParam); ASSERT(AfxIsValidString(pszMessage)); // Display the received message m_strMessage = pszMessage; UpdateData(FALSE); MessageBeep(MB_ICONASTERISK); return 0L; }

Here are a few code lines that describe the "clearing up work" of the server application.

When the server application is closed by pressing the "exit" button, the window is destroyed and the "OnDestroy" function is called. Within this function we can describe our "clearing up work", i.e. closing all open Socket connectors, terminating the background task and waiting until it is terminated.

First of all we must examine if we have something to clear up at all. For these purposes the variable "m_bWaitForMessage" is used. If its value is "FALSE", then there are no open Socket connectors, and the background task is already terminated, too.

If the "m_bWaitForMessage" variable's value is "TRUE", then there is a background task and a Socket connector still open. They must be closed. First the "m_bAutoDelete" flag of the background task must be set to "FALSE" in order to be able to wait for its completion. Since the background task does not delete itself, it must be deleted by the "delete" function by hand. Subsequently, the "m_bWaitForMessage" variable, which is queried within the background task, is set to "FALSE", so that the background task is also terminated. The opened Socket is closed by the "closesocket" function, while all active writing and reading accesses are broken off by the "shutdown" function. At the conclusion the background task that has been waiting for closing by means of "WaitForSingleObject" as it is already mentioned is finally deleted. Regardless of the "m_bWaitForMessage" variable state the "WSAStartup" will call a complementary "WSACleanup" function, which releases all open resources and afterwards the WinSock DLL logs out itself.

// TODO: Code fur the Behandlungsroutine fur Nachrichten hier einfugen if (m_bWaitForMessage == TRUE) { m_pWaitForMessageThread->m_bAutoDelete = FALSE; m_bWaitForMessage = FALSE; // Shutdown and close the client socket if (m_clientSocket != INVALID_SOCKET) { shutdown(m_clientSocket, SD_BOTH); closesocket(m_clientSocket); } // Shutdown and close the server socket shutdown(m_serverSocket, SD_BOTH); closesocket(m_serverSocket); // Wait until the thread is terminated WaitForSingleObject(m_pWaitForMessageThread->m_hThread, INFINITE); delete m_pWaitForMessageThread; } WSACleanup();

The server application is therefore terminated and we can turn to the Client application, the one that works on a Pocket PC.

The Client application

As the Server application as the Client application are dialog-based applications, coding of which is provided by MFC Frameworks. However you need eMbedded Visual C++ 3.0, which you will use along with eMbedded Visual Tools 3.0 for creating this application for Pocket PC. You can download this package free of charge from the Microsoft homepage.

The user interface of the Client application (see Fig. 6) is quite simple. There is only a text input field for writing messages and a button for sending them. When the "Send message" button is pressed, the text from the input field is taken and send to the server as a single message.


Figure 6. Screenshot of the Client application

In order to be able to use the Socket functionality as for the Client application, as for the Server application link the WinSock Library to your project. However the library name in Windows CE is "Winsock.lib", not "Ws2_32.lib".

The entire Socket functionality for the infrared connection with the Client application is packed into two classes named "CWinSock" and "CIrDASocket" and found in "IrDASocket.h" and "IrDASocket.cpp" files. These two classes cover the entire Socket functionality for infrared interface access and reduce thus the programming costs for the Client application. These two classes are designed in a way to be simply re-used in other projects.

Now if the "Send" button of the dialog is pressed, then first of all the text in the input field is taken and handed over to the transmission routine, which tries to send this message to the server application.

// TODO: Add your control notification handler code here UpdateData(); // Send message to server UINT uiResult = Send(m_strMessage); if (uiResult) { // Display an error message CString strCaption, strText; strCaption.LoadString(IDS_ERROR_MESSAGE_SEND); strText.LoadString(uiResult); MessageBox(strText, strCaption, MB_ICONEXCLAMATION | MB_OK); }

During the transmission procedure the message is examined for its length, i.e. it is checked whether there is anything at all to be transferred. If no text was entered into the input field, then the message has a zero length, and the transmission procedure breaks off with an error.

The next instance of the "CWinSock" class is added to guarantee that the WinSock layer is properly initialized. The "CWinSock" class calls the "WSAStartup" function within its constructor and the "WSACleanup" complementary function within its destruktor. This will guarantee that the WinSock Layer was properly initialized and then properly cleared up on completion of the transmission.

The next two lines of the code produce an instance of the "CIrDASocket" class and try to open a Socket by means of the "open" function.

ASSERT(AfxIsValidString(lpszMessage)); // Check the message length if (_tcslen(lpszMessage) <= 0) return IDS_ERROR_NO_MESSAGE; CWaitCursor wc; USES_CONVERSION; // Make sure the socket layer is active CWinSock winSock; // Open an IrDA socket CIrDASocket sd; if (!sd.Open()) return IDS_ERROR_IRDA_NO_SOCKET;

If you look at the "open" function of the "CIrDASocket" class more closely, you can recognize, how the opened Socket connector, produced by the "socket" command, is switched into the non-blocking mode by the "ioctlsocket" command. For a developer it means that the WinSock functions do not perform blocking themselves, but it is the "WaitForOperation" function of the "CIrDASocket" class that waits for the completion of an instruction, otherwise after a certain time interval the whole thing breaks off. Most functions of the "CIrDASocket" class expect a so-called TIMEOUT time, a parameter that indicates, how long is to be waited for the completion of an instruction. If this time is exceeded, then the instruction is broken off with an error.

So you can set a TIMEOUT time period, which is waited by the "Receive" function, until all data is received. If the time passes before all data arrives, the function breaks off with an error, otherwise it returns with success.

For some time (in our example it is approx. 10 seconds) the application looks for a compatible infrared device in the environment. If no equipment with an activated infrared port in the closest environment is found, then the transmission procedure breaks off with an error. The functionality to look for compatible infrared equipment in the proximity to a Pocket PC, is predefined in the "EnumDevices" method. This procedure lists, as it can be understtod from its name, all available infrared devices in direct proximity to a Pocket PC. The "EnumDevices" function, in its turn, calls the "getsockopt" WinSock function with the "IRLMP_ENUMDEVICES" parameter, that returns the list of the found infrared devices to the buffer.

If equipment was found, then its name is determined and a user is asked whether to send the message to the found device with the determined name or not (see Fig. 7).

// Try several times to find a device DEVICELIST deviceList = { 0 }; for (int nTry = 0; nTry < (LONG_TIMEOUT / 1000); nTry++) { // Try to find a device if (!sd.EnumDevices(&deviceList, sizeof(DEVICELIST))) return IDS_ERROR_IRDA_CANNOT_ENUMDEVS; // Abort if we have found a device if (deviceList.numDevice > 0) break; // Wait a second ::Sleep(1000); } // Check if all retries have been finished if (deviceList.numDevice == 0) return IDS_ERROR_NO_DEVICES_FOUND; // Obtain the devicename (ANSI) LPCTSTR lpszDeviceName = A2CT(deviceList.Device[0].irdaDeviceName); // Ask the user if recepient is okay CString strCaption, strText; strCaption.LoadString(IDS_MESSAGE_SEND); strText.Format(IDS_MESSAGE_SENDTOHOST, lpszDeviceName); if (MessageBox(strText, strCaption, MB_ICONQUESTION | MB_YESNO) != IDYES) return 0;

If a user accepts, then the program tries to establish a connection with the server on the second infrared equipment. Here comes again the so-called service name, "MySampleIrDAService" in our example. If no server application on the second infrared device is found, which is connected with the service name "MySampleIrDAService", then no connection is established and the "Connect" function of the "CIrDASocket" class returns with an error, whereby the whole transmission procedure is terminated with an error.


Figure 7. Asking for user's confirmation

You can also set the TIMEOUT time here for the "Connect" function to wait for a connection. If this time expires before a connection comes the transmission breaks off.

If a connection installs, the next step is the determination of a message length. The "htonl" Socket functions converts this value into the so-called "TCP/IP network byte order". The necessity of this converting was described before.

Finally, this value, followed by the actual message, is sent to the server application. Since character strings under Windows CE are in UNICODE format, the message before sending is converted into an ANSI string with the help of the "T2CA" ATL macros.

//Restore the WAIT cursor wc.Restore(); //A DEVICES has been found, so try tons connect if (!sd.Connect(IRDA_SERVICENAME, deviceList.Device[0].irdaDeviceID, LONG_TIMEOUT)) return IDS_ERROR_IRDA_CANNOT_CONNECT; //send the length OF the message ton the servers u_long ulLength = htonl(_tcslen(lpszMessage)); if (!sd.Send(ulLength, SHORT_TIMEOUT)) return IDS_ERROR_IRDA_CANNOT_SEND; //send the message ton the servers if (!sd.Send((LPCVOID) T2CA(lpszMessage), _ tcslen(lpszMessage), SHORT_TIMEOUT)) return IDS_ERROR_IRDA_CANNOT_SEND; //Return successful return 0;

The Socket connector is closed automatically in the destructor of the "CIrDASocket" class when leaving the transmission procedure.

A little hint at the end of this article: If you want to show or hide the Soft Input Panel (SIP) on program start or end, you should call the "SHSipPreference" API function with different parameters.

To show the SIP on program start, insert the following line within the "OnInitDialog" function of your dialog:

SHSipPreference(m_hWnd, SIP_UP);

To hide the SIP with the program end insert the following line into the "OnDestroy" method of your dialog:

SHSipPreference(m_hWnd, SIP_DOWN);

Sample

Abount the author

Daniel Strigl works as a software developer in an Austrian company. In his spare time he develops Windows software and spends some time developing Pocket PC applications. Find them on his homepage:
http://www.hh-system.com/danielstrigl.

Related resources:

Discuss

Discuss this article. Here you can write your comments and read comments of other developers.
Rate this article:     Poor Excellent    
 12345 
© 2001-2005 Pocket PC Developer Network, a division of Spb Software House