Anybody mess with the CAN Commanded Torque Message ID 0x1D4?

My Nissan Leaf Forum

Help Support My Nissan Leaf Forum:

This site may earn a commission from merchant affiliate links, including eBay, Amazon, and others.
JeremyW said:
weber said:
In case anyone still cares about the 8-bit CRC in the Leaf's ID 0x1D4 CAN packets, my colleague Coulomb and I have reverse-engineered it. We used the excellent method described in New Zealander Greg Ewing's awesome paper, "Reverse-Engineering a CRC Algorithm".
http://www.cosc.canterbury.ac.nz/greg.ewing/essays/CRC-Reverse-Engineering.html

The polynomial is 0x85 using left shifts. It takes the bytes in little-endian order and the initial value and final XOR are both zero. The only place that I can find that this polynomial has been used before is in a Nintendo game controller.

We'd be very interested to know if anyone has any evidence that this actually contains a torque command from VCM to TMI. It looks to us like telemetry going the other way. We'd also be interested if anyone has any theories about what ID packet does contain the torque command.

Thank you so much for this. Seriously. I'm going to use it to "tune" my leaf. Oh yes. :)
Sweet! Nice job!
 
I can't get the the checksum to match using polynomial 0x85. I programmed the rutine to check it myself so there could be a problem but it really seems like it is working correctly.

When you say little endian do you mean that the data should be check in this order D6 D5 D4 D3 D2 D1 D0:
For example, when I use the data posted here where:
D0:F7 D1:07 D2:00 D3:00 D4:07 D5:44 D6:30 CRC:70

My algorethem gives me a CRC of 0x6B

Let me post my code in case there is some sort of glaring problem:

X=0;

matrix[X+0]=((TEST.m_sWhichBit.m_aucData[6] & 0x80) >> 7);
matrix[X+1]=((TEST.m_sWhichBit.m_aucData[6] & 0x40) >> 6);
matrix[X+2]=((TEST.m_sWhichBit.m_aucData[6] & 0x20) >> 5);
matrix[X+3]=((TEST.m_sWhichBit.m_aucData[6] & 0x10) >> 4);
matrix[X+4]=((TEST.m_sWhichBit.m_aucData[6] & 0x08) >> 3);
matrix[X+5]=((TEST.m_sWhichBit.m_aucData[6] & 0x04) >> 2);
matrix[X+6]=((TEST.m_sWhichBit.m_aucData[6] & 0x02) >> 1);
matrix[X+7]=((TEST.m_sWhichBit.m_aucData[6] & 0x01) >> 0);
X=X+8;
matrix[X+0]=((TEST.m_sWhichBit.m_aucData[5] & 0x80) >> 7);
matrix[X+1]=((TEST.m_sWhichBit.m_aucData[5] & 0x40) >> 6);
matrix[X+2]=((TEST.m_sWhichBit.m_aucData[5] & 0x20) >> 5);
matrix[X+3]=((TEST.m_sWhichBit.m_aucData[5] & 0x10) >> 4);
matrix[X+4]=((TEST.m_sWhichBit.m_aucData[5] & 0x08) >> 3);
matrix[X+5]=((TEST.m_sWhichBit.m_aucData[5] & 0x04) >> 2);
matrix[X+6]=((TEST.m_sWhichBit.m_aucData[5] & 0x02) >> 1);
matrix[X+7]=((TEST.m_sWhichBit.m_aucData[5] & 0x01) >> 0);
X=X+8;
matrix[X+0]=((TEST.m_sWhichBit.m_aucData[4] & 0x80) >> 7);
matrix[X+1]=((TEST.m_sWhichBit.m_aucData[4] & 0x40) >> 6);
matrix[X+2]=((TEST.m_sWhichBit.m_aucData[4] & 0x20) >> 5);
matrix[X+3]=((TEST.m_sWhichBit.m_aucData[4] & 0x10) >> 4);
matrix[X+4]=((TEST.m_sWhichBit.m_aucData[4] & 0x08) >> 3);
matrix[X+5]=((TEST.m_sWhichBit.m_aucData[4] & 0x04) >> 2);
matrix[X+6]=((TEST.m_sWhichBit.m_aucData[4] & 0x02) >> 1);
matrix[X+7]=((TEST.m_sWhichBit.m_aucData[4] & 0x01) >> 0);
X=X+8;
matrix[X+0]=((TEST.m_sWhichBit.m_aucData[3] & 0x80) >> 7);
matrix[X+1]=((TEST.m_sWhichBit.m_aucData[3] & 0x40) >> 6);
matrix[X+2]=((TEST.m_sWhichBit.m_aucData[3] & 0x20) >> 5);
matrix[X+3]=((TEST.m_sWhichBit.m_aucData[3] & 0x10) >> 4);
matrix[X+4]=((TEST.m_sWhichBit.m_aucData[3] & 0x08) >> 3);
matrix[X+5]=((TEST.m_sWhichBit.m_aucData[3] & 0x04) >> 2);
matrix[X+6]=((TEST.m_sWhichBit.m_aucData[3] & 0x02) >> 1);
matrix[X+7]=((TEST.m_sWhichBit.m_aucData[3] & 0x01) >> 0);
X=X+8;
matrix[X+0]=((TEST.m_sWhichBit.m_aucData[2] & 0x80) >> 7);
matrix[X+1]=((TEST.m_sWhichBit.m_aucData[2] & 0x40) >> 6);
matrix[X+2]=((TEST.m_sWhichBit.m_aucData[2] & 0x20) >> 5);
matrix[X+3]=((TEST.m_sWhichBit.m_aucData[2] & 0x10) >> 4);
matrix[X+4]=((TEST.m_sWhichBit.m_aucData[2] & 0x08) >> 3);
matrix[X+5]=((TEST.m_sWhichBit.m_aucData[2] & 0x04) >> 2);
matrix[X+6]=((TEST.m_sWhichBit.m_aucData[2] & 0x02) >> 1);
matrix[X+7]=((TEST.m_sWhichBit.m_aucData[2] & 0x01) >> 0);
X=X+8;
matrix[X+0]=((TEST.m_sWhichBit.m_aucData[1] & 0x80) >> 7);
matrix[X+1]=((TEST.m_sWhichBit.m_aucData[1] & 0x40) >> 6);
matrix[X+2]=((TEST.m_sWhichBit.m_aucData[1] & 0x20) >> 5);
matrix[X+3]=((TEST.m_sWhichBit.m_aucData[1] & 0x10) >> 4);
matrix[X+4]=((TEST.m_sWhichBit.m_aucData[1] & 0x08) >> 3);
matrix[X+5]=((TEST.m_sWhichBit.m_aucData[1] & 0x04) >> 2);
matrix[X+6]=((TEST.m_sWhichBit.m_aucData[1] & 0x02) >> 1);
matrix[X+7]=((TEST.m_sWhichBit.m_aucData[1] & 0x01) >> 0);
X=X+8;
matrix[X+0]=((TEST.m_sWhichBit.m_aucData[0] & 0x80) >> 7);
matrix[X+1]=((TEST.m_sWhichBit.m_aucData[0] & 0x40) >> 6);
matrix[X+2]=((TEST.m_sWhichBit.m_aucData[0] & 0x20) >> 5);
matrix[X+3]=((TEST.m_sWhichBit.m_aucData[0] & 0x10) >> 4);
matrix[X+4]=((TEST.m_sWhichBit.m_aucData[0] & 0x08) >> 3);
matrix[X+5]=((TEST.m_sWhichBit.m_aucData[0] & 0x04) >> 2);
matrix[X+6]=((TEST.m_sWhichBit.m_aucData[0] & 0x02) >> 1);
matrix[X+7]=((TEST.m_sWhichBit.m_aucData[0] & 0x01) >> 0);
X=X+8;

matrix[X]=0;
matrix[X+1]=0;
matrix[X+2]=0;
matrix[X+3]=0;
matrix[X+4]=0;
matrix[X+5]=0;
matrix[X+6]=0;
matrix[X+7]=0;

Workingvalue= ((matrix[0]<<7) | (matrix[1]<<6) | (matrix[2]<<5) | (matrix[3]<<4) | (matrix[4]<<3) | (matrix[5]<<2) | (matrix[6]<<1) | (matrix[7]));
X=8;
while (X<=63){


while (((Workingvalue & 0x80)==0) && X<=63){
Workingvalue=(Workingvalue<<1)|matrix[X];
X++;}
Workingvalue=Workingvalue ^ (0x85);
Trace("%x", Workingvalue);
}






