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): pass
WaterTemp
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): pass
IntakeFan
# 5 -- IntakeFan - NOT IMPLEMENTED YET class IntakeFan(Controllers): pass
OutTakeFan
# 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): pass
Child 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-=1
Child 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 |