diff --git a/platformio.ini b/platformio.ini index 16bb0eb96..81f95a7e3 100644 --- a/platformio.ini +++ b/platformio.ini @@ -118,7 +118,7 @@ lib_deps = [device-ui_base] lib_deps = # renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master - https://github.com/meshtastic/device-ui/archive/233d18ef42e9d189f90fdfe621f0cd7edff2d221.zip + https://github.com/meshtastic/device-ui/archive/3677476c8a823ee85056b5fb1d146a3e193f8276.zip ; Common libs for environmental measurements in telemetry module [environmental_base] diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 7a253ff50..8d451be70 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -1,5 +1,4 @@ #include // Include for strstr -#include #include #include "configuration.h" @@ -1384,34 +1383,42 @@ GnssModel_t GPS::probe(int serialSpeed) GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vector &responseMap) { - String response = ""; + char response[256] = {0}; // Fixed buffer instead of String + uint16_t responseLen = 0; unsigned long start = millis(); while (millis() - start < timeout) { if (_serial_gps->available()) { - response += (char)_serial_gps->read(); + char c = _serial_gps->read(); - if (response.endsWith(",") || response.endsWith("\r\n")) { + // Add char to buffer if there's space + if (responseLen < sizeof(response) - 1) { + response[responseLen++] = c; + response[responseLen] = '\0'; + } + + if (c == ',' || (responseLen >= 2 && response[responseLen - 2] == '\r' && response[responseLen - 1] == '\n')) { #ifdef GPS_DEBUG - LOG_DEBUG(response.c_str()); + LOG_DEBUG(response); #endif // check if we can see our chips for (const auto &chipInfo : responseMap) { - if (strstr(response.c_str(), chipInfo.detectionString.c_str()) != nullptr) { + if (strstr(response, chipInfo.detectionString.c_str()) != nullptr) { LOG_INFO("%s detected", chipInfo.chipName.c_str()); return chipInfo.driver; } } } - if (response.endsWith("\r\n")) { - response.trim(); - response = ""; // Reset the response string for the next potential message + if (responseLen >= 2 && response[responseLen - 2] == '\r' && response[responseLen - 1] == '\n') { + // Reset the response buffer for the next potential message + responseLen = 0; + response[0] = '\0'; } } } #ifdef GPS_DEBUG - LOG_DEBUG(response.c_str()); + LOG_DEBUG(response); #endif - return GNSS_MODEL_UNKNOWN; // Return empty string on timeout + return GNSS_MODEL_UNKNOWN; // Return unknown on timeout } GPS *GPS::createGps() diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 2cc4197c1..7e35fccd8 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -61,8 +61,10 @@ Allocator &queueStatusPool = staticQueueStatusPool; #include "Router.h" MeshService::MeshService() - : toPhoneQueue(MAX_RX_TOPHONE), toPhoneQueueStatusQueue(MAX_RX_TOPHONE), toPhoneMqttProxyQueue(MAX_RX_TOPHONE), - toPhoneClientNotificationQueue(MAX_RX_TOPHONE / 2) +#ifdef ARCH_PORTDUINO + : toPhoneQueue(MAX_RX_TOPHONE), toPhoneQueueStatusQueue(MAX_RX_QUEUESTATUS_TOPHONE), + toPhoneMqttProxyQueue(MAX_RX_MQTTPROXY_TOPHONE), toPhoneClientNotificationQueue(MAX_RX_NOTIFICATION_TOPHONE) +#endif { lastQueueStatus = {0, 0, 16, 0}; } diff --git a/src/mesh/MeshService.h b/src/mesh/MeshService.h index f7d79366e..5d074368f 100644 --- a/src/mesh/MeshService.h +++ b/src/mesh/MeshService.h @@ -9,7 +9,12 @@ #include "MeshRadio.h" #include "MeshTypes.h" #include "Observer.h" +#ifdef ARCH_PORTDUINO #include "PointerQueue.h" +#else +#include "StaticPointerQueue.h" +#endif +#include "mesh-pb-constants.h" #if defined(ARCH_PORTDUINO) #include "../platform/portduino/SimRadio.h" #endif @@ -37,16 +42,32 @@ class MeshService /// FIXME, change to a DropOldestQueue and keep a count of the number of dropped packets to ensure /// we never hang because android hasn't been there in a while /// FIXME - save this to flash on deep sleep +#ifdef ARCH_PORTDUINO PointerQueue toPhoneQueue; +#else + StaticPointerQueue toPhoneQueue; +#endif // keep list of QueueStatus packets to be send to the phone +#ifdef ARCH_PORTDUINO PointerQueue toPhoneQueueStatusQueue; +#else + StaticPointerQueue toPhoneQueueStatusQueue; +#endif // keep list of MqttClientProxyMessages to be send to the client for delivery +#ifdef ARCH_PORTDUINO PointerQueue toPhoneMqttProxyQueue; +#else + StaticPointerQueue toPhoneMqttProxyQueue; +#endif // keep list of ClientNotifications to be send to the client (phone) +#ifdef ARCH_PORTDUINO PointerQueue toPhoneClientNotificationQueue; +#else + StaticPointerQueue toPhoneClientNotificationQueue; +#endif // This holds the last QueueStatus send meshtastic_QueueStatus lastQueueStatus; diff --git a/src/mesh/StaticPointerQueue.h b/src/mesh/StaticPointerQueue.h new file mode 100644 index 000000000..398ee450c --- /dev/null +++ b/src/mesh/StaticPointerQueue.h @@ -0,0 +1,77 @@ +#pragma once + +#include "concurrency/OSThread.h" +#include "freertosinc.h" +#include + +/** + * A static circular buffer queue for pointers. + * This provides the same interface as PointerQueue but uses a statically allocated + * buffer instead of dynamic allocation. + */ +template class StaticPointerQueue +{ + static_assert(MaxElements > 0, "MaxElements must be greater than 0"); + + T *buffer[MaxElements]; + int head = 0; + int tail = 0; + int count = 0; + concurrency::OSThread *reader = nullptr; + + public: + StaticPointerQueue() + { + // Initialize all buffer elements to nullptr to silence warnings and ensure clean state + for (int i = 0; i < MaxElements; i++) { + buffer[i] = nullptr; + } + } + + int numFree() const { return MaxElements - count; } + + bool isEmpty() const { return count == 0; } + + int numUsed() const { return count; } + + bool enqueue(T *x, TickType_t maxWait = portMAX_DELAY) + { + if (count >= MaxElements) { + return false; // Queue is full + } + + if (reader) { + reader->setInterval(0); + concurrency::mainDelay.interrupt(); + } + + buffer[tail] = x; + tail = (tail + 1) % MaxElements; + count++; + return true; + } + + bool dequeue(T **p, TickType_t maxWait = portMAX_DELAY) + { + if (count == 0) { + return false; // Queue is empty + } + + *p = buffer[head]; + head = (head + 1) % MaxElements; + count--; + return true; + } + + // returns a ptr or null if the queue was empty + T *dequeuePtr(TickType_t maxWait = portMAX_DELAY) + { + T *p; + return dequeue(&p, maxWait) ? p : nullptr; + } + + void setReader(concurrency::OSThread *t) { reader = t; } + + // For compatibility with PointerQueue interface + int getMaxLen() const { return MaxElements; } +}; diff --git a/src/mesh/mesh-pb-constants.h b/src/mesh/mesh-pb-constants.h index 08c03dc6b..224f45de2 100644 --- a/src/mesh/mesh-pb-constants.h +++ b/src/mesh/mesh-pb-constants.h @@ -18,6 +18,21 @@ #define MAX_RX_TOPHONE 32 #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 +#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 +#endif + +/// max number of ClientNotification packets which can be waiting for delivery to phone +#ifndef MAX_RX_NOTIFICATION_TOPHONE +#define MAX_RX_NOTIFICATION_TOPHONE 4 +#endif + /// Verify baseline assumption of node size. If it increases, we need to reevaluate /// the impact of its memory footprint, notably on MAX_NUM_NODES. static_assert(sizeof(meshtastic_NodeInfoLite) <= 200, "NodeInfoLite size increased. Reconsider impact on MAX_NUM_NODES."); diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index b9b4dd3e5..85d183aef 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -141,7 +141,10 @@ void setupModules() detectionSensorModule = new DetectionSensorModule(); #endif #if !MESHTASTIC_EXCLUDE_ATAK - atakPluginModule = new AtakPluginModule(); + if (IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_TAK, + meshtastic_Config_DeviceConfig_Role_TAK_TRACKER)) { + atakPluginModule = new AtakPluginModule(); + } #endif #if !MESHTASTIC_EXCLUDE_PKI keyVerificationModule = new KeyVerificationModule(); diff --git a/test/test_meshpacket_serializer/ports/test_text_message.cpp b/test/test_meshpacket_serializer/ports/test_text_message.cpp index 6213b08d5..7dddf3d7c 100644 --- a/test/test_meshpacket_serializer/ports/test_text_message.cpp +++ b/test/test_meshpacket_serializer/ports/test_text_message.cpp @@ -1,6 +1,48 @@ #include "../test_helpers.h" #include +// Helper function to test common packet fields and structure +void verify_text_message_packet_structure(const std::string &json, const char *expected_text) +{ + TEST_ASSERT_TRUE(json.length() > 0); + + // Use smart pointer for automatic memory management + std::unique_ptr root(JSON::Parse(json.c_str())); + TEST_ASSERT_NOT_NULL(root.get()); + TEST_ASSERT_TRUE(root->IsObject()); + + JSONObject jsonObj = root->AsObject(); + + // Check basic packet fields - use helper function to reduce duplication + auto check_field = [&](const char *field, uint32_t expected_value) { + auto it = jsonObj.find(field); + TEST_ASSERT_TRUE(it != jsonObj.end()); + TEST_ASSERT_EQUAL(expected_value, (uint32_t)it->second->AsNumber()); + }; + + check_field("from", 0x11223344); + check_field("to", 0x55667788); + check_field("id", 0x9999); + + // Check message type + auto type_it = jsonObj.find("type"); + TEST_ASSERT_TRUE(type_it != jsonObj.end()); + TEST_ASSERT_EQUAL_STRING("text", type_it->second->AsString().c_str()); + + // Check payload + auto payload_it = jsonObj.find("payload"); + TEST_ASSERT_TRUE(payload_it != jsonObj.end()); + TEST_ASSERT_TRUE(payload_it->second->IsObject()); + + JSONObject payload = payload_it->second->AsObject(); + auto text_it = payload.find("text"); + TEST_ASSERT_TRUE(text_it != payload.end()); + TEST_ASSERT_EQUAL_STRING(expected_text, text_it->second->AsString().c_str()); + + // No need for manual delete with smart pointer +} +#include + // Helper function to test common packet fields and structure void verify_text_message_packet_structure(const std::string &json, const char *expected_text) { @@ -102,4 +144,4 @@ void test_text_message_serialization_invalid_utf8() // Should not crash, may produce replacement characters std::string json = MeshPacketSerializer::JsonSerialize(&packet, false); TEST_ASSERT_TRUE(json.length() > 0); -} +} \ No newline at end of file diff --git a/variants/native/portduino-buildroot/variant.h b/variants/native/portduino-buildroot/variant.h index a453c3b71..3e91c6820 100644 --- a/variants/native/portduino-buildroot/variant.h +++ b/variants/native/portduino-buildroot/variant.h @@ -2,4 +2,4 @@ #define CANNED_MESSAGE_MODULE_ENABLE 1 #define HAS_GPS 1 #define MAX_RX_TOPHONE portduino_config.maxtophone -#define MAX_NUM_NODES portduino_config.MaxNodes \ No newline at end of file +#define MAX_NUM_NODES portduino_config.MaxNodes