/*
ESP32 + ILI9341 LCD Basic Example
https://wokwi.com/projects/325324981270479442
*/
#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
#include <Fonts/FreeSerif9pt7b.h>
#include <Fonts/FreeSerif12pt7b.h>
#include <Fonts/FreeMono9pt7b.h>
#include <Fonts/FreeSans9pt7b.h>
//#define CHARGING
#define TFT_DC 2
#define TFT_CS 15
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);
uint16_t tftwidth = 128;
uint16_t tftheight = 160;
#define ST77XX_WHITE ILI9341_WHITE
#define ST77XX_BLACK ILI9341_BLACK
#define ST77XX_RED ILI9341_RED
#define ST77XX_GREEN ILI9341_GREEN
#define ST77XX_BLUE ILI9341_BLUE
#define Adafruit_ST7735 Adafruit_ILI9341
class CaleST7735FloatWidget{
public:
CaleST7735FloatWidget(Adafruit_ST7735 &tftRef, uint16_t x, uint16_t y, int16_t n_integer_digits=3, int16_t precision = 2, const char *unitNameStr=NULL, uint16_t fColor=ST77XX_WHITE, const GFXfont *f=NULL):tft(tftRef),font(f),fontColor(fColor),xpos(x),ypos(y),num_digits_after_decimal(precision){
unitName[0]='\0';
buff[0]='\0';
min_width = n_integer_digits + (precision > 0 ? 1 + precision : 0);
minVal = -pow(10.0f,n_integer_digits-1) + 1.0f/pow(10.0f,precision);
maxVal = pow(10.0f,n_integer_digits) - 1.0f/pow(10.0f,precision);
valDelta = 0.5/pow(10.0f,precision);
if(unitNameStr && strlen(unitNameStr)<sizeof(unitName)){
strcpy(unitName, unitNameStr);
if(strlen(unitNameStr)==0) unitsPrinted = true; //empty string
}else{
unitName[0]='\0';
unitsPrinted = true;
}
}
bool update(float val){
if(val>maxVal) val = maxVal;
if(val<minVal) val = minVal;
if(abs(val-lastPrintedVal)>valDelta){
if(font) tft.setFont(font);
ErasePreviouslyPrinted();
tft.setTextColor(fontColor);
tft.setCursor(xpos, ypos);
dtostrf(val, min_width, num_digits_after_decimal, buff);
tft.print(buff);
if(!unitsPrinted) { tft.print(unitName); unitsPrinted = true; }
if(font) tft.setFont();
lastPrintedVal = val;
//if(debug) { Serial.print('['); Serial.print(buff); Serial.println(']'); }
return true;
}
return false;
}
private:
void ErasePreviouslyPrintedO(){
tft.setTextColor(ST77XX_BLACK);
tft.setCursor(xpos, ypos);
tft.print(buff);
}
void ErasePreviouslyPrinted(){
int16_t x1, y1;
uint16_t w, h;
tft.getTextBounds(buff, xpos, ypos, &x1, &y1, &w, &h); //doesn't need to draw on display
tft.fillRect(x1, y1, w, h, ST77XX_BLACK);
}
Adafruit_ST7735& tft;
const GFXfont *font;
uint16_t fontColor; //ST77XX_WHITE
uint16_t xpos;
uint16_t ypos;
float lastPrintedVal = -999.99;
char buff[8]; //last printed value
float valDelta;
int16_t min_width;//minimal field width of the output string (n_integer_digits + 1(.) + num_digits_after_decimal)
int16_t num_digits_after_decimal; //length of fractional part
float minVal;
float maxVal;
char unitName[8];
bool unitsPrinted = false;
};
typedef enum {
BATT_SOC,
CELL_VMIN,
CELL_VMAX,
CAC_VOLTS,
CAC_AMPS,
CDC_VOLTS,
CDC_AMPS,
BATT_HEAT,
CABIN_HEAT,
CABIN_AC,
MILEAGE,
AMB_TEMP,
BATT_TEMP,
SPEED,
HV_VOLTS,
HV_AMPS,
BATT_VOLT,
CABIN_KW,
CAC_KW,
CDC_KW,
POWER_KW,
AGG_DIST,
AGG_WH,
AGG_EFF,
AGG_EFFCL,
TRIP_SPEED,
TRIP_DIST,
TRIP_WH,
TRIP_EFF,
TRIP_EFFCL,
EWMA_SPEED,
EWMA_DIST,
EWMA_WH,
EWMA_EFF,
EWMA_EFFCL,
MAXOBDPID } obd_pids;
CaleST7735FloatWidget* widgets[MAXOBDPID];
void AddActivePid(obd_pids ePid, CaleST7735FloatWidget *pW){
widgets[ePid] = pW;
}
void AddActivePidW(obd_pids ePid, CaleST7735FloatWidget *pW){
widgets[ePid] = pW;
}
CaleST7735FloatWidget w(tft,130,80,3,1,"V",ST77XX_WHITE,&FreeSerif9pt7b);
void GetXYAdvance(char symb, int16_t &xAdvance, int16_t &yAdvance){
int16_t cx0 = 20;
int16_t cy0 = 40;
tft.setCursor(cx0, cy0);
tft.print(symb);
xAdvance = tft.getCursorX()-cx0;
tft.println();
yAdvance = tft.getCursorY()-cy0;
}
void setup() {
Serial.begin(115200);
tft.begin();
tft.drawRect(0,0,128,160,ST77XX_BLUE);
tft.drawRect(0,0,tft.width(),tft.height(),ST77XX_BLUE);
//tft.setCursor(0, 170);
//tft.println(tft.width()); //128 240
//tft.println(tft.height()); //160 320
/*
int16_t xAdvance;
int16_t yAdvance;
for(uint8_t idx=0; idx<60; ++idx){
char symb = 0x3A + idx;
tft.setFont(&FreeSerif9pt7b);
GetXYAdvance(symb, xAdvance, yAdvance);
tft.setCursor(0 + 30*(idx/10), 170+10*(idx%10));
tft.setFont();
tft.print(symb); tft.print(','); tft.print(xAdvance);
}
*/
//debug fonts
/*tft.setFont();
tft.setCursor(10, 92);
tft.print("MHKW66%"); tft.fillCircle(10,92,1, ST77XX_RED);
tft.setFont(&FreeSerif9pt7b);
tft.setCursor(10, 117);
tft.print("MHKW66%"); tft.fillCircle(10,117,1, ST77XX_RED);
tft.setFont(&FreeMono9pt7b);
tft.setCursor(10, 135);
tft.print("MHKW66%"); tft.fillCircle(10,135,1, ST77XX_RED);
tft.setFont(&FreeSans9pt7b);
tft.setCursor(10, 153);
tft.print("MHKW66%"); tft.fillCircle(10,153,1, ST77XX_RED);
tft.setFont();//*/
#ifdef CHARGING
AddActivePid(BATT_SOC, new CaleST7735FloatWidget(tft,0,20,2,2,"%",ST77XX_RED,&FreeSerif12pt7b));
AddActivePid(CELL_VMIN, new CaleST7735FloatWidget(tft,77,3,1,3,"V"));
AddActivePid(CELL_VMAX, new CaleST7735FloatWidget(tft,77,3+12,1,3,"V"));
AddActivePid(CAC_VOLTS, new CaleST7735FloatWidget(tft,0,45,3,1,"V"));
AddActivePid(CAC_AMPS, new CaleST7735FloatWidget(tft,0,45+12,3,1,"A"));
AddActivePid(CDC_VOLTS, new CaleST7735FloatWidget(tft,40,45,3,1,"V"));
AddActivePid(CDC_AMPS, new CaleST7735FloatWidget(tft,40,45+12,3,1,"A"));
AddActivePid(HV_VOLTS, new CaleST7735FloatWidget(tft,80,45,3,1,"V"));
AddActivePid(HV_AMPS, new CaleST7735FloatWidget(tft,80,45+12,3,1,"A"));
AddActivePid(BATT_TEMP, new CaleST7735FloatWidget(tft,0,90,3,1,"F"));
AddActivePid(BATT_HEAT, new CaleST7735FloatWidget(tft,5,102,4,0,"W"));
AddActivePid(CABIN_HEAT, new CaleST7735FloatWidget(tft,90,90,4,0,"W"));
AddActivePid(CABIN_AC, new CaleST7735FloatWidget(tft,90,102,4,0,"W"));
AddActivePid(AMB_TEMP, new CaleST7735FloatWidget(tft,0,tftheight-20,3,1,"F"));
AddActivePid(MILEAGE, new CaleST7735FloatWidget(tft,tftwidth-52,tftheight-20,5,1,"M"));
AddActivePidW(BATT_VOLT, new CaleST7735FloatWidget(tft,tftwidth-32,tftheight-10,1,2,"V"));
AddActivePidW(CAC_KW, new CaleST7735FloatWidget(tft,0,70,2,1,""));
AddActivePidW(CDC_KW, new CaleST7735FloatWidget(tft,35,70,3,1,""));
AddActivePidW(POWER_KW, new CaleST7735FloatWidget(tft,80,70,3,1,"kW"));
#else
AddActivePid(BATT_SOC, new CaleST7735FloatWidget(tft,0,20,2,2,"%",ST77XX_RED,&FreeSerif12pt7b));
AddActivePid(BATT_TEMP, new CaleST7735FloatWidget(tft,78,10,3,1,"F"));
AddActivePid(CABIN_HEAT, NULL); //new CaleST7735FloatWidget(tft,30,40,4,0,"W")
AddActivePid(CABIN_AC, NULL); //new CaleST7735FloatWidget(tft,30,50,4,0,"W")
AddActivePid(SPEED, new CaleST7735FloatWidget(tft,5,35,3,1,"MPH"));
AddActivePid(HV_VOLTS, NULL); //new CaleST7735FloatWidget(tft,60,20,3,1,"V")
AddActivePid(HV_AMPS, NULL); //new CaleST7735FloatWidget(tft,60,30,3,1,"A")
AddActivePid(AMB_TEMP, new CaleST7735FloatWidget(tft,0,tftheight-20,3,1,"F"));
AddActivePid(MILEAGE, new CaleST7735FloatWidget(tft,tftwidth-52,tftheight-20,5,1,"M"));
AddActivePidW(BATT_VOLT, new CaleST7735FloatWidget(tft,tftwidth-32,tftheight-10,1,2,"V"));
AddActivePidW(POWER_KW, new CaleST7735FloatWidget(tft,65,35-5,3,1,"kW"));
AddActivePidW(CABIN_KW, new CaleST7735FloatWidget(tft,72,35+5,1,2,"kW"));
AddActivePidW(TRIP_EFF, new CaleST7735FloatWidget(tft,2,77,1,2,"$",ST77XX_GREEN,&FreeSerif9pt7b));
AddActivePidW(TRIP_EFFCL, new CaleST7735FloatWidget(tft,50,72,1,2,"$"));
AddActivePidW(TRIP_DIST, new CaleST7735FloatWidget(tft,85,72,3,2,"M")); //AddActivePidW(TRIP_WH, new CaleST7735FloatWidget(tft,60,90,4,1,"WH"));
AddActivePidW(TRIP_SPEED, new CaleST7735FloatWidget(tft,45,60,3,1,"MPH",ST77XX_GREEN,NULL));
AddActivePidW(AGG_EFF, new CaleST7735FloatWidget(tft,2,85,1,2,"$"));
AddActivePidW(AGG_EFFCL, new CaleST7735FloatWidget(tft,50,85,1,2,"$"));
AddActivePidW(AGG_DIST, new CaleST7735FloatWidget(tft,85,85,3,2,"M")); //AddActivePidW(AGG_WH, new CaleST7735FloatWidget(tft,60,60,4,1,"WH"));
AddActivePidW(EWMA_EFF, new CaleST7735FloatWidget(tft,2,110,1,2,"$",ST77XX_BLUE,&FreeSerif9pt7b));
AddActivePidW(EWMA_EFFCL, new CaleST7735FloatWidget(tft,50,98,1,2,"$"));
AddActivePidW(EWMA_DIST, new CaleST7735FloatWidget(tft,85,98,3,2,"M")); //AddActivePidW(EWMA_WH, new CaleST7735FloatWidget(tft,60,120,4,1,"WH"));
AddActivePidW(EWMA_SPEED, new CaleST7735FloatWidget(tft,45,112,3,1,"MPH",ST77XX_BLUE,NULL));
#endif
tft.setCursor(0, tftheight - 10);
tft.print("533 SavePSkpThr");
}
uint16_t logFileIdx=0;
bool toggle = true;
void loop() {
#ifdef CHARGING
widgets[BATT_SOC]->update(67.61f);
widgets[CELL_VMIN]->update(3.81f);
widgets[CELL_VMAX]->update(3.82f);
widgets[CAC_VOLTS]->update(120.0f);
widgets[CAC_AMPS]->update(32.0f);
widgets[CDC_VOLTS]->update(400.0f);
widgets[CDC_AMPS]->update(150.0f);
widgets[HV_VOLTS]->update(400.0f);
widgets[HV_AMPS]->update(75.0f);
widgets[BATT_TEMP]->update(99.1f);
widgets[BATT_HEAT]->update(1500.0f);
widgets[CABIN_HEAT]->update(7500.0f);
widgets[CABIN_AC]->update(2500.0f);
widgets[AMB_TEMP]->update(75.1f+float(logFileIdx));
widgets[MILEAGE]->update(12345.4f);
widgets[BATT_VOLT]->update(3.81f+0.01*float(logFileIdx));
widgets[CAC_KW]->update(6.3f);
widgets[CDC_KW]->update(25.3f);
widgets[POWER_KW]->update(50.3f+float(logFileIdx<10 ? logFileIdx : 0));
#else
logFileIdx++;
widgets[BATT_SOC]->update(67.61f+0.01*float(logFileIdx));
widgets[BATT_TEMP]->update(67.61f+0.1*float(logFileIdx));
widgets[AMB_TEMP]->update(75.1f+float(logFileIdx));
widgets[MILEAGE]->update(12345.1f+174.3f*float(logFileIdx));
widgets[SPEED]->update(65.1f+0.1*float(logFileIdx));
//widgets[HV_VOLTS]->update(388.1f+float(logFileIdx<10 ? logFileIdx : 0));
//widgets[HV_AMPS]->update(123.2f+float(logFileIdx<10 ? logFileIdx : 0));
widgets[BATT_VOLT]->update(3.81f+0.01*float(logFileIdx));
widgets[POWER_KW]->update(50.3f+float(logFileIdx<10 ? logFileIdx : 0));
widgets[CABIN_KW]->update(3.5f+0.200f*float(logFileIdx));
widgets[TRIP_EFF]->update(5.51f+float(logFileIdx<10 ? logFileIdx : 0));
widgets[TRIP_EFFCL]->update(6.12f+float(logFileIdx<10 ? logFileIdx : 0));
widgets[TRIP_DIST]->update(204.3f+float(logFileIdx<10 ? logFileIdx : 0));
//widgets[TRIP_WH]->update(211.4f+float(logFileIdx<10 ? logFileIdx : 0));
widgets[TRIP_SPEED]->update(65.1f+0.1*float(logFileIdx));
widgets[AGG_EFF]->update(3.92f+float(logFileIdx<10 ? logFileIdx : 0));
widgets[AGG_EFFCL]->update(4.52f+float(logFileIdx<10 ? logFileIdx : 0));
widgets[AGG_DIST]->update(523.8f+float(logFileIdx<10 ? logFileIdx : 0));
//widgets[AGG_WH]->update(315.2f+float(logFileIdx<10 ? logFileIdx : 0));
widgets[EWMA_EFF]->update(3.51f+float(logFileIdx<10 ? logFileIdx : 0));
widgets[EWMA_EFFCL]->update(3.51f+float(logFileIdx<10 ? logFileIdx : 0));
widgets[EWMA_DIST]->update(24.3f+float(logFileIdx<10 ? logFileIdx : 0));
//widgets[EWMA_WH]->update(211.4f+float(logFileIdx<10 ? logFileIdx : 0));
widgets[EWMA_SPEED]->update(65.1f+0.1*float(logFileIdx));
#endif
if(toggle){
tft.fillCircle(tftwidth-5, 14, 3, ST77XX_RED);
tft.setCursor(tftwidth-12, 20);
tft.print(13); //file index
}else{
tft.fillCircle(tftwidth-5, 14, 3, ST77XX_BLACK);
}
toggle = !toggle;
/*
//check position relative to cursor for different fonts:
w.update(2.54); tft.fillCircle(130,80,1, ST77XX_RED);
delay(1000);
w.update(2.541); tft.fillCircle(130,80,1, ST77XX_RED);
delay(1000);
w.update(7.23); tft.fillCircle(130,80,1, ST77XX_RED);
delay(1000);
w.update(107.95); tft.fillCircle(130,80,1, ST77XX_RED);
delay(1000);
w.update(6.62); tft.fillCircle(130,80,1, ST77XX_RED);
delay(1000);*/
delay(1000);
}