Converting canbus signal that spans multiple bytes but not all data from each byte

I am very new to programming and generally find examples and work from those, but I am stuck on how to convert an RPM signal from a canbus message. There are examples of this out there, but none seem to read the default message that is sent from the PCM to the instrument cluster in my case. I know the arbitration ID of the message that contains the signal I want. In this message, the signal for RPM starts at bit 28 and spans 13bits. So the info I need is in byte 3,4, and 5 of the message. I can't figure out a way to read 28-31 from byte 3, 32-39 from byte 4, and 40 from byte 5, and then read this as one 13bit number. Once this is combined and read, it will then need to be multiplied by 2, but that should be the easy part.

For example, message 0x204 C0 00 7D 00 73 00 00 00 would be- 1,840 then it would be multiplied by 2 to get the RPM of 3,680

The goal of this is to try to build a digital dash for an engine swap project where I am making the standalone harness and don't want to use the instrument panel from the donor car. I believe the vehicle speed is similar.

Thanks,
Leigh

I don't see how you get 1840 (0x0730) with the low four bits from byte 3 (0b0000) all 8 bits from byte 4 (0b01110011) and one bit from byte 5 (0b0). That comes out to 0b0000011100110 or 0x00E6 or 230. The RPM (3680) would be 0x0E60.

What WOULD work is 8 bits from byte 4 and 5 bits from byte 5.

I would put the three bytes together into one number:

  unsigned long RPM;

  RPM = data[4]; // 0x73 (Now 0x00000073)
  RPM <<= 8;
  RPM |= data[5]; // 0x00 (Now 0x00007300)
  // Now shift away the 3 extra bits from data[5]
  RPM >>= 3; // Now 0x00000E60 = 3680

If this is going to be on the road especially in the US you need to check into the regulations regarding odometer and speedometer replacement. There is a federal regulation effective in 2005 and found in 49 CFR ยง393.82 that provides a car's speedometer must be accurate to within a plus or minus 5 mph at a speed of 50 mph. NHTSA (National Highway Traffic Safety Administration) is reminding consumers that, last year starting January 1, 2021, odometer disclosures will be required for every transfer of ownership for the first 20 years, beginning with Model Year 2011 vehicles.

I see how that works and I think it could do the trick. Thank you

My understanding is that it's just merging all of those bits into one decimal number, the total of the 13 bits being 8,191 and the actual RPM would be double that number. It's a really weird way of doing it but I have checked the math on some can bus logging from a different vehicle that uses the same generation PCM and the math lines up.

Thanks for the advice, my plan is to put this engine in a 1967 vehicle, so I think whatever I end up with is going to be way more accurate than the 55 year old speedo I am taking out. Also the odometer is exempt on this vehicle, so not too worried about that. It only went up to 99K so it's probably rolled over a few times now.

Thanks.

I built a test using just a single can packet to test the decoding and just couldn't get there with bit shifting. I put together the code below to read the specific bits out of the packet and then write those bits into long value for each can bit. I then wrote those to another long value to the specific bit that I wanted. So canbit 28 goes to bit 0 of my RPM value, 29 to 1, 30 to 2, etc. The resulting number for RPM was then 672, which matched my manual conversion of the packet, I then added the factor of 2, and got the expected 1344 rpm. This is probably a terrible way of doing it, but it's the best I could come up with. I have a lot of serial prints in there for testing it out along the way. Now I will test it out by adding in the can commands and remove all the serial prints. I can play back a log file from another device to the can shield to simulate and see if I am on the right track.

 Serial.begin(115200);
    while(!Serial);
unsigned char data [8] = {0xC0, 0x00, 0x7D, 0x03, 0x2A, 0x00, 0x00, 0x00};
//unsigned long RPM
unsigned long canbit28;
unsigned long canbit29;
unsigned long canbit30;
unsigned long canbit31;
unsigned long canbit32;
unsigned long canbit33;
unsigned long canbit34;
unsigned long canbit35;
unsigned long canbit36;
unsigned long canbit37;
unsigned long canbit38;
unsigned long canbit39;
unsigned long canbit40;
unsigned long RPM = 0x0000000000000;
unsigned long factor = 2;


