Digital Photo Frame – DIY

If you’ve ever fancied a digital display frame, but perhaps wanted more than just a simple carousel of your pictures, or greater control over how the device behaves, then read on.

What we’ll build

A digital display board that showcases your photographs, with an overlaid calendar and weather forecast. For those of you into home automation, I’ll also cover how to turn the display on and off by integrating with Samsung SmartThings home hub – the basic approach could serve as a starting point for other integrations.

How we’ll do it

A Raspberry Pi zero w (about $26 with case and power supply) combined with a Dell 24″ monitor (about $120), and the Dakboard web digital display service.

Dakboard

The simplest way to get started is to follow this handy guide. This will help you get your Pi up and running, though I found that for the release of Raspbian current as of December 2019, I need to follow the alternative instructions for updating the autostart instructions :

/etc/xdg/lxsession/LXDE-pi/autostart

Automating the Pi display

We’re going to create a RESTful web service (API) that can be invoked with a simple URL to turn the display on and off, this is what our home automation will call. This will leverage Python3, Flask, and NGINX.

NGINX

This is a popular web server for the Pi, you could use apache but the tutorials I was following used nginx.

sudo apt-get install nginx

Python3

If your Raspbian install is fresh (you used noobs to get up and running) you probably don’t need to sudo apt-get update, but it might not include Python3, so let’s install that.

Log into your Pi (I use SSH from my Macbook)

sudo apt-get install python3-pip

UWSGI flask

This will install the flask API framework, which greatly simplifies the work required to create RESTful apis in python.

sudo pip3 install flask uwsgi
sudo pip3 install flask-restful

Python api program

You can clone from https://github.com/klcampbell6502/pirest.git or use the code as displayed there.

from flask import Flask
from flask_restful import Resource, Api
import os
 
app = Flask(__name__)
 
api = Api(app)

class ScreenOff(Resource):
    def get(self):
        os.system("vcgencmd display_power 0")
        return "off"
 
class ScreenOn(Resource):
    def get(self):
        os.system("vcgencmd display_power 1")
        return "on"

api.add_resource(ScreenOff,'/screen/off')
api.add_resource(ScreenOn,'/screen/on')

if __name__ == '__main__':
    app.run(host='0.0.0.0')

Testing

We should now be able to test the flask API using the debug environment:

uwsgi --socket 0.0.0.0:8000 --protocol=http -w api:app

and then invoke the URL:

http://yourpiaddress:8000/screen/off

You should see the single word “off” in your browser, and if you look in your ssh output you should see something like this:

*** uWSGI is running in multiple interpreter mode ***
spawned uWSGI worker 1 (and the only) (pid: 7822, cores: 1)
display_power=0
[pid: 7822|app: 0|req: 1/1] 192.168.86.24 () {32 vars in 644 bytes} [Wed Jan  1 12:16:54 2020] GET /screen/off => generated 6 bytes in 273 msecs (HTTP/1.1 200) 2 headers in 70 bytes (1 switches on core 0)
[pid: 7822|app: 0|req: 2/2] 192.168.86.24 () {32 vars in 574 bytes} [Wed Jan  1 12:16:55 2020] GET /favicon.ico => generated 233 bytes in 330 msecs (HTTP/1.1 404) 2 headers in 72 bytes (1 switches on core 0)

Oh, and your HDMI display is now off 🙂 Type ctrl-C in your ssh session to stop the uWSGI test server. To make things more robust we’re now going to integrate uWSGI with the nginx web server.

nginx integration

If you didn’t download the project from git then you’ll need to create a file uwsgi.ini in the directory containing your .py file:

[uwsgi]
chdir = /home/pi/Documents/pirest
module = api:app
master = true
processes = 1
threads = 2

uid = www-data
gid = www-data

socket = /tmp/pirest.sock
chmod-socket = 664
vacuum = true
die-on-term = true
touch-reload = /home/pi/Documents/pirest/api.py

This will provide the startup instructions for uwsgi when invoked from nginx.
Now we’ll get rid of the default nginx site

sudo rm /etc/nginx/sites-enabled/default

and now create a proxy file

sudo nano /etc/nginx/sites-available/pirest_proxy

containing the following:

server {
listen 80;
server_name localhost;
location / { try_files $uri @app; }
location @app {
include uwsgi_params;
uwsgi_pass unix:/tmp/pirest.sock;
}
}

Now we need to create a symbolic link to this file

sudo ln -s /etc/nginx/sites-available/pirest_proxy /etc/nginx/sites-enabled

