Add configurable Sofar ID via Webserver

This commit is contained in:
Daniel Öster 2025-07-03 20:06:35 +03:00
parent 25c60cb42b
commit 920cd83561
6 changed files with 39 additions and 3 deletions

View file

@ -70,6 +70,10 @@ void init_stored_settings() {
datalayer.battery.settings.max_user_set_discharge_voltage_dV = temp; datalayer.battery.settings.max_user_set_discharge_voltage_dV = temp;
} }
datalayer.battery.settings.user_set_voltage_limits_active = settings.getBool("USEVOLTLIMITS", false); datalayer.battery.settings.user_set_voltage_limits_active = settings.getBool("USEVOLTLIMITS", false);
temp = settings.getUInt("SOFAR_ID", false);
if (temp < 16) {
datalayer.battery.settings.sofar_user_specified_battery_id = temp;
}
#ifdef COMMON_IMAGE #ifdef COMMON_IMAGE
user_selected_battery_type = (BatteryType)settings.getUInt("BATTTYPE", (int)BatteryType::None); user_selected_battery_type = (BatteryType)settings.getUInt("BATTTYPE", (int)BatteryType::None);
@ -135,6 +139,9 @@ void store_settings() {
if (!settings.putUInt("TARGETDISCHVOLT", datalayer.battery.settings.max_user_set_discharge_voltage_dV)) { if (!settings.putUInt("TARGETDISCHVOLT", datalayer.battery.settings.max_user_set_discharge_voltage_dV)) {
set_event(EVENT_PERSISTENT_SAVE_INFO, 11); set_event(EVENT_PERSISTENT_SAVE_INFO, 11);
} }
if (!settings.putUInt("SOFAR_ID", datalayer.battery.settings.sofar_user_specified_battery_id)) {
set_event(EVENT_PERSISTENT_SAVE_INFO, 12);
}
settings.end(); // Close preferences handle settings.end(); // Close preferences handle
} }

View file

@ -162,6 +162,9 @@ typedef struct {
/* Maximum voltage for entire battery pack during forced balancing */ /* Maximum voltage for entire battery pack during forced balancing */
uint16_t balancing_max_pack_voltage_dV = 3940; uint16_t balancing_max_pack_voltage_dV = 3940;
/** Sofar CAN Battery ID (0-15) used to parallel multiple packs */
uint8_t sofar_user_specified_battery_id = 0;
} DATALAYER_BATTERY_SETTINGS_TYPE; } DATALAYER_BATTERY_SETTINGS_TYPE;
typedef struct { typedef struct {

View file

@ -149,6 +149,9 @@ String settings_processor(const String& var) {
if (inverter) { if (inverter) {
content += "<h4 style='color: white;'>Inverter interface: <span id='Inverter'>" + content += "<h4 style='color: white;'>Inverter interface: <span id='Inverter'>" +
String(inverter->interface_name()) + "</span></h4>"; String(inverter->interface_name()) + "</span></h4>";
content += "<h4 style='color: white;'>Sofar battery ID: " +
String(datalayer.battery.settings.sofar_user_specified_battery_id) +
" </span> <button onclick='editSofarID()'>Edit</button></h4>";
} }
if (shunt) { if (shunt) {
@ -284,6 +287,13 @@ String settings_processor(const String& var) {
"function editPassword(){var value=prompt('Enter new password:');if(value!==null){var xhr=new " "function editPassword(){var value=prompt('Enter new password:');if(value!==null){var xhr=new "
"XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/" "XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/"
"updatePassword?value='+encodeURIComponent(value),true);xhr.send();}}"; "updatePassword?value='+encodeURIComponent(value),true);xhr.send();}}";
content +=
"function editSofarID(){var value=prompt('For double battery setups. Which battery ID should this emulator "
"send? Enter new value between "
"(0-15):');if(value!==null){if(value>=0&&value<=15){var xhr=new "
"XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/"
"updateSofarID?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value between 0 "
"and 15.');}}}";
content += content +=
"function editWh(){var value=prompt('How much energy the battery can store. Enter new Wh value " "function editWh(){var value=prompt('How much energy the battery can store. Enter new Wh value "
"(1-120000):');if(value!==null){if(value>=1&&value<=120000){var xhr=new " "(1-120000):');if(value!==null){if(value>=1&&value<=120000){var xhr=new "

View file

@ -487,6 +487,20 @@ void init_webserver() {
} }
}); });
// Route for editing Sofar ID
server.on("/updateSofarID", 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.sofar_user_specified_battery_id = value.toInt();
store_settings();
request->send(200, "text/plain", "Updated successfully");
} else {
request->send(400, "text/plain", "Bad Request");
}
});
// Route for editing Wh // Route for editing Wh
server.on("/updateBatterySize", HTTP_GET, [](AsyncWebServerRequest* request) { server.on("/updateBatterySize", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))

View file

@ -117,7 +117,7 @@ void SofarInverter::transmit_can(unsigned long currentMillis) {
void SofarInverter::setup(void) { // Performs one time setup at startup over CAN bus void SofarInverter::setup(void) { // Performs one time setup at startup over CAN bus
// Dymanically set CAN ID according to which battery index we are on // Dymanically set CAN ID according to which battery index we are on
uint16_t base_offset = battery_index << 12; uint16_t base_offset = (datalayer.battery.settings.sofar_user_specified_battery_id << 12);
auto init_frame = [&](CAN_frame& frame, uint16_t base_id) { auto init_frame = [&](CAN_frame& frame, uint16_t base_id) {
frame.FD = false; frame.FD = false;
frame.ext_ID = true; frame.ext_ID = true;
@ -142,4 +142,7 @@ void SofarInverter::setup(void) { // Performs one time setup at startup over CA
strncpy(datalayer.system.info.inverter_protocol, Name, 63); strncpy(datalayer.system.info.inverter_protocol, Name, 63);
datalayer.system.info.inverter_protocol[63] = '\0'; datalayer.system.info.inverter_protocol[63] = '\0';
String tempStr(datalayer.battery.settings.sofar_user_specified_battery_id);
strncpy(datalayer.system.info.inverter_brand, tempStr.c_str(), 7);
datalayer.system.info.inverter_brand[7] = '\0';
} }

View file

@ -14,11 +14,10 @@ class SofarInverter : public CanInverterProtocol {
void update_values(); void update_values();
void transmit_can(unsigned long currentMillis); void transmit_can(unsigned long currentMillis);
void map_can_frame_to_variable(CAN_frame rx_frame); void map_can_frame_to_variable(CAN_frame rx_frame);
static constexpr const char* Name = "Sofar BMS (Extended Frame) over CAN bus"; static constexpr const char* Name = "Sofar BMS (Extended) via CAN, Battery ID";
private: private:
unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
uint8_t battery_index = 0; // Predefined battery ID (015)
uint16_t calculated_capacity_AH = 0; // Pack Capacity in AH (Updates based on battery stats) uint16_t calculated_capacity_AH = 0; // Pack Capacity in AH (Updates based on battery stats)
const char* BatteryType = "BATxEMU"; // Manufacturer name in ASCII const char* BatteryType = "BATxEMU"; // Manufacturer name in ASCII