Round detection ranges like before, restore the 0 0 fallback and test the command text

This commit is contained in:
J. Nick Koston
2026-10-16 10:29:36 -10:00
parent 2c693fad5b
commit c464a48afb
3 changed files with 79 additions and 7 deletions
@@ -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<size_t>(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<int16_t>(clamp(rintf(values[j] / DET_RANGE_STEP_M), 0.0f, 32767.0f)) : -1;
steps[j] = in_use ? static_cast<int16_t>(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:
@@ -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<Command, COMMAND_QUEUE_SIZE> 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);
};
@@ -0,0 +1,65 @@
#include <gtest/gtest.h>
#include <string>
#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