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.
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
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 tosys
so python knows where to find it. (Example code below) - Import
epd2in7b
into the project file. You do not need to importepdconfig
, as this is imported intoepd2in7b
already.
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).
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
.
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.
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).
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.
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. 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)
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))
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!")
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
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.
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
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
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!")
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
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)
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
I believe what you need for that is jumper. adafruit.com/product/1951
Yeah but which pins and which holes should i connect in order to use the buttons?
Oh, 5, 6, 13, 19. You start counting at the top left and then down and around in a U shape form
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/...
and when the running ends there is nothing I can do...
Thank you, you are already so helpful!!!
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.
Once again at Thonny
dev-to-uploads.s3.amazonaws.com/i/...
Did the screen flash?
yes, according to
epd.init() # initialize the display
print("Clear...") # print message to console
epd.Clear(0xFF)
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.
I always got this error:
I tried to put files in lib folder, but I got the same result.
What I did wrong? Here is the file code:
Using
"./lib"
gave me error, and I figured out that in this way is working. At least is finding the files.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.
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
Hi, it looks like they moved some things around. Try the github repo: github.com/waveshare/e-Paper/tree/...
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 :(
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!
Hey Rane! Was wondering if there was a way to run a processing sketch on the epaper. You think?
I'm not sure can you give me more information about what a processing sketch is?
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...