Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions lib/bme280/bme280/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,26 @@
CALIB_H_SIZE,
CALIB_TP_SIZE,
DATA_BLOCK_SIZE,
FILTER_SHIFT,
MODE_FORCED,
MODE_MASK,
MODE_NORMAL,
MODE_SLEEP,
OSRS_P_SHIFT,
OSRS_T_SHIFT,
OSRS_X1,
REG_CALIB_HUM,
REG_CALIB_TEMP_PRESS,
REG_CHIP_ID,
REG_CONFIG,
REG_CTRL_HUM,
REG_CTRL_MEAS,
REG_DATA_START,
REG_SOFT_RESET,
REG_STATUS,
RESET_DELAY_MS,
SOFT_RESET_CMD,
STANDBY_SHIFT,
STATUS_IM_UPDATE,
STATUS_MEASURING,
)
Expand Down Expand Up @@ -139,6 +143,74 @@ def reset(self):
self._read_calibration()
self._configure_default()

# --------------------------------------------------
# Power and mode control
# --------------------------------------------------

def power_off(self):
"""Enter sleep mode. Stops all measurements."""
ctrl = self._read_reg(REG_CTRL_MEAS)
self._write_reg(REG_CTRL_MEAS, ctrl & ~MODE_MASK)

def power_on(self):
"""Enter normal mode. Continuous measurements at configured standby rate."""
ctrl = self._read_reg(REG_CTRL_MEAS)
self._write_reg(REG_CTRL_MEAS, (ctrl & ~MODE_MASK) | MODE_NORMAL)

def set_continuous(self, standby=None):
"""Start continuous measurements in normal mode.

Args:
standby: standby time constant (STANDBY_0_5_MS .. STANDBY_1000_MS).
If None, the current config register value is kept.
"""
if standby is not None:
self.set_standby(standby)
self.power_on()

# --------------------------------------------------
# Sensor configuration
# --------------------------------------------------

def set_oversampling(self, temperature=None, pressure=None, humidity=None):
"""Configure oversampling for one or more channels.

Args:
temperature: OSRS_SKIP .. OSRS_X16 (None = keep current).
pressure: OSRS_SKIP .. OSRS_X16 (None = keep current).
humidity: OSRS_SKIP .. OSRS_X16 (None = keep current).
"""
if humidity is not None:
self._write_reg(REG_CTRL_HUM, humidity)
ctrl = self._read_reg(REG_CTRL_MEAS)
if temperature is not None:
ctrl = (ctrl & ~(0x07 << OSRS_T_SHIFT)) | (temperature << OSRS_T_SHIFT)
if pressure is not None:
ctrl = (ctrl & ~(0x07 << OSRS_P_SHIFT)) | (pressure << OSRS_P_SHIFT)
# ctrl_meas must always be rewritten: changes to ctrl_hum are only
# latched when ctrl_meas is written (datasheet section 5.4.3).
self._write_reg(REG_CTRL_MEAS, ctrl)

def set_iir_filter(self, coefficient):
"""Configure the IIR filter coefficient.

Args:
coefficient: FILTER_OFF .. FILTER_16.
"""
config = self._read_reg(REG_CONFIG)
config = (config & ~(0x07 << FILTER_SHIFT)) | (coefficient << FILTER_SHIFT)
self._write_reg(REG_CONFIG, config)

def set_standby(self, standby):
"""Configure the standby time for normal mode.

Args:
standby: STANDBY_0_5_MS .. STANDBY_1000_MS.
"""
config = self._read_reg(REG_CONFIG)
config = (config & ~(0x07 << STANDBY_SHIFT)) | (standby << STANDBY_SHIFT)
self._write_reg(REG_CONFIG, config)

# --------------------------------------------------
# Status
# --------------------------------------------------
Expand Down
142 changes: 142 additions & 0 deletions tests/scenarios/bme280.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -274,3 +274,145 @@ tests:
result = triggered and abs(t - 25.08) < 0.1
expect_true: true
mode: [mock]

# ----- Power and mode control -----

- name: "power_off sets sleep mode"
action: script
script: |
i2c.clear_write_log()
dev.power_off()
log = i2c.get_write_log()
wrote_sleep = any(reg == 0xF4 and (data[0] & 0x03) == 0x00 for reg, data in log)
result = wrote_sleep
expect_true: true
mode: [mock]

- name: "power_on sets normal mode"
action: script
script: |
i2c.clear_write_log()
dev.power_on()
log = i2c.get_write_log()
wrote_normal = any(reg == 0xF4 and (data[0] & 0x03) == 0x03 for reg, data in log)
result = wrote_normal
expect_true: true
mode: [mock]

