Getting started with the Waveshare 2.7" ePaper HAT on Raspberry Pi
Tags: raspberrypi, eink, python, tutorial
Published: 2019-08-30 18:53:31

---
title: Getting started with the Waveshare 2.7" ePaper HAT on Raspberry Pi
published: true
description: An article discussing how to install the Waveshare 2.7" ePaper HAT on a Raspberry Pi and write your first python program.
tags: raspberrypi, eInk, python, tutorial
cover_image: https://thepracticaldev.s3.amazonaws.com/i/k5ahyvraidfvxv6qu72f.jpg
---
## Introduction

I have been curious about the Raspberry Pi for a long time, but I only started working with one recently when it was provided to me for a class I have this semester. The model I received is the Raspberry Pi 3 B+. RaspberryPi.org describes it as:

> The Raspberry Pi 3 Model B+ is the latest product in the Raspberry Pi 3 range, boasting a 64-bit quad core processor running at 1.4 GHz, dual-band 2.4 GHz and 5 GHz wireless LAN, Bluetooth 4.2/BLE, faster Ethernet, and PoE capability via a separate PoE HAT

As soon as I received it I started looking at the various accessories and add-ons available. The selection of available displays caught my eye, and I decided I wanted to play with an eInk display. There are numerous eInk displays available for the Raspberry Pi. For my purposes, I chose the cheapest one I could find that didn't have terrible reviews.

