Skip to content

EDD-002 — Motor de Reglas y Recetas de Cultivo ​

Metadata ​

CampoValor
AutorAlejandro Maturana
EstadoACCEPTED
Fecha2026-06-15
Versión1.0.0
ADRs relacionadosADR-009, ADR-011
RFC relacionados—

1. Problema / Contexto ​

Cada especie de hongo adaptógeno requiere condiciones ambientales distintas en cada fase de su ciclo de vida. Un Reishi en FRUITING necesita CO₂ < 700ppm, mientras que en INCUBATION tolera hasta 5000ppm. Un Shiitake requiere un shock térmico (cold shock, ΔT -8°C por 48h) para iniciar la formación de primordios.

El sistema necesita un motor de reglas que:

  1. Ejecute reglas locales en el firmware (sin red) con histéresis simple
  2. Ejecute reglas complejas en el backend con acceso a la receta activa y datos históricos
  3. Soporte recetas con múltiples fases y transiciones configurables

2. Objetivos ​

  • Permitir que el firmware opere de forma autónoma durante interrupciones de red
  • Soportar recetas con hasta 4 fases: INCUBATION, PRIMORDIA, FRUITING, HARVESTING
  • Evaluar reglas en el backend cada 60 segundos basándose en la receta activa
  • Prevenir oscilaciones de actuadores con histéresis configurable (T, HR, CO₂)
  • Garantizar que los actuadores nunca queden en estado inseguro (overheat fail-safe)

3. No-objetivos ​

  • Transiciones de fase automáticas por condición de sensor (planificadas en Fase 12 / EDD futuro)
  • Lógica fuzzy de control avanzado (evaluada en ADR-009, diferida para versiones futuras)
  • Control de iluminación por fotoperiodo con temporizador (pendiente, canal CH4 disponible)
  • Optimización ML de setpoints (Fase 15)

4. Alternativas consideradas ​

4.1 Estrategia de control: ¿dónde vive la lógica? ​

OpciónProsContrasDecisión
Dual (firmware + backend)Firmware autónomo en degraded; backend con contexto de recetaMayor complejidad de sincronización✅ Elegida
Solo firmwareAutónomo, sin redNo puede aplicar recetas complejas; configuración limitada❌
Solo backendRecetas ricas, contexto completoSi backend cae, actuadores sin control❌
Edge computing (ESP-NN)Modelos ML en chipComplejidad excesiva para el estado actual❌ diferida

4.2 Algoritmo de control ​

OpciónProsContrasDecisión
Histéresis simple (elegida)Simple, predecible, fácil de configurarNo maneja sistemas con inercia térmica alta✅ Elegida — ADR-009
PIDPreciso, elimina oscilacionesRequiere tuning por especie y hardware❌ diferido
Fuzzy LogicTolerante a incertidumbreComplejo de implementar en C++ embebido❌ diferido para v3+
Bang-bang puroMuy simpleOscilaciones frecuentes, desgaste de relés❌

5. Solución propuesta ​

5.1 Capa de firmware: histéresis local ​

El firmware implementa control por histéresis con banda configurable:

si temperatura < setpoint - histeresis_t → encender calefacción (CH2)
si temperatura > setpoint + histeresis_t → apagar calefacción
si humedad < setpoint - histeresis_h → encender humidificador (CH3)
si humedad > setpoint + histeresis_h → apagar humidificador
si co2 > umbral_co2 → encender ventilación (CH1)
si co2 < umbral_co2 - histeresis_co2 → apagar ventilación

Modos de operación por canal SSR:

  • LOCAL — Control por histéresis del firmware
  • REMOTE — Acepta comandos del backend vía HTTP polling
  • OFF — Canal desactivado

Temporizadores de seguridad:

  • minOn: tiempo mínimo que un actuador permanece encendido (evita flapping)
  • maxOn: tiempo máximo de encendido continuo (protección de motores)

5.2 Capa de backend: motor de reglas con receta ​

