diff --git a/Board320_240.cpp b/Board320_240.cpp new file mode 100644 index 0000000..c837e2c --- /dev/null +++ b/Board320_240.cpp @@ -0,0 +1,1183 @@ +#ifndef BOARD320_240_CPP +#define BOARD320_240_CPP + +#include +#include +#include "config.h" +#include "BoardInterface.h" +#include "Board320_240.h" + +/** + Init board +*/ +void Board320_240::initBoard() { + + // Set button pins for input + pinMode(this->pinButtonMiddle, INPUT); + pinMode(this->pinButtonLeft, INPUT); + pinMode(this->pinButtonRight, INPUT); + + // mute speaker + if (this->pinSpeaker != 0) { + Serial.println("Mute speaker for m5stack"); + dacWrite(this->pinSpeaker, 0); + } + + // Init display + Serial.println("Init this->tft display"); + this->tft.begin(); + this->tft.invertDisplay(this->invertDisplay); + this->tft.setRotation(this->liveData->settings.displayRotation); + this->setBrightness((this->liveData->settings.lcdBrightness == 0) ? 100 : this->liveData->settings.lcdBrightness); + this->tft.fillScreen(TFT_BLACK); + + bool psramUsed = false; // 320x240 16bpp sprites requires psram +#if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT) + if (psramFound()) + psramUsed = true; +#endif + this->spr.setColorDepth((psramUsed) ? 16 : 8); + this->spr.createSprite(320, 240); +} + +/** + After setup device +*/ +void Board320_240::afterSetup() { + // Show test data on right button during boot device + this->displayScreen = this->liveData->settings.defaultScreen; + if (digitalRead(this->pinButtonRight) == LOW) { + this->loadTestData(); + } +} + +/** + Set brightness level +*/ +void Board320_240::setBrightness(byte lcdBrightnessPerc) { + + analogWrite(TFT_BL, lcdBrightnessPerc); +} + +/** + Clear screen a display two lines message +*/ +void Board320_240::displayMessage(const char* row1, const char* row2) { + + // Must draw directly, withou sprite (due to psramFound check) + this->tft.fillScreen(TFT_BLACK); + this->tft.setTextDatum(ML_DATUM); + this->tft.setTextColor(TFT_WHITE, TFT_BLACK); + this->tft.setFreeFont(&Roboto_Thin_24); + this->tft.setTextDatum(BL_DATUM); + this->tft.drawString(row1, 0, 240 / 2, GFXFF); + this->tft.drawString(row2, 0, (240 / 2) + 30, GFXFF); +} + +/** + Draw cell on dashboard +*/ +void Board320_240::drawBigCell(int32_t x, int32_t y, int32_t w, int32_t h, const char* text, const char* desc, uint16_t bgColor, uint16_t fgColor) { + + int32_t posx, posy; + + posx = (x * 80) + 4; + posy = (y * 60) + 1; + + this->spr.fillRect(x * 80, y * 60, ((w) * 80) - 1, ((h) * 60) - 1, bgColor); + this->spr.drawFastVLine(((x + w) * 80) - 1, ((y) * 60) - 1, h * 60, TFT_BLACK); + this->spr.drawFastHLine(((x) * 80) - 1, ((y + h) * 60) - 1, w * 80, TFT_BLACK); + this->spr.setTextDatum(TL_DATUM); // Topleft + this->spr.setTextColor(TFT_SILVER, bgColor); // Bk, fg color + this->spr.setTextSize(1); // Size for small 5x7 font + this->spr.drawString(desc, posx, posy, 2); + + // Big 2x2 cell in the middle of screen + if (w == 2 && h == 2) { + + // Bottom 2 numbers with charged/discharged kWh from start + posx = (x * 80) + 5; + posy = ((y + h) * 60) - 32; + sprintf(this->tmpStr3, "-%01.01f", this->liveData->params.cumulativeEnergyDischargedKWh - this->liveData->params.cumulativeEnergyDischargedKWhStart); + this->spr.setFreeFont(&Roboto_Thin_24); + this->spr.setTextDatum(TL_DATUM); + this->spr.drawString(this->tmpStr3, posx, posy, GFXFF); + + posx = ((x + w) * 80) - 8; + sprintf(this->tmpStr3, "+%01.01f", this->liveData->params.cumulativeEnergyChargedKWh - this->liveData->params.cumulativeEnergyChargedKWhStart); + this->spr.setTextDatum(TR_DATUM); + this->spr.drawString(this->tmpStr3, posx, posy, GFXFF); + + // Main number - kwh on roads, amps on charges + posy = (y * 60) + 24; + this->spr.setTextColor(fgColor, bgColor); + this->spr.setFreeFont(&Orbitron_Light_32); + this->spr.drawString(text, posx, posy, 7); + + } else { + + // All others 1x1 cells + this->spr.setTextDatum(MC_DATUM); + this->spr.setTextColor(fgColor, bgColor); + this->spr.setFreeFont(&Orbitron_Light_24); + posx = (x * 80) + (w * 80 / 2) - 3; + posy = (y * 60) + (h * 60 / 2) + 4; + this->spr.drawString(text, posx, posy, (w == 2 ? 7 : GFXFF)); + } +} + +/** + Draw small rect 80x30 +*/ +void Board320_240::drawSmallCell(int32_t x, int32_t y, int32_t w, int32_t h, const char* text, const char* desc, int16_t bgColor, int16_t fgColor) { + + int32_t posx, posy; + + posx = (x * 80) + 4; + posy = (y * 32) + 1; + + this->spr.fillRect(x * 80, y * 32, ((w) * 80), ((h) * 32), bgColor); + this->spr.drawFastVLine(((x + w) * 80) - 1, ((y) * 32) - 1, h * 32, TFT_BLACK); + this->spr.drawFastHLine(((x) * 80) - 1, ((y + h) * 32) - 1, w * 80, TFT_BLACK); + this->spr.setTextDatum(TL_DATUM); // Topleft + this->spr.setTextColor(TFT_SILVER, bgColor); // Bk, fg bgColor + this->spr.setTextSize(1); // Size for small 5x7 font + this->spr.drawString(desc, posx, posy, 2); + + this->spr.setTextDatum(TC_DATUM); + this->spr.setTextColor(fgColor, bgColor); + posx = (x * 80) + (w * 80 / 2) - 3; + this->spr.drawString(text, posx, posy + 14, 2); +} + +/** + Show tire pressures / temperatures + Custom field +*/ +void Board320_240::showTires(int32_t x, int32_t y, int32_t w, int32_t h, const char* topleft, const char* topright, const char* bottomleft, const char* bottomright, uint16_t color) { + + int32_t posx, posy; + + this->spr.fillRect(x * 80, y * 60, ((w) * 80) - 1, ((h) * 60) - 1, color); + this->spr.drawFastVLine(((x + w) * 80) - 1, ((y) * 60) - 1, h * 60, TFT_BLACK); + this->spr.drawFastHLine(((x) * 80) - 1, ((y + h) * 60) - 1, w * 80, TFT_BLACK); + + this->spr.setTextDatum(TL_DATUM); + this->spr.setTextColor(TFT_SILVER, color); + this->spr.setTextSize(1); + posx = (x * 80) + 4; + posy = (y * 60) + 0; + this->spr.drawString(topleft, posx, posy, 2); + posy = (y * 60) + 14; + this->spr.drawString(bottomleft, posx, posy, 2); + + this->spr.setTextDatum(TR_DATUM); + posx = ((x + w) * 80) - 4; + posy = (y * 60) + 0; + this->spr.drawString(topright, posx, posy, 2); + posy = (y * 60) + 14; + this->spr.drawString(bottomright, posx, posy, 2); +} + +/** + Main screen (Screen 1) +*/ +void Board320_240::drawSceneMain() { + + // Tire pressure + char pressureStr[4] = "bar"; + char temperatureStr[2] = "C"; + if (this->liveData->settings.pressureUnit != 'b') + strcpy(pressureStr, "psi"); + if (this->liveData->settings.temperatureUnit != 'c') + strcpy(temperatureStr, "F"); + sprintf(this->tmpStr1, "%01.01f%s %02.00f%s", this->liveData->bar2pressure(this->liveData->params.tireFrontLeftPressureBar), pressureStr, this->liveData->celsius2temperature(this->liveData->params.tireFrontLeftTempC), temperatureStr); + sprintf(this->tmpStr2, "%02.00f%s %01.01f%s", this->liveData->celsius2temperature(this->liveData->params.tireFrontRightTempC), temperatureStr, this->liveData->bar2pressure(this->liveData->params.tireFrontRightPressureBar), pressureStr); + sprintf(this->tmpStr3, "%01.01f%s %02.00f%s", this->liveData->bar2pressure(this->liveData->params.tireRearLeftPressureBar), pressureStr, this->liveData->celsius2temperature(this->liveData->params.tireRearLeftTempC), temperatureStr); + sprintf(this->tmpStr4, "%02.00f%s %01.01f%s", this->liveData->celsius2temperature(this->liveData->params.tireRearRightTempC), temperatureStr, this->liveData->bar2pressure(this->liveData->params.tireRearRightPressureBar), pressureStr); + showTires(1, 0, 2, 1, this->tmpStr1, this->tmpStr2, this->tmpStr3, this->tmpStr4, TFT_BLACK); + + // Added later - kwh total in tires box + // TODO: refactoring + this->spr.setTextDatum(TL_DATUM); + this->spr.setTextColor(TFT_GREEN, TFT_BLACK); + sprintf(this->tmpStr1, "C: %01.01f +%01.01fkWh", this->liveData->params.cumulativeEnergyChargedKWh, this->liveData->params.cumulativeEnergyChargedKWh - this->liveData->params.cumulativeEnergyChargedKWhStart); + this->spr.drawString(this->tmpStr1, (1 * 80) + 4, (0 * 60) + 30, 2); + this->spr.setTextColor(TFT_YELLOW, TFT_BLACK); + sprintf(this->tmpStr1, "D: %01.01f -%01.01fkWh", this->liveData->params.cumulativeEnergyDischargedKWh, this->liveData->params.cumulativeEnergyDischargedKWh - this->liveData->params.cumulativeEnergyDischargedKWhStart); + this->spr.drawString(this->tmpStr1, (1 * 80) + 4, (0 * 60) + 44, 2); + + // batPowerKwh100 on roads, else batPowerAmp + if (this->liveData->params.speedKmh > 20) { + sprintf(this->tmpStr1, "%01.01f", this->liveData->km2distance(this->liveData->params.batPowerKwh100)); + drawBigCell(1, 1, 2, 2, this->tmpStr1, ((this->liveData->settings.distanceUnit == 'k') ? "POWER KWH/100KM" : "POWER KWH/100MI"), (this->liveData->params.batPowerKwh100 >= 0 ? TFT_DARKGREEN2 : (this->liveData->params.batPowerKwh100 < -30.0 ? TFT_RED : TFT_DARKRED)), TFT_WHITE); + } else { + // batPowerAmp on chargers (under 10kmh) + sprintf(this->tmpStr1, "%01.01f", this->liveData->params.batPowerKw); + drawBigCell(1, 1, 2, 2, this->tmpStr1, "POWER KW", (this->liveData->params.batPowerKw >= 0 ? TFT_DARKGREEN2 : (this->liveData->params.batPowerKw <= -30 ? TFT_RED : TFT_DARKRED)), TFT_WHITE); + } + + // socPerc + sprintf(this->tmpStr1, "%01.00f%%", this->liveData->params.socPerc); + sprintf(this->tmpStr2, (this->liveData->params.sohPerc == 100.0 ? "SOC/H%01.00f%%" : "SOC/H%01.01f%%"), this->liveData->params.sohPerc); + drawBigCell(0, 0, 1, 1, this->tmpStr1, this->tmpStr2, (this->liveData->params.socPerc < 10 || this->liveData->params.sohPerc < 100 ? TFT_RED : (this->liveData->params.socPerc > 80 ? TFT_DARKGREEN2 : TFT_DEFAULT_BK)), TFT_WHITE); + + // batPowerAmp + sprintf(this->tmpStr1, (abs(this->liveData->params.batPowerAmp) > 9.9 ? "%01.00f" : "%01.01f"), this->liveData->params.batPowerAmp); + drawBigCell(0, 1, 1, 1, this->tmpStr1, "CURRENT A", (this->liveData->params.batPowerAmp >= 0 ? TFT_DARKGREEN2 : TFT_DARKRED), TFT_WHITE); + + // batVoltage + sprintf(this->tmpStr1, "%03.00f", this->liveData->params.batVoltage); + drawBigCell(0, 2, 1, 1, this->tmpStr1, "VOLTAGE", TFT_DEFAULT_BK, TFT_WHITE); + + // batCellMinV + sprintf(this->tmpStr1, "%01.02f", this->liveData->params.batCellMaxV - this->liveData->params.batCellMinV); + sprintf(this->tmpStr2, "CELLS %01.02f", this->liveData->params.batCellMinV); + drawBigCell(0, 3, 1, 1, ( this->liveData->params.batCellMaxV - this->liveData->params.batCellMinV == 0.00 ? "OK" : this->tmpStr1), this->tmpStr2, TFT_DEFAULT_BK, TFT_WHITE); + + // batTempC + sprintf(this->tmpStr1, ((this->liveData->settings.temperatureUnit == 'c') ? "%01.00f" : "%01.01f"), this->liveData->celsius2temperature(this->liveData->params.batMinC)); + sprintf(this->tmpStr2, ((this->liveData->settings.temperatureUnit == 'c') ? "BATT. %01.00fC" : "BATT. %01.01fF"), this->liveData->celsius2temperature(this->liveData->params.batMaxC)); + drawBigCell(1, 3, 1, 1, this->tmpStr1, this->tmpStr2, TFT_TEMP, (this->liveData->params.batTempC >= 15) ? ((this->liveData->params.batTempC >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED); + + // batHeaterC + sprintf(this->tmpStr1, ((this->liveData->settings.temperatureUnit == 'c') ? "%01.00f" : "%01.01f"), this->liveData->celsius2temperature(this->liveData->params.batHeaterC)); + drawBigCell(2, 3, 1, 1, this->tmpStr1, "BAT.HEAT", TFT_TEMP, TFT_WHITE); + + // Aux perc + sprintf(this->tmpStr1, "%01.00f%%", this->liveData->params.auxPerc); + drawBigCell(3, 0, 1, 1, this->tmpStr1, "AUX BAT.", (this->liveData->params.auxPerc < 60 ? TFT_RED : TFT_DEFAULT_BK), TFT_WHITE); + + // Aux amp + sprintf(this->tmpStr1, (abs(this->liveData->params.auxCurrentAmp) > 9.9 ? "%01.00f" : "%01.01f"), this->liveData->params.auxCurrentAmp); + drawBigCell(3, 1, 1, 1, this->tmpStr1, "AUX AMPS", (this->liveData->params.auxCurrentAmp >= 0 ? TFT_DARKGREEN2 : TFT_DARKRED), TFT_WHITE); + + // auxVoltage + sprintf(this->tmpStr1, "%01.01f", this->liveData->params.auxVoltage); + drawBigCell(3, 2, 1, 1, this->tmpStr1, "AUX VOLTS", (this->liveData->params.auxVoltage < 12.1 ? TFT_RED : (this->liveData->params.auxVoltage < 12.6 ? TFT_ORANGE : TFT_DEFAULT_BK)), TFT_WHITE); + + // indoorTemperature + sprintf(this->tmpStr1, "%01.01f", this->liveData->celsius2temperature(this->liveData->params.indoorTemperature)); + sprintf(this->tmpStr2, "IN/OUT%01.01f", this->liveData->celsius2temperature(this->liveData->params.outdoorTemperature)); + drawBigCell(3, 3, 1, 1, this->tmpStr1, this->tmpStr2, TFT_TEMP, TFT_WHITE); +} + +/** + Speed + kwh/100km (Screen 2) +*/ +void Board320_240::drawSceneSpeed() { + + int32_t posx, posy; + + // HUD + if (this->displayScreenSpeedHud) { + + // Change rotation to vertical & mirror + if (this->tft.getRotation() != 6) { + this->tft.setRotation(6); + } + + this->tft.fillScreen(TFT_BLACK); + this->tft.setTextDatum(TR_DATUM); // top-right alignment + this->tft.setTextColor(TFT_WHITE, TFT_BLACK); // foreground, background text color + + // Draw speed + this->tft.setTextSize((this->liveData->params.speedKmh > 99) ? 1 : 2); + sprintf(this->tmpStr3, "0"); + if (this->liveData->params.speedKmh > 10) + sprintf(this->tmpStr3, "%01.00f", this->liveData->km2distance(this->liveData->params.speedKmh)); + this->tft.drawString(this->tmpStr3, 240, 0, 8); + + // Draw power kWh/100km (>25kmh) else kW + this->tft.setTextSize(1); + if (this->liveData->params.speedKmh > 25 && this->liveData->params.batPowerKw < 0) + sprintf(this->tmpStr3, "%01.01f", this->liveData->km2distance(this->liveData->params.batPowerKwh100)); + else + sprintf(this->tmpStr3, "%01.01f", this->liveData->params.batPowerKw); + this->tft.drawString(this->tmpStr3, 240, 150, 8); + + // Draw soc% + sprintf(this->tmpStr3, "%01.00f", this->liveData->params.socPerc); + this->tft.drawString(this->tmpStr3, 240 , 230, 8); + + // Cold gate cirlce + this->tft.fillCircle(30, 280, 25, (this->liveData->params.batTempC >= 15) ? ((this->liveData->params.batTempC >= 25) ? TFT_DARKGREEN2 : TFT_BLUE) : TFT_RED); + + // Brake lights + this->tft.fillRect(0, 310, 240, 10, (this->liveData->params.brakeLights) ? TFT_RED : TFT_BLACK); + + return; + } + + // + this->spr.fillRect(0, 36, 200, 160, TFT_DARKRED); + + posx = 320 / 2; + posy = 40; + this->spr.setTextDatum(TR_DATUM); + this->spr.setTextColor(TFT_WHITE, TFT_DARKRED); + this->spr.setTextSize(2); // Size for small 5cix7 font + sprintf(this->tmpStr3, "0"); + if (this->liveData->params.speedKmh > 10) + sprintf(this->tmpStr3, "%01.00f", this->liveData->km2distance(this->liveData->params.speedKmh)); + this->spr.drawString(this->tmpStr3, 200, posy, 7); + + posy = 145; + this->spr.setTextDatum(TR_DATUM); // Top center + this->spr.setTextSize(1); + if (this->liveData->params.speedKmh > 25 && this->liveData->params.batPowerKw < 0) { + sprintf(this->tmpStr3, "%01.01f", this->liveData->km2distance(this->liveData->params.batPowerKwh100)); + } else { + sprintf(this->tmpStr3, "%01.01f", this->liveData->params.batPowerKw); + } + this->spr.drawString(this->tmpStr3, 200, posy, 7); + + // Bottom 2 numbers with charged/discharged kWh from start + this->spr.setFreeFont(&Roboto_Thin_24); + this->spr.setTextColor(TFT_WHITE, TFT_BLACK); + posx = 5; + posy = 5; + this->spr.setTextDatum(TL_DATUM); + sprintf(this->tmpStr3, ((this->liveData->settings.distanceUnit == 'k') ? "%01.00fkm " : "%01.00fmi "), this->liveData->km2distance(this->liveData->params.odoKm)); + this->spr.drawString(this->tmpStr3, posx, posy, GFXFF); + if (this->liveData->params.motorRpm > -1) { + this->spr.setTextDatum(TR_DATUM); + sprintf(this->tmpStr3, " %01.00frpm" , this->liveData->params.motorRpm); + this->spr.drawString(this->tmpStr3, 320 - posx, posy, GFXFF); + } + + // Bottom info + // Cummulative regen/power + posy = 240 - 5; + sprintf(this->tmpStr3, "-%01.01f ", this->liveData->params.cumulativeEnergyDischargedKWh - this->liveData->params.cumulativeEnergyDischargedKWhStart); + this->spr.setTextDatum(BL_DATUM); + this->spr.drawString(this->tmpStr3, posx, posy, GFXFF); + posx = 320 - 5; + sprintf(this->tmpStr3, " +%01.01f", this->liveData->params.cumulativeEnergyChargedKWh - this->liveData->params.cumulativeEnergyChargedKWhStart); + this->spr.setTextDatum(BR_DATUM); + this->spr.drawString(this->tmpStr3, posx, posy, GFXFF); + // Bat.power + posx = 320 / 2; + sprintf(this->tmpStr3, " %01.01fkw ", this->liveData->params.batPowerKw); + this->spr.setTextDatum(BC_DATUM); + this->spr.drawString(this->tmpStr3, posx, posy, GFXFF); + + // RIGHT INFO + // Battery "cold gate" detection - red < 15C (43KW limit), <25 (blue - 55kW limit), green all ok + this->spr.fillCircle(290, 60, 25, (this->liveData->params.batTempC >= 15) ? ((this->liveData->params.batTempC >= 25) ? TFT_DARKGREEN2 : TFT_BLUE) : TFT_RED); + this->spr.setTextColor(TFT_WHITE, (this->liveData->params.batTempC >= 15) ? ((this->liveData->params.batTempC >= 25) ? TFT_DARKGREEN2 : TFT_BLUE) : TFT_RED); + this->spr.setFreeFont(&Roboto_Thin_24); + this->spr.setTextDatum(MC_DATUM); + sprintf(this->tmpStr3, "%01.00f", this->liveData->celsius2temperature(this->liveData->params.batTempC)); + this->spr.drawString(this->tmpStr3, 290, 60, GFXFF); + // Brake lights + this->spr.fillRect(210, 40, 40, 40, (this->liveData->params.brakeLights) ? TFT_RED : TFT_BLACK); + + // Soc%, bat.kWh + this->spr.setFreeFont(&Orbitron_Light_32); + this->spr.setTextColor(TFT_WHITE, TFT_BLACK); + this->spr.setTextDatum(TR_DATUM); + sprintf(this->tmpStr3, " %01.00f%%", this->liveData->params.socPerc); + this->spr.drawString(this->tmpStr3, 320, 94, GFXFF); + if (this->liveData->params.socPerc > 0) { + float capacity = this->liveData->params.batteryTotalAvailableKWh * (this->liveData->params.socPerc / 100); + // calibration for Niro/Kona, real available capacity is ~66.5kWh, 0-10% ~6.2kWh, 90-100% ~7.2kWh + if (this->liveData->settings.carType == CAR_KIA_ENIRO_2020_64 || this->liveData->settings.carType == CAR_HYUNDAI_KONA_2020_64) { + capacity = (this->liveData->params.socPerc * 0.615) * (1 + (this->liveData->params.socPerc * 0.0008)); + } + sprintf(this->tmpStr3, " %01.01f", capacity); + this->spr.drawString(this->tmpStr3, 320, 129, GFXFF); + this->spr.drawString("kWh", 320, 164, GFXFF); + } +} + +/** + Battery cells (Screen 3) +*/ +void Board320_240::drawSceneBatteryCells() { + + int32_t posx, posy; + + sprintf(this->tmpStr1, ((this->liveData->settings.temperatureUnit == 'c') ? "%01.00f C" : "%01.01f F"), this->liveData->celsius2temperature(this->liveData->params.batHeaterC)); + drawSmallCell(0, 0, 1, 1, this->tmpStr1, "HEATER", TFT_TEMP, TFT_CYAN); + sprintf(this->tmpStr1, ((this->liveData->settings.temperatureUnit == 'c') ? "%01.00f C" : "%01.01f F"), this->liveData->celsius2temperature(this->liveData->params.batInletC)); + drawSmallCell(1, 0, 1, 1, this->tmpStr1, "BAT.INLET", TFT_TEMP, TFT_CYAN); + sprintf(this->tmpStr1, ((this->liveData->settings.temperatureUnit == 'c') ? "%01.00f C" : "%01.01f F"), this->liveData->celsius2temperature(this->liveData->params.batModuleTempC[0])); + drawSmallCell(0, 1, 1, 1, this->tmpStr1, "MO1", TFT_TEMP, (this->liveData->params.batModuleTempC[0] >= 15) ? ((this->liveData->params.batModuleTempC[0] >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED); + sprintf(this->tmpStr1, ((this->liveData->settings.temperatureUnit == 'c') ? "%01.00f C" : "%01.01f F"), this->liveData->celsius2temperature(this->liveData->params.batModuleTempC[1])); + drawSmallCell(1, 1, 1, 1, this->tmpStr1, "MO2", TFT_TEMP, (this->liveData->params.batModuleTempC[1] >= 15) ? ((this->liveData->params.batModuleTempC[1] >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED); + sprintf(this->tmpStr1, ((this->liveData->settings.temperatureUnit == 'c') ? "%01.00f C" : "%01.01f F"), this->liveData->celsius2temperature(this->liveData->params.batModuleTempC[2])); + drawSmallCell(2, 1, 1, 1, this->tmpStr1, "MO3", TFT_TEMP, (this->liveData->params.batModuleTempC[2] >= 15) ? ((this->liveData->params.batModuleTempC[2] >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED); + sprintf(this->tmpStr1, ((this->liveData->settings.temperatureUnit == 'c') ? "%01.00f C" : "%01.01f F"), this->liveData->celsius2temperature(this->liveData->params.batModuleTempC[3])); + drawSmallCell(3, 1, 1, 1, this->tmpStr1, "MO4", TFT_TEMP, (this->liveData->params.batModuleTempC[3] >= 15) ? ((this->liveData->params.batModuleTempC[3] >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED); + // Ioniq (up to 12 cells) + for (uint16_t i = 4; i < this->liveData->params.batModuleTempCount; i++) { + if (this->liveData->params.batModuleTempC[i] == 0) + continue; + posx = (((i - 4) % 8) * 40); + posy = ((floor((i - 4) / 8)) * 13) + 64; + //this->spr.fillRect(x * 80, y * 32, ((w) * 80), ((h) * 32), bgColor); + this->spr.setTextSize(1); // Size for small 5x7 font + this->spr.setTextDatum(TL_DATUM); + this->spr.setTextColor(((this->liveData->params.batModuleTempC[i] >= 15) ? ((this->liveData->params.batModuleTempC[i] >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED), TFT_BLACK); + sprintf(this->tmpStr1, ((this->liveData->settings.temperatureUnit == 'c') ? "%01.00fC" : "%01.01fF"), this->liveData->celsius2temperature(this->liveData->params.batModuleTempC[i])); + this->spr.drawString(this->tmpStr1, posx + 4, posy, 2); + } + + this->spr.setTextDatum(TL_DATUM); // Topleft + this->spr.setTextSize(1); // Size for small 5x7 font + + // Find min and max val + float minVal = -1, maxVal = -1; + for (int i = 0; i < 98; i++) { + if ((this->liveData->params.cellVoltage[i] < minVal || minVal == -1) && this->liveData->params.cellVoltage[i] != -1) + minVal = this->liveData->params.cellVoltage[i]; + if ((this->liveData->params.cellVoltage[i] > maxVal || maxVal == -1) && this->liveData->params.cellVoltage[i] != -1) + maxVal = this->liveData->params.cellVoltage[i]; + if (this->liveData->params.cellVoltage[i] > 0 && i > this->liveData->params.cellCount + 1) + this->liveData->params.cellCount = i + 1; + } + + // Draw cell matrix + for (int i = 0; i < 98; i++) { + if (this->liveData->params.cellVoltage[i] == -1) + continue; + posx = ((i % 8) * 40) + 4; + posy = ((floor(i / 8) + (this->liveData->params.cellCount > 96 ? 0 : 1)) * 13) + 68; + sprintf(this->tmpStr3, "%01.02f", this->liveData->params.cellVoltage[i]); + this->spr.setTextColor(TFT_NAVY, TFT_BLACK); + if (this->liveData->params.cellVoltage[i] == minVal && minVal != maxVal) + this->spr.setTextColor(TFT_RED, TFT_BLACK); + if (this->liveData->params.cellVoltage[i] == maxVal && minVal != maxVal) + this->spr.setTextColor(TFT_GREEN, TFT_BLACK); + this->spr.drawString(this->tmpStr3, posx, posy, 2); + } +} + +/** + drawPreDrawnChargingGraphs + P = U.I +*/ +void Board320_240::drawPreDrawnChargingGraphs(int zeroX, int zeroY, int mulX, int mulY) { + + // Rapid gate + this->spr.drawLine(zeroX + (/* SOC FROM */ 1 * mulX), zeroY - (/*I*/ 180 * /*U SOC*/ (1 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 57 * mulX), zeroY - (/*I*/ 180 * /*U SOC*/ (57 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_RAPIDGATE35); + // Coldgate <5C + this->spr.drawLine(zeroX + (/* SOC FROM */ 1 * mulX), zeroY - (/*I*/ 60 * /*U SOC*/ (1 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 65 * mulX), zeroY - (/*I*/ 60 * /*U SOC*/ (65 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_COLDGATE0_5); + // Coldgate 5-14C + this->spr.drawLine(zeroX + (/* SOC FROM */ 1 * mulX), zeroY - (/*I*/ 110 * /*U SOC*/ (1 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 57 * mulX), zeroY - (/*I*/ 110 * /*U SOC*/ (57 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_COLDGATE5_14); + this->spr.drawLine(zeroX + (/* SOC FROM */ 57 * mulX), zeroY - (/*I*/ 110 * /*U SOC*/ (57 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 58 * mulX), zeroY - (/*I*/ 75 * /*U SOC*/ (58 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_COLDGATE5_14); + this->spr.drawLine(zeroX + (/* SOC FROM */ 58 * mulX), zeroY - (/*I*/ 75 * /*U SOC*/ (58 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 64 * mulX), zeroY - (/*I*/ 75 * /*U SOC*/ (64 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_COLDGATE5_14); + this->spr.drawLine(zeroX + (/* SOC FROM */ 64 * mulX), zeroY - (/*I*/ 75 * /*U SOC*/ (64 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 65 * mulX), zeroY - (/*I*/ 60 * /*U SOC*/ (65 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_COLDGATE5_14); + this->spr.drawLine(zeroX + (/* SOC FROM */ 65 * mulX), zeroY - (/*I*/ 60 * /*U SOC*/ (65 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 82 * mulX), zeroY - (/*I*/ 60 * /*U SOC*/ (82 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_COLDGATE5_14); + this->spr.drawLine(zeroX + (/* SOC FROM */ 82 * mulX), zeroY - (/*I*/ 60 * /*U SOC*/ (82 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 83 * mulX), zeroY - (/*I*/ 40 * /*U SOC*/ (83 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_COLDGATE5_14); + // Coldgate 15-24C + this->spr.drawLine(zeroX + (/* SOC FROM */ 1 * mulX), zeroY - (/*I*/ 150 * /*U SOC*/ (1 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 57 * mulX), zeroY - (/*I*/ 150 * /*U SOC*/ (57 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_COLDGATE15_24); + this->spr.drawLine(zeroX + (/* SOC FROM */ 57 * mulX), zeroY - (/*I*/ 150 * /*U SOC*/ (57 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC FROM */ 58 * mulX), zeroY - (/*I*/ 110 * /*U SOC*/ (58 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_COLDGATE15_24); + this->spr.drawLine(zeroX + (/* SOC TO */ 58 * mulX), zeroY - (/*I*/ 110 * /*U SOC*/ (58 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 78 * mulX), zeroY - (/*I*/ 110 * /*U SOC*/ (78 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_COLDGATE15_24); + // Optimal + this->spr.drawLine(zeroX + (/* SOC FROM */ 1 * mulX), zeroY - (/*I*/ 200 * /*U SOC*/ (1 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 57 * mulX), zeroY - (/*I*/ 200 * /*U SOC*/ (57 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); + this->spr.drawLine(zeroX + (/* SOC FROM */ 51 * mulX), zeroY - (/*I*/ 200 * /*U SOC*/ (51 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 51 * mulX), zeroY - (/*I*/ 195 * /*U SOC*/ (51 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); + this->spr.drawLine(zeroX + (/* SOC FROM */ 53 * mulX), zeroY - (/*I*/ 200 * /*U SOC*/ (53 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 53 * mulX), zeroY - (/*I*/ 195 * /*U SOC*/ (53 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); + this->spr.drawLine(zeroX + (/* SOC FROM */ 55 * mulX), zeroY - (/*I*/ 200 * /*U SOC*/ (55 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 55 * mulX), zeroY - (/*I*/ 195 * /*U SOC*/ (55 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); + this->spr.drawLine(zeroX + (/* SOC FROM */ 57 * mulX), zeroY - (/*I*/ 200 * /*U SOC*/ (57 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 58 * mulX), zeroY - (/*I*/ 150 * /*U SOC*/ (58 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); + this->spr.drawLine(zeroX + (/* SOC FROM */ 58 * mulX), zeroY - (/*I*/ 150 * /*U SOC*/ (58 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 77 * mulX), zeroY - (/*I*/ 150 * /*U SOC*/ (77 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); + this->spr.drawLine(zeroX + (/* SOC FROM */ 71 * mulX), zeroY - (/*I*/ 150 * /*U SOC*/ (71 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 71 * mulX), zeroY - (/*I*/ 145 * /*U SOC*/ (71 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); + this->spr.drawLine(zeroX + (/* SOC FROM */ 73 * mulX), zeroY - (/*I*/ 150 * /*U SOC*/ (73 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 73 * mulX), zeroY - (/*I*/ 145 * /*U SOC*/ (73 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); + this->spr.drawLine(zeroX + (/* SOC FROM */ 75 * mulX), zeroY - (/*I*/ 150 * /*U SOC*/ (75 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 75 * mulX), zeroY - (/*I*/ 145 * /*U SOC*/ (75 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); + this->spr.drawLine(zeroX + (/* SOC FROM */ 77 * mulX), zeroY - (/*I*/ 150 * /*U SOC*/ (77 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 78 * mulX), zeroY - (/*I*/ 90 * /*U SOC*/ (78 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); + this->spr.drawLine(zeroX + (/* SOC FROM */ 78 * mulX), zeroY - (/*I*/ 90 * /*U SOC*/ (78 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 82 * mulX), zeroY - (/*I*/ 90 * /*U SOC*/ (82 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); + this->spr.drawLine(zeroX + (/* SOC FROM */ 82 * mulX), zeroY - (/*I*/ 90 * /*U SOC*/ (82 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 83 * mulX), zeroY - (/*I*/ 60 * /*U SOC*/ (83 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); + this->spr.drawLine(zeroX + (/* SOC FROM */ 83 * mulX), zeroY - (/*I*/ 60 * /*U SOC*/ (83 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 92 * mulX), zeroY - (/*I*/ 60 * /*U SOC*/ (92 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); + this->spr.drawLine(zeroX + (/* SOC FROM */ 92 * mulX), zeroY - (/*I*/ 60 * /*U SOC*/ (92 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 95 * mulX), zeroY - (/*I*/ 35 * /*U SOC*/ (95 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); + this->spr.drawLine(zeroX + (/* SOC FROM */ 95 * mulX), zeroY - (/*I*/ 35 * /*U SOC*/ (95 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 98 * mulX), zeroY - (/*I*/ 35 * /*U SOC*/ (98 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); + this->spr.drawLine(zeroX + (/* SOC FROM */ 98 * mulX), zeroY - (/*I*/ 35 * /*U SOC*/ (98 * 55 / 100 + 352) /**/ / 1000 * mulY), + zeroX + (/* SOC TO */ 100 * mulX), zeroY - (/*I*/ 15 * /*U SOC*/ (100 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); + // Triangles + int x = zeroX; + int y; + if (this->liveData->params.batMaxC >= 35) { + y = zeroY - (/*I*/ 180 * /*U SOC*/ (1 * 55 / 100 + 352) /**/ / 1000 * mulY); + } else if (this->liveData->params.batMinC >= 25) { + y = zeroY - (/*I*/ 200 * /*U SOC*/ (1 * 55 / 100 + 352) /**/ / 1000 * mulY); + } else if (this->liveData->params.batMinC >= 15) { + y = zeroY - (/*I*/ 150 * /*U SOC*/ (1 * 55 / 100 + 352) /**/ / 1000 * mulY); + } else if (this->liveData->params.batMinC >= 5) { + y = zeroY - (/*I*/ 110 * /*U SOC*/ (1 * 55 / 100 + 352) /**/ / 1000 * mulY); + } else { + y = zeroY - (/*I*/ 60 * /*U SOC*/ (1 * 55 / 100 + 352) /**/ / 1000 * mulY); + } + this->spr.fillTriangle(x + 5, y, x , y - 5, x, y + 5, TFT_ORANGE); +} + +/** + Charging graph (Screen 4) +*/ +void Board320_240::drawSceneChargingGraph() { + + int zeroX = 0; + int zeroY = 238; + int mulX = 3; // 100% = 300px; + int mulY = 2; // 100kW = 200px + int maxKw = 80; + int posy = 0; + uint16_t color; + + this->spr.fillSprite(TFT_BLACK); + + sprintf(this->tmpStr1, "%01.00f", this->liveData->params.socPerc); + drawSmallCell(0, 0, 1, 1, this->tmpStr1, "SOC", TFT_TEMP, TFT_CYAN); + sprintf(this->tmpStr1, "%01.01f", this->liveData->params.batPowerKw); + drawSmallCell(1, 0, 1, 1, this->tmpStr1, "POWER kW", TFT_TEMP, TFT_CYAN); + sprintf(this->tmpStr1, "%01.01f", this->liveData->params.batPowerAmp); + drawSmallCell(2, 0, 1, 1, this->tmpStr1, "CURRENT A", TFT_TEMP, TFT_CYAN); + sprintf(this->tmpStr1, "%03.00f", this->liveData->params.batVoltage); + drawSmallCell(3, 0, 1, 1, this->tmpStr1, "VOLTAGE", TFT_TEMP, TFT_CYAN); + + sprintf(this->tmpStr1, ((this->liveData->settings.temperatureUnit == 'c') ? "%01.00f C" : "%01.01f F"), this->liveData->celsius2temperature(this->liveData->params.batHeaterC)); + drawSmallCell(0, 1, 1, 1, this->tmpStr1, "HEATER", TFT_TEMP, TFT_RED); + sprintf(this->tmpStr1, ((this->liveData->settings.temperatureUnit == 'c') ? "%01.00f C" : "%01.01f F"), this->liveData->celsius2temperature(this->liveData->params.batInletC)); + drawSmallCell(1, 1, 1, 1, this->tmpStr1, "BAT.INLET", TFT_TEMP, TFT_CYAN); + sprintf(this->tmpStr1, ((this->liveData->settings.temperatureUnit == 'c') ? "%01.00f C" : "%01.01f F"), this->liveData->celsius2temperature(this->liveData->params.batMinC)); + drawSmallCell(2, 1, 1, 1, this->tmpStr1, "BAT.MIN", (this->liveData->params.batMinC >= 15) ? ((this->liveData->params.batMinC >= 25) ? TFT_DARKGREEN2 : TFT_BLUE) : TFT_RED, TFT_CYAN); + sprintf(this->tmpStr1, ((this->liveData->settings.temperatureUnit == 'c') ? "%01.00f C" : "%01.01f F"), this->liveData->celsius2temperature(this->liveData->params.outdoorTemperature)); + drawSmallCell(3, 1, 1, 1, this->tmpStr1, "OUT.TEMP.", TFT_TEMP, TFT_CYAN); + + this->spr.setTextColor(TFT_SILVER, TFT_TEMP); + + for (int i = 0; i <= 10; i++) { + color = TFT_DARKRED2; + if (i == 0 || i == 5 || i == 10) + color = TFT_DARKRED; + this->spr.drawFastVLine(zeroX + (i * 10 * mulX), zeroY - (maxKw * mulY), maxKw * mulY, color); + /*if (i != 0 && i != 10) { + sprintf(this->tmpStr1, "%d%%", i * 10); + this->spr.setTextDatum(BC_DATUM); + this->spr.drawString(this->tmpStr1, zeroX + (i * 10 * mulX), zeroY - (maxKw * mulY), 2); + }*/ + if (i <= (maxKw / 10)) { + this->spr.drawFastHLine(zeroX, zeroY - (i * 10 * mulY), 100 * mulX, color); + if (i > 0) { + sprintf(this->tmpStr1, "%d", i * 10); + this->spr.setTextDatum(ML_DATUM); + this->spr.drawString(this->tmpStr1, zeroX + (100 * mulX) + 3, zeroY - (i * 10 * mulY), 2); + } + } + } + + // Draw suggested curves + if (this->liveData->settings.predrawnChargingGraphs == 1) { + drawPreDrawnChargingGraphs(zeroX, zeroY, mulX, mulY); + } + + // Draw realtime values + for (int i = 0; i <= 100; i++) { + if (this->liveData->params.chargingGraphBatMinTempC[i] > -10) + this->spr.drawFastHLine(zeroX + (i * mulX) - (mulX / 2), zeroY - (this->liveData->params.chargingGraphBatMinTempC[i]*mulY), mulX, TFT_BLUE); + if (this->liveData->params.chargingGraphBatMaxTempC[i] > -10) + this->spr.drawFastHLine(zeroX + (i * mulX) - (mulX / 2), zeroY - (this->liveData->params.chargingGraphBatMaxTempC[i]*mulY), mulX, TFT_BLUE); + if (this->liveData->params.chargingGraphWaterCoolantTempC[i] > -10) + this->spr.drawFastHLine(zeroX + (i * mulX) - (mulX / 2), zeroY - (this->liveData->params.chargingGraphWaterCoolantTempC[i]*mulY), mulX, TFT_PURPLE); + if (this->liveData->params.chargingGraphHeaterTempC[i] > -10) + this->spr.drawFastHLine(zeroX + (i * mulX) - (mulX / 2), zeroY - (this->liveData->params.chargingGraphHeaterTempC[i]*mulY), mulX, TFT_RED); + + if (this->liveData->params.chargingGraphMinKw[i] > 0) + this->spr.drawFastHLine(zeroX + (i * mulX) - (mulX / 2), zeroY - (this->liveData->params.chargingGraphMinKw[i]*mulY), mulX, TFT_GREENYELLOW); + if (this->liveData->params.chargingGraphMaxKw[i] > 0) + this->spr.drawFastHLine(zeroX + (i * mulX) - (mulX / 2), zeroY - (this->liveData->params.chargingGraphMaxKw[i]*mulY), mulX, TFT_YELLOW); + } + + // Bat.module temperatures + this->spr.setTextSize(1); // Size for small 5x7 font + this->spr.setTextDatum(BL_DATUM); + sprintf(this->tmpStr1, ((this->liveData->settings.temperatureUnit == 'c') ? "1=%01.00fC " : "1=%01.00fF "), this->liveData->celsius2temperature(this->liveData->params.batModuleTempC[0])); + this->spr.setTextColor((this->liveData->params.batModuleTempC[0] >= 15) ? ((this->liveData->params.batModuleTempC[0] >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED, TFT_TEMP); + this->spr.drawString(this->tmpStr1, 0, zeroY - (maxKw * mulY), 2); + + sprintf(this->tmpStr1, ((this->liveData->settings.temperatureUnit == 'c') ? "2=%01.00fC " : "2=%01.00fF "), this->liveData->celsius2temperature(this->liveData->params.batModuleTempC[1])); + this->spr.setTextColor((this->liveData->params.batModuleTempC[1] >= 15) ? ((this->liveData->params.batModuleTempC[1] >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED, TFT_TEMP); + this->spr.drawString(this->tmpStr1, 48, zeroY - (maxKw * mulY), 2); + + sprintf(this->tmpStr1, ((this->liveData->settings.temperatureUnit == 'c') ? "3=%01.00fC " : "3=%01.00fF "), this->liveData->celsius2temperature(this->liveData->params.batModuleTempC[2])); + this->spr.setTextColor((this->liveData->params.batModuleTempC[2] >= 15) ? ((this->liveData->params.batModuleTempC[2] >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED, TFT_TEMP); + this->spr.drawString(this->tmpStr1, 96, zeroY - (maxKw * mulY), 2); + + sprintf(this->tmpStr1, ((this->liveData->settings.temperatureUnit == 'c') ? "4=%01.00fC " : "4=%01.00fF "), this->liveData->celsius2temperature(this->liveData->params.batModuleTempC[3])); + this->spr.setTextColor((this->liveData->params.batModuleTempC[3] >= 15) ? ((this->liveData->params.batModuleTempC[3] >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED, TFT_TEMP); + this->spr.drawString(this->tmpStr1, 144, zeroY - (maxKw * mulY), 2); + sprintf(this->tmpStr1, "ir %01.00fkOhm", this->liveData->params.isolationResistanceKOhm ); + + // Bms max.regen/power available + this->spr.setTextColor(TFT_WHITE, TFT_BLACK); + sprintf(this->tmpStr1, "xC=%01.00fkW ", this->liveData->params.availableChargePower); + this->spr.drawString(this->tmpStr1, 192, zeroY - (maxKw * mulY), 2); + this->spr.setTextColor(TFT_WHITE, TFT_BLACK); + sprintf(this->tmpStr1, "xD=%01.00fkW", this->liveData->params.availableDischargePower); + this->spr.drawString(this->tmpStr1, 256, zeroY - (maxKw * mulY), 2); + + // + this->spr.setTextDatum(TR_DATUM); + if (this->liveData->params.coolingWaterTempC != -1) { + sprintf(this->tmpStr1, ((this->liveData->settings.temperatureUnit == 'c') ? "W=%01.00fC" : "W=%01.00fF"), this->liveData->celsius2temperature(this->liveData->params.coolingWaterTempC)); + this->spr.setTextColor(TFT_PURPLE, TFT_TEMP); + this->spr.drawString(this->tmpStr1, zeroX + (10 * 10 * mulX), zeroY - (maxKw * mulY) + (posy * 15), 2); + posy++; + } + this->spr.setTextColor(TFT_WHITE, TFT_TEMP); + if (this->liveData->params.batFanFeedbackHz > 0) { + sprintf(this->tmpStr1, "FF=%03.00fHz", this->liveData->params.batFanFeedbackHz); + this->spr.drawString(this->tmpStr1, zeroX + (10 * 10 * mulX), zeroY - (maxKw * mulY) + (posy * 15), 2); + posy++; + } + if (this->liveData->params.batFanStatus > 0) { + sprintf(this->tmpStr1, "FS=%03.00f", this->liveData->params.batFanStatus); + this->spr.drawString(this->tmpStr1, zeroX + (10 * 10 * mulX), zeroY - (maxKw * mulY) + (posy * 15), 2); + posy++; + } + if (this->liveData->params.coolantTemp1C != -1 && this->liveData->params.coolantTemp2C != -1) { + sprintf(this->tmpStr1, ((this->liveData->settings.temperatureUnit == 'c') ? "C1/2:%01.00f/%01.00fC" : "C1/2:%01.00f/%01.00fF"), this->liveData->celsius2temperature(this->liveData->params.coolantTemp1C), this->liveData->celsius2temperature(this->liveData->params.coolantTemp2C)); + this->spr.drawString(this->tmpStr1, zeroX + (10 * 10 * mulX), zeroY - (maxKw * mulY) + (posy * 15), 2); + posy++; + } + if (this->liveData->params.bmsUnknownTempA != -1) { + sprintf(this->tmpStr1, ((this->liveData->settings.temperatureUnit == 'c') ? "A=%01.00fC" : "W=%01.00fF"), this->liveData->celsius2temperature(this->liveData->params.bmsUnknownTempA)); + this->spr.drawString(this->tmpStr1, zeroX + (10 * 10 * mulX), zeroY - (maxKw * mulY) + (posy * 15), 2); + posy++; + } + if (this->liveData->params.bmsUnknownTempB != -1) { + sprintf(this->tmpStr1, ((this->liveData->settings.temperatureUnit == 'c') ? "B=%01.00fC" : "W=%01.00fF"), this->liveData->celsius2temperature(this->liveData->params.bmsUnknownTempB)); + this->spr.drawString(this->tmpStr1, zeroX + (10 * 10 * mulX), zeroY - (maxKw * mulY) + (posy * 15), 2); + posy++; + } + if (this->liveData->params.bmsUnknownTempC != -1) { + sprintf(this->tmpStr1, ((this->liveData->settings.temperatureUnit == 'c') ? "C=%01.00fC" : "W=%01.00fF"), this->liveData->celsius2temperature(this->liveData->params.bmsUnknownTempC)); + this->spr.drawString(this->tmpStr1, zeroX + (10 * 10 * mulX), zeroY - (maxKw * mulY) + (posy * 15), 2); + posy++; + } + if (this->liveData->params.bmsUnknownTempD != -1) { + sprintf(this->tmpStr1, ((this->liveData->settings.temperatureUnit == 'c') ? "D=%01.00fC" : "W=%01.00fF"), this->liveData->celsius2temperature(this->liveData->params.bmsUnknownTempD)); + this->spr.drawString(this->tmpStr1, zeroX + (10 * 10 * mulX), zeroY - (maxKw * mulY) + (posy * 15), 2); + posy++; + } + + // Print charging time + time_t diffTime = this->liveData->params.currentTime - this->liveData->params.chargingStartTime; + if ((diffTime / 60) > 99) + sprintf(this->tmpStr1, "%02d:%02d:%02d", (diffTime / 3600) % 24, (diffTime / 60) % 60, diffTime % 60); + else + sprintf(this->tmpStr1, "%02d:%02d", (diffTime / 60), diffTime % 60); + this->spr.setTextDatum(TL_DATUM); + this->spr.setTextColor(TFT_SILVER, TFT_BLACK); + this->spr.drawString(this->tmpStr1, 0, zeroY - (maxKw * mulY), 2); +} + +/** + SOC 10% table (screen 5) +*/ +void Board320_240::drawSceneSoc10Table() { + + int zeroY = 2; + float diffCec, diffCed, diffOdo = -1; + float firstCed = -1, lastCed = -1, diffCed0to5 = 0; + float firstCec = -1, lastCec = -1, diffCec0to5 = 0; + float firstOdo = -1, lastOdo = -1, diffOdo0to5 = 0; + float diffTime; + + this->spr.setTextSize(1); // Size for small 5x7 font + this->spr.setTextColor(TFT_SILVER, TFT_TEMP); + this->spr.setTextDatum(TL_DATUM); + this->spr.drawString("CONSUMPTION | DISCH.100%->4% SOC", 2, zeroY, 2); + + this->spr.setTextDatum(TR_DATUM); + + this->spr.drawString("dis./char.kWh", 128, zeroY + (1 * 15), 2); + this->spr.drawString(((this->liveData->settings.distanceUnit == 'k') ? "km" : "mi"), 160, zeroY + (1 * 15), 2); + this->spr.drawString("kWh100", 224, zeroY + (1 * 15), 2); + this->spr.drawString("avg.speed", 310, zeroY + (1 * 15), 2); + + for (int i = 0; i <= 10; i++) { + sprintf(this->tmpStr1, "%d%%", (i == 0) ? 5 : i * 10); + this->spr.drawString(this->tmpStr1, 32, zeroY + ((12 - i) * 15), 2); + + firstCed = (this->liveData->params.soc10ced[i] != -1) ? this->liveData->params.soc10ced[i] : firstCed; + lastCed = (lastCed == -1 && this->liveData->params.soc10ced[i] != -1) ? this->liveData->params.soc10ced[i] : lastCed; + firstCec = (this->liveData->params.soc10cec[i] != -1) ? this->liveData->params.soc10cec[i] : firstCec; + lastCec = (lastCec == -1 && this->liveData->params.soc10cec[i] != -1) ? this->liveData->params.soc10cec[i] : lastCec; + firstOdo = (this->liveData->params.soc10odo[i] != -1) ? this->liveData->params.soc10odo[i] : firstOdo; + lastOdo = (lastOdo == -1 && this->liveData->params.soc10odo[i] != -1) ? this->liveData->params.soc10odo[i] : lastOdo; + + if (i != 10) { + diffCec = (this->liveData->params.soc10cec[i + 1] != -1 && this->liveData->params.soc10cec[i] != -1) ? (this->liveData->params.soc10cec[i] - this->liveData->params.soc10cec[i + 1]) : 0; + diffCed = (this->liveData->params.soc10ced[i + 1] != -1 && this->liveData->params.soc10ced[i] != -1) ? (this->liveData->params.soc10ced[i + 1] - this->liveData->params.soc10ced[i]) : 0; + diffOdo = (this->liveData->params.soc10odo[i + 1] != -1 && this->liveData->params.soc10odo[i] != -1) ? (this->liveData->params.soc10odo[i] - this->liveData->params.soc10odo[i + 1]) : -1; + diffTime = (this->liveData->params.soc10time[i + 1] != -1 && this->liveData->params.soc10time[i] != -1) ? (this->liveData->params.soc10time[i] - this->liveData->params.soc10time[i + 1]) : -1; + if (diffCec != 0) { + sprintf(this->tmpStr1, "+%01.01f", diffCec); + this->spr.drawString(this->tmpStr1, 128, zeroY + ((12 - i) * 15), 2); + diffCec0to5 = (i == 0) ? diffCec : diffCec0to5; + } + if (diffCed != 0) { + sprintf(this->tmpStr1, "%01.01f", diffCed); + this->spr.drawString(this->tmpStr1, 80, zeroY + ((12 - i) * 15), 2); + diffCed0to5 = (i == 0) ? diffCed : diffCed0to5; + } + if (diffOdo != -1) { + sprintf(this->tmpStr1, "%01.00f", this->liveData->km2distance(diffOdo)); + this->spr.drawString(this->tmpStr1, 160, zeroY + ((12 - i) * 15), 2); + diffOdo0to5 = (i == 0) ? diffOdo : diffOdo0to5; + if (diffTime > 0) { + sprintf(this->tmpStr1, "%01.01f", this->liveData->km2distance(diffOdo) / (diffTime / 3600)); + this->spr.drawString(this->tmpStr1, 310, zeroY + ((12 - i) * 15), 2); + } + } + if (diffOdo > 0 && diffCed != 0) { + sprintf(this->tmpStr1, "%01.1f", (-diffCed * 100.0 / this->liveData->km2distance(diffOdo))); + this->spr.drawString(this->tmpStr1, 224, zeroY + ((12 - i) * 15), 2); + } + } + + if (diffOdo == -1 && this->liveData->params.soc10odo[i] != -1) { + sprintf(this->tmpStr1, "%01.00f", this->liveData->km2distance(this->liveData->params.soc10odo[i])); + this->spr.drawString(this->tmpStr1, 160, zeroY + ((12 - i) * 15), 2); + } + } + + this->spr.drawString("0%", 32, zeroY + (13 * 15), 2); + this->spr.drawString("0-5% is calculated (same) as 5-10%", 310, zeroY + (13 * 15), 2); + + this->spr.drawString("TOT.", 32, zeroY + (14 * 15), 2); + diffCed = (lastCed != -1 && firstCed != -1) ? firstCed - lastCed + diffCed0to5 : 0; + sprintf(this->tmpStr1, "%01.01f", diffCed); + this->spr.drawString(this->tmpStr1, 80, zeroY + (14 * 15), 2); + diffCec = (lastCec != -1 && firstCec != -1) ? lastCec - firstCec + diffCec0to5 : 0; + sprintf(this->tmpStr1, "+%01.01f", diffCec); + this->spr.drawString(this->tmpStr1, 128, zeroY + (14 * 15), 2); + diffOdo = (lastOdo != -1 && firstOdo != -1) ? lastOdo - firstOdo + diffOdo0to5 : 0; + sprintf(this->tmpStr1, "%01.00f", this->liveData->km2distance(diffOdo)); + this->spr.drawString(this->tmpStr1, 160, zeroY + (14 * 15), 2); + sprintf(this->tmpStr1, "AVAIL.CAP: %01.01f kWh", -diffCed - diffCec); + this->spr.drawString(this->tmpStr1, 310, zeroY + (14 * 15), 2); +} + +/** + DEBUG screen +*/ +void Board320_240::drawSceneDebug() { + + int32_t posx, posy; + String chHex, chHex2; + uint8_t chByte; + + this->spr.setTextSize(1); // Size for small 5x7 font + this->spr.setTextColor(TFT_SILVER, TFT_TEMP); + this->spr.setTextDatum(TL_DATUM); + this->spr.drawString(debugAtshRequest, 0, 0, 2); + this->spr.drawString(debugCommandRequest, 128, 0, 2); + this->spr.drawString(this->liveData->commandRequest, 256, 0, 2); + this->spr.setTextDatum(TR_DATUM); + + for (int i = 0; i < debugLastString.length() / 2; i++) { + chHex = debugLastString.substring(i * 2, (i * 2) + 2); + chHex2 = debugPreviousString.substring(i * 2, (i * 2) + 2); + this->spr.setTextColor(((chHex.equals(chHex2)) ? TFT_SILVER : TFT_GREEN), TFT_TEMP); + chByte = this->liveData->hexToDec(chHex.c_str(), 1, false); + posx = (((i) % 10) * 32) + 24; + posy = ((floor((i) / 10)) * 32) + 24; + sprintf(this->tmpStr1, "%03d", chByte); + this->spr.drawString(this->tmpStr1, posx + 4, posy, 2); + + this->spr.setTextColor(TFT_YELLOW, TFT_TEMP); + sprintf(this->tmpStr1, "%c", (char)chByte); + this->spr.drawString(this->tmpStr1, posx + 4, posy + 13, 2); + } + + debugPreviousString = debugLastString; +} + +/** + Modify caption +*/ +String Board320_240::menuItemCaption(int16_t menuItemId, String title) { + + String prefix = "", suffix = ""; + + if (menuItemId == 10) // Version + suffix = APP_VERSION; + + if (menuItemId == 401) // distance + suffix = (this->liveData->settings.distanceUnit == 'k') ? "[km]" : "[mi]"; + if (menuItemId == 402) // temperature + suffix = (this->liveData->settings.temperatureUnit == 'c') ? "[C]" : "[F]"; + if (menuItemId == 403) // pressure + suffix = (this->liveData->settings.pressureUnit == 'b') ? "[bar]" : "[psi]"; + + title = ((prefix == "") ? "" : prefix + " ") + title + ((suffix == "") ? "" : " " + suffix); + + return title; +} + +/** + Display menu +*/ +void Board320_240::showMenu() { + + uint16_t posY = 0, tmpCurrMenuItem = 0; + + this->liveData->menuVisible = true; +Serial.println("A"); + this->spr.fillSprite(TFT_BLACK); + this->spr.setTextDatum(TL_DATUM); + this->spr.setFreeFont(&Roboto_Thin_24); + +Serial.println("B"); + // Page scroll + uint8_t visibleCount = (int)(this->tft.height() / this->spr.fontHeight()); + if (this->liveData->menuItemSelected >= this->liveData->menuItemOffset + visibleCount) + this->liveData->menuItemOffset = this->liveData->menuItemSelected - visibleCount + 1; + if (this->liveData->menuItemSelected < this->liveData->menuItemOffset) + this->liveData->menuItemOffset = this->liveData->menuItemSelected; +Serial.println("C"); + + // Print items + for (uint16_t i = 0; i < this->liveData->menuItemsCount; ++i) { + if (this->liveData->menuCurrent == this->liveData->menuItems[i].parentId) { +Serial.println("D"); + if (tmpCurrMenuItem >= this->liveData->menuItemOffset) { + this->spr.fillRect(0, posY, 320, this->spr.fontHeight() + 2, (this->liveData->menuItemSelected == tmpCurrMenuItem) ? TFT_DARKGREEN2 : TFT_BLACK); + this->spr.setTextColor((this->liveData->menuItemSelected == tmpCurrMenuItem) ? TFT_WHITE : TFT_WHITE, (this->liveData->menuItemSelected == tmpCurrMenuItem) ? TFT_DARKGREEN2 : TFT_BLACK); + this->spr.drawString(this->menuItemCaption(this->liveData->menuItems[i].id, this->liveData->menuItems[i].title), 0, posY + 2, GFXFF); + posY += this->spr.fontHeight(); + } + tmpCurrMenuItem++; + } + } + + this->spr.pushSprite(0, 0); +} + +/** + Hide menu +*/ +void Board320_240::hideMenu() { + + this->liveData->menuVisible = false; + this->liveData->menuCurrent = 0; + this->liveData->menuItemSelected = 0; + this->redrawScreen(); +} + +/** + Move in menu with left/right button +*/ +void Board320_240::menuMove(bool forward) { + + if (forward) { + uint16_t tmpCount = 0; + for (uint16_t i = 0; i < this->liveData->menuItemsCount; ++i) { + if (this->liveData->menuCurrent == this->liveData->menuItems[i].parentId) { + tmpCount++; + } + } + this->liveData->menuItemSelected = (this->liveData->menuItemSelected >= tmpCount - 1 ) ? tmpCount - 1 : this->liveData->menuItemSelected + 1; + } else { + this->liveData->menuItemSelected = (this->liveData->menuItemSelected <= 0) ? 0 : this->liveData->menuItemSelected - 1; + } + this->showMenu(); +} + +/** + Enter menu item +*/ +void Board320_240::menuItemClick() { + + // Locate menu item for meta data + MENU_ITEM tmpMenuItem; + uint16_t tmpCurrMenuItem = 0; + for (uint16_t i = 0; i < this->liveData->menuItemsCount; ++i) { + if (this->liveData->menuCurrent == this->liveData->menuItems[i].parentId) { + if (this->liveData->menuItemSelected == tmpCurrMenuItem) { + tmpMenuItem = this->liveData->menuItems[i]; + break; + } + tmpCurrMenuItem++; + } + } + + // Exit menu, parent level menu, open item + if (this->liveData->menuItemSelected == 0) { + // Exit menu + if (tmpMenuItem.parentId == 0 && tmpMenuItem.id == 0) { + this->liveData->menuVisible = false; + this->redrawScreen(); + } else { + // Parent menu + this->liveData->menuCurrent = tmpMenuItem.targetParentId; + this->showMenu(); + } + return; + } else { + Serial.println(tmpMenuItem.id); + // Device list + if (tmpMenuItem.id > 10000 && tmpMenuItem.id < 10100) { + strlcpy((char*)this->liveData->settings.obdMacAddress, (char*)tmpMenuItem.obdMacAddress, 20); + Serial.print("Selected adapter MAC address "); + Serial.println(this->liveData->settings.obdMacAddress); + this->saveSettings(); + ESP.restart(); + } + // Other menus + switch (tmpMenuItem.id) { + // Set vehicle type + case 101: this->liveData->settings.carType = CAR_KIA_ENIRO_2020_64; break; + case 102: this->liveData->settings.carType = CAR_HYUNDAI_KONA_2020_64; break; + case 103: this->liveData->settings.carType = CAR_HYUNDAI_IONIQ_2018; break; + case 104: this->liveData->settings.carType = CAR_KIA_ENIRO_2020_39; break; + case 105: this->liveData->settings.carType = CAR_HYUNDAI_KONA_2020_39; break; + case 107: this->liveData->settings.carType = CAR_DEBUG_OBD2_KIA; break; + // Screen orientation + case 3011: this->liveData->settings.displayRotation = 1; this->tft.setRotation(this->liveData->settings.displayRotation); break; + case 3012: this->liveData->settings.displayRotation = 3; this->tft.setRotation(this->liveData->settings.displayRotation); break; + // Default screen + case 3021: this->liveData->settings.defaultScreen = 1; break; + case 3022: this->liveData->settings.defaultScreen = 2; break; + case 3023: this->liveData->settings.defaultScreen = 3; break; + case 3024: this->liveData->settings.defaultScreen = 4; break; + case 3025: this->liveData->settings.defaultScreen = 5; break; + // Debug screen off/on + case 3031: this->liveData->settings.debugScreen = 0; break; + case 3032: this->liveData->settings.debugScreen = 1; break; + // Lcd brightness + case 3041: this->liveData->settings.lcdBrightness = 0; break; + case 3042: this->liveData->settings.lcdBrightness = 20; break; + case 3043: this->liveData->settings.lcdBrightness = 50; break; + case 3044: this->liveData->settings.lcdBrightness = 100; break; + // Pre-drawn charg.graphs off/on + case 3051: this->liveData->settings.predrawnChargingGraphs = 0; break; + case 3052: this->liveData->settings.predrawnChargingGraphs = 1; break; + // Distance + case 4011: this->liveData->settings.distanceUnit = 'k'; break; + case 4012: this->liveData->settings.distanceUnit = 'm'; break; + // Temperature + case 4021: this->liveData->settings.temperatureUnit = 'c'; break; + case 4022: this->liveData->settings.temperatureUnit = 'f'; break; + // Pressure + case 4031: this->liveData->settings.pressureUnit = 'b'; break; + case 4032: this->liveData->settings.pressureUnit = 'p'; break; + // Pair ble device + case 2: scanDevices = true; /*startBleScan(); */return; + // Reset settings + case 8: this->resetSettings(); hideMenu(); return; + // Save settings + case 9: this->saveSettings(); break; + // Version + case 10: hideMenu(); return; + // Shutdown + case 11: this->shutdownDevice(); return; + default: + // Submenu + this->liveData->menuCurrent = tmpMenuItem.id; + this->liveData->menuItemSelected = 0; + this->showMenu(); + return; + } + + // close menu + this->hideMenu(); + } +} + +/** + Redraw screen +*/ +void Board320_240::redrawScreen() { + + if (this->liveData->menuVisible) { + return; + } + + // Lights not enabled + if (!this->testDataMode && this->liveData->params.forwardDriveMode && !this->liveData->params.headLights && !this->liveData->params.dayLights) { + this->spr.fillSprite(TFT_RED); + this->spr.setFreeFont(&Orbitron_Light_32); + this->spr.setTextColor(TFT_WHITE, TFT_RED); + this->spr.setTextDatum(MC_DATUM); + this->spr.drawString("! LIGHTS OFF !", 160, 120, GFXFF); + this->spr.pushSprite(0, 0); + + return; + } + + this->spr.fillSprite(TFT_BLACK); + + // 1. Auto mode = >5kpm Screen 3 - speed, other wise basic Screen2 - Main screen, if charging then Screen 5 Graph + if (this->displayScreen == SCREEN_AUTO) { + if (this->liveData->params.speedKmh > 5) { + if (this->displayScreenAutoMode != 3) { + this->displayScreenAutoMode = 3; + } + this->drawSceneSpeed(); + } else if (this->liveData->params.batPowerKw > 1) { + if (this->displayScreenAutoMode != 5) { + this->displayScreenAutoMode = 5; + } + this->drawSceneChargingGraph(); + } else { + if (this->displayScreenAutoMode != 2) { + this->displayScreenAutoMode = 2; + } + this->drawSceneMain(); + } + } + // 2. Main screen + if (this->displayScreen == SCREEN_DASH) { + this->drawSceneMain(); + } + // 3. Big speed + kwh/100km + if (this->displayScreen == SCREEN_SPEED) { + this->drawSceneSpeed(); + } + // 4. Battery cells + if (this->displayScreen == SCREEN_CELLS) { + this->drawSceneBatteryCells(); + } + // 5. Charging graph + if (this->displayScreen == SCREEN_CHARGING) { + this->drawSceneChargingGraph(); + } + // 6. SOC10% table (CEC-CED) + if (this->displayScreen == SCREEN_SOC10) { + this->drawSceneSoc10Table(); + } + // 7. DEBUG SCREEN + if (this->displayScreen == SCREEN_DEBUG) { + this->drawSceneDebug(); + } + + if (!this->displayScreenSpeedHud) { + // BLE not connected + if (!this->liveData->bleConnected && this->liveData->bleConnect) { + // Print message + this->spr.setTextSize(1); + this->spr.setTextColor(TFT_WHITE, TFT_BLACK); + this->spr.setTextDatum(TL_DATUM); + this->spr.drawString("BLE4 OBDII not connected...", 0, 180, 2); + this->spr.drawString("Press middle button to menu.", 0, 200, 2); + this->spr.drawString(APP_VERSION, 0, 220, 2); + } + + this->spr.pushSprite(0, 0); + } +} + +/** + Parse test data +*/ +void Board320_240::loadTestData() { + + this->testDataMode = true; // skip lights off message + this->carInterface->loadTestData(); + this->redrawScreen(); +} + +/** + Board looop +*/ +void Board320_240::mainLoop() { + + /////////////////////////////////////////////////////////////////////// + // Handle buttons + // MIDDLE - menu select + if (digitalRead(this->pinButtonMiddle) == HIGH) { + this->btnMiddlePressed = false; + } else { + if (!this->btnMiddlePressed) { + this->btnMiddlePressed = true; + this->tft.setRotation(this->liveData->settings.displayRotation); + if (this->liveData->menuVisible) { + menuItemClick(); + } else { + this->showMenu(); + } + } + } + // LEFT - screen rotate, menu + if (digitalRead((this->liveData->settings.displayRotation == 1) ? this->pinButtonRight : this->pinButtonLeft) == HIGH) { + this->btnLeftPressed = false; + } else { + if (!this->btnLeftPressed) { + this->btnLeftPressed = true; + this->tft.setRotation(this->liveData->settings.displayRotation); + // Menu handling + if (this->liveData->menuVisible) { + this->menuMove(false); + } else { + this->displayScreen++; + if (this->displayScreen > this->displayScreenCount - (this->liveData->settings.debugScreen == 0) ? 1 : 0) + this->displayScreen = 0; // rotate screens + // Turn off display on screen 0 + this->setBrightness((this->displayScreen == SCREEN_BLANK) ? 0 : (this->liveData->settings.lcdBrightness == 0) ? 100 : this->liveData->settings.lcdBrightness); + this->redrawScreen(); + } + } + } + // RIGHT - menu, debug screen rotation + if (digitalRead((this->liveData->settings.displayRotation == 1) ? this->pinButtonLeft : this->pinButtonRight) == HIGH) { + this->btnRightPressed = false; + } else { + if (!this->btnRightPressed) { + this->btnRightPressed = true; + this->tft.setRotation(this->liveData->settings.displayRotation); + // Menu handling + if (this->liveData->menuVisible) { + this->menuMove(true); + } else { + // doAction + if (this->displayScreen == SCREEN_SPEED) { + this->displayScreenSpeedHud = !this->displayScreenSpeedHud; + this->redrawScreen(); + } + if (this->liveData->settings.debugScreen == 1 && this->displayScreen == SCREEN_DEBUG) { + this->debugCommandIndex = (this->debugCommandIndex >= this->liveData->commandQueueCount) ? this->liveData->commandQueueLoopFrom : this->debugCommandIndex + 1; + this->redrawScreen(); + } + + } + } + } + +} + +/** + * skipAdapterScan + */ +bool Board320_240::skipAdapterScan() { + return digitalRead(this->pinButtonMiddle) == LOW || digitalRead(this->pinButtonRight) == LOW; +} + +#endif // BOARD320_240_CPP diff --git a/Board320_240.h b/Board320_240.h new file mode 100644 index 0000000..cff7838 --- /dev/null +++ b/Board320_240.h @@ -0,0 +1,67 @@ +#ifndef BOARD320_240_H +#define BOARD320_240_H + +// TFT COMMON +#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH +#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters +#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters +#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm +#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. +#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts +#define SMOOTH_FONT +#define GFXFF 1 // TFT FOnts + +// +#include +#include "BoardInterface.h" + +class Board320_240 : public BoardInterface { + + private: + // TFT, SD SPI + TFT_eSPI tft = TFT_eSPI(); + TFT_eSprite spr = TFT_eSprite(&tft); + char tmpStr1[20]; + char tmpStr2[20]; + char tmpStr3[20]; + char tmpStr4[20]; + public: + bool invertDisplay = false; + byte pinButtonLeft = 0; + byte pinButtonRight = 0; + byte pinButtonMiddle = 0; + byte pinSpeaker = 0; + byte pinBrightness = 0; + // + void initBoard() override; + void afterSetup() override; + void mainLoop() override; + bool skipAdapterScan() override; + // Basic GUI + void setBrightness(byte lcdBrightnessPerc) override; + void displayMessage(const char* row1, const char* row2) override; + void redrawScreen() override; + // Custom screens + void drawBigCell(int32_t x, int32_t y, int32_t w, int32_t h, const char* text, const char* desc, uint16_t bgColor, uint16_t fgColor); + void drawSmallCell(int32_t x, int32_t y, int32_t w, int32_t h, const char* text, const char* desc, int16_t bgColor, int16_t fgColor); + void showTires(int32_t x, int32_t y, int32_t w, int32_t h, const char* topleft, const char* topright, const char* bottomleft, const char* bottomright, uint16_t color); + void drawSceneMain(); + void drawSceneSpeed(); + void drawSceneBatteryCells(); + void drawPreDrawnChargingGraphs(int zeroX, int zeroY, int mulX, int mulY); + void drawSceneChargingGraph(); + void drawSceneSoc10Table(); + void drawSceneDebug(); + // Menu + String menuItemCaption(int16_t menuItemId, String title); + void showMenu() override; + void hideMenu() override; + void menuMove(bool forward); + void menuItemClick(); + // + void loadTestData(); + // +}; + +#endif // BOARD320_240_H diff --git a/BoardInterface.cpp b/BoardInterface.cpp index e69de29..3019651 100644 --- a/BoardInterface.cpp +++ b/BoardInterface.cpp @@ -0,0 +1,151 @@ +#ifndef BOARDINTERFACE_CPP +#define BOARDINTERFACE_CPP + +#include +#include +#include "BoardInterface.h" +#include "LiveData.h" + +/** + Set live data +*/ +void BoardInterface::setLiveData(LiveData* pLiveData) { + this->liveData = pLiveData; +} + +/** + Attach car interface +*/ +void BoardInterface::attachCar(CarInterface* pCarInterface) { + this->carInterface = pCarInterface; +} + + +/** + Shutdown device +*/ +void BoardInterface::shutdownDevice() { + + Serial.println("Shutdown."); + + this->displayMessage("Shutdown in 3 sec.", ""); + delay(3000); + + setCpuFrequencyMhz(80); + this->setBrightness(0); + //WiFi.disconnect(true); + //WiFi.mode(WIFI_OFF); + btStop(); + //adc_power_off(); + //esp_wifi_stop(); + esp_bt_controller_disable(); + + delay(2000); + //esp_sleep_enable_timer_wakeup(525600L * 60L * 1000000L); // minutes + esp_deep_sleep_start(); +} + +/** + Load settings from flash memory, upgrade structure if version differs +*/ +void BoardInterface::saveSettings() { + + // Flash to memory + Serial.println("Settings saved to eeprom."); + EEPROM.put(0, this->liveData->settings); + EEPROM.commit(); +} + +/** + Reset settings (factory reset) +*/ +void BoardInterface::resetSettings() { + + // Flash to memory + Serial.println("Factory reset."); + this->liveData->settings.initFlag = 1; + EEPROM.put(0, this->liveData->settings); + EEPROM.commit(); + + this->displayMessage("Settings erased", "Restarting in 5 seconds"); + + delay(5000); + ESP.restart(); +} + +/** + Load setting from flash memory, upgrade structure if version differs +*/ +void BoardInterface::loadSettings() { + + String tmpStr; + + // Init + this->liveData->settings.initFlag = 183; + this->liveData->settings.settingsVersion = 3; + this->liveData->settings.carType = CAR_KIA_ENIRO_2020_64; + + // Default OBD adapter MAC and UUID's + tmpStr = "00:00:00:00:00:00"; // Pair via menu (middle button) + tmpStr.toCharArray(this->liveData->settings.obdMacAddress, tmpStr.length() + 1); + tmpStr = "000018f0-0000-1000-8000-00805f9b34fb"; // Default UUID's for VGate iCar Pro BLE4 adapter + tmpStr.toCharArray(this->liveData->settings.serviceUUID, tmpStr.length() + 1); + tmpStr = "00002af0-0000-1000-8000-00805f9b34fb"; + tmpStr.toCharArray(this->liveData->settings.charTxUUID, tmpStr.length() + 1); + tmpStr = "00002af1-0000-1000-8000-00805f9b34fb"; + tmpStr.toCharArray(this->liveData->settings.charRxUUID, tmpStr.length() + 1); + + this->liveData->settings.displayRotation = 1; // 1,3 + this->liveData->settings.distanceUnit = 'k'; + this->liveData->settings.temperatureUnit = 'c'; + this->liveData->settings.pressureUnit = 'b'; + this->liveData->settings.defaultScreen = 1; + this->liveData->settings.lcdBrightness = 0; + this->liveData->settings.debugScreen = 0; + this->liveData->settings.predrawnChargingGraphs = 1; + +#ifdef SIM800L_ENABLED + tmpStr = "internet.t-mobile.cz"; + tmpStr.toCharArray(liveData->settings.gprsApn, tmpStr.length() + 1); + tmpStr = "http://api.example.com"; + tmpStr.toCharArray(liveData->settings.remoteApiSrvr, tmpStr.length() + 1); + tmpStr = "example"; + tmpStr.toCharArray(liveData->settings.remoteApiKey, tmpStr.length() + 1); +#endif //SIM800L_ENABLED + + // Load settings and replace default values + Serial.println("Reading settings from eeprom."); + EEPROM.begin(sizeof(SETTINGS_STRUC)); + EEPROM.get(0, this->liveData->tmpSettings); + + // Init flash with default settings + if (this->liveData->tmpSettings.initFlag != 183) { + Serial.println("Settings not found. Initialization."); + this->saveSettings(); + } else { + Serial.print("Loaded settings ver.: "); + Serial.println(this->liveData->settings.settingsVersion); + + // Upgrade structure + if (this->liveData->settings.settingsVersion != this->liveData->tmpSettings.settingsVersion) { + if (this->liveData->tmpSettings.settingsVersion == 1) { + this->liveData->tmpSettings.settingsVersion = 2; + this->liveData->tmpSettings.defaultScreen = this->liveData->settings.defaultScreen; + this->liveData->tmpSettings.lcdBrightness = this->liveData->settings.lcdBrightness; + this->liveData->tmpSettings.debugScreen = this->liveData->settings.debugScreen; + } + if (this->liveData->tmpSettings.settingsVersion == 2) { + this->liveData->tmpSettings.settingsVersion = 3; + this->liveData->tmpSettings.predrawnChargingGraphs = this->liveData->settings.predrawnChargingGraphs; + } + this->saveSettings(); + } + + // Save version? No need to upgrade structure + if (this->liveData->settings.settingsVersion == this->liveData->tmpSettings.settingsVersion) { + this->liveData->settings = this->liveData->tmpSettings; + } + } +} + +#endif // BOARDINTERFACE_CPP diff --git a/BoardInterface.h b/BoardInterface.h index e69de29..5ae2d08 100644 --- a/BoardInterface.h +++ b/BoardInterface.h @@ -0,0 +1,50 @@ +#ifndef BOARDINTERFACE_H +#define BOARDINTERFACE_H + +#include "LiveData.h" +#include "CarInterface.h" + +class BoardInterface { + + private: + public: + // Screens, buttons + byte displayScreen = SCREEN_AUTO; + byte displayScreenAutoMode = 0; + byte displayScreenSpeedHud = false; + byte displayScreenCount = 7; + bool btnLeftPressed = true; + bool btnMiddlePressed = true; + bool btnRightPressed = true; + bool testDataMode = false; + bool scanDevices = false; + // Debug screen - next command with right button + uint16_t debugCommandIndex = 0; + String debugAtshRequest = "ATSH7E4"; + String debugCommandRequest = "220101"; + String debugLastString = "620101FFF7E7FF99000000000300B10EFE120F11100F12000018C438C30B00008400003864000035850000153A00001374000647010D017F0BDA0BDA03E8"; + String debugPreviousString = "620101FFF7E7FFB3000000000300120F9B111011101011000014CC38CB3B00009100003A510000367C000015FB000013D3000690250D018E0000000003E8"; + // + LiveData* liveData; + CarInterface* carInterface; + void setLiveData(LiveData* pLiveData); + void attachCar(CarInterface* pCarInterface); + virtual void initBoard()=0; + virtual void afterSetup()=0; + virtual void mainLoop()=0; + virtual bool skipAdapterScan() {return false;}; + // Graphics & GUI + virtual void displayMessage(const char* row1, const char* row2)=0; + virtual void setBrightness(byte lcdBrightnessPerc)=0; + virtual void redrawScreen()=0; + // Menu + virtual void showMenu()=0; + virtual void hideMenu()=0; + // Common + void shutdownDevice(); + void saveSettings(); + void resetSettings(); + void loadSettings(); +}; + +#endif // BOARDINTERFACE_H diff --git a/BoardM5stackCore.cpp b/BoardM5stackCore.cpp index e69de29..744165e 100644 --- a/BoardM5stackCore.cpp +++ b/BoardM5stackCore.cpp @@ -0,0 +1,24 @@ +#ifndef BOARDM5STACKCORE_CPP +#define BOARDM5STACKCORE_CPP + +#include "BoardInterface.h" +#include "Board320_240.h" +#include "BoardM5stackCore.h" + +/** + Init board +*/ +void BoardM5stackCore::initBoard() { + + this->invertDisplay = true; + this->pinButtonLeft = BUTTON_LEFT; + this->pinButtonRight = BUTTON_RIGHT; + this->pinButtonMiddle = BUTTON_MIDDLE; + this->pinSpeaker = SPEAKER_PIN; + this->pinBrightness = TFT_BL; + + Board320_240::initBoard(); + +} + +#endif // BOARDM5STACKCORE_CPP diff --git a/BoardM5stackCore.h b/BoardM5stackCore.h index c289d05..55045d4 100644 --- a/BoardM5stackCore.h +++ b/BoardM5stackCore.h @@ -1,11 +1,11 @@ +#ifndef BOARDM5STACKCORE_H +#define BOARDM5STACKCORE_H // Setup for m5stack core #define USER_SETUP_LOADED 1 #define SPI_FREQUENCY 27000000 #define SPI_TOUCH_FREQUENCY 2500000 -#ifdef BOARD_M5STACK_CORE - #define USER_SETUP_LOADED 1 #define ILI9341_DRIVER #define M5STACK @@ -19,7 +19,6 @@ #define SPI_FREQUENCY 27000000 #define SPI_READ_FREQUENCY 5000000 #define SPEAKER_PIN 25 -#define INVERT_DISPLAY #define BUTTON_LEFT 37 #define BUTTON_MIDDLE 38 @@ -30,4 +29,16 @@ #define SD_MISO 19 #define SD_SCLK 18 -#endif // BOARD_M5STACK_CORE +// +#include "BoardInterface.h" +#include "Board320_240.h" + +class BoardM5stackCore : public Board320_240 { + + private: + public: + void initBoard() override; + +}; + +#endif // BOARDM5STACKCORE_H diff --git a/BoardTtgoT4v13.cpp b/BoardTtgoT4v13.cpp index e69de29..bbfcc7b 100644 --- a/BoardTtgoT4v13.cpp +++ b/BoardTtgoT4v13.cpp @@ -0,0 +1,22 @@ +#ifndef BOARDTTGOT4V13_CPP +#define BOARDTTGOT4V13_CPP + +#include "BoardInterface.h" +#include "Board320_240.h" +#include "BoardTtgoT4v13.h" + +/** + Init board +*/ +void BoardTtgoT4v13::initBoard() { + + this->pinButtonLeft = BUTTON_LEFT; + this->pinButtonRight = BUTTON_RIGHT; + this->pinButtonMiddle = BUTTON_MIDDLE; + //this->pinSpeaker = SPEAKER_PIN; + this->pinBrightness = TFT_BL; + + Board320_240::initBoard(); +} + +#endif // BOARDTTGOT4V13_CPP diff --git a/BoardTtgoT4v13.h b/BoardTtgoT4v13.h index ed9d1a4..f8e02d8 100644 --- a/BoardTtgoT4v13.h +++ b/BoardTtgoT4v13.h @@ -1,11 +1,11 @@ +#ifndef BOARDTTGOT4V13_H +#define BOARDTTGOT4V13_H // Setup for TTGO T4 v13 #define USER_SETUP_LOADED 1 #define SPI_FREQUENCY 27000000 #define SPI_TOUCH_FREQUENCY 2500000 -#ifdef BOARD_TTGO_T4 - #define ILI9341_DRIVER #define TFT_MISO 12 #define TFT_MOSI 23 @@ -29,4 +29,17 @@ #define BUTTON_MIDDLE 37 #define BUTTON_RIGHT 39 -#endif // BOARD_TTGO_T4 +// +#include "BoardInterface.h" +#include "Board320_240.h" + +// +class BoardTtgoT4v13 : public Board320_240 { + + private: + public: + void initBoard() override; + +}; + +#endif // BOARDTTGOT4V13_H diff --git a/CarKiaEniro.cpp b/CarKiaEniro.cpp index 043feee..fc7e604 100644 --- a/CarKiaEniro.cpp +++ b/CarKiaEniro.cpp @@ -127,7 +127,7 @@ void CarKiaEniro::parseRowMerged() { this->liveData->params.ignitionOnPrevious = this->liveData->params.ignitionOn; this->liveData->params.ignitionOn = (bitRead(tempByte, 5) == 1); if (this->liveData->params.ignitionOnPrevious && !this->liveData->params.ignitionOn) - this->liveData->params.automatickShutdownTimer = this->liveData->params.currentTime; + this->liveData->params.automaticShutdownTimer = this->liveData->params.currentTime; this->liveData->params.lightInfo = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(18, 20).c_str(), 1, false); this->liveData->params.headLights = (bitRead(this->liveData->params.lightInfo, 5) == 1); diff --git a/LiveData.cpp b/LiveData.cpp index ad4ccbe..8a0d159 100644 --- a/LiveData.cpp +++ b/LiveData.cpp @@ -3,6 +3,103 @@ #define LIVEDATA_CPP #include "LiveData.h" +#include "menu.h" + +/** + Init params with default values +*/ +void LiveData::initParams() { + + this->params.automaticShutdownTimer = 0; +#ifdef SIM800L_ENABLED + this->params.lastDataSent = 0; + this->params.sim800l_enabled = false; +#endif //SIM800L_ENABLED + this->params.ignitionOn = false; + this->params.ignitionOnPrevious = false; + this->params.chargingStartTime = this->params.currentTime = 0; + this->params.lightInfo = 0; + this->params.headLights = false; + this->params.dayLights = false; + this->params.brakeLights = false; + this->params.brakeLightInfo = 0; + this->params.forwardDriveMode = false; + this->params.reverseDriveMode = false; + this->params.parkModeOrNeutral = false; + this->params.espState = 0; + this->params.speedKmh = -1; + this->params.motorRpm = -1; + this->params.odoKm = -1; + this->params.socPerc = -1; + this->params.socPercPrevious = -1; + this->params.sohPerc = -1; + this->params.cumulativeEnergyChargedKWh = -1; + this->params.cumulativeEnergyChargedKWhStart = -1; + this->params.cumulativeEnergyDischargedKWh = -1; + this->params.cumulativeEnergyDischargedKWhStart = -1; + this->params.availableChargePower = -1; + this->params.availableDischargePower = -1; + this->params.isolationResistanceKOhm = -1; + this->params.batPowerAmp = -1; + this->params.batPowerKw = -1; + this->params.batPowerKwh100 = -1; + this->params.batVoltage = -1; + this->params.batCellMinV = -1; + this->params.batCellMaxV = -1; + this->params.batTempC = -1; + this->params.batHeaterC = -1; + this->params.batInletC = -1; + this->params.batFanStatus = -1; + this->params.batFanFeedbackHz = -1; + this->params.batMinC = -1; + this->params.batMaxC = -1; + for (int i = 0; i < 12; i++) { + this->params.batModuleTempC[i] = 0; + } + this->params.batModuleTempC[0] = -1; + this->params.batModuleTempC[1] = -1; + this->params.batModuleTempC[2] = -1; + this->params.batModuleTempC[3] = -1; + this->params.coolingWaterTempC = -1; + this->params.coolantTemp1C = -1; + this->params.coolantTemp2C = -1; + this->params.bmsUnknownTempA = -1; + this->params.bmsUnknownTempB = -1; + this->params.bmsUnknownTempC = -1; + this->params.bmsUnknownTempD = -1; + this->params.auxPerc = -1; + this->params.auxCurrentAmp = -1; + this->params.auxVoltage = -1; + this->params.indoorTemperature = -1; + this->params.outdoorTemperature = -1; + this->params.tireFrontLeftTempC = -1; + this->params.tireFrontLeftPressureBar = -1; + this->params.tireFrontRightTempC = -1; + this->params.tireFrontRightPressureBar = -1; + this->params.tireRearLeftTempC = -1; + this->params.tireRearLeftPressureBar = -1; + this->params.tireRearRightTempC = -1; + this->params.tireRearRightPressureBar = -1; + for (int i = 0; i <= 10; i++) { + this->params.soc10ced[i] = this->params.soc10cec[i] = this->params.soc10odo[i] = -1; + this->params.soc10time[i] = 0; + } + for (int i = 0; i < 98; i++) { + this->params.cellVoltage[i] = 0; + } + this->params.cellCount = 0; + for (int i = 0; i <= 100; i++) { + this->params.chargingGraphMinKw[i] = -1; + this->params.chargingGraphMaxKw[i] = -1; + this->params.chargingGraphBatMinTempC[i] = -100; + this->params.chargingGraphBatMaxTempC[i] = -100; + this->params.chargingGraphHeaterTempC[i] = -100; + this->params.chargingGraphWaterCoolantTempC[i] = -100; + } + + // Menu + this->menuItems = menuItemsSource; +} /** Hex to dec (1-2 byte values, signed/unsigned) @@ -33,5 +130,29 @@ float LiveData::hexToDec(String hexString, byte bytes, bool signedNum) { return (decValue > 32767 ? (float)decValue - 65536.0 : decValue); } +/** + Convert km to km or miles +*/ +float LiveData::km2distance(float inKm) { + return (this->settings.distanceUnit == 'k') ? inKm : inKm / 1.609344; +} + +/** + Convert celsius to celsius or farenheit +*/ +float LiveData::celsius2temperature(float inCelsius) { + return (this->settings.temperatureUnit == 'c') ? inCelsius : (inCelsius * 1.8) + 32; +} + +/** + Convert bar to bar or psi +*/ +float LiveData::bar2pressure(float inBar) { + return (this->settings.pressureUnit == 'b') ? inBar : inBar * 14.503773800722; +} + + + + // #endif // LIVEDATA_CPP diff --git a/LiveData.h b/LiveData.h index 39d6d7a..77f9a30 100644 --- a/LiveData.h +++ b/LiveData.h @@ -7,6 +7,8 @@ #include #include #include +#include +#include "config.h" // SUPPORTED CARS #define CAR_KIA_ENIRO_2020_64 0 @@ -31,7 +33,7 @@ typedef struct { time_t currentTime; time_t chargingStartTime; - time_t automatickShutdownTimer; + time_t automaticShutdownTimer; #ifdef SIM800L_ENABLED time_t lastDataSent; bool sim800l_enabled; @@ -147,6 +149,7 @@ typedef struct { } SETTINGS_STRUC; +// class LiveData { private: public: @@ -160,10 +163,36 @@ class LiveData { bool canSendNextAtCommand = false; String commandRequest = ""; String currentAtshRequest = ""; - // + // Menu + bool menuVisible = false; + uint8_t menuItemsCount = 78; + uint16_t menuCurrent = 0; + uint8_t menuItemSelected = 0; + uint8_t menuItemOffset = 0; + uint16_t scanningDeviceIndex = 0; + MENU_ITEM* menuItems; + + // Bluetooth4 + boolean bleConnect = true; + boolean bleConnected = false; + BLEAddress *pServerAddress; + BLERemoteCharacteristic* pRemoteCharacteristic; + BLERemoteCharacteristic* pRemoteCharacteristicWrite; + BLEAdvertisedDevice* foundMyBleDevice; + BLEClient* pClient; + BLEScan* pBLEScan; + + // Params PARAMS_STRUC params; // Realtime sensor values + // Settings SETTINGS_STRUC settings, tmpSettings; // Settings stored into flash + + // + void initParams(); float hexToDec(String hexString, byte bytes = 2, bool signedNum = true); + float km2distance(float inKm); + float celsius2temperature(float inCelsius); + float bar2pressure(float inBar); }; diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 805031a..127e1ff 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -2,6 +2,7 @@ ### v1.9.0 2020-11-30 - Refactoring (classes) +- SIM800L (m5stack) code from https://github.com/kolaCZek ### v1.8.3 2020-11-28 - Automatic shutdown when car goes off diff --git a/build.bat b/build.bat index e650cd4..a5386ed 100644 --- a/build.bat +++ b/build.bat @@ -1,8 +1,9 @@ rem slow GUI performance via arduino-cli yet +arduino-cli compile -v -b esp32:esp32:esp32wrover --build-properties build.extra_flags=-BOARD_TTGO_T4=1 -v enirodashboard.ino rem arduino-cli compile -v -b esp32:esp32:esp32:PSRAM=enabled,PartitionScheme=huge_app,CPUFreq=80 --build-properties build.extra_flags=-BOARD_TTGO_T4=1 -v enirodashboard.ino -rem arduino-cli upload -b esp32:esp32:esp32 -v -p COM6 +rem rduino-cli upload -b esp32:esp32:esp32 -v -p COM6 rem arduino-cli compile -v -b esp32:esp32:m5stack-core-esp32 --build-properties build.extra_flags=-BOARD_M5STACK=1 enirodashboard.ino rem arduino-cli upload -v -b esp32:esp32:m5stack-core-esp32 -p COM9 diff --git a/config.h b/config.h index 0442df7..7ab2b15 100644 --- a/config.h +++ b/config.h @@ -1,16 +1,10 @@ #ifndef CONFIG_H #define CONFIG_H -// TFT COMMON -#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH -#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters -#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters -#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm -#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. -#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. -#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts -#define SMOOTH_FONT -#define GFXFF 1 // TFT FOnts +#include + +#define APP_VERSION "v1.9.0b" +#define APP_RELEASE_DATE "2020-11-30" // TFT COLORS FOR TTGO #define TFT_BLACK 0x0000 /* 0, 0, 0 */ @@ -62,6 +56,14 @@ #define SIM800L_TIMER 120 #endif //SIM800L_ENABLED -///////////////////////////////////////////////////////////// -// +// MENU ITEM +typedef struct { + int16_t id; + int16_t parentId; + int16_t targetParentId; + char title[50]; + char obdMacAddress[20]; + char serviceUUID[40]; +} MENU_ITEM; + #endif // CONFIG_H diff --git a/enirodashboard.ino b/enirodashboard.ino index 01b4ee2..1c8861e 100644 --- a/enirodashboard.ino +++ b/enirodashboard.ino @@ -3,18 +3,20 @@ !! Supported adapter is Vgate ICar Pro (must be BLE4.0 version) !! Not working with standard BLUETOOTH 3 adapters -Required libraries -- esp32 board support -- tft_espi -- ArduinoJson -For SIM800L m5stack -- SIM800L.h, SoftwareSerial.h + Required libraries + - esp32 board support + - tft_espi + - ArduinoJson + + SIM800L m5stack (https://github.com/kolaCZek) + - SIM800L.h, SoftwareSerial.h */ //////////////////////////////////////////////////////////// // SELECT HARDWARE //////////////////////////////////////////////////////////// +// Boards //#define BOARD_TTGO_T4 #define BOARD_M5STACK_CORE @@ -24,31 +26,25 @@ For SIM800L m5stack //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// -#define APP_VERSION "v1.9.0" -#define APP_RELEASE_DATE "2020-11-30" - #include -#include - #include +#include "BoardInterface.h" + #ifdef BOARD_TTGO_T4 #include "BoardTtgoT4v13.h" #endif // BOARD_TTGO_T4 #ifdef BOARD_M5STACK_CORE #include "BoardM5stackCore.h" #endif // BOARD_M5STACK_CORE -#include "config.h" #ifdef SD_ENABLED #include //#include #endif -#include #include -#include +#include "config.h" #include "LiveData.h" -#include "menu.h" #include "CarInterface.h" #include "CarKiaEniro.h" #include "CarHyundaiIoniq.h" @@ -65,1440 +61,24 @@ SIM800L* sim800l; // PLEASE CHANGE THIS SETTING for your BLE4 uint32_t PIN = 1234; -// TFT, SD SPI -TFT_eSPI tft = TFT_eSPI(); -TFT_eSprite spr = TFT_eSprite(&tft); -//SPIClass spiSD(HSPI); - -// BLUETOOTH4 -static boolean bleConnect = true; -static boolean bleConnected = false; -static BLEAddress *pServerAddress; -static BLERemoteCharacteristic* pRemoteCharacteristic; -static BLERemoteCharacteristic* pRemoteCharacteristicWrite; -BLEAdvertisedDevice* foundMyBleDevice; -BLEClient* pClient; -BLEScan* pBLEScan; - // Temporary variables char ch; String line; -char tmpStr1[20]; -char tmpStr2[20]; -char tmpStr3[20]; -char tmpStr4[20]; -// Screens, buttons -#define displayScreenCount 7 -byte displayScreen = SCREEN_AUTO; -byte displayScreenAutoMode = 0; -byte displayScreenSpeedHud = false; -bool btnLeftPressed = true; -bool btnMiddlePressed = true; -bool btnRightPressed = true; -bool testDataMode = false; - -// Debug screen - next command with right button -uint16_t debugCommandIndex = 0; -String debugAtshRequest = "ATSH7E4"; -String debugCommandRequest = "220101"; -String debugLastString = "620101FFF7E7FF99000000000300B10EFE120F11100F12000018C438C30B00008400003864000035850000153A00001374000647010D017F0BDA0BDA03E8"; -String debugPreviousString = "620101FFF7E7FFB3000000000300120F9B111011101011000014CC38CB3B00009100003A510000367C000015FB000013D3000690250D018E0000000003E8"; -bool debugTmpCharging = false; -String debugTmpChargingLast05 = ""; -String debugTmpChargingPrevious05 = ""; -String debugTmpChargingRef05 = "620105003FFF900000000000000000--####################--------00000000--0000----##00--00000000AAAA"; -String debugTmpChargingLast06 = ""; -String debugTmpChargingPrevious06 = ""; -String debugTmpChargingRef06 = "620106FFFFFFFF--00--########################00--28EA00"; - -// Car -CarInterface* carInterface; +// Board, Car, Livedata (params, settings) +BoardInterface* board; +CarInterface* car; LiveData* liveData; /** - Clear screen a display two lines message -*/ -bool displayMessage(const char* row1, const char* row2) { - - // Must draw directly, withou sprite (due to psramFound check) - tft.fillScreen(TFT_BLACK); - tft.setTextDatum(ML_DATUM); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setFreeFont(&Roboto_Thin_24); - tft.setTextDatum(BL_DATUM); - tft.drawString(row1, 0, 240 / 2, GFXFF); - tft.drawString(row2, 0, (240 / 2) + 30, GFXFF); - - return true; -} - -/** - Shutdown device -*/ -bool shutdownDevice() { - - Serial.println("Shutdown."); - - displayMessage("Shutdown in 3 sec.", ""); - delay(3000); - - setCpuFrequencyMhz(80); - analogWrite(TFT_BL, 0); - //WiFi.disconnect(true); - //WiFi.mode(WIFI_OFF); - btStop(); - //adc_power_off(); - //esp_wifi_stop(); - esp_bt_controller_disable(); - - delay(2000); - //esp_sleep_enable_timer_wakeup(/*minutes*/ 525600L * 60L * 1000000L); - esp_deep_sleep_start(); - - return true; -} - -/** - Load settings from flash memory, upgrade structure if version differs -*/ -bool saveSettings() { - - // Flash to memory - Serial.println("Settings saved to eeprom."); - EEPROM.put(0, liveData->settings); - EEPROM.commit(); - - return true; -} - -/** - Reset settings (factory reset) -*/ -bool resetSettings() { - - // Flash to memory - Serial.println("Factory reset."); - liveData->settings.initFlag = 1; - EEPROM.put(0, liveData->settings); - EEPROM.commit(); - - displayMessage("Settings erased", "Restarting in 5 seconds"); - - delay(5000); - ESP.restart(); - - return true; -} - -/** - Load setting from flash memory, upgrade structure if version differs -*/ -bool loadSettings() { - - String tmpStr; - - // Init - liveData->settings.initFlag = 183; - liveData->settings.settingsVersion = 3; - liveData->settings.carType = CAR_KIA_ENIRO_2020_64; - - // Default OBD adapter MAC and UUID's - tmpStr = "00:00:00:00:00:00"; // Pair via menu (middle button) - tmpStr.toCharArray(liveData->settings.obdMacAddress, tmpStr.length() + 1); - tmpStr = "000018f0-0000-1000-8000-00805f9b34fb"; // Default UUID's for VGate iCar Pro BLE4 adapter - tmpStr.toCharArray(liveData->settings.serviceUUID, tmpStr.length() + 1); - tmpStr = "00002af0-0000-1000-8000-00805f9b34fb"; - tmpStr.toCharArray(liveData->settings.charTxUUID, tmpStr.length() + 1); - tmpStr = "00002af1-0000-1000-8000-00805f9b34fb"; - tmpStr.toCharArray(liveData->settings.charRxUUID, tmpStr.length() + 1); - - liveData->settings.displayRotation = 1; // 1,3 - liveData->settings.distanceUnit = 'k'; - liveData->settings.temperatureUnit = 'c'; - liveData->settings.pressureUnit = 'b'; - liveData->settings.defaultScreen = 1; - liveData->settings.lcdBrightness = 0; - liveData->settings.debugScreen = 0; - liveData->settings.predrawnChargingGraphs = 1; - -#ifdef SIM800L_ENABLED - tmpStr = "internet.t-mobile.cz"; - tmpStr.toCharArray(liveData->settings.gprsApn, tmpStr.length() + 1); - tmpStr = "http://api.example.com"; - tmpStr.toCharArray(liveData->settings.remoteApiSrvr, tmpStr.length() + 1); - tmpStr = "example"; - tmpStr.toCharArray(liveData->settings.remoteApiKey, tmpStr.length() + 1); -#endif //SIM800L_ENABLED - - // Load settings and replace default values - Serial.println("Reading settings from eeprom."); - EEPROM.begin(sizeof(SETTINGS_STRUC)); - EEPROM.get(0, liveData->tmpSettings); - - // Init flash with default settings - if (liveData->tmpSettings.initFlag != 183) { - Serial.println("Settings not found. Initialization."); - saveSettings(); - } else { - Serial.print("Loaded settings ver.: "); - Serial.println(liveData->settings.settingsVersion); - - // Upgrade structure - if (liveData->settings.settingsVersion != liveData->tmpSettings.settingsVersion) { - if (liveData->tmpSettings.settingsVersion == 1) { - liveData->tmpSettings.settingsVersion = 2; - liveData->tmpSettings.defaultScreen = liveData->settings.defaultScreen; - liveData->tmpSettings.lcdBrightness = liveData->settings.lcdBrightness; - liveData->tmpSettings.debugScreen = liveData->settings.debugScreen; - } - if (liveData->tmpSettings.settingsVersion == 2) { - liveData->tmpSettings.settingsVersion = 3; - liveData->tmpSettings.predrawnChargingGraphs = liveData->settings.predrawnChargingGraphs; - } - saveSettings(); - } - - // Save version? No need to upgrade structure - if (liveData->settings.settingsVersion == liveData->tmpSettings.settingsVersion) { - liveData->settings = liveData->tmpSettings; - } - } - - // Load command queue - if (liveData->settings.carType == CAR_KIA_ENIRO_2020_64 || liveData->settings.carType == CAR_HYUNDAI_KONA_2020_64 || - liveData->settings.carType == CAR_KIA_ENIRO_2020_39 || liveData->settings.carType == CAR_HYUNDAI_KONA_2020_39) { - CarKiaEniro* tmpCar = new CarKiaEniro(); - carInterface = tmpCar; - } else - if (liveData->settings.carType == CAR_HYUNDAI_IONIQ_2018) { - CarHyundaiIoniq* tmpCar = new CarHyundaiIoniq(); - carInterface = tmpCar; - } else { - // if (liveData->settings.carType == CAR_DEBUG_OBD2_KIA) - CarKiaDebugObd2* tmpCar = new CarKiaDebugObd2(); - carInterface = tmpCar; - } - carInterface->setLiveData(liveData); - carInterface->activateCommandQueue(); - debugCommandIndex = liveData->commandQueueLoopFrom; - - return true; -} - -/** - Init structure with data -*/ -bool initStructure() { - - liveData->params.automatickShutdownTimer = 0; -#ifdef SIM800L_ENABLED - liveData->params.lastDataSent = 0; - liveData->params.sim800l_enabled = false; -#endif //SIM800L_ENABLED - liveData->params.ignitionOn = false; - liveData->params.ignitionOnPrevious = false; - liveData->params.chargingStartTime = liveData->params.currentTime = 0; - liveData->params.lightInfo = 0; - liveData->params.headLights = false; - liveData->params.dayLights = false; - liveData->params.brakeLights = false; - liveData->params.brakeLightInfo = 0; - liveData->params.forwardDriveMode = false; - liveData->params.reverseDriveMode = false; - liveData->params.parkModeOrNeutral = false; - liveData->params.espState = 0; - liveData->params.speedKmh = -1; - liveData->params.motorRpm = -1; - liveData->params.odoKm = -1; - liveData->params.socPerc = -1; - liveData->params.socPercPrevious = -1; - liveData->params.sohPerc = -1; - liveData->params.cumulativeEnergyChargedKWh = -1; - liveData->params.cumulativeEnergyChargedKWhStart = -1; - liveData->params.cumulativeEnergyDischargedKWh = -1; - liveData->params.cumulativeEnergyDischargedKWhStart = -1; - liveData->params.availableChargePower = -1; - liveData->params.availableDischargePower = -1; - liveData->params.isolationResistanceKOhm = -1; - liveData->params.batPowerAmp = -1; - liveData->params.batPowerKw = -1; - liveData->params.batPowerKwh100 = -1; - liveData->params.batVoltage = -1; - liveData->params.batCellMinV = -1; - liveData->params.batCellMaxV = -1; - liveData->params.batTempC = -1; - liveData->params.batHeaterC = -1; - liveData->params.batInletC = -1; - liveData->params.batFanStatus = -1; - liveData->params.batFanFeedbackHz = -1; - liveData->params.batMinC = -1; - liveData->params.batMaxC = -1; - for (int i = 0; i < 12; i++) { - liveData->params.batModuleTempC[i] = 0; - } - liveData->params.batModuleTempC[0] = -1; - liveData->params.batModuleTempC[1] = -1; - liveData->params.batModuleTempC[2] = -1; - liveData->params.batModuleTempC[3] = -1; - liveData->params.coolingWaterTempC = -1; - liveData->params.coolantTemp1C = -1; - liveData->params.coolantTemp2C = -1; - liveData->params.bmsUnknownTempA = -1; - liveData->params.bmsUnknownTempB = -1; - liveData->params.bmsUnknownTempC = -1; - liveData->params.bmsUnknownTempD = -1; - liveData->params.auxPerc = -1; - liveData->params.auxCurrentAmp = -1; - liveData->params.auxVoltage = -1; - liveData->params.indoorTemperature = -1; - liveData->params.outdoorTemperature = -1; - liveData->params.tireFrontLeftTempC = -1; - liveData->params.tireFrontLeftPressureBar = -1; - liveData->params.tireFrontRightTempC = -1; - liveData->params.tireFrontRightPressureBar = -1; - liveData->params.tireRearLeftTempC = -1; - liveData->params.tireRearLeftPressureBar = -1; - liveData->params.tireRearRightTempC = -1; - liveData->params.tireRearRightPressureBar = -1; - for (int i = 0; i <= 10; i++) { - liveData->params.soc10ced[i] = liveData->params.soc10cec[i] = liveData->params.soc10odo[i] = -1; - liveData->params.soc10time[i] = 0; - } - for (int i = 0; i < 98; i++) { - liveData->params.cellVoltage[i] = 0; - } - liveData->params.cellCount = 0; - for (int i = 0; i <= 100; i++) { - liveData->params.chargingGraphMinKw[i] = -1; - liveData->params.chargingGraphMaxKw[i] = -1; - liveData->params.chargingGraphBatMinTempC[i] = -100; - liveData->params.chargingGraphBatMaxTempC[i] = -100; - liveData->params.chargingGraphHeaterTempC[i] = -100; - liveData->params.chargingGraphWaterCoolantTempC[i] = -100; - } - - return true; -} - -/** - Convert km to km or miles -*/ -float km2distance(float inKm) { - return (liveData->settings.distanceUnit == 'k') ? inKm : inKm / 1.609344; -} - -/** - Convert celsius to celsius or farenheit -*/ -float celsius2temperature(float inCelsius) { - return (liveData->settings.temperatureUnit == 'c') ? inCelsius : (inCelsius * 1.8) + 32; -} - -/** - Convert bar to bar or psi -*/ -float bar2pressure(float inBar) { - return (liveData->settings.pressureUnit == 'b') ? inBar : inBar * 14.503773800722; -} - -/** - Draw cell on dashboard -*/ -bool monitoringRect(int32_t x, int32_t y, int32_t w, int32_t h, const char* text, const char* desc, uint16_t bgColor, uint16_t fgColor) { - - int32_t posx, posy; - - posx = (x * 80) + 4; - posy = (y * 60) + 1; - - spr.fillRect(x * 80, y * 60, ((w) * 80) - 1, ((h) * 60) - 1, bgColor); - spr.drawFastVLine(((x + w) * 80) - 1, ((y) * 60) - 1, h * 60, TFT_BLACK); - spr.drawFastHLine(((x) * 80) - 1, ((y + h) * 60) - 1, w * 80, TFT_BLACK); - spr.setTextDatum(TL_DATUM); // Topleft - spr.setTextColor(TFT_SILVER, bgColor); // Bk, fg color - spr.setTextSize(1); // Size for small 5x7 font - spr.drawString(desc, posx, posy, 2); - - // Big 2x2 cell in the middle of screen - if (w == 2 && h == 2) { - - // Bottom 2 numbers with charged/discharged kWh from start - posx = (x * 80) + 5; - posy = ((y + h) * 60) - 32; - sprintf(tmpStr3, "-%01.01f", liveData->params.cumulativeEnergyDischargedKWh - liveData->params.cumulativeEnergyDischargedKWhStart); - spr.setFreeFont(&Roboto_Thin_24); - spr.setTextDatum(TL_DATUM); - spr.drawString(tmpStr3, posx, posy, GFXFF); - - posx = ((x + w) * 80) - 8; - sprintf(tmpStr3, "+%01.01f", liveData->params.cumulativeEnergyChargedKWh - liveData->params.cumulativeEnergyChargedKWhStart); - spr.setTextDatum(TR_DATUM); - spr.drawString(tmpStr3, posx, posy, GFXFF); - - // Main number - kwh on roads, amps on charges - posy = (y * 60) + 24; - spr.setTextColor(fgColor, bgColor); - spr.setFreeFont(&Orbitron_Light_32); - spr.drawString(text, posx, posy, 7); - - } else { - - // All others 1x1 cells - spr.setTextDatum(MC_DATUM); - spr.setTextColor(fgColor, bgColor); - spr.setFreeFont(&Orbitron_Light_24); - posx = (x * 80) + (w * 80 / 2) - 3; - posy = (y * 60) + (h * 60 / 2) + 4; - spr.drawString(text, posx, posy, (w == 2 ? 7 : GFXFF)); - } - - return true; -} - -/** - Draw small rect 80x30 -*/ -bool drawSmallRect(int32_t x, int32_t y, int32_t w, int32_t h, const char* text, const char* desc, int16_t bgColor, int16_t fgColor) { - - int32_t posx, posy; - - posx = (x * 80) + 4; - posy = (y * 32) + 1; - - spr.fillRect(x * 80, y * 32, ((w) * 80), ((h) * 32), bgColor); - spr.drawFastVLine(((x + w) * 80) - 1, ((y) * 32) - 1, h * 32, TFT_BLACK); - spr.drawFastHLine(((x) * 80) - 1, ((y + h) * 32) - 1, w * 80, TFT_BLACK); - spr.setTextDatum(TL_DATUM); // Topleft - spr.setTextColor(TFT_SILVER, bgColor); // Bk, fg bgColor - spr.setTextSize(1); // Size for small 5x7 font - spr.drawString(desc, posx, posy, 2); - - spr.setTextDatum(TC_DATUM); - spr.setTextColor(fgColor, bgColor); - posx = (x * 80) + (w * 80 / 2) - 3; - spr.drawString(text, posx, posy + 14, 2); - - return true; -} - -/** - Show tire pressures / temperatures - Custom field -*/ -bool showTires(int32_t x, int32_t y, int32_t w, int32_t h, const char* topleft, const char* topright, const char* bottomleft, const char* bottomright, uint16_t color) { - - int32_t posx, posy; - - spr.fillRect(x * 80, y * 60, ((w) * 80) - 1, ((h) * 60) - 1, color); - spr.drawFastVLine(((x + w) * 80) - 1, ((y) * 60) - 1, h * 60, TFT_BLACK); - spr.drawFastHLine(((x) * 80) - 1, ((y + h) * 60) - 1, w * 80, TFT_BLACK); - - spr.setTextDatum(TL_DATUM); - spr.setTextColor(TFT_SILVER, color); - spr.setTextSize(1); - posx = (x * 80) + 4; - posy = (y * 60) + 0; - spr.drawString(topleft, posx, posy, 2); - posy = (y * 60) + 14; - spr.drawString(bottomleft, posx, posy, 2); - - spr.setTextDatum(TR_DATUM); - posx = ((x + w) * 80) - 4; - posy = (y * 60) + 0; - spr.drawString(topright, posx, posy, 2); - posy = (y * 60) + 14; - spr.drawString(bottomright, posx, posy, 2); - - return true; -} - -/** - Main screen (Screen 1) -*/ -bool drawSceneMain() { - - // Tire pressure - char pressureStr[4] = "bar"; - char temperatureStr[2] = "C"; - if (liveData->settings.pressureUnit != 'b') - strcpy(pressureStr, "psi"); - if (liveData->settings.temperatureUnit != 'c') - strcpy(temperatureStr, "F"); - sprintf(tmpStr1, "%01.01f%s %02.00f%s", bar2pressure(liveData->params.tireFrontLeftPressureBar), pressureStr, celsius2temperature(liveData->params.tireFrontLeftTempC), temperatureStr); - sprintf(tmpStr2, "%02.00f%s %01.01f%s", celsius2temperature(liveData->params.tireFrontRightTempC), temperatureStr, bar2pressure(liveData->params.tireFrontRightPressureBar), pressureStr); - sprintf(tmpStr3, "%01.01f%s %02.00f%s", bar2pressure(liveData->params.tireRearLeftPressureBar), pressureStr, celsius2temperature(liveData->params.tireRearLeftTempC), temperatureStr); - sprintf(tmpStr4, "%02.00f%s %01.01f%s", celsius2temperature(liveData->params.tireRearRightTempC), temperatureStr, bar2pressure(liveData->params.tireRearRightPressureBar), pressureStr); - showTires(1, 0, 2, 1, tmpStr1, tmpStr2, tmpStr3, tmpStr4, TFT_BLACK); - - // Added later - kwh total in tires box - // TODO: refactoring - spr.setTextDatum(TL_DATUM); - spr.setTextColor(TFT_GREEN, TFT_BLACK); - sprintf(tmpStr1, "C: %01.01f +%01.01fkWh", liveData->params.cumulativeEnergyChargedKWh, liveData->params.cumulativeEnergyChargedKWh - liveData->params.cumulativeEnergyChargedKWhStart); - spr.drawString(tmpStr1, (1 * 80) + 4, (0 * 60) + 30, 2); - spr.setTextColor(TFT_YELLOW, TFT_BLACK); - sprintf(tmpStr1, "D: %01.01f -%01.01fkWh", liveData->params.cumulativeEnergyDischargedKWh, liveData->params.cumulativeEnergyDischargedKWh - liveData->params.cumulativeEnergyDischargedKWhStart); - spr.drawString(tmpStr1, (1 * 80) + 4, (0 * 60) + 44, 2); - - // batPowerKwh100 on roads, else batPowerAmp - if (liveData->params.speedKmh > 20) { - sprintf(tmpStr1, "%01.01f", km2distance(liveData->params.batPowerKwh100)); - monitoringRect(1, 1, 2, 2, tmpStr1, ((liveData->settings.distanceUnit == 'k') ? "POWER KWH/100KM" : "POWER KWH/100MI"), (liveData->params.batPowerKwh100 >= 0 ? TFT_DARKGREEN2 : (liveData->params.batPowerKwh100 < -30.0 ? TFT_RED : TFT_DARKRED)), TFT_WHITE); - } else { - // batPowerAmp on chargers (under 10kmh) - sprintf(tmpStr1, "%01.01f", liveData->params.batPowerKw); - monitoringRect(1, 1, 2, 2, tmpStr1, "POWER KW", (liveData->params.batPowerKw >= 0 ? TFT_DARKGREEN2 : (liveData->params.batPowerKw <= -30 ? TFT_RED : TFT_DARKRED)), TFT_WHITE); - } - - // socPerc - sprintf(tmpStr1, "%01.00f%%", liveData->params.socPerc); - sprintf(tmpStr2, (liveData->params.sohPerc == 100.0 ? "SOC/H%01.00f%%" : "SOC/H%01.01f%%"), liveData->params.sohPerc); - monitoringRect(0, 0, 1, 1, tmpStr1, tmpStr2, (liveData->params.socPerc < 10 || liveData->params.sohPerc < 100 ? TFT_RED : (liveData->params.socPerc > 80 ? TFT_DARKGREEN2 : TFT_DEFAULT_BK)), TFT_WHITE); - - // batPowerAmp - sprintf(tmpStr1, (abs(liveData->params.batPowerAmp) > 9.9 ? "%01.00f" : "%01.01f"), liveData->params.batPowerAmp); - monitoringRect(0, 1, 1, 1, tmpStr1, "CURRENT A", (liveData->params.batPowerAmp >= 0 ? TFT_DARKGREEN2 : TFT_DARKRED), TFT_WHITE); - - // batVoltage - sprintf(tmpStr1, "%03.00f", liveData->params.batVoltage); - monitoringRect(0, 2, 1, 1, tmpStr1, "VOLTAGE", TFT_DEFAULT_BK, TFT_WHITE); - - // batCellMinV - sprintf(tmpStr1, "%01.02f", liveData->params.batCellMaxV - liveData->params.batCellMinV); - sprintf(tmpStr2, "CELLS %01.02f", liveData->params.batCellMinV); - monitoringRect(0, 3, 1, 1, ( liveData->params.batCellMaxV - liveData->params.batCellMinV == 0.00 ? "OK" : tmpStr1), tmpStr2, TFT_DEFAULT_BK, TFT_WHITE); - - // batTempC - sprintf(tmpStr1, ((liveData->settings.temperatureUnit == 'c') ? "%01.00f" : "%01.01f"), celsius2temperature(liveData->params.batMinC)); - sprintf(tmpStr2, ((liveData->settings.temperatureUnit == 'c') ? "BATT. %01.00fC" : "BATT. %01.01fF"), celsius2temperature(liveData->params.batMaxC)); - monitoringRect(1, 3, 1, 1, tmpStr1, tmpStr2, TFT_TEMP, (liveData->params.batTempC >= 15) ? ((liveData->params.batTempC >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED); - - // batHeaterC - sprintf(tmpStr1, ((liveData->settings.temperatureUnit == 'c') ? "%01.00f" : "%01.01f"), celsius2temperature(liveData->params.batHeaterC)); - monitoringRect(2, 3, 1, 1, tmpStr1, "BAT.HEAT", TFT_TEMP, TFT_WHITE); - - // Aux perc - sprintf(tmpStr1, "%01.00f%%", liveData->params.auxPerc); - monitoringRect(3, 0, 1, 1, tmpStr1, "AUX BAT.", (liveData->params.auxPerc < 60 ? TFT_RED : TFT_DEFAULT_BK), TFT_WHITE); - - // Aux amp - sprintf(tmpStr1, (abs(liveData->params.auxCurrentAmp) > 9.9 ? "%01.00f" : "%01.01f"), liveData->params.auxCurrentAmp); - monitoringRect(3, 1, 1, 1, tmpStr1, "AUX AMPS", (liveData->params.auxCurrentAmp >= 0 ? TFT_DARKGREEN2 : TFT_DARKRED), TFT_WHITE); - - // auxVoltage - sprintf(tmpStr1, "%01.01f", liveData->params.auxVoltage); - monitoringRect(3, 2, 1, 1, tmpStr1, "AUX VOLTS", (liveData->params.auxVoltage < 12.1 ? TFT_RED : (liveData->params.auxVoltage < 12.6 ? TFT_ORANGE : TFT_DEFAULT_BK)), TFT_WHITE); - - // indoorTemperature - sprintf(tmpStr1, "%01.01f", celsius2temperature(liveData->params.indoorTemperature)); - sprintf(tmpStr2, "IN/OUT%01.01f", celsius2temperature(liveData->params.outdoorTemperature)); - monitoringRect(3, 3, 1, 1, tmpStr1, tmpStr2, TFT_TEMP, TFT_WHITE); - - return true; -} - -/** - Speed + kwh/100km (Screen 2) -*/ -bool drawSceneSpeed() { - - int32_t posx, posy; - - // HUD - if (displayScreenSpeedHud) { - - // Change rotation to vertical & mirror - if (tft.getRotation() != 6) { - tft.setRotation(6); - } - - tft.fillScreen(TFT_BLACK); - tft.setTextDatum(TR_DATUM); // top-right alignment - tft.setTextColor(TFT_WHITE, TFT_BLACK); // foreground, background text color - - // Draw speed - tft.setTextSize((liveData->params.speedKmh > 99) ? 1 : 2); - sprintf(tmpStr3, "0"); - if (liveData->params.speedKmh > 10) - sprintf(tmpStr3, "%01.00f", km2distance(liveData->params.speedKmh)); - tft.drawString(tmpStr3, 240, 0, 8); - - // Draw power kWh/100km (>25kmh) else kW - tft.setTextSize(1); - if (liveData->params.speedKmh > 25 && liveData->params.batPowerKw < 0) - sprintf(tmpStr3, "%01.01f", km2distance(liveData->params.batPowerKwh100)); - else - sprintf(tmpStr3, "%01.01f", liveData->params.batPowerKw); - tft.drawString(tmpStr3, 240, 150, 8); - - // Draw soc% - sprintf(tmpStr3, "%01.00f", liveData->params.socPerc); - tft.drawString(tmpStr3, 240 , 230, 8); - - // Cold gate cirlce - tft.fillCircle(30, 280, 25, (liveData->params.batTempC >= 15) ? ((liveData->params.batTempC >= 25) ? TFT_DARKGREEN2 : TFT_BLUE) : TFT_RED); - - // Brake lights - tft.fillRect(0, 310, 240, 10, (liveData->params.brakeLights) ? TFT_RED : TFT_BLACK); - - return true; - } - - // - spr.fillRect(0, 36, 200, 160, TFT_DARKRED); - - posx = 320 / 2; - posy = 40; - spr.setTextDatum(TR_DATUM); - spr.setTextColor(TFT_WHITE, TFT_DARKRED); - spr.setTextSize(2); // Size for small 5cix7 font - sprintf(tmpStr3, "0"); - if (liveData->params.speedKmh > 10) - sprintf(tmpStr3, "%01.00f", km2distance(liveData->params.speedKmh)); - spr.drawString(tmpStr3, 200, posy, 7); - - posy = 145; - spr.setTextDatum(TR_DATUM); // Top center - spr.setTextSize(1); - if (liveData->params.speedKmh > 25 && liveData->params.batPowerKw < 0) { - sprintf(tmpStr3, "%01.01f", km2distance(liveData->params.batPowerKwh100)); - } else { - sprintf(tmpStr3, "%01.01f", liveData->params.batPowerKw); - } - spr.drawString(tmpStr3, 200, posy, 7); - - // Bottom 2 numbers with charged/discharged kWh from start - spr.setFreeFont(&Roboto_Thin_24); - spr.setTextColor(TFT_WHITE, TFT_BLACK); - posx = 5; - posy = 5; - spr.setTextDatum(TL_DATUM); - sprintf(tmpStr3, ((liveData->settings.distanceUnit == 'k') ? "%01.00fkm " : "%01.00fmi "), km2distance(liveData->params.odoKm)); - spr.drawString(tmpStr3, posx, posy, GFXFF); - if (liveData->params.motorRpm > -1) { - spr.setTextDatum(TR_DATUM); - sprintf(tmpStr3, " %01.00frpm" , liveData->params.motorRpm); - spr.drawString(tmpStr3, 320 - posx, posy, GFXFF); - } - - // Bottom info - // Cummulative regen/power - posy = 240 - 5; - sprintf(tmpStr3, "-%01.01f ", liveData->params.cumulativeEnergyDischargedKWh - liveData->params.cumulativeEnergyDischargedKWhStart); - spr.setTextDatum(BL_DATUM); - spr.drawString(tmpStr3, posx, posy, GFXFF); - posx = 320 - 5; - sprintf(tmpStr3, " +%01.01f", liveData->params.cumulativeEnergyChargedKWh - liveData->params.cumulativeEnergyChargedKWhStart); - spr.setTextDatum(BR_DATUM); - spr.drawString(tmpStr3, posx, posy, GFXFF); - // Bat.power - posx = 320 / 2; - sprintf(tmpStr3, " %01.01fkw ", liveData->params.batPowerKw); - spr.setTextDatum(BC_DATUM); - spr.drawString(tmpStr3, posx, posy, GFXFF); - - // RIGHT INFO - // Battery "cold gate" detection - red < 15C (43KW limit), <25 (blue - 55kW limit), green all ok - spr.fillCircle(290, 60, 25, (liveData->params.batTempC >= 15) ? ((liveData->params.batTempC >= 25) ? TFT_DARKGREEN2 : TFT_BLUE) : TFT_RED); - spr.setTextColor(TFT_WHITE, (liveData->params.batTempC >= 15) ? ((liveData->params.batTempC >= 25) ? TFT_DARKGREEN2 : TFT_BLUE) : TFT_RED); - spr.setFreeFont(&Roboto_Thin_24); - spr.setTextDatum(MC_DATUM); - sprintf(tmpStr3, "%01.00f", celsius2temperature(liveData->params.batTempC)); - spr.drawString(tmpStr3, 290, 60, GFXFF); - // Brake lights - spr.fillRect(210, 40, 40, 40, (liveData->params.brakeLights) ? TFT_RED : TFT_BLACK); - - // Soc%, bat.kWh - spr.setFreeFont(&Orbitron_Light_32); - spr.setTextColor(TFT_WHITE, TFT_BLACK); - spr.setTextDatum(TR_DATUM); - sprintf(tmpStr3, " %01.00f%%", liveData->params.socPerc); - spr.drawString(tmpStr3, 320, 94, GFXFF); - if (liveData->params.socPerc > 0) { - float capacity = liveData->params.batteryTotalAvailableKWh * (liveData->params.socPerc / 100); - // calibration for Niro/Kona, real available capacity is ~66.5kWh, 0-10% ~6.2kWh, 90-100% ~7.2kWh - if (liveData->settings.carType == CAR_KIA_ENIRO_2020_64 || liveData->settings.carType == CAR_HYUNDAI_KONA_2020_64) { - capacity = (liveData->params.socPerc * 0.615) * (1 + (liveData->params.socPerc * 0.0008)); - } - sprintf(tmpStr3, " %01.01f", capacity); - spr.drawString(tmpStr3, 320, 129, GFXFF); - spr.drawString("kWh", 320, 164, GFXFF); - } - - return true; -} - -/** - Battery cells (Screen 3) -*/ -bool drawSceneBatteryCells() { - - int32_t posx, posy; - - sprintf(tmpStr1, ((liveData->settings.temperatureUnit == 'c') ? "%01.00f C" : "%01.01f F"), celsius2temperature(liveData->params.batHeaterC)); - drawSmallRect(0, 0, 1, 1, tmpStr1, "HEATER", TFT_TEMP, TFT_CYAN); - sprintf(tmpStr1, ((liveData->settings.temperatureUnit == 'c') ? "%01.00f C" : "%01.01f F"), celsius2temperature(liveData->params.batInletC)); - drawSmallRect(1, 0, 1, 1, tmpStr1, "BAT.INLET", TFT_TEMP, TFT_CYAN); - sprintf(tmpStr1, ((liveData->settings.temperatureUnit == 'c') ? "%01.00f C" : "%01.01f F"), celsius2temperature(liveData->params.batModuleTempC[0])); - drawSmallRect(0, 1, 1, 1, tmpStr1, "MO1", TFT_TEMP, (liveData->params.batModuleTempC[0] >= 15) ? ((liveData->params.batModuleTempC[0] >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED); - sprintf(tmpStr1, ((liveData->settings.temperatureUnit == 'c') ? "%01.00f C" : "%01.01f F"), celsius2temperature(liveData->params.batModuleTempC[1])); - drawSmallRect(1, 1, 1, 1, tmpStr1, "MO2", TFT_TEMP, (liveData->params.batModuleTempC[1] >= 15) ? ((liveData->params.batModuleTempC[1] >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED); - sprintf(tmpStr1, ((liveData->settings.temperatureUnit == 'c') ? "%01.00f C" : "%01.01f F"), celsius2temperature(liveData->params.batModuleTempC[2])); - drawSmallRect(2, 1, 1, 1, tmpStr1, "MO3", TFT_TEMP, (liveData->params.batModuleTempC[2] >= 15) ? ((liveData->params.batModuleTempC[2] >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED); - sprintf(tmpStr1, ((liveData->settings.temperatureUnit == 'c') ? "%01.00f C" : "%01.01f F"), celsius2temperature(liveData->params.batModuleTempC[3])); - drawSmallRect(3, 1, 1, 1, tmpStr1, "MO4", TFT_TEMP, (liveData->params.batModuleTempC[3] >= 15) ? ((liveData->params.batModuleTempC[3] >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED); - // Ioniq (up to 12 cells) - for (uint16_t i = 4; i < liveData->params.batModuleTempCount; i++) { - if (liveData->params.batModuleTempC[i] == 0) - continue; - posx = (((i - 4) % 8) * 40); - posy = ((floor((i - 4) / 8)) * 13) + 64; - //spr.fillRect(x * 80, y * 32, ((w) * 80), ((h) * 32), bgColor); - spr.setTextSize(1); // Size for small 5x7 font - spr.setTextDatum(TL_DATUM); - spr.setTextColor(((liveData->params.batModuleTempC[i] >= 15) ? ((liveData->params.batModuleTempC[i] >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED), TFT_BLACK); - sprintf(tmpStr1, ((liveData->settings.temperatureUnit == 'c') ? "%01.00fC" : "%01.01fF"), celsius2temperature(liveData->params.batModuleTempC[i])); - spr.drawString(tmpStr1, posx + 4, posy, 2); - } - - spr.setTextDatum(TL_DATUM); // Topleft - spr.setTextSize(1); // Size for small 5x7 font - - // Find min and max val - float minVal = -1, maxVal = -1; - for (int i = 0; i < 98; i++) { - if ((liveData->params.cellVoltage[i] < minVal || minVal == -1) && liveData->params.cellVoltage[i] != -1) - minVal = liveData->params.cellVoltage[i]; - if ((liveData->params.cellVoltage[i] > maxVal || maxVal == -1) && liveData->params.cellVoltage[i] != -1) - maxVal = liveData->params.cellVoltage[i]; - if (liveData->params.cellVoltage[i] > 0 && i > liveData->params.cellCount + 1) - liveData->params.cellCount = i + 1; - } - - // Draw cell matrix - for (int i = 0; i < 98; i++) { - if (liveData->params.cellVoltage[i] == -1) - continue; - posx = ((i % 8) * 40) + 4; - posy = ((floor(i / 8) + (liveData->params.cellCount > 96 ? 0 : 1)) * 13) + 68; - sprintf(tmpStr3, "%01.02f", liveData->params.cellVoltage[i]); - spr.setTextColor(TFT_NAVY, TFT_BLACK); - if (liveData->params.cellVoltage[i] == minVal && minVal != maxVal) - spr.setTextColor(TFT_RED, TFT_BLACK); - if (liveData->params.cellVoltage[i] == maxVal && minVal != maxVal) - spr.setTextColor(TFT_GREEN, TFT_BLACK); - spr.drawString(tmpStr3, posx, posy, 2); - } - - return true; -} - -/** - drawPreDrawnChargingGraphs - P = U.I -*/ -bool drawPreDrawnChargingGraphs(int zeroX, int zeroY, int mulX, int mulY) { - - // Rapid gate - spr.drawLine(zeroX + (/* SOC FROM */ 1 * mulX), zeroY - (/*I*/ 180 * /*U SOC*/ (1 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 57 * mulX), zeroY - (/*I*/ 180 * /*U SOC*/ (57 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_RAPIDGATE35); - // Coldgate <5C - spr.drawLine(zeroX + (/* SOC FROM */ 1 * mulX), zeroY - (/*I*/ 60 * /*U SOC*/ (1 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 65 * mulX), zeroY - (/*I*/ 60 * /*U SOC*/ (65 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_COLDGATE0_5); - // Coldgate 5-14C - spr.drawLine(zeroX + (/* SOC FROM */ 1 * mulX), zeroY - (/*I*/ 110 * /*U SOC*/ (1 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 57 * mulX), zeroY - (/*I*/ 110 * /*U SOC*/ (57 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_COLDGATE5_14); - spr.drawLine(zeroX + (/* SOC FROM */ 57 * mulX), zeroY - (/*I*/ 110 * /*U SOC*/ (57 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 58 * mulX), zeroY - (/*I*/ 75 * /*U SOC*/ (58 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_COLDGATE5_14); - spr.drawLine(zeroX + (/* SOC FROM */ 58 * mulX), zeroY - (/*I*/ 75 * /*U SOC*/ (58 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 64 * mulX), zeroY - (/*I*/ 75 * /*U SOC*/ (64 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_COLDGATE5_14); - spr.drawLine(zeroX + (/* SOC FROM */ 64 * mulX), zeroY - (/*I*/ 75 * /*U SOC*/ (64 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 65 * mulX), zeroY - (/*I*/ 60 * /*U SOC*/ (65 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_COLDGATE5_14); - spr.drawLine(zeroX + (/* SOC FROM */ 65 * mulX), zeroY - (/*I*/ 60 * /*U SOC*/ (65 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 82 * mulX), zeroY - (/*I*/ 60 * /*U SOC*/ (82 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_COLDGATE5_14); - spr.drawLine(zeroX + (/* SOC FROM */ 82 * mulX), zeroY - (/*I*/ 60 * /*U SOC*/ (82 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 83 * mulX), zeroY - (/*I*/ 40 * /*U SOC*/ (83 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_COLDGATE5_14); - // Coldgate 15-24C - spr.drawLine(zeroX + (/* SOC FROM */ 1 * mulX), zeroY - (/*I*/ 150 * /*U SOC*/ (1 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 57 * mulX), zeroY - (/*I*/ 150 * /*U SOC*/ (57 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_COLDGATE15_24); - spr.drawLine(zeroX + (/* SOC FROM */ 57 * mulX), zeroY - (/*I*/ 150 * /*U SOC*/ (57 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC FROM */ 58 * mulX), zeroY - (/*I*/ 110 * /*U SOC*/ (58 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_COLDGATE15_24); - spr.drawLine(zeroX + (/* SOC TO */ 58 * mulX), zeroY - (/*I*/ 110 * /*U SOC*/ (58 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 78 * mulX), zeroY - (/*I*/ 110 * /*U SOC*/ (78 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_COLDGATE15_24); - // Optimal - spr.drawLine(zeroX + (/* SOC FROM */ 1 * mulX), zeroY - (/*I*/ 200 * /*U SOC*/ (1 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 57 * mulX), zeroY - (/*I*/ 200 * /*U SOC*/ (57 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); - spr.drawLine(zeroX + (/* SOC FROM */ 51 * mulX), zeroY - (/*I*/ 200 * /*U SOC*/ (51 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 51 * mulX), zeroY - (/*I*/ 195 * /*U SOC*/ (51 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); - spr.drawLine(zeroX + (/* SOC FROM */ 53 * mulX), zeroY - (/*I*/ 200 * /*U SOC*/ (53 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 53 * mulX), zeroY - (/*I*/ 195 * /*U SOC*/ (53 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); - spr.drawLine(zeroX + (/* SOC FROM */ 55 * mulX), zeroY - (/*I*/ 200 * /*U SOC*/ (55 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 55 * mulX), zeroY - (/*I*/ 195 * /*U SOC*/ (55 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); - spr.drawLine(zeroX + (/* SOC FROM */ 57 * mulX), zeroY - (/*I*/ 200 * /*U SOC*/ (57 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 58 * mulX), zeroY - (/*I*/ 150 * /*U SOC*/ (58 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); - spr.drawLine(zeroX + (/* SOC FROM */ 58 * mulX), zeroY - (/*I*/ 150 * /*U SOC*/ (58 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 77 * mulX), zeroY - (/*I*/ 150 * /*U SOC*/ (77 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); - spr.drawLine(zeroX + (/* SOC FROM */ 71 * mulX), zeroY - (/*I*/ 150 * /*U SOC*/ (71 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 71 * mulX), zeroY - (/*I*/ 145 * /*U SOC*/ (71 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); - spr.drawLine(zeroX + (/* SOC FROM */ 73 * mulX), zeroY - (/*I*/ 150 * /*U SOC*/ (73 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 73 * mulX), zeroY - (/*I*/ 145 * /*U SOC*/ (73 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); - spr.drawLine(zeroX + (/* SOC FROM */ 75 * mulX), zeroY - (/*I*/ 150 * /*U SOC*/ (75 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 75 * mulX), zeroY - (/*I*/ 145 * /*U SOC*/ (75 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); - spr.drawLine(zeroX + (/* SOC FROM */ 77 * mulX), zeroY - (/*I*/ 150 * /*U SOC*/ (77 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 78 * mulX), zeroY - (/*I*/ 90 * /*U SOC*/ (78 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); - spr.drawLine(zeroX + (/* SOC FROM */ 78 * mulX), zeroY - (/*I*/ 90 * /*U SOC*/ (78 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 82 * mulX), zeroY - (/*I*/ 90 * /*U SOC*/ (82 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); - spr.drawLine(zeroX + (/* SOC FROM */ 82 * mulX), zeroY - (/*I*/ 90 * /*U SOC*/ (82 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 83 * mulX), zeroY - (/*I*/ 60 * /*U SOC*/ (83 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); - spr.drawLine(zeroX + (/* SOC FROM */ 83 * mulX), zeroY - (/*I*/ 60 * /*U SOC*/ (83 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 92 * mulX), zeroY - (/*I*/ 60 * /*U SOC*/ (92 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); - spr.drawLine(zeroX + (/* SOC FROM */ 92 * mulX), zeroY - (/*I*/ 60 * /*U SOC*/ (92 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 95 * mulX), zeroY - (/*I*/ 35 * /*U SOC*/ (95 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); - spr.drawLine(zeroX + (/* SOC FROM */ 95 * mulX), zeroY - (/*I*/ 35 * /*U SOC*/ (95 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 98 * mulX), zeroY - (/*I*/ 35 * /*U SOC*/ (98 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); - spr.drawLine(zeroX + (/* SOC FROM */ 98 * mulX), zeroY - (/*I*/ 35 * /*U SOC*/ (98 * 55 / 100 + 352) /**/ / 1000 * mulY), - zeroX + (/* SOC TO */ 100 * mulX), zeroY - (/*I*/ 15 * /*U SOC*/ (100 * 55 / 100 + 352) /**/ / 1000 * mulY), TFT_GRAPH_OPTIMAL25); - // Triangles - int x = zeroX; - int y; - if (liveData->params.batMaxC >= 35) { - y = zeroY - (/*I*/ 180 * /*U SOC*/ (1 * 55 / 100 + 352) /**/ / 1000 * mulY); - } else if (liveData->params.batMinC >= 25) { - y = zeroY - (/*I*/ 200 * /*U SOC*/ (1 * 55 / 100 + 352) /**/ / 1000 * mulY); - } else if (liveData->params.batMinC >= 15) { - y = zeroY - (/*I*/ 150 * /*U SOC*/ (1 * 55 / 100 + 352) /**/ / 1000 * mulY); - } else if (liveData->params.batMinC >= 5) { - y = zeroY - (/*I*/ 110 * /*U SOC*/ (1 * 55 / 100 + 352) /**/ / 1000 * mulY); - } else { - y = zeroY - (/*I*/ 60 * /*U SOC*/ (1 * 55 / 100 + 352) /**/ / 1000 * mulY); - } - spr.fillTriangle(x + 5, y, x , y - 5, x, y + 5, TFT_ORANGE); -} - -/** - Charging graph (Screen 4) -*/ -bool drawSceneChargingGraph() { - - int zeroX = 0; - int zeroY = 238; - int mulX = 3; // 100% = 300px; - int mulY = 2; // 100kW = 200px - int maxKw = 80; - int posy = 0; - uint16_t color; - - spr.fillSprite(TFT_BLACK); - - sprintf(tmpStr1, "%01.00f", liveData->params.socPerc); - drawSmallRect(0, 0, 1, 1, tmpStr1, "SOC", TFT_TEMP, TFT_CYAN); - sprintf(tmpStr1, "%01.01f", liveData->params.batPowerKw); - drawSmallRect(1, 0, 1, 1, tmpStr1, "POWER kW", TFT_TEMP, TFT_CYAN); - sprintf(tmpStr1, "%01.01f", liveData->params.batPowerAmp); - drawSmallRect(2, 0, 1, 1, tmpStr1, "CURRENT A", TFT_TEMP, TFT_CYAN); - sprintf(tmpStr1, "%03.00f", liveData->params.batVoltage); - drawSmallRect(3, 0, 1, 1, tmpStr1, "VOLTAGE", TFT_TEMP, TFT_CYAN); - - sprintf(tmpStr1, ((liveData->settings.temperatureUnit == 'c') ? "%01.00f C" : "%01.01f F"), celsius2temperature(liveData->params.batHeaterC)); - drawSmallRect(0, 1, 1, 1, tmpStr1, "HEATER", TFT_TEMP, TFT_RED); - sprintf(tmpStr1, ((liveData->settings.temperatureUnit == 'c') ? "%01.00f C" : "%01.01f F"), celsius2temperature(liveData->params.batInletC)); - drawSmallRect(1, 1, 1, 1, tmpStr1, "BAT.INLET", TFT_TEMP, TFT_CYAN); - sprintf(tmpStr1, ((liveData->settings.temperatureUnit == 'c') ? "%01.00f C" : "%01.01f F"), celsius2temperature(liveData->params.batMinC)); - drawSmallRect(2, 1, 1, 1, tmpStr1, "BAT.MIN", (liveData->params.batMinC >= 15) ? ((liveData->params.batMinC >= 25) ? TFT_DARKGREEN2 : TFT_BLUE) : TFT_RED, TFT_CYAN); - sprintf(tmpStr1, ((liveData->settings.temperatureUnit == 'c') ? "%01.00f C" : "%01.01f F"), celsius2temperature(liveData->params.outdoorTemperature)); - drawSmallRect(3, 1, 1, 1, tmpStr1, "OUT.TEMP.", TFT_TEMP, TFT_CYAN); - - spr.setTextColor(TFT_SILVER, TFT_TEMP); - - for (int i = 0; i <= 10; i++) { - color = TFT_DARKRED2; - if (i == 0 || i == 5 || i == 10) - color = TFT_DARKRED; - spr.drawFastVLine(zeroX + (i * 10 * mulX), zeroY - (maxKw * mulY), maxKw * mulY, color); - /*if (i != 0 && i != 10) { - sprintf(tmpStr1, "%d%%", i * 10); - spr.setTextDatum(BC_DATUM); - spr.drawString(tmpStr1, zeroX + (i * 10 * mulX), zeroY - (maxKw * mulY), 2); - }*/ - if (i <= (maxKw / 10)) { - spr.drawFastHLine(zeroX, zeroY - (i * 10 * mulY), 100 * mulX, color); - if (i > 0) { - sprintf(tmpStr1, "%d", i * 10); - spr.setTextDatum(ML_DATUM); - spr.drawString(tmpStr1, zeroX + (100 * mulX) + 3, zeroY - (i * 10 * mulY), 2); - } - } - } - - // Draw suggested curves - if (liveData->settings.predrawnChargingGraphs == 1) { - drawPreDrawnChargingGraphs(zeroX, zeroY, mulX, mulY); - } - - // Draw realtime values - for (int i = 0; i <= 100; i++) { - if (liveData->params.chargingGraphBatMinTempC[i] > -10) - spr.drawFastHLine(zeroX + (i * mulX) - (mulX / 2), zeroY - (liveData->params.chargingGraphBatMinTempC[i]*mulY), mulX, TFT_BLUE); - if (liveData->params.chargingGraphBatMaxTempC[i] > -10) - spr.drawFastHLine(zeroX + (i * mulX) - (mulX / 2), zeroY - (liveData->params.chargingGraphBatMaxTempC[i]*mulY), mulX, TFT_BLUE); - if (liveData->params.chargingGraphWaterCoolantTempC[i] > -10) - spr.drawFastHLine(zeroX + (i * mulX) - (mulX / 2), zeroY - (liveData->params.chargingGraphWaterCoolantTempC[i]*mulY), mulX, TFT_PURPLE); - if (liveData->params.chargingGraphHeaterTempC[i] > -10) - spr.drawFastHLine(zeroX + (i * mulX) - (mulX / 2), zeroY - (liveData->params.chargingGraphHeaterTempC[i]*mulY), mulX, TFT_RED); - - if (liveData->params.chargingGraphMinKw[i] > 0) - spr.drawFastHLine(zeroX + (i * mulX) - (mulX / 2), zeroY - (liveData->params.chargingGraphMinKw[i]*mulY), mulX, TFT_GREENYELLOW); - if (liveData->params.chargingGraphMaxKw[i] > 0) - spr.drawFastHLine(zeroX + (i * mulX) - (mulX / 2), zeroY - (liveData->params.chargingGraphMaxKw[i]*mulY), mulX, TFT_YELLOW); - } - - // Bat.module temperatures - spr.setTextSize(1); // Size for small 5x7 font - spr.setTextDatum(BL_DATUM); - sprintf(tmpStr1, ((liveData->settings.temperatureUnit == 'c') ? "1=%01.00fC " : "1=%01.00fF "), celsius2temperature(liveData->params.batModuleTempC[0])); - spr.setTextColor((liveData->params.batModuleTempC[0] >= 15) ? ((liveData->params.batModuleTempC[0] >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED, TFT_TEMP); - spr.drawString(tmpStr1, 0, zeroY - (maxKw * mulY), 2); - - sprintf(tmpStr1, ((liveData->settings.temperatureUnit == 'c') ? "2=%01.00fC " : "2=%01.00fF "), celsius2temperature(liveData->params.batModuleTempC[1])); - spr.setTextColor((liveData->params.batModuleTempC[1] >= 15) ? ((liveData->params.batModuleTempC[1] >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED, TFT_TEMP); - spr.drawString(tmpStr1, 48, zeroY - (maxKw * mulY), 2); - - sprintf(tmpStr1, ((liveData->settings.temperatureUnit == 'c') ? "3=%01.00fC " : "3=%01.00fF "), celsius2temperature(liveData->params.batModuleTempC[2])); - spr.setTextColor((liveData->params.batModuleTempC[2] >= 15) ? ((liveData->params.batModuleTempC[2] >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED, TFT_TEMP); - spr.drawString(tmpStr1, 96, zeroY - (maxKw * mulY), 2); - - sprintf(tmpStr1, ((liveData->settings.temperatureUnit == 'c') ? "4=%01.00fC " : "4=%01.00fF "), celsius2temperature(liveData->params.batModuleTempC[3])); - spr.setTextColor((liveData->params.batModuleTempC[3] >= 15) ? ((liveData->params.batModuleTempC[3] >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED, TFT_TEMP); - spr.drawString(tmpStr1, 144, zeroY - (maxKw * mulY), 2); - sprintf(tmpStr1, "ir %01.00fkOhm", liveData->params.isolationResistanceKOhm ); - - // Bms max.regen/power available - spr.setTextColor(TFT_WHITE, TFT_BLACK); - sprintf(tmpStr1, "xC=%01.00fkW ", liveData->params.availableChargePower); - spr.drawString(tmpStr1, 192, zeroY - (maxKw * mulY), 2); - spr.setTextColor(TFT_WHITE, TFT_BLACK); - sprintf(tmpStr1, "xD=%01.00fkW", liveData->params.availableDischargePower); - spr.drawString(tmpStr1, 256, zeroY - (maxKw * mulY), 2); - - // - spr.setTextDatum(TR_DATUM); - if (liveData->params.coolingWaterTempC != -1) { - sprintf(tmpStr1, ((liveData->settings.temperatureUnit == 'c') ? "W=%01.00fC" : "W=%01.00fF"), celsius2temperature(liveData->params.coolingWaterTempC)); - spr.setTextColor(TFT_PURPLE, TFT_TEMP); - spr.drawString(tmpStr1, zeroX + (10 * 10 * mulX), zeroY - (maxKw * mulY) + (posy * 15), 2); - posy++; - } - spr.setTextColor(TFT_WHITE, TFT_TEMP); - if (liveData->params.batFanFeedbackHz > 0) { - sprintf(tmpStr1, "FF=%03.00fHz", liveData->params.batFanFeedbackHz); - spr.drawString(tmpStr1, zeroX + (10 * 10 * mulX), zeroY - (maxKw * mulY) + (posy * 15), 2); - posy++; - } - if (liveData->params.batFanStatus > 0) { - sprintf(tmpStr1, "FS=%03.00f", liveData->params.batFanStatus); - spr.drawString(tmpStr1, zeroX + (10 * 10 * mulX), zeroY - (maxKw * mulY) + (posy * 15), 2); - posy++; - } - if (liveData->params.coolantTemp1C != -1 && liveData->params.coolantTemp2C != -1) { - sprintf(tmpStr1, ((liveData->settings.temperatureUnit == 'c') ? "C1/2:%01.00f/%01.00fC" : "C1/2:%01.00f/%01.00fF"), celsius2temperature(liveData->params.coolantTemp1C), celsius2temperature(liveData->params.coolantTemp2C)); - spr.drawString(tmpStr1, zeroX + (10 * 10 * mulX), zeroY - (maxKw * mulY) + (posy * 15), 2); - posy++; - } - if (liveData->params.bmsUnknownTempA != -1) { - sprintf(tmpStr1, ((liveData->settings.temperatureUnit == 'c') ? "A=%01.00fC" : "W=%01.00fF"), celsius2temperature(liveData->params.bmsUnknownTempA)); - spr.drawString(tmpStr1, zeroX + (10 * 10 * mulX), zeroY - (maxKw * mulY) + (posy * 15), 2); - posy++; - } - if (liveData->params.bmsUnknownTempB != -1) { - sprintf(tmpStr1, ((liveData->settings.temperatureUnit == 'c') ? "B=%01.00fC" : "W=%01.00fF"), celsius2temperature(liveData->params.bmsUnknownTempB)); - spr.drawString(tmpStr1, zeroX + (10 * 10 * mulX), zeroY - (maxKw * mulY) + (posy * 15), 2); - posy++; - } - if (liveData->params.bmsUnknownTempC != -1) { - sprintf(tmpStr1, ((liveData->settings.temperatureUnit == 'c') ? "C=%01.00fC" : "W=%01.00fF"), celsius2temperature(liveData->params.bmsUnknownTempC)); - spr.drawString(tmpStr1, zeroX + (10 * 10 * mulX), zeroY - (maxKw * mulY) + (posy * 15), 2); - posy++; - } - if (liveData->params.bmsUnknownTempD != -1) { - sprintf(tmpStr1, ((liveData->settings.temperatureUnit == 'c') ? "D=%01.00fC" : "W=%01.00fF"), celsius2temperature(liveData->params.bmsUnknownTempD)); - spr.drawString(tmpStr1, zeroX + (10 * 10 * mulX), zeroY - (maxKw * mulY) + (posy * 15), 2); - posy++; - } - - // Print charging time - time_t diffTime = liveData->params.currentTime - liveData->params.chargingStartTime; - if ((diffTime / 60) > 99) - sprintf(tmpStr1, "%02d:%02d:%02d", (diffTime / 3600) % 24, (diffTime / 60) % 60, diffTime % 60); - else - sprintf(tmpStr1, "%02d:%02d", (diffTime / 60), diffTime % 60); - spr.setTextDatum(TL_DATUM); - spr.setTextColor(TFT_SILVER, TFT_BLACK); - spr.drawString(tmpStr1, 0, zeroY - (maxKw * mulY), 2); - - // Debug info - if (debugTmpCharging) { - if (liveData->params.currentTime % 10 > 5) { - int32_t posx, posy; - String chHex, chHex2, chRef; - uint8_t chByte; - - spr.setTextSize(1); // Size for small 5x7 font - spr.setTextColor(TFT_SILVER, TFT_TEMP); - spr.setTextDatum(TR_DATUM); - // tft.fillRect(0, 240-104, 320, 104, TFT_BLACK); - - for (int i = 0; i < debugTmpChargingLast05.length() / 2; i++) { - chHex = debugTmpChargingLast05.substring(i * 2, (i * 2) + 2); - chHex2 = debugTmpChargingPrevious05.substring(i * 2, (i * 2) + 2); - chRef = debugTmpChargingRef05.substring(i * 2, (i * 2) + 2); - if (chRef.equals("--") || chRef.equals(chHex)) - continue; - spr.setTextColor(((chHex.equals(chHex2)) ? TFT_SILVER : TFT_GREEN), TFT_TEMP); - chByte = liveData->hexToDec(chHex.c_str(), 1, false); - posx = (((i) % 10) * 32) + 24; - posy = ((floor((i) / 10)) * 13) + 240 - 104; - sprintf(tmpStr1, "%03d", chByte); - spr.drawString(tmpStr1, posx + 4, posy, 2); - } - for (int i = 0; i < debugTmpChargingLast06.length() / 2; i++) { - chHex = debugTmpChargingLast06.substring(i * 2, (i * 2) + 2); - chHex2 = debugTmpChargingPrevious06.substring(i * 2, (i * 2) + 2); - chRef = debugTmpChargingRef06.substring(i * 2, (i * 2) + 2); - if (chRef.equals("--") || chRef.equals(chHex)) - continue; - spr.setTextColor(((chHex.equals(chHex2)) ? TFT_SILVER : TFT_GREEN), TFT_TEMP); - chByte = liveData->hexToDec(chHex.c_str(), 1, false); - posx = (((i) % 10) * 32) + 24; - posy = ((floor((i) / 10)) * 13) + 65 + 240 - 104; - sprintf(tmpStr1, "%03d", chByte); - spr.drawString(tmpStr1, posx + 4, posy, 2); - } - - debugTmpChargingPrevious05 = debugTmpChargingLast05; - debugTmpChargingPrevious06 = debugTmpChargingLast06; - } - } - - return true; -} - -/** - SOC 10% table (screen 5) -*/ -bool drawSceneSoc10Table() { - - int zeroY = 2; - float diffCec, diffCed, diffOdo = -1; - float firstCed = -1, lastCed = -1, diffCed0to5 = 0; - float firstCec = -1, lastCec = -1, diffCec0to5 = 0; - float firstOdo = -1, lastOdo = -1, diffOdo0to5 = 0; - float diffTime; - spr.setTextSize(1); // Size for small 5x7 font - spr.setTextColor(TFT_SILVER, TFT_TEMP); - spr.setTextDatum(TL_DATUM); - spr.drawString("CONSUMPTION | DISCH.100%->4% SOC", 2, zeroY, 2); - - spr.setTextDatum(TR_DATUM); - - spr.drawString("dis./char.kWh", 128, zeroY + (1 * 15), 2); - spr.drawString(((liveData->settings.distanceUnit == 'k') ? "km" : "mi"), 160, zeroY + (1 * 15), 2); - spr.drawString("kWh100", 224, zeroY + (1 * 15), 2); - spr.drawString("avg.speed", 310, zeroY + (1 * 15), 2); - - for (int i = 0; i <= 10; i++) { - sprintf(tmpStr1, "%d%%", (i == 0) ? 5 : i * 10); - spr.drawString(tmpStr1, 32, zeroY + ((12 - i) * 15), 2); - - firstCed = (liveData->params.soc10ced[i] != -1) ? liveData->params.soc10ced[i] : firstCed; - lastCed = (lastCed == -1 && liveData->params.soc10ced[i] != -1) ? liveData->params.soc10ced[i] : lastCed; - firstCec = (liveData->params.soc10cec[i] != -1) ? liveData->params.soc10cec[i] : firstCec; - lastCec = (lastCec == -1 && liveData->params.soc10cec[i] != -1) ? liveData->params.soc10cec[i] : lastCec; - firstOdo = (liveData->params.soc10odo[i] != -1) ? liveData->params.soc10odo[i] : firstOdo; - lastOdo = (lastOdo == -1 && liveData->params.soc10odo[i] != -1) ? liveData->params.soc10odo[i] : lastOdo; - - if (i != 10) { - diffCec = (liveData->params.soc10cec[i + 1] != -1 && liveData->params.soc10cec[i] != -1) ? (liveData->params.soc10cec[i] - liveData->params.soc10cec[i + 1]) : 0; - diffCed = (liveData->params.soc10ced[i + 1] != -1 && liveData->params.soc10ced[i] != -1) ? (liveData->params.soc10ced[i + 1] - liveData->params.soc10ced[i]) : 0; - diffOdo = (liveData->params.soc10odo[i + 1] != -1 && liveData->params.soc10odo[i] != -1) ? (liveData->params.soc10odo[i] - liveData->params.soc10odo[i + 1]) : -1; - diffTime = (liveData->params.soc10time[i + 1] != -1 && liveData->params.soc10time[i] != -1) ? (liveData->params.soc10time[i] - liveData->params.soc10time[i + 1]) : -1; - if (diffCec != 0) { - sprintf(tmpStr1, "+%01.01f", diffCec); - spr.drawString(tmpStr1, 128, zeroY + ((12 - i) * 15), 2); - diffCec0to5 = (i == 0) ? diffCec : diffCec0to5; - } - if (diffCed != 0) { - sprintf(tmpStr1, "%01.01f", diffCed); - spr.drawString(tmpStr1, 80, zeroY + ((12 - i) * 15), 2); - diffCed0to5 = (i == 0) ? diffCed : diffCed0to5; - } - if (diffOdo != -1) { - sprintf(tmpStr1, "%01.00f", km2distance(diffOdo)); - spr.drawString(tmpStr1, 160, zeroY + ((12 - i) * 15), 2); - diffOdo0to5 = (i == 0) ? diffOdo : diffOdo0to5; - if (diffTime > 0) { - sprintf(tmpStr1, "%01.01f", km2distance(diffOdo) / (diffTime / 3600)); - spr.drawString(tmpStr1, 310, zeroY + ((12 - i) * 15), 2); - } - } - if (diffOdo > 0 && diffCed != 0) { - sprintf(tmpStr1, "%01.1f", (-diffCed * 100.0 / km2distance(diffOdo))); - spr.drawString(tmpStr1, 224, zeroY + ((12 - i) * 15), 2); - } - } - - if (diffOdo == -1 && liveData->params.soc10odo[i] != -1) { - sprintf(tmpStr1, "%01.00f", km2distance(liveData->params.soc10odo[i])); - spr.drawString(tmpStr1, 160, zeroY + ((12 - i) * 15), 2); - } - } - - spr.drawString("0%", 32, zeroY + (13 * 15), 2); - spr.drawString("0-5% is calculated (same) as 5-10%", 310, zeroY + (13 * 15), 2); - - spr.drawString("TOT.", 32, zeroY + (14 * 15), 2); - diffCed = (lastCed != -1 && firstCed != -1) ? firstCed - lastCed + diffCed0to5 : 0; - sprintf(tmpStr1, "%01.01f", diffCed); - spr.drawString(tmpStr1, 80, zeroY + (14 * 15), 2); - diffCec = (lastCec != -1 && firstCec != -1) ? lastCec - firstCec + diffCec0to5 : 0; - sprintf(tmpStr1, "+%01.01f", diffCec); - spr.drawString(tmpStr1, 128, zeroY + (14 * 15), 2); - diffOdo = (lastOdo != -1 && firstOdo != -1) ? lastOdo - firstOdo + diffOdo0to5 : 0; - sprintf(tmpStr1, "%01.00f", km2distance(diffOdo)); - spr.drawString(tmpStr1, 160, zeroY + (14 * 15), 2); - sprintf(tmpStr1, "AVAIL.CAP: %01.01f kWh", -diffCed - diffCec); - spr.drawString(tmpStr1, 310, zeroY + (14 * 15), 2); - - return true; -} - -/** - DEBUG screen -*/ -bool drawSceneDebug() { - - int32_t posx, posy; - String chHex, chHex2; - uint8_t chByte; - - spr.setTextSize(1); // Size for small 5x7 font - spr.setTextColor(TFT_SILVER, TFT_TEMP); - spr.setTextDatum(TL_DATUM); - spr.drawString(debugAtshRequest, 0, 0, 2); - spr.drawString(debugCommandRequest, 128, 0, 2); - spr.drawString(liveData->commandRequest, 256, 0, 2); - spr.setTextDatum(TR_DATUM); - - for (int i = 0; i < debugLastString.length() / 2; i++) { - chHex = debugLastString.substring(i * 2, (i * 2) + 2); - chHex2 = debugPreviousString.substring(i * 2, (i * 2) + 2); - spr.setTextColor(((chHex.equals(chHex2)) ? TFT_SILVER : TFT_GREEN), TFT_TEMP); - chByte = liveData->hexToDec(chHex.c_str(), 1, false); - posx = (((i) % 10) * 32) + 24; - posy = ((floor((i) / 10)) * 32) + 24; - sprintf(tmpStr1, "%03d", chByte); - spr.drawString(tmpStr1, posx + 4, posy, 2); - - spr.setTextColor(TFT_YELLOW, TFT_TEMP); - sprintf(tmpStr1, "%c", (char)chByte); - spr.drawString(tmpStr1, posx + 4, posy + 13, 2); - } - - debugPreviousString = debugLastString; - - return true; -} - -/** - Modify caption -*/ -String menuItemCaption(int16_t menuItemId, String title) { - - String prefix = "", suffix = ""; - - if (menuItemId == 10) // Version - suffix = APP_VERSION; - - if (menuItemId == 401) // distance - suffix = (liveData->settings.distanceUnit == 'k') ? "[km]" : "[mi]"; - if (menuItemId == 402) // temperature - suffix = (liveData->settings.temperatureUnit == 'c') ? "[C]" : "[F]"; - if (menuItemId == 403) // pressure - suffix = (liveData->settings.pressureUnit == 'b') ? "[bar]" : "[psi]"; - - title = ((prefix == "") ? "" : prefix + " ") + title + ((suffix == "") ? "" : " " + suffix); - return title; -} - -/** - Display menu -*/ -bool showMenu() { - - uint16_t posY = 0, tmpCurrMenuItem = 0; - - menuVisible = true; - - spr.fillSprite(TFT_BLACK); - spr.setTextDatum(TL_DATUM); - spr.setFreeFont(&Roboto_Thin_24); - - // Page scroll - uint8_t visibleCount = (int)(tft.height() / spr.fontHeight()); - if (menuItemSelected >= menuItemOffset + visibleCount) - menuItemOffset = menuItemSelected - visibleCount + 1; - if (menuItemSelected < menuItemOffset) - menuItemOffset = menuItemSelected; - - // Print items - for (uint16_t i = 0; i < menuItemsCount; ++i) { - if (menuCurrent == menuItems[i].parentId) { - if (tmpCurrMenuItem >= menuItemOffset) { - spr.fillRect(0, posY, 320, spr.fontHeight() + 2, (menuItemSelected == tmpCurrMenuItem) ? TFT_DARKGREEN2 : TFT_BLACK); - spr.setTextColor((menuItemSelected == tmpCurrMenuItem) ? TFT_WHITE : TFT_WHITE, (menuItemSelected == tmpCurrMenuItem) ? TFT_DARKGREEN2 : TFT_BLACK); - spr.drawString(menuItemCaption(menuItems[i].id, menuItems[i].title), 0, posY + 2, GFXFF); - posY += spr.fontHeight(); - } - tmpCurrMenuItem++; - } - } - - spr.pushSprite(0, 0); - - return true; -} - -/** - Hide menu -*/ -bool hideMenu() { - - menuVisible = false; - menuCurrent = 0; - menuItemSelected = 0; - redrawScreen(); - - return false; -} - -/** - Move in menu with left/right button -*/ -bool menuMove(bool forward) { - - if (forward) { - uint16_t tmpCount = 0; - for (uint16_t i = 0; i < menuItemsCount; ++i) { - if (menuCurrent == menuItems[i].parentId) { - tmpCount++; - } - } - menuItemSelected = (menuItemSelected >= tmpCount - 1 ) ? tmpCount - 1 : menuItemSelected + 1; - } else { - menuItemSelected = (menuItemSelected <= 0) ? 0 : menuItemSelected - 1; - } - showMenu(); - - return true; -} - -/** - Enter menu item -*/ -bool menuItemClick() { - - // Locate menu item for meta data - MENU_ITEM tmpMenuItem; - uint16_t tmpCurrMenuItem = 0; - for (uint16_t i = 0; i < menuItemsCount; ++i) { - if (menuCurrent == menuItems[i].parentId) { - if (menuItemSelected == tmpCurrMenuItem) { - tmpMenuItem = menuItems[i]; - break; - } - tmpCurrMenuItem++; - } - } - - // Exit menu, parent level menu, open item - if (menuItemSelected == 0) { - // Exit menu - if (tmpMenuItem.parentId == 0 && tmpMenuItem.id == 0) { - menuVisible = false; - redrawScreen(); - } else { - // Parent menu - menuCurrent = tmpMenuItem.targetParentId; - showMenu(); - } - return true; - } else { - Serial.println(tmpMenuItem.id); - // Device list - if (tmpMenuItem.id > 10000 && tmpMenuItem.id < 10100) { - strlcpy((char*)liveData->settings.obdMacAddress, (char*)tmpMenuItem.obdMacAddress, 20); - Serial.print("Selected adapter MAC address "); - Serial.println(liveData->settings.obdMacAddress); - saveSettings(); - ESP.restart(); - } - // Other menus - switch (tmpMenuItem.id) { - // Set vehicle type - case 101: liveData->settings.carType = CAR_KIA_ENIRO_2020_64; break; - case 102: liveData->settings.carType = CAR_HYUNDAI_KONA_2020_64; break; - case 103: liveData->settings.carType = CAR_HYUNDAI_IONIQ_2018; break; - case 104: liveData->settings.carType = CAR_KIA_ENIRO_2020_39; break; - case 105: liveData->settings.carType = CAR_HYUNDAI_KONA_2020_39; break; - case 107: liveData->settings.carType = CAR_DEBUG_OBD2_KIA; break; - // Screen orientation - case 3011: liveData->settings.displayRotation = 1; tft.setRotation(liveData->settings.displayRotation); break; - case 3012: liveData->settings.displayRotation = 3; tft.setRotation(liveData->settings.displayRotation); break; - // Default screen - case 3021: liveData->settings.defaultScreen = 1; break; - case 3022: liveData->settings.defaultScreen = 2; break; - case 3023: liveData->settings.defaultScreen = 3; break; - case 3024: liveData->settings.defaultScreen = 4; break; - case 3025: liveData->settings.defaultScreen = 5; break; - // Debug screen off/on - case 3031: liveData->settings.debugScreen = 0; break; - case 3032: liveData->settings.debugScreen = 1; break; - // Lcd brightness - case 3041: liveData->settings.lcdBrightness = 0; break; - case 3042: liveData->settings.lcdBrightness = 20; break; - case 3043: liveData->settings.lcdBrightness = 50; break; - case 3044: liveData->settings.lcdBrightness = 100; break; - // Pre-drawn charg.graphs off/on - case 3051: liveData->settings.predrawnChargingGraphs = 0; break; - case 3052: liveData->settings.predrawnChargingGraphs = 1; break; - // Distance - case 4011: liveData->settings.distanceUnit = 'k'; break; - case 4012: liveData->settings.distanceUnit = 'm'; break; - // Temperature - case 4021: liveData->settings.temperatureUnit = 'c'; break; - case 4022: liveData->settings.temperatureUnit = 'f'; break; - // Pressure - case 4031: liveData->settings.pressureUnit = 'b'; break; - case 4032: liveData->settings.pressureUnit = 'p'; break; - // Pair ble device - case 2: startBleScan(); return false; - // Reset settings - case 8: resetSettings(); hideMenu(); return false; - // Save settings - case 9: saveSettings(); break; - // Version - case 10: hideMenu(); return false; - // Shutdown - case 11: shutdownDevice(); return false; - default: - // Submenu - menuCurrent = tmpMenuItem.id; - menuItemSelected = 0; - showMenu(); - return true; - } - - // close menu - hideMenu(); - } - - return true; -} - -/** - Redraw screen -*/ -bool redrawScreen() { - - if (menuVisible) { - return false; - } - - // Lights not enabled - if (!testDataMode && liveData->params.forwardDriveMode && !liveData->params.headLights && !liveData->params.dayLights) { - spr.fillSprite(TFT_RED); - spr.setFreeFont(&Orbitron_Light_32); - spr.setTextColor(TFT_WHITE, TFT_RED); - spr.setTextDatum(MC_DATUM); - spr.drawString("! LIGHTS OFF !", 160, 120, GFXFF); - spr.pushSprite(0, 0); - - return true; - } - - spr.fillSprite(TFT_BLACK); - - // 1. Auto mode = >5kpm Screen 3 - speed, other wise basic Screen2 - Main screen, if charging then Screen 5 Graph - if (displayScreen == SCREEN_AUTO) { - if (liveData->params.speedKmh > 5) { - if (displayScreenAutoMode != 3) { - displayScreenAutoMode = 3; - } - drawSceneSpeed(); - } else if (liveData->params.batPowerKw > 1) { - if (displayScreenAutoMode != 5) { - displayScreenAutoMode = 5; - } - drawSceneChargingGraph(); - } else { - if (displayScreenAutoMode != 2) { - displayScreenAutoMode = 2; - } - drawSceneMain(); - } - } - // 2. Main screen - if (displayScreen == SCREEN_DASH) { - drawSceneMain(); - } - // 3. Big speed + kwh/100km - if (displayScreen == SCREEN_SPEED) { - drawSceneSpeed(); - } - // 4. Battery cells - if (displayScreen == SCREEN_CELLS) { - drawSceneBatteryCells(); - } - // 5. Charging graph - if (displayScreen == SCREEN_CHARGING) { - drawSceneChargingGraph(); - } - // 6. SOC10% table (CEC-CED) - if (displayScreen == SCREEN_SOC10) { - drawSceneSoc10Table(); - } - // 7. DEBUG SCREEN - if (displayScreen == SCREEN_DEBUG) { - drawSceneDebug(); - } - - if (!displayScreenSpeedHud) { - // BLE not connected - if (!bleConnected && bleConnect) { - // Print message - spr.setTextSize(1); - spr.setTextColor(TFT_WHITE, TFT_BLACK); - spr.setTextDatum(TL_DATUM); - spr.drawString("BLE4 OBDII not connected...", 0, 180, 2); - spr.drawString("Press middle button to menu.", 0, 200, 2); - spr.drawString(APP_VERSION, 0, 220, 2); - } - - spr.pushSprite(0, 0); - } - - return true; -} - -/** - Do next AT command from queue + Do next AT command from queue */ bool doNextAtCommand() { // Restart loop with AT commands if (liveData->commandQueueIndex >= liveData->commandQueueCount) { liveData->commandQueueIndex = liveData->commandQueueLoopFrom; - redrawScreen(); + board->redrawScreen(); } // Send AT command to obd @@ -1510,7 +90,7 @@ bool doNextAtCommand() { Serial.print(">>> "); Serial.println(liveData->commandRequest); String tmpStr = liveData->commandRequest + "\r"; - pRemoteCharacteristicWrite->writeValue(tmpStr.c_str(), tmpStr.length()); + liveData->pRemoteCharacteristicWrite->writeValue(tmpStr.c_str(), tmpStr.length()); liveData->commandQueueIndex++; return true; @@ -1545,57 +125,39 @@ bool parseRowMerged() { Serial.println(liveData->responseRowMerged); // Catch output for debug screen - if (displayScreen == SCREEN_DEBUG) { - if (debugCommandIndex == liveData->commandQueueIndex) { - debugAtshRequest = liveData->currentAtshRequest; - debugCommandRequest = liveData->commandRequest; - debugLastString = liveData->responseRowMerged; + if (board->displayScreen == SCREEN_DEBUG) { + if (board->debugCommandIndex == liveData->commandQueueIndex) { + board->debugAtshRequest = liveData->currentAtshRequest; + board->debugCommandRequest = liveData->commandRequest; + board->debugLastString = liveData->responseRowMerged; } } - if (debugTmpCharging && liveData->currentAtshRequest.equals("ATSH7E4")) { - if (liveData->commandRequest.equals("220105")) - debugTmpChargingLast05 = liveData->responseRowMerged; - if (liveData->commandRequest.equals("220106")) - debugTmpChargingLast06 = liveData->responseRowMerged; - } // Parse by selected car interface - carInterface->parseRowMerged(); + car->parseRowMerged(); return true; } -/** - Parse test data -*/ -bool testData() { - - testDataMode = true; // skip lights off message - carInterface->loadTestData(); - redrawScreen(); - - return true; -} - /** BLE callbacks */ class MyClientCallback : public BLEClientCallbacks { /** - On connect + On BLE connect */ void onConnect(BLEClient* pclient) { Serial.println("onConnect"); } /** - On disconnect + On BLE disconnect */ void onDisconnect(BLEClient* pclient) { //connected = false; Serial.println("onDisconnect"); - displayMessage("BLE disconnected", ""); + board->displayMessage("BLE disconnected", ""); } }; @@ -1615,18 +177,19 @@ class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { // Add to device list (max. 9 devices allowed yet) String tmpStr; - if (scanningDeviceIndex < 10/* && advertisedDevice.haveServiceUUID()*/) { - for (uint16_t i = 0; i < menuItemsCount; ++i) { - if (menuItems[i].id == 10001 + scanningDeviceIndex) { + + if (liveData->scanningDeviceIndex < 10/* && advertisedDevice.haveServiceUUID()*/) { + for (uint16_t i = 0; i < liveData->menuItemsCount; ++i) { + if (liveData->menuItems[i].id == 10001 + liveData->scanningDeviceIndex) { tmpStr = advertisedDevice.toString().c_str(); tmpStr.replace("Name: ", ""); tmpStr.replace("Address: ", ""); - tmpStr.toCharArray(menuItems[i].title, 48); + tmpStr.toCharArray(liveData->menuItems[i].title, 48); tmpStr = advertisedDevice.getAddress().toString().c_str(); - tmpStr.toCharArray(menuItems[i].obdMacAddress, 18); + tmpStr.toCharArray(liveData->menuItems[i].obdMacAddress, 18); } } - scanningDeviceIndex++; + liveData->scanningDeviceIndex++; } /* if (advertisedDevice.getServiceDataUUID().toString() != "") { @@ -1642,7 +205,7 @@ class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { (strcmp(advertisedDevice.getAddress().toString().c_str(), liveData->settings.obdMacAddress) == 0)) { Serial.println("Stop scanning. Found my BLE device."); BLEDevice::getScan()->stop(); - foundMyBleDevice = new BLEAdvertisedDevice(advertisedDevice); + liveData->foundMyBleDevice = new BLEAdvertisedDevice(advertisedDevice); } } }; @@ -1676,7 +239,7 @@ class MySecurity : public BLESecurityCallbacks { Serial.printf("onAuthenticationComplete\r\n"); } else { Serial.println("Auth failure. Incorrect PIN?"); - bleConnect = false; + liveData->bleConnect = false; } } }; @@ -1714,11 +277,11 @@ static void notifyCallback (BLERemoteCharacteristic * pBLERemoteCharacteristic, */ bool connectToServer(BLEAddress pAddress) { - displayMessage(" > Connecting device", ""); + board->displayMessage(" > Connecting device", ""); - Serial.print("bleConnect "); + Serial.print("liveData->bleConnect "); Serial.println(pAddress.toString().c_str()); - displayMessage(" > Connecting device - init", pAddress.toString().c_str()); + board->displayMessage(" > Connecting device - init", pAddress.toString().c_str()); BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); BLEDevice::setSecurityCallbacks(new MySecurity()); @@ -1728,65 +291,64 @@ bool connectToServer(BLEAddress pAddress) { pSecurity->setCapability(ESP_IO_CAP_KBDISP); pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); - displayMessage(" > Connecting device", pAddress.toString().c_str()); + board->displayMessage(" > Connecting device", pAddress.toString().c_str()); + liveData->pClient = BLEDevice::createClient(); + liveData->pClient->setClientCallbacks(new MyClientCallback()); + if (liveData->pClient->connect(pAddress, BLE_ADDR_TYPE_RANDOM) ) Serial.println("liveData->bleConnected"); + Serial.println(" - liveData->bleConnected to server"); - pClient = BLEDevice::createClient(); - pClient->setClientCallbacks(new MyClientCallback()); - if (pClient->connect(pAddress, BLE_ADDR_TYPE_RANDOM) ) Serial.println("bleConnected"); - Serial.println(" - bleConnected to server"); - - displayMessage(" > Connecting device", "Connecting service..."); // Remote service - BLERemoteService* pRemoteService = pClient->getService(BLEUUID(liveData->settings.serviceUUID)); + board->displayMessage(" > Connecting device", "Connecting service..."); + BLERemoteService* pRemoteService = liveData->pClient->getService(BLEUUID(liveData->settings.serviceUUID)); if (pRemoteService == nullptr) { Serial.print("Failed to find our service UUID: "); Serial.println(liveData->settings.serviceUUID); - displayMessage(" > Connecting device", "Unable to find service"); + board->displayMessage(" > Connecting device", "Unable to find service"); return false; } Serial.println(" - Found our service"); // Get characteristics - displayMessage(" > Connecting device", "Connecting TxUUID..."); - pRemoteCharacteristic = pRemoteService->getCharacteristic(BLEUUID(liveData->settings.charTxUUID)); - if (pRemoteCharacteristic == nullptr) { + board->displayMessage(" > Connecting device", "Connecting TxUUID..."); + liveData->pRemoteCharacteristic = pRemoteService->getCharacteristic(BLEUUID(liveData->settings.charTxUUID)); + if (liveData->pRemoteCharacteristic == nullptr) { Serial.print("Failed to find our characteristic UUID: "); Serial.println(liveData->settings.charTxUUID);//.toString().c_str()); - displayMessage(" > Connecting device", "Unable to find TxUUID"); + board->displayMessage(" > Connecting device", "Unable to find TxUUID"); return false; } Serial.println(" - Found our characteristic"); // Get characteristics - displayMessage(" > Connecting device", "Connecting RxUUID..."); - pRemoteCharacteristicWrite = pRemoteService->getCharacteristic(BLEUUID(liveData->settings.charRxUUID)); - if (pRemoteCharacteristicWrite == nullptr) { + board->displayMessage(" > Connecting device", "Connecting RxUUID..."); + liveData->pRemoteCharacteristicWrite = pRemoteService->getCharacteristic(BLEUUID(liveData->settings.charRxUUID)); + if (liveData->pRemoteCharacteristicWrite == nullptr) { Serial.print("Failed to find our characteristic UUID: "); Serial.println(liveData->settings.charRxUUID);//.toString().c_str()); - displayMessage(" > Connecting device", "Unable to find RxUUID"); + board->displayMessage(" > Connecting device", "Unable to find RxUUID"); return false; } Serial.println(" - Found our characteristic write"); - displayMessage(" > Connecting device", "Register callbacks..."); + board->displayMessage(" > Connecting device", "Register callbacks..."); // Read the value of the characteristic. - if (pRemoteCharacteristic->canNotify()) { + if (liveData->pRemoteCharacteristic->canNotify()) { Serial.println(" - canNotify"); - //pRemoteCharacteristic->registerForNotify(notifyCallback); - if (pRemoteCharacteristic->canIndicate()) { + //liveData->pRemoteCharacteristic->registerForNotify(notifyCallback); + if (liveData->pRemoteCharacteristic->canIndicate()) { Serial.println(" - canIndicate"); const uint8_t indicationOn[] = {0x2, 0x0}; //const uint8_t indicationOff[] = {0x0,0x0}; - pRemoteCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)indicationOn, 2, true); - //pRemoteCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notifyOff,2,true); - pRemoteCharacteristic->registerForNotify(notifyCallback, false); + liveData->pRemoteCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)indicationOn, 2, true); + //liveData->pRemoteCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notifyOff,2,true); + liveData->pRemoteCharacteristic->registerForNotify(notifyCallback, false); delay(200); } } - displayMessage(" > Connecting device", "Done..."); - if (pRemoteCharacteristicWrite->canWrite()) { + board->displayMessage(" > Connecting device", "Done..."); + if (liveData->pRemoteCharacteristicWrite->canWrite()) { Serial.println(" - canWrite"); } @@ -1796,43 +358,41 @@ bool connectToServer(BLEAddress pAddress) { /** Start ble scan */ -bool startBleScan() { +void startBleScan() { - foundMyBleDevice = NULL; - scanningDeviceIndex = 0; - - displayMessage(" > Scanning BLE4 devices", "40 seconds"); + liveData->foundMyBleDevice = NULL; + liveData->scanningDeviceIndex = 0; + board->displayMessage(" > Scanning BLE4 devices", "40 seconds"); // Start scanning Serial.println("Scanning BLE devices..."); Serial.print("Looking for "); Serial.println(liveData->settings.obdMacAddress); - BLEScanResults foundDevices = pBLEScan->start(40, false); + BLEScanResults foundDevices = liveData->pBLEScan->start(40, false); Serial.print("Devices found: "); Serial.println(foundDevices.getCount()); Serial.println("Scan done!"); - pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory + liveData->pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory + char tmpStr1[20]; sprintf(tmpStr1, "Found %d devices", foundDevices.getCount()); - displayMessage(" > Scanning BLE4 devices", tmpStr1); + board->displayMessage(" > Scanning BLE4 devices", tmpStr1); // Scan devices from menu, show list of devices - if (menuItemSelected == 2) { + if (liveData->menuItemSelected == 2) { Serial.println("Display menu with devices"); - menuVisible = true; - menuCurrent = 9999; - menuItemSelected = 0; - showMenu(); + liveData->menuVisible = true; + liveData->menuCurrent = 9999; + liveData->menuItemSelected = 0; + board->showMenu(); } else { // Redraw screen - if (foundMyBleDevice == NULL) { - displayMessage("Device not found", "Middle button - menu"); + if (liveData->foundMyBleDevice == NULL) { + board->displayMessage("Device not found", "Middle button - menu"); } else { - redrawScreen(); + board->redrawScreen(); } } - - return true; } /** @@ -1846,7 +406,7 @@ bool sim800lSetup() { sim800l = new SIM800L((Stream *)serial, SIM800L_RST, 512 , 512); bool sim800l_ready = sim800l->isReady(); - for(uint8_t i = 0; i < 5 && !sim800l_ready; i++) { + for (uint8_t i = 0; i < 5 && !sim800l_ready; i++) { Serial.println("Problem to initialize SIM800L module, retry in 1 sec"); delay(1000); sim800l_ready = sim800l->isReady(); @@ -1874,30 +434,30 @@ bool sim800lSetup() { Serial.println("Problem to set GPRS"); } } - - return true; + + return true; } bool sendDataViaGPRS() { Serial.println("Sending data via GPRS"); NetworkRegistration network = sim800l->getRegistrationStatus(); - if(network != REGISTERED_HOME && network != REGISTERED_ROAMING) { + if (network != REGISTERED_HOME && network != REGISTERED_ROAMING) { Serial.println("SIM800L module not connected to network!"); return false; } bool connected = sim800l->connectGPRS(); - for(uint8_t i = 0; i < 5 && !connected; i++) { + for (uint8_t i = 0; i < 5 && !connected; i++) { delay(1000); connected = sim800l->connectGPRS(); } - - if(!connected) { + + if (!connected) { Serial.println("GPRS not connected! Reseting SIM800L module!"); sim800l->reset(); sim800lSetup(); - + return false; } @@ -1938,7 +498,7 @@ bool sendDataViaGPRS() { } sim800l->disconnectGPRS(); - + return true; } #endif //SIM800L_ENABLED @@ -1953,65 +513,57 @@ void setup(void) { Serial.println(""); Serial.println("Booting device..."); - // + // Init settings/params, board library liveData = new LiveData(); - initStructure(); - loadSettings(); - - // Set button pins for input - pinMode(BUTTON_MIDDLE, INPUT); - pinMode(BUTTON_LEFT, INPUT); - pinMode(BUTTON_RIGHT, INPUT); + liveData->initParams(); +#ifdef BOARD_TTGO_T4 + board = new BoardTtgoT4v13(); +#endif // BOARD_TTGO_T4 #ifdef BOARD_M5STACK_CORE - // mute speaker - Serial.println("Mute speaker for m5stack"); - dacWrite(SPEAKER_PIN, 0); -#endif // BOARD_M5STACK_C - // Init display - Serial.println("Init TFT display"); - tft.begin(); + board = new BoardM5stackCore(); +#endif // BOARD_M5STACK_CORE + board->setLiveData(liveData); + board->loadSettings(); + board->initBoard(); -#ifdef INVERT_DISPLAY - tft.invertDisplay(true); -#endif // INVERT_DISPLAY + // Car interface + if (liveData->settings.carType == CAR_KIA_ENIRO_2020_64 || liveData->settings.carType == CAR_HYUNDAI_KONA_2020_64 || + liveData->settings.carType == CAR_KIA_ENIRO_2020_39 || liveData->settings.carType == CAR_HYUNDAI_KONA_2020_39) { + car = new CarKiaEniro(); + } else if (liveData->settings.carType == CAR_HYUNDAI_IONIQ_2018) { + car = new CarHyundaiIoniq(); + } else { + // if (liveData->settings.carType == CAR_DEBUG_OBD2_KIA) + car = new CarKiaDebugObd2(); + } + car->setLiveData(liveData); + car->activateCommandQueue(); + board->attachCar(car); + board->debugCommandIndex = liveData->commandQueueLoopFrom; - tft.setRotation(liveData->settings.displayRotation); - analogWrite(TFT_BL, (liveData->settings.lcdBrightness == 0) ? 100 : liveData->settings.lcdBrightness); - tft.fillScreen(TFT_BLACK); - - bool psramUsed = false; // 320x240 16bpp sprites requires psram -#if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT) - if (psramFound()) - psramUsed = true; -#endif - spr.setColorDepth((psramUsed) ? 16 : 8); - spr.createSprite(320, 240); - redrawScreen(); + // Redraw screen + board->redrawScreen(); // Init time library struct timeval tv; tv.tv_sec = 1589011873; settimeofday(&tv, NULL); - struct tm now; getLocalTime(&now, 0); liveData->params.chargingStartTime = liveData->params.currentTime = mktime(&now); - // Show test data on right button during boot device - displayScreen = liveData->settings.defaultScreen; - if (digitalRead(BUTTON_RIGHT) == LOW) { - testData(); - } + // Hold right button + board->afterSetup(); #ifdef SD_ENABLED // Init SDCARD - if (!SD.begin(SD_CS, SD_MOSI, SD_MISO, SD_SCLK)) { + /*if (!SD.begin(SD_CS, SD_MOSI, SD_MISO, SD_SCLK)) { Serial.println("SDCARD initialization failed!"); - } else { + } else { Serial.println("SDCARD initialization done."); - } - /*spiSD.begin(SD_SCLK,SD_MISO,SD_MOSI,SD_CS); + } + /*spiSD.begin(SD_SCLK,SD_MISO,SD_MOSI,SD_CS); if(!SD.begin( SD_CS, spiSD, 27000000)){ Serial.println("SDCARD initialization failed!"); } else { @@ -2027,14 +579,14 @@ void setup(void) { // Retrieve a Scanner and set the callback we want to use to be informed when we have detected a new device. // Specify that we want active scanning and start the scan to run for 10 seconds. Serial.println("Setup BLE scan"); - pBLEScan = BLEDevice::getScan(); - pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); - pBLEScan->setInterval(1349); - pBLEScan->setWindow(449); - pBLEScan->setActiveScan(true); + liveData->pBLEScan = BLEDevice::getScan(); + liveData->pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + liveData->pBLEScan->setInterval(1349); + liveData->pBLEScan->setWindow(449); + liveData->pBLEScan->setActiveScan(true); // Skip BLE scan if middle button pressed - if (digitalRead(BUTTON_MIDDLE) == HIGH && digitalRead(BUTTON_RIGHT) == HIGH) { + if (!board->skipAdapterScan()) { startBleScan(); } @@ -2052,17 +604,17 @@ void setup(void) { void loop() { // Connect BLE device - if (bleConnect == true && foundMyBleDevice != NULL) { - pServerAddress = new BLEAddress(liveData->settings.obdMacAddress); - if (connectToServer(*pServerAddress)) { + if (liveData->bleConnect == true && liveData->foundMyBleDevice != NULL) { + liveData->pServerAddress = new BLEAddress(liveData->settings.obdMacAddress); + if (connectToServer(*liveData->pServerAddress)) { - bleConnected = true; - bleConnect = false; + liveData->bleConnected = true; + liveData->bleConnect = false; Serial.println("We are now connected to the BLE device."); // Print message - displayMessage(" > Processing init AT cmds", ""); + board->displayMessage(" > Processing init AT cmds", ""); // Serve first command (ATZ) doNextAtCommand(); @@ -2072,13 +624,13 @@ void loop() { } // Send command from TTY to OBD2 - if (bleConnected) { + if (liveData->bleConnected) { if (Serial.available()) { ch = Serial.read(); line = line + ch; if (ch == '\r' || ch == '\n') { Serial.println(line); - pRemoteCharacteristicWrite->writeValue(line.c_str(), line.length()); + liveData->pRemoteCharacteristicWrite->writeValue(line.c_str(), line.length()); line = ""; } } @@ -2095,74 +647,19 @@ void loop() { sendDataViaGPRS(); liveData->params.lastDataSent = liveData->params.currentTime; } -#endif //SIM800L_ENABLED +#endif // SIM800L_ENABLED - /////////////////////////////////////////////////////////////////////// - // Handle buttons - // MIDDLE - menu select - if (digitalRead(BUTTON_MIDDLE) == HIGH) { - btnMiddlePressed = false; - } else { - if (!btnMiddlePressed) { - btnMiddlePressed = true; - tft.setRotation(liveData->settings.displayRotation); - if (menuVisible) { - menuItemClick(); - } else { - showMenu(); - } - } - } - // LEFT - screen rotate, menu - if (digitalRead((liveData->settings.displayRotation == 1) ? BUTTON_RIGHT : BUTTON_LEFT) == HIGH) { - btnLeftPressed = false; - } else { - if (!btnLeftPressed) { - btnLeftPressed = true; - tft.setRotation(liveData->settings.displayRotation); - // Menu handling - if (menuVisible) { - menuMove(false); - } else { - displayScreen++; - if (displayScreen > displayScreenCount - (liveData->settings.debugScreen == 0) ? 1 : 0) - displayScreen = 0; // rotate screens - // Turn off display on screen 0 - analogWrite(TFT_BL, (displayScreen == SCREEN_BLANK) ? 0 : (liveData->settings.lcdBrightness == 0) ? 100 : liveData->settings.lcdBrightness); - redrawScreen(); - } - } - } - // RIGHT - menu, debug screen rotation - if (digitalRead((liveData->settings.displayRotation == 1) ? BUTTON_LEFT : BUTTON_RIGHT) == HIGH) { - btnRightPressed = false; - } else { - if (!btnRightPressed) { - btnRightPressed = true; - tft.setRotation(liveData->settings.displayRotation); - // Menu handling - if (menuVisible) { - menuMove(true); - } else { - // doAction - if (displayScreen == SCREEN_SPEED) { - displayScreenSpeedHud = !displayScreenSpeedHud; - redrawScreen(); - } - if (liveData->settings.debugScreen == 1 && displayScreen == SCREEN_DEBUG) { - debugCommandIndex = (debugCommandIndex >= liveData->commandQueueCount) ? liveData->commandQueueLoopFrom : debugCommandIndex + 1; - redrawScreen(); - } - - } - } - } + board->mainLoop(); // currentTime & 1ms delay struct tm now; getLocalTime(&now, 0); liveData->params.currentTime = mktime(&now); // Shutdown when car is off - if (liveData->params.automatickShutdownTimer != 0 && liveData->params.currentTime - liveData->params.automatickShutdownTimer > 5) - shutdownDevice(); + if (liveData->params.automaticShutdownTimer != 0 && liveData->params.currentTime - liveData->params.automaticShutdownTimer > 5) + board->shutdownDevice(); + if (board->scanDevices) { + board->scanDevices = false; + startBleScan(); + } } diff --git a/menu.h b/menu.h index b4615e4..7340014 100644 --- a/menu.h +++ b/menu.h @@ -1,24 +1,8 @@ -#ifndef MENU_H -#define MENU_H -// Menu id/parent/title -typedef struct { - int16_t id; - int16_t parentId; - int16_t targetParentId; - char title[50]; - char obdMacAddress[20]; - char serviceUUID[40]; -} MENU_ITEM; +#include "config.h"; -#define menuItemsCount 78 -bool menuVisible = false; -uint16_t menuCurrent = 0; -uint8_t menuItemSelected = 0; -uint8_t menuItemOffset = 0; -uint16_t scanningDeviceIndex = 0; -MENU_ITEM menuItems[menuItemsCount] = { +MENU_ITEM menuItemsSource[78] = { {0, 0, 0, "<- exit menu"}, {1, 0, -1, "Vehicle type"}, @@ -114,6 +98,3 @@ MENU_ITEM menuItems[menuItemsCount] = { {10008, 9999, -1, "-"}, {10009, 9999, -1, "-"}, }; - -// -#endif // MENU_H