Merge pull request #125 from helgeerbe/powerlimiter
Translations for powerlimiter admin; Pylontech CAN Pins in Device manager; other fixes & enhancements
This commit is contained in:
commit
6dd34a8401
@ -270,7 +270,9 @@ This can be achieved by copying one of the [env:....] sections from 'platformio.
|
|||||||
-DHOYMILES_PIN_CE=4
|
-DHOYMILES_PIN_CE=4
|
||||||
-DHOYMILES_PIN_CS=5
|
-DHOYMILES_PIN_CS=5
|
||||||
-DVICTRON_PIN_TX=21
|
-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.
|
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).
|
You can also change the pins by creating a custom [device profile](docs/DeviceProfiles.md).
|
||||||
|
|||||||
@ -31,6 +31,8 @@ struct PinMapping_t {
|
|||||||
uint8_t display_reset;
|
uint8_t display_reset;
|
||||||
uint8_t victron_tx;
|
uint8_t victron_tx;
|
||||||
uint8_t victron_rx;
|
uint8_t victron_rx;
|
||||||
|
uint8_t battery_rx;
|
||||||
|
uint8_t battery_tx;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PinMappingClass {
|
class PinMappingClass {
|
||||||
@ -42,9 +44,10 @@ public:
|
|||||||
bool isValidNrf24Config();
|
bool isValidNrf24Config();
|
||||||
bool isValidEthConfig();
|
bool isValidEthConfig();
|
||||||
bool isValidVictronConfig();
|
bool isValidVictronConfig();
|
||||||
|
bool isValidBatteryConfig();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PinMapping_t _pinMapping;
|
PinMapping_t _pinMapping;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern PinMappingClass PinMapping;
|
extern PinMappingClass PinMapping;
|
||||||
|
|||||||
@ -17,7 +17,8 @@
|
|||||||
|
|
||||||
class PylontechCanReceiverClass {
|
class PylontechCanReceiverClass {
|
||||||
public:
|
public:
|
||||||
void init();
|
void init(int8_t rx, int8_t tx);
|
||||||
|
void enable();
|
||||||
void loop();
|
void loop();
|
||||||
void parseCanPackets();
|
void parseCanPackets();
|
||||||
void mqtt();
|
void mqtt();
|
||||||
|
|||||||
@ -55,6 +55,8 @@ build_flags = ${env.build_flags}
|
|||||||
-DHOYMILES_PIN_CS=5
|
-DHOYMILES_PIN_CS=5
|
||||||
-DVICTRON_PIN_TX=21
|
-DVICTRON_PIN_TX=21
|
||||||
-DVICTRON_PIN_RX=22
|
-DVICTRON_PIN_RX=22
|
||||||
|
-DPYLONTECH_PIN_RX=27
|
||||||
|
-DPYLONTECH_PIN_TX=14
|
||||||
|
|
||||||
|
|
||||||
[env:olimex_esp32_poe]
|
[env:olimex_esp32_poe]
|
||||||
|
|||||||
@ -64,6 +64,8 @@ PinMappingClass::PinMappingClass()
|
|||||||
_pinMapping.victron_tx = VICTRON_PIN_TX;
|
_pinMapping.victron_tx = VICTRON_PIN_TX;
|
||||||
_pinMapping.victron_rx = VICTRON_PIN_RX;
|
_pinMapping.victron_rx = VICTRON_PIN_RX;
|
||||||
|
|
||||||
|
_pinMapping.battery_rx = PYLONTECH_PIN_RX;
|
||||||
|
_pinMapping.battery_tx = PYLONTECH_PIN_TX;
|
||||||
}
|
}
|
||||||
|
|
||||||
PinMapping_t& PinMappingClass::get()
|
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_rx = doc[i]["victron"]["rx"] | VICTRON_PIN_RX;
|
||||||
_pinMapping.victron_tx = doc[i]["victron"]["tx"] | VICTRON_PIN_TX;
|
_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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -145,4 +150,10 @@ bool PinMappingClass::isValidVictronConfig()
|
|||||||
{
|
{
|
||||||
return _pinMapping.victron_rx > 0
|
return _pinMapping.victron_rx > 0
|
||||||
&& _pinMapping.victron_tx > 0;
|
&& _pinMapping.victron_tx > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PinMappingClass::isValidBatteryConfig()
|
||||||
|
{
|
||||||
|
return _pinMapping.battery_rx > 0
|
||||||
|
&& _pinMapping.battery_tx > 0;
|
||||||
|
}
|
||||||
|
|||||||
@ -40,7 +40,7 @@ void PowerLimiterClass::init()
|
|||||||
}
|
}
|
||||||
|
|
||||||
_consumeSolarPowerOnly = true;
|
_consumeSolarPowerOnly = true;
|
||||||
_lastCommandSent = 0;
|
_lastCommandSent = 0;
|
||||||
_lastLoop = 0;
|
_lastLoop = 0;
|
||||||
_lastPowerMeterUpdate = 0;
|
_lastPowerMeterUpdate = 0;
|
||||||
_lastRequestedPowerLimit = 0;
|
_lastRequestedPowerLimit = 0;
|
||||||
@ -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)
|
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();
|
CONFIG_T& config = Configuration.get();
|
||||||
|
|
||||||
if (strcmp(topic, config.PowerLimiter_MqttTopicPowerMeter1) == 0) {
|
if (strcmp(topic, config.PowerLimiter_MqttTopicPowerMeter1) == 0) {
|
||||||
@ -109,11 +107,10 @@ void PowerLimiterClass::loop()
|
|||||||
float acPower = inverter->Statistics()->getChannelFieldValue(TYPE_AC, (ChannelNum_t) config.PowerLimiter_InverterChannelId, FLD_PAC);
|
float acPower = inverter->Statistics()->getChannelFieldValue(TYPE_AC, (ChannelNum_t) config.PowerLimiter_InverterChannelId, FLD_PAC);
|
||||||
float correctedDcVoltage = dcVoltage + (acPower * config.PowerLimiter_VoltageLoadCorrectionFactor);
|
float correctedDcVoltage = dcVoltage + (acPower * config.PowerLimiter_VoltageLoadCorrectionFactor);
|
||||||
|
|
||||||
if ((_consumeSolarPowerOnly && isStartThresholdReached(inverter))
|
if ((_consumeSolarPowerOnly && isStartThresholdReached(inverter))) {
|
||||||
|| !canUseDirectSolarPower()) {
|
|
||||||
// The battery is full enough again, use the full battery power from now on.
|
// The battery is full enough again, use the full battery power from now on.
|
||||||
_consumeSolarPowerOnly = false;
|
_consumeSolarPowerOnly = false;
|
||||||
} else if (!_consumeSolarPowerOnly && !isStopThresholdReached(inverter) && canUseDirectSolarPower()) {
|
} else if (!_consumeSolarPowerOnly && isStopThresholdReached(inverter) && canUseDirectSolarPower()) {
|
||||||
// The battery voltage dropped too low
|
// The battery voltage dropped too low
|
||||||
_consumeSolarPowerOnly = true;
|
_consumeSolarPowerOnly = true;
|
||||||
}
|
}
|
||||||
@ -224,7 +221,7 @@ bool PowerLimiterClass::canUseDirectSolarPower()
|
|||||||
|
|
||||||
uint16_t PowerLimiterClass::getDirectSolarPower()
|
uint16_t PowerLimiterClass::getDirectSolarPower()
|
||||||
{
|
{
|
||||||
if (!this->canUseDirectSolarPower()) {
|
if (!canUseDirectSolarPower()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,7 +246,7 @@ bool PowerLimiterClass::isStartThresholdReached(std::shared_ptr<InverterAbstract
|
|||||||
{
|
{
|
||||||
CONFIG_T& config = Configuration.get();
|
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 start threshold is reached
|
||||||
if (config.Battery_Enabled
|
if (config.Battery_Enabled
|
||||||
&& config.PowerLimiter_BatterySocStartThreshold > 0.0
|
&& config.PowerLimiter_BatterySocStartThreshold > 0.0
|
||||||
&& (millis() - Battery.stateOfChargeLastUpdate) < 60000
|
&& (millis() - Battery.stateOfChargeLastUpdate) < 60000
|
||||||
@ -270,7 +267,7 @@ bool PowerLimiterClass::isStopThresholdReached(std::shared_ptr<InverterAbstract>
|
|||||||
{
|
{
|
||||||
CONFIG_T& config = Configuration.get();
|
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
|
if (config.Battery_Enabled
|
||||||
&& config.PowerLimiter_BatterySocStopThreshold > 0.0
|
&& config.PowerLimiter_BatterySocStopThreshold > 0.0
|
||||||
&& (millis() - Battery.stateOfChargeLastUpdate) < 60000
|
&& (millis() - Battery.stateOfChargeLastUpdate) < 60000
|
||||||
|
|||||||
@ -9,16 +9,21 @@
|
|||||||
|
|
||||||
PylontechCanReceiverClass PylontechCanReceiver;
|
PylontechCanReceiverClass PylontechCanReceiver;
|
||||||
|
|
||||||
void PylontechCanReceiverClass::init()
|
void PylontechCanReceiverClass::init(int8_t rx, int8_t tx)
|
||||||
{
|
{
|
||||||
|
CAN.setPins(rx, tx);
|
||||||
|
|
||||||
CONFIG_T& config = Configuration.get();
|
CONFIG_T& config = Configuration.get();
|
||||||
|
|
||||||
if (!config.Battery_Enabled) {
|
if (!config.Battery_Enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CAN.setPins(PYLONTECH_PIN_RX, PYLONTECH_PIN_TX);
|
|
||||||
|
|
||||||
|
enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PylontechCanReceiverClass::enable()
|
||||||
|
{
|
||||||
if (!CAN.begin(500E3)) {
|
if (!CAN.begin(500E3)) {
|
||||||
Hoymiles.getMessageOutput()->println("Starting CAN failed!");
|
Hoymiles.getMessageOutput()->println("Starting CAN failed!");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -108,6 +108,6 @@ void WebApiBatteryClass::onAdminPost(AsyncWebServerRequest* request)
|
|||||||
request->send(response);
|
request->send(response);
|
||||||
|
|
||||||
if (config.Battery_Enabled) {
|
if (config.Battery_Enabled) {
|
||||||
PylontechCanReceiver.init();
|
PylontechCanReceiver.enable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -73,6 +73,10 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request)
|
|||||||
victronPinObj[F("rx")] = pin.victron_rx;
|
victronPinObj[F("rx")] = pin.victron_rx;
|
||||||
victronPinObj[F("tx")] = pin.victron_tx;
|
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();
|
response->setLength();
|
||||||
request->send(response);
|
request->send(response);
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/main.cpp
13
src/main.cpp
@ -7,6 +7,7 @@
|
|||||||
#include "InverterSettings.h"
|
#include "InverterSettings.h"
|
||||||
#include "MessageOutput.h"
|
#include "MessageOutput.h"
|
||||||
#include "VeDirectFrameHandler.h"
|
#include "VeDirectFrameHandler.h"
|
||||||
|
#include "PylontechCanReceiver.h"
|
||||||
#include "MqttHandleDtu.h"
|
#include "MqttHandleDtu.h"
|
||||||
#include "MqttHandleHass.h"
|
#include "MqttHandleHass.h"
|
||||||
#include "MqttHandleVedirectHass.h"
|
#include "MqttHandleVedirectHass.h"
|
||||||
@ -20,7 +21,6 @@
|
|||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include "WebApi.h"
|
#include "WebApi.h"
|
||||||
#include "PowerLimiter.h"
|
#include "PowerLimiter.h"
|
||||||
#include "PylontechCanReceiver.h"
|
|
||||||
#include "defaults.h"
|
#include "defaults.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <LittleFS.h>
|
#include <LittleFS.h>
|
||||||
@ -149,8 +149,15 @@ void setup()
|
|||||||
// Dynamic power limiter
|
// Dynamic power limiter
|
||||||
PowerLimiter.init();
|
PowerLimiter.init();
|
||||||
|
|
||||||
// Pylontech / CAN bus
|
// Initialize Pylontech Battery / CAN bus
|
||||||
PylontechCanReceiver.init();
|
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()
|
void loop()
|
||||||
|
|||||||
@ -447,6 +447,12 @@
|
|||||||
"Save": "@:dtuadmin.Save"
|
"Save": "@:dtuadmin.Save"
|
||||||
},
|
},
|
||||||
"powerlimiteradmin": {
|
"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",
|
"InverterId": "Wechselrichter ID",
|
||||||
"InverterIdHint": "Wähle den Wechselrichter an dem die Batterie hängt.",
|
"InverterIdHint": "Wähle den Wechselrichter an dem die Batterie hängt.",
|
||||||
"InverterChannelId": "Kanal ID",
|
"InverterChannelId": "Kanal ID",
|
||||||
@ -454,8 +460,23 @@
|
|||||||
"TargetPowerConsumption": "Erlaubter Stromverbrauch",
|
"TargetPowerConsumption": "Erlaubter Stromverbrauch",
|
||||||
"TargetPowerConsumptionHint": "Angestrebter erlaubter Stromverbrauch.",
|
"TargetPowerConsumptionHint": "Angestrebter erlaubter Stromverbrauch.",
|
||||||
"TargetPowerConsumptionHysteresis": "Hysterese für den Zielstromverbrauch",
|
"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": "<b>Hinweis:</b> 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": "<b>Hinweis:</b> 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": {
|
"batteryadmin": {
|
||||||
"BatterySettings": "Batterie Einstellungen",
|
"BatterySettings": "Batterie Einstellungen",
|
||||||
|
|||||||
@ -451,7 +451,7 @@
|
|||||||
"PowerLimiterConfiguration": "Power Limiter Configuration",
|
"PowerLimiterConfiguration": "Power Limiter Configuration",
|
||||||
"General": "General",
|
"General": "General",
|
||||||
"Enable": "Enable",
|
"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.",
|
"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",
|
"InverterId": "Inverter ID",
|
||||||
"InverterIdHint": "Select proper inverter ID where battery is connected to.",
|
"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.",
|
"TargetPowerConsumptionHint": "Set the grid power consumption the limiter tries to achieve.",
|
||||||
"TargetPowerConsumptionHysteresis": "Hysteresis for power consumption",
|
"TargetPowerConsumptionHysteresis": "Hysteresis for power consumption",
|
||||||
"TargetPowerConsumptionHysteresisHint": "Value around which the target power consumption fluctuates without readjustment.",
|
"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",
|
"UpperPowerLimit": "Upper power limit",
|
||||||
"PowerMeters": "Power meters - MQTT",
|
"PowerMeters": "Power meters - MQTT",
|
||||||
"MqttTopicPowerMeter1": "MQTT topic - Power meter #1",
|
"MqttTopicPowerMeter1": "MQTT topic - Power meter #1",
|
||||||
"MqttTopicPowerMeter2": "MQTT topic - Power meter #2",
|
"MqttTopicPowerMeter2": "MQTT topic - Power meter #2 (optional)",
|
||||||
"MqttTopicPowerMeter3": "MQTT topic - Power meter #3",
|
"MqttTopicPowerMeter3": "MQTT topic - Power meter #3 (optional)",
|
||||||
"BatterySocStartThreshold": "Battery SOC - Start threshold",
|
"BatterySocStartThreshold": "Battery SOC - Start threshold",
|
||||||
"BatterySocStopThreshold": "Battery SOC - Stop threshold",
|
"BatterySocStopThreshold": "Battery SOC - Stop threshold",
|
||||||
"VoltageStartThreshold": "DC Voltage - Start threshold",
|
"VoltageStartThreshold": "DC Voltage - Start threshold",
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user