then restart nginx

sudo systemctl restart nginx

Set uWSGI to run on boot

cd /etc/systemd/system
sudo nano uwsgi.service

and into this new file, paste the following:

[Unit]
Description=uWSGI Service
After=network.target

[Service]
User=pi
Group=www-data
WorkingDirectory=/home/pi/Documents/pirest
ExecStart=/usr/local/bin/uwsgi --ini /home/pi/Documents/pirest/uwsgi.ini
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$

[Install]
WantedBy=multi-user.target

restart the daemon so the new configuration is picked up:

sudo systemctl daemon-reload

and start the service we’ve just defined:

sudo systemctl start uwsgi.service

You can check the status of the running service like this:

sudo systemctl status uwsgi.service

you should see something like this:

? uwsgi.service - uWSGI Service
   Loaded: loaded (/etc/systemd/system/uwsgi.service; disabled; vendor preset: enabled)
   Active: active (running) since Wed 2020-01-01 12:49:52 CST; 1min 3s ago

 Main PID: 8038 (uwsgi)
   Memory: 13.1M
   CGroup: /system.slice/uwsgi.service
           ??8038 /usr/local/bin/uwsgi --ini /home/pi/Documents/pirest/uwsgi.ini
           ??8044 /usr/local/bin/uwsgi --ini /home/pi/Documents/pirest/uwsgi.ini
Jan 01 12:49:55 piboard02 uwsgi[8038]: mapped 143936 bytes (140 KB) for 2 cores
Jan 01 12:49:55 piboard02 uwsgi[8038]: *** Operational MODE: threaded ***
Jan 01 12:50:07 piboard02 uwsgi[8038]: WSGI app 0 (mountpoint='') ready in 12 seconds on interpreter 0x16984f0 pid: 8038 (de
Jan 01 12:50:07 piboard02 uwsgi[8038]: *** uWSGI is running in multiple interpreter mode ***
Jan 01 12:50:07 piboard02 uwsgi[8038]: spawned uWSGI master process (pid: 8038)
Jan 01 12:50:07 piboard02 uwsgi[8038]: spawned uWSGI worker 1 (pid: 8044, cores: 2)
Jan 01 12:50:07 piboard02 uwsgi[8038]: display_power=0
Jan 01 12:50:07 piboard02 uwsgi[8038]: [pid: 8044|app: 0|req: 1/1] 192.168.86.24 () {42 vars in 767 bytes} [Wed Jan  1 12:50
Jan 01 12:50:15 piboard02 uwsgi[8038]: display_power=1
Jan 01 12:50:15 piboard02 uwsgi[8038]: [pid: 8044|app: 0|req: 2/2] 192.168.86.24 () {40 vars in 734 bytes} [Wed Jan  1 12:50
lines 1-19/19 (END)

You’ll see here that I tried the service on and off by entering this URL in my browser:

http://piboard02/screen/on

The final step is to ensure that this starts with every boot:

sudo systemctl enable uwsgi.service

and you’ll see something along these lines:

“Created symlink /etc/systemd/system/multi-user.target.wants/uwsgi.service /etc/systemd/system/uwsgi.service”

Reboot and test using the URL – give it time to boot, especially if using a pi zero as they’re pretty slow.

SmartThings Integration

Device Handler

OK, so you’ve got a pi running a display board, and you can turn the HDMI output on and off by opening a URL. Now to create a device handler for SmartThings that enables you to include this in automations, and of course control it from your SmartThings app.

Go grab the source over at my github repo. For now, I’m going to assume the reader knows how to create an account at the Samsung developer site https://graph.api.smartthings.com/

Go to My Device Handlers, and Create – From Code, then paste the code from Git. Or, you could just import my repo into your account. You’ll then need to publish it, choosing “just me” should be sufficient.

New Device

Using the IDE, create a new device:

 

Complete the new device screen – use your own values for Network ID (which must be unique), name, label, etc. I’ve used IP address for network ID, but what you use actually doesn’t matter.

When you get to the Type pulldown, scroll all the way down past the commercial handlers until you see the REST Switch device handler we just created. Location and Hub must be set to your SmartThings hub, you’ll typically only see a single choice in those pulldowns.

Click on the Edit link alongside Settings in the device display, and then enter the IP address and port 80 for your Pi. Your hub must be on the same network as your pi, as the hub itself will run the device handler code we just established.

After a couple of minutes the newly created device should appear in your app: