[bmp581] Add SPI support for BMP581 (#13124)

Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
This commit is contained in:
Daniel Kent
2026-03-26 12:11:46 -04:00
committed by GitHub
parent 8a6b009173
commit 9260401747
11 changed files with 188 additions and 2 deletions
+1
View File
@@ -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
@@ -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;
}
@@ -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};
@@ -0,0 +1,73 @@
#include <cstdint>
#include <cstddef>
#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
@@ -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<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_HIGH,
spi::CLOCK_PHASE_TRAILING, spi::DATA_RATE_200KHZ> {
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
+48
View File
@@ -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)
+9
View File
@@ -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
@@ -0,0 +1,7 @@
substitutions:
cs_pin: GPIO5
packages:
spi: !include ../../test_build_components/common/spi/esp32-idf.yaml
<<: !include common.yaml
@@ -0,0 +1,7 @@
substitutions:
cs_pin: GPIO15
packages:
spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml
<<: !include common.yaml
@@ -0,0 +1,7 @@
substitutions:
cs_pin: GPIO5
packages:
spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml
<<: !include common.yaml