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