diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp
index 582316d8..91adc099 100644
--- a/Software/src/communication/nvm/comm_nvm.cpp
+++ b/Software/src/communication/nvm/comm_nvm.cpp
@@ -96,6 +96,13 @@ void init_stored_settings() {
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);
+ user_selected_inverter_cells = settings.getUInt("INVCELLS", 0);
+ user_selected_inverter_modules = settings.getUInt("INVMODULES", 0);
+ user_selected_inverter_cells_per_module = settings.getUInt("INVCELLSPER", 0);
+ user_selected_inverter_voltage_level = settings.getUInt("INVVLEVEL", 0);
+ user_selected_inverter_ah_capacity = settings.getUInt("INVAHCAPACITY", 0);
+ user_selected_inverter_battery_type = settings.getUInt("INVBTYPE", 0);
+ user_selected_inverter_ignore_contactors = settings.getBool("INVICNT", false);
auto readIf = [](const char* settingName) {
auto batt1If = (comm_interface)settings.getUInt(settingName, (int)comm_interface::CanNative);
@@ -193,10 +200,7 @@ void store_settings() {
if (!settings.putUInt("TARGETDISCHVOLT", datalayer.battery.settings.max_user_set_discharge_voltage_dV)) {
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);
- }
- if (!settings.putUInt("BMSRESETDUR", datalayer.battery.settings.sofar_user_specified_battery_id)) {
+ if (!settings.putUInt("BMSRESETDUR", datalayer.battery.settings.user_set_bms_reset_duration_ms)) {
set_event(EVENT_PERSISTENT_SAVE_INFO, 13);
}
diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp
index e9e52804..7ca338ce 100644
--- a/Software/src/devboard/webserver/settings_html.cpp
+++ b/Software/src/devboard/webserver/settings_html.cpp
@@ -468,6 +468,38 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti
return String(datalayer.charger.charger_setpoint_HV_IDC, 1);
}
+ if (var == "SOFAR_ID") {
+ return String(settings.getUInt("SOFAR_ID", 0));
+ }
+
+ if (var == "INVCELLS") {
+ return String(settings.getUInt("INVCELLS", 0));
+ }
+
+ if (var == "INVMODULES") {
+ return String(settings.getUInt("INVMODULES", 0));
+ }
+
+ if (var == "INVCELLSPER") {
+ return String(settings.getUInt("INVCELLSPER", 0));
+ }
+
+ if (var == "INVVLEVEL") {
+ return String(settings.getUInt("INVVLEVEL", 0));
+ }
+
+ if (var == "INVCAPACITY") {
+ return String(settings.getUInt("INVCAPACITY", 0));
+ }
+
+ if (var == "INVBTYPE") {
+ return String(settings.getUInt("INVBTYPE", 0));
+ }
+
+ if (var == "INVICNT") {
+ return settings.getBool("INVICNT") ? "checked" : "";
+ }
+
return String();
}
@@ -527,10 +559,6 @@ const char* getCANInterfaceName(CAN_Interface interface) {
function editPassword(){var value=prompt('Enter new password:');if(value!==null){var xhr=new
XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/updatePassword?value='+encodeURIComponent(value),true);xhr.send();}}
- function editSofarID(){var value=prompt('For double battery setups. Which battery ID should this emulator send? Remember to reboot after configuring this! 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.');}}}
-
function editWh(){var value=prompt('How much energy the battery can store. Enter new Wh value (1-400000):');
if(value!==null){if(value>=1&&value<=400000){var xhr=new
XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/updateBatterySize?value='+value,true);xhr.send();}else{
@@ -663,6 +691,21 @@ const char* getCANInterfaceName(CAN_Interface interface) {
display: contents;
}
+ form .if-sofar { display: none; }
+ form[data-inverter="17"] .if-sofar {
+ display: contents;
+ }
+
+ form .if-pylonish { display: none; }
+ form[data-inverter="4"] .if-pylonish, form[data-inverter="10"] .if-pylonish, form[data-inverter="19"] .if-pylonish {
+ display: contents;
+ }
+
+ form .if-solax { display: none; }
+ form[data-inverter="18"] .if-solax {
+ display: contents;
+ }
+
form .if-mqtt { display: none; }
form[data-mqttenabled="true"] .if-mqtt {
display: contents;
@@ -726,6 +769,40 @@ const char* getCANInterfaceName(CAN_Interface interface) {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -826,8 +903,6 @@ const char* getCANInterfaceName(CAN_Interface interface) {
Battery interface: %BATTERY2INTF%
Inverter interface: %INVINTF%
-
- Battery ID: %INVBID%
Shunt interface: %SHUNTINTF%
diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp
index b431ce2d..c40373f3 100644
--- a/Software/src/devboard/webserver/webserver.cpp
+++ b/Software/src/devboard/webserver/webserver.cpp
@@ -414,7 +414,7 @@ void init_webserver() {
const char* boolSettingNames[] = {
"DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET", "REMBMSRESET",
- "CANFDASCAN", "WIFIAPENABLED", "MQTTENABLED", "HADISC", "MQTTTOPICS",
+ "CANFDASCAN", "WIFIAPENABLED", "MQTTENABLED", "HADISC", "MQTTTOPICS", "INVICNT",
};
// Handles the form POST from UI to save settings of the common image
@@ -494,6 +494,27 @@ void init_webserver() {
settings.saveString("MQTTDEVICENAME", p->value().c_str());
} else if (p->name() == "HADEVICEID") {
settings.saveString("HADEVICEID", p->value().c_str());
+ } else if (p->name() == "SOFAR_ID") {
+ auto type = atoi(p->value().c_str());
+ settings.saveUInt("SOFAR_ID", type);
+ } else if (p->name() == "INVCELLS") {
+ auto type = atoi(p->value().c_str());
+ settings.saveUInt("INVCELLS", type);
+ } else if (p->name() == "INVMODULES") {
+ auto type = atoi(p->value().c_str());
+ settings.saveUInt("INVMODULES", type);
+ } else if (p->name() == "INVCELLSPER") {
+ auto type = atoi(p->value().c_str());
+ settings.saveUInt("INVCELLSPER", type);
+ } else if (p->name() == "INVVLEVEL") {
+ auto type = atoi(p->value().c_str());
+ settings.saveUInt("INVVLEVEL", type);
+ } else if (p->name() == "INVCAPACITY") {
+ auto type = atoi(p->value().c_str());
+ settings.saveUInt("INVCAPACITY", type);
+ } else if (p->name() == "INVBTYPE") {
+ auto type = atoi(p->value().c_str());
+ settings.saveUInt("INVBTYPE", (int)type);
}
for (auto& boolSetting : boolSettings) {
@@ -579,10 +600,6 @@ void init_webserver() {
update_string_setting(route, [setter](String value) { setter(value.toInt()); });
};
- // Route for editing Sofar ID
- update_int_setting("/updateSofarID",
- [](int value) { datalayer.battery.settings.sofar_user_specified_battery_id = value; });
-
// Route for editing Wh
update_int_setting("/updateBatterySize", [](int value) { datalayer.battery.info.total_capacity_Wh = value; });
diff --git a/Software/src/inverter/FERROAMP-CAN.cpp b/Software/src/inverter/FERROAMP-CAN.cpp
index 0ecdf69e..cb9757d5 100644
--- a/Software/src/inverter/FERROAMP-CAN.cpp
+++ b/Software/src/inverter/FERROAMP-CAN.cpp
@@ -1,6 +1,7 @@
#include "FERROAMP-CAN.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
+#include "../inverter/INVERTERS.h"
//#define SEND_0 //If defined, the messages will have ID ending with 0 (useful for some inverters)
#define SEND_1 //If defined, the messages will have ID ending with 1 (useful for some inverters)
@@ -364,3 +365,38 @@ void FerroampCanInverter::send_system_data() { //System equipment information
transmit_can_frame(&PYLON_4291);
#endif
}
+
+bool FerroampCanInverter::setup() {
+ if (user_selected_inverter_cells > 0) {
+ PYLON_7320.data.u8[0] = user_selected_inverter_cells & 0xff;
+ PYLON_7320.data.u8[1] = (uint8_t)(user_selected_inverter_cells >> 8);
+ PYLON_7321.data.u8[0] = user_selected_inverter_cells & 0xff;
+ PYLON_7321.data.u8[1] = (uint8_t)(user_selected_inverter_cells >> 8);
+ }
+
+ if (user_selected_inverter_modules > 0) {
+ PYLON_7320.data.u8[2] = user_selected_inverter_modules;
+ PYLON_7321.data.u8[2] = user_selected_inverter_modules;
+ }
+
+ if (user_selected_inverter_cells_per_module > 0) {
+ PYLON_7320.data.u8[3] = user_selected_inverter_cells_per_module;
+ PYLON_7321.data.u8[3] = user_selected_inverter_cells_per_module;
+ }
+
+ if (user_selected_inverter_voltage_level > 0) {
+ PYLON_7320.data.u8[4] = user_selected_inverter_voltage_level & 0xff;
+ PYLON_7320.data.u8[5] = (uint8_t)(user_selected_inverter_voltage_level >> 8);
+ PYLON_7321.data.u8[4] = user_selected_inverter_voltage_level & 0xff;
+ PYLON_7321.data.u8[5] = (uint8_t)(user_selected_inverter_voltage_level >> 8);
+ }
+
+ if (user_selected_inverter_ah_capacity > 0) {
+ PYLON_7320.data.u8[6] = user_selected_inverter_ah_capacity & 0xff;
+ PYLON_7320.data.u8[7] = (uint8_t)(user_selected_inverter_ah_capacity >> 8);
+ PYLON_7321.data.u8[6] = user_selected_inverter_ah_capacity & 0xff;
+ PYLON_7321.data.u8[7] = (uint8_t)(user_selected_inverter_ah_capacity >> 8);
+ }
+
+ return true;
+}
diff --git a/Software/src/inverter/FERROAMP-CAN.h b/Software/src/inverter/FERROAMP-CAN.h
index f45dabc4..00ec78bd 100644
--- a/Software/src/inverter/FERROAMP-CAN.h
+++ b/Software/src/inverter/FERROAMP-CAN.h
@@ -10,6 +10,7 @@
class FerroampCanInverter : public CanInverterProtocol {
public:
const char* name() override { return Name; }
+ bool setup() override;
void update_values();
void transmit_can(unsigned long currentMillis);
void map_can_frame_to_variable(CAN_frame rx_frame);
diff --git a/Software/src/inverter/INVERTERS.cpp b/Software/src/inverter/INVERTERS.cpp
index aa762e75..cbd64f4b 100644
--- a/Software/src/inverter/INVERTERS.cpp
+++ b/Software/src/inverter/INVERTERS.cpp
@@ -4,6 +4,17 @@ InverterProtocol* inverter = nullptr;
InverterProtocolType user_selected_inverter_protocol = InverterProtocolType::BydModbus;
+// Some user-configurable settings that can be used by inverters. These
+// inverters should use sensible defaults if the corresponding user_selected
+// value is zero.
+uint16_t user_selected_inverter_cells = 0;
+uint16_t user_selected_inverter_modules = 0;
+uint16_t user_selected_inverter_cells_per_module = 0;
+uint16_t user_selected_inverter_voltage_level = 0;
+uint16_t user_selected_inverter_ah_capacity = 0;
+uint16_t user_selected_inverter_battery_type = 0;
+bool user_selected_inverter_ignore_contactors = false;
+
std::vector supported_inverter_protocols() {
std::vector types;
diff --git a/Software/src/inverter/INVERTERS.h b/Software/src/inverter/INVERTERS.h
index c706c8a9..b8207f7d 100644
--- a/Software/src/inverter/INVERTERS.h
+++ b/Software/src/inverter/INVERTERS.h
@@ -36,4 +36,12 @@ extern InverterProtocol* inverter;
// Call to initialize the build-time selected inverter. Safe to call even though inverter was not selected.
bool setup_inverter();
+extern uint16_t user_selected_inverter_cells;
+extern uint16_t user_selected_inverter_modules;
+extern uint16_t user_selected_inverter_cells_per_module;
+extern uint16_t user_selected_inverter_voltage_level;
+extern uint16_t user_selected_inverter_ah_capacity;
+extern uint16_t user_selected_inverter_battery_type;
+extern bool user_selected_inverter_ignore_contactors;
+
#endif
diff --git a/Software/src/inverter/InverterProtocol.h b/Software/src/inverter/InverterProtocol.h
index 3e48ea70..7b9a3f5f 100644
--- a/Software/src/inverter/InverterProtocol.h
+++ b/Software/src/inverter/InverterProtocol.h
@@ -5,27 +5,27 @@
enum class InverterProtocolType {
None = 0,
- AforeCan,
- BydCan,
- BydModbus,
- FerroampCan,
- Foxess,
- GrowattHv,
- GrowattLv,
- GrowattWit,
- Kostal,
- Pylon,
- PylonLv,
- Schneider,
- SmaBydH,
- SmaBydHvs,
- SmaLv,
- SmaTripower,
- Sofar,
- Solax,
- Solxpow,
- SolArkLv,
- Sungrow,
+ AforeCan = 1,
+ BydCan = 2,
+ BydModbus = 3,
+ FerroampCan = 4,
+ Foxess = 5,
+ GrowattHv = 6,
+ GrowattLv = 7,
+ GrowattWit = 8,
+ Kostal = 9,
+ Pylon = 10,
+ PylonLv = 11,
+ Schneider = 12,
+ SmaBydH = 13,
+ SmaBydHvs = 14,
+ SmaLv = 15,
+ SmaTripower = 16,
+ Sofar = 17,
+ Solax = 18,
+ Solxpow = 19,
+ SolArkLv = 20,
+ Sungrow = 21,
Highest
};
diff --git a/Software/src/inverter/PYLON-CAN.cpp b/Software/src/inverter/PYLON-CAN.cpp
index 293e53cf..dabd778f 100644
--- a/Software/src/inverter/PYLON-CAN.cpp
+++ b/Software/src/inverter/PYLON-CAN.cpp
@@ -1,6 +1,7 @@
#include "PYLON-CAN.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
+#include "../inverter/INVERTERS.h"
#define SEND_0 //If defined, the messages will have ID ending with 0 (useful for some inverters)
//#define SEND_1 //If defined, the messages will have ID ending with 1 (useful for some inverters)
@@ -351,3 +352,38 @@ void PylonInverter::send_system_data() { //System equipment information
transmit_can_frame(&PYLON_4291);
#endif
}
+
+bool PylonInverter::setup() {
+ if (user_selected_inverter_cells > 0) {
+ PYLON_7320.data.u8[0] = user_selected_inverter_cells & 0xff;
+ PYLON_7320.data.u8[1] = (uint8_t)(user_selected_inverter_cells >> 8);
+ PYLON_7321.data.u8[0] = user_selected_inverter_cells & 0xff;
+ PYLON_7321.data.u8[1] = (uint8_t)(user_selected_inverter_cells >> 8);
+ }
+
+ if (user_selected_inverter_modules > 0) {
+ PYLON_7320.data.u8[2] = user_selected_inverter_modules;
+ PYLON_7321.data.u8[2] = user_selected_inverter_modules;
+ }
+
+ if (user_selected_inverter_cells_per_module > 0) {
+ PYLON_7320.data.u8[3] = user_selected_inverter_cells_per_module;
+ PYLON_7321.data.u8[3] = user_selected_inverter_cells_per_module;
+ }
+
+ if (user_selected_inverter_voltage_level > 0) {
+ PYLON_7320.data.u8[4] = user_selected_inverter_voltage_level & 0xff;
+ PYLON_7320.data.u8[5] = (uint8_t)(user_selected_inverter_voltage_level >> 8);
+ PYLON_7321.data.u8[4] = user_selected_inverter_voltage_level & 0xff;
+ PYLON_7321.data.u8[5] = (uint8_t)(user_selected_inverter_voltage_level >> 8);
+ }
+
+ if (user_selected_inverter_ah_capacity > 0) {
+ PYLON_7320.data.u8[6] = user_selected_inverter_ah_capacity & 0xff;
+ PYLON_7320.data.u8[7] = (uint8_t)(user_selected_inverter_ah_capacity >> 8);
+ PYLON_7321.data.u8[6] = user_selected_inverter_ah_capacity & 0xff;
+ PYLON_7321.data.u8[7] = (uint8_t)(user_selected_inverter_ah_capacity >> 8);
+ }
+
+ return true;
+}
diff --git a/Software/src/inverter/PYLON-CAN.h b/Software/src/inverter/PYLON-CAN.h
index 5c55b039..33a8b1b6 100644
--- a/Software/src/inverter/PYLON-CAN.h
+++ b/Software/src/inverter/PYLON-CAN.h
@@ -10,6 +10,7 @@
class PylonInverter : public CanInverterProtocol {
public:
const char* name() override { return Name; }
+ bool setup() override;
void update_values();
void transmit_can(unsigned long currentMillis);
void map_can_frame_to_variable(CAN_frame rx_frame);
diff --git a/Software/src/inverter/SOLAX-CAN.cpp b/Software/src/inverter/SOLAX-CAN.cpp
index a3f77a3e..16df4f2a 100644
--- a/Software/src/inverter/SOLAX-CAN.cpp
+++ b/Software/src/inverter/SOLAX-CAN.cpp
@@ -4,11 +4,7 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../devboard/utils/logging.h"
-
-#define NUMBER_OF_MODULES 0
-#define BATTERY_TYPE 0x50
-// If you are having BattVoltFault issues, configure the above values according to wiki page
-// https://github.com/dalathegreat/Battery-Emulator/wiki/Solax-inverters
+#include "../inverter/INVERTERS.h"
// __builtin_bswap64 needed to convert to ESP32 little endian format
// Byte[4] defines the requested contactor state: 1 = Closed , 0 = Open
@@ -18,7 +14,7 @@
void SolaxInverter::
update_values() { //This function maps all the values fetched from battery CAN to the correct CAN messages
// If not receiveing any communication from the inverter, open contactors and return to battery announce state
- if (millis() - LastFrameTime >= SolaxTimeout) {
+ if (millis() - LastFrameTime >= SolaxTimeout && !configured_ignore_contactors) {
datalayer.system.status.inverter_allows_contactor_closing = false;
STATE = BATTERY_ANNOUNCE;
}
@@ -73,8 +69,8 @@ void SolaxInverter::
//BMS_Status
SOLAX_1875.data.u8[0] = (uint8_t)temperature_average;
SOLAX_1875.data.u8[1] = (temperature_average >> 8);
- SOLAX_1875.data.u8[2] = (uint8_t)NUMBER_OF_MODULES; // Number of slave batteries
- SOLAX_1875.data.u8[4] = (uint8_t)0; // Contactor Status 0=off, 1=on.
+ SOLAX_1875.data.u8[2] = (uint8_t)configured_number_of_modules; // Number of slave batteries
+ SOLAX_1875.data.u8[4] = (uint8_t)0; // Contactor Status 0=off, 1=on.
//BMS_PackTemps (strange name, since it has voltages?)
SOLAX_1876.data.u8[0] = (int8_t)datalayer.battery.status.temperature_max_dC;
@@ -88,8 +84,8 @@ void SolaxInverter::
SOLAX_1876.data.u8[7] = (datalayer.battery.status.cell_min_voltage_mV >> 8);
//Unknown
- SOLAX_1877.data.u8[4] = (uint8_t)BATTERY_TYPE; // Battery type (Default 0x50)
- SOLAX_1877.data.u8[6] = (uint8_t)0x22; // Firmware version?
+ SOLAX_1877.data.u8[4] = (uint8_t)configured_battery_type; // Battery type (Default 0x50)
+ SOLAX_1877.data.u8[6] = (uint8_t)0x22; // Firmware version?
SOLAX_1877.data.u8[7] =
(uint8_t)0x02; // The above firmware version applies to:02 = Master BMS, 10 = S1, 20 = S2, 30 = S3, 40 = S4
@@ -129,6 +125,26 @@ void SolaxInverter::map_can_frame_to_variable(CAN_frame rx_frame) {
if (rx_frame.ID == 0x1871 && rx_frame.data.u8[0] == (0x01) ||
rx_frame.ID == 0x1871 && rx_frame.data.u8[0] == (0x02)) {
LastFrameTime = millis();
+
+ if (configured_ignore_contactors) {
+ // Skip the state machine since we're not going to open/close contactors,
+ // and the Solax would otherwise wait forever for us to do so.
+
+ datalayer.system.status.inverter_allows_contactor_closing = true;
+ SOLAX_1875.data.u8[4] = (0x01); // Inform Inverter: Contactor 0=off, 1=on.
+ transmit_can_frame(&SOLAX_187E);
+ transmit_can_frame(&SOLAX_187A);
+ transmit_can_frame(&SOLAX_1872);
+ transmit_can_frame(&SOLAX_1873);
+ transmit_can_frame(&SOLAX_1874);
+ transmit_can_frame(&SOLAX_1875);
+ transmit_can_frame(&SOLAX_1876);
+ transmit_can_frame(&SOLAX_1877);
+ transmit_can_frame(&SOLAX_1878);
+ transmit_can_frame(&SOLAX_100A001);
+ return;
+ }
+
switch (STATE) {
case (BATTERY_ANNOUNCE):
#ifdef DEBUG_LOG
@@ -208,7 +224,26 @@ void SolaxInverter::map_can_frame_to_variable(CAN_frame rx_frame) {
}
}
-bool SolaxInverter::setup(void) { // Performs one time setup at startup
- datalayer.system.status.inverter_allows_contactor_closing = false; // The inverter needs to allow first
+bool SolaxInverter::setup(void) { // Performs one time setup at startup
+ // Use user selected values if nonzero, otherwise use defaults
+ if (user_selected_inverter_modules > 0) {
+ configured_number_of_modules = user_selected_inverter_modules;
+ } else {
+ configured_number_of_modules = NUMBER_OF_MODULES;
+ }
+
+ if (user_selected_inverter_battery_type > 0) {
+ configured_battery_type = user_selected_inverter_battery_type;
+ } else {
+ configured_battery_type = BATTERY_TYPE;
+ }
+
+ configured_ignore_contactors = user_selected_inverter_ignore_contactors;
+
+ if (!configured_ignore_contactors) {
+ // Only prevent closing if we're not ignoring contactors
+ datalayer.system.status.inverter_allows_contactor_closing = false; // The inverter needs to allow first
+ }
+
return true;
}
diff --git a/Software/src/inverter/SOLAX-CAN.h b/Software/src/inverter/SOLAX-CAN.h
index 0a3d8efa..6a67e8bf 100644
--- a/Software/src/inverter/SOLAX-CAN.h
+++ b/Software/src/inverter/SOLAX-CAN.h
@@ -17,6 +17,11 @@ class SolaxInverter : public CanInverterProtocol {
static constexpr const char* Name = "SolaX Triple Power LFP over CAN bus";
private:
+ static const int NUMBER_OF_MODULES = 0;
+ static const int BATTERY_TYPE = 0x50;
+ // If you are having BattVoltFault issues, configure the above values according to wiki page
+ // https://github.com/dalathegreat/Battery-Emulator/wiki/Solax-inverters
+
// Timeout in milliseconds
static const int SolaxTimeout = 2000;
@@ -34,6 +39,13 @@ class SolaxInverter : public CanInverterProtocol {
uint16_t capped_capacity_Wh;
uint16_t capped_remaining_capacity_Wh;
+ int configured_number_of_modules = 0;
+ int configured_battery_type = 0;
+ // If true, the integration will ignore the inverter's requests to open the
+ // battery contactors. Useful for batteries that can't open contactors on
+ // request.
+ bool configured_ignore_contactors = false;
+
//CAN message translations from this amazing repository: https://github.com/rand12345/solax_can_bus
CAN_frame SOLAX_1801 = {.FD = false,
diff --git a/Software/src/inverter/SOLXPOW-CAN.cpp b/Software/src/inverter/SOLXPOW-CAN.cpp
index d6f2cdcc..b29946f1 100644
--- a/Software/src/inverter/SOLXPOW-CAN.cpp
+++ b/Software/src/inverter/SOLXPOW-CAN.cpp
@@ -1,6 +1,7 @@
#include "SOLXPOW-CAN.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
+#include "../inverter/INVERTERS.h"
#define SEND_0 //If defined, the messages will have ID ending with 0 (useful for some inverters)
//#define SEND_1 //If defined, the messages will have ID ending with 1 (useful for some inverters)
@@ -353,3 +354,38 @@ void SolxpowInverter::send_system_data() { //System equipment information
transmit_can_frame(&SOLXPOW_4291);
#endif
}
+
+bool SolxpowInverter::setup() {
+ if (user_selected_inverter_cells > 0) {
+ SOLXPOW_7320.data.u8[0] = user_selected_inverter_cells & 0xff;
+ SOLXPOW_7320.data.u8[1] = (uint8_t)(user_selected_inverter_cells >> 8);
+ SOLXPOW_7321.data.u8[0] = user_selected_inverter_cells & 0xff;
+ SOLXPOW_7321.data.u8[1] = (uint8_t)(user_selected_inverter_cells >> 8);
+ }
+
+ if (user_selected_inverter_modules > 0) {
+ SOLXPOW_7320.data.u8[2] = user_selected_inverter_modules;
+ SOLXPOW_7321.data.u8[2] = user_selected_inverter_modules;
+ }
+
+ if (user_selected_inverter_cells_per_module > 0) {
+ SOLXPOW_7320.data.u8[3] = user_selected_inverter_cells_per_module;
+ SOLXPOW_7321.data.u8[3] = user_selected_inverter_cells_per_module;
+ }
+
+ if (user_selected_inverter_voltage_level > 0) {
+ SOLXPOW_7320.data.u8[4] = user_selected_inverter_voltage_level & 0xff;
+ SOLXPOW_7320.data.u8[5] = (uint8_t)(user_selected_inverter_voltage_level >> 8);
+ SOLXPOW_7321.data.u8[4] = user_selected_inverter_voltage_level & 0xff;
+ SOLXPOW_7321.data.u8[5] = (uint8_t)(user_selected_inverter_voltage_level >> 8);
+ }
+
+ if (user_selected_inverter_ah_capacity > 0) {
+ SOLXPOW_7320.data.u8[6] = user_selected_inverter_ah_capacity & 0xff;
+ SOLXPOW_7320.data.u8[7] = (uint8_t)(user_selected_inverter_ah_capacity >> 8);
+ SOLXPOW_7321.data.u8[6] = user_selected_inverter_ah_capacity & 0xff;
+ SOLXPOW_7321.data.u8[7] = (uint8_t)(user_selected_inverter_ah_capacity >> 8);
+ }
+
+ return true;
+}
diff --git a/Software/src/inverter/SOLXPOW-CAN.h b/Software/src/inverter/SOLXPOW-CAN.h
index 73bc7e13..f45970ee 100644
--- a/Software/src/inverter/SOLXPOW-CAN.h
+++ b/Software/src/inverter/SOLXPOW-CAN.h
@@ -10,6 +10,7 @@
class SolxpowInverter : public CanInverterProtocol {
public:
const char* name() override { return Name; }
+ bool setup() override;
void update_values();
void transmit_can(unsigned long currentMillis);
void map_can_frame_to_variable(CAN_frame rx_frame);