New library for epaper-like (bistable) graphical displays

Hi all,
I just released the first version of a display library for 'epaper-like' bistable displays (displays that retain their last image even without power). This initial version supports the 128x32 cholesteric displays from Kent Displays, Inc. This is the display used in the 2010 Defcon badge. Other displays and display types (e-ink??) to be added as they become commercially available to hobbyists and/or I get my hands on one :wink:

Library download with fonts + example sketch
Library documentation
Schematics/PCB (EAGLE 5) and gerbers for a 128x32 display breakout board.

The font and graphics formats are compatible with GLCD.

Action shot:

Well done, thanks for sharing,

A quick look at your library documentation triggered the following thoughts:

on() off()
Because the display is bistable the library should hide the on() and off() functions. Let the library call them when needed, why bother the user with it. In other words, let the library do the energy management! OK with external power this would mean overhead but I guess the complexity of both functions are minimal.

set_temp_comp(int temperature)
Could a breakout board be build with sensor to do this automagically?

pixmap(int x, int y, const char * pImg)
How it knows the size? or is it full screeen?

commit()
Is it possible to read out the display to see the commit() is done? Or is commit async?

Missing functions;
clearEndOfLine(x,y); to partially clear the (text) display, not a must but a nice to have.

setPixel(x,y) , getPixel(x,y)
Is this possible?

Rob

robtillaart:
on() off()
Because the display is bistable the library should hide the on() and off() functions. Let the library call them when needed, why bother the user with it. In other words, let the library do the energy management! OK with external power this would mean overhead but I guess the complexity of both functions are minimal.

Good idea. It's possible that a user will want to control that themselves (e.g. back-to-back commit()s of small image areas; the "big brother" of this display is 132x64), but probably unlikely. Speaking of power management, an #ifdef for the PinChangeInt library (if available) to sleep until the display update is finished would reduce power usage quite a bit. I was in kind of a 'get er done' mood for this first release :wink:

set_temp_comp(int temperature)
Could a breakout board be build with sensor to do this automagically?

Maybe! The breakout board linked above actually has pads for an optional thermistor and cap that could be read by measuring the time constant across them, but I haven't tested this yet. The temp comp table from the manufacturer only has steps of 5degC anyway, so it need not be too accurate.

pixmap(int x, int y, const char * pImg)
How it knows the size? or is it full screeen?

It's the same format as a GLCD image - the width and height are encoded as the first 2 bytes of the image.

commit()
Is it possible to read out the display to see the commit() is done? Or is commit async?

There is a BUSY pin that can be polled (or tied to an interrupt) to see if the display charge pump has finished pumping (about 300ms) and again to see when commit is done. Handling this in a smarter way (the current version just polls for it) is definitely on my todo list :slight_smile:

Missing functions;
clearEndOfLine(x,y); to partially clear the (text) display, not a must but a nice to have.

Good idea.

setPixel(x,y) , getPixel(x,y)
Is this possible?

Unfortunately not, on this display at least. Several limitations currently in the library (e.g. text and images aligning on 8-pixel vertical page boundaries) stems from the fact that the display RAM is not readable on this display. This makes a read-modify-write operation impossible without maintaining a local framebuffer (something I steered clear of for now in interest of memory footprint, although it may be an option in the future).

Thanks for your response, there is good work in progress !

maintaining a local framebuffer (something I steered clear of for now in interest of memory footprint, although it may be an option in the future).

maybe an #ifdef MEGA option ?

Rob

robtillaart:
Thanks for your response, there is good work in progress !

maintaining a local framebuffer (something I steered clear of for now in interest of memory footprint, although it may be an option in the future).

maybe an #ifdef MEGA option ?

Rob

If you are going to do it, I wouldn't base it on chip type but rather something else.
The current GLCD v3 library does this. While not a true frame buffer,
it has a READ_CACHE ifdef to turn on a write-through read cache.

