element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • About Us
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
1 Meter of Pi
  • Challenges & Projects
  • Design Challenges
  • 1 Meter of Pi
  • More
  • Cancel
1 Meter of Pi
Blog Mushrooms' Paradise #10. Code for the components controlled by Raspberry PI
  • Blog
  • Forum
  • Documents
  • Polls
  • Files
  • Events
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: ilvyanyatka
  • Date Created: 3 Jan 2021 1:34 AM Date Created
  • Views 1125 views
  • Likes 5 likes
  • Comments 4 comments
  • python
  • 1meterofpi
  • raspi
  • 1 meter of pi
  • 1 meter of pi - design challenge
  • raspberry pi
  • mushrooms' paradise
  • growing mushrooms
Related
Recommended

Mushrooms' Paradise #10. Code for the components controlled by Raspberry PI

ilvyanyatka
ilvyanyatka
3 Jan 2021

The whole Mushrooms' Paradise blog

 

    • The whole Python code
    • Code explanations
      • Imported Libraries and classes
      • Classes
        • Common properties for all the classes
        • Common methods for all the classes
        • Code for lights
        • Code for fan
        • Code for Humidifier
      • Initialization of ThingSpeak data
      • Main loop
      • Error handling code

 

 

This project has 3 types of components: mushrooms specific, vegetables specific and common.

Mushrooms specific components are fans and a humidifier.

Common components are Enviro HAT and lights.

Vegetable specific components are soil moisture sensors and water pumps.

Mushrooms specific and common components are controlled by Raspberry PI. Vegetables specific - by ESP32.

This post is about RPI part of the software. It is written in Python and here is the whole program on GitHub

 

The whole Python code

 

from __future__ import print_function
import paho.mqtt.publish as publish
import psutil
import string
import random
import sys


from smbus import SMBus
from bme280 import BME280
import RPi.GPIO as GPIO # Importing the GPIO library to use the GPIO pins of Raspberry pi
from time import sleep # Importing the time library to provide the delays in program
from datetime import datetime, timedelta


# setup GPIO pins
#GPIO.cleanup()         # clean up 
GPIO.setmode(GPIO.BCM)


humidifier_pin = 16  # Initializing pin
light_pin = 17
fan_pin = 27
print_sensor_values = 0



# classes




# controls humidifier
class humidifier :


    Pin = humidifier_pin
    AirHumidity = 0
    Temperature = 0
    FlagRun = 0
    FlagRunning = 0
    CheckTimeCycle = timedelta(hours = 3)
    CheckTimeOnInterval = timedelta(minutes = 15)
    LastCheckTime = datetime.now() - CheckTimeCycle
    DesiredHumidity = 70
    
    #setup enviro hat
    bus = SMBus(1)
    bme280 = BME280(i2c_dev=bus)




    def __init__(self, p=16):
        self.Pin = p
        GPIO.setup(self.Pin, GPIO.OUT)    # set GPIO pin as output  
    


    def ReadValues(self) :
        self.Temperature = self.bme280.get_temperature()
        self.AirHumidity = self.bme280.get_humidity()
        # there is a bug on Enviro HAT -
        # sometimes humidity sensor returns values over 90 when real value is not even close to that
        # in this case sleep for a second and reread the value
        if self.AirHumidity > 85:
            sleep(1)
            self.AirHumidity = self.bme280.get_humidity()
        if print_sensor_values == 1:
            print("Inside ReadValues")
            print ("Humidity is " + str(self.AirHumidity))
            print ("Temperature is " + str(self.Temperature))

    
    def Run(self) :
        
        Now = datetime.now()
        self.ReadValues()
        
        # if running currently
        if self.LastCheckTime + self.CheckTimeCycle < Now :
            # time to check
            print ("Humidity is " + str(self.AirHumidity))
            print ("Temperature is " + str(self.Temperature))
            
            if self.AirHumidity < self.DesiredHumidity :
                GPIO.output(self.Pin, 1)
                print ("humidifier is running")
                self.FlagRunning = 1
            
            self.LastCheckTime = Now
                
        if self.FlagRunning :
            if self.AirHumidity > self.DesiredHumidity :
                GPIO.output(self.Pin, 0)
                print ("humidifier stopped")
                self.FlagRunning = 0
                self.FlagRun = 1
            if self.LastCheckTime + self.CheckTimeOnInterval < Now :
                # time to stop
                GPIO.output(self.Pin, 0)
                print ("humidifier stopped ")
                self.FlagRunning = 0
                self.FlagRun = 1


                    
    def ShutDown(self):
        GPIO.output(self.Pin, 0)
        self.FlagRunning = 0
            


    




# controls led lights
class light :


    Pin = light_pin
    CheckTimeCycle = timedelta(days = 1)
    CheckTimeOnInterval = timedelta(hours = 7)
    LastCheckTime = datetime.now() - CheckTimeCycle
    FlagRun = 0
    FlagRunning = 0




    def __init__(self, p=16):
        self.Pin = p
        GPIO.setup(self.Pin, GPIO.OUT)
        


    def Run(self):
        Now = datetime.now()
        
        # if running currently
        if self.LastCheckTime + self.CheckTimeCycle < Now :
            GPIO.output(self.Pin, 1)
            print ("lights are ON")
            self.FlagRunning = 1
            self.LastCheckTime = Now
                
        if self.FlagRunning :
            if self.LastCheckTime + self.CheckTimeOnInterval < Now :
                # time to stop
                GPIO.output(self.Pin, 0)
                print ("lights are OFF")
                self.FlagRunning = 0
                self.FlagRun = 1


     
    def ShutDown(self):
        GPIO.output(self.Pin, 0)
        self.FlagRunning = 0


     
# controls fans
class fan:


    Pin = fan_pin
    CheckTimeCycle = timedelta(hours = 1)
    CheckTimeOnInterval = timedelta(minutes = 5)
    LastCheckTime = datetime.now() - CheckTimeCycle
    FlagRun = 0
    FlagRunning = 0
    


    def __init__(self, p=11):
        self.Pin = p
        GPIO.setup(self.Pin, GPIO.OUT)


    def Run(self):
        Now = datetime.now()
        
        # if running currently
        if self.LastCheckTime + self.CheckTimeCycle < Now :
            GPIO.output(self.Pin, 1)
            print ("fan is running")
            self.FlagRunning = 1
            self.LastCheckTime = Now
                
        if self.FlagRunning :
            if self.LastCheckTime + self.CheckTimeOnInterval < Now :
                # time to stop
                GPIO.output(self.Pin, 0)
                print ("fan stopped ")
                self.FlagRunning = 0
                self.FlagRun = 1




    def ShutDown(self):
        GPIO.output(self.Pin, 0)
        self.FlagRunning = 0




SoilHumidityR1 = 2000
SoilHumidityR2 = 2000
SoilHumidityL1 = 2000
SoilHumidityL2  = 2000




# init variables for ThingSpeak
timeFormat = '%x %X'


# The ThingSpeak Channel ID.
# Replace <YOUR-CHANNEL-ID> with your channel ID.
channelID = "1263910"


# The write API key for the channel.
# Replace <YOUR-CHANNEL-WRITEAPIKEY> with your write API key.
writeAPIKey = "<YOUR-CHANNEL-WRITEAPIKEY>"


# The hostname of the ThingSpeak MQTT broker.
mqttHost = "mqtt.thingspeak.com"


# Use any username.
mqttUsername = "Mushrooms"


# use any clientID.
clientID = 'Test'


# Your MQTT API key from Account > My Profile.
mqttAPIKey = "<MQTT-API-KEY>"


#Define the connection type as websockets, and set the port to 80.
tTransport = "websockets"
tPort = 80


# Create a topic string in the form shown in Publish to a Channel Feed that updates field 1 and field 2 of the specified channel simultaneously.
topic = "channels/" + channelID + "/publish/" + writeAPIKey
    
# parameters for publishing frequence
PublishTimeCycle = timedelta(hours=1)
# first publish will be in 2 minutes
FirstPublishDelay = timedelta(minutes=2)
LastPublished = datetime.now() - PublishTimeCycle + FirstPublishDelay




# components objects
Humidifier = humidifier(humidifier_pin)
Light = light(light_pin)
Fan = fan(fan_pin)


    # ************** main loop


