TuBrief
Subscribed Channels
Videos
Community

Créer un LLM on-device avec saisie en temps réel sur ESP32-S3 et flash de 16 Mo

TuBrief Editorial
August 12, 2026
0
Computing/Software

Written with AI assistance from the source video. The video is the authority.

Français한국어中文العربيةEspañolDeutschEnglishहिन्दीPortuguêsРусскийBahasa Indonesia日本語

Related Video

Ce microcontrôleur à 8 $ exécute un modèle linguistique en local !8:14

Ce microcontrôleur à 8 $ exécute un modèle linguistique en local !

Better Stack

More from the community

사내 시스템에 llm api 붙일 때 마주하는 현실적인 한계와 대응법

September 13, 2026

레거시 백엔드에 GPT-6 Astra 붙일 때 예산 승인과 보안 통과를 먼저 끝내는 법이 있습니다

September 13, 2026

에이전트끼리 대화하다 6천만 원 청구서가 나오는 이유

September 13, 2026

사내 RAG 벡터 검색에 Okta 권한 필터를 직접 거는 방법

September 13, 2026

브라우저 에이전트에게 내 구글 계정을 통째로 넘기면 안 되는 이유

September 12, 2026

Apple Won the AI Race

September 12, 2026

Comments (0)

Log in to leave a comment

No posts yet

© 2026 . All rights reserved.

TuBrief
Subscribed Channels
Videos
Community
Log in

Créer un LLM on-device avec saisie en temps réel sur ESP32-S3 et flash de 16 Mo

Tenter de faire tourner un modèle de langage de 28,9 millions de paramètres sur une carte ESP32-S3 à 8 dollars mène rapidement à une impasse. Un modèle qui fonctionne à merveille dans une vidéo de démonstration se bloque instantanément dès qu'on le confronte aux périphériques matériels de notre propre conception. Avec seulement 512 Ko de SRAM, et l'obligation de reflasher une partition de 15 Mo à chaque modification de phrase, le développement devient vite exaspérant. Pour surmonter ces contraintes et construire un appareil capable de fonctionner avec de véritables saisies au clavier, il faut repenser entièrement la mise en cache de la mémoire et les tampons d'E/S.

Injecter des prompts via le port série sans reflasher à chaque fois

Perdre son temps à reflasher la carte juste pour modifier une phrase de test est une impasse. En combinant les interruptions UART asynchrones et les sémaphores FreeRTOS, il n'est plus nécessaire de redémarrer la carte. Chaque phrase saisie via le moniteur série ou le clavier est immédiatement comprise par le modèle.

  1. Attribuez les broches GPIO 16/17 au port UART_NUM_2, définissez le seuil FIFO matériel à 120 octets et activez l'interruption de réception.
  2. Créez un buffer circulaire de 2048 octets dans la SRAM intégrée pour éviter tout dépassement de données pendant l'inférence.
  3. Intégrez un parseur de streaming capable de détecter les caractères de saut de ligne, et convertissez directement le texte d'entrée en un tableau d'ID de tokens à l'aide d'une bibliothèque d'encodeur BTK1 de 43 056 octets.
  4. Mettez en place un double sémaphore xPromptSemaphore. Une fois que la tâche de réception a terminé d'écrire les tokens et appelle xSemaphoreGive, la tâche d'inférence en attente obtient le verrouillage de la mémoire via xSemaphoreTake et lance le calcul.

Cette configuration permet de réduire de plus de 80 % le temps consacré à la modification et au test des prompts.

`
+------------------+ +-------------------+ +--------------------+
| External UART | ---> | HW FIFO Buffer | ---> | SW Ring Buffer |
| (Keypad/Monitor) | | (120 Bytes) | | (2048 Bytes) |
+------------------+ +-------------------+ +--------------------+
|
v
+------------------+ +-------------------+ +--------------------+
| LLM Forward Pass | <--- | xPromptSemaphore | <--- | BTK1 Tokenizer |
| (Inference Task) | | (Binary Lock) | | (43,056 B Library) |
+------------------+ +-------------------+ +--------------------+

`

