Library for BMP280 and BME280

46K2734

Intro: Library for BMP280 and BME280

Introduction

I did not set out to write this library. It "happened" as a side-effect of a project I started that uses a BMP280. That project is not yet finished, but I think the library is ready to share with others.
Subsequently I had a need to use a BME280, which adds humidity measurement to the pressure and temperature capability of the BMP280. The BME280 is "backward-compatible" with the BMP280 - that is, all the registers and the steps needed to read pressure and temperature from the BME280 are the same as those used for the BMP280. There are additional registers and steps needed to read humidity, applicable to the BME280 only. This raises the question, one library for both, or two separate libraries. The hardware for the two device types is fully interchangeable. Even many of the modules being sold (for example on Ebay and AliExpress) are labelled BME/P280. To find out which type it is, you have to look at the (miniscule) writing on the sensor itself, or test the device ID byte. I decided to go for a single library. It seems to have worked out OK.

Feedback, especially any suggestions for improvements, will be appreciated.

Library features and capabilities

A library is a piece of software that provides an Application Programming Interface (API) for a programmer to exercise the capabilities of the device, without necessarily having to deal with all the fine-grain details. Desirably, the API should be easy for a beginner with simple requirements to get started, while providing for full exploitation of the device capabilities. Desirably the library should follow any specific guidelines from the device manufacturer, as well as general software good practice. I have endeavoured to achieve all of these.
When starting out with the BMP280, I found 3 different libraries for it: Adafruit_BMP280; Seeed_BMP280; and one called BMP280 from the device manufacturer. Neither Adafruit nor Seeed provided extended capabilities, although they worked well and were easy to use for basic applications. I could not figure our how to use the one produced by the device manufacturer (Bosch Sensortec). This may be my deficiency, rather than theirs. However the library was much more complicated than the other two, I could not find any instructions or examples of use (I subsequently found examples were in the file "bmp280_support.c", however these were not particularly helpful to me).

As a result of these factors, I decided to write my own library for the BMP280.

Looking into the library situation for the BME280, I found separate libraries Adafruit_BME280, Seed_BME280 and another one BME280_MOD-1022 written by Embedded Adventures. None of them combined the functions for BMP280 in a library capable of using the BME280. None of them explicitly supported the capability of the devices to store a few bits of data while the device and its controlling microprocessor are sleeping (this capability is evident in the datasheet and supported in the library I have written and described here).

A combined library should have support for all the capabilities of the BME280, but when used with a BMP280 it should not impose any overhead from the unused functions. Benefits of a combined library include fewer library files to manage, easy mix-and-match of different devices in the same project, and simplified changes for maintenance or upgrades which only have to be done in one place rather than two. These are probably all quite minor, even insignificant, but ...

Device capabilities

The BMP280 and BME280 are surface-mount devices about 5mm square and 1 mm high. There are 8 interface pads, including 2 separate power input pads and two Ground pads. They are available on eBay as a module with either 4 or 6 pins brought out.
The 4-pin module has a fixed I2C address and cannot be configured to use the SPI protocol.

The 6-pin module or the bare device can be used with either I2C or SPI protocols. In I2C mode it can have two different addresses, achieved by connecting the SDO pin either to Ground (for base address = 0x76) or to Vdd (for base address +1 = 0x77). In SPI mode it has the usual arrangement of 1 clock, 2 data (one for each direction) and a device select pin (CS).

The library I wrote and describe here only supports I2C. The Adafruit_BMP280 and the BME_MOD-1022 libraries have support for both i2C and SPI.

The library can be downloaded here:

https://github.com/farmerkeith/BMP280-library

STEP 1: Setting Up the Hardware

Before the library can be useful it is necessary to connect a microcontroller to the BMP280 (or to two of them if you wish).

I used a WeMos D1 mini pro, so I will show its connections. Other microcontrollers will be similar, you just need to get the SDA and SCL pins connected correctly.

In the case of the WeMos D1 mini pro, the connections are:

Function         WeMos pin    BMP280 pin     Notes
SDA               D2           SDA
SCL               D1           SCL
Vdd               3V3          Vin          Nominal 3.3V
Ground                         GND
Address control                SDO          Ground or Vdd 
I2C select                     CSB          Vdd (GND selects SPI)

Note that the SDO pin on some of the MP280 modules is labelled SDD, and the Vdd pin may be labelled VCC.
Note: SDA and SCL lines should have pull-up resistors between the line and the Vin pin. Typically a value of 4.7K should be OK. Some BMP280 and BME280 modules have 10K pull-up resistors included in the module (which is not good practice, since putting multiple devices on the I2C bus may load it excessively). However using 2 BME/P280 modules each with a 10K resistor should not be a problem in practice so long as there are not too many other devices on the same bus also with pull-up resistors.

Once you have the hardware connected, you can easily check whether your device is a BMP280 or a BME280 by running the sketch I2CScan_ID which you can find here: https://github.com/farmerkeith/I2CScanner

