Offline Wikipedia-lezer voor ESP32 CYD

Geschreven door Alun Morris en Claude Code.

Hardware

Optie 1 — ESP32-2432S028 ("Cheap Yellow Display")

ComponentDetail
BoardESP32-2432S028 (CYD)
Display320×240 ILI9341 (HSPI bus)
TouchXPT2046 resistief (gedeelde HSPI)
SD-kaartVSPI bus — CS=5, MOSI=23, MISO=19, SCK=18
AchtergrondverlichtingGPIO 21

Gebruik de PlatformIO-omgeving cyd (pio run -e cyd -t upload).

Optie 2 — ESP32-C3 + JC2432S024 displaymodule

De JC2432S024 is een losse 2.4″ 320×240 ILI9341 displaymodule met XPT2046 resistieve touch en een SD-kaartsleuf. Deze heeft geen ingebouwde processor — verbind deze met een ESP32-C3 dev board (zoals de SuperMini of DevKitM-1).

De ESP32-C3 heeft één enkele SPI-peripheral, waardoor het display, de SD-kaart en de touch-controller allemaal één SPI-bus delen met aparte chip-select-lijnen.

Module pinESP32-C3 GPIONotities
SCK4Gedeeld door TFT + Touch + SD
MOSI / SDI / TDIN / SDMOSI6Gedeeld
MISO / SDO / TDO / SDMISO5Gedeeld
CS (TFT)7
DC / RS1
RST3V3Hoog aansluiten — wordt niet aangestuurd door firmware
SD_CS10
T_CS3
T_IRQ8PENIRQ — idle HIGH, veilig op bootstrapping pin
LED / BL (backlight)0Of direct op 3V3 aansluiten voor altijd aan zijnde verlichting
VCC3V3
GNDGND

Vermijd GPIO 2 en GPIO 9 voor externe belastingen — dit zijn ESP32-C3 bootstrapping-pinnen. Gebruik de PlatformIO-omgeving c3 (pio run -e c3 -t upload).

Algemene hardware-opmerkingen

  • Eerste boot: De touch-kalibratie wordt automatisch uitgevoerd. Tik op de twee rode kruislijnen wanneer daarom wordt gevraagd. De kalibratie wordt opgeslagen in het flashgeheugen en overgeslagen bij volgende boots.
  • SD-kaart: Een microSD-kaart van minimaal 8 GB is vereist (Simple English Wikipedia neemt ongeveer 7 GB in beslag op de kaart).

Keuze van een ZIM-bestand

De database wordt gebouwd vanuit een Kiwix ZIM-bestand. Deze kunnen worden gedownload via de Wikimedia dumps.

Aanbevolen: Simple English Wikipediawikipediaensimpleallmaxi_YYYY-MM.zim

  • Downloadgrootte: ~3.3 GB, bevat ~285.000 artikelen.
  • Kortere artikelen en eenvoudigere taal — zeer geschikt voor een klein scherm.
  • Bevat afbeeldingen (_maxi variant).
  • Verwerkte grootte is ongeveer 10 GB.

Andere ZIM-bestanden werken ook, maar grotere edities (bijv. de volledige Engelse versie van ~90 GB) kunnen de maximale SD-kaartgrootte overschrijden die de CYD ondersteunt (32 GB is OK, 64 GB kan werken).

Kies de maxi variant (inclusief afbeeldingen). De mini variant laat afbeeldingen weg en bevat slechts de top 50-100k artikelen. In de preprocessor map staat een kleine demo-Wiki over knopen.

Preprocessor

De preprocessor converteert een ZIM-bestand naar het binaire databaseformaat dat door de firmware wordt gelezen.

Waarom een preprocessor nodig is

