Driving E-Paper Displays With Memory Limited MCUs

It’s easy to become jaded by modern microcontrollers: for just a few bucks you can get a MCU that’s powerful enough to give a desktop computer from the early 90s a run for its money while packing in contemporary technology like WiFi and Bluetooth. For many projects we don’t even have to consider optimizing our code, because we aren’t even scratching the surface of what the hardware is capable of.

But sometimes you don’t have the luxury of using the latest-and-greatest chip, and have to play the hand you’re dealt. That’s when folks like [Larry Bank] really shine. In a recent write-up, he goes over his experiments with driving e-paper displays (specifically, salvaged electronic shelf labels) with 8-bit MCUs that on paper shouldn’t have the resources to run them.

A similar trick can be used on OLEDs

The problem is that these displays generally expect to be handed a fully-formed image, which can easily exceed the free RAM on a low-end chip. For example, a 1-bit 128 x 128 image would consume 2 KB of RAM — more than four times the available memory on an ATtiny85.

As [Larry] explains, his alternate approach is to write data to the display in columns that are only one byte wide. Combined with his existing work with image decompression on constrained hardware, he’s able to rapidly draw out full-screen TIFF images using an Arduino UNO as demonstrated in the video after the break. He hopes the work will inspire others to experiment with what’s possible using the dinky MCUs you generally find in second-hand shelf labels.

20 thoughts on “Driving E-Paper Displays With Memory Limited MCUs

      1. If you’re looking for displays that are ‘new’ and might be less expensive than what I’m seeing on e-bay, some are also available on Amazon such as amazon.com/dp/B0BJ1SGSYX for a lot of 20, comes to about $19 US each. That includes the manufacturer’s hardware, so there may be other options if you can get into that, perhaps with a Zigbee solution. I can see setting up some of these on the interior of exterior doors to let people a last chance to know what the weather is before they step outside, perhaps indicate that someone is, or has been on the steps there, etc. Or just buy a lot, split it up and resell what you don’t expect to use for fun and profit.

          1. Hey guys, try to include the title of the item when posting links like this. Otherwise the information is lost when the listing expires and the wayback machine doesn’t seem to have it

  1. I’ve written similar libraries to interface atmega328p’s to larger higher resolution epd’s writing bitmaps stored in flash to the display. I made a desktop epd clock/calendar that runs a 4″ display at 300×400 pixels off a 328p, storing higher resolution fonts in flash using progmem. The stock libraries provided by epd manufacturers that rely on double buffering from ram just cant even hope to run on mcu’s with only a few KB of ram (especially when you get into the “3” color displays). Doing things like image rotation, scaling or mirroring requires trading cpu cycles for memory but is possible even with dinky chips.

  2. Thanks to the author for reminding us how to get more from less. This reminds me of old 80×24 terminals which used a single byte of RAM per character, repetitively converting ASCII to pixels on the fly via a “fast” character-generator ROM driving the video = 1920 bytes per screen[1].

    Neopixel/WS2812 libraries also often use the naive approach, which is to allocate 3 bytes (R, G, B) per LED, limiting CPUs like the ATTiny85 to barely 85.

    As long as the CPU can keep up with the WS2812 timing, it may be better to /compute/ the color on the fly.

    For Neopixel displays which only use a few colors, a color map saves memory. A VU meter for example might only have 4 colors: off, green, yellow, and red; requiring 4colors x 3(RGB)=12 bytes, allowing the ATTiny85 to drive over 200 LEDs storing one byte per pixel (which is a waste since only 2 bits are necessary, but the library code’s easy and flexible). If the color map’s known at compile time, it can be compiled into program memory, saving precious RAM for other things like more pixels. The ATTiny85’s plenty fast enough to do the color lookup while programming pixels[2]

    Custom things like bar graphs can use even less memory. Imagine a 1024-pixel WS2812 string used to display a 10-bit value from an A/D converter as a red bar graph. The neopixel driver simply loops 1024 times and programs each pixel either red or off, on the fly. 1024 pixels using around 8 bytes of RAM (the colors and loop+comparison are in program memory).

    [1] With video attributes (reverse, blink, underline, bold) and often a 25th text line, RAM was often a bit higher than 1920 bytes
    [2] It’s easier to do this if the super-restrictive idea of WS2812 time is relaxed aka http://wp.josh.com/2014/05/11/ws2812-neopixels-made-easy/ with which I’ve had good success

  3. Can do vector graphics a line at a time on an atmega328p no problem. Vector graphics can be smaller than bitmapped for certain designs. To render, only need enough memory to store a list of edges intersecting the current line, then render the line after sorting the edges. 2k is more than plenty of memory to do that.

  4. I had to do something similar to run more than 83 NeoPixels from an ATTINY85. Almost all the existing Arduino libraries expect you to hold a copy of the bitmap in memory, make your modifications to the bitmap, then dump that bitmap to the pixels. But 83 pixels * 3 bytes = 249 bytes, and there’s only 512 bytes available on an ATTINY85. And if you want to do animation, you’re having to scroll through the entire array doing memory copies the whole way down.

    Starting from a library written by John ‘bigjosh’ Levine, that directly twiddled bits, I set up a 1D array of bytes that I could programmatically convert to the 3 RGB bytes on the fly. You had to do the conversion quickly, because you only had a few cycles between each LED in the NeoPixel protocol, but it ended up working perfectly for me!

    Animation — especially shifting — was especially easy. You could choose anywhere in the array to start, then step in either direction and whatever rate you desired.

    I ended up using a full byte when I only actually needed 1 bit for my use case, but I imagine one could easily come up with a packed-byte format to add colors, or even a GIF-style palate that you could rotate.

    I owe a debt of gratitude to the writers of NeoPixel drivers, but there are sometimes when twiddling bits is your best — any maybe only — option.

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.