As an auto repair expert at autelfrance.com, I often encounter enthusiasts diving into DIY car diagnostics. Today, we’ll address a common issue faced when using an Arduino with an OBD-II UART module to read real-time engine data like RPM and speed. This project, while focused on data retrieval, touches upon the broader capabilities of OBD-II, including more advanced commands beyond basic sensor readings.
The user in our example is building a project with a Sparkfun OBDII UART module, Arduino Uno, and an LCD screen. They aim to display live RPM, speed, coolant temperature, and oxygen sensor readings. Let’s break down their setup and troubleshoot why they’re seeing static RPM and speed values even after starting the engine.
Their wiring is as follows, which is generally correct for UART communication:
- OBD TX -> Arduino RX
- OBD RX -> Arduino TX
- OBD GND -> Arduino GND
They are using the example code provided by Sparkfun as a base and have adapted it for their DFRobot_RGBLCD. Here’s their code snippet:
#include <softwareserial.h>
#include "DFRobot_RGBLCD.h"
const int colorR = 255;
const int colorG = 0;
const int colorB = 0;
DFRobot_RGBLCD lcd(16,2); //16 characters and 2 lines of show
char rxData[20];
char rxIndex=0;
int vehicleSpeed=0;
int vehicleRPM=0;
void setup() {
lcd.init();
lcd.setRGB(colorR, colorG, colorB);
Serial.begin(9600);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Speed: ");
lcd.setCursor(0,1);
lcd.print("RPM: ");
delay(1500);
Serial.println("ATZ"); // Reset OBD-II adapter
delay(2000);
Serial.flush();
}
void loop() {
Serial.println("010D"); // Request vehicle speed (PID 010D)
getResponse();
getResponse(); // Call twice to discard potential extra bytes
vehicleSpeed = strtol(&rxData[6],0,16);
lcd.setCursor(7,0);
lcd.print(vehicleSpeed);
lcd.print(" km/h");
delay(100);
Serial.flush();
Serial.println("010C"); // Request engine RPM (PID 010C)
getResponse();
getResponse(); // Call twice to discard potential extra bytes
vehicleRPM = ((strtol(&rxData[6],0,16)*256)+strtol(&rxData[9],0,16))/4;
lcd.setCursor(5,1);
lcd.print(vehicleRPM);
delay(100);
}
void getResponse(void){
char inChar=0;
while(inChar != 'r'){
if(Serial.available() > 0){
if(Serial.peek() == 'r'){
inChar=Serial.read();
rxData[rxIndex]='';
rxIndex=0;
}
else{
inChar = Serial.read();
rxData[rxIndex++]=inChar;
}
}
}
}
The core issue is that the Arduino seems to be reading and displaying RPM and speed values only once, likely upon initial connection when the ignition is in “battery mode” (accessory mode), but these values do not update after the engine is started. They observe RPM as 832 and speed as 13 km/h initially, which remain static.
To troubleshoot, the user wisely used a USB FTDI (TTL) adapter and a terminal program (Tera Term) to directly communicate with the OBD-II module, bypassing the Arduino. This is a crucial step in isolating the problem. They connected:
- OBD TX -> TTL RX
- OBD RX -> TTL TX
- OBD GND -> TTL GND
And used these commands in Tera Term, as suggested by the OBD-II UART module manual:
ATZ -- Reset the OBD-II adapter
ATRV -- Read battery voltage
ATSP0 -- Auto-detect OBD-II protocol
010C -- Request RPM (PID 010C)
010D -- Request Speed (PID 010D)
The results from Tera Term were successful. They received voltage readings and live RPM and speed data, confirming that the OBD-II module itself is functioning correctly and communicating with the car’s ECU. This is a significant finding, indicating the problem likely lies within the Arduino setup or code.
Possible Causes and Solutions
-
Serial Communication Issues: While the wiring seems correct, double-check the baud rate. The Arduino code uses
Serial.begin(9600)
. Ensure the OBD-II UART module is also set to 9600 baud or configured for auto-baud detection if it supports it. Mismatched baud rates are a common cause of communication problems. -
Code Logic in
loop()
: The code sends “010D” and “010C” commands and callsgetResponse()
twice after each. While callinggetResponse()
twice might be intended to clear extra bytes, it might be consuming the correct response as well. Try callinggetResponse()
only once after each command to see if this resolves the issue. -
Serial Buffer Overflow/Data Handling: The
rxData
buffer is 20 bytes, which should be sufficient for typical OBD-II responses. However, if there are issues in thegetResponse()
function or if the responses are longer than expected, it could lead to data corruption or incorrect parsing. Review thegetResponse()
function carefully. It waits for a carriage return (r
) to terminate the response. This is standard OBD-II behavior. -
Serial.flush()
Usage:Serial.flush()
in Arduino waits for the transmission of outgoing serial data to complete. In this code, it’s used after sending commands and after displaying values. While not directly harmful, it might be adding unnecessary delays. Try removingSerial.flush()
calls within theloop()
to see if it makes a difference.Serial.flush()
in thesetup()
is generally good practice after sending theATZ
command to ensure the reset command is fully transmitted. -
Power Supply: Although less likely since the initial readings are obtained, ensure the Arduino and OBD-II module are receiving stable and sufficient power, especially when the engine is running and electrical loads in the car change.
OBD-II Commands and Beyond: Engine Start Considerations
While this project focuses on reading data, it’s worth briefly mentioning the command capabilities of OBD-II. OBD-II standards primarily focus on diagnostic data and emissions-related information. Standard OBD-II commands (PIDs like 010C and 010D) are designed for reading sensor data, not for controlling vehicle functions like starting the engine.
The idea of an “Obdii Start Engine Command” is more related to advanced or manufacturer-specific commands, often outside the scope of standard OBD-II. While some advanced diagnostic tools or aftermarket devices might utilize proprietary commands to control certain vehicle functions, including remote start or engine manipulation, these are not part of the standard OBD-II protocol and are highly vehicle-specific and potentially risky to implement without deep understanding.
For typical DIY projects with Arduino and basic OBD-II UART modules, focusing on reading and displaying diagnostic data is the primary and safest application. Attempting to send commands to control critical engine functions like starting is generally not recommended and could lead to unintended consequences or even damage to the vehicle if not implemented correctly and with proper safety measures.
Next Steps for Troubleshooting
-
Simplify the Code: For testing, simplify the Arduino code to only request RPM (010C) and display it. Remove the speed request and LCD code temporarily to isolate the RPM reading.
-
Debug Serial Output: Add
Serial.println(rxData);
inside theloop()
after callinggetResponse()
. This will print the raw response received from the OBD-II module to the Arduino serial monitor. Examine this output to see exactly what data is being received and if it’s changing after the engine starts. -
Check Protocol Detection: The code uses
ATSP0
for auto protocol detection. While usually reliable, in some cases, manually setting the protocol usingATSP
followed by a specific protocol number (e.g., ATSP6 for ISO 15765-4 CAN) might be necessary. Consult the OBD-II UART module documentation and your vehicle’s specifications to determine the correct protocol if auto-detection is suspected to be an issue. -
Verify Wiring Again: Double, even triple-check the wiring connections between the Arduino, OBD-II UART module, and the OBD-II port in your car. A loose or incorrect connection can cause intermittent or no communication.
By systematically checking these points and simplifying the setup for initial debugging, you should be able to pinpoint why the RPM and speed values are not updating after engine start. Remember to focus on reading and understanding the serial data being transmitted to ensure correct parsing and display of the OBD-II information.
[