Serial communication+button menu on LCD

This is teensy 3.2 so perhaps wrong place?
Right, so I’m making an car computer (I know, there are pre-made and cheaper models out there) by using sparkfun’s obd-uart board, teensy 3.2 and i2c 16x2 lcd. Idea is simple: Send AT commands to obd and display result (converted/calculated) on screen, while also updating. The problem currently is that even with that one Serial print,menu system doesn’t update conveniently quick & doesn’t update, seems it only sends one serial string data in current code. What I’d like it to do, is that it constantly updates screen, and menu could be changed.
Code I’m currently working on:
obd-uart board:

I’ve managed to get couple live information as real-life data, but there are many stuff hidden in that sketch…
Ask if there is anything confusing!
So, if i take serial stuff out and print only menu system on lcd, works like charm, but serial communication causes problems.
and originally found code here, trying to change this to my needs:

int const UpButton = 2;
int const DownButton = 3;
int UpButtonState;
int LastUpButtonState;
int DownButtonState;
int LastDownButtonState;
int counter;
char rxData[20];
char rxIndex = 0;
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
String a;

// Set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup()
{
  pinMode (UpButton, INPUT);
  pinMode (DownButton, INPUT);
  lcd.begin();
  lcd.backlight();
  Serial1.begin(9600);
  //ODB_init();


}

void loop()
{
  menuselect();
}

void menuselect()
{
  UpButtonState = digitalRead (UpButton);
  if (UpButtonState != LastUpButtonState)
  {
  lcd.clear();
  if (UpButtonState == HIGH)
  {
  counter++;
  }
  LastUpButtonState = UpButtonState;
  }
  if (counter > 9)
  { (counter = 1);
  }

  DownButtonState = digitalRead (DownButton);
  if (DownButtonState != LastDownButtonState)
  {
  lcd.clear();
  if (DownButtonState == HIGH)
  {
  counter--;
  }
  LastDownButtonState = DownButtonState;
  }
  if (counter == 0)
  {
  (counter = 9);
  }



  switch (counter)  //switch what is displayed depending on number of counter
  {
  case 1:  //speed
  lcd.setCursor(0, 0);
  lcd.print("KM/h:");
  lcd.setCursor(5,0);
  Serial1.print("H");  //just for testing, but even this causes momentary freeze
  a = Serial1.readString(); // read the incoming data as string
  lcd.print(a);
  lcd.setCursor(0, 1);
  lcd.print("<-Clear  RPM->");
  break;
  case 2:  //RPM
  lcd.setCursor(0, 0);
  lcd.print("RPM:");
  lcd.setCursor(0, 1);
  lcd.print("<-Speed  Cool.->");
  break;
  case 3:  //coolant
  lcd.setCursor(0, 0);
  lcd.print("`c");
  lcd.setCursor(0, 1);
  lcd.print("<-RPM  Intake->");
  break;
  case 4:
  lcd.setCursor(0, 0);
  lcd.print("`c");
  lcd.setCursor(0, 1);  //Intake
  lcd.print("<-Coolant  MAF->");
  break;
  case 5:
  lcd.setCursor(0, 0);
  lcd.print("g/sec:");
  lcd.setCursor(0, 1);  //MAF
  lcd.print("<-Intake Pedal->");
  break;
  case 6:
  lcd.setCursor(0, 0);
  lcd.print("%:");
  lcd.setCursor(0, 1);  //Pedal
  lcd.print("<-MAF  Stress->");
  break;
  case 7:
  lcd.setCursor(0, 0);
  lcd.print("%:");
  lcd.setCursor(0, 1);  //Stress
  lcd.print("<-Pedal  RunT.->");
  break;
  case 8:
  lcd.setCursor(0, 0);
  lcd.print("T:");
  lcd.setCursor(0, 1);  //Run Time
  lcd.print("<-Stress Clear->");
  break;
  case 9:
  lcd.setCursor(0, 0);
  lcd.print("Clear codes?");
  lcd.setCursor(0, 1);  //clear
  lcd.print("<-RunT.  Speed->");
  break;
  }
}

