This is my festive craft for 2015 and builds on my 2014 Arduino based Christmas Tune Playing Christmas Wreath. I got into playing with the Raspberry PI this year and liked the aspects of running on linux and the various options I had with Linux and Python (or other programming) languages.
Last year's wreath simply used a PIR motion sensor to detect movement and then played a recorded greeting in an "elfish" voice and played a random Christmas song. This year I decided to make a photo booth type greeting complete with a touch screen interface, camera, and remote printer.
When the interface isn't presenting a "Selfie" or as I call it "Elfie" taking option I use a screensaver package to loop through festive Christmas graphics.
Raspberry PI 2 - $60 (Canakit from Amazon)
Raspberry PI Camera - $23 on Amazon
8 Gig SD card configured with Raspbian Jessie
Powered USB Hub - $10
Waveshare 800x600 Raspberry PI Touchscreen - $60
Raspberry Pi 2 Banana Pi LCD Display 7inch HDMI LCD (B) Capacitive Touch Screen
Polaroid Pogo - Polaroid CZA-10011B PoGo Instant Mobile Thermal Printer - $57
Bluetooth Dongle for Raspberry PI - $10
Inateck USB Bluetooth 4.0 Low Energy Micro Adapter USB Wireless Bluetooth Dongle, Compatible with Windows XP/ Vista/ 7/ 8/ 8.1, Support A2DP Bluetooth
Foam board - $3 for 2 (30x30) boards
PIR Sensor - $9
Christmas Wrapping Paper
Assorted Wire / Electrical Tape
Christmas Wreath 1.0 see (https://www.instructables.com/id/Christmas-Greeting-Wreath-Arduino-and-MP3/)
Configure Raspberry PI with Raspbian "Jessie" Version
This version should have Python "Idle" installed with Python 2.7 which is what was used in the programming. I had ZERO experience with Python before starting this effort and had no opinion on the use of different versions of Python. I'm sure real programmers will have a multitude of reasons why Python 3.x should have been used but I am able to do everything I need to do for this application.
Install Linux Components
Install FEH Screensaver App for rotating between festive holiday pictures
sudo apt-get install feh
Install Python Tkinter Components
sudo apt-get install python python-tk idle python-pmw python-imaging sudo apt-get install python-pip
Install ImageTK for Python
sudo apt-get install python-imaging-tk
Enable the PI camera http://thepihut.com/blogs/raspberry-pi-tutorials/...
Install PI-camera app
sudo apt-get install python-picamera
Enable BlueTooth
(GETTING BLUETOOTH TO WORK STUNK!!!!)
sudo apt-get install bluez
sudo apt-get install obexftp
sudo apt-get install ussp-push
sudo apt-get install blueman
Configure BlueMan Bluetooth on the PI in Linux
Various Useful Websites Referenced to Get the Above Going
Pi-Camera Docs: https://picamera.readthedocs.org/en/release-1.10/i...
Tkinter tutorials: https://www.daniweb.com/programming/software-devel...
Tkinter info at Effbot:http://effbot.org/tkinterbook/button.htm
Linux / Bluetooth:https://www.garron.me/en/go2linux/how-transfer-fil...
I started the programming out by focusing on getting a simple interactive touch screen system using mostly Tkinter objects to create a python GUI. All of the code is included to have a kiosk style photobooth. The code is included in a zip file in this instructable. After running this for several days I have had a few little changes that I've rectified in the upload zip file and in these examples.
The project consists of three main programs:
Code:
christmas_motion.py
import RPi.GPIO as GPIO
import time import os from subprocess import Popen import os.pathGPIO.setmode(GPIO.BCM) PIR_PIN = 7 GPIO.setup(PIR_PIN, GPIO.IN)a
def MOTION(PIR_PIN): print "Motion Detected!" #try: # os.system("killall -9 feh") # Open a file in write mode if os.path.exists("/home/pi/Documents/pythonprograms/christmas_wreath_placeholder.txt"): logfile=open("motionlog.txt", "rw+") print "Name of the file: ", logfile.name
str1 = time.strftime("%Y%m%d-%H%M%S") str2 = "\n" str3 = str1+str2 # Write a line at the end of the file. logfile.seek(0, 2) line = logfile.write( str3 )
# Close opend file logfile.close() else: proc = Popen(["python /home/pi/Documents/pythonprograms/christmas_wreath_2.0.py"], shell=True, stdin=None, stdout=None, stderr=None, close_fds=True) time.sleep(3) os.system("killall -9 feh")
print "PIR Module Test (CTRL+C to exit)" time.sleep(10) print "Ready" proc = Popen(["python /home/pi/Documents/pythonprograms/christmas_wreath_2.0.py"], shell=True, stdin=None, stdout=None, stderr=None, close_fds=True)
if os.path.exists("/home/pi/Documents/pythonprograms/christmas_wreath_placeholder.txt"): os.remove("/home/pi/Documents/pythonprograms/christmas_wreath_placeholder.txt")
try: GPIO.add_event_detect(PIR_PIN, GPIO.RISING, callback=MOTION) while 1: time.sleep(100) except KeyboardInterrupt: print "Quit" GPIO.cleanup()
christmas_wreath_2.0.py
#!/usr/bin/env python
import os
from time import sleep import tkFont from Tkinter import * import Tkinter as tk from ImageWin import Dib,HWND from PIL import Image from PIL import ImageTk from subprocess import Popen import shutil import os.path
import time
#from SimpleCV import Image
import picamera
class App: def __init__(self, master):
self.counter = 0
im = Image.open("/home/pi/Pictures/christmasbg1.jpg") resized = im.resize((340, 240),Image.ANTIALIAS) tkimage = ImageTk.PhotoImage(resized) myvar=Label(frame,image = tkimage,bg="black", text="", compound=tk.BOTTOM, font=btnFont, fg='white') myvar.image = tkimage myvar.grid(row=1,column=0,sticky=N+S+E+W)
## notice, don't use button.pack and button.grid interchangeably - grid forces buttons into the column/row locations you specify ## pack allows the tkinter system to determine what should work best self.button1 = Button(frame,relief=FLAT,bg="red",activebackground="white",foreground="silver", text="No Thanks,\n Merry Christmas", font=btnFont, command=self.quit_pressed) self.button1.grid(row=1,column=1,sticky=N+S+E+W) #self.button1.pack(fill=BOTH,expand=1)
self.button2 = Button(frame,relief=FLAT,bg="green",activebackground="red",foreground="silver", text="Take 'Elfie'?", font=btnFont, command=self.pi_picture) self.button2.grid(row=0,column=0,sticky=N+S+E+W) #self.button2.pack(fill=BOTH,expand=1)
#SHOW ELFIE BUTTON - BLANK UNTIL ELFIE TAKEN self.button4 = Button(frame,relief=FLAT,bg="green",activebackground="red",foreground="silver", font=btnFont, state=DISABLED, command=self.show_elfie) self.button4.grid(row=0,column=1,sticky=N+S+E+W) #self.button4.pack(fill=BOTH,expand=1)
def show_elfie(self): im = Image.open("/home/pi/Documents/pythonprograms/imagetest2.jpg") resized = im.resize((340, 240),Image.ANTIALIAS) tkimage = ImageTk.PhotoImage(resized) myvar=Label(frame,image = tkimage,bg="red", text="Your Elfie Preview", compound=tk.BOTTOM, font=btnFont, fg='white') myvar.image = tkimage self.button4["text"] = "Print Elfie?\nClick 'Take Elfie' to re-take" myvar.grid(row=1,column=0,sticky=N+S+E+W)
def create_window(self): self.counter += 1 t = Toplevel() center(t) t.title("Picture Here") msg = Message(t,text="Put Picture Here") msg.pack() t.wm_title("Window #%s" % self.counter) im = Image.open("/home/pi/Pictures/christmasbg1.jpg") #tkroot = Tk() tkimage = ImageTk.PhotoImage(im) frame = Frame(relief=FLAT, bg='green') frame.pack_propagate(0) frame.pack(fill=BOTH, expand=1) frame.rowconfigure((0,1),weight=1) frame.columnconfigure((0,1),weight=1) Label(frame,image=tkimage).pack() button = Button(frame,text="Dismiss", command=t.destroy) button.pack()
def quit_pressed(self): proc = Popen(["feh -Y -x -q -D 5 -B black -F -Z -z -r /home/pi/Pictures"], shell=True, stdin=None, stdout=None, stderr=None, close_fds=True) sleep(2) os.remove("christmas_wreath_placeholder.txt") #os.system("feh -Y -x -q -D 5 -B black -F -Z -z -r /home/aaron/Pictures") quit() #This will kill the application itself, not the self frame.
def pi_picture(self): with picamera.PiCamera() as camera: camera.resolution=(1920,1080) #camera.exposure_mode="backlight" #camera.awb_mode="shade" camera.start_preview() sleep(4) global filepath filepath="/home/pi/Documents/pythonprograms/wreathpics/" global filenamestring filenamestring=time.strftime("%Y%m%d-%H%M%S") global filext filext=".jpg" global filefullname filefullname=filepath+filenamestring+filext camera.capture(filefullname) camera.stop_preview() #sleep(2) im = Image.open(filefullname) resized = im.resize((340, 240),Image.ANTIALIAS) tkimage = ImageTk.PhotoImage(resized) myvar=Label(frame,image = tkimage,bg="red", text="Elfie Preview", compound=tk.BOTTOM, font=btnFont, fg='white') myvar.image = tkimage self.button2["text"] = "Re-Take Elfie?" self.button4["text"] = "Print Elfie?" self.button4["state"]= "normal" self.button4["command"] = self.print_elfie myvar.grid(row=1,column=0,sticky=N+S+E+W)
def print_elfie(self): #setup print stuff destdir="/home/pi/Documents/pythonprograms/wreathpics/prints/" destpath=destdir+filenamestring+filext shutil.copy2(filefullname,destpath) obexftpstr="sudo obexftp -b 00:04:48:10:0B:21 --channel 1 -p " obexftpfull=obexftpstr+filefullname print obexftpfull proc = Popen([obexftpfull], shell=True, stdin=None, stdout=None, stderr=None, close_fds=True) proc = Popen(["python christmas_nowprinting.py"], shell=True, stdin=None, stdout=None, stderr=None, close_fds=True) sleep(1) #os.system("feh -Y -x -q -D 5 -B black -F -Z -z -r /home/aaron/Pictures") quit() #This will kill the application itself, not the self frame.
class ImageView(Frame): def __init__(self,master,**options): Frame.__init__(self,master,**options) self.dib = None self.bind("",self._expose)
def setimage(self,image): self.config(bg="") self.dib = Dib(image) self.event_generate("")
def _expose(self,event): if self.dib: self.dib.expose(HWDN(self.winfo_id()))
def center(win): """ centers a tkinter window :param win: the root or Toplevel window to center """ win.update_idletasks() width = win.winfo_width() frm_width = win.winfo_rootx() - win.winfo_x() win_width = width + 2 * frm_width height = win.winfo_height() titlebar_height = win.winfo_rooty() - win.winfo_y() win_height = height + titlebar_height + frm_width x = win.winfo_screenwidth() // 2 - win_width // 2 y = win.winfo_screenheight() // 2 - win_height // 2 win.geometry('{}x{}+{}+{}'.format(width, height, x, y)) win.deiconify()
def close_after(): proc = Popen(["feh -Y -x -q -D 5 -B black -F -Z -z -r /home/pi/Pictures"], shell=True, stdin=None, stdout=None, stderr=None, close_fds=True) sleep(2) os.remove("christmas_wreath_placeholder.txt") #os.system("feh -Y -x -q -D 5 -B black -F -Z -z -r /home/aaron/Pictures") quit() #This will kill the application itself, not the self frame.
#this is the main program root = Tk()
w, h = root.winfo_screenwidth(), root.winfo_screenheight() # use the next line if you also want to get rid of the titlebar to run a full screen app root.overrideredirect(1)
root.geometry("%dx%d+0+0" % (w, h)) root.config(bg="black",padx=10,pady=10) #root.rowconfigure((0,1),weight=1) #root.columnconfigure((0,1),weight=1)
#put the frame here to make it globally available to the sub routines frame = Frame(relief=FLAT, bg='darkgreen', bd="0") frame.rowconfigure((0,1),weight=1) frame.columnconfigure((0,1),weight=1) frame.pack_propagate(0) frame.pack(fill=BOTH, expand=1)
btnFont = tkFont.Font(family='Times',size=32,weight='bold')
if not os.path.exists("christmas_wreath_placeholder.txt"): open("christmas_wreath_placeholder.txt","w")
root.after(300000, close_after) #call quit after 5 minutes no matter what
app = App(root) root.mainloop()
#os.system("pkill -x feh")
#exit()
christmas_nowprinting.py
from Tkinter import *
from PIL import Image, ImageTk from subprocess import Popen import os.pathclass MyLabel(Label): def __init__(self, master, filename): im = Image.open(filename) seq = [] try: while 1: seq.append(im.copy()) im.seek(len(seq)) # skip to next frame except EOFError: pass # we're done
try: self.delay = im.info['duration'] except KeyError: self.delay = 100
first = seq[0].convert('RGBA') self.frames = [ImageTk.PhotoImage(first)]
Label.__init__(self, master, image=self.frames[0])
temp = seq[0] for image in seq[1:]: temp.paste(image) frame = temp.convert('RGBA') self.frames.append(ImageTk.PhotoImage(frame))
self.idx = 0
self.cancel = self.after(self.delay, self.play)
def play(self): self.config(image=self.frames[self.idx]) self.idx += 1 if self.idx == len(self.frames): self.idx = 0 self.cancel = self.after(self.delay, self.play)
root = Tk() w, h = root.winfo_screenwidth(), root.winfo_screenheight() root.overrideredirect(1) root.geometry("%dx%d+0+0" % (w, h)) root.config(bg="darkgreen",padx=10,pady=10) anim = MyLabel(root, "/home/pi/Documents/pythonprograms/nowprinting.gif") anim.pack()
def stop_it(): anim.after_cancel(anim.cancel)
#Button(root, text='stop', command=stop_it).pack() root.after(107000,lambda: os.remove("christmas_wreath_placeholder.txt")) root.after(109000,lambda: Popen(["feh -Y -x -q -D 5 -B black -F -Z -z -r /home/pi/Pictures"], shell=True, stdin=None, stdout=None, stderr=None, close_fds=True)) root.after(110000,lambda: root.destroy()) #destroy the widget after 20seconds
root.mainloop()
Getting the PI to AutoStart the Tkinter based Python app with the User Session
That's a mouthful but basically you can't just run this tkinter dependent python app from the command line in a console only environment, you need to be in an x-windows or LXDE session, aka, a normal Linux desktop - NOT telnet or SSH since this is a GUI app. To do this on a raspberry PI there is a specific autostart file that you edit which executes GUI OR non-GUI apps AFTER the desktop is loaded. The best I can compare this to is the windows "startup" folder.
Edit the following file:
/home/pi/.config/lxsession/LXDE-pi/autostart
You can do this via a command line session by typing:
sudo nano /home/pi/.config/lxsession/LXDE-pi/autostart
Then add your script to kick off your program at the bottom of the file, I used a SH script to kick off my christmas_motion.py file. Here's what the autostart file looks like:
@lxpanel --profile LXDE-pi<br>@pcmanfm --desktop --profile LXDE-pi @xscreensaver -no-splash <br>@sh ${HOME}/.config/lxsession/LXDE-pi/autokey.sh <br>@sh /home/pi/Documents/pythonprograms/christmas_launcher.sh
NOTE the last line points to my Launcher file (christmas_launcher.sh) - my launcher file looks like this:
#!/bin/sh<br>#christmas_launcher.sh <br>#navigate to directory<br>cd / cd /home/pi/Documents/pythonprograms sudo python christmas_motion.py cd /
The launcher file makes sure that I run the christmas_motion.py using SUDO / root privileges so that the GPIO requests in the python code have the correct level of permissions to access the GPIO pins, otherwise the standard "PI" user does not have adequate permissions to access the motion sensor pins.
I tried launching the christmas_motion.py directly from the autostart file but it resulted in a weird infinite loop of kicking off the same app over and over again so I resorted back to my standard SH script which has worked every time.
Building the Foam Box
I created the mounting for the PI, Camera, and Printer out of simple craft foam board. It was very easy to work with and cost practically nothing so when I cut twice and measured once, whoops "measure twice and cut once" it didn't cost me much to start over. I simply measured the interior of the wreath and started from there. I outlined the screen and made a mount for the screen which was free floating, pictured in the third and fourth pictures. The free floating screen holder allowed me to move it around in the "box top" to get the correct placement in the view port.
I used brown packing tape which has very tacky and holds to the foam board very well, plus it lies flat vs something like duct tape or masking type that would either have texture or not be tacky enough to keep the box stiff.
Mounting the Wreath
I cut a piece of plywood that I could prop above some of the steel rods on the wreath and wired the wood to the rods to secure it. I then put velcro on the plywood cross bars so I could "set" the box on that, those are pictured in the last picture. Every wreath will probably be different so good luck!
When a visitor walks up to your door the PIR sensor from last year's wreath picks up first and greets the visitor with an "Elfish" high pitched "Welcome to the Jones', Merry Christmas" and begins to play a random Christmas MP3 song. The motion sensor hooked to the raspberry PI then picks up and presents the screens (christmas_wreath_2.0.py) for taking an Elfie picture.
You can "Take Elfie" or quit by touching "No Thanks, Merry Christmas"
A preview takes over the entire screen(not pictured above) where you have about 3-5 seconds (easily configurable in code) and then a picture is taken
The Picture is loaded into the lower left of the screen and you can either click "Re-take Elfie", "Print Elfie", or Quit again.
If you click "Re-take" the process re-takes a picture and loads the new preview.
If you click "Print Elfie" the "Now Printing" animation takes over so you know something is happening since the bluetooth printer takes about 2 minutes to receive a standard 5mp photo coming out of the PI camera, then the photo prints out and the app quits until the next movement is detected.
We put the wreath through its paces with a bunch of 4-17 year olds at a family Christmas Part and it held up pretty well. They were able to intuitively use the system and took a boatload of "Elfies". One participant managed to be in almost every Elfie picture, they loved it so much.
We also bought a bunch of kistchy props for making the Elfies more festive and fun and have stuck them in the wreath for easy access and storage for visitors.
Some enhancements I'd like to make later: