¿Se puede transcribir voz con un modelo tipo Whisper dentro de un ESP32, sin internet y sin servidor? Hasta hace poco la respuesta era no: el modelo base de Whisper pesa unos 140 MB. Este proyecto usa Whistle, el modelo de voz a texto de Cactus Compute, recortado para que quepa en la flash de un ESP32-S3 con 16 MB de flash y 8 MB de PSRAM. Hablas a un micrófono I²S INMP441 mientras mantienes apretado un botón, sueltas, y en unos seis segundos el ESP32 entiende la orden y cambia el color del LED RGB que la placa trae soldado.

Al final vas a saber recortar el modelo, cargar el firmware con ESP IDF, escribir el modelo en su partición de flash y probarlo desde un monitor de terminal. También vas a entender por qué cada pin va donde va, que es lo que te permite después cambiar el LED por un relé o un servo.

El concepto: cuánto cabe en 16 MB

El modelo descargado pesa 16,9 MB, más que toda la flash del módulo. El repositorio trae un script que le saca capas al modelo y lo deja en 14,52 MB con cuatro capas. Ese número no es casual: el firmware vive en los primeros 2 MB de la flash y el modelo se escribe a partir de la dirección 0x210000, así que ocupa prácticamente todo lo que queda. Por eso este proyecto no funciona en una placa de 4 u 8 MB de flash: no es cuestión de optimizar, el archivo simplemente no entra. La PSRAM de 8 MB es la otra mitad del truco, porque ahí se guardan el audio grabado y los cálculos intermedios de la red mientras transcribe.

Comparado con las alternativas, el enfoque es distinto:

  • Servicios en la nube (Wit.ai y similares): transcriben mejor y más rápido, pero necesitan WiFi, mandan tu voz a un servidor y dependen de una API que puede cambiar.
  • Detección de palabras clave (Edge Impulse y parecidos): reconocen un puñado de palabras entrenadas, responden casi al instante y caben en placas pequeñas, pero no entienden frases libres.
  • Whistle en el ESP32-S3: transcribe frases completas sin conexión, a cambio de unos seis segundos de espera y de exigir una placa con 16 MB de flash.

El hardware

Componentes del proyecto: placa ESP32-S3, micrófono INMP441, pulsador, protoboard y cables

Son solo tres piezas activas. La placa del autor es un módulo ESP32-S3 CAM (N16R8), pero la cámara no se usa: lo que importa es la combinación de 16 MB de flash, 8 MB de PSRAM y el LED RGB WS2812 en GPIO 48. El INMP441 es un micrófono MEMS que entrega el audio ya digitalizado por I²S, así que el ESP32 no tiene que lidiar con un conversor analógico ni con el ruido de un preamplificador. El pulsador marca el inicio y el fin de la grabación.

Diagrama de conexión entre el ESP32-S3, el micrófono INMP441 y el pulsador

Pin del componente Pin del ESP32-S3 Por qué
INMP441 VDD 3V3 El micrófono es de 3,3 V. Nunca a 5V.
INMP441 GND GND Tierra común
INMP441 L/R GND Con L/R a tierra el micrófono transmite en el canal izquierdo
INMP441 SCK GPIO 4 Reloj de bit I²S
INMP441 WS GPIO 5 Selección de palabra (izquierda o derecha)
INMP441 SD GPIO 6 Datos de audio
Pulsador, pata 1 GPIO 7 Usa el pull up interno
Pulsador, pata 2 GND Al apretar, GPIO 7 lee 0
LED RGB WS2812 GPIO 48 Ya viene en la placa

Dos detalles que suelen fallar. El pin L/R no se puede dejar al aire: si flota, el micrófono puede transmitir en el canal que el firmware no escucha y la grabación sale en silencio. Y SCK con WS invertidos es el error de cableado más común con el INMP441: la placa graba, pero lo que llega es ruido. Si el LED parpadea en rojo dos veces después de cada intento (es la señal de que no se detectó texto), revisa esas dos cosas antes que nada.

El software, paso a paso

El proyecto usa ESP IDF 5.5 o superior (el autor usó la 5.5.5) en Windows con PowerShell. Instálalo desde la página oficial de Espressif y ábrelo desde el menú Inicio.

1. Clonar el repositorio y preparar Python

Ubícate en la carpeta donde guardas tus proyectos y clona el repositorio:

Powershell
cd $HOME\Documents\Projects
Powershell
git clone https://github.com/andrisgauracs/whistle-esp32.git
cd whistle-esp32

Crea un entorno virtual para las herramientas del proyecto y actívalo:

Powershell
python -m venv .venv
Powershell
.\.venv\Scripts\Activate.ps1

Si PowerShell bloquea el script de activación, habilítalo solo para la sesión actual:

Powershell
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process

Instala las dependencias:

Powershell
pip install numpy rich pyserial huggingface_hub 
pip install esptool

2. Descargar y recortar el modelo

Desde la raíz del repositorio, crea la carpeta del modelo, descárgalo desde Hugging Face y recórtalo a cuatro capas:

Powershell
mkdir -Force model\whistle
Powershell
python -c "from huggingface_hub import hf_hub_download; import shutil; shutil.copy(hf_hub_download('Cactus-Compute/whistle','whistle.cact'), 'model/whistle/whistle.cact')"
Powershell
python tools\whistle_strip.py model\whistle\whistle.cact 4 model\whistle\whistle_d4.cact

Captura con el tamaño del modelo descargado (16,9 MB) y del modelo recortado (14,52 MB)

