Android application to generate/process images for and upload to WaveShare Passive NFC-Powered (aka parasitic) E-Paper / E-Ink displays.
Contents:
- Features
- How to build and install this to an Android device
- Demos
- Known Issues
- Task Backlog
- Technical Details from the original project
It's only intended to interact with WaveShare displays through their Android SDK,
and unlikely to work with similar displays from any other vendor, unless they are
intentionally designed to be compatible.
WaveShare documentation explicitly says "This product is not compatible with
Samsung smartphones", but not sure why specifically - could be that it's just
their original app that doesn't work on these.
This application requires Android 11+ OS (API level 30 or higher).
Passive NFC E-Paper devices supported by the app are typically intended for
commercial usage like shelf labels and product tags, other informational notes,
or smaller color ones also seem to be for decorative uses (like a chain/keyfob toy).
Some of the compatible epaper device links: 1.54", 2.13", 2.7", 2.9", 4.2", 7.5", etc.
This particular version of the app is a code fork of the original version, written by Joshua Tzucker (in joshuatz/nfc-epaper-writer repository), with fixes/features merged from other forks, aimed to fix some build and UI issues that popped-up when I tried to use it, since first-party WaveShare application straight-up doesn't work on my device.
Alternative URLs for this repository:
- https://github.com/mk-fg/nfc-epaper-writer
- https://codeberg.org/mk-fg/nfc-epaper-writer
- https://fraggod.net/code/git/nfc-epaper-writer
Main application screen should contain following elements, top-to-bottom:
-
Screen size selection (in top toolbar, remembered) - determines image size and crop/scaling.
-
Re-upload last-used image box (with preview) - tap to immediately go do that.
-
Select exact image to upload - for pre-made images of exactly right size that don't need any processing.
-
Select/take and crop/scale/dither image - for using camera or image files of any size from any source.
Cropping dialog will allow to pick part of the image with right aspect ratio for selected screen, which will then be scaled to its size, and converted to 3-color (BWR) image using color dithering (added in DevPika/nfc-epaper-writer-update fork).
-
Create image from any composed text. Supports emojis.
-
Draw image on the phone screen, using JSPaint as WYSIWYG editor.
App should work with all screen sizes supported by WaveShare SDK.
Not sure how well last two sources work, main use-case for mk-fg/nfc-epaper-writer fork is exact image upload - there are plenty of good tools to make/edit images.
Floyd-Steinberg black-white-red (BWR) color dithering (merged from DevPika's fork) used for processing non-exact images might work suboptimally with 1-bit black-and-white displays - also didn't test it much here.
Steps below require any system with modern docker (aka Docker Engine) installed
(via e.g. apt install docker
on ubuntu/debian linux), any terminal console app
available to also run its command-line tools from, ~5 GiB of peak memory and
about 3 GiB of disk space temporarily.
-
Copy or download Dockerfile from this repository into any directory.
Doesn't really matter which dir, it'll only be used to copy build result (apk file) into by "docker build" command below.
For example, to fetch it using curl from the command line:
curl -OL https://raw.githubusercontent.com/mk-fg/nfc-epaper-writer/main/Dockerfile
-
Build and copy Android application package (APK) to the current directory:
docker build --output type=local,dest=. .
This will use Dockerfile as a recipe for
app-debug.apk
and will put it next to that Dockerfile afterwards.An older docker setup might give "unknown --output option" error, in which case docker-buildx plugin might be required, and command will be
docker buildx build --output type=local,dest=. .
instead of the one above. -
Copy generated
app-debug.apk
file to an Android device, and open it there (e.g. find and tap on it in Files app). Note that Android 11+ is required for this app, shouldn't work on older devices. -
Follow Android OS instruction popups from there on how to enable necessary settings to be able to install this tool from a sideloaded APK file.
This type of app installation from an APK file is called "sideloading", and steps required to allow it change between Android versions, but should be easy to lookup for specific one on the internet, if Android's built-in prompts don't make it clear enough.
-
Installed tool should show up among the usual "application drawer" icons with "NFC E-Ink Writer" name and a generic "android robot head" icon.
Another, more developer-focused option, is to build APK using Android Studio IDE, which might be more or less complicated, depending on one's experience working with such tools/ecosystem (vs command-line steps above) - open the project there, and pick APK option from the Build menu.
These showcase application version from the original repository and displays packaged separately from the control board.
NFC can be a little finnicky, and especially with these EInk displays. Depending on the power and capabilities of your phone, it may take time perfecting the exact distance and position to hold your phone in proximity to the EInk display in order to get successful updates.
On certain Android phones, you might also see a high rate of your NFC radio / chipset randomly "dying". This happens at a lower level of system APIs, so it is really hard for my application to detect or attempt to recover from.
When detected by the lower-level APIs, Android will throw this as a
android.os.DeadObjectException
, with the entry:NFC service dead - attempting to recover
. You can see the internal recovery efforts here.
Additionally, sometimes you might see corrupted writes, where something goes wrong during the transceiving process and the display ends up with random noise:
Disabling-enabling NFC on the phone (by tapping its Quick Settings tile at the top) and re-sending the image afterwards works for me to work around the issue. NFC reset is needed because usually it stops working when this happens (like due to problem being "NFC dying" mentioned above), sometimes crashing/restarting the app as well.
-
Replace "flashing" wording from original app with "update" or "upload", as don't think it's called that in WaveShare docs, and memory is only used as a temp buffer here, so seem to be a wrong/misleading word to use.
-
Better recovery methods for NFC adapter dying.
-
When caching generated image, prefix or suffix with resolution, and then only allow cached image for re-upload if saved resolution matches.
-
App Icon.
-
Publish APK and/or provide better build options.
(from the original joshuatz/nfc-epaper-writer repository)
Building this project was my first time touching Kotlin, Java, or Android APIs, of which this project uses all three. I opted to go this route (native Android dev) instead of React Native or Flutter, because I knew I was going to need access to a lot of lower level APIs, and saw it as an opportunity to learn some new skills.
This project uses a bunch of different technologies, and takes some interesting "shortcuts":
-
For the custom image generation options - both the text editor or WYSIWYG editor - I used WebView so that I could use HTML + JS + Web Canvas, and pass back the bitmap data to Android.
-
The WYSIWYG editor is actually just JSPaint, but with injected JavaScript for capturing the bitmap data from the app's canvas.
-
The text editor is a custom tiny webpage I put together that renders the text to a Canvas element, and then captures the raw bitmap data.
-
-
Local image option with processing uses CanHub/Android-Image-Cropper for cropping and resizing.
-
By using scoped storage and the right APIs, no special permissions (other than NFC and Internet) are required from the User.
-
For actually sending the bitmap data over the NFC protocol, this uses the WaveShare Android SDK, and the JAR file that they provided.
-
Kotlin coroutines are used throughout, as there are a lot of operations that are blocking in nature - main transceive operation is basically one long blocking sequence.