/*
* SISTEMA DE INVERNADERO INTELIGENTE
* Proyecto: artificialroot.es
*
* Hardware requerido:
* - ESP32
* - Pantalla OLED 128x64 (I2C)
* - Sensor DHT22 (temperatura y humedad ambiente)
* - Sensor de humedad de suelo capacitivo/resistivo
* - Relé para bomba de agua
* - LEDs indicadores (Verde: OK, Amarillo: Alerta, Rojo: Crítico)
*
* Librerías necesarias (instalar desde el gestor de librerías de Arduino IDE):
* - Adafruit_SSD1306
* - Adafruit_GFX
* - DHT sensor library (Adafruit)
*/
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <DHT.h>
// ========== CONFIGURACIÓN DE PINES ==========
#define DHT_PIN 4 // Pin del sensor DHT22
#define SOIL_SENSOR_PIN 34 // Pin analógico para sensor de humedad de suelo
#define PUMP_PIN 25 // Pin para controlar el relé de la bomba
#define LED_GREEN 26 // LED verde - Sistema OK
#define LED_YELLOW 27 // LED amarillo - Alerta
#define LED_RED 14 // LED rojo - Crítico
// ========== CONFIGURACIÓN DE SENSORES ==========
#define DHT_TYPE DHT22 // Tipo de sensor DHT
DHT dht(DHT_PIN, DHT_TYPE); // Inicializar sensor DHT
// ========== CONFIGURACIÓN PANTALLA OLED ==========
// Conexiones I2C de la pantalla OLED:
// SDA -> GPIO 21 (pin SDA del ESP32)
// SCL -> GPIO 22 (pin SCL del ESP32)
// VCC -> 3.3V
// GND -> GND
#define SCREEN_WIDTH 128 // Ancho de la pantalla OLED
#define SCREEN_HEIGHT 64 // Alto de la pantalla OLED
#define OLED_RESET -1 // Pin de reset (usar -1 si comparte reset con ESP32)
#define SCREEN_ADDRESS 0x3C // Dirección I2C de la pantalla (0x3C o 0x3D)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// ========== UMBRALES Y CONFIGURACIÓN ==========
const int SOIL_DRY_THRESHOLD = 40; // Porcentaje: por debajo de esto está seco
const int SOIL_WET_THRESHOLD = 70; // Porcentaje: por encima de esto está húmedo
const float TEMP_MIN = 15.0; // Temperatura mínima ideal (°C)
const float TEMP_MAX = 30.0; // Temperatura máxima ideal (°C)
const float HUMIDITY_MIN = 40.0; // Humedad ambiente mínima ideal (%)
const float HUMIDITY_MAX = 80.0; // Humedad ambiente máxima ideal (%)
const unsigned long PUMP_TIME = 5000; // Tiempo de riego (5 segundos)
const unsigned long READ_INTERVAL = 2000; // Intervalo de lectura de sensores (2 segundos)
// ========== VARIABLES GLOBALES ==========
float temperature = 0; // Temperatura actual
float humidity = 0; // Humedad ambiente actual
int soilMoisture = 0; // Humedad del suelo (%)
bool pumpActive = false; // Estado de la bomba
unsigned long lastReadTime = 0; // Última lectura de sensores
unsigned long pumpStartTime = 0; // Tiempo de inicio del riego
// ========== SETUP - CONFIGURACIÓN INICIAL ==========
void setup() {
// Inicializar comunicación serial para debug
Serial.begin(115200);
Serial.println("=== Sistema de Invernadero Iniciando ===");
// Configurar pines de salida
pinMode(PUMP_PIN, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
pinMode(LED_YELLOW, OUTPUT);
pinMode(LED_RED, OUTPUT);
// Asegurar que la bomba esté apagada al inicio
digitalWrite(PUMP_PIN, LOW);
// Inicializar sensor DHT22
dht.begin();
// Inicializar pantalla OLED
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println("Error: No se encontró la pantalla OLED");
// Parpadear LED rojo para indicar error
while(1) {
digitalWrite(LED_RED, HIGH);
delay(500);
digitalWrite(LED_RED, LOW);
delay(500);
}
}
// Configurar pantalla
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("ArtificialRoot");
display.println("Invernadero v1.0");
display.println("");
display.println("Iniciando...");
display.display();
delay(2000);
Serial.println("Sistema iniciado correctamente");
}
// ========== LOOP PRINCIPAL ==========
void loop() {
unsigned long currentTime = millis();
// Leer sensores en intervalos regulares
if (currentTime - lastReadTime >= READ_INTERVAL) {
lastReadTime = currentTime;
readSensors();
checkConditions();
updateDisplay();
}
// Controlar tiempo de riego de la bomba
if (pumpActive && (currentTime - pumpStartTime >= PUMP_TIME)) {
stopPump();
}
}
// ========== FUNCIÓN: LEER SENSORES ==========
void readSensors() {
// Leer temperatura y humedad del DHT22
temperature = dht.readTemperature();
humidity = dht.readHumidity();
// Verificar si las lecturas son válidas
if (isnan(temperature) || isnan(humidity)) {
Serial.println("Error leyendo DHT22");
temperature = 0;
humidity = 0;
}
// Leer humedad del suelo (valor analógico)
int soilValue = analogRead(SOIL_SENSOR_PIN);
// Convertir a porcentaje (0-100%)
// Nota: Ajustar según calibración de tu sensor
// Valores típicos: seco = 4095 (aire), húmedo = 1500 (agua)
soilMoisture = map(soilValue, 4095, 1500, 0, 100);
soilMoisture = constrain(soilMoisture, 0, 100);
// Mostrar lecturas en serial para debug
Serial.println("--- Lecturas de Sensores ---");
Serial.print("Temperatura: "); Serial.print(temperature); Serial.println(" °C");
Serial.print("Humedad Amb: "); Serial.print(humidity); Serial.println(" %");
Serial.print("Humedad Suelo: "); Serial.print(soilMoisture); Serial.println(" %");
Serial.print("Bomba: "); Serial.println(pumpActive ? "ON" : "OFF");
}
// ========== FUNCIÓN: VERIFICAR CONDICIONES ==========
void checkConditions() {
bool criticalCondition = false;
bool warningCondition = false;
// Verificar temperatura crítica
if (temperature < TEMP_MIN - 5 || temperature > TEMP_MAX + 5) {
criticalCondition = true;
Serial.println("ALERTA: Temperatura crítica!");
}
// Verificar temperatura en alerta
else if (temperature < TEMP_MIN || temperature > TEMP_MAX) {
warningCondition = true;
Serial.println("ADVERTENCIA: Temperatura fuera del rango ideal");
}
// Verificar humedad ambiente crítica
if (humidity < HUMIDITY_MIN - 10 || humidity > HUMIDITY_MAX + 10) {
criticalCondition = true;
Serial.println("ALERTA: Humedad ambiente crítica!");
}
// Verificar humedad ambiente en alerta
else if (humidity < HUMIDITY_MIN || humidity > HUMIDITY_MAX) {
warningCondition = true;
Serial.println("ADVERTENCIA: Humedad ambiente fuera del rango ideal");
}
// Verificar humedad del suelo y activar riego si es necesario
if (soilMoisture < SOIL_DRY_THRESHOLD && !pumpActive) {
Serial.println("Suelo seco - Activando riego");
startPump();
warningCondition = true;
}
// Actualizar LEDs según las condiciones
updateLEDs(criticalCondition, warningCondition);
}
// ========== FUNCIÓN: ACTUALIZAR LEDs ==========
void updateLEDs(bool critical, bool warning) {
// Apagar todos los LEDs primero
digitalWrite(LED_GREEN, LOW);
digitalWrite(LED_YELLOW, LOW);
digitalWrite(LED_RED, LOW);
// Encender LED según estado
if (critical) {
digitalWrite(LED_RED, HIGH); // Condición crítica
} else if (warning || pumpActive) {
digitalWrite(LED_YELLOW, HIGH); // Advertencia o bomba activa
} else {
digitalWrite(LED_GREEN, HIGH); // Todo OK
}
}
// ========== FUNCIÓN: INICIAR BOMBA DE AGUA ==========
void startPump() {
pumpActive = true;
pumpStartTime = millis();
digitalWrite(PUMP_PIN, HIGH);
Serial.println("Bomba ACTIVADA");
}
// ========== FUNCIÓN: DETENER BOMBA DE AGUA ==========
void stopPump() {
pumpActive = false;
digitalWrite(PUMP_PIN, LOW);
Serial.println("Bomba DESACTIVADA");
}
// ========== FUNCIÓN: ACTUALIZAR PANTALLA OLED ==========
void updateDisplay() {
display.clearDisplay();
// Título
display.setTextSize(1);
display.setCursor(0, 0);
display.println("ArtificialRoot");
display.drawLine(0, 10, 128, 10, SSD1306_WHITE);
// Temperatura
display.setCursor(0, 14);
display.print("Temp: ");
display.print(temperature, 1);
display.println(" C");
// Humedad ambiente
display.setCursor(0, 24);
display.print("Hum: ");
display.print(humidity, 0);
display.println(" %");
// Humedad del suelo
display.setCursor(0, 34);
display.print("Suelo: ");
display.print(soilMoisture);
display.println(" %");
// Barra visual de humedad del suelo
int barWidth = map(soilMoisture, 0, 100, 0, 100);
display.drawRect(0, 44, 102, 8, SSD1306_WHITE);
display.fillRect(1, 45, barWidth, 6, SSD1306_WHITE);
// Estado de la bomba
display.setCursor(0, 54);
if (pumpActive) {
display.print("BOMBA: ON");
} else {
display.print("Sistema: OK");
}
display.display();
}