
ESP32-S3 con PlatformIO desde cero
Configura el ESP32-S3 en PlatformIO: PSRAM octal con qio_opi, monitor serie silencioso por USB-CDC y tabla de particiones para usar 16 MB de flash.
Requisitos de hardware:
- Placa ESP32-S3 (referência: módulo N16R8)
- Cabo USB-C de dados
Descarga el material de este 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.
¿Quieres recibir nuevos manuales y esquemas industriales? Únete a nuestro canal de Telegram / WhatsApp
Para quién es esta guía
Esta guía parte de PlatformIO ya instalado en VS Code y llega hasta el firmware funcionando en la placa, con la PSRAM reconocida y el monitor serie respondiendo. Si todavía no has instalado PlatformIO, empieza por la guía de PlatformIO y vuelve después.
La placa de referencia es un módulo ESP32-S3 N16R8. Las diferencias entre variantes se explican a continuación, así que puedes adaptar la configuración sin salir del texto.
Lo importante es que el ESP32-S3 no es el ESP32 clásico con un nombre nuevo. Cambia el núcleo por un Xtensa LX7 de doble núcleo a 240 MHz, cambia por completo el esquema USB y añade PSRAM octal. Esos son exactamente los tres puntos en los que deja de funcionar una configuración que servía para el ESP32 antiguo, y son el centro de esta guía.
Qué significa N16R8
El sufijo después del nombre del módulo describe la memoria y determina la mitad de la configuración.
La N es la flash: N16 significa 16 MB. La R es la PSRAM: R8 significa 8 MB. Una N8R2 tiene 8 MB de flash y 2 MB de PSRAM; un módulo sin la parte R no tiene PSRAM.
El detalle que más problemas causa no se ve en la sigla. En las variantes R8 la PSRAM es octal (OPI, ocho líneas de datos), mientras que en las R2 suele ser quad (QSPI, cuatro líneas). Declarar octal como quad hace que la placa se reinicie en bucle o ignore silenciosamente los 8 MB, sin ningún mensaje de error.
Crear el proyecto en PlatformIO
Abre PlatformIO Home en VS Code y haz clic en New Project. Ponle un nombre, elige la placa Espressif ESP32-S3-DevKitC-1-N8 y el framework Arduino.
No te aferres a esa elección. Solo sirve para generar un platformio.ini inicial; a partir de ahí ese archivo lo controla todo. La variante N16R8 ni siquiera aparece en la lista estándar, pero funciona: basta ajustar platformio.ini como se muestra a continuación.
El platformio.ini que funciona en el S3
Este es el archivo completo para una N16R8 con 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 |
Las tres líneas que realmente deciden el resultado son memory_type, flash_size y las dos flags de USB. Las siguientes secciones explican cada una.
PSRAM octal: el parámetro que casi todo el mundo se equivoca
La línea board_build.arduino.memory_type describe, en un solo valor, cómo se comunican la flash y la PSRAM con el chip. El formato es flash seguido de psram.
El valor qio_opi significa flash en QIO y PSRAM en OPI, la combinación correcta para una N16R8. Una placa con PSRAM quad usaría qio_qspi y un módulo sin PSRAM usaría solo qio.
Declarar memory_type no es suficiente: Arduino Core también necesita la flag -DBOARD_HAS_PSRAM para exponer la memoria al código. Sin ella, la PSRAM puede inicializarse, pero las funciones de asignación siguen usando solo la RAM interna.
Conviene dimensionar lo que está en juego. La RAM interna del S3 tiene unos 320 KB utilizables. La PSRAM añade 8 MB. Es la diferencia entre no poder montar un búfer de imagen y trabajar con margen.
Cómo confirmar que la PSRAM fue reconocida
Nunca des por hecho que funcionó solo porque compiló. Graba esta prueba y lee el serie:
| 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() {} |
En una N16R8 configurada correctamente, la flash informa de 16777216 bytes y la PSRAM de 8388608 bytes. Si la PSRAM aparece en cero, el problema está en memory_type o en la flag, no en la placa.
Por qué el monitor serie queda mudo en el S3
Es la duda que más aparece y no tiene nada que ver con el código.
El ESP32-S3 tiene USB nativo. Por eso la mayoría de los kits de desarrollo trae dos puertos USB-C: uno marcado UART, que pasa por un convertidor serie tradicional, y otro marcado USB, conectado directamente al chip. Los dos pueden grabar firmware. Solo uno entrega Serial y cuál depende de una flag.
Con ARDUINO_USB_CDC_ON_BOOT=0, el valor del ejemplo anterior, el objeto Serial está conectado a UART0. Tienes que conectar el monitor al puerto marcado UART. Si conectas el USB nativo, el firmware funciona perfectamente y el monitor permanece en blanco para siempre.
Con ARDUINO_USB_CDC_ON_BOOT=1, Serial sale por el USB nativo. Es más práctico porque un solo cable hace todo, pero tiene un efecto secundario: después de cada grabación el puerto desaparece y vuelve a aparecer en el sistema, y los primeros mensajes se pierden durante ese intervalo. Por eso el ejemplo anterior espera un segundo al comienzo de setup.
Si el monitor está mudo, cambia el puerto físico antes de tocar el código. Resuelve la mayoría de los casos.
Aprovechar los 16 MB de flash
La tabla de particiones predeterminada está diseñada para módulos de 4 MB. Usarla en una N16R8 desperdicia más de dos tercios del chip y el compilador se queja de que el sketch es demasiado grande mientras quedan 12 MB sin usar.
Crea un archivo llamado partitions.csv en la raíz del proyecto:
| 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, |
Indica ese archivo en platformio.ini con la línea board_build.partitions = partitions.csv.
Este diseño reserva dos espacios de aplicación de 3 MB, requisito para actualizaciones OTA: el firmware nuevo se graba en el espacio libre mientras el actual sigue funcionando y el cambio solo se produce si la grabación termina correctamente. Los 9 MB restantes quedan para SPIFFS, archivos de configuración, páginas web o registros locales.
Si no piensas usar OTA, también es válida una sola aplicación de 6 MB y más espacio de datos. Lo importante es no dejar la tabla de 4 MB en una placa de 16.
Cuándo conviene crear una definición de placa propia
Repetir diez líneas de board_build en cada proyecto nuevo cansa y favorece los errores. La alternativa es registrar la placa una vez.
Crea una carpeta boards en la raíz del proyecto y dentro un archivo JSON con el nombre de tu 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 | } |
Al guardarlo como esp32s3_n16r8.json, todo el platformio.ini se reduce a board = esp32s3_n16r8 más el framework. PlatformIO busca primero en la carpeta boards del proyecto y después en las placas oficiales, así que la definición viaja con el repositorio y el siguiente desarrollador compila sin configurar nada.
Errores comunes del ESP32-S3 y cómo resolverlos
La placa se reinicia en bucle justo después de grabar. Casi siempre es un memory_type incompatible con la PSRAM física. Confirma si el módulo es R8 (octal, qio_opi) o R2 (quad, qio_qspi).
El monitor serie no muestra nada. Cambia el puerto USB antes de cualquier otra cosa, como se explicó arriba. Si persiste, comprueba que monitor_speed en platformio.ini coincide con el valor de Serial.begin.
A fatal error occurred: Failed to connect to ESP32-S3. Mantén BOOT pulsado, toca RESET, suelta BOOT y graba de nuevo. Así fuerzas el modo de grabación.
La grabación falla a mitad o corrompe datos. Una upload_speed de 921600 es agresiva para cables largos o de baja calidad. Bájala a 460800 y prueba.
Sketch too big, incluso con 16 MB. La tabla de particiones sigue siendo la de 4 MB. Aplica el partitions.csv de la sección anterior.
La PSRAM aparece como cero. Falta la flag -DBOARD_HAS_PSRAM o memory_type es incorrecto. Ejecuta la prueba de diagnóstico de esta página antes de cambiar el resto del proyecto.