You can also check whether you have a BMP280 or BME280 by looking at the device itself. I found it necessary to use a digital microscope to do this, but if your eyesight is very good you may be able to do it without any aids. There are two lines of printing on the casing of the device. The key is the first letter on the second line, which in the case of BMP280 devices is a "K" and in the case of BME280 devices is a "U".

STEP 2: APIs Provided by the Library

Including the library in a sketch

The library is included in a sketch in the standard way using the statement

#include "farmerkeith_BMP280.h"

This statement needs to be included in the early part of the sketch prior to the start of the setup() function.

Creating a BME or BMP software object

There are 3 levels for creating the BMP280 software object. The simplest is just

bme280 objectName; <br>or
bmp280 objectName; 

for example, BMP280 bmp0;

This creates a software object with the default address of 0x76 (ie for SDO connected to ground).

The next level for creating a BME280 or BMP280 software object has a parameter of either 0 or 1, as follows:

bme280 objectNameA(0); 
bmp280 objectNameB(1);

The parameter (0 or 1) is added to the I2C base address, so that two BME280 or BMP280 devices can be used on the same I2C bus (including one of each).

The third level for creating a BME or BMP280 software object has two parameters. The first parameter, which is either 0 or 1, is for the address, as for the previous case. The second parameter controls debug printing. If it is set to 1, each transaction with the software object results in Serial.print outputs that enables the programmer to see the details of the transaction. For example:

bmp280 objectNameB(1,1);

If the debug printing parameter is set to 0, the software object reverts to normal behaviour (no printing).

This statement or statements needs to be included after the #include and before the setup() function.

Initialising the BME or BMP software object

Before being used, it is necessary to read the calibration parameters from the device, and to configure it for whatever measurement mode, oversampling, and filter settings are appropriate.

For a simple, general purpose initialisation, the statement is:

objectName.begin();

This version of begin() reads the calibration parameters from the device and sets osrs_t=7 (16 temperature measurements), osrs_p=7 (16 pressure measurements), mode=3 (continuous, Normal), t_sb=0 (0.5 ms sleep between measurement sets), filter=0 (K=1, so no filtering) and spiw_en=0 (SPI disabled, so use I2C). In the case of the BME280, there is an extra parameter osrs_h=7 for 16 humidity measurements.

There is another version of begin() that takes all six (or 7) parameters. The equivalent of the above statement is

objectName.begin(7,7,3,0,0,0); // osrs_t, osrs_p, mode, t_sb, filter, spiw_en
or 
objectName.begin(7,7,3,0,0,0,7); // osrs_t, osrs_p, mode, t_sb, filter, spiw_en, osrs_h

The full list of codes and their meanings is in the BME280 and BMP280 data sheet, and also in the comments in the .cpp file in the library.

Simple temperature and pressure measurement

To get a temperature measurement the simplest way is

double temperature=objectName.readTemperature (); // measure temperature

To get a pressure measurement the simplest way is

double pressure=objectName.readPressure (); // measure pressure

To get a humidity measurement the simplest way is

double humidity=objectName.readHumidity (); // measure humidity (BME280 only)

To get both temperature and pressure the above two statements can be used one after the other, but there is another option, which is:

double temperature;
double pressure=objectName.readPressure (temperature); // measure pressure and temperature

This statement reads the data from the BME280 or BMP280 device only once, and returns both temperature and pressure. This is slightly more efficient use of the I2C bus and ensures that the two readings correspond to the same measurement cycle.

For the BME 280, a combined statement that gets all three values (humidity, temperature and pressure) is:

double temperature, pressure;<br>double humidity=objectName.readHumidity (temperature, pressure); // measure humidity, pressure and temperature 

This statement reads the data from the BMP280 device only once, and returns all three values. This is slightly more efficient use of the I2C bus and ensures that the three readings correspond to the same measurement cycle. Note that the names of the variables can be changed to anything the user likes, but their order is fixed - temperature comes first, and pressure comes second.

These use cases are covered in example sketches provided with the library, being basicTemperature.ino, basicPressure.ino, basicHumidity.ino, basicTemperatureAndPressure.ino and basicHumidityAndTemperatureAndPressure.ino.

More sophisticated temperature and pressure measurement

Although the above series of statements will work without problems, there are a couple of issues:

  1. the device is running continuously, and therefore is consuming power at its maximum level. If the energy is coming from a battery, it may be necessary to reduce this.
  2. due to the power consumed, the device will experience warming, and therefore the measured temperature will be higher than the ambient temperature. I will cover this more in a later step.

A result that uses less power, and gives a temperature that is closer to ambient, can be obtained by using begin() with parameters that put it to sleep (eg mode=0). For example:

objectName.begin(1,1,0,0,0,0[,1]); // osrs_t, osrs_p, mode, t_sb, filter, spiw_en [,osrs_h]

Then, when a measurement is wanted, wake up the device with a configuration command to registers F2 (if required) and F4 that sets the appropriate values of osrs_h, osrs_t and osrs_p, plus mode=1 (single shot mode). For example:

[objectName.updateF2Control(1);]    // osrs_h - never needed for BMP280, 
// and not needed for BME280 if the No. of measurements is not being changed 
// from the value provided in begin().
objectName.updateF4Control(1,1,1); // osrs_t, osrs_p, mode

