10static const char *
const TAG =
"rd03d";
13static constexpr uint32_t SETUP_TIMEOUT_MS = 100;
16static constexpr uint8_t FRAME_HEADER[] = {0xAA, 0xFF, 0x03, 0x00};
17static constexpr uint8_t FRAME_FOOTER[] = {0x55, 0xCC};
20static constexpr uint8_t CMD_FRAME_HEADER[] = {0xFD, 0xFC, 0xFB, 0xFA};
21static constexpr uint8_t CMD_FRAME_FOOTER[] = {0x04, 0x03, 0x02, 0x01};
24static constexpr uint16_t CMD_SINGLE_TARGET = 0x0080;
25static constexpr uint16_t CMD_MULTI_TARGET = 0x0090;
29static constexpr int16_t SPEED_SENTINEL_248 = 248;
30static constexpr int16_t SPEED_SENTINEL_256 = 256;
34static constexpr int16_t decode_value(uint8_t low_byte, uint8_t high_byte) {
35 int16_t value = ((high_byte & 0x7F) << 8) | low_byte;
36 if ((high_byte & 0x80) == 0) {
44static constexpr bool is_speed_valid(int16_t
speed) {
46 return speed != 0 && abs_speed != SPEED_SENTINEL_248 && abs_speed != SPEED_SENTINEL_256;
50 ESP_LOGCONFIG(TAG,
"Setting up RD-03D...");
55 ESP_LOGCONFIG(TAG,
"RD-03D:");
57 ESP_LOGCONFIG(TAG,
" Tracking Mode: %s",
61 ESP_LOGCONFIG(TAG,
" Throttle: %" PRIu32
"ms", this->
throttle_);
66#ifdef USE_BINARY_SENSOR
69 for (uint8_t i = 0; i < MAX_TARGETS; i++) {
70 ESP_LOGCONFIG(TAG,
" Target %d:", i + 1);
72 LOG_SENSOR(
" ",
"X", this->
targets_[i].
x);
73 LOG_SENSOR(
" ",
"Y", this->
targets_[i].
y);
75 LOG_SENSOR(
" ",
"Distance", this->
targets_[i].distance);
77 LOG_SENSOR(
" ",
"Angle", this->
targets_[i].angle);
79#ifdef USE_BINARY_SENSOR
90 size_t to_read = std::min(avail,
sizeof(buf));
95 for (
size_t i = 0; i < to_read; i++) {
96 uint8_t
byte = buf[i];
97 ESP_LOGVV(TAG,
"Received byte: 0x%02X, buffer_pos: %d",
byte, this->
buffer_pos_);
102 this->
buffer_[this->buffer_pos_++] = byte;
103 }
else if (
byte == FRAME_HEADER[0]) {
106 this->buffer_pos_ = 1;
108 this->buffer_pos_ = 0;
119 if (this->
buffer_[FRAME_SIZE - 2] == FRAME_FOOTER[0] && this->
buffer_[FRAME_SIZE - 1] == FRAME_FOOTER[1]) {
122 ESP_LOGW(TAG,
"Invalid frame footer: 0x%02X 0x%02X (expected 0x55 0xCC)", this->
buffer_[FRAME_SIZE - 2],
123 this->
buffer_[FRAME_SIZE - 1]);
141 uint8_t target_count = 0;
143 for (uint8_t i = 0; i < MAX_TARGETS; i++) {
146 uint8_t offset = FRAME_HEADER_SIZE + (i * TARGET_DATA_SIZE);
149 uint8_t x_low = this->
buffer_[offset + 0];
150 uint8_t x_high = this->
buffer_[offset + 1];
151 uint8_t y_low = this->
buffer_[offset + 2];
152 uint8_t y_high = this->
buffer_[offset + 3];
153 uint8_t speed_low = this->
buffer_[offset + 4];
154 uint8_t speed_high = this->
buffer_[offset + 5];
155 uint8_t res_low = this->
buffer_[offset + 6];
156 uint8_t res_high = this->
buffer_[offset + 7];
159 int16_t
x = decode_value(x_low, x_high);
160 int16_t
y = decode_value(y_low, y_high);
161 int16_t
speed = decode_value(speed_low, speed_high);
162 uint16_t
resolution = (res_high << 8) | res_low;
167 bool has_position = (
x != 0 ||
y != 0);
168 bool has_valid_speed = is_speed_valid(
speed);
169 bool target_present = has_position && has_valid_speed;
170 if (target_present) {
178#ifdef USE_BINARY_SENSOR
191#ifdef USE_BINARY_SENSOR
204 if (target.
x !=
nullptr) {
209 if (target.
y !=
nullptr) {
214 if (target.
speed !=
nullptr) {
234 if (target.
angle !=
nullptr) {
236 float angle = std::atan2(
static_cast<float>(
x),
static_cast<float>(
y)) * 180.0f / M_PI;
247 this->
write_array(CMD_FRAME_HEADER,
sizeof(CMD_FRAME_HEADER));
250 uint16_t
len = 2 + data_len;
259 if (data !=
nullptr && data_len > 0) {
264 this->
write_array(CMD_FRAME_FOOTER,
sizeof(CMD_FRAME_FOOTER));
266 ESP_LOGD(TAG,
"Sent command 0x%04X with %d bytes of data", command, data_len);
ESPDEPRECATED("Use const char* or uint32_t overload instead. Removed in 2026.7.0", "2026.1.0") void set_timeout(const std voi set_timeout)(const char *name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
void publish_state(bool new_state)
Publish a new state to the front-end.
optional< TrackingMode > tracking_mode_
std::array< TargetSensor, MAX_TARGETS > targets_
void dump_config() override
void send_command_(uint16_t command, const uint8_t *data=nullptr, uint8_t data_len=0)
uint32_t last_publish_time_
binary_sensor::BinarySensor * target_binary_sensor_
std::array< uint8_t, FRAME_SIZE > buffer_
void publish_target_(uint8_t target_num, int16_t x, int16_t y, int16_t speed, uint16_t resolution)
std::array< binary_sensor::BinarySensor *, MAX_TARGETS > target_presence_
sensor::Sensor * target_count_sensor_
void publish_state(float state)
Publish a new state to the front-end.
optional< std::array< uint8_t, N > > read_array()
void write_byte(uint8_t data)
void write_array(const uint8_t *data, size_t len)
uint32_t IRAM_ATTR HOT millis()
sensor::Sensor * resolution
sensor::Sensor * distance