Raspberry Pi with BME280: Temperature, Humidity, and Pressure Data Logger (Python)

In this project, you’re going to build a data logger with the Raspberry Pi and the BME280 sensor that automatically stores data on temperature, humidity, pressure, and the corresponding timestamp on a .txt file. This project gives you the basics of data collection, which is useful in many different applications that use sensors. You can apply the concepts from this project to any sensor.

Raspberry Pi with BME280 Temperature Humidity and Pressure Data Logger Python

New to the BME280 environmental sensor? Read our getting started guide: Raspberry Pi: BME280 Temperature, Humidity, and Pressure Sensor (Python).

Table of Contents

Prerequisites

Before continuing with this tutorial, check the following prerequisites.

  1. Get familiar with the Raspberry Pi board—if you’re not familiar with the Raspberry Pi, you can read our Raspberry Pi Getting Started Guide here.
  2. You must know how to run and create Python files on your Raspberry Pi. We like to program our Raspberry Pi via SSH using an extension on VS Code. We have a detailed tutorial about that subject: Programming Raspberry Pi Remotely using VS Code (Remote-SSH).
  3. Know how to use the Raspberry Pi GPIOs so that you know how to wire the circuit properly. Read the following tutorial: Raspberry Pi Pinout Guide: How to use the Raspberry Pi GPIOs?

Introducing BME280 Sensor Module

The BME280 sensor module reads barometric pressure, temperature, and humidity. Because pressure changes with altitude, you can also estimate altitude. There are several versions of this sensor module. We’re using the module illustrated in the figure below.

BME280 Sensor I2C Module reads pressure, temperature, and humidity

This sensor communicates using I2C communication protocol, so the wiring is very simple. You can use the default Raspberry Pi I2C pins as shown in the following table:

BME280Raspberry Pi
Vin3.3V
GNDGND
SCLGPIO 3
SDAGPIO 2

Learn more about the Raspberry Pi GPIOs: Raspberry Pi Pinout Guide: How to use the Raspberry Pi GPIOs?

Parts Required

Raspberry Pi with BME280 Sensor

Here’s a list of parts you need to build the circuit:

You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!

Enable I2C on the Raspberry Pi

I2C communication is not enabled by default. You need to enable it manually. Open a terminal window on your Raspberry Pi and type the following command:

sudo raspi-config

The following menu will open. Select Interface Options.

Raspberry Pi Enable I2C communication

Then, select the I2C option.

Raspberry Pi Enable I2C communication

Finally, enable I2C by selecting Yes.

Raspberry Pi Enable I2C communication

I2C should be successfully enabled.

Raspberry Pi I2C communication enabled

After enabling I2C, reboot your Raspberry Pi by running the following command:

sudo reboot

Wiring the BME280 to the Raspberry Pi

Wire the BME280 to the Raspberry Pi default I2C pins.

Raspberry Pi Wiring BME280 Diagram
BME280Raspberry Pi
Vin3.3V
GNDGND
SCLGPIO 3
SDAGPIO 2

Getting the Sensor I2C Address

With the sensor connected to the Raspberry Pi, let’s check if the sensor is connected properly by searching for its I2C address.

Open a Terminal window on your Raspberry Pi and run the following command:

sudo i2cdetect -y 1

It should show your sensor I2C address:

BME280 Getting BME280 I2C Address

Install BME280 Library

There are several libraries compatible with the Raspberry Pi to read from the BME280 sensor. We’ll be using the RPi.BME280 library. Besides having functions to read from the BME280 sensor, it also has the option to get a timestamp associated with each reading. This makes it extremely simple to use for data logging applications.

This library is available to install through PIP (a package manager for Python packages).

First, install or upgrade pip by running the following command:

sudo pip install --upgrade pip

Then, run the following command to install the library using pip:

sudo pip install RPI.BME280

Installing the pytz library

The RPi.280 library has an option to get the timestamp associated with each reading. However, that timestamp will be in UTC. If you want to convert it to a specific timezone, you can use the pytz library. Open a terminal window on your Raspberry Pi and run the following command to install the pytz library.

pip install pytz

Raspberry Pi BME280 Data Logger Python Script

