diff --git a/platformio.ini b/platformio.ini index e2e5e1a18..47b5f823d 100644 --- a/platformio.ini +++ b/platformio.ini @@ -60,7 +60,7 @@ monitor_speed = 115200 monitor_filters = direct lib_deps = # renovate: datasource=git-refs depName=meshtastic-esp8266-oled-ssd1306 packageName=https://github.com/meshtastic/esp8266-oled-ssd1306 gitBranch=master - https://github.com/meshtastic/esp8266-oled-ssd1306/archive/9573abb64dc9c94f3051348f2bf4fc5cedf03c22.zip + https://github.com/meshtastic/esp8266-oled-ssd1306/archive/0cbc26b1f8f61957af0475f486b362eafe7cc4e2.zip # renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip # renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master diff --git a/protobufs b/protobufs index a84657c22..8caf42396 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit a84657c220421536f18d11fc5edf680efadbceeb +Subproject commit 8caf42396438f0d8a0305143485fd671c1fc7126 diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 8d451be70..a4c7464b8 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -1213,7 +1213,7 @@ static const char *DETECTED_MESSAGE = "%s detected"; LOG_DEBUG(PROBE_MESSAGE, COMMAND, FAMILY_NAME); \ clearBuffer(); \ _serial_gps->write(COMMAND "\r\n"); \ - GnssModel_t detectedDriver = getProbeResponse(TIMEOUT, RESPONSE_MAP); \ + GnssModel_t detectedDriver = getProbeResponse(TIMEOUT, RESPONSE_MAP, serialSpeed); \ if (detectedDriver != GNSS_MODEL_UNKNOWN) { \ return detectedDriver; \ } \ @@ -1381,9 +1381,18 @@ GnssModel_t GPS::probe(int serialSpeed) return GNSS_MODEL_UNKNOWN; } -GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vector &responseMap) +GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vector &responseMap, int serialSpeed) { - char response[256] = {0}; // Fixed buffer instead of String + // Calculate buffer size based on baud rate - 256 bytes for 9600 baud as baseline + // Higher baud rates get proportionally larger buffers to handle more data + int bufferSize = (serialSpeed * 256) / 9600; + // Clamp buffer size between reasonable limits + if (bufferSize < 128) + bufferSize = 128; + if (bufferSize > 2048) + bufferSize = 2048; + + char *response = new char[bufferSize](); // Dynamically allocate based on baud rate uint16_t responseLen = 0; unsigned long start = millis(); while (millis() - start < timeout) { @@ -1391,7 +1400,7 @@ GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vectorread(); // Add char to buffer if there's space - if (responseLen < sizeof(response) - 1) { + if (responseLen < bufferSize - 1) { response[responseLen++] = c; response[responseLen] = '\0'; } @@ -1404,6 +1413,7 @@ GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vector &responseMap); + GnssModel_t getProbeResponse(unsigned long timeout, const std::vector &responseMap, int serialSpeed); // Get GNSS model GnssModel_t probe(int serialSpeed); diff --git a/src/gps/RTC.cpp b/src/gps/RTC.cpp index e75102deb..da20e28eb 100644 --- a/src/gps/RTC.cpp +++ b/src/gps/RTC.cpp @@ -9,6 +9,9 @@ static RTCQuality currentQuality = RTCQualityNone; uint32_t lastSetFromPhoneNtpOrGps = 0; +static uint32_t lastTimeValidationWarning = 0; +static const uint32_t TIME_VALIDATION_WARNING_INTERVAL_MS = 15000; // 15 seconds + RTCQuality getRTCQuality() { return currentQuality; @@ -48,7 +51,9 @@ RTCSetResult readFromRTC() #ifdef BUILD_EPOCH if (tv.tv_sec < BUILD_EPOCH) { - LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH); + if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) { + LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH); + } return RTCSetResultInvalidTime; } #endif @@ -87,7 +92,10 @@ RTCSetResult readFromRTC() #ifdef BUILD_EPOCH if (tv.tv_sec < BUILD_EPOCH) { - LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH); + if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) { + LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH); + lastTimeValidationWarning = millis(); + } return RTCSetResultInvalidTime; } #endif @@ -130,15 +138,20 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd uint32_t printableEpoch = tv->tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms #ifdef BUILD_EPOCH if (tv->tv_sec < BUILD_EPOCH) { -#ifdef GPS_DEBUG - LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH); -#endif + if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) { + LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH); + lastTimeValidationWarning = millis(); + } return RTCSetResultInvalidTime; - } else if (tv->tv_sec > (BUILD_EPOCH + FORTY_YEARS)) { -#ifdef GPS_DEBUG - LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch, BUILD_EPOCH, - BUILD_EPOCH + FORTY_YEARS); -#endif + } else if ((uint64_t)tv->tv_sec > ((uint64_t)BUILD_EPOCH + FORTY_YEARS)) { + if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) { + // Calculate max allowed time safely to avoid overflow in logging + uint64_t maxAllowedTime = (uint64_t)BUILD_EPOCH + FORTY_YEARS; + uint32_t maxAllowedPrintable = (maxAllowedTime > UINT32_MAX) ? UINT32_MAX : (uint32_t)maxAllowedTime; + LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch, + (uint32_t)BUILD_EPOCH, maxAllowedPrintable); + lastTimeValidationWarning = millis(); + } return RTCSetResultInvalidTime; } #endif @@ -256,15 +269,20 @@ RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t) uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms #ifdef BUILD_EPOCH if (tv.tv_sec < BUILD_EPOCH) { -#ifdef GPS_DEBUG - LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH); -#endif + if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) { + LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH); + lastTimeValidationWarning = millis(); + } return RTCSetResultInvalidTime; - } else if (tv.tv_sec > (BUILD_EPOCH + FORTY_YEARS)) { -#ifdef GPS_DEBUG - LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch, BUILD_EPOCH, - BUILD_EPOCH + FORTY_YEARS); -#endif + } else if ((uint64_t)tv.tv_sec > ((uint64_t)BUILD_EPOCH + FORTY_YEARS)) { + if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) { + // Calculate max allowed time safely to avoid overflow in logging + uint64_t maxAllowedTime = (uint64_t)BUILD_EPOCH + FORTY_YEARS; + uint32_t maxAllowedPrintable = (maxAllowedTime > UINT32_MAX) ? UINT32_MAX : (uint32_t)maxAllowedTime; + LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch, + (uint32_t)BUILD_EPOCH, maxAllowedPrintable); + lastTimeValidationWarning = millis(); + } return RTCSetResultInvalidTime; } #endif diff --git a/src/gps/RTC.h b/src/gps/RTC.h index 03350823c..eca17bf35 100644 --- a/src/gps/RTC.h +++ b/src/gps/RTC.h @@ -56,5 +56,5 @@ time_t gm_mktime(struct tm *tm); #define SEC_PER_HOUR 3600 #define SEC_PER_MIN 60 #ifdef BUILD_EPOCH -#define FORTY_YEARS (40UL * 365 * SEC_PER_DAY) // probably time to update your firmware +static constexpr uint64_t FORTY_YEARS = (40ULL * 365 * SEC_PER_DAY); // Use 64-bit arithmetic to prevent overflow #endif diff --git a/src/mesh/MemoryPool.h b/src/mesh/MemoryPool.h index 0c5ba6c71..eb5ac5109 100644 --- a/src/mesh/MemoryPool.h +++ b/src/mesh/MemoryPool.h @@ -115,12 +115,11 @@ template class MemoryPool : public Allocator bool used[MaxSize]; public: - MemoryPool() + MemoryPool() : pool{}, used{} { - // Initialize the used array to false (all slots free) - for (int i = 0; i < MaxSize; i++) { - used[i] = false; - } + // Arrays are now zero-initialized by member initializer list + // pool array: all elements are default-constructed (zero for POD types) + // used array: all elements are false (zero-initialized) } /// Return a buffer for use by others diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 607766ab6..96782cda5 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -46,11 +46,14 @@ the new node can build its node db) MeshService *service; -static MemoryDynamic staticMqttClientProxyMessagePool; +#define MAX_MQTT_PROXY_MESSAGES 16 +static MemoryPool staticMqttClientProxyMessagePool; -static MemoryDynamic staticQueueStatusPool; +#define MAX_QUEUE_STATUS 4 +static MemoryPool staticQueueStatusPool; -static MemoryDynamic staticClientNotificationPool; +#define MAX_CLIENT_NOTIFICATIONS 4 +static MemoryPool staticClientNotificationPool; Allocator &mqttClientProxyMessagePool = staticMqttClientProxyMessagePool; diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index e1e81d7cc..37b70dab6 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -5,6 +5,7 @@ #include "MeshService.h" #include "NodeDB.h" #include "RTC.h" + #include "configuration.h" #include "detect/LoRaRadioType.h" #include "main.h" @@ -27,14 +28,24 @@ // I think this is right, one packet for each of the three fifos + one packet being currently assembled for TX or RX // And every TX packet might have a retransmission packet or an ack alive at any moment + +#ifdef ARCH_PORTDUINO +// Portduino (native) targets can use dynamic memory pools with runtime-configurable sizes #define MAX_PACKETS \ (MAX_RX_TOPHONE + MAX_RX_FROMRADIO + 2 * MAX_TX_QUEUE + \ 2) // max number of packets which can be in flight (either queued from reception or queued for sending) -// static MemoryPool staticPool(MAX_PACKETS); -static MemoryDynamic staticPool; +static MemoryDynamic dynamicPool; +Allocator &packetPool = dynamicPool; +#else +// Embedded targets use static memory pools with compile-time constants +#define MAX_PACKETS_STATIC \ + (MAX_RX_TOPHONE + MAX_RX_FROMRADIO + 2 * MAX_TX_QUEUE + \ + 2) // max number of packets which can be in flight (either queued from reception or queued for sending) +static MemoryPool staticPool; Allocator &packetPool = staticPool; +#endif static uint8_t bytes[MAX_LORA_PAYLOAD_LEN + 1] __attribute__((__aligned__)); diff --git a/src/mesh/generated/meshtastic/device_ui.pb.h b/src/mesh/generated/meshtastic/device_ui.pb.h index 8313438f8..8f693e570 100644 --- a/src/mesh/generated/meshtastic/device_ui.pb.h +++ b/src/mesh/generated/meshtastic/device_ui.pb.h @@ -66,6 +66,8 @@ typedef enum _meshtastic_Language { meshtastic_Language_UKRAINIAN = 16, /* Bulgarian */ meshtastic_Language_BULGARIAN = 17, + /* Czech */ + meshtastic_Language_CZECH = 18, /* Simplified Chinese (experimental) */ meshtastic_Language_SIMPLIFIED_CHINESE = 30, /* Traditional Chinese (experimental) */ diff --git a/src/mesh/http/ContentHandler.cpp b/src/mesh/http/ContentHandler.cpp index 74953d8fc..fb66dae7c 100644 --- a/src/mesh/http/ContentHandler.cpp +++ b/src/mesh/http/ContentHandler.cpp @@ -292,11 +292,14 @@ JSONArray htmlListDir(const char *dirname, uint8_t levels) JSONObject thisFileMap; thisFileMap["size"] = new JSONValue((int)file.size()); #ifdef ARCH_ESP32 - thisFileMap["name"] = new JSONValue(String(file.path()).substring(1).c_str()); + String fileName = String(file.path()).substring(1); + thisFileMap["name"] = new JSONValue(fileName.c_str()); #else - thisFileMap["name"] = new JSONValue(String(file.name()).substring(1).c_str()); + String fileName = String(file.name()).substring(1); + thisFileMap["name"] = new JSONValue(fileName.c_str()); #endif - if (String(file.name()).substring(1).endsWith(".gz")) { + String tempName = String(file.name()).substring(1); + if (tempName.endsWith(".gz")) { #ifdef ARCH_ESP32 String modifiedFile = String(file.path()).substring(1); #else @@ -339,7 +342,8 @@ void handleFsBrowseStatic(HTTPRequest *req, HTTPResponse *res) JSONValue *value = new JSONValue(jsonObjOuter); - res->print(value->Stringify().c_str()); + std::string jsonString = value->Stringify(); + res->print(jsonString.c_str()); delete value; @@ -367,7 +371,8 @@ void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res) JSONObject jsonObjOuter; jsonObjOuter["status"] = new JSONValue("ok"); JSONValue *value = new JSONValue(jsonObjOuter); - res->print(value->Stringify().c_str()); + std::string jsonString = value->Stringify(); + res->print(jsonString.c_str()); delete value; return; } else { @@ -376,7 +381,8 @@ void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res) JSONObject jsonObjOuter; jsonObjOuter["status"] = new JSONValue("Error"); JSONValue *value = new JSONValue(jsonObjOuter); - res->print(value->Stringify().c_str()); + std::string jsonString = value->Stringify(); + res->print(jsonString.c_str()); delete value; return; } @@ -622,10 +628,7 @@ void handleReport(HTTPRequest *req, HTTPResponse *res) tempArray.push_back(new JSONValue((int)logArray[i])); } JSONValue *result = new JSONValue(tempArray); - // Clean up original array to prevent memory leak - for (auto *val : tempArray) { - delete val; - } + // Note: Don't delete tempArray elements here - JSONValue now owns them return result; }; @@ -656,7 +659,9 @@ void handleReport(HTTPRequest *req, HTTPResponse *res) // data->wifi JSONObject jsonObjWifi; jsonObjWifi["rssi"] = new JSONValue(WiFi.RSSI()); - jsonObjWifi["ip"] = new JSONValue(WiFi.localIP().toString().c_str()); + String wifiIPString = WiFi.localIP().toString(); + std::string wifiIP = wifiIPString.c_str(); + jsonObjWifi["ip"] = new JSONValue(wifiIP.c_str()); // data->memory JSONObject jsonObjMemory; @@ -702,7 +707,8 @@ void handleReport(HTTPRequest *req, HTTPResponse *res) jsonObjOuter["status"] = new JSONValue("ok"); // serialize and write it to the stream JSONValue *value = new JSONValue(jsonObjOuter); - res->print(value->Stringify().c_str()); + std::string jsonString = value->Stringify(); + res->print(jsonString.c_str()); delete value; } @@ -773,7 +779,8 @@ void handleNodes(HTTPRequest *req, HTTPResponse *res) jsonObjOuter["status"] = new JSONValue("ok"); // serialize and write it to the stream JSONValue *value = new JSONValue(jsonObjOuter); - res->print(value->Stringify().c_str()); + std::string jsonString = value->Stringify(); + res->print(jsonString.c_str()); delete value; // Clean up the nodesArray to prevent memory leak @@ -926,7 +933,8 @@ void handleBlinkLED(HTTPRequest *req, HTTPResponse *res) JSONObject jsonObjOuter; jsonObjOuter["status"] = new JSONValue("ok"); JSONValue *value = new JSONValue(jsonObjOuter); - res->print(value->Stringify().c_str()); + std::string jsonString = value->Stringify(); + res->print(jsonString.c_str()); delete value; } @@ -968,7 +976,8 @@ void handleScanNetworks(HTTPRequest *req, HTTPResponse *res) // serialize and write it to the stream JSONValue *value = new JSONValue(jsonObjOuter); - res->print(value->Stringify().c_str()); + std::string jsonString = value->Stringify(); + res->print(jsonString.c_str()); delete value; // Clean up the networkObjs to prevent memory leak diff --git a/src/mesh/mesh-pb-constants.h b/src/mesh/mesh-pb-constants.h index 12aec98cd..e4f65aa28 100644 --- a/src/mesh/mesh-pb-constants.h +++ b/src/mesh/mesh-pb-constants.h @@ -15,17 +15,21 @@ // FIXME - max_count is actually 32 but we save/load this as one long string of preencoded MeshPacket bytes - not a big array in // RAM #define MAX_RX_TOPHONE (member_size(DeviceState, receive_queue) / member_size(DeviceState, receive_queue[0])) #ifndef MAX_RX_TOPHONE +#if defined(ARCH_ESP32) && !(defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)) +#define MAX_RX_TOPHONE 8 +#else #define MAX_RX_TOPHONE 32 #endif +#endif /// max number of QueueStatus packets which can be waiting for delivery to phone #ifndef MAX_RX_QUEUESTATUS_TOPHONE -#define MAX_RX_QUEUESTATUS_TOPHONE 4 +#define MAX_RX_QUEUESTATUS_TOPHONE 2 #endif /// max number of MqttClientProxyMessage packets which can be waiting for delivery to phone #ifndef MAX_RX_MQTTPROXY_TOPHONE -#define MAX_RX_MQTTPROXY_TOPHONE 32 +#define MAX_RX_MQTTPROXY_TOPHONE 8 #endif /// max number of ClientNotification packets which can be waiting for delivery to phone diff --git a/variants/esp32s3/tlora-pager/rfswitch.h b/variants/esp32s3/tlora-pager/rfswitch.h index 1e5eb7a9e..337346ec5 100644 --- a/variants/esp32s3/tlora-pager/rfswitch.h +++ b/variants/esp32s3/tlora-pager/rfswitch.h @@ -1,18 +1,15 @@ #include "RadioLib.h" -static const uint32_t rfswitch_dio_pins[] = { - RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, - RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC -}; +static const uint32_t rfswitch_dio_pins[] = {RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC}; static const Module::RfSwitchMode_t rfswitch_table[] = { // mode DIO5 DIO6 - { LR11x0::MODE_STBY, { LOW, LOW } }, - { LR11x0::MODE_RX, { LOW, HIGH } }, - { LR11x0::MODE_TX, { HIGH, LOW } }, - { LR11x0::MODE_TX_HP, { HIGH, LOW } }, - { LR11x0::MODE_TX_HF, { LOW, LOW } }, - { LR11x0::MODE_GNSS, { LOW, LOW } }, - { LR11x0::MODE_WIFI, { LOW, LOW } }, + {LR11x0::MODE_STBY, {LOW, LOW}}, + {LR11x0::MODE_RX, {LOW, HIGH}}, + {LR11x0::MODE_TX, {HIGH, LOW}}, + {LR11x0::MODE_TX_HP, {HIGH, LOW}}, + {LR11x0::MODE_TX_HF, {LOW, LOW}}, + {LR11x0::MODE_GNSS, {LOW, LOW}}, + {LR11x0::MODE_WIFI, {LOW, LOW}}, END_OF_MODE_TABLE, }; \ No newline at end of file