Skip to content

Commit 7ffab5d

Browse files
committed
Add support for Mode 9 PIDS
This adds support for the Mode 9 PIDS, including VIN and CVN. This is baed on the original work by Paul Mundt (#151). This patch includes adding a few test cases for the new decoders. Signed-off-by: Alistair Francis <alistair@alistair23.me>
1 parent 55c7926 commit 7ffab5d

File tree

5 files changed

+86
-0
lines changed

5 files changed

+86
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ Here are a handful of the supported commands (sensors). For a full list, see [th
6060
- Time since trouble codes cleared
6161
- Hybrid battery pack remaining life
6262
- Engine fuel rate
63+
- Vehicle Identification Number (VIN)
6364

6465
Common Issues
6566
-------------

docs/Command Tables.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,3 +261,25 @@ The return value will be encoded in the same structure as the Mode 03 `GET_DTC`
261261
| N/A | GET_CURRENT_DTC | Get DTCs from the current/last driving cycle | [special](Responses.md#diagnostic-trouble-codes-dtcs) |
262262

263263
<br>
264+
265+
# Mode 09
266+
267+
<span style="color:red">*WARNING: mode 09 is experimental. While it has been tested on a hardware simulator, only a subset of the supported
268+
commands have (00-06) been tested. Any debug output for this mode, especially for the untested PIDs, would be greatly appreciated.*</span>
269+
270+
|PID | Name | Description | Response Value |
271+
|----|------------------------------|----------------------------------------------------|-----------------------|
272+
| 00 | PIDS_9A | Supported PIDs [01-20] | BitArray |
273+
| 01 | VIN_MESSAGE_COUNT | VIN Message Count | Unit.count |
274+
| 02 | VIN | Vehicle Identification Number | string |
275+
| 03 | CALIBRATION_ID_MESSAGE_COUNT | Calibration ID message count for PID 04 | Unit.count |
276+
| 04 | CALIBRATION_ID | Calibration ID | string |
277+
| 05 | CVN_MESSAGE_COUNT | CVN Message Count for PID 06 | Unit.count |
278+
| 06 | CVN | Calibration Verification Numbers | hex string |
279+
| 07 | PERF_TRACKING_MESSAGE_COUNT | Performance tracking message count | TODO |
280+
| 08 | PERF_TRACKING_SPARK | In-use performance tracking (spark ignition) | TODO |
281+
| 09 | ECU_NAME_MESSAGE_COUNT | ECU Name Message Count for PID 0A | TODO |
282+
| 0a | ECU_NAME | ECU Name | TODO |
283+
| 0b | PERF_TRACKING_COMPRESSION | In-use performance tracking (compression ignition) | TODO |
284+
285+
<br>

obd/commands.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,27 @@
279279
OBDCommand("GET_CURRENT_DTC", "Get DTCs from the current/last driving cycle", b"07", 0, dtc, ECU.ALL, False),
280280
]
281281

282+
283+
__mode9__ = [
284+
# name description cmd bytes decoder ECU fast
285+
OBDCommand("PIDS_9A" , "Supported PIDs [01-20]" , b"0900", 7, pid, ECU.ALL, True),
286+
OBDCommand("VIN_MESSAGE_COUNT" , "VIN Message Count" , b"0901", 3, count, ECU.ENGINE, True),
287+
OBDCommand("VIN" , "Vehicle Identification Number" , b"0902", 22, encoded_string(17), ECU.ENGINE, True),
288+
OBDCommand("CALIBRATION_ID_MESSAGE_COUNT","Calibration ID message count for PID 04" , b"0903", 3, count, ECU.ALL, True),
289+
OBDCommand("CALIBRATION_ID" , "Calibration ID" , b"0904", 18, encoded_string(16), ECU.ALL, True),
290+
OBDCommand("CVN_MESSAGE_COUNT" , "CVN Message Count for PID 06" , b"0905", 3, count, ECU.ALL, True),
291+
OBDCommand("CVN" , "Calibration Verification Numbers" , b"0906", 10, cvn, ECU.ALL, True),
292+
293+
#
294+
# NOTE: The following are untested
295+
#
296+
# OBDCommand("PERF_TRACKING_MESSAGE_COUNT", "Performance tracking message count" , b"0907", 3, count, ECU.ALL, True),
297+
# OBDCommand("PERF_TRACKING_SPARK" , "In-use performance tracking (spark ignition)" , b"0908", 4, raw_string, ECU.ALL, True),
298+
# OBDCommand("ECU_NAME_MESSAGE_COUNT" , "ECU Name Message Count for PID 0A" , b"0909", 3, count, ECU.ALL, True),
299+
# OBDCommand("ECU_NAME" , "ECU Name" , b"090a", 20, raw_string, ECU.ALL, True),
300+
# OBDCommand("PERF_TRACKING_COMPRESSION" , "In-use performance tracking (compression ignition)", b"090b", 4, raw_string, ECU.ALL, True),
301+
]
302+
282303
__misc__ = [
283304
OBDCommand("ELM_VERSION", "ELM327 version string", b"ATI", 0, raw_string, ECU.UNKNOWN, False),
284305
OBDCommand("ELM_VOLTAGE", "Voltage detected by OBD-II adapter", b"ATRV", 0, elm_voltage, ECU.UNKNOWN, False),
@@ -303,6 +324,7 @@ def __init__(self):
303324
__mode6__,
304325
__mode7__,
305326
[],
327+
__mode9__,
306328
]
307329

308330
# allow commands to be accessed by name
@@ -350,6 +372,7 @@ def base_commands(self):
350372
"""
351373
return [
352374
self.PIDS_A,
375+
self.PIDS_9A,
353376
self.MIDS_A,
354377
self.GET_DTC,
355378
self.CLEAR_DTC,

obd/decoders.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ def decode_uas(messages, id_):
9494
Return pint Quantities
9595
"""
9696

97+
def count(messages):
98+
d = messages[0].data[2:]
99+
v = bytes_to_int(d)
100+
return v * Unit.count
97101

98102
# 0 to 100 %
99103
def percent(messages):
@@ -484,3 +488,31 @@ def monitor(messages):
484488
mon.add_test(test)
485489

486490
return mon
491+
492+
493+
def encoded_string(length):
494+
""" Extract an encoded string from multi-part messages """
495+
return functools.partial(decode_encoded_string, length=length)
496+
497+
498+
def decode_encoded_string(messages, length):
499+
d = messages[0].data[2:]
500+
501+
if len(d) < length:
502+
logger.debug("Invalid string {}. Discarding...", d)
503+
return None
504+
505+
# Encoded strings come in bundles of messages with leading null values to
506+
# pad out the string to the next full message size. We strip off the
507+
# leading null characters here and return the resulting string.
508+
d.strip()
509+
d.strip([b'\x00', b'\x01', b'\x02', b'\\x00', b'\\x01', b'\\x02'])
510+
511+
return d
512+
513+
514+
def cvn(messages):
515+
d = decode_encoded_string(messages, 4)
516+
if d is None:
517+
return None
518+
return bytes_to_hex(d)

tests/test_decoders.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,14 @@ def test_dtc():
295295
("B0003", ""),
296296
]
297297

298+
def test_vin_message_count():
299+
assert d.count(m("0901")) == 0
300+
301+
def test_vin():
302+
assert d.encoded_string(17)(m("0201575030" + "5A5A5A39395A54" + "53333932313234")) == bytearray(b'WP0ZZZ99ZTS392124')
303+
304+
def test_cvn():
305+
assert d.cvn(m("6021791bc8216e0b")) == '791bc8216e0b'
298306

299307
def test_monitor():
300308
# single test -----------------------------------------

0 commit comments

Comments
 (0)