Correct BME680 gas calculation and heater_off (#4498)

* Fix missing data array

* Fix incorrect bit offset

* Correct variable types

* Do same conversions as in original library

* Correct clang-format

* Move out float conversion for clarity

* Added check for heater stability

* Correct clang format

* Allow reporting gas resistance when heater is disabled

* Correct clang format

* Better error reporting by @DAVe3283

* Correct signed operation, range switching error was positive all the time
This commit is contained in:
Carlos Garcia Saura 2023-03-09 01:34:06 +01:00 committed by Jesse Hills
parent 7c91b4474a
commit d82c6df57e
No known key found for this signature in database
GPG key ID: BEAAE804EFD8E83A
2 changed files with 51 additions and 21 deletions

View file

@ -117,18 +117,24 @@ void BME680Component::setup() {
this->calibration_.gh2 = cal2[12] << 8 | cal2[13]; this->calibration_.gh2 = cal2[12] << 8 | cal2[13];
this->calibration_.gh3 = cal2[15]; this->calibration_.gh3 = cal2[15];
if (!this->read_byte(0x02, &this->calibration_.res_heat_range)) { uint8_t temp_var = 0;
if (!this->read_byte(0x02, &temp_var)) {
this->mark_failed(); this->mark_failed();
return; return;
} }
if (!this->read_byte(0x00, &this->calibration_.res_heat_val)) { this->calibration_.res_heat_range = ((temp_var & 0x30) / 16);
if (!this->read_byte(0x00, &temp_var)) {
this->mark_failed(); this->mark_failed();
return; return;
} }
if (!this->read_byte(0x04, &this->calibration_.range_sw_err)) { this->calibration_.res_heat_val = (int8_t) temp_var;
if (!this->read_byte(0x04, &temp_var)) {
this->mark_failed(); this->mark_failed();
return; return;
} }
this->calibration_.range_sw_err = ((int8_t) temp_var & (int8_t) 0xf0) / 16;
this->calibration_.ambient_temperature = 25; // prime ambient temperature this->calibration_.ambient_temperature = 25; // prime ambient temperature
@ -181,7 +187,7 @@ void BME680Component::setup() {
return; return;
} }
gas0_control &= ~0b00001000; gas0_control &= ~0b00001000;
gas0_control |= heat_off ? 0b100 : 0b000; gas0_control |= heat_off << 3;
if (!this->write_byte(BME680_REGISTER_CONTROL_GAS0, gas0_control)) { if (!this->write_byte(BME680_REGISTER_CONTROL_GAS0, gas0_control)) {
this->mark_failed(); this->mark_failed();
return; return;
@ -249,12 +255,12 @@ uint8_t BME680Component::calc_heater_resistance_(uint16_t temperature) {
if (temperature > 400) if (temperature > 400)
temperature = 400; temperature = 400;
const uint8_t ambient_temperature = this->calibration_.ambient_temperature; const int8_t ambient_temperature = this->calibration_.ambient_temperature;
const int8_t gh1 = this->calibration_.gh1; const int8_t gh1 = this->calibration_.gh1;
const int16_t gh2 = this->calibration_.gh2; const int16_t gh2 = this->calibration_.gh2;
const int8_t gh3 = this->calibration_.gh3; const int8_t gh3 = this->calibration_.gh3;
const uint8_t res_heat_range = this->calibration_.res_heat_range; const uint8_t res_heat_range = this->calibration_.res_heat_range;
const uint8_t res_heat_val = this->calibration_.res_heat_val; const int8_t res_heat_val = this->calibration_.res_heat_val;
uint8_t heatr_res; uint8_t heatr_res;
int32_t var1; int32_t var1;
@ -293,35 +299,57 @@ uint8_t BME680Component::calc_heater_duration_(uint16_t duration) {
void BME680Component::read_data_() { void BME680Component::read_data_() {
uint8_t data[15]; uint8_t data[15];
if (!this->read_bytes(BME680_REGISTER_FIELD0, data, 15)) { if (!this->read_bytes(BME680_REGISTER_FIELD0, data, 15)) {
if (this->temperature_sensor_ != nullptr)
this->temperature_sensor_->publish_state(NAN);
if (this->pressure_sensor_ != nullptr)
this->pressure_sensor_->publish_state(NAN);
if (this->humidity_sensor_ != nullptr)
this->humidity_sensor_->publish_state(NAN);
if (this->gas_resistance_sensor_ != nullptr)
this->gas_resistance_sensor_->publish_state(NAN);
ESP_LOGW(TAG, "Communication with BME680 failed!");
this->status_set_warning(); this->status_set_warning();
return; return;
} }
this->status_clear_warning();
uint32_t raw_temperature = (uint32_t(data[5]) << 12) | (uint32_t(data[6]) << 4) | (uint32_t(data[7]) >> 4); uint32_t raw_temperature = (uint32_t(data[5]) << 12) | (uint32_t(data[6]) << 4) | (uint32_t(data[7]) >> 4);
uint32_t raw_pressure = (uint32_t(data[2]) << 12) | (uint32_t(data[3]) << 4) | (uint32_t(data[4]) >> 4); uint32_t raw_pressure = (uint32_t(data[2]) << 12) | (uint32_t(data[3]) << 4) | (uint32_t(data[4]) >> 4);
uint32_t raw_humidity = (uint32_t(data[8]) << 8) | uint32_t(data[9]); uint32_t raw_humidity = (uint32_t(data[8]) << 8) | uint32_t(data[9]);
uint16_t raw_gas = (uint16_t(data[13]) << 2) | (uint16_t(14) >> 6); uint16_t raw_gas = (uint16_t)((uint32_t) data[13] * 4 | (((uint32_t) data[14]) / 64));
uint8_t gas_range = data[14] & 0x0F; uint8_t gas_range = data[14] & 0x0F;
float temperature = this->calc_temperature_(raw_temperature); float temperature = this->calc_temperature_(raw_temperature);
float pressure = this->calc_pressure_(raw_pressure); float pressure = this->calc_pressure_(raw_pressure);
float humidity = this->calc_humidity_(raw_humidity); float humidity = this->calc_humidity_(raw_humidity);
float gas_resistance = NAN; float gas_resistance = this->calc_gas_resistance_(raw_gas, gas_range);
if (data[14] & 0x20) {
gas_resistance = this->calc_gas_resistance_(raw_gas, gas_range); bool gas_valid = (data[14] >> 5) & 1;
} bool heat_stable = (data[14] >> 4) & 1;
if (this->heater_temperature_ == 0 || this->heater_duration_ == 0)
heat_stable = true; // Allow reporting gas resistance when heater is disabled
ESP_LOGD(TAG, "Got temperature=%.1f°C pressure=%.1fhPa humidity=%.1f%% gas_resistance=%.1fΩ", temperature, pressure, ESP_LOGD(TAG, "Got temperature=%.1f°C pressure=%.1fhPa humidity=%.1f%% gas_resistance=%.1fΩ", temperature, pressure,
humidity, gas_resistance); humidity, gas_resistance);
if (!gas_valid)
ESP_LOGW(TAG, "Gas measurement unsuccessful, reading invalid!");
if (!heat_stable)
ESP_LOGW(TAG, "Heater unstable, reading invalid! (Normal for a few readings after a power cycle)");
if (this->temperature_sensor_ != nullptr) if (this->temperature_sensor_ != nullptr)
this->temperature_sensor_->publish_state(temperature); this->temperature_sensor_->publish_state(temperature);
if (this->pressure_sensor_ != nullptr) if (this->pressure_sensor_ != nullptr)
this->pressure_sensor_->publish_state(pressure); this->pressure_sensor_->publish_state(pressure);
if (this->humidity_sensor_ != nullptr) if (this->humidity_sensor_ != nullptr)
this->humidity_sensor_->publish_state(humidity); this->humidity_sensor_->publish_state(humidity);
if (this->gas_resistance_sensor_ != nullptr) if (this->gas_resistance_sensor_ != nullptr) {
this->gas_resistance_sensor_->publish_state(gas_resistance); if (gas_valid && heat_stable) {
this->status_clear_warning(); this->gas_resistance_sensor_->publish_state(gas_resistance);
} else {
this->status_set_warning();
this->gas_resistance_sensor_->publish_state(NAN);
}
}
} }
float BME680Component::calc_temperature_(uint32_t raw_temperature) { float BME680Component::calc_temperature_(uint32_t raw_temperature) {
@ -428,20 +456,22 @@ float BME680Component::calc_humidity_(uint16_t raw_humidity) {
return calc_hum; return calc_hum;
} }
uint32_t BME680Component::calc_gas_resistance_(uint16_t raw_gas, uint8_t range) { float BME680Component::calc_gas_resistance_(uint16_t raw_gas, uint8_t range) {
float calc_gas_res; float calc_gas_res;
float var1 = 0; float var1 = 0;
float var2 = 0; float var2 = 0;
float var3 = 0; float var3 = 0;
float raw_gas_f = raw_gas;
float range_f = 1U << range;
const float range_sw_err = this->calibration_.range_sw_err; const float range_sw_err = this->calibration_.range_sw_err;
var1 = 1340.0f + (5.0f * range_sw_err); var1 = 1340.0f + (5.0f * range_sw_err);
var2 = var1 * (1.0f + BME680_GAS_LOOKUP_TABLE_1[range] / 100.0f); var2 = var1 * (1.0f + BME680_GAS_LOOKUP_TABLE_1[range] / 100.0f);
var3 = 1.0f + (BME680_GAS_LOOKUP_TABLE_2[range] / 100.0f); var3 = 1.0f + (BME680_GAS_LOOKUP_TABLE_2[range] / 100.0f);
calc_gas_res = 1.0f / (var3 * 0.000000125f * float(1 << range) * (((float(raw_gas) - 512.0f) / var2) + 1.0f)); calc_gas_res = 1.0f / (var3 * 0.000000125f * range_f * (((raw_gas_f - 512.0f) / var2) + 1.0f));
return static_cast<uint32_t>(calc_gas_res); return calc_gas_res;
} }
uint32_t BME680Component::calc_meas_duration_() { uint32_t BME680Component::calc_meas_duration_() {
uint32_t tph_dur; // Calculate in us uint32_t tph_dur; // Calculate in us

View file

@ -59,11 +59,11 @@ struct BME680CalibrationData {
int8_t gh3; int8_t gh3;
uint8_t res_heat_range; uint8_t res_heat_range;
uint8_t res_heat_val; int8_t res_heat_val;
uint8_t range_sw_err; int8_t range_sw_err;
float tfine; float tfine;
uint8_t ambient_temperature; int8_t ambient_temperature;
}; };
class BME680Component : public PollingComponent, public i2c::I2CDevice { class BME680Component : public PollingComponent, public i2c::I2CDevice {
@ -117,7 +117,7 @@ class BME680Component : public PollingComponent, public i2c::I2CDevice {
/// Calculate the relative humidity in % using the provided raw ADC value. /// Calculate the relative humidity in % using the provided raw ADC value.
float calc_humidity_(uint16_t raw_humidity); float calc_humidity_(uint16_t raw_humidity);
/// Calculate the gas resistance in Ω using the provided raw ADC value. /// Calculate the gas resistance in Ω using the provided raw ADC value.
uint32_t calc_gas_resistance_(uint16_t raw_gas, uint8_t range); float calc_gas_resistance_(uint16_t raw_gas, uint8_t range);
/// Calculate how long the sensor will take until we can retrieve data. /// Calculate how long the sensor will take until we can retrieve data.
uint32_t calc_meas_duration_(); uint32_t calc_meas_duration_();