jueves, 9 de abril de 2026

Exercises

 

Exercise 1: The Darkness Sensor

Goal: The LED should turn on when the sensor detects that it is dark.

int led = 2;
int ldr = 34;

void setup() {
  pinMode(led, INPUT);
  Serial.begin(115200);
}

void loop() {
  int light = analogRead(ldr);
  
  if (light < 500) {
    digitalWrite(led, LOW);
  } else {
    digitalWrite(led, HIGH);
  }
  
  Serial.println(ldr);
  delay(5000);
}

Exercise 2: The Irrigation Servo

Goal: If the potentiometer (humidity) drops below 30, the servo should open to 90 degrees.

#include <ESP32Servo.h>
Servo myservo;

void setup() {
  myservo.attach(18);
}

void loop() {
  int value = analogRead(32);
  int humidity = map(value, 0, 4095, 0, 100);

  if (humidity = 20) {
    myservo.write(90);
  } else {
    myservo.write(0);
  }

  delay(1);
  humidity = 100;
  Serial.print(humidity);
}

Exercise 3: Temperature Alarm

Goal: If the temperature rises above 30 degrees, the LED should blink as an alert.


#include "DHT.h"
DHT dht(15, DHT22);

void setup() {
  dht.begin();
  pinMode(2, OUTPUT);
}

void loop() {
  float temp = dht.readTemperature();

  while (temp > 30) {
    digitalWrite(2, HIGH);
    delay(200);
    digitalWrite(2, LOW);
    delay(200);
  }

  if (temp < 30) {
    digitalWrite(2, LOW);
  }
  
  delay(20);
}

Exercise 4: Motion Sensor Door

Goal: When motion is detected (PIR), the servo should open the door (180 degrees).

#include <ESP32Servo.h>
Servo door;

void setup() {
  pinMode(13, OUTPUT);
  door.attach(12);
}

void loop() {
  int pir = digitalRead(12);

  if (pir == LOW) {
    door.write(180);
  } else {
    door.write(0);
  }

  return;
  delay(100);
}

miércoles, 8 de abril de 2026

Lab Guide

 

Lab Guide: Cobot End-Effector Programming for Mushroom Bio-Factories

Objective: Program a 3-DOF Cobot’s end-effector to automate the handling of Orellana mushroom substrate and spore collection.

 General Wokwi Setup:

  • Microcontroller: ESP32.

  • Servos: Connect VCC to 5V (Red), GND to GND (Black), and Signal to the assigned Pin (Orange).

  • Button: Connect one side to GND and the other to the assigned Pin.


 Mission 1: The Substrate Shear (Sequential Logic)

Scenario: The cobot must perform a quick, forceful cut into the substrate blocks to prepare them for inoculation.

  • Task: Program a fast closing motion and a slow safety opening.

  • Wokwi Connection: Signal to Pin 18.

#include <ESP32Servo.h>

Servo shearServo;
const int shearPin = 18;

void setup() {
  shearServo.attach(shearPin);
  shearServo.write(0); // Initial position: Open
}

void loop() {
  // --- STUDENT CHALLENGE ---
  // 1. Move the servo to 130 degrees for a fast cut
  shearServo.______(130); 
  
  // 2. Add a delay of 200ms to allow the blade to pass through
  ______(200); 
  
  // 3. Return the servo to 0 degrees
  shearServo.write(0);
  
  // 4. Wait 2 seconds before the next substrate block arrives
  delay(2000);
}

Mission 2: The Inoculum Mixer (Iterative Logic - Loops)

Scenario: Mix the spores into the substrate gently using a sweeping motion.

  • Task: Use a for loop to create a smooth "sweep" to avoid damaging the mycelium.

  • Wokwi Connection: Signal to Pin 19.


#include <ESP32Servo.h>

Servo mixerServo;
const int mixerPin = 19;

void setup() {
  mixerServo.attach(mixerPin);
}

void loop() {
  // Forward sweep: from 45 to 135 degrees
  for (int pos = 45; pos <= 135; pos++) {
    mixerServo.write(pos);
    delay(20); 
  }

  // --- STUDENT CHALLENGE ---
  // Complete the 'for' loop to return from 135 back to 45 degrees
  for (int pos = ____; pos >= ____; pos____) { 
    mixerServo.write(pos);
    delay(20);
  }
}

Mission 3: The Nutrient Dispenser (Conditional Logic)

Scenario: Release water or nutrients into the substrate only when an operator triggers the system.

  • Task: Use an if statement to activate the valve upon button press.

  • Wokwi Connection: Servo Signal to Pin 21 | Button to Pin 12.


#include <ESP32Servo.h>

Servo valveServo;
const int buttonPin = 12;

void setup() {
  valveServo.attach(21);
  pinMode(buttonPin, INPUT_PULLUP); // Internal pull-up resistor
}

void loop() {
  // --- STUDENT CHALLENGE ---
  // Detect if the button is pressed (Remember: INPUT_PULLUP logic)
  if (digitalRead(buttonPin) == ____) { 
    valveServo.write(90); // Open valve
    delay(1500);          // Dispensing time
    valveServo.write(0);  // Close valve
  }
}

Mission 4: Vertical Spore Sampler (Functions & Precision)

Scenario: Collect spore samples from the substrate surface using a high-precision vertical descent.

  • Task: Organize your code using Functions. Move the servo degree-by-degree very slowly.

  • Wokwi Connection: Signal to Pin 5.


#include <ESP32Servo.h>

Servo samplerServo;

void setup() {
  samplerServo.attach(5);
  samplerServo.write(0); // Start at top position
}

void loop() {
  // Call the custom functions
  descendSlowly();
  delay(1000); // Time for collection
  ascendFast();
  delay(5000); 
}

// --- STUDENT CHALLENGE ---

void descendSlowly() {
  // Use a loop to move from 0 to 100 degrees slowly
  for (int i = 0; i <= 100; i++) {
    samplerServo.write(____); // Use the iterator variable
    delay(60);               // Slow speed for precision
  }
}

void ascendFast() {
  // Move the servo back to 0 degrees instantly
  samplerServo.write(____); 
}

martes, 7 de abril de 2026

Sistema: Rastreador de Posición del Sol

 

/*

 * =========================================================

 * COLEGIO GIMNASIO BILINGÜE MARIE CURIE

 * Proyecto de Tecnología y Automatización

 * Sistema: Rastreador de Posición del Sol

 * Desarrollado por: Centro Prisma

 * Versión: 1.0

 * Fecha: 19 de Febrero de 2026

 * =========================================================

*/

#include <ESP32Servo.h>

 

/*

=========================================================

RASTREADOR DE POSICIÓN DEL SOL

=========================================================

 

¿QUÉ HACE ESTE PROYECTO?

 

Este sistema usa:

- 2 sensores de luz

- 1 servo (motor que gira por ángulo)

 

El programa compara cuánta luz recibe cada sensor.

Si un lado recibe más luz, el servo gira hacia ese lado.

 

El proceso se repite muchas veces por segundo.

Eso permite que el sistema se ajuste constantemente.

Eso es un sistema de lazo cerrado con retroalimentación.

=========================================================

*/

 

 

// ================= SENSORES =================

 

// Pin donde conectamos el sensor izquierdo

const int SENSOR_IZQUIERDO = 34; // Lee valores entre 0 y 4095

 

// Pin donde conectamos el sensor derecho

const int SENSOR_DERECHO = 35; // También lee valores entre 0 y 4095

 

 

// ================= ACTUADOR =================

const int PIN_SERVO = 27; // Pin que envía la señal de control al servo

 

Servo actuador; // Creamos el objeto que controla el servo

 

 

// ================= AJUSTES DEL SISTEMA =================

const int SENSIBILIDAD = 50; // Diferencia mínima entre los sensores de luz para considerar que sí hay cambio real de luz

 

 

const int LIMITE_IZQ = 10; // Límite mínimo permitido para proteger el servo

 

const int LIMITE_DER = 170; // Límite máximo permitido para proteger el servo

 

const int PASO_GIRO = 2; // Cantidad de grados que gira cada vez que se mueve

 

int posicion = 90; // Posición actual del servo (empieza en el centro)

 

 

// ================= FUNCIONES =================

// Devuelve la cantidad de luz que recibe el sensor izquierdo

int medirIzquierda() {

  return analogRead(SENSOR_IZQUIERDO);

}

 

 

// Devuelve la cantidad de luz que recibe el sensor derecho

int medirDerecha() {

  return analogRead(SENSOR_DERECHO);

}

 

 