/*void ODB_init(void)
{
  //Wait for a little while before sending the reset command to the OBD-II-UART
  delay(2000);
  //Reset the OBD-II-UART
  Serial1.print("ATZ\r");
  //Wait for a bit before starting to send commands after the reset.
  delay(2000);
  OBD_read();
  Serial1.print("ATE0\r");
  OBD_read();
  Serial1.flush();
}

void OBD_read(void)
{
  char c;
  do {
  if (Serial1.available() > 0)
  {
  c = Serial1.read();
  //lcd.print(c);
  if ((c != '>') && (c != '\r') && (c != '\n')) //Keep these out of our buffer
  {
  rxData[rxIndex++] = c; //Add whatever we receive to the buffer
  }
  }
  } while (c != '>'); //The ELM327 ends its response with this char so when we get it we exit out.
  rxData[rxIndex++] = '\0';//Converts the array into a string
  rxIndex = 0; //Set this to 0 so next time we call the read we get a "clean buffer"
}


int getSpeed(void)
{
  //Query the OBD-II-UART for the Vehicle rpm
  Serial1.flush();
  Serial1.print("0111\r");
  OBD_read();
  return strtol(&rxData[6], 0, 16) * 100 / 255;
}*/

It looks as if you are updating the LCD on every iteration of loop(). Try limiting it to once or twice per second and see what happens. Writing to the LCD (or to the Serial port) is a slow operation.

...R

Thanks robin, I tried to disable serial with this but didn’t help, placed on start of loop() (btw, I had to switch to pro mini now as teensy doesn’t work for some reason with my car’s usb ports…but pro mini works just fine)

Serial.begin(9600);
if(millis()/1000 % 2
{
  Serial.end();
}

And here’s code I talked earlier which shows real-life data, only bit flickery, I mean those value portions of screen, I did notice lcd.Display() and lcd noDisplay(), perhaps those could be used to display better.
Menu system is not in this sketch yet…but figured to show this too anyway

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);



//Set up ring buffer
char rxData[20];
char rxIndex = 0;


void setup()
{
  lcd.begin();
  lcd.backlight();
  Serial.begin(9600);
  ODB_init();
}

void loop()
{
    Serial.flush();
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Temp:");
  lcd.setCursor(0, 1);
  lcd.print("RPM:");
  Serial.flush();
  lcd.setCursor(4, 1);
  lcd.print("    ");
  lcd.setCursor(4, 1);
  lcd.print(getRPM());
  Serial.flush();
  lcd.setCursor(5, 0);
  lcd.print("    ");
  lcd.setCursor(5, 0);
lcd.print(getTemp());
delay(200);

}

void ODB_init(void)
{
  //Wait for a little while before sending the reset command to the OBD-II-UART
  delay(2000);
  //Reset the OBD-II-UART
  Serial.print("ATZ\r");
  //Wait for a bit before starting to send commands after the reset.
  delay(2000);
  OBD_read();
  Serial.print("ATE0\r");
  OBD_read();
  Serial.flush();
}

int getLoad(void)
{
  Serial.flush();
  Serial.print("0104\r");
  OBD_read();
  return strtol(&rxData[6], 0, 16)*100/255;
}

int getTemp(void)                       //coolant temperature
{
  Serial.flush();
  Serial.print("0105\r");
  OBD_read();
  return strtol(&rxData[6], 0, 16) - 40;
}

int getRPM(void)                   //RPM
{
  Serial.flush();
  Serial.print("010C\r");
  OBD_read();
  return ((strtol(&rxData[6], 0, 16) * 256) + strtol(&rxData[9], 0, 16)) / 4;
}


int getSpeed(void)                  //speed km/h
{
  Serial.flush();
  Serial.print("010D\r");
  OBD_read();
  return strtol(&rxData[6], 0, 16);
}

int getIntake(void)                 //intake air temperature
{
  Serial.flush();
  Serial.print("010F\r");
  OBD_read();
  return strtol(&rxData[6], 0, 16) - 40;
}

int getMAF(void)                   //MAf rate, g/sec
{
  Serial.flush();
  Serial.print("0110\r");
  OBD_read();
  return ((strtol(&rxData[6], 0, 16) * 256) + strtol(&rxData[9], 0, 16)) / 100;
}


int getThrottle(void)              //throttle, %
{
  Serial.flush();
  Serial.print("0111\r");
  OBD_read();
  return strtol(&rxData[6], 0, 16)*100/255;
}

int getRuntime(void)                   //Run time
{
  Serial.print("011F\r");
  OBD_read();
  return ((strtol(&rxData[6], 0, 16) * 256) + strtol(&rxData[9], 0, 16));
}


void OBD_read(void)
{
  char c;
  do {
    if (Serial.available() > 0)
    {
      c = Serial.read();
      //lcd.print(c);
      if ((c != '>') && (c != '\r') && (c != '\n')) //Keep these out of our buffer
      {
        rxData[rxIndex++] = c; //Add whatever we receive to the buffer
      }
    }
  } while (c != '>'); //The ELM327 ends its response with this char so when we get it we exit out.
  rxData[rxIndex++] = '\0';//Converts the array into a string
  rxIndex = 0; //Set this to 0 so next time we call the read we get a "clean buffer"
}
    Serial.flush();
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Temp:");
  lcd.setCursor(0, 1);
  lcd.print("RPM:");
  Serial.flush();

Without having sent anything out the serial port, block until all data has been sent. Then, without having sent anything, block again until all data has been sent.

Clearly, you do not understand what Serial.flush() does, so quit calling it.

My bad, I thought serial.flush() just empties any serial data still lurking, but not sending it. I admit, I didnt know what serial.flush does!

fezder:
Thanks robin, I tried to disable serial with this but didn’t help,

My suggestion in Reply #1 was NOT about Serial - it was about the frequency of updating the LCD.

…R

Robin2:
Writing to the LCD (or to the Serial port) is a slow operation.

Misread this one here then it seems....

I got it working, used interrupt to change menus, will post code here once I get it bit neater. I read that interrupts can cause serial data to be missed, but in this application data is re-called instantly so it doesn't matter

Got it working, code, improvements welcome!

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
char rxData[20];
char rxIndex = 0;
String a;
volatile int counter = 1;
void setup()
{
  lcd.begin();
  lcd.backlight();
  //Serial.begin(9600);
  //ODB_init();
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  attachInterrupt(digitalPinToInterrupt(2), MenuUp, RISING);
  attachInterrupt(digitalPinToInterrupt(3), MenuDown, RISING);
}


void MenuUp() {
  counter++;
  if (counter > 9)
  {
    counter = 1;
  }
}


void MenuDown() {
  counter--;
  if (counter == 0)
  {
    counter = 9;
  }
}

void loop()
{

delay(1000);
  lcd.clear();

  switch (counter)
  {

    case 1:

  lcd.setCursor(0, 0);
  lcd.print("KM/h:");
  lcd.setCursor(0, 1);
  lcd.print("<-Clear  RPM->");
  lcd.setCursor(5, 0);
 // lcd.print(getSpeed());

      break;

    case 2:
  lcd.setCursor(0, 0);
  lcd.print("RPM:");
  lcd.setCursor(0, 1);
  lcd.print("<-Speed  Cool.->");
  lcd.setCursor(5, 0);
  //lcd.print(getRPM());
      break;

    case 3:

  lcd.setCursor(0, 0);
  lcd.print("`c");
  lcd.setCursor(0, 1);
  lcd.print("<-RPM  Intake->");
  lcd.setCursor(5, 0);
 // lcd.print(getTemp());
      break;

    case 4:
  lcd.setCursor(0, 0);
  lcd.print("`c");
  lcd.setCursor(0, 1);  //Intake
  lcd.print("<-Coolant  MAF->");
  lcd.setCursor(5, 0);
  //lcd.print(getIntake());
      break;

    case 5:

  lcd.setCursor(0, 0);
  lcd.print("g/sec:");
  lcd.setCursor(0, 1);  //MAF
  lcd.print("<-Intake Pedal->");
  lcd.setCursor(5, 0);
 // lcd.print(getMAF());
      break;

    case 6:

  lcd.setCursor(0, 0);
  lcd.print("%:");
  lcd.setCursor(0, 1);  //Pedal
  lcd.print("<-MAF  Stress->");
  lcd.setCursor(5, 0);
 // lcd.print(getThrottle());
      break;

    case 7:
  lcd.setCursor(0, 0);
  lcd.print("%:");
  lcd.setCursor(0, 1);  //Stress
  lcd.print("<-Pedal  RunT.->");
  lcd.setCursor(5, 0);
  //lcd.print(getStress());
      break;

    case 8:
  lcd.setCursor(0, 0);
  lcd.print("T:");
  lcd.setCursor(0, 1);  //Run Time
  lcd.print("<-Stress Clear->");
  lcd.setCursor(5, 0);
  //lcd.print(getRuntime());
      break;

    case 9:
  lcd.setCursor(0, 0);
  lcd.print("Clear codes?");
  lcd.setCursor(0, 1);  //clear
  lcd.print("<-RunT.  Speed->");
      break;
  }

}




void ODB_init(void)
{
  //Wait for a little while before sending the reset command to the OBD-II-UART
  delay(2000);
  //Reset the OBD-II-UART
  Serial.print("ATZ\r");
  //Wait for a bit before starting to send commands after the reset.
  delay(2000);
  OBD_read();
  Serial.print("ATE0\r");
  OBD_read();
  Serial.flush();
}

