Serial communication+button menu on LCD

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

What about Reply #1 ?

...R

okay Paul, I'll try to modify code so switch presses are only taken account on LOW. That part worked so didn't bother modifying it (counting worked ok)
started re-doing that switch part, but at least for now that else blews things up since each time throught loop counter goes up....I don't get it how such simple action can be so hard!

const int SwitchPin = 2;     // the number of the pushSwitch pin
int count;               // Count the Switch presses
int SwitchState;         // variable for reading the pushSwitch status
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
boolean Switchpressed = false;

void setup() 
{
  lcd.begin();
  lcd.backlight();
  pinMode(SwitchPin, INPUT);
  digitalWrite(SwitchPin, HIGH);
  Serial.begin(9600);
}

void loop() {
  SwitchState = digitalRead(SwitchPin);

  if (SwitchState == LOW && Switchpressed == false)
  {
    SwitchState = HIGH;
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(count);
    Switchpressed = true;
    count++;
    
    if (count >= 8) 
    {
      count = 0;
    }
  }
  else
  {
    Switchpressed = false;
  }
}

Robin, I didn't mean to ignore that reply, in fact I forgot to reply it.....but, i did try with this manner but didn't work:

if(millis()/1000 % 2)            //every other second right?
{
//update lcd, write and stuff
}

Well, was hard, off to adapting this to code:

int UpSwitch = 2;
int DownSwitch = 3;

int UpSwitchReading = 0;
int DownSwitchReading = 0;

int UpcurrentState = 0;
int DowncurrentState = 0;

int UppreviousState = 0;
int DownpreviousState = 0;

byte counter = 0;

void setup() {
  pinMode(UpSwitch, INPUT);
  pinMode(DownSwitch, INPUT);

  digitalWrite(UpSwitch, HIGH);
  digitalWrite(DownSwitch, HIGH);

  Serial.begin(9600);
}

void loop()
{
  UpSwitchReading = digitalRead(UpSwitch);
  if (UpSwitchReading == LOW)
  {
    UpcurrentState = 1;
  }
  else
  {
    UpcurrentState = 0;
  }
  if (UpcurrentState !=  UppreviousState)
  {
    if (UpcurrentState == 1)
    {
      counter++;
      if (counter > 8)
      {
        counter = 1;
      }
      Serial.println(counter);
    }
  }
  UppreviousState = UpcurrentState;




  DownSwitchReading = digitalRead(DownSwitch);
  if (DownSwitchReading == LOW)
  {
    DowncurrentState = 1;
  }
  else
  {
    DowncurrentState = 0;
  }
  if (DowncurrentState !=  DownpreviousState)
  {
    if (DowncurrentState == 1)
    {
      counter--;
      if (counter == 0)
      {
        counter = 8;
      }
      Serial.println(counter);
    }
  }
  DownpreviousState = DowncurrentState;
}

This I came up with:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
char rxData[20];
char rxIndex = 0;
int UpSwitch = 2;
int DownSwitch = 3;

int UpSwitchReading = 0;
int DownSwitchReading = 0;

int UpcurrentState = 0;
int DowncurrentState = 0;

int UppreviousState = 0;
int DownpreviousState = 0;

byte counter = 1;
bool newData = false;
bool UpSwitchPressed = false;   //initially Switches hasn't been pressed
bool DownSwitchPressed = false;   //initially Switches hasn't been pressed
bool SwitchPressed = false;   //initially Switches hasn't been pressed



void setup()
{
  lcd.begin();
  lcd.backlight();
  Serial.begin(9600);
  ODB_init();
  pinMode(UpSwitch, INPUT);
  pinMode(DownSwitch, INPUT);
  digitalWrite(UpSwitch, HIGH);
  digitalWrite(DownSwitch, HIGH);
}




void loop()
{
  UpSwitchReading = digitalRead(UpSwitch);
  if (UpSwitchReading == LOW)
  {
    UpcurrentState = 1;
  }
  else
  {
    UpcurrentState = 0;
    UpSwitchPressed == false;
  }
  if (UpcurrentState !=  UppreviousState)
  {
    if (UpcurrentState == 1)
    {
      lcd.clear();
      counter++;
      UpSwitchPressed == true;

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




  DownSwitchReading = digitalRead(DownSwitch);
  if (DownSwitchReading == LOW)
  {
    DowncurrentState = 1;
  }
  else
  {
    DowncurrentState = 0;
    DownSwitchPressed == false;
  }
  if (DowncurrentState !=  DownpreviousState)
  {
    if (DowncurrentState == 1)
    {
      lcd.clear();
      counter--;
      DownSwitchPressed == true;
      if (counter == 0)
      {
        counter = 8;
      }
    }
  }
  DownpreviousState = DowncurrentState;






  if (newData == true  && (UpSwitchPressed == false || DownSwitchPressed == false)) //if there is new data gathered, and neither or either-or switches pressed?
  {
    newData = false; //reset newdata flag, ready to re-gather data
    //SwitchPressed = false; //clear also SwitchPressed flag to aknowledge menu change

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

      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
}
    UpSwitchPressed == false;
      UpSwitchPressed == true;

Bzzzt.

I give up. That code is WAY too complicated, with way too many global variables involved in determining if a counter should be increased or decreased.

Calling OBD_read() should NOT result in newData being set to true UNCONDITIONALLY.

There is still nothing to allow the rest of the program to know how much data OBD_read() put in rxData.

The type of rxData implies that the data is ASCII. The NULL terminator implies that the data is ASCII. The way that the data is used implies that the data is binary.

So, what kind of data is the function getting? Why doesn't it return the number of bytes read?

How would you do this? I'm open book for suggestions, I really can't tell what's right and what's wrong in code as I don't have any sort of education apart from google.
Does this link help at all?

I can check what sort of data board sends back with analyzer if needed.

I would get the OBD thing off the hardware serial port, so I could use the hardware serial port for debugging.

I would look at the state change detection example, to see how to determine that a switch has become pressed. I would increment or decrement counter when that happens. Independently, I would USE counter to determine what to do.

I would have OBD_read() return the number of bytes it read.

I would not bother with newData, yet. OBD_read() got n bytes or it got 0 bytes. Neither indicates that the data is complete.

I might bother with newData if I knew how many bytes of data I was supposed to get for each command.

Hmm, I thought that whenever ending character, like > in this case comes to serial, is the mark data has been received, but indeed it doesn't tell if there is all data needed. And in this case same command is repeated new data is soon sent again and any faulty readings would be "ignored". I can't take my laptop to car as it's quite cold, but is it possible to use lcd screen to show bytes received? So far I've only used teraterm and similar to see what data is coming, but I did use lcd when debugging SIM800L how many characters came through...

fezder:
Robin, I didn't mean to ignore that reply, in fact I forgot to reply it.....but, i did try with this manner but didn't work:

if(millis()/1000 % 2)            //every other second right?

{
//update lcd, write and stuff
}

The STICKY Several Things at a Time illustrates the use of mills().

...R
PS I can live without a reply - but I had hoped you might consider the issue.

Thanks robin, I've read some of your stickies and well, guite chore you've seen writing those, nice work!
Learning proper refreshing of LCD can't hurt, even thought i'll most likely place that OLED screen and that fortunately doesn't have that "ghosting" (wrong word perhaps) issue what is here now, and that would allow to make graph/bar display as well. Made FFT on that also which behaved quite well...used teensy and audio shield however.

and any faulty readings would be "ignored".

That assumes that most readings are valid. I've yet to see proof of that.

PaulS:
That assumes that most readings are valid. I've yet to see proof of that.

well at least so far I haven't see any suspicious readings, apart from situation when I reversed TX/RX or forgot GND wire....how could that be checked whether most readings would be valid?

Sorry to bother, but I ended using, well at least testing 7-segment display and bargraph instead of OLED/lcd, as 7 segment/bargraph gives quite same result and uses less memory.
Now then, the issue is quite similar to when LCD's character was dim; when 7 segments are lit, they light up way too fast. I have feeling this problem is due that both reading as well as 7 segments control are in same loop, serial stuff takes longer than shift registers control.
I thought of using interrupts, but hardware serial used interrupts so that is no-go....
I have 4-digit 7-segment wires as: shift register controls anodes and darlington array uln2804 grounds cathodes, quite traditional way. And this sketch is basic skeleteon what I use for other stuff, and it works in any other environment/data else than serial , like if I read analog value from potentiometer, or millis() counter, no annoying flicker/ghosting....also I have 8x32 matrix that scrolls RTC time while updating smoothly, so this should be doable.

int dataPin = 2;  //Define which pins will be used for the Shift Register control
int latchPin = 3;
int clockPin = 4;
int ones = 5;
int tens = 6;
int hundreds = 7;
int thousands = 8;

int val = 0;
byte m = 1;
int t = 1000;
char rxData[20];
char rxIndex = 0;
boolean newData = false;


void setup()
{
  Serial.begin(9600);
  ODB_init();
  pinMode(dataPin, OUTPUT);  //Configure each IO Pin
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(ones, OUTPUT);
  pinMode(tens, OUTPUT);
  pinMode(hundreds, OUTPUT);
  pinMode(thousands, OUTPUT);
}



void loop()
{
  val = getThrottle();                       //just one data to be measured for now

  clearSegments();                      //clear all before rewrite
  digitalWrite(latchPin, LOW);          //Pull latch LOW to start sending data
  pickNumber((val / m / 1) % 10);       //ones
  digitalWrite(latchPin, HIGH);         //Pull latch HIGH to stop sending data
  digitalWrite(ones, HIGH);             //show number
  delayMicroseconds(t);                 //small delay to show number

  clearSegments();                      //clear all before rewrite
  digitalWrite(latchPin, LOW);          //Pull latch LOW to start sending data
  pickNumber((val / m / 10) % 10);      //tens
  digitalWrite(latchPin, HIGH);         //Pull latch HIGH to stop sending data
  digitalWrite(tens, HIGH);             //show number
  delayMicroseconds(t);

  clearSegments();                      //clear all before rewrite
  digitalWrite(latchPin, LOW);          //Pull latch LOW to start sending data
  pickNumber((val / m / 100) % 10);     //hundreds
  digitalWrite(latchPin, HIGH);         //Pull latch HIGH to stop sending data
  digitalWrite(hundreds, HIGH);         //show number
  delayMicroseconds(t);                 //small delay to show number

  clearSegments();                      //clear all before rewrite
  digitalWrite(latchPin, LOW);          //Pull latch LOW to start sending data
  pickNumber((val / m / 1000) % 10);    //thousands
  digitalWrite(latchPin, HIGH);         //Pull latch HIGH to stop sending data
  digitalWrite(thousands, HIGH);        //show number
  delayMicroseconds(t);                 //small delay to show number
}


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



void OBD_read(void)
{
  char c;
  do {
    if (Serial.available() > 0)
    {
      c = Serial.read();
      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

}


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


void clearSegments()
{
  digitalWrite(ones, LOW);
  digitalWrite(tens, LOW);
  digitalWrite(hundreds, LOW);
  digitalWrite(thousands, LOW);
}


void pickNumber( int count)  //pick numbers from array, at start of whole code
{
  switch (count)  //could be if/else too, but this has worked thus far just fine
  {
    case 1: shiftOut(dataPin, clockPin, MSBFIRST, B00001100); break;  //1
    case 2: shiftOut(dataPin, clockPin, MSBFIRST, B10110110); break;  //2
    case 3: shiftOut(dataPin, clockPin, MSBFIRST, B10011110); break;  //3
    case 4: shiftOut(dataPin, clockPin, MSBFIRST, B11001100); break;  //4
    case 5: shiftOut(dataPin, clockPin, MSBFIRST, B11011010); break;  //5
    case 6: shiftOut(dataPin, clockPin, MSBFIRST, B11111010); break;  //6
    case 7: shiftOut(dataPin, clockPin, MSBFIRST, B00001110); break;  //7
    case 8: shiftOut(dataPin, clockPin, MSBFIRST, B11111110); break;  //8
    case 9: shiftOut(dataPin, clockPin, MSBFIRST, B11011110); break;  //9
    //case 10: shiftOut(dataPin, clockPin, MSBFIRST, B01111110); break;  //9
    default: shiftOut(dataPin, clockPin, MSBFIRST, B01111110); break;  //0
  }
}

I got it working, tried with my matrix screen code and it worked with it. :slight_smile:

fezder:
Now then, the issue is quite similar to when LCD's character was dim; when 7 segments are lit, they light up way too fast. I have feeling this problem is due that both reading as well as 7 segments control are in same loop, serial stuff takes longer than shift registers control.
I thought of using interrupts, but hardware serial used interrupts so that is no-go...

I am not going to offer specific advice about 7-segment of LCD displays because I don't have enough experience of them.

However there is no reason why you can't use an LCD or 7-segment displays if the code is organized properly.

I can't imagine that interrupts are necessary but the fact that hardware serial uses an interrupt is completely irrelevant.

...R