Welcome!I want to dive back into the past, a few years to a fun trip to Cambridge. Not only is it a beautiful city, it has a raspberry pi shop! I wanted a pi to tinker with and it was a fun trip to go and buy it in person. I picked up a Pi4 with 8Gb RAM. There were tons stuff in the shop and I picked up a Pimoroni Enviro+ pHAT, and stopped my self at that. Tempting as it was to buy much more!

I had a fun project to start my home lab adventures with. The HAT has a few abilities:

Component What it measures
BME280 Temperature, barometric pressure, humidity
LTR-559 Light (lux) and proximity

I wanted to use this oportunity to set up the Pi HAT and gather the data for interest, but make the most out of it. Learning about the tempurate difference in my office was interesting to me, see how much it dipped over night and how high it got during the day.

I took sample code from the HAT github page, giving me a starting point. The HAT has a small LED screen to display the metrics. First job was to strip that part of the code out, just leaving the data from the sensors.

The reason for just having the data and not outputting to the screen was to see if I could encorporate some other technilogies and learn how to hang them together. I created a PostgreSQL database that was running on a seperate server, very simple that had had table that would accept the metrics and a date/time stamp. Trying to get the script to write to the database was my first big hurdle, don’t ask me why, but during the database set up, I had changed the default port that the database was running on. Took me an embarrassingly long time to realise why I get getting connection errors. Once that was fixed I had it writing to the database fine. Was a great sense of acheivement!

A few tweaks to the factor (I’ll show the code shorlty) that the temp was being adjusted by. In hindsight, I could have taken the internal reading of the CPU temp and used that to adjust. What I actually did, was more old school. Thermometer next to the Pi, and adjusted the factor being applied to the temp until they matched. Took a while and I did it at different times of the day, but got the job done. I had a pretty good temp reading that was matching reality, certainly close enough for my puposes.

The last step in the chain was a display. I could (and did to start with) just query the database and read a list of numbers, but that’s a bit dull. I chose to try and use Graphana. There is of course a cloud offerening which comes with AI enhancements now. However, if you dig about a bit you can self host, exactly what I wanted. This part was the easiest to set up, once the hosting was done on the same server the database was running on, all I had to do was create a read only account for graphana to use, this avoided any possibility of graphana overwrtiting data if I did somethings wrong. Point graphana to the table and I could open the web UI from my main office PC. I got beautful graphs of the metrics and a huge sense of accomplishment to having got it all to work!

So here is the code. BUT with a big caveat, that I should really stress. I have redacted them, but in the script are hardocded credentials for the database connection. Yes I know, terrible. In my defence, I hadn’t learnt how to properly use a .env and store them there. Equally, this was all running locally in my home lab, so for this use case, didn’t matter.

#!/home/<USER>/envirohatenv/bin/python3

import time
import colorsys
import sys
import psycopg2
try:
    # Transitional fix for breaking change in LTR559
    from ltr559 import LTR559
    ltr559 = LTR559()
except ImportError:
    import ltr559
    
from bme280 import BME280
from pms5003 import PMS5003, ReadTimeoutError as pmsReadTimeoutError, SerialTimeoutError
from subprocess import PIPE, Popen
import logging

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

logging.info("""combined.py - Displays readings from all of Enviro plus' sensors

Press Ctrl+C to exit!

""")

# Define database connection parameters
db_params = {
    "host": "redacted",
    "port": "redacted",
    "user": "redacted",
    "password": "redacted",
    "database": "redacted",
}
# BME280 temperature/pressure/humidity sensor
bme280 = BME280()

# Create a values dict to store the data
variables = ["temperature",
             "pressure",
             "humidity",
             "light"]


units = ["C",
         "hPa",
         "%",
         "Lux"]


values = {}

factor = 1.62

# Saves the data to be used in the graphs later and prints to the log
def save_data(idx, data):
    variable = variables[idx]
    # Maintain length of list
    values[variable] = values[variable][1:] + [data]
    unit = units[idx]
    message = "{}: {:.1f} {}".format(variable[:4], data, unit)
    logging.info(message)

# Get the temperature of the CPU for compensation
def get_cpu_temperature():
    process = Popen(['vcgencmd', 'measure_temp'], stdout=PIPE, universal_newlines=True)
    output, _error = process.communicate()
    return float(output[output.index('=') + 1:output.rindex("'")])


# Function to insert into the database
def insert_data_into_database(data):
    try:
        conn = psycopg2.connect(**db_params)
        cursor = conn.cursor()

        # Define the INSERT query with placeholders for each value
        query = "INSERT INTO office (timestamp, temperature, pressure, humidity, light) VALUES (%s, %s, %s, %s, %s)"

        # Get the current timestamp
        timestamp = time.strftime("%Y-%m-%d %H:%M:%S")

        # Specify the values to insert in the same order as the placeholders
        values = (timestamp, data["temperature"], data["pressure"], data["humidity"], data["light"])

        # Execute the query with the values
        cursor.execute(query, values)

        # Commit the transaction
        conn.commit()

    except (Exception, psycopg2.Error) as error:
        print("Error while inserting data into PostgreSQL:", error)

    finally:
        if conn:
            cursor.close()
            conn.close()

    # The main loop
try:
    while True:

        data = {
            "temperature": (bme280.get_temperature()/factor),
            "pressure": bme280.get_pressure(),
            "humidity": bme280.get_humidity(),
            "light": ltr559.get_lux(),
        }
        insert_data_into_database(data)
        time.sleep(6*60)

# Exit cleanly
except KeyboardInterrupt:
    sys.exit(0)


if __name__ == "__main__":
    main()

I did try to find the original code from Pimoroni GitHub, but could not find it. I suspect it’s been updated with newer code as I’m sure there has been some revisions since I got mine, it’s been about 4 years.

The server that was running graphana and postgreSQL has since been retired, I had proved I could cobble together some tech for a purpose and no longer needed to dive quite that deep into my office temparature. I think I overwrote it to try a different server OS.

A key take away from this was to challange myself to do something I’d never done before. Lots of evening googling error messages, dealing with frustration before elation. Yes, the data at the end was interesting, even if my familly did think I was nuts, but the feeling of finally getting it all working was the real win.

You’ll be pleased to here the Pi and the Enviro+ are still going strong. The Enviro+ is now displaying the temp, humidity and pressure on it’s LCD screen. The Pi is hosting a few things, one of which is Hermes Agent and certainly a lot more to come on that on a later post.



More notes from the lab next time - pirranasaurus