TuBrief
Subscribed Channels
Videos
Community

Criando um LLM embarcado com entradas em tempo real usando ESP32-S3 e Flash de 16MB

TuBrief Editorial
August 12, 2026
0
Computing/Software

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

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

Related Video

Este Microcontrolador de US$ 8 Está Executando um Modelo de Linguagem Localmente!8:14

Este Microcontrolador de US$ 8 Está Executando um Modelo de Linguagem Localmente!

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

Criando um LLM embarcado com entradas em tempo real usando ESP32-S3 e Flash de 16MB

Decidir rodar um modelo de linguagem de 28,9 milhões de parâmetros em uma placa ESP32-S3 de 8 dólares rapidamente traz uma sensação de impasse. O modelo que roda perfeitamente em vídeos de demonstração simplesmente trava assim que encontra os periféricos de hardware que você criou. Com apenas 512KB de SRAM e a necessidade de regravar uma partição de 15MB a cada alteração de frase, o desenvolvimento rapidamente se torna frustrante. Para superar essas restrições e construir um dispositivo que funcione recebendo entradas de teclado reais, você precisa projetar o cache de memória e os buffers de E/S de forma totalmente diferente desde o início.

Injetando prompts via serial sem precisar reflashear a cada vez

Perder tempo gravando a placa novamente só para mudar uma frase de teste é um desperdício. Ao vincular interrupções UART assíncronas e semáforos FreeRTOS, você elimina a necessidade de reiniciar a placa. Cada frase digitada no monitor serial ou no teclado é compreendida instantaneamente pelo modelo.

  1. Atribua os pinos GPIO 16/17 à porta UART_NUM_2, defina o limite do FIFO de hardware como 120 bytes e habilite a interrupção de recepção.
  2. Crie um buffer circular de 2048 bytes na SRAM interna. Isso evita estouros de dados (buffer overflow) enquanto a computação de inferência está emandamento.
  3. Conecte um parser de streaming que detecta caracteres de nova linha e converta diretamente o texto de entrada em uma matriz de IDs de token usando a biblioteca de tokenizador BTK1 de 43.056 bytes.
  4. Configure o semáforo duplo xPromptSemaphore. Quando a tarefa de recepção terminar de gravar os tokens e chamar xSemaphoreGive, a tarefa de inferência em espera obtém o bloqueio de memória via xSemaphoreTake e inicia o cálculo.

Essa configuração reduz o tempo gasto na modificação e teste de prompts em mais de 80%.

`
+------------------+ +-------------------+ +--------------------+
| 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) |
+------------------+ +-------------------+ +--------------------+

`

Alcançando 14 tokens por segundo com divisão de embeddings por camada e cache na SRAM

Utilizando a estrutura Per-Layer Embeddings (PLE) proposta pelos pesquisadores do Google Gemma 3n, podemos dividir e acomodar um modelo de 14,9MB reduzido para 4 bits. A tabela de embeddings, que alcança 25 milhões de parâmetros, fica na flash SPI de 16MB, enquanto os pesos do Output Head (3,1 milhões de parâmetros) e o cache KV ficam na PSRAM de 8MB. Apenas o Dense Compute Core de 559K parâmetros mais utilizado e o buffer Hot Activation são armazenados na SRAM de 512KB.

Aumentar a velocidade para mais de 14 tokens por segundo é mais complicado do que parece.

  1. Adicione o atributo __attribute__((noinline)) diretamente à função de cálculo matvec_i8_range. Se o compilador realizar a in-lining por conta própria, ocorrerão falhas de cache I-RAM e o tempo de computação despencará de 94,9 ms para 155.2 ms.
  2. Divida as operações ple_model_proj e qkv entre os núcleos Dual Xtensa LX7 do ESP32-S3. A velocidade desejada só é alcançada operando ambos os núcleos simultaneamente.
  3. Amplie o buffer de pré-carregamento (prefetch), que carrega os dados de embedding das camadas iniciais antecipadamente no espaço livre da SRAM, para eliminar o gargalo da memória flash.

Ao apenas fazer a portabilidade pura em linguagem C, obtemos uma velocidade desanimadora de 0,57 tokens por segundo. No entanto, após concluir o staging INT8 e o prefetch na SRAM, ultrapassamos 14,0 tokens por segundo.

Estágio de Otimização Latência de cálculo por token Tokens gerados por segundo Principais tecnologias aplicadas
Portabilidade pura em C (Baseline) 1.757,2 ms 0,57 tok/s Núcleo único, computação FP32
PSRAM Head & Otimização Escalar 193,9 ms 4,61 tok/s Alocação do Output Head na PSRAM
Aplicação Dual-Core FP32 139,4 ms 6,22 tok/s Operação de divisão de camadas em dual-core
INT8 Staging + Otimização SRAM 94,9 ms 9,88 tok/s Quantização INT8, aplicação de noinline
Expansão do Buffer de Prefetch SRAM ~71.4 ms 14,0+ tok/s Prefetch de camadas iniciais na SRAM

Controlando a corrente de 210mA com Light Sleep

Manter os dois núcleos rodando continuamente com um clock de 240MHz faz o consumo de corrente disparar para 210mA (777mW). Além de aquecer a placa, isso esgota a bateria rapidamente. Você precisa usar o comando esp_pm_configure para reduzir o clock para 40MHz e ativar o light sleep automático quando não houver inferência em execução.

  1. Na estrutura esp_pm_config_esp32s3_t, defina max_freq_mhz como 240, min_freq_mhz como 40 e ative light_sleep_enable como true.
  2. Execute esp_sleep_pd_config(ESP_PD_DOMAIN_VDDSDIO, ESP_PD_OPTION_ON). Mesmo entrando no estado de sleep, a alimentação da flash SPI e da RAM deve ser mantida para evitar a perda de dados.
  3. Utilize uart_set_wakeup_threshold e esp_sleep_enable_uart_wakeup para configurar uma interrupção de recuperação que desperta a CPU do sleep assim que um sinal serial é recebido.

Se você continuar executando inferências continuamente com uma bateria de polímero de lítio de 3,7V e 1000mAh, ela durará apenas 4,7 horas. No entanto, no modo de espera em light sleep, a corrente cai drasticamente para 0,24mA (0,88mW). Em ambientes de uso real com tempos de espera intercalados, a corrente média é mantida na faixa de 15 a 30mA, triplicando ou mais a autonomia da bateria.