From 40c4fdd2d1f2a9ad76efde0d8e4607d77ab9b1df Mon Sep 17 00:00:00 2001 From: proddy Date: Thu, 24 Sep 2026 11:28:14 +0200 Subject: [PATCH] move hardcoded consts to header files --- project-words.txt | 1 - src/ESP32React/UploadFileService.cpp | 2 +- src/core/analogsensor.h | 2 ++ src/core/emsdevice.cpp | 4 +-- src/core/emsdevice.h | 3 ++ src/core/emsesp.cpp | 4 +-- src/core/emsesp.h | 2 ++ src/core/httpClient.cpp | 6 ++-- src/core/httpClient.h | 8 +++++ src/core/mqtt.cpp | 2 +- src/core/mqtt.h | 5 +-- src/core/system.cpp | 46 ++++++++++++---------------- src/core/system.h | 15 +++++++++ src/core/telegram.cpp | 2 +- src/core/telegram.h | 3 ++ src/web/WebLogService.cpp | 4 +-- src/web/WebLogService.h | 5 +++ 17 files changed, 72 insertions(+), 42 deletions(-) diff --git a/project-words.txt b/project-words.txt index 0e8f7a52e..1a2a9624e 100644 --- a/project-words.txt +++ b/project-words.txt @@ -786,7 +786,6 @@ negcond negheat nein nennvolumentstrom -neopixel netcmp netflowtemp netif diff --git a/src/ESP32React/UploadFileService.cpp b/src/ESP32React/UploadFileService.cpp index a10266192..e9fa36ed4 100644 --- a/src/ESP32React/UploadFileService.cpp +++ b/src/ESP32React/UploadFileService.cpp @@ -71,7 +71,7 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri _is_filesystem = true; _md5[0] = '\0'; // clear any stale md5 so Update.end() doesn't compare against it _md5_applied = false; - } else if ((extension == "bin") && (filesize > 1000000)) { + } else if ((extension == "bin") && (filesize >= emsesp::System::MIN_FIRMWARE_SIZE)) { _is_firmware = true; } else if (extension == "json") { _md5[0] = '\0'; // clear md5 diff --git a/src/core/analogsensor.h b/src/core/analogsensor.h index e44e48b7d..b0020e7b9 100644 --- a/src/core/analogsensor.h +++ b/src/core/analogsensor.h @@ -189,8 +189,10 @@ class AnalogSensor { static constexpr double T25 = 298.15; static constexpr double R0 = 100000; static constexpr double Rt = 60000; + static constexpr uint32_t NTC_VREF_MV = 3300; // NTC voltage divider supply (in mV) - 3.3 V static constexpr uint8_t MAX_SENSORS = 20; static constexpr uint32_t MEASURE_ANALOG_INTERVAL = 500; + static constexpr uint32_t FREQ_ZERO_TIMEOUT_US = 10000000; // report 0 Hz if no edge arrives for this long (in us) - 10 seconds static uuid::log::Logger logger_; void remove_ha_topic(const int8_t type, const uint8_t id) const; diff --git a/src/core/emsdevice.cpp b/src/core/emsdevice.cpp index 43d9a4ed3..dc4ecdd2c 100644 --- a/src/core/emsdevice.cpp +++ b/src/core/emsdevice.cpp @@ -2281,8 +2281,8 @@ bool EMSdevice::handle_telegram(const std::shared_ptr & telegram EMSESP::logger().debug("This telegram (%s) is not recognized by the EMS bus", tf.telegram_type_name_); #endif // removing fetch after start causes issue: https://github.com/emsesp/EMS-ESP32/issues/1420 - // continue retry the first 5 minutes, then disable (added 15.3.2024) - if (uuid::get_uptime_sec() > 600) { + // continue retrying for a while after boot, then disable (added 15.3.2024) + if (uuid::get_uptime_sec() > FETCH_RETRY_DURATION_SEC) { tf.fetch_ = false; } return false; diff --git a/src/core/emsdevice.h b/src/core/emsdevice.h index fa21a8744..8b8c640fe 100644 --- a/src/core/emsdevice.h +++ b/src/core/emsdevice.h @@ -441,6 +441,9 @@ class EMSdevice { static constexpr uint8_t EMS_DEVICES_MAX_TELEGRAMS = 20; + // keep fetching telegrams the bus master doesn't recognise for this long after boot, then give up (in seconds) - 10 minutes + static constexpr uint32_t FETCH_RETRY_DURATION_SEC = 600; + // static device IDs static constexpr uint8_t EMS_DEVICE_ID_BOILER = 0x08; // fixed device_id for Master Boiler/UBA static constexpr uint8_t EMS_DEVICE_ID_HS1 = 0x70; // fixed device_id for 1st. Cascade Boiler/UBA diff --git a/src/core/emsesp.cpp b/src/core/emsesp.cpp index e0d5eb3d5..c4767f9ac 100644 --- a/src/core/emsesp.cpp +++ b/src/core/emsesp.cpp @@ -1978,12 +1978,12 @@ void EMSESP::loop() { } } - // reset status after 5 Minutes + // reset status after a timeout if (EMSESP::system_.systemStatus() != SYSTEM_STATUS::SYSTEM_STATUS_NORMAL) { static uint32_t starttime = 0; if (starttime == 0) { starttime = uuid::get_uptime_ms(); - } else if (uuid::get_uptime_ms() - starttime > 300000) { + } else if (uuid::get_uptime_ms() - starttime > SYSTEM_STATUS_RESET_TIMEOUT) { starttime = 0; EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_NORMAL); } diff --git a/src/core/emsesp.h b/src/core/emsesp.h index 3111395ff..06382ba85 100644 --- a/src/core/emsesp.h +++ b/src/core/emsesp.h @@ -291,6 +291,8 @@ class EMSESP { static constexpr uint32_t EMS_FETCH_FREQUENCY = 60000; // check every minute static constexpr uint8_t EMS_WAIT_KM_TIMEOUT = 60; // wait one minute + static constexpr uint32_t SYSTEM_STATUS_RESET_TIMEOUT = 300000; // return a non-normal system status to normal after this (in ms) - 5 minutes + struct Device_record { uint8_t product_id; EMSdevice::DeviceType device_type; diff --git a/src/core/httpClient.cpp b/src/core/httpClient.cpp index b46fe85ab..29f00eba8 100644 --- a/src/core/httpClient.cpp +++ b/src/core/httpClient.cpp @@ -66,13 +66,13 @@ int HttpClient::request(std::string url, const std::string & method, const std:: ssl_client->setInsecure(); // Notes: with root CA we should set here: ssl_client->setCACert(rootCACert); // 1 KB RX buffer is fine for small JSON-style endpoints used by the scheduler/shunting-yard but it is NOT enough for servers that send full-size TLS records (>1 KB) - ssl_client->setBufferSizes(16384, 1024); - ssl_client->setSessionTimeout(120); // Set the timeout in seconds (>=120 seconds) + ssl_client->setBufferSizes(TLS_RX_BUFFER_SIZE, TLS_TX_BUFFER_SIZE); + ssl_client->setSessionTimeout(TLS_SESSION_TIMEOUT_S); } // WiFiClient is NetworkClient, which declares no setTimeout() of its own - calling it binds to // Stream::setTimeout() and only affects readBytes(), leaving the socket on the core's 3s default basic_client->setConnectionTimeout(CONNECT_TIMEOUT_MS); - ssl_client->setTimeout(5); // seconds, drives BearSSL only - unused on the plain HTTP path + ssl_client->setTimeout(TLS_READ_TIMEOUT_S); // drives BearSSL only - unused on the plain HTTP path ssl_client->setClient(basic_client, is_https); // enableSSL = false for plain HTTP const uint16_t port = is_https ? 443 : 80; diff --git a/src/core/httpClient.h b/src/core/httpClient.h index fe30167ab..9dd5423f9 100644 --- a/src/core/httpClient.h +++ b/src/core/httpClient.h @@ -29,6 +29,14 @@ class HttpClient { public: static int request(std::string url, const std::string & method, const std::string & value, JsonObjectConst headers, std::string & result); + // ESP_SSLClient (BearSSL) settings, shared by every TLS client in the project. + // The RX buffer must hold one full TLS record: servers such as GitHub's CDN send up-to-16 KB + // records and don't negotiate max_fragment_length, so a smaller buffer can't decode the body + static constexpr size_t TLS_RX_BUFFER_SIZE = 16384; + static constexpr size_t TLS_TX_BUFFER_SIZE = 1024; + static constexpr uint32_t TLS_SESSION_TIMEOUT_S = 120; // in seconds, the library minimum + static constexpr uint32_t TLS_READ_TIMEOUT_S = 5; // in seconds, not ms like the socket timeouts + private: static constexpr uint32_t CONNECT_TIMEOUT_MS = 5000; // TCP connect, the core defaults to 3s static constexpr uint32_t FIRST_BYTE_TIMEOUT_MS = 8000; // how long the server may take to start replying diff --git a/src/core/mqtt.cpp b/src/core/mqtt.cpp index c58952318..25223c9d4 100644 --- a/src/core/mqtt.cpp +++ b/src/core/mqtt.cpp @@ -643,7 +643,7 @@ bool Mqtt::queue_message(const uint8_t operation, const std::string & topic, con // check free mem #ifndef EMSESP_STANDALONE // if (ESP.getFreeHeap() < 60 * 1024 || ESP.getMaxAllocHeap() < 40 * 1024) { - if (heap_caps_get_free_size(MALLOC_CAP_8BIT) < 60 * 1024) { // checks free Heap+PSRAM + if (heap_caps_get_free_size(MALLOC_CAP_8BIT) < MQTT_MIN_FREE_HEAP) { // checks free Heap+PSRAM if (operation == Operation::PUBLISH) { mqtt_message_id_++; mqtt_publish_fails_++; diff --git a/src/core/mqtt.h b/src/core/mqtt.h index 81d1aed7f..5e8266030 100644 --- a/src/core/mqtt.h +++ b/src/core/mqtt.h @@ -61,9 +61,10 @@ class Mqtt { enum Operation : uint8_t { PUBLISH, SUBSCRIBE, UNSUBSCRIBE }; enum NestedFormat : uint8_t { NESTED = 1, SINGLE }; - static constexpr uint8_t MQTT_TOPIC_MAX_SIZE = 128; // fixed, not a user setting anymore + static constexpr uint8_t MQTT_TOPIC_MAX_SIZE = 128; // fixed, not a user setting anymore static constexpr uint16_t MQTT_QUEUE_MAX_SIZE = 300; - static constexpr uint32_t MQTT_DISCONNECT_TIMEOUT = 1000; // max ms to flush a graceful disconnect on non-PSRAM boards + static constexpr uint32_t MQTT_DISCONNECT_TIMEOUT = 1000; // max ms to flush a graceful disconnect on non-PSRAM boards + static constexpr uint32_t MQTT_MIN_FREE_HEAP = 60 * 1024; // don't queue any MQTT message below this much free heap+PSRAM (in bytes) - 60 KB static void on_connect(); static void on_disconnect(espMqttClientTypes::DisconnectReason reason); diff --git a/src/core/system.cpp b/src/core/system.cpp index 8ffdede52..a2860bfc3 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -33,6 +33,7 @@ #include "firmwareVersion.h" #include "shuntingYard.h" // for compute() used by the message and sendmail commands +#include "httpClient.h" // for the shared TLS client settings #if defined(EMSESP_TEST) #include "../test/test.h" @@ -218,7 +219,7 @@ bool System::command_sendmail(const char * value, const int8_t) { ESP_SSLClient * ssl_client = nullptr; SMTPClient * smtp = nullptr; - basic_client->setTimeout(5000); + basic_client->setTimeout(SMTP_TIMEOUT_MS); const bool implicit_ssl = (security == EMAIL_SECURITY::SSL); const bool start_tls = (security == EMAIL_SECURITY::STARTTLS); @@ -229,8 +230,8 @@ bool System::command_sendmail(const char * value, const int8_t) { } else { ssl_client = new ESP_SSLClient; ssl_client->setInsecure(); - ssl_client->setBufferSizes(16384, 1024); - ssl_client->setTimeout(5); + ssl_client->setBufferSizes(HttpClient::TLS_RX_BUFFER_SIZE, HttpClient::TLS_TX_BUFFER_SIZE); + ssl_client->setTimeout(HttpClient::TLS_READ_TIMEOUT_S); // enableSSL is baked in at setClient() — true only for implicit SSL (port 465) ssl_client->setClient(basic_client, implicit_ssl); if (start_tls) { @@ -3093,8 +3094,7 @@ void System::ntp_connected(bool b) { // get NTP status bool System::ntp_connected() { - // timeout 2 hours, ntp sync is normally every hour. - if ((uuid::get_uptime_sec() - ntp_last_check_ > 7201) && ntp_connected_) { + if ((uuid::get_uptime_sec() - ntp_last_check_ > NTP_TIMEOUT_SEC) && ntp_connected_) { ntp_connected(false); } @@ -3200,19 +3200,13 @@ bool System::uploadFirmwareURL(const char * url) { if (is_https) { ssl_client.setInsecure(); // no CA validation, matches the rest of the project - // BearSSL needs a receive buffer large enough to hold one full TLS record. - // GitHub's release-assets CDN sends standard up-to-16 KB records and does NOT - // negotiate max_fragment_length, so a small (e.g. 1 KB) RX buffer makes the - // body unreadable (headers still fit one small record, hence Content-Length - // looks fine, but the first body record cannot be decoded). 16384 + overhead - // is the safe value the library itself uses by default; we go a bit smaller - // to be friendlier to 4 MB / no-PSRAM boards while still big enough for any - // record the CDN actually sends in practice. - ssl_client.setBufferSizes(16384, 1024); - ssl_client.setSessionTimeout(120); + // a small (e.g. 1 KB) RX buffer makes the body unreadable: the headers still fit one small + // record, hence Content-Length looks fine, but the first body record cannot be decoded + ssl_client.setBufferSizes(HttpClient::TLS_RX_BUFFER_SIZE, HttpClient::TLS_TX_BUFFER_SIZE); + ssl_client.setSessionTimeout(HttpClient::TLS_SESSION_TIMEOUT_S); } - basic_client.setTimeout(15000); // socket-level read timeout - ssl_client.setTimeout(15); // Stream::readBytes timeout used by Update + basic_client.setTimeout(FIRMWARE_UPLOAD_READ_TIMEOUT_S * 1000); // socket-level read timeout, in ms + ssl_client.setTimeout(FIRMWARE_UPLOAD_READ_TIMEOUT_S); // Stream::readBytes timeout used by Update ssl_client.setClient(&basic_client, is_https); // enableSSL = false for plain HTTP const uint16_t port = is_https ? 443 : 80; @@ -3248,9 +3242,9 @@ bool System::uploadFirmwareURL(const char * url) { ssl_client.println("Connection: close"); ssl_client.print("\r\n"); - // wait for the first byte (up to 8s, matching the previous HTTP timeout) + // wait for the first byte (matching the previous HTTP timeout) uint32_t ms = millis(); - while (ssl_client.connected() && !ssl_client.available() && millis() - ms < 8000) { + while (ssl_client.connected() && !ssl_client.available() && millis() - ms < FIRMWARE_UPLOAD_RESPONSE_TIMEOUT) { delay(1); } @@ -3326,7 +3320,7 @@ bool System::uploadFirmwareURL(const char * url) { // wait for the first byte of the body so the read loop sees real data // (headers and body may arrive in separate TLS records) uint32_t body_wait = millis(); - while (ssl_client.connected() && !ssl_client.available() && millis() - body_wait < 8000) { + while (ssl_client.connected() && !ssl_client.available() && millis() - body_wait < FIRMWARE_UPLOAD_RESPONSE_TIMEOUT) { delay(1); } if (!ssl_client.available()) { @@ -3341,7 +3335,7 @@ bool System::uploadFirmwareURL(const char * url) { } // check we have a valid size - if (firmware_size < 1677721) { // 1.6MB or greater is required + if (firmware_size < (int)MIN_FIRMWARE_SIZE) { LOG_ERROR("Firmware upload failed - invalid size"); return false; // error } @@ -3360,9 +3354,7 @@ bool System::uploadFirmwareURL(const char * url) { EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING); // explicit chunked read loop instead of Update.writeStream(): - constexpr size_t CHUNK_SIZE = 1024; - constexpr uint32_t READ_TIMEOUT_MS = 30000; // overall stall timeout per chunk - uint8_t buf[CHUNK_SIZE]; + uint8_t buf[FIRMWARE_UPLOAD_CHUNK_SIZE]; size_t total_read = 0; bool magic_ok = false; int last_pct = -1; @@ -3386,7 +3378,7 @@ bool System::uploadFirmwareURL(const char * url) { if (!ssl_client.connected()) { break; } - if (millis() - wait_start > READ_TIMEOUT_MS) { + if (millis() - wait_start > FIRMWARE_UPLOAD_STALL_TIMEOUT) { break; } // also bail out promptly if a cancel arrives mid-stall @@ -3407,8 +3399,8 @@ bool System::uploadFirmwareURL(const char * url) { } size_t want = (size_t)firmware_size - total_read; - if (want > CHUNK_SIZE) { - want = CHUNK_SIZE; + if (want > FIRMWARE_UPLOAD_CHUNK_SIZE) { + want = FIRMWARE_UPLOAD_CHUNK_SIZE; } size_t n = stream->readBytes(buf, want); diff --git a/src/core/system.h b/src/core/system.h index 64f2d1254..056275afc 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -370,6 +370,9 @@ class System { static constexpr uint8_t HEALTHCHECK_NO_NETWORK = (1 << 1); // 2 static constexpr uint8_t HEALTHCHECK_RESET = (1 << 7); // 128 + // smallest .bin accepted as a firmware image, for both URL and web uploads - 1.6 MB + static constexpr size_t MIN_FIRMWARE_SIZE = 1677721; + private: static uuid::log::Logger logger_; @@ -402,6 +405,18 @@ class System { static constexpr uint32_t SYSTEM_CHECK_FREQUENCY = 5000; // do a system check every 5 seconds #endif + // NTP syncs about every hour, so allow one missed sync before treating it as lost (in seconds) - 2 hours + static constexpr uint32_t NTP_TIMEOUT_SEC = 7201; + + // email + static constexpr uint32_t SMTP_TIMEOUT_MS = 5000; // socket read timeout (in ms) - 5 seconds + + // firmware upload from URL + static constexpr uint32_t FIRMWARE_UPLOAD_RESPONSE_TIMEOUT = 8000; // max wait for the first byte of the response headers/body (in ms) - 8 seconds + static constexpr uint32_t FIRMWARE_UPLOAD_READ_TIMEOUT_S = 15; // socket and Stream::readBytes timeout used by Update (in seconds) + static constexpr uint32_t FIRMWARE_UPLOAD_STALL_TIMEOUT = 30000; // abort if no data arrives for a chunk (in ms) - 30 seconds + static constexpr size_t FIRMWARE_UPLOAD_CHUNK_SIZE = 1024; // read buffer, on the stack + #ifndef EMSESP_STANDALONE static uuid::syslog::SyslogService syslog_; #endif diff --git a/src/core/telegram.cpp b/src/core/telegram.cpp index 73294d8c9..23dfc0efe 100644 --- a/src/core/telegram.cpp +++ b/src/core/telegram.cpp @@ -286,7 +286,7 @@ void TxService::send_poll() const { uint8_t TxService::get_send_id() { static uint32_t count = 0; if (!tx_telegrams_.empty() && tx_telegrams_.front().telegram_->src != ems_bus_id()) { - if (++count > 500) { // after 500 polls (~3-10 sec) there will be no master poll for this id + if (++count > MAX_POLLS_WITHOUT_MASTER) { tx_telegrams_.pop_front(); count = 0; return tx_telegrams_.empty() ? ems_bus_id() : tx_telegrams_.front().telegram_->src; diff --git a/src/core/telegram.h b/src/core/telegram.h index db8595fc0..7ebe90f07 100644 --- a/src/core/telegram.h +++ b/src/core/telegram.h @@ -440,6 +440,9 @@ class TxService : public EMSbus { static constexpr uint8_t MAXIMUM_TX_RETRIES = 3; static constexpr uint32_t POST_SEND_DELAY = 2000; + // drop a queued Tx telegram sent from another id if the master hasn't polled that id after this many polls (~3-10 seconds) + static constexpr uint32_t MAX_POLLS_WITHOUT_MASTER = 500; + private: std::deque> tx_telegrams_; // the Tx queue diff --git a/src/web/WebLogService.cpp b/src/web/WebLogService.cpp index 7b2cce9c0..007ce3c70 100644 --- a/src/web/WebLogService.cpp +++ b/src/web/WebLogService.cpp @@ -76,9 +76,9 @@ void WebLogService::operator<<(std::shared_ptr message) { limit_log_messages_ = maximum_log_messages_; } else { uint32_t maxAlloc = ESP.getMaxAllocHeap(); - if (limit_log_messages_ > 5 && maxAlloc < (50 * 1024)) { // 50k + if (limit_log_messages_ > 5 && maxAlloc < LOG_HEAP_LOW) { --limit_log_messages_; - } else if (limit_log_messages_ < maximum_log_messages_ && maxAlloc > (60 * 1024)) { // 60k + } else if (limit_log_messages_ < maximum_log_messages_ && maxAlloc > LOG_HEAP_HIGH) { ++limit_log_messages_; } } diff --git a/src/web/WebLogService.h b/src/web/WebLogService.h index 330a3ed0a..42fa884f1 100644 --- a/src/web/WebLogService.h +++ b/src/web/WebLogService.h @@ -43,6 +43,11 @@ class WebLogService : public uuid::log::Handler { virtual void operator<<(std::shared_ptr message); private: + // on boards without PSRAM, shrink the log buffer below LOW and grow it again above HIGH (max alloc heap, in bytes). + // The gap between them stops the limit flapping around a single threshold + static constexpr uint32_t LOG_HEAP_LOW = 50 * 1024; // 50 KB + static constexpr uint32_t LOG_HEAP_HIGH = 60 * 1024; // 60 KB + AsyncEventSource events_; class QueuedLogMessage {