// Arduino Due - Displays all traffic found on either canbus port
// By Thibaut Viard/Wilfredo Molina/Collin Kidder 2013-2014
// Modified by Paul Dove/ David Cecil to make a CAN bridge between ECU and BMU in 2012 Mitsubishi i-MiEV
// Reads all traffic on CAN0 and forwards it to CAN1 (and in the reverse direction)
// but modifies Soc1, SoC2 and Capacity values first PID 0x374 Bytes [0], [1], and [6].
// Required libraries
#include "variant.h"
#include <due_can.h>
#include <SPI.h>
//#include <SdFat.h> not used uncomment if using SD shield
//const int chipSelect = 4; //not used scared for SD card on Ethernet shield
//Leave defined if you use Due native port, comment if using programming port
#define Serial SerialUSB
//SdFat sd;//Not used at this time
//SdFile myFile;// scared in for writing to SD card using Ethernet shield
//Global variables
float volts; //Average cell voltage
byte ah; //Battery Voltage high byte PID 0x373 byte[4]
byte al; //Battery Voltage low byte PID 0x373 byte[5]
float amps; //Battery Curent
float remAh; //Capacity remaining in battery
double capAh = 90; //Battery Capacity
float SoCv; //State of Charge based on battery voltage
float SoCah; //State of Charge based on Coulomb counting
long presTime; //Time in milliseconds
float step; //Current interval
long prevTime = millis(); //time since last current calculation
int flag = 0; //power up flag
long lowMins; //Battery current at 0 timer
int j = 0; //counter for valid data
void setup() {
Serial.begin(115200); //Initialize Serial port on Arduino 115,200 Baud
// Initialize CAN0 and CAN1, Set the proper baud rates here
Can0.begin(CAN_BPS_500K);
Can1.begin(CAN_BPS_500K);
//Begin looking for data on Can0 and Can1
Can0.watchFor(); //class CANRaw instance watchfor()
Can1.watchFor(); //class CANRaw instance watchfor()
}
//subroutine to store SoC based on voltage of the battery
void storeSoC(){
uint8_t i = 0;
uint16_t Vbat = 0;
uint16_t Vsoc[] = {2687,3051,3181,3235,3269,3301,3321,3323,3327,3333,3340,3351,3364,3383,3410,3446,3473,3500,3531,3565,3601};//
Vbat = uint16_t(volts);
while((Vbat >= Vsoc[i] && Vbat <= Vsoc[i+1]) != 1){
i++;
}
if(Vbat >= Vsoc[0]){
SoCv = float(map(Vbat, Vsoc[i], Vsoc[i+1], i*5, (i+1)*5));
}else{
SoCv = 0;
}
i = 0;
}
//Main program loop
void 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){ //save BMU reported voltage and current in variable volts
byte vh = incoming.data.byte[4];
byte vl = incoming.data.byte[5];
volts = (vh * 256 + vl);//4.1
if (flag == 0){ //check to see if this is a new power up
if (j == 20){ //wait 20 frames until CMU is providing good data
storeSoC(); //get battery SoC based on voltage
remAh = (SoCv/100)*capAh; //calculate the capacity remaining in the battery based on voltage
//Serial.print("remAh1 = "); Serial.println(remAh);
//Serial.print("SoCv = ");Serial.println(SoCv);
flag = 1;
} j=j+1;
}
byte ah = incoming.data.byte[2]; //store current bytes and calculate battery current
byte al = incoming.data.byte[3];
amps = (ah * 256 - al -32768)/100.0;
//Serial.print ("volts = "); Serial.println (volts);
//Serial.print ("amps = "); Serial.println (amps);
presTime = millis(); //calculate the interval for amp hour calculation
step = (presTime - prevTime)/1000.0/3600.0;
prevTime = presTime;
remAh = remAh + amps*step; //update remaining amp hours based on current in or out of battery since startup
if (amps < 1 && amps > -1) { //store minutes elapsed since current was 0
lowMins += step * 60;
} else {lowMins = 0;}
//Serial.print("remAh2 ="); Serial.println(remAh);
Can1.sendFrame(incoming); //send can frame out to ECU
}
if (incoming.id == 0x374){ //capture SoC and Capacity from BMU
incoming.data.byte[6] = 0xB4; //modify capacity to new capacity of 90 Ah
if (lowMins > 3) { //when battery current has been 0 for long enough for battery voltage to settle
storeSoC(); //get SoC based on battery voltage
remAh = (SoCv/100)*capAh; //correct remaining capacity bsed on voltage
//Serial.print("remAh3 = "); Serial.println(remAh);
}
else { lowMins < 10; //wait if current has not been 0 long enough
SoCv = 100.0 * remAh / capAh;
}
//Serial.println(remAh);
//Serial.print("SoCv = ");Serial.println(SoCv);
incoming.data.byte[0] = 2 * SoCv + 10; //modify data coming from BMU
incoming.data.byte[1] = 2 * SoCv + 10;
Can1.sendFrame(incoming); //send corrected SoC based on 90 Amp hours to ECU
}
else Can1.sendFrame(incoming); //send any captured frame from BMU that was not 0x373 or 0x374 to ECU
}
if (Can1.available() > 0) { //look for messages from ECU and forward to BMU
Can1.read(incoming);
Can0.sendFrame(incoming);
}
}