## The eInk display
Waveshare Electronics offers a wide variety of add-ons for the Raspberry Pi, as well as other electronics. I was drawn to the [2.7" ePaper HAT](https://www.waveshare.com/2.7inch-e-Paper-HAT-B.htm) because it was relatively cheap and offered plug and play compatibility. The version I bought is version B, which is the three-color black, white, and red display.

The display is only $19.99 and was easy to install on my Raspberry Pi. It does have one big drawback, though. The refresh takes 15 seconds, which is a very long time. It also needs a full refresh between screens, so that is 15 seconds every time the image changes in any way. Waveshare also offers a black and white version for the same price that takes 6 seconds to refresh.

## Initial setup
Installing the display is very straight forward. Just line up the female header (black lego-looking thing) on the HAT with the male header (black spiky-looking thing) on the RPi. I find the easiest way to get it to connect is to pushed down on the two white circles with my thumbs once it is lined up. It should slide right on. No additional connections are required. The HAT comes with an SPI interface, but this is only needed if you are not connecting using the GPIO header.

![On the left, the eInk HAT showing the female header. On the right, the Raspberry Pi showing the male header.](https://thepracticaldev.s3.amazonaws.com/i/bkx2lyk56qy0c2a6lk0r.png)

Waveshare has also provided schematics, example code, and libraries for working with the display. All of this can be found in the [wiki](https://www.waveshare.com/wiki/2.7inch_e-Paper_HAT_(B)). It's important to note that the drivers and libraries are different for each display. The code in the wiki above only works with the 2.7" 3-color display. If you are working with a different display, the code will be similar but not the same.

## Drawing to the screen
To get started, make sure you are connected to your Raspberry Pi, either directly connected with keyboard, mouse, and monitor or remoting into the RPi using VNC (or similar service). The ePaper display does not interfere with the regular HDMI input, so you can connect a monitor to it while working with the ePaper display without any issues.

You may also need to install the libraries below if they are not already on your Pi (they probably are).

```
sudo apt-get install python3-rpi.gpio python-imaging python-smbus python-dev
```

Create a folder for your ePaper projects. I put mine in `~/pi/epaper/`, but you can put yours anywhere you want. Waveshare provides library files in C, python2, and python3. For this project, I am working with python3. You will need two files from the example code provided by Waveshare for any projects you do; they are `epd2in7b.py` and `epdconfig.py`. We will call these the drivers.

You can put a copy of the drivers in every project you make, or you can put them into a dedicated folder and import them into your project from there. I chose the latter. This was my first python project, so it took me a minute to figure out how to make that work, but it's actually pretty simple:
* Create a folder in your project directory called `lib`
* Place copies of both files into that folder
* In any project file that needs the drivers, import `sys` and then append the lib folder to `sys` so python knows where to find it. (Example code below)
* Import `epd2in7b` into the project file. You do not need to import `epdconfig`, as this is imported into `epd2in7b` already.

```python
import sys
sys.path.insert(1, "./lib") # Adds lib folder in this directory to sys

import epd2in7b
```

To draw an image to the screen, we also need `Image`, `ImageDraw`, and `ImageFont` from the Python Image Library (PIL).

```python
from PIL import Image, ImageDraw, ImageFont
```

Before we can work with the display, we need to create a variable for it and initialize the display. We also want to clear it before doing anything else. With the 2.7" display, we need to pass a color in hex to the `Clear` function. Even though `Clear` requires a color, I have not seen any difference from passing in different values, so just stick with `0xFF`.

```python
epd = epd2in7b.EPD() # get the display
epd.init()           # initialize the display
print("Clear...")    # prints to console, not the display, for debugging
epd.Clear(0xFF)      # clear the display
```

This is now a fully functioning program! If you run it, you should see the display turn black and then flash several times before clearing to "white," which is actually a greyish color similar to a book page.

To print to the display, we are going to create a function that accepts a string.

```python
def printToDisplay(string):
```

This version of the display has both red and black pixels. Each color is rendered as a separate layer, the black is rendered first and then the red. When we create anything on the red layer, we still use _black and white_ for our colors on that layer, but it will render the black pixels in red.
 
We have to create a separate image for red and black, even if we are only printing in one color. To do this, for each color we call `Image.new()` and pass in the height and width of the display and a color to use for the display, which in this case is 255 (white).

```python
    HBlackImage = Image.new('1', (epd2in7b.EPD_HEIGHT, epd2in7b.EPD_WIDTH), 255)
    HRedImage = Image.new('1', (epd2in7b.EPD_HEIGHT, epd2in7b.EPD_WIDTH), 255)
```

Passing in the height first, and then the width, for the display dimensions, as above, will cause the text to display horizontally. To display  the text vertically just swap the heights and widths in the above statements.

For this demo we are just printing text to the screen. We first need to get a draw object and then set our font. We will only be drawing to the black layer. The red layer will stay blank.

```python
    draw = ImageDraw.Draw(HBlackImage) # Create draw object and pass in the image layer we want to work with (HBlackImage)
    font = ImageFont.truetype('/usr/share/fonts/truetype/google/Bangers-Regular.ttf', 30) # Create our font, passing in the font file and font size
```

The Bangers font is from [Google Fonts](https://fonts.google.com/). If you don't have this font and don't want to install it, you can use any other font you like.

Once we create the draw and font objects, we can use them to create our text. The first argument is the starting position of the text in pixels, then the string, the font, and the fill.

```python
   draw.text((25, 65), string, font = font, fill = 0)
```

We need to add our images to the screen. We use `display()` and `getbuffer()` from the `epd2in7b` drivers to do this.

```python
    epd.display(epd.getbuffer(HBlackImage), epd.getbuffer(HRedImage))
```

Even though we didn't do anything with the red layer, we still have to pass it to the `display()` function.

Finally, to see what we have done, we need to invoke our `printToDisplay()` function.

```python
printToDisplay("Hello, World!")
```
Now save and run the program. It will clear the screen first, then after a couple of seconds the screen will start flashing again to place the text. If you watch your console, you will see that the screen remains busy a few seconds after it is cleared. Once the screen is released, the text starts to render. 

[Here is a link to this complete program](https://gist.github.com/RaneWallin/3d8645d06aed9251eed8c9079314807f)

Awesome! You can write to your display. But what about those buttons? Let's talk about that.

## Accessing the buttons

For this part of the project we will use the `gpiozero` library provided by RaspberryPi.org to access our buttons and use them to interact with the program. Start by importing the Button module from the `gpiozero` library. 

```python
from gpiozero import Button
```

The trickiest part about working with the buttons is figuring out which pin goes with which button. Waveshare offers a [schematic](https://www.waveshare.com/wiki/File:2.7inch_e-Paper_HAT_Schematic.pdf). Since this was my first time working with RPi, python, and schematics, it took me a bit of time to work out what it was telling me. I'll save you some trouble and tell you right now, the corresponding pins, from top to bottom are 5, 6, 13, 19. 

 ![Buttons on the 2.7" ePaper HAT labeled top to bottom: 5, 6, 13, 19](https://thepracticaldev.s3.amazonaws.com/i/altsbd45hhbgg7ms3o1p.png)

To use the button, first assign it to a variable. You can use the `Button()` function imported from `gpiozero` and pass in the pin number from above. For this demo, we are only working with the first button, at pin 5.This assignment will be near the top of the file right below the imports.

```python
    btn = Button(5) # Assign btn to the button on the HAT at pin 5
```

To perform an action when the button is pressed, we will assign a function to the when_pressed property on the button. This should happen at the bottom of the file.

```python
btn.when_pressed = handleBtnPress
```

Now we need to make our `handleBtnPress()` function. We will do this directly above the previous assignment and we will move the line where we invoked `printToDisplay()` into that function.

```python
def handleBtnPress():
    printToDisplay("Hello, World!")
```

To test your program, run it and wait for the console to show that the display is not busy. It will show the message `e-paper busy release`. Once that is done, press the first button. If everything is correct, it should update to display "Hello, World!"

[A complete version of the updated program is here](https://gist.github.com/RaneWallin/339e3644018d11ef9d6e33960059c433)

## Putting it all together

Now you can print to the display and use a button to cause the display to update. But, there are four buttons! /picard

If you want each button to do a different thing, you have some options. You can create functions for each button and assign those functions to when_pressed as above or you could even have a single function that changes the behavior depending on which button is pressed. To do this, look for the pin number of the button and use a `switcher` to decide what to do. The `when_pressed` function passes the button as an argument automatically. To get the pin number, you need to access `btn.pin.number`. 

*Don't forget, you need to wait for the display to release before pressing the next button.*

See the example below.

```python
btn1 = Button(5)
btn2 = Button(6)
btn3 = Button(13)
btn4 = Button(19)

def handleBtnPress(btn):
    pinNum = btn.pin.number
    switcher = {
        5: "Hello, World!",
        6: "This is my first \nRPi project.",
        13: "Hope you lik it.",
        19: "Goodbye!"
    }
    msg = switcher.get(pinNum, "Error")
    printToDisplay(msg)

btn1.when_pressed = handleBtnPress
btn2.when_pressed = handleBtnPress
btn3.when_pressed = handleBtnPress
btn4.when_pressed = handleBtnPress
```

[The complete code for the multi-button project is here](https://gist.github.com/RaneWallin/fd73ddbffdabea23358f722adb9f4075)

{% youtube rJBt09Y7BkE %}

## Final thoughts

This was my first Raspberry Pi project and my first time working with python. It was a lot of fun, but it was also hard to find resources for working with the ePaper display. Even though Waveshare has a lot of information, they don't have any tutorials or real documentation around working with the display.

The long refresh time matters a lot. It's fun to play with, but it would be too frustrating to use for any real projects. After some extra research, I believe that [2.9" flexible eInk HAT](https://www.waveshare.com/2.9inch-e-Paper-HAT-D.htm) from Waveshare would have been a better option. It is a 2-color display and has a refresh time of 2 seconds. This one is only a few dollars more at $23.99. For a more production-ready version, the [800x600 6" display](https://www.waveshare.com/6inch-e-Paper-HAT.htm) has a refresh time of less than 1 second, but is a bit more pricey at $74.99.

{% gist https://gist.github.com/RaneWallin/fd73ddbffdabea23358f722adb9f4075 %}