
If you want to build a compact weather station with the Raspberry Pi or simply want to experiment with different sensors, then this tutorial will be perfect for you! In this article, you will familiarise yourself with using the BME280 sensor, a multi-purpose temperature, humidity and barometric pressure sensor, with the Raspberry Pi. Let’s begin!
Project Overview
Bosch designed the BME280 chipset which is a newer generation in this line of environmental sensors compared to its brother, the BME180. With high accuracy and a fairly low cost, the BME280 allows for a greater measurement range than the BME180 in terms of temperature (-40°C – 85°C ± 1.0°C), humidity (0% – 100% ± 1%) and pressure (300Pa – 1100 hPa ± 1 hPa).
Another additional feature of the BME280 is that although it technically doesn’t have the hardware nor the capabilities to directly measure altitude, it can still be calculated based on pressure readings. Regarding the interfacing of the sensor, it is fully capable of using both i2c and SPI to communicate with an Arduino or Raspberry Pi. In this tutorial, the i2c bus will be used and note that this sensor can support two i2c addresses, 0x76 and 0x77, meaning that two sensors can be utilized on the same bus.
In regards to the code, a unique feature of this project is that we are not going to import a pre-made BME280 library/package to interface this sensor with the Pi. Hence, the code will consist of multiple blocks that will be responsible for reading and transforming the data to a readable form for us to view.
What You Need
For this project, the components required are quite minimal and aside from the BME280 itself, you should be able to find all these components in your electronics workshop:
- Raspberry Pi (any model should work)
- Male-Female Jumper Wires (4)
- BME280 Environmental Sensor
Wiring
You will immediately notice that most BME280 sensor variants consist of two separate rows of header pins and the purpose of this is for its two communication interfaces: i2c and SPI (Serial Peripheral Interface). A typical sensor compatible with SPI will often have a 6-pin configuration (VCC, GND, Serial Clock (SCK), Serial Data Out (SDO), Serial Data In (SDI) and Chip Select (CS)), whereas i2c devices will usually have a 4-pin configuration (VCC, GND, Serial Clock (SCL) and Serial Data (SDA)).
If you are brand new to the Raspberry Pi, connecting wires to the Pi may be a bit daunting at first due to none of the 40 pins having any labels to indicate what pin it may be. This means that it is recommended that you always follow a pinout guide to identify pins and double-check your wiring before powering up the Pi to avoid any shorts. Other than that, the wiring for this project is simple as aside from the main sensor, not a lot of extra components are needed. A circuit diagram of the BME280 Raspberry Pi wiring is also featured below.



- VCC/VIN to Raspberry Pi’s 3.3V (Pin 01)
- GND to Raspberry Pi’s GND (Pin 09)
- Serial Clock (SCL) to GPIO03 (Pin 05)
- Serial Data (SDL) to GPIO02 (Pin 03)
- You’re now prepared to run the code and start your project!
Project Code
Before you start uploading the code, you should run the following command in the Raspberry Pi’s Terminal to identify the i2c address of your BME280 sensor:
i2cdetect -y 1
From there, go to line 7 of the project code and if needed, change the last two digits of the address to match up with what you have received above. Another important point is that in order for your sensor to communicate with your Pi, you must enable i2c interfacing via the Raspberry Pi’s configuration settings. If i2c interfacing is not enabled, the Pi will not be able to recognize the sensor and therefore, your code will not work. BME280 Raspberry Pi python is as follows:



