Dual CANbus monitor and instrumentation cluster. Presently tuned for the Nissan Leaf EV.

Dependencies:   SPI_TFTx2_ILI9341 TFT_fonts TOUCH_TFTx2_ILI9341 mbed

Fork of CANary_corrupt by Tick Tock

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 // main.cpp
00002 //
00003 //To Do:
00004 // * Add 50% charge option
00005 // * Add linear efficiency graph with 10 minute values
00006 // * Add in-device config editor
00007 // * Change pack volt color when CVLI fails
00008 // * Add tire pressure cal (40psi for me = FR 38, RR 38.2, FL 37.8, RL 38 - maybe 2psi error on my tire gauge?)
00009 // * Be more efficient with write buffer (use msgLen instead of always storing 8 bytes)
00010 // * fix bug where charging while on screws up efficiency computation
00011 // * find better kWh estimate than gids
00012 
00013 // rev208
00014 //
00015 
00016 // Include this before other header files
00017 #include "precompile.h"
00018 
00019 #include "mbed.h"
00020 #include "CAN.h"
00021 #include "ff.h"
00022 #include "PowerControl.h"
00023 #include "EthernetPowerControl.h"
00024 #include "utility.h"
00025 #include "displayModes.h"
00026 #include "TOUCH_TFTx2.h"
00027 
00028 char revStr[7] = "208";
00029 unsigned long maxTarget = 1000;
00030 FATFS USBdrive;
00031 LocalFileSystem local("local");
00032 unsigned char wait5secs = 5;
00033 // to write to USB Flash Drives, or equivalent (SD card in Reader/Writer)
00034 // class10 SDcard in Reader/Writer recommended
00035 FRESULT mfr = f_mount(0,&USBdrive);
00036 
00037 time_t seconds ;
00038 
00039 Ticker autoPoll;
00040 Ticker playback;
00041 Ticker msgReq;
00042 Ticker geiger;
00043 Timer timer;
00044 
00045 DigitalOut led1(LED1);
00046 DigitalOut led2(LED2);
00047 DigitalOut led3(LED3);
00048 DigitalOut led4(LED4);
00049 
00050 InterruptIn touchpad(p17);
00051 CAN can1(p9, p10);      // CAN1 (EV) uses pins 9 and 10 (rx, tx) and pin 8 (rs)
00052 DigitalOut can1SleepMode(p8);     // Use pin 8 to control the sleep mode of can2
00053 CAN can2(p30, p29);     // CAN2 (CAR) uses pins 30 and 29 (rx, tx) and pin 28 (rs)
00054 DigitalOut can2SleepMode(p28);     // Use pin 28 to control the sleep mode of can1
00055 AnalogIn mon12V(p15);
00056 #if USE_ILI9341 == 1
00057 TOUCH_TFTx2 tt(p16, p17, p19, p20, p11, p12, p13, p6, p7, p5, p14, "TFT"); // x+, x-, y+, y-, mosi, miso, sclk, cs0, cs1, reset, dc
00058 #else
00059 TOUCH_TFTx2 tt(p16, p17, p19, p20, p11, p12, p13, p6, p7, p5, "TFT"); // x+,x-,y+,y-,mosi, miso, sclk, cs0, cs1, reset
00060 #endif
00061 PwmOut dled(p23);
00062 PwmOut spkr(p21);
00063 
00064 bool debugMode = false;
00065 bool usbEn = false;
00066 bool logEn = false;
00067 bool logOpen = false; 
00068 bool yesBattLog = true; // gg - Batt Log
00069 unsigned char tNavRow = 3; // gg - 4x4 touch
00070 bool brakeMon = false;  // disable until desired value read from config
00071 bool regenMon = false;
00072 bool heaterMon = false;
00073 bool autoSync = false;  // auto clock sync on powerup
00074 bool syncDone = true;
00075 bool heaterOn = false;
00076 bool lHeaterOn = false;
00077 bool CCon = false;
00078 bool lCCon = false;
00079 bool checkFWupdate = true;
00080 
00081 FILE *hfile; // history file
00082 FILE *rfile;
00083 FILE *file;
00084 FIL efile; // external usb file
00085 FRESULT efr; // external file access flags
00086 FILINFO fno;
00087 eDIR dir;
00088 char *fn;   /* This function assumes non-Unicode configuration */ 
00089 FRESULT res;
00090 unsigned int bytesRW;
00091 char logFileName[35] = "";
00092 char writeBuffer[maxBufLen][13] __attribute__ ((section("AHBSRAM1"))); // buffer for USB write
00093 char indexLastMsg[0x800]={0}; // index table for last message
00094 CANMessage lastMsg[100]; // table to store last message of eachtype
00095 
00096 unsigned char battData[BatDataBufMax]={0}; // 7 * 0x3D = BatDataBufMax
00097 
00098 unsigned char msgChanged[100]; // inidcates which bytes changed
00099 char c;
00100 volatile int writePointer = 0;
00101 int readPointer=0;
00102 volatile unsigned short secsNoCarCanMsg = canTimeout;
00103 volatile unsigned short secsNoEvCanMsg = canTimeout;
00104 volatile unsigned short secsNoTouch = 0;
00105 volatile unsigned short secsTouch = 0;
00106 volatile bool carCanIdle,evCanIdle,userIdle;
00107 bool touched=false; //flag to read touchscreen
00108 bool longTouch=false; //flag for long touch
00109 bool extraLongTouch=false; //flag for long touch
00110 unsigned char whichTouched = 0;
00111 char counter = 0;
00112 unsigned char dMode[2] = {mainScreen,brakeScreen}; //display mode
00113 unsigned char sMode = 0; // setup mode
00114 unsigned char lastDMode[2] = {0,0}; //last screen mode
00115 unsigned char dtMode = 0;
00116 char displayLog[20][40];
00117 unsigned char displayLoc = 0;
00118 unsigned int fwCount=1;
00119 unsigned char indexOffset = 1;
00120 bool showCP = false;
00121 bool logCP = false; //Turbo3
00122 bool logOnce = false;
00123 bool repeatPoll = true;
00124 bool headlights = false;
00125 bool miles_kmbar = true;
00126 bool tick = false;
00127 bool ZeroSecTick = false;
00128 float ledHi = 0.5; // Bright LED value (until config file read)
00129 float ledLo = 0.5; // Dim LED value (until config file read)
00130 unsigned short pollInt = 300; // polling interval=5 minutes (until config file read)
00131 bool accOn = false; // Accessories on
00132 bool laccOn = false;
00133 float scale12V = 16.2; // R1:R2 ratio
00134 float kWperGid = 0.080;
00135 int daysLog = 999; // How many days of log files to save
00136 unsigned short startGids = 0; // Gids at start of trip
00137 unsigned short dailyGids = 0; // Gids per day
00138 bool getGids = false;
00139 signed long mWs_x4 = 0;
00140 unsigned short numWsamples = 0;
00141 unsigned short numSsamples = 0;
00142 unsigned long keypad = 0;
00143 unsigned char uidx = 99;
00144 float accV = 0;
00145 float accV2 = 0;
00146 float CCkW = 0;
00147 bool playbackEn = false;
00148 bool playbackOpen = false;
00149 //float playbackInt = 0.05; //read messages every 50 ms
00150 float playbackInt = 0.005; //read messages every 5 ms
00151 bool step = false;
00152 char header[5];
00153 char data[8];
00154 signed long motorRPM;
00155 unsigned char skin = ttSkin ;
00156 unsigned char dtePeriod = 14; //ten minute averaging interval
00157 float CCkWh_trip[4]={0};
00158 float kWh_trip[4]={0};
00159 float miles_trip[4]={0};
00160 float curEff = 0;
00161 float maxTripEff = 0;
00162 float minTripEff = 5;
00163 float maxTripMiles = 0;
00164 float maxTripkWh = 1;
00165 float maxTripCCkWh = 0;
00166 float minTripMiles = 5;
00167 float minTripkWh = 1;
00168 float minTripCCkWh = 0;
00169 float mph[39]={0};
00170 float kW[39]={0};
00171 float mpkWh[39]={0};
00172 float mpkWh_noCC=0;
00173 float unloadedV_x2,Resr,curRmax,curRmin,redRmax,redRmin,incRmax,incRmin;
00174 signed short Imax, Imin;
00175 // Logarithmic division scale (roughly - snapped to common units of time)
00176 float timeConstant[39] = {1, 1.58, 2.51, 3.98, 6.31, 10, 15.8, 25.1, 39.8, 60, // 1 minute
00177                      60*1.58, 60*2.51, 60*3.98, 60*6.31, 60*10, 60*15.8, 60*25.1, 60*39.8, 60*60, // 1 hour
00178                      60*60*1.58, 60*60*2.51, 60*60*3.98, 60*60*6.31, 60*60*10, 60*60*15.8, 60*60*24, // 1 day
00179                      60*60*24*1.58, 60*60*24*2.51, 60*60*24*3.98, 60*60*24*6.31, 60*60*24*10, 60*60*24*15.8, 60*60*24*30, // 1 month
00180                      60*60*24*39.8, 60*60*24*63.1, 60*60*24*100, 60*60*24*158, 60*60*24*251, 60*60*24*365}; // 1 year
00181 bool tock = false;
00182 unsigned short pointerSep;
00183 unsigned char reqMsgCnt = 99;
00184 unsigned long Ah_x10000 = 0;
00185 unsigned long SOC_x10000 = 0;
00186 unsigned short SOH2_x100 = 0;
00187 float maxTemp = 0;
00188 bool metric = false;
00189 bool shunt[96]={0};
00190 bool charging=false;
00191 bool showHealth=false;
00192 unsigned char saveDmode[2] = {99, 99};
00193 bool moving=false;
00194 unsigned short chirpInt;
00195 unsigned short uMsgId[8] = {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}; // messages to display on debug screen msgId:byte
00196 char uCmdBus = 1; // 1 = EVCan ; 2 = CarCan
00197 short uCmdId = 0x79b;
00198 char uCmdLen = 8;
00199 char uCmdData[8] = {0x02, 0x21, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff};
00200 int daysUptoMonth[12] = {0,31,59,90,120,151,181,212,243,273,304,334};
00201 unsigned short modelYear = 2011;
00202 bool idir, lidir;
00203 bool enableSound=true;
00204 bool clearTest=true;
00205 unsigned long tbScalar = 72464;
00206 unsigned long rbScalar = 8696;
00207 unsigned long fbScalar = 132;
00208 int effCheckTime = 3;
00209 bool ignoreDayData = true;
00210 unsigned short cgids,lgids=0;
00211 unsigned short whpg[300]={0};
00212 float wh[300];
00213 float maxWhpg,minWh,whOff;
00214 
00215 int main() {
00216     char sTemp[40];
00217     unsigned long secs;
00218     unsigned short i,j;
00219     unsigned char display=0,lwt=0;
00220     point lastTouch;
00221     float average;
00222     tt.set_orientation(1);
00223     tt.background(Black);
00224     tt.set_display(2);       // select both displays
00225     tt.cls();
00226     tt.foreground(White);
00227     tt.set_font((unsigned char*) Arial12x12_prop);  // select the font
00228     tt.locate(0,0);
00229     tt.claim(stdout);        // send stdout to the TFT display
00230     touchpad.rise(&touch_ISR);
00231     tt.wfi();               // enable interrupt on touch
00232     dled.period(0.001);
00233     dled = ledHi; // turn on display LED 80%
00234     spkr = 0;
00235     Resr = 0.075; // initial guess of Resr
00236     timer.start() ;
00237     RTC_Init(); // start the RTC Interrupts that sync the timer
00238     struct tm t,lt; // pointer to a static tm structure
00239     NVIC_SetPriority(CAN_IRQn, 2); //set can priority just below RTC
00240     NVIC_SetPriority(TIMER3_IRQn, 3); //set ticker priority just below can
00241 
00242     seconds = time(NULL);
00243     t = *localtime(&seconds);
00244     lt = t; // initialize
00245     // is it a date before 2012 ?
00246     if ((t.tm_year + 1900) < 2012 ) {
00247         // before 2013 so update year to make date entry easier
00248         t.tm_year = 2013 - 1900;
00249         // set the RTC
00250         set_time(mktime(&t));
00251         seconds = time(NULL);
00252     }
00253     t = *localtime(&seconds) ;
00254     strftime(sTemp, 32, "%a %m/%d/%Y %X\n", &t);
00255     printMsg(sTemp); // record RTC
00256     
00257     // revision
00258     sprintf(sTemp,"CANary firmware rev%s\n", revStr); // gg - for Logging the revision
00259     printMsg(sTemp); // revision
00260 
00261     for(i=0;i<300;i++){ // initialize wh lookup
00262         wh[i]=i*kWperGid*1000;
00263     }
00264 
00265     //read efficiency history data
00266     hfile = fopen("/local/ehist.cny", "r");
00267     if (hfile!=NULL){ // found a efficiency history file
00268         for(i=0;i<39;i++){
00269             if(!feof(hfile)){
00270                 fscanf(hfile,"%f %f\r\n",&mph[i],&kW[i]);
00271                 mpkWh[i]=mph[i]/kW[i];
00272                 if(i==dtePeriod) mpkWh_noCC=mpkWh[i];
00273             }
00274         }
00275         if(!feof(hfile)){
00276             fscanf(hfile,"%f %f\r\n",&maxTripEff,&minTripEff);
00277         }
00278         if(!feof(hfile)){
00279             fscanf(hfile,"%f\r\n",&Resr);
00280         }
00281         if(!feof(hfile)){
00282             fscanf(hfile,"%f %f\r\n",&maxTripMiles,&minTripMiles);
00283         }
00284         if(!feof(hfile)){
00285             fscanf(hfile,"%f %f\r\n",&maxTripkWh,&minTripkWh);
00286         }
00287         if(!feof(hfile)){
00288             fscanf(hfile,"%f %f\r\n",&maxTripCCkWh,&minTripCCkWh);
00289         }
00290         for(i=0;i<300;i++){
00291             if(feof(hfile)) break;
00292             fscanf(hfile,"%f\r\n",&wh[i]);
00293         }
00294         fclose(hfile);
00295         printMsg("History Loaded.\n"); // History loaded
00296     } else { // create initial file
00297         printMsg("History not found.  Created.\n"); // history not found, created
00298         for(i=0;i<39;i++){
00299             // Pre-load with 4 mpkWh @ 40 mph
00300             mph[i]=40*timeConstant[i];
00301             kW[i]=10*timeConstant[i];
00302             mpkWh[i]=4;
00303         }
00304     }
00305 
00306     // Read config file
00307     readConfig();
00308     if (brakeMon){
00309         geiger.attach(&chirp,0.02);
00310     }
00311     if (repeatPoll) { // enable autopolling if enabled
00312         autoPoll.attach(&autoPollISR,pollInt);
00313     }
00314     
00315     // Start monitors
00316     can1.monitor(true); // set to snoop mode
00317     can2.monitor(true); // set to snoop mode
00318     can1.frequency(500000);
00319     can2.frequency(500000);
00320     can1SleepMode = VP230Sleep;         // Turn on Monitor_only Mode
00321     can2SleepMode = VP230Sleep;         // Turn on Monitor_only Mode
00322     can1.attach(&recieve1);
00323     can2.attach(&recieve2);
00324 
00325     touched=false;
00326     longTouch=false;
00327     extraLongTouch=false;
00328     secsNoTouch=2;
00329     secsTouch=0;
00330     
00331     while (true) {
00332         if (!logOpen) { // Open new file if one is not already open
00333             if(logEn&&usbEn){ //logging enabled and USB device detected
00334                 strftime(logFileName, 32, "%m%d%H%M.alc", &t); //mmddhhmm.alc
00335                 efr = f_open(&efile,logFileName,FA_WRITE|FA_OPEN_ALWAYS);
00336                 seconds = time(NULL);
00337                 t = *localtime(&seconds) ;
00338                 lastDMode[0]=99;//force refresh
00339                 lastDMode[1]=99;//force refresh
00340                 if(efr != FR_OK){
00341                     sprintf(sTemp,"\nERR:%d Unable to open %s\n\n\n\n",efr,logFileName);
00342                     printMsg(sTemp); // cannot open alc file
00343                     logEn=false;
00344                     usbEn=detectUSB();
00345                     beep(1000,0.25);
00346                     wait_ms(500);
00347                     beep(1000,0.25);
00348                 } else {
00349                     logOpen = true;
00350                     readPointer=writePointer;
00351                     sprintf(sTemp,"Starting Can Log %s\n",logFileName);
00352                     printMsg(sTemp); // starting alc log file 
00353                     
00354                     logTS(); // Date Time at start
00355                     logEvent("Starting"); // Log startup msg for testing
00356                     sprintf(sTemp,"Cr%s",revStr);
00357                     logEvent(sTemp); // gg - log firmware version   
00358                     beep(2000,0.25);
00359                     file = fopen("/local/loglog.txt", "a"); // save filename log
00360                     fprintf(file,"%s\r\n",logFileName);
00361                     fclose(file);
00362                 }
00363             }//logging enabled and USB detected
00364         } else { // if (logOpen)
00365             pointerSep=(writePointer+maxBufLen-readPointer)%maxBufLen;
00366             if (pointerSep>(maxBufLen/16)||carCanIdle||!logEn) {
00367                 // Dump buffer if > 1/16 full or canbus has stopped
00368                 if (efr != FR_OK) {
00369                     logOpen = false;
00370                     printMsg("Failed to append log file.\n"); // failed to append 
00371                     beep(3000,0.25);
00372                     beep(1500,0.25);
00373                     beep(750,0.25);
00374                     beep(375,0.25);
00375                     logEn=false;
00376                     usbEn=detectUSB();
00377                 } else {
00378                     while (readPointer != writePointer) {
00379                         efr=f_write(&efile,&writeBuffer[readPointer][0],13,&bytesRW);
00380                         if(++readPointer >= maxBufLen){
00381                             readPointer=0;
00382                             led4 = !led4;
00383                         }
00384                     }
00385                 }
00386             } // if > 1/16 full, canbus has stopped, or logging stopped
00387             if (!logEn) {
00388                 sprintf(sTemp,"Stopping Can Log %s\n",logFileName);
00389                 printMsg(sTemp); // stopping alc log file 
00390                 f_close(&efile);
00391                 logOpen=false;
00392                 pointerSep=0;
00393                 led4=false;
00394             }
00395         } // if logOpen
00396         if (carCanIdle && (evCanIdle || !logOpen) && userIdle && !playbackEn) { // canbus idle --> sleep to save power
00397             if (repeatPoll) { // stop autopolling if enabled
00398                 autoPoll.detach();
00399             }
00400             if (logOpen){ //close file to dump buffer
00401                 f_close(&efile);
00402             } else { //detach EVcan so only carcan will trigger wake
00403                 can1.attach(NULL);
00404             }// if (logOpen)
00405 
00406             // Take advantage of the idle time to clear some room on USB drive
00407             if(logEn && usbEn){
00408                 seconds = time(NULL);
00409                 t = *localtime(&seconds) ;
00410                 int fmon;
00411                 int fday;
00412                 int ftime;
00413                 int fdays;
00414                 res = f_opendir(&dir, ""); //Get USB contents                 
00415                 if(res == FR_OK) {
00416                     for (i=1;i<200;i++) {
00417                         res = f_readdir(&dir, &fno);                   /* Read a directory item */
00418                         if (res != FR_OK || fno.fname[0] == 0) break;  /* Break on error or end of dir */
00419                         //if (fno.fname[0] == '.') continue;             /* Ignore dot entry */
00420                         fn = fno.fname;
00421                         if (fno.fattrib & AM_DIR) {                    /* It is a directory */
00422                             continue; // Do nothing
00423                         } else {                                       /* It is a file. */
00424                             if(sscanf(fn,"%2d%2d%4d.alc",&fmon,&fday,&ftime)==3){
00425                                 fdays = fday + daysUptoMonth[fmon-1]; //Leap years save one extra day at the end of February
00426                                 if (fdays>(t.tm_mday+daysUptoMonth[t.tm_mon])){//file cannot be from the future so must be very old
00427                                     fdays = fdays - 365;
00428                                 }
00429                                 if ((fdays+daysLog)<(t.tm_mday+daysUptoMonth[t.tm_mon])){ // Delete all files more than daysLog old
00430                                     sprintf(sTemp,"%02d%02d%04d.alc",fmon,fday,ftime);
00431                                     f_unlink(sTemp);
00432                                     sprintf(sTemp,"Deleted logfile %02d%02d%04d.alc\n",fmon,fday,ftime);
00433                                     printMsg(sTemp);
00434                                 }
00435                             }
00436                         }
00437                     }
00438                     //f_closedir(&dir);
00439                 }                 
00440                 wait(2); // wait a few seconds to ensure file access
00441             } //if logen
00442             seconds = time(NULL);
00443             t = *localtime(&seconds) ;
00444             strftime(sTemp, 40, "Sleeping: %a %m/%d/%Y %X\n", &t);
00445             printMsg(sTemp); // sleeping date time
00446             updateDisplay(0); //Added for turbo3 who has a display override and wants to see the sleep message before going to sleep
00447             updateDisplay(1);
00448             //LPC_RTC->CIIR=0x00; // block RTC interrupts
00449             led1=0;
00450             led2=0;
00451             led3=0;
00452             led4=0;
00453             dled=0; // turn off display
00454             secs = time(NULL); // seconds past 12:00:00 AM 1 Jan 1900
00455             while (secsNoCarCanMsg>canTimeout && (secsNoEvCanMsg>canTimeout || !logOpen) && !touched) {
00456                 //DeepPowerDown();
00457                 tt.wfi(); //enable touch interrupt
00458                 //__wfi(); // freeze CPU and wait for interrupt (from canbus or touch)
00459                 Sleep();
00460             }
00461             if (!logOpen){ // Re-attach EVcan
00462                 can1.attach(&recieve1);
00463             }
00464             secsNoTouch=2;
00465             secsTouch=0;
00466             carCanIdle=secsNoCarCanMsg>canTimeout;
00467             evCanIdle=secsNoEvCanMsg>canTimeout;
00468             dled=ledHi; // turn on display LED
00469             seconds = time(NULL);
00470             t = *localtime(&seconds) ;
00471             strftime(sTemp, 40, "Waking: %a %m/%d/%Y %X\n", &t);
00472             printMsg(sTemp); // wakeup date time
00473             if (time(NULL)>(secs+1800)) {
00474                 if (logOpen){
00475                     f_close(&efile);
00476                     logOpen = false; // Start new file if asleep for more than 30 minutes
00477                 } // if (logOpen)
00478                 if (secsNoTouch>100) secsNoTouch = 100; // also mostly reset user Idle counter
00479             } else if (logOpen){ // insert timestamp on each wake if logging enabled (disabled for now)
00480                 efr = f_open(&efile,logFileName,FA_WRITE|FA_OPEN_ALWAYS);
00481                 f_lseek(&efile,0xffffffff); // goto end of file (append existing)
00482                 logEvent("WakingUp"); // gg - use messeges
00483                 logTS(); // Date-Time at wakeup
00484             }
00485             if (repeatPoll) { // re-enable autopolling if enabled
00486                 autoPoll.attach(&autoPollISR,pollInt);
00487             }
00488             wait5secs=5; // Refresh screen after 5 seconds
00489         } // if idle
00490         
00491         if(touched){ // call touchscreen procedure if touch interrupt detected
00492             lastTouch = tt.get_touch();       
00493             lastTouch = tt.to_pixel(lastTouch);          // convert to pixel pos
00494             if((lastTouch.x!=639)&&(lastTouch.x!=319)&&(lastTouch.y!=239)){ // filter phantom touches
00495                 if (userIdle) {
00496                     secsNoTouch=2; // Ignore first touch if user idle
00497                     userIdle=false;
00498                 } else {
00499                     secsNoTouch=0;
00500                 }
00501                 if (lastTouch.x>320){
00502                     whichTouched=1;
00503                 } else {
00504                     whichTouched=0;
00505                 }
00506                 if (whichTouched!=lwt){
00507                     lastDMode[lwt]=99; // Repaint lastTouched
00508                     lwt=whichTouched;
00509                     if(sMode==2){ // Exit keypad mode if other screen touched
00510                         sMode=0; // end keypad mode
00511                         if(saveDmode[0]<=maxScreens)
00512                             dMode[0]=saveDmode[0];
00513                         if(saveDmode[1]<=maxScreens)
00514                             dMode[1]=saveDmode[1];
00515                         lastDMode[0]=99;
00516                         lastDMode[1]=99;
00517                         uidx=99;
00518                     }
00519                 }
00520                 if (sMode==0) sMode = 1; //Go to select mode1 unless already in select mode2
00521             }
00522             //sprintf(sTemp,"%d,%d ",lastTouch.x,lastTouch.y);
00523             //printMsg(sTemp); // touch x,y - for debug
00524             touched = false; // clear interrupt flag
00525         }
00526         //---------------
00527         // gg - 4x4 touch
00528         //unsigned char tScrn = 0 ; // screen 0
00529         unsigned char tCol ;                        
00530         unsigned char tRow ;
00531                         
00532         if (!userIdle) {
00533             if(longTouch&&(sMode<2)){ //long touch
00534                 if (dMode[whichTouched]==watchScreen) {
00535                     whichTouched = whichTouched ^ 1; //long press opens keypad on *other* screen
00536                     lwt=whichTouched;
00537                     saveDmode[0]=dMode[0];
00538                     saveDmode[1]=dMode[1];
00539                     dMode[whichTouched]=offScreen;
00540                     sMode=2; //start numPad entry mode
00541                     keypad=0;
00542                     if((lastTouch.y>64)&&(lastTouch.y<184)){
00543                         uidx=(lastTouch.y-64)/30;
00544                         uidx*=2;
00545                         if((lastTouch.x%320)>160){
00546                             uidx+=1;
00547                         }
00548                     }else{
00549                         uidx=99;
00550                     }
00551                     secsTouch=0;
00552                 } else if (dMode[whichTouched]==cmdScreen) {
00553                     whichTouched = whichTouched ^ 1; //long press opens keypad on *other* screen
00554                     lwt=whichTouched;
00555                     saveDmode[0]=dMode[0];
00556                     saveDmode[1]=dMode[1];
00557                     dMode[whichTouched]=offScreen;
00558                     sMode=2; //start numPad entry mode
00559                     keypad=0;
00560                     if((lastTouch.y>34)&&(lastTouch.y<184)){
00561                         uidx=(lastTouch.y-4)/30;
00562                     }else{
00563                         uidx=99;
00564                     }
00565                     secsTouch=0;
00566                 }
00567             } else if (secsNoTouch<2) {// Recently touched
00568                 secsNoTouch +=2; // increment to prevent double touch
00569                 if (sMode==0) sMode = 1; //Go to select mode1 unless already in select mode2
00570                 //sprintf(sTemp,"button %d %d,%d %d\n",i,buttonX(lastTouch.x,3),buttonY(lastTouch.y,3),lastTouch.x);
00571                 //printMsg(sTemp); // button parms - for debug
00572                 switch (sMode) {
00573                     case 0: // no select
00574                         break;
00575                     case 1: // select screen                        
00576                         //--------------
00577                         // gg - 4x4 touch
00578                         tCol = buttonX(lastTouch.x,4) ;
00579                         if( tCol >= 4 ){ tCol -= 4; } // touch is on screen 1
00580                         
00581                         tRow = buttonY(lastTouch.y,4) ;
00582                         
00583                         highlightButton( tCol,tRow, whichTouched, 4,4) ; // gg - highlight
00584                         
00585                         if( tRow == tNavRow ) tRow = 7 ; // gg                   
00586                         switch ( (tCol*10) + tRow ) {
00587                             //---------------------------------
00588                             case 00: // top row, left button on screen 0 or 1
00589                                 if (dMode[whichTouched]==monitorScreen||dMode[whichTouched]==changedScreen) {
00590                                     indexOffset=indexOffset>4?indexOffset-4:1;
00591                                 } else if (dMode[whichTouched] == indexScreen) { // gg - index
00592                                     dMode[whichTouched] = mainScreen ; // GoTo Main Screen
00593                                     sMode=0;
00594                                 } else if (dMode[whichTouched]==config2Screen) {
00595                                     wait_ms(500);
00596                                     tt.background(Black);
00597                                     tt.calibrate();
00598                                 } else if (dMode[whichTouched]==playbackScreen) { // slower
00599                                         playbackInt *=2;
00600                                     if(playbackEn){
00601                                         playback.detach();
00602                                         playback.attach(&playbackISR,playbackInt);
00603                                     }
00604                                 } else {
00605                                     lastDMode[whichTouched]=99;//repaint to clear highlight
00606                                 }
00607                                 break;
00608                             //-----------------------------------------------
00609                             case 10: // 1,0 (col left of center,top row) on screen 0 or 1
00610                                 if (dMode[whichTouched]==changedScreen) {
00611                                     for(j=0;j<100;j++) msgChanged[j]=0; // clear changed data
00612                                     lastDMode[whichTouched]=99;//force refresh
00613                                     sMode=0;
00614                                 } else if (dMode[whichTouched] == indexScreen) { // gg - index
00615                                     sMode=0;
00616                                     dMode[whichTouched] = brakeScreen ; // GoTo Brake Screen
00617                                 } else if (dMode[whichTouched]==cpScreen) {
00618                                     reqMsgCnt=0;
00619                                     msgReq.attach(&sendReq,0.015);
00620                                 } else if (dMode[whichTouched]==cpHistScreen) { // gg - hist
00621                                     reqMsgCnt=0;
00622                                     msgReq.attach(&sendReq,0.015);
00623                                 } else if (dMode[whichTouched]==cpBarScreen) { // gg - cpbars
00624                                     reqMsgCnt=0;
00625                                     msgReq.attach(&sendReq,0.015);
00626                                 } else if (dMode[whichTouched]==configScreen) {
00627                                     mbed_reset();
00628                                 } else if (dMode[whichTouched]==config2Screen) { // reset DTE Max/Min
00629                                     maxTripEff = 0;
00630                                     minTripEff = 5;
00631                                     beep(2000,0.25);
00632                                     for(i=0;i<300;i++){ // initialize wh lookup
00633                                         wh[i]=i*kWperGid*1000;
00634                                     }
00635                                 } else if (dMode[whichTouched]==playbackScreen) { // pause/unpause
00636                                     playbackEn=!playbackEn;
00637                                     if(playbackEn){
00638                                         playback.attach(&playbackISR,playbackInt);
00639                                     } else {
00640                                         playback.detach();
00641                                     }
00642                                 } else {
00643                                     lastDMode[whichTouched]=99;//repaint to clear highlight
00644                                 }
00645 
00646                                 break;
00647                             //--------------------------------------
00648                             case 20: // col 2 and row 0 on either screen 0 or 1
00649                                 if (dMode[whichTouched]==monitorScreen||dMode[whichTouched]==changedScreen) {
00650                                     indexOffset=indexOffset<77?indexOffset+4:80;
00651                                 } else if (dMode[whichTouched] == indexScreen) { // gg - index
00652                                     dMode[whichTouched] = effScreen ; // GoTo EFF Screen
00653                                     sMode=0;
00654                                 } else if (dMode[whichTouched]==configScreen) {
00655                                     dMode[whichTouched]=mainScreen;
00656                                     //write efficiency history data
00657                                     hfile = fopen("/local/ehist.cny", "w");
00658                                     if (hfile!=NULL){ // found a efficiency history file
00659                                         for(i=0;i<39;i++){
00660                                             fprintf(hfile,"%f %f\r\n",mph[i],kW[i]);
00661                                         }
00662                                         fprintf(hfile,"%f %f\r\n",maxTripEff,minTripEff); // Save max and min
00663                                         fprintf(hfile,"%f \r\n",Resr); // Save series resistance
00664                                         fprintf(hfile,"%f %f\r\n",maxTripMiles,minTripMiles); // Save max and min
00665                                         fprintf(hfile,"%f %f\r\n",maxTripkWh,minTripkWh); // Save max and min
00666                                         fprintf(hfile,"%f %f\r\n",maxTripCCkWh,minTripCCkWh); // Save max and min
00667                                         for(i=0;i<300;i++){
00668                                             fprintf(hfile,"%f\r\n",wh[i]);
00669                                         }
00670                                         fclose(hfile);
00671                                     }
00672                                     beep(2000,0.25);
00673                                     saveConfig();
00674                                     beep(2000,0.25);
00675                                 } else if (dMode[whichTouched]==config2Screen) {
00676                                     showHealth = !showHealth;
00677                                 } else if (dMode[whichTouched]==playbackScreen) { // faster
00678                                     if(playbackInt>.002){
00679                                         playbackInt/=2;
00680                                         if(playbackEn){
00681                                             playback.detach();
00682                                             playback.attach(&playbackISR,playbackInt);
00683                                         }
00684                                     }
00685                                 } else {
00686                                     lastDMode[whichTouched]=99;//repaint to clear highlight
00687                                 }
00688 
00689                                 break;
00690                                 
00691                             case 30: // right-most on top row
00692                                 
00693                                 if (dMode[whichTouched]==config2Screen) {
00694                                     // step through skins
00695                                     if( skin < maxSkin ) skin += 1 ;
00696                                     else skin = 0 ;
00697                                     
00698                                     // repaint both screens, I think
00699                                     lastDMode[whichTouched]=99;//repaint to clear highlight
00700                                     // and re-paint the other screen too, to see new skin there
00701                                     lastDMode[whichTouched ^ 1]=99; // repaint other screen (^ = XOR)
00702                                 } else if (dMode[whichTouched] == indexScreen) { // gg - index
00703                                     dMode[whichTouched] = healthScreen ; // Goto health screen
00704                                     sMode=0;
00705                                 } else if (dMode[whichTouched] == watchScreen) { 
00706                                     clearTest=true;
00707                                 } else if ((dMode[whichTouched] == cmdScreen)&&debugMode) {
00708                                     if (uCmdBus==1) { // EVcan
00709                                         can1.monitor(false); // set to active mode
00710                                         can1.write(CANMessage(uCmdId, uCmdData, uCmdLen));
00711                                         wait_ms(50);
00712                                         if (reqMsgCnt==99)
00713                                             can1.monitor(true); // restore to snoop unless other message outstanding
00714                                     } else if (uCmdBus==2){
00715                                         can2.monitor(false); // set to active mode
00716                                         can2.write(CANMessage(uCmdId, uCmdData, uCmdLen));
00717                                         wait_ms(50);
00718                                         if (reqMsgCnt==99)
00719                                             can2.monitor(true); // restore to snoop unless other message outstanding
00720                                     }
00721                                 } else { // top-right corner always mute/unmute unless used by specific screen
00722                                     enableSound = !enableSound;
00723                                     if(!enableSound) spkr=0;
00724                                     lastDMode[whichTouched]=99;//repaint to clear highlight
00725                                 }
00726 
00727                                 break;
00728                             //----------------------------------
00729                             //----------------------------------
00730                             case 01: // left col middle row
00731                                 if (dMode[whichTouched]==configScreen) {
00732                                     logEn = !logEn;
00733                                 } else if (dMode[whichTouched] == indexScreen) { // gg - index
00734                                     dMode[whichTouched] = cpScreen ; // GoTo CP Data Screen                                
00735                                     sMode=0;
00736                                 } else if (dMode[whichTouched]==dateScreen){
00737                                     dtMode=(dtMode<5)?dtMode+1:0;
00738                                     lastDMode[whichTouched]=99;
00739                                 } else {
00740                                     lastDMode[whichTouched]=99;//repaint to clear highlight
00741                                 }
00742 
00743                                 break;
00744                             //------------------------------
00745                             case 11:
00746                                 if (dMode[whichTouched]==configScreen){
00747                                     repeatPoll = !repeatPoll;
00748                                     if (repeatPoll) {
00749                                         autoPoll.attach(&autoPollISR,pollInt);
00750                                     } else {
00751                                         autoPoll.detach();
00752                                     }
00753                                 } else if (dMode[whichTouched] == indexScreen) { // gg - index
00754                                     dMode[whichTouched] = cpHistScreen ; // GoTo CP Hist Screen
00755                                     sMode=0;
00756                                 } else if (dMode[whichTouched]==playbackScreen) {
00757                                     // Start/stop playback
00758                                     if(!playbackOpen){
00759                                         if(!carCanIdle){
00760                                             printMsg("Cannot playback while connected to canbus\n");
00761                                         }else if(!logOpen){
00762                                             efr = f_open(&efile,"playback.alc",FA_READ|FA_OPEN_EXISTING);
00763                                             lastDMode[whichTouched]=99;//force refresh
00764                                             if(efr != FR_OK){
00765                                                 printMsg("Unable to open /usb/playback.alc\n"); // no playback.alc
00766                                                 beep(1000,0.25);
00767                                                 usbEn=detectUSB();
00768                                             } else {
00769                                                 playbackOpen = true;
00770                                                 playbackEn=true;
00771                                                 playback.attach(&playbackISR,playbackInt);
00772                                                 printMsg("Starting playback\n"); // start playback
00773                                                 beep(2000,0.25);
00774                                                 can1.attach(NULL);// Stop recieving EVCAN data
00775                                                 can2.attach(NULL);// Stop recieving CARCAN data
00776                                             }
00777                                         } else {
00778                                             printMsg("Must stop logging first\n");
00779                                         }
00780                                     } else {
00781                                         playback.detach();
00782                                         f_close(&efile);
00783                                         playbackOpen=false;
00784                                         playbackEn=false;
00785                                         can1.attach(&recieve1);// Restore EVCAN data recieve
00786                                         can2.attach(&recieve2);// Restore EVCAN data recieve
00787                                         lastDMode[whichTouched]=99;
00788                                     }
00789                                 } else if (dMode[whichTouched]==dateScreen){
00790                                     upDate(dtMode,true);
00791                                     lastDMode[whichTouched]=99;
00792                                 } else {
00793                                     lastDMode[whichTouched]=99;//repaint to clear highlight
00794                                 }
00795 
00796                                 break;
00797                             //---------------------------------
00798                             case 21: // col 2 row 1
00799                                 if (dMode[whichTouched]==configScreen) { // gg - Batt Log Enable Button
00800                                     yesBattLog = !yesBattLog;
00801                                 } else if (dMode[whichTouched] == indexScreen) { // gg - index
00802                                     dMode[whichTouched] = cpBarScreen ; // GoTo CP Bars Screen  
00803                                     sMode=0;
00804                                 } else if (dMode[whichTouched]==dateScreen){
00805                                     upDate(dtMode,false);
00806                                     lastDMode[whichTouched]=99;
00807                                 } else {
00808                                     lastDMode[whichTouched]=99;//repaint to clear highlight
00809                                 }
00810 
00811                                 break;
00812                                 
00813                             case 31: // col 3 row 1
00814                                 if (dMode[whichTouched]==config2Screen) { // gg - Batt Log Enable Button
00815                                     debugMode = !debugMode;                                
00816                                 } else if (dMode[whichTouched] == indexScreen) { // gg - index
00817                                     dMode[whichTouched] = configScreen ; // GoTo Config Screen
00818                                 } else if (dMode[whichTouched]==tripScreen) { // Cancel day trip meter
00819                                     ignoreDayData=true;
00820                                     lastDMode[whichTouched]=99;//repaint to clear highlight
00821                                 } else if ((dMode[whichTouched]==dateScreen)&&accOn){
00822                                     syncDone=false; // initiate clock sync
00823                                     lastDMode[whichTouched]=99;
00824                                 } else if (dMode[whichTouched]==configScreen) {
00825                                     if(!headlights){
00826                                         ledHi += 0.025;
00827                                         if (ledHi>1.0) ledHi=1.0;
00828                                     } else {
00829                                         ledLo += 0.025;
00830                                         if (ledLo>1.0) ledLo=1.0;
00831                                     }
00832                                 } else {
00833                                     lastDMode[whichTouched]=99;//repaint to clear highlight
00834                                 }                            
00835                                 break;
00836                                 
00837                             //-----------------------------------
00838                             case 02: // left col, bottom row (not nav)
00839                                 if (dMode[whichTouched] == indexScreen) { // gg - index
00840                                     dMode[whichTouched] = playbackScreen ; // GoTo Playback Screen                                    
00841                                 } else if (dMode[whichTouched]==configScreen) {
00842                                     brakeMon = !brakeMon;
00843                                     if(brakeMon){
00844                                         geiger.attach(&chirp,0.02);
00845                                     }else{
00846                                         geiger.detach();
00847                                     }
00848                                 } else if (dMode[whichTouched]==config2Screen) {
00849                                     autoSync = !autoSync;                                
00850                                 } else {
00851                                     lastDMode[whichTouched]=99;//repaint to clear highlight
00852                                 }            
00853                                 break;
00854                                 
00855                              case 12: // left-middle col, bottom row (not nav)
00856                                 if (dMode[whichTouched]==configScreen) {
00857                                     regenMon = !regenMon;                                
00858                                 } else if (dMode[whichTouched]==config2Screen) { // gg - index
00859                                     dMode[whichTouched] = dateScreen ; // GoTo Set Date/Time Screen  
00860                                 } else {
00861                                     lastDMode[whichTouched]=99;//repaint to clear highlight
00862                                 } 
00863                                 break;
00864                              
00865                              case 22: // right-middle col, bottom row (not nav)
00866                                 if (dMode[whichTouched]==indexScreen) { // gg - index
00867                                     dMode[whichTouched] = logScreen ;    
00868                                     sMode=0;
00869                                 } else if (dMode[whichTouched]==configScreen) {
00870                                     heaterMon = !heaterMon;
00871                                 } else if (dMode[whichTouched]==config2Screen) {
00872                                     updateConfig();
00873                                     lastDMode[whichTouched]=99;//force refresh
00874                                     sMode=0;
00875                                 } else {
00876                                     lastDMode[whichTouched]=99;//repaint to clear highlight
00877                                 } 
00878                                 break;
00879 
00880                              case 32: // right col, bottom row (not nav)  
00881                                 if (dMode[whichTouched]==config2Screen) {
00882                                     logEn=false;
00883                                     updateFirmware();
00884                                 } else if (dMode[whichTouched]==tripScreen) {
00885                                     // Reset custom trip meter
00886                                     miles_trip[2]=0;
00887                                     kWh_trip[2]=0;
00888                                     CCkWh_trip[2]=0;
00889                                     sMode=0;
00890                                     lastDMode[whichTouched]=99;//repaint to clear highlight
00891                                 } else if (dMode[whichTouched]==indexScreen) {
00892                                     dMode[whichTouched] = tripScreen ;    
00893                                     sMode=0;
00894                                 } else if (dMode[whichTouched]==dateScreen){
00895                                     autoSync=!autoSync; // toggle autoSync mode
00896                                     lastDMode[whichTouched]=99;
00897                                 } else if (dMode[whichTouched]==configScreen) {
00898                                     if(!headlights){
00899                                         ledHi -= 0.1;
00900                                         if (ledHi<0) ledHi=0;
00901                                     } else {
00902                                         ledLo -= 0.1;
00903                                         if (ledLo<0) ledLo=0;
00904                                     }
00905                                 } else {                             
00906                                     lastDMode[whichTouched]=99;//repaint to clear highlight
00907                                 }
00908                                 break;
00909 
00910                             //-----------------------------------
00911                             //-----------------------------------
00912                             // Prev Navigation
00913                             case 07: // col 0 row tNavRow
00914                                 dMode[whichTouched]=dMode[whichTouched]>0?dMode[whichTouched]-1:maxScreens;
00915                                 break;
00916                             //-----------------------------------
00917                             // Select Screen Navigation
00918                             case 17:
00919                                 sMode=0;
00920                                 lastDMode[whichTouched]=99; // Repaint
00921                                 break;
00922                             //-----------------------------------
00923                             // Index Navigation
00924                             case 27: // col 2 row tNavRow
00925                                 dMode[whichTouched]= indexScreen ; // gg - index
00926                                 break;
00927                             //------------------------------------
00928                             // Next Navigation
00929                             case 37: // lower right on Nav Line gg - move next                            
00930                                 dMode[whichTouched]=dMode[whichTouched]<maxScreens?dMode[whichTouched]+1:0;
00931                                 break;
00932                             //------------------------------------
00933                             //------------------------------------
00934                             default:
00935                                 lastDMode[whichTouched]=99;//repaint to clear highlight
00936                                 break;
00937                         }
00938                         break;
00939                     case 2: // numpad
00940                         tCol = buttonX(lastTouch.x,5) ;
00941                         if( tCol >= 5 ){ tCol -= 5; } // touch is on screen 1
00942                         tRow = buttonY(lastTouch.y,4) ;
00943                         highlightButton( tCol,tRow, whichTouched, 5,4) ; // gg - highlight
00944                         if(tCol<4){
00945                             keypad = keypad<<4; // shift left
00946                             keypad += tRow*4+tCol; // add next digit
00947                         } else {
00948                             switch ( tRow ) {
00949                                 case 0: // col 4 row 0
00950                                     keypad = keypad>>4; // shift right
00951                                     break;
00952                                 case 1: // col 4 row 1
00953                                     break;
00954                                 case 2: // col 4 row 2
00955                                     break;
00956                                 case 3: // col 4 row 3
00957                                     sMode=0; // end keypad mode
00958                                     if(saveDmode[0]<=maxScreens)
00959                                         dMode[0]=saveDmode[0];
00960                                     if(saveDmode[1]<=maxScreens)
00961                                         dMode[1]=saveDmode[1];
00962                                     lastDMode[0]=99;
00963                                     lastDMode[1]=99;
00964                                     uidx=99;
00965                                     longTouch=false;
00966                                     break;
00967                                 default:
00968                                     break;
00969                             }
00970                         }
00971                         break;
00972                     case 3:
00973                         break;
00974                     default:
00975                         break;
00976                 } // case sMode
00977             } //recently touched
00978         } else if(sMode==1) { // userIdle if not in hex entry mode
00979             sMode=0;
00980             lastDMode[whichTouched]=99; //LAJ
00981         } //!userIdle
00982 
00983         // Sound tone on power reversal
00984         idir=(kW[0]>0)?true:false;
00985         if(regenMon){
00986             if (idir&&!lidir){
00987                 beep(800,0.02); // Started sinking current
00988             }else if(!idir&&lidir){
00989                 beep(3200,0.02); // Started regen
00990             }
00991         }
00992         lidir=idir;
00993 
00994         if(tick){ // Executes once a second
00995             tick=false;
00996             carCanIdle=(++secsNoCarCanMsg>canTimeout)?true:false;
00997             evCanIdle=(++secsNoEvCanMsg>canTimeout)?true:false;
00998             if(tt.is_touched()){
00999                 if(checkFWupdate){
01000                     updateFirmware();
01001                 }
01002             }else{
01003                 //userIdle=(++secsNoTouch>userTimeout)?true:false;
01004                 secsTouch = 0;
01005             }
01006             longTouch = (++secsTouch>2);
01007             extraLongTouch = (secsTouch>10);
01008             if(extraLongTouch){ //Reset if extra long touch
01009                 mbed_reset();
01010             }
01011             checkFWupdate=false; // Only check once at USB insertion or poweron
01012             lCCon = CCon;
01013             CCkW = (lastMsg[indexLastMsg[0x510]].data[3]&0x7f)*0.125;
01014             if(lastMsg[indexLastMsg[0x510]].data[3]&0x80){
01015                 CCon=true; // On when button pushed
01016             } else if(CCkW==0) {
01017                 CCon=false; // Off when power drops back to zero
01018             }
01019             if(!CCon && lCCon){
01020                 lastDMode[0]=99;//force refresh
01021                 lastDMode[1]=99;//force refresh
01022             }
01023             headlights = (lastMsg[indexLastMsg[0x358]].data[1]&0x80)?true:false;  // headlight/turn signal indicator
01024             if(heaterOn){
01025                 lHeaterOn=true; // Only indicate heater once per power cycle
01026             }
01027             heaterOn =((lastMsg[indexLastMsg[0x54f]].data[5]&0x3f)>2)?true:false;
01028             if(heaterMon && heaterOn && !lHeaterOn){ //Heat on alarm
01029                 beep3(800,0.25,1200,0.25,1600,0.25);
01030             }
01031             if(accOn&&indexLastMsg[0x355]>0){
01032                 miles_kmbar = (lastMsg[indexLastMsg[0x355]].data[4]&0x20)?true:false;  // indicates selected distance units
01033                 metric = !miles_kmbar;
01034             }
01035             accV=floor(mon12V*scale12V*10+0.5)/10; //Round to nearest 10th
01036             accOn=(playbackOpen||(accV>5))?true:false;
01037             moving=(mph[0]>0.1);
01038             charging=(lastMsg[indexLastMsg[0x5bf]].data[4]>0x40)?true:false; // MSB=QC, MSB-1=L2
01039             if (laccOn&&!accOn){ // Car turned off
01040                 dailyGids += startGids-((lastMsg[indexLastMsg[0x5bc]].data[0]<<2)+(lastMsg[indexLastMsg[0x5bc]].data[1]>>6));
01041                 lHeaterOn=false;
01042                 if (showHealth&&!playbackOpen){
01043                     if (saveDmode[0]>maxScreens){
01044                         saveDmode[0]=dMode[0];
01045                         saveDmode[1]=dMode[1];
01046                     }
01047                     dMode[0]=healthScreen;
01048                     dMode[1]=tripScreen;
01049                     secsNoTouch=2;// Keep display on a few seconds
01050                     sMode=0;
01051                     userIdle=false;
01052                 }
01053                 if (repeatPoll) { // Log on shutdown if autopoll enabled
01054                     tripLog(); // Write trip log on powerdown
01055                 }
01056                 //write efficiency history data
01057                 hfile = fopen("/local/ehist.cny", "w");
01058                 if (hfile!=NULL){ // found a efficiency history file
01059                     for(i=0;i<39;i++){
01060                         fprintf(hfile,"%f %f\r\n",mph[i],kW[i]);
01061                     }
01062                     fprintf(hfile,"%f %f\r\n",maxTripEff,minTripEff); // Save max and min
01063                     fprintf(hfile,"%f \r\n",Resr); // Save series resistance
01064                     fprintf(hfile,"%f %f\r\n",maxTripMiles,minTripMiles); // Save max and min
01065                     fprintf(hfile,"%f %f\r\n",maxTripkWh,minTripkWh); // Save max and min
01066                     fprintf(hfile,"%f %f\r\n",maxTripCCkWh,minTripCCkWh); // Save max and min
01067                     for(i=0;i<300;i++){
01068                         fprintf(hfile,"%f\r\n",wh[i]);
01069                     }
01070                     fclose(hfile);
01071                 }
01072             }
01073             if (!laccOn&&accOn){ // Car turned on
01074                 lHeaterOn=false;
01075                 getGids=true;
01076                 miles_trip[0]=0;
01077                 kWh_trip[0]=0;
01078                 CCkWh_trip[0]=0;
01079                 seconds = time(NULL);
01080                 t = *localtime(&seconds);
01081                 if(miles_trip[1]<1){ // charged since last trip
01082 
01083                     // Adjust wh lookup with whpg data
01084                     maxWhpg=0;
01085                     minWh=0;
01086                     whOff=0;
01087                     for(i=1;i<300;i++){
01088                         if(whpg[i]>maxWhpg){ //Find maxWhpg and associated Wh
01089                             maxWhpg = (float) whpg[i];
01090                             minWh = wh[i];
01091                             if(debugMode){
01092                                 sprintf(sTemp,"maxWhpg=%3.1f; minWh=%3.1f\n", maxWhpg, minWh);
01093                                 printMsg(sTemp);
01094                             }
01095                         }
01096                         if(whpg[i]>0){ // Compute adjustment to measured range
01097                             whOff = (maxWhpg-(float)whpg[i])-(wh[i]-minWh);
01098                         }else if(whpg[i-1]>0){ // Compute final offset for rest of range
01099                             whOff = maxWhpg-(wh[i]-minWh);
01100                             if(debugMode){
01101                                 sprintf(sTemp,"whOff=%3.1f\n", whOff);
01102                                 printMsg(sTemp);
01103                             }
01104                         }
01105                         wh[i] += 0.1*whOff; // Add offset; use last known good offset when no data
01106                         whpg[i-1]=0;
01107                     }
01108                     whpg[299]=0;
01109 
01110                     // Check and reset daily efficiency if charged since last trip and at least 24 hours has past
01111                     if((t.tm_yday>lt.tm_yday)&&(t.tm_hour>effCheckTime)){
01112                         if (!ignoreDayData&&(miles_trip[3]>15)){ // Ignore low mileage data
01113                             curEff = miles_trip[3]/kWh_trip[3]; // Get current daily efficiency
01114                             if (maxTripEff<curEff) {
01115                                 maxTripEff=curEff;
01116                                 maxTripMiles=miles_trip[3];
01117                                 maxTripkWh=kWh_trip[3];
01118                                 maxTripCCkWh=CCkWh_trip[3];
01119                                 printMsg("New max efficiency.\n");
01120                             }
01121                             if (minTripEff>curEff) {
01122                                 minTripEff=curEff;
01123                                 minTripMiles=miles_trip[3];
01124                                 minTripkWh=kWh_trip[3];
01125                                 minTripCCkWh=CCkWh_trip[3];
01126                                 printMsg("New min efficiency.\n");
01127                             }
01128                         }
01129                         // Clear daily efficiency data
01130                         dailyGids=0;
01131                         miles_trip[3]=0;
01132                         kWh_trip[3]=0;
01133                         CCkWh_trip[3]=0;
01134                         ignoreDayData=false;
01135                         lt=t; // Remember when counters were cleared (start time for new data)
01136                     }
01137                     if(lt.tm_yday>t.tm_yday){ //Fix for new year
01138                         lt=t;
01139                         }
01140                 } // Charged since last trip
01141                 wait5secs=5;
01142                 if (showHealth&&!playbackOpen){
01143                     if (saveDmode[0]>maxScreens){
01144                         saveDmode[0]=dMode[0];
01145                     }
01146                     dMode[0]=healthScreen;
01147                 }
01148                 syncDone=!autoSync; // clear syncDone flag if autoSync enabled
01149             } // Car turned on
01150             laccOn=accOn;
01151             if(!accOn&&userIdle&&!playbackEn){ // Car off and no user activity - turn off screen
01152                 dled = 0;
01153                 if (saveDmode[0]<=maxScreens){
01154                     dMode[0]=saveDmode[0];
01155                     saveDmode[0]=99;
01156                 }
01157                 if (saveDmode[1]<=maxScreens){
01158                     dMode[1]=saveDmode[1];
01159                     saveDmode[1]=99;
01160                 }
01161             }else if(!headlights){
01162                 dled = ledHi;
01163             } else {
01164                 dled = ledLo;
01165             }
01166             cgids=(lastMsg[indexLastMsg[0x5bc]].data[0]<<2)+(lastMsg[indexLastMsg[0x5bc]].data[1]>>6);
01167             if(getGids){
01168                 startGids=cgids;  //Get gids
01169                 if((startGids>0)&&(startGids<300)){ // Ignore bogus values at startup
01170                     getGids=false;
01171                     lgids=startGids; // initialize wh/gid array
01172                 }
01173             }
01174             if((cgids>0)&&(cgids<300)){
01175                 if(cgids!=lgids){
01176                     if((kWh_trip[1]+CCkWh_trip[1])>0){
01177                         whpg[cgids] = (unsigned short) (1000*(kWh_trip[1]+CCkWh_trip[1])); // Save kWh for each gid since last charge
01178                     }else{
01179                         whpg[cgids] = 0;
01180                     }
01181                     lgids=cgids;
01182                 }
01183             }
01184             if(wait5secs>0){ // Wait a few seconds after poweron to give BMS time to measure CP's
01185                 wait5secs-=1;
01186                 if(wait5secs==0){
01187                     if (repeatPoll) { // Poll on startup if autopoll enabled
01188                         logOnce=true;
01189                         reqMsgCnt=0;
01190                         msgReq.attach(&sendReq,0.015);
01191                         lastDMode[0]=99;
01192                         lastDMode[1]=99;
01193                     }
01194                 }
01195             }
01196             //remove health screen once moving
01197             if(moving&&(saveDmode[0]<=maxScreens)&&(wait5secs==0)){
01198                 dMode[0]=saveDmode[0];
01199                 saveDmode[0]=99;
01200             }
01201 
01202             //compute historic efficiency
01203             if(numSsamples>0){ // Avoid div0
01204                 // calibrated to dash mph which reads slightly fast.
01205                 // 227 would give more accurate mph for MXV4s@40psi - 11.75" distance from center of tire to pavement
01206                 // but then efficiency estimation would not track miles driven as read from the odometer so
01207                 // making CANary have the same error as the Leaf instrumentation
01208                 mph[0]=((float) motorRPM)/numSsamples/110; 
01209             } else {
01210                 mph[0]=0;
01211             }
01212             if(mph[0]>99){
01213                 mph[0]=0;
01214             }
01215             numSsamples=0;
01216 
01217             if(numWsamples>0){ // Avoid div0
01218                 mpkWh[0]=mph[0];
01219                 kW[0]=((float) mWs_x4)/numWsamples/4e3;
01220                 mpkWh[0]/=kW[0];
01221                 if (mpkWh[0]<0) {
01222                     mpkWh[0]=99;// negative means inf.
01223                 }
01224                 kW[0]-=CCkW; // subtract climate control power from recorded value
01225            } else {
01226                 kW[0]=0;
01227                 mpkWh[0]=0;
01228             }
01229             numWsamples=0;
01230 
01231             //if(accOn&&!charging){ // Calculate averages
01232             if(moving){ // Calculate averages for DTE.  Freeze when not moving.
01233                 for(i=1;i<39;i++){
01234                     average=mph[i]/timeConstant[i];
01235                     mph[i]-=average;
01236                     mph[i]+=mph[0];
01237                     mpkWh[i]=average;
01238                     average=kW[i]/timeConstant[i];
01239                     kW[i]-=average;
01240                     kW[i]+=kW[0];
01241                     if(i==dtePeriod) mpkWh_noCC=mpkWh[i]/average; // compute efficiency w/o CC for dtePeriod
01242                     average+=CCkW; //add climate control power back in for display
01243                     mpkWh[i]/=average;
01244                     if (mpkWh[i]<0) {
01245                         mpkWh[i]=99;// negative means inf.
01246                     }
01247                }
01248             }
01249 
01250             if (!charging){
01251                 miles_trip[0]+=mph[0]/3600; // per trip
01252                 miles_trip[1]+=mph[0]/3600; // per charge
01253                 miles_trip[2]+=mph[0]/3600; // user
01254                 miles_trip[3]+=mph[0]/3600; // per day/roundtrip
01255                 kWh_trip[0]+=kW[0]/3600;
01256                 kWh_trip[1]+=kW[0]/3600;
01257                 kWh_trip[2]+=kW[0]/3600;
01258                 kWh_trip[3]+=kW[0]/3600;
01259                 CCkWh_trip[0]+=CCkW/3600;
01260                 CCkWh_trip[1]+=CCkW/3600;
01261                 CCkWh_trip[2]+=CCkW/3600;
01262                 CCkWh_trip[3]+=CCkW/3600;
01263             } else { // charging so reset per charge trip meter
01264                 miles_trip[1]=0;
01265                 kWh_trip[1]=0;
01266                 CCkWh_trip[1]=0;
01267             }
01268             
01269             motorRPM=0;
01270             mWs_x4=0;
01271             
01272             // Compute ESR
01273             if((Imax-Imin)<40){ // do nothing - insufficient delta_I to measure
01274                 unloadedV_x2 = (curRmax+curRmin)/2;
01275             }else if ((redRmax-redRmin)<(curRmax-curRmin)) { // less variation on reduced Resr
01276                 Resr-=0.001;
01277                 unloadedV_x2 = (redRmax+redRmin)/2;
01278             } else if ((incRmax-incRmin)<(curRmax-curRmin)) { // less variation on increased Resr
01279                 Resr+=0.001;
01280                 unloadedV_x2 = (incRmax+incRmin)/2;
01281             } else { // current Resr is still best answer
01282                 unloadedV_x2 = (curRmax+curRmin)/2;
01283             }
01284             curRmin=1000;
01285             curRmax=0;
01286             incRmin=1000;
01287             incRmax=0;
01288             redRmin=1000;
01289             redRmax=0;
01290             Imax=-1000;
01291             Imin=1000;
01292 
01293             if(logCP&&usbEn){
01294                 if(logOnce){
01295                     tripLog();
01296                     logOnce=false;
01297                 }
01298                 logPackVoltages(); // Turbo3, only call
01299             }
01300             if(!usbEn){
01301                 usbEn=detectUSB(); // Keep looking if none found
01302                 if(usbEn){
01303                     //Read USB directory
01304                     sprintf(sTemp,"Reading USB drive\n");
01305                     printMsg(sTemp);
01306                     res = f_opendir(&dir, "");                       /* Open the directory */                  
01307                     if(res == FR_OK) {
01308                         for (i=1;i<20;i++) {
01309                             res = f_readdir(&dir, &fno);                   /* Read a directory item */
01310                             if (res != FR_OK || fno.fname[0] == 0) break;  /* Break on error or end of dir */
01311                             //if (fno.fname[0] == '.') continue;             /* Ignore dot entry */
01312                             fn = fno.fname;
01313                             if (fno.fattrib & AM_DIR) {                    /* It is a directory */
01314                                 sprintf(sTemp,"dir: %s\n", fn);
01315                                 printMsg(sTemp);
01316                             } else {                                       /* It is a file. */
01317                                 sprintf(sTemp,"%s\n", fn);
01318                                 printMsg(sTemp);
01319                             }
01320                         }
01321                         //f_closedir(&dir);
01322                     }
01323                     // Force update to clear USB init garbage
01324                     lastDMode[0]=99;
01325                     lastDMode[1]=99;
01326                     checkFWupdate=true; // Check if screen touched to updateFW
01327                } //if(detectUSB)
01328             } else { 
01329                 usbEn=detectUSB(); // Check to see if it is still inserted
01330                 if(!usbEn){
01331                     sprintf(sTemp,"USB disconnected\n");
01332                     printMsg(sTemp);
01333                     mfr = f_mount(0,&USBdrive); // Prepare for next insertion
01334 
01335                 }
01336             }
01337             if(!syncDone){
01338                 syncDone=syncDateTime();
01339             }
01340             tock=true;
01341         } // tick
01342         
01343         if(step){ // playback
01344             if(playbackOpen&&playbackEn){
01345                 for(i=0;i<120;i++){
01346                     if(!f_eof(&efile)){
01347                         efr=f_read(&efile,&header,5,&bytesRW);
01348                         efr=f_read(&efile,&data,8,&bytesRW);
01349                         logCan(header[0],CANMessage(0x7ff&((header[4]<<8)+header[3]), data, 8)); // Playback
01350                     } else {
01351                         f_close(&efile); // restart                                       
01352                         efr = f_open(&efile,"playback.alc",FA_READ|FA_OPEN_EXISTING);
01353                         lastDMode[whichTouched]=99;//force refresh
01354                         beep(2000,0.25);
01355                     }
01356                 }
01357             }
01358             step=false;
01359         }
01360 
01361         display=display<1?display+1:0; // toggle display
01362         updateDisplay(display);
01363     } //while (true)
01364 }