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