#include "TinyGPSPlus.h" #include "DHT.h" #include #include #include #include "FS.h" #include "SD.h" #include "SPI.h" #include "Arduino.h" /* Uncomment and set up if you want to use custom pins for the SPI communication #define REASSIGN_PINS int sck = -1; int miso = -1; int mosi = -1; int cs = -1; */ #define SCREEN_WIDTH 128 // Ancho píxeles #define SCREEN_HEIGHT 64 // Alto píxeles #define OLED_RESET -1 // Reset pin (no usado) // Pines para UART2 (Serial2) #define RX_PIN 16 // RX del ESP32 conectado a TX del GPS #define TX_PIN 17 // TX del ESP32 conectado a RX del GPS #define GPS_BAUD 115200 #define DHTPIN 4 // Digital pin connected to the DHT sensor #define DHTTYPE DHT22 // Objeto TinyGPS++ TinyGPSPlus gps; // Serial para GPS HardwareSerial gpsSerial(2); // Configuracion del DHT DHT dht(DHTPIN, DHTTYPE); //definición del objeto para la pantalla OLED Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // pin para pulsador #define BUTTON_PIN 0 //valores de los sensores struct SensorData { double latitude; double longitude; float temperature; float humidity; float pressure; }; SensorData latestData; SemaphoreHandle_t dataMutex; // Mutex para proteger el acceso a latestData SemaphoreHandle_t buttonSemaphore; // Semáforo para la tarea del botón //flag para reducir las lecturas de DHT y presion uint8_t readSensorsFlag = 0; //definicion de tiempos de pulsacion #define PULASCION_LARGA_MS 2000 #define DURACION_WATCHDOG_MS 10000 bool grabando = 0; //inicia apagado TaskHandle_t medicionesHandle = NULL; //para suspend/resume #define X 1 //separación entre mediciones (s) int pantallaEstado = 0; //maquina de estados float distancia_total = 0; void DHT_test() { dht.begin(); float h = dht.readHumidity(); float t = dht.readTemperature(); if (isnan(h) || isnan(t)) { Serial.println("Failed to read from DHT sensor!"); OLED_print("DHT22", "Error"); while(1); // Detener si hay error } else OLED_print("DHT22", "Correcto"); } void OLED_test() { //pantallazo a blanco y luego iniciando // Inicia I2C en pines default ESP32 (21 SDA, 22 SCL) if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Dirección común: 0x3C Serial.println(F("Error: OLED no encontrado!")); for (;;); // Para siempre } display.clearDisplay(); display.fillScreen(SSD1306_WHITE); // Pantalla blanca delay(500); display.display(); display.clearDisplay(); display.setTextSize(2); // Tamaño texto display.setTextColor(SSD1306_WHITE); display.setCursor(0, 0); // Posición display.println("Iniciando..."); display.display(); // Muestra delay(1000); } void OLED_print(const char* line1, const char* line2) { display.clearDisplay(); display.setTextSize(2); display.setTextColor(SSD1306_WHITE); display.setCursor(0, 0); display.println(line1); display.println(line2); display.display(); } char* SD_test() { if (!SD.begin()) { Serial.println("SD Card Mount Failed"); OLED_print("SD Card", "Error\nInserte tarjeta"); while(!SD.begin()); // Detener si hay error OLED_print("SD Card", "Insertada"); } else OLED_print("SD Card", "Correcto"); char filename[] = "data001.txt"; int num = 1; // Buscar un nombre de archivo disponible while(SD.exists(filename)) { num++; sprintf(filename, "data%03d.txt", num); } File file = SD.open(filename, FILE_WRITE); file.Println("Latitud,Longitud,Temperatura,Humedad,Presión") file.close(); return filename; } void GPS_test_wait() { // Iniciar Serial2 para GPS gpsSerial.begin(GPS_BAUD, SERIAL_8N1, RX_PIN, TX_PIN); while ((gpsSerial.available() > 0) && !gps.location.isValid()) { gps.encode(gpsSerial.read()); delay(10); OLED_print("GPS", "Esperando."); delay(100); OLED_print("GPS", "Esperando.."); delay(100); OLED_print("GPS", "Esperando..."); } OLED_print("GPS", "Encontrado"); } float calcular_delta_dist(float lat1, float long1, float lat2, float long2){ float R = 6371; float delta_lat = lat2 - lat1; float delta_long = long2 - long1; float a = sin(delta_lat/2)ˆ2+cos(lat1)*cos(lat2)*sin(delta_long/2)ˆ2; float c = 2 * atan2(sqrt(a),sqrt(1-a)); return float d = R * c; } void task_mediciones(void *pvParameters) { TickType_t xLastWakeTime = xTaskGetTickCount(); const char* filename = (const char*) pvParameters; SensorData datosAntiguos; while(1) { // se leen los valores antes de utilizar el semaphore while (gpsSerial.available() > 0) { gps.encode(gpsSerial.read()); } float new_latitude = gps.location.lat(); float new_longitude = gps.location.lng(); if ((readSensorsFlag % 60) == 0) float new_temp = dht.readTemperature(); if ((readSensorsFlag % 120) == 0) float new_hum = ddht.readHumidity(); float new_press = 0.0; // Placeholder, no hay sensor de presión File file = SD.open(filename, FILE_WRITE); if (file) { // guardar datos en latestData if (xSemaphoreTake(dataMutex, portMAX_DELAY) == pdTRUE){ latestData.latitude = new_latitude; latestData.longitude = new_longitude; latestData.temperature = new_temp; latestData.humidity = new_hum; latestData.pressure = new_press; } distancia_total += calcular_delta_dist(new_latitude, new_longitude, datosAntiguos.latitude, datosAntiguos.longitude); datosAntiguos = latestData; xSemaphoreGive(dataMutex); //Crear la string para escribir en el archivo char frase = String(datosAntiguos.latitude) + "," + String(datosAntiguos.longitude) + "," + String(datosAntiguos.temperature) + "," + String(datosAntiguos.humidity) + "," + String(datosAntiguos.pressure); // Escribir datos en el archivo file.println(frase); file.close(); if (!(readSensorsFlag % 120)) readSensorsFlag = 0; readSensorsFlag++; } vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(X*1000)); // Espera x*1000 milisegundos } } void IRAM_ATTR isr_button() { static unsigned long lastInterrupt = 0; if ((millis() - lastInterrupt) > 200 ){ // debounce de 200 ms xSemaphoreGiveFromISR(buttonSemaphore, NULL); lastInterrupt = mmillis(); } } void task_ui(void *pvParameters){ unsigned long pressTime = 0; unsigned long lastActivity = 0; bool pantallaOn = true; //comprobar el estado inicial, no se cual sera while(1){ if (xSemaphoreTake(buttonSemaphore, portMAX_DELAY) == pdTRUE){ //button pressed pressTime = millis(); lastActivity = millis(); if (!pantallaOn){ display.begin(SSD1306_SWITCHCAPVCC, 0x3C); pantallaOn = true; } while (digitalRead(BUTTON_PIN) == LOW){ vTaskDelay(pdMS_TO_TICKS(10)); if ((millis() - lastActivity) > DURACION_WATCHDOG_MS){ //WATCHDOG 'activo' solo funciona si se mantiene presionado el boton ese tiempo break; } } unsigned long duration = millis() - pressTime; if (duration >= PULASCION_LARGA_MS){ //pulsacion larga: cabia entre grabacion y no grabacion de datos grabando != grabando; if (grabando){ vTaskResume(medicionesHandle); //Mostrar que empieza la grabación OLED_print("Ruta","iniciada"); } else { vTaskSuspend(medicionesHandle); //Mostrar que se pausa/finaliza la grabación OLED_print("Ruta","pausada"); } } else { //Pulsacion corta + grabando datos, cicla datos if (grabando) { pantallaEstado = (pantallaEstado + 1) % 3; //cicla entre 0-2 SensorData currentData; if(xSemaphoreTake(dataMutex, portMAX_DELAY) == pdTRUE){ currentData = latestData; xSemaphoreGive(dataMutex); } switch (pantallaEstado){ case 0: OLED_print("Posicion",String(currentData.longitude)+","+String(currentData.latitude)); break; case 1: OLED_print("Distancia",String(distancia_total)+"km"); break; case 2: OLED_print("Altitud",String(currentData.pressure)+"m"); break; } else { OLED_print("Grabación","pausada"); } } lastActivity = mmillis(); //reset watchdog } //check watchdog fuera del boton if (pantallaOn && ((millis() - lastActivity) > DURACION_WATCHDOG_MS)){ display.ssd1306_command(SSD1306_DISPLAYOFF); //se apaga la pantalla pantallaOn = false; } } vTaskDelay(pdMS_TO_TICKS(100)); //pequeño delay para no busy waiting } } void setup() { Serial.begin(115200); // OLED check OLED_test(); delay(500); // DHT check DHT_test(); delay(500); // SD Card check char *filename = SD_test(); delay(500); // GPS check GPS_test_wait(); delay(2000); // Crear tarea para mediciones xTaskCreatePinnedToCore( task_mediciones, // Función de la tarea "Mediciones", // Nombre de la tarea 2048, // Tamaño del stack (void*)filename, // 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 ); } void loop() { vTaskDelay(pdMS_TO_TICKS(1000)); // Espera 1 segundo }