fix(Serial): Uart::write() must not spin when begin() failed - #3074
Merged
fpistm merged 1 commit intoSep 8, 2026
Merged
Conversation
fpistm
requested changes
Sep 8, 2026
fpistm
left a comment
Member
There was a problem hiding this comment.
Hi @saikumar-mandaji
Thanks for the PR. Makes sense.
Only one comment to address.
If Serial.begin(baud) fails during hardware init (e.g. an LPUART
clocked from LSE can't reach the requested baud rate), uart_init()
returns false and _ready is set to false -- but Uart::write() never
checks _ready before entering its transmit path.
Uart::write(const uint8_t*, size_t) contains:
while (!availableForWrite()) {
// nop, the interrupt handler will free up space for us
}
Since the hardware was never actually brought up, the TX interrupt
this loop waits on never fires, so once the 63-byte TX ring buffer
fills up (a few Serial.print() calls after a failed begin()), this
spins forever and the MCU deadlocks permanently.
Fix: return 0 immediately if !_ready, before touching the buffer or
entering the wait loop, using the same _ready accessor already used
elsewhere in this file (see Uart::begin(), which sets it, and the
existing operator bool()-style accessor in Serial.h). write(uint8_t)
needs no separate guard since it already delegates to this overload.
Fixes stm32duino#3071
fpistm
force-pushed
the
fix-uart-write-deadlock-when-not-ready
branch
from
September 8, 2026 08:55
41e8ca7 to
955c68b
Compare
fpistm
approved these changes
Sep 8, 2026
|
Thank you so much for the fix, I will test with Meshtastic very soon! |
13 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bug
If
Serial.begin(baud)fails during hardware init (e.g. an LPUART clocked from LSE can't reach the requested baud rate),uart_init()returnsfalseand_readyis set tofalse— butUart::write()never checks_readybefore entering its transmit path.Uart::write(const uint8_t*, size_t)contains:Since the hardware was never actually brought up, the TX interrupt this loop waits on never fires, so once the 63-byte TX ring buffer fills up (a few
Serial.print()calls after a failedbegin()), this spins forever and the MCU deadlocks permanently. See #3071 for the full repro (STM32WLE5 LPUART1 on LSE, GPS driver baud-probing 9600 -> 38400 -> deadlock).Fix
Return
0immediately if!_ready, before touching the buffer or entering the wait loop — this is exactly the guard suggested in #3071.write(uint8_t)needs no separate guard since it already delegates to this overload.Verification
arm-none-eabi-g++ -fsyntax-onlyagainst the actual STM32WLxx variant/CMSIS/HAL headers in this repo, but this checkout is missing the CMSIS-Core headers (core_cm4.hetc.) that a full Arduino IDE/arduino-cli install would normally fetch separately — they aren't vendored in the git tree itself, so a from-scratch clone can't be fully compiled without that tooling. I'm disclosing this honestly rather than claiming a green compile I didn't actually get._readymember (already set inUart::begin()and already exposed via an accessor inSerial.h, both in the same file/class), so the risk profile is low even without a full build.Fixes #3071