Dual CANbus monitor and instrumentation cluster supporting ILI9341 display controller
Dependencies: SPI_TFTx2_ILI9341 TOUCH_TFTx2_ILI9341 TFT_fonts mbed
Fork of CANary by
displayModes.cpp
- Committer:
- TickTock
- Date:
- 2013-10-06
- Revision:
- 152:a4d66901785d
- Parent:
- 151:3047ebb3c9a8
- Child:
- 153:e94cfe3c339c
File content as of revision 152:a4d66901785d:
//displayModes.cpp #include "displayModes.h" char sTemp1[40]; char sTemp2[16]; void mainDisplay (bool force, bool showButtons){ unsigned short gids, SOC_x10, packV_x2, tireP; float useable_kWh,dte; unsigned char aTemp; static unsigned short lgids=0, lSOC=0, lpackV_x2=0, ltireP=0; static unsigned char laTemp=0; static float lmaxTemp=0, lkW=0, laccV=0, lmpkWh=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_x10 = (msg.data[0]<<2)+(msg.data[1]>>6); msg = lastMsg[indexLastMsg[0x1db]]; //Get pack volts packV_x2 = (msg.data[2]<<2)+(msg.data[3]>>6); msg = lastMsg[indexLastMsg[0x54c]]; //Get ambient aTemp = msg.data[6]-56; //msg = lastMsg[indexLastMsg[0x79a]]; //Get ambient and cabin temperature //aTemp = msg.data[5]-41; // Need to add convertsion to C if metric //c1Temp = msg.data[4]-41; //c2Temp = msg.data[6]-41; msg = lastMsg[indexLastMsg[0x385]]; //Get tire pressure tireP = msg.data[2]+msg.data[3]+msg.data[4]+msg.data[5]; tt.background(Navy); tt.set_font((unsigned char*) Arial28x28); if(force) tt.cls(); if(skin==ttSkin){ if(force||gids!=lgids||mpkWh[dtePeriod]!=lmpkWh){ tt.locate(16,10); tt.foreground(White); printf("%3d gids \n",gids); tt.locate(10,40); tt.foreground(Cyan); useable_kWh = (float)(gids-5)*0.075; if (useable_kWh<0){ useable_kWh=0; } printf("%4.1f kWh \n",useable_kWh); // Display DTE dte=convertDistance(minTripEff*useable_kWh); tt.foreground(Green); tt.locate(20,80); if(dte>=9.5){ printf("%2.0f \n",dte); //LM - add metric conversion }else{ printf("%2.1f \n",dte); //LM - add metric conversion } tt.set_font((unsigned char*) SCProSB31x55); tt.foreground(Yellow); dte=convertDistance(mpkWh[dtePeriod]*useable_kWh); if(dte>199){ dte=199; } // " "=0x10, "."=0x15, #=0x1D if(dte>=99.5){ tt.locate(80,85); printf(" %3.0f\n",dte); //LM - add metric conversion }else if(dte>=9.5){ tt.locate(94,85); printf(" %2.0f\n",dte); //LM - add metric conversion }else{ tt.locate(89,85); printf(" %2.1f\n",dte); //LM - add metric conversion } tt.set_font((unsigned char*) Arial28x28); tt.locate(195,106); printf("%s\n",distanceUnit()); //LM - add metric conversion dte=convertDistance(maxTripEff*useable_kWh); tt.foreground(Orange); if(dte>=99.5){ tt.locate(255,80); printf("%3.0f \n",dte); //LM - add metric conversion }else if(dte>=9.5){ tt.locate(270,80); printf("%2.0f \n",dte); //LM - add metric conversion }else{ tt.locate(265,80); printf("%2.1f \n",dte); //LM - add metric conversion } lgids=gids; lmpkWh=mpkWh[dtePeriod]; } if(force||kW[0]!=lkW){ tt.foreground(Cyan); if(kW[0]<-10){ //Right justify tt.locate(171,40); printf("%4.2fkW\n",kW[0]); } else if (kW[0]<0){ tt.locate(171,40); printf(" %4.2fkW\n",kW[0]); } else if (kW[0]<10){ tt.locate(165,40); printf(" %4.2fkW\n",kW[0]); } else { tt.locate(165,40); printf(" %4.2fkW\n",kW[0]); } lkW=kW[0]; } if(force||SOC_x10!=lSOC){ tt.locate(215,10); tt.foreground(White); printf("%4.1f%s\n",(float)SOC_x10/10,"%"); lSOC=SOC_x10; } if(force||packV_x2!=lpackV_x2){ tt.locate(210,176); tt.foreground(Yellow); printf("%4.1fV\n",(float)packV_x2/2); lpackV_x2=packV_x2; ltireP=0;//Force tire pressure redraw, too } if(force||aTemp!=laTemp){ tt.foreground(Cyan); tt.locate(20,146); printf("%2.0f%s\n",convertF(aTemp),temperatureUnit()); laTemp=aTemp; } if(force||maxTemp!=lmaxTemp){ tt.foreground(Cyan); tt.locate(210,146); if (convertC(maxTemp)<100){ printf(" %3.1f%s\n",convertC(maxTemp),temperatureUnit()); }else{ printf("%4.1f%s\n",convertC(maxTemp),temperatureUnit()); } lmaxTemp=maxTemp; } if(force||accV!=laccV){ tt.locate(20,176); tt.foreground(Yellow); printf("%3.1fV \n",accV); laccV=accV; } if(force||tireP!=ltireP){ if(msg.data[2]<minTirePressure){ tt.foreground(Orange); // Hi-light if any are low (<35psi) }else{ tt.foreground(LightGrey); } if(msg.data[6]&0x80){ if(msg.data[2]<minTirePressure){ tt.foreground(Orange); // Hi-light if any are low (<35psi) }else{ tt.foreground(LightGrey); } tt.locate(10,206); printf("%3.1f\n",(float)msg.data[2]/4); } if(msg.data[6]&0x40){ if(msg.data[3]<minTirePressure){ tt.foreground(Orange); // Hi-light if any are low (<35psi) }else{ tt.foreground(LightGrey); } tt.locate(90,206); printf("%3.1f\n",(float)msg.data[3]/4); } if(msg.data[6]&0x20){ if(msg.data[4]<minTirePressure){ tt.foreground(Orange); // Hi-light if any are low (<35psi) }else{ tt.foreground(LightGrey); } tt.locate(170,206); printf("%3.1f\n",(float)msg.data[4]/4); } if(msg.data[6]&0x10){ if(msg.data[5]<minTirePressure){ tt.foreground(Orange); // Hi-light if any are low (<35psi) }else{ tt.foreground(LightGrey); } tt.locate(250,206); printf("%3.1f\n",(float)msg.data[5]/4); } ltireP=tireP; } }else {//if(skin==ggSkin){ if(force||gids!=lgids){ tt.locate(10,10); printf("%4d GIDs \n",gids); tt.locate(40,40); // gg - add GIDs Percent of 281 printf("%4.1f%s \n", (float)gids*0.355872, "% ") ; tt.locate(20,70); printf("%4.1f kwh \n",(float)gids*0.075); // gg - closer to usable tt.set_font((unsigned char*) SCProSB31x55); tt.foreground(Green); //tt.locate(60,96); tt.locate(60,116); // gg - move down a little printf("%4.1f %s \n",convertDistance((float)(gids-5)*0.31),distanceUnit()); // Approx for now - LM added metric lgids=gids; tt.foreground(Yellow); tt.set_font((unsigned char*) Arial28x28); } if(force||SOC_x10!=lSOC){ tt.locate(200,10); printf("%4.1f%s\n",(float)SOC_x10/10,"% "); lSOC=SOC_x10; } if(force||packV_x2!=lpackV_x2){ tt.locate(200,200); printf("%4.1fV \n",(float)packV_x2/2); lpackV_x2=packV_x2; } if(force||accV!=laccV){ tt.locate(20,200); printf("%3.1fV \n",accV); laccV=accV; } if(force||kW[0]!=lkW){ tt.locate(160,40); // gg - move left to keep from wrap printf("%3.2fkw \n",kW[0]); // use small w to save space lkW=kW[0]; } } if(led4){ tt.fillcircle(310,10,6,Red); }else{ tt.fillcircle(310,10,6,Navy); } } void printLast (bool force, bool showButtons){ 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]); } if(showButtons){ showButton(0,0," <up>","",4,4); showButton(2,0,"<down>","",4,4); } } void printChanged (bool force, bool showButtons){ 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); if(showButtons){ showButton(0,0," <up>","",4,4); showButton(2,0," <down>","",4,4); showButton(1,0," Reset","Baseline",4,4); } } void printLog (bool force, bool showButtons){ static unsigned char lastldl = 0; unsigned char ldl=displayLoc; if(force||ldl!=lastldl){ //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[ldl]); ldl=ldl>17?0:ldl+1; } } lastldl=ldl; } void tripDisplay (bool force, bool showButtons){ static float lkWh=0; float mpkwh_f; tt.background(White); if(force){ tt.cls(); } if(force||(int)(lkWh*100)!=(int)(kWh_trip[0]*100)){ //only update if changed tt.foreground(Navy); tt.set_font((unsigned char*) Arial28x28); tt.locate(6,210); printf("kWh : %s : Eff\n",distanceUnit()); for(int i=0; i<3; i++){ if(kWh_trip[i]>0.01){ mpkwh_f = convertDistance(miles_trip[i])/kWh_trip[i]; } else { mpkwh_f = 0; } tt.locate(6,20+i*60); printf("%3.2f : %3.1f : %2.1f \n",kWh_trip[i],convertDistance(miles_trip[i]),mpkwh_f); } tt.foreground(Navy); tt.set_font((unsigned char*) Arial12x12); tt.locate(274,18); printf("per\n"); tt.locate(274,33); printf("trip\n"); tt.locate(274,78); printf("per\n"); tt.locate(265,93); printf("charge\n"); tt.locate(265,145); printf("custom\n"); lkWh=kWh_trip[0]; } if(showButtons){ showButton(3,2," Reset"," ",4,4); } } void healthDisplay (bool force, bool showButtons){ unsigned short gids, SOC_x10, SOH_x2; static unsigned short lgids=0, lSOC=0, lSOH=0; static float lmaxTemp=0, lresr=0, lunlV=0; static unsigned long lAh=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_x10 = (msg.data[0]<<2)+(msg.data[1]>>6); msg = lastMsg[indexLastMsg[0x5b3]]; //Get SOH SOH_x2 = msg.data[1]; tt.background(Blue); tt.foreground(Yellow); tt.set_font((unsigned char*) Arial28x28); if(force) tt.cls(); /*if(force||tock){ // for esr debug tt.locate(10,10); printf("%d %d amps\n",Imax,Imin); tt.locate(10,40); printf(" %4.1f %4.1f\n",incRmax/2,incRmin/2); tt.locate(10,70); printf(" %4.1f %4.1f\n",redRmax/2,redRmin/2); tt.locate(10,100); printf(" %4.1f %4.1f\n",curRmax/2,curRmin/2); //tt.locate(10,130); curRmin=1000; curRmax=0; incRmin=1000; incRmax=0; redRmin=1000; redRmax=0; Imax=-1000; Imin=1000; }*/ if(force||gids!=lgids){ tt.locate(10,10); printf("%4d gids \n",gids); lgids=gids; } if(force||SOC_x10!=lSOC){ tt.locate(10,40); printf(" %4.1f%s \n",(float)SOC_x10/10,"% SOC"); lSOC=SOC_x10; } if(force||SOH2_x100!=lSOH){ tt.locate(10,70); printf(" %4.1f%s,%4.1f%s \n",(float)SOH_x2/2,"%",(float)SOH2_x100/100,"% SOH"); lSOH=SOH2_x100; } if(force||Ah_x10000!=lAh){ tt.locate(10,100); printf(" %4.2f Ah cap \n",(float)Ah_x10000/10000); lAh=Ah_x10000; } if(force||maxTemp!=lmaxTemp){ tt.locate(10,130); printf(" %4.1f %s (max) \n",convertC(maxTemp),temperatureUnit()); lmaxTemp=maxTemp; } if(force||unloadedV_x2!=lunlV){ tt.locate(10,160); printf(" %4.1f V \n",unloadedV_x2/2); lunlV=unloadedV_x2; } if(force||Resr!=lresr){ tt.locate(10,190); printf(" %3.0f mOhms \n",Resr*1000); lresr=Resr; } } void braking (bool force, bool showButtons, bool prdata=false){ unsigned long targetBraking, regenBraking; static unsigned long maxTarget = 1000, maxRegen = 1000, tardivreg_x1000 = 1000; unsigned long temp; static unsigned char lastPressure[4] = {200,200,200,200}; unsigned char i,r,t; static unsigned char lr=0, lt=0; signed short steering; unsigned short s; static unsigned short ls; unsigned char throttle; static unsigned char lthrottle; short steerOutBounds = 0 ; CANMessage msg; //--------------- msg = lastMsg[indexLastMsg[0x180]]; //Get Throttle position throttle = msg.data[5]; // ---- steering ---- msg = lastMsg[indexLastMsg[0x002]]; //Get Steering angle steering = (msg.data[1]<<8)+msg.data[0]; if(skin==ttSkin){ s= (unsigned short) ((steering/10)+155)%310; // this modulo wraps display }else{// if(skin==ggSkin){ // do not go off screen left or right. gg - steering short ss = (short) ((steering/15)+160); // less gain 10 -> 15 if(ss<0) { ss=0; steerOutBounds = 1; } if(ss>310) { ss=310; steerOutBounds = 1; } s = (unsigned short) ss; } //-------------- 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; } // display the steering position small square if (s!=ls){ // steering position has moved //tt.fillrect(ls,5,ls+9,14, Navy); // blank old position //---- gg - steering red // box is blanked by top of Braking bar, so move up 5 tt.fillrect(ls,0,ls+9,9, Navy); // blank old position if( steerOutBounds != 0 ) // draw out-of-bounds as a red box tt.fillrect(s,0,s+9,9, Red); // draw out-of-bounds position else tt.fillrect(s,0,s+9,9, White); // draw new in-bounds position //---- //tt.foreground(Yellow); //tt.set_font((unsigned char*) Arial28x28); //tt.locate(10,40); //printf("%d %d \n",s,ls); ls=s; } if (throttle!=lthrottle){ if (throttle>239) throttle=239; if(throttle<lthrottle){ tt.fillrect(280,239-lthrottle,310,239-throttle,Navy); }else{ tt.fillrect(280,239-throttle,310,239,Yellow); } lthrottle=throttle; } // 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]; } } 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); msg = lastMsg[indexLastMsg[0x421]]; //Get Drive Mode if (msg.data[0]==0x18) { // In Neutral regenBraking = 0; // No regen when in Neutral } if (targetBraking<2045){ if ((targetBraking>50)&&(regenBraking>50)){ temp = targetBraking; temp *= 1000; temp /= regenBraking; if (temp<tardivreg_x1000) tardivreg_x1000=temp; } if (targetBraking>maxTarget) maxTarget=targetBraking; if (regenBraking>maxRegen) maxRegen=regenBraking; temp = targetBraking; temp *=200; temp /= maxTarget; t = (char) temp; if (t>200) t=200; temp = regenBraking; temp *= tardivreg_x1000; temp /= maxTarget; temp /= 5; // 1000/200=5 r = (char) temp; if (r>200) r=200; if(lr!=r&&prdata){ tt.foreground(Yellow); tt.set_font((unsigned char*) Arial28x28); tt.locate(100,40); printf("%d %d \n",regenBraking,maxRegen); tt.locate(100,70); printf("%3.1f (%3.1f%s) \n",(float)tardivreg_x1000/10,(float)regenBraking*tardivreg_x1000/targetBraking/10,"%"); } if(lt!=t&&prdata){ tt.foreground(Yellow); tt.set_font((unsigned char*) Arial28x28); tt.locate(100,10); printf("%d %d \n",targetBraking,maxTarget); } if (r>t) t=r; //Should never happen if((lr!=r||lt!=t)&&!prdata){ tt.fillrect(190,10,260,239-t,Navy); tt.fillrect(190,239-t,260,239-r,Red); tt.fillrect(190,239-r,260,239,Green); } lt=t; lr=r; } } void cpData(bool force, bool showButtons){ short unsigned max, min, jv, i, bd; unsigned avg; static char step=0; // counter to allow incremental update if(force){ tt.foreground(White); tt.background(Navy); tt.set_font((unsigned char*) Arial12x12_prop); // select the font max=0; min=9999; avg=0; for(i=0; i<96; i++){ bd=(battData[BatDataBaseG2*7+i*2+3]<<8)+battData[BatDataBaseG2*7+i*2+4]; avg+=bd; if(bd>max) max=bd; if(bd<min) min=bd; } avg /= 96; if(min<3713) { jv=avg-(max-avg)*1.5; } else { // Only compute judgement value if min cellpair meets <= 3712mV requirement jv=0; } char* sTemperatureUnit = temperatureUnit(); switch(step){ case 0: tt.cls(); showCP=true; break; case 1: tt.locate(0,6); printf(" MAX MIN AVG CVLI T1 T2 T3 T4\n %04d %04d %04d %04d %2.0f%s %2.0f%s %2.0f%s %2.0f%s\n\n", max,min,avg,jv, convertC(battData[BatDataBaseG4*7+5]),sTemperatureUnit,convertC(battData[BatDataBaseG4*7+8]),sTemperatureUnit, convertC(battData[BatDataBaseG4*7+11]),sTemperatureUnit,convertC(battData[BatDataBaseG4*7+14]),sTemperatureUnit); tt.rect(8+0*41,16,40+0*41,28,Green); tt.rect(8+1*41,16,40+1*41,28,Yellow); //tt.rect(8+2*41,16,40+2*41,28,White); tt.rect(8+3*41,16,40+3*41,28,Red); break; default: tt.locate(0,36+(step-2)*48); for(i=(step-2)*4; i<(step-1)*4; i++){ printf("%02d-%02d : %04d %04d %04d %04d %04d %04d\n", i*6+1,i*6+6, (battData[BatDataBaseG2*7+i*12+3]<<8)+battData[BatDataBaseG2*7+i*12+4],(battData[BatDataBaseG2*7+i*12+5]<<8)+battData[BatDataBaseG2*7+i*12+6], (battData[BatDataBaseG2*7+i*12+7]<<8)+battData[BatDataBaseG2*7+i*12+8],(battData[BatDataBaseG2*7+i*12+9]<<8)+battData[BatDataBaseG2*7+i*12+10], (battData[BatDataBaseG2*7+i*12+11]<<8)+battData[BatDataBaseG2*7+i*12+12],(battData[BatDataBaseG2*7+i*12+13]<<8)+battData[BatDataBaseG2*7+i*12+14]); } for(i=(step-2)*24; i<(step-1)*24; i++){ bd=(battData[BatDataBaseG2*7+i*2+3]<<8)+battData[BatDataBaseG2*7+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); } } } step=step<5?step+1:0; if(step==0){ showCP=false; } } if(showButtons){ showButton(1,0,"Request","CP Data",4,4); } } //---------------- // gg - index void showIndex(bool force, bool showButtons){ if(force){ tt.foreground(White); tt.background(Navy); //tt.set_font((unsigned char*) Arial12x12_prop); // select the font tt.cls(); // add the buttons to GoTo to other screens // top row showButton(0,0," GoTo"," Main",4,4); showButton(1,0," GoTo"," Brake",4,4); showButton(2,0," GoTo"," EFF",4,4); showButton(3,0," GoTo","Health",4,4); // middle row showButton(0,1," GoTo","CP Data",4,4); showButton(1,1," GoTo","CP Hist",4,4); showButton(2,1," GoTo","CP Bars",4,4); showButton(3,1," GoTo"," Config",4,4); // bottom (not Nav) row showButton(0,2," GoTo","Playback",4,4); //showButton(1,2," GoTo","Set Time",4,4); showButton(2,2," GoTo"," Log",4,4); showButton(3,2," GoTo"," Trip",4,4); showCP=false; } if(sMode==1&&showButtons){ tt.foreground(Yellow); tt.background(DarkCyan); tt.set_font((unsigned char*) Arial12x12); // do nothing here? } } //---------------- // gg - cpbars void cpBarPlot(bool force, bool showButtons){ short unsigned max, min, jv, i, bd; unsigned avg; short unsigned nBar[96] ; // bar height over min if(force){ tt.foreground(White); tt.background(Navy); tt.set_font((unsigned char*) Arial12x12_prop); // select the font max=0; min=9999; avg=0; // calc each cell-pair voltage, find max and min for(i=0; i<96; i++){ bd=(battData[BatDataBaseG2*7+i*2+3]<<8)+battData[BatDataBaseG2*7+i*2+4]; nBar[i] = bd; // init to bar height avg+=bd; if(bd>max) max=bd; if(bd<min) min=bd; } avg /= 96; if(min<3713) { jv=avg-(max-avg)*1.5; } else { // Only compute judgement value if min cellpair meets <= 3712mV requirement jv=0; } //------------------ tt.cls(); // show as vertical bar plot int xWinMin = 26; int xWinMax = 316; int yWinMin = 50; int yWinMax = 150; // draw the Bar Graph Frame, 2 pixels wide tt.rect( xWinMin-1,yWinMin-1, xWinMax+1,yWinMax+1,Red); tt.rect( xWinMin-2,yWinMin-2, xWinMax+2,yWinMax+2,Green); // bar heights int height = yWinMax - yWinMin ; int iBarValMax = max - min ; // zero to N //---------------- if( iBarValMax == 0 ) { // for testing min = 3501 ; //max = min + 95*2 ; // for tall values max = min + 95/4 ; // for small values avg = ( max + min ) / 2; iBarValMax = max - min ; // zero to N for(int i=0; i<96; i++) { //nBar[i] = i*2 + min ; // test tall values nBar[i] = i/4 + min ; // test small values } } //--------------- float nBarScale = float(height) / iBarValMax ; if( nBarScale < 0.1 ) nBarScale = 0.1 ; // do the Bar-height scaling for(int i=0; i<96; i++){ nBar[i] -= min ; // now, 0 to N = iBinValMax nBar[i] *= nBarScale ; // scale, as needed } // label the X axis (approximate) //tt.locate( 2, yWinMin-14 ); printf("%04d = %04d from %1.4f", max, int( height / nBarScale ) + min, nBarScale ); tt.locate( 2, yWinMin-14 ); printf("%04d = (%d) mv range.\n", max , max - min ); tt.locate( 2, yWinMax+5); printf("%04d\n", min ); // values, for now // BatDataBaseG4 * 7 = 280 tt.locate( 0, yWinMax+40 ); char* sTemperatureUnit = temperatureUnit(); printf(" MAX MIN AVG CVLI T1 T2 T3 T4\n %04d %04d %04d %04d %2.0f%s %2.0f%s %2.0f%s %2.0f%s\n", max,min,avg,jv, convertC(battData[BatDataBaseG4*7+5]),sTemperatureUnit,convertC(battData[BatDataBaseG4*7+8]),sTemperatureUnit, convertC(battData[BatDataBaseG4*7+11]),sTemperatureUnit,convertC(battData[BatDataBaseG4*7+14]),sTemperatureUnit); //printf(" MAX MIN AVG CVLI T1 T2 T3 T4\n %04d %04d %04d %04d %02dC %02dC %02dC %02dC\n\n", // max,min,avg,jv, battData[BatDataBaseG4*7+5],battData[BatDataBaseG4*7+8], battData[BatDataBaseG4*7+11],battData[BatDataBaseG4*7+14]); //--------------- // show the bars int nBarWidth = 2 ; int nBarSpace = 1 ; // 1 for testing int xPos = xWinMin + 2 ; // start one from the left for( int i=0; i<96; i++) { height = nBar[i] ; if( height > 100 ) height = 100 ; // clip tops // draw the bar, is always inside x-window if (shunt[i]){ tt.fillrect( xPos,yWinMax-height, xPos+nBarWidth-1,yWinMax, Red); } else { tt.fillrect( xPos,yWinMax-height, xPos+nBarWidth-1,yWinMax, Green); } // tic mark the y axis each 5 if(i%5 == 4){ tt.line( xPos,yWinMax+2, xPos,yWinMax+5, White); // a white tick mark tt.line( xPos+1,yWinMax+2, xPos+1,yWinMax+5, White); // a white tick mark, to widen //tt.rect( xPos,yWinMax+2, xPos+1,yWinMax+5, White); // a white 2-wide tick mark is SLOW } // label the y axis each 10 if(i%10 == 9){ tt.locate( xPos-6, yWinMax+8 ); printf("%02d\n", i+1 ); } // step to the next bar position xPos += nBarWidth + nBarSpace ; } showCP=false; } // handle the button if(sMode==1&&showButtons){ showButton(1,0,"Request","CP Data",4,4); } } //---------------- // gg - hist void cpHistogram(bool force, bool showButtons){ short unsigned max, min, jv, i, bd; unsigned avg; if(force){ tt.foreground(White); tt.background(Navy); tt.set_font((unsigned char*) Arial12x12_prop); // select the font max=0; min=9999; avg=0; for(i=0; i<96; i++){ bd=(battData[BatDataBaseG2*7+i*2+3]<<8)+battData[BatDataBaseG2*7+i*2+4]; avg+=bd; if(bd>max) max=bd; if(bd<min) min=bd; } avg /= 96; if(min<3713) { jv=avg-(max-avg)*1.5; } else { // Only compute judgement value if min cellpair meets <= 3712mV requirement jv=0; } //------------------ tt.cls(); // show as histogram int xWinMin = 20; int xWinMax = 300; int yWinMin = 50; int yWinMax = 150; // draw the Histogram Frame, 2 pixels wide tt.rect( xWinMin-1,yWinMin-1, xWinMax+1,yWinMax+1,Red); tt.rect( xWinMin-2,yWinMin-2, xWinMax+2,yWinMax+2,Green); // binning short nBin[301] ; // bins to count Min values in nBin[0], etc. int height ; int iBinIndxMax = 300 ; int iBinValMax = max - min ; // zero to N if( iBinValMax > iBinIndxMax ) iBinValMax = iBinIndxMax ; // clean the bins for(int i=0; i<=iBinIndxMax; i++) { nBin[i] = 0; } // do the bin counting for(int i=0; i<96; i++){ bd=(battData[BatDataBaseG2*7+i*2+3]<<8)+battData[BatDataBaseG2*7+i*2+4] - min ; if( bd > iBinValMax ) bd = iBinValMax ; nBin[bd] ++ ; } //---------------- if( iBinValMax == 0 ) { // for testing min = 10 ; max = 50 ; avg = ( max + min ) / 2; iBinValMax = max - min ; for(int i=0; i<=(iBinValMax/2); i++) { nBin[i] = i ; nBin[iBinValMax-i] = i ; } } // the values, for now // BatDataBaseG4 * 7 = 280 tt.locate( 0, yWinMax+40 ); char* sTemperatureUnit = temperatureUnit(); printf(" MAX MIN AVG CVLI T1 T2 T3 T4\n %04d %04d %04d %04d %2.0f%s %2.0f%s %2.0f%s %2.0f%s\n\n", max,min,avg,jv, convertC(battData[BatDataBaseG4*7+5]),sTemperatureUnit,convertC(battData[BatDataBaseG4*7+8]),sTemperatureUnit, convertC(battData[BatDataBaseG4*7+11]),sTemperatureUnit,convertC(battData[BatDataBaseG4*7+14]),sTemperatureUnit); //printf(" MAX MIN AVG CVLI T1 T2 T3 T4\n %04d %04d %04d %04d %02dC %02dC %02dC %02dC\n\n", // max,min,avg,jv, battData[BatDataBaseG2*7+BatDataBaseG4*7+5],battData[BatDataBaseG2*7+BatDataBaseG4*7+8], battData[BatDataBaseG2*7+BatDataBaseG4*7+11],battData[BatDataBaseG2*7+BatDataBaseG4*7+14]); //--------------- // show the bars int nBarWidth = 3 ; int nBarSpace = 1 ; // 1 for testing int xPos = (xWinMin + xWinMax) / 2 ; xPos -= (avg-min) * (nBarWidth + nBarSpace) ; for( int i=0; i<=iBinValMax; i++) { height = 4 * nBin[i] ; if( height > 100 ) height = 100 ; // clip tops // if inside the window, draw the bar if( ( xPos + nBarWidth < xWinMax ) && ( xPos > xWinMin ) ) tt.fillrect( xPos,yWinMax-height, xPos+nBarWidth-1,yWinMax, Green); // step to the next bar position xPos += nBarWidth + nBarSpace ; } showCP=false; } // handle the button if(sMode==1&&showButtons){ showButton(1,0,"Request","CP Data",4,4); } } //--------------- void config(bool force, bool showButtons){ if (force) { tt.background(Black); tt.cls(); } //-------- top row -------- showButton(0,0,"Calibrate"," Touch",4,4); // gg - 4x4 showButton(1,0," Reset","",4,4); showButton(2,0," Save"," Config",4,4); // a button to step to the next skin unsigned int nextSkin = skin + 1 ; if( nextSkin > maxSkin ) nextSkin = 0 ; if( nextSkin == ttSkin ) sprintf(sTemp1,"Skin TT"); else if( nextSkin == ggSkin ) sprintf(sTemp1,"Skin GG"); else sprintf(sTemp1,"Skin %d",nextSkin); showButton(3,0," Use",sTemp1,4,4); //------- second row ----- if (logEn&&usbEn) { sprintf(sTemp1,"Disable"); } else { sprintf(sTemp1,"Enable"); } showButton(0,1,sTemp1,"Logging",4,4); if (repeatPoll) { sprintf(sTemp1,"Disable"); } else { sprintf(sTemp1,"Enable"); } showButton(1,1,sTemp1,"Auto CP",4,4); // add Enable/Disable Batt Log gg - yesBattLog if (yesBattLog) { sprintf(sTemp1,"Disable"); } else { sprintf(sTemp1,"Enable"); } showButton(2,1,sTemp1,"Batt Log",4,4); // add Enable/Disable Debug - debugMode if (debugMode) { sprintf(sTemp1," Disable"); } else { sprintf(sTemp1," Enable"); } showButton(3,1,sTemp1," Debug",4,4); if(brakeMon) showButton(0,2,"Disable","BrkMon",4,4); else showButton(0,2," Enable"," BrkMon",4,4); showButton(1,2," Set"," Time",4,4); if (showHealth) { sprintf(sTemp1," Hide"); } else { sprintf(sTemp1," Show"); } showButton(2,2,sTemp1," Health",4,4); showButton(3,2,"Update","Firmware",4,4); } void pbScreen(bool force, bool showButtons){ if (force) { tt.background(Black); tt.cls(); } if(playbackOpen){ showButton(0,0,"Slower"," <--",4,4); if(playbackEn){ sprintf(sTemp1,"Pause"); }else{ sprintf(sTemp1," Run"); } sprintf(sTemp2,"%4.3f ",playbackInt); showButton(1,0,sTemp1,sTemp2,4,4); showButton(2,0,"Faster"," -->",4,4); } if(playbackOpen){ sprintf(sTemp1," Stop"); }else{ sprintf(sTemp1,"Start"); } showButton(1,1,sTemp1,"Playback",4,4); } void showDateTime(bool force, bool showButtons){ //unsigned char year, month, day, hour, minute, second; CANMessage msg; struct tm t; // pointer to a static tm structure time_t seconds ; tt.foreground(Yellow); tt.background(Black); if (force||tock) { tt.cls(); // Read time from car /*msg = lastMsg[indexLastMsg[0x5fa]]; month = msg.data[5]>>4; day = msg.data[2]>>3; msg = lastMsg[indexLastMsg[0x5fb]]; year = msg.data[1]; msg = lastMsg[indexLastMsg[0x5fc]]; hour = msg.data[0]>>3; minute = (msg.data[1]<<4&0x30)+(msg.data[2]>>4); second = msg.data[1]>>2;*/ tt.locate(10,10); tt.set_font((unsigned char*) Arial12x12); if(accOn){ seconds = time(NULL); t = *localtime(&seconds); msg = lastMsg[indexLastMsg[0x5fa]]; t.tm_mon = (msg.data[5]>>4)-1; t.tm_mday = msg.data[2]>>3; msg = lastMsg[indexLastMsg[0x5fb]]; //t.tm_year = msg.data[1]; msg = lastMsg[indexLastMsg[0x5fc]]; t.tm_hour = msg.data[0]>>3; t.tm_min = (msg.data[1]<<4&0x30)+(msg.data[2]>>4); t.tm_sec = msg.data[1]>>2; strftime(sTemp1, 32, "%a %m/%d/%Y %X \n", &t); //printf("Leaf: %02d:%02d:%02d %02d/%02d/%03d\n",hour,minute,second,month,day,year); printf("Leaf: %s",sTemp1); } seconds = time(NULL); t = *localtime(&seconds); strftime(sTemp1, 32, "%a %m/%d/%Y %X \n", &t); tt.locate(10,24); printf("CANary: %s",sTemp1); if(showButtons){ switch(dtMode){ case 0: sprintf(sTemp1,"Year"); break; case 1: sprintf(sTemp1,"Month"); break; case 2: sprintf(sTemp1,"Day"); break; case 3: sprintf(sTemp1,"Hour"); break; case 4: sprintf(sTemp1,"Minute"); break; case 5: sprintf(sTemp1,"Second"); break; default: break; } showButton(0,1,sTemp1,"",4,4); showButton(1,1," Up","",4,4); showButton(2,1," Down","",4,4); if(accOn){ showButton(3,1," Sync","w/ car",4,4); } if(autoSync){ showButton(3,2,"disable"," auto",4,4); }else{ showButton(3,2,"enable"," auto",4,4); } } } } void dteDisplay(bool force, bool showButtons, bool showMiles){ unsigned short i,x,y,lx,ly,gids,radius,color,r,t; unsigned char toVal; static unsigned short lgids=0; static unsigned char leff[39]={0}; CANMessage msg; unsigned long targetBraking, regenBraking, temp; static unsigned long maxTarget = 1000, maxRegen = 1000, tardivreg_x1000 = 1000; static unsigned char lr=0, lt=0; msg = lastMsg[indexLastMsg[0x5bc]]; //Get gids gids = (msg.data[0]<<2)+(msg.data[1]>>6); if(gids==0){ gids=281; // Display new, fully charged capacity until real data obtained } tt.background(Navy); tt.foreground(Yellow); if(force){ tt.set_font((unsigned char*) Arial12x12); tt.cls(); x=50+0*6; tt.locate(x-10,226); printf("sec\n"); tt.line(x,10,x,220,DarkGrey); x=50+9*6; tt.locate(x-10,226); printf("min\n"); tt.line(x,10,x,220,DarkGrey); x=50+18*6; tt.locate(x-10,226); printf("hour\n"); tt.line(x,10,x,220,DarkGrey); x=50+25*6; tt.locate(x-10,226); printf("day\n"); tt.line(x,10,x,220,DarkGrey); x=50+32*6; tt.locate(x-10,226); printf("mon\n"); tt.line(x,10,x,220,DarkGrey); x=50+38*6; //tt.locate(x-10,226); //printf("year\n"); //tt.line(x,10,x,220,DarkGrey); toVal=33; } else { toVal=24;// no need to constantly update the long tc values } if(force||lgids!=gids){ // update Y axis when kWh changes tt.set_font((unsigned char*) Arial24x23); //for(i=0;i<10;i++){ //y=200-i*20; for(i=2;i<7;i++){ y=200-(i-2)*40; tt.locate(0,y-8); if (showMiles){ printf("%3.0f\n",convertDistance(i*((float)(gids-5)*.075))); // LM - Added metric support }else{ printf("%d.0\n",i); } tt.line(48,y,toVal*6+56,y,DarkGrey); } lgids=gids; } if(tock||force){ for(i=2;i<7;i++){ y=200-(i-2)*40; tt.line(40,y,158,y,DarkGrey); } x=50+0*6; tt.line(x,10,x,220,DarkGrey); x=50+9*6; tt.line(x,10,x,220,DarkGrey); x=50+18*6; tt.line(x,10,x,220,DarkGrey); //x=50+25*6; //tt.line(x,60,x,220,DarkGrey); //x=50+32*6; //tt.line(x,60,x,220,DarkGrey); //x=50+38*6; //tt.line(x,60,x,220,DarkGrey); tt.set_font((unsigned char*) SCProSB31x55); tt.foreground(Green); if (showMiles){ float miles = mpkWh[dtePeriod]*((float)(gids-5)*.075); miles = convertDistance(miles); // LM - Metric support // Right justify if (miles>99.9){ //space=18; num=31; . = 23 tt.locate(161,8); printf("%4.1f\n",miles); } else if (miles>9.9){ tt.locate(156,8); printf(" %3.1f\n",miles); } else { tt.locate(151,8); printf(" %2.1f\n",miles); } tt.foreground(Cyan); tt.set_font((unsigned char*) Arial24x23); tt.locate(198,70); printf("%3.1f \n",mpkWh[dtePeriod]); } else { tt.locate(200,10); printf("%3.1f \n",mpkWh[dtePeriod]); tt.foreground(Cyan); tt.set_font((unsigned char*) Arial24x23); tt.locate(222,70); printf("%2.1f \n",curEff); } lx=50; ly=mpkWh[0]*40; if(dtePeriod==0){ radius=6; color=Yellow; }else{ radius=2; color=Green; } if(ly<60){ ly=220; color=Red; }else if(ly<280) { ly=280-ly; }else{ ly=0; } tt.fillcircle(lx,leff[0],radius,Navy); tt.fillcircle(lx,ly,radius,color); for(i=1;i<toVal;i++){ x=50+i*6; y=mpkWh[i]*40; if(i==dtePeriod){ radius=6; color=Yellow; }else{ radius=2; color=Green; } if(y<60){ y=220; color=Red; }else if(y<280) { y=280-y; }else{ y=0; } tt.fillcircle(x,leff[i],radius,Navy); tt.line(x-6,leff[i-1],x,leff[i],Navy); leff[i-1]=ly; if(y>0){ tt.fillcircle(x,y,radius,color); } tt.line(lx,ly,x,y,White); lx=x; ly=y; } leff[i-1]=y; } 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); msg = lastMsg[indexLastMsg[0x421]]; //Get Drive Mode if (msg.data[0]==0x18) { // In Neutral regenBraking = 0; // No regen when in Neutral } if (targetBraking<2045){ if ((targetBraking>50)&&(regenBraking>50)){ temp = targetBraking; temp *= 1000; temp /= regenBraking; if (temp<tardivreg_x1000) tardivreg_x1000=temp; } if (targetBraking>maxTarget) maxTarget=targetBraking; if (regenBraking>maxRegen) maxRegen=regenBraking; temp = targetBraking; temp *=200; temp /= maxTarget; t = (char) temp; if (t>175) t=175; temp = regenBraking; temp *= tardivreg_x1000; temp /= maxTarget; temp /= 5; // 1000/200=5 r = (char) temp; if (r>175) r=175; if (r>t) t=r; //Should never happen if(lr!=r||lt!=t){ tt.fillrect(264,64,310,239-t,Navy); tt.fillrect(264,239-t,310,239-r,Red); tt.fillrect(264,239-r,310,239,Green); } lt=t; lr=r; } } void testDisplay (bool force, bool showButtons){ static unsigned short maxPS=0; unsigned char i, uData[8], year, month, day, hour, minute, second; CANMessage msg; tt.set_font((unsigned char*) Arial24x23); tt.foreground(Yellow); tt.background(Navy); if(force){ tt.cls(); } if(pointerSep>maxPS){maxPS=pointerSep;} tt.locate(10,10); printf("%3d sep %3d max\n",pointerSep,maxPS); msg = lastMsg[indexLastMsg[0x5fa]]; month = msg.data[5]>>4; day = msg.data[2]>>3; msg = lastMsg[indexLastMsg[0x5fb]]; year = msg.data[1]; msg = lastMsg[indexLastMsg[0x5fc]]; hour = msg.data[0]>>3; minute = (msg.data[1]<<4&0x30)+(msg.data[2]>>4); second = msg.data[1]>>2; tt.locate(0,40); printf("%02d%02d%02d %02d%02d%03d\n",hour,minute,second,month,day,year); //printf("%4.2fV %4.2fV \n",accV,accV2); for (i=0; i<8; i++){ msg = lastMsg[indexLastMsg[(uMsgId[i]>>4)]]; uData[i] = msg.data[(uMsgId[i]&0x000f)]; } for (i=0; i<4; i++){ tt.locate(10,90+i*30); printf("%4x:%2x %4x:%2x\n",uMsgId[i],uData[i],uMsgId[i+4],uData[i+4]); } } void updateDisplay(char display){ bool changed,showButtons; changed = dMode[display]!=lastDMode[display]; showButtons = (display==whichTouched)&&(sMode==1); tt.set_display(display); switch (dMode[display]) { case logScreen: printLog(changed,showButtons); break; case mainScreen: mainDisplay(changed,showButtons); break; case brakeScreen: braking(changed,showButtons); break; case dteScreen: dteDisplay(changed,showButtons,true); break; case effScreen: dteDisplay(changed,showButtons,false); break; case monitorScreen: printLast(changed,showButtons); break; case changedScreen: printChanged(changed,showButtons); break; case cpScreen: cpData(changed||showCP,showButtons); break; case configScreen: config(changed,showButtons); break; case playbackScreen: pbScreen(changed,showButtons); break; case dateScreen: showDateTime(changed,showButtons); break; case cpHistScreen: // gg - hist cpHistogram(changed||showCP,showButtons); break; case cpBarScreen: // gg - cpbars cpBarPlot(changed||showCP,showButtons); break; case indexScreen: showIndex(changed,showButtons); break; case tripScreen: tripDisplay(changed,showButtons); break; case healthScreen: healthDisplay(changed,showButtons); break; case testScreen: testDisplay(changed,showButtons); break; default: if (changed){ tt.background(Black); tt.cls(); } break; } lastDMode[display]=dMode[display]; if(display==whichTouched){ switch (sMode) { case 1: // Select screens showButton(0,tNavRow," <-Prev","",4,4); // gg - 4x4 // col 1 see below showButton(2,tNavRow," Go To"," Index",4,4); // gg - index showButton(3,tNavRow," Next->","",4,4); // gg - move next // col 1 in Nav row switch (dMode[display]) { case offScreen: sprintf(sTemp2," Off"); break; case logScreen: sprintf(sTemp2," Log"); break; case mainScreen: sprintf(sTemp2," Main"); break; case brakeScreen: sprintf(sTemp2,"Braking"); break; case dteScreen: sprintf(sTemp2," DTE"); break; case effScreen: sprintf(sTemp2," Eff"); break; case monitorScreen: sprintf(sTemp2," Monitor"); break; case changedScreen: sprintf(sTemp2,"DeltaMon"); break; case cpScreen: sprintf(sTemp2,"CP Data"); break; case configScreen: sprintf(sTemp2," Config"); break; case playbackScreen: sprintf(sTemp2,"Playback"); break; case dateScreen: sprintf(sTemp2,"Set Time"); break; case cpHistScreen: // gg - hist sprintf(sTemp2,"CP Hist"); break; case cpBarScreen: // gg - cpbars sprintf(sTemp2,"CP Bars"); break; case tripScreen: sprintf(sTemp2," Trip"); break; case healthScreen: sprintf(sTemp2,"Health"); break; case testScreen: sprintf(sTemp2," Test"); break; case indexScreen: // gg - index sprintf(sTemp2," Index"); break; } showButton(1,tNavRow," Select",sTemp2,4,4); wait_ms(100); // pause a moment to reduce flicker break; case 2: // numpad showButton(0,0," 1","",4,4); showButton(1,0," 2","",4,4); showButton(2,0," 3","",4,4); showButton(0,1," 4","",4,4); showButton(1,1," 5","",4,4); showButton(2,1," 6","",4,4); showButton(0,2," 7","",4,4); showButton(1,2," 8","",4,4); showButton(2,2," 9","",4,4); showButton(1,3," 0","",4,4); showButton(0,3,"<--","",4,4); showButton(2,3,"-->","",4,4); showButton(3,3,"return","",4,4); case 3: break; default: break; } } tock=false; } // updateDisplay //--------------------- // gg - highlight void highlightButton(unsigned char column, unsigned char row, unsigned char tScn, unsigned char columns, unsigned char rows){ unsigned short x1,x2,y1,y2; x1=column*(320/columns)+btnGap/2; x2=(column+1)*(320/columns)-btnGap/2; y1=row*(240/rows)+btnGap/2; y2=(row+1)*(240/rows)-btnGap/2; tt.set_display(tScn); if( skin == ggSkin ){ // paint the whole button box, for a better visual effect // especially on a screen with a yellow background if( tScn == 0 ) tt.fillrect(x1,y1,x2,y2,White); // DarkCyan); else tt.fillrect(x1,y1,x2,y2,Green); // DarkCyan); } else { tt.fillrect(x1,y1,x2,y2,Green); // DarkCyan); } // paint the outer pixel as a yellow frame tt.rect(x1,y1,x2,y2,Yellow) ; // DarkCyan); } //--------------------- void showButton(unsigned char column, unsigned char row, char * text1, char * text2, unsigned char columns, unsigned char rows){ unsigned short x1,x2,y1,y2; x1=column*(320/columns)+btnGap/2; x2=(column+1)*(320/columns)-btnGap/2; y1=row*(240/rows)+btnGap/2; y2=(row+1)*(240/rows)-btnGap/2; tt.fillrect(x1,y1,x2,y2,DarkCyan); tt.foreground(Yellow); tt.background(DarkCyan); tt.set_font((unsigned char*) Arial12x12); // adapt formatting of text to the smaller 4x4 box tt.locate(x1+btnGap/2,y1+btnGap); // gg - 4x4 printf("%s\n",text1); tt.locate(x1+btnGap/2,y1+btnGap+20); printf("%s\n",text2); } //The temps are stored as metric, distances as imperial... I'm assuming the input based on that - LM float convertC(float input) { if (!metric) { //convert! float output = input *1.8f; output += 32.0f; return output; } return input; } float convertF(float input) { if (metric) { //convert! float output = input -32.0f; output /= 1.8f; return output; } return input; } float convertDistance(float input) { if (metric) { return input / 0.62137f; } return input; } char* distanceUnit() { if(metric) return "km"; return "mi"; } char* temperatureUnit() { if(metric) return "C"; return "F"; }