TuBrief
구독 채널
비디오
커뮤니티

Creación de un LLM en el dispositivo con entrada en tiempo real usando ESP32S3 y 16MB de Flash

TuBrief 편집팀
2026년 8월 12일
0
Computing/Software

원본 영상을 바탕으로 AI의 도움을 받아 작성했습니다. 원본 영상이 기준입니다.

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

관련 영상

¡Este microcontrolador de $8 ejecuta un modelo de lenguaje de forma local!8:14

¡Este microcontrolador de $8 ejecuta un modelo de lenguaje de forma local!

Better Stack

커뮤니티의 다른 글

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

2026년 9월 13일

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

2026년 9월 13일

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

2026년 9월 13일

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

2026년 9월 13일

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

2026년 9월 12일

Apple Won the AI Race

2026년 9월 12일

댓글 (0)

Log in to leave a comment

아직 작성된 글이 없습니다

© 2026 . All rights reserved.

TuBrief
구독 채널
비디오
커뮤니티
로그인

Creación de un LLM en el dispositivo con entrada en tiempo real usando ESP32S3 y 16MB de Flash

Proponerse ejecutar un modelo de lenguaje de 28.9 millones de parámetros en una placa ESP32-S3 de 8 dólares rápidamente lleva a un callejón sin salida. Un modelo que funciona de maravilla en un video de demostración se bloquea instantáneamente al entrar en contacto con los periféricos de hardware caseros. Con apenas 512KB de SRAM y la necesidad de flashear una partición de 15MB cada vez que se cambia una sola oración, el desarrollo se vuelve frustrante. Para superar estas limitaciones y construir un dispositivo que funcione con entradas de teclado reales, es necesario rediseñar por completo el almacenamiento en caché de memoria y los búferes de E/S desde cero.

Inyección de prompts por serie sin necesidad de reflashear constantemente

Querer cambiar una sola oración de prueba y tener que reflashear la placa es una pérdida de tiempo. Al vincular las interrupciones UART asíncronas y los semáforos de FreeRTOS, se elimina la necesidad de reiniciar la placa. El modelo comprende inmediatamente las oraciones a medida que se escriben desde el monitor serie o el teclado.

  1. Asigna los pines GPIO 16/17 al puerto UART_NUM_2, establece el umbral del FIFO de hardware en 120 bytes y habilita la interrupción de recepción.
  2. Crea un búfer circular de 2048 bytes en la SRAM interna. Esto evita desbordamientos y fallos de datos mientras se ejecuta la inferencia.
  3. Incorpora un analizador de streaming que detecte los caracteres de salto de línea, y utiliza una biblioteca de codificador BTK1 de 43,056 bytes para convertir directamente el texto de entrada en una matriz de ID de tokens.
  4. Configura un doble semáforo xPromptSemaphore. Cuando la tarea de recepción termina de escribir los tokens y llama a xSemaphoreGive, la tarea de inferencia en espera obtiene el bloqueo de memoria mediante xSemaphoreTake y comienza los cálculos.

Esta configuración permite reducir en más del 80% el tiempo dedicado a modificar y probar 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) |
+------------------+ +-------------------+ +--------------------+

`

Alcanzando 14 tokens por segundo mediante la división de embeddings por capas y caché en SRAM

Utilizando la estructura de Embeddings por Capa (PLE, por sus siglas en inglés) propuesta por los investigadores de Google Gemma 3n, es posible dividir y alojar un modelo de 14.9MB reducido a 4 bits. La tabla de embeddings, que alcanza los 25 millones de parámetros, se almacena en la memoria flash SPI de 16MB, mientras que los pesos del Output Head (3.1 millones de parámetros) y la caché KV se colocan en la PSRAM de 8MB. El núcleo de cómputo denso (Dense Compute Core) de 559K parámetros de uso más frecuente y el búfer Hot Activation se ubican directamente en la SRAM de 512KB.

Aumentar la velocidad a más de 14 tokens por segundo es más complicado de lo esperado.

  1. Añade el atributo __attribute__((noinline)) directamente a la función de operación matvec_i8_range. Si el compilador realiza la inlining por su cuenta, se producen fallos de caché en la I-RAM y el tiempo de cálculo empeora drásticamente, pasando de 94.9ms a 155.2ms.
  2. Divide las operaciones ple_model_proj y qkv entre los dos núcleos Xtensa LX7 del ESP32-S3. La velocidad adecuada solo se consigue poniendo a trabajar ambos núcleos simultáneamente.
  3. Amplía el búfer de prefetch para cargar previamente los datos de los embeddings de las primeras capas en el espacio libre de SRAM, rompiendo así el cuello de botella de la memoria flash.

Una simple adaptación a C arroja una velocidad desesperante de 0.57 tokens por segundo. Sin embargo, al completar el almacenamiento en fase INT8 y el prefetch en SRAM, se superan los 14.0 tokens por segundo.

Etapa de optimización Latencia por token Tokens generados por segundo Tecnologías clave aplicadas
Puerto puro en C (Baseline) 1,757.2 ms 0.57 tok/s Monocúcleo, cálculo FP32
PSRAM Head y optimización escalar 193.9 ms 4.61 tok/s Asignación de Output Head en PSRAM
Aplicación de FP32 en doble núcleo 139.4 ms 6.22 tok/s Cálculo dividido por capas en doble núcleo
INT8 Staging + Optimización SRAM 94.9 ms 9.88 tok/s Cuantización INT8, aplicación de noinline
Ampliación del búfer de prefetch SRAM ~71.4 ms 14.0+ tok/s Prefetch de capas iniciales en SRAM

Control de suspensión ligera para mantener el consumo en 210mA

Mantener los dos núcleos funcionando continuamente a un reloj de 240MHz dispara el consumo de corriente hasta los 210mA (777mW). Dejando de lado que la placa se calienta, la batería se agota en un abrir y cerrar de ojos. Mediante el comando esp_pm_configure, es necesario reducir la frecuencia del reloj a 40MHz y activar la suspensión ligera automática cuando no hay inferencias.

  1. En la estructura esp_pm_config_esp32s3_t, establece max_freq_mhz en 240, min_freq_mhz en 40 y habilita light_sleep_enable en true.
  2. Ejecuta esp_sleep_pd_config(ESP_PD_DOMAIN_VDDSDIO, ESP_PD_OPTION_ON). Es necesario mantener la alimentación de la memoria flash SPI y la RAM incluso al entrar en estado de suspensión para evitar la pérdida de datos.
  3. Utiliza uart_set_wakeup_threshold y esp_sleep_enable_uart_wakeup para configurar una interrupción de recuperación que despierte a la CPU de la suspensión en el momento exacto en que entra una señal serie.

Si se realizan cálculos continuos con una batería de polímero de litio de 3.7V y 1000mAh, esta dura apenas 4.7 horas. Sin embargo, en el modo de espera por suspensión ligera, la corriente cae drásticamente a 0.24mA (0.88mW). En un entorno de uso real que intercala tiempos de espera, la corriente promedio se mantiene en un nivel de 15 a 30mA, lo que multiplica la duración de la batería por más de 3.