From 487401e359e889a9c808206cf71009db37bf9c37 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Tue, 26 May 2020 15:25:02 +0100 Subject: [PATCH] Start tidying display.py --- examples/display.py | 222 ++++++++++++++++++++++++++++---------------- 1 file changed, 142 insertions(+), 80 deletions(-) diff --git a/examples/display.py b/examples/display.py index c04d917..c10c886 100644 --- a/examples/display.py +++ b/examples/display.py @@ -2,6 +2,7 @@ import time import ST7735 import random import math +import logging from PIL import Image, ImageDraw, ImageFont from fonts.ttf import RobotoMedium as UserFont import RPi.GPIO as GPIO @@ -9,14 +10,33 @@ import RPi.GPIO as GPIO from grow.moisture import Moisture from grow.pump import Pump -GPIO.setmode(GPIO.BCM) -GPIO.setwarnings(False) +# Level at which a watering event is triggered +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] LABELS = ['A', 'B', 'X', 'Y'] +CHANNEL_COUNT = 3 -channel_count = 3 channel_selected = 0 +alarm = False sensors = [ Moisture(channel=1), @@ -30,22 +50,70 @@ pumps = [ 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): - global channel_selected + global channel_selected, alarm index = BUTTONS.index(pin) label = LABELS[index] - if label == 'A': + if label == 'A': # Select Channel 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)) + +GPIO.setmode(GPIO.BCM) +GPIO.setwarnings(False) +GPIO.setup(BUTTONS, GPIO.IN, pull_up_down=GPIO.PUD_UP) + for pin in BUTTONS: 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( port=0, cs=1, @@ -54,25 +122,18 @@ display = ST7735.ST7735( rotation=270, spi_speed_hz=80000000 ) +display.begin() +WIDTH, HEIGHT = display.width, display.height -ramp = [ - (192, 225, 254), # Blue - (196, 255, 209), # Green - (255, 243, 192), # Yellow - (254, 192, 192) # Red -] - -ramp_sat = [ - (32, 137, 251), # Blue - (100, 255, 124), # Green - (254, 219, 82), # Yellow - (254, 82, 82), # Red -] +# Set up our canvas and prepare for drawing +image = Image.new("RGBA", (WIDTH, HEIGHT), color=(255, 255, 255)) +font = ImageFont.truetype(UserFont, 14) +draw = ImageDraw.Draw(image) def indicator_color(value, r=None): if r is None: - r = ramp + r = bar_colours if value == 1.0: return r[-1] if value == 0.0: @@ -88,96 +149,97 @@ def indicator_color(value, r=None): return (r, g, b) -display.begin() - -WIDTH, HEIGHT = display.width, display.height - -# 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 = [] - -for x in range(1, 15): - plants.append(Image.open("icons/flat-{}.png".format(x))) - -CHANNEL_W = 40 -CHANNEL_M = 2 - -print(WIDTH, HEIGHT) - -image = Image.new("RGBA", (WIDTH, HEIGHT), color=(255, 255, 255)) -draw = ImageDraw.Draw(image) - -draw.rectangle((0, 0, 19, 19), (32, 138, 251)) -draw.rectangle((0, HEIGHT - 19, 19, HEIGHT), (255, 255, 255)) -draw.rectangle((WIDTH - 20, 0, WIDTH, 19), (75, 166, 252)) -draw.rectangle((WIDTH - 20, HEIGHT - 19, WIDTH, HEIGHT), (254, 218, 80)) - def icon(image, icon, position, color): 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_snooze, (0, HEIGHT - 20), (129, 129, 129)) -icon(image, icon_drop, (WIDTH - 20, 0), (255, 255, 255)) -icon(image, icon_nodrop, (WIDTH - 20, HEIGHT - 20), (255, 255, 255)) -def plant(image, plant, channel): +def plant(image, plant, channel, available=True): x = [18, 58, 98][channel] y = HEIGHT - plant.height - image.paste(plant, (x, y), mask=plant) + mask = plant + if not available: + plant = plant.convert('LA').convert('RGB') + image.paste(plant, (x, y), mask=mask) -picked = random.sample(plants, 3) -# image.save("test.png") +def update(): + pass -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 + +def render(): + t = time.time() + + # Icon backdrops + draw.rectangle((0, 0, 19, 19), (32, 138, 251)) + draw.rectangle((0, HEIGHT - 19, 19, HEIGHT), (255, 255, 255)) + draw.rectangle((WIDTH - 20, 0, WIDTH, 19), (75, 166, 252)) + draw.rectangle((WIDTH - 20, HEIGHT - 19, WIDTH, HEIGHT), (254, 218, 80)) + + # Icons + icon(image, icon_rightarrow, (0, 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)) + + # Draw the snooze icon- will be pulsing red if the alarm state is True + r = 129 + if alarm: + r = int(((math.sin(t * 3 * math.pi) + 1.0) / 2.0) * 255) + icon(image, icon_snooze, (0, HEIGHT - 20), (r, 129, 129)) + + # Saturation amounts from each sensor c1 = 1.0 - sensors[0].saturation c2 = 1.0 - sensors[1].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 background bars - draw.rectangle((21, int(c1 * HEIGHT), 58, HEIGHT), indicator_color(c1)) - draw.rectangle((61, int(c2 * HEIGHT), 98, HEIGHT), indicator_color(c2)) - draw.rectangle((101, int(c3 * HEIGHT), 138, HEIGHT), indicator_color(c3)) + 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) if ca2 else (229, 229, 229)) + draw.rectangle((101, int(c3 * HEIGHT), 138, HEIGHT), indicator_color(c3) if ca3 else (229, 229, 229)) # Draw plant images for p in range(3): - plant(image, picked[p], p) + plant(image, picked[p], p, [ca1, ca2, ca3][p]) # Channel selection icons - draw.rectangle((33, 2, 48, 17), indicator_color(c1, ramp_sat)) - draw.rectangle((33 + 40, 2, 48 + 40, 17), indicator_color(c2, ramp_sat)) - draw.rectangle((33 + 80, 2, 48 + 80, 17), indicator_color(c3, 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, label_colours) if ca2 else (129, 129, 129)) + 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 - 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([ (selected_x, 20), (selected_x + 9, 25), (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 + 40 + 4, 2), "2", 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)) - display.display(image.convert("RGB")) - time.sleep(1.0 / 5) +def main(): + while True: + update() + render() + display.display(image.convert("RGB")) + + # 5 FPS + time.sleep(1.0 / 5) + + +if __name__ == "__main__": + main()