From c464a48afb83e5d05ea73c2c810d68eb6582ea2d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 16 Oct 2026 10:29:36 -1000 Subject: [PATCH] Round detection ranges like before, restore the 0 0 fallback and test the command text --- .../dfrobot_sen0395/dfrobot_sen0395.cpp | 13 ++-- .../dfrobot_sen0395/dfrobot_sen0395.h | 8 ++- .../dfrobot_sen0395/format_command.cpp | 65 +++++++++++++++++++ 3 files changed, 79 insertions(+), 7 deletions(-) create mode 100644 tests/components/dfrobot_sen0395/format_command.cpp diff --git a/esphome/components/dfrobot_sen0395/dfrobot_sen0395.cpp b/esphome/components/dfrobot_sen0395/dfrobot_sen0395.cpp index d2caacd2781..cda381b84e5 100644 --- a/esphome/components/dfrobot_sen0395/dfrobot_sen0395.cpp +++ b/esphome/components/dfrobot_sen0395/dfrobot_sen0395.cpp @@ -20,6 +20,8 @@ static constexpr float LATENCY_STEP_S = 0.025f; PROGMEM_STRING_TABLE(BusyActions, "", "configure range config", "configure output latency", "configure sensor startup behavior", "factory reset", "", "", "set led mode", "set uart output mode", "set sensitivity"); +static_assert(BusyActions::COUNT == static_cast(CommandType::COMMAND_TYPE_SENSITIVITY) + 1, + "BusyActions needs one entry per CommandType"); void DfrobotSen0395Component::dump_config() { ESP_LOGCONFIG(TAG, "Dfrobot Mmwave Radar:"); @@ -64,10 +66,13 @@ void DfrobotSen0395Component::enqueue_det_range_cfg(float min1, float max1, floa for (size_t i = 0; i < 8; i += 2) { in_use = in_use && values[i] >= 0 && values[i + 1] >= 0; for (size_t j = i; j < i + 2; j++) { - // rintf rounds halves to even, like the "%.0f" the sensor command was formatted with before - steps[j] = in_use ? static_cast(clamp(rintf(values[j] / DET_RANGE_STEP_M), 0.0f, 32767.0f)) : -1; + steps[j] = in_use ? static_cast(clamp(roundf(values[j] / DET_RANGE_STEP_M), 0.0f, 32767.0f)) : -1; } } + if (steps[0] < 0) { + ESP_LOGW(TAG, "Invalid detection range, using 0 0"); + steps[0] = steps[1] = 0; + } if (this->enqueue(CommandType::COMMAND_TYPE_DET_RANGE_CFG)) memcpy(this->det_range_steps_, steps, sizeof(steps)); } @@ -98,7 +103,7 @@ bool DfrobotSen0395Component::run_command_(const Command &cmd, uint32_t now) { if (!this->cmd_sent_) { if (now - this->ts_last_cmd_sent_ > duration) { char buf[64]; // "detRangeCfg -1" + 8 * " 32767" + null - this->format_command_(cmd, buf, sizeof(buf)); + this->format_command(cmd, buf, sizeof(buf)); ESP_LOGD(TAG, "Sending command: %s", buf); this->write_str(buf); this->ts_last_cmd_sent_ = now; @@ -147,7 +152,7 @@ static size_t append_cfg_key(char *buf, size_t size, size_t pos) { return buf_append_str(buf, size, pos, " 0x45670123 0xCDEF89AB 0x956128C6 0xDF54AC89"); } -void DfrobotSen0395Component::format_command_(const Command &cmd, char *buf, size_t size) { +void DfrobotSen0395Component::format_command(const Command &cmd, char *buf, size_t size) const { buf[0] = '\0'; switch (cmd.type) { case CommandType::COMMAND_TYPE_POWER: diff --git a/esphome/components/dfrobot_sen0395/dfrobot_sen0395.h b/esphome/components/dfrobot_sen0395/dfrobot_sen0395.h index a35798160ef..82e57203750 100644 --- a/esphome/components/dfrobot_sen0395/dfrobot_sen0395.h +++ b/esphome/components/dfrobot_sen0395/dfrobot_sen0395.h @@ -17,7 +17,7 @@ namespace esphome::dfrobot_sen0395 { static constexpr uint8_t MMWAVE_READ_BUFFER_LENGTH = 255; static constexpr uint8_t COMMAND_QUEUE_SIZE = 20; -// Keep in sync with the BUSY_ACTIONS table in dfrobot_sen0395.cpp +// Keep in sync with the BusyActions table in dfrobot_sen0395.cpp enum class CommandType : uint8_t { COMMAND_TYPE_POWER, COMMAND_TYPE_DET_RANGE_CFG, @@ -106,6 +106,8 @@ class DfrobotSen0395Component final : public uart::UARTDevice, public Component void enqueue_saved_setting(CommandType type, bool value); /// Queue a sensor reset void reset_system() { this->enqueue(CommandType::COMMAND_TYPE_RESET_SYSTEM); } + /// Write the text sent to the sensor for cmd into buf + void format_command(const Command &cmd, char *buf, size_t size) const; protected: #ifdef USE_BINARY_SENSOR @@ -124,7 +126,8 @@ class DfrobotSen0395Component final : public uart::UARTDevice, public Component StaticRingBuffer cmd_queue_; uint32_t ts_last_cmd_sent_{0}; // Values of the queued range and latency commands, in the sensor's steps of 0.15 m and 0.025 s. - // A later command replaces them, which leaves the sensor in the same final state. + // A later command replaces them, which leaves the sensor in the same final state. One replaced while its + // command waits for a reply makes that command's log and any retry use the newer values. int16_t det_range_steps_[8]{}; uint16_t latency_steps_[2]{}; @@ -134,7 +137,6 @@ class DfrobotSen0395Component final : public uart::UARTDevice, public Component bool run_command_(const Command &cmd, uint32_t now); /// Handle a reply to a sent command; true when the command is done bool handle_reply_(const Command &cmd); - void format_command_(const Command &cmd, char *buf, size_t size); void set_detected_(bool detected); }; diff --git a/tests/components/dfrobot_sen0395/format_command.cpp b/tests/components/dfrobot_sen0395/format_command.cpp new file mode 100644 index 00000000000..36b6b8318ee --- /dev/null +++ b/tests/components/dfrobot_sen0395/format_command.cpp @@ -0,0 +1,65 @@ +#include +#include +#include "esphome/components/dfrobot_sen0395/dfrobot_sen0395.h" + +namespace esphome::dfrobot_sen0395::testing { + +// The text each command sends, as the per-command classes sent it before the queue held plain values. +class FormatCommandTest : public ::testing::Test { + protected: + std::string format(CommandType type, uint8_t arg = 0) { + char buf[64]; + this->radar_.format_command({type, arg}, buf, sizeof(buf)); + return buf; + } + + DfrobotSen0395Component radar_; +}; + +TEST_F(FormatCommandTest, FixedCommands) { + EXPECT_EQ(this->format(CommandType::COMMAND_TYPE_POWER, true), "sensorStart"); + EXPECT_EQ(this->format(CommandType::COMMAND_TYPE_POWER, false), "sensorStop"); + EXPECT_EQ(this->format(CommandType::COMMAND_TYPE_FACTORY_RESET), + "factoryReset 0x45670123 0xCDEF89AB 0x956128C6 0xDF54AC89"); + EXPECT_EQ(this->format(CommandType::COMMAND_TYPE_SAVE_CFG), "saveCfg 0x45670123 0xCDEF89AB 0x956128C6 0xDF54AC89"); + EXPECT_EQ(this->format(CommandType::COMMAND_TYPE_RESET_SYSTEM), "resetSystem"); + EXPECT_EQ(this->format(CommandType::COMMAND_TYPE_LED_MODE, true), "setLedMode 1 0"); + EXPECT_EQ(this->format(CommandType::COMMAND_TYPE_LED_MODE, false), "setLedMode 1 1"); + EXPECT_EQ(this->format(CommandType::COMMAND_TYPE_UART_OUTPUT, true), "setUartOutput 1 1"); + EXPECT_EQ(this->format(CommandType::COMMAND_TYPE_UART_OUTPUT, false), "setUartOutput 1 0"); + EXPECT_EQ(this->format(CommandType::COMMAND_TYPE_SENSOR_CFG_START, true), "sensorCfgStart 1"); + EXPECT_EQ(this->format(CommandType::COMMAND_TYPE_SENSOR_CFG_START, false), "sensorCfgStart 0"); + EXPECT_EQ(this->format(CommandType::COMMAND_TYPE_SENSITIVITY, 7), "setSensitivity 7"); +} + +TEST_F(FormatCommandTest, DetectionRangeSegments) { + this->radar_.enqueue_det_range_cfg(0.3f, 1.2f, -1, -1, -1, -1, -1, -1); + EXPECT_EQ(this->format(CommandType::COMMAND_TYPE_DET_RANGE_CFG), "detRangeCfg -1 2 8"); + this->radar_.enqueue_det_range_cfg(0.0f, 1.5f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 9.0f); + EXPECT_EQ(this->format(CommandType::COMMAND_TYPE_DET_RANGE_CFG), "detRangeCfg -1 0 10 13 20 27 33 40 60"); + // Segments stop at the first one with a negative bound + this->radar_.enqueue_det_range_cfg(0.0f, 1.5f, 2.0f, -1, 4.0f, 5.0f, 6.0f, 9.0f); + EXPECT_EQ(this->format(CommandType::COMMAND_TYPE_DET_RANGE_CFG), "detRangeCfg -1 0 10"); +} + +TEST_F(FormatCommandTest, DetectionRangeRoundsHalfStepsUp) { + // 0.075 m is half a 0.15 m step + this->radar_.enqueue_det_range_cfg(0.0f, 0.075f, -1, -1, -1, -1, -1, -1); + EXPECT_EQ(this->format(CommandType::COMMAND_TYPE_DET_RANGE_CFG), "detRangeCfg -1 0 1"); +} + +TEST_F(FormatCommandTest, DetectionRangeNegativeFirstSegmentFallsBack) { + this->radar_.enqueue_det_range_cfg(-1, 3.0f, -1, -1, -1, -1, -1, -1); + EXPECT_EQ(this->format(CommandType::COMMAND_TYPE_DET_RANGE_CFG), "detRangeCfg -1 0 0"); +} + +TEST_F(FormatCommandTest, Latency) { + this->radar_.enqueue_set_latency(0.0f, 2.5f); + EXPECT_EQ(this->format(CommandType::COMMAND_TYPE_SET_LATENCY), "setLatency 0.000 2.500"); + this->radar_.enqueue_set_latency(0.0125f, 0.0374f); + EXPECT_EQ(this->format(CommandType::COMMAND_TYPE_SET_LATENCY), "setLatency 0.025 0.025"); + this->radar_.enqueue_set_latency(1638.375f, 5000.0f); + EXPECT_EQ(this->format(CommandType::COMMAND_TYPE_SET_LATENCY), "setLatency 1638.375 1638.375"); +} + +} // namespace esphome::dfrobot_sen0395::testing