#include #include #include #include #include #include // Biblioteca para manejar JSON // Estructura para almacenar datos de los clientes struct Cliente { String id; String ubicacion; float medida; // Medida original String ip; // Dirección IP del cliente unsigned long ultimaActualizacion; // Timestamp de la última actualización }; std::vector clients; // Lista dinámica para almacenar los datos de los clientes // Configuración del AP const char* ssid = "RedRecolector"; const char* password = "MateUruguayo"; IPAddress local_IP(192, 168, 4, 2); IPAddress gateway(192, 168, 4, 1); IPAddress subnet(255, 255, 255, 0); const unsigned long KEEP_ALIVE_INTERVAL = 30000; // Intervalo para enviar keep-alive (30 segundos) WebServer server(80); unsigned long lastKeepAlive = 0; // Marca de tiempo para el último keep-alive // URL del servidor Flask String flaskServerURL = "http://192.168.4.200:5000/update"; // Prototipos de funciones void handleRoot(); void handleUpdate(); void handleData(); void checkClientAlive(); //void enviarDatosAlServidorFlask(String id, String ubicacion, float medida, String ip); void setup() { Serial.begin(115200); // Configurar AP con IP fija if (!WiFi.softAPConfig(local_IP, gateway, subnet)) { Serial.println("Error al configurar la IP fija."); return; } // Iniciar el punto de acceso if (WiFi.softAP(ssid, password, 1)) { // Canal 1 especificado Serial.println("Punto de acceso iniciado con éxito."); Serial.print("SSID: "); Serial.println(ssid); Serial.print("Contraseña: "); Serial.println(password); } else { Serial.println("Error al iniciar el punto de acceso."); return; } Serial.print("Servidor iniciado. Dirección IP: "); Serial.println(WiFi.softAPIP()); // Configurar rutas del servidor web server.on("/", handleRoot); server.on("/update", handleUpdate);// Ruta para recibir los datos de los clientes server.on("/data", handleData); // Ruta para enviar datos en tiempo real server.begin(); } void loop() { server.handleClient(); // Enviar keep-alive a los clientes if (millis() - lastKeepAlive >= KEEP_ALIVE_INTERVAL) { checkClientAlive(); lastKeepAlive = millis(); } } // Página principal para mostrar clientes conectados void handleRoot() { String html = R"rawliteral( Servidor Recipientes
Recipientes conectados
)rawliteral"; server.send(200, "text/html", html); } void handleData() { StaticJsonDocument<2048> doc; JsonArray array = doc.to(); for (const auto& client : clients) { JsonObject obj = array.createNestedObject(); obj["id"] = client.id; obj["ubicacion"] = client.ubicacion; obj["ip"] = client.ip; // Calcular porcentaje de llenado float percentage = (28.8 - client.medida) * 5; percentage = max(0.0f, min(100.0f, percentage)); // Redondear y convertir a entero int percentageInt = static_cast(percentage + 0.5); obj["percentage"] = percentageInt; // Determinar color y estado if (client.medida < 9.3) { obj["color"] = "red"; obj["status"] = "CRÍTICO"; } else if (client.medida <= 10.8) { obj["color"] = "yellow"; obj["status"] = "Listo para retirar"; } else { obj["color"] = "green"; obj["status"] = "Normal"; } } String json; serializeJson(doc, json); server.send(200, "application/json", json); } void handleUpdate() { if (server.hasArg("id") && server.hasArg("ubicacion") && server.hasArg("medida") && server.hasArg("clienteIP")) { String id = server.arg("id"); String ubicacion = server.arg("ubicacion"); float medida = server.arg("medida").toFloat(); String clienteIP = server.arg("clienteIP"); Serial.println("ID: " + id); Serial.println("Ubicación: " + ubicacion); Serial.println("Medida: " + String(medida)); Serial.println("IP Cliente: " + clienteIP); // Actualizar la lista de clientes bool updated = false; unsigned long now = millis(); for (auto& client : clients) { if (client.id == id) { client.ubicacion = ubicacion; client.medida = medida; client.ip = clienteIP; client.ultimaActualizacion = now; updated = true; break; } } if (!updated) { clients.push_back({id, ubicacion, medida, clienteIP, now}); Serial.println("Nuevo cliente conectado: " + id); } // Enviar datos al servidor Flask enviarDatosAlServidorFlask(id, ubicacion, medida, clienteIP); server.send(200, "text/plain", "Datos recibidos y procesados."); } else { server.send(400, "text/plain", "Parámetros faltantes"); } } void checkClientAlive() { for (auto it = clients.begin(); it != clients.end();) { HTTPClient http; String url = "http://" + it->ip + "/keepalive"; // Eliminar el parámetro ID http.begin(url); int httpResponseCode = http.GET(); if (httpResponseCode == 200) { // El cliente respondió correctamente, mantenemos su registro Serial.println("Cliente activo: " + it->ip); ++it; } else { // El cliente no responde, eliminamos el registro Serial.println("Cliente no responde: " + it->id + " en IP: " + it->ip); it = clients.erase(it); } http.end(); } } // Función para enviar los datos al servidor Flask void enviarDatosAlServidorFlask(String id, String ubicacion, float medida, String ip) { HTTPClient http; http.begin(flaskServerURL); http.addHeader("Content-Type", "application/x-www-form-urlencoded"); String postData = "id=" + id + "&ubicacion=" + ubicacion + "&medida=" + String(medida) + "&ip=" + ip; int httpResponseCode = http.POST(postData); if (httpResponseCode > 0) { Serial.println("Datos enviados al servidor Flask: " + String(httpResponseCode)); } else { Serial.println("Error enviando datos al servidor Flask: " + String(httpResponseCode)); } http.end(); }