Make custom-BMS voltage limits configurable via settings

This commit is contained in:
Jonny 2025-08-15 22:54:04 +01:00
parent 552c304be4
commit 0d8bc06f04
17 changed files with 156 additions and 91 deletions

View file

@ -171,6 +171,12 @@
// 3000 = 300.0V, Target discharge voltage (Value can be tuned on the fly via webserver). Not used unless BATTERY_USE_VOLTAGE_LIMITS = true // 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 #define BATTERY_MAX_DISCHARGE_VOLTAGE 3000
/* Pack/cell voltage limits for custom-BMS batteries (RJXZS, Daly, etc.) */
//#define MAX_CUSTOM_PACK_VOLTAGE_DV 5000 // 5000 = 500.0V , Maximum pack voltage in decivolts
//#define MIN_CUSTOM_PACK_VOLTAGE_DV 2500 // 2500 = 250.0V , Minimum pack voltage in decivolts
//#define MAX_CUSTOM_CELL_VOLTAGE_MV 4250 // 4250 = 4.250V , Maximum cell voltage in millivolts (4250 = 4.250V)
//#define MIN_CUSTOM_CELL_VOLTAGE_MV 2650 // 2650 = 2.650V , Minimum cell voltage in millivolts (2650 = 2.650V)
/* LED settings. Optional customization for how the blinking pattern on the LED should behave. /* LED settings. Optional customization for how the blinking pattern on the LED should behave.
* CLASSIC - Slow up/down ramp. If CLASSIC, then a ramp up and ramp down will finish in LED_PERIOD_MS milliseconds * CLASSIC - Slow up/down ramp. If CLASSIC, then a ramp up and ramp down will finish in LED_PERIOD_MS milliseconds
* FLOW - Ramp up/down depending on flow of energy * FLOW - Ramp up/down depending on flow of energy

View file

@ -312,3 +312,23 @@ void setup_battery() {
#endif #endif
} }
#endif #endif
/* User-selected voltages used for custom-BMS batteries (RJXZS etc.) */
#if defined(MAX_CUSTOM_PACK_VOLTAGE_DV) && defined(MIN_CUSTOM_PACK_VOLTAGE_DV) && \
defined(MAX_CUSTOM_CELL_VOLTAGE_MV) && defined(MIN_CUSTOM_CELL_VOLTAGE_MV)
// Use USER_SETTINGS.h values for cell/pack voltage defaults
uint16_t user_selected_max_pack_voltage_default_dV = MAX_CUSTOM_PACK_VOLTAGE_DV;
uint16_t user_selected_min_pack_voltage_default_dV = MIN_CUSTOM_PACK_VOLTAGE_DV;
uint16_t user_selected_max_cell_voltage_default_mV = MAX_CUSTOM_CELL_VOLTAGE_MV;
uint16_t user_selected_min_cell_voltage_default_mV = MIN_CUSTOM_CELL_VOLTAGE_MV;
#else
// Use 0V for user selected cell/pack voltage defaults (COMMON_IMAGE will replace with saved values from NVM)
uint16_t user_selected_max_pack_voltage_default_dV = 0;
uint16_t user_selected_min_pack_voltage_default_dV = 0;
uint16_t user_selected_max_cell_voltage_default_mV = 0;
uint16_t user_selected_min_cell_voltage_default_mV = 0;
#endif
uint16_t user_selected_max_pack_voltage_dV = user_selected_max_pack_voltage_default_dV;
uint16_t user_selected_min_pack_voltage_dV = user_selected_min_pack_voltage_default_dV;
uint16_t user_selected_max_cell_voltage_mV = user_selected_max_cell_voltage_default_mV;
uint16_t user_selected_min_cell_voltage_mV = user_selected_min_cell_voltage_default_mV;

View file

@ -55,4 +55,9 @@ void setup_can_shunt();
void setup_battery(void); void setup_battery(void);
extern uint16_t user_selected_max_pack_voltage_dV;
extern uint16_t user_selected_min_pack_voltage_dV;
extern uint16_t user_selected_max_cell_voltage_mV;
extern uint16_t user_selected_min_cell_voltage_mV;
#endif #endif

