mirror of
https://github.com/esphome/esphome.git
synced 2024-11-24 12:06:26 +01:00
t6615: tolerate sensor dropping commands (#2255)
The Amphenol T6615 has a built-in calibration system which means that the sensor could go away for a couple of seconds to figure itself out. While this is happening, commands are silently dropped. This caused the previous version of this code to lock up completely, since there was no way for the command_ state machine to tick back to the NONE state. Instead of just breaking the state machine, which might be harmful on a multi-core or multi-threaded device, add a timestamp and only break the lock if it's been more than a second since the command was issued. The command usually doesn't take more than a few milliseconds to complete, so this should not affect things unduly. While we're at it, rewrite the rx side to be more robust against bytes going missing. Instead of reading in the data essentially inline, read into a buffer and process it when enough has been read to make progress. If data stops coming when we expect it to, or the data is malformed, have a timeout that sends a new command. Co-authored-by: jas <jas@asspa.in>
This commit is contained in:
parent
23ead416d5
commit
460a144ca8
@ -6,7 +6,7 @@ namespace t6615 {
|
||||
|
||||
static const char *const TAG = "t6615";
|
||||
|
||||
static const uint8_t T6615_RESPONSE_BUFFER_LENGTH = 32;
|
||||
static const uint32_t T6615_TIMEOUT = 1000;
|
||||
static const uint8_t T6615_MAGIC = 0xFF;
|
||||
static const uint8_t T6615_ADDR_HOST = 0xFA;
|
||||
static const uint8_t T6615_ADDR_SENSOR = 0xFE;
|
||||
@ -19,31 +19,49 @@ static const uint8_t T6615_COMMAND_ENABLE_ABC[] = {0xB7, 0x01};
|
||||
static const uint8_t T6615_COMMAND_DISABLE_ABC[] = {0xB7, 0x02};
|
||||
static const uint8_t T6615_COMMAND_SET_ELEVATION[] = {0x03, 0x0F};
|
||||
|
||||
void T6615Component::loop() {
|
||||
if (!this->available())
|
||||
return;
|
||||
void T6615Component::send_ppm_command_() {
|
||||
this->command_time_ = millis();
|
||||
this->command_ = T6615Command::GET_PPM;
|
||||
this->write_byte(T6615_MAGIC);
|
||||
this->write_byte(T6615_ADDR_SENSOR);
|
||||
this->write_byte(sizeof(T6615_COMMAND_GET_PPM));
|
||||
this->write_array(T6615_COMMAND_GET_PPM, sizeof(T6615_COMMAND_GET_PPM));
|
||||
}
|
||||
|
||||
// Read header
|
||||
uint8_t header[3];
|
||||
this->read_array(header, 3);
|
||||
if (header[0] != T6615_MAGIC || header[1] != T6615_ADDR_HOST) {
|
||||
ESP_LOGW(TAG, "Reading data from T6615 failed!");
|
||||
while (this->available())
|
||||
this->read(); // Clear the incoming buffer
|
||||
this->status_set_warning();
|
||||
void T6615Component::loop() {
|
||||
if (this->available() < 5) {
|
||||
if (this->command_ == T6615Command::GET_PPM && millis() - this->command_time_ > T6615_TIMEOUT) {
|
||||
/* command got eaten, clear the buffer and fire another */
|
||||
while (this->available())
|
||||
this->read();
|
||||
this->send_ppm_command_();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Read body
|
||||
uint8_t length = header[2];
|
||||
uint8_t response[T6615_RESPONSE_BUFFER_LENGTH];
|
||||
this->read_array(response, length);
|
||||
uint8_t response_buffer[6];
|
||||
|
||||
/* by the time we get here, we know we have at least five bytes in the buffer */
|
||||
this->read_array(response_buffer, 5);
|
||||
|
||||
// Read header
|
||||
if (response_buffer[0] != T6615_MAGIC || response_buffer[1] != T6615_ADDR_HOST) {
|
||||
ESP_LOGW(TAG, "Got bad data from T6615! Magic was %02X and address was %02X", response_buffer[0],
|
||||
response_buffer[1]);
|
||||
/* make sure the buffer is empty */
|
||||
while (this->available())
|
||||
this->read();
|
||||
/* try again to read the sensor */
|
||||
this->send_ppm_command_();
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
|
||||
this->status_clear_warning();
|
||||
|
||||
switch (this->command_) {
|
||||
case T6615Command::GET_PPM: {
|
||||
const uint16_t ppm = encode_uint16(response[0], response[1]);
|
||||
const uint16_t ppm = encode_uint16(response_buffer[3], response_buffer[4]);
|
||||
ESP_LOGD(TAG, "T6615 Received CO₂=%uppm", ppm);
|
||||
this->co2_sensor_->publish_state(ppm);
|
||||
break;
|
||||
@ -51,23 +69,19 @@ void T6615Component::loop() {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
this->command_time_ = 0;
|
||||
this->command_ = T6615Command::NONE;
|
||||
}
|
||||
|
||||
void T6615Component::update() { this->query_ppm_(); }
|
||||
|
||||
void T6615Component::query_ppm_() {
|
||||
if (this->co2_sensor_ == nullptr || this->command_ != T6615Command::NONE) {
|
||||
if (this->co2_sensor_ == nullptr ||
|
||||
(this->command_ != T6615Command::NONE && millis() - this->command_time_ < T6615_TIMEOUT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->command_ = T6615Command::GET_PPM;
|
||||
|
||||
this->write_byte(T6615_MAGIC);
|
||||
this->write_byte(T6615_ADDR_SENSOR);
|
||||
this->write_byte(sizeof(T6615_COMMAND_GET_PPM));
|
||||
this->write_array(T6615_COMMAND_GET_PPM, sizeof(T6615_COMMAND_GET_PPM));
|
||||
this->send_ppm_command_();
|
||||
}
|
||||
|
||||
float T6615Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
@ -32,8 +32,10 @@ class T6615Component : public PollingComponent, public uart::UARTDevice {
|
||||
|
||||
protected:
|
||||
void query_ppm_();
|
||||
void send_ppm_command_();
|
||||
|
||||
T6615Command command_ = T6615Command::NONE;
|
||||
unsigned long command_time_ = 0;
|
||||
|
||||
sensor::Sensor *co2_sensor_{nullptr};
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user