diff --git a/Firmware/RTK_Everywhere/AP-Config/src/main.js b/Firmware/RTK_Everywhere/AP-Config/src/main.js
index 5552ae69a..50822d42c 100644
--- a/Firmware/RTK_Everywhere/AP-Config/src/main.js
+++ b/Firmware/RTK_Everywhere/AP-Config/src/main.js
@@ -1004,8 +1004,8 @@ function validateFields() {
//System Config
if (ge("enableLogging").checked == true) {
- checkElementValue("maxLogTime", 1, 1051200, "Must be 1 to 1,051,200", "collapseSystemConfig");
- checkElementValue("maxLogLength", 1, 1051200, "Must be 1 to 1,051,200", "collapseSystemConfig");
+ checkElementValue("maxLogTime", 0, 1051200, "Must be 0 to 1,051,200", "collapseSystemConfig");
+ checkElementValue("maxLogLength", 0, 2880, "Must be 0 to 2880", "collapseSystemConfig");
}
else {
clearElement("maxLogTime", 60 * 24);
diff --git a/Firmware/RTK_Everywhere/Begin.ino b/Firmware/RTK_Everywhere/Begin.ino
index ed582782d..c5ad98578 100644
--- a/Firmware/RTK_Everywhere/Begin.ino
+++ b/Firmware/RTK_Everywhere/Begin.ino
@@ -843,10 +843,10 @@ void beginSD()
gotSemaphore = false;
- while (settings.enableSD == true)
+ while (settings.enableSD == true) // Note: settings.enableSD is never set to false
{
// Setup SD card access semaphore
- if (sdCardSemaphore == nullptr)
+ if (sdCardSemaphore == NULL)
sdCardSemaphore = xSemaphoreCreateMutex();
else if (xSemaphoreTake(sdCardSemaphore, fatSemaphore_shortWait_ms) != pdPASS)
{
@@ -862,7 +862,7 @@ void beginSD()
break; // Give up on loop
// If an SD card is present, allow SdFat to take over
- log_d("SD card detected");
+ systemPrintf("SD card detected @ %s\r\n", getTimeStamp());
// Allocate the data structure that manages the microSD card
if (!sd)
@@ -930,7 +930,7 @@ void beginSD()
sdCardSize = 0;
outOfSDSpace = true;
- systemPrintln("microSD: Online");
+ systemPrintf("microSD: Online @ %s\r\n", getTimeStamp());
online.microSD = true;
break;
}
@@ -951,7 +951,7 @@ void endSD(bool alreadyHaveSemaphore, bool releaseSemaphore)
sd->end();
online.microSD = false;
- systemPrintln("microSD: Offline");
+ systemPrintf("microSD: Offline @ %s\r\n", getTimeStamp());
}
// Free the caches for the microSD card
@@ -1699,7 +1699,7 @@ void tpISR()
{
if (online.rtc) // Only sync if the RTC has been set via PVT first
{
- if (timTpUpdated) // Only sync if timTpUpdated is true
+ if (timTpUpdated) // Only sync if timTpUpdated is true - set by storeTIMTPdata on ZED platforms only
{
if (millisNow - lastRTCSync >
syncRTCInterval) // Only sync if it is more than syncRTCInterval since the last sync
diff --git a/Firmware/RTK_Everywhere/Buttons.ino b/Firmware/RTK_Everywhere/Buttons.ino
index 76c240300..3a6a7d644 100644
--- a/Firmware/RTK_Everywhere/Buttons.ino
+++ b/Firmware/RTK_Everywhere/Buttons.ino
@@ -119,7 +119,7 @@ void buttonRead()
gpioChanged = false;
// Get all the pins in one read
- uint8_t currentState = io.getInputRegister() & 0b00111111; // Ignore unconnected GPIO6/7
+ uint8_t currentState = io.getInputRegister() & 0b00011111; // Mask the five buttons. Ignore SD detect
if (currentState != gpioExpander_previousState)
{
diff --git a/Firmware/RTK_Everywhere/ESPNOW.ino b/Firmware/RTK_Everywhere/ESPNOW.ino
index 3a444e985..b725a4a84 100644
--- a/Firmware/RTK_Everywhere/ESPNOW.ino
+++ b/Firmware/RTK_Everywhere/ESPNOW.ino
@@ -161,6 +161,7 @@ void espNowOnDataReceived(const esp_now_recv_info *mac, const uint8_t *incomingD
{
// Pass RTCM bytes (presumably) from ESP NOW out ESP32-UART to GNSS
gnss->pushRawData((uint8_t *)incomingData, len);
+ sempParseNextBytes(rtcmParse, (uint8_t *)incomingData, len); // Parse the data for RTCM1005/1006
if ((settings.debugEspNow == true || settings.debugCorrections == true) && !inMainMenu)
systemPrintf("ESPNOW received %d RTCM bytes, pushed to GNSS, RSSI: %d\r\n", len, espNowRSSI);
diff --git a/Firmware/RTK_Everywhere/GNSS.ino b/Firmware/RTK_Everywhere/GNSS.ino
index d4cc47ec9..09719b908 100644
--- a/Firmware/RTK_Everywhere/GNSS.ino
+++ b/Firmware/RTK_Everywhere/GNSS.ino
@@ -72,29 +72,52 @@ bool GNSS::supportsAntennaShortOpen()
}
// Periodically push GGA sentence over NTRIP Client, to Caster, if enabled
-void pushGPGGA(char *ggaData)
+// We must not push to the Caster while we are reading data from the Caster
+// See #695
+// pushGPGGA is called by processUart1Message from gnssReadTask
+// ntripClient->read is called by ntripClientUpdate and ntripClientResponse from networkUpdate from loop
+// We need to make sure processUart1Message doesn't gatecrash
+// If ggaData is provided, store it. If ggaData is nullptr, try to push it
+static void pushGPGGA(char *ggaData)
{
-#ifdef COMPILE_NETWORK
- // Wait until the client has been created
- if (ntripClient != nullptr)
+ static char storedGPGGA[100];
+
+ static SemaphoreHandle_t reentrant = xSemaphoreCreateMutex(); // Create the mutex
+
+ if (xSemaphoreTake(reentrant, 10 / portTICK_PERIOD_MS) == pdPASS)
{
- // Provide the caster with our current position as needed
- if (ntripClient->connected() && settings.ntripClient_TransmitGGA == true)
+ if (ggaData)
{
- if (millis() - lastGGAPush > NTRIPCLIENT_MS_BETWEEN_GGA)
- {
- lastGGAPush = millis();
+ snprintf(storedGPGGA, sizeof(storedGPGGA), "%s", ggaData);
+ xSemaphoreGive(reentrant);
+ return;
+ }
- if (settings.debugNtripClientRtcm || PERIODIC_DISPLAY(PD_NTRIP_CLIENT_GGA))
+#ifdef COMPILE_NETWORK
+ // Wait until the client has been created
+ if (ntripClient != nullptr)
+ {
+ // Provide the caster with our current position as needed
+ if (ntripClient->connected() && settings.ntripClient_TransmitGGA == true)
+ {
+ if (millis() - lastGGAPush > NTRIPCLIENT_MS_BETWEEN_GGA)
{
- PERIODIC_CLEAR(PD_NTRIP_CLIENT_GGA);
- systemPrintf("NTRIP Client pushing GGA to server: %s", (const char *)ggaData);
- }
+ lastGGAPush = millis();
- // Push our current GGA sentence to caster
- ntripClient->print((const char *)ggaData);
+ if ((settings.debugNtripClientRtcm || PERIODIC_DISPLAY(PD_NTRIP_CLIENT_GGA)) && !inMainMenu)
+ {
+ PERIODIC_CLEAR(PD_NTRIP_CLIENT_GGA);
+ systemPrintf("NTRIP Client pushing GGA to server: %s", (const char *)storedGPGGA);
+ }
+
+ // Push our current GGA sentence to caster
+ if (strlen(storedGPGGA) > 0)
+ ntripClient->write((const uint8_t *)storedGPGGA, strlen(storedGPGGA));
+ }
}
}
- }
#endif // COMPILE_NETWORK
+
+ xSemaphoreGive(reentrant);
+ }
}
diff --git a/Firmware/RTK_Everywhere/GNSS_ZED.ino b/Firmware/RTK_Everywhere/GNSS_ZED.ino
index 4f1533373..09d5a08d7 100644
--- a/Firmware/RTK_Everywhere/GNSS_ZED.ino
+++ b/Firmware/RTK_Everywhere/GNSS_ZED.ino
@@ -542,10 +542,6 @@ bool GNSS_ZED::configureGNSS()
_zed->setAutoPVTcallbackPtr(&storePVTdata, VAL_LAYER_ALL); // Enable automatic NAV PVT messages with callback to storePVTdata
response &= _zed->setAutoHPPOSLLHcallbackPtr(
&storeHPdata, VAL_LAYER_ALL); // Enable automatic NAV HPPOSLLH messages with callback to storeHPdata
- _zed->setRTCM1005InputcallbackPtr(
- &storeRTCM1005data); // Configure a callback for RTCM 1005 - parsed from pushRawData
- _zed->setRTCM1006InputcallbackPtr(
- &storeRTCM1006data); // Configure a callback for RTCM 1006 - parsed from pushRawData
if (present.timePulseInterrupt)
response &= _zed->setAutoTIMTPcallbackPtr(
@@ -2598,30 +2594,6 @@ void GNSS_ZED::storeMONCOMMSdataRadio(UBX_MON_COMMS_data_t *ubxDataStruct)
}
}
-//----------------------------------------
-// Callback to save ARPECEF*
-//----------------------------------------
-void storeRTCM1005data(RTCM_1005_data_t *rtcmData1005)
-{
- ARPECEFX = rtcmData1005->AntennaReferencePointECEFX;
- ARPECEFY = rtcmData1005->AntennaReferencePointECEFY;
- ARPECEFZ = rtcmData1005->AntennaReferencePointECEFZ;
- ARPECEFH = 0;
- newARPAvailable = true;
-}
-
-//----------------------------------------
-// Callback to save ARPECEF*
-//----------------------------------------
-void storeRTCM1006data(RTCM_1006_data_t *rtcmData1006)
-{
- ARPECEFX = rtcmData1006->AntennaReferencePointECEFX;
- ARPECEFY = rtcmData1006->AntennaReferencePointECEFY;
- ARPECEFZ = rtcmData1006->AntennaReferencePointECEFZ;
- ARPECEFH = rtcmData1006->AntennaHeight;
- newARPAvailable = true;
-}
-
//----------------------------------------
void storeTIMTPdata(UBX_TIM_TP_data_t *ubxDataStruct)
{
diff --git a/Firmware/RTK_Everywhere/LoRa.ino b/Firmware/RTK_Everywhere/LoRa.ino
index 7901fc3a7..0141ef48a 100644
--- a/Firmware/RTK_Everywhere/LoRa.ino
+++ b/Firmware/RTK_Everywhere/LoRa.ino
@@ -175,6 +175,9 @@ void updateLora()
// Pass RTCM bytes (presumably) from LoRa out ESP32-UART to GNSS
gnss->pushRawData(rtcmData, rtcmCount); // Push RTCM to GNSS module
+ // Parse the data for RTCM1005/1006
+ sempParseNextBytes(rtcmParse, rtcmData, rtcmCount);
+
if (((settings.debugCorrections == true) || (settings.debugLora == true)) && !inMainMenu)
{
systemFlush(); // Complete prints
@@ -254,6 +257,9 @@ void updateLora()
// Pass RTCM bytes (presumably) from LoRa out ESP32-UART to GNSS
gnss->pushRawData(rtcmData, rtcmCount); // Push RTCM to GNSS module
+ // Parse the data for RTCM1005/1006
+ sempParseNextBytes(rtcmParse, rtcmData, rtcmCount);
+
if (((settings.debugCorrections == true) || (settings.debugLora == true)) && !inMainMenu)
{
systemFlush(); // Complete prints
diff --git a/Firmware/RTK_Everywhere/MQTT_Client.ino b/Firmware/RTK_Everywhere/MQTT_Client.ino
index 90ef48162..71081001c 100644
--- a/Firmware/RTK_Everywhere/MQTT_Client.ino
+++ b/Firmware/RTK_Everywhere/MQTT_Client.ino
@@ -514,6 +514,7 @@ int mqttClientProcessZedMessage(uint8_t *mqttData, uint16_t mqttCount, int bytes
zed->updateCorrectionsSource(0); // Set SOURCE to 0 (IP) if needed
gnss->pushRawData(mqttData, mqttCount);
+ // Corrections are SPARTN. No point in pushing them to rtcmParse
bytesPushed += mqttCount;
mqttClientDataReceived = true;
@@ -542,6 +543,7 @@ int mqttClientProcessZedMessage(uint8_t *mqttData, uint16_t mqttCount, int bytes
}
gnss->pushRawData(mqttData, mqttCount);
+ // No point in pushing keys / MGA to rtcmParse
bytesPushed += mqttCount;
}
#endif // COMPILE_ZED
diff --git a/Firmware/RTK_Everywhere/NtripClient.ino b/Firmware/RTK_Everywhere/NtripClient.ino
index a686367d6..8bc8d8097 100644
--- a/Firmware/RTK_Everywhere/NtripClient.ino
+++ b/Firmware/RTK_Everywhere/NtripClient.ino
@@ -138,7 +138,7 @@ static const uint32_t NTRIP_CLIENT_RESPONSE_TIMEOUT = 10 * 1000; // Milliseconds
static const uint32_t NTRIP_CLIENT_RECEIVE_DATA_TIMEOUT = 30 * 1000; // Milliseconds
// Most incoming data is around 500 bytes but may be larger
-static const int RTCM_DATA_SIZE = 512 * 4;
+static const size_t RTCM_DATA_SIZE = 512 * 4;
// NTRIP client server request buffer size
static const int SERVER_BUFFER_SIZE = CREDENTIALS_BUFFER_SIZE + 3;
@@ -228,7 +228,7 @@ bool ntripClientConnect()
systemPrintf("NTRIP Client connecting to %s:%d\r\n", settings.ntripClient_CasterHost,
settings.ntripClient_CasterPort);
- int connectResponse = ntripClient->connect(settings.ntripClient_CasterHost, settings.ntripClient_CasterPort);
+ int connectResponse = ntripClient->connect(settings.ntripClient_CasterHost, settings.ntripClient_CasterPort, NTRIP_CLIENT_RESPONSE_TIMEOUT);
if (connectResponse < 1)
{
@@ -248,7 +248,7 @@ bool ntripClientConnect()
length = strlen(serverRequest);
serverRequest[length++] = '\r';
serverRequest[length++] = '\n';
- serverRequest[length++] = 0;
+ serverRequest[length] = 0;
// Set up the credentials
char credentials[CREDENTIALS_BUFFER_SIZE];
@@ -657,7 +657,8 @@ void ntripClientUpdate()
{
// Allocate the ntripClient structure
networkUseDefaultInterface();
- ntripClient = new NetworkClient();
+ if (!ntripClient)
+ ntripClient = new NetworkClient();
if (!ntripClient)
{
// Failed to allocate the ntripClient structure
@@ -803,6 +804,7 @@ void ntripClientUpdate()
// We don't use a task because we use I2C hardware (and don't have a semaphore).
online.ntripClient = true;
ntripClientStartTime = millis();
+ ntripClient->setConnectionTimeout(NTRIP_CLIENT_RECEIVE_DATA_TIMEOUT);
ntripClientSetState(NTRIP_CLIENT_CONNECTED);
}
}
@@ -919,9 +921,10 @@ void ntripClientUpdate()
{
// Push RTCM to GNSS module over I2C / SPI
gnss->pushRawData(rtcmData, rtcmCount);
+ sempParseNextBytes(rtcmParse, rtcmData, rtcmCount); // Parse the data for RTCM1005/1006
if ((settings.debugCorrections || settings.debugNtripClientRtcm ||
- PERIODIC_DISPLAY(PD_NTRIP_CLIENT_DATA)) &&
+ PERIODIC_DISPLAY(PD_NTRIP_CLIENT_DATA)) &&
(!inMainMenu))
{
PERIODIC_CLEAR(PD_NTRIP_CLIENT_DATA);
@@ -931,7 +934,7 @@ void ntripClientUpdate()
else
{
if ((settings.debugCorrections || settings.debugNtripClientRtcm ||
- PERIODIC_DISPLAY(PD_NTRIP_CLIENT_DATA)) &&
+ PERIODIC_DISPLAY(PD_NTRIP_CLIENT_DATA)) &&
(!inMainMenu))
{
PERIODIC_CLEAR(PD_NTRIP_CLIENT_DATA);
@@ -943,6 +946,9 @@ void ntripClientUpdate()
}
}
}
+
+ // Now that the ntripClient->read is complete, write GPGGA if needed and available. See #695
+ pushGPGGA(nullptr);
}
break;
}
diff --git a/Firmware/RTK_Everywhere/NtripServer.ino b/Firmware/RTK_Everywhere/NtripServer.ino
index 85a5417e5..c8698fd62 100644
--- a/Firmware/RTK_Everywhere/NtripServer.ino
+++ b/Firmware/RTK_Everywhere/NtripServer.ino
@@ -173,7 +173,7 @@ const RtkMode_t ntripServerMode = RTK_MODE_BASE_FIXED;
//----------------------------------------
// NTRIP Servers
-static NTRIP_SERVER_DATA ntripServerArray[NTRIP_SERVER_MAX];
+volatile static NTRIP_SERVER_DATA ntripServerArray[NTRIP_SERVER_MAX];
//----------------------------------------
// NTRIP Server Routines
@@ -184,7 +184,7 @@ static NTRIP_SERVER_DATA ntripServerArray[NTRIP_SERVER_MAX];
//----------------------------------------
bool ntripServerConnectCaster(int serverIndex)
{
- NTRIP_SERVER_DATA *ntripServer = &ntripServerArray[serverIndex];
+ volatile NTRIP_SERVER_DATA *ntripServer = &ntripServerArray[serverIndex];
const int SERVER_BUFFER_SIZE = 512;
char serverBuffer[SERVER_BUFFER_SIZE];
@@ -239,7 +239,7 @@ bool ntripServerConnectLimitReached(int serverIndex)
{
bool limitReached;
int minutes;
- NTRIP_SERVER_DATA *ntripServer = &ntripServerArray[serverIndex];
+ volatile NTRIP_SERVER_DATA *ntripServer = &ntripServerArray[serverIndex];
int seconds;
// Retry the connection a few times
@@ -249,8 +249,8 @@ bool ntripServerConnectLimitReached(int serverIndex)
// Shutdown the NTRIP server
ntripServerStop(serverIndex, limitReached || (!ntripServerEnabled(serverIndex, nullptr)));
- ntripServer->connectionAttempts++;
- ntripServer->connectionAttemptsTotal++;
+ ntripServer->connectionAttempts = ntripServer->connectionAttempts + 1;
+ ntripServer->connectionAttemptsTotal = ntripServer->connectionAttemptsTotal + 1;
if (settings.debugNtripServerState)
ntripServerPrintStatus(serverIndex);
@@ -334,7 +334,7 @@ bool ntripServerEnabled(int serverIndex, const char ** line)
//----------------------------------------
void ntripServerPrintStateSummary(int serverIndex)
{
- NTRIP_SERVER_DATA *ntripServer = &ntripServerArray[serverIndex];
+ volatile NTRIP_SERVER_DATA *ntripServer = &ntripServerArray[serverIndex];
switch (ntripServer->state)
{
@@ -362,7 +362,7 @@ void ntripServerPrintStateSummary(int serverIndex)
//----------------------------------------
void ntripServerPrintStatus(int serverIndex)
{
- NTRIP_SERVER_DATA *ntripServer = &ntripServerArray[serverIndex];
+ volatile NTRIP_SERVER_DATA *ntripServer = &ntripServerArray[serverIndex];
uint64_t milliseconds;
uint32_t days;
byte hours;
@@ -410,7 +410,7 @@ void ntripServerPrintStatus(int serverIndex)
//----------------------------------------
void ntripServerProcessRTCM(int serverIndex, uint8_t incoming)
{
- NTRIP_SERVER_DATA *ntripServer = &ntripServerArray[serverIndex];
+ volatile NTRIP_SERVER_DATA *ntripServer = &ntripServerArray[serverIndex];
if (ntripServer->state == NTRIP_SERVER_CASTING)
{
@@ -425,14 +425,7 @@ void ntripServerProcessRTCM(int serverIndex, uint8_t incoming)
(!settings.enableRtcmMessageChecking) && (!inMainMenu) && ntripServer->bytesSent)
{
PERIODIC_CLEAR(PD_NTRIP_SERVER_DATA);
- printTimeStamp();
- // 1 2 3
- // 123456789012345678901234567890
- // YYYY-mm-dd HH:MM:SS.xxxrn0
- struct tm timeinfo = rtc.getTimeStruct();
- char timestamp[30];
- strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", &timeinfo);
- systemPrintf(" Tx%d RTCM: %s.%03ld, %d bytes sent\r\n", serverIndex, timestamp, rtc.getMillis(),
+ systemPrintf(" Tx%d RTCM: %s, %d bytes sent\r\n", serverIndex, getTimeStamp(),
ntripServer->rtcmBytesSent);
ntripServer->rtcmBytesSent = 0;
}
@@ -452,8 +445,8 @@ void ntripServerProcessRTCM(int serverIndex, uint8_t incoming)
if (ntripServer->networkClient && ntripServer->networkClient->connected())
{
ntripServer->networkClient->write(incoming); // Send this byte to socket
- ntripServer->bytesSent++;
- ntripServer->rtcmBytesSent++;
+ ntripServer->bytesSent = ntripServer->bytesSent + 1;
+ ntripServer->rtcmBytesSent = ntripServer->rtcmBytesSent + 1;
ntripServer->timer = millis();
netOutgoingRTCM = true;
}
@@ -471,7 +464,7 @@ void ntripServerProcessRTCM(int serverIndex, uint8_t incoming)
//----------------------------------------
void ntripServerResponse(int serverIndex, char *response, size_t maxLength)
{
- NTRIP_SERVER_DATA *ntripServer = &ntripServerArray[serverIndex];
+ volatile NTRIP_SERVER_DATA *ntripServer = &ntripServerArray[serverIndex];
char *responseEnd;
// Make sure that we can zero terminate the response
@@ -490,7 +483,7 @@ void ntripServerResponse(int serverIndex, char *response, size_t maxLength)
//----------------------------------------
void ntripServerRestart(int serverIndex)
{
- NTRIP_SERVER_DATA *ntripServer = &ntripServerArray[serverIndex];
+ volatile NTRIP_SERVER_DATA *ntripServer = &ntripServerArray[serverIndex];
// Save the previous uptime value
if (ntripServer->state == NTRIP_SERVER_CASTING)
@@ -503,7 +496,7 @@ void ntripServerRestart(int serverIndex)
//----------------------------------------
void ntripServerSetState(int serverIndex, uint8_t newState)
{
- NTRIP_SERVER_DATA * ntripServer;
+ volatile NTRIP_SERVER_DATA * ntripServer;
ntripServer = &ntripServerArray[serverIndex];
if (settings.debugNtripServerState)
@@ -555,7 +548,7 @@ void ntripServerStop(int serverIndex, bool shutdown)
{
bool enabled;
int index;
- NTRIP_SERVER_DATA *ntripServer = &ntripServerArray[serverIndex];
+ volatile NTRIP_SERVER_DATA *ntripServer = &ntripServerArray[serverIndex];
if (ntripServer->networkClient)
{
@@ -619,7 +612,7 @@ void ntripServerUpdate(int serverIndex)
const char * line = "";
// Get the NTRIP data structure
- NTRIP_SERVER_DATA *ntripServer = &ntripServerArray[serverIndex];
+ volatile NTRIP_SERVER_DATA *ntripServer = &ntripServerArray[serverIndex];
// Shutdown the NTRIP server when the mode or setting changes
DMW_if
diff --git a/Firmware/RTK_Everywhere/PointPerfectLibrary.ino b/Firmware/RTK_Everywhere/PointPerfectLibrary.ino
index 1116dd4ca..93e3175ec 100644
--- a/Firmware/RTK_Everywhere/PointPerfectLibrary.ino
+++ b/Firmware/RTK_Everywhere/PointPerfectLibrary.ino
@@ -48,6 +48,7 @@ void updatePplTask(void *e)
}
gnss->pushRawData(pplRtcmBuffer, rtcmLength);
+ sempParseNextBytes(rtcmParse, pplRtcmBuffer, rtcmLength); // Parse the data for RTCM1005/1006
if (settings.debugCorrections == true && !inMainMenu)
systemPrintf("Received %d RTCM bytes from PPL. Pushed to the GNSS.\r\n", rtcmLength);
diff --git a/Firmware/RTK_Everywhere/RTK_Everywhere.ino b/Firmware/RTK_Everywhere/RTK_Everywhere.ino
index 43627718f..c4b71e954 100644
--- a/Firmware/RTK_Everywhere/RTK_Everywhere.ino
+++ b/Firmware/RTK_Everywhere/RTK_Everywhere.ino
@@ -307,6 +307,7 @@ const int COMMON_COORDINATES_MAX_STATIONS = 50; // Record up to 50 ECEF and Geod
#include //http://librarymanager/All#ESP32Time by FBiego
ESP32Time rtc;
unsigned long syncRTCInterval = 1000; // To begin, sync RTC every second. Interval can be increased once sync'd.
+void printTimeStamp(bool always = false); // Header
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// microSD Interface
@@ -315,6 +316,8 @@ unsigned long syncRTCInterval = 1000; // To begin, sync RTC every second. Interv
void beginSPI(bool force = false); // Header
+// Important note: the firmware currently requires SdFat v2.1.1
+// sd->begin will crash second time around with ~v2.2.3
#include "SdFat.h" //http://librarymanager/All#sdfat_exfat by Bill Greiman.
SdFat *sd;
@@ -322,16 +325,28 @@ SdFat *sd;
SdFile *logFile; // File that all GNSS messages sentences are written to
unsigned long lastUBXLogSyncTime; // Used to record to SD every half second
-int startLogTime_minutes; // Mark when we start any logging so we can stop logging after maxLogTime_minutes
-int startCurrentLogTime_minutes;
-// Mark when we start this specific log file so we can close it after x minutes and start a new one
+int startLogTime_minutes; // Mark when we (re)start any logging so we can stop logging after maxLogTime_minutes
+unsigned long nextLogTime_ms; // Open the next log file at this many millis()
// System crashes if two tasks access a file at the same time
// So we use a semaphore to see if the file system is available
-SemaphoreHandle_t sdCardSemaphore;
+// https://github.com/espressif/arduino-esp32/blob/master/libraries/ESP32/examples/FreeRTOS/Mutex/Mutex.ino#L11
+SemaphoreHandle_t sdCardSemaphore = NULL;
TickType_t loggingSemaphoreWait_ms = 10 / portTICK_PERIOD_MS;
const TickType_t fatSemaphore_shortWait_ms = 10 / portTICK_PERIOD_MS;
const TickType_t fatSemaphore_longWait_ms = 200 / portTICK_PERIOD_MS;
+const TickType_t ringBuffer_shortWait_ms = 20 / portTICK_PERIOD_MS;
+const TickType_t ringBuffer_longWait_ms = 300 / portTICK_PERIOD_MS;
+
+// ringBuffer semaphore - prevent processUart1Message (gnssReadTask) and handleGnssDataTask
+// from gatecrashing each other.
+SemaphoreHandle_t ringBufferSemaphore = NULL;
+const char *ringBufferSemaphoreHolder = "None";
+
+// tcpServer semaphore - prevent tcpServerClientSendData (handleGnssDataTask) and tcpServerUpdate
+// from gatecrashing each other. See #695 for why this is needed.
+SemaphoreHandle_t tcpServerSemaphore = NULL;
+const char *tcpServerSemaphoreHolder = "None";
// Display used/free space in menu and config page
uint64_t sdCardSize;
@@ -460,7 +475,7 @@ uint32_t timTpEpoch;
uint32_t timTpMicros;
unsigned long lastARPLog; // Time of the last ARP log event
-bool newARPAvailable;
+bool newARPAvailable = false;
int64_t ARPECEFX; // ARP ECEF is 38-bit signed
int64_t ARPECEFY;
int64_t ARPECEFZ;
@@ -472,6 +487,7 @@ unsigned long rtcmLastPacketReceived; // Time stamp of RTCM coming in (from BT,
bool usbSerialIncomingRtcm; // Incoming RTCM over the USB serial port
#define RTCM_CORRECTION_INPUT_TIMEOUT (2 * 1000)
+
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Extensible Message Parser
@@ -480,6 +496,7 @@ bool usbSerialIncomingRtcm; // Incoming RTCM over the USB serial port
SEMP_PARSE_STATE *rtkParse = nullptr;
SEMP_PARSE_STATE *sbfParse = nullptr; // mosaic-X5
SEMP_PARSE_STATE *spartnParse = nullptr; // mosaic-X5
+SEMP_PARSE_STATE *rtcmParse = nullptr; // Parse incoming corrections for RTCM1005 / 1006 base locations
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
@@ -806,7 +823,7 @@ std::vector setupButtons; // A vector (linked list) of the setup 'b
bool firstRoverStart; // Used to detect if the user is toggling the power button at POR to enter the test menu
-bool newEventToRecord; // Goes true when INT pin goes high
+bool newEventToRecord; // Goes true when INT pin goes high. Currently this is ZED-specific.
uint32_t triggerCount; // Global copy - TM2 event counter
uint32_t triggerTowMsR; // Global copy - Time Of Week of rising edge (ms)
uint32_t triggerTowSubMsR; // Global copy - Millisecond fraction of Time Of Week of rising edge in nanoseconds
@@ -1222,6 +1239,9 @@ void setup()
beginVersion(); // Assemble platform name. Requires settings/LFS.
+ if ((settings.haltOnPanic) && (esp_reset_reason() == ESP_RST_PANIC)) // Halt on PANIC - to trap rare crashes
+ reportFatalError("ESP_RST_PANIC");
+
DMW_b("beginGnssUart");
beginGnssUart(); // Requires settings. Start the UART connected to the GNSS receiver on core 0. Start before
// gnssBegin in case it is needed (Torch).
@@ -1229,12 +1249,18 @@ void setup()
DMW_b("beginGnssUart2");
beginGnssUart2();
+ DMW_b("beginRtcmParse");
+ beginRtcmParse();
+
DMW_b("displaySplash");
displaySplash(); // Display the RTK product name and firmware version
DMW_b("gnss->begin");
gnss->begin(); // Requires settings. Connect to GNSS to get module type
+ DMW_b("beginButtons");
+ beginButtons(); // Start task for button monitoring. Needed for beginSD (gpioExpander)
+
DMW_b("beginSD");
beginSD(); // Requires settings. Test if SD is present
@@ -1279,9 +1305,6 @@ void setup()
DMW_b("beginInterrupts");
beginInterrupts(); // Begin the TP interrupts
- DMW_b("beginButtons");
- beginButtons(); // Start task for button monitoring.
-
DMW_b("beginSystemState");
beginSystemState(); // Determine initial system state.
@@ -1437,6 +1460,25 @@ void loopDelay()
delay(10);
}
+bool logTimeExceeded() // Limit total logging time to maxLogTime_minutes
+{
+ if (settings.maxLogTime_minutes == 0) // No limit if maxLogTime_minutes is zero
+ return false;
+
+ return ((systemTime_minutes - startLogTime_minutes) >= settings.maxLogTime_minutes);
+}
+
+bool logLengthExceeded() // Limit individual files to maxLogLength_minutes
+{
+ if (settings.maxLogLength_minutes == 0) // No limit if maxLogLength_minutes is zero
+ return false;
+
+ if (nextLogTime_ms == 0) // Keep logging if nextLogTime_ms has not been set
+ return false;
+
+ return (millis() >= nextLogTime_ms); // Note: this will roll over every ~50 days...
+}
+
// Create or close files as needed (startup or as the user changes settings)
// Push new data to log as needed
void logUpdate()
@@ -1458,7 +1500,7 @@ void logUpdate()
if (outOfSDSpace == true)
return; // We can't log if we are out of SD space
- if (online.logging == false && settings.enableLogging == true && blockLogging == false)
+ if (online.logging == false && settings.enableLogging == true && blockLogging == false && !logTimeExceeded())
{
if (beginLogging() == false)
{
@@ -1474,93 +1516,24 @@ void logUpdate()
// Close down file
endSD(false, true);
}
- else if (online.logging == true && settings.enableLogging == true &&
- (systemTime_minutes - startCurrentLogTime_minutes) >= settings.maxLogLength_minutes)
- {
- endSD(false, true); // Close down file. A new one will be created at the next calling of updateLogs().
- }
-
- if (online.logging == true)
+ else if (online.logging == true && settings.enableLogging == true && (logLengthExceeded() || logTimeExceeded()))
{
- // Record any pending trigger events
- if (newEventToRecord == true)
+ if (logTimeExceeded())
{
- systemPrintln("Recording event");
-
- // Record trigger count with Time Of Week of rising edge (ms), Millisecond fraction of Time Of Week of
- // rising edge (ns), and accuracy estimate (ns)
- char eventData[82]; // Max NMEA sentence length is 82
- snprintf(eventData, sizeof(eventData), "%d,%d,%d,%d", triggerCount, triggerTowMsR, triggerTowSubMsR,
- triggerAccEst);
-
- char nmeaMessage[82]; // Max NMEA sentence length is 82
- createNMEASentence(CUSTOM_NMEA_TYPE_EVENT, nmeaMessage, sizeof(nmeaMessage),
- eventData); // textID, buffer, sizeOfBuffer, text
-
- if (xSemaphoreTake(sdCardSemaphore, fatSemaphore_shortWait_ms) == pdPASS)
- {
- markSemaphore(FUNCTION_EVENT);
-
- logFile->println(nmeaMessage);
-
- xSemaphoreGive(sdCardSemaphore);
- newEventToRecord = false;
- }
- else
- {
- char semaphoreHolder[50];
- getSemaphoreFunction(semaphoreHolder);
-
- // While a retry does occur during the next loop, it is possible to lose
- // trigger events if they occur too rapidly or if the log file is closed
- // before the trigger event is written!
- log_w("sdCardSemaphore failed to yield, held by %s, RTK_Everywhere.ino line %d", semaphoreHolder,
- __LINE__);
- }
+ systemPrintln("Log file: maximum logging time reached");
+ endSD(false, true); // Close down SD.
}
-
- // Record the Antenna Reference Position - if available
- if (newARPAvailable == true && settings.enableARPLogging &&
- ((millis() - lastARPLog) > (settings.ARPLoggingInterval_s * 1000)))
+ else
{
- systemPrintln("Recording Antenna Reference Position");
-
- lastARPLog = millis();
- newARPAvailable = false;
-
- double x = ARPECEFX;
- x /= 10000.0; // Convert to m
- double y = ARPECEFY;
- y /= 10000.0; // Convert to m
- double z = ARPECEFZ;
- z /= 10000.0; // Convert to m
- double h = ARPECEFH;
- h /= 10000.0; // Convert to m
- char ARPData[82]; // Max NMEA sentence length is 82
- snprintf(ARPData, sizeof(ARPData), "%.4f,%.4f,%.4f,%.4f", x, y, z, h);
-
- char nmeaMessage[82]; // Max NMEA sentence length is 82
- createNMEASentence(CUSTOM_NMEA_TYPE_ARP_ECEF_XYZH, nmeaMessage, sizeof(nmeaMessage),
- ARPData); // textID, buffer, sizeOfBuffer, text
-
- if (xSemaphoreTake(sdCardSemaphore, fatSemaphore_shortWait_ms) == pdPASS)
- {
- markSemaphore(FUNCTION_EVENT);
-
- logFile->println(nmeaMessage);
-
- xSemaphoreGive(sdCardSemaphore);
- newEventToRecord = false;
- }
- else
- {
- char semaphoreHolder[50];
- getSemaphoreFunction(semaphoreHolder);
- log_w("sdCardSemaphore failed to yield, held by %s, RTK_Everywhere.ino line %d", semaphoreHolder,
- __LINE__);
- }
+ systemPrintln("Log file: log length reached");
+ endLogging(false, true); //(gotSemaphore, releaseSemaphore) Close file. Reset parser stats.
+ beginLogging(); // Create new file based on current RTC.
+ setLoggingType(); // Determine if we are standard, PPP, or custom. Changes logging icon accordingly.
}
+ }
+ if (online.logging == true)
+ {
// Report file sizes to show recording is working
if ((millis() - lastFileReport) > 5000)
{
@@ -1572,7 +1545,7 @@ void logUpdate()
{
systemPrintf("Log file size: %lld", logFileSize);
- if ((systemTime_minutes - startLogTime_minutes) < settings.maxLogTime_minutes)
+ if (!logTimeExceeded())
{
// Calculate generation and write speeds every 5 seconds
uint64_t fileSizeDelta = logFileSize - lastLogSize;
@@ -1807,5 +1780,8 @@ void getSemaphoreFunction(char *functionName)
case FUNCTION_NTPEVENT:
strcpy(functionName, "NTP Event");
break;
+ case FUNCTION_ARPWRITE:
+ strcpy(functionName, "ARP Write");
+ break;
}
}
diff --git a/Firmware/RTK_Everywhere/SD.ino b/Firmware/RTK_Everywhere/SD.ino
index 2cf8e48c7..09d485d75 100644
--- a/Firmware/RTK_Everywhere/SD.ino
+++ b/Firmware/RTK_Everywhere/SD.ino
@@ -27,7 +27,7 @@ void sdUpdate()
}
else if (sdCardPresent() == true) // Poll card to see if a card is inserted
{
- systemPrintln("SD inserted");
+ systemPrintf("SD inserted @ %s\r\n", getTimeStamp());
beginSD(); // Attempt to start SD
}
}
@@ -35,7 +35,7 @@ void sdUpdate()
if (online.logging == true && sdCardSize > 0 &&
sdFreeSpace < sdMinAvailableSpace) // Stop logging if we are below the min
{
- log_d("Logging stopped. SD full.");
+ systemPrintf("Logging stopped. SD full @ %s\r\n", getTimeStamp());
outOfSDSpace = true;
endSD(false, true); //(alreadyHaveSemaphore, releaseSemaphore) Close down file.
return;
@@ -49,7 +49,10 @@ void sdUpdate()
// Check if SD card is still present
if (sdCardPresent() == false)
+ {
+ systemPrintf("SD removed @ %s\r\n", getTimeStamp());
endSD(false, true); //(alreadyHaveSemaphore, releaseSemaphore) Close down SD.
+ }
}
/*
@@ -94,44 +97,75 @@ bool sdCardPresent(void)
return (true); // Card detect high = SD in place
return (false); // Card detect low = No SD
}
- else if (present.microSdCardDetectGpioExpanderHigh == true && online.gpioExpander == true)
+ else if (present.microSdCardDetectGpioExpanderHigh == true)
{
- if (io.digitalRead(gpioExpander_cardDetect) == GPIO_EXPANDER_CARD_INSERTED)
- return (true); // Card detect high = SD in place
- return (false); // Card detect low = No SD
+ if (online.gpioExpander == true)
+ {
+ if (io.digitalRead(gpioExpander_cardDetect) == GPIO_EXPANDER_CARD_INSERTED)
+ return (true); // Card detect high = SD in place
+ return (false); // Card detect low = No SD
+ }
+ else
+ {
+ reportFatalError("sdCardPresent: gpioExpander not online.");
+ return (false);
+ }
}
// else - no card detect pin. Use software detect
- // Use software to detect a card
- DMW_if systemPrintf("pin_microSD_CS: %d\r\n", pin_microSD_CS);
- if (pin_microSD_CS == -1)
- reportFatalError("Illegal SD CS pin assignment.");
+ // Note: even though this is protected by the semaphore,
+ // this will probably cause issues / corruption if
+ // a SdFile is open for writing...?
+
+ static bool previousCardPresentBySW = false;
+
+ if (xSemaphoreTake(sdCardSemaphore, fatSemaphore_longWait_ms) == pdPASS)
+ {
+ markSemaphore(FUNCTION_RECORDSETTINGS);
+
+ // Use software to detect a card
+ DMW_if systemPrintf("pin_microSD_CS: %d\r\n", pin_microSD_CS);
+ if (pin_microSD_CS == -1)
+ reportFatalError("Illegal SD CS pin assignment.");
+
+ byte response = 0;
- byte response = 0;
+ beginSPI(false);
+ SPI.setClockDivider(SPI_CLOCK_DIV2);
+ SPI.setDataMode(SPI_MODE0);
+ SPI.setBitOrder(MSBFIRST);
+ pinMode(pin_microSD_CS, OUTPUT);
- beginSPI(false);
- SPI.setClockDivider(SPI_CLOCK_DIV2);
- SPI.setDataMode(SPI_MODE0);
- SPI.setBitOrder(MSBFIRST);
- pinMode(pin_microSD_CS, OUTPUT);
+ // Sending clocks while card power stabilizes...
+ sdDeselectCard(); // always make sure
+ for (byte i = 0; i < 30; i++) // send several clocks while card power stabilizes
+ xchg(0xff);
+
+ // Sending CMD0 - GO IDLE...
+ for (byte i = 0; i < 0x10; i++) // Attempt to go idle
+ {
+ response = sdSendCommand(SD_GO_IDLE, 0); // send CMD0 - go to idle state
+ if (response == 1)
+ break;
+ }
- // Sending clocks while card power stabilizes...
- sdDeselectCard(); // always make sure
- for (byte i = 0; i < 30; i++) // send several clocks while card power stabilizes
- xchg(0xff);
+ xSemaphoreGive(sdCardSemaphore);
+
+ if (response != 1)
+ {
+ previousCardPresentBySW = false;
+ return (false); // Card failed to respond to idle
+ }
- // Sending CMD0 - GO IDLE...
- for (byte i = 0; i < 0x10; i++) // Attempt to go idle
+ previousCardPresentBySW = true;
+ return (true); // Card detected
+ }
+ else
{
- response = sdSendCommand(SD_GO_IDLE, 0); // send CMD0 - go to idle state
- if (response == 1)
- break;
+ // Could not get semaphore. Return previous state
+ return previousCardPresentBySW;
}
- if (response != 1)
- return (false); // Card failed to respond to idle
-
- return (true); // Card detected
}
/*
diff --git a/Firmware/RTK_Everywhere/States.ino b/Firmware/RTK_Everywhere/States.ino
index 3154afb01..0cc059062 100644
--- a/Firmware/RTK_Everywhere/States.ino
+++ b/Firmware/RTK_Everywhere/States.ino
@@ -764,16 +764,8 @@ void changeState(SystemState newState)
if (!online.rtc)
systemPrintf("%s%s%s%s\r\n", asterisk, initialState, arrow, endingState);
else
- {
// Timestamp the state change
- // 1 2
- // 12345678901234567890123456
- // YYYY-mm-dd HH:MM:SS.xxxrn0
- struct tm timeinfo = rtc.getTimeStruct();
- char s[30];
- strftime(s, sizeof(s), "%Y-%m-%d %H:%M:%S", &timeinfo);
- systemPrintf("%s%s%s%s, %s.%03ld\r\n", asterisk, initialState, arrow, endingState, s, rtc.getMillis());
- }
+ systemPrintf("%s%s%s%s, %s\r\n", asterisk, initialState, arrow, endingState, getTimeStamp());
}
}
diff --git a/Firmware/RTK_Everywhere/Tasks.ino b/Firmware/RTK_Everywhere/Tasks.ino
index bbdeebbdf..9e330b2e8 100644
--- a/Firmware/RTK_Everywhere/Tasks.ino
+++ b/Firmware/RTK_Everywhere/Tasks.ino
@@ -94,6 +94,7 @@ const char *const sbfParserNames[] = {
"SBF",
};
const int sbfParserNameCount = sizeof(sbfParserNames) / sizeof(sbfParserNames[0]);
+
SEMP_PARSE_ROUTINE const spartnParserTable[] = {sempSpartnPreamble};
const int spartnParserCount = sizeof(spartnParserTable) / sizeof(spartnParserTable[0]);
const char *const spartnParserNames[] = {
@@ -101,6 +102,11 @@ const char *const spartnParserNames[] = {
};
const int spartnParserNameCount = sizeof(spartnParserNames) / sizeof(spartnParserNames[0]);
+SEMP_PARSE_ROUTINE const rtcmParserTable[] = { sempRtcmPreamble };
+const int rtcmParserCount = sizeof(rtcmParserTable) / sizeof(rtcmParserTable[0]);
+const char *const rtcmParserNames[] = { "RTCM" };
+const int rtcmParserNameCount = sizeof(rtcmParserNames) / sizeof(rtcmParserNames[0]);
+
//----------------------------------------
// Locals
//----------------------------------------
@@ -287,6 +293,7 @@ void sendGnssBuffer()
{
if (correctionLastSeen(CORR_BLUETOOTH))
{
+ sempParseNextBytes(rtcmParse, bluetoothOutgoingToGnss, bluetoothOutgoingToGnssHead); // Parse the data for RTCM1005/1006
if (gnss->pushRawData(bluetoothOutgoingToGnss, bluetoothOutgoingToGnssHead))
{
if ((settings.debugCorrections || PERIODIC_DISPLAY(PD_GNSS_DATA_TX)) && !inMainMenu)
@@ -779,222 +786,271 @@ void processUart1Message(SEMP_PARSE_STATE *parse, uint16_t type)
parse->length = 0;
}
- // Determine if this message will fit into the ring buffer
- bytesToCopy = parse->length;
- space = availableHandlerSpace;
- use = settings.gnssHandlerBufferSize - space;
- consumer = (char *)slowConsumer;
- if ((bytesToCopy > space) && (!inMainMenu))
+ // If parse->length is zero, we should exit now.
+ // Previously, the code would continue past here and fill rbOffsetArray with 'empty' entries.
+ // E.g. RTCM1019/1042/1046 suppressed above
+ if (parse->length == 0)
+ return;
+
+ // Use a semaphore to prevent handleGnssDataTask from gatecrashing
+ if (ringBufferSemaphore == NULL)
+ ringBufferSemaphore = xSemaphoreCreateMutex(); // Create the mutex
+
+ // Take the semaphore. Long wait. handleGnssDataTask could block
+ // Enable printing of the ring buffer offsets (s d 10) and the SD buffer sizes (s h 7)
+ // to see this in action. No more gatecrashing!
+ if (xSemaphoreTake(ringBufferSemaphore, ringBuffer_longWait_ms) == pdPASS)
{
- int32_t bufferedData;
- int32_t bytesToDiscard;
- int32_t discardedBytes;
- int32_t listEnd;
- int32_t messageLength;
- int32_t previousTail;
- int32_t rbOffsetTail;
-
- // Determine the tail of the ring buffer
- previousTail = dataHead + space + 1;
- if (previousTail >= settings.gnssHandlerBufferSize)
- previousTail -= settings.gnssHandlerBufferSize;
-
- /* The rbOffsetArray holds the offsets into the ring buffer of the
- * start of each of the parsed messages. A head (rbOffsetHead) and
- * tail (rbOffsetTail) offsets are used for this array to insert and
- * remove entries. Typically this task only manipulates the head as
- * new messages are placed into the ring buffer. The handleGnssDataTask
- * normally manipulates the tail as data is removed from the buffer.
- * However this task will manipulate the tail under two conditions:
- *
- * 1. The ring buffer gets full and data must be discarded
- *
- * 2. The rbOffsetArray is too small to hold all of the message
- * offsets for the data in the ring buffer. The array is full
- * when (Head + 1) == Tail
- *
- * Notes:
- * The rbOffsetArray is allocated along with the ring buffer in
- * Begin.ino
- *
- * The first entry rbOffsetArray[0] is initialized to zero (0)
- * in Begin.ino
- *
- * The array always has one entry in it containing the head offset
- * which contains a valid offset into the ringBuffer, handled below
- *
- * The empty condition is Tail == Head
- *
- * The amount of data described by the rbOffsetArray is
- * rbOffsetArray[Head] - rbOffsetArray[Tail]
- *
- * rbOffsetArray ringBuffer
- * .-----------------. .-----------------.
- * | | | |
- * +-----------------+ | |
- * Tail --> | Msg 1 Offset |---------->+-----------------+ <-- Tail n
- * +-----------------+ | Msg 1 |
- * | Msg 2 Offset |--------. | |
- * +-----------------+ | | |
- * | Msg 3 Offset |------. '->+-----------------+
- * +-----------------+ | | Msg 2 |
- * Head --> | Head Offset |--. | | |
- * +-----------------+ | | | |
- * | | | | | |
- * +-----------------+ | | | |
- * | | | '--->+-----------------+
- * +-----------------+ | | Msg 3 |
- * | | | | |
- * +-----------------+ '------->+-----------------+ <-- dataHead
- * | | | |
- */
-
- // Determine the index for the end of the circular ring buffer
- // offset list
- listEnd = rbOffsetHead;
- WRAP_OFFSET(listEnd, 1, rbOffsetEntries);
-
- // Update the tail, walk newest message to oldest message
- rbOffsetTail = rbOffsetHead;
- bufferedData = 0;
- messageLength = 0;
- while ((rbOffsetTail != listEnd) && (bufferedData < use))
+ ringBufferSemaphoreHolder = "processUart1Message";
+
+ // Determine if this message will fit into the ring buffer
+ int32_t discardedBytes = 0;
+ bytesToCopy = parse->length;
+ space = availableHandlerSpace; // Take a copy of availableHandlerSpace here
+ use = settings.gnssHandlerBufferSize - space;
+ consumer = (char *)slowConsumer;
+ if (bytesToCopy > space) // Paul removed the && (!inMainMenu)) check 7-25-25
{
- // Determine the amount of data in the ring buffer up until
- // either the tail or the end of the rbOffsetArray
+ int32_t bufferedData;
+ int32_t bytesToDiscard;
+ int32_t listEnd;
+ int32_t messageLength;
+ int32_t previousTail;
+ int32_t rbOffsetTail;
+
+ // Determine the tail of the ring buffer
+ previousTail = dataHead + space + 1;
+ if (previousTail >= settings.gnssHandlerBufferSize)
+ previousTail -= settings.gnssHandlerBufferSize;
+
+ /* The rbOffsetArray holds the offsets into the ring buffer of the
+ * start of each of the parsed messages. A head (rbOffsetHead) and
+ * tail (rbOffsetTail) offsets are used for this array to insert and
+ * remove entries. Typically this task only manipulates the head as
+ * new messages are placed into the ring buffer. The handleGnssDataTask
+ * normally manipulates the tail as data is removed from the buffer.
+ * However this task will manipulate the tail under two conditions:
+ *
+ * 1. The ring buffer gets full and data must be discarded
+ *
+ * 2. The rbOffsetArray is too small to hold all of the message
+ * offsets for the data in the ring buffer. The array is full
+ * when (Head + 1) == Tail
+ *
+ * Notes:
+ * The rbOffsetArray is allocated along with the ring buffer in
+ * Begin.ino
+ *
+ * The first entry rbOffsetArray[0] is initialized to zero (0)
+ * in Begin.ino
+ *
+ * The array always has one entry in it containing the head offset
+ * which contains a valid offset into the ringBuffer, handled below
+ *
+ * The empty condition is Tail == Head
+ *
+ * The amount of data described by the rbOffsetArray is
+ * rbOffsetArray[Head] - rbOffsetArray[Tail]
+ *
+ * rbOffsetArray ringBuffer
+ * .-----------------. .-----------------.
+ * | | | |
+ * +-----------------+ | |
+ * Tail --> | Msg 1 Offset |---------->+-----------------+ <-- Tail n
+ * +-----------------+ | Msg 1 |
+ * | Msg 2 Offset |--------. | |
+ * +-----------------+ | | |
+ * | Msg 3 Offset |------. '->+-----------------+
+ * +-----------------+ | | Msg 2 |
+ * Head --> | Head Offset |--. | | |
+ * +-----------------+ | | | |
+ * | | | | | |
+ * +-----------------+ | | | |
+ * | | | '--->+-----------------+
+ * +-----------------+ | | Msg 3 |
+ * | | | | |
+ * +-----------------+ '------->+-----------------+ <-- dataHead
+ * | | | |
+ */
+
+ // Determine the index for the end of the circular ring buffer
+ // offset list
+ listEnd = rbOffsetHead;
+ WRAP_OFFSET(listEnd, 1, rbOffsetEntries);
+
+ // Update the tail, walk newest message to oldest message
+ rbOffsetTail = rbOffsetHead;
+ bufferedData = 0;
+ messageLength = 0;
+ while ((rbOffsetTail != listEnd) && (bufferedData < use))
+ {
+ // Determine the amount of data in the ring buffer up until
+ // either the tail or the end of the rbOffsetArray
+ //
+ // | |
+ // | | Valid, still in ring buffer
+ // | Newest |
+ // +-----------+ <-- rbOffsetHead
+ // | |
+ // | | free space
+ // | |
+ // rbOffsetTail --> +-----------+ <-- bufferedData
+ // | ring |
+ // | buffer | <-- used
+ // | data |
+ // +-----------+ Valid, still in ring buffer
+ // | |
+ //
+ messageLength = rbOffsetArray[rbOffsetTail];
+ WRAP_OFFSET(rbOffsetTail, rbOffsetEntries - 1, rbOffsetEntries);
+ messageLength -= rbOffsetArray[rbOffsetTail];
+ if (messageLength < 0)
+ messageLength += settings.gnssHandlerBufferSize;
+ bufferedData += messageLength;
+ }
+
+ // Account for any data in the ring buffer not described by the array
//
// | |
- // | | Valid, still in ring buffer
+ // +-----------+
+ // | Oldest |
+ // | |
+ // | ring |
+ // | buffer | <-- used
+ // | data |
+ // +-----------+ Valid, still in ring buffer
+ // | |
+ // rbOffsetTail --> +-----------+ <-- bufferedData
+ // | |
// | Newest |
// +-----------+ <-- rbOffsetHead
// | |
- // | | free space
+ //
+ if (bufferedData < use)
+ discardedBytes = use - bufferedData;
+
+ // Writing to the SD card, the network or Bluetooth, a partial
+ // message may be written leaving the tail pointer mid-message
+ //
+ // | |
+ // rbOffsetTail --> +-----------+
+ // | Oldest |
// | |
- // rbOffsetTail --> +-----------+ <-- bufferedData
// | ring |
// | buffer | <-- used
- // | data |
- // +-----------+ Valid, still in ring buffer
+ // | data | Valid, still in ring buffer
+ // +-----------+ <--
+ // | |
+ // +-----------+
+ // | |
+ // | Newest |
+ // +-----------+ <-- rbOffsetHead
// | |
//
- messageLength = rbOffsetArray[rbOffsetTail];
- WRAP_OFFSET(rbOffsetTail, rbOffsetEntries - 1, rbOffsetEntries);
- messageLength -= rbOffsetArray[rbOffsetTail];
- if (messageLength < 0)
- messageLength += settings.gnssHandlerBufferSize;
- bufferedData += messageLength;
- }
+ else if (bufferedData > use)
+ {
+ // Remove the remaining portion of the oldest entry in the array
+ discardedBytes = messageLength + use - bufferedData;
+ WRAP_OFFSET(rbOffsetTail, 1, rbOffsetEntries);
+ }
- // Account for any data in the ring buffer not described by the array
- //
- // | |
- // +-----------+
- // | Oldest |
- // | |
- // | ring |
- // | buffer | <-- used
- // | data |
- // +-----------+ Valid, still in ring buffer
- // | |
- // rbOffsetTail --> +-----------+ <-- bufferedData
- // | |
- // | Newest |
- // +-----------+ <-- rbOffsetHead
- // | |
- //
- discardedBytes = 0;
- if (bufferedData < use)
- discardedBytes = use - bufferedData;
+ // rbOffsetTail now points to the beginning of a message in the
+ // ring buffer
+ // Determine the amount of data to discard
+ bytesToDiscard = discardedBytes;
+ if (bytesToDiscard < bytesToCopy)
+ bytesToDiscard = bytesToCopy;
+ if (bytesToDiscard < AMOUNT_OF_RING_BUFFER_DATA_TO_DISCARD)
+ bytesToDiscard = AMOUNT_OF_RING_BUFFER_DATA_TO_DISCARD;
+
+ // Walk the ring buffer messages from oldest to newest
+ while ((discardedBytes < bytesToDiscard) && (rbOffsetTail != rbOffsetHead))
+ {
+ // Determine the length of the oldest message
+ WRAP_OFFSET(rbOffsetTail, 1, rbOffsetEntries);
+ discardedBytes = rbOffsetArray[rbOffsetTail] - previousTail;
+ if (discardedBytes < 0)
+ discardedBytes += settings.gnssHandlerBufferSize;
+ }
- // Writing to the SD card, the network or Bluetooth, a partial
- // message may be written leaving the tail pointer mid-message
- //
- // | |
- // rbOffsetTail --> +-----------+
- // | Oldest |
- // | |
- // | ring |
- // | buffer | <-- used
- // | data | Valid, still in ring buffer
- // +-----------+ <--
- // | |
- // +-----------+
- // | |
- // | Newest |
- // +-----------+ <-- rbOffsetHead
- // | |
- //
- else if (bufferedData > use)
- {
- // Remove the remaining portion of the oldest entry in the array
- discardedBytes = messageLength + use - bufferedData;
- WRAP_OFFSET(rbOffsetTail, 1, rbOffsetEntries);
+ // Discard the oldest data from the ring buffer
+ // Printing the slow consumer is not that useful as any consumer will be
+ // considered 'slow' if its data wraps over the end of the buffer and
+ // needs a second write to clear...
+ if (!inMainMenu)
+ {
+ if (consumer)
+ systemPrintf("Ring buffer full: discarding %d bytes, %s could be slow\r\n", discardedBytes, consumer);
+ else
+ systemPrintf("Ring buffer full: discarding %d bytes\r\n", discardedBytes);
+ Serial.flush(); // TODO - delete me!
+ }
+
+ // Update the tails. This needs semaphore protection
+ updateRingBufferTails(previousTail, rbOffsetArray[rbOffsetTail]);
}
- // rbOffsetTail now points to the beginning of a message in the
- // ring buffer
- // Determine the amount of data to discard
- bytesToDiscard = discardedBytes;
- if (bytesToDiscard < bytesToCopy)
- bytesToDiscard = bytesToCopy;
- if (bytesToDiscard < AMOUNT_OF_RING_BUFFER_DATA_TO_DISCARD)
- bytesToDiscard = AMOUNT_OF_RING_BUFFER_DATA_TO_DISCARD;
-
- // Walk the ring buffer messages from oldest to newest
- while ((discardedBytes < bytesToDiscard) && (rbOffsetTail != rbOffsetHead))
+ if (bytesToCopy > (space + discardedBytes - 1)) // Sanity check
{
- // Determine the length of the oldest message
- WRAP_OFFSET(rbOffsetTail, 1, rbOffsetEntries);
- discardedBytes = rbOffsetArray[rbOffsetTail] - previousTail;
- if (discardedBytes < 0)
- discardedBytes += settings.gnssHandlerBufferSize;
+ systemPrintf("Ring buffer update error %s: bytesToCopy (%d) is > space (%d) + discardedBytes (%d) - 1\r\n",
+ getTimeStamp(), bytesToCopy, space, discardedBytes);
+ Serial.flush(); // Flush Serial - the code is about to go bang...!
+ }
+
+ // Add another message to the ring buffer
+ // Account for this message
+ // Diagnostic prints are provided by settings.enablePrintSDBuffers and the handleGnssDataTask
+ // The semaphore prevents badness here. Previously availableHandlerSpace may have been updated
+ // by handleGnssDataTask
+ availableHandlerSpace = availableHandlerSpace + discardedBytes - bytesToCopy;
+
+ // Copy dataHead so we can update with a single write - redundant with the semaphore
+ RING_BUFFER_OFFSET newDataHead = dataHead;
+
+ // Fill the buffer to the end and then start at the beginning
+ if ((newDataHead + bytesToCopy) > settings.gnssHandlerBufferSize)
+ bytesToCopy = settings.gnssHandlerBufferSize - newDataHead;
+
+ // Display the dataHead offset
+ if (settings.enablePrintRingBufferOffsets && (!inMainMenu))
+ systemPrintf("DH: %4d --> ", newDataHead);
+
+ // Copy the data into the ring buffer
+ memcpy(&ringBuffer[newDataHead], parse->buffer, bytesToCopy);
+ newDataHead = newDataHead + bytesToCopy;
+ if (newDataHead >= settings.gnssHandlerBufferSize)
+ newDataHead = newDataHead - settings.gnssHandlerBufferSize;
+
+ // Determine the remaining bytes
+ remainingBytes = parse->length - bytesToCopy;
+ if (remainingBytes)
+ {
+ // Copy the remaining bytes into the beginning of the ring buffer
+ memcpy(ringBuffer, &parse->buffer[bytesToCopy], remainingBytes);
+ newDataHead = newDataHead + remainingBytes;
+ if (newDataHead >= settings.gnssHandlerBufferSize)
+ newDataHead = newDataHead - settings.gnssHandlerBufferSize;
}
- // Discard the oldest data from the ring buffer
- if (consumer)
- systemPrintf("Ring buffer full: discarding %d bytes, %s is slow\r\n", discardedBytes, consumer);
- else
- systemPrintf("Ring buffer full: discarding %d bytes\r\n", discardedBytes);
- updateRingBufferTails(previousTail, rbOffsetArray[rbOffsetTail]);
- availableHandlerSpace = availableHandlerSpace + discardedBytes;
- }
-
- // Add another message to the ring buffer
- // Account for this message
- availableHandlerSpace = availableHandlerSpace - bytesToCopy;
-
- // Fill the buffer to the end and then start at the beginning
- if ((dataHead + bytesToCopy) > settings.gnssHandlerBufferSize)
- bytesToCopy = settings.gnssHandlerBufferSize - dataHead;
+ // Add the head offset to the offset array
+ WRAP_OFFSET(rbOffsetHead, 1, rbOffsetEntries);
+ rbOffsetArray[rbOffsetHead] = newDataHead;
- // Display the dataHead offset
- if (settings.enablePrintRingBufferOffsets && (!inMainMenu))
- systemPrintf("DH: %4d --> ", dataHead);
+ // Display the dataHead offset
+ if (settings.enablePrintRingBufferOffsets && (!inMainMenu))
+ systemPrintf("%4d @ %s\r\n", newDataHead, getTimeStamp());
- // Copy the data into the ring buffer
- memcpy(&ringBuffer[dataHead], parse->buffer, bytesToCopy);
- dataHead = dataHead + bytesToCopy;
- if (dataHead >= settings.gnssHandlerBufferSize)
- dataHead = dataHead - settings.gnssHandlerBufferSize;
+ // Update dataHead in a single write - redundant with the semaphore
+ // handleGnssDataTask will use it as soon as it updates
+ dataHead = newDataHead;
- // Determine the remaining bytes
- remainingBytes = parse->length - bytesToCopy;
- if (remainingBytes)
+ // handleGnssDataTask will be chomping at the bit. Let it fly!
+ xSemaphoreGive(ringBufferSemaphore);
+ }
+ else
{
- // Copy the remaining bytes into the beginning of the ring buffer
- memcpy(ringBuffer, &parse->buffer[bytesToCopy], remainingBytes);
- dataHead = dataHead + remainingBytes;
- if (dataHead >= settings.gnssHandlerBufferSize)
- dataHead = dataHead - settings.gnssHandlerBufferSize;
+ systemPrintf("processUart1Message could not get ringBuffer semaphore - held by %s\r\n", ringBufferSemaphoreHolder);
}
-
- // Add the head offset to the offset array
- WRAP_OFFSET(rbOffsetHead, 1, rbOffsetEntries);
- rbOffsetArray[rbOffsetHead] = dataHead;
-
- // Display the dataHead offset
- if (settings.enablePrintRingBufferOffsets && (!inMainMenu))
- systemPrintf("%4d\r\n", dataHead);
}
// Remove previous messages from the ring buffer
@@ -1002,10 +1058,11 @@ void updateRingBufferTails(RING_BUFFER_OFFSET previousTail, RING_BUFFER_OFFSET n
{
// Trim any long or medium tails
discardRingBufferBytes(&btRingBufferTail, previousTail, newTail);
- discardRingBufferBytes(&sdRingBufferTail, previousTail, newTail);
tcpClientDiscardBytes(previousTail, newTail);
tcpServerDiscardBytes(previousTail, newTail);
udpServerDiscardBytes(previousTail, newTail);
+ discardRingBufferBytes(&sdRingBufferTail, previousTail, newTail);
+ discardRingBufferBytes(&usbRingBufferTail, previousTail, newTail);
}
// Remove previous messages from the ring buffer
@@ -1056,7 +1113,7 @@ void handleGnssDataTask(void *e)
uint32_t deltaMillis;
int32_t freeSpace;
static uint32_t maxMillis[RBC_MAX];
- uint32_t startMillis;
+ unsigned long startMillis;
int32_t usedSpace;
// Start notification
@@ -1070,6 +1127,7 @@ void handleGnssDataTask(void *e)
tcpServerZeroTail();
udpServerZeroTail();
sdRingBufferTail = 0;
+ usbRingBufferTail = 0;
// Run task until a request is raised
task.handleGnssDataTaskStopRequest = false;
@@ -1084,337 +1142,445 @@ void handleGnssDataTask(void *e)
usedSpace = 0;
- //----------------------------------------------------------------------
- // Send data over Bluetooth
- //----------------------------------------------------------------------
-
- startMillis = millis();
+ // Use a semaphore to prevent handleGnssDataTask from gatecrashing
+ if (ringBufferSemaphore == NULL)
+ ringBufferSemaphore = xSemaphoreCreateMutex(); // Create the mutex
+
+ // Take the semaphore. Short wait. processUart1Message shouldn't block for long
+ if (xSemaphoreTake(ringBufferSemaphore, ringBuffer_shortWait_ms) == pdPASS)
+ {
+ ringBufferSemaphoreHolder = "handleGnssDataTask";
- // Determine BT connection state
- bool connected = (bluetoothGetState() == BT_CONNECTED);
+ //----------------------------------------------------------------------
+ // Send data over Bluetooth
+ //----------------------------------------------------------------------
- if (!connected)
- // Discard the data
- btRingBufferTail = dataHead;
- else
- {
- // Determine the amount of Bluetooth data in the buffer
- bytesToSend = dataHead - btRingBufferTail;
- if (bytesToSend < 0)
- bytesToSend += settings.gnssHandlerBufferSize;
- if (bytesToSend > 0)
- {
- // Reduce bytes to send if we have more to send then the end of
- // the buffer, we'll wrap next loop
- if ((btRingBufferTail + bytesToSend) > settings.gnssHandlerBufferSize)
- bytesToSend = settings.gnssHandlerBufferSize - btRingBufferTail;
+ startMillis = millis();
- // If we are in the config menu, suppress data flowing from GNSS to cell phone
- if (btPrintEcho == false)
- {
- // Push new data over Bluetooth
- bytesToSend = bluetoothWrite(&ringBuffer[btRingBufferTail], bytesToSend);
- }
+ // Determine BT connection state
+ bool connected = (bluetoothGetState() == BT_CONNECTED);
- // Account for the data that was sent
+ if (!connected)
+ // Discard the data
+ btRingBufferTail = dataHead;
+ else
+ {
+ // Determine the amount of Bluetooth data in the buffer
+ bytesToSend = dataHead - btRingBufferTail;
+ if (bytesToSend < 0)
+ bytesToSend += settings.gnssHandlerBufferSize;
if (bytesToSend > 0)
{
- // If we are in base mode, assume part of the outgoing data is RTCM
- if (inBaseMode() == true)
- bluetoothOutgoingRTCM = true;
-
- // Account for the sent or dropped data
- btRingBufferTail += bytesToSend;
- if (btRingBufferTail >= settings.gnssHandlerBufferSize)
- btRingBufferTail -= settings.gnssHandlerBufferSize;
-
- // Remember the maximum transfer time
- deltaMillis = millis() - startMillis;
- if (maxMillis[RBC_BLUETOOTH] < deltaMillis)
- maxMillis[RBC_BLUETOOTH] = deltaMillis;
-
- // Display the data movement
- if (PERIODIC_DISPLAY(PD_BLUETOOTH_DATA_TX))
+ // Reduce bytes to send if we have more to send then the end of
+ // the buffer, we'll wrap next loop
+ if ((btRingBufferTail + bytesToSend) > settings.gnssHandlerBufferSize)
+ bytesToSend = settings.gnssHandlerBufferSize - btRingBufferTail;
+
+ // If we are in the config menu, suppress data flowing from GNSS to cell phone
+ if (btPrintEcho == false)
{
- PERIODIC_CLEAR(PD_BLUETOOTH_DATA_TX);
- systemPrintf("Bluetooth: %d bytes written\r\n", bytesToSend);
+ // Push new data over Bluetooth
+ bytesToSend = bluetoothWrite(&ringBuffer[btRingBufferTail], bytesToSend);
+ }
+
+ // Account for the data that was sent
+ if (bytesToSend > 0)
+ {
+ // If we are in base mode, assume part of the outgoing data is RTCM
+ if (inBaseMode() == true)
+ bluetoothOutgoingRTCM = true;
+
+ // Account for the sent or dropped data
+ btRingBufferTail += bytesToSend;
+ if (btRingBufferTail >= settings.gnssHandlerBufferSize)
+ btRingBufferTail -= settings.gnssHandlerBufferSize;
+
+ // Remember the maximum transfer time
+ deltaMillis = millis() - startMillis;
+ if (maxMillis[RBC_BLUETOOTH] < deltaMillis)
+ maxMillis[RBC_BLUETOOTH] = deltaMillis;
+
+ // Display the data movement
+ if (PERIODIC_DISPLAY(PD_BLUETOOTH_DATA_TX))
+ {
+ PERIODIC_CLEAR(PD_BLUETOOTH_DATA_TX);
+ systemPrintf("Bluetooth: %d bytes written\r\n", bytesToSend);
+ }
+ }
+ else
+ log_w("BT failed to send");
+
+ // Determine the amount of data that remains in the buffer
+ bytesToSend = dataHead - btRingBufferTail;
+ if (bytesToSend < 0)
+ bytesToSend += settings.gnssHandlerBufferSize;
+ if (usedSpace < bytesToSend)
+ {
+ usedSpace = bytesToSend;
+ slowConsumer = "Bluetooth";
}
}
- else
- log_w("BT failed to send");
+ }
- // Determine the amount of data that remains in the buffer
- bytesToSend = dataHead - btRingBufferTail;
+ //----------------------------------------------------------------------
+ // Send data over USB serial
+ //----------------------------------------------------------------------
+
+ startMillis = millis();
+
+ // Determine USB serial connection state
+ if (!forwardGnssDataToUsbSerial)
+ // Discard the data
+ usbRingBufferTail = dataHead;
+ else
+ {
+ // Determine the amount of USB serial data in the buffer
+ bytesToSend = dataHead - usbRingBufferTail;
if (bytesToSend < 0)
bytesToSend += settings.gnssHandlerBufferSize;
- if (usedSpace < bytesToSend)
+ if (bytesToSend > 0)
{
- usedSpace = bytesToSend;
- slowConsumer = "Bluetooth";
+ // Reduce bytes to send if we have more to send then the end of
+ // the buffer, we'll wrap next loop
+ if ((usbRingBufferTail + bytesToSend) > settings.gnssHandlerBufferSize)
+ bytesToSend = settings.gnssHandlerBufferSize - usbRingBufferTail;
+
+ // Send data over USB serial to the PC
+ bytesToSend = systemWriteGnssDataToUsbSerial(&ringBuffer[usbRingBufferTail], bytesToSend);
+
+ // Account for the data that was sent
+ if (bytesToSend > 0)
+ {
+ // Account for the sent or dropped data
+ usbRingBufferTail += bytesToSend;
+ if (usbRingBufferTail >= settings.gnssHandlerBufferSize)
+ usbRingBufferTail -= settings.gnssHandlerBufferSize;
+
+ // Remember the maximum transfer time
+ deltaMillis = millis() - startMillis;
+ if (maxMillis[RBC_USB_SERIAL] < deltaMillis)
+ maxMillis[RBC_USB_SERIAL] = deltaMillis;
+ }
+
+ // Determine the amount of data that remains in the buffer
+ bytesToSend = dataHead - usbRingBufferTail;
+ if (bytesToSend < 0)
+ bytesToSend += settings.gnssHandlerBufferSize;
+ if (usedSpace < bytesToSend)
+ {
+ usedSpace = bytesToSend;
+ slowConsumer = "USB Serial";
+ }
}
}
- }
- //----------------------------------------------------------------------
- // Send data over USB serial
- //----------------------------------------------------------------------
+ //----------------------------------------------------------------------
+ // Send data to the network clients
+ //----------------------------------------------------------------------
- startMillis = millis();
+ startMillis = millis();
- // Determine USB serial connection state
- if (!forwardGnssDataToUsbSerial)
- // Discard the data
- usbRingBufferTail = dataHead;
- else
- {
- // Determine the amount of USB serial data in the buffer
- bytesToSend = dataHead - usbRingBufferTail;
- if (bytesToSend < 0)
- bytesToSend += settings.gnssHandlerBufferSize;
- if (bytesToSend > 0)
+ // Update space available for use in UART task
+ bytesToSend = tcpClientSendData(dataHead);
+ if (usedSpace < bytesToSend)
{
- // Reduce bytes to send if we have more to send then the end of
- // the buffer, we'll wrap next loop
- if ((usbRingBufferTail + bytesToSend) > settings.gnssHandlerBufferSize)
- bytesToSend = settings.gnssHandlerBufferSize - usbRingBufferTail;
+ usedSpace = bytesToSend;
+ slowConsumer = "TCP client";
+ }
- // Send data over USB serial to the PC
- bytesToSend = systemWriteGnssDataToUsbSerial(&ringBuffer[usbRingBufferTail], bytesToSend);
+ // Remember the maximum transfer time
+ deltaMillis = millis() - startMillis;
+ if (maxMillis[RBC_TCP_CLIENT] < deltaMillis)
+ maxMillis[RBC_TCP_CLIENT] = deltaMillis;
- // Account for the data that was sent
- if (bytesToSend > 0)
- {
- // Account for the sent or dropped data
- usbRingBufferTail += bytesToSend;
- if (usbRingBufferTail >= settings.gnssHandlerBufferSize)
- usbRingBufferTail -= settings.gnssHandlerBufferSize;
-
- // Remember the maximum transfer time
- deltaMillis = millis() - startMillis;
- if (maxMillis[RBC_USB_SERIAL] < deltaMillis)
- maxMillis[RBC_USB_SERIAL] = deltaMillis;
- }
+ startMillis = millis();
- // Determine the amount of data that remains in the buffer
- bytesToSend = dataHead - usbRingBufferTail;
+ // Update space available for use in UART task
+ bytesToSend = tcpServerSendData(dataHead);
+ if (usedSpace < bytesToSend)
+ {
+ usedSpace = bytesToSend;
+ slowConsumer = "TCP server";
+ }
+
+ // Remember the maximum transfer time
+ deltaMillis = millis() - startMillis;
+ if (maxMillis[RBC_TCP_SERVER] < deltaMillis)
+ maxMillis[RBC_TCP_SERVER] = deltaMillis;
+
+ startMillis = millis();
+
+ // Update space available for use in UART task
+ bytesToSend = udpServerSendData(dataHead);
+ if (usedSpace < bytesToSend)
+ {
+ usedSpace = bytesToSend;
+ slowConsumer = "UDP server";
+ }
+
+ // Remember the maximum transfer time
+ deltaMillis = millis() - startMillis;
+ if (maxMillis[RBC_UDP_SERVER] < deltaMillis)
+ maxMillis[RBC_UDP_SERVER] = deltaMillis;
+
+ //----------------------------------------------------------------------
+ // Log data to the SD card
+ //----------------------------------------------------------------------
+
+ // Determine if the SD card is enabled for logging
+ connected = online.logging && (!logTimeExceeded());
+
+ // Block logging during Web Config to avoid SD collisions
+ // See issue: https://github.com/sparkfun/SparkFun_RTK_Everywhere_Firmware/issues/693
+ if(webServerIsRunning() == true)
+ connected = false;
+
+ // If user wants to log, record to SD
+ if (!connected)
+ // Discard the data
+ sdRingBufferTail = dataHead;
+ else
+ {
+ // Determine the amount of microSD card logging data in the buffer
+ bytesToSend = dataHead - sdRingBufferTail;
if (bytesToSend < 0)
bytesToSend += settings.gnssHandlerBufferSize;
- if (usedSpace < bytesToSend)
+ if (bytesToSend > 0)
{
- usedSpace = bytesToSend;
- slowConsumer = "USB Serial";
- }
- }
- }
+ // Attempt to gain access to the SD card, avoids collisions with file
+ // writing from other functions like recordSystemSettingsToFile()
+ if (xSemaphoreTake(sdCardSemaphore, loggingSemaphoreWait_ms) == pdPASS)
+ {
+ markSemaphore(FUNCTION_WRITESD);
- //----------------------------------------------------------------------
- // Send data to the network clients
- //----------------------------------------------------------------------
+ do // Do the SD write in a do loop so we can break out if needed
+ {
+ if (settings.enablePrintSDBuffers && (!inMainMenu))
+ {
+ int bufferAvailable = serialGNSS->available();
- startMillis = millis();
+ int availableUARTSpace = settings.uartReceiveBufferSize - bufferAvailable;
- // Update space available for use in UART task
- bytesToSend = tcpClientSendData(dataHead);
- if (usedSpace < bytesToSend)
- {
- usedSpace = bytesToSend;
- slowConsumer = "TCP client";
- }
+ systemPrintf("SD Incoming Serial @ %s: %04d\tToRead: %04d\tMovedToBuffer: %04d\tavailableUARTSpace: "
+ "%04d\tavailableHandlerSpace: %04d\tToRecord: %04d\tRecorded: %04d\tBO: %d\r\n",
+ getTimeStamp(), bufferAvailable, 0, 0, availableUARTSpace, availableHandlerSpace,
+ bytesToSend, 0, bufferOverruns);
+ }
- // Remember the maximum transfer time
- deltaMillis = millis() - startMillis;
- if (maxMillis[RBC_TCP_CLIENT] < deltaMillis)
- maxMillis[RBC_TCP_CLIENT] = deltaMillis;
+ // For the SD card, we need to write everything we've got
+ // to prevent the ARP Write and Events from gatecrashing...
+
+ int32_t sendTheseBytes = bytesToSend;
- startMillis = millis();
+ // Reduce bytes to record if we have more then the end of the buffer
+ if ((sdRingBufferTail + sendTheseBytes) > settings.gnssHandlerBufferSize)
+ sendTheseBytes = settings.gnssHandlerBufferSize - sdRingBufferTail;
- // Update space available for use in UART task
- bytesToSend = tcpServerSendData(dataHead);
- if (usedSpace < bytesToSend)
- {
- usedSpace = bytesToSend;
- slowConsumer = "TCP server";
- }
+ startMillis = millis();
- // Remember the maximum transfer time
- deltaMillis = millis() - startMillis;
- if (maxMillis[RBC_TCP_SERVER] < deltaMillis)
- maxMillis[RBC_TCP_SERVER] = deltaMillis;
+ // Write the data to the file
+ int32_t bytesSent = logFile->write(&ringBuffer[sdRingBufferTail], sendTheseBytes);
- startMillis = millis();
+ // Account for the sent data or dropped
+ sdRingBufferTail += bytesSent;
+ if (sdRingBufferTail >= settings.gnssHandlerBufferSize)
+ sdRingBufferTail -= settings.gnssHandlerBufferSize;
- // Update space available for use in UART task
- bytesToSend = udpServerSendData(dataHead);
- if (usedSpace < bytesToSend)
- {
- usedSpace = bytesToSend;
- slowConsumer = "UDP server";
- }
+ if (bytesSent != sendTheseBytes)
+ {
+ systemPrintf("SD write mismatch (1) @ %s: wrote %d bytes of %d\r\n",
+ getTimeStamp(), bytesSent, sendTheseBytes);
+ break; // Exit the do loop
+ }
- // Remember the maximum transfer time
- deltaMillis = millis() - startMillis;
- if (maxMillis[RBC_UDP_SERVER] < deltaMillis)
- maxMillis[RBC_UDP_SERVER] = deltaMillis;
+ // If we have more data to write - and the first write was successful
+ if (bytesToSend > sendTheseBytes)
+ {
+ sendTheseBytes = bytesToSend - sendTheseBytes;
- //----------------------------------------------------------------------
- // Log data to the SD card
- //----------------------------------------------------------------------
+ bytesSent = logFile->write(&ringBuffer[sdRingBufferTail], sendTheseBytes);
- // Determine if the SD card is enabled for logging
- connected = online.logging && ((systemTime_minutes - startLogTime_minutes) < settings.maxLogTime_minutes);
+ // Account for the sent data or dropped
+ sdRingBufferTail += bytesSent;
+ if (sdRingBufferTail >= settings.gnssHandlerBufferSize) // Should be redundant
+ sdRingBufferTail -= settings.gnssHandlerBufferSize;
- // Block logging during Web Config to avoid SD collisions
- // See issue: https://github.com/sparkfun/SparkFun_RTK_Everywhere_Firmware/issues/693
- if(webServerIsRunning() == true)
- connected = false;
+ if (bytesSent != sendTheseBytes)
+ {
+ systemPrintf("SD write mismatch (2) @ %s: wrote %d bytes of %d\r\n",
+ getTimeStamp(), bytesSent, sendTheseBytes);
+ break; // Exit the do loop
+ }
+ }
- // If user wants to log, record to SD
- if (!connected)
- // Discard the data
- sdRingBufferTail = dataHead;
- else
- {
- // Determine the amount of microSD card logging data in the buffer
- bytesToSend = dataHead - sdRingBufferTail;
- if (bytesToSend < 0)
- bytesToSend += settings.gnssHandlerBufferSize;
- if (bytesToSend > 0)
- {
- // Attempt to gain access to the SD card, avoids collisions with file
- // writing from other functions like recordSystemSettingsToFile()
- if (xSemaphoreTake(sdCardSemaphore, loggingSemaphoreWait_ms) == pdPASS)
- {
- markSemaphore(FUNCTION_WRITESD);
+ if (PERIODIC_DISPLAY(PD_SD_LOG_WRITE) && (bytesSent > 0))
+ {
+ PERIODIC_CLEAR(PD_SD_LOG_WRITE);
+ systemPrintf("SD %d bytes written to log file\r\n", bytesToSend);
+ }
- // Reduce bytes to record if we have more then the end of the buffer
- if ((sdRingBufferTail + bytesToSend) > settings.gnssHandlerBufferSize)
- bytesToSend = settings.gnssHandlerBufferSize - sdRingBufferTail;
+ sdFreeSpace -= bytesToSend; // Update remaining space on SD
- if (settings.enablePrintSDBuffers && (!inMainMenu))
- {
- int bufferAvailable = serialGNSS->available();
+ // Record any pending trigger events
+ if (newEventToRecord == true)
+ {
+ newEventToRecord = false;
- int availableUARTSpace = settings.uartReceiveBufferSize - bufferAvailable;
+ if (settings.enablePrintLogFileStatus)
+ systemPrintln("Log file: recording event");
- systemPrintf("SD Incoming Serial: %04d\tToRead: %04d\tMovedToBuffer: %04d\tavailableUARTSpace: "
- "%04d\tavailableHandlerSpace: %04d\tToRecord: %04d\tRecorded: %04d\tBO: %d\r\n",
- bufferAvailable, 0, 0, availableUARTSpace, availableHandlerSpace, bytesToSend, 0,
- bufferOverruns);
- }
+ // Record trigger count with Time Of Week of rising edge (ms), Millisecond fraction of Time Of Week of
+ // rising edge (ns), and accuracy estimate (ns)
+ char eventData[82]; // Max NMEA sentence length is 82
+ snprintf(eventData, sizeof(eventData), "%d,%d,%d,%d", triggerCount, triggerTowMsR, triggerTowSubMsR,
+ triggerAccEst);
- // Write the data to the file
- long startTime = millis();
- startMillis = millis();
+ char nmeaMessage[82]; // Max NMEA sentence length is 82
+ createNMEASentence(CUSTOM_NMEA_TYPE_EVENT, nmeaMessage, sizeof(nmeaMessage),
+ eventData); // textID, buffer, sizeOfBuffer, text
- bytesToSend = logFile->write(&ringBuffer[sdRingBufferTail], bytesToSend);
- if (PERIODIC_DISPLAY(PD_SD_LOG_WRITE) && (bytesToSend > 0))
- {
- PERIODIC_CLEAR(PD_SD_LOG_WRITE);
- systemPrintf("SD %d bytes written to log file\r\n", bytesToSend);
- }
+ logFile->write(nmeaMessage, strlen(nmeaMessage));
+ const char *crlf = "\r\n";
+ logFile->write(crlf, 2);
- logFileSize = logFile->fileSize(); // Update file size
+ sdFreeSpace -= strlen(nmeaMessage) + 2; // Update remaining space on SD
+ }
- sdFreeSpace -= bytesToSend; // Update remaining space on SD
+ // Record the Antenna Reference Position - if available
+ if (newARPAvailable == true && settings.enableARPLogging &&
+ ((millis() - lastARPLog) > (settings.ARPLoggingInterval_s * 1000)))
+ {
+ lastARPLog = millis();
+ newARPAvailable = false; // Clear flag. It doesn't matter if the ARP cannot be logged
+
+ double x = ARPECEFX;
+ x /= 10000.0; // Convert to m
+ double y = ARPECEFY;
+ y /= 10000.0; // Convert to m
+ double z = ARPECEFZ;
+ z /= 10000.0; // Convert to m
+ double h = ARPECEFH;
+ h /= 10000.0; // Convert to m
+ char ARPData[82]; // Max NMEA sentence length is 82
+ snprintf(ARPData, sizeof(ARPData), "%.4f,%.4f,%.4f,%.4f", x, y, z, h);
+
+ if (settings.enablePrintLogFileStatus)
+ systemPrintf("Log file: recording Antenna Reference Position %s\r\n", ARPData);
+
+ char nmeaMessage[82]; // Max NMEA sentence length is 82
+ createNMEASentence(CUSTOM_NMEA_TYPE_ARP_ECEF_XYZH, nmeaMessage, sizeof(nmeaMessage),
+ ARPData); // textID, buffer, sizeOfBuffer, text
+
+ logFile->write(nmeaMessage, strlen(nmeaMessage));
+ const char *crlf = "\r\n";
+ logFile->write(crlf, 2);
+
+ sdFreeSpace -= strlen(nmeaMessage) + 2; // Update remaining space on SD
+ }
- // Force file sync every 60s
- if (millis() - lastUBXLogSyncTime > 60000)
- {
- baseStatusLedBlink(); // Blink LED to indicate logging activity
+ logFileSize = logFile->fileSize(); // Update file size
- logFile->sync();
- sdUpdateFileAccessTimestamp(logFile); // Update the file access time & date
+ // Force file sync every 60s
+ if (millis() - lastUBXLogSyncTime > 60000)
+ {
+ baseStatusLedBlink(); // Blink LED to indicate logging activity
- baseStatusLedBlink(); // Blink LED to indicate logging activity
+ logFile->sync();
+ sdUpdateFileAccessTimestamp(logFile); // Update the file access time & date
- lastUBXLogSyncTime = millis();
- }
+ baseStatusLedBlink(); // Blink LED to indicate logging activity
- // Remember the maximum transfer time
- deltaMillis = millis() - startMillis;
- if (maxMillis[RBC_SD_CARD] < deltaMillis)
- maxMillis[RBC_SD_CARD] = deltaMillis;
- long endTime = millis();
+ lastUBXLogSyncTime = millis();
+ }
- if (settings.enablePrintBufferOverrun)
- {
- if (endTime - startTime > 150)
- systemPrintf("Long Write! Time: %ld ms / Location: %ld / Recorded %d bytes / "
- "spaceRemaining %d bytes\r\n",
- endTime - startTime, logFileSize, bytesToSend, combinedSpaceRemaining);
- }
+ // Remember the maximum transfer time
+ deltaMillis = millis() - startMillis;
+ if (maxMillis[RBC_SD_CARD] < deltaMillis)
+ maxMillis[RBC_SD_CARD] = deltaMillis;
- xSemaphoreGive(sdCardSemaphore);
+ if (settings.enablePrintBufferOverrun)
+ {
+ if (deltaMillis > 150)
+ systemPrintf("Long Write! Time: %ld ms / Location: %ld / Recorded %d bytes / "
+ "spaceRemaining %d bytes\r\n",
+ deltaMillis, logFileSize, bytesToSend, combinedSpaceRemaining);
+ }
+ } while(0);
- // Account for the sent data or dropped
- if (bytesToSend > 0)
+ xSemaphoreGive(sdCardSemaphore);
+ } // End sdCardSemaphore
+ else
{
- sdRingBufferTail += bytesToSend;
- if (sdRingBufferTail >= settings.gnssHandlerBufferSize)
- sdRingBufferTail -= settings.gnssHandlerBufferSize;
- }
- } // End sdCardSemaphore
- else
- {
- char semaphoreHolder[50];
- getSemaphoreFunction(semaphoreHolder);
- log_w("sdCardSemaphore failed to yield for SD write, held by %s, Tasks.ino line %d",
- semaphoreHolder, __LINE__);
-
- delay(1); // Needed to prevent WDT resets during long Record Settings locks
- taskYIELD();
- }
+ char semaphoreHolder[50];
+ getSemaphoreFunction(semaphoreHolder);
+ log_w("sdCardSemaphore failed to yield for SD write, held by %s, Tasks.ino line %d",
+ semaphoreHolder, __LINE__);
- // Update space available for use in UART task
- bytesToSend = dataHead - sdRingBufferTail;
- if (bytesToSend < 0)
- bytesToSend += settings.gnssHandlerBufferSize;
- if (usedSpace < bytesToSend)
- {
- usedSpace = bytesToSend;
- slowConsumer = "SD card";
- }
- } // bytesToSend
- } // End connected
+ feedWdt();
+ taskYIELD();
+ }
- //----------------------------------------------------------------------
- // Update the available space in the ring buffer
- //----------------------------------------------------------------------
+ // Update space available for use in UART task
+ bytesToSend = dataHead - sdRingBufferTail;
+ if (bytesToSend < 0)
+ bytesToSend += settings.gnssHandlerBufferSize;
+ if (usedSpace < bytesToSend)
+ {
+ usedSpace = bytesToSend;
+ slowConsumer = "SD card";
+ }
+ } // bytesToSend
+ } // End connected
- freeSpace = settings.gnssHandlerBufferSize - usedSpace;
+ //----------------------------------------------------------------------
+ // Update the available space in the ring buffer
+ //----------------------------------------------------------------------
- // Don't fill the last byte to prevent buffer overflow
- if (freeSpace)
- freeSpace -= 1;
- availableHandlerSpace = freeSpace;
+ freeSpace = settings.gnssHandlerBufferSize - usedSpace;
- //----------------------------------------------------------------------
- // Display the millisecond values for the different ring buffer consumers
- //----------------------------------------------------------------------
+ // Don't fill the last byte to prevent buffer overflow
+ if (freeSpace)
+ freeSpace -= 1;
+ availableHandlerSpace = freeSpace;
- if (PERIODIC_DISPLAY(PD_RING_BUFFER_MILLIS))
- {
- int milliseconds;
- int seconds;
+ //----------------------------------------------------------------------
+ // Display the millisecond values for the different ring buffer consumers
+ //----------------------------------------------------------------------
- PERIODIC_CLEAR(PD_RING_BUFFER_MILLIS);
- for (int index = 0; index < RBC_MAX; index++)
+ if (PERIODIC_DISPLAY(PD_RING_BUFFER_MILLIS))
{
- milliseconds = maxMillis[index];
- if (milliseconds > 1)
+ int milliseconds;
+ int seconds;
+
+ PERIODIC_CLEAR(PD_RING_BUFFER_MILLIS);
+ for (int index = 0; index < RBC_MAX; index++)
{
- seconds = milliseconds / MILLISECONDS_IN_A_SECOND;
- milliseconds %= MILLISECONDS_IN_A_SECOND;
- systemPrintf("%s: %d:%03d Sec\r\n", ringBufferConsumer[index], seconds, milliseconds);
+ milliseconds = maxMillis[index];
+ if (milliseconds > 1)
+ {
+ seconds = milliseconds / MILLISECONDS_IN_A_SECOND;
+ milliseconds %= MILLISECONDS_IN_A_SECOND;
+ systemPrintf("%s: %d:%03d Sec\r\n", ringBufferConsumer[index], seconds, milliseconds);
+ }
}
}
+
+ //----------------------------------------------------------------------
+ // processUart1Message will be chomping at the bit. Let it fly!
+ //----------------------------------------------------------------------
+
+ xSemaphoreGive(ringBufferSemaphore);
+ }
+ else
+ {
+ systemPrintf("handleGnssDataTask could not get ringBuffer semaphore - held by %s\r\n", ringBufferSemaphoreHolder);
}
//----------------------------------------------------------------------
// Let other tasks run, prevent watch dog timer (WDT) resets
//----------------------------------------------------------------------
- delay(1);
+ feedWdt();
taskYIELD();
}
@@ -2123,7 +2289,7 @@ void sdSizeCheckTask(void *e)
}
}
- delay(1);
+ feedWdt();
taskYIELD(); // Let other tasks run
}
@@ -2200,3 +2366,45 @@ void bluetoothCommandTask(void *pvParameters)
task.bluetoothCommandTaskRunning = false;
vTaskDelete(NULL);
}
+
+void beginRtcmParse()
+{
+ // Begin the RTCM parser - which will extract the base location from RTCM1005 / 1006
+ rtcmParse = sempBeginParser(rtcmParserTable, rtcmParserCount, rtcmParserNames, rtcmParserNameCount,
+ 0, // Scratchpad bytes
+ 1050, // Buffer length
+ processRTCMMessage, // eom Call Back
+ "rtcmParse"); // Parser Name
+ if (!rtcmParse)
+ reportFatalError("Failed to initialize the RTCM parser");
+
+ if (settings.debugNtripClientRtcm)
+ {
+ sempEnableDebugOutput(rtcmParse);
+ sempPrintParserConfiguration(rtcmParse);
+ }
+}
+
+// Check and record the base location in RTCM1005/1006
+void processRTCMMessage(SEMP_PARSE_STATE *parse, uint16_t type)
+{
+ SEMP_SCRATCH_PAD *scratchPad = (SEMP_SCRATCH_PAD *)parse->scratchPad;
+
+ if (sempRtcmGetMessageNumber(parse) == 1005)
+ {
+ ARPECEFX = sempRtcmGetSignedBits(parse, 34, 38);
+ ARPECEFY = sempRtcmGetSignedBits(parse, 74, 38);
+ ARPECEFZ = sempRtcmGetSignedBits(parse, 114, 38);
+ ARPECEFH = 0;
+ newARPAvailable = true;
+ }
+
+ if (sempRtcmGetMessageNumber(parse) == 1006)
+ {
+ ARPECEFX = sempRtcmGetSignedBits(parse, 34, 38);
+ ARPECEFY = sempRtcmGetSignedBits(parse, 74, 38);
+ ARPECEFZ = sempRtcmGetSignedBits(parse, 114, 38);
+ ARPECEFH = sempRtcmGetUnsignedBits(parse, 152, 16);
+ newARPAvailable = true;
+ }
+}
\ No newline at end of file
diff --git a/Firmware/RTK_Everywhere/TcpServer.ino b/Firmware/RTK_Everywhere/TcpServer.ino
index 170f86979..7f4385570 100644
--- a/Firmware/RTK_Everywhere/TcpServer.ino
+++ b/Firmware/RTK_Everywhere/TcpServer.ino
@@ -139,26 +139,42 @@ int32_t tcpServerClientSendData(int index, uint8_t *data, uint16_t length)
{
if (tcpServerClient[index])
{
- length = tcpServerClient[index]->write(data, length);
- if (length > 0)
+ // Use a semaphore to prevent tcpServerUpdate from gatecrashing
+ if (tcpServerSemaphore == NULL)
+ tcpServerSemaphore = xSemaphoreCreateMutex(); // Create the mutex
+
+ // Take the semaphore
+ if (xSemaphoreTake(tcpServerSemaphore, 50 / portTICK_PERIOD_MS) == pdPASS)
{
- // Update the data sent flag when data successfully sent
+ tcpServerSemaphoreHolder = "tcpServerClientSendData";
+
+ length = tcpServerClient[index]->write(data, length);
if (length > 0)
- tcpServerClientDataSent = tcpServerClientDataSent | (1 << index);
- if ((settings.debugTcpServer || PERIODIC_DISPLAY(PD_TCP_SERVER_CLIENT_DATA)) && (!inMainMenu))
{
- PERIODIC_CLEAR(PD_TCP_SERVER_CLIENT_DATA);
- systemPrintf("TCP server wrote %d bytes to %s\r\n", length,
- tcpServerClientIpAddress[index].toString().c_str());
+ // Update the data sent flag when data successfully sent
+ if (length > 0)
+ tcpServerClientDataSent = tcpServerClientDataSent | (1 << index);
+ if ((settings.debugTcpServer || PERIODIC_DISPLAY(PD_TCP_SERVER_CLIENT_DATA)) && (!inMainMenu))
+ {
+ PERIODIC_CLEAR(PD_TCP_SERVER_CLIENT_DATA);
+ systemPrintf("TCP server wrote %d bytes to %s\r\n", length,
+ tcpServerClientIpAddress[index].toString().c_str());
+ }
}
- }
- // Failed to write the data
+ // Failed to write the data
+ else
+ {
+ // Done with this client connection
+ tcpServerStopClient(index);
+ length = 0;
+ }
+
+ xSemaphoreGive(tcpServerSemaphore);
+ }
else
{
- // Done with this client connection
- tcpServerStopClient(index);
- length = 0;
+ systemPrintf("tcpServerClientSendData could not get semaphore - held by %s\r\n", tcpServerSemaphoreHolder);
}
}
return length;
@@ -553,46 +569,62 @@ void tcpServerUpdate()
// and respond accordingly
if (settings.enableNtripCaster || settings.baseCasterOverride)
{
- // Read response from client
- char response[512];
- int spot = 0;
- while (tcpServerClient[index]->available())
- {
- response[spot++] = tcpServerClient[index]->read();
- if (spot == sizeof(response))
- spot = 0; // Wrap
- }
- response[spot] = '\0'; // Terminate string
-
- if (strnstr(response, "GET / ", sizeof(response)) != NULL) // No mount point in header
- {
- if (settings.debugTcpServer)
- systemPrintln("Mount point table requested.");
-
- // Respond with a single mountpoint
- const char fakeSourceTable[] =
- "SOURCETABLE 200 OK\r\nServer: SparkPNT Caster/1.0\r\nContent-Type: "
- "text/plain\r\nContent-Length: 96\r\n\r\nSTR;SparkBase;none;RTCM "
- "3.0;none;none;none;none;none;none;none;none;none;none;none;B;N;none;none";
-
- tcpServerClient[index]->write(fakeSourceTable, strlen(fakeSourceTable));
-
- tcpServerStopClient(index); // Disconnect from client
- }
- else if (strnstr(response, "GET /", sizeof(response)) != NULL) // Mount point in header
+ // Use a semaphore to prevent tcpServerClientSendData from gatecrashing
+ if (tcpServerSemaphore == NULL)
+ tcpServerSemaphore = xSemaphoreCreateMutex(); // Create the mutex
+
+ // Take the semaphore
+ if (xSemaphoreTake(tcpServerSemaphore, 50 / portTICK_PERIOD_MS) == pdPASS)
{
- // NTRIP Client is sending us their mount point. Begin sending RTCM.
- if (settings.debugTcpServer)
- systemPrintln("NTRIP Client connected - Sending ICY 200 OK");
-
- char confirmConnection[] = "ICY 200 OK\r\n";
- tcpServerClient[index]->write(confirmConnection, strlen(confirmConnection));
+ tcpServerSemaphoreHolder = "tcpServerUpdate";
+
+ // Read response from client
+ char response[512];
+ int spot = 0;
+ while (tcpServerClient[index]->available())
+ {
+ response[spot++] = tcpServerClient[index]->read();
+ if (spot == sizeof(response))
+ spot = 0; // Wrap
+ }
+ response[spot] = '\0'; // Terminate string
+
+ if (strnstr(response, "GET / ", sizeof(response)) != NULL) // No mount point in header
+ {
+ if (settings.debugTcpServer)
+ systemPrintln("Mount point table requested.");
+
+ // Respond with a single mountpoint
+ const char fakeSourceTable[] =
+ "SOURCETABLE 200 OK\r\nServer: SparkPNT Caster/1.0\r\nContent-Type: "
+ "text/plain\r\nContent-Length: 96\r\n\r\nSTR;SparkBase;none;RTCM "
+ "3.0;none;none;none;none;none;none;none;none;none;none;none;B;N;none;none";
+
+ tcpServerClient[index]->write(fakeSourceTable, strlen(fakeSourceTable));
+
+ tcpServerStopClient(index); // Disconnect from client
+ }
+ else if (strnstr(response, "GET /", sizeof(response)) != NULL) // Mount point in header
+ {
+ // NTRIP Client is sending us their mount point. Begin sending RTCM.
+ if (settings.debugTcpServer)
+ systemPrintln("NTRIP Client connected - Sending ICY 200 OK");
+
+ char confirmConnection[] = "ICY 200 OK\r\n";
+ tcpServerClient[index]->write(confirmConnection, strlen(confirmConnection));
+ }
+ else
+ {
+ // Unknown response
+ if (settings.debugTcpServer)
+ systemPrintf("Unknown response: %s\r\n", response);
+ }
+
+ xSemaphoreGive(tcpServerSemaphore);
}
else
{
- // Unknown response
- if (settings.debugTcpServer)
- systemPrintf("Unknown response: %s\r\n", response);
+ systemPrintf("tcpServerUpdate could not get semaphore - held by %s\r\n", tcpServerSemaphoreHolder);
}
} // settings.enableNtripCaster == true || settings.baseCasterOverride == true
diff --git a/Firmware/RTK_Everywhere/WebServer.ino b/Firmware/RTK_Everywhere/WebServer.ino
index 666c0b74a..7588866b0 100644
--- a/Firmware/RTK_Everywhere/WebServer.ino
+++ b/Firmware/RTK_Everywhere/WebServer.ino
@@ -1093,16 +1093,8 @@ void webServerSetState(uint8_t newState)
if (!online.rtc)
systemPrintf("%s%s%s%s\r\n", asterisk, initialState, arrow, endingState);
else
- {
// Timestamp the state change
- // 1 2
- // 12345678901234567890123456
- // YYYY-mm-dd HH:MM:SS.xxxrn0
- struct tm timeinfo = rtc.getTimeStruct();
- char s[30];
- strftime(s, sizeof(s), "%Y-%m-%d %H:%M:%S", &timeinfo);
- systemPrintf("%s%s%s%s, %s.%03ld\r\n", asterisk, initialState, arrow, endingState, s, rtc.getMillis());
- }
+ systemPrintf("%s%s%s%s, %s\r\n", asterisk, initialState, arrow, endingState, getTimeStamp());
}
// Validate the state
@@ -1343,6 +1335,8 @@ static esp_err_t ws_handler(httpd_req_t *req)
for (int i = 0; i < ws_pkt.len; i++)
{
incomingSettings[incomingSettingsSpot++] = ws_pkt.payload[i];
+ if (incomingSettingsSpot == AP_CONFIG_SETTING_SIZE)
+ systemPrintln("incomingSettings wrap-around. Increase AP_CONFIG_SETTING_SIZE");
incomingSettingsSpot %= AP_CONFIG_SETTING_SIZE;
}
timeSinceLastIncomingSetting = millis();
diff --git a/Firmware/RTK_Everywhere/menuFirmware.ino b/Firmware/RTK_Everywhere/menuFirmware.ino
index 2f7382cc1..9f5b399c7 100644
--- a/Firmware/RTK_Everywhere/menuFirmware.ino
+++ b/Firmware/RTK_Everywhere/menuFirmware.ino
@@ -754,16 +754,8 @@ void otaSetState(uint8_t newState)
if (!online.rtc)
systemPrintf("%s%s%s%s\r\n", asterisk, initialState, arrow, endingState);
else
- {
// Timestamp the state change
- // 1 2
- // 12345678901234567890123456
- // YYYY-mm-dd HH:MM:SS.xxxrn0
- struct tm timeinfo = rtc.getTimeStruct();
- char s[30];
- strftime(s, sizeof(s), "%Y-%m-%d %H:%M:%S", &timeinfo);
- systemPrintf("%s%s%s%s, %s.%03ld\r\n", asterisk, initialState, arrow, endingState, s, rtc.getMillis());
- }
+ systemPrintf("%s%s%s%s, %s\r\n", asterisk, initialState, arrow, endingState, getTimeStamp());
}
// Validate the firmware update state
diff --git a/Firmware/RTK_Everywhere/menuMain.ino b/Firmware/RTK_Everywhere/menuMain.ino
index 1a93a755c..16b18f709 100644
--- a/Firmware/RTK_Everywhere/menuMain.ino
+++ b/Firmware/RTK_Everywhere/menuMain.ino
@@ -32,7 +32,11 @@ void terminalUpdate()
// Push RTCM to GNSS module over I2C / SPI
if (correctionLastSeen(CORR_USB))
+ {
gnss->pushRawData((uint8_t *)buffer, length);
+ sempParseNextBytes(rtcmParse, (uint8_t *)buffer, length); // Parse the data for RTCM1005/1006
+ }
+
}
// Does incoming data consist of RTCM correction messages
@@ -51,7 +55,10 @@ void terminalUpdate()
// Push RTCM to GNSS module over I2C / SPI
if (correctionLastSeen(CORR_USB))
+ {
gnss->pushRawData((uint8_t *)buffer, length);
+ sempParseNextBytes(rtcmParse, (uint8_t *)buffer, length); // Parse the data for RTCM1005/1006
+ }
}
else
{
diff --git a/Firmware/RTK_Everywhere/menuMessages.ino b/Firmware/RTK_Everywhere/menuMessages.ino
index aa726b230..e6f7962d5 100644
--- a/Firmware/RTK_Everywhere/menuMessages.ino
+++ b/Firmware/RTK_Everywhere/menuMessages.ino
@@ -82,6 +82,15 @@ void menuLog()
systemPrintln("Disabled");
}
+ if (settings.enableLogging == true)
+ {
+ systemPrint("9) Log file length alignment: ");
+ if (settings.alignedLogFiles == true)
+ systemPrintln("Enabled");
+ else
+ systemPrintln("Disabled");
+ }
+
systemPrintln("x) Exit");
int incoming = getUserInputNumber(); // Returns EXIT, TIMEOUT, or long
@@ -90,15 +99,17 @@ void menuLog()
{
settings.enableLogging ^= 1;
- // Reset the maximum logging time when logging is disabled to ensure that
- // the next time logging is enabled that the maximum amount of data can be
- // captured.
- if (settings.enableLogging == false)
- startLogTime_minutes = 0;
+ // Reset the start logging time when logging is enabled to ensure that
+ // data can be captured.
+ if (settings.enableLogging == true)
+ startLogTime_minutes = millis() / 1000L / 60;
}
else if (incoming == 2 && settings.enableLogging == true)
{
// Arbitrary 2 year limit. See https://github.com/sparkfun/SparkFun_RTK_Firmware/issues/86
+ // Note: the 2 year limit is fine. But systemTime_minutes is based on millis(), and millis()
+ // will roll over every 2^32ms = ~50 days...
+ // TODO: use the GNSS epoch (uint32_t seconds plus uint32_t microseconds) to resolve this.
getNewSetting("Enter max minutes before logging stops", 0, 60 * 24 * 365 * 2, &settings.maxLogTime_minutes);
}
else if (incoming == 3 && settings.enableLogging == true)
@@ -120,7 +131,7 @@ void menuLog()
else if (incoming == 6 && settings.enableLogging == true && settings.enableARPLogging == true)
{
// Arbitrary 10 minute limit
- getNewSetting("Enter the ARP logging interval in seconds", 0, 60 * 10, &settings.ARPLoggingInterval_s);
+ getNewSetting("Enter the ARP logging interval in seconds", 1, 60 * 10, &settings.ARPLoggingInterval_s);
}
else if (incoming == 7)
{
@@ -130,6 +141,10 @@ void menuLog()
{
settings.enableNTPFile ^= 1;
}
+ else if (incoming == 9 && settings.enableLogging == true)
+ {
+ settings.alignedLogFiles ^= 1;
+ }
else if (incoming == 'x')
break;
else if (incoming == INPUT_RESPONSE_GETNUMBER_EXIT)
@@ -289,11 +304,25 @@ bool beginLogging(const char *customFileName)
sdUpdateFileCreateTimestamp(logFile); // Update the file to create time & date
- startCurrentLogTime_minutes = millis() / 1000L / 60; // Mark now as start of logging
-
- // If it hasn't been done before, mark the initial start of logging for total run time
- if (startLogTime_minutes == 0)
- startLogTime_minutes = millis() / 1000L / 60;
+ // Calculate the time of the next log file change
+ nextLogTime_ms = 0; // Default to no limit
+ if ((settings.alignedLogFiles) && (settings.maxLogLength_minutes > 0))
+ {
+ // Aligned logging is only possible if the interval is an integral fraction of 24 hours
+ if ((24 * 60 * 2) % settings.maxLogLength_minutes == 0)
+ {
+ // Calculate when the next log file should be opened - in millis()
+ unsigned long hoursAsMillis = rtc.getMillis() + (rtc.getSecond() * 1000)
+ + (rtc.getMinute() * 1000 * 60)
+ + (rtc.getHour(true) * 1000 * 60 * 60);
+ unsigned long maxLogLength_ms = (unsigned long)settings.maxLogLength_minutes * 60 * 1000;
+ unsigned long millisFromPreviousLog = hoursAsMillis % maxLogLength_ms;
+ unsigned long millisToNextLog = maxLogLength_ms - millisFromPreviousLog;
+ nextLogTime_ms = millis() + millisToNextLog;
+ }
+ }
+ if ((nextLogTime_ms == 0) && (settings.maxLogLength_minutes > 0)) // Non-aligned logging
+ nextLogTime_ms = millis() + ((unsigned long)settings.maxLogLength_minutes * 60 * 1000);
// Add NMEA txt message with restart reason
char rstReason[30];
@@ -378,6 +407,8 @@ bool beginLogging(const char *customFileName)
currentDate); // textID, buffer, sizeOfBuffer, text
logFile->println(nmeaMessage);
+ logFile->sync(); // Sync any partially written data
+
if (reuseLastLog == true)
{
systemPrintln("Appending last available log");
@@ -422,6 +453,7 @@ void endLogging(bool gotSemaphore, bool releaseSemaphore)
char nmeaMessage[82]; // Max NMEA sentence length is 82
createNMEASentence(CUSTOM_NMEA_TYPE_PARSER_STATS, nmeaMessage, sizeof(nmeaMessage),
parserStats); // textID, buffer, sizeOfBuffer, text
+ logFile->sync(); // Sync any partially written data
logFile->println(nmeaMessage);
logFile->sync();
@@ -436,7 +468,7 @@ void endLogging(bool gotSemaphore, bool releaseSemaphore)
delete logFile;
logFile = nullptr;
- systemPrintln("Log file closed");
+ systemPrintf("Log file closed @ %s\r\n", getTimeStamp());
// Release the semaphore if requested
if (releaseSemaphore)
diff --git a/Firmware/RTK_Everywhere/menuSystem.ino b/Firmware/RTK_Everywhere/menuSystem.ino
index 4bb1a6134..52b8ec7f1 100644
--- a/Firmware/RTK_Everywhere/menuSystem.ino
+++ b/Firmware/RTK_Everywhere/menuSystem.ino
@@ -8,7 +8,7 @@ void menuSystem()
systemPrintln();
systemPrintln("System Status");
- printTimeStamp();
+ printTimeStamp(true);
systemPrint("GNSS: ");
if (online.gnss == true)
@@ -801,6 +801,8 @@ void menuDebugSoftware()
systemPrintf("40) Print LittleFS and settings management: %s\r\n",
settings.debugSettings ? "Enabled" : "Disabled");
+ systemPrintf("41) Halt on ESP_RST_PANIC: %s\r\n",
+ settings.haltOnPanic ? "Enabled" : "Disabled");
// Tasks
systemPrint("50) Task Highwater Reporting: ");
@@ -856,6 +858,8 @@ void menuDebugSoftware()
else if (incoming == 40)
settings.debugSettings ^= 1;
+ else if (incoming == 41)
+ settings.haltOnPanic ^= 1;
else if (incoming == 50)
settings.enableTaskReports ^= 1;
@@ -1279,12 +1283,14 @@ void menuPeriodicPrint()
else if (incoming == 20)
{
+ systemPrint("Enter the new periodic print mask: ");
int value = getUserInputNumber();
if ((value != INPUT_RESPONSE_GETNUMBER_EXIT) && (value != INPUT_RESPONSE_GETNUMBER_TIMEOUT))
settings.periodicDisplay = value;
}
else if (incoming == 21)
{
+ systemPrint("Enter the new periodic display interval (s): ");
int seconds = getUserInputNumber();
if ((seconds != INPUT_RESPONSE_GETNUMBER_EXIT) && (seconds != INPUT_RESPONSE_GETNUMBER_TIMEOUT))
settings.periodicDisplayInterval = seconds * 1000;
diff --git a/Firmware/RTK_Everywhere/settings.h b/Firmware/RTK_Everywhere/settings.h
index a4c84f1d7..b67e230ef 100644
--- a/Firmware/RTK_Everywhere/settings.h
+++ b/Firmware/RTK_Everywhere/settings.h
@@ -358,12 +358,12 @@ enum PeriodDisplayValues
#ifdef COMPILE_NETWORK
-// NTRIP Server data
-typedef struct _NTRIP_SERVER_DATA
+// NTRIP Server data - the array is declared volatile in NtripServer.ino
+typedef struct
{
// Network connection used to push RTCM to NTRIP caster
NetworkClient *networkClient;
- volatile uint8_t state;
+ uint8_t state;
// Count of bytes sent by the NTRIP server to the NTRIP caster
uint32_t bytesSent;
@@ -495,6 +495,7 @@ typedef enum
FUNCTION_LOG_CLOSURE,
FUNCTION_PRINT_FILE_LIST,
FUNCTION_NTPEVENT,
+ FUNCTION_ARPWRITE,
} SemaphoreFunction;
// Print the base coordinates in different formats, depending on the type the user has entered
@@ -725,11 +726,12 @@ struct Settings
bool debugHttpClientState = false; // Debug the HTTP Client state machine
// Log file
- bool enableLogging = true; // If an SD card is present, log default sentences
+ bool alignedLogFiles = false; // If true, align log files as per #630
+ bool enableLogging = true; // If an SD card is present, log default sentences
bool enablePrintLogFileMessages = false;
bool enablePrintLogFileStatus = true;
int maxLogLength_minutes = 60 * 24; // Default to 24 hours
- int maxLogTime_minutes = 60 * 24; // Default to 24 hours
+ int maxLogTime_minutes = 60 * 24; // Default to 24 hours
// MQTT
bool debugMqttClientData = false; // Debug the MQTT SPARTAN data flow
@@ -832,6 +834,7 @@ struct Settings
1; // Read from GNSS and Write to circular buffer (SD, TCP, BT). 3 being the highest, and 0 being the lowest
uint8_t gnssUartInterruptsCore =
1; // Core where hardware is started and interrupts are assigned to, 0=core, 1=Arduino
+ bool haltOnPanic = false; // Halt after beginVersion if the reset reason was panic
uint8_t handleGnssDataTaskCore = 1; // Core where task should run, 0=core, 1=Arduino
uint8_t handleGnssDataTaskPriority = 1; // Read from the circular buffer and dole out to end points (SD, TCP, BT).
uint8_t i2cInterruptsCore = 1; // Core where hardware is started and interrupts are assigned to, 0=core, 1=Arduino
@@ -1336,6 +1339,7 @@ const RTK_Settings_Entry rtkSettingsEntries[] =
// g s x k 2 c h d d Type Qual Variable Name
// Log file
+ { 1, 1, 0, 1, 1, 1, 0, 1, 1, _bool, 0, & settings.alignedLogFiles, "alignedLogFiles", },
{ 1, 1, 0, 1, 1, 1, 0, 1, 1, _bool, 0, & settings.enableLogging, "enableLogging", },
{ 0, 0, 0, 1, 1, 1, 0, 1, 1, _bool, 0, & settings.enablePrintLogFileMessages, "enablePrintLogFileMessages", },
{ 0, 0, 0, 1, 1, 1, 0, 1, 1, _bool, 0, & settings.enablePrintLogFileStatus, "enablePrintLogFileStatus", },
@@ -1419,6 +1423,21 @@ const RTK_Settings_Entry rtkSettingsEntries[] =
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, tNSMtPt, NTRIP_SERVER_MAX, & settings.ntripServer_MountPoint[0], "ntripServerMountPoint_", },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, tNSMtPtPw, NTRIP_SERVER_MAX, & settings.ntripServer_MountPointPW[0], "ntripServerMountPointPW_", },
+// F
+// a
+// F c
+// i a e
+// n i c t
+// W n u e
+// e C s F t V P
+// b o e a 2 o
+// C m S c M s
+// o m u e o T L t
+// n a f t s o B c
+// f n f E a r a a
+// i d i v V i c n r
+// g s x k 2 c h d d Type Qual Variable Name
+
// OS
{ 0, 0, 0, 1, 1, 1, 1, 1, 1, _uint8_t, 0, & settings.bluetoothInterruptsCore, "bluetoothInterruptsCore", },
{ 0, 0, 0, 1, 1, 1, 1, 1, 1, _uint8_t, 0, & settings.btReadTaskCore, "btReadTaskCore", },
@@ -1431,6 +1450,7 @@ const RTK_Settings_Entry rtkSettingsEntries[] =
{ 0, 0, 0, 1, 1, 1, 1, 1, 1, _uint8_t, 0, & settings.gnssReadTaskCore, "gnssReadTaskCore", },
{ 0, 0, 0, 1, 1, 1, 1, 1, 1, _uint8_t, 0, & settings.gnssReadTaskPriority, "gnssReadTaskPriority", },
{ 0, 0, 0, 1, 1, 1, 1, 1, 1, _uint8_t, 0, & settings.gnssUartInterruptsCore, "gnssUartInterruptsCore", },
+ { 0, 1, 0, 1, 1, 1, 1, 1, 1, _bool, 0, & settings.haltOnPanic, "haltOnPanic", },
{ 0, 0, 0, 1, 1, 1, 1, 1, 1, _uint8_t, 0, & settings.handleGnssDataTaskCore, "handleGnssDataTaskCore", },
{ 0, 0, 0, 1, 1, 1, 1, 1, 1, _uint8_t, 0, & settings.handleGnssDataTaskPriority, "handleGnssDataTaskPriority", },
{ 0, 0, 0, 1, 1, 1, 1, 1, 1, _uint8_t, 0, & settings.i2cInterruptsCore, "i2cInterruptsCore", },
diff --git a/Firmware/RTK_Everywhere/support.ino b/Firmware/RTK_Everywhere/support.ino
index 8d0f67a84..ef61e6625 100644
--- a/Firmware/RTK_Everywhere/support.ino
+++ b/Firmware/RTK_Everywhere/support.ino
@@ -302,6 +302,14 @@ InputResponse getUserInputString(char *userString, uint16_t stringSize, bool loc
gnss->update(); // Regularly poll to get latest data
+ // Keep the ntripClient alive by pushing GPGGA.
+ // I'm not sure if we want / need to do this, but that's how it was when
+ // the GPGGA push was performed from processUart1Message from gnssReadTask.
+ // Doing it here keeps the user experience the same. It is safe to do it here
+ // because the loop is suspended and networkUpdate / ntripClientUpdate aren't
+ // being called. Maybe we _should_ call networkUpdate() here? Just sayin'...
+ pushGPGGA(nullptr);
+
// Keep processing NTP requests
if (online.ethernetNTPServer)
{
@@ -475,23 +483,33 @@ void printElapsedTime(const char *title)
#define TIMESTAMP_INTERVAL 1000 // Milliseconds
+// Get the timestamp
+const char *getTimeStamp()
+{
+ static char theTime[30];
+
+ // 1 2 3
+ // 123456789012345678901234567890
+ // YYYY-mm-dd HH:MM:SS.xxxrn0
+ struct tm timeinfo = rtc.getTimeStruct();
+ char timestamp[30];
+ strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", &timeinfo);
+ snprintf(theTime, sizeof(theTime), "%s.%03ld", timestamp, rtc.getMillis());
+
+ return (const char *)theTime;
+}
+
// Print the timestamp
-void printTimeStamp()
+void printTimeStamp(bool always)
{
uint32_t currentMilliseconds;
static uint32_t previousMilliseconds;
// Timestamp the messages
currentMilliseconds = millis();
- if ((currentMilliseconds - previousMilliseconds) >= TIMESTAMP_INTERVAL)
+ if (always || ((currentMilliseconds - previousMilliseconds) >= TIMESTAMP_INTERVAL))
{
- // 1 2 3
- // 123456789012345678901234567890
- // YYYY-mm-dd HH:MM:SS.xxxrn0
- struct tm timeinfo = rtc.getTimeStruct();
- char timestamp[30];
- strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", &timeinfo);
- systemPrintf("%s.%03ld\r\n", timestamp, rtc.getMillis());
+ systemPrintln(getTimeStamp());
// Select the next time to display the timestamp
previousMilliseconds = currentMilliseconds;