mirror of
https://github.com/esphome/esphome.git
synced 2026-10-20 04:56:48 +00:00
[dfrobot_sen0395] Queue commands by value instead of allocating them
This commit is contained in:
@@ -27,9 +27,9 @@ class DfrobotSen0395SettingsAction final : public Action<Ts...>, public Parented
|
||||
TEMPLATABLE_VALUE(float, det_max4)
|
||||
|
||||
void play(const Ts &...x) override {
|
||||
this->parent_->enqueue(make_unique<PowerCommand>(0));
|
||||
this->parent_->enqueue(CommandType::COMMAND_TYPE_POWER, false);
|
||||
if (this->factory_reset_.has_value() && this->factory_reset_.value(x...) == true) {
|
||||
this->parent_->enqueue(make_unique<FactoryResetCommand>());
|
||||
this->parent_->enqueue(CommandType::COMMAND_TYPE_FACTORY_RESET);
|
||||
}
|
||||
if (this->det_min1_.has_value() && this->det_max1_.has_value()) {
|
||||
// Each segment is read once, so the guard and the command see the same values.
|
||||
@@ -37,10 +37,10 @@ class DfrobotSen0395SettingsAction final : public Action<Ts...>, public Parented
|
||||
if (min1 >= 0) {
|
||||
const float max1 = this->det_max1_.value(x...);
|
||||
if (max1 >= 0) {
|
||||
this->parent_->enqueue(make_unique<DetRangeCfgCommand>(
|
||||
min1, max1, this->det_min2_.value_or(x..., -1), this->det_max2_.value_or(x..., -1),
|
||||
this->det_min3_.value_or(x..., -1), this->det_max3_.value_or(x..., -1),
|
||||
this->det_min4_.value_or(x..., -1), this->det_max4_.value_or(x..., -1)));
|
||||
this->parent_->enqueue_det_range_cfg(min1, max1, this->det_min2_.value_or(x..., -1),
|
||||
this->det_max2_.value_or(x..., -1), this->det_min3_.value_or(x..., -1),
|
||||
this->det_max3_.value_or(x..., -1), this->det_min4_.value_or(x..., -1),
|
||||
this->det_max4_.value_or(x..., -1));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,38 +48,35 @@ class DfrobotSen0395SettingsAction final : public Action<Ts...>, public Parented
|
||||
float detect = this->delay_after_detect_.value(x...);
|
||||
float disappear = this->delay_after_disappear_.value(x...);
|
||||
if (detect >= 0 && disappear >= 0) {
|
||||
this->parent_->enqueue(make_unique<SetLatencyCommand>(detect, disappear));
|
||||
this->parent_->enqueue_set_latency(detect, disappear);
|
||||
}
|
||||
}
|
||||
if (this->start_after_power_on_.has_value()) {
|
||||
int8_t val = this->start_after_power_on_.value(x...);
|
||||
if (val >= 0) {
|
||||
this->parent_->enqueue(make_unique<SensorCfgStartCommand>(val));
|
||||
this->parent_->enqueue(CommandType::COMMAND_TYPE_SENSOR_CFG_START, val);
|
||||
}
|
||||
}
|
||||
if (this->turn_on_led_.has_value()) {
|
||||
int8_t val = this->turn_on_led_.value(x...);
|
||||
if (val >= 0) {
|
||||
this->parent_->enqueue(make_unique<LedModeCommand>(val));
|
||||
this->parent_->enqueue(CommandType::COMMAND_TYPE_LED_MODE, val);
|
||||
}
|
||||
}
|
||||
if (this->presence_via_uart_.has_value()) {
|
||||
int8_t val = this->presence_via_uart_.value(x...);
|
||||
if (val >= 0) {
|
||||
this->parent_->enqueue(make_unique<UartOutputCommand>(val));
|
||||
this->parent_->enqueue(CommandType::COMMAND_TYPE_UART_OUTPUT, val);
|
||||
}
|
||||
}
|
||||
if (this->sensitivity_.has_value()) {
|
||||
int8_t val = this->sensitivity_.value(x...);
|
||||
if (val >= 0) {
|
||||
if (val > 9) {
|
||||
val = 9;
|
||||
}
|
||||
this->parent_->enqueue(make_unique<SensitivityCommand>(val));
|
||||
this->parent_->enqueue(CommandType::COMMAND_TYPE_SENSITIVITY, val);
|
||||
}
|
||||
}
|
||||
this->parent_->enqueue(make_unique<SaveCfgCommand>());
|
||||
this->parent_->enqueue(make_unique<PowerCommand>(1));
|
||||
this->parent_->enqueue(CommandType::COMMAND_TYPE_SAVE_CFG);
|
||||
this->parent_->enqueue(CommandType::COMMAND_TYPE_POWER, true);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#include "commands.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#include "dfrobot_sen0395.h"
|
||||
@@ -10,323 +12,236 @@ namespace esphome::dfrobot_sen0395 {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "dfrobot_sen0395.commands");
|
||||
|
||||
uint8_t Command::execute(DfrobotSen0395Component *parent) {
|
||||
this->parent_ = parent;
|
||||
if (this->cmd_sent_) {
|
||||
if (this->parent_->read_message_()) {
|
||||
std::string message(this->parent_->read_buffer_);
|
||||
if (message.rfind("is not recognized as a CLI command") != std::string::npos) {
|
||||
ESP_LOGD(TAG, "Command not recognized properly by sensor");
|
||||
if (this->retries_left_ > 0) {
|
||||
this->retries_left_ -= 1;
|
||||
this->cmd_sent_ = false;
|
||||
ESP_LOGD(TAG, "Retrying");
|
||||
return 0;
|
||||
} else {
|
||||
this->parent_->find_prompt_();
|
||||
return 1; // Command done
|
||||
}
|
||||
}
|
||||
uint8_t rc = on_message(message);
|
||||
if (rc == 2) {
|
||||
if (this->retries_left_ > 0) {
|
||||
this->retries_left_ -= 1;
|
||||
this->cmd_sent_ = false;
|
||||
ESP_LOGD(TAG, "Retrying");
|
||||
return 0;
|
||||
} else {
|
||||
this->parent_->find_prompt_();
|
||||
return 1; // Command done
|
||||
}
|
||||
} else if (rc == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
this->parent_->find_prompt_();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (millis() - this->parent_->ts_last_cmd_sent_ > this->timeout_ms_) {
|
||||
ESP_LOGD(TAG, "Command timeout");
|
||||
if (this->retries_left_ > 0) {
|
||||
this->retries_left_ -= 1;
|
||||
this->cmd_sent_ = false;
|
||||
ESP_LOGD(TAG, "Retrying");
|
||||
} else {
|
||||
return 1; // Command done
|
||||
}
|
||||
}
|
||||
} else if (this->parent_->send_cmd_(this->cmd_.c_str(), this->cmd_duration_ms_)) {
|
||||
this->cmd_sent_ = true;
|
||||
}
|
||||
return 0; // Command not done yet
|
||||
}
|
||||
static constexpr float DET_RANGE_STEP_M = 0.15f;
|
||||
static constexpr float LATENCY_STEP_S = 0.025f;
|
||||
|
||||
uint8_t ReadStateCommand::execute(DfrobotSen0395Component *parent) {
|
||||
this->parent_ = parent;
|
||||
if (this->parent_->read_message_()) {
|
||||
std::string message(this->parent_->read_buffer_);
|
||||
if (message.rfind("$JYBSS,0, , , *") != std::string::npos) {
|
||||
this->parent_->set_detected_(false);
|
||||
this->parent_->set_active(true);
|
||||
return 1; // Command done
|
||||
} else if (message.rfind("$JYBSS,1, , , *") != std::string::npos) {
|
||||
this->parent_->set_detected_(true);
|
||||
this->parent_->set_active(true);
|
||||
return 1; // Command done
|
||||
}
|
||||
}
|
||||
if (millis() - this->parent_->ts_last_cmd_sent_ > this->timeout_ms_) {
|
||||
return 1; // Command done, timeout
|
||||
}
|
||||
return 0; // Command not done yet.
|
||||
}
|
||||
|
||||
uint8_t ReadStateCommand::on_message(std::string &message) { return 1; }
|
||||
|
||||
uint8_t PowerCommand::on_message(std::string &message) {
|
||||
if (message == "sensor stopped already") {
|
||||
this->parent_->set_active(false);
|
||||
ESP_LOGI(TAG, "Stopped sensor (already stopped)");
|
||||
return 1; // Command done
|
||||
} else if (message == "sensor started already") {
|
||||
this->parent_->set_active(true);
|
||||
ESP_LOGI(TAG, "Started sensor (already started)");
|
||||
return 1; // Command done
|
||||
} else if (message == "new parameter isn't save, can't startSensor") {
|
||||
this->parent_->set_active(false);
|
||||
ESP_LOGE(TAG, "Can't start sensor! (Use SaveCfgCommand to save config first)");
|
||||
return 1; // Command done
|
||||
} else if (message == "Done") {
|
||||
this->parent_->set_active(this->power_on_);
|
||||
if (this->power_on_) {
|
||||
ESP_LOGI(TAG, "Started sensor");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Stopped sensor");
|
||||
}
|
||||
return 1; // Command done
|
||||
}
|
||||
return 0; // Command not done yet.
|
||||
}
|
||||
|
||||
DetRangeCfgCommand::DetRangeCfgCommand(float min1, float max1, float min2, float max2, float min3, float max3,
|
||||
float min4, float max4) {
|
||||
void DfrobotSen0395Component::enqueue_det_range_cfg(float min1, float max1, float min2, float max2, float min3,
|
||||
float max3, float min4, float max4) {
|
||||
// TODO: Print warning when values are rounded
|
||||
if (min1 < 0 || max1 < 0) {
|
||||
this->min1_ = min1 = 0;
|
||||
this->max1_ = max1 = 0;
|
||||
this->min2_ = min2 = this->max2_ = max2 = this->min3_ = min3 = this->max3_ = max3 = this->min4_ = min4 =
|
||||
this->max4_ = max4 = -1;
|
||||
|
||||
const float values[8] = {min1, max1, min2, max2, min3, max3, min4, max4};
|
||||
int16_t steps[8];
|
||||
// Segments are used up to the first one with a negative bound
|
||||
bool in_use = true;
|
||||
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++) {
|
||||
steps[j] = in_use ? static_cast<int16_t>(clamp(rintf(values[j] / DET_RANGE_STEP_M), 0.0f, 32767.0f)) : -1;
|
||||
}
|
||||
}
|
||||
if (steps[0] < 0) {
|
||||
ESP_LOGW(TAG, "DetRangeCfgCommand invalid input parameters. Using range config 0 0.");
|
||||
|
||||
this->cmd_ = "detRangeCfg -1 0 0";
|
||||
} else if (min2 < 0 || max2 < 0) {
|
||||
this->min1_ = min1 = roundf(min1 / 0.15f) * 0.15f;
|
||||
this->max1_ = max1 = roundf(max1 / 0.15f) * 0.15f;
|
||||
this->min2_ = min2 = this->max2_ = max2 = this->min3_ = min3 = this->max3_ = max3 = this->min4_ = min4 =
|
||||
this->max4_ = max4 = -1;
|
||||
|
||||
char buf[72]; // max 72: "detRangeCfg -1 "(15) + 8 * (float(5) + space(1)) + null
|
||||
snprintf(buf, sizeof(buf), "detRangeCfg -1 %.0f %.0f", min1 / 0.15f, max1 / 0.15f);
|
||||
this->cmd_ = buf;
|
||||
} else if (min3 < 0 || max3 < 0) {
|
||||
this->min1_ = min1 = roundf(min1 / 0.15f) * 0.15f;
|
||||
this->max1_ = max1 = roundf(max1 / 0.15f) * 0.15f;
|
||||
this->min2_ = min2 = roundf(min2 / 0.15f) * 0.15f;
|
||||
this->max2_ = max2 = roundf(max2 / 0.15f) * 0.15f;
|
||||
this->min3_ = min3 = this->max3_ = max3 = this->min4_ = min4 = this->max4_ = max4 = -1;
|
||||
|
||||
char buf[72]; // max 72: "detRangeCfg -1 "(15) + 8 * (float(5) + space(1)) + null
|
||||
snprintf(buf, sizeof(buf), "detRangeCfg -1 %.0f %.0f %.0f %.0f", min1 / 0.15f, max1 / 0.15f, min2 / 0.15f,
|
||||
max2 / 0.15f);
|
||||
this->cmd_ = buf;
|
||||
} else if (min4 < 0 || max4 < 0) {
|
||||
this->min1_ = min1 = roundf(min1 / 0.15f) * 0.15f;
|
||||
this->max1_ = max1 = roundf(max1 / 0.15f) * 0.15f;
|
||||
this->min2_ = min2 = roundf(min2 / 0.15f) * 0.15f;
|
||||
this->max2_ = max2 = roundf(max2 / 0.15f) * 0.15f;
|
||||
this->min3_ = min3 = roundf(min3 / 0.15f) * 0.15f;
|
||||
this->max3_ = max3 = roundf(max3 / 0.15f) * 0.15f;
|
||||
this->min4_ = min4 = this->max4_ = max4 = -1;
|
||||
|
||||
char buf[72]; // max 72: "detRangeCfg -1 "(15) + 8 * (float(5) + space(1)) + null
|
||||
snprintf(buf, sizeof(buf), "detRangeCfg -1 %.0f %.0f %.0f %.0f %.0f %.0f", min1 / 0.15f, max1 / 0.15f, min2 / 0.15f,
|
||||
max2 / 0.15f, min3 / 0.15f, max3 / 0.15f);
|
||||
this->cmd_ = buf;
|
||||
} else {
|
||||
this->min1_ = min1 = roundf(min1 / 0.15f) * 0.15f;
|
||||
this->max1_ = max1 = roundf(max1 / 0.15f) * 0.15f;
|
||||
this->min2_ = min2 = roundf(min2 / 0.15f) * 0.15f;
|
||||
this->max2_ = max2 = roundf(max2 / 0.15f) * 0.15f;
|
||||
this->min3_ = min3 = roundf(min3 / 0.15f) * 0.15f;
|
||||
this->max3_ = max3 = roundf(max3 / 0.15f) * 0.15f;
|
||||
this->min4_ = min4 = roundf(min4 / 0.15f) * 0.15f;
|
||||
this->max4_ = max4 = roundf(max4 / 0.15f) * 0.15f;
|
||||
|
||||
char buf[72]; // max 72: "detRangeCfg -1 "(15) + 8 * (float(5) + space(1)) + null
|
||||
snprintf(buf, sizeof(buf), "detRangeCfg -1 %.0f %.0f %.0f %.0f %.0f %.0f %.0f %.0f", min1 / 0.15f, max1 / 0.15f,
|
||||
min2 / 0.15f, max2 / 0.15f, min3 / 0.15f, max3 / 0.15f, min4 / 0.15f, max4 / 0.15f);
|
||||
this->cmd_ = buf;
|
||||
steps[0] = steps[1] = 0;
|
||||
}
|
||||
if (this->enqueue(CommandType::COMMAND_TYPE_DET_RANGE_CFG))
|
||||
memcpy(this->det_range_steps_, steps, sizeof(steps));
|
||||
}
|
||||
|
||||
this->min1_ = min1;
|
||||
this->max1_ = max1;
|
||||
this->min2_ = min2;
|
||||
this->max2_ = max2;
|
||||
this->min3_ = min3;
|
||||
this->max3_ = max3;
|
||||
this->min4_ = min4;
|
||||
this->max4_ = max4;
|
||||
};
|
||||
void DfrobotSen0395Component::enqueue_set_latency(float delay_after_detection, float delay_after_disappear) {
|
||||
if (!this->enqueue(CommandType::COMMAND_TYPE_SET_LATENCY))
|
||||
return;
|
||||
// 1638.375 s is 65535 steps
|
||||
this->latency_steps_[0] =
|
||||
static_cast<uint16_t>(clamp(std::round(delay_after_detection / LATENCY_STEP_S), 0.0f, 65535.0f));
|
||||
this->latency_steps_[1] =
|
||||
static_cast<uint16_t>(clamp(std::round(delay_after_disappear / LATENCY_STEP_S), 0.0f, 65535.0f));
|
||||
}
|
||||
|
||||
uint8_t DetRangeCfgCommand::on_message(std::string &message) {
|
||||
if (message == "sensor is not stopped") {
|
||||
ESP_LOGE(TAG, "Cannot configure range config. Sensor is not stopped!");
|
||||
return 1; // Command done
|
||||
} else if (message == "Done") {
|
||||
ESP_LOGI(TAG,
|
||||
"Updated detection area config:\n"
|
||||
" Detection area 1 from %.02fm to %.02fm.",
|
||||
this->min1_, this->max1_);
|
||||
if (this->min2_ >= 0 && this->max2_ >= 0) {
|
||||
ESP_LOGI(TAG, " Detection area 2 from %.02fm to %.02fm.", this->min2_, this->max2_);
|
||||
void DfrobotSen0395Component::format_command_(const Command &cmd, char *buf, size_t size) {
|
||||
const char *text = "";
|
||||
switch (cmd.type) {
|
||||
case CommandType::COMMAND_TYPE_POWER:
|
||||
text = cmd.arg ? "sensorStart" : "sensorStop";
|
||||
break;
|
||||
case CommandType::COMMAND_TYPE_DET_RANGE_CFG: {
|
||||
size_t pos = buf_append_printf(buf, size, 0, "detRangeCfg -1");
|
||||
for (size_t i = 0; i < 8 && this->det_range_steps_[i] >= 0; i += 2) {
|
||||
pos = buf_append_printf(buf, size, pos, " %d %d", this->det_range_steps_[i], this->det_range_steps_[i + 1]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (this->min3_ >= 0 && this->max3_ >= 0) {
|
||||
ESP_LOGI(TAG, " Detection area 3 from %.02fm to %.02fm.", this->min3_, this->max3_);
|
||||
}
|
||||
if (this->min4_ >= 0 && this->max4_ >= 0) {
|
||||
ESP_LOGI(TAG, " Detection area 4 from %.02fm to %.02fm.", this->min4_, this->max4_);
|
||||
}
|
||||
ESP_LOGD(TAG, " Used command: %s", this->cmd_.c_str());
|
||||
return 1; // Command done
|
||||
case CommandType::COMMAND_TYPE_SET_LATENCY:
|
||||
snprintf(buf, size, "setLatency %.03f %.03f", this->latency_steps_[0] * LATENCY_STEP_S,
|
||||
this->latency_steps_[1] * LATENCY_STEP_S);
|
||||
return;
|
||||
case CommandType::COMMAND_TYPE_SENSOR_CFG_START:
|
||||
snprintf(buf, size, "sensorCfgStart %d", cmd.arg);
|
||||
return;
|
||||
case CommandType::COMMAND_TYPE_FACTORY_RESET:
|
||||
text = "factoryReset 0x45670123 0xCDEF89AB 0x956128C6 0xDF54AC89";
|
||||
break;
|
||||
case CommandType::COMMAND_TYPE_RESET_SYSTEM:
|
||||
text = "resetSystem";
|
||||
break;
|
||||
case CommandType::COMMAND_TYPE_SAVE_CFG:
|
||||
text = "saveCfg 0x45670123 0xCDEF89AB 0x956128C6 0xDF54AC89";
|
||||
break;
|
||||
case CommandType::COMMAND_TYPE_LED_MODE:
|
||||
text = cmd.arg ? "setLedMode 1 0" : "setLedMode 1 1";
|
||||
break;
|
||||
case CommandType::COMMAND_TYPE_UART_OUTPUT:
|
||||
text = cmd.arg ? "setUartOutput 1 1" : "setUartOutput 1 0";
|
||||
break;
|
||||
case CommandType::COMMAND_TYPE_SENSITIVITY:
|
||||
snprintf(buf, size, "setSensitivity %d", cmd.arg);
|
||||
return;
|
||||
case CommandType::COMMAND_TYPE_READ_STATE:
|
||||
break;
|
||||
}
|
||||
return 0; // Command not done yet.
|
||||
snprintf(buf, size, "%s", text);
|
||||
}
|
||||
|
||||
SetLatencyCommand::SetLatencyCommand(float delay_after_detection, float delay_after_disappear) {
|
||||
delay_after_detection = std::round(delay_after_detection / 0.025f) * 0.025f;
|
||||
delay_after_disappear = std::round(delay_after_disappear / 0.025f) * 0.025f;
|
||||
this->delay_after_detection_ = clamp(delay_after_detection, 0.0f, 1638.375f);
|
||||
this->delay_after_disappear_ = clamp(delay_after_disappear, 0.0f, 1638.375f);
|
||||
// max 32: "setLatency "(11) + float(8) + " "(1) + float(8) + null, rounded to 32
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof(buf), "setLatency %.03f %.03f", this->delay_after_detection_, this->delay_after_disappear_);
|
||||
this->cmd_ = buf;
|
||||
};
|
||||
bool DfrobotSen0395Component::handle_reply_(const Command &cmd) {
|
||||
const char *message = this->read_buffer_;
|
||||
auto is = [message](const char *text) { return strcmp(message, text) == 0; };
|
||||
const bool not_stopped = is("sensor is not stopped");
|
||||
const bool done = is("Done");
|
||||
char used[72];
|
||||
|
||||
uint8_t SetLatencyCommand::on_message(std::string &message) {
|
||||
if (message == "sensor is not stopped") {
|
||||
ESP_LOGE(TAG, "Cannot configure output latency. Sensor is not stopped!");
|
||||
return 1; // Command done
|
||||
} else if (message == "Done") {
|
||||
ESP_LOGI(TAG,
|
||||
"Updated output latency config:\n"
|
||||
" Signal that someone was detected is delayed by %.03f s.\n"
|
||||
" Signal that nobody is detected anymore is delayed by %.03f s.",
|
||||
this->delay_after_detection_, this->delay_after_disappear_);
|
||||
ESP_LOGD(TAG, " Used command: %s", this->cmd_.c_str());
|
||||
return 1; // Command done
|
||||
}
|
||||
return 0; // Command not done yet
|
||||
}
|
||||
switch (cmd.type) {
|
||||
case CommandType::COMMAND_TYPE_READ_STATE:
|
||||
return true;
|
||||
|
||||
uint8_t SensorCfgStartCommand::on_message(std::string &message) {
|
||||
if (message == "sensor is not stopped") {
|
||||
ESP_LOGE(TAG, "Cannot configure sensor startup behavior. Sensor is not stopped!");
|
||||
return 1; // Command done
|
||||
} else if (message == "Done") {
|
||||
ESP_LOGI(TAG, "Updated sensor startup behavior:");
|
||||
if (startup_mode_) {
|
||||
this->parent_->set_start_after_boot(true);
|
||||
ESP_LOGI(TAG, "Sensor will start automatically after power-on.");
|
||||
} else {
|
||||
this->parent_->set_start_after_boot(false);
|
||||
ESP_LOGI(TAG, "Sensor needs to be started manually after power-on.");
|
||||
}
|
||||
ESP_LOGD(TAG, "Used command: %s", this->cmd_.c_str());
|
||||
return 1; // Command done
|
||||
}
|
||||
return 0; // Command not done yet
|
||||
}
|
||||
case CommandType::COMMAND_TYPE_POWER:
|
||||
if (is("sensor stopped already")) {
|
||||
this->set_active(false);
|
||||
ESP_LOGI(TAG, "Stopped sensor (already stopped)");
|
||||
} else if (is("sensor started already")) {
|
||||
this->set_active(true);
|
||||
ESP_LOGI(TAG, "Started sensor (already started)");
|
||||
} else if (is("new parameter isn't save, can't startSensor")) {
|
||||
this->set_active(false);
|
||||
ESP_LOGE(TAG, "Can't start sensor! (Use SaveCfgCommand to save config first)");
|
||||
} else if (done) {
|
||||
this->set_active(cmd.arg);
|
||||
if (cmd.arg) {
|
||||
ESP_LOGI(TAG, "Started sensor");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Stopped sensor");
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
uint8_t FactoryResetCommand::on_message(std::string &message) {
|
||||
if (message == "sensor is not stopped") {
|
||||
ESP_LOGE(TAG, "Cannot factory reset. Sensor is not stopped!");
|
||||
return 1; // Command done
|
||||
} else if (message == "Done") {
|
||||
ESP_LOGI(TAG, "Sensor factory reset done.");
|
||||
return 1; // Command done
|
||||
}
|
||||
return 0; // Command not done yet
|
||||
}
|
||||
case CommandType::COMMAND_TYPE_DET_RANGE_CFG:
|
||||
if (not_stopped) {
|
||||
ESP_LOGE(TAG, "Cannot configure range config. Sensor is not stopped!");
|
||||
return true;
|
||||
}
|
||||
if (!done)
|
||||
return false;
|
||||
ESP_LOGI(TAG,
|
||||
"Updated detection area config:\n"
|
||||
" Detection area 1 from %.02fm to %.02fm.",
|
||||
this->det_range_steps_[0] * DET_RANGE_STEP_M, this->det_range_steps_[1] * DET_RANGE_STEP_M);
|
||||
for (size_t i = 2; i < 8 && this->det_range_steps_[i] >= 0; i += 2) {
|
||||
ESP_LOGI(TAG, " Detection area %u from %.02fm to %.02fm.", static_cast<unsigned>(i / 2 + 1),
|
||||
this->det_range_steps_[i] * DET_RANGE_STEP_M, this->det_range_steps_[i + 1] * DET_RANGE_STEP_M);
|
||||
}
|
||||
break;
|
||||
|
||||
uint8_t ResetSystemCommand::on_message(std::string &message) {
|
||||
if (message == "leapMMW:/>") {
|
||||
ESP_LOGI(TAG, "Restarted sensor.");
|
||||
return 1; // Command done
|
||||
}
|
||||
return 0; // Command not done yet
|
||||
}
|
||||
case CommandType::COMMAND_TYPE_SET_LATENCY:
|
||||
if (not_stopped) {
|
||||
ESP_LOGE(TAG, "Cannot configure output latency. Sensor is not stopped!");
|
||||
return true;
|
||||
}
|
||||
if (!done)
|
||||
return false;
|
||||
ESP_LOGI(TAG,
|
||||
"Updated output latency config:\n"
|
||||
" Signal that someone was detected is delayed by %.03f s.\n"
|
||||
" Signal that nobody is detected anymore is delayed by %.03f s.",
|
||||
this->latency_steps_[0] * LATENCY_STEP_S, this->latency_steps_[1] * LATENCY_STEP_S);
|
||||
break;
|
||||
|
||||
uint8_t SaveCfgCommand::on_message(std::string &message) {
|
||||
if (message == "no parameter has changed") {
|
||||
ESP_LOGI(TAG, "Not saving config (no parameter changed).");
|
||||
return 1; // Command done
|
||||
} else if (message == "Done") {
|
||||
ESP_LOGI(TAG, "Saved config. Saving a lot may damage the sensor.");
|
||||
return 1; // Command done
|
||||
}
|
||||
return 0; // Command not done yet
|
||||
}
|
||||
case CommandType::COMMAND_TYPE_SENSOR_CFG_START:
|
||||
if (not_stopped) {
|
||||
ESP_LOGE(TAG, "Cannot configure sensor startup behavior. Sensor is not stopped!");
|
||||
return true;
|
||||
}
|
||||
if (!done)
|
||||
return false;
|
||||
ESP_LOGI(TAG, "Updated sensor startup behavior:");
|
||||
this->set_start_after_boot(cmd.arg);
|
||||
if (cmd.arg) {
|
||||
ESP_LOGI(TAG, "Sensor will start automatically after power-on.");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Sensor needs to be started manually after power-on.");
|
||||
}
|
||||
break;
|
||||
|
||||
uint8_t LedModeCommand::on_message(std::string &message) {
|
||||
if (message == "sensor is not stopped") {
|
||||
ESP_LOGE(TAG, "Cannot set led mode. Sensor is not stopped!");
|
||||
return 1; // Command done
|
||||
} else if (message == "Done") {
|
||||
ESP_LOGI(TAG, "Set led mode done.");
|
||||
if (this->active_) {
|
||||
this->parent_->set_led_active(true);
|
||||
ESP_LOGI(TAG, "Sensor LED will blink.");
|
||||
} else {
|
||||
this->parent_->set_led_active(false);
|
||||
ESP_LOGI(TAG, "Turned off LED.");
|
||||
}
|
||||
ESP_LOGD(TAG, "Used command: %s", this->cmd_.c_str());
|
||||
return 1; // Command done
|
||||
}
|
||||
return 0; // Command not done yet
|
||||
}
|
||||
case CommandType::COMMAND_TYPE_FACTORY_RESET:
|
||||
if (not_stopped) {
|
||||
ESP_LOGE(TAG, "Cannot factory reset. Sensor is not stopped!");
|
||||
return true;
|
||||
}
|
||||
if (!done)
|
||||
return false;
|
||||
ESP_LOGI(TAG, "Sensor factory reset done.");
|
||||
return true;
|
||||
|
||||
uint8_t UartOutputCommand::on_message(std::string &message) {
|
||||
if (message == "sensor is not stopped") {
|
||||
ESP_LOGE(TAG, "Cannot set uart output mode. Sensor is not stopped!");
|
||||
return 1; // Command done
|
||||
} else if (message == "Done") {
|
||||
ESP_LOGI(TAG, "Set uart mode done.");
|
||||
if (this->active_) {
|
||||
this->parent_->set_uart_presence_active(true);
|
||||
ESP_LOGI(TAG, "Presence information is sent via UART and GPIO.");
|
||||
} else {
|
||||
this->parent_->set_uart_presence_active(false);
|
||||
ESP_LOGI(TAG, "Presence information is only sent via GPIO.");
|
||||
}
|
||||
ESP_LOGD(TAG, "Used command: %s", this->cmd_.c_str());
|
||||
return 1; // Command done
|
||||
}
|
||||
return 0; // Command not done yet
|
||||
}
|
||||
case CommandType::COMMAND_TYPE_RESET_SYSTEM:
|
||||
if (!is("leapMMW:/>"))
|
||||
return false;
|
||||
ESP_LOGI(TAG, "Restarted sensor.");
|
||||
return true;
|
||||
|
||||
uint8_t SensitivityCommand::on_message(std::string &message) {
|
||||
if (message == "sensor is not stopped") {
|
||||
ESP_LOGE(TAG, "Cannot set sensitivity. Sensor is not stopped!");
|
||||
return 1; // Command done
|
||||
} else if (message == "Done") {
|
||||
ESP_LOGI(TAG, "Set sensitivity done. Set to value %d.", this->sensitivity_);
|
||||
ESP_LOGD(TAG, "Used command: %s", this->cmd_.c_str());
|
||||
return 1; // Command done
|
||||
case CommandType::COMMAND_TYPE_SAVE_CFG:
|
||||
if (is("no parameter has changed")) {
|
||||
ESP_LOGI(TAG, "Not saving config (no parameter changed).");
|
||||
return true;
|
||||
}
|
||||
if (!done)
|
||||
return false;
|
||||
ESP_LOGI(TAG, "Saved config. Saving a lot may damage the sensor.");
|
||||
return true;
|
||||
|
||||
case CommandType::COMMAND_TYPE_LED_MODE:
|
||||
if (not_stopped) {
|
||||
ESP_LOGE(TAG, "Cannot set led mode. Sensor is not stopped!");
|
||||
return true;
|
||||
}
|
||||
if (!done)
|
||||
return false;
|
||||
ESP_LOGI(TAG, "Set led mode done.");
|
||||
this->set_led_active(cmd.arg);
|
||||
if (cmd.arg) {
|
||||
ESP_LOGI(TAG, "Sensor LED will blink.");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Turned off LED.");
|
||||
}
|
||||
break;
|
||||
|
||||
case CommandType::COMMAND_TYPE_UART_OUTPUT:
|
||||
if (not_stopped) {
|
||||
ESP_LOGE(TAG, "Cannot set uart output mode. Sensor is not stopped!");
|
||||
return true;
|
||||
}
|
||||
if (!done)
|
||||
return false;
|
||||
ESP_LOGI(TAG, "Set uart mode done.");
|
||||
this->set_uart_presence_active(cmd.arg);
|
||||
if (cmd.arg) {
|
||||
ESP_LOGI(TAG, "Presence information is sent via UART and GPIO.");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Presence information is only sent via GPIO.");
|
||||
}
|
||||
break;
|
||||
|
||||
case CommandType::COMMAND_TYPE_SENSITIVITY:
|
||||
if (not_stopped) {
|
||||
ESP_LOGE(TAG, "Cannot set sensitivity. Sensor is not stopped!");
|
||||
return true;
|
||||
}
|
||||
if (!done)
|
||||
return false;
|
||||
ESP_LOGI(TAG, "Set sensitivity done. Set to value %d.", cmd.arg);
|
||||
break;
|
||||
}
|
||||
return 0; // Command not done yet
|
||||
|
||||
this->format_command_(cmd, used, sizeof(used));
|
||||
ESP_LOGD(TAG, "Used command: %s", used);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace esphome::dfrobot_sen0395
|
||||
|
||||
@@ -1,152 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome::dfrobot_sen0395 {
|
||||
|
||||
class DfrobotSen0395Component;
|
||||
|
||||
// Use command queue and time stamps to avoid blocking.
|
||||
// When component has run time, check if minimum time (1s) between
|
||||
// commands has passed. After that run a command from the queue.
|
||||
class Command {
|
||||
public:
|
||||
virtual ~Command() = default;
|
||||
virtual uint8_t execute(DfrobotSen0395Component *parent);
|
||||
virtual uint8_t on_message(std::string &message) = 0;
|
||||
|
||||
protected:
|
||||
DfrobotSen0395Component *parent_{nullptr};
|
||||
std::string cmd_;
|
||||
bool cmd_sent_{false};
|
||||
int8_t retries_left_{2};
|
||||
uint32_t cmd_duration_ms_{1000};
|
||||
uint32_t timeout_ms_{1500};
|
||||
enum class CommandType : uint8_t {
|
||||
COMMAND_TYPE_READ_STATE,
|
||||
COMMAND_TYPE_POWER,
|
||||
COMMAND_TYPE_DET_RANGE_CFG,
|
||||
COMMAND_TYPE_SET_LATENCY,
|
||||
COMMAND_TYPE_SENSOR_CFG_START,
|
||||
COMMAND_TYPE_FACTORY_RESET,
|
||||
COMMAND_TYPE_RESET_SYSTEM,
|
||||
COMMAND_TYPE_SAVE_CFG,
|
||||
COMMAND_TYPE_LED_MODE,
|
||||
COMMAND_TYPE_UART_OUTPUT,
|
||||
COMMAND_TYPE_SENSITIVITY,
|
||||
};
|
||||
|
||||
class ReadStateCommand : public Command {
|
||||
public:
|
||||
ReadStateCommand() { timeout_ms_ = 500; }
|
||||
uint8_t execute(DfrobotSen0395Component *parent) override;
|
||||
uint8_t on_message(std::string &message) override;
|
||||
};
|
||||
|
||||
class PowerCommand : public Command {
|
||||
public:
|
||||
PowerCommand(bool power_on) : power_on_(power_on) {
|
||||
if (power_on) {
|
||||
cmd_ = "sensorStart";
|
||||
} else {
|
||||
cmd_ = "sensorStop";
|
||||
}
|
||||
};
|
||||
uint8_t on_message(std::string &message) override;
|
||||
|
||||
protected:
|
||||
bool power_on_;
|
||||
};
|
||||
|
||||
class DetRangeCfgCommand : public Command {
|
||||
public:
|
||||
DetRangeCfgCommand(float min1, float max1, float min2, float max2, float min3, float max3, float min4, float max4);
|
||||
uint8_t on_message(std::string &message) override;
|
||||
|
||||
protected:
|
||||
float min1_, max1_, min2_, max2_, min3_, max3_, min4_, max4_;
|
||||
// TODO: Set min max values in component, so they can be published as sensor.
|
||||
};
|
||||
|
||||
class SetLatencyCommand : public Command {
|
||||
public:
|
||||
SetLatencyCommand(float delay_after_detection, float delay_after_disappear);
|
||||
uint8_t on_message(std::string &message) override;
|
||||
|
||||
protected:
|
||||
float delay_after_detection_;
|
||||
float delay_after_disappear_;
|
||||
};
|
||||
|
||||
class SensorCfgStartCommand : public Command {
|
||||
public:
|
||||
SensorCfgStartCommand(bool startup_mode) : startup_mode_(startup_mode) {
|
||||
char tmp_cmd[20]; // "sensorCfgStart " (15) + "0/1" (1) + null = 17
|
||||
buf_append_printf(tmp_cmd, sizeof(tmp_cmd), 0, "sensorCfgStart %d", startup_mode);
|
||||
cmd_ = std::string(tmp_cmd);
|
||||
}
|
||||
uint8_t on_message(std::string &message) override;
|
||||
|
||||
protected:
|
||||
bool startup_mode_;
|
||||
};
|
||||
|
||||
class FactoryResetCommand : public Command {
|
||||
public:
|
||||
FactoryResetCommand() { cmd_ = "factoryReset 0x45670123 0xCDEF89AB 0x956128C6 0xDF54AC89"; };
|
||||
uint8_t on_message(std::string &message) override;
|
||||
};
|
||||
|
||||
class ResetSystemCommand : public Command {
|
||||
public:
|
||||
ResetSystemCommand() { cmd_ = "resetSystem"; }
|
||||
uint8_t on_message(std::string &message) override;
|
||||
};
|
||||
|
||||
class SaveCfgCommand : public Command {
|
||||
public:
|
||||
SaveCfgCommand() {
|
||||
cmd_ = "saveCfg 0x45670123 0xCDEF89AB 0x956128C6 0xDF54AC89";
|
||||
cmd_duration_ms_ = 3000;
|
||||
timeout_ms_ = 3500;
|
||||
}
|
||||
uint8_t on_message(std::string &message) override;
|
||||
};
|
||||
|
||||
class LedModeCommand : public Command {
|
||||
public:
|
||||
LedModeCommand(bool active) : active_(active) {
|
||||
if (active) {
|
||||
cmd_ = "setLedMode 1 0";
|
||||
} else {
|
||||
cmd_ = "setLedMode 1 1";
|
||||
}
|
||||
};
|
||||
uint8_t on_message(std::string &message) override;
|
||||
|
||||
protected:
|
||||
bool active_;
|
||||
};
|
||||
|
||||
class UartOutputCommand : public Command {
|
||||
public:
|
||||
UartOutputCommand(bool active) : active_(active) {
|
||||
if (active) {
|
||||
cmd_ = "setUartOutput 1 1";
|
||||
} else {
|
||||
cmd_ = "setUartOutput 1 0";
|
||||
}
|
||||
};
|
||||
uint8_t on_message(std::string &message) override;
|
||||
|
||||
protected:
|
||||
bool active_;
|
||||
};
|
||||
|
||||
class SensitivityCommand : public Command {
|
||||
public:
|
||||
SensitivityCommand(uint8_t sensitivity) : sensitivity_(sensitivity) {
|
||||
if (sensitivity > 9)
|
||||
sensitivity_ = sensitivity = 9;
|
||||
char tmp_cmd[20]; // "setSensitivity " (15) + "0-9" (1) + null = 17
|
||||
buf_append_printf(tmp_cmd, sizeof(tmp_cmd), 0, "setSensitivity %d", sensitivity);
|
||||
cmd_ = std::string(tmp_cmd);
|
||||
};
|
||||
uint8_t on_message(std::string &message) override;
|
||||
|
||||
protected:
|
||||
uint8_t sensitivity_;
|
||||
/// A queued command. arg is the on/off flag, startup mode or sensitivity; the detection range and
|
||||
/// latency values are held by the component.
|
||||
struct Command {
|
||||
CommandType type;
|
||||
uint8_t arg;
|
||||
};
|
||||
|
||||
} // namespace esphome::dfrobot_sen0395
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "dfrobot_sen0395.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
@@ -23,20 +25,84 @@ void DfrobotSen0395Component::dump_config() {
|
||||
}
|
||||
|
||||
void DfrobotSen0395Component::loop() {
|
||||
if (cmd_queue_.is_empty()) {
|
||||
if (this->cmd_queue_.is_empty()) {
|
||||
// Command queue empty. Read sensor state.
|
||||
cmd_queue_.enqueue(make_unique<ReadStateCommand>());
|
||||
this->cmd_queue_.enqueue({CommandType::COMMAND_TYPE_READ_STATE, 0});
|
||||
}
|
||||
|
||||
// Commands are non-blocking and need to be called repeatedly.
|
||||
if (cmd_queue_.process(this)) {
|
||||
if (this->run_command_(this->cmd_queue_.front())) {
|
||||
// Dequeue if command is done
|
||||
cmd_queue_.dequeue();
|
||||
this->cmd_queue_.dequeue();
|
||||
this->cmd_sent_ = false;
|
||||
this->retries_left_ = 2;
|
||||
}
|
||||
}
|
||||
|
||||
int8_t DfrobotSen0395Component::enqueue(std::unique_ptr<Command> cmd) {
|
||||
return cmd_queue_.enqueue(std::move(cmd)); // Transfer ownership using std::move
|
||||
bool DfrobotSen0395Component::enqueue(CommandType type, uint8_t arg) {
|
||||
if (type == CommandType::COMMAND_TYPE_SENSITIVITY && arg > 9)
|
||||
arg = 9;
|
||||
return this->cmd_queue_.enqueue({type, arg});
|
||||
}
|
||||
|
||||
bool DfrobotSen0395Component::run_command_(const Command &cmd) {
|
||||
if (cmd.type == CommandType::COMMAND_TYPE_READ_STATE)
|
||||
return this->run_read_state_();
|
||||
|
||||
const bool save_cfg = cmd.type == CommandType::COMMAND_TYPE_SAVE_CFG;
|
||||
if (!this->cmd_sent_) {
|
||||
// The interval between two commands must be larger than the command duration
|
||||
if (millis() - this->ts_last_cmd_sent_ > (save_cfg ? 3000 : 1000)) {
|
||||
char buf[72]; // "detRangeCfg -1" + 8 * " 32767" + null
|
||||
this->format_command_(cmd, buf, sizeof(buf));
|
||||
this->write_str(buf);
|
||||
this->ts_last_cmd_sent_ = millis();
|
||||
this->cmd_sent_ = true;
|
||||
}
|
||||
return false; // Command not done yet
|
||||
}
|
||||
|
||||
if (this->read_message_()) {
|
||||
if (strstr(this->read_buffer_, "is not recognized as a CLI command") != nullptr) {
|
||||
ESP_LOGD(TAG, "Command not recognized properly by sensor");
|
||||
} else if (this->handle_reply_(cmd)) {
|
||||
this->find_prompt_();
|
||||
return true; // Command done
|
||||
} else {
|
||||
return false; // Command not done yet
|
||||
}
|
||||
} else if (millis() - this->ts_last_cmd_sent_ > (save_cfg ? 3500 : 1500)) {
|
||||
ESP_LOGD(TAG, "Command timeout");
|
||||
if (this->retries_left_ == 0)
|
||||
return true; // Command done
|
||||
} else {
|
||||
return false; // Command not done yet
|
||||
}
|
||||
|
||||
if (this->retries_left_ == 0) {
|
||||
this->find_prompt_();
|
||||
return true; // Command done
|
||||
}
|
||||
this->retries_left_--;
|
||||
this->cmd_sent_ = false;
|
||||
ESP_LOGD(TAG, "Retrying");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DfrobotSen0395Component::run_read_state_() {
|
||||
if (this->read_message_()) {
|
||||
if (strstr(this->read_buffer_, "$JYBSS,0, , , *") != nullptr) {
|
||||
this->set_detected_(false);
|
||||
this->set_active(true);
|
||||
return true;
|
||||
}
|
||||
if (strstr(this->read_buffer_, "$JYBSS,1, , , *") != nullptr) {
|
||||
this->set_detected_(true);
|
||||
this->set_active(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return millis() - this->ts_last_cmd_sent_ > 500; // Done on timeout
|
||||
}
|
||||
|
||||
uint8_t DfrobotSen0395Component::read_message_() {
|
||||
@@ -71,26 +137,12 @@ uint8_t DfrobotSen0395Component::read_message_() {
|
||||
}
|
||||
|
||||
uint8_t DfrobotSen0395Component::find_prompt_() {
|
||||
if (this->read_message_()) {
|
||||
std::string message(this->read_buffer_);
|
||||
if (message.rfind("leapMMW:/>") != std::string::npos) {
|
||||
return 1; // Prompt found
|
||||
}
|
||||
if (this->read_message_() && strstr(this->read_buffer_, "leapMMW:/>") != nullptr) {
|
||||
return 1; // Prompt found
|
||||
}
|
||||
return 0; // Not found yet
|
||||
}
|
||||
|
||||
uint8_t DfrobotSen0395Component::send_cmd_(const char *cmd, uint32_t duration) {
|
||||
// The interval between two commands must be larger than the specified duration (in ms).
|
||||
if (millis() - ts_last_cmd_sent_ > duration) {
|
||||
this->write_str(cmd);
|
||||
ts_last_cmd_sent_ = millis();
|
||||
return 1; // Command sent
|
||||
}
|
||||
// Could not send command yet as command duration did not fully pass yet.
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DfrobotSen0395Component::set_detected_(bool detected) {
|
||||
this->detected_ = detected;
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
@@ -99,43 +151,26 @@ void DfrobotSen0395Component::set_detected_(bool detected) {
|
||||
#endif
|
||||
}
|
||||
|
||||
int8_t CircularCommandQueue::enqueue(std::unique_ptr<Command> cmd) {
|
||||
bool CircularCommandQueue::enqueue(Command cmd) {
|
||||
if (this->is_full()) {
|
||||
ESP_LOGE(TAG, "Command queue is full");
|
||||
return -1;
|
||||
} else if (this->is_empty()) {
|
||||
front_++;
|
||||
return false;
|
||||
}
|
||||
rear_ = (rear_ + 1) % COMMAND_QUEUE_SIZE;
|
||||
commands_[rear_] = std::move(cmd); // Transfer ownership using std::move
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::unique_ptr<Command> CircularCommandQueue::dequeue() {
|
||||
if (this->is_empty())
|
||||
return nullptr;
|
||||
std::unique_ptr<Command> dequeued_cmd = std::move(commands_[front_]);
|
||||
if (front_ == rear_) {
|
||||
front_ = -1;
|
||||
rear_ = -1;
|
||||
} else {
|
||||
front_ = (front_ + 1) % COMMAND_QUEUE_SIZE;
|
||||
}
|
||||
|
||||
return dequeued_cmd;
|
||||
this->front_ = 0;
|
||||
this->rear_ = (this->rear_ + 1) % COMMAND_QUEUE_SIZE;
|
||||
this->commands_[this->rear_] = cmd;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CircularCommandQueue::is_empty() { return front_ == -1; }
|
||||
|
||||
bool CircularCommandQueue::is_full() { return (rear_ + 1) % COMMAND_QUEUE_SIZE == front_; }
|
||||
|
||||
// Run execute method of first in line command.
|
||||
// Execute is non-blocking and has to be called until it returns 1.
|
||||
uint8_t CircularCommandQueue::process(DfrobotSen0395Component *parent) {
|
||||
if (!is_empty()) {
|
||||
return commands_[front_]->execute(parent);
|
||||
void CircularCommandQueue::dequeue() {
|
||||
if (this->is_empty())
|
||||
return;
|
||||
if (this->front_ == this->rear_) {
|
||||
this->front_ = -1;
|
||||
this->rear_ = -1;
|
||||
} else {
|
||||
return 1;
|
||||
this->front_ = (this->front_ + 1) % COMMAND_QUEUE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,16 +24,16 @@ static const uint8_t COMMAND_QUEUE_SIZE = 20;
|
||||
|
||||
class CircularCommandQueue {
|
||||
public:
|
||||
int8_t enqueue(std::unique_ptr<Command> cmd);
|
||||
std::unique_ptr<Command> dequeue();
|
||||
bool is_empty();
|
||||
bool is_full();
|
||||
uint8_t process(DfrobotSen0395Component *parent);
|
||||
bool enqueue(Command cmd);
|
||||
void dequeue();
|
||||
bool is_empty() const { return this->front_ == -1; }
|
||||
bool is_full() const { return (this->rear_ + 1) % COMMAND_QUEUE_SIZE == this->front_; }
|
||||
const Command &front() const { return this->commands_[this->front_]; }
|
||||
|
||||
protected:
|
||||
int front_{-1};
|
||||
int rear_{-1};
|
||||
std::unique_ptr<Command> commands_[COMMAND_QUEUE_SIZE];
|
||||
int8_t front_{-1};
|
||||
int8_t rear_{-1};
|
||||
Command commands_[COMMAND_QUEUE_SIZE];
|
||||
};
|
||||
|
||||
class DfrobotSen0395Component final : public uart::UARTDevice, public Component {
|
||||
@@ -93,9 +93,14 @@ class DfrobotSen0395Component final : public uart::UARTDevice, public Component
|
||||
}
|
||||
#endif
|
||||
|
||||
int8_t enqueue(std::unique_ptr<Command> cmd);
|
||||
bool enqueue(CommandType type, uint8_t arg = 0);
|
||||
/// Queue a detection range config; a negative value ends the list of segments
|
||||
void enqueue_det_range_cfg(float min1, float max1, float min2, float max2, float min3, float max3, float min4,
|
||||
float max4);
|
||||
/// Queue an output latency config, both delays in seconds
|
||||
void enqueue_set_latency(float delay_after_detection, float delay_after_disappear);
|
||||
/// Queue a sensor reset
|
||||
void reset_system() { this->enqueue(make_unique<ResetSystemCommand>()); }
|
||||
void reset_system() { this->enqueue(CommandType::COMMAND_TYPE_RESET_SYSTEM); }
|
||||
|
||||
protected:
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
@@ -109,17 +114,25 @@ class DfrobotSen0395Component final : public uart::UARTDevice, public Component
|
||||
bool start_after_boot_{false};
|
||||
char read_buffer_[MMWAVE_READ_BUFFER_LENGTH];
|
||||
size_t read_pos_{0};
|
||||
bool cmd_sent_{false};
|
||||
int8_t retries_left_{2};
|
||||
CircularCommandQueue 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.
|
||||
int16_t det_range_steps_[8]{};
|
||||
uint16_t latency_steps_[2]{};
|
||||
|
||||
uint8_t read_message_();
|
||||
uint8_t find_prompt_();
|
||||
uint8_t send_cmd_(const char *cmd, uint32_t duration);
|
||||
/// Run the command at the front of the queue; true when it is done
|
||||
bool run_command_(const Command &cmd);
|
||||
bool run_read_state_();
|
||||
/// 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);
|
||||
|
||||
friend class Command;
|
||||
friend class ReadStateCommand;
|
||||
};
|
||||
|
||||
} // namespace esphome::dfrobot_sen0395
|
||||
|
||||
@@ -2,18 +2,18 @@
|
||||
|
||||
namespace esphome::dfrobot_sen0395 {
|
||||
|
||||
void Sen0395PowerSwitch::write_state(bool state) { this->parent_->enqueue(make_unique<PowerCommand>(state)); }
|
||||
void Sen0395PowerSwitch::write_state(bool state) { this->parent_->enqueue(CommandType::COMMAND_TYPE_POWER, state); }
|
||||
|
||||
void Sen0395LedSwitch::write_state(bool state) {
|
||||
bool was_active = false;
|
||||
if (this->parent_->is_active()) {
|
||||
was_active = true;
|
||||
this->parent_->enqueue(make_unique<PowerCommand>(false));
|
||||
this->parent_->enqueue(CommandType::COMMAND_TYPE_POWER, false);
|
||||
}
|
||||
this->parent_->enqueue(make_unique<LedModeCommand>(state));
|
||||
this->parent_->enqueue(make_unique<SaveCfgCommand>());
|
||||
this->parent_->enqueue(CommandType::COMMAND_TYPE_LED_MODE, state);
|
||||
this->parent_->enqueue(CommandType::COMMAND_TYPE_SAVE_CFG);
|
||||
if (was_active) {
|
||||
this->parent_->enqueue(make_unique<PowerCommand>(true));
|
||||
this->parent_->enqueue(CommandType::COMMAND_TYPE_POWER, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,12 +21,12 @@ void Sen0395UartPresenceSwitch::write_state(bool state) {
|
||||
bool was_active = false;
|
||||
if (this->parent_->is_active()) {
|
||||
was_active = true;
|
||||
this->parent_->enqueue(make_unique<PowerCommand>(false));
|
||||
this->parent_->enqueue(CommandType::COMMAND_TYPE_POWER, false);
|
||||
}
|
||||
this->parent_->enqueue(make_unique<UartOutputCommand>(state));
|
||||
this->parent_->enqueue(make_unique<SaveCfgCommand>());
|
||||
this->parent_->enqueue(CommandType::COMMAND_TYPE_UART_OUTPUT, state);
|
||||
this->parent_->enqueue(CommandType::COMMAND_TYPE_SAVE_CFG);
|
||||
if (was_active) {
|
||||
this->parent_->enqueue(make_unique<PowerCommand>(true));
|
||||
this->parent_->enqueue(CommandType::COMMAND_TYPE_POWER, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,12 +34,12 @@ void Sen0395StartAfterBootSwitch::write_state(bool state) {
|
||||
bool was_active = false;
|
||||
if (this->parent_->is_active()) {
|
||||
was_active = true;
|
||||
this->parent_->enqueue(make_unique<PowerCommand>(false));
|
||||
this->parent_->enqueue(CommandType::COMMAND_TYPE_POWER, false);
|
||||
}
|
||||
this->parent_->enqueue(make_unique<SensorCfgStartCommand>(state));
|
||||
this->parent_->enqueue(make_unique<SaveCfgCommand>());
|
||||
this->parent_->enqueue(CommandType::COMMAND_TYPE_SENSOR_CFG_START, state);
|
||||
this->parent_->enqueue(CommandType::COMMAND_TYPE_SAVE_CFG);
|
||||
if (was_active) {
|
||||
this->parent_->enqueue(make_unique<PowerCommand>(true));
|
||||
this->parent_->enqueue(CommandType::COMMAND_TYPE_POWER, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ float segment_value(size_t index, float x, int y = 0) {
|
||||
} // namespace
|
||||
|
||||
// The component is final and its command queue protected, so these tests check the segment
|
||||
// reads and their arguments; the enqueued DetRangeCfgCommand itself is not inspected.
|
||||
// reads and their arguments; the queued detection range command itself is not inspected.
|
||||
class SettingsActionTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override { call_record() = CallRecord{}; }
|
||||
|
||||
Reference in New Issue
Block a user