# SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries
# SPDX-License-Identifier: MIT

import time
import board
import simpleio
import adafruit_bmp280
import pwmio
import displayio
from adafruit_motor import servo
import terminalio
from adafruit_clue import clue
from adafruit_display_text import label

#  pwm setup for servo
pwm = pwmio.PWMOut(board.D0, duty_cycle=2 ** 15, frequency=50)
gauge = servo.Servo(pwm)

#  bmp280 sensor setup
i2c = board.I2C()  # uses board.SCL and board.SDA
# i2c = board.STEMMA_I2C()  # For using the built-in STEMMA QT connector on a microcontroller
bmp280 = adafruit_bmp280.Adafruit_BMP280_I2C(i2c)

#  change depending on your location's elevation
NOMINAL_PRESSURE = 1005.94

PRESSURE_RANGE = 40 # hPa, expected pressure range variance caused by local weather
PRESSURE_LOW_LIM = NOMINAL_PRESSURE - PRESSURE_RANGE
PRESSURE_HIGH_LIM = NOMINAL_PRESSURE + PRESSURE_RANGE

#  board display
#  scaling for terminalio font
display = board.DISPLAY
group = displayio.Group(scale=3)

#  text elements
temp_header = "Temperature:"
press_header = "Pressure:"
temp_text = "   ºC"
press_text = "   hPa"
font = terminalio.FONT
blue = 0x0000FF
red = 0xFF0000

#  temperature text elements
temp_label = label.Label(font, text=temp_header, color=red, x=5, y=10)
temp_data = label.Label(font, text=temp_text, color=red, x=5, y=25)

#  pressure text elements
press_label = label.Label(font, text=press_header, color=blue, x=5, y=50)
press_data = label.Label(font, text=press_text, color=blue, x=5, y=65)

#  adding text to display group
group.append(temp_label)
group.append(press_label)
group.append(temp_data)
group.append(press_data)

display.root_group = group

#  function to convert celcius to fahrenheit
def c_to_f(temp):
    temp_f = (temp * 9/5) + 32
    return temp_f

#  function to convert hPa to inHg
def hpa_to_inHg(hPa):
    inches_mercury = hPa * 0.02953
    return inches_mercury

#  time.monotonic clock
clock = 0
#  units state
metric_units = False

while True:
    #  non-blocking 2 second delay
    if (clock + 2) < time.monotonic():
        #  map servo range to barometric pressure range
        servo_value = simpleio.map_range(bmp280.pressure,
                                         PRESSURE_LOW_LIM, PRESSURE_HIGH_LIM, 180, 0)
        #  set servo to pressure
        gauge.angle = servo_value
        #  print data for debugging
        print("\nTemperature: %0.1f C" % bmp280.temperature)
        print("Pressure: %0.1f hPa" % bmp280.pressure)
        print(servo_value)
        #  if metric units...
        if metric_units:
            #  update temp & pressure text in celcius and hPa
            temp_data.text = "%0.1f ºC" % bmp280.temperature
            press_data.text = "%0.1f hPa" % bmp280.pressure
        #  if imperial units...
        else:
            #  convert celcius to fahrenheit
            temp_fahrenheit = c_to_f(bmp280.temperature)
            #  convert hPa to inHg
            pressure_inHg = hpa_to_inHg(bmp280.pressure)
            #  update temp & pressure text
            temp_data.text = "%0.1f ºF" % temp_fahrenheit
            press_data.text = "%0.1f inHg" % pressure_inHg
        #  reset time.monotonic() clock
        clock = time.monotonic()
    #  if a button is pressed, metric_units is True, show metric
    if clue.button_a:
        metric_units = True
    #  if b button is pressed, metric_units is False, show imperial units
    if clue.button_b:
        metric_units = False

How the CircuitPython Code Works

The code begins by creating a displayio group and some text elements to show on the CLUE display. 

#  board display
#  scaling for terminalio font
display = board.DISPLAY
group = displayio.Group(scale=3)

#  text elements
temp_header = "Temperature:"
press_header = "Pressure:"
temp_text = "   ºC"
press_text = "   hPa"
font = terminalio.FONT
blue = 0x0000FF
red = 0xFF0000

PWM and I2C

Then, the servo object is created with PWM and the BMP280 sensor is instantiated over I2C.

#  pwm setup for servo
pwm = pwmio.PWMOut(board.D0, duty_cycle=2 ** 15, frequency=50)
gauge = servo.Servo(pwm)

