diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 44f00a5..276c508 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1,336 +1,323 @@ -# System Architecture — EZO RTD Temperature Monitor +# Especificación de Arquitectura del Sistema +## Sistema de Monitoreo de Parámetros Fisicoquímicos para Fotobiorreactor -> **Purpose of this document:** Provide a complete architectural snapshot of the project so that any contributor (or AI assistant) can understand the full system — hardware, firmware, data pipeline, and frontend — without reading every source file individually. +**Revisión:** 2.0 +**Estado:** Vigente --- -## 1. System Overview +## 1. Visión General del Sistema -The system is a **single-node embedded IoT monitoring platform** composed of four layers: +El sistema implementa una arquitectura de cuatro capas desacopladas que operan de forma asíncrona sobre una red local. El flujo de datos se inicia en el hardware físico del sensor (bus I²C) y se propaga hasta el navegador web del operador mediante una cadena de transformaciones bien definidas. En la fase actual de desarrollo, la capa de hardware es emulada por un servidor de simulación (*mock*) Node.js que reproduce fielmente las latencias y los formatos de respuesta de los módulos Atlas Scientific EZO. + +### 1.1. Diagrama de Capas ``` -┌─────────────────────────────────────────────────────────────┐ -│ Layer 4 — Presentation index.html / index-css.html │ -│ Browser polls JSON via HTTP every 1 s │ -├─────────────────────────────────────────────────────────────┤ -│ Layer 3 — Web Server Nginx (static file server) │ -│ Serves HTML, SVG assets, and JSON data files │ -├─────────────────────────────────────────────────────────────┤ -│ Layer 2 — Backend Daemon ezortd_daemon (C binary) │ -│ Reads I²C → writes JSON + appends CSV │ -├─────────────────────────────────────────────────────────────┤ -│ Layer 1 — Hardware / HAL EZO-RTD via I²C on RPi │ -│ Atlas Scientific ISCCB-2 @ 0x66, I²C bus 1 │ -└─────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────┐ +│ CAPA 4 — PRESENTACIÓN │ +│ Navegador web (HTML5, CSS3, Vanilla JS ES6+, Chart.js, SheetJS) │ +│ Polling cada 1000 ms → GET /api/sensors/:type │ +│ Envío de comandos → POST /api/sensors/:type/command │ +├──────────────────────────────────────────────────────────────────────┤ +│ CAPA 3 — ENRUTAMIENTO Y PROXY │ +│ Nginx, puerto 8888 │ +│ - Entrega de archivos estáticos del frontend (HTML, CSS, JS) │ +│ - Proxy inverso transparente: /api/* → http://127.0.0.1:3000 │ +├──────────────────────────────────────────────────────────────────────┤ +│ CAPA 2 — LÓGICA DE NEGOCIO Y API │ +│ Node.js + Express, puerto 3000 │ +│ - GET /api/sensors/:type → 60 registros simulados, latencia 400 ms │ +│ - POST /api/sensors/:type/command → parser léxico EZO completo │ +├──────────────────────────────────────────────────────────────────────┤ +│ CAPA 1 — ADQUISICIÓN DE DATOS (HARDWARE / SIMULADO) │ +│ Demonios C nativos en Raspberry Pi (producción futura) │ +│ Lectura por I²C: /dev/i2c-1 @ 100 kHz │ +│ Módulos: EZO-RTD (0x66), EZO-pH (0x63), EZO-DO (0x61), EZO-EC (0x64)│ +└──────────────────────────────────────────────────────────────────────┘ ``` -**Host board:** Raspberry Pi (any model with 40-pin GPIO; developed on Raspberry Pi Zero or similar) -**OS:** Raspberry Pi OS (Debian-based, Linux kernel) -**Language:** C (backend), HTML/CSS/JavaScript (frontend) -**IPC mechanism:** File system — JSON files in `data/` act as the shared state between daemon and web server - --- -## 2. Hardware Layer +## 2. Topología de Red Local y Flujo de Datos Asíncrono -### 2.1 Sensor — Atlas Scientific EZO-RTD™ (ISCCB-2) +### 2.1. Topología -The EZO-RTD is a signal-conditioning circuit for Pt-100 or Pt-1000 RTD probes. It digitizes the analog RTD signal internally and exposes a clean digital interface via I²C or UART. - -| Property | Value | -|---|---| -| Module | Atlas Scientific ISCCB-2 | -| Protocol in use | I²C | -| I²C Address | `0x66` (fixed, factory default) | -| I²C Bus | Bus 1 (`/dev/i2c-1`) | -| Supply voltage | 3.3 V | -| Read command | ASCII string `"R"` (1 byte) | -| Response time | ~1000 ms after issuing `"R"` | -| Response format | Byte 0: status code (`0x01` = success); Bytes 1–N: null-terminated ASCII float | -| Resolution | 0.001 °C | - -### 2.2 Physical Wiring +El sistema opera exclusivamente en una red de área local (LAN). Un único nodo Raspberry Pi actúa como servidor de todos los servicios. Los clientes son navegadores web en la misma red. No existe comunicación hacia redes externas en la configuración de producción. ``` -EZO-RTD (ISCCB-2) Raspberry Pi 40-pin Header -───────────────── ────────────────────────── -VCC ────────────────────── Pin 1 (3.3 V) -GND ────────────────────── Pin 6 (GND) -SDA ────────────────────── Pin 3 (GPIO2, I²C1 SDA) -SCL ────────────────────── Pin 5 (GPIO3, I²C1 SCL) -OFF ──── not connected + [Navegador del operador] + │ + │ HTTP, puerto 8888 + ▼ + [Nginx — Proxy Inverso] + │ │ + │ Estático │ /api/* → proxy + ▼ ▼ + [frontend/] [Node.js Express — puerto 3000] + │ + │ Simula protocolo I²C EZO (fase actual) + │ ───────────────────────────────────── + │ Producción futura: lectura real de + │ archivos JSON escritos por demonios C + ▼ + [data/EZORTD.json] + [data/EZOPH.json ] ← escritos por demonios C + [data/EZODO.json ] (Capa 1 / hardware) + [data/EZOEC.json ] ``` -> The `OFF` pin (active-low sleep input) is left floating/unconnected; the circuit is permanently powered. Future implementations may drive this pin from a GPIO to enable power management. +### 2.2. Flujo de Datos Asíncrono — Lectura en Vivo -### 2.3 I²C Bus Configuration - -- **Bus:** `/dev/i2c-1` -- **Speed:** 100 kHz (standard mode; the EZO-RTD supports up to 400 kHz Fast Mode) -- **Pull-ups:** Provided internally by the Raspberry Pi (1.8 kΩ on SDA/SCL lines) -- **Enable:** `sudo raspi-config` → Interface Options → I2C → Enable - ---- - -## 3. Firmware / HAL Layer (C) - -### 3.1 Source Files - -| File | Role | -|---|---| -| `sensors/EZORTD/ezortd.h` | Public API: I²C address constant, `getTemperature()` declaration | -| `sensors/EZORTD/ezortd.c` | `getTemperature()` implementation | -| `sensors/EZORTD/main.c` | One-shot binary: single read → JSON to stdout | -| `sensors/EZORTD/ezortd_daemon.c` | Daemon: infinite loop, writes JSON + CSV every second | -| `sensors/EZORTD/Makefile` | Builds `EZORTD` binary (one-shot target) | - -### 3.2 `getTemperature()` — Protocol Detail - -```c -// Signature -int getTemperature(int fd, double *temperature); -// Returns 0 on success, -1 on error +``` +1. El navegador ejecuta setInterval(updateDashboard, 1000) +2. dashboard.js llama a fetch('/api/sensors/rtd'), fetch('/api/sensors/ph'), etc. +3. Las peticiones alcanzan Nginx en el puerto 8888 +4. Nginx reenvía /api/* → http://127.0.0.1:3000/api/* +5. Express evalúa el parámetro :type y genera 60 registros simulados +6. El servidor responde con un arreglo JSON tras una latencia de 400 ms +7. dashboard.js parsea la respuesta, actualiza el DOM y evalúa alarmas +8. renderAlarmSummary() actualiza el resumen de riesgo global ``` -**Communication sequence:** +### 2.3. Flujo de Datos Asíncrono — Comando EZO (Consola de Hardware) ``` -1. ioctl(fd, I2C_SLAVE, 0x66) — select slave address -2. write(fd, "R", 1) — issue read command (ASCII 'R') -3. usleep(1000000) — wait 1000 ms for conversion -4. read(fd, response, 32) — read up to 32 bytes -5. Check response[0] == 0x01 — status byte: 0x01 = success -6. atof(&response[1]) — parse ASCII float from bytes 1..N +1. El operador selecciona el sensor y emite un comando desde la interfaz +2. mock-service.js ejecuta: POST /api/sensors/:type/command + Body: { "command": "cal,mid,7.00" } +3. Nginx reenvía la petición al puerto 3000 +4. El parser léxico de Express descompone el comando por comas: + parts = ["cal", "mid", "7.00"], baseCmd = "cal" +5. Se aplica la latencia de procesamiento químico correspondiente (600 ms para CAL) +6. El servidor responde: { "success": true, "sensor": "PH", "response": "*OK" } +7. mock-service.js imprime el intercambio TX/RX en el terminal virtual de la UI ``` -**Error codes in `response[0]`:** - -| Code | Meaning | -|---|---| -| `0x01` | Success | -| `0x02` | Syntax error in command | -| `0xFF` | No data (not ready) | -| `0xFE` | Pending (not ready yet) | - -### 3.3 One-Shot Executable (`EZORTD`) +--- -Compiled from `main.c + ezortd.c`. Opens `/dev/i2c-1`, calls `getTemperature()` once, prints result as JSON to stdout, exits. +## 3. Especificación de la API REST -```sh -./EZORTD -{ "temperature": 24.414 } -``` +### 3.1. Endpoint de Telemetría -Used for testing and for cron-based updates. +**`GET /api/sensors/:type`** -### 3.4 Daemon (`ezortd_daemon`) +Parámetros de ruta: -Compiled from `ezortd_daemon.c + ezortd.c`. Runs an infinite loop with 1-second sleep between iterations. On each iteration: +| Parámetro | Valores válidos | Descripción | +|---|---|---| +| `type` | `rtd`, `ph`, `do`, `ec`, `all` | Identificador del módulo sensor | -1. Reads temperature via `getTemperature()` -2. Overwrites `data/EZORTD.json` with current value -3. Appends a Unix-timestamp + temperature row to `logs/temperature.csv` +Latencia simulada: 400 ms (emula el tiempo de procesamiento I²C más la conversión del ADC interno del EZO). -**Output paths** (hardcoded in source — update before compiling): +Respuesta exitosa — tipo específico (HTTP 200): -```c -// JSON -"/home/cristian/airquality/basic-ui-dashboard/data/EZORTD.json" -// CSV log -"/home/cristian/airquality/basic-ui-dashboard/logs/temperature.csv" +```json +[ + { + "Timestamp": "2025-05-30T14:22:01.000Z", + "Sensor": "RTD", + "Valor": "25.04" + }, + ... +] ``` -**JSON output format:** +Respuesta exitosa — tipo `all` (HTTP 200): ```json -{ "temperature": 24.414 } +[ + { + "Timestamp": "2025-05-30T14:22:01.000Z", + "Temperatura_C": "25.04", + "pH": "7.21", + "DO_mgL": "8.52", + "EC_uS": "1048" + }, + ... +] ``` -**CSV output format:** +Respuesta de error — tipo no reconocido (HTTP 400): -``` -timestamp,temperature ← header row (from EZORTD.csv template) -1717027200,24.414 ← Unix epoch (seconds), float to 3 decimal places +```json +{ "error": "Sensor no válido" } ``` ---- +### 3.2. Endpoint de Comandos EZO + +**`POST /api/sensors/:type/command`** -## 4. Data Pipeline +Cuerpo de la petición (`Content-Type: application/json`): +```json +{ "command": "" } ``` -[EZO-RTD sensor] - │ - │ I²C (0x66, /dev/i2c-1) - ▼ -[ezortd_daemon — C process] - │ - ├──► data/EZORTD.json (overwrite, every 1 s) - │ { "temperature": 24.414 } - │ - └──► logs/temperature.csv (append, every 1 s) - 1717027200,24.414 - │ - ▼ - [Future: historical chart] - -[Nginx — static file server] - │ - │ HTTP GET ./data/EZORTD.json - ▼ -[Browser — index.html] - │ - │ setInterval(fetchData, 1000) - ▼ -[DOM update — temp-value element] + +Respuesta exitosa (HTTP 200): + +```json +{ + "success": true, + "sensor": "PH", + "command": "cal,mid,7.00", + "response": "*OK" +} ``` +### 3.3. Tabla de Comandos EZO Soportados por el Parser Léxico + +| Comando base | Aplica a | Latencia simulada | Respuesta representativa | +|---|---|---|---| +| `i` | Todos | 300 ms | `?I,RTD,2.12` | +| `status` | Todos | 300 ms | `?STATUS,P,5.03` | +| `r` | Todos | 900 ms | `25.047` / `7.22` / `8.51` / `1052` | +| `cal` | Todos | 600 ms | `*OK` / `?CAL,1` | +| `sleep` | Todos | 0 ms | `[SLEEP MODE ACTIVADO]` | +| `factory` | Todos | 800 ms | `*OK` | +| `find` | Todos | 300 ms | `*OK` | +| `led` | Todos | 300 ms | `?LED,1` / `*OK` | +| `plock` | Todos | 300 ms | `?PLOCK,1` / `*OK` | +| `i2c` | Todos | 300 ms | `*OK` | +| `t`, `s`, `p` | Todos | 300 ms | `?T,25.0` / `*OK` | +| `slope` | PH | 300 ms | `?Slope,99.7,100.3,-0.89` | +| `k` | EC | 300 ms | `?K,1.0` / `*OK` | +| `tc` | EC | 300 ms | `?TC,1.90` / `*OK` | +| `o` | EC | 300 ms | `?O,EC,TDS,S,SG` / `*OK` | +| Desconocido | Todos | 300 ms | `*ER` | + --- -## 5. Web Server Layer +## 4. Especificación Técnica de la Capa de Hardware -**Server:** Nginx (static file serving only — no application logic) +### 4.1. Protocolo de Comunicación I²C con Módulos EZO -**Document root:** Set to the repository root directory in `/etc/nginx/sites-available/default`: +Todos los módulos EZO de Atlas Scientific implementan el protocolo I²C con la siguiente secuencia de operación para una lectura (comando `R`): -```nginx -root /home/pi/; +``` +1. ioctl(fd, I2C_SLAVE, ADDR) — seleccionar dirección del esclavo +2. write(fd, "R", 1) — emitir el comando de lectura (ASCII 0x52) +3. usleep(1000000) — esperar 1000 ms para conversión analógica interna +4. read(fd, response, 32) — leer la respuesta del módulo +5. Verificar response[0] == 0x01 — código de estado: 0x01 indica éxito +6. atof(&response[1]) — parsear el float ASCII desde el byte 1 en adelante ``` -**Served assets:** +Tabla de códigos de estado en `response[0]`: -| Path | Description | +| Código | Significado | |---|---| -| `/` or `/index.html` | Minimal temperature dashboard | -| `/index-css.html` | Extended multi-sensor dashboard | -| `/data/EZORTD.json` | Live temperature JSON (written by daemon) | -| `/images/*.svg` | Ionicons SVG assets (thermometer, water, cloud, sun) | +| `0x01` | Éxito — dato válido disponible | +| `0x02` | Error de sintaxis en el comando | +| `0xFE` | Pendiente — conversión no completada | +| `0xFF` | Sin datos disponibles | -No server-side scripting is used. The daemon writes files to disk; Nginx serves them as-is. +### 4.2. Mapa de Direcciones I²C del Bus ---- +| Módulo | Dirección I²C | Constante en código fuente | +|---|---|---| +| EZO-RTD | `0x66` | `EZORTD_I2C_ADDR` | +| EZO-pH | `0x63` | `EZOPH_I2C_ADDR` | +| EZO-DO | `0x61` | `EZODO_I2C_ADDR` | +| EZO-EC | `0x64` | `EZOEC_I2C_ADDR` | -## 6. Frontend Layer +Ninguna dirección colisiona en el espacio de 7 bits del protocolo I²C estándar. El bus opera en modo maestro único (*single-master*), lo que elimina la necesidad de arbitraje. -### 6.1 `index.html` — Minimal Dashboard +--- -Single-sensor display. Pure HTML + inline CSS + vanilla JS. +## 5. Arquitectura de Hardware Propuesta — Fase 9: Shield PCB con Aislamiento Galvánico -**Data source:** `GET ./data/EZORTD.json` +### 5.1. Justificación Técnica del Aislamiento Galvánico -**Polling interval:** 1000 ms (`setInterval`) +La operación de sensores electroquímicos en un medio líquido conductor presenta una condición de riesgo inherente: la existencia de **corrientes parásitas de bucle de masa** (*ground loop currents*). Este fenómeno se produce cuando dos o más sensores sumergidos en el mismo líquido establecen caminos de retorno de corriente a través del propio medio líquido, creando diferencias de potencial espurias entre sus masas de referencia. -**DOM targets:** +Las consecuencias operativas de esta condición son las siguientes: -| Element ID | Content | -|---|---| -| `temp-value` | `data.temperature.toFixed(3) + " ℃"` | -| `last-update` | `"Updated: " + new Date().toLocaleTimeString()` | +**Para el módulo EZO-pH:** La sonda de pH opera mediante la detección de una diferencia de potencial electroquímico (típicamente entre −414 mV y +414 mV para el rango de 0 a 14 pH) generada en el electrodo de vidrio. Una corriente parásita que atraviese el medio líquido introduce un potencial de interferencia directamente sumado a esta señal de alta impedancia, produciendo lecturas de pH sistemáticamente desplazadas y no reproducibles. -### 6.2 `index-css.html` — Extended Dashboard +**Para el módulo EZO-EC:** La medición de conductividad eléctrica se realiza mediante la inyección de una señal de corriente alterna de frecuencia controlada a través de electrodos de acero inoxidable o platino sumergidos. La presencia de corrientes parásitas de corriente continua procedentes de otros sistemas altera la conductividad aparente del medio, introduciendo errores proporcionales a la magnitud de la corriente de interferencia. -Multi-sensor card layout using Roboto (Google Fonts), flexbox, and CSS animations. +**Para el módulo EZO-DO:** Aunque la sonda de oxígeno disuelto es en términos eléctricos menos sensible que el electrodo de pH, las corrientes parásitas pueden inducir polarización electrolítica en los electrodos de platino de las sondas galvánicas, degradando irreversiblemente la membrana de politetrafluoroetileno (PTFE) y alterando la cinética de reducción del oxígeno. -**Data sources (partially commented out):** +### 5.2. Solución de Diseño: Aisladores Digitales I²C y Convertidores DC-DC Aislados -```javascript -const res = await Promise.all([ - // fetch("./data/BH1750.json"), // light — pending - // fetch("./data/BMP180.json"), // pressure — pending - fetch("./data/HTU21D.json"), // temp + humidity (legacy sensor, active) - // fetch("./data/MH_Z19.json") // CO₂ — pending -]); -``` +La arquitectura de la PCB propuesta para la Fase 9 implementa una barrera galvánica completa en cada canal de sensor mediante dos componentes: -**Polling interval:** 6000 ms +**Aislador digital bidireccional I²C — Texas Instruments ISO1540:** -**DOM targets:** `temp-value`, `humidity-value`, `co2-value`, `pressure-value` +El ISO1540 es un aislador de capacitancia de silicio que implementa los canales SDA y SCL del bus I²C de forma completamente aislada, con una rigidez dieléctrica de 2500 V RMS. El dispositivo detecta el estado lógico de cada línea en el lado primario (Raspberry Pi) y reproduce la señal en el lado secundario (módulo EZO) sin conexión eléctrica directa. Sus características operativas relevantes son: ---- +- Velocidad máxima de transferencia: 1 Mbps (compatible con el modo *Fast-mode Plus* de I²C) +- Corriente de cortocircuito de salida: ±4 mA (compatible con los pull-ups del bus) +- Tiempo de propagación: < 15 ns +- Consumo en standby: < 1 mA por canal -## 7. Key Constants and Configuration +**Convertidor DC-DC aislado — Mornsun B0303S-1W (o equivalente):** -| Constant | Value | Location | -|---|---|---| -| EZO-RTD I²C address | `0x66` | `sensors/EZORTD/ezortd.h` | -| HTU21D I²C address | `0x40` | `sensors/HTU21D/htu21d.h` | -| I²C bus device | `/dev/i2c-1` | `sensors/EZORTD/main.c`, `ezortd_daemon.c` | -| Sensor read delay | 1,000,000 µs (1 s) | `sensors/EZORTD/ezortd.c` | -| Daemon loop interval | `sleep(1)` — 1 s | `sensors/EZORTD/ezortd_daemon.c` | -| JSON output path | `/home/cristian/…/data/EZORTD.json` | `ezortd_daemon.c` (**update per deployment**) | -| CSV log path | `/home/cristian/…/logs/temperature.csv` | `ezortd_daemon.c` (**update per deployment**) | -| Frontend poll interval (primary) | 1000 ms | `index.html` | -| Frontend poll interval (extended) | 6000 ms | `index-css.html` | -| Nginx document root | `/home/pi/` | `/etc/nginx/sites-available/default` | +El ISO1540 requiere dos dominios de alimentación físicamente separados: VCC1 (lado Raspberry Pi, 3.3 V) y VCC2 (lado módulo EZO). Si ambos dominios comparten la misma referencia de tierra, la barrera galvánica del aislador digital resulta inoperante, ya que el bucle de masa se cierra a través del plano de tierra compartido de la PCB. ---- +El B0303S-1W es un convertidor DC-DC de 1 W, entrada 3.3 V, salida 3.3 V, con aislamiento galvánico de 1500 V DC entre sus terminales de entrada y salida. Su interposición entre el plano de tierra de la Raspberry Pi y el plano de tierra de cada módulo EZO garantiza que no exista ningún camino eléctrico directo entre ambos dominios, eliminando efectivamente el bucle de masa. -## 8. Build System +### 5.3. Esquema Conceptual del Aislamiento por Canal -### EZO RTD +``` + Dominio Raspberry Pi (GND_PI) Dominio Sensor (GND_EZO) + ───────────────────────────── ──────────────────────── + 3.3V ──┬─────────────────────────────── VCC_in (B0303S) ──► VCC_out → VCC_EZO + │ │ │ + │ [BARRERA 1500V] │ + │ GND_EZO (flotante respecto a GND_PI) + │ + ├── SDA_PI ──► ISO1540 ──────────────────────────────────► SDA_EZO + │ [BARRERA 2500V RMS] + └── SCL_PI ──► ISO1540 ──────────────────────────────────► SCL_EZO +``` -```makefile -CC=gcc -CFLAGS=-I. -OBJ=main.o ezortd.o +Este esquema se replica de forma independiente para cada uno de los cuatro módulos EZO, garantizando el aislamiento no solo respecto a la Raspberry Pi sino también entre los sensores entre sí, lo que elimina por completo los caminos de corriente parásita a través del medio líquido del fotobiorreactor. -%.o: %.c - $(CC) -c -o $@ $< $(CFLAGS) +--- -EZORTD: $(OBJ) - $(CC) -o $@ $^ $(CFLAGS) -``` +## 6. Sistema de Evaluación de Alarmas -No external libraries required. Uses only standard Linux I²C headers: ``, ``. +### 6.1. Lógica de Evaluación -### HTU21D (legacy) +El módulo `evaluateSensorAlarm()` de `dashboard.js` implementa una máquina de estados de cuatro niveles para cada sensor, evaluada en cada ciclo de actualización (1000 ms): -```makefile -EXTRA_LIBS=-li2c +``` +Estado OFFLINE: JSON faltante, HTTP error, valor no numérico +Estado CRITICAL: valor < limits.min OR valor > limits.max +Estado WARNING: valor dentro del rango, pero dentro del 10% de los límites +Estado NORMAL: valor dentro del rango, fuera de la banda de advertencia ``` -Requires `libi2c` (`sudo apt install libi2c-dev`). Uses SMBus wrappers from ``. - ---- - -## 9. Legacy Sensor — HTU21D +La banda de advertencia se calcula como: -Retained in `sensors/HTU21D/` for reference and backward compatibility with `index-css.html`. +``` +margen_advertencia = (limits.max - limits.min) × WARNING_MARGIN_RATIO +WARNING si: valor ≤ (limits.min + margen_advertencia) OR + valor ≥ (limits.max - margen_advertencia) +``` -| Property | Value | -|---|---| -| I²C Address | `0x40` | -| Measurements | Temperature + Relative Humidity | -| Temperature formula | `T = -46.85 + 175.72 × raw / 65536.0` | -| Humidity formula | `RH = -6 + 125 × raw / 65536.0` | -| Library dependency | `libi2c` | -| Command | SMBus block read (`0xE3` temp, `0xE5` humidity) | +donde `WARNING_MARGIN_RATIO = 0.10` (configurable en `dashboard.js`). ---- +### 6.2. Umbrales Operativos Configurados -## 10. Planned Sensors (Not Yet Integrated) +Definidos en `config/alarms.json`: -| Sensor | Measurement | Interface | Data file | +| Variable | Mínimo | Máximo | Unidad | |---|---|---|---| -| BH1750 | Ambient light (lux) | I²C | `data/BH1750.json` | -| BMP581 | Atmospheric pressure (hPa) | I²C | `data/BMP581.json` | -| ZMOD4410 | TVOC / IAQ index | I²C | `data/ZMOD4410.json` | +| Temperatura | 20.0 | 30.0 | °C | +| pH | 6.8 | 7.5 | pH | +| Oxígeno Disuelto | 4.0 | 12.0 | mg/L | +| Conductividad Eléctrica | 500 | 2500 | µS/cm | --- -## 11. File I/O Summary +## 7. Módulo de Exportación de Datos -| File | Writer | Reader | Format | Update Rate | -|---|---|---|---|---| -| `data/EZORTD.json` | `ezortd_daemon` | Nginx → browser | JSON object | 1 s | -| `logs/temperature.csv` | `ezortd_daemon` | Browser historical chart | CSV (`timestamp,value`) | 1 s (append) | -| `data/HTU21D.json` | `HTU21D` binary (manual / cron) | Nginx → browser | JSON object | On demand | -| `data/EZORTD.csv` | (template only — unused) | — | CSV | — | +### 7.1. Exportación CSV ---- +La función `handleExport(sensorType)` en `export-service.js` recupera el historial mediante `fetchHistoricalData()`, convierte el arreglo de objetos JSON a texto CSV mediante `convertToCSV()` y fuerza la descarga del archivo en el navegador mediante un enlace temporal con `URL.createObjectURL()`. -## 12. Security and Deployment Notes +### 7.2. Exportación XLSX Multipagina -- The Nginx instance serves on port 80 with no authentication. Suitable for a local LAN network only. -- The daemon runs as root (required for I²C access unless the user is added to the `i2c` group). Use `sudo usermod -aG i2c $USER` to avoid running as root. -- File paths in `ezortd_daemon.c` are hardcoded and must be updated per deployment before compilation. -- No input validation is performed on the JSON data in the browser; malformed JSON causes a silent catch/error in the console. +La función `handleExportExcel()` en `export-excel.js` itera sobre los cuatro sensores, recupera los datos de cada uno, crea una hoja de cálculo independiente con `XLSX.utils.json_to_sheet()` y las consolida en un único libro de trabajo (`Workbook`) mediante `XLSX.utils.book_append_sheet()`. El archivo binario `.xlsx` se descarga mediante `XLSX.writeFile()`. \ No newline at end of file diff --git a/PROJECT_STATUS.md b/PROJECT_STATUS.md deleted file mode 100644 index 9734f16..0000000 --- a/PROJECT_STATUS.md +++ /dev/null @@ -1,229 +0,0 @@ -# Project Status - Photobioreactor Dashboard v1.0 + Phase 3 - -## Estado general - -Este repositorio contiene un sistema de monitoreo local para un fotobiorreactor basado en sensores Atlas Scientific EZO. La arquitectura actual separa la captura de datos en C, los archivos de intercambio en `data/`, los historiales CSV en `logs/` y un dashboard web estatico en `frontend/`, pensado para desplegarse posteriormente en Raspberry Pi con Nginx. - -## Arquitectura actual - -El flujo principal del sistema es: - -1. Los sensores Atlas Scientific se comunican por I2C con la Raspberry Pi. -2. Los programas en `sensors/` leen cada circuito EZO. -3. Cada lectura actual se escribe como JSON en `data/`. -4. Los historiales se almacenan como CSV en `logs/`. -5. Nginx sirve el repositorio como contenido estatico. -6. `frontend/index.html` carga `dashboard.css`, Chart.js y `dashboard.js`. -7. El navegador consulta los JSON cada segundo y actualiza las lecturas actuales. -8. La seccion `Alarm Summary` evalua umbrales desde `config/alarms.json`. -9. La seccion `Historical Trends` consulta los CSV cada 10 segundos y actualiza las graficas. - -No hay backend web ni base de datos en esta version. El filesystem funciona como interfaz entre los daemons de sensores y el frontend. - -## Sensores y archivos de datos actuales - -| Sensor | Variable | Archivo JSON | Unidad | -|---|---|---|---| -| EZO-RTD | Temperatura | `data/EZORTD.json` | degC | -| EZO-pH | pH | `data/EZOPH.json` | pH | -| EZO-DO | Oxigeno disuelto | `data/EZODO.json` | mg/L | -| EZO-EC | Conductividad | `data/EZOEC.json` | uS/cm | - -## Archivos de historial - -| Variable | Archivo CSV | Grafica | -|---|---|---| -| Temperatura | `logs/temperature.csv` | Temperature vs Time | -| pH | `logs/ph.csv` | pH vs Time | -| Oxigeno disuelto | `logs/do.csv` | Dissolved Oxygen vs Time | -| Conductividad | `logs/ec.csv` | Conductivity vs Time | - -Formato CSV soportado: - -```csv -timestamp,value -1717027200,25.488 -``` - -El frontend tambien acepta timestamps ISO 8601 en la primera columna. - -## Archivos utilizados - -| Ruta | Proposito | -|---|---| -| `frontend/index.html` | Estructura del dashboard, seccion `System Status` y seccion `Historical Trends`. | -| `frontend/dashboard.css` | Estilos responsive para metricas, estado del sistema y graficas. | -| `frontend/dashboard.js` | Lectura periodica de JSON, validacion de datos, lectura CSV y actualizacion de Chart.js. | -| `data/EZORTD.json` | Lectura actual de temperatura. | -| `data/EZOPH.json` | Lectura actual de pH. | -| `data/EZODO.json` | Lectura actual de oxigeno disuelto. | -| `data/EZOEC.json` | Lectura actual de conductividad. | -| `logs/temperature.csv` | Historial de temperatura para graficas. | -| `logs/ph.csv` | Historial de pH para graficas. | -| `logs/do.csv` | Historial de oxigeno disuelto para graficas. | -| `logs/ec.csv` | Historial de conductividad para graficas. | -| `config/sensors.json` | Configuracion declarativa de sensores Atlas Scientific. | -| `config/alarms.json` | Umbrales configurables para estados NORMAL, WARNING y CRITICAL. | -| `sensors/EZORTD/` | Codigo C existente para lectura EZO-RTD. | -| `sensors/EZOPH/` | Codigo C existente para lectura EZO-pH. | -| `sensors/EZODO/` | Codigo C existente para lectura EZO-DO. | -| `sensors/EZOEC/` | Codigo C existente para lectura EZO-EC. | - -## Funciones implementadas en v1.0 - -- Dashboard profesional para monitoreo del fotobiorreactor. -- Lectura de temperatura, pH, oxigeno disuelto y conductividad. -- Actualizacion automatica de lecturas actuales cada 1 segundo. -- Fecha y hora de ultima actualizacion. -- Validacion independiente por sensor. -- Estado `OFFLINE` cuando un JSON no existe, no responde, contiene JSON invalido o no incluye un valor numerico valido. -- Estado global del sistema: `ALL SYSTEMS ONLINE`, `PARTIAL DATA` o `SYSTEM OFFLINE`. -- Seccion inferior `System Status`. -- Conteo de sensores activos y sensores offline. -- Diseno responsive para escritorio, tablet y movil. - -## Funciones implementadas en Phase 2 - Historical Trends - -- Nueva seccion `Historical Trends` debajo de `System Status`. -- Integracion de Chart.js para graficas de linea. -- Cuatro graficas independientes: - - Temperature vs Time. - - pH vs Time. - - Dissolved Oxygen vs Time. - - Conductivity vs Time. -- Lectura de historiales desde `logs/temperature.csv`, `logs/ph.csv`, `logs/do.csv` y `logs/ec.csv`. -- Actualizacion automatica de graficas cada 10 segundos. -- Estado visual `No historical data available` cuando un CSV no existe, esta vacio o no contiene datos numericos validos. -- Parser CSV reutilizable con soporte principal para `timestamp,value` y compatibilidad adicional con encabezados como `time`, `date`, nombres de variable y `reading`. -- Las instancias Chart.js se conservan en memoria y las actualizaciones cambian datasets existentes con `chart.update("none")`. -- Configuracion Chart.js para dashboard cientifico: `responsive: true`, `maintainAspectRatio: false` y `animation: false`. -- Funciones reutilizables para futuras graficas: - - `readHistoricalData()` - - `parseHistoricalCsv()` - - `buildChartDataset()` - - `getChartOptions()` - - `renderHistoricalChart()` - - `updateHistoricalTrends()` -- Diseno responsive para dos columnas en escritorio y una columna en movil. - -## Funciones implementadas en Phase 3 - Alarmas y umbrales configurables - -- Archivo `config/alarms.json` normalizado con umbrales por variable: - - Temperatura: 20 a 30 degC. - - pH: 6.8 a 7.5. - - Oxigeno disuelto: 4.0 a 12.0 mg/L. - - Conductividad: 500 a 2500 uS/cm. -- El frontend lee `../config/alarms.json` durante el ciclo de actualizacion. -- Cada sensor se evalua como: - - `NORMAL`: valor dentro del rango y fuera de la banda de advertencia. - - `WARNING`: valor dentro del rango, pero cerca de `min` o `max`. - - `CRITICAL`: valor por debajo de `min` o por encima de `max`. - - `OFFLINE`: JSON faltante, invalido o sin valor numerico. -- La banda `WARNING` usa el 10% del ancho del rango configurado, definido en `WARNING_MARGIN_RATIO`. -- Las tarjetas cambian visualmente por estado: - - Verde para `NORMAL`. - - Amarillo para `WARNING`. - - Rojo para `CRITICAL`. - - Gris para `OFFLINE`. -- Nueva seccion `Alarm Summary` con: - - `Active Critical Alarms`. - - `Active Warnings`. - - `Offline Sensors`. - - `Overall Risk Level`. -- Lista activa de sensores en alarma u offline. -- Estructura de eventos de alarma preparada para integraciones futuras: - - `sensorId` - - `sensorName` - - `severity` - - `value` - - `message` - - `createdAt` - -## Arquitectura de alarmas - -El flujo de alarmas es: - -1. `dashboard.js` lee los JSON de sensores desde `data/`. -2. `dashboard.js` lee los umbrales desde `config/alarms.json`. -3. `evaluateSensorAlarm()` compara cada valor contra `min` y `max`. -4. `renderSystemStatus()` actualiza el estado general. -5. `renderAlarmSummary()` actualiza conteos, riesgo global y lista de alarmas. - -La logica queda encapsulada para que una fase posterior pueda enviar los eventos generados por `getAlarmEvents()` a correo, Telegram o MQTT sin acoplar esas salidas al renderizado visual. - -## Dependencias nuevas - -| Dependencia | Uso | Carga | -|---|---|---| -| Chart.js | Renderizado de graficas historicas de linea | CDN en `frontend/index.html` | - -Nota de despliegue: Chart.js por CDN requiere conectividad desde el navegador. Para una Raspberry Pi aislada o una red local sin internet, se recomienda descargar una copia local versionada y servirla desde `frontend/vendor/`. - -## Nomenclatura de historiales CSV - -Se detecto una inconsistencia entre `logs/temp.csv` y `logs/temperature.csv`: - -- `frontend/dashboard.js` consume `logs/temperature.csv`. -- El archivo presente en `logs/` es `temperature.csv`. -- Versiones anteriores de `ARCHITECTURE.md` y `sensors/EZORTD/ezortd_daemon.c` apuntaban a `logs/temp.csv`. - -Nomenclatura unica propuesta y aplicada para todo el proyecto: - -| Variable | Nombre canonico | -|---|---| -| Temperatura | `logs/temperature.csv` | -| pH | `logs/ph.csv` | -| Oxigeno disuelto | `logs/do.csv` | -| Conductividad | `logs/ec.csv` | - -La razon es mantener nombres descriptivos, consistentes con las etiquetas cientificas del dashboard y faciles de extender en exportaciones futuras. - -## Proximas fases - -1. Historial CSV persistente - - Unificar la escritura de logs en `logs/` o `data/`. - - Definir encabezados estables para cada sensor. - - Registrar timestamp ISO y valor numerico por lectura. - -2. Controles de rango para graficas - - Agregar rangos de tiempo: 5 min, 1 h, 24 h. - - Permitir pausa/reanudacion de actualizacion historica. - - Mostrar minimos, maximos y promedios por ventana. - -3. Notificaciones de alarmas - - Enviar eventos de `getAlarmEvents()` por correo. - - Enviar alertas por Telegram. - - Publicar alarmas por MQTT. - - Preparar salidas GPIO locales para alarmas criticas. - -4. Exportacion CSV - - Descargar historiales por sensor. - - Exportar ventanas de tiempo seleccionadas. - - Mantener compatibilidad con herramientas de analisis cientifico. - -5. Exportacion Excel - - Generar archivos `.xlsx` con hojas por sensor. - - Incluir metadatos del experimento. - - Preparar tablas y graficas basicas. - -6. Calibracion Atlas Scientific - - Crear interfaz guiada para rutinas de calibracion. - - Registrar fecha, operador y resultado de calibracion. - - Proteger comandos criticos con confirmaciones. - -7. Comandos EZO desde la web - - Agregar una API local para enviar comandos a los circuitos EZO. - - Implementar endpoints seguros para lectura, calibracion y diagnostico. - - Separar permisos de monitoreo y administracion. - -## Notas de despliegue - -Para Raspberry Pi con Nginx, el `root` del sitio debe apuntar al directorio raiz del repositorio. El dashboard se abre desde: - -```text -/frontend/index.html -``` - -Desde esa ubicacion, el frontend lee los datos usando rutas relativas hacia `../data/*.json` y `../logs/*.csv`. - -Chart.js se carga desde CDN en esta fase. Para uso sin internet en la Raspberry Pi, la siguiente mejora recomendada es descargar una copia local versionada de Chart.js y servirla desde `frontend/vendor/`. diff --git a/README.md b/README.md index df2d809..81f4fe2 100644 --- a/README.md +++ b/README.md @@ -1,62 +1,219 @@ # Sistema de Monitoreo de Parámetros Fisicoquímicos para Fotobiorreactor -**Estado del Proyecto:** Desarrollo Activo (Fase de Pruebas de Integración y Simulación I2C) -**Plataforma de Hardware:** Raspberry Pi 4 Model B -**Pila Tecnológica:** Node.js, Express, Vanilla JavaScript, Nginx, C/C++ +**Plataforma de hardware:** Raspberry Pi 4 Model B +**Estado del proyecto:** Desarrollo activo — Integración de hardware en curso (Fase 9) +**Pila tecnológica:** C, Node.js, Express, Vanilla JavaScript, Nginx +**Protocolo de comunicación físico:** I²C (Inter-Integrated Circuit), 100 kHz — 400 kHz -## 1. Resumen del Proyecto +--- -Este repositorio documenta la arquitectura de software y los lineamientos de diseño de hardware para un sistema de adquisición de datos en tiempo real orientado a fotobiorreactores. Aunque el repositorio fue inicializado bajo el título de un proyecto para la sonda de temperatura PT1000, el sistema actual constituye una plataforma integral para el monitoreo simultáneo de cuatro parámetros críticos utilizando módulos OEM de la serie EZO de Atlas Scientific: +## 1. Resumen Ejecutivo -* **Temperatura** (Sonda RTD PT1000) -* **Potencial de Hidrógeno** (Sonda de pH) -* **Oxígeno Disuelto** (Sonda DO galvánica/óptica) -* **Conductividad Eléctrica** (Sonda EC) +Este repositorio documenta la arquitectura de software y las especificaciones de diseño de hardware de un sistema de adquisición de datos (DAQ) en tiempo real orientado a fotobiorreactores de escala de laboratorio. El proyecto se inició como un sistema de medición de temperatura de precisión basado en el módulo **EZO-RTD™** de Atlas Scientific y una sonda de platino PT1000; sin embargo, ha evolucionado en una plataforma integral de monitoreo que gestiona de forma simultánea cuatro parámetros fisicoquímicos críticos para el control de cultivos fotosintéticos. -## 2. Arquitectura del Sistema +Los cuatro parámetros monitoreados y los módulos OEM asociados son los siguientes: -La solución implementa una topología de red distribuida localmente, utilizando un modelo Cliente-Servidor optimizado por un proxy inverso para la gestión del tráfico y la mitigación de restricciones de intercambio de recursos de origen cruzado (CORS). +| Parámetro | Módulo EZO | Dirección I²C | Unidad de medida | +|---|---|---|---| +| Temperatura | EZO-RTD™ (ISCCB-2) | `0x66` | °C | +| Potencial de Hidrógeno | EZO-pH™ | `0x63` | pH | +| Oxígeno Disuelto | EZO-DO™ | `0x61` | mg/L | +| Conductividad Eléctrica | EZO-EC™ | `0x64` | µS/cm | + +La arquitectura del sistema implementa una topología de red distribuida localmente, separando de forma explícita las responsabilidades en cuatro capas: presentación (frontend), enrutamiento (Nginx), lógica de negocio y API (Node.js/Express) y adquisición de datos en hardware (demonios en C). Este diseño por capas garantiza la extensibilidad del sistema y permite la sustitución o incorporación de nuevos módulos sensores sin alterar la lógica de presentación. + +--- + +## 2. Pila Tecnológica ### 2.1. Capa de Presentación (Frontend) -Interfaz gráfica de usuario (GUI) asíncrona desarrollada en HTML5, CSS3 y JavaScript puro (ES6+). Implementa rutinas de *polling* de alta frecuencia (1000 ms) para la actualización de métricas en tiempo real y renderizado de series temporales mediante la biblioteca `Chart.js`. -### 2.2. Capa de Enrutamiento y Proxy (Nginx) -Servidor web Nginx configurado en el puerto `8888`. Actúa como servidor de archivos estáticos para la capa de presentación y opera como proxy inverso, redirigiendo todas las solicitudes HTTP bajo el prefijo `/api/` hacia el proceso de Node.js en el puerto `3000`. +- **Lenguaje:** Vanilla JavaScript ES6+, HTML5, CSS3 puro +- **Biblioteca de visualización:** Chart.js (entregada vía CDN) +- **Biblioteca de exportación tabular:** SheetJS (`xlsx.full.min.js`, vía CDN) +- **Ciclo de actualización de lecturas en vivo:** 1000 ms (intervalo de *polling*) +- **Ciclo de actualización de tendencias históricas:** 10 000 ms +- **Idioma de la interfaz:** Español neutro + +### 2.2. Capa de Enrutamiento y Proxy + +- **Servidor:** Nginx +- **Puerto de entrada:** `8888` +- **Función:** Entrega de archivos estáticos del frontend y proxy inverso transparente hacia el puerto `3000` para el prefijo `/api/` -### 2.3. Capa de Lógica de Negocio y API (Backend) -Servidor HTTP desarrollado en entorno Node.js utilizando el framework Express. Sus responsabilidades principales incluyen: -* Exposición de endpoints RESTful para la transmisión de telemetría. -* Simulación de retardos y latencias físicas inherentes al protocolo I2C (300 ms - 900 ms). -* Implementación de un analizador léxico (*Lexical Parser*) capaz de procesar el conjunto de instrucciones oficial de Atlas Scientific (calibración, compensación ambiental y diagnóstico). +### 2.3. Capa de Lógica de Negocio y API -### 2.4. Capa Física (Adquisición de Datos) -Demonios ejecutables compilados en C/C++ responsables de la interrogación directa de los registros de hardware en el bus I2C de la Raspberry Pi a través de los pines GPIO (SDA/SCL). +- **Entorno de ejecución:** Node.js (≥ v18) +- **Framework HTTP:** Express v5 +- **Puerto de escucha:** `3000` +- **Dependencias de producción:** `express ^5.2.1`, `cors ^2.8.6` -## 3. Características Técnicas Implementadas +### 2.4. Capa de Adquisición de Datos (Hardware) -* **Motor de Evaluación de Alarmas:** Subsistema lógico que compara la telemetría en tiempo real contra umbrales de operación críticos y de advertencia definidos paramétricamente en el archivo `config/alarms.json`. -* **Consola de Hardware Virtual:** Emulador integrado en la interfaz de usuario que permite la inyección de cadenas de comandos estandarizadas hacia los módulos EZO, facilitando rutinas de calibración multipunto y configuración de registros de compensación. -* **Módulo de Exportación de Datos:** Procesamiento de arreglos de datos históricos en el cliente y serialización a formato `.xlsx` utilizando la biblioteca `SheetJS`, permitiendo la extracción de registros tabulares para análisis posterior. +- **Lenguaje:** C (estándar C99) +- **Interfaz de hardware:** Bus I²C del sistema operativo Linux mediante `/dev/i2c-1` +- **Encabezados del sistema utilizados:** ``, `` +- **Sistema de construcción:** GNU Make -## 4. Estructura del Directorio Fuente +--- + +## 3. Estructura del Directorio Fuente ```text / -├── api/ -│ └── server.js # Lógica de enrutamiento Express y simulador de protocolo EZO -├── config/ -│ ├── alarms.json # Definición de límites operativos de seguridad -│ └── sensors.json # Metadatos y resolución de los instrumentos -├── data/ -│ └── *.json # Vectores de estado transitorios poblados por procesos C++ -├── frontend/ -│ ├── index.html # Interfaz principal de control -│ ├── dashboard.js # Rutinas de renderizado y evaluación de estado -│ ├── mock-service.js # Interfaz de comunicación asíncrona con el backend -│ ├── export-service.js # Lógica de construcción de conjuntos de datos para gráficos -│ └── export-excel.js # Formateo y serialización de archivos de hoja de cálculo -├── logs/ -│ └── *.csv # Registros históricos persistentes para series temporales -├── sensors/ -│ └── */*.c # Código fuente de los controladores de hardware (I2C) -└── README.md \ No newline at end of file +├── api/ +│ └── server.js # Servidor Express: endpoints REST y parser léxico EZO +│ +├── config/ +│ ├── alarms.json # Umbrales operativos configurables por variable +│ └── sensors.json # Metadatos declarativos de los módulos EZO (dirección I²C, habilitación) +│ +├── data/ +│ ├── EZORTD.json # Vector de estado actual: temperatura (escrito por el demonio C) +│ ├── EZOPH.json # Vector de estado actual: pH +│ ├── EZODO.json # Vector de estado actual: oxígeno disuelto +│ └── EZOEC.json # Vector de estado actual: conductividad eléctrica +│ +├── frontend/ +│ ├── index.html # Punto de entrada del dashboard de monitoreo +│ ├── dashboard.css # Hoja de estilos responsiva del sistema +│ ├── dashboard.js # Lógica principal: polling, evaluación de alarmas, gráficas +│ ├── mock-service.js # Capa de comunicación asíncrona con el backend (fetch/POST) +│ ├── export-service.js # Serialización de datos históricos a formato CSV +│ └── export-excel.js # Generación de reportes multipagina en formato XLSX +│ +├── logs/ +│ ├── temperature.csv # Historial persistente de temperatura (escrito por el demonio) +│ ├── ph.csv # Historial persistente de pH +│ ├── do.csv # Historial persistente de oxígeno disuelto +│ └── ec.csv # Historial persistente de conductividad eléctrica +│ +├── sensors/ +│ ├── EZORTD/ +│ │ ├── ezortd.h # API pública: constante de dirección I²C y firma de getTemperature() +│ │ ├── ezortd.c # Implementación del protocolo I²C para el EZO-RTD +│ │ ├── main.c # Ejecutable de lectura única (one-shot), salida JSON a stdout +│ │ ├── ezortd_daemon.c # Demonio de lectura continua: escribe JSON y appends CSV +│ │ └── Makefile # Sistema de construcción del módulo RTD +│ │ +│ ├── EZOPH/ +│ │ ├── ezoph.h # API pública del módulo pH +│ │ ├── ezoph.c # Implementación del protocolo I²C para el EZO-pH +│ │ ├── main.c # Ejecutable de lectura única +│ │ └── Makefile +│ │ +│ ├── EZODO/ +│ │ ├── ezodo.h # API pública del módulo DO +│ │ ├── ezodo.c # Implementación del protocolo I²C para el EZO-DO +│ │ ├── main.c # Ejecutable de lectura única +│ │ └── Makefile +│ │ +│ └── EZOEC/ +│ ├── ezoec.h # API pública del módulo EC +│ ├── ezoec.c # Implementación del protocolo I²C para el EZO-EC +│ ├── main.c # Ejecutable de lectura única +│ └── Makefile +│ +├── package.json # Manifiesto de dependencias Node.js +├── package-lock.json # Árbol de dependencias resuelto y bloqueado +├── README.md # Este documento +├── ARCHITECTURE.md # Especificación técnica de la arquitectura del sistema +└── PROJECT_STATUS.md # Estado de hitos, riesgos y fases pendientes +``` + +--- + +## 4. Instrucciones de Despliegue + +### 4.1. Requisitos Previos + +- Node.js v18 o superior instalado en el sistema anfitrión +- Nginx instalado (`sudo apt install nginx` en sistemas Debian/Ubuntu) +- Acceso al directorio raíz del repositorio + +### 4.2. Inicialización del Servidor de Backend (Node.js) + +Desde el directorio raíz del repositorio, instalar las dependencias de producción y levantar el servidor: + +```bash +npm install +node api/server.js +``` + +El servidor quedará escuchando en `http://localhost:3000`. Verificar el inicio exitoso con el mensaje: + +``` +[MOCK SERVER] Backend Node.js corriendo en http://localhost:3000 +``` + +Para ejecución persistente en segundo plano se recomienda el gestor de procesos `pm2`: + +```bash +npm install -g pm2 +pm2 start api/server.js --name fotobiorreactor-api +pm2 save +``` + +### 4.3. Configuración del Proxy Inverso Nginx + +Crear o editar el bloque de servidor activo de Nginx. En sistemas Debian-based, el archivo de configuración canónico es `/etc/nginx/sites-available/fotobiorreactor`: + +```nginx +server { + listen 8888; + server_name localhost; + + # Raíz del contenido estático: directorio raíz del repositorio + root /ruta/absoluta/al/repositorio; + index frontend/index.html; + + # Entrega de archivos estáticos del frontend + location / { + try_files $uri $uri/ =404; + } + + # Proxy inverso transparente hacia el backend Node.js + location /api/ { + proxy_pass http://127.0.0.1:3000; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } +} +``` + +Habilitar el sitio y recargar el servicio: + +```bash +sudo ln -s /etc/nginx/sites-available/fotobiorreactor /etc/nginx/sites-enabled/ +sudo nginx -t +sudo systemctl reload nginx +``` + +### 4.4. Acceso al Cliente + +Abrir un navegador web y dirigirse a: + +``` +http://localhost:8888/frontend/index.html +``` + +El dashboard iniciará automáticamente el ciclo de polling hacia la API y las lecturas en vivo comenzarán a actualizarse con los datos simulados del backend. + +### 4.5. Construcción de los Demonios en C (Hardware Real) + +Para compilar los controladores de hardware en la Raspberry Pi, ejecutar el siguiente procedimiento por módulo sensor (se ilustra con el módulo RTD): + +```bash +cd sensors/EZORTD +make +``` + +Esto generará el ejecutable `EZORTD`. Antes de compilar el demonio continuo (`ezortd_daemon.c`), actualizar las rutas absolutas codificadas en el código fuente para que correspondan al directorio de despliegue real en la Raspberry Pi. + +**Nota:** El bus I²C debe estar habilitado en la Raspberry Pi mediante `sudo raspi-config` → *Interface Options* → *I2C* → *Enable*. Se recomienda agregar el usuario de ejecución al grupo `i2c` para evitar la ejecución con privilegios de superusuario: + +```bash +sudo usermod -aG i2c $USER +``` \ No newline at end of file