| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349 |
- #include "TinyGPSPlus.h"
- #include "DHT.h"
- #include <Wire.h>
- #include <Adafruit_GFX.h>
- #include <Adafruit_SSD1306.h>
- #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
- }
|