Battery-Emulator/Software/src/inverter/SMA-CAN.cpp
2024-08-21 22:31:48 +03:00

266 lines
11 KiB
C++

#include "../include.h"
#ifdef SMA_CAN
#include "../datalayer/datalayer.h"
#include "SMA-CAN.h"
/* TODO: Map error bits in 0x158 */
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis60s = 0;
//Actual content messages
CAN_frame SMA_558 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x558,
.data = {0x03, 0x12, 0x00, 0x04, 0x00, 0x59, 0x07, 0x07}}; //7x BYD modules, Vendor ID 7 BYD
CAN_frame SMA_598 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x598,
.data = {0x00, 0x00, 0x12, 0x34, 0x5A, 0xDE, 0x07, 0x4F}}; //B0-4 Serial, rest unknown
CAN_frame SMA_5D8 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x5D8,
.data = {0x00, 0x42, 0x59, 0x44, 0x00, 0x00, 0x00, 0x00}}; //B Y D
CAN_frame SMA_618_1 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x618,
.data = {0x00, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79}}; //0 B A T T E R Y
CAN_frame SMA_618_2 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x618,
.data = {0x01, 0x2D, 0x42, 0x6F, 0x78, 0x20, 0x48, 0x39}}; //1 - B O X H
CAN_frame SMA_618_3 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x618,
.data = {0x02, 0x2E, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00}}; //2 - 0
CAN_frame SMA_358 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x358,
.data = {0x0F, 0x6C, 0x06, 0x20, 0x00, 0x00, 0x00, 0x00}};
CAN_frame SMA_3D8 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x3D8,
.data = {0x04, 0x10, 0x27, 0x10, 0x00, 0x18, 0xF9, 0x00}};
CAN_frame SMA_458 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x458,
.data = {0x00, 0x00, 0x06, 0x75, 0x00, 0x00, 0x05, 0xD6}};
CAN_frame SMA_518 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x518,
.data = {0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}};
CAN_frame SMA_4D8 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x4D8,
.data = {0x09, 0xFD, 0x00, 0x00, 0x00, 0xA8, 0x02, 0x08}};
CAN_frame SMA_158 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x158,
.data = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x6A, 0xAA, 0xAA}};
static int16_t discharge_current = 0;
static int16_t charge_current = 0;
static int16_t temperature_average = 0;
static uint16_t ampere_hours_remaining = 0;
void update_values_can_inverter() { //This function maps all the values fetched from battery CAN to the correct CAN messages
//Calculate values
if (datalayer.battery.status.voltage_dV > 10) { // Only update value when we have voltage available to avoid div0
discharge_current =
((datalayer.battery.status.max_discharge_power_W * 10) /
datalayer.battery.status.voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
discharge_current = (discharge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A)
charge_current =
((datalayer.battery.status.max_charge_power_W * 10) /
datalayer.battery.status.voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
charge_current = (charge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A)
}
if (charge_current > datalayer.battery.info.max_charge_amp_dA) {
charge_current =
datalayer.battery.info
.max_charge_amp_dA; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
}
if (discharge_current > datalayer.battery.info.max_discharge_amp_dA) {
discharge_current =
datalayer.battery.info
.max_discharge_amp_dA; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
}
temperature_average =
((datalayer.battery.status.temperature_max_dC + datalayer.battery.status.temperature_min_dC) / 2);
if (datalayer.battery.status.voltage_dV > 10) { // Only update value when we have voltage available to avoid div0
ampere_hours_remaining = ((datalayer.battery.status.remaining_capacity_Wh / datalayer.battery.status.voltage_dV) *
100); //(WH[10000] * V+1[3600])*100 = 270 (27.0Ah)
}
//Map values to CAN messages
//Maxvoltage (eg 400.0V = 4000 , 16bits long)
SMA_358.data.u8[0] = (datalayer.battery.info.max_design_voltage_dV >> 8);
SMA_358.data.u8[1] = (datalayer.battery.info.max_design_voltage_dV & 0x00FF);
//Minvoltage (eg 300.0V = 3000 , 16bits long)
SMA_358.data.u8[2] = (datalayer.battery.info.min_design_voltage_dV >>
8); //Minvoltage behaves strange on SMA, cuts out at 56% of the set value?
SMA_358.data.u8[3] = (datalayer.battery.info.min_design_voltage_dV & 0x00FF);
//Discharge limited current, 500 = 50A, (0.1, A)
SMA_358.data.u8[4] = (discharge_current >> 8);
SMA_358.data.u8[5] = (discharge_current & 0x00FF);
//Charge limited current, 125 =12.5A (0.1, A)
SMA_358.data.u8[6] = (charge_current >> 8);
SMA_358.data.u8[7] = (charge_current & 0x00FF);
//SOC (100.00%)
SMA_3D8.data.u8[0] = (datalayer.battery.status.reported_soc >> 8);
SMA_3D8.data.u8[1] = (datalayer.battery.status.reported_soc & 0x00FF);
//StateOfHealth (100.00%)
SMA_3D8.data.u8[2] = (datalayer.battery.status.soh_pptt >> 8);
SMA_3D8.data.u8[3] = (datalayer.battery.status.soh_pptt & 0x00FF);
//State of charge (AH, 0.1)
SMA_3D8.data.u8[4] = (ampere_hours_remaining >> 8);
SMA_3D8.data.u8[5] = (ampere_hours_remaining & 0x00FF);
//Voltage (370.0)
SMA_4D8.data.u8[0] = (datalayer.battery.status.voltage_dV >> 8);
SMA_4D8.data.u8[1] = (datalayer.battery.status.voltage_dV & 0x00FF);
//Current (TODO: signed OK?)
SMA_4D8.data.u8[2] = (datalayer.battery.status.current_dA >> 8);
SMA_4D8.data.u8[3] = (datalayer.battery.status.current_dA & 0x00FF);
//Temperature average
SMA_4D8.data.u8[4] = (temperature_average >> 8);
SMA_4D8.data.u8[5] = (temperature_average & 0x00FF);
//Battery ready
if (datalayer.battery.status.bms_status == ACTIVE) {
SMA_4D8.data.u8[6] = READY_STATE;
} else {
SMA_4D8.data.u8[6] = STOP_STATE;
}
//Error bits
if (!datalayer.system.status.inverter_allows_contactor_closing) {
SMA_158.data.u8[2] = 0x6A;
} else {
SMA_158.data.u8[2] = 0xAA;
}
/*
//SMA_158.data.u8[0] = //bit12 Fault high temperature, bit34Battery cellundervoltage, bit56 Battery cell overvoltage, bit78 batterysystemdefect
//TODO: add all error bits. Sending message with all 0xAA until that.
0x158 can be used to send error messages or warnings.
Each message is defined of two bits:
01=message triggered
10=no message triggered
0xA9=10101001, triggers first message
0xA6=10100110, triggers second message
0x9A=10011010, triggers third message
0x6A=01101010, triggers forth message
bX defines the byte
b0 A9 Battery system defect
b0 A6 Battery cell overvoltage fault
b0 9A Battery cell undervoltage fault
b0 6A Battery high temperature fault
b1 A9 Battery low temperature fault
b1 A6 Battery high temperature fault
b1 9A Battery low temperature fault
b1 6A Overload (reboot required)
b2 A9 Overload (reboot required)
b2 A6 Incorrect switch position for the battery disconnection point
b2 9A Battery system short circuit
b2 6A Internal battery hardware fault
b3 A9 Battery imbalancing fault
b3 A6 Battery service life expiry
b3 9A Battery system thermal management defective
b3 6A Internal battery hardware fault
b4 A9 Battery system defect (warning)
b4 A6 Battery cell overvoltage fault (warning)
b4 9A Battery cell undervoltage fault (warning)
b4 6A Battery high temperature fault (warning)
b5 A9 Battery low temperature fault (warning)
b5 A6 Battery high temperature fault (warning)
b5 9A Battery low temperature fault (warning)
b5 6A Self-diagnosis (warning)
b6 A9 Self-diagnosis (warning)
b6 A6 Incorrect switch position for the battery disconnection point (warning)
b6 9A Battery system short circuit (warning)
b6 6A Internal battery hardware fault (warning)
b7 A9 Battery imbalancing fault (warning)
b7 A6 Battery service life expiry (warning)
b7 9A Battery system thermal management defective (warning)
b7 6A Internal battery hardware fault (warning)
*/
}
void receive_can_inverter(CAN_frame rx_frame) {
switch (rx_frame.ID) {
case 0x360: //Message originating from SMA inverter - Voltage and current
//Frame0-1 Voltage
//Frame2-3 Current
break;
case 0x3E0: //Message originating from SMA inverter - ?
break;
case 0x420: //Message originating from SMA inverter - Timestamp
//Frame0-3 Timestamp
transmit_can(&SMA_158, can_config.inverter);
transmit_can(&SMA_358, can_config.inverter);
transmit_can(&SMA_3D8, can_config.inverter);
transmit_can(&SMA_458, can_config.inverter);
transmit_can(&SMA_518, can_config.inverter);
transmit_can(&SMA_4D8, can_config.inverter);
break;
case 0x5E0: //Message originating from SMA inverter - String
break;
case 0x560: //Message originating from SMA inverter - Init
break;
case 0x5E7: //Pairing request
transmit_can(&SMA_558, can_config.inverter);
transmit_can(&SMA_598, can_config.inverter);
transmit_can(&SMA_5D8, can_config.inverter);
transmit_can(&SMA_618_1, can_config.inverter);
transmit_can(&SMA_618_2, can_config.inverter);
transmit_can(&SMA_618_3, can_config.inverter);
transmit_can(&SMA_158, can_config.inverter);
transmit_can(&SMA_358, can_config.inverter);
transmit_can(&SMA_3D8, can_config.inverter);
transmit_can(&SMA_458, can_config.inverter);
transmit_can(&SMA_518, can_config.inverter);
transmit_can(&SMA_4D8, can_config.inverter);
break;
default:
break;
}
}
void send_can_inverter() {
unsigned long currentMillis = millis();
// Send CAN Message every 60s
if (currentMillis - previousMillis60s >= INTERVAL_60_S) {
previousMillis60s = currentMillis;
transmit_can(&SMA_158, can_config.inverter);
transmit_can(&SMA_358, can_config.inverter);
transmit_can(&SMA_3D8, can_config.inverter);
transmit_can(&SMA_458, can_config.inverter);
transmit_can(&SMA_518, can_config.inverter);
transmit_can(&SMA_4D8, can_config.inverter);
}
}
#endif