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
18 changes: 17 additions & 1 deletion lib/bme280/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,22 @@ Returns the dew point temperature in **degrees Celsius**, computed from the curr

---

### Measurement Time

```python
ms = sensor.measurement_time_ms()
```

Returns the maximum measurement time in **milliseconds** (integer, rounded up) based on the current oversampling settings (datasheet section 9.1). The result can be passed directly to `sleep_ms()`. Useful for estimating how long a forced measurement takes:

```python
print("Measurement takes up to", sensor.measurement_time_ms(), "ms")
```

Note: in practice, prefer `read_one_shot()` which handles triggering and waiting automatically.

---

## Data-Ready Status

```python
Expand Down Expand Up @@ -331,7 +347,7 @@ Performs a soft reset, re-reads calibration data, and re-applies default configu
| read_one_shot | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| set_continuous | ✅ | ❌ | ⚠️ Via mode | ❌ | ❌ | ❌ |
| Integer compensation | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ |
| Measurement time estimate | | ❌ | ✅ | ❌ | ❌ | ❌ |
| Measurement time estimate | | ❌ | ✅ | ❌ | ❌ | ❌ |
| Multi-unit temperature | ❌ | ❌ | ❌ | ✅ C/F/K | ❌ | ❌ |
| BMP280 compatibility | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| Dedicated exceptions | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
Expand Down
30 changes: 28 additions & 2 deletions lib/bme280/bme280/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,26 @@ def set_standby(self, standby):
config = (config & ~(0x07 << STANDBY_SHIFT)) | (standby << STANDBY_SHIFT)
self._write_reg(REG_CONFIG, config)

def measurement_time_ms(self):
"""Return the maximum measurement time in milliseconds (int, rounded up).

Computed from the current oversampling settings using the formula
from the BME280 datasheet (section 9.1). The result is always an
integer ceiling so it can be passed directly to ``sleep_ms()``.
"""
ctrl_meas = self._read_reg(REG_CTRL_MEAS)
osrs_t = (ctrl_meas >> OSRS_T_SHIFT) & 0x07
osrs_p = (ctrl_meas >> OSRS_P_SHIFT) & 0x07
osrs_h = self._read_reg(REG_CTRL_HUM) & 0x07
t_ms = 1.25
if osrs_t:
t_ms += 2.3 * (1 << (osrs_t - 1))
if osrs_p:
t_ms += 2.3 * (1 << (osrs_p - 1)) + 0.575
if osrs_h:
t_ms += 2.3 * (1 << (osrs_h - 1)) + 0.575
return int(t_ms) + (1 if t_ms != int(t_ms) else 0)

# --------------------------------------------------
# Status
# --------------------------------------------------
Expand Down Expand Up @@ -260,8 +280,14 @@ def trigger_one_shot(self):
ctrl = self._read_reg(REG_CTRL_MEAS)
self._write_reg(REG_CTRL_MEAS, (ctrl & ~MODE_MASK) | MODE_FORCED)

def _wait_measurement(self, timeout_ms=100):
"""Wait for measurement to complete. Raises on timeout."""
def _wait_measurement(self):
"""Wait for measurement to complete. Raises on timeout.

The timeout is derived from :meth:`measurement_time_ms` with a
2x safety margin so that high-oversampling configurations (e.g.
x16/x16/x16 ≈ 113 ms) never hit a spurious timeout.
"""
timeout_ms = self.measurement_time_ms() * 2 + 10
for _ in range(timeout_ms // 5):
if self.data_ready():
return
Expand Down
52 changes: 52 additions & 0 deletions tests/scenarios/bme280.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -654,3 +654,55 @@ tests:
result = abs(dp - expected) < 0.01
expect_true: true
mode: [mock]

# ----- Measurement time -----

- name: "measurement_time_ms returns int ceiling with default x1"
action: script
script: |
# Default: OSRS_X1 for all three → ceil(9.3) = 10 ms
t = dev.measurement_time_ms()
result = t == 10 and isinstance(t, int)
expect_true: true
mode: [mock]

- name: "measurement_time_ms with weather station config"
action: script
script: |
from bme280.const import OSRS_X2, OSRS_X16
dev.set_oversampling(temperature=OSRS_X2, pressure=OSRS_X16, humidity=OSRS_X2)
t = dev.measurement_time_ms()
# ceil(1.25 + 2.3*2 + (2.3*16+0.575) + (2.3*2+0.575)) = ceil(48.4) = 49 ms
result = t == 49
# Restore default
from bme280.const import OSRS_X1
dev.set_oversampling(temperature=OSRS_X1, pressure=OSRS_X1, humidity=OSRS_X1)
expect_true: true
mode: [mock]

- name: "measurement_time_ms with all channels skipped"
action: script
script: |
from bme280.const import OSRS_SKIP
dev.set_oversampling(temperature=OSRS_SKIP, pressure=OSRS_SKIP, humidity=OSRS_SKIP)
t = dev.measurement_time_ms()
# ceil(1.25) = 2 ms
result = t == 2
# Restore default
from bme280.const import OSRS_X1
dev.set_oversampling(temperature=OSRS_X1, pressure=OSRS_X1, humidity=OSRS_X1)
expect_true: true
mode: [mock]

- name: "measurement_time_ms handles x16 on all channels"
action: script
script: |
from bme280.const import OSRS_X16
dev.set_oversampling(temperature=OSRS_X16, pressure=OSRS_X16, humidity=OSRS_X16)
t = dev.measurement_time_ms()
# ceil(1.25 + 2.3*16 + (2.3*16+0.575) + (2.3*16+0.575)) = ceil(112.8) = 113 ms
result = t == 113
from bme280.const import OSRS_X1
dev.set_oversampling(temperature=OSRS_X1, pressure=OSRS_X1, humidity=OSRS_X1)
expect_true: true
mode: [mock]
Loading