Skip to content

Commit

Permalink
Add target voltage setting, configurable
Browse files Browse the repository at this point in the history
  • Loading branch information
dalathegreat committed Dec 15, 2024
1 parent b356356 commit 3653ff6
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 28 deletions.
74 changes: 56 additions & 18 deletions Software/Software.ino
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ void init_stored_settings() {
//always save the equipment stop status
settings.putBool("EQUIPMENT_STOP", datalayer.system.settings.equipment_stop_active);

#endif
#endif //LOAD_SAVED_SETTINGS_ON_BOOT

#ifdef WIFI

Expand All @@ -404,7 +404,7 @@ void init_stored_settings() {
password = tempPasswordString;
} else { // Reading from settings failed. Do nothing with SSID. Raise event?
}
#endif
#endif //WIFI

temp = settings.getUInt("BATTERY_WH_MAX", false);
if (temp != 0) {
Expand All @@ -425,10 +425,20 @@ void init_stored_settings() {
temp = settings.getUInt("MAXDISCHARGEAMP", false);
if (temp != 0) {
datalayer.battery.settings.max_user_set_discharge_dA = temp;
temp = settings.getBool("USE_SCALED_SOC", false);
datalayer.battery.settings.soc_scaling_active = temp; //This bool needs to be checked inside the temp!= block
} // No way to know if it wasnt reset otherwise
}
datalayer.battery.settings.soc_scaling_active = settings.getBool("USE_SCALED_SOC", false);
settings.end();

settings.begin("batteryExtra", false);
temp = settings.getUInt("TARGETCHARGEVOLTAGE", false);
if (temp != 0) {
datalayer.battery.settings.max_user_set_charge_voltage_dV = temp;
}
temp = settings.getUInt("TARGETDISCHARGEVOLTAGE", false);
if (temp != 0) {
datalayer.battery.settings.max_user_set_discharge_voltage_dV = temp;
}
datalayer.battery.settings.user_set_voltage_limits_active = settings.getBool("USE_VOLTAGE_LIMITS", false);
settings.end();
}

Expand Down Expand Up @@ -1049,20 +1059,48 @@ void store_settings_equipment_stop() {
}

void storeSettings() {
settings.begin("batterySettings", false);
if (!settings.begin("batterySettings", false)) {
set_event(EVENT_PERSISTENT_SAVE_INFO, 0);
return;
}

#ifdef WIFI
settings.putString("SSID", String(ssid.c_str()));
settings.putString("PASSWORD", String(password.c_str()));
#endif
settings.putUInt("BATTERY_WH_MAX", datalayer.battery.info.total_capacity_Wh);
settings.putUInt("MAXPERCENTAGE",
datalayer.battery.settings.max_percentage / 10); // Divide by 10 for backwards compatibility
settings.putUInt("MINPERCENTAGE",
datalayer.battery.settings.min_percentage / 10); // Divide by 10 for backwards compatibility
settings.putUInt("MAXCHARGEAMP", datalayer.battery.settings.max_user_set_charge_dA);
settings.putUInt("MAXDISCHARGEAMP", datalayer.battery.settings.max_user_set_discharge_dA);
settings.putBool("USE_SCALED_SOC", datalayer.battery.settings.soc_scaling_active);
settings.end();
if (!settings.putString("SSID", String(ssid.c_str()))) {
set_event(EVENT_PERSISTENT_SAVE_INFO, 1);
}
if (!settings.putString("PASSWORD", String(password.c_str()))) {
set_event(EVENT_PERSISTENT_SAVE_INFO, 2);
}
#endif

if (!settings.putUInt("BATTERY_WH_MAX", datalayer.battery.info.total_capacity_Wh)) {
set_event(EVENT_PERSISTENT_SAVE_INFO, 3);
}
if (!settings.putBool("USE_SCALED_SOC", datalayer.battery.settings.soc_scaling_active)) {
set_event(EVENT_PERSISTENT_SAVE_INFO, 4);
}
if (!settings.putUInt("MAXPERCENTAGE", datalayer.battery.settings.max_percentage / 10)) {
set_event(EVENT_PERSISTENT_SAVE_INFO, 5);
}
if (!settings.putUInt("MINPERCENTAGE", datalayer.battery.settings.min_percentage / 10)) {
set_event(EVENT_PERSISTENT_SAVE_INFO, 6);
}
if (!settings.putUInt("MAXCHARGEAMP", datalayer.battery.settings.max_user_set_charge_dA)) {
set_event(EVENT_PERSISTENT_SAVE_INFO, 7);
}
if (!settings.putUInt("MAXDISCHARGEAMP", datalayer.battery.settings.max_user_set_discharge_dA)) {
set_event(EVENT_PERSISTENT_SAVE_INFO, 8);
}
if (!settings.putBool("USE_VOLTAGE_LIMITS", datalayer.battery.settings.user_set_voltage_limits_active)) {
set_event(EVENT_PERSISTENT_SAVE_INFO, 9);
}
if (!settings.putUInt("TARGETCHARGEVOLTAGE", datalayer.battery.settings.max_user_set_charge_voltage_dV)) {
set_event(EVENT_PERSISTENT_SAVE_INFO, 10);
}
if (!settings.putUInt("TARGETDISCHARGEVOLTAGE", datalayer.battery.settings.max_user_set_discharge_voltage_dV)) {
set_event(EVENT_PERSISTENT_SAVE_INFO, 11);
}
settings.end(); // Close preferences handle
}

/** Reset reason numbering and description
Expand Down
10 changes: 8 additions & 2 deletions Software/USER_SETTINGS.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,16 @@
#define BATTERY_MAXTEMPERATURE 500
// -250 = -25.0 °C , Min temperature (Will produce a battery frozen event if below)
#define BATTERY_MINTEMPERATURE -250
// 300 = 30.0A , BYD CAN specific setting, Max charge in Amp (Some inverters needs to be limited)
// 300 = 30.0A , Max charge in Amp (Some inverters needs to be limited)
#define BATTERY_MAX_CHARGE_AMP 300
// 300 = 30.0A , BYD CAN specific setting, Max discharge in Amp (Some inverters needs to be limited)
// 300 = 30.0A , Max discharge in Amp (Some inverters needs to be limited)
#define BATTERY_MAX_DISCHARGE_AMP 300
// Enable this to manually set voltage limits on how much battery can be discharged/charged. Normally not used.
#define BATTERY_USE_VOLTAGE_LIMITS false
// 5000 = 500.0V , Target charge voltage (Value can be tuned on the fly via webserver). Not used unless BATTERY_USE_VOLTAGE_LIMITS = true
#define BATTERY_MAX_CHARGE_VOLTAGE 5000
// 3000 = 300.0V, Target discharge voltage (Value can be tuned on the fly via webserver). Not used unless BATTERY_USE_VOLTAGE_LIMITS = true
#define BATTERY_MAX_DISCHARGE_VOLTAGE 3000

/* Do not change any code below this line unless you are sure what you are doing */
/* Only change battery specific settings in "USER_SETTINGS.h" */
Expand Down
10 changes: 10 additions & 0 deletions Software/src/datalayer/datalayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,20 @@ typedef struct {
* you want the inverter to be able to use. At this real SOC, the inverter
* will "see" 100% */
uint16_t max_percentage = BATTERY_MAXPERCENTAGE;

/** The user specified maximum allowed charge rate, in deciAmpere. 300 = 30.0 A */
uint16_t max_user_set_charge_dA = BATTERY_MAX_CHARGE_AMP;
/** The user specified maximum allowed discharge rate, in deciAmpere. 300 = 30.0 A */
uint16_t max_user_set_discharge_dA = BATTERY_MAX_DISCHARGE_AMP;

/** User specified discharge/charge voltages in use. Set to true to use user specified values */
/** Some inverters like to see a specific target voltage for charge/discharge. Use these values to override automatic voltage limits*/
bool user_set_voltage_limits_active = BATTERY_USE_VOLTAGE_LIMITS;
/** The user specified maximum allowed charge voltage, in deciVolt. 4000 = 400.0 V */
uint16_t max_user_set_charge_voltage_dV = BATTERY_MAX_CHARGE_VOLTAGE;
/** The user specified maximum allowed discharge voltage, in deciVolt. 3000 = 300.0 V */
uint16_t max_user_set_discharge_voltage_dV = BATTERY_MAX_DISCHARGE_VOLTAGE;

} DATALAYER_BATTERY_SETTINGS_TYPE;

typedef struct {
Expand Down
3 changes: 3 additions & 0 deletions Software/src/devboard/utils/events.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ void init_events(void) {
events.entries[EVENT_DUMMY_DEBUG].level = EVENT_LEVEL_DEBUG;
events.entries[EVENT_DUMMY_WARNING].level = EVENT_LEVEL_WARNING;
events.entries[EVENT_DUMMY_ERROR].level = EVENT_LEVEL_ERROR;
events.entries[EVENT_PERSISTENT_SAVE_INFO].level = EVENT_LEVEL_INFO;
events.entries[EVENT_SERIAL_RX_WARNING].level = EVENT_LEVEL_WARNING;
events.entries[EVENT_SERIAL_RX_FAILURE].level = EVENT_LEVEL_ERROR;
events.entries[EVENT_SERIAL_TX_FAILURE].level = EVENT_LEVEL_ERROR;
Expand Down Expand Up @@ -368,6 +369,8 @@ const char* get_event_message_string(EVENTS_ENUM_TYPE event) {
return "The dummy warning event was set!"; // Don't change this event message!
case EVENT_DUMMY_ERROR:
return "The dummy error event was set!"; // Don't change this event message!
case EVENT_PERSISTENT_SAVE_INFO:
return "Info: Failed to save user settings. Namespace full?";
case EVENT_SERIAL_RX_WARNING:
return "Error in serial function: No data received for some time, see data for minutes";
case EVENT_SERIAL_RX_FAILURE:
Expand Down
3 changes: 2 additions & 1 deletion Software/src/devboard/utils/events.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

// #define INCLUDE_EVENTS_TEST // Enable to run an event test loop, see events_test_on_target.cpp

#define EE_MAGIC_HEADER_VALUE 0x0017 // 0x0000 to 0xFFFF
#define EE_MAGIC_HEADER_VALUE 0x0018 // 0x0000 to 0xFFFF

#define GENERATE_ENUM(ENUM) ENUM,
#define GENERATE_STRING(STRING) #STRING,
Expand Down Expand Up @@ -79,6 +79,7 @@
XX(EVENT_DUMMY_DEBUG) \
XX(EVENT_DUMMY_WARNING) \
XX(EVENT_DUMMY_ERROR) \
XX(EVENT_PERSISTENT_SAVE_INFO) \
XX(EVENT_SERIAL_RX_WARNING) \
XX(EVENT_SERIAL_RX_FAILURE) \
XX(EVENT_SERIAL_TX_FAILURE) \
Expand Down
46 changes: 45 additions & 1 deletion Software/src/devboard/webserver/settings_html.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,21 @@ String settings_processor(const String& var) {
content += "<h4 style='color: white;'>Max discharge speed: " +
String(datalayer.battery.settings.max_user_set_discharge_dA / 10.0, 1) +
" A </span> <button onclick='editMaxDischargeA()'>Edit</button></h4>";
content += "<h4 style='color: white;'>Manual charge voltage limits: <span id='BATTERY_USE_VOLTAGE_LIMITS'>" +
String(datalayer.battery.settings.user_set_voltage_limits_active
? "<span>&#10003;</span>"
: "<span style='color: red;'>&#10005;</span>") +
"</span> <button onclick='editUseVoltageLimit()'>Edit</button></h4>";
content +=
"<h4 style='color: " +
String(datalayer.battery.settings.user_set_voltage_limits_active ? "white" : "darkgrey") +
";'>Target charge voltage: " + String(datalayer.battery.settings.max_user_set_charge_voltage_dV / 10.0, 1) +
" V </span> <button onclick='editMaxChargeVoltage()'>Edit</button></h4>";
content += "<h4 style='color: " +
String(datalayer.battery.settings.user_set_voltage_limits_active ? "white" : "darkgrey") +
";'>Target discharge voltage: " +
String(datalayer.battery.settings.max_user_set_discharge_voltage_dV / 10.0, 1) +
" V </span> <button onclick='editMaxDischargeVoltage()'>Edit</button></h4>";
// Close the block
content += "</div>";

Expand Down Expand Up @@ -130,7 +145,9 @@ String settings_processor(const String& var) {
"updateBatterySize?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value between 1 "
"and 120000.');}}}";
content +=
"function editUseScaledSOC(){var value=prompt('Should SOC% be scaled? (0 = No, 1 = "
"function editUseScaledSOC(){var value=prompt('Extends battery life by rescaling the SOC within the configured "
"minimum "
"and maximum percentage. Should SOC scaling be applied? (0 = No, 1 = "
"Yes):');if(value!==null){if(value==0||value==1){var xhr=new "
"XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/"
"updateUseScaledSOC?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value between 0 "
Expand Down Expand Up @@ -161,6 +178,33 @@ String settings_processor(const String& var) {
"XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/"
"updateMaxDischargeA?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value between 0 "
"and 1000.0');}}}";
content +=
"function editUseVoltageLimit(){var value=prompt('Enable this option to manually restrict charge/discharge to "
"a specific voltage set below."
"If disabled the emulator automatically determines this based on battery limits. Restrict manually? (0 = No, 1 "
"= Yes)"
":');if(value!==null){if(value==0||value==1){var xhr=new "
"XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/"
"updateUseVoltageLimit?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value between "
"0 "
"and 1.');}}}";
content +=
"function editMaxChargeVoltage(){var value=prompt('Some inverters needs to be artificially limited. Enter new "
"voltage setpoint batttery should charge to (0-1000.0):');if(value!==null){if(value>=0&&value<=1000){var "
"xhr=new "
"XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/"
"updateMaxChargeVoltage?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value "
"between 0 "
"and 1000.0');}}}";
content +=
"function editMaxDischargeVoltage(){var value=prompt('Some inverters needs to be artificially limited. Enter "
"new "
"voltage setpoint batttery should discharge to (0-1000.0):');if(value!==null){if(value>=0&&value<=1000){var "
"xhr=new "
"XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/"
"updateMaxDischargeVoltage?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value "
"between 0 "
"and 1000.0');}}}";

#ifdef TEST_FAKE_BATTERY
content +=
Expand Down
42 changes: 42 additions & 0 deletions Software/src/devboard/webserver/webserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,48 @@ void init_webserver() {
}
});

// Route for editing BATTERY_USE_VOLTAGE_LIMITS
server.on("/updateUseVoltageLimit", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication();
if (request->hasParam("value")) {
String value = request->getParam("value")->value();
datalayer.battery.settings.user_set_voltage_limits_active = value.toInt();
storeSettings();
request->send(200, "text/plain", "Updated successfully");
} else {
request->send(400, "text/plain", "Bad Request");
}
});

// Route for editing MaxChargeVoltage
server.on("/updateMaxChargeVoltage", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication();
if (request->hasParam("value")) {
String value = request->getParam("value")->value();
datalayer.battery.settings.max_user_set_charge_voltage_dV = static_cast<uint16_t>(value.toFloat() * 10);
storeSettings();
request->send(200, "text/plain", "Updated successfully");
} else {
request->send(400, "text/plain", "Bad Request");
}
});

// Route for editing MaxDischargeVoltage
server.on("/updateMaxDischargeVoltage", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication();
if (request->hasParam("value")) {
String value = request->getParam("value")->value();
datalayer.battery.settings.max_user_set_discharge_voltage_dV = static_cast<uint16_t>(value.toFloat() * 10);
storeSettings();
request->send(200, "text/plain", "Updated successfully");
} else {
request->send(400, "text/plain", "Bad Request");
}
});

// Route for resetting SOH on Nissan LEAF batteries
server.on("/resetSOH", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) {
Expand Down
24 changes: 18 additions & 6 deletions Software/src/inverter/BYD-CAN.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ static unsigned long previousMillis2s = 0; // will store last time a 2s CAN Me
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

#define VOLTAGE_OFFSET_DV 20

CAN_frame BYD_250 = {.FD = false,
.ext_ID = false,
.DLC = 8,
Expand Down Expand Up @@ -98,12 +100,22 @@ void update_values_can_inverter() { //This function maps all the values fetched
}

//Map values to CAN messages
//Maxvoltage (eg 400.0V = 4000 , 16bits long)
BYD_110.data.u8[0] = (datalayer.battery.info.max_design_voltage_dV >> 8);
BYD_110.data.u8[1] = (datalayer.battery.info.max_design_voltage_dV & 0x00FF);
//Minvoltage (eg 300.0V = 3000 , 16bits long)
BYD_110.data.u8[2] = (datalayer.battery.info.min_design_voltage_dV >> 8);
BYD_110.data.u8[3] = (datalayer.battery.info.min_design_voltage_dV & 0x00FF);
if (datalayer.battery.settings.user_set_voltage_limits_active) { //If user is requesting a specific voltage
//Target charge voltage (eg 400.0V = 4000 , 16bits long)
BYD_110.data.u8[0] = (datalayer.battery.settings.max_user_set_charge_voltage_dV >> 8);
BYD_110.data.u8[1] = (datalayer.battery.settings.max_user_set_charge_voltage_dV & 0x00FF);
//Target discharge voltage (eg 300.0V = 3000 , 16bits long)
BYD_110.data.u8[2] = (datalayer.battery.settings.max_user_set_discharge_voltage_dV >> 8);
BYD_110.data.u8[3] = (datalayer.battery.settings.max_user_set_discharge_voltage_dV & 0x00FF);
} else { //Use the voltage based on battery reported design voltage +- offset to avoid triggering events
//Target charge voltage (eg 400.0V = 4000 , 16bits long)
BYD_110.data.u8[0] = ((datalayer.battery.info.max_design_voltage_dV - VOLTAGE_OFFSET_DV) >> 8);
BYD_110.data.u8[1] = ((datalayer.battery.info.max_design_voltage_dV - VOLTAGE_OFFSET_DV) & 0x00FF);
//Target discharge voltage (eg 300.0V = 3000 , 16bits long)
BYD_110.data.u8[2] = ((datalayer.battery.info.min_design_voltage_dV + VOLTAGE_OFFSET_DV) >> 8);
BYD_110.data.u8[3] = ((datalayer.battery.info.min_design_voltage_dV + VOLTAGE_OFFSET_DV) & 0x00FF);
}

//Maximum discharge power allowed (Unit: A+1)
BYD_110.data.u8[4] = (datalayer.battery.status.max_discharge_current_dA >> 8);
BYD_110.data.u8[5] = (datalayer.battery.status.max_discharge_current_dA & 0x00FF);
Expand Down

0 comments on commit 3653ff6

Please sign in to comment.