summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoruvok2025-08-10 18:29:01 +0200
committeruvok2025-08-10 18:29:01 +0200
commita510673221fe872a75ed1f868ba6d3a1a762615e (patch)
treea71ba10569c797ee321e51a0879154871b5a3733
parentf082814544146376870be8d4a95aa9412d8fc0f1 (diff)
Finish article
-rw-r--r--_drafts/building-an-epaper-badge.md260
1 files changed, 222 insertions, 38 deletions
diff --git a/_drafts/building-an-epaper-badge.md b/_drafts/building-an-epaper-badge.md
index 866e0e8..43cd56b 100644
--- a/_drafts/building-an-epaper-badge.md
+++ b/_drafts/building-an-epaper-badge.md
@@ -14,32 +14,33 @@ Two weeks before [Awoostria]({% post_url 2025-07-30-awoostria-con-report %}):
So it begins… The story how I built myself an ePaper badge.
-Actually, the story begins way earlier, when I still had a physical Raspberry
-Pi running stuff in my home network. I wanted to tinker around a bit and bought
-myself a Waveshare ePaper. These are simple black-and-white displays which
-maintain their content when the power switches off. They are also inside eBook
-readers.
+Actually, the story begins way earlier (unrelated, when I still had a physical
+Raspberry Pi running stuff in my home network). I wanted to tinker around a bit
+and bought myself a Waveshare ePaper. These are simple black-and-white displays
+which maintain their content when the power switches off. They are also inside
+eBook readers.
-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
-development never really took off.
+This was also when 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 change motives via MiFare RFID transponders (using an MFRC5xx reader). Work
+on that development never really took off. (Ugh, too little flash for all the
+pictures, too much stuff to code myself!)
## Requirements
So this time, I simply said "fuck it", and threw an ESP32 on the problem.
Also, I decided to use [PlatformIO](https://platformio.org/), a
toolchain/SDK/library manager. I started with the Arduino framework, which is…
-pretty wasteful in terms of resources (Flash/RAM/…), but speeds up development
-significantly.
+pretty wasteful in terms of resources (Flash, RAM, CPU etc.), but speeds up
+development significantly.
I had a simple ESP32 devboard, and one of the Waveshare modules, and started
coding.
-But wait, what to I even want to achieve? Well, I wanted to "mood badge", i.e.
+But wait, what do I even want to achieve? Well, I wanted to "mood badge", i.e.
show my current mood with funny pictures. I couldn't get one on previous
conventions, so I was just gonna build one myself.
@@ -64,7 +65,8 @@ 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 bit cumbersome.
+That's a bit cumbersome. But still better than writing everything myself. Also,
+it supports graphics primitives!
<span id="pic"></span>
## Get the picture on the display
@@ -76,13 +78,15 @@ but that makes it even more complicated, actually. Even when you don't use it,
refresh is slow.
So, you definitely can't throw a color picture on the display, nor a monochrome
-one. There are displays which support a few gray-levels, though.
+one. There are displays which support a few gray-levels, but I don't have one
+of those.
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
-background to dithering (see below), but I simply used either GIMP, the
-Floyd-something algorithm, or one of the "ordered" modes of ImageMagick. It was
-a bit of trial-and-error and seeing-what-looks-best.
+technical background to dithering (see the Reference section), but I simply
+used either GIMP with the Floyd-something algorithm, or one of the "ordered"
+modes of ImageMagick. It was a bit of trial-and-error and
+seeing-what-looks-best.
The result, then, looks like this:
@@ -100,26 +104,30 @@ rotation first, though.
## How to attach the badge to myself
I have a Waveshare module/PiHat (which is too heavy), and a simple "ePaper
-sheet" including a housing for it. The housing can only fit the ePaper, not the
-devboard, though. Also, it would be too cumbersome to attach to the devboard -
-loose wires! So, at this point, I decided to switch from the prototyping
-platform onto something better.
+sheet" including a plastic housing for it. The housing can only fit the ePaper,
+not the devboard, though. Also, it would be too cumbersome to attach to the
+devboard - loose wires! So, at this point, I decided to switch from the
+prototyping platform 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),
+[CrowPanel](https://www.elecrow.com/wiki/CrowPanel_ESP32_E-paper_2.9-inch_HMI_Display.html),
which is exactly what I need. It has a display, a built-in ESP32 controller, a
housing, and even some switches! As a huge plus, they even specify 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 &emdash; not ideal. I positioned the magnets in
+on the inner side of my shirt &mdash; 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 &emdash; and now there's a hole in it. :( This problem is still
+inside &mdash; and now there's a hole in it :( . This problem is still
unsolved. I can kinda attach the magnets to the housing screws at the top, but
that's not *very* stable.
+When starting to work with the Elecrow display, at first nothing would work.
+When I looked at their example code I noticed there's an additional power pin
+that needs to be toggled.
+
<span id="setdisp"></span>
## Set what is displayed
@@ -129,10 +137,10 @@ something "more direct", so I added the
of coding, I added a service and some characteristics, so the available motives
could be read via BLE. Also, the motive could be selected via another
characteristic. I started writing the characteristic with *nRF Connect For
-Mobile*, but started writing an app later.
+Mobile*, but started writing an app [later](#theapp).
Actually, the switch-selection was a bit troublesome. Redrawing the whole
-display takes around 2 seconds &emdash; but that is not acceptable when
+display takes around 2 seconds &mdash; 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".
@@ -142,14 +150,14 @@ vertical-center-right of the display with the mood text. The picture stays the
same, but the text reflects the selected mood. 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 &emdash; 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!n the CrowPanel).
-I'm gonna research a good wear-levelling file system for that. Probably not
+This is still not ideal. The first update after power-up must be a full one,
+and I don't save the selected preset in NVS &mdash; I don't want to destroy the
+flash by lots 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 in the CrowPanel).
+I'm 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 on power-up would also be an
-option, but I don't like it that much.
+can be written in FUSE). Alternatively, drawing the first motive on power-up
+would also be an option, but I don't like it that much.
<span id="powersave"></span>
## Power saving
@@ -170,7 +178,15 @@ why!
Apropos of powering: The badge is normally unpowered and only powered if I need
to change the motive. I don't want to have an USB cable hanging on me the whole
-time!
+time! Using a battery might be an option, but I had problems with mismatched
+connectors &mdash; the Elecrow display doesn't have a standard JST connector
+like the LiPo battery I bought (Li-Ion might even be the safer option? I have
+no clue about this stuff. With a quick search, I only found these cyclindric
+Li-Ion batteries and have no idea how I would connect them). The badge seems to
+have a "mini" variant of that connector.
+
+For the time being, I power it with a USB powerbank, but this I still consider
+an unsolved problem.
<span id="theapp"></span>
## The app
@@ -1020,9 +1036,177 @@ settled for Flutter.
Again, this was completely new to me. I started off with a popular BLE library,
which turned out to be an unfortunate choice, as Linux support had a few
quirks. That was probably a good thing in hindsight, as this made me abstract
-away the BLE stuff in implementation classes, and only use the abstract base
-classes in the code. Well, you can see what the code looks like, I linked my
-repo below.
+away the BLE stuff in implementation classes, so I could easily try out
+different libraries, and only use the abstract base classes in the code. Well,
+you can see what the code looks like, I linked my repo below. This is what the
+motive selection looks like:
+
+<img src='data:image/gif;base64,
+R0lGODlhkwH0AffGABkYHAoKCR4dIiAeJSUiKiglLS0pMysnMDIuOjAuNDYxPDY0OTo1RDw3REI8TUU
++UUZDSUhFS0lCVk5LUU1FWkhBVVFOVFFJX1RRVlZTWVlWXFxZXlVMZFhPZ15bYVxSbF9VcGFeZGNZdW
+lefGRadmRgZ2djaWpmbGxob25rcWtgf3JudHRxd3dzeXp2fHx4f5UiMp41RKQ2RqI8Sqk+TaE3R6lAT
+6ZAUatDUqdJW7BJWa5QXahMY6tRZqpSaKlZc6tZdLNbaLdmcrRhbW1akm5jgnVpi3NniH56gXxqnXlp
+loJ9hIN1nIByl7twgYFvoYRzo4p7pIx8qYh4o5B/q8J7hYSAh4aCiYmFi42Ij4+KkZKNlJWQl5eSmZq
+VnJ2Yn5KCrp6aoJaGspqKtZ2MvJeIso+Aq6KdpKWIt6WLuqWWvaWgp6ahqKqlrK2or6+psLKttLWwt7
+axuLu1vL24v8aDjcuNlc2WntCSmtCepdOjqtesstuzuaaPwaWSxKmWyauZzaqYxK6e1LCf1bOZzraoy
+b+5wL2xzrOg17Wi2rmk3Lys37Klxr+q5bun4MO9xMO409+8wMGs5sKt6Mex7siy78y19M6498+4+NO8
+/dG89eC9w8bAx8fByMvFzM7Iz8/J0M3C2tLM1NPJ3tfR2NvV3N7Y39bQ1+THy+fO0evW2dnP4tbM4d/
+Y4NzT5dfA/tnD/uLc5eXd6+jd6eDH/+LJ/+jO/+vR//HW//TZ//ne/+bg5+fh6erk7O7o7/To6u3n8O
+/o8PPs9fbv+Pfw+f73//////7+/v/6//jy9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAACH5BAAAAAAAIf8LSW1hZ2VNYWdpY2sNZ2FtbWE9MC40NTQ1NQAsAAAAAJMB9AEACP
+8AMwkcSLCgwYMIEypcyLChw4cQI0qcSLGixYsYM2pMiMnSJEVOcIicQbIkjh5oBClqRMnSpY0wY8qcS
+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'
+ alt="Screenshot of the Flutter app, allowing selection from
+ sleepy, hungry, hugs?, uvok, overstim, contact, games?"
+/>
## Resources