[ld2410] Add frame header synchronization to readline_()#14136
[ld2410] Add frame header synchronization to readline_()#14136swoboda1337 merged 1 commit intoesphome:devfrom
Conversation
Add frame header validation to prevent the parser from getting stuck in an overflow loop when it loses sync with the UART byte stream (e.g. after module restart or UART noise). This is the same latent bug fixed in ld2450 (PR esphome#14135) and present in ld2420. The fix validates the first 4 bytes of each frame match a known header (DATA or CMD) before accumulating data. On header byte mismatch, the buffer resets and checks if the mismatched byte starts a new frame. Also adds a return after buffer overflow reset to prevent immediately re-accumulating the overflowed byte. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
To use the changes from this PR as an external component, add the following to your ESPHome configuration YAML file: external_components:
- source: github://pr#14136
components: [ld2410]
refresh: 1h(Added by the PR bot) |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## dev #14136 +/- ##
=======================================
Coverage 74.11% 74.11%
=======================================
Files 55 55
Lines 11590 11590
Branches 1578 1578
=======================================
Hits 8590 8590
Misses 2598 2598
Partials 402 402 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
[21:01:09.812][D][ld2410:208]: LD2410 Sun Light: 7% 👍 |
There was a problem hiding this comment.
Pull request overview
Adds UART frame header synchronization to the LD2410 byte-stream parser to prevent endless buffer overflows/log spam when the parser starts mid-frame (e.g., restart/noise), improving initialization robustness.
Changes:
- Validate the first 4 bytes of an incoming frame against known DATA/CMD headers before accumulating the frame.
- Discard non-header bytes to re-synchronize on valid frame boundaries.
- Return immediately after buffer overflow reset to avoid continuing processing in the overflow path.
Comments suppressed due to low confidence (1)
esphome/components/ld2410/ld2410.cpp:640
HEADER_FOOTER_SIZEwas introduced/used for header handling, but the footer checks still use the literal- 4when indexing intobuffer_data_. For consistency and to avoid future mistakes if the header/footer size changes, useHEADER_FOOTER_SIZEin those index calculations as well (e.g.,buffer_pos_ - HEADER_FOOTER_SIZE).
if (ld2410::validate_header_footer(DATA_FRAME_FOOTER, &this->buffer_data_[this->buffer_pos_ - 4])) {
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
char hex_buf[format_hex_pretty_size(MAX_LINE_LENGTH)];
ESP_LOGV(TAG, "Handling Periodic Data: %s", format_hex_pretty_to(hex_buf, this->buffer_data_, this->buffer_pos_));
#endif
this->handle_periodic_data_();
this->buffer_pos_ = 0; // Reset position index for next message
} else if (ld2410::validate_header_footer(CMD_FRAME_FOOTER, &this->buffer_data_[this->buffer_pos_ - 4])) {
|
Thanks |
…me resync Reverts the frame header synchronization added in esphome#14135 and esphome#14136 in favor of a simpler fix: increasing MAX_LINE_LENGTH so that the existing footer-based resynchronization can recover after losing sync. Both components already check for frame footers at every byte position, which naturally resyncs the parser. The problem was that the buffers were sized exactly to fit the largest frame, so a desynced parser's footer could land at the overflow boundary and get discarded. Increasing the buffer by 4 bytes (footer size) ensures the footer always lands inside the buffer. - ld2450: 41 -> 45 (zone query response = 40 bytes + 1 null + 4 footer) - ld2410: 46 -> 50 (engineering data frame = 45 bytes + 1 null + 4 footer) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…me resync Reverts the frame header synchronization added in esphome#14135 and esphome#14136 in favor of a simpler fix: increasing MAX_LINE_LENGTH so that the existing footer-based resynchronization can recover after losing sync. Both components already check for frame footers at every byte position, which naturally resyncs the parser. The problem was that the buffers were sized exactly to fit the largest frame, so a desynced parser's footer could land at the overflow boundary and get discarded. Increasing the buffer by 4 bytes (footer size) ensures the footer always lands inside the buffer. - ld2450: 41 -> 45 (zone query response = 40 bytes + 1 null + 4 footer) - ld2410: 46 -> 50 (engineering data frame = 45 bytes + 1 null + 4 footer) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…me resync Reverts the frame header synchronization added in esphome#14135 and esphome#14136 in favor of a simpler fix: increasing MAX_LINE_LENGTH so that the existing footer-based resynchronization can recover after losing sync. Both components already check for frame footers at every byte position, which naturally resyncs the parser. The problem was that the buffers were sized exactly to fit the largest frame, so a desynced parser's footer could land at the overflow boundary and get discarded. Increasing the buffer by 4 bytes (footer size) ensures the footer always lands inside the buffer. - ld2450: 41 -> 45 (zone query response = 40 bytes + 1 null + 4 footer) - ld2410: 46 -> 50 (engineering data frame = 45 bytes + 1 null + 4 footer) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…me resync Reverts the frame header synchronization added in esphome#14135 and esphome#14136 in favor of a simpler fix: increasing MAX_LINE_LENGTH so that the existing footer-based resynchronization can recover after losing sync. Both components already check for frame footers at every byte position, which naturally resyncs the parser. The problem was that the buffers were sized exactly to fit the largest frame, so a desynced parser's footer could land at the overflow boundary and get discarded. Increasing the buffer by 4 bytes (footer size) ensures the footer always lands inside the buffer. - ld2450: 41 -> 45 (zone query response = 40 bytes + 1 null + 4 footer) - ld2410: 46 -> 50 (engineering data frame = 45 bytes + 1 null + 4 footer) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…me resync Reverts the frame header synchronization added in esphome#14135 and esphome#14136 in favor of a simpler fix: increasing MAX_LINE_LENGTH so that the existing footer-based resynchronization can recover after losing sync. Both components already check for frame footers at every byte position, which naturally resyncs the parser. The problem was that the buffers were sized exactly to fit the largest frame, so a desynced parser's footer could land at the overflow boundary and get discarded. Increasing the buffer by 4 bytes (footer size) ensures the footer always lands inside the buffer. - ld2450: 41 -> 45 (zone query response = 40 bytes + 1 null + 4 footer) - ld2410: 46 -> 50 (engineering data frame = 45 bytes + 1 null + 4 footer) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
What does this implement/fix?
Adds frame header synchronization to
readline_()to prevent the parser from getting stuck in an infinite overflow loop when it loses sync with the UART byte stream.This is the same latent bug as fixed in LD2450 (#14135). Without header validation, if the parser starts reading mid-frame (e.g. after module restart during
setup(), or due to UART noise), the accumulated bytes never form a valid frame. The buffer overflows, resets to position 0, and immediately starts accumulating the next byte — which is still mid-frame garbage. This cycle repeats indefinitely, producing "Max command length exceeded; ignoring" log spam and preventing initialization.The fix validates the first 4 bytes of each frame match a known header (DATA
F4 F3 F2 F1or CMDFD FC FB FA) before accumulating data. Non-header bytes are discarded. On header byte mismatch mid-header, the buffer resets and checks if the mismatched byte starts a new frame. Also adds areturnafter buffer overflow reset to prevent immediately re-accumulating the overflowed byte.Types of changes
Related issue or feature (if applicable):
Pull request in esphome-docs with documentation (if applicable):
Test Environment
Example entry for
config.yaml:Checklist:
tests/folder).If user exposed functionality or configuration variables are added/changed: