This took me a few days of tinkering to understand. Was trying to use RS232 between a Rak 1w board and a Heltec WSLv3 but it wasn't working.
Confirmed the WSL could hear and talk RS232 with a external USB-UART adapter but the Rak 3401 / 19007 wasn't listening or talking. Now everything seems to work.
The rest is entirely Claude generated.
I will leave a human to read / submit this as this is way over my head.
Fix: RS232 bridge transmits at wrong baud on nRF52 when the bridge UART is opened earlier in boot
Summary
On nRF52 boards, RS232Bridge::begin() calls Uart::begin(bridge_baud) to configure
the bridge serial port. If that same Uart object (e.g. Serial1) was already opened
earlier in the boot sequence at a different baud rate, the Adafruit nRF52 core silently
ignores the bridge's begin() call, and the port keeps transmitting at the earlier baud.
The result: the bridge appears to work (pins are configured, BRIDGE: TX logs fire,
bytes physically leave the TX pin), but the peripheral is clocking them out at the wrong
rate, so nothing on the other end can decode them.
Affected hardware
Any nRF52 variant where the bridge UART is brought up before RS232Bridge::begin() runs.
Confirmed on the RAK3401 (WisMesh 1W), whose variant.h aliases Serial1 to the GPS
UART:
#define PIN_GPS_RX PIN_SERIAL1_RX
#define PIN_GPS_TX PIN_SERIAL1_TX
#define GPS_BAUD_RATE 9600
Serial1 is opened at 9600 during board/GPS bring-up, so the bridge's later
begin(115200) is discarded. RAK4631 / Heltec T114 are unaffected because they do not
pre-open the bridge UART.
Root cause
Uart::begin() in the Adafruit nRF52 core guards against re-initialization:
void Uart::begin(unsigned long baudrate, uint16_t config) {
if ( _begun ) return; // <-- silently no-ops if already begun
...
// BAUDRATE register is only written past this guard
}
Because setPins() writes the PSEL registers directly (outside the guard), the pins
end up correct while the BAUDRATE register retains its earlier value — which is exactly
why the failure is so hard to see: every register looks right except the baud.
Diagnosis (register-level evidence)
Reading the raw UARTE registers on a RAK3401 bridge build, before any fix:
[BEFORE setPins] UARTE0 ENABLE=ON BAUD=0x00275000 (=9600) TXD->P0.15 RXD->P0.16
[AFTER begin] UARTE0 ENABLE=ON BAUD=0x00275000 (=9600) <-- begin(115200) had no effect
get bridge.baud correctly reported 115200 (the pref was right); the value simply never
reached the peripheral. After the fix:
[AFTER end()] UARTE0 ENABLE=off DISCONNECTED
[AFTER begin] UARTE0 ENABLE=ON BAUD=0x01D60000 (115200) TXD->P0.16 RXD->P0.15
Bytes then decode correctly on the far end (verified end-to-end: adverts from a peer node
were received and parsed over the bridge).
Fix
Call end() before begin() in the nRF52 branch of RS232Bridge::begin() to clear the
_begun guard, then re-apply pins (since end() disconnects PSEL) and begin at the
bridge baud:
#elif defined(NRF52_PLATFORM)
((Uart *)_serial)->end();
((Uart *)_serial)->setPins(WITH_RS232_BRIDGE_RX, WITH_RS232_BRIDGE_TX);
((Uart *)_serial)->begin(_prefs->bridge_baud);
end() on a not-yet-begun port is a harmless no-op, so this is safe for boards that
were already working (RAK4631, T114) — they simply end() an unopened port and proceed
as before. No behavior change on ESP32 / RP2040 / STM32 branches.
Testing
- RAK3401 (WisMesh 1W) bridged to a Heltec Wireless Stick Lite V3 over RS232 (J10 UART1,
pins P0.15/P0.16). Before: BRIDGE: TX logged but far end saw only garbled bytes.
After: self-test text and real mesh packets decode cleanly at 115200.
- Verified the UARTE
BAUDRATE register now reflects the configured bridge baud.
This took me a few days of tinkering to understand. Was trying to use RS232 between a Rak 1w board and a Heltec WSLv3 but it wasn't working.
Confirmed the WSL could hear and talk RS232 with a external USB-UART adapter but the Rak 3401 / 19007 wasn't listening or talking. Now everything seems to work.
The rest is entirely Claude generated.
I will leave a human to read / submit this as this is way over my head.
Fix: RS232 bridge transmits at wrong baud on nRF52 when the bridge UART is opened earlier in boot
Summary
On nRF52 boards,
RS232Bridge::begin()callsUart::begin(bridge_baud)to configurethe bridge serial port. If that same
Uartobject (e.g.Serial1) was already openedearlier in the boot sequence at a different baud rate, the Adafruit nRF52 core silently
ignores the bridge's
begin()call, and the port keeps transmitting at the earlier baud.The result: the bridge appears to work (pins are configured,
BRIDGE: TXlogs fire,bytes physically leave the TX pin), but the peripheral is clocking them out at the wrong
rate, so nothing on the other end can decode them.
Affected hardware
Any nRF52 variant where the bridge UART is brought up before
RS232Bridge::begin()runs.Confirmed on the RAK3401 (WisMesh 1W), whose
variant.haliasesSerial1to the GPSUART:
Serial1is opened at 9600 during board/GPS bring-up, so the bridge's laterbegin(115200)is discarded. RAK4631 / Heltec T114 are unaffected because they do notpre-open the bridge UART.
Root cause
Uart::begin()in the Adafruit nRF52 core guards against re-initialization:Because
setPins()writes thePSELregisters directly (outside the guard), the pinsend up correct while the
BAUDRATEregister retains its earlier value — which is exactlywhy the failure is so hard to see: every register looks right except the baud.
Diagnosis (register-level evidence)
Reading the raw UARTE registers on a RAK3401 bridge build, before any fix:
get bridge.baudcorrectly reported115200(the pref was right); the value simply neverreached the peripheral. After the fix:
Bytes then decode correctly on the far end (verified end-to-end: adverts from a peer node
were received and parsed over the bridge).
Fix
Call
end()beforebegin()in the nRF52 branch ofRS232Bridge::begin()to clear the_begunguard, then re-apply pins (sinceend()disconnectsPSEL) and begin at thebridge baud:
end()on a not-yet-begun port is a harmless no-op, so this is safe for boards thatwere already working (RAK4631, T114) — they simply end() an unopened port and proceed
as before. No behavior change on ESP32 / RP2040 / STM32 branches.
Testing
pins P0.15/P0.16). Before:
BRIDGE: TXlogged but far end saw only garbled bytes.After: self-test text and real mesh packets decode cleanly at 115200.
BAUDRATEregister now reflects the configured bridge baud.