import smbus
import time
from ctypes import c_short
from ctypes import c_byte
from ctypes import c_ubyte
DEVICE = 0x77 #This address may have to change depending on your BME280 sensor.
bus = smbus.SMBus(1) # Version 2 Pi, Pi 2 & Pi 3 uses bus 1. Rev 1 Pi uses bus 0.
def getShort(data, index):
# return two bytes from data as a signed 16-bit value
return c_short((data[index+1] << 8) + data[index]).value
def getUShort(data, index):
# return two bytes from data as an unsigned 16-bit value
return (data[index+1] << 8) + data[index]
def getChar(data,index):
# return one byte from data as a signed char
result = data[index]
if result > 127:
result -= 256
return result
def getUChar(data,index):
# return one byte from data as an unsigned char
result = data[index] & 0xFF
return result
def readBME280ID(addr=DEVICE):
# Chip ID Register Address
REG_ID = 0xD0
(chip_id, chip_version) = bus.read_i2c_block_data(addr, REG_ID, 2)
return (chip_id, chip_version)
def readBME280All(addr=DEVICE):
# Register Addresses
REG_DATA = 0xF7
REG_CONTROL = 0xF4
REG_CONFIG = 0xF5
REG_CONTROL_HUM = 0xF2
REG_HUM_MSB = 0xFD
REG_HUM_LSB = 0xFE
# Oversample setting - page 27
OVERSAMPLE_TEMP = 2
OVERSAMPLE_PRES = 2
MODE = 1
# Oversample setting for humidity register - page 26
OVERSAMPLE_HUM = 2
bus.write_byte_data(addr, REG_CONTROL_HUM, OVERSAMPLE_HUM)
control = OVERSAMPLE_TEMP<<5 | OVERSAMPLE_PRES<<2 | MODE
bus.write_byte_data(addr, REG_CONTROL, control)
# Read blocks of calibration data from EEPROM
# See Page 22 data sheet
cal1 = bus.read_i2c_block_data(addr, 0x88, 24)
cal2 = bus.read_i2c_block_data(addr, 0xA1, 1)
cal3 = bus.read_i2c_block_data(addr, 0xE1, 7)
# Convert byte data to word values
dig_T1 = getUShort(cal1, 0)
dig_T2 = getShort(cal1, 2)
dig_T3 = getShort(cal1, 4)
dig_P1 = getUShort(cal1, 6)
dig_P2 = getShort(cal1, 8)
dig_P3 = getShort(cal1, 10)
dig_P4 = getShort(cal1, 12)
dig_P5 = getShort(cal1, 14)
dig_P6 = getShort(cal1, 16)
dig_P7 = getShort(cal1, 18)
dig_P8 = getShort(cal1, 20)
dig_P9 = getShort(cal1, 22)
dig_H1 = getUChar(cal2, 0)
dig_H2 = getShort(cal3, 0)
dig_H3 = getUChar(cal3, 2)
dig_H4 = getChar(cal3, 3)
dig_H4 = (dig_H4 << 24) >> 20
dig_H4 = dig_H4 | (getChar(cal3, 4) & 0x0F)
dig_H5 = getChar(cal3, 5)
dig_H5 = (dig_H5 << 24) >> 20
dig_H5 = dig_H5 | (getUChar(cal3, 4) >> 4 & 0x0F)
dig_H6 = getChar(cal3, 6)
# Wait in ms (Datasheet Appendix B: Measurement time and current calculation)
wait_time = 1.25 + (2.3 * OVERSAMPLE_TEMP) + ((2.3 * OVERSAMPLE_PRES) + 0.575) + ((2.3 * OVERSAMPLE_HUM)+0.575)
time.sleep(wait_time/1000) # Wait the required time
# Read temperature/pressure/humidity
data = bus.read_i2c_block_data(addr, REG_DATA, 8)
pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4)
hum_raw = (data[6] << 8) | data[7]
#Refine temperature
var1 = ((((temp_raw>>3)-(dig_T1<<1)))*(dig_T2)) >> 11
var2 = (((((temp_raw>>4) - (dig_T1)) * ((temp_raw>>4) - (dig_T1))) >> 12) * (dig_T3)) >> 14
t_fine = var1+var2
temperature = float(((t_fine * 5) + 128) >> 8);
# Refine pressure and adjust for temperature
var1 = t_fine / 2.0 - 64000.0
var2 = var1 * var1 * dig_P6 / 32768.0
var2 = var2 + var1 * dig_P5 * 2.0
var2 = var2 / 4.0 + dig_P4 * 65536.0
var1 = (dig_P3 * var1 * var1 / 524288.0 + dig_P2 * var1) / 524288.0
var1 = (1.0 + var1 / 32768.0) * dig_P1
if var1 == 0:
pressure=0
else:
pressure = 1048576.0 - pres_raw
pressure = ((pressure - var2 / 4096.0) * 6250.0) / var1
var1 = dig_P9 * pressure * pressure / 2147483648.0
var2 = pressure * dig_P8 / 32768.0
pressure = pressure + (var1 + var2 + dig_P7) / 16.0
# Refine humidity
humidity = t_fine - 76800.0
humidity = (hum_raw - (dig_H4 * 64.0 + dig_H5 / 16384.0 * humidity)) * (dig_H2 / 65536.0 * (1.0 + dig_H6 / 67108864.0 * humidity * (1.0 + dig_H3 / 67108864.0 * humidity)))
humidity = humidity * (1.0 - dig_H1 * humidity / 524288.0)
if humidity > 100:
humidity = 100
elif humidity < 0:
humidity = 0
return temperature/100.0,pressure/100.0,humidity
def main():
(chip_id, chip_version) = readBME280ID()
print "Chip ID :", chip_id
print "Version :", chip_version
temperature,pressure,humidity = readBME280All()
print "Temperature : ", temperature, "C"
print "Pressure : ", pressure, "hPa"
print "Humidity : ", humidity, "%"
if __name__=="__main__":
main()
Code Explanation
A large portion of this project code is simply reading and transforming raw data that is coming from the BME280 from bytes of data to legible environmental values. Hence, in this explanation, not every single line of the code will be covered but the main, important blocks will be explained.
In the first block of the code, several libraries and packages are defined including the smbus library which basically allows for i2c communication with the sensor, the time library for delay/timing purposes and the ctypes library which is responsible for reading the raw data and running all the mathematics behind analyzing it. Then, the device address is declared and we set up the i2c interface using the smbus.SMbus function.
In the next four blocks of code, the raw data is received and processed with essential Python functions that can read from bytes. Then, the readBME280ID function is first used to get the ID of the BME280 device and the readBME280All function is subsequently used to establish oversampling settings, ensure proper calibration of the sensor and read the raw data into word values. In addition, calculations are later performed to refine the environmental data values and of course, towards the end of the code, print those values to the monitor (as seen by the main() block).
Ending
From this article, I hope that you are now confident in using the BME280 environmental sensor with the Raspberry Pi as quite frankly, the i2c interface is so common with lots of sensors. Plus, this project code offers a unique side to how one can extract and transform raw data without using a pre-made library. However, with all the open-source resources available online, I undoubtedly would recommend utilizing packages, modules and libraries for your projects simply because it can save you a lot of time on the software development aspect. Plus, libraries may offer additional features that you may have had trouble building yourself or features that you did not realize were available. Regardless, with the i2c interface allowing for many devices to be connected on the same bus, you could use this project as a stepping stone to work with even more modules and sensors!
You Might Be Interested


When conceiving an electronic project, the Raspberry Pi might be your ideal choice to embark on realizing your goals. Once you’ve purchased a Raspberry Pi


In this Raspberry Pi project, we built a distance detector using the HC-SR04 ultrasonic sensor. The Raspberry Pi served as the microcontroller, programmed to receive


This tutorial guides precision drone landing with Raspberry Pi, covering ROS Kinetic, MAVROS, and PX4 Autopilot setup. It explains Gazebo config, showcasing landmark recognition via


This article provides a comprehensive guide on mining Bitcoin using a Raspberry Pi, offering an accessible entry point for cryptocurrency enthusiasts. It covers essential steps,


The Raspberry Pi obstacle avoidance car project uses GPIO, Python, and network communication for a DIY robotics project. It employs ultrasonic sensors and motor modules


Raspberry Pi, known for versatility, offers diverse operating systems. Raspberry Pi OS is the official, optimized choice. Ubuntu has MATE and Server versions. LibreELEC is


Raspberry Pi, a revolutionary single-board computer introduced by the Raspberry Pi Foundation, has become a global sensation, initially designed for educational purposes. With its integrated


This guide provides a comprehensive overview of how to use SSH (Secure Shell) for remote management of a Raspberry Pi. It covers enabling SSH via