Dual CANbus monitor and instrumentation cluster supporting ILI9341 display controller

Dependencies:   SPI_TFTx2_ILI9341 TOUCH_TFTx2_ILI9341 TFT_fonts mbed

Fork of CANary by Tick Tock

main.cpp

Committer:
TickTock
Date:
2013-02-27
Revision:
10:ebc6326d9bd6
Parent:
9:c7857e87dd07
Child:
11:e9d155aad4e2

File content as of revision 10:ebc6326d9bd6:

#include "CANary.h"
//To Do:
// USB device detect
// config file on local fs
// user programmable message decode
// brake trainer
// write and read the Mode Data
LocalFileSystem local("local");

// to write to USB Flash Drives, or equivalent (SD card in Reader/Writer)
MSCFileSystem fs("fs"); // to write to a USB Flash Drive

extern "C" void mbed_reset();

time_t seconds ;
Beep spkr(p21);

Ticker ticker;
Timer timer;
DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);
DigitalOut led4(LED4);
PwmOut dled(p24);

InterruptIn touchpad(p17);
CAN can1(p9, p10);      // CAN2 uses pins 9 and 10 (rx, tx) and pin 27 (rs)
DigitalOut can2SleepMode(p8);     // Use pin 8 to control the sleep mode of can2
CAN can2(p30, p29);     // CAN1 uses pins 30 and 29 (rx, tx) and pin 28 (rs)
DigitalOut can1SleepMode(p28);     // Use pin 28 to control the sleep mode of can1
bool logEn = true,logOpen = false;
FILE *rfile;
FILE *file;
char fileName[35] = "" ;
char writeBuffer[maxBufLen][13]; // buffer for USB write
char indexLastMsg[0x800]={0}; // index table for last message
CANMessage lastMsg[100]; // table to store last message of eachtype
unsigned char battData[256]={0};
unsigned char msgChanged[100]; // inidcates which bytes changed
char c;
volatile int writePointer = 0;
volatile int secsNoMsg = 0, secsNoTouch = 0;
volatile bool canIdle = false, userIdle = false;
bool getXY=0; //flag to read touchscreen
char counter = 0;
unsigned char dMode[2] = {7,2}; //display mode
unsigned char sMode = 0; // setup mode
unsigned char lastDMode[2] = {0,0}; //last screen mode
char displayLog[20][40];
unsigned char displayLoc = 0;
unsigned char indexOffset = 1;
bool showCP = false;

TOUCH_TFTx2 tt(p16, p17, p19, p20, p11, p12, p13, p6, p7, p5, "TFT"); // x+,x-,y+,y-,mosi, miso, sclk, cs0, cs1, reset

extern "C" void RTC_IRQHandler() {
    timer.reset(); // zero ms at the-seconds-tic
    canIdle=(++secsNoMsg>canTimeout);
    userIdle=(++secsNoTouch>userTimeout);
    LPC_RTC->ILR |= (1<<0); // clear interrupt to prepare for next
}

extern "C" void RTC_Init (void) {
    LPC_RTC->ILR=0x00; // set up the RTC interrupts
    LPC_RTC->CIIR=0x01; // interrupts each second
    LPC_RTC->CCR = 0x01;  // Clock enable
    //NVIC_SetPriority( RTC_IRQn, 10 );
    NVIC_EnableIRQ( RTC_IRQn );
}

void logMsg (char *msg) {
    strcpy(displayLog[displayLoc],msg);
    displayLoc=displayLoc>17?0:displayLoc+1;
}

void touched(){
    LPC_GPIOINT->IO2IntClr = (LPC_GPIOINT->IO2IntStatR | LPC_GPIOINT->IO2IntStatF);
    secsNoTouch = 0;
    getXY=true;
}

unsigned short getTimeStamp() {
    unsigned short msec = timer.read_ms() ; // read ms from the timer
    unsigned long secs = time(NULL); // seconds past 12:00:00 AM 1 Jan 1900
    unsigned short isecs = secs%60 ; // modulo 60 for 0-59 seconds from RTC
    return ((isecs<<10)+msec) ; // return the two byte time stamp
}

