just an example of what SDCC can do for YOU
[fw/sdcc] / device / examples / ds390 / ow390 / thermo21.c
1 //---------------------------------------------------------------------------
2 // Copyright (C) 2000 Dallas Semiconductor Corporation, All Rights Reserved.
3 // 
4 // Permission is hereby granted, free of charge, to any person obtaining a 
5 // copy of this software and associated documentation files (the "Software"), 
6 // to deal in the Software without restriction, including without limitation 
7 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 
8 // and/or sell copies of the Software, and to permit persons to whom the 
9 // Software is furnished to do so, subject to the following conditions:
10 // 
11 // The above copyright notice and this permission notice shall be included 
12 // in all copies or substantial portions of the Software.
13 // 
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
16 // MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
17 // IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES 
18 // OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
19 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
20 // OTHER DEALINGS IN THE SOFTWARE.
21 // 
22 // Except as contained in this notice, the name of Dallas Semiconductor 
23 // shall not be used except as stated in the Dallas Semiconductor 
24 // Branding Policy. 
25 //---------------------------------------------------------------------------
26 //
27 //  thermo21.c - Thermochron iButton utility functions 
28 //
29 //  Version: 2.00
30 //    
31 //    History:
32 //           1.03 -> 2.00  Reorganization of Public Domain Kit 
33 //                         Convert to global CRC utility functions
34 //                         Y2K fix.
35
36 #include <stdio.h>
37 #include "time.h"
38 #include <string.h>
39 #include "ownet.h"
40 #include "thermo21.h"   
41
42 // a hack for sdcc/TINI, just printf to stdout
43 int fprintf (FILE *fp, xdata char *format, ...) reentrant {
44   va_list arg;
45   *fp; // hush the compiler
46   va_start(arg, format);
47   vsprintf(NULL, format, arg);
48   va_end(arg);
49 }
50
51 static int RunThermoScript(int,ThermoStateType *,ThermoScript script[], FILE *fp);
52 static int ThermoStep(int,ThermoStateType *,ThermoScript *,int *,int *,int *,char *);
53 static int ReadPages(int,int,int,int *,uchar *);
54 static int WriteScratch(int,uchar *,int,int);
55 static int CopyScratch(int,int,int);
56 static int WriteMemory(int,uchar *, int, int);
57
58 // step constants
59 enum { ST_SETUP=0, ST_READ_STATUS, ST_READ_ALARM, ST_READ_HIST,
60        ST_READ_LOG, ST_CLEAR_MEM, ST_CLEAR_VERIFY, ST_WRITE_TIME,
61        ST_WRITE_CONTROL, ST_WRITE_RATE, ST_FINISH, ST_GET_SESSION, 
62        ST_FIND_THERMO, ST_REL_SESSION, ST_READ_PAGES, ST_WRITE_MEM,
63        ST_CLEAR_SETUP };
64
65 // status contants
66 enum { STATUS_STEP_COMPLETE, STATUS_COMPLETE, STATUS_INPROGRESS,
67        STATUS_ERROR_HALT, STATUS_ERROR_TRANSIENT };
68
69 // download steps
70 static ThermoScript Download[] = 
71     {{ ST_READ_STATUS,  "Setup to read the mission status"},
72      { ST_READ_PAGES,   "Read the status page"},
73      { ST_READ_ALARM,   "Setup to read alarm pages"},
74      { ST_READ_PAGES,   "Read the alarm pages"},
75      { ST_READ_HIST,    "Setup to read histogram pages"},
76      { ST_READ_PAGES,   "Read the histogram pages"},
77      { ST_READ_LOG,     "Setup to read log pages"},
78      { ST_READ_PAGES,   "Read the log pages"},
79      { ST_FINISH,       "Finished"}}; 
80
81 // read status only steps
82 static ThermoScript GetStatus[] = 
83     {{ ST_READ_STATUS,  "Setup to read the mission status"},
84      { ST_READ_PAGES,   "Read the status page"},
85      { ST_FINISH,       "Finished"}}; 
86
87 // mission steps (assume already did StatusThermo)
88 static ThermoScript Mission[] = 
89     {{ ST_CLEAR_SETUP,  "Setup clear memory"},
90      { ST_WRITE_MEM,    "Write clear memory bit"},
91      { ST_CLEAR_MEM,    "Clear the memory"},
92      { ST_READ_STATUS,  "Setup to read the mission status"},
93      { ST_READ_PAGES,   "Read the status page"},
94      { ST_CLEAR_VERIFY, "Verify memory is clear"},
95      { ST_WRITE_TIME,   "Setup to write the real time clock"},
96      { ST_WRITE_MEM,    "Write the real time clock"},
97      { ST_WRITE_CONTROL,"Setup to write the control"},
98      { ST_WRITE_MEM,    "Write the control"},
99      { ST_WRITE_RATE,   "Setup to write the sample rate to start mission"},
100      { ST_WRITE_MEM,    "Write the sample rate"},
101      { ST_READ_STATUS,  "Read the new mission status"},
102      { ST_FINISH,       "Finished"}}; 
103
104 // global state information
105 static int current_speed[MAX_PORTNUM];
106
107 //--------------------------------------------------------------------------
108 // The 'DownloadThermo' downloads the specified Thermochron in 'SerialNum'
109 // and puts the data in the state variable 'ThermoState'.  Progress output
110 // is printed to the specified file 'fp'. 
111 //
112 // 'portnum'     - number 0 to MAX_PORTNUM-1.  This number is provided to
113 //                 indicate the symbolic port number.
114 // 'SerialNum'   - Device serial number to download
115 // 'ThermoState' - pointer to a structure type that holds the raw and
116 //                 translated Thermochron data.
117 // 'fp'          - file pointer to print status information to
118 //
119 // Returns:   TRUE (1) : Thermochron download with raw data in ThermoState
120 //            FALSE (0): not downloaded.  Abort due to repeated errors
121 //                       or user keypress.
122 //
123 int DownloadThermo(int portnum, uchar *SerialNum, 
124                    ThermoStateType *ThermoState, FILE *fp)
125 {
126    // set the serial num
127    owSerialNum(portnum, SerialNum, FALSE);
128
129    // run the script and download thermochron
130    return RunThermoScript(portnum,ThermoState,Download,fp);
131 }
132
133 //--------------------------------------------------------------------------
134 // The 'ReadThermoStatus' reads the Thermochron status in 'SerialNum'
135 // and puts the data in the state variable 'ThermoState'.  Progress output
136 // is printed to the specified file 'fp'. 
137 //
138 // 'portnum'     - number 0 to MAX_PORTNUM-1.  This number is provided to
139 //                 indicate the symbolic port number.
140 // 'SerialNum'   - Device serial number to download
141 // 'ThermoState' - pointer to a structure type that holds the raw and
142 //                 translated Thermochron data.
143 // 'fp'          - file pointer to print status information to
144 //
145 // Returns:   TRUE (1) : Thermochron status read with raw data in ThermoState
146 //            FALSE (0): status not read.  Abort due to repeated errors
147 //                       or user keypress.
148 //
149 int ReadThermoStatus(int portnum, uchar *SerialNum, 
150                    ThermoStateType *ThermoState, FILE *fp)
151 {
152    // set the serial num
153    owSerialNum(portnum, SerialNum, FALSE);
154
155    // run the script and read status of thermochron
156    return RunThermoScript(portnum,ThermoState,GetStatus,fp);
157
158
159 //--------------------------------------------------------------------------
160 // The 'MissionThermo' starts a new Thermochron mission on 'SerialNum'
161 // from the state information provided in 'ThermoState'. Progress output
162 // is printed to the specified file 'fp'. 
163 //
164 // 'portnum'     - number 0 to MAX_PORTNUM-1.  This number is provided to
165 //                 indicate the symbolic port number.
166 // 'SerialNum'   - Device serial number to download
167 // 'ThermoState' - pointer to a structure type that holds the raw and
168 //                 translated Thermochron data.
169 // 'fp'          - file pointer to print status information to
170 //
171 // Returns:   TRUE (1) : Thermochron missioned
172 //            FALSE (0): not missioned.  Abort due to repeated errors
173 //                       or user keypress.
174 //
175 int MissionThermo(int portnum, uchar *SerialNum, 
176                    ThermoStateType *ThermoState, FILE *fp)
177 {
178    // set the serial num
179    owSerialNum(portnum, SerialNum, FALSE);
180
181    // run the script and mission thermochron
182    return RunThermoScript(portnum,ThermoState,Mission,fp);
183 }
184
185 //--------------------------------------------------------------------------
186 // Run the specified script.  Return TRUE if all steps completed else FALSE.
187 // Status is printed to file 'fp'.  
188 //
189 int RunThermoScript(int portnum, ThermoStateType *ThermoState,
190                     ThermoScript script[], FILE *fp)
191 {
192    char msg[256],LastDescription[256],LastMsg[256];
193    int StepCount,SubStep,ErrorCount,Status;
194    int last_clear_step=0;
195    
196    // reset the step to the begining
197    StepCount = 0;
198    SubStep = 0;
199    ErrorCount = 0;
200    Status = STATUS_INPROGRESS;
201  
202    // loop to perform all of the steps to download the Thermochron
203    do
204    {   
205       // switch on the status of the last step done
206       switch(Status)
207       {
208          // step complete so go to the next
209          case STATUS_STEP_COMPLETE:
210             StepCount++;
211             SubStep = 0;
212             ErrorCount = 0;
213             Status = STATUS_INPROGRESS;
214             LastDescription[0] = 0;
215             LastMsg[0] = 0;
216             break;
217          // in progress so call again
218          case STATUS_INPROGRESS:
219             // record the step position of the last memory clear
220             // this is in case we need to attempt a clear again
221             if (script[StepCount].Step == ST_CLEAR_SETUP)
222                last_clear_step = StepCount;               
223
224             // print step description if different 
225             if (strcmp(LastDescription,
226                 script[StepCount].StepDescription) != 0)
227             {
228                fprintf(fp,"%s --> ",script[StepCount].StepDescription);
229                sprintf(LastDescription,"%s",script[StepCount].StepDescription);
230             }
231
232             // perform a step in the job
233             ThermoStep(portnum,ThermoState,&script[StepCount],&SubStep, 
234                       &Status, &ErrorCount, msg);
235
236             // print results if different
237             if (strcmp(LastMsg,msg) != 0)
238             {
239                fprintf(fp,"%s\n",msg);
240                sprintf(LastMsg,"%s",msg);
241             }
242             else
243                fprintf(fp,".");
244             break;     
245          // encountered a transient error
246          case STATUS_ERROR_TRANSIENT:
247             // check if transient error is a memory clear
248             if (script[StepCount].Step == ST_CLEAR_VERIFY)
249             {
250                // put back to starting clear over again
251                StepCount = last_clear_step;
252                SubStep = 0;
253                ErrorCount = 0;
254                Status = STATUS_INPROGRESS;
255                break; 
256             }    
257             // if 20 tansient errors in a row then abort
258             if (ErrorCount > 20)
259                Status = STATUS_ERROR_HALT;
260             else
261                Status = STATUS_INPROGRESS;
262             break;
263          // all steps complete
264          case STATUS_COMPLETE:
265             fprintf(fp,"End script normally\n");
266             return TRUE;
267             break;
268          // non-recoverable error
269          case STATUS_ERROR_HALT:
270             fprintf(fp,"Aborting script due to non-recoverable error\n");
271             return FALSE;
272             break;
273       }
274    }
275    while (!Serial0CharArrived());
276  
277    // key abort
278    fprintf(fp,"Aborting script due to key press\n");
279    return FALSE;
280 }
281
282 //----------------------------------------------------------------------
283 //  Use the script to perform a step and return.
284 //
285 int ThermoStep(int portnum, ThermoStateType *ThermoState, 
286                ThermoScript *StateScript, int *SubStep, 
287                int *Status, int *ErrorCount, char *msg)
288 {
289    short  rslt;
290    static int read_page_num, read_pages, write_addr, write_len;
291    static uchar *read_buf, *write_buf;
292    static uchar tbuf[5];
293
294    ErrorCount; // hush the compiler
295
296    // do the current step
297    switch (StateScript->Step)
298    {
299       // the operation is complete      
300       case ST_FINISH:
301          sprintf(msg,"Operation complete");
302          *Status = STATUS_COMPLETE;
303          break;      
304
305       // read the mission status page
306       case ST_READ_STATUS:
307          read_page_num = STATUS_PAGE;
308          read_pages = 1;
309          read_buf = ThermoState->MissStat.status_raw;
310          sprintf(msg,"Ready to read status page %d",
311                       read_page_num);
312          *Status = STATUS_STEP_COMPLETE;
313          break;      
314
315       // set up to read the alarm registers
316       case ST_READ_ALARM:
317          read_page_num = 17;
318          read_pages = 3;
319          read_buf = ThermoState->AlarmData.alarm_raw;
320          sprintf(msg,"Ready to read alarm pages %d to %d",
321                       read_page_num, read_page_num + read_pages - 1);
322          *Status = STATUS_STEP_COMPLETE;
323          break;
324          
325       // set up to read the histogram data
326       case ST_READ_HIST:
327          read_page_num = 64;
328          read_pages = 4;
329          read_buf = ThermoState->HistData.hist_raw;
330          sprintf(msg,"Ready to read histogram pages %d to %d",
331                       read_page_num, read_page_num + read_pages - 1);
332          *Status = STATUS_STEP_COMPLETE;
333          break;
334
335       // set up to read the log data
336       case ST_READ_LOG:
337          read_page_num = 128;
338          read_pages = 64;
339          read_buf = ThermoState->LogData.log_raw;
340          sprintf(msg,"Ready to read log pages %d to %d",
341                       read_page_num, read_page_num + read_pages - 1);
342          *Status = STATUS_STEP_COMPLETE;
343          break;
344
345       // read the specified pages
346       case ST_READ_PAGES:
347          // check for last page
348          if (*SubStep == 0)
349             // set the sub-step to the current page being read
350             *SubStep =  read_page_num;
351          // read the status page
352          rslt = ReadPages(portnum, read_page_num, read_pages, SubStep, read_buf);
353          if (rslt == FALSE)
354          {
355             sprintf(msg,"Thermochron not on 1-Wire Net");
356             *Status = STATUS_INPROGRESS;
357          }
358          else 
359          {
360             sprintf(msg,"Pages read from Thermochron");
361             *Status = STATUS_STEP_COMPLETE;
362          }
363          break;      
364
365       // setup the clear memory
366       case ST_CLEAR_SETUP:
367          // create a small buff to write to start the clear memory
368          tbuf[0] = 0x40;
369          write_buf = &tbuf[0];
370          write_len = 1;
371          write_addr = 0x20E;
372          sprintf(msg,"Write to setup clear memory");
373          *Status = STATUS_STEP_COMPLETE;
374          break;
375
376       // clear the memory
377       case ST_CLEAR_MEM:
378          // set the clear memory command (not check return because verify)        
379          owAccess(portnum);
380          owWriteByte(portnum,0x3C);
381          msDelay(3);
382          owTouchReset(portnum);
383          sprintf(msg,"Clear memory command sent");
384          *Status = STATUS_STEP_COMPLETE;
385          break;
386
387       // clear the memory
388       case ST_CLEAR_VERIFY:
389          // look at the memory clear bit
390          if ((ThermoState->MissStat.status_raw[0x14] & 0x40) == 0x40)
391          {
392             sprintf(msg,"Memory is clear");
393             *Status = STATUS_STEP_COMPLETE;
394             break;
395          }
396          else
397          {
398             sprintf(msg,"Memory did NOT clear");
399             *Status = STATUS_ERROR_TRANSIENT;
400             break;
401          }
402          break;      
403
404       // setup write time, clock alarm, control, trips
405       case ST_WRITE_TIME:
406          // create the write buffer
407          FormatMission(&ThermoState->MissStat);
408          write_buf = &ThermoState->MissStat.status_raw[0x00];
409          write_len = 13;
410          write_addr = 0x200;
411          sprintf(msg,"Write time, clock alarm, and trips setup");
412          *Status = STATUS_STEP_COMPLETE;
413          break;
414
415       // write the control, mission delay and clear flags
416       case ST_WRITE_CONTROL:
417          write_buf = &ThermoState->MissStat.status_raw[0x0E];
418          write_len = 7;
419          write_addr = 0x20E;
420          sprintf(msg,"Write control, mission delay, clear flags setup");
421          *Status = STATUS_STEP_COMPLETE;
422          break;
423
424       case ST_WRITE_RATE:
425          write_buf = &ThermoState->MissStat.status_raw[0x0D];
426          write_len = 1;
427          write_addr = 0x20D;
428          sprintf(msg,"Write sample rate setup");
429          *Status = STATUS_STEP_COMPLETE;
430          break;
431
432       // write the specified memory location 
433       case ST_WRITE_MEM:
434          if (WriteMemory(portnum, write_buf, write_len, write_addr))
435          {
436             sprintf(msg,"Memory written to Thermochron");
437             *Status = STATUS_STEP_COMPLETE;
438          }
439          else
440          {
441             sprintf(msg,"Thermochron not on 1-Wire Net");
442             *Status = STATUS_INPROGRESS;
443          }
444       default:
445            break;
446    }
447
448    return *Status;
449 }
450
451 //----------------------------------------------------------------------
452 //  Read a specified number of pages in overdrive
453 //
454 // 'portnum'  - number 0 to MAX_PORTNUM-1.  This number is provided to
455 //              indicate the symbolic port number.
456 //
457 int ReadPages(int portnum, int start_pg, int num_pgs, int *last_pg, uchar *finalbuf)
458 {
459    int skip_overaccess = 0, skip_access = 0;
460    uchar pkt[60];
461    int len,i;
462    uchar  SerialNumber[8];
463    ushort lastcrc16;
464
465    // read the rom number 
466    owSerialNum(portnum,SerialNumber,TRUE);
467
468    // verify device is in overdrive
469    if (current_speed[portnum] == MODE_OVERDRIVE)
470    {
471       if (owVerify(portnum,FALSE)) 
472          skip_overaccess = 1;
473    }
474
475    if (!skip_overaccess)
476    {
477       if (owOverdriveAccess(portnum))
478          current_speed[portnum] = MODE_OVERDRIVE;
479       else
480          current_speed[portnum] = MODE_NORMAL;
481    }
482
483    // loop while there is pages to read
484    do
485    {
486       // create a packet to read a page
487       len = 0;
488       setcrc16(portnum,0);
489       // optional skip access on subsequent pages 
490       if (!skip_access)
491       {  
492          // match
493          pkt[len++] = 0x55; 
494          // rom number
495          for (i = 0; i < 8; i++)
496             pkt[len++] = SerialNumber[i];
497          // read memory with crc command 
498          pkt[len] = 0xA5; 
499          docrc16(portnum,pkt[len++]);         
500          // address
501          pkt[len] = (uchar)((*last_pg << 5) & 0xFF);
502          docrc16(portnum,pkt[len++]);         
503          pkt[len] = (uchar)(*last_pg >> 3); 
504          docrc16(portnum,pkt[len++]);         
505       }
506
507       // set 32 reads for data and 2 for crc
508       for (i = 0; i < 34; i++)
509          pkt[len++] = 0xFF; 
510          
511       // send the bytes
512       if (owBlock(portnum,!skip_access,pkt,len))
513       {
514          // calucate the CRC over the last 34 bytes
515          for (i = 0; i < 34; i++)
516             lastcrc16 = docrc16(portnum,pkt[len - 34 + i]);
517
518          // check crc
519          if (lastcrc16 == 0xB001)
520          {
521             // copy the data into the buffer
522 #ifdef LetsCrashTheCompiler
523            for (i = 0; i < 32; i++)
524              finalbuf[i + (*last_pg - start_pg) * 32] = pkt[len - 34 + i];
525 #endif
526            {
527              ushort k;
528              for (i = 0; i < 32; i++) {
529                k=i + (*last_pg - start_pg) * 32;
530                finalbuf[k] = pkt[len - 34 + i];
531              }
532            }
533             // change number of pages 
534             *last_pg = *last_pg + 1;
535
536             // now skip access 
537             skip_access = TRUE;
538          }
539          else
540             return FALSE;
541       }
542       else
543          return FALSE;
544    }
545    while ((*last_pg - start_pg) < num_pgs);
546
547    return TRUE;
548 }
549
550 //----------------------------------------------------------------------------}
551 // Write a memory location. Data must all be on the same page
552 //
553 // 'portnum'  - number 0 to MAX_PORTNUM-1.  This number is provided to
554 //              indicate the symbolic port number.
555 //
556 int WriteMemory(int portnum, uchar *Buf, int ln, int adr)
557 {
558    // write to scratch and then copy
559    if (WriteScratch(portnum,Buf,ln,adr)) 
560       return CopyScratch(portnum,ln,adr);
561
562    return FALSE;
563 }
564
565 //----------------------------------------------------------------------------}
566 // Write the scratch pad
567 //
568 // 'portnum'  - number 0 to MAX_PORTNUM-1.  This number is provided to
569 //              indicate the symbolic port number.
570 //
571 int WriteScratch(int portnum, uchar *Buf, int ln, int adr)
572 {
573    int i;
574    uchar pbuf[80];
575
576    // check for alarm indicator 
577    if (owAccess(portnum)) 
578    {
579       // construct a packet to send  
580       pbuf[0] = 0x0F; // write scratch command 
581       pbuf[1] = (adr & 0xFF); // address 1 
582       pbuf[2] = ((adr >> 8) & 0xFF); // address 2 
583
584       // the write bytes 
585       for (i = 0; i < ln; i++)
586         pbuf[3 + i] = (uchar)(Buf[i]); // data 
587
588       // perform the block 
589       if (!owBlock(portnum,FALSE,pbuf,ln+3))
590          return FALSE;
591
592       // Now read back the scratch 
593       if (owAccess(portnum)) 
594       {
595          // construct a packet to send 
596          pbuf[0] = 0xAA; // read scratch command 
597          pbuf[1] = 0xFF; // address 1 
598          pbuf[2] = 0xFF; // address 2 
599          pbuf[3] = 0xFF; // offset 
600
601          // the write bytes 
602          for (i = 0; i < ln; i++)
603             pbuf[4 + i] = 0xFF; // data 
604
605          // perform the block  
606          if (!owBlock(portnum,FALSE,pbuf,ln+4))
607             return FALSE;
608
609          // read address 1 
610          if (pbuf[1] != (adr & 0xFF)) 
611             return FALSE;
612          // read address 2 
613          if (pbuf[2] != ((adr >> 8) & 0xFF)) 
614             return FALSE;
615          // read the offset 
616          if (pbuf[3] != ((adr + ln - 1) & 0x1F)) 
617             return FALSE;
618          // read and compare the contents 
619          for (i = 0; i < ln; i++)
620          {
621             if (pbuf[4 + i] != Buf[i]) 
622               return FALSE;
623          }
624          // success
625          return TRUE;
626       }
627    }
628
629    return FALSE;
630 }
631
632
633 //----------------------------------------------------------------------------}
634 // Copy the scratch pad
635 //
636 // 'portnum'  - number 0 to MAX_PORTNUM-1.  This number is provided to
637 //              indicate the symbolic port number.
638 //
639 int CopyScratch(int portnum, int ln, int adr)
640 {
641    int i;
642    uchar pbuf[50];
643
644    // check for alarm indicator 
645    if (owAccess(portnum)) 
646    {
647       // construct a packet to send 
648       pbuf[0] = 0x55;                  // copy scratch command 
649       pbuf[1] = (adr & 0xFF);          // address 1 
650       pbuf[2] = ((adr >> 8) & 0xFF);   // address 2 
651       pbuf[3] = (adr + ln - 1) & 0x1F; // offset 
652       for (i = 0; i <= 9; i++)
653          pbuf[4 + i] = 0xFF;           // result of copy 
654
655       // perform the block 
656       if (owBlock(portnum,FALSE,pbuf,14))
657       {
658          if ((pbuf[13] == 0x55) ||
659              (pbuf[13] == 0xAA)) 
660            return TRUE;
661       }
662    }
663
664    return FALSE;
665 }
666
667 //----------------------------------------------------------------------
668 //  Interpret the Status by looking at the 'raw' portion of the 
669 //  mission status structure.
670 //
671 void InterpretStatus(MissionStatus *mstatus)
672 {
673    timedate td,tdtmp;
674    int offset;
675    ulong tmtmp;
676    time_t tlong; 
677    struct tm *tstruct; 
678
679    // mission in progress flag
680    mstatus->mission_in_progress = (0x20 & mstatus->status_raw[0x14]) >> 5;
681
682    // sample rate
683    mstatus->sample_rate = mstatus->status_raw[0x0D];
684
685    // rollover enabled 
686    mstatus->rollover_enable = (0x08 & mstatus->status_raw[0x0E]) >> 3;
687
688    // startdelay
689    mstatus->start_delay = ((int)mstatus->status_raw[0x13] << 8) | 
690                             mstatus->status_raw[0x12];
691
692    // number of samples in this mission
693    mstatus->mission_samples = ((long)mstatus->status_raw[0x1C] << 16) |
694                               ((int)mstatus->status_raw[0x1B] << 8) | 
695                                mstatus->status_raw[0x1A];
696
697    // total number of samples 
698    mstatus->samples_total = ((long)mstatus->status_raw[0x1F] << 16) |
699                             ((int)mstatus->status_raw[0x1E] << 8) | 
700                              mstatus->status_raw[0x1D];
701
702    // temperature thresholds
703    mstatus->high_threshold = mstatus->status_raw[0x0C];
704    mstatus->low_threshold = mstatus->status_raw[0x0B];
705
706    // rollover occurred
707    if ((mstatus->mission_samples > 2048) && mstatus->rollover_enable)
708       mstatus->rollover_occurred = 1;
709    else
710       mstatus->rollover_occurred = 0;
711
712    // current real-time clock value
713    offset = 0x00;
714    td.second = BCDToBin((uchar)(mstatus->status_raw[offset] & 0x7F));
715    td.minute = BCDToBin((uchar)(mstatus->status_raw[offset + 1] & 0x7F));
716    // check for 12 hour mode
717    if (mstatus->status_raw[offset + 2] & 0x40)
718    {
719       td.hour = BCDToBin((uchar)(mstatus->status_raw[offset + 2] & 0x1F));
720       // check for PM
721       if (mstatus->status_raw[offset + 2] & 0x20)
722          td.hour += 12;
723    }
724    else
725       td.hour = BCDToBin((uchar)(mstatus->status_raw[offset + 2] & 0x3F));
726    td.day = BCDToBin((uchar)(mstatus->status_raw[offset + 4] & 0x3F));
727    td.month = BCDToBin((uchar)(mstatus->status_raw[offset + 5] & 0x1F));
728    td.year = BCDToBin(mstatus->status_raw[offset + 6]) + 1900;
729    // check for century bit
730    if (mstatus->status_raw[offset + 5] & 0x80)
731       td.year = BCDToBin(mstatus->status_raw[offset + 6]) + 2000; // (2.00)
732    // convert to seconds since 1970
733    mstatus->current_time = DateToSeconds(&td);
734
735    // date/time when mission started
736    offset = 0x15;
737    td.second = (uchar)0;
738    td.minute = BCDToBin((uchar)(mstatus->status_raw[offset] & 0x7F));
739    // check for 12 hour mode
740    if (mstatus->status_raw[offset + 1] & 0x40)
741    {
742       td.hour = BCDToBin((uchar)(mstatus->status_raw[offset + 1] & 0x1F));
743       // check for PM
744       if (mstatus->status_raw[offset + 1] & 0x20)
745          td.hour += 12;
746    }
747    else
748       td.hour = BCDToBin((uchar)(mstatus->status_raw[offset + 1] & 0x3F));
749    td.day = BCDToBin((uchar)(mstatus->status_raw[offset + 2] & 0x3F));
750    td.month = BCDToBin((uchar)(mstatus->status_raw[offset + 3] & 0x1F));
751    td.year = BCDToBin((uchar)(mstatus->status_raw[offset + 4])); // (2.00)
752    // (2.00) logic to decide on century of mission stamp   
753    // check if century bit set in mission stamp
754    if (mstatus->status_raw[offset + 3] & 0x80)
755       td.year += 2000;
756    // check in mission in progress
757    else if (mstatus->mission_in_progress)
758    {
759       // calculate the mission start year back from real time clock
760       tmtmp = mstatus->current_time - 
761              (mstatus->sample_rate * mstatus->mission_samples * 60);
762       SecondsToDate(&tdtmp,tmtmp);
763       td.year = tdtmp.year;      
764    }
765    else
766    {
767       // mission stopped so get century by year window
768       if (td.year <= 70)
769          td.year += 2000;
770       else
771          td.year += 1900;
772    }
773    // convert to seconds since 1970
774    if ((td.month == 0) || (td.day == 0))
775       mstatus->mission_start_time = 0;
776    else
777       mstatus->mission_start_time = DateToSeconds(&td);
778       
779    // download stations time of reading
780    time(&tlong);
781    tstruct = localtime(&tlong); 
782    td.day = tstruct->tm_mday;
783    td.month = tstruct->tm_mon + 1;  // (1.01)
784    td.year = tstruct->tm_year + 1900;
785    td.hour = tstruct->tm_hour;
786    td.minute = tstruct->tm_min;
787    td.second = tstruct->tm_sec;
788    mstatus->download_time = DateToSeconds(&td);
789
790    // skip alarm modes and status for now
791 }
792
793 //--------------------------------------------------------------------------
794 // Take the Mission Status structure and create new raw data to start
795 // a new mission.
796 //
797 void FormatMission(MissionStatus *mstatus)
798 {
799    int i;
800    time_t tlong; 
801    struct tm *tstruct; 
802
803    // clear the buffer
804    for (i = 0; i < 32; i++)
805       mstatus->status_raw[i] = 0;
806    
807    // Real Time Clock
808    time(&tlong);
809    tlong++;  // add 1 second
810    tstruct = localtime(&tlong); 
811    // convert to BCD
812    mstatus->status_raw[0x00] = ToBCD((short)tstruct->tm_sec);
813    mstatus->status_raw[0x01] = ToBCD((short)tstruct->tm_min);
814    mstatus->status_raw[0x02] = ToBCD((short)tstruct->tm_hour);
815    mstatus->status_raw[0x03] = ToBCD((short)(tstruct->tm_wday + 1));
816    mstatus->status_raw[0x04] = ToBCD((short)tstruct->tm_mday);
817    mstatus->status_raw[0x05] = ToBCD((short)(tstruct->tm_mon + 1));
818    if (tstruct->tm_year >= 100)
819       mstatus->status_raw[0x05] |= 0x80;
820    mstatus->status_raw[0x06] = ToBCD((short)(tstruct->tm_year % 100));
821    // Real Time clock Alarm (leave 0's)
822    // Low temp alarm
823    mstatus->status_raw[0x0B] = mstatus->low_threshold;
824    // High temp alarm
825    mstatus->status_raw[0x0C] = mstatus->high_threshold;
826    // sample rate
827    mstatus->status_raw[0x0D] = mstatus->sample_rate;
828    // control
829    mstatus->status_raw[0x0E] = 0x40;
830    if (mstatus->rollover_enable)
831       mstatus->status_raw[0x0E] |= 0x08;
832    // mission start delay
833    mstatus->status_raw[0x12] = mstatus->start_delay & 0xFF;
834    mstatus->status_raw[0x13] = (mstatus->start_delay >> 8) & 0xFF;
835 }
836
837 //--------------------------------------------------------------------------
838 // Convert an integer to a 1 Byte BCD number (99 max) 
839 //
840 uchar ToBCD(short num)
841 {
842    uchar rtbyte;
843
844    rtbyte = (num - ((num / 10) * 10)) & 0x0F;
845    rtbyte = rtbyte | ((num / 10) << 4);
846    
847    return rtbyte;
848 }
849
850
851 //--------------------------------------------------------------------------
852 // Take the Mission Status structure and convert to string format
853 //
854 void MissionStatusToString(MissionStatus *mstatus, int ConvertToF, char *str)
855 {
856    int cnt=0,i;
857    timedate td;
858    time_t tlong; 
859    struct tm *tstruct; 
860
861    // title
862    cnt += sprintf(&str[cnt],"Mission State\n-------------\n");
863
864    // serial number
865    cnt += sprintf(&str[cnt],"Serial Number of DS1921: ");
866    for (i = 7; i >= 0; i--)
867       cnt += sprintf(&str[cnt],"%02X",mstatus->serial_num[i]);
868
869    // mission state
870    if (mstatus->mission_in_progress)
871       cnt += sprintf(&str[cnt],"\nMission is in progress\n");
872    else
873       cnt += sprintf(&str[cnt],"\nMission is ended\n");
874
875    // sample rate
876    cnt += sprintf(&str[cnt],"Sample rate: %d minute(s)\n",mstatus->sample_rate);
877
878    // rollover
879    cnt += sprintf(&str[cnt],"Roll-Over Enabled: ");
880    if (mstatus->rollover_enable)
881       cnt += sprintf(&str[cnt],"yes\n");
882    else
883       cnt += sprintf(&str[cnt],"no\n");
884    cnt += sprintf(&str[cnt],"Roll-Over Occurred: ");
885    if (mstatus->rollover_occurred)
886       cnt += sprintf(&str[cnt],"yes\n");
887    else
888       cnt += sprintf(&str[cnt],"no\n");
889   
890    // mission start time
891    if (mstatus->start_delay == 0)
892    {
893       SecondsToDate(&td,mstatus->mission_start_time);      
894       if (mstatus->mission_start_time == 0)
895          cnt += sprintf(&str[cnt],"Mission Start time: not started yet\n");
896       else
897          cnt += sprintf(&str[cnt],"Mission Start time: %02d/%02d/%04d  %02d:%02d:%02d\n",
898             td.month,td.day,td.year,td.hour,td.minute,td.second);
899    }
900    else
901       cnt += sprintf(&str[cnt],"Mission Start time: na\n");
902
903    // mission start delay
904    cnt += sprintf(&str[cnt],"Mission Start delay: %d minute(s)\n",mstatus->start_delay);
905    
906    // mission samples
907    cnt += sprintf(&str[cnt],"Mission Samples: %d\n",mstatus->mission_samples);
908
909    // device total samples
910    cnt += sprintf(&str[cnt],"Device total samples: %d\n",mstatus->samples_total);
911
912    // temperature display mode
913    cnt += sprintf(&str[cnt],"Temperature displayed in: ");   
914    if (ConvertToF)
915       cnt += sprintf(&str[cnt],"(Fahrenheit)\n");
916    else
917       cnt += sprintf(&str[cnt],"(Celsius)\n");
918
919    // thresholds
920    cnt += sprintf(&str[cnt],"High Threshold: %6.1f\n",
921           TempToFloat(mstatus->high_threshold,ConvertToF));   
922    cnt += sprintf(&str[cnt],"Low Threshold: %6.1f\n",
923           TempToFloat(mstatus->low_threshold,ConvertToF));   
924    
925    // time from D1921
926    SecondsToDate(&td,mstatus->current_time);      
927    cnt += sprintf(&str[cnt],"Current Real-Time Clock from DS1921: %02d/%02d/%04d  %02d:%02d:%02d\n",
928        td.month,td.day,td.year,td.hour,td.minute,td.second);
929
930    // current PC time
931    time(&tlong);
932    tstruct = localtime(&tlong); 
933    cnt += sprintf(&str[cnt],"Current PC Time: %02d/%02d/%04d  %02d:%02d:%02d\n",
934        tstruct->tm_mon + 1,tstruct->tm_mday,tstruct->tm_year + 1900,
935        tstruct->tm_hour,tstruct->tm_min,tstruct->tm_sec);
936
937    // zero terminate string
938    str[cnt] = 0;
939 }
940
941 //----------------------------------------------------------------------
942 //  Interpret the Histogram by looking at the 'raw' portion of the 
943 //  Histogram structure.  Store the temperature range values in Celsius.
944 //
945 void InterpretHistogram(Histogram *hist)
946 {
947    int i;
948
949    // loop through each bin value
950    for (i = 0; i < 126; i += 2) // (2.00)
951    {
952       // get the bin value
953       hist->bin_count[i / 2] = hist->hist_raw[i] | ((int)hist->hist_raw[i + 1] << 8);
954
955       // start value for this bin
956       hist->start_range[i / 2] = TempToFloat((uchar)((i / 2) << 2),FALSE);
957
958       // end value for this bin
959       hist->end_range[i / 2] = TempToFloat((uchar)(((i / 2) << 2) | 0x03),FALSE);
960    }
961 }
962
963 //--------------------------------------------------------------------------
964 // Take the Histogram structure and convert to string format
965 //
966 void HistogramToString(Histogram *hist, int ConvertToF, char *str)
967 {
968    int cnt=0,i;
969
970    // title
971    cnt += sprintf(&str[cnt],"Temperature Histogram\n---------------------\n"  
972                             "Format: [Temp Range, Count] ");
973    if (ConvertToF)
974       cnt += sprintf(&str[cnt],"(Fahrenheit)\n");
975    else
976       cnt += sprintf(&str[cnt],"(Celsius)\n");
977
978    // loop through bins
979    for (i = 0; i < 63; i++) // (2.00)
980    {
981       cnt += sprintf(&str[cnt],"%6.1f to %6.1f, %d\n", 
982                      (ConvertToF) ? CToF(hist->start_range[i]): hist->start_range[i],
983                      (ConvertToF) ? CToF(hist->end_range[i]): hist->end_range[i],
984                       hist->bin_count[i]);
985    }
986
987    // zero terminate string
988    str[cnt] = 0;
989 }
990
991 //----------------------------------------------------------------------
992 //  Interpret the Temperature Alarm Event data by looking at the 'raw' 
993 //  portion of the TempAlarmEvents structure.  Mission Status is needed
994 //  to interpret the events.
995 //
996 void InterpretAlarms(TempAlarmEvents *alarm, MissionStatus *mstatus)
997 {
998    int i;
999    ulong event_mission_count;
1000    uchar duration;
1001
1002    // low events
1003    alarm->num_low = 0;
1004    for (i = 0; i < 48; i += 4)
1005    {
1006       // get the mission start count of this event
1007       event_mission_count = ((long)alarm->alarm_raw[i + 2] << 16) |
1008                             ((int)alarm->alarm_raw[i + 1] << 8) |
1009                              alarm->alarm_raw[i];  
1010
1011       // check if done with low events
1012       if (!event_mission_count)
1013          break;
1014
1015       // get the duration
1016       duration = alarm->alarm_raw[i + 3];
1017    
1018       // calculate the start time
1019       alarm->low_start_time[alarm->num_low] =
1020           mstatus->mission_start_time + 
1021           (event_mission_count - 1) * (mstatus->sample_rate * 60);  
1022
1023       // calculate the end time
1024       alarm->low_end_time[alarm->num_low] =
1025           alarm->low_start_time[alarm->num_low] + 
1026           (duration - 1) * (mstatus->sample_rate * 60);
1027
1028       // increment number of low events
1029       alarm->num_low++;
1030    }
1031
1032    // high events
1033    alarm->num_high = 0;
1034    for (i = 48; i < 96; i += 4)
1035    {
1036       // get the mission start count of this event
1037       event_mission_count = ((long)alarm->alarm_raw[i + 2] << 16) |
1038                             ((int)alarm->alarm_raw[i + 1] << 8) |
1039                              alarm->alarm_raw[i];  
1040
1041       // check if done with low events
1042       if (!event_mission_count)
1043          break;
1044
1045       // get the duration
1046       duration = alarm->alarm_raw[i + 3];
1047
1048       // calculate the start time
1049       alarm->high_start_time[alarm->num_high] =
1050           mstatus->mission_start_time + 
1051           (event_mission_count - 1) * (mstatus->sample_rate * 60);  
1052
1053       // calculate the end time
1054       alarm->high_end_time[alarm->num_high] =
1055           alarm->high_start_time[alarm->num_high] + 
1056           (duration - 1) * (mstatus->sample_rate * 60);
1057
1058       // increment number of low events
1059       alarm->num_high++;
1060    }
1061 }
1062
1063 //--------------------------------------------------------------------------
1064 // Take the Temperature Alarms Events structure and convert to string 
1065 // format
1066 //
1067 void AlarmsToString(TempAlarmEvents *alarm, char *str)
1068 {
1069    int i, cnt=0;
1070    timedate td;
1071
1072    // title
1073    cnt += sprintf(&str[cnt],"Temperature Alarms\n------------------\n"  
1074                             "Format: [(HIGH/LOW), Time/Date Range]\n");
1075
1076    // loop through each low alarm
1077    for (i = 0; i < alarm->num_low; i++)
1078    {
1079       cnt += sprintf(&str[cnt],"LOW  , ");
1080       // start time
1081       SecondsToDate(&td,alarm->low_start_time[i]);      
1082       cnt += sprintf(&str[cnt]," %02d/%02d/%04d  %02d:%02d  to  ",
1083          td.month,td.day,td.year,td.hour,td.minute);
1084       // end time
1085       SecondsToDate(&td,alarm->low_end_time[i]);      
1086       cnt += sprintf(&str[cnt]," %02d/%02d/%04d  %02d:%02d\n",
1087          td.month,td.day,td.year,td.hour,td.minute);
1088    }
1089
1090    // loop through each high alarm
1091    for (i = 0; i < alarm->num_high; i++)
1092    {
1093       cnt += sprintf(&str[cnt],"HIGH , ");
1094       // start time
1095       SecondsToDate(&td,alarm->high_start_time[i]);      
1096       cnt += sprintf(&str[cnt]," %02d/%02d/%04d  %02d:%02d  to  ",
1097          td.month,td.day,td.year,td.hour,td.minute);
1098       // end time
1099       SecondsToDate(&td,alarm->high_end_time[i]);      
1100       cnt += sprintf(&str[cnt]," %02d/%02d/%04d  %02d:%02d\n",
1101          td.month,td.day,td.year,td.hour,td.minute);
1102    }
1103
1104    // zero terminate string
1105    str[cnt] = 0;
1106 }
1107
1108 //----------------------------------------------------------------------
1109 //  Interpret the Log data by looking at the 'raw' 
1110 //  portion of the Log structure.  Mission Status is needed
1111 //  to interpret when the logs occurred.
1112 //
1113 void InterpretLog(Log *log, MissionStatus *mstatus)
1114 {
1115    ulong loops=0,overlap=0,lastlog=2048,i;
1116    int logcnt=0;
1117
1118    // check if wrap occurred
1119    if (mstatus->rollover_occurred)
1120    {
1121       // calculate the number loops 
1122       loops = (mstatus->mission_samples / 2048) - 1;
1123       // calculate the number of overlap
1124       overlap = mstatus->mission_samples % 2048;
1125       log->num_log = 2048;
1126    }
1127    else
1128    {
1129       log->start_time = mstatus->mission_start_time;
1130       if (mstatus->mission_samples > 2048)  // (1.02)  
1131          lastlog = 2048;
1132       else
1133          lastlog = mstatus->mission_samples;
1134       log->num_log = (int)lastlog;
1135    }
1136
1137    // set the interval
1138    log->interval = mstatus->sample_rate * 60;
1139
1140    // caluclate the start time of the first log value
1141    log->start_time = mstatus->mission_start_time +
1142       loops * 2048 * log->interval + overlap * log->interval;
1143
1144    // loop to fill in the remainder first
1145    for (i = overlap; i < lastlog; i++)
1146       log->temp[logcnt++] = TempToFloat(log->log_raw[i],FALSE);
1147
1148    // loop to get the overlap
1149    for (i = 0; i < overlap; i++)
1150       log->temp[logcnt++] = TempToFloat(log->log_raw[i],FALSE);
1151 }
1152
1153 //--------------------------------------------------------------------------
1154 // Take the Log structure and convert to string 
1155 // format
1156 //
1157 void LogToString(Log *log, int ConvertToF, char *str)
1158 {
1159    int i,cnt=0;
1160    ulong logtime;
1161    timedate td;
1162
1163    // title
1164    cnt += sprintf(&str[cnt],"Log Data\n--------\n"  
1165                             "Format: [Time/Date , Temperature] ");
1166    if (ConvertToF)
1167       cnt += sprintf(&str[cnt],"(Fahrenheit)\n");
1168    else
1169       cnt += sprintf(&str[cnt],"(Celsius)\n");
1170
1171    // loop through the logs
1172    logtime = log->start_time;
1173    for (i = 0; i < log->num_log; i++)
1174    {
1175       // time
1176       SecondsToDate(&td,logtime);      
1177       cnt += sprintf(&str[cnt],"%02d/%02d/%04d  %02d:%02d ,",
1178          td.month,td.day,td.year,td.hour,td.minute);
1179       // temp
1180       cnt += sprintf(&str[cnt],"%6.1f\n", 
1181              (ConvertToF) ? CToF(log->temp[i]): log->temp[i]);
1182
1183       // increment the time
1184       logtime += log->interval;
1185    }
1186
1187    // zero terminate string
1188    str[cnt] = 0;
1189 }
1190
1191 //--------------------------------------------------------------------------
1192 // Convert the raw debug data to a string
1193 //
1194 void DebugToString(MissionStatus *mstatus, TempAlarmEvents *alarm, 
1195                    Histogram *hist, Log *log, char *str) 
1196 {
1197    int i,cnt=0;
1198
1199    // title
1200    cnt += sprintf(&str[cnt],"Debug Dump\n----------\nRegister Page:\n");
1201
1202    // reg
1203    for (i = 0; i < 32; i++)
1204    {
1205       cnt += sprintf(&str[cnt],"%02X ",mstatus->status_raw[i]);
1206       if (i && (((i + 1) % 16) == 0))
1207          cnt += sprintf(&str[cnt],"\n");
1208    }
1209
1210    // alarms
1211    cnt += sprintf(&str[cnt],"Alarms:\n");
1212    for (i = 0; i < 96; i++)
1213    {
1214       cnt += sprintf(&str[cnt],"%02X ",alarm->alarm_raw[i]);
1215       if (i && (((i + 1) % 16) == 0))
1216          cnt += sprintf(&str[cnt],"\n");
1217    }
1218
1219    // histogram
1220    cnt += sprintf(&str[cnt],"Histogram:\n");
1221    for (i = 0; i < 128; i++)
1222    {
1223       cnt += sprintf(&str[cnt],"%02X ",hist->hist_raw[i]);
1224       if (i && (((i + 1) % 16) == 0))
1225          cnt += sprintf(&str[cnt],"\n");
1226    }
1227
1228
1229    // log
1230    cnt += sprintf(&str[cnt],"Log:\n");
1231    for (i = 0; i < ((log->num_log > 2048) ? 2048 : log->num_log); i++)
1232    {
1233       cnt += sprintf(&str[cnt],"%02X ",log->log_raw[i]);
1234       if (i && (((i + 1) % 16) == 0))
1235          cnt += sprintf(&str[cnt],"\n");
1236    }
1237
1238    // zero terminate string
1239    str[cnt] = 0;
1240 }
1241
1242 //--------------------------------------------------------------------------
1243 // Take one byte BCD value and return binary value
1244 //
1245 uchar BCDToBin(uchar bcd)
1246 {
1247    return (((bcd & 0xF0) >> 4) * 10) + (bcd & 0x0F);
1248 }
1249
1250
1251 //--------------------------------------------------------------------------
1252 // Take a 4 byte long string and convert it into a timedata structure.
1253 //
1254 static int dm[] = { 0,0,31,59,90,120,151,181,212,243,273,304,334,365 };
1255
1256 void SecondsToDate(timedate *td, ulong x)
1257 {
1258    short tmp,i,j;
1259    ulong y;
1260    
1261    // check to make sure date is not over 2070 (sanity check)
1262    if (x > 0xBBF81E00L)
1263       x = 0;
1264    
1265    y = x/60;  td->second = (ushort)(x-60*y);
1266    x = y/60;  td->minute = (ushort)(y-60*x);
1267    y = x/24;  td->hour   = (ushort)(x-24*y);
1268    x = 4*(y+731);  td->year = (ushort)(x/1461);
1269    i = (int)((x-1461*(ulong)(td->year))/4);  td->month = 13;
1270    
1271    do
1272    {
1273       td->month -= 1;
1274       tmp = (td->month > 2) && ((td->year & 3)==0) ? 1 : 0;
1275       j = dm[td->month]+tmp;
1276    
1277    } while (i < j);
1278    
1279    td->day = i-j+1;
1280    
1281    // slight adjustment to algorithm 
1282    if (td->day == 0) 
1283       td->day = 1;
1284    
1285    td->year = (td->year < 32)  ? td->year + 68 + 1900: td->year - 32 + 2000;
1286 }
1287
1288 //--------------------------------------------------------------------------
1289 // DateToSeconds takes a time/date structure and converts it into the 
1290 // number of seconds since 1970
1291 //
1292 ulong DateToSeconds(timedate *td)
1293 {
1294    ulong Sv,Bv,Xv;
1295
1296    // convert the date/time values into the 5 byte format used in the touch 
1297    if (td->year >= 2000) 
1298       Sv = td->year + 32 - 2000;
1299    else 
1300       Sv = td->year - 68 - 1900;
1301
1302    if ((td->month > 2) && ( (Sv & 3) == 0))
1303      Bv = 1;
1304    else
1305      Bv = 0;
1306
1307    Xv = 365 * (Sv-2) + (Sv-1)/4 + dm[td->month] + td->day + Bv - 1;
1308
1309    Xv = 86400 * Xv + (ulong)(td->second) + 60*((ulong)(td->minute) + 60*(ulong)(td->hour));
1310
1311    return Xv;
1312 }
1313
1314 //--------------------------------------------------------------------------
1315 // Convert from DS1921 termature format to a float
1316 // 
1317 //
1318 float TempToFloat(uchar tmp, int ConvertToF)
1319 {
1320    float tfloat;
1321
1322    tfloat = (float)((tmp / 2.0) - 40.0);
1323
1324    if (ConvertToF)
1325       return (float)(tfloat * 9.0 / 5.0 + 32.0);
1326    else
1327       return tfloat;   
1328 }
1329
1330 //--------------------------------------------------------------------------
1331 // Convert from Celsius to Fahrenheit
1332 //
1333 float CToF(float CVal)
1334 {
1335    return (float)(CVal * 9.0 / 5.0 + 32.0);
1336 }