jackelliott93
Posts: 6
Joined: Sun Aug 02, 2015 10:22 am

Greenhouse Automation Code Help

Tue Aug 04, 2015 6:44 pm

Hey guys,
I currently am undertaking a project to automate my small greenhouse.
I am seeking to control the fan, heat mat and grow lights by the light sensor(GY30) and temperature and humidity sensor(DHT22).
In addition i am seeking to control my bore pump by using a solenoid at the tank corresponding with the water level obtained from the ultrasonic sensor (SR04)

I have written up the majority of the code but am having trouble finding any reference to code for the light sensor after looking for a significant period. Also i wish to thread the 2 while statement so they run concurrently and am unsure how to do this.

I have the following code:

Code: Select all

import common
import threading
from queue import Queue
import RPi.GPIO as GPIO
import time
from time import sleep

GPIO.setmode(GPIO.BCM)
FAN=18
LIGHT=19
HEAT=20
PUMP=21
TRIG=23
ECHO=24
TEMP=25

# Set relay pins as output
GPIO.setup(FAN, GPIO.OUT)#FAN
GPIO.setup(GROW, GPIO.OUT)#LIGHT
GPIO.setup(HEAT, GPIO.OUT)#HEAT MAT
GPIO.setup(PUMP, GPIO.OUT)#Water Pump
GPIO.setup(ECHO, GPIO.IN)#Ultrasonic sensor echo SR04
GPIO.setup(TRIG, GPIO.OUT)#Ultrasonic sensor trigger SR04
GPIO.setup(TEMP, GPIO.IN)#Temp Sensor DHT22
GPIO.setup(LIGHTSENSOR, GPIO.IN) #LIGHT SENSOR GY30
   
def get_temperature():
    result, humidity, temp = driver.read(sensor, int(TEMP))
    temperature=round(temp,2)
    
def get_water_level():
GPIO.output(TRIG,False)
time.sleep(2)
GPIO.output(TRIG,True)
time.sleep(0.00001)
GPIO.output(TRIG,False)
while GPIO.input(ECHO)==0:
    pulse_start=time.time()
while GPIO.input(ECHO)==1:
    pulse_end=time.time()
pulse_duration=pulse_end - pulse_start
distance=pulse_duration*17150
distance=round(distance,2)
water_level=distance

def get_light_level():

#NEED HELP HERE

    
##NEED HELP HERE WITH THREADING THIS
    
while (True):
    get_light_level
    get_temperature
    If temperature>30:
        GPIO.output(FAN, GPIO.HIGH) #Turn fun on when hot
    else if temperature<27:
        GPIO.output(FAN, GPIO.LOW)

    If temperature<5:
        GPIO.output(HEAT, GPIO.HIGH) #Turn heat mat on when cold
    else if temperature>10:
        GPIO.output(HEAT, GPIO.LOW) #Turn heat mat of when hot
		
    If light_level<50:
        GPIO.output(GROWLIGHT, GPIO.HIGH)
    else if light_level>70:
	GPIO.output(GROWLIGHT, GPIO.LOW)
    #sleep for 10 minutes
    sleep(600)

while water_level>0.5:
    get_water_level
    GPIO.output(PUMP, GPIO.HIGH))
    #Sleep for two hours
    sleep(7200))
    GPIO.output(PUMP, GPIO.LOW))
		
    # Sleep for 1 day
    sleep(86400)
  
Any help would be greatly appreciated.
Cheers :)

User avatar
DougieLawson
Posts: 37607
Joined: Sun Jun 16, 2013 11:19 pm
Location: Basingstoke, UK
Contact: Website Twitter

Re: Greenhouse Automation Code Help

Tue Aug 04, 2015 6:58 pm

Have a look at http://www.themagpi.com/issue/issue-27/ there's an article on the Matboard with some python code that drives that hardware add-on.
Note: Having anything humorous in your signature is completely banned on this forum. Wear a tin-foil hat and you'll get a ban.

Any DMs sent on Twitter will be answered next month.

This is a doctor free zone.

asandford
Posts: 1998
Joined: Mon Dec 31, 2012 12:54 pm
Location: Waterlooville

Re: Greenhouse Automation Code Help

Tue Aug 04, 2015 7:58 pm

Forget python.

I did the same sort of thing early last year with my greenhouse, and it was horrible.

I reworked the whole thing in Node Red (NR) in an afternoon, and it's been working fine for the last 10 months.

It consists of an RPi with an Arduino Nano clone hanging (literally) off the USB. The nano has a DHT11, soil moisture and light sensor (LDR) as well as a 433MHz transmitter attached.

I have written a 'flow' that:
Once a minute reads the temp and humidiy - if the temp goes below 5C t turns on an oil filled radiator and if it goes above 35C turns on extractor fans (same happens if the humidity rises above 85%)
Using the Sunset-Sunrise node, it switches on the pond lights between dusk and dawn
If the soil on a reference pot gets too dry, it turns on a pump for a minute.

All the mains connected devices are plugged into radio controlled sockets, and the nano just sends codes to turn them off and on (Maplin kit, 4 channel, 4 button - 16 devices - well defined on and off codes for them all).

All actions are timestamped and sent across MQTT to a MySQL DB running on another pi (could have been done with just a MySQL node but I was testing MQTT<==>RabbitMQ<==>MySQL).

The whole setup is pretty much self contained (a wifi link is needed for MQTT, and a broker, but none of it is needed for the core processes)

NR is asynchronous and non-blocking, so it can be waiting for one set of data while you send other commands / process inputs/ whatever.

The code running on the nano is a modified version of LLAP so that if I send a--DHT------ command, I get a--DHTTEM:XX and a--DHTHUM:XX back, also I can send a--REMOTEXYZ which maps to channel, button On/Off codes sent.

The whole thing is only 15 nodes including manual overrides (an additional 6 allow me to control the patio, floodlight and decking lights as well).

The next step is to integrate my weather station into the setup

ame
Posts: 3172
Joined: Sat Aug 18, 2012 1:21 am
Location: New Zealand

Re: Greenhouse Automation Code Help

Wed Aug 05, 2015 12:18 am

I recommend you should write a standalone program to read the light sensor. After it's working you can import it as a module into your main program.

Regarding concurrency, you could probably do it with a simple state machine. Your main loop should execute as often as possible, and in the loop you check various conditions. For the pump you need a state variable, and some code to move between states.

i.e.

Code: Select all

if pumping_state==0: # Initial state
    turn pump off
    reset other things and variables
    pumping_state=1 # Initialisation done.  Move to next state.
elif pumping_state==1: # Pump off.  Waiting for water level to increase.
    get_water_level
    if water_level > 0.5:
        turn_on_pump
        pump_start_time=time.time()
        pumping_state=2 # The water level is above 0.5 and the pump is running
elif pumping_state==2: # Pump running.  Waiting for 2 hours.
    if time.time()-pump_start_time > 7200:
        turn_off_pump
        pump_end_time=time.time()
        pumping_state=3 # The pump ran for 2 hours and is now off
elif pumping_state==3: # Pump off.  Waiting for 1 day.
    if time.time()-pump_end_time > 86400:
        pumping_state=1 # After one day, return to state 1 and wait for water level to increase
This is a quick, hacked-together illustration. As you can see, you can execute this code as often as you like, but the state won't change until certain conditions are met. In other parts of your code you can check the state and do other stuff:
e.g.

Code: Select all

if pumping_state==0: # Initial state
    print("Initialising pump")
elif pumping_state==1: # Pump off.  Waiting for water level to increase.
    print("Waiting for water level > 0.5")
elif pumping_state==2: # Pump running.  Waiting for 2 hours.
    print("Pump running.  Will run for 2 hours.')
elif pumping_state==3: # Pump off.  Waiting for 1 day.
    print("Pump has run for 2 hours.  Waiting for 1 day.")
You can have as many states as you like, and you don't have to visit them sequentially.

User avatar
KnightOfPi
Posts: 50
Joined: Sun Dec 28, 2014 12:57 pm
Location: Vienna - Austria
Contact: Website

Re: Greenhouse Automation Code Help

Wed Aug 05, 2015 7:11 am

For managing parallel tasks, i would recommend multiprocessing instead of threading:
https://docs.python.org/2/library/multiprocessing.html
http://stackoverflow.com/questions/3044 ... ing-python

It uses real processes instead of threads and works around the Global Interpreter Lock by this.

Code: Select all

multiprocessing.active_children()
may become handy for controlling the processes from the main process.
Check out www.knight-of-pi.org for many beginner-friendly tutorials!

jackelliott93
Posts: 6
Joined: Sun Aug 02, 2015 10:22 am

Re: Greenhouse Automation Code Help

Wed Aug 05, 2015 5:32 pm

Many thanks for the excellent advice guys! Really helped me a lot.
Could i have further advice on whether the following code will work and if there any improvements to be made.
Also will this code run continuously and if there is a power outage is there a way i can make it run on startup.
I am open for also additional interesting ideas to make my greenhouse more tech savvy.

Code: Select all

import common
import smbus
import RPi.GPIO as GPIO
import time
from time import sleep
GPIO.setmode(GPIO.BCM)

#GPIO PORTS MUST CHANGE BEFORE OPERATING!!!!!!!
FAN=18
GROW=19
HEAT=20
PUMP=21
TRIG=23
ECHO=24
TEMP=25
LIGHTSENSOR=26

#Setting state defaults
pumping_state=0
water_email_state=0
pump_total_count=0
pump_weekly_count=0
fan_state=0
light_state=0
heat_state=0

# Set relay pins as output
GPIO.setup(FAN, GPIO.OUT)#FAN
GPIO.setup(GROW, GPIO.OUT)#LIGHT
GPIO.setup(HEAT, GPIO.OUT)#HEAT MAT
GPIO.setup(PUMP, GPIO.OUT)#Water Pump
GPIO.setup(ECHO, GPIO.IN)#Ultrasonic sensor echo SR04
GPIO.setup(TRIG, GPIO.OUT)#Ultrasonic sensor trigger SR04
GPIO.setup(TEMP, GPIO.IN)#Temp Sensor DHT22
GPIO.setup(LIGHTSENSOR, GPIO.IN) #LIGHT SENSOR GY30

#SENSOR FUNCTIONS
def get_temperature():
    result, humidity, temp = driver.read(sensor, int(TEMP))
    temperature=round(temp,2)
    
def get_water_level():
    GPIO.output(TRIG,False)
    time.sleep(2)
    GPIO.output(TRIG,True)
    time.sleep(0.00001)
    GPIO.output(TRIG,False)
    while GPIO.input(ECHO)==0:
        pulse_start=time.time()
    while GPIO.input(ECHO)==1:
        pulse_end=time.time()
    pulse_duration=pulse_end - pulse_start
    distance=pulse_duration*17150
    distance=round(distance,2)
    height_sensor=2.02 #height of sensor from ground
    max_water_level=1.95
    water_level=height_sensor-distance
    water_level_percent=water_level/max_water_level
    
def get_light_level():
    DEVICE     = 0x23 # Default device I2C address
 
    POWER_DOWN = 0x00 # No active state
    POWER_ON   = 0x01 # Power on
    RESET      = 0x07 # Reset data register value
 
    # Start measurement at 4lx resolution. Time typically 16ms.
    CONTINUOUS_LOW_RES_MODE = 0x13
    # Start measurement at 1lx resolution. Time typically 120ms
    CONTINUOUS_HIGH_RES_MODE_1 = 0x10
    # Start measurement at 0.5lx resolution. Time typically 120ms
    CONTINUOUS_HIGH_RES_MODE_2 = 0x11
    # Start measurement at 1lx resolution. Time typically 120ms
    # Device is automatically set to Power Down after measurement.
    ONE_TIME_HIGH_RES_MODE_1 = 0x20
    # Start measurement at 0.5lx resolution. Time typically 120ms
    # Device is automatically set to Power Down after measurement.
    ONE_TIME_HIGH_RES_MODE_2 = 0x21
    # Start measurement at 1lx resolution. Time typically 120ms
    # Device is automatically set to Power Down after measurement.
    ONE_TIME_LOW_RES_MODE = 0x23
 
    bus = smbus.SMBus(1)  # Rev 2 Pi uses 1
 
    def convertToNumber(data):
        # Simple function to convert 2 bytes of data
        # into a decimal number
        return ((data[1] + (256 * data[0])) / 1.2)
 
    def readLight(addr=DEVICE):
        data = bus.read_i2c_block_data(addr,ONE_TIME_HIGH_RES_MODE_1)
        light_level=convertToNumber(data)

    readLight()

def send_water_level():
    print ("hi")
#need to enter code in here
    

while (True):
    get_light_level
    get_temperature
    
    if fan_state==0: #fan off
        if temperature>30:
            GPIO.output(FAN, GPIO.HIGH)#Turn fan on when hot
            fan_state=1
    elif fan_state==1: #fan running
        if temperature<27:
            GPIO.output(FAN, GPIO.LOW)
            fan_state=0
        
    if heat_state==0:#heat mat off
        if temperature<5:
            GPIO.output(HEAT, GPIO.HIGH) #Turn heat mat on when cold
            heat_state=1
    elif heat_state==1: #heat mat running
        if temperature>10:
            GPIO.output(HEAT, GPIO.LOW) #Turn heat mat off when hot

    if light_state==0: #light off	
        if light_level<20: 
            GPIO.output(GROWLIGHT, GPIO.HIGH)
            light_state=1
    if light_state==1: #light on
        if light_level>70:
            GPIO.output(GROWLIGHT, GPIO.LOW)
            light_state=0
    

    if pumping_state==0: #default pump off
        GPIO.output(PUMP, GPIO.LOW)
        pumping_state=1

    elif pumping_state==1: #waiting to check water level
        get_water_level
        if water_level_percent<0.8:
            GPIO.output(PUMP, GPIO.HIGH)
            pump_total_count=pump_total_count+1
            pump_weekly_count=pump_weekly_count+1
            pump_start_time=time.time()
            pumping_state=2
        
        if water_email_state==0: # EMAIL HASN'T BEEN SENT
            send_water_level()
            water_email_state==1
            time_email=time.time()
        elif water_email_state==1: #Check time since been sent
            time_since_email=time.time()-time_email
            if time_since_email>604800:
                send_water_level()
                water_email_state=1
                
    elif pumping_state==2: #pump on
        pump_run_time=(time.time()-pump_start_time)
        if pump_run_time>7200:
            GPIO.output(PUMP, GPIO.LOW)
            pump_end_time=time.time
            pumping_state=3 #pump ran for 2 hours and is now off
    elif pumping_state==3: #waiting for 1 day
            time_since_pump=time.time()-pump_end_time
            if time_since_pump>79200: #pump same time every day
                pumping_state=1
            
    sleep(600)
        
    
 

ame
Posts: 3172
Joined: Sat Aug 18, 2012 1:21 am
Location: New Zealand

Re: Greenhouse Automation Code Help

Wed Aug 05, 2015 10:47 pm

Woah, trigger!

Baby steps.

You've written a lot of code. You really need to write a little, then test it. Then write a bit more.

Anyway, I have two suggestions.

First, put lots if print statements in, to show what your program is doing while it is running.

Second, change all if these numbers into named constants:

Code: Select all

    if fan_state==0: #fan off
        if temperature>30:
            GPIO.output(FAN, GPIO.HIGH)#Turn fan on when hot
            fan_state=1
    elif fan_state==1: #fan running
        if temperature<27:
e.g. you have two temperature values, 30 and 27, so define them somewhere, then in your code you can have:

Code: Select all

if temperature>MAX_TEMP_START_COOLING:
.
.
.
if temperature<MIN_TEMP_STOP_COOLING:
This give you a lot more readability, and if you use the same constant in several places you only need to change its value in one place (you don't have to go hunting through the code for magic numbers and hoping you got them all).

Return to “Beginners”