• Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

(solved)Serial communication+button menu on LCD

Status
Not open for further replies.

fezder

Well-Known Member
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:
https://www.sparkfun.com/products/9555
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!
C:
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;
}*/
 

djsfantasi

Member
It would be nice if the code was indented to show the code blocks. If you are using the Arduino IDE, go to Tools | Autoformat and it will be done for you.

Usually when performing a serial read, you check first to see if there are any characters available. The code might be hanging waiting for input that's not coming.
 

fezder

Well-Known Member
Hmm, I did autoformat. Hanging could bc cause, as when i keep button pressed, then I can change menu while serial read, but not if I press quickly.
 

fezder

Well-Known Member
here's original code I found which I'm trying to change to my purposes:
C:
//Define Serial Ports so I remember which is which
#include <SoftwareSerial.h>

//Create an instance of the new soft serial library to control the serial LCD
//Note, digital pin 3 of the Arduino should be connected to Rx of the serial LCD.

SoftwareSerial lcd(2,3);


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

void setup() 
{
   lcd.begin(9600);
   Serial.begin(9600);

  //Clear the old data from the LCD.
  lcd.write(254);
  lcd.write(1);
  lcd.print("Temp: ");
  lcd.write(254);
  lcd.write(128+64);
  lcd.print("RPM: ");
  
  ODB_init();
}

void loop() 
{


  //Delete any data that may be left over in the serial port.
  Serial.flush();
  //Move the serial cursor to the position where we want the RPM data.
  lcd.print(" "); 
  lcd.write(254);
  lcd.write(128 + 69);
  lcd.print(getRPM()); //Print the int returned from getRPM
  
  Serial.flush();
  
  lcd.print(" ");
  lcd.write(254);
  lcd.write(128 + 6);
  lcd.print(getTemp()); //Print the int returned from getTemp
  lcd.print(" C");
  
  delay(200);//wait .5 seconds and grab another reading
  

  
  
  
}

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 getRPM(void)
{
  //Query the OBD-II-UART for the Vehicle rpm
  Serial.flush();
  Serial.print("010C\r");
  OBD_read();

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

int getTemp(void)
{
  //Query the OBD-II-UART for the Engine Coolant Temp
  Serial.flush();
  Serial.print("0105\r");
  OBD_read();

  return strtol(&rxData[6],0,16)-40;
}


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"
}
 

fezder

Well-Known Member
The code might be hanging waiting for input that's not coming.
Is there way to make timeout or similar to prevent? I was thinking like this:
C:
if(Serial.available() > 0)
{
//do stuff
}
else
{
lcd.setCursor(0,0);
lcd.print("ERROR");
}
 

djsfantasi

Member
There is a way to check for a timeout during inout. Here is a code bit assuming a timeout of 500ms. Note that this is serial processing; other coding skeletons can be made to allow other processing to occur while waiting for input, but I decided to show this simple example first. Depending on what you want to do in process input, the "break" statement may not be necessary.
Code:
  // It is also possible to code so that other processing
  // can be performed while waiting for input.
  // This is a simple example of an input timeout.

  unsigned long timeout = 0;
  unsigned long waitfor = 500;

  timeout = millis() + waitfor;
  while (timeout > millis()) {
    if (Serial.available()) {
      // process input
      break;
    }
  }
  if (millis() > timeout) {
    // timeout occurred; error?
    // process timeout
  }
  // do more stuff
 

fezder

Well-Known Member
Thanks fantasi, that codes looks simple enought....I made scrolling menu system, so at least partially code works (not yet including your timeout):
C:
int const UpButton = 2;
int const DownButton = 3;
int UpButtonState;
int LastUpButtonState;
int DownButtonState;
int LastDownButtonState;
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

//Set up ring buffer
char rxData[20];
char rxIndex = 0;
String a;
volatile int counter;
int t = 1000;
void setup()
{

  lcd.begin();
  lcd.backlight();
  Serial.begin(9600);
  ODB_init();
}

