Click here to Skip to main content
15,890,512 members
Articles / Mobile Apps / Windows Mobile

Infrared Communication with your Mobile Phone

Rate me:
Please Sign up or sign in to vote.
4.89/5 (88 votes)
17 Dec 2003CPOL7 min read 1.2M   3.6K   218   274
Learn how to make your Pocket PC speak with your mobile phone.

Sample Image

Introduction

My English is not very well and so I hope everyone can understand this article. Still if questions should exist, I will gladly answer those. And if someone should find some errors, please send me the correct version of the wrong text - thanks for the help in improving my English knowledge ;-).

For data exchange, most of the new mobile phones and Pocket PCs possess an infrared (IRDA) port. So, why not use this infrared connection to communicate between these two devices?

This simple Pocket PC 2002 application shows how to communicate with your mobile phone, it will read the manufacturer, the model identification and the Phone Book of your mobile phone to your Pocket PC.

I have tried this Pocket PC application (running on my HP jornada 565) only with a Nokia 5210, my new Siemens S55 and a very old Siemens S25 mobile phone. So, I hope the application will also work with other mobile phones.

How can I speak with the mobile phone?

To speak with your mobile phone you have to use some special commands, called AT commands. You have to send these commands via the IRDA port of your Pocket PC to the mobile phone and the mobile phone will respond to you. If the mobile phone supports the received AT command, it will send you a valid response, otherwise it will send you an error response.

The AT commands have a general structure, for example take a look at the figure below:

AT command

<CR> ... Carriage return

This command is to request the manufacturer identification of the mobile phone. For example a Nokia 5210 will send you the following response:

AT response

<CR> ... Carriage return
<LF> ... Line feed

If the mobile phone doesn't support the received AT command, it will send you an error response:

AT error

<CR> ... Carriage return
<LF> ... Line feed

You can find some detailed documentation about the AT commands at the following sites:

The Code

The application is a simple dialog based MFC application including some simple API calls.

For the infrared communication, I have written a simple class called CIrdaPort. The class supports some functions to open and close the IRDA port and some functions for the read and write operations.

UML class diagram

To open the IRDA port use the following function:

C++
BOOL CIrdaPort::Open(UINT uiPort);

This function will open the IRDA port, the uiPort parameter defines the index of the IRDA port (1 for COM1, 2 for COM2, 3 for COM3). The return value TRUE indicates success, the value FALSE failure.

You can use the following function to take a look in the registry for the index of the IRDA port:

C++
UINT CIrdaPort::FindPortIndex();

If the function finds some index of the IRDA port in the registry this index will be returned, otherwise the return value is 0.

If you want to write some characters to the IRDA port you can use the following function:

C++
BOOL CIrdaPort::Send(const CString& strSend) const;

This function will send the string strSend to the mobile phone. The return value FALSE indicates failure, the value TRUE success.

To wait for a response you can use the following function:

C++
BOOL CIrdaPort::WaitForResponse(CString& strResponse, DWORD dwTimeout) const;

This function waits for a response strResponse of the mobile phone or returns with FALSE if the timeout dwTimeout occurred.

If you are finished with all read and write operations you have to close the port with the following function:

C++
void CIrdaPort::Close();

This function will close the IRDA port.

When you click on the "Read ..." button of the dialog, the application tries to connect to the mobile phone and read some information about the phone, like the manufacturer and the model identification (for example: Nokia Mobile Phones, Nokia 5210 or SIEMENS, S55). After that it tries to read all Phone Book entries of your mobile phone and display it in a list box on the dialog.

Don't forget to activate the infrared port of your mobile phone!

Before you can test the communication between your Pocket PC and your mobile phone, you have to activate the infrared (IRDA) port of your mobile phone. Most of the mobile phones have their own menu item to activate the infrared port. For example, for a Nokia mobile phone you have to choose Menu -> Infrared. After activating the infrared port, you will see the following symbol IR symbol at the top left corner on the display of your Nokia mobile phone and you will know that the infrared port is enabled.

Some mobile phones, like the Nokia phones, disable the infrared port after some minutes if there is no data exchange on the infrared connection. So, before you start a communication take a look on your mobile phone and reactivate the infrared port if necessary. Look also that there is a straight connection between the infrared sensor of your Pocket PC and your mobile phone and that there is nothing between the two devices which can block the communication.

Bluetooth connection between Pocket PC and mobile phone

After I published this article, I received many questions about how to connect your Pocket PC with your mobile phone via Bluetooth. I couldn't try it because I have no Bluetooth mobile phone or Bluetooth Pocket PC at the moment, but João Paulo Figueira tried it out and found the answer:

Change the name of the communications port from COMx: to BTC1: and comment out the following line:

VERIFY(SetupComm(m_hPort, 2048, 2048));

It's also possible to increase the port speed up to 115200.

João Paulo Figueira also found the registry key that holds the port's name, but he didn't know if this is standard. So, it would be great if you have some information about that and send it to me or João Paulo Figueira.

The registry key that he found that holds the port's name is HKEY_LOCAL_MACHINE\ExtModems\BtDialupModem.

Related links

If you have some problems with the sample application or you need some more information about RAW Infrared Communication on Pocket PC, take a look at Ruud en Frida Vermeij's homepage at http://vermeij.filternet.nl/programmer/rawir.html. In this site you can find many useful tips and tricks about the RAW Infrared Communication on Pocket PC.

