Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 78 additions & 34 deletions src/Components/Wallbox/HeidelbergWallbox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,34 @@ HeidelbergWallbox *HeidelbergWallbox::Instance()
return &instance;
}

// The wallbox accepts 0 A or 6-16 A. Below the minimum means blocked, which is
// also what a client writing 0 to stop charging expects.
float HeidelbergWallbox::ClampToWallboxRange(float currentLimitA)
{
if (currentLimitA < Constants::HeidelbergWallbox::MinChargingCurrentA)
{
return 0.0f;
}
if (currentLimitA > Constants::HeidelbergWallbox::MaxChargingCurrentA)
{
return Constants::HeidelbergWallbox::MaxChargingCurrentA;
}
return currentLimitA;
}

// 0 A blocks charging, 6-16 A permits it
bool HeidelbergWallbox::WriteCurrentLimitRegister(float currentLimitA)
{
const uint16_t rawCurrent = static_cast<uint16_t>(currentLimitA / Constants::HeidelbergWallbox::CurrentFactor);
if (!ModbusRTU::Instance()->WriteHoldRegister16(Constants::HeidelbergRegisters::MaximalCurrent, rawCurrent))
{
// Error writing modbus register
Logger::Error("Heidelberg wallbox: ERROR: Could not set maximum charging current");
return false;
}
return true;
}

void HeidelbergWallbox::Init()
{
uint16_t rawCurrent = static_cast<uint16_t>(Constants::HeidelbergWallbox::FailSafeCurrentA / Constants::HeidelbergWallbox::CurrentFactor);
Expand All @@ -36,6 +64,30 @@ void HeidelbergWallbox::Init()
// Error writing modbus register
Logger::Error("ERROR: Could not set watchdog timeout");
}

// Register 261 keeps its value when this bridge restarts, so seed our state
// from it rather than assuming charging is enabled.
uint16_t rawLimit[1];
if (ModbusRTU::Instance()->ReadRegisters(Constants::HeidelbergRegisters::MaximalCurrent, 1, 0x3, rawLimit))
{
mObservedChargingCurrentLimitA = static_cast<float>(rawLimit[0] * Constants::HeidelbergWallbox::CurrentFactor);
mChargingEnabled = mObservedChargingCurrentLimitA >= Constants::HeidelbergWallbox::MinChargingCurrentA;

if (mChargingEnabled)
{
// Adopt what the wallbox is already applying. Clamped because the
// register is 16 bit and another controller may have left a larger
// value in it.
mRequestedChargingCurrentLimitA = ClampToWallboxRange(mObservedChargingCurrentLimitA);
}

Logger::Info("Heidelberg wallbox: seeded state from register: %f A, charging %s",
mObservedChargingCurrentLimitA, mChargingEnabled ? "enabled" : "disabled");
}
else
{
Logger::Error("Heidelberg wallbox: ERROR: Could not read charging current limit during init");
}
}

VehicleState HeidelbergWallbox::GetState()
Expand Down Expand Up @@ -68,50 +120,40 @@ VehicleState HeidelbergWallbox::GetState()

bool HeidelbergWallbox::SetChargingCurrentLimit(float currentLimitA)
{
if (mChargingEnabled)
{
mChargingCurrentLimitA = currentLimitA;
Logger::Info("Heidelberg wallbox: setting charging current limit to %f A", mChargingCurrentLimitA);
currentLimitA = ClampToWallboxRange(currentLimitA);
mRequestedChargingCurrentLimitA = currentLimitA;

uint16_t rawCurrent = static_cast<uint16_t>(mChargingCurrentLimitA / Constants::HeidelbergWallbox::CurrentFactor);
if (!ModbusRTU::Instance()->WriteHoldRegister16(Constants::HeidelbergRegisters::MaximalCurrent, rawCurrent))
{
// Error writing modbus register
Logger::Error("Heidelberg wallbox: ERROR: Could not set maximum charging current");
}
}
else
if (!mChargingEnabled)
{
mPreviousChargingCurrentLimitA = currentLimitA;
Logger::Info("Heidelberg wallbox: charging is disabled. current limit %f A is not applied", mChargingCurrentLimitA);
Logger::Info("Heidelberg wallbox: charging is disabled. current limit %f A is not applied yet", currentLimitA);
return true;
}

return true;
Logger::Info("Heidelberg wallbox: setting charging current limit to %f A", currentLimitA);
return WriteCurrentLimitRegister(currentLimitA);
}

