Skip to content

Platform upgrade#3341

Merged
pkendall64 merged 8 commits intoExpressLRS:masterfrom
pkendall64:next/platform-upgrade
Oct 17, 2025
Merged

Platform upgrade#3341
pkendall64 merged 8 commits intoExpressLRS:masterfrom
pkendall64:next/platform-upgrade

Conversation

@pkendall64
Copy link
Collaborator

Upgrade the platform base platform and libraries.

  • ESP32 Platform upgrade harmonises RGB LED handling
  • TCP/WebServer upgrade to the newly maintained version
  • Upgraded JSON library for wifi and hardware/options loading
  • Upgrade BLE joystick & NimBLE for more stable joystick
  • OLED/TFT updates to current versions

@pkendall64 pkendall64 added V4.0 🍔 house keeping 🧹 Cleanup of code and house keeping labels Sep 21, 2025
@CapnBry CapnBry self-assigned this Oct 12, 2025
Copy link
Member

@CapnBry CapnBry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The updates to AsyncWebServer seem to change the behavior of the `request->client()->close()" which doesn't block until all the data is sent, it just drops the connection. This leads to errors in firmware upload which will cause errors in Configurator:

100 1667k    0     0  100 1667k      0   119k  0:00:13  0:00:13 --:--:-- 86509
curl: (52) Empty reply from server
*** [upload] .pio\build\Unified_ESP32_LR1121_TX_via_WIFI\firmware-output.json: No such file or directory

Also confirmed with these on ESP32, no response is generated:

  • /reboot
  • /reset

I believe there is no way to flush the connection without using onAck() handlers but I think just the standard waits are fine, just remove the ->close() lines everywhere

Not related to your changes but can we also fix this uninitialized warning in the MAVlink code? We could be sending some garbage in our mavlink data for this

--- a/src/lib/MAVLink/MAVLink.cpp
+++ b/src/lib/MAVLink/MAVLink.cpp
@@ -306,7 +306,7 @@ bool isThisAMavPacket(uint8_t *buffer, uint16_t bufferSize)
 uint16_t buildMAVLinkELRSModeChange(uint8_t mode, uint8_t *buffer)
 {
     constexpr uint8_t ELRS_MODE_CHANGE = 0x8;
-    mavlink_command_int_t commandMsg;
+    mavlink_command_int_t commandMsg{};
     commandMsg.target_system = 255;
     commandMsg.target_component = MAV_COMP_ID_UDP_BRIDGE;
     commandMsg.command = MAV_CMD_USER_1;

Finally a major issue: DEBUG_LOG on TX is broken after this. The logging works right up to the point the CRSFHandset::begin() where it opens the port, and the logging just stops. I'm looking into this but I am not sure where the issue is. If we dig into HardwareSerial, it stops on the line:

_uart = uartBegin(_uart_nr, baud ? baud : 9600, config, rxPin, txPin, _rxBufferSize, _txBufferSize, invert, rxfifo_full_thrhd);

I've also tried compiling with NO_GLOBAL_SERIAL so it doesn't create the Serial objects and it still fails. uart_is_driver_installed(0) returns 0, nothing installed. I am guessing it is in esp32-hal-uart.c where it might be calling _uartDetachPins() on debug pins 1 and 3? EDIT: Yup, somehow UART0 in the underlying bits has pins assigned to 3 and 1 and therefore when we try to start HardwareSerial(0), it detaches the pins and breaks the debug log.

@CapnBry
Copy link
Member

CapnBry commented Oct 13, 2025

Oh can you also rebase this or merge master?

@CapnBry
Copy link
Member

CapnBry commented Oct 13, 2025

Alright the issue appears to be this stupid constructor code

HardwareSerial::HardwareSerial(uint8_t uart_nr) :
_uart_nr(uart_nr),
_uart(NULL),
_rxBufferSize(256),
_txBufferSize(0),
_onReceiveCB(NULL),
_onReceiveErrorCB(NULL),
_onReceiveTimeout(false),
_rxTimeout(2),
_rxFIFOFull(0),
_eventTask(NULL)
#if !CONFIG_DISABLE_HAL_LOCKS
    ,_lock(NULL)
#endif
{
#if !CONFIG_DISABLE_HAL_LOCKS
    if(_lock == NULL){
        _lock = xSemaphoreCreateMutex();
        if(_lock == NULL){
            log_e("xSemaphoreCreateMutex failed");
            return;
        }
    }
#endif
    // do the same as boot time, that will set default UART0 pins RX, TX.
    if(uart_nr == 0) uartSetPins(0, SOC_RX0, SOC_TX0, -1, -1);
}

This means on construction of any HardwareSerial(0), the pins are set in the internal static uart structure so any HardwareSerial(0)::begin() after that will turn them off, even if begin() was never called on them previously. Not only does Arduino allocate HardwareSerial Serial(0), but our CRSFHandset uses a static member Port(0) that's constructed against UART0 as well which will trigger this code. Bonus code, you can't even call uartSetPins(0, -1, -1, -1, -1); to "unclaim" them because their code takes -1 to both mean "do not initialize" and "do not change". This code has been removed in 2023

The only workaround I can come up with is to do something dumb like this (although it needs more junk around it to protect things like ESP32_S3?). HardwareSerial.end() always detaches its pins even if begin was never called. We might also need this for some target RXes.

--- a/src/src/tx_main.cpp
+++ b/src/src/tx_main.cpp
@@ -1282,6 +1282,7 @@ static void setupSerial()
   }
   else if (GPIO_PIN_DEBUG_RX != UNDEF_PIN && GPIO_PIN_DEBUG_TX != UNDEF_PIN)
   {
+    Serial.end();
     serialPort = new HardwareSerial(2);
     ((HardwareSerial *)serialPort)->begin(BACKPACK_LOGGING_BAUD, SERIAL_8N1, GPIO_PIN_DEBUG_RX, GPIO_PIN_DEBUG_TX);
   }

Suggestions?

@pkendall64 pkendall64 force-pushed the next/platform-upgrade branch from 36c25a4 to e776a60 Compare October 14, 2025 02:42
@pkendall64
Copy link
Collaborator Author

This last one is rather problematic. looking over the HardwareSerial code from the ESP32 implementation it looks like calling Serial.end, potentially for all the HardwareSerial's at the start of our main setup function may work for us. That way all the pins will be set to -1. Which is basically what you did anyway 😅

@pkendall64 pkendall64 requested a review from CapnBry October 14, 2025 19:05
Copy link
Member

@CapnBry CapnBry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Golly, another day of digging into what's not working only to find it is just the RED_LED and my flashing procedure not working.

All looks good apart from Serial.end()s. Tested with ESP32 TX, ESP8285 RXasTX, ESP8285 RX, LR1121 ESP32 RX, LR1121 ESP32 TX, ESP32C3 LR1121 RX

@pkendall64 pkendall64 requested a review from CapnBry October 16, 2025 18:37
Copy link
Member

@CapnBry CapnBry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I told ya! 😆 I'm just glad Serial.end() calls it without even checking if you called begin(). It could have been even more hacky! They changed this a while back to use their newish peripheral manager, and then later removed the code from the constructor entirely so at some point that will maybe filter down into the version platformio uses and we can remove it.

Looks good though, thanks for bringing everything up to date.

@pkendall64 pkendall64 merged commit c5469a3 into ExpressLRS:master Oct 17, 2025
25 checks passed
@pkendall64 pkendall64 deleted the next/platform-upgrade branch October 17, 2025 18:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

house keeping 🧹 Cleanup of code and house keeping V4.0 🍔

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants