How to simplify this code...?

Hi all!

I have an Arduino running that does two things;

  1. It operates as a trip computer for my car, displaying information on a tiny OLED screen
  2. It operates as an OBD-II CAN Bus interface with my car engine ECU.

It all works ok and I’m now trying to optimise and simplify the code where I can and there is one bit that I’m struggling with.

This relates to the OLED screen. It takes a long time in Arduino terms to update the screen, and it blocks other operations (like serial and CAN interfaces) so I would rather only update the screen when it needs to.

There are two conditions when it would need to be updated:

  1. If some data has changed
  2. If I want to change the page (there are 15 menu pages or so)

At the moment, I’ve got a separate function for each menu page to clear the screen, then format and add data as necessary. I’ve got a variable for each piece of data and another variable for what the data was last time the screen was updated. As such, before updating the OLED for each function, I check whether any data for that page has changed, or whether I’m looking to change the menu page. As a fall back, I update the screen periodically just in case I missed something.

This works fine, but there are a lot of “PREV” variables used for comparing old vs new data and it looks inelegant to me, but I can’t think of a better way to do it. I’m sure there are a lot smarter people on here than me though so hopefully someone can help me understand how to approach this better?

//CAN Enabled Dash Software - Last Updated 20 Apr 2021 - Nick Simpson
//This version uses the Test Board Menu Button Input!!

#include <EEPROM.h>
#include <Bounce2.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Wire.h>
#include <FlexCAN.h> //Can Bus
#include <AnalogSmooth.h>

//=======================================================================START GLOBAL VARIABLES
//--------------------------------------------------------Global Variables
//Used by CAN
int RPM;
int SPEED;
int OIL_TEMP;
int COOLANT_TEMP;
int AMBIENT_TEMP;
int INTAKE_TEMP;
int BATT_VOLTAGE; //float BATT_VOLTAGE = 0;
int FUEL_PRESSURE;
int FUEL_RATE;  //float FUEL_RATE;
int THROTTLE_POS;

//Analogue Only
float FUEL_LEVEL = 0;
int OIL_PRESS = 0;

//REMOVE THESE
float SPEED_KPH;
int SPEED_MPH = 0;

//Pins
const int MenuPin = 20;     // the pin number of the Menu button
int ShutDownPin = 11;  // read analogue voltage on pin A0 into Shutdown
int MWarnPin = A0;  //SUPPOSED TO BE ECU ON pin but no reading
int OilP_Pin = 21;
int Gear_Pin = 15;
int AirT_Pin = 16;

//Modes
int ShutDown = 0;
int DarkMode = 0;

//Menus
int timerstart; //0-60
int timerstop; //0-60
char flash;
char textflashoil;
char textflashwater;
char textflashoilp;
char textflashfuel;

int menuincrement;
int OIL_TEMP_TEXT;
int WATER_TEXT;
int RPM_TEXT;
int INTAKE_TEXT;
int distance_since_service;
int service_interval = 6000;
int distance_to_service;

//Gear Position
int GEAR_POS; //was float
String GPrev;
int GearChange = 0;
int GearSetupMode = 0;
unsigned long GearTime;
unsigned long GearShowTime = 499; //Time in milliseconds to show gear on shift
int SetGear = 0;
int GearWindow = 6; //was 6 0-255 +- Gear position capture
int lastmenu;
String GEAR = "N";
//----

//Send Receive Timer
unsigned long Last_Send_Receive = 10;
unsigned long Serial_Interval = 5;
unsigned long Send_Receive_Timer = 0;

//Time Variables------------
float distm;
float meterssubtotal;
unsigned long kphspeed;
float SPEED_MPS;
unsigned long currentMillis = 0;
unsigned long ODOmillis;
unsigned long Fuelmillis;
unsigned long Startmillis;
unsigned long flashmillis;
unsigned long Finishmillis;
unsigned long DTCmillis;
unsigned long DTCclearmillis;
unsigned long timeto60;
unsigned long DoneTime;
float timeseconds;
//----

//MENU------------------------
int Menubutton = 0;         // int for reading pushbutton state
int buttonPressedMillis;
int buttonPressedDuration;
String servicetext;
int BlinkState = LOW;
int menupressed = 0;
int SPEED_DISPLAY = 0;
float SPEED_MULTIPLIER = 1;
boolean ResetSteppers = 0;
boolean Done = 0;
// Instantiate a Bounce object :
Bounce menubutton = Bounce();
unsigned long buttonPressTimeStamp;
int  menu = 1;

int ChangeSource = 0;
int CheckEngineOn = 0;

//=======================================================================END GLOBAL VARIABLES

//=======================================================================START EEPROM RELATED VARIABLES
// start reading from the first byte (address 0) of the EEPROM
unsigned int address = 0;
byte value;
int starteeprom;

int EEPROMAddress;         //S1
int EEPROMWriteCount;      //S2
int tripdisthundredm;      //S3
int tripdistkm;            //S4
int tripdisthundredkm;     //S5
int tripdistthousandkm;    //S6
int disthundredm;          //S7
int distkm;                //S8
int distenkm;              //S9
int disthundredkm;         //S10
int disthousandkm;         //S11
int disttenthousandkm;     //S12
int disthundredthousandkm; //S13
float Best060;             //S14
float PrevMPG;             //S15
int MaxRPM;                //S16
int MaxSpeed;              //S17
int MaxTemp;               //S18
float MaxOil;              //S19
int servicedisthundredm;   //S20
int servicedistkm;         //S21
int servicedisthundredkm;  //S22
int servicedistthousandkm; //S23
int MenuMem;               //S24
int Neutralp;              //S25
int Gear1p;                //S26
int Gear2p;                //S27
int Gear3p;                //S28
int Gear4p;                //S29
int Gear5p;                //S30
int Gear6p;                //S31
int Reversep;              //S32
int MODE_SPEED;            //S33 1 is serial mode, 2 is CAN mode
int MODE_RPM;              //S34 1 is serial mode, 2 is CAN mode
int MODE_COOLANT_TEMP;     //S35 1 is serial mode, 2 is CAN mode
int MODE_OIL_TEMP;         //S36 1 is serial mode, 2 is CAN mode
int MODE_AMBIENT_TEMP;     //S37 1 is serial mode, 2 is CAN mode

int MODE_INTAKE_TEMP = 2; //not currently saved in EEPROM (no need)
int MODE_THROTTLE_POS = 2; //not currently saved in EEPROM (no need)
int MODE_FUEL_PRESSURE = 2; //not currently saved in EEPROM (no need)
int MODE_FUEL_RATE = 2; //not currently saved in EEPROM (no need)
int MODE_BATT_VOLTAGE = 2; //not currently saved in EEPROM (no need)
int MODE_numDTC = 2; //not currently saved in EEPROM (no need)

// set the epprom addresses for the settings that we are keeping
#define E_INIT         1023
// the setting array size must match the settings number
uint32_t settings[23];
// settings addresses
#define S1   1020
#define S2   3
#define S3   6
#define S4   9
#define S5   12
#define S6   15
#define S7   18
#define S8   21
#define S9   24
#define S10  27
#define S11  30
#define S12  33
#define S13  36
#define S14  39
#define S15  42
#define S16  45
#define S17  48
#define S18  51
#define S19  54
#define S20  57
#define S21  60
#define S22  63
#define S23  66
#define S24  69
#define S25  72
#define S26  75
#define S27  78
#define S28  81
#define S29  84
#define S30  87
#define S31  90
#define S32  93
#define S33  96
#define S34  99
#define S35  102
#define S36  105
#define S37  108
#define S38  111
int EEPROM_OFFSET = 114;

//=======================================================================END EEPROM RELATED VARIABLES

//=======================================================================START CAN BUS RELATED VARIABLES
//------------CAN PIDs------------
/* Details from http://en.wikipedia.org/wiki/OBD-II_PIDs */
#define MODE1               0x01        //Show current data
#define MODE2               0x02        //Show freeze frame data
#define MODE3               0x03        //Show stored Diagnostic Trouble Codes
#define MODE4               0x04        //Clear Diagnostic Trouble Codes and stored values

#define PID_SUPPORTED       0x00
#define PID_MONITOR_STATUS  0x01
#define PID_SPEED           0x0D
#define PID_RPM             0x0C
#define PID_OIL_TEMP        0x5C
#define PID_COOLANT_TEMP    0x05
#define PID_INTAKE_TEMP     0x0F
#define PID_AMBIENT_TEMP    0x46
#define PID_BATT_VOLTAGE    0x42
#define O2_VOLTAGE_1        0x24
#define O2_VOLTAGE_2        0x25
#define PID_FUEL_PRESSURE   0x0A
#define PID_FUEL_RATE       0x5E
#define PID_THROTTLE_POS    0x11

#define MODE1_RESPONSE      0x41
#define MODE3_RESPONSE      0x43
#define MODE4_RESPONSE      0x44
#define PID_REQUEST         0x7DF
#define PID_REPLY           0x7E8
#define DTC_PID             0x00

//------------DIAGNOSTIC SERIAL DUMP------------
static uint8_t hex[17] = "0123456789abcdef";
static void hexDump(uint8_t dumpLen, uint8_t *bytePtr)
{
  uint8_t working;
  while ( dumpLen-- ) {
    working = *bytePtr++;
    Serial.write( hex[ working >> 4 ] );
    Serial.write( hex[ working & 15 ] );
  }
  Serial.write('\r');
  Serial.write('\n');
}

//------------CAN Variables------------
byte PID_CALL;
byte PID;
byte MODE;
byte PID_RESPONSE;
CAN_message_t can_MsgRx, can_MsgTx;
bool CANWaiting = false;
int CAN_Retry = 0;

//------------Structure Arrray Initialisation------------
struct OBD {
  String Name;
  word   PID;
  int*  Active;
  word   MODE;
  int*   Data;
  int    NoReply;
};

//------------DTC Variables------------
int DTC = 2;
int numDTC;
int DTC1Raw;
int DTC2Raw;
char DTC1 [5];
char DTC2 [5];
int SeenDTC = 0;
int DTCp1;
int DTCp2;
int DTC_CLEARED = 0;

//------------CAN Time Variables------------

//For Serial/CAN
unsigned long CAN_Request_Millis = 10;
unsigned long CAN_Timeout = 100; //CHANGE TO 100
unsigned long CAN_Wait_Duration = 0;

//------------Set Up Structure Array------------
int Row = 0;
// *******************************************************************************************************************************************************SET UP REQUIRED PIDS (currently all)
OBD OBDArray[] = {{"RPM", PID_RPM, &MODE_RPM, MODE1, &RPM, 0}, // Overwritten by EEPROM saved state
  {"SPEED", PID_SPEED, &MODE_SPEED, MODE1, &SPEED, 0}, // Overwritten by EEPROM saved state
  {"COOLANT_TEMP", PID_COOLANT_TEMP, &MODE_COOLANT_TEMP, MODE1, &COOLANT_TEMP, 0}, // Overwritten by EEPROM saved state
  {"OIL_TEMP", PID_OIL_TEMP, &MODE_OIL_TEMP, MODE1, &OIL_TEMP, 0}, // Overwritten by EEPROM saved state
  {"AMBIENT_TEMP", PID_AMBIENT_TEMP, &MODE_AMBIENT_TEMP, MODE1, &AMBIENT_TEMP, 0}, // Overwritten by EEPROM saved state
  {"INTAKE_TEMP", PID_INTAKE_TEMP, &MODE_INTAKE_TEMP, MODE1, &INTAKE_TEMP, 0},
  {"THROTTLE_POS", PID_THROTTLE_POS, &MODE_THROTTLE_POS, MODE1, &THROTTLE_POS, 0},
  {"FUEL_PRESSURE", PID_FUEL_PRESSURE, &MODE_FUEL_PRESSURE, MODE1, &FUEL_PRESSURE, 0},
  {"FUEL_RATE", PID_FUEL_RATE, &MODE_FUEL_RATE, MODE1, &FUEL_RATE, 0},
  {"BATT_VOLTAGE", PID_BATT_VOLTAGE, &MODE_BATT_VOLTAGE, MODE1, &BATT_VOLTAGE, 0},
  {"numDTC", DTC_PID, &MODE_numDTC, MODE3, &numDTC, 0}
};
byte Arraysize = sizeof(OBDArray) / sizeof(OBDArray[0]);
//=======================================================================END CAN BUS RELATED VARIABLES

//=======================================================================START SERIAL RELATED VARIABLES

unsigned long Serial1millis;
unsigned long Serial2millis;

//Serial
int SPEED_SEND;
int RPM_SEND;
int CT_SEND;
int FL_SEND;
int DarkMode_SEND = 0;
int ShutDown_SEND = 0;
int CAN_MODES_SEND;
int UpdateSerial1 = 0;
int UpdateSerial2 = 0;
int LightsOn = 0;

//Serial1 Receive--------------------------------
const byte S1numChars = 32;
char S1receivedChars[S1numChars];
char S1tempChars[S1numChars];        // temporary array for use when parsing
static boolean S1recvInProgress = false;
static byte S1ndx = 0;
char S1startMarker = '<';
char S1endMarker = '>';
char S1Received;
boolean S1newData = false;

//Serial2 Receive--------------------------------
const byte S2numChars = 32;
char S2receivedChars[S2numChars];
char S2tempChars[S2numChars];        // temporary array for use when parsing
static boolean S2recvInProgress = false;
static byte S2ndx = 0;
char S2startMarker = '<';
char S2endMarker = '>';
char S2Received;
boolean S2newData = false;

//=======================================================================END SERIAL RELATED VARIABLES

//=======================================================================START ANALOGSMOOTH RELATED VARIABLES
float damping_coefficient = 0.01;
float Hz = 0;
int i = 1;
// Defaults to window size 10
//AnalogSmooth as = AnalogSmooth();

// Window size can range from 1 - 100
AnalogSmooth as100 = AnalogSmooth(100);
AnalogSmooth as101 = AnalogSmooth(100);
AnalogSmooth as102 = AnalogSmooth(100);
AnalogSmooth as50 = AnalogSmooth(5);
AnalogSmooth asQ = AnalogSmooth(5);
//=======================================================================END ANALOGSMOOTH RELATED VARIABLES

//=======================================================================START OLED SCREEN RELATED VARIABLES
#define OLED_RESET  -1
Adafruit_SSD1306 display(128, 32, &Wire, OLED_RESET);
//=======================================================================END OLED SCREEN RELATED VARIABLES

//=======================================================================START SPLASH SCREEN RELATED VARIABLES
unsigned int ShowServiceTime = 5000;
unsigned int ShowODOTime = 8000;
unsigned int ShowMenuMem = 9000;

#define LOGO_HEIGHT   27
#define LOGO_WIDTH    77
static const unsigned char PROGMEM logo_bmp[] =
{ 0x3F, 0xFF, 0xFF, 0xFF, 0x80, 0x1F, 0xFF, 0xFF, 0xF0, 0x00, //   ###############################          #########################
  0x3F, 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xFF, 0xF8, 0x00, //   ###############################         ###########################
  0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xFF, 0xFE, 0x00, //  ################################         #############################
  0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xFE, 0x00, //  ################################        ##############################
  0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, //  ################################        ###############################
  0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, //  ################################       ################################
  0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, // #################################       #################################
  0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, // #################################       #################################
  0x00, 0x00, 0x00, 0xFF, 0xC1, 0xFF, 0xC0, 0x01, 0xFF, 0x80, //                         ##########     ###########             ##########
  0x00, 0x00, 0x00, 0xFF, 0xC1, 0xFF, 0x80, 0x01, 0xFF, 0x80, //                         ##########     ##########              ##########
  0x00, 0xFF, 0xC0, 0xFF, 0xC3, 0xFF, 0x80, 0x03, 0xFF, 0x80, //         ##########      ##########    ###########             ###########
  0x00, 0xFF, 0x80, 0xFF, 0xC3, 0xFF, 0x07, 0xFF, 0xFF, 0x80, //         #########       ##########    ##########     ####################
  0x00, 0xFF, 0x80, 0x7F, 0xC7, 0xFF, 0x0F, 0xFF, 0xFF, 0x00, //         #########        #########   ###########    ####################
  0x01, 0xFF, 0x80, 0x7F, 0xC7, 0xFE, 0x0F, 0xFF, 0xFF, 0x00, //        ##########        #########   ##########     ####################
  0x01, 0xFF, 0x80, 0x7F, 0xCF, 0xFE, 0x0F, 0xFF, 0xFE, 0x00, //        ##########        #########  ###########     ###################
  0x01, 0xFF, 0x00, 0x7F, 0xCF, 0xFC, 0x0F, 0xFF, 0xFC, 0x00, //        #########         #########  ##########      ##################
  0x01, 0xFF, 0x00, 0x7F, 0xDF, 0xFC, 0x0F, 0xFF, 0xF8, 0x00, //        #########         ######### ###########      #################
  0x03, 0xFF, 0x00, 0x7F, 0xFF, 0xF8, 0x0F, 0xFF, 0xE0, 0x00, //       ##########         ####################       ###############
  0x03, 0xFF, 0x00, 0x7F, 0xFF, 0xF8, 0x07, 0xFF, 0x00, 0x00, //       ##########         ####################        ###########
  0x03, 0xFE, 0x00, 0x7F, 0xFF, 0xF0, 0x07, 0xFF, 0x00, 0x00, //       #########          ###################         ###########
  0x03, 0xFE, 0x00, 0x7F, 0xFF, 0xF0, 0x03, 0xFF, 0x80, 0x00, //       #########          ###################          ###########
  0x07, 0xFE, 0x00, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, 0xC0, 0x00, //      ##########          ##################            ###########
  0x07, 0xFE, 0x00, 0x3F, 0xFF, 0xE0, 0x01, 0xFF, 0xC0, 0x00, //      ##########           #################            ###########
  0x07, 0xFC, 0x00, 0x3F, 0xFF, 0xC0, 0x00, 0xFF, 0xE0, 0x00, //      #########            ################              ###########
  0x07, 0xFC, 0x00, 0x3F, 0xFF, 0xC0, 0x00, 0xFF, 0xE0, 0x00, //      #########            ################              ###########
  0x0F, 0xFC, 0x00, 0x3F, 0xFF, 0x80, 0x00, 0x7F, 0xF0, 0x00, //     ##########            ###############                ###########
  0x0F, 0xF8, 0x00, 0x3F, 0xFF, 0x80, 0x00, 0x7F, 0xF0, 0x00, //     #########             ###############                ###########
};
//=======================================================================END SPLASH SCREEN RELATED VARIABLES


// *******************************************************************************************************************************************************CHECK THE CODE FOR WHERE THESE ARE USED AND REWRITE SO THEY AREN'T NEEDED (USING ARRAY?)
int RPM_CAN;
int SPEED_MPH_CAN;
int OIL_TEMP_CAN;
int COOLANT_TEMP_CAN;
int AMBIENT_TEMP_CAN;
int INTAKE_TEMP_CAN;
float BATT_VOLTAGE_CAN;
int FUEL_PRESSURE_CAN;
float FUEL_RATE_CAN;
int THROTTLE_POS_CAN;
byte MONITOR_STATUS;
byte MONITOR_STATUS_CAN;

int RPM_SER;
int SPEED_SER;
int OIL_TEMP_SER;
int COOLANT_TEMP_SER;
int COOLANT_TEMP_D;
int FUEL_LEVEL_SER;
int FUEL_LEVEL_D;
int AMBIENT_TEMP_SER;
int OIL_PRESS_SER;

//------------------------------------------------





// *******************************************************************************************************************************************************IS THERE A SMARTER WAY TO ONLY UPDATE THE OLED WHEN NEEDED?

//PREVS
int SPEED_MPH_PREV;
int RPM_PREV;
int distkm_PREV;
int tripdisthundredm_PREV;
int AMBIENT_TEMP_PREV;
String GEAR_PREV;
int INTAKE_TEMP_PREV;
int OIL_TEMP_PREV;
int ChangeSourceTo_PREV;
int OIL_TEMP_TEXT_PREV;
int WATER_TEXT_PREV;
int RPM_TEXT_PREV;
int INTAKE_TEXT_PREV;
int SetGear_PREV;
int COOLANT_TEMP_PREV;
int OIL_PRESS_PREV;
int FUEL_PRESSURE_PREV;
int textflashoilp_PREV;
float FUEL_LEVEL_PREV;
float FUEL_RATE_PREV;
float BATT_VOLTAGE_PREV;
int numDTC_PREV;
int DTC_PREV;
int MaxSpeed_PREV;
int MaxRPM_PREV;
int MaxTemp_PREV;
int MaxOil_PREV;
int distance_to_service_PREV;
int mode060_PREV;
int mode060;
int menu_PREV;
int DarkMode_PREV;
boolean Done_PREV = 0;
int BlinkState_PREV = 0;

//=======================================================================END SET UP VARIABLES



void setup(void) {//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++START SETUP

  Serial.begin(9600);
  Serial1.begin(250000);
  Serial2.begin(250000);
  Can0.begin(500000);
  Wire.setClock(3400000);
  loadEEPROMSettings();

  //----------------------------------------------------------------------MENU BUTTON
  pinMode( MenuPin, INPUT_PULLUP );
  // After setting up the button, setup the Bounce instance :
  menubutton.attach(MenuPin, INPUT_PULLUP) ;
  menubutton.interval(60); // De-bounce time of x milliseconds
  menu = 1; // set default menu display for oled


  //----------------------------------------------------------------------MISC
  timerstart = 0;
  timerstop = 0;
  starteeprom = 0;
  pinMode(ShutDownPin, INPUT_PULLUP);
  pinMode(MWarnPin, INPUT_PULLUP);
  pinMode(Gear_Pin, INPUT_PULLDOWN);
  pinMode(AirT_Pin, INPUT_PULLDOWN);

  //----------------------------------------------------------------------INITIATE SCREEN
  Serial.println("Starting OLED");
  display.begin(SSD1306_SWITCHCAPVCC);
  display.clearDisplay();
  drawTVR();
  delay(2500); //Show TVR Splash screen
  //----------------------------------------------------------------------INITIATE SERIVCE INTERVAL

  distance_since_service = (servicedistthousandkm * 1000) + (servicedisthundredkm * 100) + (servicedistkm * 10) + servicedisthundredm  ;
  distance_to_service = service_interval - distance_since_service;

  if   (distance_since_service <= service_interval) {

    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(10, 25);
    display.println("T O  S E R V I C E");
    display.setTextSize(2);
    display.setCursor(42, 3);
    display.println(distance_to_service);
    display.display();
  }

  if   (distance_since_service > service_interval) {
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(5, 25);
    display.println("S E R V I C E  D U E"); //THIS LOOKS LIKE IT WONT FIT
    display.setTextSize(2);
    display.setCursor(35, 3);
    display.println(distance_to_service);
    display.setTextSize(2);
    display.setCursor(0, 0);
    display.println("**");
    display.setCursor(98, 0);
    display.println("**");
    display.display();
  }

  ODOmillis = (0);
  Fuelmillis = (0);

}//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++END SETUP


void loop(void) {//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++START LOOP
  currentMillis = millis();
  Send_Receive_Timer = currentMillis - Last_Send_Receive;
  // *******************************************************************************************************************************************************THIS TIMER PROBABLY DOESN'T NEED TO APPLY TO THE CAN FUNCTION
  if (Send_Receive_Timer > Serial_Interval)
  {
    //===========SERIAL OR CAN SET BASED ON MODES=======================
    //----------------------------------------------------------------------READ SERIAL MONITOR (menu control)
    if (Serial.available()) keyboard();
    //----------------------------------------------------------------------READ SERIAL1
    Serial1Receive();
    if (S1newData == true) {
      strcpy(S1tempChars, S1receivedChars);
      Serial1Parse();
      //showParsedData(); //DIAGNOSTICS ONLY
      S1newData = false;
    }
    //----------------------------------------------------------------------READ SERIAL2
    Serial2Receive();
    if (S2newData == true) {
      strcpy(S2tempChars, S2receivedChars);
      Serial2Parse();
      S2newData = false;
    }
  }

  //----------------------------------------------------------------------READ CAN BUS
  Get_CAN_Data();
  //Print_CAN_Data();//--------------------------Diagnostic only

  if (Send_Receive_Timer > Serial_Interval)
  {
    //----------------------------------------------------------------------Write SERIAL
    Send_Serial1_Data();
    Send_Serial2_Data();
    Last_Send_Receive = currentMillis;
  }



  //----------------------------------------------------------------------READ ANALOGUE INPUTS
  //-------------------------------------------READ FUEL LEVEL
  // *******************************************************************************************************************************************************NEED TO WRITE THIS
  //---------------------------------------------
  //----------------------------------------------------------------------ECM Check Engine Light Warning
  if (digitalRead(MWarnPin) == HIGH)CheckEngineOn = 1;
  if (digitalRead(MWarnPin) == HIGH)CheckEngineOn = 0;

  //----------------------------------------------------------------------Gear Position
  GEAR_POS = as50.analogReadSmooth(Gear_Pin); //constrain(as50.analogReadSmooth(Gear_Pin), 15, 1000);
  //GEAR_POS = constrain(asQ.analogReadSmooth(Gear_Pin), 15, 1000);
  GEAR_POS = map(GEAR_POS, 15, 1000, 0, 255);
  //  Serial.print("Current = ");
  //  Serial.println(GEAR_POS);
  //  Serial.print("Neutral = ");
  //  Serial.println(Neutralp);
  //  Serial.print("Gear1 = ");
  //  Serial.println(Gear1p);
  //  Serial.print("Gear2 = ");
  //  Serial.println(Gear2p);
  //  Serial.print("Gear3 = ");
  //  Serial.println(Gear3p);
  //  Serial.print("Gear4 = ");
  //  Serial.println(Gear4p);
  //  Serial.print("Gear5 = ");
  //  Serial.println(Gear5p);
  //  Serial.print("Gear6 = ");
  //  Serial.println(Gear6p);
  //  Serial.print("GearR = ");
  //  Serial.println(Reversep);
  //  if (ChangeSource == 1) ChangeSourceFunc();
  if (GearSetupMode == 1) Gear_Setup();
  if (GearSetupMode == 0 && millis() - GearTime > 100) GEARS();

  //----------------------------------------------------------------------Oil Pressure
  OIL_PRESS_SER = as100.analogReadSmooth(OilP_Pin) * 1.5; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ THIS COMES OUT WHEN NEW BOARD ARRIVES (FUEL ON OTHER BOARD)
  OIL_PRESS_SER = constrain(map(OIL_PRESS_SER, 120, 830, 60, 0), 0, 60);      // map
  OIL_PRESS += 0.1 * (OIL_PRESS_SER - OIL_PRESS);
  //Serial.println(OIL_PRESS_SER);

  //----------------------------------------------------------------------Ambient Temperature
  if (MODE_AMBIENT_TEMP == 1) {
    AMBIENT_TEMP_SER = as101.analogReadSmooth(AirT_Pin) * 1.5;
    AMBIENT_TEMP_SER = map(AMBIENT_TEMP_SER, 80, 1370, 130, 0);        // map
    AMBIENT_TEMP += 0.1 * (AMBIENT_TEMP_SER - AMBIENT_TEMP); //FILTER (smaller filter value (0.1) means more filtering, 1 is no filter)
  }

  //----------------------------------------------------------------------READ MENU PIN
  // Update the Bounce instance :
  menubutton.update();

  //Button Handling
  if (menubutton.fell()) buttonPressedMillis = millis();

  if (menubutton.rose()) buttonPressedDuration = millis() - buttonPressedMillis;

  //Button Action
  if (buttonPressedDuration) {

    //----------------------------------------------------------------------BUTTON CLICK FUNCTIONS
    if (buttonPressedDuration < 1000 && GearSetupMode == 0 && DarkMode == 1) DarkMode = 0;

    else if (buttonPressedDuration < 1000 && GearSetupMode == 0) {
      menupressed = 1;
      menu++;
    }

    else if (buttonPressedDuration < 1000 && GearSetupMode == 1) {

      if (SetGear == 1) Neutralp = GEAR_POS;
      if (SetGear == 2) Gear1p = GEAR_POS;
      if (SetGear == 3) Gear2p = GEAR_POS;
      if (SetGear == 4) Gear3p = GEAR_POS;
      if (SetGear == 5) Gear4p = GEAR_POS;
      if (SetGear == 6) Gear5p = GEAR_POS;
      if (SetGear == 7) Gear6p = GEAR_POS;
      if (SetGear == 8) Reversep = GEAR_POS;
      if (SetGear == 9) GearSetupMode = 0;
      SetGear ++;
    }

    //+++++++++++++++++++++++++++++++ SHORT BUTTON HOLD FUNCTIONS +++++++++++++++++++++++++++++++

    else if (buttonPressedDuration < 5000) {
      //---------- Normal Menu ----------
      if (menu == 1)  cleartrip();
      //if (menu == 4)  ; //RESET MPG
      else if (menu == 5) DarkMode = 1;
      else if (menu == 8) clearstats();
      else if (menu == 9) Best060 = 0;
      else if (menu == 14) {
        CAN_Request(MODE4, 0); //Clear Trouble Codes
        DTCclearmillis = millis();
        DTC_CLEARED = 1;
      }
      else if (menu == 15) menu = 21; //Enter setup sub-menu

      //---------- Setup Sub-Menu ----------
      else if (menu == 21) { // Speed
        if (SPEED_DISPLAY == 2) {
          SPEED_DISPLAY = 1;
          SPEED_MULTIPLIER = 1; //MPH currently set to *1  (* 0.621371 for KPH to MPH)
        }
        else if (SPEED_DISPLAY == 1) {
          SPEED_DISPLAY = 2;
          SPEED_MULTIPLIER = 1.60934; //KPH currently set to *1.60934
        }
        ChangeSource = 1;
      }

      //-----Source Change-----
      else if (menu == 22) { // Speed
        if (MODE_SPEED == 2) MODE_SPEED = 1;
        else if (MODE_SPEED == 1) MODE_SPEED = 2;
        ChangeSource = 1;
      }

      else if (menu == 23) { // RPM
        if (MODE_RPM == 2) MODE_RPM = 1;
        else if (MODE_RPM == 1) MODE_RPM = 2;
        ChangeSource = 1;
      }

      else if (menu == 24) {   //Coolant temp
        if (MODE_COOLANT_TEMP == 2) MODE_COOLANT_TEMP = 1;
        else if (MODE_COOLANT_TEMP == 1) MODE_COOLANT_TEMP = 2;
        ChangeSource = 1;
      }

      else if (menu == 25) {
        if (MODE_AMBIENT_TEMP == 2) MODE_AMBIENT_TEMP = 1;
        else if (MODE_AMBIENT_TEMP == 1) MODE_AMBIENT_TEMP = 2;
        ChangeSource = 1;
      }

      else if (menu == 26) {   //Oil temp
        if (MODE_OIL_TEMP == 2) MODE_OIL_TEMP = 1;
        else if (MODE_OIL_TEMP == 1) MODE_OIL_TEMP = 2;
        ChangeSource = 1;
      }

      else if (menu == 27) ResetSteppers = 1;
      else if (menu == 31) menu = 15;
    }

    //+++++++++++++++++++++++++++++++ LONG BUTTON HOLD FUNCTIONS +++++++++++++++++++++++++++++++
    else {
      //---------- Normal Menu ----------
      if (menu == 12) clearservice();

      //---------- Setup Sub-Menu ----------
      else if (menu == 27) {
        ResetSteppers = 1;
        DoneTime = millis();
        Done = 1;
      }
      else if (menu == 28) {
        GearSetupMode = 1;
        SetGear = 1;
      }
      else if (menu == 29) {
        clearODO();
        DoneTime = millis();
        Done = 1;
      }
      else if (menu == 30) menu = 35; //Alignment Box
    }

    //reset duration to only trigger once!
    buttonPressedDuration = 0;
  }

  //Show Odometer on startup
  if (ODOmillis < ShowODOTime) menu = 1;

  //Show memory page after startup
  if (ODOmillis > ShowODOTime && ODOmillis < ShowMenuMem && menupressed == 0) menu = MenuMem;

  //Menu Loop Normal
  if (menu == 16) menu = 1;
  //Menu Loop Setup
  if (menu == 32) menu = 21;
  //Alignment Box
  if (menu == 36) menu = 30;

  //Gearchange
  if (GearChange == 1 && millis() - GearTime < GearShowTime) menu = 3;
  if (GearChange == 1 && millis() - GearTime > GearShowTime) {
    menu = lastmenu;
    GearChange = 0;
  }

  //  //Change Sensor Source
  //  if (ChangeSource == 1 && millis() - ChangeSourceTime > ChangeSourceShowTime) ChangeSource = 0;


  //----------------------------------------------------------------------Update Odometer Counter and Display
  //
  if (millis() - ODOmillis >= 10) { //Update every 0.1s.
    if (millis() - ODOmillis >= 1000) { //Update every 1s
      SPEED_MPS = SPEED_MPH * 0.44704; //convert to distance meters per second
      distm = SPEED_MPS / 1 ; // spare conversion factor
      meterssubtotal += distm;  // add meters traveled to a total called meters
      updateodometer(); //add the meters traveled to the meters counter
      // Update lasmillis
      ODOmillis = millis();
    }

    if (ODOmillis > ShowServiceTime) { //Service timer display
      if (ShutDown != 1 && GearSetupMode != 1) { // && ChangeSource != 1
        checkdisplay(); // update lcd readings
      }
    }
  }


  //----------------------------------------------------------------------Update Max Stats
  if (SPEED_MPH > MaxSpeed) {
    MaxSpeed = SPEED_MPH;
  }
  if (RPM > MaxRPM) {
    MaxRPM = RPM;
  }
  if (COOLANT_TEMP > MaxTemp) {
    MaxTemp = COOLANT_TEMP;
  }
  if (OIL_TEMP > MaxOil) {
    MaxOil = OIL_TEMP;
  }

  //----------------------------------------------------------------------START EEPROM UPDATE (if igniton is killed)
  //BATT_VOLTAGE = asQ.analogReadSmooth(ShutDownPin);
  //Serial.println(BATT_VOLTAGE); //*15.699
  if (digitalRead(ShutDownPin) == LOW)starteeprom = 1;
  if (digitalRead(ShutDownPin) == HIGH  && starteeprom == 1) {


    ShutDown = 1;
    updateeeprom();
    starteeprom = 0;
    drawTVR();
    display.clearDisplay();
  }

  //----------------------------------------------------------------------START DTC CYCLE TEXT WARNING
  // *******************************************************************************************************************************************************CAN DTC HANDLE MORE THAN 2 CODES?
  if (millis() - DTCmillis > 1000) { //Update every one second.

    DTCmillis = millis(); // Update lasmillis
    if (DTC_CLEARED == 1 && (millis() - DTCclearmillis < 5000)) {
      DTC = 4;
    }
    else if (DTC_CLEARED == 1 && (millis() - DTCclearmillis > 5000)) {
      DTC = 1;
      DTC_CLEARED = 0;
    }
    else if (DTC == 1) DTC = 2;
    else if (numDTC > 2) {
      if (DTC == 3) DTC = 1;
      if (DTC == 2) DTC = 3;
    }
    else if (DTC == 2) DTC = 1;
  }

  //----------------------------------------------------------------------START FLASH FUNCTION FOR TEXT WARNING----------------------------

  if (millis() - flashmillis > 500) { //Update every half second.

    flashmillis = millis(); // Update lasmillis

    // if the LED is off turn it on and vice-versa:
    if (BlinkState == LOW) BlinkState = HIGH;
    else BlinkState = LOW;
  }

  if (BlinkState == 1)flash = BLACK;
  if (BlinkState == 0)flash = WHITE;

  //----------------------------------------------------------------------START ALARM CHANNELS==============================================================

  //------------------------DTC-------------------------------------------
  if (numDTC > 0 && SeenDTC == 0) {
    SeenDTC = 1;
    menu = 14;
  }
  else RPM_TEXT = WHITE;

  //------------------------RPM-------------------------------------------
  if (RPM >= 5500) {

    RPM_TEXT = flash;
    //MASTER WARNING LIGHT ON
  }
  else RPM_TEXT = WHITE;

  //------------------------INTAKE TEMP-------------------------------------------
  if (INTAKE_TEMP >= 50) {

    INTAKE_TEXT = flash;
    menu = 2;
    //MASTER WARNING LIGHT ON
  }
  else INTAKE_TEXT = WHITE;

  //------------------------OIL TEMP-------------------------------------------

  if (OIL_TEMP >= 110) {

    OIL_TEMP_TEXT = flash;
    menu = 6;
    //MASTER WARNING LIGHT ON
  }
  else OIL_TEMP_TEXT = WHITE;

  //------------------------WATER TEMP-------------------------------------------
  if (COOLANT_TEMP >= 100) {

    WATER_TEXT = flash;
    menu = 6;
    //MASTER WARNING LIGHT ON
  }
  else WATER_TEXT = WHITE;

  //------------------------OIL PRESS-------------------------------------------
  if (OIL_PRESS > 0 && OIL_PRESS < 20 && RPM > 2000) {
    textflashoilp = flash;
    menu = 7;
  }
  else textflashoilp = WHITE;

  if (OIL_PRESS > 0 && OIL_PRESS < 7) {
    textflashoilp = flash;
    menu = 7;
  }
  else textflashoilp = WHITE;


  //------------------------FUEL LEVEL-------------------------------------------
  if (millis() - Fuelmillis >= 500) { //Read temperatures every three seconds

    if (FUEL_LEVEL > 0 && FUEL_LEVEL < 6) {

      textflashfuel = flash;
      menu = 4;
    }
    else textflashfuel = WHITE;
  }

  //----------------------------------------------------------------------???
  if (millis() - DoneTime > 2000) {
    Done = 0;
    ResetSteppers = 0;
  }

  //----------------------------------------------------------------------Dim Display==================
  if (LightsOn == 1) display.dim(true);
  else display.dim(false);

}//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++END LOOP




