// ════════════════════════════════════════════════════════
// BASURERO INTELIGENTE - WOKWI CON GOOGLE SHEETS
// ¡Ahora SÍ envía datos reales!
// ════════════════════════════════════════════════════════
#include <WiFi.h>
#include <HTTPClient.h>
// ═══════ WIFI ═══════
const char* ssid = "Wokwi-GUEST";
const char* password = "";
// ═══════ GOOGLE APPS SCRIPT ═══════
String GOOGLE_SCRIPT_URL = "https://script.google.com/macros/s/AKfycbyp1AKH-TuuUibFV6jubN6Ct_OsCRwTYGSJuChV2lzBN4jQOvoMC7whwHPFhoOiaPcB7g/exec";
// ═══════════════════════════════════════
// CONFIGURACIÓN DEL BOTE
// ═══════════════════════════════════════
const float ALTURA_BOTE = 61.0;
const float DISTANCIA_MINIMA_SENSOR = 2.0;
const float UMBRAL_LLENO_CM = 20.0;
const float UMBRAL_MEDIO_CM = 45.0;
// ═══════ PINES ═══════
const int PIN_TRIG = 18;
const int PIN_ECHO = 19;
#define PIN_LED_VERDE 27
#define PIN_LED_AMARILLO 26
#define PIN_LED_ROJO 25
String dispositivoID = "";
// ═══════ PROTOTIPOS ═══════
void conectarWiFi();
void obtenerID();
float medirDistancia();
String determinarEstado(float distancia);
void mostrarEstadoLED(String estado);
void enviarDatosAGoogle(float nivel, float ocupado, String estado);
// ════════════════════════════════════════════════════════
// SETUP
// ════════════════════════════════════════════════════════
void setup() {
Serial.begin(115200);
delay(1000);
pinMode(PIN_LED_VERDE, OUTPUT);
pinMode(PIN_LED_AMARILLO, OUTPUT);
pinMode(PIN_LED_ROJO, OUTPUT);
pinMode(PIN_TRIG, OUTPUT);
pinMode(PIN_ECHO, INPUT);
Serial.println("\n╔══════════════════════════════════════╗");
Serial.println("║ BASURERO INTELIGENTE (WOKWI) ║");
Serial.println("╚══════════════════════════════════════╝\n");
obtenerID();
conectarWiFi();
Serial.println("\n✅ SISTEMA LISTO. Altura del bote: " + String(ALTURA_BOTE) + " cm");
}
// ════════════════════════════════════════════════════════
// LOOP
// ════════════════════════════════════════════════════════
void loop() {
Serial.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
Serial.println("Iniciando nueva lectura...");
float distanciaSensor = medirDistancia();
if(distanciaSensor < 0) {
Serial.println("❌ Error en el sensor. Omitiendo lectura.");
mostrarEstadoLED("ERROR");
delay(5000);
return;
}
Serial.println(" 📏 Distancia detectada: " + String(distanciaSensor, 1) + " cm");
// Calcular Datos
float nivelOcupadoCM = ALTURA_BOTE - distanciaSensor;
float porcentajeOcupado = (nivelOcupadoCM / ALTURA_BOTE) * 100.0;
String estadoStr = determinarEstado(distanciaSensor);
// Mostrar Estado
mostrarEstadoLED(estadoStr);
Serial.println("\n --- DATOS CALCULADOS ---");
Serial.println(" 📊 Nivel Basura: " + String(nivelOcupadoCM, 1) + " cm");
Serial.println(" 📈 Ocupado: " + String(porcentajeOcupado, 1) + "%");
Serial.println(" 🚦 Estado: " + estadoStr);
// Enviar a Google
enviarDatosAGoogle(nivelOcupadoCM, porcentajeOcupado, estadoStr);
Serial.println("\n⏳ Próxima lectura en 15 segundos...\n");
delay(15000);
}
// ════════════════════════════════════════════════════════
// FUNCIONES AUXILIARES
// ════════════════════════════════════════════════════════
void enviarDatosAGoogle(float nivel, float ocupado, String estado) {
if (WiFi.status() != WL_CONNECTED) {
Serial.println(" ❌ Error: WiFi Desconectado. Reconectando...");
conectarWiFi();
if (WiFi.status() != WL_CONNECTED) {
Serial.println(" ❌ No se pudo reconectar. Omitiendo envío.");
return;
}
}
HTTPClient http;
// Construir URL con parámetros
String url = GOOGLE_SCRIPT_URL;
url += "?dispositivo=" + dispositivoID;
url += "&nivel=" + String(nivel, 2);
url += "&ocupado=" + String(ocupado, 2);
url += "&estado=" + estado;
Serial.println("\n📤 Enviando a Google Sheets...");
Serial.println(" URL: " + url);
// CLAVE: No usar WiFiClientSecure en Wokwi, usar HTTPClient directo
http.begin(url);
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
http.setTimeout(20000); // 20 segundos timeout
int httpCode = http.GET();
String payload = http.getString();
Serial.println(" 📡 Código HTTP: " + String(httpCode));
Serial.println(" 📨 Respuesta: " + payload);
if (httpCode == 200 || httpCode == 302) {
if (payload.indexOf("success") >= 0 || payload.length() > 0) {
Serial.println(" ✅ ¡Datos enviados a Google Sheets!");
} else {
Serial.println(" ⚠️ Respuesta recibida pero sin confirmación");
}
} else if (httpCode > 0) {
Serial.println(" ⚠️ Código inesperado. Verifica el script de Google");
} else {
Serial.println(" ❌ Error de conexión: " + http.errorToString(httpCode));
}
http.end();
}
void conectarWiFi() {
// Desconectar primero si había algo conectado
WiFi.disconnect(true);
delay(1000);
// Configurar modo estación
WiFi.mode(WIFI_STA);
delay(100);
Serial.println("Conectando a WiFi [" + String(ssid) + "]...");
WiFi.begin(ssid, password);
int intentos = 0;
while (WiFi.status() != WL_CONNECTED && intentos < 40) {
delay(500);
Serial.print(".");
intentos++;
// Reintentar conexión cada 10 intentos
if (intentos % 10 == 0) {
Serial.print(" [reintentando] ");
WiFi.disconnect();
delay(500);
WiFi.begin(ssid, password);
}
}
Serial.println();
if(WiFi.status() == WL_CONNECTED) {
Serial.println("✅ WiFi Conectado Exitosamente");
Serial.println(" IP: " + WiFi.localIP().toString());
Serial.println(" Señal: " + String(WiFi.RSSI()) + " dBm");
} else {
Serial.println("❌ ERROR: No se pudo conectar a WiFi");
Serial.println(" Estado: " + String(WiFi.status()));
Serial.println(" Reiniciando en 5 segundos...");
delay(5000);
ESP.restart();
}
}
void obtenerID() {
uint8_t mac[6];
WiFi.macAddress(mac);
char macStr[18];
sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
dispositivoID = String(macStr);
Serial.println("🔑 ID Dispositivo: " + dispositivoID);
}
float medirDistancia() {
long sumaDuracion = 0;
int lecturasValidas = 0;
for (int i = 0; i < 5; i++) {
digitalWrite(PIN_TRIG, LOW);
delayMicroseconds(2);
digitalWrite(PIN_TRIG, HIGH);
delayMicroseconds(10);
digitalWrite(PIN_TRIG, LOW);
long duration = pulseIn(PIN_ECHO, HIGH, 30000);
if (duration > 0) {
sumaDuracion += duration;
lecturasValidas++;
}
delay(20);
}
if (lecturasValidas > 0) {
float distanciaPromedio = (sumaDuracion / (float)lecturasValidas) * 0.034 / 2.0;
if (distanciaPromedio > ALTURA_BOTE) distanciaPromedio = ALTURA_BOTE;
if (distanciaPromedio < DISTANCIA_MINIMA_SENSOR) distanciaPromedio = DISTANCIA_MINIMA_SENSOR;
return distanciaPromedio;
} else {
return -1.0;
}
}
String determinarEstado(float distancia) {
if (distancia < UMBRAL_LLENO_CM) return "LLENO";
if (distancia < UMBRAL_MEDIO_CM) return "MEDIO";
return "VACIO";
}
void mostrarEstadoLED(String estado) {
digitalWrite(PIN_LED_VERDE, LOW);
digitalWrite(PIN_LED_AMARILLO, LOW);
digitalWrite(PIN_LED_ROJO, LOW);
if (estado == "VACIO") {
digitalWrite(PIN_LED_VERDE, HIGH);
} else if (estado == "MEDIO") {
digitalWrite(PIN_LED_AMARILLO, HIGH);
} else if (estado == "LLENO") {
digitalWrite(PIN_LED_ROJO, HIGH);
} else if (estado == "ERROR") {
digitalWrite(PIN_LED_ROJO, HIGH);
delay(100);
digitalWrite(PIN_LED_ROJO, LOW);
}
}