Pebbles PC
SlideShowCmd
RemoteCmd
RemoteClipBook
WebAssist
PebblesChat
Scribble
Switcher
MultiCursor
ShortCutter
ButtonScrl
RateScrl
SlideScrl

Pebbles Software Architecture

From the Pittsburgh Pebbles PDA Project     

by Rob Miller, Carl Evankovich, Benjamin Bostwick, Brad Myers

1. Purpose

The Pebbles architecture supports adding in new applications both on the PC and on the PDA side. We also provide a sample application called "Ping" with full source code, as a guide for others to develop new applications.

2. Ping Sample Program

The Ping application demonstrates simple two-way communication. When you press the button on the PDA screen, both the PC and the PDA should beep. The PC beeps because it receives a message from the PDA ("ping"), and the PDA beeps because the PC sends back a reply message ("pong"). If you hear both beeps, then both directions of communication are working.

2.1. Downloading Ping

The Ping example application, including source code, is available in a ZIP archive:

2.2. Understanding Ping

Like all Pebbles applications, Ping consists of two components: an client that runs on your PDA, and a plugin that runs on your PC. In the ZIP file, you will find two versions of the client, one for Palm and one for Windows CE. There are also two versions of the plugin. One version is a DLL (dynamic link library) that is loaded dynamically into PebblesPC and communicates with PebblesPC using Windows messages. The other version of the plugin is a separate executable that runs in a different process and communicates with PebblesPC through a socket.

Here is a guide to major directories and source code files in the ZIP file:

Ping\
Top-level directory of the ZIP archive.
Pebbles.h
Defines a set of constants and macros shared by all Pebbles applications. The contents of the file are documented by the Pebbles Protocol section of this document.
PingMessages.h
Defines the message codes used specifically by Ping. The file is placed here in the top-level directory because both client and plugin must include it.
 
Palm\
Source code for the Palm client.
Ping.mcp
Project file for building Ping for the Palm for Metrowerks Codewarrior.
Ping.c
Creates the user interface for Ping, displaying a button on the Pilot screen and using the PebblesPilot library (below) to communicate with the Ping plugin.
PebblesPilot.c
Library of functions for sending and receiving Pebbles messages using serial port, infrared, or network. Written independently of Ping, it can be used by any Pebbles application.
PebblesPilot.h
Header file for PebblesPilot library
PC-dll\
Source code for the DLL plugin. Requires Microsoft Foundation Classes.
Ping.dsp
Visual C++ project file for building ping.dll.
Ping.cpp
Defines the CPing class which handles the plugin side of the Ping application.
Also defines the plugin's initialization routine PebblesMain().
Ping.h
Header file for Ping.cpp.
PC-exe\
Source code for the EXE plugin. Requires Microsoft Foundation Classes.
Ping.dsp
Visual C++ project file for building ping.exe.
Ping.cpp
The main program of the EXE plugin.
Ping.h
Header file for Ping.cpp.
PebblesSocket.cpp
Library of functions for sending and receiving Pebbles messages using Windows network sockets. Written independently of Ping, it can be used by any Pebbles application.
PebblesSocket.h
Header file for PebblesSocket library
WinCE\
Source code for the Windows CE / PocketPC client.
Ping.vcp
Project file for Microsoft Embedded Visual C++ to build the files for Windows CE/PocketPC.
Ping.cpp
Creates the user interface for Ping, displaying a button on the Windows CE screen and using the PebblesCE library (below) to communicate with the Ping plugin.
PebblesCE.cpp
Library of functions for sending and receiving Pebbles messages using serial port, infrared, or network. Written independently of Ping, it can be used by any Pebbles application.
PebblesCE.h
Header file for PebblesCE library

2.3. Compiling and Installing Ping

If you want to compile Ping, you'll need the right tools. We use the following tools:

  • Microsoft Visual C++ 6.0 with Microsoft Foundation Classes for developing PC-side plugins;
  • Metrowerks CodeWarrior Release 6 (MS Windows) for developing Palm clients;
  • Microsoft Embedded Visual C++ 3.0 for developing Windows CE and Pocket PC clients.

The ZIP file includes project files for these environments. The source code may work with other tools (such as gcc for the Pilot, or Borland C++ for Windows), but we haven't tried.

