Category: Projects Related to Palm OS PDAs

Serial Communication with Palm OS - Part 1/4

The Palm OS Application

Published on: 2019-11-17

This page describes how to write a small Palm OS application that can send some data via the serial interface.

The code is fully compilable with the tools mentioned here: Compiling and building Palm OS applications on Ubuntu 18.04 LTS (64 Bit).

Feel free to make your own copy and edit it as you want/need.

File Structure

To build a functional Palm OS application, three files are needed:

Header File

This is the header file of the application:

Show/hide source code
#ifndef SERIALCOMM_H_
#define SERIALCOMM_H_

#define appFileCreator			'TJS9'
#define appName					"SerialComm"
#define appVersionNum			0x01
#define appPrefID				0x00
#define appPrefVersionNum		0x01

#define MainForm				1000
#define PredefinedStringButton	1001
#define CustomStringButton		1002
#define PredefinedHexButton		1003
#define CustomStringField       1004

#define	MainMenu				1018
#define	MainOptionsHelpCmd		1019
#define	MainOptionsAboutCmd		1020

#define	HelpAlert				1050
#define	AboutApplicationAlert	1051
#define	OsVersionTooLowAlert	1052
#define	ErrorOccurredAlert		1053

#endif

90% of these are definitions for forms, buttons, menus, and alerts. "appFileCreator" needs some attention because this is an ID that identifies your application and must not be identical to other "appFileCreator" IDs, otherwise it can lead to data loss. There was a service from Palm that ensured there was only one unique ID for each application. Of course, this service is not online anymore. So today, it is pure luck if it works with other applications or not.

Resources File

This is the resources file of the application:

Show/hide source code
GENERATEHEADER "serialcomm_Rsc.h"
#include "serialcomm.h"

FORM ID MainForm AT (0 0 160 160)
MENUID MainMenu
BEGIN
  TITLE "Serial Comm. with Palm OS"
  LABEL "Send a predefined String:" AUTOID AT (CENTER 20) FONT 1
  BUTTON "Send" ID PredefinedStringButton  AT (CENTER 35 AUTO AUTO) FONT 0
  LABEL "Send a custom String:" AUTOID AT (CENTER 100) FONT 1
  FIELD ID CustomStringField AT (CENTER PREVBOTTOM+8 110 AUTO) FONT 0 UNDERLINED MAXCHARS 23
  BUTTON "Send" ID CustomStringButton  AT (CENTER 140 AUTO AUTO) FONT 0
  FRAME
  FRAME
  LABEL "Send predefined hex-values:" AUTOID AT (CENTER 55) FONT 1
  BUTTON "41 42 43 44 45 46 47 48 49 4A" ID PredefinedHexButton  AT (CENTER 75 AUTO AUTO) FONT 0
END

ALERT ID HelpAlert
INFORMATION
BEGIN
  TITLE "Help"
  MESSAGE "Help is available at\n"\
            "https://palm2000.de/\n"\
            "projects/\n"\
            "serialCommunication\nWithPalmOsPart1.php"
  BUTTONS "OK"
END

ALERT ID AboutApplicationAlert
INFORMATION
BEGIN
  TITLE "Serial Comm. with Palm OS"
  MESSAGE "This is a small demo-application to send some data over the serial interface of a Palm PDA.\n"
  BUTTONS "OK"
END

ALERT ID OsVersionTooLowAlert
ERROR
BEGIN
  TITLE "Palm OS Version too low!"
  MESSAGE "Palm OS 2.0 or greater is required to run this application."
  BUTTONS "OK"
END

MENU ID MainMenu
BEGIN
    PULLDOWN "Options"
    BEGIN
    MENUITEM "Help"         ID MainOptionsHelpCmd    "H"
      MENUITEM SEPARATOR
    MENUITEM "About this application"         ID MainOptionsAboutCmd    "A"
    END
END

ALERT ID ErrorOccurredAlert
ERROR
BEGIN
  TITLE "An error occurred!"
  MESSAGE "^1\n^2\n^3"
  BUTTONS "OK"
END

VERSION 1 "0.0.1"

LAUNCHERCATEGORY ID 1000 "Utilities"

ICON "serialcomm_22.bmp"
SMALLICON "serialcomm_9.bmp"

Here all UI elements are defined, along with their parameters like titles and locations.

C File

This is the C file with all the code, containing the "business logic" of the application:

Show/hide source code
#include "serialcomm.h"
#include <UI/UIPublic.h>
#include <System/SystemPublic.h>
#include <PalmCompatibility.h>
#include <PalmTypes.h>

// Here is the baud rate defined. It is very important that this value matches the baud rate of the server application
#define BAUDRATE 9600

UInt16 comPort = 0;

static Char* GetFromInputField(UInt16, UInt16);
static void SendDataSerial(void*);

// This function fetches the input from the "CustomStringField" and returns it
static Char* GetFromInputField(UInt16 formID, UInt16 fieldID)
{
        FormPtr formPtr;
        FieldPtr fieldPtr;
        UInt16 objectIndex;

        formPtr = FrmGetFormPtr(formID);
        objectIndex = FrmGetObjectIndex(formPtr, fieldID);
        fieldPtr = (FieldPtr)FrmGetObjectPtr(formPtr, objectIndex);
        return FldGetTextPtr(fieldPtr);
}

