/******************************************************* Author: Manfred Langemann mailto: Manfred.Langemann@t-online.de Begin of project: 09.08.2026 Latest version generated: 14.08.2026 Filename: BME280.c Description: Functions for performing BME280 measurements: - Air Pressure in hPa - Temperature in deg C * 10 - Humidity in %RH It uses the AZ-Delivery GY-BME280 board, bought Aug. 2026 with an I2C interface and a power supply of 5 Volt. This code is adapted from the Arduino BME280 library, written by Tyler Glenn in 2016, updated at Oct 07 2017. See https://github.com/finitespace/BME280 The calibration algorithm is based on the Bosch documentation BST-BME280-DS001-23 Revision_1.23_012022 on page 25 To do single measurements, the code uses the "forced mode". The C code has been implemented on an ATmega328 with a 2 MHz crystal, using the Atmel-Studio 7 To interface the I2C (TWI), use the code, as provided in files TWI_Master.c/h Delay functions are provided in files Delay.c/h RS232 functionality is provided in files RS232.c/h (9600 baud, 8N1) General defines are provided in file General.h The main() function for testing this software is provided in file main.c Use the Data-Visualizer Tool of the Atmel-Studio 7 to visualize the results of the printf() statements *******************************************************/ #include #include #include #include "General.h" #include "TWI_Master.h" #include "Delay.h" #include "BME280.h" /* ** Private defines. */ #define BME280_I2C_ADDRESS 0x76 // The I2C address of the BME280 sensor, SDO Pin = GND //#define BME280_I2C_ADDRESS 0x77 // The I2C address of the BME280 sensor, SDO Pin = +3.3V #define BME280_CTRL_HUM_ADDR 0xF2 // the humidity control register begin #define BME280_CTRL_MEAS_ADDR 0xF4 // the data measurement control register begin #define BME280_CONFIG_ADDR 0xF5 // the configuration control register begin #define BME280_PRESS_ADDR 0xF7 // the pressure data register begin #define BME280_TEMP_ADDR 0xFA // the temperature data register begin #define BME280_HUM_ADDR 0xFD // the humidity data register begin #define BME280_STATUS_ADDR 0xF3 // the status register #define BME280_TEMP_TRIM_ADDR 0x88 // the temperature trim register begin #define BME280_PRESS_TRIM_ADDR 0x8E // the pressure trim register begin #define BME280_HUM_TRIM_ADDR1 0xA1 // the humidity 1 trim register begin #define BME280_HUM_TRIM_ADDR2 0xE1 // the humidity 2 trim register begin #define BME280_ID_ADDR 0xD0 // the chip ID register #define BME280_SOFT_RESET_ADDR 0xE0 // the software reset register #define BME280_SOFT_RESET_VALUE 0xB6 // the software reset value to write #define BME280_TEMP_TRIM_LENGTH 6 // the number of temperature trim registers / bytes #define BME280_PRESS_TRIM_LENGTH 18 // the number of pressure trim registers / bytes #define BME280_HUM_TRIM_ADDR1_LENGTH 1 // the number of humidity 1 trim registers / bytes #define BME280_HUM_TRIM_ADDR2_LENGTH 7 // the number of humidity 2 trim registers / bytes #define BME280_TRIM_LENGTH 32 // total number of trim registers / bytes #define BME280_SENSOR_DATA_LENGTH 8 // the number of sensor registers / bytes /* ** Private enums. */ enum BME280_OSR // (Over-Sampling-Rate) { BME280_OSR_Off = 0, BME280_OSR_X1 = 1, BME280_OSR_X2 = 2, BME280_OSR_X4 = 3, BME280_OSR_X8 = 4, BME280_OSR_X16 = 5 }; enum BME280_Mode { BME280_Mode_Sleep = 0, BME280_Mode_Forced = 1, BME280_Mode_Normal = 3 }; enum BME280_StandbyTime { BME280_StandbyTime_500us = 0, BME280_StandbyTime_62500us = 1, BME280_StandbyTime_125ms = 2, BME280_StandbyTime_250ms = 3, BME280_StandbyTime_50ms = 4, BME280_StandbyTime_1000ms = 5, BME280_StandbyTime_10ms = 6, BME280_StandbyTime_20ms = 7 }; enum BME280_Filter { BME280_Filter_Off = 0, BME280_Filter_2 = 1, BME280_Filter_4 = 2, BME280_Filter_8 = 3, BME280_Filter_16 = 4 }; enum BME280_SpiEnable { BME280_SpiEnable_False = 0, BME280_SpiEnable_True = 1 }; enum BME_ChipModel { BME280_ChipModel_UNKNOWN = 0, BME280_ChipModel_BMP280 = 0x58, BME280_ChipModel_BME280 = 0x60 }; /* ** Private functions. */ uint8_t BME280_SetControlRegisters (void); uint8_t BME280_ReadTrimRegisters (void); uint8_t BME280_ReadMeasurementData (int32_t *data); void BME280_CalculateTemperature (int32_t iRawTemp, int32_t *iTempFine, int32_t *iTempDeg100); uint8_t BME280_ReadRegisters (uint8_t iStartRegister, uint8_t iNumRegisters, uint8_t *iBuffer); uint8_t BME280_WriteRegister (uint8_t iRegister, uint8_t iValue); uint8_t BME280_ReadChipID (void); /* ** Private global variables. */ volatile uint8_t BME280_TrimDataBuffer[BME280_TRIM_LENGTH]; // the calibration/trim data read once from the BME280 /******************************************************* Public Function: BME280_Init Purpose: Init the BME280 functionality Input parameters: void Return value: uint8_t TRUE: Init is OK FALSE: Init is NOT OK *******************************************************/ uint8_t BME280_Init (void) { uint8_t iStatus; /* ** Check on correct chip ID. */ if (!BME280_ReadChipID ()) return FALSE; printf ("Chip-ID OK\n"); /* ** Reset the device using soft-reset, this makes sure the IIR is off, etc. ** Then wait 10 ms for chip to wake up. */ if (!BME280_WriteRegister (BME280_SOFT_RESET_ADDR, BME280_SOFT_RESET_VALUE)) return FALSE; Delay_ms (10); /* ** Wait as long as the chip is copying calibration data to image registers. ** For this purpose we read the status register. ** If still copying, wait for additional 10 ms. */ l_ReadStatus: BME280_ReadRegisters (BME280_STATUS_ADDR, 1, &iStatus); if ((iStatus & 0x01) != 0) { Delay_ms (10); goto l_ReadStatus; } /* ** Read the Calibration/Trim registers. ** Will be stored in global array BME280_TrimDataBuffer[] for later use. */ if (!BME280_ReadTrimRegisters ()) return FALSE; /* ** Set the control registers: ** - Oversampling rate to 1 for all measurement data (P, T, H) ** - Forced mode enabled ** - Standby Time to 1000 ms ** - Filter to Off ** - SPI mode to FALSE */ if (!BME280_SetControlRegisters ()) return FALSE; return TRUE; } /******************************************************* Public Function: BME280_GetPressure Purpose: Get the pressure value from the sensor Note: The measured pressure value P has to be corrected to sea level (NN) by: P(NN) = P + Elevation in m / 7.6 Example: Measured pressure P = 1000 hPa Elevation = 440 m P(NN) = 1000 + 440 / 7.6 = 1058 hPa Code based on calibration algorithm provided by Bosch documentation: BST-BME280-DS001-23 Revision_1.23_012022 on page 25 Output parameters: uint16_t *iPressure Pointer, where to return the pressure value in hPa Return value: uint8_t TRUE: returned value in iPressure is valid FALSE: returned value in iPressure is NOT valid *******************************************************/ uint8_t BME280_GetPressure (uint16_t *iPressure) { int64_t var1, var2, pressure; // intermediate computation variables uint16_t dig_P1; // dig_P1 - dig_P9: sorted trim data as read from the sensor int16_t dig_P2; // For data type definitions see document on page 24 in table 16 int16_t dig_P3; int16_t dig_P4; int16_t dig_P5; int16_t dig_P6; int16_t dig_P7; int16_t dig_P8; int16_t dig_P9; int32_t data[8]; // array to read the measurement data from the sensor int32_t FineTemp; // intermediate computed fine temperature value int32_t DegC100Temp; // temperature value in deg C * 100 int32_t RawTemp; // raw temperature, as read from the sensor uint32_t RawPressure; // raw pressure, as read from the sensor /* ** Set return value to zero in case of below errors. */ *iPressure = 0; /* ** Sort the compensation/trim data. */ dig_P1 = (BME280_TrimDataBuffer[7] << 8) | BME280_TrimDataBuffer[6]; dig_P2 = (BME280_TrimDataBuffer[9] << 8) | BME280_TrimDataBuffer[8]; dig_P3 = (BME280_TrimDataBuffer[11] << 8) | BME280_TrimDataBuffer[10]; dig_P4 = (BME280_TrimDataBuffer[13] << 8) | BME280_TrimDataBuffer[12]; dig_P5 = (BME280_TrimDataBuffer[15] << 8) | BME280_TrimDataBuffer[14]; dig_P6 = (BME280_TrimDataBuffer[17] << 8) | BME280_TrimDataBuffer[16]; dig_P7 = (BME280_TrimDataBuffer[19] << 8) | BME280_TrimDataBuffer[18]; dig_P8 = (BME280_TrimDataBuffer[21] << 8) | BME280_TrimDataBuffer[20]; dig_P9 = (BME280_TrimDataBuffer[23] << 8) | BME280_TrimDataBuffer[22]; //printf ("digP1-P9 = %i %i %i %i %i %i %i %i %i\n", dig_P1, dig_P2, dig_P3, dig_P4, dig_P5, dig_P6, dig_P7, dig_P8, dig_P9); /* ** Read the raw measurement data from the registers and derive the: ** - raw temperature and ** - raw pressure ** Based on the raw temperature, calculate the corrected fine temperature, ** which is needed to compute the pressure. ** The function also returns the calculated temperature in deg C * 100. */ if (!BME280_ReadMeasurementData (&data[0])) return FALSE; RawTemp = (int32_t)((data[3] << 12) | (data[4] << 4) | (data[5] >> 4)); RawPressure = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4); //printf ("RawPres: %i %i %i\n", (int16_t)data[0], (int16_t)data[1], (int16_t)data[2]); BME280_CalculateTemperature (RawTemp, &FineTemp, &DegC100Temp); /* ** Do the compensation computation as of Bosch documentation page 25. ** Computes the pressure in unsigned 32 bit integer in Q24.8 format (24 integer bit, 8 fractional bits). ** Example: e.g. pressure = 24674867 represents 24674867 / 256 = 96386.2 Pa = 963.862 hPa. */ var1 = (int64_t)FineTemp - 128000; var2 = var1 * var1 * (int64_t)dig_P6; var2 = var2 + ((var1 * (int64_t)dig_P5) << 17); var2 = var2 + (((int64_t)dig_P4) << 35); var1 = ((var1 * var1 * (int64_t)dig_P3) >> 8) + ((var1 * (int64_t)dig_P2) << 12); var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)dig_P1) >> 33; if (var1 == 0) return FALSE; // Don't divide by zero below ! pressure = 1048576 - RawPressure; pressure = (((pressure << 31) - var2) * 3125) / var1; var1 = (((int64_t)dig_P9) * (pressure >> 13) * (pressure >> 13)) >> 25; var2 = (((int64_t)dig_P8) * pressure) >> 19; pressure = ((pressure + var1 + var2) >> 8) + (((int64_t)dig_P7) << 4); /* ** 1. Convert 'pressure' to dimension Pa. ** 2. Return the pressure value in dimension hPa. */ pressure = pressure / 256; *iPressure = (uint16_t)(pressure / 100); return TRUE; } /******************************************************* Public Function: BME280_GetTemperature Purpose: Get the temperature value from the sensor It calculates the temperature in deg C * 10 Example: Output value iTemp e.g. -132 equals to -13.2 deg C Code based on calibration algorithm provided by Bosch documentation: BST-BME280-DS001-23 Revision_1.23_012022 on page 25 Output parameters: int16_t *iTemp Pointer, where to return the temperature in deg C * 10 Return value: uint8_t TRUE: returned value in iTemp is valid FALSE: returned value in iTemp is NOT valid *******************************************************/ uint8_t BME280_GetTemperature (int16_t *iTemp) { int32_t data[8]; // array to read the measurement data from the sensor int32_t FineTemp; // intermediate computed fine temperature value int32_t DegC100Temp; // temperature value in deg C * 100 int32_t RawTemp; // raw temperature, as read from the sensor /* ** Read the raw measurement data from the registers and derive the raw temperature. ** Based on the raw temperature, calculate the corrected fine temperature. ** The function also returns the calculated temperature in deg C * 100. */ if (!BME280_ReadMeasurementData (&data[0])) return FALSE; RawTemp = (int32_t)((data[3] << 12) | (data[4] << 4) | (data[5] >> 4)); //printf ("RawTemp: %i %i %i\n", (int16_t)data[3], (int16_t)data[4], (int16_t)data[5]); BME280_CalculateTemperature (RawTemp, &FineTemp, &DegC100Temp); /* ** Convert deg C * 100 --> deg C * 10 */ *iTemp = (int16_t)(DegC100Temp / 10); return TRUE; } /******************************************************* Public Function: BME280_GetHumidity Purpose: Get the humidity value from the sensor It calculates in var1 the humidity in %RH as unsigned 32 bit integer n Q22.10 format (22 integer and 10 fractional bits) Example: Output value of 47445 represents 47445 / 1024 = 46.333 %RH Returned value in iHumi is in dimension %RH, element of {0, ... ,100} Values < 0% will be set to 0% Values > 100% will be set to 100% Code based on calibration algorithm provided by Bosch documentation: BST-BME280-DS001-23 Revision_1.23_012022 on page 25 Output parameters: uint8_t *iHumi Pointer, where to return the corrected humidity value in dimension %RH as element of {0, ... ,100} Return value: uint8_t TRUE: returned value in iHumi is valid FALSE: returned value in iHumi is NOT valid *******************************************************/ uint8_t BME280_GetHumidity (uint8_t *iHumi) { int32_t var1; int32_t data[8]; // array to read the measurement data from the sensor uint8_t dig_H1; // dig_H1 - dig_H6: sorted trim data as read from the sensor int16_t dig_H2; // For data type definitions see document on page 24 in table 16 uint8_t dig_H3; int16_t dig_H4; int16_t dig_H5; int8_t dig_H6; int32_t FineTemp; // intermediate computed fine temperature value int32_t DegC100Temp; // temperature value in deg C * 100 int32_t RawTemp; // raw temperature, as read from the sensor uint32_t RawHumi; // raw humidity, as read from the sensor /* ** Sort the compensation/trim data. */ dig_H1 = BME280_TrimDataBuffer[24]; dig_H2 = (BME280_TrimDataBuffer[26] << 8) | BME280_TrimDataBuffer[25]; dig_H3 = BME280_TrimDataBuffer[27]; dig_H4 = (BME280_TrimDataBuffer[28] << 4) | (0x0F & BME280_TrimDataBuffer[29]); dig_H5 = (BME280_TrimDataBuffer[30] << 4) | ((BME280_TrimDataBuffer[29] >> 4) & 0x0F); dig_H6 = BME280_TrimDataBuffer[31]; //printf ("digH1-H6 = %i %i %i %i %i %i\n", dig_H1, dig_H2, dig_H3, dig_H4, dig_H5, dig_H6); /* ** Read the raw measurement data from the registers and derive the: ** - raw temperature and ** - raw humidity ** Based on the raw temperature, calculate the corrected fine temperature, ** which is needed to compute the humidity. ** The function also returns the calculated temperature in deg C * 100. */ if (!BME280_ReadMeasurementData (&data[0])) return FALSE; RawTemp = (int32_t)((data[3] << 12) | (data[4] << 4) | (data[5] >> 4)); RawHumi = (data[6] << 8) | data[7]; //printf ("RawHumi: %i %i\n", (int16_t)data[6], (int16_t)data[7]); BME280_CalculateTemperature (RawTemp, &FineTemp, &DegC100Temp); /* ** Calculate the humidity value in %RH. */ var1 = (FineTemp - ((int32_t)76800)); var1 = (((((RawHumi << 14) - (((int32_t)dig_H4) << 20) - (((int32_t)dig_H5) * var1)) + ((int32_t)16384)) >> 15) * (((((((var1 * ((int32_t)dig_H6)) >> 10) * (((var1 * ((int32_t)dig_H3)) >> 11) + ((int32_t)32768))) >> 10) + ((int32_t)2097152)) * ((int32_t)dig_H2) + 8192) >> 14)); var1 = (var1 - (((((var1 >> 15) * (var1 >> 15)) >> 7) * ((int32_t)dig_H1)) >> 4)); var1 = (var1 < 0 ? 0 : var1); var1 = (var1 > 419430400 ? 419430400 : var1); *iHumi = (uint8_t)(((uint32_t)(var1 >> 12))/1024); return TRUE; } /******************************************************* Private Function: BME280_CalculateTemperature Purpose: Calculate the temperature based on the raw data read from the sensor Code based on calibration algorithm provided by Bosch documentation: BST-BME280-DS001-23 Revision_1.23_012022 on page 25 Input / Output parameters: int32_t iRawTemp The raw temperature, as read from registers int32_t *iFineTemp Pointer, where to return the corrected fine temperature, need for pressure calculation int32_t *iDeg100Temp Pointer, where to return the corrected temperature value in dimension deg C * 100 Return value: void *******************************************************/ void BME280_CalculateTemperature (int32_t iRawTemp, int32_t *iFineTemp, int32_t *iDeg100Temp) { int32_t var1, var2; uint16_t dig_T1; // dig_!1 - dig_T3: sorted trim data as read from the sensor int16_t dig_T2; // For data type definitions see document on page 24 in table 16 int16_t dig_T3; /* ** Sort the compensation/trim data. */ dig_T1 = (BME280_TrimDataBuffer[1] << 8) | BME280_TrimDataBuffer[0]; dig_T2 = (BME280_TrimDataBuffer[3] << 8) | BME280_TrimDataBuffer[2]; dig_T3 = (BME280_TrimDataBuffer[5] << 8) | BME280_TrimDataBuffer[4]; //printf ("digT1-T3 = %i %i %i\n", dig_T1, dig_T2, dig_T3); /* ** Do the compensation computation as of Bosch documentation page 25. ** Calculate the temperature value in degree C * 100. ** Resolution is 0.01 deg C. ** Output value iDeg100Temp e.g. -3125 equals to -31.25 deg C. */ var1 = ((((iRawTemp >> 3) - ((int32_t)dig_T1 << 1))) * ((int32_t)dig_T2)) >> 11; var2 = (((((iRawTemp >> 4) - ((int32_t)dig_T1)) * ((iRawTemp >> 4) - ((int32_t)dig_T1))) >> 12) * ((int32_t)dig_T3)) >> 14; *iFineTemp = var1 + var2; *iDeg100Temp = (*iFineTemp * 5 + 128) >> 8; return; } /******************************************************* Private Function: BME280_ReadMeasurementData Purpose: Read from the BME280 sensor the measurement data via I2C Output parameters: uint8_t *data Pointer, where to return the read measurement data The array data[] holds the following information: Raw-Pressure data[0] = press_msb data[1] = press_lsb data[2] = press_xlsb (xlsb = extra least significant bits) To be concatenated to an uint32_t value by (press_msb << 12) | (press_lsb << 4) | (press_xlsb >> 4) Raw-Temperature data[3] = temp_msb data[4] = temp_lsb data[5] = temp_xlsb (xlsb = extra least significant bits) To be concatenated to an uint32_t value by (temp_msb << 12) | (temp_lsb << 4) | (temp_xlsb >> 4) Raw-Humidity data[6] = humi_msb data[7] = humi_lsb To be concatenated to an uint32_t value by (humi_msb << 8) | humi_lsb Return value: uint8_t TRUE: Read Measurement Data is OK FALSE: Read Measurement Data is NOT OK *******************************************************/ uint8_t BME280_ReadMeasurementData (int32_t *data) { uint8_t i; uint8_t iStatus; uint8_t buffer[BME280_SENSOR_DATA_LENGTH]; /* ** For forced mode we need to write the mode to BME280 register before reading the measurement data. */ if (!BME280_SetControlRegisters ()) return FALSE; /* ** Wait as long as the conversion of the measurement is running. ** For this purpose we read the status register. ** If still running, wait for additional 10 ms. */ l_ReadStatus: BME280_ReadRegisters (BME280_STATUS_ADDR, 1, &iStatus); if ((iStatus & 0x08) != 0) { Delay_ms (10); goto l_ReadStatus; } /* ** Read the measurement data beginning at the pressure msb register and then read 8 bytes. */ if (!BME280_ReadRegisters (BME280_PRESS_ADDR, BME280_SENSOR_DATA_LENGTH, &buffer[0])) return FALSE; /* ** Shuffle the read data to the output array data[]. */ for (i=0; i