The ZIP file includes precompiled binaries if you want to try Ping without compiling it first. The binaries are located in the same places the compilers would put them, so you can just skip the compiling steps in the instructions below.

DLL Plugin

To compile and install the DLL plugin:

  1. Download and install Pebbles, if you haven't already.
  2. Load and compile the plugin's Visual C++ project file (Ping\PC-dll\Ping.dsw).
  3. Install the plugin (Ping\PC-dll\Debug\Ping.dll) by copying it into the directory containing PebblesPC.exe.
  4. Restart PebblesPC and make sure "Ping" appears in its Plugins dialog box.

EXE Plugin

To compile and install the EXE plugin:

  1. Download and install Pebbles, if you haven't already.
  2. Load and compile the plugin's Visual C++ project file (Ping\PC-exe\Ping.dsw).
  3. Run PebblesPC. Click the checkbox to allow network connections.
  4. Run Ping\PC-exe\Debug\Ping.exe (or just use the Run command in Visual C++).
  5. In the window that appears, go to the menubar and choose File/Connect. Messages should appear in the window indicating the progress of the connection.
  6. Once a connection has been made, go to PebblesPC's Plugins dialog box and confirm that the Ping plugin is listed and served from 127.0.0.1 (as opposed to "Local", which indicates a DLL plugin).

Palm Client

To compile and install the Palm client:

  1. Load and compile the client's CodeWarrior project file (Ping\Palm\Ping.mcp).
  2. Install the client (Ping\Palm\Ping.prc) on your PalmPilot with HotSync.

Windows CE Client

To compile and install the Windows CE client:

  1. Load the client's Visual Tools project file (Ping\WindowsCE\Ping.vcp).
  2. Choose the platform and CPU appropriate to your Windows CE device (e.g., PocketPC/MIPS).
  3. Build the project, and let Visual Tools download the client to your Windows CE device.

Once you have at least one client and one plugin installed correctly, you should be able to connect your PDA to the PC and run Ping on your PDA. Tapping on the "Ping" button should make the PC beep, followed a short time later by a beep from your PDA.

3. Pebbles Protocol

The communication protocol used by Pebbles applications is defined by the header file Pebbles.h (also available in the Ping ZIP archive as Ping\Pebbles.h). The following discussion essentially duplicates the comments in this header file.

A Pebbles application consists of two parts: a program running on a PDA (the "client"), and a program running on a desktop computer (the "plugin"). The client and the plugin communicate through a central mediator called PebblesPC, a process which runs on the desktop computer and multiplexes clients with plugins. The basic architecture looks like this:

						  +------------> DLL plugin
						  | Windows
						  | messages
						  |
    PDA client  <------------->  PebblesPC  <-----+
		  serial port			  |
		      or			  | network
		  network socket		  | socket
		      				  +------------> remote plugin process

