I will try to get this code up onto Github soon as well.
Code: Select all
#include <Arduino.h>
#include <SPI.h>
#include <mcp_canbus.h>
#define SPI_CS_PIN 9
MCP_CAN CAN(SPI_CS_PIN); // Set CS pin
#define CAN_500KBPS 16
unsigned long lastSendTime = 0;
const unsigned long sendInterval = 250; // 0.25 seconds
const int maxChargePower = 100000; // 100kW in watts
const int targetVoltage = 398; // Target voltage in volts
const int maxVoltage = 398; // Maximum voltage in volts
const int minVoltage = 288; // Minimum voltage in volts
const int RampPoint1 = 382; //Voltage at which power reduced to Ramp1per
const int RampPoint2 = 386; //Voltage at which power reduced to Ramp2per
const int RampPoint3 = 393; //Voltage at which power reduced to Ramp3per
const int RampPoint4 = 396; //Voltage at which power reduced to Ramp4per
const float Ramp1per = 0.75; //Percentage of max power for Ramp Point 1, as a decimal
const float Ramp2per = 0.50; //Percentage of max power for Ramp Point 2, as a decimal
const float Ramp3per = 0.25; //Percentage of max power for Ramp Point 3, as a decimal
const float Ramp4per = 0.15; //Percentage of max power for Ramp Point 4, as a decimal
int voltage = 0;
void setup() {
// Initialize serial communication
Serial.begin(9600);
while (CAN_OK != CAN.begin(CAN_500KBPS)) { // init can bus : baudrate = 500k
Serial.println("CAN BUS FAIL!");
delay(100);
}
Serial.println("CAN BUS OK!");
// Set mask to accept all bits
CAN.init_Mask(0, 0, 0x7FF);
CAN.init_Mask(1, 0, 0x7FF);
// Set filter for specific ID 0x522
CAN.init_Filt(0, 0, 0x522); // Filter 0 for ID 0x522
}
void loop() {
// Declare required variables
long unsigned int rxId;
unsigned char len = 0;
unsigned char rxBuf[8];
int batteryVoltage = 0;
// Check for new CAN messages
if (CAN.checkReceive() == CAN_MSGAVAIL) { // Check if data is available
CAN.readMsgBufID(&rxId, &len, rxBuf);
if (rxId == 0x522) { // Process only messages with ID 0x522
// Extract battery voltage from CAN message
long batteryVoltage = (long)((rxBuf[5] << 24) | (rxBuf[4] << 16) | (rxBuf[3] << 8) | rxBuf[2]); // Voltage in millivolts
voltage = batteryVoltage / 1000; // Convert to volts and store
}
}
if (millis() - lastSendTime >= sendInterval) {
lastSendTime = millis();
// Calculate charge current request
int chargeCurrent = calculateChargeCurrent(voltage);
// Send charge current request
sendChargeCurrentRequest(chargeCurrent);
// Send target voltage
sendTargetVoltage(targetVoltage);
// Calculate and send SOC
int soc = calculateSOC(voltage);
sendSOC(soc);
}
}
int calculateChargeCurrent(int voltage) {
int chargePower;
if (voltage < 300) {
chargePower = 20000; // 20kW
} else if (voltage < RampPoint1) {
chargePower = maxChargePower; //
} else if (voltage < RampPoint2) {
chargePower = maxChargePower * Ramp1per;
} else if (voltage < RampPoint3) {
chargePower = maxChargePower * Ramp2per;
} else if (voltage < RampPoint4) {
chargePower = maxChargePower * Ramp3per;
} else if (voltage < maxVoltage) {
chargePower = maxChargePower * Ramp4per;
} else {
chargePower = 0;
}
return chargePower / voltage; // Current in amps
}
void sendChargeCurrentRequest(int chargeCurrent) {
unsigned char data[8] = {0};
data[0] = chargeCurrent & 0xFF;
data[1] = (chargeCurrent >> 8) & 0xFF;
CAN.sendMsgBuf(0x300, 0, 8, data); //current request as CAN message 0x300
}
void sendTargetVoltage(int targetVoltage) {
unsigned char data[8] = {0};
data[0] = targetVoltage & 0xFF;
data[1] = (targetVoltage >> 8) & 0xFF;
CAN.sendMsgBuf(0x301, 0, 8, data); //Target Voltage as CAN message 0x301
}
int calculateSOC(int voltage) {
// Calculate the SOC as a percentage based on the min and max voltages
int soc = (voltage - minVoltage) * 100 / (maxVoltage - minVoltage);
// Round down to the nearest 10%
soc = (soc / 10) * 10;
// Ensure SOC is between 0 and 100
if (soc > 100) soc = 100;
if (soc < 0) soc = 0;
return soc;
}
void sendSOC(int soc) {
unsigned char data[8] = {0};
data[0] = soc & 0xFF;
data[1] = (soc >> 8) & 0xFF;
CAN.sendMsgBuf(0x305, 0, 8, data); //State of Charge as CAN message 0x305
}