View file

@ -1,4 +1,5 @@
#include "CELLPOWER-BMS.h" #include "CELLPOWER-BMS.h"
#include "../battery/BATTERIES.h"
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage #include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
@ -231,8 +232,8 @@ void CellPowerBms::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, Name, 63); strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0'; datalayer.system.info.battery_protocol[63] = '\0';
datalayer.system.status.battery_allows_contactor_closing = true; datalayer.system.status.battery_allows_contactor_closing = true;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.max_design_voltage_dV = user_selected_max_pack_voltage_dV;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = user_selected_min_pack_voltage_dV;
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; datalayer.battery.info.max_cell_voltage_mV = user_selected_max_cell_voltage_mV;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; datalayer.battery.info.min_cell_voltage_mV = user_selected_min_cell_voltage_mV;
} }

View file

@ -22,11 +22,6 @@ class CellPowerBms : public CanBattery {
private: private:
CellpowerHtmlRenderer renderer; CellpowerHtmlRenderer renderer;
/* Tweak these according to your battery build */
static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 1500;
static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value
static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value
unsigned long previousMillis1s = 0; // will store last time a 1s CAN Message was sent unsigned long previousMillis1s = 0; // will store last time a 1s CAN Message was sent

View file

@ -1,6 +1,7 @@
#include "DALY-BMS.h" #include "DALY-BMS.h"
#include <Arduino.h> #include <Arduino.h>
#include <cstdint> #include <cstdint>
#include "../battery/BATTERIES.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/hal/hal.h" #include "../devboard/hal/hal.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
@ -67,10 +68,10 @@ void DalyBms::update_values() {
void DalyBms::setup(void) { // Performs one time setup at startup void DalyBms::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, Name, 63); strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0'; datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.max_design_voltage_dV = user_selected_max_pack_voltage_dV;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = user_selected_min_pack_voltage_dV;
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; datalayer.battery.info.max_cell_voltage_mV = user_selected_max_cell_voltage_mV;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; datalayer.battery.info.min_cell_voltage_mV = user_selected_min_cell_voltage_mV;
datalayer.battery.info.total_capacity_Wh = BATTERY_WH_MAX; datalayer.battery.info.total_capacity_Wh = BATTERY_WH_MAX;
datalayer.system.status.battery_allows_contactor_closing = true; datalayer.system.status.battery_allows_contactor_closing = true;

View file

@ -17,11 +17,6 @@ class DalyBms : public RS485Battery {
private: private:
/* Tweak these according to your battery build */ /* Tweak these according to your battery build */
static const int CELL_COUNT = 14;
static const int MAX_PACK_VOLTAGE_DV = 580; //580 = 58.0V
static const int MIN_PACK_VOLTAGE_DV = 460; //480 = 48.0V
static const int MAX_CELL_VOLTAGE_MV = 4200; //Battery is put into emergency stop if one cell goes over this value
static const int MIN_CELL_VOLTAGE_MV = 3200; //Battery is put into emergency stop if one cell goes below this value
static const int POWER_PER_PERCENT = static const int POWER_PER_PERCENT =
50; // below 20% and above 80% limit power to 50W * SOC (i.e. 150W at 3%, 500W at 10%, ...) 50; // below 20% and above 80% limit power to 50W * SOC (i.e. 150W at 3%, 500W at 10%, ...)
static const int POWER_PER_DEGREE_C = 60; // max power added/removed per degree above/below 0°C static const int POWER_PER_DEGREE_C = 60; // max power added/removed per degree above/below 0°C

View file

@ -1,4 +1,5 @@
#include "ORION-BMS.h" #include "ORION-BMS.h"
#include "../battery/BATTERIES.h"
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
@ -115,9 +116,9 @@ void OrionBms::transmit_can(unsigned long currentMillis) {
void OrionBms::setup(void) { // Performs one time setup at startup void OrionBms::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, Name, 63); strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0'; datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.max_design_voltage_dV = user_selected_max_pack_voltage_dV;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = user_selected_min_pack_voltage_dV;
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; datalayer.battery.info.max_cell_voltage_mV = user_selected_max_cell_voltage_mV;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; datalayer.battery.info.min_cell_voltage_mV = user_selected_min_cell_voltage_mV;
datalayer.system.status.battery_allows_contactor_closing = true; datalayer.system.status.battery_allows_contactor_closing = true;
} }

View file

@ -17,13 +17,6 @@ class OrionBms : public CanBattery {
static constexpr const char* Name = "DIY battery with Orion BMS (Victron setting)"; static constexpr const char* Name = "DIY battery with Orion BMS (Victron setting)";
private: private:
/* Change the following to suit your battery */
static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 1500;
static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value
static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value
static const int MAX_CELL_DEVIATION_MV = 150;
uint16_t cellvoltages[MAX_AMOUNT_CELLS]; //array with all the cellvoltages uint16_t cellvoltages[MAX_AMOUNT_CELLS]; //array with all the cellvoltages
uint16_t Maximum_Cell_Voltage = 3700; uint16_t Maximum_Cell_Voltage = 3700;
uint16_t Minimum_Cell_Voltage = 3700; uint16_t Minimum_Cell_Voltage = 3700;

View file

@ -1,4 +1,5 @@
#include "PYLON-BATTERY.h" #include "PYLON-BATTERY.h"
#include "../battery/BATTERIES.h"
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
@ -131,10 +132,10 @@ void PylonBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Pylon compatible battery", 63); strncpy(datalayer.system.info.battery_protocol, "Pylon compatible battery", 63);
datalayer.system.info.battery_protocol[63] = '\0'; datalayer.system.info.battery_protocol[63] = '\0';
datalayer_battery->info.number_of_cells = 2; datalayer_battery->info.number_of_cells = 2;
datalayer_battery->info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer_battery->info.max_design_voltage_dV = user_selected_max_pack_voltage_dV;
datalayer_battery->info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; datalayer_battery->info.min_design_voltage_dV = user_selected_min_pack_voltage_dV;
datalayer_battery->info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; datalayer_battery->info.max_cell_voltage_mV = user_selected_max_cell_voltage_mV;
datalayer_battery->info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; datalayer_battery->info.min_cell_voltage_mV = user_selected_min_cell_voltage_mV;
datalayer.battery2.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; datalayer.battery2.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;

View file

@ -32,11 +32,6 @@ class PylonBattery : public CanBattery {
static constexpr const char* Name = "Pylon compatible battery"; static constexpr const char* Name = "Pylon compatible battery";
private: private:
/* Change the following to suit your battery */
static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 1500;
static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value
static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value
static const int MAX_CELL_DEVIATION_MV = 150; static const int MAX_CELL_DEVIATION_MV = 150;
DATALAYER_BATTERY_TYPE* datalayer_battery; DATALAYER_BATTERY_TYPE* datalayer_battery;

View file

@ -1,4 +1,5 @@
#include "RJXZS-BMS.h" #include "RJXZS-BMS.h"
#include "../battery/BATTERIES.h"
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
@ -511,9 +512,9 @@ void RjxzsBms::transmit_can(unsigned long currentMillis) {
void RjxzsBms::setup(void) { // Performs one time setup at startup void RjxzsBms::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, Name, 63); strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0'; datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.max_design_voltage_dV = user_selected_max_pack_voltage_dV;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = user_selected_min_pack_voltage_dV;
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; datalayer.battery.info.max_cell_voltage_mV = user_selected_max_cell_voltage_mV;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; datalayer.battery.info.min_cell_voltage_mV = user_selected_min_cell_voltage_mV;
datalayer.system.status.battery_allows_contactor_closing = true; datalayer.system.status.battery_allows_contactor_closing = true;
} }

View file

@ -20,11 +20,6 @@ class RjxzsBms : public CanBattery {
private: private:
/* Tweak these according to your battery build */ /* Tweak these according to your battery build */
static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 1500;
static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value
static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value
static const int MAX_CELL_DEVIATION_MV = 250;
static const int MAX_DISCHARGE_POWER_ALLOWED_W = 5000; static const int MAX_DISCHARGE_POWER_ALLOWED_W = 5000;
static const int MAX_CHARGE_POWER_ALLOWED_W = 5000; static const int MAX_CHARGE_POWER_ALLOWED_W = 5000;
static const int MAX_CHARGE_POWER_WHEN_TOPBALANCING_W = 500; static const int MAX_CHARGE_POWER_WHEN_TOPBALANCING_W = 500;

View file

@ -1,4 +1,5 @@
#include "SIMPBMS-BATTERY.h" #include "SIMPBMS-BATTERY.h"
#include "../battery/BATTERIES.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
@ -93,9 +94,9 @@ void SimpBmsBattery::transmit_can(unsigned long currentMillis) {
void SimpBmsBattery::setup(void) { // Performs one time setup at startup void SimpBmsBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, Name, 63); strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0'; datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.max_design_voltage_dV = user_selected_max_pack_voltage_dV;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = user_selected_min_pack_voltage_dV;
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; datalayer.battery.info.max_cell_voltage_mV = user_selected_max_cell_voltage_mV;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; datalayer.battery.info.min_cell_voltage_mV = user_selected_min_cell_voltage_mV;
datalayer.system.status.battery_allows_contactor_closing = true; datalayer.system.status.battery_allows_contactor_closing = true;
} }

View file

@ -16,13 +16,6 @@ class SimpBmsBattery : public CanBattery {
static constexpr const char* Name = "SIMPBMS battery"; static constexpr const char* Name = "SIMPBMS battery";
private: private:
/* DEFAULT VALUES BMS will send configured */
static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 1500;
static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value
static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value
static const int MAX_CELL_DEVIATION_MV = 500;
static const int SIMPBMS_MAX_CELLS = 128; static const int SIMPBMS_MAX_CELLS = 128;
unsigned long previousMillis1000 = 0; // will store last time a 1s CAN Message was sent unsigned long previousMillis1000 = 0; // will store last time a 1s CAN Message was sent

View file

@ -1,4 +1,5 @@
#include "comm_nvm.h" #include "comm_nvm.h"
#include "../../battery/BATTERIES.h"
#include "../../battery/Battery.h" #include "../../battery/Battery.h"
#include "../../battery/Shunt.h" #include "../../battery/Shunt.h"
#include "../../charger/CanCharger.h" #include "../../charger/CanCharger.h"
@ -91,6 +92,10 @@ void init_stored_settings() {
user_selected_inverter_protocol = (InverterProtocolType)settings.getUInt("INVTYPE", (int)InverterProtocolType::None); user_selected_inverter_protocol = (InverterProtocolType)settings.getUInt("INVTYPE", (int)InverterProtocolType::None);
user_selected_charger_type = (ChargerType)settings.getUInt("CHGTYPE", (int)ChargerType::None); user_selected_charger_type = (ChargerType)settings.getUInt("CHGTYPE", (int)ChargerType::None);
user_selected_shunt_type = (ShuntType)settings.getUInt("SHUNTTYPE", (int)ShuntType::None); user_selected_shunt_type = (ShuntType)settings.getUInt("SHUNTTYPE", (int)ShuntType::None);
user_selected_max_pack_voltage_dV = settings.getUInt("BATTPVMAX", 0);
user_selected_min_pack_voltage_dV = settings.getUInt("BATTPVMIN", 0);
user_selected_max_cell_voltage_mV = settings.getUInt("BATTCVMAX", 0);
user_selected_min_cell_voltage_mV = settings.getUInt("BATTCVMIN", 0);
auto readIf = [](const char* settingName) { auto readIf = [](const char* settingName) {
auto batt1If = (comm_interface)settings.getUInt(settingName, (int)comm_interface::CanNative); auto batt1If = (comm_interface)settings.getUInt(settingName, (int)comm_interface::CanNative);

View file

@ -306,6 +306,22 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti
} }
} }
if (var == "BATTPVMAX") {
return String(static_cast<float>(settings.getUInt("BATTPVMAX", 0)) / 10.0, 1);
}
if (var == "BATTPVMIN") {
return String(static_cast<float>(settings.getUInt("BATTPVMIN", 0)) / 10.0, 1);
}
if (var == "BATTCVMAX") {
return String(settings.getUInt("BATTCVMAX", 0));
}
if (var == "BATTCVMIN") {
return String(settings.getUInt("BATTCVMIN", 0));
}
if (var == "BATTERY_WH_MAX") { if (var == "BATTERY_WH_MAX") {
return String(datalayer.battery.info.total_capacity_Wh); return String(datalayer.battery.info.total_capacity_Wh);
} }
@ -593,23 +609,13 @@ const char* getCANInterfaceName(CAN_Interface interface) {
function goToMainPage() { window.location.href = '/'; } function goToMainPage() { window.location.href = '/'; }
function toggleMqtt() { document.querySelectorAll('select,input').forEach(function(sel) {
var mqttEnabled = document.querySelector('input[name="MQTTENABLED"]').checked; function ch() {
document.querySelectorAll('.mqtt-settings').forEach(function (el) { sel.closest('form').setAttribute('data-' + sel.name?.toLowerCase(), sel.type=='checkbox'?sel.checked:sel.value);
el.style.display = mqttEnabled ? 'contents' : 'none'; }
}); sel.addEventListener('change', ch);
} ch();
});
document.addEventListener('DOMContentLoaded', toggleMqtt);
function toggleTopics() {
var topicsEnabled = document.querySelector('input[name="MQTTTOPICS"]').checked;
document.querySelectorAll('.mqtt-topics').forEach(function (el) {
el.style.display = topicsEnabled ? 'contents' : 'none';
});
}
document.addEventListener('DOMContentLoaded', toggleTopics);
</script> </script>
)rawliteral" )rawliteral"
@ -621,7 +627,7 @@ const char* getCANInterfaceName(CAN_Interface interface) {
cursor: pointer; border-radius: 10px; } cursor: pointer; border-radius: 10px; }
button:hover { background-color: #3A4A52; } button:hover { background-color: #3A4A52; }
h4 { margin: 0.6em 0; line-height: 1.2; } h4 { margin: 0.6em 0; line-height: 1.2; }
select { max-width: 250px; } select, input { max-width: 250px; box-sizing: border-box; }
.hidden { .hidden {
display: none; display: none;
} }
@ -641,6 +647,32 @@ const char* getCANInterfaceName(CAN_Interface interface) {
grid-column: span 2; grid-column: span 2;
} }
form .if-battery, form .if-inverter, form .if-charger, form .if-shunt { display: contents; }
form[data-battery="0"] .if-battery { display: none; }
form[data-inverter="0"] .if-inverter { display: none; }
form[data-charger="0"] .if-charger { display: none; }
form[data-shunt="0"] .if-shunt { display: none; }
form .if-cbms { display: none; }
form[data-battery="6"] .if-cbms, form[data-battery="11"] .if-cbms, form[data-battery="22"] .if-cbms, form[data-battery="23"] .if-cbms, form[data-battery="24"] .if-cbms, form[data-battery="31"] .if-cbms {
display: contents;
}
form .if-dblbtr { display: none; }
form[data-dblbtr="true"] .if-dblbtr {
display: contents;
}
form .if-mqtt { display: none; }
form[data-mqttenabled="true"] .if-mqtt {
display: contents;
}
form .if-topics { display: none; }
form[data-mqtttopics="true"] .if-topics {
display: contents;
}
</style> </style>
)rawliteral" )rawliteral"
@ -654,13 +686,13 @@ const char* getCANInterfaceName(CAN_Interface interface) {
<div style='background-color: #404E47; padding: 10px; margin-bottom: 10px;border-radius: 50px; class="%COMMONIMAGEDIVCLASS%"> <div style='background-color: #404E47; padding: 10px; margin-bottom: 10px;border-radius: 50px; class="%COMMONIMAGEDIVCLASS%">
<div style='max-width: 500px;'> <div style='max-width: 500px;'>
<form action='saveSettings' method='post' style='display: grid; grid-template-columns: 1fr 2fr; gap: 10px; <form action='saveSettings' method='post' style='display: grid; grid-template-columns: 1fr 1.5fr; gap: 10px; align-items: center;'>
align-items: center;'>
<label for='battery'>Battery: </label><select name='battery' if='battery'> <label for='battery'>Battery: </label><select name='battery' if='battery'>
%BATTTYPE% %BATTTYPE%
</select> </select>
<div class="if-battery">
<label for='BATTCOMM'>Battery comm I/F: </label><select name='BATTCOMM' id='BATTCOMM'> <label for='BATTCOMM'>Battery comm I/F: </label><select name='BATTCOMM' id='BATTCOMM'>
%BATTCOMM% %BATTCOMM%
</select> </select>
@ -668,30 +700,51 @@ const char* getCANInterfaceName(CAN_Interface interface) {
<label>Battery chemistry: </label><select name='BATTCHEM'> <label>Battery chemistry: </label><select name='BATTCHEM'>
%BATTCHEM% %BATTCHEM%
</select> </select>
</div>
<div class="if-cbms">
<label>Battery max design voltage (V): </label>
<input name='BATTPVMAX' pattern="^[0-9]+(\.[0-9]+)?$" type='text' value='%BATTPVMAX%' />
<label>Battery min design voltage (V): </label>
<input name='BATTPVMIN' pattern="^[0-9]+(\.[0-9]+)?$" type='text' value='%BATTPVMIN%' />
<label>Cell max design voltage (mV): </label>
<input name='BATTCVMAX' pattern="^[0-9]+$" type='text' value='%BATTCVMAX%' />
<label>Cell min design voltage (mV): </label>
<input name='BATTCVMIN' pattern="^[0-9]+$" type='text' value='%BATTCVMIN%' />
</div>
<label>Inverter protocol: </label><select name='inverter'> <label>Inverter protocol: </label><select name='inverter'>
%INVTYPE% %INVTYPE%
</select> </select>
<div class="if-inverter">
<label>Inverter comm I/F: </label><select name='INVCOMM'> <label>Inverter comm I/F: </label><select name='INVCOMM'>
%INVCOMM% %INVCOMM%
</select> </select>
</div>
<label>Charger: </label><select name='charger'> <label>Charger: </label><select name='charger'>
%CHGTYPE% %CHGTYPE%
</select> </select>
<div class="if-charger">
<label>Charger comm I/F: </label><select name='CHGCOMM'> <label>Charger comm I/F: </label><select name='CHGCOMM'>
%CHGCOMM% %CHGCOMM%
</select> </select>
</div>
<label>Shunt: </label><select name='SHUNT'> <label>Shunt: </label><select name='SHUNT'>
%SHUNTTYPE% %SHUNTTYPE%
</select> </select>
<div class="if-shunt">
<label>Shunt comm I/F: </label><select name='SHUNTCOMM'> <label>Shunt comm I/F: </label><select name='SHUNTCOMM'>
%SHUNTCOMM% %SHUNTCOMM%
</select> </select>
</div>
<label>Equipment stop button: </label><select name='EQSTOP'> <label>Equipment stop button: </label><select name='EQSTOP'>
%EQSTOP% %EQSTOP%
@ -700,15 +753,19 @@ const char* getCANInterfaceName(CAN_Interface interface) {
<label>Double battery: </label> <label>Double battery: </label>
<input type='checkbox' name='DBLBTR' value='on' style='margin-left: 0;' %DBLBTR% /> <input type='checkbox' name='DBLBTR' value='on' style='margin-left: 0;' %DBLBTR% />
<div class="if-dblbtr">
<label>Battery 2 comm I/F: </label><select name='BATT2COMM'> <label>Battery 2 comm I/F: </label><select name='BATT2COMM'>
%BATT2COMM% %BATT2COMM%
</select> </select>
</div>
<label>Contactor control: </label> <label>Contactor control: </label>
<input type='checkbox' name='CNTCTRL' value='on' style='margin-left: 0;' %CNTCTRL% /> <input type='checkbox' name='CNTCTRL' value='on' style='margin-left: 0;' %CNTCTRL% />
<div class="if-dblbtr">
<label>Contactor control double battery: </label> <label>Contactor control double battery: </label>
<input type='checkbox' name='CNTCTRLDBL' value='on' style='margin-left: 0;' %CNTCTRLDBL% /> <input type='checkbox' name='CNTCTRLDBL' value='on' style='margin-left: 0;' %CNTCTRLDBL% />
</div>
<label>PWM contactor control: </label> <label>PWM contactor control: </label>
<input type='checkbox' name='PWMCNTCTRL' value='on' style='margin-left: 0;' %PWMCNTCTRL% /> <input type='checkbox' name='PWMCNTCTRL' value='on' style='margin-left: 0;' %PWMCNTCTRL% />
@ -726,26 +783,26 @@ const char* getCANInterfaceName(CAN_Interface interface) {
<input type='checkbox' name='WIFIAPENABLED' value='on' style='margin-left: 0;' %WIFIAPENABLED% /> <input type='checkbox' name='WIFIAPENABLED' value='on' style='margin-left: 0;' %WIFIAPENABLED% />
<label>Custom hostname: </label> <label>Custom hostname: </label>
<input style='max-width: 250px;' type='text' name='HOSTNAME' value="%HOSTNAME%" /> <input type='text' name='HOSTNAME' value="%HOSTNAME%" />
<label>Enable MQTT: </label> <label>Enable MQTT: </label>
<input type='checkbox' name='MQTTENABLED' value='on' onchange='toggleMqtt()' style='margin-left: 0;' %MQTTENABLED% /> <input type='checkbox' name='MQTTENABLED' value='on' style='margin-left: 0;' %MQTTENABLED% />
<div class='mqtt-settings'> <div class='if-mqtt'>
<label>MQTT server: </label><input style='max-width: 250px;' type='text' name='MQTTSERVER' value="%MQTTSERVER%" /> <label>MQTT server: </label><input type='text' name='MQTTSERVER' value="%MQTTSERVER%" />
<label>MQTT port: </label><input style='max-width: 250px;' type='text' name='MQTTPORT' value="%MQTTPORT%" /> <label>MQTT port: </label><input type='text' name='MQTTPORT' value="%MQTTPORT%" />
<label>MQTT user: </label><input style='max-width: 250px;' type='text' name='MQTTUSER' value="%MQTTUSER%" /> <label>MQTT user: </label><input type='text' name='MQTTUSER' value="%MQTTUSER%" />
<label>MQTT password: </label><input style='max-width: 250px;' type='password' name='MQTTPASSWORD' value="%MQTTPASSWORD%" /> <label>MQTT password: </label><input type='password' name='MQTTPASSWORD' value="%MQTTPASSWORD%" />
<label>Customized MQTT topics: </label> <label>Customized MQTT topics: </label>
<input type='checkbox' name='MQTTTOPICS' value='on' onchange='toggleTopics()' style='margin-left: 0;' %MQTTTOPICS% /> <input type='checkbox' name='MQTTTOPICS' value='on' style='margin-left: 0;' %MQTTTOPICS% />
<div class='mqtt-topics'> <div class='if-topics'>
<label>MQTT topic name: </label><input style='max-width: 250px;' type='text' name='MQTTTOPIC' value="%MQTTTOPIC%" /> <label>MQTT topic name: </label><input type='text' name='MQTTTOPIC' value="%MQTTTOPIC%" />
<label>Prefix for MQTT object ID: </label><input style='max-width: 250px;' type='text' name='MQTTOBJIDPREFIX' value="%MQTTOBJIDPREFIX%" /> <label>Prefix for MQTT object ID: </label><input type='text' name='MQTTOBJIDPREFIX' value="%MQTTOBJIDPREFIX%" />
<label>HA device name: </label><input style='max-width: 250px;' type='text' name='MQTTDEVICENAME' value="%MQTTDEVICENAME%" /> <label>HA device name: </label><input type='text' name='MQTTDEVICENAME' value="%MQTTDEVICENAME%" />
<label>HA device ID: </label><input style='max-width: 250px;' type='text' name='HADEVICEID' value="%HADEVICEID%" /> <label>HA device ID: </label><input type='text' name='HADEVICEID' value="%HADEVICEID%" />
</div> </div>