main.ino 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. #include "TinyGPSPlus.h"
  2. #include "DHT.h"
  3. #include <Wire.h>
  4. #include <Adafruit_GFX.h>
  5. #include <Adafruit_SSD1306.h>
  6. #include "FS.h"
  7. #include "SD.h"
  8. #include "SPI.h"
  9. #include "Arduino.h"
  10. #include "math.h"
  11. #include "WiFi.h"
  12. #include "WebServer.h"
  13. #include "esp_pm.h"
  14. #include "esp_sleep.h"
  15. /*
  16. Uncomment and set up if you want to use custom pins for the SPI communication
  17. #define REASSIGN_PINS
  18. int sck = -1;
  19. int miso = -1;
  20. int mosi = -1;
  21. int cs = -1;
  22. */
  23. // Inicia I2C en pines default ESP32 (21 SDA, 22 SCL)
  24. #define SCREEN_WIDTH 128 // Ancho píxeles
  25. #define SCREEN_HEIGHT 64 // Alto píxeles
  26. #define OLED_RESET -1 // Reset pin (no usado)
  27. // Pines para UART2 (Serial2)
  28. #define RX_PIN 16 // RX del ESP32 conectado a TX del GPS
  29. #define TX_PIN 17 // TX del ESP32 conectado a RX del GPS
  30. #define GPS_BAUD 115200
  31. #define DHTPIN 4 // Digital pin connected to the DHT sensor
  32. #define DHTTYPE DHT22
  33. #define BUTTON_PIN 27 // pin para pulsador
  34. //definicion de tiempos de pulsacion
  35. #define PULASCION_LARGA_MS 2000
  36. #define DURACION_WATCHDOG_MS 10000
  37. #define MEASUREMENT_INTERVAL_S 1 //separación entre mediciones (s)
  38. #define DEG2RAD (M_PI/180.0)
  39. // Objeto TinyGPS++
  40. TinyGPSPlus gps;
  41. HardwareSerial gpsSerial(2); // Usar UART2
  42. DHT dht(DHTPIN, DHTTYPE);
  43. Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
  44. //valores de los sensores
  45. struct SensorData {
  46. double latitude = 0.0;
  47. double longitude = 0.0;
  48. float altura = 0.0;
  49. String tiempo = "";
  50. float velocidad = 0.0;
  51. float temperature = 0.0;
  52. float humidity = 0.0;
  53. float pressure = 0.0;
  54. };
  55. SensorData latestData;
  56. SensorData datosAntiguos;
  57. SemaphoreHandle_t dataMutex; // Mutex para proteger el acceso a latestData
  58. SemaphoreHandle_t buttonSemaphore; // Semáforo para la tarea del botón
  59. WebServer server(80);
  60. bool wifiActivado = false;
  61. unsigned long wifiLastActivity = 0;
  62. const unsigned long WIFI_TIMEOUT_MS = 300000; // 5 minutos
  63. const char* apSSID = "ESP32_GPS_Logger";
  64. const char* apPassword = "12345678";
  65. bool grabando = false; //inicia apagado
  66. bool finalizado = true; //indica que no hay ninguna grabacion ni iniciada ni pausada
  67. TaskHandle_t medicionesHandle = NULL; //para suspend/resume
  68. int pantallaEstado_grab = -1; //maquina de estados cuando se graba ruta
  69. int pantallaEstado_menu = -1; //maquina de estados cuando no se esta grabando ruta
  70. float distancia_total = 0.0;
  71. volatile unsigned long ignore_isr_until = 0; //para debounce
  72. char filename[13] = "/panchas.gpx";
  73. void OLED_print(const String& line1, const String& line2) {
  74. display.clearDisplay();
  75. display.setTextSize(2);
  76. display.setTextColor(SSD1306_WHITE);
  77. display.setCursor(0, 0);
  78. display.println(line1);
  79. display.println(line2);
  80. display.display();
  81. }
  82. void DHT_test() {
  83. dht.begin();
  84. float h = dht.readHumidity();
  85. float t = dht.readTemperature();
  86. if (isnan(h) || isnan(t)) {
  87. Serial.println("Failed to read from DHT sensor!");
  88. OLED_print("DHT22", "Error");
  89. delay(5000);
  90. DHT_test(); // Reintentar
  91. }
  92. else OLED_print("DHT22", "Correcto");
  93. }
  94. void OLED_test() { //pantallazo a blanco y luego iniciando
  95. // Inicia I2C en pines default ESP32 (21 SDA, 22 SCL)
  96. Wire.begin();
  97. if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Dirección común: 0x3C
  98. Serial.println(F("Error: OLED no encontrado!"));
  99. for (;;); // Para siempre
  100. }
  101. display.clearDisplay();
  102. display.fillScreen(SSD1306_WHITE); // Pantalla blanca
  103. delay(500);
  104. display.display();
  105. display.clearDisplay();
  106. display.setTextSize(2); // Tamaño texto
  107. display.setTextColor(SSD1306_WHITE);
  108. display.setCursor(0, 0); // Posición
  109. display.println("Iniciando...");
  110. display.display(); // Muestra
  111. delay(1000);
  112. }
  113. void SD_test(){
  114. if (!SD.begin()) {
  115. OLED_print("SD Card", "Error\nInserte");
  116. while (!SD.begin());
  117. OLED_print("SD Card", "Insertada");
  118. } else {
  119. OLED_print("SD Card", "Correcto");
  120. }
  121. uint8_t cardType = SD.cardType();
  122. if (cardType == CARD_NONE) {
  123. OLED_print("SD Card", "No detectada");
  124. while (cardType == CARD_NONE) {
  125. delay(1000);
  126. cardType = SD.cardType();
  127. }
  128. OLED_print("SD Card", "Detectada");
  129. }
  130. uint8_t cardSize = SD.cardSize() / (1024 * 1024);
  131. }
  132. void GPS_test_wait() {
  133. // Iniciar Serial2 para GPS
  134. bool fixObtained = false;
  135. gpsSerial.begin(GPS_BAUD, SERIAL_8N1, RX_PIN, TX_PIN);
  136. while (!fixObtained) {
  137. while (gpsSerial.available() > 0) {
  138. if (gps.encode(gpsSerial.read())) { // Procesa si hay sentencia NMEA completa
  139. if (gps.location.isValid() && gps.date.isValid() && gps.time.isValid()) {
  140. fixObtained = true;
  141. break;
  142. }
  143. }
  144. }
  145. delay(300);
  146. OLED_print("GPS", "Esperando");
  147. delay(300);
  148. OLED_print("GPS", "Esperando .");
  149. delay(300);
  150. OLED_print("GPS", "Esperando ..");
  151. delay(300);
  152. OLED_print("GPS", "Esperando ...");
  153. }
  154. OLED_print("GPS", "Encontrado");
  155. }
  156. float calcular_delta_dist(float lat1, float long1, float lat2, float long2){
  157. float R = 6371.0; // Radio de la Tierra en km
  158. float delta_lat = (lat2 - lat1) * DEG2RAD;
  159. float delta_long = (long2 - long1) * DEG2RAD;
  160. lat1 = lat1 * DEG2RAD;
  161. lat2 = lat2 * DEG2RAD;
  162. float a = sin(delta_lat/2)*sin(delta_lat/2)+cos(lat1)*cos(lat2)*sin(delta_long/2)*sin(delta_long/2);
  163. float c = 2 * atan2(sqrt(a),sqrt(1-a));
  164. return R * c; //En km
  165. }
  166. void task_mediciones(void *pvParameters) {
  167. TickType_t xLastWakeTime = xTaskGetTickCount();
  168. while(1) {
  169. unsigned long startMillis = millis();
  170. // se leen los valores antes de utilizar el semaphore
  171. while (gpsSerial.available() > 0) {
  172. if (gps.encode(gpsSerial.read())) {
  173. break;
  174. }
  175. }
  176. float new_latitude = gps.location.lat();
  177. float new_longitude = gps.location.lng();
  178. float new_altitude = gps.altitude.meters();
  179. String new_fecha = String(gps.date.year())+"-"+String(gps.date.month())+"-"+
  180. String(gps.date.day())+"T"+String(gps.time.hour())+":"+
  181. String(gps.time.minute())+":"+String(gps.time.second())+"."+
  182. String(gps.time.centisecond(),3);
  183. float new_speed = gps.speed.kmph();
  184. float new_temp = dht.readTemperature();
  185. float new_hum = dht.readHumidity();
  186. float new_press = 0.0; // Placeholder, no hay sensor de presión
  187. distancia_total += calcular_delta_dist(datosAntiguos.latitude, datosAntiguos.longitude, new_latitude, new_longitude);
  188. if (xSemaphoreTake(dataMutex, portMAX_DELAY) == pdTRUE) {
  189. latestData.latitude = new_latitude;
  190. latestData.longitude = new_longitude;
  191. latestData.altura = new_altitude;
  192. latestData.tiempo = new_fecha;
  193. latestData.velocidad = new_speed;
  194. latestData.temperature = new_temp;
  195. latestData.humidity = new_hum;
  196. latestData.pressure = new_press;
  197. datosAntiguos = latestData;
  198. xSemaphoreGive(dataMutex);
  199. }
  200. File file = SD.open(filename, FILE_APPEND);
  201. if (file) {
  202. //Crear la string para escribir en el archivo
  203. file.print(F("\t\t\t<trkpt lat=\""));
  204. file.print(datosAntiguos.latitude, 6);
  205. file.print(F("\" lon=\""));
  206. file.print(datosAntiguos.longitude, 6);
  207. file.println(F("\">"));
  208. file.print(F("\t\t\t\t<ele>"));
  209. file.print(datosAntiguos.altura);
  210. file.println(F("</ele>"));
  211. file.print(F("\t\t\t\t<time>"));
  212. file.print(datosAntiguos.tiempo);
  213. file.println(F("</time>"));
  214. file.print(F("\t\t\t\t<speed>"));
  215. file.print(datosAntiguos.velocidad);
  216. file.println(F("</speed>"));
  217. file.println(F("\t\t\t\t<extensions>"));
  218. file.println(F("\t\t\t\t\t<gpxtpx:TrackPointExtension>"));
  219. file.print(F("\t\t\t\t\t\t<gpxtpx:atemp>"));
  220. file.print(datosAntiguos.temperature);
  221. file.println(F("</gpxtpx:atemp>"));
  222. file.println(F("\t\t\t\t\t</gpxtpx:TrackPointExtension>"));
  223. file.print(F("\t\t\t\t\t<custom:humidity>"));
  224. file.print(datosAntiguos.humidity);
  225. file.println(F("</custom:humidity>"));
  226. file.print(F("\t\t\t\t\t<custom:pressure>"));
  227. file.print(datosAntiguos.pressure);
  228. file.println(F("</custom:pressure>"));
  229. file.println(F("\t\t\t\t</extensions>"));
  230. file.println(F("\t\t\t</trkpt>"));
  231. file.close();
  232. }
  233. unsigned long elapsedMillis = millis() - startMillis;
  234. Serial.println(elapsedMillis);
  235. vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(MEASUREMENT_INTERVAL_S*1000)); // Espera x*1000 milisegundos
  236. }
  237. }
  238. void crear_archivo(){
  239. distancia_total = 0.0;
  240. int num = 1;
  241. sprintf(filename, "/data%03d.gpx", num);
  242. while (SD.exists(filename)) {
  243. num++;
  244. sprintf(filename, "/data%03d.gpx", num);
  245. }
  246. File file = SD.open(filename, FILE_WRITE);
  247. if (file) {
  248. file.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
  249. "<gpx creator=\"ESP32 GPS LOGGER\" version=\"1.1\"\n"
  250. "\txmlns=\"http://www.topografix.com/GPX/1/1\"\n"
  251. "\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
  252. "\txmlns:gpxtpx=\"http://www.garmin.com/xmlschemas/TrackPointExtension/v2\"\n"
  253. "\txmlns:gpxdata=\"http://www.cluetrust.com/XML/GPXDATA/1/0\"\n"
  254. "\txsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\">\n"
  255. "\t<metadata>\n"
  256. "\t\t<name>Ruta grabada con ESP32 GPS Logger</name>\n"
  257. "\t\t<time>");
  258. file.print(String(gps.date.year()) + "-" + String(gps.date.month()) + "-" +
  259. String(gps.date.day()) + "T" + String(gps.time.hour()) + ":" +
  260. String(gps.time.minute()) + ":" + String(gps.time.second()) +
  261. "." + String(gps.time.centisecond()));
  262. file.println("</time>\n\t</metadata>\n"
  263. "\t<trk>\n"
  264. "\t\t<name>Rutita</name>\n"
  265. "\t\t<type>hiking</type>\n"
  266. "\t\t<trkseg>");
  267. file.close();
  268. } else {
  269. OLED_print("Error","creando archivo");
  270. delay(2000);
  271. }
  272. }
  273. void cerrar_archivo() {
  274. File file = SD.open(filename, FILE_APPEND);
  275. if (file){
  276. file.print("\t\t</trkseg>\n\t</trk>\n</gpx>");
  277. file.close();
  278. }
  279. //int num = 1;
  280. //sprintf(filename, "/data%03d.gpx", num);
  281. //while (SD.exists(filename)) {
  282. // num++;
  283. // sprintf(filename, "/data%03d.gpx", num);
  284. //}
  285. }
  286. void activarWiFi(){
  287. OLED_print("WiFi","Activando...");
  288. WiFi.mode(WIFI_AP);
  289. WiFi.softAP(apSSID, apPassword);
  290. IPAddress IP = WiFi.softAPIP();
  291. OLED_print("WiFi Activo", IP.toString());
  292. delay(2000);
  293. server.on("/", HTTP_GET, []() {
  294. wifiLastActivity = millis();
  295. String html = "<!DOCTYPE html><html><head><meta charset='UTF-8'><title>ESP32 GPS Logger</title>";
  296. html += "<style>body{font-family:Arial;text-align:center;margin:50px;}";
  297. html += "h1{color:#333;} a.button{background:#4CAF50;color:white;padding:20px 40px;";
  298. html += "text-decoration:none;font-size:24px;border-radius:12px;display:inline-block;margin:20px;}</style></head>";
  299. html += "<body><h1>GPS Logger</h1><p>Archivo listo para descargar:</p>";
  300. String nombreArchivo = String(filename).substring(1); // Ej. "data001.gpx"
  301. html += "<a href='/download' class='button' download='" + nombreArchivo + "'>Descargar " + nombreArchivo + "</a>";
  302. html += "<hr><p>IP: " + WiFi.softAPIP().toString() + "</p>";
  303. html += "<p>Se apagar&aacute; en 5 min sin uso.</p></body></html>";
  304. server.send(200, "text/html", html);
  305. });
  306. server.on("/download",HTTP_GET, []() {
  307. wifiLastActivity = millis();
  308. if (!SD.exists(filename)) {
  309. server.send(404, "text/plain", "Archivo no encontrado");
  310. return;
  311. }
  312. File file = SD.open(filename, FILE_READ);
  313. if (!file) {
  314. server.send(404, "text/plain", "Error al abrir el archivo");
  315. return;
  316. }
  317. String nombreArchivo = String(filename).substring(1); // Ej. "data001.gpx"
  318. server.sendHeader("Content-Disposition", "attachment; filename=\"" + nombreArchivo + "\"");
  319. server.streamFile(file, "application/gpx+xml");
  320. file.close();
  321. });
  322. server.begin();
  323. wifiActivado = true;
  324. wifiLastActivity = millis();
  325. }
  326. void desactivarWiFi(){
  327. server.stop();
  328. WiFi.softAPdisconnect(true);
  329. WiFi.mode(WIFI_OFF);
  330. wifiActivado = false;
  331. OLED_print("WiFi","Apagado");
  332. }
  333. void IRAM_ATTR isr_button() {
  334. unsigned long now = millis();
  335. if (now < ignore_isr_until) {
  336. return; // Ignorar interrupción si está dentro del período de debounce
  337. }
  338. static unsigned long lastInterrupt = 0;
  339. if ((now - lastInterrupt) > 300 ){ // debounce de 300 ms
  340. BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  341. xSemaphoreGiveFromISR(buttonSemaphore, &xHigherPriorityTaskWoken);
  342. lastInterrupt = now;
  343. if (xHigherPriorityTaskWoken) {
  344. portYIELD_FROM_ISR();
  345. }
  346. }
  347. }
  348. void drawProgressBar(int x, int y, int w, int h, unsigned long progress, unsigned long total) {
  349. display.drawRect(x, y, w, h, SSD1306_WHITE); // Dibuja el borde
  350. int filledWidth = (progress * w) / total;
  351. display.fillRect(x + 1, y + 1, filledWidth - 2, h - 2, SSD1306_WHITE); // Dibuja la barra llena
  352. display.display();
  353. }
  354. void task_ui(void *pvParameters){
  355. unsigned long pressTime = 0;
  356. unsigned long lastActivity = millis();
  357. bool pantallaOn = true; //comprobar el estado inicial, no se cual sera
  358. bool processingButton = false;
  359. while(1){
  360. if (xSemaphoreTake(buttonSemaphore, pdMS_TO_TICKS(200)) == pdTRUE){ //button pressed
  361. if (processingButton) continue; //evita reentradas
  362. processingButton = true;
  363. pressTime = millis();
  364. lastActivity = millis(); //reset watchdog
  365. if (!pantallaOn){
  366. display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  367. pantallaOn = true;
  368. }
  369. bool timed_out = false;
  370. unsigned long checkTime = millis();
  371. while (digitalRead(BUTTON_PIN) == LOW){
  372. vTaskDelay(pdMS_TO_TICKS(10));
  373. checkTime = millis();
  374. if ((checkTime - pressTime) > DURACION_WATCHDOG_MS){ //10s timeout para evitar bloqueos
  375. timed_out = true;
  376. break;
  377. }
  378. drawProgressBar(0, SCREEN_HEIGHT - 10, SCREEN_WIDTH, 8, checkTime - pressTime, PULASCION_LARGA_MS);
  379. }
  380. ignore_isr_until = millis() + 500; //ignorar nuevas interrupciones durante 500 ms
  381. unsigned long duration = checkTime - pressTime;
  382. if (timed_out){
  383. OLED_print("Apagando","pantalla");
  384. display.ssd1306_command(SSD1306_DISPLAYOFF); //se apaga la
  385. pantallaOn = false;
  386. } else {
  387. if (grabando){
  388. if (duration >= PULASCION_LARGA_MS){
  389. grabando = false;
  390. vTaskSuspend(medicionesHandle);
  391. OLED_print("Ruta","pausada");
  392. } else {
  393. pantallaEstado_grab = (pantallaEstado_grab + 1) % 5; //cicla entre 0-4
  394. SensorData currentData;
  395. if(xSemaphoreTake(dataMutex, portMAX_DELAY) == pdTRUE){
  396. currentData = latestData;
  397. xSemaphoreGive(dataMutex);
  398. }
  399. switch (pantallaEstado_grab){
  400. case 0:
  401. OLED_print("Posicion",String(currentData.longitude) + "," + String(currentData.latitude));
  402. break;
  403. case 1:
  404. OLED_print("Distancia",String(distancia_total)+"km");
  405. break;
  406. case 2:
  407. OLED_print("Altitud",String(currentData.altura, 1)+"m");
  408. break;
  409. case 3:
  410. OLED_print("Temp/Hum",String(currentData.temperature,1)+"C/"+String(currentData.humidity,1)+"%");
  411. break;
  412. case 4:
  413. OLED_print("Velocidad",String(currentData.velocidad, 1)+"km/h");
  414. break;
  415. }
  416. }
  417. } else {
  418. if (duration >= PULASCION_LARGA_MS){
  419. switch (pantallaEstado_menu){
  420. case 0:
  421. //activar la ruta y crear el archivo
  422. crear_archivo();
  423. vTaskResume(medicionesHandle);
  424. OLED_print("Ruta","iniciada");
  425. finalizado = false;
  426. grabando = true;
  427. break;
  428. case 1:
  429. //cerrar el archivo y cambiar el valor de 'filename'
  430. cerrar_archivo();
  431. finalizado = true;
  432. break;
  433. case 2:
  434. //implementacion wifi
  435. if(!wifiActivado){
  436. activarWiFi();
  437. } else {
  438. desactivarWiFi();
  439. }
  440. break;
  441. }
  442. } else {
  443. pantallaEstado_menu = (pantallaEstado_menu + 1) % 3;
  444. int previous_state = -1;
  445. while (pantallaEstado_menu != previous_state) {
  446. previous_state = pantallaEstado_menu;
  447. switch (pantallaEstado_menu) {
  448. case 0:
  449. if (!finalizado) OLED_print("Reanudar","ruta");
  450. else OLED_print("Iniciar","ruta");
  451. break;
  452. case 1:
  453. if (SD.exists(filename) && !finalizado) {
  454. OLED_print("Finalizar","ruta");
  455. break;
  456. } else {
  457. pantallaEstado_menu = (pantallaEstado_menu + 1) % 3;
  458. }
  459. break;
  460. case 2:
  461. if (finalizado) {
  462. OLED_print("Conexion","WiFi");
  463. break;
  464. } else {
  465. pantallaEstado_menu = (pantallaEstado_menu + 1) % 3;
  466. }
  467. break;
  468. }
  469. }
  470. }
  471. }
  472. }
  473. lastActivity = millis(); //reset watchdog
  474. processingButton = false;
  475. vTaskDelay(pdMS_TO_TICKS(100)); //pequeño delay para no busy waiting
  476. }
  477. //check watchdog fuera del boton
  478. if (pantallaOn && ((millis() - lastActivity) > DURACION_WATCHDOG_MS)){
  479. display.ssd1306_command(SSD1306_DISPLAYOFF); //se apaga la pantalla
  480. pantallaOn = false;
  481. }
  482. //check wifi timeout
  483. if (wifiActivado){
  484. server.handleClient();
  485. if (WiFi.softAPgetStationNum() > 0){
  486. wifiLastActivity = millis(); //reset si hay clientes conectados
  487. }
  488. if ((millis() - wifiLastActivity) > WIFI_TIMEOUT_MS){
  489. desactivarWiFi();
  490. }
  491. }
  492. }
  493. }
  494. void setup() {
  495. Serial.begin(115200);
  496. pinMode(BUTTON_PIN, INPUT_PULLUP);
  497. attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), isr_button, FALLING);
  498. buttonSemaphore = xSemaphoreCreateBinary();
  499. dataMutex = xSemaphoreCreateMutex();
  500. // OLED check
  501. OLED_test();
  502. delay(1000);
  503. // DHT check
  504. DHT_test();
  505. delay(1000);
  506. // SD Card check
  507. SD_test();
  508. delay(1000);
  509. // GPS check
  510. // Enable RMC and VTG at 1 Hz
  511. gpsSerial.println("$PMTK314,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29"); // Enables GGA, RMC, VTG
  512. gpsSerial.println("$PMTK220,1000*1F"); // Set update rate to 1 Hz (1000ms)
  513. delay(1000); // Give time to apply
  514. GPS_test_wait();
  515. delay(2000);
  516. // Crear tarea para mediciones
  517. xTaskCreatePinnedToCore(
  518. task_mediciones, // Función de la tarea
  519. "Mediciones", // Nombre de la tarea
  520. 8192, // Tamaño del stack
  521. NULL, // Parámetro de la tarea
  522. 10, // Prioridad de la tarea
  523. &medicionesHandle, // Handle de la tarea
  524. 0 // Núcleo donde se ejecuta
  525. );
  526. xTaskCreatePinnedToCore(
  527. task_ui, // Función de la tarea
  528. "UI", // Nombre de la tarea
  529. 8192, // Tamaño del stack
  530. NULL, // Parámetro de la tarea
  531. 5, // Prioridad de la tarea
  532. NULL, // Handle de la tarea
  533. 1 // Núcleo donde se ejecuta
  534. );
  535. esp_pm_config_esp32_t pm_config = {
  536. .max_freq_mhz = 240,
  537. .min_freq_mhz = 80,
  538. .light_sleep_enable = true
  539. };
  540. esp_err_t err = esp_pm_configure(&pm_config);
  541. if (err != ESP_OK) {
  542. Serial.println("Error configuring power management");
  543. }
  544. WiFi.setSleep(true); // Desactiva el modo de ahorro de energía del WiFi
  545. vTaskSuspend(medicionesHandle); //inicia suspendida
  546. }
  547. void loop() {
  548. vTaskDelete(NULL); // Deshabilita el loop
  549. }