//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++START FUNCTIONS

//=====================================START CAN BUS FUNTIONS====================================================
//-------------GET CAN DATA-------------
void Get_CAN_Data() {

  //Are we waiting for a response?
  if (CANWaiting == true) { // IS IT THE RESPONSE WE'RE WAITING FOR?? IF SO, SAVE IT AND MOVE ON
    if (Can0.available() > 0) {//Is a response waiting?
      Can0.read(can_MsgRx);
      if ((can_MsgRx.id == PID_REPLY) && ((can_MsgRx.buf[2] == OBDArray[Row].PID) || can_MsgRx.buf[1] == MODE3_RESPONSE)) //Is this message the response we're looking for?
      {
        //Yes
        if (can_MsgRx.buf[1] == MODE3_RESPONSE) {//Is the message an error code?
          //Serial.print("CAN ERROR RECEIVED: "); hexDump(8, can_MsgRx.buf); //Diagnostic - Dump message to Serial useful explanation: https://www.csselectronics.com/screen/page/simple-intro-obd2-explained/language/en
          numDTC = CAN_ERR_Receive(can_MsgRx.buf[2], can_MsgRx.buf[3], can_MsgRx.buf[4], can_MsgRx.buf[5], can_MsgRx.buf[6]);
        }
        else {//Normal PID Received
          //Serial.print("CAN RECEIVED: "); hexDump(8, can_MsgRx.buf); //Diagnostic - Dump message to Serial useful explanation: https://www.csselectronics.com/screen/page/simple-intro-obd2-explained/language/en
          *(OBDArray[Row].Data) = CAN_Receive_Parse(can_MsgRx.buf[2], can_MsgRx.buf[3], can_MsgRx.buf[4]);
        }
        CANWaiting = false; //Reset waiting flag
        NextPID();//Prepare next PID
      }
    }

    else { //Still waiting - check for timeout
      CAN_Wait_Duration = currentMillis - CAN_Request_Millis;
      if (CAN_Wait_Duration > CAN_Timeout) //Waited too long
      {
        OBDArray[Row].NoReply ++; //Add to the no reply counter for this PID
        if (OBDArray[Row].NoReply > 100) { //if >100 no replys then this PID is not available, so stop requesting it and show an error
          //Switch off this PID Request
          *(OBDArray[Row].Active) = 1;
          //Show Error
          Serial.print("OBD Error - ");
          Serial.println(OBDArray[Row].Name);
          //Try Next PID
          NextPID();
          CAN_Retry = 0;
          CANWaiting = false;
        }
        //If it has just dropped a single packet then give it another try
        if (CAN_Retry == 2) {
          //Try Next PID
          NextPID();
          CAN_Retry = 0;
          CANWaiting = false;
        }
        else {
          CAN_Retry ++; //Retry this PID
        }
      }
    }
  }

  if (CANWaiting == false) {
    //Request next Data
    CAN_Request(OBDArray[Row].MODE, OBDArray[Row].PID);
    CAN_Request_Millis = currentMillis;
    CANWaiting = true;
  }
}

//-------------REQUEST CAN DATA-------------
void CAN_Request(byte CAN_MODE, byte PID_CALL) {
  can_MsgTx.ext = 0;
  can_MsgTx.id = PID_REQUEST;
  can_MsgTx.len = 8;

  can_MsgTx.buf[0] = 0x02; //Length in number of bytes of remaining data (2 bytes = 02, 3 bytes = 03 etc)
  can_MsgTx.buf[1] = CAN_MODE;
  can_MsgTx.buf[2] = PID_CALL;
  can_MsgTx.buf[3] = 0;
  can_MsgTx.buf[4] = 0;
  can_MsgTx.buf[5] = 0;
  can_MsgTx.buf[6] = 0;
  can_MsgTx.buf[7] = 0;

  can_MsgTx.flags.extended = 0;
  can_MsgTx.flags.remote = 0;

  //Serial.print("CAN SENT: "); hexDump(8, can_MsgTx.buf); //Diagnostic - Dump message to Serial//@@@@@@@@@@@@@@@@@@@@@@@@@@@CAN DIAGNOSTICS@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  //Serial.println(currentMillis / 10);
  Can0.write(can_MsgTx); //Send message
}

//-------------NEXT PID TO RUN-------------
void NextPID() {
  //Start with next row unless at end of table, then start from 0
  for (int i = 0; i < (Arraysize - 1); i++) {

    if ((i + Row) == (Arraysize - 1)) Row = 0;
    else Row = (Row + 1);
    if (*(OBDArray[Row].Active) == 2) break;
  }
}

//-------------SERIAL PRINT CAN DATA-------------
void Print_CAN_Data() {
  Serial.print("RPM: ");
  Serial.print(RPM);
  Serial.print(" | SPEED: ");
  Serial.print(SPEED);
  Serial.print(" | COOLANT: ");
  Serial.print(COOLANT_TEMP);
  Serial.print(" | OIL: ");
  Serial.print(OIL_TEMP);
  Serial.print(" | AMBIENT: ");
  Serial.print(AMBIENT_TEMP);
  Serial.print(" | INTAKE: ");
  Serial.print(INTAKE_TEMP);
  Serial.print(" | TPS: ");
  Serial.print(THROTTLE_POS);
  Serial.print(" | FUEL P: ");
  Serial.print(FUEL_PRESSURE);
  Serial.print(" | FUEL R: ");
  Serial.print(FUEL_RATE);
  Serial.print(" | BATT: ");
  Serial.print(BATT_VOLTAGE);
  Serial.print(" | DTC: ");
  Serial.println(numDTC);
}

//-------------PROCESS DTC INCOMING-------------
int CAN_ERR_Receive(byte pkt2, byte pkt3, byte pkt4, byte pkt5, byte pkt6) {
  numDTC = pkt2;

  if (numDTC > 0) {
    DTCp1 = pkt3;
    DTCp2 = pkt4;
    if (DTCp1 < 10) DTC1Raw = (DTCp1 * 100) + DTCp2;
    else DTC1Raw = (DTCp1 * 1000) + DTCp2;
    sprintf (DTC1, "P%04u", DTC1Raw);
  }
  else sprintf (DTC1, "");

  if (numDTC > 1) {
    DTCp1 = pkt5;
    DTCp2 = pkt6;
    if (DTCp1 < 10) DTC2Raw = (DTCp1 * 100) + DTCp2;
    else DTC2Raw = (DTCp1 * 1000) + DTCp2;
    sprintf (DTC2, "P%04u", DTC2Raw);
  }
  else sprintf (DTC2, "");
  return numDTC;
}

//-------------PROCESS NORMAL PID INCOMING-------------
int CAN_Receive_Parse(byte PID_Received, int Data_1, int Data_2) {
  switch (PID_Received) //Switch means look at different cases
  { /* Details from http://en.wikipedia.org/wiki/OBD-II_PIDs */

    case PID_SPEED:         // A                  [km]  int SPEED =
      return  Data_1 * 1; //CHECK SPEED IS IN KPH AND CONVERSION!!!!
      break;

    case PID_RPM:              //   ((A*256)+B)/4    [RPM]
      return  ((Data_1 * 256) + Data_2) / 4;
      break;

    case PID_OIL_TEMP:     //     A-40              [degree C]
      return  Data_1 - 40;
      break;

    case PID_COOLANT_TEMP:     //     A-40              [degree C]
      return  Data_1 - 40;
      break;

    case PID_INTAKE_TEMP:     //     A-40              [degree C]
      return  Data_1 - 40;
      break;

    case PID_AMBIENT_TEMP:     //     A-40              [degree C]
      return  Data_1 - 40;
      break;

    case PID_BATT_VOLTAGE:            //
      return ((Data_1 * 256) + Data_2);
      break;

    case PID_FUEL_PRESSURE:     //                   [kPa]
      return  Data_1 * 3;
      break;

    case PID_FUEL_RATE:     //                   [l/h]
      return  ((Data_1 * 256) + Data_2) / 2;
      break;

    case PID_THROTTLE_POS:
      return int((Data_1 * 100) / 255);
      break;

    case PID_MONITOR_STATUS:
      return Data_1;// & Data_2 & can_MsgRx.buf[5];
      break;
  }
}
//=========================================END CAN BUS FUNCTIONS====================================================

//=========================================START SERIAL FUNTIONS====================================================

void keyboard() {
  switch (Serial.read())
  {
    case '1': menu = 1; break; //ODO
    case '2': menu = 2; break; //Main
    case '3': menu = 3; break; //GearOnly
    case '4': menu = 4; break; //FuelR
    case '5': menu = 5; break; //DarkMode
    case '6': menu = 6; break; //Oil Water T
    case '7': menu = 7; break; //Oil P Fuel P
    case '8': menu = 8; break; // Stats
    case '9': menu = 9; break; // 0-60
    case 'A': menu = 10; break; // Speed
    case 'B': menu = 11; break; // RPM
    case 'C': menu = 12; break; // Batt
    case 'D': menu = 13; break; // Service
    case 'E': menu = 14; break; // DTC
    case 'F': menu = 15; break; // Setup
    case 'O': {
        MODE_RPM = 2;
        MODE_SPEED = 2;
        MODE_COOLANT_TEMP = 2;
        MODE_OIL_TEMP = 2;
        MODE_AMBIENT_TEMP  = 2;
        break;
      }
  }
}

//----------------------SERIAL 1 Receive------------------------
void Serial1Receive() {
  while (Serial1.available() > 0 && S1newData == false) {
    S1Received = Serial1.read();

    if (S1recvInProgress == true) {
      if (S1Received != S1endMarker) {
        S1receivedChars[S1ndx] = S1Received;
        S1ndx++;
        if (S1ndx >= S1numChars) {
          S1ndx = S1numChars - 1;
        }
      }
      else {
        S1receivedChars[S1ndx] = '\0'; // terminate the string
        S1recvInProgress = false;
        S1ndx = 0;
        S1newData = true;
      }
    }

    else if (S1Received == S1startMarker) {
      S1recvInProgress = true;
    }
  }
}

//----------------------SERIAL 1 PARSE------------------------

void Serial1Parse() {      // split the data into its parts
  //Check incoming datasteam:
  //Serial.println(tempChars);
  char *ptr;
  ptr = strtok(S1tempChars, ",");

  if (ptr == NULL)
  {
    //Serial.println("No comma found");
  }
  else
  {
    do
    {
      switch (ptr[0])
      {
        case 'S':
          SPEED_SER = atoi(&ptr[1]);
          break;
        case 'R':
          RPM_SER = atoi(&ptr[1]);
          break;
        case 'T':
          COOLANT_TEMP_SER = atoi(&ptr[1]) / 10;
          break;
        case 'F':
          FUEL_LEVEL_SER = atoi(&ptr[1]) / 10;
          break;
      }
      ptr = strtok(NULL, ",");


    } while (ptr != NULL);
  }
  if (MODE_SPEED == 1) SPEED_MPH = SPEED_SER;

  if (MODE_RPM == 1) RPM = RPM_SER;

  FUEL_LEVEL = FUEL_LEVEL_SER;

  if (MODE_COOLANT_TEMP == 1) COOLANT_TEMP = COOLANT_TEMP_SER;
}

//============

void showParsedData() {
  //FOR DIAGNOSIS ONLY
  Serial.print("<");
  Serial.print(SPEED_MPH);
  Serial.print(", ");
  Serial.print(RPM);
  Serial.print(", ");
  Serial.print(COOLANT_TEMP);
  Serial.print(", ");
  Serial.print(FUEL_LEVEL);
  Serial.println(">");

}

