Enviro Indoor/Outdoor Monitor - Python Code

I just acquired the Enviro Monitor, not Enviro+. I was hoping it would be an out of the box sort of thing but …anyone have python code that woud provide a display basically like the one in the ads or anything similar that a total newbie ca easily install? :)

Any help along those lines appreciated.

The code the Enviro is running as shown on the product page is the weather-and-light example file.
enviroplus-python/weather-and-light.py at master · pimoroni/enviroplus-python (github.com)
It’s in the examples folder here,
pimoroni/enviroplus-python: Python library for the Enviro+ environmental monitoring board (github.com)

If you haven’t already, you’ll need to run the one line installer.
curl -sSL https://get.pimoroni.com/enviroplus | bash
It will work for the enviro or enviro+

I’m currently running a slightly modified version of the weather and light python example on an equivalent Breakout Garden setup.

I have plans to add a second display and a second BME280 for an indoor outdoor setup.
Indoor temperature etc on one screen, outdoor on the other. I’m just waiting for the extra parts to arrive.

Nice! Thanks for the quick response. I load enviro via the github clone. For some reason I must have missed seeing that python script in the Examples folder. Thought I checked those before I put out the SOS.

Is it basically a matter of beginning that script and just letting it run? I’m going into command prompt on the Rpi. I’m guessing once I get it started I can unplug from mouse and monitor and let it fly. I hope to customize once I have this basic code. For example, I want F VS C so might need to do a conversion as it looks like there’s a function that grabs the temp in C.

I’m just happy I got the Rpi going! 😊

I customized mine a bit. I swapped the date time on the screen to opposite sides. And changed the format a bit. I also had to do an edit to get the correct time to display. I live in Sydney Nova Scotia, Canada.

# The city and timezone that you want to display.
city_name = "Halifax"
time_zone = "Canada/Atlantic"

If it complains about a missing font do the following from a terminal window
sudo pip3 install fonts
sudo pip3 install font-roboto
You may also need the following, I’m not sure if the Enviro installer installs it. I don’t run the Enviro installer, I do each breakout one by one. I had to install these manually.
sudo pip3 install pytz
sudo pip3 install astral

To auto start mine on bootup I do the following
from terminal I run crontab -e
I select 1 nano and add
@reboot python3 /home/pi/weather-and-light.py &
ctrl x and y to save.
You may have to change the path to where your file is. I moved mine to the home folder before modifying it. That way I have the original to look at if I mess something up.

Very much appreciated! I will play around with it and see what kind of things I can do.

Are there a list of time zones they use or is it just country/zone… I live in Missouri so would it be USA/Central? I’m assuming there must be some standard listing they are going off of.

Thanks

Never mind… Found pytz list. Thanks

Do you know if there are any code snippets for changing from Celsius to Fahrenheit? It looks pretty integrated. Thanks

OK, actually another one I figured out after my call for help. Have it all going.

Thanks again for the help. Let me know how your project is going.

Yeah, I ended up hunting up the pytz list. Sydney didn’t work for city_name, same deal if I entered Glace Bay. It had me head scratching at first as I had my local all set correctly in PiOS and I had a RTC module attached and all setup. I didn’t know I had to also set it up in the python file. First time I’ve had to do something like that.

My plan is to have a BME280, the LTR-559, and a VEML6075 UVA/B Sensor remote mounted on a white proto board. The BME280 will be on the bottom out of direct sunlight.
Adafruit Perma-Proto Breadboard PCB - 3 Pack! – Pimoroni
That will get me indoor temperature and humidity, outdoor temperature and humidity, The barometric pressure, light level and UV index. And one extra spot for something?
The two 0.96" SPI Color LCD’s will be mounted to a Proto Zero. The RV3028 RTC and indoor BME280 will be on a second Proto Zero. The two Proto Zero’s will be plugged into a pHat Stack for now. Or a Hat Hacker. It’s easier to modularize things, for me it is anyway. The two displays will be on SPI0 and the other breakouts are all on i2c. Wiring them up to the GPIO pins is easier if they are on separate proto boards.

You might like this, its my spin on the Enviro+ all-in-one-enviro-mini python example that is shown running on the Enviro+. I went with 3 separate displays, one for temperature, one for humidity, and one for pressure. I played around with sampling rates to get a good graph. The temperature and humidity show ~13 minutes of plots and the pressure plots 2 hours.

That is cool!!! I felt good getting just the basics. Got Fahrenheit and changed date and time formats. Might do more later as I look. Hardly wait until they get the sound.

It can be a lot of fun. Especially when you have working code to start off with.

Have you coded for the sound monitor yet? I’d like to add and looks like the all python code has it. Wasn’t sure how you put this on another screen and page between screens.

Thanks

I don’t have a microphone in my setup, mine is all Breakout Garden stuff.
It was a bit of work splitting it up into three separate screens. There is code in the all-in-one-mini example that I know what it does, I just have no idea how it does it.
This is my code for the 3 display setup.

#!/usr/bin/env python3

import os
import sys
import time
import colorsys
import logging
import ST7735
import RPi.GPIO as GPIO

from sys import exit
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
from subprocess import PIPE, Popen
from fonts.ttf import RobotoMedium as UserFont
from bme280 import BME280

def Shutdown(channel):  
    os.system("sudo shutdown now -P")
    time.sleep(30)

GPIO.setmode(GPIO.BCM)  
GPIO.setwarnings(False)
GPIO.setup(24, GPIO.IN, pull_up_down = GPIO.PUD_UP)
GPIO.add_event_detect(24, GPIO.FALLING, callback = Shutdown, bouncetime = 2000)

logging.basicConfig(
    format='%(asctime)s.%(msecs)03d %(levelname)-8s %(message)s',
    level=logging.INFO,
    datefmt='%Y-%m-%d %H:%M:%S')

logging.info("""Displays readings from BME280 sensor
Press Ctrl+C to exit!
""")

bme280 = BME280()

disp0 = ST7735.ST7735(
    port=1,
    cs=0, 
    dc=19,
#    backlight=5, 
    rotation=90,
    spi_speed_hz=10000000
)

disp1 = ST7735.ST7735(
    port=1,
    cs=1, 
    dc=19,
 #   backlight=6, 
    rotation=90,
    spi_speed_hz=10000000
)

disp2 = ST7735.ST7735(
    port=1,
    cs=2, 
    dc=19,
  #  backlight=13, 
    rotation=90,
    spi_speed_hz=10000000
)

WIDTH = 160
HEIGHT = 80

disp0.begin()
WIDTH0 = disp0.width
HEIGHT0 = disp0.height

disp1.begin()
WIDTH1 = disp1.width
HEIGHT1 = disp1.height

disp2.begin()
WIDTH2 = disp2.width
HEIGHT2 = disp2.height

img = Image.new('RGB', (WIDTH, HEIGHT), color=(0, 0, 0))
draw = ImageDraw.Draw(img)
path = os.path.dirname(os.path.realpath(__file__))
font_size = 20
font = ImageFont.truetype(UserFont, font_size)

message = ""

top_pos = 25

def display_text0(variable, data, unit):
    values[variable] = values[variable][1:] + [data]
    vmin = min(values[variable])
    vmax = max(values[variable])
    colours = [(v - vmin + 1) / (vmax - vmin + 1) for v in values[variable]]
    message = "{}:{:.0f}{}".format(variable[:11],data,unit)
    logging.info(message)
    draw.rectangle((0, 0, WIDTH0, HEIGHT0), (255, 255, 255))
    for i in range(len(colours)):
        colour = (1.0 - colours[i]) * 0.6
        r, g, b = [int(x * 255.0) for x in colorsys.hsv_to_rgb(colour, 1.0, 1.0)]
        draw.rectangle((i, top_pos, i + 1, HEIGHT0), (r, g, b))
        line_y = HEIGHT0 - (top_pos + (colours[i] * (HEIGHT0 - top_pos))) + top_pos
        draw.rectangle((i, line_y, i + 1, line_y + 1), (0, 0, 0))

    draw.text((0, 0), message, font=font, fill=(0, 0, 0))
    disp0.display(img)

def display_text1(variable, data, unit):
    values[variable] = values[variable][1:] + [data]
    vmin = min(values[variable])
    vmax = max(values[variable])
    colours = [(v - vmin + 1) / (vmax - vmin + 1) for v in values[variable]]
    message = "{}:{:.0f}{}".format(variable[:11],data,unit)
    logging.info(message)
    draw.rectangle((0, 0, WIDTH1, HEIGHT1), (255, 255, 255))
    for i in range(len(colours)):
        colour = (1.0 - colours[i]) * 0.6
        r, g, b = [int(x * 255.0) for x in colorsys.hsv_to_rgb(colour, 1.0, 1.0)]
        draw.rectangle((i, top_pos, i + 1, HEIGHT1), (r, g, b))
        line_y = HEIGHT1 - (top_pos + (colours[i] * (HEIGHT1 - top_pos))) + top_pos
        draw.rectangle((i, line_y, i + 1, line_y + 1), (0, 0, 0))

    draw.text((0, 0), message, font=font, fill=(0, 0, 0))
    disp1.display(img)

def display_text2(variable, data, unit):
    values[variable] = values[variable][1:] + [data]
    vmin = min(values[variable])
    vmax = max(values[variable])
    colours = [(v - vmin + 1) / (vmax - vmin + 1) for v in values[variable]]
    message = "{}:{:.0f}{}".format(variable[:11],data,unit)
    logging.info(message)
    draw.rectangle((0, 0, WIDTH2, HEIGHT2), (255, 255, 255))
    for i in range(len(colours)):
        colour = (1.0 - colours[i]) * 0.6
        g, r, b = [int(x * 255.0) for x in colorsys.hsv_to_rgb(colour, 1.0, 1.0)]
        draw.rectangle((i, top_pos, i + 1, HEIGHT2), (r, g, b))
        line_y = HEIGHT2 - (top_pos + (colours[i] * (HEIGHT2 - top_pos))) + top_pos
        draw.rectangle((i, line_y, i + 1, line_y + 1), (0, 0, 0))

    draw.text((0, 0), message, font=font, fill=(0, 0, 0))
    disp2.display(img)    

last_page = 0

variables = ["Temperature",
             "Humidity",
             "Pressure"]

values = {}

for v in variables:
    values[v] = [1] * WIDTH

mode = 0
F = 0
D = 0

while True:

    
    if F < 161:
        F = F + 1 
    elif F >= 161:
        time.sleep(5)
        D = D + 1 
            
    if mode == 0:
        mode %= len(variables)
        last_page = time.time()
        unit = "C"
        data = bme280.get_temperature()
        display_text0(variables[mode], data, unit)
        mode = 1

    if mode == 1:
        mode %= len(variables)
        last_page = time.time()
        unit = "%"
        data = bme280.get_humidity()
        display_text1(variables[mode], data, unit)
        
    if D == 0:        
        mode = 2
    elif D > 0 and D < 9:
        mode = 0
    elif D == 9:
        mode = 2
        D = 0
    
    if mode == 2:
        mode %= len(variables)
        last_page = time.time()
        unit = "mb"
        data = bme280.get_pressure()
        display_text2(variables[mode], data, unit)
        mode = 0

Thanks I’ll take a look.

Splitting the weather-and-light into two screens should be a lot easier than my 3 screen setup above. Toggling what’s displayed on one screen will be tougher. The above uses mode to do it and I have no idea how that works? I wanted to remove the mode stuff from my file but I couldn’t get it to work without errors any time I tried to remove it.

I have my parts for my new build, just have to warm up my soldering iron. Really sore today from clearing snow so its not going to happen for a day or two.

I hear you. Still snowing here. I’m ready for warmer climes. 😁

I suffer from chronic back pain, and even with my snow blower its a PITA clearing snow.

In my above code the F = part is to fill the screens. They are 160 pixels wide so I do the first 161 readings with no delay. After that I read the temperature and humidity every 5 seconds. That gets me a nice graph that will show a trend, if its going up or down.
And then read the pressure once every 9 readings of temp and humidity. I do one pressure reading every 45 seconds. That gets me 2 hours of readings across the screen. The pressure changes so slowly that to see any kind of trend you need a long delay between samples. A 2mb change over 2 hours is considered as changing rapidly.
With the stock sampling rates you won’t see a change, the plotted graph is just a flat line unless you blow on the BME280.

I have my second BME280 installed. I moved my first one, the indoor one, to a Proto Zero. I cut the ADDR+1 jumper on the second one, the to be outdoor one" and plugged it into my Breakout garden for now. It’s all plugged into a pHat Stack to make testing code easier.
bme280 = BME280()
gets changed to
bme280_in = BME280(0x76)
bme280_out = BME280(0x77)
Then

# Temperature
    temperature = bme280.get_temperature()

gets changed to something like

# Temperature indoor
    temperature_in = bme280_in.get_temperature()
and 
# Temperature outdoor
    temperature_out = bme280_out.get_temperature()

etc.
My plan for today is to edit my current file adding all the _in references. Then make sure it still runs. After that it just a clip and past to copy it to a second block, then edit that block so its all the _out code.
The display code gets edited like this, for me anyway as I plan on using two displays. You could go _in _out instead of 0 and 1. I like matching the display number to chip select its using.

disp0 = ST7735.ST7735(
    port=0,
    cs=0, 
    dc=9,
#    backlight=5, 
    rotation=90,
    spi_speed_hz=10000000
)
disp0.begin()

WIDTH = disp.width
HEIGHT = disp.height

disp1 = ST7735.ST7735(
    port=0,
    cs=1, 
    dc=9,
 #   backlight=6, 
    rotation=90,
    spi_speed_hz=10000000
)

disp1.begin()

WIDTH = disp.width
HEIGHT = disp.height


It will be a day or two or three before I get the second display wired up. I’ll be wiring the two of them up to SPI0 on a Proto Zero. My setup will be kind of modular.
Proto Zero for SPI and a Proto Zero for the indoor i2c stuff. The outdoor breakouts are likely to end up soldered to a white Proto Board. Right now all the outdoor breakouts are plugged into a Breakout Garden i2c MINI. I can plug it all into the pHat Stack for testing and working on my finale code.