Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mode 04 Clear Trouble Codes, CEL, MIL #8

Closed
crashmaxx opened this issue Dec 12, 2017 · 16 comments · Fixed by #10
Closed

Mode 04 Clear Trouble Codes, CEL, MIL #8

crashmaxx opened this issue Dec 12, 2017 · 16 comments · Fixed by #10

Comments

@crashmaxx
Copy link

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.

@iwanders
Copy link
Owner

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!

@crashmaxx
Copy link
Author

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!

@crashmaxx
Copy link
Author

OBD9141_Mode04.zip

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!

@iwanders
Copy link
Owner

iwanders commented Dec 15, 2017

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.

@iwanders
Copy link
Owner

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).

@crashmaxx
Copy link
Author

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.

@crashmaxx
Copy link
Author

Everything_Fails.zip

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.

@iwanders
Copy link
Owner

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.

@crashmaxx
Copy link
Author

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.

@produmann
Copy link

produmann commented Jan 28, 2018

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

@iwanders
Copy link
Owner

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.

@produmann
Copy link

I understood, but how can I prevent the 0xc7 byte from appearing? increase time OBD9141_REQUEST_ECHO_MS_PER_BYTE? on how much?

@iwanders
Copy link
Owner

iwanders commented Feb 2, 2018

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.

@produmann
Copy link

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

@iwanders
Copy link
Owner

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.

@produmann
Copy link

OBD9141_REQUEST_ECHO_MS_PER_BYTE I did not change, set by default, earned so

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants