main.ino 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  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. /*
  12. Uncomment and set up if you want to use custom pins for the SPI communication
  13. #define REASSIGN_PINS
  14. int sck = -1;
  15. int miso = -1;
  16. int mosi = -1;
  17. int cs = -1;
  18. */
  19. // Inicia I2C en pines default ESP32 (21 SDA, 22 SCL)
  20. #define SCREEN_WIDTH 128 // Ancho píxeles
  21. #define SCREEN_HEIGHT 64 // Alto píxeles
  22. #define OLED_RESET -1 // Reset pin (no usado)
  23. // Pines para UART2 (Serial2)
  24. #define RX_PIN 16 // RX del ESP32 conectado a TX del GPS
  25. #define TX_PIN 17 // TX del ESP32 conectado a RX del GPS
  26. #define GPS_BAUD 115200
  27. #define DHTPIN 4 // Digital pin connected to the DHT sensor
  28. #define DHTTYPE DHT22
  29. #define BUTTON_PIN 27 // pin para pulsador
  30. //definicion de tiempos de pulsacion
  31. #define PULASCION_LARGA_MS 2000
  32. #define DURACION_WATCHDOG_MS 10000
  33. #define MEASUREMENT_INTERVAL_S 1 //separación entre mediciones (s)
  34. #define DEG2RAD (M_PI/180.0)
  35. // Objeto TinyGPS++
  36. TinyGPSPlus gps;
  37. HardwareSerial gpsSerial(2); // Usar UART2
  38. DHT dht(DHTPIN, DHTTYPE);
  39. Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
  40. //valores de los sensores
  41. struct SensorData {
  42. double latitude = 0.0;
  43. double longitude = 0.0;
  44. float temperature = 0.0;
  45. float humidity = 0.0;
  46. float pressure = 0.0;
  47. float altura = 0.0;
  48. };
  49. SensorData latestData;
  50. SensorData datosAntiguos;
  51. SemaphoreHandle_t dataMutex; // Mutex para proteger el acceso a latestData
  52. SemaphoreHandle_t buttonSemaphore; // Semáforo para la tarea del botón
  53. //flag para reducir las lecturas de DHT y presion
  54. uint8_t readSensorsFlag = 0;
  55. bool grabando = false; //inicia apagado
  56. TaskHandle_t medicionesHandle = NULL; //para suspend/resume
  57. int pantallaEstado = 0; //maquina de estados
  58. float distancia_total = 0.0;
  59. char filename[13];
  60. void OLED_print(const String& line1, const String& line2) {
  61. display.clearDisplay();
  62. display.setTextSize(2);
  63. display.setTextColor(SSD1306_WHITE);
  64. display.setCursor(0, 0);
  65. display.println(line1);
  66. display.println(line2);
  67. display.display();
  68. }
  69. void DHT_test() {
  70. dht.begin();
  71. float h = dht.readHumidity();
  72. float t = dht.readTemperature();
  73. if (isnan(h) || isnan(t)) {
  74. Serial.println("Failed to read from DHT sensor!");
  75. OLED_print("DHT22", "Error");
  76. delay(5000);
  77. DHT_test(); // Reintentar
  78. }
  79. else OLED_print("DHT22", "Correcto");
  80. }
  81. void OLED_test() { //pantallazo a blanco y luego iniciando
  82. // Inicia I2C en pines default ESP32 (21 SDA, 22 SCL)
  83. Wire.begin();
  84. if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Dirección común: 0x3C
  85. Serial.println(F("Error: OLED no encontrado!"));
  86. for (;;); // Para siempre
  87. }
  88. display.clearDisplay();
  89. display.fillScreen(SSD1306_WHITE); // Pantalla blanca
  90. delay(500);
  91. display.display();
  92. display.clearDisplay();
  93. display.setTextSize(2); // Tamaño texto
  94. display.setTextColor(SSD1306_WHITE);
  95. display.setCursor(0, 0); // Posición
  96. display.println("Iniciando...");
  97. display.display(); // Muestra
  98. delay(1000);
  99. }
  100. void SD_test(){
  101. if (!SD.begin()) {
  102. Serial.println("SD Card Mount Failed");
  103. OLED_print("SD Card", "Error\nInserte");
  104. while (!SD.begin());
  105. OLED_print("SD Card", "Insertada");
  106. } else {
  107. OLED_print("SD Card", "Correcto");
  108. Serial.println("SD Card initialized.");
  109. }
  110. uint8_t cardType = SD.cardType();
  111. if (cardType == CARD_NONE) {
  112. Serial.println("No SD card attached");
  113. OLED_print("SD Card", "No detectada");
  114. while (cardType == CARD_NONE) {
  115. delay(1000);
  116. cardType = SD.cardType();
  117. }
  118. OLED_print("SD Card", "Detectada");
  119. }
  120. uint8_t cardSize = SD.cardSize() / (1024 * 1024);
  121. Serial.printf("SD Card Size: %uMB\n", cardSize);
  122. int num = 1;
  123. sprintf(filename, "/data%03d.txt", num);
  124. while (SD.exists(filename)) {
  125. Serial.printf("File %s already exists. Trying next.\n", filename);
  126. num++;
  127. sprintf(filename, "/data%03d.txt", num);
  128. }
  129. Serial.printf("Using file: %s\n", filename);
  130. File file = SD.open(filename, FILE_WRITE);
  131. if (file) {
  132. Serial.println("File created successfully");
  133. file.println("Latitud,Longitud,Temperatura,Humedad,Presion");
  134. file.close();
  135. } else {
  136. Serial.println("Error creando archivo");
  137. }
  138. }
  139. void GPS_test_wait() {
  140. // Iniciar Serial2 para GPS
  141. gpsSerial.begin(GPS_BAUD, SERIAL_8N1, RX_PIN, TX_PIN);
  142. while ((gpsSerial.available() > 0) && !gps.location.isValid()) {
  143. gps.encode(gpsSerial.read());
  144. delay(10);
  145. OLED_print("GPS", "Esperando.");
  146. delay(100);
  147. OLED_print("GPS", "Esperando..");
  148. delay(100);
  149. OLED_print("GPS", "Esperando...");
  150. }
  151. OLED_print("GPS", "Encontrado");
  152. }
  153. float calcular_delta_dist(float lat1, float long1, float lat2, float long2){
  154. float R = 6371.0; // Radio de la Tierra en km
  155. float delta_lat = (lat2 - lat1) * DEG2RAD;
  156. float delta_long = (long2 - long1) * DEG2RAD;
  157. lat1 = lat1 * DEG2RAD;
  158. lat2 = lat2 * DEG2RAD;
  159. float a = sin(delta_lat/2)*sin(delta_lat/2)+cos(lat1)*cos(lat2)*sin(delta_long/2)*sin(delta_long/2);
  160. float c = 2 * atan2(sqrt(a),sqrt(1-a));
  161. return R * c; //En km
  162. }
  163. void task_mediciones(void *pvParameters) {
  164. TickType_t xLastWakeTime = xTaskGetTickCount();
  165. while(1) {
  166. // se leen los valores antes de utilizar el semaphore
  167. while (gpsSerial.available() > 0) {
  168. gps.encode(gpsSerial.read());
  169. }
  170. float new_latitude = gps.location.lat();
  171. float new_longitude = gps.location.lng();
  172. float new_temp = (readSensorsFlag % 60 == 0) ? dht.readTemperature() : latestData.temperature;
  173. float new_hum = (readSensorsFlag % 120 == 0) ? dht.readHumidity() : latestData.humidity;
  174. float new_press = 0.0; // Placeholder, no hay sensor de presión
  175. if (gps.location.isValid() && datosAntiguos.latitude != 0.0) {
  176. distancia_total += calcular_delta_dist(datosAntiguos.latitude, datosAntiguos.longitude, new_latitude, new_longitude);
  177. }
  178. if (xSemaphoreTake(dataMutex, portMAX_DELAY) == pdTRUE) {
  179. latestData.latitude = new_latitude;
  180. latestData.longitude = new_longitude;
  181. latestData.temperature = new_temp;
  182. latestData.humidity = new_hum;
  183. latestData.pressure = new_press;
  184. datosAntiguos = latestData;
  185. xSemaphoreGive(dataMutex);
  186. }
  187. File file = SD.open(filename, FILE_APPEND);
  188. if (file) {
  189. //Crear la string para escribir en el archivo
  190. String frase = String(datosAntiguos.latitude) + "," +
  191. String(datosAntiguos.longitude) + "," +
  192. String(datosAntiguos.temperature) + "," +
  193. String(datosAntiguos.humidity) + "," +
  194. String(datosAntiguos.pressure);
  195. // Escribir datos en el archivo
  196. file.println(frase);
  197. file.close();
  198. }
  199. if (!(readSensorsFlag % 120)) readSensorsFlag = 0;
  200. readSensorsFlag++;
  201. vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(MEASUREMENT_INTERVAL_S*1000)); // Espera x*1000 milisegundos
  202. }
  203. }
  204. void IRAM_ATTR isr_button() {
  205. static unsigned long lastInterrupt = 0;
  206. if ((millis() - lastInterrupt) > 200 ){ // debounce de 200 ms
  207. BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  208. xSemaphoreGiveFromISR(buttonSemaphore, NULL);
  209. lastInterrupt = millis();
  210. if (xHigherPriorityTaskWoken) {
  211. portYIELD_FROM_ISR();
  212. }
  213. }
  214. }
  215. void task_ui(void *pvParameters){
  216. unsigned long pressTime = 0;
  217. unsigned long lastActivity = millis();
  218. bool pantallaOn = true; //comprobar el estado inicial, no se cual sera
  219. while(1){
  220. if (xSemaphoreTake(buttonSemaphore, pdMS_TO_TICKS(200)) == pdTRUE){ //button pressed
  221. pressTime = millis();
  222. if (!pantallaOn){
  223. display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  224. pantallaOn = true;
  225. }
  226. bool timed_out = false;
  227. while (digitalRead(BUTTON_PIN) == LOW){
  228. vTaskDelay(pdMS_TO_TICKS(10));
  229. if ((millis() - pressTime) > DURACION_WATCHDOG_MS){ //10s timeout para evitar bloqueos
  230. timed_out = true;
  231. break;
  232. }
  233. }
  234. if (timed_out){
  235. OLED_print("Apagando","pantalla");
  236. delay(1000);
  237. display.ssd1306_command(SSD1306_DISPLAYOFF); //se apaga la
  238. pantallaOn = false;
  239. } else {
  240. unsigned long duration = millis() - pressTime;
  241. if (duration >= PULASCION_LARGA_MS){
  242. //pulsacion larga: cabia entre grabacion y no grabacion de datos
  243. grabando != grabando;
  244. if (grabando){
  245. vTaskResume(medicionesHandle);
  246. //Mostrar que empieza la grabación
  247. OLED_print("Ruta","iniciada");
  248. } else {
  249. vTaskSuspend(medicionesHandle);
  250. //Mostrar que se pausa/finaliza la grabación
  251. OLED_print("Ruta","pausada");
  252. }
  253. } else {
  254. //Pulsacion corta + grabando datos, cicla datos
  255. if (grabando) {
  256. pantallaEstado = (pantallaEstado + 1) % 3; //cicla entre 0-2
  257. SensorData currentData;
  258. if(xSemaphoreTake(dataMutex, portMAX_DELAY) == pdTRUE){
  259. currentData = latestData;
  260. xSemaphoreGive(dataMutex);
  261. }
  262. switch (pantallaEstado){
  263. case 0:
  264. OLED_print("Posicion",String(currentData.longitude) + "," + String(currentData.latitude));
  265. break;
  266. case 1:
  267. OLED_print("Distancia",String(distancia_total)+"km");
  268. break;
  269. case 2:
  270. OLED_print("Altitud",String(gps.altitude.meters(), 1)+"m");
  271. break;
  272. }
  273. } else {
  274. OLED_print("Grabacion","pausada");
  275. }
  276. }
  277. }
  278. lastActivity = millis(); //reset watchdog
  279. }
  280. //check watchdog fuera del boton
  281. if (pantallaOn && ((millis() - lastActivity) > DURACION_WATCHDOG_MS)){
  282. display.ssd1306_command(SSD1306_DISPLAYOFF); //se apaga la pantalla
  283. pantallaOn = false;
  284. }
  285. vTaskDelay(pdMS_TO_TICKS(100)); //pequeño delay para no busy waiting
  286. }
  287. }
  288. void setup() {
  289. Serial.begin(115200);
  290. pinMode(BUTTON_PIN, INPUT_PULLUP);
  291. attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), isr_button, FALLING);
  292. buttonSemaphore = xSemaphoreCreateBinary();
  293. dataMutex = xSemaphoreCreateMutex();
  294. // OLED check
  295. OLED_test();
  296. delay(1000);
  297. // DHT check
  298. DHT_test();
  299. delay(1000);
  300. // SD Card check
  301. SD_test();
  302. delay(1000);
  303. // GPS check
  304. GPS_test_wait();
  305. delay(2000);
  306. // Crear tarea para mediciones
  307. xTaskCreatePinnedToCore(
  308. task_mediciones, // Función de la tarea
  309. "Mediciones", // Nombre de la tarea
  310. 2048, // Tamaño del stack
  311. NULL, // Parámetro de la tarea
  312. 10, // Prioridad de la tarea
  313. &medicionesHandle, // Handle de la tarea
  314. 0 // Núcleo donde se ejecuta
  315. );
  316. xTaskCreatePinnedToCore(
  317. task_ui, // Función de la tarea
  318. "UI", // Nombre de la tarea
  319. 8192, // Tamaño del stack
  320. NULL, // Parámetro de la tarea
  321. 5, // Prioridad de la tarea
  322. NULL, // Handle de la tarea
  323. 1 // Núcleo donde se ejecuta
  324. );
  325. vTaskSuspend(medicionesHandle); //inicia suspendida
  326. }
  327. void loop() {
  328. vTaskDelay(pdMS_TO_TICKS(1000)); // Espera 1 segundo
  329. }