Help with a Simple (Hopefully) CCS Charge Curve? Topic is solved

Development and discussion of fast charging systems eg Chademo , CCS etc
P.S.Mangelsdorf
Posts: 1081
Joined: Tue Sep 17, 2019 8:33 pm
Location: Raleigh, NC, USA
Has thanked: 235 times
Been thanked: 278 times

Re: Help with a Simple (Hopefully) CCS Charge Curve?

Post by P.S.Mangelsdorf »

Yes, below is the current version. This has only been tested on a 100kW EVGo charger (built by Delta) but I see no reason it wouldn't work elsewhere. Hoping to do more testing soon. There is still a bit of bouncing around at the transition points, but I've even seen my production EV do that. Overall, I'm happy with this version.

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
}
If at first you don't succeed, buy a bigger hammer.

1940 Chevrolet w/ Tesla LDU - "Shocking Chevy" - Completed Hot Rod Drag Week 2023 and 2024

https://www.youtube.com/@MangelsdorfSpeed
Post Reply