If you are interested in Infrared Winsock Communication (IrSock) on Pocket PC and Desktop PC devices, take a look at my IRDA article at the Pocket PC Developer Network. This article is going to demonstrate, how simply you can implement the infrared support into your Windows applications. It provides 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.

IrdaMobile under the normal Windows operating system (Windows 2000 or XP)

Since I have published this article in January this year, one of the most asked questions in the forum and the mails was, how the implement the sample application under the normal Windows operating system, like Windows 2000 or XP.

Sample Image

So, now I took the time to implement the sample application of this article under the normal Windows operating system, like Windows 2000 or XP.

Now, you can download a working executable of the IrdaMobile application for the normal Windows operating system and the hole source code at the top of this article.

The only thing you need to run the sample application under your normal Windows operating system (Windows 2000 or XP) is a virtual COM port driver like IrCOMM2k (Virtual Infrared COM Port for Windows 2000/XP). IrCOMM2k is a driver for Windows 2000 and XP, it emulates a serial port which can be used to exchange data via IrDA with mobile devices. For more information about this topic take a look at the "Technical Details" of this driver at http://www.ircomm2k.de/.

Bugs and comments

If you have any comments or find some bugs, I would love to hear about it and make the code better.

Release history

  • 18.12.2003

    Added the IrdaMobile sample for the normal Windows operating system (Windows 2000 or XP).

  • 31.10.2003

    Some changes in the CIrdaPort::FindPortIndex() function.

  • 14.09.2003

    Added the "Bluetooth Connection between Pocket PC and Mobile Phone" part to the article (thanks to João Paulo Figueira).

  • 09.09.2003

    Correction of the wrong implementation of the CIrdaPort::FindPortIndex() function (thanks to Ruud en Frida Vermeij).

  • 24.01.2003

    Removed the APS file from the source code archive (thanks to Johann Gerell).

  • 22.01.2003

    First release in CodeProject.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Austria Austria
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralRead Function will block the application Pin
chencen20003-Sep-04 11:01
chencen20003-Sep-04 11:01 
GeneralRe: Read Function will block the application Pin
Daniel Strigl12-Sep-04 19:56
Daniel Strigl12-Sep-04 19:56 
Generalbluetooth not work on R520m and t68i Pin
riki_risnandar2-Sep-04 5:05
riki_risnandar2-Sep-04 5:05 
GeneralRe: bluetooth not work on R520m and t68i Pin
Daniel Strigl2-Sep-04 19:25
Daniel Strigl2-Sep-04 19:25 
GeneralRe: bluetooth not work on R520m and t68i Pin
riki_risnandar5-Sep-04 3:06
riki_risnandar5-Sep-04 3:06 
GeneralUploading files from a PC to a Mobile Handset Pin
joshibv23-Aug-04 0:16
joshibv23-Aug-04 0:16 
GeneralRe: Uploading files from a PC to a Mobile Handset Pin
Daniel Strigl2-Sep-04 19:20
Daniel Strigl2-Sep-04 19:20 
GeneralRe: Uploading files from a PC to a Mobile Handset Pin
Daniel Strigl2-Sep-04 19:22
Daniel Strigl2-Sep-04 19:22 
Here is also some code I have written two years ago to send files from my Pocket PC to my Desktop PC and vise versa:

// IrObexDlg.h : header file
//
#if !defined(AFX_IROBEXDLG_H__7B1476C2_4204_4279_856C_E5F8CEF4ED2D__INCLUDED_)
#define AFX_IROBEXDLG_H__7B1476C2_4204_4279_856C_E5F8CEF4ED2D__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

#include <af_irda.h> // need for IrSock functions
#include "MyProgressCtrl.h"

/////////////////////////////////////////////////////////////////////////////
// CIrObexDlg dialog

