| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914 |
- #include <stdio.h>
- #include "freertos/FreeRTOS.h"
- #include "freertos/task.h"
- #include "driver/gpio.h"
- #include "esp_rom_sys.h"
- #include "esp_log.h"
- #include "dht22.h"
- #include "GPS_parser.h"
- #include "driver/uart.h"
- #include <string.h>
- #include <sys/unistd.h>
- #include <sys/stat.h>
- #include "esp_vfs_fat.h"
- #include "sdmmc_cmd.h"
- #include "ssd1306.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"
- #include "driver/gpio.h"
- #include "esp_attr.h"
- #include "esp_pm.h"
- const char *DHT_TAG = "DHT22";
- const char *GPS_TAG = "GPS_PARSER";
- const char *SD_TAG = "SD_CARD";
- const char *OLED_TAG = "SSD1306OLED";
- const char *GEN_TAG = "General";
- #define MOUNT_POINT "/sdcard"
- #define PIN_NUM_MISO CONFIG_PIN_MISO
- #define PIN_NUM_MOSI CONFIG_PIN_MOSI
- #define PIN_NUM_CLK CONFIG_PIN_CLK
- #define PIN_NUM_CS CONFIG_PIN_CS
- #define PIN_NUM_SDA CONFIG_PIN_SDA
- #define PIN_NUM_SCL CONFIG_PIN_SCL
- #define PIN_TX GPIO_NUM_17
- #define PIN_RX GPIO_NUM_16
- #define PIN_BUTTON CONFIG_PIN_BUTTON
- //definicion de tiempos de pulsacion
- #define PULSACION_LARGA_MS 2000
- #define DURACION_WATCHDOG_MS 10000
- #define WIFI_TIMEOUT_MS 3000000
- #define MEASUREMENT_INTERVAL_S 1
- #define DEG2RAD (M_PI/180.0)
- const int uart_buffer_size = (1024 * 2);
- int length = 0;
- uint8_t data;
- static gps_parser_t gps;
- 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[64]; // tu archivo gpx
- const char *apSSID = "MiAP";
- const char *apPassword = "password123";
- struct SensorData {
- double latitude;
- double longitude;
- float altura;
- char tiempo[64];
- float velocidad;
- float temperature;
- float humidity;
- float pressure;
- };
- struct SensorData latestData;
- struct SensorData datosAntiguos;
- SemaphoreHandle_t dataMutex; // Mutex para proteger el acceso a latestData
- SemaphoreHandle_t buttonSemaphore; // Semáforo para la tarea del botón
- bool grabando = false; //inicia apagado
- bool finalizado = true; //indica que no hay ninguna grabacion ni iniciada ni pausada
- TaskHandle_t medicionesHandle = NULL; //para suspend/resume
- int pantallaEstado_grab = -1; //maquina de estados cuando se graba ruta
- int pantallaEstado_menu = -1; //maquina de estados cuando no se esta grabando ruta
- float distancia_total = 0.0;
- volatile unsigned long ignore_isr_until = 0; //para debounce
- char filename[64] = "/panchas.gpx";
- ssd1306_handle_t d = NULL;
- i2c_master_dev_handle_t i2c_dev_handle = NULL;
- const char mount_point[] = MOUNT_POINT;
- // Configuración del GPIO del botón
- gpio_config_t btn_cfg = {
- .pin_bit_mask = (1ULL << PIN_BUTTON),
- .mode = GPIO_MODE_INPUT,
- .pull_up_en = GPIO_PULLUP_ENABLE,
- .pull_down_en = GPIO_PULLDOWN_DISABLE,
- .intr_type = GPIO_INTR_NEGEDGE, // FALLING = flanco de bajada
- };
- void uart_configurator() {
- // Configure UART parameters
- uart_config_t uart_config = {
- .baud_rate = 115200,
- .data_bits = UART_DATA_8_BITS,
- .parity = UART_PARITY_DISABLE,
- .stop_bits = UART_STOP_BITS_1,
- .flow_ctrl = UART_HW_FLOWCTRL_DISABLE
- };
- QueueHandle_t uart_queue;
- // 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_param_config(UART_NUM_2, &uart_config));
- ESP_ERROR_CHECK(uart_set_pin(UART_NUM_2, PIN_TX, PIN_RX, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
- }
- // Init i2c
- static i2c_master_bus_handle_t i2c_bus0_init(gpio_num_t sda, gpio_num_t scl, uint32_t hz) {
- i2c_master_bus_config_t bus_cfg = {
- .i2c_port = I2C_NUM_0,
- .sda_io_num = sda,
- .scl_io_num = scl,
- .clk_source = I2C_CLK_SRC_DEFAULT,
- .glitch_ignore_cnt = 0,
- .flags.enable_internal_pullup = true,
- };
- i2c_master_bus_handle_t bus = NULL;
- ESP_ERROR_CHECK(i2c_new_master_bus(&bus_cfg, &bus));
- // NOTE: per-device speed is set when adding the device (in driver bind).
- return bus;
- }
- static esp_err_t wifi_ap_init(void)
- {
- 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 stream_file_response(httpd_req_t *req, FILE *file)
- {
- char buf[1024];
- size_t read_len;
- esp_err_t ret;
- while ((read_len = fread(buf, 1, sizeof(buf), file)) > 0) {
- ret = httpd_resp_send_chunk(req, buf, read_len);
- if (ret != ESP_OK) {
- fclose(file);
- return ret;
- }
- }
- fclose(file);
- return httpd_resp_send_chunk(req, NULL, 0);
- }
- 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);
-
- return stream_file_response(req, file);
- }
- 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;
- }
- void OLED_test(){
- i2c_master_bus_handle_t i2c_bus = i2c_bus0_init(PIN_NUM_SDA, PIN_NUM_SCL, 400000);
- ssd1306_config_t cfg = {
- .width = 128,
- .height = 64,
- .fb = NULL, // let driver allocate internally
- .fb_len = 0,
- .iface.i2c =
- {
- .port = I2C_NUM_0,
- .addr = 0x3C, // typical SSD1306 I2C address
- .rst_gpio = GPIO_NUM_NC, // no reset pin
- },
- };
- i2c_device_config_t dev_cfg = {
- .dev_addr_length = I2C_ADDR_BIT_LEN_7,
- .device_address = 0x3C,
- .scl_speed_hz = 400000,
- };
- i2c_master_bus_add_device(i2c_bus, &dev_cfg, &i2c_dev_handle);
- ESP_ERROR_CHECK(ssd1306_new_i2c(&cfg, &d));
- ESP_ERROR_CHECK(ssd1306_clear(d));
- ESP_ERROR_CHECK(ssd1306_draw_rect(d, cfg.width / 2 - 50, cfg.height / 2 - 10, 100, 20, false));
-
- ESP_ERROR_CHECK(ssd1306_display(d));
- ESP_ERROR_CHECK(ssd1306_draw_circle(d, cfg.width / 2, cfg.height / 2, 20, true));
- ESP_ERROR_CHECK(ssd1306_display(d));
-
- ESP_ERROR_CHECK(ssd1306_draw_text_scaled(d, 0, 0, "Iniciando...", true, 1));
- ESP_ERROR_CHECK(ssd1306_display(d));
- vTaskDelay(pdMS_TO_TICKS(1000));
- }
- void OLED_print(const char *line1, const char *line2){
-
- ESP_ERROR_CHECK(ssd1306_clear(d));
- ESP_ERROR_CHECK(ssd1306_draw_text_scaled(d, 0, 0, line1, true, 2));
- ESP_ERROR_CHECK(ssd1306_draw_text_scaled(d, 0, 40, line2, true, 2));
- ESP_ERROR_CHECK(ssd1306_display(d));
- }
- void DHT_test (){
- float t;
- float h;
- esp_err_t err = dht_attach_pin();
- if (err != ESP_OK) {
- OLED_print("DHT22", "Error");
- ESP_LOGE(DHT_TAG, "Failed to attach DHT pin: %s", esp_err_to_name(err));
- vTaskDelay(pdMS_TO_TICKS(5000)); // Wait for 5 seconds before retrying
- DHT_test(); //Reintentar
- }
-
- err = dht_read(&t, &h);
- if (err != ESP_OK) {
- OLED_print("DHT22", "Error");
- ESP_LOGE(DHT_TAG, "Failed to read DHT : %s", esp_err_to_name(err));
- vTaskDelay(pdMS_TO_TICKS(5000)); // Wait for 5 seconds before retrying
- DHT_test(); //Reintentar
- }
- else OLED_print("DHT22", "Correcto");
- }
- 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;
- char nombre[32];
- snprintf(nombre, sizeof(nombre), "data%03d.gpx", num);
- snprintf(filename, sizeof(filename), "%s/%s", MOUNT_POINT, nombre);
- FILE *f = fopen(filename, "r");
- while (f != NULL) {
- fclose(f);
- num++;
- snprintf(nombre, sizeof(nombre), "data%03d.gpx", num);
- snprintf(filename, sizeof(filename), "%s/%s", MOUNT_POINT, nombre);
- f = fopen(filename, "r");
- }
- fclose(f);
- 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 IRAM_ATTR isr_button(void *arg) {
- unsigned long now = pdTICKS_TO_MS(xTaskGetTickCount());
- if (now < ignore_isr_until) {
- return; // Ignorar interrupción si está dentro del período de debounce
- }
- static unsigned long lastInterrupt = 0;
-
- if ((now - lastInterrupt) > 300 ){ // debounce de 300 ms
- BaseType_t xHigherPriorityTaskWoken = pdFALSE;
- xSemaphoreGiveFromISR(buttonSemaphore, &xHigherPriorityTaskWoken);
- lastInterrupt = now;
- if (xHigherPriorityTaskWoken) {
- portYIELD_FROM_ISR();
- }
- }
- }
- void drawProgressBar(int x, int y, int w, int h, unsigned long progress, unsigned long total) {
- int filledWidth = (progress * w) / total;
- ESP_ERROR_CHECK(ssd1306_draw_rect(d, x, y, filledWidth, h, true)); //dibuja un trozo de rectangulo relleno
- ESP_ERROR_CHECK(ssd1306_draw_rect(d, x + filledWidth, y, w - filledWidth, h, false)); //dibuja el resto del rectangulo vacio
- ESP_ERROR_CHECK(ssd1306_display(d));
- }
- void task_ui(void *pvParameters){
- unsigned long pressTime = 0;
- unsigned long lastActivity = pdTICKS_TO_MS(xTaskGetTickCount());
- bool pantallaOn = true; //comprobar el estado inicial, no se cual sera
- bool processingButton = false;
- while(1){
- if (xSemaphoreTake(buttonSemaphore, pdMS_TO_TICKS(200)) == pdTRUE){ //button pressed
- if (processingButton) continue; //evita reentradas
- processingButton = true;
- pressTime = pdTICKS_TO_MS(xTaskGetTickCount());
- lastActivity = pdTICKS_TO_MS(xTaskGetTickCount()); //reset watchdog
- if (!pantallaOn){
- uint8_t cmd_on[] = {0x00, 0xAF};
- i2c_master_transmit(i2c_dev_handle, cmd_on, sizeof(cmd_on), pdMS_TO_TICKS(100));
- pantallaOn = true;
- }
- bool timed_out = false;
- unsigned long checkTime = pdTICKS_TO_MS(xTaskGetTickCount());
- while (gpio_get_level(PIN_BUTTON) == 0){
- vTaskDelay(pdMS_TO_TICKS(10));
- checkTime = pdTICKS_TO_MS(xTaskGetTickCount());
- if ((checkTime - pressTime) > DURACION_WATCHDOG_MS){ //10s timeout para evitar bloqueos
- timed_out = true;
- break;
- }
- drawProgressBar(0, 64 - 10, 128, 8, checkTime - pressTime, PULSACION_LARGA_MS);
- }
- ignore_isr_until = pdTICKS_TO_MS(xTaskGetTickCount()) + 500; //ignorar nuevas interrupciones durante 500 ms
- unsigned long duration = checkTime - pressTime;
- if (timed_out){
- OLED_print("Apagando","pantalla");
- uint8_t cmd_off[] = {0x00, 0xAE};
- i2c_master_transmit(i2c_dev_handle, cmd_off, sizeof(cmd_off), pdMS_TO_TICKS(100));
- pantallaOn = false;
- } else {
- if (grabando){
- if (duration >= PULSACION_LARGA_MS){
- grabando = false;
- vTaskSuspend(medicionesHandle);
- OLED_print("Ruta","pausada");
- } else {
- pantallaEstado_grab = (pantallaEstado_grab + 1) % 5; //cicla entre 0-4
- struct SensorData currentData;
- if(xSemaphoreTake(dataMutex, portMAX_DELAY) == pdTRUE){
- currentData = latestData;
- xSemaphoreGive(dataMutex);
- }
- switch (pantallaEstado_grab){
- char line2[32];
- case 0:
- snprintf(line2, sizeof(line2), "%.4f,%.4f", currentData.longitude, currentData.latitude);
- OLED_print("Posicion", line2);
- break;
- case 1:
- snprintf(line2, sizeof(line2), "%.1fkm", distancia_total);
- OLED_print("Distancia", line2);
- break;
- case 2:
- snprintf(line2, sizeof(line2), "%.1fm", currentData.altura);
- OLED_print("Altitud", line2);
- break;
- case 3:
- snprintf(line2, sizeof(line2), "%.1fC/%.1f%%", currentData.temperature, currentData.humidity);
- OLED_print("Temp/Hum", line2);
- break;
- case 4:
- snprintf(line2, sizeof(line2), "%.1fkm/h", currentData.velocidad);
- OLED_print("Velocidad", line2);
- break;
- }
- }
- } else {
- if (duration >= PULSACION_LARGA_MS){
- switch (pantallaEstado_menu){
- case 0:
- //activar la ruta y crear el archivo
- crear_archivo();
- vTaskResume(medicionesHandle);
- OLED_print("Ruta","iniciada");
- finalizado = false;
- grabando = true;
- break;
- case 1:
- //cerrar el archivo y cambiar el valor de 'filename'
- cerrar_archivo();
- finalizado = true;
- break;
- case 2:
- //implementacion wifi
- if(!wifiActivado){
- activarWiFi();
- } else {
- desactivarWiFi();
- }
- break;
- }
- } else {
- pantallaEstado_menu = (pantallaEstado_menu + 1) % 3;
- int previous_state = -1;
- while (pantallaEstado_menu != previous_state) {
- previous_state = pantallaEstado_menu;
- switch (pantallaEstado_menu) {
- case 0:
- if (!finalizado) OLED_print("Reanudar","ruta");
- else OLED_print("Iniciar","ruta");
- break;
- case 1:
- FILE *f = fopen(filename, "r");
- if (f && !finalizado) {
- OLED_print("Finalizar","ruta");
- break;
- } else {
- pantallaEstado_menu = (pantallaEstado_menu + 1) % 3;
- }
- break;
- case 2:
- if (finalizado) {
- OLED_print("Conexion","WiFi");
- break;
- } else {
- pantallaEstado_menu = (pantallaEstado_menu + 1) % 3;
- }
- break;
- }
- }
- }
- }
- }
- lastActivity = pdTICKS_TO_MS(xTaskGetTickCount()); //reset watchdog
- processingButton = false;
- vTaskDelay(pdMS_TO_TICKS(100)); //pequeño delay para no busy waiting
- }
- //check watchdog fuera del boton
- if (pantallaOn && ((pdTICKS_TO_MS(xTaskGetTickCount()) - lastActivity) > DURACION_WATCHDOG_MS)){
- uint8_t cmd_off[] = {0x00, 0xAE};
- i2c_master_transmit(i2c_dev_handle, cmd_off, sizeof(cmd_off), pdMS_TO_TICKS(100));
- pantallaOn = false;
- }
- //check wifi timeout
- if (wifiActivado) {
- // server.handleClient() → NO necesario, httpd corre en su propio task
- // WiFi.softAPgetStationNum() → esp_wifi_ap_get_sta_list()
- wifi_sta_list_t sta_list;
- if (esp_wifi_ap_get_sta_list(&sta_list) == ESP_OK && sta_list.num > 0) {
- wifiLastActivity = pdTICKS_TO_MS(xTaskGetTickCount());
- }
- if ((pdTICKS_TO_MS(xTaskGetTickCount()) - wifiLastActivity) > WIFI_TIMEOUT_MS) {
- desactivarWiFi();
- }
- }
- }
- }
- void app_main(void) {
- gpio_config(&btn_cfg);
- // Instalar el servicio de interrupciones y adjuntar la ISR
- gpio_install_isr_service(0);
- gpio_isr_handler_add(PIN_BUTTON, isr_button, NULL);
-
- buttonSemaphore = xSemaphoreCreateBinary();
- dataMutex = xSemaphoreCreateMutex();
- // OLED check
- OLED_test();
- vTaskDelay(pdMS_TO_TICKS(1000));
- // DHT check
- DHT_test();
- vTaskDelay(pdMS_TO_TICKS(1000));
- // SD Card check
- SD_test();
- vTaskDelay(pdMS_TO_TICKS(1000));
- // GPS check
- GPS_test_wait();
- vTaskDelay(pdMS_TO_TICKS(2000));
- // Crear tarea para mediciones
- xTaskCreatePinnedToCore(
- task_mediciones, // Función de la tarea
- "Mediciones", // Nombre de la tarea
- 8192, // Tamaño del stack
- NULL, // Parámetro de la tarea
- 10, // Prioridad de la tarea
- &medicionesHandle, // Handle de la tarea
- 0 // Núcleo donde se ejecuta
- );
- xTaskCreatePinnedToCore(
- task_ui, // Función de la tarea
- "UI", // Nombre de la tarea
- 8192, // Tamaño del stack
- NULL, // Parámetro de la tarea
- 5, // Prioridad de la tarea
- NULL, // Handle de la tarea
- 1 // Núcleo donde se ejecuta
- );
- esp_pm_config_t pm_config = {
- .max_freq_mhz = 240,
- .min_freq_mhz = 80,
- .light_sleep_enable = true
- };
- esp_err_t err = esp_pm_configure(&pm_config);
- if (err != ESP_OK) {
- ESP_LOGE("General", "Error configuring power management");
- }
- ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE)); // Desactiva ahorro de energía WiFi
- vTaskSuspend(medicionesHandle); //inicia suspendida
- }
|