First release
This commit is contained in:
894
enirodashboard.ino
Normal file
894
enirodashboard.ino
Normal file
@@ -0,0 +1,894 @@
|
||||
/*
|
||||
|
||||
KIA eNiro Dashboard
|
||||
|
||||
!! 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
|
||||
- choose IOS-VLINK device
|
||||
- get mac address a replace HM_MAC constant, then open CUSTOM service (first of 2)
|
||||
- there is serviceUUID (replace bellow in code)
|
||||
- open it.. there are 2x custom characteristics (first is for NOTIFY (read), and second for WRITE,WRITE_REQUEST).
|
||||
set charTxUUID with UUID from NOTIFY section
|
||||
set charRxUUID with UUID from WRITE section
|
||||
|
||||
Example.
|
||||
#define HM_MAC "dd:0d:30:50:ed:63"
|
||||
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"
|
||||
#include "TFT_eSPI.h"
|
||||
#include "BLEDevice.h"
|
||||
|
||||
// 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)
|
||||
///////////////////////////////////////////////
|
||||
|
||||
// LILYGO TTGO T4 v1.3 BUTTONS
|
||||
#define BUTTON_MIDDLE 37
|
||||
#define BUTTON_LEFT 38
|
||||
#define BUTTON_RIGHT 39
|
||||
|
||||
/* TFT COLORS */
|
||||
#define TFT_BLACK 0x0000 /* 0, 0, 0 */
|
||||
#define TFT_DEFAULT_BK 0x0000 // 0x38E0
|
||||
#define TFT_TEMP 0x0000 // NAVY
|
||||
#define TFT_GREEN 0x07E0 /* 0, 255, 0 */
|
||||
#define TFT_RED 0xF800 /* 255, 0, 0 */
|
||||
#define TFT_SILVER 0xC618 /* 192, 192, 192 */
|
||||
#define TFT_YELLOW 0xFFE0 /* 255, 255, 0 */
|
||||
#define TFT_DARKRED 0x3800 /* 128, 0, 0 */
|
||||
#define TFT_DARKGREEN2 0x01E0 /* 128, 0, 0 */
|
||||
|
||||
// Misc
|
||||
#define GFXFF 1 // TFT FOnts
|
||||
#define PSI2BAR_DIVIDER 14.503773800722 // tires psi / 14,503773800722 -> bar
|
||||
|
||||
TFT_eSPI tft = TFT_eSPI();
|
||||
|
||||
static boolean bleConnect = true;
|
||||
static boolean bleConnected = false;
|
||||
static BLEAddress *pServerAddress;
|
||||
static BLERemoteCharacteristic* pRemoteCharacteristic;
|
||||
static BLERemoteCharacteristic* pRemoteCharacteristicWrite;
|
||||
BLEClient* pClient;
|
||||
|
||||
// Temporary variables
|
||||
char ch;
|
||||
String line;
|
||||
char tmpStr1[20];
|
||||
char tmpStr2[20];
|
||||
char tmpStr3[20];
|
||||
char tmpStr4[20];
|
||||
|
||||
// Main
|
||||
byte displayScreen = 0; // 0 - bash board, 1 - battery cells
|
||||
bool btnLeftPressed = true;
|
||||
bool btnMiddlePressed = true;
|
||||
bool btnRightPressed = true;
|
||||
|
||||
|
||||
// Commands loop
|
||||
#define commandQueueCount 23
|
||||
#define commandQueueLoopFrom 6
|
||||
String responseRow;
|
||||
String responseRowMerged;
|
||||
byte commandQueueIndex;
|
||||
bool couldSendNextAtCommand = false;
|
||||
String commandRequest = "";
|
||||
String commandQueue[commandQueueCount] = {
|
||||
"AT Z", // Reset all
|
||||
"AT I", // Print the version ID
|
||||
"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 S0", // Printing of spaces on
|
||||
//"AT D1", // Display of the DLC on
|
||||
//"AT CAF0", // Automatic formatting off
|
||||
"AT DP",
|
||||
"atst16",
|
||||
|
||||
// Loop from (KIA ENIRO)
|
||||
"atsh7e4",
|
||||
"220101", // power kw, ...
|
||||
//"220102", // cell voltages
|
||||
//"220103", // cell voltages
|
||||
//"220104", // cell voltages
|
||||
"220105", // soh, soc, ..
|
||||
//"220106",
|
||||
"atsh7e2",
|
||||
"2101", // speed, ...
|
||||
"2102", // aux, ...
|
||||
"atsh7df",
|
||||
//"2106",
|
||||
//"220106",
|
||||
"atsh7b3",
|
||||
"220100", // in/out temp
|
||||
"atsh7a0",
|
||||
"22c00b", // tire pressure/temp
|
||||
};
|
||||
|
||||
// Structure with realtime values
|
||||
struct strucParams {
|
||||
float speedKmh;
|
||||
float socPerc;
|
||||
float sohPerc;
|
||||
float cumulativeEnergyChargedKWh;
|
||||
float cumulativeEnergyChargedKWhStart;
|
||||
float cumulativeEnergyDischargedKWh;
|
||||
float cumulativeEnergyDischargedKWhStart;
|
||||
float batPowerAmp;
|
||||
float batPowerKw;
|
||||
float batPowerKwh100;
|
||||
float batVoltage;
|
||||
float batCellMin;
|
||||
float batCellMax;
|
||||
float batTempC;
|
||||
float batHeaterC;
|
||||
float auxPerc;
|
||||
float auxCurrentAmp;
|
||||
float auxVoltage;
|
||||
float indoorTemperature;
|
||||
float outdoorTemperature;
|
||||
float tireFrontLeftTempC;
|
||||
float tireFrontLeftPressureBar;
|
||||
float tireFrontRightTempC;
|
||||
float tireFrontRightPressureBar;
|
||||
float tireRearLeftTempC;
|
||||
float tireRearLeftPressureBar;
|
||||
float tireRearRightTempC;
|
||||
float tireRearRightPressureBar;
|
||||
};
|
||||
|
||||
strucParams params; // Current
|
||||
strucParams oldParams; // Old states used for redraw changed values only
|
||||
|
||||
/**
|
||||
Init structure with data
|
||||
*/
|
||||
bool initStructure() {
|
||||
|
||||
params.speedKmh = -1;
|
||||
params.socPerc = -1;
|
||||
params.sohPerc = -1;
|
||||
params.cumulativeEnergyChargedKWh = -1;
|
||||
params.cumulativeEnergyChargedKWhStart = -1;
|
||||
params.cumulativeEnergyDischargedKWh = -1;
|
||||
params.cumulativeEnergyDischargedKWhStart = -1;
|
||||
params.batPowerAmp = -1;
|
||||
params.batPowerKw = -1;
|
||||
params.batPowerKwh100 = -1;
|
||||
params.batVoltage = -1;
|
||||
params.batCellMin = -1;
|
||||
params.batCellMax = -1;
|
||||
params.batTempC = -1;
|
||||
params.batHeaterC = -1;
|
||||
params.auxPerc = -1;
|
||||
params.auxCurrentAmp = -1;
|
||||
params.auxVoltage = -1;
|
||||
params.indoorTemperature = -1;
|
||||
params.outdoorTemperature = -1;
|
||||
params.tireFrontLeftTempC = -1;
|
||||
params.tireFrontLeftPressureBar = -1;
|
||||
params.tireFrontRightTempC = -1;
|
||||
params.tireFrontRightPressureBar = -1;
|
||||
params.tireRearLeftTempC = -1;
|
||||
params.tireRearLeftPressureBar = -1;
|
||||
params.tireRearRightTempC = -1;
|
||||
params.tireRearRightPressureBar = -1;
|
||||
oldParams = params;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
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) {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
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) {
|
||||
|
||||
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.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.setTextSize(1); // Size for small 5x7 font
|
||||
tft.drawString(desc, posx, posy, 2);
|
||||
|
||||
// Big 2x2 cell in the middle of screen
|
||||
if (w == 2 && h == 2) {
|
||||
|
||||
// Bottom 2 numbers with charged/discharged kWh from start
|
||||
posx = (x * 80) + 5;
|
||||
posy = ((y + h) * 60) - 32;
|
||||
sprintf(tmpStr3, "-%01.01f", params.cumulativeEnergyDischargedKWh - params.cumulativeEnergyDischargedKWhStart);
|
||||
tft.setFreeFont(&Roboto_Thin_24);
|
||||
tft.setTextDatum(TL_DATUM);
|
||||
tft.drawString(tmpStr3, posx, posy, GFXFF);
|
||||
|
||||
posx = ((x + w) * 80) - 8;
|
||||
sprintf(tmpStr3, "+%01.01f", params.cumulativeEnergyChargedKWh - params.cumulativeEnergyChargedKWhStart);
|
||||
tft.setTextDatum(TR_DATUM);
|
||||
tft.drawString(tmpStr3, posx, posy, GFXFF);
|
||||
|
||||
// Main number - kwh on roads, amps on charges
|
||||
posy = (y * 60) + 24;
|
||||
tft.setTextColor(TFT_WHITE, color);
|
||||
tft.setFreeFont(&Orbitron_Light_32);
|
||||
tft.drawString(text, posx, posy, 7);
|
||||
|
||||
} else {
|
||||
|
||||
// All others 1x1 cells
|
||||
tft.setTextDatum(MC_DATUM);
|
||||
tft.setTextColor(TFT_WHITE, color);
|
||||
tft.setFreeFont(&Orbitron_Light_24);
|
||||
posx = (x * 80) + (w * 80 / 2) - 3;
|
||||
posy = (y * 60) + (h * 60 / 2) + 4;
|
||||
tft.drawString(text, posx, posy, (w == 2 ? 7 : GFXFF));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
Show tire pressures / temperatures
|
||||
Custom field
|
||||
*/
|
||||
bool showTires(int32_t x, int32_t y, int32_t w, int32_t h, const char* topleft, const char* topright, const char* bottomleft, const char* bottomright, int16_t color) {
|
||||
|
||||
int32_t posx, posy;
|
||||
|
||||
tft.fillRect(x * 80, y * 60, ((w) * 80) - 1, ((h) * 60) - 1, color);
|
||||
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);
|
||||
tft.setTextColor(TFT_SILVER, color);
|
||||
tft.setTextSize(1);
|
||||
posx = (x * 80) + 4;
|
||||
posy = (y * 60) + 0;
|
||||
tft.drawString(topleft, posx, posy, 2);
|
||||
posy = (y * 60) + 14;
|
||||
tft.drawString(bottomleft, posx, posy, 2);
|
||||
|
||||
tft.setTextDatum(TR_DATUM);
|
||||
posx = ((x + w) * 80) - 4;
|
||||
posy = (y * 60) + 0;
|
||||
tft.drawString(topright, posx, posy, 2);
|
||||
posy = (y * 60) + 14;
|
||||
tft.drawString(bottomright, posx, posy, 2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
Main screen (Screen 0)
|
||||
*/
|
||||
bool drawSceneMain(bool force) {
|
||||
|
||||
// Tire pressure
|
||||
if (force || params.tireFrontLeftTempC != oldParams.tireFrontLeftTempC
|
||||
|| params.tireFrontRightTempC != oldParams.tireFrontRightTempC || params.tireRearLeftTempC != oldParams.tireRearLeftTempC || params.tireRearRightTempC != oldParams.tireRearRightTempC
|
||||
|| oldParams.cumulativeEnergyChargedKWhStart != params.cumulativeEnergyChargedKWhStart
|
||||
|| oldParams.cumulativeEnergyChargedKWh != params.cumulativeEnergyChargedKWh
|
||||
|| oldParams.cumulativeEnergyDischargedKWhStart != params.cumulativeEnergyDischargedKWhStart
|
||||
|| oldParams.cumulativeEnergyDischargedKWh != params.cumulativeEnergyDischargedKWh
|
||||
) {
|
||||
sprintf(tmpStr1, "%01.01fbar %02.00fC", params.tireFrontLeftPressureBar, params.tireFrontLeftTempC);
|
||||
sprintf(tmpStr2, "%02.00fC %01.01fbar", params.tireFrontRightTempC, params.tireFrontRightPressureBar);
|
||||
sprintf(tmpStr3, "%01.01fbar %02.00fC", params.tireRearLeftPressureBar, params.tireRearLeftTempC);
|
||||
sprintf(tmpStr4, "%02.00fC %01.01fbar", params.tireRearRightTempC, params.tireRearRightPressureBar);
|
||||
showTires(1, 0, 2, 1, tmpStr1, tmpStr2, tmpStr3, tmpStr4, TFT_BLACK);
|
||||
|
||||
// Added later - kwh total in tires box
|
||||
// TODO: refactoring
|
||||
tft.setTextDatum(TL_DATUM);
|
||||
tft.setTextColor(TFT_GREEN, TFT_BLACK);
|
||||
sprintf(tmpStr1, "C: %01.01f +%01.01fkWh", params.cumulativeEnergyChargedKWh, params.cumulativeEnergyChargedKWh - params.cumulativeEnergyChargedKWhStart);
|
||||
tft.drawString(tmpStr1, (1 * 80) + 4, (0 * 60) + 30, 2);
|
||||
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
|
||||
sprintf(tmpStr1, "D: %01.01f -%01.01fkWh", params.cumulativeEnergyDischargedKWh, params.cumulativeEnergyDischargedKWh - params.cumulativeEnergyDischargedKWhStart);
|
||||
tft.drawString(tmpStr1, (1 * 80) + 4, (0 * 60) + 44, 2);
|
||||
|
||||
oldParams.tireFrontLeftTempC = params.tireFrontLeftTempC;
|
||||
oldParams.tireFrontLeftPressureBar = params.tireFrontLeftPressureBar;
|
||||
oldParams.tireFrontRightTempC = params.tireFrontRightTempC;
|
||||
oldParams.tireFrontRightPressureBar = params.tireFrontRightPressureBar;
|
||||
oldParams.tireRearLeftTempC = params.tireRearLeftTempC;
|
||||
oldParams.tireRearLeftPressureBar = params.tireRearLeftPressureBar;
|
||||
oldParams.tireRearRightTempC = params.tireRearRightTempC;
|
||||
oldParams.tireRearRightPressureBar = params.tireRearRightPressureBar;
|
||||
oldParams.cumulativeEnergyChargedKWhStart = params.cumulativeEnergyChargedKWhStart;
|
||||
oldParams.cumulativeEnergyChargedKWh = params.cumulativeEnergyChargedKWh;
|
||||
oldParams.cumulativeEnergyDischargedKWhStart = params.cumulativeEnergyDischargedKWhStart;
|
||||
oldParams.cumulativeEnergyDischargedKWh = params.cumulativeEnergyDischargedKWh;
|
||||
}
|
||||
|
||||
// batPowerKwh100 on roads
|
||||
if (params.speedKmh > 10) {
|
||||
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)));
|
||||
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));
|
||||
oldParams.batPowerAmp = params.batPowerAmp;
|
||||
}
|
||||
}
|
||||
|
||||
// socPerc
|
||||
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)));
|
||||
oldParams.socPerc = params.socPerc;
|
||||
oldParams.sohPerc = params.sohPerc;
|
||||
}
|
||||
|
||||
// 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)));
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
// batTempC
|
||||
if (force || params.batTempC != oldParams.batTempC) {
|
||||
sprintf(tmpStr1, "%01.00fC", params.batTempC);
|
||||
monitoringRect(1, 3, 1, 1, tmpStr1, "BAT.TEMP", TFT_TEMP);
|
||||
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);
|
||||
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));
|
||||
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));
|
||||
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)));
|
||||
oldParams.auxVoltage = params.auxVoltage;
|
||||
}
|
||||
|
||||
// indoorTemperature
|
||||
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);
|
||||
oldParams.indoorTemperature = params.indoorTemperature;
|
||||
oldParams.outdoorTemperature = params.outdoorTemperature;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
Redraw screen
|
||||
*/
|
||||
bool redrawScreen(bool force) {
|
||||
|
||||
// Clear screen if needed
|
||||
if (force) {
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
}
|
||||
|
||||
// Main screen
|
||||
if (displayScreen == 0) {
|
||||
drawSceneMain(force);
|
||||
}
|
||||
|
||||
// Battery cells
|
||||
if (displayScreen == 1) {
|
||||
// UNDER CONSTRUCTION
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
Do next AT command from queue
|
||||
*/
|
||||
bool doNextAtCommand() {
|
||||
|
||||
// Restart loop with AT commands
|
||||
if (commandQueueIndex >= commandQueueCount) {
|
||||
commandQueueIndex = commandQueueLoopFrom;
|
||||
// Redraw only changed values
|
||||
redrawScreen(false);
|
||||
}
|
||||
|
||||
// Send AT command to obd
|
||||
commandRequest = commandQueue[commandQueueIndex];
|
||||
Serial.print(">>> ");
|
||||
Serial.println(commandRequest);
|
||||
String tmpStr = commandRequest + "\r";
|
||||
pRemoteCharacteristicWrite->writeValue(tmpStr.c_str(), tmpStr.length());
|
||||
commandQueueIndex++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
Parse result from OBD, create single line responseRowMerged
|
||||
*/
|
||||
bool parseRow() {
|
||||
|
||||
// Simple 1 line responses
|
||||
Serial.print("");
|
||||
Serial.println(responseRow);
|
||||
|
||||
// Merge 0:xxxx 1:yyyy 2:zzzz to single xxxxyyyyzzzz string
|
||||
if (responseRow.length() >= 2 && responseRow.charAt(1) == ':') {
|
||||
if (responseRow.charAt(0) == '0') {
|
||||
responseRowMerged = "";
|
||||
}
|
||||
responseRowMerged += responseRow.substring(2);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
Parse merged row (after merge completed)
|
||||
*/
|
||||
bool parseRowMerged() {
|
||||
|
||||
Serial.print("merged:");
|
||||
Serial.println(responseRowMerged);
|
||||
|
||||
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 (commandRequest.equals("2102")) {
|
||||
params.auxPerc = hexToDec(responseRowMerged.substring(50, 52).c_str(), 1, false); // === OK Valid
|
||||
params.auxCurrentAmp = - hexToDec(responseRowMerged.substring(46, 50).c_str(), 2, true) / 1000.0;
|
||||
}
|
||||
if (commandRequest.equals("220100")) {
|
||||
params.indoorTemperature = (hexToDec(responseRowMerged.substring(16, 18).c_str(), 1, false) / 2) - 40; // === OK Valid
|
||||
params.outdoorTemperature = (hexToDec(responseRowMerged.substring(18, 20).c_str(), 1, false) / 2) - 40; // === OK Valid
|
||||
}
|
||||
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; // === 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
|
||||
}
|
||||
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
|
||||
}
|
||||
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.tireRearLeftPressureBar = hexToDec(responseRowMerged.substring(30, 32).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722
|
||||
params.tireRearRightPressureBar = hexToDec(responseRowMerged.substring(38, 40).c_str(), 2, false) / 72.51886900361; // === OK Valid *0.2 / 14.503773800722
|
||||
params.tireRearLeftTempC = hexToDec(responseRowMerged.substring(16, 18).c_str(), 2, false) - 50; // === OK Valid
|
||||
params.tireRearRightTempC = hexToDec(responseRowMerged.substring(24, 26).c_str(), 2, false) - 50; // === OK Valid
|
||||
params.tireFrontLeftTempC = hexToDec(responseRowMerged.substring(32, 34).c_str(), 2, false) - 50; // === OK Valid
|
||||
params.tireFrontRightTempC = hexToDec(responseRowMerged.substring(40, 42).c_str(), 2, false) - 50; // === OK Valid
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
Parse test data
|
||||
*/
|
||||
bool testData() {
|
||||
|
||||
redrawScreen(true);
|
||||
|
||||
// 2101
|
||||
commandRequest = "2101";
|
||||
responseRowMerged = "6101FFF8000009285A3B0648030000B4179D763404080805000000";
|
||||
parseRowMerged();
|
||||
|
||||
// 2102
|
||||
commandRequest = "2102";
|
||||
responseRowMerged = "6102F8FFFC000101000000840FBF83BD33270680953033757F59291C76000001010100000007000000";
|
||||
responseRowMerged = "6102F8FFFC000101000000931CC77F4C39040BE09BA7385D8158832175000001010100000007000000";
|
||||
parseRowMerged();
|
||||
|
||||
// 2106
|
||||
commandRequest = "2106";
|
||||
responseRowMerged = "6106FFFF800000000000000200001B001C001C000600060006000E000000010000000000000000013D013D013E013E00";
|
||||
parseRowMerged();
|
||||
|
||||
// 220100
|
||||
commandRequest = "220100";
|
||||
responseRowMerged = "6201007E5027C8FF7F765D05B95AFFFF5AFF11FFFFFFFFFFFF6AFFFF2DF0757630FFFF00FFFF000000";
|
||||
responseRowMerged = "6201007E5027C8FF867C58121010FFFF10FF8EFFFFFFFFFFFF10FFFF0DF0617900FFFF01FFFF000000";
|
||||
parseRowMerged();
|
||||
|
||||
// 220101
|
||||
commandRequest = "220101";
|
||||
responseRowMerged = "620101FFF7E7FF99000000000300B10EFE120F11100F12000018C438C30B00008400003864000035850000153A00001374000647010D017F0BDA0BDA03E8";
|
||||
responseRowMerged = "620101FFF7E7FFB3000000000300120F9B111011101011000014CC38CB3B00009100003A510000367C000015FB000013D3000690250D018E0000000003E8";
|
||||
parseRowMerged();
|
||||
|
||||
// 220102
|
||||
commandRequest = "220102";
|
||||
responseRowMerged = "620102FFFFFFFFCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBAAAA";
|
||||
parseRowMerged();
|
||||
|
||||
// 220103
|
||||
commandRequest = "220103";
|
||||
responseRowMerged = "620103FFFFFFFFCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCCCBCBCBCBCBCBCBCBAAAA";
|
||||
parseRowMerged();
|
||||
|
||||
// 220104
|
||||
commandRequest = "220104";
|
||||
responseRowMerged = "620104FFFFFFFFCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBCBAAAA";
|
||||
parseRowMerged();
|
||||
|
||||
// 220105
|
||||
commandRequest = "220105";
|
||||
responseRowMerged = "620105003fff9000000000000000000F8A86012B4946500101500DAC03E800000000AC0000C7C701000F00000000AAAA";
|
||||
responseRowMerged = "620105003FFF90000000000000000014918E012927465000015013BB03E800000000BB0000CBCB01001300000000AAAA";
|
||||
parseRowMerged();
|
||||
|
||||
// 220106
|
||||
commandRequest = "220106";
|
||||
responseRowMerged = "620106FFFFFFFF14001A00240000003A7C86B4B30000000928EA00";
|
||||
parseRowMerged();
|
||||
|
||||
// 22c002
|
||||
commandRequest = "22c002";
|
||||
responseRowMerged = "62C002FFFF0000D2E84E93D2E84EBBD2DBDACBD2E149F3AAAAAAAA";
|
||||
parseRowMerged();
|
||||
|
||||
// 22c00b
|
||||
commandRequest = "22c00b";
|
||||
responseRowMerged = "62C00BFFFF0000B93D0100B43E0100B43D0100BB3C0100AAAAAAAA";
|
||||
parseRowMerged();
|
||||
|
||||
redrawScreen(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
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?");
|
||||
bleConnect = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Do connect BLE with server (OBD device)
|
||||
*/
|
||||
bool connectToServer(BLEAddress pAddress) {
|
||||
|
||||
Serial.print("bleConnect ");
|
||||
Serial.println(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);
|
||||
|
||||
pClient = BLEDevice::createClient();
|
||||
if ( pClient->connect(pAddress, BLE_ADDR_TYPE_RANDOM) ) Serial.println("bleConnected");
|
||||
Serial.println(" - bleConnected to server");
|
||||
|
||||
// Remote service
|
||||
BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
|
||||
if (pRemoteService == nullptr)
|
||||
{
|
||||
Serial.print("Failed to find our service UUID: ");
|
||||
Serial.println(serviceUUID.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
Serial.println(" - Found our service");
|
||||
|
||||
// Get characteristics
|
||||
pRemoteCharacteristic = pRemoteService->getCharacteristic(charTxUUID);
|
||||
if (pRemoteCharacteristic == nullptr) {
|
||||
Serial.print("Failed to find our characteristic UUID: ");
|
||||
Serial.println(charTxUUID.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
Serial.println(" - Found our characteristic");
|
||||
|
||||
// Get characteristics
|
||||
pRemoteCharacteristicWrite = pRemoteService->getCharacteristic(charRxUUID);
|
||||
if (pRemoteCharacteristicWrite == nullptr) {
|
||||
Serial.print("Failed to find our characteristic UUID: ");
|
||||
Serial.println(charRxUUID.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
Serial.println(" - Found our characteristic write");
|
||||
|
||||
// Read the value of the characteristic.
|
||||
if (pRemoteCharacteristic->canNotify()) {
|
||||
Serial.println(" - canNotify");
|
||||
//pRemoteCharacteristic->registerForNotify(notifyCallback);
|
||||
if (pRemoteCharacteristic->canIndicate()) {
|
||||
Serial.println(" - canIndicate");
|
||||
const uint8_t indicationOn[] = {0x2, 0x0};
|
||||
//const uint8_t indicationOff[] = {0x0,0x0};
|
||||
pRemoteCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)indicationOn, 2, true);
|
||||
//pRemoteCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notifyOff,2,true);
|
||||
pRemoteCharacteristic->registerForNotify(notifyCallback, false);
|
||||
delay(200);
|
||||
}
|
||||
}
|
||||
|
||||
if (pRemoteCharacteristicWrite->canWrite()) {
|
||||
Serial.println(" - canWrite");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
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 = "";
|
||||
couldSendNextAtCommand = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Setup device
|
||||
*/
|
||||
void setup(void) {
|
||||
|
||||
// Serial console, init structures
|
||||
Serial.begin(115200);
|
||||
Serial.println("");
|
||||
Serial.println("Booting device...");
|
||||
initStructure();
|
||||
|
||||
// Set button pins for input
|
||||
pinMode(BUTTON_MIDDLE, INPUT);
|
||||
pinMode(BUTTON_LEFT, INPUT);
|
||||
pinMode(BUTTON_RIGHT, INPUT);
|
||||
|
||||
// Init display
|
||||
Serial.println("Init TFT display");
|
||||
tft.begin();
|
||||
tft.setRotation(1);
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
redrawScreen(true);
|
||||
|
||||
// Show test data on right button during boot device
|
||||
if (digitalRead(BUTTON_RIGHT) == LOW) {
|
||||
testData();
|
||||
}
|
||||
|
||||
// Start BLE connection
|
||||
Serial.println("Start BLE with PIN auth");
|
||||
BLEDevice::init("");
|
||||
line = "";
|
||||
}
|
||||
|
||||
/**
|
||||
Loop
|
||||
*/
|
||||
void loop() {
|
||||
|
||||
// Connect BLE device
|
||||
if (bleConnect == true) {
|
||||
pServerAddress = new BLEAddress(HM_MAC);
|
||||
if (connectToServer(*pServerAddress)) {
|
||||
|
||||
bleConnected = true;
|
||||
bleConnect = false;
|
||||
Serial.println("We are now connected to the BLE device.");
|
||||
|
||||
// Serve first command (ATZ)
|
||||
doNextAtCommand();
|
||||
} else {
|
||||
Serial.println("We have failed to connect to the server; there is nothin more we will do.");
|
||||
}
|
||||
}
|
||||
|
||||
// Read char from BLE
|
||||
if (bleConnected) {
|
||||
if (Serial.available()) {
|
||||
ch = Serial.read();
|
||||
line = line + ch;
|
||||
if (ch == '\r' || ch == '\n') {
|
||||
Serial.print("Sending line: ");
|
||||
Serial.println(line);
|
||||
pRemoteCharacteristicWrite->writeValue(line.c_str(), line.length());
|
||||
line = "";
|
||||
}
|
||||
}
|
||||
|
||||
if (couldSendNextAtCommand) {
|
||||
couldSendNextAtCommand = false;
|
||||
// Debug
|
||||
// Serial.println("DO NEXT AT COMMAND");
|
||||
// delay(1000);
|
||||
doNextAtCommand();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Handle buttons (under construction) LOW - pressed, HIGH - not pressed
|
||||
if (digitalRead(BUTTON_MIDDLE) == HIGH) {
|
||||
btnMiddlePressed = false;
|
||||
} else {
|
||||
if (!btnMiddlePressed) {
|
||||
btnMiddlePressed = true;
|
||||
// doAction
|
||||
}
|
||||
}
|
||||
if (digitalRead(BUTTON_LEFT) == HIGH) {
|
||||
btnLeftPressed = false;
|
||||
} else {
|
||||
if (!btnLeftPressed) {
|
||||
btnLeftPressed = true;
|
||||
displayScreen++;
|
||||
if (displayScreen == 2)
|
||||
displayScreen = 0; // rotate screens
|
||||
redrawScreen(true);
|
||||
}
|
||||
}
|
||||
if (digitalRead(BUTTON_RIGHT) == HIGH) {
|
||||
btnRightPressed = false;
|
||||
} else {
|
||||
if (!btnRightPressed) {
|
||||
btnRightPressed = true;
|
||||
// doAction
|
||||
}
|
||||
}
|
||||
|
||||
// 1ms delay
|
||||
delay(1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user