mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	Add support for BMP388 / BMP 390 pressure and temperature sensor (#2716)
This commit is contained in:
		| @@ -31,6 +31,7 @@ esphome/components/binary_sensor/* @esphome/core | ||||
| esphome/components/bl0940/* @tobias- | ||||
| esphome/components/ble_client/* @buxtronix | ||||
| esphome/components/bme680_bsec/* @trvrnrth | ||||
| esphome/components/bmp3xx/* @martgras | ||||
| esphome/components/button/* @esphome/core | ||||
| esphome/components/canbus/* @danielschramm @mvturnho | ||||
| 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: | ||||
|       name: CO2 Sensor | ||||
|  | ||||
|   - platform: bmp3xx | ||||
|     temperature: | ||||
|       name: "BMP Temperature" | ||||
|       oversampling: 16x | ||||
|     pressure: | ||||
|       name: "BMP Pressure" | ||||
|     address: 0x77 | ||||
|     iir_filter: 2X | ||||
|  | ||||
| script: | ||||
|   - id: automation_test | ||||
|     then: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user