瀏覽代碼

Add SDA and SCL pin configuration for OLED display in Kconfig and main.c

Co-authored-by: Copilot <copilot@github.com>
dacowars 1 周之前
父節點
當前提交
43639e4e4c
共有 2 個文件被更改,包括 457 次插入13 次删除
  1. 14 0
      main/Kconfig.projbuild
  2. 443 13
      main/main.c

+ 14 - 0
main/Kconfig.projbuild

@@ -34,4 +34,18 @@ menu "SD SPI Example Configuration"
             GPIO number where the SD card CS pin is connected.
             GPIO number where the SD card CS pin is connected.
             Make sure the pin supports input/output and has pull-up if needed.
             Make sure the pin supports input/output and has pull-up if needed.
 
 
+    config PIN_SDA
+        int "SDA pin configuration"
+        default 21
+        range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
+        help 
+            GPIO number where the OLED display SDA pin is connected.        
+
+    config PIN_SCL
+        int "SCL pin configuration"
+        default 22
+        range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
+        help 
+            GPIO number where the OLED display SCL pin is connected.  
+
 endmenu
 endmenu

+ 443 - 13
main/main.c

@@ -14,11 +14,20 @@
 #include "sdmmc_cmd.h"
 #include "sdmmc_cmd.h"
 #include "ssd1306.h"
 #include "ssd1306.h"
 #include <driver/i2c_master.h>
 #include <driver/i2c_master.h>
+#include "math.h"
+#include "string.h"
+#include "esp_wifi.h"
+#include "esp_netif.h"
+#include "esp_event.h"
+#include "esp_http_server.h"
+#include "lwip/inet.h"
+#include "sys/stat.h"
 
 
 const char *DHT_TAG = "DHT22";
 const char *DHT_TAG = "DHT22";
 const char *GPS_TAG = "GPS_PARSER";
 const char *GPS_TAG = "GPS_PARSER";
 const char *SD_TAG = "SD_CARD";
 const char *SD_TAG = "SD_CARD";
 const char *OLED_TAG = "SSD1306OLED";
 const char *OLED_TAG = "SSD1306OLED";
+const char *GEN_TAG = "General";
 
 
 #define MOUNT_POINT "/sdcard"
 #define MOUNT_POINT "/sdcard"
 
 
@@ -30,12 +39,17 @@ const char *OLED_TAG = "SSD1306OLED";
 #define PIN_NUM_SDA CONFIG_PIN_SDA
 #define PIN_NUM_SDA CONFIG_PIN_SDA
 #define PIN_NUM_SCL CONFIG_PIN_SCL
 #define PIN_NUM_SCL CONFIG_PIN_SCL
 
 
+#define PIN_TX GPIO_NUM_17
+#define PIN_RX GPIO_NUM_16
+
 //definicion de tiempos de pulsacion
 //definicion de tiempos de pulsacion
 #define PULSACION_LARGA_MS 2000
 #define PULSACION_LARGA_MS 2000
 #define DURACION_WATCHDOG_MS 10000
 #define DURACION_WATCHDOG_MS 10000
 
 
 #define MEASUREMENT_INTERVAL_S 1
 #define MEASUREMENT_INTERVAL_S 1
 
 
+#define DEG2RAD (M_PI/180.0)
+
 const int uart_buffer_size = (1024 * 2);
 const int uart_buffer_size = (1024 * 2);
 int length = 0;
 int length = 0;
 
 
@@ -43,18 +57,20 @@ uint8_t data;
 
 
 static gps_parser_t gps;
 static gps_parser_t gps;
 
 
