#include #include #include #include #include // Necesario para la fecha y hora #include #include #include // Controladores #include "bmp180.h" #include "htu21d.h" #include "sen0472_o3.h" #include "sen0471_no2.h" #include "sen0470_so2.h" #include "sen0466_co.h" // Definimos la ruta donde vivirá tu base de datos final #define RUTA_BD "/var/www/html/AirQualityMicrostation/Database/microestacionAQ.db" // Ajusta esta ruta a tu carpeta real void guardar_lote_en_bd(const char *ruta_lote) { sqlite3 *bd; char *mensaje_error = 0; // 1. Abrimos o creamos la base de datos if (sqlite3_open(RUTA_BD, &bd) != SQLITE_OK) { printf("Error: No se pudo abrir la base de datos: %s\n", sqlite3_errmsg(bd)); return; } // 2. Creamos la tabla si no existe const char *instruccion_crear = "CREATE TABLE IF NOT EXISTS mediciones (" "id INTEGER PRIMARY KEY AUTOINCREMENT, " "fecha_hora TEXT, presion REAL, temperatura REAL, " "humedad REAL, ozono REAL, no2 REAL, co INTEGER, so2 REAL, " "ica INTEGER, banda_ica TEXT);"; if (sqlite3_exec(bd, instruccion_crear, 0, 0, &mensaje_error) != SQLITE_OK) { printf("Error al crear tabla: %s\n", mensaje_error); sqlite3_free(mensaje_error); } // 3. Abrimos el archivo temporal que contiene las lecturas de los últimos 5 minutos FILE *archivo = fopen(ruta_lote, "r"); if (archivo == NULL) { printf("Advertencia: No se encontró el archivo temporal para guardar.\n"); sqlite3_close(bd); return; } // Arreglo temporal para guardar cada línea de texto que leamos char linea[256]; // 4. Leemos el archivo línea por línea hasta llegar al final while (fgets(linea, sizeof(linea), archivo)) { char fecha_hora[64]; double presion, temperatura, humedad, ozono, no2, so2; int co; // 5. Extraemos los valores separados por comas // El formato %[^,] significa "lee todo el texto hasta encontrar una coma" if (sscanf(linea, "%[^,],%lf,%lf,%lf,%lf,%lf,%d,%lf", fecha_hora, &presion, &temperatura, &humedad, &ozono, &no2, &co, &so2) == 8) { // 6. Armamos la instrucción de inserción con los valores exactos char instruccion_insertar[512]; sprintf(instruccion_insertar, "INSERT INTO mediciones (fecha_hora, presion, temperatura, humedad, ozono, no2, co, so2, ica, banda_ica) " "VALUES ('%s', %.2f, %.2f, %.2f, %.2f, %.2f, %d, %.2f, NULL, NULL);", fecha_hora, presion, temperatura, humedad, ozono, no2, co, so2); // 7. Ejecutamos la inserción en la base de datos if (sqlite3_exec(bd, instruccion_insertar, 0, 0, &mensaje_error) != SQLITE_OK) { printf("Error al insertar fila: %s\n", mensaje_error); sqlite3_free(mensaje_error); } } } // 8. Limpiamos y cerramos todo fclose(archivo); sqlite3_close(bd); } int main() { // --- Apertura de los buses --- // Bus principal: Clima (BMP180 y HTU21D) int fd_principal = open("/dev/i2c-1", O_RDWR); if (fd_principal < 0) { printf("Error: No se pudo abrir el bus I2C principal.\n"); return 1; } // Bus secundario: Gases (Familia DFRobot) int fd_secundario = open("/dev/i2c-3", O_RDWR); if (fd_secundario < 0) { printf("Error: No se pudo abrir el bus I2C secundario.\n"); return 1; } // --- Configuración inicial --- printf("Iniciando microestación...\n"); // BMP180 en el bus principal (Conexión e inicialización) ioctl(fd_principal, I2C_SLAVE, 0x77); // <- LÍNEA NUEVA BMP180_CalibData calibracion_bmp; bmp180_init(fd_principal, &calibracion_bmp); // Sensores de gas en el bus secundario changeQA_O3(fd_secundario); changeQA_NO2(fd_secundario); changeQA_CO(fd_secundario); changeQA_SO2(fd_secundario); double presion, temp_htu, humedad, ozono, dioxido_nitrogeno, dioxido_azufre; int monoxido; int contador_bd = 0; // --- Ciclo continuo de monitoreo --- while (1) { // 1. Obtener la fecha y hora actual del sistema time_t t = time(NULL); struct tm tm = *localtime(&t); char fecha_hora[64]; // Formato: Año-Mes-Día Hora:Minuto:Segundo sprintf(fecha_hora, "%04d-%02d-%02d %02d:%02d:%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); // 2. Extracción de datos separada por buses // Bus Principal ioctl(fd_principal, I2C_SLAVE, 0x77); // Recuperamos el control del bus para el bmp180 bmp180_get_pressure(fd_principal, &calibracion_bmp, 2, &presion); getTemperature(fd_principal, &temp_htu); getHumidity(fd_principal, &humedad); // Bus Secundario getO3(fd_secundario, &ozono); getNO2(fd_secundario, &dioxido_nitrogeno); getCO(fd_secundario, &monoxido); getSO2(fd_secundario, &dioxido_azufre); // 3. Escribir el estado actual (Sobrescribir archivo para el comando terminal) FILE *archivo_actual = fopen("/dev/shm/aql_actual.txt", "w"); if (archivo_actual != NULL) { fprintf(archivo_actual, "PRES=%.2f\n", presion); fprintf(archivo_actual, "TEMP=%.2f\n", temp_htu); fprintf(archivo_actual, "HUM=%.2f\n", humedad); fprintf(archivo_actual, "O3=%.2f\n", ozono); fprintf(archivo_actual, "NO2=%.2f\n", dioxido_nitrogeno); fprintf(archivo_actual, "CO=%d\n", monoxido); fprintf(archivo_actual, "SO2=%.2f\n", dioxido_azufre); fprintf(archivo_actual, "FECHA=\"%s\"\n", fecha_hora); fclose(archivo_actual); } // 4. Escribir el historial temporal (Agregar línea para la Base de Datos) // Usamos "a" (append) para añadir sin borrar lo anterior FILE *archivo_lote = fopen("/dev/shm/lote_bd.csv", "a"); if (archivo_lote != NULL) { // Guardamos todo separado por comas para facilitar la lectura posterior fprintf(archivo_lote, "%s,%.2f,%.2f,%.2f,%.2f,%.2f,%d,%.2f\n", fecha_hora, presion, temp_htu, humedad, ozono, dioxido_nitrogeno, monoxido, dioxido_azufre); fclose(archivo_lote); } // 5. Lógica de almacenamiento masivo en la Base de Datos (Cada 5 minutos) contador_bd++; if (contador_bd >= 30) { // Llamamos a la función pasándole la ruta del archivo temporal guardar_lote_en_bd("/dev/shm/lote_bd.csv"); // Una vez guardado, borramos el archivo de lote para empezar de cero FILE *limpiar_lote = fopen("/dev/shm/lote_bd.csv", "w"); if (limpiar_lote != NULL) fclose(limpiar_lote); contador_bd = 0; } // Esperar 10 segundos sleep(10); } close(fd_principal); close(fd_secundario); return 0; }