
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.
Requisitos de hardware:
- Placa ESP32-S3 (referência: módulo N16R8)
- Cabo USB-C de dados
Baixe o material deste tutorial:
- Projeto completo (ZIP)112 KBProjeto base para N16R8 com três ambientes prontos, definição de placa própria, tabela de partições de 16 MB e o sketch de diagnóstico de flash, PSRAM e porta USB.
- Manual em PDF (6 páginas)134 KBO que significa N16R8, as três linhas que decidem tudo, passo a passo, tabela de problemas e checklist final.
Quer receber novos manuais e esquemas industriais? Entre no nosso canal do Telegram / WhatsApp
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:
| 1 | [env:esp32-s3-n16r8] |
| 2 | platform = espressif32 |
| 3 | board = esp32-s3-devkitc-1 |
| 4 | framework = arduino |
| 5 | monitor_speed = 115200 |
| 6 | upload_speed = 921600 |
| 7 | board_build.mcu = esp32s3 |
| 8 | board_build.f_cpu = 240000000L |
| 9 | board_build.flash_mode = qio |
| 10 | board_build.arduino.memory_type = qio_opi |
| 11 | board_upload.flash_size = 16MB |
| 12 | board_upload.maximum_size = 16777216 |
| 13 | build_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:
| 1 | void 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 | } |
| 12 | void 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:
| 1 | # Name, Type, SubType, Offset, Size, Flags |
| 2 | nvs, data, nvs, 0x9000, 0x5000, |
| 3 | otadata, data, ota, 0xe000, 0x2000, |
| 4 | app0, app, ota_0, 0x10000, 0x300000, |
| 5 | app1, app, ota_1, , 0x300000, |
| 6 | spiffs, 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:
| 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.
