13static constexpr uint8_t FRAME_ADDRESS = 0x01;
15static constexpr uint8_t NO_COMMAND = 0x00;
16static constexpr uint8_t CMD_CHANGE_ACQUIRE_MODE = 0x78;
17static constexpr uint8_t CMD_GET_ALL_DATA = 0x88;
18static constexpr uint8_t CMD_SET_THRESHOLD_ALARM = 0x89;
19static constexpr uint8_t ACQUIRE_MODE_PASSIVE = 0x04;
20static constexpr uint8_t ALARM_SWITCH_OFF = 0x00;
21static constexpr uint8_t ALARM_SWITCH_ON = 0x01;
22static constexpr uint8_t ALARM_LOW_THRESHOLD = 0x00;
23static constexpr uint8_t ALARM_HIGH_THRESHOLD = 0x01;
25static constexpr uint8_t RESULT_OK = 0x01;
28static constexpr uint32_t MODE_CHANGE_DELAY_MS = 1000;
29static constexpr uint32_t RETRY_DELAY_MS = 1000;
30static constexpr uint8_t MAX_ATTEMPTS = 3;
33static uint8_t frame_checksum(
const uint8_t *frame) {
35 return static_cast<uint8_t
>(~sum16_bytes(frame + 1, FRAME_SIZE - 3) + 1);
38static const LogString *step_name(
SetupStep step) {
42void DFRobotSEN0465Component::setup() {
49void DFRobotSEN0465Component::update() {
55 ESP_LOGV(TAG,
"Previous request still waiting for an answer, skipping update");
59 ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL);
64void DFRobotSEN0465Component::dump_config() {
66 "DFRobot SEN0465 series gas sensor:\n"
67 " Gas: %s (type 0x%02X)",
69#ifdef USE_DFROBOT_SEN0465_TEMPERATURE_COMPENSATION
70 ESP_LOGCONFIG(TAG,
" Temperature compensation: %s", YESNO(!this->
compensation_.empty()));
73 ESP_LOGCONFIG(TAG,
" Alarm: off");
79 ESP_LOGCONFIG(TAG,
" Alarm: %s %.*f",
81 this->alarm_decimals_, threshold);
84 ESP_LOGCONFIG(TAG,
" Setup incomplete: yes");
87 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
89 LOG_UPDATE_INTERVAL(
this);
90 LOG_SENSOR(
" ",
"Concentration", this->concentration_sensor_);
91 LOG_SENSOR(
" ",
"Temperature", this->temperature_sensor_);
95 uint8_t frame[FRAME_SIZE] = {FRAME_HEAD, FRAME_ADDRESS, command, arg0, arg1, arg2, arg3};
96 frame[FRAME_SIZE - 1] = frame_checksum(frame);
106 return this->
send_command_(CMD_CHANGE_ACQUIRE_MODE, ACQUIRE_MODE_PASSIVE);
109 return this->
send_command_(CMD_SET_THRESHOLD_ALARM, ALARM_SWITCH_OFF);
112 CMD_SET_THRESHOLD_ALARM, ALARM_SWITCH_ON,
static_cast<uint8_t
>(this->
alarm_threshold_ >> 8),
124 ESP_LOGW(TAG,
"%s failed: %s (attempt %u of %u)", LOG_STR_ARG(step_name(this->
step_)), LOG_STR_ARG(reason),
153 ESP_LOGW(TAG,
"No response from the sensor");
159 if (this->concentration_sensor_ !=
nullptr)
160 this->concentration_sensor_->publish_state(NAN);
161 if (this->temperature_sensor_ !=
nullptr)
162 this->temperature_sensor_->publish_state(NAN);
168 const bool expected = frame[0] == FRAME_HEAD && frame[1] == this->
pending_command_;
182 ESP_LOGD_HEX(TAG, frame, FRAME_SIZE, FRAME_SIZE,
"%s response: %s", LOG_STR_ARG(step_name(this->
step_)));
192 if (frame[2] != RESULT_OK) {
196 ESP_LOGD(TAG,
"%s confirmed", LOG_STR_ARG(step_name(this->
step_)));
201 if (frame[FRAME_SIZE - 1] != frame_checksum(frame)) {
210 const uint16_t temperature_raw =
encode_uint16(frame[6], frame[7]);
211 if (temperature_raw > 0 && temperature_raw < 1024) {
212 const float voltage = 3.0f * temperature_raw / 1024.0f;
213 const float resistance = voltage * 10000.0f / (3.0f - voltage);
214 temperature = 1.0f / (1.0f / 298.15f + std::log(resistance / 10000.0f) / 3380.13f) - 273.15f;
217 ESP_LOGW(TAG,
"Temperature reading %u is out of range", temperature_raw);
219 if (this->temperature_sensor_ !=
nullptr)
220 this->temperature_sensor_->publish_state(
temperature);
223 ESP_LOGW(TAG,
"Probe reports gas type 0x%02X, expected %s (0x%02X); check the configured model", frame[4],
225 if (this->concentration_sensor_ !=
nullptr)
226 this->concentration_sensor_->publish_state(NAN);
236 concentration *= 0.1f;
239 concentration *= 0.01f;
242 ESP_LOGW(TAG,
"Invalid number of decimal places: %u", frame[5]);
243 if (this->concentration_sensor_ !=
nullptr)
244 this->concentration_sensor_->publish_state(NAN);
249#ifdef USE_DFROBOT_SEN0465_TEMPERATURE_COMPENSATION
253 if (concentration < 0.0f)
254 concentration = 0.0f;
256 if (this->concentration_sensor_ !=
nullptr)
257 this->concentration_sensor_->publish_state(concentration);
261#ifdef USE_DFROBOT_SEN0465_TEMPERATURE_COMPENSATION
267 ESP_LOGW(TAG,
"Temperature %.1f C is outside the compensation range",
temperature);
void mark_failed()
Mark this component as failed.
void status_set_warning()
bool status_comm_result(bool ok)
Clears the warning when ok, else sets it with message (default "Communication failed"); returns ok.
void set_timeout(const char *name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a const char* name.
StaticVector< CompensationRange, DFROBOT_SEN0465_COMPENSATION_RANGES > compensation_
void handle_no_response_()
uint16_t alarm_threshold_
bool send_step_request_()
Sends the request of the current step.
virtual void read_response()=0
Waits for the answer to a request, then calls either handle_response_() or handle_no_response_(),...
void step_failed_(const LogString *reason)
void retry_step_request_()
Sends the request again from a scheduled callback; a failed write counts as a failed attempt.
void handle_data_(const uint8_t *frame)
bool setup_incomplete_
A setup step gave up, so the alarm or the mode may not be what was configured.
virtual bool write_frame(const uint8_t *frame)=0
Sends one complete request frame.
uint8_t pending_command_
Command whose answer is awaited, 0 when no request is outstanding.
bool send_command_(uint8_t command, uint8_t arg0=0, uint8_t arg1=0, uint8_t arg2=0, uint8_t arg3=0)
Sends a request and waits for its answer; returns false when the request could not be sent.
void handle_response_(const uint8_t *frame)
float compensate_(float concentration, float temperature) const
Returns NAN when the temperature is outside every range.
void fail_reading_()
Publish unknown on both sensors and set the warning when a reading cannot be used.
ESPHOME_LOG_TAG(TAG, "dfrobot_sen0465")
SetupStep
Settings sent once after boot, before the first reading.
@ SETUP_STEP_ACQUIRE_MODE
char * format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator)
Format byte array as uppercase hex to buffer (base implementation).
constexpr size_t format_hex_pretty_size(size_t byte_count)
Calculate buffer size needed for format_hex_pretty_to with separator: "XX:XX:...:XX\0".
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.