javascript
// controlEngine.js — evaluación cada 60s
async function evaluateRecipeRules(cycleId) {
  const cycle = await CultivationCycle.findByPk(cycleId, {
    include: [Recipe, CycleState],
  });
  const phase = cycle.currentPhase; // INCUBATION | PRIMORDIA | FRUITING | HARVESTING
  const setpoints = cycle.Recipe.stages[phase];
  const telemetry = await Telemetry.getLatestByDevice(cycle.deviceId);

  const rules = [
    {
      sensor: "temperature",
      channel: "CH2",
      setpoint: setpoints.temperature,
      hyst: 0.5,
    },
    {
      sensor: "humidity",
      channel: "CH3",
      setpoint: setpoints.humidity,
      hyst: 2.0,
    },
    { sensor: "co2", channel: "CH1", setpoint: setpoints.co2_max, hyst: 50 },
  ];

  for (const rule of rules) {
    const action = evaluateHysteresis(
      telemetry[rule.sensor],
      rule.setpoint,
      rule.hyst,
    );
    if (action !== null) {
      await publishCommand(cycle.deviceId, rule.channel, action);
    }
  }
}

5.3 Modelo de recetas (base de datos) ​

Recipe
├── id, name, species, description
├── stages: JSONB
│   ├── INCUBATION: { temperature, humidity, co2_max, ventilation_interval, duration_days }
│   ├── PRIMORDIA:  { ... }
│   ├── FRUITING:   { ... }
│   └── HARVESTING: { ... }
└── isTemplate, authorId

CultivationCycle
├── id, chamberDeviceId, recipeId
├── currentPhase: ENUM
├── startDate, estimatedEndDate
└── CycleStates[] — snapshots de transición de fase

5.4 Fail-safe Overheat ​

Implementado en firmware como override de máxima prioridad:

cpp
// Estado evaluado antes que cualquier regla de receta
if (temperature > OVERHEAT_THRESHOLD) {
  // Override: todos los SSR a OFF excepto ventilación (CH1 ON)
  setSsrState(CH1, ON);   // Ventilación máxima
  setSsrState(CH2, OFF);  // Calefacción apagada
  setSsrState(CH3, OFF);  // Humidificador apagado
  reportAlarm(ALARM_OVERHEAT);
}

6. Impacto en componentes ​

ComponenteImpactoArchivos
FirmwareAltohysteresis_controller.h/.cpp, ssr_controller.h/.cpp, state_machine.cpp
BackendAltocontrolEngine.js, Recipe.js, CultivationCycle.js, CycleState.js
FrontendMedioRecipes.jsx, Cycles.jsx, DeviceDetail.jsx (panel de alarmas)
Base de datosAltoMigración: Recipe, CultivationCycle, CycleState, Alarm

7. Plan de implementación ​

Esta funcionalidad está completamente implementada en Fases 3 y 4 del roadmap.

Evolución planificada en Fase 12 (Automatización Adaptativa):

  • Transiciones de fase por condición de sensor (no solo por tiempo)
  • Histéresis configurable por canal (no global)
  • Bitácora de transiciones con trazabilidad

8. Métricas de éxito ​

MétricaObjetivoEstado
Evaluación de reglas backendCada 60s✅
Deduplicación de alarmas60s backend, 120s firmware✅
Histéresis evita flappingminOn configurable✅
Modo degradado sin redHistéresis local activa✅
Fail-safe overheatThreshold T > 32°C✅

9. Riesgos y mitigaciones ​

RiesgoProb.ImpactoMitigación
Backend y firmware con setpoints distintosMediaAltoBackend envía setpoints al firmware vía polling
Actuador bloqueado ON por firmware bugBajaAltomaxOn timer + fail-safe overheat
Sensor reporta valor erróneo → regla incorrectaMediaMedioValidación de rangos + 3 lecturas inválidas → DEGRADED
Transición de fase incorrectaBajaMedioLogs de CycleState + alerta al operador

10. Referencias ​

Mush2 — Sistema IoT de control ambiental