I've been messing around with a lot of different ESP32-based devices, and the reTerminal E1001 is one of the more recent devices I've experienced. It has three buttons at the top, a temperature and humidity sensor, a buzzer, and even a microphone. However, what it also has is a 7.5-inch ePaper display, and all of that hardware got me thinking: what if I built a Kindle-esque device with it?
Of course, a lot of clever engineering goes into building a device as sophisticiated as a Kindle, and my weekend project encompassing a few hours is no match for the brains and brawn at Amazon. Yet, despite that, I was able to build a very basic ereader using just ESPHome, Home Assistant, and the Feedparser custom integration installed through HACS. It's a bit janky, but it works, and I'll be publishing all of the source code on GitHub so that others can use it, learn from it, and maybe even make it better.
reTerminal E1001/E1002
The reTerminal E1001 and E1002 are the end-game of ESP32 ePaper displays, packing expandable IO, an SHT40 temperature and humidity sensor, a buzzer, microphone, and more, all in a metal-frame. The E1001 is a monochrome display, and the E1002 is a full-color display.
Home Assistant's API can be called from any device
I didn't just rely on ESPHome
Home Assistant has a very tight integration with ESPHome, but I quickly realized a problem: what if I wanted to pull the entire article and display it on the device? It's technically possible, but Home Assistant's input helpers, that can be used to store temporary data (such as the contents of an article), are limited to 255 characters. I could have avoided this by going with a basic sensor where I stored the data in attributes, but by that stage, I already had another idea in mind: the Home Assistant API.
An ESP32 is just a microcontroller at the end of the day, and it can make HTTP requests and even host its own web server. These components do get a bit heavy, but the reTerminal E1001 has 16MB of flash storage and 8MB of PSRAM, more than enough to for pretty much any major project. That's when I concocted a plan: rather than relying on ESPHome's integrated API with Home Assistant, I'd make direct calls to Home Assistant's /api/template endpoint. It's a bit janky, and probably not what you're supposed to do, but I partially went down this path simply because I had the idea and wanted to see it through.
Of course, this was a rather difficult endeavor. I started off by looking at the Home Assistant documentation for that particular endpoint, and what I saw was deceptively simple:
curl \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"template": "It is {{ now() }}!"}' http://localhost:8123/api/template
So, that's how I got here. By the end of it, I'd written code in C++ with raw string literals that sent templates to the API endpoint, collected data from the Feedparser sensor, then processed it for displaying on the screen. The buttons control selections: the green button is for switching between reading mode and list mode, and the buttons move forwards and backwards on the home page or through the page index. It's quite janky, and the screen refreshes can make the experience slow, but the result is a working Kindle-like device, powered by an ESP32, so I guess it worked out.
This is not a replacement for a Kindle. Rather, it serves as a proof of concept of what you can achieve with a device like this once you put in the time to learn how to do it yourself. Using something like Pyscript, it should even be possible to use the EBookLib and process ePub files for rendering on the display as well. You could then rotate it for displaying on the screen in an actual book-like format, with the list propogated with books instead of content from an RSS feed.
Setting up our RSS feed and display
Pulling from Home Assistant using the API
Working with these devices, be prepared to manage everything. You invoke the hardware, you draw to the display more or less by the pixel, and you manage your memory, too. When I set up Feedparser with XDA and the Home Assistant subreddit, I knew I was really only getting started. Remember how I mentioned the Home Assistant API? Well, this is the concoction that ended up turning into:
json: |-
root["template"] = R"JINJA(
{%- set feed = feed | default('sensor.xda') -%}
{%- set entries = state_attr(feed, 'entries') or [] -%}
{%- set sel = sel | int(0) -%}
{%- set n = [entries|length, 10]|min -%}
{%- if n == 0 -%}
[no feed or no entries]
{%- else -%}
{%- for i in range(0, n) -%}
{{ '>' if i == sel else ' ' }} {{ i+1 }}. {{ (entries[i].title | default('')) }}
{%- if not loop.last %}\n{%- endif -%}
{%- endfor -%}
{%- endif -%}
)JINJA";
auto vars = root.createNestedObject("variables");
vars["feed"] = "sensor.xda";
vars["sel"] = (int) id(selected_in_list);
Thankfully, there are only two calls that I actually need to make: the first is the list of articles, the second is the contents of an individual article. The "R"JINJA" section of the code is the part that took the longest time by far, as I was facing numerous HTTP 400 errors (bad request) until I realized it wasn't interpreting my string correctly. Once I sent it as a raw string literal, then I started getting results that made sense. "JINJA," by the way, can be anything. It's just good practice to make it easily identifiable.
Once I'd worked that out, the rest was rather simple. I mostly re-used the same concepts I had used for my e-ink dashboard, where the screen tracks my tasks for the week and draws my daily tasks by tracking a newline character to go line by line. The page indexer uses a caching mechanism to only display what will fit on the screen, while storing the rest of the post in a buffer that it can switch to drawing instead if required.
This can be extended further in a number of ways: firstly, the buzzer can be incorporated to confirm inputs, and the display can essentially go into deep_sleep mode until another button is pressed in order to conserve battery. Other options, like clearing the screen, would also be fantastic, or automatic refreshing of the RSS feed if all you want is to keep track of headlines. Finally, the current temperature and humidity could also be displayed in the footer, though it's likely not to be super accurate if you're holding the device at the same time.
This was a difficult but rewarding project
It's really neat to see what these devices can do
I'll be the first to say that this is not going to be a go-to reading device for me, nor do I expect others to do the same. Instead, it's meant to serve as a demonstration of what you could potentially do with a device like this. The ePaper panel used here, while good, is simply no match in terms of refresh rate speeds to the likes of an actual Kindle, not to mention that the software is still quite janky. Yet it still manages work in a way that I didn't quite expect, especially given that I didn't know how feasible this project was before I started. The above photo is what you get from Seeed's SenseCraft HMI software that you can use to generate content on the display, and honestly, with a bit more work, this could probably reach feature parity.
If I were to improve this further, I reckon the first thing I would incorporate is a way to pull an entire article before displaying it. Many RSS feeds simply pull just a paragraph or two from the article, but Reddit's RSS feeds are an example where the entire post is surfaced. These are where it can shine, or when combined with an ePub interpreter like we mentioned earlier.
If you want to give this project a try, the source code is over on my GitHub. All you need is a display alongside the Feedparser integration on HACS to get started. Maybe a second weekend is all it needs to provide a significantly better experience.