DEV Community

Cover image for Getting started with the Waveshare 2.7" ePaper HAT on Raspberry Pi
Rane Wallin
Rane Wallin

Posted on • Edited on

Getting started with the Waveshare 2.7" ePaper HAT on Raspberry Pi

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 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.

Waveshare has also provided schematics, example code, and libraries for working with the display. All of this can be found in the wiki. 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


Enter fullscreen mode Exit fullscreen mode

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.


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

import epd2in7b


Enter fullscreen mode Exit fullscreen mode

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



from PIL import Image, ImageDraw, ImageFont


Enter fullscreen mode Exit fullscreen mode

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.



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


Enter fullscreen mode Exit fullscreen mode

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.



def printToDisplay(string):


Enter fullscreen mode Exit fullscreen mode

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



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


Enter fullscreen mode Exit fullscreen mode

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.



    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


Enter fullscreen mode Exit fullscreen mode

The Bangers font is from Google Fonts. 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.



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


Enter fullscreen mode Exit fullscreen mode

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



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


Enter fullscreen mode Exit fullscreen mode

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.



printToDisplay("Hello, World!")


Enter fullscreen mode Exit fullscreen mode

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

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.



from gpiozero import Button


Enter fullscreen mode Exit fullscreen mode

The trickiest part about working with the buttons is figuring out which pin goes with which button. Waveshare offers a schematic. 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

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.



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


Enter fullscreen mode Exit fullscreen mode

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.



btn.when_pressed = handleBtnPress


Enter fullscreen mode Exit fullscreen mode

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.



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


Enter fullscreen mode Exit fullscreen mode

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

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.



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


Enter fullscreen mode Exit fullscreen mode

The complete code for the multi-button project is here

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 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 has a refresh time of less than 1 second, but is a bit more pricey at $74.99.

Top comments (20)

Collapse
 
sergeantpol profile image
sergeantpol

Hello,
I have wired 2.7inch e-Paper HAT to my Raspberry pi Zero W as it says here: waveshare.com/wiki/2.7inch_e-Paper... .
And is actually working.
I just wanted to ask if I can enable-activate the buttons without attaching it on my Rpi board but somehow wiring them with my Rpi board.
Best regards and thanks for your contribution
Kostas

Collapse
 
ranewallin profile image
Rane Wallin

I believe what you need for that is jumper. adafruit.com/product/1951

Collapse
 
sergeantpol profile image
sergeantpol

Yeah but which pins and which holes should i connect in order to use the buttons?

Thread Thread
 
ranewallin profile image
Rane Wallin

Oh, 5, 6, 13, 19. You start counting at the top left and then down and around in a U shape form

Thread Thread
 
sergeantpol profile image
sergeantpol • Edited

Yeah thanks! It's working but I have actually one more question.
When I am running your program when should I press the buttons because on the terminal it appears the image below dev-to-uploads.s3.amazonaws.com/i/...

Thread Thread
 
sergeantpol profile image
sergeantpol

and when the running ends there is nothing I can do...
Thank you, you are already so helpful!!!

Thread Thread
 
ranewallin profile image
Rane Wallin

It looks like it may be crashing. It prints "Clear..." when it's clearing the buffer. At that point the screen should start flashing. Do you have an ide you can run it where you might see more information? I think I used a Thonny.

Thread Thread
 
sergeantpol profile image
sergeantpol
Thread Thread
 
ranewallin profile image
Rane Wallin

Did the screen flash?

Thread Thread
 
sergeantpol profile image
sergeantpol

yes, according to
epd.init() # initialize the display
print("Clear...") # print message to console
epd.Clear(0xFF)

Thread Thread
 
ranewallin profile image
Rane Wallin

So the eink display flashes? After it clears the display it should put a message to the console. That message comes from the drivers. If that's not happening, check the waveshare repo to see if they have a new version.

Collapse
 
glanzi profile image
Giacomo Lanzi

I always got this error:

Traceback (most recent call last):
  File "/home/<user>/epaper/test.py", line 4, in <module>
    import epd2in7
  File "/home/<user>/epaper/lib/epd2in7.py", line 31, in <module>
    from . import epdconfig
ImportError: attempted relative import with no known parent package
Enter fullscreen mode Exit fullscreen mode

I tried to put files in lib folder, but I got the same result.
What I did wrong? Here is the file code:

 import sys
 sys.path.insert(1,"lib")

 import epd2in7

 from PIL import Image, ImageDraw, ImageFont
 epd=epd2in7.EPD()
 epd.init()
 epd.Clear(0xFF)
Enter fullscreen mode Exit fullscreen mode

Using "./lib" gave me error, and I figured out that in this way is working. At least is finding the files.

Collapse
 
gudongfeng profile image
DONGFENG GU

Nice post, the "Accessing the buttons" section really helps a lot. I wasn't able to find an "official" document for the buttons on waveshare website. But I got redirected to here through waveshare.com/wiki/2.7inch_e-Paper.... Thank for your contribution.

Collapse
 
powersoft profile image
powersoft

Hello,

I have tied to get the files "epd2in7b.py and epdconfig.py" but could not find them.
Please can you help me with these so I can download it.
Thanks in advance.

Jan Kromhout
Hellevoetsluis-NL

Collapse
 
ranewallin profile image
Rane Wallin

Hi, it looks like they moved some things around. Try the github repo: github.com/waveshare/e-Paper/tree/...

Collapse
 
pr0ffarnsworth profile image
Thomas

i like your code :) but cant figure out what display() does, can u pls upload the old epd2in7b.py and epdconfig.py? seems that waveshare removed it :(

Collapse
 
ravens28 profile image
Ravens28

Do you manage to still use the buttons after an epd.sleep()?

Because I wanted to use a btn.was_held to get out of sleep mode... But after getting into deep sleep the buttons doesn't work anymore...

Thx for your post it helped me a lot to start my project a few month ago!

Collapse
 
ujjwalagarwal profile image
Ujjwal Agarwal

Hey Rane! Was wondering if there was a way to run a processing sketch on the epaper. You think?

Collapse
 
ranewallin profile image
Rane Wallin

I'm not sure can you give me more information about what a processing sketch is?

Collapse
 
dmeylman profile image
dmeylman • Edited

Hello,
Sorry, newbie here. I'm trying to run this on my raspberry pi 3 and get an error:
AttributeError: 'EPD' object has no attribute 'Clear'

Please help. Thanks...