void logCan (char mType, CANMessage canRXmsg) {
    char sTemp[40];
    unsigned short ts = getTimeStamp();
    unsigned long secs = time(NULL); // seconds past 12:00:00 AM 1 Jan 1900
    static unsigned char ii = 0, lasti = 0; // indexindex
    unsigned char changed,i;
    static unsigned char bdi;
    if(logOpen){
        if(canRXmsg.id>0) {
            writeBuffer[writePointer][0]=mType;
            writeBuffer[writePointer][1]=((secs%60)<<2)+((ts&0x300)>>8);
            writeBuffer[writePointer][2]=ts&0xff;
            writeBuffer[writePointer][3]=canRXmsg.id&0xff;
            writeBuffer[writePointer][4]=(canRXmsg.id>>8)+(canRXmsg.len<<4);
            for(i=5;i<13;i++){
                writeBuffer[writePointer][i]=canRXmsg.data[i-5];
            }
            if (++writePointer >= maxBufLen) {
                writePointer = 0;
                led3 = !led3;
            }
        }
    }//if logOpen
    if(indexLastMsg[canRXmsg.id]==0) { //Check if no entry
        ii=ii<99?ii+1:0;
        indexLastMsg[canRXmsg.id]=ii; //Create entry if first message
    }
    if(dMode[0]==changedMode||dMode[1]==changedMode){
        changed=msgChanged[indexLastMsg[canRXmsg.id]];
        for(i=0;i<8;i++){
            if(lastMsg[indexLastMsg[canRXmsg.id]].data[i]!=canRXmsg.data[i]){
                changed |= 1<<i;
            }
        }
        msgChanged[indexLastMsg[canRXmsg.id]]=changed;
    }
    lastMsg[indexLastMsg[canRXmsg.id]]=canRXmsg; //Store in table
    if(mType==1&&canRXmsg.id==0x7bb){ // is battery data?  Need to store all responses
        if(canRXmsg.data[0]<0x20){
            if(canRXmsg.data[3]==2){//cellpair data
                bdi=0;
                sprintf(sTemp,"Getting cell pair data\n");
                logMsg(sTemp);
           }else if(canRXmsg.data[3]==4){//temperature data
                bdi=0x20;
                sprintf(sTemp,"Getting temperature data\n");
                logMsg(sTemp);
            }else bdi=0;
            lasti=0;
        }
        i=canRXmsg.data[0]&0x0f; //lower nibble of D0 is index
        if(lasti>i){ //detect rolloever and offset index appropriately
            bdi=0x10;
        }
        lasti=i; //remember the msb to detect rollover next time around
        i+=bdi;
        i*=7;
        if(i<0xfa){
            battData[i+0]=canRXmsg.data[1];
            battData[i+1]=canRXmsg.data[2];
            battData[i+2]=canRXmsg.data[3];
            battData[i+3]=canRXmsg.data[4];
            battData[i+4]=canRXmsg.data[5];
            battData[i+5]=canRXmsg.data[6];
            battData[i+6]=canRXmsg.data[7];
        }
    }//if 0x7bb
}

void logTS () {
    CANMessage tsMsg;
    unsigned long secs = time(NULL); // seconds past 12:00:00 AM 1 Jan 1900
    tsMsg.id=0xfff;
    tsMsg.len=0xf;
    tsMsg.data[0]=secs&0xff;
    tsMsg.data[1]=(secs>>8)&0xff;
    tsMsg.data[2]=(secs>>16)&0xff;
    tsMsg.data[3]=secs>>24;
    tsMsg.data[4]=0xff;
    tsMsg.data[5]=0xff;
    tsMsg.data[6]=0xff;
    tsMsg.data[7]=0xff;
    logCan(0,tsMsg);
}

void sendCPreq() {
    char i;
    char data[8] = {0x02, 0x21, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff};
    can1.monitor(false); // set to active mode
    can1SleepMode = 0; // enable TX
    can1.write(CANMessage(0x79b, data, 8));
    data[0]=0x30; //change to request next line message
    data[1]=0x01;
    data[2]=0x00;
    for(i=0;i<64;i++){
        wait_ms(16); //wait 16ms
        can1.write(CANMessage(0x79b, data, 8));
    }
    can1SleepMode = 1; // disable TX
    can1.monitor(true); // set to snoop mode
}