Having woken up the device, it will start measuring, but the result will not be available for some milliseconds - at least 4 ms, maybe up to 70 ms or more, depending on the number of measurements that have been specified. If the read command is sent immediately, the device will return the values from the previous measurement - which may be acceptable in some applications, but in most cases it is probably better to delay until the new measurement is available.

This delay can be done in several ways.

  1. wait a fixed amount of time to cover the longest expected delay
  2. wait an amount of time calculated from the maximum measurement time per measurement (ie 2.3ms) times the number of measurements, plus overhead, plus a margin.
  3. wait a shorter amount of time calculated as above, but using the nominal measurement time (ie 2 ms) plus overhead, and then start checking the "I am measuring" bit in the status register. When the status bit reads 0 (ie, not measuring), get the temperature and pressure readings.
  4. immediately start checking the status register, and get the temperature and pressure readings when the status bit reads 0,

I will show an example of one way of doing this a bit later.

Configuration register operations

To make all this happen, we need several tools that I have not yet introduced. They are:

byte readRegister(reg) 
void updateRegister(reg, value)

Each of these has several derived commands in the library, which make the software for specific actions a bit simpler.

The example powerSaverPressureAndTemperature.ino uses method No. 3. The line of code that does the repeated checking is

while (bmp0.readRegister(0xF3)>>3); // loop untl F3bit 3 ==0

Note that this sketch is for an ESP8266 microcontroller. I used a WeMos D1 mini pro. The sketch will not work with Atmega microcontrollers, which have different instructions for sleeping. This sketch exercises several other commands, so I will introduce all of them before describing that sketch in more detail.

When the microcontoller is sleeping in parallel with the BMP280 sensor, the configuration of the sensor for the required measurements can be done in the begin() command, using the 6 parameters. However if the microcontroller is not sleeping, but the sensor is, then at the time of measurement the sensor has to be woken up and told its measurement configuration. This can be done directly with

updateRegister(reg, value)

but is a bit easier with the following three commands:

updateF2Control(osrs_h); // BME280 only
updateF4Control(osrs_t, osrs_p, mode); 
updateF5Config(t_sb, filter, spi3W_en);

After the measurement is done, if the mode used is Single shot (Forced mode), then the device will automatically go back to sleep. However, if the measurement set involves multiple measurements using continuous (Normal) mode then the BMP280 will need to be put back to sleep. This can be done with either of the two following commands:

updateF4Control16xSleep();       
updateF4ControlSleep(value);

Both of these set the mode bits to 00 (ie sleep mode). However the first sets the osrs_t and osrs_p to 111 (ie 16 measurements) while the second one stores the low 6 bits from "value" into bits 7:2 of the 0xF4 register.

Similarly the following statement stores the low six bits of "value" into bits 7:2 of the 0xF5 register.

updateF5ConfigSleep(value); 

The use of these latter commands enable storage of 12 bits of information in the BMP280 registers F4 and F5. At least in the case of the ESP8266, when the microcontroller wakes up after a period of sleep, it starts at the beginning of the sketch with no knowledge of its state prior to the sleep command. To store knowledge of its state prior to the sleep command, data can be stored in flash memory, using either the EEPROM functions or by writing a file using SPIFFS. However flash memory has a limitation of the number of write cycles, of the order of 10,000 to 100,000. This means that if the microcontroller is going through a sleep-wake cycle every few seconds, it can exceed the allowable memory write limit in a few months. Storing a few bits of data in the BMP280 has no such limitation.

The data stored in registers F4 and F5 can be recovered when the microcontroller wakes up using the commands

readF4Sleep();
readF5Sleep();

These functions read the corresponding register, shift the contents to remove the 2 LSBs and return the remaining 6 bits. These functions are used in the example sketch powerSaverPressureAndTemperatureESP.ino as follows:

// read value of EventCounter back from bmp0
byte bmp0F4value= bmp0.readF4Sleep(); // 0 to 63
byte bmp0F5value= bmp0.readF5Sleep(); // 0 to 63
eventCounter= bmp0F5value*64+bmp0F4value; // 0 to 4095

These functions read the corresponding register, shift the contents to remove the 2 LSBs and return the remaining 6 bits. These functions are used in the example sketch powerSaverPressureAndTemperature.ino as follows:

// read value of EventCounter back from bmp1
byte bmp1F4value= bmp1.readF4Sleep(); // 0 to 63
byte bmp1F5value= bmp1.readF5Sleep(); // 0 to 63
eventCounter= bmp1F5value*64+bmp1F4value; // 0 to 4095
Raw temperature and pressure functions

The basic readTemperature, readPressure and readHumidity functions have two components. First the raw 20-bit temperature and pressure values are obtained from the BME/P280, or the raw 16-bit humidity value is obtained from the BME280. Then the compensation algorithm is used to generate the output values in degrees Celsius, hPa or %RH.

The library provides separate functions for these components, so that the raw temperature, presssure and humidity data can be obtained, and perhaps manipulated in some way. The algorithm to derive the temperature, pressure and humidity from these raw values is also provided. In the library these algorithms are implemented using double length floating point arithmetic. It works well on the ESP8266 which is a 32-bit processor and uses 64 bits for "double" float variables. Making these functions accessible may be useful for assessing and possibly changing the calculation for other platforms.

These functions are:

readRawPressure (rawTemperature); // reads raw pressure and temperature data from BME/P280<br>readRawHumidity (rawTemperature, rawPressure); // reads raw humidity, temperature and pressure data from BME280
calcTemperature (rawTemperature, t_fine);
calcPressure (rawPressure, t_fine);
calcHumidity (rawHumidity, t_fine)

The "t-fine" argument to these functions is worth a bit of explanation. Both pressure and humidity compensation algorithms include a temperature dependent component which is achieved through the t_fine variable. The calcTemperature function writes a value in t_fine based on the temperature compensation algorithm logic, which is then used as an input in both calcPressure and calcHumidity.

An example of the use of these functions can be found in the example sketch rawPressureAndTemperature.ino, and also in the code for the readHumidity() function in the .cpp file of the library.

Altitude and Sea Level pressure

There is a known relationship between atmospheric pressure and altitude. The weather also influences pressure. When the weather organisations publish atmospheric pressure information, they usually adjust it for altitude and so the "synoptic chart" shows isobars (lines of constant pressure) standardised to mean sea level. So really there are 3 values in this relationship, and knowing two of them enables derivation of the third one. The 3 values are:

  • altitude above sea level
  • actual air pressure at that altitude
  • equivalent air pressure at sea level (more strictly, mean sea level, because instantaneous sea level constantly changes)

This library provides two functions for this relationship, as follows:

calcAltitude (pressure, seaLevelhPa);
calcNormalisedPressure (pressure, altitude);

There is also a simpllified version, which assumes the standard sea level pressure of 1013.15 hPa.

calcAltitude (pressure); // standard seaLevelPressure assumed

STEP 3: BMP280 Device Details

Hardware capabilities

The BMP280 has 2 bytes of configuration data (at register addresses 0xF4 and 0xF5) which is used to control multiple measurement and data output options. It also provides 2 bits of status information, and 24 bytes of calibration parameters which are used in converting the raw temperature and pressure values into conventional temperature and pressure units.
The BME280 has additional data as follows:

  • 1 extra byte of configuration data at register address 0xF2 used to control multiple humidity measurements;
  • 8 extra bytes of calibration parameters used in converting the raw humidity value into relative humidity percentage.

The temperature, pressure and status registers for the BME280 are the same as for the BMP280 with minor exceptions as follows:

  • the "ID" bits of the BME280 are set to 0x60, so it can be distinguished from BMP280 which may be 0x56, 0x57 or 0x58
  • the sleep time control (t_sb) is changed so that the two long times in the BMP280 (2000 ms and 4000 ms) are replaced in the BME280 with short times of 10 ms and 20 ms. The maximum sleep time in the BME280 is 1000 ms.
  • In the BME280 the temperature and pressure raw values are always 20 bits if filtering is applied. The use of 16 to 19 bit values is limited to cases with no filtering (ie filter=0).

Temperature and pressure are each 20 bit values, which need to be converted into conventional temperature and pressure via a rather complex algorithm using 3 16 bit calibration parameters for temperature, and 9 16 bit calibration parameters plus the temperature for pressure. The granulatity of the temperature measurement is 0.0003 degrees Celsius for a least significant bit change (20 bit readout), increasing to 0.0046 degrees Celsius if the 16 bit readout is used.

Humidity is a 16 bit value which needs to be converted into relative humidity via another complex algorithm using 6 calibration parameters which are a mix of 8, 12 and 16 bits.

The data sheet shows the absolute accuracy of the temperature readout as +-0.5 C at 25 C and +-1 C over the range 0 to 65 C.

The granularity of the pressure measurement is 0.15 Pascals (ie 0.0015 hectoPascals) at 20 bit resolution, or 2.5 Pascals at 16 bit resolution. The raw pressure value is affected by the temperature, so that around 25C, an increase in temperature of 1 degree C decreases the measured pressure by 24 Pascals. The temperature sensitivity is accounted for in the calibration algorithm, so the delivered pressure values should be accurate at different temperatures.

The data sheet shows the absolute accuracy of the pressure readout as +-1 hPa for temperatures between 0 C and 65 C.

The accuracy of the humidity is given in the data sheet as +-3% RH, and +-1% hysteresis.

How it works

The 24 bytes of temperature and pressure calibration data, and also in the case of the BME280 the 8 bytes of humidity calibration data, have to be read from the device and stored in variables. These data are individually programmed into the device in the factory, so different devices have different values - at least for some of the parameters.
A BME/P280 can be in one of two states. In one state it is measuring. In the other state it is waiting (sleeping).

Which state it is in can be checked by looking at bit 3 of register 0xF3.

The results of the most recent measurement can be obtained at any time by reading the corresponding data value, irrespective of whether the device is sleeping or measuring.

