bms settings

A project log for Model S BMS hacking

Reverse engineer, reprogram or otherwise use the UV/OV/balancing circuits built into each of the 16 modules in a Tesla Model S battery pack.

JarrodJarrod 02/16/2017 at 08:412 Comments

One of the reasons I wanted to reverse engineer rather than replace the microcontroller on the module BMS board is that Tesla have carefully selected voltage levels for under/over voltage thresholds and balancing levels. So it's a bit of a bummer to find that the modules themselves don't seem to have these values built in, it's handled higher up the control chain.

But the BQ76 does have a hardware over and under voltage threshold, as well as over temperature built in, which pulls a shared fault line low.

There are also some other configuration values worth reading out, and since I do have a bunch of boards which have had power applied since the last time they were used, since they have this huge battery backup - the hundreds of 18650 cells - to keep the RAM powered!

So how do we read it out? need to find the address first, using my python script we can iterate through the possible addresses with something like

for address in range(0x3E):
    sendData(Read,[address, 0x00, 0x4C])

And eventually, at address=0x0F I get the result:

TX: 0x1e, 0x0, 0x4c,

RX: 0x1e, 0x00, 0x4c, 0x81, 0x2a, 0x25, 0x25, 0xa5, 0x25, 0xab, 0x25, 0xae, 0x25, 0xad, 0x25, 0xac, 0x25, 0xaa, 0x0a, 0x99, 0x0a, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x31, 0x81, 0x08, 0x81, 0x66, 0xff, 0x15, 0x00, 0x00, 0x00, 0xad,

Labeling each register:

0: 81        #status register
1: 2a 25     #GPAI measurement data
3: 25 a5     #cell 1 voltage data
5: 25 ab     #cell 2 voltage data
7: 25 ae     #cell 3 voltage data
9 25 ad      #cell 4 voltage data
b: 25 ac     #cell 5 voltage data
d: 25 aa     #cell 6 voltage data
f: a 99      #TS1 voltage data
11: a 93     #TS2 voltage data
13-1f rsvd   
20: 0        #alert status
21: 0        #fault status
22: 0        #OV fault state
23: 0        #UV fault state
24: 0        #parity result A
25: 0        #parity result B
26-2f rsvd
30: 3d       #ADC measurement control
31: 3        #I/O pin control
32: 0        #cell balancing control
33: 0        #cell balancing max on time
34: 0        #ADC conversion start
35-39: rsvd
3a: 0        #group 3 registers write access control
3b: 8f       #Address register
3c: 0        #reset control
3d: 0        #test mode selection
3e: rsvd
3f: 0        #EPROM programming enable
40: 10       #Function configuration
41: 80       #IO configuration
42: 31       #OV setpoint
43: 81       #OV time delay
44: 8        #UV setpoint
45: 81       #UV time delay
46: 66       #over temperature set point
47: ff       #over temperature time delay
48: 15       #user data 1
49: 0        #user data 2
4a: 0        #user data 3
4b: 0        #user data 4

So the values after 0x3F are identical to those read from the EPROM on a restart, they set to OV/UV/OT and time delays.

OV setpoint = 0x31, datasheet says 2V + 50mV * 49 = 4.45V

UV setpoint = 0x08. datasheet says 0.7V + 100mV * 8 = 1.50V

OT setpoint = 0x66. Table 2 says this corresponds to 1.578V, which is 65C for a 10k NTC such as ERT-J1VG103FA

Interesting values, the cells will basically be on fire if they reach these voltages.. The temperature value is much more reasonable.

Since we can't guarantee the register content, best practice is to just reset the BQ76 chips with a broadcast reset, then set it up from scratch. EPROM values will be loaded automatically but there are a few registers that require setup after a reset, Address control, ADC control, IO control and the fault and alert registers.

### ~~ Startup Code ~~ ###
#A5 is the magic value to reset the chips
sendData( Write, [broadcast, RESET_CONTROL, 0xA5])    
#set address
sendData( Write, [0x00, ADDRESS_CONTROL, address|0x80]) #do this for each BMS on the daisychain
#configure ADC
sendData( Write, [address, ADC_CONTROL, 0x3D])
#configure IO
sendData( Write, [address, IO_CONRTOL, 0x03])
#clear faults, need to write a 1 to the fault bit to be cleared, then a zero
sendData( Write, [address, ALERT_STATUS, 0x80])
sendData( Write, [address, ALERT_STATUS, 0x00])
sendData( Write, [address, FAULT_STATUS, 0x08])
sendData( Write, [address, FAULT_STATUS, 0x00])

I've put a new version of the python script in the files section which also reads out and converts the ADC values to voltages.


kenny house wrote 02/23/2017 at 21:21 point

Excellent progress--thanks for the update report.

Was this the same board that you were testing previously, or was this a different board--possibly from BMB15 on the daisy-chain?  i found serial number data embedded in the flash code, still looking for where the device ID is stored.   

The bq76 has the capability to use shadow data to override the stored eprom values--look at sending the access code to the shadow_control register 3A.  That way you can adjust the OV, UV, OT values to more useful values that might actually protect the cells.


  Are you sure? yes | no

Jarrod wrote 02/23/2017 at 21:55 point

This board has BMB-16 printed on it's J1 connector, the last board from which I read the firmware had BMB-11 on the sticker. The number appears to correspond to the stored device address. Not that it really matters as you can easily reset and reassign the addresses. 

The 'shadow data' is the EPROM data, which yes is programmable although it requires a 7V programming voltage. But the values in there kinda make sense:
The secondary protector (UV/OV/OT fault line) as the name implies, is secondary. Primary protection is done by reading out values and processing them in an external microcontroller. It therefore makes sense for the secondary protector values to have wider margins than you would expect for proper a BMS. Under normal use it should never trigger a fault

  Are you sure? yes | no