Skip to content

Commit

Permalink
Feature: Add configurable limit to charge/discharge on BYD Modbus (#290)
Browse files Browse the repository at this point in the history
* Add configurable limit to charge/discharge BYD modbus + optimizations
  • Loading branch information
dalathegreat authored May 12, 2024
1 parent 00e4dde commit e5c408f
Showing 1 changed file with 40 additions and 100 deletions.
140 changes: 40 additions & 100 deletions Software/src/inverter/BYD-MODBUS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -27,123 +35,55 @@ 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
bms_char_dis_status = DISCHARGING;
} 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);

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
} else {
battery_data[8] = 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)
}
// 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.max_charge_power_W > 30000) {
battery_data[7] = 30000;
if (datalayer.battery.status.bms_status == ACTIVE) {
mbPV[308] = datalayer.battery.status.voltage_dV;
} 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
mbPV[308] = 0;
}
//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() {
Expand Down

0 comments on commit e5c408f

Please sign in to comment.