* DPL: wait for valid time information we know that the Hoymiles library refuses to send any message to any inverter until the system has valid time information. until then we can do nothing, not even shutdown the inverter. * DPL: wait for device info to be ready a calculated power limit will always be limited to the reported device's max power. that upper limit is only known after the first DevInfoSimpleCommand succeeded. wait for that information to be available. * DPL: fix initial calculcation backoff if the calculation backoff is initialized to zero, the backoff will be doubled to zero until a new, different power limit was calculated for the first time. this lead to the DPL recalculating a power limit hundreds of times without a backoff after startup.
90 lines
3.1 KiB
C++
90 lines
3.1 KiB
C++
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
#pragma once
|
|
|
|
#include "Configuration.h"
|
|
#include <espMqttClient.h>
|
|
#include <Arduino.h>
|
|
#include <Hoymiles.h>
|
|
#include <memory>
|
|
|
|
#define PL_UI_STATE_INACTIVE 0
|
|
#define PL_UI_STATE_CHARGING 1
|
|
#define PL_UI_STATE_USE_SOLAR_ONLY 2
|
|
#define PL_UI_STATE_USE_SOLAR_AND_BATTERY 3
|
|
|
|
#define PL_MODE_ENABLE_NORMAL_OP 0
|
|
#define PL_MODE_FULL_DISABLE 1
|
|
#define PL_MODE_SOLAR_PT_ONLY 2
|
|
|
|
typedef enum {
|
|
EMPTY_WHEN_FULL= 0,
|
|
EMPTY_AT_NIGHT
|
|
} batDrainStrategy;
|
|
|
|
|
|
class PowerLimiterClass {
|
|
public:
|
|
enum class Status : unsigned {
|
|
Initializing,
|
|
DisabledByConfig,
|
|
DisabledByMqtt,
|
|
WaitingForValidTimestamp,
|
|
PowerMeterDisabled,
|
|
PowerMeterTimeout,
|
|
PowerMeterPending,
|
|
InverterInvalid,
|
|
InverterChanged,
|
|
InverterOffline,
|
|
InverterCommandsDisabled,
|
|
InverterLimitPending,
|
|
InverterPowerCmdPending,
|
|
InverterDevInfoPending,
|
|
InverterStatsPending,
|
|
UnconditionalSolarPassthrough,
|
|
NoVeDirect,
|
|
Settling,
|
|
Stable,
|
|
LowerLimitUndercut
|
|
};
|
|
|
|
void init();
|
|
void loop();
|
|
uint8_t getPowerLimiterState();
|
|
int32_t getLastRequestedPowerLimit();
|
|
void setMode(uint8_t mode);
|
|
bool getMode();
|
|
void calcNextInverterRestart();
|
|
|
|
private:
|
|
int32_t _lastRequestedPowerLimit = 0;
|
|
bool _shutdownInProgress;
|
|
Status _lastStatus = Status::Initializing;
|
|
uint32_t _lastStatusPrinted = 0;
|
|
uint32_t _lastCalculation = 0;
|
|
static constexpr uint32_t _calculationBackoffMsDefault = 128;
|
|
uint32_t _calculationBackoffMs = _calculationBackoffMsDefault;
|
|
uint8_t _mode = PL_MODE_ENABLE_NORMAL_OP;
|
|
std::shared_ptr<InverterAbstract> _inverter = nullptr;
|
|
bool _batteryDischargeEnabled = false;
|
|
uint32_t _nextInverterRestart = 0; // Values: 0->not calculated / 1->no restart configured / >1->time of next inverter restart in millis()
|
|
uint32_t _nextCalculateCheck = 5000; // time in millis for next NTP check to calulate restart
|
|
bool _fullSolarPassThroughEnabled = false;
|
|
|
|
std::string const& getStatusText(Status status);
|
|
void announceStatus(Status status);
|
|
void shutdown(Status status);
|
|
int32_t inverterPowerDcToAc(std::shared_ptr<InverterAbstract> inverter, int32_t dcPower);
|
|
void unconditionalSolarPassthrough(std::shared_ptr<InverterAbstract> inverter);
|
|
bool canUseDirectSolarPower();
|
|
int32_t calcPowerLimit(std::shared_ptr<InverterAbstract> inverter, bool solarPowerEnabled, bool batteryDischargeEnabled);
|
|
void commitPowerLimit(std::shared_ptr<InverterAbstract> inverter, int32_t limit, bool enablePowerProduction);
|
|
bool setNewPowerLimit(std::shared_ptr<InverterAbstract> inverter, int32_t newPowerLimit);
|
|
int32_t getSolarChargePower();
|
|
float getLoadCorrectedVoltage(std::shared_ptr<InverterAbstract> inverter);
|
|
bool isStartThresholdReached(std::shared_ptr<InverterAbstract> inverter);
|
|
bool isStopThresholdReached(std::shared_ptr<InverterAbstract> inverter);
|
|
bool useFullSolarPassthrough(std::shared_ptr<InverterAbstract> inverter);
|
|
};
|
|
|
|
extern PowerLimiterClass PowerLimiter;
|