// Resta 2 grados a la posición actual

void girarIzquierda() {

  posicion = posicion - PASO_GIRO;

}

 

 

// Suma 2 grados a la posición actual

void girarDerecha() {

  posicion = posicion + PASO_GIRO;

}

 

 

// Evita que el servo se pase del límite izquierdo

void protegerIzquierda() {

 

  // Si la posición es menor al límite permitido en el servo

  if (posicion < LIMITE_IZQ) {

 

    // Forzamos la posición al límite seguro del servo

    posicion = LIMITE_IZQ;

  }

}

 

 

// Evita que el servo se pase del límite derecho

void protegerDerecha() {

 

  // Si la posición es mayor al límite permitido del servo

  if (posicion > LIMITE_DER) {

 

    // Forzamos la posición al límite seguro del servo

    posicion = LIMITE_DER;

  }

}

 

 

// Envía la posición actual al servo

void moverServo() {

  actuador.write(posicion); //escribe la nueva posición del servo

}

 

 

 

// ================= INICIO =================

void setup() {

 

  // Inicia comunicación con el computador

  Serial.begin(115200);

 

  // Conecta el servo al pin definido

  actuador.attach(PIN_SERVO);

 

  // Lleva el servo a la posición inicial

  moverServo();

 

  Serial.println("RASTREADOR DE SOL INICIADO");

}

 

 

 

// ================= CICLO PRINCIPAL =================

// Esta parte se repite una y otra vez mientras el programa esté encendido.

// Es como el "ciclo de pensamiento" del sistema.

void loop() {

 

  // 1️⃣ MEDIR LUZ

  // Le preguntamos a cada sensor cuánta luz está viendo.

  // Guardamos esos números en dos variables.

  int luzIzq = medirIzquierda();   // Luz que ve el sensor izquierdo

  int luzDer = medirDerecha();     // Luz que ve el sensor derecho

 

  // 2️⃣ CALCULAR DIFERENCIA

  // Restamos izquierda menos derecha para comparar.

  // Si el resultado es positivo → hay más luz a la izquierda.

  // Si es negativo → hay más luz a la derecha.

  int diferencia = luzIzq - luzDer;

 

  // 3️⃣ DECIDIR MOVIMIENTO

  // Si la diferencia es suficientemente grande hacia la izquierda,

  // el servo gira hacia la izquierda.

  if (diferencia > SENSIBILIDAD) {

    girarIzquierda();

  }

 

  // Si la diferencia es suficientemente grande hacia la derecha,

  // el servo gira hacia la derecha.

  if (diferencia < -SENSIBILIDAD) {

    girarDerecha();

  }

 

  // 4️⃣ PROTEGER EL SERVO

  // Revisamos que el servo no se pase de los límites seguros.

  // Esto evita que el motor se fuerce o se dañe.

  protegerIzquierda();

  protegerDerecha();

 

  // 5️⃣ MOVER EL SERVO

  // Ahora sí enviamos la nueva posición al motor.

  // Aquí es cuando realmente se mueve.

  moverServo();

 

  // 6️⃣ MOSTRAR DATOS EN PANTALLA

  // Esto nos permite ver qué está "pensando" el sistema.

  // No cambia el movimiento, solo muestra información.

  Serial.print("Sensor Izq: ");

  Serial.print(luzIzq);

 

  Serial.print(" | Sensor Der: ");

  Serial.print(luzDer);

 

  Serial.print(" | Diferencia: ");

  Serial.print(diferencia);

 

  Serial.print(" | Posicion Servo (grados): ");

  Serial.println(posicion);

 

  // Esperamos un momento antes de repetir todo.

  // Esto hace que el movimiento sea más estable.

  delay(20);

}

 

 

 

Sistema: Control Automático de Luz (LED + LDR)

 

/*

 * =========================================================

 * COLEGIO GIMNASIO BILINGÜE MARIE CURIE

 * Proyecto de Tecnología y Automatización

 * Sistema: Control Automático de Luz (LED + LDR)

 * Desarrollado por: Centro Prisma

 * Versión: 1.0 (Control proporcional con Kp + unidades físicas)

 * Fecha: 19 de Febrero de 2026

 * =========================================================

*/

 

#include <Arduino.h>

 

