Showing posts with label BME680. Show all posts
Showing posts with label BME680. Show all posts

Monday, 17 January 2022

Raspberry Pi Pico Explorer

The Raspberry Pi Pico Explorer is a plug-in board that provides a number of built-in devices including a 240x240 colour display, four buttons, a piezo buzzer, two I2S Breakout Garden sockets and sockets connected to the Pico's pins plus a small solderless breadboard.

A Raspberry Pi Pico with pre-soldered headers makes development easy without needing to solder delicate electronics.


Assembly was easy, care being taken to make sure all the pins aligned with the corresponding holes in the socket on the board.

Updating the Firmware

To use the components on the Explorer board, Pimoroni has created a custom UF2 file, including the required drivers. As of 03/01/2022 the latest version of the Pimoroni firmware (0.3.2) does not work, use version 0.3.1. This might not be the case for other boards.

Use the Update Firmware option on Thonny to return to a vanilla flavour and the Blink example if the current Pimoroni version does not work as expected. Select an earlier version of the UF2 and try again.

There is an example that measures the cpu temperature which is then displayed as text and a bar graph.
The next stage was to make use of the Breakout Garden sockets and add some breakout units. 
To provide some measurements, a BME680 environmental sensor was added to the right hand socket. In the left hand socket is a rotary encoder with a built in RGB LED.
The two sockets allow the addition of two breakouts. PIMORONI have a range of breakouts, including additional convertors that allow connection to STEMMA or Qwiic devices.

The BME680 breakout is an older model, the current version includes a built in STEMMA or Qwiic connector.
The example program was modified to get the temperature, pressure and humidity from the BME680 sensor plus the cpi temperature.

The code to set the encoder's RGB LED colour was also added.
The graph code was designed to display both the cpu temperature and the sensor temperature.

Finally a maximum and minimum sensor temperature was added to the display.

Code

Do check the indenting as Python uses the indenting to define the structure of the program.

import machine
import utime

from breakout_bme68x import BreakoutBME68X
from pimoroni_i2c import PimoroniI2C

PINS_BREAKOUT_GARDEN = {"sda": 4, "scl": 5}
PINS_PICO_EXPLORER = {"sda": 20, "scl": 21}

i2c = PimoroniI2C(**PINS_PICO_EXPLORER)

# Pico Explorer boilerplate
import picoexplorer as display
width = display.get_width()
height = display.get_height()
display_buffer = bytearray(width * height * 2)
display.init(display_buffer)

# BME68x configuration
bme = BreakoutBME68X(i2c)
#bme.configure(FILTER_COEFF_3, STANDBY_TIME_1000_MS, OVERSAMPLING_16X, OVERSAMPLING_2X, OVERSAMPLING_1X)

# reads from Pico's temp sensor and converts it into a more manageable number
sensor_temp = machine.ADC(4)
conversion_factor = 3.3 / (65535)

# Set up text areas
blockHeight = 33
textBlocks = 8
textArea=list()
for x in range(textBlocks):
    if(textBlocks>4):
        if(x<4):
            textArea.append([10,(blockHeight*x)+1,120,blockHeight])        
        else:
            textArea.append([120,(blockHeight*(x-4))+1,120,blockHeight])
    else:
        textArea.append([120,(blockHeight*x)+1,120,blockHeight])

# Set up background and text pens
background_pen = display.create_pen(0,0,0)
cpu_temp_pen = display.create_pen(255, 64, 64)
bme_temp_pen = display.create_pen(64, 255, 64)
pressure_pen = display.create_pen(64, 64, 255)
humidity_pen = display.create_pen(0, 255, 255)

# Define number of pixels for the graph points
graph_element_size = 1
graph_y_scale = 4

def drawTemp(i,cpu_temp,sensor_temp):
    diff = abs((cpu_temp*graph_element_size)-(sensor_temp*graph_element_size))
    if(diff<graph_element_size):
        display.set_pen(255,255,0)
        display.rectangle(i, height - (cpu_temp * graph_y_scale), graph_element_size,graph_element_size)
    else:
        display.set_pen(cpu_temp_pen)
        display.rectangle(i, height - (cpu_temp * graph_y_scale), graph_element_size,graph_element_size)
        display.set_pen(bme_temp_pen)
        display.rectangle(i, height - (int(sensor_temp) * graph_y_scale), graph_element_size,graph_element_size)
    
def writeInBlock(text, location, pen, paper, size):
    # Draw background to clear display area
    display.set_pen(paper)
    display.rectangle(location[0],location[1],location[2],location[3])
    # Write text in location one pixel left and down
    display.set_pen(pen)
    display.text(text, location[0]+1,location[1]+1,100,size)
    
# Initialise run variables
i = 0
count = 0
max_temp = 0
min_temp = 100

