commit fb6c1efba48c56dccc8b9c1f09ff920fad405f7f Author: Fernando Martínez Valenzuela Date: Tue Jun 30 19:46:22 2026 -0600 Primer Commit Repositorio diff --git a/EsquemaPines.png b/EsquemaPines.png new file mode 100644 index 0000000..5d4fe92 Binary files /dev/null and b/EsquemaPines.png differ diff --git a/InterfazWeb.png b/InterfazWeb.png new file mode 100644 index 0000000..089ae82 Binary files /dev/null and b/InterfazWeb.png differ diff --git a/MHZ19B/Data/MHZ19B.json b/MHZ19B/Data/MHZ19B.json new file mode 100644 index 0000000..c0c2fe1 --- /dev/null +++ b/MHZ19B/Data/MHZ19B.json @@ -0,0 +1,3 @@ +{ + "ppm": 400 +} diff --git a/MHZ19B/mhz19b_driver.c b/MHZ19B/mhz19b_driver.c new file mode 100644 index 0000000..1b2e305 --- /dev/null +++ b/MHZ19B/mhz19b_driver.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include +#include + +// --- 1. FUNCIÓN DE INICIALIZACIÓN --- +// Devuelve el descriptor de archivo (fd) o -1 si falla. +int init_uart(const char *puerto, speed_t baudrate) { + // Abrir el puerto: Lectura/Escritura, no ser la terminal controladora, no bloquear. + int fd = open(puerto, O_RDWR | O_NOCTTY | O_NDELAY); + if (fd == -1) { + perror("Error al abrir el puerto UART"); + return -1; + } + + struct termios tty; + if (tcgetattr(fd, &tty) != 0) { + perror("Error al obtener los atributos del puerto"); + close(fd); + return -1; + } + + // Configurar Baudios + cfsetispeed(&tty, baudrate); + cfsetospeed(&tty, baudrate); + + // MODO CRUDO: 8N1 (8 bits, Sin paridad, 1 bit de parada) + tty.c_cflag &= ~PARENB; // Sin paridad + tty.c_cflag &= ~CSTOPB; // 1 bit de parada + tty.c_cflag &= ~CSIZE; // Limpiar máscara de tamaño + tty.c_cflag |= CS8; // 8 bits de datos + + // Desactivar control de flujo por hardware y señales de módem + tty.c_cflag &= ~CRTSCTS; + tty.c_cflag |= CREAD | CLOCAL; + + // Desactivar procesamiento de software (Modo crudo absoluto) + tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + tty.c_oflag &= ~OPOST; + tty.c_iflag &= ~(IXON | IXOFF | IXANY); + tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL); + + // Configuración de tiempos de espera (Timeout) + tty.c_cc[VMIN] = 0; // No exigir un mínimo de bytes + tty.c_cc[VTIME] = 20; // Esperar máximo 2 segundos (20 decimas) + + // Limpiar basura del búfer y aplicar la configuración + tcflush(fd, TCIFLUSH); + if (tcsetattr(fd, TCSANOW, &tty) != 0) { + perror("Error al aplicar la configuración UART"); + close(fd); + return -1; + } + + // Quitar el modo "No Bloqueante" para que VTIME y VMIN funcionen correctamente + fcntl(fd, F_SETFL, 0); + return fd; +} + +// --- 2. FUNCIÓN PARA ENVIAR --- +void send_uart(int fd, unsigned char *trama, int longitud) { + tcflush(fd, TCIOFLUSH); // Limpiar tubería antes de hablar + int escritos = write(fd, trama, longitud); + if (escritos < 0) { + perror("Error al escribir en el puerto"); + } +} + +// --- 3. FUNCIÓN PARA RECIBIR --- +int receive_uart(int fd, unsigned char *buffer, int longitud_esperada) { + int bytes_leidos = read(fd, buffer, longitud_esperada); + return bytes_leidos; +} + +// --- EJEMPLO DE USO --- +int main() { + int uart_fd = init_uart("/dev/serial0", B9600); + if (uart_fd == -1) return 1; + + unsigned char comando[] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; + unsigned char respuesta[9]; + + while(1) { + send_uart(uart_fd, comando, sizeof(comando)); + usleep(100000); // 100ms para que el sensor responda + + int bytes = receive_uart(uart_fd, respuesta, 9); + + if (bytes == 9) { + // 1. Calcular el valor real + int co2_ppm = (respuesta[2] * 256) + respuesta[3]; + + // 2. Imprimir en consola (para tu propio monitoreo) + printf("Respuesta OK: CO2 = %d ppm\n", co2_ppm); + + // 3. Escribir el valor en el archivo JSON para la interfaz web + FILE *archivo_json = fopen("Data/MHZ19B.json", "w"); // "w" sobreescribe el archivo cada vez + if (archivo_json != NULL) { + // Formatear estrictamente como JSON + fprintf(archivo_json, "{\n \"ppm\": %d\n}\n", co2_ppm); + fclose(archivo_json); // Liberar el archivo para que JS pueda leerlo + } else { + perror("Error: No se pudo crear/abrir el archivo JSON"); + } + + } else { + printf("Error: Bytes recibidos = %d\n", bytes); + } + + sleep(10); + } + + close(uart_fd); + return 0; +} diff --git a/MHZ19B/mhz_run b/MHZ19B/mhz_run new file mode 100755 index 0000000..c624929 Binary files /dev/null and b/MHZ19B/mhz_run differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..881faef --- /dev/null +++ b/README.md @@ -0,0 +1,245 @@ +# Proyecto de Investigación: Integración del Sensor NDIR MH-Z19B vía UART + +Este repositorio documenta la integración, adquisición y visualización de datos del sensor de dióxido de carbono (CO₂) **MH-Z19B**. + +El propósito principal de este proyecto es estudiar y comprender la arquitectura del protocolo de comunicación asíncrona **UART** a bajo nivel en sistemas embebidos basados en Linux utilizando una **Raspberry Pi 4**. Esta implementación integra una prueba experimental para la integración de sensores que formarán parte del desarrollo de microestaciones destinadas al monitoreo de la calidad del aire, desarrolladas durante el **Verano de la Investigación Científica y Tecnológica del Programa Delfín 2026**. + +--- +# Objetivo del Proyecto + +Implementar un controlador de bajo nivel para el sensor **MH-Z19B** utilizando comunicación **UART**, integrándolo con una interfaz web capaz de visualizar las mediciones de CO₂ en tiempo real. Esta implementación constituye la base para el desarrollo de futuras microestaciones inteligentes destinadas al monitoreo ambiental y la calidad del aire. + +--- + +# Especificaciones Técnicas del Sensor (MH-Z19B) + +El **MH-Z19B** es un sensor compacto que utiliza la tecnología **NDIR (Non-Dispersive Infrared)** para medir la concentración de dióxido de carbono (CO₂) en el aire. Esta tecnología se basa en que las moléculas de gas absorben luz infrarroja a longitudes de onda muy específicas por lo que proporciona alta selectividad, buena estabilidad y no depende de la concentración de oxígeno para realizar las mediciones. + +Las principales características del sensor son: + +| Característica | Valor | +| :--- | :--- | +| Tecnología | NDIR (Non-Dispersive Infrared) | +| Gas medido | CO₂ | +| Rango de medición | 0–2000 ppm o 0–5000 ppm (configurable) | +| Voltaje de alimentación | 3.6–5.5 V DC | +| Tiempo de precalentamiento | 3 minutos | +| Comunicación | UART TTL | +| Velocidad de transmisión | 9600 baudios | +| Configuración UART | 8 bits de datos, sin paridad, 1 bit de parada (8N1) | +| Vida útil estimada | Mayor a 5 años | + +--- + +# Material +## Hardware + +- Raspberry Pi 4 +- Sensor MH-Z19B +- Cables Jumper +- MicroSD card with Raspberry Pi OS + +## Software +- UART (TTL) +- Lenguaje C +- HTML5 +- CSS3 +- JavaScript +- JSON +- Nginx +- Linux +- Git + +--- + + +# Conexiones de Hardware (Capa Física) + +Para establecer correctamente la comunicación UART entre el sensor y la Raspberry Pi 4 es necesario cruzar las líneas de transmisión y recepción. + +| Pin MH-Z19B | Función | Raspberry Pi 4 | Descripción | +| :--- | :--- | :--- | :--- | +| Vin (Pin 6) | Alimentación | Pin físico **2** o **4** (5 V) | Alimenta el sensor. | +| GND (Pin 7) | Tierra | Pin físico **6** (GND) | Referencia eléctrica común. | +| TXD (Pin 3) | Transmisión | Pin físico **10** (GPIO15 / RXD) | Envía los datos del sensor hacia la Raspberry Pi. | +| RXD (Pin 2) | Recepción | Pin físico **8** (GPIO14 / TXD) | Recibe los comandos enviados por la Raspberry Pi. | + +![Esquema de Conexiones](EsquemaPines.png) + +--- + +# Guía de Uso + +## 1. Actualizar el Sistema + +```bash +sudo apt update && sudo apt upgrade -y +``` + +--- + +## 2. Instalar Dependencias + +```bash +sudo apt install git gcc nginx -y +``` + +--- + +## 3. Habilitar UART + +Ejecutar: + +```bash +sudo raspi-config +``` + +Ir a: + +```text +Interface Options + └── Serial Port +``` + +Configurar: + +```text +Would you like a login shell to be accessible over serial? +No + +Would you like the serial port hardware to be enabled? +Yes +``` + +Reiniciar el sistema: + +```bash +sudo reboot +``` + +--- + +## 4. Clonar el Repositorio + +```bash +git clone https://gitea.itmorelia.com/Verano-Delfin-2026/CO2-Sensor.git +cd CO2-Sensor +``` + +--- + +## 5. Configurar Nginx + +Copiar los archivos de la interfaz web al servidor: + +```bash +sudo cp index.html /var/www/html/ +sudo cp -r MHZ19B /var/www/html/ +``` + +Reiniciar Nginx: + +```bash +sudo systemctl restart nginx +``` + +--- + +# Compilación del Controlador en C + +Ubicarse en el directorio del proyecto: + +```bash +cd /var/www/html/ +``` + +Compilar el controlador: + +```bash +gcc mhz19b_driver.c -o driver_co2 +``` + +Asignar permisos: + +```bash +sudo chmod +x driver_co2 +``` + +Ejecutar el programa: + +```bash +sudo ./driver_co2 +``` + +El controlador se comunicará con el sensor mediante el dispositivo: + +```text +/dev/serial0 +``` + +Las mediciones serán almacenadas periódicamente en el archivo: + +```text +MHZ19B.json +``` + +Este archivo será utilizado posteriormente por la interfaz web. + +--- + +# Visualización del Dashboard + +Obtener la dirección IP de la Raspberry Pi: + +```bash +hostname -I +``` + +Abrir un navegador web desde cualquier dispositivo conectado a la misma red local e ingresar: + +```text +http:// +``` +![Pagina resultante con las mediciones](InterfazWeb.png) + +La interfaz web consultará periódicamente el archivo **MHZ19B.json** mediante JavaScript utilizando peticiones asíncronas (`fetch()`), mostrando en tiempo real la concentración de CO₂ y el estado de la calidad del aire. + +--- + +# Arquitectura General del Sistema + +```mermaid + graph TD + %% Sensor físico + A[ Sensor MH-Z19B] -->|UART 9600 baudios| B + + %% Placa/Hardware + B{{ Raspberry Pi 4}} --> C + + %% Proceso de Software + C( Driver en C) -->|Escribe datos| D + + %% Base de datos / Archivo + D[( MHZ19B.json)] -.->|Lectura local| E + + %% Servidor Web + E[[ Servidor Nginx]] -->|HTTP / Fetch API| F + + %% Nube / Web Dashboard + F(( Dashboard Web / Cliente)) +``` +--- + +# Conclusión + +El uso del protocolo UART implica un gran desafío debido a su necesidad de programación a bajo nivel en C. Durante la fase de depuración se logró identificar que dejar que el sistema operativo controle los tiempos lo hace muy propenso a fallas de sincronización. Esto se evidenció en la recepción de "basura" hexadecimal, la captura de tramas incompletas de apenas 3 bytes, el congelamiento del sistema por saturación de peticiones y un retraso de 6 segundos entre la consola y la interfaz gráfica. + +A pesar de esto, UART sigue siendo uno de los pilares dentro de los sistemas embebidos por ser una herramienta directa, robusta y útil. No solo permite una transmisión de datos confiable en tiempo real, sino que otorga el control absoluto sobre el hardware. + +# Limitaciones + +Aunque este mini proyecto cumple su objetivo de leer el CO₂ y probar la comunicación UART, tiene las siguientes limitaciones prácticas que debemos tomar en cuenta: + +- **Tiempos no exactos:** La Raspberry Pi funciona como una computadora normal y no está diseñada para controlar el tiempo con una precisión matemática perfecta. Esto hace que las pausas para comunicarse con el sensor tengan ligeras variaciones. +- **El programa se "pausa" a esperar:** Actualmente, el código en C se detiene por completo hasta que el sensor responde y se guarda el archivo. Esto funciona bien para un solo sensor, pero si en el futuro queremos conectar varios a la vez, el sistema se volvería lento porque tendría que esperar a uno por uno en lugar de atenderlos al mismo tiempo. +- **Consumo innecesario en la página web:** El panel web descarga el archivo completo cada dos segundos, haya o no haya datos nuevos. Para la versión final de la estación, lo ideal sería usar un método más inteligente donde la página se actualice *solo* cuando el nivel de CO₂ cambie, para no desperdiciar recursos ni hacer trabajar a la placa de más. \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..de96763 --- /dev/null +++ b/index.html @@ -0,0 +1,92 @@ + + + + + + Monitor de Calidad del Aire + + + + +
+

Monitoreo de CO₂ (MH-Z19B)

+
Concentración actual en el ambiente:
+ +
-- ppm
+ +
Calculando estado...
+
+ + + + +