Create a new Python file on your Raspberry Pi called bme280_data_logger.py and copy the following code.

# Complete Project Details: https://RandomNerdTutorials.com/raspberry-pi-bme280-data-logger/

import smbus2
import bme280
import os
import time
import pytz

# BME280 sensor address (default address)
address = 0x76

# Initialize I2C bus
bus = smbus2.SMBus(1)

# Load calibration parameters
calibration_params = bme280.load_calibration_params(bus, address)

# create a variable to control the while loop
running = True

# Check if the file exists before opening it in 'a' mode (append mode)
file_exists = os.path.isfile('sensor_readings_bme280.txt')
file = open('sensor_readings_bme280.txt', 'a')

# Write the header to the file if the file does not exist
if not file_exists:
    file.write('Time and Date, temperature (ºC), temperature (ºF), humidity (%), pressure (hPa)\n')

# loop forever
while running:
    try:
        # Read sensor data
        data = bme280.sample(bus, address, calibration_params)

        # Extract temperature, pressure, humidity, and corresponding timestamp
        temperature_celsius = data.temperature
        humidity = data.humidity
        pressure = data.pressure
        timestamp = data.timestamp

        # Adjust timezone
        # Define the timezone you want to use (list of timezones: https://gist.github.com/mjrulesamrat/0c1f7de951d3c508fb3a20b4b0b33a98)
        desired_timezone = pytz.timezone('Europe/Lisbon')  # Replace with your desired timezone

        # Convert the datetime to the desired timezone
        timestamp_tz = timestamp.replace(tzinfo=pytz.utc).astimezone(desired_timezone)

        # Convert temperature to Fahrenheit
        temperature_fahrenheit = (temperature_celsius * 9/5) + 32

        # Print the readings
        print(timestamp_tz.strftime('%H:%M:%S %d/%m/%Y') + " Temp={0:0.1f}ºC, Temp={1:0.1f}ºF, Humidity={2:0.1f}%, Pressure={3:0.2f}hPa".format(temperature_celsius, temperature_fahrenheit, humidity, pressure))

        # Save time, date, temperature, humidity, and pressure in .txt file
        file.write(timestamp_tz.strftime('%H:%M:%S %d/%m/%Y') + ', {:.2f}, {:.2f}, {:.2f}, {:.2f}\n'.format(temperature_celsius, temperature_fahrenheit, humidity, pressure))

        time.sleep(10)

    except KeyboardInterrupt:
        print('Program stopped')
        running = False
        file.close()
    except Exception as e:
        print('An unexpected error occurred:', str(e))
        running = False
        file.close()

View raw code

How the Code Works

Here’s a quick description of what the code does:

  • it saves sensor readings and the corresponding timestamp to a file called sensor_readings_bme280.txt.
  • but first, it checks if a file called sensor_readings_bme280.txt already exists (this is to prevent writing the file header multiple times when the program resets or stops/starts).
  • if the file doesn’t exist, it will create a new file.
  • there’s an infinite loop, in which you get temperature, humidity, and pressure readings and the corresponding timestamp in UTC.
  • it converts the timestamp to your desired timezone, and then it writes all the data to the file, including the timestamp.
  • the program keeps running and writing to the file until you tell the program to stop with a keyboard interrupt or until the program stops for any other reason. In that case, we close the file.

Continue reading for a more detailed explanation of the code, or skip to the demonstration section,

The code is very similar to the one in THIS PROJECT (but it adds some lines for data logging).

Import the Required Libraries

First, you need to import the required libraries. The smbus2 is necessary to use I2C communication, the RPi.BME280 to get readings from the BME280 sensor, the time module to add delays to your code, the os module that will allow us to interact with the operating system (in our case, we need to check if a file already exists on the filesystem), and finally, the pytz module to adjust the timezone.

import smbus2
import bme280
import os
import time
import pytz

Initialize the BME280 Sensor

We set the default address of the BME280 sensor to 0x76. This is the address that the sensor communicates with over the I2C bus (check this previous section).

# BME280 sensor address (default address)
address = 0x76

We then initialize the I2C bus using the smbus2.SMBus(1) command.

# Initialize I2C bus
bus = smbus2.SMBus(1)