3. Compilar y cargar el firmware

Entra a esp32\whistle_demo. Acá conviene desactivar el entorno virtual del proyecto, porque ESP IDF trae su propio entorno de Python y los dos chocan:

Powershell
deactivate

En Windows, ESP IDF 5.5 suele instalarse en C:\Espressif o dentro de tu carpeta de usuario. Este comando encuentra el export.ps1:

Powershell
Get-ChildItem -Path "C:\Espressif", "C:\Users\$env: USERNAME" -Filter "export.ps1" -Recurse -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName

Ejecútalo con un punto delante, según dónde haya quedado. Por ejemplo:

Powershell
. C:\Espressif\frameworks\esp-idf-v5.5.5\export.ps1
Powershell
. C:\Users\Pavilion\esp\esp-idf\export.ps1

Ahora fija el chip, compila y carga. Cambia COMx por el puerto que muestra el Administrador de dispositivos:

Powershell
idf.py set-target esp32s3
idf.py build
Powershell
idf.py -p COMx flash

4. Escribir el modelo en la flash

Vuelve a la raíz, reactiva el entorno del proyecto y escribe el modelo en la dirección 0x210000:

Powershell
cd ..\..
Powershell
.\.venv\Scripts\Activate.ps1
Powershell
esptool.py --chip esp32s3 -p COM6 -b 921600 write_flash 0x210000 model\whistle\whistle_d4.cact

Salida de esptool al terminar de escribir el modelo en el ESP32-S3

El -b 921600 acelera la escritura: con 14 MB, a la velocidad por defecto te quedarías varios minutos mirando la barra.

5. Cambiar de puerto USB y abrir el monitor

Este paso es el que más confunde. Las placas ESP32-S3 de este tipo traen dos conectores USB-C: uno marcado UART, que pasa por un chip conversor (CP2102 o CH340) conectado a los pines TX0/RX0, y otro marcado USB OTG, que va directo al USB nativo del ESP32-S3. El firmware de Whistle se comunica por el USB nativo, así que para el monitor tienes que mover el cable al conector USB OTG. Si te quedas en el UART, el monitor abre pero nunca recibe nada.

Powershell
python tools\needle_tui.py --whistle --serial COMx

Monitor de terminal de Whistle con la placa ESP32-S3 conectada

Pruebas: qué pasa cuando hablas

Mientras mantienes el botón apretado, el monitor muestra "Recording" y el LED queda en blanco. Al soltar, cambia a "Transcribing" y el LED parpadea lento en blanco; esa etapa toma cerca de seis segundos.

Monitor de Whistle en estado de grabación

Monitor de Whistle mientras transcribe el audio

Con la transcripción lista, el monitor muestra el texto y la acción, y el LED responde. Las órdenes que probó el autor (en inglés) son del tipo "Change colour to Red", "Flash green light for 5 seconds", "Blink violet every half second" o simplemente "Purple". Cualquier otra frase igual se transcribe y aparece en el monitor, aunque no dispare nada.

Monitor de Whistle con el texto transcrito y la acción ejecutada

Si quieres resultados más estables:

  • Habla a unos 10 a 20 cm del micrófono y sin ventilador ni música de fondo: el INMP441 es omnidireccional y capta todo.
  • Aprieta el botón, espera medio segundo y recién habla. Las primeras sílabas que se cortan son la causa más común de transcripciones raras.
  • Usa frases cortas y con verbo al inicio. El modelo recortado a cuatro capas pierde algo de precisión frente al completo; las órdenes simples sufren menos.

Variantes y mejoras

  • Un relé en vez del LED. El LED de GPIO 48 es solo la salida de demostración. Conecta un módulo relé de 3,3 V a un GPIO libre (evita los usados por el micrófono y el botón) y, en la función que interpreta el texto, activa el relé cuando aparezca "encender" o "apagar". Así controlas una lámpara por voz sin depender de la nube.
  • Órdenes en español. El autor probó en inglés, pero presenta el modelo como multilingüe. Antes de armar un sistema completo, graba unas cuantas frases en español y mira en el monitor qué transcribe; si las palabras clave salen bien, compara contra el texto con minúsculas y sin tildes para tolerar pequeñas diferencias.
  • Respuesta hablada. Suma un amplificador I²S MAX98357A y un parlante para que el ESP32 confirme en voz alta lo que entendió, combinando este proyecto con un sistema de texto a voz offline.

Personalización para Chile

En Chile consigues casi todo lo necesario en MechatronicStore:

  • Módulo Micrófono I2S INMP441 (SKU GL3-12): el micrófono digital que graba tu voz por I²S.
  • Botón pulsador switch de 4 pines push button 12x12x8mm (SKU GA6-16): inicia y detiene la grabación.
  • Breadboard / Protoboard 830 puntos MB102 (SKU C-302): para montar el circuito sin soldar.
  • Cables macho macho 40 piezas 20cm (SKU C-411): las conexiones entre la placa, el micrófono y el botón.
  • Cable USB Tipo C a USB Tipo A 1mt (SKU B-101): para cargar el firmware y conectar el puerto USB OTG.

La placa es el punto delicado: necesitas un ESP32-S3 con 16 MB de flash y 8 MB de PSRAM (variante N16R8), porque el modelo no entra en menos. El XIAO ESP32 S3 estándar trae 8 MB de flash, así que no sirve para este proyecto; revisa la flash de cualquier placa antes de comprarla.

Recursos

Versión chilena con componentes en stock local en MechatronicStore.