while(1):
    try:
        Now = datetime.now()
            
        Humidifier.Run()
        Fan.Run()
        Light.Run()
        
        # *********** for thingspeak
        
        # if time to publish
        if LastPublished + PublishTimeCycle < Now :
            
            # build the payload string.
            payload = "field1=" + str(Humidifier.AirHumidity) + "&field2=" + str(Humidifier.Temperature)
            payload += "&field3=" + str(SoilHumidityR1) + "&field4=" + str(SoilHumidityR2)
            payload += "&field5=" + str(SoilHumidityL1) + "&field6=" + str(SoilHumidityL2)
            payload += "&field7=" + str(Humidifier.FlagRun) + "&field8=" + str(Fan.FlagRun)




            # attempt to publish this data to the topic.
            
            publish.single(topic, payload, hostname=mqttHost, transport=tTransport, port=tPort,auth={'username':mqttUsername,'password':mqttAPIKey})
            # print (" Published on " + datetime.now('%Y-%m-%d %H:%M', "EST"))
            LastPublished = datetime.now()


            print (" Published on " + LastPublished.strftime(timeFormat))
            print("Values: Humidity: " + str(Humidifier.AirHumidity) +
                  " Temperature: " + str(Humidifier.Temperature) +
                  " Humidifier Run: " + str(Humidifier.FlagRun) +
                  " Fan Run: " + str(Fan.FlagRun))


            # reset Humidifier and Fan FlagRun so we can see if they run between this and next publish
            Humidifier.FlagRun = 0
            Fan.FlagRun = 0
            
            sleep(5)
    except (KeyboardInterrupt):
            
        print ("inside KeyboardInterrupt. Before ShutDown")
        Humidifier.ShutDown()
        Fan.ShutDown()
        Light.ShutDown()
        del Humidifier
        del Fan
        del Light
        break
        GPIO.cleanup()
    
        
    except:
        e = sys.exc_info()[0]
        print( "<p>Error: %s</p>" % e )
            
    
   

 

 

Code explanations

 

 

Imported Libraries and classes

 

paho.mqtt.publish to publish data on ThingSpeak

sys for error handling

smbus, bme280 to access Enviro HAT sensors

RPi.GPIO to access GPIO pins

datetime to work with time intervals

 

 

Classes

 

Raspberry PI is controlling humidifier, lights and fans. It also posts data to ThingSpeak channel so data can be monitored from anywhere, computers or mobile devices.

 

For each of these objects - humidifier, lights and fans - I created a separate class.

All classes have some common properties and methods.

 

Common properties for all the classes

 

Pin - RPI pin object is controlled by

LastCheckTime - the last time the object was checked

CheckTimeCycle - time cycle of the object

CheckTimeOnInterval - time interval the object should be in ON state

FlagRunning - flag indicating that object is in ON state

FlagRun - flag indicating that object was in ON state after data was published last

 

Common methods for all the classes

 

Run - the main object's method. For all the classes the code checks if it is time to run, and if run of the object is needed based on environmental parameters. If object need to run - signal 1 is sent to object's pin.

ShutDown - shutdowns the object. Basically this method sends 0 to the pin controlling the object. This method is called before exiting the program in case of exception.

Constructor for each object takes the pin number of the pin controlling the object.

 

If the setup needs to be customized in the future, for example light should be set for different duration for mushrooms and vegetables parts of enclosure, it will be easy to do. I will just create another instance of light class, set te pin and desired duration and run it in main loop togeter with other objects.

 

Code for lights

 

class light :


    Pin = light_pin
    CheckTimeCycle = timedelta(days = 1)
    CheckTimeOnInterval = timedelta(hours = 7)
    LastCheckTime = datetime.now() - CheckTimeCycle
    FlagRun = 0
    FlagRunning = 0




    def __init__(self, p=16):
        self.Pin = p
        GPIO.setup(self.Pin, GPIO.OUT)
        


    def Run(self):
        Now = datetime.now()
        
        # if running currently
        if self.LastCheckTime + self.CheckTimeCycle < Now :
            GPIO.output(self.Pin, 1)
            print ("lights are ON")
            self.FlagRunning = 1
            self.LastCheckTime = Now
                
        if self.FlagRunning :
            if self.LastCheckTime + self.CheckTimeOnInterval < Now :
                # time to stop
                GPIO.output(self.Pin, 0)
                print ("lights are OFF")
                self.FlagRunning = 0
                self.FlagRun = 1


     
    def ShutDown(self):
        GPIO.output(self.Pin, 0)
        self.FlagRunning = 0

 

Lights run for 12 hours every day, but it is easily customizable either in the class code, or while creating an instance of the class.

 

 

