diff --git a/lib/VeDirectFrameHandler/VeDirectData.cpp b/lib/VeDirectFrameHandler/VeDirectData.cpp index 74e4bc484..d929a471f 100644 --- a/lib/VeDirectFrameHandler/VeDirectData.cpp +++ b/lib/VeDirectFrameHandler/VeDirectData.cpp @@ -143,6 +143,17 @@ frozen::string const& veStruct::getPidAsString() 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(strtoul(strVer.c_str(), nullptr, 10)); + } + char const* strVersion = firmwareVer_FW; // 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 { + 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 rc = 0; diff --git a/lib/VeDirectFrameHandler/VeDirectData.h b/lib/VeDirectFrameHandler/VeDirectData.h index e5b2868c6..4cda91274 100644 --- a/lib/VeDirectFrameHandler/VeDirectData.h +++ b/lib/VeDirectFrameHandler/VeDirectData.h @@ -11,6 +11,8 @@ typedef struct { uint16_t productID_PID = 0; // product id char serialNr_SER[VE_MAX_VALUE_LEN]; // serial 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 int32_t batteryCurrent_I_mA = 0; // battery current in mA (can be negative) float mpptEfficiency_Percent = 0; // efficiency in percent (calculated, moving average) diff --git a/lib/VeDirectFrameHandler/VeDirectFrameHandler.cpp b/lib/VeDirectFrameHandler/VeDirectFrameHandler.cpp index 9de8151df..6073c56d2 100644 --- a/lib/VeDirectFrameHandler/VeDirectFrameHandler.cpp +++ b/lib/VeDirectFrameHandler/VeDirectFrameHandler.cpp @@ -249,10 +249,18 @@ void VeDirectFrameHandler::processTextData(std::string const& name, std::stri } if (name == "FW") { + _tmpFrame.firmwareVer_FWE[0] = '\0'; strncpy(_tmpFrame.firmwareVer_FW, value.c_str(), sizeof(_tmpFrame.firmwareVer_FW)); 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") { _tmpFrame.batteryVoltage_V_mV = atol(value.c_str()); return; diff --git a/src/MqttHandleVedirect.cpp b/src/MqttHandleVedirect.cpp index 1e56aff90..55334df50 100644 --- a/src/MqttHandleVedirect.cpp +++ b/src/MqttHandleVedirect.cpp @@ -110,7 +110,10 @@ void MqttHandleVedirectClass::publish_mppt_data(const VeDirectMpptController::da PUBLISH(productID_PID, "PID", currentData.getPidAsString().data()); 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_FWE, "FWE", currentData.firmwareVer_FWE); PUBLISH(currentState_CS, "CS", currentData.getCsAsString().data()); PUBLISH(errorCode_ERR, "ERR", currentData.getErrAsString().data()); PUBLISH(offReason_OR, "OR", currentData.getOrAsString().data()); diff --git a/src/MqttHandleVedirectHass.cpp b/src/MqttHandleVedirectHass.cpp index 0683c130c..279c258d7 100644 --- a/src/MqttHandleVedirectHass.cpp +++ b/src/MqttHandleVedirectHass.cpp @@ -64,7 +64,10 @@ void MqttHandleVedirectHassClass::publishConfig() if (!optMpptData.has_value()) { continue; } 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 error code", "mdi:bell", "ERR", nullptr, nullptr, nullptr, *optMpptData); publishSensor("MPPT off reason", "mdi:wrench", "OR", nullptr, nullptr, nullptr, *optMpptData);