class CIrObexDlg : public CDialog
{
// Construction
public:
CIrObexDlg(IStream* pStream, const CString& strName,
const int nPacketSize = 1024, CWnd* pParent = NULL);

// Dialog Data
//{{AFX_DATA(CIrObexDlg)
enum { IDD = IDD_IROBEX };
CMyProgressCtrl m_prgProgress;
//}}AFX_DATA

// Class enumerations
public:
enum
{
EOk = 0,
EAbort,
EInvalidFileSize,
ENetwork,
ENoDeviceFound,
EServiceNotSupported,
EInvalidPacketSize,
EConnectFailed,
EObexConnectFailed,
EObexPutFailed,
EObexDisconnectFailed
};

// Attributes
public:
static const int MAX_NAME_LENGTH; // maximale Länge des Dateinamen
static const int MIN_PACKET_SIZE; // minimale Packetlänge
static const int MAX_PACKET_SIZE; // maximale Packetlänge

// Operations
public:
static const CString GetErrorMessage(int nError);

// Internal attributes
private:
static const UINT MY_WM_TEXT_STATUS;
static const UINT MY_WM_PROG_STATUS;
static const UINT MY_WM_END;
CBrush m_brBackground;
volatile SOCKET m_socket;
volatile bool m_bAbort;
CWinThread* m_pSendDataThread;
const int m_nPacketSize;
int m_nDataSize;
IStream* m_pStream;
const CString m_strName;

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CIrObexDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:
static UINT SendData(LPVOID pParam);
void SendData();
bool Send(const char* pData, int nLength);
bool Receive(char* pData, int nLength);
bool RecvOpt(int nLength);
bool Connect();
bool Disconnect();
bool Put();

// Generated message map functions
//{{AFX_MSG(CIrObexDlg)
virtual BOOL OnInitDialog();
afx_msg void OnClose();
afx_msg void OnDestroy();
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized);
afx_msg void OnSettingChange(UINT uFlags, LPCTSTR lpszSection);
//}}AFX_MSG
afx_msg LRESULT OnTextStatus(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnProgStatus(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnEnd(WPARAM wParam, LPARAM lParam);
afx_msg void OnRecognizeGesture(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
DECLARE_MESSAGE_MAP()
};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_IROBEXDLG_H__7B1476C2_4204_4279_856C_E5F8CEF4ED2D__INCLUDED_)

// IrObexDlg.cpp : implementation file
//
#include "stdafx.h"
#include "resource.h"
#include "IrObexDlg.h"
#include "LogFile.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CIrObexDlg defines

#ifndef SD_BOTH
#define SD_BOTH 0x02 // need for "shutdown" socket function
#endif // SD_BOTH

/////////////////////////////////////////////////////////////////////////////
// CIrObexDlg constants and static variables

static const BYTE OBEX_CONNECT = 0x80;
static const BYTE OBEX_DISCONNECT = 0x81;
static const BYTE OBEX_VERSION = 0x10;
static const BYTE OBEX_CONNECT_FLAGS = 0x00;
static const BYTE OBEX_SUCCESS = 0xA0;
static const BYTE OBEX_CONTINUE = 0x90;
static const BYTE OBEX_PUT = 0x02;
static const BYTE OBEX_PUT_FINAL = 0x82;
static const BYTE OBEX_NAME = 0x01;
static const BYTE OBEX_LENGTH = 0xC3;
static const BYTE OBEX_BODY = 0x48;
static const BYTE OBEX_END_OF_BODY = 0x49;

static const int MAX_CLIENT_PACKET_SIZE = 1024;
static const DWORD TIMEOUT = 10000; // 10 seconds timeout

enum
{
ESocket = 0,
EFindDevice,
EVerifyService,
EConnect,
EObexConnect,
EObexPut,
EObexDisconnect,
EReady
};

static LPCTSTR ERROR_MESSAGES[] =
{
_T("Übertragung erfolgreich beendet."), // EOk
_T("Abbruch durch Benutzer."), // EAbort
_T("Datei überschreitet die maximale Grösse."), // EInvalidFileSize
_T("Undefinierter Netzwerkfehler."), // ENetwork
_T("Kein kompatibles Gerät gefunden."), // ENoDeviceFound
_T("OBEX Service wird nicht unterstützt."), // EServiceNotSupported
_T("Ungültige Packetgrösse."), // EInvalidPacketSize
_T("IrDA Verbindungsaufbau fehlgeschlagen."), // EConnectFailed
_T("OBEX Verbindung fehlgeschlagen."), // EObexConnectFailed
_T("OBEX Datentransfer fehlgeschlagen."), // EObexPutFailed
_T("OBEX Trennung fehlgeschlagen.") // EObexDisconnectFailed
};

static LPCTSTR STATUS_MESSAGES[] =
{
_T("Socket Verbindung aufbauen..."), // ESocket
_T("Suche nach kompatiblen Geräten..."), // EFindDevice
_T("Überprüfe OBEX Service..."), // EVerifyService
_T("IrDA Verbindung aufbauen..."), // EConnect
_T("Verbindungsaufbau zu OBEX Server..."), // EObexConnect
_T("Daten werden übertragen..."), // EObexPut
_T("Von OBEX Server trennen..."), // EObexDisconnect
_T("Übertragung beendet."), // EReady
};

/////////////////////////////////////////////////////////////////////////////
// CIrObexDlg constants

const int CIrObexDlg::MAX_NAME_LENGTH = 20;
const int CIrObexDlg::MIN_PACKET_SIZE = 255;
const int CIrObexDlg::MAX_PACKET_SIZE = 65535; // 64K bytes - 1

/////////////////////////////////////////////////////////////////////////////
// CIrObexDlg dialog

CIrObexDlg::CIrObexDlg(IStream* pStream, const CString& strName,
const int nPacketSize /*= 1024*/, CWnd* pParent /*= NULL*/)
: CDialog(CIrObexDlg::IDD, pParent),
m_socket(INVALID_SOCKET),
m_bAbort(false),
m_pSendDataThread(NULL),
m_nPacketSize(nPacketSize),
m_nDataSize(0),
m_pStream(pStream),
m_strName(strName.Left(MAX_NAME_LENGTH))
{
// Check pre-conditions
ASSERT(m_pStream != NULL);
ASSERT(AfxIsValidString(m_strName));
ASSERT(m_strName.GetLength() > 0);
ASSERT(m_strName.GetLength() <= MAX_NAME_LENGTH);
ASSERT(m_nPacketSize >= MIN_PACKET_SIZE);
ASSERT(m_nPacketSize <= MAX_PACKET_SIZE);

//{{AFX_DATA_INIT(CIrObexDlg)
//}}AFX_DATA_INIT

VERIFY(m_brBackground.CreateSysColorBrush(COLOR_BTNFACE));

m_bFullScreen = FALSE;
}

void CIrObexDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CIrObexDlg)
DDX_Control(pDX, IDC_IROBEX_PROGRESS, m_prgProgress);
//}}AFX_DATA_MAP
}

/////////////////////////////////////////////////////////////////////////////
// Message table

const UINT CIrObexDlg::MY_WM_TEXT_STATUS =
RegisterWindowMessage(_T("MY_WM_TEXT_STATUS-{72472B44-7554-11d7-9AA6-0002B32C4875}"));

const UINT CIrObexDlg::MY_WM_PROG_STATUS =
RegisterWindowMessage(_T("MY_WM_PROG_STATUS-{72472B46-7554-11d7-9AA6-0002B32C4875}"));

const UINT CIrObexDlg::MY_WM_END =
RegisterWindowMessage(_T("MY_WM_END-{72472B49-7554-11d7-9AA6-0002B32C4875}"));

BEGIN_MESSAGE_MAP(CIrObexDlg, CDialog)
//{{AFX_MSG_MAP(CIrObexDlg)
ON_WM_CLOSE()
ON_WM_DESTROY()
ON_WM_CTLCOLOR()
ON_WM_ACTIVATE()
ON_WM_SETTINGCHANGE()
//}}AFX_MSG_MAP
ON_REGISTERED_MESSAGE(MY_WM_TEXT_STATUS, OnTextStatus)
ON_REGISTERED_MESSAGE(MY_WM_PROG_STATUS, OnProgStatus)
ON_REGISTERED_MESSAGE(MY_WM_END, OnEnd)
// NM_RECOGNIZEGESTURE for PocketPC 2002
ON_NOTIFY_REFLECT((NM_FIRST-16), OnRecognizeGesture)
// NM_RECOGNIZEGESTURE for PocketPC 2003
ON_NOTIFY_REFLECT((NM_FIRST-50), OnRecognizeGesture)
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CIrObexDlg message handlers

BOOL CIrObexDlg::OnInitDialog()
{
CDialog::OnInitDialog();

// TODO: Add extra initialization here

// Get the size of the stream
STATSTG stg;
VERIFY(m_pStream->Stat(&stg, STATFLAG_NONAME) == S_OK);

if (stg.cbSize.HighPart > 0 || stg.cbSize.LowPart <= 0)
{
LOGERROR(_T("Invalid file size (%I64d bytes)."),
stg.cbSize.QuadPart);

EndDialog(EInvalidFileSize);
}
else
{
// Save the size of the stream
m_nDataSize = stg.cbSize.LowPart;

// Set the properties of the progress bar
m_prgProgress.SetRange32(0, m_nDataSize);
m_prgProgress.SetPos(0);

// Initialize Winsock
WSADATA wsaData;
const int nError = WSAStartup(MAKEWORD(2,0), &wsaData);
if (nError != 0)
{
LOGERROR(_T("The function \"WSAStartup\" failed; ")\
_T("error = %d. Could't start network service."),
nError);

EndDialog(ENetwork);
}
else
{
// Create the thread and start it
m_pSendDataThread = AfxBeginThread(SendData, this, THREAD_PRIORITY_NORMAL,
0, CREATE_SUSPENDED);
ASSERT(m_pSendDataThread != NULL);
ASSERT(AfxIsValidAddress(m_pSendDataThread, sizeof(CWinThread), TRUE));
ASSERT_VALID(m_pSendDataThread);
m_pSendDataThread->m_bAutoDelete = FALSE;

LOGNOTIFY(_T("OBEX transmission thread started successfully."));

m_pSendDataThread->ResumeThread(); // start the thread
}
}

return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}

void CIrObexDlg::OnClose()
{
// TODO: Add your message handler code here and/or call default

m_bAbort = true; // abort the transmission process
EndDialog(EAbort);

LOGNOTIFY(_T("Abort the OBEX transmission process."));

// CDialog::OnClose();
}

void CIrObexDlg::OnDestroy()
{
// TODO: Add your message handler code here

if (m_pSendDataThread != NULL)
{
// Shutdown and close the socket
if (m_socket != INVALID_SOCKET)
{
shutdown(m_socket,SD_BOTH);
closesocket(m_socket);
}

// Wait until the thread is terminated
WaitForSingleObject(m_pSendDataThread->m_hThread, INFINITE);
delete m_pSendDataThread;
m_pSendDataThread = NULL;

WSACleanup();

LOGNOTIFY(_T("OBEX transmission process terminated normaly."));
}

CDialog::OnDestroy();
}

HBRUSH CIrObexDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

// TODO: Change any attributes of the DC here

// TODO: Return a different brush if the default is not desired

if (nCtlColor == CTLCOLOR_STATIC)
{
pDC->SetBkMode(TRANSPARENT);

return m_brBackground;
}
else if (pWnd == this)
{
return m_brBackground;
}

return hbr;
}

void CIrObexDlg::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
{
CWnd::OnActivate(nState, pWndOther, bMinimized);
}

void CIrObexDlg::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
{
CWnd::OnSettingChange(uFlags, lpszSection);
}

void CIrObexDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
Default(); // call default message handler
}

void CIrObexDlg::OnRecognizeGesture(NMHDR* pNMHDR, LRESULT* pResult)
{
*pResult = TRUE; // cancel the red dot animation
}

LRESULT CIrObexDlg::OnTextStatus(WPARAM wParam, LPARAM lParam)
{
ASSERT(wParam >= 0 && wParam < _countof(STATUS_MESSAGES));
SetDlgItemText(IDC_IROBEX_STATUS, STATUS_MESSAGES[wParam]);

return 0L;
}

LRESULT CIrObexDlg::OnProgStatus(WPARAM wParam, LPARAM lParam)
{
const int nPos = static_cast<int>(wParam);

ASSERT(nPos >= 0 && nPos <= m_nDataSize);
m_prgProgress.SetPos(nPos);

return 0L;
}

