[ld2450] Add frame header synchronization to readline_()#14135
[ld2450] Add frame header synchronization to readline_()#14135swoboda1337 merged 2 commits intoesphome:devfrom
Conversation
|
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#14135
components: [ld2450]
refresh: 1h(Added by the PR bot) |
|
👋 Hi there! This PR modifies 2 file(s) with codeowners. @hareeshmu - As codeowner(s) of the affected files, your review would be appreciated! 🙏 Note: Automatic review request may have failed, but you're still welcome to review. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## dev #14135 +/- ##
=======================================
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:
|
There was a problem hiding this comment.
Pull request overview
This PR fixes a critical regression in ESPHome 2026.2.0 where LD2450 radar devices fail to initialize due to UART frame synchronization issues. The root cause is that the UART parser lacks header validation, causing it to accumulate mid-frame garbage data indefinitely when starting to read mid-stream (e.g., after module restart).
Changes:
- Added byte-by-byte frame header synchronization to validate that incoming data starts with a valid DATA_FRAME_HEADER (AA FF 03 00) or CMD_FRAME_HEADER (FD FC FB FA)
- Added explicit
returnafter buffer overflow to prevent processing stale data - Added missing
#include "esphome/core/automation.h"for Trigger<> base class
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| esphome/components/ld2450/ld2450.h | Added missing include for automation.h (needed for Trigger<> template) |
| esphome/components/ld2450/ld2450.cpp | Implemented frame header synchronization in readline_(), added return after overflow, replaced magic number with HEADER_FOOTER_SIZE constant |
| tests/components/ld2450/common.h | Added test infrastructure with MockUARTComponent, TestableLD2450 wrapper, and frame builder helpers |
| tests/components/ld2450/ld2450_readline.cpp | Added 14 comprehensive unit tests covering normal parsing, garbage rejection, header sync, overflow recovery, and restart scenarios |
…ssion The batch UART read change in esphome#13818 exposed a latent bug where the UART parser could start accumulating bytes mid-frame after module restart, causing an infinite cycle of "Max command length exceeded" warnings that prevented initialization. Add header validation for the first 4 bytes of each frame, ensuring the parser only accumulates data starting from a valid DATA_FRAME_HEADER (AA FF 03 00) or CMD_FRAME_HEADER (FD FC FB FA). Non-matching bytes are discarded until a valid frame start is found. Fixes esphome#14131 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3dcaa55 to
4ecf81a
Compare
|
Thanks |
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 ld2410. The fix validates the first 4 bytes of each frame match a known header (CMD or ENERGY) before accumulating data. In simple mode, frames are text lines without binary headers, so the check is skipped. 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>
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>
…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>
…ssion (#14135) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
What does this implement/fix?
After upgrading to ESPHome 2026.2.0, LD2450 radar devices fail to initialize and continuously log
Max command length exceeded; ignoring. This is caused by the UART parser lacking frame header synchronization — when it starts reading mid-frame (e.g. after the LD2450 module restarts duringsetup()), accumulated bytes never form a valid frame, the buffer overflows, resets, and the cycle repeats indefinitely.This PR adds header validation for the first 4 bytes of each frame in
readline_(). The parser now only starts accumulating data when it sees a validDATA_FRAME_HEADER(AA FF 03 00) orCMD_FRAME_HEADER(FD FC FB FA), ensuring it always begins at a frame boundary. If a header mismatch occurs mid-header, the parser resets and checks if the mismatched byte could start a new frame.Additionally:
returnafter buffer overflow reset to avoid processing stale data4with existingHEADER_FOOTER_SIZEconstant#include "esphome/core/automation.h"inld2450.h(needed forTrigger<>base class)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: