A lot of people are flying drones these days, and knowing the altitude your drone reached is a nice thing to know. A blazingly fast, temperature stable, pinpoint-accurate barometric pressure sensor like Bosch’s BMP388 could be the perfect choice for altitude tracking in your next drone build.
Hardware Overview
At the heart of the module is the next-generation digital pressure and temperature sensor, from Bosch – BMP388.
The BMP388 Pressure Sensor
The BMP388 is a very small, blazingly fast, precise, low power, low noise absolute barometric pressure sensor. It enables accurate altitude tracking and is perfectly suited for drone applications or any project that wants to track height-above-sea-level.
The BMP388 can measure barometric pressure from 300 hPa to 1250 hPa and has a relative accuracy of 8 Pascals, which translates to about ± 0.5 meter of altitude.
The BMP388 has an on-chip temperature sensor that can be used to compensate for the changes in the environment and to calibrate the measurements. This is a reasonably precise temperature sensor that measures the ‘die temperature’ in the range of -40˚C to +85˚C with an accuracy of ±1˚C.
Power Requirement
The module comes with a MIC5219 3.3V precise voltage regulator and voltage level translator, so you can use it with your favorite 3.3V or 5V microcontroller without worry.
The BMP388 consumes less than 0.7mA during measurements and only 2µA during sleep mode. This low power consumption allow the implementation in battery driven devices such as handsets, wearables or smart watches.
Digital interfaces
The sensor communicates via either I2C or SPI.
I2C Interface
The sensor uses the I2C interface for communication with the Arduino. It supports two separate I2C addresses: 0x77Hex and 0x76Hex. This allows two BMP388 modules to be used on the same bus or to avoid address conflicts with another device on the bus.
The SDO pin determines the I2C address of the module. This pin has a built-in pull-up resistor. Therefore, when you leave the SDO pin unconnected, the default I2C address is 0x77Hex and when you connect it to GND, the line is pulled LOW and the I2C address becomes 0x76Hex.
SPI Interface
The sensor can also communicate via the SPI interface.
FYI:
If you have to choose between the two, SPI is generally the better tool, if you need faster transfer speeds (up to 10 MHz). I2C, on the other hand, is best if you have limited pins available on your microcontroller.
Modes of Operation
The BMP388 has 3 modes of operation:
- SLEEP_MODE: puts the device into an inactive standby state (no measurements are performed).
- NORMAL_MODE: performs continuous conversions, separated by the standby time.
- FORCED_MODE: performs a single conversion, returning to SLEEP_MODE upon completion.
FIFO Buffer
The BMP388 embeds a 512 bytes FIFO (first-in-first-out) buffer for storing up to 72 pressure and/or temperature measurement results. The FIFO buffer can offload the microcontroller from reading each new data sample from the sensor, thereby saving system power.
Interrupts
The BMP388 has an interrupt (INT) output pin that enables measurements to be interrupt driven instead of using polling, allowing the host microcontroller to perform other tasks while the data is collected by the sensor. The interrupt can be enabled for 3 different sources:
- Data Ready: triggers after a pressure and temperature measurement ends and conversion results are stored to the data registers and to the FIFO.
- FIFO Watermark: triggers when fill level of the FIFO has reached a pre-set limit.
- FIFO Full: triggers when the FIFO becomes full and future data is about to be lost.
The INT pin output drive can be: PUSH_PULL or OPEN_COLLECTOR, the level of the INT pin can be: ACTIVE_LOW or ACTIVE_HIGH and interrupt itself can be: UNLATCHED or LATCHED. In UNLATCHED mode the interrupt signal automatically clears after 2.5ms, while in LATCHED mode the interrupt signal remains active until the data is read.
Technical Specifications
Here are the complete specifications:
Power supply | 3.3V to 5.5V |
Current draw | ~0.7mA (during measurements) |
~2µA (during standby mode) | |
Pressure Measurement Range | 300Pa to 1250 hPa |
Pressure Absolute Accuracy | ±0.5 hPa |
Temperature Range | -40ËšC to +85ËšC |
Temperature Accuracy | ±1˚C |
For more details, please refer below datasheet.
BMP388 Module Pinout
Now let’s have a look at the pinout.
Power Pins:
VCC is the power pin. You can connect it to 5V output from your Arduino.
3Vo is the 3.3V output from the voltage regulator. You can grab up to 100mA from this, if you need.
GND is the common ground for power and logic.
SPI Logic pins:
SCK is the SPI Clock pin, it’s an input to the chip.
SDO is the Serial Data Out (MISO) pin, for data sent from the BMP388 to your microcontroller.
SDI is the Serial Data In (MOSI) pin, for data sent from your microcontroller to the BMP388.
CS is the Chip Select pin, pull it LOW to enable an SPI communication. If you want to connect multiple BMP388’s to one microcontroller, have them share the SDI, SDO and SCK pins and assign each one a unique CS pin.
I2C Logic pins:
SCK is also the I2C clock pin, connect to your microcontrollers I2C clock line.
SDI is also the I2C data pin, connect to your microcontrollers I2C data line.
SDO pin determines the I2C address of the module. When you leave the SDO pin unconnected, the default I2C address is 0x77Hex and when you connect it to GND, the line is pulled LOW and the I2C address becomes 0x76Hex.
Interrupt Pin:
INT is the Interrupt pin. The BMP388 can be programmed to generate interrupts on certain events. Please refer Interrupts section.
Wiring up a BMP388 Module to an Arduino
Now that we know everything about the module, we can begin hooking it up to our Arduino!
I2C Wiring
Use this wiring if you want to connect via I2C interface.
Start by connecting the VCC pin to the power supply, 3V-5V is fine. Use the same voltage that your microcontroller logic is based off of. For most Arduinos, that is 5V. For 3.3V logic devices, use 3.3V. Now connect GND to common ground.
Connect the SCK pin to the I2C clock pin and the SDI pin to the I2C data pin on your Arduino. Note that each Arduino Board has different I2C pins which should be connected accordingly. On the Arduino boards with the R3 layout, the SDA (data line) and SCL (clock line) are on the pin headers close to the AREF pin. They are also known as A5 (SCL) and A4 (SDA).
The following illustration shows the wiring.
SPI Wiring
Since it’s an SPI-capable sensor, you can use hardware or software SPI. However, it is recommended that you use hardware SPI pins as they are much faster than ‘bit-banging’ the interface code using another set of pins.
Again, each Arduino Board has different SPI pins which should be connected accordingly. For Arduino boards such as the UNO/Nano V3.0 those pins are digital 13 (SCK), 12 (MISO), 11 (MOSI) and 10 (SS).
If you are using a different Arduino board, it is advisable to check the official documentation about SPI pin locations before proceeding.
The following illustration shows the wiring.
Once your module is connected to the Arduino it’s time to write some code!
Library Installation
To begin reading sensor data, you will need to install the Adafruit BMP3XX library. It is available from the Arduino library manager.
To install the library navigate to the Sketch > Include Library > Manage Libraries… Wait for Library Manager to download libraries index and update list of installed libraries.
Filter your search by typing ‘adafruit bmp3xx‘ and install the library.
The BMP3XX sensor library uses the Adafruit Sensor support backend. So, search the library manager for Adafruit Unified Sensor and install that too (you may have to scroll a bit)
Arduino Code – Reading Pressure, Altitude and Temperature
Below is a basic Arduino sketch. Go ahead and upload it to your Arduino. You should see pressure, temperature and approx. altitude on the serial monitor.
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include "Adafruit_BMP3XX.h"
//Uncomment if you want to use SPI
//#define BMP_SCK 13
//#define BMP_MISO 12
//#define BMP_MOSI 11
//#define BMP_CS 10
#define SEALEVELPRESSURE_HPA (1013.25)
Adafruit_BMP3XX bmp;
void setup() {
Serial.begin(115200);
while (!Serial);
if (!bmp.begin_I2C()) { // hardware I2C mode, can pass in address & alt Wire
//if (! bmp.begin_SPI(BMP_CS)) { // hardware SPI mode
//if (! bmp.begin_SPI(BMP_CS, BMP_SCK, BMP_MISO, BMP_MOSI)) { // software SPI mode
Serial.println("Could not find a valid BMP3 sensor, check wiring!");
while (1);
}
// Set up oversampling and filter initialization
bmp.setTemperatureOversampling(BMP3_OVERSAMPLING_8X);
bmp.setPressureOversampling(BMP3_OVERSAMPLING_4X);
bmp.setIIRFilterCoeff(BMP3_IIR_FILTER_COEFF_3);
bmp.setOutputDataRate(BMP3_ODR_50_HZ);
}
void loop() {
if (! bmp.performReading()) {
Serial.println("Failed to perform reading :(");
return;
}
Serial.print("Temperature = ");
Serial.print(bmp.temperature);
Serial.println(" *C");
Serial.print("Pressure = ");
Serial.print(bmp.pressure / 100.0);
Serial.println(" hPa");
Serial.print("Approx. Altitude = ");
Serial.print(bmp.readAltitude(SEALEVELPRESSURE_HPA));
Serial.println(" m");
Serial.println();
delay(2000);
}
Note that you must set your serial monitor to a speed of 115200 baud to try out the sketch.
You will see a lot of data displaying pressure, temperature and approx. altitude values. Try moving your sensor around and notice how the data changes.
Code Explanation:
The sketch starts with including four necessary libraries viz. Wire.h
(for I2C), SPI.h
(for SPI), Adafruit_Sensor.h
and Adafruit_BMP3XX.h
.
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include "Adafruit_BMP3XX.h"
This sketch uses I2C communication protocol to communicate with the sensor. However, the sketch is prepared if you want to use SPI. You just need to uncomment the following lines of code that define the SPI pins.
//#define BMP_SCK 13
//#define BMP_MISO 12
//#define BMP_MOSI 11
//#define BMP_CS 10
Next, a variable called SEALEVELPRESSURE_HPA
is defined. This variable saves the sea level pressure in milibar and is used to estimate the altitude for a given pressure by comparing it with the sea level pressure. This sketch uses the default value, but for accurate results, replace the value with the current sea level pressure at your location.
#define SEALEVELPRESSURE_HPA (1013.25)
The following line creates an Adafruit_BMP3XX object called bmp
.
Adafruit_BMP3XX bmp;
In the setup, we initialize the serial communication with PC and call the begin()
function.
The bmp.begin_I2C()
function initializes I2C interface and checks if the chip ID is correct. It then resets the chip using soft-reset & waits for the sensor for calibration after wake-up. Likewise the bmp.begin_SPI()
function initializes SPI interface. If you want to use SPI, you need to uncomment corresponding if
.
Serial.begin(115200);
while (!Serial);
Serial.println("Adafruit BMP388 / BMP390 test");
if (!bmp.begin_I2C()) { // hardware I2C mode, can pass in address & alt Wire
//if (! bmp.begin_SPI(BMP_CS)) { // hardware SPI mode
//if (! bmp.begin_SPI(BMP_CS, BMP_SCK, BMP_MISO, BMP_MOSI)) { // software SPI mode
Serial.println("Could not find a valid BMP3 sensor, check wiring!");
while (1);
}
Next, we set up some parameters for the sensor.
bmp.setTemperatureOversampling(BMP3_OVERSAMPLING_8X);
bmp.setPressureOversampling(BMP3_OVERSAMPLING_4X);
bmp.setIIRFilterCoeff(BMP3_IIR_FILTER_COEFF_3);
bmp.setOutputDataRate(BMP3_ODR_50_HZ);
Let’s look at each function one by one.
setTemperatureOversampling(uint8_t os)
setPressureOversampling(uint8_t os)
To increase the effective resolution of measurements and reduce noise, the BMP388 supports oversampling (taking multiple samples and averaging them together). There are two functions called setTemperatureOversampling()
and setPressureOversampling()
to set oversampling for temperature and pressure measurements, respectively. These functions can accepts one of the following parameters:
- BMP3_NO_OVERSAMPLING (turns off reading)
- BMP3_OVERSAMPLING_2X
- BMP3_OVERSAMPLING_4X
- BMP3_OVERSAMPLING_8X
- BMP3_OVERSAMPLING_16X
- BMP3_OVERSAMPLING_32X
setIIRFilterCoeff(uint8_t fs)
The BMP388 also features an internal IIR filter to reduce short-term pressure changes in sensor output values caused by external disturbances (such as slamming a door or window, or blowing air into the sensor). The setIIRFilterSize()
function sets the IIR filter. The IIR filter can be configured to different filter coefficients by choosing one of the following parameters:
- BMP3_IIR_FILTER_DISABLE (no filtering)
- BMP3_IIR_FILTER_COEFF_1
- BMP3_IIR_FILTER_COEFF_3
- BMP3_IIR_FILTER_COEFF_7
- BMP3_IIR_FILTER_COEFF_15
- BMP3_IIR_FILTER_COEFF_31
- BMP3_IIR_FILTER_COEFF_63
- BMP3_IIR_FILTER_COEFF_127
Note that higher IIR filter coefficients provide stable measurements at the cost of slow response time.
setOutputDataRate(uint8_t odr)
The setOutputDataRate()
function sets the output data rate i.e. the maximum you can read the device for the given settings. This function accepts one of the following parameters:
- BMP3_ODR_200_HZ
- BMP3_ODR_100_HZ
- BMP3_ODR_50_HZ
- BMP3_ODR_25_HZ
- BMP3_ODR_12_5_HZ
- BMP3_ODR_6_25_HZ
- BMP3_ODR_3_1_HZ
- BMP3_ODR_1_5_HZ
- BMP3_ODR_0_78_HZ
- BMP3_ODR_0_39_HZ
- BMP3_ODR_0_2_HZ
- BMP3_ODR_0_1_HZ
- BMP3_ODR_0_05_HZ
- BMP3_ODR_0_02_HZ
- BMP3_ODR_0_01_HZ
- BMP3_ODR_0_006_HZ
- BMP3_ODR_0_003_HZ
- BMP3_ODR_0_001_HZ
In the loop, we call the bmp.performReading()
function to perform a reading. Once this is done we can access object’s (bmp) instance variables using the dot operator.
bmp.temperature returns temperature reading.
bmp.pressure returns barometric pressure reading.
bmp.readAltitude(SEALEVELPRESSURE_HPA) function calculates the altitude (in meters) from the specified atmospheric pressure (in hPa), and sea-level pressure (in hPa).
void loop() {
if (! bmp.performReading()) {
Serial.println("Failed to perform reading :(");
return;
}
Serial.print("Temperature = ");
Serial.print(bmp.temperature);
Serial.println(" *C");
Serial.print("Pressure = ");
Serial.print(bmp.pressure / 100.0);
Serial.println(" hPa");
Serial.print("Approx. Altitude = ");
Serial.print(bmp.readAltitude(SEALEVELPRESSURE_HPA));
Serial.println(" m");
Serial.println();
delay(2000);
}