-
Notifications
You must be signed in to change notification settings - Fork 0
/
control.py
252 lines (209 loc) · 9.33 KB
/
control.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
"""
control.py
Author: Mike Paxton
Creation Date: 03/30/20
Python Version: 3
Free and open for all to use. But put credit where credit is due.
------------------------------------------------------------------------------------------------------------------------
NOTE: As of September 7th 2020 we lost our chicken coop and my workshop where I did my electronics projects to a wildfire
here in Oregon. Because of this I'm currently not making changes to this file. I've rebuilt the coop but only running
a Raspberry Pi with the bare minimum controls found in main.py
Once I rebuild my workshop I'll dive back into the further automation of the coop.
-----------------------------------------------------------------------------------------------------------------------
OVERVIEW:-----------------------------------------------------------------------
This script controls and monitors various aspects of the chicken coop such temperature, lighting and solar output.
Monitoring information displayed on a 20x4 LCD. A momentary push button to turn the display on and
start the cycling of the information. The purpose for using the button is so that the LCD does not waste
solar battery power.
PYTHON LIBRARIES NEEDED:-----------------------------------------------------------
gpiozero
adafruit-circuitpython-am2320
adafruit-circuitpython-ina260
i2c_lcd_driver
astral - Version 1.10.1 NEEDED!!!
schedule
Note: The remainder should be installed as dependencies or already installed on the Raspberry Pi.
HARDWARE REQUIREMENTS:-----------------------------------------------------------
Runs on any Raspberry Pi.
I am using a Raspberry Pi 2 because it uses less power than newer models which is important when running from a solar
charged battery.
2004 LCD for displaying information.
Momentary button's for activating LCD and lighting.
A relay board for the internal coop lighting. I am using Pi-OT Module with 5 built-in relays.
Two ina260 sensors from Adafruit to monitor both solar and battery voltage/current.
UPDATES:------------------------------------------------------------------------
04/04/20 - Started working on LCD information display.
04/13/20 - LCD information is working correctly.
04/24/20 - Added batterystatus to LCD info dispayed.
04/26/20 - Fixed issue with ina260's only using default i2c address. Added address 0x40 to solar and 0x41 to battery.
Fixed issue with ina260's not displaying stats on LCD. Changed Mode to CONTINUOUS as TRIGGERED was not working.
Fixed issue with Light Relay not working, was using wrong GPIO for Pi-IOT relay #1. Now using GPIO 5
Changed Light On Button to GPIO 17
Changed LCD Button to GPIO 27
05/05/20 - Added a debug function to allow printing of messages to terminal.
05/14/20 - Added Astral, Schedule modules and function astral_update() so I can display sunrise and sunset on LCD.
10/31/20 - Fixed Astral. Now using most recent version of astral (2.2).
"""
# TODO: Look into using InfluxDB and Grafana to log sensor data.
from gpiozero import Button, CPUTemperature
import gpiozero
import adafruit_am2320
from adafruit_ina260 import INA260, Mode, AveragingCount
import i2c_lcd_driver
import board
import busio
import sys
import time
from datetime import date
import datetime
from astral import LocationInfo
from astral.sun import sun
import pytz
import schedule
# Initialize lcd
lcd = i2c_lcd_driver.lcd(0x27)
# GPIO button used to toggle Light relay.
lightsOnRelay = 21 # Coop light relay pin
lightOnButton = Button(17) # Coop light button.
# GPIO button to turn on/off LCD.
lcdButton = Button(27)
# create a relay object.
# Triggered by the output pin going low: active_high=False.
# Initially off: initial_value=False
coopLightRelay = gpiozero.OutputDevice(lightsOnRelay, active_high=False, initial_value=False)
# Set to True will turn on debug printing to console.
debug = True
# Initiate variables for astral_update function.
opentime = 0
closetime = 0
def current_time():
# Used if you opt to print current date/time of opening and closing door.
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
return now
def debug_print(message):
if debug:
print(message + current_time())
def fahrenheit(temperature):
"""Function takes in celsius temperature and returns temp in Fahrenheit"""
temperature = temperature * 9.0 / 5.0 + 32.00
return temperature
def am2320():
"""Function initiates AM2320 sensor and returns temperature and humidity"""
i2c = busio.I2C(board.SCL, board.SDA) # create the I2C shared bus for AM2320 Temp/Humidity sensor.
am = adafruit_am2320.AM2320(i2c)
cooptemp = round(fahrenheit(am.temperature), 2)
coophumidity = am.relative_humidity
return cooptemp, coophumidity
def solarstatus():
"""Function initiates ina260 at address 0x40 and returns current, voltage and power of solar panel."""
i2c = board.I2C()
ina260 = INA260(i2c, 0x40)
ina260.averaging_count = AveragingCount.COUNT_4
ina260.mode = Mode.CONTINUOUS
current = ina260.current
voltage = ina260.voltage
power = ina260.power
return current, voltage, power
def batterystatus():
"""Function initiates ina260 at address 0x41 and returns current, voltage and power of battery."""
i2c = board.I2C()
ina260 = INA260(i2c, 0x41)
ina260.mode = Mode.CONTINUOUS
current = ina260.current
voltage = ina260.voltage
power = ina260.power
return current, voltage, power
def set_coop_light_relay(status):
"""Function called to set the initial state of the light relay. Under current programing should always be False."""
if status:
debug_print('Setting relay: ON ')
coopLightRelay.on()
else:
debug_print('Setting relay: OFF ')
coopLightRelay.off()
def toggle_coop_light_relay():
"""Function called to turn on/off the light on relay"""
debug_print('toggling relay ')
coopLightRelay.toggle()
def astral_update():
"""Function grabs sunrise and dusk using your location and creates a schedule of events
You can change your location by modifying the fourth line of function.
You may specify alternate open and close times by modifying 'sunrise' and 'dusk'. See astral docs for alternate
times of day"""
global opentime
global closetime
# astral.Location format is: City, Country, Long, Lat, Time Zone, elevation.
city = LocationInfo('lincoln city', 'USA', 'US/Pacific', 45.014, -123.909)
s = sun(city.observer, date=date.today(), tzinfo=pytz.timezone(city.timezone))
opentime = (str(s['sunrise'].isoformat())[11:16]) # Strips date.time to just the time.
closetime = (str(s['sunset'].isoformat())[11:16])
return opentime, closetime
def coopstats():
"""Function displays various sensor readings on LCD."""
debug_print('LCD Button Pressed: ')
lcd.backlight(1) # Turn LCD backlight on
lcd.lcd_clear()
cooptemp, coophudity = am2320()
lcd.lcd_display_string('Chicken Coop', 1, 4) # String, row, column
lcd.lcd_display_string('Temp: ' + str(cooptemp) + chr(223), 2, 0)
lcd.lcd_display_string('Humidity: ' + str(coophudity) + chr(223), 3, 0)
time.sleep(3)
lcd.lcd_clear()
current, voltage, power = solarstatus() # Grab solar panel voltage, current, power and display it.
lcd.lcd_display_string('Solar Status', 1, 4)
lcd.lcd_display_string('Voltage: %.2f V' % voltage, 2, 0)
lcd.lcd_display_string('Current: %.2f mA' % current, 3, 0)
lcd.lcd_display_string('Power: %.2f mW' % power, 4, 0)
time.sleep(4)
lcd.lcd_clear()
current, voltage, power = batterystatus() # Grab battery voltage, current, power and display it.
lcd.lcd_display_string('Battery Status', 1, 3)
lcd.lcd_display_string('Voltage: %.2f V' % voltage, 2, 0)
lcd.lcd_display_string('Current: %.2f mA' % current, 3, 0)
lcd.lcd_display_string('Power: %.2f mW' % power, 4, 0)
time.sleep(4)
lcd.lcd_clear()
lcd.lcd_display_string('Open & Close Time', 1, 2)
lcd.lcd_display_string('Sunrise: ' + str(opentime), 2, 0)
lcd.lcd_display_string('Sunset: ' + str(closetime), 3, 0)
time.sleep(4)
lcd.lcd_clear()
cpu = CPUTemperature()
lcd.lcd_display_string('CPU Temperature', 1, 2)
lcd.lcd_display_string('Temp: ' + str(cpu.temperature) + ' C', 2, 0) # Display CPU temperature.
time.sleep(3)
lcd.lcd_clear()
lcd.backlight(0) # Turn LCD backlight off.
def startup_display():
lcd.backlight(1)
lcd.lcd_clear()
lcd.lcd_display_string('Welcome to', 1, 5)
lcd.lcd_display_string('Starclucks', 2, 5)
time.sleep(5)
lcd.lcd_clear()
lcd.backlight(0)
def main_loop():
while True:
schedule.run_pending()
if lightOnButton.is_pressed:
toggle_coop_light_relay()
if lcdButton.is_pressed:
coopstats()
time.sleep(1)
if __name__ == '__main__':
try:
astral_update() # Initiate astral_update. Get Astral times
schedule.every().day.at('12:01').do(astral_update) # Update astral times first thing every morning.
startup_display()
main_loop()
except RuntimeError as error:
print(error.args[0])
# except Exception as e:
# # this covers all other exceptions
# print(str(e))
except KeyboardInterrupt:
# turn the relay off
set_coop_light_relay(False)
print('\nExiting application\n')
# exit the application
sys.exit(0)