mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	New component: Add support for bmp581 pressure and temperature sensors (#4657)
This commit is contained in:
		| @@ -49,6 +49,7 @@ esphome/components/ble_client/* @buxtronix | |||||||
| esphome/components/bluetooth_proxy/* @jesserockz | esphome/components/bluetooth_proxy/* @jesserockz | ||||||
| esphome/components/bme680_bsec/* @trvrnrth | esphome/components/bme680_bsec/* @trvrnrth | ||||||
| esphome/components/bmp3xx/* @martgras | esphome/components/bmp3xx/* @martgras | ||||||
|  | esphome/components/bmp581/* @kahrendt | ||||||
| esphome/components/bp1658cj/* @Cossid | esphome/components/bp1658cj/* @Cossid | ||||||
| esphome/components/bp5758d/* @Cossid | esphome/components/bp5758d/* @Cossid | ||||||
| esphome/components/button/* @esphome/core | esphome/components/button/* @esphome/core | ||||||
|   | |||||||
							
								
								
									
										0
									
								
								esphome/components/bmp581/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/bmp581/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										596
									
								
								esphome/components/bmp581/bmp581.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										596
									
								
								esphome/components/bmp581/bmp581.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,596 @@ | |||||||
|  | /* | ||||||
|  |  * Adds support for Bosch's BMP581 high accuracy pressure and temperature sensor | ||||||
|  |  *  - Component structure based on ESPHome's BMP3XX component (as of March, 2023) | ||||||
|  |  *    - Implementation is easier as the sensor itself automatically compensates pressure for the temperature | ||||||
|  |  *      - Temperature and pressure data is converted via simple divison operations in this component | ||||||
|  |  *    - IIR filter level can independently be applied to temperature and pressure measurements | ||||||
|  |  *  - Bosch's BMP5-Sensor-API was consulted to verify that sensor configuration is done correctly | ||||||
|  |  *    - Copyright (c) 2022 Bosch Sensortec Gmbh, SPDX-License-Identifier: BSD-3-Clause | ||||||
|  |  *  - This component uses forced power mode only so measurements are synchronized by the host | ||||||
|  |  *  - All datasheet page references refer to Bosch Document Number BST-BMP581-DS004-04 (revision number 1.4) | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "bmp581.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  | #include "esphome/core/hal.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace bmp581 { | ||||||
|  |  | ||||||
|  | static const char *const TAG = "bmp581"; | ||||||
|  |  | ||||||
|  | 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"); | ||||||
|  |     case Oversampling::OVERSAMPLING_X64: | ||||||
|  |       return LOG_STR("64x"); | ||||||
|  |     case Oversampling::OVERSAMPLING_X128: | ||||||
|  |       return LOG_STR("128x"); | ||||||
|  |     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 BMP581Component::dump_config() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "BMP581:"); | ||||||
|  |  | ||||||
|  |   switch (this->error_code_) { | ||||||
|  |     case NONE: | ||||||
|  |       break; | ||||||
|  |     case ERROR_COMMUNICATION_FAILED: | ||||||
|  |       ESP_LOGE(TAG, "  Communication with BMP581 failed!"); | ||||||
|  |       break; | ||||||
|  |     case ERROR_WRONG_CHIP_ID: | ||||||
|  |       ESP_LOGE(TAG, "  BMP581 has wrong chip ID - please verify you are using a BMP 581"); | ||||||
|  |       break; | ||||||
|  |     case ERROR_SENSOR_RESET: | ||||||
|  |       ESP_LOGE(TAG, "  BMP581 failed to reset"); | ||||||
|  |       break; | ||||||
|  |     case ERROR_SENSOR_STATUS: | ||||||
|  |       ESP_LOGE(TAG, "  BMP581 sensor status failed, there were NVM problems"); | ||||||
|  |       break; | ||||||
|  |     case ERROR_PRIME_IIR_FAILED: | ||||||
|  |       ESP_LOGE(TAG, "  BMP581's IIR Filter failed to prime with an initial measurement"); | ||||||
|  |       break; | ||||||
|  |     default: | ||||||
|  |       ESP_LOGE(TAG, "  BMP581 error code %d", (int) this->error_code_); | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   LOG_I2C_DEVICE(this); | ||||||
|  |   LOG_UPDATE_INTERVAL(this); | ||||||
|  |  | ||||||
|  |   ESP_LOGCONFIG(TAG, "  Measurement conversion time: %ums", this->conversion_time_); | ||||||
|  |  | ||||||
|  |   if (this->temperature_sensor_) { | ||||||
|  |     LOG_SENSOR("  ", "Temperature", this->temperature_sensor_); | ||||||
|  |     ESP_LOGCONFIG(TAG, "    IIR Filter: %s", LOG_STR_ARG(iir_filter_to_str(this->iir_temperature_level_))); | ||||||
|  |     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, "    IIR Filter: %s", LOG_STR_ARG(iir_filter_to_str(this->iir_pressure_level_))); | ||||||
|  |     ESP_LOGCONFIG(TAG, "    Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->pressure_oversampling_))); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void BMP581Component::setup() { | ||||||
|  |   /* | ||||||
|  |    * Setup goes through several stages, which follows the post-power-up procedure (page 18 of datasheet) and then sets | ||||||
|  |    * configured options | ||||||
|  |    *  1) Soft reboot | ||||||
|  |    *  2) Verify ASIC chip ID matches BMP581 | ||||||
|  |    *  3) Verify sensor status (check if NVM is okay) | ||||||
|  |    *  4) Enable data ready interrupt | ||||||
|  |    *  5) Write oversampling settings and set internal configuration values | ||||||
|  |    *  6) Configure and prime IIR Filter(s), if enabled | ||||||
|  |    */ | ||||||
|  |  | ||||||
|  |   this->error_code_ = NONE; | ||||||
|  |   ESP_LOGCONFIG(TAG, "Setting up BMP581..."); | ||||||
|  |  | ||||||
|  |   //////////////////// | ||||||
|  |   // 1) Soft reboot // | ||||||
|  |   //////////////////// | ||||||
|  |  | ||||||
|  |   // Power-On-Reboot bit is asserted if sensor successfully reset | ||||||
|  |   if (!this->reset_()) { | ||||||
|  |     ESP_LOGE(TAG, "BMP581 failed to reset"); | ||||||
|  |  | ||||||
|  |     this->error_code_ = ERROR_SENSOR_RESET; | ||||||
|  |     this->mark_failed(); | ||||||
|  |  | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /////////////////////////////////////////// | ||||||
|  |   // 2) Verify ASIC chip ID matches BMP581 // | ||||||
|  |   /////////////////////////////////////////// | ||||||
|  |  | ||||||
|  |   uint8_t chip_id; | ||||||
|  |  | ||||||
|  |   // read chip id from sensor | ||||||
|  |   if (!this->read_byte(BMP581_CHIP_ID, &chip_id)) { | ||||||
|  |     ESP_LOGE(TAG, "Failed to read chip id"); | ||||||
|  |  | ||||||
|  |     this->error_code_ = ERROR_COMMUNICATION_FAILED; | ||||||
|  |     this->mark_failed(); | ||||||
|  |  | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // verify id | ||||||
|  |   if (chip_id != BMP581_ASIC_ID) { | ||||||
|  |     ESP_LOGE(TAG, "Unknown chip ID, is this a BMP581?"); | ||||||
|  |  | ||||||
|  |     this->error_code_ = ERROR_WRONG_CHIP_ID; | ||||||
|  |     this->mark_failed(); | ||||||
|  |  | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   //////////////////////////////////////////////////// | ||||||
|  |   // 3) Verify sensor status (check if NVM is okay) // | ||||||
|  |   //////////////////////////////////////////////////// | ||||||
|  |  | ||||||
|  |   if (!this->read_byte(BMP581_STATUS, &this->status_.reg)) { | ||||||
|  |     ESP_LOGE(TAG, "Failed to read status register"); | ||||||
|  |  | ||||||
|  |     this->error_code_ = ERROR_COMMUNICATION_FAILED; | ||||||
|  |     this->mark_failed(); | ||||||
|  |  | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // verify status_nvm_rdy bit (it is asserted if boot was successful) | ||||||
|  |   if (!(this->status_.bit.status_nvm_rdy)) { | ||||||
|  |     ESP_LOGE(TAG, "NVM not ready after boot"); | ||||||
|  |  | ||||||
|  |     this->error_code_ = ERROR_SENSOR_STATUS; | ||||||
|  |     this->mark_failed(); | ||||||
|  |  | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // verify status_nvm_err bit (it is asserted if an error is detected) | ||||||
|  |   if (this->status_.bit.status_nvm_err) { | ||||||
|  |     ESP_LOGE(TAG, "NVM error detected on boot"); | ||||||
|  |  | ||||||
|  |     this->error_code_ = ERROR_SENSOR_STATUS; | ||||||
|  |     this->mark_failed(); | ||||||
|  |  | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   //////////////////////////////////// | ||||||
|  |   // 4) Enable data ready interrupt // | ||||||
|  |   //////////////////////////////////// | ||||||
|  |  | ||||||
|  |   // enable the data ready interrupt source | ||||||
|  |   if (!this->write_interrupt_source_settings_(true)) { | ||||||
|  |     ESP_LOGE(TAG, "Failed to write interrupt source register"); | ||||||
|  |  | ||||||
|  |     this->error_code_ = ERROR_COMMUNICATION_FAILED; | ||||||
|  |     this->mark_failed(); | ||||||
|  |  | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   ////////////////////////////////////////////////////////////////////////// | ||||||
|  |   // 5) Write oversampling settings and set internal configuration values // | ||||||
|  |   ////////////////////////////////////////////////////////////////////////// | ||||||
|  |  | ||||||
|  |   // configure pressure readings, if sensor is defined | ||||||
|  |   // otherwise, disable pressure oversampling | ||||||
|  |   if (this->pressure_sensor_) { | ||||||
|  |     this->osr_config_.bit.press_en = true; | ||||||
|  |   } else { | ||||||
|  |     this->pressure_oversampling_ = OVERSAMPLING_NONE; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // write oversampling settings | ||||||
|  |   if (!this->write_oversampling_settings_(this->temperature_oversampling_, this->pressure_oversampling_)) { | ||||||
|  |     ESP_LOGE(TAG, "Failed to write oversampling register"); | ||||||
|  |  | ||||||
|  |     this->error_code_ = ERROR_COMMUNICATION_FAILED; | ||||||
|  |     this->mark_failed(); | ||||||
|  |  | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // set output data rate to 4 Hz=0x19 (page 65 of datasheet) | ||||||
|  |   //  - ?shouldn't? matter as this component only uses FORCED_MODE - datasheet is ambiguous | ||||||
|  |   //  - If in NORMAL_MODE or NONSTOP_MODE, then this would still allow deep standby to save power | ||||||
|  |   //  - will be written to BMP581 at next requested measurement | ||||||
|  |   this->odr_config_.bit.odr = 0x19; | ||||||
|  |  | ||||||
|  |   /////////////////////////////////////////////////////// | ||||||
|  |   /// 6) Configure and prime IIR Filter(s), if enabled // | ||||||
|  |   /////////////////////////////////////////////////////// | ||||||
|  |  | ||||||
|  |   if ((this->iir_temperature_level_ != IIR_FILTER_OFF) || (this->iir_pressure_level_ != IIR_FILTER_OFF)) { | ||||||
|  |     if (!this->write_iir_settings_(this->iir_temperature_level_, this->iir_pressure_level_)) { | ||||||
|  |       ESP_LOGE(TAG, "Failed to write IIR configuration registers"); | ||||||
|  |  | ||||||
|  |       this->error_code_ = ERROR_COMMUNICATION_FAILED; | ||||||
|  |       this->mark_failed(); | ||||||
|  |  | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!this->prime_iir_filter_()) { | ||||||
|  |       ESP_LOGE(TAG, "Failed to prime the IIR filter with an intiial measurement"); | ||||||
|  |  | ||||||
|  |       this->error_code_ = ERROR_PRIME_IIR_FAILED; | ||||||
|  |       this->mark_failed(); | ||||||
|  |  | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void BMP581Component::update() { | ||||||
|  |   /* | ||||||
|  |    * Each update goes through several stages | ||||||
|  |    *  0) Verify either a temperature or pressure sensor is defined before proceeding | ||||||
|  |    *  1) Request a measurement | ||||||
|  |    *  2) Wait for measurement to finish (based on oversampling rates) | ||||||
|  |    *  3) Read data registers for temperature and pressure, if applicable | ||||||
|  |    *  4) Publish measurements to sensor(s), if applicable | ||||||
|  |    */ | ||||||
|  |  | ||||||
|  |   //////////////////////////////////////////////////////////////////////////////////// | ||||||
|  |   // 0) Verify either a temperature or pressure sensor is defined before proceeding // | ||||||
|  |   //////////////////////////////////////////////////////////////////////////////////// | ||||||
|  |  | ||||||
|  |   if ((!this->temperature_sensor_) && (!this->pressure_sensor_)) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   ////////////////////////////// | ||||||
|  |   // 1) Request a measurement // | ||||||
|  |   ////////////////////////////// | ||||||
|  |  | ||||||
|  |   ESP_LOGVV(TAG, "Requesting a measurement from sensor"); | ||||||
|  |  | ||||||
|  |   if (!this->start_measurement_()) { | ||||||
|  |     ESP_LOGW(TAG, "Failed to request forced measurement of sensor"); | ||||||
|  |     this->status_set_warning(); | ||||||
|  |  | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   ////////////////////////////////////////////////////////////////////// | ||||||
|  |   // 2) Wait for measurement to finish (based on oversampling rates) // | ||||||
|  |   ////////////////////////////////////////////////////////////////////// | ||||||
|  |  | ||||||
|  |   ESP_LOGVV(TAG, "Measurement is expected to take %d ms to complete", this->conversion_time_); | ||||||
|  |  | ||||||
|  |   this->set_timeout("measurement", this->conversion_time_, [this]() { | ||||||
|  |     float temperature = 0.0; | ||||||
|  |     float pressure = 0.0; | ||||||
|  |  | ||||||
|  |     //////////////////////////////////////////////////////////////////////// | ||||||
|  |     // 3) Read data registers for temperature and pressure, if applicable // | ||||||
|  |     //////////////////////////////////////////////////////////////////////// | ||||||
|  |  | ||||||
|  |     if (this->pressure_sensor_) { | ||||||
|  |       if (!this->read_temperature_and_pressure_(temperature, pressure)) { | ||||||
|  |         ESP_LOGW(TAG, "Failed to read temperature and pressure measurements, skipping update"); | ||||||
|  |         this->status_set_warning(); | ||||||
|  |  | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |     } else { | ||||||
|  |       if (!this->read_temperature_(temperature)) { | ||||||
|  |         ESP_LOGW(TAG, "Failed to read temperature measurement, skipping update"); | ||||||
|  |         this->status_set_warning(); | ||||||
|  |  | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ///////////////////////////////////////////////////////// | ||||||
|  |     // 4) Publish measurements to sensor(s), if applicable // | ||||||
|  |     ///////////////////////////////////////////////////////// | ||||||
|  |  | ||||||
|  |     if (this->temperature_sensor_) { | ||||||
|  |       this->temperature_sensor_->publish_state(temperature); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (this->pressure_sensor_) { | ||||||
|  |       this->pressure_sensor_->publish_state(pressure); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     this->status_clear_warning(); | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool BMP581Component::check_data_readiness_() { | ||||||
|  |   //   - verifies component is not internally in standby mode | ||||||
|  |   //   - reads interrupt status register | ||||||
|  |   //   - checks if data ready bit is asserted | ||||||
|  |   //      - If true, then internally sets component to standby mode if in forced mode | ||||||
|  |   //   - returns data readiness state | ||||||
|  |  | ||||||
|  |   if (this->odr_config_.bit.pwr_mode == STANDBY_MODE) { | ||||||
|  |     ESP_LOGD(TAG, "Data is not ready, sensor is in standby mode"); | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   uint8_t status; | ||||||
|  |  | ||||||
|  |   if (!this->read_byte(BMP581_INT_STATUS, &status)) { | ||||||
|  |     ESP_LOGE(TAG, "Failed to read interrupt status register"); | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   this->int_status_.reg = status; | ||||||
|  |  | ||||||
|  |   if (this->int_status_.bit.drdy_data_reg) { | ||||||
|  |     // If in forced mode, then set internal record of the power mode to STANDBY_MODE | ||||||
|  |     //  - sensor automatically returns to standby mode after completing a forced measurement | ||||||
|  |     if (this->odr_config_.bit.pwr_mode == FORCED_MODE) { | ||||||
|  |       this->odr_config_.bit.pwr_mode = STANDBY_MODE; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool BMP581Component::prime_iir_filter_() { | ||||||
|  |   // - temporarily disables oversampling for a fast initial measurement; avoids slowing down ESPHome's startup process | ||||||
|  |   // - enables IIR filter flushing with forced measurements | ||||||
|  |   // - forces a measurement; flushing the IIR filter and priming it with a current value | ||||||
|  |   // - disables IIR filter flushing with forced measurements | ||||||
|  |   // - reverts to internally configured oversampling rates | ||||||
|  |   // - returns success of all register writes/priming | ||||||
|  |  | ||||||
|  |   // store current internal oversampling settings to revert to after priming | ||||||
|  |   Oversampling current_temperature_oversampling = (Oversampling) this->osr_config_.bit.osr_t; | ||||||
|  |   Oversampling current_pressure_oversampling = (Oversampling) this->osr_config_.bit.osr_p; | ||||||
|  |  | ||||||
|  |   // temporarily disables oversampling for temperature and pressure for a fast priming measurement | ||||||
|  |   if (!this->write_oversampling_settings_(OVERSAMPLING_NONE, OVERSAMPLING_NONE)) { | ||||||
|  |     ESP_LOGE(TAG, "Failed to write oversampling register"); | ||||||
|  |  | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // flush the IIR filter with forced measurements (we will only flush once) | ||||||
|  |   this->dsp_config_.bit.iir_flush_forced_en = true; | ||||||
|  |   if (!this->write_byte(BMP581_DSP, this->dsp_config_.reg)) { | ||||||
|  |     ESP_LOGE(TAG, "Failed to write IIR source register"); | ||||||
|  |  | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // forces an intial measurement | ||||||
|  |   //  - this measurements flushes the IIR filter reflecting written DSP settings | ||||||
|  |   //  - flushing with this initial reading avoids having the internal previous data aquisition being 0, which | ||||||
|  |   //    (I)nfinitely affects future values | ||||||
|  |   if (!this->start_measurement_()) { | ||||||
|  |     ESP_LOGE(TAG, "Failed to request a forced measurement"); | ||||||
|  |  | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // wait for priming measurement to complete | ||||||
|  |   //  - with oversampling disabled, the conversion time for a single measurement for pressure and temperature is | ||||||
|  |   //    ceilf(1.05*(1.0+1.0)) = 3ms | ||||||
|  |   //  - see page 12 of datasheet for details | ||||||
|  |   delay(3); | ||||||
|  |  | ||||||
|  |   if (!this->check_data_readiness_()) { | ||||||
|  |     ESP_LOGE(TAG, "IIR priming measurement was not ready"); | ||||||
|  |  | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // disable IIR filter flushings on future forced measurements | ||||||
|  |   this->dsp_config_.bit.iir_flush_forced_en = false; | ||||||
|  |   if (!this->write_byte(BMP581_DSP, this->dsp_config_.reg)) { | ||||||
|  |     ESP_LOGE(TAG, "Failed to write IIR source register"); | ||||||
|  |  | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // revert oversampling rates to original settings | ||||||
|  |   return this->write_oversampling_settings_(current_temperature_oversampling, current_pressure_oversampling); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool BMP581Component::read_temperature_(float &temperature) { | ||||||
|  |   // - verifies data is ready to be read | ||||||
|  |   // - reads in 3 bytes of temperature data | ||||||
|  |   // - returns whether successful, where the the variable parameter contains | ||||||
|  |   //    - the measured temperature (in degrees Celsius) | ||||||
|  |  | ||||||
|  |   if (!this->check_data_readiness_()) { | ||||||
|  |     ESP_LOGW(TAG, "Data from sensor isn't ready, skipping this update"); | ||||||
|  |     this->status_set_warning(); | ||||||
|  |  | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   uint8_t data[3]; | ||||||
|  |   if (!this->read_bytes(BMP581_MEASUREMENT_DATA, &data[0], 3)) { | ||||||
|  |     ESP_LOGW(TAG, "Failed to read sensor's measurement data"); | ||||||
|  |     this->status_set_warning(); | ||||||
|  |  | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // temperature MSB is in data[2], LSB is in data[1], XLSB in data[0] | ||||||
|  |   int32_t raw_temp = (int32_t) data[2] << 16 | (int32_t) data[1] << 8 | (int32_t) data[0]; | ||||||
|  |   temperature = (float) (raw_temp / 65536.0);  // convert measurement to degrees Celsius (page 22 of datasheet) | ||||||
|  |  | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool BMP581Component::read_temperature_and_pressure_(float &temperature, float &pressure) { | ||||||
|  |   // - verifies data is ready to be read | ||||||
|  |   // - reads in 6 bytes of temperature data (3 for temeperature, 3 for pressure) | ||||||
|  |   // - returns whether successful, where the variable parameters contain | ||||||
|  |   //    - the measured temperature (in degrees Celsius) | ||||||
|  |   //    - the measured pressure (in Pa) | ||||||
|  |  | ||||||
|  |   if (!this->check_data_readiness_()) { | ||||||
|  |     ESP_LOGW(TAG, "Data from sensor isn't ready, skipping this update"); | ||||||
|  |     this->status_set_warning(); | ||||||
|  |  | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   uint8_t data[6]; | ||||||
|  |   if (!this->read_bytes(BMP581_MEASUREMENT_DATA, &data[0], 6)) { | ||||||
|  |     ESP_LOGW(TAG, "Failed to read sensor's measurement data"); | ||||||
|  |     this->status_set_warning(); | ||||||
|  |  | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // temperature MSB is in data[2], LSB is in data[1], XLSB in data[0] | ||||||
|  |   int32_t raw_temp = (int32_t) data[2] << 16 | (int32_t) data[1] << 8 | (int32_t) data[0]; | ||||||
|  |   temperature = (float) (raw_temp / 65536.0);  // convert measurement to degrees Celsius (page 22 of datasheet) | ||||||
|  |  | ||||||
|  |   // pressure MSB is in data[5], LSB is in data[4], XLSB in data[3] | ||||||
|  |   int32_t raw_press = (int32_t) data[5] << 16 | (int32_t) data[4] << 8 | (int32_t) data[3]; | ||||||
|  |   pressure = (float) (raw_press / 64.0);  // Divide by 2^6=64 for Pa (page 22 of datasheet) | ||||||
|  |  | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool BMP581Component::reset_() { | ||||||
|  |   // - writes reset command to the command register | ||||||
|  |   // - waits for sensor to complete reset | ||||||
|  |   // - returns the Power-On-Reboot interrupt status, which is asserted if successful | ||||||
|  |  | ||||||
|  |   // writes reset command to BMP's command register | ||||||
|  |   if (!this->write_byte(BMP581_COMMAND, RESET_COMMAND)) { | ||||||
|  |     ESP_LOGE(TAG, "Failed to write reset command"); | ||||||
|  |  | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // t_{soft_res} = 2ms (page 11 of datasheet); time it takes to enter standby mode | ||||||
|  |   //  - round up to 3 ms | ||||||
|  |   delay(3); | ||||||
|  |  | ||||||
|  |   // read interrupt status register | ||||||
|  |   if (!this->read_byte(BMP581_INT_STATUS, &this->int_status_.reg)) { | ||||||
|  |     ESP_LOGE(TAG, "Failed to read interrupt status register"); | ||||||
|  |  | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Power-On-Reboot bit is asserted if sensor successfully reset | ||||||
|  |   return this->int_status_.bit.por; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool BMP581Component::start_measurement_() { | ||||||
|  |   // - only pushes the sensor into FORCED_MODE for a reading if already in STANDBY_MODE | ||||||
|  |   // - returns whether a measurement is in progress or has been initiated | ||||||
|  |  | ||||||
|  |   if (this->odr_config_.bit.pwr_mode == STANDBY_MODE) { | ||||||
|  |     return this->write_power_mode_(FORCED_MODE); | ||||||
|  |   } else { | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool BMP581Component::write_iir_settings_(IIRFilter temperature_iir, IIRFilter pressure_iir) { | ||||||
|  |   // - ensures data registers store filtered values | ||||||
|  |   // - sets IIR filter levels on sensor | ||||||
|  |   // - matches other default settings on sensor | ||||||
|  |   // - writes configuration to the two relevant registers | ||||||
|  |   // - returns success or failure of write to the registers | ||||||
|  |  | ||||||
|  |   // If the temperature/pressure IIR filter is configured, then ensure data registers store the filtered measurement | ||||||
|  |   this->dsp_config_.bit.shdw_sel_iir_t = (temperature_iir != IIR_FILTER_OFF); | ||||||
|  |   this->dsp_config_.bit.shdw_sel_iir_p = (pressure_iir != IIR_FILTER_OFF); | ||||||
|  |  | ||||||
|  |   // set temperature and pressure IIR filter level to configured values | ||||||
|  |   this->iir_config_.bit.set_iir_t = temperature_iir; | ||||||
|  |   this->iir_config_.bit.set_iir_p = pressure_iir; | ||||||
|  |  | ||||||
|  |   // enable pressure and temperature compensation (page 61 of datasheet) | ||||||
|  |   //  - ?only relevant if IIR filter is applied?; the datasheet is ambiguous | ||||||
|  |   //  - matches BMP's default setting | ||||||
|  |   this->dsp_config_.bit.comp_pt_en = 0x3; | ||||||
|  |  | ||||||
|  |   // BMP581_DSP register and BMP581_DSP_IIR registers are successive | ||||||
|  |   //  - allows us to write the IIR configuration with one command to both registers | ||||||
|  |   uint8_t register_data[2] = {this->dsp_config_.reg, this->iir_config_.reg}; | ||||||
|  |   return this->write_bytes(BMP581_DSP, register_data, sizeof(register_data)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool BMP581Component::write_interrupt_source_settings_(bool data_ready_enable) { | ||||||
|  |   // - updates component's internal setting | ||||||
|  |   // - returns success or failure of write to interrupt source register | ||||||
|  |  | ||||||
|  |   this->int_source_.bit.drdy_data_reg_en = data_ready_enable; | ||||||
|  |  | ||||||
|  |   // write interrupt source register | ||||||
|  |   return this->write_byte(BMP581_INT_SOURCE, this->int_source_.reg); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool BMP581Component::write_oversampling_settings_(Oversampling temperature_oversampling, | ||||||
|  |                                                    Oversampling pressure_oversampling) { | ||||||
|  |   // - updates component's internal setting | ||||||
|  |   // - returns success or failure of write to Over-Sampling Rate register | ||||||
|  |  | ||||||
|  |   this->osr_config_.bit.osr_t = temperature_oversampling; | ||||||
|  |   this->osr_config_.bit.osr_p = pressure_oversampling; | ||||||
|  |  | ||||||
|  |   return this->write_byte(BMP581_OSR, this->osr_config_.reg); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool BMP581Component::write_power_mode_(OperationMode mode) { | ||||||
|  |   // - updates the component's internal power mode | ||||||
|  |   // - returns success or failure of write to Output Data Rate register | ||||||
|  |  | ||||||
|  |   this->odr_config_.bit.pwr_mode = mode; | ||||||
|  |  | ||||||
|  |   // write odr register | ||||||
|  |   return this->write_byte(BMP581_ODR, this->odr_config_.reg); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace bmp581 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										222
									
								
								esphome/components/bmp581/bmp581.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								esphome/components/bmp581/bmp581.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,222 @@ | |||||||
|  | // All datasheet page references refer to Bosch Document Number BST-BMP581-DS004-04 (revision number 1.4) | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/components/i2c/i2c.h" | ||||||
|  | #include "esphome/components/sensor/sensor.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace bmp581 { | ||||||
|  |  | ||||||
|  | static const uint8_t BMP581_ASIC_ID = 0x50;  // BMP581's ASIC chip ID (page 51 of datasheet) | ||||||
|  | static const uint8_t RESET_COMMAND = 0xB6;   // Soft reset command | ||||||
|  |  | ||||||
|  | // BMP581 Register Addresses | ||||||
|  | enum { | ||||||
|  |   BMP581_CHIP_ID = 0x01,     // read chip ID | ||||||
|  |   BMP581_INT_SOURCE = 0x15,  // write interrupt sources | ||||||
|  |   BMP581_MEASUREMENT_DATA = | ||||||
|  |       0x1D,  // read measurement registers, 0x1D-0x1F are temperature XLSB to MSB and 0x20-0x22 are pressure XLSB to MSB | ||||||
|  |   BMP581_INT_STATUS = 0x27,  // read interrupt statuses | ||||||
|  |   BMP581_STATUS = 0x28,      // read sensor status | ||||||
|  |   BMP581_DSP = 0x30,         // write sensor configuration | ||||||
|  |   BMP581_DSP_IIR = 0x31,     // write IIR filter configuration | ||||||
|  |   BMP581_OSR = 0x36,         // write oversampling configuration | ||||||
|  |   BMP581_ODR = 0x37,         // write data rate and power mode configuration | ||||||
|  |   BMP581_COMMAND = 0x7E      // write sensor command | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // BMP581 Power mode operations | ||||||
|  | enum OperationMode { | ||||||
|  |   STANDBY_MODE = 0x0,  // no active readings | ||||||
|  |   NORMAL_MODE = 0x1,   // read continuously at ODR configured rate and standby between | ||||||
|  |   FORCED_MODE = 0x2,   // read sensor once (only reading mode used by this component) | ||||||
|  |   NONSTOP_MODE = 0x3   // read continuously with no standby | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // Temperature and pressure sensors can be oversampled to reduce noise | ||||||
|  | enum Oversampling { | ||||||
|  |   OVERSAMPLING_NONE = 0x0, | ||||||
|  |   OVERSAMPLING_X2 = 0x1, | ||||||
|  |   OVERSAMPLING_X4 = 0x2, | ||||||
|  |   OVERSAMPLING_X8 = 0x3, | ||||||
|  |   OVERSAMPLING_X16 = 0x4, | ||||||
|  |   OVERSAMPLING_X32 = 0x5, | ||||||
|  |   OVERSAMPLING_X64 = 0x6, | ||||||
|  |   OVERSAMPLING_X128 = 0x7 | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // Infinite Impulse Response filter reduces noise caused by ambient disturbances | ||||||
|  | enum IIRFilter { | ||||||
|  |   IIR_FILTER_OFF = 0x0, | ||||||
|  |   IIR_FILTER_2 = 0x1, | ||||||
|  |   IIR_FILTER_4 = 0x2, | ||||||
|  |   IIR_FILTER_8 = 0x3, | ||||||
|  |   IIR_FILTER_16 = 0x4, | ||||||
|  |   IIR_FILTER_32 = 0x5, | ||||||
|  |   IIR_FILTER_64 = 0x6, | ||||||
|  |   IIR_FILTER_128 = 0x7 | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class BMP581Component : public PollingComponent, public i2c::I2CDevice { | ||||||
|  |  public: | ||||||
|  |   float get_setup_priority() const override { return setup_priority::DATA; } | ||||||
|  |  | ||||||
|  |   void dump_config() override; | ||||||
|  |  | ||||||
|  |   void setup() override; | ||||||
|  |   void update() override; | ||||||
|  |  | ||||||
|  |   void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; } | ||||||
|  |   void set_pressure_sensor(sensor::Sensor *pressure_sensor) { this->pressure_sensor_ = pressure_sensor; } | ||||||
|  |  | ||||||
|  |   void set_temperature_oversampling_config(Oversampling temperature_oversampling) { | ||||||
|  |     this->temperature_oversampling_ = temperature_oversampling; | ||||||
|  |   } | ||||||
|  |   void set_pressure_oversampling_config(Oversampling pressure_oversampling) { | ||||||
|  |     this->pressure_oversampling_ = pressure_oversampling; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void set_temperature_iir_filter_config(IIRFilter iir_temperature_level) { | ||||||
|  |     this->iir_temperature_level_ = iir_temperature_level; | ||||||
|  |   } | ||||||
|  |   void set_pressure_iir_filter_config(IIRFilter iir_pressure_level) { this->iir_pressure_level_ = iir_pressure_level; } | ||||||
|  |  | ||||||
|  |   void set_conversion_time(uint8_t conversion_time) { this->conversion_time_ = conversion_time; } | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   sensor::Sensor *temperature_sensor_{nullptr}; | ||||||
|  |   sensor::Sensor *pressure_sensor_{nullptr}; | ||||||
|  |  | ||||||
|  |   Oversampling temperature_oversampling_; | ||||||
|  |   Oversampling pressure_oversampling_; | ||||||
|  |  | ||||||
|  |   IIRFilter iir_temperature_level_; | ||||||
|  |   IIRFilter iir_pressure_level_; | ||||||
|  |  | ||||||
|  |   // Stores the sensors conversion time needed for a measurement based on oversampling settings and datasheet (page 12) | ||||||
|  |   // Computed in Python during codegen | ||||||
|  |   uint8_t conversion_time_; | ||||||
|  |  | ||||||
|  |   // Checks if the BMP581 has measurement data ready by checking the sensor's interrupts | ||||||
|  |   bool check_data_readiness_(); | ||||||
|  |  | ||||||
|  |   // Flushes the IIR filter and primes an initial reading | ||||||
|  |   bool prime_iir_filter_(); | ||||||
|  |  | ||||||
|  |   // Reads temperature data from sensor and converts data to measurement in degrees Celsius | ||||||
|  |   bool read_temperature_(float &temperature); | ||||||
|  |   // Reads temperature and pressure data from sensor and converts data to measurements in degrees Celsius and Pa | ||||||
|  |   bool read_temperature_and_pressure_(float &temperature, float &pressure); | ||||||
|  |  | ||||||
|  |   // Soft resets the BMP581 | ||||||
|  |   bool reset_(); | ||||||
|  |  | ||||||
|  |   // Initiates a measurement on sensor by switching to FORCED_MODE | ||||||
|  |   bool start_measurement_(); | ||||||
|  |  | ||||||
|  |   // Writes the IIR filter configuration to the DSP and DSP_IIR registers | ||||||
|  |   bool write_iir_settings_(IIRFilter temperature_iir, IIRFilter pressure_iir); | ||||||
|  |  | ||||||
|  |   // Writes whether to enable the data ready interrupt to the interrupt source register | ||||||
|  |   bool write_interrupt_source_settings_(bool data_ready_enable); | ||||||
|  |  | ||||||
|  |   // Writes the oversampling settings to the OSR register | ||||||
|  |   bool write_oversampling_settings_(Oversampling temperature_oversampling, Oversampling pressure_oversampling); | ||||||
|  |  | ||||||
|  |   // Sets the power mode on the BMP581 by writing to the ODR register | ||||||
|  |   bool write_power_mode_(OperationMode mode); | ||||||
|  |  | ||||||
|  |   enum ErrorCode { | ||||||
|  |     NONE = 0, | ||||||
|  |     ERROR_COMMUNICATION_FAILED, | ||||||
|  |     ERROR_WRONG_CHIP_ID, | ||||||
|  |     ERROR_SENSOR_STATUS, | ||||||
|  |     ERROR_SENSOR_RESET, | ||||||
|  |     ERROR_PRIME_IIR_FAILED | ||||||
|  |   } error_code_{NONE}; | ||||||
|  |  | ||||||
|  |   // BMP581's interrupt source register (address 0x15) to configure which interrupts are enabled (page 54 of datasheet) | ||||||
|  |   union { | ||||||
|  |     struct { | ||||||
|  |       uint8_t drdy_data_reg_en : 1;  // Data ready interrupt enable | ||||||
|  |       uint8_t fifo_full_en : 1;      // FIFO full interrupt enable | ||||||
|  |       uint8_t fifo_ths_en : 1;       // FIFO threshold/watermark interrupt enable | ||||||
|  |       uint8_t oor_p_en : 1;          // Pressure data out-of-range interrupt enable | ||||||
|  |     } bit; | ||||||
|  |     uint8_t reg; | ||||||
|  |   } int_source_ = {.reg = 0}; | ||||||
|  |  | ||||||
|  |   // BMP581's interrupt status register (address 0x27) to determine ensor's current state (page 58 of datasheet) | ||||||
|  |   union { | ||||||
|  |     struct { | ||||||
|  |       uint8_t drdy_data_reg : 1;  // Data ready | ||||||
|  |       uint8_t fifo_full : 1;      // FIFO full | ||||||
|  |       uint8_t fifo_ths : 1;       // FIFO fhreshold/watermark | ||||||
|  |       uint8_t oor_p : 1;          // Pressure data out-of-range | ||||||
|  |       uint8_t por : 1;            // Power-On-Reset complete | ||||||
|  |     } bit; | ||||||
|  |     uint8_t reg; | ||||||
|  |   } int_status_ = {.reg = 0}; | ||||||
|  |  | ||||||
|  |   // BMP581's status register (address 0x28) to determine if sensor has setup correctly (page 58 of datasheet) | ||||||
|  |   union { | ||||||
|  |     struct { | ||||||
|  |       uint8_t status_core_rdy : 1; | ||||||
|  |       uint8_t status_nvm_rdy : 1;             // asserted if NVM is ready of operations | ||||||
|  |       uint8_t status_nvm_err : 1;             // asserted if NVM error | ||||||
|  |       uint8_t status_nvm_cmd_err : 1;         // asserted if boot command error | ||||||
|  |       uint8_t status_boot_err_corrected : 1;  // asserted if a boot error has been corrected | ||||||
|  |       uint8_t : 2; | ||||||
|  |       uint8_t st_crack_pass : 1;  // asserted if crack check has executed without detecting a crack | ||||||
|  |     } bit; | ||||||
|  |     uint8_t reg; | ||||||
|  |   } status_ = {.reg = 0}; | ||||||
|  |  | ||||||
|  |   // BMP581's dsp register (address 0x30) to configure data registers iir selection (page 61 of datasheet) | ||||||
|  |   union { | ||||||
|  |     struct { | ||||||
|  |       uint8_t comp_pt_en : 2;           // enable temperature and pressure compensation | ||||||
|  |       uint8_t iir_flush_forced_en : 1;  // IIR filter is flushed in forced mode | ||||||
|  |       uint8_t shdw_sel_iir_t : 1;       // temperature data register value selected before or after iir | ||||||
|  |       uint8_t fifo_sel_iir_t : 1;       // FIFO temperature data register value secected before or after iir | ||||||
|  |       uint8_t shdw_sel_iir_p : 1;       // pressure data register value selected before or after iir | ||||||
|  |       uint8_t fifo_sel_iir_p : 1;       // FIFO pressure data register value selected before or after iir | ||||||
|  |       uint8_t oor_sel_iir_p : 1;        // pressure out-of-range value selected before or after iir | ||||||
|  |     } bit; | ||||||
|  |     uint8_t reg; | ||||||
|  |   } dsp_config_ = {.reg = 0}; | ||||||
|  |  | ||||||
|  |   // BMP581's iir register (address 0x31) to configure iir filtering(page 62 of datasheet) | ||||||
|  |   union { | ||||||
|  |     struct { | ||||||
|  |       uint8_t set_iir_t : 3;  // Temperature IIR filter coefficient | ||||||
|  |       uint8_t set_iir_p : 3;  // Pressure IIR filter coefficient | ||||||
|  |     } bit; | ||||||
|  |     uint8_t reg; | ||||||
|  |   } iir_config_ = {.reg = 0}; | ||||||
|  |  | ||||||
|  |   // BMP581's OSR register (address 0x36) to configure Over-Sampling Rates (page 64 of datasheet) | ||||||
|  |   union { | ||||||
|  |     struct { | ||||||
|  |       uint8_t osr_t : 3;     // Temperature oversampling | ||||||
|  |       uint8_t osr_p : 3;     // Pressure oversampling | ||||||
|  |       uint8_t press_en : 1;  // Enables pressure measurement | ||||||
|  |     } bit; | ||||||
|  |     uint8_t reg; | ||||||
|  |   } osr_config_ = {.reg = 0}; | ||||||
|  |  | ||||||
|  |   // BMP581's odr register (address 0x37) to configure output data rate and power mode (page 64 of datasheet) | ||||||
|  |   union { | ||||||
|  |     struct { | ||||||
|  |       uint8_t pwr_mode : 2;  // power mode of sensor | ||||||
|  |       uint8_t odr : 5;       // output data rate | ||||||
|  |       uint8_t deep_dis : 1;  // deep standby disabled if asserted | ||||||
|  |     } bit; | ||||||
|  |     uint8_t reg; | ||||||
|  |   } odr_config_ = {.reg = 0}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace bmp581 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										163
									
								
								esphome/components/bmp581/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								esphome/components/bmp581/sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,163 @@ | |||||||
|  | import math | ||||||
|  | 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_ATMOSPHERIC_PRESSURE, | ||||||
|  |     DEVICE_CLASS_TEMPERATURE, | ||||||
|  |     STATE_CLASS_MEASUREMENT, | ||||||
|  |     UNIT_CELSIUS, | ||||||
|  |     UNIT_PASCAL, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | CODEOWNERS = ["@kahrendt"] | ||||||
|  | DEPENDENCIES = ["i2c"] | ||||||
|  |  | ||||||
|  | bmp581_ns = cg.esphome_ns.namespace("bmp581") | ||||||
|  |  | ||||||
|  | Oversampling = bmp581_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, | ||||||
|  |     "64X": Oversampling.OVERSAMPLING_X64, | ||||||
|  |     "128X": Oversampling.OVERSAMPLING_X128, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | IIRFilter = bmp581_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, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | BMP581Component = bmp581_ns.class_( | ||||||
|  |     "BMP581Component", cg.PollingComponent, i2c.I2CDevice | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def compute_measurement_conversion_time(config): | ||||||
|  |     # - adds up sensor conversion time based on temperature and pressure oversampling rates given in datasheet | ||||||
|  |     # - returns a rounded up time in ms | ||||||
|  |  | ||||||
|  |     # Page 12 of datasheet | ||||||
|  |     PRESSURE_OVERSAMPLING_CONVERSION_TIMES = { | ||||||
|  |         "NONE": 1.0, | ||||||
|  |         "2X": 1.7, | ||||||
|  |         "4X": 2.9, | ||||||
|  |         "8X": 5.4, | ||||||
|  |         "16X": 10.4, | ||||||
|  |         "32X": 20.4, | ||||||
|  |         "64X": 40.4, | ||||||
|  |         "128X": 80.4, | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     # Page 12 of datasheet | ||||||
|  |     TEMPERATURE_OVERSAMPLING_CONVERSION_TIMES = { | ||||||
|  |         "NONE": 1.0, | ||||||
|  |         "2X": 1.1, | ||||||
|  |         "4X": 1.5, | ||||||
|  |         "8X": 2.1, | ||||||
|  |         "16X": 3.3, | ||||||
|  |         "32X": 5.8, | ||||||
|  |         "64X": 10.8, | ||||||
|  |         "128X": 20.8, | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pressure_conversion_time = ( | ||||||
|  |         0.0  # No conversion time necessary without a pressure sensor | ||||||
|  |     ) | ||||||
|  |     if pressure_config := config.get(CONF_PRESSURE): | ||||||
|  |         pressure_conversion_time = PRESSURE_OVERSAMPLING_CONVERSION_TIMES[ | ||||||
|  |             pressure_config.get(CONF_OVERSAMPLING) | ||||||
|  |         ] | ||||||
|  |  | ||||||
|  |     temperature_conversion_time = ( | ||||||
|  |         1.0  # BMP581 always samples the temperature even if only reading pressure | ||||||
|  |     ) | ||||||
|  |     if temperature_config := config.get(CONF_TEMPERATURE): | ||||||
|  |         temperature_conversion_time = TEMPERATURE_OVERSAMPLING_CONVERSION_TIMES[ | ||||||
|  |             temperature_config.get(CONF_OVERSAMPLING) | ||||||
|  |         ] | ||||||
|  |  | ||||||
|  |     # Datasheet indicates a 5% possible error in each conversion time listed | ||||||
|  |     return math.ceil(1.05 * (pressure_conversion_time + temperature_conversion_time)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = ( | ||||||
|  |     cv.Schema( | ||||||
|  |         { | ||||||
|  |             cv.GenerateID(): cv.declare_id(BMP581Component), | ||||||
|  |             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="NONE"): cv.enum( | ||||||
|  |                         OVERSAMPLING_OPTIONS, upper=True | ||||||
|  |                     ), | ||||||
|  |                     cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum( | ||||||
|  |                         IIR_FILTER_OPTIONS, upper=True | ||||||
|  |                     ), | ||||||
|  |                 } | ||||||
|  |             ), | ||||||
|  |             cv.Optional(CONF_PRESSURE): sensor.sensor_schema( | ||||||
|  |                 unit_of_measurement=UNIT_PASCAL, | ||||||
|  |                 accuracy_decimals=0, | ||||||
|  |                 device_class=DEVICE_CLASS_ATMOSPHERIC_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(0x46)) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 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) | ||||||
|  |     if temperature_config := config.get(CONF_TEMPERATURE): | ||||||
|  |         sens = await sensor.new_sensor(temperature_config) | ||||||
|  |         cg.add(var.set_temperature_sensor(sens)) | ||||||
|  |         cg.add( | ||||||
|  |             var.set_temperature_oversampling_config( | ||||||
|  |                 temperature_config[CONF_OVERSAMPLING] | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |         cg.add( | ||||||
|  |             var.set_temperature_iir_filter_config(temperature_config[CONF_IIR_FILTER]) | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |     if pressure_config := config.get(CONF_PRESSURE): | ||||||
|  |         sens = await sensor.new_sensor(pressure_config) | ||||||
|  |         cg.add(var.set_pressure_sensor(sens)) | ||||||
|  |         cg.add(var.set_pressure_oversampling_config(pressure_config[CONF_OVERSAMPLING])) | ||||||
|  |         cg.add(var.set_pressure_iir_filter_config(pressure_config[CONF_IIR_FILTER])) | ||||||
|  |  | ||||||
|  |     cg.add(var.set_conversion_time(compute_measurement_conversion_time(config))) | ||||||
| @@ -1355,6 +1355,14 @@ sensor: | |||||||
|     name: "Distance" |     name: "Distance" | ||||||
|     update_interval: 60s |     update_interval: 60s | ||||||
|     i2c_id: i2c_bus |     i2c_id: i2c_bus | ||||||
|  |   - platform: bmp581 | ||||||
|  |     i2c_id: i2c_bus | ||||||
|  |     temperature: | ||||||
|  |       name: "BMP581 Temperature" | ||||||
|  |       iir_filter: 2x | ||||||
|  |     pressure: | ||||||
|  |       name: "BMP581 Pressure" | ||||||
|  |       oversampling: 128x | ||||||
|  |  | ||||||
| esp32_touch: | esp32_touch: | ||||||
|   setup_mode: false |   setup_mode: false | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user