canbit28 = bitRead(data[3],4);
Serial.println(canbit28, BIN);
bitWrite(RPM,0,canbit28);
canbit29 = bitRead(data[3],5);
Serial.println(canbit29, BIN);
bitWrite(RPM,1,canbit29);
canbit30 = bitRead(data[3],6);
Serial.println(canbit30, BIN);
bitWrite(RPM,2,canbit30);
canbit31 = bitRead(data[3],7);
Serial.println(canbit31, BIN);
bitWrite(RPM,3,canbit31);
canbit32 = bitRead(data[4],0);
Serial.println(canbit32, BIN);
bitWrite(RPM,4,canbit32);
canbit33 = bitRead(data[4],1);
Serial.println(canbit33, BIN);
bitWrite(RPM,5,canbit33);
canbit34 = bitRead(data[4],2);
Serial.println(canbit34, BIN);
bitWrite(RPM,6,canbit34);
canbit35 = bitRead(data[4],3);
Serial.println(canbit35, BIN);
bitWrite(RPM,7,canbit35);
canbit36 = bitRead(data[4],4);
Serial.println(canbit36, BIN);
bitWrite(RPM,8,canbit36);
canbit37 = bitRead(data[4],5);
Serial.println(canbit37, BIN);
bitWrite(RPM,9,canbit37);
canbit38 = bitRead(data[4],6);
Serial.println(canbit38, BIN);
bitWrite(RPM,10,canbit38);
canbit39 = bitRead(data[4],7);
Serial.println(canbit39, BIN);
bitWrite(RPM,11,canbit39);
canbit40 = bitRead(data[5],0);
Serial.println(canbit40, BIN);
bitWrite(RPM,12,canbit40);


Serial.println(RPM, BIN);
Serial.println(RPM);
unsigned long RPMC = RPM * factor;
Serial.println(RPMC);

Simplifying a bit:

bitWrite(RPM,0,bitRead(data[3],4));
bitWrite(RPM,1,bitRead(data[3],5));
bitWrite(RPM,2,bitRead(data[3],6));
bitWrite(RPM,3,bitRead(data[3],7));

bitWrite(RPM,4,bitRead(data[4],0));
bitWrite(RPM,5,bitRead(data[4],1));
bitWrite(RPM,6,bitRead(data[4],2));
bitWrite(RPM,7,bitRead(data[4],3));
bitWrite(RPM,8,bitRead(data[4],4));
bitWrite(RPM,9,bitRead(data[4],5));
bitWrite(RPM,10,bitRead(data[4],6););
bitWrite(RPM,11,bitRead(data[4],7));

bitWrite(RPM,12,bitRead(data[5],0));

This explains some things. It looks like the bytes are in reverse order: data[3] is the Least Significant Byte and data[5] is the Most Significant Byte.

That would make "0x03, 0x2A, 0x00" into 0x002A03 and with the bottom 4 bits shifted off: 0x02A0 (672 = 1342 RPM).

  uint32_t RPM;  // Need room for 24 bits

  RPM = data[5]; // 0x00 (Now 0x00000000)
  RPM <<= 8;
  RPM |= data[4]; // 0x2A (Now 0x0000002A)
  RPM <<= 8;
  RPM |= data[3]; // 0x03 (Now 0x00002A03)
  // Now shift away the 4 extra bits from data[3]
  RPM >>= 4; // Now 0x000002A0 = 672 = 1342 RPM
  // For safety, mask off all but the bottom 13 bits
  RPM &= 0x00001FFF;
  // And multiply by 2 to get actual RPM
  RPM *= 2;

Thank you so much for the help. I replayed my log file and am still getting odd results, but I think that comes down to the mapping of the data, I think I still have something off. I am going to do some new captures and try to hold specific RPMs and much smaller capture files to try to narrow it down. The definition file I found seems to be off.

Thank you again-

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.