♻️
Clasificador de Residuos Solidos
Manual de uso del sistema de clasificacion automatica con ESP32-S3, inteligencia artificial y servidor en la nube
v2.0 · Mayo 2026
01 Arquitectura del sistema
El sistema usa una arquitectura PUSH: el ESP32 toma una foto y la envia al servidor en la nube, que clasifica el residuo y muestra el resultado en el dashboard. Esto permite que el ESP32 este en cualquier red con internet (como la red del colegio) sin necesidad de que el servidor pueda alcanzarlo directamente.
→
→
→
🤖
3 modelos IA
Groq + Mistral + EI
→
⚖️
DeepSeek
Arbitro final
→
El ESP32 no necesita estar en la misma red que el servidor. Solo necesita acceso a internet. El servidor ya esta corriendo en la nube en clasificador.207.180.252.88.nip.io.
Flujo de una clasificacion
El ESP32 captura una foto JPEG
La camara OV3660 toma la foto automaticamente cada X segundos (configurable en el firmware).
El ESP32 envia la foto al servidor
Hace un POST HTTPS a /api/push con la imagen como cuerpo de la peticion y la API key en el header.
El servidor procesa la imagen con 3 modelos en paralelo
Groq (Llama Vision), Mistral (Pixtral) y Edge Impulse (TFLite local) analizan la imagen simultaneamente.
DeepSeek arbitra el resultado final
Recibe los 3 resultados, detecta consenso o conflicto, y emite el veredicto definitivo con razon explicada.
El dashboard muestra el resultado
El panel se actualiza automaticamente cada 2 segundos con la categoria, objeto detectado y resultados por modelo.
02 Modelos de inteligencia artificial
El sistema usa 4 modelos de IA con roles distintos para maximizar la precision:
Groq Llama 4 Scout Vision
Modelo de vision de Meta que analiza la imagen y clasifica el residuo. Accede via API de Groq (gratuita). Responde en menos de 2 segundos.
Mistral Pixtral 12B
Modelo de vision de Mistral AI, especializado en comprension visual. Segundo opinion independiente de Groq. API gratuita con registro.
Edge Impulse TFLite local
Modelo TensorFlow Lite entrenado con datos reales de residuos. Corre directamente en el servidor sin llamadas externas. Clasifica en categorias: Reciclaje, Organica, No reciclable.
DeepSeek Arbitro de coherencia
No analiza la imagen directamente. Recibe los 3 resultados anteriores y determina el veredicto final segun reglas: si coinciden 2 de 3, aplica mayoria. Si todos difieren, razona segun el objeto detectado.
Reglas del arbitro (DeepSeek)
| SITUACION | DECISION | CONFIANZA |
| Los 3 modelos coinciden | Confirma la categoria | ALTA |
| 2 de 3 coinciden | Aplica la mayoria | ALTA |
| Todos difieren | Analiza segun objeto detectado | MEDIA |
| Algunos fallaron | Usa los resultados validos | MEDIA |
| Todos fallaron | DESCONOCIDO | BAJA |
03 Configurar el ESP32-S3
El ESP32 actua en modo PUSH: toma fotos y las envia al servidor. No necesita que el servidor le haga peticiones. Funciona desde cualquier red WiFi con internet.
Requisitos de hardware
| COMPONENTE | DETALLE |
| Placa | ESP32-S3 con camara OV3660 y PSRAM |
| Cable | USB-C al puerto USB-UART (para programar) |
| Red | WiFi 2.4GHz con acceso a internet |
Configurar Arduino IDE
Instalar Arduino IDE 2.x
Descargar de arduino.cc/en/software e instalar normalmente.
Agregar soporte para ESP32
Archivo → Preferencias → campo "URLs adicionales de placas", agregar:
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
Instalar placa ESP32
Herramientas → Placa → Gestor de Placas → buscar "esp32" → instalar "esp32 by Espressif Systems" v3.x
Configurar la placa
Herramientas → Placa → ESP32S3 Dev Module. Configurar PSRAM: OPI PSRAM, Partition Scheme: Huge APP.
Si el ESP32 no aparece en los puertos COM, instala el driver CH340 o CP210x segun tu placa. Algunos cables USB son solo de carga — usa uno con datos.
Firmware (copiar en Arduino IDE)
Este es el codigo completo para el modo PUSH. Solo cambia las 3 lineas marcadas:
CameraStream.ino — Modo PUSH al servidor
/*
ESP32-S3 - Clasificador de Residuos - Modo PUSH
Envia fotos al servidor en la nube via HTTPS
Placa: ESP32-S3 con OV3660 + PSRAM
*/
#include "esp_camera.h"
#include <WiFi.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>
// =========== CONFIGURAR AQUI ===========
const char* WIFI_SSID = "NombreDeWifi"; // ← CAMBIAR
const char* WIFI_PASS = "ClaveDeWifi"; // ← CAMBIAR
const char* SERVER_URL = "https://clasificador.207.180.252.88.nip.io/api/push";
const char* API_KEY = "esp32clasif2025";
const int INTERVALO = 5000; // ms entre fotos (5s)
// =======================================
// Pines camara OV3660 (ESP32-S3)
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 15
#define SIOD_GPIO_NUM 4
#define SIOC_GPIO_NUM 5
#define Y9_GPIO_NUM 16
#define Y8_GPIO_NUM 17
#define Y7_GPIO_NUM 18
#define Y6_GPIO_NUM 12
#define Y5_GPIO_NUM 10
#define Y4_GPIO_NUM 8
#define Y3_GPIO_NUM 9
#define Y2_GPIO_NUM 11
#define VSYNC_GPIO_NUM 6
#define HREF_GPIO_NUM 7
#define PCLK_GPIO_NUM 13
bool iniciarCamara() {
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sccb_sda = SIOD_GPIO_NUM;
config.pin_sccb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
config.frame_size = FRAMESIZE_VGA; // 640x480
config.jpeg_quality = 12;
config.fb_count = 2;
config.fb_location = CAMERA_FB_IN_PSRAM;
config.grab_mode = CAMERA_GRAB_LATEST;
if (esp_camera_init(&config) != ESP_OK) {
Serial.println("Error iniciando camara");
return false;
}
sensor_t *s = esp_camera_sensor_get();
s->set_brightness(s, 1);
s->set_saturation(s, 0);
return true;
}
void enviarFoto() {
camera_fb_t* fb = esp_camera_fb_get();
if (!fb) { Serial.println("Error capturando foto"); return; }
Serial.printf("Foto: %d bytes → enviando...\n", fb->len);
WiFiClientSecure client;
client.setInsecure(); // acepta certificado del servidor
HTTPClient http;
http.begin(client, SERVER_URL);
http.addHeader("Content-Type", "image/jpeg");
http.addHeader("X-API-Key", API_KEY);
http.setTimeout(20000);
int code = http.POST(fb->buf, fb->len);
esp_camera_fb_return(fb);
if (code == 200) {
String resp = http.getString();
Serial.println("OK: " + resp);
} else {
Serial.printf("Error HTTP %d\n", code);
}
http.end();
}
void setup() {
Serial.begin(115200);
Serial.println("\n=== Clasificador de Residuos - PUSH Mode ===");
if (!iniciarCamara()) { while(1) delay(1000); }
Serial.println("Camara OK");
WiFi.begin(WIFI_SSID, WIFI_PASS);
WiFi.setSleep(false);
Serial.print("Conectando WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500); Serial.print(".");
}
Serial.println("\nWiFi OK - IP: " + WiFi.localIP().toString());
Serial.println("Enviando a: " + String(SERVER_URL));
}
void loop() {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi perdido, reconectando...");
WiFi.reconnect();
delay(5000);
return;
}
enviarFoto();
delay(INTERVALO);
}
Conectar el ESP32 al PC por USB-UART
Usar el puerto marcado como USB-UART (no el USB-OTG). Si no aparece el puerto, instalar el driver CH340.
Pegar el codigo y cambiar WiFi
Archivo → Nuevo Sketch, pegar el codigo completo. Cambiar NombreDeWifi y ClaveDeWifi por los datos reales de la red.
Poner en modo bootloader si no sube
Mantener BOOT presionado, presionar RESET, soltar BOOT. Luego clic en Subir en Arduino IDE.
Abrir Serial Monitor (115200 baud)
Debe mostrar "Camara OK", "WiFi OK" y luego "OK: {ok: true, ...}" en cada foto enviada.
Cuando el Serial Monitor muestre OK: {"ok":true,...} el ESP32 esta correctamente conectado al servidor y el dashboard mostrara el chip ESP32 en verde: PUSH · ONLINE.
04 Usar el dashboard
El dashboard esta disponible en:
🌐 https://clasificador.207.180.252.88.nip.io
Panel de camara (izquierda)
Cuando el ESP32 no esta conectado, el panel muestra el area de carga en modo demo. Permite subir fotos desde el celular, galeria o webcam para clasificar manualmente.
Cuando el ESP32 envia su primera foto, el panel cambia automaticamente a mostrar la ultima imagen recibida.
Panel de resultados (derecha)
Muestra la categoria detectada (APROVECHABLE / NO APROVECHABLE / ORGANICO), el objeto identificado, el nivel de confianza y los resultados individuales de los 4 modelos en bloques de terminal.
Panel "CONECTAR ESP32" (abajo)
Expandible con clic. Contiene:
| ELEMENTO | DESCRIPCION |
| Endpoint | URL del servidor para configurar en el firmware del ESP32 |
| API Key | Clave de autenticacion que debe ir en el header X-API-Key |
| Ultimo frame | Tiempo transcurrido desde la ultima foto recibida |
| Estado | Badge que cambia de DESCONECTADO a CONECTADO (verde) automaticamente |
| Codigo Arduino | Codigo completo listo para copiar al IDE con boton COPIAR TODO |
Modos de captura (modo demo)
| BOTON | FUNCION |
| TOMAR FOTO | Abre la camara del dispositivo directamente (ideal en celular) |
| GALERIA | Seleccionar una imagen guardada en el dispositivo |
| CAMARA EN VIVO | Activa la webcam del computador para capturar con el boton CAPTURAR |
Indicadores en la barra superior
| CHIP | VERDE | AMARILLO / ROJO |
| ESP32 | PUSH · ONLINE — recibiendo fotos | DEMO — sin ESP32 |
| GROQ | OK — clasifico correctamente | PENDIENTE — sin resultado |
| MISTRAL | OK — clasifico correctamente | PENDIENTE — sin resultado |
| EDGE IMPULSE | OK — modelo TFLite funciono | TFLite — pendiente |
| DEEPSEEK | OK — arbitro emitio veredicto | ARBITRO — pendiente |
05 Categorias de residuos
El sistema clasifica en 3 categorias segun la normativa colombiana de separacion de residuos:
♻️
APROVECHABLE
Residuos que pueden reciclarse y reintroducirse en la cadena productiva.
Ejemplos: botellas de plastico, latas de aluminio, vidrio, carton, papel blanco, cajas de cereal, revistas, botellas de vidrio.
🚫
NO APROVECHABLE
Residuos que no pueden reciclarse ni aprovecharse. Van al relleno sanitario.
Ejemplos: papel higienico, servilletas usadas, icopor (poliestireno), empaques contaminados con grasa, pañales, papel carbon, pitillos.
🍂
ORGANICO
Residuos de origen biologico que se pueden compostar o aprovechar como abono.
Ejemplos: cascaras de fruta, restos de comida, verduras, huesos, hojas, pasto, cafe usado, residuos de jardin.
Coloca el objeto centrado frente a la camara con buena iluminacion y sin objetos de fondo que confundan al modelo. La confianza es mayor cuando el objeto ocupa la mayor parte del encuadre.
06 Referencia de la API
El servidor expone los siguientes endpoints:
| METODO | ENDPOINT | DESCRIPCION |
| GET | / | Dashboard principal (interfaz web) |
| GET | /manual | Este manual |
| GET | /api/classify | Estado actual de clasificacion (JSON) |
| POST | /api/upload | Subir imagen desde el navegador (multipart/form-data) |
| POST | /api/push | Recibir frame del ESP32 (image/jpeg raw) · requiere X-API-Key |
| GET | /api/snapshot | Ultimo frame recibido como imagen JPEG |
| GET | /api/classify_now | Fuerza clasificacion inmediata (solo modo ESP32_IP) |
Ejemplo: enviar imagen con curl
# Enviar una imagen JPEG al endpoint push
curl -X POST https://clasificador.207.180.252.88.nip.io/api/push \
-H "Content-Type: image/jpeg" \
-H "X-API-Key: esp32clasif2025" \
--data-binary @foto.jpg
# Respuesta esperada:
{"ok": true, "category": "APROVECHABLE", "confidence": "ALTA"}
07 Solucion de problemas
El ESP32 muestra "Error HTTP -1" o no conecta
- Verificar que el WiFi tiene acceso a internet (no solo a la red local)
- Algunos colegios bloquean HTTPS saliente — pedir al administrador que permita el puerto 443
- Revisar que WIFI_SSID y WIFI_PASS esten correctos (mayusculas/minusculas)
- El ESP32 solo soporta WiFi 2.4GHz — no funciona en redes 5GHz
Error HTTP 401 (Unauthorized)
La API key es incorrecta. Verificar que el header X-API-Key tenga exactamente el valor esp32clasif2025 (sin espacios ni comillas extra).
Error "Camera init failed"
Los pines de la camara pueden variar segun el modelo de placa ESP32-S3. Busca el pinout exacto de tu placa y ajusta las definiciones #define Y2_GPIO_NUM etc. al inicio del codigo.
La placa no aparece en los puertos COM
- Instalar driver CH340 (buscar "CH340 driver Windows" o "macOS")
- Probar con otro cable USB — muchos cables son solo de carga
- Usar el puerto USB-UART de la placa, no el USB-OTG
El dashboard muestra "DESCONECTADO" aunque el ESP32 envia fotos
- Verificar en el Serial Monitor que el HTTP code sea 200 (no -1 ni 401)
- Refrescar el navegador y esperar 2-3 segundos
- Revisar el log del servidor:
tail -f /tmp/clasificador.log
La clasificacion es incorrecta o dice "SIN OBJETO"
- Asegurar que el objeto este bien iluminado y centrado en el encuadre
- Evitar fondos muy cargados o similares al objeto
- Acercar el objeto a la camara para que ocupe mas del encuadre
- El nivel de confianza BAJA o MEDIA indica que los modelos no coincidieron — repetir la clasificacion
El servidor no responde o da error 502
El proceso Flask puede haberse caido. Contactar al administrador del servidor para reiniciarlo, o ejecutar en el servidor: tail -20 /tmp/clasificador.log para ver el error.