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)*