iiOTESP32 Brasil
Modo de contingência: páginas públicas e downloads seguem disponíveis; login, comunidade e dados do servidor podem ficar indisponíveis.
Voltar para catálogo de tutoriais
Ilustração: laptop com o VS Code, painéis de configuração e de terminal ao lado de uma placa ESP32 ligada por cabo USB, sobre uma bancada de trabalho
Imagem ilustrativa gerada por IA
IntermediárioPlatformIOLeitura: 12 min

ESP32-S3 com PlatformIO do zero

Configure o ESP32-S3 no PlatformIO: PSRAM octal com qio_opi, monitor serial mudo por causa do USB-CDC e tabela de partições para usar os 16 MB de flash.

Tecnologias:#ESP32-S3#PlatformIO#PSRAM#USB-CDC#Partições#Arduino Core

Requisitos de hardware:

  • Placa ESP32-S3 (referência: módulo N16R8)
  • Cabo USB-C de dados

Para quem é este guia

Este guia parte do PlatformIO já instalado no VS Code e vai até o firmware rodando na placa, com a PSRAM reconhecida e o monitor serial respondendo. Se você ainda não instalou o PlatformIO, comece pelo guia do PlatformIO e volte aqui depois.

A placa de referência é um módulo ESP32-S3 N16R8. O que muda de uma variante para outra está explicado logo abaixo, então dá para adaptar sem sair do texto.

O ponto importante: o ESP32-S3 não é o ESP32 clássico com nome novo. Ele troca o núcleo por um Xtensa LX7 dual-core de 240 MHz, muda completamente o esquema de USB e adiciona PSRAM octal. É exatamente nesses três pontos que a configuração que funcionava no ESP32 antigo para de funcionar — e é neles que este guia se concentra.

O que significa N16R8

A sigla depois do nome do módulo descreve a memória, e ela determina metade da sua configuração.

O N é a flash: N16 são 16 MB. O R é a PSRAM: R8 são 8 MB. Uma N8R2 tem 8 MB de flash e 2 MB de PSRAM, e um módulo sem a parte R simplesmente não tem PSRAM nenhuma.

O detalhe que causa mais dor de cabeça é invisível na sigla. Nas variantes R8 a PSRAM é octal (OPI, oito linhas de dados), enquanto nas R2 costuma ser quad (QSPI, quatro linhas). Declarar octal como quad faz a placa reiniciar em loop ou ignorar os 8 MB em silêncio, sem nenhuma mensagem de erro.

Criando o projeto no PlatformIO

Abra o PlatformIO Home no VS Code e clique em New Project. Dê um nome, escolha a placa Espressif ESP32-S3-DevKitC-1-N8 e o framework Arduino.

Não se prenda a essa escolha. Ela só serve para gerar um platformio.ini inicial, e a partir daí é esse arquivo que manda em tudo. A variante N16R8 nem aparece na lista padrão, e mesmo assim funciona — basta ajustar o platformio.ini como mostrado a seguir.

O platformio.ini que funciona no S3

Este é o arquivo completo para uma N16R8 com Arduino Core:

code.cpp
cpp
1[env:esp32-s3-n16r8]
2platform = espressif32
3board = esp32-s3-devkitc-1
4framework = arduino
5monitor_speed = 115200
6upload_speed = 921600
7board_build.mcu = esp32s3
8board_build.f_cpu = 240000000L
9board_build.flash_mode = qio
10board_build.arduino.memory_type = qio_opi
11board_upload.flash_size = 16MB
12board_upload.maximum_size = 16777216
13build_flags =
14 -DBOARD_HAS_PSRAM
15 -DARDUINO_USB_MODE=1
16 -DARDUINO_USB_CDC_ON_BOOT=0

As três linhas que realmente decidem o resultado são memory_type, flash_size e o par de flags de USB. As próximas seções explicam cada uma.

PSRAM octal: o parâmetro que quase todo mundo erra

A linha board_build.arduino.memory_type descreve, em uma palavra só, como a flash e a PSRAM conversam com o chip. O formato é flash seguido de psram.

O valor qio_opi significa flash em QIO e PSRAM em OPI, que é a combinação correta de uma N16R8. Uma placa com PSRAM quad usaria qio_qspi, e um módulo sem PSRAM usaria apenas qio.

Só declarar o memory_type não basta: o Arduino Core também precisa da flag -DBOARD_HAS_PSRAM para expor a memória ao seu código. Faltando ela, a PSRAM até é inicializada, mas as funções de alocação continuam usando apenas a RAM interna.

Vale ter dimensão do que está em jogo. A RAM interna do S3 gira em torno de 320 KB utilizáveis. A PSRAM adiciona 8 MB. É a diferença entre não conseguir montar um buffer de imagem e trabalhar com folga.

Como confirmar que a PSRAM foi reconhecida

Nunca assuma que deu certo porque compilou. Grave este teste e leia o serial:

code.cpp
cpp
1void setup() {
2 Serial.begin(115200);
3 delay(1000);
4 Serial.printf("Flash : %u bytes\\n", ESP.getFlashChipSize());
5 Serial.printf("PSRAM : %u bytes\\n", ESP.getPsramSize());
6 Serial.printf("Heap : %u bytes\\n", ESP.getHeapSize());
7 if (!psramFound()) {
8 Serial.println("PSRAM NAO encontrada");
9 Serial.println("Revise memory_type e a flag BOARD_HAS_PSRAM");
10 }
11}
12void loop() {}