If this code will be limited to this display (128x32) creating a frame buffer will take up 512 bytes of RAM,
while it is a good percentage of RAM on many of the arduino AVRs, it could still work on even on a m168
depending on what else was the code was doing.
Note: a good way to keep other RAM from being chewed up is to show people how to use program space for their
strings vs using the Print class functions. i.e. avoid xxx.print("hello")

A frame buffer does make life so easy and also makes certain other things possible.
(graphic drawing primitives in this case) as well offer ways to make things faster.

In cases like this where you can only write to the display memory, I'm not sure how
you can provide additional graphic primitives without one.
The challenge will be how to do the display refreshes as it sounds like updates to the display are potentially
quite time consuming so you won't want to be updating the physical display on a per pixel basis if
you can avoid it.

--- bill

Not on a per pixel base (unless the user enforces it) but just looking at e-paper behaviour, I think if there are changes in the last 250 millis, it's time to refresh, it should never be more often than the time to refresh. So when a pixel is set a timer is started/starttime is noted and 250 millis later it happens ...

robtillaart:
Not on a per pixel base (unless the user enforces it) but just looking at e-paper behaviour, I think if there are changes in the last 250 millis, it's time to refresh, it should never be more often than the time to refresh. So when a pixel is set a timer is started/starttime is noted and 250 millis later it happens ...

So that is the $1M question:
How would you implement something like that?

While it sounds easy at first, IMHO, in many cases it is a bit messy, quite difficult, or
potentially even impossible to do it that way in a simple embedded AVR type environment
particularly when trying to make it easy/simple to use for the Arduino world.

Some of it depends on the API itself as well as when you can update the display.
For example depending on how long an update takes you may not be able to do it
in an ISR. (often this is the case). And if you can't do it in an ISR how do you interrupt the
running foregound context to force the update if the update is not part of the API itself?
Obviously, you could require the user to periodically call a "refresh" function as part
of his foreground loop that handles it when needed but that is a bit messy
and the user might unknowingly get hung waiting on input
by some other library or even his other code before the output flushed out.
Some APIs use an explicit "flush" call that flushes the buffer to the display that
the user must call to avoid these types of issues.

It can actually get quite complex to do it much less efficiently and fast.
For example, if the user sets just one pixel, do you really want to update the entire display?
(even if that update is deferred?)
If you don't update the entire display, how do you efficiently track which areas of the screen
need updating. There are ways to add small amount of smarts without adding too much code
like tracking the smallest square portion of the display that needs to be updated.
This works well for text that tends to update a small rectangular region but doesn't
work as well for things like a diagonal line or a circle which would update the entire rectangular
region bounded by the line/circle.

That is why I said the challenge is how to do the physical display refreshes once
the code starts to defer any pixel updating.

--- bill

I think it makes the most sense to keep image commits exclusively as a user-controlled function. These type of displays mainly fill a niche for low-power systems where the image content changes infrequently (ebook readers, sensor displays, remote controlled price tags). The fact is anyone showing full motion video, etc. will save power and $ using a conventional LCD. For the rest, nobody will know better than the user whether they are done writing a new image or not, and we don't need to waste power second-guessing them.

That said, with a framebuffer one could easily add a few extra bytes for tracking the 'dirty' (changed but not committed) area. This display can do partial updates down to a single line (entire lines though; no rectangular areas), so for the cost of 4 more bytes we can store a flag for whether each line has changed or not, and determine the minimum enclosing area. Or, 2 bytes and we store the lowest and highest line that contains changes. Then a "commit_auto()" type function could be added to commit the dirty area and reset the flags.

Like with pin numbers (unlike some library authors ;-), I lean in favor of leaving more in control of the user, even if it makes things slightly more complex. A possible solution is to have a 'xxxLCD' and 'framebuffer' as separate libraries (the resulting 'framebuffer' object just being a thin wrapper around a hunk o' RAM and dirty area flags), and let the user pass in a pointer to the framebuffer if one is desired.

Very nice library and very well documented, thanks for sharing.