Journal

/

Built a 0 MB streaming e-reader

Built an e-reader using an ESP-WROOM-32, a 7.5” Waveshare e-paper display, and a three-button interface (prev/next/sleep).

ESP-32-WROOM has 512 KB SRAM and 4 MB flash. Internal flash is unsuitable for storing books due to P/E cycle limit. Used HTTP Range requests to stream them on-demand. Saved reading progress to RTC memory to survive deep sleep without flash wear.

Rasterized PDFs into sequences of bitmaps. 1 byte = 8 pixels, 1 page = 48 KB (display resolution), headerless. Optimized for 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);

Implemented a three-page circular buffer (prev/current/next)—maximum possible with 512 KB. GPIO interrupts triggered by button presses cycle the buffer, update the screen, and prefetch the next page.

c_page_num++;
pg.page_num = c_page_num + 2;
pg.page_buf = pages[(c_page_num + 1) % PAGE_LEN];

xSemaphoreGive(mutex);
xQueueSend(http_evt_queue, &pg, portMAX_DELAY);

epd_draw_async(pages[c_page_num % PAGE_LEN], PAGE_SIZE);
epd_draw_await();

System isn’t as responsive as I’d hoped. Scheduling GPIO, SPI, and HTTP tasks on a single core causes input lag. Pinned the GPIO/SPI tasks to one core and the HTTP task to the other.

Better, but screen updates block user input; page turning feels sluggish.

Moved the SPI buffers to DMA and made the transfers async, hoping to shave off a few more cycles.

Can’t think of anything else. Led to Etlas.

Commit: 7f691c4.