Reusable, STM32/HAL-friendly I2C driver for Bosch BME280/BMP280 sensors.
Project focus: this repository exists mainly to provide the
bme280_sensor_driver.[ch]files as a small, self-contained driver that you can drop into any STM32 (or similar HAL-based) project.
The rest of the code (startup, clocks, LCD, etc.) is just a reference application around the driver.
This project provides a lightweight C driver for the Bosch BME280/BMP280 environmental sensors:
- Written for STM32 using the STM32 HAL I2C API
- Designed to be portable to other MCUs and I2C abstractions
- Minimal dependencies beyond
<stdint.h>,<stdbool.h>, and HAL I2C - Clear separation between:
- Low-level I2C register access
- High-level configuration and data conversion
The reusable artifact is the Core/Inc/bme280_sensor_driver.h and Core/Src/bme280_sensor_driver.c pair.
Key capabilities of bme280_sensor_driver.[ch]:
-
BME280 / BMP280 support
- Supports both humidity-capable BME280 and pressure/temperature-only BMP280
- Internally tracks if the device is BME280 vs BMP280
-
Flexible oversampling / filter configuration
- Temperature, pressure, and humidity oversampling settings
- IIR filter coefficient configuration
- Standby time configuration for normal mode
-
Handle-based configuration (
BME280_Handle)- Stores:
- HAL I2C handle reference
- Sensor I2C address
- Detected chip ID and type
- Calibration data loaded from the sensor
- Runtime configuration (mode, oversampling, filter, standby)
- Stores:
-
Typed sensor data struct (
BME280_Data)- Temperature (°C)
- Pressure (hPa)
- Humidity (%RH, 0 if BMP280)
- Altitude (m, calculated)
-
Altitude calculation helper
- Convenience helper using the standard barometric formula
- Takes sea-level reference pressure as parameter
-
STM32 HAL I2C dependency only
- Uses STM32 HAL types and functions (
I2C_HandleTypeDef,HAL_StatusTypeDef, etc.) - Easy to adapt to another I2C abstraction by replacing a small number of calls
- Uses STM32 HAL types and functions (
The goal is to make integration a simple copy-and-wire task.
At minimum, copy these into your project:
Core/Inc/bme280_sensor_driver.h→ yourInc/orinclude/folderCore/Src/bme280_sensor_driver.c→ yourSrc/orsrc/folder
Make sure your compiler include paths let you do:
#include "bme280_sensor_driver.h"Your project must already provide:
- STM32 HAL headers and I2C support (e.g.
stm32f0xx_hal.h,stm32f0xx_hal_i2c.h) - An initialized
I2C_HandleTypeDef(e.g.hi2c1) - Correct BME280/BMP280 I2C pins, alternate functions, and clocks (configured via CubeMX or manually)
Typical pattern in your application code (e.g. main.c or a sensor module):
- Declare a global or static handle:
BME280_Handle hbme;
- Call the init function once after
hi2cXis initialized:extern I2C_HandleTypeDef hi2c1; if (BME280_Init(&hbme, &hi2c1) != HAL_OK) { // handle error (wrong chip ID, I2C problem, etc.) }
The init routine:
- Stores the I2C handle and sensor address in
hbme - Reads and checks the chip ID (
BME280_CHIP_ID/BMP280_CHIP_ID) - Loads the sensor calibration data into
hbme.calib
Use the BME280_Config structure and BME280_SetConfig to configure the sensor:
BME280_Config cfg = {
.osrs_t = BME280_OVERSAMPLING_1X,
.osrs_p = BME280_OVERSAMPLING_1X,
.osrs_h = BME280_OVERSAMPLING_1X,
.mode = BME280_MODE_NORMAL,
.filter = BME280_FILTER_OFF,
.standby = BME280_STANDBY_1000MS,
};
BME280_SetConfig(&hbme, &cfg);Adjust oversampling, filter and standby based on your noise vs. power trade-off.
In your main loop or RTOS task:
BME280_Data data;
for (;;) {
if (BME280_ReadSensorData(&hbme, &data) == HAL_OK) {
float altitude = BME280_CalculateAltitude(data.pressure, 1013.25f); // example sea-level pressure
data.altitude = altitude;
// use data.temperature, data.pressure, data.humidity, data.altitude
}
HAL_Delay(1000); // or your scheduler delay
}If you use forced mode, you may need to:
- Trigger a new measurement via configuration
- Wait for conversion (poll
BME280_IsMeasuringor delay) - Then call
BME280_ReadSensorData
High-level functions exposed by the driver:
HAL_StatusTypeDef BME280_Init(BME280_Handle *hbme, I2C_HandleTypeDef *hi2c);
HAL_StatusTypeDef BME280_SetConfig(BME280_Handle *hbme, BME280_Config *config);
HAL_StatusTypeDef BME280_ReadSensorData(BME280_Handle *hbme, BME280_Data *data);
HAL_StatusTypeDef BME280_ReadTemperature(BME280_Handle *hbme, float *temperature);
HAL_StatusTypeDef BME280_ReadPressure(BME280_Handle *hbme, float *pressure);
HAL_StatusTypeDef BME280_ReadHumidity(BME280_Handle *hbme, float *humidity);
float BME280_CalculateAltitude(float pressure, float sea_level_pressure);
HAL_StatusTypeDef BME280_SoftReset(BME280_Handle *hbme);
bool BME280_IsMeasuring(BME280_Handle *hbme);- Initialization:
BME280_Init - Configuration:
BME280_SetConfig - All-in-one read:
BME280_ReadSensorData - Single-quantity reads:
BME280_ReadTemperature,BME280_ReadPressure,BME280_ReadHumidity - Utility:
BME280_CalculateAltitude - Maintenance / status:
BME280_SoftReset,BME280_IsMeasuring
For register definitions, configuration enums, and structs (BME280_CalibData, BME280_Config, BME280_Handle, BME280_Data), see the header file.
- Ensure you have a valid
I2C_HandleTypeDef(e.g.hi2c1,hi2c2) - Configure the I2C pins, clock, and speed appropriate for your board
- As long as the STM32 HAL I2C API is available, you should not need to modify the driver code
If you are not using STM32 HAL:
- Identify the small number of I2C operations used in
bme280_sensor_driver.c(register read/write). - Replace those calls with your platform's I2C API, or wrap your API to look like HAL.
- Replace any HAL-specific delay calls (
HAL_Delay) with your platform delay/sleep. - Keep the header API the same so your application code does not change.
All main functions return HAL_StatusTypeDef (e.g. HAL_OK, HAL_ERROR). You can:
- Map these to your own error system
- Add logging around init and read calls
- Implement retries or backoff for transient I2C errors
- Optional SPI support for BME280/BMP280
- More example code:
- FreeRTOS task using the driver
- Low-power periodic sampling example
- Configurable compile-time options:
- Fixed-point vs floating-point calculations
- Reduced API footprint for size-constrained builds
- Unit tests or hardware-in-the-loop tests