Hopelessly in love with clean code and unobtrusive design!
Posted on Apr 15, 2024
Open-source Android app to update the batteryless eink display!
I have a love-hate relationship with batteries - while they make our lives that much more convenient, they are hard to dispose and recycle. Even rechargeable batteries lose their capacity over time. When I came across a Hackaday post about mysterious eink / epaper devices from Waveshare that do not need a battery to run, I was intrigued. Of course, the idea behind this is energy harvesting, which isn’t anything ground breaking. But I love “hanko” / stamp-sized displays and had been eyeing the 1.54” epaper displays for a while now, so I got my hands on the 1.54” model. As I was waiting for the unit to ship from China, I looked into driving these displays programmatically. The app provided by Waveshare was closed-source and the documentation for the Android SDK to interact with these displays was sparse and confusing to say the least.
Luckily, folks from the open-source hacker / maker community had experimented with making an app for the display before me, so I forked the repo and got to work. With some clever app reverse-engineering and support(!) from Waveshare, I got the NFC library working even with my 1.54” display!
The next challenge before me was to effectively use the red channel that was specific to this model. To begin my tests, I tried pushing a pre-dithered image created in GIMP (and later, for good measure, even ImageMagick) to the display. I ran into unexpected trouble here: the image was garbled even if it was the exact dimensions and only contained red, black and white! Hoping to troubleshoot the image BMP format (“Perhaps it doesn’t want ARGB_8888 after all?!?”), I tried flashing some of the test images from the decompiled app, but the results were still garbled. At this point, I suspected one of the steps was altering the final bitmap that was getting flashed to the display, so I set this problem aside for now. It was time to dither the image programmatically.
A popular Floyd-Steinberg Dithering library for Android only supported black and white colors. While a Go dithering library did what I wanted, it was a tedious process to create the JNI bindings. I took this as a learning opportunity. And thus it was time to fork the Android dithering library and add color dithering! I quickly put together the C++ native bits to get this working. Since all of this is open source, I could easily deploy the library using Jitpack, similar to the original author of the library. Waveshare provides some code using runners on their Android SDK page, so I added that in for some quick games on the stability side. Less ANRs = more fun!
The question remained though: what was changing the final bitmap that got flashed to the display, causing the image to get garbled? I experimented with the placing the dithering after the cropping library had done its work. Et voilà, the image displayed correctly! Digging into this, it looks like the Android Cropping library might have been anti-aliasing the image, which caused intermediate colors to come up. With the final bits of the dithering puzzle in place, it was time to enjoy an Initial D poster in all its 200x200, dithered glory:
Made it this far? You can find all the code for the flashing app in the Github repo for the Android app and my fork of the Android library with colored dithering here. While the dithering code is a bit of mess considering it was written within a few hours over the weekend, an easy improvement would be to pass in a custom color palette (currently only supports the original BW and BWR schemes).