void sendTreq() {
    char i;
    char data[8] = {0x02, 0x21, 0x04, 0xff, 0xff, 0xff, 0xff, 0xff};
    can1.monitor(false); // set to active mode
    can1SleepMode = 0; // enable TX
    can1.write(CANMessage(0x79b, data, 8));
    data[0]=0x30; //change to request next line message
    data[1]=0x01;
    data[2]=0x00;
    for(i=0;i<8;i++){
        wait_ms(16); //wait 16ms
        can1.write(CANMessage(0x79b, data, 8));
    }
    can1SleepMode = 1; // disable TX
    can1.monitor(true); // set to snoop mode
}

void recieve1() {
    CANMessage msg1;
    secsNoMsg=0; // reset deadman switch
    can1.read(msg1);
    logCan(1, msg1);
    led1 = !led1;
}

void recieve2() {
    CANMessage msg2;
    secsNoMsg=0; // reset deadman switch
    can2.read(msg2);
    logCan(2, msg2);
    led2 = !led2;
}

void printLast (bool force){
    CANMessage msg;
    tt.locate(0,6);
    tt.foreground(Red);
    tt.background(Yellow);
    if(force) tt.cls(); // Just clear screen if forced - always update display
    tt.set_font((unsigned char*) Arial12x12_prop);  // select the font
    for(int i=0; i<19; i++){
        msg = lastMsg[i+indexOffset];
        printf("%03x : %02x %02x %02x %02x %02x %02x %02x %02x    \n",msg.id,msg.data[0],msg.data[1],msg.data[2],msg.data[3],msg.data[4],msg.data[5],msg.data[6],msg.data[7]);
    }
}

void printChanged (bool force){
    CANMessage msg;
    unsigned char i,j;
    tt.locate(0,6);
    tt.foreground(Red);
    tt.background(Yellow);
    if(force) tt.cls(); // Just clear screen if forced - always update display
    tt.set_font((unsigned char*) Arial12x12_prop);  // select the font
    i=0;
    j=indexOffset;
    do{
        j=j<99?j+1:j;
        if(msgChanged[j]>0){
            msg = lastMsg[j];
            printf("%03x : %02x %02x %02x %02x %02x %02x %02x %02x    \n",msg.id,msg.data[0],msg.data[1],msg.data[2],msg.data[3],msg.data[4],msg.data[5],msg.data[6],msg.data[7]);
            i++;
        }// if changed
    }while(i<19&&j<99);
}

void printLog (bool force){
    static unsigned char lastDisplayLoc = 0;
    if(force||displayLoc!=lastDisplayLoc){ //only update if changed
        tt.foreground(Amber);
        tt.background(Black);
        tt.cls();
        tt.locate(0,6);
        tt.set_font((unsigned char*) Arial12x12);
        for(int i=0; i<19; i++){
            printf("%s",displayLog[displayLoc]);
            displayLoc=displayLoc>17?0:displayLoc+1;
        }
    }
    lastDisplayLoc=displayLoc;
}

void printDTE (bool force){
    unsigned short gids, SOC, packV;
    static unsigned short lgids=0, lSOC=0, lpackV=0;
    CANMessage msg;

    msg = lastMsg[indexLastMsg[0x5bc]]; //Get gids
    gids = (msg.data[0]<<2)+(msg.data[1]>>6);
    msg = lastMsg[indexLastMsg[0x55b]]; //Get SOC
    SOC = (msg.data[0]<<2)+(msg.data[1]>>6);
    msg = lastMsg[indexLastMsg[0x1db]]; //Get pack volts
    packV = (msg.data[2]<<2)+(msg.data[3]>>6);

    tt.background(Navy);
    if(force) tt.cls();
    if(force||gids!=lgids){
        tt.foreground(Amber);
        tt.set_font((unsigned char*) Arial28x28);
        tt.locate(10,10);
        printf("%4d gids\n",gids);
        tt.locate(10,200);
        printf("%4.1f kWh\n",(float)gids*0.08);
        tt.set_font((unsigned char*) SCProSB31x55);
        //tt.set_font((unsigned char*) Neu42x35);
        tt.foreground(Green);
        tt.locate(60,96);
        printf("%4.1f mi\n",(float)gids*0.33); // Approx for now
        lgids=gids;
    }
    if(force||SOC!=lSOC){
        tt.foreground(Amber);
        tt.set_font((unsigned char*) Arial28x28);
        tt.locate(200,10);
        printf("%4.1f%s\n",(float)SOC/10,"%");
        lSOC=SOC;
    }
    if(force||packV!=lpackV){
        tt.foreground(Amber);
        tt.set_font((unsigned char*) Arial28x28);
        tt.locate(200,200);
        printf("%4.1fV\n",(float)packV/2);
        lpackV=packV;
    }
}

