diff --git a/NEWTAGS.md b/NEWTAGS.md index 86b2516..6540580 100644 --- a/NEWTAGS.md +++ b/NEWTAGS.md @@ -27,6 +27,11 @@ e3dc/raw/TAG_BAT_DATA/TAG_BAT_RSOC The tag names are used to build the topic names. Raw data is only published via MQTT. +To limit the amount of raw data, a configuration entry can be created with a regular expression that describes the topics to be published: +``` +RAW_TOPIC_REGEX=raw/(TAG_DCDC_DATA|TAG_BAT_DATA)/.* +``` + ### Configuration of Tags All tags that are listed in RscpTags.h and have "REQ" in their name can be used. @@ -154,4 +159,14 @@ ADD_NEW_REQUEST=TAG_DCDC_REQ_DATA:TAG_DCDC_REQ_P_DCL-1 ADD_NEW_REQUEST=TAG_DCDC_REQ_DATA:TAG_DCDC_REQ_ON_GRID-1 ADD_NEW_REQUEST=TAG_DCDC_REQ_DATA:TAG_DCDC_REQ_DERATING-1 ADD_NEW_REQUEST=TAG_DCDC_REQ_DATA:TAG_DCDC_REQ_DERATING_VALUE-1 + +# Wallbox & Emergency Power +ADD_NEW_REQUEST=0:TAG_EMS_REQ_GET_EP_WALLBOX_ALLOW-0 +ADD_NEW_REQUEST=0:TAG_EMS_REQ_GET_MAX_EP_WALLBOX_POWER_W-0 +ADD_NEW_REQUEST=0:TAG_EMS_REQ_GET_MIN_EP_WALLBOX_POWER_W-0 +ADD_NEW_REQUEST=0:TAG_EMS_REQ_GET_EP_WALLBOX_ENERGY-0 +ADD_NEW_REQUEST=0:TAG_EMS_REQ_EP_DELAY-0 + +# System Specifications +ADD_NEW_REQUEST_AT_START=0:TAG_EMS_REQ_GET_SYS_SPECS-0 ``` diff --git a/README.md b/README.md index 578b567..69ec6ca 100644 --- a/README.md +++ b/README.md @@ -155,7 +155,7 @@ or to show the help page If everything works properly, you will see something like this: ``` -rscp2mqtt [3.26] +rscp2mqtt [3.27] E3DC system >192.168.178.111:5033< user: >your E3DC user< MQTT broker >localhost:1883< qos = >0< retain = >✗< tls >✗< client id >✗< prefix >e3dc< Requesting PVI ✓ | PM (0) | DCB ✓ (1 battery string) | Wallbox ✗ | Interval 2 | Autorefresh ✓ | Raw data ✗ | Logging OFF diff --git a/RELEASE.md b/RELEASE.md index f3de921..53a59ff 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,5 +1,10 @@ ## Release Notes +### Release v3.27 (24.07.2024) + +Bug fixes: +- Issue #81: Raw data mode: Possible segmentation fault due to a memory leak + ### Release v3.26 (14.07.2024) Features: diff --git a/RscpMqttConfig.h b/RscpMqttConfig.h index edc41be..0c39e8c 100644 --- a/RscpMqttConfig.h +++ b/RscpMqttConfig.h @@ -98,6 +98,7 @@ typedef struct _config_t { char true_value[5]; char false_value[6]; bool raw_mode; + char *raw_topic_regex; } config_t; #endif diff --git a/RscpMqttMain.cpp b/RscpMqttMain.cpp index f074b11..ef43be3 100644 --- a/RscpMqttMain.cpp +++ b/RscpMqttMain.cpp @@ -21,7 +21,7 @@ #include #include -#define RSCP2MQTT_VERSION "3.26" +#define RSCP2MQTT_VERSION "3.27" #define AES_KEY_SIZE 32 #define AES_BLOCK_SIZE 32 @@ -505,7 +505,7 @@ void publishImmediately(char *t, char *p, bool influx) { char topic[TOPIC_SIZE]; snprintf(topic, TOPIC_SIZE, "%s/%s", cfg.prefix, t); - if (mosq) mosquitto_publish(mosq, NULL, topic, strlen(p), p, cfg.mqtt_qos, cfg.mqtt_retain); + if (mosq && (mosquitto_pub_topic_check(topic) == MOSQ_ERR_SUCCESS) && p && strlen(p)) mosquitto_publish(mosq, NULL, topic, strlen(p), p, cfg.mqtt_qos, cfg.mqtt_retain); #ifdef INFLUXDB if (cfg.influxdb_on && curl && influx) { char buffer[CURL_BUFFER_SIZE]; @@ -867,17 +867,22 @@ void pushAdditionalTag(uint32_t req_container, uint32_t req_tag, int req_index, } bool updateRawData(char *topic, char *payload) { - for (std::vector::iterator it = RSCP_MQTT::rawData.begin(); it != RSCP_MQTT::rawData.end(); ++it) { - if (!strcmp(it->topic, topic)) { - if (strcmp(it->payload, payload)) strcpy(it->payload, payload); - return(true); + if (topic && payload) { + for (std::vector::iterator it = RSCP_MQTT::rawData.begin(); it != RSCP_MQTT::rawData.end(); ++it) { + if (!strcmp(it->topic, topic)) { + if (strcmp(it->payload, payload)) { + if (strlen(it->payload) != strlen(payload)) it->payload = (char *)realloc(it->payload, strlen(payload) + 1); + strcpy(it->payload, payload); + } + return(true); + } } } return(false); } void mergeRawData(char *topic, char *payload) { - if (!updateRawData(topic, payload)) { + if (topic && payload && !updateRawData(topic, payload)) { RSCP_MQTT::mqtt_data_t v; v.topic = strdup(topic); v.payload = strdup(payload); @@ -2094,29 +2099,31 @@ void createRequest(SRscpFrameBuffer * frameBuffer) { } void publishRaw(RscpProtocol *protocol, SRscpValue *response, char *topic) { - char *payload_new = (char *)malloc(PAYLOAD_SIZE * sizeof(char)); + char *payload_new = (char *)malloc(PAYLOAD_SIZE * sizeof(char) + 1); char *payload_old = readRawData(topic); + memset(payload_new, 0, sizeof(payload_new)); preparePayload(protocol, response, &payload_new); - if (payload_old && strcmp(payload_old, payload_new)) { + if (payload_old && payload_new && strcmp(payload_new, "") && strcmp(payload_old, payload_new)) { publishImmediately(topic, payload_new, false); updateRawData(topic, payload_new); - } else if (!payload_old) { + } else if (!payload_old && payload_new && strcmp(payload_new, "")) { publishImmediately(topic, payload_new, false); mergeRawData(topic, payload_new); } if (payload_new) free(payload_new); - return; -} + return; +} void handleRaw(RscpProtocol *protocol, SRscpValue *response, uint32_t *cache, int level) { int l = level + 1; char topic[TOPIC_SIZE]; + memset(topic, 0, sizeof(topic)); if (response->dataType == RSCP::eTypeError) return; if (!l && (response->dataType != RSCP::eTypeContainer)) { sprintf(topic, "raw/%s", tagName(RSCP_TAGS::RscpTagsOverview, response->tag)); - publishRaw(protocol, response, topic); + if (!cfg.raw_topic_regex || std::regex_match(topic, std::regex(cfg.raw_topic_regex))) publishRaw(protocol, response, topic); return; } std::vector data = protocol->getValueAsContainer(response); @@ -2131,11 +2138,13 @@ void handleRaw(RscpProtocol *protocol, SRscpValue *response, uint32_t *cache, in strcpy(topic, "raw"); for (int i = 0; i < RECURSION_MAX_LEVEL; i++) { if (cache[i]) { - strcat(topic, "/"); - strcat(topic, tagName(RSCP_TAGS::RscpTagsOverview, cache[i])); + if (strlen(topic) + strlen(tagName(RSCP_TAGS::RscpTagsOverview, cache[i])) + 2 < TOPIC_SIZE) { + strcat(topic, "/"); + strcat(topic, tagName(RSCP_TAGS::RscpTagsOverview, cache[i])); + } else logMessageByTag(response->tag, data[i].tag, 0, __LINE__, (char *)"Error: Topic name too long. Container >%s< Tag >%s< [%d]\n"); } } - publishRaw(protocol, &data[i], topic); + if (!cfg.raw_topic_regex || std::regex_match(topic, std::regex(cfg.raw_topic_regex))) publishRaw(protocol, &data[i], topic); } } } @@ -2185,8 +2194,8 @@ int handleResponseValue(RscpProtocol *protocol, SRscpValue *response) { if (cfg.raw_mode) { for (int i = 0; i < RECURSION_MAX_LEVEL; i++) cache[i] = 0; - handleRaw(protocol, response, cache, -1); - } + if (mosq) handleRaw(protocol, response, cache, -1); + } // check the SRscpValue TAG to detect which response it is switch (response->tag) { @@ -2740,7 +2749,7 @@ static void mainLoop(void) { if (mosq) { char topic[TOPIC_SIZE]; snprintf(topic, TOPIC_SIZE, "%s/rscp2mqtt/status", cfg.prefix); - mosquitto_threaded_set(mosq, true); + // mosquitto_threaded_set(mosq, true); // necessary? if (cfg.mqtt_tls && cfg.mqtt_tls_password) { if (mosquitto_tls_set(mosq, cfg.mqtt_tls_cafile, cfg.mqtt_tls_capath, cfg.mqtt_tls_certfile, cfg.mqtt_tls_keyfile, mqttCallbackTlsPassword) != MOSQ_ERR_SUCCESS) { logMessage(cfg.logfile, (char *)__FILE__, __LINE__, (char *)"Error: Unable to set TLS options.\n"); @@ -2755,8 +2764,10 @@ static void mainLoop(void) { if (cfg.mqtt_auth && strcmp(cfg.mqtt_user, "") && strcmp(cfg.mqtt_password, "")) mosquitto_username_pw_set(mosq, cfg.mqtt_user, cfg.mqtt_password); mosquitto_will_set(mosq, topic, strlen("disconnected"), "disconnected", cfg.mqtt_qos, cfg.mqtt_retain); if (!mosquitto_connect(mosq, cfg.mqtt_host, cfg.mqtt_port, 10)) { - std::thread th(mqttListener, mosq); - th.detach(); + if (!cfg.once) { + std::thread th(mqttListener, mosq); + th.detach(); + } if (cfg.verbose) logMessage(cfg.logfile, (char *)__FILE__, __LINE__, (char *)"Success: MQTT broker connected.\n"); } else { logMessage(cfg.logfile, (char *)__FILE__, __LINE__, (char *)"Error: MQTT broker connection failed.\n"); @@ -2935,6 +2946,7 @@ int main(int argc, char *argv[]) { strcpy(cfg.true_value, "true"); strcpy(cfg.false_value, "false"); cfg.raw_mode = false; + cfg.raw_topic_regex = NULL; // signal handler signal(SIGINT, signal_handler); @@ -3109,6 +3121,8 @@ int main(int argc, char *argv[]) { #endif else if ((strcasecmp(key, "RAW_MODE") == 0) && (strcasecmp(value, "true") == 0)) cfg.raw_mode = true; + else if (strcasecmp(key, "RAW_TOPIC_REGEX") == 0) + cfg.raw_topic_regex = strdup(value); else if (strncasecmp(key, "ADD_NEW_REQUEST", strlen("ADD_NEW_REQUEST")) == 0) { int order = 0; int index = -1; @@ -3467,6 +3481,10 @@ int main(int argc, char *argv[]) { RSCP_MQTT::IdlePeriodCache.clear(); RSCP_MQTT::ErrorCache.clear(); + // MQTT disconnect + mosquitto_disconnect(mosq); + mosq = NULL; + // MQTT cleanup mosquitto_lib_cleanup(); @@ -3486,6 +3504,7 @@ int main(int argc, char *argv[]) { if (cfg.mqtt_tls_certfile) free(cfg.mqtt_tls_certfile); if (cfg.mqtt_tls_keyfile) free(cfg.mqtt_tls_keyfile); if (cfg.mqtt_tls_password) free(cfg.mqtt_tls_password); + if (cfg.raw_topic_regex) free(cfg.raw_topic_regex); exit(EXIT_SUCCESS); } diff --git a/WALLBOX.md b/WALLBOX.md index f21566c..7dc71c1 100644 --- a/WALLBOX.md +++ b/WALLBOX.md @@ -58,45 +58,68 @@ The following topics are sent to the MQTT broker: | Wallbox Power L3 * | e3dc/wallbox/power/L3 | [W] | | Wallbox SOC * | e3dc/wallbox/soc | [%] | -*) If more than one wallbox exists, topics are extended by the number of the wallbox (1..8), e.g. e3dc/wallbox/1/charging, e3dc/wallbox/2/charging, ... +*) If more than one wallbox exists (maximum 8), topics are extended by the number of the wallbox (1..8), e.g. e3dc/wallbox/1/charging, e3dc/wallbox/2/charging, ... e3dc/wallbox/8/charging + +**) The value is required to be able to calculate the daily value. ### Wallbox Control -In addition, these topics can be published to control the wallboxes: +In addition, the following topics can be published to control the wallboxes: Sun Mode (true/1/false/0) ``` +# if one wallbox is available mosquitto_pub -h localhost -p 1883 -t "e3dc/set/wallbox/sun_mode" -m true -# or +# +# or if more than one wallbox is available mosquitto_pub -h localhost -p 1883 -t "e3dc/set/wallbox/1/sun_mode" -m true # for the first wallbox +mosquitto_pub -h localhost -p 1883 -t "e3dc/set/wallbox/2/sun_mode" -m true # for the second wallbox +# ... +mosquitto_pub -h localhost -p 1883 -t "e3dc/set/wallbox/8/sun_mode" -m true # for the eighth wallbox ``` Suspend charging (true/1/false/0) ``` mosquitto_pub -h localhost -p 1883 -t "e3dc/set/wallbox/suspended" -m true -# or +# +# or if more than one wallbox is available mosquitto_pub -h localhost -p 1883 -t "e3dc/set/wallbox/1/suspended" -m true # for the first wallbox +mosquitto_pub -h localhost -p 1883 -t "e3dc/set/wallbox/2/suspended" -m true # for the second wallbox +# ... +mosquitto_pub -h localhost -p 1883 -t "e3dc/set/wallbox/8/suspended" -m true # for the eighth wallbox ``` Toggle suspend charging ``` -mosquitto_pub -h localhost -p 1883 -t"e3dc/set/wallbox/toggle" -m 1 -# or +mosquitto_pub -h localhost -p 1883 -t "e3dc/set/wallbox/toggle" -m 1 +# +# or if more than one wallbox is available mosquitto_pub -h localhost -p 1883 -t "e3dc/set/wallbox/1/toggle" -m 1 # for the first wallbox +mosquitto_pub -h localhost -p 1883 -t "e3dc/set/wallbox/2/toggle" -m 1 # for the second wallbox +# ... +mosquitto_pub -h localhost -p 1883 -t "e3dc/set/wallbox/8/toggle" -m 1 # for the eighth wallbox ``` Set max current (1..32 A) ``` mosquitto_pub -h localhost -p 1883 -t"e3dc/set/wallbox/max_current" -m 16 -# or +# +# or if more than one wallbox is available mosquitto_pub -h localhost -p 1883 -t "e3dc/set/wallbox/1/max_current" -m 16 # for the first wallbox +mosquitto_pub -h localhost -p 1883 -t "e3dc/set/wallbox/2/max_current" -m 16 # for the second wallbox +# ... +mosquitto_pub -h localhost -p 1883 -t "e3dc/set/wallbox/8/max_current" -m 16 # for the eighth wallbox ``` Set number of phases (1/3) ``` mosquitto_pub -h localhost -p 1883 -t "e3dc/set/wallbox/number_phases" -m 1 -# or +# +# or if more than one wallbox is available mosquitto_pub -h localhost -p 1883 -t "e3dc/set/wallbox/1/number_phases" -m 1 # for the first wallbox +mosquitto_pub -h localhost -p 1883 -t "e3dc/set/wallbox/2/number_phases" -m 1 # for the second wallbox +# ... +mosquitto_pub -h localhost -p 1883 -t "e3dc/set/wallbox/8/number_phases" -m 1 # for the eighth wallbox ``` The following settings apply to all available wallboxes: diff --git a/config.template b/config.template index 18d3efe..5d27567 100644 --- a/config.template +++ b/config.template @@ -83,6 +83,8 @@ DCB_REQUESTS=true #-Raw Data Mode RAW_MODE=false +#-- filter topics to be published (optional, only one entry possible!) +RAW_TOPIC_REGEX=raw/TAG_DCDC_DATA/.* #-Additional Tags and Topics #--please look at NEWTAGS.md