De ESP32 kan een ZIM-bestand niet direct lezen vanwege verschillende harde beperkingen:

  1. Compressie: ZIM gebruikt zstd cluster-compressie. Het decompresseren van een zstd cluster vereist dat het hele cluster in het RAM wordt geladen. ZIM-clusters zijn doorgaans 1–4 MB, wat de bruikbare heap van de ESP32 (~300 KB) overschrijdt. De preprocessor comprimeert elk artikel individueel met LZ4, wat slechts enkele KB aan werkgeheugen vereist voor decompressie.
  2. Indexstructuur: De indexstructuur van ZIM is te complex voor embedded gebruik. ZIM gebruikt een URL-gesorteerde B-tree-stijl index met entries van variabele lengte. De hier geproduceerde binaire index heeft een vaste breedte (80 bytes per record), is gesorteerd op genormaliseerde titel en is gekoppeld aan een kleine "sparse index" (één entry per 64 artikelen) die volledig in het RAM past (~7 KB). Samen maken ze het mogelijk om titels op te zoeken zonder SD-seeks om het begin van de scan te vinden.
  3. Afbeeldingsformaten: Afbeeldingen moeten in formaten zijn die de ESP32 kan decoderen. De firmware decodeert JPEG via de hardware-versnelde TJpgDec bibliotheek en QOI via een lichtgewicht software-decoder. ZIM slaat afbeeldingen intern op als WebP, wat de ESP32 niet gegarandeerd in het beschikbare RAM kan decoderen. De preprocessor converteert alles naar JPEG (foto's) of QOI (diagrammen/SVGs).
  4. HTML-opschoning: Wikipedia ZIM-bestanden voegen boilerplate footers, navigatie-elementen en complexe class-structuren toe aan elk artikel. De preprocessor verwijdert deze met BeautifulSoup, zodat de minimale HTML-renderer van de firmware alleen de subset van tags hoeft te verwerken die daadwerkelijk in artikelteksten voorkomen.

Waarom de verwerkte map groter is dan het ZIM-bestand

  • Afbeeldingscodering: ZIM gebruikt WebP-afbeeldingen, wat extreem efficiënt is. De preprocessor decodeert WebP en codeert dit opnieuw als JPEG (foto's) of QOI (diagrammen). QOI is lossless en behoudt elke pixel, wat goed is voor de kwaliteit maar veel groter is dan WebP. JPEG met een kwaliteit van 90 is eveneens groter dan WebP bij een gelijkwaardige visuele kwaliteit.
  • Compressieverlies: ZIM gebruikt cross-artikel zstd-compressie. Artikelen worden in grote clusters (1–4 MB) gepakt en samen gecomprimeerd, waardoor herhaalde zinnen en boilerplate over verschillende artikelen heen worden weggefilterd. De preprocessor comprimeert elk artikel individueel met LZ4, waardoor deze cross-artikel redundantie verloren gaat. LZ4 is gekozen vanwege de decompressiesnelheid op de ESP32, niet vanwege de compressieratio.

Vereisten

  • Python 3.9+
  • Afhankelijkheden (vermeld in preprocessor/requirements.txt):

pip install libzim lz4 beautifulsoup4 lxml Pillow cairosvg qoi

  • cairosvg vereist daarnaast de systeem-cairo bibliotheek:
  • Ubuntu/Debian: sudo apt install libcairo2
  • macOS: brew install cairo

De build uitvoeren

cd preprocessor
python3 build_wiki_db.py <input.zim> <output_dir>

Voorbeeld:

python3 build_wiki_db.py --thumb-size 240x159 wikipedia_en_simple_all_maxi_2026-05.zim output_en_simple_all_maxi

Dit proces doorloopt twee fasen:

  1. Pass 1: Indexeert alle artikeltitels en bouwt index.bin, sparseindex.bin en idindex.bin.
  2. Pass 2: Decomprimeert, schoont op (verwijdert ZIM boilerplate) en comprimeert artikelen parallel, wat resulteert in articles_NNNN.dat chunks.
  • Afbeeldingen: Rendert en codeert thumbnails naar img_NNNN.dat chunks.
  • Woordindex: Bouwt wordindex.bin + titleindex.bin voor full-text "contains" zoekopdrachten.

De bouwtijd is ongeveer 30–60 minuten op een moderne PC (gebruikt standaard tot 4 CPU-cores).

Opties

VlagStandaardBeschrijving
--limit N0 (alle)Verwerk alleen de eerste N artikelen (handig voor testen)
--workers NautoAantal parallelle compressie-workers (threads)
--thumb-size WxH320x212Maximale afmetingen van thumbnails in pixels
--jpeg-quality Q90JPEG-kwaliteit voor fotothumbnails, 1–95
--no-imagesoffSla beeldverwerking over
--images-onlyoffBouw alleen de beeldendatabase opnieuw
--word-index-onlyoffBouw alleen de woord/titel index opnieuw
--verboseoffExtra voortgangsoutput

Output-bestanden

Alle bestanden komen in de <output_dir>/ terecht en moeten worden gekopieerd naar een wiki/ map op de root van de SD-kaart.

BestandBeschrijving
index.binVaste-breedte titelindex (binair doorzoekbaar, gesorteerd)
sparse_index.binElke 64e titelkey — geladen in ESP32 RAM voor snelle zoekacties
id_index.binMapt artikel ID → chunk + offset + lengte
index_meta.txtMetadata: aantal artikelen, chunk-grootte, databasenaam
articles_NNNN.datLZ4-gecomprimeerde artikel HTML, gesplitst in chunks van 32 MB
img_index.binMapt afbeelding ID → chunk + offset + lengte
img_NNNN.datGecodeerde afbeelding-thumbnails (JPEG of QOI), gesplitst in chunks van 4 MB
word_index.binWoord → lijst met artikel ID's voor "contains" zoekopdrachten
title_index.binArtikeltitel-index voor "contains" zoekopdrachten

SD-kaart Setup

  1. Formatteer de kaart als FAT32. Een allocatie-eenheid van 64KB is optimaal.
  2. Maak een map wiki/ aan op de root.
  3. Kopieer alle bestanden uit de <output_dir>/ naar /wiki/.

De mappenstructuur moet er als volgt uitzien: /wiki/

  • index.bin
  • sparse_index.bin
  • id_index.bin
  • index_meta.txt
  • articles_0000.dat
  • articles_0001.dat
  • ...
  • img_index.bin
  • img_0000.dat
  • ...
  • word_index.bin
  • title_index.bin

Firmware Build & Upload

De firmware is een PlatformIO-project.

cd firmware
pio run -t upload

Monitor de seriële output op 115200 baud:

pio device monitor

Bij de eerste boot wordt de sparse index gecached naar LittleFS, zodat volgende boots sneller verlopen.

Gebruik

  • Zoeken: Tik op het zoekveld om het toetsenbord te tonen. Typ een zoekterm en druk op GO. Resultaten op basis van prefix-matching verschijnen eerst; een "contains:" scheidingsteken markeert de full-text matches.
  • Navigeren: Tik op een blauwe onderstreepte link om deze te volgen. Tik op < BACK om terug te keren.
  • Scrollen: Swipe omhoog/omlaag, of gebruik de pijlknoppen in de navigatiebalk.
  • Afbeeldingen: Tik op een afbeelding-thumbnail om deze op volledig scherm te bekijken.

Projectstructuur

firmware/ (PlatformIO ESP32 firmware)

  • src/main.cpp: Boot, splash screen.
  • src/ui.cpp: Zoek-, artikel- en beeldweergaven.
  • src/wiki_db.cpp: Toegang tot de SD-database (zoeken, laden, afbeeldingen).
  • src/html_render.cpp: HTML → TFT renderer.
  • src/display.cpp: TFT helpers, UTF-8 transliteratie.
  • src/keyboard.cpp: On-screen QWERTY-toetsenbord.
  • src/touch.cpp: XPT2046 touch-driver.
  • src/config.h: Hardware-pinnen, bestandspaden, constanten.

preprocessor/ (PC-side database builder)

  • buildwikidb.py: Hoofdscript voor de build (ZIM → binaire DB).
  • debug_server.py: Lokale HTTP-server voor browser-gebaseerde preview.
  • outputenknots_maxi/: Klein voorbeeld van een wiki met kleinere afbeeldingen om de grootte te beperken.