Some custom Chevy Bolt PIDs work, but some don't.
I run a Chevy Bolt as a race car in 24 Hours of Lemons.
It’s useful for our racing drivers to see some additional gauges, such as the exact battery state-of-charge, the battery temperature, and the motor temperature. For these gauges, we initially used an Android phone with the Torque app and a CSV file of Bolt PIDs which I found here, and can be downloaded here.
But, we recently transitioned to using a Raspberry Pi instead of the Android phone, because the Pi gives us more options for data logging and it has enabled us to add custom gauges such as “how much battery did we use in the current lap?” On the Raspberry Pi, our setup is:
- We get the OBD2 with an ELM327 bluetooth device
- We connect the Raspberry Pi to the ELM327 via a bluetooth USB dongle
- We read the OBD2 data using the python-OBD
Success story with OBD gauges on the Raspberry Pi
For some things, our Raspberry Pi setup works great. For instance, here is a code example of how we read the exact battery percent, and we did this by reimplementing the formula from the OBD2 CSV in Python as follows.
import obd
from obd import Unit
from obd.OBDCommand import OBDCommand
from obd.protocols import ECU
OBD_PORT = "/dev/serial/by-id/<the_device>”
connection = obd.OBD(OBD_PORT)
def soc_hd_decoder(messages):
# 5 bytes of response; we use the last 2 bytes.
d = messages[0].data[3:]
A = float(d[0])
B = float(d[1])
v = ((((A*256)+B)*100)/65535)
return v * Unit.percent
c = OBDCommand("HYBRID_BATTERY_REMAINING_HD",
"Hybrid battery pack remaining life HD",
b"2243AF",
5, # bytes of expected response
soc_hd_decoder,
ECU.UNKNOWN,
True,
header=b"7E4")
connection.supported_commands.add(c)
response = connection.query(c)
print(response.value)
The above prints prints 66.01815823605708 percent, which roughly matches the car’s built-in display on the dashboard.
Failure case that we want help to debug
However, when we try to read the motor temperature, it does not work properly. Here is what we tried:
def raw_decoder(messages):
# for debugging, print the OBD2 response
d = messages[0].data
print(f" Hex: {[hex(byte) for byte in d]}")
return d
c = OBDCommand("MOTOR_TEMP",
"Motor Temperature",
b"2228CB",
4, # bytes of expected response
raw_decoder,
ECU.UNKNOWN,
True,
header=b"7E4" # spreadsheet says 7E1, but 7E4 actually gives a response.
)
connection.supported_commands.add(c)
response = connection.query(c)
print(response.value)
This prints: Hex: ['0x7f', '0x22', '0x31', '0x0']
In other words, it responds with the hexidecimal code 7f2231. When I google 7f2231, I see that it is a code that means “Request out of range,” in other words the request is not supported. But, what’s strange is that using the same ELM327 device with Torque on Android, I get a reasonable response for the motor temp. And in the Torque app, when I drive the car fast on the track, the motor temperature goes up, so I think it's really working properly in the Torque app. But, when I read this motor temperature signal (2228CB) in python-OBD, I always get the same error code, no matter how I have been driving the car.
Any advice on this? Thanks!
P.S. If you’re curious about our EV racing stuff, this video gives a bit more info.
That's a really cool project!
I don't have any advice for debugging though. Are you able to see what the Torque app is doing?
@alistair23 Thanks for the reply! I wish I had the source code for Torque. But, here is what I know:
Torque is able to take a CSV file for car-specific codes, and then it can display gauges for those codes. For example, here is a CSV file for a Chevy Bolt. Below are the formulae in the CSV file that I translated into a python-OBD implementation.
Name | ShortName | ModeAndPID | Equation | Min Value | Max Value | Units | Header
!Battery - Pack - State of Charge Raw HD | SoC Raw HD | 2243AF | ((((A*256)+B)*100)/65535) | 0 | 105 | % | 7E4
*Motor Temperature | Motor Temp | 2228CB | A-40 | -40 | 120 | C | 7E1
Trying lots of codes
I tried lots of OBD codes from the CSV. All of the following work properly in the Torque app. But, here is what does/doesn't work in python-OBD:
-
These work:
- 22434A (Min battery temperature)
- 224349 (Max battery temperature)
- 224357 (Electronics coolant pump request)
- 2213AF (Radiator fan request)
- 2243AF (Hybrid battery pack remaining life HD)
-
These always return the same value (an error):
- 224359 (Battery coolant pump request)
- 222C05 (Transmission pump request)
- 🌟 2228CB (Motor Temperature)
- 🌟 221940 (Transmission fluid temperature)
- 221C43 (Electronics coolant temperature)
I put a star 🌟 next to the ones that I really want to get working.
Any pattern to this?
One pattern I notice is that in the CSV file, the ones that work are all header 7E4. Some of the failing codes also have header 7E4. But, all the codes with header 7E1 do not work.
One more thing
P.S. The build-in commands in the python-OBD repo mostly work, including:
- SPEED
- HYBRID_BATTERY_REMAINING. This shows the same value as my custom "HYBRID_BATTERY_REMAINING_HD," but my HD version has the number to more decimal places.
- ACCELERATOR_POS_D
- ACCELERATOR_POS_E
I do have one guess of what might be wrong. It could be that when I do OBDCommand(..., ecu=ECU.UNKNOWN, ...), I should actually use a different value for ecu. For example, instead of ECU.UNKNOWN (which corresponds to 0b00000001), perhaps I should try ECU.ALL (which corresponds to 0b11111111).
https://github.com/brendan-w/python-OBD/blob/a47d791de1884da167a4bb51f6c576dc07019a9a/obd/protocols/protocol.py#L52-L61
I think I got it working! This is the winning recipe:
def four_byte_temperature_decoder(messages):
# 4 bits of response; we use the last 1 bit.
d = messages[0].data[3:]
v = float(d[0]) - 40.0
return Unit.Quantity(v, Unit.celsius)
c = OBDCommand("MOTOR_TEMP",
"Motor Temperature",
b"2228CB",
4, # bytes of expected response
four_byte_temperature_decoder,
ECU.TRANSMISSION,
True,
header=b"7E1"
)
connection.supported_commands.add(c)
response = connection.query(c)
print(response.value)
When the car is cold (not driven yet today), it prints 10.0 degree_Celsius. This approximately matches the OBD2 readings of other component temperatures in the car.