Scanning Devices Inc.



Sensors, Instruments and Controls
111 Terrace Hall Avenue
Burlington, Massachusetts (USA) 01803
1-781-272-5135
FAX: 1-781-272-4856
Email us: mail@scanningdevices.com


Bluetooth Industrial I/O Server Users Guide

Part 3: Client Application for Pocket PC 2003

Previous Parts

Part 1: SD900 Description and Specifications
Part 2: The Palm OS Application

Contents

Component Requirements
Library and Application Functions
The Sample Application
How the Library Works
A Simple Request
Multipe Step Request
Library Messages
Library Calls
Error Handling

Components

The Pocket PC 2003 Application consists of a "library" (a series of dll's) and an application program. The application, library plus program, functions as a client to the Scanning Devices SD900 Industrial Server.

Scanning Devices Inc. distributes component dll's. They require a PocketPC 2003 Pro with Bluetooth radio module (either integrated or implemented via flash card) and Windows CE bluetooth drivers by Widcomm Inc. Examples of Pocket PC handhelds with necessary prerequisites are HP IPAQ 1945, and others.

This part of the Scanning Devices SD900 Bluetooth Industrial I/O Users Guide is illustrated with a sample dialog program based on Windows Mobile 2003 / Pocket PC 2003, Microsoft Foundation Class, and Widcomm Inc. bluetooth implementation. The sample program is extensively documented and may form a basis for development of your own applications. It is available without cost from Scanning Devices via email. A Windows CE development environment with embedded visual C++ version 4.0 is required to make alterations to the sample program.

If you would like a copy, email Scanning Devices with "Request SD900 Project - Pocket PC" in the subject line.

Back to the Table of Contents

Functions performed by the library

1. Establishes and maintain a wireless connection between the Pocket PC's bluetooth radio module and the SD900 Server,
2. Receives messages from the application, constructs and sends wireless requests to the server.
3. Receives responses from the server, including requested data.
4. Delivers server responses to the application via Windows CE messages.

Functions performed by the application program:

1. Present a graphic user interface consistent with user needs and resources of the Pocket PC. Interface should organize and present data in a useful manner, collect user input to build requests for the library and handle responses from the library.
2. Data storage and retrieval in the Pocket PC's persistent memory.
3. Fail gracefully in the event of user, application or communications error.

Back to the Table of Contents

The Sample Application

"SD900Test" is the sample application excersizing all SD900 functions and illustrating a straightforward implementation. It is based on a series of modal dialog boxes, each supporting a subset function:

The main dialog presents a simple user interface with button controls, allowing the user to select the function. It also keeps track of the connection and the connected server's identity.

Modal dialogs provide the user interface to specific library functions. They generate data based on user input, make library calls to communicate with the server and handle library responses to display data or results to the user.

We will use the sample application to illustrate the program techniques implemented with the library.

Back to the Table of Contents

The Library - What it does and how it works

The sample application calls library functions directly. Each function constructs a unique message and sends it via wireless bluetooth radio to the remote server. It generates a pair of responses, one when the the library initiates transmission on the wireless link and a second when the server's response is received and returned to the application. The form of those responses is quite different and important for the implementer to understand.

First response: the library responds with an indication either that the function was processed properly or an error occured. For example, if the dialog requested that data is to be read from the server's com port, the library's first response indicates the success or failure of constructing the request and sending it to the server. The first response is not an indication that the wireless message was received at the server or the data was actually transmitted from the server's com port back to the Pocket PC.

Second response: the library generates a message containing the with the server's response and sends that message to the application. This message indicates that the library's request was received at the server and the requested operation was completed.

The two responses differ in timing as well as the method by which they are delivered.

The first response is direct from the library to the calling point in the application. The application can call the library directly and should continue processing when the first response is received. It should not wait for the second response.

Library Makes Two Responses to User Request for Wireless Server Access

The second response is asynchronous as it requires a wireless round-trip transmission and processing on the server. It may involve a time delay. The second response is implemented as a Windows message. The library delivers the message to the application's main thread (dialog) and it is the thread's responsibility to either handle the message or distribute the message to the source of the library call.

The sample application uses modal dialogs as the means to display server generated data, collect user input and generate library calls. The modal dialogs generate libarary calls directly.

Back to the Table of Contents

Tracing a Single Request

We can illustrate this technique by tracing what happens when the user wants to read data from the server's com port. Assume the user is connected, has a dialog named ComPort opened and taps the "Get Com" button.

Here is a function that responds to a tap on a send com data button:

void CComPort::OnGetCom() { // TODO: Add your control notification handler code here m_getCom.SetCheck(BST_UNCHECKED); if(GetCommData()) m_status=_T("Sent request."); else m_status=_T("Unable to send."); UpdateData(FALSE); }

This is the complete function in response to the user's tap. The first statement clears the checkbox (a cosmetic detail). The the GetCommData() call is made the library. The library constructs the appropriate message to the server and sends it over the wireless link. The library responds to the calling function which displays the status to the user in the edit box named m_status. The function exits after updating the user display with the status of the request.

The library handles the message transmission and response from the server. It responds with a message to the application's main thread, in this case the main dialog.

The library has a repriortoire of three messages. They must be declared in the application program.

const UINT SD_CONNECTEDMESSAGE = RegisterWindowMessage(_T("SD_CONNECTEDMESSAGE")); const UINT SD_DATARECEIVEDMESSAGE = RegisterWindowMessage(_T("SD_DATARECEIVEDMESSAGE")); const UINT SD_REMOTEDISCONNECTEDMESSAGE = RegisterWindowMessage(_T("SD_REMOTEDISCONNECTEDMESSAGE"));

All three will be detailed later. For now the DataReceivedMessage is the one of interest, as that this the one we are tracing.

In addition to the messages generated for the main dialog controls, the main dialog message map includes 3 registered message maps corresponding to three messages the library may generate.

Here is the complete main dialog message map, including three for the library.

BEGIN_MESSAGE_MAP(CTestDlg, CDialog) //{{AFX_MSG_MAP(CTestDlg) ON_BN_CLICKED(IDC_OK, OnOk) ON_BN_CLICKED(IDC_OUT, OnOut) ON_BN_CLICKED(IDC_COM_PORT, OnComPort) ON_BN_CLICKED(IDC_SAVED_DATA, OnSavedData) ON_BN_CLICKED(IDC_INITIALIZE, OnInitialize) ON_BN_CLICKED(IDC_NAME, OnName) ON_BN_CLICKED(IDC_CONNECT, OnConnect) //}}AFX_MSG_MAP ON_REGISTERED_MESSAGE(SD_CONNECTEDMESSAGE, ConnectedMessage) ON_REGISTERED_MESSAGE(SD_DATARECEIVEDMESSAGE, DataReceivedMessage) ON_REGISTERED_MESSAGE(SD_REMOTEDISCONNECTEDMESSAGE, RemoteDisconnectedMessage) END_MESSAGE_MAP()

The library sends the SD_DATARECEIVEDMESSAGE to the main dialog for processing. The windows message handler routes it to the the DataReceivedMessage function.

This is the complete DataReceivedMessage function to handle the library's response in the main thread. Since all calls in the application program come from modal dialogs, the main thread needs only to send the message on to the current active window. It identifies the active window (ComPort) with the GetActiveWindow() call. IF there is no active window (as would be the case if the user closed application), the response is abandoned.

Some care is needed here. If for any reason, the main dialog is the active window, (as would be the case if the user closed the modal dialog) then this function would send the message to itself. That would lock the application in an endless loop. So we have to specifically avoid that.

long CTestDlg::DataReceivedMessage(WPARAM wParam, LPARAM lParam) { CDataReceived *pData = (CDataReceived *)lParam; UINT16 size=0; // size of data received size = *pData->length; CWnd * wP = GetActiveWindow(); if((wP!= NULL) && (wP != m_mainWindowHandle)) { //make sure this is not the active window otherwise //this message comes back here wP->SendMessage(SD_DATARECEIVEDMESSAGE, 0, (LONG)pData); m_data = _T("Sent message"); } else { if(wP == NULL) m_data = _T("Window Was Closed"); else m_data = _T("No place to send it"); } UpdateData(FALSE); return 1; }

So the message is sent to the modal dialog ComPort. Here is how the model dialog handles the message:

The modal dialog's message map includes an entry for the message just sent by the main dialog.

BEGIN_MESSAGE_MAP(CComPort, CDialog) //{{AFX_MSG_MAP(CComPort) ON_BN_CLICKED(IDC_GET_COM, OnGetCom) ON_BN_CLICKED(IDC_SEND_COM, OnSendCom) //}}AFX_MSG_MAP ON_REGISTERED_MESSAGE(SD_DATARECEIVEDMESSAGE, DataReceivedMessage) END_MESSAGE_MAP()

Windows CE routes the message to the DataReceivedMessage function for handling. Here is the complete function:

long CComPort::DataReceivedMessage(WPARAM wParam, LPARAM lParam) { CDataReceived *pData = (CDataReceived *)lParam; UINT16 size=0; // size of data received size = *pData->length; switch(pData->pWideData[0]) { case 'T': //response to Send Com Data { m_status = _T("Response received."); //optionally, may blank the edit area here. UpdateData(FALSE); break; } case 'J': //response to Get Com Data { //take out the "J" using CString Mid CString message; message.GetBuffer(size); message.ReleaseBuffer(); message = pData->pWideData; m_dataIn.GetBuffer(size); m_dataIn.ReleaseBuffer(); m_dataIn = message.Mid(1); m_status = _T("Response received."); UpdateData(FALSE); break; } default: m_status = _T("Wrong response"); UpdateData(FALSE); break; } return 1; }

There are two messages directed to this function, both server responses. One is the server's response for GetComData, the one we are following. The response to SendComData is also handled in this dialog.

The first byte of the response to GetComData is the ASCII character J. The following data is the requested data read from the server's com port. (See Users Guide Part 1 for the ASCII character command usage). We use the CString MID (very similar to BASIC's LEFT$, MID$, and RIGHT$ string functions) function to take out the leading J and put the received data in the dialog control named m_dataIn. We also update the status edit box and return 1 to the message source.

The request is complete. The user taps on the screen. The application and the library generate the request and route the response back to the user.

Back to the Table of Contents

Generating and handling multiple requests

The previous example illustrated a single request and response. Often, your application may be required to send multiple requests to the server to accomplish its task. These must be sequenced and controlled by the second library response.

Here is an example which generates two requests to get the discrete inputs and the busy input (The busy input is a single discrete input sometimes used to determine an overall remote machine state; ie, running, loading, calibrating, etc. You could build an application to check the busy input before making any further requests or sending any data).

Assume the user is connected and has a dialog named InputOutput opened. The user taps the Get Input button. Here is the function that handles the tap:

void CInputOutput::OnGetIn() { // TODO: Add your control notification handler code here m_getInput.SetCheck(BST_UNCHECKED); m_haveInputs = FALSE; m_haveBusy = FALSE; if(GetInput()) m_status = _T("Requested Input"); else m_status=_T("Unable to send."); UpdateData(FALSE); }

Notice that this function generates one server request GetInput() and exits when the library's first response is received. It also sets two flags to indicate that two responses are required to complete the task.

The library response comes through the main dialog's DataReceivedMessage just as in the Com Port example. Except now the InputOutput Dialog is the active window.

The Message Map for InputOutput Modal Dialog includes the same mapping for the DataReceivedMessage:

BEGIN_MESSAGE_MAP(CInputOutput, CDialog) //{{AFX_MSG_MAP(CInputOutput) ON_BN_CLICKED(IDC_GET_IN, OnGetIn) ON_BN_CLICKED(IDC_SEND_OUTPUTS, OnSendOutputs) //}}AFX_MSG_MAP ON_REGISTERED_MESSAGE(SD_DATARECEIVEDMESSAGE, DataReceivedMessage) END_MESSAGE_MAP()

So the message is routed to the DataReceivedMessage for this dialog.

Here is the function that handles the library's second response:

long CInputOutput::DataReceivedMessage(WPARAM wParam, LPARAM lParam) { CDataReceived *pData = (CDataReceived *)lParam; UINT16 size=0; // size of data received size = *pData->length; switch(pData->pWideData[0]) { case 'B': //response to get busy input { m_busyValue = (unsigned char)pData->pWideData[1]; m_haveBusy = TRUE; if(m_haveInputs == TRUE) SetInputDisplay(m_inputValue,m_busyValue); else GetInput(); m_status = _T("Busy Response received."); UpdateData(FALSE); break; } case 'F': //response to get inputs { m_inputValue = (unsigned char)pData->pWideData[1]; m_haveInputs = TRUE; if(m_haveBusy == TRUE) SetInputDisplay(m_inputValue,m_busyValue); else GetBusyInput(); m_status = _T("Input Response received."); UpdateData(FALSE); break; } case 'N': //response to set outputs { m_status = _T("Response received."); UpdateData(FALSE); break; } default: m_status = _T("Wrong response"); UpdateData(FALSE); break; } return 1; }

The InputOutput dialog handles three responses, so there are three cases: responses to GetInput(), GetBusyInput(), and SetOutputs().

The response ("F") is examined, received data asssigned to a variable and the approriate flag is set. The busy input flag is clear, so a GetBusyInput() call is made to complete the task. With the library's first response to GetBusyInput(), the function exits.

The library generates its second response to the GetBusyInput() call and the message is routed to the DataReceivedMessage function for handling. The response ("B") is examined, data assigned to a variable and the appropriate flag cleared. Since all data has been received, the user display is updated and the function exits.

The user has tapped on the screen. The application and library have sequenced a set of requests to get the data and update the display.

This shows how the two requests are sequenced through the library's second response, insuring single thread processing through the wireless link.

Back to the Table of Contents

Library Messages

Discussion has focused on the DataReceivedMessage generated by the library. This is one of three messages the library generates. Here are the full set of library messages:

Message Description Content *
DataReceivedMessage An server response transmission has been received. ASCII character identifier
Message Data if any
ConnectedMessage A connection has been established to the server Server Bluetooth Address
Server Friendley Name
RemoteDisconnectedMessage Server has disconnected Reason Code
*Please see sample program header file for specific class, data types, variable names, etc.

Discussion has mentioned library functions which generate server requests. Additional functions request library status. These functions generate no bluetooth wireless transmission (and therefore no second response).

Here are Library Function Prototypes. These must be included in the calling program.

Library function which generate a wireless message and both first and second responses.

extern __declspec(dllimport) BOOL SendData(char *message, UINT16 *length, UINT16 *sent); extern __declspec(dllimport) BOOL Connect(); extern __declspec(dllimport) BOOL GetCommData(void); extern __declspec(dllimport) BOOL GetSavedData(void); extern __declspec(dllimport) BOOL GetInput(void); extern __declspec(dllimport) BOOL GetInitOutput(void); extern __declspec(dllimport) BOOL GetCommSettings(void); extern __declspec(dllimport) BOOL GetBusyInput(void); extern __declspec(dllimport) BOOL SetOutput(char * data); extern __declspec(dllimport) BOOL SetSavedData( void * data, UINT16 *size); extern __declspec(dllimport) BOOL SetCommData(char * data); extern __declspec(dllimport) BOOL SetComPortSettings( int * parityIndex, int * stopIndex, int * dataIndex, int * baudIndex); extern __declspec(dllimport) BOOL SetInitialOutput(char * data); extern __declspec(dllimport) BOOL SetFriendlyName(char * message);

Library Functions which do not generate a wireless message, generate a single response.

extern __declspec(dllimport) CString GetFriendlyName(void); extern __declspec(dllimport) BD_ADDR * GetBdAddr(); extern __declspec(dllimport) BOOL isConnected(); extern __declspec(dllimport) BOOL Disconnect(void);

The combination of library functions and responses make up the complete access to the SD900 Server.

Back to the Table of Contents

Error Handling

The sample application makes no attempt to handle errors beyond reporting status to the user. Errors are inevitable: user moves out of range, congestion occurs, signal is lost, user accidently closes the application, pocket PC does something else. Your application should handle these situations gracefully.

Consider a timer For example. When generating a library request, start a windows timer and set a flag to indicate that a response is due. When the response is received, clear the flag and stop the timer. IF the timer "ticks" with the flag set, you may have an error condition (such as the client has moved out of range, the signal has been lost, congestion has developed, etc.) Your application can notify the user or repeat the request.

References

Bluetooth Consortium: Bluetooth Core Specification
Altech Corporation: Enclosure Information
Palm Source: Palm OS Bluetooth Library.
WidComm Inc: BTW-CE Documentation Set.

Sample Program development required:

Microsoft Windows XP/Pro
Microsoft Active Synch version 3.0 Pocket PC Connectivity
Microsoft embedded C++ version 4.0
Widcomm Inc. BTW-CE Development Kit for Windows CE/PocketPC
Pocket PC 2003 with Bluetooth Radio Module, Widcomm implementation

Installation and operation of the sample program requires:

Windows Mobile 2003 for Pocket PC 2003 (Microsoft and Pocket PC manufacturer)
BtSdkCE30.dll (Widcomm/Scanning Devices)
BtCoreIf.dll (Widcomm/Scanning Devices)
BtInquiry.dll (Scanning Devices)
Pocket PC 2003 with integrated bluetooth capability or the ability to accept a Bluetooth Compact Flash card.

  • Integrated: Hewlett Packard IPAQ 1945, 2210. 2215, 415x, 435x, 5150, 555x, others.
  • Compact Flash: Brainboxes, Billionton, CC&C, Philips, Samsung, Sharp, Others
  • Revison History

    Author: David Chanoux

    20-NOV-2002: Initial Release:
    14-OCT-2003: Add local SD900 non-volatile memory commands, add graphics, separate into two parts.
    12-JAN-2004: Add part 3 Pocket PC Client, terminology refinements incorporating server/client.

    Back to the Table of Contents