From b57aa96659ab8ba1206035679049db48a24e1d24 Mon Sep 17 00:00:00 2001 From: uvok Date: Sun, 10 Aug 2025 17:21:04 +0200 Subject: Update epaper post --- _drafts/building-an-epaper-badge.md | 143 ++++++++++++++++++++---------------- 1 file changed, 79 insertions(+), 64 deletions(-) (limited to '_drafts/building-an-epaper-badge.md') diff --git a/_drafts/building-an-epaper-badge.md b/_drafts/building-an-epaper-badge.md index 7724393..ce23866 100644 --- a/_drafts/building-an-epaper-badge.md +++ b/_drafts/building-an-epaper-badge.md @@ -23,7 +23,7 @@ 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 +motives via MiFare RFID transponders (using an MFRC5xx reader). Work on that development never really took off. ## Requirements @@ -55,8 +55,8 @@ 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. 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 @@ -64,24 +64,27 @@ 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. +That's a bit cumbersome. ## Get the picture on the display -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. +You can't just simply throw a JPEG onto the display. The display doesn't +understand that. It only understands pixel data. Also, the display can only +draw black and white pixels. I also have a display with yellow color support, +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, not a monochrome -one. (There are displays which support a few gray-levels, though.) +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. 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, but I simply used either GIMP, the Floyd-something -algorithm, or one of the "ordered" modes of ImageMagick. +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. -This, then, looks like this: +The result, then, looks like this: 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. +basically just a C array declaration, so you can GCC that file and throw in the +array into the GxEPD2 function call. And voilá, it works. You need to set the +rotation first, though. ## How to attach the badge to myself -This would be cumbersome. I had a Waveshare module/PiHat (to heavy), and a -simple "ePaper sheet" - which would be too cumbersome to attach to the -devboard. So, at this point, I decided to switch from the prototyping platform -onto something better. +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. 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! 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. +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 - 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. +on the inner side of my shirt &emdash; 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 +unsolved. I can kinda attach the magnets to the housing screws at the top, but +that's not *very* stable. ## Set what is displayed -In addition to these controls, which allow choosing the motive, I wanted +In addition to these switches, 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. 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 +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. + +Actually, the switch-selection was a bit troublesome. Redrawing the whole +display takes around 2 seconds &emdash; 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 at runtime, +not integrated into the picture. So, I simply update a region in the +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!). 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. +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 +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. ## Power saving @@ -149,9 +158,9 @@ 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 frequency to 80 MHz. And lo +Using a bit of experimenting, and failing to cancel light sleep with a 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. @@ -159,9 +168,9 @@ 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! -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. +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! ## The app @@ -169,22 +178,28 @@ whole time. 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 +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. +The graphical programming is unusual to me. I used Scratch shortly 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. -… +TODO: insert picture -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. +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. -… +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 implmenetation 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. ## Resources -- cgit v1.2.3