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 2 - Palm OS

Back to Part 1, SD900
Ahead to Part 3, Pocket PC

Contents

  • Sample Application
  • Development of the Sample Application
  • The Development section of this Users Guide illustrates the sample application with snippets of code. The complete Metrowerks Code Warrior Project for the SD900 Demonstration Program is available without cost from Scanning Devices via email. It is extensively documented and may form a solid basis for development of your own applications.

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

    Sample Application

    The Palm OS Sample Application distributed for SD900 exercises all commands. It is built with a simple and straightforward user interface. The user generates commands by tapping on the Palm screen to make selections or enter text. The application has no database. User interface and database processing are independent of SD900. The developer can implement these in any favorite style and to any level of complexity.

  • Discovery and Connection
  • Commands and Responses
  • Discovery and Connection

    This section will concentrate on two phases. Discovery and connection makes use of the bluetooth library of basic functions provided with Palm OS. Other handheld devices offer similar libraries to perform these operations. Once connected, commands are generated in the mobile handheld and transmitted to SD900. We will show examples of these commands.

    Every session begins by the mobile host initiating a discovery operation. This operation attempts to find and identify bluetooth devices within communication range. Identification is by bluetooth device address, a unique 6-byte identifier. Additonally, the mobile host may ask each responding bluetooth devices for its "friendly name" a user-programmable data string which can uniquely identify the bluetooth device.

    Based on the bluetooth device address, the friendly name and/or user input, the mobile host then attempts to establish and configure a wireless connection to the bluetooth device.

    Multiple connection types are possible depending on the data to be exchanged. The Scanning Devices Bluetooth Industrial I/O uses an L2CAP connection.

    Successful completion of discovery and connection creates a piconet. The mobile client is the master of the piconet and the server is the network slave. Once the piconet is established, the mobile host application can exchange commands and data with the Server.

    Back to the Table of Contents

    Commands and Responses, the Client Application

    A hypothetical example will demonstrate the SD900 Industrial I/O Server and we can use this example to illustrate the application's development.

    Assume a series of discrete sensors are connected to SD900 discrete inputs, representing level in a liquid container. Process Pump-in and Drain-out controls are connected to server's discrete outputs through suitable controllers that allow activation by small signal transistors. Perhaps these are auxiliary or override controls for supervisory adjustments. Finally, flow measurement instrumentation is connected to server's RS232 port.

    The Mobile Handheld client in bluetooth radio range establishes a piconet with SD900 Industrial I/O Server. The mobile client issues a command to read discrete inputs. The server responds with the state of the inputs. The Mobile application interprets the inputs for the user. The user then issues a command to set or clear discrete outputs to adjust pump-in or drain-out controls. The mobile user then issues a command to read RS232 input from flow measurementation. The mobile application interprets the response data. The mobile user then issues a command to send RS232 data to the Flow Instrumentation for control adjustments.

    Satisfied with the situation, the mobile handheld user disconnects to end the wireless session. The user has monitored and adjusted a process using bluetooth wireless communications without physical access to the location.

    Simple? It seems so. But what the application does not do is also interesting. In the sample application, we have not specified the user interface, not logged the session on the handheld or used a database to check data or generate commands in response to data. All these are application extensions which integrate the wireless capability with the user's system. These extensions can be implmented without special Bluetooth Wireless or SD900 related programming.

    In the next session we will show how this application was developed.

    Back to the Table of Contents

    Application Development for the Wireless Application

    This section presents snippets of code to illustrate command generation and response processing. The complete Metrowerks Code Warrior Project for the SD900 Demo Application is available from Scanning Devices and may provide a basis for your application development. To obtain a copy, email Scanning Devices with "Request SD900 Project" in the subject line.

    A major part of application development involves defining and implmenting a user and/or database interface. These tasks are not described here, as we assume that the application developer has their own favorite ways of developing these as well as unique requirements for processing and storage.

    The wireless portion of the application depends on the libraries and library support function available on the bluetooth equipped host. Bluetooth options for PC's and Handheld devices are typically provided with software libraries providing access to bluetooth radios for performing discovery, establishing connections, transmitting and receiving data packets with defined formats. Libraries vary from product to product, so you should consult your library provider's documentation for specific information.

    Scanning Devices has experience in direct RS232 communications with Bluetooth modules using C. If you need help at that level, please contact us.

    We will use the Palm OS bluetooth library to illustrate the structure of the application example. Much detail such as error handling has been omitted for clarity.

    The application performs 10 steps. Here they are, with user action and application processing for each step to follow.

  • 1. Open the Bluetooth Library.
  • 2. Perform Discovery,
  • 3. Make a Connection.
  • 4. Define a function to Send Data.
  • 5. Send a Request for discrete inputs.
  • 6. Process the response with data.
  • 7. Send a Command to set discrete outputs.
  • 8. Send Data for Transmission on RS232 Port.
  • 9. Receive Data on the RS232 port.
  • 10. Close the connection and library.
  • Back to Menu

    Step 1: The application starts by presenting the user with an opening screen an a means of initiating bluetooth discovery.

    The application sets up Bluetooth parameters, finds the Bluetooth Library and opens or loads the library. Here is piece of code that does that.

    Populate Communications Parameters with default values

    gBTPrefs.UUIDorPSM = PSM; gBTPrefs.listenPSM = DEFAULT_PSM_VAL; gBTPrefs.connectPSM = DEFAULT_PSM_VAL; gBTPrefs.listenMTU = L2CAP_SOCKET_MTU; gBTPrefs.connectMTU = L2CAP_SOCKET_MTU; gBTPrefs.data = hex; StrCopy(gBTPrefs.customData, "0000100"); //Allocate a buffer for sending data gDataHandle = MemHandleNew(MAX_SOCKET_MTU + 1) ; Find the Library or load it if it can't be found if(SysLibFind(btLibName, &gBtLibRefNum)) error = SysLibLoad(sysFileTLibrary, sysFileCBtLib, &gBtLibRefNum );

    Register the callback function. The callback function is the receiver for the server's responses to commands. Responses are asynchronous so are returned as events. The callback function is the interface between the radio and the application these events. This statement associates the callback function we have named "BtLibMECallbackProc" with the library we have opened.

    BtLibRegisterManagementNotification (gBtLibRefNum, BtLibMECallbackProc, 0);

    At this point the library is opened and setup is complete. We have not made any transmissions yet.

    Back to Menu

    Step 2. Discovery

    The user starts discovery. The library does the work. This function is an example from the Palm OS library.

    BtLibDiscoverSingleDevice ( gBtLibRefNum, // btLibRefNum NULL, // instructionTxt NULL, // deviceFilterList 0, // deviceFilterListLen &gBdAddr, // selectedDeviceP false, // addressAsName false // showLastList );

    Depending on the parameters chosen for this function, the mobile handheld requests the bluetooth device address and local name and presents the user with selection options. The next step is to make a connection to the selected device.

    Back to Menu

    Step 3. Connect

    From the list of bluetooth devices, the user selects the one he wants. In this application it must be a Scanning Devices Bluetooth Industrial I/O Server.

    The application attempts to create ACL and L2CAP connections to the selected device.

    BtLibLinkConnect(gBtLibRefNum, &gBdAddr);

    The response to this command comes as an event handled by the callback function registered when the library was opened. In step 1, we registered the callback function. Here is the declaration for the callback function:

    BtLibMECallbackProc(BtLibManagementEventType *mEventP, UInt32 refCon);

    Palm OS calls this function with an event containing information about the response received from the remote bluetooth device. We have to write this function to interpret the response and take action.

    Here is the processing for the BtLibLinkConnect response. Error handling has been omitted for clarity. The more complete callback function is described later.

    switch ( mEventP -> event ) { //other cases omitted for clarity case btLibManagementEventACLConnectInbound: case btLibManagementEventACLConnectOutbound: if(mEventP->status == btLibErrNoError) { BtLibSocketConnectInfoType connectInfo; Err error ; //Create an SDP socket BtLibSocketCreate(gBtLibRefNum, &sdpSocket, BtLibSocketCallbackProc, 0, btLibSdpProtocol); //Create an L2CAP socket BtLibSocketCreate(gBtLibRefNum, &Socket[gSocketCount].number, BtLibSocketCallbackProc, 0, btLibL2CapProtocol); //Fill in the identifying data connectInfo.remoteDeviceP = &gBdAddr; connectInfo.data.L2Cap.remotePsm = gBTPrefs.connectPSM; connectInfo.data.L2Cap.localMtu = gBTPrefs.connectMTU; connectInfo.data.L2Cap.minRemoteMtu = L2CAP_SOCKET_MTU; //Connect BtLibSocketConnect(gBtLibRefNum, Socket[gSocketCount].number, &connectInfo); //error handling and housekeeping here

    If all goes according to plan, the user is connected. Remember error detection and handling is omitted here.

    Back to Menu

    Step 4: Establish a function to send data from the example application.

    This is the complete function to send data based on the opening of an L2CAP connection to the Scanning Devices server that we did in step 3. This functions sends data in the variable

    gBTPrefs.customData on the L2CAP connection. Notice that error handling takes up most of the space. static Err SendData(Byte nCount) { Char *dataPtr; Err error=0; // Send data over a L2CAP socket if (gSocketCount == 0) { error = btLibErrSocketChannelUnavailable; return error; } if (gDataHandle == NULL) { // Previous attempt to allocate a buffer failed error = btLibErrOutOfMemory; return error; } // Get a pointer to the buffer. Important - this is kept locked until // the btLibSocketEventSendComplete callback is received! dataPtr = MemHandleLock(gDataHandle); // Send the data specified in the Preferences MemMove(dataPtr, gBTPrefs.customData, nCount); error = BtLibSocketSend(gBtLibRefNum, Socket[gSelectedSocket].number, (UInt8*) dataPtr, nCount); if ( error != btLibErrPending ) { // if not btLibErrPending unlock the data handle MemHandleUnlock(gDataHandle); } return (error); }

    Back to Menu

    Step 5: Request the state of the discrete inputs to examine level sensors (from the example application). We use the SendData function previously defined. BD_GET_DISCRETE_INPUTS is a macro we have defined for readability. It is a single ascii character, the letter E, specified in a macro definition. This function sends 1 byte through the channel.

    #define BD_GET_DISCRETE_INPUTS 'E' static void SD900SLGetDiscreteInputs(void) { gBTPrefs.customData[0] = BD_GET_DISCRETE_INPUTS; SendData(1); }

    Back to Menu

    Step 6. The callback function handles the response. We don't wait for the response in the application as it would lock up the system whle waiting.

    Here is the relevant section of the callback function. The response event delivers two bytes. The first, dataP[0], is the response code, a single ascii character defined in the macro:

    #define BD_GET_DISCRETE_INPUTS_RESPONSE 'F' The second byte, dataP[1], is the state of the discrete inputs, each input represented by one of the eight bits of the byte. Notice that after we get the response and data, we spend more effort to present it to the user. The data is interpreted as "R" or "G" for ON and OFF and used to construct a graphic showning the level as red or green. A Palm OS User Interface function not related to wireless communications is used to put up the graphic. BtLibSocketCallbackProc(BtLibSocketEventType *sEventP, UInt32 refCon) { BtLibDeviceAddressType bdAddr; BtLibSocketConnectInfoType connectInfo; Err error; UInt16 i; switch(sEventP->event) { //other cases omitted for now. case btLibSocketEventData: if(sEventP->status == btLibErrNoError) { BytePtr dataPtr ; dataPtr=(BytePtr)sEventP->eventData.data.data; switch(dataPtr[0]) { case BD_GET_DISCRETE_INPUTS_RESPONSE: gPrefs.discreteInputs[7][0]=(dataPtr[1]& 0x80) ? 'R':'G'; gPrefs.discreteInputs[6][0]=(dataPtr[1]& 0x40) ? 'R':'G'; gPrefs.discreteInputs[5][0]=(dataPtr[1]& 0x20) ? 'R':'G'; gPrefs.discreteInputs[4][0]=(dataPtr[1]& 0x10) ? 'R':'G'; gPrefs.discreteInputs[3][0]=(dataPtr[1]& 0x08) ? 'R':'G'; gPrefs.discreteInputs[2][0]=(dataPtr[1]& 0x04) ? 'R':'G'; gPrefs.discreteInputs[1][0]=(dataPtr[1]& 0x02) ? 'R':'G'; gPrefs.discreteInputs[0][0]=(dataPtr[1]& 0x01) ? 'R':'G'; { Word i; for(i=1; i<=8; i++) { CtlSetLabel( GetObjectPtr( focusToIDGetDiscreteInputs[i].ID), focusToIDGetDiscreteInputs[i].Text); } } break ;

    After all this, the user sees a graphic representing the discrete inputs, Red for ON, Green for OFF. It is up to your application to interpret and display data in a manner meaningful to the user.

    Back to Menu

    Step 7. Set the discrete outputs. Since we showed how to interpret the response data in step 6, we won't bother to show how to construct the transmitted data from user input. Assume we have one byte representing the states of the 8 discrete outputs that the user wants to set. Here is the code to send the byte.

    The first byte is the command, the ascii character M. Again we defined a macro for readability:

    #define BD_SET_DISCRETE_OUTPUTS 'M'

    The second byte val has the 8 discrete output states.

    static void SD900SLSetDiscreteOutputs ( Byte val ) { gBTPrefs.customData[0] = BD_SET_DISCRETE_OUTPUTS; gBTPrefs.customData[1] = val; SendData(2); }

    Remember that the server generates a response for every command that must be handled. This is done with another case in the callback function.

    These macros define two server responses:

    #define BD_SET_DISCRETE_OUTPUTS_RESPONSE 'N' #define BD_SET_TRANSMIT_DATA_RESPONSE 'T'

    The application shows the user that a response has been received.

    case BD_SET_DISCRETE_OUTPUTS_RESPONSE: case BD_SET_TRANSMIT_DATA_RESPONSE: ShowStatus("Response received."); break;

    Back to Menu

    Step 8. The user sends some data to instrumentation using the server's RS232 port. The infrastucture is in place. Assume the data to send is dataP (dataP is a pointer to the data) and is of length dataL. This function constructs the message consisting of the command followed by the data and sends it.

    The command is a single ascii byte defined by the macro

    BD_SET_TRANSMIT_DATA. static void SD900SLSetTransmitData(BytePtr dataP, Word dataL) { gBTPrefs.customData[0] = BD_SET_TRANSMIT_DATA; MemMove(gBTPrefs.customData+1, dataP, dataL); SendData(1+dataL) ; }

    The callback function handles the response in same way it handled the response to the set discrete output command. It displays the acknowledgement to the user.

    Back to Menu

    Step 9. By now you are certainly catching on. The user wants to read data transmitted by the instrumentation connected to the server's RS232 port, probably to get a response to the data just transmitted.

    Here is the code to send the command.

    #define BC_GET_RECEIVE_DATA 'I' static void SD900SLGetReceiveData(void) { gBTPrefs.customData[0] = BD_GET_RECEIVE_DATA; SendData(1); }

    The response command has been defined:

    #define BD_GET_RECEIVE_DATA_RESPONSE 'J'

    The response is handled in the callback function. The server reads data from its input buffer and constructs a message with the command followed by data. The callback function puts it in a Palm OS User Interface Field. The application displays it or performs other processing with the response.

    case BD_GET_RECEIVE_DATA_RESPONSE: ShowStatus("Response received."); SetFieldHandle(GetObjectPtr(GetReceiveDataDataField), (CharPtr)(dataPtr+1), sEventP->eventData.data.dataLen - 1); break;

    Back to Menu

    Step 10. The user is finished and want to disconnect from the server. The application has to close the socket and connection. Some housekeeping is required.

    error = BtLibSocketClose(gBtLibRefNum, Socket[gSelectedSocket].number); if(error == btLibErrNoError) { //Shift up the sockets in the array for (i = gSelectedSocket; i< gSocketCount-1; i++) { Socket[i].number = Socket[i+1].number; StrCopy(Socket[i].description,Socket[i+1].description); } //Decrement the number of open sockets gSocketCount--; //Redraw the list of sockets, with the first socket displayed gSelectedSocket = 0; gSelectedSocket = gSocketCount-1; } error = BtLibLinkDisconnect(gBtLibRefNum, &gBdAddr); //Handle any errors

    The wireless session is done.

    This application example demonstrates some of Scanning Devices Bluetooth Industrial I/O server capability. The complete C source code for the SD903STR Palm OS application that exercises all of the server's function is available at Scanning Devices website. You can request it with an email to Scanning Devices with "Request SD900 Project" in the subject line. The 10 step code examples have been extracted from the SD903STR project.

    Back to the Table of Contents

    The users guide continues in part 3.