-void uart_configurator();
-
-static esp_err_t s_example_write_file(const char *path, char *data);
-
-static i2c_master_bus_handle_t i2c_bus0_init(gpio_num_t sda, gpio_num_t scl, uint32_t hz);
+static httpd_handle_t http_server = NULL;
+static esp_netif_t *ap_netif = NULL;
+static bool wifiActivado = false;
+static uint32_t wifiLastActivity = 0;
 
 
+extern char filename[13]; // tu archivo gpx
+const char *apSSID = "MiAP";
+const char *apPassword = "password123";
 
 
 struct SensorData {
 struct SensorData {
   double latitude;
   double latitude;
   double longitude;
   double longitude;
   float altura;
   float altura;
-  char tiempo;
+  char tiempo[64];
   float velocidad;
   float velocidad;
   float temperature;
   float temperature;
   float humidity;
   float humidity;
@@ -79,10 +95,11 @@ char filename[13] = "/panchas.gpx";
 
 
 ssd1306_handle_t d = NULL;
 ssd1306_handle_t d = NULL;
 
 
+const char mount_point[] = MOUNT_POINT;
+
 void OLED_test(){
 void OLED_test(){
 
 
-    i2c_master_bus_handle_t i2c_bus =
-        i2c_bus0_init(PIN_NUM_SDA, PIN_NUM_SCL, 400000);
+    i2c_master_bus_handle_t i2c_bus = i2c_bus0_init(PIN_NUM_SDA, PIN_NUM_SCL, 400000);
 
 
     ssd1306_config_t cfg = {
     ssd1306_config_t cfg = {
         .width  = 128,
         .width  = 128,
@@ -109,7 +126,7 @@ void OLED_test(){
 
 
     ESP_ERROR_CHECK(ssd1306_display(d));
     ESP_ERROR_CHECK(ssd1306_display(d));
     
     
-    ESP_ERROR_CHECK(ssd1306_draw_text_scaled(d, 0, 0, "Iniciando...", true, ));
+    ESP_ERROR_CHECK(ssd1306_draw_text_scaled(d, 0, 0, "Iniciando...", true, 1));
 
 
     ESP_ERROR_CHECK(ssd1306_display(d));
     ESP_ERROR_CHECK(ssd1306_display(d));
 
 
@@ -117,7 +134,7 @@ void OLED_test(){
 
 
 }
 }
 
 
-void OLED_print(const char& line1, const char& line2){
+void OLED_print(const char *line1, const char *line2){
     
     
     ESP_ERROR_CHECK(ssd1306_clear(d));
     ESP_ERROR_CHECK(ssd1306_clear(d));
 
 
@@ -132,7 +149,7 @@ void DHT_test (){
     float t;
     float t;
     float h;
     float h;
 
 
-    static esp_err_t err = dht_attach_pin();
+    esp_err_t err = dht_attach_pin();
 
 
     if (err != ESP_OK) {
     if (err != ESP_OK) {
         OLED_print("DHT22", "Error");
         OLED_print("DHT22", "Error");
@@ -154,9 +171,296 @@ void DHT_test (){
 }
 }
 
 
 void SD_test(){
 void SD_test(){
+
+    esp_vfs_fat_sdmmc_mount_config_t mount_config = {
+        .format_if_mount_failed = false,
+        .max_files = 5,
+        .allocation_unit_size = 16 * 1024
+    };
+
+    sdmmc_card_t *card;
+
+    sdmmc_host_t host = SDSPI_HOST_DEFAULT();
+    host.max_freq_khz = 9000; //a mas freq no funciona, porque pipas
+
+    spi_bus_config_t bus_cfg = {
+        .mosi_io_num = PIN_NUM_MOSI,
+        .miso_io_num = PIN_NUM_MISO,
+        .sclk_io_num = PIN_NUM_CLK,
+        .quadwp_io_num = -1,
+        .quadhd_io_num = -1,
+        .max_transfer_sz = 4092,
+    };
+
+    esp_err_t ret = spi_bus_initialize(host.slot, &bus_cfg, SDSPI_DEFAULT_DMA);
+    if (ret != ESP_OK) {
+        ESP_LOGE(SD_TAG, "Failed to initialize bus.");
+        return;
+    }
+
+    sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
+    slot_config.gpio_cs = PIN_NUM_CS;
+    slot_config.host_id = host.slot;
+
+    ESP_LOGI(SD_TAG, "Mounting filesystem");
+
+    while((ret = esp_vfs_fat_sdspi_mount(mount_point, &host, &slot_config, &mount_config, &card)) != ESP_OK){
+        if (ret == ESP_FAIL) {
+            ESP_LOGE(SD_TAG, "Failed to mount filesystem. "
+                     "If you want the card to be formatted, set the CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
+            OLED_print("Error SD", "Prueba a reinsertar");
+        } else {
+            ESP_LOGE(SD_TAG, "Failed to initialize the card (%s). "
+                     "Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
+            OLED_print("Error SD", esp_err_to_name(ret));
+        }
+        vTaskDelay(pdMS_TO_TICKS(1000));
+    }
+
+    ESP_LOGI(SD_TAG, "Filesystem mounted");
+
+    OLED_print("SD correcta","");
+
+    // Card has been initialized, print its properties
+    sdmmc_card_print_info(stdout, card);
+
+
     
     
 }
 }
 
 
+void GPS_test_wait(){
+
+    // Iniciar Serial2 para GPS
+    bool fixObtained = false;
+
+    uart_configurator();
+
+    gps_parser_init(&gps);
+
+    while (!fixObtained) {
+        while (uart_read_bytes(UART_NUM_2, &data, 1, 0)){
+            gps_parser_encode(&gps, data);
+        }
+        if (gps_location_is_valid(&gps.location) && gps_date_is_valid(&gps.date) && gps_time_is_valid(&gps.time)) {
+            fixObtained = true;
+            break;
+        }
+        vTaskDelay(pdMS_TO_TICKS(300));
+        OLED_print("GPS", "Esperando");
+        vTaskDelay(pdMS_TO_TICKS(300));
+        OLED_print("GPS", "Esperando .");
+        vTaskDelay(pdMS_TO_TICKS(300));
+        OLED_print("GPS", "Esperando ..");
+        vTaskDelay(pdMS_TO_TICKS(300));
+        OLED_print("GPS", "Esperando ...");
+    }
+
+    OLED_print("GPS", "Encontrado");
+
+}
+
+float calcular_delta_dist(float lat1, float  long1, float lat2, float long2){
+  float R = 6371000.0; // Radio de la Tierra en km
+  float delta_lat = (lat2 - lat1) * DEG2RAD;
+  float delta_long = (long2 - long1) * DEG2RAD;
+  lat1 = lat1 * DEG2RAD;
+  lat2 = lat2 * DEG2RAD;
+  float a = sin(delta_lat/2)*sin(delta_lat/2)+cos(lat1)*cos(lat2)*sin(delta_long/2)*sin(delta_long/2);
+  float c = 2 * atan2(sqrt(a),sqrt(1-a));
+  return R * c;  //En m
+}
+
+void task_mediciones(void *pvParameters) {
+    TickType_t xLastWakeTime = xTaskGetTickCount();
+    char buffer[50];
+    while(1) {
+        unsigned long startMillis = pdTICKS_TO_MS(xTaskGetTickCount());
+        // se leen los valores antes de utilizar el semaphore    
+        while (uart_read_bytes(UART_NUM_2, &data, 1, 0)){
+            gps_parser_encode(&gps, data);
+        }
+
+        float new_latitude = gps_location_lat(&gps.location);
+        float new_longitude = gps_location_lng(&gps.location);
+        float new_altitude = 0.0; //gps.altitude.meters(); falta por implementar en la libreria del gps
+        int new_fecha_len = snprintf(buffer, sizeof(buffer), "%d-%02d-%02dT%02d:%02d:%02d.%03d", gps_date_year(&gps.date), gps_date_month(&gps.date),
+                                    gps_date_day(&gps.date), gps_time_hour(&gps.time), gps_time_minute(&gps.time), 
+                                    gps_time_second(&gps.time), gps_time_centisecond(&gps.time));
+        if (new_fecha_len < 0) {
+            buffer[0] = '\0';
+        }
+        float new_temp = 0.0;
+        float new_hum = 0.0;
+        dht_read(&new_temp, &new_hum);
+        float new_press = 0.0; // Placeholder, no hay sensor de presión 
+        
+        float distancia_ciclo = calcular_delta_dist(datosAntiguos.latitude, datosAntiguos.longitude, new_latitude, new_longitude);
+        distancia_total += distancia_ciclo;
+        float new_speed = distancia_ciclo / MEASUREMENT_INTERVAL_S * 3.6;
+        
+
+        if (xSemaphoreTake(dataMutex, portMAX_DELAY) == pdTRUE) {
+            latestData.latitude = new_latitude;
+            latestData.longitude = new_longitude;
+            latestData.altura = new_altitude;
+            strncpy(latestData.tiempo, buffer, sizeof(latestData.tiempo) - 1);
+            latestData.tiempo[sizeof(latestData.tiempo) - 1] = '\0';
+            latestData.velocidad = new_speed;
+            latestData.temperature = new_temp;
+            latestData.humidity = new_hum;
+            latestData.pressure = new_press;
+            datosAntiguos = latestData;
+            xSemaphoreGive(dataMutex);
+        }
+
+        FILE *f = fopen(filename, "a");
+        if (f) {
+            //Crear la string para escribir en el archivo
+            fprintf(f, "\t\t\t<trkpt lat=\"");
+            fprintf(f, "%2.4f", datosAntiguos.latitude);
+            fprintf(f, "\" lon=\"");
+            fprintf(f, "%2.4f", datosAntiguos.longitude);
+            fprintf(f, "\">\n");
+
+            fprintf(f, "\t\t\t\t<ele>");
+            fprintf(f, "%f", datosAntiguos.altura);
+            fprintf(f, "</ele>\n");
+
+            fprintf(f, "\t\t\t\t<time>");
+            fprintf(f, "%s", datosAntiguos.tiempo);
+            fprintf(f, "</time>\n");
+
+            fprintf(f, "\t\t\t\t<speed>");
+            fprintf(f, "%f", datosAntiguos.velocidad);
+            fprintf(f, "</speed>\n");
+
+            fprintf(f, "\t\t\t\t<extensions>\n");
+            fprintf(f, "\t\t\t\t\t<gpxtpx:TrackPointExtension>\n");
+            fprintf(f, "\t\t\t\t\t\t<gpxtpx:atemp>");
+            fprintf(f, "%f", datosAntiguos.temperature);
+            fprintf(f, "</gpxtpx:atemp>\n");
+            fprintf(f, "\t\t\t\t\t</gpxtpx:TrackPointExtension>\n");
+
+            fprintf(f, "\t\t\t\t\t<custom:humidity>");
+            fprintf(f, "%f", datosAntiguos.humidity);
+            fprintf(f, "</custom:humidity>\n");
+
+            fprintf(f, "\t\t\t\t\t<custom:pressure>");
+            fprintf(f, "%f", datosAntiguos.pressure);
+            fprintf(f, "</custom:pressure>\n");
+
+            fprintf(f, "\t\t\t\t</extensions>\n");
+            fprintf(f, "\t\t\t</trkpt>\n");
+            
+            fclose(f);
+        }
+        unsigned long elapsedMillis = pdTICKS_TO_MS(xTaskGetTickCount()) - startMillis;
+        ESP_LOGI(GEN_TAG, "Elapsed millis: %d.", elapsedMillis);
+
+        vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(MEASUREMENT_INTERVAL_S*1000)); // Espera x*1000 milisegundos
+        
+    }
+}
+
+void crear_archivo(){
+    int num = 1;
+
+    sprintf(filename, MOUNT_POINT"/data%03d.gpx", num);
+
+    FILE *f = fopen(filename, "r");
+
+    while (f != NULL) {
+        num++;
+        sprintf(filename, MOUNT_POINT"/data%03d.gpx", num);
+        FILE *f = fopen(filename, "r");
+    }
+
+    FILE *f = fopen(filename, "w");
+
+    char buffer[50];
+
+    if (f) {
+        fprintf(f, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+                    "<gpx creator=\"ESP32 GPS LOGGER\" version=\"1.1\"\n"
+                    "\txmlns=\"http://www.topografix.com/GPX/1/1\"\n"
+                    "\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
+                    "\txmlns:gpxtpx=\"http://www.garmin.com/xmlschemas/TrackPointExtension/v2\"\n"
+                    "\txmlns:gpxdata=\"http://www.cluetrust.com/XML/GPXDATA/1/0\"\n"
+                    "\txsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\">\n"
+                    "\t<metadata>\n"
+                    "\t\t<name>Ruta grabada con ESP32 GPS Logger</name>\n"
+                    "\t\t<time>\n");
+        int new_fecha_len = snprintf(buffer, sizeof(buffer), "%d-%02d-%02dT%02d:%02d:%02d.%03d", gps_date_year(&gps.date), gps_date_month(&gps.date),
+                                    gps_date_day(&gps.date), gps_time_hour(&gps.time), gps_time_minute(&gps.time), 
+                                    gps_time_second(&gps.time), gps_time_centisecond(&gps.time));
+        if (new_fecha_len < 0) {
+            buffer[0] = '\0';
+        }
+        fprintf(f, "%s", buffer);
+        fprintf(f, "</time>\n\t</metadata>\n"
+                    "\t<trk>\n"
+                    "\t\t<name>Rutita</name>\n"
+                    "\t\t<type>hiking</type>\n"
+                    "\t\t<trkseg>\n");
+        fclose(f);
+    } else {
+        OLED_print("Error","creando archivo");
+        vTaskDelay(pdMS_TO_TICKS(2000));
+        crear_archivo();
+    }
+}
+
+void cerrar_archivo() {
+    FILE *f = fopen(filename, "w");
+    if (f){
+        fprintf(f, "\t\t</trkseg>\n\t</trk>\n</gpx>");
+        fclose(f);
+    }
+    //int num = 1;
+    //sprintf(filename, "/data%03d.gpx", num);
+    //while (SD.exists(filename)) {
+    //  num++;
+    //  sprintf(filename, "/data%03d.gpx", num);
+    //}
+}
+
+void activarWiFi(void)
+{
+    OLED_print("WiFi", "Activando...");
+
+    ESP_ERROR_CHECK(wifi_ap_init());
+
+    char ip_str[16] = {0};
+    get_ap_ip(ip_str, sizeof(ip_str));
+    OLED_print("WiFi Activo", ip_str);
+
+    vTaskDelay(pdMS_TO_TICKS(2000));
+
+    ESP_ERROR_CHECK(iniciar_servidor_http());
+
+    wifiActivado = true;
+    wifiLastActivity = xTaskGetTickCount();
+}
+
+void desactivarWiFi(void)
+{
+    if (http_server) {
+        httpd_stop(http_server);
+        http_server = NULL;
+    }
+
+    ESP_ERROR_CHECK(esp_wifi_stop());
+    ESP_ERROR_CHECK(esp_wifi_deinit());
+
+    if (ap_netif) {
+        esp_netif_destroy(ap_netif);
+        ap_netif = NULL;
+    }
+
+    wifiActivado = false;
+    OLED_print("WiFi", "Apagado");
+}
+
 void app_main(void)
 void app_main(void)
 {
 {
     // Initialize DHT22 sensor
     // Initialize DHT22 sensor
@@ -237,6 +541,8 @@ void app_main(void)
         return;
         return;
     }
     }
 
 
+    gpio_set_pull_mode(PIN_NUM_MISO, GPIO_PULLUP_ONLY);
+
     sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
     sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
     slot_config.gpio_cs = PIN_NUM_CS;
     slot_config.gpio_cs = PIN_NUM_CS;
     slot_config.host_id = host.slot;
     slot_config.host_id = host.slot;
@@ -278,7 +584,6 @@ void app_main(void)
     spi_bus_free(host.slot);
     spi_bus_free(host.slot);
     */
     */
 
 
-
     // Initialize display
     // Initialize display
     /*
     /*
     i2c_master_bus_handle_t i2c_bus =
     i2c_master_bus_handle_t i2c_bus =
@@ -346,7 +651,7 @@ void uart_configurator() {
     // Install UART driver using an event queue here
     // Install UART driver using an event queue here
     ESP_ERROR_CHECK(uart_driver_install(UART_NUM_2, uart_buffer_size, uart_buffer_size, 10, &uart_queue, 0));
     ESP_ERROR_CHECK(uart_driver_install(UART_NUM_2, uart_buffer_size, uart_buffer_size, 10, &uart_queue, 0));
     ESP_ERROR_CHECK(uart_param_config(UART_NUM_2, &uart_config));
     ESP_ERROR_CHECK(uart_param_config(UART_NUM_2, &uart_config));
-    ESP_ERROR_CHECK(uart_set_pin(UART_NUM_2, GPIO_NUM_17, GPIO_NUM_16, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
+    ESP_ERROR_CHECK(uart_set_pin(UART_NUM_2, PIN_TX, PIN_RX, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
 }
 }
 
 
 static esp_err_t s_example_write_file(const char *path, char *data)
 static esp_err_t s_example_write_file(const char *path, char *data)
@@ -380,3 +685,128 @@ static i2c_master_bus_handle_t i2c_bus0_init(gpio_num_t sda, gpio_num_t scl, uin
     return bus;
     return bus;
 }
 }
 
 
+static esp_err_t wifi_ap_init(void)
+{
+    esp_err_t err;
+
+    ESP_ERROR_CHECK(esp_netif_init());
+    ESP_ERROR_CHECK(esp_event_loop_create_default());
+
+    ap_netif = esp_netif_create_default_wifi_ap();
+    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
+    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
+    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
+
+    wifi_config_t wifi_config = {
+        .ap = {
+            .ssid = "",
+            .ssid_len = 0,
+            .channel = 1,
+            .password = "",
+            .max_connection = 4,
+            .authmode = WIFI_AUTH_WPA_WPA2_PSK,
+        },
+    };
+
+    strncpy((char *)wifi_config.ap.ssid, apSSID, sizeof(wifi_config.ap.ssid) - 1);
+    strncpy((char *)wifi_config.ap.password, apPassword, sizeof(wifi_config.ap.password) - 1);
+
+    if (strlen(apPassword) == 0) {
+        wifi_config.ap.authmode = WIFI_AUTH_OPEN;
+    }
+
+    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config));
+    ESP_ERROR_CHECK(esp_wifi_start());
+    return ESP_OK;
+}
+
+static esp_err_t get_ap_ip(char *out_ip, size_t len)
+{
+    esp_netif_ip_info_t ip_info;
+    if (ap_netif == NULL) {
+        return ESP_ERR_INVALID_STATE;
+    }
+    ESP_ERROR_CHECK(esp_netif_get_ip_info(ap_netif, &ip_info));
+    inet_ntoa_r(ip_info.ip, out_ip, len);
+    return ESP_OK;
+}
+
+static esp_err_t root_get_handler(httpd_req_t *req)
+{
+    wifiLastActivity = xTaskGetTickCount();
+
+    char ip_str[16] = {0};
+    get_ap_ip(ip_str, sizeof(ip_str));
+
+    const char *nombreArchivo = (filename[0] == '/') ? filename + 1 : filename;
+
+    char html[1024];
+    snprintf(html, sizeof(html),
+        "<!DOCTYPE html><html><head><meta charset='UTF-8'><title>ESP32 GPS Logger</title>"
+        "<style>body{font-family:Arial;text-align:center;margin:50px;}h1{color:#333;} "
+        "a.button{background:#4CAF50;color:white;padding:20px 40px;text-decoration:none;"
+        "font-size:24px;border-radius:12px;display:inline-block;margin:20px;}</style></head>"
+        "<body><h1>GPS Logger</h1><p>Archivo listo para descargar:</p>"
+        "<a href='/download' class='button' download='%s'>Descargar %s</a>"
+        "<hr><p>IP: %s</p><p>Se apagará en 5 min sin uso.</p></body></html>",
+        nombreArchivo, nombreArchivo, ip_str);
+
+    httpd_resp_set_type(req, "text/html");
+    return httpd_resp_send(req, html, HTTPD_RESP_USE_STRLEN);
+}
+
+static esp_err_t download_get_handler(httpd_req_t *req)
+{
+    wifiLastActivity = xTaskGetTickCount();
+
+    struct stat st;
+    if (stat(filename, &st) != 0) {
+        httpd_resp_send_404(req);
+        return ESP_FAIL;
+    }
+
+    FILE *file = fopen(filename, "r");
+    if (!file) {
+        httpd_resp_send_404(req);
+        return ESP_FAIL;
+    }
+
+    const char *nombreArchivo = (filename[0] == '/') ? filename + 1 : filename;
+    char disposition[128];
+    snprintf(disposition, sizeof(disposition),
+             "attachment; filename=\"%s\"", nombreArchivo);
+
+    httpd_resp_set_type(req, "application/gpx+xml");
+    httpd_resp_set_hdr(req, "Content-Disposition", disposition);
+    httpd_resp_send_file(req, file, st.st_size);
+
+    fclose(file);
+    return ESP_OK;
+}
+
+static const httpd_uri_t root_uri = {
+    .uri       = "/",
+    .method    = HTTP_GET,
+    .handler   = root_get_handler,
+    .user_ctx  = NULL
+};
+
+static const httpd_uri_t download_uri = {
+    .uri       = "/download",
+    .method    = HTTP_GET,
+    .handler   = download_get_handler,
+    .user_ctx  = NULL
+};
+
+static esp_err_t iniciar_servidor_http(void)
+{
+    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
+    if (httpd_start(&http_server, &config) != ESP_OK) {
+        return ESP_FAIL;
+    }
+
+    httpd_register_uri_handler(http_server, &root_uri);
+    httpd_register_uri_handler(http_server, &download_uri);
+    return ESP_OK;
+}
+