So, this was more a learning of how to get things done (=start) with Python, than anything else. I was already using BME280 sensors long time ago, and bought one 680 for test. Until recently I was only using Arduinos, ESPs and had zero Python knowledge, so I bought a Raspberry Pi to learn it and this "project" seemed like a good thing to start.
The article will show how to read a BME680 and show data from it on inexpensive OLED screen. This can be done on unit with or without screen, I used only ssh and VNC virtual remote control to make it. I suspect you have basic knowledge of I2C and Python, with control of Raspberry OS remotely via SSH or VNC. A lot of pages was already written about it and its not the purpose of this article to learn about it.
First, download a Github zipfiles of sensor driver in link below.
https://github.com/pimoroni/bme680-python
If you want to "git" it its fine by me, I like to have files for future use and find it easier when I know where I download and install my things.
After download, I unzipped and renamed folder to something less complicated. A file manager in VNC virtual machine is what I used. Then I put it into /home/pi/python_files/ folder. You can use whatever folder you want but I like to keep all python libraries in one place.
With next commands
cd ~/python_files/bme680/
sudo sh install.sh
go into "bme680" folder and install library for future use. You can look in examples folder and run few python scripts to learn what kind of readings its possible to get from sensor.
Next, we need to install oled library. Go to Luma oled website and follow the instructions for python 3 installation
https://luma-oled.readthedocs.io/en/latest/install.html
sudo -H pip3 install --upgrade luma.oled
You can download quite a few examples for this library in
https://github.com/rm-hull/luma.examples
The documentation for library is not very clear for beginner and I found lack of important info like how to change font size, draw lines, rotate text etc. Therefore I had to dig into some of the examples to search for what I needed for my script.
When running OLED examples, for SH1106 oled you need to specify type of display with command -d
python3 demo.py -d sh1106 -r 2
I am using display upside down so -r 2 rotates it by 180 degrees - it was easier for me to mount it on a PCB.
Next, the python script. It simply uses a function that reads temperature, pressure and humidity. Not the air quality yet, as the air quality sensor heats up the whole BME680 and it alters the temperature reading. It would be useful to read once in like 5 minutes or so, but the sensor also needs time to stabilise itself with longer heating.
In this configuration it reads data every 1 second. If it it too fast for you then change a time.sleep(1) for whatever number you need. Time is in seconds by the way.
from luma.core.interface.serial import i2c
from luma.core.render import canvas
from luma.oled.device import sh1106
import time
import bme680
from PIL import ImageFont
try:
sensor = bme680.BME680(bme680.I2C_ADDR_PRIMARY)
except IOError:
sensor = bme680.BME680(bme680.I2C_ADDR_SECONDARY)
# These oversampling settings can be tweaked to
# change the balance between accuracy and noise in
# the data.
sensor.set_humidity_oversample(bme680.OS_2X)
sensor.set_pressure_oversample(bme680.OS_4X)
sensor.set_temperature_oversample(bme680.OS_8X)
sensor.set_filter(bme680.FILTER_SIZE_3)
device = sh1106(i2c(port=1, address=0x3C), rotate = 2)
sensor.data.temperature = 0
sensor.data.pressure = 0
sensor.data.humidity = 0
#print('Polling:')
def temp():
return " %.2f C" \
% (sensor.data.temperature)
def humi():
return " %.2f %%" \
% (sensor.data.humidity)
def pres():
return "%.2f hPa" \
% (sensor.data.pressure)
while True:
'''
if sensor.get_sensor_data():
output = '{0:.2f} C, {1:.2f} hPa, {2:.3f} %RH'.format(
sensor.data.temperature,
sensor.data.pressure,
sensor.data.humidity)
print(output)
'''
sensor.get_sensor_data()
with canvas(device) as draw:
#draw.rectangle(device.bounding_box, outline="white", fill="black")
draw.text((10, 5), "TEMP : ", fill="white")
draw.text((50, 5), temp(), fill="white")
draw.text((10, 25), "HUMI : ", fill="white")
draw.text((50, 25), humi(), fill="white")
draw.text((10, 45), "PRES : ", fill="white")
draw.text((50, 45), pres(), fill="white")
time.sleep(1)
First it loads necessary python libraries and functions. After that, a I2C communication adress and sensor parameters are put, which removes some of the noise from measurement.
device = sh1106(i2c(port=1, address=0x3C), rotate = 2)
This line specifies the display connection. sh1106 is type of oled display I am using, with I2C on port one, with adress 0x3c and rotation of 180 degrees. For zero degrees use 0 instead.
These values are storing the readouts from the sensor, and are there to be readable from anywhere in program
sensor.data.temperature = 0
sensor.data.pressure = 0
sensor.data.humidity = 0
Next 3 def parts are subprograms which formats the readout of the sensor to value necessary for Luma driver to write on screen. I am sure there is better way input data directly into the viewing commands of LUMA, but I did not found it yet.
After while true there is commented this part of program
if sensor.get_sensor_data():
output = '{0:.2f} C, {1:.2f} hPa, {2:.3f} %RH'.format(
sensor.data.temperature,
sensor.data.pressure,
sensor.data.humidity)
print(output)
If it is necessary for you to get readout from sensor also in commandline, you can remove the triple quote characters before and after it. This can be useful eg. if you want to see it remotely in ssh terminal.
Next the
sensor.get_sensor_data()
gets the data from sensor, fills up three variables with temperature, humidity and pressure and shows it with commands
#draw.rectangle(device.bounding_box, outline="white", fill="black")
draw.text((10, 5), "TEMP : ", fill="white")
draw.text((50, 5), temp(), fill="white")
draw.text((10, 25), "HUMI : ", fill="white")
draw.text((50, 25), humi(), fill="white")
draw.text((10, 45), "PRES : ", fill="white")
draw.text((50, 45), pres(), fill="white")
on the OLED screen. The draw.text function uses beginning position of x and y pixel dimensions as a start line. Then a text between double quotes is shown, and fill="white" chooses a white text on black background. This is a multicolour display library so it can do also colored text with other displays.
At the end a
time.sleep(1)
sleeps the script for 1 second and whole readout starts again.
Now the connection -
If only my display had the same pinout as the sensor, the connection would be somewhat easier. It is a good idea to turn off RPi before connecting anything but if you are sure of yourself go ahead for a live connection, I2C bus does not care about live connect or disconnect. You need to use following pins on Raspberry GPIO
3.3V pin - #1
GND pin - #9
SCL pin - #5
SDA pin - #3
By using protoboard, it is easy to connect in less than a minute. Put these connections in order above and you have 99% chance it will fit other I2C sensors and displays as well. I had a display with VCC and GND pins switched so I made a pcb jig to break out the I2C and also with headers to slot sensors into.
sudo raspi-config
You can look if two devices are connected with
i2cdetect -y 1
It will show adresses of all deviced on I2C bus. My two are 3c and 77.
Save the code as file with.py extension. Next, locate yourself into folder with script and run it with
python3 bme_680_oled.py
Your screen should turn on and show sensor values, with refresing each appx. 1.5 second. It is not CPU hungry and can stay on forever.
If you want to run it in the background, put a "&" character after the command
python3 bme_680_oled.py &
So, if you are here, the journey is not at the end. From here, you can expand this to show more data, log these to SD card or TXT file, use other I2C device llike ADC to show measured value...
Keep on hacking.
Comments
Please log in or sign up to comment.