Serial Communication with Palm OS - Part 1/4

The Palm OS Application

17.11.2019

This page describes how to write a small Palm-OS-Application, which is able to send some data via the serial interface.

The code is fully compilable with the tools, I mentioned here: Compiling and bulding Palm-OS-Applications on Ubuntu 18.04 LTS (64 Bit)

Feel free to make your own copy and edit it in a way you want/need.

File-Structure

In order to build a functional Palm-OS-Application, three files are nedded:

Header-File

This is the header-File of the application

#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% this are definitions for forms, buttons, menus and Alerts. "appFileCreator" needs some attions, because this is an ID, wich 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, which make sure, that there was only one unique ID for each application. Of course this service is not online anymore. So today is pure luck if it is working with other applications or not.

Resources-File

This is the resources-File of the application

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 in order 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 in order 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 are all UI-elements are defined and their parameters like titles and locations.

c-File

This is the c-File with all the code, which contains the "business-logic" of the application.

#include "serialcomm.h"
#include <UI/UIPublic.h>
#include <System/SystemPublic.h>
#include <PalmCompatibility.h>
#include <PalmTypes.h>

// Here is the used baudrate defined. It is very important that this value matches the baudrate 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 prefined 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 light modification of the SendDataSerial-funciton is need, this is why the code is sperate
                        SrmReceiveFlush(comPort, 0);
                        SrmSend(comPort, hexVarPtr, 11, &err);
                        // The "\n"-char gives the command to acutally send the data
                        SrmSend(comPort, "\n", 1, &err);

                        break;

                case PredefinedStringButton:
                        SendDataSerial(preDefStrPtr);
                        break;

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

                        // This avoid 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 packaga 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 gets started on a devices, which is 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.

Make always sure the baud rate is configured on all takers (Client/Server, Palm/PC) the same, 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-commandline:
Serial Communication with Palm OS on Ubuntu 18.04 and the commandline


Misc