Closed crashmaxx closed 6 years ago
Hi Crashmaxx,
I couldn't find anything in the current code that will put the ECU in Mode 04 to clear trouble codes, the CEL, and/or MIL.
You are correct, there is no code that does this.
Assuming it doesn't exist, it should be a simple feature to add as far as I understand it. A function called OBD9141::ClearCodes or similar would be created and it would just send the hex code for Mode 04 to the ECU.
Yeah I think so! I considered implementing this when I worked on it. But since I had no way to test this I chose not to do a blind implementation. Having non working code is worse than not having that code, as it creates false expectations.
The implementation should indeed be something along the lines of:
bool OBD9141::clearTroubleCodes(){
uint8_t message[4] = {0x68, 0x6A, 0xF1, 0x04};
// 0x04 without PID value should clear the trouble codes / malfunction indicator lamp.
bool res = this->request(&message, 4, /* return probably same length? */ 4);
return res;
}
Wikipedia says there are no data bytes returned. The return format is a bit of a mystery unfortunately, I expect the response to be the same length as the request if the data length is zero. That would continue the pattern set for the other PID's (see the getPID method).
If you have a vehicle where you can safely trigger a trouble code, feel free to test the above snippet. If the malfunction light goes off but the method returns false, increase the return length from 4
into a 5
and check the result again, it wouldn't surprise me if the ECU returns something different for this case. But with some experimentation you should be able to get this to work even without a logic analyzer.
Let us know how this works out!
Wow! Thank you for the quick response!
I have a car with an engine swap, so it is missing some sensors. It will get trouble codes as soon as it starts and the CEL will come on in a few minutes.
I will test the code tomorrow and get back to you. I hope I can verify it well enough you can add it to the library. Thanks again!
Attached is a zip with my modified OBD9141.h, OBD9141.cpp, the sketch I tested with (Clear_Codes2.ino), and the serial console data I recorded (ClearCodesData.text).
It worked great! The CEL went away when I started the car. The function seems to return 0, but maybe I am collecting that data wrong.
I wanted to have it only clear the codes when the car is on, but not running, and had driven at least 10 miles since the codes were last cleared. I couldn't get PID 31 (Distance since CEL cleared) to read though.
I'd be happy to do more testing if you want to try anything different. Thanks a lot!
Your zipfile is awesome! Very thoughtful of you to include the serial console data and code, that's great. I didn't see the file Clear_Codes2.ino
in the zipfile, but there was a Clear_Codes.ino
, so I'm basing my reasoning on that.
It worked great! The CEL went away when I started the car. The function seems to return 0, but maybe I am collecting that data wrong.
Superb! Yes, the printout isn't entirely what you wanted I think, but because you included both the serial log and the code we can deduce exactly what happened, the part of the code where the new method is called:
if (RPM == 0){
delay(5000);
res = obd.clearTroubleCodes();
if (res){
SerialUSB.print("Resetting CEL: ");
SerialUSB.println(obd.readUint8());
for (int t = 0; t < 10; t++) {
digitalWrite(DS3, LOW); // turn the LED on by making the voltage LOW
delay(100);
digitalWrite(DS3, HIGH); // turn the LED off by making the voltage HIGH
delay(100);
}
}
}
And the output:
Looping
init_success:1
Result 0x0C (RPM): 0
Result 0x0D (Speed MPH): 0
Result 0x05 (ECT C): 9
Result 0x05 (IAT C): -40
Resetting CEL: 0
The thing is, the line Resetting CEL
is present, the print for this line is inside of the if (res)
clause, so this means that the obd.clearTroubleCodes();
call returned true, otherwise we wouldn't have seen anything in the serial console! So that means the clearTroubleCodes is working!
That you couldn't receive 0x31
, Distance traveled since codes cleared
could have various reasons, most likely that the ECU doesn't support it, the implementation should be something along the lines of:
res = obd.getCurrentPID(0x31, 2);
if (res){
SerialUSB.print("Result 0x31 (distance since CEL km): ");
uint16_t distance = obd.readUint16();
SerialUSB.println(distance);
}
Another plausible reason is that this value will be zero since your test setup isn't really making a lot of distance if you are swapping the engine ;) Take a look at PID 0x4D
and 0x4E
, they may be more insightful if the ECU supports them.
There are ways to check if your ECU supports a particular request. Wikipedia has a paragraph on how to interpret the bitmask returned by mode 1 PID 0x00
. This bitmask tells you which PID's are supported. If you look at the big table, at pid 0x20
, you see that that is the bitmask to obtain whether the PID's 21-40 are supported. So, you can use the bitmask from 0x00
to check whether retrieving the bitmask at PID 0x20
is supported, if it is you can retrieve that and check if PID 0x31
is supported.
The code to do this something along the lines of:
res = obd.getCurrentPID(0x00, 4); // This is the block 0x01-0x20
// res = obd.getCurrentPID(0x20, 4); This is the block 0x21-0x40
if (res){
SerialUSB.println("Supported PIDs in range 0x21-0x40");
SerialUSB.print("a: 0x"); Serial.println(obd.readUint8(0), HEX);
SerialUSB.print("b: 0x"); Serial.println(obd.readUint8(1), HEX);
SerialUSB.print("c: 0x"); Serial.println(obd.readUint8(2), HEX);
SerialUSB.print("d: 0x"); Serial.println(obd.readUint8(3), HEX);
}
Though, I don't really know if the a
byte or the d
byte is the highest, I don't remember the endianness of the system. But if 0x20
returns all zero's you know for sure that the ECU doesn't support it.
Also, small tip; if you want to to run with #define OBD9141_DEBUG
, or if you want to change a library with any defines, you have to set that before you include the library:
#define OBD9141_DEBUG
#include <OBD9141.h>
#include <Arduino.h>
// Instead of having #define OBD9141_DEBUG here.
This is because the flags / defines need to be set before the header file is included, otherwise setting it will have no effect. Alternatively (this is what I used during development), you can pass it as a flag to your compiler (add -DOBD9141_DEBUG
to the options), but if you use the Arduino IDE I'm not sure how or if that is possible.
I've added the method to the library! Could you download a fresh version and check whether it also works with the one from the library? (Just to be 100% sure).
That's awesome! Thanks for all the help! I'll download a fresh copy of the library and try it out tomorrow. I'll experiment with requesting the list of supported PIDs too.
I downloaded a new copy of the library. None of my sketches work today. I've tried checking the OBD2 port, checking my wiring to the M2, and that the M2 still works. Everything seems good, but nothing will initialize. They all output the same as Console_Log.txt.
I feel like I must have a problem with my setup, because the library looks fine. If you wouldn't mind looking at my sketches and letting me know if you see anything that looks wrong.
Or if you could give me a really basic debugging sketch that just initializes and outputs as much as possible. Thanks for all the help so far. I want to help add more features if I can get everything to work again.
Hey,
I feel like I must have a problem with my setup, because the library looks fine. If you wouldn't mind looking at my sketches and letting me know if you see anything that looks wrong.
Yeah, I'm afraid it must be something with the hardware setup, the sketches look fine. Check wiring / pullup resistor / connectors, check voltage on the K-line (should be around battery voltage when no communication is happening). As you said the library is practically unchanged, the only difference is the commit with the new method, but that certainly won't cause the init to fail.
if you could give me a really basic debugging sketch that just initializes and outputs as much as possible.
This is tricky, because I have no way to check if the debugging sketch I make works for your hardware. You should try to use the most simple example you have that used to work (probably Clean_Ex
). You could add the debug define to get some more output, then you would change the following at the top of the sketch
#include "Arduino.h"
#include "OBD9141.h"
into:
#include "Arduino.h"
#define OBD9141_DEBUG
#include "OBD9141.h"
But this is unlikely to solve the initialization problems you are having. I'm almost certain it's a wiring or hardware problem if it doesn't work with a simple sketch that used to work a few days ago.
Thanks for the feedback. I'll do more diagnostics tomorrow and get back to you.
I just wanted to make sure I didn't have a coding error I was copying over and over into all the sketches.
after analyzing the method, you are wrong
// No data is returned to this request, we expect the request itself
you can return a report on the successful erasure of error codes, here is my log:
C7 0 0 0 0 0 0 0 0 0 - return response vehicle ecu in case of unsuccessful reset
C7 48 6B 1 44 F8 0 0 0 0 - return response vehicle ecu in case of a successful reset
so I used the last returned byte 0хF8 and returned the marker for a successfully executed reset. Can be different car manufacturers in different ways will give a response to the 0x04 mode, or maybe I'm mistaken
With "No data is returned" I don't mean any bytes are returned, I just mean that the data part of the response will be zero, as per wikipedia.
you can return a report on the successful erasure of error codes, here is my log:
C7 0 0 0 0 0 0 0 0 0 - return response vehicle ecu in case of unsuccessful reset C7 48 6B 1 44 F8 0 0 0 0 - return response vehicle ecu in case of a successful reset
Hmm... this 0xC7
you have at the start is suspicious... that shouldn't be there, I'll explain. The mode 0x04
method is here. The actual request is four bytes:
0x68, 0x6A, 0xF1, 0x04
This 4 byte long request gets passed to the request
method where the checksum byte is added. If we calculate the checksum byte manually for the four request bytes (hex((0x68 + 0x6a + 0xf1 + 0x04) % 0x100) = 0xc7
, in Python for example, or use the checksum method in the class), so we get 0xc7
as a checksum byte.
So the actual bytes transmitted during the request will be:
0x68, 0x6A, 0xF1, 0x04, 0xC7
Since the tx and rx are both connected to the Kline transceiver chip, we will see anything we transmit as an echo on the rx
line. The write method tries to read this 'echo' from the data line such that it isn't read as part of the response from the ECU, but it appears that the value of OBD9141_WAIT_FOR_REQUEST_ANSWER_TIMEOUT
is too low for the ECU in your car. Increasing the echo values here may prevent that 0xC7
from appearing in the response.
In your results we see:
C7 48 6B 1 44 F8 0 0 0 0 - return response vehicle ecu in case of a successful reset
If we are to disregard the 0xC7
echo from the request... we actually have:
48 6B 1 44 F8
Which is exactly what the code and I would expect, the response is the standard 4 byte to the request and the checksum to the bytes 0x48, 0x6b, 0x01, 0x44
is the 0xF8
byte, so if you can prevent the 0xc7
from appearing you would probably see that the method would return true
.
I understood, but how can I prevent the 0xc7 byte from appearing? increase time OBD9141_REQUEST_ECHO_MS_PER_BYTE? on how much?
Change it from 3
to 5
or 10
... even setting it to 100
should work, since we only wait for echo's if we expect it. Just change it a bit and see what happens... the 3
is only tuned the car I used to write this library.
hello, it works, here's the log:
Looping
Before magic 5 baud.
Before setting port.
After setting port.
First read is: 85
read v1: 8
read v2: 8
v1: 8
v2: 8
w: 247
read 0xCC?: CC
init_success:1
w: 104
w: 106
w: 241
w: 3
w: 0
w: 198
Trying to get x bytes: 12
72 107 1 67 1 19 0 0 0 0 11 0
res 1
Read 1 codes:P0113
res = obd.clearTroubleCodes();
w: 104
w: 106
w: 241
w: 4
w: 199
Trying to get x bytes: 5
72 107 1 68 248
clearTroubleCodesclearTroubleCodes res =1
w: 104
w: 106
w: 241
w: 3
w: 0
w: 198
Trying to get x bytes: 12
72 107 1 67 0 0 0 0 0 0 247 0
res 1
Read 1 codes:P0000
Awesome, that's great news, what value did you end up using for OBD9141_REQUEST_ECHO_MS_PER_BYTE
? It may be useful for others to know what value you used, and we could even change it in the library since in theory its value doesn't matter as we only wait while we expect bytes. For the variable length request we're working on in the other issue it does make a difference though.
OBD9141_REQUEST_ECHO_MS_PER_BYTE I did not change, set by default, earned so
I couldn't find anything in the current code that will put the ECU in Mode 04 to clear trouble codes, the CEL, and/or MIL. If this feature already exists, it needs to be documented.
Assuming it doesn't exist, it should be a simple feature to add as far as I understand it. A function called OBD9141::ClearCodes or similar would be created and it would just send the hex code for Mode 04 to the ECU.