From 4bc908849a6393587ae55d0ac0b28256195a2080 Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Tue, 29 Jul 2025 07:57:59 -1000 Subject: [PATCH 01/17] Revert "Add a semaphore for tcpServer - UNTESTED" This reverts commit d66c96d6fddeb6ce4466a84106ce0846f8a6e73e. --- Firmware/RTK_Everywhere/RTK_Everywhere.ino | 5 - Firmware/RTK_Everywhere/TcpServer.ino | 130 ++++++++------------- 2 files changed, 49 insertions(+), 86 deletions(-) diff --git a/Firmware/RTK_Everywhere/RTK_Everywhere.ino b/Firmware/RTK_Everywhere/RTK_Everywhere.ino index c4b71e954..e20767436 100644 --- a/Firmware/RTK_Everywhere/RTK_Everywhere.ino +++ b/Firmware/RTK_Everywhere/RTK_Everywhere.ino @@ -343,11 +343,6 @@ const TickType_t ringBuffer_longWait_ms = 300 / portTICK_PERIOD_MS; SemaphoreHandle_t ringBufferSemaphore = NULL; const char *ringBufferSemaphoreHolder = "None"; -// tcpServer semaphore - prevent tcpServerClientSendData (handleGnssDataTask) and tcpServerUpdate -// from gatecrashing each other. See #695 for why this is needed. -SemaphoreHandle_t tcpServerSemaphore = NULL; -const char *tcpServerSemaphoreHolder = "None"; - // Display used/free space in menu and config page uint64_t sdCardSize; uint64_t sdFreeSpace; diff --git a/Firmware/RTK_Everywhere/TcpServer.ino b/Firmware/RTK_Everywhere/TcpServer.ino index 7f4385570..170f86979 100644 --- a/Firmware/RTK_Everywhere/TcpServer.ino +++ b/Firmware/RTK_Everywhere/TcpServer.ino @@ -139,42 +139,26 @@ int32_t tcpServerClientSendData(int index, uint8_t *data, uint16_t length) { if (tcpServerClient[index]) { - // Use a semaphore to prevent tcpServerUpdate from gatecrashing - if (tcpServerSemaphore == NULL) - tcpServerSemaphore = xSemaphoreCreateMutex(); // Create the mutex - - // Take the semaphore - if (xSemaphoreTake(tcpServerSemaphore, 50 / portTICK_PERIOD_MS) == pdPASS) + length = tcpServerClient[index]->write(data, length); + if (length > 0) { - tcpServerSemaphoreHolder = "tcpServerClientSendData"; - - length = tcpServerClient[index]->write(data, length); + // Update the data sent flag when data successfully sent if (length > 0) + tcpServerClientDataSent = tcpServerClientDataSent | (1 << index); + if ((settings.debugTcpServer || PERIODIC_DISPLAY(PD_TCP_SERVER_CLIENT_DATA)) && (!inMainMenu)) { - // Update the data sent flag when data successfully sent - if (length > 0) - tcpServerClientDataSent = tcpServerClientDataSent | (1 << index); - if ((settings.debugTcpServer || PERIODIC_DISPLAY(PD_TCP_SERVER_CLIENT_DATA)) && (!inMainMenu)) - { - PERIODIC_CLEAR(PD_TCP_SERVER_CLIENT_DATA); - systemPrintf("TCP server wrote %d bytes to %s\r\n", length, - tcpServerClientIpAddress[index].toString().c_str()); - } - } - - // Failed to write the data - else - { - // Done with this client connection - tcpServerStopClient(index); - length = 0; + PERIODIC_CLEAR(PD_TCP_SERVER_CLIENT_DATA); + systemPrintf("TCP server wrote %d bytes to %s\r\n", length, + tcpServerClientIpAddress[index].toString().c_str()); } - - xSemaphoreGive(tcpServerSemaphore); } + + // Failed to write the data else { - systemPrintf("tcpServerClientSendData could not get semaphore - held by %s\r\n", tcpServerSemaphoreHolder); + // Done with this client connection + tcpServerStopClient(index); + length = 0; } } return length; @@ -569,62 +553,46 @@ void tcpServerUpdate() // and respond accordingly if (settings.enableNtripCaster || settings.baseCasterOverride) { - // Use a semaphore to prevent tcpServerClientSendData from gatecrashing - if (tcpServerSemaphore == NULL) - tcpServerSemaphore = xSemaphoreCreateMutex(); // Create the mutex - - // Take the semaphore - if (xSemaphoreTake(tcpServerSemaphore, 50 / portTICK_PERIOD_MS) == pdPASS) + // Read response from client + char response[512]; + int spot = 0; + while (tcpServerClient[index]->available()) + { + response[spot++] = tcpServerClient[index]->read(); + if (spot == sizeof(response)) + spot = 0; // Wrap + } + response[spot] = '\0'; // Terminate string + + if (strnstr(response, "GET / ", sizeof(response)) != NULL) // No mount point in header + { + if (settings.debugTcpServer) + systemPrintln("Mount point table requested."); + + // Respond with a single mountpoint + const char fakeSourceTable[] = + "SOURCETABLE 200 OK\r\nServer: SparkPNT Caster/1.0\r\nContent-Type: " + "text/plain\r\nContent-Length: 96\r\n\r\nSTR;SparkBase;none;RTCM " + "3.0;none;none;none;none;none;none;none;none;none;none;none;B;N;none;none"; + + tcpServerClient[index]->write(fakeSourceTable, strlen(fakeSourceTable)); + + tcpServerStopClient(index); // Disconnect from client + } + else if (strnstr(response, "GET /", sizeof(response)) != NULL) // Mount point in header { - tcpServerSemaphoreHolder = "tcpServerUpdate"; - - // Read response from client - char response[512]; - int spot = 0; - while (tcpServerClient[index]->available()) - { - response[spot++] = tcpServerClient[index]->read(); - if (spot == sizeof(response)) - spot = 0; // Wrap - } - response[spot] = '\0'; // Terminate string - - if (strnstr(response, "GET / ", sizeof(response)) != NULL) // No mount point in header - { - if (settings.debugTcpServer) - systemPrintln("Mount point table requested."); - - // Respond with a single mountpoint - const char fakeSourceTable[] = - "SOURCETABLE 200 OK\r\nServer: SparkPNT Caster/1.0\r\nContent-Type: " - "text/plain\r\nContent-Length: 96\r\n\r\nSTR;SparkBase;none;RTCM " - "3.0;none;none;none;none;none;none;none;none;none;none;none;B;N;none;none"; - - tcpServerClient[index]->write(fakeSourceTable, strlen(fakeSourceTable)); - - tcpServerStopClient(index); // Disconnect from client - } - else if (strnstr(response, "GET /", sizeof(response)) != NULL) // Mount point in header - { - // NTRIP Client is sending us their mount point. Begin sending RTCM. - if (settings.debugTcpServer) - systemPrintln("NTRIP Client connected - Sending ICY 200 OK"); - - char confirmConnection[] = "ICY 200 OK\r\n"; - tcpServerClient[index]->write(confirmConnection, strlen(confirmConnection)); - } - else - { - // Unknown response - if (settings.debugTcpServer) - systemPrintf("Unknown response: %s\r\n", response); - } - - xSemaphoreGive(tcpServerSemaphore); + // NTRIP Client is sending us their mount point. Begin sending RTCM. + if (settings.debugTcpServer) + systemPrintln("NTRIP Client connected - Sending ICY 200 OK"); + + char confirmConnection[] = "ICY 200 OK\r\n"; + tcpServerClient[index]->write(confirmConnection, strlen(confirmConnection)); } else { - systemPrintf("tcpServerUpdate could not get semaphore - held by %s\r\n", tcpServerSemaphoreHolder); + // Unknown response + if (settings.debugTcpServer) + systemPrintf("Unknown response: %s\r\n", response); } } // settings.enableNtripCaster == true || settings.baseCasterOverride == true From 55554088b1b8471f930b3d5fa968e7f0df1cd612 Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Tue, 1 Jul 2025 04:23:42 -1000 Subject: [PATCH 02/17] TcpServer: Periodically display data sent messages for all clients --- Firmware/RTK_Everywhere/TcpServer.ino | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Firmware/RTK_Everywhere/TcpServer.ino b/Firmware/RTK_Everywhere/TcpServer.ino index 170f86979..bf13e3bcb 100644 --- a/Firmware/RTK_Everywhere/TcpServer.ino +++ b/Firmware/RTK_Everywhere/TcpServer.ino @@ -146,11 +146,8 @@ int32_t tcpServerClientSendData(int index, uint8_t *data, uint16_t length) if (length > 0) tcpServerClientDataSent = tcpServerClientDataSent | (1 << index); if ((settings.debugTcpServer || PERIODIC_DISPLAY(PD_TCP_SERVER_CLIENT_DATA)) && (!inMainMenu)) - { - PERIODIC_CLEAR(PD_TCP_SERVER_CLIENT_DATA); systemPrintf("TCP server wrote %d bytes to %s\r\n", length, tcpServerClientIpAddress[index].toString().c_str()); - } } // Failed to write the data @@ -240,6 +237,8 @@ int32_t tcpServerSendData(uint16_t dataHead) } tcpServerClientTails[index] = tail; } + if (PERIODIC_DISPLAY(PD_TCP_SERVER_CLIENT_DATA)) + PERIODIC_CLEAR(PD_TCP_SERVER_CLIENT_DATA); // Return the amount of space that TCP server client is using in the buffer return usedSpace; From 2b7196192ea500cfd0c1fa57c8e228cc1d749b3b Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Tue, 1 Jul 2025 04:38:59 -1000 Subject: [PATCH 03/17] TcpServer: Output TCP Server, NTRIP Caster or Base Caster messages --- Firmware/RTK_Everywhere/TcpServer.ino | 104 +++++++++++++++++++------- 1 file changed, 77 insertions(+), 27 deletions(-) diff --git a/Firmware/RTK_Everywhere/TcpServer.ino b/Firmware/RTK_Everywhere/TcpServer.ino index bf13e3bcb..c5c9a569d 100644 --- a/Firmware/RTK_Everywhere/TcpServer.ino +++ b/Firmware/RTK_Everywhere/TcpServer.ino @@ -82,7 +82,9 @@ const char *const tcpServerStateName[] = { const int tcpServerStateNameEntries = sizeof(tcpServerStateName) / sizeof(tcpServerStateName[0]); -const RtkMode_t tcpServerMode = RTK_MODE_BASE_FIXED | RTK_MODE_BASE_SURVEY_IN | RTK_MODE_ROVER; +const RtkMode_t baseCasterMode = RTK_MODE_BASE_FIXED; +const RtkMode_t tcpServerMode = RTK_MODE_ROVER + | RTK_MODE_BASE_SURVEY_IN; //---------------------------------------- // Locals @@ -92,6 +94,7 @@ const RtkMode_t tcpServerMode = RTK_MODE_BASE_FIXED | RTK_MODE_BASE_SURVEY_IN | static NetworkServer *tcpServer = nullptr; static uint8_t tcpServerState; static uint32_t tcpServerTimer; +static const char * tcpServerName; // TCP server clients static volatile uint8_t tcpServerClientConnected; @@ -146,7 +149,8 @@ int32_t tcpServerClientSendData(int index, uint8_t *data, uint16_t length) if (length > 0) tcpServerClientDataSent = tcpServerClientDataSent | (1 << index); if ((settings.debugTcpServer || PERIODIC_DISPLAY(PD_TCP_SERVER_CLIENT_DATA)) && (!inMainMenu)) - systemPrintf("TCP server wrote %d bytes to %s\r\n", length, + systemPrintf("%s wrote %d bytes to %s\r\n", + tcpServerName, length, tcpServerClientIpAddress[index].toString().c_str()); } @@ -167,22 +171,66 @@ int32_t tcpServerClientSendData(int index, uint8_t *data, uint16_t length) bool tcpServerEnabled(const char ** line) { bool enabled; + const char * name; do { + // Determine if the server is enabled enabled = false; + if ((settings.enableTcpServer + || settings.enableNtripCaster + || settings.baseCasterOverride) == false) + { + *line = ", Not enabled!"; + break; + } + + // Determine if the TCP server should be running + if ((EQ_RTK_MODE(tcpServerMode) && settings.enableTcpServer)) + { + // TCP server running in Rover mode + name = "TCP Server"; + } + + // Determine if the base caster should be running + else if (EQ_RTK_MODE(baseCasterMode) + && (settings.enableNtripCaster || settings.baseCasterOverride)) + { + // Select the base caster WiFi mode and port number + if (settings.baseCasterOverride) + { + name = "Base Caster"; + } + else + { + name = "NTRIP Caster"; + } + } - // Verify the operating mode - if (NEQ_RTK_MODE(tcpServerMode)) + // Wrong mode for TCP server or base caster operation + else { *line = ", Wrong mode!"; break; } - // Verify still enabled - enabled = settings.enableTcpServer || settings.baseCasterOverride; - if (enabled == false) - *line = ", Not enabled!"; + // Only change modes when in off state + if (tcpServerState == TCP_SERVER_STATE_OFF) + { + // Update the TCP server configuration + tcpServerName = name; + } + + // Shutdown and restart the TCP server when configuration changes + else if (name != tcpServerName) + { + *line = ", Wrong state to switch configuration!"; + break; + } + + // The server is enabled and in the correct mode + *line = ""; + enabled = true; } while (0); return enabled; } @@ -265,7 +313,7 @@ void tcpServerSetState(uint8_t newState) { if (newState >= TCP_SERVER_STATE_MAX) { - systemPrintf("Unknown TCP Server state: %d\r\n", tcpServerState); + systemPrintf("Unknown %s state: %d\r\n", tcpServerName, tcpServerState); reportFatalError("Unknown TCP Server state"); } else @@ -281,7 +329,7 @@ bool tcpServerStart() IPAddress localIp; if (settings.debugTcpServer && (!inMainMenu)) - systemPrintln("TCP server starting the server"); + systemPrintf("%s starting the server\r\n", tcpServerName); uint16_t tcpPort = settings.tcpServerPort; if(settings.baseCasterOverride == true) @@ -296,12 +344,8 @@ bool tcpServerStart() online.tcpServer = true; localIp = networkGetIpAddress(); - if (settings.enableNtripCaster || settings.baseCasterOverride) - systemPrintf("TCP server online, IP address %s:%d, responding as NTRIP Caster\r\n", localIp.toString().c_str(), - tcpPort); - else - systemPrintf("TCP server online, IP address %s:%d\r\n", localIp.toString().c_str(), tcpPort); - + systemPrintf("%s online, IP address %s:%d\r\n", tcpServerName, + localIp.toString().c_str(), tcpPort); return true; } @@ -316,7 +360,8 @@ void tcpServerStop() if (online.tcpServer) { if (settings.debugTcpServer && (!inMainMenu)) - systemPrintf("TcpServer: Notifying GNSS UART task to stop sending data\r\n"); + systemPrintf("%s: Notifying GNSS UART task to stop sending data\r\n", + tcpServerName); // Notify the GNSS UART tasks of the TCP server shutdown online.tcpServer = false; @@ -336,7 +381,7 @@ void tcpServerStop() { // Stop the TCP server if (settings.debugTcpServer && (!inMainMenu)) - systemPrintln("TcpServer: Stopping the server"); + systemPrintf("%s: Stopping the server\r\n", tcpServerName); tcpServer->stop(); delete tcpServer; tcpServer = nullptr; @@ -344,7 +389,7 @@ void tcpServerStop() // Stop using the network if (settings.debugTcpServer && (!inMainMenu)) - systemPrintln("TcpServer: Stopping network consumers"); + systemPrintf("%s: Stopping network consumers\r\n", tcpServerName); networkConsumerOffline(NETCONSUMER_TCP_SERVER); if (tcpServerState != TCP_SERVER_STATE_OFF) { @@ -379,10 +424,13 @@ void tcpServerStopClient(int index) dataSent = ((millis() - tcpServerTimer) < TCP_SERVER_CLIENT_DATA_TIMEOUT) || (tcpServerClientDataSent & (1 << index)); if (!dataSent) - systemPrintf("TCP Server: No data sent over %d seconds\r\n", TCP_SERVER_CLIENT_DATA_TIMEOUT / 1000); + systemPrintf("%s: No data sent over %d seconds\r\n", + tcpServerName, + TCP_SERVER_CLIENT_DATA_TIMEOUT / 1000); if (!connected) - systemPrintf("TCP Server: Link to client broken\r\n"); - systemPrintf("TCP server client %d disconnected from %s\r\n", index, + systemPrintf("%s: Link to client broken\r\n", tcpServerName); + systemPrintf("%s client %d disconnected from %s\r\n", + tcpServerName, index, tcpServerClientIpAddress[index].toString().c_str()); } @@ -447,7 +495,7 @@ void tcpServerUpdate() if (enabled) { if (settings.debugTcpServer && (!inMainMenu)) - systemPrintln("TCP server start"); + systemPrintf("%s start/r/n", tcpServerName); if (settings.tcpUdpOverWiFiStation == true) networkConsumerAdd(NETCONSUMER_TCP_SERVER, NETWORK_ANY, __FILE__, __LINE__); else @@ -485,7 +533,7 @@ void tcpServerUpdate() if ((settings.debugTcpServer || PERIODIC_DISPLAY(PD_TCP_SERVER_DATA)) && (!inMainMenu)) { PERIODIC_CLEAR(PD_TCP_SERVER_DATA); - systemPrintln("TCP server initiating shutdown"); + systemPrintf("%s initiating shutdown\r\n", tcpServerName); } // Network connection failed, attempt to restart the network @@ -510,7 +558,8 @@ void tcpServerUpdate() if (PERIODIC_DISPLAY(PD_TCP_SERVER_DATA) && (!inMainMenu)) { PERIODIC_CLEAR(PD_TCP_SERVER_DATA); - systemPrintf("TCP server client %d connected to %s\r\n", index, + systemPrintf("%s client %d connected to %s\r\n", + tcpServerName, index, tcpServerClientIpAddress[index].toString().c_str()); } } @@ -544,7 +593,8 @@ void tcpServerUpdate() if ((settings.debugTcpServer || PERIODIC_DISPLAY(PD_TCP_SERVER_DATA)) && (!inMainMenu)) { PERIODIC_CLEAR(PD_TCP_SERVER_DATA); - systemPrintf("TCP server client %d connected to %s\r\n", index, + systemPrintf("%s client %d connected to %s\r\n", + tcpServerName, index, tcpServerClientIpAddress[index].toString().c_str()); } @@ -614,7 +664,7 @@ void tcpServerUpdate() // Periodically display the TCP state if (PERIODIC_DISPLAY(PD_TCP_SERVER_STATE) && (!inMainMenu)) { - systemPrintf("TCP Server state: %s%s\r\n", + systemPrintf("%s state: %s%s\r\n", tcpServerName, tcpServerStateName[tcpServerState], line); PERIODIC_CLEAR(PD_TCP_SERVER_STATE); } From 94169c4b22ead76ecd07855c09efefc1be0fc6a6 Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Tue, 1 Jul 2025 05:08:40 -1000 Subject: [PATCH 04/17] TcpServer: Make port selection in tcpServerEnabled --- Firmware/RTK_Everywhere/TcpServer.ino | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Firmware/RTK_Everywhere/TcpServer.ino b/Firmware/RTK_Everywhere/TcpServer.ino index c5c9a569d..36281f153 100644 --- a/Firmware/RTK_Everywhere/TcpServer.ino +++ b/Firmware/RTK_Everywhere/TcpServer.ino @@ -92,6 +92,7 @@ const RtkMode_t tcpServerMode = RTK_MODE_ROVER // TCP server static NetworkServer *tcpServer = nullptr; +static uint16_t tcpServerPort; static uint8_t tcpServerState; static uint32_t tcpServerTimer; static const char * tcpServerName; @@ -172,6 +173,7 @@ bool tcpServerEnabled(const char ** line) { bool enabled; const char * name; + uint16_t port; do { @@ -190,6 +192,7 @@ bool tcpServerEnabled(const char ** line) { // TCP server running in Rover mode name = "TCP Server"; + port = settings.tcpServerPort; } // Determine if the base caster should be running @@ -200,10 +203,12 @@ bool tcpServerEnabled(const char ** line) if (settings.baseCasterOverride) { name = "Base Caster"; + port = 2101; } else { name = "NTRIP Caster"; + port = settings.tcpServerPort; } } @@ -219,10 +224,12 @@ bool tcpServerEnabled(const char ** line) { // Update the TCP server configuration tcpServerName = name; + tcpServerPort = port; } // Shutdown and restart the TCP server when configuration changes - else if (name != tcpServerName) + else if ((name != tcpServerName) + || (port != tcpServerPort)) { *line = ", Wrong state to switch configuration!"; break; @@ -331,12 +338,8 @@ bool tcpServerStart() if (settings.debugTcpServer && (!inMainMenu)) systemPrintf("%s starting the server\r\n", tcpServerName); - uint16_t tcpPort = settings.tcpServerPort; - if(settings.baseCasterOverride == true) - tcpPort = 2101; - // Start the TCP server - tcpServer = new NetworkServer(tcpPort, TCP_SERVER_MAX_CLIENTS); + tcpServer = new NetworkServer(tcpServerPort, TCP_SERVER_MAX_CLIENTS); if (!tcpServer) return false; @@ -345,7 +348,7 @@ bool tcpServerStart() localIp = networkGetIpAddress(); systemPrintf("%s online, IP address %s:%d\r\n", tcpServerName, - localIp.toString().c_str(), tcpPort); + localIp.toString().c_str(), tcpServerPort); return true; } From 9d8471edaffc59d94b0f6ffee8503ee8b060998f Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Tue, 1 Jul 2025 05:36:20 -1000 Subject: [PATCH 05/17] TcpServer: Determine operating mode in tcpServerEnabled --- Firmware/RTK_Everywhere/RTK_Everywhere.ino | 1 + Firmware/RTK_Everywhere/Tasks.ino | 2 +- Firmware/RTK_Everywhere/TcpServer.ino | 11 +++++++++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Firmware/RTK_Everywhere/RTK_Everywhere.ino b/Firmware/RTK_Everywhere/RTK_Everywhere.ino index e20767436..98af9be70 100644 --- a/Firmware/RTK_Everywhere/RTK_Everywhere.ino +++ b/Firmware/RTK_Everywhere/RTK_Everywhere.ino @@ -439,6 +439,7 @@ bool otaRequestFirmwareUpdate = false; bool enableRCFirmware; // Goes true from AP config page bool currentlyParsingData; // Goes true when we hit 750ms timeout with new data +bool tcpServerInCasterMode;// True when TCP server is running in caster mode // Give up connecting after this number of attempts // Connection attempts are throttled to increase the time between attempts diff --git a/Firmware/RTK_Everywhere/Tasks.ino b/Firmware/RTK_Everywhere/Tasks.ino index 9e330b2e8..14362fbad 100644 --- a/Firmware/RTK_Everywhere/Tasks.ino +++ b/Firmware/RTK_Everywhere/Tasks.ino @@ -754,7 +754,7 @@ void processUart1Message(SEMP_PARSE_STATE *parse, uint16_t type) // If BaseCasterOverride is enabled, remove everything but RTCM from the circular buffer // to avoid saturating the downstream radio link that is consuming over a TCP (NTRIP Caster) connection // Remove NMEA, etc after passing to the GNSS receiver library so that we still have SIV and other stats available - if (settings.baseCasterOverride == true) + if (tcpServerInCasterMode) { if (type != RTK_RTCM_PARSER_INDEX) { diff --git a/Firmware/RTK_Everywhere/TcpServer.ino b/Firmware/RTK_Everywhere/TcpServer.ino index 36281f153..053595008 100644 --- a/Firmware/RTK_Everywhere/TcpServer.ino +++ b/Firmware/RTK_Everywhere/TcpServer.ino @@ -171,6 +171,7 @@ int32_t tcpServerClientSendData(int index, uint8_t *data, uint16_t length) //---------------------------------------- bool tcpServerEnabled(const char ** line) { + bool casterMode; bool enabled; const char * name; uint16_t port; @@ -192,6 +193,7 @@ bool tcpServerEnabled(const char ** line) { // TCP server running in Rover mode name = "TCP Server"; + casterMode = false; port = settings.tcpServerPort; } @@ -199,6 +201,9 @@ bool tcpServerEnabled(const char ** line) else if (EQ_RTK_MODE(baseCasterMode) && (settings.enableNtripCaster || settings.baseCasterOverride)) { + // TCP server running in caster mode + casterMode = true; + // Select the base caster WiFi mode and port number if (settings.baseCasterOverride) { @@ -224,11 +229,13 @@ bool tcpServerEnabled(const char ** line) { // Update the TCP server configuration tcpServerName = name; + tcpServerInCasterMode = casterMode; tcpServerPort = port; } // Shutdown and restart the TCP server when configuration changes else if ((name != tcpServerName) + || (casterMode != tcpServerInCasterMode) || (port != tcpServerPort)) { *line = ", Wrong state to switch configuration!"; @@ -603,7 +610,7 @@ void tcpServerUpdate() // If we are acting as an NTRIP Caster, intercept the initial communication from the client // and respond accordingly - if (settings.enableNtripCaster || settings.baseCasterOverride) + if (tcpServerInCasterMode) { // Read response from client char response[512]; @@ -646,7 +653,7 @@ void tcpServerUpdate() if (settings.debugTcpServer) systemPrintf("Unknown response: %s\r\n", response); } - } // settings.enableNtripCaster == true || settings.baseCasterOverride == true + } // tcpServerInCasterMode // Make client online after any NTRIP injections so ring buffer can start outputting data to it tcpServerClientConnected = tcpServerClientConnected | (1 << index); From 54c0955690d86e2d71bc244c0b53faf1276f38d1 Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Tue, 1 Jul 2025 05:45:35 -1000 Subject: [PATCH 06/17] TcpServer: Select WiFi mode in tcpServerEnabled --- Firmware/RTK_Everywhere/TcpServer.ino | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Firmware/RTK_Everywhere/TcpServer.ino b/Firmware/RTK_Everywhere/TcpServer.ino index 053595008..29a65a639 100644 --- a/Firmware/RTK_Everywhere/TcpServer.ino +++ b/Firmware/RTK_Everywhere/TcpServer.ino @@ -95,6 +95,7 @@ static NetworkServer *tcpServer = nullptr; static uint16_t tcpServerPort; static uint8_t tcpServerState; static uint32_t tcpServerTimer; +static bool tcpServerWiFiSoftAp; static const char * tcpServerName; // TCP server clients @@ -175,6 +176,7 @@ bool tcpServerEnabled(const char ** line) bool enabled; const char * name; uint16_t port; + bool softAP; do { @@ -195,6 +197,7 @@ bool tcpServerEnabled(const char ** line) name = "TCP Server"; casterMode = false; port = settings.tcpServerPort; + softAP = false; } // Determine if the base caster should be running @@ -207,13 +210,17 @@ bool tcpServerEnabled(const char ** line) // Select the base caster WiFi mode and port number if (settings.baseCasterOverride) { + // Using soft AP name = "Base Caster"; port = 2101; + softAP = true; } else { name = "NTRIP Caster"; + // Using WiFi station port = settings.tcpServerPort; + softAP = false; } } @@ -231,12 +238,14 @@ bool tcpServerEnabled(const char ** line) tcpServerName = name; tcpServerInCasterMode = casterMode; tcpServerPort = port; + tcpServerWiFiSoftAp = softAP; } // Shutdown and restart the TCP server when configuration changes else if ((name != tcpServerName) || (casterMode != tcpServerInCasterMode) - || (port != tcpServerPort)) + || (port != tcpServerPort) + || (softAP != tcpServerWiFiSoftAp)) { *line = ", Wrong state to switch configuration!"; break; @@ -468,7 +477,8 @@ void tcpServerUpdate() // Shutdown the TCP server when the mode or setting changes DMW_st(tcpServerSetState, tcpServerState); - connected = networkConsumerIsConnected(NETCONSUMER_TCP_SERVER); + connected = networkConsumerIsConnected(NETCONSUMER_TCP_SERVER) + || (tcpServerWiFiSoftAp && wifiSoftApOnline); enabled = tcpServerEnabled(&line); if ((tcpServerState > TCP_SERVER_STATE_OFF) && !enabled) tcpServerStop(); @@ -517,7 +527,7 @@ void tcpServerUpdate() // Wait until the network is connected case TCP_SERVER_STATE_WAIT_FOR_NETWORK: // Wait until the network is connected to the media - if (connected || wifiSoftApOnline) + if (connected) { // Delay before starting the TCP server if ((millis() - tcpServerTimer) >= (1 * 1000)) @@ -538,7 +548,7 @@ void tcpServerUpdate() // Handle client connections and link failures case TCP_SERVER_STATE_RUNNING: // Determine if the network has failed - if ((connected == false && wifiSoftApOnline == false) || (!settings.enableTcpServer && !settings.baseCasterOverride)) + if (connected == false) { if ((settings.debugTcpServer || PERIODIC_DISPLAY(PD_TCP_SERVER_DATA)) && (!inMainMenu)) { From 5c3bd53b4dd4a1fbbf98f2fd21a12afa393544e9 Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Tue, 1 Jul 2025 06:18:55 -1000 Subject: [PATCH 07/17] TcpServer: Move the client handling into tcpServerClientUpdate --- Firmware/RTK_Everywhere/TcpServer.ino | 221 +++++++++++++------------- 1 file changed, 114 insertions(+), 107 deletions(-) diff --git a/Firmware/RTK_Everywhere/TcpServer.ino b/Firmware/RTK_Everywhere/TcpServer.ino index 29a65a639..aa23f5f3d 100644 --- a/Firmware/RTK_Everywhere/TcpServer.ino +++ b/Firmware/RTK_Everywhere/TcpServer.ino @@ -319,6 +319,119 @@ int32_t tcpServerSendData(uint16_t dataHead) // TCP Server Routines //---------------------------------------- +//---------------------------------------- +// Update the TCP server client state +//---------------------------------------- +void tcpServerClientUpdate(uint8_t index) +{ + bool clientConnected; + bool dataSent; + char response[512]; + int spot; + + // Determine if the client data structure is in use + if (tcpServerClientConnected & (1 << index)) + { + // Data structure in use + // Check for a working TCP server client connection + clientConnected = tcpServerClient[index]->connected(); + dataSent = ((millis() - tcpServerTimer) < TCP_SERVER_CLIENT_DATA_TIMEOUT) || + (tcpServerClientDataSent & (1 << index)); + if (clientConnected && dataSent) + { + // Display this client connection + if (PERIODIC_DISPLAY(PD_TCP_SERVER_DATA) && (!inMainMenu)) + { + PERIODIC_CLEAR(PD_TCP_SERVER_DATA); + systemPrintf("%s client %d connected to %s\r\n", + tcpServerName, index, + tcpServerClientIpAddress[index].toString().c_str()); + } + } + + // Shutdown the TCP server client link + else + tcpServerStopClient(index); + } + + // Determine if the client data structure is in use + if (!(tcpServerClientConnected & (1 << index))) + { + if(tcpServerClient[index] == nullptr) + tcpServerClient[index] = new NetworkClient; + + // Data structure not in use + // Check for another TCP server client + *tcpServerClient[index] = tcpServer->accept(); + + // Exit if no TCP server client found + if (*tcpServerClient[index]) + { + // TCP server client found + // Start processing the new TCP server client connection + tcpServerClientIpAddress[index] = tcpServerClient[index]->remoteIP(); + + if ((settings.debugTcpServer || PERIODIC_DISPLAY(PD_TCP_SERVER_DATA)) && (!inMainMenu)) + { + PERIODIC_CLEAR(PD_TCP_SERVER_DATA); + systemPrintf("%s client %d connected to %s\r\n", + tcpServerName, index, + tcpServerClientIpAddress[index].toString().c_str()); + } + + // If we are acting as an NTRIP Caster, intercept the initial communication from the client + // and respond accordingly + if (tcpServerInCasterMode) + { + // Read response from client + spot = 0; + while (tcpServerClient[index]->available()) + { + response[spot++] = tcpServerClient[index]->read(); + if (spot == sizeof(response)) + spot = 0; // Wrap + } + response[spot] = '\0'; // Terminate string + + if (strnstr(response, "GET / ", sizeof(response)) != NULL) // No mount point in header + { + if (settings.debugTcpServer) + systemPrintln("Mount point table requested."); + + // Respond with a single mountpoint + const char fakeSourceTable[] = + "SOURCETABLE 200 OK\r\nServer: SparkPNT Caster/1.0\r\nContent-Type: " + "text/plain\r\nContent-Length: 96\r\n\r\nSTR;SparkBase;none;RTCM " + "3.0;none;none;none;none;none;none;none;none;none;none;none;B;N;none;none"; + + tcpServerClient[index]->write(fakeSourceTable, strlen(fakeSourceTable)); + + tcpServerStopClient(index); // Disconnect from client + } + else if (strnstr(response, "GET /", sizeof(response)) != NULL) // Mount point in header + { + // NTRIP Client is sending us their mount point. Begin sending RTCM. + if (settings.debugTcpServer) + systemPrintln("NTRIP Client connected - Sending ICY 200 OK"); + + char confirmConnection[] = "ICY 200 OK\r\n"; + tcpServerClient[index]->write(confirmConnection, strlen(confirmConnection)); + } + else + { + // Unknown response + if (settings.debugTcpServer) + systemPrintf("Unknown response: %s\r\n", response); + } + } // tcpServerInCasterMode + + // Make client online after any NTRIP injections so ring buffer can start outputting data to it + tcpServerClientConnected = tcpServerClientConnected | (1 << index); + tcpServerClientDataSent = tcpServerClientDataSent | (1 << index); + } + } +} + //---------------------------------------- // Update the state of the TCP server state machine //---------------------------------------- @@ -467,9 +580,7 @@ void tcpServerStopClient(int index) //---------------------------------------- void tcpServerUpdate() { - bool clientConnected; bool connected; - bool dataSent; bool enabled; int index; IPAddress ipAddress; @@ -564,111 +675,7 @@ void tcpServerUpdate() // Walk the list of TCP server clients for (index = 0; index < TCP_SERVER_MAX_CLIENTS; index++) { - // Determine if the client data structure is in use - if (tcpServerClientConnected & (1 << index)) - { - // Data structure in use - // Check for a working TCP server client connection - clientConnected = tcpServerClient[index]->connected(); - dataSent = ((millis() - tcpServerTimer) < TCP_SERVER_CLIENT_DATA_TIMEOUT) || - (tcpServerClientDataSent & (1 << index)); - if (clientConnected && dataSent) - { - // Display this client connection - if (PERIODIC_DISPLAY(PD_TCP_SERVER_DATA) && (!inMainMenu)) - { - PERIODIC_CLEAR(PD_TCP_SERVER_DATA); - systemPrintf("%s client %d connected to %s\r\n", - tcpServerName, index, - tcpServerClientIpAddress[index].toString().c_str()); - } - } - - // Shutdown the TCP server client link - else - tcpServerStopClient(index); - } - } - - // Walk the list of TCP server clients - for (index = 0; index < TCP_SERVER_MAX_CLIENTS; index++) - { - // Determine if the client data structure is in use - if (!(tcpServerClientConnected & (1 << index))) - { - if(tcpServerClient[index] == nullptr) - tcpServerClient[index] = new NetworkClient; - - // Data structure not in use - // Check for another TCP server client - *tcpServerClient[index] = tcpServer->accept(); - - // Exit if no TCP server client found - if (! *tcpServerClient[index]) - break; - - // Start processing the new TCP server client connection - tcpServerClientIpAddress[index] = tcpServerClient[index]->remoteIP(); - - if ((settings.debugTcpServer || PERIODIC_DISPLAY(PD_TCP_SERVER_DATA)) && (!inMainMenu)) - { - PERIODIC_CLEAR(PD_TCP_SERVER_DATA); - systemPrintf("%s client %d connected to %s\r\n", - tcpServerName, index, - tcpServerClientIpAddress[index].toString().c_str()); - } - - // If we are acting as an NTRIP Caster, intercept the initial communication from the client - // and respond accordingly - if (tcpServerInCasterMode) - { - // Read response from client - char response[512]; - int spot = 0; - while (tcpServerClient[index]->available()) - { - response[spot++] = tcpServerClient[index]->read(); - if (spot == sizeof(response)) - spot = 0; // Wrap - } - response[spot] = '\0'; // Terminate string - - if (strnstr(response, "GET / ", sizeof(response)) != NULL) // No mount point in header - { - if (settings.debugTcpServer) - systemPrintln("Mount point table requested."); - - // Respond with a single mountpoint - const char fakeSourceTable[] = - "SOURCETABLE 200 OK\r\nServer: SparkPNT Caster/1.0\r\nContent-Type: " - "text/plain\r\nContent-Length: 96\r\n\r\nSTR;SparkBase;none;RTCM " - "3.0;none;none;none;none;none;none;none;none;none;none;none;B;N;none;none"; - - tcpServerClient[index]->write(fakeSourceTable, strlen(fakeSourceTable)); - - tcpServerStopClient(index); // Disconnect from client - } - else if (strnstr(response, "GET /", sizeof(response)) != NULL) // Mount point in header - { - // NTRIP Client is sending us their mount point. Begin sending RTCM. - if (settings.debugTcpServer) - systemPrintln("NTRIP Client connected - Sending ICY 200 OK"); - - char confirmConnection[] = "ICY 200 OK\r\n"; - tcpServerClient[index]->write(confirmConnection, strlen(confirmConnection)); - } - else - { - // Unknown response - if (settings.debugTcpServer) - systemPrintf("Unknown response: %s\r\n", response); - } - } // tcpServerInCasterMode - - // Make client online after any NTRIP injections so ring buffer can start outputting data to it - tcpServerClientConnected = tcpServerClientConnected | (1 << index); - tcpServerClientDataSent = tcpServerClientDataSent | (1 << index); - } + tcpServerClientUpdate(index); } // Check for data moving across the connections From 09166543d383d0758a9cc4dbe716ce7ede4b3824 Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Tue, 1 Jul 2025 06:56:40 -1000 Subject: [PATCH 08/17] TcpServer: Rework the client connection logic --- Firmware/RTK_Everywhere/TcpServer.ino | 34 ++++++++++++++------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/Firmware/RTK_Everywhere/TcpServer.ino b/Firmware/RTK_Everywhere/TcpServer.ino index aa23f5f3d..4a40a79de 100644 --- a/Firmware/RTK_Everywhere/TcpServer.ino +++ b/Firmware/RTK_Everywhere/TcpServer.ino @@ -330,32 +330,33 @@ void tcpServerClientUpdate(uint8_t index) int spot; // Determine if the client data structure is in use - if (tcpServerClientConnected & (1 << index)) + while (tcpServerClientConnected & (1 << index)) { - // Data structure in use + // The client data structure is in use // Check for a working TCP server client connection clientConnected = tcpServerClient[index]->connected(); dataSent = ((millis() - tcpServerTimer) < TCP_SERVER_CLIENT_DATA_TIMEOUT) || (tcpServerClientDataSent & (1 << index)); - if (clientConnected && dataSent) + if ((clientConnected && dataSent) == false) { - // Display this client connection - if (PERIODIC_DISPLAY(PD_TCP_SERVER_DATA) && (!inMainMenu)) - { - PERIODIC_CLEAR(PD_TCP_SERVER_DATA); - systemPrintf("%s client %d connected to %s\r\n", - tcpServerName, index, - tcpServerClientIpAddress[index].toString().c_str()); - } + // Broken connection, shutdown the TCP server client link + tcpServerStopClient(index); + break; } - // Shutdown the TCP server client link - else - tcpServerStopClient(index); + // Periodically display this client connection + if (PERIODIC_DISPLAY(PD_TCP_SERVER_DATA) && (!inMainMenu)) + { + PERIODIC_CLEAR(PD_TCP_SERVER_DATA); + systemPrintf("%s client %d connected to %s\r\n", + tcpServerName, index, + tcpServerClientIpAddress[index].toString().c_str()); + } + break; } - // Determine if the client data structure is in use - if (!(tcpServerClientConnected & (1 << index))) + // Determine if the client data structure is not in use + while ((tcpServerClientConnected & (1 << index)) == 0) { if(tcpServerClient[index] == nullptr) tcpServerClient[index] = new NetworkClient; @@ -429,6 +430,7 @@ void tcpServerClientUpdate(uint8_t index) tcpServerClientConnected = tcpServerClientConnected | (1 << index); tcpServerClientDataSent = tcpServerClientDataSent | (1 << index); } + break; } } From 72fb0bb82cc0a5e165dcef3ce3b82cb2ba8841ee Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Tue, 1 Jul 2025 07:08:26 -1000 Subject: [PATCH 09/17] TcpServer: Periodically display all of the client connections --- Firmware/RTK_Everywhere/TcpServer.ino | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/Firmware/RTK_Everywhere/TcpServer.ino b/Firmware/RTK_Everywhere/TcpServer.ino index 4a40a79de..e07e69bdb 100644 --- a/Firmware/RTK_Everywhere/TcpServer.ino +++ b/Firmware/RTK_Everywhere/TcpServer.ino @@ -346,12 +346,9 @@ void tcpServerClientUpdate(uint8_t index) // Periodically display this client connection if (PERIODIC_DISPLAY(PD_TCP_SERVER_DATA) && (!inMainMenu)) - { - PERIODIC_CLEAR(PD_TCP_SERVER_DATA); systemPrintf("%s client %d connected to %s\r\n", tcpServerName, index, tcpServerClientIpAddress[index].toString().c_str()); - } break; } @@ -371,14 +368,10 @@ void tcpServerClientUpdate(uint8_t index) // TCP server client found // Start processing the new TCP server client connection tcpServerClientIpAddress[index] = tcpServerClient[index]->remoteIP(); - if ((settings.debugTcpServer || PERIODIC_DISPLAY(PD_TCP_SERVER_DATA)) && (!inMainMenu)) - { - PERIODIC_CLEAR(PD_TCP_SERVER_DATA); systemPrintf("%s client %d connected to %s\r\n", tcpServerName, index, tcpServerClientIpAddress[index].toString().c_str()); - } // If we are acting as an NTRIP Caster, intercept the initial communication from the client // and respond accordingly @@ -550,8 +543,6 @@ void tcpServerStopClient(int index) // Done with this client connection if ((settings.debugTcpServer || PERIODIC_DISPLAY(PD_TCP_SERVER_DATA)) && (!inMainMenu)) { - PERIODIC_CLEAR(PD_TCP_SERVER_DATA); - // Determine the shutdown reason connected = tcpServerClient[index]->connected() && (!(tcpServerClientWriteError & (1 << index))); @@ -664,21 +655,19 @@ void tcpServerUpdate() if (connected == false) { if ((settings.debugTcpServer || PERIODIC_DISPLAY(PD_TCP_SERVER_DATA)) && (!inMainMenu)) - { - PERIODIC_CLEAR(PD_TCP_SERVER_DATA); systemPrintf("%s initiating shutdown\r\n", tcpServerName); - } // Network connection failed, attempt to restart the network tcpServerStop(); + if (PERIODIC_DISPLAY(PD_TCP_SERVER_DATA)) + PERIODIC_CLEAR(PD_TCP_SERVER_DATA); break; } // Walk the list of TCP server clients for (index = 0; index < TCP_SERVER_MAX_CLIENTS; index++) - { tcpServerClientUpdate(index); - } + PERIODIC_CLEAR(PD_TCP_SERVER_DATA); // Check for data moving across the connections if ((millis() - tcpServerTimer) >= TCP_SERVER_CLIENT_DATA_TIMEOUT) From b2963d5be5bd522d7c7470c8cfef06d0818e8c91 Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Tue, 1 Jul 2025 07:29:42 -1000 Subject: [PATCH 10/17] TcpServer: Separate the tcpServerTimer from the tcpServerClientTimers --- Firmware/RTK_Everywhere/TcpServer.ino | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Firmware/RTK_Everywhere/TcpServer.ino b/Firmware/RTK_Everywhere/TcpServer.ino index e07e69bdb..cfb85df61 100644 --- a/Firmware/RTK_Everywhere/TcpServer.ino +++ b/Firmware/RTK_Everywhere/TcpServer.ino @@ -101,6 +101,7 @@ static const char * tcpServerName; // TCP server clients static volatile uint8_t tcpServerClientConnected; static volatile uint8_t tcpServerClientDataSent; +static uint32_t tcpServerClientTimer[TCP_SERVER_MAX_CLIENTS]; static volatile uint8_t tcpServerClientWriteError; static NetworkClient *tcpServerClient[TCP_SERVER_MAX_CLIENTS]; static IPAddress tcpServerClientIpAddress[TCP_SERVER_MAX_CLIENTS]; @@ -335,8 +336,8 @@ void tcpServerClientUpdate(uint8_t index) // The client data structure is in use // Check for a working TCP server client connection clientConnected = tcpServerClient[index]->connected(); - dataSent = ((millis() - tcpServerTimer) < TCP_SERVER_CLIENT_DATA_TIMEOUT) || - (tcpServerClientDataSent & (1 << index)); + dataSent = ((millis() - tcpServerClientTimer[index]) < TCP_SERVER_CLIENT_DATA_TIMEOUT) + || (tcpServerClientDataSent & (1 << index)); if ((clientConnected && dataSent) == false) { // Broken connection, shutdown the TCP server client link @@ -419,6 +420,9 @@ void tcpServerClientUpdate(uint8_t index) } } // tcpServerInCasterMode + // Start the data timer + tcpServerClientTimer[index] = millis(); + // Make client online after any NTRIP injections so ring buffer can start outputting data to it tcpServerClientConnected = tcpServerClientConnected | (1 << index); tcpServerClientDataSent = tcpServerClientDataSent | (1 << index); @@ -546,7 +550,7 @@ void tcpServerStopClient(int index) // Determine the shutdown reason connected = tcpServerClient[index]->connected() && (!(tcpServerClientWriteError & (1 << index))); - dataSent = ((millis() - tcpServerTimer) < TCP_SERVER_CLIENT_DATA_TIMEOUT) + dataSent = ((millis() - tcpServerClientTimer[index]) < TCP_SERVER_CLIENT_DATA_TIMEOUT) || (tcpServerClientDataSent & (1 << index)); if (!dataSent) systemPrintf("%s: No data sent over %d seconds\r\n", From b7285c38282be3313645eba99933c415bd521c8f Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Tue, 1 Jul 2025 07:55:59 -1000 Subject: [PATCH 11/17] TcpServer: Use client states to wait for and process caster request --- Firmware/RTK_Everywhere/TcpServer.ino | 181 +++++++++++++++++--------- 1 file changed, 121 insertions(+), 60 deletions(-) diff --git a/Firmware/RTK_Everywhere/TcpServer.ino b/Firmware/RTK_Everywhere/TcpServer.ino index cfb85df61..6ca67b7d9 100644 --- a/Firmware/RTK_Everywhere/TcpServer.ino +++ b/Firmware/RTK_Everywhere/TcpServer.ino @@ -79,9 +79,27 @@ const char *const tcpServerStateName[] = { "TCP_SERVER_STATE_WAIT_FOR_NETWORK", "TCP_SERVER_STATE_RUNNING", }; - const int tcpServerStateNameEntries = sizeof(tcpServerStateName) / sizeof(tcpServerStateName[0]); +// Define the TCP server client states +enum tcpServerClientStates +{ + TCP_SERVER_CLIENT_OFF = 0, + TCP_SERVER_CLIENT_WAIT_REQUEST, + TCP_SERVER_CLIENT_GET_REQUEST, + TCP_SERVER_CLIENT_SENDING_DATA, + // Insert new states here + TCP_SERVER_CLIENT_MAX // Last entry in the state list +}; + +const char *const tcpServerClientStateName[] = { + "TCP_SERVER_CLIENT_OFF", + "TCP_SERVER_CLIENT_WAIT_REQUEST", + "TCP_SERVER_CLIENT_GET_REQUEST", + "TCP_SERVER_CLIENT_SENDING_DATA", +}; +const int tcpServerClientStateNameEntries = sizeof(tcpServerClientStateName) / sizeof(tcpServerClientStateName[0]); + const RtkMode_t baseCasterMode = RTK_MODE_BASE_FIXED; const RtkMode_t tcpServerMode = RTK_MODE_ROVER | RTK_MODE_BASE_SURVEY_IN; @@ -105,6 +123,7 @@ static uint32_t tcpServerClientTimer[TCP_SERVER_MAX_CLIENTS]; static volatile uint8_t tcpServerClientWriteError; static NetworkClient *tcpServerClient[TCP_SERVER_MAX_CLIENTS]; static IPAddress tcpServerClientIpAddress[TCP_SERVER_MAX_CLIENTS]; +static uint8_t tcpServerClientState[TCP_SERVER_MAX_CLIENTS]; static volatile RING_BUFFER_OFFSET tcpServerClientTails[TCP_SERVER_MAX_CLIENTS]; //---------------------------------------- @@ -350,83 +369,119 @@ void tcpServerClientUpdate(uint8_t index) systemPrintf("%s client %d connected to %s\r\n", tcpServerName, index, tcpServerClientIpAddress[index].toString().c_str()); + + // Process the client state + switch (tcpServerClientState[index]) + { + // Wait until the request is received from the NTRIP client + case TCP_SERVER_CLIENT_WAIT_REQUEST: + if (tcpServerClient[index]->available()) + { + // Indicate that data was received + tcpServerClientDataSent = tcpServerClientDataSent | (1 << index); + tcpServerClientTimer[index] = millis(); + tcpServerClientState[index] = TCP_SERVER_CLIENT_GET_REQUEST; + } + break; + + // Process the request from the NTRIP client + case TCP_SERVER_CLIENT_GET_REQUEST: + // Read response from client + spot = 0; + while (tcpServerClient[index]->available()) + { + response[spot++] = tcpServerClient[index]->read(); + if (spot == sizeof(response)) + spot = 0; // Wrap + } + response[spot] = '\0'; // Terminate string + + // Handle the mount point table request + if (strnstr(response, "GET / ", sizeof(response)) != NULL) // No mount point in header + { + if (settings.debugTcpServer) + systemPrintln("Mount point table requested."); + + // Respond with a single mountpoint + const char fakeSourceTable[] = + "SOURCETABLE 200 OK\r\nServer: SparkPNT Caster/1.0\r\nContent-Type: " + "text/plain\r\nContent-Length: 96\r\n\r\nSTR;SparkBase;none;RTCM " + "3.0;none;none;none;none;none;none;none;none;none;none;none;B;N;none;none"; + + tcpServerClient[index]->write(fakeSourceTable, strlen(fakeSourceTable)); + + // Disconnect from client + tcpServerStopClient(index); + } + + // Check for unknown request + else if (strnstr(response, "GET /", sizeof(response)) == NULL) + { + // Unknown response + if (settings.debugTcpServer) + systemPrintf("Unknown response: %s\r\n", response); + + // Disconnect from client + tcpServerStopClient(index); + } + + // Handle the mount point request, ignore the mount point and start sending data + else + { + // NTRIP Client is sending us their mount point. Begin sending RTCM. + if (settings.debugTcpServer) + systemPrintln("NTRIP Client connected - Sending ICY 200 OK"); + + // Successfully connected to the mount point + char confirmConnection[] = "ICY 200 OK\r\n"; + tcpServerClient[index]->write(confirmConnection, strlen(confirmConnection)); + + // Start sending RTCM + tcpServerClientState[index] = TCP_SERVER_CLIENT_SENDING_DATA; + } + break; + + case TCP_SERVER_CLIENT_SENDING_DATA: + break; + } break; } // Determine if the client data structure is not in use while ((tcpServerClientConnected & (1 << index)) == 0) { + // Data structure not in use if(tcpServerClient[index] == nullptr) tcpServerClient[index] = new NetworkClient; - // Data structure not in use // Check for another TCP server client *tcpServerClient[index] = tcpServer->accept(); // Exit if no TCP server client found - if (*tcpServerClient[index]) - { - // TCP server client found - // Start processing the new TCP server client connection - tcpServerClientIpAddress[index] = tcpServerClient[index]->remoteIP(); - if ((settings.debugTcpServer || PERIODIC_DISPLAY(PD_TCP_SERVER_DATA)) && (!inMainMenu)) - systemPrintf("%s client %d connected to %s\r\n", - tcpServerName, index, - tcpServerClientIpAddress[index].toString().c_str()); - - // If we are acting as an NTRIP Caster, intercept the initial communication from the client - // and respond accordingly - if (tcpServerInCasterMode) - { - // Read response from client - spot = 0; - while (tcpServerClient[index]->available()) - { - response[spot++] = tcpServerClient[index]->read(); - if (spot == sizeof(response)) - spot = 0; // Wrap - } - response[spot] = '\0'; // Terminate string - - if (strnstr(response, "GET / ", sizeof(response)) != NULL) // No mount point in header - { - if (settings.debugTcpServer) - systemPrintln("Mount point table requested."); - - // Respond with a single mountpoint - const char fakeSourceTable[] = - "SOURCETABLE 200 OK\r\nServer: SparkPNT Caster/1.0\r\nContent-Type: " - "text/plain\r\nContent-Length: 96\r\n\r\nSTR;SparkBase;none;RTCM " - "3.0;none;none;none;none;none;none;none;none;none;none;none;B;N;none;none"; - - tcpServerClient[index]->write(fakeSourceTable, strlen(fakeSourceTable)); + if (!*tcpServerClient[index]) + break; - tcpServerStopClient(index); // Disconnect from client - } - else if (strnstr(response, "GET /", sizeof(response)) != NULL) // Mount point in header - { - // NTRIP Client is sending us their mount point. Begin sending RTCM. - if (settings.debugTcpServer) - systemPrintln("NTRIP Client connected - Sending ICY 200 OK"); + // TCP server client found + // Start processing the new TCP server client connection + tcpServerClientIpAddress[index] = tcpServerClient[index]->remoteIP(); + if ((settings.debugTcpServer || PERIODIC_DISPLAY(PD_TCP_SERVER_DATA)) && (!inMainMenu)) + systemPrintf("%s client %d connected to %s\r\n", + tcpServerName, index, + tcpServerClientIpAddress[index].toString().c_str()); - char confirmConnection[] = "ICY 200 OK\r\n"; - tcpServerClient[index]->write(confirmConnection, strlen(confirmConnection)); - } - else - { - // Unknown response - if (settings.debugTcpServer) - systemPrintf("Unknown response: %s\r\n", response); - } - } // tcpServerInCasterMode + // Mark this client as connected + tcpServerClientConnected = tcpServerClientConnected | (1 << index); - // Start the data timer - tcpServerClientTimer[index] = millis(); + // Start the data timer + tcpServerClientTimer[index] = millis(); + tcpServerClientDataSent = tcpServerClientDataSent | (1 << index); + // Set the client state + if (tcpServerInCasterMode) + tcpServerClientState[index] = TCP_SERVER_CLIENT_WAIT_REQUEST; + else // Make client online after any NTRIP injections so ring buffer can start outputting data to it - tcpServerClientConnected = tcpServerClientConnected | (1 << index); - tcpServerClientDataSent = tcpServerClientDataSent | (1 << index); - } + tcpServerClientState[index] = TCP_SERVER_CLIENT_SENDING_DATA; break; } } @@ -647,6 +702,8 @@ void tcpServerUpdate() if (tcpServerStart()) { networkUserAdd(NETCONSUMER_TCP_SERVER, __FILE__, __LINE__); + for (index = 0; index < TCP_SERVER_MAX_CLIENTS; index++) + tcpServerClientState[index] = TCP_SERVER_CLIENT_OFF; tcpServerSetState(TCP_SERVER_STATE_RUNNING); } } @@ -707,8 +764,12 @@ void tcpServerValidateTables() sizeof(tcpServerClientConnected) * 8); reportFatalError(line); } + + // Verify the state name tables if (tcpServerStateNameEntries != TCP_SERVER_STATE_MAX) reportFatalError("Fix tcpServerStateNameEntries to match tcpServerStates"); + if (tcpServerClientStateNameEntries != TCP_SERVER_CLIENT_MAX) + reportFatalError("Fix tcpServerClientStateNameEntries to match tcpServerClientStates"); } //---------------------------------------- From eb888e4f74f1cf0f3ae223c4277754e903e0cff7 Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Tue, 1 Jul 2025 08:03:27 -1000 Subject: [PATCH 12/17] TcpServer: Handle client allocation failures --- Firmware/RTK_Everywhere/TcpServer.ino | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Firmware/RTK_Everywhere/TcpServer.ino b/Firmware/RTK_Everywhere/TcpServer.ino index 6ca67b7d9..147279c22 100644 --- a/Firmware/RTK_Everywhere/TcpServer.ino +++ b/Firmware/RTK_Everywhere/TcpServer.ino @@ -452,18 +452,29 @@ void tcpServerClientUpdate(uint8_t index) { // Data structure not in use if(tcpServerClient[index] == nullptr) + { tcpServerClient[index] = new NetworkClient; - // Check for another TCP server client + // Check for allocation failure + if(tcpServerClient[index] == nullptr) + { + if (settings.debugTcpServer) + Serial.printf("ERROR: Failed to allocate %s client!\r\n", tcpServerName); + break; + } + } + + // Check for another incoming TCP server client connection request *tcpServerClient[index] = tcpServer->accept(); // Exit if no TCP server client found if (!*tcpServerClient[index]) break; - // TCP server client found - // Start processing the new TCP server client connection + // Get the remote IP address tcpServerClientIpAddress[index] = tcpServerClient[index]->remoteIP(); + + // Display the connection if ((settings.debugTcpServer || PERIODIC_DISPLAY(PD_TCP_SERVER_DATA)) && (!inMainMenu)) systemPrintf("%s client %d connected to %s\r\n", tcpServerName, index, From 4272cf5954c863e384f2e1aee350ec663b8603cc Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Tue, 1 Jul 2025 08:09:00 -1000 Subject: [PATCH 13/17] TcpServer: Don't send data until the client enters *_CLIENT_SENDING_DATA --- Firmware/RTK_Everywhere/TcpServer.ino | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Firmware/RTK_Everywhere/TcpServer.ino b/Firmware/RTK_Everywhere/TcpServer.ino index 147279c22..a85fa5128 100644 --- a/Firmware/RTK_Everywhere/TcpServer.ino +++ b/Firmware/RTK_Everywhere/TcpServer.ino @@ -119,6 +119,7 @@ static const char * tcpServerName; // TCP server clients static volatile uint8_t tcpServerClientConnected; static volatile uint8_t tcpServerClientDataSent; +static volatile uint8_t tcpServerClientSendingData; static uint32_t tcpServerClientTimer[TCP_SERVER_MAX_CLIENTS]; static volatile uint8_t tcpServerClientWriteError; static NetworkClient *tcpServerClient[TCP_SERVER_MAX_CLIENTS]; @@ -295,7 +296,7 @@ int32_t tcpServerSendData(uint16_t dataHead) tail = tcpServerClientTails[index]; // Determine if the client is connected - if (!(tcpServerClientConnected & (1 << index))) + if ((tcpServerClientSendingData & (1 << index)) == 0) tail = dataHead; else { @@ -437,6 +438,7 @@ void tcpServerClientUpdate(uint8_t index) tcpServerClient[index]->write(confirmConnection, strlen(confirmConnection)); // Start sending RTCM + tcpServerClientSendingData = tcpServerClientSendingData | (1 << index); tcpServerClientState[index] = TCP_SERVER_CLIENT_SENDING_DATA; } break; @@ -491,8 +493,11 @@ void tcpServerClientUpdate(uint8_t index) if (tcpServerInCasterMode) tcpServerClientState[index] = TCP_SERVER_CLIENT_WAIT_REQUEST; else + { // Make client online after any NTRIP injections so ring buffer can start outputting data to it + tcpServerClientSendingData = tcpServerClientSendingData | (1 << index); tcpServerClientState[index] = TCP_SERVER_CLIENT_SENDING_DATA; + } break; } } @@ -607,6 +612,9 @@ void tcpServerStopClient(int index) bool connected; bool dataSent; + // Stop sending data + tcpServerClientSendingData = tcpServerClientSendingData & ~(1 << index); + // Determine if a client was allocated if (tcpServerClient[index]) { From a40d15596159e7ea91c23add144f61bbab53277c Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Sun, 6 Jul 2025 10:03:57 -1000 Subject: [PATCH 14/17] TcpServer: Support building with COMPILE_NETWORK compiled out --- Firmware/RTK_Everywhere/Developer.ino | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Firmware/RTK_Everywhere/Developer.ino b/Firmware/RTK_Everywhere/Developer.ino index a60ea93e2..516f21f5b 100644 --- a/Firmware/RTK_Everywhere/Developer.ino +++ b/Firmware/RTK_Everywhere/Developer.ino @@ -171,6 +171,8 @@ bool webServerStart(int httpPort = 80) bool parseIncomingSettings() {return false;} void sendStringToWebsocket(const char* stringToSend) {} void stopWebServer() {} +bool webServerSettingsCheckAndFree() {return false;} +void webServerSettingsClone() {} void webServerStop() {} void webServerUpdate() {} void webServerVerifyTables() {} From 9bbf0c884171eebb4c4e9d4f973bb9b75da521ab Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Tue, 29 Jul 2025 08:11:19 -1000 Subject: [PATCH 15/17] TcpServer: Rename settings.tcpUdpOverWiFiStation to tcpOverWiFiStation --- Firmware/RTK_Everywhere/AP-Config/index.html | 6 +++--- Firmware/RTK_Everywhere/Network.ino | 8 ++++---- Firmware/RTK_Everywhere/TcpServer.ino | 2 +- Firmware/RTK_Everywhere/menuCommands.ino | 6 +++--- Firmware/RTK_Everywhere/settings.h | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Firmware/RTK_Everywhere/AP-Config/index.html b/Firmware/RTK_Everywhere/AP-Config/index.html index 4127078fc..3430338da 100644 --- a/Firmware/RTK_Everywhere/AP-Config/index.html +++ b/Firmware/RTK_Everywhere/AP-Config/index.html @@ -1678,9 +1678,9 @@

