#include #include #include #include #include #include // P4. Needed to get the ROM version #include "RFDemoRsc.h" #pragma bool on /*********************************************************************** * * Internal Constants * ***********************************************************************/ const unsigned long appFileCreator = 'STRT'; const unsigned short appVersionNum = 0x01; const unsigned short appPrefID = 0x00; const unsigned short appPrefVersionNum = 0x01; const unsigned long version20 = 0x02000000; #define BAUDRATE 19200 #define SerRcvQueueSize 100 #define POLLING SysTicksPerSecond() #define MASK(x) 0x0001&x #define START 0x02 #define END 0x03 /*********************************************************************** * * Global variables * ***********************************************************************/ static UInt serRefNum; static Byte localNodeAddr; /*********************************************************************** * * Internal Functions * ************************************************************************/ static void RFReceiveData (); static bool RFSend (Byte ToFrom, Char* data, UInt refNum); static void RFSendData (CharPtr data); static bool RFReceive(UInt refNum, Byte localAddr, BytePtr From, BytePtr PktNum, Char* retData); static bool NodeAddr(UInt refNum, BytePtr addr); static void RFConnect(); static void RFDisconnect(); static void PalmSleep (UInt sec); static void PrintInt (Long i, SWord x, SWord y); /*********************************************************************** * * FUNCTION: RomVersionCompatible * * DESCRIPTION: This routine checks that a ROM version is meet your * minimum requirement. * * PARAMETERS: requiredVersion - minimum rom version required * (see sysFtrNumROMVersion in SystemMgr.h * for format) * launchFlags - flags that indicate if the application * UI is initialized. * * RETURNED: error code or zero if rom is compatible * * REVISION HISTORY: * ***********************************************************************/ static Err RomVersionCompatible(DWord requiredVersion, Word launchFlags) { DWord romVersion; // See if we're on in minimum required version of the ROM or later. FtrGet(sysFtrCreator, sysFtrNumROMVersion, &romVersion); if (romVersion < requiredVersion) { if ((launchFlags & (sysAppLaunchFlagNewGlobals | sysAppLaunchFlagUIApp)) == (sysAppLaunchFlagNewGlobals | sysAppLaunchFlagUIApp)) { FrmAlert (RomIncompatibleAlert); // Pilot 1.0 will continuously relaunch this app unless we switch to // another safe one. if (romVersion < version20) { //Err err; AppLaunchWithCommand(sysFileCDefaultApp, sysAppLaunchCmdNormalLaunch, NULL); } } return (sysErrRomIncompatible); } return (0); } /*********************************************************************** * * FUNCTION: GetObjectPtr * * DESCRIPTION: This routine returns a pointer to an object in the current * form. * * PARAMETERS: formId - id of the form to display * * RETURNED: VoidPtr * * REVISION HISTORY: * * ***********************************************************************/ static VoidPtr GetObjectPtr(Word objectID) { FormPtr frmP; frmP = FrmGetActiveForm(); return (FrmGetObjectPtr(frmP, FrmGetObjectIndex(frmP, objectID))); } /*********************************************************************** * * FUNCTION: MainFormInit * * DESCRIPTION: This routine initializes the MainForm form. * * PARAMETERS: frm - pointer to the MainForm form. * * RETURNED: nothing * * REVISION HISTORY: * * ***********************************************************************/ static void MainFormInit(FormPtr frmP) { } /*********************************************************************** * * FUNCTION: MainFormDoCommand * * DESCRIPTION: This routine performs the menu command specified. * * PARAMETERS: command - menu item id * * RETURNED: nothing * * REVISION HISTORY: * * ***********************************************************************/ static Boolean MainFormDoCommand(Word command) { FormPtr frmP; Boolean handled = false; switch (command) { case MainOptionsAboutSitzmark: MenuEraseStatus(0); frmP = FrmInitForm(INFOForm); FrmDoDialog(frmP); FrmDeleteForm(frmP); handled = true; break; } return handled; } /*********************************************************************** * * FUNCTION: MainFormHandleEvent * * DESCRIPTION: This routine is the event handler for the * "MainForm" of this application. * * PARAMETERS: eventP - a pointer to an EventType structure * * RETURNED: true if the event has handle and should not be passed * to a higher level handler. * * REVISION HISTORY: * * ***********************************************************************/ static Boolean MainFormHandleEvent(EventPtr eventP) { Boolean handled = false; FormPtr frmP; FieldPtr fldP; CharPtr fldTextP; UInt fldIndex; switch (eventP->eType) { case menuEvent: return MainFormDoCommand(eventP->data.menu.itemID); case frmOpenEvent: frmP = FrmGetActiveForm(); MainFormInit( frmP); FrmDrawForm ( frmP); FrmSetFocus(frmP, FrmGetObjectIndex (frmP, RFDEMODATAENTRYField)); handled = true; break; case ctlSelectEvent: switch (eventP->data.ctlSelect.controlID) { case RFDEMOCLOSEButton: frmP = FrmGetActiveForm(); FrmDeleteForm(frmP); handled = true; eventP->eType = appStopEvent; break; case RFDEMOCLEARButton: frmP = FrmGetActiveForm(); fldIndex = FrmGetObjectIndex (frmP, RFDEMODATAENTRYField); fldP = (FieldPtr) FrmGetObjectPtr (frmP, fldIndex); FldDelete (fldP, 0, StrLen(FldGetTextPtr(fldP))); break; case RFDEMOSENDButton: frmP = FrmGetActiveForm(); fldIndex = FrmGetObjectIndex (frmP, RFDEMODATAENTRYField); FrmSetFocus(frmP, fldIndex); fldP = (FieldPtr) FrmGetObjectPtr (frmP, fldIndex); fldTextP = FldGetTextPtr(fldP); RFReceiveData(); // Its safe to get all data before sending RFSendData(fldTextP); handled = true; break; default: break; } break; default: break; } return handled; } /*********************************************************************** * * FUNCTION: AppHandleEvent * * DESCRIPTION: This routine loads form resources and set the event * handler for the form loaded. * * PARAMETERS: event - a pointer to an EventType structure * * RETURNED: true if the event has handle and should not be passed * to a higher level handler. * * REVISION HISTORY: * * ***********************************************************************/ static Boolean AppHandleEvent( EventPtr eventP) { Word formId; FormPtr frmP; if (eventP->eType == frmLoadEvent) { // Load the form resource. formId = eventP->data.frmLoad.formID; frmP = FrmInitForm(formId); FrmSetActiveForm(frmP); // Set the event handler for the form. The handler of the currently // active form is called by FrmHandleEvent each time is receives an // event. switch (formId) { case RFDEMOForm: { FrmSetEventHandler(frmP, MainFormHandleEvent); // RFConnect(); Open serial connection } break; default: // ErrFatalDisplay("Invalid Form Load Event"); break; } return true; } return false; } /*********************************************************************** * * FUNCTION: AppEventLoop * * DESCRIPTION: This routine is the event loop for the application. * * PARAMETERS: nothing * * RETURNED: nothing * * REVISION HISTORY: * * ***********************************************************************/ static void AppEventLoop(void) { Word error; EventType event; do { // EvtGetEvent(&event, evtWaitForever); EvtGetEvent(&event, 100); if (! SysHandleEvent(&event)) if (! MenuHandleEvent(0, &event, &error)) if (! AppHandleEvent(&event)) FrmDispatchEvent(&event); if (event.eType != appStopEvent) RFReceiveData(); } while (event.eType != appStopEvent); } /*********************************************************************** * * FUNCTION: AppStart * * DESCRIPTION: Get the current application's preferences. * * PARAMETERS: nothing * * RETURNED: Err value 0 if nothing went wrong * * REVISION HISTORY: * * ***********************************************************************/ static AppStart (void) { SysSetAutoOffTime (0); // Never power off RFConnect(); // Open serial connection NodeAddr(serRefNum, &localNodeAddr); return 0; } /*********************************************************************** * * FUNCTION: AppStop * * DESCRIPTION: Save the current state of the application. * * PARAMETERS: nothing * * RETURNED: nothing * * REVISION HISTORY: * * ***********************************************************************/ static void AppStop(void) { RFDisconnect(); } /*********************************************************************** * * FUNCTION: StarterPilotMain * * DESCRIPTION: This is the main entry point for the application. * PARAMETERS: cmd - word value specifying the launch code. * cmdPB - pointer to a structure that is associated with the launch code. * launchFlags - word value providing extra information about the launch. * * RETURNED: Result of launch * * REVISION HISTORY: * * ***********************************************************************/ static DWord StarterPilotMain(Word cmd, Ptr cmdPBP, Word launchFlags) { Err error; error = RomVersionCompatible (version20, launchFlags); if (error) return (error); switch (cmd) { case sysAppLaunchCmdNormalLaunch: error = AppStart(); if (error) return error; FrmGotoForm(RFDEMOForm); AppEventLoop(); AppStop(); break; default: break; } return 0; } /*********************************************************************** * * FUNCTION: PilotMain * * DESCRIPTION: This is the main entry point for the application. * * PARAMETERS: cmd - word value specifying the launch code. * cmdPB - pointer to a structure that is associated with the launch code. * launchFlags - word value providing extra information about the launch. * RETURNED: Result of launch * * REVISION HISTORY: * * ***********************************************************************/ DWord PilotMain( Word cmd, Ptr cmdPBP, Word launchFlags) { return StarterPilotMain(cmd, cmdPBP, launchFlags); } /*********************************************************************** * * Function: * * Description: * * Parameters: * * Returned: * * Revision History: * * ***********************************************************************/ static void RFReceiveData() { FormPtr frmP; FieldPtr fldP; UInt fldIndex; Byte from, pktNum; CharPtr curBuf; Char retData[36] = {'\0'}; static Char oldStr [36]; Char nl = '\n'; PrintInt(localNodeAddr, 130, 17); // Display the local node address frmP = FrmGetActiveForm(); if(RFReceive(serRefNum, localNodeAddr, &from, &pktNum, retData)) { PrintInt(from, 35, 17); // Display the sender node address fldIndex = FrmGetObjectIndex (frmP, RFDEMODATARCVField); fldP = (FieldPtr) FrmGetObjectPtr (frmP, fldIndex); curBuf = FldGetTextPtr(fldP); if(StrLen(curBuf) >= FldGetMaxChars(fldP)-30) // If not enough space for one full packet FldDelete (fldP, 0, StrLen(curBuf)); // clear buffer FldInsert(fldP, retData, StrLen(retData)); FldInsert(fldP, &nl, 1); } FrmSetFocus(frmP, FrmGetObjectIndex (frmP, RFDEMODATAENTRYField)); return; } static void RFSendData (CharPtr data) { // Local node address is 1, and receiver's is 2 Byte ToFrom = 0x21; // Char msg[] = "Can't send data!"; RFSend (ToFrom, data, serRefNum); return; } /***************************************************************** * Description: Send data from sender From to receiver To * Parameters: 'ToFrom' - Receiver (higher 4 bits) and * sender (lower 4 bits ) addresses * 'data' - Data to be sent * 'refNum' - Serial reference number * Returned: true if data successfully sent, false otherwise * Note: This function is designed to work with the RFM PC terminal * program. Therefore, the data portion must be enclosed between * the start and end symbols: 0x02, 0x03 respectively. These * symbols are not part of the protocol. * Also, the data size is limited to 30 bytes (the maximum data * size per packet is 32 bytes, and 2 of which are reserved for * the start and end symbols). ******************************************************************/ static bool RFSend(Byte ToFrom, Char* data, UInt refNum) { static Byte packetNum = 1; Err err = 0; Byte pSize, beginB, endB; ULong numBytes = 0; double begin, end; Byte serRcvQueue[100]; // Serial receive queue Byte newRcvBuf[50]; // Not yet open a serial connection // or no data or length > 30 if (refNum == 0 || data == NULL || StrLen(data) > 30) goto ERROR; pSize = StrLen(data) + 2; // 2 bytes of 0x02 and 0x03 beginB = 0x02; endB = 0x03; // err = SerSetReceiveBuffer(refNum, newRcvBuf, 50); // if (err) goto ERROR; SerSend (refNum, &ToFrom, 1, &err); // Check if RFM is busy by sending ToFrom byte if (err) goto ERROR; begin = TimGetTicks(); // Begin time in ticks // If RFM echoes back in less than 50 ms, got control of RFM do { end = TimGetTicks(); // End time in secs err = SerReceiveCheck(refNum,&numBytes); if (err) goto ERROR; } while (numBytes <= 0 && ((end - begin)/sysTicksPerSecond < 0.05)); // PrintInt (numBytes, 5, 50); if (numBytes > 0) // Got echo back from RFM { SerReceive(refNum, serRcvQueue, numBytes, 0, &err); if (err) goto ERROR; if (serRcvQueue[0] == ToFrom) // If echoed byte is ToFrom byte { // then it is ready for the rest of the packet SerSend (refNum, &packetNum, 1, &err); // Send the packet-number byte if (err) goto ERROR; SerSend (refNum, &pSize, 1, &err); // Send the packe-size byte if (err) goto ERROR; SerSend (refNum, &beginB, 1, &err); // Send the start symbol if (err) goto ERROR; SerSend (refNum, data, StrLen(data), &err); // Send real data if (err) goto ERROR; SerSend (refNum, &endB, 1, &err); // Send the end symbol if (err) goto ERROR; // Wait for echo-back packet of size of 3 bytes do { err = SerReceiveCheck(refNum, &numBytes); if (err) goto ERROR; // PrintInt (numBytes, 5, 70); if (numBytes >= 3) // Wait for ACK or NAK { Char s[10]; SerReceive(refNum, serRcvQueue, numBytes, 0, &err); if (err) goto ERROR; switch (serRcvQueue[2]) { case 0xE1: // The third byte of ACK is of the form 0xEn, case 0xE2: // where n=1-8, which is the number of retries case 0xE3: case 0xE4: case 0xE5: case 0xE6: case 0xE7: case 0xE8: // err = SerSetReceiveBuffer(refNum, newRcvBuf, 0); // if (err) goto ERROR; // Packet number must be between 1 and 7 if (++packetNum >= 8) packetNum = 1; return true; case 0xDD: goto ERROR; // The third byte of NAK is 0xDD default: goto ERROR; } } } while (numBytes < 3); } } ERROR: // SerSetReceiveBuffer(refNum, newRcvBuf, 0); // if (err) goto ERROR; if (err == serErrLineErr) SerClearErr(refNum); // Reset the serial port return false; // Data not successfully sent } /***************************************************************** * Description: Get the local node address * Parameters: 'addr' - Hold the local address value * 'refNum' - Serial reference number * Returned: true if local node address obtained, false otherwise * Note: This function aquires the RFM local node address at start * up by using the special packet [0x00 0x00 0x01 0x31] ******************************************************************/ static bool NodeAddr(UInt refNum, BytePtr addr) { Err err; Byte first = 0; Byte rest[] = {0x00, 0x01, 0x31}; double begin, end; ULong numBytes = 0; Byte serRcvQueue[100]; // Serial receive queue SerSend (refNum, &first, 1, &err); // Check if RFM is busy if (err) goto ERROR; begin = TimGetTicks(); // Begin time in ticks // If RFM echoes back in less than 50 ms, got control of RFM do { end = TimGetTicks(); // End time in secs err = SerReceiveCheck(refNum,&numBytes); if (err) goto ERROR; } while (numBytes <= 0 && ((end - begin)/sysTicksPerSecond < 0.05)); if (numBytes > 0) // Got echo back from RFM { SerReceive(refNum, serRcvQueue, numBytes, 0, &err); if (err) goto ERROR; if (serRcvQueue[0] == first) // If echoed byte is the first byte { // then send the rest SerSend (refNum, rest, 3, &err); if (err) goto ERROR; do { // Wait for the returned packet err = SerReceiveCheck(refNum, &numBytes); if (err) goto ERROR; if (numBytes >= 4) // Returned packet = 4 bytes { SerReceive(refNum, serRcvQueue, numBytes, 0, &err); if (err) goto ERROR; switch (serRcvQueue[3]) { case 0x35: // Node address returned *addr = serRcvQueue[0] & 0x0F; return true; default: goto ERROR; } } } while (numBytes < 4); } } ERROR: if (err == serErrLineErr) SerClearErr(refNum); // Reset the serial port return false; // Error occurs } /***************************************************************** * Description: Receive data from sender From * Parameters: 'From' - Sender node address * 'PktNum' - Packet number * 'retData'- Data returned * 'refNum' - Serial reference number * Returned: true if data successfully sent, false otherwise * Note: This function is designed to work with the RFM PC terminal * program. Therefore, the data portion of the received packet * is surrounded with start and end symbols: 0x02, 0x03. These * symbols are not part of the RFM protocol. ******************************************************************/ static bool RFReceive(UInt refNum, Byte localAddr, BytePtr From, BytePtr PktNum, Char* retData) { Err err; ULong numBytes = 0; ULong dataSize = 0; Char serRcvQueue[36] = {'\0'}; // Serial receive queue UInt i; // Check to see if anything is on the receive queue err = SerReceiveCheck(refNum, &numBytes); if (err) goto ERROR; if (numBytes >= 3) // Must be at least 3 bytes: ToFrom, PktNum, PktSize { SerReceive(refNum, serRcvQueue, 1, 0, &err); // Check if its To/From byte if (err) goto ERROR; if ((serRcvQueue[0] >> 4) == localAddr) // Get the To nibbles { SerReceive(refNum, serRcvQueue+1, 2, 0, &err); if(err) goto ERROR; *From = serRcvQueue[0] & 0x0F; // Get the From nibble *PktNum = serRcvQueue[1]; // Get packet number dataSize = serRcvQueue[2]; // Get data size SerReceive(refNum, serRcvQueue+3, dataSize, 100, &err); // Block 1s until receive all data if (err) goto ERROR; // Skip start and end symbols (0x02 and 0x03) for(i=0; i