Sunday, August 14, 2016

Crafting a BME280 Ambient Condition Monitor



I have been having a lot of fun with my Beaglebone Black now that wifi is up and running smoothly (can't believe it took so long to get a good adapter!). One of the most recent acquisitions was a Bosche BME280 sensor - tiny, with readouts of temperature, barometric pressure, and humidity - i guess smartphones are getting smarter?

Well, thankfully I didn't have to shell out an outrageous amount of dinero for this sensor. You can get 'em for less than a Hamilton on-line, so I popped for a pair. I used Matt Hawkin's excellent bme280.py script as an accessible Python lib (simply drop it in the same folder as your script), and just used the function to get the data:

  from bme280 import readBME280All

the original script returns many digit accuracy - too many for the oled, so i trimmed the output, converted from Centigrade to Fahrenheit and hectopascals to inches of mercury:

return "%.2f degrees F" % (((temperature)*9/5)+32)
return "%.2f inches mercury" % ((pressure)/33.8638866667)
return "%.2f%% humidity" % humidity
 
I like the result and will use this sensor with some wifi-enabled Arduinos for inexpensive weather monitoring. I also have a couple wind sensors I haven't tried - that's for later! So for now, here's the script:

#!/usr/bin/env python
# bbssd.py version 0.6 - a simple system monitor for the
# Beaglebone Black GNU/Linux SBC
# kg4zqz.blogspot.com
# adapted from rmhull's wonderful ssd1306 Python lib
# crafted for the dual-color 128x96 OLED w/yellow top area
# 070916 - initial version ported from a version for the Raspberry Pi
#          shows date, wlan0 IP address, memory, and sd card usage
# 070916 - added Beagle splash screen, kernel version and serial #
# 071016 - added uptime, wifi sig strength, screen cleanup
# 072616 - added text wrap to uptime screen
# 081416 - added BME280 weather sensor data/readout
#
# note: it was a bear to get all the needed software libs for
# the Pillow install - must have libjpeg6-dev and associated zlib pkgs!

import os
import psutil
from lib_oled96 import ssd1306
from time import sleep
from datetime import datetime
from PIL import ImageFont, ImageDraw, Image
from smbus import SMBus       #  These are the only two variant lines !!
i2cbus = SMBus(2)             # NOTE: Beaglebone Green Wireless uses bus 2!
oled = ssd1306(i2cbus)

draw = oled.canvas

while True:

 # "draw" onto this canvas, then call display() to show on the OLED.
 draw = oled.canvas

# show SPLASH screen
# set font to 17 for yellow area splash title
 font = ImageFont.truetype('/home/debian/Downloads/ssd1306-master/FreeSans.ttf', 17)
 draw.text((1, 1), 'BEAGLEBONE', font=font, fill=1)
# put on the dawg, which is capital 'S' in the below font
 font = ImageFont.truetype('/home/debian/Downloads/ssd1306-master/Doggon.ttf', 50)

draw.text((1, 23), ' S', font=font, fill=1)

 # now show splash screen, then sleep, then clear for next screen
 oled.display()
 sleep(3)
 oled.cls()

 # set up, display overall stats screen
 # set font to 13 for yellow area
 font = ImageFont.truetype('/home/debian/Downloads/ssd1306-master/FreeSans.ttf', 13)

 # get, display date and time
 draw.text((1, 1), str(datetime.now().strftime('%a  %b  %d  %H:%M:%S')), font=font, fill=1)

 # reset font for four smaller lines
 font = ImageFont.truetype('FreeSans.ttf', 10)

 # get, return Linux kernel version
 def uname_r():
    k = os.popen('uname -r')
    rev = str(k.read())
    # strip out trailing chars for cleaner output
    return "KERNEL: %s" % rev.rstrip('\r\n')

  # display kernel version
 draw.text((1, 15),   uname_r(),  font=font, fill=1)

 # get, return wlan0's current IP address
 def wlan_ip():
    f = os.popen('ifconfig wlan0 | grep "inet\ addr" | cut -c 21-33')
    ip = str(f.read())
    # strip out trailing chars for cleaner output
    return "WIFI IP: %s" % ip.rstrip('\r\n')

 # display the IP address
 draw.text((1, 28),    wlan_ip(),  font=font, fill=1)

 # get amount of free memory
 def mem_usage():

    usage = psutil.virtual_memory()
    return "MEM USED: %s KB" % (int(str(usage.used)) / 1024)

 # display amount of free memory
 draw.text((1, 41),    mem_usage(),  font=font, fill=1)

 # get disk usage
 def disk_usage(dir):
    usage = psutil.disk_usage(dir)
    return "SD CARD USED: %.0f%%" % (usage.percent)
 # display disk usage
 draw.text((1, 53), disk_usage('/'),  font=font, fill=1)

 # now show the entire stats screen, then clear
 oled.display()
 sleep(5)
 oled.cls()

 # read bme280 data - limit 2-digit accuracy
 def get_temp():
  from bme280 import readBME280All
  temperature,pressure,humidity = readBME280All()
 # return "%sF" % str(((temperature)*9/5)+32)
  return "%.2f degrees F" % (((temperature)*9/5)+32)

 def get_pressure():
  from bme280 import readBME280All
  temperature,pressure,humidity = readBME280All()
 # return "%s hPa" % str(pressure)
  return "%.2f inches mercury" % ((pressure)/33.8638866667)

 def get_humidity():
  from bme280 import readBME280All
  temperature,pressure,humidity = readBME280All()
 # return "%s percent" % str(humidity)
  return "%.2f%% humidity" % humidity

 font = ImageFont.truetype('/home/debian/Downloads/ssd1306-master/FreeSans.ttf', 13)
 draw.text((1, 1), 'Ambient Conditions', font=font, fill=1)

 font = ImageFont.truetype('/home/debian/Downloads/ssd1306-master/FreeSans.ttf', 12)
 draw.text((1, 20),  get_temp(),  font=font, fill=1)
 draw.text((1, 35),  get_pressure(),  font=font, fill=1)
 draw.text((1, 50),  get_humidity(),  font=font, fill=1)

 oled.display()
 sleep(5)
 oled.cls()

 # begin series of LARGE stats
 # LARGE display - get wifi wlan0 signal strength
 def get_wifi():
    cmd = "iwconfig wlan0 | grep Signal | /usr/bin/awk '{print $4}' | /usr/bin/cut -d'=' -f2"
    strDbm = os.popen(cmd).read()
    dbm = int(strDbm)
    quality = 2 * (dbm + 100)
    if strDbm:
     return("{0} dbm = {1}%".format(dbm, quality))
    else:
     return("No Wifi")

 font = ImageFont.truetype('/home/debian/Downloads/ssd1306-master/FreeSans.ttf', 17)
 draw.text((1, 1), 'WIFI SIGNAL', font=font, fill=1)
 font = ImageFont.truetype('/home/debian/Downloads/ssd1306-master/FreeSans.ttf', 15)
 draw.text((1, 30),    get_wifi(),  font=font, fill=1)

 oled.display()
 sleep(3)
 oled.cls()

 # LARGE display - uptime
 def get_uptime():
    uptime = os.popen("uptime")
    ut = str(uptime.read())
    # strip out trailing chars for cleaner output
    return "%s" % ut.rstrip('\r\n')

 font = ImageFont.truetype('/home/debian/Downloads/ssd1306-master/FreeSans.ttf', 17)
 draw.text((1, 1),    '    UPTIME',  font=font, fill=1)

# now smaller font needed and wrap text to show info
 font = ImageFont.truetype('/home/debian/Downloads/ssd1306-master/FreeSans.ttf', 11)
 import textwrap
 lines = textwrap.wrap(get_uptime(), width=25)
 # what 'line' to start info
 y_text = 20
 w = 1 # we'll always start at left side of OLED
 for line in lines:
    width, height = font.getsize(line)
    draw.text((w, y_text), line, font=font, fill=1)
    y_text += height

 oled.display()
 sleep(4)
 oled.cls()

 # LARGE display - wlan0's IP address
 def wlan_lip():
    f = os.popen('ifconfig wlan0 | grep "inet\ addr" | cut -c 21-33')
    ip = str(f.read())
    # strip out trailing chars for cleaner output
    return "%s" % ip.rstrip('\r\n')

 font = ImageFont.truetype('/home/debian/Downloads/ssd1306-master/FreeSans.ttf', 17)

 draw.text((1, 1),   ' IP ADDRESS', font=font, fill=1)
 font = ImageFont.truetype('/home/debian/Downloads/ssd1306-master/FreeSans.ttf', 16)
 draw.text((1, 30),    wlan_lip(),  font=font, fill=1)

 oled.display()
 sleep(3)
 oled.cls()

 # LARGE display - amount of free memory
 def mem_usagel():
    usage = psutil.virtual_memory()
    return "%s KB" % (int(str(usage.used)) / 1024)

 font = ImageFont.truetype('/home/debian/Downloads/ssd1306-master/FreeSans.ttf', 17)

 draw.text((1, 1),   'MEM USED', font=font, fill=1)
 draw.text((1, 30),    mem_usagel(),  font=font, fill=1)

 oled.display()
 sleep(3)
 oled.cls()

 # LARGE display - Beaglebone's serial number
 def get_serial():
    serial = os.popen('/usr/local/bin/get_serial_no')
    snum = str(serial.read())
    # strip out trailing chars for cleaner output
    return "%s" % snum.rstrip('\r\n')

 font = ImageFont.truetype('/home/debian/Downloads/ssd1306-master/FreeSans.ttf', 17)
 draw.text((1, 1),    'SERIAL NBR',  font=font, fill=1)
 # now smaller font needed
 font = ImageFont.truetype('/home/debian/Downloads/ssd1306-master/FreeSans.ttf', 15)
 draw.text((1, 30),    get_serial(),  font=font, fill=1)

 oled.display()
 sleep(3)
 oled.cls()

 # get, return current local WX
 def do_wx():
    f = os.popen('/usr/local/bin/currentwx')
    wx = str(f.read())
    # strip out trailing chars for cleaner output
    return "%s" % wx.rstrip('\r\n')

 # display the WX
 font = ImageFont.truetype('/home/debian/Downloads/ssd1306-master/FreeSans.ttf', 13)
 draw.text((1, 1),    'CURRENT WEATHER:',  font=font, fill=1)
 font = ImageFont.truetype('/home/debian/Downloads/ssd1306-master/FreeSans.ttf', 12)
 draw.text((1, 15),    ' PINELLAS PARK, FL',  font=font, fill=1)
 draw.text((1, 41),    do_wx(),  font=font, fill=1)
 

oled.display()
sleep(3)
oled.cls()      # Oled still on, but screen contents now blacked out
 


No comments:

Post a Comment