summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--_drafts/building-an-epaper-badge.md135
1 files changed, 85 insertions, 50 deletions
diff --git a/_drafts/building-an-epaper-badge.md b/_drafts/building-an-epaper-badge.md
index 8d7c245..7724393 100644
--- a/_drafts/building-an-epaper-badge.md
+++ b/_drafts/building-an-epaper-badge.md
@@ -8,7 +8,7 @@ categories: tech
## Foreword
-Two weeks before [{% post_url 2025-07-30-awoostria-con-report %}](Awoostria):
+Two weeks before [Awoostria]({% post_url 2025-07-30-awoostria-con-report %}):
> Hey, I should build something for my Tinkering Projects Show And Tell panel!
@@ -20,7 +20,7 @@ myself a Waveshare ePaper. These are simple black-and-white displays which
maintain their content when the power goes off They are also inside eBook
readers.
-Some years ago I wanted to build myself an electronic doorsign for the EAST
+Some years ago I wanted to build myself an electronic door sign for the EAST
convention with these, and I wanted to go "as minimal as possible". I wanted to
use one of the MSP430 controllers I had laying around, and I wanted to switch
motives via MiFare RFID transponders (using an MFRC5xx reader). Work on that
@@ -55,26 +55,26 @@ This involves several sub-problems:
## Control the display
Usually, you never talk to the displays themselves, but to a display
-controller. Usually, you talk to these via a digital interface, e.g.
-SPI. There are different display controllers with different command sets.
+controller. Usually, you talk to these via a digital interface, e.g. SPI.
+There are different display controllers with different command sets.
But why bother with implementing this myself? There are ready-made libraries.
-For myself, I decided to use [GxEPD2](https://github.com/ZinggJM/GxEPD2).
-They support *some* Waveshare displays. The problem with Waveshare displays is,
-they don't disclose which display controller they use. So it's kind of an
-trial-or-error procedure. Or rather, you can look at their example code,
-figure out which commands they are using, and compare what commands
-GxEPD2 uses. That's a but cumbersome.
+For myself, I decided to use [GxEPD2](https://github.com/ZinggJM/GxEPD2). They
+support *some* Waveshare displays. The problem with Waveshare displays is, they
+don't disclose which display controller they use. So it's kind of an
+trial-or-error procedure. Or rather, you can look at their example code, figure
+out which commands they are using, and compare what commands GxEPD2 uses.
+That's a but cumbersome.
<span id="pic"></span>
## Get the picture on the display
-You can't just simply throw a jpeg onto the display. a) The display doesn't
+You can't just simply throw a JPEG onto the display. a) The display doesn't
understand that. It only understands pixel data. (the library doesn't,
either). b) The display can only draw black and white pixels.
So, you definitely can't throw a color picture on the display, not a monochrome
-one. There are displays which support a few gray-levels, though.
+one. (There are displays which support a few gray-levels, though.)
So. What to? The solution is "dithering". I.e. you trick your eye into
perceiving grey by having clusters of black and white pixels. There is some
@@ -88,8 +88,9 @@ This, then, looks like this:
alt="a dithered image of my fursona"
/>
-
-TODO: XBM
+Now, about the image format… GxEPD2 supports "XBitmaps", or XBMs, which are
+basically just a C array declaration, so you can GCC that file and throw in
+error into the GxEPD2 function call. And voilá, it works.
<span id="attach"></span>
## How to attach the badge to myself
@@ -102,13 +103,15 @@ onto something better.
Fortunately, Elecrow provides a
[CrowPanel](https://www.elecrow.com/crowpanel-esp32-2-9-e-paper-hmi-display-with-128-296-resolution-black-white-color-driven-by-spi-interface.html),
which is exactly what I need. It has a display, a built-in ESP32, a housing,
-and even some switches!
+and even some switches! As a huge plus, they even write which display
+controller they use. I had to try some of the GxEPD2 display classes, but
+finally found one working.
-I decided to glue magnets onto the housing, and attach the display
-via magnets on the inner side of my shirt - not ideal. I positioned the
-magnets in the (vertical) middle of the housing, so it wobbles and is not
-readable. Also, I accidentally washed the shirt after Awoostria with the magnets
-still sticking inside - and now there's a hole in it. :(
+I decided to glue magnets onto the housing, and attach the display via magnets
+on the inner side of my shirt - not ideal. I positioned the magnets in the
+(vertical) middle of the housing, so it wobbles and is not readable. Also, I
+accidentally washed the shirt after Awoostria with the magnets still sticking
+inside - and now there's a hole in it. :( This problem is still unsolved.
<span id="setdisp"></span>
## Set what is displayed
@@ -116,44 +119,76 @@ still sticking inside - and now there's a hole in it. :(
In addition to these controls, which allow choosing the motive, I wanted
something "more direct", so I added the
[NimBLE-Arduino](https://github.com/h2zero/NimBLE-Arduino) library. With a bit
-of coding, the available presets/motives could be read via BLE,
-and also one could be selected via another characteristic.
-
-Actually, the switch-selection was a but troublesome.
-Redrawing the whole display takes around 2 seconds - but that is
-not acceptable when navigating the presets one-by-one.
-By looking at the API, I found out you can select a "partial region".
-
-What I didn't mention yet, the text that shows the mood is drawn using
-the API - not integrated into the picture. So, I simply update a
-region in the vertical-center-right of the display with the mood text.
-The selection is confirmed my pressing the rotary switch.
-
-This is still not ideal. The first update must be a full-one,
-and I don't save the selected preset in NVS - I don't want to destroy
-the flash by hundreds/thousands of write cycles. I don't have a solution
-to this, yet. Maybe I'm gonna integrate an microSD card (there's a slot for that!).
-Gonna research a good wear-levelling file system for that. Probably not FAT. That doesn't
-need to be readable on the PC. (And even if, simple stuff can be written in FUSE).
+of coding, the available presets/motives could be read via BLE, and also one
+could be selected via another characteristic. I started writing the
+characteristic with nRF Connect For Mobile, but started an app later.
+
+Actually, the switch-selection was a but troublesome. Redrawing the whole
+display takes around 2 seconds - but that is not acceptable when navigating the
+presets one-by-one. By looking at the API, I found out you can select a
+"partial region".
+
+What I didn't mention yet, the text that shows the mood is drawn using the API
+- not integrated into the picture. So, I simply update a region in the
+vertical-center-right of the display with the mood text. The selection is
+confirmed my pressing the rotary switch.
+
+This is still not ideal. The first update must be a full-one, and I don't save
+the selected preset in NVS - I don't want to destroy the flash by
+hundreds/thousands of write cycles. I don't have a solution to this, yet. Maybe
+I'm gonna integrate an microSD card (there's a slot for that!). Gonna research
+a good wear-levelling file system for that. Probably not FAT. That doesn't need
+to be readable on the PC. (And even if, simple stuff can be written in FUSE).
+Drawing the first motive would also be an option, but I don't like it that
+much.
<span id="powersave"></span>
## Power saving
-I first measured the current and was shocked. The whole thing
-draws around 80-100 mA. Not a big surprise, given that Arduino
-basically calls `loop()` over and over again.
-
-Using a bit of experimenting, and failing to cancel light sleep with
-an GPIO interrupt, I implemented power saving by using a timer-based
-light sleep (10 ms, gives good user response) and reducing the CPU frquency to
-TODO????? MHz.
+I first measured the current and was shocked. The whole thing draws around
+80-100 mA. Not a big surprise, given that Arduino basically calls `loop()` over
+and over again.
-And lo and behold, my USB current measuring equipment (resolution 10mA) showed 0 mA. Success.
+Using a bit of experimenting, and failing to cancel light sleep with an GPIO
+interrupt, I implemented power saving by using a timer-based light sleep (10
+ms, gives good user response) and reducing the CPU frequency to 80 MHz. And lo
+and behold, my USB current measuring equipment (resolution 10mA) showed 0 mA.
+Success.
Actually, reducing the CPU frequency was a requirement! If I didn't do that,
-the CPU would constantly crash when entering or exiting light sleep. No idea why!
+the CPU would constantly crash when entering or exiting light sleep. No idea
+why!
+
+Apropos of powering: The badge is normally unpowered and only powered if I
+need to switch the motive. I don't want to have an USB cable hanging on me the
+whole time.
+
+<span id="theapp"></span>
+## The app
+
+Writing the BLE characteristics with nRF Connect is all and well, but not
+really user-friendly. I didn't want to install the Android SDK, so I looked at
+cloud based development for a start. I found [MIT
+AppInventor](https://appinventor.mit.edu/). First, I was disgusted, because
+apparently they require Login with Google. But I found [an alternative
+way](https://code2.appinventor.mit.edu/) by which you simply get a
+"Passphrase-like" codeword you use for login.
+
+The graphical programming is unusual to be. I used Scratch in the past, so it
+was not completely foreign. Actually, it was kinda fun coding this, in "event
+style", once I figured out how to to like stripping, list filtering, etc.
+
+…
+
+This was good enough for a while, but then I decided I wanted to actually
+have the source code available. So I looked again at development options,
+and settled for Flutter.
+
+…
## Resources
-bla on dithering bla.
+- [surma.dev about dithering](https://surma.dev/things/ditherpunk/)
+- [git repo with badge source code](https://git.uvok.de/espadge/)
+- [git repo with app source code](https://git.uvok.de/espadge-flutter/)