diff --git a/CODEOWNERS b/CODEOWNERS index afe4cdb8712..8d297d7b072 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -92,6 +92,7 @@ esphome/components/bmp3xx_i2c/* @latonita esphome/components/bmp3xx_spi/* @latonita esphome/components/bmp581_base/* @danielkent-net @kahrendt esphome/components/bmp581_i2c/* @danielkent-net @kahrendt +esphome/components/bmp581_spi/* @danielkent-net @kahrendt esphome/components/bp1658cj/* @Cossid esphome/components/bp5758d/* @Cossid esphome/components/bthome_mithermometer/* @nagyrobi diff --git a/esphome/components/bmp581_base/bmp581_base.cpp b/esphome/components/bmp581_base/bmp581_base.cpp index 89a92de31d2..c9d250545bc 100644 --- a/esphome/components/bmp581_base/bmp581_base.cpp +++ b/esphome/components/bmp581_base/bmp581_base.cpp @@ -469,14 +469,18 @@ bool BMP581Component::read_temperature_and_pressure_(float &temperature, float & } bool BMP581Component::reset_() { + // - activates interface (only relevant for SPI mode) // - writes reset command to the command register // - waits for sensor to complete reset + // - activates interface (only relevant for SPI mode) // - returns the Power-On-Reboot interrupt status, which is asserted if successful + // activates communication interface (SPI only) + this->activate_interface(); + // writes reset command to BMP's command register if (!this->bmp_write_byte(BMP581_COMMAND, RESET_COMMAND)) { ESP_LOGE(TAG, "Failed to write reset command"); - return false; } @@ -484,6 +488,9 @@ bool BMP581Component::reset_() { // - round up to 3 ms delay(3); + // reactivates communication interface after reset (SPI only) + this->activate_interface(); + // read interrupt status register if (!this->bmp_read_byte(BMP581_INT_STATUS, &this->int_status_.reg)) { ESP_LOGE(TAG, "Failed to read interrupt status register"); @@ -491,7 +498,7 @@ bool BMP581Component::reset_() { return false; } - // Power-On-Reboot bit is asserted if sensor successfully reset + // power-On-Reboot bit is asserted if sensor successfully reset return this->int_status_.bit.por; } diff --git a/esphome/components/bmp581_base/bmp581_base.h b/esphome/components/bmp581_base/bmp581_base.h index d99c420272a..c3920512e0d 100644 --- a/esphome/components/bmp581_base/bmp581_base.h +++ b/esphome/components/bmp581_base/bmp581_base.h @@ -87,6 +87,9 @@ class BMP581Component : public PollingComponent { virtual bool bmp_read_bytes(uint8_t a_register, uint8_t *data, size_t len) = 0; virtual bool bmp_write_bytes(uint8_t a_register, uint8_t *data, size_t len) = 0; + // Interface activation function. Only used for SPI interface; no-op for I2C. + virtual void activate_interface() {} + sensor::Sensor *temperature_sensor_{nullptr}; sensor::Sensor *pressure_sensor_{nullptr}; diff --git a/esphome/components/bmp581_spi/__init__.py b/esphome/components/bmp581_spi/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/esphome/components/bmp581_spi/bmp581_spi.cpp b/esphome/components/bmp581_spi/bmp581_spi.cpp new file mode 100644 index 00000000000..01435880f08 --- /dev/null +++ b/esphome/components/bmp581_spi/bmp581_spi.cpp @@ -0,0 +1,73 @@ +#include +#include + +#include "bmp581_spi.h" +#include "esphome/components/bmp581_base/bmp581_base.h" +#include "esphome/components/spi/spi.h" + +namespace esphome::bmp581_spi { + +static const char *const TAG = "bmp581_spi"; + +// OR (|) register with BMP_SPI_READ for read +inline constexpr uint8_t BMP_SPI_READ = 0x80; + +// AND (&) register with BMP_SPI_WRITE for write +inline constexpr uint8_t BMP_SPI_WRITE = 0x7F; + +void BMP581SPIComponent::dump_config() { + BMP581Component::dump_config(); + LOG_SPI_DEVICE(this); +} + +void BMP581SPIComponent::setup() { + this->spi_setup(); + BMP581Component::setup(); +} + +void BMP581SPIComponent::activate_interface() { + // - forces the device into SPI mode using a dummy read + uint8_t dummy_read = 0; + this->bmp_read_byte(bmp581_base::BMP581_CHIP_ID, &dummy_read); +} + +// In SPI mode, only 7 bits of the register addresses are used; the MSB of register address is not used +// and replaced by a read/write bit (RW = ‘0’ for write and RW = ‘1’ for read). +// Example: address 0xF7 is accessed by using SPI register address 0x77. For write access, the byte +// 0x77 is transferred, for read access, the byte 0xF7 is transferred. +// The expressions BMP_SPI_READ (| with register) and BMP_SPI_WRITE (& with register) +// are defined for readability. +// https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp581-ds004.pdf + +bool BMP581SPIComponent::bmp_read_byte(uint8_t a_register, uint8_t *data) { + this->enable(); + this->transfer_byte(a_register | BMP_SPI_READ); + *data = this->transfer_byte(0); + this->disable(); + return true; +} + +bool BMP581SPIComponent::bmp_write_byte(uint8_t a_register, uint8_t data) { + this->enable(); + this->transfer_byte(a_register & BMP_SPI_WRITE); + this->transfer_byte(data); + this->disable(); + return true; +} + +bool BMP581SPIComponent::bmp_read_bytes(uint8_t a_register, uint8_t *data, size_t len) { + this->enable(); + this->transfer_byte(a_register | BMP_SPI_READ); + this->read_array(data, len); + this->disable(); + return true; +} + +bool BMP581SPIComponent::bmp_write_bytes(uint8_t a_register, uint8_t *data, size_t len) { + this->enable(); + this->transfer_byte(a_register & BMP_SPI_WRITE); + this->write_array(data, len); + this->disable(); + return true; +} +} // namespace esphome::bmp581_spi diff --git a/esphome/components/bmp581_spi/bmp581_spi.h b/esphome/components/bmp581_spi/bmp581_spi.h new file mode 100644 index 00000000000..57f75588d5e --- /dev/null +++ b/esphome/components/bmp581_spi/bmp581_spi.h @@ -0,0 +1,24 @@ +#pragma once + +#include "esphome/components/bmp581_base/bmp581_base.h" +#include "esphome/components/spi/spi.h" + +namespace esphome::bmp581_spi { + +// BMP581 is technically compatible with SPI Mode0 and Mode3. Default to Mode3. +class BMP581SPIComponent : public esphome::bmp581_base::BMP581Component, + public spi::SPIDevice { + public: + void setup() override; + bool bmp_read_byte(uint8_t a_register, uint8_t *data) override; + bool bmp_write_byte(uint8_t a_register, uint8_t data) override; + bool bmp_read_bytes(uint8_t a_register, uint8_t *data, size_t len) override; + bool bmp_write_bytes(uint8_t a_register, uint8_t *data, size_t len) override; + void dump_config() override; + + protected: + void activate_interface() override; +}; + +} // namespace esphome::bmp581_spi diff --git a/esphome/components/bmp581_spi/sensor.py b/esphome/components/bmp581_spi/sensor.py new file mode 100644 index 00000000000..75f60b24604 --- /dev/null +++ b/esphome/components/bmp581_spi/sensor.py @@ -0,0 +1,48 @@ +import logging + +import esphome.codegen as cg +from esphome.components import spi +from esphome.components.spi import CONF_SPI_MODE +import esphome.config_validation as cv + +from ..bmp581_base import CONFIG_SCHEMA_BASE, to_code_base + +AUTO_LOAD = ["bmp581_base"] +CODEOWNERS = ["@kahrendt", "@danielkent-net"] +DEPENDENCIES = ["spi"] + +_LOGGER = logging.getLogger(__name__) + +VALID_SPI_MODES = { + 0: "MODE0", + "0": "MODE0", + "MODE0": "MODE0", + 3: "MODE3", + "3": "MODE3", + "MODE3": "MODE3", +} + +bmp581_ns = cg.esphome_ns.namespace("bmp581_spi") +BMP581SPIComponent = bmp581_ns.class_( + "BMP581SPIComponent", cg.PollingComponent, spi.SPIDevice +) + + +def check_spi_mode(config): + spi_mode = config.get(CONF_SPI_MODE) + if spi_mode not in VALID_SPI_MODES: + raise cv.Invalid("BMP581 only supports SPI mode 3") + return config + + +CONFIG_SCHEMA = cv.All( + CONFIG_SCHEMA_BASE.extend(spi.spi_device_schema(default_mode="mode3")).extend( + {cv.GenerateID(): cv.declare_id(BMP581SPIComponent)} + ), + check_spi_mode, +) + + +async def to_code(config): + var = await to_code_base(config) + await spi.register_spi_device(var, config) diff --git a/tests/components/bmp581_spi/common.yaml b/tests/components/bmp581_spi/common.yaml new file mode 100644 index 00000000000..f22074f867d --- /dev/null +++ b/tests/components/bmp581_spi/common.yaml @@ -0,0 +1,9 @@ +sensor: + - platform: bmp581_spi + cs_pin: ${cs_pin} + temperature: + name: BMP581 Temperature + iir_filter: 2x + pressure: + name: BMP581 Pressure + oversampling: 128x diff --git a/tests/components/bmp581_spi/test.esp32-idf.yaml b/tests/components/bmp581_spi/test.esp32-idf.yaml new file mode 100644 index 00000000000..a3352cf880d --- /dev/null +++ b/tests/components/bmp581_spi/test.esp32-idf.yaml @@ -0,0 +1,7 @@ +substitutions: + cs_pin: GPIO5 + +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + +<<: !include common.yaml diff --git a/tests/components/bmp581_spi/test.esp8266-ard.yaml b/tests/components/bmp581_spi/test.esp8266-ard.yaml new file mode 100644 index 00000000000..595f31046ab --- /dev/null +++ b/tests/components/bmp581_spi/test.esp8266-ard.yaml @@ -0,0 +1,7 @@ +substitutions: + cs_pin: GPIO15 + +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + +<<: !include common.yaml diff --git a/tests/components/bmp581_spi/test.rp2040-ard.yaml b/tests/components/bmp581_spi/test.rp2040-ard.yaml new file mode 100644 index 00000000000..79ea6ce90b0 --- /dev/null +++ b/tests/components/bmp581_spi/test.rp2040-ard.yaml @@ -0,0 +1,7 @@ +substitutions: + cs_pin: GPIO5 + +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + +<<: !include common.yaml