Responses
The query()
function returns OBDResponse
objects. These objects have the following properties:
Property | Description |
---|---|
value | The decoded value from the car |
command | The OBDCommand object that triggered this response |
message | The internal Message object containing the raw response from the car |
time | Timestamp of response (as given by time.time() ) |
is_null()
Use this function to check if a response is empty. Python-OBD will emit empty responses when it is unable to retrieve data from the car.
r = connection.query(obd.commands.RPM)
if not r.is_null():
print(r.value)
Pint Values
The value
property typically contains a Pint Quantity
object, but can also hold complex structures (depending on the request). Pint quantities combine a value and unit into a single class, and are used to represent physical values such as "4 seconds", and "88 mph". This allows for consistency when doing math and unit conversions. Pint maintains a registry of units, which is exposed in python-OBD as obd.Unit
.
Below are common operations that can be done with Pint units and quantities. For more information, check out the Pint Documentation.
NOTE: for backwards compatibility with previous versions of python-OBD, use response.value.magnitude
in place of response.value
import obd
>>> response.value
<Quantity(100, 'kph')>
# get the raw python datatype
>>> response.value.magnitude
100
# converts quantities to strings
>>> str(response.value)
'100 kph'
# convert strings to quantities
>>> obd.Unit("100 kph")
<Quantity(100, 'kph')>
# handles conversions nicely
>>> response.value.to('mph')
<Quantity(62.13711922373341, 'mph')>
# scaler math
>>> response.value / 2
<Quantity(50.0, 'kph')>
# non-scaler math requires you to specify units yourself
>>> response.value + (20 * obd.Unit.kph)
<Quantity(120, 'kph')>
# non-scaler math with different units
# handles unit conversions transparently
>>> response.value + (20 * obd.Unit.mph)
<Quantity(132.18688, 'kph')>
Status
The status command returns information about the Malfunction Indicator Light (check-engine light), the number of trouble codes being thrown, and the type of engine.
response.value.MIL # boolean for whether the check-engine is lit
response.value.DTC_count # number (int) of DTCs being thrown
response.value.ignition_type # "spark" or "compression"
The status command also provides information regarding the availability and status of various system tests. These are exposed as StatusTest
objects, loaded into named properties. Each test object has boolean flags for its availability and completion.
response.value.MISFIRE_MONITORING.available # boolean for test availability
response.value.MISFIRE_MONITORING.complete # boolean for test completion
Here are all of the tests names that python-OBD reports:
Tests |
---|
MISFIRE_MONITORING |
FUEL_SYSTEM_MONITORING |
COMPONENT_MONITORING |
CATALYST_MONITORING |
HEATED_CATALYST_MONITORING |
EVAPORATIVE_SYSTEM_MONITORING |
SECONDARY_AIR_SYSTEM_MONITORING |
OXYGEN_SENSOR_MONITORING |
OXYGEN_SENSOR_HEATER_MONITORING |
EGR_VVT_SYSTEM_MONITORING |
NMHC_CATALYST_MONITORING |
NOX_SCR_AFTERTREATMENT_MONITORING |
BOOST_PRESSURE_MONITORING |
EXHAUST_GAS_SENSOR_MONITORING |
PM_FILTER_MONITORING |
Diagnostic Trouble Codes (DTCs)
Each DTC is represented by a tuple containing the DTC code, and a description (if python-OBD has one). For commands that return multiple DTCs, a list is used.
# obd.commands.GET_DTC
response.value = [
("P0104", "Mass or Volume Air Flow Circuit Intermittent"),
("B0003", ""), # unknown error code, it's probably vehicle-specific
("C0123", "")
]
# obd.commands.FREEZE_DTC
response.value = ("P0104", "Mass or Volume Air Flow Circuit Intermittent")
Fuel Status
The fuel status is a tuple of two strings, telling the status of the first and second fuel systems. Most cars only have one system, so the second element will likely be an empty string. The possible fuel statuses are:
Fuel Status |
---|
"" |
"Open loop due to insufficient engine temperature" |
"Closed loop, using oxygen sensor feedback to determine fuel mix" |
"Open loop due to engine load OR fuel cut due to deceleration" |
"Open loop due to system failure" |
"Closed loop, using at least one oxygen sensor but there is a fault in the feedback system" |
Air Status
The air status will be one of these strings:
Air Status |
---|
"Upstream" |
"Downstream of catalytic converter" |
"From the outside atmosphere or off" |
"Pump commanded on for diagnostics" |
Oxygen Sensors Present
Returns a 2D structure of tuples (representing bank and sensor number), that holds boolean values for sensor presence.
# obd.commands.O2_SENSORS
response.value = (
(), # bank 0 is invalid, this is merely for correct indexing
(True, True, True, False), # bank 1
(False, False, False, False) # bank 2
)
# obd.commands.O2_SENSORS_ALT
response.value = (
(), # bank 0 is invalid, this is merely for correct indexing
(True, True), # bank 1
(True, False), # bank 2
(False, False), # bank 3
(False, False) # bank 4
)
# example usage:
response.value[1][2] == True # Bank 1, Sensor 2 is present
Monitors (Mode 06 Responses)
All mode 06 commands return Monitor
objects holding various test results for the requested sensor. A single monitor response can hold multiple tests, in the form of MonitorTest
objects. The OBD standard defines some tests, but vehicles can always implement custom tests beyond the standard. Here are the standard Test IDs (TIDs) that python-OBD will recognize:
TID | Name | Description |
---|---|---|
01 | RTL_THRESHOLD_VOLTAGE | Rich to lean sensor threshold voltage |
02 | LTR_THRESHOLD_VOLTAGE | Lean to rich sensor threshold voltage |
03 | LOW_VOLTAGE_SWITCH_TIME | Low sensor voltage for switch time calculation |
04 | HIGH_VOLTAGE_SWITCH_TIME | High sensor voltage for switch time calculation |
05 | RTL_SWITCH_TIME | Rich to lean sensor switch time |
06 | LTR_SWITCH_TIME | Lean to rich sensor switch time |
07 | MIN_VOLTAGE | Minimum sensor voltage for test cycle |
08 | MAX_VOLTAGE | Maximum sensor voltage for test cycle |
09 | TRANSITION_TIME | Time between sensor transitions |
0A | SENSOR_PERIOD | Sensor period |
0B | MISFIRE_AVERAGE | Average misfire counts for last ten driving cycles |
0C | MISFIRE_COUNT | Misfire counts for last/current driving cycles |
Test results can be accessed by property name or TID (same as the obd.commands
tables). All of the standard tests above will be present, though some may be null. Use the MonitorTest.is_null()
function to determine if a test is null.
response.value.MISFIRE_COUNT
# OR
response.value["MISFIRE_COUNT"]
# OR
response.value[0x0C] # TID for MISFIRE_COUNT
All MonitorTest
objects have the following properties: (for null tests, these are set to None
)
result = response.value.MISFIRE_COUNT
result.tid # integer Test ID for this test
result.name # test name
result.desc # test description
result.value # value of the test (will be a Pint value, or in rare cases, a boolean)
result.min # maximum acceptable value
result.max # minimum acceptable value
result.passed # boolean marking the test as passing
Here is an example of looking up live misfire counts for the engine's second cylinder:
import obd
connection = obd.OBD()
response = connection.query(obd.commands.MONITOR_MISFIRE_CYLINDER_2)
# in the test results, lookup the result for MISFIRE_COUNT
result = response.value.MISFIRE_COUNT
# check that we got data for this test
if not result.is_null():
print(result.value) # will be a Pint value
else:
print("Misfire count wasn't reported")