davideisinger.com

My personal website
Log | Files | Refs | README

index.md (8922B)


      1 ---
      2 title: "Making an Email-Powered E-Paper Picture Frame"
      3 date: 2021-05-12T00:00:00+00:00
      4 draft: false
      5 canonical_url: https://www.viget.com/articles/making-an-email-powered-e-paper-picture-frame/
      6 featured: true
      7 ---
      8 
      9 Over the winter, inspired by this [digital photo
     10 frame](http://toolsandtoys.net/aura-mason-smart-digital-picture-frame/)
     11 that uses email to add new photos, I built and programmed a trio of
     12 e-paper picture frames for my family, and I thought it'd be cool to
     13 walk through the process in case someone out there wants to try
     14 something similar.
     15 
     16 <!--more-->
     17 
     18 {{<dither IMG_0120.jpeg>}}A wood-and-glass stand displays a grayscale portrait, sitting beside amber glasses and books on a tidy desk.{{</dither>}}
     19 
     20 In short, it's a Raspberry Pi Zero connected to a roughly 5-by-7-inch
     21 e-paper screen, running some software I wrote in Go and living inside a
     22 frame I put together. This project consists of four main parts:
     23 
     24 1.  The email-to-S3 gateway, [described in detail in a previous
     25     post](/elsewhere/email-photos-to-an-s3-bucket-with-aws-lambda-with-cropping-in-ruby/);
     26 2.  The software to display the photos on the screen;
     27 3.  Miscellaneous Raspberry Pi configuration; and
     28 4.  The physical frame itself.
     29 
     30 As for materials, you'll need the following:
     31 
     32 -   [A Raspberry Pi Zero with
     33     headers](https://www.waveshare.com/raspberry-pi-zero-wh.htm)
     34 -   [An e-paper
     35     display](https://www.waveshare.com/7.5inch-hd-e-paper-hat.htm)
     36 -   A micro SD card (and some way to write to it)
     37 -   Some 1x4 lumber (I used oak)
     38 -   [4x metal standoffs](https://www.amazon.com/gp/product/B00TX464XQ)
     39 -   [A 6x8 piece of
     40     acrylic](https://www.amazon.com/gp/product/B07J4WX7BH)
     41 -   Some wood glue to attach the boards, and some wood screws to attach
     42     the standoffs
     43 
     44 I'll get more into the woodworking tools down below.
     45 
     46 ## The Email-to-S3 Gateway
     47 
     48 Like I said, [I've already documented this part pretty
     49 thoroughly](/elsewhere/email-photos-to-an-s3-bucket-with-aws-lambda-with-cropping-in-ruby/),
     50 but in short, we use an array of AWS services to set up an email address
     51 that fires off a Lambda function when it receives an email. The function
     52 extracts the attachments from the email, crops them a couple of ways
     53 (one for display on a webpage, the other for display on the screen), and
     54 uploads the results into an S3 bucket.
     55 
     56 {{<dither Screen_Shot_2021-05-09_at_1_26_39_PM.png>}}A web dashboard shows a green banner announcing “Photo image_50434049.JPG displayed!” above a grid of blurred photo thumbnails, each with a blue “Display” button beneath it in a browser window titled “192.168.4.61”.{{</dither>}}
     57 
     58 ## The Software
     59 
     60 The next task was to write the code that runs on the Pi that can update
     61 the display periodically. I also thought it'd be cool if it could
     62 expose a simple web interface on the local network to let my family
     63 members browse the photos and display them on the frame. When selecting
     64 a language, I could have gone with either Ruby or Python, the former
     65 since that's what I'm most familiar with, the latter because that's
     66 what [the code provided by
     67 Waveshare](https://github.com/waveshare/e-Paper/tree/master/RaspberryPi_JetsonNano/python/lib/waveshare_epd),
     68 the manufacturer, is written in.
     69 
     70 But I chose neither of those options, reader, opting instead for Go. Why
     71 Go, you ask?
     72 
     73 -   **I wanted something robust.** Ideally, this code will run on these
     74     devices for years with no downtime. If something does go wrong, I
     75     won't have any way to debug the problems remotely, instead having
     76     to wait until the next time I'm on the same wifi network with the
     77     failing device. Go's explicit error checking was appealing in this
     78     regard.
     79 
     80 -   **I wanted deployment to be simple.** I didn't have any appetite
     81     for all the configuration required to get a Python or Ruby app
     82     running on the Pi. The fact that I could compile my code into a
     83     single binary that I could `scp` onto the device and manage with
     84     `systemd` was compelling.
     85 
     86 -   **I wanted a web UI**, but it wasn't the main focus. With Go, I
     87     could just import the built-in `net/http` to add simple web
     88     functionality.
     89 
     90 To interface with the screen, I started with [this super awesome GitHub
     91 project](https://github.com/gandaldf/rpi). Out of the box, it didn't
     92 work with my screen, I *think* because Waveshare offers a bunch of
     93 different screens and the specific instructions differ between them. So
     94 I forked it and found the specific Waveshare Python code that worked
     95 with my screen ([this
     96 one](https://github.com/waveshare/e-Paper/blob/master/RaspberryPi_JetsonNano/python/lib/waveshare_epd/epd7in5_HD.py),
     97 I believe), and then it was just a matter of updating the Go code to
     98 match the Python, which was tricky because I don't know very much about
     99 low-level electronics programming, but also pretty easy since the Go and
    100 Python are set up in pretty much the same way.
    101 
    102 [Here's my
    103 fork](https://github.com/dce/rpi/blob/master/epd7in5/epd7in5.go) --- if
    104 you go with the exact screen I linked to above, it *should* work, but
    105 there's a chance you end up having to do what I did and customizing it
    106 to match Waveshare's official source.
    107 
    108 Writing the main Go program was a lot of fun. I managed to do it all ---
    109 interfacing with the screen, displaying a random photo, and serving up a
    110 web interface --- in one (IMO) pretty clean file. [Here's the
    111 source](https://github.com/dce/e-paper-frame), and I've added some
    112 scripts to hopefully making hacking on it a bit easier.
    113 
    114 ## Configuring the Raspberry Pi
    115 
    116 Setting up the Pi was pretty straightforward, though not without a lot
    117 of trial-and-error the first time through:
    118 
    119 1.  Flash Raspberry Pi OS onto the SD card
    120 2.  [Configure your wifi
    121     information](https://www.raspberrypi.org/documentation/configuration/wireless/wireless-cli.md)
    122     and [enable
    123     SSH](https://howchoo.com/g/ote0ywmzywj/how-to-enable-ssh-on-raspbian-without-a-screen#create-an-empty-file-called-ssh)
    124 3.  Plug it in --- if it doesn't join your network, you probably messed
    125     something up in step 2
    126 4.  SSH in (`ssh pi@<192.168.XXX.XXX>`, password `raspberry`) and put
    127     your public key in `.ssh`
    128 5.  Go ahead and run a full system update
    129     (`sudo apt update && sudo apt upgrade -y`)
    130 6.  Install the AWS CLI and NTP (`sudo apt-get install awscli ntp`)
    131 7.  You'll need some AWS credentials --- if you already have a local
    132     `~/.aws/config`, just put that file in the same place on the Pi; if
    133     not, run `aws configure`
    134 8.  Enable SPI --- run `sudo raspi-config`, then select "Interface
    135     Options", "SPI"
    136 9.  Upload `frame-server-arm` from your local machine using `scp`; I
    137     have it living in `/home/pi/frame`
    138 10. Copy the [cron
    139     script](https://github.com/dce/e-paper-frame/blob/main/etc/random-photo)
    140     into `/etc/cron.hourly` and make sure it has execute permissions
    141     (then give it a run to pull in the initial photos)
    142 11. Add a line into the root user's crontab to run the script on
    143     startup: `@reboot /etc/cron.hourly/random-photo`
    144 12. Copy the [`systemd`
    145     service](https://github.com/dce/e-paper-frame/blob/main/etc/frame-server.service)
    146     into `/etc/systemd/system`, then enable and start it
    147 
    148 And that should be it. The photo gallery should be accessible at a local
    149 IP and the photo should update hourly (though not ON the hour as that's
    150 not how `cron.hourly` works for some reason).
    151 
    152 {{<dither IMG_0122.jpeg>}}Wooden DIY stand cradling a small circuit board with a ribbon cable looped through.{{</dither>}}
    153 
    154 ## Building the Frame
    155 
    156 This part is strictly optional, and there are lots of ways you can
    157 display your frame. I took (a lot of) inspiration from this ["DIY
    158 Modern Wood and Acrylic Photo
    159 Stand"](https://evanandkatelyn.com/2017/10/modern-wood-and-acrylic-photo-stand/)
    160 with just a few modifications:
    161 
    162 -   I used just one sheet of acrylic instead of two
    163 -   I used a couple small pieces of wood with a shallow groove to create
    164     a shelf for the screen to rest on
    165 -   I used a drill press to make a 3/4" hole in the middle of the board
    166     to run the cable through
    167 -   I didn't bother with the pocket holes --- wood glue is plenty
    168     strong
    169 
    170 The tools I used were: a table saw, a miter saw, a drill press, a
    171 regular cordless drill (**do not** try to make the larger holes in the
    172 acrylic with a drill press omfg), an orbital sander, and some 12"
    173 clamps. I'd recommend starting with some cheap pine before using nicer
    174 wood --- you'll probably screw something up the first time if you're
    175 anything like me.
    176 
    177 ---
    178 
    179 This project was a lot of fun. Each part was pretty simple --- I'm
    180 certainly no expert at AWS, Go programming, or woodworking --- but
    181 combined together they make something pretty special. Thanks for
    182 reading, and I hope this inspires you to make something for your mom or
    183 someone else special to you.
    184 
    185 *Raspberry Pi illustration courtesy of [Jonathan
    186 Rutheiser](https://commons.wikimedia.org/wiki/File:Raspberry_Pi_Vector_Illustration.svg)*