Ausgangslage
Offenbar gibt es aufsteigenden Trends für Verwendung einer Photobox. Vor allem solche mit Sofortdruckfunktion sind aufgrund ihres Unterhaltungswerts und wegen der tollen Erinnerungen auf Feierlichkeiten beinahe unverzichtbar.
Diese Erkenntnis überbrachte mir eine Kundin mit dem Auftrag mit Raspberry Pi(RasPi) die Software dazu zu realisieren. Die Hardware wurde mir zur Verfügung gestellt und die Box wurde auch von der Kundin gebaut. Für mich war das Neuland, aber solche Herausforderungen reizen mich bei meinen Arbeiten.
Dies ist keine vollständige Beschreibung, sondern für mich um die Schritte zu dokumentieren. Aber eventuell hilft es anderen bei der Umsetzung.
Funktion
Der Wunsch war, dass von der Kamera ein Live-Bild an RasPi gesendet und im Display angezeigt wird. Darin sollen drei Buttons für Aufnahme, Drucken und Live Bild eingeblendet sein. Über einen Präsenter soll das Foto geschossen und das Live-Bild eingefroren und bei „Neu“ wieder zurückgeschaltet werden. Mit „Speichern“ soll das Bild in höherer Auflösung auf die Karte gespeichert und mit „Drucken“ ausgedruckt werden.
Material
Zur Verfügung hatte ich neben Raspberry Pi folgende Teile:
- 10,1 Zoll HDMI Touch-Screen Display
- Canon EOS 450D
- DNP DS-40
Installation
System update
Als erstes sollte RasPi aktuell gehalten werden:
sudo apt-get -y update && sudo apt-get -y upgrade
Display einrichten
Das Display anzuschliessen war nicht so ein Problem. Der RasPi benötigte noch eine Treiber-Installation, da die Proportion der Anzeige nicht stimmte:
cd /boot/
wget https://www.waveshare.com/w/upload/1/1e/LCD-show-180817.tar.gz
tar xzvf /boot/LCD-show-*.tar.gz
cd LCD-show/
chmod +x LCD101-1024x600-show
./LCD101-1024x600-show
Nach dem Neustart stimmt nun die Auflösung
Druckerinstallation
Das Einrichten des Druckers lief auch ohne Probleme mit Hilfe von Cups und
sudo apt-get install cups
sudo usermod -a -G lpadmin pi
Nun kann der Drucker angeschlossen und eingeschaltet werden. Cups erkennt diesen automatisch. Wenn RasPi im Grafikmodus läuft, kann über den Webbrowser mit der Adresse http://127.0.0.1:631 der Drucker konfiguriert werden.
Kamerasteuerung
Für die Ansteuerung der Kamera erweist sich GPhoto als hilfreich.
sudo apt-get install gphoto2 # Installation von GPhoto
gphoto2 --capture-image-and-download # Testen
Nun war das „Problem“, dass RasPi automatisch ein Laufwerk mit der Kamera verbunden hat. Ansich eine gute Sache, aber damit hat GPhoto keinen Zugriff auf die Kamera. Mit einem Kniff muss demnach die Überwachung ausgeschaltet werden. Ich mache das, indem ich die Betreffenden Dateien umbenenne:
cd /usr/share/dbus-1/services/
sudo mv org.gtk.vfs.GPhoto2VolumeMonitor.service *.orig
sudo mv *.orig org.gtk.vfs.GPhoto2VolumeMonitor.service.orig
cd /usr/share/gvfs/mounts/
sudo mv gphoto2.mount gphoto2.mount.orig
cd /usr/share/gvfs/remote-volume-monitors/
sudo mv gphoto2.mount gphoto2.mount.orig
sudo mv gphoto2.monitor gphoto2.monitor.orig
cd /usr/lib/gvfs/
sudo mv gvfs-gphoto2-volume-monitor gvfs-gphoto2-volume-monitor.orig
Und dann klappt’s auch mit dem Nachbarn.
Ein Life-Bild kann auf folgende Weise angezeigt werden:
gphoto2 --capture-movie --stdout> fifo.mjpg & omxplayer fifo.mjpg
Allerdings habe ich darüber keine Kontrolle, sodass ich das in meinem Programm nicht einbauen konnte.
Nun ist alles eingerichtet und man kann beginnen die Software zu schreiben.

Software
Als erste war die Überlegung, in welcher Programmier-Umgebung möchte ich das umsetzen. Aufgrund des Systems kam eigentlich Python in Frage. Die nächste Frage war, da Python standardmässig keine grafischen Elemente kennt, was verwende ich dafür und entschied mich für Pygame als Erweiterung.
Die grosse Herausforderung war, die Live-Bilder von der Kamera angezeigt zu bekommen, das Streamen funktioniert leider nicht per Python. Die Dokumentation war nicht sehr hilfreich und im Netz war dazu nur wenig Info auffindbar. Der Kamera Einzelbilder zu entlocken war nicht klug, da der Spiegel sich jeweils öffnete (die Mechanik lässt grüssen). Nach dem ich duzende Codes durch gestrählt habe, fand ich endlich den Befehl, der den Spiegel offenlässt. Somit lade ich Einzelbilder von der Kamera und zeige sie dann im Display an.
Ram-Disk einrichten
Der letzte Knackpunkt war eher ein versteckter, an den man nicht denkt: Das Betriebssystem und damit auch der Code und die Ablage der Einzelbilder vom Live-Bild liegen auf der SD-Karte. Diese Karten haben aber nur eine begrenzte Anzahl an Schreibzyklen, danach verabschieden sie sich allmählich. Die Lösung war, da RaspPi genügend Reserve hat, ein Ram-Disk einzurichten, wo die Ablage des Live-Bildes im Arbeitsspeicher stattfindet.
Dazu erst ein Verzeichnis anlegen:
sudo mkdir /mnt/RAMDisk
und dann folgenden Eintrag in die Datei /etc/fstab einfügen:
tmpfs /mnt/RAMDisk tmpfs nodev,nosuid,size=16M 0 0
Die Größe wird über den Parameter „16M“ auf 16 MB festgelegt. Jetzt alle Filesysteme über einbinden:
sudo mount -a
Der Erfolg lässt sich mit Diskfree überprüfen:
sudo df
Photobox Code V1.0 Beta
Nachfolgend ist der Python-Code, mit dem Userinterface was die Kamera und den Drucker steuert:

