#include #include // ============================================ // XIAO ESP32C3 + A4988 + Sensor Humedad // LCD 1602 I2C + Finales de carrera // Sistema de riego por inmersion para orquideas // Ubicacion: Cuenca, Ecuador // ============================================ // A4988 #define STEP_PIN 4 #define DIR_PIN 5 #define EN_PIN 10 // Sensor humedad #define SENSOR_PIN 2 // LCD I2C #define SDA_PIN 6 #define SCL_PIN 7 // Finales de carrera #define LIMIT_UP 8 #define LIMIT_DOWN 9 LiquidCrystal_I2C lcd(0x27, 16, 2); // ======================================================= // NIVELES DE HUMEDAD PARA ORQUIDEA EN SUSTRATO // Cuenca - Ecuador // // IMPORTANTE: // En la mayoria de sensores capacitivos con ESP32: // valor alto = sustrato mas seco // valor bajo = sustrato mas humedo // // Para orquideas no se recomienda mantener el sustrato // constantemente mojado, porque puede provocar pudricion // de raices. El riego se activa solo cuando el sustrato // esta bastante seco. // ======================================================= const int HUMEDAD_MUY_HUMEDO = 1500; // Recien regado o muy mojado const int HUMEDAD_ADECUADA = 2000; // Humedad aceptable const int HUMEDAD_SECANDOSE = 2500; // Empieza a secarse const int HUMEDAD_SECO = 2800; // Activar riego // Umbrales principales del sistema const int UMBRAL_ACTIVAR_RIEGO = 2800; // Si supera este valor, riega const int UMBRAL_HUMEDAD_OK = 2000; // Valor de referencia despues del riego // Variable de control para evitar riegos repetidos bool cicloRiegoEnProceso = false; // =============================== // AJUSTE DEL MOTOR // Mayor numero = mas lento y mas fuerza // Menor numero = mas rapido // =============================== int velocidadInicio = 16000; // arranque suave int velocidadMaximaSubida = 8500; // subida prudente int velocidadMaximaBajada = 12000; // bajada mas lenta para evitar trabas int aceleracionSubida = 15; int aceleracionBajada = 10; // Tiempo de riego por inmersion unsigned long tiempoRiego = 60000; void setup() { Serial.begin(115200); delay(500); pinMode(STEP_PIN, OUTPUT); pinMode(DIR_PIN, OUTPUT); pinMode(EN_PIN, OUTPUT); pinMode(LIMIT_UP, INPUT_PULLUP); pinMode(LIMIT_DOWN, INPUT_PULLUP); digitalWrite(EN_PIN, LOW); // LOW habilita A4988 Wire.begin(SDA_PIN, SCL_PIN); lcd.init(); lcd.backlight(); lcd.clear(); lcd.setCursor(0, 0); lcd.print("Orquideario"); lcd.setCursor(0, 1); lcd.print("Sistema listo"); Serial.println("================================="); Serial.println("SISTEMA INICIADO"); Serial.println("Riego por inmersion para orquidea"); Serial.println("Ubicacion: Cuenca, Ecuador"); Serial.println("================================="); delay(2000); } void loop() { int humedad = leerHumedadPromedio(); Serial.print("Lectura humedad: "); Serial.println(humedad); mostrarEstado(humedad); // ======================================================= // LOGICA DE RIEGO // // Para orquidea: // No se riega cuando el sustrato esta solamente secandose. // Se riega cuando el sensor indica condicion SECA o MUY SECA. // ======================================================= if (humedad >= UMBRAL_ACTIVAR_RIEGO && !cicloRiegoEnProceso) { cicloRiegoEnProceso = true; Serial.println("ESTADO: SUSTRATO SECO"); Serial.println("ACCION: INICIA CICLO DE RIEGO"); lcd.clear(); lcd.setCursor(0, 0); lcd.print("Estado: SECO"); lcd.setCursor(0, 1); lcd.print("Inicia riego"); delay(1000); subirHastaTope(); regar(); bajarHastaTope(); lcd.clear(); lcd.setCursor(0, 0); lcd.print("Ciclo terminado"); lcd.setCursor(0, 1); lcd.print("Midiendo..."); delay(3000); // Se libera el ciclo despues de terminar. // En la siguiente lectura se vuelve a evaluar la humedad. cicloRiegoEnProceso = false; } else { Serial.println("HUMEDAD SUFICIENTE O EN MONITOREO -> NO RIEGA"); delay(3000); } } // ============================================ // Leer humedad promedio // Se promedian 10 lecturas para reducir ruido // ============================================ int leerHumedadPromedio() { long suma = 0; for (int i = 0; i < 10; i++) { suma += analogRead(SENSOR_PIN); delay(20); } return suma / 10; } // ============================================ // Mostrar estado en LCD // ============================================ void mostrarEstado(int humedad) { lcd.clear(); lcd.setCursor(0, 0); if (humedad < HUMEDAD_MUY_HUMEDO) { lcd.print("Muy humedo"); Serial.println("Estado del sustrato: MUY HUMEDO / RECIEN REGADO"); } else if (humedad >= HUMEDAD_MUY_HUMEDO && humedad < HUMEDAD_ADECUADA) { lcd.print("Humedad OK"); Serial.println("Estado del sustrato: HUMEDAD ADECUADA"); } else if (humedad >= HUMEDAD_ADECUADA && humedad < HUMEDAD_SECANDOSE) { lcd.print("Secandose"); Serial.println("Estado del sustrato: SECANDOSE"); } else if (humedad >= HUMEDAD_SECANDOSE && humedad < HUMEDAD_SECO) { lcd.print("Casi seco"); Serial.println("Estado del sustrato: CASI SECO - SOLO MONITOREAR"); } else { lcd.print("Seco: regar"); Serial.println("Estado del sustrato: SECO - ACTIVAR RIEGO"); } lcd.setCursor(0, 1); lcd.print("Valor:"); lcd.print(humedad); } // ============================================ // Final de carrera con antirrebote // COM -> GND // NO -> PIN // Sin presionar = HIGH // Presionado = LOW // ============================================ bool finalPresionado(int pin) { if (digitalRead(pin) == LOW) { delay(40); if (digitalRead(pin) == LOW) { return true; } } return false; } // ============================================ // Subir hasta final superior // ============================================ void subirHastaTope() { Serial.println("SUBIENDO HASTA FINAL SUPERIOR"); lcd.clear(); lcd.setCursor(0, 0); lcd.print("Subiendo..."); lcd.setCursor(0, 1); lcd.print("Buscando tope"); digitalWrite(EN_PIN, LOW); digitalWrite(DIR_PIN, HIGH); delay(200); int velocidadActual = velocidadInicio; while (!finalPresionado(LIMIT_UP)) { moverUnPaso(velocidadActual); if (velocidadActual > velocidadMaximaSubida) { velocidadActual -= aceleracionSubida; } } detenerMotor(); Serial.println("FINAL SUPERIOR ACTIVADO"); lcd.clear(); lcd.setCursor(0, 0); lcd.print("Arriba OK"); lcd.setCursor(0, 1); lcd.print("Tope superior"); delay(1500); } // ============================================ // Regar por inmersion // ============================================ void regar() { Serial.println("REGANDO POR INMERSION"); lcd.clear(); lcd.setCursor(0, 0); lcd.print("Regando..."); lcd.setCursor(0, 1); lcd.print("Tiempo "); lcd.print(tiempoRiego / 1000); lcd.print(" s"); delay(tiempoRiego); } // ============================================ // Bajar hasta final inferior // ============================================ void bajarHastaTope() { Serial.println("BAJANDO HASTA FINAL INFERIOR"); lcd.clear(); lcd.setCursor(0, 0); lcd.print("Bajando..."); lcd.setCursor(0, 1); lcd.print("Buscando tope"); digitalWrite(EN_PIN, LOW); digitalWrite(DIR_PIN, LOW); delay(200); int velocidadActual = velocidadInicio; while (!finalPresionado(LIMIT_DOWN)) { moverUnPaso(velocidadActual); if (velocidadActual > velocidadMaximaBajada) { velocidadActual -= aceleracionBajada; } } detenerMotor(); Serial.println("FINAL INFERIOR ACTIVADO"); lcd.clear(); lcd.setCursor(0, 0); lcd.print("Abajo OK"); lcd.setCursor(0, 1); lcd.print("Tope inferior"); delay(1500); } // ============================================ // Movimiento del motor // ============================================ void moverUnPaso(int velocidad) { digitalWrite(STEP_PIN, HIGH); delayMicroseconds(8); digitalWrite(STEP_PIN, LOW); delayMicroseconds(velocidad); } // ============================================ // Detener motor // ============================================ void detenerMotor() { digitalWrite(STEP_PIN, LOW); delay(300); }