Dual CANbus monitor and instrumentation cluster

Dependencies:   SPI_TFTx2 TFT_fonts TOUCH_TFTx2 beep mbed

Fork of CANary by Tick Tock

main.cpp

Committer:
TickTock
Date:
2013-03-24
Revision:
37:fea2c1d52c5f
Parent:
36:dbd39c315258
Child:
38:155ec32c5e91

File content as of revision 37:fea2c1d52c5f:

// main.cpp

//To Do:
// * Log file playback (to enable testing of new passive display modes without having to drive around)
// * USB device detect
// * Ability to update binary from the thumb-drive (requires file timestamp)
// * Cellpair histogram
// * Audible friction brake feedback
// * User-configurable watchpoint
// * Immediately turn off when car is turned off and logging
// * Better graphical DTE display with historic efficiency information considered and displayed
// * Add 50% charge option
// * Tire Pressure Sensor display
// * CSV dump of key parameters on car on/off

#include "mbed.h"
#include "CAN.h"
#include "beep.h"
#include "MSCFileSystem.h"
#include "PowerControl.h"
#include "EthernetPowerControl.h"
#include "utility.h"
#include "displayModes.h"
#include "TOUCH_TFTx2.h"

LocalFileSystem local("local");

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

time_t seconds ;

Ticker autoPoll;
Ticker playback;
Timer timer;

DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);
DigitalOut led4(LED4);

InterruptIn touchpad(p17);
CAN can1(p9, p10);      // CAN1 (EV) uses pins 9 and 10 (rx, tx) and pin 8 (rs)
DigitalOut can1SleepMode(p8);     // Use pin 8 to control the sleep mode of can2
CAN can2(p30, p29);     // CAN2 (CAR) uses pins 30 and 29 (rx, tx) and pin 28 (rs)
DigitalOut can2SleepMode(p28);     // Use pin 28 to control the sleep mode of can1
AnalogIn mon12V(p15);
TOUCH_TFTx2 tt(p16, p17, p19, p20, p11, p12, p13, p6, p7, p5, "TFT"); // x+,x-,y+,y-,mosi, miso, sclk, cs0, cs1, reset
PwmOut dled(p23);
Beep spkr(p21);

bool logEn = false,logOpen = false;
FILE *cfile;
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;
volatile int secsNoTouch = 0;
volatile bool canIdle;
volatile bool userIdle;
bool touched=false; //flag to read touchscreen
char counter = 0;
unsigned char dMode[2] = {dteScreen,brakeScreen}; //display mode
unsigned char sMode = 0; // setup mode
unsigned char lastDMode[2] = {0,0}; //last screen mode
unsigned char dtMode = 6;
char displayLog[20][40];
unsigned char displayLoc = 0;
unsigned char indexOffset = 1;
bool showCP = false;
bool pollCP = false;
bool repeatPoll = false;
bool headlights = false;
bool tick = false;
float ledHi = 0.8; // Bright LED value (until config file read)
float ledLo = 0.1; // Dim LED value (until config file read)
unsigned short pollInt = 300; // polling interval=5 minutes (until config file read)
bool accOn = false; // Accessories on
float scale12V = 16.2; // R1:R2 ratio
signed long mWs = 0;
unsigned long miles = 0;
float mpkWh = 0;
float accV = 0;
bool playbackEn = false;
bool playbackOpen = false;
float playbackInt = 0.032; //read one message every 64 ms
bool step = false;
char header[5];
char data[8];
unsigned short pbts;