PDA clients communicate with PebblesPC through some kind of byte stream (serial ports and network sockets are supported in the current implementation). PebblesPC communicates with plugins using either Windows event messages (for DLL plugins, which are loaded into PebblesPC's address space) or a network socket (for plugins running in a different address space or across the network).

This document defines the protocols for all three interfaces: the client protocol for the interface between clients and PebblesPC, the DLL plugin protocol for the interface between PebblesPC and DLL plugins, and the socket plugin protocol for the interface between PebblesPC and remote plugins.

PalmPilot developers should be aware of the distinction between a Pebbles plugin and a HotSync conduit. The Pebbles architecture is intended to support applications that need a live connection between the PDA and the PC. Examples of such applications include chat and emulating the keyboard and mouse. A Pebbles plugin is ready to run at all times -- it responds immediately when a user connects a PDA to the PC and starts up the associated client on the PDA. HotSync conduits, on the other hand, are intended to synchronize data on the PDA with data on the PC, and are invoked only when the user initiates a HotSync.

3.1. Client Protocol

Connecting to PebblesPC

As indicated in the previous section, the PDA client communicates with PebblesPC through a byte stream. PebblesPC currently supports two byte stream interfaces:

  • Serial port. If the client connects to PebblesPC through a serial port, the client should be configured for 57600 bps (PEBBLES_BAUDRATE in Pebbles.h), 8 data bits, 1 stop bit, no parity. Hardware flow control may optionally be enabled if the handheld device needs it to keep up (see the CMD_PEBBLES_CHANGE_PLUGIN and CMD_PEBBLES_FLOW_CONTROL messages). Palms typically require hardware flow control at this speed.
  • Network socket. If PebblesPC is allowing network connections (controlled by a checkbox on the main PebblesPC dialog), it listens for client connections via TCP on port 4343 (PEBBLES_APP_PORTNUM in Pebbles.h).

Client Message Format

The message protocol is the same regardless of whether the client connects through a serial port or a network socket. Messages are passed back and forth on the byte stream using a simple format. Short messages (less than 64KB) look like this:

	command   1 byte            Command code (see below)
	length    2 bytes           Length of data field in bytes, LSB first
	data      <length> bytes    Data or arguments for command

  • The command code describes the purpose of the message. Several command codes are reserved by Pebbles for handshaking and identifying users and plugins; the rest are available for application use.
  • The length field immediately follows the command code. It represents the length of the upcoming data block in little-endian byte order (least significant byte first). If the data block is empty, the length is 0. The length can be no larger than 65534. To send a longer message, use the long message format described next.
  • The rest of the message is a data field, which is a block of bytes containing arbitrary data.

Pebbles.h defines several macros which are useful for parsing and assembling messages:

Parsing:
	PEBBLES_CMD(msg)	Returns the command code of a message
	PEBBLES_LEN(msg)	Returns the length of a message's data block
	PEBBLES_DATA(msg)	Returns a pointer to a message's data

Assembling:
	PEBBLES_HEADER_SIZE		# of bytes in a Pebbles short message header 
					   i.e., sizeof(command and length fields)
	PEBBLES_SET_CMD(msg, cmd)	Sets the command code of a message
	PEBBLES_SET_LEN(msg, len)	Sets the length of a message
	PEBBLES_DATA(msg)	        Returns a pointer to message data to be filled in

Long Message Format

For messages longer than 64KB, a long-message format is defined with a 32-bit length field:
	command   1 byte            Command code (see below)
	flag      2 bytes           Always 0xFFFF to indicate long message
	length    4 bytes           Length of data field in bytes, little-endian
	data      <length> bytes    Data or arguments for command

Several macros are provided for handling long messages:

        PEBBLES_LEN(msg) == PEBBLES_LONG_MESSAGE_SIZE
				Tests whether a message is short or long format
Parsing:
	PEBBLES_CMD(msg)	Returns the command code of a long message
	PEBBLES_LONG_LEN(msg)	Returns the length of a long message's data block
	PEBBLES_LONG_DATA(msg)	Returns a pointer to a long message's data

Assembling:
	PEBBLES_LONG_HEADER_SIZE	# of bytes in a Pebbles long message header
					   i.e., sizeof(command, flag, and length fields)
	PEBBLES_SET_CMD(msg, cmd)	Sets the command code of a long message
	PEBBLES_SET_LEN(msg, PEBBLES_LONG_MESSAGE_SIZE)	
					Sets the flag field to indicate a long message
	PEBBLES_SET_LONG_LEN(msg, len)	Sets the length of a long message
	PEBBLES_LONG_DATA(msg)	        Returns a pointer to message data to be filled in

Client Command Codes

The following command codes defined in Pebbles.h are reserved by PebblesPC for system commands:

	#define CMD_PEBBLES_NO_MESSAGE           0
	        // Reserved as a return value for PebblesReceiveHeader()

	#define CMD_PEBBLES_STARTUP              1
	        // Data: none
	        //
	        // Sent by PebblesPC to client when PebblesPC opens a serial port,
	        // just in case an client is listening at the other end.
	        // Client responds by sending CMD_PEBBLES_CHANGE_PLUGIN.

	#define CMD_PEBBLES_CHANGE_PLUGIN        2
	        // Data: 
                //   either no data
                //   or three fields:  plugin name (null-terminated string, case doesn't matter)
                //                     user name (null-terminated string)
                //                     hardware flow control, 1 byte (0 to disable, !0 to enable)
                //
	        // Sent by client to switch to a plugin.  If no data is sent with the message,
	        // the client is disconnected from its plugin.  Client should send a no-data
	        // CMD_PEBBLES_CHANGE_PLUGIN when it exits, to notify plugin that it's
	        // gone.

	#define CMD_PEBBLES_ACK_CHANGE_PLUGIN    3
	        // Data: none
	        //
	        // Sent by PebblesPC to acknowledge that it has found and
	        // connected to the plugin requested by CMD_PEBBLES_CHANGE_PLUGIN.
        	// Client should not send any user-defined commands
	        // until it sees this acknowledgement.

        #define CMD_PEBBLES_NO_ACK_CHANGE_PLUGIN    4
                // Data: none
                //
                // Sent by PebblesPC to inform the PDA that the requested
                        // plugin could not be found.

        #define CMD_PEBBLES_KEEPALIVE    5
                // Data: none
                //
                // Sent to ensure that connection between PDA and PC
                // is still active

        #define CMD_PEBBLES_FLOW_CONTROL 6
                // Data: 1-byte flag to turn on or off flow control (1 means on, 0 means off) 
                //
                // Sent by an app to change PebblesPC's output hardware flow control.
                // Flow control needs to be turned off for raw IR (which doesn't support it)
                // and turned on for regular serial ports.
                //
                // Hardware flow control is always re-enabled after every CHANGE_PLUGIN,
                // so the app must disable it after every CHANGE_PLUGIN if desired.

        #define CMD_PEBBLES_CHANGE_USERNAME	7
                // Data: user name
                //
                // Sent by app to PC and contains  user name 

	#define CMD_PEBBLES_RESERVED1            8
	        // Reserved because of a bug -- PalmPilot sometimes sends this byte
	        // when it opens its serial port.

	#define CMD_PEBBLES_RESERVED2            127
	        // Reserved because of a bug -- PalmPilot sometimes sends this byte
	        // when it closes its serial port.

	#define CMD_PEBBLES_HOTSYNC              -128
	        // Reserved because HotSync uses this byte to indicate the start of
	        // a Hotsync session.

To avoid conflicting with these and future system commands, Pebbles applications should limit their commands to the following range:

	#define CMD_PEBBLES_BASE	32	// smallest application-specific command
	#define CMD_PEBBLES_MAX		126	// largest application-specific command

Under the current protocol, only one Pebbles application can use the serial connection at a time, so application developers need not worry that their application-defined command codes will conflict with those of other applications.

3.2. DLL Plugin Protocol

A DLL plugin is dynamically loaded into PebblesPC. When the plugin is loaded, it spawns a Win32 thread in PebblesPC's address space and returns the thread handle to PebblesPC. All subsequent communication between PebblesPC and the plugin occurs by passing Windows events back and forth, using the Win32 system call PostThreadMessage.

Connecting to PebblesPC

A plugin should be compiled as a DLL which exports the following startup function for PebblesPC to call:

	extern "C" BOOL WINAPI PebblesMain (HWND hwnd, PebblesPlugin *pPlugin);

PebblesMain should perform initialization and create a thread for the plugin. The parameters are as follows:

  • hwnd -- handle of PebblesPC window (which may be useful to the plugin for creating dialog boxes)
  • pPlugin -- structure which plugin should fill with a description of itself. The format of the structure is shown below. Some fields are initialized with defaults, but pPlugin->thread should always be set.
    typedef struct _PebblesPlugin {
        unsigned long   thread;     // Plugin thread ID.
                                    // Plugin should always set this field!
    
        int             hasAbout;   // true if plugin has an About dialog box
                                    // and will respond to WM_PEBBLES_ABOUT event.
                                    // Defaults to false.
    
        int             hasPrefs;   // true if plugin has a configuration dialog box
                                    // and will respond to WM_PEBBLES_PREFS event.
                                    // Defaults to false.
    
        void *          hThread;    // Plugin thread handle (the return value of CreateThread).
                                    // Plugin should always set this field!
    } PebblesPlugin;
    

PebblesMain should return true if the plugin started successfully.

PebblesMain should start a running thread to serve as the plugin, and return the thread handle in pPlugin->thread. You can create a thread in a variety of ways under Win32: CreateThread(), _beginthread(), _beginthreadex(), or (using MFC) the class CWinThread are all possibilities.

When PebblesPC starts up, it attempts to load all DLLs located in the same directory as PebblesPC.exe and calls PebblesMain on each one. Plugins which were successfully found and loaded are displayed in the "Plugins..." dialog box.

Cautions For DLL Plugin Developers

DLL plugins run in the same address space as PebblesPC. As a result, DLL plugin developers should observe a few cautions:

  • If a plugin crashes, then PebblesPC also crashes. Windows enforces no memory protection between PebblesPC and the plugin, so either component can access the other's memory space freely (even if erroneously). Several of the events below include pointer arguments which point to data in the mutual address space, some of which point to internal data in PebblesPC (such as PebblesUser*). These pointers can be read and written as ordinary C pointers, so nothing prevents a plugin from scribbling over a PebblesUser data structure. Be careful!
  • Memory should be allocated with GlobalAlloc, and event receiver must free with GlobalFree. Some of the events below pass around Pebbles messages (as char pointers). These pointers should always be allocated with the Win32 API function GlobalAlloc. Other allocators (like malloc and new) should not be used, since PebblesPC and the DLL plugin may be using different runtime libraries, and hence different heaps. The recipient of the event is responsible for freeing the memory using GlobalFree.
  • Use unique resource names, not IDs. If a DLL plugin contains Windows resources (like dialog boxes), it should access them by a unique name, not by ID number. The reason for this is that Windows searches through all the loaded DLLs looking for resources -- including other plugins -- and returns the first matching resource found. So if a DLL plugin asks for dialog box #100, it's likely to get some other plugin's dialog box #100. The solution is to assign unique names to resources (like "ABOUT_PING"), and refer to resources by name. The Ping sample DLL demonstrates how to do this in MFC.

Plugin Event Codes

After a plugin has been started, PebblesPC communicates with it by sending events to its Windows event queue. A Windows event has three fields: MSG (an event code), WPARAM (a 32-bit argument), and LPARAM (another 32-bit argument). The MSG codes used by PebblesPC are defined below:

        #define WM_PEBBLES_BASE                (WM_USER + 4242)

        #define WM_PEBBLES_NEW_USER         (WM_PEBBLES_BASE +  0)
            // Sent by PebblesPC to plugin when a new user connects to the plugin.
            //
            //            wParam      (PebblesUser*) points to PebblesUser struct.
            //                        Pointer is valid as long as user remains
            //                        connected; plugin should not free it.
            //
            //            lParam        0

        #define WM_PEBBLES_DONE_USER        (WM_PEBBLES_BASE +  1)
            // Sent by PebblesPC to plugin when a user disconnects from the plugin.
            //
            //            wParam      (PebblesUser*) points to PebblesUser struct.
            //                        Pointer is valid as long as user remains
            //                        connected; plugin should not free it.
            //
            //            lParam        0

        #define WM_PEBBLES_RECEIVED        (WM_PEBBLES_BASE +  2)
            // Sent by PebblesPC to the plugin when a user-defined command is
            // received from a connected app.
            //
            //            wParam      (PebblesUser*) points to PebblesUser struct.
            //                        Pointer is valid as long as user remains
            //                        connected; plugin should not free it.
            //
            //            lParam      (char*) points to the message,
            //                        which starts with a CMD_ code and a
            //                        length field, in the format described above.
            //                        Plugin must free this block with GlobalFree.

        #define WM_PEBBLES_SEND            (WM_PEBBLES_BASE +  3)
            // Sent by plugin to PebblesPC to send a message back to the app.
            // The plugin should send this message with PostThreadMessage()
            // using the thread handle in the PebblesUser structure.
            //
            //            wParam      (PebblesUser*) points to PebblesUser struct.
            //                        Pointer is valid as long as user remains
            //                        connected; plugin should not free it.
            //
            //            lParam      (char*) points to the message,
            //                        which starts with a CMD_ code and a
            //                        length field, in the format described above.
            //                        Must be allocated with GlobalAlloc.
            //                        PebblesPC will free this block with GlobalFree.

        #define WM_PEBBLES_ABOUT            (WM_PEBBLES_BASE +  6)
            // Sent by PebblesPC to plugin to display an About box describing
            // the plugin. Plugins with no About box can beep or do nothing.
            //
            //            wParam        (HWND) PebblesPC window handle
            //
            //            lParam        0

        #define WM_PEBBLES_PREFS            (WM_PEBBLES_BASE +  7)
            // Sent by PebblesPC to plugin to display an optional configuration
            // dialog for the plugin.  Plugins with no configuration can beep
            // or do nothing.
            //
            //            wParam        (HWND) PebblesPC window handle
            //
            //            lParam        0


        #define WM_PEBBLES_SOCKET_PLUGIN_NAME            (WM_PEBBLES_BASE +  8)
            // Sent by PluginData to notify Pebbles that it has received a name
                // and is ready to be added to the plugin list
            //
            //            wParam        (CPebblesPluginData *) Pointer to Plugin Data class
            //
            //            lParam        0

Several of these events include a pointer to PebblesUser, which is a structure describing a Pebbles user. The most important field of PebblesUser is thread, which is the handle of a PebblesPC thread that may be used to send messages back to the PDA corresponding to this user.

	typedef struct _PebblesUser {
	    int id;               // Small integer corresponding to user's serial
                                  // connection (numbered from 0).

	    char *name;           // User name (null-terminated; may be zero
                                  // length, but never NULL).

	    unsigned long thread; // User thread ID; outgoing
        	                  // WM_PEBBLES_SEND events should be posted
                	          // here.
	} PebblesUser;

3.3. Socket Plugin Protocol

PebblesPC also supports plugins running in a remote address space, either on the same machine or across the network, communicating by a network socket. If you have an existing executable program that you want to act as a Pebbles plugin, then you want to use this protocol.

A simple implementation of the socket plugin protocol is provided with the Ping sample application (Ping\PC-exe\PebblesSocket.cpp and Ping\PC-exe\PebblesSocket.h).

Connecting to PebblesPC

To connect to PebblesPC, call PebblesSocketConnect(). This section explains how the connection process works.

The remote plugin originates the connection to PebblesPC. If PebblesPC is allowing network connections (controlled by a checkbox on the main PebblesPC dialog), it listens for plugin connections on port 4242 (PEBBLES_PLUGIN_PORTNUM in Pebbles.h).

After opening a connection to PebblesPC, the plugin sends a CMD_SOCKET_PLUGIN_NAME message to provide its name to PebblesPC.

Message Format

Messages in the socket plugin protocol are similar to the client protocol. Socket plugin messages have one additional field after the header:

Short messages (less than 64KB):
	command   1 byte            Command code (see below)
	length    2 bytes           Length of data field in bytes, LSB first
	userid    1 byte            User ID
	data      <length> bytes    Data or arguments for command

Long messages:
	command   1 byte            Command code (see below)
	flag      2 bytes           Always 0xFFFF to indicate long message
	length    4 bytes           Length of data field in bytes, little-endian
	userid    1 byte            User ID
	data      <length> bytes    Data or arguments for command
  • The userid identifies the user from whom the message originated, if the message is incoming, or to whom the message should be sent, if the message is outgoing. PebblesPC chooses the userid for every connected client, a small integer which is guaranteed to be constant and unique for as long as the client is connected. As soon as a client disconnects from PebblesPC, however, its userid may be reused.

    Two special values are defined for the userid field:

    	#define PEBBLES_USERID_ALL_USERS	0
    	#define PEBBLES_USERID_NO_USER		255
    

    NO_USER should be used for control messages intended for PebblesPC, such as CMD_SOCKET_PLUGIN_NAME. ALL_USERS can be used to broadcast a message to all users connected to this plugin.

Messages can be read and written using PebblesSocketRead() and PebblesSocketWrite(). These functions read and write full messages.

For parsing and assembling socket plugin messages, Pebbles.h defines several macros. For the command and length fields, see the macros for the client protocol.

	PEBBLES_GET_USERID(msg) Returns the userid field of any kind of message
	PEBBLES_SET_USERID(msg, userid) Sets the userid field of any kind of message
	PEBBLES_SOCKET_MESSAGE(msg)	Returns a pointer to a socket plugin message's data

Socket Command Codes

The following command codes defined in Pebbles.h are used for the socket plugin protocol.

// sent to socket plugin when a new user arrives
#define CMD_SOCKET_NEW_USER	9
	// Data: user name (no null terminator)

// sent to socket plugin when a user leaves
#define CMD_SOCKET_DONE_USER	10

// sent by socket plugin to announce its name to PebblesPC 
#define CMD_SOCKET_PLUGIN_NAME 12
	// Data: plugin name (no null terminator)

4. Example Interaction

In this section, we illustrate the Pebbles protocol with a complete example using Ping. This example shows how PebblesPC starts up, how it finds and loads a DLL plugin, how it forwards messages between a client and the plugin, and how everything shuts down. The flowchart below depicts the three software entities (client, PebblesPC, and plugin) and the messages that pass between them.

Ping Client
(on PDA)

PebblesPC
(on PC)

Ping Plugin
(DLL on PC)
1. User executes PebblesPC.exe on the PC.
	 |
	 |
	\|/
2. PebblesPC searches for DLLs in its executable's directory and tries to call PebblesMain on each one.
  PebblesMain()
----------------->
3. Plugin creates a thread, performs any needed initialization, and returns the thread handle to PebblesPC.
	 |
	 |
	\|/
5. No Pebbles client is currently running on the PDA, so the message is ignored. (If a Pebbles client were running, it would try to connect to its plugin as shown in the next step.)
CMD_STARTUP
<-----------------
4. PebblesPC opens its serial port(s) and sends out a startup message, in case a PDA is already connected and waiting on the other end.
<-----------------


6. User switches to Ping client on the PDA.
	 |
	 |
	\|/
7. Ping client opens the PDA serial port and requests a connection to its plugin.
CMD_CHANGE_PLUGIN
Data: "Ping"
----------------->
8. PebblesPC finds "Ping" in its list of loaded DLLs and sets up a virtual connection between the PDA and the plugin.
	 |
	 |
	\|/

    
10. Ping client records that it has successfully connected, and beeps to inform the user as well.
CMD_ACK_CHANGE_PLUGIN
<-----------------
9. PebblesPC notifies the client that it has successfully connected to a plugin.
	 |
	 |
	\|/

    
11. PebblesPC notifies the plugin that a new user has connected to it.
WM_PEBBLES_NEW_USER
----------------->

12. Ping plugin does nothing with this message. (Other plugins might respond by recording the user in a list, or sending some data to the client.)

13. User taps on
"Ping" button.
	 |
	 |
	\|/
14. Ping client sends an application-defined message to its plugin.
CMD_PING
----------------->
15. Since the command code is application-defined, PebblesPC simply forwards the whole message to the plugin using a Win32 event.
WM_PEBBLES_RECEIVED
----------------->
16. Ping plugin unpacks the client's message, and looks at the command code.
	 |
	 |
	\|/
19. Ping client responds to CMD_PONG message by beeping.
CMD_PONG
<-----------------
18. PebblesPC forwards the message to the client.
WM_PEBBLES_SEND
<-----------------
17. Ping plugin responds to CMD_PING by causing the PC to beep and constructing a CMD_PONG reply message. The reply message is passed back to PebblesPC as a Win32 event sent to the thread handle in PebblesUser.

20. User switches away from the Ping application.
	 |
	 |
	\|/
21. Ping disconnects from its plugin by sending an empty change-plugin message, then closes its serial port.
CMD_CHANGE_PLUGIN
Data: ""
----------------->
22. PebblesPC notifies the plugin that the user has disconnected.
WM_PEBBLES_DONE_USER
----------------->
23. Ping plugin does nothing with this message. (Other plugins might respond by removing the user from a list of current users.)

24. User exits PebblesPC.
	 |
	 |
	\|/
25. Before shutting down, PebblesPC tells each loaded plugin to clean up and exit using a standard Windows message.
WM_QUIT
----------------->
26. Ping plugin does any necessary cleanup.
	 |
	 |
	\|/
28. After detecting that all loaded plugins have exited, PebblesPC exits.
<-----------------
27. Ping plugin exits.

Back to the Pebbles software Page

Back to the Pebbles main Page

Maintained by: Robert Miller