From 5ea5becf2b3b9b543bc233ca143d8c3eed5fe290 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Thu, 25 Sep 2025 14:34:18 -0500 Subject: [PATCH 1/6] SPI support --- adafruit_bmp5xx.py | 50 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/adafruit_bmp5xx.py b/adafruit_bmp5xx.py index 0cfb20e..989d737 100644 --- a/adafruit_bmp5xx.py +++ b/adafruit_bmp5xx.py @@ -1,4 +1,3 @@ -# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries # SPDX-FileCopyrightText: Copyright (c) 2025 Tim Cocks for Adafruit Industries # # SPDX-License-Identifier: MIT @@ -34,15 +33,17 @@ import time from adafruit_bus_device.i2c_device import I2CDevice -from adafruit_register.i2c_bit import ROBit, RWBit -from adafruit_register.i2c_bits import ROBits, RWBits -from adafruit_register.i2c_struct import ROUnaryStruct, UnaryStruct +from adafruit_bus_device.spi_device import SPIDevice # noqa: PLC0415 +from adafruit_register.register_accessor import I2CRegisterAccessor, SPIRegisterAccessor +from adafruit_register.register_bit import ROBit, RWBit +from adafruit_register.register_bits import ROBits, RWBits from micropython import const try: from typing import Optional - from busio import I2C + from busio import I2C, SPI + from digitalio import DigitalInOut except ImportError: pass @@ -71,6 +72,7 @@ BMP5XX_REG_DSP_IIR = const(0x31) BMP5XX_REG_DSP_CONFIG = const(0x30) BMP5XX_REG_INT_SOURCE = const(0x15) +BMP_REG_ASIC_STATUS = const(0x11) # ODR settings BMP5XX_ODR_240_HZ = const(0x00) @@ -138,12 +140,12 @@ BMP585_CHIP_ID = const(0x51) -class BMP5XX_I2C: +class BMP5XX: """ Bosche BMP5xx temperature and pressure sensor breakout CircuitPython driver. """ - chip_id: int = ROUnaryStruct(BMP5_REG_ID, "B") + chip_id: int = ROBits(8, BMP5_REG_ID, 0) # Status register bits status_nvm_ready: bool = ROBit(BMP5_REG_STATUS, 1) # NVM ready @@ -219,14 +221,31 @@ class BMP5XX_I2C: output_data_rate = RWBits(5, BMP5XX_REG_ODR_CONFIG, 2) """Output data rate. Must be one of the ODR constants.""" - command = UnaryStruct(BMP5_REG_CMD, "B") # command register + command = RWBits(8, BMP5_REG_CMD, 0) # command register """Command register""" - def __init__(self, i2c: I2C, address: int = DEFAULT_ADAFRUIT_ADDR) -> None: - try: - self.i2c_device = I2CDevice(i2c, address) - except ValueError: - raise ValueError(f"No I2C device found at address 0x{address:02X}") + hardware_interface = RWBits(2, BMP_REG_ASIC_STATUS, 0) + + def __init__( + self, + i2c: I2C = None, + address: Optional[int] = DEFAULT_ADAFRUIT_ADDR, + spi: SPI = None, + cs: DigitalInOut = None, + ) -> None: + if spi is not None and cs is not None: + try: + self.spi_device = SPIDevice(spi, cs, baudrate=1000000) + self.register_accessor = SPIRegisterAccessor(self.spi_device) + except ValueError: + raise ValueError(f"No SPI device found.") + + elif i2c is not None: + try: + i2c_device = I2CDevice(i2c, address) + self.register_accessor = I2CRegisterAccessor(i2c_device) + except ValueError: + raise ValueError(f"No I2C device found.") self.sea_level_pressure = 1013.25 self.reset() @@ -271,10 +290,11 @@ def altitude(self) -> float: def reset(self) -> None: """Reset the BMP5xx device.""" self.command = BMP5_SOFT_RESET_CMD - time.sleep(0.006) + time.sleep(0.012) + _throwaway = self.chip_id if self.chip_id not in {BMP581_CHIP_ID, BMP585_CHIP_ID}: - raise ValueError(f"CHIP_ID was zero") + raise ValueError(f"CHIP_ID was incorrect") if not self.status_nvm_ready: raise ValueError("NVM not ready") if self.status_nvm_err: From d52f6a8d0a99e592fd1e16d976255947513b4d9a Mon Sep 17 00:00:00 2001 From: foamyguy Date: Thu, 25 Sep 2025 14:45:43 -0500 Subject: [PATCH 2/6] fix simpletest. add SPI example --- examples/bmp5xx_simpletest.py | 4 ++-- examples/bmp5xx_spi_simpletest.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 examples/bmp5xx_spi_simpletest.py diff --git a/examples/bmp5xx_simpletest.py b/examples/bmp5xx_simpletest.py index 96a3fc5..8dfc44e 100644 --- a/examples/bmp5xx_simpletest.py +++ b/examples/bmp5xx_simpletest.py @@ -5,7 +5,7 @@ import board -from adafruit_bmp5xx import BMP5XX_I2C +from adafruit_bmp5xx import BMP5XX SEALEVELPRESSURE_HPA = 1013.25 @@ -13,7 +13,7 @@ i2c = board.I2C() # uses board.SCL and board.SDA # i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller -bmp = BMP5XX_I2C(i2c) +bmp = BMP5XX(i2c) bmp.sea_level_pressure = SEALEVELPRESSURE_HPA diff --git a/examples/bmp5xx_spi_simpletest.py b/examples/bmp5xx_spi_simpletest.py new file mode 100644 index 0000000..97e168b --- /dev/null +++ b/examples/bmp5xx_spi_simpletest.py @@ -0,0 +1,30 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Tim Cocks for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense +import time + +import board +from digitalio import DigitalInOut, Direction + +from adafruit_bmp5xx import BMP5XX + +SEALEVELPRESSURE_HPA = 1013.25 + +# SPI setup +spi = board.SPI() # uses board.SCL and board.SDA +spi = board.SPI() +cs = DigitalInOut(board.D10) +cs.direction = Direction.OUTPUT +bmp = BMP5XX(spi=spi, cs=cs) + + +bmp.sea_level_pressure = SEALEVELPRESSURE_HPA + +while True: + if bmp.data_ready: + print( + f"temp F: {bmp.temperature * (9 / 5) + 32} " + f"pressure: {bmp.pressure} hPa " + f"Approx altitude: {bmp.altitude} m" + ) + time.sleep(1) From 83189a5c8dce9c570e6389a9a17a209f9eada7f4 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Fri, 26 Sep 2025 08:55:59 -0500 Subject: [PATCH 3/6] spi multi-device example --- docs/examples.rst | 8 ++++++ examples/bmp5xx_spi_multi_device.py | 44 +++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 examples/bmp5xx_spi_multi_device.py diff --git a/docs/examples.rst b/docs/examples.rst index b68bc01..0acb2cf 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -6,3 +6,11 @@ Ensure your device works with this simple test. .. literalinclude:: ../examples/bmp5xx_simpletest.py :caption: examples/bmp5xx_simpletest.py :linenos: + +.. literalinclude:: ../examples/bmp5xx_spi_simpletest.py + :caption: examples/bmp5xx_spi_simpletest.py + :linenos: + +.. literalinclude:: ../examples/bmp5xx_spi_multi_device.py + :caption: examples/bmp5xx_spi_multi_device.py + :linenos: diff --git a/examples/bmp5xx_spi_multi_device.py b/examples/bmp5xx_spi_multi_device.py new file mode 100644 index 0000000..a66094c --- /dev/null +++ b/examples/bmp5xx_spi_multi_device.py @@ -0,0 +1,44 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Tim Cocks for Adafruit Industries +# +# SPDX-License-Identifier: MIT +import time + +import board +from digitalio import DigitalInOut, Direction + +from adafruit_bmp5xx import BMP5XX + +SEALEVELPRESSURE_HPA = 1013.25 + +# SPI setup +spi = board.SPI() + +# first sensor setup +cs1 = DigitalInOut(board.D10) +cs1.direction = Direction.OUTPUT +bmp1 = BMP5XX(spi=spi, cs=cs1) + +# second sensor setup, different CS pin, same SPI bus +cs2 = DigitalInOut(board.D11) +cs2.direction = Direction.OUTPUT +bmp2 = BMP5XX(spi=spi, cs=cs2) + +bmp1.sea_level_pressure = SEALEVELPRESSURE_HPA +bmp2.sea_level_pressure = SEALEVELPRESSURE_HPA + +while True: + if bmp1.data_ready: + print( + f"BMP1 temp F: {bmp1.temperature * (9 / 5) + 32} " + f"pressure: {bmp1.pressure} hPa " + f"Approx altitude: {bmp1.altitude} m" + ) + + if bmp2.data_ready: + print( + f"BMP2 temp F: {bmp2.temperature * (9 / 5) + 32} " + f"pressure: {bmp2.pressure} hPa " + f"Approx altitude: {bmp2.altitude} m" + ) + print("-----") + time.sleep(1) From ab031e101f3327ba612433a8b3fa8bd2997a1b52 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Thu, 2 Oct 2025 12:05:24 -0500 Subject: [PATCH 4/6] removed unused register --- adafruit_bmp5xx.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/adafruit_bmp5xx.py b/adafruit_bmp5xx.py index 989d737..32a1424 100644 --- a/adafruit_bmp5xx.py +++ b/adafruit_bmp5xx.py @@ -72,7 +72,6 @@ BMP5XX_REG_DSP_IIR = const(0x31) BMP5XX_REG_DSP_CONFIG = const(0x30) BMP5XX_REG_INT_SOURCE = const(0x15) -BMP_REG_ASIC_STATUS = const(0x11) # ODR settings BMP5XX_ODR_240_HZ = const(0x00) @@ -224,8 +223,6 @@ class BMP5XX: command = RWBits(8, BMP5_REG_CMD, 0) # command register """Command register""" - hardware_interface = RWBits(2, BMP_REG_ASIC_STATUS, 0) - def __init__( self, i2c: I2C = None, From 41f82879c76c021d43c1a70960d788e10b9bc34c Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 21 Oct 2025 15:19:31 -0500 Subject: [PATCH 5/6] refactor to over_spi() and over_i2c() functions. Add fallback warning for BMP5XX_I2C() --- adafruit_bmp5xx.py | 64 +++++++++++++++++++---------- examples/bmp5xx_simpletest.py | 2 +- examples/bmp5xx_spi_multi_device.py | 4 +- examples/bmp5xx_spi_simpletest.py | 2 +- 4 files changed, 47 insertions(+), 25 deletions(-) diff --git a/adafruit_bmp5xx.py b/adafruit_bmp5xx.py index 32a1424..9c9c223 100644 --- a/adafruit_bmp5xx.py +++ b/adafruit_bmp5xx.py @@ -40,7 +40,7 @@ from micropython import const try: - from typing import Optional + from typing import Optional, Union from busio import I2C, SPI from digitalio import DigitalInOut @@ -223,26 +223,38 @@ class BMP5XX: command = RWBits(8, BMP5_REG_CMD, 0) # command register """Command register""" - def __init__( - self, - i2c: I2C = None, - address: Optional[int] = DEFAULT_ADAFRUIT_ADDR, - spi: SPI = None, - cs: DigitalInOut = None, - ) -> None: - if spi is not None and cs is not None: - try: - self.spi_device = SPIDevice(spi, cs, baudrate=1000000) - self.register_accessor = SPIRegisterAccessor(self.spi_device) - except ValueError: - raise ValueError(f"No SPI device found.") - - elif i2c is not None: - try: - i2c_device = I2CDevice(i2c, address) - self.register_accessor = I2CRegisterAccessor(i2c_device) - except ValueError: - raise ValueError(f"No I2C device found.") + @staticmethod + def over_spi(spi: SPI, cs: DigitalInOut): + """ + Initialize BMP5XX breakout over SPI bus. + + :param spi: busio.SPI instance to communicate over + :param cs: DigitalInOut instance to use for chip select + :return: Initialized BMP5XX object + """ + spi_device = SPIDevice(spi, cs) + return BMP5XX(spi_device) + + @staticmethod + def over_i2c(i2c: I2C, address=DEFAULT_ADAFRUIT_ADDR): + """ + Initialize BMP5XX breakout over I2C bus. + + :param i2c: busio.I2C instance to communicate over + :param address: The I2C address to use. Defaults to DEFAULT_ADAFRUIT_ADDR + :return: Initialized BMP5XX object + """ + i2c_device = I2CDevice(i2c, address) + return BMP5XX(i2c_device) + + def __init__(self, bus_device: Union[I2CDevice, SPIDevice]): + if isinstance(bus_device, SPIDevice): + self.register_accessor = SPIRegisterAccessor(bus_device) + + elif isinstance(bus_device, I2CDevice): + self.register_accessor = I2CRegisterAccessor(bus_device) + else: + raise ValueError("bus_device must be an instance of I2CDevice or SPIDevice.") self.sea_level_pressure = 1013.25 self.reset() @@ -326,3 +338,13 @@ def mode(self, new_mode: int) -> None: self.deep_disabled = True self._mode = new_mode + + +def BMP5XX_I2C(i2c: I2C, address: int = DEFAULT_ADAFRUIT_ADDR) -> BMP5XX: + import warnings # noqa: PLC0415, import outside top level + + warnings.warn( + "Warning: BMP5XX_I2C class is deprecated and will be removed in a future version. " + "User code should be updated to use BMP5XX.over_i2c()" + ) + return BMP5XX.over_i2c(i2c, address) diff --git a/examples/bmp5xx_simpletest.py b/examples/bmp5xx_simpletest.py index 8dfc44e..d964f92 100644 --- a/examples/bmp5xx_simpletest.py +++ b/examples/bmp5xx_simpletest.py @@ -13,7 +13,7 @@ i2c = board.I2C() # uses board.SCL and board.SDA # i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller -bmp = BMP5XX(i2c) +bmp = BMP5XX.over_i2c(i2c) bmp.sea_level_pressure = SEALEVELPRESSURE_HPA diff --git a/examples/bmp5xx_spi_multi_device.py b/examples/bmp5xx_spi_multi_device.py index a66094c..5045a76 100644 --- a/examples/bmp5xx_spi_multi_device.py +++ b/examples/bmp5xx_spi_multi_device.py @@ -16,12 +16,12 @@ # first sensor setup cs1 = DigitalInOut(board.D10) cs1.direction = Direction.OUTPUT -bmp1 = BMP5XX(spi=spi, cs=cs1) +bmp1 = BMP5XX.over_spi(spi=spi, cs=cs1) # second sensor setup, different CS pin, same SPI bus cs2 = DigitalInOut(board.D11) cs2.direction = Direction.OUTPUT -bmp2 = BMP5XX(spi=spi, cs=cs2) +bmp2 = BMP5XX.over_spi(spi=spi, cs=cs2) bmp1.sea_level_pressure = SEALEVELPRESSURE_HPA bmp2.sea_level_pressure = SEALEVELPRESSURE_HPA diff --git a/examples/bmp5xx_spi_simpletest.py b/examples/bmp5xx_spi_simpletest.py index 97e168b..32733d5 100644 --- a/examples/bmp5xx_spi_simpletest.py +++ b/examples/bmp5xx_spi_simpletest.py @@ -15,7 +15,7 @@ spi = board.SPI() cs = DigitalInOut(board.D10) cs.direction = Direction.OUTPUT -bmp = BMP5XX(spi=spi, cs=cs) +bmp = BMP5XX.over_spi(spi=spi, cs=cs) bmp.sea_level_pressure = SEALEVELPRESSURE_HPA From c4fdeb07bfb4c035458e557b28de318a4434f26f Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 21 Oct 2025 15:30:01 -0500 Subject: [PATCH 6/6] remove extra SPI() init --- examples/bmp5xx_spi_simpletest.py | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/bmp5xx_spi_simpletest.py b/examples/bmp5xx_spi_simpletest.py index 32733d5..c48b4ed 100644 --- a/examples/bmp5xx_spi_simpletest.py +++ b/examples/bmp5xx_spi_simpletest.py @@ -11,7 +11,6 @@ SEALEVELPRESSURE_HPA = 1013.25 # SPI setup -spi = board.SPI() # uses board.SCL and board.SDA spi = board.SPI() cs = DigitalInOut(board.D10) cs.direction = Direction.OUTPUT