LRESULT CIrObexDlg::OnEnd(WPARAM wParam, LPARAM lParam)
{
if (!m_bAbort)
EndDialog(wParam);

return 0L;
}

UINT CIrObexDlg::SendData(LPVOID pParam)
{
CIrObexDlg* pDialog = reinterpret_cast<CIrObexDlg*>(pParam);
ASSERT(pDialog != NULL); // don't use ASSERT_VALID here!
ASSERT(AfxIsValidAddress(pDialog, sizeof(CIrObexDlg), TRUE));

if (pDialog)
pDialog->SendData(); // send the data to the server (host)

return 0;
}

void CIrObexDlg::SendData()
{
// Set the seek pointer to the beginning of the stream
LARGE_INTEGER pos;
pos.QuadPart = 0;
VERIFY(m_pStream->Seek(pos, STREAM_SEEK_SET, NULL) == S_OK);

// Open socket
PostMessage(MY_WM_TEXT_STATUS, ESocket);

m_socket = socket(AF_IRDA, SOCK_STREAM, 0);
if (m_socket == INVALID_SOCKET)
{
LOGERROR(_T("The function \"socket\" failed; error = %d. ")\
_T("Could't start network service."), WSAGetLastError());

PostMessage(MY_WM_END, ENetwork);
return;
}

// Try several times to find a device
PostMessage(MY_WM_TEXT_STATUS, EFindDevice);

DEVICELIST deviceList = {0};
DWORD dwStart = GetTickCount();
for (;;)
{
if (GetTickCount() - dwStart >= TIMEOUT)
break;

int cbDevices = sizeof(deviceList);

// Obtain the available devices
deviceList.numDevice = 0;
if (getsockopt(m_socket, SOL_IRLMP, IRLMP_ENUMDEVICES,
reinterpret_cast<char*>(&deviceList), &cbDevices) == SOCKET_ERROR)
{
LOGERROR(_T("The function \"getsockopt\" failed; error = %d. ")\
_T("Could't obtain the available devices."), WSAGetLastError());

PostMessage(MY_WM_END, ENetwork);
return;
}

// Abort if we have found a device
if (deviceList.numDevice > 0)
break;

// Wait
::Sleep(100);

} // for (;;)

// Check if all retries have been finished
if (deviceList.numDevice == 0)
{
LOGWARNING(_T("No IrDA device found."));

PostMessage(MY_WM_END, ENoDeviceFound);
return;
}

LOGNOTIFY(_T("IrDA device found; device identifier: %02x.%02x.%02x.%02x"),
deviceList.Device[0].irdaDeviceID[0],
deviceList.Device[0].irdaDeviceID[1],
deviceList.Device[0].irdaDeviceID[2],
deviceList.Device[0].irdaDeviceID[3]);

// Try several times to verify the OBEX service
PostMessage(MY_WM_TEXT_STATUS, EVerifyService);

IAS_QUERY iasq = {0};
memset(&iasq, 0, sizeof(iasq));
memcpy(iasq.irdaDeviceID, deviceList.Device[0].irdaDeviceID, sizeof(iasq.irdaDeviceID));
strcpy(iasq.irdaClassName, "OBEX");
strcpy(iasq.irdaAttribName, "IrDA:TinyTP:LsapSel");
int iasqlen = sizeof(iasq);

dwStart = GetTickCount();
for (;;)
{
if (GetTickCount() - dwStart >= TIMEOUT)
{
LOGERROR(_T("Could't verify the OBEX service."));

PostMessage(MY_WM_END, ENetwork);
return;
}

// Do the query for the OBEX service
if (getsockopt(m_socket, SOL_IRLMP, IRLMP_IAS_QUERY,
reinterpret_cast<char*>(&iasq), &iasqlen) == SOCKET_ERROR)
{
LOGWARNING(_T("The function \"getsockopt\" failed; error = %d."),
WSAGetLastError());
}
else
{
break;
}

// Wait
::Sleep(100);

} // for (;;)

// Check the query result
if (iasq.irdaAttribType == IAS_ATTRIB_NO_CLASS || // Class not found?
iasq.irdaAttribType == IAS_ATTRIB_NO_ATTRIB) // Attribute not found?
{
LOGWARNING(_T("OBEX service not supported."));

PostMessage(MY_WM_END, EServiceNotSupported);
return;
}

// Try to connect to the server
PostMessage(MY_WM_TEXT_STATUS, EConnect);

SOCKADDR_IRDA sa = {0};

// Setup the IrDA address
sa.irdaAddressFamily = AF_IRDA;
sprintf(sa.irdaServiceName, "LSAP-SEL%d", iasq.irdaAttribute.irdaAttribInt);
memcpy(sa.irdaDeviceID, deviceList.Device[0].irdaDeviceID, sizeof(sa.irdaDeviceID));

// Do the connect to the OBEX server
if (connect(m_socket, reinterpret_cast<SOCKADDR*>(&sa), sizeof(sa)) == SOCKET_ERROR)
{
LOGERROR(_T("The function \"connect\" failed; error = %d. ")\
_T("Connect to founded device failed."), WSAGetLastError());

PostMessage(MY_WM_END, EConnectFailed);
return;
}

ASSERT(m_socket != INVALID_SOCKET);

// Send the OBEX connect request
PostMessage(MY_WM_TEXT_STATUS, EObexConnect);
if (!Connect())
return;

// Send the OBEX put request with the data
PostMessage(MY_WM_TEXT_STATUS, EObexPut);
if (!Put())
return;

// Send the OBEX disconnect request
PostMessage(MY_WM_TEXT_STATUS, EObexDisconnect);
if (!Disconnect())
return;

// Send READY message to the dialog
PostMessage(MY_WM_TEXT_STATUS, EReady);
PostMessage(MY_WM_END, EOk);
}

