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 new file mode 100644 index 0000000..3019651 --- /dev/null +++ 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 new file mode 100644 index 0000000..5ae2d08 --- /dev/null +++ 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 new file mode 100644 index 0000000..744165e --- /dev/null +++ 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 new file mode 100644 index 0000000..55045d4 --- /dev/null +++ b/BoardM5stackCore.h @@ -0,0 +1,44 @@ +#ifndef BOARDM5STACKCORE_H +#define BOARDM5STACKCORE_H + +// Setup for m5stack core +#define USER_SETUP_LOADED 1 +#define SPI_FREQUENCY 27000000 +#define SPI_TOUCH_FREQUENCY 2500000 + +#define USER_SETUP_LOADED 1 +#define ILI9341_DRIVER +#define M5STACK +#define TFT_MISO 19 +#define TFT_MOSI 23 +#define TFT_SCLK 18 +#define TFT_CS 14 // Chip select control pin +#define TFT_DC 27 // Data Command control pin +#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) +#define TFT_BL 32 // LED back-light +#define SPI_FREQUENCY 27000000 +#define SPI_READ_FREQUENCY 5000000 +#define SPEAKER_PIN 25 + +#define BUTTON_LEFT 37 +#define BUTTON_MIDDLE 38 +#define BUTTON_RIGHT 39 + +#define SD_CS 4 +#define SD_MOSI 23 +#define SD_MISO 19 +#define SD_SCLK 18 + +// +#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 new file mode 100644 index 0000000..bbfcc7b --- /dev/null +++ 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 new file mode 100644 index 0000000..f8e02d8 --- /dev/null +++ b/BoardTtgoT4v13.h @@ -0,0 +1,45 @@ +#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 + +#define ILI9341_DRIVER +#define TFT_MISO 12 +#define TFT_MOSI 23 +#define TFT_SCLK 18 +#define TFT_CS 27 +#define TFT_DC 32 +#define TFT_RST 5 +//#define TFT_BACKLIGHT_ON HIGH +#define TFT_BL 4 + +#define USE_HSPI_PORT +//#define SPI_FREQUENCY 40000000 // Maximum for ILI9341 +#define SPI_READ_FREQUENCY 6000000 // 6 MHz is the maximum SPI read speed for the ST7789V + +#define SD_CS 13 +#define SD_MOSI 15 +#define SD_MISO 2 +#define SD_SCLK 14 + +#define BUTTON_LEFT 38 +#define BUTTON_MIDDLE 37 +#define BUTTON_RIGHT 39 + +// +#include "BoardInterface.h" +#include "Board320_240.h" + +// +class BoardTtgoT4v13 : public Board320_240 { + + private: + public: + void initBoard() override; + +}; + +#endif // BOARDTTGOT4V13_H diff --git a/CarHyundaiIoniq.cpp b/CarHyundaiIoniq.cpp new file mode 100644 index 0000000..7ae1991 --- /dev/null +++ b/CarHyundaiIoniq.cpp @@ -0,0 +1,381 @@ +#ifndef CARHYUNDAIIONIQ_CPP +#define CARHYUNDAIIONIQ_CPP + +#include "CarHyundaiIoniq.h" + +#define commandQueueCountHyundaiIoniq 25 +#define commandQueueLoopFromHyundaiIoniq 8 + +/** + activatethis->liveData->commandQueue +*/ +void CarHyundaiIoniq::activateCommandQueue() { + + String commandQueueHyundaiIoniq[commandQueueCountHyundaiIoniq] = { + "AT Z", // Reset all + "AT I", // Print the version ID + "AT E0", // Echo off + "AT L0", // Linefeeds off + "AT S0", // Printing of spaces on + "AT SP 6", // Select protocol to ISO 15765-4 CAN (11 bit ID, 500 kbit/s) + //"AT AL", // Allow Long (>7 byte) messages + //"AT AR", // Automatically receive + //"AT H1", // Headers on (debug only) + //"AT D1", // Display of the DLC on + //"AT CAF0", // Automatic formatting off + "AT DP", + "AT ST16", + + // Loop from (HYUNDAI IONIQ) + // BMS + "ATSH7E4", + "2101", // power kw, ... + "2102", // cell voltages, screen 3 only + "2103", // cell voltages, screen 3 only + "2104", // cell voltages, screen 3 only + "2105", // soh, soc, .. + "2106", // cooling water temp + + // VMCU + "ATSH7E2", + "2101", // speed, ... + "2102", // aux, ... + + //"ATSH7Df", + //"2106", + //"220106", + + // Aircondition + // IONIQ OK + "ATSH7B3", + "220100", // in/out temp + "220102", // coolant temp1, 2 + + // BCM / TPMS + // IONIQ OK + "ATSH7A0", + "22c00b", // tire pressure/temp + + // CLUSTER MODULE + // IONIQ OK + "ATSH7C6", + "22B002", // odo + }; + + // 28kWh version + this->liveData->params.batteryTotalAvailableKWh = 28; + this->liveData->params.batModuleTempCount = 12; + + // Empty and fill command queue + for (int i = 0; i < 300; i++) { + this->liveData->commandQueue[i] = ""; + } + for (int i = 0; i < commandQueueCountHyundaiIoniq; i++) { + this->liveData->commandQueue[i] = commandQueueHyundaiIoniq[i]; + } + + this->liveData->commandQueueLoopFrom = commandQueueLoopFromHyundaiIoniq; + this->liveData->commandQueueCount = commandQueueCountHyundaiIoniq; +} + +/** + parseRowMerged +*/ +void CarHyundaiIoniq::parseRowMerged() { + + // VMCU 7E2 + if (this->liveData->currentAtshRequest.equals("ATSH7E2")) { + if (this->liveData->commandRequest.equals("2101")) { + this->liveData->params.speedKmh = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(32, 36).c_str(), 2, false) * 0.0155; // / 100.0 *1.609 = real to gps is 1.750 + if (this->liveData->params.speedKmh < -99 || this->liveData->params.speedKmh > 200) + this->liveData->params.speedKmh = 0; + } + if (this->liveData->commandRequest.equals("2102")) { + this->liveData->params.auxPerc = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(50, 52).c_str(), 1, false); + this->liveData->params.auxCurrentAmp = - this->liveData->hexToDec(this->liveData->responseRowMerged.substring(46, 50).c_str(), 2, true) / 1000.0; + } + } + + // Cluster module 7c6 + if (this->liveData->currentAtshRequest.equals("ATSH7C6")) { + if (this->liveData->commandRequest.equals("22B002")) { + this->liveData->params.odoKm = float(strtol(this->liveData->responseRowMerged.substring(18, 24).c_str(), 0, 16)); + } + } + + // Aircon 7b3 + if (this->liveData->currentAtshRequest.equals("ATSH7B3")) { + if (this->liveData->commandRequest.equals("220100")) { + this->liveData->params.indoorTemperature = (this->liveData->hexToDec(this->liveData->responseRowMerged.substring(16, 18).c_str(), 1, false) / 2) - 40; + this->liveData->params.outdoorTemperature = (this->liveData->hexToDec(this->liveData->responseRowMerged.substring(18, 20).c_str(), 1, false) / 2) - 40; + } + if (this->liveData->commandRequest.equals("220102") && this->liveData->responseRowMerged.substring(12, 14) == "00") { + this->liveData->params.coolantTemp1C = (this->liveData->hexToDec(this->liveData->responseRowMerged.substring(14, 16).c_str(), 1, false) / 2) - 40; + this->liveData->params.coolantTemp2C = (this->liveData->hexToDec(this->liveData->responseRowMerged.substring(16, 18).c_str(), 1, false) / 2) - 40; + } + } + + // BMS 7e4 + if (this->liveData->currentAtshRequest.equals("ATSH7E4")) { + if (this->liveData->commandRequest.equals("2101")) { + this->liveData->params.cumulativeEnergyChargedKWh = float(strtol(this->liveData->responseRowMerged.substring(80, 88).c_str(), 0, 16)) / 10.0; + if (this->liveData->params.cumulativeEnergyChargedKWhStart == -1) + this->liveData->params.cumulativeEnergyChargedKWhStart = this->liveData->params.cumulativeEnergyChargedKWh; + this->liveData->params.cumulativeEnergyDischargedKWh = float(strtol(this->liveData->responseRowMerged.substring(88, 96).c_str(), 0, 16)) / 10.0; + if (this->liveData->params.cumulativeEnergyDischargedKWhStart == -1) + this->liveData->params.cumulativeEnergyDischargedKWhStart = this->liveData->params.cumulativeEnergyDischargedKWh; + this->liveData->params.availableChargePower = float(strtol(this->liveData->responseRowMerged.substring(16, 20).c_str(), 0, 16)) / 100.0; + this->liveData->params.availableDischargePower = float(strtol(this->liveData->responseRowMerged.substring(20, 24).c_str(), 0, 16)) / 100.0; + this->liveData->params.isolationResistanceKOhm = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(118, 122).c_str(), 2, true); + this->liveData->params.batFanStatus = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(58, 60).c_str(), 2, true); + this->liveData->params.batFanFeedbackHz = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(60, 62).c_str(), 2, true); + this->liveData->params.auxVoltage = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(62, 64).c_str(), 2, true) / 10.0; + this->liveData->params.batPowerAmp = - this->liveData->hexToDec(this->liveData->responseRowMerged.substring(24, 28).c_str(), 2, true) / 10.0; + this->liveData->params.batVoltage = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(28, 32).c_str(), 2, false) / 10.0; + this->liveData->params.batPowerKw = (this->liveData->params.batPowerAmp * this->liveData->params.batVoltage) / 1000.0; + if (this->liveData->params.batPowerKw < 1) // Reset charging start time + this->liveData->params.chargingStartTime = this->liveData->params.currentTime; + this->liveData->params.batPowerKwh100 = this->liveData->params.batPowerKw / this->liveData->params.speedKmh * 100; + this->liveData->params.batCellMaxV = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(50, 52).c_str(), 1, false) / 50.0; + this->liveData->params.batCellMinV = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(54, 56).c_str(), 1, false) / 50.0; + this->liveData->params.batModuleTempC[0] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(36, 38).c_str(), 1, true); + this->liveData->params.batModuleTempC[1] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(38, 40).c_str(), 1, true); + this->liveData->params.batModuleTempC[2] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(40, 42).c_str(), 1, true); + this->liveData->params.batModuleTempC[3] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(42, 44).c_str(), 1, true); + this->liveData->params.batModuleTempC[4] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(44, 46).c_str(), 1, true); + //this->liveData->params.batTempC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(34, 36).c_str(), 1, true); + //this->liveData->params.batMaxC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(32, 34).c_str(), 1, true); + //this->liveData->params.batMinC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(34, 36).c_str(), 1, true); + + // This is more accurate than min/max from BMS. It's required to detect kona/eniro cold gates (min 15C is needed > 43kW charging, min 25C is needed > 58kW charging) + this->liveData->params.batInletC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(48, 50).c_str(), 1, true); + if (this->liveData->params.speedKmh < 10 && this->liveData->params.batPowerKw >= 1 && this->liveData->params.socPerc > 0 && this->liveData->params.socPerc <= 100) { + if ( this->liveData->params.chargingGraphMinKw[int(this->liveData->params.socPerc)] == -100 || this->liveData->params.batPowerKw < this->liveData->params.chargingGraphMinKw[int(this->liveData->params.socPerc)]) + this->liveData->params.chargingGraphMinKw[int(this->liveData->params.socPerc)] = this->liveData->params.batPowerKw; + if ( this->liveData->params.chargingGraphMaxKw[int(this->liveData->params.socPerc)] == -100 || this->liveData->params.batPowerKw > this->liveData->params.chargingGraphMaxKw[int(this->liveData->params.socPerc)]) + this->liveData->params.chargingGraphMaxKw[int(this->liveData->params.socPerc)] = this->liveData->params.batPowerKw; + this->liveData->params.chargingGraphBatMinTempC[int(this->liveData->params.socPerc)] = this->liveData->params.batMinC; + this->liveData->params.chargingGraphBatMaxTempC[int(this->liveData->params.socPerc)] = this->liveData->params.batMaxC; + this->liveData->params.chargingGraphHeaterTempC[int(this->liveData->params.socPerc)] = this->liveData->params.batHeaterC; + } + } + // BMS 7e4 + if (this->liveData->commandRequest.equals("2102") && this->liveData->responseRowMerged.substring(10, 12) == "FF") { + for (int i = 0; i < 32; i++) { + this->liveData->params.cellVoltage[i] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(12 + (i * 2), 12 + (i * 2) + 2).c_str(), 1, false) / 50; + } + } + // BMS 7e4 + if (this->liveData->commandRequest.equals("2103")) { + for (int i = 0; i < 32; i++) { + this->liveData->params.cellVoltage[32 + i] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(12 + (i * 2), 12 + (i * 2) + 2).c_str(), 1, false) / 50; + } + } + // BMS 7e4 + if (this->liveData->commandRequest.equals("2104")) { + for (int i = 0; i < 32; i++) { + this->liveData->params.cellVoltage[64 + i] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(12 + (i * 2), 12 + (i * 2) + 2).c_str(), 1, false) / 50; + } + } + // BMS 7e4 + if (this->liveData->commandRequest.equals("2105")) { + this->liveData->params.socPercPrevious = this->liveData->params.socPerc; + this->liveData->params.sohPerc = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(54, 58).c_str(), 2, false) / 10.0; + this->liveData->params.socPerc = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(66, 68).c_str(), 1, false) / 2.0; + + // Remaining battery modules (tempC) + this->liveData->params.batModuleTempC[5] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(22, 24).c_str(), 1, true); + this->liveData->params.batModuleTempC[6] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(24, 26).c_str(), 1, true); + this->liveData->params.batModuleTempC[7] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(26, 28).c_str(), 1, true); + this->liveData->params.batModuleTempC[8] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(28, 30).c_str(), 1, true); + this->liveData->params.batModuleTempC[9] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(30, 32).c_str(), 1, true); + this->liveData->params.batModuleTempC[10] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(32, 34).c_str(), 1, true); + this->liveData->params.batModuleTempC[11] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(34, 36).c_str(), 1, true); + + this->liveData->params.batMinC = this->liveData->params.batMaxC = this->liveData->params.batModuleTempC[0]; + for (uint16_t i = 1; i < this->liveData->params.batModuleTempCount; i++) { + if (this->liveData->params.batModuleTempC[i] < this->liveData->params.batMinC) + this->liveData->params.batMinC = this->liveData->params.batModuleTempC[i]; + if (this->liveData->params.batModuleTempC[i] > this->liveData->params.batMaxC) + this->liveData->params.batMaxC = this->liveData->params.batModuleTempC[i]; + } + this->liveData->params.batTempC = this->liveData->params.batMinC; + + // Soc10ced table, record x0% CEC/CED table (ex. 90%->89%, 80%->79%) + if (this->liveData->params.socPercPrevious - this->liveData->params.socPerc > 0) { + byte index = (int(this->liveData->params.socPerc) == 4) ? 0 : (int)(this->liveData->params.socPerc / 10) + 1; + if ((int(this->liveData->params.socPerc) % 10 == 9 || int(this->liveData->params.socPerc) == 4) && this->liveData->params.soc10ced[index] == -1) { + this->liveData->params.soc10ced[index] = this->liveData->params.cumulativeEnergyDischargedKWh; + this->liveData->params.soc10cec[index] = this->liveData->params.cumulativeEnergyChargedKWh; + this->liveData->params.soc10odo[index] = this->liveData->params.odoKm; + this->liveData->params.soc10time[index] = this->liveData->params.currentTime; + } + } + this->liveData->params.batHeaterC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(50, 52).c_str(), 1, true); + // + for (int i = 30; i < 32; i++) { // ai/aj position + this->liveData->params.cellVoltage[96 - 30 + i] = -1; + } + } + // BMS 7e4 + // IONIQ FAILED + if (this->liveData->commandRequest.equals("2106")) { + this->liveData->params.coolingWaterTempC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(14, 16).c_str(), 1, false); + } + } + + // TPMS 7a0 + if (this->liveData->currentAtshRequest.equals("ATSH7A0")) { + if (this->liveData->commandRequest.equals("22c00b")) { + this->liveData->params.tireFrontLeftPressureBar = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(14, 16).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722 + this->liveData->params.tireFrontRightPressureBar = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(22, 24).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722 + this->liveData->params.tireRearRightPressureBar = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(30, 32).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722 + this->liveData->params.tireRearLeftPressureBar = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(38, 40).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722 + this->liveData->params.tireFrontLeftTempC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(16, 18).c_str(), 2, false) - 50; // === OK Valid + this->liveData->params.tireFrontRightTempC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(24, 26).c_str(), 2, false) - 50; // === OK Valid + this->liveData->params.tireRearRightTempC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(32, 34).c_str(), 2, false) - 50; // === OK Valid + this->liveData->params.tireRearLeftTempC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(40, 42).c_str(), 2, false) - 50; // === OK Valid + } + } + +} + +/** + loadTestData +*/ +void CarHyundaiIoniq::loadTestData() { + + // VMCU ATSH7E2 + this->liveData->currentAtshRequest = "ATSH7E2"; + // 2101 + this->liveData->commandRequest = "2101"; + this->liveData->responseRowMerged = "6101FFE0000009211222062F03000000001D7734"; + this->parseRowMerged(); + // 2102 + this->liveData->commandRequest = "2102"; + this->liveData->responseRowMerged = "6102FF80000001010000009315B2888D390B08618B683900000000"; + this->parseRowMerged(); + + // "ATSH7DF", + this->liveData->currentAtshRequest = "ATSH7DF"; + + // AIRCON / ACU ATSH7B3 + this->liveData->currentAtshRequest = "ATSH7B3"; + // 220100 + this->liveData->commandRequest = "220100"; + this->liveData->responseRowMerged = "6201007E5007C8FF8A876A011010FFFF10FF10FFFFFFFFFFFFFFFFFF2EEF767D00FFFF00FFFF000000"; + this->parseRowMerged(); + // 220102 + this->liveData->commandRequest = "220102"; + this->liveData->responseRowMerged = "620102FF800000A3950000000000002600000000"; + this->parseRowMerged(); + + // BMS ATSH7E4 + this->liveData->currentAtshRequest = "ATSH7E4"; + // 220101 + this->liveData->commandRequest = "2101"; + this->liveData->responseRowMerged = "6101FFFFFFFF5026482648A3FFC30D9E181717171718170019B50FB501000090000142230001425F0000771B00007486007815D809015C0000000003E800"; + this->parseRowMerged(); + // 220102 + this->liveData->commandRequest = "2102"; + this->liveData->responseRowMerged = "6102FFFFFFFFB5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5000000"; + this->parseRowMerged(); + // 220103 + this->liveData->commandRequest = "2103"; + this->liveData->responseRowMerged = "6103FFFFFFFFB5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5000000"; + this->parseRowMerged(); + // 220104 + this->liveData->commandRequest = "2104"; + this->liveData->responseRowMerged = "6104FFFFFFFFB5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5000000"; + this->parseRowMerged(); + // 220105 + this->liveData->commandRequest = "2105"; + this->liveData->responseRowMerged = "6105FFFFFFFF00000000001717171817171726482648000150181703E81A03E801520029000000000000000000000000"; + this->parseRowMerged(); + // 220106 + this->liveData->commandRequest = "2106"; + this->liveData->responseRowMerged = "7F2112"; // n/a on ioniq + this->parseRowMerged(); + + // BCM / TPMS ATSH7A0 + this->liveData->currentAtshRequest = "ATSH7A0"; + // 22c00b + this->liveData->commandRequest = "22c00b"; + this->liveData->responseRowMerged = "62C00BFFFF0000B9510100B9510100B84F0100B54F0100AAAAAAAA"; + this->parseRowMerged(); + + // ATSH7C6 + this->liveData->currentAtshRequest = "ATSH7C6"; + // 22b002 + this->liveData->commandRequest = "22b002"; + this->liveData->responseRowMerged = "62B002E000000000AD003D2D0000000000000000"; + this->parseRowMerged(); + + /* this->liveData->params.batModule01TempC = 28; + this->liveData->params.batModule02TempC = 29; + this->liveData->params.batModule03TempC = 28; + this->liveData->params.batModule04TempC = 30; + //this->liveData->params.batTempC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(36, 38).c_str(), 1, true); + //this->liveData->params.batMaxC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(34, 36).c_str(), 1, true); + //this->liveData->params.batMinC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(36, 38).c_str(), 1, true); + + // This is more accurate than min/max from BMS. It's required to detect kona/eniro cold gates (min 15C is needed > 43kW charging, min 25C is needed > 58kW charging) + this->liveData->params.batMinC = this->liveData->params.batMaxC = this->liveData->params.batModule01TempC; + this->liveData->params.batMinC = (this->liveData->params.batModule02TempC < this->liveData->params.batMinC) ? this->liveData->params.batModule02TempC : this->liveData->params.batMinC ; + this->liveData->params.batMinC = (this->liveData->params.batModule03TempC < this->liveData->params.batMinC) ? this->liveData->params.batModule03TempC : this->liveData->params.batMinC ; + this->liveData->params.batMinC = (this->liveData->params.batModule04TempC < this->liveData->params.batMinC) ? this->liveData->params.batModule04TempC : this->liveData->params.batMinC ; + this->liveData->params.batMaxC = (this->liveData->params.batModule02TempC > this->liveData->params.batMaxC) ? this->liveData->params.batModule02TempC : this->liveData->params.batMaxC ; + this->liveData->params.batMaxC = (this->liveData->params.batModule03TempC > this->liveData->params.batMaxC) ? this->liveData->params.batModule03TempC : this->liveData->params.batMaxC ; + this->liveData->params.batMaxC = (this->liveData->params.batModule04TempC > this->liveData->params.batMaxC) ? this->liveData->params.batModule04TempC : this->liveData->params.batMaxC ; + this->liveData->params.batTempC = this->liveData->params.batMinC; + + // + this->liveData->params.soc10ced[10] = 2200; + this->liveData->params.soc10cec[10] = 2500; + this->liveData->params.soc10odo[10] = 13000; + this->liveData->params.soc10time[10] = 13000; + this->liveData->params.soc10ced[9] = this->liveData->params.soc10ced[10] + 6.4; + this->liveData->params.soc10cec[9] = this->liveData->params.soc10cec[10] + 0; + this->liveData->params.soc10odo[9] = this->liveData->params.soc10odo[10] + 30; + this->liveData->params.soc10time[9] = this->liveData->params.soc10time[10] + 900; + this->liveData->params.soc10ced[8] = this->liveData->params.soc10ced[9] + 6.8; + this->liveData->params.soc10cec[8] = this->liveData->params.soc10cec[9] + 0; + this->liveData->params.soc10odo[8] = this->liveData->params.soc10odo[9] + 30; + this->liveData->params.soc10time[8] = this->liveData->params.soc10time[9] + 900; + this->liveData->params.soc10ced[7] = this->liveData->params.soc10ced[8] + 7.2; + this->liveData->params.soc10cec[7] = this->liveData->params.soc10cec[8] + 0.6; + this->liveData->params.soc10odo[7] = this->liveData->params.soc10odo[8] + 30; + this->liveData->params.soc10time[7] = this->liveData->params.soc10time[8] + 900; + this->liveData->params.soc10ced[6] = this->liveData->params.soc10ced[7] + 6.7; + this->liveData->params.soc10cec[6] = this->liveData->params.soc10cec[7] + 0; + this->liveData->params.soc10odo[6] = this->liveData->params.soc10odo[7] + 30; + this->liveData->params.soc10time[6] = this->liveData->params.soc10time[7] + 900; + this->liveData->params.soc10ced[5] = this->liveData->params.soc10ced[6] + 6.7; + this->liveData->params.soc10cec[5] = this->liveData->params.soc10cec[6] + 0; + this->liveData->params.soc10odo[5] = this->liveData->params.soc10odo[6] + 30; + this->liveData->params.soc10time[5] = this->liveData->params.soc10time[6] + 900; + this->liveData->params.soc10ced[4] = this->liveData->params.soc10ced[5] + 6.4; + this->liveData->params.soc10cec[4] = this->liveData->params.soc10cec[5] + 0.3; + this->liveData->params.soc10odo[4] = this->liveData->params.soc10odo[5] + 30; + this->liveData->params.soc10time[4] = this->liveData->params.soc10time[5] + 900; + this->liveData->params.soc10ced[3] = this->liveData->params.soc10ced[4] + 6.4; + this->liveData->params.soc10cec[3] = this->liveData->params.soc10cec[4] + 0; + this->liveData->params.soc10odo[3] = this->liveData->params.soc10odo[4] + 30; + this->liveData->params.soc10time[3] = this->liveData->params.soc10time[4] + 900; + this->liveData->params.soc10ced[2] = this->liveData->params.soc10ced[3] + 5.4; + this->liveData->params.soc10cec[2] = this->liveData->params.soc10cec[3] + 0.1; + this->liveData->params.soc10odo[2] = this->liveData->params.soc10odo[3] + 30; + this->liveData->params.soc10time[2] = this->liveData->params.soc10time[3] + 900; + this->liveData->params.soc10ced[1] = this->liveData->params.soc10ced[2] + 6.2; + this->liveData->params.soc10cec[1] = this->liveData->params.soc10cec[2] + 0.1; + this->liveData->params.soc10odo[1] = this->liveData->params.soc10odo[2] + 30; + this->liveData->params.soc10time[1] = this->liveData->params.soc10time[2] + 900; + this->liveData->params.soc10ced[0] = this->liveData->params.soc10ced[1] + 2.9; + this->liveData->params.soc10cec[0] = this->liveData->params.soc10cec[1] + 0.5; + this->liveData->params.soc10odo[0] = this->liveData->params.soc10odo[1] + 15; + this->liveData->params.soc10time[0] = this->liveData->params.soc10time[1] + 900; + */ + +} + +#endif //CARHYUNDAIIONIQ_CPP diff --git a/CarHyundaiIoniq.h b/CarHyundaiIoniq.h new file mode 100644 index 0000000..32a5802 --- /dev/null +++ b/CarHyundaiIoniq.h @@ -0,0 +1,16 @@ +#ifndef CARHYUNDAIIONIQ_H +#define CARHYUNDAIIONIQ_H + +#include "CarInterface.h" + +class CarHyundaiIoniq : public CarInterface { + + private: + + public: + void activateCommandQueue() override; + void parseRowMerged() override; + void loadTestData() override; +}; + +#endif diff --git a/CarInterface.cpp b/CarInterface.cpp new file mode 100644 index 0000000..0dc7540 --- /dev/null +++ b/CarInterface.cpp @@ -0,0 +1,23 @@ +#ifndef CARINTERFACE_CPP +#define CARINTERFACE_CPP + +#include "CarInterface.h" +#include "LiveData.h" + +void CarInterface::setLiveData(LiveData* pLiveData) { + this->liveData = pLiveData; +} + +void CarInterface::activateCommandQueue() { + +} + +void CarInterface::parseRowMerged() { + +} + +void CarInterface::loadTestData() { + +} + +#endif // CARINTERFACE_CPP diff --git a/CarInterface.h b/CarInterface.h new file mode 100644 index 0000000..46966a9 --- /dev/null +++ b/CarInterface.h @@ -0,0 +1,17 @@ +#ifndef CARINTERFACE_H +#define CARINTERFACE_H + +#include "LiveData.h" + +class CarInterface { + + private: + public: + LiveData* liveData; + void setLiveData(LiveData* pLiveData); + virtual void activateCommandQueue(); + virtual void parseRowMerged(); + virtual void loadTestData(); +}; + +#endif // CARINTERFACE_H diff --git a/CarKiaDebugObd2.cpp b/CarKiaDebugObd2.cpp new file mode 100644 index 0000000..e2a3f43 --- /dev/null +++ b/CarKiaDebugObd2.cpp @@ -0,0 +1,520 @@ +#ifndef CARKIADEBUGOBD2_CPP +#define CARKIADEBUGOBD2_CPP + +#include "CarKiaDebugObd2.h" + +#define commandQueueCountDebugObd2Kia 256 +#define commandQueueLoopFromDebugObd2Kia 8 + +/** + activateCommandQueue +*/ +void CarKiaDebugObd2::activateCommandQueue() { + + String commandQueueDebugObd2Kia[commandQueueCountDebugObd2Kia] = { + "AT Z", // Reset all + "AT I", // Print the version ID + "AT E0", // Echo off + "AT L0", // Linefeeds off + "AT S0", // Printing of spaces on + "AT SP 6", // Select protocol to ISO 15765-4 CAN (11 bit ID, 500 kbit/s) + "AT DP", + "AT ST16", + + // Loop from here + + // Request ID Response ID ECU name Can bus Protocol Description + // 725 72D WPS B Wireless phone charger + //"ATSH725", + //"2201", // All with negative resp. "2202", "2203", "2101", "2102", "220101", "220102", "22B001", "22C001", "22C101", + + // 736 73E VESS P Virtual Engine Sound system + //"ATSH736", + //"2201", // All with negative resp. "2202", "2203", "2101", "2102", + //"220101", // All with 62 response "220102", "22B001", "22C001", "22C101", + + // 755 75D BSD Right Blind spot detection Right + // "ATSH755", + // "2201", // ALL with negative 7F2213, etc "2202", "2203", "2101", "2102", "220101", "220102", "22B001", "22C001", "22C101", + + // 770 778 IGPM All UDS Integrated Gateway and power control module + "ATSH770", + "22BC01", // 009 62BC01400000000001AAAAAAAA + "22BC02", // 62BC0200000000 + "22BC03", // 00B 62BC03FDEE7C730A600000AAAA + "22BC04", // 00B 62BC04B33F74EA0D002042AAAA + "22BC05", // 00B 62BC05BF13200001000000AAAA + "22BC06", // 00B 62BC06B48000002C000000AAAA + "22BC07", // 00B 62BC070849DBC000101900AAAA + //"22BC08", // ALL with NEGATIVE RESPONSE "22BC09", "22BC0A", "22BC0B", "22BC0C", "22BC0D", "22BC0E", "22BC0F", + + // 783 78B AMP M Amplifier + //"ATSH783", + // "2201",// ALL with NEGATIVE RESPONSE "2202", "2203", "2101", "2102", "220101", "220102", "22B001", "22C001", "22C101", + + // 796 79E PGS C Parking Guide System + //"ATSH796", + //"2201", // ALL with NEGATIVE RESPONSE "2202", "2203", "2101", "2102", "220101", "220102", "22B001", "22C001", "22C101", + + // 7A0 7A8 BCM / TPMS B UDS Body control module 22 B0 01 to 22 B0 0E + // C Tire Pressure Monitoring "At least 22 C0 01 to 22 C0 02 & 22 C0 0B to 22 C0 0F" + "ATSH7A0", + "22B001", // 016 62B00140C20000000000000000000001010000000001AAAAAAAAAA + "22B002", // 009 62B002C00000000300AAAAAAAA + "22B003", // 018 62B003BFCB8000A23D63B164F8F7F73DF80000A400A4A4A4AAAAAA + "22B004", // 00B 62B0047402994E0E008800AAAA + "22B005", // 00B 62B0052000100000000800AAAA + "22B006", // 00B 62B0062000000000000000AAAA + "22B007", // 00B 62B007002001E000040000AAAA + "22B008", // 00B 62B00800510C2000880004AAAA + "22B009", // 00B 62B009FEEEFEEE08000800AAAA + "22B00A", // 00B 62B00AE3FEE3000040C500AAAA + //"22B00B", // 7F2231 + "22B00C", // 00B 62B00C3F00000000000000AAAA + "22B00D", // 00B 62B00DFCFCFC0000000000AAAA + "22B00E", // 00B 62B00E0800000000000000AAAA + //"22B00F", // 7F2231 + "22C001", // 01D 62C001000000002E2E02500706B5B50A098C3C0000000001FF01000101AAAAAAAAAA + "22C002", // 017 62C002FFFF0000D2E149F3D2DBDACBD2E84EBBD2E84E93AAAAAAAA + "22C003", // 021 62C00300000000444F303101002E2E02500706B5B50A098C3C0000000001FF0100AA + "22C004", // 021 62C004000000004E41303101002E2E024B0005B5B508088C3C0100000001FF0100AA + "22C005", // 021 62C005000000004E54504D0100302F02500000ABAB00008C3C0000030001FF0000AA + "22C006", // 021 62C00600000000444F303201002E2E02500706B5AB0A098C3C0000000001010100AA + "22C007", // 021 62C007000000004E41303201002E2E024B0005B5AB08088C3C0100000001010100AA + "22C008", // 021 62C00800000000434E303101002E2E02500706B5B50A098C3C0000020001FF0100AA + "22C009", // 021 62C00900000000303030360000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA + "22C00A", // 021 62C00A00000000303030370000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA + "22C00B", // 017 62C00BFFFF0000AF470100B4480100B5460100B3460100AAAAAAAA + "22C00C", // 025 62C00CFFFF03000000000000FFFF0000000000000000FFFF0000000000000000FFFF000000AAAAAAAA + "22C00D", // 025 62C00DFFFF03000000000000FFFF0000000000000000FFFF0000000000000000FFFF000000AAAAAAAA + "22C00E", // 025 62C00EFFFF03000000000000FFFF0000000000000000FFFF0000000000000000FFFF000000AAAAAAAA + "22C00F", // 025 62C00FFFFF03000000000000FFFF0000000000000000FFFF0000000000000000FFFF000000AAAAAAAA + + // 7A1 7A9 DDM B Driver door module + // "ATSH7A1", + // "2201", // All with NO DATA "2202", "2203", "2101", "2102", "220101", "220102", "22B001", "22C001", "22C101", + + // 7A2 7AA ADM B Assist door module + //"ATSH7A2", + // "2201", // ALL with NO DATA "2202", "2203", "2101", "2102", "220101", "220102", "22B001", "22C001", "22C101", + + // 7A3 7AB PSM B UDS Power Seat Module + // "ATSH7A3", + // "22B401", // All with NO DATA "22B402", "22B403", "22B404", "22B405", "22B406", "22B407", "22B408", "22B409", "22B40A", + + // 7A5 7AD SMK B UDS Smart Key + "ATSH7A5", + "22B001", // 7F2278 7F2231 + "22B002", // positive + "22B003", // positive + "22B004", // 7F2278 7F2231 + "22B005", // positive + "22B006", // positive + "22B007", // positive + "22B008", // positive + "22B009", // positive + "22B00A", // positive + + // 7B3 7BB AIRCON / ACU UDS Aircondition + "ATSH7B3", + "220100", // 026 6201007E5027C8FF7C6D6B05EFBCFFFFEFFF10FFFFFFFFFFFFBFFFFF52B3919900FFFF01FFFF000000 in/out temp + // "220101", // 7F2231 + "220102", // 014 620102FFF80000B36B0101000101003C00016E12 coolant temp1, 2 + // "220103", // 7F2231 + + // 7B7 7BF BSD Left Blind spot detection Left + "ATSH7B7", + // "2201", // ALL NEGATIVE RESP "2202", "2203", "2101", "2102", "220101", "220102", "22B001", "22C001", "22C101", + + // 7C4 7CC MFC Multi Function Camera + "ATSH7C4", + "220101", // 6201010E + "220102", // 62010200000000 + + // 7C6 7CE CM C & M UDS Cluster Module + "ATSH7C6", + "22B001", // 008 62B00100000000000000000000 + "22B002", // 00F 62B002E0000000FFA200AD8F0000000000000000 odo + "22B003", // 008 62B00398000000010000000000 + //"22B004", // NO DATA + + // 7D0 7D8 AEB UDS? Autonomous Emergency Breaking + // "ATSH7D0", + // "2201", // ALL CODES WITH NEGATIVE RESPONSE + // "2202", // "2203", // "2101", // "2102", // "220101", // "220102", // "22B001", // "22C001", // "22C101", + + // 7D1 7D9 ABS / ESP + AHB UDS + "ATSH7D1", + "22C101", // 02A 62C1015FD7E7D0FFFF00FF04D0D400000000FF7EFF0030F5010000FFFF7F6307F207FE05FF00FF3FFFFFAAAAAAAAAAAA + "22C102", // 01A 62C10237000000FFFFFFFFFFFF00FF05FFFFFF00FF5501FFFFFFAA + "22C103", // 01A 62C103BE3000000DFFF0FCFE7FFF7FFFFFFFFFFF000005B50000AA + + // 7D2 7DA AIRBAG SRS Sytem + // "ATSH7D2", + // "2101", // 7F2211 + // "2102", // 7F2211 + // "220101", // 7F2211 + // "220102", // 7F2211 + // "22B001", // 7F2211 + // "22C001", // 7F2211 + // "22C101", // 7F2211 + + // 7D4 7DC EPS Electric power steering + "ATSH7D4", + //"2101", // 7F2121 + //"2102", // 7F2121 + "220101", // 012 6201018387FD009DFFF90100000010101921AAAA + "220102", // 008 6201020000000500AAAAAAAAAA + // "22B001", // 7F2231 + // "22C001", // 7F2231 + // "22C101", // 7F2231 + + // 7DF UNKNOWN + //"ATSH7DF", + //"2106", // 013 7F2112 028 6106FFFF800000000000000300001C001C001C000600060006000F000000010000000000000000015801580158015700 + //"220106", // 01B 620106FFFFFFFF12001200307C7C00317C830000B4B3000A28EA00 + + // 7E2 7EA VMCU H & P KWP2000 Vehicle Motor Control Unit 21 01 to 21 02 & 1A 80++ + "ATSH7E2", + "2101", // 018 6101FFF8000009285A3806480300000000C4693404080805000000 speed, .. + "2102", // 027 6102F8FFFC000101000000851BB5780234FA0BAB8D1933E07F5B211C74000001010100000007000000 aux, .. + //"2103", // 7F2112 + //"1A80", // Working VIN 1A 8A 8C 8D .. + + // 7E3 7EB MCU H & P KWP2000 Motor Control Unit 21 01 to 21 06 + "ATSH7E3", + "2101", // 01E 610100007FFF0C3C00BD8D0A3300B00900002D0252025033D500C3FF68FF00000000 + "2102", // 03A 610207FFFFFF00000D000D00260008080909000001004614CDABC2CD3F005581720085CAF5265D0DC1CD0000EC3400000000000000000000FF0000000000 + "2103", // 06E 610300007FFF0000000000000000000000000000000000005C010E02FDFD040400000000000000000000000048058D0C0200160000000000AA3F000005000000AE0102000000000000000000000000000000000000000000BB0B00000000000000000000680000000000E803000000 + "2104", // 060 6104000001FF000000000000D7425D03000000000000050000007A2B00000000000000003200000000000000000000000000000000000000000000000000000000000000010000000100010001000000030000000000000000006D0000008E1B00 + "2105", // 067 6105000001FF630200010000005900000C00630200010000000100000C006B0200020000003300250D0136010096BA03000100000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + "2106", // 013 6106000000030000000000000000000000000300 + //"2107", // 7F2112 + + // 7E4 7EC BMS P UDS Battery Management System 22 01 01 to 22 01 06 + "ATSH7E4", + "220101", // 03E 620101FFF7E7FF6B0000000003001C0E2F161414141513000012B930B9380000830003E1E30003C95B0001722C00015B5B0040D1660D016B0000000003E8 power kw, .. + "220102", // 027 620102FFFFFFFFB9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9AAAA cell voltages, screen 3 only + "220103", // 027 620103FFFFFFFFB9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9AAAA cell voltages, screen 3 only + "220104", // 027 620104FFFFFFFFB9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9AAAA + "220105", // 02E 620105003FFF90000000000000000012836A0142684268000150126E03E8000000006E0000B9B900000F00000000AAAA soh, soc, . + "220106", // 01B 620106FFFFFFFF12001200307C7C00007C030000B4B3000A28EA00 cooling water temp + // "220107", // 7F2231 + + // 7E5 7ED OBC KWP2000 OnBoard Charger 21 01 to 21 03 + "ATSH7E5", + "2101", // 01A 6101DFE0000001010001000000000E2F0533051D0078000000C800 + "2102", // 011 6102FE000000000403E8000001BF000028000000 + "2103", // 039 6103FFFFFDF80000000000000000000000000000000000000000000000000000000000000000007576767600000000000000000004000400030000000000 + "2104", // 022 6104FFF000000A280A280A280000E324006900000003000000000000000000000000 + "2105", // 046 61050000000081AA791E8013791E779C791E8BD37907874A79108D67791473777915727E7914753179156FAE7917768F79147650792876257930757E7914759379167545791D000000000000 + "2106", // 028 6106FFFF8000001C001C001C000600060006000E000000010000000000000000015801580158015800 + //"2107", // ret 7F2112 + + // 7E6 7EE ?? ?? 21 08 05 to 21 08 0F -> All negative response + //"ATSH7E6", + //"210805", // ret 7F2112 + //"210806", // ret 7F2112 + //"210807", // ret 7F2112 + }; + + // 39 or 64 kWh model? + this->liveData->params.batteryTotalAvailableKWh = 64; + + // Empty and fill command queue + for (uint16_t i = 0; i < 300; i++) { + this->liveData->commandQueue[i] = ""; + } + for (uint16_t i = 0; i < commandQueueCountDebugObd2Kia; i++) { + this->liveData->commandQueue[i] = commandQueueDebugObd2Kia[i]; + } + + this->liveData->commandQueueLoopFrom = commandQueueLoopFromDebugObd2Kia; + this->liveData->commandQueueCount = commandQueueCountDebugObd2Kia; +} + +/** + parseRowMerged +*/ +void CarKiaDebugObd2::parseRowMerged() { + + // VMCU 7E2 + if (this->liveData->currentAtshRequest.equals("ATSH7E2")) { + if (this->liveData->commandRequest.equals("2101")) { + this->liveData->params.speedKmh = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(32, 36).c_str(), 2, false) * 0.0155; // / 100.0 *1.609 = real to gps is 1.750 + if (this->liveData->params.speedKmh < -99 || this->liveData->params.speedKmh > 200) + this->liveData->params.speedKmh = 0; + } + if (this->liveData->commandRequest.equals("2102")) { + this->liveData->params.auxPerc = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(50, 52).c_str(), 1, false); + this->liveData->params.auxCurrentAmp = - this->liveData->hexToDec(this->liveData->responseRowMerged.substring(46, 50).c_str(), 2, true) / 1000.0; + } + } + + // Cluster module 7c6 + if (this->liveData->currentAtshRequest.equals("ATSH7C6")) { + if (this->liveData->commandRequest.equals("22B002")) { + this->liveData->params.odoKm = float(strtol(this->liveData->responseRowMerged.substring(18, 24).c_str(), 0, 16)); + } + } + + // Aircon 7b3 + if (this->liveData->currentAtshRequest.equals("ATSH7B3")) { + if (this->liveData->commandRequest.equals("220100")) { + this->liveData->params.indoorTemperature = (this->liveData->hexToDec(this->liveData->responseRowMerged.substring(16, 18).c_str(), 1, false) / 2) - 40; + this->liveData->params.outdoorTemperature = (this->liveData->hexToDec(this->liveData->responseRowMerged.substring(18, 20).c_str(), 1, false) / 2) - 40; + } + if (this->liveData->commandRequest.equals("220102") && this->liveData->responseRowMerged.substring(12, 14) == "00") { + this->liveData->params.coolantTemp1C = (this->liveData->hexToDec(this->liveData->responseRowMerged.substring(14, 16).c_str(), 1, false) / 2) - 40; + this->liveData->params.coolantTemp2C = (this->liveData->hexToDec(this->liveData->responseRowMerged.substring(16, 18).c_str(), 1, false) / 2) - 40; + } + } + + // BMS 7e4 + if (this->liveData->currentAtshRequest.equals("ATSH7E4")) { + if (this->liveData->commandRequest.equals("220101")) { + this->liveData->params.cumulativeEnergyChargedKWh = float(strtol(this->liveData->responseRowMerged.substring(82, 90).c_str(), 0, 16)) / 10.0; + if (this->liveData->params.cumulativeEnergyChargedKWhStart == -1) + this->liveData->params.cumulativeEnergyChargedKWhStart = this->liveData->params.cumulativeEnergyChargedKWh; + this->liveData->params.cumulativeEnergyDischargedKWh = float(strtol(this->liveData->responseRowMerged.substring(90, 98).c_str(), 0, 16)) / 10.0; + if (this->liveData->params.cumulativeEnergyDischargedKWhStart == -1) + this->liveData->params.cumulativeEnergyDischargedKWhStart = this->liveData->params.cumulativeEnergyDischargedKWh; + this->liveData->params.auxVoltage = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(64, 66).c_str(), 2, true) / 10.0; + this->liveData->params.batPowerAmp = - this->liveData->hexToDec(this->liveData->responseRowMerged.substring(26, 30).c_str(), 2, true) / 10.0; + this->liveData->params.batVoltage = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(30, 34).c_str(), 2, false) / 10.0; + this->liveData->params.batPowerKw = (this->liveData->params.batPowerAmp * this->liveData->params.batVoltage) / 1000.0; + this->liveData->params.batPowerKwh100 = this->liveData->params.batPowerKw / this->liveData->params.speedKmh * 100; + this->liveData->params.batCellMaxV = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(52, 54).c_str(), 1, false) / 50.0; + this->liveData->params.batCellMinV = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(56, 58).c_str(), 1, false) / 50.0; + this->liveData->params.batModuleTempC[0] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(38, 40).c_str(), 1, true); + this->liveData->params.batModuleTempC[1] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(40, 42).c_str(), 1, true); + this->liveData->params.batModuleTempC[2] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(42, 44).c_str(), 1, true); + this->liveData->params.batModuleTempC[3] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(44, 46).c_str(), 1, true); + //this->liveData->params.batTempC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(36, 38).c_str(), 1, true); + //this->liveData->params.batMaxC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(34, 36).c_str(), 1, true); + //this->liveData->params.batMinC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(36, 38).c_str(), 1, true); + + // This is more accurate than min/max from BMS. It's required to detect kona/eniro cold gates (min 15C is needed > 43kW charging, min 25C is needed > 58kW charging) + this->liveData->params.batMinC = this->liveData->params.batMaxC = this->liveData->params.batModuleTempC[0]; + this->liveData->params.batMinC = (this->liveData->params.batModuleTempC[1] < this->liveData->params.batMinC) ? this->liveData->params.batModuleTempC[1] : this->liveData->params.batMinC; + this->liveData->params.batMinC = (this->liveData->params.batModuleTempC[2] < this->liveData->params.batMinC) ? this->liveData->params.batModuleTempC[2] : this->liveData->params.batMinC; + this->liveData->params.batMinC = (this->liveData->params.batModuleTempC[3] < this->liveData->params.batMinC) ? this->liveData->params.batModuleTempC[3] : this->liveData->params.batMinC; + this->liveData->params.batMaxC = (this->liveData->params.batModuleTempC[1] > this->liveData->params.batMaxC) ? this->liveData->params.batModuleTempC[1] : this->liveData->params.batMaxC; + this->liveData->params.batMaxC = (this->liveData->params.batModuleTempC[2] > this->liveData->params.batMaxC) ? this->liveData->params.batModuleTempC[2] : this->liveData->params.batMaxC; + this->liveData->params.batMaxC = (this->liveData->params.batModuleTempC[3] > this->liveData->params.batMaxC) ? this->liveData->params.batModuleTempC[3] : this->liveData->params.batMaxC; + this->liveData->params.batTempC = this->liveData->params.batMinC; + + this->liveData->params.batInletC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(50, 52).c_str(), 1, true); + if (this->liveData->params.speedKmh < 10 && this->liveData->params.batPowerKw >= 1 && this->liveData->params.socPerc > 0 && this->liveData->params.socPerc <= 100) { + if ( this->liveData->params.chargingGraphMinKw[int(this->liveData->params.socPerc)] == -100 || this->liveData->params.batPowerKw < this->liveData->params.chargingGraphMinKw[int(this->liveData->params.socPerc)]) + this->liveData->params.chargingGraphMinKw[int(this->liveData->params.socPerc)] = this->liveData->params.batPowerKw; + if ( this->liveData->params.chargingGraphMaxKw[int(this->liveData->params.socPerc)] == -100 || this->liveData->params.batPowerKw > this->liveData->params.chargingGraphMaxKw[int(this->liveData->params.socPerc)]) + this->liveData->params.chargingGraphMaxKw[int(this->liveData->params.socPerc)] = this->liveData->params.batPowerKw; + this->liveData->params.chargingGraphBatMinTempC[int(this->liveData->params.socPerc)] = this->liveData->params.batMinC; + this->liveData->params.chargingGraphBatMaxTempC[int(this->liveData->params.socPerc)] = this->liveData->params.batMaxC; + this->liveData->params.chargingGraphHeaterTempC[int(this->liveData->params.socPerc)] = this->liveData->params.batHeaterC; + } + } + // BMS 7e4 + if (this->liveData->commandRequest.equals("220102") && this->liveData->responseRowMerged.substring(12, 14) == "FF") { + for (int i = 0; i < 32; i++) { + this->liveData->params.cellVoltage[i] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(14 + (i * 2), 14 + (i * 2) + 2).c_str(), 1, false) / 50; + } + } + // BMS 7e4 + if (this->liveData->commandRequest.equals("220103")) { + for (int i = 0; i < 32; i++) { + this->liveData->params.cellVoltage[32 + i] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(14 + (i * 2), 14 + (i * 2) + 2).c_str(), 1, false) / 50; + } + } + // BMS 7e4 + if (this->liveData->commandRequest.equals("220104")) { + for (int i = 0; i < 32; i++) { + this->liveData->params.cellVoltage[64 + i] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(14 + (i * 2), 14 + (i * 2) + 2).c_str(), 1, false) / 50; + } + } + // BMS 7e4 + if (this->liveData->commandRequest.equals("220105")) { + this->liveData->params.socPercPrevious = this->liveData->params.socPerc; + this->liveData->params.sohPerc = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(56, 60).c_str(), 2, false) / 10.0; + this->liveData->params.socPerc = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(68, 70).c_str(), 1, false) / 2.0; + + // Soc10ced table, record x0% CEC/CED table (ex. 90%->89%, 80%->79%) + if (this->liveData->params.socPercPrevious - this->liveData->params.socPerc > 0) { + byte index = (int(this->liveData->params.socPerc) == 4) ? 0 : (int)(this->liveData->params.socPerc / 10) + 1; + if ((int(this->liveData->params.socPerc) % 10 == 9 || int(this->liveData->params.socPerc) == 4) && this->liveData->params.soc10ced[index] == -1) { + struct tm now; + getLocalTime(&now, 0); + time_t time_now_epoch = mktime(&now); + this->liveData->params.soc10ced[index] = this->liveData->params.cumulativeEnergyDischargedKWh; + this->liveData->params.soc10cec[index] = this->liveData->params.cumulativeEnergyChargedKWh; + this->liveData->params.soc10odo[index] = this->liveData->params.odoKm; + this->liveData->params.soc10time[index] = time_now_epoch; + } + } + this->liveData->params.batHeaterC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(52, 54).c_str(), 1, true); + // + for (int i = 30; i < 32; i++) { // ai/aj position + this->liveData->params.cellVoltage[96 - 30 + i] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(14 + (i * 2), 14 + (i * 2) + 2).c_str(), 1, false) / 50; + } + } + // BMS 7e4 + if (this->liveData->commandRequest.equals("220106")) { + this->liveData->params.coolingWaterTempC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(14, 16).c_str(), 1, false); + } + } + + // TPMS 7a0 + if (this->liveData->currentAtshRequest.equals("ATSH7A0")) { + if (this->liveData->commandRequest.equals("22c00b")) { + this->liveData->params.tireFrontLeftPressureBar = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(14, 16).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722 + this->liveData->params.tireFrontRightPressureBar = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(22, 24).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722 + this->liveData->params.tireRearRightPressureBar = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(30, 32).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722 + this->liveData->params.tireRearLeftPressureBar = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(38, 40).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722 + this->liveData->params.tireFrontLeftTempC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(16, 18).c_str(), 2, false) - 50; // === OK Valid + this->liveData->params.tireFrontRightTempC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(24, 26).c_str(), 2, false) - 50; // === OK Valid + this->liveData->params.tireRearRightTempC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(32, 34).c_str(), 2, false) - 50; // === OK Valid + this->liveData->params.tireRearLeftTempC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(40, 42).c_str(), 2, false) - 50; // === OK Valid + } + } + +} + +/** + loadTestData +*/ +void CarKiaDebugObd2::loadTestData() { + + // VMCU ATSH7E2 + this->liveData->currentAtshRequest = "ATSH7E2"; + // 2101 + this->liveData->commandRequest = "2101"; + this->liveData->responseRowMerged = "6101FFF8000009285A3B0648030000B4179D763404080805000000"; + this->parseRowMerged(); + // 2102 + this->liveData->commandRequest = "2102"; + this->liveData->responseRowMerged = "6102F8FFFC000101000000840FBF83BD33270680953033757F59291C76000001010100000007000000"; + this->liveData->responseRowMerged = "6102F8FFFC000101000000931CC77F4C39040BE09BA7385D8158832175000001010100000007000000"; + this->parseRowMerged(); + + // "ATSH7DF", + this->liveData->currentAtshRequest = "ATSH7DF"; + // 2106 + this->liveData->commandRequest = "2106"; + this->liveData->responseRowMerged = "6106FFFF800000000000000200001B001C001C000600060006000E000000010000000000000000013D013D013E013E00"; + this->parseRowMerged(); + + // AIRCON / ACU ATSH7B3 + this->liveData->currentAtshRequest = "ATSH7B3"; + // 220100 + this->liveData->commandRequest = "220100"; + this->liveData->responseRowMerged = "6201007E5027C8FF7F765D05B95AFFFF5AFF11FFFFFFFFFFFF6AFFFF2DF0757630FFFF00FFFF000000"; + this->liveData->responseRowMerged = "6201007E5027C8FF867C58121010FFFF10FF8EFFFFFFFFFFFF10FFFF0DF0617900FFFF01FFFF000000"; + this->parseRowMerged(); + + // BMS ATSH7E4 + this->liveData->currentAtshRequest = "ATSH7E4"; + // 220101 + this->liveData->commandRequest = "220101"; + this->liveData->responseRowMerged = "620101FFF7E7FF99000000000300B10EFE120F11100F12000018C438C30B00008400003864000035850000153A00001374000647010D017F0BDA0BDA03E8"; + this->liveData->responseRowMerged = "620101FFF7E7FFB3000000000300120F9B111011101011000014CC38CB3B00009100003A510000367C000015FB000013D3000690250D018E0000000003E8"; + this->parseRowMerged(); + // 220102 + this->liveData->commandRequest = "220102"; + this->liveData->responseRowMerged = "620102FFFFFFFFCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBAAAA"; + this->parseRowMerged(); + // 220103 + this->liveData->commandRequest = "220103"; + this->liveData->responseRowMerged = "620103FFFFFFFFCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCACBCACACFCCCBCBCBCBCBCBCBCBAAAA"; + this->parseRowMerged(); + // 220104 + this->liveData->commandRequest = "220104"; + this->liveData->responseRowMerged = "620104FFFFFFFFCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBAAAA"; + this->parseRowMerged(); + // 220105 + this->liveData->commandRequest = "220105"; + this->liveData->responseRowMerged = "620105003fff9000000000000000000F8A86012B4946500101500DAC03E800000000AC0000C7C701000F00000000AAAA"; + this->liveData->responseRowMerged = "620105003FFF90000000000000000014918E012927465000015013BB03E800000000BB0000CBCB01001300000000AAAA"; + this->parseRowMerged(); + // 220106 + this->liveData->commandRequest = "220106"; + this->liveData->responseRowMerged = "620106FFFFFFFF14001A00240000003A7C86B4B30000000928EA00"; + this->parseRowMerged(); + + // BCM / TPMS ATSH7A0 + this->liveData->currentAtshRequest = "ATSH7A0"; + // 22c00b + this->liveData->commandRequest = "22c00b"; + this->liveData->responseRowMerged = "62C00BFFFF0000B93D0100B43E0100B43D0100BB3C0100AAAAAAAA"; + this->parseRowMerged(); + + // ATSH7C6 + this->liveData->currentAtshRequest = "ATSH7C6"; + // 22b002 + this->liveData->commandRequest = "22b002"; + this->liveData->responseRowMerged = "62B002E0000000FFB400330B0000000000000000"; + this->parseRowMerged(); + + this->liveData->params.batModuleTempC[0] = 28; + this->liveData->params.batModuleTempC[1] = 29; + this->liveData->params.batModuleTempC[2] = 28; + this->liveData->params.batModuleTempC[3] = 30; + // This is more accurate than min/max from BMS. It's required to detect kona/eniro cold gates (min 15C is needed > 43kW charging, min 25C is needed > 58kW charging) + this->liveData->params.batMinC = this->liveData->params.batMaxC = this->liveData->params.batModuleTempC[0]; + for (uint16_t i = 1; i < this->liveData->params.batModuleTempCount; i++) { + if (this->liveData->params.batModuleTempC[i] < this->liveData->params.batMinC) + this->liveData->params.batMinC = this->liveData->params.batModuleTempC[i]; + if (this->liveData->params.batModuleTempC[i] > this->liveData->params.batMaxC) + this->liveData->params.batMaxC = this->liveData->params.batModuleTempC[i]; + } + this->liveData->params.batTempC = this->liveData->params.batMinC; + + // + this->liveData->params.soc10ced[10] = 2200; + this->liveData->params.soc10cec[10] = 2500; + this->liveData->params.soc10odo[10] = 13000; + this->liveData->params.soc10time[10] = 13000; + this->liveData->params.soc10ced[9] = this->liveData->params.soc10ced[10] + 6.4; + this->liveData->params.soc10cec[9] = this->liveData->params.soc10cec[10] + 0; + this->liveData->params.soc10odo[9] = this->liveData->params.soc10odo[10] + 30; + this->liveData->params.soc10time[9] = this->liveData->params.soc10time[10] + 900; + this->liveData->params.soc10ced[8] = this->liveData->params.soc10ced[9] + 6.8; + this->liveData->params.soc10cec[8] = this->liveData->params.soc10cec[9] + 0; + this->liveData->params.soc10odo[8] = this->liveData->params.soc10odo[9] + 30; + this->liveData->params.soc10time[8] = this->liveData->params.soc10time[9] + 900; + this->liveData->params.soc10ced[7] = this->liveData->params.soc10ced[8] + 7.2; + this->liveData->params.soc10cec[7] = this->liveData->params.soc10cec[8] + 0.6; + this->liveData->params.soc10odo[7] = this->liveData->params.soc10odo[8] + 30; + this->liveData->params.soc10time[7] = this->liveData->params.soc10time[8] + 900; + this->liveData->params.soc10ced[6] = this->liveData->params.soc10ced[7] + 6.7; + this->liveData->params.soc10cec[6] = this->liveData->params.soc10cec[7] + 0; + this->liveData->params.soc10odo[6] = this->liveData->params.soc10odo[7] + 30; + this->liveData->params.soc10time[6] = this->liveData->params.soc10time[7] + 900; + this->liveData->params.soc10ced[5] = this->liveData->params.soc10ced[6] + 6.7; + this->liveData->params.soc10cec[5] = this->liveData->params.soc10cec[6] + 0; + this->liveData->params.soc10odo[5] = this->liveData->params.soc10odo[6] + 30; + this->liveData->params.soc10time[5] = this->liveData->params.soc10time[6] + 900; + this->liveData->params.soc10ced[4] = this->liveData->params.soc10ced[5] + 6.4; + this->liveData->params.soc10cec[4] = this->liveData->params.soc10cec[5] + 0.3; + this->liveData->params.soc10odo[4] = this->liveData->params.soc10odo[5] + 30; + this->liveData->params.soc10time[4] = this->liveData->params.soc10time[5] + 900; + this->liveData->params.soc10ced[3] = this->liveData->params.soc10ced[4] + 6.4; + this->liveData->params.soc10cec[3] = this->liveData->params.soc10cec[4] + 0; + this->liveData->params.soc10odo[3] = this->liveData->params.soc10odo[4] + 30; + this->liveData->params.soc10time[3] = this->liveData->params.soc10time[4] + 900; + this->liveData->params.soc10ced[2] = this->liveData->params.soc10ced[3] + 5.4; + this->liveData->params.soc10cec[2] = this->liveData->params.soc10cec[3] + 0.1; + this->liveData->params.soc10odo[2] = this->liveData->params.soc10odo[3] + 30; + this->liveData->params.soc10time[2] = this->liveData->params.soc10time[3] + 900; + this->liveData->params.soc10ced[1] = this->liveData->params.soc10ced[2] + 6.2; + this->liveData->params.soc10cec[1] = this->liveData->params.soc10cec[2] + 0.1; + this->liveData->params.soc10odo[1] = this->liveData->params.soc10odo[2] + 30; + this->liveData->params.soc10time[1] = this->liveData->params.soc10time[2] + 900; + this->liveData->params.soc10ced[0] = this->liveData->params.soc10ced[1] + 2.9; + this->liveData->params.soc10cec[0] = this->liveData->params.soc10cec[1] + 0.5; + this->liveData->params.soc10odo[0] = this->liveData->params.soc10odo[1] + 15; + this->liveData->params.soc10time[0] = this->liveData->params.soc10time[1] + 900; + +} + +#endif // CARKIADEBUGOBD2_CPP diff --git a/CarKiaDebugObd2.h b/CarKiaDebugObd2.h new file mode 100644 index 0000000..35d7c64 --- /dev/null +++ b/CarKiaDebugObd2.h @@ -0,0 +1,16 @@ +#ifndef CARKIADEBUGOBD2_H +#define CARKIADEBUGOBD2_H + +#include "CarInterface.h" + +class CarKiaDebugObd2 : public CarInterface { + + private: + + public: + void activateCommandQueue() override; + void parseRowMerged() override; + void loadTestData() override; +}; + +#endif // CARKIADEBUGOBD2_H diff --git a/CarKiaEniro.cpp b/CarKiaEniro.cpp new file mode 100644 index 0000000..fc7e604 --- /dev/null +++ b/CarKiaEniro.cpp @@ -0,0 +1,446 @@ +#ifndef CARKIAENIRO_CPP +#define CARKIAENIRO_CPP + +/* + * eNiro/Kona chargings limits depending on battery temperature (min.value of 01-04 battery module) + >= 35°C BMS allows max 180A + >= 25°C without limit (200A) + >= 15°C BMS allows max 120A + >= 5°C BMS allows max 90A + >= 1°C BMS allows max 60A + <= 0°C BMS allows max 40A + */ + +#include +#include +#include +#include +#include +#include "LiveData.h" +#include "CarKiaEniro.h" + +#define commandQueueCountKiaENiro 30 +#define commandQueueLoopFromKiaENiro 10 + +/** + * activateCommandQueue + */ +void CarKiaEniro::activateCommandQueue() { + + String commandQueueKiaENiro[commandQueueCountKiaENiro] = { + "AT Z", // Reset all + "AT I", // Print the version ID + "AT S0", // Printing of spaces on + "AT E0", // Echo off + "AT L0", // Linefeeds off + "AT SP 6", // Select protocol to ISO 15765-4 CAN (11 bit ID, 500 kbit/s) + //"AT AL", // Allow Long (>7 byte) messages + //"AT AR", // Automatically receive + //"AT H1", // Headers on (debug only) + //"AT D1", // Display of the DLC on + //"AT CAF0", // Automatic formatting off + ////"AT AT0", // disabled adaptive timing + "AT DP", + "AT ST16", // reduced timeout to 1, orig.16 + + // Loop from (KIA ENIRO) + + // ABS / ESP + AHB + "ATSH7D1", + "22C101", // brake, park/drive mode + + // IGPM + "ATSH770", + "22BC03", // low beam + "22BC06", // brake light + + // VMCU + "ATSH7E2", + "2101", // speed, ... + "2102", // aux, ... + + // BMS + "ATSH7E4", + "220101", // power kw, ... + "220102", // cell voltages + "220103", // cell voltages + "220104", // cell voltages + "220105", // soh, soc, .. + "220106", // cooling water temp + + // Aircondition + "ATSH7B3", + "220100", // in/out temp + "220102", // coolant temp1, 2 + + // BCM / TPMS + "ATSH7A0", + "22c00b", // tire pressure/temp + + // CLUSTER MODULE + "ATSH7C6", + "22B002", // odo + + }; + + // 39 or 64 kWh model? + this->liveData->params.batModuleTempCount = 4; + this->liveData->params.batteryTotalAvailableKWh = 64; + // =(I18*0,615)*(1+(I18*0,0008)) soc to kwh niro ev 2020 + if (this->liveData->settings.carType == CAR_KIA_ENIRO_2020_39 || this->liveData->settings.carType == CAR_HYUNDAI_KONA_2020_39) { + this->liveData->params.batteryTotalAvailableKWh = 39.2; + } + + // Empty and fill command queue + for (int i = 0; i < 300; i++) { + this->liveData->commandQueue[i] = ""; + } + for (int i = 0; i < commandQueueCountKiaENiro; i++) { + this->liveData->commandQueue[i] = commandQueueKiaENiro[i]; + } + + this->liveData->commandQueueLoopFrom = commandQueueLoopFromKiaENiro; + this->liveData->commandQueueCount = commandQueueCountKiaENiro; +} + +/** + * parseRowMerged + */ +void CarKiaEniro::parseRowMerged() { + + bool tempByte; + + // ABS / ESP + AHB 7D1 + if (this->liveData->currentAtshRequest.equals("ATSH7D1")) { + if (this->liveData->commandRequest.equals("22C101")) { + uint8_t driveMode = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(22, 24).c_str(), 1, false); + this->liveData->params.forwardDriveMode = (driveMode == 4); + this->liveData->params.reverseDriveMode = (driveMode == 2); + this->liveData->params.parkModeOrNeutral = (driveMode == 1); + } + } + + // IGPM + if (this->liveData->currentAtshRequest.equals("ATSH770")) { + if (this->liveData->commandRequest.equals("22BC03")) { + tempByte = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(16, 18).c_str(), 1, false); + 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.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); + this->liveData->params.dayLights = (bitRead(this->liveData->params.lightInfo, 3) == 1); + } + if (this->liveData->commandRequest.equals("22BC06")) { + this->liveData->params.brakeLightInfo = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(14, 16).c_str(), 1, false); + this->liveData->params.brakeLights = (bitRead(this->liveData->params.brakeLightInfo, 5) == 1); + } + } + + // VMCU 7E2 + if (this->liveData->currentAtshRequest.equals("ATSH7E2")) { + if (this->liveData->commandRequest.equals("2101")) { + this->liveData->params.speedKmh = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(32, 36).c_str(), 2, false) * 0.0155; // / 100.0 *1.609 = real to gps is 1.750 + if (this->liveData->params.speedKmh < -99 || this->liveData->params.speedKmh > 200) + this->liveData->params.speedKmh = 0; + } + if (this->liveData->commandRequest.equals("2102")) { + this->liveData->params.auxPerc = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(50, 52).c_str(), 1, false); + this->liveData->params.auxCurrentAmp = - this->liveData->hexToDec(this->liveData->responseRowMerged.substring(46, 50).c_str(), 2, true) / 1000.0; + } + } + + // Cluster module 7c6 + if (this->liveData->currentAtshRequest.equals("ATSH7C6")) { + if (this->liveData->commandRequest.equals("22B002")) { + this->liveData->params.odoKm = float(strtol(this->liveData->responseRowMerged.substring(18, 24).c_str(), 0, 16)); + } + } + + // Aircon 7b3 + if (this->liveData->currentAtshRequest.equals("ATSH7B3")) { + if (this->liveData->commandRequest.equals("220100")) { + this->liveData->params.indoorTemperature = (this->liveData->hexToDec(this->liveData->responseRowMerged.substring(16, 18).c_str(), 1, false) / 2) - 40; + this->liveData->params.outdoorTemperature = (this->liveData->hexToDec(this->liveData->responseRowMerged.substring(18, 20).c_str(), 1, false) / 2) - 40; + } + if (this->liveData->commandRequest.equals("220102") && this->liveData->responseRowMerged.substring(12, 14) == "00") { + this->liveData->params.coolantTemp1C = (this->liveData->hexToDec(this->liveData->responseRowMerged.substring(14, 16).c_str(), 1, false) / 2) - 40; + this->liveData->params.coolantTemp2C = (this->liveData->hexToDec(this->liveData->responseRowMerged.substring(16, 18).c_str(), 1, false) / 2) - 40; + } + } + + // BMS 7e4 + if (this->liveData->currentAtshRequest.equals("ATSH7E4")) { + if (this->liveData->commandRequest.equals("220101")) { + this->liveData->params.cumulativeEnergyChargedKWh = float(strtol(this->liveData->responseRowMerged.substring(82, 90).c_str(), 0, 16)) / 10.0; + if (this->liveData->params.cumulativeEnergyChargedKWhStart == -1) + this->liveData->params.cumulativeEnergyChargedKWhStart = this->liveData->params.cumulativeEnergyChargedKWh; + this->liveData->params.cumulativeEnergyDischargedKWh = float(strtol(this->liveData->responseRowMerged.substring(90, 98).c_str(), 0, 16)) / 10.0; + if (this->liveData->params.cumulativeEnergyDischargedKWhStart == -1) + this->liveData->params.cumulativeEnergyDischargedKWhStart = this->liveData->params.cumulativeEnergyDischargedKWh; + this->liveData->params.availableChargePower = float(strtol(this->liveData->responseRowMerged.substring(16, 20).c_str(), 0, 16)) / 100.0; + this->liveData->params.availableDischargePower = float(strtol(this->liveData->responseRowMerged.substring(20, 24).c_str(), 0, 16)) / 100.0; + //this->liveData->params.isolationResistanceKOhm = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(118, 122).c_str(), 2, true); + this->liveData->params.batFanStatus = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(60, 62).c_str(), 2, true); + this->liveData->params.batFanFeedbackHz = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(62, 64).c_str(), 2, true); + this->liveData->params.auxVoltage = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(64, 66).c_str(), 2, true) / 10.0; + this->liveData->params.batPowerAmp = - this->liveData->hexToDec(this->liveData->responseRowMerged.substring(26, 30).c_str(), 2, true) / 10.0; + this->liveData->params.batVoltage = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(30, 34).c_str(), 2, false) / 10.0; + this->liveData->params.batPowerKw = (this->liveData->params.batPowerAmp * this->liveData->params.batVoltage) / 1000.0; + if (this->liveData->params.batPowerKw < 0) // Reset charging start time + this->liveData->params.chargingStartTime = this->liveData->params.currentTime; + this->liveData->params.batPowerKwh100 = this->liveData->params.batPowerKw / this->liveData->params.speedKmh * 100; + this->liveData->params.batCellMaxV = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(52, 54).c_str(), 1, false) / 50.0; + this->liveData->params.batCellMinV = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(56, 58).c_str(), 1, false) / 50.0; + this->liveData->params.batModuleTempC[0] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(38, 40).c_str(), 1, true); + this->liveData->params.batModuleTempC[1] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(40, 42).c_str(), 1, true); + this->liveData->params.batModuleTempC[2] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(42, 44).c_str(), 1, true); + this->liveData->params.batModuleTempC[3] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(44, 46).c_str(), 1, true); + this->liveData->params.motorRpm = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(112, 116).c_str(), 2, false); + //this->liveData->params.batTempC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(36, 38).c_str(), 1, true); + //this->liveData->params.batMaxC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(34, 36).c_str(), 1, true); + //this->liveData->params.batMinC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(36, 38).c_str(), 1, true); + + // This is more accurate than min/max from BMS. It's required to detect kona/eniro cold gates (min 15C is needed > 43kW charging, min 25C is needed > 58kW charging) + this->liveData->params.batMinC = this->liveData->params.batMaxC = this->liveData->params.batModuleTempC[0]; + for (uint16_t i = 1; i < this->liveData->params.batModuleTempCount; i++) { + if (this->liveData->params.batModuleTempC[i] < this->liveData->params.batMinC) + this->liveData->params.batMinC = this->liveData->params.batModuleTempC[i]; + if (this->liveData->params.batModuleTempC[i] > this->liveData->params.batMaxC) + this->liveData->params.batMaxC = this->liveData->params.batModuleTempC[i]; + } + this->liveData->params.batTempC = this->liveData->params.batMinC; + + this->liveData->params.batInletC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(50, 52).c_str(), 1, true); + if (this->liveData->params.speedKmh < 10 && this->liveData->params.batPowerKw >= 1 && this->liveData->params.socPerc > 0 && this->liveData->params.socPerc <= 100) { + if ( this->liveData->params.chargingGraphMinKw[int(this->liveData->params.socPerc)] < 0 || this->liveData->params.batPowerKw < this->liveData->params.chargingGraphMinKw[int(this->liveData->params.socPerc)]) + this->liveData->params.chargingGraphMinKw[int(this->liveData->params.socPerc)] = this->liveData->params.batPowerKw; + if ( this->liveData->params.chargingGraphMaxKw[int(this->liveData->params.socPerc)] < 0 || this->liveData->params.batPowerKw > this->liveData->params.chargingGraphMaxKw[int(this->liveData->params.socPerc)]) + this->liveData->params.chargingGraphMaxKw[int(this->liveData->params.socPerc)] = this->liveData->params.batPowerKw; + this->liveData->params.chargingGraphBatMinTempC[int(this->liveData->params.socPerc)] = this->liveData->params.batMinC; + this->liveData->params.chargingGraphBatMaxTempC[int(this->liveData->params.socPerc)] = this->liveData->params.batMaxC; + this->liveData->params.chargingGraphHeaterTempC[int(this->liveData->params.socPerc)] = this->liveData->params.batHeaterC; + this->liveData->params.chargingGraphWaterCoolantTempC[int(this->liveData->params.socPerc)] = this->liveData->params.coolingWaterTempC; + } + } + // BMS 7e4 + if (this->liveData->commandRequest.equals("220102") && this->liveData->responseRowMerged.substring(12, 14) == "FF") { + for (int i = 0; i < 32; i++) { + this->liveData->params.cellVoltage[i] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(14 + (i * 2), 14 + (i * 2) + 2).c_str(), 1, false) / 50; + } + } + // BMS 7e4 + if (this->liveData->commandRequest.equals("220103")) { + for (int i = 0; i < 32; i++) { + this->liveData->params.cellVoltage[32 + i] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(14 + (i * 2), 14 + (i * 2) + 2).c_str(), 1, false) / 50; + } + } + // BMS 7e4 + if (this->liveData->commandRequest.equals("220104")) { + for (int i = 0; i < 32; i++) { + this->liveData->params.cellVoltage[64 + i] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(14 + (i * 2), 14 + (i * 2) + 2).c_str(), 1, false) / 50; + } + } + // BMS 7e4 + if (this->liveData->commandRequest.equals("220105")) { + this->liveData->params.socPercPrevious = this->liveData->params.socPerc; + this->liveData->params.sohPerc = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(56, 60).c_str(), 2, false) / 10.0; + this->liveData->params.socPerc = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(68, 70).c_str(), 1, false) / 2.0; + + // Soc10ced table, record x0% CEC/CED table (ex. 90%->89%, 80%->79%) + if (this->liveData->params.socPercPrevious - this->liveData->params.socPerc > 0) { + byte index = (int(this->liveData->params.socPerc) == 4) ? 0 : (int)(this->liveData->params.socPerc / 10) + 1; + if ((int(this->liveData->params.socPerc) % 10 == 9 || int(this->liveData->params.socPerc) == 4) && this->liveData->params.soc10ced[index] == -1) { + this->liveData->params.soc10ced[index] = this->liveData->params.cumulativeEnergyDischargedKWh; + this->liveData->params.soc10cec[index] = this->liveData->params.cumulativeEnergyChargedKWh; + this->liveData->params.soc10odo[index] = this->liveData->params.odoKm; + this->liveData->params.soc10time[index] = this->liveData->params.currentTime; + } + } + this->liveData->params.bmsUnknownTempA = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(30, 32).c_str(), 1, true); + this->liveData->params.batHeaterC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(52, 54).c_str(), 1, true); + this->liveData->params.bmsUnknownTempB = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(82, 84).c_str(), 1, true); + // + for (int i = 30; i < 32; i++) { // ai/aj position + this->liveData->params.cellVoltage[96 - 30 + i] = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(14 + (i * 2), 14 + (i * 2) + 2).c_str(), 1, false) / 50; + } + } + // BMS 7e4 + if (this->liveData->commandRequest.equals("220106")) { + this->liveData->params.coolingWaterTempC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(14, 16).c_str(), 1, false); + this->liveData->params.bmsUnknownTempC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(18, 20).c_str(), 1, true); + this->liveData->params.bmsUnknownTempD = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(46, 48).c_str(), 1, true); + } + } + + // TPMS 7a0 + if (this->liveData->currentAtshRequest.equals("ATSH7A0")) { + if (this->liveData->commandRequest.equals("22c00b")) { + this->liveData->params.tireFrontLeftPressureBar = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(14, 16).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722 + this->liveData->params.tireFrontRightPressureBar = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(22, 24).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722 + this->liveData->params.tireRearRightPressureBar = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(30, 32).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722 + this->liveData->params.tireRearLeftPressureBar = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(38, 40).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722 + this->liveData->params.tireFrontLeftTempC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(16, 18).c_str(), 2, false) - 50; // === OK Valid + this->liveData->params.tireFrontRightTempC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(24, 26).c_str(), 2, false) - 50; // === OK Valid + this->liveData->params.tireRearRightTempC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(32, 34).c_str(), 2, false) - 50; // === OK Valid + this->liveData->params.tireRearLeftTempC = this->liveData->hexToDec(this->liveData->responseRowMerged.substring(40, 42).c_str(), 2, false) - 50; // === OK Valid + } + } +} + +/** + * loadTestData + */ +void CarKiaEniro::loadTestData() { + + // IGPM + this->liveData->currentAtshRequest = "ATSH770"; + // 22BC03 + this->liveData->commandRequest = "22BC03"; + this->liveData->responseRowMerged = "62BC03FDEE7C730A600000AAAA"; + this->parseRowMerged(); + + // ABS / ESP + AHB ATSH7D1 + this->liveData->currentAtshRequest = "ATSH7D1"; + // 2101 + this->liveData->commandRequest = "22C101"; + this->liveData->responseRowMerged = "62C1015FD7E7D0FFFF00FF04D0D400000000FF7EFF0030F5010000FFFF7F6307F207FE05FF00FF3FFFFFAAAAAAAAAAAA"; + this->parseRowMerged(); + + // VMCU ATSH7E2 + this->liveData->currentAtshRequest = "ATSH7E2"; + // 2101 + this->liveData->commandRequest = "2101"; + this->liveData->responseRowMerged = "6101FFF8000009285A3B0648030000B4179D763404080805000000"; + this->parseRowMerged(); + // 2102 + this->liveData->commandRequest = "2102"; + this->liveData->responseRowMerged = "6102F8FFFC000101000000840FBF83BD33270680953033757F59291C76000001010100000007000000"; + this->liveData->responseRowMerged = "6102F8FFFC000101000000931CC77F4C39040BE09BA7385D8158832175000001010100000007000000"; + this->parseRowMerged(); + + // "ATSH7DF", + this->liveData->currentAtshRequest = "ATSH7DF"; + // 2106 + this->liveData->commandRequest = "2106"; + this->liveData->responseRowMerged = "6106FFFF800000000000000200001B001C001C000600060006000E000000010000000000000000013D013D013E013E00"; + this->parseRowMerged(); + + // AIRCON / ACU ATSH7B3 + this->liveData->currentAtshRequest = "ATSH7B3"; + // 220100 + this->liveData->commandRequest = "220100"; + this->liveData->responseRowMerged = "6201007E5027C8FF7F765D05B95AFFFF5AFF11FFFFFFFFFFFF6AFFFF2DF0757630FFFF00FFFF000000"; + this->liveData->responseRowMerged = "6201007E5027C8FF867C58121010FFFF10FF8EFFFFFFFFFFFF10FFFF0DF0617900FFFF01FFFF000000"; + this->parseRowMerged(); + + // BMS ATSH7E4 + this->liveData->currentAtshRequest = "ATSH7E4"; + // 220101 + this->liveData->commandRequest = "220101"; + this->liveData->responseRowMerged = "620101FFF7E7FF99000000000300B10EFE120F11100F12000018C438C30B00008400003864000035850000153A00001374000647010D017F0BDA0BDA03E8"; + this->liveData->responseRowMerged = "620101FFF7E7FFB3000000000300120F9B111011101011000014CC38CB3B00009100003A510000367C000015FB000013D3000690250D018E0000000003E8"; + this->parseRowMerged(); + // 220102 + this->liveData->commandRequest = "220102"; + this->liveData->responseRowMerged = "620102FFFFFFFFCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBAAAA"; + this->parseRowMerged(); + // 220103 + this->liveData->commandRequest = "220103"; + this->liveData->responseRowMerged = "620103FFFFFFFFCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCACBCACACFCCCBCBCBCBCBCBCBCBAAAA"; + this->parseRowMerged(); + // 220104 + this->liveData->commandRequest = "220104"; + this->liveData->responseRowMerged = "620104FFFFFFFFCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBAAAA"; + this->parseRowMerged(); + // 220105 + this->liveData->commandRequest = "220105"; + this->liveData->responseRowMerged = "620105003fff9000000000000000000F8A86012B4946500101500DAC03E800000000AC0000C7C701000F00000000AAAA"; + this->liveData->responseRowMerged = "620105003FFF90000000000000000014918E012927465000015013BB03E800000000BB0000CBCB01001300000000AAAA"; + this->parseRowMerged(); + // 220106 + this->liveData->commandRequest = "220106"; + this->liveData->responseRowMerged = "620106FFFFFFFF14001A00240000003A7C86B4B30000000928EA00"; + this->parseRowMerged(); + + // BCM / TPMS ATSH7A0 + this->liveData->currentAtshRequest = "ATSH7A0"; + // 22c00b + this->liveData->commandRequest = "22c00b"; + this->liveData->responseRowMerged = "62C00BFFFF0000B93D0100B43E0100B43D0100BB3C0100AAAAAAAA"; + this->parseRowMerged(); + + // ATSH7C6 + this->liveData->currentAtshRequest = "ATSH7C6"; + // 22b002 + this->liveData->commandRequest = "22b002"; + this->liveData->responseRowMerged = "62B002E0000000FFB400330B0000000000000000"; + this->parseRowMerged(); + + this->liveData->params.batModuleTempC[0] = 28; + this->liveData->params.batModuleTempC[1] = 29; + this->liveData->params.batModuleTempC[2] = 28; + this->liveData->params.batModuleTempC[3] = 30; + + // This is more accurate than min/max from BMS. It's required to detect kona/eniro cold gates (min 15C is needed > 43kW charging, min 25C is needed > 58kW charging) + this->liveData->params.batMinC = this->liveData->params.batMaxC = this->liveData->params.batModuleTempC[0]; + for (uint16_t i = 1; i < this->liveData->params.batModuleTempCount; i++) { + if (this->liveData->params.batModuleTempC[i] < this->liveData->params.batMinC) + this->liveData->params.batMinC = this->liveData->params.batModuleTempC[i]; + if (this->liveData->params.batModuleTempC[i] > this->liveData->params.batMaxC) + this->liveData->params.batMaxC = this->liveData->params.batModuleTempC[i]; + } + this->liveData->params.batTempC = this->liveData->params.batMinC; + + + // + this->liveData->params.soc10ced[10] = 2200; + this->liveData->params.soc10cec[10] = 2500; + this->liveData->params.soc10odo[10] = 13000; + this->liveData->params.soc10time[10] = 13000; + this->liveData->params.soc10ced[9] = this->liveData->params.soc10ced[10] + 6.4; + this->liveData->params.soc10cec[9] = this->liveData->params.soc10cec[10] + 0; + this->liveData->params.soc10odo[9] = this->liveData->params.soc10odo[10] + 30; + this->liveData->params.soc10time[9] = this->liveData->params.soc10time[10] + 900; + this->liveData->params.soc10ced[8] = this->liveData->params.soc10ced[9] + 6.8; + this->liveData->params.soc10cec[8] = this->liveData->params.soc10cec[9] + 0; + this->liveData->params.soc10odo[8] = this->liveData->params.soc10odo[9] + 30; + this->liveData->params.soc10time[8] = this->liveData->params.soc10time[9] + 900; + this->liveData->params.soc10ced[7] = this->liveData->params.soc10ced[8] + 7.2; + this->liveData->params.soc10cec[7] = this->liveData->params.soc10cec[8] + 0.6; + this->liveData->params.soc10odo[7] = this->liveData->params.soc10odo[8] + 30; + this->liveData->params.soc10time[7] = this->liveData->params.soc10time[8] + 900; + this->liveData->params.soc10ced[6] = this->liveData->params.soc10ced[7] + 6.7; + this->liveData->params.soc10cec[6] = this->liveData->params.soc10cec[7] + 0; + this->liveData->params.soc10odo[6] = this->liveData->params.soc10odo[7] + 30; + this->liveData->params.soc10time[6] = this->liveData->params.soc10time[7] + 900; + this->liveData->params.soc10ced[5] = this->liveData->params.soc10ced[6] + 6.7; + this->liveData->params.soc10cec[5] = this->liveData->params.soc10cec[6] + 0; + this->liveData->params.soc10odo[5] = this->liveData->params.soc10odo[6] + 30; + this->liveData->params.soc10time[5] = this->liveData->params.soc10time[6] + 900; + this->liveData->params.soc10ced[4] = this->liveData->params.soc10ced[5] + 6.4; + this->liveData->params.soc10cec[4] = this->liveData->params.soc10cec[5] + 0.3; + this->liveData->params.soc10odo[4] = this->liveData->params.soc10odo[5] + 30; + this->liveData->params.soc10time[4] = this->liveData->params.soc10time[5] + 900; + this->liveData->params.soc10ced[3] = this->liveData->params.soc10ced[4] + 6.4; + this->liveData->params.soc10cec[3] = this->liveData->params.soc10cec[4] + 0; + this->liveData->params.soc10odo[3] = this->liveData->params.soc10odo[4] + 30; + this->liveData->params.soc10time[3] = this->liveData->params.soc10time[4] + 900; + this->liveData->params.soc10ced[2] = this->liveData->params.soc10ced[3] + 5.4; + this->liveData->params.soc10cec[2] = this->liveData->params.soc10cec[3] + 0.1; + this->liveData->params.soc10odo[2] = this->liveData->params.soc10odo[3] + 30; + this->liveData->params.soc10time[2] = this->liveData->params.soc10time[3] + 900; + this->liveData->params.soc10ced[1] = this->liveData->params.soc10ced[2] + 6.2; + this->liveData->params.soc10cec[1] = this->liveData->params.soc10cec[2] + 0.1; + this->liveData->params.soc10odo[1] = this->liveData->params.soc10odo[2] + 30; + this->liveData->params.soc10time[1] = this->liveData->params.soc10time[2] + 900; + this->liveData->params.soc10ced[0] = this->liveData->params.soc10ced[1] + 2.9; + this->liveData->params.soc10cec[0] = this->liveData->params.soc10cec[1] + 0.5; + this->liveData->params.soc10odo[0] = this->liveData->params.soc10odo[1] + 15; + this->liveData->params.soc10time[0] = this->liveData->params.soc10time[1] + 900; + +} + +#endif // CARKIAENIRO_CPP diff --git a/CarKiaEniro.h b/CarKiaEniro.h new file mode 100644 index 0000000..a3f282a --- /dev/null +++ b/CarKiaEniro.h @@ -0,0 +1,16 @@ +#ifndef CARKIAENIRO_H +#define CARKIAENIRO_H + +#include "CarInterface.h" + +class CarKiaEniro : public CarInterface { + + private: + + public: + void activateCommandQueue() override; + void parseRowMerged() override; + void loadTestData() override; +}; + +#endif // CARKIAENIRO_H diff --git a/INSTALLATION.md b/INSTALLATION.md new file mode 100644 index 0000000..c58a6ee --- /dev/null +++ b/INSTALLATION.md @@ -0,0 +1,34 @@ +# INSTALLATION + +## Quick installation from binary files with ESP32 flash tool + +M5STACK (Many thanks to DimZen) + +https://docs.google.com/document/d/17vJmeveNfN0exQy9wKC-5igU8zzNjsuOn1DPuPV_yJA/edit?usp=sharing + +TTGO-T4 (older) + +https://docs.google.com/document/d/1nEezrtXY-8X6mQ1hiZVWDjBVse1sXQg1SlnizaRmJwU/edit?usp=sharing + +## Installation from sources +- install arduino IDE + ESP32 support +- https://github.com/Bodmer/TFT_eSPI - display library +- Configure TFT eSPI + W:\Documents\Arduino\libraries\TFT_eSP\User_Setup_Select.h +``` +// Comment +//#include // Default setup is root library folder +// And uncomment +#include // Setup file for ESP32 and TTGO T4 version 1.3 +``` + +My configuration +- Board ESP32 Dev module +- Upload speed 921600 +- CPU freq: 240MHz (Wifi/BT) +- Flash freq: 80MHz +- Flash mode: QIO +- Flash size 4MB (32mb) +- Partion scheme: default 4MB with spiffs +- Core debug level: none +- PSRAM: disable diff --git a/LiveData.cpp b/LiveData.cpp new file mode 100644 index 0000000..8a0d159 --- /dev/null +++ b/LiveData.cpp @@ -0,0 +1,158 @@ + +#ifndef LIVEDATA_CPP +#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) + For 4 byte change int to long and add part for signed numbers +*/ +float LiveData::hexToDec(String hexString, byte bytes, bool signedNum) { + + unsigned int decValue = 0; + unsigned int nextInt; + + for (int i = 0; i < hexString.length(); i++) { + nextInt = int(hexString.charAt(i)); + if (nextInt >= 48 && nextInt <= 57) nextInt = map(nextInt, 48, 57, 0, 9); + if (nextInt >= 65 && nextInt <= 70) nextInt = map(nextInt, 65, 70, 10, 15); + if (nextInt >= 97 && nextInt <= 102) nextInt = map(nextInt, 97, 102, 10, 15); + nextInt = constrain(nextInt, 0, 15); + decValue = (decValue * 16) + nextInt; + } + + // Unsigned - do nothing + if (!signedNum) { + return decValue; + } + // Signed for 1, 2 bytes + if (bytes == 1) { + return (decValue > 127 ? (float)decValue - 256.0 : decValue); + } + 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/struct.h b/LiveData.h similarity index 62% rename from struct.h rename to LiveData.h index d24f72c..77f9a30 100644 --- a/struct.h +++ b/LiveData.h @@ -1,4 +1,15 @@ +#ifndef LIVEDATA_H +#define LIVEDATA_H + +#include +#include +#include +#include +#include +#include +#include "config.h" + // SUPPORTED CARS #define CAR_KIA_ENIRO_2020_64 0 #define CAR_HYUNDAI_KONA_2020_64 1 @@ -18,22 +29,15 @@ #define SCREEN_SOC10 6 #define SCREEN_DEBUG 7 -// Commands loop -uint16_t commandQueueCount; -uint16_t commandQueueLoopFrom; -String commandQueue[300]; -String responseRow; -String responseRowMerged; -uint16_t commandQueueIndex; -bool canSendNextAtCommand = false; -String commandRequest = ""; -String currentAtshRequest = ""; - // Structure with realtime values typedef struct { - time_t currentTime; - time_t chargingStartTime; - time_t automatickShutdownTimer; + time_t currentTime; + time_t chargingStartTime; + time_t automaticShutdownTimer; +#ifdef SIM800L_ENABLED + time_t lastDataSent; + bool sim800l_enabled; +#endif //SIM800L_ENABLED bool ignitionOn; bool ignitionOnPrevious; bool forwardDriveMode; @@ -45,7 +49,7 @@ typedef struct { uint8_t lightInfo; uint8_t brakeLightInfo; uint8_t espState; - float batteryTotalAvailableKWh; + float batteryTotalAvailableKWh; float speedKmh; float motorRpm; float odoKm; @@ -110,19 +114,19 @@ typedef struct { time_t soc10time[11]; // time for avg speed // additional /* - uint8_t bmsIgnition; - uint8_t bmsMainRelay; - uint8_t highVoltageCharging; - float inverterCapacitorVoltage; - float normalChargePort; - float rapidChargePort; - float operationTimeHours;*/ + uint8_t bmsIgnition; + uint8_t bmsMainRelay; + uint8_t highVoltageCharging; + float inverterCapacitorVoltage; + float normalChargePort; + float rapidChargePort; + float operationTimeHours;*/ } PARAMS_STRUC; // Setting stored to flash typedef struct { byte initFlag; // 183 value - byte settingsVersion; // current 4 + byte settingsVersion; // current 3 uint16_t carType; // 0 - Kia eNiro 2020, 1 - Hyundai Kona 2020, 2 - Hyudai Ioniq 2018 char obdMacAddress[20]; char serviceUUID[40]; @@ -137,41 +141,60 @@ typedef struct { byte lcdBrightness; // 0 - auto, 1 .. 100% byte debugScreen; // 0 - off, 1 - on byte predrawnChargingGraphs; // 0 - off, 1 - on - byte wifiEnable; // 0 off 1 on used for NTP datetime sync - char wifiSsid[32]; - char wifiPassword[32]; - byte sdcardAutoRecord; // 0 off 1 on - +#ifdef SIM800L_ENABLED + char gprsApn[64]; + char remoteApiSrvr[64]; + char remoteApiKey[13]; +#endif //SIM800L_ENABLED } SETTINGS_STRUC; -PARAMS_STRUC params; // Realtime sensor values -SETTINGS_STRUC settings, tmpSettings; // Settings stored into flash -/** - Hex to dec (1-2 byte values, signed/unsigned) - For 4 byte change int to long and add part for signed numbers -*/ -float hexToDec(String hexString, byte bytes = 2, bool signedNum = true) { +// +class LiveData { + private: + public: + // Command loop + uint16_t commandQueueCount; + uint16_t commandQueueLoopFrom; + String commandQueue[300]; + String responseRow; + String responseRowMerged; + uint16_t commandQueueIndex; + 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; - unsigned int decValue = 0; - unsigned int nextInt; + // 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 - for (int i = 0; i < hexString.length(); i++) { - nextInt = int(hexString.charAt(i)); - if (nextInt >= 48 && nextInt <= 57) nextInt = map(nextInt, 48, 57, 0, 9); - if (nextInt >= 65 && nextInt <= 70) nextInt = map(nextInt, 65, 70, 10, 15); - if (nextInt >= 97 && nextInt <= 102) nextInt = map(nextInt, 97, 102, 10, 15); - nextInt = constrain(nextInt, 0, 15); - decValue = (decValue * 16) + nextInt; - } + // + void initParams(); + float hexToDec(String hexString, byte bytes = 2, bool signedNum = true); + float km2distance(float inKm); + float celsius2temperature(float inCelsius); + float bar2pressure(float inBar); +}; - // Unsigned - do nothing - if (!signedNum) { - return decValue; - } - // Signed for 1, 2 bytes - if (bytes == 1) { - return (decValue > 127 ? (float)decValue - 256.0 : decValue); - } - return (decValue > 32767 ? (float)decValue - 65536.0 : decValue); -} + +// +#endif // LIVEDATA_H diff --git a/README.md b/README.md index 0dd09b4..167d7cc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# KIA ENIRO DASHBOARD +# evDash (older nama enirodashboard) Supported devices 1. LILYGO TTGO T4 v1.3 @@ -34,13 +34,14 @@ Others ## Quick installation with ESP32 flash tool -M5STACK (Many thanks to DimZen) +See INSTALLATION.md -https://docs.google.com/document/d/17vJmeveNfN0exQy9wKC-5igU8zzNjsuOn1DPuPV_yJA/edit?usp=sharing +## RELEASE NOTES +see. RELEASENOTES.md file -TTGO-T4 (older) +## Installation from sources -https://docs.google.com/document/d/1nEezrtXY-8X6mQ1hiZVWDjBVse1sXQg1SlnizaRmJwU/edit?usp=sharing +See INSTALLATION.md ## Screens and shortcuts - Middle button - menu @@ -56,110 +57,7 @@ Screen list - no6. consumption table. Can be used to measure available battery capacity! - no7. debug screen (default off in the menu) +![image](https://github.com/nickn17/evDash/blob/master/screenshots/v1.jpg) -![image](https://github.com/nickn17/enirodashboard/blob/master/screenshots/v1.jpg) +[![Watch the video](https://github.com/nickn17/evDash/blob/master/screenshots/v0.9.jpg)](https://www.youtube.com/watch?v=Jg5VP2P58Yg&) -[![Watch the video](https://github.com/nickn17/enirodashboard/blob/master/screenshots/v0.9.jpg)](https://www.youtube.com/watch?v=Jg5VP2P58Yg&) - -### v1.8.3 2020-11-28 -- Automatic shutdown when car goes off -- Fixed M5stack speaker noise -- Fixed menu, added scroll support - -### v1.8.2 2020-11-25 -- Removed screen flickering. (via Sprites, esp32 with SRAM is now required!) -- Code cleaning. Removed force no/yes redraw mode. Not required with sprites -- Arrow for current (based on bat.temperature) pre-drawn charging graph - -### v1.8.1 2020-11-23 -- Pre-drawn charging graphs (based on coldgates) -- Show version in menu - -### v1.8.0 2020-11-20 -- Support for new device m5stack core1 iot development kit -- TTGO T4 is still supported device! - -### v1.7.5 2020-11-17 -- Settings: Debug screen off/on -- Settings: LCD brightness (auto, 20, 50, 100%) -- Speed screen: added motor rpm, brake lights indicator -- Soc% to kWh is now calibrated for NiroEV/KonaEV 2020 -- eNiroDashboard speed improvements - -### v1.7.4 2020-11-12 -- Added default screen option to settings -- Initial config for Renault ZOE 22kWh -- ODB response analyzer. Please help community to decode unknown values like BMS valves, heater ON switch,... - https://docs.google.com/spreadsheets/d/1eT2R8hmsD1hC__9LtnkZ3eDjLcdib9JR-3Myc97jy8M/edit?usp=sharing - -### v1.7.3 2020-11-11 -- Headlights reminder (if drive mode & headlights are off) - -### v1.7.2 2020-11-10 -- improved charging graph - -### v1.7.1 2020-10-20 -- added new screen 1 - auto mode - - automatically shows screen 3 - speed when speed is >5kph - - screen 5 chargin graph when power kw > 1kW -- added bat.fan status and fan feedback in Hz for Ioniq - -### v1.7 2020-09-16 -- added support for 39.2kWh Hyundai Kona and Kia e-Niro -- added initial support for Hyundai Ioniq 28kWh (not working yet) - -### v1.6 2020-06-30 -- fixed ble device pairing -- added command to set protocol ISO 15765-4 CAN (11 bit ID, 500 kbit/s) - some vgate adapters freezes during "init at command" phase - -### v1.5 2020-06-03 -- added support for different units (miles, fahrenheits, psi) - -### v1.4 2020-05-29 -- added menu -- Pairing with VGATE iCar Pro BLE4 adapter via menu! -- Installation with flash tool. You don't have to install Arduino and compile sources :) -- New screen 5. Conpumption... Can be used to measure available battery capacity! -- Load/Save settings -- Flip screen vertical option -- Several different improvements - -### v1.1 2020-04-12 -- added new screens (switch via left button) -- screen 0. (blank screen, lcd off) -- screen 1. (default) summary info -- screen 2. speed kmh + kwh/100km (or kw for discharge) -- screen 3. battery cells + battery module temperatures -- screen 4. charging graph -- added low batery temperature detection for slow charging on 50kW DC (15°C) and UFC >70kW (25°C). - -### v1.0 2020-03-23 -- first release -- basic dashboard - -## About T4 -ESP32-TTGO-T4 -https://github.com/fdufnews/ESP32-TTGO-T4 - -## Installation from sources -- install arduino IDE + ESP32 support -- https://github.com/Bodmer/TFT_eSPI - display library -- Configure TFT eSPI - W:\Documents\Arduino\libraries\TFT_eSP\User_Setup_Select.h -``` -// Comment -//#include // Default setup is root library folder -// And uncomment -#include // Setup file for ESP32 and TTGO T4 version 1.3 -``` - -My configuration -- Board ESP32 Dev module -- Upload speed 921600 -- CPU freq: 240MHz (Wifi/BT) -- Flash freq: 80MHz -- Flash mode: QIO -- Flash size 4MB (32mb) -- Partion scheme: default 4MB with spiffs -- Core debug level: none -- PSRAM: disable diff --git a/RELEASENOTES.md b/RELEASENOTES.md new file mode 100644 index 0000000..03b7c2f --- /dev/null +++ b/RELEASENOTES.md @@ -0,0 +1,85 @@ +# RELEASE NOTES + +### v2.0.0 2020-12-02 +- Project renamed from eNiroDashboard to evDash + +### 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 +- Fixed M5stack speaker noise +- Fixed menu, added scroll support + +### v1.8.2 2020-11-25 +- Removed screen flickering. (via Sprites, esp32 with SRAM is now required!) +- Code cleaning. Removed force no/yes redraw mode. Not required with sprites +- Arrow for current (based on bat.temperature) pre-drawn charging graph + +### v1.8.1 2020-11-23 +- Pre-drawn charging graphs (based on coldgates) +- Show version in menu + +### v1.8.0 2020-11-20 +- Support for new device m5stack core1 iot development kit +- TTGO T4 is still supported device! + +### v1.7.5 2020-11-17 +- Settings: Debug screen off/on +- Settings: LCD brightness (auto, 20, 50, 100%) +- Speed screen: added motor rpm, brake lights indicator +- Soc% to kWh is now calibrated for NiroEV/KonaEV 2020 +- eNiroDashboard speed improvements + +### v1.7.4 2020-11-12 +- Added default screen option to settings +- Initial config for Renault ZOE 22kWh +- ODB response analyzer. Please help community to decode unknown values like BMS valves, heater ON switch,... + https://docs.google.com/spreadsheets/d/1eT2R8hmsD1hC__9LtnkZ3eDjLcdib9JR-3Myc97jy8M/edit?usp=sharing + +### v1.7.3 2020-11-11 +- Headlights reminder (if drive mode & headlights are off) + +### v1.7.2 2020-11-10 +- improved charging graph + +### v1.7.1 2020-10-20 +- added new screen 1 - auto mode + - automatically shows screen 3 - speed when speed is >5kph + - screen 5 chargin graph when power kw > 1kW +- added bat.fan status and fan feedback in Hz for Ioniq + +### v1.7 2020-09-16 +- added support for 39.2kWh Hyundai Kona and Kia e-Niro +- added initial support for Hyundai Ioniq 28kWh (not working yet) + +### v1.6 2020-06-30 +- fixed ble device pairing +- added command to set protocol ISO 15765-4 CAN (11 bit ID, 500 kbit/s) - some vgate adapters freezes during "init at command" phase + +### v1.5 2020-06-03 +- added support for different units (miles, fahrenheits, psi) + +### v1.4 2020-05-29 +- added menu +- Pairing with VGATE iCar Pro BLE4 adapter via menu! +- Installation with flash tool. You don't have to install Arduino and compile sources :) +- New screen 5. Conpumption... Can be used to measure available battery capacity! +- Load/Save settings +- Flip screen vertical option +- Several different improvements + +### v1.1 2020-04-12 +- added new screens (switch via left button) +- screen 0. (blank screen, lcd off) +- screen 1. (default) summary info +- screen 2. speed kmh + kwh/100km (or kw for discharge) +- screen 3. battery cells + battery module temperatures +- screen 4. charging graph +- added low batery temperature detection for slow charging on 50kW DC (15°C) and UFC >70kW (25°C). + +### v1.0 2020-03-23 +- first release +- basic dashboard + diff --git a/build.bat b/build.bat index e650cd4..79b683a 100644 --- a/build.bat +++ b/build.bat @@ -1,13 +1,14 @@ rem slow GUI performance via arduino-cli yet -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 +arduino-cli compile -v -b esp32:esp32:esp32wrover --build-properties build.extra_flags=-BOARD_TTGO_T4=1 -v evDash.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 evDash.ino +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 compile -v -b esp32:esp32:m5stack-core-esp32 --build-properties build.extra_flags=-BOARD_M5STACK=1 evDash.ino rem arduino-cli upload -v -b esp32:esp32:m5stack-core-esp32 -p COM9 -rem arduino-cli compile -v -b esp32:esp32:esp32:PSRAM=enabled,PartitionScheme=huge_app,CPUFreq=80 --build-properties build.extra_flags=-BOARD_M5STACK=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_M5STACK=1 -v evDash.ino rem arduino-cli upload -b esp32:esp32:esp32 -v -p COM9 pause \ No newline at end of file diff --git a/car_debug_obd2_kia.h b/car_debug_obd2_kia.h deleted file mode 100644 index 1888dd5..0000000 --- a/car_debug_obd2_kia.h +++ /dev/null @@ -1,518 +0,0 @@ - -#define commandQueueCountDebugObd2Kia 256 -#define commandQueueLoopFromDebugObd2Kia 8 - -String commandQueueDebugObd2Kia[commandQueueCountDebugObd2Kia] = { - "AT Z", // Reset all - "AT I", // Print the version ID - "AT E0", // Echo off - "AT L0", // Linefeeds off - "AT S0", // Printing of spaces on - "AT SP 6", // Select protocol to ISO 15765-4 CAN (11 bit ID, 500 kbit/s) - "AT DP", - "AT ST16", - - // Loop from here - - // Request ID Response ID ECU name Can bus Protocol Description - // 725 72D WPS B Wireless phone charger - //"ATSH725", - //"2201", // All with negative resp. "2202", "2203", "2101", "2102", "220101", "220102", "22B001", "22C001", "22C101", - - // 736 73E VESS P Virtual Engine Sound system - //"ATSH736", - //"2201", // All with negative resp. "2202", "2203", "2101", "2102", - //"220101", // All with 62 response "220102", "22B001", "22C001", "22C101", - - // 755 75D BSD Right Blind spot detection Right - // "ATSH755", - // "2201", // ALL with negative 7F2213, etc "2202", "2203", "2101", "2102", "220101", "220102", "22B001", "22C001", "22C101", - - // 770 778 IGPM All UDS Integrated Gateway and power control module - "ATSH770", - "22BC01", // 009 62BC01400000000001AAAAAAAA - "22BC02", // 62BC0200000000 - "22BC03", // 00B 62BC03FDEE7C730A600000AAAA - "22BC04", // 00B 62BC04B33F74EA0D002042AAAA - "22BC05", // 00B 62BC05BF13200001000000AAAA - "22BC06", // 00B 62BC06B48000002C000000AAAA - "22BC07", // 00B 62BC070849DBC000101900AAAA - //"22BC08", // ALL with NEGATIVE RESPONSE "22BC09", "22BC0A", "22BC0B", "22BC0C", "22BC0D", "22BC0E", "22BC0F", - - // 783 78B AMP M Amplifier - //"ATSH783", - // "2201",// ALL with NEGATIVE RESPONSE "2202", "2203", "2101", "2102", "220101", "220102", "22B001", "22C001", "22C101", - - // 796 79E PGS C Parking Guide System - //"ATSH796", - //"2201", // ALL with NEGATIVE RESPONSE "2202", "2203", "2101", "2102", "220101", "220102", "22B001", "22C001", "22C101", - - // 7A0 7A8 BCM / TPMS B UDS Body control module 22 B0 01 to 22 B0 0E - // C Tire Pressure Monitoring "At least 22 C0 01 to 22 C0 02 & 22 C0 0B to 22 C0 0F" - "ATSH7A0", - "22B001", // 016 62B00140C20000000000000000000001010000000001AAAAAAAAAA - "22B002", // 009 62B002C00000000300AAAAAAAA - "22B003", // 018 62B003BFCB8000A23D63B164F8F7F73DF80000A400A4A4A4AAAAAA - "22B004", // 00B 62B0047402994E0E008800AAAA - "22B005", // 00B 62B0052000100000000800AAAA - "22B006", // 00B 62B0062000000000000000AAAA - "22B007", // 00B 62B007002001E000040000AAAA - "22B008", // 00B 62B00800510C2000880004AAAA - "22B009", // 00B 62B009FEEEFEEE08000800AAAA - "22B00A", // 00B 62B00AE3FEE3000040C500AAAA - //"22B00B", // 7F2231 - "22B00C", // 00B 62B00C3F00000000000000AAAA - "22B00D", // 00B 62B00DFCFCFC0000000000AAAA - "22B00E", // 00B 62B00E0800000000000000AAAA - //"22B00F", // 7F2231 - "22C001", // 01D 62C001000000002E2E02500706B5B50A098C3C0000000001FF01000101AAAAAAAAAA - "22C002", // 017 62C002FFFF0000D2E149F3D2DBDACBD2E84EBBD2E84E93AAAAAAAA - "22C003", // 021 62C00300000000444F303101002E2E02500706B5B50A098C3C0000000001FF0100AA - "22C004", // 021 62C004000000004E41303101002E2E024B0005B5B508088C3C0100000001FF0100AA - "22C005", // 021 62C005000000004E54504D0100302F02500000ABAB00008C3C0000030001FF0000AA - "22C006", // 021 62C00600000000444F303201002E2E02500706B5AB0A098C3C0000000001010100AA - "22C007", // 021 62C007000000004E41303201002E2E024B0005B5AB08088C3C0100000001010100AA - "22C008", // 021 62C00800000000434E303101002E2E02500706B5B50A098C3C0000020001FF0100AA - "22C009", // 021 62C00900000000303030360000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA - "22C00A", // 021 62C00A00000000303030370000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA - "22C00B", // 017 62C00BFFFF0000AF470100B4480100B5460100B3460100AAAAAAAA - "22C00C", // 025 62C00CFFFF03000000000000FFFF0000000000000000FFFF0000000000000000FFFF000000AAAAAAAA - "22C00D", // 025 62C00DFFFF03000000000000FFFF0000000000000000FFFF0000000000000000FFFF000000AAAAAAAA - "22C00E", // 025 62C00EFFFF03000000000000FFFF0000000000000000FFFF0000000000000000FFFF000000AAAAAAAA - "22C00F", // 025 62C00FFFFF03000000000000FFFF0000000000000000FFFF0000000000000000FFFF000000AAAAAAAA - - // 7A1 7A9 DDM B Driver door module - // "ATSH7A1", - // "2201", // All with NO DATA "2202", "2203", "2101", "2102", "220101", "220102", "22B001", "22C001", "22C101", - - // 7A2 7AA ADM B Assist door module - //"ATSH7A2", - // "2201", // ALL with NO DATA "2202", "2203", "2101", "2102", "220101", "220102", "22B001", "22C001", "22C101", - - // 7A3 7AB PSM B UDS Power Seat Module - // "ATSH7A3", - // "22B401", // All with NO DATA "22B402", "22B403", "22B404", "22B405", "22B406", "22B407", "22B408", "22B409", "22B40A", - - // 7A5 7AD SMK B UDS Smart Key - "ATSH7A5", - "22B001", // 7F2278 7F2231 - "22B002", // positive - "22B003", // positive - "22B004", // 7F2278 7F2231 - "22B005", // positive - "22B006", // positive - "22B007", // positive - "22B008", // positive - "22B009", // positive - "22B00A", // positive - - // 7B3 7BB AIRCON / ACU UDS Aircondition - "ATSH7B3", - "220100", // 026 6201007E5027C8FF7C6D6B05EFBCFFFFEFFF10FFFFFFFFFFFFBFFFFF52B3919900FFFF01FFFF000000 in/out temp - // "220101", // 7F2231 - "220102", // 014 620102FFF80000B36B0101000101003C00016E12 coolant temp1, 2 - // "220103", // 7F2231 - - // 7B7 7BF BSD Left Blind spot detection Left - "ATSH7B7", - // "2201", // ALL NEGATIVE RESP "2202", "2203", "2101", "2102", "220101", "220102", "22B001", "22C001", "22C101", - - // 7C4 7CC MFC Multi Function Camera - "ATSH7C4", - "220101", // 6201010E - "220102", // 62010200000000 - - // 7C6 7CE CM C & M UDS Cluster Module - "ATSH7C6", - "22B001", // 008 62B00100000000000000000000 - "22B002", // 00F 62B002E0000000FFA200AD8F0000000000000000 odo - "22B003", // 008 62B00398000000010000000000 - //"22B004", // NO DATA - - // 7D0 7D8 AEB UDS? Autonomous Emergency Breaking - // "ATSH7D0", - // "2201", // ALL CODES WITH NEGATIVE RESPONSE - // "2202", // "2203", // "2101", // "2102", // "220101", // "220102", // "22B001", // "22C001", // "22C101", - - // 7D1 7D9 ABS / ESP + AHB UDS - "ATSH7D1", - "22C101", // 02A 62C1015FD7E7D0FFFF00FF04D0D400000000FF7EFF0030F5010000FFFF7F6307F207FE05FF00FF3FFFFFAAAAAAAAAAAA - "22C102", // 01A 62C10237000000FFFFFFFFFFFF00FF05FFFFFF00FF5501FFFFFFAA - "22C103", // 01A 62C103BE3000000DFFF0FCFE7FFF7FFFFFFFFFFF000005B50000AA - - // 7D2 7DA AIRBAG SRS Sytem - // "ATSH7D2", - // "2101", // 7F2211 - // "2102", // 7F2211 - // "220101", // 7F2211 - // "220102", // 7F2211 - // "22B001", // 7F2211 - // "22C001", // 7F2211 - // "22C101", // 7F2211 - - // 7D4 7DC EPS Electric power steering - "ATSH7D4", - //"2101", // 7F2121 - //"2102", // 7F2121 - "220101", // 012 6201018387FD009DFFF90100000010101921AAAA - "220102", // 008 6201020000000500AAAAAAAAAA - // "22B001", // 7F2231 - // "22C001", // 7F2231 - // "22C101", // 7F2231 - - // 7DF UNKNOWN - //"ATSH7DF", - //"2106", // 013 7F2112 028 6106FFFF800000000000000300001C001C001C000600060006000F000000010000000000000000015801580158015700 - //"220106", // 01B 620106FFFFFFFF12001200307C7C00317C830000B4B3000A28EA00 - - // 7E2 7EA VMCU H & P KWP2000 Vehicle Motor Control Unit 21 01 to 21 02 & 1A 80++ - "ATSH7E2", - "2101", // 018 6101FFF8000009285A3806480300000000C4693404080805000000 speed, ... - "2102", // 027 6102F8FFFC000101000000851BB5780234FA0BAB8D1933E07F5B211C74000001010100000007000000 aux, ... - //"2103", // 7F2112 - //"1A80", // Working VIN 1A 8A 8C 8D ... - - // 7E3 7EB MCU H & P KWP2000 Motor Control Unit 21 01 to 21 06 - "ATSH7E3", - "2101", // 01E 610100007FFF0C3C00BD8D0A3300B00900002D0252025033D500C3FF68FF00000000 - "2102", // 03A 610207FFFFFF00000D000D00260008080909000001004614CDABC2CD3F005581720085CAF5265D0DC1CD0000EC3400000000000000000000FF0000000000 - "2103", // 06E 610300007FFF0000000000000000000000000000000000005C010E02FDFD040400000000000000000000000048058D0C0200160000000000AA3F000005000000AE0102000000000000000000000000000000000000000000BB0B00000000000000000000680000000000E803000000 - "2104", // 060 6104000001FF000000000000D7425D03000000000000050000007A2B00000000000000003200000000000000000000000000000000000000000000000000000000000000010000000100010001000000030000000000000000006D0000008E1B00 - "2105", // 067 6105000001FF630200010000005900000C00630200010000000100000C006B0200020000003300250D0136010096BA03000100000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - "2106", // 013 6106000000030000000000000000000000000300 - //"2107", // 7F2112 - - // 7E4 7EC BMS P UDS Battery Management System 22 01 01 to 22 01 06 - "ATSH7E4", - "220101", // 03E 620101FFF7E7FF6B0000000003001C0E2F161414141513000012B930B9380000830003E1E30003C95B0001722C00015B5B0040D1660D016B0000000003E8 power kw, ... - "220102", // 027 620102FFFFFFFFB9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9AAAA cell voltages, screen 3 only - "220103", // 027 620103FFFFFFFFB9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9AAAA cell voltages, screen 3 only - "220104", // 027 620104FFFFFFFFB9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9B9AAAA - "220105", // 02E 620105003FFF90000000000000000012836A0142684268000150126E03E8000000006E0000B9B900000F00000000AAAA soh, soc, .. - "220106", // 01B 620106FFFFFFFF12001200307C7C00007C030000B4B3000A28EA00 cooling water temp - // "220107", // 7F2231 - - // 7E5 7ED OBC KWP2000 OnBoard Charger 21 01 to 21 03 - "ATSH7E5", - "2101", // 01A 6101DFE0000001010001000000000E2F0533051D0078000000C800 - "2102", // 011 6102FE000000000403E8000001BF000028000000 - "2103", // 039 6103FFFFFDF80000000000000000000000000000000000000000000000000000000000000000007576767600000000000000000004000400030000000000 - "2104", // 022 6104FFF000000A280A280A280000E324006900000003000000000000000000000000 - "2105", // 046 61050000000081AA791E8013791E779C791E8BD37907874A79108D67791473777915727E7914753179156FAE7917768F79147650792876257930757E7914759379167545791D000000000000 - "2106", // 028 6106FFFF8000001C001C001C000600060006000E000000010000000000000000015801580158015800 - //"2107", // ret 7F2112 - - // 7E6 7EE ?? ?? 21 08 05 to 21 08 0F -> All negative response - //"ATSH7E6", - //"210805", // ret 7F2112 - //"210806", // ret 7F2112 - //"210807", // ret 7F2112 -}; - -/** - Init command queue -*/ -bool activateCommandQueueForDebugObd2Kia() { - - // 39 or 64 kWh model? - params.batteryTotalAvailableKWh = 64; - - // Empty and fill command queue - for (uint16_t i = 0; i < 300; i++) { - commandQueue[i] = ""; - } - for (uint16_t i = 0; i < commandQueueCountDebugObd2Kia; i++) { - commandQueue[i] = commandQueueDebugObd2Kia[i]; - } - - commandQueueLoopFrom = commandQueueLoopFromDebugObd2Kia; - commandQueueCount = commandQueueCountDebugObd2Kia; - - return true; -} - -/** - Parse merged row -*/ -bool parseRowMergedDebugObd2Kia() { - - // VMCU 7E2 - if (currentAtshRequest.equals("ATSH7E2")) { - if (commandRequest.equals("2101")) { - params.speedKmh = hexToDec(responseRowMerged.substring(32, 36).c_str(), 2, false) * 0.0155; // / 100.0 *1.609 = real to gps is 1.750 - if (params.speedKmh < -99 || params.speedKmh > 200) - params.speedKmh = 0; - } - if (commandRequest.equals("2102")) { - params.auxPerc = hexToDec(responseRowMerged.substring(50, 52).c_str(), 1, false); - params.auxCurrentAmp = - hexToDec(responseRowMerged.substring(46, 50).c_str(), 2, true) / 1000.0; - } - } - - // Cluster module 7c6 - if (currentAtshRequest.equals("ATSH7C6")) { - if (commandRequest.equals("22B002")) { - params.odoKm = float(strtol(responseRowMerged.substring(18, 24).c_str(), 0, 16)); - } - } - - // Aircon 7b3 - if (currentAtshRequest.equals("ATSH7B3")) { - if (commandRequest.equals("220100")) { - params.indoorTemperature = (hexToDec(responseRowMerged.substring(16, 18).c_str(), 1, false) / 2) - 40; - params.outdoorTemperature = (hexToDec(responseRowMerged.substring(18, 20).c_str(), 1, false) / 2) - 40; - } - if (commandRequest.equals("220102") && responseRowMerged.substring(12, 14) == "00") { - params.coolantTemp1C = (hexToDec(responseRowMerged.substring(14, 16).c_str(), 1, false) / 2) - 40; - params.coolantTemp2C = (hexToDec(responseRowMerged.substring(16, 18).c_str(), 1, false) / 2) - 40; - } - } - - // BMS 7e4 - if (currentAtshRequest.equals("ATSH7E4")) { - if (commandRequest.equals("220101")) { - params.cumulativeEnergyChargedKWh = float(strtol(responseRowMerged.substring(82, 90).c_str(), 0, 16)) / 10.0; - if (params.cumulativeEnergyChargedKWhStart == -1) - params.cumulativeEnergyChargedKWhStart = params.cumulativeEnergyChargedKWh; - params.cumulativeEnergyDischargedKWh = float(strtol(responseRowMerged.substring(90, 98).c_str(), 0, 16)) / 10.0; - if (params.cumulativeEnergyDischargedKWhStart == -1) - params.cumulativeEnergyDischargedKWhStart = params.cumulativeEnergyDischargedKWh; - params.auxVoltage = hexToDec(responseRowMerged.substring(64, 66).c_str(), 2, true) / 10.0; - params.batPowerAmp = - hexToDec(responseRowMerged.substring(26, 30).c_str(), 2, true) / 10.0; - params.batVoltage = hexToDec(responseRowMerged.substring(30, 34).c_str(), 2, false) / 10.0; - params.batPowerKw = (params.batPowerAmp * params.batVoltage) / 1000.0; - params.batPowerKwh100 = params.batPowerKw / params.speedKmh * 100; - params.batCellMaxV = hexToDec(responseRowMerged.substring(52, 54).c_str(), 1, false) / 50.0; - params.batCellMinV = hexToDec(responseRowMerged.substring(56, 58).c_str(), 1, false) / 50.0; - params.batModuleTempC[0] = hexToDec(responseRowMerged.substring(38, 40).c_str(), 1, true); - params.batModuleTempC[1] = hexToDec(responseRowMerged.substring(40, 42).c_str(), 1, true); - params.batModuleTempC[2] = hexToDec(responseRowMerged.substring(42, 44).c_str(), 1, true); - params.batModuleTempC[3] = hexToDec(responseRowMerged.substring(44, 46).c_str(), 1, true); - //params.batTempC = hexToDec(responseRowMerged.substring(36, 38).c_str(), 1, true); - //params.batMaxC = hexToDec(responseRowMerged.substring(34, 36).c_str(), 1, true); - //params.batMinC = hexToDec(responseRowMerged.substring(36, 38).c_str(), 1, true); - - // This is more accurate than min/max from BMS. It's required to detect kona/eniro cold gates (min 15C is needed > 43kW charging, min 25C is needed > 58kW charging) - params.batMinC = params.batMaxC = params.batModuleTempC[0]; - params.batMinC = (params.batModuleTempC[1] < params.batMinC) ? params.batModuleTempC[1] : params.batMinC; - params.batMinC = (params.batModuleTempC[2] < params.batMinC) ? params.batModuleTempC[2] : params.batMinC; - params.batMinC = (params.batModuleTempC[3] < params.batMinC) ? params.batModuleTempC[3] : params.batMinC; - params.batMaxC = (params.batModuleTempC[1] > params.batMaxC) ? params.batModuleTempC[1] : params.batMaxC; - params.batMaxC = (params.batModuleTempC[2] > params.batMaxC) ? params.batModuleTempC[2] : params.batMaxC; - params.batMaxC = (params.batModuleTempC[3] > params.batMaxC) ? params.batModuleTempC[3] : params.batMaxC; - params.batTempC = params.batMinC; - - params.batInletC = hexToDec(responseRowMerged.substring(50, 52).c_str(), 1, true); - if (params.speedKmh < 10 && params.batPowerKw >= 1 && params.socPerc > 0 && params.socPerc <= 100) { - if ( params.chargingGraphMinKw[int(params.socPerc)] == -100 || params.batPowerKw < params.chargingGraphMinKw[int(params.socPerc)]) - params.chargingGraphMinKw[int(params.socPerc)] = params.batPowerKw; - if ( params.chargingGraphMaxKw[int(params.socPerc)] == -100 || params.batPowerKw > params.chargingGraphMaxKw[int(params.socPerc)]) - params.chargingGraphMaxKw[int(params.socPerc)] = params.batPowerKw; - params.chargingGraphBatMinTempC[int(params.socPerc)] = params.batMinC; - params.chargingGraphBatMaxTempC[int(params.socPerc)] = params.batMaxC; - params.chargingGraphHeaterTempC[int(params.socPerc)] = params.batHeaterC; - } - } - // BMS 7e4 - if (commandRequest.equals("220102") && responseRowMerged.substring(12, 14) == "FF") { - for (int i = 0; i < 32; i++) { - params.cellVoltage[i] = hexToDec(responseRowMerged.substring(14 + (i * 2), 14 + (i * 2) + 2).c_str(), 1, false) / 50; - } - } - // BMS 7e4 - if (commandRequest.equals("220103")) { - for (int i = 0; i < 32; i++) { - params.cellVoltage[32 + i] = hexToDec(responseRowMerged.substring(14 + (i * 2), 14 + (i * 2) + 2).c_str(), 1, false) / 50; - } - } - // BMS 7e4 - if (commandRequest.equals("220104")) { - for (int i = 0; i < 32; i++) { - params.cellVoltage[64 + i] = hexToDec(responseRowMerged.substring(14 + (i * 2), 14 + (i * 2) + 2).c_str(), 1, false) / 50; - } - } - // BMS 7e4 - if (commandRequest.equals("220105")) { - params.socPercPrevious = params.socPerc; - params.sohPerc = hexToDec(responseRowMerged.substring(56, 60).c_str(), 2, false) / 10.0; - params.socPerc = hexToDec(responseRowMerged.substring(68, 70).c_str(), 1, false) / 2.0; - - // Soc10ced table, record x0% CEC/CED table (ex. 90%->89%, 80%->79%) - if (params.socPercPrevious - params.socPerc > 0) { - byte index = (int(params.socPerc) == 4) ? 0 : (int)(params.socPerc / 10) + 1; - if ((int(params.socPerc) % 10 == 9 || int(params.socPerc) == 4) && params.soc10ced[index] == -1) { - struct tm now; - getLocalTime(&now, 0); - time_t time_now_epoch = mktime(&now); - params.soc10ced[index] = params.cumulativeEnergyDischargedKWh; - params.soc10cec[index] = params.cumulativeEnergyChargedKWh; - params.soc10odo[index] = params.odoKm; - params.soc10time[index] = time_now_epoch; - } - } - params.batHeaterC = hexToDec(responseRowMerged.substring(52, 54).c_str(), 1, true); - // - for (int i = 30; i < 32; i++) { // ai/aj position - params.cellVoltage[96 - 30 + i] = hexToDec(responseRowMerged.substring(14 + (i * 2), 14 + (i * 2) + 2).c_str(), 1, false) / 50; - } - } - // BMS 7e4 - if (commandRequest.equals("220106")) { - params.coolingWaterTempC = hexToDec(responseRowMerged.substring(14, 16).c_str(), 1, false); - } - } - - // TPMS 7a0 - if (currentAtshRequest.equals("ATSH7A0")) { - if (commandRequest.equals("22c00b")) { - params.tireFrontLeftPressureBar = hexToDec(responseRowMerged.substring(14, 16).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722 - params.tireFrontRightPressureBar = hexToDec(responseRowMerged.substring(22, 24).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722 - params.tireRearRightPressureBar = hexToDec(responseRowMerged.substring(30, 32).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722 - params.tireRearLeftPressureBar = hexToDec(responseRowMerged.substring(38, 40).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722 - params.tireFrontLeftTempC = hexToDec(responseRowMerged.substring(16, 18).c_str(), 2, false) - 50; // === OK Valid - params.tireFrontRightTempC = hexToDec(responseRowMerged.substring(24, 26).c_str(), 2, false) - 50; // === OK Valid - params.tireRearRightTempC = hexToDec(responseRowMerged.substring(32, 34).c_str(), 2, false) - 50; // === OK Valid - params.tireRearLeftTempC = hexToDec(responseRowMerged.substring(40, 42).c_str(), 2, false) - 50; // === OK Valid - } - } - - return true; -} - -/** - Test data -*/ -bool testDataDebugObd2Kia() { - - // VMCU ATSH7E2 - currentAtshRequest = "ATSH7E2"; - // 2101 - commandRequest = "2101"; - responseRowMerged = "6101FFF8000009285A3B0648030000B4179D763404080805000000"; - parseRowMergedDebugObd2Kia(); - // 2102 - commandRequest = "2102"; - responseRowMerged = "6102F8FFFC000101000000840FBF83BD33270680953033757F59291C76000001010100000007000000"; - responseRowMerged = "6102F8FFFC000101000000931CC77F4C39040BE09BA7385D8158832175000001010100000007000000"; - parseRowMergedDebugObd2Kia(); - - // "ATSH7DF", - currentAtshRequest = "ATSH7DF"; - // 2106 - commandRequest = "2106"; - responseRowMerged = "6106FFFF800000000000000200001B001C001C000600060006000E000000010000000000000000013D013D013E013E00"; - parseRowMergedDebugObd2Kia(); - - // AIRCON / ACU ATSH7B3 - currentAtshRequest = "ATSH7B3"; - // 220100 - commandRequest = "220100"; - responseRowMerged = "6201007E5027C8FF7F765D05B95AFFFF5AFF11FFFFFFFFFFFF6AFFFF2DF0757630FFFF00FFFF000000"; - responseRowMerged = "6201007E5027C8FF867C58121010FFFF10FF8EFFFFFFFFFFFF10FFFF0DF0617900FFFF01FFFF000000"; - parseRowMergedDebugObd2Kia(); - - // BMS ATSH7E4 - currentAtshRequest = "ATSH7E4"; - // 220101 - commandRequest = "220101"; - responseRowMerged = "620101FFF7E7FF99000000000300B10EFE120F11100F12000018C438C30B00008400003864000035850000153A00001374000647010D017F0BDA0BDA03E8"; - responseRowMerged = "620101FFF7E7FFB3000000000300120F9B111011101011000014CC38CB3B00009100003A510000367C000015FB000013D3000690250D018E0000000003E8"; - parseRowMergedDebugObd2Kia(); - // 220102 - commandRequest = "220102"; - responseRowMerged = "620102FFFFFFFFCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBAAAA"; - parseRowMergedDebugObd2Kia(); - // 220103 - commandRequest = "220103"; - responseRowMerged = "620103FFFFFFFFCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCACBCACACFCCCBCBCBCBCBCBCBCBAAAA"; - parseRowMergedDebugObd2Kia(); - // 220104 - commandRequest = "220104"; - responseRowMerged = "620104FFFFFFFFCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBAAAA"; - parseRowMergedDebugObd2Kia(); - // 220105 - commandRequest = "220105"; - responseRowMerged = "620105003fff9000000000000000000F8A86012B4946500101500DAC03E800000000AC0000C7C701000F00000000AAAA"; - responseRowMerged = "620105003FFF90000000000000000014918E012927465000015013BB03E800000000BB0000CBCB01001300000000AAAA"; - parseRowMergedDebugObd2Kia(); - // 220106 - commandRequest = "220106"; - responseRowMerged = "620106FFFFFFFF14001A00240000003A7C86B4B30000000928EA00"; - parseRowMergedDebugObd2Kia(); - - // BCM / TPMS ATSH7A0 - currentAtshRequest = "ATSH7A0"; - // 22c00b - commandRequest = "22c00b"; - responseRowMerged = "62C00BFFFF0000B93D0100B43E0100B43D0100BB3C0100AAAAAAAA"; - parseRowMergedDebugObd2Kia(); - - // ATSH7C6 - currentAtshRequest = "ATSH7C6"; - // 22b002 - commandRequest = "22b002"; - responseRowMerged = "62B002E0000000FFB400330B0000000000000000"; - parseRowMergedDebugObd2Kia(); - - params.batModuleTempC[0] = 28; - params.batModuleTempC[1] = 29; - params.batModuleTempC[2] = 28; - params.batModuleTempC[3] = 30; - // This is more accurate than min/max from BMS. It's required to detect kona/eniro cold gates (min 15C is needed > 43kW charging, min 25C is needed > 58kW charging) - params.batMinC = params.batMaxC = params.batModuleTempC[0]; - for (uint16_t i = 1; i < params.batModuleTempCount; i++) { - if (params.batModuleTempC[i] < params.batMinC) - params.batMinC = params.batModuleTempC[i]; - if (params.batModuleTempC[i] > params.batMaxC) - params.batMaxC = params.batModuleTempC[i]; - } - params.batTempC = params.batMinC; - - // - params.soc10ced[10] = 2200; - params.soc10cec[10] = 2500; - params.soc10odo[10] = 13000; - params.soc10time[10] = 13000; - params.soc10ced[9] = params.soc10ced[10] + 6.4; - params.soc10cec[9] = params.soc10cec[10] + 0; - params.soc10odo[9] = params.soc10odo[10] + 30; - params.soc10time[9] = params.soc10time[10] + 900; - params.soc10ced[8] = params.soc10ced[9] + 6.8; - params.soc10cec[8] = params.soc10cec[9] + 0; - params.soc10odo[8] = params.soc10odo[9] + 30; - params.soc10time[8] = params.soc10time[9] + 900; - params.soc10ced[7] = params.soc10ced[8] + 7.2; - params.soc10cec[7] = params.soc10cec[8] + 0.6; - params.soc10odo[7] = params.soc10odo[8] + 30; - params.soc10time[7] = params.soc10time[8] + 900; - params.soc10ced[6] = params.soc10ced[7] + 6.7; - params.soc10cec[6] = params.soc10cec[7] + 0; - params.soc10odo[6] = params.soc10odo[7] + 30; - params.soc10time[6] = params.soc10time[7] + 900; - params.soc10ced[5] = params.soc10ced[6] + 6.7; - params.soc10cec[5] = params.soc10cec[6] + 0; - params.soc10odo[5] = params.soc10odo[6] + 30; - params.soc10time[5] = params.soc10time[6] + 900; - params.soc10ced[4] = params.soc10ced[5] + 6.4; - params.soc10cec[4] = params.soc10cec[5] + 0.3; - params.soc10odo[4] = params.soc10odo[5] + 30; - params.soc10time[4] = params.soc10time[5] + 900; - params.soc10ced[3] = params.soc10ced[4] + 6.4; - params.soc10cec[3] = params.soc10cec[4] + 0; - params.soc10odo[3] = params.soc10odo[4] + 30; - params.soc10time[3] = params.soc10time[4] + 900; - params.soc10ced[2] = params.soc10ced[3] + 5.4; - params.soc10cec[2] = params.soc10cec[3] + 0.1; - params.soc10odo[2] = params.soc10odo[3] + 30; - params.soc10time[2] = params.soc10time[3] + 900; - params.soc10ced[1] = params.soc10ced[2] + 6.2; - params.soc10cec[1] = params.soc10cec[2] + 0.1; - params.soc10odo[1] = params.soc10odo[2] + 30; - params.soc10time[1] = params.soc10time[2] + 900; - params.soc10ced[0] = params.soc10ced[1] + 2.9; - params.soc10cec[0] = params.soc10cec[1] + 0.5; - params.soc10odo[0] = params.soc10odo[1] + 15; - params.soc10time[0] = params.soc10time[1] + 900; - - return true; -} diff --git a/car_hyundai_ioniq.h b/car_hyundai_ioniq.h deleted file mode 100644 index 5462528..0000000 --- a/car_hyundai_ioniq.h +++ /dev/null @@ -1,376 +0,0 @@ -#define commandQueueCountHyundaiIoniq 25 -#define commandQueueLoopFromHyundaiIoniq 8 -String commandQueueHyundaiIoniq[commandQueueCountHyundaiIoniq] = { - "AT Z", // Reset all - "AT I", // Print the version ID - "AT E0", // Echo off - "AT L0", // Linefeeds off - "AT S0", // Printing of spaces on - "AT SP 6", // Select protocol to ISO 15765-4 CAN (11 bit ID, 500 kbit/s) - //"AT AL", // Allow Long (>7 byte) messages - //"AT AR", // Automatically receive - //"AT H1", // Headers on (debug only) - //"AT D1", // Display of the DLC on - //"AT CAF0", // Automatic formatting off - "AT DP", - "AT ST16", - - // Loop from (HYUNDAI IONIQ) - // BMS - "ATSH7E4", - "2101", // power kw, ... - "2102", // cell voltages, screen 3 only - "2103", // cell voltages, screen 3 only - "2104", // cell voltages, screen 3 only - "2105", // soh, soc, .. - "2106", // cooling water temp - - // VMCU - "ATSH7E2", - "2101", // speed, ... - "2102", // aux, ... - - //"ATSH7Df", - //"2106", - //"220106", - - // Aircondition - // IONIQ OK - "ATSH7B3", - "220100", // in/out temp - "220102", // coolant temp1, 2 - - // BCM / TPMS - // IONIQ OK - "ATSH7A0", - "22c00b", // tire pressure/temp - - // CLUSTER MODULE - // IONIQ OK - "ATSH7C6", - "22B002", // odo -}; - -/** - Init command queue -*/ -bool activateCommandQueueForHyundaiIoniq() { - - // 28kWh version - params.batteryTotalAvailableKWh = 28; - params.batModuleTempCount = 12; - - // Empty and fill command queue - for (int i = 0; i < 300; i++) { - commandQueue[i] = ""; - } - for (int i = 0; i < commandQueueCountHyundaiIoniq; i++) { - commandQueue[i] = commandQueueHyundaiIoniq[i]; - } - - commandQueueLoopFrom = commandQueueLoopFromHyundaiIoniq; - commandQueueCount = commandQueueCountHyundaiIoniq; - - return true; -} - -/** - Parse merged row -*/ -bool parseRowMergedHyundaiIoniq() { - - // VMCU 7E2 - if (currentAtshRequest.equals("ATSH7E2")) { - if (commandRequest.equals("2101")) { - params.speedKmh = hexToDec(responseRowMerged.substring(32, 36).c_str(), 2, false) * 0.0155; // / 100.0 *1.609 = real to gps is 1.750 - if (params.speedKmh < -99 || params.speedKmh > 200) - params.speedKmh = 0; - } - if (commandRequest.equals("2102")) { - params.auxPerc = hexToDec(responseRowMerged.substring(50, 52).c_str(), 1, false); - params.auxCurrentAmp = - hexToDec(responseRowMerged.substring(46, 50).c_str(), 2, true) / 1000.0; - } - } - - // Cluster module 7c6 - if (currentAtshRequest.equals("ATSH7C6")) { - if (commandRequest.equals("22B002")) { - params.odoKm = float(strtol(responseRowMerged.substring(18, 24).c_str(), 0, 16)); - } - } - - // Aircon 7b3 - if (currentAtshRequest.equals("ATSH7B3")) { - if (commandRequest.equals("220100")) { - params.indoorTemperature = (hexToDec(responseRowMerged.substring(16, 18).c_str(), 1, false) / 2) - 40; - params.outdoorTemperature = (hexToDec(responseRowMerged.substring(18, 20).c_str(), 1, false) / 2) - 40; - } - if (commandRequest.equals("220102") && responseRowMerged.substring(12, 14) == "00") { - params.coolantTemp1C = (hexToDec(responseRowMerged.substring(14, 16).c_str(), 1, false) / 2) - 40; - params.coolantTemp2C = (hexToDec(responseRowMerged.substring(16, 18).c_str(), 1, false) / 2) - 40; - } - } - - // BMS 7e4 - if (currentAtshRequest.equals("ATSH7E4")) { - if (commandRequest.equals("2101")) { - params.cumulativeEnergyChargedKWh = float(strtol(responseRowMerged.substring(80, 88).c_str(), 0, 16)) / 10.0; - if (params.cumulativeEnergyChargedKWhStart == -1) - params.cumulativeEnergyChargedKWhStart = params.cumulativeEnergyChargedKWh; - params.cumulativeEnergyDischargedKWh = float(strtol(responseRowMerged.substring(88, 96).c_str(), 0, 16)) / 10.0; - if (params.cumulativeEnergyDischargedKWhStart == -1) - params.cumulativeEnergyDischargedKWhStart = params.cumulativeEnergyDischargedKWh; - params.availableChargePower = float(strtol(responseRowMerged.substring(16, 20).c_str(), 0, 16)) / 100.0; - params.availableDischargePower = float(strtol(responseRowMerged.substring(20, 24).c_str(), 0, 16)) / 100.0; - params.isolationResistanceKOhm = hexToDec(responseRowMerged.substring(118, 122).c_str(), 2, true); - params.batFanStatus = hexToDec(responseRowMerged.substring(58, 60).c_str(), 2, true); - params.batFanFeedbackHz = hexToDec(responseRowMerged.substring(60, 62).c_str(), 2, true); - params.auxVoltage = hexToDec(responseRowMerged.substring(62, 64).c_str(), 2, true) / 10.0; - params.batPowerAmp = - hexToDec(responseRowMerged.substring(24, 28).c_str(), 2, true) / 10.0; - params.batVoltage = hexToDec(responseRowMerged.substring(28, 32).c_str(), 2, false) / 10.0; - params.batPowerKw = (params.batPowerAmp * params.batVoltage) / 1000.0; - if (params.batPowerKw < 1) // Reset charging start time - params.chargingStartTime = params.currentTime; - params.batPowerKwh100 = params.batPowerKw / params.speedKmh * 100; - params.batCellMaxV = hexToDec(responseRowMerged.substring(50, 52).c_str(), 1, false) / 50.0; - params.batCellMinV = hexToDec(responseRowMerged.substring(54, 56).c_str(), 1, false) / 50.0; - params.batModuleTempC[0] = hexToDec(responseRowMerged.substring(36, 38).c_str(), 1, true); - params.batModuleTempC[1] = hexToDec(responseRowMerged.substring(38, 40).c_str(), 1, true); - params.batModuleTempC[2] = hexToDec(responseRowMerged.substring(40, 42).c_str(), 1, true); - params.batModuleTempC[3] = hexToDec(responseRowMerged.substring(42, 44).c_str(), 1, true); - params.batModuleTempC[4] = hexToDec(responseRowMerged.substring(44, 46).c_str(), 1, true); - //params.batTempC = hexToDec(responseRowMerged.substring(34, 36).c_str(), 1, true); - //params.batMaxC = hexToDec(responseRowMerged.substring(32, 34).c_str(), 1, true); - //params.batMinC = hexToDec(responseRowMerged.substring(34, 36).c_str(), 1, true); - - // This is more accurate than min/max from BMS. It's required to detect kona/eniro cold gates (min 15C is needed > 43kW charging, min 25C is needed > 58kW charging) - params.batInletC = hexToDec(responseRowMerged.substring(48, 50).c_str(), 1, true); - if (params.speedKmh < 10 && params.batPowerKw >= 1 && params.socPerc > 0 && params.socPerc <= 100) { - if ( params.chargingGraphMinKw[int(params.socPerc)] == -100 || params.batPowerKw < params.chargingGraphMinKw[int(params.socPerc)]) - params.chargingGraphMinKw[int(params.socPerc)] = params.batPowerKw; - if ( params.chargingGraphMaxKw[int(params.socPerc)] == -100 || params.batPowerKw > params.chargingGraphMaxKw[int(params.socPerc)]) - params.chargingGraphMaxKw[int(params.socPerc)] = params.batPowerKw; - params.chargingGraphBatMinTempC[int(params.socPerc)] = params.batMinC; - params.chargingGraphBatMaxTempC[int(params.socPerc)] = params.batMaxC; - params.chargingGraphHeaterTempC[int(params.socPerc)] = params.batHeaterC; - } - } - // BMS 7e4 - if (commandRequest.equals("2102") && responseRowMerged.substring(10, 12) == "FF") { - for (int i = 0; i < 32; i++) { - params.cellVoltage[i] = hexToDec(responseRowMerged.substring(12 + (i * 2), 12 + (i * 2) + 2).c_str(), 1, false) / 50; - } - } - // BMS 7e4 - if (commandRequest.equals("2103")) { - for (int i = 0; i < 32; i++) { - params.cellVoltage[32 + i] = hexToDec(responseRowMerged.substring(12 + (i * 2), 12 + (i * 2) + 2).c_str(), 1, false) / 50; - } - } - // BMS 7e4 - if (commandRequest.equals("2104")) { - for (int i = 0; i < 32; i++) { - params.cellVoltage[64 + i] = hexToDec(responseRowMerged.substring(12 + (i * 2), 12 + (i * 2) + 2).c_str(), 1, false) / 50; - } - } - // BMS 7e4 - if (commandRequest.equals("2105")) { - params.socPercPrevious = params.socPerc; - params.sohPerc = hexToDec(responseRowMerged.substring(54, 58).c_str(), 2, false) / 10.0; - params.socPerc = hexToDec(responseRowMerged.substring(66, 68).c_str(), 1, false) / 2.0; - - // Remaining battery modules (tempC) - params.batModuleTempC[5] = hexToDec(responseRowMerged.substring(22, 24).c_str(), 1, true); - params.batModuleTempC[6] = hexToDec(responseRowMerged.substring(24, 26).c_str(), 1, true); - params.batModuleTempC[7] = hexToDec(responseRowMerged.substring(26, 28).c_str(), 1, true); - params.batModuleTempC[8] = hexToDec(responseRowMerged.substring(28, 30).c_str(), 1, true); - params.batModuleTempC[9] = hexToDec(responseRowMerged.substring(30, 32).c_str(), 1, true); - params.batModuleTempC[10] = hexToDec(responseRowMerged.substring(32, 34).c_str(), 1, true); - params.batModuleTempC[11] = hexToDec(responseRowMerged.substring(34, 36).c_str(), 1, true); - - params.batMinC = params.batMaxC = params.batModuleTempC[0]; - for (uint16_t i = 1; i < params.batModuleTempCount; i++) { - if (params.batModuleTempC[i] < params.batMinC) - params.batMinC = params.batModuleTempC[i]; - if (params.batModuleTempC[i] > params.batMaxC) - params.batMaxC = params.batModuleTempC[i]; - } - params.batTempC = params.batMinC; - - // Soc10ced table, record x0% CEC/CED table (ex. 90%->89%, 80%->79%) - if (params.socPercPrevious - params.socPerc > 0) { - byte index = (int(params.socPerc) == 4) ? 0 : (int)(params.socPerc / 10) + 1; - if ((int(params.socPerc) % 10 == 9 || int(params.socPerc) == 4) && params.soc10ced[index] == -1) { - params.soc10ced[index] = params.cumulativeEnergyDischargedKWh; - params.soc10cec[index] = params.cumulativeEnergyChargedKWh; - params.soc10odo[index] = params.odoKm; - params.soc10time[index] = params.currentTime; - } - } - params.batHeaterC = hexToDec(responseRowMerged.substring(50, 52).c_str(), 1, true); - // - for (int i = 30; i < 32; i++) { // ai/aj position - params.cellVoltage[96 - 30 + i] = -1; - } - } - // BMS 7e4 - // IONIQ FAILED - if (commandRequest.equals("2106")) { - params.coolingWaterTempC = hexToDec(responseRowMerged.substring(14, 16).c_str(), 1, false); - } - } - - // TPMS 7a0 - if (currentAtshRequest.equals("ATSH7A0")) { - if (commandRequest.equals("22c00b")) { - params.tireFrontLeftPressureBar = hexToDec(responseRowMerged.substring(14, 16).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722 - params.tireFrontRightPressureBar = hexToDec(responseRowMerged.substring(22, 24).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722 - params.tireRearRightPressureBar = hexToDec(responseRowMerged.substring(30, 32).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722 - params.tireRearLeftPressureBar = hexToDec(responseRowMerged.substring(38, 40).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722 - params.tireFrontLeftTempC = hexToDec(responseRowMerged.substring(16, 18).c_str(), 2, false) - 50; // === OK Valid - params.tireFrontRightTempC = hexToDec(responseRowMerged.substring(24, 26).c_str(), 2, false) - 50; // === OK Valid - params.tireRearRightTempC = hexToDec(responseRowMerged.substring(32, 34).c_str(), 2, false) - 50; // === OK Valid - params.tireRearLeftTempC = hexToDec(responseRowMerged.substring(40, 42).c_str(), 2, false) - 50; // === OK Valid - } - } - - return true; -} - -/** - Test data -*/ -bool testDataHyundaiIoniq() { - - // VMCU ATSH7E2 - currentAtshRequest = "ATSH7E2"; - // 2101 - commandRequest = "2101"; - responseRowMerged = "6101FFE0000009211222062F03000000001D7734"; - parseRowMergedHyundaiIoniq(); - // 2102 - commandRequest = "2102"; - responseRowMerged = "6102FF80000001010000009315B2888D390B08618B683900000000"; - parseRowMergedHyundaiIoniq(); - - // "ATSH7DF", - currentAtshRequest = "ATSH7DF"; - - // AIRCON / ACU ATSH7B3 - currentAtshRequest = "ATSH7B3"; - // 220100 - commandRequest = "220100"; - responseRowMerged = "6201007E5007C8FF8A876A011010FFFF10FF10FFFFFFFFFFFFFFFFFF2EEF767D00FFFF00FFFF000000"; - parseRowMergedHyundaiIoniq(); - // 220102 - commandRequest = "220102"; - responseRowMerged = "620102FF800000A3950000000000002600000000"; - parseRowMergedHyundaiIoniq(); - - // BMS ATSH7E4 - currentAtshRequest = "ATSH7E4"; - // 220101 - commandRequest = "2101"; - responseRowMerged = "6101FFFFFFFF5026482648A3FFC30D9E181717171718170019B50FB501000090000142230001425F0000771B00007486007815D809015C0000000003E800"; - parseRowMergedHyundaiIoniq(); - // 220102 - commandRequest = "2102"; - responseRowMerged = "6102FFFFFFFFB5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5000000"; - parseRowMergedHyundaiIoniq(); - // 220103 - commandRequest = "2103"; - responseRowMerged = "6103FFFFFFFFB5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5000000"; - parseRowMergedHyundaiIoniq(); - // 220104 - commandRequest = "2104"; - responseRowMerged = "6104FFFFFFFFB5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5B5000000"; - parseRowMergedHyundaiIoniq(); - // 220105 - commandRequest = "2105"; - responseRowMerged = "6105FFFFFFFF00000000001717171817171726482648000150181703E81A03E801520029000000000000000000000000"; - parseRowMergedHyundaiIoniq(); - // 220106 - commandRequest = "2106"; - responseRowMerged = "7F2112"; // n/a on ioniq - parseRowMergedHyundaiIoniq(); - - // BCM / TPMS ATSH7A0 - currentAtshRequest = "ATSH7A0"; - // 22c00b - commandRequest = "22c00b"; - responseRowMerged = "62C00BFFFF0000B9510100B9510100B84F0100B54F0100AAAAAAAA"; - parseRowMergedHyundaiIoniq(); - - // ATSH7C6 - currentAtshRequest = "ATSH7C6"; - // 22b002 - commandRequest = "22b002"; - responseRowMerged = "62B002E000000000AD003D2D0000000000000000"; - parseRowMergedHyundaiIoniq(); - - /* params.batModule01TempC = 28; - params.batModule02TempC = 29; - params.batModule03TempC = 28; - params.batModule04TempC = 30; - //params.batTempC = hexToDec(responseRowMerged.substring(36, 38).c_str(), 1, true); - //params.batMaxC = hexToDec(responseRowMerged.substring(34, 36).c_str(), 1, true); - //params.batMinC = hexToDec(responseRowMerged.substring(36, 38).c_str(), 1, true); - - // This is more accurate than min/max from BMS. It's required to detect kona/eniro cold gates (min 15C is needed > 43kW charging, min 25C is needed > 58kW charging) - params.batMinC = params.batMaxC = params.batModule01TempC; - params.batMinC = (params.batModule02TempC < params.batMinC) ? params.batModule02TempC : params.batMinC ; - params.batMinC = (params.batModule03TempC < params.batMinC) ? params.batModule03TempC : params.batMinC ; - params.batMinC = (params.batModule04TempC < params.batMinC) ? params.batModule04TempC : params.batMinC ; - params.batMaxC = (params.batModule02TempC > params.batMaxC) ? params.batModule02TempC : params.batMaxC ; - params.batMaxC = (params.batModule03TempC > params.batMaxC) ? params.batModule03TempC : params.batMaxC ; - params.batMaxC = (params.batModule04TempC > params.batMaxC) ? params.batModule04TempC : params.batMaxC ; - params.batTempC = params.batMinC; - - // - params.soc10ced[10] = 2200; - params.soc10cec[10] = 2500; - params.soc10odo[10] = 13000; - params.soc10time[10] = 13000; - params.soc10ced[9] = params.soc10ced[10] + 6.4; - params.soc10cec[9] = params.soc10cec[10] + 0; - params.soc10odo[9] = params.soc10odo[10] + 30; - params.soc10time[9] = params.soc10time[10] + 900; - params.soc10ced[8] = params.soc10ced[9] + 6.8; - params.soc10cec[8] = params.soc10cec[9] + 0; - params.soc10odo[8] = params.soc10odo[9] + 30; - params.soc10time[8] = params.soc10time[9] + 900; - params.soc10ced[7] = params.soc10ced[8] + 7.2; - params.soc10cec[7] = params.soc10cec[8] + 0.6; - params.soc10odo[7] = params.soc10odo[8] + 30; - params.soc10time[7] = params.soc10time[8] + 900; - params.soc10ced[6] = params.soc10ced[7] + 6.7; - params.soc10cec[6] = params.soc10cec[7] + 0; - params.soc10odo[6] = params.soc10odo[7] + 30; - params.soc10time[6] = params.soc10time[7] + 900; - params.soc10ced[5] = params.soc10ced[6] + 6.7; - params.soc10cec[5] = params.soc10cec[6] + 0; - params.soc10odo[5] = params.soc10odo[6] + 30; - params.soc10time[5] = params.soc10time[6] + 900; - params.soc10ced[4] = params.soc10ced[5] + 6.4; - params.soc10cec[4] = params.soc10cec[5] + 0.3; - params.soc10odo[4] = params.soc10odo[5] + 30; - params.soc10time[4] = params.soc10time[5] + 900; - params.soc10ced[3] = params.soc10ced[4] + 6.4; - params.soc10cec[3] = params.soc10cec[4] + 0; - params.soc10odo[3] = params.soc10odo[4] + 30; - params.soc10time[3] = params.soc10time[4] + 900; - params.soc10ced[2] = params.soc10ced[3] + 5.4; - params.soc10cec[2] = params.soc10cec[3] + 0.1; - params.soc10odo[2] = params.soc10odo[3] + 30; - params.soc10time[2] = params.soc10time[3] + 900; - params.soc10ced[1] = params.soc10ced[2] + 6.2; - params.soc10cec[1] = params.soc10cec[2] + 0.1; - params.soc10odo[1] = params.soc10odo[2] + 30; - params.soc10time[1] = params.soc10time[2] + 900; - params.soc10ced[0] = params.soc10ced[1] + 2.9; - params.soc10cec[0] = params.soc10cec[1] + 0.5; - params.soc10odo[0] = params.soc10odo[1] + 15; - params.soc10time[0] = params.soc10time[1] + 900; - */ - return true; -} diff --git a/car_kia_eniro.h b/car_kia_eniro.h deleted file mode 100644 index 8d56d3d..0000000 --- a/car_kia_eniro.h +++ /dev/null @@ -1,430 +0,0 @@ - -#define commandQueueCountKiaENiro 30 -#define commandQueueLoopFromKiaENiro 10 - -String commandQueueKiaENiro[commandQueueCountKiaENiro] = { - "AT Z", // Reset all - "AT I", // Print the version ID - "AT S0", // Printing of spaces on - "AT E0", // Echo off - "AT L0", // Linefeeds off - "AT SP 6", // Select protocol to ISO 15765-4 CAN (11 bit ID, 500 kbit/s) - //"AT AL", // Allow Long (>7 byte) messages - //"AT AR", // Automatically receive - //"AT H1", // Headers on (debug only) - //"AT D1", // Display of the DLC on - //"AT CAF0", // Automatic formatting off - ////"AT AT0", // disabled adaptive timing - "AT DP", - "AT ST16", // reduced timeout to 1, orig.16 - - // Loop from (KIA ENIRO) - - // ABS / ESP + AHB - "ATSH7D1", - "22C101", // brake, park/drive mode - - // IGPM - "ATSH770", - "22BC03", // low beam - "22BC06", // brake light - - // VMCU - "ATSH7E2", - "2101", // speed, ... - "2102", // aux, ... - - // BMS - "ATSH7E4", - "220101", // power kw, ... - "220102", // cell voltages - "220103", // cell voltages - "220104", // cell voltages - "220105", // soh, soc, .. - "220106", // cooling water temp - - // Aircondition - "ATSH7B3", - "220100", // in/out temp - "220102", // coolant temp1, 2 - - // BCM / TPMS - "ATSH7A0", - "22c00b", // tire pressure/temp - - // CLUSTER MODULE - "ATSH7C6", - "22B002", // odo - -}; - -/** - Init command queue -*/ -bool activateCommandQueueForKiaENiro() { - - // 39 or 64 kWh model? - params.batModuleTempCount = 4; - params.batteryTotalAvailableKWh = 64; - // =(I18*0,615)*(1+(I18*0,0008)) soc to kwh niro ev 2020 - if (settings.carType == CAR_KIA_ENIRO_2020_39 || settings.carType == CAR_HYUNDAI_KONA_2020_39) { - params.batteryTotalAvailableKWh = 39.2; - } - - // Empty and fill command queue - for (int i = 0; i < 300; i++) { - commandQueue[i] = ""; - } - for (int i = 0; i < commandQueueCountKiaENiro; i++) { - commandQueue[i] = commandQueueKiaENiro[i]; - } - - commandQueueLoopFrom = commandQueueLoopFromKiaENiro; - commandQueueCount = commandQueueCountKiaENiro; - - return true; -} - -/** - Parse merged row -*/ -bool parseRowMergedKiaENiro() { - - bool tempByte; - - // ABS / ESP + AHB 7D1 - if (currentAtshRequest.equals("ATSH7D1")) { - if (commandRequest.equals("22C101")) { - uint8_t driveMode = hexToDec(responseRowMerged.substring(22, 24).c_str(), 1, false); - params.forwardDriveMode = (driveMode == 4); - params.reverseDriveMode = (driveMode == 2); - params.parkModeOrNeutral = (driveMode == 1); - } - } - - // IGPM - if (currentAtshRequest.equals("ATSH770")) { - if (commandRequest.equals("22BC03")) { - tempByte = hexToDec(responseRowMerged.substring(16, 18).c_str(), 1, false); - params.ignitionOnPrevious = params.ignitionOn; - params.ignitionOn = (bitRead(tempByte, 5) == 1); - if (params.ignitionOnPrevious && !params.ignitionOn) - params.automatickShutdownTimer = params.currentTime; - - params.lightInfo = hexToDec(responseRowMerged.substring(18, 20).c_str(), 1, false); - params.headLights = (bitRead(params.lightInfo, 5) == 1); - params.dayLights = (bitRead(params.lightInfo, 3) == 1); - } - if (commandRequest.equals("22BC06")) { - params.brakeLightInfo = hexToDec(responseRowMerged.substring(14, 16).c_str(), 1, false); - params.brakeLights = (bitRead(params.brakeLightInfo, 5) == 1); - } - } - - // VMCU 7E2 - if (currentAtshRequest.equals("ATSH7E2")) { - if (commandRequest.equals("2101")) { - params.speedKmh = hexToDec(responseRowMerged.substring(32, 36).c_str(), 2, false) * 0.0155; // / 100.0 *1.609 = real to gps is 1.750 - if (params.speedKmh < -99 || params.speedKmh > 200) - params.speedKmh = 0; - } - if (commandRequest.equals("2102")) { - params.auxPerc = hexToDec(responseRowMerged.substring(50, 52).c_str(), 1, false); - params.auxCurrentAmp = - hexToDec(responseRowMerged.substring(46, 50).c_str(), 2, true) / 1000.0; - } - } - - // Cluster module 7c6 - if (currentAtshRequest.equals("ATSH7C6")) { - if (commandRequest.equals("22B002")) { - params.odoKm = float(strtol(responseRowMerged.substring(18, 24).c_str(), 0, 16)); - } - } - - // Aircon 7b3 - if (currentAtshRequest.equals("ATSH7B3")) { - if (commandRequest.equals("220100")) { - params.indoorTemperature = (hexToDec(responseRowMerged.substring(16, 18).c_str(), 1, false) / 2) - 40; - params.outdoorTemperature = (hexToDec(responseRowMerged.substring(18, 20).c_str(), 1, false) / 2) - 40; - } - if (commandRequest.equals("220102") && responseRowMerged.substring(12, 14) == "00") { - params.coolantTemp1C = (hexToDec(responseRowMerged.substring(14, 16).c_str(), 1, false) / 2) - 40; - params.coolantTemp2C = (hexToDec(responseRowMerged.substring(16, 18).c_str(), 1, false) / 2) - 40; - } - } - - // BMS 7e4 - if (currentAtshRequest.equals("ATSH7E4")) { - if (commandRequest.equals("220101")) { - params.cumulativeEnergyChargedKWh = float(strtol(responseRowMerged.substring(82, 90).c_str(), 0, 16)) / 10.0; - if (params.cumulativeEnergyChargedKWhStart == -1) - params.cumulativeEnergyChargedKWhStart = params.cumulativeEnergyChargedKWh; - params.cumulativeEnergyDischargedKWh = float(strtol(responseRowMerged.substring(90, 98).c_str(), 0, 16)) / 10.0; - if (params.cumulativeEnergyDischargedKWhStart == -1) - params.cumulativeEnergyDischargedKWhStart = params.cumulativeEnergyDischargedKWh; - params.availableChargePower = float(strtol(responseRowMerged.substring(16, 20).c_str(), 0, 16)) / 100.0; - params.availableDischargePower = float(strtol(responseRowMerged.substring(20, 24).c_str(), 0, 16)) / 100.0; - //params.isolationResistanceKOhm = hexToDec(responseRowMerged.substring(118, 122).c_str(), 2, true); - params.batFanStatus = hexToDec(responseRowMerged.substring(60, 62).c_str(), 2, true); - params.batFanFeedbackHz = hexToDec(responseRowMerged.substring(62, 64).c_str(), 2, true); - params.auxVoltage = hexToDec(responseRowMerged.substring(64, 66).c_str(), 2, true) / 10.0; - params.batPowerAmp = - hexToDec(responseRowMerged.substring(26, 30).c_str(), 2, true) / 10.0; - params.batVoltage = hexToDec(responseRowMerged.substring(30, 34).c_str(), 2, false) / 10.0; - params.batPowerKw = (params.batPowerAmp * params.batVoltage) / 1000.0; - if (params.batPowerKw < 0) // Reset charging start time - params.chargingStartTime = params.currentTime; - params.batPowerKwh100 = params.batPowerKw / params.speedKmh * 100; - params.batCellMaxV = hexToDec(responseRowMerged.substring(52, 54).c_str(), 1, false) / 50.0; - params.batCellMinV = hexToDec(responseRowMerged.substring(56, 58).c_str(), 1, false) / 50.0; - params.batModuleTempC[0] = hexToDec(responseRowMerged.substring(38, 40).c_str(), 1, true); - params.batModuleTempC[1] = hexToDec(responseRowMerged.substring(40, 42).c_str(), 1, true); - params.batModuleTempC[2] = hexToDec(responseRowMerged.substring(42, 44).c_str(), 1, true); - params.batModuleTempC[3] = hexToDec(responseRowMerged.substring(44, 46).c_str(), 1, true); - params.motorRpm = hexToDec(responseRowMerged.substring(112, 116).c_str(), 2, false); - //params.batTempC = hexToDec(responseRowMerged.substring(36, 38).c_str(), 1, true); - //params.batMaxC = hexToDec(responseRowMerged.substring(34, 36).c_str(), 1, true); - //params.batMinC = hexToDec(responseRowMerged.substring(36, 38).c_str(), 1, true); - - // This is more accurate than min/max from BMS. It's required to detect kona/eniro cold gates (min 15C is needed > 43kW charging, min 25C is needed > 58kW charging) - params.batMinC = params.batMaxC = params.batModuleTempC[0]; - for (uint16_t i = 1; i < params.batModuleTempCount; i++) { - if (params.batModuleTempC[i] < params.batMinC) - params.batMinC = params.batModuleTempC[i]; - if (params.batModuleTempC[i] > params.batMaxC) - params.batMaxC = params.batModuleTempC[i]; - } - params.batTempC = params.batMinC; - - params.batInletC = hexToDec(responseRowMerged.substring(50, 52).c_str(), 1, true); - if (params.speedKmh < 10 && params.batPowerKw >= 1 && params.socPerc > 0 && params.socPerc <= 100) { - if ( params.chargingGraphMinKw[int(params.socPerc)] < 0 || params.batPowerKw < params.chargingGraphMinKw[int(params.socPerc)]) - params.chargingGraphMinKw[int(params.socPerc)] = params.batPowerKw; - if ( params.chargingGraphMaxKw[int(params.socPerc)] < 0 || params.batPowerKw > params.chargingGraphMaxKw[int(params.socPerc)]) - params.chargingGraphMaxKw[int(params.socPerc)] = params.batPowerKw; - params.chargingGraphBatMinTempC[int(params.socPerc)] = params.batMinC; - params.chargingGraphBatMaxTempC[int(params.socPerc)] = params.batMaxC; - params.chargingGraphHeaterTempC[int(params.socPerc)] = params.batHeaterC; - params.chargingGraphWaterCoolantTempC[int(params.socPerc)] = params.coolingWaterTempC; - } - } - // BMS 7e4 - if (commandRequest.equals("220102") && responseRowMerged.substring(12, 14) == "FF") { - for (int i = 0; i < 32; i++) { - params.cellVoltage[i] = hexToDec(responseRowMerged.substring(14 + (i * 2), 14 + (i * 2) + 2).c_str(), 1, false) / 50; - } - } - // BMS 7e4 - if (commandRequest.equals("220103")) { - for (int i = 0; i < 32; i++) { - params.cellVoltage[32 + i] = hexToDec(responseRowMerged.substring(14 + (i * 2), 14 + (i * 2) + 2).c_str(), 1, false) / 50; - } - } - // BMS 7e4 - if (commandRequest.equals("220104")) { - for (int i = 0; i < 32; i++) { - params.cellVoltage[64 + i] = hexToDec(responseRowMerged.substring(14 + (i * 2), 14 + (i * 2) + 2).c_str(), 1, false) / 50; - } - } - // BMS 7e4 - if (commandRequest.equals("220105")) { - params.socPercPrevious = params.socPerc; - params.sohPerc = hexToDec(responseRowMerged.substring(56, 60).c_str(), 2, false) / 10.0; - params.socPerc = hexToDec(responseRowMerged.substring(68, 70).c_str(), 1, false) / 2.0; - - // Soc10ced table, record x0% CEC/CED table (ex. 90%->89%, 80%->79%) - if (params.socPercPrevious - params.socPerc > 0) { - byte index = (int(params.socPerc) == 4) ? 0 : (int)(params.socPerc / 10) + 1; - if ((int(params.socPerc) % 10 == 9 || int(params.socPerc) == 4) && params.soc10ced[index] == -1) { - params.soc10ced[index] = params.cumulativeEnergyDischargedKWh; - params.soc10cec[index] = params.cumulativeEnergyChargedKWh; - params.soc10odo[index] = params.odoKm; - params.soc10time[index] = params.currentTime; - } - } - params.bmsUnknownTempA = hexToDec(responseRowMerged.substring(30, 32).c_str(), 1, true); - params.batHeaterC = hexToDec(responseRowMerged.substring(52, 54).c_str(), 1, true); - params.bmsUnknownTempB = hexToDec(responseRowMerged.substring(82, 84).c_str(), 1, true); - // - for (int i = 30; i < 32; i++) { // ai/aj position - params.cellVoltage[96 - 30 + i] = hexToDec(responseRowMerged.substring(14 + (i * 2), 14 + (i * 2) + 2).c_str(), 1, false) / 50; - } - } - // BMS 7e4 - if (commandRequest.equals("220106")) { - params.coolingWaterTempC = hexToDec(responseRowMerged.substring(14, 16).c_str(), 1, false); - params.bmsUnknownTempC = hexToDec(responseRowMerged.substring(18, 20).c_str(), 1, true); - params.bmsUnknownTempD = hexToDec(responseRowMerged.substring(46, 48).c_str(), 1, true); - } - } - - // TPMS 7a0 - if (currentAtshRequest.equals("ATSH7A0")) { - if (commandRequest.equals("22c00b")) { - params.tireFrontLeftPressureBar = hexToDec(responseRowMerged.substring(14, 16).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722 - params.tireFrontRightPressureBar = hexToDec(responseRowMerged.substring(22, 24).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722 - params.tireRearRightPressureBar = hexToDec(responseRowMerged.substring(30, 32).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722 - params.tireRearLeftPressureBar = hexToDec(responseRowMerged.substring(38, 40).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722 - params.tireFrontLeftTempC = hexToDec(responseRowMerged.substring(16, 18).c_str(), 2, false) - 50; // === OK Valid - params.tireFrontRightTempC = hexToDec(responseRowMerged.substring(24, 26).c_str(), 2, false) - 50; // === OK Valid - params.tireRearRightTempC = hexToDec(responseRowMerged.substring(32, 34).c_str(), 2, false) - 50; // === OK Valid - params.tireRearLeftTempC = hexToDec(responseRowMerged.substring(40, 42).c_str(), 2, false) - 50; // === OK Valid - } - } - - return true; -} - -/** - Test data -*/ -bool testDataKiaENiro() { - - - // IGPM - currentAtshRequest = "ATSH770"; - // 22BC03 - commandRequest = "22BC03"; - responseRowMerged = "62BC03FDEE7C730A600000AAAA"; - parseRowMergedKiaENiro(); - - // ABS / ESP + AHB ATSH7D1 - currentAtshRequest = "ATSH7D1"; - // 2101 - commandRequest = "22C101"; - responseRowMerged = "62C1015FD7E7D0FFFF00FF04D0D400000000FF7EFF0030F5010000FFFF7F6307F207FE05FF00FF3FFFFFAAAAAAAAAAAA"; - parseRowMergedKiaENiro(); - - // VMCU ATSH7E2 - currentAtshRequest = "ATSH7E2"; - // 2101 - commandRequest = "2101"; - responseRowMerged = "6101FFF8000009285A3B0648030000B4179D763404080805000000"; - parseRowMergedKiaENiro(); - // 2102 - commandRequest = "2102"; - responseRowMerged = "6102F8FFFC000101000000840FBF83BD33270680953033757F59291C76000001010100000007000000"; - responseRowMerged = "6102F8FFFC000101000000931CC77F4C39040BE09BA7385D8158832175000001010100000007000000"; - parseRowMergedKiaENiro(); - - // "ATSH7DF", - currentAtshRequest = "ATSH7DF"; - // 2106 - commandRequest = "2106"; - responseRowMerged = "6106FFFF800000000000000200001B001C001C000600060006000E000000010000000000000000013D013D013E013E00"; - parseRowMergedKiaENiro(); - - // AIRCON / ACU ATSH7B3 - currentAtshRequest = "ATSH7B3"; - // 220100 - commandRequest = "220100"; - responseRowMerged = "6201007E5027C8FF7F765D05B95AFFFF5AFF11FFFFFFFFFFFF6AFFFF2DF0757630FFFF00FFFF000000"; - responseRowMerged = "6201007E5027C8FF867C58121010FFFF10FF8EFFFFFFFFFFFF10FFFF0DF0617900FFFF01FFFF000000"; - parseRowMergedKiaENiro(); - - // BMS ATSH7E4 - currentAtshRequest = "ATSH7E4"; - // 220101 - commandRequest = "220101"; - responseRowMerged = "620101FFF7E7FF99000000000300B10EFE120F11100F12000018C438C30B00008400003864000035850000153A00001374000647010D017F0BDA0BDA03E8"; - responseRowMerged = "620101FFF7E7FFB3000000000300120F9B111011101011000014CC38CB3B00009100003A510000367C000015FB000013D3000690250D018E0000000003E8"; - parseRowMergedKiaENiro(); - // 220102 - commandRequest = "220102"; - responseRowMerged = "620102FFFFFFFFCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBAAAA"; - parseRowMergedKiaENiro(); - // 220103 - commandRequest = "220103"; - responseRowMerged = "620103FFFFFFFFCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCACBCACACFCCCBCBCBCBCBCBCBCBAAAA"; - parseRowMergedKiaENiro(); - // 220104 - commandRequest = "220104"; - responseRowMerged = "620104FFFFFFFFCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBAAAA"; - parseRowMergedKiaENiro(); - // 220105 - commandRequest = "220105"; - responseRowMerged = "620105003fff9000000000000000000F8A86012B4946500101500DAC03E800000000AC0000C7C701000F00000000AAAA"; - responseRowMerged = "620105003FFF90000000000000000014918E012927465000015013BB03E800000000BB0000CBCB01001300000000AAAA"; - parseRowMergedKiaENiro(); - // 220106 - commandRequest = "220106"; - responseRowMerged = "620106FFFFFFFF14001A00240000003A7C86B4B30000000928EA00"; - parseRowMergedKiaENiro(); - - // BCM / TPMS ATSH7A0 - currentAtshRequest = "ATSH7A0"; - // 22c00b - commandRequest = "22c00b"; - responseRowMerged = "62C00BFFFF0000B93D0100B43E0100B43D0100BB3C0100AAAAAAAA"; - parseRowMergedKiaENiro(); - - // ATSH7C6 - currentAtshRequest = "ATSH7C6"; - // 22b002 - commandRequest = "22b002"; - responseRowMerged = "62B002E0000000FFB400330B0000000000000000"; - parseRowMergedKiaENiro(); - - params.batModuleTempC[0] = 28; - params.batModuleTempC[1] = 29; - params.batModuleTempC[2] = 28; - params.batModuleTempC[3] = 30; - - // This is more accurate than min/max from BMS. It's required to detect kona/eniro cold gates (min 15C is needed > 43kW charging, min 25C is needed > 58kW charging) - params.batMinC = params.batMaxC = params.batModuleTempC[0]; - for (uint16_t i = 1; i < params.batModuleTempCount; i++) { - if (params.batModuleTempC[i] < params.batMinC) - params.batMinC = params.batModuleTempC[i]; - if (params.batModuleTempC[i] > params.batMaxC) - params.batMaxC = params.batModuleTempC[i]; - } - params.batTempC = params.batMinC; - - - // - params.soc10ced[10] = 2200; - params.soc10cec[10] = 2500; - params.soc10odo[10] = 13000; - params.soc10time[10] = 13000; - params.soc10ced[9] = params.soc10ced[10] + 6.4; - params.soc10cec[9] = params.soc10cec[10] + 0; - params.soc10odo[9] = params.soc10odo[10] + 30; - params.soc10time[9] = params.soc10time[10] + 900; - params.soc10ced[8] = params.soc10ced[9] + 6.8; - params.soc10cec[8] = params.soc10cec[9] + 0; - params.soc10odo[8] = params.soc10odo[9] + 30; - params.soc10time[8] = params.soc10time[9] + 900; - params.soc10ced[7] = params.soc10ced[8] + 7.2; - params.soc10cec[7] = params.soc10cec[8] + 0.6; - params.soc10odo[7] = params.soc10odo[8] + 30; - params.soc10time[7] = params.soc10time[8] + 900; - params.soc10ced[6] = params.soc10ced[7] + 6.7; - params.soc10cec[6] = params.soc10cec[7] + 0; - params.soc10odo[6] = params.soc10odo[7] + 30; - params.soc10time[6] = params.soc10time[7] + 900; - params.soc10ced[5] = params.soc10ced[6] + 6.7; - params.soc10cec[5] = params.soc10cec[6] + 0; - params.soc10odo[5] = params.soc10odo[6] + 30; - params.soc10time[5] = params.soc10time[6] + 900; - params.soc10ced[4] = params.soc10ced[5] + 6.4; - params.soc10cec[4] = params.soc10cec[5] + 0.3; - params.soc10odo[4] = params.soc10odo[5] + 30; - params.soc10time[4] = params.soc10time[5] + 900; - params.soc10ced[3] = params.soc10ced[4] + 6.4; - params.soc10cec[3] = params.soc10cec[4] + 0; - params.soc10odo[3] = params.soc10odo[4] + 30; - params.soc10time[3] = params.soc10time[4] + 900; - params.soc10ced[2] = params.soc10ced[3] + 5.4; - params.soc10cec[2] = params.soc10cec[3] + 0.1; - params.soc10odo[2] = params.soc10odo[3] + 30; - params.soc10time[2] = params.soc10time[3] + 900; - params.soc10ced[1] = params.soc10ced[2] + 6.2; - params.soc10cec[1] = params.soc10cec[2] + 0.1; - params.soc10odo[1] = params.soc10odo[2] + 30; - params.soc10time[1] = params.soc10time[2] + 900; - params.soc10ced[0] = params.soc10ced[1] + 2.9; - params.soc10cec[0] = params.soc10cec[1] + 0.5; - params.soc10odo[0] = params.soc10odo[1] + 15; - params.soc10time[0] = params.soc10time[1] + 900; - - return true; -} diff --git a/car_renault_zoe.h b/car_renault_zoe.h deleted file mode 100644 index 11fd32c..0000000 --- a/car_renault_zoe.h +++ /dev/null @@ -1,66 +0,0 @@ - -#define commandQueueCountRenaultZoe 10 -#define commandQueueLoopFromRenaultZoe 8 - -String commandQueueRenaultZoe[commandQueueCountRenaultZoe] = { - "AT Z", // Reset all - "AT I", // Print the version ID - "AT E0", // Echo off - "AT L0", // Linefeeds off - "AT S0", // Printing of spaces on - "AT SP 6", // Select protocol to ISO 15765-4 CAN (11 bit ID, 500 kbit/s) - //"AT AL", // Allow Long (>7 byte) messages - //"AT AR", // Automatically receive - //"AT H1", // Headers on (debug only) - //"AT D1", // Display of the DLC on - //"AT CAF0", // Automatic formatting off - "AT DP", - "AT ST16", - - // Loop from (KIA ENIRO) // TODO - // BMS - "ATSH7E4", - "220101", // power kw, ... -}; - -/** - Init command queue -*/ -bool activateCommandQueueForRenaultZoe() { - - params.batModuleTempCount = 4; - params.batteryTotalAvailableKWh = 22; - - // Empty and fill command queue - for (int i = 0; i < 300; i++) { - commandQueue[i] = ""; - } - for (int i = 0; i < commandQueueCountRenaultZoe; i++) { - commandQueue[i] = commandQueueRenaultZoe[i]; - } - - commandQueueLoopFrom = commandQueueLoopFromRenaultZoe; - commandQueueCount = commandQueueCountRenaultZoe; - - return true; -} - -/** - Parse merged row -*/ -bool parseRowMergedRenaultZoe() { - - // TODO - - return true; -} - -/** - Test data -*/ -bool testDataRenaultZoe() { - - // TODO - - return true; -} diff --git a/config.h b/config.h index 07e1ea1..d8396c0 100644 --- a/config.h +++ b/config.h @@ -1,92 +1,10 @@ -//////////////////////////////////////////////////////////// -// SELECT HARDWARE !!!! -//////////////////////////////////////////////////////////// +#ifndef CONFIG_H +#define CONFIG_H -#define BOARD_TTGO_T4 -//#define BOARD_M5STACK_CORE +#include -//////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////// - -#define USER_SETUP_LOADED 1 -#define SPI_FREQUENCY 27000000 -//#define SPI_READ_FREQUENCY 20000000 -#define SPI_TOUCH_FREQUENCY 2500000 - -//////////////////////////////////////////////////////////// -// BOARD LILYGO TTGO T4 v1.3 -///////////////////////////////////////////////////////////// -#ifdef BOARD_TTGO_T4 - -#define ILI9341_DRIVER -#define TFT_MISO 12 -#define TFT_MOSI 23 -#define TFT_SCLK 18 -#define TFT_CS 27 -#define TFT_DC 32 -#define TFT_RST 5 -//#define TFT_BACKLIGHT_ON HIGH -#define TFT_BL 4 - -#define USE_HSPI_PORT -//#define SPI_FREQUENCY 40000000 // Maximum for ILI9341 -#define SPI_READ_FREQUENCY 6000000 // 6 MHz is the maximum SPI read speed for the ST7789V - -#define SD_CS 13 -#define SD_MOSI 15 -#define SD_MISO 2 -#define SD_SCLK 14 - -#define BUTTON_LEFT 38 -#define BUTTON_MIDDLE 37 -#define BUTTON_RIGHT 39 - -#endif // BOARD_TTGO_T4 - -///////////////////////////////////////////////////////////// -// BOARD M5STACK CORE IOT (M5-K001) -///////////////////////////////////////////////////////////// -#ifdef BOARD_M5STACK_CORE - -#define USER_SETUP_LOADED 1 -#define ILI9341_DRIVER -#define M5STACK -#define TFT_MISO 19 -#define TFT_MOSI 23 -#define TFT_SCLK 18 -#define TFT_CS 14 // Chip select control pin -#define TFT_DC 27 // Data Command control pin -#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) -#define TFT_BL 32 // LED back-light -#define SPI_FREQUENCY 27000000 -#define SPI_READ_FREQUENCY 5000000 -#define SPEAKER_PIN 25 -#define INVERT_DISPLAY - -#define BUTTON_LEFT 37 -#define BUTTON_MIDDLE 38 -#define BUTTON_RIGHT 39 - -#define SD_CS 4 -#define SD_MOSI 23 -#define SD_MISO 19 -#define SD_SCLK 18 - -#endif // BOARD_M5STACK_CORE - -///////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////// - -// 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 +#define APP_VERSION "v2.0.0" +#define APP_RELEASE_DATE "2020-12-02" // TFT COLORS FOR TTGO #define TFT_BLACK 0x0000 /* 0, 0, 0 */ @@ -126,3 +44,26 @@ #define TFT_GRAPH_COLDGATE15_24 0x0008 #define TFT_GRAPH_OPTIMAL25 0x0200 #define TFT_GRAPH_RAPIDGATE35 0x8300 + +//////////////////////////////////////////////////////////// +// SIM800L +///////////////////////////////////////////////////////////// + +#ifdef SIM800L_ENABLED +#define SIM800L_RX 16 +#define SIM800L_TX 17 +#define SIM800L_RST 5 +#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/dist/m5stack_core1/evDash.ino.bin b/dist/m5stack_core1/evDash.ino.bin new file mode 100644 index 0000000..9b10c92 Binary files /dev/null and b/dist/m5stack_core1/evDash.ino.bin differ diff --git a/dist/m5stack_core1/enirodashboard.ino.partitions.bin b/dist/m5stack_core1/evDash.ino.partitions.bin similarity index 100% rename from dist/m5stack_core1/enirodashboard.ino.partitions.bin rename to dist/m5stack_core1/evDash.ino.partitions.bin diff --git a/dist/ttgo_t4_v13/evDash.ino.bin b/dist/ttgo_t4_v13/evDash.ino.bin new file mode 100644 index 0000000..9005db6 Binary files /dev/null and b/dist/ttgo_t4_v13/evDash.ino.bin differ diff --git a/dist/ttgo_t4_v13/enirodashboard.ino.partitions.bin b/dist/ttgo_t4_v13/evDash.ino.partitions.bin similarity index 100% rename from dist/ttgo_t4_v13/enirodashboard.ino.partitions.bin rename to dist/ttgo_t4_v13/evDash.ino.partitions.bin diff --git a/evDash.ino b/evDash.ino new file mode 100644 index 0000000..5aa9176 --- /dev/null +++ b/evDash.ino @@ -0,0 +1,668 @@ +/* + * 2020-12-02 + * Project renamed from eNiroDashboard to evDash + * + !! working only with OBD BLE 4.0 adapters + !! 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 + + SIM800L m5stack (https://github.com/kolaCZek) + - SIM800L.h, SoftwareSerial.h +*/ + +//////////////////////////////////////////////////////////// +// SELECT HARDWARE +//////////////////////////////////////////////////////////// + +// Boards +#define BOARD_TTGO_T4 +//#define BOARD_M5STACK_CORE + +//#define SIM800L_ENABLED +//#define SD_ENABLED + +//////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////// + +#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 + +#ifdef SD_ENABLED +#include +//#include +#endif + +#include +#include "config.h" +#include "LiveData.h" +#include "CarInterface.h" +#include "CarKiaEniro.h" +#include "CarHyundaiIoniq.h" +#include "CarKiaDebugObd2.h" + +#ifdef SIM800L_ENABLED +#include +#include +#include "SIM800L.h" + +SIM800L* sim800l; +#endif //SIM800L_ENABLED + +// PLEASE CHANGE THIS SETTING for your BLE4 +uint32_t PIN = 1234; + +// Temporary variables +char ch; +String line; + +// Board, Car, Livedata (params, settings) +BoardInterface* board; +CarInterface* car; +LiveData* liveData; + +/** + Do next AT command from queue +*/ +bool doNextAtCommand() { + + // Restart loop with AT commands + if (liveData->commandQueueIndex >= liveData->commandQueueCount) { + liveData->commandQueueIndex = liveData->commandQueueLoopFrom; + board->redrawScreen(); + } + + // Send AT command to obd + liveData->commandRequest = liveData->commandQueue[liveData->commandQueueIndex]; + if (liveData->commandRequest.startsWith("ATSH")) { + liveData->currentAtshRequest = liveData->commandRequest; + } + + Serial.print(">>> "); + Serial.println(liveData->commandRequest); + String tmpStr = liveData->commandRequest + "\r"; + liveData->pRemoteCharacteristicWrite->writeValue(tmpStr.c_str(), tmpStr.length()); + liveData->commandQueueIndex++; + + return true; +} + +/** + Parse result from OBD, create single line liveData->responseRowMerged +*/ +bool parseRow() { + + // Simple 1 line responses + Serial.print(""); + Serial.println(liveData->responseRow); + + // Merge 0:xxxx 1:yyyy 2:zzzz to single xxxxyyyyzzzz string + if (liveData->responseRow.length() >= 2 && liveData->responseRow.charAt(1) == ':') { + if (liveData->responseRow.charAt(0) == '0') { + liveData->responseRowMerged = ""; + } + liveData->responseRowMerged += liveData->responseRow.substring(2); + } + + return true; +} + +/** + Parse merged row (after merge completed) +*/ +bool parseRowMerged() { + + Serial.print("merged:"); + Serial.println(liveData->responseRowMerged); + + // Catch output for debug screen + if (board->displayScreen == SCREEN_DEBUG) { + if (board->debugCommandIndex == liveData->commandQueueIndex) { + board->debugAtshRequest = liveData->currentAtshRequest; + board->debugCommandRequest = liveData->commandRequest; + board->debugLastString = liveData->responseRowMerged; + } + } + + // Parse by selected car interface + car->parseRowMerged(); + + return true; +} + +/** + BLE callbacks +*/ +class MyClientCallback : public BLEClientCallbacks { + + /** + On BLE connect + */ + void onConnect(BLEClient* pclient) { + Serial.println("onConnect"); + } + + /** + On BLE disconnect + */ + void onDisconnect(BLEClient* pclient) { + //connected = false; + Serial.println("onDisconnect"); + board->displayMessage("BLE disconnected", ""); + } +}; + +/** + Scan for BLE servers and find the first one that advertises the service we are looking for. +*/ +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + + /** + Called for each advertising BLE server. + */ + void onResult(BLEAdvertisedDevice advertisedDevice) { + + Serial.print("BLE advertised device found: "); + Serial.println(advertisedDevice.toString().c_str()); + Serial.println(advertisedDevice.getAddress().toString().c_str()); + + // Add to device list (max. 9 devices allowed yet) + String tmpStr; + + 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(liveData->menuItems[i].title, 48); + tmpStr = advertisedDevice.getAddress().toString().c_str(); + tmpStr.toCharArray(liveData->menuItems[i].obdMacAddress, 18); + } + } + liveData->scanningDeviceIndex++; + } + /* + if (advertisedDevice.getServiceDataUUID().toString() != "") { + Serial.print("ServiceDataUUID: "); + Serial.println(advertisedDevice.getServiceDataUUID().toString().c_str()); + if (advertisedDevice.getServiceUUID().toString() != "") { + Serial.print("ServiceUUID: "); + Serial.println(advertisedDevice.getServiceUUID().toString().c_str()); + } + }*/ + + if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(BLEUUID(liveData->settings.serviceUUID)) && + (strcmp(advertisedDevice.getAddress().toString().c_str(), liveData->settings.obdMacAddress) == 0)) { + Serial.println("Stop scanning. Found my BLE device."); + BLEDevice::getScan()->stop(); + liveData->foundMyBleDevice = new BLEAdvertisedDevice(advertisedDevice); + } + } +}; + +/** + BLE Security +*/ +class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest() { + Serial.printf("Pairing password: %d \r\n", PIN); + return PIN; + } + + void onPassKeyNotify(uint32_t pass_key) { + Serial.printf("onPassKeyNotify\r\n"); + } + + bool onConfirmPIN(uint32_t pass_key) { + Serial.printf("onConfirmPIN\r\n"); + return true; + } + + bool onSecurityRequest() { + Serial.printf("onSecurityRequest\r\n"); + return true; + } + + void onAuthenticationComplete(esp_ble_auth_cmpl_t auth_cmpl) { + if (auth_cmpl.success) { + Serial.printf("onAuthenticationComplete\r\n"); + } else { + Serial.println("Auth failure. Incorrect PIN?"); + liveData->bleConnect = false; + } + } +}; + +/** + Ble notification callback +*/ +static void notifyCallback (BLERemoteCharacteristic * pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) { + + char ch; + + // Parse multi line response to single lines + liveData->responseRow = ""; + for (int i = 0; i <= length; i++) { + ch = pData[i]; + if (ch == '\r' || ch == '\n' || ch == '\0') { + if (liveData->responseRow != "") + parseRow(); + liveData->responseRow = ""; + } else { + liveData->responseRow += ch; + if (liveData->responseRow == ">") { + if (liveData->responseRowMerged != "") { + parseRowMerged(); + } + liveData->responseRowMerged = ""; + liveData->canSendNextAtCommand = true; + } + } + } +} + +/** + Do connect BLE with server (OBD device) +*/ +bool connectToServer(BLEAddress pAddress) { + + board->displayMessage(" > Connecting device", ""); + + Serial.print("liveData->bleConnect "); + Serial.println(pAddress.toString().c_str()); + board->displayMessage(" > Connecting device - init", pAddress.toString().c_str()); + + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + BLEDevice::setSecurityCallbacks(new MySecurity()); + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_BOND); // + pSecurity->setCapability(ESP_IO_CAP_KBDISP); + pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + + 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"); + + // Remote service + 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); + board->displayMessage(" > Connecting device", "Unable to find service"); + return false; + } + Serial.println(" - Found our service"); + + // Get characteristics + 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()); + board->displayMessage(" > Connecting device", "Unable to find TxUUID"); + return false; + } + Serial.println(" - Found our characteristic"); + + // Get characteristics + 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()); + board->displayMessage(" > Connecting device", "Unable to find RxUUID"); + return false; + } + Serial.println(" - Found our characteristic write"); + + board->displayMessage(" > Connecting device", "Register callbacks..."); + // Read the value of the characteristic. + if (liveData->pRemoteCharacteristic->canNotify()) { + Serial.println(" - canNotify"); + //liveData->pRemoteCharacteristic->registerForNotify(notifyCallback); + if (liveData->pRemoteCharacteristic->canIndicate()) { + Serial.println(" - canIndicate"); + const uint8_t indicationOn[] = {0x2, 0x0}; + //const uint8_t indicationOff[] = {0x0,0x0}; + 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); + } + } + + board->displayMessage(" > Connecting device", "Done..."); + if (liveData->pRemoteCharacteristicWrite->canWrite()) { + Serial.println(" - canWrite"); + } + + return true; +} + +/** + Start ble scan +*/ +void startBleScan() { + + 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 = liveData->pBLEScan->start(40, false); + Serial.print("Devices found: "); + Serial.println(foundDevices.getCount()); + Serial.println("Scan done!"); + liveData->pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory + + char tmpStr1[20]; + sprintf(tmpStr1, "Found %d devices", foundDevices.getCount()); + board->displayMessage(" > Scanning BLE4 devices", tmpStr1); + + // Scan devices from menu, show list of devices + if (liveData->menuItemSelected == 2) { + Serial.println("Display menu with devices"); + liveData->menuVisible = true; + liveData->menuCurrent = 9999; + liveData->menuItemSelected = 0; + board->showMenu(); + } else { + // Redraw screen + if (liveData->foundMyBleDevice == NULL) { + board->displayMessage("Device not found", "Middle button - menu"); + } else { + board->redrawScreen(); + } + } +} + +/** + SIM800L +*/ +#ifdef SIM800L_ENABLED +bool sim800lSetup() { + Serial.println("Setting SIM800L module"); + SoftwareSerial* serial = new SoftwareSerial(SIM800L_RX, SIM800L_TX); + serial->begin(9600); + sim800l = new SIM800L((Stream *)serial, SIM800L_RST, 512 , 512); + + bool sim800l_ready = sim800l->isReady(); + 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(); + } + + if(!sim800l_ready) { + Serial.println("Problem to initialize SIM800L module"); + } else { + Serial.println("SIM800L module initialized"); + + Serial.print("Setting GPRS APN to: "); + Serial.println(liveData->settings.gprsApn); + + bool sim800l_gprs = sim800l->setupGPRS(liveData->settings.gprsApn); + for(uint8_t i = 0; i < 5 && !sim800l_gprs; i++) { + Serial.println("Problem to set GPRS connection, retry in 1 sec"); + delay(1000); + sim800l_gprs = sim800l->setupGPRS(liveData->settings.gprsApn); + } + + if(sim800l_gprs) { + liveData->params.sim800l_enabled = true; + Serial.println("GPRS OK"); + } else { + Serial.println("Problem to set GPRS"); + } + } + + return true; +} + +bool sendDataViaGPRS() { + Serial.println("Sending data via GPRS"); + + NetworkRegistration network = sim800l->getRegistrationStatus(); + 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++) { + delay(1000); + connected = sim800l->connectGPRS(); + } + + if (!connected) { + Serial.println("GPRS not connected! Reseting SIM800L module!"); + sim800l->reset(); + sim800lSetup(); + + return false; + } + + Serial.println("Start HTTP POST..."); + + StaticJsonDocument<250> jsonData; + + jsonData["akey"] = liveData->settings.remoteApiKey; + jsonData["soc"] = liveData->params.socPerc; + jsonData["soh"] = liveData->params.sohPerc; + jsonData["batK"] = liveData->params.batPowerKw; + jsonData["batA"] = liveData->params.batPowerAmp; + jsonData["batV"] = liveData->params.batVoltage; + jsonData["auxV"] = liveData->params.auxVoltage; + jsonData["MinC"] = liveData->params.batMinC; + jsonData["MaxC"] = liveData->params.batMaxC; + jsonData["InlC"] = liveData->params.batInletC; + jsonData["fan"] = liveData->params.batFanStatus; + jsonData["cumCh"] = liveData->params.cumulativeEnergyChargedKWh; + jsonData["cumD"] = liveData->params.cumulativeEnergyDischargedKWh; + + char payload[200]; + serializeJson(jsonData, payload); + + Serial.print("Sending payload: "); + Serial.println(payload); + + Serial.print("Remote API server: "); + Serial.println(liveData->settings.remoteApiSrvr); + + uint16_t rc = sim800l->doPost(liveData->settings.remoteApiSrvr, "application/json", payload, 10000, 10000); + if(rc == 200) { + Serial.println(F("HTTP POST successful")); + } else { + // Failed... + Serial.print(F("HTTP POST error: ")); + Serial.println(rc); + } + + sim800l->disconnectGPRS(); + + return true; +} +#endif //SIM800L_ENABLED + +/** + Setup device +*/ +void setup(void) { + + // Serial console, init structures + Serial.begin(115200); + Serial.println(""); + Serial.println("Booting device..."); + + // Init settings/params, board library + liveData = new LiveData(); + liveData->initParams(); + +#ifdef BOARD_TTGO_T4 + board = new BoardTtgoT4v13(); +#endif // BOARD_TTGO_T4 +#ifdef BOARD_M5STACK_CORE + board = new BoardM5stackCore(); +#endif // BOARD_M5STACK_CORE + board->setLiveData(liveData); + board->loadSettings(); + board->initBoard(); + + // 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; + + // 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); + + // Hold right button + board->afterSetup(); + + #ifdef SD_ENABLED + // Init SDCARD + /*if (!SD.begin(SD_CS, SD_MOSI, SD_MISO, SD_SCLK)) { + Serial.println("SDCARD initialization failed!"); + } else { + Serial.println("SDCARD initialization done."); + } + /*spiSD.begin(SD_SCLK,SD_MISO,SD_MOSI,SD_CS); + if(!SD.begin( SD_CS, spiSD, 27000000)){ + Serial.println("SDCARD initialization failed!"); + } else { + Serial.println("SDCARD initialization done."); + }*/ + #endif + + // Start BLE connection + line = ""; + Serial.println("Start BLE with PIN auth"); + BLEDevice::init(""); + + // 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"); + 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 (!board->skipAdapterScan()) { + startBleScan(); + } + +#ifdef SIM800L_ENABLED + sim800lSetup(); +#endif //SIM800L_ENABLED + + // End + Serial.println("Device setup completed"); +} + +/** + Loop +*/ +void loop() { + + // Connect BLE device + if (liveData->bleConnect == true && liveData->foundMyBleDevice != NULL) { + liveData->pServerAddress = new BLEAddress(liveData->settings.obdMacAddress); + if (connectToServer(*liveData->pServerAddress)) { + + liveData->bleConnected = true; + liveData->bleConnect = false; + + Serial.println("We are now connected to the BLE device."); + + // Print message + board->displayMessage(" > Processing init AT cmds", ""); + + // Serve first command (ATZ) + doNextAtCommand(); + } else { + Serial.println("We have failed to connect to the server; there is nothing more we will do."); + } + } + + // Send command from TTY to OBD2 + if (liveData->bleConnected) { + if (Serial.available()) { + ch = Serial.read(); + line = line + ch; + if (ch == '\r' || ch == '\n') { + Serial.println(line); + liveData->pRemoteCharacteristicWrite->writeValue(line.c_str(), line.length()); + line = ""; + } + } + + // Can send next command from queue to OBD + if (liveData->canSendNextAtCommand) { + liveData->canSendNextAtCommand = false; + doNextAtCommand(); + } + } + +#ifdef SIM800L_ENABLED + if(liveData->params.lastDataSent + SIM800L_TIMER < liveData->params.currentTime && liveData->params.sim800l_enabled) { + sendDataViaGPRS(); + liveData->params.lastDataSent = liveData->params.currentTime; + } +#endif // SIM800L_ENABLED + + board->mainLoop(); + + // currentTime & 1ms delay + struct tm now; + getLocalTime(&now, 0); + liveData->params.currentTime = mktime(&now); + // Shutdown when car is off + 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 011b2d4..7340014 100644 --- a/menu.h +++ b/menu.h @@ -1,20 +1,8 @@ -// 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; -#define menuItemsCount 79 -bool menuVisible = false; -uint16_t menuCurrent = 0; -uint8_t menuItemSelected = 0; -uint8_t menuItemOffset = 0; -uint16_t scanningDeviceIndex = 0; -MENU_ITEM menuItems[menuItemsCount] = { + +#include "config.h"; + +MENU_ITEM menuItemsSource[78] = { {0, 0, 0, "<- exit menu"}, {1, 0, -1, "Vehicle type"}, @@ -32,7 +20,7 @@ MENU_ITEM menuItems[menuItemsCount] = { {103, 1, -1, "Hyundai Ioniq 2018 28kWh"}, {104, 1, -1, "Kia eNiro 2020 39kWh"}, {105, 1, -1, "Hyundai Kona 2020 39kWh"}, - {106, 1, -1, "Renault Zoe 22kWh (DEV)"}, + //{106, 1, -1, "Renault Zoe 22kWh (DEV)"}, {107, 1, -1, "Debug OBD2 Kia"}, {300, 3, 0, "<- parent menu"},