Code for fan

 

class fan:


    Pin = fan_pin
    CheckTimeCycle = timedelta(hours = 1)
    CheckTimeOnInterval = timedelta(minutes = 5)
    LastCheckTime = datetime.now() - CheckTimeCycle
    FlagRun = 0
    FlagRunning = 0
    


    def __init__(self, p=11):
        self.Pin = p
        GPIO.setup(self.Pin, GPIO.OUT)


    def Run(self):
        Now = datetime.now()
        
        # if running currently
        if self.LastCheckTime + self.CheckTimeCycle < Now :
            GPIO.output(self.Pin, 1)
            print ("fan is running")
            self.FlagRunning = 1
            self.LastCheckTime = Now
                
        if self.FlagRunning :
            if self.LastCheckTime + self.CheckTimeOnInterval < Now :
                # time to stop
                GPIO.output(self.Pin, 0)
                print ("fan stopped ")
                self.FlagRunning = 0
                self.FlagRun = 1




    def ShutDown(self):
        GPIO.output(self.Pin, 0)
        self.FlagRunning = 0

Fans run for 10 minutes every hour. Time intervals an GPIO pin are easily customizable either in the class code, or while creating an instance of the class.

 

 

 

 

Code for Humidifier

 

class humidifier :


    Pin = humidifier_pin
    AirHumidity = 0
    Temperature = 0
    FlagRun = 0
    FlagRunning = 0
    CheckTimeCycle = timedelta(hours = 3)
    CheckTimeOnInterval = timedelta(minutes = 15)
    LastCheckTime = datetime.now() - CheckTimeCycle
    DesiredHumidity = 70
    
    #setup enviro hat
    bus = SMBus(1)
    bme280 = BME280(i2c_dev=bus)




    def __init__(self, p=16):
        self.Pin = p
        GPIO.setup(self.Pin, GPIO.OUT)    # set GPIO pin as output  
    


    def ReadValues(self) :
        self.Temperature = self.bme280.get_temperature()
        self.AirHumidity = self.bme280.get_humidity()
        
        # there is a bug on Enviro HAT -
        # sometimes humidity sensor returns values over 90 when real value is not even close to that
        # in this case sleep for a second and reread the value
        if self.AirHumidity > 85:
            sleep(1)
            self.AirHumidity = self.bme280.get_humidity()
        
        if print_sensor_values == 1:
            print("Inside ReadValues")
            print ("Humidity is " + str(self.AirHumidity))
            print ("Temperature is " + str(self.Temperature))


    
    def Run(self) :
        
        Now = datetime.now()
        self.ReadValues()
        
        # if running currently
        if self.LastCheckTime + self.CheckTimeCycle < Now :
            # time to check
            print ("Humidity is " + str(self.AirHumidity))
            print ("Temperature is " + str(self.Temperature))
            
            if self.AirHumidity < self.DesiredHumidity :
                GPIO.output(self.Pin, 1)
                print ("humidifier is running")
                self.FlagRunning = 1
            
            self.LastCheckTime = Now
                
        if self.FlagRunning :
            if self.AirHumidity > self.DesiredHumidity :
                GPIO.output(self.Pin, 0)
                print ("humidifier stopped")
                self.FlagRunning = 0
                self.FlagRun = 1
            if self.LastCheckTime + self.CheckTimeOnInterval < Now :
                # time to stop
                GPIO.output(self.Pin, 0)
                print ("humidifier stopped ")
                self.FlagRunning = 0
                self.FlagRun = 1


                    
    def ShutDown(self):
        GPIO.output(self.Pin, 0)
        self.FlagRunning = 0

 

Humidifier’s Run function is different from other objects because it does not run automatically when it is time to run, but checks humidity value from Enviro HAT and runs only if value is less than desired.

Humidifier runs for 15 minutes every 3 hours, if air humidity is less than 70%. Time intervals, minimum humidity value an GPIO pin are easily customizable either in the class code, or while creating an instance of the class.

ReadValues function read sensor values. There is an option to output all the values read, by setting flag print_sensor_values to 1.

Additionally, there seems to be a bug on Enviro HAT humidity sensor. First reading is ofter over 90%, when in reality humidity is much less, about 30%. As a workaround I check if humidity value is more than 85 and if so - sleep for a second and reread the value. It seems to work fine this way.

 

 

