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/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: | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user