pao9.py

import time
import os
import random
import board
import busio
import pygame
import cv2
from picamera2 import Picamera2
from adafruit_neotrellis.neotrellis import NeoTrellis
from pygame.locals import FULLSCREEN

# =========================
# PYGAME & AUDIO SETUP
# =========================
pygame.init()
pygame.mixer.init()
screen = pygame.display.set_mode((0, 0), FULLSCREEN)
clock = pygame.time.Clock()

# =========================
# DIRECTORY SETUP
# =========================
script_dir = os.path.dirname(os.path.realpath(__file__))
VIDEO_DIR = os.path.join(script_dir, "videos")
PHOTO_DIR = os.path.join(script_dir, "photos")
IMAGE_DIR = os.path.join(script_dir, "images")
WARNING_IMAGE = os.path.join(IMAGE_DIR, "paowarning.png")
PAOPROJECT_IMAGE = os.path.join(IMAGE_DIR, "paoeject.png")

os.makedirs(PHOTO_DIR, exist_ok=True)

# =========================
# STATE VARIABLES
# =========================
playing_video = None
playing_audio = None

# Initialize Picamera2 once to suppress repeated init logs
picam2 = Picamera2()
picam2.configure(picam2.create_still_configuration())
picam2.start()

# =========================
# HELPER FUNCTIONS
# =========================
def play_audio_for_video(video_file):
global playing_audio
base_name = os.path.splitext(os.path.basename(video_file))[0]
audio_file = os.path.join(VIDEO_DIR, f"{base_name}.mp3")
if os.path.exists(audio_file):
pygame.mixer.music.load(audio_file)
pygame.mixer.music.play()
playing_audio = audio_file
print(f"Playing audio: {audio_file}")
else:
print(f"No audio found for {base_name}")

def stop_audio():
global playing_audio
if pygame.mixer.music.get_busy():
pygame.mixer.music.stop()
playing_audio = None

def stop_video():
"""Stop currently playing video/audio immediately"""
global playing_video
if playing_video:
playing_video.release()
playing_video = None
stop_audio()

def take_photo():
"""Take a photo with Picamera2 and save in photos folder with random filename"""
while True:
rand_num = random.randint(1000, 9999)
photo_path = os.path.join(PHOTO_DIR, f"{rand_num}.jpg")
if not os.path.exists(photo_path):
break
picam2.capture_file(photo_path)
print(f"Photo saved: {photo_path}")
return photo_path

def play_video(video_file):
cap = cv2.VideoCapture(video_file)
if not cap.isOpened():
print("Error opening video:", video_file)
return None

while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame_surface = pygame.surfarray.make_surface(frame)
frame_surface = pygame.transform.rotate(frame_surface, -90)
frame_surface = pygame.transform.flip(frame_surface, True, False)
frame_surface = pygame.transform.scale(frame_surface, screen.get_size())

screen.blit(frame_surface, (0, 0))
pygame.display.update()

for event in pygame.event.get():
if event.type == pygame.QUIT:
stop_video()
pygame.quit()
return None

trellis.sync()
clock.tick(30)

cap.release()
stop_audio()
return cap

def show_image(image_path):
"""Display a single image fullscreen"""
if os.path.exists(image_path):
img = pygame.image.load(image_path)
img = pygame.transform.scale(img, screen.get_size())
screen.blit(img, (0, 0))
pygame.display.update()
print(f"Showing image: {image_path}")
else:
print(f"Image not found: {image_path}")

def show_warning_image():
show_image(WARNING_IMAGE)

# =========================
# BUTTON CALLBACK
# =========================
def button_callback(event):
global playing_video

if event.edge != NeoTrellis.EDGE_RISING:
return

video_index = event.number

# Stop any currently playing video/audio immediately
stop_video()

# Button 13: show paoproject.png
if video_index == 12:
show_image(PAOPROJECT_IMAGE)
return

# Button 16: take photo and display the newly taken photo
if video_index == 15:
new_photo = take_photo()
show_image(new_photo)
return

# All other buttons: play video + audio
video_file = os.path.join(VIDEO_DIR, f"{str(video_index+1).zfill(3)}.mp4")
if os.path.exists(video_file):
play_audio_for_video(video_file)
playing_video = play_video(video_file)
else:
print(f"Video file not found: {video_file}")

# =========================
# NEOTRELLIS SETUP
# =========================
i2c = busio.I2C(board.SCL, board.SDA)
trellis = NeoTrellis(i2c)
for i in range(16):
trellis.activate_key(i, NeoTrellis.EDGE_RISING)
trellis.callbacks[i] = button_callback

# =========================
# STARTUP
# =========================
show_warning_image()
for i in range(16):
trellis.pixels[i] = (0, 0, 50)

print("NeoTrellis controller ready!")

# =========================
# MAIN LOOP
# =========================
while True:
trellis.sync()
time.sleep(0.02)