Initialization of ThingSpeak data

 

There are several ways to publish data on ThingSpeak, I decided to use websockets. Every method used MQTT protocol and library to do so is paho.mqtt. Description of it is here: Publish Using WebSockets in Python on a Raspberry Pi - MATLAB & Simulink

PBublishing is done in main loop, but before publishing ThingSpeak connection data need to be initialized

 

# The ThingSpeak Channel ID.
# Replace <YOUR-CHANNEL-ID> with your channel ID.
channelID = "1263910"


# The write API key for the channel.
# Replace <YOUR-CHANNEL-WRITEAPIKEY> with your write API key.
writeAPIKey = "<YOUR-CHANNEL-WRITEAPIKEY>"


# The hostname of the ThingSpeak MQTT broker.
mqttHost = "mqtt.thingspeak.com"


# Use any username.
mqttUsername = "Mushrooms"


# use any clientID.
clientID = 'Test'


# Your MQTT API key from Account > My Profile.
mqttAPIKey = "<MQTT-API-KEY>"


#Define the connection type as websockets, and set the port to 80.
tTransport = "websockets"
tPort = 80


# Create a topic string in the form shown in Publish to a Channel Feed that updates field 1 and field 2 of the specified channel simultaneously.
topic = "channels/" + channelID + "/publish/" + writeAPIKey
    
# parameters for publishing frequence
PublishTimeCycle = timedelta(hours=1)
# first publish will be in 2 minutes
FirstPublishDelay = timedelta(minutes=2)
LastPublished = datetime.now() - PublishTimeCycle + FirstPublishDelay

Main loop

 

In the main loop I run all the objects

 

        Humidifier.Run()
        Fan.Run()
        Light.Run()

 

And publish data on ThingSpeak.

 

ThingSpeak channel for this project can be found here: Mushrooms Paradise

 

Data that is published from RPI includes Air Humidity, Air Temperature, and if humidifier and fans run at least once from the last publising. These need to be monitored closely, for example this data will show that the humidifier needs to be refilled with water.

 

if LastPublished + PublishTimeCycle < Now :
            
            # build the payload string.
            payload = "field1=" + str(Humidifier.AirHumidity) + "&field2=" + str(Humidifier.Temperature)
            payload += "&field3=" + str(SoilHumidityR1) + "&field4=" + str(SoilHumidityR2)
            payload += "&field5=" + str(SoilHumidityL1) + "&field6=" + str(SoilHumidityL2)
            payload += "&field7=" + str(Humidifier.FlagRun) + "&field8=" + str(Fan.FlagRun)




            # attempt to publish this data to the topic.
            
            publish.single(topic, payload, hostname=mqttHost, transport=tTransport, port=tPort,auth={'username':mqttUsername,'password':mqttAPIKey})
            # print (" Published on " + datetime.now('%Y-%m-%d %H:%M', "EST"))
            LastPublished = datetime.now()


            print (" Published on " + LastPublished.strftime(timeFormat))



Error handling code

 

If an exception happens software will shutdown all the signals for all the objects, delete the objects, print the exception information and exit. Exceptions (including KeyboardInterrupt) are the only way to exit software

 

except (KeyboardInterrupt):
            
        print ("inside KeyboardInterrupt. Before ShutDown")
        Humidifier.ShutDown()
        Fan.ShutDown()
        Light.ShutDown()
        del Humidifier
        del Fan
        del Light
        break
        GPIO.cleanup()
    
        
    except:
        e = sys.exc_info()[0]
        print( "<p>Error: %s</p>" % e )





 







  • Sign in to reply

Top Comments

  • cjodrey
    cjodrey over 4 years ago +1
    Great work! You seem to have a solid mastery of the code. It will be exciting to see the mushrooms popping up in a tightly controlled environment like this!
  • DAB
    DAB over 4 years ago +1
    Nice update. DAB
Parents
  • DAB
    DAB over 4 years ago

    Nice update.

     

    DAB

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • ilvyanyatka
    ilvyanyatka over 4 years ago in reply to DAB

    Thank you. I just noticed today that I never actually added the GitHub link to a blog post. It must have looked confusing.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
Comment
  • ilvyanyatka
    ilvyanyatka over 4 years ago in reply to DAB

    Thank you. I just noticed today that I never actually added the GitHub link to a blog post. It must have looked confusing.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
Children
No Data
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube