Skip to content

Commit

Permalink
fix: VictronSmartShunt and Meanwell 24 Volt Charger
Browse files Browse the repository at this point in the history
  • Loading branch information
skippermeister committed Jul 25, 2024
1 parent f8c60e2 commit b6ae8da
Show file tree
Hide file tree
Showing 25 changed files with 278 additions and 127 deletions.
67 changes: 67 additions & 0 deletions docs/DeviceProfiles/FusionV2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
[
{
"name":"FusionV2 Shunt+1xMPPT+Meanwell",
"nrf24":{
"miso": 48,
"mosi": 35,
"clk": 36,
"irq": 47,
"en": 38,
"cs": 37
},
"cmt":{
"clk": 6,
"cs": 4,
"fcs": 21,
"sdio": 5,
"gpio2": 3,
"gpio3": 8
},
"display":{
"type": 0,
"data": -1,
"clk": -1,
"cs": -1,
"reset": -1,
"busy": 255,
"dc": 255
},
"led":{
"led0":17,
"led1":18
},
"victron":{
"rs232_rx": 11,
"rs232_tx": 12,
"rs232_rx2": -1,
"rs232_tx2": -1,
"rs232_rx3": -1,
"rs232_tx3": -1
},
"battery":{
"rs232_rx": 10,
"rs232_tx": 9,
"bms_wakeup": -1
},
"charger":{
"can0_rx": 12,
"can0_tx": 14
},
"mcp2515":{
"miso":-1,
"mosi":-1,
"clk":-1,
"irq":-1,
"cs":-1
},
"batteryConnectedInverter":{
"pre_charge": -1,
"full_power": -1
},
"powermeter":{
"sdm_rs485_tx": 40,
"sdm_rs485_rx": 39,
"sdm_rs485_rts": -1
}
}
]
31 changes: 31 additions & 0 deletions include/BatteryStats.h
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,37 @@ class VictronSmartShuntStats : public BatteryStats {

void updateFrom(VeDirectShuntController::data_t const& shuntData);

float getRecommendedChargeVoltageLimit() const final {
// FIXME
return Configuration.get().Battery.RecommendedChargeVoltage;
};
float getRecommendedDischargeVoltageLimit() const final {
// FIXME
return Configuration.get().Battery.RecommendedDischargeVoltage;
};
float getRecommendedChargeCurrentLimit() const final { return 50.0; }; // FIXME
float getRecommendedDischargeCurrentLimit() const final { return 50.0; }; // FIXME

bool getChargeImmediately() const final { return ( getSoC() < 5.0); }; // FIXME below 5%
bool getFullChargeRequest() const final { return _lastFullCharge > 24*60*45; }; // FIXME 45 days

bool isChargeTemperatureValid() const final
{
if (!_tempPresent) return true; // if temperature sensor not present always return true

const Battery_CONFIG_T& cBattery = Configuration.get().Battery;
return (_temperature >= static_cast<float>(cBattery.MinChargeTemperature))
&& (_temperature <= static_cast<float>(cBattery.MaxChargeTemperature));
};
bool isDischargeTemperatureValid() const final
{
if (!_tempPresent) return true; // if temperature sensor not present always return true

const Battery_CONFIG_T& cBattery = Configuration.get().Battery;
return (_temperature >= static_cast<float>(cBattery.MinDischargeTemperature))
&& (_temperature <= static_cast<float>(cBattery.MaxDischargeTemperature));
};

private:
float _voltage;
float _current;
Expand Down
9 changes: 9 additions & 0 deletions include/Configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,11 @@ struct Battery_CONFIG_T {
char VoltageTopic[MQTT_MAX_TOPIC_STRLEN + 1];
} Mqtt;
#endif
#if defined(USE_MQTT_BATTERY) || defined(USE_VICTRON_SMART_SHUNT)
float RecommendedChargeVoltage;
float RecommendedDischargeVoltage;
#endif

bool UpdatesOnly;
int8_t MinChargeTemperature;
int8_t MaxChargeTemperature;
Expand Down Expand Up @@ -303,6 +308,10 @@ struct MeanWell_CONFIG_T {
float MaxVoltage;
float MinCurrent;
float MaxCurrent;
float VoltageLimitMin;
float VoltageLimitMax;
float CurrentLimitMin;
float CurrentLimitMax;
float Hysteresis;
bool mustInverterProduce;
};
Expand Down
8 changes: 4 additions & 4 deletions include/MqttHandleHass.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,24 +58,24 @@ class MqttHandleHassClass {
void publishConfig();
void forceUpdate();

static String getDtuUniqueId();
static String getDtuUrl();

private:
void loop();
void publish(const String& subtopic, const String& payload);
void publishDtuSensor(const char* name, const char* device_class, const char* category, const char* icon, const char* unit_of_measure, const char* subTopic);
void publishDtuBinarySensor(const char* name, const char* device_class, const char* category, const char* payload_on, const char* payload_off, const char* subTopic = "");
void publishInverterField(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const byteAssign_fieldDeviceClass_t fieldType, const bool clear = false);
void publishInverterButton(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* icon, const char* category, const char* deviceClass, const char* subTopic, const char* payload);
void publishInverterNumber(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* icon, const char* category, const char* commandTopic, const char* stateTopic, const char* unitOfMeasure, const int16_t min = 1, const int16_t max = 100);
void publishInverterNumber(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* icon, const char* category, const char* commandTopic, const char* stateTopic, const char* unitOfMeasure, const int16_t min = 1, const int16_t max = 100, float step = 1.0);
void publishInverterBinarySensor(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* subTopic, const char* payload_on, const char* payload_off);

static void createInverterInfo(JsonDocument& doc, std::shared_ptr<InverterAbstract> inv);
static void createDtuInfo(JsonDocument& doc);

static void createDeviceInfo(JsonDocument& doc, const String& name, const String& identifiers, const String& configuration_url, const String& manufacturer, const String& model, const String& sw_version, const String& via_device = "");

static String getDtuUniqueId();
static String getDtuUrl();

Task _loopTask;

bool _wasConnected = false;
Expand Down
4 changes: 2 additions & 2 deletions include/MqttHandlePowerLimiterHass.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ class MqttHandlePowerLimiterHassClass {
private:
void loop();
void publish(const String& subtopic, const String& payload);
void publishNumber(const char* caption, const char* icon, const char* category, const char* commandTopic, const char* stateTopic, const char* unitOfMeasure, const int16_t min, const int16_t max);
void publishNumber(const char* caption, const char* icon, const char* category, const char* commandTopic, const char* stateTopic, const char* unitOfMeasure, const int16_t min, const int16_t max, const float step);
void publishSelect(const char* caption, const char* icon, const char* category, const char* commandTopic, const char* stateTopic);
void createDeviceInfo(JsonObject& object);
void createDeviceInfo(JsonDocument& root);

Task _loopTask;

Expand Down
2 changes: 1 addition & 1 deletion lib/Hoymiles/src/commands/ActivePowerControlCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ bool ActivePowerControlCommand::handleResponse(const fragment_t fragment[], cons

float ActivePowerControlCommand::getLimit() const
{
const uint16_t l = (((uint16_t)_payload[12] << 8) | _payload[13]);
const float l = (((uint16_t)_payload[12] << 8) | _payload[13]);
return l / 10;
}

Expand Down
2 changes: 1 addition & 1 deletion platformio_override.ini
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ build_flags = ${env.build_flags}
; -DUSE_JKBMS_CONTROLLER=1
; -DUSE_DALYBMS_CONTROLLER=1
; -DUSE_PYTES_CAN_RECEIVER=1
; -DUSE_VICTRON_SMART_SHUNT=1
-DUSE_VICTRON_SMART_SHUNT=1
-DUSE_PYLONTECH_RS485_RECEIVER=1
-DBATTERY_PIN_RX=16 ; RS485 HW Serial (Pylontech Battery via RS485, Daly BMS via RS485/RS232 or JK BMS via RS485/RS232)
-DBATTERY_PIN_TX=17
Expand Down
1 change: 1 addition & 0 deletions src/Battery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ void BatteryClass::updateSettings()
_upProvider = std::make_unique<DalyBmsController>();
break;
#endif

#ifdef USE_MQTT_BATTERY
case 6:
_upProvider = std::make_unique<MqttBattery>();
Expand Down
4 changes: 2 additions & 2 deletions src/BatteryStats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1349,10 +1349,10 @@ void VictronSmartShuntStats::getLiveViewData(JsonVariant& root) const

// values go into the "Status" card of the web application
addLiveViewValue(root, "current", _current, "A", 1);
addLiveViewValue(root, "chargeCycles", _chargeCycles, "", 0);
addLiveViewValue(root, "cycles", _chargeCycles, "", 0);
addLiveViewValue(root, "chargedEnergy", _chargedEnergy, "KWh", 1);
addLiveViewValue(root, "dischargedEnergy", _dischargedEnergy, "KWh", 1);
addLiveViewValue(root, "instantaneousPower", _instantaneousPower, "W", 0);
addLiveViewValue(root, "power", _instantaneousPower, "W", 0);
addLiveViewValue(root, "consumedAmpHours", _consumedAmpHours, "Ah", 3);
addLiveViewValue(root, "midpointVoltage", _midpointVoltage, "V", 2);
addLiveViewValue(root, "midpointDeviation", _midpointDeviation, "%", 1);
Expand Down
13 changes: 13 additions & 0 deletions src/Configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,10 @@ bool ConfigurationClass::write()
battery["min_discharge_temp"] = config.Battery.MinDischargeTemperature;
battery["max_discharge_temp"] = config.Battery.MaxDischargeTemperature;
battery["stop_charging_soc"] = config.Battery.Stop_Charging_BatterySoC_Threshold;
#if defined(USE_MQTT_BATTERY) || defined(USE_VICTRON_SMART_SHUNT)
battery["recommended_charge_voltage"] = config.Battery.RecommendedChargeVoltage;
battery["recommended_discharge_voltage"] = config.Battery.RecommendedDischargeVoltage;
#endif

JsonObject mcp2515 = doc["mcp2515"].to<JsonObject>();
mcp2515["can_controller_frequency"] = config.MCP2515.Controller_Frequency;
Expand Down Expand Up @@ -721,6 +725,11 @@ bool ConfigurationClass::read()
config.Battery.MaxDischargeTemperature = battery["max_discharge_temp"] | BATTERY_MAX_DISCHARGE_TEMPERATURE;
config.Battery.Stop_Charging_BatterySoC_Threshold = battery["stop_charging_soc"] | 100;

#if defined(USE_MQTT_BATTERY) || defined(USE_VICTRON_SMART_SHUNT)
config.Battery.RecommendedChargeVoltage = battery["recommended_charge_voltage"] | 28.4;
config.Battery.RecommendedDischargeVoltage = battery["recommended_discharge_voltage"] | 21.0;
#endif

JsonObject mcp2515 = doc["mcp2515"];
config.MCP2515.Controller_Frequency = mcp2515["can_controller_frequency"] | MCP2515_CAN_CONTROLLER_FREQUENCY;

Expand All @@ -742,6 +751,10 @@ bool ConfigurationClass::read()
config.MeanWell.MinCurrent = meanwell["min_current"] | MEANWELL_MINCURRENT;
config.MeanWell.MaxCurrent = meanwell["max_current"] | MEANWELL_MAXCURRENT;
config.MeanWell.Hysteresis = meanwell["hystersis"] | MEANWELL_HYSTERESIS;
config.MeanWell.CurrentLimitMin = 0.0;
config.MeanWell.CurrentLimitMax = 50.0;
config.MeanWell.VoltageLimitMin = 21.0;
config.MeanWell.VoltageLimitMax = 80.0;
config.MeanWell.mustInverterProduce = meanwell["mustInverterProduce"] | true;
#endif

Expand Down
79 changes: 43 additions & 36 deletions src/MeanWell_can.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,59 +427,66 @@ void MeanWellCanClass::onReceive(uint8_t* frame, uint8_t len)
MeanWell_CONFIG_T& cMeanWell = Configuration.get().MeanWell;
if (strcmp(_rp.ManufacturerModelName, "NPB-450-48") == 0) {
_model = NPB_Model_t::NPB_450_48;
cMeanWell.MinCurrent = 1.36f; // 1.36A
cMeanWell.MaxCurrent = 6.8f; // 6.8A
cMeanWell.MinVoltage = 42.0f;
cMeanWell.MaxVoltage = 80.0f;
cMeanWell.CurrentLimitMin = 1.36f; // 1.36A
cMeanWell.CurrentLimitMax = 6.8f; // 6.8A
cMeanWell.VoltageLimitMin = 42.0f;
cMeanWell.VoltageLimitMax = 80.0f;
} else if (strcmp(_rp.ManufacturerModelName, "NPB-750-48") == 0) {
_model = NPB_Model_t::NPB_750_48;
cMeanWell.MinCurrent = 2.26f; // 2.26A
cMeanWell.MaxCurrent = 11.3f; // 11.3A
cMeanWell.MinVoltage = 42.0f;
cMeanWell.MaxVoltage = 80.0f;
cMeanWell.CurrentLimitMin = 2.26f; // 2.26A
cMeanWell.CurrentLimitMax = 11.3f; // 11.3A
cMeanWell.VoltageLimitMin = 42.0f;
cMeanWell.VoltageLimitMax = 80.0f;
} else if (strcmp(_rp.ManufacturerModelName, "NPB-1200-48") == 0) {
_model = NPB_Model_t::NPB_1200_48;
cMeanWell.MinCurrent = 3.6f; // 3.6A
cMeanWell.MaxCurrent = 18.0f; // 18.0A
cMeanWell.MinVoltage = 42.0f;
cMeanWell.MaxVoltage = 80.0f;
cMeanWell.CurrentLimitMin = 3.6f; // 3.6A
cMeanWell.CurrentLimitMax = 18.0f; // 18.0A
cMeanWell.VoltageLimitMin = 42.0f;
cMeanWell.VoltageLimitMax = 80.0f;
} else if (strcmp(_rp.ManufacturerModelName, "NPB-1700-48") == 0) {
_model = NPB_Model_t::NPB_1700_48;
cMeanWell.MinCurrent = 5.0f; // 5.0A
cMeanWell.MaxCurrent = 25.0f; // 25.0A
cMeanWell.MinVoltage = 42.0f;
cMeanWell.MaxVoltage = 80.0f;
cMeanWell.CurrentLimitMin = 5.0f; // 5.0A
cMeanWell.CurrentLimitMax = 25.0f; // 25.0A
cMeanWell.VoltageLimitMin = 42.0f;
cMeanWell.VoltageLimitMax = 80.0f;
} else if (strcmp(_rp.ManufacturerModelName, "NPB-450-24") == 0) {
_model = NPB_Model_t::NPB_450_24;
cMeanWell.MinCurrent = 2.7f; // 2.7A
cMeanWell.MaxCurrent = 13.5f; // 13.5A
cMeanWell.MinVoltage = 21.0f;
cMeanWell.MaxVoltage = 42.0f;
cMeanWell.CurrentLimitMin = 2.7f; // 2.7A
cMeanWell.CurrentLimitMax = 13.5f; // 13.5A
cMeanWell.VoltageLimitMin = 21.0f;
cMeanWell.VoltageLimitMax = 42.0f;
} else if (strcmp(_rp.ManufacturerModelName, "NPB-750-24") == 0) {
_model = NPB_Model_t::NPB_750_24;
cMeanWell.MinCurrent = 4.5f; // 4.5A
cMeanWell.MaxCurrent = 22.5f; // 22.5A
cMeanWell.MinVoltage = 21.0f;
cMeanWell.MaxVoltage = 42.0f;
cMeanWell.CurrentLimitMin = 4.5f; // 4.5A
cMeanWell.CurrentLimitMax = 22.5f; // 22.5A
cMeanWell.VoltageLimitMin = 21.0f;
cMeanWell.VoltageLimitMax = 42.0f;
} else if (strcmp(_rp.ManufacturerModelName, "NPB-1200-24") == 0) {
_model = NPB_Model_t::NPB_1200_24;
cMeanWell.MinCurrent = 7.2f; // 7.2A
cMeanWell.MaxCurrent = 36.0f; // 36.0A
cMeanWell.MinVoltage = 21.0f;
cMeanWell.MaxVoltage = 42.0f;
cMeanWell.CurrentLimitMin = 7.2f; // 7.2A
cMeanWell.CurrentLimitMax = 36.0f; // 36.0A
cMeanWell.VoltageLimitMin = 21.0f;
cMeanWell.VoltageLimitMax = 42.0f;
} else if (strcmp(_rp.ManufacturerModelName, "NPB-1700-24") == 0) {
_model = NPB_Model_t::NPB_1700_24;
cMeanWell.MinCurrent = 10.0f; // 10.0A
cMeanWell.MaxCurrent = 50.0f; // 50.0A
cMeanWell.MinVoltage = 21.0f;
cMeanWell.MaxVoltage = 42.0f;
cMeanWell.CurrentLimitMin = 10.0f; // 10.0A
cMeanWell.CurrentLimitMax = 50.0f; // 50.0A
cMeanWell.VoltageLimitMin = 21.0f;
cMeanWell.VoltageLimitMax = 42.0f;
} else {
/* we didn't recognize the charge and set it to NPB-450-48 as default*/
_model = NPB_Model_t::NPB_450_48;
cMeanWell.MinCurrent = 1.36f; // 1.36A
cMeanWell.MaxCurrent = 6.8f; // 6.8A
cMeanWell.MinVoltage = 42.0f;
cMeanWell.MaxVoltage = 80.0f;
cMeanWell.CurrentLimitMin = 1.36f; // 1.36A
cMeanWell.CurrentLimitMax = 6.8f; // 6.8A
cMeanWell.VoltageLimitMin = 42.0f;
cMeanWell.VoltageLimitMax = 80.0f;
}
// check if min/max current and voltage is in range of Meanwell charger (Limits)
if (cMeanWell.MinCurrent < cMeanWell.CurrentLimitMin || cMeanWell.MinCurrent > cMeanWell.CurrentLimitMax) cMeanWell.MinCurrent = cMeanWell.CurrentLimitMin;
if (cMeanWell.MaxCurrent < cMeanWell.CurrentLimitMin || cMeanWell.MaxCurrent > cMeanWell.CurrentLimitMax) cMeanWell.MaxCurrent = cMeanWell.CurrentLimitMax;
if (cMeanWell.MinVoltage < cMeanWell.VoltageLimitMin || cMeanWell.MinVoltage > cMeanWell.VoltageLimitMax) cMeanWell.MinVoltage = cMeanWell.VoltageLimitMin;
if (cMeanWell.MaxVoltage < cMeanWell.VoltageLimitMin || cMeanWell.MaxVoltage > cMeanWell.VoltageLimitMax) cMeanWell.MaxVoltage = cMeanWell.VoltageLimitMax;

_lastUpdate = millis();

#ifdef MEANWELL_DEBUG_ENABLED
Expand Down
4 changes: 3 additions & 1 deletion src/MqttHandlVedirectHass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "Configuration.h"
#include "MqttSettings.h"
#include "NetworkSettings.h"
#include "MqttHandleHass.h"
#include "MessageOutput.h"
#include "VictronMppt.h"
#include "Utils.h"
Expand Down Expand Up @@ -210,10 +211,11 @@ void MqttHandleVedirectHassClass::createDeviceInfo(JsonObject& object,
String serial = mpptData.serialNr_SER;
object["name"] = "Victron(" + serial + ")";
object["ids"] = serial;
object["cu"] = String("http://") + NetworkSettings.localIP().toString();
object["cu"] = MqttHandleHass.getDtuUrl();
object["mf"] = "OpenDTU";
object["mdl"] = mpptData.getPidAsString();
object["sw"] = __COMPILED_GIT_HASH__;
object["via_device"] = MqttHandleHass.getDtuUniqueId();
}

void MqttHandleVedirectHassClass::publish(const String& subtopic, const String& payload)
Expand Down
4 changes: 3 additions & 1 deletion src/MqttHandleBatteryHass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "Configuration.h"
#include "MessageOutput.h"
#include "MqttSettings.h"
#include "MqttHandleHass.h"
#include "PylontechCanReceiver.h"
#include "Utils.h"
#include "__compiled_constants.h"
Expand Down Expand Up @@ -316,10 +317,11 @@ void MqttHandleBatteryHassClass::createDeviceInfo(JsonObject& object)
}

object["ids"] = serial;
object["cu"] = String("http://") + NetworkSettings.localIP().toString();
object["cu"] = MqttHandleHass.getDtuUrl();
object["mf"] = "OpenDTU";
object["mdl"] = Battery.getStats()->getManufacturer();
object["sw"] = __COMPILED_GIT_HASH__;
object["via_device"] = MqttHandleHass.getDtuUniqueId();
}

void MqttHandleBatteryHassClass::publish(const String& subtopic, const String& payload)
Expand Down
Loading

0 comments on commit b6ae8da

Please sign in to comment.