mirror of
https://github.com/esphome/esphome.git
synced 2025-10-30 06:33:51 +00:00
🏗 Merge C++ into python codebase (#504)
## Description: Move esphome-core codebase into esphome (and a bunch of other refactors). See https://github.com/esphome/feature-requests/issues/97 Yes this is a shit ton of work and no there's no way to automate it :( But it will be worth it 👍 Progress: - Core support (file copy etc): 80% - Base Abstractions (light, switch): ~50% - Integrations: ~10% - Working? Yes, (but only with ported components). Other refactors: - Moves all codegen related stuff into a single class: `esphome.codegen` (imported as `cg`) - Rework coroutine syntax - Move from `component/platform.py` to `domain/component.py` structure as with HA - Move all defaults out of C++ and into config validation. - Remove `make_...` helpers from Application class. Reason: Merge conflicts with every single new integration. - Pointer Variables are stored globally instead of locally in setup(). Reason: stack size limit. Future work: - Rework const.py - Move all `CONF_...` into a conf class (usage `conf.UPDATE_INTERVAL` vs `CONF_UPDATE_INTERVAL`). Reason: Less convoluted import block - Enable loading from `custom_components` folder. **Related issue (if applicable):** https://github.com/esphome/feature-requests/issues/97 **Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** esphome/esphome-docs#<esphome-docs PR number goes here> ## Checklist: - [ ] The code change is tested and works locally. - [ ] Tests have been added to verify that the new code works (under `tests/` folder). If user exposed functionality or configuration variables are added/changed: - [ ] Documentation added/updated in [esphomedocs](https://github.com/OttoWinter/esphomedocs).
This commit is contained in:
0
esphome/components/bme680/__init__.py
Normal file
0
esphome/components/bme680/__init__.py
Normal file
483
esphome/components/bme680/bme680.cpp
Normal file
483
esphome/components/bme680/bme680.cpp
Normal file
@@ -0,0 +1,483 @@
|
||||
#include "bme680.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace bme680 {
|
||||
|
||||
static const char *TAG = "bme680.sensor";
|
||||
|
||||
static const uint8_t BME680_REGISTER_COEFF1 = 0x89;
|
||||
static const uint8_t BME680_REGISTER_COEFF2 = 0xE1;
|
||||
|
||||
static const uint8_t BME680_REGISTER_CONFIG = 0x75;
|
||||
static const uint8_t BME680_REGISTER_CONTROL_MEAS = 0x74;
|
||||
static const uint8_t BME680_REGISTER_CONTROL_HUMIDITY = 0x72;
|
||||
static const uint8_t BME680_REGISTER_CONTROL_GAS1 = 0x71;
|
||||
static const uint8_t BME680_REGISTER_CONTROL_GAS0 = 0x70;
|
||||
static const uint8_t BME680_REGISTER_HEATER_HEAT0 = 0x5A;
|
||||
static const uint8_t BME680_REGISTER_HEATER_WAIT0 = 0x64;
|
||||
|
||||
static const uint8_t BME680_REGISTER_CHIPID = 0xD0;
|
||||
|
||||
static const uint8_t BME680_REGISTER_FIELD0 = 0x1D;
|
||||
|
||||
const float BME680_GAS_LOOKUP_TABLE_1[16] PROGMEM = {0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, -0.8,
|
||||
0.0, 0.0, -0.2, -0.5, 0.0, -1.0, 0.0, 0.0};
|
||||
|
||||
const float BME680_GAS_LOOKUP_TABLE_2[16] PROGMEM = {0.0, 0.0, 0.0, 0.0, 0.1, 0.7, 0.0, -0.8,
|
||||
-0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
|
||||
|
||||
static const char *oversampling_to_str(BME680Oversampling oversampling) {
|
||||
switch (oversampling) {
|
||||
case BME680_OVERSAMPLING_NONE:
|
||||
return "None";
|
||||
case BME680_OVERSAMPLING_1X:
|
||||
return "1x";
|
||||
case BME680_OVERSAMPLING_2X:
|
||||
return "2x";
|
||||
case BME680_OVERSAMPLING_4X:
|
||||
return "4x";
|
||||
case BME680_OVERSAMPLING_8X:
|
||||
return "8x";
|
||||
case BME680_OVERSAMPLING_16X:
|
||||
return "16x";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *iir_filter_to_str(BME680IIRFilter filter) {
|
||||
switch (filter) {
|
||||
case BME680_IIR_FILTER_OFF:
|
||||
return "OFF";
|
||||
case BME680_IIR_FILTER_1X:
|
||||
return "1x";
|
||||
case BME680_IIR_FILTER_3X:
|
||||
return "3x";
|
||||
case BME680_IIR_FILTER_7X:
|
||||
return "7x";
|
||||
case BME680_IIR_FILTER_15X:
|
||||
return "15x";
|
||||
case BME680_IIR_FILTER_31X:
|
||||
return "31x";
|
||||
case BME680_IIR_FILTER_63X:
|
||||
return "63x";
|
||||
case BME680_IIR_FILTER_127X:
|
||||
return "127x";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
void BME680Component::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up BME680...");
|
||||
uint8_t chip_id;
|
||||
if (!this->read_byte(BME680_REGISTER_CHIPID, &chip_id) || chip_id != 0x61) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
// Read calibration
|
||||
uint8_t cal1[25];
|
||||
if (!this->read_bytes(BME680_REGISTER_COEFF1, cal1, 25)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
uint8_t cal2[16];
|
||||
if (!this->read_bytes(BME680_REGISTER_COEFF2, cal2, 16)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
this->calibration_.t1 = cal2[9] << 8 | cal2[8];
|
||||
this->calibration_.t2 = cal1[2] << 8 | cal1[1];
|
||||
this->calibration_.t3 = cal1[3];
|
||||
|
||||
this->calibration_.h1 = cal2[2] << 4 | (cal2[1] & 0x0F);
|
||||
this->calibration_.h2 = cal2[0] << 4 | cal2[1];
|
||||
this->calibration_.h3 = cal2[3];
|
||||
this->calibration_.h4 = cal2[4];
|
||||
this->calibration_.h5 = cal2[5];
|
||||
this->calibration_.h6 = cal2[6];
|
||||
this->calibration_.h7 = cal2[7];
|
||||
|
||||
this->calibration_.p1 = cal1[6] << 8 | cal1[5];
|
||||
this->calibration_.p2 = cal1[8] << 8 | cal1[7];
|
||||
this->calibration_.p3 = cal1[9];
|
||||
this->calibration_.p4 = cal1[12] << 8 | cal1[11];
|
||||
this->calibration_.p5 = cal1[14] << 8 | cal1[13];
|
||||
this->calibration_.p6 = cal1[16];
|
||||
this->calibration_.p7 = cal1[15];
|
||||
this->calibration_.p8 = cal1[20] << 8 | cal1[19];
|
||||
this->calibration_.p9 = cal1[22] << 8 | cal1[21];
|
||||
this->calibration_.p10 = cal1[23];
|
||||
|
||||
this->calibration_.gh1 = cal2[14];
|
||||
this->calibration_.gh2 = cal2[12] << 8 | cal2[13];
|
||||
this->calibration_.gh3 = cal2[15];
|
||||
|
||||
if (!this->read_byte(0x02, &this->calibration_.res_heat_range)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
if (!this->read_byte(0x00, &this->calibration_.res_heat_val)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
if (!this->read_byte(0x04, &this->calibration_.range_sw_err)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
this->calibration_.ambient_temperature = 25; // prime ambient temperature
|
||||
|
||||
// Config register
|
||||
uint8_t config_register;
|
||||
if (!this->read_byte(BME680_REGISTER_CONFIG, &config_register)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
config_register &= ~0b00011100;
|
||||
config_register |= (this->iir_filter_ & 0b111) << 2;
|
||||
if (!this->write_byte(BME680_REGISTER_CONFIG, config_register)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
// Humidity control register
|
||||
uint8_t hum_control;
|
||||
if (!this->read_byte(BME680_REGISTER_CONTROL_HUMIDITY, &hum_control)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
hum_control &= ~0b00000111;
|
||||
hum_control |= this->humidity_oversampling_ & 0b111;
|
||||
if (!this->write_byte(BME680_REGISTER_CONTROL_HUMIDITY, hum_control)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
// Gas 1 control register
|
||||
uint8_t gas1_control;
|
||||
if (!this->read_byte(BME680_REGISTER_CONTROL_GAS1, &gas1_control)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
gas1_control &= ~0b00011111;
|
||||
gas1_control |= 1 << 4;
|
||||
gas1_control |= 0; // profile 0
|
||||
if (!this->write_byte(BME680_REGISTER_CONTROL_GAS1, gas1_control)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
const bool heat_off = this->heater_temperature_ == 0 || this->heater_duration_ == 0;
|
||||
|
||||
// Gas 0 control register
|
||||
uint8_t gas0_control;
|
||||
if (!this->read_byte(BME680_REGISTER_CONTROL_GAS0, &gas0_control)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
gas0_control &= ~0b00001000;
|
||||
gas0_control |= heat_off ? 0b100 : 0b000;
|
||||
if (!this->write_byte(BME680_REGISTER_CONTROL_GAS0, gas0_control)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!heat_off) {
|
||||
// Gas Heater Temperature
|
||||
uint8_t temperature = this->calc_heater_resistance_(this->heater_temperature_);
|
||||
if (!this->write_byte(BME680_REGISTER_HEATER_HEAT0, temperature)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
// Gas Heater Duration
|
||||
uint8_t duration = this->calc_heater_duration_(this->heater_duration_);
|
||||
|
||||
if (!this->write_byte(BME680_REGISTER_HEATER_WAIT0, duration)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BME680Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "BME680:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, "Communication with BME680 failed!");
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, " IIR Filter: %s", iir_filter_to_str(this->iir_filter_));
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
|
||||
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||
ESP_LOGCONFIG(TAG, " Oversampling: %s", oversampling_to_str(this->temperature_oversampling_));
|
||||
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
|
||||
ESP_LOGCONFIG(TAG, " Oversampling: %s", oversampling_to_str(this->pressure_oversampling_));
|
||||
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
||||
ESP_LOGCONFIG(TAG, " Oversampling: %s", oversampling_to_str(this->humidity_oversampling_));
|
||||
LOG_SENSOR(" ", "Gas Resistance", this->gas_resistance_sensor_);
|
||||
if (this->heater_duration_ == 0 || this->heater_temperature_ == 0) {
|
||||
ESP_LOGCONFIG(TAG, " Heater OFF");
|
||||
} else {
|
||||
ESP_LOGCONFIG(TAG, " Heater temperature=%u°C duration=%ums", this->heater_temperature_, this->heater_duration_);
|
||||
}
|
||||
}
|
||||
|
||||
float BME680Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
||||
void BME680Component::update() {
|
||||
uint8_t meas_control = 0; // No need to fetch, we're setting all fields
|
||||
meas_control |= (this->temperature_oversampling_ & 0b111) << 5;
|
||||
meas_control |= (this->pressure_oversampling_ & 0b111) << 5;
|
||||
meas_control |= 0b01; // forced mode
|
||||
if (!this->write_byte(BME680_REGISTER_CONTROL_MEAS, meas_control)) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
|
||||
this->set_timeout("data", this->calc_meas_duration_(), [this]() { this->read_data_(); });
|
||||
}
|
||||
|
||||
uint8_t BME680Component::calc_heater_resistance_(uint16_t temperature) {
|
||||
if (temperature < 200)
|
||||
temperature = 200;
|
||||
if (temperature > 400)
|
||||
temperature = 400;
|
||||
|
||||
const uint8_t ambient_temperature = this->calibration_.ambient_temperature;
|
||||
const int8_t gh1 = this->calibration_.gh1;
|
||||
const int16_t gh2 = this->calibration_.gh2;
|
||||
const int8_t gh3 = this->calibration_.gh3;
|
||||
const uint8_t res_heat_range = this->calibration_.res_heat_range;
|
||||
const uint8_t res_heat_val = this->calibration_.res_heat_val;
|
||||
|
||||
uint8_t heatr_res;
|
||||
int32_t var1;
|
||||
int32_t var2;
|
||||
int32_t var3;
|
||||
int32_t var4;
|
||||
int32_t var5;
|
||||
int32_t heatr_res_x100;
|
||||
|
||||
var1 = (((int32_t) ambient_temperature * gh3) / 1000) * 256;
|
||||
var2 = (gh1 + 784) * (((((gh2 + 154009) * temperature * 5) / 100) + 3276800) / 10);
|
||||
var3 = var1 + (var2 / 2);
|
||||
var4 = (var3 / (res_heat_range + 4));
|
||||
var5 = (131 * res_heat_val) + 65536;
|
||||
heatr_res_x100 = (int32_t)(((var4 / var5) - 250) * 34);
|
||||
heatr_res = (uint8_t)((heatr_res_x100 + 50) / 100);
|
||||
|
||||
return heatr_res;
|
||||
}
|
||||
uint8_t BME680Component::calc_heater_duration_(uint16_t duration) {
|
||||
uint8_t factor = 0;
|
||||
uint8_t duration_value;
|
||||
|
||||
if (duration >= 0xfc0) {
|
||||
duration_value = 0xff;
|
||||
} else {
|
||||
while (duration > 0x3F) {
|
||||
duration /= 4;
|
||||
factor += 1;
|
||||
}
|
||||
duration_value = duration + (factor * 64);
|
||||
}
|
||||
|
||||
return duration_value;
|
||||
}
|
||||
void BME680Component::read_data_() {
|
||||
uint8_t data[15];
|
||||
if (!this->read_bytes(BME680_REGISTER_FIELD0, data, 15)) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
|
||||
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_humidity = (uint32_t(data[8]) << 8) | uint32_t(data[9]);
|
||||
uint16_t raw_gas = (uint16_t(data[13]) << 2) | (uint16_t(14) >> 6);
|
||||
uint8_t gas_range = data[14] & 0x0F;
|
||||
|
||||
float temperature = this->calc_temperature_(raw_temperature);
|
||||
float pressure = this->calc_pressure_(raw_pressure);
|
||||
float humidity = this->calc_humidity_(raw_humidity);
|
||||
float gas_resistance = NAN;
|
||||
if (data[14] & 0x20) {
|
||||
gas_resistance = this->calc_gas_resistance_(raw_gas, gas_range);
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Got temperature=%.1f°C pressure=%.1fhPa humidity=%.1f%% gas_resistance=%.1fΩ", temperature, pressure,
|
||||
humidity, gas_resistance);
|
||||
if (this->temperature_sensor_ != nullptr)
|
||||
this->temperature_sensor_->publish_state(temperature);
|
||||
if (this->pressure_sensor_ != nullptr)
|
||||
this->pressure_sensor_->publish_state(pressure);
|
||||
if (this->humidity_sensor_ != nullptr)
|
||||
this->humidity_sensor_->publish_state(humidity);
|
||||
if (this->gas_resistance_sensor_ != nullptr)
|
||||
this->gas_resistance_sensor_->publish_state(gas_resistance);
|
||||
this->status_clear_warning();
|
||||
}
|
||||
|
||||
float BME680Component::calc_temperature_(uint32_t raw_temperature) {
|
||||
float var1 = 0;
|
||||
float var2 = 0;
|
||||
float var3 = 0;
|
||||
float calc_temp = 0;
|
||||
float temp_adc = raw_temperature;
|
||||
|
||||
const float t1 = this->calibration_.t1;
|
||||
const float t2 = this->calibration_.t2;
|
||||
const float t3 = this->calibration_.t3;
|
||||
|
||||
/* calculate var1 data */
|
||||
var1 = ((temp_adc / 16384.0f) - (t1 / 1024.0f)) * t2;
|
||||
|
||||
/* calculate var2 data */
|
||||
var3 = (temp_adc / 131072.0f) - (t1 / 8192.0f);
|
||||
var2 = var3 * var3 * t3 * 16.0f;
|
||||
|
||||
/* t_fine value*/
|
||||
this->calibration_.tfine = (var1 + var2);
|
||||
|
||||
/* compensated temperature data*/
|
||||
calc_temp = ((this->calibration_.tfine) / 5120.0f);
|
||||
|
||||
return calc_temp;
|
||||
}
|
||||
float BME680Component::calc_pressure_(uint32_t raw_pressure) {
|
||||
const float tfine = this->calibration_.tfine;
|
||||
const float p1 = this->calibration_.p1;
|
||||
const float p2 = this->calibration_.p2;
|
||||
const float p3 = this->calibration_.p3;
|
||||
const float p4 = this->calibration_.p4;
|
||||
const float p5 = this->calibration_.p5;
|
||||
const float p6 = this->calibration_.p6;
|
||||
const float p7 = this->calibration_.p7;
|
||||
const float p8 = this->calibration_.p8;
|
||||
const float p9 = this->calibration_.p9;
|
||||
const float p10 = this->calibration_.p10;
|
||||
|
||||
float var1 = 0;
|
||||
float var2 = 0;
|
||||
float var3 = 0;
|
||||
float var4 = 0;
|
||||
float calc_pres = 0;
|
||||
|
||||
var1 = (tfine / 2.0f) - 64000.0f;
|
||||
var2 = var1 * var1 * (p6 / 131072.0f);
|
||||
var2 = var2 + var1 * p5 * 2.0f;
|
||||
var2 = (var2 / 4.0f) + (p4 * 65536.0f);
|
||||
var1 = (((p3 * var1 * var1) / 16384.0f) + (p2 * var1)) / 524288.0f;
|
||||
var1 = (1.0f + (var1 / 32768.0f)) * p1;
|
||||
calc_pres = 1048576.0f - float(raw_pressure);
|
||||
|
||||
/* Avoid exception caused by division by zero */
|
||||
if (int(var1) != 0) {
|
||||
calc_pres = ((calc_pres - (var2 / 4096.0f)) * 6250.0f) / var1;
|
||||
var1 = (p9 * calc_pres * calc_pres) / 2147483648.0f;
|
||||
var2 = calc_pres * (p8 / 32768.0f);
|
||||
var4 = calc_pres / 256.0f;
|
||||
var3 = var4 * var4 * var4 * (p10 / 131072.0f);
|
||||
calc_pres = calc_pres + (var1 + var2 + var3 + (p7 * 128.0f)) / 16.0f;
|
||||
} else {
|
||||
calc_pres = 0;
|
||||
}
|
||||
|
||||
return calc_pres / 100.0f;
|
||||
}
|
||||
|
||||
float BME680Component::calc_humidity_(uint16_t raw_humidity) {
|
||||
const float tfine = this->calibration_.tfine;
|
||||
const float h1 = this->calibration_.h1;
|
||||
const float h2 = this->calibration_.h2;
|
||||
const float h3 = this->calibration_.h3;
|
||||
const float h4 = this->calibration_.h4;
|
||||
const float h5 = this->calibration_.h5;
|
||||
const float h6 = this->calibration_.h6;
|
||||
const float h7 = this->calibration_.h7;
|
||||
|
||||
float calc_hum = 0;
|
||||
float var1 = 0;
|
||||
float var2 = 0;
|
||||
float var3 = 0;
|
||||
float var4 = 0;
|
||||
float temp_comp;
|
||||
|
||||
/* compensated temperature data*/
|
||||
temp_comp = tfine / 5120.0f;
|
||||
|
||||
var1 = float(raw_humidity) - (h1 * 16.0f + ((h3 / 2.0f) * temp_comp));
|
||||
var2 = var1 *
|
||||
(((h2 / 262144.0f) * (1.0f + ((h4 / 16384.0f) * temp_comp) + ((h5 / 1048576.0f) * temp_comp * temp_comp))));
|
||||
var3 = h6 / 16384.0f;
|
||||
var4 = h7 / 2097152.0f;
|
||||
|
||||
calc_hum = var2 + (var3 + var4 * temp_comp) * var2 * var2;
|
||||
|
||||
if (calc_hum > 100.0f)
|
||||
calc_hum = 100.0f;
|
||||
else if (calc_hum < 0.0f)
|
||||
calc_hum = 0.0f;
|
||||
|
||||
return calc_hum;
|
||||
}
|
||||
uint32_t BME680Component::calc_gas_resistance_(uint16_t raw_gas, uint8_t range) {
|
||||
float calc_gas_res;
|
||||
float var1 = 0;
|
||||
float var2 = 0;
|
||||
float var3 = 0;
|
||||
const float range_sw_err = this->calibration_.range_sw_err;
|
||||
|
||||
var1 = 1340.0f + (5.0f * range_sw_err);
|
||||
var2 = var1 * (1.0f + BME680_GAS_LOOKUP_TABLE_1[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));
|
||||
|
||||
return static_cast<uint32_t>(calc_gas_res);
|
||||
}
|
||||
uint32_t BME680Component::calc_meas_duration_() {
|
||||
uint32_t tph_dur; // Calculate in us
|
||||
uint32_t meas_cycles;
|
||||
const uint8_t os_to_meas_cycles[6] = {0, 1, 2, 4, 8, 16};
|
||||
|
||||
meas_cycles = os_to_meas_cycles[this->temperature_oversampling_];
|
||||
meas_cycles += os_to_meas_cycles[this->pressure_oversampling_];
|
||||
meas_cycles += os_to_meas_cycles[this->humidity_oversampling_];
|
||||
|
||||
/* TPH measurement duration */
|
||||
tph_dur = meas_cycles * 1963u;
|
||||
tph_dur += 477 * 4; // TPH switching duration
|
||||
tph_dur += 477 * 5; // Gas measurement duration
|
||||
tph_dur += 500; // Get it to the closest whole number.
|
||||
tph_dur /= 1000; // Convert to ms
|
||||
|
||||
tph_dur += 1; // Wake up duration of 1ms
|
||||
|
||||
/* The remaining time should be used for heating */
|
||||
tph_dur += this->heater_duration_;
|
||||
|
||||
return tph_dur;
|
||||
}
|
||||
void BME680Component::set_temperature_oversampling(BME680Oversampling temperature_oversampling) {
|
||||
this->temperature_oversampling_ = temperature_oversampling;
|
||||
}
|
||||
void BME680Component::set_pressure_oversampling(BME680Oversampling pressure_oversampling) {
|
||||
this->pressure_oversampling_ = pressure_oversampling;
|
||||
}
|
||||
void BME680Component::set_humidity_oversampling(BME680Oversampling humidity_oversampling) {
|
||||
this->humidity_oversampling_ = humidity_oversampling;
|
||||
}
|
||||
void BME680Component::set_iir_filter(BME680IIRFilter iir_filter) { this->iir_filter_ = iir_filter; }
|
||||
void BME680Component::set_heater(uint16_t heater_temperature, uint16_t heater_duration) {
|
||||
this->heater_temperature_ = heater_temperature;
|
||||
this->heater_duration_ = heater_duration;
|
||||
}
|
||||
|
||||
} // namespace bme680
|
||||
} // namespace esphome
|
||||
141
esphome/components/bme680/bme680.h
Normal file
141
esphome/components/bme680/bme680.h
Normal file
@@ -0,0 +1,141 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace bme680 {
|
||||
|
||||
/// Enum listing all IIR Filter options for the BME680.
|
||||
enum BME680IIRFilter {
|
||||
BME680_IIR_FILTER_OFF = 0b000,
|
||||
BME680_IIR_FILTER_1X = 0b001,
|
||||
BME680_IIR_FILTER_3X = 0b010,
|
||||
BME680_IIR_FILTER_7X = 0b011,
|
||||
BME680_IIR_FILTER_15X = 0b100,
|
||||
BME680_IIR_FILTER_31X = 0b101,
|
||||
BME680_IIR_FILTER_63X = 0b110,
|
||||
BME680_IIR_FILTER_127X = 0b111,
|
||||
};
|
||||
|
||||
/// Enum listing all oversampling options for the BME680.
|
||||
enum BME680Oversampling {
|
||||
BME680_OVERSAMPLING_NONE = 0b000,
|
||||
BME680_OVERSAMPLING_1X = 0b001,
|
||||
BME680_OVERSAMPLING_2X = 0b010,
|
||||
BME680_OVERSAMPLING_4X = 0b011,
|
||||
BME680_OVERSAMPLING_8X = 0b100,
|
||||
BME680_OVERSAMPLING_16X = 0b101,
|
||||
};
|
||||
|
||||
/// Struct for storing calibration data for the BME680.
|
||||
struct BME680CalibrationData {
|
||||
uint16_t t1;
|
||||
uint16_t t2;
|
||||
uint8_t t3;
|
||||
|
||||
uint16_t p1;
|
||||
int16_t p2;
|
||||
int8_t p3;
|
||||
int16_t p4;
|
||||
int16_t p5;
|
||||
int8_t p6;
|
||||
int8_t p7;
|
||||
int16_t p8;
|
||||
int16_t p9;
|
||||
int8_t p10;
|
||||
|
||||
uint16_t h1;
|
||||
uint16_t h2;
|
||||
int8_t h3;
|
||||
int8_t h4;
|
||||
int8_t h5;
|
||||
uint8_t h6;
|
||||
int8_t h7;
|
||||
|
||||
int8_t gh1;
|
||||
int16_t gh2;
|
||||
int8_t gh3;
|
||||
|
||||
uint8_t res_heat_range;
|
||||
uint8_t res_heat_val;
|
||||
uint8_t range_sw_err;
|
||||
|
||||
float tfine;
|
||||
uint8_t ambient_temperature;
|
||||
};
|
||||
|
||||
class BME680Component : public PollingComponent, public i2c::I2CDevice {
|
||||
public:
|
||||
BME680Component(uint32_t update_interval) : PollingComponent(update_interval) {}
|
||||
|
||||
/// Set the temperature oversampling value. Defaults to 16X.
|
||||
void set_temperature_oversampling(BME680Oversampling temperature_oversampling);
|
||||
/// Set the pressure oversampling value. Defaults to 16X.
|
||||
void set_pressure_oversampling(BME680Oversampling pressure_oversampling);
|
||||
/// Set the humidity oversampling value. Defaults to 16X.
|
||||
void set_humidity_oversampling(BME680Oversampling humidity_oversampling);
|
||||
/// Set the IIR Filter value. Defaults to no IIR Filter.
|
||||
void set_iir_filter(BME680IIRFilter iir_filter);
|
||||
|
||||
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
|
||||
void set_pressure_sensor(sensor::Sensor *pressure_sensor) { pressure_sensor_ = pressure_sensor; }
|
||||
void set_humidity_sensor(sensor::Sensor *humidity_sensor) { humidity_sensor_ = humidity_sensor; }
|
||||
void set_gas_resistance_sensor(sensor::Sensor *gas_resistance_sensor) {
|
||||
gas_resistance_sensor_ = gas_resistance_sensor;
|
||||
}
|
||||
|
||||
/** Set how the internal heater should operate.
|
||||
*
|
||||
* By default, the heater is off. If you want to to have more reliable
|
||||
* humidity and Gas Resistance values, you can however setup the heater
|
||||
* with this method.
|
||||
*
|
||||
* @param heater_temperature The temperature of the heater in °C.
|
||||
* @param heater_duration The duration in ms that the heater should turn on for when measuring.
|
||||
*/
|
||||
void set_heater(uint16_t heater_temperature, uint16_t heater_duration);
|
||||
|
||||
// ========== INTERNAL METHODS ==========
|
||||
// (In most use cases you won't need these)
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override;
|
||||
void update() override;
|
||||
|
||||
protected:
|
||||
/// Calculate the heater resistance value to send to the BME680 register.
|
||||
uint8_t calc_heater_resistance_(uint16_t temperature);
|
||||
/// Calculate the heater duration value to send to the BME680 register.
|
||||
uint8_t calc_heater_duration_(uint16_t duration);
|
||||
/// Read data from the BME680 and publish results.
|
||||
void read_data_();
|
||||
|
||||
/// Calculate the temperature in °C using the provided raw ADC value.
|
||||
float calc_temperature_(uint32_t raw_temperature);
|
||||
/// Calculate the pressure in hPa using the provided raw ADC value.
|
||||
float calc_pressure_(uint32_t raw_pressure);
|
||||
/// Calculate the relative humidity in % using the provided raw ADC value.
|
||||
float calc_humidity_(uint16_t raw_humidity);
|
||||
/// Calculate the gas resistance in Ω using the provided raw ADC value.
|
||||
uint32_t calc_gas_resistance_(uint16_t raw_gas, uint8_t range);
|
||||
/// Calculate how long the sensor will take until we can retrieve data.
|
||||
uint32_t calc_meas_duration_();
|
||||
|
||||
BME680CalibrationData calibration_;
|
||||
BME680Oversampling temperature_oversampling_{BME680_OVERSAMPLING_16X};
|
||||
BME680Oversampling pressure_oversampling_{BME680_OVERSAMPLING_16X};
|
||||
BME680Oversampling humidity_oversampling_{BME680_OVERSAMPLING_16X};
|
||||
BME680IIRFilter iir_filter_{BME680_IIR_FILTER_OFF};
|
||||
uint16_t heater_temperature_{320};
|
||||
uint16_t heater_duration_{150};
|
||||
|
||||
sensor::Sensor *temperature_sensor_;
|
||||
sensor::Sensor *pressure_sensor_;
|
||||
sensor::Sensor *humidity_sensor_;
|
||||
sensor::Sensor *gas_resistance_sensor_;
|
||||
};
|
||||
|
||||
} // namespace bme680
|
||||
} // namespace esphome
|
||||
101
esphome/components/bme680/sensor.py
Normal file
101
esphome/components/bme680/sensor.py
Normal file
@@ -0,0 +1,101 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import core
|
||||
from esphome.components import i2c, sensor
|
||||
from esphome.const import CONF_DURATION, CONF_GAS_RESISTANCE, CONF_HEATER, \
|
||||
CONF_HUMIDITY, CONF_ID, CONF_IIR_FILTER, CONF_OVERSAMPLING, CONF_PRESSURE, \
|
||||
CONF_TEMPERATURE, CONF_UPDATE_INTERVAL, UNIT_OHM, ICON_GAS_CYLINDER, UNIT_CELSIUS, \
|
||||
ICON_THERMOMETER, UNIT_HECTOPASCAL, ICON_GAUGE, ICON_WATER_PERCENT, UNIT_PERCENT
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
bme680_ns = cg.esphome_ns.namespace('bme680')
|
||||
BME680Oversampling = bme680_ns.enum('BME680Oversampling')
|
||||
OVERSAMPLING_OPTIONS = {
|
||||
'NONE': BME680Oversampling.BME680_OVERSAMPLING_NONE,
|
||||
'1X': BME680Oversampling.BME680_OVERSAMPLING_1X,
|
||||
'2X': BME680Oversampling.BME680_OVERSAMPLING_2X,
|
||||
'4X': BME680Oversampling.BME680_OVERSAMPLING_4X,
|
||||
'8X': BME680Oversampling.BME680_OVERSAMPLING_8X,
|
||||
'16X': BME680Oversampling.BME680_OVERSAMPLING_16X,
|
||||
}
|
||||
|
||||
BME680IIRFilter = bme680_ns.enum('BME680IIRFilter')
|
||||
IIR_FILTER_OPTIONS = {
|
||||
'OFF': BME680IIRFilter.BME680_IIR_FILTER_OFF,
|
||||
'1X': BME680IIRFilter.BME680_IIR_FILTER_1X,
|
||||
'3X': BME680IIRFilter.BME680_IIR_FILTER_3X,
|
||||
'7X': BME680IIRFilter.BME680_IIR_FILTER_7X,
|
||||
'15X': BME680IIRFilter.BME680_IIR_FILTER_15X,
|
||||
'31X': BME680IIRFilter.BME680_IIR_FILTER_31X,
|
||||
'63X': BME680IIRFilter.BME680_IIR_FILTER_63X,
|
||||
'127X': BME680IIRFilter.BME680_IIR_FILTER_127X,
|
||||
}
|
||||
|
||||
BME680Component = bme680_ns.class_('BME680Component', cg.PollingComponent, i2c.I2CDevice)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(BME680Component),
|
||||
cv.Optional(CONF_TEMPERATURE): cv.nameable(
|
||||
sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({
|
||||
cv.Optional(CONF_OVERSAMPLING, default='16X'):
|
||||
cv.one_of(*OVERSAMPLING_OPTIONS, upper=True),
|
||||
})),
|
||||
cv.Optional(CONF_PRESSURE): cv.nameable(
|
||||
sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_GAUGE, 1).extend({
|
||||
cv.Optional(CONF_OVERSAMPLING, default='16X'):
|
||||
cv.one_of(*OVERSAMPLING_OPTIONS, upper=True),
|
||||
})),
|
||||
cv.Optional(CONF_HUMIDITY): cv.nameable(
|
||||
sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1).extend({
|
||||
cv.Optional(CONF_OVERSAMPLING, default='16X'):
|
||||
cv.one_of(*OVERSAMPLING_OPTIONS, upper=True),
|
||||
})),
|
||||
cv.Optional(CONF_GAS_RESISTANCE): cv.nameable(
|
||||
sensor.sensor_schema(UNIT_OHM, ICON_GAS_CYLINDER, 1)),
|
||||
cv.Optional(CONF_IIR_FILTER, default='OFF'): cv.one_of(*IIR_FILTER_OPTIONS, upper=True),
|
||||
cv.Optional(CONF_HEATER): cv.Any(None, cv.All(cv.Schema({
|
||||
cv.Optional(CONF_TEMPERATURE, default=320): cv.All(cv.Coerce(int), cv.Range(200, 400)),
|
||||
cv.Optional(CONF_DURATION, default='150ms'): cv.All(
|
||||
cv.positive_time_period_milliseconds, cv.Range(max=core.TimePeriod(milliseconds=4032)))
|
||||
}, cv.has_at_least_one_key(CONF_TEMPERATURE, CONF_DURATION)))),
|
||||
cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval,
|
||||
}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x76))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID], config[CONF_UPDATE_INTERVAL])
|
||||
yield cg.register_component(var, config)
|
||||
yield i2c.register_i2c_device(var, config)
|
||||
|
||||
if CONF_TEMPERATURE in config:
|
||||
conf = config[CONF_TEMPERATURE]
|
||||
sens = yield sensor.new_sensor(conf)
|
||||
cg.add(var.set_temperature_sensor(sens))
|
||||
cg.add(var.set_temperature_oversampling(OVERSAMPLING_OPTIONS[conf[CONF_OVERSAMPLING]]))
|
||||
|
||||
if CONF_PRESSURE in config:
|
||||
conf = config[CONF_PRESSURE]
|
||||
sens = yield sensor.new_sensor(conf)
|
||||
cg.add(var.set_pressure_sensor(sens))
|
||||
cg.add(var.set_pressure_oversampling(OVERSAMPLING_OPTIONS[conf[CONF_OVERSAMPLING]]))
|
||||
|
||||
if CONF_HUMIDITY in config:
|
||||
conf = config[CONF_HUMIDITY]
|
||||
sens = yield sensor.new_sensor(conf)
|
||||
cg.add(var.set_humidity_sensor(sens))
|
||||
cg.add(var.set_humidity_oversampling(OVERSAMPLING_OPTIONS[conf[CONF_OVERSAMPLING]]))
|
||||
|
||||
if CONF_GAS_RESISTANCE in config:
|
||||
conf = config[CONF_GAS_RESISTANCE]
|
||||
sens = yield sensor.new_sensor(conf)
|
||||
cg.add(var.set_gas_resistance_sensor(sens))
|
||||
|
||||
cg.add(var.set_iir_filter(IIR_FILTER_OPTIONS[config[CONF_IIR_FILTER]]))
|
||||
|
||||
if CONF_HEATER in config:
|
||||
conf = config[CONF_HEATER]
|
||||
if not conf:
|
||||
cg.add(var.set_heater(0, 0))
|
||||
else:
|
||||
cg.add(var.set_heater(conf[CONF_TEMPERATURE], conf[CONF_DURATION]))
|
||||
Reference in New Issue
Block a user