mirror of
https://github.com/esphome/esphome.git
synced 2026-10-22 05:46:12 +00:00
[ld2460] Preserve buffered frames across overflow (#21123)
This commit is contained in:
@@ -37,6 +37,22 @@ static bool matches(const uint8_t *bytes, const uint8_t *pattern) {
|
||||
static constexpr uint8_t FRAME_PREFIX_SIZE = 7;
|
||||
static uint16_t frame_length(const uint8_t *frame) { return encode_uint16(frame[6], frame[5]); }
|
||||
|
||||
// First offset after 0 whose bytes, as far as they go, match a header; len when there is none (len >= 1)
|
||||
static uint8_t next_header_start(const uint8_t *buffer, uint8_t len) {
|
||||
uint8_t data_header[HEADER_FOOTER_SIZE];
|
||||
uint8_t cmd_header[HEADER_FOOTER_SIZE];
|
||||
progmem_memcpy(data_header, DATA_FRAME_HEADER, sizeof(data_header));
|
||||
progmem_memcpy(cmd_header, CMD_FRAME_HEADER, sizeof(cmd_header));
|
||||
uint8_t start = 1;
|
||||
for (; start < len; start++) {
|
||||
const size_t n = std::min<size_t>(len - start, HEADER_FOOTER_SIZE);
|
||||
if (std::memcmp(&buffer[start], data_header, n) == 0 || std::memcmp(&buffer[start], cmd_header, n) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
static constexpr float RADIANS_TO_DEGREES = 180.0f / std::numbers::pi_v<float>;
|
||||
|
||||
void LD2460Component::setup() { this->send_query_(); }
|
||||
@@ -155,8 +171,17 @@ void LD2460Component::handle_periodic_data_() {
|
||||
|
||||
void LD2460Component::readline_(uint8_t readch) {
|
||||
if (this->buffer_pos_ >= MAX_LINE_LENGTH) {
|
||||
ESP_LOGW(TAG, "Max frame length exceeded; ignoring");
|
||||
this->buffer_pos_ = 0;
|
||||
// Keep the bytes from the next offset that could start a header, so a frame already partly
|
||||
// buffered behind stray bytes survives; it is still fully validated once its footer arrives
|
||||
const uint8_t start = next_header_start(this->buffer_data_, this->buffer_pos_);
|
||||
this->buffer_pos_ -= start;
|
||||
std::memmove(this->buffer_data_, &this->buffer_data_[start], this->buffer_pos_);
|
||||
// Header-shaped noise keeps a whole header; warning only otherwise bounds the warning rate
|
||||
if (this->buffer_pos_ < HEADER_FOOTER_SIZE) {
|
||||
ESP_LOGW(TAG, "Max frame length exceeded; ignoring");
|
||||
} else {
|
||||
ESP_LOGV(TAG, "Max frame length exceeded; kept %u bytes", this->buffer_pos_);
|
||||
}
|
||||
}
|
||||
this->buffer_data_[this->buffer_pos_++] = readch;
|
||||
|
||||
|
||||
@@ -180,4 +180,43 @@ TEST_F(LD2460ReadlineTest, DataCallbackOncePerValidFrame) {
|
||||
EXPECT_EQ(calls, 2);
|
||||
}
|
||||
|
||||
TEST_F(LD2460ReadlineTest, TruncatedPrefixBeforeLargeFrames) {
|
||||
// Three targets still fit behind the truncated prefix; four and five overflow the buffer
|
||||
const std::vector<Point> targets = {{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}};
|
||||
for (int n = 3; n <= MAX_TARGETS; n++) {
|
||||
SCOPED_TRACE(n);
|
||||
this->ld2460_.feed({0x00, 0xF4, 0xF3, 0xF2, 0xF1, 0x04});
|
||||
this->ld2460_.feed(make_periodic_frame(std::vector<Point>(targets.begin(), targets.begin() + n)));
|
||||
EXPECT_FLOAT_EQ(this->count_.state, n);
|
||||
EXPECT_NEAR(this->x_[n - 1].state, (2 * n - 1) * 0.1f, 0.001f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(LD2460ReadlineTest, LostFooterTwoTargetResync) {
|
||||
// A two-target frame missing its last byte; the next frame overflows the buffer
|
||||
auto broken = make_periodic_frame({{10, 10}, {20, 20}});
|
||||
broken.pop_back();
|
||||
this->ld2460_.feed(broken);
|
||||
this->ld2460_.feed(make_periodic_frame({{30, 40}, {50, 60}}));
|
||||
EXPECT_FLOAT_EQ(this->count_.state, 2);
|
||||
EXPECT_NEAR(this->x_[1].state, 5.0f, 0.001f);
|
||||
this->ld2460_.feed(make_periodic_frame({{70, 80}}));
|
||||
EXPECT_FLOAT_EQ(this->count_.state, 1);
|
||||
EXPECT_NEAR(this->x_[0].state, 7.0f, 0.001f);
|
||||
}
|
||||
|
||||
TEST_F(LD2460ReadlineTest, HeaderLikeGarbageThenMaxTargets) {
|
||||
// Header first bytes and whole command headers in the noise do not hold back a real frame
|
||||
this->ld2460_.feed(std::vector<uint8_t>(70, 0xF4));
|
||||
this->ld2460_.feed(make_periodic_frame({{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}}));
|
||||
EXPECT_FLOAT_EQ(this->count_.state, 5);
|
||||
std::vector<uint8_t> garbage;
|
||||
for (int i = 0; i < 40; i++) {
|
||||
garbage.insert(garbage.end(), {0xFD, 0xFC, 0xFB, 0xFA});
|
||||
}
|
||||
this->ld2460_.feed(garbage);
|
||||
this->ld2460_.feed(make_periodic_frame({{11, 12}, {3, 4}, {5, 6}, {7, 8}, {9, 10}}));
|
||||
EXPECT_NEAR(this->x_[0].state, 1.1f, 0.001f);
|
||||
}
|
||||
|
||||
} // namespace esphome::ld2460::testing
|
||||
|
||||
Reference in New Issue
Block a user