Start tidying display.py

This commit is contained in:
Phil Howard
2020-05-26 15:25:02 +01:00
parent da895da6be
commit 487401e359

View File

@@ -2,6 +2,7 @@ import time
import ST7735 import ST7735
import random import random
import math import math
import logging
from PIL import Image, ImageDraw, ImageFont from PIL import Image, ImageDraw, ImageFont
from fonts.ttf import RobotoMedium as UserFont from fonts.ttf import RobotoMedium as UserFont
import RPi.GPIO as GPIO import RPi.GPIO as GPIO
@@ -9,14 +10,33 @@ import RPi.GPIO as GPIO
from grow.moisture import Moisture from grow.moisture import Moisture
from grow.pump import Pump from grow.pump import Pump
GPIO.setmode(GPIO.BCM) # Level at which a watering event is triggered
GPIO.setwarnings(False) trigger_level = [
0.5,
0.5,
0.5
]
# Level at which the alarm is sounded (watering has failed?)
alarm_level = [
0.2,
0.2,
0.2
]
# Dose settings: Pump Speed, and Dose Time
dose_settings = [
(0.7, 0.7),
(0.7, 0.7),
(0.7, 0.7)
]
BUTTONS = [5, 6, 16, 24] BUTTONS = [5, 6, 16, 24]
LABELS = ['A', 'B', 'X', 'Y'] LABELS = ['A', 'B', 'X', 'Y']
CHANNEL_COUNT = 3
channel_count = 3
channel_selected = 0 channel_selected = 0
alarm = False
sensors = [ sensors = [
Moisture(channel=1), Moisture(channel=1),
@@ -30,22 +50,70 @@ pumps = [
Pump(channel=3) Pump(channel=3)
] ]
GPIO.setup(BUTTONS, GPIO.IN, pull_up_down=GPIO.PUD_UP) logging.basicConfig(
format='%(asctime)s.%(msecs)03d %(levelname)-8s %(message)s',
level=logging.INFO,
datefmt='%Y-%m-%d %H:%M:%S')
font = ImageFont.truetype(UserFont, 14)
def handle_button(pin): def handle_button(pin):
global channel_selected global channel_selected, alarm
index = BUTTONS.index(pin) index = BUTTONS.index(pin)
label = LABELS[index] label = LABELS[index]
if label == 'A': if label == 'A': # Select Channel
channel_selected += 1 channel_selected += 1
channel_selected %= channel_count channel_selected %= CHANNEL_COUNT
if label == 'B': # Cancel Alarm
alarm = False
if label == 'X': # Set Wet Point
pass
if label == 'Y': # Set Dry Point
pass
print("Button {} pressed! Channel: {}".format(label, channel_selected)) print("Button {} pressed! Channel: {}".format(label, channel_selected))
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(BUTTONS, GPIO.IN, pull_up_down=GPIO.PUD_UP)
for pin in BUTTONS: for pin in BUTTONS:
GPIO.add_event_detect(pin, GPIO.FALLING, handle_button, bouncetime=150) GPIO.add_event_detect(pin, GPIO.FALLING, handle_button, bouncetime=150)
bar_colours = [
(192, 225, 254), # Blue
(196, 255, 209), # Green
(255, 243, 192), # Yellow
(254, 192, 192) # Red
]
label_colours = [
(32, 137, 251), # Blue
(100, 255, 124), # Green
(254, 219, 82), # Yellow
(254, 82, 82), # Red
]
# Only the ALPHA channel is used from these images
icon_drop = Image.open("icons/icon-drop.png")
icon_nodrop = Image.open("icons/icon-nodrop.png")
icon_rightarrow = Image.open("icons/icon-rightarrow.png")
icon_snooze = Image.open("icons/icon-snooze.png")
plants = []
# Load all of the plant icons
for x in range(1, 15):
plants.append(Image.open("icons/flat-{}.png".format(x)))
# Pick a random selection of plant icons to display on screen
# TODO: Make this the default, but allow override in settings
picked = random.sample(plants, 3)
CHANNEL_W = 40
CHANNEL_M = 2
# Set up the ST7735 SPI Display
display = ST7735.ST7735( display = ST7735.ST7735(
port=0, port=0,
cs=1, cs=1,
@@ -54,25 +122,18 @@ display = ST7735.ST7735(
rotation=270, rotation=270,
spi_speed_hz=80000000 spi_speed_hz=80000000
) )
display.begin()
WIDTH, HEIGHT = display.width, display.height
ramp = [ # Set up our canvas and prepare for drawing
(192, 225, 254), # Blue image = Image.new("RGBA", (WIDTH, HEIGHT), color=(255, 255, 255))
(196, 255, 209), # Green font = ImageFont.truetype(UserFont, 14)
(255, 243, 192), # Yellow draw = ImageDraw.Draw(image)
(254, 192, 192) # Red
]
ramp_sat = [
(32, 137, 251), # Blue
(100, 255, 124), # Green
(254, 219, 82), # Yellow
(254, 82, 82), # Red
]
def indicator_color(value, r=None): def indicator_color(value, r=None):
if r is None: if r is None:
r = ramp r = bar_colours
if value == 1.0: if value == 1.0:
return r[-1] return r[-1]
if value == 0.0: if value == 0.0:
@@ -88,96 +149,97 @@ def indicator_color(value, r=None):
return (r, g, b) return (r, g, b)
display.begin() def icon(image, icon, position, color):
col = Image.new("RGBA", (20, 20), color=color)
image.paste(col, position, mask=icon)
WIDTH, HEIGHT = display.width, display.height
# Only the ALPHA channel is used from these images def plant(image, plant, channel, available=True):
icon_drop = Image.open("icons/icon-drop.png") x = [18, 58, 98][channel]
icon_nodrop = Image.open("icons/icon-nodrop.png") y = HEIGHT - plant.height
icon_rightarrow = Image.open("icons/icon-rightarrow.png") mask = plant
icon_snooze = Image.open("icons/icon-snooze.png") if not available:
plant = plant.convert('LA').convert('RGB')
image.paste(plant, (x, y), mask=mask)
plants = []
for x in range(1, 15): def update():
plants.append(Image.open("icons/flat-{}.png".format(x))) pass
CHANNEL_W = 40
CHANNEL_M = 2
print(WIDTH, HEIGHT) def render():
t = time.time()
image = Image.new("RGBA", (WIDTH, HEIGHT), color=(255, 255, 255))
draw = ImageDraw.Draw(image)
# Icon backdrops
draw.rectangle((0, 0, 19, 19), (32, 138, 251)) draw.rectangle((0, 0, 19, 19), (32, 138, 251))
draw.rectangle((0, HEIGHT - 19, 19, HEIGHT), (255, 255, 255)) draw.rectangle((0, HEIGHT - 19, 19, HEIGHT), (255, 255, 255))
draw.rectangle((WIDTH - 20, 0, WIDTH, 19), (75, 166, 252)) draw.rectangle((WIDTH - 20, 0, WIDTH, 19), (75, 166, 252))
draw.rectangle((WIDTH - 20, HEIGHT - 19, WIDTH, HEIGHT), (254, 218, 80)) draw.rectangle((WIDTH - 20, HEIGHT - 19, WIDTH, HEIGHT), (254, 218, 80))
def icon(image, icon, position, color): # Icons
col = Image.new("RGBA", (20, 20), color=color)
image.paste(col, position, mask=icon)
icon(image, icon_rightarrow, (0, 0), (255, 255, 255)) icon(image, icon_rightarrow, (0, 0), (255, 255, 255))
icon(image, icon_snooze, (0, HEIGHT - 20), (129, 129, 129))
icon(image, icon_drop, (WIDTH - 20, 0), (255, 255, 255)) icon(image, icon_drop, (WIDTH - 20, 0), (255, 255, 255))
icon(image, icon_nodrop, (WIDTH - 20, HEIGHT - 20), (255, 255, 255)) icon(image, icon_nodrop, (WIDTH - 20, HEIGHT - 20), (255, 255, 255))
def plant(image, plant, channel): # Draw the snooze icon- will be pulsing red if the alarm state is True
x = [18, 58, 98][channel] r = 129
y = HEIGHT - plant.height if alarm:
image.paste(plant, (x, y), mask=plant) r = int(((math.sin(t * 3 * math.pi) + 1.0) / 2.0) * 255)
icon(image, icon_snooze, (0, HEIGHT - 20), (r, 129, 129))
picked = random.sample(plants, 3) # Saturation amounts from each sensor
# image.save("test.png")
while True:
t = time.time() / 10.0
#c1 = (math.sin(math.pi + t * math.pi) + 1.0) / 2.0
#c2 = (math.sin(t * math.pi) + 1.0) / 2.0
#c3 = (math.sin(math.pi + t * math.pi) + 1.0) / 2.0
c1 = 1.0 - sensors[0].saturation c1 = 1.0 - sensors[0].saturation
c2 = 1.0 - sensors[1].saturation c2 = 1.0 - sensors[1].saturation
c3 = 1.0 - sensors[2].saturation c3 = 1.0 - sensors[2].saturation
print(c1, c2, c3)
# Channel presence detection
# TODO: Implement presence detection that doesn't trigger on random noise!
ca1 = sensors[0].moisture > 0
ca2 = sensors[1].moisture > 0
ca3 = sensors[2].moisture > 0
draw.rectangle((21, 0, 138, HEIGHT), (255, 255, 255)) # Erase channel area draw.rectangle((21, 0, 138, HEIGHT), (255, 255, 255)) # Erase channel area
# Draw background bars # Draw background bars
draw.rectangle((21, int(c1 * HEIGHT), 58, HEIGHT), indicator_color(c1)) draw.rectangle((21, int(c1 * HEIGHT), 58, HEIGHT), indicator_color(c1) if ca1 else (229, 229, 229))
draw.rectangle((61, int(c2 * HEIGHT), 98, HEIGHT), indicator_color(c2)) draw.rectangle((61, int(c2 * HEIGHT), 98, HEIGHT), indicator_color(c2) if ca2 else (229, 229, 229))
draw.rectangle((101, int(c3 * HEIGHT), 138, HEIGHT), indicator_color(c3)) draw.rectangle((101, int(c3 * HEIGHT), 138, HEIGHT), indicator_color(c3) if ca3 else (229, 229, 229))
# Draw plant images # Draw plant images
for p in range(3): for p in range(3):
plant(image, picked[p], p) plant(image, picked[p], p, [ca1, ca2, ca3][p])
# Channel selection icons # Channel selection icons
draw.rectangle((33, 2, 48, 17), indicator_color(c1, ramp_sat)) draw.rectangle((33, 2, 48, 17), indicator_color(c1, label_colours) if ca1 else (129, 129, 129))
draw.rectangle((33 + 40, 2, 48 + 40, 17), indicator_color(c2, ramp_sat)) draw.rectangle((33 + 40, 2, 48 + 40, 17), indicator_color(c2, label_colours) if ca2 else (129, 129, 129))
draw.rectangle((33 + 80, 2, 48 + 80, 17), indicator_color(c3, ramp_sat)) draw.rectangle((33 + 80, 2, 48 + 80, 17), indicator_color(c3, label_colours) if ca3 else (129, 129, 129))
selected_x = 21 + (40 * channel_selected) + 10 selected_x = 21 + (40 * channel_selected) + 10
draw.rectangle((selected_x, 0, selected_x + 19, 20), indicator_color([c1, c2, c3][channel_selected], ramp_sat)) draw.rectangle((selected_x, 0, selected_x + 19, 20), indicator_color([c1, c2, c3][channel_selected], label_colours) if [ca1, ca2, ca3][channel_selected] else (129, 129, 129))
# TODO: replace with graphic, since PIL has no anti-aliasing
draw.polygon([ draw.polygon([
(selected_x, 20), (selected_x, 20),
(selected_x + 9, 25), (selected_x + 9, 25),
(selected_x + 19, 20) (selected_x + 19, 20)
], ],
fill=indicator_color([c1, c2, c3][channel_selected], ramp_sat)) fill=indicator_color([c1, c2, c3][channel_selected], label_colours) if [ca1, ca2, ca3][channel_selected] else (129, 129, 129))
# TODO: replace number text with graphic
draw.text((33 + 3, 2), "1", font=font, fill=(255, 255, 255)) draw.text((33 + 3, 2), "1", font=font, fill=(255, 255, 255))
draw.text((33 + 40 + 4, 2), "2", font=font, fill=(255, 255, 255)) draw.text((33 + 40 + 4, 2), "2", font=font, fill=(255, 255, 255))
draw.text((33 + 80 + 4, 2), "3", font=font, fill=(255, 255, 255)) draw.text((33 + 80 + 4, 2), "3", font=font, fill=(255, 255, 255))
# Erase snooze icon
draw.rectangle((0, HEIGHT - 19, 19, HEIGHT), (255, 255, 255))
r = int(((math.sin(time.time() * 3 * math.pi) + 1.0) / 2.0) * 255)
icon(image, icon_snooze, (0, HEIGHT - 20), (r, 129, 129))
def main():
while True:
update()
render()
display.display(image.convert("RGB")) display.display(image.convert("RGB"))
# 5 FPS
time.sleep(1.0 / 5) time.sleep(1.0 / 5)
if __name__ == "__main__":
main()