Then, we initialize the sensor by setting the I2C bus and its I2C address.

# Load calibration parameters
calibration_params = bme280.load_calibration_params(bus, address)

File Handling

The following line, checks if a file called sensor_readings_bme280.txt already exists (it will look for it in the project folder path). If the file already exists, the file_exists variable will be True. Otherwise, it will be False.

file_exists = os.path.isfile('sensor_readings_bme280.txt')

Then, it opens the file in ‘a’ (append) mode. If the file doesn’t exist yet, it will automatically create the file before opening it.

file = open('sensor_readings_bme280.txt', 'a')

When using the open() function, you can specify different modes to control how the file is accessed and used. When writing to the file, the most popular methods are ‘w’ (write) and ‘a’ (append).

Write vs Append mode: the write mode opens the file for writing and empties the file if it already exists. The append mode opens the file for writing and it appends new data to the end of the file. If you want to have a record of your readings over time, the best mode is append so that you can keep all your previous data.

If the file doesn’t exist yet, the code writes a header line to the file, containing the column names. We’ll save date and time first, then, the temperature in Celsius and Fahrenheit, the humidity, and finally the pressure.

# Write the header to the file if the file does not exist
if not file_exists:
    file.write('Time and Date, temperature (ºC), temperature (ºF), humidity (%), pressure (hPa)\n')

Notice that we add a \n at the end of the String. The \n tells Python to start the next display text on the next line, known as a newline.

Getting Sensor Readings

Then, we have a while loop that is always running as long as the running variable is True. We have set that variable to True at the beginning of the code. The running variable will change to False, when the execution of the program is stopped by a keyboard interrupt, or when an unexpected error occurs.

while running:

Inside the loop, the try block attempts to read from the sensor: we use the bme280.sample(bus, address, calibration_params) function to read sensor data.

data = bme280.sample(bus, address, calibration_params)

We extract the temperature, humidity, pressure, and the corresponding timestamp from the data returned by the sensor and save each reading on a variable: temperature_celsius, humidity, pressure, and timestamp.

# Extract temperature, pressure, humidity, and corresponding timestamp
temperature_celsius = data.temperature
humidity = data.humidity
pressure = data.pressure
timestamp = data.timestamp

Adjust the timezone

As we mentioned previously, the timestamp returned by the data object of the BME280 library comes in UTC format, by default. We can use the pytz library functions to convert that time to our desired timezone.

First, specify your desired timezone on the following line (you can find a list of all supported timezones here). We live in Portugal, so our timezone is ‘Europe/Lisbon’.

desired_timezone = pytz.timezone('Europe/Lisbon')  # Replace with your desired timezone

Then, convert the timestamp to your desired timezone as follows.

# Convert the datetime to the desired timezone
timestamp_tz = timestamp.replace(tzinfo=pytz.utc).astimezone(desired_timezone)

The new timestamp adjusted to your specific timezone is saved on the timestamp_tz variable.

Converting the Temperature to Fahrenheit

