Baktiyar Ablaikhan
λ 417.3 nm

NanoTranslate-S3

Offline Russian-to-English neural machine translation designed around an ESP32-S3 microcontroller.

Period
2026
Role
Independent researcher
Status
Preprint · not peer-reviewed
parameters in the default model
999,040
parameters in the default model
entry byte-piece vocabulary
1,536
entry byte-piece vocabulary
targets reproduced by PyTorch and the INT8 C++ runtime
20 / 20
targets reproduced by PyTorch and the INT8 C++ runtime

Abstract

We present NanoTranslate-S3, an offline Russian-to-English neural machine translation system co-designed for the ESP32-S3 N16R8 microcontroller. The target device combines two Xtensa LX7 cores running at up to 240 MHz with 512 KB on-chip SRAM, 16 MB flash, and 8 MB octal PSRAM. Rather than compressing a conventional desktop translation model after training, we jointly design the vocabulary, Transformer width and depth, numerical representation, binary layout, decoder, cache strategy, inference kernels, and physical user interface around the microcontroller. The default student is a 999,040-parameter pre-norm encoder–decoder Transformer with width 128, three encoder layers, two decoder layers, four attention heads, FFN width 256, and a 1,536-entry lossless UTF-8 byte-piece vocabulary. Weight matrices use symmetric per-row INT8 quantization with FP32 scales and dynamically quantized INT8 activations. The portable C++ runtime uses incremental autoregressive decoding, precomputed cross-attention keys and values, a tied output embedding, and a constrained English output candidate set; on ESP32-S3 the signed-int8 dot product uses PIE vector instructions. We derive exact parameter, storage, cache, and dense-MAC budgets and define a CRC-protected memory-mappable model format. A measured end-to-end correctness experiment verifies the full path from PyTorch training through Q8 export to C++ inference: on a 20-pair memorization task the loss falls from 5.7639 to 0.04128 and both PyTorch and the quantized C++ runtime reproduce all 20 training targets, including the Russian greeting mapped to hello. We explicitly separate measured results from exact analytical results, derived estimates, and future physical-board measurements; no ESP32 latency or held-out BLEU result is invented.

1 Introduction

Modern neural machine translation assumes resources that inexpensive microcontrollers lack: large DRAM, wide SIMD or GPU matrix engines, large vocabularies, long sequences and enough compute to project decoder states over tens of thousands of output symbols at every step. The ESP32-S3 sits at a different operating point: two Xtensa LX7 cores up to 240 MHz, 512 KB on-chip SRAM and vector instructions for DSP/neural workloads [Espressif Systems, 2026]. The N16R8 module adds 16 MB external flash and 8 MB octal PSRAM.

A direct port of a normal Russian-to-English model is inappropriate. The current Helsinki-NLP OPUS-MT RU–EN model is a Marian encoder–decoder with six encoder and six decoder layers, width 512, FFN width 2048 and a vocabulary of roughly 62.5k tokens; the published PyTorch checkpoint is roughly 307 MB [Helsinki-NLP, 2026; Tiedemann and Thottingal, 2020]. That is not a close fit requiring a small optimization; it is a different computational regime.

The paper therefore asks a hardware-first question: what translation model would we design if the ESP32-S3 N16R8 were the primary target from the beginning? The product requirement is deliberately narrow: the user enters one normal Russian sentence (usually 5–20 words) on a 240×240 display with a five-way joystick, presses translate, and receives an English sentence — no Wi-Fi, phone, API or cloud at inference time. The central hypothesis is that microcontroller NMT is a co-design problem: vocabulary size, output-projection cost, sequence limits, flash/PSRAM traffic, KV-cache precision, row alignment, decoding strategy and UI memory matter alongside model width [Lin et al., 2020].

Contributions.

  1. a compact RU→EN encoder–decoder Transformer family whose default model has exactly 999,040 trainable parameters;
  2. FlashPiece, a lossless UTF-8 byte-piece tokenizer with guaranteed raw-byte fallback and a 1,536-entry default vocabulary;
  3. a sequence-level distillation pipeline using a stronger translation teacher [Kim and Rush, 2016];
  4. a row-wise Q8 model format with CRC32, structural checks and 16-byte aligned tensor storage;
  5. a portable C++ runtime with incremental decoding and an ESP32-S3 PIE signed-int8 matvec path;
  6. an English-only constrained tied output head that removes source-only pieces from repeated decoder scoring;
  7. a complete ST7789 plus joystick firmware interface;
  8. exact parameter, storage, cache and dense-MAC derivations together with a reproducible measured Q8 parity test;
  9. a strict evaluation protocol that leaves physical-board BLEU and latency blank until measured.

2 Hardware and deployment envelope

