Skip to content

Commit

Permalink
get absorption- and float-voltage from MPPTs
Browse files Browse the repository at this point in the history
  • Loading branch information
SW-Niko committed Jul 29, 2024
1 parent accc70d commit 4e4cdf5
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 19 deletions.
10 changes: 10 additions & 0 deletions include/VictronMppt.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ class VictronMpptClass {
// minimum of all MPPT charge controllers' output voltages in V
float getOutputVoltage() const;

// returns the state of operation from the first available controller
int16_t getStateOfOperation() const;

// the configured value from the first available controller in V
enum class MPPTVoltage : uint8_t {
ABSORPTION = 0,
FLOAT = 1,
};
float getVoltage(MPPTVoltage kindOf) const;

private:
void loop();
VictronMpptClass(VictronMpptClass const& other) = delete;
Expand Down
15 changes: 12 additions & 3 deletions lib/VeDirectFrameHandler/VeDirectData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ frozen::string const& veMpptStruct::getCsAsString() const
{ 0, "OFF" },
{ 2, "Fault" },
{ 3, "Bulk" },
{ 4, "Absorbtion" },
{ 4, "Absorption" },
{ 5, "Float" },
{ 7, "Equalize (manual)" },
{ 245, "Starting-up" },
Expand Down Expand Up @@ -287,18 +287,27 @@ frozen::string const& VeDirectHexData::getResponseAsString() const
frozen::string const& VeDirectHexData::getRegisterAsString() const
{
using Register = VeDirectHexRegister;
static constexpr frozen::map<Register, frozen::string, 11> values = {
static constexpr frozen::map<Register, frozen::string, 20> values = {
{ Register::DeviceMode, "Device Mode" },
{ Register::DeviceState, "Device State" },
{ Register::RemoteControlUsed, "Remote Control Used" },
{ Register::PanelVoltage, "Panel Voltage" },
{ Register::PanelPower, "Panel Power" },
{ Register::ChargerVoltage, "Charger Voltage" },
{ Register::ChargerCurrent, "Charger Current" },
{ Register::NetworkTotalDcInputPower, "Network Total DC Input Power" },
{ Register::ChargeControllerTemperature, "Charger Controller Temperature" },
{ Register::SmartBatterySenseTemperature, "Smart Battery Sense Temperature" },
{ Register::NetworkInfo, "Network Info" },
{ Register::NetworkMode, "Network Mode" },
{ Register::NetworkStatus, "Network Status" }
{ Register::NetworkStatus, "Network Status" },
{ Register::BatteryAbsorptionVoltage, "Battery Absorption Voltage" },
{ Register::BatteryFloatVoltage, "Battery Float Voltage" },
{ Register::TotalChargeCurrent, "Total Charge Current" },
{ Register::ChargeStateElapsedTime, "Charge State Elapsed Time" },
{ Register::BatteryVoltageSense, "Battery Voltage Sense" },
{ Register::LoadCurrent, "Load current" },
{ Register::LoadOutputVoltage, "Load Output Voltage" }
};

return getAsString(values, addr);
Expand Down
13 changes: 12 additions & 1 deletion lib/VeDirectFrameHandler/VeDirectData.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ struct veMpptStruct : veStruct {
std::pair<uint32_t, int32_t> MpptTemperatureMilliCelsius;
std::pair<uint32_t, int32_t> SmartBatterySenseTemperatureMilliCelsius;
std::pair<uint32_t, uint32_t> NetworkTotalDcInputPowerMilliWatts;
std::pair<uint32_t, uint32_t> BatteryAbsorptionMilliVolt;
std::pair<uint32_t, uint32_t> BatteryFloatMilliVolt;
std::pair<uint32_t, uint8_t> NetworkInfo;
std::pair<uint32_t, uint8_t> NetworkMode;
std::pair<uint32_t, uint8_t> NetworkStatus;
Expand Down Expand Up @@ -121,15 +123,24 @@ enum class VeDirectHexRegister : uint16_t {
DeviceState = 0x0201,
RemoteControlUsed = 0x0202,
PanelVoltage = 0xEDBB,
PanelPower = 0xEDBC,
ChargerVoltage = 0xEDD5,
ChargerCurrent = 0xEDD7,
NetworkTotalDcInputPower = 0x2027,
ChargeControllerTemperature = 0xEDDB,
SmartBatterySenseTemperature = 0xEDEC,
NetworkInfo = 0x200D,
NetworkMode = 0x200E,
NetworkStatus = 0x200F,
HistoryTotal = 0x104F,
HistoryMPPTD30 = 0x10BE
HistoryMPPTD30 = 0x10BE,
BatteryAbsorptionVoltage = 0xEDF7,
BatteryFloatVoltage = 0xEDF6,
TotalChargeCurrent = 0x2013,
ChargeStateElapsedTime= 0x2007,
BatteryVoltageSense = 0x2002,
LoadCurrent = 0xEDAD,
LoadOutputVoltage = 0xEDA9
};

struct VeDirectHexData {
Expand Down
12 changes: 6 additions & 6 deletions lib/VeDirectFrameHandler/VeDirectFrameHexHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ HexHandler.cpp
* 1. Use sendHexCommand() to send hex messages. Use the Victron documentation to find the parameter.
* 2. The from class "VeDirectFrameHandler" derived class X must overwrite the function
* void VeDirectFrameHandler::hexDataHandler(VeDirectHexData const &data)
* to handle the received hex messages. All hex messages will be forwarted to function hexDataHandler()
* to handle the received hex messages. All hex messages will be forwarded to function hexDataHandler()
* 3. Analyse the content of data (struct VeDirectHexData) to check if a message fits.
*
* 2024.03.08 - 0.4 - adds the ability to send hex commands and to parse hex messages
Expand Down Expand Up @@ -63,9 +63,9 @@ static uint32_t AsciiHexLE2Int(const char *ascii, const uint8_t anz) {
* disassembleHexData()
* analysis the hex message and extract: response, address, flags and value/text
* buffer: pointer to message (ascii hex little endian format)
* data: disassembeled message
* return: true = successful disassembeld, false = hex sum fault or message
* do not aligin with VE.Diekt syntax
* data: disassembled message
* return: true = successful disassembled, false = hex sum fault or message
* do not align with VE.Direct syntax
*/
template<typename T>
bool VeDirectFrameHandler<T>::disassembleHexData(VeDirectHexData &data) {
Expand Down Expand Up @@ -164,14 +164,14 @@ static String Int2HexLEString(uint32_t value, uint8_t anz) {
* addr: register address, default 0
* value: value to write into a register, default 0
* valsize: size of the value, 8, 16 or 32 bit, default 0
* return: true = message assembeld and send, false = it was not possible to put the message together
* return: true = message assembled and send, false = it was not possible to put the message together
* SAMPLE: ping command: sendHexCommand(PING),
* read total DC input power sendHexCommand(GET, 0xEDEC)
* set Charge current limit 10A sendHexCommand(SET, 0x2015, 64, 16)
*
* WARNING: some values are stored in non-volatile memory. Continuous writing, for example from a control loop, will
* lead to early failure.
* On MPPT for example 0xEDE0 - 0xEDFF. Check the Vivtron doc "BlueSolar-HEX-protocol.pdf"
* On MPPT for example 0xEDE0 - 0xEDFF. Check the Victron doc "BlueSolar-HEX-protocol.pdf"
*/
template<typename T>
bool VeDirectFrameHandler<T>::sendHexCommand(VeDirectHexCommand cmd, VeDirectHexRegister addr, uint32_t value, uint8_t valsize) {
Expand Down
46 changes: 37 additions & 9 deletions lib/VeDirectFrameHandler/VeDirectMpptController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,15 +111,18 @@ void VeDirectMpptController::frameValidEvent() {
// For firmware version v1.52 and below, when no VE.Direct queries are sent to the device, the
// charger periodically sends human readable (TEXT) data to the serial port. For firmware
// versions v1.53 and above, the charger always periodically sends TEXT data to the serial port.
// --> We just use hex commandes for firmware >= 1.53 to keep text messages alive
// --> We just use hex commands for firmware >= 1.53 to keep text messages alive
if (_tmpFrame.getFwVersionAsInteger() < 153) { return; }

using Command = VeDirectHexCommand;
using Register = VeDirectHexRegister;

sendHexCommand(Command::GET, Register::ChargeControllerTemperature);
sendHexCommand(Command::GET, Register::SmartBatterySenseTemperature);
sendHexCommand(Command::GET, Register::NetworkTotalDcInputPower);
// It seems some commands get lost if we send to fast the next command.
// Maybe we produce an overflow on the MPPT receive buffer or we have to wait for the MPPT answer
// before we can send the next command.
// Now we send only one command after every text-mode-frame.
// We need a better solution if we add more commands
// Don't worry about the NetworkTotalDcInputPower. We get anyway asynchronous messages on every value change
sendHexCommand(VeDirectHexCommand::GET, _slotRegister[_slotNr++]);
if (_slotNr >= _slotRegister.size())
_slotNr = 0;

#ifdef PROCESS_NETWORK_STATE
sendHexCommand(Command::GET, Register::NetworkInfo);
Expand All @@ -142,6 +145,8 @@ void VeDirectMpptController::loop()
resetTimestamp(_tmpFrame.MpptTemperatureMilliCelsius);
resetTimestamp(_tmpFrame.SmartBatterySenseTemperatureMilliCelsius);
resetTimestamp(_tmpFrame.NetworkTotalDcInputPowerMilliWatts);
resetTimestamp(_tmpFrame.BatteryFloatMilliVolt);
resetTimestamp(_tmpFrame.BatteryAbsorptionMilliVolt);

#ifdef PROCESS_NETWORK_STATE
resetTimestamp(_tmpFrame.NetworkInfo);
Expand All @@ -153,8 +158,8 @@ void VeDirectMpptController::loop()

/*
* hexDataHandler()
* analyse the content of VE.Direct hex messages
* Handels the received hex data from the MPPT
* analyze the content of VE.Direct hex messages
* handel's the received hex data from the MPPT
*/
bool VeDirectMpptController::hexDataHandler(VeDirectHexData const &data) {
if (data.rsp != VeDirectHexResponse::GET &&
Expand Down Expand Up @@ -215,6 +220,29 @@ bool VeDirectMpptController::hexDataHandler(VeDirectHexData const &data) {
return true;
break;

case VeDirectHexRegister::BatteryAbsorptionVoltage:
_tmpFrame.BatteryAbsorptionMilliVolt =
{ millis(), static_cast<uint32_t>(data.value) * 10 };
if (_verboseLogging) {
_msgOut->printf("%s Hex Data: MPPT Absorption Voltage (0x%04X): %.2fV\r\n",
_logId, regLog,
_tmpFrame.BatteryAbsorptionMilliVolt.second / 1000.0);
}
return true;
break;

case VeDirectHexRegister::BatteryFloatVoltage:
_tmpFrame.BatteryFloatMilliVolt =
{ millis(), static_cast<uint32_t>(data.value) * 10 };

if (_verboseLogging) {
_msgOut->printf("%s Hex Data: MPPT Float Voltage (0x%04X): %.2fV\r\n",
_logId, regLog,
_tmpFrame.BatteryFloatMilliVolt.second / 1000.0);
}
return true;
break;

#ifdef PROCESS_NETWORK_STATE
case VeDirectHexRegister::NetworkInfo:
_tmpFrame.NetworkInfo =
Expand Down
6 changes: 6 additions & 0 deletions lib/VeDirectFrameHandler/VeDirectMpptController.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,10 @@ class VeDirectMpptController : public VeDirectFrameHandler<veMpptStruct> {
bool processTextDataDerived(std::string const& name, std::string const& value) final;
void frameValidEvent() final;
MovingAverage<float, 5> _efficiency;
int8_t _slotNr = 0;
std::array<VeDirectHexRegister, 5> _slotRegister { VeDirectHexRegister::NetworkTotalDcInputPower,
VeDirectHexRegister::ChargeControllerTemperature,
VeDirectHexRegister::SmartBatterySenseTemperature,
VeDirectHexRegister::BatteryFloatVoltage,
VeDirectHexRegister::BatteryAbsorptionVoltage };
};
42 changes: 42 additions & 0 deletions src/VictronMppt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ bool VictronMpptClass::isDataValid() const

for (auto const& upController: _controllers) {
if (upController->isDataValid()) { return true; }
if (upController->isDataValid()) { return true; }
}

return !_controllers.empty();
Expand Down Expand Up @@ -226,3 +227,44 @@ float VictronMpptClass::getOutputVoltage() const

return min;
}

/*
* getStateOfOperation()
* return: the state from the first available controller or
* -1 if data is not available
*/
int16_t VictronMpptClass::getStateOfOperation() const
{
for (const auto& upController : _controllers) {
if (upController->isDataValid())
return static_cast<int16_t>(upController->getData().currentState_CS);
}

return -1;
}

/*
* getVoltage()
* return: the configured value from the first available controller in V or
* -1V if data is not available
*/
float VictronMpptClass::getVoltage(MPPTVoltage kindOf) const
{
std::pair<uint32_t, uint32_t> voltX;

for (const auto& upController : _controllers) {
switch (kindOf) {
case MPPTVoltage::ABSORPTION:
voltX = upController->getData().BatteryAbsorptionMilliVolt;
break;
case MPPTVoltage::FLOAT:
voltX = upController->getData().BatteryFloatMilliVolt;
break;
}
if (voltX.first > 0) {
return static_cast<float>(voltX.second / 1000.0);
}
}

return -1.0f;
}

0 comments on commit 4e4cdf5

Please sign in to comment.