1
0
mirror of https://github.com/thooge/esp32-nmea2000-obp60.git synced 2026-02-24 20:53:07 +01:00

GwWifi race condition safe, PageNavigation check the Wifi connection befor using

This commit is contained in:
Norbert Walter
2026-02-15 16:11:19 +00:00
parent 43b0a780d5
commit 00b06f458b
3 changed files with 115 additions and 8 deletions

View File

@@ -2,6 +2,9 @@
#define _GWWIFI_H #define _GWWIFI_H
#include <WiFi.h> #include <WiFi.h>
#include <GWConfig.h> #include <GWConfig.h>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
class GwWifi{ class GwWifi{
private: private:
const GwConfigHandler *config; const GwConfigHandler *config;
@@ -16,15 +19,21 @@ class GwWifi{
bool apActive=false; bool apActive=false;
bool fixedApPass=true; bool fixedApPass=true;
bool clientIsConnected=false; bool clientIsConnected=false;
SemaphoreHandle_t wifiMutex=nullptr;
static const TickType_t WIFI_MUTEX_TIMEOUT=pdMS_TO_TICKS(1000);
bool acquireMutex();
void releaseMutex();
public: public:
const char *AP_password = "esp32nmea2k"; const char *AP_password = "esp32nmea2k";
GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass=true); GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass=true);
~GwWifi();
void setup(); void setup();
void loop(); void loop();
bool clientConnected(); bool clientConnected();
bool connectClient(); bool connectClient(); // Blocking version
bool connectClientAsync(); // Non-blocking version for other tasks
String apIP(); String apIP();
bool isApActive(){return apActive;} bool isApActive(){return apActive;}
bool isClientActive(){return wifiClient->asBoolean();} bool isClientActive(){return wifiClient->asBoolean();}}
}; };
#endif #endif

View File

@@ -1,7 +1,6 @@
#include <esp_wifi.h> #include <esp_wifi.h>
#include "GWWifi.h" #include "GWWifi.h"
GwWifi::GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass){ GwWifi::GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass){
this->config=config; this->config=config;
this->logger=log; this->logger=log;
@@ -9,6 +8,28 @@ GwWifi::GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass){
wifiSSID=config->getConfigItem(config->wifiSSID,true); wifiSSID=config->getConfigItem(config->wifiSSID,true);
wifiPass=config->getConfigItem(config->wifiPass,true); wifiPass=config->getConfigItem(config->wifiPass,true);
this->fixedApPass=fixedApPass; 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(){ void GwWifi::setup(){
LOG_DEBUG(GwLog::LOG,"Wifi setup"); LOG_DEBUG(GwLog::LOG,"Wifi setup");
@@ -85,8 +106,14 @@ bool GwWifi::connectInternal(){
if (wifiClient->asBoolean()){ if (wifiClient->asBoolean()){
clientIsConnected=false; clientIsConnected=false;
LOG_DEBUG(GwLog::LOG,"creating wifiClient ssid=%s",wifiSSID->asString().c_str()); 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 WiFi.setAutoReconnect(false); //#102
wl_status_t rt=WiFi.begin(wifiSSID->asCString(),wifiPass->asCString()); wl_status_t rt=WiFi.begin(wifiSSID->asCString(),wifiPass->asCString());
releaseMutex();
LOG_DEBUG(GwLog::LOG,"wifiClient connect returns %d",(int)rt); LOG_DEBUG(GwLog::LOG,"wifiClient connect returns %d",(int)rt);
lastConnectStart=millis(); lastConnectStart=millis();
return true; return true;
@@ -104,9 +131,16 @@ void GwWifi::loop(){
if (lastConnectStart > now || (lastConnectStart + RETRY_MILLIS) < now) if (lastConnectStart > now || (lastConnectStart + RETRY_MILLIS) < now)
{ {
LOG_DEBUG(GwLog::LOG,"wifiClient: retry connect to %s", wifiSSID->asCString()); LOG_DEBUG(GwLog::LOG,"wifiClient: retry connect to %s", wifiSSID->asCString());
// CRITICAL SECTION: WiFi-Operationen müssen serialisiert werden
if (acquireMutex()){
WiFi.disconnect(); WiFi.disconnect();
releaseMutex();
connectInternal(); connectInternal();
} }
else{
LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex timeout in loop");
}
}
} }
else{ else{
if (! clientIsConnected){ if (! clientIsConnected){
@@ -127,10 +161,39 @@ void GwWifi::loop(){
} }
} }
bool GwWifi::clientConnected(){ 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(){ 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(); 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(); return connectInternal();
} }

View File

@@ -4,9 +4,13 @@
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#include "NetworkClient.h" // Network connection #include "NetworkClient.h" // Network connection
#include "ImageDecoder.h" // Image decoder for navigation map #include "ImageDecoder.h" // Image decoder for navigation map
#include "GWWifi.h" // WiFi management (thread-safe)
#include "Logo_OBP_400x300_sw.h" #include "Logo_OBP_400x300_sw.h"
// Extern declaration of global WiFi instance
extern GwWifi gwWifi;
// Defines for reading of navigation map // Defines for reading of navigation map
#define JSON_BUFFER 30000 // Max buffer size for JSON content (30 kB picture + values) #define JSON_BUFFER 30000 // Max buffer size for JSON content (30 kB picture + values)
NetworkClient net(JSON_BUFFER); // Define network client 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; int imageBackupHeight = 0;
size_t imageBackupSize = 0; size_t imageBackupSize = 0;
bool hasImageBackup = false; bool hasImageBackup = false;
static bool wifiConnectRequested; // Track if WiFi connection was requested
public: public:
PageNavigation(CommonData &common){ PageNavigation(CommonData &common){
@@ -55,12 +60,15 @@ bool showValues = false; // Show values HDT, SOG, DBT in navigation map
} }
return 0; // Commit the key return 0; // Commit the key
} }
// Code for zoom - // Code for zoom +
if(key == 2){ if(key == 2){
zoom ++; // Zoom + zoom ++; // Zoom +
if(zoom >17){ if(zoom >17){
zoom = 17; zoom = 17;
} }
// Optional: Versuche WiFi-Verbindung nach Zoom-Änderung
// Dies ermöglicht eine neue Kartendarstellung
gwWifi.connectClientAsync();
return 0; // Commit the key return 0; // Commit the key
} }
if(key == 5){ 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 zoom = zoomLevel; // Over write zoom level with setup value
showValues = showValuesMap; // Over write showValues with setup value showValues = showValuesMap; // Over write showValues with setup value
firstRun = false; // Restet variable 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 // Local variables
@@ -344,6 +359,16 @@ bool showValues = false; // Show values HDT, SOG, DBT in navigation map
// Load 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 // URL to OBP Maps Converter
// For more details see: https://github.com/norbert-walter/maps-converter // For more details see: https://github.com/norbert-walter/maps-converter
String url = String("http://") + server + ":" + port + // OBP Server 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); getdisplay().setTextColor(commonData->fgcolor);
// If a network connection to URL then load the navigation map // 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 auto& json = net.json(); // Extract JSON content
int numPix = json["number_pixels"] | 0; // Read number of pixels 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 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){ static Page *createPage(CommonData &common){
return new PageNavigation(common); return new PageNavigation(common);
}/** }/**