AutoPi with VW ID.3 OBD Libary WIP

Here there. I am in the process of configuring Autopi for the VW ID.3.

The Following Information are “Work in Progress”

  • Autopi has “autodedected” the following
    • ISO 15765-4 (Can 29/500)
  • Autopi Community Libary
    • to be done
  • Autopi Loggers
    • i have teste both
      • rpm_motor_event will work
      • communication_event will work
  • Autopi Hipernate Settings

Now i would prepare the Community Libary. But the answers of the dumps are incomprehensible for me.
Did you have any ideas?

kai@Maus $ obd.query ENGINE_LOAD mode=01 pid=04 header=7DF unit=% baudrate=500000 protocol=7 verify=false force=true 

value: |-  
       18DAF10A037F0111  
       18DAF105034104FF
       _stamp: '2020-1217T15:19:20.556618'
      _type: engine_loadunit: "%"
      kai@Maus $ obd.query SPEED mode=01 pid=0D header=7DF unit=kph baudrate=500000 protocol=7 
      verify=false force=true 
      value: |-  
      18DAF10103410D00  
      18DAF10A037F0111  
      18DAF10503410D00  
      18DAF10603410D00
      _stamp: '2020-12-17T15:19:51.123916'
      _type: speedunit: kph

  kai@Maus $ obd.query RPM mode=01 pid=0C header=7DF unit=rpm baudrate=500000 protocol=7 
  verify=false force=true 
  
