Skip to content

Commit b7def73

Browse files
committed
DPL: increase backoff while inverter is kept shut down
if the new calculated power limit is below the minimum power limit setting, the inverter is shut down. the shutdown() function is called every time this condition is detected, which is also true if the inverter is kept shut down for longer. that happens while the battery is charging in particular (solar passthrough off). there are other cases. in such cases we still want to get into the DPL status "stable". to be able to determine this stable state, we must know if the call to shutdown did actually initiate a shutdown or if the inverter is already shut down. we then can forward this "changed" or "not changed" info up the call chain, where the loop() will know that the system is actually stable.
1 parent f68f68b commit b7def73

File tree

2 files changed

+43
-22
lines changed

2 files changed

+43
-22
lines changed

include/PowerLimiter.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ class PowerLimiterClass {
4444
NoVeDirect,
4545
Settling,
4646
Stable,
47-
LowerLimitUndercut
4847
};
4948

5049
void init();
@@ -57,7 +56,7 @@ class PowerLimiterClass {
5756

5857
private:
5958
int32_t _lastRequestedPowerLimit = 0;
60-
bool _shutdownInProgress;
59+
uint32_t _shutdownTimeout = 0;
6160
Status _lastStatus = Status::Initializing;
6261
uint32_t _lastStatusPrinted = 0;
6362
uint32_t _lastCalculation = 0;
@@ -72,7 +71,8 @@ class PowerLimiterClass {
7271

7372
std::string const& getStatusText(Status status);
7473
void announceStatus(Status status);
75-
void shutdown(Status status);
74+
bool shutdown(Status status);
75+
bool shutdown() { return shutdown(_lastStatus); }
7676
int32_t inverterPowerDcToAc(std::shared_ptr<InverterAbstract> inverter, int32_t dcPower);
7777
void unconditionalSolarPassthrough(std::shared_ptr<InverterAbstract> inverter);
7878
bool canUseDirectSolarPower();

src/PowerLimiter.cpp

+40-19
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ std::string const& PowerLimiterClass::getStatusText(PowerLimiterClass::Status st
4646
{ Status::NoVeDirect, "VE.Direct disabled, connection broken, or data outdated" },
4747
{ Status::Settling, "waiting for the system to settle" },
4848
{ Status::Stable, "the system is stable, the last power limit is still valid" },
49-
{ Status::LowerLimitUndercut, "calculated power limit undercuts configured lower limit" }
5049
};
5150

5251
auto iter = texts.find(status);
@@ -73,26 +72,39 @@ void PowerLimiterClass::announceStatus(PowerLimiterClass::Status status)
7372
_lastStatusPrinted = millis();
7473
}
7574

76-
void PowerLimiterClass::shutdown(PowerLimiterClass::Status status)
75+
/**
76+
* returns true if the inverter state was changed or is about to change, i.e.,
77+
* if it is actually in need of a shutdown. returns false otherwise, i.e., the
78+
* inverter is already (assumed to be) shut down.
79+
*/
80+
bool PowerLimiterClass::shutdown(PowerLimiterClass::Status status)
7781
{
7882
announceStatus(status);
7983

80-
if (_inverter == nullptr || !_inverter->isProducing() || !_inverter->isReachable()) {
84+
if (_inverter == nullptr || !_inverter->isProducing() ||
85+
(_shutdownTimeout > 0 && _shutdownTimeout < millis()) ) {
86+
// we are actually (already) done with shutting down the inverter,
87+
// or a shutdown attempt was initiated but it timed out.
8188
_inverter = nullptr;
82-
_shutdownInProgress = false;
83-
return;
89+
_shutdownTimeout = 0;
90+
return false;
8491
}
8592

86-
_shutdownInProgress = true;
93+
if (!_inverter->isReachable()) { return true; } // retry later (until timeout)
94+
95+
// retry shutdown for a maximum amount of time before giving up
96+
if (_shutdownTimeout == 0) { _shutdownTimeout = millis() + 10 * 1000; }
8797

8898
auto lastLimitCommandState = _inverter->SystemConfigPara()->getLastLimitCommandSuccess();
89-
if (CMD_PENDING == lastLimitCommandState) { return; }
99+
if (CMD_PENDING == lastLimitCommandState) { return true; }
90100

91101
auto lastPowerCommandState = _inverter->PowerCommand()->getLastPowerCommandSuccess();
92-
if (CMD_PENDING == lastPowerCommandState) { return; }
102+
if (CMD_PENDING == lastPowerCommandState) { return true; }
93103

94104
CONFIG_T& config = Configuration.get();
95105
commitPowerLimit(_inverter, config.PowerLimiter_LowerPowerLimit, false);
106+
107+
return true;
96108
}
97109

98110
void PowerLimiterClass::loop()
@@ -107,19 +119,22 @@ void PowerLimiterClass::loop()
107119
return announceStatus(Status::WaitingForValidTimestamp);
108120
}
109121

110-
if (_shutdownInProgress) {
122+
if (_shutdownTimeout > 0) {
111123
// we transition from SHUTDOWN to OFF when we know the inverter was
112124
// shut down. until then, we retry shutting it down. in this case we
113125
// preserve the original status that lead to the decision to shut down.
114-
return shutdown(_lastStatus);
126+
shutdown();
127+
return;
115128
}
116129

117130
if (!config.PowerLimiter_Enabled) {
118-
return shutdown(Status::DisabledByConfig);
131+
shutdown(Status::DisabledByConfig);
132+
return;
119133
}
120134

121135
if (PL_MODE_FULL_DISABLE == _mode) {
122-
return shutdown(Status::DisabledByMqtt);
136+
shutdown(Status::DisabledByMqtt);
137+
return;
123138
}
124139

125140
std::shared_ptr<InverterAbstract> currentInverter =
@@ -128,13 +143,15 @@ void PowerLimiterClass::loop()
128143
// in case of (newly) broken configuration, shut down
129144
// the last inverter we worked with (if any)
130145
if (currentInverter == nullptr) {
131-
return shutdown(Status::InverterInvalid);
146+
shutdown(Status::InverterInvalid);
147+
return;
132148
}
133149

134150
// if the DPL is supposed to manage another inverter now, we first
135151
// shut down the previous one, if any. then we pick up the new one.
136152
if (_inverter != nullptr && _inverter->serial() != currentInverter->serial()) {
137-
return shutdown(Status::InverterChanged);
153+
shutdown(Status::InverterChanged);
154+
return;
138155
}
139156

140157
// update our pointer as the configuration might have changed
@@ -177,11 +194,13 @@ void PowerLimiterClass::loop()
177194
// the normal mode of operation requires a valid
178195
// power meter reading to calculate a power limit
179196
if (!config.PowerMeter_Enabled) {
180-
return shutdown(Status::PowerMeterDisabled);
197+
shutdown(Status::PowerMeterDisabled);
198+
return;
181199
}
182200

183201
if (millis() - PowerMeter.getLastPowerMeterUpdate() > (30 * 1000)) {
184-
return shutdown(Status::PowerMeterTimeout);
202+
shutdown(Status::PowerMeterTimeout);
203+
return;
185204
}
186205

187206
// concerns both power limits and start/stop/restart commands and is
@@ -332,7 +351,8 @@ void PowerLimiterClass::unconditionalSolarPassthrough(std::shared_ptr<InverterAb
332351
CONFIG_T& config = Configuration.get();
333352

334353
if (!config.Vedirect_Enabled || !VeDirect.isDataValid()) {
335-
return shutdown(Status::NoVeDirect);
354+
shutdown(Status::NoVeDirect);
355+
return;
336356
}
337357

338358
int32_t solarPower = VeDirect.veFrame.V * VeDirect.veFrame.I;
@@ -487,8 +507,9 @@ bool PowerLimiterClass::setNewPowerLimit(std::shared_ptr<InverterAbstract> inver
487507

488508
// Stop the inverter if limit is below threshold.
489509
if (newPowerLimit < config.PowerLimiter_LowerPowerLimit) {
490-
shutdown(Status::LowerLimitUndercut);
491-
return true;
510+
// the status must not change outside of loop(). this condition is
511+
// communicated through log messages already.
512+
return shutdown();
492513
}
493514

494515
// enforce configured upper power limit

0 commit comments

Comments
 (0)