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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ This repository contains all the drivers for the main components of the [STeaMi]
| ISM330DL | [`ism330dl`](lib/ism330dl/README.md) | `0x6B` | 6-axis IMU (accel + gyro) |
| LIS2MDL | [`lis2mdl`](lib/lis2mdl/README.md) | `0x1E` | 3-axis magnetometer |
| IM34DT05 | `im34dt05` *(not yet implemented)* | — (PDM) | Digital microphone |
| BME280 | `bme280` *(not yet implemented)* | `0x76` | Pressure + humidity + temperature |
| BME280 | [`bme280`](lib/bme280/README.md) | `0x76` | Pressure + humidity + temperature |
| GC9A01 | `gc9a01` *(not yet implemented)* | — (SPI) | Round color LCD display |
| STeaMi Config | [`steami_config`](lib/steami_config/README.md) | — | Persistent board configuration |

Expand Down
320 changes: 320 additions & 0 deletions lib/bme280/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,320 @@
# BME280 MicroPython Driver

MicroPython driver for the **Bosch BME280** combined pressure, humidity, and temperature sensor.

This driver provides a simple API to read **pressure**, **humidity**, and **temperature** over **I2C**.

The BME280 is a high-precision environmental sensor suitable for applications such as:

* weather monitoring
* indoor air quality
* altimetry support
* environmental sensing

---

## Features

* I2C communication
* device identification
* pressure measurement (hPa)
* temperature measurement (C)
* relative humidity measurement (%RH)
* one-shot acquisition (forced mode)
* continuous measurement mode (normal mode)
* configurable oversampling (temperature, pressure, humidity)
* configurable IIR filter
* configurable standby time
* data-ready status helpers
* sleep mode (power off)
* soft reset and full reset with recalibration

---

## Sensor Overview

| Feature | Value |
| ---------------------- | ----------------------- |
| Pressure range | 300 hPa - 1100 hPa |
| Pressure resolution | 0.18 Pa (20-bit ADC) |
| Temperature range | -40 C to +85 C |
| Temperature resolution | 0.01 C |
| Humidity range | 0 - 100 %RH |
| Humidity resolution | 0.008 %RH (16-bit ADC) |
| Interface | I2C / SPI |
| Chip ID | 0x60 |

Comment on lines +36 to +46
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Markdown table syntax here uses double leading pipes (|| ...). GitHub-flavored Markdown tables require a single leading pipe per row, so this section will not render as intended. Convert these rows to standard | col | col | table formatting.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked: the tables use standard single-pipe Markdown syntax. No double-pipe issue in the actual source.

---

## I2C Address

The sensor can use two I2C addresses depending on the **SDO pin**:

| SDO | Address |
| ------ | ------- |
| GND | `0x76` |
| VDDIO | `0x77` |

Comment on lines +53 to +57
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This I2C address table also uses || at the start of rows, which breaks standard Markdown table rendering. Use the usual single-pipe table row format so it displays correctly.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked: the tables use standard single-pipe Markdown syntax. No double-pipe issue in the actual source.

The default address used by the driver is **0x76**.

---

## Basic Usage

```python
from machine import I2C
from time import sleep
from bme280 import BME280

i2c = I2C(1)

sensor = BME280(i2c)

while True:
temperature, pressure, humidity = sensor.read_one_shot()

print("T:", temperature, "C")
print("P:", pressure, "hPa")
print("H:", humidity, "%RH")
print()

sleep(1)
Comment on lines +69 to +81
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The “Basic Usage” snippet uses sensor.read() in a loop, but the driver defaults to sleep mode and read() does not trigger a measurement. Update the docs to either show set_continuous(...) before the loop or use read_one_shot() so the example produces fresh readings on real hardware.

Suggested change
i2c = I2C(1)
sensor = BME280(i2c)
while True:
temperature, pressure, humidity = sensor.read()
print("T:", temperature, "C")
print("P:", pressure, "hPa")
print("H:", humidity, "%RH")
print()
sleep(1)
i2c = I2C(1)
sensor = BME280(i2c)
sensor.set_continuous() # start continuous measurements
while True:
temperature, pressure, humidity = sensor.read()
print("T:", temperature, "C")
print("P:", pressure, "hPa")
print("H:", humidity, "%RH")
print()
sleep(1)

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in b660b06: Basic Usage snippet now uses read_one_shot().

```

---

## API Reference

## Initialization

```python
sensor = BME280(i2c)
```

Optional custom address:

```python
sensor = BME280(i2c, address=0x77)
```

The constructor verifies the chip ID, waits for NVM calibration data to be ready, reads factory trimming parameters, and applies a default configuration (1x oversampling, sleep mode).

---

## Measurements

### Read all channels

```python
temperature, pressure, humidity = sensor.read()
```

Returns a tuple of `(temperature_c, pressure_hpa, humidity_rh)`.

---

### Temperature

```python
sensor.temperature()
```

Returns the temperature in **degrees Celsius**.

---

### Pressure

```python
sensor.pressure_hpa()
```

Returns the pressure in **hPa**.

---

### Humidity

```python
sensor.humidity()
```

Returns the relative humidity in **%RH**.

---

### One-shot measurement

```python
temperature, pressure, humidity = sensor.read_one_shot()
```

Triggers a forced measurement, waits for completion, and returns all three channels.

---

### Trigger forced measurement

```python
sensor.trigger_one_shot()
```

Triggers a single forced measurement. Poll `data_ready()` for completion, then read values with `temperature()`, `pressure_hpa()`, `humidity()`, or `read()`.

---

## Data-Ready Status