//----------------------SERIAL 1 SEND------------------------
void Send_Serial1_Data() {
  if (millis() - Serial1millis > 1000) { //Update every 1 seconds.

    Serial1millis = millis(); // Update lasmillis
    // time to update serial
    UpdateSerial1 = 1;
  }

  //Serial.print(ShutDown);
  if (ResetSteppers == 1 || UpdateSerial1 == 1 || ShutDown == 1 || MODE_SPEED + MODE_RPM + MODE_COOLANT_TEMP != CAN_MODES_SEND || RPM / 10 != RPM_SEND || COOLANT_TEMP != CT_SEND || SPEED_MPH != SPEED_SEND) {
    Serial1.print('<');

    if (UpdateSerial1 == 1 || ShutDown != ShutDown_SEND) {
      ShutDown_SEND = ShutDown;
      Serial1.print('S');
      Serial1.print(ShutDown);
      Serial1.print(',');
    }

    if (UpdateSerial1 == 1 || MODE_SPEED + MODE_RPM + MODE_COOLANT_TEMP != CAN_MODES_SEND) {
      CAN_MODES_SEND = MODE_SPEED + MODE_RPM + MODE_COOLANT_TEMP;
      Serial1.print('M');
      Serial1.print(MODE_SPEED);
      Serial1.print(MODE_RPM);
      Serial1.print(MODE_COOLANT_TEMP);
      Serial1.print(',');
    }

    if ((RPM) != RPM_SEND && MODE_RPM == 2) {
      RPM_SEND = RPM;
      //Serial1.print('<');
      Serial1.print('R');
      Serial1.print(RPM_SEND);
      Serial1.print(',');
    }

    if (UpdateSerial1 == 1 || (COOLANT_TEMP != CT_SEND && MODE_COOLANT_TEMP == 2)) {
      CT_SEND = COOLANT_TEMP;
      Serial1.print('T');
      Serial1.print(CT_SEND);
      Serial1.print(',');
    }

    if (SPEED_MPH != SPEED_SEND && MODE_SPEED == 2) {
      SPEED_SEND = SPEED_MPH;
      Serial1.print('V');
      Serial1.print(SPEED_SEND);
      Serial1.print(',');
    }

    if (UpdateSerial1 == 1 || ResetSteppers == 1) {
      Serial1.print('Z');
      Serial1.print(ResetSteppers);
      Serial1.print(',');
    }

    Serial1.print('>');
    UpdateSerial1 = 0;
  }
}
//----------------------SERIAL 1 RECEIVE------------------------
void Serial2Receive() {


  while (Serial2.available() > 0 && S2newData == false) {
    S2Received = Serial2.read();

    if (S2recvInProgress == true) {
      if (S2Received != S2endMarker) {
        S2receivedChars[S2ndx] = S2Received;
        S2ndx++;
        if (S2ndx >= S2numChars) {
          S2ndx = S2numChars - 1;
        }
      }
      else {
        S2receivedChars[S2ndx] = '\0'; // terminate the string
        S2recvInProgress = false;
        S2ndx = 0;
        S2newData = true;
      }
    }

    else if (S2Received == S2startMarker) {
      S2recvInProgress = true;
    }
  }
}

//----------------------SERIAL 1 PARSE------------------------
void Serial2Parse() {      // split the data into its parts
  //Serial.println(tempChars); //diagnostic only: check incoming data
  char *ptr2;
  ptr2 = strtok(S2tempChars, ",");

  if (ptr2 == NULL)
  {
    //Serial.println("No comma found");
  }
  else
  {
    do
    {
      switch (ptr2[0])
      {
        case 'L':
          LightsOn = atoi(&ptr2[1]);
          break;
      }
      ptr2 = strtok(NULL, ",");


    } while (ptr2 != NULL);
  }
}

//----------------------SERIAL 1 SEND------------------------
void Send_Serial2_Data() {
  if (millis() - Serial2millis > 1000) { //Update every 1 seconds.

    Serial2millis = millis(); // Update lasmillis
    // time to update serial
    UpdateSerial2 = 1;
  }

  if (DarkMode != DarkMode_SEND || ShutDown == 1 || UpdateSerial2 == 1) {
    Serial2.print('<');

    if ((DarkMode) != DarkMode_SEND || UpdateSerial2 == 1) {
      DarkMode_SEND = DarkMode;
      Serial2.print('D');
      Serial2.print(DarkMode);
      Serial2.print(',');
    }

    if (ShutDown == 1 || UpdateSerial2 == 1) {
      ShutDown_SEND = ShutDown;
      Serial2.print('S');
      Serial2.print(ShutDown);
      Serial2.print(',');
    }

    Serial2.print('>');
  }
  UpdateSerial2 = 0;
}

//=========================================END SERIAL FUNCTIONS====================================================

//=========================================START MISC FUNCTIONS====================================================
void GEARS() {

  if (GEAR_POS > Neutralp - GearWindow && GEAR_POS < Neutralp + GearWindow && Neutralp > 0 && millis() - GearTime > 250) GEAR = "N";
  if (GEAR_POS > Gear1p - GearWindow && GEAR_POS < Gear1p + GearWindow && Gear1p > 0 && millis() - GearTime > 250) GEAR = "1";
  if (GEAR_POS > Gear2p - GearWindow && GEAR_POS < Gear2p + GearWindow && Gear2p > 0 && millis() - GearTime > 250) GEAR = "2";
  if (GEAR_POS > Gear3p - GearWindow && GEAR_POS < Gear3p + GearWindow && Gear3p > 0 && millis() - GearTime > 250) GEAR = "3";
  if (GEAR_POS > Gear4p - GearWindow && GEAR_POS < Gear4p + GearWindow && Gear4p > 0 && millis() - GearTime > 250) GEAR = "4";
  if (GEAR_POS > Gear5p - GearWindow && GEAR_POS < Gear5p + GearWindow && Gear5p > 0 && millis() - GearTime > 250) GEAR = "5";
  if (GEAR_POS > Gear6p - GearWindow && GEAR_POS < Gear6p + GearWindow && Gear6p > 0 && millis() - GearTime > 250) GEAR = "6";
  if (GEAR_POS > Reversep - GearWindow && GEAR_POS < Reversep + GearWindow && Reversep > 0 && millis() - GearTime > 250) GEAR = "R";

  if (GEAR != GPrev) {
    if ( GearChange != 1) lastmenu = menu;
    GearChange = 1;
    GearTime = millis();
  }
  GPrev = GEAR;
}


void checkdisplay() {
  //Serial.println("Display");
  if (menu == 1) display_Odometer();
  if (menu == 2) display_Main();
  if (menu == 3) display_GearOnly();
  if (menu == 4) display_FuelR();
  if (menu == 5) display_DarkMode();
  if (menu == 6) display_oil_water();
  if (menu == 7) display_OilP_FuelP();
  if (menu == 8) display_Stats();
  if (menu == 9) display_Acceltimer();
  if (menu == 10) display_speedmph();
  if (menu == 11) display_RPM();
  if (menu == 12) display_BattV();
  if (menu == 13) display_servicedist();
  if (menu == 14) display_troublecodes();
  if (menu == 15) display_Setup();

  if (menu == 21) setup_SpeedUnits();
  if (menu == 22) setup_SpeedSource();
  if (menu == 23) setup_RPMSource();
  if (menu == 24) setup_CoolTSource();
  if (menu == 25) setup_AirTSource();
  if (menu == 26) setup_OilTSource();
  if (menu == 27) setup_ZeroStep();
  if (menu == 28) setup_Gears();
  if (menu == 29) setup_ZeroODO();
  if (menu == 30) setup_ShowAlignBox();
  if (menu == 31) setup_ExitSubmenu();
  if (menu == 35) AlignmentBox();
}

//-------------------------UPDATE ODO--------------------------------------------------------

void updateodometer() {

  if (meterssubtotal > 99.99) {

    ++disthundredm;
    ++tripdisthundredm;
    ++servicedisthundredm;
    meterssubtotal = 0;
  }

  if (disthundredm > 9) {
    ++distkm;
    disthundredm = 0;
  }

  if (distkm > 9) {
    ++distenkm;
    distkm = 0;
  }

  if (distenkm > 9) {
    ++disthundredkm;
    distenkm = 0;
  }

  if (disthundredkm > 9) {
    ++disthousandkm;
    disthundredkm = 0;
  }

  if (disthousandkm > 9) {
    ++disttenthousandkm;
    disthousandkm = 0;
  }

  if (disttenthousandkm > 9) {
    ++disthundredthousandkm;
    disttenthousandkm = 0;
  }

  //-----------------------UPDATE TRIP--------------------------------
  if (tripdisthundredm > 9) {
    ++tripdistkm;
    tripdisthundredm = 0;
  }
  if (tripdistkm > 9) {
    ++tripdisthundredkm;
    tripdistkm = 0;
  }
  if (tripdisthundredkm > 9) {
    ++tripdistthousandkm;
    tripdisthundredkm = 0;
  }
  if (tripdistthousandkm == 9 && tripdisthundredkm == 9 && tripdistkm == 9 && tripdisthundredm == 9) {
    tripdisthundredm = 0;
    tripdistkm = 0;
    tripdisthundredkm = 0;
    tripdistthousandkm = 0;
  }

  //---------------------UPDATE SERVICE------------------------------
  if (servicedisthundredm > 9) {
    ++servicedistkm;
    servicedisthundredm = 0;
  }
  if (servicedistkm > 9) {
    ++servicedisthundredkm;
    servicedistkm = 0;
  }
  if (servicedisthundredkm > 9) {
    ++servicedistthousandkm;
    servicedisthundredkm = 0;
  }
  if (servicedistthousandkm == 9 && servicedisthundredkm == 9 && servicedistkm == 9 && servicedisthundredm == 9) {
    servicedisthundredm = 0;
    servicedistkm = 0;
    servicedisthundredkm = 0;
    servicedistthousandkm = 0;
  }
}

//-----------------------CLEAR MENUS--------------------------------------------------------
void cleartrip() {

  menu = 1;
  tripdisthundredm = 0;
  tripdistkm = 0;
  tripdisthundredkm = 0;
  tripdistthousandkm = 0;
}

void clearstats() {

  MaxSpeed = 0;
  MaxRPM = 0;
  MaxTemp = 0;
  MaxOil = 0;
}

void clearODO() {

  distkm = 0;
  distenkm = 0;
  disthundredkm = 0;
  disthousandkm = 0;
  disttenthousandkm = 0;
  disthundredthousandkm = 0;
}

void clearservice() {

  servicedisthundredm = 0;
  servicedistkm = 0;
  servicedisthundredkm = 0;
  servicedistthousandkm = 0;
  distance_since_service = 0;
}


//-----------------------DRAW LOGO--------------------------------------------------------
void drawTVR(void) {
  display.clearDisplay();

  display.drawBitmap(
    (display.width()  - LOGO_WIDTH ) / 2,
    (display.height() - LOGO_HEIGHT) / 2,
    logo_bmp, LOGO_WIDTH, LOGO_HEIGHT, 1);
  display.display();
}

//==========================================END MISC FUNTIONS=======================================================

//========================================START EEPROM FUNCTIONS====================================================
void updateeeprom() {
  EEPROMWriteCount = EEPROMWriteCount + 1;
  EEPROM.update(S2 + (EEPROM_OFFSET * EEPROMAddress), EEPROMWriteCount);
  EEPROM.update(S3 + (EEPROM_OFFSET * EEPROMAddress), tripdisthundredm);
  EEPROM.update(S4 + (EEPROM_OFFSET * EEPROMAddress), tripdistkm);
  EEPROM.update(S5 + (EEPROM_OFFSET * EEPROMAddress), tripdisthundredkm);
  EEPROM.update(S6 + (EEPROM_OFFSET * EEPROMAddress), tripdistthousandkm);
  EEPROM.update(S7 + (EEPROM_OFFSET * EEPROMAddress), disthundredm);
  EEPROM.update(S8 + (EEPROM_OFFSET * EEPROMAddress), distkm);
  EEPROM.update(S9 + (EEPROM_OFFSET * EEPROMAddress), distenkm);
  EEPROM.update(S10 + (EEPROM_OFFSET * EEPROMAddress), disthundredkm);
  EEPROM.update(S11 + (EEPROM_OFFSET * EEPROMAddress), disthousandkm);
  EEPROM.update(S12 + (EEPROM_OFFSET * EEPROMAddress), disttenthousandkm);
  EEPROM.update(S13 + (EEPROM_OFFSET * EEPROMAddress), disthundredthousandkm);
  EEPROM.update(S14 + (EEPROM_OFFSET * EEPROMAddress), Best060);
  EEPROM.update(S15 + (EEPROM_OFFSET * EEPROMAddress), PrevMPG);
  EEPROM.update(S16 + (EEPROM_OFFSET * EEPROMAddress), MaxRPM);
  EEPROM.update(S17 + (EEPROM_OFFSET * EEPROMAddress), MaxSpeed);
  EEPROM.update(S18 + (EEPROM_OFFSET * EEPROMAddress), MaxTemp);
  EEPROM.update(S19 + (EEPROM_OFFSET * EEPROMAddress), MaxOil);
  EEPROM.update(S20 + (EEPROM_OFFSET * EEPROMAddress), servicedisthundredm);
  EEPROM.update(S21 + (EEPROM_OFFSET * EEPROMAddress), servicedistkm);
  EEPROM.update(S22 + (EEPROM_OFFSET * EEPROMAddress), servicedisthundredkm);
  EEPROM.update(S23 + (EEPROM_OFFSET * EEPROMAddress), servicedistthousandkm);
  EEPROM.update(S24 + (EEPROM_OFFSET * EEPROMAddress), menu);
  EEPROM.update(S25 + (EEPROM_OFFSET * EEPROMAddress), Neutralp);
  EEPROM.update(S26 + (EEPROM_OFFSET * EEPROMAddress), Gear1p);
  EEPROM.update(S27 + (EEPROM_OFFSET * EEPROMAddress), Gear2p);
  EEPROM.update(S28 + (EEPROM_OFFSET * EEPROMAddress), Gear3p);
  EEPROM.update(S29 + (EEPROM_OFFSET * EEPROMAddress), Gear4p);
  EEPROM.update(S30 + (EEPROM_OFFSET * EEPROMAddress), Gear5p);
  EEPROM.update(S31 + (EEPROM_OFFSET * EEPROMAddress), Gear6p);
  EEPROM.update(S32 + (EEPROM_OFFSET * EEPROMAddress), Reversep);
  EEPROM.update(S33 + (EEPROM_OFFSET * EEPROMAddress), MODE_SPEED);
  EEPROM.update(S34 + (EEPROM_OFFSET * EEPROMAddress), MODE_RPM);
  EEPROM.update(S35 + (EEPROM_OFFSET * EEPROMAddress), MODE_COOLANT_TEMP);
  EEPROM.update(S36 + (EEPROM_OFFSET * EEPROMAddress), MODE_OIL_TEMP);
  EEPROM.update(S37 + (EEPROM_OFFSET * EEPROMAddress), MODE_AMBIENT_TEMP);
  EEPROM.update(S38 + (EEPROM_OFFSET * EEPROMAddress), SPEED_DISPLAY);

}