int main() {
    int readPointer=0;
    char sTemp[40];
    unsigned long secs;
    unsigned 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.background(Black);
    tt.set_display(2);       // select both displays
    tt.cls();
    tt.claim(stdout);        // send stdout to the TFT display
    touchpad.rise(&touch_ISR);
    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) ;
    // is it a date before 2012 ?
    if ((t.tm_year + 1900) < 2012 ) {
        // before 2013 so update year to make date entry easier
        t.tm_year = 2013 - 1900;
        // set the RTC
        set_time(mktime(&t));
        seconds = time(NULL);
    }
    t = *localtime(&seconds) ;
    strftime(sTemp, 32, "%a %m/%d/%Y %X\n", &t);
    logMsg(sTemp);

    // Look for new binary on thumbdrive
    // Can't make this work right now since USB doesn't attach the right timestamp (so new binary isn't loaded)
    /*cfile = fopen("/usb/CANary.bin", "rb");
    if (cfile!=NULL){ //found a new binary on the thumbdrive so copy it over
        sprintf(sTemp,"New binary found.\n");
        logMsg(sTemp);
        file = fopen("/local/CANary.bin", "wb");
        if (file==NULL){ //failed to open destination
            sprintf(sTemp,"Unable to open destination file.\n");
            logMsg(sTemp);
        } else {
            tt.set_display(2);
            tt.foreground(White);
            tt.background(Black);
            tt.cls();
            tt.locate(1,40);
            printf("%s\n","Copying binary - Do no remove power.");
            tt.locate(1,80);
            printf("CANary will reset when complete.\n");
            wait(1); //Wait 1 sec for display DMA to finish before writing file
            while ( int size = fread( writeBuffer, sizeof(char), maxBufLen*13, cfile )){
                fwrite( writeBuffer, sizeof(char), size, file );
                led4=led3;
                led3=led2;
                led2=led1;
                led1=!led4;
            }
        fclose(cfile);
        fclose(file);
        remove("/usb/CANary.bin"); // delete original
        mbed_reset(); //restart
        }
    }*/

    secsNoMsg = 0;
    secsNoTouch = 0;
    // Read config file
    readConfig();
    touched=false;
    secsNoTouch=2;
    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, "/usb/%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.25);
                } else {
                    logOpen = true;
                    readPointer=writePointer;
                    sprintf(sTemp,"Starting Can Log %s\n",fileName);
                    logMsg(sTemp);
                    logTS();
                    spkr.beep(2000,0.25);
                }
            }//logging enabled
        } else { // if (logOpen)
            if (((writePointer+maxBufLen-readPointer)%maxBufLen)>(maxBufLen/16)||canIdle||!logEn) {
                // Dump buffer if > 1/16 full or canbus has stopped
                if (file == NULL) {
                    logOpen = false;
                    sprintf(sTemp,"Failed to append log file.\n");
                    logMsg(sTemp);
                    spkr.beep(1000,0.25);
                    logEn=false;
                } else {
                    if (((writePointer+maxBufLen-readPointer)%maxBufLen)>(maxBufLen*7/8)) { // Hi-water mark
                        sprintf(sTemp,"Write buffer overrun.\n");
                        logMsg(sTemp);
                        spkr.beep(1000,0.25);
                    }
                    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 logging stopped
            if (!logEn) {
                fclose(file);
                logOpen=false;
            }
        } // if logOpen
        if (canIdle&&userIdle&&!playbackEn) { // canbus idle --> sleep to save power
            if (logOpen){
                fclose(file);
            } // if (logOpen)*/
            seconds = time(NULL);
            t = *localtime(&seconds) ;
            strftime(sTemp, 40, "Sleeping: %a %m/%d/%Y %X\n", &t);
            logMsg(sTemp);
            updateDisplay(0); //Added for turbo3 who has a display override and wants to see the sleep message before going to sleep
            updateDisplay(1);
            //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 && !touched) {
                //DeepPowerDown();
                tt.wfi(); //enable touch interrupt
                //__wfi(); // freeze CPU and wait for interrupt (from canbus or touch)
                Sleep();
            }
            secsNoTouch=0;
            canIdle=secsNoMsg>canTimeout;
            userIdle=secsNoTouch>userTimeout;
            dled=0.8; // turn on display LED
            seconds = time(NULL);
            t = *localtime(&seconds) ;
            strftime(sTemp, 40, "Waking: %a %m/%d/%Y %X\n", &t);
            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(touched){ // call touchscreen procedure if touch interrupt detected
            lastTouch = tt.get_touch();       
            lastTouch = tt.to_pixel(lastTouch);          // convert to pixel pos
            if((lastTouch.x!=639)&&(lastTouch.x!=319)&&(lastTouch.y!=239)){
                secsNoTouch=0; //debounce
            }
            //sprintf(sTemp,"%d,%d ",lastTouch.x,lastTouch.y);
            //logMsg(sTemp);
            touched = false; // clear interrupt flag
        }
        if (!userIdle) {
            if (secsNoTouch<2) {// Recently touched
                secsNoTouch +=2; // increment to prevent double touch
                if (lastTouch.x>320){
                    i=1;
                } else {
                    i=0;
                }
                if (sMode==0) sMode = 1;
                //sprintf(sTemp,"button %d %d,%d %d\n",i,buttonX(lastTouch.x,3),buttonY(lastTouch.y,3),lastTouch.x);
                //logMsg(sTemp);
                switch (sMode) {
                    case 0: // no select
                        break;
                    case 1: // select screen
                        switch (buttonX(lastTouch.x,3)*10+buttonY(lastTouch.y,3)) {
                            case 00:
                            case 30:
                                if (dMode[i]==monitorScreen||dMode[i]==changedScreen) {
                                indexOffset=indexOffset>4?indexOffset-4:1;
                                } else if (dMode[i]==config1Screen) {
                                    wait_ms(500);
                                    tt.background(Black);
                                    tt.calibrate();
                                } else if (dMode[i]==config2Screen) { // slower
                                        playbackInt *=2;
                                    if(playbackEn){
                                        playback.detach();
                                        playback.attach(&playbackISR,playbackInt);
                                    }
                                }
                                break;
                            case 10:
                            case 40:
                                if (dMode[i]==changedScreen) {
                                    for(j=0;j<100;j++) msgChanged[j]=0; // clear changed data
                                    lastDMode[i]=99;//force refresh
                                } else if (dMode[i]==cpScreen) {
                                    pollCP=true;
                                } else if (dMode[i]==config1Screen) {
                                    mbed_reset();
                                } else if (dMode[i]==config2Screen) { // pause/unpause
                                    playbackEn=!playbackEn;
                                    if(playbackEn){
                                        playback.attach(&playbackISR,playbackInt);
                                    }else{
                                        playback.detach();
                                    }
                                }
                                break;
                            case 20:
                            case 50:
                                if (dMode[i]==monitorScreen||dMode[i]==changedScreen) {
                                    indexOffset=indexOffset<77?indexOffset+4:80;
                                } else if (dMode[i]==config1Screen) {
                                    sprintf(sTemp,"Saving config file.\n");
                                    logMsg(sTemp);
                                    saveConfig();
                                    spkr.beep(2000,0.25);
                                } else if (dMode[i]==config2Screen) { // faster
                                    if(playbackInt>.002){
                                        playbackInt/=2;
                                        if(playbackEn){
                                            playback.detach();
                                            playback.attach(&playbackISR,playbackInt);
                                        }
                                    }
                                }
                                break;
                            case 01:
                            case 31:
                                if (dMode[i]==config1Screen) {
                                    logEn = !logEn;
                                    if (!logEn) repeatPoll=false;
                                } else if (dMode[i]==dateScreen){
                                    dtMode=(dtMode<6)?dtMode+1:0;
                                    lastDMode[i]=99;
                                }
                                break;
                            case 11:
                            case 41:
                                if (dMode[i]==config1Screen){
                                    repeatPoll = !repeatPoll&&logEn;
                                    if (repeatPoll) {
                                        autoPoll.attach(&autoPollISR,pollInt);
                                    } else {
                                        autoPoll.detach();
                                    }
                                } else if (dMode[i]==config2Screen) {
                                    // Start/stop playback
                                    if(!playbackOpen){
                                        if(!logOpen){
                                            file = fopen("/usb/playback.alc", "rb");                                          
                                            if(file==NULL){
                                                sprintf(sTemp,"Unable to open /usb/playback.alc\n");
                                                logMsg(sTemp);
                                                spkr.beep(1000,0.25);
                                            } else {
                                                playbackOpen = true;
                                                sprintf(sTemp,"Starting playback\n");
                                                logMsg(sTemp);
                                                spkr.beep(2000,0.25);
                                                can1.attach(&doNothing);// Stop recieving CAN data
                                                can2.attach(&doNothing);
                                            }
                                        } else {
                                            sprintf(sTemp,"Must stop logging first\n");
                                            logMsg(sTemp);
                                        }
                                    } else {
                                        playback.detach();
                                        fclose(file);
                                        playbackOpen=false;
                                        can1.attach(&recieve1);// Restore CAN data recieve
                                        can2.attach(&recieve2);
                                        lastDMode[i]=99;
                                    }
                                    playbackEn=false;
                                } else if (dMode[i]==dateScreen){
                                    upDate(dtMode,true);
                                    lastDMode[i]=99;
                                }
                                break;
                            case 21:
                            case 51:
                                if (dMode[i]==dateScreen){
                                    upDate(dtMode,false);
                                    lastDMode[i]=99;
                                }
                                break;
                            case 02:
                            case 32:
                                dMode[i]=dMode[i]>0?dMode[i]-1:maxScreens;
                                break;
                            case 12:
                            case 42:
                                secsNoTouch = userTimeout; // immediately exit config mode
                                break;
                            case 22:
                            case 52:
                                dMode[i]=dMode[i]<maxScreens?dMode[i]+1:0;
                                break;
                            default:
                                break;
                        }
                        break;
                    case 2: // numpad
                        break;
                    case 3:
                        break;
                    default:
                        break;
                } // case sMode
            } //recently touched
        } else { // userIdle
            if(sMode==1){
                sMode=0;
                lastDMode[0]=99;
                lastDMode[1]=99;
            }
        }
        display=display<1?display+1:0; // toggle display
        updateDisplay(display);

        if(pollCP){ // We do this inside main loop instead of ticker so CAN RX will not be blocked
            sendCPreq(); // send cellpair data request.
            wait_ms(16);
            sendTreq(); //send temperature request
            wait_ms(16);
            showCP=true;
            pollCP=false;
        }
        
        if(tick){ // Executes once a second
            accV=mon12V*scale12V;
            accOn=(accV>5)?true:false;
            if(!accOn&&!logEn&&userIdle&&!playbackEn){
                dled = 0; // turn off display if car off and logging disabled and no user activity
            }else if(!headlights){
                dled = ledHi;
            }else{
                dled = ledLo;
            }
            //if(mWs>0){
            if(true){
                mpkWh= ((float) mWs)/1e6; // just kW for now               
            }else{
                mpkWh=99;
            }
            miles=0;
            mWs=0;
            tick=false;
        }

        if(step){ // playback
            if(playbackOpen&&playbackEn){
                //sprintf(sTemp,"step\n");
                //logMsg(sTemp);
                for(i=0;i<200;i++){
                    if(!feof(file)){
                        fscanf(file,"%5c%8c",&header,&data);
                        pbts=(header[1]<<8)|header[2];
                        logCan(header[0],CANMessage(0x7ff&((header[4]<<8)+header[3]), data, 8));
                    }else{
                        fclose(file); // restart
                        file = fopen("/usb/playback.alc", "rb");                                          
                        spkr.beep(2000,0.25);
                    }
                }
            }
            step=false;
        }

    } //while (true)
}