esp32.diy

esp32-tinyLLM: Run a 28.9M-Parameter LLM on ESP32-S3

Oct 13, 2026 · 5 min read

Advanced 47 stars 3 forks Rust Apache-2.0 Updated 2026-08-20

The esp32-tinyLLM firmware generating a children's story via the serial CLI, with per-token timing visible in the output.

TL;DR esp32-tinyLLM runs a 28.9M-parameter language model on an ESP32-S3 microcontroller using a Rust inference engine, generating short children's stories at 10.5 tokens per second. It beats the C implementation it was ported from by 28.2% per token while producing byte-identical output.
What you need
  • ESP32-S3
BoardESP32-S3
LanguageRust
Model Size28.9M parameters
Speed10.50 tok/s (10.89 tok/s with --fast-mspi)
LicenseApache-2.0
DifficultyAdvanced

What You Will Build

You will flash a complete Rust-based language model inference engine onto an ESP32-S3 and watch it generate short children's stories directly on the chip — no cloud, no co-processor, no external RAM module.

The model has 28.9 million parameters. The ESP32-S3 has 512 KB of SRAM. Those two facts sound incompatible until you understand the trick: roughly 25 million of those parameters live in a flash-mapped Per-Layer Embedding (PLE) table — the same memory-layout idea used in Google's Gemma 3n — and the remaining weights are stored at 4-bit quantization. The chip never loads the full model into RAM at once; it pages what it needs from flash as it goes.

The result runs at 10.50 tok/s on stock settings, which is 28.2% faster per token than the C reference implementation it was ported from, with byte-identical output. This project is a serious benchmark of what embedded Rust can do on a microcontroller most people think of as a Wi-Fi chip, and it comes with 38 automated tests to prove every claim.

What You Need

Hardware

Software and toolchain

No hardware needed to start. The repo includes a host-side CLI demo and a full test suite you can run on your laptop before touching the board. This is worth doing first — it lets you confirm the model works and compare the Rust and C engines side by side without any flashing involved.

Typical use cases

On-Device Story Generation

Run a generative language model entirely on an ESP32-S3 with no network connection, cloud API, or external compute — just the chip, flash, and a USB cable.

Embedded Rust Benchmarking

A concrete, reproducible case study for comparing Rust and C inference engines on the same microcontroller, with automated digest verification and per-stage timing.

TinyML Research Platform

Explore quantization, PLE memory layouts, and `no_std` model math in a repo that documents every dead end alongside every win, with 38 tests to keep the numbers honest.

Host-Side LLM CLI

Run the same inference engine on your laptop via the demo CLI to prototype or test model changes before committing to a flash cycle.

How It Works

The engine is split into three Rust crates inside the engine/ workspace:

The model was trained on the TinyStories dataset (Eldan & Li 2023) and exported after 4-bit post-training quantization. It writes children's stories. It cannot answer questions or follow instructions — that is by design. The task is narrow enough that 28.9M parameters, squeezed through this memory layout, can do it well at real-time speed.

Every benchmark figure in the README is generated from benchmarks/device.toml by scripts/plot_stages.py. Nothing is typed by hand, so the numbers stay honest as the code evolves.

Build and Flash Steps

Step 1 — Verify the engine on your laptop (no board needed)

Run the full test suite to confirm the Rust engine matches the C reference and passes all golden-output checks:

cd engine/llm-host && cargo test --release

Then try the interactive CLI demo, which runs the model on your host machine:

cd demo && sh run_cli.sh

To compare Rust and C timing on your laptop before touching the board:

scripts/bench_host.py

Step 2 — Flash to the ESP32-S3

Once you have ESP-IDF installed and your board connected, a single script handles the build, flash, serial monitor, and run log:

./scripts/run_device.sh

The script flashes the firmware, opens the monitor, and streams the story output alongside per-token timing. You should see output at around 10.50 tok/s.

Step 3 — Optional: enable the fast memory bus

The repo ships with a second mode that runs the memory bus at 120 MHz instead of the default speed:

./scripts/run_device.sh --fast-mspi

This pushes throughput to 10.89 tok/s (32.9% faster than the C reference). The README notes this requires an experimental ESP-IDF flag and that Espressif documents 120 MHz as temperature-sensitive outside the commercial operating range, so it is not the default. Use it for benchmarking; be cautious in a product.

Step 4 — Confirm byte-identical output

The firmware folds every emitted token ID into a digest and prints it. The expected digest is pinned in model/models.toml and re-verified by llm-host/tests/token_stream_digest.rs. If your run diverges from the reference by even one token, the output says so. This is how the project certifies that a faster build is actually computing the same thing — not a faster approximation.

Extending the Project and Known Limits

What you can explore next

Limits to keep in mind

This model writes stories. It does not answer questions, follow instructions, or hold a conversation. That is not a bug — the TinyStories training domain is intentionally narrow, and that narrowness is what makes 28.9M parameters viable on this hardware. Do not expect ChatGPT behavior from an $8 chip with 512 KB of RAM.

The llm-display crate is listed in the repo layout but marked as not started. Serial output is the only output mode that works today.

120 MHz SPI (--fast-mspi) is documented by Espressif as temperature-sensitive outside the commercial range. Treat it as a benchmark mode rather than a production setting.

Verdict

esp32-tinyLLM is one of the most carefully engineered tinyML projects for the ESP32-S3: every benchmark is reproducible, every optimization is certified byte-identical, and the Rust engine genuinely outperforms the C reference it was built from. It is not a chatbot and does not try to be one — if you want to understand how far a $8 microcontroller can be pushed with modern quantization and Rust, this is the codebase to study.

Sources

github.comBellman281/esp32-tinyLLM — repository & README

Facts in this article come from the project's public README and GitHub metadata at the time of writing. Images belong to their respective owners and link back to the original source.