// EEPROM SETTING--------------------------------------------------------------------------
void loadEEPROMSettings() {
  if (EEPROM.read(E_INIT) == 'T') {
    //settings have been initialized, read them

    if (EEPROMWriteCount > 100000) {
      EEPROMWriteCount = 0;
      EEPROMAddress = EEPROMAddress + 1;
    }
    EEPROMAddress = EEPROM.read(S1);
    EEPROMWriteCount = EEPROM.read(S2 + (EEPROM_OFFSET * EEPROMAddress));
    tripdisthundredm = EEPROM.read(S3 + (EEPROM_OFFSET * EEPROMAddress));
    tripdistkm = EEPROM.read(S4 + (EEPROM_OFFSET * EEPROMAddress));
    tripdisthundredkm = EEPROM.read(S5 + (EEPROM_OFFSET * EEPROMAddress));
    tripdistthousandkm = EEPROM.read(S6 + (EEPROM_OFFSET * EEPROMAddress));
    disthundredm = EEPROM.read(S7 + (EEPROM_OFFSET * EEPROMAddress));
    distkm = EEPROM.read(S8 + (EEPROM_OFFSET * EEPROMAddress));
    distenkm = EEPROM.read(S9 + (EEPROM_OFFSET * EEPROMAddress));
    disthundredkm = EEPROM.read(S10 + (EEPROM_OFFSET * EEPROMAddress));
    disthousandkm = EEPROM.read(S11 + (EEPROM_OFFSET * EEPROMAddress));
    disttenthousandkm = EEPROM.read(S12 + (EEPROM_OFFSET * EEPROMAddress));
    disthundredthousandkm = EEPROM.read(S13 + (EEPROM_OFFSET * EEPROMAddress));
    Best060 = EEPROM.read(S14 + (EEPROM_OFFSET * EEPROMAddress));
    PrevMPG = EEPROM.read(S15 + (EEPROM_OFFSET * EEPROMAddress));
    MaxRPM = EEPROM.read(S16 + (EEPROM_OFFSET * EEPROMAddress));
    MaxSpeed = EEPROM.read(S17 + (EEPROM_OFFSET * EEPROMAddress));
    MaxTemp = EEPROM.read(S18 + (EEPROM_OFFSET * EEPROMAddress));
    MaxOil = EEPROM.read(S19 + (EEPROM_OFFSET * EEPROMAddress));
    servicedisthundredm = EEPROM.read(S20 + (EEPROM_OFFSET * EEPROMAddress));
    servicedistkm = EEPROM.read(S21 + (EEPROM_OFFSET * EEPROMAddress));
    servicedisthundredkm = EEPROM.read(S22 + (EEPROM_OFFSET * EEPROMAddress));
    servicedistthousandkm = EEPROM.read(S23 + (EEPROM_OFFSET * EEPROMAddress));
    MenuMem = EEPROM.read(S24 + (EEPROM_OFFSET * EEPROMAddress));
    Neutralp = EEPROM.read(S25 + (EEPROM_OFFSET * EEPROMAddress));
    Gear1p = EEPROM.read(S26 + (EEPROM_OFFSET * EEPROMAddress));
    Gear2p = EEPROM.read(S27 + (EEPROM_OFFSET * EEPROMAddress));
    Gear3p = EEPROM.read(S28 + (EEPROM_OFFSET * EEPROMAddress));
    Gear4p = EEPROM.read(S29 + (EEPROM_OFFSET * EEPROMAddress));
    Gear5p = EEPROM.read(S30 + (EEPROM_OFFSET * EEPROMAddress));
    Gear6p = EEPROM.read(S31 + (EEPROM_OFFSET * EEPROMAddress));
    Reversep = EEPROM.read(S32 + (EEPROM_OFFSET * EEPROMAddress));
    MODE_SPEED = EEPROM.read(S33 + (EEPROM_OFFSET * EEPROMAddress));
    MODE_RPM = EEPROM.read(S34 + (EEPROM_OFFSET * EEPROMAddress));
    MODE_COOLANT_TEMP = EEPROM.read(S35 + (EEPROM_OFFSET * EEPROMAddress));
    MODE_OIL_TEMP = EEPROM.read(S36 + (EEPROM_OFFSET * EEPROMAddress));
    MODE_AMBIENT_TEMP = EEPROM.read(S37 + (EEPROM_OFFSET * EEPROMAddress));
    SPEED_DISPLAY = EEPROM.read(S38 + (EEPROM_OFFSET * EEPROMAddress));

    if (MODE_SPEED != 1 && MODE_SPEED != 2) MODE_SPEED = 2;
    if (MODE_RPM != 1 && MODE_RPM != 2) MODE_RPM = 2;
    if (MODE_COOLANT_TEMP != 1 && MODE_COOLANT_TEMP != 2) MODE_COOLANT_TEMP = 2;
    if (MODE_OIL_TEMP != 1 && MODE_OIL_TEMP != 2) MODE_OIL_TEMP = 2;
    if (MODE_AMBIENT_TEMP != 1 && MODE_AMBIENT_TEMP != 2) MODE_AMBIENT_TEMP = 2;
    if (SPEED_DISPLAY != 1 && SPEED_DISPLAY != 2) SPEED_DISPLAY = 1;

  } else {
    //first time run, settings were never set
    EEPROM.write(S1, 0);
    EEPROM.write(S2, 0);
    EEPROM.write(S3, 0);
    EEPROM.write(S4, 0);
    EEPROM.write(S4, 0);
    EEPROM.write(S5, 0);
    EEPROM.write(S6, 0);
    EEPROM.write(S7, 0);
    EEPROM.write(S8, 0);
    EEPROM.write(S9, 0);
    EEPROM.write(S10, 0);
    EEPROM.write(S11, 0);
    EEPROM.write(S12, 0);
    EEPROM.write(S13, 0);
    EEPROM.write(S14, 0);
    EEPROM.write(S15, 0);
    EEPROM.write(S16, 0);
    EEPROM.write(S17, 0);
    EEPROM.write(S18, 0);
    EEPROM.write(S19, 0);
    EEPROM.write(S20, 0);
    EEPROM.write(S21, 0);
    EEPROM.write(S22, 0);
    EEPROM.write(S23, 0);
    EEPROM.write(S24, 0);
    EEPROM.write(S25, 0);
    EEPROM.write(S26, 0);
    EEPROM.write(S27, 0);
    EEPROM.write(S28, 0);
    EEPROM.write(S29, 0);
    EEPROM.write(S30, 0);
    EEPROM.write(S31, 0);
    EEPROM.write(S32, 0);
    EEPROM.write(S33, 2);
    EEPROM.write(S34, 2);
    EEPROM.write(S35, 2);
    EEPROM.write(S36, 2);
    EEPROM.write(S37, 2);
    EEPROM.write(S38, 0);
    EEPROMAddress = 0;
    EEPROMWriteCount = 0;
    tripdisthundredm = 0;
    tripdistkm = 0;
    tripdisthundredkm = 0;
    tripdistthousandkm = 0;
    disthundredm = 0;
    distkm = 0;
    distenkm = 0;
    disthundredkm = 0;
    disthousandkm = 0;
    disttenthousandkm = 0;
    disthundredthousandkm = 0;
    Best060 = 0;
    PrevMPG = 0;
    MaxRPM = 0;
    MaxSpeed = 0;
    MaxTemp = 0;
    MaxOil = 0;
    servicedisthundredm = 0;
    servicedistkm = 0;
    servicedisthundredkm = 0;
    servicedistthousandkm = 0;
    MenuMem = 1;
    Neutralp = 219;
    Gear1p = 202;
    Gear2p = 167;
    Gear3p = 132;
    Gear4p = 98;
    Gear5p = 64;
    Gear6p = 30;
    Reversep = 0;
    MODE_SPEED = 2;
    MODE_RPM = 2;
    MODE_COOLANT_TEMP = 2;
    MODE_OIL_TEMP = 2;
    MODE_AMBIENT_TEMP = 2;
    SPEED_DISPLAY = 1;
    EEPROM.write(E_INIT, 'T');
  }
}
//=============================================================================================END EEPROM FUNCTIONS

//======================================================================================START OLED DISPLAY FUNCTIONS
void AlignmentBox() {
  display.clearDisplay();
  display.drawRect(0, 0, 128, 32, WHITE);
  display.display();
}

//---------------------------------------------------------------------------------------START Display Modes

//---------------------------------------------------------------------DISPLAY ODOMETER
void display_Odometer() {
  if (distkm != distkm_PREV || tripdisthundredm != tripdisthundredm_PREV || menu != menu_PREV) {
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(0, 0);
    display.println("ODO");

    //-----Display hundred thousand km units-----
    display.setTextSize(2);
    display.setCursor(30, 0);
    display.println(disthundredthousandkm);
    //-----Display ten thousand km  units--------
    display.setCursor(45, 0);
    display.println(disttenthousandkm);
    //----Display thousand km units------------
    display.setCursor(60, 0);
    display.println(disthousandkm);
    //----Display hundreds km units-------------
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(75, 0);
    display.println(disthundredkm);
    //----Display tens km units-----------------
    display.setCursor(90, 0);
    display.println(distenkm);
    //----Display km units----------------------
    display.setCursor(105, 0);
    display.println(distkm);
    //----Display km units----------------------
    display.setTextSize(1);
    display.setCursor(125, 0);


    //-------------------------------------------------------------------DISPLAY TRIP

    display.setCursor(0, 20);
    //display.println("TRIP");
    //  if (digitalRead(ShutDownPin) == LOW)display.println("TRIP    SD");
    //  else {
    //    if (digitalRead(ShutDownPin) == HIGH) {
    //      display.println("TRIP");
    //    }
    //  }

    if (numDTC > 0)display.println("TRIP   DTC");
    else display.println("TRIP");

    //----Display Thousands---------------------
    display.setTextSize(2);
    display.setCursor(75, 17);
    display.println(tripdistthousandkm);
    //-----Display Hundreds----------------------
    display.setCursor(90, 17);
    display.println(tripdisthundredkm);
    //-----Display KMs---------------------------
    display.setCursor(105, 17);
    display.println(tripdistkm);
    //----------------Display Hundred M---------
    display.setTextSize(1);
    display.setCursor(120, 17);
    display.println(tripdisthundredm);
    display.display();
    distkm_PREV = distkm;
    tripdisthundredm_PREV = tripdisthundredm;
    menu_PREV = menu;
  }


}

//-------------------------------------------------------------------------------START MAIN DISPLAY