void braking (bool force, bool prdata){
    unsigned short targetBraking, regenBraking, speed;
    static unsigned short maxTarget = 0, maxRegen = 0, tarDivReg = 0;
    short rpm;
    unsigned long temp;
    static unsigned char lastPressure[4] = {200,200,200,200};
    unsigned char i,r,t;
    static unsigned char lr, lt;
    CANMessage msg;

    msg = lastMsg[indexLastMsg[0x1cb]]; //Get Target and Regen
    regenBraking = (msg.data[0]<<3)+(msg.data[1]>>5);
    targetBraking = (msg.data[2]<<3)+(msg.data[3]>>5);
    if (targetBraking>maxTarget) maxTarget=targetBraking;
    if (regenBraking>maxRegen) maxRegen=regenBraking;
    if (regenBraking>50) {
        temp = 1000*targetBraking;
        temp /= regenBraking;
        if (temp>tarDivReg) tarDivReg=temp;
    }
    msg = lastMsg[indexLastMsg[0x176]]; //Get rpms - not sure what this is but scales to mph with .0725
    rpm = ((short)msg.data[0]<<8)+msg.data[1];
    speed =rpm>0?rpm>>3:-rpm>>3; //Take absolute to get speed; div8
    msg = lastMsg[indexLastMsg[0x1ca]]; //Get brake pressure

    tt.background(Navy);
    if (force) {
        tt.cls();
        tt.rect(0,111,170,239,White);
        tt.line(0,207,170,207,White);
        tt.line(0,175,170,175,White);
        tt.line(0,143,170,143,White);
        lastPressure[0] = 200;
        lastPressure[1] = 200;
        lastPressure[2] = 200;
        lastPressure[3] = 200;
    }
    // plot bar graph for each wheel pressure
    for (i=0; i<4; i++){
        if (msg.data[i]<239) {
            if (msg.data[i]>lastPressure[i]){
                tt.fillrect(10+40*i,239-msg.data[i],40+40*i,239,Red);
            } else if (msg.data[i]<lastPressure[i]) {
                tt.fillrect(10+40*i,238-lastPressure[i],40+40*i,238-msg.data[i],Navy);
            }
            lastPressure[i]=msg.data[i];
        }
    }

    if(targetBraking>50){
        targetBraking *= speed;
        regenBraking *= speed;
        temp = 200*targetBraking/maxTarget;
        t = (char) temp;
        temp = 200*regenBraking*tarDivReg/maxTarget;
        r = (char) temp;
        if(lr!=r&&prdata){
            tt.foreground(Amber);
            tt.set_font((unsigned char*) Arial28x28);
            tt.locate(100,50);
            printf("%d %d    \n",regenBraking,maxRegen);
            tt.locate(100,90);
            printf("%3.1f (%3.1f%s)    \n",(float)tarDivReg/1000,(float)regenBraking*tarDivReg/targetBraking/1000,"%");
        }    
        if(lt!=t&&prdata){
            tt.foreground(Amber);
            tt.set_font((unsigned char*) Arial28x28);
            tt.locate(100,10);
            printf("%d %d    \n",targetBraking,maxTarget);
        }
        if((lr!=r||lt!=t)&&!prdata){
            if(r<lr)
                tt.fillrect(200,239-lr,300,239-r,Red);
            else
                tt.fillrect(200,239-r,300,239,Green);
            if(t<lt)
                tt.fillrect(200,239-lt,300,239-t,Navy);
            else
                tt.fillrect(200,239-t,300,238-r,Red);
            lt=t;
            lr=r;
        }
    }
}

