#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 12
#define DOWN_PIN 13
#define ENTER_PIN 14
#define BACK_PIN 27
/*
//for real board
#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);
tft.fillRect(1+offset_x, 1+offset_y, 158, 23, TFT_BLACK); //reset top
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(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();
}
}
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();
}
}
}
void clearBot(){
tft.fillSmoothRoundRect(8+offset_x, 61+offset_y, 145, 57, 5, TFT_BLACK, 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);}
}
}
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");
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;}
}
}
break;
case 3:
Serial.println("Time");
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;
}
}
}
}
break;
case 4:
Serial.println("FW Update");
break;
}
}