void display_Main() {
  if (GEAR != GEAR_PREV || AMBIENT_TEMP != AMBIENT_TEMP_PREV || INTAKE_TEMP != INTAKE_TEMP_PREV || OIL_TEMP != OIL_TEMP_PREV || INTAKE_TEXT_PREV != INTAKE_TEXT || menu != menu_PREV) {
    display.clearDisplay();

    display.setCursor(89, 3);
    display.setTextSize(4.9);
    display.println(GEAR);

    display.setCursor(55, 0);
    display.setTextSize(1);
    display.println("Gear");
    if (numDTC > 0) {
      display.setCursor(58, 25);
      display.println("DTC");
    }

    display.setTextSize(1);
    display.setTextColor(WHITE);

    display.setCursor(0, 5);
    display.println("Air");
    display.setCursor(25, 5);
    display.println(AMBIENT_TEMP);

    display.setCursor(0, 15);
    display.println("Itk");
    display.setCursor(25, 15);
    display.setTextColor(INTAKE_TEXT);
    display.println(INTAKE_TEMP);

    display.setCursor(0, 25);
    display.setTextColor(WHITE);
    display.println("Oil");
    display.setCursor(25, 25);
    display.setTextColor(OIL_TEMP_TEXT);
    display.println(OIL_TEMP);

    display.display();
    GEAR_PREV = GEAR;
    AMBIENT_TEMP_PREV = AMBIENT_TEMP;
    INTAKE_TEMP_PREV = INTAKE_TEMP;
    OIL_TEMP_PREV = OIL_TEMP;
    INTAKE_TEXT_PREV = INTAKE_TEXT;
    menu_PREV = menu;
  }

}

//-------------------------------------------------------------------------------GEAR ONLY
void display_GearOnly() {
  if (GEAR != GEAR_PREV || menu != menu_PREV) {
    display.clearDisplay();

    display.setCursor(89, 3);
    display.setTextSize(4.9);
    display.println(GEAR);

    display.setCursor(55, 0);
    display.setTextSize(1);
    display.println("Gear");

    if (numDTC > 0) {
      display.setCursor(58, 25);
      display.println("DTC");
    }

    display.display();
    GEAR_PREV = GEAR;
    menu_PREV = menu;
  }

}

//-------------------------------------------------------------------------------SPEED
void display_speedmph() {

  if (SPEED_MPH_PREV != SPEED_MPH || menu != menu_PREV) {
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(0, 0);
    display.clearDisplay();
    if (SPEED_DISPLAY == 1) display.println("MPH");
    else if (SPEED_DISPLAY == 2) display.println("KPH");
    display.setTextSize(4);
    display.setCursor(40, 0);
    display.println(int(SPEED_MPH * SPEED_MULTIPLIER));
    display.display();
    SPEED_MPH_PREV = SPEED_MPH;
    menu_PREV = menu;
  }
}

//-------------------------------------------------------------------------------RPM

void display_RPM() {
  if (RPM != RPM_PREV || BlinkState_PREV != BlinkState || menu != menu_PREV) {
    display.clearDisplay();
    display.drawRect(0, 18, 125, 14, WHITE);
    int RPMbar = map (RPM, 0, 7000, 0, 122);
    display.fillRect(3, 21, RPMbar, 8, RPM_TEXT);
    display.setCursor(79, 0);
    display.setTextSize(2);
    display.println(RPM);
    display.setTextSize(1);
    display.setCursor(0, 0);
    display.println("RPM");
    display.display();
    RPM_PREV = RPM;
    BlinkState_PREV = BlinkState;
    menu_PREV = menu;
  }

}

//--------------------------------------------------------------------INTAKE TEMP & WATER TEMP
void display_oil_water() {
  if (OIL_TEMP != OIL_TEMP_PREV || COOLANT_TEMP != COOLANT_TEMP_PREV || BlinkState_PREV != BlinkState || menu != menu_PREV) {
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(OIL_TEMP_TEXT);
    display.setCursor(0, 0);
    display.println("Oil T");
    display.setCursor(0, 10);
    display.setTextSize(3);
    display.setTextColor(OIL_TEMP_TEXT);
    display.setCursor(1, 10);
    display.println(OIL_TEMP);

    display.setTextSize(1);
    display.setCursor(70, 0);
    display.setTextColor(WATER_TEXT);
    display.println("Water T");
    display.setTextColor(WATER_TEXT);
    display.setTextSize(3);
    display.setCursor(70, 10);
    display.println(COOLANT_TEMP);
    display.display();
    COOLANT_TEMP_PREV = COOLANT_TEMP;
    OIL_TEMP_PREV = OIL_TEMP;
    BlinkState_PREV = BlinkState;
    menu_PREV = menu;
  }

}

//------------------------------------------------------------------------OIL TEMP & OIL PRESSURE
void display_OilP_FuelP() {
  if (OIL_PRESS != OIL_PRESS_PREV || FUEL_PRESSURE != FUEL_PRESSURE_PREV || BlinkState_PREV != BlinkState || menu != menu_PREV) {
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(textflashoilp);
    display.setCursor(0, 0);
    display.println("Oil P");
    display.setCursor(0, 10);

    display.setTextSize(3);
    display.setTextColor(textflashoilp);
    display.setCursor(1, 10);
    display.println(OIL_PRESS);

    display.setTextSize(1);
    display.setCursor(70, 0);
    display.setTextColor(WHITE);
    display.println("Fuel P");
    display.setTextColor(WHITE);
    display.setTextSize(3);
    display.setCursor(70, 10);
    display.println(FUEL_PRESSURE);
    display.display();
    OIL_PRESS_PREV = OIL_PRESS;
    FUEL_PRESSURE_PREV = FUEL_PRESSURE;
    BlinkState_PREV = BlinkState;
    menu_PREV = menu;
  }

}

//-----------------------------------------------------------------------------MILES REMAINING & AIR TEMP

void display_FuelR() {
  if (BlinkState_PREV != BlinkState || FUEL_LEVEL != FUEL_LEVEL_PREV || int(FUEL_RATE) != int(FUEL_RATE_PREV) || menu != menu_PREV) {
    display.clearDisplay();
    display.drawRect(0, 18, 125, 14, WHITE);

    //FuelTime
    int FuelRbar = map (int(FUEL_LEVEL), 0, 57, 0, 119);
    display.fillRect(3, 21, FuelRbar, 8, textflashfuel);
    display.setCursor(75, 0);
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.print(FUEL_RATE, 1);
    display.println(" l/h");
    display.setCursor(103, 0);
    display.setTextSize(1);
    display.setCursor(0, 0);
    display.setTextColor(textflashfuel);
    display.println("Fuel");
    display.display();
    FUEL_LEVEL_PREV = FUEL_LEVEL;
    FUEL_RATE_PREV = FUEL_RATE;
    BlinkState_PREV = BlinkState;
    menu_PREV = menu;
  }

}

//--------------------------------------------------------------------------------BATT VOLTAGE DISPLAY

void display_BattV() {
  if (BATT_VOLTAGE != BATT_VOLTAGE_PREV || menu != menu_PREV) {
    display.clearDisplay();
    display.setTextSize(1);
    display.setCursor(0, 0);
    display.setTextColor(WHITE);
    display.println("Batt");
    display.setTextColor(WHITE);
    display.setTextSize(3);
    display.setCursor(35, 10);
    display.print(BATT_VOLTAGE, 1);
    display.println("v");
    display.display();
    BATT_VOLTAGE_PREV = BATT_VOLTAGE;
    menu_PREV = menu;
  }

}

//---------------------------------------------------------------------------------DTC CODES
void display_troublecodes() {
  if (numDTC != numDTC_PREV || DTC != DTC_PREV || menu != menu_PREV) {
    display.clearDisplay();
    display.setTextSize(1);
    display.setCursor(20, 0);
    display.setTextColor(WHITE);
    display.print(numDTC);
    display.println(" DTCs Detected");
    display.setTextColor(WHITE);
    display.setTextSize(2);

    if (DTC != 4) {
      display.setCursor(35, 15);
      display.setTextSize(2);
    }
    else {
      display.setCursor(30, 20);
      display.setTextSize(1);
    }

    if (DTC == 4) display.println("DTCs Cleared");
    if (DTC == 1) display.println(DTC1);
    if (DTC == 2) display.println(DTC2);
    //Serial.println(DTC);
    display.display();
    numDTC_PREV = numDTC;
    DTC_PREV = DTC;
    menu_PREV = menu;
  }
}

//---------------------------------------------------------------------------------STATS DISPLAY

void display_Stats() {

  if (MaxSpeed != MaxSpeed_PREV || MaxRPM != MaxRPM_PREV || MaxTemp != MaxTemp_PREV || MaxOil != MaxOil_PREV || menu != menu_PREV) {
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(WHITE);

    display.setCursor(38, 0);
    display.println("Max Stats");

    display.setCursor(5, 15);
    display.println("MPH");
    display.setCursor(30, 15);
    display.println(MaxSpeed);

    display.setCursor(5, 25);
    display.println("RPM");
    display.setCursor(30, 25);
    display.println(MaxRPM);

    display.setCursor(70, 15);
    display.println("WatT");
    display.setCursor(100, 15);
    display.println(MaxTemp);

    display.setCursor(70, 25);
    display.println("OilT");
    display.setCursor(100, 25);
    display.println(MaxOil, 0);
    display.display();
    MaxSpeed_PREV = MaxSpeed;
    MaxRPM_PREV = MaxRPM;
    MaxTemp_PREV = MaxTemp;
    MaxOil_PREV = MaxOil;
    menu_PREV = menu;
  }

}


//-------------------------------------------------------------------------------SERVICE INTERVAL
void display_servicedist() {
  distance_since_service = (servicedistthousandkm * 1000) + (servicedisthundredkm * 100) + (servicedistkm * 10) + servicedisthundredm  ;
  distance_to_service = service_interval - distance_since_service;

  if (distance_to_service != distance_to_service_PREV || menu != menu_PREV) {
    if   (distance_since_service <= service_interval) {
      servicetext = ("NEXT SERVICE");
    }
    else {
      servicetext = ("SERVICE OVERDUE");
    }
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(0, 3);
    display.println(servicetext);
    display.setCursor(80, 17);
    display.setTextSize(2);
    display.println(distance_to_service);
    display.display();
    distance_to_service_PREV = distance_to_service;
    menu_PREV = menu;
  }
}


//--------------------------------------------------------------------------------0-60 TIMER
void display_Acceltimer() {


  //display.clearDisplay();

  if (timerstart == 1 && timerstop == 0) {
    mode060 = 1;
    if (mode060 != mode060_PREV || menu != menu_PREV) {
      display.clearDisplay();
      display.setTextSize(2);
      display.setTextColor(WHITE);
      display.setCursor(15, 0);
      display.println("READY..");
      display.setTextSize(1);
      display.setCursor(0, 23);
      display.println("Prev: ");
      display.setCursor(30, 23);
      display.println(timeseconds);
      display.setCursor(65, 23);
      display.println("Best: ");
      display.setCursor(95, 23);
      display.println(Best060);
      display.display();
      mode060_PREV = mode060;
      menu_PREV = menu;
    }
  }

  if (SPEED_MPH >= 1 && timerstart == 1) {
    mode060 = 2;
    Startmillis = millis();
    timerstart = 0;
    timerstop = 1;
  }
  if (mode060 == 2 && SPEED_MPH < 60 && SPEED_MPH != SPEED_MPH_PREV) {
    display.clearDisplay();
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(15, 0);
    display.println("GO!");

    display.setTextSize(4);
    display.setCursor(70, 0);
    display.println(SPEED_MPH);
    display.display();
    menu_PREV = menu;
    SPEED_MPH_PREV = SPEED_MPH;
  }

  if (SPEED_MPH >= 60 && timerstop == 1) {
    mode060 = 4;
    Finishmillis = millis();
    timerstop = 0;
    timeto60 = Finishmillis - Startmillis;
    timeseconds = timeto60 / 1000.0;
    if ((timeseconds * 1000) < (Best060 * 1000)) {
      Best060 = timeseconds;
    }
    if (Best060 == 0) {
      Best060 = timeseconds;
    }
  }

  if (timerstart == 0 && timerstop == 0)  {
    mode060 = 3;
    if (mode060 != mode060_PREV || menu != menu_PREV) {
      display.clearDisplay();
      display.setTextSize(1);
      display.setTextColor(WHITE);
      display.setCursor(0, 0);
      display.println("Time");
      display.setTextSize(2);
      display.setCursor(40, 0);
      display.println(timeseconds);
      display.setTextSize(1);
      display.setCursor(5, 23);
      display.println("Stop Car To Restart");
      display.display();
      mode060_PREV = mode060;
      menu_PREV = menu;
    }
    if (SPEED_MPH == 0) {

      timerstart = 1;
    }
  }
}

//--------------------------------------------------------------------------------SETUP
void display_Setup() {
  if (menu != menu_PREV) {
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.clearDisplay();
    display.setCursor(35, 0);
    display.print("Setup");
    display.setTextSize(1);
    display.setCursor(31, 24);
    display.print("Short Press");
    display.display();
    menu_PREV = menu;
  }
}



