Feature: process VE.Direct "FWE" text data

This commit is contained in:
Bernhard Kirchen 2024-11-20 22:35:02 +01:00 committed by Bernhard Kirchen
parent 4beea357e7
commit bfa55a8371
5 changed files with 56 additions and 1 deletions

View File

@ -143,6 +143,17 @@ frozen::string const& veStruct::getPidAsString() const
*/ */
uint32_t veStruct::getFwVersionAsInteger() const uint32_t veStruct::getFwVersionAsInteger() const
{ {
if (strlen(firmwareVer_FW) == 0) {
if (strlen(firmwareVer_FWE) == 0) { return 0; }
// the firmware version from the FWE field may be preceeded by a zero
// for padding as per VE.Direct protocol, which is fine for strtoul()
// when we use a fixed base. however, the postfix (2 chars) might be
// numeric as well to indicate a beta release, which we must not parse.
std::string strVer(firmwareVer_FWE, strlen(firmwareVer_FWE) - 2);
return static_cast<uint32_t>(strtoul(strVer.c_str(), nullptr, 10));
}
char const* strVersion = firmwareVer_FW; char const* strVersion = firmwareVer_FW;
// VE.Direct protocol manual states that the first char can be a non-digit, // VE.Direct protocol manual states that the first char can be a non-digit,
@ -157,6 +168,34 @@ uint32_t veStruct::getFwVersionAsInteger() const
*/ */
String veStruct::getFwVersionFormatted() const String veStruct::getFwVersionFormatted() const
{ {
if (strlen(firmwareVer_FW) == 0 && strlen(firmwareVer_FWE) == 0) {
return "n/a";
}
if (strlen(firmwareVer_FWE) > 0) {
char const* strVersion = firmwareVer_FWE;
// the firmware version from the FWE field may be preceeded by a zero
// for padding as per VE.Direct protocol.
while (strVersion[0] == '0') { ++strVersion; }
String res(strVersion[0]);
strVersion++;
res += ".";
res += strVersion[0];
strVersion++;
res += strVersion[0];
strVersion++;
String suffix(strVersion);
suffix.toUpperCase();
if (suffix == "FF") { return res; }
res += "-beta-";
res += suffix;
return res;
}
char const* strVersion = firmwareVer_FW; char const* strVersion = firmwareVer_FW;
char rc = 0; char rc = 0;

View File

@ -11,6 +11,8 @@ typedef struct {
uint16_t productID_PID = 0; // product id uint16_t productID_PID = 0; // product id
char serialNr_SER[VE_MAX_VALUE_LEN]; // serial number char serialNr_SER[VE_MAX_VALUE_LEN]; // serial number
char firmwareVer_FW[VE_MAX_VALUE_LEN]; // firmware release number char firmwareVer_FW[VE_MAX_VALUE_LEN]; // firmware release number
// some devices use "FWE" instead of "FW" for the firmware version.
char firmwareVer_FWE[VE_MAX_VALUE_LEN]; // firmware release number (alternative field)
uint32_t batteryVoltage_V_mV = 0; // battery voltage in mV uint32_t batteryVoltage_V_mV = 0; // battery voltage in mV
int32_t batteryCurrent_I_mA = 0; // battery current in mA (can be negative) int32_t batteryCurrent_I_mA = 0; // battery current in mA (can be negative)
float mpptEfficiency_Percent = 0; // efficiency in percent (calculated, moving average) float mpptEfficiency_Percent = 0; // efficiency in percent (calculated, moving average)

View File

@ -249,10 +249,18 @@ void VeDirectFrameHandler<T>::processTextData(std::string const& name, std::stri
} }
if (name == "FW") { if (name == "FW") {
_tmpFrame.firmwareVer_FWE[0] = '\0';
strncpy(_tmpFrame.firmwareVer_FW, value.c_str(), sizeof(_tmpFrame.firmwareVer_FW)); strncpy(_tmpFrame.firmwareVer_FW, value.c_str(), sizeof(_tmpFrame.firmwareVer_FW));
return; return;
} }
// some devices use "FWE" instead of "FW" for the firmware version.
if (name == "FWE") {
_tmpFrame.firmwareVer_FW[0] = '\0';
strncpy(_tmpFrame.firmwareVer_FWE, value.c_str(), sizeof(_tmpFrame.firmwareVer_FWE));
return;
}
if (name == "V") { if (name == "V") {
_tmpFrame.batteryVoltage_V_mV = atol(value.c_str()); _tmpFrame.batteryVoltage_V_mV = atol(value.c_str());
return; return;

View File

@ -110,7 +110,10 @@ void MqttHandleVedirectClass::publish_mppt_data(const VeDirectMpptController::da
PUBLISH(productID_PID, "PID", currentData.getPidAsString().data()); PUBLISH(productID_PID, "PID", currentData.getPidAsString().data());
PUBLISH(serialNr_SER, "SER", currentData.serialNr_SER); PUBLISH(serialNr_SER, "SER", currentData.serialNr_SER);
PUBLISH(firmwareVer_FW, "FWI", currentData.getFwVersionAsInteger());
PUBLISH(firmwareVer_FW, "FWF", currentData.getFwVersionFormatted());
PUBLISH(firmwareVer_FW, "FW", currentData.firmwareVer_FW); PUBLISH(firmwareVer_FW, "FW", currentData.firmwareVer_FW);
PUBLISH(firmwareVer_FWE, "FWE", currentData.firmwareVer_FWE);
PUBLISH(currentState_CS, "CS", currentData.getCsAsString().data()); PUBLISH(currentState_CS, "CS", currentData.getCsAsString().data());
PUBLISH(errorCode_ERR, "ERR", currentData.getErrAsString().data()); PUBLISH(errorCode_ERR, "ERR", currentData.getErrAsString().data());
PUBLISH(offReason_OR, "OR", currentData.getOrAsString().data()); PUBLISH(offReason_OR, "OR", currentData.getOrAsString().data());

View File

@ -64,7 +64,10 @@ void MqttHandleVedirectHassClass::publishConfig()
if (!optMpptData.has_value()) { continue; } if (!optMpptData.has_value()) { continue; }
publishSensor("MPPT serial number", "mdi:counter", "SER", nullptr, nullptr, nullptr, *optMpptData); publishSensor("MPPT serial number", "mdi:counter", "SER", nullptr, nullptr, nullptr, *optMpptData);
publishSensor("MPPT firmware number", "mdi:counter", "FW", nullptr, nullptr, nullptr, *optMpptData); publishSensor("MPPT firmware version integer", "mdi:counter", "FWI", nullptr, nullptr, nullptr, *optMpptData);
publishSensor("MPPT firmware version formatted", "mdi:counter", "FWF", nullptr, nullptr, nullptr, *optMpptData);
publishSensor("MPPT firmware version FW", "mdi:counter", "FW", nullptr, nullptr, nullptr, *optMpptData);
publishSensor("MPPT firmware version FWE", "mdi:counter", "FWE", nullptr, nullptr, nullptr, *optMpptData);
publishSensor("MPPT state of operation", "mdi:wrench", "CS", nullptr, nullptr, nullptr, *optMpptData); publishSensor("MPPT state of operation", "mdi:wrench", "CS", nullptr, nullptr, nullptr, *optMpptData);
publishSensor("MPPT error code", "mdi:bell", "ERR", nullptr, nullptr, nullptr, *optMpptData); publishSensor("MPPT error code", "mdi:bell", "ERR", nullptr, nullptr, nullptr, *optMpptData);
publishSensor("MPPT off reason", "mdi:wrench", "OR", nullptr, nullptr, nullptr, *optMpptData); publishSensor("MPPT off reason", "mdi:wrench", "OR", nullptr, nullptr, nullptr, *optMpptData);