diff --git a/lcd_control.py b/lcd_control.py index be19016..7494c3b 100644 --- a/lcd_control.py +++ b/lcd_control.py @@ -1,222 +1,222 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -*- - -#################### -# Inspiration / Further Reading -#################### -# https://circuitpython.readthedocs.io/projects/bh1750/en/latest/ -# https://raspi.tv/2013/how-to-use-interrupts-with-python-on-the-raspberry-pi-and-rpi-gpio-part-3 -# https://stackoverflow.com/questions/16148735/how-to-implement-a-watchdog-timer-in-python -# https://learn.adafruit.com/adafruit-bh1750-ambient-light-sensor -# https://learn.adafruit.com/adafruit-mini-pitft-135x240-color-tft-add-on-for-raspberry-pi/overview - -import board -import busio -import dbus -import digitalio -import os -import time -from threading import Timer -from PIL import Image, ImageDraw, ImageFont -import RPi.GPIO as GPIO -import adafruit_rgb_display.st7789 as st7789 -import adafruit_bh1750 - -# Generic watchdog timer used for backlight controls -class Watchdog: - def __init__(self, timeout, userHandler=None): # timeout in seconds - self.timeout = timeout - self.handler = userHandler if userHandler is not None else self.defaultHandler - self.timer = Timer(self.timeout, self.handler) - # Do NOT start timer by default ; we want this triggered by reset events that come through - # via the inotify filesystem watcher - #self.timer.start() - - def reset(self): - self.timer.cancel() - self.timer = Timer(self.timeout, self.handler) - self.timer.start() - - def stop(self): - self.timer.cancel() - - def defaultHandler(self): - raise self - -# Cheat for walking only one level deep via os.walk (faster than other options) -def walklevel(some_dir, level=1): - some_dir = some_dir.rstrip(os.path.sep) - assert os.path.isdir(some_dir) - num_sep = some_dir.count(os.path.sep) - for root, dirs, files in os.walk(some_dir): - yield root, dirs, files - num_sep_this = root.count(os.path.sep) - if num_sep + level <= num_sep_this: - del dirs[:] - -# bh1740 lux sensor -i2c = busio.I2C(board.SCL, board.SDA) -sensor = adafruit_bh1750.BH1750(i2c) - -# Configuration for CS and DC pins (these are FeatherWing defaults on M0/M4): -cs_pin = digitalio.DigitalInOut(board.CE0) -dc_pin = digitalio.DigitalInOut(board.D25) -reset_pin = None - -# Setup button pins as inputs -GPIO.setup(23, GPIO.IN) -GPIO.setup(24, GPIO.IN) - -# Config for display baudrate (default max is 24mhz): -BAUDRATE = 64000000 - -# Setup SPI bus using hardware SPI: -spi = board.SPI() - -# Create the ST7789 display: -disp = st7789.ST7789( - spi, - cs=cs_pin, - dc=dc_pin, - rst=reset_pin, - baudrate=BAUDRATE, - width=135, - height=240, - x_offset=53, - y_offset=40, -) - -# Create blank image for drawing. -# Make sure to create image with mode 'RGB' for full color. -height = disp.width # we swap height/width to rotate it to landscape! -width = disp.height -image = Image.new('RGB', (width, height)) -rotation = 90 - -# Get drawing object to draw on image. -draw = ImageDraw.Draw(image) - -# Draw a black filled box to clear the image. -draw.rectangle((0, 0, width, height), outline=0, fill=(0, 0, 0)) -disp.image(image, rotation) -# Draw some shapes. -# First define some constants to allow easy resizing of shapes. -padding = -2 -top = padding -bottom = height - padding -# Move left to right keeping track of the current x position for drawing shapes. -x = 0 - -# Alternatively load a TTF font. Make sure the .ttf font file is in the -# same directory as the python script! -# Some other nice fonts to try: http://www.dafont.com/bitmap.php -#font = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Bold.ttf', 24) -font_small = font = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Bold.ttf', 24) -font = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Bold.ttf', 48) - -# Turn on the backlight -backlight = digitalio.DigitalInOut(board.D22) -backlight.switch_to_output() -backlight.value = True - -# Watchdog timer for screen on/off control based on button presses -# Turns off screen 10s after last button press -# Turns on screen for 10s when starting up -# Restart fim slideshow when the screen blanks so there is an indicator when it's going to change the selection -def backlight_callback(): - global backlight - backlight.value = False - sysbus = dbus.SystemBus() - systemd1 = sysbus.get_object('org.freedesktop.systemd1', '/org/freedesktop/systemd1') - manager = dbus.Interface(systemd1, 'org.freedesktop.systemd1.Manager') - job = manager.RestartUnit('fim.service', 'fail') - -watchdog = Watchdog(10.0, backlight_callback) - -# Figure out which albums are present as well as the special 'All' album -albums = [ 'All' ] -selected_album = 0 -try: - with open('/etc/default/fim_album_index', 'r') as f: - selected_album = int(f.readline()) -except IOError: - pass -except ValueError: - pass -for root, dirs, files in walklevel('/tank/pictures'): - for folder in dirs: - if folder == '.stfolder': - continue - albums.append(folder) -albums.sort() - -# Method for drawing on the screen -# Called by the button handlers since we don't need anything more than a basic while(1) main for this setup -def refresh_screen(): - global width, height, x, y, top, draw, selected_album, albums, font, font_small - - # Draw a black filled box to clear the image. - draw.rectangle((0, 0, width, height), outline=0, fill=0) - - # Split the line on spaces and then add a blank line if needed to avoid crashes - text_lines = albums[selected_album].split(' ') - if len(text_lines) < 2: - text_lines.append('') - - # Write some text - y = top - draw.text((x, y), text_lines[0], font=font, fill='#DCDCDC') - y += font.getsize('0')[1] - draw.text((x, y), text_lines[1], font=font, fill='#DCDCDC') - lux = 'Light: %.2f Lux' % sensor.lux - y = height - font_small.getsize(lux)[1] - draw.text((x, y), lux, font=font_small, fill='#DCDCDC') - - # Display image. - disp.image(image, rotation) - -# Write current album when restarting the slideshow -def write_selected_album(): - with open('/etc/default/fim_album', 'w') as f: - f.write(str(selected_album)) - with open('/etc/default/fim_album_path', 'w') as f: - if not selected_album == 0: - f.write('/tank/pictures/' + albums[selected_album]) - else: - f.write('/tank/pictures') - -# Interrupt callback for changing albums (up) -def up_button(channel): - global selected_album, albums, backlight - backlight.value = True - selected_album += 1 - if selected_album >= len(albums): - selected_album = 0 - refresh_screen() - write_selected_album() - watchdog.reset() - -# Interrupt callback for changing albums (dn) -def dn_button(channel): - global selected_album, albums, backlight - backlight.value = True - selected_album -= 1 - if selected_album < 0: - selected_album = len(albums) - 1 - refresh_screen() - write_selected_album() - watchdog.reset() - -# Setup button interrupts -GPIO.add_event_detect(23, GPIO.FALLING, callback=up_button, bouncetime=300) -GPIO.add_event_detect(24, GPIO.FALLING, callback=dn_button, bouncetime=300) - -# Draw initial screen state -refresh_screen() - -# Kick off watchdog to start the 10s initial 'on' status -watchdog.reset() - -# Work with the LCD / Buttons in an infinate loop -while True: - time.sleep(300) +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +#################### +# Inspiration / Further Reading +#################### +# https://circuitpython.readthedocs.io/projects/bh1750/en/latest/ +# https://raspi.tv/2013/how-to-use-interrupts-with-python-on-the-raspberry-pi-and-rpi-gpio-part-3 +# https://stackoverflow.com/questions/16148735/how-to-implement-a-watchdog-timer-in-python +# https://learn.adafruit.com/adafruit-bh1750-ambient-light-sensor +# https://learn.adafruit.com/adafruit-mini-pitft-135x240-color-tft-add-on-for-raspberry-pi/overview + +import board +import busio +import dbus +import digitalio +import os +import time +from threading import Timer +from PIL import Image, ImageDraw, ImageFont +import RPi.GPIO as GPIO +import adafruit_rgb_display.st7789 as st7789 +import adafruit_bh1750 + +# Generic watchdog timer used for backlight controls +class Watchdog: + def __init__(self, timeout, userHandler=None): # timeout in seconds + self.timeout = timeout + self.handler = userHandler if userHandler is not None else self.defaultHandler + self.timer = Timer(self.timeout, self.handler) + # Do NOT start timer by default ; we want this triggered by reset events that come through + # via the inotify filesystem watcher + #self.timer.start() + + def reset(self): + self.timer.cancel() + self.timer = Timer(self.timeout, self.handler) + self.timer.start() + + def stop(self): + self.timer.cancel() + + def defaultHandler(self): + raise self + +# Cheat for walking only one level deep via os.walk (faster than other options) +def walklevel(some_dir, level=1): + some_dir = some_dir.rstrip(os.path.sep) + assert os.path.isdir(some_dir) + num_sep = some_dir.count(os.path.sep) + for root, dirs, files in os.walk(some_dir): + yield root, dirs, files + num_sep_this = root.count(os.path.sep) + if num_sep + level <= num_sep_this: + del dirs[:] + +# bh1740 lux sensor +i2c = busio.I2C(board.SCL, board.SDA) +sensor = adafruit_bh1750.BH1750(i2c) + +# Configuration for CS and DC pins (these are FeatherWing defaults on M0/M4): +cs_pin = digitalio.DigitalInOut(board.CE0) +dc_pin = digitalio.DigitalInOut(board.D25) +reset_pin = None + +# Setup button pins as inputs +GPIO.setup(23, GPIO.IN) +GPIO.setup(24, GPIO.IN) + +# Config for display baudrate (default max is 24mhz): +BAUDRATE = 64000000 + +# Setup SPI bus using hardware SPI: +spi = board.SPI() + +# Create the ST7789 display: +disp = st7789.ST7789( + spi, + cs=cs_pin, + dc=dc_pin, + rst=reset_pin, + baudrate=BAUDRATE, + width=135, + height=240, + x_offset=53, + y_offset=40, +) + +# Create blank image for drawing. +# Make sure to create image with mode 'RGB' for full color. +height = disp.width # we swap height/width to rotate it to landscape! +width = disp.height +image = Image.new('RGB', (width, height)) +rotation = 90 + +# Get drawing object to draw on image. +draw = ImageDraw.Draw(image) + +# Draw a black filled box to clear the image. +draw.rectangle((0, 0, width, height), outline=0, fill=(0, 0, 0)) +disp.image(image, rotation) +# Draw some shapes. +# First define some constants to allow easy resizing of shapes. +padding = -2 +top = padding +bottom = height - padding +# Move left to right keeping track of the current x position for drawing shapes. +x = 0 + +# Alternatively load a TTF font. Make sure the .ttf font file is in the +# same directory as the python script! +# Some other nice fonts to try: http://www.dafont.com/bitmap.php +#font = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Bold.ttf', 24) +font_small = font = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Bold.ttf', 24) +font = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Bold.ttf', 48) + +# Turn on the backlight +backlight = digitalio.DigitalInOut(board.D22) +backlight.switch_to_output() +backlight.value = True + +# Watchdog timer for screen on/off control based on button presses +# Turns off screen 10s after last button press +# Turns on screen for 10s when starting up +# Restart fim slideshow when the screen blanks so there is an indicator when it's going to change the selection +def backlight_callback(): + global backlight + backlight.value = False + sysbus = dbus.SystemBus() + systemd1 = sysbus.get_object('org.freedesktop.systemd1', '/org/freedesktop/systemd1') + manager = dbus.Interface(systemd1, 'org.freedesktop.systemd1.Manager') + job = manager.RestartUnit('fim.service', 'fail') + +watchdog = Watchdog(10.0, backlight_callback) + +# Figure out which albums are present as well as the special 'All' album +albums = [ 'All' ] +selected_album = 0 +try: + with open('/etc/default/fim_album_index', 'r') as f: + selected_album = int(f.readline()) +except IOError: + pass +except ValueError: + pass +for root, dirs, files in walklevel('/tank/pictures'): + for folder in dirs: + if folder == '.stfolder': + continue + albums.append(folder) +albums.sort() + +# Method for drawing on the screen +# Called by the button handlers since we don't need anything more than a basic while(1) main for this setup +def refresh_screen(): + global width, height, x, y, top, draw, selected_album, albums, font, font_small + + # Draw a black filled box to clear the image. + draw.rectangle((0, 0, width, height), outline=0, fill=0) + + # Split the line on spaces and then add a blank line if needed to avoid crashes + text_lines = albums[selected_album].split(' ') + if len(text_lines) < 2: + text_lines.append('') + + # Write some text + y = top + draw.text((x, y), text_lines[0], font=font, fill='#DCDCDC') + y += font.getsize('0')[1] + draw.text((x, y), text_lines[1], font=font, fill='#DCDCDC') + lux = 'Light: %.2f Lux' % sensor.lux + y = height - font_small.getsize(lux)[1] + draw.text((x, y), lux, font=font_small, fill='#DCDCDC') + + # Display image. + disp.image(image, rotation) + +# Write current album when restarting the slideshow +def write_selected_album(): + with open('/etc/default/fim_album', 'w') as f: + f.write(str(selected_album)) + with open('/etc/default/fim_album_path', 'w') as f: + if not selected_album == 0: + f.write('/tank/pictures/' + albums[selected_album]) + else: + f.write('/tank/pictures') + +# Interrupt callback for changing albums (up) +def up_button(channel): + global selected_album, albums, backlight + backlight.value = True + selected_album += 1 + if selected_album >= len(albums): + selected_album = 0 + refresh_screen() + write_selected_album() + watchdog.reset() + +# Interrupt callback for changing albums (dn) +def dn_button(channel): + global selected_album, albums, backlight + backlight.value = True + selected_album -= 1 + if selected_album < 0: + selected_album = len(albums) - 1 + refresh_screen() + write_selected_album() + watchdog.reset() + +# Setup button interrupts +GPIO.add_event_detect(23, GPIO.FALLING, callback=up_button, bouncetime=300) +GPIO.add_event_detect(24, GPIO.FALLING, callback=dn_button, bouncetime=300) + +# Draw initial screen state +refresh_screen() + +# Kick off watchdog to start the 10s initial 'on' status +watchdog.reset() + +# Work with the LCD / Buttons in an infinate loop +while True: + time.sleep(300)