Atteindre 14 tokens par seconde grâce au découpage des embeddings par couche et au cache SRAM

En adoptant l'architecture Per-Layer Embeddings (PLE) proposée par les chercheurs de Google Gemma 3n, il est possible de fractionner et d'embarquer un modèle de 14,9 Mo réduit à 4 bits. La table d'embeddings, qui atteint 25 millions de paramètres, est placée dans la mémoire flash SPI de 16 Mo, tandis que les poids de la tête de sortie (Output Head - 3,1 millions de paramètres) et le cache KV résident dans la PSRAM de 8 Mo. Seuls le cœur de calcul dense (Dense Compute Core) de 559 Ko le plus sollicité et le buffer Hot Activation sont stockés dans la SRAM de 512 Ko.

Faire grimper la vitesse au-delà de 14 tokens par seconde s'avère plus ardu que prévu.

  1. Ajoutez l'attribut __attribute__((noinline)) à la fonction de calcul matvec_i8_range. Si le compilateur procède à un inlining automatique, des défauts de cache I-RAM surviennent et font chuter drastiquement le temps de calcul, qui passe de 94,9 ms à 155.2 ms.
  2. Répartissez les calculs ple_model_proj et qkv entre les deux cœurs Xtensa LX7 de l'ESP32-S3. L'exploitation simultanée des cœurs du mulco-processeur est indispensable pour atteindre les performances escomptées.
  3. Agrandissez le buffer de préchargement (prefetch buffer) pour charger à l'avance les données d'embeddings des premières couches dans l'espace SRAM disponible, levant ainsi le goulet d'étranglement de la mémoire flash.

Un simple portage en langage C se solde par une vitesse désespérante de 0,57 token par seconde. En revanche, combiné au staging INT8 et au préchargement SRAM, le système dépasse les 14,0 tokens par seconde.

Étape d'optimisation Latence de calcul par token Tokens générés par seconde Principales technologies appliquées
Portage C pur (Baseline) 1 757,2 ms 0,57 tok/s Monocœur, calcul FP32
PSRAM Head & Optimisation scalaire 193,9 ms 4,61 tok/s Placement de l'Output Head en PSRAM
Application FP32 multicœur 139,4 ms 6,22 tok/s Calcul et division par couche multicœur
Staging INT8 + Optimisation SRAM 94,9 ms 9,88 tok/s Quantification INT8, application de noinline
Extension du buffer de préchargement SRAM ~71,4 ms 14,0+ tok/s Préchargement SRAM des premières couches

Maîtriser la consommation à 210 mA grâce au contrôle de veille légère (Light Sleep)

Faire tourner les deux cœurs en continu à une fréquence d'horloge de 240 MHz fait grimper la consommation de courant jusqu'à 210 mA (777 mW). Outre la surchauffe de la carte, la batterie se vide en un clin d'œil. La commande esp_pm_configure permet d'abaisser la fréquence à 40 MHz en l'absence d'inférence et d'activer la veille légère automatique.

  1. Dans la structure esp_pm_config_esp32s3_t, définissez max_freq_mhz sur 240 et min_freq_mhz sur 40, puis activez light_sleep_enable à true.
  2. Exécutez esp_sleep_pd_config(ESP_PD_DOMAIN_VDDSDIO, ESP_PD_OPTION_ON). Même en état de veille, l'alimentation de la mémoire flash SPI et de la RAM doit être maintenue pour éviter toute perte de données.
  3. Utilisez uart_set_wakeup_threshold et esp_sleep_enable_uart_wakeup pour configurer une interruption de réveil permettant au CPU de sortir de sa veille dès l'arrivée d'un signal sur le port série.

Une batterie lithium-polymère de 3,7 V et 1000 mAh ne tiendra que 4,7 heures en cas d'inférence ininterrompue. En revanche, en mode veille légère, la consommation chute brutalement à 0,24 mA (0,88 mW). Dans un scénario d'utilisation réelle alternant avec des temps d'attente, le courant moyen se maintient entre 15 et 30 mA, prolongeant l'autonomie de la batterie par un facteur supérieur à trois.