2.1 ESP32-S3 N16R8

Espressif's ESP-DL documentation presents W8A8 as its highest-speed default quantization mode, and ESP-NN includes S3-optimized neural kernels that exploit vector instructions; together these motivate an INT8-first baseline. The memory hierarchy matters:

  • internal SRAM — scarce, low-latency, suitable for hot activations and DMA buffers;
  • PSRAM — far larger, but slower and shared through the cache/memory subsystem;
  • flash — persistent storage for cold model data and the memory-mapped .s3mt image.

Fitting within total memory is therefore insufficient; the design must minimize repeatedly streamed bytes per generated token.

2.2 System path

  1. 015-way joystick
  2. 02ST7789 UI
  3. 03FlashPiece tokenizer
  4. 04Q8 encoder
  5. 05incremental decoder
  6. 06constrained tied head
  7. 07English output

↺ Then back to the screen: ST7789 UI

Figure 1. End-to-end offline translation path.Original ↗

3 Related work

The student follows the encoder–decoder Transformer [Vaswani et al., 2017]. Subword NMT showed the value of subword units for open-vocabulary translation [Sennrich et al., 2016]; FlashPiece adapts the idea to a microcontroller by guaranteeing coverage with all 256 byte values and a minimal greedy decoder. Sequence-level knowledge distillation replaces ambiguous human targets with teacher translations [Kim and Rush, 2016] — attractive for a model near one million parameters. Tied input/output embeddings reduce storage [Press and Wolf, 2017]; RMSNorm removes explicit mean centering [Zhang and Sennrich, 2019].

Integer quantization is standard for efficient inference [Jacob et al., 2018], and low-bit NMT can reach 4-bit precision [Aji and Heafield, 2019], motivating a Q4 roadmap — but the Q4 path is not labelled as implemented. CTC non-autoregressive translation removes token-by-token decoder dependencies [Libovický and Helcl, 2018], yet realistic evaluation shows NAR is not automatically faster than an optimized autoregressive system at small batch sizes [Helcl et al., 2022]. Deployment batch size here is exactly one, so the default path is autoregressive and CTC remains experimental. MCUNet established model/runtime co-design on microcontrollers [Lin et al., 2020]; ESP32-S3 community runtimes show transformer-like workloads can use PIE signed-int8 vector operations [Dory, 2026; Bareiro, 2026]. NanoTranslate-S3 applies these ideas to sequence-to-sequence translation.

4 Problem formulation

Normalized Russian input and English target:

x=(x1,…,xS),S≤Smax⁡=64,y=(y1,…,yT),T≤Tmax⁡=48.x = (x_1,\dots,x_S),\quad S \le S_{\max} = 64, \qquad y = (y_1,\dots,y_T),\quad T \le T_{\max} = 48.

The autoregressive model defines

pθ(y∣x)=∏t=1Tpθ(yt∣y<t,x).p_\theta(y \mid x) = \prod_{t=1}^{T} p_\theta(y_t \mid y_{<t}, x).

Deployment is multi-objective. For architecture aa and numeric format qq:

min⁡a,q  LMT(a,q)+λLL1(a,q)+λMM(a,q)s.t.Bflash≤16 MB,  BPSRAM≤8 MB,  Bhot≤Binternal,  S≤64,  T≤48,\min_{a,q}\; L_{MT}(a,q) + \lambda_L L_1(a,q) + \lambda_M M(a,q) \quad\text{s.t.}\quad B_{\text{flash}} \le 16\,\text{MB},\; B_{\text{PSRAM}} \le 8\,\text{MB},\; B_{\text{hot}} \le B_{\text{internal}},\; S \le 64,\; T \le 48,

where L1L_1 is batch-one wall-clock latency. No arbitrary weights are assigned to these terms; the paper derives the dominant storage/compute terms and defines a physical benchmark protocol instead.

5 FlashPiece tokenizer

Lossless byte foundation. FlashPiece operates on normalized UTF-8 bytes. IDs 0–3 are PAD, BOS, EOS and UNK; the next 256 IDs represent every possible byte, so any UTF-8 input is representable even if no learned multi-byte piece matches. Additional pieces are frequent byte n-grams from the distilled training corpus, ranked by approximate byte savings

score(p)=f(p) (∣p∣−1).\text{score}(p) = f(p)\,(|p| - 1).

The vocabulary is built only from training data; validation and test references never influence token selection.

Greedy encoding. At byte position ii the encoder searches only pieces beginning with bib_i and selects the longest match,

p⋆=arg⁡max⁡p∈Vbi∣p∣s.t.bi:i+∣p∣−1=p.p^\star = \arg\max_{p \in V_{b_i}} |p| \quad \text{s.t.}\quad b_{i:i+|p|-1} = p .

