diff --git a/lib/gwwifi/GWWifi.h b/lib/gwwifi/GWWifi.h index 9063a4e..cb5c980 100644 --- a/lib/gwwifi/GWWifi.h +++ b/lib/gwwifi/GWWifi.h @@ -2,6 +2,9 @@ #define _GWWIFI_H #include #include +#include +#include + class GwWifi{ private: const GwConfigHandler *config; @@ -16,15 +19,21 @@ class GwWifi{ bool apActive=false; bool fixedApPass=true; bool clientIsConnected=false; + SemaphoreHandle_t wifiMutex=nullptr; + static const TickType_t WIFI_MUTEX_TIMEOUT=pdMS_TO_TICKS(1000); + bool acquireMutex(); + void releaseMutex(); public: const char *AP_password = "esp32nmea2k"; GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass=true); + ~GwWifi(); void setup(); void loop(); bool clientConnected(); - bool connectClient(); + bool connectClient(); // Blocking version + bool connectClientAsync(); // Non-blocking version for other tasks String apIP(); bool isApActive(){return apActive;} - bool isClientActive(){return wifiClient->asBoolean();} + bool isClientActive(){return wifiClient->asBoolean();}} }; #endif \ No newline at end of file diff --git a/lib/gwwifi/GwWifi.cpp b/lib/gwwifi/GwWifi.cpp index c81acec..19e6941 100644 --- a/lib/gwwifi/GwWifi.cpp +++ b/lib/gwwifi/GwWifi.cpp @@ -1,7 +1,6 @@ #include #include "GWWifi.h" - GwWifi::GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass){ this->config=config; this->logger=log; @@ -9,6 +8,28 @@ GwWifi::GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass){ wifiSSID=config->getConfigItem(config->wifiSSID,true); wifiPass=config->getConfigItem(config->wifiPass,true); this->fixedApPass=fixedApPass; + wifiMutex=xSemaphoreCreateMutex(); + if (wifiMutex==nullptr){ + LOG_DEBUG(GwLog::ERROR,"GwWifi: unable to create mutex"); + } +} + +GwWifi::~GwWifi(){ + if (wifiMutex!=nullptr){ + vSemaphoreDelete(wifiMutex); + wifiMutex=nullptr; + } +} + +bool GwWifi::acquireMutex(){ + if (wifiMutex==nullptr) return false; + return xSemaphoreTake(wifiMutex,WIFI_MUTEX_TIMEOUT)==pdTRUE; +} + +void GwWifi::releaseMutex(){ + if (wifiMutex!=nullptr){ + xSemaphoreGive(wifiMutex); + } } void GwWifi::setup(){ LOG_DEBUG(GwLog::LOG,"Wifi setup"); @@ -85,8 +106,14 @@ bool GwWifi::connectInternal(){ if (wifiClient->asBoolean()){ clientIsConnected=false; LOG_DEBUG(GwLog::LOG,"creating wifiClient ssid=%s",wifiSSID->asString().c_str()); + // CRITICAL SECTION: WiFi-Operationen müssen serialisiert werden + if (!acquireMutex()){ + LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex timeout in connectInternal"); + return false; + } WiFi.setAutoReconnect(false); //#102 wl_status_t rt=WiFi.begin(wifiSSID->asCString(),wifiPass->asCString()); + releaseMutex(); LOG_DEBUG(GwLog::LOG,"wifiClient connect returns %d",(int)rt); lastConnectStart=millis(); return true; @@ -104,8 +131,15 @@ void GwWifi::loop(){ if (lastConnectStart > now || (lastConnectStart + RETRY_MILLIS) < now) { LOG_DEBUG(GwLog::LOG,"wifiClient: retry connect to %s", wifiSSID->asCString()); - WiFi.disconnect(); - connectInternal(); + // CRITICAL SECTION: WiFi-Operationen müssen serialisiert werden + if (acquireMutex()){ + WiFi.disconnect(); + releaseMutex(); + connectInternal(); + } + else{ + LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex timeout in loop"); + } } } else{ @@ -127,10 +161,39 @@ void GwWifi::loop(){ } } bool GwWifi::clientConnected(){ - return WiFi.status() == WL_CONNECTED; + // CRITICAL SECTION: WiFi.status() muss geschützt werden + if (!acquireMutex()){ + LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex timeout in clientConnected"); + return false; // Conservative: nehme an, nicht verbunden + } + bool result = WiFi.status() == WL_CONNECTED; + releaseMutex(); + return result; }; bool GwWifi::connectClient(){ + // CRITICAL SECTION: Disconnect und Connect müssen atomar sein + if (!acquireMutex()){ + LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex timeout in connectClient"); + return false; + } WiFi.disconnect(); + releaseMutex(); + return connectInternal(); +} + +bool GwWifi::connectClientAsync(){ + // Non-blocking version: Versuche Mutex zu nehmen, gib aber sofort auf + // Ideal für Tasks, die nicht blockieren dürfen + if (wifiMutex==nullptr){ + LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex not initialized in connectClientAsync"); + return false; + } + if (xSemaphoreTake(wifiMutex, 0)!=pdTRUE){ + LOG_DEBUG(GwLog::LOG,"GwWifi: connectClientAsync skipped - WiFi busy"); + return false; // WiFi ist aktuell busy, versuche es später nochmal + } + WiFi.disconnect(); + xSemaphoreGive(wifiMutex); return connectInternal(); } diff --git a/lib/obp60task/PageNavigation.cpp b/lib/obp60task/PageNavigation.cpp index bd4adf1..550d70c 100644 --- a/lib/obp60task/PageNavigation.cpp +++ b/lib/obp60task/PageNavigation.cpp @@ -4,9 +4,13 @@ #include "OBP60Extensions.h" #include "NetworkClient.h" // Network connection #include "ImageDecoder.h" // Image decoder for navigation map +#include "GWWifi.h" // WiFi management (thread-safe) #include "Logo_OBP_400x300_sw.h" +// Extern declaration of global WiFi instance +extern GwWifi gwWifi; + // Defines for reading of navigation map #define JSON_BUFFER 30000 // Max buffer size for JSON content (30 kB picture + values) NetworkClient net(JSON_BUFFER); // Define network client @@ -25,6 +29,7 @@ bool showValues = false; // Show values HDT, SOG, DBT in navigation map int imageBackupHeight = 0; size_t imageBackupSize = 0; bool hasImageBackup = false; + static bool wifiConnectRequested; // Track if WiFi connection was requested public: PageNavigation(CommonData &common){ @@ -55,12 +60,15 @@ bool showValues = false; // Show values HDT, SOG, DBT in navigation map } return 0; // Commit the key } - // Code for zoom - + // Code for zoom + if(key == 2){ zoom ++; // Zoom + if(zoom >17){ zoom = 17; } + // Optional: Versuche WiFi-Verbindung nach Zoom-Änderung + // Dies ermöglicht eine neue Kartendarstellung + gwWifi.connectClientAsync(); return 0; // Commit the key } if(key == 5){ @@ -95,6 +103,13 @@ bool showValues = false; // Show values HDT, SOG, DBT in navigation map zoom = zoomLevel; // Over write zoom level with setup value showValues = showValuesMap; // Over write showValues with setup value firstRun = false; // Restet variable + + // Versuche beim ersten Laden eine WiFi-Verbindung herzustellen (non-blocking) + // Dies ist thread-safe und blockiert das UI nicht + if (!gwWifi.clientConnected()) { + LOG_DEBUG(GwLog::LOG, "PageNavigation: Initiating WiFi connection for map download"); + gwWifi.connectClientAsync(); + } } // Local variables @@ -344,6 +359,16 @@ bool showValues = false; // Show values HDT, SOG, DBT in navigation map // Load navigation map //*********************************************************** + // Prüfe WiFi-Verbindung (Thread-Safe) + // Diese Methode ist synchronisiert und blockiert maximal 1 Sekunde + bool wifiConnected = gwWifi.clientConnected(); + + if (!wifiConnected && !wifiConnectRequested) { + LOG_DEBUG(GwLog::LOG, "PageNavigation: WiFi not connected, attempting async connect"); + gwWifi.connectClientAsync(); + wifiConnectRequested = true; + } + // URL to OBP Maps Converter // For more details see: https://github.com/norbert-walter/maps-converter String url = String("http://") + server + ":" + port + // OBP Server @@ -375,7 +400,8 @@ bool showValues = false; // Show values HDT, SOG, DBT in navigation map getdisplay().setTextColor(commonData->fgcolor); // If a network connection to URL then load the navigation map - if (net.fetchAndDecompressJson(url)) { + if (wifiConnected && net.fetchAndDecompressJson(url)) { + wifiConnectRequested = false; // Reset flag after successful connection auto& json = net.json(); // Extract JSON content int numPix = json["number_pixels"] | 0; // Read number of pixels @@ -442,6 +468,12 @@ bool showValues = false; // Show values HDT, SOG, DBT in navigation map } lostCounter++; // Increment lost counter + + // Nur einmal pro Sekunde einen neuen Verbindungsversuch machen + if (!wifiConnectRequested) { + gwWifi.connectClientAsync(); + wifiConnectRequested = true; + } } @@ -488,6 +520,9 @@ bool showValues = false; // Show values HDT, SOG, DBT in navigation map }; }; +// Initialize static member variable +bool PageNavigation::wifiConnectRequested = false; + static Page *createPage(CommonData &common){ return new PageNavigation(common); }/**