From c1d8cc8885095feffa91507e221ed79c232b8043 Mon Sep 17 00:00:00 2001 From: Lubos Petrovic Date: Sun, 12 Apr 2020 21:30:26 +0200 Subject: [PATCH] version 1.1 - new screens - blank/lcd off, speed+kwh/100km, battery cells, charging graph --- README.md | 42 +-- enirodashboard.ino | 706 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 605 insertions(+), 143 deletions(-) diff --git a/README.md b/README.md index bb20c0c..ad95e31 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# KIA ENIRO DASHBOARD v1.0 +# KIA ENIRO DASHBOARD -OBDII dashboard for TTGO-T4 module (ESP32) + OBD BLE4.0 adapter. Programmed for KIA ENIRO 2020, but could work on Hyundai Kona EV. Contact me if not. +OBDII dashboard for TTGO-T4 module (ESP32) + OBD BLE4.0 adapter. Developed for my KIA ENIRO 2020, but could work on Hyundai Kona EV. Contact me if not. Author: nick.n17@gmail.com (Lubos Petrovic) @@ -17,18 +17,26 @@ Author: nick.n17@gmail.com (Lubos Petrovic) Thank you for supporting me. ![image](https://github.com/nickn17/enirodashboard/blob/master/screenshots/v1.jpg) -[![Watch the video](https://github.com/nickn17/enirodashboard/blob/master/screenshots/v0.9.jpg)](https://www.youtube.com/watch?v=shpCb4CsNHg) -## Roadmap -- connect to BLE function and deploy HEX file for common users (easy installation) -- 2.screen with eNiro battery cells and temperature sensors -- 3.screen with charging graph -- ext.relay control to power on dashboard, sleep CPU to save AUX during parking -- etc. +[![Watch the video](https://github.com/nickn17/enirodashboard/blob/master/screenshots/v0.9.jpg)](https://www.youtube.com/watch?v=Jg5VP2P58Yg&) + +## Hardware and software +- TTGO-T4. I used this from banggood (T4 v1.3) ~ USD $30 https://www.banggood.com/LILYGO-TTGO-T-Watcher-BTC-Ticker-ESP32-For-Bitcoin-Price-Program-4M-SPI-Flash-Psram-LCD-Display-Module-p-1345292.html +- OBD BLE4.0 adapter. Ex. Vgate iCar Pro Bluetooth 4.0 (BLE) OBD2 ~ USD $30 +- Software is written for Arduino IDE (ESP32). ## Release notes -### v1.0 +### 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 @@ -40,12 +48,13 @@ https://github.com/fdufnews/ESP32-TTGO-T4 - install arduino IDE + ESP32 support - https://github.com/Bodmer/TFT_eSPI - display library - Configure TFT eSPI - You need to do some user setup in library folder (Adruino/library/tft/espi/userSetup..) - see TFT eSPI readme or google it. Settings for TFT_eSPI library - userSetup required for T4 v1.3 + 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 ``` - #define TFT_DC 32 // v1.3 has DC on 32 port - #define TFT_BL 4 // Backlight port - required (otherwise you obtain black screen) - #define TFT_BACKLIGHT_ON HIGH // Backlight ON - required -``` My configuration - Board ESP32 Dev module @@ -57,6 +66,3 @@ My configuration - Partion scheme: default 4MB with spiffs - Core debug level: none - PSRAM: disable - - - diff --git a/enirodashboard.ino b/enirodashboard.ino index 1688d34..e751dd5 100644 --- a/enirodashboard.ino +++ b/enirodashboard.ino @@ -1,8 +1,10 @@ /* - KIA eNiro Dashboard + KIA eNiro Dashboard 1.01, 2020-04-12 + working only with OBD BLE 4.0 adapters ex. Vgate ICar Pro (BLE4.0 version) + + IMPORTANT Replace HM_MAC, serviceUUID, charTxUUID, charRxUUID as described below - !! IMPORTANT Replace HM_MAC, serviceUUID, charTxUUID, charRxUUID as described below !! How to obtain MAC + 3x UUID? (I want to add pairing via buttons later) Run Android BLE scanner @@ -18,14 +20,6 @@ static BLEUUID serviceUUID("000018f0-0000-1000-8000-00805f9b34fb"); static BLEUUID charTxUUID("00002af0-0000-1000-8000-00805f9b34fb"); static BLEUUID charRxUUID("00002af1-0000-1000-8000-00805f9b34fb"); - - -- - Display il9341 is using TFT_eSPI - You need to do some user setup in library folder (Adruino/library/tft/espi/userSetup.. - Settings for TFT_eSPI library - userSetup required for T4 v1.3 - #define TFT_DC 32 // v1.3 has DC on 32 port - #define TFT_BL 4 // Backlight port - required (otherwise you got black screen) - #define TFT_BACKLIGHT_ON HIGH // Backlight ON - required */ #include "SPI.h" @@ -34,10 +28,11 @@ // PLEASE CHANGE THIS SETTING for your BLE4 uint32_t PIN = 1234; -#define HM_MAC "dd:0d:30:50:ed:63" // mac ios-vlink cez nRf connect -static BLEUUID serviceUUID("000018f0-0000-1000-8000-00805f9b34fb"); // nRf connect to ios.vlink / client / dblclick on unknown service - this is service UUID -static BLEUUID charTxUUID("00002af0-0000-1000-8000-00805f9b34fb"); // UUID from NOTIFY section (one of custom characteristics under unknown service) -static BLEUUID charRxUUID("00002af1-0000-1000-8000-00805f9b34fb"); // UUID from WRITE section (one of custom characteristics under unknown service) +// Temporary moved to initSettings(). Preparing to load/save settings from flash memory +//#define HM_MAC "dd:0d:30:50:ed:63" // mac ios-vlink cez nRf connect +//static BLEUUID serviceUUID("000018f0-0000-1000-8000-00805f9b34fb"); // nRf connect to ios.vlink / client / dblclick on unknown service - this is service UUID +//static BLEUUID charTxUUID("00002af0-0000-1000-8000-00805f9b34fb"); // UUID from NOTIFY section (one of custom characteristics under unknown service) +//static BLEUUID charRxUUID("00002af1-0000-1000-8000-00805f9b34fb"); // UUID from WRITE section (one of custom characteristics under unknown service) /////////////////////////////////////////////// // LILYGO TTGO T4 v1.3 BUTTONS @@ -67,7 +62,9 @@ static boolean bleConnected = false; static BLEAddress *pServerAddress; static BLERemoteCharacteristic* pRemoteCharacteristic; static BLERemoteCharacteristic* pRemoteCharacteristicWrite; -BLEClient* pClient; +BLEAdvertisedDevice* foundMyBleDevice; +BLEClient* pClient; +BLEScan* pBLEScan; // Temporary variables char ch; @@ -78,19 +75,19 @@ char tmpStr3[20]; char tmpStr4[20]; // Main -byte displayScreen = 0; // 0 - bash board, 1 - battery cells +#define displayScreenCount 4 +byte displayScreen = 1; // 0 - blank screen, 1 - dash board (default), 2 - big speed + kwh/100, 3 - battery cells, 4 - charging graph bool btnLeftPressed = true; bool btnMiddlePressed = true; bool btnRightPressed = true; - // Commands loop #define commandQueueCount 23 -#define commandQueueLoopFrom 6 +#define commandQueueLoopFrom 7 String responseRow; String responseRowMerged; byte commandQueueIndex; -bool couldSendNextAtCommand = false; +bool canSendNextAtCommand = false; String commandRequest = ""; String commandQueue[commandQueueCount] = { "AT Z", // Reset all @@ -108,23 +105,55 @@ String commandQueue[commandQueueCount] = { "atst16", // Loop from (KIA ENIRO) - "atsh7e4", - "220101", // power kw, ... - //"220102", // cell voltages - //"220103", // cell voltages - //"220104", // cell voltages - "220105", // soh, soc, .. + "atsh7e4", // BMS + "220101", // power kw, ... + "220102", // cell voltages, screen 3 only + "220103", // cell voltages, screen 3 only + "220104", // cell voltages, screen 3 only + "220105", // soh, soc, .. //"220106", - "atsh7e2", - "2101", // speed, ... - "2102", // aux, ... - "atsh7df", + + "atsh7e2", // VMCU + "2101", // speed, ... + "2102", // aux, ... + + //"atsh7df", //"2106", //"220106", - "atsh7b3", - "220100", // in/out temp - "atsh7a0", - "22c00b", // tire pressure/temp + + "atsh7b3", + "220100", // in/out temp + + "atsh7a0", // TMPS? + "22c00b", // tire pressure/temp +}; + +// Menu id/parent/title +typedef struct { + uint8_t lang; + char* sound; + char* value; +} menuItem; + +String menu[] = { + "1/0/Vehicle type", + "100/1/Kia eNiro", + "101/1/Hyundai Kona EV", + "2/0/Pair OBD2 BLE adapter", + "3/0/Other", + "30/3/Screen rotation", + "300/30/Normal", + "301/30/Flip vertical", + "4/0/Units", + "40/4/Distance", + "400/40/Kilometers", + "401/40/Miles", + "41/4/Temperature", + "410/41/Celsius", + "411/41/Fahrenheit", + "42/4/Pressure", + "420/42/Bar", + "421/42/Psi", }; // Structure with realtime values @@ -140,10 +169,17 @@ struct strucParams { float batPowerKw; float batPowerKwh100; float batVoltage; - float batCellMin; - float batCellMax; + float batCellMinV; + float batCellMaxV; float batTempC; float batHeaterC; + float batInletC; + float batMinC; + float batMaxC; + float batModule01TempC; + float batModule02TempC; + float batModule03TempC; + float batModule04TempC; float auxPerc; float auxCurrentAmp; float auxVoltage; @@ -157,10 +193,46 @@ struct strucParams { float tireRearLeftPressureBar; float tireRearRightTempC; float tireRearRightPressureBar; + float cellVoltage[98]; // 1..98 has index 0..97 + float chargingGraphKw[101]; // 0..100% .. how many HW in each step + float chargingGraphMinTempC[101]; // 0..100% .. Min temp in.C + float chargingGraphMaxTempC[101]; // 0..100% .. Max temp in.C }; -strucParams params; // Current -strucParams oldParams; // Old states used for redraw changed values only +// Setting stored to flash +struct strucSettings { + byte initFlag; // 183 value + byte settingsVersion; // 1 + char obdMacAddress[20]; + char serviceUUID[40]; + char charTxUUID[40]; + char charRxUUID[40]; +}; + +strucParams params; // Realtime sensor values +strucParams oldParams; // Old states used for change detection (draw optimization) +strucSettings settings; // Settings stored into flash + +/** + Init settings +*/ +bool initSettings() { + + String tmpStr; + + settings.initFlag = 183; + settings.settingsVersion = 1; + tmpStr = "dd:0d:30:50:ed:63"; + tmpStr.toCharArray(settings.obdMacAddress, 18); + tmpStr = "000018f0-0000-1000-8000-00805f9b34fb"; + tmpStr.toCharArray(settings.serviceUUID, 37); + tmpStr = "00002af0-0000-1000-8000-00805f9b34fb"; + tmpStr.toCharArray(settings.charTxUUID, 37); + tmpStr = "00002af1-0000-1000-8000-00805f9b34fb"; + tmpStr.toCharArray(settings.charRxUUID, 37); + + return true; +} /** Init structure with data @@ -178,10 +250,17 @@ bool initStructure() { params.batPowerKw = -1; params.batPowerKwh100 = -1; params.batVoltage = -1; - params.batCellMin = -1; - params.batCellMax = -1; + params.batCellMinV = -1; + params.batCellMaxV = -1; params.batTempC = -1; params.batHeaterC = -1; + params.batInletC = -1; + params.batMinC = -1; + params.batMaxC = -1; + params.batModule01TempC = -1; + params.batModule02TempC = -1; + params.batModule03TempC = -1; + params.batModule04TempC = -1; params.auxPerc = -1; params.auxCurrentAmp = -1; params.auxVoltage = -1; @@ -195,6 +274,15 @@ bool initStructure() { params.tireRearLeftPressureBar = -1; params.tireRearRightTempC = -1; params.tireRearRightPressureBar = -1; + for (int i = 0; i < 98; i++) { + params.cellVoltage[i] = 0; + } + for (int i = 0; i <= 100; i++) { + params.chargingGraphKw[i] = 0; + params.chargingGraphMinTempC[i] = -100; + params.chargingGraphMaxTempC[i] = -100; + } + oldParams = params; return true; @@ -232,18 +320,18 @@ float hexToDec(String hexString, byte bytes = 2, bool signedNum = true) { /** Draw cell on dashboard */ -bool monitoringRect(int32_t x, int32_t y, int32_t w, int32_t h, const char* text, const char* desc, int16_t color) { +bool monitoringRect(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 * 60) + 1; - tft.fillRect(x * 80, y * 60, ((w) * 80) - 1, ((h) * 60) - 1, color); + tft.fillRect(x * 80, y * 60, ((w) * 80) - 1, ((h) * 60) - 1, bgColor); tft.drawFastVLine(((x + w) * 80) - 1, ((y) * 60) - 1, h * 60, TFT_BLACK); tft.drawFastHLine(((x) * 80) - 1, ((y + h) * 60) - 1, w * 80, TFT_BLACK); tft.setTextDatum(TL_DATUM); // Topleft - tft.setTextColor(TFT_SILVER, color); // Bk, fg color + tft.setTextColor(TFT_SILVER, bgColor); // Bk, fg color tft.setTextSize(1); // Size for small 5x7 font tft.drawString(desc, posx, posy, 2); @@ -265,7 +353,7 @@ bool monitoringRect(int32_t x, int32_t y, int32_t w, int32_t h, const char* text // Main number - kwh on roads, amps on charges posy = (y * 60) + 24; - tft.setTextColor(TFT_WHITE, color); + tft.setTextColor(fgColor, bgColor); tft.setFreeFont(&Orbitron_Light_32); tft.drawString(text, posx, posy, 7); @@ -273,7 +361,7 @@ bool monitoringRect(int32_t x, int32_t y, int32_t w, int32_t h, const char* text // All others 1x1 cells tft.setTextDatum(MC_DATUM); - tft.setTextColor(TFT_WHITE, color); + tft.setTextColor(fgColor, bgColor); tft.setFreeFont(&Orbitron_Light_24); posx = (x * 80) + (w * 80 / 2) - 3; posy = (y * 60) + (h * 60 / 2) + 4; @@ -283,6 +371,32 @@ bool monitoringRect(int32_t x, int32_t y, int32_t w, int32_t h, const char* text return true; } +/** + Draw small rect 80x30 +*/ +bool drawSmallRect(int32_t x, int32_t y, int32_t w, int32_t h, const char* text, const char* desc, int16_t bgColor, int16_t fgColor) { + + int32_t posx, posy; + + posx = (x * 80) + 4; + posy = (y * 32) + 1; + + tft.fillRect(x * 80, y * 32, ((w) * 80), ((h) * 32), bgColor); + tft.drawFastVLine(((x + w) * 80) - 1, ((y) * 32) - 1, h * 32, TFT_BLACK); + tft.drawFastHLine(((x) * 80) - 1, ((y + h) * 32) - 1, w * 80, TFT_BLACK); + tft.setTextDatum(TL_DATUM); // Topleft + tft.setTextColor(TFT_SILVER, bgColor); // Bk, fg bgColor + tft.setTextSize(1); // Size for small 5x7 font + tft.drawString(desc, posx, posy, 2); + + tft.setTextDatum(TC_DATUM); + tft.setTextColor(fgColor, bgColor); + posx = (x * 80) + (w * 80 / 2) - 3; + tft.drawString(text, posx, posy + 14, 2); + + return true; +} + /** Show tire pressures / temperatures Custom field @@ -357,18 +471,18 @@ bool drawSceneMain(bool force) { oldParams.cumulativeEnergyDischargedKWh = params.cumulativeEnergyDischargedKWh; } - // batPowerKwh100 on roads - if (params.speedKmh > 10) { + // batPowerKwh100 on roads, else batPowerAmp + if (params.speedKmh > 20) { if (force || params.batPowerKwh100 != oldParams.batPowerKwh100) { sprintf(tmpStr1, "%01.01f", params.batPowerKwh100); - monitoringRect(1, 1, 2, 2, tmpStr1, "KWH/100KM", (params.batPowerKw >= 0 ? TFT_DARKGREEN2 : (params.batPowerKw < -16.0 ? TFT_RED : TFT_DARKRED))); + monitoringRect(1, 1, 2, 2, tmpStr1, "KWH/100KM", (params.batPowerKwh100 >= 0 ? TFT_DARKGREEN2 : (params.batPowerKwh100 < -30.0 ? TFT_RED : TFT_DARKRED)), TFT_WHITE); oldParams.speedKmh = params.batPowerKwh100; } } else { // batPowerAmp on chargers (under 10kmh) if (force || params.batPowerAmp != oldParams.batPowerAmp) { sprintf(tmpStr1, (abs(params.batPowerAmp) > 9.9 ? "%01.00f" : "%01.01f"), params.batPowerAmp); - monitoringRect(1, 1, 2, 2, tmpStr1, "BATTERY POWER [A]", (params.batPowerAmp >= 0 ? TFT_DARKGREEN2 : TFT_DARKRED)); + monitoringRect(1, 1, 2, 2, tmpStr1, "BATTERY POWER [A]", (params.batPowerAmp >= 0 ? TFT_DARKGREEN2 : TFT_DARKRED), TFT_WHITE); oldParams.batPowerAmp = params.batPowerAmp; } } @@ -377,7 +491,7 @@ bool drawSceneMain(bool force) { if (force || params.socPerc != oldParams.socPerc) { sprintf(tmpStr1, "%01.00f%%", params.socPerc); sprintf(tmpStr2, (params.sohPerc == 100.0 ? "SOC/H%01.00f%%" : "SOC/H%01.01f%%"), params.sohPerc); - monitoringRect(0, 0, 1, 1, tmpStr1, tmpStr2, (params.socPerc < 10 || params.sohPerc < 100 ? TFT_RED : (params.socPerc > 80 ? TFT_DARKGREEN2 : TFT_DEFAULT_BK))); + monitoringRect(0, 0, 1, 1, tmpStr1, tmpStr2, (params.socPerc < 10 || params.sohPerc < 100 ? TFT_RED : (params.socPerc > 80 ? TFT_DARKGREEN2 : TFT_DEFAULT_BK)), TFT_WHITE); oldParams.socPerc = params.socPerc; oldParams.sohPerc = params.sohPerc; } @@ -385,58 +499,58 @@ bool drawSceneMain(bool force) { // batPowerAmp if (force || params.batPowerKw != oldParams.batPowerKw) { sprintf(tmpStr1, "%01.01f", params.batPowerKw); - monitoringRect(0, 1, 1, 1, tmpStr1, "POWER KW", (params.batPowerKw >= 0 ? TFT_DARKGREEN2 : (params.batPowerKw <= -30 ? TFT_RED : TFT_DARKRED))); + monitoringRect(0, 1, 1, 1, tmpStr1, "POWER KW", (params.batPowerKw >= 0 ? TFT_DARKGREEN2 : (params.batPowerKw <= -30 ? TFT_RED : TFT_DARKRED)), TFT_WHITE); oldParams.batPowerKw = params.batPowerKw; } // batVoltage if (force || params.batVoltage != oldParams.batVoltage) { sprintf(tmpStr1, "%03.00f", params.batVoltage); - monitoringRect(0, 2, 1, 1, tmpStr1, "VOLTAGE", TFT_DEFAULT_BK); + monitoringRect(0, 2, 1, 1, tmpStr1, "VOLTAGE", TFT_DEFAULT_BK, TFT_WHITE); oldParams.batVoltage = params.batVoltage; } - // batCellMin - if (force || params.batCellMin != oldParams.batCellMin || params.batCellMax != oldParams.batCellMax) { - sprintf(tmpStr1, "%01.02f", params.batCellMax - params.batCellMin); - sprintf(tmpStr2, "CELLS %01.02f", params.batCellMin); - monitoringRect(0, 3, 1, 1, ( params.batCellMax - params.batCellMin == 0.00 ? "OK" : tmpStr1), tmpStr2, TFT_DEFAULT_BK); - oldParams.batCellMax = params.batCellMax; - oldParams.batCellMin = params.batCellMin; + // batCellMinV + if (force || params.batCellMinV != oldParams.batCellMinV || params.batCellMaxV != oldParams.batCellMaxV) { + sprintf(tmpStr1, "%01.02f", params.batCellMaxV - params.batCellMinV); + sprintf(tmpStr2, "CELLS %01.02f", params.batCellMinV); + monitoringRect(0, 3, 1, 1, ( params.batCellMaxV - params.batCellMinV == 0.00 ? "OK" : tmpStr1), tmpStr2, TFT_DEFAULT_BK, TFT_WHITE); + oldParams.batCellMaxV = params.batCellMaxV; + oldParams.batCellMinV = params.batCellMinV; } // batTempC if (force || params.batTempC != oldParams.batTempC) { - sprintf(tmpStr1, "%01.00fC", params.batTempC); - monitoringRect(1, 3, 1, 1, tmpStr1, "BAT.TEMP", TFT_TEMP); + sprintf(tmpStr1, "%01.00f", params.batTempC); + monitoringRect(1, 3, 1, 1, tmpStr1, "BAT.TEMP.C", TFT_TEMP, (params.batTempC >= 15) ? ((params.batTempC >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED); oldParams.batTempC = params.batTempC; } // batHeaterC if (force || params.batHeaterC != oldParams.batHeaterC) { - sprintf(tmpStr1, "%01.00fC", params.batHeaterC); - monitoringRect(2, 3, 1, 1, tmpStr1, "BAT.HEAT", TFT_TEMP); + sprintf(tmpStr1, "%01.00f", params.batHeaterC); + monitoringRect(2, 3, 1, 1, tmpStr1, "BAT.HEAT C", TFT_TEMP, TFT_WHITE); oldParams.batHeaterC = params.batHeaterC; } // Aux perc if (force || params.auxPerc != oldParams.auxPerc) { sprintf(tmpStr1, "%01.00f%%", params.auxPerc); - monitoringRect(3, 0, 1, 1, tmpStr1, "AUX BAT.", (params.auxPerc < 60 ? TFT_RED : TFT_DEFAULT_BK)); + monitoringRect(3, 0, 1, 1, tmpStr1, "AUX BAT.", (params.auxPerc < 60 ? TFT_RED : TFT_DEFAULT_BK), TFT_WHITE); oldParams.auxPerc = params.auxPerc; } // Aux amp if (force || params.auxCurrentAmp != oldParams.auxCurrentAmp) { sprintf(tmpStr1, (abs(params.auxCurrentAmp) > 9.9 ? "%01.00f" : "%01.01f"), params.auxCurrentAmp); - monitoringRect(3, 1, 1, 1, tmpStr1, "AUX AMPS", (params.auxCurrentAmp >= 0 ? TFT_DARKGREEN2 : TFT_DARKRED)); + monitoringRect(3, 1, 1, 1, tmpStr1, "AUX AMPS", (params.auxCurrentAmp >= 0 ? TFT_DARKGREEN2 : TFT_DARKRED), TFT_WHITE); oldParams.auxCurrentAmp = params.auxCurrentAmp; } // auxVoltage if (force || params.auxVoltage != oldParams.auxVoltage) { sprintf(tmpStr1, "%01.01f", params.auxVoltage); - monitoringRect(3, 2, 1, 1, tmpStr1, "AUX VOLTS", (params.auxVoltage < 12.1 ? TFT_RED : (params.auxVoltage < 12.6 ? TFT_ORANGE : TFT_DEFAULT_BK))); + monitoringRect(3, 2, 1, 1, tmpStr1, "AUX VOLTS", (params.auxVoltage < 12.1 ? TFT_RED : (params.auxVoltage < 12.6 ? TFT_ORANGE : TFT_DEFAULT_BK)), TFT_WHITE); oldParams.auxVoltage = params.auxVoltage; } @@ -444,7 +558,7 @@ bool drawSceneMain(bool force) { if (force || params.indoorTemperature != oldParams.indoorTemperature || params.outdoorTemperature != oldParams.outdoorTemperature) { sprintf(tmpStr1, "%01.01f", params.indoorTemperature); sprintf(tmpStr2, "IN/OUT%01.01fC", params.outdoorTemperature); - monitoringRect(3, 3, 1, 1, tmpStr1, tmpStr2, TFT_TEMP); + monitoringRect(3, 3, 1, 1, tmpStr1, tmpStr2, TFT_TEMP, TFT_WHITE); oldParams.indoorTemperature = params.indoorTemperature; oldParams.outdoorTemperature = params.outdoorTemperature; } @@ -452,6 +566,195 @@ bool drawSceneMain(bool force) { return true; } +/** + Speed + kwh/100km (Screen 1) +*/ +bool drawSceneSpeed(bool force) { + + int32_t posx, posy; + + posx = 320 / 2; + posy = 32; + tft.setTextDatum(TC_DATUM); // Top center + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextSize(2); // Size for small 5x7 font + sprintf(tmpStr3, " 0 "); + if (params.speedKmh > 10) + sprintf(tmpStr3, " %01.00f ", params.speedKmh); + tft.drawString(tmpStr3, posx, posy, 7); + + posy = 140; + tft.setTextDatum(TC_DATUM); // Top center + tft.setTextSize(1); + if (params.speedKmh > 25 && params.batPowerKw > 0) { + sprintf(tmpStr3, " %01.01f ", params.batPowerKwh100); + } else { + sprintf(tmpStr3, " %01.01f ", params.batPowerKw); + } + tft.drawString(tmpStr3, posx, posy, 7); + + // Bottom 2 numbers with charged/discharged kWh from start + posx = 10; + posy = 240 - 10; + sprintf(tmpStr3, "-%01.01f ", params.cumulativeEnergyDischargedKWh - params.cumulativeEnergyDischargedKWhStart); + tft.setFreeFont(&Roboto_Thin_24); + tft.setTextDatum(BL_DATUM); + tft.drawString(tmpStr3, posx, posy, GFXFF); + + posx = 320 - 10; + sprintf(tmpStr3, " +%01.01f", params.cumulativeEnergyChargedKWh - params.cumulativeEnergyChargedKWhStart); + tft.setTextDatum(BR_DATUM); + tft.drawString(tmpStr3, posx, posy, GFXFF); + + posx = 320 / 2; + sprintf(tmpStr3, " %01.01fkw ", params.batPowerKw); + tft.setTextDatum(BC_DATUM); + tft.drawString(tmpStr3, posx, posy, GFXFF); + + // Battery "cold gate" detection - red < 15C (43KW limit), <25 (blue - 55kW limit), green all ok + sprintf(tmpStr3, "%01.00f", params.batTempC); + tft.fillCircle(290, 30, 25, (params.batTempC >= 15) ? ((params.batTempC >= 25) ? TFT_DARKGREEN2 : TFT_BLUE) : TFT_RED); + tft.setTextColor(TFT_WHITE, (params.batTempC >= 15) ? ((params.batTempC >= 25) ? TFT_DARKGREEN2 : TFT_BLUE) : TFT_RED); + tft.setFreeFont(&Roboto_Thin_24); + tft.setTextDatum(MC_DATUM); + tft.drawString(tmpStr3, 290, 30, GFXFF); + + return true; +} + +/** + Battery cells (Screen 2) +*/ +bool drawSceneBatteryCells(bool force) { + + int32_t posx, posy; + + sprintf(tmpStr1, "%01.00f C", params.batHeaterC); + drawSmallRect(0, 0, 1, 1, tmpStr1, "HEATER", TFT_TEMP, TFT_CYAN); + sprintf(tmpStr1, "%01.00f C", params.batInletC); + drawSmallRect(1, 0, 1, 1, tmpStr1, "BAT.INLET", TFT_TEMP, TFT_CYAN); + /*Not needed yet + sprintf(tmpStr1, "%01.00fC", params.batMinC); + drawSmallRect(2, 0, 1, 1, tmpStr1, "BAT.MIN", TFT_TEMP, TFT_CYAN); + sprintf(tmpStr1, "%01.00fC", params.batMaxC); + drawSmallRect(3, 0, 1, 1, tmpStr1, "BAT.MAX", TFT_TEMP, TFT_CYAN);*/ + + sprintf(tmpStr1, "%01.00f C", params.batModule01TempC); + drawSmallRect(0, 1, 1, 1, tmpStr1, "MO1", TFT_TEMP, (params.batModule01TempC >= 15) ? ((params.batModule01TempC >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED); + sprintf(tmpStr1, "%01.00f C", params.batModule02TempC); + drawSmallRect(1, 1, 1, 1, tmpStr1, "MO2", TFT_TEMP, (params.batModule02TempC >= 15) ? ((params.batModule02TempC >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED); + sprintf(tmpStr1, "%01.00f C", params.batModule03TempC); + drawSmallRect(2, 1, 1, 1, tmpStr1, "MO3", TFT_TEMP, (params.batModule03TempC >= 15) ? ((params.batModule03TempC >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED); + sprintf(tmpStr1, "%01.00f C", params.batModule04TempC); + drawSmallRect(3, 1, 1, 1, tmpStr1, "MO4", TFT_TEMP, (params.batModule04TempC >= 15) ? ((params.batModule04TempC >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED); + + tft.setTextDatum(TL_DATUM); // Topleft + tft.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 (params.cellVoltage[i] < minVal || minVal == -1) + minVal = params.cellVoltage[i]; + if (params.cellVoltage[i] > maxVal || maxVal == -1) + maxVal = params.cellVoltage[i]; + } + + // Draw cell matrix + for (int i = 0; i < 98; i++) { + posx = ((i % 8) * 40) + 4; + posy = ((floor(i / 8)) * 13) + 68; + sprintf(tmpStr3, "%01.02f", params.cellVoltage[i]); + tft.setTextColor(TFT_NAVY, TFT_BLACK); + if (params.cellVoltage[i] == minVal && minVal != maxVal) + tft.setTextColor(TFT_RED, TFT_BLACK); + if (params.cellVoltage[i] == maxVal && minVal != maxVal) + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.drawString(tmpStr3, posx, posy, 2); + } + + return true; +} + +/** + Charging graph (Screen 3) +*/ +bool drawSceneChargingGraph(bool force) { + + int zeroX = 0; + int zeroY = 238; + int mulX = 3; // 100% = 300px; + int mulY = 2; // 100kW = 200px + int maxKw = 80; + int16_t color; + + sprintf(tmpStr1, "%01.00f", params.socPerc); + drawSmallRect(0, 0, 1, 1, tmpStr1, "SOC", TFT_TEMP, TFT_CYAN); + sprintf(tmpStr1, "%01.01f", params.batPowerKw); + drawSmallRect(1, 0, 1, 1, tmpStr1, "POWER kW", TFT_TEMP, TFT_CYAN); + sprintf(tmpStr1, "%01.01f", params.batPowerAmp); + drawSmallRect(2, 0, 1, 1, tmpStr1, "POWER A", TFT_TEMP, TFT_CYAN); + sprintf(tmpStr1, "%03.00f", params.batVoltage); + drawSmallRect(3, 0, 1, 1, tmpStr1, "VOLTAGE", TFT_TEMP, TFT_CYAN); + + sprintf(tmpStr1, "%01.00f C", params.batHeaterC); + drawSmallRect(0, 1, 1, 1, tmpStr1, "HEATER", TFT_TEMP, TFT_CYAN); + sprintf(tmpStr1, "%01.00f C", params.batInletC); + drawSmallRect(1, 1, 1, 1, tmpStr1, "BAT.INLET", TFT_TEMP, TFT_CYAN); + sprintf(tmpStr1, "%01.00f C", params.batMinC); + drawSmallRect(2, 1, 1, 1, tmpStr1, "BAT.MIN", (params.batMinC >= 15) ? ((params.batMinC >= 25) ? TFT_DARKGREEN2 : TFT_BLUE) : TFT_RED, TFT_CYAN); + sprintf(tmpStr1, "%01.00f C", params.batMaxC); + drawSmallRect(3, 1, 1, 1, tmpStr1, "BAT.MAX", TFT_TEMP, TFT_CYAN); + + tft.setTextSize(1); // Size for small 5x7 font + tft.setTextDatum(TR_DATUM); + sprintf(tmpStr1, "%01.00fC", params.batModule01TempC); + tft.setTextColor((params.batModule01TempC >= 15) ? ((params.batModule01TempC >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED, TFT_TEMP); + tft.drawString(tmpStr1, zeroX + (10 * 10 * mulX), zeroY - (maxKw * mulY) + 00, 2); + sprintf(tmpStr1, "%01.00fC", params.batModule02TempC); + tft.setTextColor((params.batModule02TempC >= 15) ? ((params.batModule02TempC >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED, TFT_TEMP); + tft.drawString(tmpStr1, zeroX + (10 * 10 * mulX), zeroY - (maxKw * mulY) + 20, 2); + sprintf(tmpStr1, "%01.00fC", params.batModule03TempC); + tft.setTextColor((params.batModule03TempC >= 15) ? ((params.batModule03TempC >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED, TFT_TEMP); + tft.drawString(tmpStr1, zeroX + (10 * 10 * mulX), zeroY - (maxKw * mulY) + 40, 2); + sprintf(tmpStr1, "%01.00fC", params.batModule04TempC); + tft.setTextColor((params.batModule04TempC >= 15) ? ((params.batModule04TempC >= 25) ? TFT_GREEN : TFT_BLUE) : TFT_RED, TFT_TEMP); + tft.drawString(tmpStr1, zeroX + (10 * 10 * mulX), zeroY - (maxKw * mulY) + 60, 2); + tft.setTextColor(TFT_SILVER, TFT_TEMP); + + + for (int i = 0; i <= 10; i++) { + color = TFT_DARKRED; + if (i == 0 || i == 5 || i == 10) + color = TFT_NAVY; + tft.drawFastVLine(zeroX + (i * 10 * mulX), zeroY - (maxKw * mulY), maxKw * mulY, color); + if (i != 0 && i != 10) { + sprintf(tmpStr1, "%d%%", i*10); + tft.setTextDatum(BC_DATUM); + tft.drawString(tmpStr1, zeroX + (i * 10 * mulX), zeroY - (maxKw * mulY), 2); + } + if (i <= (maxKw / 10)) { + tft.drawFastHLine(zeroX, zeroY - (i * 10 * mulY), 100 * mulX, color); + if (i > 0) { + sprintf(tmpStr1, "%d", i*10); + tft.setTextDatum(ML_DATUM); + tft.drawString(tmpStr1, zeroX + (100 * mulX)+ 3, zeroY - (i * 10 * mulY), 2); + } + } + } + + for (int i = 0; i <= 100; i++) { + if (params.chargingGraphKw[i] > 0) + tft.drawFastHLine(zeroX + (i * mulX) - (mulX / 2), zeroY - (params.chargingGraphKw[i]*mulY), mulX, TFT_YELLOW); + if (params.chargingGraphMinTempC[i] > -10) + tft.drawFastHLine(zeroX + (i * mulX) - (mulX / 2), zeroY - (params.chargingGraphMinTempC[i]*mulY), mulX, TFT_RED); + if (params.chargingGraphMaxTempC[i] > -10) + tft.drawFastHLine(zeroX + (i * mulX) - (mulX / 2), zeroY - (params.chargingGraphMaxTempC[i]*mulY), mulX, TFT_BLUE); + } + + return true; +} + /** Redraw screen */ @@ -462,14 +765,21 @@ bool redrawScreen(bool force) { tft.fillScreen(TFT_BLACK); } - // Main screen - if (displayScreen == 0) { + // 1. Main screen + if (displayScreen == 1) { drawSceneMain(force); } - - // Battery cells - if (displayScreen == 1) { - // UNDER CONSTRUCTION + // 2. Big speed + kwh/100km + if (displayScreen == 2) { + drawSceneSpeed(force); + } + // 3. Battery cells + if (displayScreen == 3) { + drawSceneBatteryCells(force); + } + // Charging graph + if (displayScreen == 4) { + drawSceneChargingGraph(force); } return true; @@ -549,14 +859,56 @@ bool parseRowMerged() { params.batVoltage = hexToDec(responseRowMerged.substring(30, 34).c_str(), 2, false) / 10.0; // === OK Valid params.batPowerKw = (params.batPowerAmp * params.batVoltage) / 1000.0; params.batPowerKwh100 = params.batPowerKw / params.speedKmh * 100; - params.batCellMax = hexToDec(responseRowMerged.substring(52, 54).c_str(), 1, false) / 50.0; // === OK Valid - params.batCellMin = hexToDec(responseRowMerged.substring(56, 58).c_str(), 1, false) / 50.0; // === OK Valid - params.batTempC = hexToDec(responseRowMerged.substring(36, 38).c_str(), 1, true); // === OK Valid + params.batCellMaxV = hexToDec(responseRowMerged.substring(52, 54).c_str(), 1, false) / 50.0; // === OK Valid + params.batCellMinV = hexToDec(responseRowMerged.substring(56, 58).c_str(), 1, false) / 50.0; // === OK Valid + params.batModule01TempC = hexToDec(responseRowMerged.substring(38, 40).c_str(), 1, true); + params.batModule02TempC = hexToDec(responseRowMerged.substring(40, 42).c_str(), 1, true); + params.batModule03TempC = hexToDec(responseRowMerged.substring(42, 44).c_str(), 1, true); + params.batModule04TempC = 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.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.batInletC = hexToDec(responseRowMerged.substring(50, 52).c_str(), 1, true); + if (params.speedKmh < 15 && params.batPowerKw >= 1 && params.socPerc > 0 && params.socPerc <= 100) { + params.chargingGraphKw[int(params.socPerc)] = params.batPowerKw; + params.chargingGraphMinTempC[int(params.socPerc)] = params.batMinC; + params.chargingGraphMaxTempC[int(params.socPerc)] = params.batMaxC; + } + } + if (commandRequest.equals("220102")) { + 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; + } + } + 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; + } + } + 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; + } } if (commandRequest.equals("220105")) { - params.sohPerc = hexToDec(responseRowMerged.substring(56, 60).c_str(), 2, false) / 10.0; // === OK Valid - params.socPerc = hexToDec(responseRowMerged.substring(68, 70).c_str(), 1, false) / 2.0; // === OK Valid - params.batHeaterC = hexToDec(responseRowMerged.substring(52, 54).c_str(), 1, true); // === OK Valid + 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; + 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; + } } if (commandRequest.equals("22c00b")) { params.tireFrontLeftPressureBar = hexToDec(responseRowMerged.substring(14, 16).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722 @@ -614,7 +966,7 @@ bool testData() { // 220103 commandRequest = "220103"; - responseRowMerged = "620103FFFFFFFFCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCCCBCBCBCBCBCBCBCBAAAA"; + responseRowMerged = "620103FFFFFFFFCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCACBCACACFCCCBCBCBCBCBCBCBCBAAAA"; parseRowMerged(); // 220104 @@ -643,12 +995,71 @@ bool testData() { responseRowMerged = "62C00BFFFF0000B93D0100B43E0100B43D0100BB3C0100AAAAAAAA"; parseRowMerged(); + 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; + redrawScreen(false); return true; } /** - BLE Security + BLE callbacks +*/ +class MyClientCallback : public BLEClientCallbacks { + + /** + On connect + */ + void onConnect(BLEClient* pclient) { + Serial.println("onConnect"); + } + + /** + On disconnect + */ + void onDisconnect(BLEClient* pclient) { + //connected = false; + Serial.println("onDisconnect"); + } +}; + +/** + 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()); + + if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(BLEUUID(settings.serviceUUID))) { + Serial.println("Stop scanning. Found my BLE device."); + BLEDevice::getScan()->stop(); + foundMyBleDevice = new BLEAdvertisedDevice(advertisedDevice); + } + } +}; + +/** + BLE Security */ class MySecurity : public BLESecurityCallbacks { @@ -681,6 +1092,34 @@ class MySecurity : public BLESecurityCallbacks { } }; +/** + 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 + responseRow = ""; + for (int i = 0; i <= length; i++) { + ch = pData[i]; + if (ch == '\r' || ch == '\n' || ch == '\0') { + if (responseRow != "") + parseRow(); + responseRow = ""; + } else { + responseRow += ch; + if (responseRow == ">") { + if (responseRowMerged != "") { + parseRowMerged(); + } + responseRowMerged = ""; + canSendNextAtCommand = true; + } + } + } +} + /** Do connect BLE with server (OBD device) */ @@ -698,33 +1137,34 @@ bool connectToServer(BLEAddress pAddress) { pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); pClient = BLEDevice::createClient(); + pClient->setClientCallbacks(new MyClientCallback()); if ( pClient->connect(pAddress, BLE_ADDR_TYPE_RANDOM) ) Serial.println("bleConnected"); Serial.println(" - bleConnected to server"); // Remote service - BLERemoteService* pRemoteService = pClient->getService(serviceUUID); + BLERemoteService* pRemoteService = pClient->getService(BLEUUID(settings.serviceUUID)); if (pRemoteService == nullptr) { Serial.print("Failed to find our service UUID: "); - Serial.println(serviceUUID.toString().c_str()); + Serial.println(settings.serviceUUID); return false; } Serial.println(" - Found our service"); // Get characteristics - pRemoteCharacteristic = pRemoteService->getCharacteristic(charTxUUID); + pRemoteCharacteristic = pRemoteService->getCharacteristic(BLEUUID(settings.charTxUUID)); if (pRemoteCharacteristic == nullptr) { Serial.print("Failed to find our characteristic UUID: "); - Serial.println(charTxUUID.toString().c_str()); + Serial.println(settings.charTxUUID);//.toString().c_str()); return false; } Serial.println(" - Found our characteristic"); // Get characteristics - pRemoteCharacteristicWrite = pRemoteService->getCharacteristic(charRxUUID); + pRemoteCharacteristicWrite = pRemoteService->getCharacteristic(BLEUUID(settings.charRxUUID)); if (pRemoteCharacteristicWrite == nullptr) { Serial.print("Failed to find our characteristic UUID: "); - Serial.println(charRxUUID.toString().c_str()); + Serial.println(settings.charRxUUID);//.toString().c_str()); return false; } Serial.println(" - Found our characteristic write"); @@ -752,31 +1192,33 @@ bool connectToServer(BLEAddress pAddress) { } /** - Ble notification callback + Start ble scan */ -static void notifyCallback (BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) { +bool startBleScan() { - char ch; + foundMyBleDevice = NULL; + int32_t posx, posy; - // Parse multi line response to single lines - responseRow = ""; - for (int i = 0; i <= length; i++) { - ch = pData[i]; - if (ch == '\r' || ch == '\n' || ch == '\0') { - if (responseRow != "") - parseRow(); - responseRow = ""; - } else { - responseRow += ch; - if (responseRow == ">") { - if (responseRowMerged != "") { - parseRowMerged(); - } - responseRowMerged = ""; - couldSendNextAtCommand = true; - } - } - } + // Print message + tft.fillScreen(TFT_BLACK); + tft.setTextDatum(ML_DATUM); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setFreeFont(&Roboto_Thin_24); + tft.setTextDatum(BL_DATUM); + tft.drawString(" Searching BLE device... ", 0, 240 / 2, GFXFF); + + // Start scanning + Serial.println("Scanning BLE devices..."); + BLEScanResults foundDevices = pBLEScan->start(5, false); + Serial.print("Devices found: "); + Serial.println(foundDevices.getCount()); + Serial.println("Scan done!"); + pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory + + // Redraw screen + redrawScreen(true); + + return true; } /** @@ -789,6 +1231,7 @@ void setup(void) { Serial.println(""); Serial.println("Booting device..."); initStructure(); + initSettings(); // Set button pins for input pinMode(BUTTON_MIDDLE, INPUT); @@ -798,19 +1241,33 @@ void setup(void) { // Init display Serial.println("Init TFT display"); tft.begin(); - tft.setRotation(1); + tft.setRotation(3 ); tft.fillScreen(TFT_BLACK); redrawScreen(true); // Show test data on right button during boot device if (digitalRead(BUTTON_RIGHT) == LOW) { + displayScreen = 1; testData(); } // Start BLE connection + line = ""; Serial.println("Start BLE with PIN auth"); BLEDevice::init(""); - line = ""; + + // 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 5 seconds. + Serial.println("Setup BLE scan"); + pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setInterval(1349); + pBLEScan->setWindow(449); + pBLEScan->setActiveScan(true); + startBleScan(); + + // End + Serial.println("Device setup completed"); } /** @@ -818,23 +1275,26 @@ void setup(void) { */ void loop() { + //Serial.println("Loop"); + // Connect BLE device - if (bleConnect == true) { - pServerAddress = new BLEAddress(HM_MAC); + if (bleConnect == true && foundMyBleDevice) { + pServerAddress = new BLEAddress(settings.obdMacAddress); if (connectToServer(*pServerAddress)) { bleConnected = true; bleConnect = false; Serial.println("We are now connected to the BLE device."); + redrawScreen(true); // Serve first command (ATZ) doNextAtCommand(); } else { - Serial.println("We have failed to connect to the server; there is nothin more we will do."); + Serial.println("We have failed to connect to the server; there is nothing more we will do."); } } - // Read char from BLE + // Send line from TTY to OBD (custom command) if (bleConnected) { if (Serial.available()) { ch = Serial.read(); @@ -847,14 +1307,11 @@ void loop() { } } - if (couldSendNextAtCommand) { - couldSendNextAtCommand = false; - // Debug - // Serial.println("DO NEXT AT COMMAND"); - // delay(1000); + // Can send next command from queue to OBD + if (canSendNextAtCommand) { + canSendNextAtCommand = false; doNextAtCommand(); } - } // Handle buttons (under construction) LOW - pressed, HIGH - not pressed @@ -863,7 +1320,7 @@ void loop() { } else { if (!btnMiddlePressed) { btnMiddlePressed = true; - // doAction + // doAction } } if (digitalRead(BUTTON_LEFT) == HIGH) { @@ -872,8 +1329,10 @@ void loop() { if (!btnLeftPressed) { btnLeftPressed = true; displayScreen++; - if (displayScreen == 2) + if (displayScreen > displayScreenCount) displayScreen = 0; // rotate screens + // Turn off display on screen 0 + digitalWrite(TFT_BL, (displayScreen == 0) ? LOW : HIGH); redrawScreen(true); } } @@ -889,6 +1348,3 @@ void loop() { // 1ms delay delay(1); } - - -