Numa N16R8 configurada corretamente, a flash reporta 16777216 bytes e a PSRAM reporta 8388608 bytes. Se a PSRAM vier zerada, o problema está no memory_type ou na flag, não na placa.

Por que o monitor serial fica mudo no S3

Esta é a dúvida que mais aparece, e ela não tem nada a ver com o seu código.

O ESP32-S3 tem USB nativo. Por isso a maioria dos kits de desenvolvimento traz duas portas USB-C: uma marcada UART, que passa por um conversor serial tradicional, e outra marcada USB, ligada direto no chip. As duas gravam firmware. Só uma entrega o Serial, e qual delas depende de uma flag.

Com ARDUINO_USB_CDC_ON_BOOT=0, que é o valor do exemplo acima, o objeto Serial é ligado à UART0. Você precisa conectar o monitor na porta marcada UART. Se plugar na porta USB nativa, o firmware roda perfeitamente e o monitor fica em branco para sempre.

Com ARDUINO_USB_CDC_ON_BOOT=1, o Serial passa a sair pela porta USB nativa. É mais prático, porque um cabo só faz tudo, mas tem um efeito colateral: depois de cada gravação a porta some e reaparece no sistema, e as primeiras mensagens se perdem nesse intervalo. Por isso o delay de um segundo no começo do setup do exemplo anterior.

Se o monitor está mudo, antes de mexer no código troque de porta física. Resolve na maioria dos casos.

Aproveitando os 16 MB de flash

A tabela de partições padrão foi desenhada para módulos de 4 MB. Usá-la numa N16R8 desperdiça mais de dois terços do chip, e o compilador ainda reclama de sketch grande demais enquanto sobram 12 MB parados.

Crie um arquivo chamado partitions.csv na raiz do projeto:

code.cpp
cpp
1# Name, Type, SubType, Offset, Size, Flags
2nvs, data, nvs, 0x9000, 0x5000,
3otadata, data, ota, 0xe000, 0x2000,
4app0, app, ota_0, 0x10000, 0x300000,
5app1, app, ota_1, , 0x300000,
6spiffs, data, spiffs, , 0x900000,

E aponte para ele no platformio.ini com a linha board_build.partitions = partitions.csv.

Esse layout reserva dois espaços de aplicação de 3 MB cada, o que é o requisito para atualização OTA: o firmware novo é gravado no espaço livre enquanto o atual continua rodando, e a troca só acontece se a gravação terminar íntegra. Os 9 MB restantes ficam para SPIFFS, servindo arquivos de configuração, páginas web ou registros locais.

Se você não pretende usar OTA, um único espaço de aplicação de 6 MB e mais espaço de dados também é uma escolha válida. O importante é não deixar a tabela de 4 MB numa placa de 16.

Quando vale criar uma definição de placa própria

Repetir dez linhas de board_build em cada projeto novo cansa e convida a erro. A alternativa é registrar a placa uma vez.

Crie uma pasta boards na raiz do projeto e dentro dela um arquivo JSON com o nome da sua placa:

code.cpp
cpp
1{
2 "build": {
3 "arduino": {
4 "ldscript": "esp32s3_out.ld",
5 "memory_type": "qio_opi",
6 "partitions": "default_16MB.csv"
7 },
8 "core": "esp32",
9 "extra_flags": [
10 "-DBOARD_HAS_PSRAM",
11 "-DARDUINO_USB_MODE=1",
12 "-DARDUINO_USB_CDC_ON_BOOT=0"
13 ],
14 "f_cpu": "240000000L",
15 "f_flash": "80000000L",
16 "flash_mode": "qio",
17 "mcu": "esp32s3",
18 "variant": "esp32_s3r8n16"
19 },
20 "frameworks": ["arduino", "espidf"],
21 "name": "ESP32-S3 N16R8",
22 "upload": {
23 "flash_size": "16MB",
24 "maximum_ram_size": 327680,
25 "maximum_size": 16777216,
26 "speed": 921600
27 }
28}

Salvando como esp32s3_n16r8.json, o platformio.ini inteiro se reduz a board = esp32s3_n16r8 mais o framework. O PlatformIO procura na pasta boards do projeto antes de procurar nas placas oficiais, então a definição viaja junto com o repositório e o próximo desenvolvedor compila sem configurar nada.

Erros comuns no ESP32-S3 e como resolver

A placa reinicia em loop logo após gravar. Quase sempre é memory_type incompatível com a PSRAM física. Confirme se o seu módulo é R8 (octal, qio_opi) ou R2 (quad, qio_qspi).

O monitor serial não mostra nada. Troque de porta USB antes de qualquer outra coisa, conforme explicado acima. Se persistir, confira se o monitor_speed do platformio.ini bate com o valor do Serial.begin.

A fatal error occurred: Failed to connect to ESP32-S3. Segure o botão BOOT, toque o RESET, solte o BOOT e grave de novo. Isso força o modo de gravação.

A gravação falha no meio ou corrompe. O upload_speed de 921600 é agressivo para cabos longos ou de baixa qualidade. Baixe para 460800 e teste.

Sketch too big, mesmo com 16 MB. A tabela de partições continua sendo a de 4 MB. Aplique o partitions.csv da seção anterior.

A PSRAM aparece como zero. Falta a flag -DBOARD_HAS_PSRAM, ou o memory_type está errado. Rode o teste de diagnóstico desta página antes de mexer no resto do projeto.

Ver mais tutoriais técnicosIIoT ESP32 Brasil — Documentação Prática