while True:
    # the following two lines do some maths to convert the number from the temp sensor into celsius
    reading = sensor_temp.read_u16() * conversion_factor
    temperature = round(27 - (reading - 0.706) / 0.001721)
    bmetemp, pressure, humidity, gas_resistance, status, gas_index, meas_index = bme.read()
    max_temp=max(max_temp,bmetemp)
    min_temp=min(min_temp,bmetemp)
    
    # Clear the display and reset counter if the graph reaches the right hand side
    if i >= (width + 1):
        i = 0
        display.set_pen(0, 0, 0)
        display.clear()

    # Draw graph element
    drawTemp(i,temperature,bmetemp)

    writeInBlock("{:.0f}".format(temperature) + "c", textArea[0],cpu_temp_pen,background_pen,4)
    writeInBlock("{:.0f}".format(bmetemp) + "c", textArea[1],bme_temp_pen,background_pen,4)
    writeInBlock("{:.0f}".format(pressure/1000) + "kPa", textArea[2],pressure_pen,background_pen,3)
    writeInBlock("{:.0f}".format(humidity)+"%", textArea[3],humidity_pen,background_pen,4)
    writeInBlock("Mx {:.0f}".format(max_temp) + "c", textArea[4],bme_temp_pen,background_pen,3)
    writeInBlock("Mn {:.0f}".format(min_temp) + "c", textArea[5],bme_temp_pen,background_pen,3)
    # time to update the display
    display.update()

    # waits for 5 seconds
    utime.sleep(1)

    # Set next graph element location
    i+=graph_element_size



Sunday, 5 April 2020

Pimoroni BME680 Environment sensor

So, having a display is all well and good, but now you need something to display.

The Pimoroni BME680 breakout board has a Bosch BME680 temperature, pressure, humidity and air quality sensor.

The following code assumes you have the BME680 and the SH1106 OLED display on the default I2C addresses.

import os
import time
import datetime
import sys

from PIL import Image

from PIL import ImageFont
from PIL import ImageDraw

import bme680

from luma.core.interface.serial import i2c
from luma.oled.device import sh1106
from luma.core.render import canvas

TEMPERATURE_UPDATE_INTERVAL = 0.1  # in seconds


# Temperature offset 

TEMP_OFFSET = 0.0

print("Temperature/pressure monitor - OLED")

print("Initialising")

# Instantiate object for SH1106 OLED display

oled = sh1106(i2c(port=1, address=0x3C), rotate=2, height=128, width=128)

# Instantiate object to read  BME680 sensor

sensor = bme680.BME680()

# Set up the sensors

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)
sensor.set_temp_offset(TEMP_OFFSET)

# Build the fonts

rr_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'fonts',
                                       'Roboto-Regular.ttf'))
rb_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'fonts',
                                       'Roboto-Black.ttf'))
rr_24 = ImageFont.truetype(rr_path, 24)
rb_20 = ImageFont.truetype(rb_path, 20)
rr_15 = ImageFont.truetype(rr_path, 15)
rr_40 = ImageFont.truetype(rr_path, 40)

# Get sensor data first so that device settings take effect

sensor.get_sensor_data()
# Get the initial date/time and max/min temperature
low_temp = sensor.data.temperature
high_temp = sensor.data.temperature
curr_date = datetime.date.today().day

last_checked = time.time()


# Main loop

while True:
    if sensor.get_sensor_data():
        temp = sensor.data.temperature
        press = sensor.data.pressure
        humidity =sensor.data.humidity
        if datetime.datetime.today().day == curr_date:
            if temp < low_temp:
                low_temp = temp
            elif temp > high_temp:
                high_temp = temp
        else:
            curr_date = datetime.datetime.today().day
            low_temp = temp
            high_temp = temp

        # Write data to canvas

        with canvas(oled) as draw:
            draw.text((65, 55), u"{0:4.0f}".format(press), fill="white", font=rb_20)
            draw.text((1, 55), u"{0:2.1f}%".format(humidity), fill="white", font=rb_20)
            draw.text((1, 1), u"{0:2.0f}°".format(temp), fill="white", font=rr_40)

            draw.text((65, 4), u"max: {0:2.0f}°".format(high_temp), fill="white", font=rr_15)

            draw.text((65, 30), u"min: {0:2.0f}°".format(low_temp), fill="white", font=rr_15)

            if int(time.time()) % 2 == 0:

                draw.text((14, 78), datetime.datetime.now().strftime("%H:%M"),
                          fill="white", font=rr_40)
            else:
                draw.text((14, 78), datetime.datetime.now().strftime("%H %M"),
                          fill="white", font=rr_40)

    time.sleep(TEMPERATURE_UPDATE_INTERVAL)


And this is the result.

There is a potential issue with the temperature. The temperature sensor is directly above the Raspberry Pi Zero, and more importantly close to being above the processor. This does mean that the heat from the processor may well be affecting the temperature being sensed and hence displayed.

Dealing with that is for the next instalment.