From 804f225908dbc37d900f13510f1a7986e7674985 Mon Sep 17 00:00:00 2001 From: Bernhard Kaszt Date: Wed, 1 Mar 2023 21:35:32 +0100 Subject: [PATCH 1/7] Fix _consumeSolarPowerOnly sometimes being set to false when it shouldn't --- src/PowerLimiter.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/PowerLimiter.cpp b/src/PowerLimiter.cpp index 0e60a1f8f..9c3c80dec 100644 --- a/src/PowerLimiter.cpp +++ b/src/PowerLimiter.cpp @@ -40,7 +40,7 @@ void PowerLimiterClass::init() } _consumeSolarPowerOnly = true; - _lastCommandSent = 0; + _lastCommandSent = 0; _lastLoop = 0; _lastPowerMeterUpdate = 0; _lastRequestedPowerLimit = 0; @@ -109,8 +109,7 @@ void PowerLimiterClass::loop() float acPower = inverter->Statistics()->getChannelFieldValue(TYPE_AC, (ChannelNum_t) config.PowerLimiter_InverterChannelId, FLD_PAC); float correctedDcVoltage = dcVoltage + (acPower * config.PowerLimiter_VoltageLoadCorrectionFactor); - if ((_consumeSolarPowerOnly && isStartThresholdReached(inverter)) - || !canUseDirectSolarPower()) { + if ((_consumeSolarPowerOnly && isStartThresholdReached(inverter))) { // The battery is full enough again, use the full battery power from now on. _consumeSolarPowerOnly = false; } else if (!_consumeSolarPowerOnly && !isStopThresholdReached(inverter) && canUseDirectSolarPower()) { @@ -224,7 +223,7 @@ bool PowerLimiterClass::canUseDirectSolarPower() uint16_t PowerLimiterClass::getDirectSolarPower() { - if (!this->canUseDirectSolarPower()) { + if (!canUseDirectSolarPower()) { return 0; } @@ -249,7 +248,7 @@ bool PowerLimiterClass::isStartThresholdReached(std::shared_ptr 0.0 && (millis() - Battery.stateOfChargeLastUpdate) < 60000 @@ -270,7 +269,7 @@ bool PowerLimiterClass::isStopThresholdReached(std::shared_ptr { CONFIG_T& config = Configuration.get(); - // If the Battery interface is enabled, use the SOC value + // Check if the Battery interface is enabled and the SOC stop threshold is reached if (config.Battery_Enabled && config.PowerLimiter_BatterySocStopThreshold > 0.0 && (millis() - Battery.stateOfChargeLastUpdate) < 60000 From a6e720f154aef8b149c0cd2fe166513a8dd4397f Mon Sep 17 00:00:00 2001 From: Bernhard Kaszt Date: Sun, 5 Mar 2023 15:41:21 +0100 Subject: [PATCH 2/7] Powerlimiter: Remove MQTT Topic debug message --- src/PowerLimiter.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/PowerLimiter.cpp b/src/PowerLimiter.cpp index 9c3c80dec..d0af92767 100644 --- a/src/PowerLimiter.cpp +++ b/src/PowerLimiter.cpp @@ -48,8 +48,6 @@ void PowerLimiterClass::init() void PowerLimiterClass::onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total) { - MessageOutput.printf("PowerLimiterClass: Received MQTT message on topic: %s\r\n", topic); - CONFIG_T& config = Configuration.get(); if (strcmp(topic, config.PowerLimiter_MqttTopicPowerMeter1) == 0) { From 304d90062d22b9c89e5057e2f9fa0c6e69b7af81 Mon Sep 17 00:00:00 2001 From: Bernhard Kaszt Date: Sun, 5 Mar 2023 16:30:53 +0100 Subject: [PATCH 3/7] Revert broken change in condition that sets _consumeSolarPowerOnly https://github.com/helgeerbe/OpenDTU-OnBattery/commit/6709338dbd5b989f109ff2721228b340b4e93288 --- src/PowerLimiter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PowerLimiter.cpp b/src/PowerLimiter.cpp index d0af92767..0b2cac159 100644 --- a/src/PowerLimiter.cpp +++ b/src/PowerLimiter.cpp @@ -110,7 +110,7 @@ void PowerLimiterClass::loop() if ((_consumeSolarPowerOnly && isStartThresholdReached(inverter))) { // The battery is full enough again, use the full battery power from now on. _consumeSolarPowerOnly = false; - } else if (!_consumeSolarPowerOnly && !isStopThresholdReached(inverter) && canUseDirectSolarPower()) { + } else if (!_consumeSolarPowerOnly && isStopThresholdReached(inverter) && canUseDirectSolarPower()) { // The battery voltage dropped too low _consumeSolarPowerOnly = true; } From 43436e19b714b895d5efa2563f1d7466762f048d Mon Sep 17 00:00:00 2001 From: Bernhard Kaszt Date: Sun, 5 Mar 2023 19:17:30 +0100 Subject: [PATCH 4/7] Translate all remaning Powerlimiter settings --- webapp/src/locales/de.json | 25 +++++++++++++++++++++++-- webapp/src/locales/en.json | 8 ++++---- webapp_dist/index.html.gz | Bin 329 -> 329 bytes webapp_dist/js/app.js.gz | Bin 153457 -> 154144 bytes webapp_dist/zones.json.gz | Bin 4100 -> 4100 bytes 5 files changed, 27 insertions(+), 6 deletions(-) diff --git a/webapp/src/locales/de.json b/webapp/src/locales/de.json index 1dc762706..771637ae1 100644 --- a/webapp/src/locales/de.json +++ b/webapp/src/locales/de.json @@ -447,6 +447,12 @@ "Save": "@:dtuadmin.Save" }, "powerlimiteradmin": { + "PowerLimiterSettings": "Power Limiter Einstellungen", + "PowerLimiterConfiguration": "Power Limiter Konfiguration", + "General": "Allgemein", + "Enable": "Aktiviert", + "EnableSolarPasstrough": "Aktiviere Solar Pass-trough", + "SolarpasstroughInfo": "Diese Einstellung aktiviert die direkte Weitergabe der aktuell vom Laderegler gemeldeten Solarleistung an den Wechselrichter um eine unnötige Speicherung zu vermeiden und die Energieverluste zu minimieren.", "InverterId": "Wechselrichter ID", "InverterIdHint": "Wähle den Wechselrichter an dem die Batterie hängt.", "InverterChannelId": "Kanal ID", @@ -454,8 +460,23 @@ "TargetPowerConsumption": "Erlaubter Stromverbrauch", "TargetPowerConsumptionHint": "Angestrebter erlaubter Stromverbrauch.", "TargetPowerConsumptionHysteresis": "Hysterese für den Zielstromverbrauch", - "TargetPowerConsumptionHysteresisHint": "Wert um den der Zielstromverbrauch schwanken darf, ohne dass nachgeregelt wird." - + "TargetPowerConsumptionHysteresisHint": "Wert um den der Zielstromverbrauch schwanken darf, ohne dass nachgeregelt wird.", + "LowerPowerLimit": "Unteres Leistungslimit", + "UpperPowerLimit": "Oberes Leistungslimit", + "PowerMeters": "Leistungsmesser - MQTT", + "MqttTopicPowerMeter1": "MQTT topic - Power meter #1", + "MqttTopicPowerMeter2": "MQTT topic - Power meter #2 (Optional)", + "MqttTopicPowerMeter3": "MQTT topic - Power meter #3 (Optional)", + "BatterySocStartThreshold": "Akku SOC - Start", + "BatterySocStopThreshold": "Akku SOC - Stop", + "VoltageStartThreshold": "DC Spannung - Start", + "VoltageStopThreshold": "DC Spannung - Stop", + "VoltageLoadCorrectionFactor": "DC Spannung - Lastkorrekturfaktor", + "BatterySocInfo": "Hinweis: Der Battery SOC (State of charge) -Wert kann nur benutzt werden wenn das Battery CAN Bus Interface aktiviert ist. Wenn die Batterie innerhalb der letzten Minute keine Werte geschickt hat, werden als Fallback-Option die Spannungseinstellungen verwendet.", + "InverterIsBehindPowerMeter": "Welchselrichter ist hinter Leistungsmesser", + "Battery": "DC / Akku", + "VoltageLoadCorrectionInfo": "Hinweis: Wenn Leistung von der Batterie abgegeben wird, bricht normalerweise die Spannung etwas ein. Damit nicht vorzeitig der Wechelrichter ausgeschaltet wird sobald der \"Stop\"-Schwellenwert erreicht wird, wird der hier angegebene Korrekturfaktor mit einberechnet. Korrigierte Spannung = DC Spannung + (Aktuelle Leistung (W) + Korrekturfaktor).", + "Save": "@:dtuadmin.Save" }, "batteryadmin": { "BatterySettings": "Batterie Einstellungen", diff --git a/webapp/src/locales/en.json b/webapp/src/locales/en.json index 9dcd16287..88a7e8f3b 100644 --- a/webapp/src/locales/en.json +++ b/webapp/src/locales/en.json @@ -451,7 +451,7 @@ "PowerLimiterConfiguration": "Power Limiter Configuration", "General": "General", "Enable": "Enable", - "EnableSolarPasstrough": "Enable Solar Passtrough", + "EnableSolarPasstrough": "Enable Solar-Passtrough", "SolarpasstroughInfo": "When the sun is shining, this setting enables the sychronization of the inverter limit with the current solar power of the Victron MPPT charger. This optimizes battery degradation and loses.", "InverterId": "Inverter ID", "InverterIdHint": "Select proper inverter ID where battery is connected to.", @@ -461,12 +461,12 @@ "TargetPowerConsumptionHint": "Set the grid power consumption the limiter tries to achieve.", "TargetPowerConsumptionHysteresis": "Hysteresis for power consumption", "TargetPowerConsumptionHysteresisHint": "Value around which the target power consumption fluctuates without readjustment.", - "LowerPowerLimit": "Lower power limit / continuous feed", + "LowerPowerLimit": "Lower power limit", "UpperPowerLimit": "Upper power limit", "PowerMeters": "Power meters - MQTT", "MqttTopicPowerMeter1": "MQTT topic - Power meter #1", - "MqttTopicPowerMeter2": "MQTT topic - Power meter #2", - "MqttTopicPowerMeter3": "MQTT topic - Power meter #3", + "MqttTopicPowerMeter2": "MQTT topic - Power meter #2 (optional)", + "MqttTopicPowerMeter3": "MQTT topic - Power meter #3 (optional)", "BatterySocStartThreshold": "Battery SOC - Start threshold", "BatterySocStopThreshold": "Battery SOC - Stop threshold", "VoltageStartThreshold": "DC Voltage - Start threshold", diff --git a/webapp_dist/index.html.gz b/webapp_dist/index.html.gz index 0830730b7beedfaac59c2b0a4aebb08a2c0c79ad..9996657e593aa31502822de16caa8c3fca841fc1 100644 GIT binary patch delta 19 YcmX@fbdrfnzMF#q1elmNa@jKi04dG`Qvd(} delta 19 YcmX@fbdrfnzMF#q1ek<3a@jKi04g2>V*mgE diff --git a/webapp_dist/js/app.js.gz b/webapp_dist/js/app.js.gz index 0eb045184950a1b2fae6a3840ae611ca55411022..ade61ebc8aa0d4fbbfc38d4f3dbafc2854a110d6 100644 GIT binary patch delta 19800 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%0D`D7B0V)X*l%~{>e(Z{G)z8dZ2 z9p9~X=D^6;!lQhHg_iPp01fHUBofe@o8Daft$TlVxUgIOF$sHZ{;>hk_&T=&m_1Nb3vxUlr37^KQ!oT!)!KxIQ{V`gX@d3#P-ZQN>QB<+T#rL zH8hlQfB5(eV^84Yr`9ZZH|)4B^dmnN6fv7ijW$P&V^TR+EHcjVAZp>ZFRV4f?+ksS z6=8qPMM;sFXq+uDh!T;DFm5S#XnsSN-IiL$W^|*kXs8iZxy-YCU=|!mi%SxZDFR)} zl^9HhEBnR9juLqA{@q5Dz!k8Aie#^9EJDr))qWW`3~V@SWwj=xZ&|gTI%+DdUK_n) zeV;bE>2cMwa@LNpc9C5;D&oP&a4}-Z{TP3mck-{TpAlmvhrZbyc$ccFbHXAY8{omB z57typBF1 z&LbQ^in8Mr1jKzz2u`QqY~A;L zzYGe2_7(wfi}2ukNHM6k3iU8jM`hf*e&^VwPcLi~R@{ZVm^K~vw|?2sC@W}fW2_ku znwp;<@^w}4p^w;~TF&6Nb9-vy+SPwb2~3+rVsm?7PT@MDI|?7GY!Aaf)>pGRN6ao* z$Gta{p#fJ`4WRekaQNg=10iGR)FZ+N*2X9_+B>~-tTxc>B?&M;Vi{0wx0`v^nYX)o zTikG0bso}kA^d+-F4jZ)2Lj<2AW~Vr0I39$-mOcZH`2b-U(LGjh>bY5fwO<`fgvXd zm=`gc3jWurU^Do$m`>|m(9574E%%S^9X7D=usJ&bXc{wg+>( z;<6fR-MG?P()r7PuG07pj{w{usEoU!TU2uFbAbPpRe9#XcK@aUff%Q(f?fQM;izK^ zM<>XW?y+>dNByY}0pJl^HLZU}pN4O~O{m|6@e8~zM(~gTxle~lo=N1#DcWmGnHB=G z;j${IT{iJ+7EeIM#FA^Mt)8)(>JxT`=2^vj;OZFR+XaZQ8aBEZB|H`) z4(|<=1w}bDdw3Cn7HhdD{WFLHI3=SC#_s^y#YcM%x7=woPA�KVp9ZDK-KrpWC{~ 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 nh25Ba-W Date: Sun, 5 Mar 2023 19:55:56 +0100 Subject: [PATCH 5/7] Add Pylontech battery to device pin manager --- include/PinMapping.h | 5 ++++- include/PylontechCanReceiver.h | 3 ++- src/PinMapping.cpp | 13 ++++++++++++- src/PylontechCanReceiver.cpp | 11 ++++++++--- src/WebApi_battery.cpp | 2 +- src/WebApi_device.cpp | 4 ++++ src/main.cpp | 11 +++++++++-- 7 files changed, 40 insertions(+), 9 deletions(-) diff --git a/include/PinMapping.h b/include/PinMapping.h index cdd1e73d1..4e2900f0c 100644 --- a/include/PinMapping.h +++ b/include/PinMapping.h @@ -31,6 +31,8 @@ struct PinMapping_t { uint8_t display_reset; uint8_t victron_tx; uint8_t victron_rx; + uint8_t battery_rx; + uint8_t battery_tx; }; class PinMappingClass { @@ -42,9 +44,10 @@ class PinMappingClass { bool isValidNrf24Config(); bool isValidEthConfig(); bool isValidVictronConfig(); + bool isValidBatteryConfig(); private: PinMapping_t _pinMapping; }; -extern PinMappingClass PinMapping; \ No newline at end of file +extern PinMappingClass PinMapping; diff --git a/include/PylontechCanReceiver.h b/include/PylontechCanReceiver.h index 1efb03ac7..2cb13ac1c 100644 --- a/include/PylontechCanReceiver.h +++ b/include/PylontechCanReceiver.h @@ -17,7 +17,8 @@ class PylontechCanReceiverClass { public: - void init(); + void init(int8_t rx, int8_t tx); + void enable(); void loop(); void parseCanPackets(); void mqtt(); diff --git a/src/PinMapping.cpp b/src/PinMapping.cpp index 486834d7d..248e369bd 100644 --- a/src/PinMapping.cpp +++ b/src/PinMapping.cpp @@ -64,6 +64,8 @@ PinMappingClass::PinMappingClass() _pinMapping.victron_tx = VICTRON_PIN_TX; _pinMapping.victron_rx = VICTRON_PIN_RX; + _pinMapping.battery_rx = PYLONTECH_PIN_RX; + _pinMapping.battery_tx = PYLONTECH_PIN_TX; } PinMapping_t& PinMappingClass::get() @@ -119,6 +121,9 @@ bool PinMappingClass::init(const String& deviceMapping) _pinMapping.victron_rx = doc[i]["victron"]["rx"] | VICTRON_PIN_RX; _pinMapping.victron_tx = doc[i]["victron"]["tx"] | VICTRON_PIN_TX; + _pinMapping.battery_rx = doc[i]["battery"]["rx"] | PYLONTECH_PIN_RX; + _pinMapping.battery_tx = doc[i]["battery"]["tx"] | PYLONTECH_PIN_TX; + return true; } } @@ -145,4 +150,10 @@ bool PinMappingClass::isValidVictronConfig() { return _pinMapping.victron_rx > 0 && _pinMapping.victron_tx > 0; -} \ No newline at end of file +} + +bool PinMappingClass::isValidBatteryConfig() +{ + return _pinMapping.battery_rx > 0 + && _pinMapping.battery_tx > 0; +} diff --git a/src/PylontechCanReceiver.cpp b/src/PylontechCanReceiver.cpp index 0219642ca..27955ed75 100644 --- a/src/PylontechCanReceiver.cpp +++ b/src/PylontechCanReceiver.cpp @@ -9,16 +9,21 @@ PylontechCanReceiverClass PylontechCanReceiver; -void PylontechCanReceiverClass::init() +void PylontechCanReceiverClass::init(int8_t rx, int8_t tx) { + CAN.setPins(rx, tx); + CONFIG_T& config = Configuration.get(); if (!config.Battery_Enabled) { return; } - - CAN.setPins(PYLONTECH_PIN_RX, PYLONTECH_PIN_TX); + enable(); +} + +void PylontechCanReceiverClass::enable() +{ if (!CAN.begin(500E3)) { Hoymiles.getMessageOutput()->println("Starting CAN failed!"); } diff --git a/src/WebApi_battery.cpp b/src/WebApi_battery.cpp index 4726f2b59..a11d14fbe 100644 --- a/src/WebApi_battery.cpp +++ b/src/WebApi_battery.cpp @@ -108,6 +108,6 @@ void WebApiBatteryClass::onAdminPost(AsyncWebServerRequest* request) request->send(response); if (config.Battery_Enabled) { - PylontechCanReceiver.init(); + PylontechCanReceiver.enable(); } } diff --git a/src/WebApi_device.cpp b/src/WebApi_device.cpp index abde3123d..2cc191cf4 100644 --- a/src/WebApi_device.cpp +++ b/src/WebApi_device.cpp @@ -73,6 +73,10 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request) victronPinObj[F("rx")] = pin.victron_rx; victronPinObj[F("tx")] = pin.victron_tx; + JsonObject batteryPinObj = curPin.createNestedObject("battery"); + batteryPinObj[F("rx")] = pin.battery_rx; + batteryPinObj[F("tx")] = pin.battery_tx; + response->setLength(); request->send(response); } diff --git a/src/main.cpp b/src/main.cpp index e30b73eb9..07b7ea5fb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -149,8 +149,15 @@ void setup() // Dynamic power limiter PowerLimiter.init(); - // Pylontech / CAN bus - PylontechCanReceiver.init(); + // Initialize Pylontech Battery / CAN bus + MessageOutput.println(F("Initialize Pylontech battery interface... ")); + if (PinMapping.isValidBatteryConfig()) { + MessageOutput.printf("Pylontech Battery rx = %d, tx = %d\r\n", pin.battery_rx, pin.battery_tx); + PylontechCanReceiver.init(pin.battery_rx, pin.battery_tx); + MessageOutput.println(F("done")); + } else { + MessageOutput.println(F("Invalid pin config")); + } } void loop() From 06a0f76fed67ecab07219f34bde95f282312b35e Mon Sep 17 00:00:00 2001 From: Bernhard Kaszt Date: Sun, 5 Mar 2023 20:45:27 +0100 Subject: [PATCH 6/7] Fix local build working but Github build failing --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 07b7ea5fb..33066ac94 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,6 +7,7 @@ #include "InverterSettings.h" #include "MessageOutput.h" #include "VeDirectFrameHandler.h" +#include "PylontechCanReceiver.h" #include "MqttHandleDtu.h" #include "MqttHandleHass.h" #include "MqttHandleVedirectHass.h" @@ -20,7 +21,6 @@ #include "Utils.h" #include "WebApi.h" #include "PowerLimiter.h" -#include "PylontechCanReceiver.h" #include "defaults.h" #include #include From 44a770be0e0e02687c6fc7d918759b7172928102 Mon Sep 17 00:00:00 2001 From: Bernhard Kaszt Date: Sun, 5 Mar 2023 21:20:00 +0100 Subject: [PATCH 7/7] Add Pylontech PIN numbers to platformio.ini --- README.md | 4 +++- platformio.ini | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 40d66b7b6..26bfb437c 100644 --- a/README.md +++ b/README.md @@ -270,7 +270,9 @@ This can be achieved by copying one of the [env:....] sections from 'platformio. -DHOYMILES_PIN_CE=4 -DHOYMILES_PIN_CS=5 -DVICTRON_PIN_TX=21 --DVICTRON_PIN_RX=22 +-DVICTRON_PIN_RX=22 +-DPYLONTECH_PIN_RX=27 +-DPYLONTECH_PIN_TX=14 ``` It is recommended to make all changes only in the 'platformio_override.ini', this is your personal copy. You can also change the pins by creating a custom [device profile](docs/DeviceProfiles.md). diff --git a/platformio.ini b/platformio.ini index 96d8e7e58..5ccc43467 100644 --- a/platformio.ini +++ b/platformio.ini @@ -55,6 +55,8 @@ build_flags = ${env.build_flags} -DHOYMILES_PIN_CS=5 -DVICTRON_PIN_TX=21 -DVICTRON_PIN_RX=22 + -DPYLONTECH_PIN_RX=27 + -DPYLONTECH_PIN_TX=14 [env:olimex_esp32_poe]