diff --git a/src/CHANGES.md b/src/CHANGES.md index f0491335..c01127ce 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -1,5 +1,10 @@ # Development Changes +## 0.8.75 - 2024-02-06 +* fix active power control value #1406, #1409 +* update Mqtt lib to version `1.6.0` +* take care of null terminator of chars #1410, #1411 + ## 0.8.74 - 2024-02-05 * reduced cppcheck linter warnings significantly diff --git a/src/defines.h b/src/defines.h index bc9d2452..cdbeee42 100644 --- a/src/defines.h +++ b/src/defines.h @@ -13,7 +13,7 @@ //------------------------------------- #define VERSION_MAJOR 0 #define VERSION_MINOR 8 -#define VERSION_PATCH 74 +#define VERSION_PATCH 75 //------------------------------------- typedef struct { diff --git a/src/hm/hmInverter.h b/src/hm/hmInverter.h index 275fce2c..51cb0d6d 100644 --- a/src/hm/hmInverter.h +++ b/src/hm/hmInverter.h @@ -120,7 +120,7 @@ class Inverter { uint8_t type = INV_TYPE_1CH; // integer which refers to inverter type uint16_t alarmMesIndex = 0; // Last recorded Alarm Message Index uint16_t powerLimit[2] = {0xffff, AbsolutNonPersistent}; // limit power output (multiplied by 10) - float actPowerLimit = -1; // actual power limit + uint16_t actPowerLimit = 0xffff; // actual power limit bool powerLimitAck = false; // acknowledged power limit uint8_t devControlCmd = InitDataState; // carries the requested cmd serial_u radioId; // id converted to modbus diff --git a/src/platformio.ini b/src/platformio.ini index 9b97e018..f949aa37 100644 --- a/src/platformio.ini +++ b/src/platformio.ini @@ -28,7 +28,7 @@ lib_deps = https://github.com/yubox-node-org/ESPAsyncWebServer https://github.com/nRF24/RF24 @ 1.4.8 paulstoffregen/Time @ ^1.6.1 - https://github.com/bertmelis/espMqttClient#v1.5.0 + https://github.com/bertmelis/espMqttClient#v1.6.0 bblanchon/ArduinoJson @ ^6.21.3 https://github.com/JChristensen/Timezone @ ^1.2.4 olikraus/U8g2 @ ^2.35.9 @@ -197,7 +197,7 @@ lib_deps = khoih-prog/AsyncUDP_ESP32_W5500 https://github.com/nRF24/RF24 @ ^1.4.8 paulstoffregen/Time @ ^1.6.1 - https://github.com/bertmelis/espMqttClient#v1.5.0 + https://github.com/bertmelis/espMqttClient#v1.6.0 bblanchon/ArduinoJson @ ^6.21.3 https://github.com/JChristensen/Timezone @ ^1.2.4 olikraus/U8g2 @ ^2.35.9 @@ -220,7 +220,7 @@ lib_deps = khoih-prog/AsyncUDP_ESP32_W5500 https://github.com/nRF24/RF24 @ ^1.4.8 paulstoffregen/Time @ ^1.6.1 - https://github.com/bertmelis/espMqttClient#v1.5.0 + https://github.com/bertmelis/espMqttClient#v1.6.0 bblanchon/ArduinoJson @ ^6.21.3 https://github.com/JChristensen/Timezone @ ^1.2.4 olikraus/U8g2 @ ^2.35.9 @@ -414,7 +414,7 @@ lib_deps = khoih-prog/AsyncUDP_ESP32_W5500 https://github.com/nrf24/RF24 @ ^1.4.8 paulstoffregen/Time @ ^1.6.1 - https://github.com/bertmelis/espMqttClient#v1.5.0 + https://github.com/bertmelis/espMqttClient#v1.6.0 bblanchon/ArduinoJson @ ^6.21.3 https://github.com/JChristensen/Timezone @ ^1.2.4 olikraus/U8g2 @ ^2.35.9 @@ -459,7 +459,7 @@ lib_deps = khoih-prog/AsyncUDP_ESP32_W5500 https://github.com/nrf24/RF24 @ ^1.4.8 paulstoffregen/Time @ ^1.6.1 - https://github.com/bertmelis/espMqttClient#v1.5.0 + https://github.com/bertmelis/espMqttClient#v1.6.0 bblanchon/ArduinoJson @ ^6.21.3 https://github.com/JChristensen/Timezone @ ^1.2.4 olikraus/U8g2 @ ^2.35.9 diff --git a/src/publisher/pubMqtt.h b/src/publisher/pubMqtt.h index 7799b6d2..0e10e688 100644 --- a/src/publisher/pubMqtt.h +++ b/src/publisher/pubMqtt.h @@ -67,15 +67,15 @@ class PubMqtt { }); mDiscovery.running = false; - snprintf(mLwtTopic.data(), mLwtTopic.size(), "%s/mqtt", mCfgMqtt->topic); + snprintf(mLwtTopic.data(), mLwtTopic.size() - 1, "%s/mqtt", mCfgMqtt->topic); if((strlen(mCfgMqtt->user) > 0) && (strlen(mCfgMqtt->pwd) > 0)) mClient.setCredentials(mCfgMqtt->user, mCfgMqtt->pwd); if(strlen(mCfgMqtt->clientId) > 0) - snprintf(mClientId.data(), mClientId.size(), "%s", mCfgMqtt->clientId); + snprintf(mClientId.data(), mClientId.size() - 1, "%s", mCfgMqtt->clientId); else { - snprintf(mClientId.data(), mClientId.size(), "%s-", mDevName); + snprintf(mClientId.data(), mClientId.size() - 1, "%s-", mDevName); uint8_t pos = strlen(mClientId.data()); mClientId[pos++] = WiFi.macAddress().substring( 9, 10).c_str()[0]; mClientId[pos++] = WiFi.macAddress().substring(10, 11).c_str()[0]; @@ -129,7 +129,7 @@ class PubMqtt { } void tickerMinute() { - snprintf(mVal.data(), mVal.size(), "%u", (*mUptime)); + snprintf(mVal.data(), mVal.size() - 1, "%u", (*mUptime)); publish(subtopics[MQTT_UPTIME], mVal.data()); publish(subtopics[MQTT_RSSI], String(WiFi.RSSI()).c_str()); publish(subtopics[MQTT_FREE_HEAP], String(ESP.getFreeHeap()).c_str()); @@ -152,11 +152,11 @@ class PubMqtt { if(NULL == iv) continue; - snprintf(mSubTopic.data(), mSubTopic.size(), "%s/dis_night_comm", iv->config->name); + snprintf(mSubTopic.data(), mSubTopic.size() - 1, "%s/dis_night_comm", iv->config->name); publish(mSubTopic.data(), ((iv->commEnabled) ? dict[STR_TRUE] : dict[STR_FALSE]), true); } - snprintf(mSubTopic.data(), mSubTopic.size(), "comm_disabled"); + snprintf(mSubTopic.data(), mSubTopic.size() - 1, "comm_disabled"); publish(mSubTopic.data(), (((*mUtcTimestamp > (sunset + offsE)) || (*mUtcTimestamp < (sunrise + offsM))) ? dict[STR_TRUE] : dict[STR_FALSE]), true); return true; @@ -179,8 +179,8 @@ class PubMqtt { void tickerMidnight() { // set Total YieldDay to zero - snprintf(mSubTopic.data(), mSubTopic.size(), "total/%s", fields[FLD_YD]); - snprintf(mVal.data(), mVal.size(), "0"); + snprintf(mSubTopic.data(), mSubTopic.size() - 1, "total/%s", fields[FLD_YD]); + snprintf(mVal.data(), mVal.size() - 1, "0"); publish(mSubTopic.data(), mVal.data(), true); } @@ -200,9 +200,9 @@ class PubMqtt { return; if(addTopic) - snprintf(mTopic.data(), mTopic.size(), "%s/%s", mCfgMqtt->topic, subTopic); + snprintf(mTopic.data(), mTopic.size() - 1, "%s/%s", mCfgMqtt->topic, subTopic); else - snprintf(mTopic.data(), mTopic.size(), "%s", subTopic); + snprintf(mTopic.data(), mTopic.size() - 1, "%s", subTopic); mClient.publish(mTopic.data(), qos, retained, payload); yield(); @@ -241,7 +241,7 @@ class PubMqtt { void setPowerLimitAck(Inverter<> *iv) { if (NULL != iv) { - snprintf(mSubTopic.data(), mSubTopic.size(), "%s/%s", iv->config->name, subtopics[MQTT_ACK_PWR_LMT]); + snprintf(mSubTopic.data(), mSubTopic.size() - 1, "%s/%s", iv->config->name, subtopics[MQTT_ACK_PWR_LMT]); publish(mSubTopic.data(), "true", true, true, QOS_2); } } @@ -261,11 +261,11 @@ class PubMqtt { publish(mLwtTopic.data(), mqttStr[MQTT_STR_LWT_CONN], true, false); for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) { - snprintf(mVal.data(), mVal.size(), "ctrl/limit/%d", i); + snprintf(mVal.data(), mVal.size() - 1, "ctrl/limit/%d", i); subscribe(mVal.data(), QOS_2); - snprintf(mVal.data(), mVal.size(), "ctrl/restart/%d", i); + snprintf(mVal.data(), mVal.size() - 1, "ctrl/restart/%d", i); subscribe(mVal.data()); - snprintf(mVal.data(), mVal.size(), "ctrl/power/%d", i); + snprintf(mVal.data(), mVal.size() - 1, "ctrl/power/%d", i); subscribe(mVal.data()); } subscribe(subscr[MQTT_SUBS_SET_TIME]); @@ -400,20 +400,20 @@ class PubMqtt { const char *devCls, *stateCls; if (!total) { if (rec->assign[mDiscovery.sub].ch == CH0) - snprintf(name.data(), name.size(), "%s", iv->getFieldName(mDiscovery.sub, rec)); + snprintf(name.data(), name.size() - 1, "%s", iv->getFieldName(mDiscovery.sub, rec)); else - snprintf(name.data(), name.size(), "CH%d_%s", rec->assign[mDiscovery.sub].ch, iv->getFieldName(mDiscovery.sub, rec)); - snprintf(topic.data(), name.size(), "/ch%d/%s", rec->assign[mDiscovery.sub].ch, iv->getFieldName(mDiscovery.sub, rec)); - snprintf(uniq_id.data(), uniq_id.size(), "ch%d_%s", rec->assign[mDiscovery.sub].ch, iv->getFieldName(mDiscovery.sub, rec)); + snprintf(name.data(), name.size() - 1, "CH%d_%s", rec->assign[mDiscovery.sub].ch, iv->getFieldName(mDiscovery.sub, rec)); + snprintf(topic.data(), name.size() - 1, "/ch%d/%s", rec->assign[mDiscovery.sub].ch, iv->getFieldName(mDiscovery.sub, rec)); + snprintf(uniq_id.data(), uniq_id.size() - 1, "ch%d_%s", rec->assign[mDiscovery.sub].ch, iv->getFieldName(mDiscovery.sub, rec)); devCls = getFieldDeviceClass(rec->assign[mDiscovery.sub].fieldId); stateCls = getFieldStateClass(rec->assign[mDiscovery.sub].fieldId); } else { // total values - snprintf(name.data(), name.size(), "Total %s", fields[fldTotal[mDiscovery.sub]]); - snprintf(topic.data(), topic.size(), "/%s", fields[fldTotal[mDiscovery.sub]]); - snprintf(uniq_id.data(), uniq_id.size(), "total_%s", fields[fldTotal[mDiscovery.sub]]); + snprintf(name.data(), name.size() - 1, "Total %s", fields[fldTotal[mDiscovery.sub]]); + snprintf(topic.data(), topic.size() - 1, "/%s", fields[fldTotal[mDiscovery.sub]]); + snprintf(uniq_id.data(), uniq_id.size() - 1, "total_%s", fields[fldTotal[mDiscovery.sub]]); devCls = getFieldDeviceClass(fldTotal[mDiscovery.sub]); stateCls = getFieldStateClass(fldTotal[mDiscovery.sub]); } @@ -433,9 +433,9 @@ class PubMqtt { doc2[F("stat_cla")] = String(stateCls); if (!total) - snprintf(topic.data(), topic.size(), "%s/sensor/%s/ch%d_%s/config", MQTT_DISCOVERY_PREFIX, iv->config->name, rec->assign[mDiscovery.sub].ch, iv->getFieldName(mDiscovery.sub, rec)); + snprintf(topic.data(), topic.size() - 1, "%s/sensor/%s/ch%d_%s/config", MQTT_DISCOVERY_PREFIX, iv->config->name, rec->assign[mDiscovery.sub].ch, iv->getFieldName(mDiscovery.sub, rec)); else // total values - snprintf(topic.data(), topic.size(), "%s/sensor/%s/total_%s/config", MQTT_DISCOVERY_PREFIX, node_id.c_str(), fields[fldTotal[mDiscovery.sub]]); + snprintf(topic.data(), topic.size() - 1, "%s/sensor/%s/total_%s/config", MQTT_DISCOVERY_PREFIX, node_id.c_str(), fields[fldTotal[mDiscovery.sub]]); size_t size = measureJson(doc2) + 1; buf.fill(0); serializeJson(doc2, buf.data(), size); @@ -509,14 +509,14 @@ class PubMqtt { mLastIvState[id] = status; changed = true; - snprintf(mSubTopic.data(), mSubTopic.size(), "%s/available", iv->config->name); - snprintf(mVal.data(), mVal.size(), "%d", (uint8_t)status); + snprintf(mSubTopic.data(), mSubTopic.size() - 1, "%s/available", iv->config->name); + snprintf(mVal.data(), mVal.size() - 1, "%d", (uint8_t)status); publish(mSubTopic.data(), mVal.data(), true); } } if(changed) { - snprintf(mVal.data(), mVal.size(), "%d", ((allAvail) ? MQTT_STATUS_ONLINE : ((anyAvail) ? MQTT_STATUS_PARTIAL : MQTT_STATUS_OFFLINE))); + snprintf(mVal.data(), mVal.size() - 1, "%d", ((allAvail) ? MQTT_STATUS_ONLINE : ((anyAvail) ? MQTT_STATUS_PARTIAL : MQTT_STATUS_OFFLINE))); publish("status", mVal.data(), true); } @@ -539,14 +539,14 @@ class PubMqtt { mSendAlarm[i] = false; - snprintf(mSubTopic.data(), mSubTopic.size(), "%s/alarm/cnt", iv->config->name); - snprintf(mVal.data(), mVal.size(), "%d", iv->alarmCnt); + snprintf(mSubTopic.data(), mSubTopic.size() - 1, "%s/alarm/cnt", iv->config->name); + snprintf(mVal.data(), mVal.size() - 1, "%d", iv->alarmCnt); publish(mSubTopic.data(), mVal.data(), false); for(uint8_t j = 0; j < 10; j++) { if(0 != iv->lastAlarm[j].code) { - snprintf(mSubTopic.data(), mSubTopic.size(), "%s/alarm/%d", iv->config->name, j); - snprintf(mVal.data(), mVal.size(), "{\"code\":%d,\"str\":\"%s\",\"start\":%d,\"end\":%d}", + snprintf(mSubTopic.data(), mSubTopic.size() - 1, "%s/alarm/%d", iv->config->name, j); + snprintf(mVal.data(), mVal.size() - 1, "{\"code\":%d,\"str\":\"%s\",\"start\":%d,\"end\":%d}", iv->lastAlarm[j].code, iv->getAlarmStr(iv->lastAlarm[j].code).c_str(), iv->lastAlarm[j].start + lastMidnight, @@ -581,8 +581,8 @@ class PubMqtt { } } - snprintf(mSubTopic.data(), mSubTopic.size(), "%s/ch%d/%s", iv->config->name, rec->assign[i].ch, fields[rec->assign[i].fieldId]); - snprintf(mVal.data(), mVal.size(), "%g", ah::round3(iv->getValue(i, rec))); + snprintf(mSubTopic.data(), mSubTopic.size() - 1, "%s/ch%d/%s", iv->config->name, rec->assign[i].ch, fields[rec->assign[i].fieldId]); + snprintf(mVal.data(), mVal.size() - 1, "%g", ah::round3(iv->getValue(i, rec))); publish(mSubTopic.data(), mVal.data(), retained); yield(); diff --git a/src/web/RestApi.h b/src/web/RestApi.h index acd085f3..ce580dd5 100644 --- a/src/web/RestApi.h +++ b/src/web/RestApi.h @@ -500,7 +500,7 @@ class RestApi { obj[F("name")] = String(iv->config->name); obj[F("serial")] = String(iv->config->serial.u64, HEX); obj[F("version")] = String(iv->getFwVersion()); - obj[F("power_limit_read")] = ah::round3(iv->actPowerLimit); + obj[F("power_limit_read")] = iv->actPowerLimit; obj[F("power_limit_ack")] = iv->powerLimitAck; obj[F("max_pwr")] = iv->getMaxPower(); obj[F("ts_last_success")] = rec->ts; diff --git a/src/web/web.h b/src/web/web.h index a166377f..c40c817c 100644 --- a/src/web/web.h +++ b/src/web/web.h @@ -656,7 +656,7 @@ class Web { { "is_enabled", "gauge", metricConstInverterFormat, [](Inverter<> *iv)-> uint64_t {return iv->config->enabled;} }, { "is_available", "gauge", metricConstInverterFormat, [](Inverter<> *iv)-> uint64_t {return iv->isAvailable();} }, { "is_producing", "gauge", metricConstInverterFormat, [](Inverter<> *iv)-> uint64_t {return iv->isProducing();} }, - { "power_limit_read", "gauge", metricConstInverterFormat, [](Inverter<> *iv)-> uint64_t {return (int64_t)ah::round3(iv->actPowerLimit);} }, + { "power_limit_read", "gauge", metricConstInverterFormat, [](Inverter<> *iv)-> uint64_t {return iv->actPowerLimit;} }, { "power_limit_ack", "gauge", metricConstInverterFormat, [](Inverter<> *iv)-> uint64_t {return (iv->powerLimitAck)?1:0;} }, { "max_power", "gauge", metricConstInverterFormat, [](Inverter<> *iv)-> uint64_t {return iv->getMaxPower();} }, { "radio_rx_success", "counter" ,metricConstInverterFormat, [](Inverter<> *iv)-> uint64_t {return iv->radioStatistics.rxSuccess;} }, @@ -786,14 +786,14 @@ class Web { char total[7]; if (metricDeclared) { // A declaration and value for channels have been delivered. So declare and deliver a _total metric - strncpy(total, "_total", 6); + snprintf(total, sizeof(total)-1, "_total"); } if (!metricTotalDeclard) { - snprintf(type, sizeof(type), "# TYPE %s%s%s%s %s\n",metricConstPrefix, iv->getFieldName(metricsChannelId, rec), promUnit.c_str(), total, promType.c_str()); + snprintf(type, sizeof(type)-1, "# TYPE %s%s%s%s %s\n",metricConstPrefix, iv->getFieldName(metricsChannelId, rec), promUnit.c_str(), total, promType.c_str()); metrics += type; metricTotalDeclard = true; } - snprintf(topic, sizeof(topic), "%s%s%s%s{inverter=\"%s\"}",metricConstPrefix, iv->getFieldName(metricsChannelId, rec), promUnit.c_str(), total,iv->config->name); + snprintf(topic, sizeof(topic)-1, "%s%s%s%s{inverter=\"%s\"}",metricConstPrefix, iv->getFieldName(metricsChannelId, rec), promUnit.c_str(), total,iv->config->name); } else { // Report (non zero) channel value // Use a fallback channel name (ch0, ch1, ...)if non is given by user @@ -801,11 +801,11 @@ class Web { if (iv->config->chName[channel-1][0] != 0) { strncpy(chName, iv->config->chName[channel-1], sizeof(chName)); } else { - snprintf(chName,sizeof(chName),"ch%1d",channel); + snprintf(chName,sizeof(chName)-1,"ch%1d",channel); } - snprintf(topic, sizeof(topic), "%s%s%s{inverter=\"%s\",channel=\"%s\"}",metricConstPrefix, iv->getFieldName(metricsChannelId, rec), promUnit.c_str(), iv->config->name,chName); + snprintf(topic, sizeof(topic)-1, "%s%s%s{inverter=\"%s\",channel=\"%s\"}",metricConstPrefix, iv->getFieldName(metricsChannelId, rec), promUnit.c_str(), iv->config->name,chName); } - snprintf(val, sizeof(val), " %.3f\n", iv->getValue(metricsChannelId, rec)); + snprintf(val, sizeof(val)-1, " %.3f\n", iv->getValue(metricsChannelId, rec)); metrics += topic; metrics += val; } @@ -835,7 +835,7 @@ class Web { case metricsStateAlarmData: // Alarm Info loop : fit to one packet // Perform grouping on metrics according to Prometheus exposition format specification - snprintf(type, sizeof(type),"# TYPE %s%s gauge\n",metricConstPrefix,fields[FLD_LAST_ALARM_CODE]); + snprintf(type, sizeof(type)-1,"# TYPE %s%s gauge\n",metricConstPrefix,fields[FLD_LAST_ALARM_CODE]); metrics = type; for (metricsInverterId = 0; metricsInverterId < mSys->getNumInverters();metricsInverterId++) { @@ -847,8 +847,8 @@ class Web { alarmChannelId = 0; if (alarmChannelId < rec->length) { std::tie(promUnit, promType) = convertToPromUnits(iv->getUnit(alarmChannelId, rec)); - snprintf(topic, sizeof(topic), "%s%s%s{inverter=\"%s\"}",metricConstPrefix, iv->getFieldName(alarmChannelId, rec), promUnit.c_str(), iv->config->name); - snprintf(val, sizeof(val), " %.3f\n", iv->getValue(alarmChannelId, rec)); + snprintf(topic, sizeof(topic)-1, "%s%s%s{inverter=\"%s\"}",metricConstPrefix, iv->getFieldName(alarmChannelId, rec), promUnit.c_str(), iv->config->name); + snprintf(val, sizeof(val)-1, " %.3f\n", iv->getValue(alarmChannelId, rec)); metrics += topic; metrics += val; }