The C++ runtime precomputes 256 first-byte buckets at load time, turning a vocabulary-wide scan per byte into a small bucket search.

Vocabulary as a hardware parameter. A larger vocabulary shortens sequences but increases output-head work. Scoring CC allowed candidates at each of TT steps with width dd costs

Mhead=T C dM_{\text{head}} = T\,C\,d

dot-product MACs, so vocabulary size is simultaneously an NLP and a systems variable.

6 Student Transformer

The default configuration is

(V,d,f,H,Le,Ld,Smax⁡,Tmax⁡)=(1536, 128, 256, 4, 3, 2, 64, 48).(V, d, f, H, L_e, L_d, S_{\max}, T_{\max}) = (1536,\,128,\,256,\,4,\,3,\,2,\,64,\,48).

All dense layers are bias-free; source and target positions use separate learned embeddings.

RMSNorm and residual structure. For h∈Rdh \in \mathbb{R}^d and learned gain gg,

RMSNorm⁡(h)=g⊙hd−1∑i=1dhi2+ϵ,ϵ=10−5.\operatorname{RMSNorm}(h) = g \odot \frac{h}{\sqrt{d^{-1}\sum_{i=1}^{d} h_i^2 + \epsilon}}, \qquad \epsilon = 10^{-5}.

An encoder block is u=h+MHA⁡(RMSNorm⁡(h))u = h + \operatorname{MHA}(\operatorname{RMSNorm}(h)), h′=u+W2 SiLU⁡(W1RMSNorm⁡(u))h' = u + W_2\,\operatorname{SiLU}(W_1 \operatorname{RMSNorm}(u)). A decoder block adds causal self-attention and cross-attention:

u=h+MHA⁡self(RMSNorm⁡(h)),v=u+MHA⁡cross(RMSNorm⁡(u),He),h′=v+W2 SiLU⁡(W1RMSNorm⁡(v)).u = h + \operatorname{MHA}_{\text{self}}(\operatorname{RMSNorm}(h)),\quad v = u + \operatorname{MHA}_{\text{cross}}(\operatorname{RMSNorm}(u), H_e),\quad h' = v + W_2\,\operatorname{SiLU}(W_1 \operatorname{RMSNorm}(v)).

Attention. With head width dh=d/Hd_h = d/H,

Attn⁡(Q,K,V)=softmax⁡ ⁣(QK⊤dh+M)V,\operatorname{Attn}(Q,K,V) = \operatorname{softmax}\!\left(\frac{QK^\top}{\sqrt{d_h}} + M\right) V,

where MM is the causal mask for decoder self-attention.

Tied and constrained output head. With token embedding matrix E∈RV×dE \in \mathbb{R}^{V\times d}, the classifier reuses EE: ℓt(j)=Ej⊤ht/d\ell_t(j) = E_j^\top h_t / \sqrt{d}. Because deployment is strictly RU→EN, the runtime precomputes

C={EOS}∪{ j:bytes(j)⊆[0x20,0x7E] }C = \{\text{EOS}\} \cup \{\, j : \text{bytes}(j) \subseteq [\texttt{0x20}, \texttt{0x7E}] \,\}

and scores only IDs in CC. This is not algebraically equivalent to full-vocabulary decoding — if a removed source-language token would otherwise win, the output changes — so it is described as constrained decoding. Training targets are normalized to printable ASCII; evaluation preserves raw references so the normalization penalty can be measured.

7 Training and distillation

Data. The recommended starting corpus is OPUS-100 [Zhang et al., 2020; Helsinki-NLP, 2026]. The downloader preserves the train/validation/test separation, creates a device-normalized English view and separately saves raw validation and test references.

Sequence-level teacher targets. For teacher distribution qϕq_\phi, y^=arg⁡max⁡yqϕ(y∣x)\hat y = \arg\max_y q_\phi(y \mid x) (approximated by beam search), and the student minimizes

LKD=−∑tlog⁡pθ(y^t∣y^<t,x).\mathcal{L}_{KD} = -\sum_t \log p_\theta(\hat y_t \mid \hat y_{<t}, x).

The reference implementation uses Helsinki-NLP/opus-mt-ru-en as a workstation-side teacher; the teacher never runs on the ESP32. FlashPiece is built after distillation, on Russian source plus teacher English, so a fixed vocabulary budget is spent on the exact target distribution the student sees.

8 Row-wise Q8 inference

Weight quantization. For row wr∈Rnw_r \in \mathbb{R}^n:

sr=max⁡j∣wrj∣127,qrj=clip⁡(round⁡(wrj/sr), −127, 127).s_r = \frac{\max_j |w_{rj}|}{127}, \qquad q_{rj} = \operatorname{clip}\big(\operatorname{round}(w_{rj}/s_r),\,-127,\,127\big).

Each row stores one FP32 scale and nn signed bytes; a zero row receives scale one. Activations are quantized the same way with sx=max⁡j∣xj∣/127s_x = \max_j |x_j| / 127, and the reconstructed matvec is

y^r=srsx∑j=1nqrj qjx,\hat y_r = s_r s_x \sum_{j=1}^{n} q_{rj}\, q^x_j ,

with the inner sum accumulated in INT32.

Rounding error. Writing wj=srqjw+δwjw_j = s_r q^w_j + \delta w_j and xj=sxqjx+δxjx_j = s_x q^x_j + \delta x_j with ∣δwj∣≤sr/2|\delta w_j| \le s_r/2, ∣δxj∣≤sx/2|\delta x_j| \le s_x/2,

w⊤x−y^=∑j(srqjw δxj+sxqjx δwj+δwj δxj),∣e∣≤sx2∑j∣srqjw∣+sr2∑j∣sxqjx∣+n srsx4.w^\top x - \hat y = \sum_j \big(s_r q^w_j\,\delta x_j + s_x q^x_j\,\delta w_j + \delta w_j\,\delta x_j\big), \qquad |e| \le \frac{s_x}{2}\sum_j |s_r q^w_j| + \frac{s_r}{2}\sum_j |s_x q^x_j| + \frac{n\, s_r s_x}{4}.

This is not a quality guarantee; it motivates later calibration and QAT experiments.

Why Q8 first. W8A8 is ESP-DL's fastest general quantized mode and ESP-NN has S3-specific int8 kernels, so Q8 is a simple, verifiable baseline. Q4 reduces weight traffic, but only helps latency if reduced memory traffic outweighs unpacking overhead; it is treated as a hypothesis until benchmarked.

9 Model binary format

The exporter produces one memory-mappable .s3mt file. A fixed 128-byte little-endian header stores magic, version, architecture dimensions, special token IDs, tokenizer bytes, payload bytes, CRC32 and total length; tokenizer pieces follow, then tensors in fixed architecture order, with rows and scale blocks 16-byte aligned. Before exposing tensor pointers the C++ loader checks CRC32, all lengths, vocabulary bounds, width alignment, FFN bounds, head divisibility, layer counts, sequence limits, special IDs, every tensor shape and absence of trailing data — corruption becomes a deterministic load error instead of undefined memory access.

CRC32-protected payload
128-B header
tokenizer
embeddings
encoder
decoder
norm
Figure 2. Logical .s3mt layout.Original ↗

10 Portable runtime and ESP32-S3 specialization

Parity-first implementation. The same C++ parser, tokenizer, encoder, decoder and output projection are compiled for a host CLI and for the ESP-IDF firmware, so exporter/runtime bugs reproduce without a flash cycle.

PIE int8 dot product. The S3 path uses aligned 128-bit vector loads and signed int8 multiply-accumulate, a=∑j=1nqjxqjwa = \sum_{j=1}^{n} q^x_j q^w_j, processing 16 signed bytes per vector step with a scalar fallback — the same class of optimization as ESP-NN and public S3 transformer runtimes [Espressif Systems, 2026; Dory, 2026].

Incremental decoder. For each decoder layer the runtime caches self-attention K1:t(ℓ),V1:t(ℓ)K^{(\ell)}_{1:t}, V^{(\ell)}_{1:t}, so previous projections are never recomputed; cross-attention keys and values are computed once before generation. Each step evaluates only the current token's self Q/K/V, cross Q, output projections, FFN and tied head.

11 Exact analytical budgets

Parameter formula.

P=Vd+(Sm+Tm)d+Le(4d2+2df)+Ld(8d2+2df)+(2Le+3Ld+1)d.P = Vd + (S_m + T_m)d + L_e(4d^2 + 2df) + L_d(8d^2 + 2df) + (2L_e + 3L_d + 1)d .

For the base preset: Ptok=1536⋅128=196,608P_{\text{tok}} = 1536 \cdot 128 = 196{,}608; Ppos=(64+48)⋅128=14,336P_{\text{pos}} = (64+48)\cdot 128 = 14{,}336; Penc=3 (4⋅1282+2⋅128⋅256)=393,216P_{\text{enc}} = 3\,(4\cdot128^2 + 2\cdot128\cdot256) = 393{,}216; Pdec=2 (8⋅1282+2⋅128⋅256)=393,216P_{\text{dec}} = 2\,(8\cdot128^2 + 2\cdot128\cdot256) = 393{,}216; Pnorm=13⋅128=1,664P_{\text{norm}} = 13 \cdot 128 = 1{,}664 — hence P=999,040P = 999{,}040.

