Mild annoyances in e-paper displayery
05-07-2025
To bake a cake from scratch, first you must invent the universe. Although I'm rather lacking in the omnipotence department, these are my musings following a battle with trying to show planes on a paper screen.
E-paper
I chose e-paper because, as a technology, it's pretty neat, and well suited for my ultimate goal: an interactive decor piece that could display live aeroplane positions. Once your content is displayed on the screen, it needs no power to display it, much like real paper. Sounds simple enough, right? Not entirely.
Unlike OLED or other more "established" display techniques, display driver modules are often immensely overkill, incorporating whole ESP32 microcontrollers, and lots of listings for driver + display combos do not mention the driver chip, which is of utmost importance if you're writing drivers from scratch.
I chose a 4.2 inch WeAct display for a number of reasons: it had a sturdy PCB backing the display (as e-paper displays are thin and fragile), they were the first one I found which had a "discrete" driver that had a well-documented set of functions, and the price was also rather nice. There are more "user friendly" options from companies like AdaFruit, but these either have microcontrollers attached to the display, or include unwanted features, like SD card readers.
The driver of choice was a SSD1683, which I wrote a driver for. Thankfully, things were simple enough once I actually found a datasheet, and only required some minor headbashing because of the timings required for the cold start procedure.
Displaying
Then, came the time to actually display things. Naturally, you can't just upload a PNG file to an e-paper display, it expects (in my case) a sequence of bytes, where each bit represents a different pixel. SSD1683s can handle two RAMs, one for black and one for red, of which I only used the black RAM. This meant that I had to convert my images to XPM (a glorified array of bytes) first.
To my surprise, the images had weird artifacts and the colours were inverted, so after changing the bit order and inverting all bits, things started to look OK-ish. Changing images, however, showed me that e-paper has some nice features, such as partial updates. Albeit far, far quicker than "refreshing" the whole screen, partial updates leave "shadows" of the previous image on the display, meaning that to make sure you aren't displaying odd artifacts, you must refresh the whole thing (which gives you a very dramatic "black-to-white-to-black" transition, which most Kindle users are familiar with).
Then, superimposing the planes on top of the framebuffer proved slightly more difficult than anticipated. Essentially, I had to calculate how far away from the westernmost lattitude/northernmost longitudes the plane positions were, and position them on screen accordingly. Slight issue: remember how we pass our framebuffer to the display with a byte array? This means that performing ORs on the current framebuffer was "easy" as long as the plane images were multiples of 8, and the X positions were multiples of 8.
After calculating the remainder of each "row" for images that had a width that was not a multiple of 8, adding them to the next row and so forth, I also had to do the same thing for X positions which weren't multiples of 8. A bit annoying, and did introduce some processing overhead, but things were looking good. Then, I had to roate the images if the planes had a heading of anything other than 0.
Rotation matrices are a fun topic, and thankfully handled by those who came before me, like in this Amiga forum topic. This, however, leaves out the problem of scaling: if I rotate the plane image 45 degrees, a 32x32 image will now have a size of 45x45 if I want to display the full plane (sqrt(32^2 + 32^2)). I could either scale it, or handle it the coward's way: have a 48x48 image where the plane is only 32x32 in size inside that image.
Time savings
I could have saved a lot of time for going for a library such as GxEPD2, which wasn't necessarily easy to set up either, as you have to manually find the driver you're going for and comment it out from the appropriate headers, but it was much quicker than making everything from scratch.
Additionally, and this is a matter for a different post, but MicroPython had plenty of tools already in place. The C side of things for the Pico is surprisingly lacking (sometimes even in documentation and examples), and I almost resorted to Arduino-like frameworks for things like TLS/WiFi.
There really is no moral to this story, other than I should've used existing libs to save time.