diff --git a/Firmware/RTK_Everywhere/Begin.ino b/Firmware/RTK_Everywhere/Begin.ino index 0489081ac..edb3a8321 100644 --- a/Firmware/RTK_Everywhere/Begin.ino +++ b/Firmware/RTK_Everywhere/Begin.ino @@ -778,7 +778,6 @@ void beginFS() } // Check if configureViaEthernet.txt exists -// Used to indicate if SparkFun_WebServer_ESP32_W5500 needs _exclusive_ access to SPI and interrupts bool checkConfigureViaEthernet() { if (online.fs == false) @@ -796,7 +795,6 @@ bool checkConfigureViaEthernet() } // Force configure-via-ethernet mode by creating configureViaEthernet.txt in LittleFS -// Used to indicate if SparkFun_WebServer_ESP32_W5500 needs _exclusive_ access to SPI and interrupts bool forceConfigureViaEthernet() { if (online.fs == false) @@ -836,8 +834,6 @@ void beginInterrupts() { DMW_if systemPrintf("pin_Ethernet_Interrupt: %d\r\n", pin_Ethernet_Interrupt); pinMode(pin_Ethernet_Interrupt, INPUT); // Prepare the interrupt pin - // TODO: figure out how to handle NTP mode and timestamp the arrival of UDP NTP requests - // attachInterrupt(pin_Ethernet_Interrupt, ethernetISR, FALLING); // Attach the interrupt } #endif // COMPILE_ETHERNET } diff --git a/Firmware/RTK_Everywhere/Display.ino b/Firmware/RTK_Everywhere/Display.ino index 44fb44835..c53e2559a 100644 --- a/Firmware/RTK_Everywhere/Display.ino +++ b/Firmware/RTK_Everywhere/Display.ino @@ -261,6 +261,7 @@ void displayUpdate() paintLogging(&iconPropertyList); displaySivVsOpenShort(&iconPropertyList); displayBatteryVsEthernet(&iconPropertyList); + displayFullIPAddress(&iconPropertyList); // Bottom left - 128x64 only setRadioIcons(&iconPropertyList); break; case (STATE_ROVER_NO_FIX): @@ -269,6 +270,7 @@ void displayUpdate() paintLogging(&iconPropertyList); displaySivVsOpenShort(&iconPropertyList); displayBatteryVsEthernet(&iconPropertyList); + displayFullIPAddress(&iconPropertyList); // Bottom left - 128x64 only setRadioIcons(&iconPropertyList); break; case (STATE_ROVER_FIX): @@ -277,6 +279,7 @@ void displayUpdate() paintLogging(&iconPropertyList); displaySivVsOpenShort(&iconPropertyList); displayBatteryVsEthernet(&iconPropertyList); + displayFullIPAddress(&iconPropertyList); // Bottom left - 128x64 only setRadioIcons(&iconPropertyList); break; case (STATE_ROVER_RTK_FLOAT): @@ -285,6 +288,7 @@ void displayUpdate() paintLogging(&iconPropertyList); displaySivVsOpenShort(&iconPropertyList); displayBatteryVsEthernet(&iconPropertyList); + displayFullIPAddress(&iconPropertyList); // Bottom left - 128x64 only setRadioIcons(&iconPropertyList); break; case (STATE_ROVER_RTK_FIX): @@ -293,6 +297,7 @@ void displayUpdate() paintLogging(&iconPropertyList); displaySivVsOpenShort(&iconPropertyList); displayBatteryVsEthernet(&iconPropertyList); + displayFullIPAddress(&iconPropertyList); // Bottom left - 128x64 only setRadioIcons(&iconPropertyList); break; @@ -309,27 +314,32 @@ void displayUpdate() paintLogging(&iconPropertyList); displaySivVsOpenShort(&iconPropertyList); displayBatteryVsEthernet(&iconPropertyList); + displayFullIPAddress(&iconPropertyList); // Bottom left - 128x64 only setRadioIcons(&iconPropertyList); break; case (STATE_BASE_TEMP_SURVEY_STARTED): paintLogging(&iconPropertyList); displayBatteryVsEthernet(&iconPropertyList); // Top right + displayFullIPAddress(&iconPropertyList); // Bottom left - 128x64 only setRadioIcons(&iconPropertyList); paintBaseTempSurveyStarted(&iconPropertyList); break; case (STATE_BASE_TEMP_TRANSMITTING): paintLogging(&iconPropertyList); displayBatteryVsEthernet(&iconPropertyList); // Top right + displayFullIPAddress(&iconPropertyList); // Bottom left - 128x64 only setRadioIcons(&iconPropertyList); paintRTCM(&iconPropertyList); break; case (STATE_BASE_FIXED_NOT_STARTED): displayBatteryVsEthernet(&iconPropertyList); // Top right + displayFullIPAddress(&iconPropertyList); // Bottom left - 128x64 only setRadioIcons(&iconPropertyList); break; case (STATE_BASE_FIXED_TRANSMITTING): paintLogging(&iconPropertyList); displayBatteryVsEthernet(&iconPropertyList); // Top right + displayFullIPAddress(&iconPropertyList); // Bottom left - 128x64 only setRadioIcons(&iconPropertyList); paintRTCM(&iconPropertyList); break; @@ -1767,36 +1777,66 @@ void paintConnectingToNtripCaster() printTextwithKerning("Connecting", textX, textY, textKerning); } -// Scroll through IP address. Wipe with spaces both ends. +// Shuttle through IP address void paintIPAddress() { - char ipAddress[32]; - snprintf(ipAddress, sizeof(ipAddress), " %d.%d.%d.%d ", + char ipAddress[16]; + snprintf(ipAddress, sizeof(ipAddress), "%s", #ifdef COMPILE_ETHERNET - ETH.localIP()[0], ETH.localIP()[1], ETH.localIP()[2], ETH.localIP()[3]); + ETH.localIP().toString()); #else // COMPILE_ETHERNET - 0, 0, 0, 0); + "0.0.0.0"); #endif // COMPILE_ETHERNET - static uint8_t ipAddressPosition = 0; + oled->setFont(QW_FONT_5X7); // Set font to smallest + oled->setCursor(0, 3); - // Check if IP address is all single digits and can be printed without scrolling - if (strlen(ipAddress) <= 21) - ipAddressPosition = 7; + // If we can print the full IP address without shuttling + if (strlen(ipAddress) <= 7) + { + oled->print(ipAddress); + } + else + { + // Print as many characters as we can. Shuttle back and forth to display all. + static int startPos = 0; + char printThis[7 + 1]; + int extras = strlen(ipAddress) - 7; + int shuttle[(2 * extras) + 2]; // Wait for a double state at each end + shuttle[0] = 0; + int x; + for (x = 0; x <= extras; x++) + shuttle[x + 1] = x; + shuttle[extras + 2] = extras; + x += 2; + for (int y = extras - 1; y > 0; y--) + shuttle[x++] = y; + if (startPos >= (2 * extras) + 2) + startPos = 0; + snprintf(printThis, sizeof(printThis), &ipAddress[shuttle[startPos]]); + startPos++; + oled->print(printThis); + } +} + +void displayFullIPAddress(std::vector *iconList) // Bottom left - 128x64 only +{ + if (present.display_type == DISPLAY_128x64) + { + char myAddress[16]; - // Print seven characters of IP address - char printThis[9]; - snprintf(printThis, sizeof(printThis), "%c%c%c%c%c%c%c", ipAddress[ipAddressPosition + 0], - ipAddress[ipAddressPosition + 1], ipAddress[ipAddressPosition + 2], ipAddress[ipAddressPosition + 3], - ipAddress[ipAddressPosition + 4], ipAddress[ipAddressPosition + 5], ipAddress[ipAddressPosition + 6]); + uint8_t networkType = networkGetType(); + IPAddress ipAddress = networkGetIpAddress(networkType); - oled->setFont(QW_FONT_5X7); // Set font to smallest - oled->setCursor(0, 3); - oled->print(printThis); + if (ipAddress != IPAddress((uint32_t)0)) + { + snprintf(myAddress, sizeof(myAddress), "%s", ipAddress.toString()); - ipAddressPosition++; // Increment the print position - if (ipAddress[ipAddressPosition + 7] == 0) // Wrap - ipAddressPosition = 0; + oled->setFont(QW_FONT_5X7); // Set font to smallest + oled->setCursor(0, 55); + oled->print(ipAddress); + } + } } void paintMACAddress4digit(uint8_t xPos, uint8_t yPos) @@ -2081,7 +2121,7 @@ void displayWiFiConfig() // Convert to string char myIP[20] = {'\0'}; - snprintf(myIP, sizeof(myIP), "%d.%d.%d.%d", myIpAddress[0], myIpAddress[1], myIpAddress[2], myIpAddress[3]); + snprintf(myIP, sizeof(myIP), "%s", myIpAddress.toString()); char myIPFront[displayMaxCharacters + 1]; // 1 for null terminator char myIPBack[displayMaxCharacters + 1]; // 1 for null terminator @@ -3008,7 +3048,7 @@ void displayConfigViaEthernet() char ipAddress[16]; IPAddress localIP = ETH.localIP(); - snprintf(ipAddress, sizeof(ipAddress), "%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]); + snprintf(ipAddress, sizeof(ipAddress), "%s", localIP.toString()); int displayWidthChars = ((present.display_type == DISPLAY_128x64) ? 21 : 10); diff --git a/Firmware/RTK_Everywhere/Ethernet.ino b/Firmware/RTK_Everywhere/Ethernet.ino index 72298779e..d0a244e13 100644 --- a/Firmware/RTK_Everywhere/Ethernet.ino +++ b/Firmware/RTK_Everywhere/Ethernet.ino @@ -208,17 +208,6 @@ bool ethernetIsNeeded() return needed; } -// Ethernet (W5500) ISR -// Triggered by the falling edge of the W5500 interrupt signal - indicates the arrival of a packet -// Record the time the packet arrived -void ethernetISR() -{ - // Don't check or clear the interrupt here - - // it may clash with a GNSS SPI transaction and cause a wdt timeout. - // Do it in updateEthernet - gettimeofday((timeval *)ðernetNtpTv, nullptr); // Record the time of the NTP interrupt -} - // Restart the Ethernet controller void ethernetRestart() { @@ -393,7 +382,7 @@ void onEthernetEvent(arduino_event_id_t event, arduino_event_info_t info) // Web server routines //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -// Start Ethernet WebServer ESP32 W5500 - needs exclusive access to WiFi, SPI and Interrupts +// Start Ethernet for the web server void ethernetWebServerStartESP32W5500() { Network.onEvent(onEthernetEvent); @@ -405,10 +394,10 @@ void ethernetWebServerStartESP32W5500() ETH.config(settings.ethernetIP, settings.ethernetGateway, settings.ethernetSubnet, settings.ethernetDNS); } -// Stop the Ethernet web server +// Stop Ethernet for the web server void ethernetWebServerStopESP32W5500() { - ETH.end(); // This is _really_ important. It undoes the low-level changes to SPI and interrupts + ETH.end(); } #endif // COMPILE_ETHERNET diff --git a/Firmware/RTK_Everywhere/NTP.ino b/Firmware/RTK_Everywhere/NTP.ino index 739b27dfd..22856d34e 100644 --- a/Firmware/RTK_Everywhere/NTP.ino +++ b/Firmware/RTK_Everywhere/NTP.ino @@ -78,8 +78,6 @@ const RtkMode_t ntpServerMode = RTK_MODE_NTP; static NetworkUDP *ntpServer; // This will be instantiated when we know the NTP port static uint8_t ntpServerState; -static volatile uint8_t - ntpSockIndex; // The W5500 socket index for NTP - so we can enable and read the correct interrupt static uint32_t lastLoggedNTPRequest; //---------------------------------------- @@ -115,6 +113,9 @@ void menuNTP() systemPrint("5) Reference ID: "); systemPrintln(settings.ntpReferenceId); + systemPrint("6) NTP Port: "); + systemPrintln(settings.ethernetNtpPort); + systemPrintln("x) Exit"); byte incoming = getUserInputCharacterNumber(); @@ -170,6 +171,15 @@ void menuNTP() else systemPrintln("Error: invalid Reference ID"); } + else if (incoming == 6) + { + systemPrint("Enter new NTP port: "); + long newVal = getUserInputNumber(); + if ((newVal >= 0) && (newVal <= 65535)) + settings.ethernetNtpPort = newVal; + else + systemPrintln("Error: port number out of range"); + } else if (incoming == 'x') break; else if (incoming == INPUT_RESPONSE_GETCHARACTERNUMBER_EMPTY) @@ -474,7 +484,7 @@ struct NTPpacket // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // NTP process one request -// recTv contains the timeval the NTP packet was received - from the W5500 interrupt +// recTv contains the timeval the NTP packet was received // syncTv contains the timeval when the RTC was last sync'd // ntpDiag will contain useful diagnostics bool ntpProcessOneRequest(bool process, const timeval *recTv, const timeval *syncTv, char *ntpDiag = nullptr, @@ -486,162 +496,195 @@ bool ntpProcessOneRequest(bool process, const timeval *recTv, const timeval *syn if (ntpDiag != nullptr) *ntpDiag = 0; // Clear any existing diagnostics - int packetDataSize = ntpServer->parsePacket(); - - IPAddress remoteIP = ntpServer->remoteIP(); - uint16_t remotePort = ntpServer->remotePort(); + ntpServer->parsePacket(); - if (ntpDiag != nullptr) // Add the packet size and remote IP/Port to the diagnostics - { - snprintf(ntpDiag, ntpDiagSize, "NTP request from: Remote IP: %d.%d.%d.%d Remote Port: %d\r\n", remoteIP[0], - remoteIP[1], remoteIP[2], remoteIP[3], remotePort); - } + int packetDataSize = ntpServer->available(); - if (packetDataSize && (packetDataSize >= NTPpacket::NTPpacketSize)) + if (packetDataSize > 0) { - // Read the NTP packet - NTPpacket packet; + gettimeofday((timeval *)ðernetNtpTv, nullptr); // Record the time of the NTP request. This was in ethernetISR() - ntpServer->read((char *)&packet.packet, NTPpacket::NTPpacketSize); // Copy the NTP data into our packet + IPAddress remoteIP = ntpServer->remoteIP(); + uint16_t remotePort = ntpServer->remotePort(); - // If process is false, return now - if (!process) + if (ntpDiag != nullptr) // Add the packet size and remote IP/Port to the diagnostics { - char tmpbuf[128]; - snprintf(tmpbuf, sizeof(tmpbuf), - "NTP request ignored. Time has not been synchronized - or not in NTP mode.\r\n"); - strlcat(ntpDiag, tmpbuf, ntpDiagSize); - return false; + snprintf(ntpDiag, ntpDiagSize, "NTP request from: Remote IP: %s Remote Port: %d\r\n", + remoteIP.toString(), remotePort); } - packet.extract(); // Extract the raw data into fields - - packet.LI(packet.defaultLeapInd); // Clear the leap second adjustment. TODO: set this correctly using - // getLeapSecondEvent from the GNSS - packet.VN(packet.defaultVersion); // Set the version number - packet.mode(packet.defaultMode); // Set the mode - packet.stratum = packet.defaultStratum; // Set the stratum - packet.pollExponent = settings.ntpPollExponent; // Set the poll interval - packet.precision = settings.ntpPrecision; // Set the precision - packet.rootDelay = packet.convertMicrosToSecsAndFraction(settings.ntpRootDelay); // Set the Root Delay - packet.rootDispersion = - packet.convertMicrosToSecsAndFraction(settings.ntpRootDispersion); // Set the Root Dispersion - for (uint8_t i = 0; i < packet.referenceIdLen; i++) - packet.referenceId[i] = settings.ntpReferenceId[i]; // Set the reference Id - - // REF: http://support.ntp.org/bin/view/Support/DraftRfc2030 - // '.. the client sets the Transmit Timestamp field in the request - // to the time of day according to the client clock in NTP timestamp format.' - // '.. The server copies this field to the originate timestamp in the reply and - // sets the Receive Timestamp and Transmit Timestamp fields to the time of day - // according to the server clock in NTP timestamp format.' - - // Important note: the NTP Era started January 1st 1900. - // tv will contain the time based on the Unix epoch (January 1st 1970) - // We need to adjust... - - // First, add the client transmit timestamp to our diagnostics - if (ntpDiag != nullptr) + if (packetDataSize >= NTPpacket::NTPpacketSize) { - char tmpbuf[128]; - snprintf(tmpbuf, sizeof(tmpbuf), "Originate Timestamp (Client Transmit): %u.%06u\r\n", - packet.transmitTimestampSeconds, packet.convertFractionToMicros(packet.transmitTimestampFraction)); - strlcat(ntpDiag, tmpbuf, ntpDiagSize); - } + // Read the NTP packet + NTPpacket packet; - // Copy the client transmit timestamp into the originate timestamp - packet.originateTimestampSeconds = packet.transmitTimestampSeconds; - packet.originateTimestampFraction = packet.transmitTimestampFraction; + ntpServer->read((char *)&packet.packet, NTPpacket::NTPpacketSize); // Copy the NTP data into our packet - // Set the receive timestamp to the time we received the packet (logged by the W5500 interrupt) - uint32_t recUnixSeconds = recTv->tv_sec; - recUnixSeconds -= settings.timeZoneSeconds; // Subtract the time zone offset to convert recTv to Unix time - recUnixSeconds -= settings.timeZoneMinutes * 60; - recUnixSeconds -= settings.timeZoneHours * 60 * 60; - packet.receiveTimestampSeconds = packet.convertUnixSecondsToNTP(recUnixSeconds); // Unix -> NTP - packet.receiveTimestampFraction = packet.convertMicrosToFraction(recTv->tv_usec); // Micros to 1/2^32 + /* + // Add the incoming packet to the diagnostics + if (ntpDiag != nullptr) + { + char tmpbuf[128]; + snprintf(tmpbuf, sizeof(tmpbuf), "Packet <-- : "); + strlcat(ntpDiag, tmpbuf, ntpDiagSize); + for (int i = 0; i < NTPpacket::NTPpacketSize; i++) + { + snprintf(tmpbuf, sizeof(tmpbuf), "%02X ", packet.packet[i]); + strlcat(ntpDiag, tmpbuf, ntpDiagSize); + } + snprintf(tmpbuf, sizeof(tmpbuf), "\r\n"); + strlcat(ntpDiag, tmpbuf, ntpDiagSize); + } + */ - // Add the receive timestamp to the diagnostics - if (ntpDiag != nullptr) - { - char tmpbuf[128]; - snprintf(tmpbuf, sizeof(tmpbuf), "Received Timestamp: %u.%06u\r\n", - packet.receiveTimestampSeconds, packet.convertFractionToMicros(packet.receiveTimestampFraction)); - strlcat(ntpDiag, tmpbuf, ntpDiagSize); - } + // If process is false, return now + if (!process) + { + char tmpbuf[128]; + snprintf(tmpbuf, sizeof(tmpbuf), + "NTP request ignored. Time has not been synchronized - or not in NTP mode.\r\n"); + strlcat(ntpDiag, tmpbuf, ntpDiagSize); + return false; + } + + packet.extract(); // Extract the raw data into fields + + packet.LI(packet.defaultLeapInd); // Clear the leap second adjustment. TODO: set this correctly using + // getLeapSecondEvent from the GNSS + packet.VN(packet.defaultVersion); // Set the version number + packet.mode(packet.defaultMode); // Set the mode + packet.stratum = packet.defaultStratum; // Set the stratum + packet.pollExponent = settings.ntpPollExponent; // Set the poll interval + packet.precision = settings.ntpPrecision; // Set the precision + packet.rootDelay = packet.convertMicrosToSecsAndFraction(settings.ntpRootDelay); // Set the Root Delay + packet.rootDispersion = + packet.convertMicrosToSecsAndFraction(settings.ntpRootDispersion); // Set the Root Dispersion + for (uint8_t i = 0; i < packet.referenceIdLen; i++) + packet.referenceId[i] = settings.ntpReferenceId[i]; // Set the reference Id + + // REF: http://support.ntp.org/bin/view/Support/DraftRfc2030 + // '.. the client sets the Transmit Timestamp field in the request + // to the time of day according to the client clock in NTP timestamp format.' + // '.. The server copies this field to the originate timestamp in the reply and + // sets the Receive Timestamp and Transmit Timestamp fields to the time of day + // according to the server clock in NTP timestamp format.' + + // Important note: the NTP Era started January 1st 1900. + // tv will contain the time based on the Unix epoch (January 1st 1970) + // We need to adjust... + + // First, add the client transmit timestamp to our diagnostics + if (ntpDiag != nullptr) + { + char tmpbuf[128]; + snprintf(tmpbuf, sizeof(tmpbuf), "Originate Timestamp (Client Transmit): %u.%06u\r\n", + packet.transmitTimestampSeconds, packet.convertFractionToMicros(packet.transmitTimestampFraction)); + strlcat(ntpDiag, tmpbuf, ntpDiagSize); + } - // Add when our clock was last sync'd - uint32_t syncUnixSeconds = syncTv->tv_sec; - syncUnixSeconds -= settings.timeZoneSeconds; // Subtract the time zone offset to convert recTv to Unix time - syncUnixSeconds -= settings.timeZoneMinutes * 60; - syncUnixSeconds -= settings.timeZoneHours * 60 * 60; - packet.referenceTimestampSeconds = packet.convertUnixSecondsToNTP(syncUnixSeconds); // Unix -> NTP - packet.referenceTimestampFraction = packet.convertMicrosToFraction(syncTv->tv_usec); // Micros to 1/2^32 + // Copy the client transmit timestamp into the originate timestamp + packet.originateTimestampSeconds = packet.transmitTimestampSeconds; + packet.originateTimestampFraction = packet.transmitTimestampFraction; - // Add that to the diagnostics - if (ntpDiag != nullptr) - { - char tmpbuf[128]; - snprintf(tmpbuf, sizeof(tmpbuf), "Reference Timestamp (Last Sync): %u.%06u\r\n", - packet.referenceTimestampSeconds, - packet.convertFractionToMicros(packet.referenceTimestampFraction)); - strlcat(ntpDiag, tmpbuf, ntpDiagSize); - } + // Set the receive timestamp to the time we received the packet (logged by the W5500 interrupt) + uint32_t recUnixSeconds = recTv->tv_sec; + recUnixSeconds -= settings.timeZoneSeconds; // Subtract the time zone offset to convert recTv to Unix time + recUnixSeconds -= settings.timeZoneMinutes * 60; + recUnixSeconds -= settings.timeZoneHours * 60 * 60; + packet.receiveTimestampSeconds = packet.convertUnixSecondsToNTP(recUnixSeconds); // Unix -> NTP + packet.receiveTimestampFraction = packet.convertMicrosToFraction(recTv->tv_usec); // Micros to 1/2^32 + + // Add the receive timestamp to the diagnostics + if (ntpDiag != nullptr) + { + char tmpbuf[128]; + snprintf(tmpbuf, sizeof(tmpbuf), "Received Timestamp: %u.%06u\r\n", + packet.receiveTimestampSeconds, packet.convertFractionToMicros(packet.receiveTimestampFraction)); + strlcat(ntpDiag, tmpbuf, ntpDiagSize); + } - // Add the transmit time - i.e. now! - timeval txTime; - gettimeofday(&txTime, nullptr); - uint32_t nowUnixSeconds = txTime.tv_sec; - nowUnixSeconds -= settings.timeZoneSeconds; // Subtract the time zone offset to convert recTv to Unix time - nowUnixSeconds -= settings.timeZoneMinutes * 60; - nowUnixSeconds -= settings.timeZoneHours * 60 * 60; - packet.transmitTimestampSeconds = packet.convertUnixSecondsToNTP(nowUnixSeconds); // Unix -> NTP - packet.transmitTimestampFraction = packet.convertMicrosToFraction(txTime.tv_usec); // Micros to 1/2^32 - - packet.insert(); // Copy the data fields back into the buffer - - // Now transmit the response to the client. - ntpServer->beginPacket(remoteIP, remotePort); - ntpServer->write(packet.packet, NTPpacket::NTPpacketSize); - // int result = ntpServer->endPacket(); - processed = true; - - // Add our server transmit time to the diagnostics - if (ntpDiag != nullptr) + // Add when our clock was last sync'd + uint32_t syncUnixSeconds = syncTv->tv_sec; + syncUnixSeconds -= settings.timeZoneSeconds; // Subtract the time zone offset to convert recTv to Unix time + syncUnixSeconds -= settings.timeZoneMinutes * 60; + syncUnixSeconds -= settings.timeZoneHours * 60 * 60; + packet.referenceTimestampSeconds = packet.convertUnixSecondsToNTP(syncUnixSeconds); // Unix -> NTP + packet.referenceTimestampFraction = packet.convertMicrosToFraction(syncTv->tv_usec); // Micros to 1/2^32 + + // Add that to the diagnostics + if (ntpDiag != nullptr) + { + char tmpbuf[128]; + snprintf(tmpbuf, sizeof(tmpbuf), "Reference Timestamp (Last Sync): %u.%06u\r\n", + packet.referenceTimestampSeconds, + packet.convertFractionToMicros(packet.referenceTimestampFraction)); + strlcat(ntpDiag, tmpbuf, ntpDiagSize); + } + + // Add the transmit time - i.e. now! + timeval txTime; + gettimeofday(&txTime, nullptr); + uint32_t nowUnixSeconds = txTime.tv_sec; + nowUnixSeconds -= settings.timeZoneSeconds; // Subtract the time zone offset to convert recTv to Unix time + nowUnixSeconds -= settings.timeZoneMinutes * 60; + nowUnixSeconds -= settings.timeZoneHours * 60 * 60; + packet.transmitTimestampSeconds = packet.convertUnixSecondsToNTP(nowUnixSeconds); // Unix -> NTP + packet.transmitTimestampFraction = packet.convertMicrosToFraction(txTime.tv_usec); // Micros to 1/2^32 + + packet.insert(); // Copy the data fields back into the buffer + + // Now transmit the response to the client. + ntpServer->beginPacket(remoteIP, remotePort); + ntpServer->write(packet.packet, NTPpacket::NTPpacketSize); + int result = ntpServer->endPacket(); + processed = true; + + // Add our server transmit time to the diagnostics + if (ntpDiag != nullptr) + { + char tmpbuf[128]; + snprintf(tmpbuf, sizeof(tmpbuf), "Transmit Timestamp: %u.%06u\r\n", + packet.transmitTimestampSeconds, packet.convertFractionToMicros(packet.transmitTimestampFraction)); + strlcat(ntpDiag, tmpbuf, ntpDiagSize); + } + + /* + // Add the socketSendUDP result to the diagnostics + if (ntpDiag != nullptr) + { + char tmpbuf[128]; + snprintf(tmpbuf, sizeof(tmpbuf), "socketSendUDP result: %d\r\n", result); + strlcat(ntpDiag, tmpbuf, ntpDiagSize); + } + */ + + /* + // Add the outgoing packet to the diagnostics + if (ntpDiag != nullptr) + { + char tmpbuf[128]; + snprintf(tmpbuf, sizeof(tmpbuf), "Packet --> : "); + strlcat(ntpDiag, tmpbuf, ntpDiagSize); + for (int i = 0; i < NTPpacket::NTPpacketSize; i++) + { + snprintf(tmpbuf, sizeof(tmpbuf), "%02X ", packet.packet[i]); + strlcat(ntpDiag, tmpbuf, ntpDiagSize); + } + snprintf(tmpbuf, sizeof(tmpbuf), "\r\n"); + strlcat(ntpDiag, tmpbuf, ntpDiagSize); + } + */ + } + else { - char tmpbuf[128]; - snprintf(tmpbuf, sizeof(tmpbuf), "Transmit Timestamp: %u.%06u\r\n", - packet.transmitTimestampSeconds, packet.convertFractionToMicros(packet.transmitTimestampFraction)); + char tmpbuf[64]; + snprintf(tmpbuf, sizeof(tmpbuf), "Invalid size: %d\r\n", packetDataSize); strlcat(ntpDiag, tmpbuf, ntpDiagSize); } - - /* - // Add the socketSendUDP result to the diagnostics - if (ntpDiag != nullptr) - { - char tmpbuf[128]; - snprintf(tmpbuf, sizeof(tmpbuf), "socketSendUDP result: %d\r\n", result); - strlcat(ntpDiag, tmpbuf, ntpDiagSize); - } - */ - - /* - // Add the packet to the diagnostics - if (ntpDiag != nullptr) - { - char tmpbuf[128]; - snprintf(tmpbuf, sizeof(tmpbuf), "Packet: "); - strlcat(ntpDiag, tmpbuf, ntpDiagSize); - for (int i = 0; i < NTPpacket::NTPpacketSize; i++) - { - snprintf(tmpbuf, sizeof(tmpbuf), "%02X ", packet.packet[i]); - strlcat(ntpDiag, tmpbuf, ntpDiagSize); - } - snprintf(tmpbuf, sizeof(tmpbuf), "\r\n"); - strlcat(ntpDiag, tmpbuf, ntpDiagSize); - } - */ } + + ntpServer->clear(); + return processed; } @@ -747,9 +790,9 @@ void ntpServerSetState(uint8_t newState) if ((settings.debugNtp || PERIODIC_DISPLAY(PD_NTP_SERVER_STATE)) && (!inMainMenu)) { if (ntpServerState == newState) - systemPrint("*"); + systemPrint("NTP Server: *"); else - systemPrintf("%s --> ", ntpServerStateName[ntpServerState]); + systemPrintf("NTP Server: %s --> ", ntpServerStateName[ntpServerState]); } ntpServerState = newState; if (settings.debugNtp || PERIODIC_DISPLAY(PD_NTP_SERVER_STATE)) @@ -757,7 +800,7 @@ void ntpServerSetState(uint8_t newState) PERIODIC_CLEAR(PD_NTP_SERVER_STATE); if (newState >= NTP_STATE_MAX) { - systemPrintf("Unknown NTP Server state: %d\r\n", newState); + systemPrintf("Unknown state: %d\r\n", newState); reportFatalError("Unknown NTP Server state"); } else if (!inMainMenu) @@ -774,7 +817,6 @@ void ntpServerStop() // Release the NTP server memory if (ntpServer) { - w5500DisableSocketInterrupt(ntpSockIndex); // Disable the receive interrupt ntpServer->stop(); delete ntpServer; ntpServer = nullptr; @@ -793,7 +835,7 @@ void ntpServerStop() // Update the NTP server state void ntpServerUpdate() { - char ntpDiag[512]; // Char array to hold diagnostic messages + char ntpDiag[768]; // Char array to hold diagnostic messages if (present.ethernet_ws5500 == false) return; @@ -850,12 +892,7 @@ void ntpServerUpdate() ntpServerStop(); else { - // Start the NTP server - // TODO - //ntpServer->begin(settings.ethernetNtpPort); - //ntpSockIndex = ntpServer->getSockIndex(); // Get the socket index - //w5500ClearSocketInterrupts(); // Clear all interrupts - //w5500EnableSocketInterrupt(ntpSockIndex); // Enable the RECV interrupt for the desired socket index + ntpServer->begin(settings.ethernetNtpPort); // Start the NTP server online.ethernetNTPServer = true; if (!inMainMenu) reportHeapNow(settings.debugNtp); @@ -872,21 +909,19 @@ void ntpServerUpdate() else { - if (w5500CheckSocketInterrupt(ntpSockIndex)) - w5500ClearSocketInterrupt(ntpSockIndex); // Clear the socket interrupt here - // Check for new NTP requests - if the time has been sync'd bool processed = ntpProcessOneRequest(systemState == STATE_NTPSERVER_SYNC, (const timeval *)ðernetNtpTv, (const timeval *)&gnssSyncTv, ntpDiag, sizeof(ntpDiag)); - if (processed) + + // Print the diagnostics - if enabled + if ((settings.debugNtp || PERIODIC_DISPLAY(PD_NTP_SERVER_DATA)) && (strlen(ntpDiag) > 0) && (!inMainMenu)) { - // Print the diagnostics - if enabled - if ((settings.debugNtp || PERIODIC_DISPLAY(PD_NTP_SERVER_DATA)) && (!inMainMenu)) - { - PERIODIC_CLEAR(PD_NTP_SERVER_DATA); - systemPrint(ntpDiag); - } + PERIODIC_CLEAR(PD_NTP_SERVER_DATA); + systemPrint(ntpDiag); + } + if (processed) + { // Log the NTP request to file - if enabled if (settings.enableNTPFile) { diff --git a/Firmware/RTK_Everywhere/RTK_Everywhere.ino b/Firmware/RTK_Everywhere/RTK_Everywhere.ino index ac08aa92b..91f84746b 100644 --- a/Firmware/RTK_Everywhere/RTK_Everywhere.ino +++ b/Firmware/RTK_Everywhere/RTK_Everywhere.ino @@ -788,8 +788,9 @@ correctionsSource pplCorrectionsSource = CORR_NUM; // Record which source is fee // configureViaEthernet: // Set to true if configureViaEthernet.txt exists in LittleFS. -// Causes setup and loop to skip any code which would cause SPI or interrupts to be initialized. -// This is to allow SparkFun_WebServer_ESP32_W5500 to have _exclusive_ access to WiFi, SPI and Interrupts. +// Previously, the SparkFun_WebServer_ESP32_W5500 needed _exclusive_ access to SPI and Interrupts. +// That's no longer true - thanks to Espressif adding full support for the W5500 within the +// arduino-esp32 core (v3.0.0+). But it's easier to leave the code as it is. bool configureViaEthernet; int floatLockRestarts; @@ -1106,7 +1107,7 @@ void setup() gnssBeginPPS(); // Configure the time pulse output DMW_b("beginInterrupts"); - beginInterrupts(); // Begin the TP and W5500 interrupts + beginInterrupts(); // Begin the TP interrupts DMW_b("beginButtons"); beginButtons(); // Start task for button monitoring. @@ -1242,7 +1243,13 @@ void loop() DMW_c("updateProvisioning"); updateProvisioning(); // Check if we should attempt to connect to PointPerfect to get keys / certs / correction topic etc. - delay(10); // A small delay prevents panic if no other I2C or functions are called + loopDelay(); // A small delay prevents panic if no other I2C or functions are called +} + +void loopDelay() +{ + if (systemState != STATE_NTPSERVER_SYNC) // No delay in NTP mode + delay(10); } // Create or close files as needed (startup or as the user changes settings) diff --git a/Firmware/RTK_Everywhere/States.ino b/Firmware/RTK_Everywhere/States.ino index e0bec2aa1..1609fe6e1 100644 --- a/Firmware/RTK_Everywhere/States.ino +++ b/Firmware/RTK_Everywhere/States.ino @@ -591,7 +591,12 @@ void stateUpdate() break; case (STATE_NTPSERVER_SYNC): { - // Do nothing - display only + if (!rtcSyncd) + { + if (settings.debugNtp) + systemPrintln("NTP Server RTC sync lost"); + changeState(STATE_NTPSERVER_NO_SYNC); + } } break; @@ -612,8 +617,6 @@ void stateUpdate() RTK_MODE(RTK_MODE_ETHERNET_CONFIG); // The code should only be able to enter this state if configureViaEthernet is true. // If configureViaEthernet is not true, we need to restart again. - //(If we continue, startEthernerWebServerESP32W5500 will fail as it won't have exclusive access to SPI and - // ints). if (!configureViaEthernet) { displayConfigViaEthStarting(1500); diff --git a/Firmware/RTK_Everywhere/TcpServer.ino b/Firmware/RTK_Everywhere/TcpServer.ino index d3a9fa75f..6eccd4938 100644 --- a/Firmware/RTK_Everywhere/TcpServer.ino +++ b/Firmware/RTK_Everywhere/TcpServer.ino @@ -93,9 +93,7 @@ int32_t tcpServerClientSendData(int index, uint8_t *data, uint16_t length) 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 %d.%d.%d.%d\r\n", length, tcpServerClientIpAddress[index][0], - tcpServerClientIpAddress[index][1], tcpServerClientIpAddress[index][2], - tcpServerClientIpAddress[index][3]); + systemPrintf("TCP server wrote %d bytes to %s\r\n", length, tcpServerClientIpAddress[index].toString()); } } @@ -106,9 +104,8 @@ int32_t tcpServerClientSendData(int index, uint8_t *data, uint16_t length) if ((settings.debugTcpServer || PERIODIC_DISPLAY(PD_TCP_SERVER_CLIENT_DATA)) && (!inMainMenu)) { PERIODIC_CLEAR(PD_TCP_SERVER_CLIENT_DATA); - systemPrintf("TCP server breaking connection %d with client %d.%d.%d.%d\r\n", index, - tcpServerClientIpAddress[index][0], tcpServerClientIpAddress[index][1], - tcpServerClientIpAddress[index][2], tcpServerClientIpAddress[index][3]); + systemPrintf("TCP server breaking connection %d with client %s\r\n", index, + tcpServerClientIpAddress[index].toString()); } tcpServerClient[index]->stop(); @@ -241,8 +238,7 @@ bool tcpServerStart() tcpServer->begin(); online.tcpServer = true; localIp = networkGetIpAddress(networkGetType()); - systemPrintf("TCP server online, IP address %d.%d.%d.%d:%d\r\n", localIp[0], localIp[1], localIp[2], localIp[3], - settings.tcpServerPort); + systemPrintf("TCP server online, IP address %s:%d\r\n", localIp.toString(), settings.tcpServerPort); return true; } @@ -308,9 +304,8 @@ void tcpServerStopClient(int index) systemPrintf("TCP Server: No data sent over %d seconds\r\n", TCP_SERVER_CLIENT_DATA_TIMEOUT / 1000); if (!connected) systemPrintf("TCP Server: Link to client broken\r\n"); - systemPrintf("TCP server client %d disconnected from %d.%d.%d.%d\r\n", index, - tcpServerClientIpAddress[index][0], tcpServerClientIpAddress[index][1], - tcpServerClientIpAddress[index][2], tcpServerClientIpAddress[index][3]); + systemPrintf("TCP server client %d disconnected from %s\r\n", index, + tcpServerClientIpAddress[index].toString()); } // Shutdown the TCP server client link @@ -431,9 +426,8 @@ void tcpServerUpdate() if (PERIODIC_DISPLAY(PD_TCP_SERVER_DATA) && (!inMainMenu)) { PERIODIC_CLEAR(PD_TCP_SERVER_DATA); - systemPrintf("TCP server client %d connected to %d.%d.%d.%d\r\n", index, - tcpServerClientIpAddress[index][0], tcpServerClientIpAddress[index][1], - tcpServerClientIpAddress[index][2], tcpServerClientIpAddress[index][3]); + systemPrintf("TCP server client %d connected to %s\r\n", index, + tcpServerClientIpAddress[index].toString()); } } @@ -467,9 +461,8 @@ void tcpServerUpdate() if ((settings.debugTcpServer || PERIODIC_DISPLAY(PD_TCP_SERVER_DATA)) && (!inMainMenu)) { PERIODIC_CLEAR(PD_TCP_SERVER_DATA); - systemPrintf("TCP server client %d connected to %d.%d.%d.%d\r\n", index, - tcpServerClientIpAddress[index][0], tcpServerClientIpAddress[index][1], - tcpServerClientIpAddress[index][2], tcpServerClientIpAddress[index][3]); + systemPrintf("TCP server client %d connected to %s\r\n", index, + tcpServerClientIpAddress[index].toString()); } } } diff --git a/Firmware/RTK_Everywhere/W5500.ino b/Firmware/RTK_Everywhere/W5500.ino deleted file mode 100644 index db83daa18..000000000 --- a/Firmware/RTK_Everywhere/W5500.ino +++ /dev/null @@ -1,160 +0,0 @@ -#ifdef COMPILE_ETHERNET - -// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -// Extra code for the W5500 (vs. w5100.h) - -const uint16_t w5500RTR = 0x0019; // Retry Count Register - Common Register Block -const uint16_t w5500SIR = 0x0017; // Socket Interrupt Register - Common Register Block -const uint16_t w5500SIMR = 0x0018; // Socket Interrupt Mask Register - Common Register Block -const uint16_t w5500SnIR = 0x0002; // Socket n Interrupt Register - Socket Register Block -const uint16_t w5500SnIMR = 0x002C; // Socket n Interrupt Mask Register - Socket Register Block - -const uint8_t w5500CommonRegister = 0x00 << 3; // Block Select -const uint8_t w5500Socket0Register = 0x01 << 3; // Block Select -const uint8_t w5500Socket1Register = 0x05 << 3; // Block Select -const uint8_t w5500Socket2Register = 0x09 << 3; // Block Select -const uint8_t w5500Socket3Register = 0x0D << 3; // Block Select -const uint8_t w5500Socket4Register = 0x11 << 3; // Block Select -const uint8_t w5500Socket5Register = 0x15 << 3; // Block Select -const uint8_t w5500Socket6Register = 0x19 << 3; // Block Select -const uint8_t w5500Socket7Register = 0x1D << 3; // Block Select -const uint8_t w5500RegisterWrite = 0x01 << 2; // Read/Write bit -const uint8_t w5500VDM = 0x00 << 0; // Variable Data Length Mode -const uint8_t w5500FDM1 = 0x01 << 0; // Fixed Data Length Mode 1 Byte -const uint8_t w5500FDM2 = 0x02 << 0; // Fixed Data Length Mode 2 Byte -const uint8_t w5500FDM4 = 0x03 << 0; // Fixed Data Length Mode 4 Byte - -const uint8_t w5500SocketRegisters[] = {w5500Socket0Register, w5500Socket1Register, w5500Socket2Register, - w5500Socket3Register, w5500Socket4Register, w5500Socket5Register, - w5500Socket6Register, w5500Socket7Register}; - -const uint8_t w5500SIR_ClearAll = 0xFF; -const uint8_t w5500SIMR_EnableAll = 0xFF; - -const uint8_t w5500SnIR_ClearAll = 0xFF; - -const uint8_t w5500SnIMR_CON = 0x01 << 0; -const uint8_t w5500SnIMR_DISCON = 0x01 << 1; -const uint8_t w5500SnIMR_RECV = 0x01 << 2; -const uint8_t w5500SnIMR_TIMEOUT = 0x01 << 3; -const uint8_t w5500SnIMR_SENDOK = 0x01 << 4; - -void w5500write(SPIClass &spiPort, const int cs, uint16_t address, uint8_t control, uint8_t *data, uint8_t len) -{ - // Apply settings - spiPort.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); - - // Signal communication start - digitalWrite(cs, LOW); - - spiPort.transfer(address >> 8); // Address Phase - spiPort.transfer(address & 0xFF); - - spiPort.transfer(control | w5500RegisterWrite | w5500VDM); // Control Phase - - for (uint8_t i = 0; i < len; i++) - { - spiPort.transfer(*data++); // Data Phase - } - - // End communication - digitalWrite(cs, HIGH); - spiPort.endTransaction(); -} - -void w5500read(SPIClass &spiPort, const int cs, uint16_t address, uint8_t control, uint8_t *data, uint8_t len) -{ - // Apply settings - spiPort.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); - - // Signal communication start - digitalWrite(cs, LOW); - - spiPort.transfer(address >> 8); // Address Phase - spiPort.transfer(address & 0xFF); - - spiPort.transfer(control | w5500VDM); // Control Phase - - for (uint8_t i = 0; i < len; i++) - { - *data++ = spiPort.transfer(0x00); // Data Phase - } - - // End communication - digitalWrite(cs, HIGH); - spiPort.endTransaction(); -} - -void w5500ClearSocketInterrupts() -{ - // Clear all W5500 socket interrupts. - for (uint8_t i = 0; i < (sizeof(w5500SocketRegisters) / sizeof(uint8_t)); i++) - { - w5500write(SPI, pin_Ethernet_CS, w5500SnIR, w5500SocketRegisters[i], (uint8_t *)&w5500SnIR_ClearAll, 1); - } - w5500write(SPI, pin_Ethernet_CS, w5500SIR, w5500CommonRegister, (uint8_t *)&w5500SIR_ClearAll, 1); -} - -void w5500ClearSocketInterrupt(uint8_t sockIndex) -{ - // Clear the interrupt for sockIndex only - - // Sn_IR indicates the status of Socket Interrupt such as establishment, termination, - // receiving data, timeout). When an interrupt occurs and the corresponding bit of - // Sn_IMR is ‘1’, the corresponding bit of Sn_IR becomes ‘1’. - // In order to clear the Sn_IR bit, the host should write the bit to ‘1’. - w5500write(SPI, pin_Ethernet_CS, w5500SnIR, w5500SocketRegisters[sockIndex], (uint8_t *)&w5500SnIR_ClearAll, 1); - - // SIR indicates the interrupt status of Socket. Each bit of SIR be still ‘1’ until Sn_IR is - // cleared by the host. If Sn_IR is not equal to ‘0x00’, the n-th bit of SIR is ‘1’ and INTn - // PIN is asserted until SIR is ‘0x00’. - uint8_t SIR = 1 << sockIndex; - w5500write(SPI, pin_Ethernet_CS, w5500SIR, w5500CommonRegister, &SIR, 1); -} - -bool w5500CheckSocketInterrupt(uint8_t sockIndex) -{ - // Check the interrupt for sockIndex only - uint8_t S_INT = 1 << sockIndex; - uint8_t SIR; - w5500read(SPI, pin_Ethernet_CS, w5500SIR, w5500CommonRegister, &SIR, 1); - return ((S_INT & SIR) > 0); -} - -void w5500EnableSocketInterrupts() -{ - // Enable the RECV interrupt on all eight sockets - for (uint8_t i = 0; i < (sizeof(w5500SocketRegisters) / sizeof(uint8_t)); i++) - { - w5500write(SPI, pin_Ethernet_CS, w5500SnIMR, w5500SocketRegisters[i], (uint8_t *)&w5500SnIMR_RECV, 1); - } - - w5500write(SPI, pin_Ethernet_CS, w5500SIMR, w5500CommonRegister, (uint8_t *)&w5500SIMR_EnableAll, - 1); // Enable the socket interrupt on all eight sockets -} - -void w5500EnableSocketInterrupt(uint8_t sockIndex) -{ - w5500write(SPI, pin_Ethernet_CS, w5500SnIMR, w5500SocketRegisters[sockIndex], (uint8_t *)&w5500SnIMR_RECV, - 1); // Enable the RECV interrupt for sockIndex only - - // Read-Modify-Write - uint8_t SIMR; - w5500read(SPI, pin_Ethernet_CS, w5500SIMR, w5500CommonRegister, &SIMR, 1); - SIMR |= 1 << sockIndex; - w5500write(SPI, pin_Ethernet_CS, w5500SIMR, w5500CommonRegister, &SIMR, 1); // Enable the socket interrupt -} - -void w5500DisableSocketInterrupt(uint8_t sockIndex) -{ - w5500write(SPI, pin_Ethernet_CS, w5500SnIMR, w5500SocketRegisters[sockIndex], (uint8_t *)&w5500SnIMR_RECV, - 1); // Enable the RECV interrupt for sockIndex only - - // Read-Modify-Write - uint8_t SIMR; - w5500read(SPI, pin_Ethernet_CS, w5500SIMR, w5500CommonRegister, &SIMR, 1); - SIMR &= ~(1 << sockIndex); - w5500write(SPI, pin_Ethernet_CS, w5500SIMR, w5500CommonRegister, &SIMR, 1); // Disable the socket interrupt -} - -#endif // COMPILE_ETHERNET diff --git a/Firmware/RTK_Everywhere/menuSystem.ino b/Firmware/RTK_Everywhere/menuSystem.ino index 0bd1c783e..34e69e9b0 100644 --- a/Firmware/RTK_Everywhere/menuSystem.ino +++ b/Firmware/RTK_Everywhere/menuSystem.ino @@ -107,7 +107,7 @@ void menuSystem() systemPrintf("%02X:%02X:%02X:%02X:%02X:%02X\r\n", ethernetMACAddress[0], ethernetMACAddress[1], ethernetMACAddress[2], ethernetMACAddress[3], ethernetMACAddress[4], ethernetMACAddress[5]); systemPrint("Ethernet IP Address: "); - systemPrintln(ETH.localIP()); + systemPrintln(ETH.localIP().toString()); if (!settings.ethernetDHCP) { systemPrint("Ethernet DNS: "); diff --git a/Firmware/RTK_Everywhere/support.ino b/Firmware/RTK_Everywhere/support.ino index 25c0718ed..dccc58237 100644 --- a/Firmware/RTK_Everywhere/support.ino +++ b/Firmware/RTK_Everywhere/support.ino @@ -115,13 +115,7 @@ void systemPrint(int value, uint8_t printType) // Pretty print IP addresses void systemPrint(IPAddress ipaddress) { - systemPrint(ipaddress[0], DEC); - systemPrint("."); - systemPrint(ipaddress[1], DEC); - systemPrint("."); - systemPrint(ipaddress[2], DEC); - systemPrint("."); - systemPrint(ipaddress[3], DEC); + systemPrint(ipaddress.toString()); } void systemPrintln(IPAddress ipaddress) {