bool CIrObexDlg::Connect()
{
ASSERT(m_socket != INVALID_SOCKET);

// Create the client request
BYTE request[7] = {0};
request[0] = OBEX_CONNECT;
request[1] = static_cast<BYTE>((htons(7) & 0x00FF) >> 0);
request[2] = static_cast<BYTE>((htons(7) & 0xFF00) >> 8);
request[3] = OBEX_VERSION;
request[4] = OBEX_CONNECT_FLAGS;
request[5] = static_cast<BYTE>((htons(MAX_CLIENT_PACKET_SIZE) & 0x00FF) >> 0);
request[6] = static_cast<BYTE>((htons(MAX_CLIENT_PACKET_SIZE) & 0xFF00) >> 8);

// Send the client request
if (!Send(reinterpret_cast<const char*>(request), sizeof(request)))
{
PostMessage(MY_WM_END, EObexConnectFailed);
return false;
}

// Read the response code
BYTE response = 0;
if (!Receive(reinterpret_cast<char*>(&response), sizeof(response)))
{
PostMessage(MY_WM_END, EObexConnectFailed);
return false;
}

// Check the response code
if (response != OBEX_SUCCESS)
{
PostMessage(MY_WM_END, EObexConnectFailed);
return false;
}

// Read the packet length
WORD packetLength = 0;
if (!Receive(reinterpret_cast<char*>(&packetLength), sizeof(packetLength)))
{
PostMessage(MY_WM_END, EObexConnectFailed);
return false;
}

packetLength = ntohs(packetLength);

// Check the packet length
if (packetLength < 7)
{
PostMessage(MY_WM_END, EObexConnectFailed);
return false;
}

// Read the OBEX version number
BYTE version = 0;
if (!Receive(reinterpret_cast<char*>(&version), sizeof(version)))
{
PostMessage(MY_WM_END, EObexConnectFailed);
return false;
}

// Read the connect flags
BYTE flags = 0;
if (!Receive(reinterpret_cast<char*>(&flags), sizeof(flags)))
{
PostMessage(MY_WM_END, EObexConnectFailed);
return false;
}

// Read the maximum OBEX packet length
WORD maxPacketLength = 0;
if (!Receive(reinterpret_cast<char*>(&maxPacketLength), sizeof(maxPacketLength)))
{
PostMessage(MY_WM_END, EObexConnectFailed);
return false;
}

maxPacketLength = ntohs(maxPacketLength);

// Check the maximum packet size that the server can accept
if (m_nPacketSize > static_cast<int>(maxPacketLength))
{
PostMessage(MY_WM_END, EInvalidPacketSize);
return false;
}

// Read the optional headers
if (!RecvOpt(packetLength - 7))
{
PostMessage(MY_WM_END, EObexConnectFailed);
return false;
}

return true;
}

bool CIrObexDlg::Disconnect()
{
ASSERT(m_socket != INVALID_SOCKET);

// Create the client request
BYTE request[3] = {0};
request[0] = OBEX_DISCONNECT;
request[1] = static_cast<BYTE>((htons(3) & 0x00FF) >> 0);
request[2] = static_cast<BYTE>((htons(3) & 0xFF00) >> 8);

// Send the client request
if (!Send(reinterpret_cast<const char*>(request), sizeof(request)))
{
PostMessage(MY_WM_END, EObexDisconnectFailed);
return false;
}

// Read the response code
BYTE response = 0;
if (!Receive(reinterpret_cast<char*>(&response), sizeof(response)))
{
PostMessage(MY_WM_END, EObexDisconnectFailed);
return false;
}

// Check the response code
if (response != OBEX_SUCCESS)
{
PostMessage(MY_WM_END, EObexDisconnectFailed);
return false;
}

// Read the packet length
WORD packetLength = 0;
if (!Receive(reinterpret_cast<char*>(&packetLength), sizeof(packetLength)))
{
PostMessage(MY_WM_END, EObexDisconnectFailed);
return false;
}

packetLength = ntohs(packetLength);

// Check the packet length
if (packetLength < 3)
{
PostMessage(MY_WM_END, EObexDisconnectFailed);
return false;
}

// Read the optional headers
if (!RecvOpt(packetLength - 3))
{
PostMessage(MY_WM_END, EObexDisconnectFailed);
return false;
}

return true;
}

