diff --git a/Firmware/RTK_Surveyor/Developer.ino b/Firmware/RTK_Surveyor/Developer.ino index 91c8387d4..40298dc73 100644 --- a/Firmware/RTK_Surveyor/Developer.ino +++ b/Firmware/RTK_Surveyor/Developer.ino @@ -92,6 +92,17 @@ void pvtUdpServerUpdate() {} void pvtUdpServerZeroTail() {} void discardPvtUdpServerBytes(RING_BUFFER_OFFSET previousTail, RING_BUFFER_OFFSET newTail) {} +//---------------------------------------- +// PVT server +//---------------------------------------- + +int32_t pvtServerSendData(uint16_t dataHead) {return 0;} +void pvtServerStop() {} +void pvtServerUpdate() {} +void pvtServerZeroTail() {} +void pvtServerValidateTables() {} +void discardPvtServerBytes(RING_BUFFER_OFFSET previousTail, RING_BUFFER_OFFSET newTail) {} + #endif // COMPILE_NETWORK //---------------------------------------- @@ -109,23 +120,12 @@ void stopWebServer() {} bool parseIncomingSettings() {return false;} #endif // COMPILE_AP -#ifndef COMPILE_WIFI - -//---------------------------------------- -// PVT server -//---------------------------------------- - -int32_t pvtServerSendData(uint16_t dataHead) {return 0;} -void pvtServerStop() {} -void pvtServerUpdate() {} -void pvtServerZeroTail() {} -void pvtServerValidateTables() {} -void discardPvtServerBytes(RING_BUFFER_OFFSET previousTail, RING_BUFFER_OFFSET newTail) {} //---------------------------------------- // WiFi //---------------------------------------- +#ifndef COMPILE_WIFI void menuWiFi() {systemPrintln("WiFi not compiled");}; bool wifiConnect(unsigned long timeout) {return false;} IPAddress wifiGetGatewayIpAddress() {return IPAddress((uint32_t)0);} diff --git a/Firmware/RTK_Surveyor/Display.ino b/Firmware/RTK_Surveyor/Display.ino index ded614fb7..bedf5f029 100644 --- a/Firmware/RTK_Surveyor/Display.ino +++ b/Firmware/RTK_Surveyor/Display.ino @@ -193,400 +193,433 @@ void updateDisplay() oled.reset(false); // Incase of previous corruption, force re-alignment of CGRAM. Do not init buffers as it // takes time and causes screen to blink. - oled.erase(); - - icons = 0; - iconsRadio = 0; - switch (systemState) - { - - /* - 111111111122222222223333333333444444444455555555556666 - 0123456789012345678901234567890123456789012345678901234567890123 - .---------------------------------------------------------------- - 0| ******* ** ** ***************** - 1| * * ** ** * * - 2| * ***** * ** ****** * *** *** *** * - 3|* * * * ** * * * *** *** *** *** - 4| * *** * ** * * **** * * * *** *** *** * - 5| * * ** ** ** * * **** * * * *** *** *** * - 6| * ****** * * * * * *** *** *** * - 7| *** **** * * * * * *** *** *** * - 8| * ** * * * * * *** *** *** *** - 9| * * * * * *** *** *** * - 10| * * * * - 11| ****** ***************** - 12| - 13| - 14| - 15| - 16| - 17| - 18| * - 19| * - 20| ******* - 21| * * * *** *** *** - 22| * * * * * * * * * - 23| * * * * * * * * * - 24| * * * ** * * * * * * - 25|******* ******* ** * * * - 26| * * * * * * * * * - 27| * * * * * * * * * - 28| * * * * * * * * * - 29| * * * ** * * ** * * * * - 30| ******* ** *** ** *** *** - 31| * - 32| * - 33| - 34| - 35| - 36| ** ******* - 37| * * *** *** * ** - 38| * * * * * * * * ** - 39| * * * * * * * * * - 40| * * ** * * * * * ***** * - 41| * * ** * * * * - 42| * * * * * * * ***** * - 43| ** * * * * * * * - 44| **** * * * * * * ***** * - 45| ** **** ** * * * * * * - 46| ** ** *** *** * * - 47| ****** ********* - */ - - case (STATE_ROVER_NOT_STARTED): - icons = ICON_CROSS_HAIR // Center left - | ICON_HORIZONTAL_ACCURACY // Center right - | ICON_LOGGING; // Bottom right - displaySivVsOpenShort(); // Bottom left - displayBatteryVsEthernet(); // Top right - iconsRadio = setRadioIcons(); // Top left - break; - case (STATE_ROVER_NO_FIX): - icons = ICON_CROSS_HAIR // Center left - | ICON_HORIZONTAL_ACCURACY // Center right - | ICON_LOGGING; // Bottom right - displaySivVsOpenShort(); // Bottom left - displayBatteryVsEthernet(); // Top right - iconsRadio = setRadioIcons(); // Top left - break; - case (STATE_ROVER_FIX): - icons = ICON_CROSS_HAIR // Center left - | ICON_HORIZONTAL_ACCURACY // Center right - | ICON_LOGGING; // Bottom right - displaySivVsOpenShort(); // Bottom left - displayBatteryVsEthernet(); // Top right - iconsRadio = setRadioIcons(); // Top left - break; - case (STATE_ROVER_RTK_FLOAT): - blinking_icons ^= ICON_CROSS_HAIR_DUAL; - icons = (blinking_icons & ICON_CROSS_HAIR_DUAL) // Center left - | ICON_HORIZONTAL_ACCURACY // Center right - | ICON_LOGGING; // Bottom right - displaySivVsOpenShort(); // Bottom left - displayBatteryVsEthernet(); // Top right - iconsRadio = setRadioIcons(); // Top left - break; - case (STATE_ROVER_RTK_FIX): - icons = ICON_CROSS_HAIR_DUAL // Center left - | ICON_HORIZONTAL_ACCURACY // Center right - | ICON_LOGGING; // Bottom right - displaySivVsOpenShort(); // Bottom left - displayBatteryVsEthernet(); // Top right - iconsRadio = setRadioIcons(); // Top left - break; - - case (STATE_BASE_NOT_STARTED): - // Do nothing. Static display shown during state change. - break; - - // Start of base / survey in / NTRIP mode - // Screen is displayed while we are waiting for horz accuracy to drop to appropriate level - // Blink crosshair icon until we have we have horz accuracy < user defined level - case (STATE_BASE_TEMP_SETTLE): - blinking_icons ^= ICON_CROSS_HAIR; - icons = (blinking_icons & ICON_CROSS_HAIR) // Center left - | ICON_HORIZONTAL_ACCURACY // Center right - | ICON_LOGGING; // Bottom right - displaySivVsOpenShort(); // Bottom left - displayBatteryVsEthernet(); // Top right - iconsRadio = setRadioIcons(); // Top left - break; - case (STATE_BASE_TEMP_SURVEY_STARTED): - icons = ICON_LOGGING; // Bottom right - displayBatteryVsEthernet(); // Top right - iconsRadio = setRadioIcons(); // Top left - paintBaseTempSurveyStarted(); - break; - case (STATE_BASE_TEMP_TRANSMITTING): - icons = ICON_LOGGING; // Bottom right - displayBatteryVsEthernet(); // Top right - iconsRadio = setRadioIcons(); // Top left - paintRTCM(); - break; - case (STATE_BASE_FIXED_NOT_STARTED): - icons = 0; // Top right - displayBatteryVsEthernet(); // Top right - iconsRadio = setRadioIcons(); // Top left - break; - case (STATE_BASE_FIXED_TRANSMITTING): - icons = ICON_LOGGING; // Bottom right - displayBatteryVsEthernet(); // Top right - iconsRadio = setRadioIcons(); // Top left - paintRTCM(); - break; - - case (STATE_NTPSERVER_NOT_STARTED): - case (STATE_NTPSERVER_NO_SYNC): - blinking_icons ^= ICON_CLOCK; - icons = (blinking_icons & ICON_CLOCK) // Center left - | ICON_CLOCK_ACCURACY; // Center right - displaySivVsOpenShort(); // Bottom left - if (online.ethernetStatus == ETH_CONNECTED) - blinking_icons |= ICON_ETHERNET; // Don't blink if link is up +#if COMPILE_NETWORK + // If enabled, display server info every displayServerRepeatInterval millis + // Display "PVT Server" for displayServerSplashTime millis + // Display the IP for (displayServerTotalTime - displayServerSplashTime) millis + // These won't be exact, due to the >= 500ms display updates + const unsigned long displayServerRepeatInterval = 16384; + const unsigned long displayServerTotalTime = 4096; + const unsigned long displayServerSplashTime = 1024; + bool displayPvtServerIP = settings.displayServerIP + && ((millis() % displayServerRepeatInterval) < displayServerTotalTime) + && pvtServerRunning(); + bool displayUdpServerIP = settings.displayServerIP + && ((millis() % displayServerRepeatInterval) < displayServerTotalTime) + && pvtUdpServerRunning(); + + if (displayPvtServerIP) + { + if ((millis() % displayServerRepeatInterval) < displayServerSplashTime) + paintPvtServer(); else - blinking_icons ^= ICON_ETHERNET; - icons |= (blinking_icons & ICON_ETHERNET); // Top Right - iconsRadio = ICON_IP_ADDRESS; // Top left - break; - - case (STATE_NTPSERVER_SYNC): - icons = ICON_CLOCK // Center left - | ICON_CLOCK_ACCURACY // Center right - | ICON_LOGGING_NTP; // Bottom right - displaySivVsOpenShort(); // Bottom left - if (online.ethernetStatus == ETH_CONNECTED) - blinking_icons |= ICON_ETHERNET; // Don't blink if link is up - else - blinking_icons ^= ICON_ETHERNET; - icons |= (blinking_icons & ICON_ETHERNET); // Top Right - iconsRadio = ICON_IP_ADDRESS; // Top left - break; - - case (STATE_CONFIG_VIA_ETH_NOT_STARTED): - break; - case (STATE_CONFIG_VIA_ETH_STARTED): - break; - case (STATE_CONFIG_VIA_ETH): - displayConfigViaEthernet(); - break; - case (STATE_CONFIG_VIA_ETH_RESTART_BASE): - break; - - case (STATE_BUBBLE_LEVEL): - paintBubbleLevel(); - break; - case (STATE_PROFILE): - paintProfile(displayProfile); - break; - case (STATE_MARK_EVENT): - // Do nothing. Static display shown during state change. - break; - case (STATE_DISPLAY_SETUP): - paintDisplaySetup(); - break; - case (STATE_WIFI_CONFIG_NOT_STARTED): - displayWiFiConfigNotStarted(); // Display 'WiFi Config' - break; - case (STATE_WIFI_CONFIG): - iconsRadio = setWiFiIcon(); // Blink WiFi in center - displayWiFiConfig(); // Display SSID and IP - break; - case (STATE_TEST): - paintSystemTest(); - break; - case (STATE_TESTING): - paintSystemTest(); - break; - - case (STATE_KEYS_STARTED): - paintRTCWait(); - break; - case (STATE_KEYS_NEEDED): - // Do nothing. Quick, fall through state. - break; - case (STATE_KEYS_WIFI_STARTED): - iconsRadio = setWiFiIcon(); // Blink WiFi in center - paintGettingKeys(); - break; - case (STATE_KEYS_WIFI_CONNECTED): - iconsRadio = setWiFiIcon(); // Blink WiFi in center - paintGettingKeys(); - break; - case (STATE_KEYS_WIFI_TIMEOUT): - // Do nothing. Quick, fall through state. - break; - case (STATE_KEYS_EXPIRED): - // Do nothing. Quick, fall through state. - break; - case (STATE_KEYS_DAYS_REMAINING): - // Do nothing. Quick, fall through state. - break; - case (STATE_KEYS_LBAND_CONFIGURE): - paintLBandConfigure(); - break; - case (STATE_KEYS_LBAND_ENCRYPTED): - // Do nothing. Quick, fall through state. - break; - case (STATE_KEYS_PROVISION_WIFI_STARTED): - iconsRadio = setWiFiIcon(); // Blink WiFi in center - paintGettingKeys(); - break; - case (STATE_KEYS_PROVISION_WIFI_CONNECTED): - iconsRadio = setWiFiIcon(); // Blink WiFi in center - paintGettingKeys(); - break; - - case (STATE_ESPNOW_PAIRING_NOT_STARTED): - paintEspNowPairing(); - break; - case (STATE_ESPNOW_PAIRING): - paintEspNowPairing(); - break; - - case (STATE_SHUTDOWN): - displayShutdown(); - break; - default: - systemPrintf("Unknown display: %d\r\n", systemState); - displayError("Display"); - break; + paintPvtServerIP(); } - - // Top left corner - Radio icon indicators take three spots (left/center/right) - // Allowed icon combinations: - // Bluetooth + Rover/Base - // WiFi + Bluetooth + Rover/Base - // ESP-Now + Bluetooth + Rover/Base - // ESP-Now + Bluetooth + WiFi - // See setRadioIcons() for the icon selection logic - - // Left spot - if (iconsRadio & ICON_MAC_ADDRESS) + else if (displayUdpServerIP) { - char macAddress[5]; - const uint8_t *rtkMacAddress = getMacAddress(); - - // Print four characters of MAC - snprintf(macAddress, sizeof(macAddress), "%02X%02X", rtkMacAddress[4], rtkMacAddress[5]); - oled.setFont(QW_FONT_5X7); // Set font to smallest - oled.setCursor(0, 3); - oled.print(macAddress); + if ((millis() % displayServerRepeatInterval) < displayServerSplashTime) + paintUdpServer(); + else + paintUdpServerIP(); } - else if (iconsRadio & ICON_BT_SYMBOL_LEFT) - displayBitmap(1, 0, BT_Symbol_Width, BT_Symbol_Height, BT_Symbol); - else if (iconsRadio & ICON_WIFI_SYMBOL_0_LEFT) - displayBitmap(0, 0, WiFi_Symbol_Width, WiFi_Symbol_Height, WiFi_Symbol_0); - else if (iconsRadio & ICON_WIFI_SYMBOL_1_LEFT) - displayBitmap(0, 0, WiFi_Symbol_Width, WiFi_Symbol_Height, WiFi_Symbol_1); - else if (iconsRadio & ICON_WIFI_SYMBOL_2_LEFT) - displayBitmap(0, 0, WiFi_Symbol_Width, WiFi_Symbol_Height, WiFi_Symbol_2); - else if (iconsRadio & ICON_WIFI_SYMBOL_3_LEFT) - displayBitmap(0, 0, WiFi_Symbol_Width, WiFi_Symbol_Height, WiFi_Symbol_3); - else if (iconsRadio & ICON_ESPNOW_SYMBOL_0_LEFT) - displayBitmap(0, 0, ESPNOW_Symbol_Width, ESPNOW_Symbol_Height, ESPNOW_Symbol_0); - else if (iconsRadio & ICON_ESPNOW_SYMBOL_1_LEFT) - displayBitmap(0, 0, ESPNOW_Symbol_Width, ESPNOW_Symbol_Height, ESPNOW_Symbol_1); - else if (iconsRadio & ICON_ESPNOW_SYMBOL_2_LEFT) - displayBitmap(0, 0, ESPNOW_Symbol_Width, ESPNOW_Symbol_Height, ESPNOW_Symbol_2); - else if (iconsRadio & ICON_ESPNOW_SYMBOL_3_LEFT) - displayBitmap(0, 0, ESPNOW_Symbol_Width, ESPNOW_Symbol_Height, ESPNOW_Symbol_3); - else if (iconsRadio & ICON_DOWN_ARROW_LEFT) - displayBitmap(1, 0, DownloadArrow_Width, DownloadArrow_Height, DownloadArrow); - else if (iconsRadio & ICON_UP_ARROW_LEFT) - displayBitmap(1, 0, UploadArrow_Width, UploadArrow_Height, UploadArrow); - else if (iconsRadio & ICON_BLANK_LEFT) + else +#endif // COMPILE_NETWORK { - ; - } + oled.erase(); - // Center radio spots - if (iconsRadio & ICON_BT_SYMBOL_CENTER) - { - // Moved to center to give space for ESP NOW icon on far left - displayBitmap(16, 0, BT_Symbol_Width, BT_Symbol_Height, BT_Symbol); - } - else if (iconsRadio & ICON_MAC_ADDRESS_2DIGIT) - { - char macAddress[5]; - const uint8_t *rtkMacAddress = getMacAddress(); - - // Print only last two digits of MAC - snprintf(macAddress, sizeof(macAddress), "%02X", rtkMacAddress[5]); - oled.setFont(QW_FONT_5X7); // Set font to smallest - oled.setCursor(14, 3); - oled.print(macAddress); - } - else if (iconsRadio & ICON_DOWN_ARROW_CENTER) - displayBitmap(16, 0, DownloadArrow_Width, DownloadArrow_Height, DownloadArrow); - else if (iconsRadio & ICON_UP_ARROW_CENTER) - displayBitmap(16, 0, UploadArrow_Width, UploadArrow_Height, UploadArrow); - - // Radio third spot - if (iconsRadio & ICON_WIFI_SYMBOL_0_RIGHT) - displayBitmap(28, 0, WiFi_Symbol_Width, WiFi_Symbol_Height, WiFi_Symbol_0); - else if (iconsRadio & ICON_WIFI_SYMBOL_1_RIGHT) - displayBitmap(28, 0, WiFi_Symbol_Width, WiFi_Symbol_Height, WiFi_Symbol_1); - else if (iconsRadio & ICON_WIFI_SYMBOL_2_RIGHT) - displayBitmap(28, 0, WiFi_Symbol_Width, WiFi_Symbol_Height, WiFi_Symbol_2); - else if (iconsRadio & ICON_WIFI_SYMBOL_3_RIGHT) - displayBitmap(28, 0, WiFi_Symbol_Width, WiFi_Symbol_Height, WiFi_Symbol_3); - else if ((iconsRadio & ICON_DYNAMIC_MODEL) && (online.gnss == true)) - paintDynamicModel(); - else if (iconsRadio & ICON_BASE_TEMPORARY) - displayBitmap(28, 0, BaseTemporary_Width, BaseTemporary_Height, BaseTemporary); - else if (iconsRadio & ICON_BASE_FIXED) - displayBitmap(28, 0, BaseFixed_Width, BaseFixed_Height, BaseFixed); // true - blend with other pixels - else if (iconsRadio & ICON_DOWN_ARROW_RIGHT) - displayBitmap(31, 0, DownloadArrow_Width, DownloadArrow_Height, DownloadArrow); - else if (iconsRadio & ICON_UP_ARROW_RIGHT) - displayBitmap(31, 0, UploadArrow_Width, UploadArrow_Height, UploadArrow); - else if (iconsRadio & ICON_BLANK_RIGHT) - { - ; - } + icons = 0; + iconsRadio = 0; + switch (systemState) + { - // Left + center spot - if (iconsRadio & ICON_IP_ADDRESS) - paintIPAddress(); - - // Top right corner - if (icons & ICON_BATTERY) - paintBatteryLevel(); - else if (icons & ICON_ETHERNET) - displayBitmap(45, 0, Ethernet_Icon_Width, Ethernet_Icon_Height, Ethernet_Icon); - - // Center left - if (icons & ICON_CROSS_HAIR) - displayBitmap(0, 18, CrossHair_Width, CrossHair_Height, CrossHair); - else if (icons & ICON_CROSS_HAIR_DUAL) - displayBitmap(0, 18, CrossHairDual_Width, CrossHairDual_Height, CrossHairDual); - else if (icons & ICON_CLOCK) - paintClock(); - - // Center right - if (icons & ICON_HORIZONTAL_ACCURACY) - paintHorizontalAccuracy(); - else if (icons & ICON_CLOCK_ACCURACY) - paintClockAccuracy(); - - // Bottom left corner - if (icons & ICON_SIV_ANTENNA) - displayBitmap(2, 35, SIV_Antenna_Width, SIV_Antenna_Height, SIV_Antenna); - else if (icons & ICON_SIV_ANTENNA_LBAND) - displayBitmap(2, 35, SIV_Antenna_LBand_Width, SIV_Antenna_LBand_Height, SIV_Antenna_LBand); - else if (icons & ICON_ANTENNA_SHORT) - displayBitmap(2, 35, Antenna_Short_Width, Antenna_Short_Height, Antenna_Short); - else if (icons & ICON_ANTENNA_OPEN) - displayBitmap(2, 35, Antenna_Open_Width, Antenna_Open_Height, Antenna_Open); + /* + 111111111122222222223333333333444444444455555555556666 + 0123456789012345678901234567890123456789012345678901234567890123 + .---------------------------------------------------------------- + 0| ******* ** ** ***************** + 1| * * ** ** * * + 2| * ***** * ** ****** * *** *** *** * + 3|* * * * ** * * * *** *** *** *** + 4| * *** * ** * * **** * * * *** *** *** * + 5| * * ** ** ** * * **** * * * *** *** *** * + 6| * ****** * * * * * *** *** *** * + 7| *** **** * * * * * *** *** *** * + 8| * ** * * * * * *** *** *** *** + 9| * * * * * *** *** *** * + 10| * * * * + 11| ****** ***************** + 12| + 13| + 14| + 15| + 16| + 17| + 18| * + 19| * + 20| ******* + 21| * * * *** *** *** + 22| * * * * * * * * * + 23| * * * * * * * * * + 24| * * * ** * * * * * * + 25|******* ******* ** * * * + 26| * * * * * * * * * + 27| * * * * * * * * * + 28| * * * * * * * * * + 29| * * * ** * * ** * * * * + 30| ******* ** *** ** *** *** + 31| * + 32| * + 33| + 34| + 35| + 36| ** ******* + 37| * * *** *** * ** + 38| * * * * * * * * ** + 39| * * * * * * * * * + 40| * * ** * * * * * ***** * + 41| * * ** * * * * + 42| * * * * * * * ***** * + 43| ** * * * * * * * + 44| **** * * * * * * ***** * + 45| ** **** ** * * * * * * + 46| ** ** *** *** * * + 47| ****** ********* + */ + + case (STATE_ROVER_NOT_STARTED): + icons = ICON_CROSS_HAIR // Center left + | ICON_HORIZONTAL_ACCURACY // Center right + | ICON_LOGGING; // Bottom right + displaySivVsOpenShort(); // Bottom left + displayBatteryVsEthernet(); // Top right + iconsRadio = setRadioIcons(); // Top left + break; + case (STATE_ROVER_NO_FIX): + icons = ICON_CROSS_HAIR // Center left + | ICON_HORIZONTAL_ACCURACY // Center right + | ICON_LOGGING; // Bottom right + displaySivVsOpenShort(); // Bottom left + displayBatteryVsEthernet(); // Top right + iconsRadio = setRadioIcons(); // Top left + break; + case (STATE_ROVER_FIX): + icons = ICON_CROSS_HAIR // Center left + | ICON_HORIZONTAL_ACCURACY // Center right + | ICON_LOGGING; // Bottom right + displaySivVsOpenShort(); // Bottom left + displayBatteryVsEthernet(); // Top right + iconsRadio = setRadioIcons(); // Top left + break; + case (STATE_ROVER_RTK_FLOAT): + blinking_icons ^= ICON_CROSS_HAIR_DUAL; + icons = (blinking_icons & ICON_CROSS_HAIR_DUAL) // Center left + | ICON_HORIZONTAL_ACCURACY // Center right + | ICON_LOGGING; // Bottom right + displaySivVsOpenShort(); // Bottom left + displayBatteryVsEthernet(); // Top right + iconsRadio = setRadioIcons(); // Top left + break; + case (STATE_ROVER_RTK_FIX): + icons = ICON_CROSS_HAIR_DUAL // Center left + | ICON_HORIZONTAL_ACCURACY // Center right + | ICON_LOGGING; // Bottom right + displaySivVsOpenShort(); // Bottom left + displayBatteryVsEthernet(); // Top right + iconsRadio = setRadioIcons(); // Top left + break; + + case (STATE_BASE_NOT_STARTED): + // Do nothing. Static display shown during state change. + break; + + // Start of base / survey in / NTRIP mode + // Screen is displayed while we are waiting for horz accuracy to drop to appropriate level + // Blink crosshair icon until we have we have horz accuracy < user defined level + case (STATE_BASE_TEMP_SETTLE): + blinking_icons ^= ICON_CROSS_HAIR; + icons = (blinking_icons & ICON_CROSS_HAIR) // Center left + | ICON_HORIZONTAL_ACCURACY // Center right + | ICON_LOGGING; // Bottom right + displaySivVsOpenShort(); // Bottom left + displayBatteryVsEthernet(); // Top right + iconsRadio = setRadioIcons(); // Top left + break; + case (STATE_BASE_TEMP_SURVEY_STARTED): + icons = ICON_LOGGING; // Bottom right + displayBatteryVsEthernet(); // Top right + iconsRadio = setRadioIcons(); // Top left + paintBaseTempSurveyStarted(); + break; + case (STATE_BASE_TEMP_TRANSMITTING): + icons = ICON_LOGGING; // Bottom right + displayBatteryVsEthernet(); // Top right + iconsRadio = setRadioIcons(); // Top left + paintRTCM(); + break; + case (STATE_BASE_FIXED_NOT_STARTED): + icons = 0; // Top right + displayBatteryVsEthernet(); // Top right + iconsRadio = setRadioIcons(); // Top left + break; + case (STATE_BASE_FIXED_TRANSMITTING): + icons = ICON_LOGGING; // Bottom right + displayBatteryVsEthernet(); // Top right + iconsRadio = setRadioIcons(); // Top left + paintRTCM(); + break; + + case (STATE_NTPSERVER_NOT_STARTED): + case (STATE_NTPSERVER_NO_SYNC): + blinking_icons ^= ICON_CLOCK; + icons = (blinking_icons & ICON_CLOCK) // Center left + | ICON_CLOCK_ACCURACY; // Center right + displaySivVsOpenShort(); // Bottom left + if (online.ethernetStatus == ETH_CONNECTED) + blinking_icons |= ICON_ETHERNET; // Don't blink if link is up + else + blinking_icons ^= ICON_ETHERNET; + icons |= (blinking_icons & ICON_ETHERNET); // Top Right + iconsRadio = ICON_IP_ADDRESS; // Top left + break; + + case (STATE_NTPSERVER_SYNC): + icons = ICON_CLOCK // Center left + | ICON_CLOCK_ACCURACY // Center right + | ICON_LOGGING_NTP; // Bottom right + displaySivVsOpenShort(); // Bottom left + if (online.ethernetStatus == ETH_CONNECTED) + blinking_icons |= ICON_ETHERNET; // Don't blink if link is up + else + blinking_icons ^= ICON_ETHERNET; + icons |= (blinking_icons & ICON_ETHERNET); // Top Right + iconsRadio = ICON_IP_ADDRESS; // Top left + break; + + case (STATE_CONFIG_VIA_ETH_NOT_STARTED): + break; + case (STATE_CONFIG_VIA_ETH_STARTED): + break; + case (STATE_CONFIG_VIA_ETH): + displayConfigViaEthernet(); + break; + case (STATE_CONFIG_VIA_ETH_RESTART_BASE): + break; + + case (STATE_BUBBLE_LEVEL): + paintBubbleLevel(); + break; + case (STATE_PROFILE): + paintProfile(displayProfile); + break; + case (STATE_MARK_EVENT): + // Do nothing. Static display shown during state change. + break; + case (STATE_DISPLAY_SETUP): + paintDisplaySetup(); + break; + case (STATE_WIFI_CONFIG_NOT_STARTED): + displayWiFiConfigNotStarted(); // Display 'WiFi Config' + break; + case (STATE_WIFI_CONFIG): + iconsRadio = setWiFiIcon(); // Blink WiFi in center + displayWiFiConfig(); // Display SSID and IP + break; + case (STATE_TEST): + paintSystemTest(); + break; + case (STATE_TESTING): + paintSystemTest(); + break; + + case (STATE_KEYS_STARTED): + paintRTCWait(); + break; + case (STATE_KEYS_NEEDED): + // Do nothing. Quick, fall through state. + break; + case (STATE_KEYS_WIFI_STARTED): + iconsRadio = setWiFiIcon(); // Blink WiFi in center + paintGettingKeys(); + break; + case (STATE_KEYS_WIFI_CONNECTED): + iconsRadio = setWiFiIcon(); // Blink WiFi in center + paintGettingKeys(); + break; + case (STATE_KEYS_WIFI_TIMEOUT): + // Do nothing. Quick, fall through state. + break; + case (STATE_KEYS_EXPIRED): + // Do nothing. Quick, fall through state. + break; + case (STATE_KEYS_DAYS_REMAINING): + // Do nothing. Quick, fall through state. + break; + case (STATE_KEYS_LBAND_CONFIGURE): + paintLBandConfigure(); + break; + case (STATE_KEYS_LBAND_ENCRYPTED): + // Do nothing. Quick, fall through state. + break; + case (STATE_KEYS_PROVISION_WIFI_STARTED): + iconsRadio = setWiFiIcon(); // Blink WiFi in center + paintGettingKeys(); + break; + case (STATE_KEYS_PROVISION_WIFI_CONNECTED): + iconsRadio = setWiFiIcon(); // Blink WiFi in center + paintGettingKeys(); + break; + + case (STATE_ESPNOW_PAIRING_NOT_STARTED): + paintEspNowPairing(); + break; + case (STATE_ESPNOW_PAIRING): + paintEspNowPairing(); + break; + + case (STATE_SHUTDOWN): + displayShutdown(); + break; + default: + systemPrintf("Unknown display: %d\r\n", systemState); + displayError("Display"); + break; + } - // Bottom right corner - if (icons & ICON_LOGGING) - paintLogging(); - else if (icons & ICON_LOGGING_NTP) - paintLoggingNTP(true); // NTP, no pulse + // Top left corner - Radio icon indicators take three spots (left/center/right) + // Allowed icon combinations: + // Bluetooth + Rover/Base + // WiFi + Bluetooth + Rover/Base + // ESP-Now + Bluetooth + Rover/Base + // ESP-Now + Bluetooth + WiFi + // See setRadioIcons() for the icon selection logic - oled.display(); // Push internal buffer to display + // Left spot + if (iconsRadio & ICON_MAC_ADDRESS) + { + char macAddress[5]; + const uint8_t *rtkMacAddress = getMacAddress(); + + // Print four characters of MAC + snprintf(macAddress, sizeof(macAddress), "%02X%02X", rtkMacAddress[4], rtkMacAddress[5]); + oled.setFont(QW_FONT_5X7); // Set font to smallest + oled.setCursor(0, 3); + oled.print(macAddress); + } + else if (iconsRadio & ICON_BT_SYMBOL_LEFT) + displayBitmap(1, 0, BT_Symbol_Width, BT_Symbol_Height, BT_Symbol); + else if (iconsRadio & ICON_WIFI_SYMBOL_0_LEFT) + displayBitmap(0, 0, WiFi_Symbol_Width, WiFi_Symbol_Height, WiFi_Symbol_0); + else if (iconsRadio & ICON_WIFI_SYMBOL_1_LEFT) + displayBitmap(0, 0, WiFi_Symbol_Width, WiFi_Symbol_Height, WiFi_Symbol_1); + else if (iconsRadio & ICON_WIFI_SYMBOL_2_LEFT) + displayBitmap(0, 0, WiFi_Symbol_Width, WiFi_Symbol_Height, WiFi_Symbol_2); + else if (iconsRadio & ICON_WIFI_SYMBOL_3_LEFT) + displayBitmap(0, 0, WiFi_Symbol_Width, WiFi_Symbol_Height, WiFi_Symbol_3); + else if (iconsRadio & ICON_ESPNOW_SYMBOL_0_LEFT) + displayBitmap(0, 0, ESPNOW_Symbol_Width, ESPNOW_Symbol_Height, ESPNOW_Symbol_0); + else if (iconsRadio & ICON_ESPNOW_SYMBOL_1_LEFT) + displayBitmap(0, 0, ESPNOW_Symbol_Width, ESPNOW_Symbol_Height, ESPNOW_Symbol_1); + else if (iconsRadio & ICON_ESPNOW_SYMBOL_2_LEFT) + displayBitmap(0, 0, ESPNOW_Symbol_Width, ESPNOW_Symbol_Height, ESPNOW_Symbol_2); + else if (iconsRadio & ICON_ESPNOW_SYMBOL_3_LEFT) + displayBitmap(0, 0, ESPNOW_Symbol_Width, ESPNOW_Symbol_Height, ESPNOW_Symbol_3); + else if (iconsRadio & ICON_DOWN_ARROW_LEFT) + displayBitmap(1, 0, DownloadArrow_Width, DownloadArrow_Height, DownloadArrow); + else if (iconsRadio & ICON_UP_ARROW_LEFT) + displayBitmap(1, 0, UploadArrow_Width, UploadArrow_Height, UploadArrow); + else if (iconsRadio & ICON_BLANK_LEFT) + { + ; + } + + // Center radio spots + if (iconsRadio & ICON_BT_SYMBOL_CENTER) + { + // Moved to center to give space for ESP NOW icon on far left + displayBitmap(16, 0, BT_Symbol_Width, BT_Symbol_Height, BT_Symbol); + } + else if (iconsRadio & ICON_MAC_ADDRESS_2DIGIT) + { + char macAddress[5]; + const uint8_t *rtkMacAddress = getMacAddress(); + + // Print only last two digits of MAC + snprintf(macAddress, sizeof(macAddress), "%02X", rtkMacAddress[5]); + oled.setFont(QW_FONT_5X7); // Set font to smallest + oled.setCursor(14, 3); + oled.print(macAddress); + } + else if (iconsRadio & ICON_DOWN_ARROW_CENTER) + displayBitmap(16, 0, DownloadArrow_Width, DownloadArrow_Height, DownloadArrow); + else if (iconsRadio & ICON_UP_ARROW_CENTER) + displayBitmap(16, 0, UploadArrow_Width, UploadArrow_Height, UploadArrow); + + // Radio third spot + if (iconsRadio & ICON_WIFI_SYMBOL_0_RIGHT) + displayBitmap(28, 0, WiFi_Symbol_Width, WiFi_Symbol_Height, WiFi_Symbol_0); + else if (iconsRadio & ICON_WIFI_SYMBOL_1_RIGHT) + displayBitmap(28, 0, WiFi_Symbol_Width, WiFi_Symbol_Height, WiFi_Symbol_1); + else if (iconsRadio & ICON_WIFI_SYMBOL_2_RIGHT) + displayBitmap(28, 0, WiFi_Symbol_Width, WiFi_Symbol_Height, WiFi_Symbol_2); + else if (iconsRadio & ICON_WIFI_SYMBOL_3_RIGHT) + displayBitmap(28, 0, WiFi_Symbol_Width, WiFi_Symbol_Height, WiFi_Symbol_3); + else if ((iconsRadio & ICON_DYNAMIC_MODEL) && (online.gnss == true)) + paintDynamicModel(); + else if (iconsRadio & ICON_BASE_TEMPORARY) + displayBitmap(28, 0, BaseTemporary_Width, BaseTemporary_Height, BaseTemporary); + else if (iconsRadio & ICON_BASE_FIXED) + displayBitmap(28, 0, BaseFixed_Width, BaseFixed_Height, BaseFixed); // true - blend with other pixels + else if (iconsRadio & ICON_DOWN_ARROW_RIGHT) + displayBitmap(31, 0, DownloadArrow_Width, DownloadArrow_Height, DownloadArrow); + else if (iconsRadio & ICON_UP_ARROW_RIGHT) + displayBitmap(31, 0, UploadArrow_Width, UploadArrow_Height, UploadArrow); + else if (iconsRadio & ICON_BLANK_RIGHT) + { + ; + } + + // Left + center spot + if (iconsRadio & ICON_IP_ADDRESS) + paintIPAddress(); + + // Top right corner + if (icons & ICON_BATTERY) + paintBatteryLevel(); + else if (icons & ICON_ETHERNET) + displayBitmap(45, 0, Ethernet_Icon_Width, Ethernet_Icon_Height, Ethernet_Icon); + + // Center left + if (icons & ICON_CROSS_HAIR) + displayBitmap(0, 18, CrossHair_Width, CrossHair_Height, CrossHair); + else if (icons & ICON_CROSS_HAIR_DUAL) + displayBitmap(0, 18, CrossHairDual_Width, CrossHairDual_Height, CrossHairDual); + else if (icons & ICON_CLOCK) + paintClock(); + + // Center right + if (icons & ICON_HORIZONTAL_ACCURACY) + paintHorizontalAccuracy(); + else if (icons & ICON_CLOCK_ACCURACY) + paintClockAccuracy(); + + // Bottom left corner + if (icons & ICON_SIV_ANTENNA) + displayBitmap(2, 35, SIV_Antenna_Width, SIV_Antenna_Height, SIV_Antenna); + else if (icons & ICON_SIV_ANTENNA_LBAND) + displayBitmap(2, 35, SIV_Antenna_LBand_Width, SIV_Antenna_LBand_Height, SIV_Antenna_LBand); + else if (icons & ICON_ANTENNA_SHORT) + displayBitmap(2, 35, Antenna_Short_Width, Antenna_Short_Height, Antenna_Short); + else if (icons & ICON_ANTENNA_OPEN) + displayBitmap(2, 35, Antenna_Open_Width, Antenna_Open_Height, Antenna_Open); + + // Bottom right corner + if (icons & ICON_LOGGING) + paintLogging(); + else if (icons & ICON_LOGGING_NTP) + paintLoggingNTP(true); // NTP, no pulse + + oled.display(); // Push internal buffer to display + } } } // End display online } @@ -2942,13 +2975,23 @@ void printTextCenter(const char *text, uint8_t yPos, QwiicFont &fontType, uint8_ } } -// Given a message (one or two words) display centered +// Given a message (one, two or three words) display centered void displayMessage(const char *message, uint16_t displayTime) +{ + displayMessageFont(message, displayTime, false); // Large font +} +void displayMessageSmall(const char *message, uint16_t displayTime) +{ + displayMessageFont(message, displayTime, true); // Small font +} +void displayMessageFont(const char *message, uint16_t displayTime, bool smallFont) { if (online.display == true) { char temp[21]; - uint8_t fontHeight = 15; // Assume fontsize 1 + uint8_t fontHeight = 16; // Assume fontsize 1 + if (smallFont) + fontHeight = 10; // Count words based on spaces uint8_t wordCount = 0; @@ -2961,7 +3004,9 @@ void displayMessage(const char *message, uint16_t displayTime) } uint8_t yPos = (oled.getHeight() / 2) - (fontHeight / 2); - if (wordCount == 2) + if (wordCount >= 2) + yPos -= (fontHeight / 2); + if (wordCount >= 3) yPos -= (fontHeight / 2); oled.erase(); @@ -2972,7 +3017,10 @@ void displayMessage(const char *message, uint16_t displayTime) token = strtok(temp, " "); while (token != nullptr) { - printTextCenter(token, yPos, QW_FONT_8X16, 1, false); // text, y, font type, kerning, inverted + if (smallFont) + printTextCenter(token, yPos, QW_FONT_5X7, 1, false); // text, y, font type, kerning, inverted + else + printTextCenter(token, yPos, QW_FONT_8X16, 1, false); // text, y, font type, kerning, inverted token = strtok(nullptr, " "); yPos += fontHeight; } @@ -3412,3 +3460,14 @@ const uint8_t *getMacAddress() #endif // COMPILE_ETHERNET return zero; } + +void paintPvtServer() +{ + displayMessage("PVT Server", 0); +} + +void paintUdpServer() +{ + displayMessage("UDP Server", 0); +} + diff --git a/Firmware/RTK_Surveyor/Network.ino b/Firmware/RTK_Surveyor/Network.ino index 5211b6919..aa45fdfaf 100644 --- a/Firmware/RTK_Surveyor/Network.ino +++ b/Firmware/RTK_Surveyor/Network.ino @@ -236,6 +236,9 @@ void menuNetwork() if (settings.enablePvtUdpServer) systemPrintf("7) PVT UDP Server Port: %ld\r\n", settings.pvtUdpServerPort); + if ((settings.enablePvtServer) || (settings.enablePvtUdpServer)) + systemPrintf("8) Display server IP address: %s\r\n", settings.displayServerIP ? "Enabled" : "Disabled"); + if (HAS_ETHERNET) { //------------------------------ @@ -348,7 +351,10 @@ void menuNetwork() } } - //------------------------------ + else if (incoming == 8 && ((settings.enablePvtServer) || (settings.enablePvtUdpServer))) + settings.displayServerIP ^= 1; + + //------------------------------ // Get the network layer parameters //------------------------------ @@ -386,7 +392,7 @@ void menuNetwork() NetworkClient * networkClient(uint8_t user, bool useSSL) { NetworkClient * client; - int type; + uint8_t type; type = networkGetType(user); #if defined(COMPILE_ETHERNET) @@ -395,7 +401,7 @@ NetworkClient * networkClient(uint8_t user, bool useSSL) if (useSSL) client = new NetworkEthernetSslClient(); else - client = new NetworkEthernetClient; + client = new NetworkEthernetClient(); } else #endif // COMPILE_ETHERNET @@ -412,6 +418,58 @@ NetworkClient * networkClient(uint8_t user, bool useSSL) return client; } +//---------------------------------------- +// Allocate a network server +//---------------------------------------- +NetworkServer * networkServer(uint8_t user, uint16_t port) +{ + NetworkServer * _server; + uint8_t type; + + type = networkGetType(user); +#if defined(COMPILE_ETHERNET) + if (type == NETWORK_TYPE_ETHERNET) + { + _server = new NetworkEthernetServer(port); + } + else +#endif // COMPILE_ETHERNET + { +#if defined(COMPILE_WIFI) + _server = new NetworkWiFiServer(port); +#else // COMPILE_WIFI + _server = nullptr; +#endif // COMPILE_WIFI + } + return _server; +} + +//---------------------------------------- +// Constructor for NetworkServer - header is in NetworkServer.h +//---------------------------------------- +NetworkServer::NetworkServer(uint8_t user, uint16_t port) : + _friendClass(false), + _networkType{networkGetType(user)}, + _port{port} +{ +#if defined(COMPILE_ETHERNET) + if (_networkType == NETWORK_TYPE_ETHERNET) + { + _server = new EthernetServer(port); + } + else +#endif // COMPILE_ETHERNET +#if defined(COMPILE_WIFI) + { + _server = new WiFiServer(port); + } +#else // COMPILE_WIFI + { + _server = nullptr; + } +#endif // COMPILE_WIFI +} + //---------------------------------------- // Display the IP address //---------------------------------------- diff --git a/Firmware/RTK_Surveyor/NetworkClient.h b/Firmware/RTK_Surveyor/NetworkClient.h index 0ceba6fa4..707632e9e 100644 --- a/Firmware/RTK_Surveyor/NetworkClient.h +++ b/Firmware/RTK_Surveyor/NetworkClient.h @@ -37,7 +37,7 @@ class NetworkClient : public Client #else // COMPILE_WIFI _client = nullptr; #endif // COMPILE_WIFI - }; + } //------------------------------ // Delete the network client @@ -51,7 +51,7 @@ class NetworkClient : public Client delete _client; _client = nullptr; } - }; + } //------------------------------ // Determine if receive data is available @@ -70,7 +70,17 @@ class NetworkClient : public Client operator bool() { - return _client; +#if defined(COMPILE_ETHERNET) + if (_networkType == NETWORK_TYPE_ETHERNET) + if (_client) + return (*((EthernetClient *)_client)); +#endif // COMPILE_ETHERNET +#if defined(COMPILE_WIFI) + if (_networkType == NETWORK_TYPE_WIFI) + if (_client) + return (*((WiFiClient *)_client)); +#endif // COMPILE_WIFI + return false; } //------------------------------ diff --git a/Firmware/RTK_Surveyor/NetworkServer.h b/Firmware/RTK_Surveyor/NetworkServer.h new file mode 100644 index 000000000..1f0bc81d3 --- /dev/null +++ b/Firmware/RTK_Surveyor/NetworkServer.h @@ -0,0 +1,273 @@ +#ifndef __NETWORK_SERVER_H__ +#define __NETWORK_SERVER_H__ + +extern uint8_t networkGetType(uint8_t user); + +#define PVT_SERVER_MAX_CLIENTS 4 + +class NetworkServer : public Server +{ + protected: + + bool _friendClass; + Server * _server; // Ethernet or WiFi server + uint8_t _networkType; + uint16_t _port; +#if defined(COMPILE_ETHERNET) + EthernetClient _ethernetClient[PVT_SERVER_MAX_CLIENTS]; +#endif // COMPILE_ETHERNET +#if defined(COMPILE_WIFI) + WiFiClient _wifiClient[PVT_SERVER_MAX_CLIENTS]; +#endif // COMPILE_WIFI + + public: + + //------------------------------ + // Create the network server + //------------------------------ + NetworkServer(Server * server, uint8_t networkType, uint16_t port) : + _friendClass(true), + _server{server}, + _networkType{networkType}, + _port{port} + { + } + + //------------------------------ + // Create the network server - implemented in Network.ino + //------------------------------ + NetworkServer(uint8_t user, uint16_t port); + + //------------------------------ + // Delete the network server + //------------------------------ + ~NetworkServer() + { + if (_server) + { +#if defined(COMPILE_WIFI) + if (_networkType == NETWORK_TYPE_WIFI) + { + ((WiFiServer *)_server)->stop(); + } +#endif // COMPILE_WIFI + if (!_friendClass) + delete _server; + _server = nullptr; + } + } + + //------------------------------ + // Begin the network server + //------------------------------ + + void begin() + { + // if (_server) _server->begin(); doesn't work... +#if defined(COMPILE_ETHERNET) + if (_networkType == NETWORK_TYPE_ETHERNET) + if (_server) + ((EthernetServer *)_server)->begin(); +#endif // COMPILE_ETHERNET +#if defined(COMPILE_WIFI) + if (_networkType == NETWORK_TYPE_WIFI) + if (_server) + ((WiFiServer *)_server)->begin(); +#endif // COMPILE_WIFI + } + + //------------------------------ + // Determine if new client is available + //------------------------------ + + Client *available(uint8_t index) + { + if (index < PVT_SERVER_MAX_CLIENTS) + { +#if defined(COMPILE_ETHERNET) + if (_networkType == NETWORK_TYPE_ETHERNET) + if (_server) + { + _ethernetClient[index] = ((EthernetServer *)_server)->available(); + return &_ethernetClient[index]; + } +#endif // COMPILE_ETHERNET +#if defined(COMPILE_WIFI) + if (_networkType == NETWORK_TYPE_WIFI) + if (_server) + { + _wifiClient[index] = ((WiFiServer *)_server)->available(); + return &_wifiClient[index]; + } +#endif // COMPILE_WIFI + } + return nullptr; + } + + //------------------------------ + // Accept new client + //------------------------------ + + Client *accept(uint8_t index) + { + if (index < PVT_SERVER_MAX_CLIENTS) + { +#if defined(COMPILE_ETHERNET) + if (_networkType == NETWORK_TYPE_ETHERNET) + if (_server) + { + _ethernetClient[index] = ((EthernetServer *)_server)->accept(); + return &_ethernetClient[index]; + } +#endif // COMPILE_ETHERNET +#if defined(COMPILE_WIFI) + if (_networkType == NETWORK_TYPE_WIFI) + if (_server) + { + _wifiClient[index] = ((WiFiServer *)_server)->accept(); + return &_wifiClient[index]; + } +#endif // COMPILE_WIFI + } + return nullptr; + } + + //------------------------------ + // Determine if the network server was allocated + //------------------------------ + + operator bool() + { +#if defined(COMPILE_ETHERNET) + if (_networkType == NETWORK_TYPE_ETHERNET) + if (_server) + return (*((EthernetServer *)_server)); +#endif // COMPILE_ETHERNET +#if defined(COMPILE_WIFI) + if (_networkType == NETWORK_TYPE_WIFI) + if (_server) + return (*((WiFiServer *)_server)); +#endif // COMPILE_WIFI + return false; + } + + //------------------------------ + // Stop the network server + //------------------------------ + + void stop() // WiFiServer only - EthernetServer has no stop method + { +#if defined(COMPILE_WIFI) + if (_networkType == NETWORK_TYPE_WIFI) + if (_server) + ((WiFiServer *)_server)->stop(); +#endif // COMPILE_WIFI + } + + //------------------------------ + // Send a data byte to the server + //------------------------------ + + size_t write(uint8_t b) + { + // if (_server) return _server->write(b); doesn't work... +#if defined(COMPILE_ETHERNET) + if (_networkType == NETWORK_TYPE_ETHERNET) + if (_server) + return ((EthernetServer *)_server)->write(b); +#endif // COMPILE_ETHERNET +#if defined(COMPILE_WIFI) + if (_networkType == NETWORK_TYPE_WIFI) + if (_server) + return ((WiFiServer *)_server)->write(b); +#endif // COMPILE_WIFI + return 0; + } + + //------------------------------ + // Send a buffer of data to the server + //------------------------------ + + size_t write(const uint8_t *buf, size_t size) + { + // if (_server) return _server->write(buf, size); doesn't work... +#if defined(COMPILE_ETHERNET) + if (_networkType == NETWORK_TYPE_ETHERNET) + if (_server) + return ((EthernetServer *)_server)->write(buf, size); +#endif // COMPILE_ETHERNET +#if defined(COMPILE_WIFI) + if (_networkType == NETWORK_TYPE_WIFI) + if (_server) + return ((WiFiServer *)_server)->write(buf, size); +#endif // COMPILE_WIFI + return 0; + } + + protected: + + //------------------------------ + // Declare the friend classes + //------------------------------ + + friend class NetworkEthernetServer; + friend class NetworkWiFiServer; +}; + +#ifdef COMPILE_ETHERNET +class NetworkEthernetServer : public NetworkServer +{ + protected: + + EthernetServer _ethernetServer; + + public: + + NetworkEthernetServer(uint16_t port) : + _ethernetServer{EthernetServer(port)}, + NetworkServer(&_ethernetServer, NETWORK_TYPE_ETHERNET, port) + { + } + + NetworkEthernetServer(EthernetServer& server, uint16_t port) : + _ethernetServer{server}, + NetworkServer(&_ethernetServer, NETWORK_TYPE_ETHERNET, port) + { + } + + ~NetworkEthernetServer() + { + this->~NetworkServer(); + } +}; +#endif // COMPILE_ETHERNET + +#ifdef COMPILE_WIFI +class NetworkWiFiServer : public NetworkServer +{ + protected: + + WiFiServer _wifiServer; + + public: + + NetworkWiFiServer(uint16_t port) : + _wifiServer{WiFiServer(port)}, + NetworkServer(&_wifiServer, NETWORK_TYPE_WIFI, port) + { + } + + NetworkWiFiServer(WiFiServer& server, uint16_t port) : + _wifiServer{server}, + NetworkServer(&_wifiServer, NETWORK_TYPE_WIFI, port) + { + } + + ~NetworkWiFiServer() + { + this->~NetworkServer(); + } +}; +#endif // COMPILE_WIFI + +#endif // __NETWORK_SERVER_H__ diff --git a/Firmware/RTK_Surveyor/NetworkUDP.h b/Firmware/RTK_Surveyor/NetworkUDP.h index 3548d0c75..68a2e51b4 100644 --- a/Firmware/RTK_Surveyor/NetworkUDP.h +++ b/Firmware/RTK_Surveyor/NetworkUDP.h @@ -56,6 +56,7 @@ class NetworkUDP : public UDP //------------------------------ // Determine if the network client was allocated + // Note: EthernetUDP and WiFiUDP do not have operator bool() methods //------------------------------ operator bool() diff --git a/Firmware/RTK_Surveyor/NtripClient.ino b/Firmware/RTK_Surveyor/NtripClient.ino index 7b01b4a6c..638a132b0 100644 --- a/Firmware/RTK_Surveyor/NtripClient.ino +++ b/Firmware/RTK_Surveyor/NtripClient.ino @@ -721,7 +721,7 @@ void ntripClientUpdate() { // Set the Main Talker ID to "GP". The NMEA GGA messages will be GPGGA instead of GNGGA theGNSS.setVal8(UBLOX_CFG_NMEA_MAINTALKERID, 1); - theGNSS.setNMEAGPGGAcallbackPtr(&pushGPGGA); // Set up the callback for GPGGA + theGNSS.setNMEAGPGGAcallbackPtr(&newGPGGA); // Set up the callback for GPGGA float measurementFrequency = (1000.0 / settings.measurementRate) / settings.navigationRate; if (measurementFrequency < 0.2) @@ -850,6 +850,9 @@ void ntripClientUpdate() } } } + + // Now that the ntripClient->read is complete, write GPGGA if needed and available. See RTK Everywhere issue #695 + pushGPGGA(nullptr); } break; } @@ -869,24 +872,60 @@ void ntripClientValidateTables() reportFatalError("Fix ntripClientStateNameEntries to match NTRIPClientState"); } -void pushGPGGA(NMEA_GGA_data_t *nmeaData) +void newGPGGA(NMEA_GGA_data_t *nmeaData) +{ + // New GPGGA data received from GNSS. Store it. ntripClientUpdate will push it. + pushGPGGA((char *)nmeaData->nmea); +} + +// Periodically push GGA sentence over NTRIP Client, to Caster, if enabled +// We must not push to the Caster while we are reading data from the Caster +// See RTK Everywhere Issue #695 +// pushGPGGA is called by processUart1Message from gnssReadTask +// ntripClient->read is called by ntripClientUpdate and ntripClientResponse from networkUpdate from loop +// We need to make sure processUart1Message doesn't gatecrash +// If ggaData is provided, store it. If ggaData is nullptr, try to push it +static void pushGPGGA(char *ggaData) { - // Provide the caster with our current position as needed - if (ntripClient->connected() && settings.ntripClient_TransmitGGA == true) + static char storedGPGGA[100]; + + static SemaphoreHandle_t reentrant = xSemaphoreCreateMutex(); // Create the mutex + + if (xSemaphoreTake(reentrant, 10 / portTICK_PERIOD_MS) == pdPASS) { - if (millis() - lastGGAPush > NTRIPCLIENT_MS_BETWEEN_GGA) + if (ggaData) { - lastGGAPush = millis(); + snprintf(storedGPGGA, sizeof(storedGPGGA), "%s", ggaData); + xSemaphoreGive(reentrant); + return; + } - if (settings.debugNtripClientRtcm || PERIODIC_DISPLAY(PD_NTRIP_CLIENT_GGA)) +#ifdef COMPILE_NETWORK + // Wait until the client has been created + if (ntripClient != nullptr) + { + // Provide the caster with our current position as needed + if (ntripClient->connected() && settings.ntripClient_TransmitGGA == true) { - PERIODIC_CLEAR(PD_NTRIP_CLIENT_GGA); - systemPrintf("NTRIP Client pushing GGA to server: %s", (const char *)nmeaData->nmea); - } + if (millis() - lastGGAPush > NTRIPCLIENT_MS_BETWEEN_GGA) + { + lastGGAPush = millis(); + + if ((settings.debugNtripClientRtcm || PERIODIC_DISPLAY(PD_NTRIP_CLIENT_GGA)) && !inMainMenu) + { + PERIODIC_CLEAR(PD_NTRIP_CLIENT_GGA); + systemPrintf("NTRIP Client pushing GGA to server: %s", (const char *)storedGPGGA); + } - // Push our current GGA sentence to caster - ntripClient->print((const char *)nmeaData->nmea); + // Push our current GGA sentence to caster + if (strlen(storedGPGGA) > 0) + ntripClient->write((const uint8_t *)storedGPGGA, strlen(storedGPGGA)); + } + } } +#endif // COMPILE_NETWORK + + xSemaphoreGive(reentrant); } } diff --git a/Firmware/RTK_Surveyor/PvtServer.ino b/Firmware/RTK_Surveyor/PvtServer.ino index ed91cd710..85e547f09 100644 --- a/Firmware/RTK_Surveyor/PvtServer.ino +++ b/Firmware/RTK_Surveyor/PvtServer.ino @@ -31,13 +31,12 @@ pvtServer.ino */ -#ifdef COMPILE_WIFI +#if COMPILE_NETWORK //---------------------------------------- // Constants //---------------------------------------- -#define PVT_SERVER_MAX_CLIENTS 4 #define PVT_SERVER_CLIENT_DATA_TIMEOUT (15 * 1000) // Define the PVT server states @@ -68,7 +67,7 @@ const RtkMode_t pvtServerMode = RTK_MODE_BASE_FIXED //---------------------------------------- // PVT server -static WiFiServer * pvtServer = nullptr; +static NetworkServer * pvtServer = nullptr; static uint8_t pvtServerState; static uint32_t pvtServerTimer; @@ -211,6 +210,8 @@ void discardPvtServerBytes(RING_BUFFER_OFFSET previousTail, RING_BUFFER_OFFSET n // PVT Server Routines //---------------------------------------- +bool pvtServerRunning() { return (pvtServerState == PVT_SERVER_STATE_RUNNING); } + // Update the state of the PVT server state machine void pvtServerSetState(uint8_t newState) { @@ -240,17 +241,23 @@ bool pvtServerStart() { IPAddress localIp; + NETWORK_DATA * network; + network = &networkData; + if (network) + localIp = networkGetIpAddress(network->type); + if (settings.debugPvtServer && (!inMainMenu)) systemPrintln("PVT server starting the server"); // Start the PVT server - pvtServer = new WiFiServer(settings.pvtServerPort); + if (pvtServer == nullptr) + pvtServer = networkServer(NETWORK_USER_PVT_SERVER, settings.pvtServerPort); if (!pvtServer) return false; pvtServer->begin(); online.pvtServer = true; - localIp = wifiGetIpAddress(); + systemPrintf("PVT server online, IP address %d.%d.%d.%d:%d\r\n", localIp[0], localIp[1], localIp[2], localIp[3], settings.pvtServerPort); @@ -306,31 +313,37 @@ void pvtServerStopClient(int index) bool connected; bool dataSent; - // Done with this client connection - if ((settings.debugPvtServer || PERIODIC_DISPLAY(PD_PVT_SERVER_DATA)) && (!inMainMenu)) + // Determine if a client was allocated + if (pvtServerClient[index]) { - PERIODIC_CLEAR(PD_PVT_SERVER_DATA); - - // Determine the shutdown reason - connected = pvtServerClient[index]->connected() - && (!(pvtServerClientWriteError & (1 << index))); - dataSent = ((millis() - pvtServerTimer) < PVT_SERVER_CLIENT_DATA_TIMEOUT) - || (pvtServerClientDataSent & (1 << index)); - if (!dataSent) - systemPrintf("PVT Server: No data sent over %d seconds\r\n", - PVT_SERVER_CLIENT_DATA_TIMEOUT / 1000); - if (!connected) - systemPrintf("PVT Server: Link to client broken\r\n"); - systemPrintf("PVT server client %d disconnected from %d.%d.%d.%d\r\n", - index, - pvtServerClientIpAddress[index][0], - pvtServerClientIpAddress[index][1], - pvtServerClientIpAddress[index][2], - pvtServerClientIpAddress[index][3]); - } + // Done with this client connection + if ((settings.debugPvtServer || PERIODIC_DISPLAY(PD_PVT_SERVER_DATA)) && (!inMainMenu)) + { + //PERIODIC_CLEAR(PD_PVT_SERVER_DATA); + + // Determine the shutdown reason + connected = pvtServerClient[index]->connected() + && (!(pvtServerClientWriteError & (1 << index))); + dataSent = ((millis() - pvtServerTimer) < PVT_SERVER_CLIENT_DATA_TIMEOUT) + || (pvtServerClientDataSent & (1 << index)); + if (!dataSent) + systemPrintf("PVT Server: No data sent over %d seconds\r\n", + PVT_SERVER_CLIENT_DATA_TIMEOUT / 1000); + if (!connected) + systemPrintf("PVT Server: Link to client broken\r\n"); + systemPrintf("PVT server client %d disconnected from %d.%d.%d.%d\r\n", + index, + pvtServerClientIpAddress[index][0], + pvtServerClientIpAddress[index][1], + pvtServerClientIpAddress[index][2], + pvtServerClientIpAddress[index][3]); + } - // Shutdown the PVT server client link - pvtServerClient[index]->stop(); + // Shutdown the PVT server client link + pvtServerClient[index]->stop(); + delete pvtServerClient[index]; + pvtServerClient[index] = nullptr; + } pvtServerClientConnected &= ~(1 << index); pvtServerClientWriteError &= ~(1 << index); } @@ -380,7 +393,9 @@ void pvtServerUpdate() // Wait until the PVT server is enabled case PVT_SERVER_STATE_OFF: // Determine if the PVT server should be running - if (EQ_RTK_MODE(pvtServerMode) && settings.enablePvtServer && (!wifiIsConnected())) + NETWORK_DATA * network; + network = &networkData; + if (EQ_RTK_MODE(pvtServerMode) && settings.enablePvtServer && network && (!networkIsTypeConnected(network->type))) { if (networkUserOpen(NETWORK_USER_PVT_SERVER, NETWORK_TYPE_ACTIVE)) { @@ -433,7 +448,7 @@ void pvtServerUpdate() // Walk the list of PVT server clients for (index = 0; index < PVT_SERVER_MAX_CLIENTS; index++) { - // Determine if the client data structure is in use + // Determine if the client data structure is still in use if (pvtServerClientConnected & (1 << index)) { // Data structure in use @@ -465,27 +480,53 @@ void pvtServerUpdate() // Walk the list of PVT server clients for (index = 0; index < PVT_SERVER_MAX_CLIENTS; index++) { - // Determine if the client data structure is in use + // Determine if the client data structure is not in use if (!(pvtServerClientConnected & (1 << index))) { - WiFiClient client; - // Data structure not in use + NETWORK_DATA * network; + network = &networkData; + if (!network) + break; + // Check for another PVT server client - client = pvtServer->available(); + // Use accept, not available: + // Ethernet accept will return the connected client even if no data received + // Ethernet available expects the client to send data first + // The client instances are stored within the NetworkServer instance + Client *client = pvtServer->accept(index); // Done if no PVT server client found - if (!client) + if (!*client) break; + // Check if the data structure has been initialized + if (pvtServerClient[index] == nullptr) + { + pvtServerClient[index] = new NetworkClient(client, network->type); + + // Check for allocation failure + if(pvtServerClient[index] == nullptr) + { + if (settings.debugPvtServer) + Serial.printf("ERROR: Failed to allocate PVT server client %d!\r\n", index); + break; + } + } + else + { + // This should never happen... + Serial.printf("ERROR: pvtServerClient[%d] already exists!\r\n", index); + break; + } + // Start processing the new PVT server client connection - pvtServerClient[index] = new NetworkWiFiClient(client); pvtServerClientIpAddress[index] = pvtServerClient[index]->remoteIP(); pvtServerClientConnected |= 1 << index; pvtServerClientDataSent |= 1 << index; if ((settings.debugPvtServer || PERIODIC_DISPLAY(PD_PVT_SERVER_DATA)) && (!inMainMenu)) { - PERIODIC_CLEAR(PD_PVT_SERVER_DATA); + PERIODIC_CLEAR(PD_PVT_SERVER_DATA); // This will only print the first client... systemPrintf("PVT server client %d connected to %d.%d.%d.%d\r\n", index, pvtServerClientIpAddress[index][0], @@ -502,6 +543,29 @@ void pvtServerUpdate() // Restart the data verification pvtServerTimer = millis(); pvtServerClientDataSent = 0; + + // Keep WiFi alive for PvtServer when no clients are connected + // Prevents: + // [D][WiFiGeneric.cpp:831] _eventCallback(): Arduino Event: 5 - STA_DISCONNECTED + // [W][WiFiGeneric.cpp:852] _eventCallback(): Reason: 4 - ASSOC_EXPIRE + if (!pvtServerClientConnected) + { + NETWORK_DATA * network; + network = &networkData; + if (network->type == NETWORK_TYPE_WIFI) + { + // Generate a little DNS traffic + // Use a random host. Checking the same host more than once generates no new traffic... + char randomHost[strlen("www.00000000.com") + 1]; + snprintf(randomHost, sizeof(randomHost), "www.%08x.com", millis()); + IPAddress theIP; + unsigned long start = millis(); + WiFi.hostByName(randomHost, theIP); + if (settings.debugPvtServer) + systemPrintf("PVT Server keep alive: WiFi.hostByName(%s) took %ld ms\r\n", randomHost, millis() - start); + } + } + } break; } @@ -537,4 +601,21 @@ void pvtServerZeroTail() pvtServerClientTails[index] = 0; } -#endif // COMPILE_WIFI +void paintPvtServerIP() +{ + IPAddress localIp; + NETWORK_DATA * network; + network = &networkData; + if (network) + localIp = networkGetIpAddress(network->type); + + char message[31]; + + snprintf(message, sizeof(message), "%d.%d. %d.%d: %d", + localIp[0], localIp[1], localIp[2], localIp[3], + settings.pvtServerPort); + + displayMessageSmall(message, 0); +} + +#endif // COMPILE_NETWORK diff --git a/Firmware/RTK_Surveyor/PvtUdpServer.ino b/Firmware/RTK_Surveyor/PvtUdpServer.ino index 36637b0e4..dc8cde354 100644 --- a/Firmware/RTK_Surveyor/PvtUdpServer.ino +++ b/Firmware/RTK_Surveyor/PvtUdpServer.ino @@ -199,6 +199,8 @@ void discardPvtUdpServerBytes(RING_BUFFER_OFFSET previousTail, RING_BUFFER_OFFSE // PVT Server Routines //---------------------------------------- +bool pvtUdpServerRunning() { return (pvtUdpServerState == PVT_UDP_SERVER_STATE_RUNNING); } + // Update the state of the PVT server state machine void pvtUdpServerSetState(uint8_t newState) { @@ -226,8 +228,6 @@ void pvtUdpServerSetState(uint8_t newState) // Start the PVT server bool pvtUdpServerStart() { - IPAddress localIp; - if (settings.debugPvtUdpServer && (!inMainMenu)) systemPrintln("PVT UDP server starting"); @@ -380,4 +380,21 @@ void pvtUdpServerZeroTail() pvtUdpServerTail = 0; } +void paintUdpServerIP() +{ + IPAddress localIp; + NETWORK_DATA * network; + network = &networkData; + if (network) + localIp = networkGetIpAddress(network->type); + + char message[31]; + + snprintf(message, sizeof(message), "%d.%d. %d.%d: %d", + localIp[0], localIp[1], localIp[2], localIp[3], + settings.pvtUdpServerPort); + + displayMessageSmall(message, 0); +} + #endif // COMPILE_NETWORK diff --git a/Firmware/RTK_Surveyor/RTK_Surveyor.ino b/Firmware/RTK_Surveyor/RTK_Surveyor.ino index ff33c7ffe..abf5a77d5 100644 --- a/Firmware/RTK_Surveyor/RTK_Surveyor.ino +++ b/Firmware/RTK_Surveyor/RTK_Surveyor.ino @@ -481,6 +481,12 @@ const uint8_t btMaxEscapeCharacters = 3; // Number of characters needed to enter // External Display //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #include //http://librarymanager/All#SparkFun_Qwiic_Graphic_OLED + +#if COMPILE_NETWORK +bool pvtServerRunning(); // Header +bool pvtUdpServerRunning(); // Header +#endif // COMPILE_NETWORK + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // Firmware binaries loaded from SD @@ -1126,85 +1132,6 @@ void updateLogs() if (online.logging == true) { - // Record any pending trigger events - if (newEventToRecord == true) - { - systemPrintln("Recording event"); - - // Record trigger count with Time Of Week of rising edge (ms), Millisecond fraction of Time Of Week of - // rising edge (ns), and accuracy estimate (ns) - char eventData[82]; // Max NMEA sentence length is 82 - snprintf(eventData, sizeof(eventData), "%d,%d,%d,%d", triggerCount, triggerTowMsR, triggerTowSubMsR, - triggerAccEst); - - char nmeaMessage[82]; // Max NMEA sentence length is 82 - createNMEASentence(CUSTOM_NMEA_TYPE_EVENT, nmeaMessage, sizeof(nmeaMessage), - eventData); // textID, buffer, sizeOfBuffer, text - - if (xSemaphoreTake(sdCardSemaphore, fatSemaphore_shortWait_ms) == pdPASS) - { - markSemaphore(FUNCTION_EVENT); - - ubxFile->println(nmeaMessage); - - xSemaphoreGive(sdCardSemaphore); - newEventToRecord = false; - } - else - { - char semaphoreHolder[50]; - getSemaphoreFunction(semaphoreHolder); - - // While a retry does occur during the next loop, it is possible to loose - // trigger events if they occur too rapidly or if the log file is closed - // before the trigger event is written! - log_w("sdCardSemaphore failed to yield, held by %s, RTK_Surveyor.ino line %d", semaphoreHolder, - __LINE__); - } - } - - // Record the Antenna Reference Position - if available - if (newARPAvailable == true && settings.enableARPLogging && - ((millis() - lastARPLog) > (settings.ARPLoggingInterval_s * 1000))) - { - systemPrintln("Recording Antenna Reference Position"); - - lastARPLog = millis(); - newARPAvailable = false; - - double x = ARPECEFX; - x /= 10000.0; // Convert to m - double y = ARPECEFY; - y /= 10000.0; // Convert to m - double z = ARPECEFZ; - z /= 10000.0; // Convert to m - double h = ARPECEFH; - h /= 10000.0; // Convert to m - char ARPData[82]; // Max NMEA sentence length is 82 - snprintf(ARPData, sizeof(ARPData), "%.4f,%.4f,%.4f,%.4f", x, y, z, h); - - char nmeaMessage[82]; // Max NMEA sentence length is 82 - createNMEASentence(CUSTOM_NMEA_TYPE_ARP_ECEF_XYZH, nmeaMessage, sizeof(nmeaMessage), - ARPData); // textID, buffer, sizeOfBuffer, text - - if (xSemaphoreTake(sdCardSemaphore, fatSemaphore_shortWait_ms) == pdPASS) - { - markSemaphore(FUNCTION_EVENT); - - ubxFile->println(nmeaMessage); - - xSemaphoreGive(sdCardSemaphore); - newEventToRecord = false; - } - else - { - char semaphoreHolder[50]; - getSemaphoreFunction(semaphoreHolder); - log_w("sdCardSemaphore failed to yield, held by %s, RTK_Surveyor.ino line %d", semaphoreHolder, - __LINE__); - } - } - // Report file sizes to show recording is working if ((millis() - lastFileReport) > 5000) { diff --git a/Firmware/RTK_Surveyor/System.ino b/Firmware/RTK_Surveyor/System.ino index dd0031a40..46a395e39 100644 --- a/Firmware/RTK_Surveyor/System.ino +++ b/Firmware/RTK_Surveyor/System.ino @@ -600,6 +600,8 @@ void settingsToDefaults() { static const Settings defaultSettings; memcpy(&settings, &defaultSettings, sizeof(defaultSettings)); + + checkMessageRates(); // Ensure message rates are valid - especially before they are sent to web config } // Given a spot in the ubxMsg array, return true if this message is supported on this platform and firmware version diff --git a/Firmware/RTK_Surveyor/Tasks.ino b/Firmware/RTK_Surveyor/Tasks.ino index ae80edfad..83e265cd1 100644 --- a/Firmware/RTK_Surveyor/Tasks.ino +++ b/Firmware/RTK_Surveyor/Tasks.ino @@ -840,97 +840,185 @@ void handleGnssDataTask(void *e) { markSemaphore(FUNCTION_WRITESD); - // Reduce bytes to record if we have more then the end of the buffer - if ((sdRingBufferTail + bytesToSend) > settings.gnssHandlerBufferSize) - bytesToSend = settings.gnssHandlerBufferSize - sdRingBufferTail; - - if (settings.enablePrintSDBuffers && (!inMainMenu)) + do // Do the SD write in a do loop so we can break out if needed { - int bufferAvailable; - if (USE_I2C_GNSS) - bufferAvailable = serialGNSS.available(); - else + if (settings.enablePrintSDBuffers && (!inMainMenu)) { - theGNSS.checkUblox(); - bufferAvailable = theGNSS.fileBufferAvailable(); + int bufferAvailable; + if (USE_I2C_GNSS) + bufferAvailable = serialGNSS.available(); + else + { + theGNSS.checkUblox(); + bufferAvailable = theGNSS.fileBufferAvailable(); + } + int availableUARTSpace; + if (USE_I2C_GNSS) + availableUARTSpace = settings.uartReceiveBufferSize - bufferAvailable; + else + // Use gnssHandlerBufferSize for now. TODO: work out if the SPI GNSS needs its own buffer + // size setting + availableUARTSpace = settings.gnssHandlerBufferSize - bufferAvailable; + systemPrintf("SD Incoming Serial: %04d\tToRead: %04d\tMovedToBuffer: %04d\tavailableUARTSpace: " + "%04d\tavailableHandlerSpace: %04d\tToRecord: %04d\tRecorded: %04d\tBO: %d\r\n", + bufferAvailable, 0, 0, availableUARTSpace, availableHandlerSpace, bytesToSend, 0, + bufferOverruns); } - int availableUARTSpace; - if (USE_I2C_GNSS) - availableUARTSpace = settings.uartReceiveBufferSize - bufferAvailable; - else - // Use gnssHandlerBufferSize for now. TODO: work out if the SPI GNSS needs its own buffer - // size setting - availableUARTSpace = settings.gnssHandlerBufferSize - bufferAvailable; - systemPrintf("SD Incoming Serial: %04d\tToRead: %04d\tMovedToBuffer: %04d\tavailableUARTSpace: " - "%04d\tavailableHandlerSpace: %04d\tToRecord: %04d\tRecorded: %04d\tBO: %d\r\n", - bufferAvailable, 0, 0, availableUARTSpace, availableHandlerSpace, bytesToSend, 0, - bufferOverruns); - } - // Write the data to the file - long startTime = millis(); - startMillis = millis(); + // For the SD card, we need to write everything we've got + // to prevent the ARP Write and Events from gatecrashing... + + int32_t sendTheseBytes = bytesToSend; - bytesToSend = ubxFile->write(&ringBuffer[sdRingBufferTail], bytesToSend); - if (PERIODIC_DISPLAY(PD_SD_LOG_WRITE) && (bytesToSend > 0)) - { - PERIODIC_CLEAR(PD_SD_LOG_WRITE); - systemPrintf("SD %d bytes written to log file\r\n", bytesToSend); - } + // Reduce bytes to record if we have more then the end of the buffer + if ((sdRingBufferTail + sendTheseBytes) > settings.gnssHandlerBufferSize) + sendTheseBytes = settings.gnssHandlerBufferSize - sdRingBufferTail; - static unsigned long lastFlush = 0; - if (USE_MMC_MICROSD) - { - if (millis() > (lastFlush + 250)) // Flush every 250ms, not every write + // Write the data to the file + startMillis = millis(); + + int32_t bytesSent = ubxFile->write(&ringBuffer[sdRingBufferTail], sendTheseBytes); + + // Account for the sent data or dropped + sdRingBufferTail += bytesSent; + if (sdRingBufferTail >= settings.gnssHandlerBufferSize) + sdRingBufferTail -= settings.gnssHandlerBufferSize; + + if (bytesSent != sendTheseBytes) { - ubxFile->flush(); - lastFlush += 250; + systemPrintf("SD write mismatch (1): wrote %d bytes of %d\r\n", + bytesSent, sendTheseBytes); + break; // Exit the do loop } - } - fileSize = ubxFile->fileSize(); // Update file size - sdFreeSpace -= bytesToSend; // Update remaining space on SD + // If we have more data to write - and the first write was successful + if (bytesToSend > sendTheseBytes) + { + sendTheseBytes = bytesToSend - sendTheseBytes; - // Force file sync every 60s - if (millis() - lastUBXLogSyncTime > 60000) - { - if (productVariant == RTK_SURVEYOR) - digitalWrite(pin_baseStatusLED, - !digitalRead(pin_baseStatusLED)); // Blink LED to indicate logging activity + bytesSent = ubxFile->write(&ringBuffer[sdRingBufferTail], sendTheseBytes); - ubxFile->sync(); - ubxFile->updateFileAccessTimestamp(); // Update the file access time & date + // Account for the sent data or dropped + sdRingBufferTail += bytesSent; + if (sdRingBufferTail >= settings.gnssHandlerBufferSize) // Should be redundant + sdRingBufferTail -= settings.gnssHandlerBufferSize; - if (productVariant == RTK_SURVEYOR) - digitalWrite(pin_baseStatusLED, - !digitalRead(pin_baseStatusLED)); // Return LED to previous state + if (bytesSent != sendTheseBytes) + { + systemPrintf("SD write mismatch (2): wrote %d bytes of %d\r\n", + bytesSent, sendTheseBytes); + break; // Exit the do loop + } + } - lastUBXLogSyncTime = millis(); - } + if (PERIODIC_DISPLAY(PD_SD_LOG_WRITE) && (bytesToSend > 0) && (!inMainMenu)) + { + PERIODIC_CLEAR(PD_SD_LOG_WRITE); + systemPrintf("SD %d bytes written to log file\r\n", bytesToSend); + } - // Remember the maximum transfer time - deltaMillis = millis() - startMillis; - if (maxMillis[RBC_SD_CARD] < deltaMillis) - maxMillis[RBC_SD_CARD] = deltaMillis; - long endTime = millis(); + sdFreeSpace -= bytesToSend; // Update remaining space on SD - if (settings.enablePrintBufferOverrun) - { - if (endTime - startTime > 150) - systemPrintf("Long Write! Time: %ld ms / Location: %ld / Recorded %d bytes / " - "spaceRemaining %d bytes\r\n", - endTime - startTime, fileSize, bytesToSend, combinedSpaceRemaining); - } + // Record any pending trigger events + if (newEventToRecord == true) + { + newEventToRecord = false; - xSemaphoreGive(sdCardSemaphore); + if ((settings.enablePrintLogFileStatus) && (!inMainMenu)) + systemPrintln("Recording event"); - // Account for the sent data or dropped - if (bytesToSend > 0) - { - sdRingBufferTail += bytesToSend; - if (sdRingBufferTail >= settings.gnssHandlerBufferSize) - sdRingBufferTail -= settings.gnssHandlerBufferSize; - } + // Record trigger count with Time Of Week of rising edge (ms), Millisecond fraction of Time Of Week of + // rising edge (ns), and accuracy estimate (ns) + char eventData[82]; // Max NMEA sentence length is 82 + snprintf(eventData, sizeof(eventData), "%d,%d,%d,%d", triggerCount, triggerTowMsR, triggerTowSubMsR, + triggerAccEst); + + char nmeaMessage[82]; // Max NMEA sentence length is 82 + createNMEASentence(CUSTOM_NMEA_TYPE_EVENT, nmeaMessage, sizeof(nmeaMessage), + eventData); // textID, buffer, sizeOfBuffer, text + + ubxFile->write((const uint8_t *)nmeaMessage, strlen(nmeaMessage)); + const char *crlf = "\r\n"; + ubxFile->write((const uint8_t *)crlf, 2); + + sdFreeSpace -= strlen(nmeaMessage) + 2; // Update remaining space on SD + } + + // Record the Antenna Reference Position - if available + if (newARPAvailable == true && settings.enableARPLogging && + ((millis() - lastARPLog) > (settings.ARPLoggingInterval_s * 1000))) + { + lastARPLog = millis(); + newARPAvailable = false; + + double x = ARPECEFX; + x /= 10000.0; // Convert to m + double y = ARPECEFY; + y /= 10000.0; // Convert to m + double z = ARPECEFZ; + z /= 10000.0; // Convert to m + double h = ARPECEFH; + h /= 10000.0; // Convert to m + char ARPData[82]; // Max NMEA sentence length is 82 + snprintf(ARPData, sizeof(ARPData), "%.4f,%.4f,%.4f,%.4f", x, y, z, h); + + if ((settings.enablePrintLogFileStatus) && (!inMainMenu)) + systemPrintf("Recording Antenna Reference Position %s\r\n", ARPData); + + char nmeaMessage[82]; // Max NMEA sentence length is 82 + createNMEASentence(CUSTOM_NMEA_TYPE_ARP_ECEF_XYZH, nmeaMessage, sizeof(nmeaMessage), + ARPData); // textID, buffer, sizeOfBuffer, text + + ubxFile->write((const uint8_t *)nmeaMessage, strlen(nmeaMessage)); + const char *crlf = "\r\n"; + ubxFile->write((const uint8_t *)crlf, 2); + + sdFreeSpace -= strlen(nmeaMessage) + 2; // Update remaining space on SD + } + + static unsigned long lastFlush = 0; + if (USE_MMC_MICROSD) + { + if (millis() > (lastFlush + 250)) // Flush every 250ms, not every write + { + ubxFile->flush(); + lastFlush += 250; + } + } + fileSize = ubxFile->fileSize(); // Update file size + + // Force file sync every 60s + if (millis() - lastUBXLogSyncTime > 60000) + { + if (productVariant == RTK_SURVEYOR) + digitalWrite(pin_baseStatusLED, + !digitalRead(pin_baseStatusLED)); // Blink LED to indicate logging activity + + ubxFile->sync(); + ubxFile->updateFileAccessTimestamp(); // Update the file access time & date + + if (productVariant == RTK_SURVEYOR) + digitalWrite(pin_baseStatusLED, + !digitalRead(pin_baseStatusLED)); // Return LED to previous state + + lastUBXLogSyncTime = millis(); + } + + // Remember the maximum transfer time + deltaMillis = millis() - startMillis; + if (maxMillis[RBC_SD_CARD] < deltaMillis) + maxMillis[RBC_SD_CARD] = deltaMillis; + + if (settings.enablePrintBufferOverrun) + { + if (deltaMillis > 150) + systemPrintf("Long Write! Time: %ld ms / Location: %ld / Recorded %d bytes / " + "spaceRemaining %d bytes\r\n", + deltaMillis, fileSize, bytesToSend, combinedSpaceRemaining); + } + } while(0); + + xSemaphoreGive(sdCardSemaphore); } // End sdCardSemaphore else { diff --git a/Firmware/RTK_Surveyor/WiFi.ino b/Firmware/RTK_Surveyor/WiFi.ino index e9101f451..fecb8dae1 100644 --- a/Firmware/RTK_Surveyor/WiFi.ino +++ b/Firmware/RTK_Surveyor/WiFi.ino @@ -524,6 +524,8 @@ bool wifiConnect(unsigned long timeout) int wifiResponse = wifiMulti.run(timeout); if (wifiResponse == WL_CONNECTED) { + // addService fails with "[E][ESPmDNS.cpp:148] addService(): Failed adding service http.tcp." + // Do we need it? Maybe we should wait until after the WiFiServer is started? if (settings.enablePvtClient == true || settings.enablePvtServer == true || settings.enablePvtUdpServer == true) { if (settings.mdnsEnable == true) diff --git a/Firmware/RTK_Surveyor/ZED.ino b/Firmware/RTK_Surveyor/ZED.ino index e2e2ae25a..d5b5240c7 100644 --- a/Firmware/RTK_Surveyor/ZED.ino +++ b/Firmware/RTK_Surveyor/ZED.ino @@ -97,7 +97,7 @@ bool zedDisableLBandCommunication() } else { - systemPrintln("zedEnableLBandCorrections: Unknown platform"); + systemPrintln("zedDisableLBandCorrections: Unknown platform"); return (false); } @@ -106,4 +106,4 @@ bool zedDisableLBandCommunication() #endif return (response); -} \ No newline at end of file +} diff --git a/Firmware/RTK_Surveyor/bluetoothSelect.h b/Firmware/RTK_Surveyor/bluetoothSelect.h index 11d1279a0..aee95a103 100644 --- a/Firmware/RTK_Surveyor/bluetoothSelect.h +++ b/Firmware/RTK_Surveyor/bluetoothSelect.h @@ -5,7 +5,7 @@ //https://github.com/sparkfun/SparkFun_RTK_Firmware/issues/469 #include "src/BluetoothSerial/BluetoothSerial.h" -#include //Click here to get the library: http://librarymanager/All#ESP32_BleSerial v1.0.4 by Avinab Malla +#include //Click here to get the library: http://librarymanager/All#ESP32_BleSerial v1.0.5 by Avinab Malla class BTSerialInterface { diff --git a/Firmware/RTK_Surveyor/settings.h b/Firmware/RTK_Surveyor/settings.h index 2a792e2e8..10633ea1e 100644 --- a/Firmware/RTK_Surveyor/settings.h +++ b/Firmware/RTK_Surveyor/settings.h @@ -280,6 +280,7 @@ enum WiFiState volatile byte wifiState = WIFI_STATE_OFF; #include "NetworkClient.h" // Built-in - Supports both WiFiClient and EthernetClient +#include "NetworkServer.h" // Built-in - Supports both WiFiServer and EthernetServer #include "NetworkUDP.h" //Built-in - Supports both WiFiUdp and EthernetUdp // NTRIP Server data @@ -1191,6 +1192,7 @@ typedef struct bool debugPvtServer = false; bool enablePvtServer = false; uint16_t pvtServerPort = 2948; // PVT server port, 2948 is GPS Daemon: http://tcp-udp-ports.com/port-2948.htm + bool displayServerIP = true; // UDP Server bool debugPvtUdpServer = false;