#  bmp280 sensor setup
i2c = board.I2C()  # uses board.SCL and board.SDA
bmp280 = adafruit_bmp280.Adafruit_BMP280_I2C(i2c)

Nominal Baseline Pressure

The deviation from the nominal baseline pressure is used to show your current weather conditions with the servo. The NOMINAL_PRESSURE value must be set for your location (covered later in guide on the Usage page). The PRESSURE_RANGE value can be adjusted as needed for typical local weather driven deviations. A lower value will make the detection more sensitive.

#  change depending on your location's elevation
NOMINAL_PRESSURE = 1005.94

PRESSURE_RANGE = 40 # hPa, expected pressure range variance caused by local weather
PRESSURE_LOW_LIM = NOMINAL_PRESSURE - PRESSURE_RANGE
PRESSURE_HIGH_LIM = NOMINAL_PRESSURE + PRESSURE_RANGE

Text Labels

There are four text labels that are shown on the display. temp_label and press_label are headers with the text "Temperature:" and "Pressure:". temp_data and press_data are updated in the loop to show the readings from the BMP280 sensor.

#  temperature text elements
temp_label = label.Label(font, text=temp_header, color=red, x=5, y=10)
temp_data = label.Label(font, text=temp_text, color=red, x=5, y=25)

#  pressure text elements
press_label = label.Label(font, text=press_header, color=blue, x=5, y=50)
press_data = label.Label(font, text=press_text, color=blue, x=5, y=65)

#  adding text to display group
group.append(temp_label)
group.append(press_label)
group.append(temp_data)
group.append(press_data)

display.root_group = group

Unit Conversion Functions

There are two functions to convert metric units to imperial units. c_to_f() converts Celsius to Fahrenheit by passing the temperature in Celsius and returning the temperature in Fahrenheit.

hpa_to_inHg() converts hectopascals to inches of mercury by passing the barometric pressure in hectopascals and returning the pressure reading in inches of mercury.

Two variables are declared before the loop. clock is a time.monotonic() device and metric_units is used to determine which units are being shown on the display and is affected by the CLUE buttons.

#  function to convert celcius to fahrenheit
def c_to_f(temp):
    temp_f = (temp * 9/5) + 32
    return temp_f

#  function to convert hPa to inHg
def hpa_to_inHg(hPa):
    inches_mercury = hPa * 0.02953
    return inches_mercury

#  time.monotonic clock
clock = time.monotonic()
#  units state
metric_units = True

The Loop

In the loop, the BMP280 pressure reading is mapped from the calculated pressure range with your nominal baseline pressure to the value range of the servo motor angle. The servo motor angle is then set to that mapped value.

#  non-blocking 2 second delay
    if (clock + 2) < time.monotonic():
        #  map servo range to barometric pressure range
        servo_value = simpleio.map_range(bmp280.pressure,
                                         PRESSURE_LOW_LIM, PRESSURE_HIGH_LIM, 180, 0)
        #  set servo to pressure
        gauge.angle = servo_value

Displaying Data

If metric_units is True, then the temperature and air pressure readings are displayed on the CLUE in Celsius and hPa.

#  if metric units...
        if metric_units:
            #  update temp & pressure text in celcius and hPa
            temp_data.text = "%0.1f ºC" % bmp280.temperature
            press_data.text = "%0.1f hPa" % bmp280.pressure

If metric_units is False, the readings from the BMP280 are converted to imperial units with the two conversion functions and are shown on the display in Fahrenheit and inHg.

#  if imperial units...
        else:
            #  convert celcius to fahrenheit
            temp_fahrenheit = c_to_f(bmp280.temperature)
            #  convert hPa to inHg
            pressure_inHg = hpa_to_inHg(bmp280.pressure)
            #  update temp & pressure text
            temp_data.text = "%0.1f ºF" % temp_fahrenheit
            press_data.text = "%0.1f inHg" % pressure_inHg

The CLUE Buttons

If CLUE button A is pressed, then the BMP280 readings are displayed in metric units. If the CLUE button B is pressed, then the BMP280 readings are displayed in imperial units.

#  if a button is pressed, metric_units is True, show metric
    if clue.button_a:
        metric_units = True
    #  if b button is pressed, metric_units is False, show imperial units
    if clue.button_b:
        metric_units = False

This guide was first published on Oct 19, 2022. It was last updated on Mar 27, 2024.

This page (PyLeap CLUE Barometer Code) was last updated on Mar 27, 2024.

Text editor powered by tinymce.