bool CIrObexDlg::Put()
{
// Check pre-conditions
ASSERT(m_socket != INVALID_SOCKET);
ASSERT(m_nDataSize > 0);
ASSERT(AfxIsValidString(m_strName));
ASSERT(m_strName.GetLength() > 0);
ASSERT(m_strName.GetLength() <= MAX_NAME_LENGTH);
ASSERT(m_nPacketSize >= MIN_PACKET_SIZE);
ASSERT(m_nPacketSize <= MAX_PACKET_SIZE);

// Create some temporary variables
int dataSize = m_nDataSize;
CTtoW lpwszName(m_strName); // convert string from TCHAR to WCHAR
LPBYTE request = new BYTE[m_nPacketSize];
ASSERT(request != NULL);
ASSERT(AfxIsValidAddress(request, sizeof(BYTE) * m_nPacketSize, TRUE));

// Some calculations
int namelen = (lpwszName.GetLength() + 1) * sizeof(WCHAR);
int restlen = m_nPacketSize - (14 + namelen);
int bodlen = dataSize > restlen ? restlen : dataSize;

// Create the client request
request[0] = (dataSize - bodlen) > 0 ? OBEX_PUT : OBEX_PUT_FINAL;
request[1] = static_cast<BYTE>((htons(bodlen + 14 + namelen) & 0x00FF) >> 0);
request[2] = static_cast<BYTE>((htons(bodlen + 14 + namelen) & 0xFF00) >> 8);

request[3] = OBEX_NAME;
request[4] = static_cast<BYTE>((htons(namelen + 3) & 0x00FF) >> 0);
request[5] = static_cast<BYTE>((htons(namelen + 3) & 0xFF00) >> 8);

for (unsigned int n = 0; n < lpwszName.GetLength() + 1; ++n)
{
request[6 + n * 2 + 0] = static_cast<BYTE>((htons(lpwszName[n]) & 0x00FF) >> 0);
request[6 + n * 2 + 1] = static_cast<BYTE>((htons(lpwszName[n]) & 0xFF00) >> 8);
}

request[6 + namelen + 0] = OBEX_LENGTH;
request[6 + namelen + 1] = static_cast<BYTE>((htonl(dataSize) & 0x000000FF) >> 0);
request[6 + namelen + 2] = static_cast<BYTE>((htonl(dataSize) & 0x0000FF00) >> 8);
request[6 + namelen + 3] = static_cast<BYTE>((htonl(dataSize) & 0x00FF0000) >> 16);
request[6 + namelen + 4] = static_cast<BYTE>((htonl(dataSize) & 0xFF000000) >> 24);

request[6 + namelen + 5] = (dataSize - bodlen) > 0 ? OBEX_BODY : OBEX_END_OF_BODY;
request[6 + namelen + 6] = static_cast<BYTE>((htons(bodlen + 3) & 0x00FF) >> 0);
request[6 + namelen + 7] = static_cast<BYTE>((htons(bodlen + 3) & 0xFF00) >> 8);

// Copy the data in the packet
VERIFY(m_pStream->Read(&request[6 + namelen + 8], bodlen, NULL) == S_OK);

// Send the client request
if (!Send(reinterpret_cast<const char*>(request), bodlen + 14 + namelen))
{
delete [] request;
PostMessage(MY_WM_END, EObexPutFailed);
// ASSERT(false);
return false;
}

// Read the response code
BYTE response = 0;
if (!Receive(reinterpret_cast<char*>(&response), sizeof(response)))
{
delete [] request;
PostMessage(MY_WM_END, EObexPutFailed);
return false;
}

// Check the response
if (((dataSize - bodlen) > 0 && response != OBEX_CONTINUE) ||
((dataSize - bodlen) <= 0 && response != OBEX_SUCCESS))
{
delete [] request;
PostMessage(MY_WM_END, EObexPutFailed);
// ASSERT(false);
return false;
}

// Read the packet length
WORD packetLength = 0;
if (!Receive(reinterpret_cast<char*>(&packetLength), sizeof(packetLength)))
{
delete [] request;
PostMessage(MY_WM_END, EObexPutFailed);
return false;
}

packetLength = ntohs(packetLength);

// Check the packet length
if (packetLength < 3)
{
delete [] request;
PostMessage(MY_WM_END, EObexPutFailed);
return false;
}

// Read the optional headers
if (!RecvOpt(packetLength - 3))
{
delete [] request;
PostMessage(MY_WM_END, EObexPutFailed);
return false;
}

dataSize -= bodlen;

// Update the progress bar
PostMessage(MY_WM_PROG_STATUS, m_nDataSize - dataSize);

while (dataSize > 0)
{
// Some calculations
restlen = m_nPacketSize - 6;
bodlen = dataSize > restlen ? restlen : dataSize;

// Create the client request
request[0] = (dataSize - bodlen) > 0 ? OBEX_PUT : OBEX_PUT_FINAL;
request[1] = static_cast<BYTE>((htons(bodlen + 6) & 0x00FF) >> 0);
request[2] = static_cast<BYTE>((htons(bodlen + 6) & 0xFF00) >> 8);

request[3] = (dataSize - bodlen) > 0 ? OBEX_BODY : OBEX_END_OF_BODY;
request[4] = static_cast<BYTE>((htons(bodlen + 3) & 0x00FF) >> 0);
request[5] = static_cast<BYTE>((htons(bodlen + 3) & 0xFF00) >> 8);

// Copy the data in the packet
VERIFY(m_pStream->Read(&request[6], bodlen, NULL) == S_OK);

// Send the client request
if (!Send(reinterpret_cast<const char*>(request), bodlen + 6))
{
delete [] request;
PostMessage(MY_WM_END, EObexPutFailed);
// ASSERT(false);
return false;
}

// Read the response code
if (!Receive(reinterpret_cast<char*>(&response), sizeof(response)))
{
delete [] request;
PostMessage(MY_WM_END, EObexPutFailed);
return false;
}

// Check the response
if (((dataSize - bodlen) > 0 && response != OBEX_CONTINUE) ||
((dataSize - bodlen) <= 0 && response != OBEX_SUCCESS))
{
delete [] request;
PostMessage(MY_WM_END, EObexPutFailed);
// ASSERT(false);
return false;
}

// Read the packet length
if (!Receive(reinterpret_cast<char*>(&packetLength), sizeof(packetLength)))
{
delete [] request;
PostMessage(MY_WM_END, EObexPutFailed);
return false;
}

packetLength = ntohs(packetLength);

// Check the packet length
if (packetLength < 3)
{
delete [] request;
PostMessage(MY_WM_END, EObexPutFailed);
return false;
}

// Read the optional headers
if (!RecvOpt(packetLength - 3))
{
delete [] request;
PostMessage(MY_WM_END, EObexPutFailed);
return false;
}

dataSize -= bodlen;

// Update the progress bar
PostMessage(MY_WM_PROG_STATUS, m_nDataSize - dataSize);
}

ASSERT(dataSize == 0);

delete [] request;

return true;
}

