From faf22e6a4c01b20cd7726018095a90879ab9d834 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 25 Nov 2024 23:05:34 +0200 Subject: [PATCH 1/5] Update SMA Tripower based on logs --- Software/Software.ino | 9 + Software/src/inverter/SMA-TRIPOWER-CAN.cpp | 371 +++++++-------------- Software/src/inverter/SMA-TRIPOWER-CAN.h | 3 + 3 files changed, 136 insertions(+), 247 deletions(-) diff --git a/Software/Software.ino b/Software/Software.ino index d88ef46bf..cd09171aa 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -545,6 +545,10 @@ void init_contactors() { pinMode(BMS_POWER, OUTPUT); digitalWrite(BMS_POWER, HIGH); #endif //HW_STARK +#ifdef SMA_TRIPOWER_CAN + //pinMode(INVERTER_CONTACTOR_ENABLE_PIN, OUTPUT); //Incase this is an output + //digitalWrite(INVERTER_CONTACTOR_ENABLE_PIN, LOW); //Incase this is an output +#endif //SMA_TRIPOWER_CAN } void init_rs485() { @@ -742,6 +746,11 @@ void handle_contactors() { #ifdef BYD_SMA datalayer.system.status.inverter_allows_contactor_closing = digitalRead(INVERTER_CONTACTOR_ENABLE_PIN); #endif +#ifdef SMA_TRIPOWER_CAN + //set(INVERTER_CONTACTOR_ENABLE_PIN, ON); //TODO: is this an input, or an output? Figure out and make logic + datalayer.system.status.inverter_allows_contactor_closing = + digitalRead(INVERTER_CONTACTOR_ENABLE_PIN); //Incase it is input +#endif #ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY handle_contactors_battery2(); diff --git a/Software/src/inverter/SMA-TRIPOWER-CAN.cpp b/Software/src/inverter/SMA-TRIPOWER-CAN.cpp index 7a242daee..f6c9ea054 100644 --- a/Software/src/inverter/SMA-TRIPOWER-CAN.cpp +++ b/Software/src/inverter/SMA-TRIPOWER-CAN.cpp @@ -13,276 +13,145 @@ /* Do not change code below unless you are sure what you are doing */ static unsigned long previousMillis500ms = 0; // will store last time a 100ms CAN Message was send +static unsigned long previousMillis2s = 0; // will store last time a 2s CAN Message was send +static unsigned long previousMillis10s = 0; // will store last time a 10s CAN Message was send + +static uint32_t inverter_time = 0; +static uint16_t inverter_voltage = 0; +static int16_t inverter_current = 0; +static bool pairing_completed = false; //Actual content messages -CAN_frame SMA_00D = {.FD = false, - .ext_ID = false, - .DLC = 8, - .ID = 0x00D, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame SMA_00F = {.FD = false, - .ext_ID = false, - .DLC = 8, - .ID = 0x00F, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame SMA_011 = {.FD = false, - .ext_ID = false, - .DLC = 8, - .ID = 0x011, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame SMA_013 = {.FD = false, - .ext_ID = false, - .DLC = 8, - .ID = 0x013, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame SMA_014 = {.FD = false, +CAN_frame SMA_558 = {.FD = false, //Pairing first message .ext_ID = false, .DLC = 8, - .ID = 0x014, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame SMA_005 = {.FD = false, + .ID = 0x558, // BYD HVS 10.2 kWh (0x66 might be kWh) + .data = {0x03, 0x24, 0x00, 0x04, 0x00, 0x66, 0x04, 0x09}}; //Amount of modules? Vendor ID? +CAN_frame SMA_598 = {.FD = false, .ext_ID = false, .DLC = 8, - .ID = 0x005, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame SMA_007 = {.FD = false, + .ID = 0x598, + .data = {0x12, 0x30, 0x8A, 0x5B, 0x00, 0x00, 0x00, 0x00}}; //B0-4 Serial? +CAN_frame SMA_5D8 = {.FD = false, .ext_ID = false, .DLC = 8, - .ID = 0x007, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame SMA_006 = {.FD = false, + .ID = 0x5D8, + .data = {0x00, 0x42, 0x59, 0x44, 0x00, 0x00, 0x00, 0x00}}; //B Y D +CAN_frame SMA_618_0 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x618, + .data = {0x00, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79}}; //BATTERY +CAN_frame SMA_618_1 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x618, + .data = {0x01, 0x2D, 0x42, 0x6F, 0x78, 0x20, 0x50, 0x72}}; //-Box Pr +CAN_frame SMA_618_2 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x618, + .data = {0x02, 0x65, 0x6D, 0x69, 0x75, 0x6D, 0x20, 0x48}}; //emium H +CAN_frame SMA_618_3 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x618, + .data = {0x03, 0x56, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00}}; //VS +CAN_frame SMA_358 = {.FD = false, .ext_ID = false, .DLC = 8, - .ID = 0x006, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame SMA_008 = {.FD = false, + .ID = 0x358, + .data = {0x12, 0x40, 0x0C, 0x80, 0x01, 0x00, 0x01, 0x00}}; +CAN_frame SMA_3D8 = {.FD = false, .ext_ID = false, .DLC = 8, - .ID = 0x008, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame SMA_015 = {.FD = false, + .ID = 0x3D8, + .data = {0x04, 0x06, 0x27, 0x10, 0x00, 0x19, 0x00, 0xFA}}; +CAN_frame SMA_458 = {.FD = false, .ext_ID = false, .DLC = 8, - .ID = 0x015, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame SMA_016 = {.FD = false, + .ID = 0x458, + .data = {0x00, 0x00, 0x73, 0xAE, 0x00, 0x00, 0x64, 0x64}}; +CAN_frame SMA_4D8 = {.FD = false, .ext_ID = false, .DLC = 8, - .ID = 0x016, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame SMA_017 = {.FD = false, + .ID = 0x4D8, + .data = {0x10, 0x62, 0x00, 0x00, 0x00, 0x78, 0x02, 0x08}}; +CAN_frame SMA_518 = {.FD = false, .ext_ID = false, .DLC = 8, - .ID = 0x017, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame SMA_018 = {.FD = false, - .ext_ID = false, - .DLC = 8, - .ID = 0x018, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static int16_t temperature_average = 0; -static uint16_t ampere_hours_remaining = 0; -static uint16_t ampere_hours_max = 0; -static bool batteryAlarm = false; -static bool BMSevent = false; - -enum BatteryState { NA, INIT, BAT_STANDBY, OPERATE, WARNING, FAULTED, UPDATE, BAT_UPDATE }; -BatteryState batteryState = OPERATE; -enum InverterControlFlags { - EMG_CHARGE_REQUEST, - EMG_DISCHARGE_REQUEST, - NOT_ENOUGH_ENERGY_FOR_START, - INVERTER_STAY_ON, - FORCED_BATTERY_SHUTDOWN, - RESERVED, - BATTERY_UPDATE_AVAILABLE, - NO_BATTERY_UPDATED_BY_INV -}; -InverterControlFlags inverterControlFlags = BATTERY_UPDATE_AVAILABLE; -enum Events0 { - START_SOC_CALIBRATE, - STOP_SOC_CALIBRATE, - START_POWERLIMIT, - STOP_POWERLIMIT, - PREVENTATIVE_BAT_SHUTDOWN, - THERMAL_MANAGEMENT, - START_BALANCING, - STOP_BALANCING -}; -Events0 events0 = START_BALANCING; -enum Events1 { START_BATTERY_SELFTEST, STOP_BATTERY_SELFTEST }; -Events1 events1 = START_BATTERY_SELFTEST; -enum Command2Battery { IDLE, RUN, NOT_USED1, NOT_USED2, SHUTDOWN, FIRMWARE_UPDATE, BATSELFUPDATE, NOT_USED3 }; -Command2Battery command2Battery = RUN; -enum InvInitState { SYSTEM_FREQUENCY, XPHASE_SYSTEM, BLACKSTART_OPERATION }; -InvInitState invInitState = SYSTEM_FREQUENCY; + .ID = 0x518, + .data = {0x00, 0x96, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00}}; void update_values_can_inverter() { //This function maps all the values fetched from battery CAN to the inverter CAN - //Calculate values - - temperature_average = - ((datalayer.battery.status.temperature_max_dC + datalayer.battery.status.temperature_min_dC) / 2); - - ampere_hours_remaining = - ((datalayer.battery.status.reported_remaining_capacity_Wh / datalayer.battery.status.voltage_dV) * - 100); //(WH[10000] * V+1[3600])*100 = 270 (27.0Ah) - ampere_hours_max = ((datalayer.battery.info.total_capacity_Wh / datalayer.battery.status.voltage_dV) * - 100); //(WH[10000] * V+1[3600])*100 = 270 (27.0Ah) - - batteryState = OPERATE; - inverterControlFlags = INVERTER_STAY_ON; //Map values to CAN messages - // Battery Limits - //Battery Max Charge Voltage (eg 400.0V = 4000 , 16bits long) - SMA_00D.data.u8[0] = (datalayer.battery.info.max_design_voltage_dV >> 8); - SMA_00D.data.u8[1] = (datalayer.battery.info.max_design_voltage_dV & 0x00FF); - //Battery Min Discharge Voltage (eg 300.0V = 3000 , 16bits long) - SMA_00D.data.u8[2] = (datalayer.battery.info.min_design_voltage_dV >> 8); - SMA_00D.data.u8[3] = (datalayer.battery.info.min_design_voltage_dV & 0x00FF); + + //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); + SMA_358.data.u8[3] = (datalayer.battery.info.min_design_voltage_dV & 0x00FF); //Discharge limited current, 500 = 50A, (0.1, A) - SMA_00D.data.u8[4] = (datalayer.battery.status.max_discharge_current_dA >> 8); - SMA_00D.data.u8[5] = (datalayer.battery.status.max_discharge_current_dA & 0x00FF); + SMA_358.data.u8[4] = (datalayer.battery.status.max_discharge_current_dA >> 8); + SMA_358.data.u8[5] = (datalayer.battery.status.max_discharge_current_dA & 0x00FF); //Charge limited current, 125 =12.5A (0.1, A) - SMA_00D.data.u8[6] = (datalayer.battery.status.max_charge_current_dA >> 8); - SMA_00D.data.u8[7] = (datalayer.battery.status.max_charge_current_dA & 0x00FF); + SMA_358.data.u8[6] = (datalayer.battery.status.max_charge_current_dA >> 8); + SMA_358.data.u8[7] = (datalayer.battery.status.max_charge_current_dA & 0x00FF); - // Battery State //SOC (100.00%) - SMA_00F.data.u8[0] = (datalayer.battery.status.reported_soc >> 8); - SMA_00F.data.u8[1] = (datalayer.battery.status.reported_soc & 0x00FF); + 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_00F.data.u8[2] = (datalayer.battery.status.soh_pptt >> 8); - SMA_00F.data.u8[3] = (datalayer.battery.status.soh_pptt & 0x00FF); + 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_00F.data.u8[4] = (ampere_hours_remaining >> 8); - SMA_00F.data.u8[5] = (ampere_hours_remaining & 0x00FF); - //Fully charged (AH, 0.1) - SMA_00F.data.u8[6] = (ampere_hours_max >> 8); - SMA_00F.data.u8[7] = (ampere_hours_max & 0x00FF); - - // Battery Energy - //Charged Energy Counter TODO: are these needed? - //SMA_011.data.u8[0] = (X >> 8); - //SMA_011.data.u8[1] = (X & 0x00FF); - //SMA_011.data.u8[2] = (X >> 8); - //SMA_011.data.u8[3] = (X & 0x00FF); - //Discharged Energy Counter TODO: are these needed? - //SMA_011.data.u8[4] = (X >> 8); - //SMA_011.data.u8[5] = (X & 0x00FF); - //SMA_011.data.u8[6] = (X >> 8); - //SMA_011.data.u8[7] = (X & 0x00FF); + SMA_3D8.data.u8[4] = (ampere_hours_remaining >> 8); + SMA_3D8.data.u8[5] = (ampere_hours_remaining & 0x00FF); - // Battery Measurements //Voltage (370.0) - SMA_013.data.u8[0] = (datalayer.battery.status.voltage_dV >> 8); - SMA_013.data.u8[1] = (datalayer.battery.status.voltage_dV & 0x00FF); + 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_013.data.u8[2] = (datalayer.battery.status.current_dA >> 8); - SMA_013.data.u8[3] = (datalayer.battery.status.current_dA & 0x00FF); + 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_013.data.u8[4] = (temperature_average >> 8); - SMA_013.data.u8[5] = (temperature_average & 0x00FF); - //Battery state - SMA_013.data.u8[6] = batteryState; - SMA_013.data.u8[6] = inverterControlFlags; - - // Battery Temperature and Cellvoltages - // Battery max temperature - SMA_014.data.u8[0] = (datalayer.battery.status.temperature_max_dC >> 8); - SMA_014.data.u8[1] = (datalayer.battery.status.temperature_max_dC & 0x00FF); - // Battery min temperature - SMA_014.data.u8[2] = (datalayer.battery.status.temperature_min_dC >> 8); - SMA_014.data.u8[3] = (datalayer.battery.status.temperature_min_dC & 0x00FF); - // Battery Cell Voltage (sum) - //SMA_014.data.u8[4] = (??? >> 8); //TODO scaling? - //SMA_014.data.u8[5] = (??? & 0x00FF); //TODO scaling? - // Cell voltage min - //SMA_014.data.u8[6] = (??? >> 8); //TODO scaling? 0-255 - // Cell voltage max - //SMA_014.data.u8[7] = (??? >> 8); //TODO scaling? 0-255 - - //SMA_006.data.u8[0] = (ErrorCode >> 8); - //SMA_006.data.u8[1] = (ErrorCode & 0x00FF); - //SMA_006.data.u8[2] = ModuleNumber; - //SMA_006.data.u8[3] = ErrorLevel; - //SMA_008.data.u8[0] = Events0; - //SMA_008.data.u8[1] = Events1; - - //SMA_005.data.u8[0] = BMSalarms0; - //SMA_005.data.u8[1] = BMSalarms1; - //SMA_005.data.u8[2] = BMSalarms2; - //SMA_005.data.u8[3] = BMSalarms3; - //SMA_005.data.u8[4] = BMSalarms4; - //SMA_005.data.u8[5] = BMSalarms5; - //SMA_005.data.u8[6] = BMSalarms6; - //SMA_005.data.u8[7] = BMSalarms7; - - //SMA_007.data.u8[0] = DCDCalarms0; - //SMA_007.data.u8[1] = DCDCalarms1; - //SMA_007.data.u8[2] = DCDCalarms2; - //SMA_007.data.u8[3] = DCDCalarms3; - //SMA_007.data.u8[4] = DCDCwarnings0; - //SMA_007.data.u8[5] = DCDCwarnings1; - //SMA_007.data.u8[6] = DCDCwarnings2; - //SMA_007.data.u8[7] = DCDCwarnings3; - - //SMA_015.data.u8[0] = BatterySystemVersion; - //SMA_015.data.u8[1] = BatterySystemVersion; - //SMA_015.data.u8[2] = BatterySystemVersion; - //SMA_015.data.u8[3] = BatterySystemVersion; - //SMA_015.data.u8[4] = BatteryCapacity; - //SMA_015.data.u8[5] = BatteryCapacity; - //SMA_015.data.u8[6] = NumberOfModules; - //SMA_015.data.u8[7] = BatteryManufacturerID; - - //SMA_016.data.u8[0] = SerialNumber; - //SMA_016.data.u8[1] = SerialNumber; - //SMA_016.data.u8[2] = SerialNumber; - //SMA_016.data.u8[3] = SerialNumber; - //SMA_016.data.u8[4] = ManufacturingDate; - //SMA_016.data.u8[5] = ManufacturingDate; - //SMA_016.data.u8[6] = ManufacturingDate; - //SMA_016.data.u8[7] = ManufacturingDate; - - //SMA_017.data.u8[0] = Multiplex; - //SMA_017.data.u8[1] = ManufacturerName; - //SMA_017.data.u8[2] = ManufacturerName; - //SMA_017.data.u8[3] = ManufacturerName; - //SMA_017.data.u8[4] = ManufacturerName; - //SMA_017.data.u8[5] = ManufacturerName; - //SMA_017.data.u8[6] = ManufacturerName; - //SMA_017.data.u8[7] = ManufacturerName; - - //SMA_018.data.u8[0] = Multiplex; - //SMA_018.data.u8[1] = BatteryName; - //SMA_018.data.u8[2] = BatteryName; - //SMA_018.data.u8[3] = BatteryName; - //SMA_018.data.u8[4] = BatteryName; - //SMA_018.data.u8[5] = BatteryName; - //SMA_018.data.u8[6] = BatteryName; - //SMA_018.data.u8[7] = BatteryName; + SMA_4D8.data.u8[4] = (temperature_average >> 8); + SMA_4D8.data.u8[5] = (temperature_average & 0x00FF); + //Battery ready + if (datalayer.battery.status.bms_status == FAULT) { + SMA_4D8.data.u8[6] = STOP_STATE; + } else { + SMA_4D8.data.u8[6] = READY_STATE; + } } void receive_can_inverter(CAN_frame rx_frame) { switch (rx_frame.ID) { - case 0x00D: //Inverter Measurements + case 0x360: //Message originating from SMA inverter - Voltage and current datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; + inverter_voltage = (rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]; + inverter_current = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; break; - case 0x00F: //Inverter Feedback + case 0x3E0: //Message originating from SMA inverter - ? datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; break; - case 0x010: //Time from inverter + case 0x420: //Message originating from SMA inverter - Timestamp datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; + inverter_time = + (rx_frame.data.u8[0] << 24) | (rx_frame.data.u8[1] << 16) | (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; break; - case 0x015: //Initialization message from inverter + case 0x560: //Message originating from SMA inverter - Init datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; - send_tripower_init(); break; - case 0x017: //Initialization message from inverter 2 + case 0x5E0: //Message originating from SMA inverter - String + datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; + //Inverter brand (frame1-3 = 0x53 0x4D 0x41) = SMA + break; + case 0x660: //Message originating from SMA inverter - Pairing request datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; - //send_tripower_init(); + send_tripower_init(); break; default: break; @@ -292,33 +161,41 @@ void receive_can_inverter(CAN_frame rx_frame) { void send_can_inverter() { unsigned long currentMillis = millis(); - // Send CAN Message every 500ms - if (currentMillis - previousMillis500ms >= INTERVAL_500_MS) { - previousMillis500ms = currentMillis; + //TODO, should we break before pairing_completed? Or rely on Enable line? - transmit_can(&SMA_00D, can_config.inverter); //Battery limits - transmit_can(&SMA_00F, can_config.inverter); // Battery state - transmit_can(&SMA_011, can_config.inverter); // Battery Energy - transmit_can(&SMA_013, can_config.inverter); // Battery Measurements - transmit_can(&SMA_014, can_config.inverter); // Battery Temperatures and cellvoltages + // Send CAN Message every 2s + if (currentMillis - previousMillis2s >= INTERVAL_2_S) { + previousMillis2s = currentMillis; + transmit_can(&SMA_358, can_config.inverter); } - - if (batteryAlarm) { //Non-cyclic - transmit_can(&SMA_005, can_config.inverter); // Battery Alarms 1 - transmit_can(&SMA_007, can_config.inverter); // Battery Alarms 2 - } - - if (BMSevent) { //Non-cyclic - transmit_can(&SMA_006, can_config.inverter); // Battery Errorcode - transmit_can(&SMA_008, can_config.inverter); // Battery Events + // Send CAN Message every 10s + if (currentMillis - previousMillis10s >= INTERVAL_10_S) { + previousMillis10s = currentMillis; + transmit_can(&SMA_518, can_config.inverter); + transmit_can(&SMA_4D8, can_config.inverter); + transmit_can(&SMA_3D8, can_config.inverter); + if (pairing_completed) { + transmit_can( + &SMA_458, + can_config + .inverter); //TODO; not confirmed if battery sends. Transmission starts only after battery is paired + } } } - void send_tripower_init() { - transmit_can(&SMA_015, can_config.inverter); // Battery Data 1 - transmit_can(&SMA_016, can_config.inverter); // Battery Data 2 - transmit_can(&SMA_017, can_config.inverter); // Battery Manufacturer - transmit_can(&SMA_018, can_config.inverter); // Battery Name + transmit_can(&SMA_558, can_config.inverter); //Pairing start - Vendor + transmit_can(&SMA_598, can_config.inverter); //Serial + transmit_can(&SMA_5D8, can_config.inverter); //BYD + transmit_can(&SMA_618_0, can_config.inverter); //BATTERY + transmit_can(&SMA_618_1, can_config.inverter); //-Box Pr + transmit_can(&SMA_618_2, can_config.inverter); //emium H + transmit_can(&SMA_618_3, can_config.inverter); //VS + transmit_can(&SMA_358, can_config.inverter); + transmit_can(&SMA_3D8, can_config.inverter); + transmit_can(&SMA_458, can_config.inverter); + transmit_can(&SMA_4D8, can_config.inverter); + transmit_can(&SMA_518, can_config.inverter); + pairing_completed = true; } void setup_inverter(void) { // Performs one time setup at startup over CAN bus diff --git a/Software/src/inverter/SMA-TRIPOWER-CAN.h b/Software/src/inverter/SMA-TRIPOWER-CAN.h index 90967001f..2645efce3 100644 --- a/Software/src/inverter/SMA-TRIPOWER-CAN.h +++ b/Software/src/inverter/SMA-TRIPOWER-CAN.h @@ -4,6 +4,9 @@ #define CAN_INVERTER_SELECTED +#define READY_STATE 0x03 +#define STOP_STATE 0x02 + void send_tripower_init(); void transmit_can(CAN_frame* tx_frame, int interface); void setup_inverter(void); From ab755aa9131f16dc5e7d718c89ed0443eb4231f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 25 Nov 2024 23:17:53 +0200 Subject: [PATCH 2/5] Add missing value mappings --- Software/src/inverter/SMA-TRIPOWER-CAN.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Software/src/inverter/SMA-TRIPOWER-CAN.cpp b/Software/src/inverter/SMA-TRIPOWER-CAN.cpp index f6c9ea054..777f1f3ed 100644 --- a/Software/src/inverter/SMA-TRIPOWER-CAN.cpp +++ b/Software/src/inverter/SMA-TRIPOWER-CAN.cpp @@ -20,6 +20,8 @@ static uint32_t inverter_time = 0; static uint16_t inverter_voltage = 0; static int16_t inverter_current = 0; static bool pairing_completed = false; +static int16_t temperature_average = 0; +static uint16_t ampere_hours_remaining = 0; //Actual content messages CAN_frame SMA_558 = {.FD = false, //Pairing first message @@ -84,7 +86,15 @@ CAN_frame SMA_518 = {.FD = false, .data = {0x00, 0x96, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00}}; void update_values_can_inverter() { //This function maps all the values fetched from battery CAN to the inverter CAN + // Update 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.reported_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) From d411b0a875812da642fb23d188734d807818f8b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 1 Dec 2024 14:40:19 +0200 Subject: [PATCH 3/5] Change enable line logic --- Software/Software.ino | 11 +---------- Software/src/inverter/SMA-TRIPOWER-CAN.cpp | 5 ++++- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/Software/Software.ino b/Software/Software.ino index cd09171aa..5374625a0 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -545,10 +545,6 @@ void init_contactors() { pinMode(BMS_POWER, OUTPUT); digitalWrite(BMS_POWER, HIGH); #endif //HW_STARK -#ifdef SMA_TRIPOWER_CAN - //pinMode(INVERTER_CONTACTOR_ENABLE_PIN, OUTPUT); //Incase this is an output - //digitalWrite(INVERTER_CONTACTOR_ENABLE_PIN, LOW); //Incase this is an output -#endif //SMA_TRIPOWER_CAN } void init_rs485() { @@ -743,14 +739,9 @@ void check_interconnect_available() { #endif //DOUBLE_BATTERY void handle_contactors() { -#ifdef BYD_SMA +#if defined(BYD_SMA) || defined(SMA_TRIPOWER_CAN) datalayer.system.status.inverter_allows_contactor_closing = digitalRead(INVERTER_CONTACTOR_ENABLE_PIN); #endif -#ifdef SMA_TRIPOWER_CAN - //set(INVERTER_CONTACTOR_ENABLE_PIN, ON); //TODO: is this an input, or an output? Figure out and make logic - datalayer.system.status.inverter_allows_contactor_closing = - digitalRead(INVERTER_CONTACTOR_ENABLE_PIN); //Incase it is input -#endif #ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY handle_contactors_battery2(); diff --git a/Software/src/inverter/SMA-TRIPOWER-CAN.cpp b/Software/src/inverter/SMA-TRIPOWER-CAN.cpp index 777f1f3ed..1296cbe7d 100644 --- a/Software/src/inverter/SMA-TRIPOWER-CAN.cpp +++ b/Software/src/inverter/SMA-TRIPOWER-CAN.cpp @@ -171,7 +171,10 @@ void receive_can_inverter(CAN_frame rx_frame) { void send_can_inverter() { unsigned long currentMillis = millis(); - //TODO, should we break before pairing_completed? Or rely on Enable line? + // Send CAN Message only if we're enabled by inverter + if (!datalayer.system.status.inverter_allows_contactor_closing) { + return; + } // Send CAN Message every 2s if (currentMillis - previousMillis2s >= INTERVAL_2_S) { From c9c5e696cbf82f89c86cf8f60ea1a5c5180a7520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sat, 28 Dec 2024 14:42:44 +0300 Subject: [PATCH 4/5] Apply diff from successful testing --- Software/src/inverter/SMA-TRIPOWER-CAN.cpp | 91 ++++++++++++++++------ 1 file changed, 66 insertions(+), 25 deletions(-) diff --git a/Software/src/inverter/SMA-TRIPOWER-CAN.cpp b/Software/src/inverter/SMA-TRIPOWER-CAN.cpp index 1296cbe7d..dac1af9c3 100644 --- a/Software/src/inverter/SMA-TRIPOWER-CAN.cpp +++ b/Software/src/inverter/SMA-TRIPOWER-CAN.cpp @@ -12,9 +12,19 @@ */ /* Do not change code below unless you are sure what you are doing */ -static unsigned long previousMillis500ms = 0; // will store last time a 100ms CAN Message was send +static unsigned long previousMillis250ms = 0; // will store last time a 250ms CAN Message was send +static unsigned long previousMillis500ms = 0; // will store last time a 500ms CAN Message was send static unsigned long previousMillis2s = 0; // will store last time a 2s CAN Message was send static unsigned long previousMillis10s = 0; // will store last time a 10s CAN Message was send +static unsigned long previousMillis60s = 0; // will store last time a 60s CAN Message was send + +typedef struct { + CAN_frame* frame; + void (*callback)(); +} Frame; + +static unsigned short listLength = 0; +static Frame framesToSend[20]; static uint32_t inverter_time = 0; static uint16_t inverter_voltage = 0; @@ -33,7 +43,7 @@ CAN_frame SMA_598 = {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x598, - .data = {0x12, 0x30, 0x8A, 0x5B, 0x00, 0x00, 0x00, 0x00}}; //B0-4 Serial? + .data = {0x12, 0xD6, 0x43, 0xA4, 0x00, 0x00, 0x00, 0x00}}; //B0-4 Serial 301100932 CAN_frame SMA_5D8 = {.FD = false, .ext_ID = false, .DLC = 8, @@ -168,6 +178,17 @@ void receive_can_inverter(CAN_frame rx_frame) { } } +void pushFrame(CAN_frame* frame, void (*callback)() = NULL) { + if (listLength >= 20) { + return; //TODO: scream. + } + framesToSend[listLength] = { + .frame = frame, + .callback = callback, + }; + listLength++; +} + void send_can_inverter() { unsigned long currentMillis = millis(); @@ -176,43 +197,63 @@ void send_can_inverter() { return; } + if (listLength > 0 && currentMillis - previousMillis250ms >= INTERVAL_250_MS) { + previousMillis250ms = currentMillis; + // Send next frame. + Frame frame = framesToSend[0]; + transmit_can(frame.frame, can_config.inverter); + if (frame.callback != NULL) { + frame.callback(); + } + for (int i = 0; i < listLength - 1; i++) { + framesToSend[i] = framesToSend[i + 1]; + } + listLength--; + } + + if (!pairing_completed) { + return; + } + // Send CAN Message every 2s if (currentMillis - previousMillis2s >= INTERVAL_2_S) { previousMillis2s = currentMillis; - transmit_can(&SMA_358, can_config.inverter); + pushFrame(&SMA_358); } // Send CAN Message every 10s if (currentMillis - previousMillis10s >= INTERVAL_10_S) { previousMillis10s = currentMillis; - transmit_can(&SMA_518, can_config.inverter); - transmit_can(&SMA_4D8, can_config.inverter); - transmit_can(&SMA_3D8, can_config.inverter); - if (pairing_completed) { - transmit_can( - &SMA_458, - can_config - .inverter); //TODO; not confirmed if battery sends. Transmission starts only after battery is paired - } + pushFrame(&SMA_518); + pushFrame(&SMA_4D8); + pushFrame(&SMA_3D8); } } -void send_tripower_init() { - transmit_can(&SMA_558, can_config.inverter); //Pairing start - Vendor - transmit_can(&SMA_598, can_config.inverter); //Serial - transmit_can(&SMA_5D8, can_config.inverter); //BYD - transmit_can(&SMA_618_0, can_config.inverter); //BATTERY - transmit_can(&SMA_618_1, can_config.inverter); //-Box Pr - transmit_can(&SMA_618_2, can_config.inverter); //emium H - transmit_can(&SMA_618_3, can_config.inverter); //VS - transmit_can(&SMA_358, can_config.inverter); - transmit_can(&SMA_3D8, can_config.inverter); - transmit_can(&SMA_458, can_config.inverter); - transmit_can(&SMA_4D8, can_config.inverter); - transmit_can(&SMA_518, can_config.inverter); + +void completePairing() { pairing_completed = true; } +void send_tripower_init() { + listLength = 0; // clear all frames + + pushFrame(&SMA_558); //Pairing start - Vendor + pushFrame(&SMA_598); //Serial + pushFrame(&SMA_5D8); //BYD + pushFrame(&SMA_618_0); //BATTERY + pushFrame(&SMA_618_1); //-Box Pr + pushFrame(&SMA_618_2); //emium H + pushFrame(&SMA_618_3); //VS + pushFrame(&SMA_358); + pushFrame(&SMA_3D8); + pushFrame(&SMA_458); + pushFrame(&SMA_4D8); + pushFrame(&SMA_518, completePairing); +} + void setup_inverter(void) { // Performs one time setup at startup over CAN bus strncpy(datalayer.system.info.inverter_protocol, "SMA Tripower CAN", 63); datalayer.system.info.inverter_protocol[63] = '\0'; + datalayer.system.status.inverter_allows_contactor_closing = false; // The inverter needs to allow first + pinMode(INVERTER_CONTACTOR_ENABLE_PIN, INPUT); } #endif From 866357157994fa850b92d91e4c02e104df60837e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sat, 28 Dec 2024 14:58:47 +0300 Subject: [PATCH 5/5] Add SMA Tripower to contactor allow writing --- .../communication/contactorcontrol/comm_contactorcontrol.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp index c09e6c171..9a93936a8 100644 --- a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp +++ b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp @@ -90,9 +90,9 @@ void init_contactors() { // Main functions void handle_contactors() { -#ifdef BYD_SMA +#if defined(BYD_SMA) || defined(SMA_TRIPOWER_CAN) datalayer.system.status.inverter_allows_contactor_closing = digitalRead(INVERTER_CONTACTOR_ENABLE_PIN); -#endif // BYD_SMA +#endif #ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY handle_contactors_battery2();