From 425a4fc1d35946e091e9cec868d802221551a4b6 Mon Sep 17 00:00:00 2001 From: Paul Clark Date: Tue, 18 Nov 2025 14:03:15 +0000 Subject: [PATCH 01/26] TCP Server: update timer after client write --- Firmware/RTK_Everywhere/TcpServer.ino | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Firmware/RTK_Everywhere/TcpServer.ino b/Firmware/RTK_Everywhere/TcpServer.ino index 51a47c400..0878d75fd 100644 --- a/Firmware/RTK_Everywhere/TcpServer.ino +++ b/Firmware/RTK_Everywhere/TcpServer.ino @@ -120,7 +120,7 @@ static const char * tcpServerName; 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 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]; @@ -168,9 +168,12 @@ int32_t tcpServerClientSendData(int index, uint8_t *data, uint16_t length) length = tcpServerClient[index]->write(data, length); if (length > 0) { - // Update the data sent flag when data successfully sent + // Update the data sent flag and timer when data successfully sent if (length > 0) + { tcpServerClientDataSent = tcpServerClientDataSent | (1 << index); + tcpServerClientTimer[index] = millis(); + } if ((settings.debugTcpServer || PERIODIC_DISPLAY(PD_TCP_SERVER_CLIENT_DATA)) && (!inMainMenu)) systemPrintf("%s wrote %d bytes to %s\r\n", tcpServerName, length, From 44ca99fb1f3523be9bc2050eae531345f1b0e5ea Mon Sep 17 00:00:00 2001 From: Paul Clark Date: Tue, 18 Nov 2025 17:48:18 +0000 Subject: [PATCH 02/26] Record total bytes sent when calling ntripServer->checkBytesSentAndReset --- Firmware/RTK_Everywhere/NtripServer.ino | 5 +++-- Firmware/RTK_Everywhere/settings.h | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Firmware/RTK_Everywhere/NtripServer.ino b/Firmware/RTK_Everywhere/NtripServer.ino index 0d89974e1..f3823e5b8 100644 --- a/Firmware/RTK_Everywhere/NtripServer.ino +++ b/Firmware/RTK_Everywhere/NtripServer.ino @@ -434,9 +434,10 @@ void ntripServerProcessRTCM(int serverIndex, uint8_t incoming) } // If we have not gotten new RTCM bytes for a period of time, assume end of frame - if (ntripServer->checkBytesSentAndReset(100) && (!inMainMenu) && settings.debugNtripServerRtcm) + uint32_t totalBytesSent; + if (ntripServer->checkBytesSentAndReset(100, &totalBytesSent) && (!inMainMenu) && settings.debugNtripServerRtcm) systemPrintf("NTRIP Server %d transmitted %d RTCM bytes to Caster\r\n", serverIndex, - ntripServer->bytesSent); + totalBytesSent); if (ntripServer->networkClient && ntripServer->networkClient->connected()) { diff --git a/Firmware/RTK_Everywhere/settings.h b/Firmware/RTK_Everywhere/settings.h index 6f148f997..45daa833e 100644 --- a/Firmware/RTK_Everywhere/settings.h +++ b/Firmware/RTK_Everywhere/settings.h @@ -460,7 +460,7 @@ typedef struct } } - bool checkBytesSentAndReset(uint32_t timerLimit) + bool checkBytesSentAndReset(uint32_t timerLimit, uint32_t *totalBytesSent) { bool retVal = false; if (serverSemaphore == NULL) @@ -470,6 +470,7 @@ typedef struct if (((millis() - timer) > timerLimit) && (bytesSent > 0)) { retVal = true; + *totalBytesSent = bytesSent; bytesSent = 0; } xSemaphoreGive(serverSemaphore); From 48ec988d17aa7c74f99b6c022b52fed486c92865 Mon Sep 17 00:00:00 2001 From: Paul Clark Date: Fri, 21 Nov 2025 17:32:46 +0000 Subject: [PATCH 03/26] Add pin_debug to help solve the NTRIP Server write stall --- Firmware/RTK_Everywhere/Base.ino | 10 ++- Firmware/RTK_Everywhere/Begin.ino | 7 ++- Firmware/RTK_Everywhere/Developer.ino | 3 +- Firmware/RTK_Everywhere/LED.ino | 12 ++++ Firmware/RTK_Everywhere/NtripServer.ino | 72 ++++++++++++++++++++++ Firmware/RTK_Everywhere/RTK_Everywhere.ino | 7 ++- Firmware/RTK_Everywhere/Tasks.ino | 35 +++++++++-- Firmware/RTK_Everywhere/settings.h | 15 ++++- 8 files changed, 151 insertions(+), 10 deletions(-) diff --git a/Firmware/RTK_Everywhere/Base.ino b/Firmware/RTK_Everywhere/Base.ino index a1508c913..6c6186b3c 100644 --- a/Firmware/RTK_Everywhere/Base.ino +++ b/Firmware/RTK_Everywhere/Base.ino @@ -2,12 +2,18 @@ // Pass data along to NTRIP Server, or ESP-NOW radio void processRTCM(uint8_t *rtcmData, uint16_t dataLength) { + // Give this byte to the various possible transmission methods - for (int x = 0; x < dataLength; x++) + // Note: the "no increase in file size" and "due to lack of RTCM" glitch happens + // somewhere in ntripServerProcessRTCM + //pinDebugOn(); + //for (int x = 0; x < dataLength; x++) { for (int serverIndex = 0; serverIndex < NTRIP_SERVER_MAX; serverIndex++) - ntripServerProcessRTCM(serverIndex, rtcmData[x]); + //ntripServerProcessRTCM(serverIndex, rtcmData[x]); + ntripServerProcessRTCM(serverIndex, rtcmData, dataLength); } + //pinDebugOff(); for (int x = 0; x < dataLength; x++) espNowProcessRTCM(rtcmData[x]); diff --git a/Firmware/RTK_Everywhere/Begin.ino b/Firmware/RTK_Everywhere/Begin.ino index 8ca6fef21..4ce362117 100644 --- a/Firmware/RTK_Everywhere/Begin.ino +++ b/Firmware/RTK_Everywhere/Begin.ino @@ -356,7 +356,8 @@ void beginBoard() // 25, D0 : Boot + Boot Button pin_modeButton = 0; // 24, D2 : Status LED - pin_baseStatusLED = 2; + //pin_baseStatusLED = 2; + pin_debug = 2; // 29, D5 : GNSS TP via 74LVC4066 switch pin_GNSS_TimePulse = 5; // 14, D12 : I2C1 SDA via 74LVC4066 switch @@ -421,6 +422,10 @@ void beginBoard() pinMode(pin_baseStatusLED, OUTPUT); baseStatusLedOff(); + DMW_if systemPrintf("pin_debug: %d\r\n", pin_debug); + pinMode(pin_debug, OUTPUT); + pinDebugOff(); + DMW_if systemPrintf("pin_Cellular_Network_Indicator: %d\r\n", pin_Cellular_Network_Indicator); pinMode(pin_Cellular_Network_Indicator, INPUT); diff --git a/Firmware/RTK_Everywhere/Developer.ino b/Firmware/RTK_Everywhere/Developer.ino index 02af7c3bb..3c0086332 100644 --- a/Firmware/RTK_Everywhere/Developer.ino +++ b/Firmware/RTK_Everywhere/Developer.ino @@ -129,7 +129,8 @@ void ntripClientSettingsChanged() {} #ifndef COMPILE_NTRIP_SERVER bool ntripServerIsCasting(int serverIndex) {return false;} void ntripServerPrintStatus(int serverIndex) {systemPrintf("**NTRIP Server %d not compiled**\r\n", serverIndex);} -void ntripServerProcessRTCM(int serverIndex, uint8_t incoming) {} +//void ntripServerProcessRTCM(int serverIndex, uint8_t incoming) {} +void ntripServerProcessRTCM(int serverIndex, uint8_t *rtcmData, uint16_t dataLength) {} void ntripServerStop(int serverIndex, bool shutdown) {online.ntripServer[serverIndex] = false;} void ntripServerUpdate() {} void ntripServerValidateTables() {} diff --git a/Firmware/RTK_Everywhere/LED.ino b/Firmware/RTK_Everywhere/LED.ino index ba0b1f2f0..22dd2e29f 100644 --- a/Firmware/RTK_Everywhere/LED.ino +++ b/Firmware/RTK_Everywhere/LED.ino @@ -46,6 +46,18 @@ void baseStatusLedOff() digitalWrite(pin_baseStatusLED, LOW); } +void pinDebugOn() +{ + if (pin_debug != PIN_UNDEFINED) + digitalWrite(pin_debug, HIGH); +} + +void pinDebugOff() +{ + if (pin_debug != PIN_UNDEFINED) + digitalWrite(pin_debug, LOW); +} + void baseStatusLedBlink() { if (pin_baseStatusLED != PIN_UNDEFINED) diff --git a/Firmware/RTK_Everywhere/NtripServer.ino b/Firmware/RTK_Everywhere/NtripServer.ino index f3823e5b8..1bedf9dba 100644 --- a/Firmware/RTK_Everywhere/NtripServer.ino +++ b/Firmware/RTK_Everywhere/NtripServer.ino @@ -409,8 +409,13 @@ void ntripServerPrintStatus(int serverIndex) //---------------------------------------- // This function gets called as each RTCM byte comes in //---------------------------------------- +/* void ntripServerProcessRTCM(int serverIndex, uint8_t incoming) { + // ntripServerProcessRTCM can take up to 15ms to complete + // But when the "no increase in file size" and "due to lack of RTCM" glitch happens, + // the code stalls here for 10 seconds! + NTRIP_SERVER_DATA *ntripServer = &ntripServerArray[serverIndex]; if (ntripServer->state == NTRIP_SERVER_CASTING) @@ -441,6 +446,7 @@ void ntripServerProcessRTCM(int serverIndex, uint8_t incoming) if (ntripServer->networkClient && ntripServer->networkClient->connected()) { + pinDebugOn(); if (ntripServer->networkClient->write(incoming) == 1) // Send this byte to socket { ntripServer->updateTimerAndBytesSent(); @@ -456,6 +462,70 @@ void ntripServerProcessRTCM(int serverIndex, uint8_t incoming) systemPrintf("NTRIP Server %d broken connection to %s\r\n", serverIndex, settings.ntripServer_CasterHost[serverIndex]); } + pinDebugOff(); + } + } + + // Indicate that the GNSS is providing correction data + else if (ntripServer->state == NTRIP_SERVER_WAIT_GNSS_DATA) + { + ntripServerSetState(serverIndex, NTRIP_SERVER_CONNECTING); + } +} +*/ +void ntripServerProcessRTCM(int serverIndex, uint8_t *rtcmData, uint16_t dataLength) +{ + // ntripServerProcessRTCM can take up to 15ms to complete + // But when the "no increase in file size" and "due to lack of RTCM" glitch happens, + // the code stalls here for 10 seconds! + + NTRIP_SERVER_DATA *ntripServer = &ntripServerArray[serverIndex]; + + if (ntripServer->state == NTRIP_SERVER_CASTING) + { + // Generate and print timestamp if needed + uint32_t currentMilliseconds; + if (online.rtc) + { + // Timestamp the RTCM messages + currentMilliseconds = millis(); + if (((settings.debugNtripServerRtcm && ((currentMilliseconds - ntripServer->previousMilliseconds) > 5)) || + PERIODIC_DISPLAY(PD_NTRIP_SERVER_DATA)) && + (!settings.enableRtcmMessageChecking) && (!inMainMenu) && ntripServer->bytesSent) + { + PERIODIC_CLEAR(PD_NTRIP_SERVER_DATA); + systemPrintf(" Tx%d RTCM: %s, %d bytes sent\r\n", serverIndex, getTimeStamp(), + ntripServer->rtcmBytesSent); + ntripServer->rtcmBytesSent = 0; + } + ntripServer->previousMilliseconds = currentMilliseconds; + } + + // If we have not gotten new RTCM bytes for a period of time, assume end of frame + uint32_t totalBytesSent; + if (ntripServer->checkBytesSentAndReset(100, &totalBytesSent) && (!inMainMenu) && settings.debugNtripServerRtcm) + systemPrintf("NTRIP Server %d transmitted %d RTCM bytes to Caster\r\n", serverIndex, + totalBytesSent); + + if (ntripServer->networkClient && ntripServer->networkClient->connected()) + { + pinDebugOn(); + if (ntripServer->networkClient->write(rtcmData, dataLength) == dataLength) // Send this byte to socket + { + ntripServer->updateTimerAndBytesSent(dataLength); + netOutgoingRTCM = true; + while (ntripServer->networkClient->available()) + ntripServer->networkClient->read(); // Absorb any unwanted incoming traffic + } + // Failed to write the data + else + { + // Done with this client connection + if (settings.debugNtripServerRtcm && (!inMainMenu)) + systemPrintf("NTRIP Server %d broken connection to %s\r\n", serverIndex, + settings.ntripServer_CasterHost[serverIndex]); + } + pinDebugOff(); } } @@ -824,6 +894,8 @@ void ntripServerUpdate(int serverIndex) else if (ntripServer->millisSinceTimer() > (10 * 1000)) { // GNSS stopped sending RTCM correction data + if (pin_debug != PIN_UNDEFINED) + systemPrint(debugMessagePrefix); systemPrintf("NTRIP Server %d breaking connection to %s due to lack of RTCM data!\r\n", serverIndex, settings.ntripServer_CasterHost[serverIndex]); ntripServerRestart(serverIndex); diff --git a/Firmware/RTK_Everywhere/RTK_Everywhere.ino b/Firmware/RTK_Everywhere/RTK_Everywhere.ino index 1e700c93b..35c5c5d02 100644 --- a/Firmware/RTK_Everywhere/RTK_Everywhere.ino +++ b/Firmware/RTK_Everywhere/RTK_Everywhere.ino @@ -206,10 +206,13 @@ const uint16_t HTTPS_PORT = 443; #define SECONDS_IN_AN_HOUR (MINUTES_IN_AN_HOUR * SECONDS_IN_A_MINUTE) #define SECONDS_IN_A_DAY (HOURS_IN_A_DAY * SECONDS_IN_AN_HOUR) +const char *debugMessagePrefix = "# => "; // Something ~unique and easy to trigger on + // Hardware connections //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // These pins are set in beginBoard() #define PIN_UNDEFINED -1 +int pin_debug = PIN_UNDEFINED; // LED on EVK int pin_batteryStatusLED = PIN_UNDEFINED; // LED on Torch int pin_baseStatusLED = PIN_UNDEFINED; // LED on EVK int pin_bluetoothStatusLED = PIN_UNDEFINED; // LED on Torch @@ -602,7 +605,7 @@ volatile bool inDirectConnectMode = false; // Global state to indicate if GNSS/L #define SERIAL_SIZE_TX 512 uint8_t wBuffer[SERIAL_SIZE_TX]; // Buffer for writing from incoming SPP to F9P -const int btReadTaskStackSize = 4000; +const int btReadTaskStackSize = 3000; // Array of start-of-sentence offsets into the ring buffer #define AMOUNT_OF_RING_BUFFER_DATA_TO_DISCARD (settings.gnssHandlerBufferSize >> 2) @@ -1696,6 +1699,8 @@ void logUpdate() } else { + if (pin_debug != PIN_UNDEFINED) + systemPrint(debugMessagePrefix); if ((settings.enablePrintLogFileStatus) && (!inMainMenu)) systemPrintf("No increase in file size: %llu -> %llu\r\n", lastLogSize, logFileSize); logIncreasing = false; diff --git a/Firmware/RTK_Everywhere/Tasks.ino b/Firmware/RTK_Everywhere/Tasks.ino index 7b2509362..e860b808e 100644 --- a/Firmware/RTK_Everywhere/Tasks.ino +++ b/Firmware/RTK_Everywhere/Tasks.ino @@ -357,7 +357,15 @@ void gnssReadTask(void *e) reportFatalError("Failed to initialize the parser"); if (settings.debugGnss) - sempEnableDebugOutput(rtkParse); + { + sempEnableDebugOutput(rtkParse); // Standard debug on Serial + //sempEnableDebugOutput(rtkParse, &Serial, true); // Verbose debug + } + + // Abort NMEA and Unicore Hash on non-printable character + // Help faster recovery from UART errors on EVK + sempAbortNmeaOnNonPrintable(rtkParse); + sempAbortHashOnNonPrintable(rtkParse); bool sbfParserNeeded = present.gnss_mosaicX5; bool spartnParserNeeded = present.gnss_mosaicX5 && (productVariant != RTK_FLEX); @@ -450,20 +458,35 @@ void gnssReadTask(void *e) while (serialGNSS->available()) { // Read the data from UART1 - uint8_t incomingData[500]; + static uint8_t incomingData[256]; int bytesIncoming = serialGNSS->read(incomingData, sizeof(incomingData)); totalRxByteCount += bytesIncoming; + if ((bytesIncoming < 0) || (bytesIncoming > sizeof(incomingData))) + { + systemPrint(debugMessagePrefix); + systemPrintf("gnssReadTask: bytesIncoming = %d\r\n", bytesIncoming); + } + for (int x = 0; x < bytesIncoming; x++) { // Update the parser state based on the incoming byte // On mosaic-X5, pass the byte to sbfParse. On all other platforms, pass it straight to rtkParse - sempParseNextByte(sbfParserNeeded ? sbfParse : rtkParse, incomingData[x]); + if (!sbfParserNeeded) + { + // Note: the "no increase in file size" and "due to lack of RTCM" glitch happens + // somewhere in sempParseNextByte (processUart1Message) + //pinDebugOn(); + sempParseNextByte(rtkParse, incomingData[x]); + //pinDebugOff(); + } // See notes above. On the mosaic-X5, check that the incoming SBF blocks have expected IDs and // lengths to help prevent raw L-Band data being misidentified as SBF - if (sbfParserNeeded) + else // if (sbfParserNeeded) { + sempParseNextByte(sbfParse, incomingData[x]); + SEMP_SCRATCH_PAD *scratchPad = (SEMP_SCRATCH_PAD *)sbfParse->scratchPad; // Check if this is Length MSB @@ -884,7 +907,11 @@ void processUart1Message(SEMP_PARSE_STATE *parse, uint16_t type) if (inBaseMode() && type == RTK_RTCM_PARSER_INDEX) { // Pass data along to NTRIP Server, ESP-NOW radio, or LoRa + // Note: the "no increase in file size" and "due to lack of RTCM" glitch happens + // somewhere in processRTCM + //pinDebugOn(); processRTCM(parse->buffer, parse->length); + //pinDebugOff(); } // Determine if we are using the PPL - UM980, LG290P, or mosaic-X5 diff --git a/Firmware/RTK_Everywhere/settings.h b/Firmware/RTK_Everywhere/settings.h index 45daa833e..1ce5d021a 100644 --- a/Firmware/RTK_Everywhere/settings.h +++ b/Firmware/RTK_Everywhere/settings.h @@ -460,6 +460,19 @@ typedef struct } } + void updateTimerAndBytesSent(uint16_t dataLength) + { + if (serverSemaphore == NULL) + serverSemaphore = xSemaphoreCreateMutex(); + if (xSemaphoreTake(serverSemaphore, 10 / portTICK_PERIOD_MS) == pdPASS) + { + bytesSent = bytesSent + dataLength; + rtcmBytesSent = rtcmBytesSent + dataLength; + timer = millis(); + xSemaphoreGive(serverSemaphore); + } + } + bool checkBytesSentAndReset(uint32_t timerLimit, uint32_t *totalBytesSent) { bool retVal = false; @@ -854,7 +867,7 @@ struct Settings // GNSS UART uint16_t serialGNSSRxFullThreshold = 50; // RX FIFO full interrupt. Max of ~128. See pinUART2Task(). - int uartReceiveBufferSize = 1024 * 4; // This buffer is filled automatically as the UART receives characters. EVK needs 4K + int uartReceiveBufferSize = 1024 * 2; // This buffer is filled automatically as the UART receives characters // Hardware bool enableExternalHardwareEventLogging = false; // Log when INT/TM2 pin goes low From 34820f94db8a9b1c8ae0f96dce13f619e3d6b65f Mon Sep 17 00:00:00 2001 From: Paul Clark Date: Sat, 22 Nov 2025 16:29:58 +0000 Subject: [PATCH 04/26] Add settings.networkClientWriteTimeout_ms and ntripServer->networkClientConnected() --- Firmware/RTK_Everywhere/NtripServer.ino | 43 +++++++++++++++--- Firmware/RTK_Everywhere/Tasks.ino | 5 +++ Firmware/RTK_Everywhere/menuSystem.ino | 15 ++++++- Firmware/RTK_Everywhere/settings.h | 15 +++++++ Firmware/Tools/NTRIP_Sink.py | 60 +++++++++++++++++++++++++ 5 files changed, 131 insertions(+), 7 deletions(-) create mode 100644 Firmware/Tools/NTRIP_Sink.py diff --git a/Firmware/RTK_Everywhere/NtripServer.ino b/Firmware/RTK_Everywhere/NtripServer.ino index 1bedf9dba..5979951cd 100644 --- a/Firmware/RTK_Everywhere/NtripServer.ino +++ b/Firmware/RTK_Everywhere/NtripServer.ino @@ -444,9 +444,9 @@ void ntripServerProcessRTCM(int serverIndex, uint8_t incoming) systemPrintf("NTRIP Server %d transmitted %d RTCM bytes to Caster\r\n", serverIndex, totalBytesSent); - if (ntripServer->networkClient && ntripServer->networkClient->connected()) + if (ntripServer->networkClient && ntripServer->networkClientConnected()) { - pinDebugOn(); + //pinDebugOn(); if (ntripServer->networkClient->write(incoming) == 1) // Send this byte to socket { ntripServer->updateTimerAndBytesSent(); @@ -462,7 +462,7 @@ void ntripServerProcessRTCM(int serverIndex, uint8_t incoming) systemPrintf("NTRIP Server %d broken connection to %s\r\n", serverIndex, settings.ntripServer_CasterHost[serverIndex]); } - pinDebugOff(); + //pinDebugOff(); } } @@ -507,11 +507,22 @@ void ntripServerProcessRTCM(int serverIndex, uint8_t *rtcmData, uint16_t dataLen systemPrintf("NTRIP Server %d transmitted %d RTCM bytes to Caster\r\n", serverIndex, totalBytesSent); - if (ntripServer->networkClient && ntripServer->networkClient->connected()) + if (ntripServer->networkClient && ntripServer->networkClientConnected()) { + unsigned long entryTime = millis(); + + // Note: the "no increase in file size" and "due to lack of RTCM" glitch + // on EVK usually happens during the writing of RTCM 1005. + // RTCM 1074,1084,1094,1124 arrive from the F9P and are written to the ntripServer + // Then a blob of NMEA arrives - and is written to consumers + // Then RTCM 1005 arrives. The length is only 19+6 bytes. The stall happens during its write... + // I have seen the stall happen during the RTCM 1074,1084,1094,1124 but it seems even more rare + pinDebugOn(); + ntripServer->networkClient->setConnectionTimeout(settings.networkClientWriteTimeout_ms); // Belt & Braces if (ntripServer->networkClient->write(rtcmData, dataLength) == dataLength) // Send this byte to socket { + pinDebugOff(); ntripServer->updateTimerAndBytesSent(dataLength); netOutgoingRTCM = true; while (ntripServer->networkClient->available()) @@ -526,6 +537,17 @@ void ntripServerProcessRTCM(int serverIndex, uint8_t *rtcmData, uint16_t dataLen settings.ntripServer_CasterHost[serverIndex]); } pinDebugOff(); + + if ((millis() - entryTime) > 1000) + { + systemPrintf("# => ntripServer write took %ldms\r\n", millis() - entryTime); + dumpBuffer(rtcmData, dataLength); + + // # => ntripServer write took 3419ms + // 0x00000000: D3 00 13 3E D0 00 03 88 90 14 78 26 BF C9 56 50 ...>......x&..VP + // 0x00000010: 45 0C 17 41 6B 26 2B D1 C7 E..Ak&+.. + + } } } @@ -630,7 +652,7 @@ void ntripServerStop(int serverIndex, bool shutdown) if (ntripServer->networkClient) { // Break the NTRIP server connection if necessary - if (ntripServer->networkClient->connected()) + if (ntripServer->networkClientConnected()) ntripServer->networkClient->stop(); // Free the NTRIP server resources @@ -852,6 +874,15 @@ void ntripServerUpdate(int serverIndex) online.ntripServer[serverIndex] = true; ntripServer->startTime = millis(); ntripServerSetState(serverIndex, NTRIP_SERVER_CASTING); + + // Now we are ready to start casting, decrease timeout to networkClientWriteTimeout_ms + // The default timeout is WIFI_CLIENT_DEF_CONN_TIMEOUT_MS (3000) + // Each write will retry up to WIFI_CLIENT_MAX_WRITE_RETRY (10) times + // NetworkClient uses the same _timeout for both the initial connection and + // subsequent writes. The trick is to setConnectionTimeout after we are connected + // By reducing the timeout, we (hopefully) prevent the + // "no increase in file size" and "due to lack of RTCM" glitch + ntripServer->networkClient->setConnectionTimeout(settings.networkClientWriteTimeout_ms); } // Look for '401 Unauthorized' @@ -884,7 +915,7 @@ void ntripServerUpdate(int serverIndex) // NTRIP server authorized to send RTCM correction data to NTRIP caster case NTRIP_SERVER_CASTING: // Check for a broken connection - if (!ntripServer->networkClient->connected()) + if (!ntripServer->networkClientConnected()) { // Broken connection, retry the NTRIP connection systemPrintf("Connection to NTRIP Caster %d - %s was lost\r\n", serverIndex, diff --git a/Firmware/RTK_Everywhere/Tasks.ino b/Firmware/RTK_Everywhere/Tasks.ino index e860b808e..99c9c9ea4 100644 --- a/Firmware/RTK_Everywhere/Tasks.ino +++ b/Firmware/RTK_Everywhere/Tasks.ino @@ -1592,6 +1592,8 @@ void handleGnssDataTask(void *e) { markSemaphore(FUNCTION_WRITESD); + //pinDebugOn(); + do // Do the SD write in a do loop so we can break out if needed { if (settings.enablePrintSDBuffers && (!inMainMenu)) @@ -1749,6 +1751,9 @@ void handleGnssDataTask(void *e) } } while (0); + + //pinDebugOff(); + xSemaphoreGive(sdCardSemaphore); } // End sdCardSemaphore else diff --git a/Firmware/RTK_Everywhere/menuSystem.ino b/Firmware/RTK_Everywhere/menuSystem.ino index 1b78af4b3..fe705d7ba 100644 --- a/Firmware/RTK_Everywhere/menuSystem.ino +++ b/Firmware/RTK_Everywhere/menuSystem.ino @@ -642,7 +642,8 @@ void menuDebugHardware() int newDelay = getUserInputNumber(); // Returns EXIT, TIMEOUT, or long if ((newDelay != INPUT_RESPONSE_GETNUMBER_EXIT) && (newDelay != INPUT_RESPONSE_GETNUMBER_TIMEOUT)) { - settings.cliBlePrintDelay_ms = newDelay; + if ((newDelay >= 0) && (newDelay <= 1000)) + settings.cliBlePrintDelay_ms = newDelay; } } @@ -709,6 +710,8 @@ void menuDebugNetwork() systemPrint("11) Print network layer status: "); systemPrintf("%s\r\n", settings.printNetworkStatus ? "Enabled" : "Disabled"); + systemPrintf("12) NetworkClient write timeout: %ldms\r\n", settings.networkClientWriteTimeout_ms); + // NTP systemPrint("20) Debug NTP: "); systemPrintf("%s\r\n", settings.debugNtp ? "Enabled" : "Disabled"); @@ -767,6 +770,16 @@ void menuDebugNetwork() settings.debugNetworkLayer ^= 1; else if (incoming == 11) settings.printNetworkStatus ^= 1; + else if (incoming == 12) + { + systemPrintf("Enter NetworkClient timeout (%d to %d): ", 100, 3000); + int newDelay = getUserInputNumber(); // Returns EXIT, TIMEOUT, or long + if ((newDelay != INPUT_RESPONSE_GETNUMBER_EXIT) && (newDelay != INPUT_RESPONSE_GETNUMBER_TIMEOUT)) + { + if ((newDelay >= 100) && (newDelay <= 3000)) + settings.networkClientWriteTimeout_ms = newDelay; + } + } else if (incoming == 20) settings.debugNtp ^= 1; else if (incoming == 21) diff --git a/Firmware/RTK_Everywhere/settings.h b/Firmware/RTK_Everywhere/settings.h index 1ce5d021a..19151d595 100644 --- a/Firmware/RTK_Everywhere/settings.h +++ b/Firmware/RTK_Everywhere/settings.h @@ -530,6 +530,19 @@ typedef struct } return retVal; } + + bool networkClientConnected() + { + bool retVal = false; + if (serverSemaphore == NULL) + serverSemaphore = xSemaphoreCreateMutex(); + if (xSemaphoreTake(serverSemaphore, 100 / portTICK_PERIOD_MS) == pdPASS) + { + retVal = (bool)networkClient->connected(); + xSemaphoreGive(serverSemaphore); + } + return retVal; + } } NTRIP_SERVER_DATA; #endif // COMPILE_NETWORK @@ -900,6 +913,7 @@ struct Settings // Network layer bool debugNetworkLayer = false; // Enable debugging of the network layer bool printNetworkStatus = true; // Print network status (delays, failovers, IP address) + uint32_t networkClientWriteTimeout_ms = 250; // networkClient _timeout in ms // NTP bool debugNtp = false; @@ -1628,6 +1642,7 @@ const RTK_Settings_Entry rtkSettingsEntries[] = // Network layer { 0, 0, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _bool, 0, & settings.debugNetworkLayer, "debugNetworkLayer", nullptr, }, { 0, 0, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _bool, 0, & settings.printNetworkStatus, "printNetworkStatus", nullptr, }, + { 0, 0, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _uint32_t, 0, & settings.networkClientWriteTimeout_ms, "networkClientWriteTimeout", nullptr, }, // F // a diff --git a/Firmware/Tools/NTRIP_Sink.py b/Firmware/Tools/NTRIP_Sink.py new file mode 100644 index 000000000..5e489833c --- /dev/null +++ b/Firmware/Tools/NTRIP_Sink.py @@ -0,0 +1,60 @@ +import socket +import argparse +import multiprocessing +import time + +def client(server, port): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.bind((server, port)) + sock.listen() + totalBytes = 0 + lastBytes = 0 + startTime = time.time() + while True: + print("Waiting for new connection") + conn, addr = sock.accept() + print("Connection from " + str(addr)) + print("Sending ICY 200 OK") + conn.send(b"ICY 200 OK") + conn.settimeout(5.0) + keepGoing = True + while keepGoing: + try: + payload = conn.recv(1024) + except TimeoutError: + keepGoing = False + totalBytes += len(payload) + if time.time() - startTime > 5: + startTime = time.time() + rate = int((totalBytes - lastBytes) / 5) + lastBytes = totalBytes + print("Total bytes: " + str(totalBytes) + " (" + str(rate) + " Bps)") + conn.close() + print() + +if __name__ == "__main__": + + parser = argparse.ArgumentParser( + description='TCP Server') + + parser.add_argument('-server', type=str, default="", + help='Host Name or IP Address (e.g. 127.0.0.1 localhost)') + + parser.add_argument('-port', type=int, default=2101, + help='TCP Port Number') + + args = parser.parse_args() + + if (args.server != ""): + print("Listening on " + args.server + ":" + str(args.port)) + else: + print("Listening on port " + str(args.port)) + + proc = multiprocessing.Process(target = client, args = (args.server, args.port)) + proc.start() + + try: + while True: + pass + except KeyboardInterrupt: + proc.terminate() From 1d6500b53f93751220ba3ebd71784fd64630b269 Mon Sep 17 00:00:00 2001 From: Paul Clark Date: Sun, 23 Nov 2025 08:01:16 +0000 Subject: [PATCH 05/26] Use semaphore to protect write from connected --- Firmware/RTK_Everywhere/NtripServer.ino | 20 +++++----- Firmware/RTK_Everywhere/settings.h | 53 +++++++++++++++++++------ 2 files changed, 51 insertions(+), 22 deletions(-) diff --git a/Firmware/RTK_Everywhere/NtripServer.ino b/Firmware/RTK_Everywhere/NtripServer.ino index 5979951cd..86008cb8f 100644 --- a/Firmware/RTK_Everywhere/NtripServer.ino +++ b/Firmware/RTK_Everywhere/NtripServer.ino @@ -444,7 +444,7 @@ void ntripServerProcessRTCM(int serverIndex, uint8_t incoming) systemPrintf("NTRIP Server %d transmitted %d RTCM bytes to Caster\r\n", serverIndex, totalBytesSent); - if (ntripServer->networkClient && ntripServer->networkClientConnected()) + if (ntripServer->networkClient && ntripServer->networkClientConnected(true)) { //pinDebugOn(); if (ntripServer->networkClient->write(incoming) == 1) // Send this byte to socket @@ -507,7 +507,7 @@ void ntripServerProcessRTCM(int serverIndex, uint8_t *rtcmData, uint16_t dataLen systemPrintf("NTRIP Server %d transmitted %d RTCM bytes to Caster\r\n", serverIndex, totalBytesSent); - if (ntripServer->networkClient && ntripServer->networkClientConnected()) + if (ntripServer->networkClient && ntripServer->networkClientConnected(true)) { unsigned long entryTime = millis(); @@ -519,14 +519,12 @@ void ntripServerProcessRTCM(int serverIndex, uint8_t *rtcmData, uint16_t dataLen // I have seen the stall happen during the RTCM 1074,1084,1094,1124 but it seems even more rare pinDebugOn(); - ntripServer->networkClient->setConnectionTimeout(settings.networkClientWriteTimeout_ms); // Belt & Braces - if (ntripServer->networkClient->write(rtcmData, dataLength) == dataLength) // Send this byte to socket + if (ntripServer->networkClientWrite(rtcmData, dataLength) == dataLength) // Send this byte to socket { pinDebugOff(); ntripServer->updateTimerAndBytesSent(dataLength); netOutgoingRTCM = true; - while (ntripServer->networkClient->available()) - ntripServer->networkClient->read(); // Absorb any unwanted incoming traffic + ntripServer->networkClientAbsorb(); // Absorb any unwanted incoming traffic } // Failed to write the data else @@ -538,9 +536,11 @@ void ntripServerProcessRTCM(int serverIndex, uint8_t *rtcmData, uint16_t dataLen } pinDebugOff(); - if ((millis() - entryTime) > 1000) + if (((millis() - entryTime) > 1000) )//&& settings.debugNtripServerRtcm && (!inMainMenu)) { - systemPrintf("# => ntripServer write took %ldms\r\n", millis() - entryTime); + if (pin_debug != PIN_UNDEFINED) + systemPrint(debugMessagePrefix); + systemPrintf("ntripServer write took %ldms\r\n", millis() - entryTime); dumpBuffer(rtcmData, dataLength); // # => ntripServer write took 3419ms @@ -652,7 +652,7 @@ void ntripServerStop(int serverIndex, bool shutdown) if (ntripServer->networkClient) { // Break the NTRIP server connection if necessary - if (ntripServer->networkClientConnected()) + if (ntripServer->networkClientConnected(true)) ntripServer->networkClient->stop(); // Free the NTRIP server resources @@ -915,7 +915,7 @@ void ntripServerUpdate(int serverIndex) // NTRIP server authorized to send RTCM correction data to NTRIP caster case NTRIP_SERVER_CASTING: // Check for a broken connection - if (!ntripServer->networkClientConnected()) + if (!ntripServer->networkClientConnected(true)) { // Broken connection, retry the NTRIP connection systemPrintf("Connection to NTRIP Caster %d - %s was lost\r\n", serverIndex, diff --git a/Firmware/RTK_Everywhere/settings.h b/Firmware/RTK_Everywhere/settings.h index 19151d595..5fab5dda9 100644 --- a/Firmware/RTK_Everywhere/settings.h +++ b/Firmware/RTK_Everywhere/settings.h @@ -388,6 +388,8 @@ enum PeriodDisplayValues #ifdef COMPILE_NETWORK // NTRIP Server data +const TickType_t serverSemaphore_shortWait_ms = 10 / portTICK_PERIOD_MS; +const TickType_t serverSemaphore_longWait_ms = 100 / portTICK_PERIOD_MS; typedef struct { // Network connection used to push RTCM to NTRIP caster @@ -419,6 +421,8 @@ typedef struct // Protect all methods that manipulate timer with a mutex - to avoid race conditions // Remember that data is pushed to the servers by // gnssReadTask -> processUart1Message -> processRTCM -> ntripServerProcessRTCM + // These methods also protect the ntripServerProcessRTCM task write from connected checks + // by ntripServerUpdate in the loop SemaphoreHandle_t serverSemaphore = NULL; unsigned long millisSinceTimer() @@ -426,7 +430,7 @@ typedef struct unsigned long retVal = 0; if (serverSemaphore == NULL) serverSemaphore = xSemaphoreCreateMutex(); - if (xSemaphoreTake(serverSemaphore, 10 / portTICK_PERIOD_MS) == pdPASS) + if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) { retVal = millis() - timer; xSemaphoreGive(serverSemaphore); @@ -439,7 +443,7 @@ typedef struct unsigned long retVal = 0; if (serverSemaphore == NULL) serverSemaphore = xSemaphoreCreateMutex(); - if (xSemaphoreTake(serverSemaphore, 10 / portTICK_PERIOD_MS) == pdPASS) + if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) { retVal = millis() - startTime; xSemaphoreGive(serverSemaphore); @@ -451,7 +455,7 @@ typedef struct { if (serverSemaphore == NULL) serverSemaphore = xSemaphoreCreateMutex(); - if (xSemaphoreTake(serverSemaphore, 10 / portTICK_PERIOD_MS) == pdPASS) + if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) { bytesSent = bytesSent + 1; rtcmBytesSent = rtcmBytesSent + 1; @@ -464,7 +468,7 @@ typedef struct { if (serverSemaphore == NULL) serverSemaphore = xSemaphoreCreateMutex(); - if (xSemaphoreTake(serverSemaphore, 10 / portTICK_PERIOD_MS) == pdPASS) + if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) { bytesSent = bytesSent + dataLength; rtcmBytesSent = rtcmBytesSent + dataLength; @@ -478,7 +482,7 @@ typedef struct bool retVal = false; if (serverSemaphore == NULL) serverSemaphore = xSemaphoreCreateMutex(); - if (xSemaphoreTake(serverSemaphore, 10 / portTICK_PERIOD_MS) == pdPASS) + if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) { if (((millis() - timer) > timerLimit) && (bytesSent > 0)) { @@ -496,7 +500,7 @@ typedef struct unsigned long retVal = 0; if (serverSemaphore == NULL) serverSemaphore = xSemaphoreCreateMutex(); - if (xSemaphoreTake(serverSemaphore, 10 / portTICK_PERIOD_MS) == pdPASS) + if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) { retVal = timer - startTime; xSemaphoreGive(serverSemaphore); @@ -508,7 +512,7 @@ typedef struct { if (serverSemaphore == NULL) serverSemaphore = xSemaphoreCreateMutex(); - if (xSemaphoreTake(serverSemaphore, 10 / portTICK_PERIOD_MS) == pdPASS) + if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) { timer = millis(); xSemaphoreGive(serverSemaphore); @@ -520,7 +524,7 @@ typedef struct bool retVal = false; if (serverSemaphore == NULL) serverSemaphore = xSemaphoreCreateMutex(); - if (xSemaphoreTake(serverSemaphore, 10 / portTICK_PERIOD_MS) == pdPASS) + if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) { if ((millis() - timer) >= connectionAttemptTimeout) { @@ -531,18 +535,43 @@ typedef struct return retVal; } - bool networkClientConnected() + bool networkClientConnected(bool assumeConnected) { - bool retVal = false; + bool retVal = assumeConnected; if (serverSemaphore == NULL) serverSemaphore = xSemaphoreCreateMutex(); - if (xSemaphoreTake(serverSemaphore, 100 / portTICK_PERIOD_MS) == pdPASS) + if (xSemaphoreTake(serverSemaphore, serverSemaphore_longWait_ms) == pdPASS) { retVal = (bool)networkClient->connected(); xSemaphoreGive(serverSemaphore); } return retVal; } + + size_t networkClientWrite(const uint8_t *buf, size_t size) + { + size_t retVal = 0; + if (serverSemaphore == NULL) + serverSemaphore = xSemaphoreCreateMutex(); + if (xSemaphoreTake(serverSemaphore, serverSemaphore_longWait_ms) == pdPASS) + { + retVal = networkClient->write(buf, size); + xSemaphoreGive(serverSemaphore); + } + return retVal; + } + + void networkClientAbsorb() + { + if (serverSemaphore == NULL) + serverSemaphore = xSemaphoreCreateMutex(); + if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) + { + while (networkClient->available()) + networkClient->read(); // Absorb any unwanted incoming traffic + xSemaphoreGive(serverSemaphore); + } + } } NTRIP_SERVER_DATA; #endif // COMPILE_NETWORK @@ -913,7 +942,7 @@ struct Settings // Network layer bool debugNetworkLayer = false; // Enable debugging of the network layer bool printNetworkStatus = true; // Print network status (delays, failovers, IP address) - uint32_t networkClientWriteTimeout_ms = 250; // networkClient _timeout in ms + uint32_t networkClientWriteTimeout_ms = 1000; // networkClient _timeout in ms (lib default is 3000) // NTP bool debugNtp = false; From affa6f1fe7b27fa6bf2151cdd1aeead4f789266a Mon Sep 17 00:00:00 2001 From: Paul Clark Date: Sun, 23 Nov 2025 13:52:43 +0000 Subject: [PATCH 06/26] Add rtcmConsumerBuffer Add rtcmConsumerBuffer in Base.ino storeRTCMForConsumers stores RTCM in PSRAM sendRTCMToConsumers() sends RTCM to consumers: ntripServer, LoRa and ESP-NOW gnssReadTask calls sendRTCMToConsumers() Add Tools \ Dinger.py --- Firmware/RTK_Everywhere/Base.ino | 100 ++++++++++++++++++++---- Firmware/RTK_Everywhere/Developer.ino | 3 +- Firmware/RTK_Everywhere/NtripServer.ino | 88 +-------------------- Firmware/RTK_Everywhere/Tasks.ino | 19 ++++- Firmware/RTK_Everywhere/settings.h | 8 +- Firmware/Tools/Dinger.py | 47 +++++++++++ 6 files changed, 152 insertions(+), 113 deletions(-) create mode 100644 Firmware/Tools/Dinger.py diff --git a/Firmware/RTK_Everywhere/Base.ino b/Firmware/RTK_Everywhere/Base.ino index 6c6186b3c..92df9a633 100644 --- a/Firmware/RTK_Everywhere/Base.ino +++ b/Firmware/RTK_Everywhere/Base.ino @@ -1,24 +1,94 @@ -// This function gets called when an RTCM packet passes parser check in processUart1Message() task -// Pass data along to NTRIP Server, or ESP-NOW radio -void processRTCM(uint8_t *rtcmData, uint16_t dataLength) +// Enough storage for two rounds of RTCM 1074,1084,1094,1124,1005 +// To prevent the "no increase in file size" and "due to lack of RTCM" glitch: +// RTCM is stored in PSRAM by the Uart1 task and written by tripServerUpdate +const uint8_t rtcmConsumerBufferEntries = 16; +const uint16_t rtcmConsumerBufferEntrySize = 1032; // RTCM can be up to 1024 + 6 bytes +uint16_t rtcmConsumerBufferLengths[rtcmConsumerBufferEntries]; +uint8_t rtcmConsumerBufferHead; +uint8_t rtcmConsumerBufferTail; +uint8_t *rtcmConsumerBufferPtr = nullptr; + +bool rtcmConsumerBufferAllocated() +{ + if (rtcmConsumerBufferPtr == nullptr) + { + rtcmConsumerBufferPtr = (uint8_t *)rtkMalloc( + (size_t)rtcmConsumerBufferEntrySize * (size_t)rtcmConsumerBufferEntries, + "ntripBuffer"); + for (uint8_t i = 0; i < rtcmConsumerBufferEntries; i++) + rtcmConsumerBufferLengths[i] = 0; + rtcmConsumerBufferHead = 0; + rtcmConsumerBufferTail = 0; + } + if (rtcmConsumerBufferPtr == nullptr) + { + systemPrintln("rtcmConsumerBuffer malloc failed!"); + return false; + } + + return true; +} + +void storeRTCMForConsumers(uint8_t *rtcmData, uint16_t dataLength) { + // Store each RTCM message in a PSRAM buffer + // The messages are written to the servers by ntripServerUpdate - // Give this byte to the various possible transmission methods - // Note: the "no increase in file size" and "due to lack of RTCM" glitch happens - // somewhere in ntripServerProcessRTCM - //pinDebugOn(); - //for (int x = 0; x < dataLength; x++) + if (!rtcmConsumerBufferAllocated()) + return; + + // Check if a buffer is available + uint8_t buffersInUse; + if (rtcmConsumerBufferHead == rtcmConsumerBufferTail) // All buffers are empty + buffersInUse = 0; + else if (rtcmConsumerBufferHead > rtcmConsumerBufferTail) + buffersInUse = rtcmConsumerBufferHead - rtcmConsumerBufferTail; + else // rtcmConsumerBufferHead < rtcmConsumerBufferTail + buffersInUse = (rtcmConsumerBufferEntries + rtcmConsumerBufferHead) - rtcmConsumerBufferTail; + if (buffersInUse < (rtcmConsumerBufferEntries - 1)) { - for (int serverIndex = 0; serverIndex < NTRIP_SERVER_MAX; serverIndex++) - //ntripServerProcessRTCM(serverIndex, rtcmData[x]); - ntripServerProcessRTCM(serverIndex, rtcmData, dataLength); + uint8_t *dest = rtcmConsumerBufferPtr; + dest += (size_t)rtcmConsumerBufferEntrySize * (size_t)rtcmConsumerBufferHead; + memcpy(dest, rtcmData, dataLength); // Store the RTCM + rtcmConsumerBufferLengths[rtcmConsumerBufferHead] = dataLength; + rtcmConsumerBufferHead++; + rtcmConsumerBufferHead %= rtcmConsumerBufferEntries; + } + else + { + if (settings.debugNtripServerRtcm && (!inMainMenu)) + systemPrintln("rtcmConsumerBuffer full. RTCM lost"); } - //pinDebugOff(); +} + +void sendRTCMToConsumers() +{ + if (!rtcmConsumerBufferAllocated()) + return; + + while (rtcmConsumerBufferHead != rtcmConsumerBufferTail) + { + uint8_t *dest = rtcmConsumerBufferPtr; + dest += (size_t)rtcmConsumerBufferEntrySize * (size_t)rtcmConsumerBufferTail; + + for (int serverIndex = 0; serverIndex < NTRIP_SERVER_MAX; serverIndex++) + ntripServerSendRTCM(serverIndex, dest, rtcmConsumerBufferLengths[rtcmConsumerBufferTail]); - for (int x = 0; x < dataLength; x++) - espNowProcessRTCM(rtcmData[x]); + loraProcessRTCM(dest, rtcmConsumerBufferLengths[rtcmConsumerBufferTail]); - loraProcessRTCM(rtcmData, dataLength); + for (uint16_t x = 0; x < rtcmConsumerBufferLengths[rtcmConsumerBufferTail]; x++) + espNowProcessRTCM(dest[x]); + + rtcmConsumerBufferTail++; + rtcmConsumerBufferTail %= rtcmConsumerBufferEntries; + } +} + +// This function gets called when an RTCM packet passes parser check in processUart1Message() task +// Pass data along to NTRIP Server, or ESP-NOW radio +void processRTCM(uint8_t *rtcmData, uint16_t dataLength) +{ + storeRTCMForConsumers(rtcmData, dataLength); rtcmLastPacketSent = millis(); rtcmPacketsSent++; diff --git a/Firmware/RTK_Everywhere/Developer.ino b/Firmware/RTK_Everywhere/Developer.ino index 3c0086332..7e9fe4a81 100644 --- a/Firmware/RTK_Everywhere/Developer.ino +++ b/Firmware/RTK_Everywhere/Developer.ino @@ -129,8 +129,7 @@ void ntripClientSettingsChanged() {} #ifndef COMPILE_NTRIP_SERVER bool ntripServerIsCasting(int serverIndex) {return false;} void ntripServerPrintStatus(int serverIndex) {systemPrintf("**NTRIP Server %d not compiled**\r\n", serverIndex);} -//void ntripServerProcessRTCM(int serverIndex, uint8_t incoming) {} -void ntripServerProcessRTCM(int serverIndex, uint8_t *rtcmData, uint16_t dataLength) {} +void ntripServerProcessRTCM(uint8_t *rtcmData, uint16_t dataLength) {} void ntripServerStop(int serverIndex, bool shutdown) {online.ntripServer[serverIndex] = false;} void ntripServerUpdate() {} void ntripServerValidateTables() {} diff --git a/Firmware/RTK_Everywhere/NtripServer.ino b/Firmware/RTK_Everywhere/NtripServer.ino index 86008cb8f..fc9843b1b 100644 --- a/Firmware/RTK_Everywhere/NtripServer.ino +++ b/Firmware/RTK_Everywhere/NtripServer.ino @@ -406,79 +406,8 @@ void ntripServerPrintStatus(int serverIndex) } } -//---------------------------------------- -// This function gets called as each RTCM byte comes in -//---------------------------------------- -/* -void ntripServerProcessRTCM(int serverIndex, uint8_t incoming) -{ - // ntripServerProcessRTCM can take up to 15ms to complete - // But when the "no increase in file size" and "due to lack of RTCM" glitch happens, - // the code stalls here for 10 seconds! - - NTRIP_SERVER_DATA *ntripServer = &ntripServerArray[serverIndex]; - - if (ntripServer->state == NTRIP_SERVER_CASTING) - { - // Generate and print timestamp if needed - uint32_t currentMilliseconds; - if (online.rtc) - { - // Timestamp the RTCM messages - currentMilliseconds = millis(); - if (((settings.debugNtripServerRtcm && ((currentMilliseconds - ntripServer->previousMilliseconds) > 5)) || - PERIODIC_DISPLAY(PD_NTRIP_SERVER_DATA)) && - (!settings.enableRtcmMessageChecking) && (!inMainMenu) && ntripServer->bytesSent) - { - PERIODIC_CLEAR(PD_NTRIP_SERVER_DATA); - systemPrintf(" Tx%d RTCM: %s, %d bytes sent\r\n", serverIndex, getTimeStamp(), - ntripServer->rtcmBytesSent); - ntripServer->rtcmBytesSent = 0; - } - ntripServer->previousMilliseconds = currentMilliseconds; - } - - // If we have not gotten new RTCM bytes for a period of time, assume end of frame - uint32_t totalBytesSent; - if (ntripServer->checkBytesSentAndReset(100, &totalBytesSent) && (!inMainMenu) && settings.debugNtripServerRtcm) - systemPrintf("NTRIP Server %d transmitted %d RTCM bytes to Caster\r\n", serverIndex, - totalBytesSent); - - if (ntripServer->networkClient && ntripServer->networkClientConnected(true)) - { - //pinDebugOn(); - if (ntripServer->networkClient->write(incoming) == 1) // Send this byte to socket - { - ntripServer->updateTimerAndBytesSent(); - netOutgoingRTCM = true; - while (ntripServer->networkClient->available()) - ntripServer->networkClient->read(); // Absorb any unwanted incoming traffic - } - // Failed to write the data - else - { - // Done with this client connection - if (settings.debugNtripServerRtcm && (!inMainMenu)) - systemPrintf("NTRIP Server %d broken connection to %s\r\n", serverIndex, - settings.ntripServer_CasterHost[serverIndex]); - } - //pinDebugOff(); - } - } - - // Indicate that the GNSS is providing correction data - else if (ntripServer->state == NTRIP_SERVER_WAIT_GNSS_DATA) - { - ntripServerSetState(serverIndex, NTRIP_SERVER_CONNECTING); - } -} -*/ -void ntripServerProcessRTCM(int serverIndex, uint8_t *rtcmData, uint16_t dataLength) +void ntripServerSendRTCM(int serverIndex, uint8_t *rtcmData, uint16_t dataLength) { - // ntripServerProcessRTCM can take up to 15ms to complete - // But when the "no increase in file size" and "due to lack of RTCM" glitch happens, - // the code stalls here for 10 seconds! - NTRIP_SERVER_DATA *ntripServer = &ntripServerArray[serverIndex]; if (ntripServer->state == NTRIP_SERVER_CASTING) @@ -511,13 +440,6 @@ void ntripServerProcessRTCM(int serverIndex, uint8_t *rtcmData, uint16_t dataLen { unsigned long entryTime = millis(); - // Note: the "no increase in file size" and "due to lack of RTCM" glitch - // on EVK usually happens during the writing of RTCM 1005. - // RTCM 1074,1084,1094,1124 arrive from the F9P and are written to the ntripServer - // Then a blob of NMEA arrives - and is written to consumers - // Then RTCM 1005 arrives. The length is only 19+6 bytes. The stall happens during its write... - // I have seen the stall happen during the RTCM 1074,1084,1094,1124 but it seems even more rare - pinDebugOn(); if (ntripServer->networkClientWrite(rtcmData, dataLength) == dataLength) // Send this byte to socket { @@ -541,12 +463,6 @@ void ntripServerProcessRTCM(int serverIndex, uint8_t *rtcmData, uint16_t dataLen if (pin_debug != PIN_UNDEFINED) systemPrint(debugMessagePrefix); systemPrintf("ntripServer write took %ldms\r\n", millis() - entryTime); - dumpBuffer(rtcmData, dataLength); - - // # => ntripServer write took 3419ms - // 0x00000000: D3 00 13 3E D0 00 03 88 90 14 78 26 BF C9 56 50 ...>......x&..VP - // 0x00000010: 45 0C 17 41 6B 26 2B D1 C7 E..Ak&+.. - } } } @@ -880,8 +796,6 @@ void ntripServerUpdate(int serverIndex) // Each write will retry up to WIFI_CLIENT_MAX_WRITE_RETRY (10) times // NetworkClient uses the same _timeout for both the initial connection and // subsequent writes. The trick is to setConnectionTimeout after we are connected - // By reducing the timeout, we (hopefully) prevent the - // "no increase in file size" and "due to lack of RTCM" glitch ntripServer->networkClient->setConnectionTimeout(settings.networkClientWriteTimeout_ms); } diff --git a/Firmware/RTK_Everywhere/Tasks.ino b/Firmware/RTK_Everywhere/Tasks.ino index 99c9c9ea4..5f5c088b4 100644 --- a/Firmware/RTK_Everywhere/Tasks.ino +++ b/Firmware/RTK_Everywhere/Tasks.ino @@ -474,8 +474,6 @@ void gnssReadTask(void *e) // On mosaic-X5, pass the byte to sbfParse. On all other platforms, pass it straight to rtkParse if (!sbfParserNeeded) { - // Note: the "no increase in file size" and "due to lack of RTCM" glitch happens - // somewhere in sempParseNextByte (processUart1Message) //pinDebugOn(); sempParseNextByte(rtkParse, incomingData[x]); //pinDebugOff(); @@ -907,8 +905,6 @@ void processUart1Message(SEMP_PARSE_STATE *parse, uint16_t type) if (inBaseMode() && type == RTK_RTCM_PARSER_INDEX) { // Pass data along to NTRIP Server, ESP-NOW radio, or LoRa - // Note: the "no increase in file size" and "due to lack of RTCM" glitch happens - // somewhere in processRTCM //pinDebugOn(); processRTCM(parse->buffer, parse->length); //pinDebugOff(); @@ -1388,6 +1384,21 @@ void handleGnssDataTask(void *e) { ringBufferSemaphoreHolder = "handleGnssDataTask"; + //---------------------------------------------------------------------- + // Send RTCM to consumers + // + // RTCM has its own storage in rtcmConsumerBuffer (Base.ino) + // It does not use the main ringBuffer + // But we do the writing here so all traffic generated in the same place + //---------------------------------------------------------------------- + + startMillis = millis(); + + sendRTCMToConsumers(); + + if ((millis() - startMillis) > settings.networkClientWriteTimeout_ms) + slowConsumer = "RTCM Consumers"; + //---------------------------------------------------------------------- // Send data over Bluetooth //---------------------------------------------------------------------- diff --git a/Firmware/RTK_Everywhere/settings.h b/Firmware/RTK_Everywhere/settings.h index 5fab5dda9..e1832fce7 100644 --- a/Firmware/RTK_Everywhere/settings.h +++ b/Firmware/RTK_Everywhere/settings.h @@ -419,10 +419,7 @@ typedef struct // Protect all methods that manipulate timer with a mutex - to avoid race conditions - // Remember that data is pushed to the servers by - // gnssReadTask -> processUart1Message -> processRTCM -> ntripServerProcessRTCM - // These methods also protect the ntripServerProcessRTCM task write from connected checks - // by ntripServerUpdate in the loop + // Also protect the write from connected checks SemaphoreHandle_t serverSemaphore = NULL; unsigned long millisSinceTimer() @@ -942,7 +939,8 @@ struct Settings // Network layer bool debugNetworkLayer = false; // Enable debugging of the network layer bool printNetworkStatus = true; // Print network status (delays, failovers, IP address) - uint32_t networkClientWriteTimeout_ms = 1000; // networkClient _timeout in ms (lib default is 3000) + // networkClient _timeout in ms (lib default is 3000). This limits write glitches to about 3.4s + uint32_t networkClientWriteTimeout_ms = 250; // NTP bool debugNtp = false; diff --git a/Firmware/Tools/Dinger.py b/Firmware/Tools/Dinger.py new file mode 100644 index 000000000..12fa4ecbb --- /dev/null +++ b/Firmware/Tools/Dinger.py @@ -0,0 +1,47 @@ +import argparse +import multiprocessing +import serial +from datetime import datetime + +def dinger(port, baud, find): + ser = serial.Serial(port, baud) + buffer = list(find) + for i in range(len(find)): + buffer[i] = ' ' + while True: + for i in range(1, len(find)): + buffer[i-1] = buffer[i] + buffer[len(find) - 1] = ser.read() + s = '' + for c in buffer: + s += chr(ord(c)) + #print(s) + if s == find: + print('\a') # Ding + print("Ding! " + str(find) + " found at " + datetime.now().isoformat()) + print(str(ser.readline())) + +if __name__ == "__main__": + + parser = argparse.ArgumentParser( + description='Dinger') + + parser.add_argument('-port', type=str, default="COM1", + help='COM port') + + parser.add_argument('-baud', type=int, default=115200, + help='Baud rate') + + parser.add_argument('-find', type=str, default="# => ", + help='Ding on this') + + args = parser.parse_args() + + proc = multiprocessing.Process(target = dinger, args = (args.port, args.baud, args.find)) + proc.start() + + try: + while True: + pass + except KeyboardInterrupt: + proc.terminate() From e4853e1e7c43a4044ab13c7ee81b898cfab5e166 Mon Sep 17 00:00:00 2001 From: Paul Clark Date: Sun, 23 Nov 2025 14:01:57 +0000 Subject: [PATCH 07/26] Add ntripServerSendRTCM to Developer.ino --- Firmware/RTK_Everywhere/Developer.ino | 1 + 1 file changed, 1 insertion(+) diff --git a/Firmware/RTK_Everywhere/Developer.ino b/Firmware/RTK_Everywhere/Developer.ino index 7e9fe4a81..e483bfe37 100644 --- a/Firmware/RTK_Everywhere/Developer.ino +++ b/Firmware/RTK_Everywhere/Developer.ino @@ -130,6 +130,7 @@ void ntripClientSettingsChanged() {} bool ntripServerIsCasting(int serverIndex) {return false;} void ntripServerPrintStatus(int serverIndex) {systemPrintf("**NTRIP Server %d not compiled**\r\n", serverIndex);} void ntripServerProcessRTCM(uint8_t *rtcmData, uint16_t dataLength) {} +void ntripServerSendRTCM(int serverIndex, uint8_t *rtcmData, uint16_t dataLength) {} void ntripServerStop(int serverIndex, bool shutdown) {online.ntripServer[serverIndex] = false;} void ntripServerUpdate() {} void ntripServerValidateTables() {} From 43cd0083c86380fc50f7451bc51a24d79e497349 Mon Sep 17 00:00:00 2001 From: Paul Clark Date: Sun, 23 Nov 2025 14:07:06 +0000 Subject: [PATCH 08/26] Update comments --- Firmware/RTK_Everywhere/Base.ino | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Firmware/RTK_Everywhere/Base.ino b/Firmware/RTK_Everywhere/Base.ino index 92df9a633..15894e6a3 100644 --- a/Firmware/RTK_Everywhere/Base.ino +++ b/Firmware/RTK_Everywhere/Base.ino @@ -1,6 +1,6 @@ // Enough storage for two rounds of RTCM 1074,1084,1094,1124,1005 // To prevent the "no increase in file size" and "due to lack of RTCM" glitch: -// RTCM is stored in PSRAM by the Uart1 task and written by tripServerUpdate +// RTCM is stored in PSRAM by the processUart1Message task and written by sendRTCMToConsumers const uint8_t rtcmConsumerBufferEntries = 16; const uint16_t rtcmConsumerBufferEntrySize = 1032; // RTCM can be up to 1024 + 6 bytes uint16_t rtcmConsumerBufferLengths[rtcmConsumerBufferEntries]; @@ -32,7 +32,7 @@ bool rtcmConsumerBufferAllocated() void storeRTCMForConsumers(uint8_t *rtcmData, uint16_t dataLength) { // Store each RTCM message in a PSRAM buffer - // The messages are written to the servers by ntripServerUpdate + // The messages are written to the servers by sendRTCMToConsumers if (!rtcmConsumerBufferAllocated()) return; @@ -61,6 +61,7 @@ void storeRTCMForConsumers(uint8_t *rtcmData, uint16_t dataLength) } } +// Send the stored RTCM to consumers: ntripServer, LoRa and ESP-NOW void sendRTCMToConsumers() { if (!rtcmConsumerBufferAllocated()) @@ -85,7 +86,7 @@ void sendRTCMToConsumers() } // This function gets called when an RTCM packet passes parser check in processUart1Message() task -// Pass data along to NTRIP Server, or ESP-NOW radio +// Store data ready to be passed along to NTRIP Server, or ESP-NOW radio void processRTCM(uint8_t *rtcmData, uint16_t dataLength) { storeRTCMForConsumers(rtcmData, dataLength); From 0de67dad7573c2fa2916eaa115bcfb0b9950a324 Mon Sep 17 00:00:00 2001 From: Paul Clark Date: Sun, 23 Nov 2025 14:15:03 +0000 Subject: [PATCH 09/26] More comments --- Firmware/RTK_Everywhere/Base.ino | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/Firmware/RTK_Everywhere/Base.ino b/Firmware/RTK_Everywhere/Base.ino index 15894e6a3..3fecf4698 100644 --- a/Firmware/RTK_Everywhere/Base.ino +++ b/Firmware/RTK_Everywhere/Base.ino @@ -8,6 +8,7 @@ uint8_t rtcmConsumerBufferHead; uint8_t rtcmConsumerBufferTail; uint8_t *rtcmConsumerBufferPtr = nullptr; +// Allocate and initialize the rtcmConsumerBuffer bool rtcmConsumerBufferAllocated() { if (rtcmConsumerBufferPtr == nullptr) @@ -29,11 +30,10 @@ bool rtcmConsumerBufferAllocated() return true; } +// Store each RTCM message in a PSRAM buffer +// The messages are written to the servers by sendRTCMToConsumers void storeRTCMForConsumers(uint8_t *rtcmData, uint16_t dataLength) { - // Store each RTCM message in a PSRAM buffer - // The messages are written to the servers by sendRTCMToConsumers - if (!rtcmConsumerBufferAllocated()) return; @@ -50,9 +50,9 @@ void storeRTCMForConsumers(uint8_t *rtcmData, uint16_t dataLength) uint8_t *dest = rtcmConsumerBufferPtr; dest += (size_t)rtcmConsumerBufferEntrySize * (size_t)rtcmConsumerBufferHead; memcpy(dest, rtcmData, dataLength); // Store the RTCM - rtcmConsumerBufferLengths[rtcmConsumerBufferHead] = dataLength; - rtcmConsumerBufferHead++; - rtcmConsumerBufferHead %= rtcmConsumerBufferEntries; + rtcmConsumerBufferLengths[rtcmConsumerBufferHead] = dataLength; // Store the length + rtcmConsumerBufferHead++; // Increment the Head + rtcmConsumerBufferHead %= rtcmConsumerBufferEntries; // Wrap } else { @@ -72,16 +72,19 @@ void sendRTCMToConsumers() uint8_t *dest = rtcmConsumerBufferPtr; dest += (size_t)rtcmConsumerBufferEntrySize * (size_t)rtcmConsumerBufferTail; + // NTRIP Server for (int serverIndex = 0; serverIndex < NTRIP_SERVER_MAX; serverIndex++) ntripServerSendRTCM(serverIndex, dest, rtcmConsumerBufferLengths[rtcmConsumerBufferTail]); + // LoRa loraProcessRTCM(dest, rtcmConsumerBufferLengths[rtcmConsumerBufferTail]); + // ESP-NOW for (uint16_t x = 0; x < rtcmConsumerBufferLengths[rtcmConsumerBufferTail]; x++) espNowProcessRTCM(dest[x]); - rtcmConsumerBufferTail++; - rtcmConsumerBufferTail %= rtcmConsumerBufferEntries; + rtcmConsumerBufferTail++; // Increment the Tail + rtcmConsumerBufferTail %= rtcmConsumerBufferEntries; // Wrap } } From 76e99e912f50a9cdb4593248872a2f0451d946d6 Mon Sep 17 00:00:00 2001 From: Paul Clark Date: Sun, 23 Nov 2025 14:38:43 +0000 Subject: [PATCH 10/26] Comment pin_debug for PR --- Firmware/RTK_Everywhere/Begin.ino | 4 ++-- Firmware/RTK_Everywhere/NtripServer.ino | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Firmware/RTK_Everywhere/Begin.ino b/Firmware/RTK_Everywhere/Begin.ino index 4ce362117..0840aa700 100644 --- a/Firmware/RTK_Everywhere/Begin.ino +++ b/Firmware/RTK_Everywhere/Begin.ino @@ -356,8 +356,8 @@ void beginBoard() // 25, D0 : Boot + Boot Button pin_modeButton = 0; // 24, D2 : Status LED - //pin_baseStatusLED = 2; - pin_debug = 2; + pin_baseStatusLED = 2; + //pin_debug = 2; // On EVK we can use the Status LED for debug // 29, D5 : GNSS TP via 74LVC4066 switch pin_GNSS_TimePulse = 5; // 14, D12 : I2C1 SDA via 74LVC4066 switch diff --git a/Firmware/RTK_Everywhere/NtripServer.ino b/Firmware/RTK_Everywhere/NtripServer.ino index fc9843b1b..5f16175e8 100644 --- a/Firmware/RTK_Everywhere/NtripServer.ino +++ b/Firmware/RTK_Everywhere/NtripServer.ino @@ -406,6 +406,9 @@ void ntripServerPrintStatus(int serverIndex) } } +//---------------------------------------- +// This function gets called as each complete RTCM message comes in +//---------------------------------------- void ntripServerSendRTCM(int serverIndex, uint8_t *rtcmData, uint16_t dataLength) { NTRIP_SERVER_DATA *ntripServer = &ntripServerArray[serverIndex]; @@ -440,10 +443,10 @@ void ntripServerSendRTCM(int serverIndex, uint8_t *rtcmData, uint16_t dataLength { unsigned long entryTime = millis(); - pinDebugOn(); + //pinDebugOn(); if (ntripServer->networkClientWrite(rtcmData, dataLength) == dataLength) // Send this byte to socket { - pinDebugOff(); + //pinDebugOff(); ntripServer->updateTimerAndBytesSent(dataLength); netOutgoingRTCM = true; ntripServer->networkClientAbsorb(); // Absorb any unwanted incoming traffic @@ -456,9 +459,9 @@ void ntripServerSendRTCM(int serverIndex, uint8_t *rtcmData, uint16_t dataLength systemPrintf("NTRIP Server %d broken connection to %s\r\n", serverIndex, settings.ntripServer_CasterHost[serverIndex]); } - pinDebugOff(); + //pinDebugOff(); - if (((millis() - entryTime) > 1000) )//&& settings.debugNtripServerRtcm && (!inMainMenu)) + if (((millis() - entryTime) > settings.networkClientWriteTimeout_ms) && settings.debugNtripServerRtcm && (!inMainMenu)) { if (pin_debug != PIN_UNDEFINED) systemPrint(debugMessagePrefix); From 18e2de206488e8ce711d5817d397151c42ed0e99 Mon Sep 17 00:00:00 2001 From: Paul Clark Date: Mon, 24 Nov 2025 09:25:04 +0000 Subject: [PATCH 11/26] Improve buffersInUse --- Firmware/RTK_Everywhere/Base.ino | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Firmware/RTK_Everywhere/Base.ino b/Firmware/RTK_Everywhere/Base.ino index 3fecf4698..fb69ad35f 100644 --- a/Firmware/RTK_Everywhere/Base.ino +++ b/Firmware/RTK_Everywhere/Base.ino @@ -1,5 +1,5 @@ // Enough storage for two rounds of RTCM 1074,1084,1094,1124,1005 -// To prevent the "no increase in file size" and "due to lack of RTCM" glitch: +// To help prevent the "no increase in file size" and "due to lack of RTCM" glitch: // RTCM is stored in PSRAM by the processUart1Message task and written by sendRTCMToConsumers const uint8_t rtcmConsumerBufferEntries = 16; const uint16_t rtcmConsumerBufferEntrySize = 1032; // RTCM can be up to 1024 + 6 bytes @@ -38,13 +38,9 @@ void storeRTCMForConsumers(uint8_t *rtcmData, uint16_t dataLength) return; // Check if a buffer is available - uint8_t buffersInUse; - if (rtcmConsumerBufferHead == rtcmConsumerBufferTail) // All buffers are empty - buffersInUse = 0; - else if (rtcmConsumerBufferHead > rtcmConsumerBufferTail) - buffersInUse = rtcmConsumerBufferHead - rtcmConsumerBufferTail; - else // rtcmConsumerBufferHead < rtcmConsumerBufferTail - buffersInUse = (rtcmConsumerBufferEntries + rtcmConsumerBufferHead) - rtcmConsumerBufferTail; + uint8_t buffersInUse = rtcmConsumerBufferHead - rtcmConsumerBufferTail; + if (buffersInUse >= rtcmConsumerBufferEntries) // Wrap if Tail is > Head + buffersInUse += rtcmConsumerBufferEntries; if (buffersInUse < (rtcmConsumerBufferEntries - 1)) { uint8_t *dest = rtcmConsumerBufferPtr; From b95c0934619244028c60e5b6f69875c1462712a4 Mon Sep 17 00:00:00 2001 From: Paul Clark Date: Mon, 24 Nov 2025 09:27:20 +0000 Subject: [PATCH 12/26] Update Dinger.py - add printLines --- Firmware/Tools/Dinger.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Firmware/Tools/Dinger.py b/Firmware/Tools/Dinger.py index 12fa4ecbb..311b9303e 100644 --- a/Firmware/Tools/Dinger.py +++ b/Firmware/Tools/Dinger.py @@ -3,7 +3,7 @@ import serial from datetime import datetime -def dinger(port, baud, find): +def dinger(port, baud, find, printLines): ser = serial.Serial(port, baud) buffer = list(find) for i in range(len(find)): @@ -15,11 +15,12 @@ def dinger(port, baud, find): s = '' for c in buffer: s += chr(ord(c)) - #print(s) if s == find: print('\a') # Ding print("Ding! " + str(find) + " found at " + datetime.now().isoformat()) - print(str(ser.readline())) + if (printLines > 0): + for i in range (printLines): + print(str(ser.readline())[2:-1]) if __name__ == "__main__": @@ -35,9 +36,12 @@ def dinger(port, baud, find): parser.add_argument('-find', type=str, default="# => ", help='Ding on this') + parser.add_argument('-print', type=int, default=1, + help='Print following lines') + args = parser.parse_args() - proc = multiprocessing.Process(target = dinger, args = (args.port, args.baud, args.find)) + proc = multiprocessing.Process(target = dinger, args = (args.port, args.baud, args.find, args.print)) proc.start() try: From 2163c378573e0bc2d4aa8c95490db9bca2a161ac Mon Sep 17 00:00:00 2001 From: Paul Clark Date: Mon, 24 Nov 2025 09:28:35 +0000 Subject: [PATCH 13/26] Move sendRTCMToConsumers() outside ringBufferSemaphore --- Firmware/RTK_Everywhere/Tasks.ino | 40 ++++++++++++++++--------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/Firmware/RTK_Everywhere/Tasks.ino b/Firmware/RTK_Everywhere/Tasks.ino index 5f5c088b4..df1ae5f81 100644 --- a/Firmware/RTK_Everywhere/Tasks.ino +++ b/Firmware/RTK_Everywhere/Tasks.ino @@ -1281,8 +1281,9 @@ void processUart1Message(SEMP_PARSE_STATE *parse, uint16_t type) } else { - systemPrintf("processUart1Message could not get ringBuffer semaphore - held by %s\r\n", - ringBufferSemaphoreHolder); + if (settings.debugGnss && (!inMainMenu)) + systemPrintf("processUart1Message could not get ringBuffer semaphore - held by %s\r\n", + ringBufferSemaphoreHolder); } } @@ -1373,6 +1374,21 @@ void handleGnssDataTask(void *e) systemPrintln("handleGnssDataTask running"); } + //---------------------------------------------------------------------- + // Send RTCM to consumers + // + // RTCM has its own storage in rtcmConsumerBuffer (Base.ino) + // It does not use the main ringBuffer + // But we do the writing here so all traffic generated in the same place + //---------------------------------------------------------------------- + + startMillis = millis(); + + sendRTCMToConsumers(); + + if ((millis() - startMillis) > settings.networkClientWriteTimeout_ms) + slowConsumer = "RTCM Consumers"; + usedSpace = 0; // Use a semaphore to prevent handleGnssDataTask from gatecrashing @@ -1384,21 +1400,6 @@ void handleGnssDataTask(void *e) { ringBufferSemaphoreHolder = "handleGnssDataTask"; - //---------------------------------------------------------------------- - // Send RTCM to consumers - // - // RTCM has its own storage in rtcmConsumerBuffer (Base.ino) - // It does not use the main ringBuffer - // But we do the writing here so all traffic generated in the same place - //---------------------------------------------------------------------- - - startMillis = millis(); - - sendRTCMToConsumers(); - - if ((millis() - startMillis) > settings.networkClientWriteTimeout_ms) - slowConsumer = "RTCM Consumers"; - //---------------------------------------------------------------------- // Send data over Bluetooth //---------------------------------------------------------------------- @@ -1831,8 +1832,9 @@ void handleGnssDataTask(void *e) } else { - systemPrintf("handleGnssDataTask could not get ringBuffer semaphore - held by %s\r\n", - ringBufferSemaphoreHolder); + if (settings.debugGnss && (!inMainMenu)) + systemPrintf("handleGnssDataTask could not get ringBuffer semaphore - held by %s\r\n", + ringBufferSemaphoreHolder); } //---------------------------------------------------------------------- From ddc032b59fe84907f42e76d2f7002a5afec3934a Mon Sep 17 00:00:00 2001 From: Paul Clark Date: Mon, 24 Nov 2025 09:30:34 +0000 Subject: [PATCH 14/26] Move NTRIP_SERVER_DATA into NtripServer.ino --- Firmware/RTK_Everywhere/NtripServer.ino | 207 ++++++++++++++++++++++++ Firmware/RTK_Everywhere/settings.h | 188 --------------------- 2 files changed, 207 insertions(+), 188 deletions(-) diff --git a/Firmware/RTK_Everywhere/NtripServer.ino b/Firmware/RTK_Everywhere/NtripServer.ino index 5f16175e8..5867f7974 100644 --- a/Firmware/RTK_Everywhere/NtripServer.ino +++ b/Firmware/RTK_Everywhere/NtripServer.ino @@ -122,6 +122,213 @@ NtripServer.ino =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +#ifdef COMPILE_NETWORK + +// NTRIP Server data +const TickType_t serverSemaphore_shortWait_ms = 10 / portTICK_PERIOD_MS; +const TickType_t serverSemaphore_longWait_ms = 100 / portTICK_PERIOD_MS; +typedef struct +{ + // Network connection used to push RTCM to NTRIP caster + NetworkClient *networkClient; + volatile uint8_t state; + + // Count of bytes sent by the NTRIP server to the NTRIP caster + volatile uint32_t bytesSent; + + // Throttle the time between connection attempts + // ms - Max of 4,294,967,295 or 4.3M seconds or 71,000 minutes or 1193 hours or 49 days between attempts + volatile uint32_t connectionAttemptTimeout; + volatile int connectionAttempts; // Count the number of connection attempts between restarts + + // NTRIP server timer usage: + // * Reconnection delay + // * Measure the connection response time + // * Receive RTCM correction data timeout + // * Monitor last RTCM byte received for frame counting + volatile uint32_t timer; + volatile uint32_t startTime; + volatile int connectionAttemptsTotal; // Count the number of connection attempts absolutely + + // Better debug printing by ntripServerProcessRTCM + volatile uint32_t rtcmBytesSent; + volatile uint32_t previousMilliseconds; + + + // Protect all methods that manipulate timer with a mutex - to avoid race conditions + // Also protect the write from connected checks + SemaphoreHandle_t serverSemaphore = NULL; + + unsigned long millisSinceTimer() + { + unsigned long retVal = 0; + if (serverSemaphore == NULL) + serverSemaphore = xSemaphoreCreateMutex(); + if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) + { + retVal = millis() - timer; + xSemaphoreGive(serverSemaphore); + } + return retVal; + } + + unsigned long millisSinceStartTime() + { + unsigned long retVal = 0; + if (serverSemaphore == NULL) + serverSemaphore = xSemaphoreCreateMutex(); + if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) + { + retVal = millis() - startTime; + xSemaphoreGive(serverSemaphore); + } + return retVal; + } + + void updateTimerAndBytesSent() + { + if (serverSemaphore == NULL) + serverSemaphore = xSemaphoreCreateMutex(); + if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) + { + bytesSent = bytesSent + 1; + rtcmBytesSent = rtcmBytesSent + 1; + timer = millis(); + xSemaphoreGive(serverSemaphore); + } + } + + void updateTimerAndBytesSent(uint16_t dataLength) + { + if (serverSemaphore == NULL) + serverSemaphore = xSemaphoreCreateMutex(); + if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) + { + bytesSent = bytesSent + dataLength; + rtcmBytesSent = rtcmBytesSent + dataLength; + timer = millis(); + xSemaphoreGive(serverSemaphore); + } + } + + bool checkBytesSentAndReset(uint32_t timerLimit, uint32_t *totalBytesSent) + { + bool retVal = false; + if (serverSemaphore == NULL) + serverSemaphore = xSemaphoreCreateMutex(); + if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) + { + if (((millis() - timer) > timerLimit) && (bytesSent > 0)) + { + retVal = true; + *totalBytesSent = bytesSent; + bytesSent = 0; + } + xSemaphoreGive(serverSemaphore); + } + return retVal; + } + + unsigned long getUptime() + { + unsigned long retVal = 0; + if (serverSemaphore == NULL) + serverSemaphore = xSemaphoreCreateMutex(); + if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) + { + retVal = timer - startTime; + xSemaphoreGive(serverSemaphore); + } + return retVal; + } + + void setTimerToMillis() + { + if (serverSemaphore == NULL) + serverSemaphore = xSemaphoreCreateMutex(); + if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) + { + timer = millis(); + xSemaphoreGive(serverSemaphore); + } + } + + bool checkConnectionAttemptTimeout() + { + bool retVal = false; + if (serverSemaphore == NULL) + serverSemaphore = xSemaphoreCreateMutex(); + if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) + { + if ((millis() - timer) >= connectionAttemptTimeout) + { + retVal = true; + } + xSemaphoreGive(serverSemaphore); + } + return retVal; + } + + bool networkClientConnected(bool assumeConnected) + { + bool retVal = assumeConnected; + if (serverSemaphore == NULL) + serverSemaphore = xSemaphoreCreateMutex(); + if (xSemaphoreTake(serverSemaphore, serverSemaphore_longWait_ms) == pdPASS) + { + retVal = (bool)networkClient->connected(); + xSemaphoreGive(serverSemaphore); + } + return retVal; + } + + size_t networkClientWrite(const uint8_t *buf, size_t size) + { + size_t retVal = 0; + if (serverSemaphore == NULL) + serverSemaphore = xSemaphoreCreateMutex(); + if (xSemaphoreTake(serverSemaphore, serverSemaphore_longWait_ms) == pdPASS) + { + retVal = networkClient->write(buf, size); + xSemaphoreGive(serverSemaphore); + } + return retVal; + } + + void networkClientAbsorb() + { + static uint8_t *buffer = nullptr; + const size_t bufferSize = 256; + if (buffer == nullptr) + buffer = (uint8_t *)rtkMalloc(bufferSize, "networkClientAbsorb"); + if (serverSemaphore == NULL) + serverSemaphore = xSemaphoreCreateMutex(); + if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) + { + if (buffer == nullptr) + { + while (networkClient->available()) + networkClient->read(); // Absorb any unwanted incoming traffic + } + else + { + while (networkClient->available()) + { + int bytesRead = networkClient->read(buffer, bufferSize); // Absorb any unwanted incoming traffic + if ((bytesRead > 0) && settings.debugNtripServerRtcm && (!inMainMenu)) + { + systemPrintln("Data received from networkClient:"); + dumpBuffer(buffer, bytesRead); + } + } + } + xSemaphoreGive(serverSemaphore); + } + } +} NTRIP_SERVER_DATA; + +#endif // COMPILE_NETWORK + #ifdef COMPILE_NTRIP_SERVER //---------------------------------------- diff --git a/Firmware/RTK_Everywhere/settings.h b/Firmware/RTK_Everywhere/settings.h index e1832fce7..dd149fab7 100644 --- a/Firmware/RTK_Everywhere/settings.h +++ b/Firmware/RTK_Everywhere/settings.h @@ -385,194 +385,6 @@ enum PeriodDisplayValues #define PERIODIC_SETTING(x) (settings.periodicDisplay & PERIODIC_MASK(x)) #define PERIODIC_TOGGLE(x) settings.periodicDisplay = settings.periodicDisplay ^ PERIODIC_MASK(x) -#ifdef COMPILE_NETWORK - -// NTRIP Server data -const TickType_t serverSemaphore_shortWait_ms = 10 / portTICK_PERIOD_MS; -const TickType_t serverSemaphore_longWait_ms = 100 / portTICK_PERIOD_MS; -typedef struct -{ - // Network connection used to push RTCM to NTRIP caster - NetworkClient *networkClient; - volatile uint8_t state; - - // Count of bytes sent by the NTRIP server to the NTRIP caster - volatile uint32_t bytesSent; - - // Throttle the time between connection attempts - // ms - Max of 4,294,967,295 or 4.3M seconds or 71,000 minutes or 1193 hours or 49 days between attempts - volatile uint32_t connectionAttemptTimeout; - volatile int connectionAttempts; // Count the number of connection attempts between restarts - - // NTRIP server timer usage: - // * Reconnection delay - // * Measure the connection response time - // * Receive RTCM correction data timeout - // * Monitor last RTCM byte received for frame counting - volatile uint32_t timer; - volatile uint32_t startTime; - volatile int connectionAttemptsTotal; // Count the number of connection attempts absolutely - - // Better debug printing by ntripServerProcessRTCM - volatile uint32_t rtcmBytesSent; - volatile uint32_t previousMilliseconds; - - - // Protect all methods that manipulate timer with a mutex - to avoid race conditions - // Also protect the write from connected checks - SemaphoreHandle_t serverSemaphore = NULL; - - unsigned long millisSinceTimer() - { - unsigned long retVal = 0; - if (serverSemaphore == NULL) - serverSemaphore = xSemaphoreCreateMutex(); - if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) - { - retVal = millis() - timer; - xSemaphoreGive(serverSemaphore); - } - return retVal; - } - - unsigned long millisSinceStartTime() - { - unsigned long retVal = 0; - if (serverSemaphore == NULL) - serverSemaphore = xSemaphoreCreateMutex(); - if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) - { - retVal = millis() - startTime; - xSemaphoreGive(serverSemaphore); - } - return retVal; - } - - void updateTimerAndBytesSent() - { - if (serverSemaphore == NULL) - serverSemaphore = xSemaphoreCreateMutex(); - if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) - { - bytesSent = bytesSent + 1; - rtcmBytesSent = rtcmBytesSent + 1; - timer = millis(); - xSemaphoreGive(serverSemaphore); - } - } - - void updateTimerAndBytesSent(uint16_t dataLength) - { - if (serverSemaphore == NULL) - serverSemaphore = xSemaphoreCreateMutex(); - if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) - { - bytesSent = bytesSent + dataLength; - rtcmBytesSent = rtcmBytesSent + dataLength; - timer = millis(); - xSemaphoreGive(serverSemaphore); - } - } - - bool checkBytesSentAndReset(uint32_t timerLimit, uint32_t *totalBytesSent) - { - bool retVal = false; - if (serverSemaphore == NULL) - serverSemaphore = xSemaphoreCreateMutex(); - if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) - { - if (((millis() - timer) > timerLimit) && (bytesSent > 0)) - { - retVal = true; - *totalBytesSent = bytesSent; - bytesSent = 0; - } - xSemaphoreGive(serverSemaphore); - } - return retVal; - } - - unsigned long getUptime() - { - unsigned long retVal = 0; - if (serverSemaphore == NULL) - serverSemaphore = xSemaphoreCreateMutex(); - if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) - { - retVal = timer - startTime; - xSemaphoreGive(serverSemaphore); - } - return retVal; - } - - void setTimerToMillis() - { - if (serverSemaphore == NULL) - serverSemaphore = xSemaphoreCreateMutex(); - if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) - { - timer = millis(); - xSemaphoreGive(serverSemaphore); - } - } - - bool checkConnectionAttemptTimeout() - { - bool retVal = false; - if (serverSemaphore == NULL) - serverSemaphore = xSemaphoreCreateMutex(); - if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) - { - if ((millis() - timer) >= connectionAttemptTimeout) - { - retVal = true; - } - xSemaphoreGive(serverSemaphore); - } - return retVal; - } - - bool networkClientConnected(bool assumeConnected) - { - bool retVal = assumeConnected; - if (serverSemaphore == NULL) - serverSemaphore = xSemaphoreCreateMutex(); - if (xSemaphoreTake(serverSemaphore, serverSemaphore_longWait_ms) == pdPASS) - { - retVal = (bool)networkClient->connected(); - xSemaphoreGive(serverSemaphore); - } - return retVal; - } - - size_t networkClientWrite(const uint8_t *buf, size_t size) - { - size_t retVal = 0; - if (serverSemaphore == NULL) - serverSemaphore = xSemaphoreCreateMutex(); - if (xSemaphoreTake(serverSemaphore, serverSemaphore_longWait_ms) == pdPASS) - { - retVal = networkClient->write(buf, size); - xSemaphoreGive(serverSemaphore); - } - return retVal; - } - - void networkClientAbsorb() - { - if (serverSemaphore == NULL) - serverSemaphore = xSemaphoreCreateMutex(); - if (xSemaphoreTake(serverSemaphore, serverSemaphore_shortWait_ms) == pdPASS) - { - while (networkClient->available()) - networkClient->read(); // Absorb any unwanted incoming traffic - xSemaphoreGive(serverSemaphore); - } - } -} NTRIP_SERVER_DATA; - -#endif // COMPILE_NETWORK - typedef enum { ESPNOW_OFF = 0, From 1de1d1f3a88abb8715245c00229d41fdb168c796 Mon Sep 17 00:00:00 2001 From: Paul Clark Date: Mon, 24 Nov 2025 09:35:51 +0000 Subject: [PATCH 15/26] Remove redundant debugMessagePrefix --- Firmware/RTK_Everywhere/Tasks.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Firmware/RTK_Everywhere/Tasks.ino b/Firmware/RTK_Everywhere/Tasks.ino index df1ae5f81..6e4bd028d 100644 --- a/Firmware/RTK_Everywhere/Tasks.ino +++ b/Firmware/RTK_Everywhere/Tasks.ino @@ -464,8 +464,8 @@ void gnssReadTask(void *e) if ((bytesIncoming < 0) || (bytesIncoming > sizeof(incomingData))) { - systemPrint(debugMessagePrefix); - systemPrintf("gnssReadTask: bytesIncoming = %d\r\n", bytesIncoming); + if (settings.debugGnss) + systemPrintf("gnssReadTask: bytesIncoming = %d\r\n", bytesIncoming); } for (int x = 0; x < bytesIncoming; x++) From 0f581265ae9a6c9b208ddedc40c811b89984a836 Mon Sep 17 00:00:00 2001 From: Paul Clark Date: Mon, 24 Nov 2025 09:39:06 +0000 Subject: [PATCH 16/26] Correct debugMessagePrefix --- Firmware/RTK_Everywhere/RTK_Everywhere.ino | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Firmware/RTK_Everywhere/RTK_Everywhere.ino b/Firmware/RTK_Everywhere/RTK_Everywhere.ino index 35c5c5d02..a2fb5af48 100644 --- a/Firmware/RTK_Everywhere/RTK_Everywhere.ino +++ b/Firmware/RTK_Everywhere/RTK_Everywhere.ino @@ -1699,10 +1699,12 @@ void logUpdate() } else { - if (pin_debug != PIN_UNDEFINED) - systemPrint(debugMessagePrefix); if ((settings.enablePrintLogFileStatus) && (!inMainMenu)) + { + if (pin_debug != PIN_UNDEFINED) + systemPrint(debugMessagePrefix); systemPrintf("No increase in file size: %llu -> %llu\r\n", lastLogSize, logFileSize); + } logIncreasing = false; endSD(false, true); // alreadyHaveSemaphore, releaseSemaphore From d4e188f6b7fa023a93ca9e3602cb1212449108f4 Mon Sep 17 00:00:00 2001 From: Paul Clark Date: Mon, 24 Nov 2025 10:31:20 +0000 Subject: [PATCH 17/26] Require SEMP v1.0.6 --- Firmware/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/Dockerfile b/Firmware/Dockerfile index 0d77432d1..cb1eef806 100644 --- a/Firmware/Dockerfile +++ b/Firmware/Dockerfile @@ -75,7 +75,7 @@ RUN arduino-cli lib install "SparkFun MAX1704x Fuel Gauge Arduino Library"@1.0.4 RUN arduino-cli lib install "SparkFun u-blox GNSS v3"@3.1.10 RUN arduino-cli lib install "SparkFun Qwiic OLED Arduino Library"@1.0.13 RUN arduino-cli lib install SSLClientESP32@2.0.0 -RUN arduino-cli lib install "SparkFun Extensible Message Parser"@1.0.4 +RUN arduino-cli lib install "SparkFun Extensible Message Parser"@1.0.6 RUN arduino-cli lib install "SparkFun BQ40Z50 Battery Manager Arduino Library"@1.0.0 RUN arduino-cli lib install "ArduinoMqttClient"@0.1.8 RUN arduino-cli lib install "SparkFun u-blox PointPerfect Library"@1.11.4 From 7653d4d465dfc3c336308a38921ffdad7b89ef73 Mon Sep 17 00:00:00 2001 From: Paul Clark Date: Mon, 24 Nov 2025 17:05:02 +0000 Subject: [PATCH 18/26] Add ntripServer_CasterEnabled --- Firmware/RTK_Everywhere/Base.ino | 38 +++++++++++-- Firmware/RTK_Everywhere/Developer.ino | 2 +- Firmware/RTK_Everywhere/NVM.ino | 17 ++++++ Firmware/RTK_Everywhere/NtripClient.ino | 12 ++--- Firmware/RTK_Everywhere/NtripServer.ino | 68 +++++++++++++++++++----- Firmware/RTK_Everywhere/menuBase.ino | 49 ++++++++++------- Firmware/RTK_Everywhere/menuCommands.ino | 38 +++++++++++++ Firmware/RTK_Everywhere/settings.h | 11 ++++ 8 files changed, 190 insertions(+), 45 deletions(-) diff --git a/Firmware/RTK_Everywhere/Base.ino b/Firmware/RTK_Everywhere/Base.ino index fb69ad35f..467f32457 100644 --- a/Firmware/RTK_Everywhere/Base.ino +++ b/Firmware/RTK_Everywhere/Base.ino @@ -8,7 +8,9 @@ uint8_t rtcmConsumerBufferHead; uint8_t rtcmConsumerBufferTail; uint8_t *rtcmConsumerBufferPtr = nullptr; +//---------------------------------------- // Allocate and initialize the rtcmConsumerBuffer +//---------------------------------------- bool rtcmConsumerBufferAllocated() { if (rtcmConsumerBufferPtr == nullptr) @@ -30,18 +32,40 @@ bool rtcmConsumerBufferAllocated() return true; } +//---------------------------------------- +// Check how many RTCM buffers contain data +//---------------------------------------- +uint8_t rtcmBuffersInUse() +{ + if (!rtcmConsumerBufferAllocated()) + return 0; + + uint8_t buffersInUse = rtcmConsumerBufferHead - rtcmConsumerBufferTail; + if (buffersInUse >= rtcmConsumerBufferEntries) // Wrap if Tail is > Head + buffersInUse += rtcmConsumerBufferEntries; + return buffersInUse; +} + +//---------------------------------------- +// Check if any RTCM data is available +//---------------------------------------- +bool rtcmDataAvailable() +{ + return (rtcmBuffersInUse() > 0); +} + +//---------------------------------------- // Store each RTCM message in a PSRAM buffer +// This function gets called as each complete RTCM message comes in // The messages are written to the servers by sendRTCMToConsumers +//---------------------------------------- void storeRTCMForConsumers(uint8_t *rtcmData, uint16_t dataLength) { if (!rtcmConsumerBufferAllocated()) return; // Check if a buffer is available - uint8_t buffersInUse = rtcmConsumerBufferHead - rtcmConsumerBufferTail; - if (buffersInUse >= rtcmConsumerBufferEntries) // Wrap if Tail is > Head - buffersInUse += rtcmConsumerBufferEntries; - if (buffersInUse < (rtcmConsumerBufferEntries - 1)) + if (rtcmBuffersInUse() < (rtcmConsumerBufferEntries - 1)) { uint8_t *dest = rtcmConsumerBufferPtr; dest += (size_t)rtcmConsumerBufferEntrySize * (size_t)rtcmConsumerBufferHead; @@ -57,7 +81,9 @@ void storeRTCMForConsumers(uint8_t *rtcmData, uint16_t dataLength) } } +//---------------------------------------- // Send the stored RTCM to consumers: ntripServer, LoRa and ESP-NOW +//---------------------------------------- void sendRTCMToConsumers() { if (!rtcmConsumerBufferAllocated()) @@ -84,8 +110,10 @@ void sendRTCMToConsumers() } } -// This function gets called when an RTCM packet passes parser check in processUart1Message() task +//---------------------------------------- // Store data ready to be passed along to NTRIP Server, or ESP-NOW radio +// This function gets called when an RTCM packet passes parser check in processUart1Message() task +//---------------------------------------- void processRTCM(uint8_t *rtcmData, uint16_t dataLength) { storeRTCMForConsumers(rtcmData, dataLength); diff --git a/Firmware/RTK_Everywhere/Developer.ino b/Firmware/RTK_Everywhere/Developer.ino index e483bfe37..6dde39672 100644 --- a/Firmware/RTK_Everywhere/Developer.ino +++ b/Firmware/RTK_Everywhere/Developer.ino @@ -129,11 +129,11 @@ void ntripClientSettingsChanged() {} #ifndef COMPILE_NTRIP_SERVER bool ntripServerIsCasting(int serverIndex) {return false;} void ntripServerPrintStatus(int serverIndex) {systemPrintf("**NTRIP Server %d not compiled**\r\n", serverIndex);} -void ntripServerProcessRTCM(uint8_t *rtcmData, uint16_t dataLength) {} void ntripServerSendRTCM(int serverIndex, uint8_t *rtcmData, uint16_t dataLength) {} void ntripServerStop(int serverIndex, bool shutdown) {online.ntripServer[serverIndex] = false;} void ntripServerUpdate() {} void ntripServerValidateTables() {} +void ntripServerSettingsChanged(int serverIndex) {} #endif // COMPILE_NTRIP_SERVER //---------------------------------------- diff --git a/Firmware/RTK_Everywhere/NVM.ino b/Firmware/RTK_Everywhere/NVM.ino index 94dc3f771..8f3d58546 100644 --- a/Firmware/RTK_Everywhere/NVM.ino +++ b/Firmware/RTK_Everywhere/NVM.ino @@ -413,6 +413,14 @@ void recordSystemSettingsToFile(File *settingsFile) } } break; + case tNSCEn: { + for (int x = 0; x < rtkSettingsEntries[i].qualifier; x++) + { + settingsFile->printf("%s%d=%d\r\n", rtkSettingsEntries[i].name, x, + settings.ntripServer_CasterEnabled[x]); + } + } + break; case tNSCHost: { for (int x = 0; x < rtkSettingsEntries[i].qualifier; x++) { @@ -1447,6 +1455,15 @@ bool parseLine(char *str) } } break; + case tNSCEn: { + int server; + if (sscanf(suffix, "%d", &server) == 1) + { + settings.ntripServer_CasterEnabled[server] = d; + knownSetting = true; + } + } + break; case tNSCHost: { int server; if (sscanf(suffix, "%d", &server) == 1) diff --git a/Firmware/RTK_Everywhere/NtripClient.ino b/Firmware/RTK_Everywhere/NtripClient.ino index 9dedcb419..9948acc6e 100644 --- a/Firmware/RTK_Everywhere/NtripClient.ino +++ b/Firmware/RTK_Everywhere/NtripClient.ino @@ -200,7 +200,7 @@ unsigned long lastGGAPush; bool ntripClientForcedShutdown = false; // NTRIP Client was turned off due to an error. Don't allow restart. -bool settingsChanged = false; // Goes true when a menu or command modified the client credentials +bool ntripClientSettingsHaveChanged = false; // Goes true when a menu or command modified the client credentials //---------------------------------------- // NTRIP Client Routines @@ -396,9 +396,9 @@ bool ntripClientEnabled(const char **line) enabled = settings.enableNtripClient; // Allow restart if settings change - if(settingsChanged == true) + if(ntripClientSettingsHaveChanged == true) { - settingsChanged = false; + ntripClientSettingsHaveChanged = false; ntripClientForcedShutdown = false; } @@ -600,7 +600,7 @@ void ntripClientSetState(uint8_t newState) // Called from CLI call backs or serial menus to let machine know it can restart the client if it is shut down void ntripClientSettingsChanged() { - settingsChanged = true; + ntripClientSettingsHaveChanged = true; } //---------------------------------------- @@ -916,9 +916,9 @@ void ntripClientUpdate() } // Check if the there have been changes to the client settings - if(settingsChanged == true) + if(ntripClientSettingsHaveChanged == true) { - settingsChanged = false; + ntripClientSettingsHaveChanged = false; ntripClientRestart(); } // Check for timeout receiving NTRIP data diff --git a/Firmware/RTK_Everywhere/NtripServer.ino b/Firmware/RTK_Everywhere/NtripServer.ino index 5867f7974..448a5cef4 100644 --- a/Firmware/RTK_Everywhere/NtripServer.ino +++ b/Firmware/RTK_Everywhere/NtripServer.ino @@ -150,7 +150,7 @@ typedef struct volatile uint32_t startTime; volatile int connectionAttemptsTotal; // Count the number of connection attempts absolutely - // Better debug printing by ntripServerProcessRTCM + // Better debug printing by ntripServerSendRTCM volatile uint32_t rtcmBytesSent; volatile uint32_t previousMilliseconds; @@ -382,6 +382,14 @@ const RtkMode_t ntripServerMode = RTK_MODE_BASE_FIXED; // NTRIP Servers static NTRIP_SERVER_DATA ntripServerArray[NTRIP_SERVER_MAX]; +bool ntripServerSettingsHaveChanged[NTRIP_SERVER_MAX] = +{ + false, + false, + false, + false, +}; // Goes true when a menu or command modified the server credentials + //---------------------------------------- // NTRIP Server Routines //---------------------------------------- @@ -412,9 +420,12 @@ bool ntripServerConnectCaster(int serverIndex) systemPrintf("NTRIP Server %d connecting to %s:%d\r\n", serverIndex, settings.ntripServer_CasterHost[serverIndex], settings.ntripServer_CasterPort[serverIndex]); - // Attempt a connection to the NTRIP caster + // Record the settings as unchanged + ntripServerSettingsHaveChanged[serverIndex] = false; + + // Attempt a connection to the NTRIP caster - using the full default 3000ms _timeout if (!ntripServer->networkClient->connect(settings.ntripServer_CasterHost[serverIndex], - settings.ntripServer_CasterPort[serverIndex])) + settings.ntripServer_CasterPort[serverIndex], 3000)) { if (settings.debugNtripServerState) systemPrintf("NTRIP Server %d connection to NTRIP caster %s:%d failed\r\n", serverIndex, @@ -513,13 +524,16 @@ bool ntripServerEnabled(int serverIndex, const char ** line) } // Verify that the parameters were specified - if ((settings.ntripServer_CasterHost[serverIndex][0] == 0) + if ((settings.ntripServer_CasterEnabled[serverIndex] == false) + || (settings.ntripServer_CasterHost[serverIndex][0] == 0) || (settings.ntripServer_CasterPort[serverIndex] == 0) || (settings.ntripServer_MountPoint[serverIndex][0] == 0)) { if (line) { - if (settings.ntripServer_CasterHost[serverIndex][0] == 0) + if (settings.ntripServer_CasterEnabled[serverIndex] == false) + *line = ", Caster not enabled!"; + else if (settings.ntripServer_CasterHost[serverIndex][0] == 0) *line = ", Caster host not specified!"; else if (settings.ntripServer_CasterPort[serverIndex] == 0) *line = ", Caster port not specified!"; @@ -614,7 +628,7 @@ void ntripServerPrintStatus(int serverIndex) } //---------------------------------------- -// This function gets called as each complete RTCM message comes in +// This function sends stored, complete RTCM messages to connected servers //---------------------------------------- void ntripServerSendRTCM(int serverIndex, uint8_t *rtcmData, uint16_t dataLength) { @@ -676,12 +690,6 @@ void ntripServerSendRTCM(int serverIndex, uint8_t *rtcmData, uint16_t dataLength } } } - - // Indicate that the GNSS is providing correction data - else if (ntripServer->state == NTRIP_SERVER_WAIT_GNSS_DATA) - { - ntripServerSetState(serverIndex, NTRIP_SERVER_CONNECTING); - } } //---------------------------------------- @@ -799,6 +807,8 @@ void ntripServerStop(int serverIndex, bool shutdown) { if (settings.debugNtripServerState) systemPrintf("NTRIP Server %d shutdown requested!\r\n", serverIndex); + if (settings.debugNtripServerState && (!settings.ntripServer_CasterEnabled[serverIndex])) + systemPrintf("NTRIP Server %d caster not enabled!\r\n", serverIndex); if (settings.debugNtripServerState && (!settings.ntripServer_CasterHost[serverIndex][0])) systemPrintf("NTRIP Server %d caster host not configured!\r\n", serverIndex); if (settings.debugNtripServerState && (!settings.ntripServer_CasterPort[serverIndex])) @@ -847,6 +857,13 @@ void ntripServerUpdate(int serverIndex) if (!enabled && (ntripServer->state > NTRIP_SERVER_OFF)) ntripServerShutdown(serverIndex); + // Determine if the settings have changed + else if (ntripServerSettingsHaveChanged[serverIndex]) + { + ntripServerSettingsHaveChanged[serverIndex] = false; + ntripServerRestart(serverIndex); + } + // Determine if the network has failed else if ((ntripServer->state > NTRIP_SERVER_WAIT_FOR_NETWORK) && (!connected)) @@ -902,6 +919,13 @@ void ntripServerUpdate(int serverIndex) // Failed to connect to to the network, attempt to restart the network ntripServerRestart(serverIndex); + // Determine if the settings have changed + else if (ntripServerSettingsHaveChanged[serverIndex]) + { + ntripServerSettingsHaveChanged[serverIndex] = false; + ntripServerRestart(serverIndex); + } + else if (settings.enableNtripServer) { // No RTCM correction data sent yet @@ -914,7 +938,11 @@ void ntripServerUpdate(int serverIndex) // Wait for GNSS correction data case NTRIP_SERVER_WAIT_GNSS_DATA: - // State change handled in ntripServerProcessRTCM + // There is a small risk that any other connected servers will absorb the + // data before rtcmDataAvailable() is able to return true. We may need to + // add a data-was-available timer or similar? + if (rtcmDataAvailable()) + ntripServerSetState(serverIndex, NTRIP_SERVER_CONNECTING); break; // Initiate the connection to the NTRIP caster @@ -1046,6 +1074,14 @@ void ntripServerUpdate(int serverIndex) settings.ntripServer_CasterHost[serverIndex]); ntripServerRestart(serverIndex); } + // Determine if the settings have changed + else if (ntripServerSettingsHaveChanged[serverIndex]) + { + systemPrintf("NTRIP Server %d breaking connection to %s - settings changed!\r\n", serverIndex, + settings.ntripServer_CasterHost[serverIndex]); + ntripServerSettingsHaveChanged[serverIndex] = false; + ntripServerRestart(serverIndex); + } else if (ntripServer->millisSinceTimer() > (10 * 1000)) { // GNSS stopped sending RTCM correction data @@ -1105,4 +1141,10 @@ void ntripServerValidateTables() reportFatalError("Fix ntripServerStateNameEntries to match NTRIPServerState"); } +// Called from CLI call backs or serial menus to let machine know it can restart the server +void ntripServerSettingsChanged(int serverIndex) +{ + ntripServerSettingsHaveChanged[serverIndex] = true; +} + #endif // COMPILE_NTRIP_SERVER diff --git a/Firmware/RTK_Everywhere/menuBase.ino b/Firmware/RTK_Everywhere/menuBase.ino index 8c279198c..b7657a58e 100644 --- a/Firmware/RTK_Everywhere/menuBase.ino +++ b/Firmware/RTK_Everywhere/menuBase.ino @@ -120,17 +120,21 @@ void menuBase() { systemPrintf("NTRIP Server #%d\r\n", serverIndex + 1); - systemPrintf("%d) Set Caster Address: %s\r\n", (0 + (serverIndex * 6)) + ntripServerOptionOffset, + int menuEntry = (serverIndex * 7) + ntripServerOptionOffset; + systemPrintf("%d) Caster: %s%s\r\n", 0 + menuEntry, + (menuEntry < 10) ? " " : "", + settings.ntripServer_CasterEnabled[serverIndex] ? "Enabled" : "Disabled"); + systemPrintf("%d) Set Caster Address: %s\r\n", 1 + menuEntry, &settings.ntripServer_CasterHost[serverIndex][0]); - systemPrintf("%d) Set Caster Port: %d\r\n", (1 + (serverIndex * 6)) + ntripServerOptionOffset, + systemPrintf("%d) Set Caster Port: %d\r\n", 2 + menuEntry, settings.ntripServer_CasterPort[serverIndex]); - systemPrintf("%d) Set Caster User: %s\r\n", (2 + (serverIndex * 6)) + ntripServerOptionOffset, + systemPrintf("%d) Set Caster User: %s\r\n", 3 + menuEntry, &settings.ntripServer_CasterUser[serverIndex][0]); - systemPrintf("%d) Set Caster User PW: %s\r\n", (3 + (serverIndex * 6)) + ntripServerOptionOffset, + systemPrintf("%d) Set Caster User PW: %s\r\n", 4 + menuEntry, &settings.ntripServer_CasterUserPW[serverIndex][0]); - systemPrintf("%d) Set Mountpoint: %s\r\n", (4 + (serverIndex * 6)) + ntripServerOptionOffset, + systemPrintf("%d) Set Mountpoint: %s\r\n", 5 + menuEntry, &settings.ntripServer_MountPoint[serverIndex][0]); - systemPrintf("%d) Set Mountpoint PW: %s\r\n", (5 + (serverIndex * 6)) + ntripServerOptionOffset, + systemPrintf("%d) Set Mountpoint PW: %s\r\n", 6 + menuEntry, &settings.ntripServer_MountPointPW[serverIndex][0]); } } @@ -334,23 +338,28 @@ void menuBase() // NTRIP Server entries else if ((settings.enableNtripServer == true) && (incoming >= ntripServerOptionOffset) && - incoming < (ntripServerOptionOffset + 6 * NTRIP_SERVER_MAX)) + incoming < (ntripServerOptionOffset + 7 * NTRIP_SERVER_MAX)) { // Down adjust user's selection incoming -= ntripServerOptionOffset; - int serverNumber = incoming / 6; - incoming -= (serverNumber * 6); + int serverNumber = incoming / 7; + incoming -= (serverNumber * 7); if (incoming == 0) + { + settings.ntripServer_CasterEnabled[serverNumber] ^= 1; // Toggle + ntripServerSettingsChanged(serverNumber); // Notify the NTRIP Server state machine of new credentials + } + else if (incoming == 1) { systemPrintf("Enter Caster Address for Server %d: ", serverNumber + 1); if (getUserInputString(&settings.ntripServer_CasterHost[serverNumber][0], NTRIP_SERVER_STRING_SIZE) == INPUT_RESPONSE_VALID) { - // NTRIP Server state machine will update automatically + ntripServerSettingsChanged(serverNumber); // Notify the NTRIP Server state machine of new credentials } } - else if (incoming == 1) + else if (incoming == 2) { // Arbitrary 99k max port # char tempString[100]; @@ -362,10 +371,10 @@ void menuBase() if (getNewSetting(tempString, 1, 99999, &settings.ntripServer_CasterPort[serverNumber]) == INPUT_RESPONSE_VALID) { - // NTRIP Server state machine will update automatically + ntripServerSettingsChanged(serverNumber); // Notify the NTRIP Server state machine of new credentials } } - else if (incoming == 2) + else if (incoming == 3) { if (strlen(settings.ntripServer_CasterHost[serverNumber]) > 0) systemPrintf("Enter Caster User for %s: ", settings.ntripServer_CasterHost[serverNumber]); @@ -375,10 +384,10 @@ void menuBase() if (getUserInputString(&settings.ntripServer_CasterUser[serverNumber][0], NTRIP_SERVER_STRING_SIZE) == INPUT_RESPONSE_VALID) { - // NTRIP Server state machine will update automatically + ntripServerSettingsChanged(serverNumber); // Notify the NTRIP Server state machine of new credentials } } - else if (incoming == 3) + else if (incoming == 4) { if (strlen(settings.ntripServer_MountPoint[serverNumber]) > 0) systemPrintf("Enter password for Caster User %s: ", settings.ntripServer_CasterUser[serverNumber]); @@ -388,10 +397,10 @@ void menuBase() if (getUserInputString(&settings.ntripServer_CasterUserPW[serverNumber][0], NTRIP_SERVER_STRING_SIZE) == INPUT_RESPONSE_VALID) { - // NTRIP Server state machine will update automatically + ntripServerSettingsChanged(serverNumber); // Notify the NTRIP Server state machine of new credentials } } - else if (incoming == 4) + else if (incoming == 5) { if (strlen(settings.ntripServer_CasterHost[serverNumber]) > 0) systemPrintf("Enter Mount Point for %s: ", settings.ntripServer_CasterHost[serverNumber]); @@ -401,10 +410,10 @@ void menuBase() if (getUserInputString(&settings.ntripServer_MountPoint[serverNumber][0], NTRIP_SERVER_STRING_SIZE) == INPUT_RESPONSE_VALID) { - // NTRIP Server state machine will update automatically + ntripServerSettingsChanged(serverNumber); // Notify the NTRIP Server state machine of new credentials } } - else if (incoming == 5) + else if (incoming == 6) { if (strlen(settings.ntripServer_MountPoint[serverNumber]) > 0) systemPrintf("Enter password for Mount Point %s: ", settings.ntripServer_MountPoint[serverNumber]); @@ -414,7 +423,7 @@ void menuBase() if (getUserInputString(&settings.ntripServer_MountPointPW[serverNumber][0], NTRIP_SERVER_STRING_SIZE) == INPUT_RESPONSE_VALID) { - // NTRIP Server state machine will update automatically + ntripServerSettingsChanged(serverNumber); // Notify the NTRIP Server state machine of new credentials } } } diff --git a/Firmware/RTK_Everywhere/menuCommands.ino b/Firmware/RTK_Everywhere/menuCommands.ino index cdac62c90..1ee2f9f07 100644 --- a/Firmware/RTK_Everywhere/menuCommands.ino +++ b/Firmware/RTK_Everywhere/menuCommands.ino @@ -1073,6 +1073,15 @@ SettingValueResponse updateSettingWithValue(bool inCommands, const char *setting } } break; + case tNSCEn: { + int server; + if (sscanf(suffix, "%d", &server) == 1) + { + settings.ntripServer_CasterEnabled[server] = (bool)settingValue; + knownSetting = true; + } + } + break; case tNSCHost: { int server; if (sscanf(suffix, "%d", &server) == 1) @@ -1900,6 +1909,16 @@ void createSettingsString(char *newSettings) } } break; + case tNSCEn: { + for (int x = 0; x < rtkSettingsEntries[i].qualifier; x++) + { + char tempString[50]; + snprintf(tempString, sizeof(tempString), "%s%d,%d,", rtkSettingsEntries[i].name, x, + settings.ntripServer_CasterEnabled[x]); + stringRecord(newSettings, tempString); + } + } + break; case tNSCHost: { for (int x = 0; x < rtkSettingsEntries[i].qualifier; x++) { @@ -2785,6 +2804,15 @@ SettingValueResponse getSettingValue(bool inCommands, const char *settingName, c } } break; + case tNSCEn: { + int server; + if (sscanf(suffix, "%d", &server) == 1) + { + writeToString(settingValueStr, settings.ntripServer_CasterEnabled[server]); + knownSetting = true; + } + } + break; case tNSCHost: { int server; if (sscanf(suffix, "%d", &server) == 1) @@ -3421,6 +3449,16 @@ void commandList(bool inCommands, int i) } } break; + case tNSCEn: { + for (int x = 0; x < rtkSettingsEntries[i].qualifier; x++) + { + snprintf(settingName, sizeof(settingName), "%s%d", rtkSettingsEntries[i].name, x); + + getSettingValue(inCommands, settingName, settingValue); + commandSendExecuteListResponse(settingName, "bool", settingValue); + } + } + break; case tNSCHost: { for (int x = 0; x < rtkSettingsEntries[i].qualifier; x++) { diff --git a/Firmware/RTK_Everywhere/settings.h b/Firmware/RTK_Everywhere/settings.h index dd149fab7..184fb9f61 100644 --- a/Firmware/RTK_Everywhere/settings.h +++ b/Firmware/RTK_Everywhere/settings.h @@ -786,6 +786,13 @@ struct Settings bool debugNtripServerState = false; bool enableNtripServer = false; bool enableRtcmMessageChecking = false; + bool ntripServer_CasterEnabled[NTRIP_SERVER_MAX] = + { + false, + false, + false, + false, + }; char ntripServer_CasterHost[NTRIP_SERVER_MAX][NTRIP_SERVER_STRING_SIZE] = // It's free... { "rtk2go.com", @@ -1153,6 +1160,9 @@ typedef enum { tCmnRtNm, tCnRtRtB, tCnRtRtR, + + tNSCEn, + // Add new settings types above <----------------> // (Maintain the enum of existing settings types!) } RTK_Settings_Types; @@ -1526,6 +1536,7 @@ const RTK_Settings_Entry rtkSettingsEntries[] = { 0, 0, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _bool, 0, & settings.debugNtripServerState, "debugNtripServerState", nullptr, }, { 1, 1, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _bool, 0, & settings.enableNtripServer, "enableNtripServer", nullptr, }, { 0, 0, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _bool, 0, & settings.enableRtcmMessageChecking, "enableRtcmMessageChecking", nullptr, }, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, ALL, 1, tNSCEn, NTRIP_SERVER_MAX, & settings.ntripServer_CasterEnabled[0], "ntripServerCasterEnabled_", nullptr, }, { 1, 1, 1, 1, 1, 1, 1, 1, 1, ALL, 1, tNSCHost, NTRIP_SERVER_MAX, & settings.ntripServer_CasterHost[0], "ntripServerCasterHost_", nullptr, }, { 1, 1, 1, 1, 1, 1, 1, 1, 1, ALL, 1, tNSCPort, NTRIP_SERVER_MAX, & settings.ntripServer_CasterPort[0], "ntripServerCasterPort_", nullptr, }, { 1, 1, 1, 1, 1, 1, 1, 1, 1, ALL, 1, tNSCUser, NTRIP_SERVER_MAX, & settings.ntripServer_CasterUser[0], "ntripServerCasterUser_", nullptr, }, From 21739da2cc918bd0518f35ad709a90eb3284a6de Mon Sep 17 00:00:00 2001 From: Paul Clark Date: Tue, 25 Nov 2025 13:05:23 +0000 Subject: [PATCH 19/26] Add ntripServerCasterEnabled to web config --- Firmware/RTK_Everywhere/AP-Config/index.html | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Firmware/RTK_Everywhere/AP-Config/index.html b/Firmware/RTK_Everywhere/AP-Config/index.html index c16fc0aaa..83283d9ac 100644 --- a/Firmware/RTK_Everywhere/AP-Config/index.html +++ b/Firmware/RTK_Everywhere/AP-Config/index.html @@ -903,6 +903,11 @@
+
+ + +
+