¿Y si un microcontrolador de menos de treinta lucas pudiera describir con voz lo que hay adelante de una persona ciega, en vez de limitarse a pitar cuando detecta un obstáculo? Esa es la diferencia entre un bastón con sensor ultrasónico y este colgante: uno avisa "hay algo cerca", el otro dice "una silla a tu izquierda y una persona caminando de frente". En este proyecto vas a construir un asistente de visión con IA basado en un ESP32-CAM que captura una foto al apretar un botón, la sube a la nube para que un modelo la describa, convierte ese texto en audio y lo reproduce por un parlante. Al terminar vas a entender cómo compartir los GPIO entre la cámara y el amplificador de audio, y cómo encadenar dos servicios en la nube desde un chip diminuto.
Concepto: por qué visión en lugar de proximidad
La mayoría de las ayudas DIY para personas ciegas usan sensores de distancia (ultrasónico, IR, PIR) y entregan un pitido o una vibración. Funcionan, pero solo responden a la pregunta "¿hay algo enfrente?". Un enfoque basado en cámara responde algo mucho más útil: "¿qué es lo que tengo enfrente?". Esa es la apuesta de este colgante.
El flujo completo tiene cuatro etapas encadenadas: el ESP32-CAM toma la imagen, la sube a un servicio de imagen-a-texto que devuelve una descripción en JSON, ese texto se manda a un motor de texto-a-voz (TTS) que devuelve el audio en Base64, y finalmente el ESP32 decodifica y reproduce ese audio amplificado por el MAX98357.

Adaptación para Chile (y el mundo hispano): el tutorial original usa Sarvam AI, un motor de TTS especializado en idiomas de India (hindi, tamil, malayalam). Para un usuario chileno eso no sirve. La buena noticia es que el arreglo del array de idiomas es abierto: puedes reemplazar el motor por uno con voz en español neutro (Google Cloud TTS, Azure Speech o la API de ElevenLabs), y ajustar el prompt para que la descripción salga en español: "Describe en menos de 15 palabras los obstáculos que hay al frente para una persona ciega. Indica si el camino está libre o bloqueado." El resto de la arquitectura no cambia.
Hardware y conexiones
Lo interesante a nivel de ingeniería es que el ESP32-CAM casi no tiene GPIO libres: la mayoría los ocupa el sensor de cámara y el lector de microSD. Por eso el amplificador I2S comparte físicamente las líneas de datos de la cámara (GPIO 12, 14 y 15) y el código va instalando y desinstalando el driver I2S según le toque hablar o fotografiar. Esa multiplexación en el tiempo es el truco central del proyecto.

La alimentación parte de una batería que pasa por un interruptor y sube a 5V estables con un convertidor boost; esos 5V alimentan tanto el ESP32-CAM como el amplificador. No hay obligación de usar exactamente los mismos componentes: cualquier boost que entregue unos 600 mA en promedio sirve.