int getStress(void)                                                  //Stress
{
  Serial.flush();
  Serial.print("0104\r");
  OBD_read();
  return strtol(&rxData[6], 0, 16) * 100 / 255;
}

int getTemp(void)                                                    //coolant temperature
{
  Serial.flush();
  Serial.print("0105\r");
  OBD_read();
  return strtol(&rxData[6], 0, 16) - 40;
}

int getRPM(void)                                                     //RPM
{
  Serial.flush();
  Serial.print("010C\r");
  OBD_read();
  return ((strtol(&rxData[6], 0, 16) * 256) + strtol(&rxData[9], 0, 16)) / 4;
}


int getSpeed(void)                                                  //speed km/h
{
  Serial.flush();
  Serial.print("010D\r");
  OBD_read();
  return strtol(&rxData[6], 0, 16);
}

int getIntake(void)                                              //intake air temperature
{
  Serial.flush();
  Serial.print("010F\r");
  OBD_read();
  return strtol(&rxData[6], 0, 16) - 40;
}

int getMAF(void)                                                  //MAF rate, g/sec
{
  Serial.flush();
  Serial.print("0110\r");
  OBD_read();
  return ((strtol(&rxData[6], 0, 16) * 256) + strtol(&rxData[9], 0, 16)) / 100;
}


int getThrottle(void)                                            //throttle, %
{
  Serial.flush();
  Serial.print("0111\r");
  OBD_read();
  return strtol(&rxData[6], 0, 16) * 100 / 255;
}

int getRuntime(void)                                             //Run time
{
  Serial.print("011F\r");
  OBD_read();
  return ((strtol(&rxData[6], 0, 16) * 256) + strtol(&rxData[9], 0, 16));
}


void OBD_read(void)
{
  char c;
  do {
    if (Serial.available() > 0)
    {
      c = Serial.read();
      //lcd.print(c);
      if ((c != '>') && (c != '\r') && (c != '\n')) //Keep these out of our buffer
      {
        rxData[rxIndex++] = c; //Add whatever we receive to the buffer
      }
    }
  } while (c != '>'); //The ELM327 ends its response with this char so when we get it we exit out.
  rxData[rxIndex++] = '\0';//Converts the array into a string
  rxIndex = 0; //Set this to 0 so next time we call the read we get a "clean buffer"
}

code, improvements welcome!

Get rid if this crap:

String a;

First, the String class has no place on a limited memory microcontroller like the Arduino.

Second, the name doesn't mean a thing. Variable names, especially global ones, need to mean something.

volatile int counter = 1;

Since counter is to hold values in the range 0 to 9, you might need to make this a long long, instead.

void loop()
{

delay(1000);

Fart away a full second on EVERY pass through loop(). That is just SO wrong.

Get rid of this stupid delay, and you won't need interrupts to make the code responsive.

  Serial.flush();
  Serial.print("0104\r");
  OBD_read();

Block until all pending serial data has been sent. Then, send some more, and expect it to be delivered immediately. Nope! Ain't gonna happen.

If there is ANY value to flushing the outgoing serial buffer (blocking until it is empty), the time to do it is AFTER sending data that must be delivered before a read can possibly be expected to work.

  rxIndex = 0; //Set this to 0 so next time we call the read we get a "clean buffer"

That is NOT why to set rxIndex to 0.

  return ((strtol(&rxData[6], 0, 16) * 256) + strtol(&rxData[9], 0, 16));

Regardless of how much data is actually in the rxData array, use some possibly garbage values. Hmmm. Not on MY microcontroller.

back to drawing board :)...

By eliminating the String variable “a”, which you don’t even use, the sketch size goes from 9230 bytes to 7856 bytes.

By using the F macro for double-quoted literals (C strings), the RAM usage goes from 1222 bytes to
to 959 bytes:

  lcd.print( F("<-Clear  RPM->") );

Well, you’re on a Teensy, so that doesn’t matter, but it would help anyone who uses your code on an UNO or other 8-bit AVR board.

Instead of delaying in loop, set a flag in OBD_read. Then check the flag in loop and only update if there’s new data:

bool newData = false;

void loop()
{
  if (newData) {
    newData = false; // clear the flag for next time

    // lcd print stuff...

  }
}

void OBD_read(void)
{
   .
   .
   .
  } while (c != '>'); //The ELM327 ends its response with this char so when we get it we exit out.
  rxData[rxIndex++] = '\0';//Converts the array into a string
  rxIndex = 0; //Set this to 0 so next time we call the read we get a "clean buffer"
  newData = true;
}

But you also need to change OBD_read so it doesn’t wait until it gets the whole thing. Go read Serial Input Basics. Then your loop would be:

void loop()
{
  OBD_read(); // get a little data, might set the newData flag...

  if (newData) {
    newData = false; // clear the flag for next time

    // lcd print stuff...
  }
}

Notice: no delays.

You might want to search for “button debounce”. MenuUp/Down are too simple, and they really don’t need to be attached to an interrupt. Just check them in loop, like OBD_read and newData.

Cheers,
/dev

this was left behind of one earlier sketch where I tested what sort of data does OBD board send back, forgot to delete it...

String a;

Hmm? So int isn't big enough storage to hold values up to 9?

volatile int counter = 1;

Well, this was only to reduce flicker on lcd, but of course there should be better way....the reason why I ended using interrupts were because for some reason that button-counter I had earlier didn't work during Serial reads (or anytime after read-section in loop) Interrupt can on the other hand happen anytime, althought it can mess up serial communication....Buddy suggested that there is "waiting for user input period" but there isn't any of such in current car computer I have now (reason why I'm building new one is because UI is russian in current one.)

void loop()
{
delay(1000);

I STILL thought that flush empties serial buffer, again

 Serial.flush();
  Serial.print("0104\r");
  OBD_read();

These lines weren't from my head at all, but I didn't bother with them as they "worked"....

 rxIndex = 0; //Set this to 0 so next time we call the read we get a "clean buffer"
  return ((strtol(&rxData[6], 0, 16) * 256) + strtol(&rxData[9], 0, 16));

good thing at least someone reads and corrects stuff up!

@/dev, I noticed yor reply while writing this but gotta wait while due forums "rules"

The reason why I ended using teensy is that I have in mind to attach other stuff too along same system, like SIM800L for tracking purposes and to send SMS in case car doors are opened without my presence, as well as other "pointless" stuff but car computer is main issue for now....I might have to ditch LCD, won't work that well in cold temperatures, but I do have some small (0.96") OLED screens.
Those memory saving tips were great, hopefully I can remember them in time of need.....
As for Serial knowledge, sadly I don't as of yet have much experience of serial communication, apart from basics you could say like baud rates and such.

fezder:
Hmm? So int isn't big enough storage to hold values up to 9?

@PaulS was making fun. An int is bigger than needed, A byte will be sufficient and will halve the memory usage.

...R

Robin2:
@PaulS was making fun. An int is bigger than needed, A byte will be sufficient and will halve the memory usage.

…R

Lol, you have no idea how much I pondered why int Isn’t enough…

But, made revision of code that has booleans and other stuff you guys suggested, menu system works and data flows all the time, but no data is shown on screen, only very quikcly. I can’t find any obvious reason, as other stuff like stationary letters are clearly visible

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
char rxData[20];
char rxIndex = 0;
int const UpButton = 2;
int const DownButton = 3;
int UpButtonState;
int LastUpButtonState;
int DownButtonState;
int LastDownButtonState;
long counter = 1;
bool newData = false;
bool buttonPressed = false;   //initially buttons hasn't been pressed

void setup()
{
  lcd.begin();
  lcd.backlight();
  Serial.begin(9600);
  ODB_init();
  pinMode (UpButton, INPUT);
  pinMode (DownButton, INPUT);
  digitalWrite(UpButton, HIGH);       // turn on pullup resistors
  digitalWrite(DownButton, HIGH);       // turn on pullup resistors
}

void loop()
{

  UpButtonState = digitalRead (UpButton);
  if (UpButtonState != LastUpButtonState)
  {
    buttonPressed=true;        //we got new press
  if (UpButtonState == HIGH)
  {
  counter++;
  }
  LastUpButtonState = UpButtonState;
  }

    else
  {
    buttonPressed=false;
  }

  
  if (counter > 8)
  {
    (counter = 1);
  }

  DownButtonState = digitalRead (DownButton);
  if (DownButtonState != LastDownButtonState)
  {
        buttonPressed=true;     //we got new press
  if (DownButtonState == HIGH)
  {
  counter--;
  }
  LastDownButtonState = DownButtonState;
  }

  else
  {
    buttonPressed=false;
  }
  if (counter == 0)
  {
  (counter = 8);
  }

if(newData ==true && buttonPressed==false)     //if there is new data gathered, as well as menu is not changed
{
newData=false; //reset newdata flag, ready to re-gather data
buttonPressed=false; //clear also buttonPressed flag to aknowledge menu change
lcd.clear(); //clear lcd each time new data has been gathered, and before writing new stuff to lcd

  switch (counter)  //choose correct menu depending on counter value set by pushbuttons
  {

    case 1:

      lcd.setCursor(0, 0);
      lcd.print("KM/h:");
      lcd.setCursor(0, 1);
      lcd.print(F("<-Clear  RPM->"));
      lcd.setCursor(5, 0);
      lcd.print(getSpeed());
      break;

    case 2:
      lcd.setCursor(0, 0);
      lcd.print("RPM:");
      lcd.setCursor(0, 1);
      lcd.print(F("<-Speed  Cool.->"));
      lcd.setCursor(5, 0);
      lcd.print(getRPM());
      break;

    case 3:

      lcd.setCursor(0, 0);
      lcd.print("`c");
      lcd.setCursor(0, 1);
      lcd.print(F("<-RPM  Intake->"));
      lcd.setCursor(5, 0);
      lcd.print(getTemp());
      break;

    case 4:
      lcd.setCursor(0, 0);
      lcd.print("`c");
      lcd.setCursor(0, 1);  //Intake
      lcd.print(F("<-Coolant  MAF->"));
      lcd.setCursor(5, 0);
      lcd.print(getIntake());
      break;

    case 5:

      lcd.setCursor(0, 0);
      lcd.print("g/sec:");
      lcd.setCursor(0, 1);  //MAF
      lcd.print(F("<-Intake Pedal->"));
      lcd.setCursor(5, 0);
      lcd.print(getMAF());
      break;

    case 6:

      lcd.setCursor(0, 0);
      lcd.print("%:");
      lcd.setCursor(0, 1);  //Pedal
      lcd.print(F("<-MAF  Stress->"));
      lcd.setCursor(5, 0);
      lcd.print(getThrottle());
      break;

    case 7:
      lcd.setCursor(0, 0);
      lcd.print("%:");
      lcd.setCursor(0, 1);  //Stress
      lcd.print(F("<-Pedal  RunT.->"));
      lcd.setCursor(5, 0);
      lcd.print(getStress());
      break;

    case 8:
      lcd.setCursor(0, 0);
      lcd.print("T:");
      lcd.setCursor(0, 1);  //Run Time
      lcd.print(F("<-Stress Clear->"));
      lcd.setCursor(5, 0);
      lcd.print(getRuntime());
      break;

   /* case 9:
      lcd.setCursor(0, 0);
      lcd.print("Clear codes?");
      lcd.setCursor(0, 1);  //clear
      lcd.print(F("<-RunT.  Speed->"));
      break;*/
  }
}

}




void ODB_init(void)
{
  //Wait for a little while before sending the reset command to the OBD-II-UART
  delay(2000);
  //Reset the OBD-II-UART
  Serial.print("ATZ\r");
  //Wait for a bit before starting to send commands after the reset.
  delay(2000);
  OBD_read();
  Serial.print("ATE0\r");
  OBD_read();
}

int getStress(void)                                                  //Stress
{
  Serial.print("0104\r");
  OBD_read();
  return strtol(&rxData[6], 0, 16) * 100 / 255;
}

int getTemp(void)                                                    //coolant temperature
{
  Serial.print("0105\r");
  OBD_read();
  return strtol(&rxData[6], 0, 16) - 40;
}

int getRPM(void)                                                     //RPM
{
  Serial.print("010C\r");
  OBD_read();
  return ((strtol(&rxData[6], 0, 16) * 256) + strtol(&rxData[9], 0, 16)) / 4;
}


int getSpeed(void)                                                  //speed km/h
{
  Serial.print("010D\r");
  OBD_read();
  return strtol(&rxData[6], 0, 16);
}

int getIntake(void)                                              //intake air temperature
{
  Serial.print("010F\r");
  OBD_read();
  return strtol(&rxData[6], 0, 16) - 40;
}

int getMAF(void)                                                  //MAF rate, g/sec
{
  Serial.print("0110\r");
  OBD_read();
  return ((strtol(&rxData[6], 0, 16) * 256) + strtol(&rxData[9], 0, 16)) / 100;
}


int getThrottle(void)                                            //throttle, %
{
  Serial.print("0111\r");
  OBD_read();
  return strtol(&rxData[6], 0, 16) * 100 / 255;
}

int getRuntime(void)                                             //Run time
{
  Serial.print("011F\r");
  OBD_read();
  return ((strtol(&rxData[6], 0, 16) * 256) + strtol(&rxData[9], 0, 16));
}


void OBD_read(void)
{
  char c;
  do {
    if (Serial.available() > 0)
    {
      c = Serial.read();
      //lcd.print(c);
      if ((c != '>') && (c != '\r') && (c != '\n')) //Keep these out of our buffer
      {
        rxData[rxIndex++] = c; //Add whatever we receive to the buffer
      }
    }
  } while (c != '>'); //The ELM327 ends its response with this char so when we get it we exit out.
  rxData[rxIndex++] = '\0';//Converts the array into a string
  rxIndex = 0;
  newData = true;    //set flag for newdata
}

so like this portion of lcd is “blank”:

      lcd.print(getSpeed());

problem was too frequent updating of that part, updated code coming up soonish....

  if (UpButtonState != LastUpButtonState)
  {
    buttonPressed=true;        //we got new press

That comment is wrong. You got a change. That could have been because the switch became pressed. But, it could have been because the switch became released, too.

Only if the switch IS pressed can you count a press.

updated code coming up soonish....

Please use
Tools + Auto Format,
first, to correct
your drunken indenting
style.

Code should NOT be hard to read.

Ok, here’s code that allows to check data, BUT, issue now is that if there was some data on screen, new data is placed on top of earlier one, so some action is needed to erase earlier data on screen, I succeeded in this in some earlier project I was doing without clearing whole screen (I did autoformat now, any better?)

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
char rxData[20];
char rxIndex = 0;
int const UpButton = 2;
int const DownButton = 3;
byte UpButtonState;
byte LastUpButtonState;
byte DownButtonState;
byte LastDownButtonState;
byte counter = 1;
bool newData = false;
bool buttonPressed = false;   //initially buttons hasn't been pressed

void setup()
{
  lcd.begin();
  lcd.backlight();
  Serial.begin(9600);
  ODB_init();
  pinMode (UpButton, INPUT);
  pinMode (DownButton, INPUT);
  digitalWrite(UpButton, HIGH);       // turn on pullup resistors
  digitalWrite(DownButton, HIGH);       // turn on pullup resistors
}

void loop()
{

  UpButtonState = digitalRead (UpButton);
  if (UpButtonState != LastUpButtonState)
  {
    lcd.clear();
    buttonPressed = true;
    if (UpButtonState == HIGH)
    {
      counter++;
    }
    LastUpButtonState = UpButtonState;
  }

  else
  {
    buttonPressed = false;
  }


  if (counter > 8)
  {
    (counter = 1);
  }

  DownButtonState = digitalRead (DownButton);
  if (DownButtonState != LastDownButtonState)
  {
    lcd.clear();
    buttonPressed = true;   //we got new press
    if (DownButtonState == HIGH)
    {
      counter--;
    }
    LastDownButtonState = DownButtonState;
  }

  else
  {
    buttonPressed = false;
  }
  if (counter == 0)
  {
    (counter = 8);
  }

  if (newData == true && buttonPressed == false) //if there is new data gathered, as well as menu is not changed
  {

    newData = false; //reset newdata flag, ready to re-gather data
    buttonPressed = false; //clear also buttonPressed flag to aknowledge menu change

    switch (counter)  //choose correct menu depending on counter value set by pushbuttons
    {

      case 1:
        lcd.setCursor(0, 0);
        lcd.print("KM/h:");
        lcd.setCursor(0, 1);
        lcd.print(F("<-Clear  RPM->"));
        lcd.setCursor(5, 0);
        lcd.print(getSpeed());
        break;

      case 2:
        lcd.setCursor(0, 0);
        lcd.print("RPM:");
        lcd.setCursor(0, 1);
        lcd.print(F("<-Speed  Cool.->"));
        lcd.setCursor(5, 0);
        lcd.print(getRPM());
        break;

      case 3:

        lcd.setCursor(0, 0);
        lcd.print("`c");
        lcd.setCursor(0, 1);
        lcd.print(F("<-RPM  Intake->"));
        lcd.setCursor(5, 0);
        lcd.print(getTemp());
        break;

      case 4:
        lcd.setCursor(0, 0);
        lcd.print("`c");
        lcd.setCursor(0, 1);  //Intake
        lcd.print(F("<-Coolant  MAF->"));
        lcd.setCursor(5, 0);
        lcd.print(getIntake());
        break;

      case 5:

        lcd.setCursor(0, 0);
        lcd.print("g/sec:");
        lcd.setCursor(0, 1);  //MAF
        lcd.print(F("<-Intake Pedal->"));
        lcd.setCursor(5, 0);
        lcd.print(getMAF());
        break;

      case 6:

        lcd.setCursor(0, 0);
        lcd.print("%:");
        lcd.setCursor(0, 1);  //Pedal
        lcd.print(F("<-MAF Stress->"));
        lcd.setCursor(5, 0);
        lcd.print(getThrottle());

        break;

      case 7:
        lcd.setCursor(0, 0);
        lcd.print("%:");
        lcd.setCursor(0, 1);  //Stress
        lcd.print(F("<-Pedal  RunT.->"));
        lcd.setCursor(5, 0);
        lcd.print(getStress());
        break;

      case 8:
        lcd.setCursor(0, 0);
        lcd.print("T:");
        lcd.setCursor(0, 1);  //Run Time
        lcd.print(F("<-Stress Clear->"));
        lcd.setCursor(5, 0);
        lcd.print(getRuntime());
        break;

        /* case 9:
           lcd.setCursor(0, 0);
           lcd.print("Clear codes?");
           lcd.setCursor(0, 1);  //clear
           lcd.print(F("<-RunT.  Speed->"));
           break;*/
    }
  }
}






void ODB_init(void)
{
  //Wait for a little while before sending the reset command to the OBD-II-UART
  delay(2000);
  //Reset the OBD-II-UART
  Serial.print("ATZ\r");
  //Wait for a bit before starting to send commands after the reset.
  delay(2000);
  OBD_read();
  Serial.print("ATE0\r");
  OBD_read();
}

int getStress(void)                                                  //Stress
{
  Serial.print("0104\r");
  OBD_read();
  return strtol(&rxData[6], 0, 16) * 100 / 255;
}

int getTemp(void)                                                    //coolant temperature
{
  Serial.print("0105\r");
  OBD_read();
  return strtol(&rxData[6], 0, 16) - 40;
}

int getRPM(void)                                                     //RPM
{
  Serial.print("010C\r");
  OBD_read();
  return ((strtol(&rxData[6], 0, 16) * 256) + strtol(&rxData[9], 0, 16)) / 4;
}


int getSpeed(void)                                                  //speed km/h
{
  Serial.print("010D\r");
  OBD_read();
  return strtol(&rxData[6], 0, 16);
}

int getIntake(void)                                              //intake air temperature
{
  Serial.print("010F\r");
  OBD_read();
  return strtol(&rxData[6], 0, 16) - 40;
}

int getMAF(void)                                                  //MAF rate, g/sec
{
  Serial.print("0110\r");
  OBD_read();
  return ((strtol(&rxData[6], 0, 16) * 256) + strtol(&rxData[9], 0, 16)) / 100;
}


int getThrottle(void)                                            //throttle, %
{
  Serial.print("0111\r");
  OBD_read();
  return strtol(&rxData[6], 0, 16) * 100 / 255;
}

int getRuntime(void)                                             //Run time
{
  Serial.print("011F\r");
  OBD_read();
  return ((strtol(&rxData[6], 0, 16) * 256) + strtol(&rxData[9], 0, 16));
}


void OBD_read(void)
{
  char c;
  do {
    if (Serial.available() > 0)
    {
      c = Serial.read();
      //lcd.print(c);
      if ((c != '>') && (c != '\r') && (c != '\n')) //Keep these out of our buffer
      {
        rxData[rxIndex++] = c; //Add whatever we receive to the buffer
      }
    }
  } while (c != '>'); //The ELM327 ends its response with this char so when we get it we exit out.
  rxData[rxIndex++] = '\0';//Converts the array into a string
  rxIndex = 0;
  newData = true;    //set flag for newdata
}

UpButtonState = digitalRead (UpButton);
if (UpButtonState != LastUpButtonState)
{
lcd.clear();
buttonPressed = true;

The last statement might mean something different to you than it does to me. The variable name implies, to me, that it contains a true or false indicating whether a button (switch, really. They are the same number of characters. Why is switch so hard for people to type?) is pressed, or not.

If the name implies the same to you, then setting the value when the switch becomes released is wrong. At that location in the code, you do not know what the change was. You get there when the switch becomes pressed AND when the switch becomes released.

If you won’t fix this, I don’t see a reason to try to help you.

I succeeded in this in some earlier project I was doing without clearing whole screen

Writing 32 spaces to the screen will clear it, too. That may, or may not, be faster than calling clear().