8static const char *
const TAG =
"rd03d";
11static constexpr uint32_t SETUP_TIMEOUT_MS = 100;
14static constexpr uint8_t FRAME_HEADER[] = {0xAA, 0xFF, 0x03, 0x00};
15static constexpr uint8_t FRAME_FOOTER[] = {0x55, 0xCC};
18static constexpr uint8_t CMD_FRAME_HEADER[] = {0xFD, 0xFC, 0xFB, 0xFA};
19static constexpr uint8_t CMD_FRAME_FOOTER[] = {0x04, 0x03, 0x02, 0x01};
22static constexpr uint16_t CMD_SINGLE_TARGET = 0x0080;
23static constexpr uint16_t CMD_MULTI_TARGET = 0x0090;
27static constexpr int16_t SPEED_SENTINEL_248 = 248;
28static constexpr int16_t SPEED_SENTINEL_256 = 256;
32static constexpr int16_t decode_value(uint8_t low_byte, uint8_t high_byte) {
33 int16_t value = ((high_byte & 0x7F) << 8) | low_byte;
34 if ((high_byte & 0x80) == 0) {
42static constexpr bool is_speed_valid(int16_t
speed) {
44 return speed != 0 && abs_speed != SPEED_SENTINEL_248 && abs_speed != SPEED_SENTINEL_256;
48 ESP_LOGCONFIG(TAG,
"Setting up RD-03D...");
53 ESP_LOGCONFIG(TAG,
"RD-03D:");
55 ESP_LOGCONFIG(TAG,
" Tracking Mode: %s",
59 ESP_LOGCONFIG(TAG,
" Throttle: %ums", this->
throttle_);
64#ifdef USE_BINARY_SENSOR
67 for (uint8_t i = 0; i < MAX_TARGETS; i++) {
68 ESP_LOGCONFIG(TAG,
" Target %d:", i + 1);
70 LOG_SENSOR(
" ",
"X", this->
targets_[i].
x);
71 LOG_SENSOR(
" ",
"Y", this->
targets_[i].
y);
73 LOG_SENSOR(
" ",
"Distance", this->
targets_[i].distance);
75 LOG_SENSOR(
" ",
"Angle", this->
targets_[i].angle);
77#ifdef USE_BINARY_SENSOR
88 size_t to_read = std::min(avail,
sizeof(buf));
93 for (
size_t i = 0; i < to_read; i++) {
94 uint8_t
byte = buf[i];
95 ESP_LOGVV(TAG,
"Received byte: 0x%02X, buffer_pos: %d",
byte, this->
buffer_pos_);
100 this->
buffer_[this->buffer_pos_++] = byte;
101 }
else if (
byte == FRAME_HEADER[0]) {
104 this->buffer_pos_ = 1;
106 this->buffer_pos_ = 0;
117 if (this->
buffer_[FRAME_SIZE - 2] == FRAME_FOOTER[0] && this->
buffer_[FRAME_SIZE - 1] == FRAME_FOOTER[1]) {
120 ESP_LOGW(TAG,
"Invalid frame footer: 0x%02X 0x%02X (expected 0x55 0xCC)", this->
buffer_[FRAME_SIZE - 2],
121 this->
buffer_[FRAME_SIZE - 1]);
139 uint8_t target_count = 0;
141 for (uint8_t i = 0; i < MAX_TARGETS; i++) {
144 uint8_t offset = FRAME_HEADER_SIZE + (i * TARGET_DATA_SIZE);
147 uint8_t x_low = this->
buffer_[offset + 0];
148 uint8_t x_high = this->
buffer_[offset + 1];
149 uint8_t y_low = this->
buffer_[offset + 2];
150 uint8_t y_high = this->
buffer_[offset + 3];
151 uint8_t speed_low = this->
buffer_[offset + 4];
152 uint8_t speed_high = this->
buffer_[offset + 5];
153 uint8_t res_low = this->
buffer_[offset + 6];
154 uint8_t res_high = this->
buffer_[offset + 7];
157 int16_t
x = decode_value(x_low, x_high);
158 int16_t
y = decode_value(y_low, y_high);
159 int16_t
speed = decode_value(speed_low, speed_high);
160 uint16_t
resolution = (res_high << 8) | res_low;
165 bool has_position = (
x != 0 ||
y != 0);
166 bool has_valid_speed = is_speed_valid(
speed);
167 bool target_present = has_position && has_valid_speed;
168 if (target_present) {
176#ifdef USE_BINARY_SENSOR
189#ifdef USE_BINARY_SENSOR
199 bool valid = is_speed_valid(
speed);
202 if (target.
x !=
nullptr) {
207 if (target.
y !=
nullptr) {
212 if (target.
speed !=
nullptr) {
232 if (target.
angle !=
nullptr) {
234 float angle = std::atan2(
static_cast<float>(
x),
static_cast<float>(
y)) * 180.0f / M_PI;
245 this->
write_array(CMD_FRAME_HEADER,
sizeof(CMD_FRAME_HEADER));
248 uint16_t
len = 2 + data_len;
257 if (data !=
nullptr && data_len > 0) {
262 this->
write_array(CMD_FRAME_FOOTER,
sizeof(CMD_FRAME_FOOTER));
264 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