void loop()
{




  delay(t);


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


  lcd.clear();  //RPM
  lcd.setCursor(0, 0);
  lcd.print("RPM:");
  lcd.setCursor(0, 1);
  lcd.print("<-Speed  Cool.->");
  lcd.setCursor(5, 0);
  lcd.print(getRPM());
  delay(t);


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


  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("`c");
  lcd.setCursor(0, 1);  //Intake
  lcd.print("<-Coolant  MAF->");
  lcd.setCursor(5, 0);
  lcd.print(getIntake());
  delay(t);


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


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


  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("%:");
  lcd.setCursor(0, 1);  //Stress
  lcd.print("<-Pedal  RunT.->");
  lcd.setCursor(5, 0);
  lcd.print(getStress());
  delay(t);


  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("T:");
  lcd.setCursor(0, 1);  //Run Time
  lcd.print("<-Stress Clear->");
  lcd.setCursor(5, 0);
  lcd.print(getRuntime());
  delay(t);


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

  delay(t);

}




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)
{
  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"
}
 

fezder

Well-Known Member
Hmm, buddy suggested that every bit of needed information is gathered inside loop in variables, and then menu controls what are shown, opinions?

other coding skeletons can be made to allow other processing to occur while waiting for input
^or then this was that what you meant by what I mean? That sounded confusing.....
 

fezder

Well-Known Member
I got it working! will post code later when I get couple small details done, but the idea is that in loop() is menu system that loops all the time, but hardware interrupt controls what menu is used! I read that interrupts can cause serial data to be missed, but since it's constantly re-called, it doesn't matter
 

fezder

Well-Known Member
Here we go: improvements welcome
C:
#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"
}
 

fezder

Well-Known Member
Better code coming u, got rid of interrupts so they don't interrupt Serial reading and cause hanging in case of missed ending characters. And I ended using OLED.
 

fezder

Well-Known Member
OK, now I got another problem: use of 7 segment, 4 digit display. I can't figure out how to combine shift registers with hardware serial, or then I again think too complicated....
following code works well even with potentiomere is i take val from:
C:
val=analogRead(A0);
wthout flicker at all. tested with even as low time interval between digits as 1ms, so drive circuit works as intended.
but, when I toss serial (hardware) in soup, things get messy; only last digit stays on brighter, and rest only flickers, should I use interrupt timer, and if, for segments or serial reading? And I read that interrupts confuse/cause dataloss on serial. I bother with menu later. BTW, I ended using non-interrupt code with booleans for menu system earler, but I decide to make 7 segment & bar graph display instead of OLED; I leave OLED for FFT for audio. I have feeling that this issue is due that because both reading as well as controlling of 7 segments are in loop(), but then again, they were also in LCD and OLED variations
C:
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;
unsigned long previousMillis = 0;  // will store last time LED was updated
const long interval = 1000;  // interval at which to blink (milliseconds)
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()  //G  F  E  D  C  B  A
{
  val = getThrottle();
  clearSegments();
  digitalWrite(ones, LOW);
  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);
  delayMicroseconds(t);

  clearSegments();
  digitalWrite(tens, LOW);
  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);
  delayMicroseconds(t);

  clearSegments();
  digitalWrite(hundreds, LOW);
  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);
  delayMicroseconds(t);

  clearSegments();
  digitalWrite(thousands, LOW);
  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);
  delayMicroseconds(t);
}

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();
}



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

}


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)
  {
  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
  }
}
 

fezder

Well-Known Member
Any ideas? Seems interrupts are no-go as even adding nointerrupts-interrupts in obd_read section causes code to hang.....is it posssible to make stuff during serial read?
Dr_Doggy , you got any ideas?
This code I have working with OLED:, but even this hangs/waits during serial read but won't matter that much as OLED doesn't wipe during waiting period, but shift registers do.....
C:
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);




char rxData[20];
char rxIndex = 0;
int UpSwitch = 2;
int DownSwitch = 3;

byte UpSwitchReading = 0;
byte DownSwitchReading = 0;

byte UpcurrentState = 0;
byte DowncurrentState = 0;

byte UppreviousState = 0;
byte  DownpreviousState = 0;
byte refresh = 2;
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
int Speed;
int Temp;
int RPM;
int mappedRPM;
int Intake;
int Throttle;
int Stress;