// This is the function with the business logic
static Boolean MainFormHandleEvent(EventType *event)
{

        //char hexVar[10] = {0x7E,0xFF,0x06,0x09,0x00,0x00,0x04,0x44,0xDD,0xEF};

        Boolean handled = false;
        Err err;
        char  *custStrFieldInputPtr;

        // This is the predefined hex codes and the predefined char array
        char hexVar[10] = {0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A};
        char preDefStr[31] = "A text from a Palm OS device.\0";

        char  (*hexVarPtr)[10];
        char  (*preDefStrPtr)[31];

        hexVarPtr = &hexVar;
        preDefStrPtr = &preDefStr;

        if(event->eType == frmOpenEvent) {

                FrmDrawForm(FrmGetActiveForm());
                handled = true;
        }

        if(event->eType == ctlSelectEvent) {

                switch (event->data.ctlSelect.controlID)
                {
                case PredefinedHexButton:

                        // For sending hex data, a slight modification of the SendDataSerial function is needed, this is why the code is separate
                        SrmReceiveFlush(comPort, 0);
                        SrmSend(comPort, hexVarPtr, 11, &err);
                        // The "\n" char gives the command to actually send the data
                        SrmSend(comPort, "\n", 1, &err);

                        break;

                case PredefinedStringButton:
                        SendDataSerial(preDefStrPtr);
                        break;

                case CustomStringButton:
                        custStrFieldInputPtr = GetFromInputField(MainForm,CustomStringField);

                        // This avoids problems when the input field is empty and the user hits "send"
                        if(!custStrFieldInputPtr) {
                                SendDataSerial("NULL");
                        }else{
                                SendDataSerial(custStrFieldInputPtr);
                        }

                        break;

                default:
                        break;
                }

        }

        return handled;
}

// This function sends text over the serial interface
static void SendDataSerial(void* value){

        Err err;

        SrmReceiveFlush(comPort, 0);
        SrmSend(comPort, value, StrLen(value), &err);
        SrmSend(comPort, "\n", 1, &err);
}

static Boolean AppHandleEvent(EventPtr event)
{
        FormType* formTypePtr;
        UInt16 formId;
        Boolean handled = false;

        if (event->eType == frmLoadEvent)
        {

                formId = event->data.frmLoad.formID;
                formTypePtr = FrmInitForm(formId);
                FrmSetActiveForm(formTypePtr);

                switch (formId)
                {
                case MainForm:
                        FrmSetEventHandler(formTypePtr, MainFormHandleEvent);
                        break;

                default:
                        break;
                }

                return true;
        }

        if (event->eType == menuEvent)
        {
                MenuEraseStatus(NULL);
                switch (event->data.menu.itemID)
                {
                case MainOptionsHelpCmd:
                        FrmAlert(HelpAlert);
                        handled = true;
                        break;

                case MainOptionsAboutCmd:
                        FrmAlert(AboutApplicationAlert);
                        handled = true;
                        break;

                default:
                        break;
                }

                return true;
        }

        return false;
}

static void AppEventLoop(void)
{
        EventType event;
        Err error;

        do
        {
                EvtGetEvent(&event, evtWaitForever);

                if (SysHandleEvent(&event)) {
                        continue;
                }

                if (MenuHandleEvent((void *)0, &event, &error)) {
                        continue;
                }

                if (AppHandleEvent(&event)) {
                        continue;
                }

                FrmDispatchEvent(&event);
        }
        while (event.eType != appStopEvent);
}

static Err stopApplication()
{
        Err returnCode;
        FrmCloseAllForms();
        returnCode = SrmClose(comPort);
        return returnCode;
}

static Err startApplication()
{
        Err returnCode = 0;
        returnCode = SrmOpen(serPortCradlePort, BAUDRATE, &comPort);

        if (returnCode == 0) {
                FrmGotoForm(MainForm);
        }

        return returnCode;
}

UInt32 PilotMain(UInt16 cmd, MemPtr cmdPBP, UInt16 launchFlags)
{
        UInt32 currentRomVersion;
        UInt32 requiredRomVersion;
        requiredRomVersion = 0x02000000;

        FtrGet(sysFtrCreator, sysFtrNumROMVersion, ¤tRomVersion);
        if (currentRomVersion < requiredRomVersion)
        {
                FrmAlert(OsVersionTooLowAlert);
                return(sysErrRomIncompatible);
        }

        if (cmd == sysAppLaunchCmdNormalLaunch)
        {
                Err returnCode;
                returnCode = startApplication();

                if (returnCode != 0)
                {
                        ErrAlert(returnCode);
                        return(returnCode);
                }
                AppEventLoop();

                returnCode = stopApplication();
                if (returnCode != 0)
                {
                        ErrAlert(returnCode);
                        return(returnCode);
                }
        }

        return 0;
}

The complete package is downloadable with a proper Makefile HERE.
The PRC file is also available: HERE.

Known Problems

This application only works with the old COM port (DE-9 connector). If the application is started on a device connected to a USB cable or cradle (the USB cable does not need to be plugged in!), the application crashes with a "Serial: timeout [...] (Ser 0305)" error message.

Always make sure the baud rate is configured the same on all devices (Client/Server, Palm/PC), e.g., 9600 [bits per second]. Otherwise, the transmission will fail.

The application was successfully developed and tested on a Palm m100 and POSE.

The next part (2/4) describes how the data from the application can be read on a Linux command line:
Serial Communication with Palm OS on Ubuntu 18.04 and the command line