//===============================================================================================SETUP SUB-MENU
//--------------------------------------------------------------------------------GEARS
void Gear_Setup() {

  if (SetGear != SetGear_PREV || menu != menu_PREV) {

  }
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.clearDisplay();
  display.setCursor(5, 0);

  if (SetGear == 1) display.println("Select Neutral");
  if (SetGear == 2) display.println("Select 1st Gear");
  if (SetGear == 3) display.println("Select 2nd Gear");
  if (SetGear == 4) display.println("Select 3rd Gear");
  if (SetGear == 5) display.println("Select 4th Gear");
  if (SetGear == 6) display.println("Select 5th Gear");
  if (SetGear == 7) display.println("Select 6th Gear");
  if (SetGear == 8) display.println("Select Reverse");
  if (SetGear == 9) display.println("Gear Positions Saved");

  display.setCursor(15, 15);
  display.println("Press Menu Button");
  display.display();
  SetGear_PREV = SetGear;
  menu_PREV = menu;
}

//--------------------------------------------------------------------------------SPEED UNITS
void setup_SpeedUnits() {
  if (ChangeSource == 1 || menu != menu_PREV) {
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.clearDisplay();
    display.setCursor(7, 0);

    display.print("Speed Display Units");

    display.setCursor(45, 16);
    display.setTextSize(2);

    if (SPEED_DISPLAY == 1) display.print("MPH");
    else if (SPEED_DISPLAY == 2) display.print("KPH");
    display.display();
    ChangeSource = 0;
    menu_PREV = menu;
  }
}

//--------------------------------------------------------------------------------SPEED INPUT SOURCE
void setup_SpeedSource() {
  if (ChangeSource == 1 || menu != menu_PREV) {
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.clearDisplay();
    display.setCursor(32, 0);

    display.print("Speed Input");
    display.setTextSize(2);
    if (MODE_SPEED == 1) {
      display.setCursor(30, 16);
      display.print("Analog");
    }
    else if (MODE_SPEED == 2) {
      display.setCursor(45, 17);
      display.print("CAN");
    }
    display.display();
    ChangeSource = 0;
    menu_PREV = menu;
  }
}

//--------------------------------------------------------------------------------RPM INPUT SOURCE
void setup_RPMSource() {
  if (ChangeSource == 1 || menu != menu_PREV) {
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.clearDisplay();
    display.setCursor(38, 0);

    display.print("RPM Input");
    display.setTextSize(2);
    if (MODE_RPM == 1) {
      display.setCursor(30, 16);
      display.print("Analog");
    }
    else if (MODE_RPM == 2) {
      display.setCursor(45, 17);
      display.print("CAN");
    }
    display.display();
    ChangeSource = 0;
    menu_PREV = menu;
  }
}

//--------------------------------------------------------------------------------COOLANT TEMP INPUT SOURCE
void setup_CoolTSource() {
  if (ChangeSource == 1 || menu != menu_PREV) {
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.clearDisplay();
    display.setCursor(11, 0);

    display.print("Coolant Temp Input");
    display.setTextSize(2);
    if (MODE_COOLANT_TEMP == 1) {
      display.setCursor(30, 16);
      display.print("Analog");
    }
    else if (MODE_COOLANT_TEMP == 2) {
      display.setCursor(45, 17);
      display.print("CAN");
    }
    display.display();
    ChangeSource = 0;
    menu_PREV = menu;
  }
}

//--------------------------------------------------------------------------------AIR TEMP INPUT SOURCE
void setup_AirTSource() {
  if (ChangeSource == 1 || menu != menu_PREV) {
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.clearDisplay();
    display.setCursor(11, 0);

    display.print("Ambient Temp Input");
    display.setTextSize(2);
    if (MODE_AMBIENT_TEMP == 1) {
      display.setCursor(30, 16);
      display.print("Analog");
    }
    else if (MODE_AMBIENT_TEMP == 2) {
      display.setCursor(45, 17);
      display.print("CAN");
    }
    display.display();
    ChangeSource = 0;
    menu_PREV = menu;
  }
}

//--------------------------------------------------------------------------------OIL TEMP INPUT SOURCE
void setup_OilTSource() {
  if (ChangeSource == 1 || menu != menu_PREV) {
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.clearDisplay();
    display.setCursor(23, 0);

    display.print("Oil Temp Input");
    display.setTextSize(2);
    if (MODE_OIL_TEMP == 1) {
      display.setCursor(30, 16);
      display.print("Analog");
    }
    else if (MODE_OIL_TEMP == 2) {
      display.setCursor(45, 17);
      display.print("CAN");
    }
    display.display();
    ChangeSource = 0;
    menu_PREV = menu;
  }
}

//--------------------------------------------------------------------------------ZERO STEPPERS
void setup_ZeroStep() {
  if (Done != Done_PREV || menu != menu_PREV) {
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.clearDisplay();
    display.setCursor(16, 0);
    display.print("Re-Zero Steppers");
    display.setTextSize(1);
    if (Done == 0) {
      display.setCursor(33, 24);
      display.print("Long Press");
    }
    else if (Done == 1) {
      display.setTextSize(2);
      display.setCursor(40, 17);
      display.print("DONE");
    }
    display.display();
    menu_PREV = menu;
    Done_PREV = Done;
  }
}

//--------------------------------------------------------------------------------SET UP GEARS
void setup_Gears() {
  if (menu != menu_PREV) {
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.clearDisplay();
    display.setCursor(12, 0);
    display.print("Recalibrate Gears");
    display.setTextSize(1);
    display.setCursor(33, 24);
    display.print("Long Press");
    display.display();
    menu_PREV = menu;
  }
}

//--------------------------------------------------------------------------------ZERO ODOMETER
void setup_ZeroODO() {
  if (Done != Done_PREV || menu != menu_PREV) {
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.clearDisplay();
    display.setCursor(20, 0);
    display.print("Reset Odometer");
    display.setTextSize(1);
    if (Done == 0) {
      display.setCursor(33, 24);
      display.print("Long Press");
    }
    else if (Done == 1) {
      display.setTextSize(2);
      display.setCursor(40, 17);
      display.print("DONE");
    }
    display.display();
    menu_PREV = menu;
    Done_PREV = Done;
  }
}

//--------------------------------------------------------------------------------SHOW ALIGNMENT BOX
void setup_ShowAlignBox() {
  if (menu != menu_PREV) {
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.clearDisplay();
    display.setCursor(11, 0);
    display.print("Show Alignment Box");
    display.setTextSize(1);
    display.setCursor(33, 24);
    display.print("Long Press");
    display.display();
    menu_PREV = menu;
  }
}

//--------------------------------------------------------------------------------EXIT SUB MENU
void setup_ExitSubmenu() {
  if (menu != menu_PREV) {
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.clearDisplay();
    display.setCursor(6, 0);
    display.print("Exit Setup");
    display.setTextSize(1);
    display.setCursor(31, 24);
    display.print("Short Press");
    display.display();
    menu_PREV = menu;
  }
}

//--------------------------------------------------------------------------------DARK MODE
void display_DarkMode() {
  if (menu != menu_PREV || DarkMode != DarkMode_PREV) {

    if (DarkMode == 0) {
      display.setTextSize(2);
      display.setTextColor(WHITE);
      display.clearDisplay();
      display.setCursor(12, 0);
      display.print("Dark Mode");
      display.setTextSize(1);
      display.setCursor(31, 24);
      display.print("Short Press");
      display.display();
      menu_PREV = menu;
    }
    else if (DarkMode == 1) {
      display.clearDisplay();
      display.display();
    }
    DarkMode_PREV = DarkMode;
  }
}
//========================================================================================END OLED DISPLAY FUNCTIONS

1 Like

some simple things without really figuring out how the code work

put all the #defines in a head file (-66 lines)

rather than invoke mostly the same commands to update the display, create a single function that updates the display given a set of strings.

void
disp (
    char *s0,
    char *s0 )
{
        display.setTextSize(1);
        display.setTextColor(WHITE);

        display.clearDisplay();
        display.setCursor(11, 0);
        display.print(s0);

        display.setTextSize(1);
        display.setCursor(33, 24);
        display.print(s1)

        display.display();
}

void setup_ShowAlignBox() {
    if (menu != menu_PREV) {
        disp("Show Alignment Box", "Long Press");
        menu_PREV = menu;
    }
}

Your variables with an SER suffix can do the job for you. Add a boolean for each display function Use it to control whether to update the display. Before you set the main variable to the SER one, if they’re not the same, set the boolean. Clear it in the display function.

It will only eliminate one variable in things like speed, but on the oil and fuel pressure screen, it’s two.

Ultimately though, I think a more generic, data driven approach to driving the displays is needed, but I will need more coffee for that :slightly_smiling_face:

i think it also makes sense to use a table describing EEPROM contents.

a table (struct) can identify a address and pointer to a variable. additional table fields can identify type of variable (e.g. byte, int, string). routines can then loop over the table to read or write to the EEPROM, handling different types generically with just a few cases. (looks like you have a 146 lines of code dealing with 38 eeprom values).

there’s probably other places your code could use tables and be data driven instead of logic driven. functionality can then be easily expanded by adding table entries without getting bogged down with logic

Algorithms + Data Structures = Programs

Thanks both! Yes I have recently learned about Structs and have one in there now but perhaps I could also tackle the EEPROM. I don’t understand how you can have rows in the table using different data types though when using pointers. Do you not have to stick with one data type per field?

Regarding the display, I had thought about using a generic display function but some of the screens are very different in their construction so I’d need to make it quite clever. Maybe worth some more thought…

Looks like you have several variables with the same name followed by CAN, SER, and PREV. One method I’ve used it to make an array of variables, another way is to make each variable a struct. Does not change the amount of memory used for the variables, but does tend to make the declarations a lot shorter, as well as making it easier to do something like compare the previous and current values of a variable using a function.

const byte cur = 0;
const byte prev = 1;
const byte ser = 2;
const byte can = 3;
int SPEED_MPH[4]; 
//referenced as SPEED_MPH[cur], SPEED_MPH[prev], SPEED_MPH[ser], SPEED_MPH[can]

struct four_int {
  int cur;
  int prev;
  int ser;
  int can;
};
four_int SPEED_MPH; 
//referenced as SPEED_MPH.cur, SPEED_MPH.prev SPEED_MPH.ser SPEED_MPH.can

Make sure you have a copy (I guess this thread takes care of that) before you mess with your code. While it’ll certainly be instructive, you might consider the old saw “If it ain’t broke, don’t fix it” :wink:

here’s an incomplete example. pointers are all cast as void*

struct CfgVar_s {
    CfgId_e     id;
    void       *p;
    byte        nByte;
    byte        type;
    const char *desc;
};

// -------------------------------------
//  stored variables

CfgVar_s cfgVarTbl [] = {
    { C_NAME, (void*)   name,       MAX_CHAR,       V_STR,  "name" },

    { C_SSID, (void*)   ssid,       MAX_CHAR,       V_STR,  "ssid"},
    { C_PASS, (void*)   pass,       MAX_CHAR,       V_STR,  "password" },
    { C_HOST, (void*)   host,       MAX_CHAR,       V_STR,  "hostname" },
    { C_PORT, (void*) & port,       sizeof(int),    V_INT,  "port" },
    { C_L_00, (void*) & locos [0],  sizeof(Loco_s), V_LOCO, "loco_1" },
    { C_L_01, (void*) & locos [1],  sizeof(Loco_s), V_LOCO, "loco_2" },

here’s code operating on the table and saving it using SPIFFS

void
cfgSave (
    const char *filename )
{
    printf ("%s: %s\n", __func__, filename);

    FILE * fp = fopen (filename, "wb+");
    if (NULL == fp)  {
        perror ("cfgSave - fopen");
        return;
    }

    if (1 != fwrite ((void*) cfgMagic, sizeof(cfgMagic), 1, fp)) {
        perror ("cfgSave: fwrite cfgMagic");
        goto done;
    }

    CfgHdr_s  hdr;
    for (CfgVar_s *p = cfgVarTbl; V_NONE != p->type; p++)  {
        hdr.id   = p->id;
        hdr.size = p->nByte;

        if (1 != fwrite ((void*) & hdr, sizeof(hdr), 1, fp)) {
            perror ("cfgSave: fwrite hdr");
            goto done;
        }

        int nwr = fwrite ((void*) p->p, p->nByte, 1, fp);
        if (! nwr)  {
            printf (" %s: fwrite incomplete, %d %d",
                        __func__, nwr, p->nByte);
            perror (" cfgSave - fwrite");
            return;
        }
    }

done:
    fclose (fp);
}

it would be simpler if you updated the entire display instead of specific cursor positions. whenever you update, create a complete set of strings that are passed to come update display function, or else pass cursor positions for each string

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.