/*

=========================================================

CONTROL AUTOMÁTICO DE LUZ CON LAZO CERRADO (LED + LDR)

=========================================================

 

¿QUÉ HACE ESTE PROYECTO?

 

Este sistema usa:

- 1 sensor de luz (LDR)  -> mide la luz como un número (ADC)

- 1 LED (actuador)       -> cambia su brillo con PWM

- Control proporcional (Kp)

 

El sistema hace esto todo el tiempo:

 

1) Mide la luz (ENTRADA).

2) La compara con una meta llamada SETPOINT.

3) Calcula la diferencia (ERROR).

4) Decide el brillo del LED con una regla proporcional: PWM = Kp × ERROR.

5) Enciende el LED con ese brillo (SALIDA).

6) Vuelve a medir y repite.

 

Eso es un sistema de LAZO CERRADO:

mide → piensa → actúa → vuelve a medir.

 

=========================================================

*/

 

 

// ================= SENSOR DE LUZ (LDR) =================

//

// El LDR cambia su resistencia según la luz.

// El ESP32 convierte esa señal en un número digital con su ADC.

//

// ADC del ESP32 (típico): 12 bits

// Esto significa que puede leer:

//

// 0    -> 0.0V (voltaje mínimo)

// 4095 -> 3.3V (voltaje máximo)

//

// OJO: Este número NO son lúmenes reales.

// Es una lectura digital del sensor.

//

// IMPORTANTE EN ESTE MONTAJE:

//

// Más luz sobre el LDR    -> el número BAJA

// Menos luz (si lo tapas)-> el número SUBE

const int PIN_LDR = 34;

 

 

// ================= SALIDA (LED) =================

//

// Este LED será el “músculo” del sistema.

// Su brillo se controla con PWM (0 a 255).

const int PIN_LED = 12;

 

 

// ================= CONFIGURACIÓN DEL PWM =================

//

// PWM = Modulación por Ancho de Pulso

//

// El ESP32 no cambia el voltaje del LED.

// En cambio, prende y apaga el pin muy rápido.

//

// FRECUENCIA_PWM = 5000 Hz

// Esto significa que la señal se repite

// 5000 veces por segundo.

//

// RESOLUCION_PWM = 8 bits

// Esto significa 256 niveles de potencia:

//

// 0   -> 0%   (apagado)

// 255 -> 100% (máximo brillo)

const int CANAL_PWM = 0;

const int FRECUENCIA_PWM = 5000;

const int RESOLUCION_PWM = 8;

 

 

// ================= AJUSTES DEL SISTEMA (CONTROL) =================

//

// SETPOINT es la meta de luz que queremos mantener.

// Es un número del ADC (0 a 4095), no lúmenes reales.

const int SETPOINT = 2000;

 

// Kp es la “fuerza” del control proporcional.

// Entre más grande sea Kp, más fuerte reacciona el LED.

const float KP = 0.12;

 

 

// ================= TIEMPO ENTRE CICLOS =================

//

// delay(100) = 100 milisegundos = 0.1 segundos

// Eso significa que el sistema repite el lazo:

// 10 veces por segundo (aprox.)

const int TIEMPO_ENTRE_CICLOS_MS = 300;

 

 

// ================= VARIABLES (DATOS QUE CAMBIAN) =================

//

// Estas variables guardan lo que el sistema mide y calcula

// en cada vuelta del lazo cerrado.

int luzActual = 0;     // Lectura ADC (0 a 4095)

int errorLuz = 0;      // Diferencia entre lectura y meta

int pwmCalculado = 0;  // Salida PWM (0 a 255)

 

 

// =================================================

// FUNCIONES DEL SISTEMA

// =================================================

 

 

// -------------------------------------------------

// Función: medirLuz()

// -------------------------------------------------

// Lee el valor del LDR con analogRead().

//

// Resultado:

// - Guarda el valor en "luzActual"

//

// Unidad real:

// - Es un número ADC (0 a 4095)

// - 0 equivale a 0.0V y 4095 equivale a 3.3V (aprox.)

// -------------------------------------------------

void medirLuz() {

  luzActual = analogRead(PIN_LDR);

}

 

 

// -------------------------------------------------

// Función: calcularError()

// -------------------------------------------------

// Calcula qué tan lejos estamos de la meta.

//

// Fórmula:

