diff --git a/Firmware/RTK_Surveyor/AP-Config/index.html b/Firmware/RTK_Surveyor/AP-Config/index.html
index 4872a1176..ae1f40b2d 100644
--- a/Firmware/RTK_Surveyor/AP-Config/index.html
+++ b/Firmware/RTK_Surveyor/AP-Config/index.html
@@ -1274,8 +1274,8 @@
-
- TCP Client
+
@@ -1283,12 +1283,12 @@
-
- TCP Server
+
@@ -1296,19 +1296,46 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Firmware/RTK_Surveyor/AP-Config/src/main.js b/Firmware/RTK_Surveyor/AP-Config/src/main.js
index 0e075dfed..fbfc9deef 100644
--- a/Firmware/RTK_Surveyor/AP-Config/src/main.js
+++ b/Firmware/RTK_Surveyor/AP-Config/src/main.js
@@ -353,6 +353,7 @@ function parseIncoming(msg) {
updateECEFList();
updateGeodeticList();
tcpBoxes();
+ udpBoxes();
tcpBoxesEthernet();
dhcpEthernet();
updateLatLong();
@@ -592,10 +593,13 @@ function validateFields() {
checkElementString("wifiNetwork2Password", 0, 50, "Must be 0 to 50 characters", "collapseWiFiConfig");
checkElementString("wifiNetwork3SSID", 0, 50, "Must be 0 to 50 characters", "collapseWiFiConfig");
checkElementString("wifiNetwork3Password", 0, 50, "Must be 0 to 50 characters", "collapseWiFiConfig");
- if (ge("enableTcpClient").checked || ge("enableTcpServer").checked) {
- checkElementString("wifiTcpPort", 1, 65535, "Must be 1 to 65535", "collapseWiFiConfig");
+ if (ge("enablePvtClient").checked || ge("enablePvtServer").checked) {
+ checkElementString("pvtServerPort", 1, 65535, "Must be 1 to 65535", "collapseWiFiConfig");
}
- checkCheckboxMutex("enableTcpClient", "enableTcpServer", "TCP Client and Server can not be enabled at the same time", "collapseWiFiConfig");
+ if (ge("enablePvtUdpServer").checked) {
+ checkElementString("pvtUdpServerPort", 1, 65535, "Must be 1 to 65535", "collapseWiFiConfig");
+ }
+ checkCheckboxMutex("enablePvtClient", "enablePvtServer", "TCP Client and Server can not be enabled at the same time", "collapseWiFiConfig");
//System Config
if (ge("enableLogging").checked) {
@@ -1567,12 +1571,22 @@ function abortHandler(event) {
}
function tcpBoxes() {
- if (ge("enableTcpClient").checked || ge("enableTcpServer").checked) {
+ if (ge("enablePvtServer").checked || ge("enablePvtClient").checked) {
show("tcpSettingsConfig");
}
else {
hide("tcpSettingsConfig");
- ge("wifiTcpPort").value = 2947;
+ ge("pvtServerPort").value = 2947;
+ }
+}
+
+function udpBoxes() {
+ if (ge("enablePvtUdpServer").checked) {
+ show("udpSettingsConfig");
+ }
+ else {
+ hide("udpSettingsConfig");
+ ge("pvtUdpServerPort").value = 10110;
}
}
diff --git a/Firmware/RTK_Surveyor/Developer.ino b/Firmware/RTK_Surveyor/Developer.ino
index 43bdd9ec0..59bb23f0a 100644
--- a/Firmware/RTK_Surveyor/Developer.ino
+++ b/Firmware/RTK_Surveyor/Developer.ino
@@ -113,6 +113,14 @@ void pvtServerZeroTail() {}
void pvtServerValidateTables() {}
void discardPvtServerBytes(RING_BUFFER_OFFSET previousTail, RING_BUFFER_OFFSET newTail) {}
+//----------------------------------------
+// PVT UDP server
+//----------------------------------------
+
+int pvtUdpServerSendData(uint16_t dataHead) {return 0;}
+void pvtUdpServerUpdate() {}
+void pvtUdpServerZeroTail() {}
+
//----------------------------------------
// WiFi
//----------------------------------------
diff --git a/Firmware/RTK_Surveyor/Ethernet.ino b/Firmware/RTK_Surveyor/Ethernet.ino
index c7b3cb242..1afa3dd90 100644
--- a/Firmware/RTK_Surveyor/Ethernet.ino
+++ b/Firmware/RTK_Surveyor/Ethernet.ino
@@ -254,7 +254,7 @@ bool ethernetIsNeeded()
return true;
// Does PVT client or server need Ethernet?
- if (settings.enablePvtClient || settings.enablePvtServer)
+ if (settings.enablePvtClient || settings.enablePvtServer || settings.enablePvtUdpServer)
return true;
return false;
diff --git a/Firmware/RTK_Surveyor/Form.ino b/Firmware/RTK_Surveyor/Form.ino
index 810d31186..27bf2a339 100644
--- a/Firmware/RTK_Surveyor/Form.ino
+++ b/Firmware/RTK_Surveyor/Form.ino
@@ -1004,7 +1004,11 @@ void createSettingsString(char *newSettings)
else
stringRecord(newSettings, "wifiConfigOverAP", 0); // 1 = AP mode, 0 = WiFi
+ stringRecord(newSettings, "enablePvtServer", settings.enablePvtServer);
+ stringRecord(newSettings, "enablePvtClient", settings.enablePvtClient);
stringRecord(newSettings, "pvtServerPort", settings.pvtServerPort);
+ stringRecord(newSettings, "enablePvtUdpServer", settings.enablePvtUdpServer);
+ stringRecord(newSettings, "pvtUdpServerPort", settings.pvtUdpServerPort);
stringRecord(newSettings, "enableRCFirmware", enableRCFirmware);
// New settings not yet integrated
@@ -1250,6 +1254,8 @@ void updateSettingWithValue(const char *settingName, const char *settingValueStr
}
else if (strcmp(settingName, "pvtServerPort") == 0)
settings.pvtServerPort = settingValue;
+ else if (strcmp(settingName, "pvtUdpServerPort") == 0)
+ settings.pvtUdpServerPort = settingValue;
else if (strcmp(settingName, "wifiConfigOverAP") == 0)
{
if (settingValue == 1) // Drop downs come back as a value
@@ -1262,6 +1268,8 @@ void updateSettingWithValue(const char *settingName, const char *settingValueStr
settings.enablePvtClient = settingValueBool;
else if (strcmp(settingName, "enablePvtServer") == 0)
settings.enablePvtServer = settingValueBool;
+ else if (strcmp(settingName, "enablePvtUdpServer") == 0)
+ settings.enablePvtUdpServer = settingValueBool;
else if (strcmp(settingName, "enableRCFirmware") == 0)
enableRCFirmware = settingValueBool;
else if (strcmp(settingName, "minElev") == 0)
diff --git a/Firmware/RTK_Surveyor/NVM.ino b/Firmware/RTK_Surveyor/NVM.ino
index 22ec198c0..80fa95046 100644
--- a/Firmware/RTK_Surveyor/NVM.ino
+++ b/Firmware/RTK_Surveyor/NVM.ino
@@ -323,8 +323,10 @@ void recordSystemSettingsToFile(File *settingsFile)
settingsFile->printf("%s=%d\r\n", "bluetoothRadioType", settings.bluetoothRadioType);
settingsFile->printf("%s=%d\r\n", "enablePvtClient", settings.enablePvtClient);
settingsFile->printf("%s=%d\r\n", "enablePvtServer", settings.enablePvtServer);
+ settingsFile->printf("%s=%d\r\n", "enablePvtUdpServer", settings.enablePvtUdpServer);
settingsFile->printf("%s=%d\r\n", "debugPvtClient", settings.debugPvtClient);
settingsFile->printf("%s=%d\r\n", "debugPvtServer", settings.debugPvtServer);
+ settingsFile->printf("%s=%d\r\n", "debugPvtUdpServer", settings.debugPvtUdpServer);
settingsFile->printf("%s=%d\r\n", "espnowBroadcast", settings.espnowBroadcast);
settingsFile->printf("%s=%d\r\n", "antennaHeight", settings.antennaHeight);
settingsFile->printf("%s=%0.2f\r\n", "antennaReferencePoint", settings.antennaReferencePoint);
@@ -350,6 +352,7 @@ void recordSystemSettingsToFile(File *settingsFile)
settingsFile->printf("%s=%d\r\n", "wifiConfigOverAP", settings.wifiConfigOverAP);
settingsFile->printf("%s=%d\r\n", "pvtServerPort", settings.pvtServerPort);
+ settingsFile->printf("%s=%d\r\n", "pvtUdpServerPort", settings.pvtUdpServerPort);
settingsFile->printf("%s=%d\r\n", "minElev", settings.minElev);
settingsFile->printf("%s=%d\r\n", "imuYaw", settings.imuYaw);
@@ -1130,10 +1133,14 @@ bool parseLine(char *str, Settings *settings)
settings->enablePvtClient = d;
else if (strcmp(settingName, "enablePvtServer") == 0)
settings->enablePvtServer = d;
+ else if (strcmp(settingName, "enablePvtUdpServer") == 0)
+ settings->enablePvtUdpServer = d;
else if (strcmp(settingName, "debugPvtClient") == 0)
settings->debugPvtClient = d;
else if (strcmp(settingName, "debugPvtServer") == 0)
settings->debugPvtServer = d;
+ else if (strcmp(settingName, "debugPvtUdpServer") == 0)
+ settings->debugPvtUdpServer = d;
else if (strcmp(settingName, "espnowBroadcast") == 0)
settings->espnowBroadcast = d;
else if (strcmp(settingName, "antennaHeight") == 0)
@@ -1162,6 +1169,8 @@ bool parseLine(char *str, Settings *settings)
settings->wifiConfigOverAP = d;
else if (strcmp(settingName, "pvtServerPort") == 0)
settings->pvtServerPort = d;
+ else if (strcmp(settingName, "pvtUdpServerPort") == 0)
+ settings->pvtUdpServerPort = d;
else if (strcmp(settingName, "minElev") == 0)
{
if (settings->minElev != d)
diff --git a/Firmware/RTK_Surveyor/Network.ino b/Firmware/RTK_Surveyor/Network.ino
index af0735623..298ebf489 100644
--- a/Firmware/RTK_Surveyor/Network.ino
+++ b/Firmware/RTK_Surveyor/Network.ino
@@ -186,6 +186,7 @@ const char * const networkUser[] =
"NTRIP Server",
"PVT Client",
"PVT Server",
+ "PVT UDP Server",
};
const int networkUserEntries = sizeof(networkUser) / sizeof(networkUser[0]);
@@ -228,6 +229,11 @@ void menuNetwork()
if (settings.enablePvtServer)
systemPrintf("5) PVT Server Port: %ld\r\n", settings.pvtServerPort);
+ systemPrintf("6) PVT UDP Server: %s\r\n", settings.enablePvtUdpServer ? "Enabled" : "Disabled");
+
+ if (settings.enablePvtUdpServer)
+ systemPrintf("7) PVT UDP Server Port: %ld\r\n", settings.pvtUdpServerPort);
+
if (HAS_ETHERNET)
{
//------------------------------
@@ -319,6 +325,27 @@ void menuNetwork()
}
}
+ //------------------------------
+ // Get the PVT UDP server parameters
+ //------------------------------
+
+ else if (incoming == 6)
+ // Toggle WiFi UDP NEMA server
+ settings.enablePvtUdpServer ^= 1;
+
+ else if (incoming == 7 && settings.enablePvtUdpServer)
+ {
+ systemPrint("Enter the UDP port to use (0 to 65535): ");
+ int portNumber = getNumber(); // Returns EXIT, TIMEOUT, or long
+ if ((portNumber != INPUT_RESPONSE_GETNUMBER_EXIT) && (portNumber != INPUT_RESPONSE_GETNUMBER_TIMEOUT))
+ {
+ if (portNumber < 0 || portNumber > 65535)
+ systemPrintln("Error: UDP Port out of range");
+ else
+ settings.pvtUdpServerPort = portNumber; // Recorded to NVM and file at main menu exit
+ }
+ }
+
//------------------------------
// Get the network layer parameters
//------------------------------
@@ -845,6 +872,12 @@ void networkStop(uint8_t networkType)
systemPrintln("Network layer stopping PVT server");
pvtServerStop();
break;
+
+ case NETWORK_USER_PVT_UDP_SERVER:
+ if (settings.debugNetworkLayer)
+ systemPrintln("Network layer stopping PVT UDP server");
+ pvtUdpServerStop();
+ break;
}
}
}
@@ -1082,6 +1115,7 @@ void networkUpdate()
ntripServerUpdate(); // Check the NTRIP server connection and move data ZED --> NTRIP
pvtClientUpdate(); // Turn on the PVT client as needed
pvtServerUpdate(); // Turn on the PVT server as needed
+ pvtUdpServerUpdate(); // Turn on the PVT UDP server as needed
}
// Display the IP addresses
diff --git a/Firmware/RTK_Surveyor/NetworkUDP.h b/Firmware/RTK_Surveyor/NetworkUDP.h
new file mode 100644
index 000000000..3548d0c75
--- /dev/null
+++ b/Firmware/RTK_Surveyor/NetworkUDP.h
@@ -0,0 +1,306 @@
+#ifndef __NETWORK_UDP_H__
+#define __NETWORK_UDP_H__
+
+extern uint8_t networkGetType(uint8_t user);
+
+class NetworkUDP : public UDP
+{
+ protected:
+
+ UDP * _udp; // Ethernet or WiFi udp
+ bool _friendClass;
+ uint8_t _networkType;
+
+ public:
+
+ //------------------------------
+ // Create the network client
+ //------------------------------
+ NetworkUDP(UDP * udp, uint8_t networkType)
+ {
+ _friendClass = true;
+ _networkType = networkType;
+ _udp = udp;
+ }
+
+ NetworkUDP(uint8_t user)
+ {
+ _friendClass = false;
+ _networkType = networkGetType(user);
+#if defined(COMPILE_ETHERNET)
+ if (_networkType == NETWORK_TYPE_ETHERNET)
+ _udp = new EthernetUDP;
+ else
+#endif // COMPILE_ETHERNET
+#if defined(COMPILE_WIFI)
+ _udp = new WiFiUDP;
+#else // COMPILE_WIFI
+ _udp = nullptr;
+#endif // COMPILE_WIFI
+ };
+
+ //------------------------------
+ // Delete the network client
+ //------------------------------
+ ~NetworkUDP()
+ {
+ if (_udp)
+ {
+ _udp->stop();
+ if (!_friendClass)
+ delete _udp;
+ _udp = nullptr;
+ }
+ };
+
+
+ //------------------------------
+ // Determine if the network client was allocated
+ //------------------------------
+
+ operator bool()
+ {
+ return _udp;
+ }
+
+ //------------------------------
+ // Start to the server
+ //------------------------------
+
+ uint8_t begin(uint16_t port)
+ {
+ if (_udp)
+ return _udp->begin(port);
+ return 0;
+ }
+
+ //------------------------------
+ // Stop the network client
+ //------------------------------
+
+ void stop()
+ {
+ if (_udp)
+ _udp->stop();
+ }
+
+ //------------------------------
+ // Determine if receive data is available
+ //------------------------------
+
+ int available()
+ {
+ if (_udp)
+ return _udp->available();
+ return 0;
+ }
+
+ //------------------------------
+ // Read available data
+ //------------------------------
+
+ int read()
+ {
+ if (_udp)
+ return _udp->read();
+ return 0;
+ }
+
+ //------------------------------
+ // Read available data
+ //------------------------------
+
+ int read(unsigned char* buf, size_t length)
+ {
+ if (_udp)
+ return _udp->read(buf, length);
+ return 0;
+ }
+
+ //------------------------------
+ // Read available data
+ //------------------------------
+
+ int read(char* buf, size_t length)
+ {
+ if (_udp)
+ return _udp->read(buf, length);
+ return 0;
+ }
+
+ //------------------------------
+ // Look at the next received byte in the data stream
+ //------------------------------
+
+ int peek()
+ {
+ if (_udp)
+ return _udp->peek();
+ return 0;
+ }
+
+ //------------------------------
+ // Finish transmitting all the data
+ //------------------------------
+
+ void flush()
+ {
+ if (_udp)
+ _udp->flush();
+ }
+
+ //------------------------------
+ // Send a data byte to the server
+ //------------------------------
+
+ size_t write(uint8_t b)
+ {
+ if (_udp)
+ return _udp->write(b);
+ return 0;
+ }
+
+ //------------------------------
+ // Send a buffer of data to the server
+ //------------------------------
+
+ size_t write(const uint8_t *buf, size_t size)
+ {
+ if (_udp)
+ return _udp->write(buf, size);
+ return 0;
+ }
+
+ //------------------------------
+ // Begin a UDP packet
+ //------------------------------
+
+ int beginPacket(IPAddress ip, uint16_t port)
+ {
+ if (_udp)
+ return _udp->beginPacket(ip, port);
+ return 0;
+ }
+
+ //------------------------------
+ // Begin a UDP packet
+ //------------------------------
+
+ int beginPacket(const char* host, uint16_t port)
+ {
+ if (_udp)
+ return _udp->beginPacket(host, port);
+ return 0;
+ }
+
+ //------------------------------
+ // Parse UDP packet
+ //------------------------------
+
+ int parsePacket()
+ {
+ if (_udp)
+ return _udp->parsePacket();
+ return 0;
+ }
+
+ //------------------------------
+ // End the current UDP packet
+ //------------------------------
+
+ int endPacket()
+ {
+ if (_udp)
+ return _udp->endPacket();
+ return 0;
+ }
+
+ //------------------------------
+ // Get the remote IP address
+ //------------------------------
+
+ IPAddress remoteIP()
+ {
+#if defined(COMPILE_ETHERNET)
+ if (_networkType == NETWORK_TYPE_ETHERNET)
+ return ((EthernetUDP *)_udp)->remoteIP();
+#endif // COMPILE_ETHERNET
+#if defined(COMPILE_WIFI)
+ if (_networkType == NETWORK_TYPE_WIFI)
+ return ((WiFiUDP *)_udp)->remoteIP();
+#endif // COMPILE_WIFI
+ return IPAddress((uint32_t)0);
+ }
+
+ //------------------------------
+ // Get the remote port number
+ //------------------------------
+
+ uint16_t remotePort()
+ {
+#if defined(COMPILE_ETHERNET)
+ if (_networkType == NETWORK_TYPE_ETHERNET)
+ return ((EthernetUDP *)_udp)->remotePort();
+#endif // COMPILE_ETHERNET
+#if defined(COMPILE_WIFI)
+ if (_networkType == NETWORK_TYPE_WIFI)
+ return ((WiFiUDP *)_udp)->remotePort();
+#endif // COMPILE_WIFI
+ return 0;
+ }
+
+ protected:
+
+ //------------------------------
+ // Declare the friend classes
+ //------------------------------
+
+ friend class NetworkEthernetUdp;
+ friend class NetworkWiFiUdp;
+};
+
+#ifdef COMPILE_ETHERNET
+class NetworkEthernetUdp : public NetworkUDP
+{
+ private:
+
+ EthernetUDP _udp;
+
+ public:
+
+ NetworkEthernetUdp(EthernetUDP& udp) :
+ _udp{udp},
+ NetworkUDP(&_udp, NETWORK_TYPE_ETHERNET)
+ {
+ }
+
+ ~NetworkEthernetUdp()
+ {
+ this->~NetworkUDP();
+ }
+};
+#endif // COMPILE_ETHERNET
+
+#ifdef COMPILE_WIFI
+class NetworkWiFiUdp : public NetworkUDP
+{
+ private:
+
+ WiFiUDP _udp;
+
+ public:
+
+ NetworkWiFiUdp(WiFiUDP& udp) :
+ _udp{udp},
+ NetworkUDP(&_udp, NETWORK_TYPE_WIFI)
+ {
+ }
+
+ ~NetworkWiFiUdp()
+ {
+ this->~NetworkUDP();
+ }
+};
+#endif // COMPILE_WIFI
+
+#endif // __NETWORK_CLIENT_H__
diff --git a/Firmware/RTK_Surveyor/PvtUdpServer.ino b/Firmware/RTK_Surveyor/PvtUdpServer.ino
new file mode 100644
index 000000000..4fe4b9e20
--- /dev/null
+++ b/Firmware/RTK_Surveyor/PvtUdpServer.ino
@@ -0,0 +1,372 @@
+/*
+pvtUdpServer.ino
+
+ The PVT (position, velocity and time) server sits on top of the network layer
+ and sends position data to one or more computers or cell phones for display.
+
+ Satellite ... Satellite
+ | | |
+ | | |
+ | V |
+ | RTK |
+ '------> Base <------'
+ Station
+ |
+ | NTRIP Server sends correction data
+ V
+ NTRIP Caster
+ |
+ | NTRIP Client receives correction data
+ |
+ V
+ Bluetooth RTK Network: PVT Client
+ .---------------- Rover ----------------------------------.
+ | | |
+ | | Network: PVT Server |
+ | PVT data | Position, velocity & time data | PVT data
+ | V |
+ | Computer or |
+ '------------> Cell Phone <-------------------------------'
+ for display
+
+ PVT UDP Server Testing:
+
+ RTK Express(+) with WiFi enabled, PvtUdpServer enabled, PvtUdpPort setup,
+ Smartphone with QField:
+
+ Network Setup:
+ Connect the Smartphone and the RTK Express to the same Network (e.g. the Smartphones HotSpot).
+
+ QField Setup:
+ * Open a project
+ * Open the left menu
+ * Click on the gear icon
+ * Click on Settings
+ * Click on Positioning
+ * Add a new Positioning device
+ * Set Connection type to UDP (NMEA)
+ * Set the Adress to
+ * Set the Port to the value of the specified PvtUdpPort (default 10110)
+ * Optional: give it a name (e.g. RTK Express UDP)
+ * Click on the Checkmark
+ * Make sure the new device is set as the Postioning device in use
+ * Exit the menus
+
+ 1. Long press on the location icon in the lower right corner
+
+ 2. Enable Show Position Information
+
+ 3. Verify that the displayed coordinates, fix tpe etc. are valid
+*/
+
+#ifdef COMPILE_WIFI
+
+//----------------------------------------
+// Constants
+//----------------------------------------
+
+// Define the PVT server states
+enum PvtUdpServerStates
+{
+ PVT_UDP_SERVER_STATE_OFF = 0,
+ PVT_UDP_SERVER_STATE_NETWORK_STARTED,
+ PVT_UDP_SERVER_STATE_RUNNING,
+ // Insert new states here
+ PVT_UDP_SERVER_STATE_MAX // Last entry in the state list
+};
+
+const char * const pvtUdpServerStateName[] =
+{
+ "PVT_UDP_SERVER_STATE_OFF",
+ "PVT_UDP_SERVER_STATE_NETWORK_STARTED",
+ "PVT_UDP_SERVER_STATE_RUNNING",
+};
+
+const int pvtUdpServerStateNameEntries = sizeof(pvtUdpServerStateName) / sizeof(pvtUdpServerStateName[0]);
+
+//----------------------------------------
+// Locals
+//----------------------------------------
+
+// PVT UDP server
+static NetworkUDP *pvtUdpServer = nullptr;
+static uint8_t pvtUdpServerState;
+static uint32_t pvtUdpServerTimer;
+static volatile RING_BUFFER_OFFSET pvtUdpServerTail;
+//----------------------------------------
+// PVT UDP Server handleGnssDataTask Support Routines
+//----------------------------------------
+
+// Send data as broadcast
+int32_t pvtUdpServerSendDataBroadcast(uint8_t *data, uint16_t length)
+{
+ if (!length)
+ return 0;
+
+ // Send the data as broadcast
+ if (settings.enablePvtUdpServer && online.pvtUdpServer && wifiIsConnected())
+ {
+ pvtUdpServer->beginPacket(WiFi.broadcastIP(), settings.pvtUdpServerPort);
+ pvtUdpServer->write(data, length);
+ if(pvtUdpServer->endPacket()){
+ if ((settings.debugPvtUdpServer || PERIODIC_DISPLAY(PD_PVT_UDP_SERVER_BROADCAST_DATA)) && (!inMainMenu))
+ {
+ systemPrintf("PVT UDP Server wrote %d bytes as broadcast on port %d\r\n", length, settings.pvtUdpServerPort);
+ PERIODIC_CLEAR(PD_PVT_UDP_SERVER_BROADCAST_DATA);
+ }
+ }
+ // Failed to write the data
+ else if ((settings.debugPvtUdpServer || PERIODIC_DISPLAY(PD_PVT_UDP_SERVER_BROADCAST_DATA)) && (!inMainMenu))
+ {
+ PERIODIC_CLEAR(PD_PVT_UDP_SERVER_BROADCAST_DATA);
+ systemPrintf("PVT UDP Server failed to write %d bytes as broadcast\r\n", length);
+ length = 0;
+ }
+ }
+ return length;
+}
+
+// Send PVT data as broadcast
+int32_t pvtUdpServerSendData(uint16_t dataHead)
+{
+ int32_t usedSpace = 0;
+
+ int32_t bytesToSend;
+
+ uint16_t tail;
+
+ tail = pvtUdpServerTail;
+
+ // Determine the amount of PVT data in the buffer
+ bytesToSend = dataHead - tail;
+ 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 ((tail + bytesToSend) > settings.gnssHandlerBufferSize)
+ bytesToSend = settings.gnssHandlerBufferSize - tail;
+
+ // Send the data
+ bytesToSend = pvtUdpServerSendDataBroadcast(&ringBuffer[tail], bytesToSend);
+
+ // Assume all data was sent, wrap the buffer pointer
+ tail += bytesToSend;
+ if (tail >= settings.gnssHandlerBufferSize)
+ tail -= settings.gnssHandlerBufferSize;
+
+ // Update space available for use in UART task
+ bytesToSend = dataHead - tail;
+ if (bytesToSend < 0)
+ bytesToSend += settings.gnssHandlerBufferSize;
+ if (usedSpace < bytesToSend)
+ usedSpace = bytesToSend;
+ }
+
+ pvtUdpServerTail = tail;
+
+ // Return the amount of space that PVT server client is using in the buffer
+ return usedSpace;
+}
+
+// Remove previous messages from the ring buffer
+void discardPvtUdpServerBytes(RING_BUFFER_OFFSET previousTail, RING_BUFFER_OFFSET newTail)
+{
+ int index;
+ uint16_t tail;
+
+ tail = pvtUdpServerTail;
+ if (previousTail < newTail)
+ {
+ // No buffer wrap occurred
+ if ((tail >= previousTail) && (tail < newTail))
+ pvtUdpServerTail = newTail;
+ }
+ else
+ {
+ // Buffer wrap occurred
+ if ((tail >= previousTail) || (tail < newTail))
+ pvtUdpServerTail = newTail;
+ }
+}
+
+//----------------------------------------
+// PVT Server Routines
+//----------------------------------------
+
+// Update the state of the PVT server state machine
+void pvtUdpServerSetState(uint8_t newState)
+{
+ if ((settings.debugPvtUdpServer || PERIODIC_DISPLAY(PD_PVT_UDP_SERVER_STATE)) && (!inMainMenu))
+ {
+ if (pvtUdpServerState == newState)
+ systemPrint("*");
+ else
+ systemPrintf("%s --> ", pvtUdpServerStateName[pvtUdpServerState]);
+ }
+ pvtUdpServerState = newState;
+ if ((settings.debugPvtUdpServer || PERIODIC_DISPLAY(PD_PVT_UDP_SERVER_STATE)) && (!inMainMenu))
+ {
+ PERIODIC_CLEAR(PD_PVT_UDP_SERVER_STATE);
+ if (newState >= PVT_UDP_SERVER_STATE_MAX)
+ {
+ systemPrintf("Unknown PVT UDP Server state: %d\r\n", pvtUdpServerState);
+ reportFatalError("Unknown PVT UDP Server state");
+ }
+ else
+ systemPrintln(pvtUdpServerStateName[pvtUdpServerState]);
+ }
+}
+
+// Start the PVT server
+bool pvtUdpServerStart()
+{
+ IPAddress localIp;
+
+ if (settings.debugPvtUdpServer && (!inMainMenu))
+ systemPrintln("PVT UDP server starting");
+
+ // Start the PVT server
+ pvtUdpServer = new NetworkUDP(NETWORK_USER_PVT_UDP_SERVER);
+ if (!pvtUdpServer)
+ return false;
+
+ pvtUdpServer->begin(settings.pvtUdpServerPort);
+ online.pvtUdpServer = true;
+ systemPrintf("PVT UDP server online, broadcasting to port %d\r\n", settings.pvtUdpServerPort);
+ return true;
+}
+
+// Stop the PVT UDP server
+void pvtUdpServerStop()
+{
+ int index;
+
+ // Notify the rest of the system that the PVT server is shutting down
+ if (online.pvtUdpServer)
+ {
+ // Notify the UART2 tasks of the PVT server shutdown
+ online.pvtUdpServer = false;
+ delay(5);
+ }
+
+ // Shutdown the PVT server
+ if (pvtUdpServer != nullptr)
+ {
+ // Stop the PVT server
+ if (settings.debugPvtUdpServer && (!inMainMenu))
+ systemPrintln("PVT UDP server stopping");
+ pvtUdpServer->stop();
+ delete pvtUdpServer;
+ pvtUdpServer = nullptr;
+ }
+
+ // Stop using the network
+ if (pvtUdpServerState != PVT_UDP_SERVER_STATE_OFF)
+ {
+ networkUserClose(NETWORK_USER_PVT_UDP_SERVER);
+
+ // The PVT server is now off
+ pvtUdpServerSetState(PVT_UDP_SERVER_STATE_OFF);
+ pvtUdpServerTimer = millis();
+ }
+}
+
+// Update the PVT server state
+void pvtUdpServerUpdate()
+{
+ bool connected;
+ bool dataSent;
+ int index;
+ IPAddress ipAddress;
+
+ /*
+ PVT UDP Server state machine
+
+ .--------------->PVT_UDP_SERVER_STATE_OFF
+ | |
+ | pvtUdpServerStop | settings.enablePvtUdpServer
+ | |
+ | V
+ +<---------PVT_UDP_SERVER_STATE_NETWORK_STARTED
+ ^ |
+ | | networkUserConnected
+ | |
+ | V
+ '--------------PVT_UDP_SERVER_STATE_RUNNING
+ */
+
+ DMW_st(pvtUdpServerSetState, pvtUdpServerState);
+ switch (pvtUdpServerState)
+ {
+ default:
+ break;
+
+ // Wait until the PVT server is enabled
+ case PVT_UDP_SERVER_STATE_OFF:
+ // Determine if the PVT server should be running
+ if (settings.enablePvtUdpServer && (!wifiInConfigMode()) && (!wifiIsConnected()))
+ {
+ if (networkUserOpen(NETWORK_USER_PVT_UDP_SERVER, NETWORK_TYPE_ACTIVE))
+ {
+ if (settings.debugPvtUdpServer && (!inMainMenu))
+ systemPrintln("PVT UDP server starting the network");
+ pvtUdpServerSetState(PVT_UDP_SERVER_STATE_NETWORK_STARTED);
+ }
+ }
+ break;
+
+ // Wait until the network is connected
+ case PVT_UDP_SERVER_STATE_NETWORK_STARTED:
+ // Determine if the network has failed
+ if (networkIsShuttingDown(NETWORK_USER_PVT_UDP_SERVER))
+ // Failed to connect to to the network, attempt to restart the network
+ pvtUdpServerStop();
+
+ // Wait for the network to connect to the media
+ else if (networkUserConnected(NETWORK_USER_PVT_UDP_SERVER))
+ {
+ // Delay before starting the PVT server
+ if ((millis() - pvtUdpServerTimer) >= (1 * 1000))
+ {
+ // The network type and host provide a valid configuration
+ pvtUdpServerTimer = millis();
+
+ // Start the PVT UDP server
+ if (pvtUdpServerStart())
+ pvtUdpServerSetState(PVT_UDP_SERVER_STATE_RUNNING);
+ }
+ }
+ break;
+
+ // Handle client connections and link failures
+ case PVT_UDP_SERVER_STATE_RUNNING:
+ // Determine if the network has failed
+ if ((!settings.enablePvtUdpServer) || networkIsShuttingDown(NETWORK_USER_PVT_UDP_SERVER))
+ {
+ if ((settings.debugPvtUdpServer || PERIODIC_DISPLAY(PD_PVT_UDP_SERVER_DATA)) && (!inMainMenu))
+ {
+ PERIODIC_CLEAR(PD_PVT_UDP_SERVER_DATA);
+ systemPrintln("PVT server initiating shutdown");
+ }
+
+ // Network connection failed, attempt to restart the network
+ pvtUdpServerStop();
+ break;
+ }
+ break;
+ }
+
+ // Periodically display the PVT state
+ if (PERIODIC_DISPLAY(PD_PVT_UDP_SERVER_STATE) && (!inMainMenu))
+ pvtUdpServerSetState(pvtUdpServerState);
+}
+
+// Zero the PVT server client tails
+void pvtUdpServerZeroTail()
+{
+ pvtUdpServerTail = 0;
+}
+
+#endif // COMPILE_WIFI
diff --git a/Firmware/RTK_Surveyor/RTK_Surveyor.ino b/Firmware/RTK_Surveyor/RTK_Surveyor.ino
index 44273ecb0..849bf95b7 100644
--- a/Firmware/RTK_Surveyor/RTK_Surveyor.ino
+++ b/Firmware/RTK_Surveyor/RTK_Surveyor.ino
@@ -576,6 +576,7 @@ unsigned long lastEthernetCheck = 0; // Prevents cable checking from continually
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include "NetworkClient.h" //Supports both WiFiClient and EthernetClient
+#include "NetworkUDP.h" //Supports both WiFiUdp and EthernetUdp
// Global variables
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
@@ -745,6 +746,7 @@ volatile bool deadManWalking;
settings.debugNtripServerState = true; \
settings.debugPvtClient = true; \
settings.debugPvtServer = true; \
+ settings.debugPvtUdpServer = true; \
}
#else // 0
diff --git a/Firmware/RTK_Surveyor/Tasks.ino b/Firmware/RTK_Surveyor/Tasks.ino
index 9f87b9a91..e3ab05ebe 100644
--- a/Firmware/RTK_Surveyor/Tasks.ino
+++ b/Firmware/RTK_Surveyor/Tasks.ino
@@ -55,6 +55,7 @@ enum RingBufferConsumers
RBC_PVT_CLIENT,
RBC_PVT_SERVER,
RBC_SD_CARD,
+ RBC_PVT_UDP_SERVER,
// Insert new consumers here
RBC_MAX
};
@@ -65,6 +66,7 @@ const char * const ringBufferConsumer[] =
"PVT Client",
"PVT Server",
"SD Card",
+ "PVT UDP Server",
};
const int ringBufferConsumerEntries = sizeof(ringBufferConsumer) / sizeof(ringBufferConsumer[0]);
@@ -626,6 +628,7 @@ void updateRingBufferTails(RING_BUFFER_OFFSET previousTail, RING_BUFFER_OFFSET n
discardRingBufferBytes(&sdRingBufferTail, previousTail, newTail);
discardPvtClientBytes(previousTail, newTail);
discardPvtServerBytes(previousTail, newTail);
+ discardPvtUdpServerBytes(previousTail, newTail);
}
// Remove previous messages from the ring buffer
@@ -685,6 +688,7 @@ void handleGnssDataTask(void *e)
btRingBufferTail = 0;
pvtClientZeroTail();
pvtServerZeroTail();
+ pvtUdpServerZeroTail();
sdRingBufferTail = 0;
while (true)
@@ -801,6 +805,21 @@ void handleGnssDataTask(void *e)
if (maxMillis[RBC_PVT_SERVER] < deltaMillis)
maxMillis[RBC_PVT_SERVER] = deltaMillis;
+ startMillis = millis();
+
+ // Update space available for use in UART task
+ bytesToSend = pvtUdpServerSendData(dataHead);
+ if (usedSpace < bytesToSend)
+ {
+ usedSpace = bytesToSend;
+ slowConsumer = "PVT UDP server";
+ }
+
+ // Remember the maximum transfer time
+ deltaMillis = millis() - startMillis;
+ if (maxMillis[RBC_PVT_UDP_SERVER] < deltaMillis)
+ maxMillis[RBC_PVT_UDP_SERVER] = deltaMillis;
+
//----------------------------------------------------------------------
// Log data to the SD card
//----------------------------------------------------------------------
diff --git a/Firmware/RTK_Surveyor/WiFi.ino b/Firmware/RTK_Surveyor/WiFi.ino
index 79abb8b81..3eee9b781 100644
--- a/Firmware/RTK_Surveyor/WiFi.ino
+++ b/Firmware/RTK_Surveyor/WiFi.ino
@@ -518,7 +518,7 @@ bool wifiConnect(unsigned long timeout)
int wifiResponse = wifiMulti.run(timeout);
if (wifiResponse == WL_CONNECTED)
{
- if (settings.enablePvtClient == true || settings.enablePvtServer == true)
+ if (settings.enablePvtClient == true || settings.enablePvtServer == true || settings.enablePvtUdpServer == true)
{
if (settings.mdnsEnable == true)
{
@@ -551,6 +551,8 @@ bool wifiIsNeeded()
needed = true;
if (settings.enablePvtServer == true)
needed = true;
+ if (settings.enablePvtUdpServer == true)
+ needed = true;
// Handle WiFi within systemStates
if (systemState <= STATE_ROVER_RTK_FIX && settings.enableNtripClient == true)
diff --git a/Firmware/RTK_Surveyor/menuSystem.ino b/Firmware/RTK_Surveyor/menuSystem.ino
index 6272576ed..46eb8749b 100644
--- a/Firmware/RTK_Surveyor/menuSystem.ino
+++ b/Firmware/RTK_Surveyor/menuSystem.ino
@@ -524,6 +524,10 @@ void menuDebugNetwork()
systemPrint("26) Debug PVT server: ");
systemPrintf("%s\r\n", settings.debugPvtServer ? "Enabled" : "Disabled");
+ // PVT Server
+ systemPrint("27) Debug PVT UDP server: ");
+ systemPrintf("%s\r\n", settings.debugPvtUdpServer ? "Enabled" : "Disabled");
+
systemPrintln("r) Force system reset");
systemPrintln("x) Exit");
@@ -554,6 +558,8 @@ void menuDebugNetwork()
settings.debugPvtClient ^= 1;
else if (incoming == 26)
settings.debugPvtServer ^= 1;
+ else if (incoming == 27)
+ settings.debugPvtUdpServer ^= 1;
// Menu exit control
else if (incoming == 'r')
diff --git a/Firmware/RTK_Surveyor/settings.h b/Firmware/RTK_Surveyor/settings.h
index b2a29edf2..7dddae9b1 100644
--- a/Firmware/RTK_Surveyor/settings.h
+++ b/Firmware/RTK_Surveyor/settings.h
@@ -183,6 +183,7 @@ enum NetworkUsers
NETWORK_USER_NTRIP_SERVER, // NTRIP server
NETWORK_USER_PVT_CLIENT, // PVT client
NETWORK_USER_PVT_SERVER, // PVT server
+ NETWORK_USER_PVT_UDP_SERVER, // PVT UDP server
// Last network user
NETWORK_USER_MAX
};
@@ -471,21 +472,25 @@ enum PeriodDisplayValues
PD_PVT_SERVER_STATE, // 15
PD_PVT_SERVER_CLIENT_DATA, // 16
- PD_RING_BUFFER_MILLIS, // 17
+ PD_PVT_UDP_SERVER_DATA, // 17
+ PD_PVT_UDP_SERVER_STATE, // 18
+ PD_PVT_UDP_SERVER_BROADCAST_DATA, // 19
- PD_SD_LOG_WRITE, // 18
+ PD_RING_BUFFER_MILLIS, // 20
- PD_TASK_BLUETOOTH_READ, // 19
- PD_TASK_BUTTON_CHECK, // 20
- PD_TASK_GNSS_READ, // 21
- PD_TASK_HANDLE_GNSS_DATA, // 22
- PD_TASK_SD_SIZE_CHECK, // 23
+ PD_SD_LOG_WRITE, // 21
- PD_WIFI_IP_ADDRESS, // 24
- PD_WIFI_STATE, // 25
+ PD_TASK_BLUETOOTH_READ, // 22
+ PD_TASK_BUTTON_CHECK, // 23
+ PD_TASK_GNSS_READ, // 24
+ PD_TASK_HANDLE_GNSS_DATA, // 25
+ PD_TASK_SD_SIZE_CHECK, // 26
- PD_ZED_DATA_RX, // 26
- PD_ZED_DATA_TX, // 27
+ PD_WIFI_IP_ADDRESS, // 27
+ PD_WIFI_STATE, // 28
+
+ PD_ZED_DATA_RX, // 29
+ PD_ZED_DATA_TX, // 30
};
#define PERIODIC_MASK(x) (1 << x)
@@ -1060,6 +1065,12 @@ typedef struct
bool enablePvtServer = false;
uint16_t pvtServerPort = 2948; // PVT server port, 2948 is GPS Daemon: http://tcp-udp-ports.com/port-2948.htm
+ // UDP Server
+ bool debugPvtUdpServer = false;
+ bool enablePvtUdpServer = false;
+ uint16_t pvtUdpServerPort =
+ 10110; //https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=nmea
+
uint8_t rtcmTimeoutBeforeUsingLBand_s = 10; //If 10s have passed without RTCM, enable PMP corrections over L-Band if available
//Add new settings above
@@ -1087,6 +1098,7 @@ struct struct_online
bool i2c = false;
bool pvtClient = false;
bool pvtServer = false;
+ bool pvtUdpServer = false;
ethernetStatus_e ethernetStatus = ETH_NOT_STARTED;
bool NTPServer = false; // EthernetUDP
} online;
diff --git a/docs/configure_with_settings_file.md b/docs/configure_with_settings_file.md
index ffe615f36..d9fbccbc1 100644
--- a/docs/configure_with_settings_file.md
+++ b/docs/configure_with_settings_file.md
@@ -20,7 +20,7 @@ Keep in mind:
For example, if you only wanted to set up two wireless networks for profile 2, you could create a file named "SFE_Express_Settings_1.txt" that only contained the following settings:
profileName=a name you choose
- enableTcpServer=1
+ enablePvtServer=1
wifiNetwork0SSID=your SSID name 1
wifiNetwork0Password=your SSID password 1
wifiNetwork1SSID=your SSID name 2