generated from pimoroni/pga
-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Examples: initial version of 'image_gallery.py'
- Loading branch information
Showing
1 changed file
with
294 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,294 @@ | ||
''' | ||
An image gallery demo to turn your Pimoroni Presto into a desktop photo frame! | ||
- Create a folder called 'gallery' on the root of your SD card and fill it with JPEGs. | ||
- The image will change automatically every 5 minutes | ||
- You can also tap the right side of the screen to skip next image and left side to go to the previous :) | ||
''' | ||
import os | ||
import time | ||
|
||
import jpegdec | ||
import machine | ||
import plasma | ||
import sdcard | ||
import uos | ||
from ft6236 import FT6236 | ||
from picographics import DISPLAY_PRESTO, PicoGraphics | ||
from presto import Presto | ||
|
||
machine.freq(264000000) | ||
|
||
# The total number of LEDs to set, the Presto has 7 | ||
NUM_LEDS = 7 | ||
|
||
# Where our images are located on the SD card | ||
DIR = 'sd/gallery' | ||
|
||
# Seconds between changing the image on screen | ||
# This interval shows us a new image every 5 minutes | ||
INTERVAL = 60 * 5 | ||
|
||
LEDS_LEFT = [4, 5, 6] | ||
LEDS_RIGHT = [0, 1, 2] | ||
|
||
# Setup for the Presto display | ||
portal = Presto() | ||
display = PicoGraphics(DISPLAY_PRESTO, buffer=memoryview(portal)[240 * 240 * 2:], layers=2) | ||
WIDTH, HEIGHT = display.get_bounds() | ||
|
||
BACKGROUND = display.create_pen(1, 1, 1) | ||
WHITE = display.create_pen(255, 255, 255) | ||
BLACK = display.create_pen(0, 0, 0) | ||
|
||
# We'll need this for the touch element of the screen | ||
touch = FT6236() | ||
|
||
# JPEG Dec | ||
j = jpegdec.JPEG(display) | ||
|
||
# Plasma setup | ||
bl = plasma.WS2812(7, 0, 0, 33) | ||
bl.start() | ||
|
||
# Stores the total number of images in the user gallery | ||
total_image_count = 0 | ||
|
||
# Store our current location within the user gallery | ||
current_image = 1 # First image in our gallery | ||
|
||
|
||
# We might not have enough space to load the entire directory list into RAM | ||
# so we iterate though 'os.ilistdir' and store each file name in a .txt file for later | ||
def create_index(dir): | ||
|
||
# check to see if contents.txt exists and skip the indexing if it does. | ||
if not file_exists(dir + "/index.txt"): | ||
|
||
f = open(dir + "/index.txt", "a") | ||
|
||
for file in os.ilistdir(dir): | ||
# We're only looking for jpeg images, we don't want to index any other file type | ||
if ".jpg" in file[0] or ".jpeg" in file[0]: | ||
f.write(str(file[0]) + "\n") | ||
|
||
f.close() | ||
|
||
print("'index.txt' generated successfully!") | ||
|
||
else: | ||
print("\nindex.txt already generated! \nIf you have changed the files in the current directory you will need to delete 'content.txt' and run the program again.\n\n") | ||
|
||
|
||
def count_files(dir): | ||
c = 0 | ||
for file in os.ilistdir(dir): | ||
c += 1 | ||
return c - 1 | ||
|
||
|
||
# Delete the current index file if it exists | ||
# This will force it to recreate it on the next boot. | ||
def delete_index(dir): | ||
print("Removing 'index.txt'") | ||
os.remove(dir + "/index.txt") | ||
|
||
|
||
lfsr = 1 | ||
tap = 0xdc29 | ||
|
||
|
||
def return_point(): | ||
global lfsr | ||
|
||
x = lfsr & 0x00ff | ||
y = (lfsr & 0xff00) >> 8 | ||
|
||
lsb = lfsr & 1 | ||
lfsr >>= 1 | ||
|
||
if lsb: | ||
lfsr ^= tap | ||
|
||
if x - 1 < 240 and y < 240: | ||
return x - 1, y | ||
|
||
return -1, -1 | ||
|
||
|
||
def fizzlefade(): | ||
display.set_pen(BLACK) | ||
display.set_layer(1) | ||
|
||
while True: | ||
|
||
for i in range(2000): | ||
x, y = return_point() | ||
if x > -1 and y > -1: | ||
display.pixel(x, y) | ||
if lfsr == 1: | ||
break | ||
|
||
portal.update(display) | ||
if lfsr == 1: | ||
break | ||
|
||
|
||
def show_image(show_next=False, show_previous=False): | ||
global current_image | ||
global total_image_count | ||
|
||
# Get the next image in the gallery | ||
# If we're at the end of the gallery, loop back and start from 1. | ||
if show_next: | ||
if current_image < total_image_count: | ||
current_image += 1 | ||
else: | ||
current_image = 1 | ||
if show_previous: | ||
if current_image > 1: | ||
current_image -= 1 | ||
else: | ||
current_image = total_image_count | ||
|
||
# Open the index file and read lines until we're at the correct position | ||
try: | ||
f = open(DIR + "/index.txt", 'r') | ||
for i in range(current_image - 1): | ||
f.readline() | ||
file = f.readline() | ||
f.close() | ||
|
||
j.open_file(f"{DIR}/{file}") | ||
|
||
img_height, img_width = j.get_height(), j.get_width() | ||
|
||
img_x = 0 | ||
img_y = 0 | ||
|
||
# if the image isn't exactly 240x240 then we'll try to centre the image | ||
if img_width < WIDTH: | ||
img_x = (WIDTH // 2) - (img_width // 2) | ||
|
||
if img_height < HEIGHT: | ||
img_y = (HEIGHT // 2) - (img_height // 2) | ||
|
||
display.set_layer(0) | ||
display.set_pen(BACKGROUND) | ||
display.clear() | ||
j.decode(img_x, img_y, jpegdec.JPEG_SCALE_FULL, dither=True) | ||
|
||
fizzlefade() | ||
|
||
# Now draw the current image to Layer 1 | ||
display.set_layer(1) | ||
# Decode the JPEG | ||
j.decode(img_x, img_y, jpegdec.JPEG_SCALE_FULL, dither=True) | ||
|
||
except OSError: | ||
display_error("Unable to find/read file.\n\nCheck that the 'gallery' folder in the root of your SD card contains JPEG images!") | ||
|
||
|
||
def clear(): | ||
display.set_pen(BACKGROUND) | ||
display.set_layer(0) | ||
display.clear() | ||
display.set_layer(1) | ||
display.clear() | ||
|
||
|
||
# Display an error msg on screen and keep it looping | ||
def display_error(text): | ||
while 1: | ||
display.set_pen(BACKGROUND) | ||
display.clear() | ||
display.set_pen(WHITE) | ||
display.text(f"Error: {text}", 10, 10, WIDTH - 10, 1) | ||
portal.update(display) | ||
time.sleep(1) | ||
|
||
|
||
try: | ||
# Setup for SD Card | ||
sd_spi = machine.SPI(0, sck=machine.Pin(34, machine.Pin.OUT), mosi=machine.Pin(35, machine.Pin.OUT), miso=machine.Pin(36, machine.Pin.OUT)) | ||
sd = sdcard.SDCard(sd_spi, machine.Pin(39)) | ||
|
||
# Mount the SD to the directory 'sd' | ||
uos.mount(sd, "/sd") | ||
except OSError: | ||
display_error("Unable to mount SD card") | ||
|
||
|
||
# Function to check if a file is present on the filesystem | ||
def file_exists(filename): | ||
try: | ||
return (os.stat(filename)[0] & 0x4000) == 0 | ||
except OSError: | ||
return False | ||
|
||
|
||
try: | ||
# Delete the index to force it to recreate the file in the next step | ||
delete_index(DIR) | ||
except OSError: | ||
print("Unable to delete index") | ||
|
||
# Create the index | ||
# And count the images | ||
try: | ||
create_index(DIR) | ||
total_image_count = count_files(DIR) | ||
except OSError: | ||
display_error("Unable to create index file. \nCheck that your file names do not contain non unicode characters.") | ||
|
||
|
||
# Store the last time the screen was updated | ||
last_updated = time.time() | ||
|
||
# Show the first image on the screen so it's not just noise :) | ||
# We're not passing the arg for 'show_next' or 'show_previous' so it'll show whichever image is current | ||
clear() | ||
show_image() | ||
portal.update(display) | ||
portal.update(display) | ||
|
||
while True: | ||
|
||
# Poll the touch so we can see if anything changed since the last time | ||
touch.poll() | ||
|
||
# Check if it's time to update the image! | ||
if time.time() - last_updated > INTERVAL: | ||
|
||
last_updated = time.time() | ||
show_image(show_next=True) | ||
portal.update(display) | ||
|
||
# if the screen is reporting that there is touch we want to handle that here | ||
if touch.state: | ||
# Right half of the screen moves to the next image | ||
# The LEDs on the right side of the presto light up to show it is working | ||
if touch.x > WIDTH // 2: | ||
for i in LEDS_RIGHT: | ||
bl.set_rgb(i, 255, 255, 255) | ||
show_image(show_next=True) | ||
portal.update(display) | ||
last_updated = time.time() | ||
for i in LEDS_RIGHT: | ||
bl.set_rgb(i, 0, 0, 0) | ||
|
||
# Left half of the screen moves to the previous image | ||
elif touch.x < WIDTH // 2: | ||
for i in LEDS_LEFT: | ||
bl.set_rgb(i, 255, 255, 255) | ||
show_image(show_previous=True) | ||
portal.update(display) | ||
last_updated = time.time() | ||
for i in LEDS_LEFT: | ||
bl.set_rgb(i, 0, 0, 0) | ||
|
||
# Wait here until the user stops touching the screen | ||
while touch.state: | ||
touch.poll() | ||
time.sleep(0.02) |