//--------------------------------------------------------------------------- // Copyright (C) 2000 Dallas Semiconductor Corporation, All Rights Reserved. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES // OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // // Except as contained in this notice, the name of Dallas Semiconductor // shall not be used except as stated in the Dallas Semiconductor // Branding Policy. //--------------------------------------------------------------------------- // // thermo21.c - Thermochron iButton utility functions // // Version: 2.00 // // History: // 1.03 -> 2.00 Reorganization of Public Domain Kit // Convert to global CRC utility functions // Y2K fix. #include "ownet.h" #include "thermo21.h" #include #include // Include files for the Palm and Visor #ifdef __MC68K__ #include #include #else #include #endif // structure to hold each state in the StateMachine typedef struct { int Step; char StepDescription[50]; } ThermoScript; // step constants enum { ST_SETUP=0, ST_READ_STATUS, ST_READ_ALARM, ST_READ_HIST, ST_READ_LOG, ST_CLEAR_MEM, ST_CLEAR_VERIFY, ST_WRITE_TIME, ST_WRITE_CONTROL, ST_WRITE_RATE, ST_FINISH, ST_GET_SESSION, ST_FIND_THERMO, ST_REL_SESSION, ST_READ_PAGES, ST_WRITE_MEM, ST_CLEAR_SETUP }; // status contants enum { STATUS_STEP_COMPLETE, STATUS_COMPLETE, STATUS_INPROGRESS, STATUS_ERROR_HALT, STATUS_ERROR_TRANSIENT }; // download steps static ThermoScript Download[] = {{ ST_READ_STATUS, "Setup to read the mission status"}, { ST_READ_PAGES, "Read the status page"}, { ST_READ_ALARM, "Setup to read alarm pages"}, { ST_READ_PAGES, "Read the alarm pages"}, { ST_READ_HIST, "Setup to read histogram pages"}, { ST_READ_PAGES, "Read the histogram pages"}, { ST_READ_LOG, "Setup to read log pages"}, { ST_READ_PAGES, "Read the log pages"}, { ST_FINISH, "Finished"}}; // read status only steps static ThermoScript GetStatus[] = {{ ST_READ_STATUS, "Setup to read the mission status"}, { ST_READ_PAGES, "Read the status page"}, { ST_FINISH, "Finished"}}; // mission steps (assume already did StatusThermo) static ThermoScript Mission[] = {{ ST_CLEAR_SETUP, "Setup clear memory"}, { ST_WRITE_MEM, "Write clear memory bit"}, { ST_CLEAR_MEM, "Clear the memory"}, { ST_READ_STATUS, "Setup to read the mission status"}, { ST_READ_PAGES, "Read the status page"}, { ST_CLEAR_VERIFY, "Verify memory is clear"}, { ST_WRITE_TIME, "Setup to write the real time clock"}, { ST_WRITE_MEM, "Write the real time clock"}, { ST_WRITE_CONTROL,"Setup to write the control"}, { ST_WRITE_MEM, "Write the control"}, { ST_WRITE_RATE, "Setup to write the sample rate to start mission"}, { ST_WRITE_MEM, "Write the sample rate"}, { ST_READ_STATUS, "Read the new mission status"}, { ST_FINISH, "Finished"}}; // external One Wire functions from nework layer extern SMALLINT owAccess(int); extern void owSerialNum(int,uchar *,SMALLINT); extern void owFamilySearchSetup(int,SMALLINT); extern SMALLINT owNext(int,SMALLINT,SMALLINT); extern SMALLINT owVerify(int,SMALLINT); extern SMALLINT owOverdriveAccess(int); // external One Wire functions from transaction layer extern SMALLINT owBlock(int,SMALLINT,uchar *,SMALLINT); // external One Wire functions from link layer extern SMALLINT owTouchReset(int); extern SMALLINT owWriteByte(int,SMALLINT); extern SMALLINT owSpeed(int,SMALLINT); extern void msDelay(int); // external functions defined in crcutil.c extern void setcrc16(int,ushort); extern ushort docrc16(int,ushort); // extern function prototypes extern int key_abort(void); // Local Function Prototypes int DownloadThermo(int,uchar *,ThermoStateType *,FILE *); int ReadThermoStatus(int,uchar *,ThermoStateType *,FILE *); int MissionThermo(int,uchar *,ThermoStateType *,FILE *); static int RunThermoScript(int,ThermoStateType *,ThermoScript script[],FILE *fp); void MissionStatusToString(MissionStatus *,int,char *); void SecondsToDate(timedate *, ulong); ulong DateToSeconds(timedate *); uchar BCDToBin(uchar); void InterpretStatus(MissionStatus *); void FormatMission(MissionStatus *); void InterpretHistogram(Histogram *); void HistogramToString(Histogram *, int, char *); void InterpretAlarms(TempAlarmEvents *, MissionStatus *); void AlarmsToString(TempAlarmEvents *, char *); void InterpretLog(Log *, MissionStatus *); void LogToString(Log *, int, char *); void DebugToString(MissionStatus *, TempAlarmEvents *, Histogram *, Log *, char *); float TempToFloat(uchar, int); float CToF(float); uchar ToBCD(short); static int ThermoStep(int,ThermoStateType *,ThermoScript *,int *,int *,int *,char *); static int ReadPages(int,int,int,int *,uchar *); static int WriteScratch(int,uchar *,int,int); static int CopyScratch(int,int,int); static int WriteMemory(int,uchar *, int, int); // global state information static int current_speed[MAX_PORTNUM]; //-------------------------------------------------------------------------- // The 'DownloadThermo' downloads the specified Thermochron in 'SerialNum' // and puts the data in the state variable 'ThermoState'. Progress output // is printed to the specified file 'fp'. // // 'portnum' - number 0 to MAX_PORTNUM-1. This number is provided to // indicate the symbolic port number. // 'SerialNum' - Device serial number to download // 'ThermoState' - pointer to a structure type that holds the raw and // translated Thermochron data. // 'fp' - file pointer to print status information to // // Returns: TRUE (1) : Thermochron download with raw data in ThermoState // FALSE (0): not downloaded. Abort due to repeated errors // or user keypress. // int DownloadThermo(int portnum, uchar *SerialNum, ThermoStateType *ThermoState, FILE *fp) { // set the serial num owSerialNum(portnum, SerialNum, FALSE); // run the script and download thermochron return RunThermoScript(portnum,ThermoState,Download,fp); } //-------------------------------------------------------------------------- // The 'ReadThermoStatus' reads the Thermochron status in 'SerialNum' // and puts the data in the state variable 'ThermoState'. Progress output // is printed to the specified file 'fp'. // // 'portnum' - number 0 to MAX_PORTNUM-1. This number is provided to // indicate the symbolic port number. // 'SerialNum' - Device serial number to download // 'ThermoState' - pointer to a structure type that holds the raw and // translated Thermochron data. // 'fp' - file pointer to print status information to // // Returns: TRUE (1) : Thermochron status read with raw data in ThermoState // FALSE (0): status not read. Abort due to repeated errors // or user keypress. // int ReadThermoStatus(int portnum, uchar *SerialNum, ThermoStateType *ThermoState, FILE *fp) { // set the serial num owSerialNum(portnum, SerialNum, FALSE); // run the script and read status of thermochron return RunThermoScript(portnum,ThermoState,GetStatus,fp); } //-------------------------------------------------------------------------- // The 'MissionThermo' starts a new Thermochron mission on 'SerialNum' // from the state information provided in 'ThermoState'. Progress output // is printed to the specified file 'fp'. // // 'portnum' - number 0 to MAX_PORTNUM-1. This number is provided to // indicate the symbolic port number. // 'SerialNum' - Device serial number to download // 'ThermoState' - pointer to a structure type that holds the raw and // translated Thermochron data. // 'fp' - file pointer to print status information to // // Returns: TRUE (1) : Thermochron missioned // FALSE (0): not missioned. Abort due to repeated errors // or user keypress. // int MissionThermo(int portnum, uchar *SerialNum, ThermoStateType *ThermoState,FILE *fp) { // set the serial num owSerialNum(portnum, SerialNum, FALSE); // run the script and mission thermochron return RunThermoScript(portnum,ThermoState,Mission,fp); } //-------------------------------------------------------------------------- // Run the specified script. Return TRUE if all steps completed else FALSE. // Status is printed to file 'fp'. // int RunThermoScript(int portnum, ThermoStateType *ThermoState, ThermoScript script[], FILE *fp) { char msg[256],LastDescription[256],LastMsg[256]; int StepCount,SubStep,ErrorCount,Status; int last_clear_step=0; // reset the step to the begining StepCount = 0; SubStep = 0; ErrorCount = 0; Status = STATUS_INPROGRESS; // loop to perform all of the steps to download the Thermochron do { // switch on the status of the last step done switch(Status) { // step complete so go to the next case STATUS_STEP_COMPLETE: StepCount++; SubStep = 0; ErrorCount = 0; Status = STATUS_INPROGRESS; LastDescription[0] = 0; LastMsg[0] = 0; break; // in progress so call again case STATUS_INPROGRESS: // record the step position of the last memory clear // this is in case we need to attempt a clear again if (script[StepCount].Step == ST_CLEAR_SETUP) last_clear_step = StepCount; // print step description if different if (strcmp(LastDescription, script[StepCount].StepDescription) != 0) { fprintf(fp,"%s --> ",script[StepCount].StepDescription); sprintf(LastDescription,"%s",script[StepCount].StepDescription); } // perform a step in the job ThermoStep(portnum,ThermoState,&script[StepCount],&SubStep, &Status, &ErrorCount, msg); // print results if different if (strcmp(LastMsg,msg) != 0) { fprintf(fp,"%s\n",msg); sprintf(LastMsg,"%s",msg); } else fprintf(fp,"."); break; // encountered a transient error case STATUS_ERROR_TRANSIENT: // check if transient error is a memory clear if (script[StepCount].Step == ST_CLEAR_VERIFY) { // put back to starting clear over again StepCount = last_clear_step; SubStep = 0; ErrorCount = 0; Status = STATUS_INPROGRESS; break; } // if 20 tansient errors in a row then abort if (ErrorCount > 20) Status = STATUS_ERROR_HALT; else Status = STATUS_INPROGRESS; break; // all steps complete case STATUS_COMPLETE: fprintf(fp,"End script normally\n"); return TRUE; break; // non-recoverable error case STATUS_ERROR_HALT: fprintf(fp,"Aborting script due to non-recoverable error\n"); return FALSE; break; } } while (!key_abort()); // key abort fprintf(fp,"Aborting script due to key press\n"); return FALSE; } //---------------------------------------------------------------------- // Use the script to perform a step and return. // int ThermoStep(int portnum, ThermoStateType *ThermoState, ThermoScript *StateScript, int *SubStep, int *Status, int *ErrorCount, char *msg) { short rslt; static int read_page_num, read_pages, write_addr, write_len; static uchar *read_buf, *write_buf; static uchar tbuf[5]; // do the current step switch (StateScript->Step) { // the operation is complete case ST_FINISH: sprintf(msg,"Operation complete"); *Status = STATUS_COMPLETE; break; // read the mission status page case ST_READ_STATUS: read_page_num = STATUS_PAGE; read_pages = 1; read_buf = ThermoState->MissStat.status_raw; sprintf(msg,"Ready to read status page %d", read_page_num); *Status = STATUS_STEP_COMPLETE; break; // set up to read the alarm registers case ST_READ_ALARM: read_page_num = 17; read_pages = 3; read_buf = ThermoState->AlarmData.alarm_raw; sprintf(msg,"Ready to read alarm pages %d to %d", read_page_num, read_page_num + read_pages - 1); *Status = STATUS_STEP_COMPLETE; break; // set up to read the histogram data case ST_READ_HIST: read_page_num = 64; read_pages = 4; read_buf = ThermoState->HistData.hist_raw; sprintf(msg,"Ready to read histogram pages %d to %d", read_page_num, read_page_num + read_pages - 1); *Status = STATUS_STEP_COMPLETE; break; // set up to read the log data case ST_READ_LOG: read_page_num = 128; read_pages = 64; read_buf = ThermoState->LogData.log_raw; sprintf(msg,"Ready to read log pages %d to %d", read_page_num, read_page_num + read_pages - 1); *Status = STATUS_STEP_COMPLETE; break; // read the specified pages case ST_READ_PAGES: // check for last page if (*SubStep == 0) // set the sub-step to the current page being read *SubStep = read_page_num; // read the status page rslt = ReadPages(portnum, read_page_num, read_pages, SubStep, read_buf); if (rslt == FALSE) { sprintf(msg,"Thermochron not on 1-Wire Net"); #ifdef __MC68K__ *Status = STATUS_ERROR_HALT; #else *Status = STATUS_INPROGRESS; #endif } else { sprintf(msg,"Pages read from Thermochron"); *Status = STATUS_STEP_COMPLETE; } break; // setup the clear memory case ST_CLEAR_SETUP: // create a small buff to write to start the clear memory tbuf[0] = 0x40; write_buf = &tbuf[0]; write_len = 1; write_addr = 0x20E; sprintf(msg,"Write to setup clear memory"); *Status = STATUS_STEP_COMPLETE; break; // clear the memory case ST_CLEAR_MEM: // set the clear memory command (not check return because verify) owAccess(portnum); owWriteByte(portnum,0x3C); msDelay(3); owTouchReset(portnum); sprintf(msg,"Clear memory command sent"); *Status = STATUS_STEP_COMPLETE; break; // clear the memory case ST_CLEAR_VERIFY: // look at the memory clear bit if ((ThermoState->MissStat.status_raw[0x14] & 0x40) == 0x40) { sprintf(msg,"Memory is clear"); *Status = STATUS_STEP_COMPLETE; break; } else { sprintf(msg,"Memory did NOT clear"); *Status = STATUS_ERROR_TRANSIENT; break; } break; // setup write time, clock alarm, control, trips case ST_WRITE_TIME: // create the write buffer FormatMission(&ThermoState->MissStat); write_buf = &ThermoState->MissStat.status_raw[0x00]; write_len = 13; write_addr = 0x200; sprintf(msg,"Write time, clock alarm, and trips setup"); *Status = STATUS_STEP_COMPLETE; break; // write the control, mission delay and clear flags case ST_WRITE_CONTROL: write_buf = &ThermoState->MissStat.status_raw[0x0E]; write_len = 7; write_addr = 0x20E; sprintf(msg,"Write control, mission delay, clear flags setup"); *Status = STATUS_STEP_COMPLETE; break; case ST_WRITE_RATE: write_buf = &ThermoState->MissStat.status_raw[0x0D]; write_len = 1; write_addr = 0x20D; sprintf(msg,"Write sample rate setup"); *Status = STATUS_STEP_COMPLETE; break; // write the specified memory location case ST_WRITE_MEM: if (WriteMemory(portnum, write_buf, write_len, write_addr)) { sprintf(msg,"Memory written to Thermochron"); *Status = STATUS_STEP_COMPLETE; } else { sprintf(msg,"Thermochron not on 1-Wire Net"); *Status = STATUS_INPROGRESS; } default: break; } return *Status; } //---------------------------------------------------------------------- // Read a specified number of pages in overdrive // // 'portnum' - number 0 to MAX_PORTNUM-1. This number is provided to // indicate the symbolic port number. // int ReadPages(int portnum, int start_pg, int num_pgs, int *last_pg, uchar *finalbuf) { int skip_overaccess = 0, skip_access = 0; uchar pkt[60]; int len,i; uchar SerialNumber[8]; ushort lastcrc16; // read the rom number owSerialNum(portnum,SerialNumber,TRUE); #ifndef __MC68K__ // verify device is in overdrive if (current_speed[portnum] == MODE_OVERDRIVE) { if (owVerify(portnum,FALSE)) skip_overaccess = 1; } if (!skip_overaccess) { if (owOverdriveAccess(portnum)) current_speed[portnum] = MODE_OVERDRIVE; else current_speed[portnum] = MODE_NORMAL; } #endif // loop while there is pages to read do { // create a packet to read a page len = 0; setcrc16(portnum,0); // optional skip access on subsequent pages if (!skip_access) { // match pkt[len++] = 0x55; // rom number for (i = 0; i < 8; i++) pkt[len++] = SerialNumber[i]; // read memory with crc command pkt[len] = 0xA5; docrc16(portnum,pkt[len++]); // address pkt[len] = (uchar)((*last_pg << 5) & 0xFF); docrc16(portnum,pkt[len++]); pkt[len] = (uchar)(*last_pg >> 3); docrc16(portnum,pkt[len++]); } // set 32 reads for data and 2 for crc for (i = 0; i < 34; i++) pkt[len++] = 0xFF; // send the bytes if (owBlock(portnum,!skip_access,pkt,len)) { // calucate the CRC over the last 34 bytes for (i = 0; i < 34; i++) lastcrc16 = docrc16(portnum,pkt[len - 34 + i]); // check crc if (lastcrc16 == 0xB001) { // copy the data into the buffer for (i = 0; i < 32; i++) finalbuf[i + (*last_pg - start_pg) * 32] = pkt[len - 34 + i]; // change number of pages *last_pg = *last_pg + 1; // now skip access skip_access = TRUE; } else return FALSE; } else return FALSE; } while ((*last_pg - start_pg) < num_pgs); return TRUE; } //----------------------------------------------------------------------------} // Write a memory location. Data must all be on the same page // // 'portnum' - number 0 to MAX_PORTNUM-1. This number is provided to // indicate the symbolic port number. // int WriteMemory(int portnum, uchar *Buf, int ln, int adr) { // write to scratch and then copy if (WriteScratch(portnum,Buf,ln,adr)) return CopyScratch(portnum,ln,adr); return FALSE; } //----------------------------------------------------------------------------} // Write the scratch pad // // 'portnum' - number 0 to MAX_PORTNUM-1. This number is provided to // indicate the symbolic port number. // int WriteScratch(int portnum, uchar *Buf, int ln, int adr) { int i; uchar pbuf[80]; // check for alarm indicator if (owAccess(portnum)) { // construct a packet to send pbuf[0] = 0x0F; // write scratch command pbuf[1] = (adr & 0xFF); // address 1 pbuf[2] = ((adr >> 8) & 0xFF); // address 2 // the write bytes for (i = 0; i < ln; i++) pbuf[3 + i] = (uchar)(Buf[i]); // data // perform the block if (!owBlock(portnum,FALSE,pbuf,ln+3)) return FALSE; // Now read back the scratch if (owAccess(portnum)) { // construct a packet to send pbuf[0] = 0xAA; // read scratch command pbuf[1] = 0xFF; // address 1 pbuf[2] = 0xFF; // address 2 pbuf[3] = 0xFF; // offset // the write bytes for (i = 0; i < ln; i++) pbuf[4 + i] = 0xFF; // data // perform the block if (!owBlock(portnum,FALSE,pbuf,ln+4)) return FALSE; // read address 1 if (pbuf[1] != (adr & 0xFF)) return FALSE; // read address 2 if (pbuf[2] != ((adr >> 8) & 0xFF)) return FALSE; // read the offset if (pbuf[3] != ((adr + ln - 1) & 0x1F)) return FALSE; // read and compare the contents for (i = 0; i < ln; i++) { if (pbuf[4 + i] != Buf[i]) return FALSE; } // success return TRUE; } } return FALSE; } //----------------------------------------------------------------------------} // Copy the scratch pad // // 'portnum' - number 0 to MAX_PORTNUM-1. This number is provided to // indicate the symbolic port number. // int CopyScratch(int portnum, int ln, int adr) { int i; uchar pbuf[50]; // check for alarm indicator if (owAccess(portnum)) { // construct a packet to send pbuf[0] = 0x55; // copy scratch command pbuf[1] = (adr & 0xFF); // address 1 pbuf[2] = ((adr >> 8) & 0xFF); // address 2 pbuf[3] = (adr + ln - 1) & 0x1F; // offset for (i = 0; i <= 9; i++) pbuf[4 + i] = 0xFF; // result of copy // perform the block if (owBlock(portnum,FALSE,pbuf,14)) { if ((pbuf[13] == 0x55) || (pbuf[13] == 0xAA)) return TRUE; } } return FALSE; } //---------------------------------------------------------------------- // Interpret the Status by looking at the 'raw' portion of the // mission status structure. // void InterpretStatus(MissionStatus *mstatus) { timedate td,tdtmp; int offset; ulong tmtmp; #ifndef __MC68K__ time_t tlong; struct tm *tstruct; #endif // mission in progress flag mstatus->mission_in_progress = (0x20 & mstatus->status_raw[0x14]) >> 5; // sample rate mstatus->sample_rate = mstatus->status_raw[0x0D]; // rollover enabled mstatus->rollover_enable = (0x08 & mstatus->status_raw[0x0E]) >> 3; // startdelay mstatus->start_delay = (mstatus->status_raw[0x13] << 8) | mstatus->status_raw[0x12]; // number of samples in this mission #ifdef __MC68K__ mstatus->mission_samples = (((ulong) mstatus->status_raw[0x1C]) * 65536) + (((ulong) mstatus->status_raw[0x1B]) * 256) + ((ulong) mstatus->status_raw[0x1A]); #else mstatus->mission_samples = (mstatus->status_raw[0x1C] << 16) | (mstatus->status_raw[0x1B] << 8) | mstatus->status_raw[0x1A]; #endif // total number of samples #ifdef __MC68K__ mstatus->samples_total = (((ulong) mstatus->status_raw[0x1F]) * 65536) + (((ulong) mstatus->status_raw[0x1E]) * 256) + ((ulong) mstatus->status_raw[0x1D]); #else mstatus->samples_total = (mstatus->status_raw[0x1F] << 16) | (mstatus->status_raw[0x1E] << 8) | mstatus->status_raw[0x1D]; #endif // temperature thresholds mstatus->high_threshold = mstatus->status_raw[0x0C]; mstatus->low_threshold = mstatus->status_raw[0x0B]; // rollover occurred if ((mstatus->mission_samples > 2048) && mstatus->rollover_enable) mstatus->rollover_occurred = 1; else mstatus->rollover_occurred = 0; // current real-time clock value offset = 0x00; td.second = BCDToBin((uchar)(mstatus->status_raw[offset] & 0x7F)); td.minute = BCDToBin((uchar)(mstatus->status_raw[offset + 1] & 0x7F)); // check for 12 hour mode if (mstatus->status_raw[offset + 2] & 0x40) { td.hour = BCDToBin((uchar)(mstatus->status_raw[offset + 2] & 0x1F)); // check for PM if (mstatus->status_raw[offset + 2] & 0x20) td.hour += 12; } else td.hour = BCDToBin((uchar)(mstatus->status_raw[offset + 2] & 0x3F)); td.day = BCDToBin((uchar)(mstatus->status_raw[offset + 4] & 0x3F)); td.month = BCDToBin((uchar)(mstatus->status_raw[offset + 5] & 0x1F)); td.year = BCDToBin(mstatus->status_raw[offset + 6]) + 1900; // check for century bit if (mstatus->status_raw[offset + 5] & 0x80) td.year = BCDToBin(mstatus->status_raw[offset + 6]) + 2000; // (2.00) // convert to seconds since 1970 mstatus->current_time = DateToSeconds(&td); // date/time when mission started offset = 0x15; td.second = (uchar)0; td.minute = BCDToBin((uchar)(mstatus->status_raw[offset] & 0x7F)); // check for 12 hour mode if (mstatus->status_raw[offset + 1] & 0x40) { td.hour = BCDToBin((uchar)(mstatus->status_raw[offset + 1] & 0x1F)); // check for PM if (mstatus->status_raw[offset + 1] & 0x20) td.hour += 12; } else td.hour = BCDToBin((uchar)(mstatus->status_raw[offset + 1] & 0x3F)); td.day = BCDToBin((uchar)(mstatus->status_raw[offset + 2] & 0x3F)); td.month = BCDToBin((uchar)(mstatus->status_raw[offset + 3] & 0x1F)); td.year = BCDToBin((uchar)(mstatus->status_raw[offset + 4])); // (2.00) // (2.00) logic to decide on century of mission stamp // check if century bit set in mission stamp if (mstatus->status_raw[offset + 3] & 0x80) td.year += 2000; // check in mission in progress else if (mstatus->mission_in_progress) { // calculate the mission start year back from real time clock tmtmp = mstatus->current_time - (mstatus->sample_rate * mstatus->mission_samples * 60); SecondsToDate(&tdtmp,tmtmp); td.year = tdtmp.year; } else { // mission stopped so get century by year window if (td.year <= 70) td.year += 2000; else td.year += 1900; } // convert to seconds since 1970 if ((td.month == 0) || (td.day == 0)) mstatus->mission_start_time = 0; else mstatus->mission_start_time = DateToSeconds(&td); // download stations time of reading #ifdef __MC68K__ mstatus->download_time = TimGetSeconds(); #else time(&tlong); tstruct = localtime(&tlong); td.day = tstruct->tm_mday; td.month = tstruct->tm_mon + 1; // (1.01) td.year = tstruct->tm_year + 1900; td.hour = tstruct->tm_hour; td.minute = tstruct->tm_min; td.second = tstruct->tm_sec; mstatus->download_time = DateToSeconds(&td); #endif // skip alarm modes and status for now } //-------------------------------------------------------------------------- // Take the Mission Status structure and create new raw data to start // a new mission. // void FormatMission(MissionStatus *mstatus) { int i; time_t tlong; #ifndef __MC68K__ struct tm *tstruct; #else DateTimePtr tstruct = NULL; #endif // clear the buffer for (i = 0; i < 32; i++) mstatus->status_raw[i] = 0; // Real Time Clock #ifdef __MC68K__ tlong = TimGetSeconds(); #else time(&tlong); #endif tlong++; // add 1 second #ifdef __MC68K__ TimSecondsToDateTime(tlong, tstruct); #else tstruct = localtime(&tlong); #endif // convert to BCD #ifdef __MC68K__ mstatus->status_raw[0x00] = ToBCD((short)tstruct->second); mstatus->status_raw[0x01] = ToBCD((short)tstruct->minute); mstatus->status_raw[0x02] = ToBCD((short)tstruct->hour); mstatus->status_raw[0x03] = ToBCD((short)(tstruct->weekDay + 1)); mstatus->status_raw[0x04] = ToBCD((short)tstruct->day); mstatus->status_raw[0x05] = ToBCD((short)(tstruct->month + 1)); if (tstruct->year >= 100) mstatus->status_raw[0x05] |= 0x80; mstatus->status_raw[0x06] = ToBCD((short)(tstruct->year % 100)); #else mstatus->status_raw[0x00] = ToBCD((short)tstruct->tm_sec); mstatus->status_raw[0x01] = ToBCD((short)tstruct->tm_min); mstatus->status_raw[0x02] = ToBCD((short)tstruct->tm_hour); mstatus->status_raw[0x03] = ToBCD((short)(tstruct->tm_wday + 1)); mstatus->status_raw[0x04] = ToBCD((short)tstruct->tm_mday); mstatus->status_raw[0x05] = ToBCD((short)(tstruct->tm_mon + 1)); if (tstruct->tm_year >= 100) mstatus->status_raw[0x05] |= 0x80; mstatus->status_raw[0x06] = ToBCD((short)(tstruct->tm_year % 100)); #endif // Real Time clock Alarm (leave 0's) // Low temp alarm mstatus->status_raw[0x0B] = mstatus->low_threshold; // High temp alarm mstatus->status_raw[0x0C] = mstatus->high_threshold; // sample rate mstatus->status_raw[0x0D] = mstatus->sample_rate; // control mstatus->status_raw[0x0E] = 0x40; if (mstatus->rollover_enable) mstatus->status_raw[0x0E] |= 0x08; // mission start delay mstatus->status_raw[0x12] = mstatus->start_delay & 0xFF; mstatus->status_raw[0x13] = (mstatus->start_delay >> 8) & 0xFF; } //-------------------------------------------------------------------------- // Convert an integer to a 1 Byte BCD number (99 max) // uchar ToBCD(short num) { uchar rtbyte; rtbyte = (num - ((num / 10) * 10)) & 0x0F; rtbyte = rtbyte | ((num / 10) << 4); return rtbyte; } //-------------------------------------------------------------------------- // Take the Mission Status structure and convert to string format // void MissionStatusToString(MissionStatus *mstatus, int ConvertToF, char *str) { int cnt=0,i; timedate td; #ifndef __MC68K__ time_t tlong; struct tm *tstruct; #else DateTimePtr tstruct = NULL; #endif // title #ifdef __MC68K__ cnt += sprintf(&str[cnt],"Stat For DS1921:"); #else cnt += sprintf(&str[cnt],"Mission State\n-------------\n"); cnt += sprintf(&str[cnt],"Serial Number of DS1921: "); #endif // serial number for (i = 7; i >= 0; i--) cnt += sprintf(&str[cnt],"%02X",mstatus->serial_num[i]); // mission state if (mstatus->mission_in_progress) cnt += sprintf(&str[cnt],"\nMission is in progress\n"); else cnt += sprintf(&str[cnt],"\nMission is ended\n"); // sample rate cnt += sprintf(&str[cnt],"Sample rate: %d minute(s)\n",mstatus->sample_rate); // rollover cnt += sprintf(&str[cnt],"Roll-Over Enabled: "); if (mstatus->rollover_enable) cnt += sprintf(&str[cnt],"yes\n"); else cnt += sprintf(&str[cnt],"no\n"); cnt += sprintf(&str[cnt],"Roll-Over Occurred: "); if (mstatus->rollover_occurred) cnt += sprintf(&str[cnt],"yes\n"); else cnt += sprintf(&str[cnt],"no\n"); // mission start time if (mstatus->start_delay == 0) { SecondsToDate(&td,mstatus->mission_start_time); if (mstatus->mission_start_time == 0) cnt += sprintf(&str[cnt],"Mission Start time: not started yet\n"); else cnt += sprintf(&str[cnt],"Mission Start: %02d/%02d/%04d %02d:%02d:%02d\n", td.month,td.day,td.year,td.hour,td.minute,td.second); } else cnt += sprintf(&str[cnt],"Mission Start time: na\n"); // mission start delay cnt += sprintf(&str[cnt],"Mission Start delay: %d minute(s)\n",mstatus->start_delay); // mission samples cnt += sprintf(&str[cnt],"Mission Samples: %ld\n",mstatus->mission_samples); // device total samples cnt += sprintf(&str[cnt],"Device total samples: %ld\n",mstatus->samples_total); // temperature display mode cnt += sprintf(&str[cnt],"Temp displayed in: "); if (ConvertToF) cnt += sprintf(&str[cnt],"(Fahrenheit)\n"); else cnt += sprintf(&str[cnt],"(Celsius)\n"); // thresholds cnt += sprintf(&str[cnt],"High Threshold: %6.1f\n", TempToFloat(mstatus->high_threshold,ConvertToF)); cnt += sprintf(&str[cnt],"Low Threshold: %6.1f\n", TempToFloat(mstatus->low_threshold,ConvertToF)); // time from D1921 SecondsToDate(&td,mstatus->current_time); cnt += sprintf(&str[cnt],"Current Real-Time Clock from DS1921: %02d/%02d/%04d %02d:%02d:%02d\n", td.month,td.day,td.year,td.hour,td.minute,td.second); #ifndef __MC68K__ // current PC time time(&tlong); tstruct = localtime(&tlong); cnt += sprintf(&str[cnt],"Current PC Time: %02d/%02d/%04d %02d:%02d:%02d\n", tstruct->tm_mon + 1,tstruct->tm_mday,tstruct->tm_year + 1900, tstruct->tm_hour,tstruct->tm_min,tstruct->tm_sec); #endif // zero terminate string str[cnt] = 0; } //---------------------------------------------------------------------- // Interpret the Histogram by looking at the 'raw' portion of the // Histogram structure. Store the temperature range values in Celsius. // void InterpretHistogram(Histogram *hist) { int i; // loop through each bin value for (i = 0; i < 126; i += 2) // (2.00) { // get the bin value hist->bin_count[i / 2] = hist->hist_raw[i] | (hist->hist_raw[i + 1] << 8); // start value for this bin hist->start_range[i / 2] = TempToFloat((uchar)((i / 2) << 2),FALSE); // end value for this bin hist->end_range[i / 2] = TempToFloat((uchar)(((i / 2) << 2) | 0x03),FALSE); } } //-------------------------------------------------------------------------- // Take the Histogram structure and convert to string format // void HistogramToString(Histogram *hist, int ConvertToF, char *str) { int cnt=0,i; // title cnt += sprintf(&str[cnt],"Temperature Histogram\n---------------------\n" "Format: [Temp Range, Count] "); if (ConvertToF) cnt += sprintf(&str[cnt],"(Fahrenheit)\n"); else cnt += sprintf(&str[cnt],"(Celsius)\n"); // loop through bins for (i = 0; i < 63; i++) // (2.00) { cnt += sprintf(&str[cnt],"%6.1f to %6.1f, %d\n", (ConvertToF) ? CToF(hist->start_range[i]): hist->start_range[i], (ConvertToF) ? CToF(hist->end_range[i]): hist->end_range[i], hist->bin_count[i]); } // zero terminate string str[cnt] = 0; } //---------------------------------------------------------------------- // Interpret the Temperature Alarm Event data by looking at the 'raw' // portion of the TempAlarmEvents structure. Mission Status is needed // to interpret the events. // void InterpretAlarms(TempAlarmEvents *alarm, MissionStatus *mstatus) { int i; ulong event_mission_count; uchar duration; // low events alarm->num_low = 0; for (i = 0; i < 48; i += 4) { // get the mission start count of this event event_mission_count = (alarm->alarm_raw[i + 2] << 16) | (alarm->alarm_raw[i + 1] << 8) | alarm->alarm_raw[i]; // check if done with low events if (!event_mission_count) break; // get the duration duration = alarm->alarm_raw[i + 3]; // calculate the start time alarm->low_start_time[alarm->num_low] = mstatus->mission_start_time + (event_mission_count - 1) * (mstatus->sample_rate * 60); // calculate the end time alarm->low_end_time[alarm->num_low] = alarm->low_start_time[alarm->num_low] + (duration - 1) * (mstatus->sample_rate * 60); // increment number of low events alarm->num_low++; } // high events alarm->num_high = 0; for (i = 48; i < 96; i += 4) { // get the mission start count of this event event_mission_count = (alarm->alarm_raw[i + 2] << 16) | (alarm->alarm_raw[i + 1] << 8) | alarm->alarm_raw[i]; // check if done with low events if (!event_mission_count) break; // get the duration duration = alarm->alarm_raw[i + 3]; // calculate the start time alarm->high_start_time[alarm->num_high] = mstatus->mission_start_time + (event_mission_count - 1) * (mstatus->sample_rate * 60); // calculate the end time alarm->high_end_time[alarm->num_high] = alarm->high_start_time[alarm->num_high] + (duration - 1) * (mstatus->sample_rate * 60); // increment number of low events alarm->num_high++; } } //-------------------------------------------------------------------------- // Take the Temperature Alarms Events structure and convert to string // format // void AlarmsToString(TempAlarmEvents *alarm, char *str) { int i, cnt=0; timedate td; // title cnt += sprintf(&str[cnt],"Temperature Alarms\n------------------\n" "Format: [(HIGH/LOW), Time/Date Range]\n"); // loop through each low alarm for (i = 0; i < alarm->num_low; i++) { cnt += sprintf(&str[cnt],"LOW , "); // start time SecondsToDate(&td,alarm->low_start_time[i]); cnt += sprintf(&str[cnt]," %02d/%02d/%04d %02d:%02d to ", td.month,td.day,td.year,td.hour,td.minute); // end time SecondsToDate(&td,alarm->low_end_time[i]); cnt += sprintf(&str[cnt]," %02d/%02d/%04d %02d:%02d\n", td.month,td.day,td.year,td.hour,td.minute); } // loop through each high alarm for (i = 0; i < alarm->num_high; i++) { cnt += sprintf(&str[cnt],"HIGH , "); // start time SecondsToDate(&td,alarm->high_start_time[i]); cnt += sprintf(&str[cnt]," %02d/%02d/%04d %02d:%02d to ", td.month,td.day,td.year,td.hour,td.minute); // end time SecondsToDate(&td,alarm->high_end_time[i]); cnt += sprintf(&str[cnt]," %02d/%02d/%04d %02d:%02d\n", td.month,td.day,td.year,td.hour,td.minute); } // zero terminate string str[cnt] = 0; } //---------------------------------------------------------------------- // Interpret the Log data by looking at the 'raw' // portion of the Log structure. Mission Status is needed // to interpret when the logs occurred. // void InterpretLog(Log *log, MissionStatus *mstatus) { ulong loops=0,overlap=0,lastlog=2048,i; int logcnt=0; // check if wrap occurred if (mstatus->rollover_occurred) { // calculate the number loops loops = (mstatus->mission_samples / 2048) - 1; // calculate the number of overlap overlap = mstatus->mission_samples % 2048; log->num_log = 2048; } else { log->start_time = mstatus->mission_start_time; if (mstatus->mission_samples > 2048) // (1.02) lastlog = 2048; else lastlog = mstatus->mission_samples; log->num_log = (int)lastlog; } // set the interval log->interval = mstatus->sample_rate * 60; // caluclate the start time of the first log value log->start_time = mstatus->mission_start_time + loops * 2048 * log->interval + overlap * log->interval; // loop to fill in the remainder first for (i = overlap; i < lastlog; i++) log->temp[logcnt++] = TempToFloat(log->log_raw[i],FALSE); // loop to get the overlap for (i = 0; i < overlap; i++) log->temp[logcnt++] = TempToFloat(log->log_raw[i],FALSE); } //-------------------------------------------------------------------------- // Take the Log structure and convert to string // format // void LogToString(Log *log, int ConvertToF, char *str) { int i,cnt=0; ulong logtime; timedate td; // title cnt += sprintf(&str[cnt],"Log Data\n--------\n" "Format: [Time/Date , Temperature] "); if (ConvertToF) cnt += sprintf(&str[cnt],"(Fahrenheit)\n"); else cnt += sprintf(&str[cnt],"(Celsius)\n"); // loop through the logs logtime = log->start_time; for (i = 0; i < log->num_log; i++) { // time SecondsToDate(&td,logtime); cnt += sprintf(&str[cnt],"%02d/%02d/%04d %02d:%02d ,", td.month,td.day,td.year,td.hour,td.minute); // temp cnt += sprintf(&str[cnt],"%6.1f\n", (ConvertToF) ? CToF(log->temp[i]): log->temp[i]); // increment the time logtime += log->interval; } // zero terminate string str[cnt] = 0; } //-------------------------------------------------------------------------- // Convert the raw debug data to a string // void DebugToString(MissionStatus *mstatus, TempAlarmEvents *alarm, Histogram *hist, Log *log, char *str) { int i,cnt=0; // title cnt += sprintf(&str[cnt],"Debug Dump\n----------\nRegister Page:\n"); // reg for (i = 0; i < 32; i++) { cnt += sprintf(&str[cnt],"%02X ",mstatus->status_raw[i]); if (i && (((i + 1) % 16) == 0)) cnt += sprintf(&str[cnt],"\n"); } // alarms cnt += sprintf(&str[cnt],"Alarms:\n"); for (i = 0; i < 96; i++) { cnt += sprintf(&str[cnt],"%02X ",alarm->alarm_raw[i]); if (i && (((i + 1) % 16) == 0)) cnt += sprintf(&str[cnt],"\n"); } // histogram cnt += sprintf(&str[cnt],"Histogram:\n"); for (i = 0; i < 128; i++) { cnt += sprintf(&str[cnt],"%02X ",hist->hist_raw[i]); if (i && (((i + 1) % 16) == 0)) cnt += sprintf(&str[cnt],"\n"); } // log cnt += sprintf(&str[cnt],"Log:\n"); for (i = 0; i < ((log->num_log > 2048) ? 2048 : log->num_log); i++) { cnt += sprintf(&str[cnt],"%02X ",log->log_raw[i]); if (i && (((i + 1) % 16) == 0)) cnt += sprintf(&str[cnt],"\n"); } // zero terminate string str[cnt] = 0; } //-------------------------------------------------------------------------- // Take one byte BCD value and return binary value // uchar BCDToBin(uchar bcd) { return (((bcd & 0xF0) >> 4) * 10) + (bcd & 0x0F); } //-------------------------------------------------------------------------- // Take a 4 byte long string and convert it into a timedata structure. // static int dm[] = { 0,0,31,59,90,120,151,181,212,243,273,304,334,365 }; void SecondsToDate(timedate *td, ulong x) { short tmp,i,j; ulong y; // check to make sure date is not over 2070 (sanity check) if (x > 0xBBF81E00L) x = 0; y = x/60; td->second = (ushort)(x-60*y); x = y/60; td->minute = (ushort)(y-60*x); y = x/24; td->hour = (ushort)(x-24*y); x = 4*(y+731); td->year = (ushort)(x/1461); i = (int)((x-1461*(ulong)(td->year))/4); td->month = 13; do { td->month -= 1; tmp = (td->month > 2) && ((td->year & 3)==0) ? 1 : 0; j = dm[td->month]+tmp; } while (i < j); td->day = i-j+1; // slight adjustment to algorithm if (td->day == 0) td->day = 1; td->year = (td->year < 32) ? td->year + 68 + 1900: td->year - 32 + 2000; } //-------------------------------------------------------------------------- // DateToSeconds takes a time/date structure and converts it into the // number of seconds since 1970 // ulong DateToSeconds(timedate *td) { ulong Sv,Bv,Xv; // convert the date/time values into the 5 byte format used in the touch if (td->year >= 2000) Sv = td->year + 32 - 2000; else Sv = td->year - 68 - 1900; if ((td->month > 2) && ( (Sv & 3) == 0)) Bv = 1; else Bv = 0; Xv = 365 * (Sv-2) + (Sv-1)/4 + dm[td->month] + td->day + Bv - 1; Xv = 86400 * Xv + (ulong)(td->second) + 60*((ulong)(td->minute) + 60*(ulong)(td->hour)); return Xv; } //-------------------------------------------------------------------------- // Convert from DS1921 termature format to a float // // float TempToFloat(uchar tmp, int ConvertToF) { float tfloat; tfloat = (float)((tmp / 2.0) - 40.0); if (ConvertToF) return (float)(tfloat * 9.0 / 5.0 + 32.0); else return tfloat; } //-------------------------------------------------------------------------- // Convert from Celsius to Fahrenheit // float CToF(float CVal) { return (float)(CVal * 9.0 / 5.0 + 32.0); }