---
title: ESP-32 streaming e-reader
date: 2023-10-24
layout: post
project: true
thumbnail: thumb_sm.png
---
Stitched together an e-reader using an ESP-WROOM-32, a 7.5" Waveshare e-paper
display, and a three-button interface (prev/next/sleep).
ESP-WROOM-32 has 512 KB SRAM and 4 MB flash. Internal flash is unsuitable for
storing books due to P/E cycle limit. Streamed pages via HTTP on-demand
instead. Reading progress is saved in RTC memory to survive deep sleep without
flash wear.
Rasterized PDFs into sequences of bitmaps. 1 byte = 8 pixels, 1 page = 48 KB
(the display resolution), headerless; optimized for HTTP Range requests without
server-side logic:
```
int r0 = ((page_n - 1) * PAGE_SIZE);
int rn = page_n * PAGE_SIZE - 1;
int n = snprintf(NULL, 0, "bytes=%d-%d", r0, rn) + 1;
char *buf = malloc(sizeof(char) * n);
snprintf(buf, n, "bytes=%d-%d", r0, rn);
esp_http_client_set_header(http_client, "Range", buf);
esp_http_client_perform(http_client);
```
A circular buffer stores three pages—maximum possible with the 512 KB.
Button-triggered GPIO interrupts cycle the buffer, update the screen, and
prefetch the next page.
Reader isn't as responsive as I'd hoped. Scheduling GPIO, SPI, and HTTP tasks
on a single core causes input lag.
Pinned the GPIO and SPI tasks to one core and the HTTP task to the other.
Better, but screen updates block user input. Page turning feels sluggish.
Moved SPI buffers to DMA and made transfers async, hoping to offload the CPU.
Can't think of anything else. Led to [Etlas](../etlas/).
Commit: 7f691c4.