main.ino 9.8 KB

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