Table 1. Exact preset budgets at vocabulary 1,536. Q4 is a storage estimate only.

PresetddLe/LdL_e/L_dParamsQ8 KiBQ4 est. KiB
Tiny962/2527,904538.6281.3
Base1283/2999,0401008.4521.4
Quality1604/21,699,6801704.6875.8

Serialized Q8 storage. All non-norm values are quantized: Q=P−Pnorm=997,376Q = P - P_{\text{norm}} = 997{,}376. The number of row scales is R=V+Sm+Tm+Le(5d+f)+Ld(9d+f)=7,152R = V + S_m + T_m + L_e(5d + f) + L_d(9d + f) = 7{,}152. Ignoring small headers, tokenizer strings and padding,

BQ8=Q+4R+4Pnorm=1,032,640 bytes=1008.44 KiB,BQ4est=⌈Q/2⌉+4R+4Pnorm=533,952 bytes=521.44 KiB.B_{Q8} = Q + 4R + 4P_{\text{norm}} = 1{,}032{,}640\ \text{bytes} = 1008.44\ \text{KiB}, \qquad B^{\text{est}}_{Q4} = \lceil Q/2 \rceil + 4R + 4P_{\text{norm}} = 533{,}952\ \text{bytes} = 521.44\ \text{KiB}.

No Q4 speed claim is made.

Mutable memory. The maximum FP32 encoder state is Benc=Smd⋅4B_{\text{enc}} = S_m d \cdot 4 = 32 KiB. FP32 decoder self/cross caches consume Bcache=Ld [2Tmd+2Smd]⋅4=229,376B_{\text{cache}} = L_d\,[2T_m d + 2S_m d]\cdot 4 = 229{,}376 bytes = 224 KiB. The RGB565 240×240 framebuffer uses 115,200 bytes (112.5 KiB) and the 20-row DMA strip 9,600 bytes (9.375 KiB).

Dense MAC model. Encoder: Me=Le(4Sd2+2S2d+2Sdf)M_e = L_e(4Sd^2 + 2S^2 d + 2Sdf); cross-attention K/V precompute: MxKV=Ld(2Sd2)M_{xKV} = L_d(2Sd^2); decoder step tt: Md,t=Ld(6d2+2df+2d(t+S))M_{d,t} = L_d(6d^2 + 2df + 2d(t+S)); output head as above. At representative S=32S = 32, T=24T = 24, C=1536C = 1536:

TermMACs
MeM_e13.369344 M
MxKVM_{xKV}2.097152 M
MdecoderM_{\text{decoder}}8.411136 M
MheadM_{\text{head}}4.718592 M
Total28.596224 M

The output head alone contributes about 16.5% of these dense MACs and repeatedly streams embedding rows, which makes candidate pruning attractive.

02468105121,0241,5362,0482,5603,072Vocabulary size →Output-head MACs (millions)
Figure 3. Output projection cost for T = 24, d = 128 when every vocabulary row is scored. The deployed constrained head scores only English-compatible IDs.
Data
VocabularyOutput-head MACs
5121,572,864
7682,359,296
1,0243,145,728
1,5364,718,592
2,0486,291,456
3,0729,437,184
Original ↗

Roofline-style interpretation. A useful lower bound is

L≳max⁡ ⁣(MΠMAC, Breadβmem)+Lsoftmax+Lcontrol,L \gtrsim \max\!\left(\frac{M}{\Pi_{\text{MAC}}},\ \frac{B_{\text{read}}}{\beta_{\text{mem}}}\right) + L_{\text{softmax}} + L_{\text{control}},

where ΠMAC\Pi_{\text{MAC}} and βmem\beta_{\text{mem}} must be measured on the actual board — host values are not substituted. The model explains why Q4 may help a bandwidth-bound configuration and why a second core may not help if both cores contend for the same external-memory path.

12 Physical interface

The firmware renders a six-row Russian keyboard and input/output text on a 240×240 ST7789; a five-way active-low joystick uses internal pull-ups. Example (configurable) pins:

SignalDefault GPIODirection
LCD MOSI11MCU→LCD
LCD SCLK12MCU→LCD
LCD CS/DC/RST/BL10/9/8/7MCU→LCD
Joystick U/D/L/R4/5/6/13input
Joystick press14input

These are examples, not universal board wiring. The framebuffer lives in PSRAM; a 20-row DMA-capable strip lives in internal RAM, and because ESP-IDF LCD transfers are asynchronous the code waits for the colour-transfer-complete callback before overwriting the strip — avoiding corruption while SPI DMA still owns the buffer.

13 Measured correctness experiment