There are also two ways of operating the BME/P280. One is Continuous mode (called Normal mode in the data sheet) which repeatedly cycles between Measuring and Sleeping states. In this mode the device performs a set of measurements, then goes to sleep, then wakes up for another set of measurements, and so on. The number of individual measurements and the duration of the sleep part of the cycle can all be controlled through the configuration registers.

The other way of operating the BME/P280 is Single Shot mode (called Forced mode in the data sheet). In this mode the device is woken from sleep by a command to measure, it does a set of measurements, then goes back to sleep. The number of individual measurements in the set is controlled in the configuration command that wakes up the device.

In the BMP280, if a single measurement is made, the 16 most significant bits in the value are populated, and the four least significant bits in the value readout are all zeros. The number of measurements can be set to 1, 2, 4, 8 or 16 and as the number of measurements is increased, the number of bits populated with data increases, so that with 16 measurements all 20 bits are populated with measurement data. The data sheet refers to this process as oversampling.

In the BME280, the same arrangement applies so long as the result is not being filtered. If filtering is used, the values are always 20 bits, irrespective of how many measurements are taken in each measurement cycle.

Each individual measurement takes about 2 milliseconds (typical value; maximum value is 2.3 ms). Add to this a fixed overhead of about 2 ms (usually a bit less) means that a measurement sequence, which can consist of from 1 to 32 individual measurements, can take from 4 ms up to 66 ms.

The data sheet provides a set of recommended combinations of temperature and pressure oversampling for various applications.

Configuration control registers

The two configuration control registers in the BMP280 are at register addresses 0xF4 and 0xF5, and are mapped onto 6 individual configuration control values. 0xF4 consists of:

  • 3 bits osrs_t (measure temperature 0, 1, 2, 4, 8 or 16 times);
  • 3 bits osrs_p (measure pressure 0, 1, 2, 4, 8 or 16 times); and
  • 2 bits Mode (Sleep, Forced (ie Single Shot), Normal (ie continuous).

0xF5 consists of:

  • 3 bits t_sb (standby time, 0.5ms to 4000 ms);
  • 3 bits filter (see below); and
  • 1 bit spiw_en which selects SPI or I2C.

The filter parameter controls a type of exponential decay algorithm, or Infinite Impulse Response (IIR) filter, applied to the raw pressure and temperature measurement values (but not to the humidity values). The equation is given in the data sheet. Another presentation is:

Value(n) = Value(n-1) * (K-1)/K + measurement(n) / K

where (n) indicates the most recent measurement and output value; and K is the filter parameter. The filter parameter K and can be set to 1,2,4,8 or 16. If K is set to 1 the equation just becomes Value(n) = measurement(n). The coding of the filter parameter is:

  • filter = 000, K=1
  • filter = 001, K=2
  • filter = 010, K=4
  • filter = 011, K=8
  • filter = 1xx, K=16

The BME 280 adds a further configuration control register at address 0xF2, "ctrl_hum" with a single 3-bit parameter osrs_h (measure humidity 0, 1, 2, 4, 8 or 16 times).

STEP 4: Measurement and Readout Timing

I plan to add this later, showing the timing of commands and measurement responses.

Iddt - current at temperature measurement. Typical value 325 uA

Iddp - current at pressure measurement. Typical value 720 uA, max 1120 uA

Iddsb - current in standby mode. Typical value 0.2 uA, max 0.5 uA

Iddsl - current in sleep mode. Typical value 0.1 uA, max 0.3 uA


STEP 5: Software Guidelines

I2C Burst mode

The BMP280 data sheet provides guidance about data readout (section 3.9). It says "it is strongly recommended to use a burst read and not address every register individually. This will prevent a possible mix-up of bytes belonging to different measurements and reduce interface traffic."
No guidance is given regarding the reading of the compensation/calibration parameters. Presumably these are not an issue because they are static and do not change.

This library reads all contiguous values in a single read operation - 24 bytes in the case of the temperature and pressure compensation parameters, 6 bytes for temperature and pressure combined, and 8 bytes for humidity, temperature and pressure combined. When temperature alone is checked, only 3 bytes are read.

Use of macros (#define etc.)

There are no macros in this library other than the usual library "include guard" macro which prevents duplication.

All constants are defined using the const keyword, and debug printing is controlled with standard C functions.

It has been the source of some uncertainty for me, but the advice I get from reading many posts on this subject is that use of #define for declaration of constants (at least) and (probably) debug printing control is unnecessary and undesirable.

The case for the use of const rather than #define is pretty clear - const uses the same resources as #define (ie nil) and the resultant values follow the scoping rules, thereby reducing the chance of errors.

The case for debug printing control is a bit less clear, because the way I have done it means that the final code contains the logic for the debug printing statements, even though they are never exercised. If the library is to be used in a big project on a microcontroller with very limited memory, this may become an issue. Since my development was on an ESP8266 with a large flash memory, this did not seem to be an issue for me.

STEP 6: Temperature Performance

I plan to add this later.

STEP 7: Pressure Performance

I plan to add this later.

23 Comments

Hi,
I'm Working on Jetson and I detect the sensor by using the sudo i2cdetect -r -y 1 command.
can I use this Library in C++ for jetson to use BME280?
I cannot find any library that can communication with BME280 on jetson and c++
all libraries are on the Arduino Platform....
Hello Kabviz Dashi,
I may be able to help you, but if so it will be a joint learning exercise.
Can you tell me some more about your Jetson configuration and where you are on the journey of getting it working for your project objectives?
Keith
Excellent tutorial - very well written - thank you
Update: in case this helps anyone else, I changed the "const float seaLevelhPa 1013.15;" to "#define seaLevelhPa 1013.15;" and then call this function to get the altititude:
bmeAltitude = bme0.calcAltitude (pressure);

Still hoping that @farmerkeith will show a proper example...
-----------------
Hi, I love this library. Thanks for sharing!
I'm trying to implement using BME280, on Teensy 3.2.
Pressure, Temperature, and Humidity readings work great.
However, I can't figure out how to calculate the altititude in my program, and I don't see this as an example in your programs. Can you please update one of your example programs to show the 3 altitude functions?

I tried:
---------- declarations -------
bme280 bme0 ; // creates object bme0 of type bme280, base address
double temperature, pressure, humidity;
float bmeAltitude;
const float seaLevelhPa 1013.15; //in hPa. The standard sea level pressure is 1013.15 hPa.

loop() {
humidity = bme0.readHumidity (temperature, pressure); // measure temperature and pressure
//bmeAltitude = calcAltitude (pressure, seaLevelhPa);
bmeAltitude = bme0.calcAltitude (pressure);
//calcAltitude(pressure, seaLevelhPa);
Serial.println(bmeAltitude);
}
Hey Farmerkeith,
I tried to use your library on an Atmega328P working like an arduino uno.
Using basicTemperature, I only get "Start o" on serial line that is the beginning of "Start of basicTemperature sketch".
So my question is: do you think this library works on arduino uno or like?

Hi bananof,
I would expect this library to work well on your Atmega328P. Although I have not tested on a Uno, it works on a Nano and in essence the software environments are the same.

Do you get the trucated introductory print line on restart (when you press your reset button) or when you disconnect and reconnect the power?
Could there be a possibility that you have different settings for the bit rate in your computer's serial monitor, and the example sketch?
If you come back with more details I will try to help.
Keith

Thank you for your answer.
I tested using reset, the result is the same. Baudrate is 115200 and I use basicTemperature.ino to test.
Furthermore, this sensor works with sparkfun bme280 library.
As far as I can see, the system freezes around bmp0.begin(); instruction.
I tried to comment this line and other referencing to this object and the system does'nt freeze anymore.
Hello. So i just tried on an stm32 and i ended up having the EXACT same problem so it is probably not a compatibility problem with the microcontroller. I tried to use the other begin functions with or without parameters but the program just freezes and the serial output displays the first line and the first word of the second print instruction just before the ".begin()" isntruction. If i comment the begin the program runs but obviusly i can`t use the sensor.
i have a bmp / bme280 could i get the humidity too? How could I do it?
In blynk I only collected temperature and pressure data, the battery was impossible ... at the moment I am using it with thingSpeak
Hi krtpowa,
Whether you can get humidity, or not, depends on whether your sensor is BMP280 or BME 280. Please see the last section of Step 1 for guidance on working out which type you have.
I am not sure what you mean by "..the battery was impossible".
If this is not helpful please let me know.
Keith

ik wil buiten meten met de bmp280 hoeveel draad lengte kan ik tussen de bmp en mijn nano gebruiken
bvd a merts
Hello alphons1, I think I answered this question, but I cannot see the answer in my correspondence. Do you need any more information?
Keith
Hey. I'm trying to get this to work on atmega8, it has 8kb flash. I get an empty terminal using LTO
and my program

Is there a way to reduce the library code?

#include
#include
/************* USER Configuration *****************************/
// Hardware configuration
bmp280 bmp0 ;
/***************************************************************/
void setup(void) {
Serial.begin(115200);
// Serial.begin(9600); // use this if you get errors with the faster rate
Serial.println("\n--");
Wire.begin(); // initialise I2C protocol
bmp0.begin(); // initialise BMP280 for continuous measurements

}
void loop(void){
double temperature=bmp0.readTemperature (); // measure temperature
Serial.print(temperature,2); // print with 2 decimal places
delay(1000);
}

info:
use: 6856 byte (89%) flash. all byte 7680 byte.
global vars: 927 byte (90%) - 97 bytes free. Max: 1024 byte.


BUT ths code work fine:
#include <Wire.h>
#include <Adafruit_BMP280.h>
Adafruit_BMP280 bmp; // I2C
void setup() {
Serial.begin(9600);
// Serial.println("BMP280 test");
if (!bmp.begin(0x76)) {
// Serial.println("err");
while (1);
}
/* Default settings from datasheet. */
bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */
Adafruit_BMP280::SAMPLING_X2, /* Temp. oversampling */
Adafruit_BMP280::SAMPLING_X16, /* Pressure oversampling */
Adafruit_BMP280::FILTER_X16, /* Filtering. */
Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
Serial.print("T=");
Serial.print(bmp.readTemperature());
delay(2000);
}


Hi



СергейД17


Yes it is possible to cut down the size of both program and data usage by removing things that you don't use.
I don't have a setup to give me the atmega8 values, and the values below may not be strictly comparable, but what I get when compiling for an Arduino Nano is
Library Program Data
farmerkeith 8142 1245
Adafruit 10154 467
Your numbers (atmega8) 6856 927
I see your sketch only has the temperature and not pressure. If you don't want pressure,, there are many things you could remove. However the main areas for reduction are:
the farmerkeith library has a lot of debug print statements in it. These can be removed without any impact on the working. Any that you want to keep should be enclosed in a F() macro which results in the text being in program memory only and not also in data memory. This can be a big saving if memory is in short supply. I think this is probably the reason for the farmerkeith library using so much more data memory than the adafruit one.
The altitude and standardized pressure calculation functions could be removed.
With some loss in accuracy, the data type used for temperature and pressure could be changed from "double" to "float". Depending on your application and the accuracy required, this could save a little bit of both memory and real time.

Separately, the Adafruit library also supports the SPI mode of the BMP280. If you are using I2C, you could remove the SPI support (not a big saving I think but with a very limited memory space it may help to get you over the line to do what you want).

Are you able to make changes like this yourself? If you need guidance on how to go about this I can give you some, or I could even publish a cut-down version aimed at your application.
I hope this helps,
Keith

Hey.
The final device is a temperature \ pressure sensor + nrf24l01.

I get 7736 \ 300 bytes.

I looked at your library .. Could not find what to cut.
Everything looks good. If (debug) {...} constructs will not be compiled if they are not used in the code.
you lib:
In file included from C:\Program Files (x86)\Arduino\libraries\BMP280-library-master\farmerkeith_BMP280.cpp:14:0:
C:\Users\indevor\AppData\Local\Arduino15\packages\MiniCore\hardware\avr\2.0.3\libraries\Wire\src/Wire.h: In member function 'byte bmp280::readRegister(byte)':
C:\Users\indevor\AppData\Local\Arduino15\packages\MiniCore\hardware\avr\2.0.3\libraries\Wire\src/Wire.h:64:13: note: candidate 1: uint8_t TwoWire::requestFrom(int, int)
uint8_t requestFrom(int, int);
^
C:\Users\indevor\AppData\Local\Arduino15\packages\MiniCore\hardware\avr\2.0.3\libraries\Wire\src/Wire.h:61:13: note: candidate 2: uint8_t TwoWire::requestFrom(uint8_t, uint8_t)
uint8_t requestFrom(uint8_t, uint8_t);
^
c:/program files (x86)/arduino/hardware/tools/avr/bin/../lib/gcc/avr/5.4.0/../../../../avr/bin/ld.exe: C:\Users\indevor\AppData\Local\Temp\arduino_build_573275/bmp280_2.ino.elf section `.data' will not fit in region `text'
c:/program files (x86)/arduino/hardware/tools/avr/bin/../lib/gcc/avr/5.4.0/../../../../avr/bin/ld.exe: region `text' overflowed by 84 bytes
collect2.exe: error: ld returned 1 exit status
exit status 1
-----------------------------------------------
adaf lib:

7736 байт (100%) from 7680 byte.
300 байт (29%) 724 from 1024 byte.
The sketch is too big; read http://www.arduino.cc/en/Guide/Troubleshooting#size
Compilation error for the board ATmega8.

---------------------------------------------------------------------------------------

#include <SPI.h>
#include "RF24.h"
#include <Wire.h>
#include <Adafruit_BMP280.h>
Adafruit_BMP280 bmp; // I2C
RF24 radio(9,10); // Set up nRF24L01 radio on SPI bus plus pins 7 & 8
const uint64_t pipes[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; // Radio pipe addresses for the 2 nodes to communicate.
struct telemetry_t {
byte id;
byte mode;
byte davlen;
};
telemetry_t telemetry; // массив принятых от приёмника данных телеметрии
struct transmit_data_t {
byte id;
float temp;
byte davl;
};
transmit_data_t transmit_data; // массив переданных от приёмника данных телеметрии
bool is_payload_sent = false;
void setup() {
// Serial.begin(9600);
// Serial.println("BMP280 test");
if (!bmp.begin(0x76)) {
// Serial.println("err");
while (1);
}
/* Default settings from datasheet. */
bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */
Adafruit_BMP280::SAMPLING_X2, /* Temp. oversampling */
Adafruit_BMP280::SAMPLING_X16, /* Pressure oversampling */
Adafruit_BMP280::FILTER_X16, /* Filtering. */
Adafruit_BMP280::STANDBY_MS_2000); /* Standby time. */
radio.begin(); // Setup and configure rf radio
radio.setChannel(1);
radio.setPALevel(RF24_PA_MAX); // If you want to save power use "RF24_PA_MIN" but keep in mind that reduces the module's range
radio.setDataRate(RF24_1MBPS);
radio.setAutoAck(1); // Ensure autoACK is enabled
radio.setRetries(2,15); // Optionally, increase the delay between retries & # of retries
radio.setCRCLength(RF24_CRC_8); // Use 8-bit CRC for performance
radio.openWritingPipe(pipes[0]);
radio.powerDown(); // начать работу
radio.stopListening(); // не слушаем радиоэфир, мы передатчик
}
void loop() {
// Serial.print("T=");
// Serial.print(bmp.readTemperature());
// Serial.println();
// // Serial.print("P=");
// // Serial.print(bmp.readPressure());
////
//// Serial.print("A= ");
//// Serial.print(bmp.readAltitude(1013.25)); /* Adjusted to local forecast! */
////
if (!is_payload_sent) {
radio.powerUp();
if (radio.write(&transmit_data, sizeof(transmit_data))) { // отправка пакета transmit_data
if (radio.available()) {
// если получаем пустой ответ
// если в ответе что-то есть
radio.read(&telemetry, sizeof(telemetry)); // читаем // получили забитый данными массив telemetry ответа от приёмника
radio.flush_tx();
radio.flush_rx();
}
} else {
}
radio.powerDown();
is_payload_sent = true;
}
transmit_data.id = 1;
transmit_data.temp = bmp.readTemperature();
transmit_data.davl = bmp.readPressure();
is_payload_sent = false;
}


although I was wrong, a very strange compiler ..
if you remove the if (debug) blocks {...} = 6648 (86%) from 7680 byte.


I am not a great specialist in this. I always thought that if something is not used in the code, it will be ignored by the compiler. That is, he seems to be doing the assembly without taking these blocks into account .. I was wrong
heads up for the next ATTiny85 victims :)
- pow() defined in math.h should be replaced with alternative methods, you'll save about 1.2K that way
- https://github.com/cano64/ATTiny85-ATTiny84-BMP085-Arduino-Library-FastAltitude/blob/master/tinyBMP085.cpp - take the altitude reading function from here, it's a approximation table which may work for you, you'll save some space by not doing pow()
- removing if (debug) doesn't do anything, compiler is pretty smart.
- of course, comment out Serial.print, we don't have such a fancy tool.
- TinyWireM as Wire.h replacement
- with LCD I2C driver (from tinusaur, but mixed with TinyWireM, because bit-banging with simple writer I2C doesn't go well together with "real" I2C for BMP280) for SSD1306 and this library, after reading temperature, pressure and altitude, I'm filling up exactly 100% of Program Memory.

Time to do further optimization if I want my altimeter to be born alive with this chip :)
Hello,
Thank you for your library.
I'm doing the solar powered weather station from Open Green Energy and I'm testing the library. My end goal is to send the weather parameters to Home Assistant in MQTT.
So I'm testing the library but I' didn't make such projects since years.

When I'm doing the very simple sketch :

#include <farmerkeith_BMP280.h>
#include <Wire.h>
bmp280 bmp0;
void setup() {
Serial.begin(115200);
Serial.println("\nStart of basic Pressure and temperature sketch");
Serial.println("Printing pressure and temperature at 5 second intervals");
Wire.begin();
bmp0.begin();
}
void loop() {
double temperature, pressure;
double humidity=objectName.readHumidity (temperature, pressure); // measure humidity, pressure and temperature
Serial.println("Atmospheric pressure = ");
Serial.println(pressure,4);
Serial.println("hPa. Tmperature = ");
Serial.println(temperature,2);
Serial.println("degrees celsius");
Serial.println(humidity,0);
Serial.println("%");
delay(5000);
}

I get :
exit status 1
'objectName' was not declared in this scope

It's very weird because I've the filling that I only copy paste what's above. Imissed something...
Thanks

Lionel
Hi Lionel,
There are two problems here. The first is you have to replace "objectName" with the name of the software object you are using. The second is that you cannot read humidity with a bmp280 device and using the bmp280 class. You need a bme280 device and to use the bme280 class to read humidity,
Do you know if you have a bmp280 or a bme280 device? Some guidance on working that out is at the end of Step 1 of this Instructable.
If you do have a bme 280 device, you can change the code in the line just before "void setup(){" to read "bme280 bme0;". In this statement, the "bme280" calls up the bme280 class definition. The "bme0" defines the object name. You can use any object name you like. You just need to use the same object name wherever you want to refer to that object.
Then in the line "double humidity=objectName.readHumidity (temperature, pressure); // measure humidity, pressure and temperature" you need to replace "objectName" with the name of the bme280 software object you are using, as described above.

If your device is a bmp280, you cannot get humidity readings from it, and you should leave the software class as bmp280, and your code should follow the example for reading pressure and temperature only, not humidity.
I hope this works out for you. Don't hesitate to come back with more questions if you have them.
Keith

Thanks a lot for your support and your swift reply! Very clear, now it's well working ! I've a BME280. Indeed all the explainations were above. Sorry.

Merry chirstmas !

Lionel
More Comments