Maybe some important information about my own background, I am an engineer who has spent years writing simulations, image analysis and signal analysis software in both C/C++ and Python. I adore Python for these kinds of projects as the community is so open and thriving. Yes, I could probably find some C/C++ library for the display, or the manufacturer would have some snippets of example code which work if you have the same compiler and architecture. In a project like this there are many moving wheels and it’s important you can quickly determine which wheel is stuck. With Python I can make notebooks or scripts with each part and debug them in minutes. Sadly, with C/C++ even with years of experience I tend to do this by either linking the code to Python or making minimum working python versions. Anyway, enough about me.
My first thought once I had some hardware up and running was “OK, now I will pip install ereader
and something will install and a few minutes later it’ll be running!”, sadly this was not the case. There are several E-book parsing software using python. Most famous is probably calibre which is a e-book library manager with an e-book reader and editor. Calibre is excellent and I use it to pre-process e-books before putting them on the device. There is an excellent terminal like e-book renderer which I noticed curiously was parsing the e-books itself. Finally, I also found ebooklib which is an EPUB parser. While playing around with this software and several test EPUB files I was regularly running into issues with the parsing of text from the e-books. These issues and the fact I wanted to have a very good integration between the display and the content meant I decided to borrow inspiration and in places code to write a new parser for this project.
What is an EPUB
An EPUB is an open format used to store electronic books, in practice an EPUB is a zip file containing a website. I say website and not webpage because they will have multiple XML/HTML files and images. There are three major EPUB versions and probably the most surprising thing for me was how loosely it seems the format is adhered to even within one version. This led me to always pass books through calibre and convert them to EPUB even if they were EPUB, as missing meta-data would be added with empty fields, malformed HTML would be corrected. This single change in the process reduced the occurrence of bugs immeasurably.
Application design
The application had to have several features
- Record user input to control the device
- Navigate an e-book
- Navigate a library of e-books
- Save status in book
- Load book at current location
- Manage connections to the Bluetooth trigger
The model which I came up with for this was as follows, there is a main application that controls which “mode” is active and has access to the display. When the system starts it loads the previous state, on first start (when no state exists) it defaults to library mode.
These modes are essentially functions of this class that update the screen buffer and interpret the button presses into actions. When a mode exits it will either indicate the next mode to be used or exit through starting a system shutdown process.
The library mode essentially is a selection list of all compatible files on the device. When a file is chosen the main application records this state and launches the reader mode. The reader mode is a thin interaction with the E-Book object, it translates the buttons presses into pages changes, system changes and state changes. The e-book objects are where the EPUB files are loaded and converted on the fly into rendered pages, they say a picture is a thousand words, however quantitatively each picture rendered on the E-ink display only contains about 100 Words.
The fact the screen needs ~1MB of raw data to describe it means a typical book would need >300MB, even smaller classics like 50 shades of grey would need 100MB. This would be a significant drain on the limited RAM of the Pi Zero, so instead pages are rendered almost on the fly. “Almost” means does not mean too late, the EPUB class which provides these images has a symmetric buffer around the current page. When a page advances the next pages in the buffer is provided, the oldest page is discarded, and a new page is added to the front of the buffer. I don’t have the numbers on hand but rendering a page is quite slow, but it is still faster than reading, so all the rendering occurs while the user is reading. This solves the Responsive enough to be useable requirement! Right now, the speed of the page turn is sub second and limited by the SPI and page turn. In theory the SPI could be eliminated for page forwards turns by moving the image to the IT8951 while the user reads. However, I am doubtful the user would notice this change in speed.
The controls are monitored by an independent thread that provides updates on any button presses. An input will be caught and executed at the next opportunity. Subsequent inputs before it has executed are discarded. This was to simplify the application. This thread also spawns a BLE thread on demand for connection to a remote page turning trigger.
Wrist remote design
The wrist trigger had two different versions of software. The first version was written in C++ using Platformio (a fantastic IDE extension for VScode). While this ended up being decent for “bench development” I discovered that the poor architecture I developed meant parameter changes required the device to be re-flashed with a newly compiled version. While working on the project I first encountered Micropython which allowed me to modify the application over the air via the webrpl while in use. This was quite a game changer. The other massive advantage for me with micropython was that the BLE library is quite mature and handling exceptions was easier than in C++. I am sure there are good C++ libraries on PlatformIO for the ESP32, but this separation of concerns in development (not having to doubt my interactions with the BLE library) was a huge relief.
The trigger application was quite simple. It checked the accelerometer at 10hz and computed the magnitude of the acceleration using ulab. It was then calculating an exponential moving average and variance. The difference from the current measurement and the mean was then compared to the variance.
\[\frac{ \delta^{2}}{Var} < Threshold\]If this criterion is met, then a signal was is sent via BLE to the e-reader that a page turn is expected.
The only other thing which the remote did was monitor a GPIO port for the power button being held for >4 seconds. When this occurred, it disabled the boost converter causing the system to shut down.