Hello,
i'm building my first Arduino project which combines and OBD UART interface and and OLED display to get some live data from my car and print them on the display.
My setup is:
- Arduino UNO Rev 3 board
- Sparkfun OBD UART interface 9555 (wiring and code instructions here)
- An unbranded OLED 0.96" display compatible with the Adafruit SSD1306 library
The OBD UART is ELM327 compatible and basically is a serial interface which accepts AT commands and pass them to the car throuth the OBDII port. Then, the car sends back some replies, which are sent back to the serial port by the ELM327.
I've not yet connected the interface to the car, bu I'm already testing the setup by sending a simpler serial command ("ATRV"). After receiving this command, the ELM327 does not pass the request to the car but simply reply with the measurement of DC voltage that is provided to the OBD connector. This test allows me to setup the initial code in the comfort of my house.
It turns out that I've already spotted a problem that I'd like to solve before going to the car. The code kinda works, but results I receive are formatted in the wrong way, with missing characters here and there.
On one hand, if I plug the OBD interface to my pc and manually send the ATRV command a few times, I get consistent results like this.
atrv
12.2V
>atrv
12.2V
>atrv
12.2V
>
On the other hand, when I try to automate this inb my void loop {}, I get variable results. Sometimes I receive the correct output, but other times I receive only partial strings, missing one or more characters. Something similar to this:
>ATRV
12.2V
>ATRV
12.2V
>ATRV
12.2V
12.2VATRV
12ATRV
I have tested two alternative setups: first one with the OBD device wired to the 0 - 1 pins of my arduino (as per manufacturer instructions), and the result printed on the LCD display. The second one with the OBD device wired to pins 8-9 using AltSoftSerial, and the result printed to the hardware serial. I get similar output.
This is my code, mostly based on the example of the OBD manufacturer, related to the first setup.
#include <SPI.h>
#include <Wire.h>
// #include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
char rxData[20];
char rxIndex=0;
// the setup function runs once when you press reset or power the board
void setup() {
Serial.begin(9600);
// mySerial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
// Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
// Show initial display buffer contents on the screen --
// the library initializes this with an Adafruit splash screen.
display.display();
delay(2000); // Pause for 2 seconds
// Clear the buffer
display.clearDisplay();
display.display();
//Wait for a little while before sending the reset command to the OBD-II-UART
delay(1500);
//Reset the OBD-II-UART
Serial.println("ATZ");
//Wait for a bit before starting to send commands after the reset.
delay(2000);
//Delete any data that may be in the serial port before we begin.
Serial.flush();
}
void loop() {
//Delete any data that may be in the serial port before we begin.
Serial.flush();
delay(100);
//Query the OBD-II-UART for Battery Voltage
Serial.println("ATRV");
delay(100);
//Get the response from the OBD-II-UART board. We get two responses
//because the OBD-II-UART echoes the command that is sent.
//We want the data in the second response.
getResponse();
delay(100);
getResponse();
delay(100);
// Serial.println(rxData);
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.clearDisplay();
display.println(rxData);
display.display();
delay(1000);
}
//The getResponse function collects incoming data from the UART into the rxData buffer
// and only exits when a carriage return character is seen. Once the carriage return
// string is detected, the rxData buffer is null terminated (so we can treat it as a string)
// and the rxData index is reset to 0 so that the next string can be copied.
void getResponse(void){
char inChar=0;
//Keep reading characters until we get a carriage return
while(inChar != '\r'){
//If a character comes in on the serial port, we need to act on it.
if(Serial.available() > 0){
//Start by checking if we've received the end of message character ('\r').
if(Serial.peek() == '\r'){
//Clear the Serial buffer
inChar=Serial.read();
//Put the end of string character on our data string
rxData[rxIndex]='\0';
//Reset the buffer index so that the next character goes back at the beginning of the string.
rxIndex=0;
}
//If we didn't get the end of message character, just add the new character to the string.
else{
//Get the new character from the Serial port.
inChar = Serial.read();
//Add the new character to the string, and increment the index variable.
rxData[rxIndex++]=inChar;
}
}
}
}
As I mentioned, I results are meaningful, but not presented in a consistent way. And this will be a problem when I'll move to the car.
I don't know if this erratic behaviour depends on some error I made in the code... Is the serial comunication not properly handled?!? I've read that it is slow and asyncrhonous, but I thought the "Serial.available()>0 in the getResponse() function was taking care of this. I copied such function from the website of OBD interface manufactures, but it looks similar to the examples posted here in this forum.
Additinally, so I've added some delay(100); here and there in the main loop, but they don't help.