-
- - diff --git a/Firmware/RTK_Everywhere/Network.ino b/Firmware/RTK_Everywhere/Network.ino index e15235dc9..fcf05c6ba 100644 --- a/Firmware/RTK_Everywhere/Network.ino +++ b/Firmware/RTK_Everywhere/Network.ino @@ -226,8 +226,8 @@ void menuTcpUdp() if (settings.mdnsEnable) systemPrintf("n) MDNS host name: %s\r\n", settings.mdnsHostName); - systemPrint("a) Broadcast TCP/UDP Server packets over local WiFi or act as Access Point: "); - systemPrintf("%s\r\n", settings.tcpUdpOverWiFiStation ? "WiFi" : "AP"); + systemPrint("t) Broadcast TCP/UDP Server packets over local WiFi or act as Access Point: "); + systemPrintf("%s\r\n", settings.tcpOverWiFiStation ? "WiFi" : "AP"); systemPrint("u) Broadcast UDP Server packets over local WiFi or act as Access Point: "); systemPrintf("%s\r\n", settings.udpOverWiFiStation ? "WiFi" : "AP"); @@ -326,9 +326,9 @@ void menuTcpUdp() getUserInputString((char *)&settings.mdnsHostName, sizeof(settings.mdnsHostName)); } - else if (incoming == 'a') + else if (incoming == 't') { - settings.tcpUdpOverWiFiStation ^= 1; + settings.tcpOverWiFiStation ^= 1; wifiUpdateSettings(); } diff --git a/Firmware/RTK_Everywhere/TcpServer.ino b/Firmware/RTK_Everywhere/TcpServer.ino index a85fa5128..38afa5b3c 100644 --- a/Firmware/RTK_Everywhere/TcpServer.ino +++ b/Firmware/RTK_Everywhere/TcpServer.ino @@ -698,7 +698,7 @@ void tcpServerUpdate() { if (settings.debugTcpServer && (!inMainMenu)) systemPrintf("%s start/r/n", tcpServerName); - if (settings.tcpUdpOverWiFiStation == true) + if (settings.tcpOverWiFiStation == true) networkConsumerAdd(NETCONSUMER_TCP_SERVER, NETWORK_ANY, __FILE__, __LINE__); else networkSoftApConsumerAdd(NETCONSUMER_TCP_SERVER, __FILE__, __LINE__); diff --git a/Firmware/RTK_Everywhere/menuCommands.ino b/Firmware/RTK_Everywhere/menuCommands.ino index f7927d72f..bc5422349 100644 --- a/Firmware/RTK_Everywhere/menuCommands.ino +++ b/Firmware/RTK_Everywhere/menuCommands.ino @@ -1968,10 +1968,10 @@ void createSettingsString(char *newSettings) else stringRecord(newSettings, "wifiConfigOverAP", 0); // 1 = AP mode, 0 = WiFi - if (settings.tcpUdpOverWiFiStation == true) - stringRecord(newSettings, "tcpUdpOverWiFiStation", 1); // 1 = WiFi mode, 0 = AP + if (settings.tcpOverWiFiStation == true) + stringRecord(newSettings, "tcpOverWiFiStation", 1); // 1 = WiFi mode, 0 = AP else - stringRecord(newSettings, "tcpUdpOverWiFiStation", 0); // 1 = WiFi mode, 0 = AP + stringRecord(newSettings, "tcpOverWiFiStation", 0); // 1 = WiFi mode, 0 = AP if (settings.udpOverWiFiStation == true) stringRecord(newSettings, "udpOverWiFiStation", 1); // 1 = WiFi mode, 0 = AP diff --git a/Firmware/RTK_Everywhere/settings.h b/Firmware/RTK_Everywhere/settings.h index d0d1ee180..623e44244 100644 --- a/Firmware/RTK_Everywhere/settings.h +++ b/Firmware/RTK_Everywhere/settings.h @@ -938,7 +938,7 @@ struct Settings bool debugTcpServer = false; bool enableTcpServer = false; uint16_t tcpServerPort = 2948; // TCP server port, 2948 is GPS Daemon: http://tcp-udp-ports.com/port-2948.htm - bool tcpUdpOverWiFiStation = true; // Controls if TCP/UDP settings should use Station or AP + bool tcpOverWiFiStation = true; // Should TCP server use Station (true) or AP (false) bool udpOverWiFiStation = true; // Should UDP server use Station (true) or AP (false) // Time Zone - Default to UTC @@ -1586,7 +1586,7 @@ const RTK_Settings_Entry rtkSettingsEntries[] = { 0, 0, 0, 1, 1, 1, 1, 1, 1, _bool, 0, & settings.debugTcpServer, "debugTcpServer", }, { 1, 1, 0, 1, 1, 1, 1, 1, 1, _bool, 0, & settings.enableTcpServer, "enableTcpServer", }, { 1, 1, 0, 1, 1, 1, 1, 1, 1, _uint16_t, 0, & settings.tcpServerPort, "tcpServerPort", }, - { 1, 1, 0, 1, 1, 1, 1, 1, 1, _bool, 0, & settings.tcpUdpOverWiFiStation, "tcpUdpOverWiFiStation", }, + { 1, 1, 0, 1, 1, 1, 1, 1, 1, _bool, 0, & settings.tcpOverWiFiStation, "tcpOverWiFiStation", }, // Time Zone { 0, 1, 0, 1, 1, 1, 1, 1, 1, _int8_t, 0, & settings.timeZoneHours, "timeZoneHours", }, From 2b2921e771c895d321468d30cfb851717cb93524 Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Tue, 29 Jul 2025 08:33:46 -1000 Subject: [PATCH 16/17] TcpServer: Fix use of baseCasterOverride and tcpOverWiFiStation --- Firmware/RTK_Everywhere/TcpServer.ino | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Firmware/RTK_Everywhere/TcpServer.ino b/Firmware/RTK_Everywhere/TcpServer.ino index 38afa5b3c..cbbf532dd 100644 --- a/Firmware/RTK_Everywhere/TcpServer.ino +++ b/Firmware/RTK_Everywhere/TcpServer.ino @@ -229,7 +229,7 @@ bool tcpServerEnabled(const char ** line) casterMode = true; // Select the base caster WiFi mode and port number - if (settings.baseCasterOverride) + if (settings.baseCasterOverride || (settings.tcpOverWiFiStation == false)) { // Using soft AP name = "Base Caster"; @@ -698,10 +698,10 @@ void tcpServerUpdate() { if (settings.debugTcpServer && (!inMainMenu)) systemPrintf("%s start/r/n", tcpServerName); - if (settings.tcpOverWiFiStation == true) - networkConsumerAdd(NETCONSUMER_TCP_SERVER, NETWORK_ANY, __FILE__, __LINE__); - else + if (tcpServerWiFiSoftAp) networkSoftApConsumerAdd(NETCONSUMER_TCP_SERVER, __FILE__, __LINE__); + else + networkConsumerAdd(NETCONSUMER_TCP_SERVER, NETWORK_ANY, __FILE__, __LINE__); tcpServerSetState(TCP_SERVER_STATE_WAIT_FOR_NETWORK); } break; From 710f3003372049fa4582239e2c539b5153915e6a Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Tue, 29 Jul 2025 08:12:00 -1000 Subject: [PATCH 17/17] Web Config: Move tcpWiFiTypeDropdown location --- Firmware/RTK_Everywhere/AP-Config/index.html | 23 ++++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/Firmware/RTK_Everywhere/AP-Config/index.html b/Firmware/RTK_Everywhere/AP-Config/index.html index 3430338da..c3e2faeb5 100644 --- a/Firmware/RTK_Everywhere/AP-Config/index.html +++ b/Firmware/RTK_Everywhere/AP-Config/index.html @@ -1637,6 +1637,17 @@
+
+ + + + + +

@@ -1677,18 +1688,6 @@

- -
- - - - - -