mirror of
https://github.com/esphome/esphome.git
synced 2024-11-23 23:48:11 +01:00
mlx90614 bugfix
* Check PEC (CRC) reading registers * Optional read reties. Defaults to 2 * Write emissivity back to device if changed
This commit is contained in:
parent
f2caaf85c8
commit
b2c5c5ef3b
2 changed files with 127 additions and 41 deletions
|
@ -38,16 +38,7 @@ void MLX90614Component::setup() {
|
|||
bool MLX90614Component::write_emissivity_() {
|
||||
if (std::isnan(this->emissivity_))
|
||||
return true;
|
||||
uint16_t value = (uint16_t) (this->emissivity_ * 65535);
|
||||
if (!this->write_bytes_(MLX90614_EMISSIVITY, 0)) {
|
||||
return false;
|
||||
}
|
||||
delay(10);
|
||||
if (!this->write_bytes_(MLX90614_EMISSIVITY, value)) {
|
||||
return false;
|
||||
}
|
||||
delay(10);
|
||||
return true;
|
||||
return this->write_register_(MLX90614_EMISSIVITY, this->emissivity_ * 0xFFFF);
|
||||
}
|
||||
|
||||
uint8_t MLX90614Component::crc8_pec_(const uint8_t *data, uint8_t len) {
|
||||
|
@ -65,14 +56,95 @@ uint8_t MLX90614Component::crc8_pec_(const uint8_t *data, uint8_t len) {
|
|||
return crc;
|
||||
}
|
||||
|
||||
bool MLX90614Component::write_bytes_(uint8_t reg, uint16_t data) {
|
||||
i2c::ErrorCode MLX90614Component::write_register_(uint8_t reg, uint16_t data, uint8_t max_try) {
|
||||
uint8_t buf[5];
|
||||
buf[0] = this->address_ << 1;
|
||||
buf[1] = reg;
|
||||
buf[2] = data & 0xFF;
|
||||
buf[3] = data >> 8;
|
||||
buf[4] = this->crc8_pec_(buf, 4);
|
||||
return this->write_bytes(reg, buf + 2, 3);
|
||||
i2c::ErrorCode ec = i2c::ERROR_UNKNOWN;
|
||||
auto init_buffer = [&]() {
|
||||
buf[0] = this->address_ << 1;
|
||||
buf[1] = reg;
|
||||
};
|
||||
|
||||
// See 8.3.3.1. ERPROM write sequence
|
||||
// 1. Power up the device
|
||||
const uint8_t delay_ms = 10;
|
||||
for (uint8_t i_try = 0; i_try < max_try; ++i_try) {
|
||||
init_buffer();
|
||||
|
||||
// 2. Write 0x0000 into the cell of interest (effectively erasing the cell)
|
||||
buf[2] = buf[3] = 0;
|
||||
buf[4] = this->crc8_pec_(buf, 4);
|
||||
if (!this->write_bytes(reg, buf + 2, 3)) {
|
||||
ESP_LOGW(TAG, "Try %d: Can't clean register %x", i_try, reg);
|
||||
ec = i2c::ERROR_UNKNOWN;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 3. Wait at least 5ms (10ms to be on the safe side)
|
||||
delay(delay_ms);
|
||||
|
||||
// 4. Write the new value
|
||||
if (data != 0) {
|
||||
buf[2] = data & 0xFF;
|
||||
buf[3] = data >> 8;
|
||||
buf[4] = this->crc8_pec_(buf, 4);
|
||||
if (!this->write_bytes(reg, buf + 2, 3)) {
|
||||
ESP_LOGW(TAG, "Try %d: Can't write register %x", i_try, reg);
|
||||
ec = i2c::ERROR_UNKNOWN;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 5. Wait at least 5ms (10ms to be on the safe side)
|
||||
delay(delay_ms);
|
||||
}
|
||||
|
||||
uint8_t read_buf[3];
|
||||
// 6. Read back and compare if the write was successful
|
||||
ec = this->read_register(reg, read_buf, 3, false);
|
||||
if (ec != i2c::ERROR_OK) {
|
||||
ESP_LOGW(TAG, "Try %d: Can't check register value %x", i_try, reg);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (read_buf[0] != buf[2] || read_buf[1] != buf[3] || read_buf[2] != buf[4]) {
|
||||
ESP_LOGW(TAG, "Try %d: Read back value is not the same. Expected %x%x%x. Actual %x%x%x", i_try, buf[2], buf[3],
|
||||
buf[4], read_buf[0], read_buf[1], read_buf[2]);
|
||||
ec = i2c::ERROR_CRC;
|
||||
continue;
|
||||
}
|
||||
|
||||
return i2c::ERROR_OK;
|
||||
}
|
||||
|
||||
ESP_LOGE(TAG, "Out of tries");
|
||||
return ec;
|
||||
}
|
||||
|
||||
uint16_t MLX90614Component::read_register_(uint8_t reg, i2c::ErrorCode &ec, uint8_t max_try) {
|
||||
uint8_t buf[6] = {
|
||||
uint8_t(this->address_ << 1),
|
||||
reg,
|
||||
uint8_t(0x01 | (this->address_ << 1)),
|
||||
};
|
||||
for (uint8_t i_try = 0; i_try < max_try; ++i_try) {
|
||||
ec = this->read_register(reg, buf + 3, 3, false);
|
||||
|
||||
if (ec != i2c::ERROR_OK) {
|
||||
ESP_LOGW(TAG, "Try %d: i2c read error %d", i_try, ec);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto expected_pec = this->crc8_pec_(buf, 5);
|
||||
if (buf[5] != expected_pec) {
|
||||
ESP_LOGW(TAG, "Try %d: i2c CRC error. Expected %x. Actual %x", i_try, expected_pec, buf[4]);
|
||||
ec = i2c::ERROR_CRC;
|
||||
continue;
|
||||
}
|
||||
|
||||
ec = i2c::ERROR_OK;
|
||||
return encode_uint16(buf[4], buf[3]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MLX90614Component::dump_config() {
|
||||
|
@ -89,33 +161,46 @@ void MLX90614Component::dump_config() {
|
|||
float MLX90614Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
||||
void MLX90614Component::update() {
|
||||
uint8_t emissivity[3];
|
||||
if (this->read_register(MLX90614_EMISSIVITY, emissivity, 3, false) != i2c::ERROR_OK) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
uint8_t raw_object[3];
|
||||
if (this->read_register(MLX90614_TEMPERATURE_OBJECT_1, raw_object, 3, false) != i2c::ERROR_OK) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
bool write_emissivity_status = true;
|
||||
if (!std::isnan(this->emissivity_)) {
|
||||
i2c::ErrorCode ec = i2c::ERROR_OK;
|
||||
const auto read_emissivity = read_register_(MLX90614_EMISSIVITY, ec);
|
||||
if (ec == i2c::ERROR_OK) {
|
||||
const auto desired_emissivity = uint16_t(this->emissivity_ * 0xFFFF);
|
||||
if (read_emissivity != desired_emissivity) {
|
||||
if (i2c::ERROR_OK != this->write_register_(MLX90614_EMISSIVITY, desired_emissivity)) {
|
||||
write_emissivity_status = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
write_emissivity_status = false;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t raw_ambient[3];
|
||||
if (this->read_register(MLX90614_TEMPERATURE_AMBIENT, raw_ambient, 3, false) != i2c::ERROR_OK) {
|
||||
auto publish_sensor = [&](sensor::Sensor *sensor, uint8_t reg) {
|
||||
if (nullptr == sensor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
i2c::ErrorCode ec = i2c::ERROR_OK;
|
||||
const auto raw = read_register_(reg, ec);
|
||||
if (ec != i2c::ERROR_OK) {
|
||||
sensor->publish_state(NAN);
|
||||
return false;
|
||||
}
|
||||
float value = raw & 0x8000 ? NAN : raw * 0.02f - 273.15f;
|
||||
sensor->publish_state(value);
|
||||
return true;
|
||||
};
|
||||
|
||||
const auto object_updated = publish_sensor(this->object_sensor_, MLX90614_TEMPERATURE_OBJECT_1);
|
||||
const auto ambient_updated = publish_sensor(this->ambient_sensor_, MLX90614_TEMPERATURE_AMBIENT);
|
||||
|
||||
if (object_updated && ambient_updated && write_emissivity_status) {
|
||||
this->status_clear_warning();
|
||||
} else {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
|
||||
float ambient = raw_ambient[1] & 0x80 ? NAN : encode_uint16(raw_ambient[1], raw_ambient[0]) * 0.02f - 273.15f;
|
||||
float object = raw_object[1] & 0x80 ? NAN : encode_uint16(raw_object[1], raw_object[0]) * 0.02f - 273.15f;
|
||||
|
||||
ESP_LOGD(TAG, "Got Temperature=%.1f°C Ambient=%.1f°C", object, ambient);
|
||||
|
||||
if (this->ambient_sensor_ != nullptr && !std::isnan(ambient))
|
||||
this->ambient_sensor_->publish_state(ambient);
|
||||
if (this->object_sensor_ != nullptr && !std::isnan(object))
|
||||
this->object_sensor_->publish_state(object);
|
||||
this->status_clear_warning();
|
||||
}
|
||||
|
||||
} // namespace mlx90614
|
||||
|
|
|
@ -23,7 +23,8 @@ class MLX90614Component : public PollingComponent, public i2c::I2CDevice {
|
|||
bool write_emissivity_();
|
||||
|
||||
uint8_t crc8_pec_(const uint8_t *data, uint8_t len);
|
||||
bool write_bytes_(uint8_t reg, uint16_t data);
|
||||
i2c::ErrorCode write_register_(uint8_t reg, uint16_t data, uint8_t max_try = 2);
|
||||
uint16_t read_register_(uint8_t reg, i2c::ErrorCode &ec, uint8_t max_try = 2);
|
||||
|
||||
sensor::Sensor *ambient_sensor_{nullptr};
|
||||
sensor::Sensor *object_sensor_{nullptr};
|
||||
|
|
Loading…
Reference in a new issue