Raspberry PI Pico and e-paper (e-ink) Display
Raspberry PI Pico e-paper (e-ink) displays are output devices that make cool your project results and help to save a lot of power. Once configured with the content to show, they can keep the display images active even without the need for any power source!
In this tutorial, I will show you how to connect and use a Raspberry PI Pico e-paper (e-ink) display by using MicroPython.
How e-paper Displays Work
The technology behind e-paper displays is not really new. According to Wikipedia, the e-ink notion exists since the 1970s and the official E Ink company have been founded in 1997.
Leaving the history to the related Wikipedia link, the e-paper displays have a really ingenious working principle, that I will describe briefly.
A delimited, clear fluid space encloses a consistent number of particles with different colors (usually black and white). The black pigments are electrically attracted by positive poles, while the white particles are attracted by negative poles.
On the top and bottom of these fluid spaces, two controlled electrodes will create the positive or negative pole required to move on the top side (the side visible from the user) the white or black pigments, giving the output result according to the color moving to the top:
Each of these systems will compose a single pixel of the e-paper display, so allowing our image to be shown as a dense assemble of particles moving up or down.
What is important is that once the pigments have moved to the top (or down) side, they don’t need to get still polarized: they will keep the state until a different polarization will move them into a different position.
What We Need
As usual, I suggest adding from now to your favourite e-commerce shopping cart all the needed hardware, so that at the end you will be able to evaluate overall costs and decide if continue with the project or remove them from the shopping cart. So, hardware will be only:
- A common computer (maybe with Windows, Linux or Mac). It can also be a Raspberry PI Computer board
- Raspberry PI Pico microcontroller (with a common micro USB cable)
- e-paper module (the mine is from SunFounder shop)
Wiring diagram for e-paper Raspberry PI Pico
Please arrange the Raspberry PI Pico e-paper display connections according to the following diagram, according to the Raspberry PI Pico pinout:
|Raspberry PI Pico||e-paper|
Please find below some pictures from my connection, where I had a 8-wires cable allowing me to connect the e-paper display directly to my Raspberry PI Pico
Check the Epaper Hardware Version
For people having the Waveshare Raspberry PI Pico Epaper display, it’s really important to check your display hardware version in order to get it working properly. I found that mine is the V2, as you can see from the label on my display backside:
Install MicroPython on Raspberry PI Pico
Connect RPI Pico to Thonny. For the very first steps and to install MicroPython, you can refer to my tutorial about the First steps with Raspberry PI Pico.
Get the Epaper MicroPython Library
Download the proper library from Waveshare library: https://github.com/waveshare/Pico_ePaper_Code/tree/main/python
A copy of the Raspberry PI Pico V2 e-paper display library with MicroPython is also available from my download area, with the following link:
Download the library code (for my download area or the raw code from the Waveshare GitHub page) and save it in your RPI Pico Storage naming it “pico_epaper”. As it includes a few tests at the end, you can test the raw library by running it with Thonny in order to double-check if the downloaded library is the right for your hardware.
Please note that the library file must be saved in your Raspberry PI Pico root folder or in your library folders according to my Adding external modules to MicroPython with Raspberry PI Pico tutorial.
The MicroPython Framebuffer module
In order to correctly work with the display, you need to know that its library uses the MicroPython Framebuffer module.
In a few words, it means that every drawing command you submit to the display will be converted into a byte array and sent to the display with the “epd.display()” (or similar partial display) command.
When you send a new drawing command, it will be merged over the old byte array buffer, so resulting in a new image coming with the old buffer plus the new drawing.
It’s really important also for the cleaning phase of your display, as the default “Clear” command results in the display becoming white, but a new drawing will bring back all the old pixels. The right way to get your display to show fresh images is to fill all the display with the white color or fill a specific area with white.
We’ll see it better in the following examples.
The e-paper Display Axes
As you will see in the following examples, the e-paper display usually refers to a 2-axes coordinate in order to address the position where objects are positioned. I will refer in my examples to X-Axis and Y-axis that match what is shown in the following picture:
Create your Custom MicroPython code for Raspberry PI Pico e-paper Display
For the following examples, you can both write the lines in your Thonny IDE or get the file with all the examples from my download area with this link:
Create a new empty file in your Raspberry PI Pico storage. It will include our custom code.
Identify the main class in the e-paper downloaded library. For my V2 display, I have only one class named “EPD_2in13”, while you could have different classes for different hardware (sometimes a landscape class and a portrait class), depending on your library. It will impact only the first 2 lines of this custom code
Call the class from the first line of your empty code with the MicroPython import function, by using the library file name (without the “.py” extension) and the class name.
from pico_epaper import EPD_2in13
We needed to remove the minus (“-“) and dots characters from the library name as they will result in MicroPython errors as these chars are not allowed on library names.
We can now initialize the epd (E-Paped Display):
epd = EPD_2in13() epd.init(epd.full_update)
Some newest hardware should also support the partial update, but this didn’t work with my hardware… So I will give you in the examples a way to get only part of the screen updated.
I also suggest defining two variables with color names in order to keep our code simpler:
If you are here now, the hardest job has been performed and now the funny things become with usage examples.
Raspberry PI Pico E-paper Drawing Examples
In the following examples, I will assume that you already run the previous commands and that your Raspberry PI Pico session is active. I also suppose that you will fill the screen with white color before each of the following examples, as shown in the first example.
Fill the entire display
You can fill the entire screen with a specified color by using the fill property and then updating the display. This is a common operation at the beginning of every script, as it allows us to start with a blank screen. You can fill it both with white:
The two results are shown in the following picture:
Draw a Pixel
To draw a pixel, we can use the “pixel” method. Let’s mark a pixel at column 5 (X-axis) and row 20 (Y-axis). As we’re going to draw a black pixel, I’ll start with a screen filled with white. The command will be:
And the result in our Raspberry PI Pico e-paper display:
Draw a vertical line
The Framebuffer allows a simple method to draw a vertical line: the vline(). The MicroPython command will be the following:
epd.vline(10, 90, 60, black) epd.display(epd.buffer)
Starting from column 10 (X-axis) and row 90 (Y-axis), this command will draw a 60-pixel vertical line:
Drawing a horizontal line
Similarly, the hline() will make it possible for us to draw a horizontal line starting from a specified display pixel. The command:
epd.hline(10, 90, 80, black) epd.display(epd.buffer)
Will create a line starting from column 10 (X-axis) and row 90 (Y-axis), large 80 pixels:
Draw a diagonal line
The MicroPython Framebuffer library also allows us to draw a line with specified coordinates for both start and end of the line itself. For example, the command:
epd.line(10, 90, 90, 150, black) epd.display(epd.buffer)
will draw a line starting from the pixel at 10 (X-axis) / 90 (Y-axis) till the pixel 90 (X-axis) / 150 (Y-axis):
In the image, the line appears to be pixeled as of the high resolution I used. When You will see it live, you will check that the pixelled effect is far less annoying.
Rectangles can be drawn both empty (with the lines defining edges) or filled with a specified color. Both require the starting position and the sizes on X-axis / Y-axis of the rectangle, with the color.
The rect() method draws an empty rectangle. You can define a black empty rectangle starting from the pixel at 10 (X-axis) / 180 (Y-axis), large 50 pixels and tall 40 pixels with the following:
epd.rect(10, 180, 50, 40, black) epd.display(epd.buffer)
The fill_rect() uses the same inputs to define a filled rectangle:
epd.fill_rect(70, 180, 50, 40, black) epd.display(epd.buffer)
The following picture shows our results:
Displaying a Text
Also for this task, the Framebuffer library makes the job for us. Writing whatever text to your display will require just telling our Raspberry PI Pico e-paper display the string to show, the starting position and the color:
epd.text("peppe8o.com", 0, 10, black) epd.display(epd.buffer)
Please note that each character of our text will be drawn within an imaginary box of 8×8 pixels. This is useful to know as it allows us to define the position for different text lines. Usually, different lines keep a 2-pixel distance in order to avoid getting them too close. This means that if you write a text line from row 10, you should write the following line from row 20 = 10 (the previous line position) + 8 (the character height of every single line) + 2 (the spacing between different lines).
Back to our example, this will be the result:
Updating the text
What we learned in the previous examples is also useful when trying to update your displayed text. In fact, if you try to write a new text over the existing one, you will get an overlapping of the two prints, resulting in a wrong display output. For example, let’s try changing the previous text with an upper case for the first char (the first “p” letter). Without cleaning the screen, the command sequence:
epd.text("peppe8o.com", 0, 10, black) epd.display(epd.buffer) epd.text("Peppe8o", 0, 10, black) epd.display(epd.buffer)
will finally result in the following output, where you can check that the first letter is coming from the overlapping of the previous text with the new one:
In order to avoid this problem, if the partial update doesn’t work with your hardware (as in mine one), you can use a white-filled rectangle starting at the first char position and with height and width given from your line height (8 pixels, as said) and your text lenght multiplied by 8 pixels.
For example, I want to update the “peppe8o.com”, 11 chars = (11×8) 88 pixels width, to “Giuseppe”, I can use the following steps:
epd.text("peppe8o.com", 0, 10, black) epd.display(epd.buffer) epd.fill_rect(0, 10, 88, 8, white) epd.text("Giuseppe", 0, 10, black) epd.display(epd.buffer)
That will show correctly the update of our e-paper display section, with the final result:
Composing the drawings
Even if the previous examples may appear so simple, we can already perform complex drawings by putting them together into a combined picture. The following example combines different forms to get the MicroPython logo printed in our screen:
epd.fill_rect(0, 1, 32, 32, black) epd.fill_rect(2, 3, 28, 28, white) epd.vline(9, 9, 22, black) epd.vline(16, 3, 22, black) epd.vline(23, 9, 22, black) epd.fill_rect(26, 25, 2, 4, black) epd.text('peppe8o.com', 35, 1, black) epd.text('e-paper', 35, 13, black) epd.text('Pico', 35, 25, black) epd.display(epd.buffer)
and gives the result shown in the following picture:
Draw a Picture from a File
The Framebuffer library offers also a great method allowing us to display whatever picture or image from a file. This is a bit more complex to perform as it requires image manipulation before showing it in our Raspberry PI Pico e-paper display, as already performed in my Add an OLED display to Raspberry PI Pico with MicroPython tutorial.
Briefly, you must scale the picture you want to show to fit your display size, convert it to a black/white image and export it as PBM file. All these operations can be performed with the great open-source GIMP.
For this example, I will convert my logo (a 512×512 jpg image):
With GIMP, open your file with GIMP and resize it to the desired size (limited by your e-paper display. I will resize my logo to 60×61 with the GIMP function available from the Image menu -> Scale Image. The result is the following:
When there are parts with light colors, they may not appear correctly in your display (that is Black/White). So, we can get a better final result by managing the color curves to get an increased contrast (a darker picture): in GIMP, Colors -> Curves and move the curve to the lower part to get a higher contrast. The following is the result:
Now, we’ll use the GIMP function Image -> Mode -> Indexed function to get an indexed image color with black/white palette. The following are the settings to use:
And this will be the result:
In order to make it work with the e-paper display and differently from the OLED display, I also had to work with an inverted logic of the color. So, the final job from GIMP is to invert the black/white with Colors -> Invert menu from GIMP. The result is the following:
Now you can export this image to the PBM format with GIMP File -> Export by setting the correct file extension.
Once you have the “.pbm” image uploaded to your Raspberry PI Pico storage, you can use a custom function that I’ve coded to make the job simpler. At the start of your script, you have to import the MicroPython framebuf library and use the following custom function:
import framebuf def pbm_draw(x_pos,y_pos,file): with open(file, 'rb') as f: f.readline() f.readline() size = f.readline().decode('utf-8') (x,y)=size.split("\n").split(" ") data = bytearray(f.read()) fbuf = framebuf.FrameBuffer(data, int(x), int(y), framebuf.MONO_HLSB) epd.blit(fbuf, x_pos, y_pos, white)
In this custom function, after opening the pbm file we ignore the first 2 rows (with the readline commands) as they include some general information about the file and the program that generated it. The third line includes the file size, which is read and translated from a byte record into numbers. Then, the bytearray() function reads all the bits from the image, finally stored into a FrameBuffer object. The blit() method prepares the display buffer. Please note that the blit() function gets finally the “white” color input to get the image color inversion managed.
With this custom function, you can draw your picture with the following command:
And the result is:
Scrolling the display
The Raspberry PI Pico e-paper display technically supports also the scroll of the screen. I’m specifying “technically” as the result get (at least for my hardware/library) shows part of the previous image that hurts the final result, so I would suggest you to test with your hardware in order to check if it may work with different versions. Starting from the previous logo image still in place, the following command:
will generate a scroll of 0 pixels in the horizontal line and 50 pixels in the vertical line, with the following result:
Cleaning the Display
As already mentioned, the clear() method will make your display empty:
This will clear your display, but just calling the epd.display() will show back the old screen.
So, the only way to get your Raspberry PI Pico e-paper display really clear is to fill it with white color.
Sleeping the Display
The last method is the sleep(). This function allows our display to go sleeping:
It is important to know that, even this function hasn’t a great impact on our power budget because the e-paper display doesn’t have a big power consumption, the producer suggests using it to avoid the display remaining in a high voltage state for a long time, so risking damages. E-paper displays don’t need to get powered to show their content (even after the sleep function, you will continue to see your content). You have to mind only that after the sleep method you will need a new “epd.init(epd.full_update)” command to get back control of your display.
Moreover, even after completely powering off your Raspberry PI Pico you will get your images still printed on your display!
Interested to do more with your Raspberry PI Pico? Try to look at my Raspberry PI Pico tutorials for useful and funny projects!
We are sorry that this post was not useful for you!
Let us improve this post!
Tell us how we can improve this post?