mirror of
https://github.com/esphome/esphome.git
synced 2024-11-25 00:18:11 +01:00
Add support for BMP388 / BMP 390 pressure and temperature sensor (#2716)
This commit is contained in:
parent
df929f9445
commit
5e1e543b06
6 changed files with 735 additions and 0 deletions
|
@ -31,6 +31,7 @@ esphome/components/binary_sensor/* @esphome/core
|
||||||
esphome/components/bl0940/* @tobias-
|
esphome/components/bl0940/* @tobias-
|
||||||
esphome/components/ble_client/* @buxtronix
|
esphome/components/ble_client/* @buxtronix
|
||||||
esphome/components/bme680_bsec/* @trvrnrth
|
esphome/components/bme680_bsec/* @trvrnrth
|
||||||
|
esphome/components/bmp3xx/* @martgras
|
||||||
esphome/components/button/* @esphome/core
|
esphome/components/button/* @esphome/core
|
||||||
esphome/components/canbus/* @danielschramm @mvturnho
|
esphome/components/canbus/* @danielschramm @mvturnho
|
||||||
esphome/components/cap1188/* @MrEditor97
|
esphome/components/cap1188/* @MrEditor97
|
||||||
|
|
0
esphome/components/bmp3xx/__init__.py
Normal file
0
esphome/components/bmp3xx/__init__.py
Normal file
388
esphome/components/bmp3xx/bmp3xx.cpp
Normal file
388
esphome/components/bmp3xx/bmp3xx.cpp
Normal file
|
@ -0,0 +1,388 @@
|
||||||
|
/*
|
||||||
|
based on BMP388_DEV by Martin Lindupp
|
||||||
|
under MIT License (MIT)
|
||||||
|
Copyright (C) Martin Lindupp 2020
|
||||||
|
http://github.com/MartinL1/BMP388_DEV
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "bmp3xx.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace bmp3xx {
|
||||||
|
|
||||||
|
static const char *const TAG = "bmp3xx.sensor";
|
||||||
|
|
||||||
|
static const LogString *chip_type_to_str(uint8_t chip_type) {
|
||||||
|
switch (chip_type) {
|
||||||
|
case BMP388_ID:
|
||||||
|
return LOG_STR("BMP 388");
|
||||||
|
case BMP390_ID:
|
||||||
|
return LOG_STR("BMP 390");
|
||||||
|
default:
|
||||||
|
return LOG_STR("Unknown Chip Type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const LogString *oversampling_to_str(Oversampling oversampling) {
|
||||||
|
switch (oversampling) {
|
||||||
|
case Oversampling::OVERSAMPLING_NONE:
|
||||||
|
return LOG_STR("None");
|
||||||
|
case Oversampling::OVERSAMPLING_X2:
|
||||||
|
return LOG_STR("2x");
|
||||||
|
case Oversampling::OVERSAMPLING_X4:
|
||||||
|
return LOG_STR("4x");
|
||||||
|
case Oversampling::OVERSAMPLING_X8:
|
||||||
|
return LOG_STR("8x");
|
||||||
|
case Oversampling::OVERSAMPLING_X16:
|
||||||
|
return LOG_STR("16x");
|
||||||
|
case Oversampling::OVERSAMPLING_X32:
|
||||||
|
return LOG_STR("32x");
|
||||||
|
default:
|
||||||
|
return LOG_STR("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const LogString *iir_filter_to_str(IIRFilter filter) {
|
||||||
|
switch (filter) {
|
||||||
|
case IIRFilter::IIR_FILTER_OFF:
|
||||||
|
return LOG_STR("OFF");
|
||||||
|
case IIRFilter::IIR_FILTER_2:
|
||||||
|
return LOG_STR("2x");
|
||||||
|
case IIRFilter::IIR_FILTER_4:
|
||||||
|
return LOG_STR("4x");
|
||||||
|
case IIRFilter::IIR_FILTER_8:
|
||||||
|
return LOG_STR("8x");
|
||||||
|
case IIRFilter::IIR_FILTER_16:
|
||||||
|
return LOG_STR("16x");
|
||||||
|
case IIRFilter::IIR_FILTER_32:
|
||||||
|
return LOG_STR("32x");
|
||||||
|
case IIRFilter::IIR_FILTER_64:
|
||||||
|
return LOG_STR("64x");
|
||||||
|
case IIRFilter::IIR_FILTER_128:
|
||||||
|
return LOG_STR("128x");
|
||||||
|
default:
|
||||||
|
return LOG_STR("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BMP3XXComponent::setup() {
|
||||||
|
this->error_code_ = NONE;
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up BMP3XX...");
|
||||||
|
// Call the Device base class "initialise" function
|
||||||
|
if (!reset()) {
|
||||||
|
ESP_LOGE(TAG, "Failed to reset BMP3XX...");
|
||||||
|
this->error_code_ = ERROR_SENSOR_RESET;
|
||||||
|
this->mark_failed();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!read_byte(BMP388_CHIP_ID, &this->chip_id_.reg)) {
|
||||||
|
ESP_LOGE(TAG, "Can't read chip id");
|
||||||
|
this->error_code_ = ERROR_COMMUNICATION_FAILED;
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ESP_LOGCONFIG(TAG, "Chip %s Id 0x%X", LOG_STR_ARG(chip_type_to_str(this->chip_id_.reg)), this->chip_id_.reg);
|
||||||
|
|
||||||
|
if (chip_id_.reg != BMP388_ID && chip_id_.reg != BMP390_ID) {
|
||||||
|
ESP_LOGE(TAG, "Unknown chip id - is this really a BMP388 or BMP390?");
|
||||||
|
this->error_code_ = ERROR_WRONG_CHIP_ID;
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// set sensor in sleep mode
|
||||||
|
stop_conversion();
|
||||||
|
// Read the calibration parameters into the params structure
|
||||||
|
if (!read_bytes(BMP388_TRIM_PARAMS, (uint8_t *) &compensation_params_, sizeof(compensation_params_))) {
|
||||||
|
ESP_LOGE(TAG, "Can't read calibration data");
|
||||||
|
this->error_code_ = ERROR_COMMUNICATION_FAILED;
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
compensation_float_params_.param_T1 =
|
||||||
|
(float) compensation_params_.param_T1 / powf(2.0f, -8.0f); // Calculate the floating point trim parameters
|
||||||
|
compensation_float_params_.param_T2 = (float) compensation_params_.param_T2 / powf(2.0f, 30.0f);
|
||||||
|
compensation_float_params_.param_T3 = (float) compensation_params_.param_T3 / powf(2.0f, 48.0f);
|
||||||
|
compensation_float_params_.param_P1 = ((float) compensation_params_.param_P1 - powf(2.0f, 14.0f)) / powf(2.0f, 20.0f);
|
||||||
|
compensation_float_params_.param_P2 = ((float) compensation_params_.param_P2 - powf(2.0f, 14.0f)) / powf(2.0f, 29.0f);
|
||||||
|
compensation_float_params_.param_P3 = (float) compensation_params_.param_P3 / powf(2.0f, 32.0f);
|
||||||
|
compensation_float_params_.param_P4 = (float) compensation_params_.param_P4 / powf(2.0f, 37.0f);
|
||||||
|
compensation_float_params_.param_P5 = (float) compensation_params_.param_P5 / powf(2.0f, -3.0f);
|
||||||
|
compensation_float_params_.param_P6 = (float) compensation_params_.param_P6 / powf(2.0f, 6.0f);
|
||||||
|
compensation_float_params_.param_P7 = (float) compensation_params_.param_P7 / powf(2.0f, 8.0f);
|
||||||
|
compensation_float_params_.param_P8 = (float) compensation_params_.param_P8 / powf(2.0f, 15.0f);
|
||||||
|
compensation_float_params_.param_P9 = (float) compensation_params_.param_P9 / powf(2.0f, 48.0f);
|
||||||
|
compensation_float_params_.param_P10 = (float) compensation_params_.param_P10 / powf(2.0f, 48.0f);
|
||||||
|
compensation_float_params_.param_P11 = (float) compensation_params_.param_P11 / powf(2.0f, 65.0f);
|
||||||
|
|
||||||
|
// Initialise the BMP388 IIR filter register
|
||||||
|
if (!set_iir_filter(this->iir_filter_)) {
|
||||||
|
ESP_LOGE(TAG, "Failed to set IIR filter");
|
||||||
|
this->error_code_ = ERROR_COMMUNICATION_FAILED;
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set power control registers
|
||||||
|
pwr_ctrl_.bit.press_en = 1;
|
||||||
|
pwr_ctrl_.bit.temp_en = 1;
|
||||||
|
// Disable pressure if no sensor defined
|
||||||
|
// keep temperature enabled since it's needed for compensation
|
||||||
|
if (this->pressure_sensor_ == nullptr) {
|
||||||
|
pwr_ctrl_.bit.press_en = 0;
|
||||||
|
this->pressure_oversampling_ = OVERSAMPLING_NONE;
|
||||||
|
}
|
||||||
|
// just disable oeversampling for temp if not used
|
||||||
|
if (this->temperature_sensor_ == nullptr) {
|
||||||
|
this->temperature_oversampling_ = OVERSAMPLING_NONE;
|
||||||
|
}
|
||||||
|
// Initialise the BMP388 oversampling register
|
||||||
|
if (!set_oversampling_register(this->pressure_oversampling_, this->temperature_oversampling_)) {
|
||||||
|
ESP_LOGE(TAG, "Failed to set oversampling register");
|
||||||
|
this->error_code_ = ERROR_COMMUNICATION_FAILED;
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BMP3XXComponent::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "BMP3XX:");
|
||||||
|
ESP_LOGCONFIG(TAG, " Type: %s (0x%X)", LOG_STR_ARG(chip_type_to_str(this->chip_id_.reg)), this->chip_id_.reg);
|
||||||
|
LOG_I2C_DEVICE(this);
|
||||||
|
switch (this->error_code_) {
|
||||||
|
case NONE:
|
||||||
|
break;
|
||||||
|
case ERROR_COMMUNICATION_FAILED:
|
||||||
|
ESP_LOGE(TAG, "Communication with BMP3XX failed!");
|
||||||
|
break;
|
||||||
|
case ERROR_WRONG_CHIP_ID:
|
||||||
|
ESP_LOGE(
|
||||||
|
TAG,
|
||||||
|
"BMP3XX has wrong chip ID (reported id: 0x%X) - please check if you are really using a BMP 388 or BMP 390",
|
||||||
|
this->chip_id_.reg);
|
||||||
|
break;
|
||||||
|
case ERROR_SENSOR_RESET:
|
||||||
|
ESP_LOGE(TAG, "BMP3XX failed to reset");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ESP_LOGE(TAG, "BMP3XX error code %d", (int) this->error_code_);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ESP_LOGCONFIG(TAG, " IIR Filter: %s", LOG_STR_ARG(iir_filter_to_str(this->iir_filter_)));
|
||||||
|
LOG_UPDATE_INTERVAL(this);
|
||||||
|
if (this->temperature_sensor_) {
|
||||||
|
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||||
|
ESP_LOGCONFIG(TAG, " Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->temperature_oversampling_)));
|
||||||
|
}
|
||||||
|
if (this->pressure_sensor_) {
|
||||||
|
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
|
||||||
|
ESP_LOGCONFIG(TAG, " Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->pressure_oversampling_)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
float BMP3XXComponent::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
|
inline uint8_t oversampling_to_time(Oversampling over_sampling) { return (1 << uint8_t(over_sampling)); }
|
||||||
|
|
||||||
|
void BMP3XXComponent::update() {
|
||||||
|
// Enable sensor
|
||||||
|
ESP_LOGV(TAG, "Sending conversion request...");
|
||||||
|
float meas_time = 1.0f;
|
||||||
|
// Ref: https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp390-ds002.pdf 3.9.2
|
||||||
|
meas_time += 2.02f * oversampling_to_time(this->temperature_oversampling_) + 0.163f;
|
||||||
|
meas_time += 2.02f * oversampling_to_time(this->pressure_oversampling_) + 0.392f;
|
||||||
|
meas_time += 0.234f;
|
||||||
|
if (!set_mode(FORCED_MODE)) {
|
||||||
|
ESP_LOGE(TAG, "Failed start forced mode");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGVV(TAG, "measurement time %d", uint32_t(ceilf(meas_time)));
|
||||||
|
this->set_timeout("data", uint32_t(ceilf(meas_time)), [this]() {
|
||||||
|
float temperature = 0.0f;
|
||||||
|
float pressure = 0.0f;
|
||||||
|
if (this->pressure_sensor_ != nullptr) {
|
||||||
|
if (!get_measurements(temperature, pressure)) {
|
||||||
|
ESP_LOGW(TAG, "Failed to read pressure and temperature - skipping update");
|
||||||
|
this->status_set_warning();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "Got temperature=%.1f°C pressure=%.1fhPa", temperature, pressure);
|
||||||
|
} else {
|
||||||
|
if (!get_temperature(temperature)) {
|
||||||
|
ESP_LOGW(TAG, "Failed to read temperature - skipping update");
|
||||||
|
this->status_set_warning();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "Got temperature=%.1f°C", temperature);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->temperature_sensor_ != nullptr)
|
||||||
|
this->temperature_sensor_->publish_state(temperature);
|
||||||
|
if (this->pressure_sensor_ != nullptr)
|
||||||
|
this->pressure_sensor_->publish_state(pressure);
|
||||||
|
this->status_clear_warning();
|
||||||
|
set_mode(SLEEP_MODE);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the BMP3XX
|
||||||
|
uint8_t BMP3XXComponent::reset() {
|
||||||
|
write_byte(BMP388_CMD, RESET_CODE); // Write the reset code to the command register
|
||||||
|
// Wait for 10ms
|
||||||
|
delay(10);
|
||||||
|
this->read_byte(BMP388_EVENT, &event_.reg); // Read the BMP388's event register
|
||||||
|
return event_.bit.por_detected; // Return if device reset is complete
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a one shot measurement in FORCED_MODE
|
||||||
|
bool BMP3XXComponent::start_forced_conversion() {
|
||||||
|
// Only set FORCED_MODE if we're already in SLEEP_MODE
|
||||||
|
if (pwr_ctrl_.bit.mode == SLEEP_MODE) {
|
||||||
|
return set_mode(FORCED_MODE);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the conversion and return to SLEEP_MODE
|
||||||
|
bool BMP3XXComponent::stop_conversion() { return set_mode(SLEEP_MODE); }
|
||||||
|
|
||||||
|
// Set the pressure oversampling rate
|
||||||
|
bool BMP3XXComponent::set_pressure_oversampling(Oversampling oversampling) {
|
||||||
|
osr_.bit.osr_p = oversampling;
|
||||||
|
return this->write_byte(BMP388_OSR, osr_.reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the temperature oversampling rate
|
||||||
|
bool BMP3XXComponent::set_temperature_oversampling(Oversampling oversampling) {
|
||||||
|
osr_.bit.osr_t = oversampling;
|
||||||
|
return this->write_byte(BMP388_OSR, osr_.reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the IIR filter setting
|
||||||
|
bool BMP3XXComponent::set_iir_filter(IIRFilter iir_filter) {
|
||||||
|
config_.bit.iir_filter = iir_filter;
|
||||||
|
return this->write_byte(BMP388_CONFIG, config_.reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get temperature
|
||||||
|
bool BMP3XXComponent::get_temperature(float &temperature) {
|
||||||
|
// Check if a measurement is ready
|
||||||
|
if (!data_ready()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint8_t data[3];
|
||||||
|
// Read the temperature
|
||||||
|
if (!this->read_bytes(BMP388_DATA_3, &data[0], 3)) {
|
||||||
|
ESP_LOGE(TAG, "Failed to read temperature");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Copy the temperature data into the adc variables
|
||||||
|
int32_t adc_temp = (int32_t) data[2] << 16 | (int32_t) data[1] << 8 | (int32_t) data[0];
|
||||||
|
// Temperature compensation (function from BMP388 datasheet)
|
||||||
|
temperature = bmp388_compensate_temperature_((float) adc_temp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the pressure
|
||||||
|
bool BMP3XXComponent::get_pressure(float &pressure) {
|
||||||
|
float temperature;
|
||||||
|
return get_measurements(temperature, pressure);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get temperature and pressure
|
||||||
|
bool BMP3XXComponent::get_measurements(float &temperature, float &pressure) {
|
||||||
|
// Check if a measurement is ready
|
||||||
|
if (!data_ready()) {
|
||||||
|
ESP_LOGD(TAG, "BMP3XX Get measurement - data not ready skipping update");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t data[6];
|
||||||
|
// Read the temperature and pressure data
|
||||||
|
if (!this->read_bytes(BMP388_DATA_0, &data[0], 6)) {
|
||||||
|
ESP_LOGE(TAG, "Failed to read measurements");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Copy the temperature and pressure data into the adc variables
|
||||||
|
int32_t adc_pres = (int32_t) data[2] << 16 | (int32_t) data[1] << 8 | (int32_t) data[0];
|
||||||
|
int32_t adc_temp = (int32_t) data[5] << 16 | (int32_t) data[4] << 8 | (int32_t) data[3];
|
||||||
|
|
||||||
|
// Temperature compensation (function from BMP388 datasheet)
|
||||||
|
temperature = bmp388_compensate_temperature_((float) adc_temp);
|
||||||
|
// Pressure compensation (function from BMP388 datasheet)
|
||||||
|
pressure = bmp388_compensate_pressure_((float) adc_pres, temperature);
|
||||||
|
// Calculate the pressure in millibar/hPa
|
||||||
|
pressure /= 100.0f;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the BMP388's mode in the power control register
|
||||||
|
bool BMP3XXComponent::set_mode(OperationMode mode) {
|
||||||
|
pwr_ctrl_.bit.mode = mode;
|
||||||
|
return this->write_byte(BMP388_PWR_CTRL, pwr_ctrl_.reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the BMP388 oversampling register
|
||||||
|
bool BMP3XXComponent::set_oversampling_register(Oversampling pressure_oversampling,
|
||||||
|
Oversampling temperature_oversampling) {
|
||||||
|
osr_.reg = temperature_oversampling << 3 | pressure_oversampling;
|
||||||
|
return this->write_byte(BMP388_OSR, osr_.reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if measurement data is ready
|
||||||
|
bool BMP3XXComponent::data_ready() {
|
||||||
|
// If we're in SLEEP_MODE return immediately
|
||||||
|
if (pwr_ctrl_.bit.mode == SLEEP_MODE) {
|
||||||
|
ESP_LOGD(TAG, "Not ready - sensor is in sleep mode");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Read the interrupt status register
|
||||||
|
uint8_t status;
|
||||||
|
if (!this->read_byte(BMP388_INT_STATUS, &status)) {
|
||||||
|
ESP_LOGE(TAG, "Failed to read status register");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int_status_.reg = status;
|
||||||
|
ESP_LOGVV(TAG, "data ready status %d", status);
|
||||||
|
// If we're in FORCED_MODE switch back to SLEEP_MODE
|
||||||
|
if (int_status_.bit.drdy) {
|
||||||
|
if (pwr_ctrl_.bit.mode == FORCED_MODE) {
|
||||||
|
pwr_ctrl_.bit.mode = SLEEP_MODE;
|
||||||
|
}
|
||||||
|
return true; // The measurement is ready
|
||||||
|
}
|
||||||
|
return false; // The measurement is still pending
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Bosch BMP3XXComponent (Private) Member Functions
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
float BMP3XXComponent::bmp388_compensate_temperature_(float uncomp_temp) {
|
||||||
|
float partial_data1 = uncomp_temp - compensation_float_params_.param_T1;
|
||||||
|
float partial_data2 = partial_data1 * compensation_float_params_.param_T2;
|
||||||
|
return partial_data2 + partial_data1 * partial_data1 * compensation_float_params_.param_T3;
|
||||||
|
}
|
||||||
|
|
||||||
|
float BMP3XXComponent::bmp388_compensate_pressure_(float uncomp_press, float t_lin) {
|
||||||
|
float partial_data1 = compensation_float_params_.param_P6 * t_lin;
|
||||||
|
float partial_data2 = compensation_float_params_.param_P7 * t_lin * t_lin;
|
||||||
|
float partial_data3 = compensation_float_params_.param_P8 * t_lin * t_lin * t_lin;
|
||||||
|
float partial_out1 = compensation_float_params_.param_P5 + partial_data1 + partial_data2 + partial_data3;
|
||||||
|
partial_data1 = compensation_float_params_.param_P2 * t_lin;
|
||||||
|
partial_data2 = compensation_float_params_.param_P3 * t_lin * t_lin;
|
||||||
|
partial_data3 = compensation_float_params_.param_P4 * t_lin * t_lin * t_lin;
|
||||||
|
float partial_out2 =
|
||||||
|
uncomp_press * (compensation_float_params_.param_P1 + partial_data1 + partial_data2 + partial_data3);
|
||||||
|
partial_data1 = uncomp_press * uncomp_press;
|
||||||
|
partial_data2 = compensation_float_params_.param_P9 + compensation_float_params_.param_P10 * t_lin;
|
||||||
|
partial_data3 = partial_data1 * partial_data2;
|
||||||
|
float partial_data4 =
|
||||||
|
partial_data3 + uncomp_press * uncomp_press * uncomp_press * compensation_float_params_.param_P11;
|
||||||
|
return partial_out1 + partial_out2 + partial_data4;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace bmp3xx
|
||||||
|
} // namespace esphome
|
237
esphome/components/bmp3xx/bmp3xx.h
Normal file
237
esphome/components/bmp3xx/bmp3xx.h
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
/*
|
||||||
|
based on BMP388_DEV by Martin Lindupp
|
||||||
|
under MIT License (MIT)
|
||||||
|
Copyright (C) Martin Lindupp 2020
|
||||||
|
http://github.com/MartinL1/BMP388_DEV
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace bmp3xx {
|
||||||
|
|
||||||
|
static const uint8_t BMP388_ID = 0x50; // The BMP388 device ID
|
||||||
|
static const uint8_t BMP390_ID = 0x60; // The BMP390 device ID
|
||||||
|
static const uint8_t RESET_CODE = 0xB6; // The BMP388 reset code
|
||||||
|
|
||||||
|
/// BMP388_DEV Registers
|
||||||
|
enum {
|
||||||
|
BMP388_CHIP_ID = 0x00, // Chip ID register sub-address
|
||||||
|
BMP388_ERR_REG = 0x02, // Error register sub-address
|
||||||
|
BMP388_STATUS = 0x03, // Status register sub-address
|
||||||
|
BMP388_DATA_0 = 0x04, // Pressure eXtended Least Significant Byte (XLSB) register sub-address
|
||||||
|
BMP388_DATA_1 = 0x05, // Pressure Least Significant Byte (LSB) register sub-address
|
||||||
|
BMP388_DATA_2 = 0x06, // Pressure Most Significant Byte (MSB) register sub-address
|
||||||
|
BMP388_DATA_3 = 0x07, // Temperature eXtended Least Significant Byte (XLSB) register sub-address
|
||||||
|
BMP388_DATA_4 = 0x08, // Temperature Least Significant Byte (LSB) register sub-address
|
||||||
|
BMP388_DATA_5 = 0x09, // Temperature Most Significant Byte (MSB) register sub-address
|
||||||
|
BMP388_SENSORTIME_0 = 0x0C, // Sensor time register 0 sub-address
|
||||||
|
BMP388_SENSORTIME_1 = 0x0D, // Sensor time register 1 sub-address
|
||||||
|
BMP388_SENSORTIME_2 = 0x0E, // Sensor time register 2 sub-address
|
||||||
|
BMP388_EVENT = 0x10, // Event register sub-address
|
||||||
|
BMP388_INT_STATUS = 0x11, // Interrupt Status register sub-address
|
||||||
|
BMP388_INT_CTRL = 0x19, // Interrupt Control register sub-address
|
||||||
|
BMP388_IF_CONFIG = 0x1A, // Interface Configuration register sub-address
|
||||||
|
BMP388_PWR_CTRL = 0x1B, // Power Control register sub-address
|
||||||
|
BMP388_OSR = 0x1C, // Oversampling register sub-address
|
||||||
|
BMP388_ODR = 0x1D, // Output Data Rate register sub-address
|
||||||
|
BMP388_CONFIG = 0x1F, // Configuration register sub-address
|
||||||
|
BMP388_TRIM_PARAMS = 0x31, // Trim parameter registers' base sub-address
|
||||||
|
BMP388_CMD = 0x7E // Command register sub-address
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Device mode bitfield in the control and measurement register
|
||||||
|
enum OperationMode { SLEEP_MODE = 0x00, FORCED_MODE = 0x01, NORMAL_MODE = 0x03 };
|
||||||
|
|
||||||
|
/// Oversampling bit fields in the control and measurement register
|
||||||
|
enum Oversampling {
|
||||||
|
OVERSAMPLING_NONE = 0x00,
|
||||||
|
OVERSAMPLING_X2 = 0x01,
|
||||||
|
OVERSAMPLING_X4 = 0x02,
|
||||||
|
OVERSAMPLING_X8 = 0x03,
|
||||||
|
OVERSAMPLING_X16 = 0x04,
|
||||||
|
OVERSAMPLING_X32 = 0x05
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Infinite Impulse Response (IIR) filter bit field in the configuration register
|
||||||
|
enum IIRFilter {
|
||||||
|
IIR_FILTER_OFF = 0x00,
|
||||||
|
IIR_FILTER_2 = 0x01,
|
||||||
|
IIR_FILTER_4 = 0x02,
|
||||||
|
IIR_FILTER_8 = 0x03,
|
||||||
|
IIR_FILTER_16 = 0x04,
|
||||||
|
IIR_FILTER_32 = 0x05,
|
||||||
|
IIR_FILTER_64 = 0x06,
|
||||||
|
IIR_FILTER_128 = 0x07
|
||||||
|
};
|
||||||
|
|
||||||
|
/// This class implements support for the BMP3XX Temperature+Pressure i2c sensor.
|
||||||
|
class BMP3XXComponent : public PollingComponent, public i2c::I2CDevice {
|
||||||
|
public:
|
||||||
|
void setup() override;
|
||||||
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
|
void update() override;
|
||||||
|
|
||||||
|
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
|
||||||
|
void set_pressure_sensor(sensor::Sensor *pressure_sensor) { pressure_sensor_ = pressure_sensor; }
|
||||||
|
|
||||||
|
/// Set the oversampling value for the temperature sensor. Default is 16x.
|
||||||
|
void set_temperature_oversampling_config(Oversampling temperature_oversampling) {
|
||||||
|
this->temperature_oversampling_ = temperature_oversampling;
|
||||||
|
}
|
||||||
|
/// Set the oversampling value for the pressure sensor. Default is 16x.
|
||||||
|
void set_pressure_oversampling_config(Oversampling pressure_oversampling) {
|
||||||
|
this->pressure_oversampling_ = pressure_oversampling;
|
||||||
|
}
|
||||||
|
/// Set the IIR Filter used to increase accuracy, defaults to no IIR Filter.
|
||||||
|
void set_iir_filter_config(IIRFilter iir_filter) { this->iir_filter_ = iir_filter; }
|
||||||
|
|
||||||
|
/// Soft reset the sensor
|
||||||
|
uint8_t reset();
|
||||||
|
/// Start continuous measurement in NORMAL_MODE
|
||||||
|
bool start_normal_conversion();
|
||||||
|
/// Start a one shot measurement in FORCED_MODE
|
||||||
|
bool start_forced_conversion();
|
||||||
|
/// Stop the conversion and return to SLEEP_MODE
|
||||||
|
bool stop_conversion();
|
||||||
|
/// Set the pressure oversampling: OFF, X1, X2, X4, X8, X16, X32
|
||||||
|
bool set_pressure_oversampling(Oversampling pressure_oversampling);
|
||||||
|
/// Set the temperature oversampling: OFF, X1, X2, X4, X8, X16, X32
|
||||||
|
bool set_temperature_oversampling(Oversampling temperature_oversampling);
|
||||||
|
/// Set the IIR filter setting: OFF, 2, 3, 8, 16, 32
|
||||||
|
bool set_iir_filter(IIRFilter iir_filter);
|
||||||
|
/// Get a temperature measurement
|
||||||
|
bool get_temperature(float &temperature);
|
||||||
|
/// Get a pressure measurement
|
||||||
|
bool get_pressure(float &pressure);
|
||||||
|
/// Get a temperature and pressure measurement
|
||||||
|
bool get_measurements(float &temperature, float &pressure);
|
||||||
|
/// Get a temperature and pressure measurement
|
||||||
|
bool get_measurement();
|
||||||
|
/// Set the barometer mode
|
||||||
|
bool set_mode(OperationMode mode);
|
||||||
|
/// Set the BMP388 oversampling register
|
||||||
|
bool set_oversampling_register(Oversampling pressure_oversampling, Oversampling temperature_oversampling);
|
||||||
|
/// Checks if a measurement is ready
|
||||||
|
bool data_ready();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Oversampling temperature_oversampling_{OVERSAMPLING_X16};
|
||||||
|
Oversampling pressure_oversampling_{OVERSAMPLING_X16};
|
||||||
|
IIRFilter iir_filter_{IIR_FILTER_OFF};
|
||||||
|
OperationMode operation_mode_{FORCED_MODE};
|
||||||
|
sensor::Sensor *temperature_sensor_;
|
||||||
|
sensor::Sensor *pressure_sensor_;
|
||||||
|
enum ErrorCode {
|
||||||
|
NONE = 0,
|
||||||
|
ERROR_COMMUNICATION_FAILED,
|
||||||
|
ERROR_WRONG_CHIP_ID,
|
||||||
|
ERROR_SENSOR_STATUS,
|
||||||
|
ERROR_SENSOR_RESET,
|
||||||
|
} error_code_{NONE};
|
||||||
|
|
||||||
|
struct { // The BMP388 compensation trim parameters (coefficients)
|
||||||
|
uint16_t param_T1;
|
||||||
|
uint16_t param_T2;
|
||||||
|
int8_t param_T3;
|
||||||
|
int16_t param_P1;
|
||||||
|
int16_t param_P2;
|
||||||
|
int8_t param_P3;
|
||||||
|
int8_t param_P4;
|
||||||
|
uint16_t param_P5;
|
||||||
|
uint16_t param_P6;
|
||||||
|
int8_t param_P7;
|
||||||
|
int8_t param_P8;
|
||||||
|
int16_t param_P9;
|
||||||
|
int8_t param_P10;
|
||||||
|
int8_t param_P11;
|
||||||
|
} __attribute__((packed)) compensation_params_;
|
||||||
|
|
||||||
|
struct FloatParams { // The BMP388 float point compensation trim parameters
|
||||||
|
float param_T1;
|
||||||
|
float param_T2;
|
||||||
|
float param_T3;
|
||||||
|
float param_P1;
|
||||||
|
float param_P2;
|
||||||
|
float param_P3;
|
||||||
|
float param_P4;
|
||||||
|
float param_P5;
|
||||||
|
float param_P6;
|
||||||
|
float param_P7;
|
||||||
|
float param_P8;
|
||||||
|
float param_P9;
|
||||||
|
float param_P10;
|
||||||
|
float param_P11;
|
||||||
|
} compensation_float_params_;
|
||||||
|
|
||||||
|
union { // Copy of the BMP388's chip id register
|
||||||
|
struct {
|
||||||
|
uint8_t chip_id_nvm : 4;
|
||||||
|
uint8_t chip_id_fixed : 4;
|
||||||
|
} bit;
|
||||||
|
uint8_t reg;
|
||||||
|
} chip_id_ = {.reg = 0};
|
||||||
|
|
||||||
|
union { // Copy of the BMP388's event register
|
||||||
|
struct {
|
||||||
|
uint8_t por_detected : 1;
|
||||||
|
} bit;
|
||||||
|
uint8_t reg;
|
||||||
|
} event_ = {.reg = 0};
|
||||||
|
|
||||||
|
union { // Copy of the BMP388's interrupt status register
|
||||||
|
struct {
|
||||||
|
uint8_t fwm_int : 1;
|
||||||
|
uint8_t ffull_int : 1;
|
||||||
|
uint8_t : 1;
|
||||||
|
uint8_t drdy : 1;
|
||||||
|
} bit;
|
||||||
|
uint8_t reg;
|
||||||
|
} int_status_ = {.reg = 0};
|
||||||
|
|
||||||
|
union { // Copy of the BMP388's power control register
|
||||||
|
struct {
|
||||||
|
uint8_t press_en : 1;
|
||||||
|
uint8_t temp_en : 1;
|
||||||
|
uint8_t : 2;
|
||||||
|
uint8_t mode : 2;
|
||||||
|
} bit;
|
||||||
|
uint8_t reg;
|
||||||
|
} pwr_ctrl_ = {.reg = 0};
|
||||||
|
|
||||||
|
union { // Copy of the BMP388's oversampling register
|
||||||
|
struct {
|
||||||
|
uint8_t osr_p : 3;
|
||||||
|
uint8_t osr_t : 3;
|
||||||
|
} bit;
|
||||||
|
uint8_t reg;
|
||||||
|
} osr_ = {.reg = 0};
|
||||||
|
|
||||||
|
union { // Copy of the BMP388's output data rate register
|
||||||
|
struct {
|
||||||
|
uint8_t odr_sel : 5;
|
||||||
|
} bit;
|
||||||
|
uint8_t reg;
|
||||||
|
} odr_ = {.reg = 0};
|
||||||
|
|
||||||
|
union { // Copy of the BMP388's configuration register
|
||||||
|
struct {
|
||||||
|
uint8_t : 1;
|
||||||
|
uint8_t iir_filter : 3;
|
||||||
|
} bit;
|
||||||
|
uint8_t reg;
|
||||||
|
} config_ = {.reg = 0};
|
||||||
|
|
||||||
|
// Bosch temperature compensation function
|
||||||
|
float bmp388_compensate_temperature_(float uncomp_temp);
|
||||||
|
// Bosch pressure compensation function
|
||||||
|
float bmp388_compensate_pressure_(float uncomp_press, float t_lin);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace bmp3xx
|
||||||
|
} // namespace esphome
|
100
esphome/components/bmp3xx/sensor.py
Normal file
100
esphome/components/bmp3xx/sensor.py
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import i2c, sensor
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_ID,
|
||||||
|
CONF_IIR_FILTER,
|
||||||
|
CONF_OVERSAMPLING,
|
||||||
|
CONF_PRESSURE,
|
||||||
|
CONF_TEMPERATURE,
|
||||||
|
DEVICE_CLASS_PRESSURE,
|
||||||
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
UNIT_CELSIUS,
|
||||||
|
UNIT_HECTOPASCAL,
|
||||||
|
)
|
||||||
|
|
||||||
|
CODEOWNERS = ["@martgras"]
|
||||||
|
DEPENDENCIES = ["i2c"]
|
||||||
|
|
||||||
|
bmp3xx_ns = cg.esphome_ns.namespace("bmp3xx")
|
||||||
|
Oversampling = bmp3xx_ns.enum("Oversampling")
|
||||||
|
OVERSAMPLING_OPTIONS = {
|
||||||
|
"NONE": Oversampling.OVERSAMPLING_NONE,
|
||||||
|
"2X": Oversampling.OVERSAMPLING_X2,
|
||||||
|
"4X": Oversampling.OVERSAMPLING_X4,
|
||||||
|
"8X": Oversampling.OVERSAMPLING_X8,
|
||||||
|
"16X": Oversampling.OVERSAMPLING_X16,
|
||||||
|
"32x": Oversampling.OVERSAMPLING_X32,
|
||||||
|
}
|
||||||
|
|
||||||
|
IIRFilter = bmp3xx_ns.enum("IIRFilter")
|
||||||
|
IIR_FILTER_OPTIONS = {
|
||||||
|
"OFF": IIRFilter.IIR_FILTER_OFF,
|
||||||
|
"2X": IIRFilter.IIR_FILTER_2,
|
||||||
|
"4X": IIRFilter.IIR_FILTER_4,
|
||||||
|
"8X": IIRFilter.IIR_FILTER_8,
|
||||||
|
"16X": IIRFilter.IIR_FILTER_16,
|
||||||
|
"32X": IIRFilter.IIR_FILTER_32,
|
||||||
|
"64X": IIRFilter.IIR_FILTER_64,
|
||||||
|
"128X": IIRFilter.IIR_FILTER_128,
|
||||||
|
}
|
||||||
|
|
||||||
|
BMP3XXComponent = bmp3xx_ns.class_(
|
||||||
|
"BMP3XXComponent", cg.PollingComponent, i2c.I2CDevice
|
||||||
|
)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = (
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(BMP3XXComponent),
|
||||||
|
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
).extend(
|
||||||
|
{
|
||||||
|
cv.Optional(CONF_OVERSAMPLING, default="2X"): cv.enum(
|
||||||
|
OVERSAMPLING_OPTIONS, upper=True
|
||||||
|
),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_HECTOPASCAL,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_PRESSURE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
).extend(
|
||||||
|
{
|
||||||
|
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
|
||||||
|
OVERSAMPLING_OPTIONS, upper=True
|
||||||
|
),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum(
|
||||||
|
IIR_FILTER_OPTIONS, upper=True
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.polling_component_schema("60s"))
|
||||||
|
.extend(i2c.i2c_device_schema(0x77))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
await i2c.register_i2c_device(var, config)
|
||||||
|
cg.add(var.set_iir_filter_config(config[CONF_IIR_FILTER]))
|
||||||
|
if CONF_TEMPERATURE in config:
|
||||||
|
conf = config[CONF_TEMPERATURE]
|
||||||
|
sens = await sensor.new_sensor(conf)
|
||||||
|
cg.add(var.set_temperature_sensor(sens))
|
||||||
|
cg.add(var.set_temperature_oversampling_config(conf[CONF_OVERSAMPLING]))
|
||||||
|
|
||||||
|
if CONF_PRESSURE in config:
|
||||||
|
conf = config[CONF_PRESSURE]
|
||||||
|
sens = await sensor.new_sensor(conf)
|
||||||
|
cg.add(var.set_pressure_sensor(sens))
|
||||||
|
cg.add(var.set_pressure_oversampling_config(conf[CONF_OVERSAMPLING]))
|
|
@ -180,6 +180,15 @@ sensor:
|
||||||
co2:
|
co2:
|
||||||
name: CO2 Sensor
|
name: CO2 Sensor
|
||||||
|
|
||||||
|
- platform: bmp3xx
|
||||||
|
temperature:
|
||||||
|
name: "BMP Temperature"
|
||||||
|
oversampling: 16x
|
||||||
|
pressure:
|
||||||
|
name: "BMP Pressure"
|
||||||
|
address: 0x77
|
||||||
|
iir_filter: 2X
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- id: automation_test
|
- id: automation_test
|
||||||
then:
|
then:
|
||||||
|
|
Loading…
Reference in a new issue