//
void loop() { //Main program loop
CAN_FRAME incoming; //capture can frames
if (Can0.available() > 0) { //if there is an incoming frame read it
Can0.read(incoming);
if (incoming.id == 0x373) { // the BMU sends 0x373 100 times a second
ah = incoming.data.byte[2]; //store current bytes
al = incoming.data.byte[3];
vh = incoming.data.byte[4]; //store voltage bytes
vl = incoming.data.byte[5];
if (incoming.data.byte[0] > 65 && incoming.data.byte[0] < 210) { b0 = incoming.data.byte[0]; } //update b0 to good data between 2.75 and 4.2
if (incoming.data.byte[1] > 65 && incoming.data.byte[1] < 210) { b1 = incoming.data.byte[1]; }
incoming.data.byte[0] = b0;
incoming.data.byte[1] = b1;
Can1.sendFrame(incoming); //send the last good cell voltage
// end 0x373
} else if (incoming.id == 0x374) { // the BMU sends 0x374 10 times a second, one 0x374 for every 10 0x373
volts = (vh * 256 + vl) / 880.0; //the average cell voltage from 3.05v to 4.1v. 88 is the number of cells
//in a car with omly 80 cells change 88 to 80
if (flag == 0) { //check to see if this is a new power up
if (j == 20) { //wait 20 0x374 frames until CMU is providing good data
storeSoC2(); //get battery SoC based on voltage
remAh1 = (SoC2 / 100.0) * capAh; //calculate the start remaining Ah in the battery based on voltage
remAh2 = (SoC2 / 100.0) * capAh; //calculate the start remaining Ah in the battery based on voltage
flag = 1;
}
j = j + 1;
}
if (ah > 48 && ah < 206) { //update amps to good data between -200 and 200
amps = (ah * 256 + al - 32700) / 100.0; //Use 32700 not 32768 32700 is the calibrated value
}
float Ah = amps / 36000.0; //The amphours to or from the battery since the last 0x374
//presTime = millis(); //calculate the interval between 0x373 frames for amp hour calculation
//step = (presTime - prevTime) / 1000.0 / 3600.0;
//prevTime = presTime;
if (amps > -1.0 && amps < 1.0) { //store 10ths of a second elapsed while the amps are > -1 and < 1
deciSec += 1;
} else {
deciSec = 0;
}
remAh1 = remAh1 + Ah; //update remaining amp hours based on current in or out of battery
SoC1 = 100.0 * remAh1 / capAh; //compute SoC1 based on the remaining Ah
if (deciSec > 6000) { //when battery current has been low for long enough for battery voltage to settle = 10 min
storeSoC2(); //gets SoC2 based on battery voltage
remAh2 = (SoC2 / 100.0) * capAh; //correct remaining capacity based on voltage
} else { //current has not been low, long enough to use the SoC based on voltage
remAh2 = remAh2 + Ah; //update remaining amp hours based on current in or out of battery
SoC2 = 100.0 * remAh2 / capAh; //compute SoC2 based on the remaining Ah
}
Serial.print("remAh1 = ");Serial.println(remAh1);
Serial.print("remAh2 = ");Serial.println(remAh2);
Serial.print("volts = ");Serial.println(volts);
incoming.data.byte[0] = 2 * SoC1 + 10; //modify the SoC coming from BMU
incoming.data.byte[1] = 2 * SoC2 + 10;
incoming.data.byte[6] = 2 * capAh; //modify the capacity coming from BMU
Serial.print("SoC1 = ");Serial.println(SoC1);
Serial.print("SoC2 = ");Serial.println(SoC2);
Can1.sendFrame(incoming); //send the corrected SoCs and the correct 100% capacity to the ECU
// end 0x374
} else if (incoming.id == 0x6E1) { //The BMU sends a 0x6E1 every 0.04 seconds or 25 per second. In cars with only 80 cells remember to block modules 6 og 12.
if ((incoming.data.byte[4] == 0 && incoming.data.byte[5] > 130) || (incoming.data.byte[4] == 1 && incoming.data.byte[5] < 164)) { // b4 to b7 are updated with good data between 2.75 and 4.2
b41 = incoming.data.byte[4];
b51 = incoming.data.byte[5];
b61 = incoming.data.byte[6];
b71 = incoming.data.byte[7];
}
incoming.data.byte[4] = b41;
incoming.data.byte[5] = b51;
incoming.data.byte[6] = b61;
incoming.data.byte[7] = b71;
Can1.sendFrame(incoming); //send the corrected voltages to the ECU
// end 0x6E1
} else if (incoming.id == 0x6E2) { //The BMU sends a 0x6E2 every 0.04 seconds or 25 per second In cars with only 80 cells remember to block modules 6 og 12.
if ((incoming.data.byte[4] == 0 && incoming.data.byte[5] > 130) || (incoming.data.byte[4] == 1 && incoming.data.byte[5] < 164)) { // b4 to b7 are updated with good data between 2.75 and 4.2
b42 = incoming.data.byte[4];
b52 = incoming.data.byte[5];
b62 = incoming.data.byte[6];
b72 = incoming.data.byte[7];
}
incoming.data.byte[4] = b42;
incoming.data.byte[5] = b52;
incoming.data.byte[6] = b62;
incoming.data.byte[7] = b72;
Can1.sendFrame(incoming); //send the corrected voltages to the ECU
// end 0x6E3
} else if (incoming.id == 0x6E3) { //The BMU sends a 0x6E3 every 0.04 seconds or 25 per second
if (incoming.data.byte[0] != 6 && incoming.data.byte[0] != 12) { // modules 6 and 12 only contain 4 cells not 8
if ((incoming.data.byte[4] == 0 && incoming.data.byte[5] > 130) || (incoming.data.byte[4] == 1 && incoming.data.byte[5] < 164)) { // b4 to b7 are updated with good data between 2.75 and 4.2
b43 = incoming.data.byte[4];
b53 = incoming.data.byte[5];
b63 = incoming.data.byte[6];
b73 = incoming.data.byte[7];
}
incoming.data.byte[4] = b43;
incoming.data.byte[5] = b53;
incoming.data.byte[6] = b63;
incoming.data.byte[7] = b73;
}
Can1.sendFrame(incoming); //send the corrected voltages to the ECU
// end 0x6E4
} else if (incoming.id == 0x6E4) { //The BMU sends a 0x6E4 every 0.04 seconds or 25 per second
if (incoming.data.byte[0] != 6 && incoming.data.byte[0] != 12) { // modules 6 and 12 only contain 4 cells not 8
if ((incoming.data.byte[4] == 0 && incoming.data.byte[5] > 130) || (incoming.data.byte[4] == 1 && incoming.data.byte[5] < 164)) { // b4 to b7 are updated with good data between 2.75v and 4.2v
b44 = incoming.data.byte[4];
b54 = incoming.data.byte[5];
b64 = incoming.data.byte[6];
b74 = incoming.data.byte[7];
}
incoming.data.byte[4] = b44;
incoming.data.byte[5] = b54;
incoming.data.byte[6] = b64;
incoming.data.byte[7] = b74;
}
Can1.sendFrame(incoming); //send the corrected voltages to the ECU
// end 0x6E4
} else if (incoming.id == 0x5A1) { //This hides an error bit from the CMUs. It helps when the error is due to a bad CMU. Use with caution because it can hide real problems with the cells
if (incoming.data.byte[1] == 48) { incoming.data.byte[1] = 16; }
Can1.sendFrame(incoming); //send the corrected error bit to the ECU
// end 0x5A1
} else Can1.sendFrame(incoming); //send any captured frames from BMU other than 0x373, 0x374 and 6E1-6E4 to ECU.
} // end incoming frame from the BMU
if (Can1.available() > 0) { //look for frames from ECU and forward to BMU
Can1.read(incoming);
Can0.sendFrame(incoming);
}
} //end main program loop