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);