We convert the temperature from Celsius to Fahrenheit as follows (the temperature in Fahrenheit is saved on the temperature_fahrenheit variable.

# Convert temperature to Fahrenheit
temperature_fahrenheit = celsius_to_fahrenheit(temperature_celsius)

Print the Readings

Finally, we print the readings formatted with two decimal places and the corresponding timestamp:

# Print the readings
 print(timestamp_tz.strftime('%H:%M:%S %d/%m/%Y') + " Temp={0:0.1f}ºC, Temp={1:0.1f}ºF, Humidity={2:0.1f}%, Pressure={3:0.2f}hPa".format(temperature_celsius, temperature_fahrenheit, humidity, pressure))

The strftime() function converts a datetime, time, and date object into a string object according to the given format. In our case, we’re using the format hour:minutes:seconds for the time and day/month/year for the date.

  • %H: decimal number represents the Hour, 24-hour clock format(00 to 23)
  • %M: decimal number that represents the Minute (01 to 59)
  • %S: decimal number that represents the Second (01 to 59)
  • %d: day of the month as a zero-padded decimal number. (01 to 31)
  • %m: month as a zero-padded decimal number(01 to 12)
  • %Y: decimal number represents the Year with the century.

For different date and time formats, check all the format codes here.

Writing Data to the File

Then, we use the write() function on the file object to actually write data to the file. We print the timestamp, temperature in Celsius, temperature in Fahrenheit, humidity, and pressure.

file.write(timestamp_tz.strftime('%H:%M:%S %d/%m/%Y') + ', {:.2f}, {:.2f}, {:.2f}, {:.2f}\n'.format(temperature_celsius, temperature_fahrenheit, humidity, pressure))

New readings are printed and written to the file every 10 seconds. You can adjust the time between each sample by passing a different number to the sleep() method.

time.sleep(10)

Interrupt and Exception Handling

If you interrupt the program by pressing Ctrl+C, the code catches the KeyboardInterrupt exception, prints a message indicating program termination, and then closes the file before setting running to False to exit the loop.

except KeyboardInterrupt:
    print('Program stopped')
    running = False
    file.close()

Important: you need to call file.close() so that all data is written to the file.

We proceed in a similar way if any other error or exception occurs.

except Exception as e:
    print('An unexpected error occurred:', str(e))
    running = False
    file.close()

Demonstration

Save your Python file. Then run it on your Raspberry Pi. Run the following command on the directory of your file:

python bme280_data_logger.py

You’ll start getting new temperature and humidity readings on the terminal window, and a new file called sensors_readings_bme280.txt will be created in your project folder. If you’re using VS Code, you’ll see the newly created file on the file explorer (don’t open it yet).

Raspberry Pi Datalogger VS Code BME280

Let the code run for a while so it gathers a considerable amount of data. Then, stop the execution of the code by pressing CTRL+C.

Raspberry Pi VS Code Stop Python program

Now, you can open the sensors_readings_bme280.txt file and see all the data it collected. You can run the program again to gather more data and it won’t overwrite the previous data.

Sensor Readings Data Logger Raspberry Pi VS Code

If you’re using PuTTY, navigate to your project folder. List all files inside the project folder using the ls command and note that a file called sensor_readings_bme280.txt is listed.

Raspberry Pi List Files Putty

To open that file, use the following command:

cat sensor_readings_bme280.txt

Then, you’ll see all data gathered by your sensor with the corresponding timestamps.

Raspberry Pi Datalogger File txt Putty

Wrapping Up

In this project, you’ve learned a very useful concept: data logging. Now you can use data logging in other monitoring projects or use other sensors. We have guides for other sensors that you may find useful:

We hope you found this tutorial useful. If you’re quite new to the Raspberry Pi, check the following tutorials:

You can check all our Raspberry Pi projects on the following link:



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Recommended Resources

Build a Home Automation System from Scratch » With Raspberry Pi, ESP8266, Arduino, and Node-RED.

Home Automation using ESP8266 eBook and video course » Build IoT and home automation projects.

Arduino Step-by-Step Projects » Build 25 Arduino projects with our course, even with no prior experience!

What to Read Next…


Enjoyed this project? Stay updated by subscribing our newsletter!

4 thoughts on “Raspberry Pi with BME280: Temperature, Humidity, and Pressure Data Logger (Python)”

  1. Hi.
    If the time on the raspberrypi is correct,
    then we might as well use

    import datetime

    Read sensor data

    data = bme280.sample(bus, address, calibration_params)

    timenow = datetime.datetime.now().strftime(“%a %d-%m-%Y %H:%M:%S”)

    # Print the readings
    print((timenow) + " Temp={0:0.1f}ºC, Temp={1:0.1f}ºF, Humidity={2:0.1f}%, Pressure={3:0.2f}hPa".format( temperature_celsius, temperature_fahrenheit, humidity, pressure))

    # Save time, date, temperature, humidity, and pressure in .txt file
    file.write(timenow + ', {:.2f}, {:.2f}, {:.2f}, {:.2f}\n'.format(temperature_celsius, temperature_fahrenheit, humidity, pressure))

    instead of pytz library, or what do you think.

    Reply
  2. Olá!

    Muito bom, rodou fácil, ótimo tutorial!

    Seria interessante também um projeto com interface RPi + python + MySql e PHP.

    Abraços aqui do Brasil!

    ATP

    Reply

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.