// ERROR = Luz actual - SETPOINT

//

// Ejemplos:

//

// Si ERROR = 0

// -> estamos exactamente en la meta.

//

// Si ERROR es grande

// -> estamos lejos de la meta.

//

// Si ERROR es negativo

// -> significa que ya hay suficiente luz.

// -> lo convertimos en 0 porque el LED no puede

//    tener “potencia negativa”.

// -------------------------------------------------

void calcularError() {

 

  errorLuz = luzActual - SETPOINT;

 

  if (errorLuz < 0) {

    errorLuz = 0;

  }

}

 

 

// -------------------------------------------------

// Función: calcularBrillo()

// -------------------------------------------------

// Decide qué brillo debe tener el LED.

//

// Regla proporcional:

// PWM = Kp × ERROR

//

// - ERROR pequeño -> PWM pequeño -> LED suave.

// - ERROR grande  -> PWM grande  -> LED fuerte.

//

// Regla de seguridad:

// PWM máximo con 8 bits = 255.

// Si el cálculo supera 255, lo dejamos en 255.

// -------------------------------------------------

void calcularBrillo() {

 

  pwmCalculado = (int)(KP * errorLuz);

 

  if (pwmCalculado > 255) {

    pwmCalculado = 255;

  }

}

 

 

// -------------------------------------------------

// Función: aplicarSalida()

// -------------------------------------------------

// Envía el PWM al LED.

//

// Unidad real:

// - PWM 0   -> 0%   de potencia (apagado)

// - PWM 255 -> 100% de potencia (máximo brillo)

// -------------------------------------------------

void aplicarSalida() {

  ledcWrite(CANAL_PWM, pwmCalculado);

}

 

 

// -------------------------------------------------

// Función: mostrarDatos()

// -------------------------------------------------

// Muestra lo que está pasando en el lazo cerrado.

//

// Mostramos unidades físicas y rangos:

//

// - ADC (0 a 4095) y su voltaje aproximado (0.0V a 3.3V)

// - PWM (0 a 255) y su porcentaje aproximado (0% a 100%)

// - Tiempo del ciclo en milisegundos (ms)

// -------------------------------------------------

void mostrarDatos() {

 

  // Convertimos la lectura ADC a voltaje aproximado (0.0V a 3.3V)

  // Fórmula: voltaje = (ADC / 4095) * 3.3

  float voltajeAprox = ( (float)luzActual / 4095.0 ) * 3.3;

 

  // Convertimos PWM a porcentaje aproximado

  // Fórmula: porcentaje = (PWM / 255) * 100

  float porcentajePWM = ( (float)pwmCalculado / 255.0 ) * 100.0;

 

  Serial.println("=================================");

  Serial.println("   LAZO CERRADO DE LUZ (LED+LDR)  ");

  Serial.println("=================================");

 

  Serial.print("Luz medida (ADC 0-4095) Lumenes: ");

  Serial.print(4095 - luzActual);

 

 

  Serial.print("Meta (SETPOINT) (ADC):   ");

  Serial.println(SETPOINT);

 

  Serial.print("Error (ADC):             ");

  Serial.println(errorLuz);

 

  Serial.print("Salida LED (PWM 0-255):  ");

  Serial.print(pwmCalculado);

 

}

 

 

// =================================================

// INICIO DEL PROGRAMA

// =================================================

void setup() {

 

  // Comunicación con el computador para ver mensajes.

  Serial.begin(115200);

 

  // Configuramos el PWM:

  ledcSetup(CANAL_PWM, FRECUENCIA_PWM, RESOLUCION_PWM);

 

  // Conectamos el canal PWM al pin del LED.

  ledcAttachPin(PIN_LED, CANAL_PWM);

 

  // Iniciamos el LED apagado.

  ledcWrite(CANAL_PWM, 0);

 

  Serial.println("CONTROL DE LUZ INICIADO");

}

 

 

// =================================================

// CICLO PRINCIPAL (LAZO CERRADO)

// =================================================