bool HeidelbergWallbox::SetChargingEnabled(bool chargingEnabled)
{
bool ok = true;
Logger::Info("Heidelberg wallbox: %s charging", chargingEnabled ? "enabling" : "disabling");

if (!mChargingEnabled && chargingEnabled)
{
Logger::Info("Heidelberg wallbox: enabling charging");
mChargingEnabled = chargingEnabled;

// Enable charging
mChargingEnabled = true;
ok = SetChargingCurrentLimit(mPreviousChargingCurrentLimitA);
}
else if (mChargingEnabled && !chargingEnabled)
// Written on every call, not only on a transition: the flag is ours, the
// register is the wallbox's, and the two can disagree.
if (!chargingEnabled)
{
Logger::Info("Heidelberg wallbox: disabling charging");
return WriteCurrentLimitRegister(0.0f);
}

// Disable charging
mPreviousChargingCurrentLimitA = mChargingCurrentLimitA;
ok = SetChargingCurrentLimit(0.0f);
mChargingEnabled = false;
// A setpoint below the minimum would leave charging blocked, which contradicts
// the request to enable it. Fall back to the default.
if (mRequestedChargingCurrentLimitA < Constants::HeidelbergWallbox::MinChargingCurrentA)
{
mRequestedChargingCurrentLimitA = Constants::HeidelbergWallbox::InitialChargingCurrentLimitA;
}

return ok;
return WriteCurrentLimitRegister(mRequestedChargingCurrentLimitA);
}

bool HeidelbergWallbox::IsChargingEnabled()
Expand Down Expand Up @@ -167,19 +209,21 @@ bool HeidelbergWallbox::GetStandbyEnabled()

float HeidelbergWallbox::GetChargingCurrentLimit()
{
// Telemetry only. Must not write mRequestedChargingCurrentLimitA: a reading of
// 0 A would destroy the limit to apply the next time charging is enabled.
uint16_t registerValue[1];
if (ModbusRTU::Instance()->ReadRegisters(Constants::HeidelbergRegisters::MaximalCurrent, 1, 0x3, registerValue))
{
mChargingCurrentLimitA = static_cast<float>(registerValue[0] * Constants::HeidelbergWallbox::CurrentFactor);
Logger::Debug("Heidelberg wallbox: Read max. charging current: %f", mChargingCurrentLimitA);
return mChargingCurrentLimitA;
mObservedChargingCurrentLimitA = static_cast<float>(registerValue[0] * Constants::HeidelbergWallbox::CurrentFactor);
Logger::Debug("Heidelberg wallbox: Read max. charging current: %f", mObservedChargingCurrentLimitA);
}
else
{
// Error reading modbus register
Logger::Error("Heidelberg wallbox: ERROR: Could not read max. charging current");
return mChargingCurrentLimitA; // Return last valid value
}

return mObservedChargingCurrentLimitA; // last known value if the read failed
}

float HeidelbergWallbox::GetEnergyMeterValue()
Expand Down
8 changes: 5 additions & 3 deletions src/Components/Wallbox/HeidelbergWallbox.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ class HeidelbergWallbox : public IWallbox
{
private:
HeidelbergWallbox() {};
float ClampToWallboxRange(float currentLimitA);
bool WriteCurrentLimitRegister(float currentLimitA);

public:
static HeidelbergWallbox *Instance();
Expand All @@ -31,11 +33,11 @@ class HeidelbergWallbox : public IWallbox

private:
VehicleState mState{VehicleState::Disconnected};
float mChargingCurrentLimitA{Constants::HeidelbergWallbox::InitialChargingCurrentLimitA};
float mRequestedChargingCurrentLimitA{Constants::HeidelbergWallbox::InitialChargingCurrentLimitA}; // intent
float mObservedChargingCurrentLimitA{0.0f}; // last read back
float mFailsafeCurrentA{0.0f};
float mLastPowerMeterValueW{0.0f};
float mLastEnergyMeterValueWh{0.0f};
bool mChargingEnabled{true};
bool mChargingEnabled{true}; // seeded from the wallbox in Init()
bool mStandbyEnabled{true}; // default: standby enabled
float mPreviousChargingCurrentLimitA{Constants::HeidelbergWallbox::InitialChargingCurrentLimitA};
};
4 changes: 3 additions & 1 deletion src/Configuration/Constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ namespace Constants
constexpr float CurrentFactor = 0.1f;
constexpr float VoltageFactor = 1.0f;
constexpr float TemperatureFactor = 0.1f;
constexpr float InitialChargingCurrentLimitA = 16.0f;
constexpr float MinChargingCurrentA = 6.0f;
constexpr float MaxChargingCurrentA = 16.0f;
constexpr float InitialChargingCurrentLimitA = MaxChargingCurrentA;
constexpr uint8_t ModbusServerId = 1;
constexpr uint32_t ModbusBaudrate = 19200;
constexpr uint16_t ModbusTimeoutMs = 2000;
Expand Down
Loading