Evidence categories. The paper separates (1) measured: host compilation, tokenizer tests, toy training, Q8 export, C++ parity, CRC rejection; (2) exact analytical: parameter count, Q8 bytes, cache bytes, framebuffer bytes, dense MACs; (3) derived estimate: packed-Q4 model bytes; (4) unverified: held-out full-model BLEU/chrF/COMET, physical ESP32 latency, tokens/s, energy, thermal behaviour.

Toy memorization and Q8 parity. A deterministic script uses 20 small RU–EN pairs, a 320-entry FlashPiece tokenizer, one encoder and one decoder layer, d=32d = 32, f=64f = 64, four heads and seed 0. It trains for 600 steps, recording loss every ten updates; cross-entropy falls from 5.7639008 to 0.04127665. PyTorch greedy decoding reproduces 20/20 training targets; the exported row-wise-Q8 model loaded by the C++ runtime also reproduces 20/20. The Russian greeting in the smoke set maps to hello. A deliberately flipped payload byte makes the loader fail with a CRC32 mismatch. This demonstrates end-to-end implementation parity; it is intentionally not a held-out NMT benchmark.

0.050.10.20.51250100200300400500600Training step →Cross-entropy (log scale)
Figure 4. Measured toy memorization loss, recorded every ten steps by the reproducibility script.
Data
StepCross-entropy
05.7639
105.4712
205.1539
304.8221
404.4946
504.1889
603.9205
703.6993
803.5268
903.3985
1003.3071
1103.2421
1203.1923
1303.1514
1403.1103
1503.0967
1603.0487
1702.9956
1802.9447
1902.8740
2002.7852
2102.7012
2202.5907
2302.4653
2402.3381
2502.1874
2602.0195
2701.9039
2801.6839
2901.5199
3001.3273
3101.1521
3200.9982
3300.8446
3400.8078
3500.6274
3600.5333
3700.4494
3800.3804
3900.3230
4000.2747
4100.2392
4200.2596
4300.1992
4400.1648
4500.1415
4600.1246
4700.1113
4800.1003
4900.0910
5000.0831
5100.0763
5200.0703
5300.0651
5400.0605
5500.0564
5600.0527
5700.0494
5800.0464
5900.0437
6000.0413
Original ↗

Table 2. Current verification status.

CheckResult
FlashPiece unit testspass
Host C++ buildpass
PyTorch toy train exact20/20
Q8 C++ toy train exact20/20
Loss5.7639 → 0.04128
Corrupt modelrejected by CRC
Physical ESP32 BLEU/latencynot yet measured

14 Full evaluation protocol

Translation quality. Final checkpoints should be evaluated on the frozen OPUS-100 test split with SacreBLEU [Post, 2018], chrF [Popović, 2015] and offline COMET [Rei et al., 2020], against both the device-normalized English view and the preserved raw references.

Architecture sweep. A controlled sweep holds teacher, train split, seeds, optimizer and test set fixed:

RunVVddLe/LdL_e/L_dffGoal
T1768962/2192speed floor
B110241283/2256vocab tradeoff
B215361283/2256default
B320481283/2256shorter sequences
Q115361604/2320quality ceiling

Physical-board benchmark. For every exported model: same N16R8 board, CPU clock, flash/PSRAM modes, compiler flags and firmware commit; at least five warm-up translations; a fixed battery of short, medium and long sentences; record source/output tokens, encoder and decoder microseconds, total latency and tokens/s; record internal-RAM and PSRAM free/low-water marks; repeat each sentence at least 30 times; report median and IQR rather than minimum latency; save model checksum, ESP-IDF version, chip revision and firmware Git SHA.

Table 3. Physical-board results intentionally blank until measurements exist.

ModelBLEUchrFEnc msDec msTotal mstok/s
Tiny Q8––––––
Base Q8––––––
Base Q4––––––
CTC/NAR––––––

15 Optimization roadmap

  • Vocabulary sweep before scaling width. Smaller vocabularies lengthen sequences; larger ones increase classifier traffic. The optimum must be measured.
  • Packed Q4. Base Q8 payload is 1008.44 KiB; the packed-Q4 estimate is 521.44 KiB. If inference is bandwidth-limited this may be the largest remaining lever; if nibble extraction dominates, Q4 can lose. A custom PIE Q4×Q8 kernel should be benchmarked against Q8 on identical checkpoints.
  • Quantized decoder caches. The FP32 cache is 224 KiB; INT8 storage could cut raw cache bytes roughly fourfold before scales. Per-vector vs per-head scales must be measured.
  • Fusion. RMSNorm, activation quantization and projection are separate passes; a fused path would reduce hot-vector traffic.
  • Dual-core execution. Helps only if kernels are compute-bound; if rows already saturate flash/PSRAM/cache bandwidth, splitting rows can add contention [Dory, 2026].
  • CTC/NAR. The repository contains an experimental CTC-style encoder path; its acceptance criterion is batch-one wall-clock latency on the actual S3 at acceptable BLEU/chrF [Helcl et al., 2022].