void cpData(bool force){
    short unsigned max, min, jv, i, bd;
    unsigned avg;
    if(force){
        tt.foreground(White);
        tt.background(Maroon);
        tt.set_font((unsigned char*) Arial12x12_prop);  // select the font
        max=0;
        min=9999;
        avg=0;
        //battData[4]=2;
        //battData[38]=8;
        //battData[79]=3;
        for(i=0; i<96; i++){
           bd=(battData[i*2+3]<<8)+battData[i*2+4];
           avg+=bd;
            if(bd>max) max=bd;
            if(bd<min) min=bd;
        }
        avg /= 96;
        jv=avg-(max-avg)*2.5;
        tt.cls();
        tt.locate(0,6);
        //printf("cellpair temperatures: %dC %dC %dC %dC\ncellpair voltages (mV):\n\n",battData[224+5],battData[224+8],battData[224+11],battData[224+14]);
        printf(" MAX  MIN  AVG  CVLI  T1 T2 T3 T4\n %04d %04d %04d  %04d  %dC %dC %dC %dC\n\n",max,min,avg,jv,battData[224+5],battData[224+8],battData[224+11],battData[224+14]);
        tt.locate(0,36);
        for(i=0; i<16; i++){
            printf("%02d-%02d : %04d %04d %04d %04d %04d %04d\n",i*6+1,i*6+6,(battData[i*12+3]<<8)+battData[i*12+4],(battData[i*12+5]<<8)+battData[i*12+6],(battData[i*12+7]<<8)+battData[i*12+8],(battData[i*12+9]<<8)+battData[i*12+10],(battData[i*12+11]<<8)+battData[i*12+12],(battData[i*12+13]<<8)+battData[i*12+14]);
        }
        for(i=0; i<96; i++){
            bd=(battData[i*2+3]<<8)+battData[i*2+4];
            if(bd>0){
                if(bd==max) tt.rect(58+(i%6)*41,34+(int)(i/6)*12,90+(i%6)*41,46+(int)(i/6)*12,Green);
                if(bd==avg) tt.rect(58+(i%6)*41,34+(int)(i/6)*12,90+(i%6)*41,46+(int)(i/6)*12,White);
                if(bd==min) tt.rect(58+(i%6)*41,34+(int)(i/6)*12,90+(i%6)*41,46+(int)(i/6)*12,Yellow);
                if(bd<jv) tt.rect(58+(i%6)*41,34+(int)(i/6)*12,90+(i%6)*41,46+(int)(i/6)*12,Red);
            }
        }
        showCP=false;
    }
}

void updateDisplay(char display){
    bool changed;
    changed = dMode[display]!=lastDMode[display];
    tt.set_display(display);
    switch (dMode[display]) {
        case logMode:
            printLog(changed);
            break;
        case dteMode:
            printDTE(changed);
            break;
        case brakeMode:
            braking(changed,true);
            break;
        case powerMode:
            braking(changed,false);
            break;
        case monitorMode:
            printLast(changed);
            break;
        case changedMode:
            printChanged(changed);
            break;
        case cpMode:
            cpData(changed||showCP);
            break;
        default:
            tt.background(Black);
            tt.cls();
            break;
    }
    lastDMode[display]=dMode[display];

    switch (sMode) {
        case 1:
            tt.foreground(Yellow);
            tt.background(DarkCyan);
            tt.set_font((unsigned char*) Arial12x12);
            tt.fillrect(btn31x1,btn11y1,btn31x2,btn11y2,DarkCyan);
            tt.locate(btn31x1+5,btn11y1+5);
            printf("<-Prev\n");
            tt.fillrect(btn32x1,btn11y1,btn32x2,btn11y2,DarkCyan);
            tt.fillrect(btn33x1,btn11y1,btn33x2,btn11y2,DarkCyan);
            tt.locate(btn33x2-50,btn11y1+5);
            printf("Next->\n");
            tt.set_display(0);
            tt.locate(btn32x1+15,btn11y1+5);
            printf("Select %d\n",dMode[0]);
            tt.set_display(1);
            tt.locate(btn32x1+15,btn11y1+5);
            printf("Select %d\n",dMode[1]);
            tt.background(Black);
            break;
        default:
            break;
    }
}

