I’d been curious to experiment with E-paper displays for a long time, and recently I finally got around to some basic experimentation. This short blog post documents some work with a compact 2.9 inch display, building it into a simple clock.
Check out the short 2-minute video demonstration:
I’ve never owned an Amazon Kindle (I use the Kindle software on my tablet – but I’m seriously considering purchasing an Amazon Kindle now!), and this was my first time seeing an e-paper display up close. I was extremely impressed at the image quality; it really does look like real inking on a paper-like background. The particular display I used has quite good contrast and fairly good resolution (296x128 pixels), and it can be readable close-up or from a distance.
This display technology is not always appropriate (the displayed content cannot be modified as rapidly as with other display technologies) but it has other benefits. Images are retained even when power is removed. This means that for information and displays that need infrequent updates, power consumption can be extremely low because the display can have the power removed, and even the attached microcontroller could be powered down! It makes it ideal for some battery powered applications. Also, the display is very readable in bright outdoor conditions, unlike some other display technologies.
How does it work?
The technology is quite interesting. Unlike liquid crystal displays (LCD) which rely on polarizing light, the E-paper displays rely on simple reflection off coloured particles, much like light bouncing off real ink on paper. There are two types of coloured particles inside the display, and they are made to move close to the top or the bottom surface of the display under electronic control. When the white particles are closer to the surface, then the display appears white, and vice-versa.
There are actually many different topologies; some rely on the fluid and the particles being contained in many microcapsules for instance. Another topology relies on just single colour particles (instead of two different particle types) and a dyed fluid. There is also complexity in the way the display is driven. The quality of the display is determined by factors such as how white/black the image can appear, and how long the image can be retained when power is removed, and if there is any residual image when the image is changed. Each coloured particle inside the display is tiny (of the order of micrometers in diameter) and as a result many different forces can significantly impact the position of the particle. These forces can be related to charge between particles, or forces between the particle and the fluid, and forces between the particle and the electrodes. The way the display is driven electrically, aims to get the particles into as stable a state as possible with no force moving them from the desired position after the power is removed. As a result, it can be possible for the display to retain the image accurately for a long time (in some cases for years!).
When it is desired for the display to update, there are some interesting steps that occur under electronic control. It is known as the ‘driving waveform’ for the physical display. The particles may have no charge on them, so initially an AC waveform can be applied (the topologies vary, but as an example the top electrode will be set to 0V and the lower pixel electrodes will have a voltage of around +/-15V applied) in order to cause the particles to rub and collide, to create a charge on them. Then, a small delay and positive or negative charge on the electrode can be used to try to get the particles into an expected position, for maximum contrast and reduced residual image. Then, a charge is applied to make the white or black particles move toward the surface of the display. The waveform is complex enough and depends on batches of the display, expected temperature and so on. The manufacturer of the display will supply detail of the waveform for the batch, and the driver chip can then be programmed with this information. Without this information, the quality of the displayed image will be poor.
The particular e-paper display that I used has the driver chip embedded inside. The display and driver chip are mounted on top of a PCB; the rear of the PCB has voltage regulators to generate the appropriate voltages for the driver chip and the electrodes. The control interface is serial peripheral interface (SPI) based which means it is compatible with any microcontroller with little effort. The whole thing operates from 3.3V.
This was super straightforward because the display comes with the necessary jumper wires. All that was needed was to connect it to the microcontroller. I decided to use the board which can be coded much like an Arduino, using C/C++.
The board comes with no header pins attached so these were soldered on. Next, the display was attached using the jumper wires as shown here. That’s it! Nothing else is required to make use of the display. For the example clock application (described below) I also attached three buttons as shown in the diagram.
In a nutshell the wires are used to communicate to the display using the SPI interface. Some additional wires are used to reset the display, and select between command instructions and display data. There is also a busy wire, which indicates to the microcontroller when the display is still busy processing an instruction.
The software consists of just three files; a main.cpp file, and the file that contains the display driver, called ep29.cpp, and the header file for it called ep29.h. The code is essentially a re-write of the code that the display module supplier (Waveshare) supplies for their display. I wanted to re-write it in order to understand it and to not implement some unnecessary bits and pieces, and add some functionality. The bits I did not implement include things like fixed (bitmapped) fonts. The functionality I added includes a vector font instead, which is a bit nicer for some use-cases, and the ability to draw lines with different thicknesses as opposed to just single pixel wide lines. The font used is known as a Hershey font. I also made the driver default to a landscape orientation because the original waveshare source code assumes a vertical portrait orientation which seemed awkward to use in projects.
For a detailed step-by-step guide on using the board and the mbed compiler, see the getting started guide Working with FRDM Boards and ARM mbed . It will guide through the steps of how to log onto the online mbed environment and enter the code (i.e. copy the contents of each file from these locations: main.cpp, ep29.cpp and ep29.h by using Ctrl-A to select the entire code and Ctrl-C to select it, and paste it into the online mbed compiler. To do that, in the mbed compiler, select New->File give it the same filename and then when the blank editor window appears paste in the copied code using Ctrl-V) The guide shows how to compile the code and transfer the finished result to the board using a USB cable.
The driver code exposes some functions that you can use in your main function. These are the minimal steps to print ‘Hello world’ to the display:
unsigned char fbuf[EP_FRAMEMEMSIZE]; // create a frame buffer ep_spi_init(); // initialize the microcontroller SPI interface ep_init(full_mode); // initialize the E-paper ep_setbg(fbuf, ALL_WHITE); // set the frame to a white paper ep_write_text(fbuf, “Hello world”, 0,10, COL_BLACK, 1.0,1.0, NULL, PEN_THIN); ep_setframe(fbuf, 0,0, EP_WIDTH, EP_HEIGHT); ep_displayframe();
The first line defines storage for the frame of information that is to be displayed on the screen. The microcontroller needs to have enough RAM for this(4736 bytes). Fortunately the board has plenty of RAM(16k).
Line 3 initializes the e-paper, using an array called ‘full_mode’. It is actually a table of values which defines the waveforms used to control the display, as mentioned in the introduction. The waveform table can be modified but the ‘full_mode’ array provides best quality.
The next line is used to fill the entire buffer with 0xff, which corresponds to a white background.
Line 5 is used to populate the buffer with some text. In this case, the text is to be drawn from location (0,10), in black, and scaled to a size of (1.0,1.0) and drawn with a thin pen. A scale of (1.0,1.0) allows for about five rows of 16 characters to fit on the screen. If more characters need to be fitted, the scale could be reduced to (say) (0.5,1.0) to allow five rows of 32 characters and so on. These values are approximate because to improve readability, the font is not monospaced (monospacing is optionally possible by individually positioning characters if desired). For very large characters, the pen size could be increased to PEN_MED or PEN_THICK settings.
The last two lines are used to program the display with the frame buffer contents.
As an example application, I decided to implement a clock. The design doesn’t use an external real time clock (RTC) chip or back-up battery. As a result, if the power is disconnected, the clock time will need to be set again. An improvement to this project would be to add this functionality. I was only interested in getting something running with the e-paper for now. It still works very well, albeit with that caveat that the time is lost when the power is removed.
To create the clock, a timer is implemented to increment a seconds variable every second. Every 60 seconds, the e-paper display is updated. The conversion from seconds to a real time in hours and minutes is achieved using the same system that a UNIX computer uses; the mbed libraries offer this functionality. To populate a string buffer array with the time, one just needs this single line of code:
strftime(timebuf, strlen(timebuf), "%H:%M", localtime(&seconds));
You can see in the main.cpp file that this buffer is then written into the frame buffer using ep_write_text and sent to the display as discussed earlier.
Setting the time is achieved using three push-buttons labelled SET, DOWN and UP. It poses an interesting problem, how to implement a responsive user interface when the e-paper refresh time could take a couple of seconds. The design that was implemented allows the user to continue pressing buttons as fast as desired, and updates the display once the user has stopped pressing buttons for a second. So, if the user is adjusting the hours value and presses the UP or DOWN button repeatedly (say) five times quickly, then the display won’t update until the user stops and waits a second, and the displayed value will reflect that the hour value has been modified by five.
It actually feels more intuitive than it sounds. To set the time, the SET button is pressed and the display changes to ‘Set Hour:’. Then the DOWN/UP buttons can be pressed to set the hour, and then the SET button is pressed to advance to the ‘Set Min:’ display. Similarly, the year, month and date are set, and finally when SET is pressed again, the display goes back to showing the time.
The code implements a very simplistic button debounce which inhibits the acknowledgement of bouncing contacts for a fixed time period. It is more than adequate for the clock application.
Porting to other microcontrollers
The e-paper driver code was deliberately written in C and (unlike the original waveshare code) does not make use of C++, in order to provide for very easy porting to other microcontrollers which might not have a C++ development environment. Any microcontroller can be used provided it has three general purpose output pins, one input pin, and a SPI interface. The file ep29.cpp has the definitions for each pin and they can be modified. For example, the #define EP_RST_LOW is defined to set the pin connected to the RST (reset) line on the display low, and it can be modified to suit the microcontroller and pin that is chosen. The SPI interaction occurs in just two functions, called ep_cmd and ep_data, so it would be easy to modify too. The code could be ported to single board computers like the BeagleBone Black or Raspberry Pi too.
It is extremely easy to get going with the e-paper display described here Just eight wires need plugging into a board, and the code is straightforward. Vector based fonts were explored. As next steps I’d like to modify the clock code to display a moon phase indication. Another cool thing to do would be to put the microcontroller and some buttons onto a custom PCB, to build the clock into a more usable form-factor and enclosure. The clock project was quite basic so far just to gain some familiarity with the e-paper display, but it works well enough that I’m considering making a PCB for it and perhaps building a couple of them for family members.
More useful applications and additional display technologies will hopefully be explored in future.