Skip to content
HardwareLab

28.9M-parameter LLM on an ESP32-S3

A 28.9M-parameter language model writing stories on an ESP32-S3, fully offline

Full build guide

28.9M-parameter LLM on an ESP32-S3
No photo in the source; GitHub repository card, served from HardwareLab.The creator’s demo is a GIF in the GitHub README (media/esp32-ple-demo.gif); open the repo to watch it.

By slvDev (GitHub repository owner)

GitHub stars
4.5K (GitHub)

Build sheet

Board
ESP32-S3 N16R8
Other parts
OLED
AI model or service
28.9M-param TinyStories LLM, 4-bit, on-device
Difficulty
Advanced

Parts list

  • ESP32-S3 board (512 KB SRAM, 8 MB PSRAM, 16 MB flash) [9]
  • 128×64 I2C OLED (SH1106 or SSD1306) (1.3" usually SH1106, 0.96" usually SSD1306) [optional] [10]
  • arduino-cli [11]
  • uv [12]

Prices and notes: Parts and requirements

Steps

Skill level
Advanced
  1. scripts/fetch_model.sh tinystories (download, verify, install into artifacts/)
  2. scripts/deploy.sh tinystories (generate headers, run gates, compile, flash) [17]

Full instructions: View original

Like this build? Get the weekly builds

Get the best new AI hardware builds every Thursday. One email a week, free.

Built something? Submit a build

Overview

Uses Gemma-style Per-Layer Embeddings: the 25M-parameter embedding table stays in flash, so a 28.9M-parameter model generates text at 9.88 tokens/s on the ESP32-S3 itself. Covered by Tom’s Hardware.

What it does

  • Runs a 28.9 million parameter language model on the ESP32-S3 itself, with nothing sent to a server, showing the text on a small screen at 9.88 tokens per second. [1]
  • Fits because most of the model, a 25M-parameter embedding table, stays in flash and only about 450 bytes are read per token (Per-Layer Embeddings, from Google’s Gemma 3n). [2]
  • The model is 14.9 MB at 4-bit; activations and norms sit in 512 KB SRAM, the core and output head in 8 MB PSRAM. [3]
  • The same chip also runs part of the fruit fly connectome (48,311 neurons) escaping a spider on screen. [4]

The creator's config

Copied word for word from the creator's repository, at the commit linked in each source.

  • FQBN used by deploy.sh [5]

    FQBN='esp32:esp32:esp32s3:UploadSpeed=921600,USBMode=hwcdc,CDCOnBoot=cdc,UploadMode=default,CPUFreq=240,FlashMode=qio,FlashSize=16M,PartitionScheme=custom,PSRAM=opi,DebugLevel=info'
  • OLED wiring (display.h) [6]

    //   DISPLAY_OLED_I2C (default) -- 128x64 I2C mono OLED. 4 wires:
    //     GND->GND, VCC->3V3, SCL->GPIO46, SDA->GPIO18. Set OLED_CONTROLLER to match
    //     the panel: 1.3" is usually SH1106, 0.96" usually SSD1306. Wrong choice ->
    //     top row correct, rest noise (SH1106-as-SSD1306) -> flip the define.

Use-case ideas

  • Run the other published models on the same board: Barista (espresso question answering) or the fly connectome, with the same two commands. [7]
  • A reference for Per-Layer Embeddings on microcontroller memory: the reusable architecture is in src/ and the training and quantization code in research/tinystories/. [8]

Parts and requirements

PartSpec / notesNeededPrice
ESP32-S3 board [9]512 KB SRAM, 8 MB PSRAM, 16 MB flashRequired
128×64 I2C OLED (SH1106 or SSD1306) [10]1.3" usually SH1106, 0.96" usually SSD1306 · Optional screen; a 2.0" ST7789 SPI TFT is also supportedOptional
arduino-cli [11]deploy.sh stops if arduino-cli is missingRequired
uv [12]deploy.sh runs two of its header tools through uvRequired
  • Network: Only for fetch_model.sh, which downloads the model; deploy.sh downloads no model
  • Model files from Hugging Face (slvDev/esp32-ai-tinystories), checked against a pinned SHA-256 by fetch_model.sh
  • Requirements source: [13]

How it works

Per-Layer Embeddings keep the 25M-parameter embedding table in flash; only the rows each token needs are read, while the dense core sits in PSRAM. 9.88 tokens/s on a small screen.

LayerComponentRuns on
Language model28.9M-parameter model, 4-bit (14.9 MB) [14]ESP32-S3
Embedding table25M parameters in flash, ~450 B read per token [15]Flash
RuntimeFirmware compiled at global -O3 by deploy.sh [16]Board

Setup effort

Skill level
Advanced

Two commands: one downloads and verifies the model, the other compiles and flashes it. [17]

Limits and caveats

  • Trained on TinyStories: it writes short simple stories and will not answer questions, follow instructions, write code or know facts. [18]
  • The board holds one model at a time; deploying replaces it. [19]

Why it's interesting

The trick here is memory layout, not raw speed. The ESP32-S3 has 512 KB of SRAM, 8 MB of PSRAM and 16 MB of flash, so activations and norms stay in SRAM, the dense core and output head sit in PSRAM, and a 25-million-parameter embedding table stays in flash, where only about 450 bytes are pulled per token. The README credits this Per-Layer Embeddings idea to Google's Gemma 3n. The result is 9.88 tokens per second end to end, a 14.9 MB model at 4-bit, and no network connection at all. It is also clear about limits: trained on TinyStories, it writes short simple stories and will not answer questions, follow instructions or know facts. The same chip and repo also run the fruit fly connectome demo listed separately on HardwareLab.

Source: github.com/slvDev/esp32-ai#readme

Hardware

  • ESP32-S3 N16R8
  • OLED

More ESP32 builds

Credits and links

Sources

  1. github.com/slvDev/esp32-ai/blob/52ebcbbd905270592c1d415efd1128d04d0fa365/README.md?plain=1#L10-L14
  2. github.com/slvDev/esp32-ai/blob/52ebcbbd905270592c1d415efd1128d04d0fa365/README.md?plain=1#L38-L46
  3. github.com/slvDev/esp32-ai/blob/52ebcbbd905270592c1d415efd1128d04d0fa365/README.md?plain=1#L22-L35
  4. github.com/slvDev/esp32-ai/blob/52ebcbbd905270592c1d415efd1128d04d0fa365/README.md?plain=1#L16-L18
  5. github.com/slvDev/esp32-ai/blob/52ebcbbd905270592c1d415efd1128d04d0fa365/scripts/deploy.sh#L187
  6. github.com/slvDev/esp32-ai/blob/52ebcbbd905270592c1d415efd1128d04d0fa365/firmware/esp32_tinystories/display.h#L4-L7
  7. github.com/slvDev/esp32-ai/blob/52ebcbbd905270592c1d415efd1128d04d0fa365/README.md?plain=1#L108-L124
  8. github.com/slvDev/esp32-ai/blob/52ebcbbd905270592c1d415efd1128d04d0fa365/README.md?plain=1#L138-L144
  9. github.com/slvDev/esp32-ai/blob/52ebcbbd905270592c1d415efd1128d04d0fa365/README.md?plain=1#L25
  10. github.com/slvDev/esp32-ai/blob/52ebcbbd905270592c1d415efd1128d04d0fa365/firmware/esp32_tinystories/display.h#L1-L8
  11. github.com/slvDev/esp32-ai/blob/52ebcbbd905270592c1d415efd1128d04d0fa365/scripts/deploy.sh#L190-L191
  12. github.com/slvDev/esp32-ai/blob/52ebcbbd905270592c1d415efd1128d04d0fa365/README.md?plain=1#L130-L133
  13. github.com/slvDev/esp32-ai/blob/52ebcbbd905270592c1d415efd1128d04d0fa365/README.md?plain=1#L111-L133
  14. github.com/slvDev/esp32-ai/blob/52ebcbbd905270592c1d415efd1128d04d0fa365/README.md?plain=1#L22-L28
  15. github.com/slvDev/esp32-ai/blob/52ebcbbd905270592c1d415efd1128d04d0fa365/README.md?plain=1#L48-L54
  16. github.com/slvDev/esp32-ai/blob/52ebcbbd905270592c1d415efd1128d04d0fa365/firmware/esp32_tinystories/README.md?plain=1#L14-L18
  17. github.com/slvDev/esp32-ai/blob/52ebcbbd905270592c1d415efd1128d04d0fa365/README.md?plain=1#L119-L125
  18. github.com/slvDev/esp32-ai/blob/52ebcbbd905270592c1d415efd1128d04d0fa365/README.md?plain=1#L58-L63
  19. github.com/slvDev/esp32-ai/blob/52ebcbbd905270592c1d415efd1128d04d0fa365/README.md?plain=1#L124-L125

This page is a short summary written by HardwareLab. Build steps, code and photos belong to the creator; follow “View original” for the full project.

Featured? Add this badge

Built this? Paste the badge into your README. It links back to this page.

Featured on HardwareLab

Markdown

[![Featured on HardwareLab](https://hardwarelab.dev/badge.svg)](https://hardwarelab.dev/projects/esp32-28m-llm/?utm_source=badge&utm_medium=referral&utm_campaign=launch_2w)

HTML

<a href="https://hardwarelab.dev/projects/esp32-28m-llm/?utm_source=badge&utm_medium=referral&utm_campaign=launch_2w"><img src="https://hardwarelab.dev/badge.svg" alt="Featured on HardwareLab" width="188" height="20"></a>