int main() {
    int readPointer=0;
    char sTemp[40];
    unsigned long secs;
    char i,j,display=0;
    point lastTouch;    

    can1.monitor(true); // set to snoop mode
    can2.monitor(true); // set to snoop mode
    can1.frequency(500000);
    can2.frequency(500000);
    can1SleepMode = 1;         // Turn on Monitor_only Mode
    can2SleepMode = 1;         // Turn on Monitor_only Mode
    can1.attach(&recieve1);
    can2.attach(&recieve2);
    
    tt.set_orientation(1);
    tt.set_font((unsigned char*) Arial12x12_prop);  // select the font
    tt.set_display(2);     // select right display
    tt.background(Black);
    tt.cls();
    tt.set_display(0);       // select left display
    tt.calibrate();           // calibrate the touch
    tt.claim(stdout);        // send stdout to the TFT display
    touchpad.rise(&touched);
    tt.wfi();               // enable interrupt on touch
    dled = 0.8; // turn on display LED 80%
    timer.start() ;
    RTC_Init(); // start the RTC Interrupts that sync the timer
    struct tm t; // pointer to a static tm structure
    //NVIC_SetPriority(TIMER3_IRQn, 1); //set ticker priority
    //NVIC_SetPriority(CAN_IRQn, 2); //higher than can (so RTC sync works)
    seconds = time(NULL);
    t = *localtime(&seconds) ;
    strftime(sTemp, 32, "%a %m/%d/%Y %X", &t);
    //tt.locate(0,0);
    //printf("\nCurrent time : %s\n", sTemp); // DAY MM/DD/YYYY HH:MM:SS

    // is it a date before 2012 ?
    if ((t.tm_year + 1900) < 2012 ) {
        // before 2012, so the RTC probably lost power
        // So, set a near-recent date in 2012

        // enter people-values here
        t.tm_year = 2013 ; // 28 May 2012
        t.tm_mon = 3 ; // 1 to 12
        t.tm_mday = 5;
        t.tm_hour = 12; // 12:59:56 PM (after noon)
        t.tm_min = 59;
        t.tm_sec = 56;

        // adjust for tm structure required values
        t.tm_year = t.tm_year - 1900;
        t.tm_mon = t.tm_mon - 1;

        // set the RTC
        set_time(mktime(&t));
        seconds = time(NULL);

        //    printf("Set RTC to:\n" );
        //    strftime(sTemp, 32, "%a %m/%d/%Y %X", localtime(&seconds));
        //    printf("%s\n", sTemp); // DAY MM/DD/YYYY HH:MM:SS
    }
    while (true) {
        if (!logOpen) { // Open new file if one is not already open
            if(logEn){ //logging enable
                seconds = time(NULL);
                t = *localtime(&seconds) ;
                strftime(fileName, 32, "/fs/%m%d%H%M.alc", &t); //mmddhhmm.alc
                sprintf(sTemp,"Using file %s\n",fileName);
                logMsg(sTemp);
                file = fopen(fileName, "ab");
                
                if(file==NULL){
                    sprintf(sTemp,"\nUnable to open %s\n\n\n\n",fileName);
                    logMsg(sTemp);
                    logEn=false;
                    spkr.beep(1000,0.2);
                } else {
                    logOpen = true;
                    readPointer=writePointer;
                    sprintf(sTemp,"Starting Can Log %s\n",fileName);
                    logMsg(sTemp);
                    logTS();
                    spkr.beep(2000,0.2);
                }
            }//logging enabled
        } else { // if (!logOpen)
            if (((writePointer+maxBufLen-readPointer)%maxBufLen)>(maxBufLen/16)||canIdle) {
                // Dump buffer if > 1/16 full or canbus has stopped
                if (file == NULL) {
                    logOpen = false;
                    sprintf(sTemp,"Failed to append log file.\n\n");
                    spkr.beep(1000,0.2);
                    logMsg(sTemp);
                    logEn=false;
                } else {
                    while (readPointer != writePointer) {
                        for (j = 0; j<13; j++){
                            fprintf(file,"%c",writeBuffer[readPointer][j]);
                        }
                        if(++readPointer >= maxBufLen)
                            readPointer=0;
                    }
                    led4 = !led4;
                }
            } // if > 1/16 full, canbus has stopped, or PB1 pressed
        } // if logOpen
        if (canIdle&&userIdle) { // canbus idle --> sleep to save power
            if (logOpen){
                fclose(file);
            } // if (logOpen)*/
            sprintf(sTemp,"Putting uC to sleep.\n");
            logMsg(sTemp);
            //LPC_RTC->CIIR=0x00; // block RTC interrupts
            led1=0;
            led2=0;
            led3=0;
            led4=0;
            dled=0; // turn off display
            secs = time(NULL); // seconds past 12:00:00 AM 1 Jan 1900
            while (secsNoMsg>canTimeout && secsNoTouch>userTimeout) {
                //DeepPowerDown();
                tt.wfi(); //enable touchpad input
                __wfi(); // freeze CPU and wait for interrupt (from canbus or touch)
                //Sleep();
                //DeepPowerDown();
            }
            canIdle=secsNoMsg>canTimeout;
            userIdle=userIdle>userTimeout;
            dled=0.8; // turn on display LED
            sprintf(sTemp,"Waking uC.\n");
            logMsg(sTemp);
            if (time(NULL)>(secs+1800)) {
                logOpen = false; // Start new file if asleep for more than 30 minutes
                if (secsNoTouch>100) secsNoTouch = 100; // also mostly reset user Idle counter
            } else if (false){ // insert timestamp on each wake if logging enabled (disabled for now)
                file = fopen(fileName, "ab");
                logTS();
            }
        } // if idle
        
        if(getXY){
            lastTouch = tt.get_touch();       
            lastTouch = tt.to_pixel(lastTouch);          // convert to pixel pos
            getXY = false; // clear interrupt flag
        }
        if (!userIdle) {
            if (secsNoTouch<2) {// Recently touched
                secsNoTouch +=2; // increment to prevent double touch
                if (lastTouch.x>320){
                    i=1;
                    lastTouch.x-=320;
                } else {
                    i=0;
                }
                if (lastTouch.y>btn11y1 && lastTouch.y<btn11y2) {
                    if(sMode==1){
                        if (lastTouch.x>btn31x1 && lastTouch.x<btn31x2) {
                            dMode[i]=dMode[i]>0?dMode[i]-1:maxModes;
                        } else if (lastTouch.x>btn32x1 && lastTouch.x<btn32x2) {
                            secsNoTouch = userTimeout; // immediately exit config mode
                        } else if (lastTouch.x>btn33x1 && lastTouch.x<btn33x2) {
                            dMode[i]=dMode[i]<maxModes?dMode[i]+1:0;
                        }
                    } else sMode=1;
                } else {
                    if (dMode[i]==monitorMode||dMode[i]==changedMode) {
                        if (lastTouch.x>btn31x1 && lastTouch.x<btn31x2) {
                            indexOffset=indexOffset>4?indexOffset-4:1;
                        } else if (lastTouch.x>btn32x1 && lastTouch.x<btn32x2) {
                            for(j=0;j<100;j++) msgChanged[j]=0; // clear changed data
                            lastDMode[i]=99;//force refresh
                        } else if (lastTouch.x>btn33x1 && lastTouch.x<btn33x2) {
                            indexOffset=indexOffset<77?indexOffset+4:80;
                        }
                    } else if (dMode[i]==cpMode) {
                        if (lastTouch.x>btn32x1 && lastTouch.x<btn32x2){
                            sendCPreq(); // send cellpair data request.
                            wait_ms(16);
                            sendTreq(); //send temperature request
                            wait_ms(16);
                            showCP=true;
                        }
                    }
                } //top of screen
            }
        } else { // userIdle
            if(sMode==1){
                sMode=0;
                lastDMode[0]=99;
                lastDMode[1]=99;
            }
        }
        display=display<1?display+1:0; // toggle display
        updateDisplay(display);
        //wait(0.1); // We get >2K messages per second
    } //while (true)
}