- name: "power_off preserves oversampling bits"
action: script
script: |
from bme280.const import OSRS_X4, OSRS_T_SHIFT, OSRS_P_SHIFT
dev.set_oversampling(temperature=OSRS_X4, pressure=OSRS_X4)
i2c.clear_write_log()
dev.power_off()
log = i2c.get_write_log()
ctrl = [data[0] for reg, data in log if reg == 0xF4][-1]
osrs_t = (ctrl >> 5) & 0x07
osrs_p = (ctrl >> 2) & 0x07
result = osrs_t == OSRS_X4 and osrs_p == OSRS_X4 and (ctrl & 0x03) == 0x00
expect_true: true
mode: [mock]

- name: "set_continuous enters normal mode"
action: script
script: |
dev.power_off()
i2c.clear_write_log()
dev.set_continuous()
log = i2c.get_write_log()
wrote_normal = any(reg == 0xF4 and (data[0] & 0x03) == 0x03 for reg, data in log)
result = wrote_normal
expect_true: true
mode: [mock]

- name: "set_continuous with standby configures both"
action: script
script: |
from bme280.const import STANDBY_500_MS, STANDBY_SHIFT
i2c.clear_write_log()
dev.set_continuous(standby=STANDBY_500_MS)
log = i2c.get_write_log()
wrote_config = any(reg == 0xF5 for reg, data in log)
wrote_normal = any(reg == 0xF4 and (data[0] & 0x03) == 0x03 for reg, data in log)
result = wrote_config and wrote_normal
expect_true: true
mode: [mock]

# ----- Configuration -----

- name: "set_oversampling configures temperature and pressure"
action: script
script: |
from bme280.const import OSRS_X2, OSRS_X4, OSRS_T_SHIFT, OSRS_P_SHIFT
i2c.clear_write_log()
dev.set_oversampling(temperature=OSRS_X2, pressure=OSRS_X4)
log = i2c.get_write_log()
ctrl_writes = [data[0] for reg, data in log if reg == 0xF4]
ctrl = ctrl_writes[-1]
osrs_t = (ctrl >> OSRS_T_SHIFT) & 0x07
osrs_p = (ctrl >> OSRS_P_SHIFT) & 0x07
result = osrs_t == OSRS_X2 and osrs_p == OSRS_X4
expect_true: true
mode: [mock]

- name: "set_oversampling configures humidity and latches via ctrl_meas"
action: script
script: |
from bme280.const import OSRS_X8
i2c.clear_write_log()
dev.set_oversampling(humidity=OSRS_X8)
log = i2c.get_write_log()
hum_writes = [data[0] for reg, data in log if reg == 0xF2]
# ctrl_meas must be rewritten after ctrl_hum for the change to take effect
ctrl_writes = [data[0] for reg, data in log if reg == 0xF4]
# Verify write order: ctrl_hum (0xF2) must come before ctrl_meas (0xF4)
hum_idx = next(i for i, (reg, _) in enumerate(log) if reg == 0xF2)
meas_idx = next(i for i, (reg, _) in enumerate(log) if reg == 0xF4)
result = (
len(hum_writes) == 1
and hum_writes[0] == OSRS_X8
and len(ctrl_writes) == 1
and hum_idx < meas_idx
)
expect_true: true
mode: [mock]

- name: "set_iir_filter writes config register"
action: script
script: |
from bme280.const import FILTER_16, FILTER_SHIFT
i2c.clear_write_log()
dev.set_iir_filter(FILTER_16)
log = i2c.get_write_log()
config_writes = [data[0] for reg, data in log if reg == 0xF5]
result = len(config_writes) == 1 and ((config_writes[0] >> FILTER_SHIFT) & 0x07) == FILTER_16
expect_true: true
mode: [mock]

- name: "set_standby writes config register"
action: script
script: |
from bme280.const import STANDBY_250_MS, STANDBY_SHIFT
i2c.clear_write_log()
dev.set_standby(STANDBY_250_MS)
log = i2c.get_write_log()
config_writes = [data[0] for reg, data in log if reg == 0xF5]
result = len(config_writes) == 1 and ((config_writes[0] >> STANDBY_SHIFT) & 0x07) == STANDBY_250_MS
expect_true: true
mode: [mock]

- name: "set_iir_filter preserves standby bits"
action: script
script: |
from bme280.const import STANDBY_500_MS, STANDBY_SHIFT, FILTER_4, FILTER_SHIFT
dev.set_standby(STANDBY_500_MS)
i2c.clear_write_log()
dev.set_iir_filter(FILTER_4)
log = i2c.get_write_log()
config = [data[0] for reg, data in log if reg == 0xF5][-1]
standby = (config >> STANDBY_SHIFT) & 0x07
filt = (config >> FILTER_SHIFT) & 0x07
result = standby == STANDBY_500_MS and filt == FILTER_4
expect_true: true
mode: [mock]
Loading