void loop() {

 

  /*

  ==========================================================

  ================== CICLO DE LAZO CERRADO =================

  ==========================================================

 

  1) MEDIR        -> medirLuz()

  2) COMPARAR     -> calcularError()

  3) DECIDIR      -> calcularBrillo()   (PWM = Kp × ERROR)

  4) ACTUAR       -> aplicarSalida()

  5) OBSERVAR     -> mostrarDatos()

  6) ESPERAR      -> delay(ms)

  7) REPETIR      -> vuelve al paso 1 automáticamente

 

  Este ciclo se repite muchas veces.

  Eso es el LAZO CERRADO.

  ==========================================================

  */

 

  // 1️⃣ MEDIR (ENTRADA)

  medirLuz();

 

  // 2️⃣ COMPARAR (ERROR)

  calcularError();

 

  // 3️⃣ DECIDIR (CONTROL PROPORCIONAL)

  calcularBrillo();

 

  // 4️⃣ ACTUAR (SALIDA)

  aplicarSalida();

 

  // 5️⃣ OBSERVAR (RETROALIMENTACIÓN)

  mostrarDatos();

 

  // 6️⃣ ESPERAR Y REPETIR

  delay(TIEMPO_ENTRE_CICLOS_MS);

}

 

 

Sistema: Control Automático de Humedad

 

/*

 * =========================================================

 * COLEGIO GIMNASIO BILINGÜE MARIE CURIE

 * Proyecto de Tecnología y Automatización

 * Sistema: Control Automático de Humedad

 * Desarrollado por: Centro Prisma

 * Versión: 1.0 (Lazo cerrado ON/OFF + Kp en tiempo, sin PWM)

 * Fecha: 23 de Febrero de 2026

 * =========================================================

 *

 * ¿QUÉ HACE ESTE SISTEMA?

 *

 * Este programa controla automáticamente la humedad del aire.

 *

 * Funciona así:

 * 1) Mide la humedad con un sensor.

 * 2) La compara con un valor deseado llamado SETPOINT.

 * 3) Calcula cuánto "falta" para llegar a ese valor.

 * 4) Enciende o apaga el humidificador.

 * 5) Ajusta la velocidad de reacción sin usar PWM.

 *

 * Esto es un sistema de LAZO CERRADO porque:

 * Mide → Compara → Actúa → Vuelve a medir.

 *

 */

 

// =========================================================

// LIBRERÍAS

// =========================================================

 

#include <Wire.h>              // Permite comunicación I2C

#include <Adafruit_SHT31.h>    // Librería del sensor SHT31

 

 

// ================= SENSOR =================

 

// Creamos el objeto del sensor.

// Es como decir: "Vamos a usar este sensor de humedad".

Adafruit_SHT31 sensorHumedad = Adafruit_SHT31();

 

 

// ================= PIN =================

 

// Pin donde está conectado el humidificador.

const int PIN_HUMIDIFICADOR = 25;

 

 

// ================= CONTROL =================

 

// SETPOINT = humedad deseada.

// En este caso queremos llegar al 85%.

const float SETPOINT = 85.0;

 

// KP controla qué tan rápido reacciona el sistema.

// Entre mayor sea, más fuerte será la reacción.

const float KP = 1.0;

 

 

// ================= TIEMPOS =================

 

// Tiempo base de espera entre mediciones.

const int TIEMPO_BASE = 1000;

 

// Tiempo mínimo permitido (para que no sea demasiado rápido).

const int TIEMPO_MIN  = 200;

 

// Tiempo máximo permitido (para que no sea demasiado lento).

const int TIEMPO_MAX  = 3000;

 

 

// ================= VARIABLES =================

 

// Aquí se guarda la humedad medida por el sensor.

float humedadActual = 0.0;

 

// Aquí se guarda cuánto falta para llegar al setpoint.

// Nunca será negativo.

float error = 0.0;

 

// Tiempo que el sistema esperará antes de volver a medir.

int tiempoEspera = TIEMPO_BASE;

 

 

// =================================================

// FUNCIONES

// =================================================

 

 

// -------------------------------------------------

// MIDE LA HUMEDAD DEL AIRE

// -------------------------------------------------

void medirHumedad() {

 

  // Le pedimos al sensor que mida la humedad.

  humedadActual = sensorHumedad.readHumidity();

 

  // Seguridad: si por alguna razón el valor es menor que 0,

  // lo corregimos a 0%.

  if (humedadActual < 0) {

    humedadActual = 0;

  }

 

  // Seguridad: si supera 100%, lo corregimos.

  if (humedadActual > 100) {

    humedadActual = 100;

  }

}

 

 