const CString CIrObexDlg::GetErrorMessage(int nError)
{
// Check pre-conditions
ASSERT(nError >= 0 && nError < _countof(ERROR_MESSAGES));

if (nError >= 0 && nError < _countof(ERROR_MESSAGES))
return CString(ERROR_MESSAGES[nError]);

return CString(_T("Undefinierter Fehler."));
}

bool CIrObexDlg::Send(const char* pData, int nLength)
{
// Check pre-conditions
ASSERT(m_socket != INVALID_SOCKET);
ASSERT(pData != NULL);
ASSERT(AfxIsValidAddress(pData, nLength, FALSE));
ASSERT(nLength > 0);

while (nLength > 0)
{
const int nResult = send(m_socket, pData, nLength, 0);

if (nResult == SOCKET_ERROR || nResult == 0)
return false;

nLength -= nResult;
pData += nResult;
}

return true;
}

bool CIrObexDlg::Receive(char* pData, int nLength)
{
// Check pre-conditions
ASSERT(m_socket != INVALID_SOCKET);
ASSERT(pData != NULL);
ASSERT(AfxIsValidAddress(pData, nLength, TRUE));
ASSERT(nLength > 0);

while (nLength > 0)
{
const int nResult = recv(m_socket, pData, nLength, 0);

if (nResult == SOCKET_ERROR || nResult == 0)
return false;

nLength -= nResult;
pData += nResult;
}

return true;
}

bool CIrObexDlg::RecvOpt(int nLength)
{
// Check pre-conditions
ASSERT(m_socket != INVALID_SOCKET);
ASSERT(nLength >= 0);

while (nLength > 0)
{
BYTE tmp = 0;

int nResult = recv(m_socket, reinterpret_cast<char*>(&tmp), sizeof(tmp), 0);

if (nResult == SOCKET_ERROR || nResult == 0)
return false;

nLength -= nResult;
}

return true;
}

Regards,
Daniel.
GeneralRe: Uploading files from a PC to a Mobile Handset Pin
joshibv3-Sep-04 2:03
joshibv3-Sep-04 2:03 
GeneralRe: Uploading files from a PC to a Mobile Handset Pin
Daniel Strigl3-Sep-04 6:36
Daniel Strigl3-Sep-04 6:36 
GeneralRe: Uploading files from a PC to a Mobile Handset Pin
joshibv6-Sep-04 2:25
joshibv6-Sep-04 2:25 
GeneralRe: Uploading files from a PC to a Mobile Handset Pin
joshibv6-Sep-04 22:58
joshibv6-Sep-04 22:58 
GeneralRe: Uploading files from a PC to a Mobile Handset Pin
riki_risnandar23-Dec-04 8:19
riki_risnandar23-Dec-04 8:19 
GeneralRe: Uploading files from a PC to a Mobile Handset Pin
Daniel Strigl23-Dec-04 10:13
Daniel Strigl23-Dec-04 10:13 
GeneralRe: Uploading files from a PC to a Mobile Handset Pin
riki_risnandar24-Dec-04 1:05
riki_risnandar24-Dec-04 1:05 
GeneralRe: Uploading files from a PC to a Mobile Handset Pin
Daniel Strigl24-Dec-04 21:48
Daniel Strigl24-Dec-04 21:48 
QuestionHow to debug IrDA in Emulator Pin
chencen200020-Aug-04 9:13
chencen200020-Aug-04 9:13 
AnswerRe: How to debug IrDA in Emulator Pin
Daniel Strigl2-Sep-04 19:03
Daniel Strigl2-Sep-04 19:03 
GeneralIR send/recieve Pin
relaxwork19-Aug-04 2:57
relaxwork19-Aug-04 2:57 
GeneralRe: IR send/recieve Pin
Daniel Strigl2-Sep-04 19:02
Daniel Strigl2-Sep-04 19:02 
GeneralSonyEricsson T610 and Nokia 8210 Pin
JimmyLam18-Aug-04 8:57
JimmyLam18-Aug-04 8:57 
GeneralRe: SonyEricsson T610 and Nokia 8210 Pin
Daniel Strigl2-Sep-04 19:00
Daniel Strigl2-Sep-04 19:00 
GeneralRe: SonyEricsson T610 and Nokia 8210 Pin
riki_risnandar5-Sep-04 3:16
riki_risnandar5-Sep-04 3:16 
Generalpocket pc 2003 Pin
anlex8-Aug-04 21:36
anlex8-Aug-04 21:36 
GeneralRe: pocket pc 2003 Pin
Daniel Strigl2-Sep-04 18:56
Daniel Strigl2-Sep-04 18:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

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