From e5c408fb7c9719a791d94e1e3818dbfcec54ca38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 12 May 2024 19:47:25 +0300 Subject: [PATCH] Feature: Add configurable limit to charge/discharge on BYD Modbus (#290) * Add configurable limit to charge/discharge BYD modbus + optimizations --- Software/src/inverter/BYD-MODBUS.cpp | 140 ++++++++------------------- 1 file changed, 40 insertions(+), 100 deletions(-) diff --git a/Software/src/inverter/BYD-MODBUS.cpp b/Software/src/inverter/BYD-MODBUS.cpp index fd579b30..b92b6254 100644 --- a/Software/src/inverter/BYD-MODBUS.cpp +++ b/Software/src/inverter/BYD-MODBUS.cpp @@ -3,6 +3,14 @@ #include "../datalayer/datalayer.h" #include "BYD-MODBUS.h" +// For modbus register definitions, see https://gitlab.com/pelle8/inverter_resources/-/blob/main/byd_registers_modbus_rtu.md + +static uint8_t bms_char_dis_status = STANDBY; +static uint32_t user_configured_max_discharge_W = 0; +static uint32_t user_configured_max_charge_W = 0; +static uint32_t max_discharge_W = 0; +static uint32_t max_charge_W = 0; + void update_modbus_registers_inverter() { verify_temperature_modbus(); handle_update_data_modbusp201_byd(); @@ -27,46 +35,20 @@ void handle_static_data_modbus_byd() { memcpy(&mbPV[i], data_array_pointers[arr_idx], data_size); i += data_size / sizeof(uint16_t); } + static uint16_t init_p201[13] = {0, 0, 0, MAX_POWER, MAX_POWER, 0, 0, 53248, 10, 53248, 10, 0, 0}; + memcpy(&mbPV[200], init_p201, sizeof(init_p201)); + static uint16_t init_p301[24] = {0, 0, 128, 0, 0, 0, 0, 0, 0, 2000, 0, 2000, + 75, 95, 0, 0, 16, 22741, 0, 0, 13, 52064, 230, 9900}; + memcpy(&mbPV[300], init_p301, sizeof(init_p301)); } void handle_update_data_modbusp201_byd() { - // Store the data into the array - static uint16_t system_data[13]; - system_data[0] = 0; // Id.: p201 Value.: 0 Scaled value.: 0 Comment.: Always 0 - system_data[1] = 0; // Id.: p202 Value.: 0 Scaled value.: 0 Comment.: Always 0 - if (datalayer.battery.info.total_capacity_Wh > 60000) { - system_data[2] = 60000; - } else { - system_data[2] = - (datalayer.battery.info - .total_capacity_Wh); // Id.: p203 Value.: 32000 Scaled value.: 32kWh Comment.: Capacity rated, maximum value is 60000 (60kWh) - } - system_data[3] = MAX_POWER; // Id.: p204 Value.: 32000 Scaled value.: 32kWh Comment.: Nominal capacity - system_data[4] = - MAX_POWER; // Id.: p205 Value.: 14500 Scaled value.: 30,42kW Comment.: Max Charge/Discharge Power=10.24kW (lowest value of 204 and 205 will be enforced by Gen24) - system_data[5] = - (datalayer.battery.info - .max_design_voltage_dV); // Id.: p206 Value.: 3667 Scaled value.: 362,7VDC Comment.: Max Voltage, if higher charging is not possible (goes into forced discharge) - system_data[6] = - (datalayer.battery.info - .min_design_voltage_dV); // Id.: p207 Value.: 2776 Scaled value.: 277,6VDC Comment.: Min Voltage, if lower Gen24 disables battery - system_data[7] = - 53248; // Id.: p208 Value.: 53248 Scaled value.: 53248 Comment.: Always 53248 for this BYD, Peak Charge power? - system_data[8] = 10; // Id.: p209 Value.: 10 Scaled value.: 10 Comment.: Always 10 - system_data[9] = - 53248; // Id.: p210 Value.: 53248 Scaled value.: 53248 Comment.: Always 53248 for this BYD, Peak Discharge power? - system_data[10] = 10; // Id.: p211 Value.: 10 Scaled value.: 10 Comment.: Always 10 - system_data[11] = 0; // Id.: p212 Value.: 0 Scaled value.: 0 Comment.: Always 0 - system_data[12] = 0; // Id.: p213 Value.: 0 Scaled value.: 0 Comment.: Always 0 - static uint16_t i = 200; - memcpy(&mbPV[i], system_data, sizeof(system_data)); + mbPV[202] = std::min(datalayer.battery.info.total_capacity_Wh, 60000u); //Cap capacity to 60kWh if needed + mbPV[205] = (datalayer.battery.info.max_design_voltage_dV); // Max Voltage, if higher Gen24 forces discharge + mbPV[206] = (datalayer.battery.info.min_design_voltage_dV); // Min Voltage, if lower Gen24 disables battery } void handle_update_data_modbusp301_byd() { - // Store the data into the array - static uint16_t battery_data[24]; - - static uint8_t bms_char_dis_status = STANDBY; if (datalayer.battery.status.current_dA == 0) { bms_char_dis_status = STANDBY; } else if (datalayer.battery.status.current_dA < 0) { //Negative value = Discharging @@ -74,76 +56,34 @@ void handle_update_data_modbusp301_byd() { } else { //Positive value = Charging bms_char_dis_status = CHARGING; } + // Convert max discharge Amp value to max Watt + user_configured_max_discharge_W = + ((datalayer.battery.info.max_discharge_amp_dA * datalayer.battery.info.max_design_voltage_dV) / 100); + // Use the smaller value, battery reported value OR user configured value + max_discharge_W = std::min(datalayer.battery.status.max_discharge_power_W, user_configured_max_discharge_W); + + // Convert max charge Amp value to max Watt + user_configured_max_charge_W = + ((datalayer.battery.info.max_charge_amp_dA * datalayer.battery.info.max_design_voltage_dV) / 100); + // Use the smaller value, battery reported value OR user configured value + max_charge_W = std::min(datalayer.battery.status.max_charge_power_W, user_configured_max_charge_W); if (datalayer.battery.status.bms_status == ACTIVE) { - battery_data[8] = - datalayer.battery.status - .voltage_dV; // Id.: p309 Value.: 3161 Scaled value.: 316,1VDC Comment.: Batt Voltage outer (0 if status !=3, maybe a contactor closes when active): 173.4V + mbPV[308] = datalayer.battery.status.voltage_dV; } else { - battery_data[8] = 0; + mbPV[308] = 0; } - battery_data[0] = - datalayer.battery.status - .bms_status; // Id.: p301 Value.: 3 Scaled value.: 3 Comment.: status(*): ACTIVE - [0..5]<>[STANDBY,INACTIVE,DARKSTART,ACTIVE,FAULT,UPDATING] - battery_data[1] = 0; // Id.: p302 Value.: 0 Scaled value.: 0 Comment.: always 0 - battery_data[2] = 128 + bms_char_dis_status; // Id.: p303 Value.: 130 Scaled value.: 130 Comment.: mode(*): normal - battery_data[3] = - datalayer.battery.status - .reported_soc; // Id.: p304 Value.: 1700 Scaled value.: 50% Comment.: SOC: (50% would equal 5000) - if (datalayer.battery.info.total_capacity_Wh > 60000) { - battery_data[4] = 60000; - } else { - battery_data[4] = - datalayer.battery.info.total_capacity_Wh; // Id.: p305 Value.: 32000 Scaled value.: 32kWh Comment.: tot cap: - } - if (datalayer.battery.status.remaining_capacity_Wh > 60000) { - battery_data[5] = 60000; - } else { - // Id.: p306 Value.: 13260 Scaled value.: 13,26kWh Comment.: remaining cap: 7.68kWh - battery_data[5] = datalayer.battery.status.remaining_capacity_Wh; - } - if (datalayer.battery.status.max_discharge_power_W > 30000) { - battery_data[6] = 30000; - } else { - battery_data[6] = - datalayer.battery.status - .max_discharge_power_W; // Id.: p307 Value.: 25604 Scaled value.: 25,604kW Comment.: max/target discharge power: 0W (0W > restricts to no discharge) - } - - if (datalayer.battery.status.max_charge_power_W > 30000) { - battery_data[7] = 30000; - } else { - battery_data[7] = - datalayer.battery.status - .max_charge_power_W; // Id.: p308 Value.: 25604 Scaled value.: 25,604kW Comment.: max/target charge power: 4.3kW (during charge), both 307&308 can be set (>0) at the same time - } - //Battery_data[8] set previously in function // Id.: p309 Value.: 3161 Scaled value.: 316,1VDC Comment.: Batt Voltage outer (0 if status !=3, maybe a contactor closes when active): 173.4V - battery_data[9] = - 2000; // Id.: p310 Value.: 64121 Scaled value.: 6412,1W Comment.: Current Power to API: if>32768... -(65535-61760)=3775W - battery_data[10] = - datalayer.battery.status - .voltage_dV; // Id.: p311 Value.: 3161 Scaled value.: 316,1VDC Comment.: Batt Voltage inner: 173.2V - battery_data[11] = 2000; // Id.: p312 Value.: 64121 Scaled value.: 6412,1W Comment.: p310 - battery_data[12] = - datalayer.battery.status - .temperature_min_dC; // Id.: p313 Value.: 75 Scaled value.: 7,5 Comment.: temp min: 7 degrees (if below 0....65535-t) - battery_data[13] = - datalayer.battery.status - .temperature_max_dC; // Id.: p314 Value.: 95 Scaled value.: 9,5 Comment.: temp max: 9 degrees (if below 0....65535-t) - battery_data[14] = 0; // Id.: p315 Value.: 0 Scaled value.: 0 Comment.: always 0 - battery_data[15] = 0; // Id.: p316 Value.: 0 Scaled value.: 0 Comment.: always 0 - battery_data[16] = 16; // Id.: p317 Value.: 0 Scaled value.: 0 Comment.: counter charge hi - battery_data[17] = - 22741; // Id.: p318 Value.: 0 Scaled value.: 0 Comment.: counter charge lo....65536*101+9912 = 6629048 Wh? - battery_data[18] = 0; // Id.: p319 Value.: 0 Scaled value.: 0 Comment.: always 0 - battery_data[19] = 0; // Id.: p320 Value.: 0 Scaled value.: 0 Comment.: always 0 - battery_data[20] = 13; // Id.: p321 Value.: 0 Scaled value.: 0 Comment.: counter discharge hi - battery_data[21] = - 52064; // Id.: p322 Value.: 0 Scaled value.: 0 Comment.: counter discharge lo....65536*92+7448 = 6036760 Wh? - battery_data[22] = 230; // Id.: p323 Value.: 0 Scaled value.: 0 Comment.: device temperature (23 degrees) - battery_data[23] = datalayer.battery.status.soh_pptt; // Id.: p324 Value.: 9900 Scaled value.: 99% Comment.: SOH - static uint16_t i = 300; - memcpy(&mbPV[i], battery_data, sizeof(battery_data)); + mbPV[300] = datalayer.battery.status.bms_status; + mbPV[302] = 128 + bms_char_dis_status; + mbPV[303] = datalayer.battery.status.reported_soc; + mbPV[304] = std::min(datalayer.battery.info.total_capacity_Wh, 60000u); //Cap capacity to 60kWh if needed + mbPV[305] = std::min(datalayer.battery.status.remaining_capacity_Wh, 60000u); //Cap capacity to 60kWh if needed + mbPV[306] = std::min(max_discharge_W, 30000u); //Cap to 30000 if exceeding + mbPV[307] = std::min(max_charge_W, 30000u); //Cap to 30000 if exceeding + mbPV[310] = datalayer.battery.status.voltage_dV; + mbPV[312] = datalayer.battery.status.temperature_min_dC; + mbPV[313] = datalayer.battery.status.temperature_max_dC; + mbPV[323] = datalayer.battery.status.soh_pptt; } void verify_temperature_modbus() {