value: |-  
      18DAF10A037F0111  
      18DAF10504410C0000
      _stamp: '2020-12-17T15:20:17.994407'
      _type: rpmunit: rpm`

I´ve also test it with the elmproxy and the Android Carscanner App. And have any Value with the VW E-Golf Profile Settings. But i don´t know the App Profile Settings.But this must at least mean that it is possible to read RPM, Speed and Battery Stats.

EDIT:
Looks like the dump is a hex value answer. Found a little informations.
But i don´t understand.
2 bytes from 18DAF10A037F0111 are 0111? The last 4 numbers are always the value?

You can find a lot of information in the elm327 datasheet here:
https://www.elmelectronics.com/products/dsheets/
for example around p39 of this :

Holy Cow…That´s a huge impact for an paramedic teacher…

At short. I don´t understand this. Thats why i would use Autopi…

But i have play around and try this:
Without “unit=rpm” autopi print me the hex value at last. I know i must slice the hexcodes. I´ve seen the Hyndai Kona examples.
obd.query RPM mode=01 pid=0C header=7DF baudrate=500000 protocol=7 verify=false force=true_type: rpm_stamp: '2020-12-19T12:03:23.926990'value: |- 18DAF10A037F0111 18DAF10504410C0000
but no luck with the last 4 number slicing

obd.query RPM mode=01 pid=0C header=7DF formula='bytes_to_int(message.data[-4:])' baudrate=500000 protocol=7 verify=false force=true

_type: rpm

_stamp: '2020-12-19T12:03:40.248149'

value: 8323345

I may be confused by the formatting above, but there seem to be two values output there, the first one being 0x18DAF10A037F0111, and the second one being 0x18DAF10504410C0000.

That first one is an error value, I think, judging by the 0x7F in the middle, and might be a response to something else? The 0x01 following that 0x7F is the custom service this is in response to, and I don’t know what that last 0x11 is (the end of the Wikipedia page I was looking at displays this as 0x31 instead).

The second value looks like the actual response to your query. 0x18DAF10504 is probably the response header (I don’t know pretty much anything about CAN, so I might be wrong), and the remaining part is the response. There, 0x41 means “response to mode 0x01” (0x01 + 0x40), 0x0C is again the PID you were querying, and the last two bytes are the actual data – in this case, your RPM was 0?

Again, I must emphasize that I’m not an expert, and this is just my interpretation of the data above. My main car doesn’t even use CAN. :slight_smile:

Hej Topeju

Sure i´ve also think about the first Hexcode. But i´ve no idea.

What do you mean with “0x7F” the 7F before 0111?

The Second value “18DAF10504410C0000” is the rpm. I´ve seen entry with this hex codes when i was driving. and the last 4 digits have change. the 2 byte for rpm. But how can i slice this value.
It is not sure that the RPM hex code is always output last. Therefore I think the list slicing (-4:) can be difficult. And with the test here should come out actually also 0 and not 8323345 as value.

I can show a output with more entrys:

kai@Maus $ obd.query PIDS_A mode=01 pid=00 header=7DF baudrate=500000 protocol=7 verify=false force=true

_type: pids_a
_stamp: '2020-12-19T11:03:55.250947'
value: 18DAF101037F0122

kai@Maus $ obd.query PIDS_B mode=01 pid=20 header=7DF baudrate=500000 protocol=7 verify=false force=true

_type: pids_b
_stamp: '2020-12-19T11:04:29.411849'
value: |-
18DAF101037F0122
18DAF10A037F0111
18DAF10506412000000001

kai@Maus $ obd.query PIDS_C mode=01 pid=40 header=7DF baudrate=500000 protocol=7 verify=false force=true
_type: pids_c
_stamp: '2020-12-19T11:04:50.281881'
value: |-
18DAF101037F0122
18DAF10A037F0111

Yes, with 0x7F I was referring to the 7F before the 0111. The last section in that Wikipedia page I linked to describes how the responses are formatted. 18 DA F1 01 03 7F 01 22 has 4 bytes in the beginning that are a response header of some sort (18 DA F1 01). The next byte tells the number of bytes that follow, i.e. 3 (03), and 7F is a general response that indicates the module does not recognize the request. The last two bytes tell something, but certainly not the PIDS supported.

In the RPM query response 18 DA F1 05 04 41 0C 00 00, you again see the header at the beginning (05 and 04 probably refer to module IDs or something – again, I really must emphasize that I do not know this stuff). 41 indicates the service (mode) number being responded to (0x40 is added in the response, so the query was to mode 0x01). 0C is the PID code you requested. And, finally, 00 00 are the actual value.

All of the responses in your latest example indicate errors, except that last response to PIDS_B: 18 DA F1 05 06 41 20 00 00 00 01, which again has the header 18 DA F1 05 06 followed by the service 41, PID 20 (which is PIDS_B), and then the data, 00 00 00 01 which means that only one PID in the range 0x21 to 0x40 is supported, and that is apparently 0x40 (PIDS_C).

I don’t know why you keep getting those errors in your responses. Note that your value 8323345 (decimal) corresponds to 0x7F0111 in hexadecimal, so it is clearly using the error response for the calculation rather than the real value in the second value of the response. In your shoes, I’d try to investigate two things:

  1. Why are you getting those errors and how can you get rid of them?
  2. Is there some way you could only restrict parsing to the non-error responses when there are several values returned?

I’m afraid I can’t help you on either one of those.

Getting back to your question of how to slice the values, I generally don’t use slices from the end but from the beginning, and have to use bytes_to_int(message.data[3:]) to get the value from responses like 6C F1 10 62 12 22 12 9C (fuel rate in my old Chevy). I made the following observations, and hope someone from AutoPi might be able to confirm them:

  • In this example, the actual data is that last 12 - the 9C is some garbage that needs to be ignored, and it seems that AutoPi already drops it from message.data but I’ve not investigated how/where.
  • The starting offset 3 was also a bit strange, but I realized that the slice is not being taken from that hex string but instead from a list of bytes instead, and that list doesn’t have the response header. So, bytes_to_int(message.data[3:]) seems to operate not on any substring of "6CF110621222129C", but this list of bytes instead: [0x62, 0x12, 0x22, 0x12].

It becomes a little clearer.

0x7F0111 is a PID Error . That may be so. Because i don´t know anything about this iso. Google can´t help me.

The starting offset 3 was also a bit strange, but I realized that the slice is not being taken from that hex string but instead from a list of bytes instead, and that list doesn’t have the response header. So, bytes_to_int(message.data[3:]) seems to operate not on any substring of "6CF110621222129C", but this list of bytes instead: [0x62, 0x12, 0x22, 0x12]

how can I display this list?that brings an error
obd.query RPM mode=01 pid=0C header=7DF formula='print(message.data[:])' baudrate=500000 protocol=7 verify=false force=true

i´ve test also with list slicing

kai@Maus $ obd.query RPM mode=01 pid=0C header=7DF formula='bytes_to_int(message.data[32:33])' baudrate=500000 protocol=7 verify=false force=true
        _type: rpm
        _stamp: '2020-12-19T12:03:40.248149'
        value: 0

because i think (but don´t know) this list must be with
value: |- 18DAF10A03**7F0111** 18DAF10504**41***0C***0000**
List slicing with [5:] or 6 or 32:33 also 0 (zero value)

This is all just a guess. Unfortunately, this does not help.
And the sniffer also remains without entries.

I wrote some code to get some info from the errors and hex values.
RPM and Speed is successful so far.

I hope I get some more pids via another OBD tool
import re

def parse(raw_msg):
    pattern = re.compile(r'410D..')
    speed_list = []
    for m in re.finditer(pattern, raw_msg):
        hexed_string = m.group(0)[-2:]
        pyhex = int(hexed_string, 16)
        speed_list.append(pyhex)
    return speed_list
    
def get_speed():
    args = ['speed']
    kwargs = {
        'mode': '01',
        'pid': '0D',
        'header': '7DF',
        'baudrate': 500000,
        'protocol': '7',
        'verify': False,
        'force': True,
    }
    command_query = __salt__['obd.query'](*args, **kwargs)['value']
    result = parse(command_query)
    log.info("Speed Return Value is: {:}".format(result))

    return result

I have found a solution to read the Speed, RPM and a few HighVoltageBattery Information for the ID3 ( i think ID4 or ID5 will work also)
You have to set up different Header with an AT Command. I have create a OBD Worker:

With this header, its possible the read a few Battery Informations:

  • args = [‘SOC’]
    kwargs = {‘mode’: ‘220’, ‘pid’: ‘028C’, ‘unit’: ‘%’,‘header’: ‘FC007B’, ‘baudrate’: 500000, ‘protocol’: ‘7’, ‘verify’: False, ‘force’: True}
    command_query = salt[‘obd.query’](*args, **kwargs)
  • args = [‘hbatt_volt’]
    kwargs = {‘mode’: ‘22’, ‘pid’: ‘1E3B’, ‘unit’: ‘v’,‘header’: ‘FC007B’, ‘baudrate’: 500000, ‘protocol’: ‘7’, ‘verify’: False, ‘force’: True}
    command_query = salt[‘obd.query’](*args, **kwargs)
  • args = [‘hbatt_amp’]
    kwargs = {
    ‘mode’: ‘22’, ‘pid’: ‘1E3D’, ‘unit’: ‘A’, ‘header’: ‘FC007B’, ‘baudrate’: 500000, ‘protocol’: ‘7’,
    ‘verify’: False, ‘force’: True
    }
  • args = [‘charging_status’]
    kwargs = {
    ‘mode’: ‘22’, ‘pid’: ‘7448’, ‘header’: ‘FC007B’, ‘baudrate’: 500000, ‘protocol’: ‘7’,
    ‘verify’: False, ‘force’: True
    }
  • args = [‘batt_max_temp’]
    kwargs = {
    ‘mode’: ‘22’, ‘pid’: ‘1E0F’, ‘unit’: ‘c’, ‘header’: ‘FC007B’, ‘baudrate’: 500000, ‘protocol’: ‘7’,
    ‘verify’: False, ‘force’: True
    }
    command_query = salt[‘obd.query’](*args, **kwargs)
  • args = [‘batt_max_temp’]
    kwargs = {
    ‘mode’: ‘22’, ‘pid’: ‘1E0E’, ‘unit’: ‘c’, ‘header’: ‘FC007B’, ‘baudrate’: 500000, ‘protocol’: ‘7’,
    ‘verify’: False, ‘force’: True
    }
    command_query = salt[‘obd.query’](*args, **kwargs)

The functions for this will follow!

Code Update:
get Speed func

def parse(result):
    value_msg = result.get('value')
    for m in re.finditer(re.compile(r'410D..'), value_msg):
       hexed_string = m.group(0)[-2:]
        pyhex = int(hexed_string, 16)
        result['value'] = pyhex
    return result


def get_speed(*args, **kwargs):
    command_query = __salt__['obd.query'](*args, **kwargs)
    result = parse(command_query)
    return result

Get RPM:

def parse(result):
    error = extract_error_from(result)
    if error:
        log.warning("parse function got error result: {:}".format(result))
        return
    pattern = re.compile(r'410C....')
    value_msg = result.get('value')
    for m in re.finditer(pattern, value_msg):
        hexed_string = m.group(0)[-4:]
        pyhex = int(hexed_string, 16)
        result['value'] = pyhex
        if pyhex >= 1090:
            if not __context__.get("vehicle/motor/stopped"):
                __salt__["event.fire"]({}, "vehicle/motor/running")
        else:
            if not __context__.get("vehicle/motor/running"):
                __salt__["event.fire"]({}, "vehicle/motor/stopped")
     return result

def get_rpm(*args, **kwargs):
    #args = ['RPM']
    #kwargs = {'mode': '01', 'pid': '0C', 'header': 'FC007B', 'baudrate': 500000, 'protocol': '7', 'verify': False, 'force': True}
    command_query = __salt__['obd.query'](*args, **kwargs)
    result = parse(command_query)
    #log.info("RPM Return Value is: {:}".format(result['value']))
    return result

At the moment I am working on a general parser. It looks something like this:

def parser(pattern, byte_length, raw_input):
    """
    (re.compile(r'028C..') == 2
    (re.compile(r'028C....') == 4
    :param pattern: "1E0E"
    :param byte_length: 4
    :param raw_input: "d83831221E0E6B00"
    :return: list of hex ['6B00']
    """
    for i in range(byte_length):
        pattern = pattern + "."
    raw_pattern = r'{}'.format(pattern)
    raw_output = []
    for m in re.finditer(raw_pattern, raw_input):
        hexed_string = m.group(0)[byte_length * -1:]
        print(f"hexed_string is {hexed_string}")
        raw_output.append(hexed_string)
    return raw_output

#print(parser("1E0E", 4, "d83831221E0E6B00"))

But this is also work in progress.
Did you have more Information for me?

3 Likes

Nice work @dasTholo! I wonder what hardware do you use? Because I was trying to get your change_header trick to get working and I don’t seem to be able to use “obd.execute” at all:

# autopi obd.execute ATCP17
: error: Not supported by SocketCAN interface

And it seems like this is the key problem to getting all the PIDs working… Any advice would be appreciated

I have an update! I was able to solve this with a help from Nikola from support. Basically can_flow_control_filter needs to be added which should contain expected reply header as well as mask. For example, SOC can be queried like this:

obd.query SOC_BMS mode=22 pid=028c header=17fc007b formula='bytes_to_int(messages[0].data[3:4]) / 2.5' unit=% baudrate=500000 protocol=7 verify=false force=true can_flow_control_filter=17fe007b,1fffffff
1 Like

This Commands and Codes are for the “old” Autopi with ELM327. I have a few problems with the newer Chipset at the Dimo Device. But maybe i can help with flow filters. I will try

Do you might have more statements with the can_flow_control_filter available?