BLOG# 8.1 -Edible Algae System - (Growing Spirulina in space)
System Build
This is Part 1 of my 8th blog post in a series of blog post for the Design Challenge 1 Meter of Pi
version 1.1
,<<< PREVIOUS BLOG | NEXT BLOG >>> |
|---|---|
| Blog# 8.2 (Part 2 of 2) EAS - DATA REPOSITORY MongoDB |
INTRODUCTION
Ok, here we are on the 9th of December the year 2020. I'm on the home stretch.
As of today, the Challenge Page Dates states, that the Challenge Projects are due on the "5th January. 2020.". I know that we did lose the year 2020 because of a global covid-19 pandemic. This was kind of surreal when I checked on the finish line date. I'm assuming you meant to say the year 2021.
Updated Just checked the date has now been changed to 2021
"5th January. 2020.", That is lest than 4 weeks away, time to get cranking on the implementation of my design..
Updated Now 10 days away as of this blogs first publish to the Design Challenge.
- This blog will describe the implementation of my system design from my previous Blog# 7 EAS - System Designs -- Software Design
- I divided up the build process into two build phases.
- Part 1, describes the devices attached and controlled by the Rasberry PI.,
- Part 2, decribes the classes developed to implement the management of the attached devices.
- List Additional Material needed for the project showing links to sourced.
Electronics Build
The Electronic Build section will describe the Wireing for the connections of the 2 hats, water temp sensor and the devices attached to the Automation Mini Hat
Enviro Hat and Automation Mini Hat
are wired to the rasberry using the wireing scheme- I was somewhat disappointed that I could not stack the Hat's using the Pico Hat Hacker,but happy to find that another challenger, dules had a solution in one of his blogs Aquaponics Box: #2 - Hacking the HATs (both HATs from the challenger kit on single RPi)
- I was able to get both HATs working, using the wiring diagram and his PIN analyst contained in this blog.
- Thanks dules
- The only wiring I did different is that I used GPIO 13 for the Enviro display backlight.

Water temp Sensor
- This sensor is attached to the breadboard off the rasberry pi as described in my Blog# 5 EAS - Research Temperature Sensor - DS18B20
Devices attached to the Automation Mini Hat
LEDLight
Automation Hat output1
- WaterPump
- Automation Hat output2
- WaterHeaterPad
- Automation Hat output3
- RoomHeater
- not Implemented YET
- IntakeFan
- Automation Hat relay NO
- OutTakeFan
- Automation Hat relay NC
- IntakeFan
Additional Devices needed for the project
| IItem | NOTES |
|---|---|
| Glass Container | I bought this at my local store. Not 20 gallons, but It will surfice for now |
| Heating Unit |
Used to Heat the water to grow Algae. The glass container will be placed on this
|
| Air Pump |
Used to Airerate the water to grow Algea
|
| Lamp LED | HAVE
This needs to be 12Volt |
| Room heater | TO Purchase The problem is the power draw of a heater. Is there a heater on the maket that uses 12 Volts.' |
Intake and OutTake Fans | Purchased 2 small ones with 2 leads
|
Software Build
The Software Build will contain sections on the python code used to implement each class from the from the class diagram described in Blog#7
For this build, I developed 6 Python scripts that implement the classes described in my Design Challenge Blog# 7 EAS - System Designs -- Software Design
Each of the following sections contain the classes code..
Class Diagram
CODE
Sensors (senors.py)
Base class
# --Sensors
# This module will contain all communication with the sensors on the Enviro Hat
# and on a seperate sensor the W1ThemSensor
# BASE CLASS
classSensors:
def__init__(self,type):
self.value = 0
self.type = type
iftype != 2 :
self.device = "Enviro"
else:
self.device = "W1ThemSensor"
self.description = "Sensor"Child classes that inheret from Sensors:
Light
class Light(Sensors):
def __init__(self,type=1):
super().__init__(type)
def getValue(self):
# gets the value of the sensor
import logging
import time
try:
# Transitional fix for breaking change in LTR559
from ltr559 import LTR559
ltr559 = LTR559()
except ImportError:
import ltr559
logging.basicConfig(
format='%(asctime)s.%(msecs)03d %(levelname)-8s %(message)s',
level=logging.INFO,
datefmt='%Y-%m-%d %H:%M:%S')
#sleep for a moment
time.sleep(1)
self.value = ltr559.get_lux()
logging.info("""Light: {:05.02f}""".format(self.value))
return self.value
def IsInRange(self):
passWaterTemp
class WaterTemp(Sensors):
def __init__(self,type=2):
super().__init__(type)
def getValue(self):
# gets the value of the water Tempreture sensor
import logging
from w1thermsensor import W1ThermSensor
logging.basicConfig(
format='%(asctime)s.%(msecs)03d %(levelname)-8s %(message)s',
level=logging.INFO,
datefmt='%Y-%m-%d %H:%M:%S')
sensor = W1ThermSensor()
temperature_in_celsius = sensor.get_temperature()
temperature_in_fahrenheit = sensor.get_temperature(sensor.DEGREES_F)
#temperature_in_fahrenheit = sensor.get_temperature(2)
self.value = temperature_in_fahrenheit
logging.info("""Water Tempreture: {:05.02f}C {:05.02f}F""".format(temperature_in_celsius,temperature_in_fahrenheit))
return self.value
def IsInRange(self):
pass
RoomHumidity
class Humidity(Sensors):
def __init__(self,type=3):
super().__init__(type)
def getValue(self):
# gets the value of the sensor
import logging
from bme280 import BME280
# BME280 temperature/pressure/humidity sensor
bme280 = BME280()
logging.basicConfig(
format='%(asctime)s.%(msecs)03d %(levelname)-8s %(message)s',
level=logging.INFO,
datefmt='%Y-%m-%d %H:%M:%S')
self.value = bme280.get_humidity()
logging.info("""Room Humitity: {:05.02f}%""".format(self.value))
return self.value
def IsInRange(self):
pass
RoomTemp
class RoomTemp(Sensors):
def __init__(self,type=4):
super().__init__(type)
def getValue(self):
from bme280 import BME280
try:
from smbus2 import SMBus
except ImportError:
from smbus import SMBus
import logging
logging.basicConfig(
format='%(asctime)s.%(msecs)03d %(levelname)-8s %(message)s',
level=logging.INFO,
datefmt='%Y-%m-%d %H:%M:%S')
bus = SMBus(1)
bme280 = BME280(i2c_dev=bus)
# Get the temperature of the CPU for compensation
def get_cpu_temperature():
with open("/sys/class/thermal/thermal_zone0/temp", "r") as f:
temp = f.read()
temp = int(temp) / 1000.0
return temp
# Tuning factor for compensation. Decrease this number to adjust the
# temperature down, and increase to adjust up
factor = 2.25
factor = 18
cpu_temps = [get_cpu_temperature()] * 5
cpu_temp = get_cpu_temperature()
# Smooth out with some averaging to decrease jitter
cpu_temps = cpu_temps[1:] + [cpu_temp]
avg_cpu_temp = sum(cpu_temps) / float(len(cpu_temps))
raw_temp = bme280.get_temperature()
comp_temp = raw_temp - ((avg_cpu_temp - raw_temp) / factor)
# Conversion to F (0°C × 9/5) + 32
temp_f = (comp_temp * 9/5) + 32
logging.info("Room Temperature: {:05.2f}C {:05.2f}F".format(comp_temp,temp_f))
self.value = temp_f
return self.value
def IsInRange(self):
pass
Controller (controllers.py)
BASE Class Controllers
# Controllers
# this module will contain all communication with the Automation HAT Mini
#set up logging
import logging
logging.basicConfig(
format='%(asctime)s.%(msecs)03d %(levelname)-8s %(message)s',
level=logging.INFO,
datefmt='%Y-%m-%d %H:%M:%S')
# BASE CLASS
class Controllers:
def __init__(self,type):
self.value = "OFF" # "OFF" "ON"
self.type = type # 1 -- light # 2 -- WaterPump # 3 -- WaterHeater
# 4 -- RoomHeater # 5 -- IntakeFan # 6 -- OutTakeFan
self.channel = "NONE" # ADC INPUT OUTPUT RELAY
self.chanNum = 0 # 1,2,3 1,2,3 1,2,3 -1=NO 0=COM 1=NC
self.IsOn = False
import automationhat #use the automation hat module
Child Classes that inheret from Controller
LEDLight
# 1 -- Light Growing LAMP
class Light(Controllers):
def __init__(self,type=1):
super().__init__(type)
self.channel = "OUTPUT" # ADC INPUT OUTPUT RELAY
self.chanNum = 1 # 1,2,3 1,2,3 1,2,3 -1=NO 0=COM 1=NC
self.IsOn = False
def Set(self,value):
logging.info("""START Light.Set({:})""".format(value))
self.value = value
#check value
if (value == "ON"):
automationhat.output.one.on()
else:
automationhat.output.one.off()
def Get(self):
return self.value
def IsOnCheck(self):
#check value
if automationhat.output.one.is_on():
return True
else:
return False
WaterPump
# 2 -- WaterPump
class WaterPump(Controllers):
def __init__(self,type=2):
super().__init__(type)
self.channel = "OUTPUT" # ADC INPUT OUTPUT RELAY
self.chanNum = 2 # 1,2,3 1,2,3 1,2,3 -1=NO 0=COM 1=NC
#ERROR IN2 is not working !!! WHY?????
self.IsOn = False
def Set(self,value):
logging.info("""START WaterPump.Set({:})""".format(value))
self.value = value
#check value
if (value == "ON"):
automationhat.output.two.on()
else:
automationhat.output.two.off()
def Get(self):
return self.value
def IsOnCheck(self):
#check value
if automationhat.output.two.is_on():
return True
else:
return False
WaterHeaterPad
# 3 -- WaterHeater
class WaterHeater(Controllers):
def __init__(self,type=3):
super().__init__(type)
self.channel = "OUTPUT" # ADC INPUT OUTPUT RELAY
self.chanNum = 3 # 1,2,3 1,2,3 1,2,3 -1=NO 0=COM 1=NC
self.IsOn = False
def Set(self,value):
logging.info("""START WaterHeater.Set({:})""".format(value))
self.value = value
#check value
if (value == "ON"):
automationhat.output.three.on()
else:
automationhat.output.three.off()
def Get(self):
return self.value
def IsOnCheck(self):
#check value
if automationhat.output.three.is_on():
return True
else:
return False
RoomHeater
# 4 -- RoomHeater - NOT IMPLEMENTED YET
class RoomHeater(Controllers):
passIntakeFan
# 5 -- IntakeFan - NOT IMPLEMENTED YET
class IntakeFan(Controllers):
passOutTakeFan
# 6 -- OutTakeFan - NOT IMPLEMENTED YET
class OutTakeFan(Controllers):
pass
Alerts (alerts.py)
BASE Class
# Alert class
# this module contains the alert class used to
# - save alert message to the data repository
# - sending message in an email to the system Admin
# - logging message to stderr
#######Child classes that inherit from base class: Sensors
#type -- Nane
# 1 -- RoomHumidityAlert
# 2 -- WaterTempAlert
# 3 -- LightAlert
# 4 -- RommTempAlert
# 5 -- other - NOT IMPLEMENTED YET
#set up logging
# Logging Levels
# The numeric values of logging levels are given in the following table.
# These are primarily of interest if you want to define your own levels,
# and need them to have specific values relative to the predefined levels.
# If you define a level with the same numeric value, it overwrites the predefined value;
# the predefined name is lost.
# Level Numeric value
# CRITICAL 50
# ERROR 40
# WARNING 30
# INFO 20
# DEBUG 10
# NOTSET 0
import logging
logging.basicConfig(
format='%(asctime)s.%(msecs)03d %(levelname)-8s %(message)s',
level=logging.NOTSET,
datefmt='%Y-%m-%d %H:%M:%S')
# BASE CLASS Alerts
class Alerts:
def __init__(self,type):
self.type = type
self.message = "none"
#Valud Values NOTSET, DEBUG, INFO, WARNING,ERROR, CRITICAL
self.severity = logging.NOTSET
# sendEmail to be implemented
import email
def sendEmail(message):
passChild Classes that inheret from Alerts
RoomHumidityAlert
import db
# 1 -- RoomHumidityAlert
class RoomHumidityAlert(Alerts):
def __init__(self,type=1):
super().__init__(type)
def send(self,message):
self.message = message
self.severity = logging.WARNING
sendEmail(message)
logging.warning("""ALERT-WARNING: RoomHumidity({:})""".format(message))
database=db.Db()
database.putAlert(self.type, self.message,self.severity)WaterTempAlert
# 2 -- WaterTempAlert
class WaterTempAlert(Alerts):
def __init__(self,type=2):
super().__init__(type)
def send(self,message):
self.message = message
self.severity = logging.CRITICAL
sendEmail(message)
logging.warning("""ALERT-CRITICAL: WaterTemp({:})""".format(message))
database=db.Db()
database.putAlert(self.type, self.message,self.severity)RoomTempAlert
# 4 -- RommTempAlert
class RommTempAlert(Alerts):
def __init__(self,type=4):
super().__init__(type)
def send(self,message):
self.message = message
self.severity = logging.WARNING
sendEmail(message)
logging.warning("""ALERT-WARNING: RommTemp({:})""".format(message))
database=db.Db()
database.putAlert(self.type, self.message,self.severity)
LightAlert
# 3 -- LightAlert
class LightAlert(Alerts):
def __init__(self,type=3):
super().__init__(type)
def send(self,message):
self.message = message
self.severity = logging.CRITICAL
sendEmail(message)
logging.warning("""ALERT-CRITICAL: Light({:})""".format(message))
database=db.Db()
database.putAlert(self.type, self.message,self.severity)
Timers
# 5 -- OTHER - Critical
class OtherAlert(Alerts):
def __init__(self,type=5):
super().__init__(type)
def send(self,message):
self.message = message
self.severity = logging.CRITICAL
sendEmail(message)
logging.warning("""ALERT-CRITICAL: OTHER({:})""".format(message))
database=db.Db()
database.putAlert(self.type, self.message,self.severity)
Timers (timer.py)
BASE Class
#Timer Class
#sets a timer
#set up logging
import logging
logging.basicConfig(
format='%(asctime)s.%(msecs)03d %(levelname)-8s %(message)s',
level=logging.INFO,
datefmt='%Y-%m-%d %H:%M:%S')
#######Child classes that inherit from base class: Timers
#type -- Nane
# 1 -- GenericTImer
# 2 -- WaterPumpTImer
# 3 -- LightTimer45
# 4 -- LightTimer15
#BASE Class
class Timers(object):
def __init__(self,type):
self.status=""
self.type = type
import time
def countdown(t):
while t:
mins,secs=divmod(t,60)
status = " STATUS:{:02d}:{:02d}".format(mins,secs)
print(status ,end="\r")
time.sleep(1)
t-=1Child Classes that inheret from Timers
Generic Timer
import db
#1 generic timer
class timer(Timers):
def __init__(self,type=1):
super().__init__(type)
def Set(self,value):
mins,secs=divmod(value,60)
status = "Start Timer for: {:02d}:{:02d}".format(mins,secs)
database=db.Db()
database.putTimer(self.type,status)
logging.info(status)
countdown(value)
logging.info("DONE Timer: ")
LightTimer45
# 3 -- LightTimer45
class LightTimer45(Timers):
def __init__(self,type=3):
super().__init__(type)
def Set(self):
value = 60 * 45 # set timer for 46 miinutes
mins,secs=divmod(value,60)
status = "Start Timer for: {:02d}:{:02d}".format(mins,secs)
database=db.Db()
database.putTimer(self.type,status)
logging.info(status)
countdown(value)
logging.info("DONE Timer: ")LightTimer15
# 4 -- LightTimer15
class LightTimer15(Timers):
def __init__(self,type=4):
super().__init__(type)
def Set(self):
value = 60 * 15 # set timer for 15 miinutes
mins,secs=divmod(value,60)
status = "Start Timer for: {:02d}:{:02d}".format(mins,secs)
database=db.Db()
database.putTimer(self.type,status)
logging.info(status)
countdown(value)
logging.info("DONE Timer: ")
WaterpumpTimer
# 2 -- WaterPumpTimer
class WaterPumpTimer(Timers):
def __init__(self,type=2):
super().__init__(type)
def Set(self):
value = 60 #set timer to 1 minute
mins,secs=divmod(value,60)
status = "Start Timer for: {:02d}:{:02d}".format(mins,secs)
database=db.Db()
database.putTimer(self.type,status)
logging.info(status)
countdown(value)
logging.info("DONE Timer: ")
DATA (db.py)
BASE Class
# Data Repository Class
# Connect to MongoDB Atlas in the cloud.
# use the eas database to store telemetry data
from pymongo import MongoClient
import datetime
class Db(object):
def __init__(self):
uri = "<YOUR CONNECTION STRING HERE>"
client = MongoClient(uri)
self.db = client.eas
Database PUT methods:
PutSensor
def putSensor(self,type,value):
timestamp = datetime.datetime.now()
data = { "type" : type,"value": value, "timestamp":timestamp }
# 1 - light
# 2 - waterTemp
# 3 - humidity
# 4 - roomTemp
if type == 1:
self.db.light.insert_one(data)
if type == 2:
self.db.waterTemp.insert_one(data)
if type == 3:
self.db.humidity.insert_one(data)
if type == 4:
self.db.roomTemp.insert_one(data)
PutController
def putController(self,type,value):
timestamp = datetime.datetime.now()
data = { "type" : type,"value": value, "timestamp":timestamp }
#type -- Nane
# 1 -- LedLight
# 2 -- WaterPump
# 3 -- WaterHeater
# 4 -- RoomHeater - NOT IMPLEMENTED YET
# 5 -- IntakeFan - NOT IMPLEMENTED YET
# 6 -- OutTakeFan - NOT IMPLEMENTED YET
if type == 1:
self.db.LedLight.insert_one(data)
if type == 2:
self.db.WaterPump.insert_one(data)
if type == 3:
self.db.WaterHeater.insert_one(data)
if type == 4:
self.db.RoomHeater.insert_one(data)
if type == 5:
self.db.IntakeFan.insert_one(data)
if type == 6:
self.db.OutTakeFan.insert_one(data)
PutAlert
def putAlert(self,type,message,severity):
timestamp = datetime.datetime.now()
data = { "type":type,"severityLevel":severity,"message":message,"timestamp":timestamp }
self.db.Alerts.insert_one(data)
PutTimer
def putTimer(self,type,status):
timestamp = datetime.datetime.now()
data = { "type":type,"status":status,"timestamp":timestamp }
self.db.Timers.insert_one(data)
Conclusion and summary
- Ths blog documents the classes that I wrote to implement my design diagram from my Blog# 7 EAS - System Designs -- Software Design
- It includes all the code to be used by the python scripts to be produced and featured in a later blog post.
- Unit test of each module, will be featured in a future blog post.
- I started using Microsoft Visual Code, which I was pleased to find, ran the same as on Windows. I had a few problems getting familier to the Python environment (I use C# on the PC), but all in all it was a plesent experience
- Part 1 and 2 of this blog post took a lot of my time to complete, but I was able to get most of the connections in my design implemented. I spent a lot of time researching and experimenting with the new technologies.
- I am farlly new to Python programming and I needed to get up to speed with Object Oriented Programming in the language. I'll need to do some refactoring, but in order to reach the challenge deadline , I'm going to plow ahead.
- I will still need to implement the email fuction in alerts, I just didn't have the time.
- The mongoDB database section started to get to long, so I decided to include it in another blog. I had a lot of fun with it, and it will help with analysing the data generated during the growing cycle.
- Be sure to take a look at the next blog. The DATA class uses mongoDB and it will show how flexible this tool is to store data.
- Now onto testing the classes.
REFERENCES | |
|---|---|
| Mongo Atlas | https://www.mongodb.com/cloud/atlas/lp/day-zero?tck=brand-campaign-page |
| Mongo Course | https://university.mongodb.com/mercury/M220P/2019_May/overview |
| Mongo Documentation | https://docs.mongodb.com/ |
| Mongo client API | http://api.mongodb.com/python/current/api/pymongo/mongo_client.html |
,<<< PREVIOUS BLOG | NEXT BLOG >>> |
|---|---|
| Blog# 8.2 (Part 2 of 2) EAS - DATA REPOSITORY MongoDB |