import piggyphoto
import pygame
import os
import time
import subprocess as sub
from shutil import copyfile
usecamera = True
cameraonline = False
running = True
freeze = False
pos = [0,0]
click = [0,0,0]
width = 1024
height = 600
button_width = 200
button_hight = 50
white = (255,255,255)
black = (0,0,0)
save_button = (200,100,50)
save_button_light = (255,155,105)
print_button = (200,200,0)
print_button_light= (255,255,0)
live_button = (34,177,76)
live_button_light = (90,230,130)
intro_file = "images/intro.jpg"
preview_file = "/mnt/RAMDisk/preview.jpg"
capture_file = "/mnt/RAMDisk/capture.jpg"
discon_file = "images/offline.jpg"
photo_dest_file = "photos/capture_"
def DrawCenterMessage(message,width,height,x,y):
#displays notification messages onto the screen
backgroundCenterSurface = pygame.Surface((width,height))#size
backgroundCenterSurface.fill(black)
main_surface.blit(backgroundCenterSurface,(x,y))#position
main_surface.blit(pygame.font.SysFont("freeserif",40,bold=1).render(message, 1, white),(x+10,y+10))
pygame.display.update()
def text_objects(text, color):
textSurface = pygame.font.SysFont("comicsansms", 50).render(text, True, color)
return textSurface, textSurface.get_rect()
def text_to_button(msg, color, buttonx, buttony, buttonwidth, buttonheight):
textSurf, textRect = text_objects(msg,color)
textRect.center = ((buttonx+(buttonwidth/2)), buttony+(buttonheight/2))
main_surface.blit(textSurf, textRect)
def button(text, x, y, width, height, inactive_color, active_color, action = None):
global pos
global click
if x + width > pos[0] > x and y + height > pos[1] > y:
pygame.draw.rect(main_surface, active_color, (x,y,width,height))
if click[0] == 1 and action != None:
if action != None:
action()
click = [0,0,0]
else:
pygame.draw.rect(main_surface, inactive_color, (x,y,width,height))
text_to_button(text,black,x,y,width,height)
def keypressed_event():
global pos
global click
for event in pygame.event.get():
if event.type == pygame.QUIT:
return "quit"
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
return "photoshut"
if event.key == pygame.K_RIGHT:
return "photoshut"
if event.key == pygame.K_LEFT:
return "photoshut"
if event.key == pygame.K_q:
return "quit"
if event.type == pygame.MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
click = [1,0,0]
def show(file):
picture = pygame.image.load(file)
picture = pygame.transform.scale(picture, (width, height))
main_surface.blit(picture, (0, 0))
def doPhotoShut():
if cameraonline:
DrawCenterMessage("CHEEEEESSSSSE....",400,70,((width/2)-220),((height/2)-2))
print C.abilities
C.capture_image(capture_file)
show(preview_file)
def doCopy():
DrawCenterMessage("BILD GESPEICHERT!",400,70,((width/2)-220),((height/2)-2))
date_string = time.strftime("%Y-%m-%d-%H:%M")
copyfile(capture_file, photo_dest_file + date_string + ".jpg")
def doPrint():
DrawCenterMessage("PRINT....",400,70,((width/2)-220),((height/2)-2))
cmd = "lpr -P DNP_DP-DS620 -o PageSize=w288h432 " + capture_file
p = sub.Popen(cmd,stdout=sub.PIPE,stderr=sub.PIPE,shell=True)
show(preview_file)
def doCatch():
global freeze
freeze = False
def initCamera():
global main_surface
global usecamera
global cameraonline
global C
if usecamera:
try:
C = piggyphoto.camera()
C.leave_locked()
C.capture_preview(preview_file)
cameraonline = True
except:
print "PiggyPhoto Fehler: Keine Kammera verbunden oder nicht eingeschaltet!"
cameraonline = False
else:
cameraonline = False
picture = pygame.image.load(intro_file)
picture = pygame.transform.scale(picture, (width, height))
pygame.display.set_mode(picture.get_size(), pygame.FULLSCREEN)
main_surface = pygame.display.get_surface()
pygame.init()
initCamera()
while running:
if cameraonline:
if freeze == False:
try:
C.capture_preview(preview_file)
show(preview_file)
except:
cameraonline = False
else:
initCamera()
show(discon_file)
if freeze:
button("Speichern", 150,500,button_width,button_hight, save_button, save_button_light, action = doCopy)
button("Drucken", 400,500,button_width,button_hight, print_button, print_button_light, action = doPrint)
button("Neu", 650,500,button_width,button_hight, live_button, live_button_light, action = doCatch)
pygame.display.update()
event = keypressed_event()
if event == "quit":
running = False
if event == "photoshut":
doPhotoShut()
freeze = True
So, damit ist das Projekt auch gesichert 😆