mirror of
https://github.com/esphome/esphome.git
synced 2025-07-28 14:16:40 +00:00
[ld2450] Reduce CPU usage, eliminate redundant sensor updates (#9334)
This commit is contained in:
parent
b0f8922056
commit
a1291c2730
@ -1,5 +1,6 @@
|
||||
#include "ld2450.h"
|
||||
#include <utility>
|
||||
#include <cmath>
|
||||
#ifdef USE_NUMBER
|
||||
#include "esphome/components/number/number.h"
|
||||
#endif
|
||||
@ -123,16 +124,11 @@ static const uint8_t CMD_SET_ZONE = 0xC2;
|
||||
|
||||
static inline uint16_t convert_seconds_to_ms(uint16_t value) { return value * 1000; };
|
||||
|
||||
static inline std::string convert_signed_int_to_hex(int value) {
|
||||
auto value_as_str = str_snprintf("%04x", 4, value & 0xFFFF);
|
||||
return value_as_str;
|
||||
}
|
||||
|
||||
static inline void convert_int_values_to_hex(const int *values, uint8_t *bytes) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
std::string temp_hex = convert_signed_int_to_hex(values[i]);
|
||||
bytes[i * 2] = std::stoi(temp_hex.substr(2, 2), nullptr, 16); // Store high byte
|
||||
bytes[i * 2 + 1] = std::stoi(temp_hex.substr(0, 2), nullptr, 16); // Store low byte
|
||||
uint16_t val = values[i] & 0xFFFF;
|
||||
bytes[i * 2] = val & 0xFF; // Store low byte first (little-endian)
|
||||
bytes[i * 2 + 1] = (val >> 8) & 0xFF; // Store high byte second
|
||||
}
|
||||
}
|
||||
|
||||
@ -428,6 +424,12 @@ void LD2450Component::send_command_(uint8_t command, const uint8_t *command_valu
|
||||
// [AA FF 03 00] [0E 03 B1 86 10 00 40 01] [00 00 00 00 00 00 00 00] [00 00 00 00 00 00 00 00] [55 CC]
|
||||
// Header Target 1 Target 2 Target 3 End
|
||||
void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) {
|
||||
// Early throttle check - moved before any processing to save CPU cycles
|
||||
if (App.get_loop_component_start_time() - this->last_periodic_millis_ < this->throttle_) {
|
||||
ESP_LOGV(TAG, "Throttling: %d", this->throttle_);
|
||||
return;
|
||||
}
|
||||
|
||||
if (len < 29) { // header (4 bytes) + 8 x 3 target data + footer (2 bytes)
|
||||
ESP_LOGE(TAG, "Invalid message length");
|
||||
return;
|
||||
@ -441,11 +443,6 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (App.get_loop_component_start_time() - this->last_periodic_millis_ < this->throttle_) {
|
||||
ESP_LOGV(TAG, "Throttling: %d", this->throttle_);
|
||||
return;
|
||||
}
|
||||
|
||||
this->last_periodic_millis_ = App.get_loop_component_start_time();
|
||||
|
||||
int16_t target_count = 0;
|
||||
@ -473,7 +470,10 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) {
|
||||
if (sx != nullptr) {
|
||||
val = ld2450::decode_coordinate(buffer[start], buffer[start + 1]);
|
||||
tx = val;
|
||||
sx->publish_state(val);
|
||||
if (this->cached_target_data_[index].x != val) {
|
||||
sx->publish_state(val);
|
||||
this->cached_target_data_[index].x = val;
|
||||
}
|
||||
}
|
||||
// Y
|
||||
start = TARGET_Y + index * 8;
|
||||
@ -481,14 +481,20 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) {
|
||||
if (sy != nullptr) {
|
||||
val = ld2450::decode_coordinate(buffer[start], buffer[start + 1]);
|
||||
ty = val;
|
||||
sy->publish_state(val);
|
||||
if (this->cached_target_data_[index].y != val) {
|
||||
sy->publish_state(val);
|
||||
this->cached_target_data_[index].y = val;
|
||||
}
|
||||
}
|
||||
// RESOLUTION
|
||||
start = TARGET_RESOLUTION + index * 8;
|
||||
sensor::Sensor *sr = this->move_resolution_sensors_[index];
|
||||
if (sr != nullptr) {
|
||||
val = (buffer[start + 1] << 8) | buffer[start];
|
||||
sr->publish_state(val);
|
||||
if (this->cached_target_data_[index].resolution != val) {
|
||||
sr->publish_state(val);
|
||||
this->cached_target_data_[index].resolution = val;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// SPEED
|
||||
@ -502,13 +508,17 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) {
|
||||
#ifdef USE_SENSOR
|
||||
sensor::Sensor *ss = this->move_speed_sensors_[index];
|
||||
if (ss != nullptr) {
|
||||
ss->publish_state(val);
|
||||
if (this->cached_target_data_[index].speed != val) {
|
||||
ss->publish_state(val);
|
||||
this->cached_target_data_[index].speed = val;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// DISTANCE
|
||||
val = (uint16_t) sqrt(
|
||||
pow(ld2450::decode_coordinate(buffer[TARGET_X + index * 8], buffer[(TARGET_X + index * 8) + 1]), 2) +
|
||||
pow(ld2450::decode_coordinate(buffer[TARGET_Y + index * 8], buffer[(TARGET_Y + index * 8) + 1]), 2));
|
||||
// Optimized: use already decoded tx and ty values, replace pow() with multiplication
|
||||
int32_t x_squared = (int32_t) tx * tx;
|
||||
int32_t y_squared = (int32_t) ty * ty;
|
||||
val = (uint16_t) sqrt(x_squared + y_squared);
|
||||
td = val;
|
||||
if (val > 0) {
|
||||
target_count++;
|
||||
@ -516,7 +526,10 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) {
|
||||
#ifdef USE_SENSOR
|
||||
sensor::Sensor *sd = this->move_distance_sensors_[index];
|
||||
if (sd != nullptr) {
|
||||
sd->publish_state(val);
|
||||
if (this->cached_target_data_[index].distance != val) {
|
||||
sd->publish_state(val);
|
||||
this->cached_target_data_[index].distance = val;
|
||||
}
|
||||
}
|
||||
// ANGLE
|
||||
angle = calculate_angle(static_cast<float>(ty), static_cast<float>(td));
|
||||
@ -525,7 +538,11 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) {
|
||||
}
|
||||
sensor::Sensor *sa = this->move_angle_sensors_[index];
|
||||
if (sa != nullptr) {
|
||||
sa->publish_state(angle);
|
||||
if (std::isnan(this->cached_target_data_[index].angle) ||
|
||||
std::abs(this->cached_target_data_[index].angle - angle) > 0.1f) {
|
||||
sa->publish_state(angle);
|
||||
this->cached_target_data_[index].angle = angle;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
@ -536,7 +553,10 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) {
|
||||
}
|
||||
text_sensor::TextSensor *tsd = this->direction_text_sensors_[index];
|
||||
if (tsd != nullptr) {
|
||||
tsd->publish_state(direction);
|
||||
if (this->cached_target_data_[index].direction != direction) {
|
||||
tsd->publish_state(direction);
|
||||
this->cached_target_data_[index].direction = direction;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -563,32 +583,50 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) {
|
||||
// Publish Still Target Count in Zones
|
||||
sensor::Sensor *szstc = this->zone_still_target_count_sensors_[index];
|
||||
if (szstc != nullptr) {
|
||||
szstc->publish_state(zone_still_targets);
|
||||
if (this->cached_zone_data_[index].still_count != zone_still_targets) {
|
||||
szstc->publish_state(zone_still_targets);
|
||||
this->cached_zone_data_[index].still_count = zone_still_targets;
|
||||
}
|
||||
}
|
||||
// Publish Moving Target Count in Zones
|
||||
sensor::Sensor *szmtc = this->zone_moving_target_count_sensors_[index];
|
||||
if (szmtc != nullptr) {
|
||||
szmtc->publish_state(zone_moving_targets);
|
||||
if (this->cached_zone_data_[index].moving_count != zone_moving_targets) {
|
||||
szmtc->publish_state(zone_moving_targets);
|
||||
this->cached_zone_data_[index].moving_count = zone_moving_targets;
|
||||
}
|
||||
}
|
||||
// Publish All Target Count in Zones
|
||||
sensor::Sensor *sztc = this->zone_target_count_sensors_[index];
|
||||
if (sztc != nullptr) {
|
||||
sztc->publish_state(zone_all_targets);
|
||||
if (this->cached_zone_data_[index].total_count != zone_all_targets) {
|
||||
sztc->publish_state(zone_all_targets);
|
||||
this->cached_zone_data_[index].total_count = zone_all_targets;
|
||||
}
|
||||
}
|
||||
|
||||
} // End loop thru zones
|
||||
|
||||
// Target Count
|
||||
if (this->target_count_sensor_ != nullptr) {
|
||||
this->target_count_sensor_->publish_state(target_count);
|
||||
if (this->cached_global_data_.target_count != target_count) {
|
||||
this->target_count_sensor_->publish_state(target_count);
|
||||
this->cached_global_data_.target_count = target_count;
|
||||
}
|
||||
}
|
||||
// Still Target Count
|
||||
if (this->still_target_count_sensor_ != nullptr) {
|
||||
this->still_target_count_sensor_->publish_state(still_target_count);
|
||||
if (this->cached_global_data_.still_count != still_target_count) {
|
||||
this->still_target_count_sensor_->publish_state(still_target_count);
|
||||
this->cached_global_data_.still_count = still_target_count;
|
||||
}
|
||||
}
|
||||
// Moving Target Count
|
||||
if (this->moving_target_count_sensor_ != nullptr) {
|
||||
this->moving_target_count_sensor_->publish_state(moving_target_count);
|
||||
if (this->cached_global_data_.moving_count != moving_target_count) {
|
||||
this->moving_target_count_sensor_->publish_state(moving_target_count);
|
||||
this->cached_global_data_.moving_count = moving_target_count;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
#include <limits>
|
||||
#include <cmath>
|
||||
#ifdef USE_SENSOR
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#endif
|
||||
@ -100,7 +102,7 @@ class LD2450Component : public Component, public uart::UARTDevice {
|
||||
void dump_config() override;
|
||||
void loop() override;
|
||||
void set_presence_timeout();
|
||||
void set_throttle(uint16_t value) { this->throttle_ = value; };
|
||||
void set_throttle(uint16_t value) { this->throttle_ = value; }
|
||||
void read_all_info();
|
||||
void query_zone_info();
|
||||
void restart_and_read_all_info();
|
||||
@ -164,6 +166,32 @@ class LD2450Component : public Component, public uart::UARTDevice {
|
||||
Zone zone_config_[MAX_ZONES];
|
||||
std::string version_{};
|
||||
std::string mac_{};
|
||||
|
||||
// Change detection - cache previous values to avoid redundant publishes
|
||||
// All values are initialized to sentinel values that are outside the valid sensor ranges
|
||||
// to ensure the first real measurement is always published
|
||||
struct CachedTargetData {
|
||||
int16_t x = std::numeric_limits<int16_t>::min(); // -32768, outside range of -4860 to 4860
|
||||
int16_t y = std::numeric_limits<int16_t>::min(); // -32768, outside range of 0 to 7560
|
||||
int16_t speed = std::numeric_limits<int16_t>::min(); // -32768, outside practical sensor range
|
||||
uint16_t resolution = std::numeric_limits<uint16_t>::max(); // 65535, unlikely resolution value
|
||||
uint16_t distance = std::numeric_limits<uint16_t>::max(); // 65535, outside range of 0 to ~8990
|
||||
float angle = NAN; // NAN, safe sentinel for floats
|
||||
std::string direction = ""; // Empty string, will differ from any real direction
|
||||
} cached_target_data_[MAX_TARGETS];
|
||||
|
||||
struct CachedZoneData {
|
||||
uint8_t still_count = std::numeric_limits<uint8_t>::max(); // 255, unlikely zone count
|
||||
uint8_t moving_count = std::numeric_limits<uint8_t>::max(); // 255, unlikely zone count
|
||||
uint8_t total_count = std::numeric_limits<uint8_t>::max(); // 255, unlikely zone count
|
||||
} cached_zone_data_[MAX_ZONES];
|
||||
|
||||
struct CachedGlobalData {
|
||||
uint8_t target_count = std::numeric_limits<uint8_t>::max(); // 255, max 3 targets possible
|
||||
uint8_t still_count = std::numeric_limits<uint8_t>::max(); // 255, max 3 targets possible
|
||||
uint8_t moving_count = std::numeric_limits<uint8_t>::max(); // 255, max 3 targets possible
|
||||
} cached_global_data_;
|
||||
|
||||
#ifdef USE_NUMBER
|
||||
ESPPreferenceObject pref_; // only used when numbers are in use
|
||||
ZoneOfNumbers zone_numbers_[MAX_ZONES];
|
||||
|
Loading…
x
Reference in New Issue
Block a user