void setup()
{
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x32)
  display.setTextSize(1);
  display.setTextColor(WHITE);
  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)
  {
  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)
  {
  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;
  switch (counter)  //choose correct menu depending on counter value set by pushbuttons
  {

  case 1:
  Speed = getSpeed();
  display.setCursor(0, 0);
  display.print(F("KM/h:"));
  display.setCursor(0, 24);
  display.print(F("<-Clear  RPM->"));
  display.setCursor(32, 0);
  display.print(Speed);
  display.drawRect(0, 8, 127, 8, WHITE);
  display.fillRect(0, 8, Speed, 8, WHITE);
  display.display();
  display.clearDisplay();
  break;

  case 2:
  RPM = getRPM();
  mappedRPM = map(RPM, 0, 8000, 0, 100);
  display.setCursor(0, 0);
  display.print(F("RPM:"));
  display.setCursor(0, 24);
  display.print(F("<-Speed  Cool.->"));
  display.setCursor(24, 0);
  display.print(RPM);
  display.drawRect(0, 8, 100, 8, WHITE);
  display.fillRect(0, 8, mappedRPM, 8, WHITE);
  display.display();
  display.clearDisplay();
  break;

  case 3:
  Temp = getTemp();
  display.setCursor(0, 0);
  display.print(F("c:"));
  display.setCursor(0, 24);
  display.print(F("<-RPM  Intake->"));
  display.setCursor(16, 0);
  display.print(Temp);
  display.drawRect(0, 8, 127, 8, WHITE);
  display.fillRect(0, 8, Temp, 8, WHITE);
  display.display();
  display.clearDisplay();
  break;

  case 4:
  Intake = getIntake();
  display.setCursor(0, 0);
  display.print(F("c:"));
  display.setCursor(0, 24);  //Intake
  display.print(F("<-Coolant  MAF->"));
  display.setCursor(16, 0);
  display.print(Intake);
  display.drawRect(0, 8, 127, 8, WHITE);
  display.fillRect(0, 8, Intake, 8, WHITE);
  display.display();
  display.clearDisplay();
  break;

  case 5:
  display.setCursor(0, 0);
  display.print(F("g/sec:"));
  display.setCursor(0, 24);  //MAF
  display.print(F("<-Intake Pedal->"));
  display.setCursor(40, 0);
  display.print(getMAF());
  display.display();
  display.clearDisplay();
  break;

  case 6:
  Throttle = getThrottle();
  display.setCursor(0, 0);
  display.print(F("%:"));
  display.setCursor(0, 24);  //Pedal
  display.print(F("<-MAF Stress->"));
  display.setCursor(16, 0);
  display.print(Throttle);
  display.drawRect(0, 8, 100, 8, WHITE);
  display.fillRect(0, 8, Throttle, 8, WHITE);
  display.display();
  display.clearDisplay();
  break;

  case 7:
  Stress = getStress();
  display.setCursor(0, 0);
  display.print(F("%:"));
  display.setCursor(0, 24);  //Stress
  display.print(F("<-Pedal  RunT.->"));
  display.setCursor(16, 0);
  display.print(Stress);
  display.drawRect(0, 8, 100, 8, WHITE);
  display.fillRect(0, 8, Stress, 8, WHITE);
  display.display();
  display.clearDisplay();
  break;

  case 8:
  display.setCursor(0, 0);
  display.print(F("T:"));
  display.setCursor(0, 24);  //Run Time
  display.print(F("<-Stress Clear->"));
  display.setCursor(16, 0);
  display.print(getRuntime());
  display.display();
  display.clearDisplay();
  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();
  //display.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
}
 

fezder

Well-Known Member
Dr_Doggy , guess what? I think your matrix code earlier is the solution, I managed to get data on that! :p I'll post code in a few!
 

fezder

Well-Known Member
C:
#include "FontMap.h"
int dataPin = 2; // ic: 14, ser_in Define which pins will be used for the Shift Register control
int latchPin = 3; // ic:12 silkscreen numbers!
int clockPin = 4;

char rxData[20];
char rxIndex = 0;
unsigned char displayPointer = 0; // for interrupt use...
unsigned char buffer1[32]; // buffer for screen
unsigned char backbuffer[32]; // Spare screen for drawing on
unsigned char power[8] = {128, 64, 32, 16, 8, 4, 2, 1};
int val;

ISR(TIMER1_COMPA_vect) // timer compare interrupt service routine, called every now and then, outside of loop
{
  if (TIFR2) // Make sure its the timer interrupt.
  {
  setcolumn(displayPointer);  //column scanning
  setdata(buffer1[displayPointer]);
  PORTD = (1 << PORTD3);
  PORTD = (0 << PORTD3);

  //digitalWrite(latchPin ,HIGH);
  //digitalWrite(latchPin , LOW ); // STORECLOCK

  if (++displayPointer == 32)
  { displayPointer = 0;
  } // 32 LED row sections in total
  }
  TIFR2 = 0; // Clear timer 2 interrupt flag
}

void setcolumn(unsigned char col)  //loop that takes care of column scanning
{
  signed char pos;
  for (pos = 32; pos > -1; pos--)
  {
  if (col == pos)
  {
  PORTD = (1 << PORTD2);
  //digitalWrite(dataPin ,HIGH);
  }
  else
  {
  PORTD = (0 << PORTD2);
  //digitalWrite(dataPin ,LOW);
  } // PIN1 DATA pin
  //PORTD=(1<<PORTD4);
  //PORTD=(0<<PORTD4);

  digitalWrite(clockPin , HIGH);
  digitalWrite(clockPin , LOW);
  }
}



void setdata(unsigned char dat) {
  unsigned char pos;
  for (pos = 0; pos < 8; pos++)
  {
  if (dat & 128)
  {
  dat -= 128;
  PORTD = (1 << PORTD2);
  //digitalWrite(dataPin ,HIGH);
  }
  else
  {
  PORTD = (0 << PORTD2);
  }
  //digitalWrite(dataPin ,LOW);} // PIN1 DATA pin
  dat = dat * 2;
  digitalWrite(clockPin , HIGH);
  digitalWrite(clockPin , LOW);
  }
}
void clr() //clear
{
  int addr;
  for (addr = 0; addr < 32; addr++) // Empty display buffer
  backbuffer[addr] = 0;
}

void Blit() //transfers data between display buffer to screen buffer
{
  int addr = 0;
  noInterrupts(); // disable all interrupts during setup
  for (addr = 0; addr < 32; addr ++)
  {
  buffer1[addr] = backbuffer[addr]; // put all data from display buffer
  } // to screen buffer
  interrupts(); // enable all interrupts
}

void pixel(signed char x, signed char y, int cond)
{
  unsigned char pix, msk;
  if (x < 0 || y < 0) return; // outside drawing limits negative
  if (x > 31 || y > 7) return; // outside drawing limits positive
  pix = power[y];
  msk = backbuffer[x]; // get exsisting data


  if (cond == 2)
  pix ^= msk; // XOR data to screen
  if (cond == 1)
  {
  pix = ~pix;
  pix &= msk; // AND data to screen
  }
  if (cond == 0)
  pix |= msk; // OR data to screen
  backbuffer[x] = pix; // apply changes
}

void charput(unsigned char ch, signed char x, signed char y)
{
  signed char x1, y1;
  unsigned char disp;
  unsigned char disp2;
  for ( x1 = 0; x1 < 8; x1++) // eight rows
  {
  disp = font[x1 + (ch * 8)];
  for (y1 = 0; y1 < 8; y1++) // eight pixels
  {
  disp2 = disp & power[y1];
  if (disp2 > 0)
  {
  pixel(x + x1, y + y1, 0); // OR the pixel to the display buffer
  }
  }

  }
}

void strput(const char* ch, signed char x, signed char y)
{
  int addr;
  while (*ch )
  {
  charput(*ch++, x, y); // write a string to the display buffer
  x += 7;
  }
}



void setup() //setup runs once
{
  Serial.begin(9600);
  ODB_init();

  signed char cntr;
  noInterrupts(); // disable all interrupts during setup
  DDRD = DDRD | B11111100; //port registers used to set pin directions
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;
  OCR1A = 31; // compare match register 16MHz/256/2Hz -----------------------------------> delay time (lcd flicker/brightness)
  TCCR1B |= (1 << WGM12); // CTC mode, free-running, clear on match
  TCCR1B |= (0 << CS10); // 256 prescaler
  TCCR1B |= (0 << CS11); // 256 prescaler
  TCCR1B |= (1 << CS12); // 256 prescaler
  TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
  interrupts(); // enable all interrupts

} // End main

void loop() //just sitting here
{

  int val = getThrottle();

  clr();
  charput(((val / 1) % 10), 24, 0);
  charput(((val / 10) % 10), 16, 0);
  charput(((val / 100) % 10), 8, 0);
  charput(((val / 1000) % 10), 0, 0);
  Blit();
}

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;
}
 
Status
Not open for further replies.

Latest threads

EE World Online Articles

Loading
Top