diff --git a/Firmware/RTK_Everywhere/Begin.ino b/Firmware/RTK_Everywhere/Begin.ino index 69f48d173..84d79970f 100644 --- a/Firmware/RTK_Everywhere/Begin.ino +++ b/Firmware/RTK_Everywhere/Begin.ino @@ -161,6 +161,7 @@ void identifyBoard() productVariant = RTK_POSTCARD; #ifndef NOT_FACET_FLEX + systemPrintln("<<<<<<<<<< !!!!!!!!!! FLEX FORCED !!!!!!!!!! >>>>>>>>>>"); productVariant = RTK_FLEX; // TODO remove once v1.1 Flex has ID resistors #endif } @@ -1129,27 +1130,8 @@ void beginGnssUart() } } -// Assign GNSS UART interrupts to the core that started the task. See: -// https://github.com/espressif/arduino-esp32/issues/3386 -void pinGnssUartTask(void *pvParameters) +void forceGnssCommunicationRate(uint32_t &platformGnssCommunicationRate) { - // Start notification - if (settings.printTaskStartStop) - systemPrintln("Task pinGnssUartTask started"); - - if (serialGNSS == nullptr) - serialGNSS = new HardwareSerial(2); // Use UART2 on the ESP32 for communication with the GNSS module - - serialGNSS->setRxBufferSize(settings.uartReceiveBufferSize); - serialGNSS->setTimeout(settings.serialTimeoutGNSS); // Requires serial traffic on the UART pins for detection - - if (pin_GnssUart_RX == -1 || pin_GnssUart_TX == -1) - reportFatalError("Illegal UART pin assignment."); - - uint32_t platformGnssCommunicationRate = - settings.dataPortBaud; // Default to 230400bps for ZED. This limits GNSS fixes at 4Hz but allows SD buffer to be - // reduced to 6k. - if (productVariant == RTK_TORCH) { // Override user setting. Required because beginGnssUart() is called before beginBoard(). @@ -1178,6 +1160,29 @@ void pinGnssUartTask(void *pvParameters) platformGnssCommunicationRate = 115200; } } +} +// Assign GNSS UART interrupts to the core that started the task. See: +// https://github.com/espressif/arduino-esp32/issues/3386 +void pinGnssUartTask(void *pvParameters) +{ + // Start notification + if (settings.printTaskStartStop) + systemPrintln("Task pinGnssUartTask started"); + + if (serialGNSS == nullptr) + serialGNSS = new HardwareSerial(2); // Use UART2 on the ESP32 for communication with the GNSS module + + serialGNSS->setRxBufferSize(settings.uartReceiveBufferSize); + serialGNSS->setTimeout(settings.serialTimeoutGNSS); // Requires serial traffic on the UART pins for detection + + if (pin_GnssUart_RX == -1 || pin_GnssUart_TX == -1) + reportFatalError("Illegal UART pin assignment."); + + uint32_t platformGnssCommunicationRate = + settings.dataPortBaud; // Default to 230400bps for ZED. This limits GNSS fixes at 4Hz but allows SD buffer to be + // reduced to 6k. + + forceGnssCommunicationRate(platformGnssCommunicationRate); serialGNSS->begin(platformGnssCommunicationRate, SERIAL_8N1, pin_GnssUart_RX, pin_GnssUart_TX); // Start UART on platform dependent pins for SPP. The GNSS will be diff --git a/Firmware/RTK_Everywhere/Display.ino b/Firmware/RTK_Everywhere/Display.ino index 0fbbf7fa8..644005dcb 100644 --- a/Firmware/RTK_Everywhere/Display.ino +++ b/Firmware/RTK_Everywhere/Display.ino @@ -3288,3 +3288,30 @@ void displayWebConfig(std::vector &iconPropertyList) printTextCenter(myIP, yPos, QW_FONT_5X7, 1, false); } + +// Show GNSS update - button exit +void paintGnssUpdate() +{ + paintGenericUpdate("GNSS"); +} +void paintLoRaUpdate() +{ + paintGenericUpdate("LoRa"); +} +void paintGenericUpdate(const char *device) +{ + if (online.display) + { + oled->erase(); // Clear the display's internal buffer + int yPos = (oled->getHeight() - 38) / 2; + uint8_t fontHeight = 8; + printTextCenter(device, yPos, QW_FONT_5X7, 1, false); // text, y, font type, kerning, inverted + yPos = yPos + fontHeight + 1; + printTextCenter("Update", yPos, QW_FONT_5X7, 1, false); + yPos = yPos + fontHeight + 3; + printTextCenter("Button", yPos, QW_FONT_5X7, 1, true); // text, y, font type, kerning, inverted + yPos = yPos + fontHeight + 1; + printTextCenter("To Exit", yPos, QW_FONT_5X7, 1, true); + oled->display(); // Push internal buffer to display + } +} diff --git a/Firmware/RTK_Everywhere/GNSS.ino b/Firmware/RTK_Everywhere/GNSS.ino index 6c558f985..e58b16356 100644 --- a/Firmware/RTK_Everywhere/GNSS.ino +++ b/Firmware/RTK_Everywhere/GNSS.ino @@ -142,7 +142,10 @@ void gnssDetectReceiverType() // TODO remove after testing, force retest on each boot // Note: with this in place, the X5 detection will take a lot longer due to the baud rate change - settings.detectedGnssReceiver = GNSS_RECEIVER_UNKNOWN; +#ifndef NOT_FACET_FLEX + systemPrintln("<<<<<<<<<< !!!!!!!!!! FLEX FORCED !!!!!!!!!! >>>>>>>>>>"); + //settings.detectedGnssReceiver = GNSS_RECEIVER_UNKNOWN; // This may be causing weirdness on the LG290P. Commenting for now +#endif // Start auto-detect if NVM is not yet set if (settings.detectedGnssReceiver == GNSS_RECEIVER_UNKNOWN) @@ -243,4 +246,174 @@ void gnssReset() { digitalWrite(pin_GNSS_Reset, LOW); // Tell LG290P to reset } -} \ No newline at end of file +} + +//---------------------------------------- +// Force UART connection to GNSS for firmware update on the next boot by special file in +// LittleFS +//---------------------------------------- +bool createGNSSPassthrough() +{ + return createPassthrough("/updateGnssFirmware.txt"); +} +bool createPassthrough(const char *filename) +{ + if (online.fs == false) + return false; + + if (LittleFS.exists(filename)) + { + if (settings.debugGnss) + systemPrintf("LittleFS %s already exists\r\n", filename); + return true; + } + + File updateUm980Firmware = LittleFS.open(filename, FILE_WRITE); + updateUm980Firmware.close(); + + if (LittleFS.exists(filename)) + return true; + + if (settings.debugGnss) + systemPrintf("Unable to create %s on LittleFS\r\n", filename); + return false; +} + +//---------------------------------------- +void gnssFirmwareBeginUpdate() +{ + // Note: UM980 needs its own dedicated update function, due to the T@ and bootloader trigger + + // Note: gnssFirmwareBeginUpdate is called during setup, after identify board. I2C, gpio expanders, buttons + // and display have all been initialized. But, importantly, the UARTs have not yet been started. + // This makes our job much easier... + + // NOTE: QGNSS firmware update fails on LG290P due, I think, to QGNSS doing some kind of autobaud + // detection at the start of the update. I think the delays introduced by serialGNSS->write(Serial.read()) + // and Serial.write(serialGNSS->read()) cause the failure, but I'm not sure. + // It seems that LG290P needs a dedicated hardware link from USB to GNSS UART for a successful update. + // This will be added in the next rev of the Flex motherboard. + + // NOTE: X20P will expect a baud rate change during the update, unless we force 9600 baud. + // The dedicated hardware link will make X20P firmware updates easy. + + // Flag that we are in direct connect mode. Button task will gnssFirmwareRemoveUpdate and exit + inDirectConnectMode = true; + + // Note: we can't call gnssFirmwareRemoveUpdate() here as closing Tera Term will reset the ESP32, + // returning the firmware to normal operation... + + // Paint GNSS Update + paintGnssUpdate(); + + // Stop all UART tasks. Redundant + tasksStopGnssUart(); + + uint32_t serialBaud = 115200; + + forceGnssCommunicationRate(serialBaud); // On Flex, must be called after gnssDetectReceiverType + + systemPrintln(); + systemPrintf("Entering GNSS direct connect for firmware update and configuration. Disconnect this terminal " + "connection. Use the GNSS manufacturer software " + "to update the firmware. Baudrate: %dbps. Press the %s button to return " + "to normal operation.\r\n", serialBaud, present.button_mode ? "mode" : "power"); + systemFlush(); + + Serial.end(); // We must end before we begin otherwise the UART settings are corrupted + Serial.begin(serialBaud); + + if (serialGNSS == nullptr) + serialGNSS = new HardwareSerial(2); // Use UART2 on the ESP32 for communication with the GNSS module + + serialGNSS->setRxBufferSize(settings.uartReceiveBufferSize); + serialGNSS->setTimeout(settings.serialTimeoutGNSS); // Requires serial traffic on the UART pins for detection + + // This is OK for Flex too. We're using the main GNSS pins. + serialGNSS->begin(serialBaud, SERIAL_8N1, pin_GnssUart_RX, pin_GnssUart_TX); + + // Echo everything to/from GNSS + while (1) + { + static unsigned long lastSerial = millis(); // Temporary fix for buttonless Flex + + if (Serial.available()) // Note: use if, not while + { + serialGNSS->write(Serial.read()); + lastSerial = millis(); + } + + if (serialGNSS->available()) // Note: use if, not while + Serial.write(serialGNSS->read()); + + // Button task will gnssFirmwareRemoveUpdate and restart + + // Temporary fix for buttonless Flex. TODO - remove + if ((productVariant == RTK_FLEX) && (millis() > (lastSerial + 30000))) + { + // Beep to indicate exit + beepOn(); + delay(300); + beepOff(); + delay(100); + beepOn(); + delay(300); + beepOff(); + + gnssFirmwareRemoveUpdate(); + + systemPrintln("Exiting direct connection (passthrough) mode"); + systemFlush(); // Complete prints + + ESP.restart(); + } + } +} + +//---------------------------------------- +// Check if direct connection file exists +//---------------------------------------- +bool gnssFirmwareCheckUpdate() +{ + return gnssFirmwareCheckUpdateFile("/updateGnssFirmware.txt"); +} +bool gnssFirmwareCheckUpdateFile(const char *filename) +{ + if (online.fs == false) + return false; + + if (LittleFS.exists(filename)) + { + if (settings.debugGnss) + systemPrintf("LittleFS %s exists\r\n", filename); + + // We do not remove the file here. See removeupdateUm980Firmware(). + + return true; + } + + return false; +} + +//---------------------------------------- +// Remove direct connection file +//---------------------------------------- +void gnssFirmwareRemoveUpdate() +{ + return gnssFirmwareRemoveUpdateFile("/updateGnssFirmware.txt"); +} +void gnssFirmwareRemoveUpdateFile(const char *filename) +{ + if (online.fs == false) + return; + + if (LittleFS.exists(filename)) + { + if (settings.debugGnss) + systemPrintf("Removing %s\r\n", filename); + + LittleFS.remove(filename); + } +} + +//---------------------------------------- diff --git a/Firmware/RTK_Everywhere/GNSS_UM980.ino b/Firmware/RTK_Everywhere/GNSS_UM980.ino index 76bfc1170..e104a8638 100644 --- a/Firmware/RTK_Everywhere/GNSS_UM980.ino +++ b/Firmware/RTK_Everywhere/GNSS_UM980.ino @@ -2040,30 +2040,12 @@ bool GNSS_UM980::setRtcmRoverMessageRateByName(const char *msgName, uint8_t msgR //---------------------------------------- //---------------------------------------- -// Force UART connection to UM980 for firmware update on the next boot by creating updateUm980Firmware.txt in +// Force UART connection to GNSS for firmware update on the next boot by special file in // LittleFS //---------------------------------------- bool createUm980Passthrough() { - if (online.fs == false) - return false; - - if (LittleFS.exists("/updateUm980Firmware.txt")) - { - if (settings.debugGnss) - systemPrintln("LittleFS updateUm980Firmware.txt already exists"); - return true; - } - - File updateUm980Firmware = LittleFS.open("/updateUm980Firmware.txt", FILE_WRITE); - updateUm980Firmware.close(); - - if (LittleFS.exists("/updateUm980Firmware.txt")) - return true; - - if (settings.debugGnss) - systemPrintln("Unable to create updateUm980Firmware.txt on LittleFS"); - return false; + return createPassthrough("/updateUm980Firmware.txt"); } //---------------------------------------- @@ -2076,22 +2058,41 @@ void um980FirmwareBeginUpdate() // then reconfigures the UM980 to 115200bps, then resets, but autobaud detection in the UM980 library is // not yet supported. - // Stop all UART tasks + // Note: UM980 needs its own dedicated update function, due to the T@ and bootloader trigger + + // Note: UM980 is cuurrently only available on Torch. + // But um980FirmwareBeginUpdate has been reworked so it will work on Facet too. + + // Note: um980FirmwareBeginUpdate is called during setup, after identify board. I2C, gpio expanders, buttons + // and display have all been initialized. But, importantly, the UARTs have not yet been started. + // This makes our job much easier... + + // Flag that we are in direct connect mode. Button task will um980FirmwareRemoveUpdate and exit + inDirectConnectMode = true; + + // Paint GNSS Update + paintGnssUpdate(); + + // Stop all UART tasks. Redundant tasksStopGnssUart(); systemPrintln(); - systemPrintln("Entering UM980 direct connect for firmware update and configuration. Disconnect this terminal " + systemPrintf("Entering UM980 direct connect for firmware update and configuration. Disconnect this terminal " "connection. Use " - "UPrecise to update the firmware. Baudrate: 115200bps. Press the power button to return " - "to normal operation."); + "UPrecise to update the firmware. Baudrate: 115200bps. Press the %s button to return " + "to normal operation.\r\n", present.button_mode ? "mode" : "power"); systemFlush(); - // Make sure ESP-UART1 is connected to UM980 + // Make sure ESP-UART is connected to UM980 muxSelectUm980(); if (serialGNSS == nullptr) serialGNSS = new HardwareSerial(2); // Use UART2 on the ESP32 for communication with the GNSS module + serialGNSS->setRxBufferSize(settings.uartReceiveBufferSize); + serialGNSS->setTimeout(settings.serialTimeoutGNSS); // Requires serial traffic on the UART pins for detection + + // This is OK for Flex too. We're using the main GNSS pins. serialGNSS->begin(115200, SERIAL_8N1, pin_GnssUart_RX, pin_GnssUart_TX); // UPrecise needs to query the device before entering bootload mode @@ -2102,11 +2103,11 @@ void um980FirmwareBeginUpdate() while (1) { // Data coming from UM980 to external USB - if (serialGNSS->available()) + if (serialGNSS->available()) // Note: use if, not while Serial.write(serialGNSS->read()); // Data coming from external USB to UM980 - if (Serial.available()) + if (Serial.available()) // Note: use if, not while { byte incoming = Serial.read(); serialGNSS->write(incoming); @@ -2127,67 +2128,24 @@ void um980FirmwareBeginUpdate() } } - if (readAnalogPinAsDigital(pin_powerButton) == HIGH) - { - while (readAnalogPinAsDigital(pin_powerButton) == HIGH) - delay(100); - - // Remove file and reset to exit pass-through mode - um980FirmwareRemoveUpdate(); - - // Beep to indicate exit - beepOn(); - delay(300); - beepOff(); - delay(100); - beepOn(); - delay(300); - beepOff(); - - systemPrintln("Exiting UM980 passthrough mode"); - systemFlush(); // Complete prints - - ESP.restart(); - } + // Button task will um980FirmwareRemoveUpdate and restart } - - systemFlush(); // Complete prints } //---------------------------------------- -// Check if updateUm980Firmware.txt exists +// Check if direct connection file exists //---------------------------------------- bool um980FirmwareCheckUpdate() { - if (online.fs == false) - return false; - - if (LittleFS.exists("/updateUm980Firmware.txt")) - { - if (settings.debugGnss) - systemPrintln("LittleFS updateUm980Firmware.txt exists"); - - // We do not remove the file here. See removeupdateUm980Firmware(). - - return true; - } - - return false; + return gnssFirmwareCheckUpdateFile("/updateUm980Firmware.txt"); } +//---------------------------------------- +// Remove direct connection file //---------------------------------------- void um980FirmwareRemoveUpdate() { - if (online.fs == false) - return; - - if (LittleFS.exists("/updateUm980Firmware.txt")) - { - if (settings.debugGnss) - systemPrintln("Removing updateUm980Firmware.txt "); - - LittleFS.remove("/updateUm980Firmware.txt"); - } + return gnssFirmwareRemoveUpdateFile("/updateUm980Firmware.txt"); } //---------------------------------------- diff --git a/Firmware/RTK_Everywhere/LoRa.ino b/Firmware/RTK_Everywhere/LoRa.ino index 0141ef48a..f82846abb 100644 --- a/Firmware/RTK_Everywhere/LoRa.ino +++ b/Firmware/RTK_Everywhere/LoRa.ino @@ -1,35 +1,46 @@ /*------------------------------------------------------------------------------ LoRa.ino - This module implements the interface to the LoRa radio in the Torch. + This module implements the interface to the LoRa radio in the Torch and Flex. - ESP32 (UART1) <-> Switch U12 B0 <-> UM980 (UART3) - ESP32 (UART1) <-> Switch U12 B1 <-> STM32 LoRa(UART0) + Torch: + ESP32 (UART1) <-> Switch U12 B0 <-> UM980 (UART3) + ESP32 (UART1) <-> Switch U12 B1 <-> STM32 LoRa(UART0) - UART0 on the STM32 is used for debug messages and bootloading new firmware. + UART0 on the STM32 is used for debug messages and bootloading new firmware. - ESP32 (UART0) <-> Switch U18 B0 <-> USB to Serial - ESP32 (UART0) <-> Switch U18 B1 <-> Switch U11 + ESP32 (UART0) <-> Switch U18 B0 <-> USB to Serial + ESP32 (UART0) <-> Switch U18 B1 <-> Switch U11 - Switch U11 B0 <-> STM32 LoRa(UART2) - Switch U11 B1 <-> UM980 (UART1) - Not generally used + Switch U11 B0 <-> STM32 LoRa(UART2) + Switch U11 B1 <-> UM980 (UART1) - Not generally used - UART2 on the STM32 is used for configuration and pushing data across the link. - This poses a bit of a problem: we have to disconnect from USB serial (no prints) - while configuration or data is being passed. + UART2 on the STM32 is used for configuration and pushing data across the link. + This poses a bit of a problem: we have to disconnect from USB serial (no prints) + while configuration or data is being passed. - If we are in Base mode, listen from RTCM. Once received, disconnect from USB, send to - LoRa radio, then re-connect to USB. + If we are in Base mode, listen from RTCM. Once received, disconnect from USB, send to + LoRa radio, then re-connect to USB. - If we are in Rover mode, and LoRa is enabled, then we are connected permanently to the LoRa - radio to listen for incoming serial data. If no USB cable is attached, immediately - go into dedicated listening mode. If a USB cable is detected, then the dedicated listening mode is exited - for X seconds before re-entering the dedicated listening mode. Any serial traffic from USB during this time - resets the timeout. + If we are in Rover mode, and LoRa is enabled, then we are connected permanently to the LoRa + radio to listen for incoming serial data. If no USB cable is attached, immediately + go into dedicated listening mode. If a USB cable is detected, then the dedicated listening mode is exited + for X seconds before re-entering the dedicated listening mode. Any serial traffic from USB during this time + resets the timeout. + + Flex: + Flex GNSS (UART2) <-> Switch 4 B0 <-> 4-Pin Serial TTL on 1mm JST under microSD + Flex GNSS (UART2) <-> Switch 4 B1 <-> STM32 LoRa (UART0) + + ESP32 (UART2) <-> Switch 3 B0 <-> Flex GNSS Tilt (UART3) + ESP32 (UART2) <-> Switch 3 B1 <-> STM32 LoRa (UART2) + + UART0 on the STM32 is used for pushing data across the link. + UART2 on the STM32 is used for configuration. Printing: - Use systemPrint() to output serial to the USB serial interface - Use Serial.print() along with to output + On Torch, Serial must be used to send and receive data from the radio. At times, this requires disconnecting from the USB interface. + On Flex, SerialForLoRa is used on UART2 to configure and TX/RX data from the radio. If active, SerialForTilt must be ended first. Updating the STM32 LoRa Firmware: Bootloading the STM32 requires a connection to the USB serial. Because it is @@ -65,6 +76,8 @@ static volatile uint8_t loraState = LORA_OFF; char loraFirmwareVersion[25] = {'\0'}; int loraBytesSent = 0; +HardwareSerial *SerialForLoRa; // Don't instantiate until we know the platform. May compete with SerialForTilt + // Called from main loop // Control incoming/outgoing RTCM data from STM32 based LoRa radio (if supported by platform) void updateLora() @@ -105,7 +118,7 @@ void updateLora() loraSetupReceive(); systemFlush(); // Complete prints - muxSelectLoRa(); // Disconnect from USB + muxSelectLoRaCommunication(); // Disconnect from USB loraState = LORA_RX_DEDICATED; } else // USB cable attached, share the ESP32 UART0 connection between USB and LoRa @@ -162,7 +175,7 @@ void updateLora() break; case (LORA_RX_DEDICATED): - if (Serial.available()) + if (loraAvailable()) { uint8_t rtcmData[512]; int rtcmCount = 0; @@ -175,9 +188,6 @@ void updateLora() // Pass RTCM bytes (presumably) from LoRa out ESP32-UART to GNSS gnss->pushRawData(rtcmData, rtcmCount); // Push RTCM to GNSS module - // Parse the data for RTCM1005/1006 - sempParseNextBytes(rtcmParse, rtcmData, rtcmCount); - if (((settings.debugCorrections == true) || (settings.debugLora == true)) && !inMainMenu) { systemFlush(); // Complete prints @@ -186,7 +196,7 @@ void updateLora() systemPrintf("LoRa received %d RTCM bytes, pushed to GNSS\r\n", rtcmCount); systemFlush(); // Allow print to complete - muxSelectLoRa(); // Disconnect from USB + muxSelectLoRaCommunication(); // Disconnect from USB } } else @@ -199,7 +209,7 @@ void updateLora() systemPrintf("LoRa received %d RTCM bytes, NOT pushed due to priority\r\n", rtcmCount); systemFlush(); // Allow print to complete - muxSelectLoRa(); // Disconnect from USB + muxSelectLoRaCommunication(); // Disconnect from USB } } } @@ -226,8 +236,8 @@ void updateLora() if ((millis() - loraLastIncomingSerial) / 1000 > settings.loraSerialInteractionTimeout_s) { systemPrintln("LoRa shared port timeout expired. Moving to dedicated LoRa receive with no USB output."); - systemFlush(); // Complete prints - muxSelectLoRa(); // Disconnect from USB + systemFlush(); // Complete prints + muxSelectLoRaCommunication(); // Disconnect from USB loraState = LORA_RX_DEDICATED_USB; } @@ -244,7 +254,7 @@ void updateLora() // USB cable is present. loraSerialInteractionTimeout_s has occurred. Be dedicated until USB is // disconnected. - if (Serial.available()) + if (loraAvailable()) { uint8_t rtcmData[512]; int rtcmCount = 0; @@ -257,9 +267,6 @@ void updateLora() // Pass RTCM bytes (presumably) from LoRa out ESP32-UART to GNSS gnss->pushRawData(rtcmData, rtcmCount); // Push RTCM to GNSS module - // Parse the data for RTCM1005/1006 - sempParseNextBytes(rtcmParse, rtcmData, rtcmCount); - if (((settings.debugCorrections == true) || (settings.debugLora == true)) && !inMainMenu) { systemFlush(); // Complete prints @@ -268,7 +275,7 @@ void updateLora() systemPrintf("LoRa received %d RTCM bytes, pushed to GNSS\r\n", rtcmCount); systemFlush(); // Allow print to complete - muxSelectLoRa(); // Disconnect from USB + muxSelectLoRaCommunication(); // Disconnect from USB } } else @@ -281,7 +288,7 @@ void updateLora() systemPrintf("LoRa received %d RTCM bytes, NOT pushed due to priority\r\n", rtcmCount); systemFlush(); // Allow print to complete - muxSelectLoRa(); // Disconnect from USB + muxSelectLoRaCommunication(); // Disconnect from USB } } } @@ -326,65 +333,111 @@ void loraStop() void muxSelectUm980() { - digitalWrite(pin_muxA, LOW); // Connect ESP UART1 to UM980 -} - -// Used during firmware updates -void muxSelectLoRaUart0() -{ - digitalWrite(pin_muxA, HIGH); // Connect ESP UART1 to LoRa UART0 + // On a possible Flex UM980 variant, UM980 UART1 will be hardwired to ESP32 UART0. No muxes to change + if (productVariant == RTK_TORCH) + digitalWrite(pin_muxA, LOW); // Connect ESP UART1 to UM980 } void muxSelectUsb() { - pinMode(pin_muxB, OUTPUT); // Make really sure we can control this pin - digitalWrite(pin_muxB, LOW); // Connect ESP UART0 to CH340 Serial + if (productVariant == RTK_TORCH) + { + pinMode(pin_muxB, OUTPUT); // Make really sure we can control this pin + digitalWrite(pin_muxB, LOW); // Connect ESP UART0 to CH340 Serial - usbSerialIsSelected = true; // Let other print operations know we are connected to the CH34x + usbSerialIsSelected = true; // Let other print operations know we are connected to the CH34x + } } -void muxSelectLoRa() +// Connect ESP32 to LoRa for regular transmissions +void muxSelectLoRaCommunication() { - pinMode(pin_muxB, OUTPUT); // Make really sure we can control this pin - digitalWrite(pin_muxA, LOW); // Connect ESP UART1 to UM980 - digitalWrite(pin_muxB, HIGH); // Connect ESP UART0 to U11 + if (productVariant == RTK_TORCH) + { + pinMode(pin_muxB, OUTPUT); // Make really sure we can control this pin + digitalWrite(pin_muxA, LOW); // Connect ESP UART1 to UM980 + digitalWrite(pin_muxB, HIGH); // Connect ESP UART0 to U11 - usbSerialIsSelected = false; // Let other print operations know we are not connected to the CH34x + usbSerialIsSelected = false; // Let other print operations know we are not connected to the CH34x + } + else if (productVariant == RTK_FLEX) + { + gpioExpanderSelectLoraCommunication(); + } +} + +// Connect ESP32 to LoRa for configuration and bootloading +void muxSelectLoRaConfigure() +{ + if (productVariant == RTK_TORCH) + digitalWrite(pin_muxA, HIGH); // Connect ESP UART1 to LoRa UART0 + else if (productVariant == RTK_FLEX) + gpioExpanderSelectLoraConfigure(); // Connect ESP32 UART2 to LoRa UART2 } void loraEnterBootloader() { - digitalWrite(pin_loraRadio_boot, HIGH); // Enter bootload mode + if (productVariant == RTK_TORCH) + digitalWrite(pin_loraRadio_boot, HIGH); // Enter bootload mode + else if (productVariant == RTK_FLEX) + gpioExpanderLoraBootEnable(); + loraReset(); } void loraExitBootloader() { - digitalWrite(pin_loraRadio_boot, LOW); // Exit bootload mode + if (productVariant == RTK_TORCH) + digitalWrite(pin_loraRadio_boot, LOW); // Exit bootload mode + else if (productVariant == RTK_FLEX) + gpioExpanderLoraBootDisable(); + loraReset(); } void loraReset() { - digitalWrite(pin_loraRadio_reset, LOW); // Reset STM32/radio - delay(15); - digitalWrite(pin_loraRadio_reset, HIGH); // Run STM32/radio - delay(15); + if (productVariant == RTK_TORCH) + { + digitalWrite(pin_loraRadio_reset, LOW); // Reset STM32/radio + delay(15); + digitalWrite(pin_loraRadio_reset, HIGH); // Run STM32/radio + delay(15); + } + else if (productVariant == RTK_FLEX) + { + gpioExpanderLoraDisable(); + delay(15); + gpioExpanderLoraEnable(); + delay(15); + } } void loraPowerOn() { - digitalWrite(pin_loraRadio_power, HIGH); // Power STM32/radio + if (productVariant == RTK_TORCH) + digitalWrite(pin_loraRadio_power, HIGH); // Power STM32/radio + else if (productVariant == RTK_FLEX) + gpioExpanderLoraEnable(); } void loraPowerOff() { - digitalWrite(pin_loraRadio_power, LOW); // Power off STM32/radio + if (productVariant == RTK_TORCH) + digitalWrite(pin_loraRadio_power, LOW); // Power off STM32/radio + else if (productVariant == RTK_FLEX) + gpioExpanderLoraDisable(); } bool loraIsOn() { - if (digitalRead(pin_loraRadio_power) == HIGH) - return (true); + if (productVariant == RTK_TORCH) + { + if (digitalRead(pin_loraRadio_power) == HIGH) + return (true); + return (false); + } + else if (productVariant == RTK_FLEX) + return (gpioExpanderLoraIsOn()); return (false); } @@ -448,6 +501,19 @@ bool createLoRaPassthrough() void beginLoraFirmwareUpdate() { + // NOTE: this currently fails on Flex due to the way LoRa_EN and LoRa_NRST are interconnected. + // This will be resolved with the next Flex motherboard rev. + // TODO: delete this comment once new hardware is available. + + // Flag that we are in direct connect mode. Button task will removeUpdateLoraFirmware and exit + inDirectConnectMode = true; + + // Paint GNSS Update + paintLoRaUpdate(); + + // Stop all UART tasks. Redundant + tasksStopGnssUart(); + systemPrintln(); systemPrintln("Entering STM32 direct connect for firmware update. Disconnect this terminal connection. Use " "'STM32CubeProgrammer' to update the " @@ -464,12 +530,20 @@ void beginLoraFirmwareUpdate() Serial.begin(57600); // Keep this at slower rate if (serialGNSS == nullptr) - serialGNSS = new HardwareSerial(2); // Use UART2 on the ESP32 for communication with the GNSS module + serialGNSS = new HardwareSerial(2); // Use UART2 on the ESP32 for communication with the LoRa radio + + serialGNSS->setRxBufferSize(settings.uartReceiveBufferSize); + serialGNSS->setTimeout(settings.serialTimeoutGNSS); // Requires serial traffic on the UART pins for detection - serialGNSS->begin(115200, SERIAL_8N1, pin_GnssUart_RX, pin_GnssUart_TX); // Keep this at 115200 + if (productVariant == RTK_TORCH) + serialGNSS->begin(115200, SERIAL_8N1, pin_GnssUart_RX, pin_GnssUart_TX); // Keep this at 115200 + else if (productVariant == RTK_FLEX) + serialGNSS->begin(115200, SERIAL_8N1, pin_IMU_RX, pin_IMU_TX); // Keep this at 115200 + else + systemPrintln("ERROR: productVariant does not support LoRa"); - // Make sure ESP-UART1 is connected to LoRA STM32 UART0 - muxSelectLoRaUart0(); + // Make sure ESP32 is connected to LoRa STM32 UART + muxSelectLoRaConfigure(); loraEnterBootloader(); // Push boot pin high and reset STM32 @@ -478,37 +552,41 @@ void beginLoraFirmwareUpdate() while (Serial.available()) Serial.read(); - // Push any incoming ESP32 UART0 to UART1 and vice versa + // Push any incoming ESP32 UART0 to UART1 or UART2 and vice versa // Infinite loop until button is pressed while (1) { - while (Serial.available()) + static unsigned long lastSerial = millis(); // Temporary fix for buttonless Flex + + if (Serial.available()) // Note: use if, not while + { serialGNSS->write(Serial.read()); + lastSerial = millis(); + } - while (serialGNSS->available()) + if (serialGNSS->available()) // Note: use if, not while Serial.write(serialGNSS->read()); - if (readAnalogPinAsDigital(pin_powerButton) == HIGH) + // Button task will removeUpdateLoraFirmware and restart + + // Temporary fix for buttonless Flex. TODO - remove + if ((productVariant == RTK_FLEX) && (millis() > (lastSerial + 30000))) { - while (readAnalogPinAsDigital(pin_powerButton) == HIGH) + // Beep to indicate exit + beepOn(); + delay(300); + beepOff(); delay(100); + beepOn(); + delay(300); + beepOff(); - // Remove file and reset to exit LoRa update pass-through mode - removeUpdateLoraFirmware(); - - // Beep to indicate exit - beepOn(); - delay(300); - beepOff(); - delay(100); - beepOn(); - delay(300); - beepOff(); + removeUpdateLoraFirmware(); - systemPrintln("Exiting LoRa Firmware update mode"); - systemFlush(); // Complete prints + systemPrintln("Exiting direct connection (passthrough) mode"); + systemFlush(); // Complete prints - ESP.restart(); + ESP.restart(); } } } @@ -579,10 +657,10 @@ bool loraSendCommand(const char *command, char *response, int *responseSize) systemFlush(); // Complete prints - muxSelectLoRa(); // Disconnect from USB + muxSelectLoRaCommunication(); // Disconnect from USB - Serial.printf("%s\r\n", command); - while (Serial.available() == 0) + loraPrintf("%s\r\n", command); + while (loraAvailable() == 0) { delay(1); responseTime++; @@ -594,9 +672,9 @@ bool loraSendCommand(const char *command, char *response, int *responseSize) } delay(10); // Allow all serial to arrive - while (Serial.available()) + while (loraAvailable()) { - response[responseSpot++] = Serial.read(); + response[responseSpot++] = loraRead(); if (responseSpot == *responseSize) { responseSpot--; @@ -613,7 +691,8 @@ bool loraSendCommand(const char *command, char *response, int *responseSize) return (false); } -// Disconnects from USB +// On the Torch, USB and LoRa radio are shared, so disconnects from USB are required +// On the Flex, LoRa UART2 is on ESP32 UART2 // Sends AT+V?, if response, we are already in command mode -> Reconnects to USB, Return // Sends +++ (but there is no response) // Sends AT+V?, if response, we are in command mode -> Reconnects to USB, Return @@ -628,14 +707,14 @@ bool loraEnterCommandMode() systemFlush(); // Complete prints - muxSelectLoRa(); // Disconnect from USB + muxSelectLoRaConfigure(); // Connect to the STM32 for configuration delay(50); // Wait for incoming serial to complete while (Serial.available()) Serial.read(); // Read any incoming and trash // Send version query. Wait up to 2000ms for a response - Serial.print("AT+V?\r\n"); + loraPrint("AT+V?\r\n"); for (int x = 0; x < 2000; x++) { if (Serial.available()) @@ -654,11 +733,11 @@ bool loraEnterCommandMode() } // No response so send +++ - Serial.print("+++\r\n"); + loraPrint("+++\r\n"); delay(100); // Allow STM32 time to enter command mode // Send version query. Wait up to 2000ms for a response - Serial.print("AT+V?\r\n"); + loraPrint("AT+V?\r\n"); for (int x = 0; x < 2000; x++) { if (Serial.available()) @@ -729,16 +808,79 @@ void loraProcessRTCM(uint8_t *rtcmData, uint16_t dataLength) { if (loraState == LORA_TX) { - // Send this data to the LoRa radio + // Only needed for Torch. Flex has GNSS tied directly to LoRa. + if (productVariant == RTK_TORCH) + { + // Send this data to the LoRa radio - systemFlush(); // Complete prints - muxSelectLoRa(); // Disconnect from USB + systemFlush(); // Complete prints + muxSelectLoRaCommunication(); // Connect to STM32 for regular TX/RX of corrections - Serial.write(rtcmData, dataLength); + loraWrite(rtcmData, dataLength); - systemFlush(); // Complete prints - muxSelectUsb(); // Connect USB + systemFlush(); // Complete prints + muxSelectUsb(); // Connect USB + } loraBytesSent += dataLength; } } + +// Write data to the LoRa radio, depends on platform +void loraWrite(uint8_t *data, uint16_t dataLength) +{ + if (productVariant == RTK_TORCH) + Serial.write(data, dataLength); + else if (productVariant == RTK_FLEX) + SerialForLoRa->write(data, dataLength); +} + +void loraPrint(const char *data) +{ + if (productVariant == RTK_TORCH) + Serial.print(data); + else if (productVariant == RTK_FLEX) + SerialForLoRa->print(data); +} + +void loraPrintf(const char *format, ...) +{ + va_list args; + va_start(args, format); + + va_list args2; + va_copy(args2, args); + char buf[vsnprintf(nullptr, 0, format, args) + 1]; + + vsnprintf(buf, sizeof buf, format, args2); + + if (productVariant == RTK_TORCH) + Serial.printf(buf); + else if (productVariant == RTK_FLEX) + SerialForLoRa->printf(buf); + + va_end(args); + va_end(args2); +} + +uint16_t loraAvailable() +{ + if (productVariant == RTK_TORCH) + return (Serial.available()); + else if (productVariant == RTK_FLEX) + return (SerialForLoRa->available()); + + systemPrintln("loraAvailable - invalid ProductVariant"); + return 0; +} + +uint16_t loraRead() +{ + if (productVariant == RTK_TORCH) + return (Serial.read()); + else if (productVariant == RTK_FLEX) + return (SerialForLoRa->read()); + + systemPrintln("loraRead - invalid ProductVariant"); + return 0; +} \ No newline at end of file diff --git a/Firmware/RTK_Everywhere/RTK_Everywhere.ino b/Firmware/RTK_Everywhere/RTK_Everywhere.ino index e7d4d767e..8519d3a85 100644 --- a/Firmware/RTK_Everywhere/RTK_Everywhere.ino +++ b/Firmware/RTK_Everywhere/RTK_Everywhere.ino @@ -285,14 +285,15 @@ int gpioExpander_left = 3; int gpioExpander_center = 4; int gpioExpander_cardDetect = 5; -int gpioExpanderSwitch_S1 = 0; // Controls U16 switch 1: connect ESP UART0 to CH342 or SW2 -int gpioExpanderSwitch_S2 = 1; // Controls U17 switch 2: connect SW1 to RS232 Output or GNSS UART4 -int gpioExpanderSwitch_S3 = 2; // Controls U18 switch 3: connect ESP UART2 to GNSS UART3 or LoRa UART2 -int gpioExpanderSwitch_S4 = 3; // Controls U19 switch 4: connect GNSS UART2 to 4-pin JST TTL Serial or LoRa UART0 -int gpioExpanderSwitch_LoraEnable = 4; // LoRa_EN -int gpioExpanderSwitch_GNSS_Reset = 5; // RST_GNSS -int gpioExpanderSwitch_LoraBoot = 6; // LoRa_BOOT0 - Used for bootloading the STM32 radio IC -int gpioExpanderSwitch_PowerFastOff = 7; // PWRKILL +const int gpioExpanderSwitch_S1 = 0; // Controls U16 switch 1: connect ESP UART0 to CH342 or SW2 +const int gpioExpanderSwitch_S2 = 1; // Controls U17 switch 2: connect SW1 to RS232 Output or GNSS UART4 +const int gpioExpanderSwitch_S3 = 2; // Controls U18 switch 3: connect ESP UART2 to GNSS UART3 or LoRa UART2 +const int gpioExpanderSwitch_S4 = 3; // Controls U19 switch 4: connect GNSS UART2 to 4-pin JST TTL Serial or LoRa UART0 +const int gpioExpanderSwitch_LoraEnable = 4; // LoRa_EN +const int gpioExpanderSwitch_GNSS_Reset = 5; // RST_GNSS +const int gpioExpanderSwitch_LoraBoot = 6; // LoRa_BOOT0 - Used for bootloading the STM32 radio IC +const int gpioExpanderSwitch_PowerFastOff = 7; // PWRKILL +const int gpioExpanderNumSwitches = 8; //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= @@ -588,6 +589,8 @@ volatile bool forwardGnssDataToUsbSerial; HardwareSerial *serialGNSS = nullptr; // Don't instantiate until we know what gnssPlatform we're on HardwareSerial *serial2GNSS = nullptr; // Don't instantiate until we know what gnssPlatform we're on +volatile bool inDirectConnectMode = false; // Global state to indicate if GNSS/LoRa has direct connection for update + #define SERIAL_SIZE_TX 512 uint8_t wBuffer[SERIAL_SIZE_TX]; // Buffer for writing from incoming SPP to F9P const int btReadTaskStackSize = 4000; @@ -943,7 +946,7 @@ unsigned long loraLastIncomingSerial; // Last time a user sent a serial command. // Display boot times //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -#define MAX_BOOT_TIME_ENTRIES 47 +#define MAX_BOOT_TIME_ENTRIES 50 uint8_t bootTimeIndex; uint32_t bootTime[MAX_BOOT_TIME_ENTRIES]; const char *bootTimeString[MAX_BOOT_TIME_ENTRIES]; @@ -1260,14 +1263,6 @@ void setup() DMW_b("tickerBegin"); tickerBegin(); // Start ticker tasks for LEDs and beeper - DMW_b("checkUpdateLoraFirmware"); - if (checkUpdateLoraFirmware() == true) // Check if updateLoraFirmware.txt exists - beginLoraFirmwareUpdate(); - - DMW_b("um980FirmwareCheckUpdate"); - if (um980FirmwareCheckUpdate() == true) // Check if updateUm980Firmware.txt exists - um980FirmwareBeginUpdate(); - DMW_b("beginPsram"); beginPsram(); // Initialize PSRAM (if available). Needs to occur before beginGnssUart and other malloc users. @@ -1314,6 +1309,18 @@ void setup() DMW_b("gnssDetectReceiverType"); gnssDetectReceiverType(); // If we don't know the receiver from the platform, auto-detect it. Uses settings. + DMW_b("checkUpdateLoraFirmware"); + if (checkUpdateLoraFirmware() == true) // Check if updateLoraFirmware.txt exists + beginLoraFirmwareUpdate(); // Needs I2C, GPIO Expander Switches, display, buttons, etc. + + DMW_b("um980FirmwareCheckUpdate"); + if (um980FirmwareCheckUpdate() == true) // UM980 needs special treatment + um980FirmwareBeginUpdate(); // Needs Flex GNSS, I2C, GPIO Expander Switches, display, buttons, etc. + + DMW_b("gnssFirmwareCheckUpdate"); + if (gnssFirmwareCheckUpdate() == true) // Check if updateGnssFirmware.txt exists + gnssFirmwareBeginUpdate(); // Needs Flex GNSS, I2C, GPIO Expander Switches, display, buttons, etc. + DMW_b("commandIndexFillActual"); commandIndexFillActual(); // Shrink the commandIndex table now we're certain what GNSS we have recordSystemSettings(); // Save the reduced settings now we're certain what GNSS we have diff --git a/Firmware/RTK_Everywhere/System.ino b/Firmware/RTK_Everywhere/System.ino index bf5e25add..5f91abffb 100644 --- a/Firmware/RTK_Everywhere/System.ino +++ b/Firmware/RTK_Everywhere/System.ino @@ -112,14 +112,24 @@ void beepOn() { // Disallow beeper if setting is turned off if ((pin_beeper != PIN_UNDEFINED) && (settings.enableBeeper == true)) - digitalWrite(pin_beeper, HIGH); + { + if (productVariant == RTK_TORCH) + digitalWrite(pin_beeper, HIGH); + else if (productVariant == RTK_FLEX) + tone(pin_beeper, 523); // NOTE_C5 + } } void beepOff() { // Disallow beeper if setting is turned off if ((pin_beeper != PIN_UNDEFINED) && (settings.enableBeeper == true)) - digitalWrite(pin_beeper, LOW); + { + if (productVariant == RTK_TORCH) + digitalWrite(pin_beeper, LOW); + else if (productVariant == RTK_FLEX) + noTone(pin_beeper); + } } // Only useful for pin_chargerLED on Facet mosaic @@ -896,15 +906,15 @@ void beginGpioExpanderSwitches() // SW1 is on pin 0. Driving it high will disconnect the ESP32 from USB // GNSS_RST is on pin 5. Driving it low when an LG290P is connected will kill the I2C bus. // PWRKILL is on pin 7. Driving it low will turn off the system - for (int i = 0; i < 8; i++) + for (int i = 0; i < gpioExpanderNumSwitches; i++) { - //Set all pins to low expect GNSS RESET and PWRKILL - if (i == 5 || i == 7) + // Set all pins to low except GNSS RESET and PWRKILL + if (i == gpioExpanderSwitch_GNSS_Reset || i == gpioExpanderSwitch_PowerFastOff) gpioExpanderSwitches->digitalWrite(i, HIGH); else gpioExpanderSwitches->digitalWrite(i, LOW); - gpioExpanderSwitches->pinMode(i, OUTPUT); + gpioExpanderSwitches->pinMode(i, OUTPUT); } online.gpioExpanderSwitches = true; @@ -941,8 +951,54 @@ void gpioExpanderSelectImu() gpioExpanderSwitches->digitalWrite(gpioExpanderSwitch_S3, LOW); } -void gpioExpanderSelectLora() +//Connect ESP32 UART2 to LoRa UART2 for configuration and bootloading/firmware updates +void gpioExpanderSelectLoraConfigure() { if (online.gpioExpanderSwitches == true) gpioExpanderSwitches->digitalWrite(gpioExpanderSwitch_S3, HIGH); +} + +// Connect Flex GNSS UART2 to LoRa UART0 for normal TX/RX of corrections and data +void gpioExpanderSelectLoraCommunication() +{ + if (online.gpioExpanderSwitches == true) + gpioExpanderSwitches->digitalWrite(gpioExpanderSwitch_S4, HIGH); +} + +// Connect Flex GNSS UART2 to 4-pin JST RADIO port +void gpioExpanderSelectRadioPort() +{ + if (online.gpioExpanderSwitches == true) + gpioExpanderSwitches->digitalWrite(gpioExpanderSwitch_S4, LOW); +} + +// Drive GPIO pin high to enable LoRa Radio +void gpioExpanderLoraEnable() +{ + if (online.gpioExpanderSwitches == true) + gpioExpanderSwitches->digitalWrite(gpioExpanderSwitch_LoraEnable, HIGH); +} +void gpioExpanderLoraDisable() +{ + if (online.gpioExpanderSwitches == true) + gpioExpanderSwitches->digitalWrite(gpioExpanderSwitch_LoraEnable, LOW); +} +bool gpioExpanderLoraIsOn() +{ + if (online.gpioExpanderSwitches == true) + { + if (gpioExpanderSwitches->digitalRead(gpioExpanderSwitch_LoraEnable) == HIGH) + return (true); + } + return(false); +} +void gpioExpanderLoraBootEnable() +{ + if (online.gpioExpanderSwitches == true) + gpioExpanderSwitches->digitalWrite(gpioExpanderSwitch_LoraBoot, HIGH); +} +void gpioExpanderLoraBootDisable() +{ + if (online.gpioExpanderSwitches == true) + gpioExpanderSwitches->digitalWrite(gpioExpanderSwitch_LoraBoot, LOW); } \ No newline at end of file diff --git a/Firmware/RTK_Everywhere/Tasks.ino b/Firmware/RTK_Everywhere/Tasks.ino index 12f7d2cbd..7da280da5 100644 --- a/Firmware/RTK_Everywhere/Tasks.ino +++ b/Firmware/RTK_Everywhere/Tasks.ino @@ -2035,7 +2035,35 @@ void buttonCheckTask(void *e) // End button checking - if (present.imu_im19 && (present.display_type == DISPLAY_MAX_NONE)) + // If in direct connect mode. Note: this is just a flag not a STATE. + if (inDirectConnectMode) + { + // TODO: check if this works on both Torch and Flex. Note: Flex does not yet support buttons + if (singleTap || doubleTap) + { + // Beep to indicate exit + beepOn(); + delay(300); + beepOff(); + delay(100); + beepOn(); + delay(300); + beepOff(); + + // Remove all the special files + removeUpdateLoraFirmware(); + um980FirmwareRemoveUpdate(); + gnssFirmwareRemoveUpdate(); + + systemPrintln("Exiting direct connection (passthrough) mode"); + systemFlush(); // Complete prints + + ESP.restart(); + } + } + // Torch is a special case. Handle tilt stop and web config mode + else if (productVariant == RTK_TORCH) + //else if (present.imu_im19 && (present.display_type == DISPLAY_MAX_NONE)) // TODO delete me { // Platform has no display and tilt corrections, ie RTK Torch diff --git a/Firmware/RTK_Everywhere/menuMessages.ino b/Firmware/RTK_Everywhere/menuMessages.ino index 01ddd69e9..f7d3a3495 100644 --- a/Firmware/RTK_Everywhere/menuMessages.ino +++ b/Firmware/RTK_Everywhere/menuMessages.ino @@ -3,8 +3,10 @@ void menuLogSelection() { if (present.microSd && !present.mosaicMicroSd) menuLog(); +#ifdef COMPILE_MOSAICX5 if (!present.microSd && present.mosaicMicroSd) menuLogMosaic(); +#endif if (present.microSd && present.mosaicMicroSd) { while (1) diff --git a/Firmware/RTK_Everywhere/menuSystem.ino b/Firmware/RTK_Everywhere/menuSystem.ino index b67bb52ff..bb0c7849d 100644 --- a/Firmware/RTK_Everywhere/menuSystem.ino +++ b/Firmware/RTK_Everywhere/menuSystem.ino @@ -462,8 +462,24 @@ void menuDebugHardware() systemPrint("12) Print Tilt/IMU Compensation Debugging: "); systemPrintf("%s\r\n", settings.enableImuCompensationDebug ? "Enabled" : "Disabled"); + // GNSS Firmware upgrades: + // On Torch: we need a direct connection (passthrough) from USB to CH342 B to + // ESP32 UART0 to ESP32 UART1 to UM980 UART3 for firmware upgrade. + // On Postcard: firmware can be updated over USB and the CH342 B connection to GNSS + // UART1. A hardware GNSS reset may be beneficial, but it is possible + // to reset over USB / UART too ($PQTMSRR*4B). + // On Flex: mosaic-X5 can be updated over USB via the USB Hub. + // A direct connection can be created from USB to USB Hub to CH342 B to + // ESP32 UART0 to ESP32 UART1 to GNSS UART1. + // ZED-X20P will need a direct connection. Update via USB is not possible. + // LG290P needs a direct connection. + // A future UM980 variant will also need a direct connection. + // Updates via the 4-pin JST RADIO connector and GNSS UART2 may also be possible. + if (present.gnss_um980) - systemPrintln("13) UM980 direct connect"); + systemPrintln("13) UM980 direct connect for firmware upgrade"); + else if ((productVariant == RTK_FLEX) && (present.gnss_lg290p || present.gnss_zedx20p)) + systemPrintln("13) GNSS direct connect for firmware update"); else if (present.gnss_lg290p) systemPrintln("13) LG290P reset for firmware update"); @@ -488,8 +504,15 @@ void menuDebugHardware() systemPrint("16) Print LoRa Debugging: "); systemPrintf("%s\r\n", settings.debugLora ? "Enabled" : "Disabled"); + // LoRa Firmware upgrades: + // On Torch: we need a direct connection from USB to CH342 B to ESP32 UART0 to + // ESP32 UART1 to LoRa UART0. + // On Flex: we need a direct connection from USB to USB Hub to ESP32 UART0 to + // ESP32 UART2 to LoRa UART2. + // TODO: check STM32 can be updated via UART2!! + if (present.radio_lora) - systemPrintln("17) STM32 direct connect"); + systemPrintln("17) STM32 direct connect for LoRa firmware upgrade"); systemPrintln("18) Display littleFS stats"); @@ -549,6 +572,18 @@ void menuDebugHardware() ESP.restart(); } } + else if ((productVariant == RTK_FLEX) && (present.gnss_lg290p || present.gnss_zedx20p)) + { + // Create a file in LittleFS + if (createGNSSPassthrough() == true) + { + systemPrintln(); + systemPrintln("GNSS passthrough mode has been recorded to LittleFS. Device will now reset."); + systemFlush(); // Complete prints + + ESP.restart(); + } + } else if (present.gnss_lg290p) { systemPrintln(); diff --git a/Firmware/RTK_Everywhere/settings.h b/Firmware/RTK_Everywhere/settings.h index c4b1c1556..516899eb3 100644 --- a/Firmware/RTK_Everywhere/settings.h +++ b/Firmware/RTK_Everywhere/settings.h @@ -1861,6 +1861,7 @@ struct struct_present bool gnss_zedf9p = false; bool gnss_mosaicX5 = false; // L-Band is implicit bool gnss_lg290p = false; + bool gnss_zedx20p = false; // A GNSS TP interrupt - for accurate clock setting // The GNSS UBX PVT message is sent ahead of the top-of-second