```python
sensor.data_ready() # True when all channels are ready
sensor.temperature_ready() # True when temperature is ready
sensor.pressure_ready() # True when pressure is ready
sensor.humidity_ready() # True when humidity is ready
```

---

## Configuration

### Oversampling

```python
from bme280.const import OSRS_X2, OSRS_X4, OSRS_X16

sensor.set_oversampling(temperature=OSRS_X2, pressure=OSRS_X16, humidity=OSRS_X4)
```

Available constants: `OSRS_SKIP`, `OSRS_X1`, `OSRS_X2`, `OSRS_X4`, `OSRS_X8`, `OSRS_X16`.

Pass `None` to keep the current setting for a channel.

---

### IIR Filter

```python
from bme280.const import FILTER_4

sensor.set_iir_filter(FILTER_4)
```

Available constants: `FILTER_OFF`, `FILTER_2`, `FILTER_4`, `FILTER_8`, `FILTER_16`.

---

### Standby Time

```python
from bme280.const import STANDBY_500_MS

sensor.set_standby(STANDBY_500_MS)
```

Available constants: `STANDBY_0_5_MS`, `STANDBY_62_5_MS`, `STANDBY_125_MS`, `STANDBY_250_MS`, `STANDBY_500_MS`, `STANDBY_1000_MS`.

---

## Modes

### Sleep mode

```python
sensor.power_off() # enter sleep mode, stop measurements
sensor.power_on() # enter normal mode, continuous measurements
```

---

### Continuous mode

```python
from bme280.const import STANDBY_125_MS

sensor.set_continuous(standby=STANDBY_125_MS)
```

Enters normal mode with the specified standby time between measurements. If `standby` is `None`, the current standby setting is kept.

---

## Device Management

### Device ID

```python
sensor.device_id() # returns 0x60
```

---

### Soft Reset

```python
sensor.soft_reset()
```

Sends the reset command and waits for NVM reload.

---

### Full Reset

```python
sensor.reset()
```

Performs a soft reset, re-reads calibration data, and re-applies default configuration.

---

## Examples

| Example | Description |
| -------------------- | -------------------------------------------------- |
| `basic_reader.py` | Read temperature, pressure, and humidity |
| `weather_station.py` | Continuous logging with altitude computation |

Comment on lines +272 to +276
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The examples table at the end also starts rows with ||, which won’t render as a proper Markdown table. Switch to standard single-pipe table syntax.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked: the tables use standard single-pipe syntax. No double-pipe issue found in the actual file.

---

## Comparison with other MicroPython BME280 drivers

| Feature | **STeaMi** | **robert-hh** | **Adafruit** | **neliogodoi** | **Pimoroni** | **RandomNerd** |
|---|---|---|---|---|---|---|
| I2C | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| SPI | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
| Sleep mode | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
| Forced mode | ✅ | ✅ | ✅ | ⚠️ Implicit | ❌ | ❌ |
| Normal mode | ✅ | ✅ | ✅ | ❌ | ⚠️ Fixed | ❌ |
| Oversampling (per channel) | ✅ | ✅ | ✅ | ✅ | ⚠️ Fixed x16 | ⚠️ Constants only |
| IIR filter | ✅ | ❌ | ✅ | ✅ | ⚠️ Fixed x16 | ❌ |
| Standby time | ✅ | ❌ | ✅ | ❌ | ⚠️ Fixed 500ms | ❌ |
| Altitude | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ |
| Sea-level pressure | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ |
| Dew point | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
| Soft reset | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ |
| Full reset + recalibration | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| power_off / power_on | ✅ | ❌ | ⚠️ Via mode | ❌ | ❌ | ❌ |
| data_ready | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| read_one_shot | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| set_continuous | ✅ | ❌ | ⚠️ Via mode | ❌ | ❌ | ❌ |
| Integer compensation | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ |
| Measurement time estimate | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
| Multi-unit temperature | ❌ | ❌ | ❌ | ✅ C/F/K | ❌ | ❌ |
| BMP280 compatibility | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| Dedicated exceptions | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Mock test suite | ✅ (39) | ❌ | ❌ | ❌ | ❌ | ❌ |
| Hardware test suite | ✅ (6) | ❌ | ❌ | ❌ | ❌ | ❌ |

Reference implementations:

* [robert-hh/BME280](https://github.com/robert-hh/BME280) — integer compensation, altitude, dew point
* [Adafruit CircuitPython BME280](https://github.com/adafruit/Adafruit_CircuitPython_BME280) — I2C + SPI, basic/advanced split
* [neliogodoi/MicroPython-BME280](https://github.com/neliogodoi/MicroPython-BME280) — configurable oversampling and IIR
* [Pimoroni envirobit](https://github.com/pimoroni/micropython-envirobit) — Micro:bit driver, BMP280 alias
* [RandomNerdTutorials](https://randomnerdtutorials.com/micropython-bme280-esp32-esp8266/) — ESP32/ESP8266 tutorial

---

## References

* [BME280 Datasheet](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf)
24 changes: 24 additions & 0 deletions lib/bme280/examples/basic_reader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from time import sleep

from bme280 import BME280
from machine import I2C

# Update the I2C bus number and pins to match your board
i2c = I2C(1)

# Create the sensor object
sensor = BME280(i2c)

print("BME280 found")
print("Device ID:", hex(sensor.device_id()))

for _ in range(10):
temperature, pressure, humidity = sensor.read_one_shot()

print(
"T: {:.1f} C P: {:.1f} hPa H: {:.1f} %RH".format(
temperature, pressure, humidity
)
)

sleep(1)
Loading
Loading