diff --git a/lib/bme280/README.md b/lib/bme280/README.md index ab85c9be..5b91f94f 100644 --- a/lib/bme280/README.md +++ b/lib/bme280/README.md @@ -163,6 +163,29 @@ Triggers a single forced measurement. Poll `data_ready()` for completion, then r --- +### Altitude + +```python +alt = sensor.altitude() +``` + +Returns the estimated altitude in **meters** using the ICAO barometric formula. + +You can also pass an already-read pressure value to avoid a redundant I2C read: + +```python +temperature, pressure, humidity = sensor.read() +alt = sensor.altitude(pressure_hpa=pressure) +``` + +The computation uses `sea_level_pressure_hpa` as reference (default: 1013.25 hPa). Adjust it for your location: + +```python +sensor.sea_level_pressure_hpa = 1020.0 +``` + +--- + ## Data-Ready Status ```python @@ -288,8 +311,8 @@ Performs a soft reset, re-reads calibration data, and re-applies default configu | Oversampling (per channel) | ✅ | ✅ | ✅ | ✅ | ⚠️ Fixed x16 | ⚠️ Constants only | | IIR filter | ✅ | ❌ | ✅ | ✅ | ⚠️ Fixed x16 | ❌ | | Standby time | ✅ | ❌ | ✅ | ❌ | ⚠️ Fixed 500ms | ❌ | -| Altitude | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | -| Sea-level pressure | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | +| Altitude | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | +| Sea-level pressure | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | | Dew point | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | | Soft reset | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | | Full reset + recalibration | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | diff --git a/lib/bme280/bme280/device.py b/lib/bme280/bme280/device.py index 7ec28c83..a0108e07 100644 --- a/lib/bme280/bme280/device.py +++ b/lib/bme280/bme280/device.py @@ -39,6 +39,7 @@ class BME280(object): def __init__(self, i2c, address=BME280_I2C_DEFAULT_ADDR): self.i2c = i2c self.address = address + self.sea_level_pressure_hpa = 1013.25 self._check_device() self._wait_boot() self._read_calibration() @@ -395,3 +396,16 @@ def read_one_shot(self): press_hpa = self._compensate_pressure(raw_press) / 25600.0 hum_rh = self._compensate_humidity(raw_hum) / 1024.0 return temp_c, press_hpa, hum_rh + + def altitude(self, pressure_hpa=None): + """Return estimated altitude in meters. + + Uses the ICAO barometric formula with ``sea_level_pressure_hpa`` + as reference (default 1013.25 hPa, adjustable). + + Args: + pressure_hpa: pressure in hPa. If None, a new reading is taken + via :meth:`pressure_hpa`. + """ + p = self.pressure_hpa() if pressure_hpa is None else pressure_hpa + return 44330.0 * (1.0 - (p / self.sea_level_pressure_hpa) ** 0.1903) diff --git a/lib/bme280/examples/weather_station.py b/lib/bme280/examples/weather_station.py index acfb7aa9..a83e24ef 100644 --- a/lib/bme280/examples/weather_station.py +++ b/lib/bme280/examples/weather_station.py @@ -1,7 +1,7 @@ """Continuous weather monitoring with altitude estimation. Reads temperature, pressure, and humidity every 5 seconds in normal mode, -computes approximate altitude from pressure using the barometric formula, +computes altitude using the driver's built-in barometric formula, and logs each measurement to the DAPLink flash as CSV. """ @@ -13,15 +13,15 @@ from daplink_flash import DaplinkFlash from machine import I2C -# Sea-level reference pressure in hPa (adjust to local conditions) -SEA_LEVEL_PRESSURE = 1013.25 - i2c = I2C(1) sensor = BME280(i2c) bridge = DaplinkBridge(i2c) flash = DaplinkFlash(bridge) +# Adjust sea-level pressure to local conditions for accurate altitude +# sensor.sea_level_pressure_hpa = 1020.0 + # Configure for weather monitoring: high pressure resolution, moderate temp/hum sensor.set_oversampling(temperature=OSRS_X2, pressure=OSRS_X16, humidity=OSRS_X2) sensor.set_iir_filter(FILTER_16) @@ -35,15 +35,9 @@ flash.write_line("temperature;pressure;humidity;altitude") - -def altitude_m(pressure_hpa): - """Estimate altitude in meters from pressure using the barometric formula.""" - return 44330.0 * (1.0 - (pressure_hpa / SEA_LEVEL_PRESSURE) ** 0.1903) - - while True: temperature, pressure, humidity = sensor.read() - alt = altitude_m(pressure) + alt = sensor.altitude(pressure_hpa=pressure) print( "T: {:.1f} C P: {:.1f} hPa H: {:.1f} %RH Alt: {:.0f} m".format( diff --git a/tests/scenarios/bme280.yaml b/tests/scenarios/bme280.yaml index b39b2cea..fca42d32 100644 --- a/tests/scenarios/bme280.yaml +++ b/tests/scenarios/bme280.yaml @@ -581,3 +581,52 @@ tests: result = len(forced_writes) == 1 and abs(t - 25.08) < 0.1 expect_true: true mode: [mock] + + # ----- Altitude ----- + + - name: "Default sea_level_pressure_hpa is 1013.25" + action: script + script: | + result = dev.sea_level_pressure_hpa == 1013.25 + expect_true: true + mode: [mock] + + - name: "altitude() returns plausible value with default reference" + action: script + script: | + alt = dev.altitude() + # Mock pressure ~1009.21 hPa → altitude ~33.7 m at 1013.25 hPa reference + result = abs(alt - 33.7) < 1.0 + expect_true: true + mode: [mock] + + - name: "altitude() uses custom sea_level_pressure_hpa" + action: script + script: | + dev.sea_level_pressure_hpa = 1020.0 + alt = dev.altitude() + # Mock pressure ~1009.21 hPa → altitude ~89.6 m at 1020.0 hPa reference + result = abs(alt - 89.6) < 1.0 + dev.sea_level_pressure_hpa = 1013.25 + expect_true: true + mode: [mock] + + - name: "altitude() returns zero when pressure equals reference" + action: script + script: | + # Set reference to match actual pressure so altitude should be ~0 + dev.sea_level_pressure_hpa = dev.pressure_hpa() + alt = dev.altitude() + result = abs(alt) < 0.5 + dev.sea_level_pressure_hpa = 1013.25 + expect_true: true + mode: [mock] + + - name: "altitude() accepts pressure_hpa parameter" + action: script + script: | + # Pass pressure directly to avoid redundant I2C read + alt = dev.altitude(pressure_hpa=1009.21) + result = abs(alt - 33.7) < 1.0 + expect_true: true + mode: [mock]