Software: configuración de pines
En el arranque del sketch se definen las librerías, las credenciales y el mapeo de pines. GPIO 13 dispara la captura, GPIO 2 cambia el idioma y GPIO 4 controla el LED flash integrado. GPIO 14, 12 y 15 quedan reservados para el amplificador I2S:
#define BOARD_HAS_PSRAM
#include "esp_camera.h"
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include "driver/i2s.h"
#include "mbedtls/base64.h"
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#ifndef RTC_CNTL_BROWNOUT_REG
#define RTC_CNTL_BROWNOUT_REG RTC_CNTL_BROWN_OUT_REG
#endif
// --- PINS ---
#define CAPTURE_BTN_PIN 13
#define LANG_BTN_PIN 2
#define FLASH_LED_PIN 4
// --- MAX98357A I2S PINS (Shared with Camera Data Lines) ---
#define I2S_BCLK 14
#define I2S_LRC 12
#define I2S_DOUT 15
#define I2S_NUM I2S_NUM_0
Inicializar y liberar el amplificador I2S
Esta rutina levanta el MAX98357 a la frecuencia de muestreo indicada (16.000 Hz). Si el driver ya estaba corriendo, lo desinstala primero. Ese vaivén de instalar/desinstalar es justamente lo que permite que la cámara y el audio convivan en los mismos pines:
void initI2S(uint32_t sampleRate) {
if (i2sInitialized) {
i2s_driver_uninstall(I2S_NUM);
i2sInitialized = false;
}
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
.sample_rate = sampleRate,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 8,
.dma_buf_len = 512,
.use_apll = false,
.tx_desc_auto_clear = true,
.fixed_mclk = 0
};
i2s_pin_config_t pin_config = {
.bck_io_num = I2S_BCLK,
.ws_io_num = I2S_LRC,
.data_out_num = I2S_DOUT,
.data_in_num = I2S_PIN_NO_CHANGE
};
if (i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL) == ESP_OK) {
i2s_set_pin(I2S_NUM, &pin_config);
i2s_zero_dma_buffer(I2S_NUM);
i2sInitialized = true;
}
}
Parsear la respuesta del TTS
En vez de armar un árbol JSON completo (pesado para la RAM del ESP32), el código busca directamente la cadena Base64 del audio dentro de la respuesta. Es una optimización deliberada para no saturar la memoria:
String extractBase64Audio(const String& jsonResponse) {
int keyIndex = jsonResponse.indexOf("\"audios\":[\"");
if (keyIndex == -1) keyIndex = jsonResponse.indexOf("\"audios\": [\"");
if (keyIndex != -1) {
int start = jsonResponse.indexOf("\"", keyIndex + 10) + 1;
int end = jsonResponse.indexOf("\"", start);
if (start > 0 && end > start) {
return jsonResponse.substring(start, end);
}
}
return "";
}
Ejecución: capturar y cambiar de idioma
El loop() implementa un antirrebote por software (mínimo 500 ms entre pulsaciones). El botón de GPIO 13 dispara la captura y análisis; el de GPIO 2 rota entre los idiomas configurados y anuncia el cambio por voz:
void loop() {
if (millis() - lastBtnPressTime < 500) return;
// Button 13: Capture Image & Process
if (digitalRead(CAPTURE_BTN_PIN) == LOW) {
lastBtnPressTime = millis();
captureAndAnalyze();
}
// Button 2: Toggle Language
if (digitalRead(LANG_BTN_PIN) == LOW) {
lastBtnPressTime = millis();
currentLangIndex = (currentLangIndex + 1) % TOTAL_LANGUAGES;
LanguageConfig current = languages[currentLangIndex];
Serial.printf("\n[LANG] Switched to: %s\n", current.name);
TTSTiming dummyTiming = {0};
speakWithSarvam(current.notifyText, current.code, current.voice, dummyTiming);
}
}
Un detalle práctico de montaje: para flashear el ESP32-CAM tienes que dejar GPIO 0 a tierra para entrar en modo programación. Si el dispositivo no enciende con todo bien conectado, casi siempre es la batería: este proyecto consume harto y una pila débil no arranca.

Variantes y mejoras
- Voz en español offline: si no quieres depender de la nube para el TTS, puedes reemplazar el motor en línea por síntesis de voz offline en el propio ESP32. Pierde naturalidad, pero funciona sin internet, ideal para exteriores sin señal.
- Disparo por sensor de proximidad: en lugar del botón de captura puedes gatillar la foto automáticamente cuando un sensor ultrasónico detecta algo a menos de 1,5 m, para que el usuario no tenga que apretar nada.
- Registro en microSD: el ESP32-CAM ya trae ranura microSD. Puedes guardar cada foto con su descripción y hora para revisar después qué "vio" el dispositivo durante el día.
Personalización para Chile
En Chile puedes conseguir casi todo el proyecto en MechatronicStore:
- Módulo ESP32-CAM (ACEBOTT QD002) (SKU N-703). $27.320 CLP
- Convertidor Boost Step Up 5V (SKU GN1-5). $1.490 CLP
- Micro interruptor / final de carrera (disparo) (SKU GC1-11). $790 CLP
- Botón pulsador 4 pines (cambio de idioma) (SKU GA6-16). $290 CLP
- Mini switch deslizante on/off (SKU GF1-15). $590 CLP
- Conversor USB a serial TTL CP2102 (para programar) (SKU GP3-5). $3.000 CLP
- Parlante 8Ω 1W (SKU G-234V1). $2.590 CLP
- Cables macho hembra 30 cm (SKU C-418). $1.990 CLP
Dos aclaraciones honestas de equivalencia: el amplificador MAX98357 I2S que pide el tutorial no está hoy en el catálogo (es una pieza específica de audio digital I2S, y sustituirla por un amplificador análogo como el PAM8403 cambiaría la parte de código del I2S, así que no lo forzamos). Y el parlante del catálogo es de 8Ω; el original menciona 4Ω, pero el MAX98357 maneja bien de 4 a 8Ω, así que el de 8Ω cumple igual.
Recursos
- Tutorial original (inglés): ESP32-CAM AI Vision Assistant Pendant: DIY Build Guide
- Archivos descargables: el proyecto original publica el código completo, los STL de la carcasa impresa en 3D y el diagrama de circuito en el enlace del artículo de Circuit Digest.
Versión chilena con componentes en stock local en MechatronicStore, inspirada en el proyecto de Circuit Digest y re adaptada para voz en español.












