#include <TFT_eSPI.h> // Hardware-specific library
#include <SPI.h>
#include <JC_Button.h>
#include <ESP32Time.h>
ESP32Time rtc(0);
TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
// button pins
#define UP_PIN 3
#define DOWN_PIN 8
#define ENTER_PIN 17
#define BACK_PIN 18
Button btnUP(UP_PIN), btnDOWN(DOWN_PIN), btnENTER(ENTER_PIN), btnBACK(BACK_PIN); // define the buttons
//offset of all the graphics if the screen is incorrecly placed in the cluster
int offset_x = 3;
int offset_y = 2;
//global variables
int speedDIR = 0;
int current_screen = 0; //0==big1sensor(speed), 1==sensor4, 2==sensor7, 3=menu
int selected_item = -1;
int sensor_item = -1;
const int NUM_SCREENS = 3; //4 screens
const char *SENSOR_NAMES[] = {"RPM", "IAT", "TPS", "CLT", "EGT", "MAP", "MAF", "SPD"}; //sensor name array
const char *SENSOR_UNITS[] = {"RPM ", "deg ", "% ", "deg ", "deg ", "mbar", "g/s ", "km/h"};
int SENSOR_VALUES[] = {890, 26, 50, 89, 660, 1400, 380, 100}; //sensor value array
const int SENSOR_NUMBER = 7; //amount of sensors (number-1)
int SCREEN_BIG = 7; //selection to show big number
int SCREEN_SENSOR4_SELECTION[] = {0, 1, 2, 3}; //array which sensor to show in screen4sensors
int SCREEN_SENSOR7_SELECTION[] = {0, 1, 2, 3, 4, 5, 6}; //array which sensor to show in screen7sensors
const int sens4_xy[4][2] = {{77, 81}, {150, 81}, {77, 110}, {150, 110}}; //4 sensor screen item coordinates
const int MENU_ITEMS_NUMBER = 4; //how many items in main menu start from 0, so -1
const char *MENU_NAMES[] = {"DIAG CAN ", "DIAG K-line", "Brightness ", "Time ", "FW Update "};
bool select_state = false; // if the select button is pressed
bool activated_state = false; //placeholder for aditional show sensor list or menu items
int brightness = 5;
void setup() {
Serial.begin(115200);
rtc.setTime(30, 24, 15, 17, 1, 2021); // Set time to 17th Jan 2021 15:24:30
btnUP.begin();
btnDOWN.begin();
btnENTER.begin();
btnBACK.begin();
tft.init();
tft.setRotation(1);
tft.fillScreen(TFT_BLACK);
tft.setCursor(10 + offset_x, 2 + offset_y);
tft.setTextColor(TFT_RED);
tft.setTextSize(3);
tft.drawRect(0 + offset_x, 0 + offset_y, 160, 25, TFT_RED);
tft.println("GOLF GTD");
tft.drawSmoothRoundRect(7 + offset_x, 60 + offset_y, 5, 5, 146, 58, TFT_RED, TFT_BLACK); //x,y,r1,r2, w,h, color, bgcolor
logo(TFT_WHITE, TFT_BLUE);
delay(1000);
clearTop();
drawTopLine();
clearBot();
}
void loop() {
readSensors();
drawBot(activated_state);
drawTopLine();
btnUP.read();
if (btnUP.wasPressed()) {
if (select_state == true && activated_state == false) { //check if selection is activated
selected_item++;
if (current_screen == 1) { //screen 1 only has 4 sensors
if (selected_item > 3) {
selected_item = 0;
}
}
else if (current_screen == 2) {//screen 2 only has 7 sensors
if (selected_item > 6) {
selected_item = 0;
}
}
else if (current_screen == 3) {//screen 1 only has MENU_ITEMS_NUMBER
if (selected_item > MENU_ITEMS_NUMBER) {
selected_item = 0;
}
}
Serial.print("up button in select state: ");
Serial.println(selected_item);
}
else if (select_state == false && activated_state == true) {
sensor_item++;
if (sensor_item > SENSOR_NUMBER) {
sensor_item = 0;
}
Serial.print("up button in sensor: ");
Serial.println(sensor_item);
}
else if (select_state == false && activated_state == false) { //else cycle through screens
current_screen++;
if (current_screen > NUM_SCREENS) {
current_screen = 0;
}
clearBot();
Serial.print("->");
}
}
btnDOWN.read();
if (btnDOWN.wasPressed()) {
if (select_state == true && activated_state == false) { //check if selection is activated
selected_item--;
if (selected_item < 0) {
if (current_screen == 1) {
selected_item = 3; //4sensor
}
if (current_screen == 2) {
selected_item = 6; //7sensor
}
if (current_screen == 3) {
selected_item = MENU_ITEMS_NUMBER; //menu items
}
}
Serial.print("bown button in select state: ");
Serial.println(selected_item);
}
else if (select_state == false && activated_state == true) {
sensor_item--;
if (sensor_item < 0) {
sensor_item = SENSOR_NUMBER;
}
Serial.print("down button in sensor: ");
Serial.println(sensor_item);
}
else if (select_state == false && activated_state == false) { //else cycle through screens
current_screen--;
if (current_screen < 0) {
current_screen = NUM_SCREENS;
}
clearBot();
Serial.println("<-");
}
}
btnENTER.read();
if (btnENTER.wasPressed()) {
Serial.print("ent pressed: ");
if (current_screen == 0 && select_state == false && activated_state == false){
select_state = true; //as there is no selections
Serial.println("bypass selection");
}
if (select_state == false && activated_state == false) {
select_state = true;
selected_item = 0;
Serial.println("menu->selection");
}
else if (select_state == true && activated_state == false) {
sensor_item = 0;
select_state = false;
activated_state = true;
Serial.println("selection->sensor_menu");
clearBot();
}
else if (select_state == false && activated_state == true) {
//sensor_item = 0;
select_state = true;
activated_state = false;
Serial.print("return sensor: ");
Serial.println(sensor_item);
clearBot();
assignSensorItem();
if (current_screen == 0){
select_state = false;
}
}
}
btnBACK.read();
if (btnBACK.wasPressed()) {
Serial.print("bck pressed: ");
if (select_state == true && activated_state == false) {
select_state = false;
selected_item = -1;
Serial.println("selection->menu");
}
else if (select_state == false && activated_state == true) {
select_state = true;
activated_state = false;
sensor_item = -1;
Serial.println("sensor_menu->selection");
clearBot();
if (current_screen == 0){ //as screen 0 has no selection
select_state = false;
}
}
}
}
void clearBot() {
tft.fillSmoothRoundRect(8 + offset_x, 61 + offset_y, 145, 57, 5, TFT_BLACK, TFT_BLACK);
}
void clearTop() {
tft.fillRect(1 + offset_x, 1 + offset_y, 158, 23, TFT_BLACK);
}
void drawTopLine() {
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setTextSize(2);
tft.setTextDatum(ML_DATUM);
tft.drawFloat(26.3, 1, 4 + offset_x, 14 + offset_y); //draw temp value
tft.drawChar(53 + offset_x, 6 + offset_y, 0xF7, TFT_WHITE, TFT_BLACK, 2); //draw degree sign
tft.setTextDatum(MR_DATUM);
tft.drawString(rtc.getTime("%H:%M"), 157 + offset_x, 14 + offset_y); //draw time
}
void logo(uint16_t color1, uint16_t color2) {
//Circles
tft.fillSmoothCircle(80 + offset_x, 89 + offset_y, 26, color2, TFT_BLACK);
tft.fillSmoothCircle(80 + offset_x, 89 + offset_y, 22, color1, color2);
tft.fillSmoothCircle(80 + offset_x, 89 + offset_y, 18, color2, color1);
//topV
tft.fillTriangle(70 + offset_x, 72 + offset_y, 77 + offset_x, 87 + offset_y, 82 + offset_x, 87 + offset_y, color1); // first
tft.fillTriangle(73 + offset_x, 69 + offset_y, 70 + offset_x, 72 + offset_y, 82 + offset_x, 87 + offset_y, color1); // this part /* \ */
tft.fillTriangle(86 + offset_x, 69 + offset_y, 77 + offset_x, 87 + offset_y, 82 + offset_x, 87 + offset_y, color1); // this one /
tft.fillTriangle(90 + offset_x, 72 + offset_y, 86 + offset_x, 69 + offset_y, 82 + offset_x, 87 + offset_y, color1); // last
//bottomW
tft.fillTriangle(62 + offset_x, 78 + offset_y, 66 + offset_x, 78 + offset_y, 69 + offset_x, 104 + offset_y, color1);
tft.fillTriangle(69 + offset_x, 104 + offset_y, 66 + offset_x, 75 + offset_y, 76 + offset_x, 106 + offset_y, color1);
tft.fillTriangle(70 + offset_x, 107 + offset_y, 77 + offset_x, 90 + offset_y, 82 + offset_x, 90 + offset_y, color1);
tft.fillTriangle(74 + offset_x, 108 + offset_y, 70 + offset_x, 107 + offset_y, 82 + offset_x, 90 + offset_y, color1);
tft.fillTriangle(90 + offset_x, 107 + offset_y, 77 + offset_x, 90 + offset_y, 82 + offset_x, 90 + offset_y, color1);
tft.fillTriangle(90 + offset_x, 106 + offset_y, 85 + offset_x, 107 + offset_y, 79 + offset_x, 93 + offset_y, color1);
tft.fillTriangle(83 + offset_x, 108 + offset_y, 94 + offset_x, 75 + offset_y, 98 + offset_x, 81 + offset_y, color1);
tft.fillTriangle(86 + offset_x, 106 + offset_y, 91 + offset_x, 107 + offset_y, 97 + offset_x, 76 + offset_y, color1);
}
void drawBot(bool activated) {
if (current_screen == 0) { // speed screen
if (activated) {
drawSensorMenu(sensor_item);
}
else {
drawScreeen0(SCREEN_BIG);
}
}
else if (current_screen == 1) { //4Sensors menu
if (activated) {
drawSensorMenu(sensor_item);
}
else {
drawScreeen1(selected_item);
}
}
else if (current_screen == 2) { //7Sensors menu
if (activated) {
drawSensorMenu(sensor_item);
}
else {
drawScreeen2(selected_item);
}
}
else if (current_screen == 3) { // options menu
if (activated) {
clearBot();
Serial.print("menu selection: ");
Serial.println(selected_item);
drawMenuItems(selected_item);
select_state = true;
activated_state = false;
}
else {
drawScreeen3(select_state, selected_item);
}
}
}
// in this function I will add all the available sensors to read
// add timers for slow changing items, average maybe?
// probably through CAN also if there is something
void readSensors() {
SENSOR_VALUES[0] = random(850, 870); //RPM
SENSOR_VALUES[1] = random(25, 30); //IAT
SENSOR_VALUES[2] = random(50, 60); //TPS
SENSOR_VALUES[3] = random(85, 87); //CLT
SENSOR_VALUES[4] = random(850, 870); //EGT
SENSOR_VALUES[5] = random(1300, 1310); //MAP
SENSOR_VALUES[6] = random(380, 390); //MAF
if (speedDIR == 0) {
SENSOR_VALUES[7]++;
if (SENSOR_VALUES[7] >= 200) {
speedDIR = 1;
}
}
else {
SENSOR_VALUES[7]--;
if (SENSOR_VALUES[7] <= 0) {
speedDIR = 0;
}
}
//SENSOR_VALUES[7] = random(380, 390); //SPD
}
void drawScreeen0(int item) {
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setCursor(110 + offset_x, 100 + offset_y);
tft.setTextSize(1);
tft.print(SENSOR_UNITS[item]);
tft.setCursor(35 + offset_x, 75 + offset_y);
tft.setTextSize(4);
if (SENSOR_VALUES[item] < 10) {
tft.print(" "); //add 2 spaces
tft.print(SENSOR_VALUES[item]);
}
else if (SENSOR_VALUES[item] < 100) {
tft.print(" "); //add 1 space
tft.print(SENSOR_VALUES[item]);
}
else {
tft.print(SENSOR_VALUES[item]);
}
}
void drawScreeen1(int item) {
tft.drawLine(80 + offset_x, 60 + offset_y, 80 + offset_x, 118 + offset_y, TFT_RED);
tft.drawLine(7 + offset_x, 89 + offset_y, 153 + offset_x, 89 + offset_y, TFT_RED);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setTextDatum(MR_DATUM); //middle right text aligment on point
for (int i = 0; i < 4; i++) {
if (i == item) { //check for selected item and change color for it
tft.setTextColor(TFT_RED, TFT_BLACK);
}
else {
tft.setTextColor(TFT_WHITE, TFT_BLACK);
}
tft.setTextSize(1);
tft.drawString(SENSOR_NAMES[SCREEN_SENSOR4_SELECTION[i]], sens4_xy[i][0] + offset_x, sens4_xy[i][1] + offset_y - 14);
tft.setTextSize(2);
tft.drawNumber(SENSOR_VALUES[SCREEN_SENSOR4_SELECTION[i]], sens4_xy[i][0] + offset_x, sens4_xy[i][1] + offset_y);
}
}
void drawScreeen2(int item) {
tft.setTextSize(1);
tft.setTextDatum(ML_DATUM); //middle left text aligment on point
tft.setTextColor(TFT_WHITE, TFT_BLACK);
for (int i = 0; i < 7; i++) {
if (i == item) {
tft.setTextColor(TFT_RED, TFT_BLACK);
}
else {
tft.setTextColor(TFT_WHITE, TFT_BLACK);
}
tft.drawString(SENSOR_NAMES[SCREEN_SENSOR7_SELECTION[i]], 14 + offset_x, 66 + offset_y + (i * 8)); //font size is 7pixels so +8
tft.drawNumber(SENSOR_VALUES[SCREEN_SENSOR7_SELECTION[i]], 65 + offset_x, 66 + offset_y + (i * 8)); //font size is 7pixels so +8
}
}
void drawScreeen3(bool activated, int item) {
tft.setTextSize(2);
tft.setTextDatum(ML_DATUM); //middle left text aligment on point
tft.setTextColor(TFT_WHITE, TFT_BLACK);
int previous = item - 1;
if (previous < 0) {
previous = MENU_ITEMS_NUMBER; //previous item would be below first = make it the last
}
int next = item + 1;
if (next > MENU_ITEMS_NUMBER) {
next = 0; // next item would be after last = make it the first
}
if (activated) {
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.drawString(MENU_NAMES[previous], 14 + offset_x, 73 + offset_y);
tft.setTextColor(TFT_RED, TFT_BLACK);
tft.drawString(MENU_NAMES[item], 14 + offset_x, 73 + offset_y + 18);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.drawString(MENU_NAMES[next], 14 + offset_x, 73 + offset_y + 36);
}
else {
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.drawString(MENU_NAMES[MENU_ITEMS_NUMBER], 14 + offset_x, 73 + offset_y);
tft.drawString(MENU_NAMES[0], 14 + offset_x, 73 + offset_y + 18);
tft.drawString(MENU_NAMES[1], 14 + offset_x, 73 + offset_y + 36);
}
}
void drawSensorMenu(int item) {
tft.setTextSize(2);
tft.setTextDatum(ML_DATUM); //middle left text aligment on point
int previous = item - 1;
if (previous < 0) {
previous = SENSOR_NUMBER; //previous item would be below first = make it the last
}
int next = item + 1;
if (next > SENSOR_NUMBER) {
next = 0; // next item would be after last = make it the first
}
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.drawString(SENSOR_NAMES[previous], 14 + offset_x, 73 + offset_y);
tft.setTextColor(TFT_RED, TFT_BLACK);
tft.drawString(SENSOR_NAMES[item], 14 + offset_x, 73 + offset_y + 18);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.drawString(SENSOR_NAMES[next], 14 + offset_x, 73 + offset_y + 36);
}
void assignSensorItem() {
if (current_screen == 0) {
SCREEN_BIG = sensor_item; //main big
}
if (current_screen == 1) {
SCREEN_SENSOR4_SELECTION[selected_item] = sensor_item;
} //4sensor
if (current_screen == 2) {
SCREEN_SENSOR7_SELECTION[selected_item] = sensor_item;
} //7sensor
}
void drawMenuItems(int item) {
switch (item) {
case 0:
Serial.println("Diag can");
break;
case 1:
Serial.println("Diag K-line");
break;
case 2:
Serial.println("Brightness");
setBrightness();
break;
case 3:
Serial.println("Time");
setTime();
break;
case 4:
Serial.println("FW Update");
break;
}
}
void setTime() {
while (1) {
static bool set_time = false;
static int time_selection = 0;
tft.setTextDatum(MC_DATUM);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.drawString("Time", 80 + offset_x, 73 + offset_y);
tft.drawString(rtc.getTime("-"), 74 + offset_x, 90 + offset_y); // first - for date
tft.drawString(rtc.getTime("-"), 110 + offset_x, 90 + offset_y); // second - for date
tft.drawString(rtc.getTime(":"), 62 + offset_x, 110 + offset_y); // first : for time
tft.drawString(rtc.getTime(":"), 98 + offset_x, 110 + offset_y); // second : for time
if (set_time && time_selection == 0) {
tft.setTextColor(TFT_RED, TFT_BLACK);
tft.drawString(rtc.getTime("%G"), 44 + offset_x, 90 + offset_y); //year
}
else {
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.drawString(rtc.getTime("%G"), 44 + offset_x, 90 + offset_y); //year
}
if (set_time && time_selection == 1) {
tft.setTextColor(TFT_RED, TFT_BLACK);
tft.drawString(rtc.getTime("%m"), 92 + offset_x, 90 + offset_y); //month
}
else {
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.drawString(rtc.getTime("%m"), 92 + offset_x, 90 + offset_y); //month
}
if (set_time && time_selection == 2) {
tft.setTextColor(TFT_RED, TFT_BLACK);
tft.drawString(rtc.getTime("%d"), 128 + offset_x, 90 + offset_y); //day
}
else {
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.drawString(rtc.getTime("%d"), 128 + offset_x, 90 + offset_y); //day
}
if (set_time && time_selection == 3) {
tft.setTextColor(TFT_RED, TFT_BLACK);
tft.drawString(rtc.getTime("%H"), 44 + offset_x, 110 + offset_y); //hour
}
else {
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.drawString(rtc.getTime("%H"), 44 + offset_x, 110 + offset_y); //hour
}
if (set_time && time_selection == 4) {
tft.setTextColor(TFT_RED, TFT_BLACK);
tft.drawString(rtc.getTime("%M"), 80 + offset_x, 110 + offset_y); //minutes
}
else {
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.drawString(rtc.getTime("%M"), 80 + offset_x, 110 + offset_y); //minutes
}
if (set_time && time_selection == 5) {
tft.setTextColor(TFT_RED, TFT_BLACK);
tft.drawString(rtc.getTime("%S"), 116 + offset_x, 110 + offset_y); //seconds
}
else {
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.drawString(rtc.getTime("%S"), 116 + offset_x, 110 + offset_y); //seconds
}
Serial.print("set_time: ");
Serial.print(set_time);
Serial.print(" selection: ");
Serial.println(time_selection);
btnBACK.read();
if (btnBACK.wasPressed()) {
if (set_time) {
time_selection--;
if (time_selection < 0) {
set_time = false;
time_selection = 0;
}
}
else {
clearBot();
break;
}
}
btnENTER.read();
if (btnENTER.wasPressed()) {
if (set_time) {
time_selection++;
}
else {
set_time = true;
}
if (time_selection > 5) {
set_time = false;
time_selection = 0;
}
}
btnUP.read();
if (btnUP.wasPressed()) {
if (set_time) {
switch (time_selection) {
case 0://year
rtc.setTime(rtc.getSecond(), rtc.getMinute(), rtc.getHour(true), rtc.getDay(), rtc.getMonth() + 1, rtc.getYear() + 1);
break;
case 1://month
rtc.setTime(rtc.getSecond(), rtc.getMinute(), rtc.getHour(true), rtc.getDay(), rtc.getMonth() + 2, rtc.getYear());
break;
case 2://day
rtc.setTime(rtc.getSecond(), rtc.getMinute(), rtc.getHour(true), rtc.getDay() + 1, rtc.getMonth() + 1, rtc.getYear());
break;
case 3://hour
rtc.setTime(rtc.getSecond(), rtc.getMinute(), rtc.getHour(true) + 1, rtc.getDay(), rtc.getMonth() + 1, rtc.getYear());
break;
case 4://minute
rtc.setTime(rtc.getSecond(), rtc.getMinute() + 1, rtc.getHour(true), rtc.getDay(), rtc.getMonth() + 1, rtc.getYear());
break;
case 5://second
rtc.setTime(rtc.getSecond() + 1, rtc.getMinute(), rtc.getHour(true), rtc.getDay(), rtc.getMonth() + 1, rtc.getYear());
break;
}
}
}
btnDOWN.read();
if (btnDOWN.wasPressed()) {
if (set_time) {
switch (time_selection) {
case 0://year
rtc.setTime(rtc.getSecond(), rtc.getMinute(), rtc.getHour(true), rtc.getDay(), rtc.getMonth() + 1, rtc.getYear() - 1);
break;
case 1://month
rtc.setTime(rtc.getSecond(), rtc.getMinute(), rtc.getHour(true), rtc.getDay(), rtc.getMonth(), rtc.getYear());
break;
case 2://day
rtc.setTime(rtc.getSecond(), rtc.getMinute(), rtc.getHour(true), rtc.getDay() - 1, rtc.getMonth() + 1, rtc.getYear());
break;
case 3://hour
rtc.setTime(rtc.getSecond(), rtc.getMinute(), rtc.getHour(true) - 1, rtc.getDay(), rtc.getMonth() + 1, rtc.getYear());
break;
case 4://minute
rtc.setTime(rtc.getSecond(), rtc.getMinute() - 1, rtc.getHour(true), rtc.getDay(), rtc.getMonth() + 1, rtc.getYear());
break;
case 5://second
rtc.setTime(rtc.getSecond() - 1, rtc.getMinute(), rtc.getHour(true), rtc.getDay(), rtc.getMonth() + 1, rtc.getYear());
break;
}
}
}
}
}
void setBrightness() {
while (1) {
tft.setTextDatum(MC_DATUM);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.drawString("Brightness", 80 + offset_x, 73 + offset_y);
tft.drawNumber(brightness, 80 + offset_x, 105 + offset_y);
btnBACK.read();
if (btnBACK.wasPressed()) {
clearBot();
break;
}
btnENTER.read();
if (btnENTER.wasPressed()) {
clearBot();
break;
}
btnUP.read();
if (btnUP.wasPressed()) {
brightness++;
if (brightness > 9) {
brightness = 9;
}
}
btnDOWN.read();
if (btnDOWN.wasPressed()) {
brightness--;
if (brightness < 1) {
brightness = 1;
}
}
}
}