Trace("%x", Workingvalue);
 
ernieskaggs said:
When you say little endian do you mean that the data should be check in this order D6 D5 D4 D3 D2 D1 D0:

Sorry, Ernie. I may have used the wrong terminology, but I actually meant to process them in the order they are transmitted D0 D1 D2 D3 D4 D5 D6, where D7 is the CRC.
 
Weber and all, you might wanna take a look at EV can messages 1DB, 1DC, and 55B to see if 0x85 works for those too. The last byte looks like a CRC (values all over the place).

CRC stuff isn't clicking for me yet. I'll have to read that paper for like the 3rd or 4th time. :?
 
ernieskaggs,

I don't fully understand your code, so please ignore me if I've got this wrong. But it looks to me that you're using the 0x85 as an XOR value, but it's a generator polynomial (I hope I got that term right, see http://en.wikipedia.org/wiki/Cyclic_redundancy_check" onclick="window.open(this.href);return false; ). The value 0x85 determines which taps are present in a standard CRC algorithm; in this case, since 0x85 has three ones, there are three taps.

So it seems to me that your code needs rewriting from scratch. See if you can find some example code on the web, as we did. We tested algorithms in a spreadsheet, and I think I tried some C code, but Weber seemed to be getting more progress with the spreadsheet.
 
Thanks Coulomb, I hadn't noticed that. Ernie, the XORing with the polynomial needs to be inside the loop, not outside. And it needs to be conditional on the value of the bit being shifted out of the working value. I think you want something like:

Code:
while (X<=63){
    if ((Workingvalue & 0x80)==0) {
        Workingvalue=(Workingvalue<<1)|matrix[X]; }
    else {
        Workingvalue=(Workingvalue<<1)|matrix[X];
        Workingvalue=Workingvalue ^ 0x85; }
    X++;}

But note the fine details in this chapter of Ross Williams' excellent "A Painless Guide to CRC Error Detection Algorithms"
http://www.repairfaq.org/filipg/LINK/F_crc_v33.html#CRCV_001
 
FWIW, I cannot seem to make it work either. The above code does match the "SIMPLE" algorithm from the chapter, but I end up of 0x57 instead of 0x70. Below is the line-by-line computation and it seems correct for 0x85 poly, but no luck.

Code:
F7	01	00	00	07	44	30	70
data	 work
1	01	00000001
1	03	00000011
1	07	00000111
1	0F	00001111
0	1E	00011110
1	3D	00111101
1	7B	01111011
1	F7	11110111
0	6B	01101011
0	D6	11010110
0	29	00101001
0	52	01010010
0	A4	10100100
0	CD	11001101
0	1F	00011111
1	3F	00111111
0	7E	01111110
0	FC	11111100
0	7D	01111101
0	FA	11111010
0	71	01110001
0	E2	11100010
0	41	01000001
0	82	10000010
0	81	10000001
0	87	10000111
0	8B	10001011
0	93	10010011
0	A3	10100011
0	C3	11000011
0	03	00000011
0	06	00000110
0	0C	00001100
0	18	00011000
0	30	00110000
0	60	01100000
0	C0	11000000
1	04	00000100
1	09	00001001
1	13	00010011
0	26	00100110
1	4D	01001101
0	9A	10011010
0	B1	10110001
0	E7	11100111
1	4A	01001010
0	94	10010100
0	AD	10101101
0	DF	11011111
0	3B	00111011
1	77	01110111
1	EF	11101111
0	5B	01011011
0	B6	10110110
0	E9	11101001
0	57	01010111
 
TickTock said:
FWIW, I cannot seem to make it work either. The above code does match the "SIMPLE" algorithm from the chapter, but I end up of 0x57 instead of 0x70. Below is the line-by-line computation and it seems correct for 0x85 poly, but no luck.

F7 01 00 00 07 44 30 70
There are two problems there TickTock. First you transcribed the data wrong. That 01 should be 07. i.e.
F7 07 00 00 07 44 30 70
And second, you have to do 8 more turns of the crank after the data runs out, shifting in zeros, to flush it through.
 
weber said:
TickTock said:
FWIW, I cannot seem to make it work either. The above code does match the "SIMPLE" algorithm from the chapter, but I end up of 0x57 instead of 0x70. Below is the line-by-line computation and it seems correct for 0x85 poly, but no luck.

F7 01 00 00 07 44 30 70
There are two problems there TickTock. First you transcribed the data wrong. That 01 should be 07. i.e.
F7 07 00 00 07 44 30 70
And second, you have to do 8 more turns of the crank after the data runs out, shifting in zeros, to flush it through.

Gah! You know I tried turning the crank 8 more time, reversing the bits, and a bunch of other stuff. Even checked the data (but apparently not close enough). Thanks - that did it. I checked this as well as the other 7 examples in this thread and the CRC matches for all. Cool!

I did it in excel with the following line copied in one column (C) and the data in another column (labeled data).
Cell C11: =IF(C10>127,bitxor(bitand(C10*2,255)+data,poly),bitand(C10*2,255)+data)

I uploaded the spreadsheet here if anyone want to look at it. You have to enable macros for the bitwise functions to work. Change the number in cell H2 to point to the different examples on the examples tab.
 
I've written a quick and dirty C# app that can correctly implement the algorithm that was posted in this forum and in the recently posted spreadsheet. It can be downloaded at: http://www.kkmfg.com/CRC_Nissan.zip there is an executable in there as well as the source code.

I will upload it to my github account as well. I'm Collin80 on github: https://github.com/collin80 I don' t have a Nissan Leaf but I am heavily into canbus and reverse engineering so there should still be relevant stuff there.

You can enter in the CRC polynomial and the first 7 bytes and it will return the correct CRC byte. I have tested this on the examples from the spreadsheet.
 
JeremyW said:
Weber and all, you might wanna take a look at EV can messages 1DB, 1DC, and 55B to see if 0x85 works for those too. The last byte looks like a CRC (values all over the place).

CRC stuff isn't clicking for me yet. I'll have to read that paper for like the 3rd or 4th time. :?

The posted security byte algorithm does in fact work for very many of the other frames. I think I only found a couple that didn't match and they probably were non-command frames that they didn't bother to secure.

1DA works
1DB works
1DC works
11A works
1D4 works
1F2 does not check out
284 does not check out
55B works

So, mostly things that work out show up as having a possible CRC in the last byte in the spreadsheet. The odd one out is 1DA which someone guessed to have phase current in that byte. It doesn't - it's a security byte.
 
Hmm. I was confident that 1DA byte 7 had the phase currents with the mux on byte 6. The signals were periodic in nature and were steady when stationary.

11F is VCM state and has at least command charge power on it (for 2012's). Considering pumping in extra amps doesn't seem to bother things, not surprised this isn't secured.

284 is a relay via the VCM from the ABS module (which handles wheel speeds). It's for the shift selector so that it can decide whether to allow parking or change of direction. It would be really bad to fake this and well shift into reverse or park at speed. Not sure if everything else would comply or not. Wouldn't test it! :shock:
 
camasleaf said:
If we have enough information to make an inverter go to regen mode, then one could buy a used Leaf motor and inverter drive it with an ICE engine to quick charge at up to 30kW.

Why do that when you can just bolt it to the back wheels of a LEAF and then drive infinitely?!!!

::insert troll physics image of your choice::
 
If you guys get this working I have another request... Aside from upping the off-the-line torque, I'd like to have the B-mode feel of 2011/12 Eco mode without making the accelerator sluggish. Ie, I'd like to UN-map the the eco sluggishness accelerator pedal remapping while keeping the extra regen!
 
I checked all the packets I have logged on EV-CAN to identify valid 0x85 checksum bytes and agree that Collin has already found all the 8-byte ones (11A 1D4 1DA 1DB 1DC 55B), but there is also one shorter packet, 0x50C of length 6, which also seem to consistently have a valid checksum in the last byte. I don't have any idea what this message is for, and it's unexcitingly repetitive: byte 3 cycles 0, 1, 2, 3 every 4 packets and byte 4 toggles between 5D and B2 every 5 packets (almost inverted bits %01011101 and %10110010, except for bit 4 which is always 1).

I also spotted one packet of length 7 on the CAR-CAN which has a valid checksum in the last byte: 0x1CB - which makes sense if it is indeed commanded brake/regen as TickTock's spreadsheet suggests.
 
Sorry to resurrect this thread but I'm interested in the leaf CANBUS crc and security type code formulae.
I'm doing a similar project on another system and some sample repeating CANBUS data is below.

I'm struggling to work out how the two bytes at the end of the packet are generated.
I assume the last is a CRC and the penultimate one some sort of security check as it is may not be data related.
Any leads would be appreciated. Sadly polynomials is not my strong point.

ID,D1,D2,D3,D4,D5,D6,D7,D8
203,0,0,0,0,0,0,0,9A
203,0,0,0,0,0,0,20,2F
203,0,0,0,0,0,0,1D,7E
203,0,0,0,0,0,0,0,8B
203,0,0,0,0,0,0,20,10
203,0,0,0,0,0,0,1D,6F
203,0,0,0,0,0,0,0,B8
203,0,0,0,0,0,0,20,1
203,0,0,0,0,0,0,1D,50
203,0,0,0,0,0,0,0,A9
203,0,0,0,0,0,0,20,3E
203,0,0,0,0,0,0,1D,41
203,0,0,0,0,0,0,0,9A
203,0,0,0,0,0,0,20,2F
203,0,0,0,0,0,0,1D,7E
203,0,0,0,0,0,0,0,8B
203,0,0,0,0,0,0,20,10
203,0,0,0,0,0,0,1D,6F
203,0,0,0,0,0,0,0,B8
203,0,0,0,0,0,0,20,1
203,0,0,0,0,0,0,1D,50
203,0,0,0,0,0,0,0,A9
203,0,0,0,0,0,0,20,3E
203,0,0,0,0,0,0,1D,41
203,0,0,0,0,0,0,0,9A
203,0,0,0,0,0,0,20,2F
203,0,0,0,0,0,0,1D,7E
203,0,0,0,0,0,0,0,8B
203,0,0,0,0,0,0,20,10
203,0,0,0,0,0,0,1D,6F
203,0,0,0,0,0,0,0,B8
203,0,0,0,0,0,0,20,1
203,0,0,0,0,0,0,1D,50
203,0,0,0,0,0,0,0,A9
203,0,0,0,0,0,0,20,3E
203,0,0,0,0,0,0,1D,41
203,0,0,0,0,0,0,0,9A
203,0,0,0,0,0,0,20,2F
203,0,0,0,0,0,0,1D,7E
203,0,0,0,0,0,0,0,8B
203,0,0,0,0,0,0,20,10

and a bit more with some data.

203,24,62,46,24,72,47,20,3C
203,24,92,46,24,72,47,2B,4D
203,24,A2,4B,0,0,0,0,99
203,24,62,46,24,72,46,20,2E
203,24,92,46,24,72,46,2B,7B
203,24,92,4D,0,0,0,0,89
203,24,42,44,24,72,44,20,15
203,24,92,44,24,72,4A,2B,6A
203,24,C2,4C,0,0,0,0,B4
203,24,22,42,24,62,3F,20,1
203,24,92,42,24,62,4F,2B,59
203,25,32,4B,0,0,0,0,AE
 
Hi retepsnikrep. The fact that the last 16 bit number cycles through 12 different values while the preceding 6 bytes remain all zero, means that it is not simply a CRC. Nor can the last 8 bit number be a CRC since we see four different values when the 7 preceding bytes are all zero. So I'm sorry, I don't know what's going on there.
 
Ok a slightly different problem.
The canbus sends me the following hex data from a battery management system.

Just to summarise the 12 bit hex voltage data obtained from the 10 cell Lithium BMS. I have grouped it as required. The test voltages I used are not 100% accurate but should be pretty near the number quoted below. e.g. 4v quoted could be 3.95 or 4.05 etc etc

4v per cell
9DD,9E2,9DF,9E2
9E4,9E3,9E5,9E7
9F0,9EA

3v per cell
756,75C,75A,75C
75C,75E,760,761
76B,764,

2v per cell
4D2,4D2,4D2,4D4
4D7,4D2,4D4,4D3
4D7,4D6

1v per cell
246 246 247 246
249 246 247 246
249 24D

0V = 0v per cell (Some leakage and noise here)
0D0,0D4,0C1,0E2
0DE,0E6,0FC,0E3
0C5,0E7

So the question.. How do we get the cell voltage from the above hex data?
Formulae..
 
Ignore the zero volt readings. It looks like it can't measure anything below about 0.4. V. Average the readings for each of the other voltages and fit a straight line to it. In decimal, it looks like approximately voltage = (reading + 64) / 648.
 
Back
Top