16 Limitations

The current empirical result proves pipeline correctness, not general translation quality. No full-data model has been reported with held-out BLEU/chrF. The physical firmware still needs measurement on the user's exact board/display assembly. The output constraint normalizes English to printable ASCII, so diacritics and typography can be lost. Q8 is post-training export, not a full quantization-aware training study. Analytical MAC counts exclude softmax exponentials, normalization, tokenization, cache misses, control overhead and display I/O, so they must not be presented as latency predictions. OPUS-100 is heterogeneous and may not match every domain. Q4, quantized caches, dual-core execution and NAR remain hypotheses until benchmarked.

17 Conclusion

NanoTranslate-S3 treats translation on an ESP32-S3 as a hardware–software co-design problem rather than a demonstration of fitting the largest possible language model. A scoped RU→EN system can combine a roughly one-million-parameter Transformer, a 1.5k lossless byte-piece vocabulary, teacher distillation, tied embeddings, row-wise Q8 weights, incremental decoding, constrained output scoring, a memory-mappable binary and a low-memory physical UI. The PyTorch→Q8→C++ path is verified by a deterministic parity test, and all central memory/compute quantities are derived exactly. The next scientific step is to train the full distilled grid and populate the deliberately blank physical-board quality/latency table.

Reproducibility

The repository contains the tokenizer, PyTorch architecture, OPUS-100 downloader, teacher distillation script, trainer, evaluator, Q8 exporter, model checker, analytical scripts, portable C++ runtime, ESP-IDF firmware, host CLI, tests and CI. A typical workflow:

python scripts/download_opus100.py --out-dir data/opus100 --limit-train 500000
python model/distill_teacher.py --input data/opus100/train.tsv \
  --output data/distilled.tsv
python model/tokenizer.py build --input data/distilled.tsv \
  --output artifacts/tokenizer.json --vocab-size 1536
python model/train.py --train data/distilled.tsv \
  --val data/opus100/validation.tsv \
  --tokenizer artifacts/tokenizer.json --preset base --out artifacts/base
python model/evaluate.py --checkpoint artifacts/base/best.pt \
  --tokenizer artifacts/tokenizer.json --data data/opus100/test.tsv \
  --raw-data data/opus100/test.raw.tsv
python model/export_s3mt.py --checkpoint artifacts/base/best.pt \
  --tokenizer artifacts/tokenizer.json --output artifacts/nanotranslate.s3mt
cmake -S host -B host/build
cmake --build host/build -j
./host/build/nanomt_cli artifacts/nanotranslate.s3mt

Acknowledgments and disclosure of funding. This is an independent student engineering research project. Open academic literature, Espressif documentation and public reference runtimes informed the design. No physical-device performance numbers are claimed without measurement.

Appendix A — Binary header schema

The fixed .s3mt header is 128 bytes, little-endian (reproduced as printed; see flags):

OffsetTypeMeaning
0char[8]magic S3MT0001
8u32format version
12u32flags
16–52u32vocab, d, f, heads, layers, max lengths
56–68u32PAD/BOS/EOS/UNK IDs
64u32tokenizer payload bytes
68u32protected payload bytes
72u32CRC32 over payload
76u32total file bytes
80–127reservedzero/future fields

A Q8 tensor stores a small shape header, one FP32 scale per output row, alignment padding, signed-int8 row-major values, then trailing alignment. RMSNorm vectors remain FP32.

Appendix B — Base parameter derivation

Ptok=196,608P_{\text{tok}} = 196{,}608, Ppos=14,336P_{\text{pos}} = 14{,}336, Penc=393,216P_{\text{enc}} = 393{,}216, Pdec=393,216P_{\text{dec}} = 393{,}216, Pnorm=1,664P_{\text{norm}} = 1{,}664; their sum is exactly 999,040.

Appendix C — Default wiring

ST7789 MOSI/SCLK/CS/DC/RST/BL on GPIO 11/12/10/9/8/7 and joystick up/down/left/right/press on GPIO 4/5/6/13/14, all configurable in menuconfig. These are logical defaults, not a universal electrical schematic.

Appendix D — Checklist before publishing ESP32 measurements

Every physical result should record board/module part number, chip revision, ESP-IDF version, compiler version, CPU clock, flash mode/frequency, PSRAM mode/frequency, model checksum, firmware commit, sentence battery, warm-up count, repetition count, latency median/IQR and memory low-water marks — so that a single optimistic timing cannot become an irreproducible performance claim.

