mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-03 01:39:30 +02:00
Add target voltage setting, configurable
This commit is contained in:
parent
b35635676f
commit
3653ff6449
8 changed files with 183 additions and 27 deletions
|
@ -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
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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()));
|
||||
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
|
||||
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.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
|
||||
|
|
|
@ -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" */
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
@ -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) \
|
||||
|
|
|
@ -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>✓</span>"
|
||||
: "<span style='color: red;'>✕</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>";
|
||||
|
||||
|
@ -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 "
|
||||
|
@ -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 +=
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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,
|
||||
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue