A PDA itself is nice toy, maybe even useful for some common tasks such as ToDo or MemoPad applications, but without an option to communicate the to outer world, it's just peace of metal. In this set of articles, we will try to cover the main available communication types that you can use in your own applications.
Serial Manager
Serial Manager is simple in usage and its capabilities. The first thing I woild like to point out is that serial communications under Palm OS are synchronous. That keeps all the business really simple. All Serial Manager's API calls may be divided into several logical groups:
- Opening and Closing the Serial Port
- Status, DeviceInfo, and Configuration
- Sending and Receiving Data
Let's briefly consider all these function groups.
Group 1: Opening and Closing the Serial Port
As a starting point, you'll be required to open a serial port. Serial Manager API offers you several functions for this purpose:
Err SrmOpen(UInt32 port, UInt32 baud, UInt16 *newPortIdP)
Err SrmExtOpen(UInt32 port, SrmOpenConfigType* configP,
UInt16 configSize, UInt16 *newPortIdP)
Err SrmExtOpenBackground(UInt32 port, SrmOpenConfigType* configP,
UInt16 configSize, UInt16 *newPortIdP)
Err SrmOpenBackground(UInt32 port, UInt32 baud, UInt16 *newPortIdP)
All of them take a port number (either logical port number or 4-character port name) and return the Port ID for usage with other functions. You also may initially configure opening port providing either baud rate or configuration data. You will find common port constants in Table 1:
Constant | Value | Description |
---|---|---|
serPortLocalHotSync | 0x8000 | The physical HotSync port. The Serial Manager automatically detects whether this port is USB or RS-232. |
serPortCradlePort | 0x8000 | Cradle port. The Serial Manager automatically detects whether this port is USB or RS-232. Most applications should specify this as the port. |
serPortIrPort | 0x8001 | The IR port. This is a raw IrDA port with no protocol support. |
serPortConsolePort | 0x8002 | The debug console port, either USB or RS-232. USB is preferred where both are available. |
serPortCradleRS232Port | 0x8003 | Port for the RS-232 cradle. Specify this port if you want to ensure that your application uses RS-232 communications only. |
serPortCradleUSBPort | 0x8004 | Port for the USB cradle. Specify this port if you want to ensure that your application uses USB communications only. |
Using 4-character constants for physical hardware port names (refer to SystemResources.h for details) is not a good practice because it may be not present on some devices. There are also several virtual ports, as shown in Table 2:
Constant | Value | Description |
---|---|---|
sysFileCVirtIrComm | 'ircm' | A virtual serial cable over an IrDA link using the IRComm protocol. It can only be used to talk to another IRComm device. |
sysFileCVirtRfComm | 'rfcm' | RFCOMM (Bluetooth) virtual port plug-in. |
sysFileCBtConnectPanelHelper | 'btcp' | Bluetooth Connection Panel helper application. |
You can open a serial port in two different modes: foreground and background. Background ports relinquish control when another task opens the port with the SrmOpen or SrmExtOpen call. It's not recommended to keep a serial port in open mode longer than really neccessary because it consumes battery power. Finally, after the port is not needed, you may close it by making a SrmClose call.
SerOpen calls may return serErrAlreadyOpen error. It means that some other application is using it at this time. A good practice is to notify the user and close the port with SerClose.
Group 2: Configuration and Status
This functional group includes the following calls:
Err SrmGetDeviceCount(UInt16 *numOfDevicesP)
Err SrmGetDeviceInfo(UInt32 deviceID, DeviceInfoType *deviceInfoP)
Err SrmGetStatus(UInt16 portId, UInt32 *statusFieldP, UInt16 *lineErrsP)
Err SrmClearErr (UInt16 portId)
Err SrmSetReceiveBuffer(UInt16 portId, void *bufP, UInt16 bufSize);
Err SrmControl(UInt16 portId, UInt16 op, void *valueP, UInt16 *valueLenP)
Err SrmCustomControl(UInt16 portId, UInt16 opCode, UInt32 creator,
void* valueP, UInt16* valueLenP)
Here we will discuss only the SrmControl function. Through it, you can configure the serial port. The valueP parameter depends on the specified op code (see SrmCtlEnum for all possible values). The following sample code demonstrates a simple settings definition:
UInt32 dwFlags = srmSettingsFlagBitsPerChar8 | srmSettingsFlagStopBits1;
UInt16 wValueSize = sizeof(dwFlags);
SrmControl(srmCtlSetFlags, &dwFlags, wValueSize);
The SrmCustomControl call in turn gives you an opportunity to communicate to the virtual driver in response to the specific op codes. All the rest of the functions are trivial enough, so we will move to the next section.
Group 3: Sending and Receiving Data
A wide family of Send/Receive functions allows you to carry out data exchange in different ways. Actually, all functions in this group may be broken into several categories:
UInt32 SrmSend (UInt16 portId, const void *bufP, UInt32 count, Err *errP)
Err SrmSendWait(UInt16 portId)
Err SrmSendCheck(UInt16 portId, UInt32 *numBytesP)
Err SrmSendFlush(UInt16 portId)
UInt32 SrmReceive(UInt16 portId, void *rcvBufP, UInt32 count,
Int32 timeout, Err *errP)
Err SrmReceiveWait(UInt16 portId, UInt32 bytes, Int32 timeout)
Err SrmReceiveCheck(UInt16 portId, UInt32 *numBytesP)
Err SrmReceiveFlush(UInt16 portId, Int32 timeout)
Err SrmReceiveWindowOpen(UInt16 portId, UInt8 **bufPP, UInt32 *sizeP);
Err SrmReceiveWindowClose(UInt16 portId, UInt32 bytesPulled);
Err SrmSetWakeupHandler(UInt16 portId, WakeupHandlerProcPtr procP,
UInt32 refCon);
Err SrmPrimeWakeupHandler(UInt16 portId, UInt16 minBytes);
First, you can bluntly use a Send/Receive buffer. In addition, if you need to check the input/output queue or wait for completion of the transmition, xxxCheck and xxxWait will fulfill your requirements. xxxFlush purges the FIFO transmit queue.
Now, let's consider some RX queue-related issues. Its default size is 512 bytes; you may change it by a SrmSetReceiveBuffer call. Your application is responsible to release this buffer by setting its size to zero, because Serial Manager won't do it. If you're planning to transmit large amount of data, think about sending/receiving it by blocks, say 1K, otherwise buffer overflows, and you will get an error serLineErrorSWOverrun. You may want to know when Serial Manager sent all data or put something into receiving buffer. Well, you can do it pretty simply. In the case of reading data, that's probably the best way to handle it. Your application will need to pass two steps. The first one is to call SrmReceiveWait. This will block all the processes during the required timeout or data arriving. Note, that the timeout is applied for every new incoming byte; in other words, you may control maximum delay between two incoming bytes of data. In case of SrmSendWait "timeout" means "send buffer is empty." SrmReceiveWait also saves the PDA's battery because processor is put into a low-power state. The second step is trivial: Yes, you simply read data that is ready to be received. If there were any errors during transmitting, you should call SrmClearErr to clean them up. For lengthly Serial I/O operations, your application should take care of at least two things. One is to handle user input during transmitting, either by calling EvtSysEventAvail or EvtGetEvent. Doing it with at least every 'timeout' will make the user's life much happier. In addition, if you have an I/O session that should not be interrupted, you should prevent the PDA from switching to automatic sleeping mode, which occurs when no user input is available. Calling EvtReserAutoOffTimer once per minute is a good solution in such cases. Serial Manager also provides direct access to its receive queue. This method is not communly used, but it gives a "backdoor" solution. Thus, call SrmReceiveWindowOpen/SrmReceiveWindowClose when you really need it. And, finally, you can set up Serial Manager to notify your application when a preset amount of data is ready to be received. SrmSetWakeupHandler calls for a callback in your program (WakeupHandlerProcPtr proc parameter), which in turn gets refCon as the only parameter. The minimal thethreshold is defined by the SrmPrimeWakeupHandler function. To illustrate all the dry theory from above, take a look at the following example. For more details, please refer to the attached sample project.Some Samples
static void OnSerialIO()
{
FieldType * field = GetObjectPtr
if (field)
{
FldDelete(field, 0, 0xFFFF);
FldDrawField(field);
}
Char szError[128];
MemSet(szError,sizeof(szError),0);
Char szAnswer[128];
MemSet(szAnswer,sizeof(szAnswer),0);
Char szBuffer[128];
MemSet(szBuffer,127,0x30);
szBuffer[127] = 0;
MemMove(szBuffer,"Sample Message",StrLen("Sample Message"));
UInt16 dwSize = StrLen(szBuffer) + 1;
UInt16 nInsPos = 0;
Char szInfo[1024];
StrPrintF(szInfo,"PackSize = %u\nMessage = %s\n",dwSize,szBuffer);
if (field)
{
nInsPos = Log(field, szInfo, nInsPos);
}
UInt16 nPortId;
Err err = SrmOpen(0x8000,9600,&nPortId);
if ( err != errNone )
{
StrPrintF(szInfo,"SrmOpen returned error = 0x%X - %s\n",err,
GetSerialErrorText(err, szError));
if (field)
{
nInsPos = Log(field, szInfo, nInsPos);
}
return;
}
UInt32 nBytes = SrmSend(nPortId, &dwSize, sizeof(dwSize), &err);
StrPrintF(szInfo,"1. SrmSend returned %lu\n",nBytes);
if (field)
{
nInsPos = Log(field, szInfo, nInsPos);
}
if ( err != errNone )
{
StrPrintF(szInfo,"1. SrmSend returned error = 0x%X - %s\n",
err,GetSerialErrorText(err, szError));
if (field)
{
nInsPos = Log(field, szInfo, nInsPos);
}
return;
}
err = SrmReceiveWait(nPortId,3,5 * SysTicksPerSecond());
if ( err != errNone )
{
StrPrintF(szInfo,"1. SrmReceiveWait returned error =
0x%X - %s\n",err,GetSerialErrorText(err, szError));
if (field)
{
nInsPos = Log(field, szInfo, nInsPos);
}
return;
}
nBytes = SrmReceive(nPortId, (void*)szAnswer, sizeof(szAnswer),
5 * SysTicksPerSecond(), &err);
if ( err != errNone )
{
StrPrintF(szInfo,"1. SrmReceive returned error = 0x%X - %s\n",
err,GetSerialErrorText(err, szError));
if (field)
{
nInsPos = Log(field, szInfo, nInsPos);
}
return;
}
StrPrintF(szInfo,"1. SrmReceive returned buffer %s\n",szAnswer);
if (field)
{
nInsPos = Log(field, szInfo, nInsPos);
}
nBytes = SrmSend(nPortId, szBuffer, StrLen(szBuffer) + 1, &err);
StrPrintF(szInfo,"2. SrmSend returned %lu\n",nBytes);
if (field)
{
nInsPos = Log(field, szInfo, nInsPos);
}
if ( err != errNone )
{
StrPrintF(szInfo,"2. SrmSend returned error = 0x%X - %s\n",
err,GetSerialErrorText(err, szError));
if (field)
{
nInsPos = Log(field, szInfo, nInsPos);
}
return;
}
SrmClose(nPortId);
}
This snippet just transmits data by the simplest way. The sample project also contains the MFC application as PC-side stuff. Download the accompanying code's zip file here.Download
|