References

  • Ashish Vaswani, Noam Shazeer, Noam Parmar, Jakob Uszkoreit, Llion Jones, Aidan N. Gomez, Lukasz Kaiser, and Illia Polosukhin. Attention is all you need. In Advances in Neural Information Processing Systems, volume 30, 2017.
  • Yoon Kim and Alexander M. Rush. Sequence-level knowledge distillation. In Proceedings of EMNLP, pages 1317–1327, 2016.
  • Rico Sennrich, Barry Haddow, and Alexandra Birch. Neural machine translation of rare words with subword units. In Proceedings of ACL, pages 1715–1725, 2016.
  • Ofir Press and Lior Wolf. Using the output embedding to improve language models. In Proceedings of EACL, pages 157–163, 2017.
  • Jindřich Libovický and Jindřich Helcl. End-to-end non-autoregressive neural machine translation with connectionist temporal classification. In Proceedings of EMNLP, pages 3016–3021, 2018.
  • Jindřich Helcl, Barry Haddow, and Alexandra Birch. Non-autoregressive machine translation: It's not as fast as it seems. In Proceedings of NAACL-HLT, pages 1780–1790, 2022.
  • Jörg Tiedemann and Santhosh Thottingal. OPUS-MT – building open translation services for the world. In Proceedings of EAMT, 2020.
  • Biao Zhang, Philip Williams, Ivan Titov, and Rico Sennrich. Improving massively multilingual neural machine translation and zero-shot translation. In Proceedings of ACL, pages 1628–1639, 2020.
  • Alham Fikri Aji and Kenneth Heafield. Neural machine translation with 4-bit precision and beyond. arXiv:1909.06091, 2019.
  • Benoit Jacob, Skirmantas Kligys, Bo Chen, Menglong Zhu, Matthew Tang, Andrew Howard, Hartwig Adam, and Dmitry Kalenichenko. Quantization and training of neural networks for efficient integer-arithmetic-only inference. In Proceedings of CVPR, pages 2704–2713, 2018.
  • Biao Zhang and Rico Sennrich. Root mean square layer normalization. arXiv:1910.07467, 2019.
  • Ji Lin, Wei-Ming Chen, Yujun Lin, John Cohn, Chuang Gan, and Song Han. MCUNet: Tiny deep learning on IoT devices. In Advances in Neural Information Processing Systems, 2020.
  • Matt Post. A call for clarity in reporting BLEU scores. In Proceedings of WMT, 2018.
  • Maja Popović. chrF: character n-gram F-score for automatic MT evaluation. In Proceedings of WMT, 2015.
  • Ricardo Rei, Craig Stewart, Ana C. Farinha, and Alon Lavie. COMET: A neural framework for MT evaluation. In Proceedings of EMNLP, pages 2685–2702, 2020.
  • Espressif Systems. ESP32-S3 series datasheet and product overview, 2026.
  • Espressif Systems. ESP-DL user guide: How to quantize model, 2026.
  • Espressif Systems. ESP-NN: Optimised neural network functions for Espressif chipsets, 2026.
  • Helsinki-NLP. Helsinki-NLP/opus-mt-ru-en model repository, 2026.
  • Helsinki-NLP. Helsinki-NLP/opus-100 dataset card, 2026.
  • Dory. ESP32-S3 LLM inference engine, 2026.
  • Jorge Bareiro. esp32-command-llm, 2026.

From the record

If a microcontroller were the target from the start, what translation model would you design? The ESP32-S3 has 512 KB of on-chip SRAM; a standard Russian–English model (OPUS-MT) is around 307 MB.

Approach

Instead of compressing a desktop model, I designed the model, tokenizer, number format and runtime together:

  • a pre-norm encoder–decoder Transformer — width 128, three encoder and two decoder layers;
  • FlashPiece, a lossless UTF-8 byte-piece tokenizer with raw-byte fallback;
  • symmetric per-row INT8 weights with FP32 scales,
W^ij=si qij,si=max⁡j∣Wij∣127,qij∈{−127,…,127}\hat W_{ij} = s_i\, q_{ij}, \qquad s_i = \frac{\max_j |W_{ij}|}{127}, \qquad q_{ij} \in \{-127,\dots,127\}
  • a CRC-protected, memory-mappable model file;
  • a C++ runtime with incremental decoding and cached cross-attention;
  • firmware for a 240×240 ST7789 display and a five-way joystick.

What is measured

One end-to-end correctness experiment: on a 20-pair memorization task the training loss falls from 5.76 to 0.041, and both PyTorch and the quantized C++ runtime reproduce all 20 targets.

What is not measured yet

There is no held-out BLEU, no trained translation checkpoint, and no latency from a physical board. The firmware builds in CI but has not been verified on hardware. The preprint leaves those fields empty.