// -------------------------------------------------

// CALCULA CUÁNTO FALTA PARA LLEGAR AL SETPOINT

// -------------------------------------------------

void calcularError() {

 

  // Restamos: meta menos humedad actual.

  error = SETPOINT - humedadActual;

 

  // Si ya estamos por encima del setpoint,

  // no hay "falta", entonces el error es 0.

  if (error < 0) {

    error = 0;

  }

}

 

 

// -------------------------------------------------

// ENCIENDE EL HUMIDIFICADOR

// -------------------------------------------------

void encenderHumidificador() {

 

  digitalWrite(PIN_HUMIDIFICADOR, HIGH);

  Serial.println("HUMIDIFICADOR: ENCENDIDO");

}

 

 

// -------------------------------------------------

// APAGA EL HUMIDIFICADOR

// -------------------------------------------------

void apagarHumidificador() {

 

  digitalWrite(PIN_HUMIDIFICADOR, LOW);

  Serial.println("HUMIDIFICADOR: APAGADO");

}

 

 

// -------------------------------------------------

// DECIDE SI ENCENDER O APAGAR

// -------------------------------------------------

void decidirAccion() {

 

  // Si estamos por debajo del setpoint, encendemos.

  if (humedadActual < SETPOINT) {

    encenderHumidificador();

  }

 

  // Si estamos por encima del setpoint, apagamos.

  if (humedadActual > SETPOINT) {

    apagarHumidificador();

  }

}

 

 

// -------------------------------------------------

// AJUSTA LA VELOCIDAD DE REACCIÓN (SIN PWM)

// -------------------------------------------------

void calcularVelocidadReaccion() {

 

  // Empezamos con el tiempo base.

  tiempoEspera = TIEMPO_BASE;

 

  // Si hay error, ajustamos el tiempo.

  if (error > 0) {

 

    // Entre más grande sea el error,

    // más rápido reaccionará el sistema.

    tiempoEspera = TIEMPO_BASE - (int)(KP * error * 20);

  }

 

  // Evitamos que el tiempo sea demasiado pequeño.

  if (tiempoEspera < TIEMPO_MIN) {

    tiempoEspera = TIEMPO_MIN;

  }

 

  // Evitamos que sea demasiado grande.

  if (tiempoEspera > TIEMPO_MAX) {

    tiempoEspera = TIEMPO_MAX;

  }

}

 

 

// -------------------------------------------------

// MUESTRA TODA LA INFORMACIÓN EN PANTALLA

// -------------------------------------------------

void mostrarDatos() {

 

  Serial.println("=================================");

 

  Serial.print("Humedad actual: ");

  Serial.print(humedadActual);

  Serial.println(" %");

 

  Serial.print("Setpoint: ");

  Serial.print(SETPOINT);

  Serial.println(" %");

 

  Serial.print("Error (Falta): ");

  Serial.println(error);

 

}

 

 

// -------------------------------------------------

// ESPERA ANTES DE VOLVER A MEDIR

// -------------------------------------------------

void esperar() {

  delay(tiempoEspera);

}

 

 

// =================================================

// SETUP (SE EJECUTA UNA SOLA VEZ)

// =================================================

void setup() {

 

  Serial.begin(115200);

 

  // Iniciamos comunicación I2C

  Wire.begin(21, 22);

 

  // Encendemos el sensor

  sensorHumedad.begin(0x44);

 

  // Configuramos el pin del humidificador como salida

  pinMode(PIN_HUMIDIFICADOR, OUTPUT);

 

  // Comenzamos con el humidificador apagado

  digitalWrite(PIN_HUMIDIFICADOR, LOW);

 

  Serial.println("CONTROL DE HUMEDAD INICIADO");

}

 

 

// =================================================

// LAZO CERRADO (SE REPITE SIEMPRE)

// =================================================

void loop() {

 

  // 1️⃣ Medir la humedad

  medirHumedad();

 

  // 2️⃣ Calcular cuánto falta

  calcularError();

 

  // 3️⃣ Decidir si encender o apagar

  decidirAccion();

 

  // 4️⃣ Ajustar velocidad de reacción

  calcularVelocidadReaccion();

 

  // 5️⃣ Mostrar información

  mostrarDatos();

 

  // 6️⃣ Esperar antes de repetir

  esperar();

}