Invernadero inteligente con Arduino: sensores, riego y código
Invernadero inteligente con Arduino: mide con DHT22, humedad del suelo y LDR; controla ventilador, calefactor, bomba y LED con relé y MOSFET, histéresis y LCD.
Un invernadero inteligente con Arduino es un sistema de automatización en el que un Arduino o un ESP32 mide la temperatura, la humedad del aire, la humedad del suelo y la luz del invernadero, y pone en marcha por sí solo el ventilador, el calefactor, la bomba de agua y las luces de cultivo. Es uno de los temas favoritos de las ferias de ciencias, los concursos escolares y las clases de tecnología, porque el resultado se ve: cuando la tierra se seca, arranca la bomba; cuando hace calor dentro, gira el ventilador; al anochecer, se encienden los LED. Además, entre una maqueta hecha con una caja de almacenaje y un mini invernadero de verdad en el balcón casi no hay diferencia de circuito ni de código.
En esta guía empezamos por lo que de verdad necesitan las plantas y después montamos la maqueta paso a paso: la lista de materiales y el coste aproximado, la conexión de los sensores (DHT22, sensor capacitivo de humedad del suelo, LDR) y de los actuadores (ventilador, calefactor, bomba y LED mediante relés y un MOSFET), el plan de alimentación y la tabla de pines. Luego explicamos la lógica de umbral e histéresis que evita que los relés hagan clic sin parar, el código Arduino completo sin delay() y la pantalla LCD de 16x2. En las últimas secciones verás cómo vigilar el invernadero desde el móvil con un ESP32, una versión en MicroPython para la Raspberry Pi Pico, consejos para presentaciones escolares y ferias de ciencias, y los errores más comunes.





¿Cómo funciona un invernadero inteligente?
Todo invernadero inteligente sigue el mismo ciclo de cuatro pasos: medir, decidir, actuar y mostrar. Los sensores miden el entorno: ¿qué temperatura hay dentro?, ¿qué humedad tiene el aire?, ¿se ha secado la tierra?, ¿hay luz suficiente? El Arduino compara esos valores con las reglas que tú fijas: «si la temperatura pasa de 30 °C, enciende el ventilador», «si la humedad del suelo baja del 35 %, riega». Los actuadores ejecutan la decisión: los relés encienden y apagan el ventilador, el calefactor y la bomba, y un MOSFET regula el brillo de la tira LED. Por último, la LCD y el móvil te muestran el estado del invernadero en ese momento. Los ordenadores de control climático de los invernaderos comerciales funcionan exactamente con este ciclo; solo usan más sensores y motores más grandes.
| ¿Qué se mide? | Sensor | Actuador | Regla (ejemplo) |
|---|---|---|---|
| Temperatura del aire | DHT22 | Ventilador y calefactor | El ventilador arranca a 30 °C y el calefactor se enciende a 16 °C |
| Humedad relativa | DHT22 | Ventilador | El ventilador arranca al 85 % y se para al 75 % |
| Humedad del suelo | Sensor capacitivo de humedad del suelo | Mini bomba sumergible | Por debajo del 35 % riega en pulsos de 3 segundos |
| Luz | LDR | Tira LED (MOSFET) | Al oscurecer, los LED se encienden como máximo 4 horas |
| Agua del depósito | Interruptor de flotador | Bloqueo de la bomba | Si el depósito está vacío, la bomba nunca arranca |
¿Qué necesitan las plantas? Temperatura, humedad y luz objetivo
Antes de escribir las reglas necesitas saber a qué apuntas. La mayoría de las plantas quieren temperaturas distintas de día y de noche; refrescar un poco por la noche ayuda a la planta a descansar. Si la humedad del aire se mantiene mucho tiempo por encima del 85 %, el agua se condensa sobre las hojas y las enfermedades por hongos, como el moho y el mildiu, se extienden rápido; por eso, en un invernadero el ventilador no solo sirve para refrescar, también para sacar la humedad. En cuanto a la luz, la mayoría de los plantones de hortalizas quieren 14–16 horas al día, pero también necesitan un periodo de oscuridad de al menos 6–8 horas; dejar los LED encendidos toda la noche hace más daño que bien. Los valores de abajo son una buena guía para empezar; ajústalos según lo que cultives.
| Planta | Día | Noche | Humedad relativa | Nota |
|---|---|---|---|---|
| Tomate | 21–27 °C | 15–18 °C | 60–75 % | Por encima de 32 °C pueden caerse las flores |
| Pimiento | 21–29 °C | 16–20 °C | 60–75 % | Más sensible al frío que el tomate |
| Lechuga y espinaca | 15–20 °C | 10–15 °C | 60–70 % | Con calor y días largos se espiga pronto |
| Fresa | 18–24 °C | 10–13 °C | 60–75 % | Con humedad alta aumenta el riesgo de podredumbre gris |
| Plantones y germinación | 20–25 °C | 18–20 °C | 70–85 % | La temperatura del sustrato importa más que la del aire |
Cómo construir la maqueta del invernadero
La carcasa más fácil y barata es una caja de plástico transparente con tapa: una de 40–60 litros basta para dos o tres macetas, es impermeable y se agujerea fácilmente con un cúter o una punta caliente. Si quieres un aspecto más realista, monta un armazón con listones de madera y fórralo con planchas transparentes de metacrilato o PETG de 2–3 mm; un invernadero con tejado a dos aguas luce mucho más en una presentación. Unos 50×35×40 cm es un buen tamaño para empezar. Elijas la carcasa que elijas, la tapa o una pared lateral tiene que poder abrirse: vas a meter la mano para regar, mover el sensor y ordenar los cables.
- Electrónica fuera: Dentro del invernadero hay mucha humedad y por la mañana el agua se condensa en las paredes. Coloca el Arduino, el módulo de relés y la fuente de alimentación en una caja aparte fuera del invernadero; dentro solo van los sensores, el ventilador, el calefactor, la manguera de la bomba y los LED.
- Ventilación: El aire caliente sube, así que monta el ventilador en la parte alta de una de las paredes cortas, sacando el aire hacia fuera. En la parte baja de la pared opuesta abre una entrada de aire del tamaño del ventilador y tápala con malla mosquitera; así el aire fresco entra por abajo y el aire caliente y húmedo sale por arriba.
- Calefactor: Monta el calefactor cerca del suelo, sobre una chapa de aluminio o un azulejo, a al menos 5 cm de las paredes de plástico y de las plantas. El aire caliente recorre todo el invernadero al subir.
- Iluminación LED: Pega la tira LED en la cara interior de la tapa, a 15–30 cm por encima de las plantas. Si la pegas sobre un perfil fino de aluminio, disipa mejor el calor y el adhesivo no se despega con el tiempo.
- Sensores: Coloca el DHT22 a la altura de las plantas, donde no le dé el sol ni la luz LED directamente. Entierra el sensor de humedad en la zona de las raíces, lejos del borde de la maceta. La LDR no debe ver los LED: ponla fuera del invernadero, en el tejado, mirando al cielo.
- Depósito de agua: Un recipiente de 1–2 litros basta para la bomba y el flotador. Pon platos de drenaje bajo las macetas; el agua sobrante se queda en el plato y el fondo de la caja no se inunda.
Lista de materiales y coste
| Pieza | Cant. | Función | Nivel de precio |
|---|---|---|---|
| Arduino Uno R3 o ESP32 DevKit | 1 | El cerebro del invernadero | Medio |
| Módulo de temperatura y humedad DHT22 (AM2302) | 1 | Temperatura y humedad del aire | Bajo |
| Sensor capacitivo de humedad del suelo (v1.2) | 1 (uno por maceta) | Humedad del suelo | Bajo |
| LDR (GL5528) y resistencia de 10 kΩ | 1 + 1 | Luz ambiente | Muy bajo |
| Interruptor de flotador para nivel de agua | 1 | Bloquea la bomba con el depósito vacío | Bajo |
| Módulo de 4 relés de 5 V (con optoacoplador y JD-VCC) | 1 | Ventilador, calefactor y bomba | Bajo |
| MOSFET de canal N de nivel lógico (IRLZ44N), resistencias de 220 Ω y 100 kΩ | 1 juego | Brillo de la tira LED (PWM) | Muy bajo |
| Mini bomba sumergible de 3–6 V y 1 m de manguera de silicona | 1 | Riego | Bajo |
| Ventilador de PC de 12 V y 80 mm | 1–2 | Ventilación y extracción de humedad | Bajo |
| Calefactor PTC de 12 V (10–30 W) y fusible térmico | 1 + 1 | Calefacción | Bajo |
| Tira LED de 12 V (de cultivo o blanco frío), aprox. 1 m | 1 | Iluminación complementaria | Bajo |
| Pantalla LCD I2C de 16x2 | 1 | Pantalla de estado | Bajo – medio |
| Fuente de 12 V 5 A y convertidor reductor LM2596 | 1 + 1 | Alimentación | Medio |
| Diodo 1N5819 o 1N4007, condensador de 470 µF, bornas, cables dupont | – | Protección y conexiones | Muy bajo |
| Caja transparente con tapa o planchas de metacrilato, macetas, platos de drenaje | – | Estructura del invernadero | Medio |
Los precios cambian mucho según la tienda, así que mejor un presupuesto aproximado que cifras exactas. Sin contar la placa, la versión básica (DHT22, sensor de humedad del suelo, LDR, relé de 2 canales, bomba y ventilador de 5 V) suele costar unos 25–40 €. La versión completa, con calefactor, tira LED, LCD, fuente de 12 V, LM2596, flotador y carcasa, suele rondar los 70–120 €; la mayor parte del presupuesto se va en la placa, la fuente, la pantalla y la carcasa. Si tienes un kit de inicio de Arduino, ya cuentas con la placa, la protoboard, los cables, la LDR, las resistencias y muchas veces la LCD. Con poco presupuesto, deja el calefactor y los LED para una segunda fase: el riego y la ventilación por sí solos ya son un proyecto útil y funcional.
Sensores: DHT22, sensor de humedad del suelo y LDR
Para el invernadero te recomendamos el DHT22 (AM2302). Cuesta algo más que el DHT11, pero mide la temperatura de −40 a 80 °C con ±0,5 °C y la humedad en todo el rango de 0–100 % con unos ±2 %; el DHT11 solo es preciso a ±2 °C entre 0 y 50 °C y no detecta humedades por encima del 90 %. Como un invernadero puede superar el 90 % de humedad por la mañana y bajar de cero en invierno, el DHT22 encaja mucho mejor. El sensor da como máximo una lectura cada 2 segundos, así que el código lo lee cada 2,5 segundos. Si le da directamente el sol o la luz de los LED, su carcasa se calienta y marca varios grados más que el aire; una tapa blanca con agujeros que le dé sombra lo evita. La conexión y la librería se explican en el artículo sensor de temperatura y humedad DHT11 y DHT22.

Para la humedad del suelo, elige un sensor capacitivo. Los sensores resistivos baratos (YL-69, FC-28) tienen dos puntas metálicas en contacto directo con la tierra y, si están alimentados todo el tiempo, se corroen en pocas semanas por electrólisis; no sirven para un sistema que funcionará meses. En el sensor capacitivo el electrodo está recubierto y ningún metal toca la tierra. Su salida es analógica y baja a medida que la tierra se moja: en un Uno, un sensor al aire marca unos 500–600 y sumergido en agua unos 250–300. Estos números cambian de un sensor a otro, así que antes de cargar el programa principal lee en el monitor serie los valores de tu sensor al aire y en agua y escríbelos en las constantes SOIL_DRY_RAW y SOIL_WET_RAW. El código convierte esos dos valores en 0–100 % con map(). Tienes más detalles en el artículo sensor de humedad del suelo.
Para la luz basta con la LDR (GL5528) de tu kit. Conéctala como divisor de tensión con una resistencia de 10 kΩ: 5V → LDR → A1 → 10 kΩ → GND. Al aumentar la luz baja la resistencia de la LDR y sube el valor en A1; el código también lo convierte en 0–100 % a partir de dos números que mides a oscuras y a pleno sol. Una LDR no es un luxómetro preciso, pero responde con fiabilidad a la pregunta «¿ha oscurecido, está nublado, hace sol?». Si quieres medir la luz con más precisión, puedes pasar a un sensor de luz I2C como el BH1750. Usa la herramienta Divisor de tensión para el cálculo y el artículo sensor de luz LDR para los detalles.
Actuadores: ¿relé o MOSFET?
Un pin de Arduino solo puede dar unos 20 mA, mientras que el ventilador, el calefactor, la bomba y la tira LED consumen cientos de miliamperios o incluso varios amperios. Por eso cada carga se controla a través de un relé o de un MOSFET. Un relé es un interruptor mecánico: enciende y apaga una carga en un circuito totalmente separado, sea de alterna o de continua, pero solo puede encender o apagar, hace clic en cada cambio y sus contactos tienen una vida limitada. Un MOSFET es un interruptor electrónico silencioso; solo funciona con cargas de continua, pero puede conmutar cientos de veces por segundo y por eso permite regular brillo y velocidad con PWM. En este proyecto usamos un módulo de 4 relés para el calefactor, el ventilador y la bomba, y un MOSFET para el brillo de la tira LED.
| Carga | Valor típico | Controlador | ¿Por qué? |
|---|---|---|---|
| Calefactor PTC de 12 V | 10–30 W, aprox. 1–2,5 A | Relé (IN2) | Corriente alta, solo hace falta encender y apagar |
| Ventilador de 12 V y 80 mm | 0,1–0,2 A | Relé (IN1) o MOSFET | El relé es sencillo; con un MOSFET también puedes regular la velocidad por PWM |
| Mini bomba sumergible de 3–6 V | 0,1–0,25 A, más al arrancar | Relé (IN3) o MOSFET con diodo | Funciona en pulsos cortos; hay que aislar el ruido del motor |
| Tira LED de 12 V (5050, 60 LED/m) | Unos 1,2 A por metro | MOSFET de nivel lógico | Regulación de brillo por PWM y funcionamiento silencioso |

Según la altura de la manguera, una mini bomba sumergible da aproximadamente 1–1,5 litros por minuto, así que un pulso de 3 segundos aporta 50–75 mL de agua a la maceta. Por eso no hacemos funcionar la bomba sin parar «hasta que la tierra esté húmeda», sino en pulsos cortos: riega 3 segundos, espera 60 segundos a que el agua se reparta por la tierra y vuelve a medir. Si el sensor se leyera otra vez antes de que le llegara el agua, la tierra seguiría pareciendo seca y la bomba funcionaría hasta desbordar la maceta. Una bomba que gira en seco se calienta y se estropea en pocos minutos; el flotador del depósito la bloquea cuando se acaba el agua. Cada vez que el motor se para, su bobina genera un pico de tensión inversa; un diodo en inversa en paralelo con la bomba (diodo de rueda libre 1N5819 o 1N4007, con la franja hacia el +) protege de ese ruido tanto el contacto del relé como el Arduino. La elección de la bomba y las formas de controlarla se explican en el artículo mini bomba de agua.
Para la calefacción usa un calefactor PTC de 12 V. En los elementos cerámicos PTC (coeficiente de temperatura positivo) la resistencia aumenta al calentarse, así que su temperatura superficial se limita sola a un valor fijo; es mucho más seguro que un hilo de resistencia al rojo vivo. En una carcasa de plástico, elige un modelo de baja temperatura cuya superficie se limite a unos 80 °C. Un calefactor de 24 W consume 2 A a 12 V según P = V × I; en los primeros segundos, en frío, la corriente es algo mayor. Para la iluminación puedes usar una tira LED de cultivo rojo-azul o una tira LED de blanco frío; las plantas aprovechan sobre todo la luz roja (unos 660 nm) y azul (unos 450 nm) en la fotosíntesis. Controla la tira con un MOSFET de nivel lógico como el IRLZ44N: la puerta (gate) va a D9 a través de una resistencia de 220 Ω y a GND con una de 100 kΩ; el + de la tira va a 12 V, el − al drenador del MOSFET y la fuente a GND. Los módulos MOSFET estándar como el IRF520 no conducen del todo con 5 V en la puerta y se calientan con cargas de varios amperios; tienes los detalles en el artículo cómo usar un MOSFET con Arduino. Para calcular corriente y potencia puedes usar la herramienta Ley de Ohm.
Plan de alimentación: líneas de 12 V y 5 V
Los problemas más frecuentes de los invernaderos inteligentes («el Arduino se reinicia cuando arranca la bomba», «salen caracteres raros en la LCD») vienen de la alimentación. La solución más limpia es sacar dos líneas de una única fuente de 12 V 5 A. La línea de 12 V alimenta directamente el calefactor, los ventiladores y la tira LED, y además va al conector DC del Arduino Uno. Esos mismos 12 V entran en un convertidor reductor LM2596 ajustado a 5 V; las bobinas de los relés, la bomba y la LCD se alimentan de esa línea de 5 V. Si quitas el puente JD-VCC del módulo de relés, la corriente de las bobinas sale entera del LM2596 y el lado del Arduino solo enciende los LED de los optoacopladores. Los sensores (DHT22, sensor de humedad del suelo y divisor de la LDR), en cambio, aliméntalos desde el pin 5V del propio Arduino: analogRead() mide respecto a los 5 V de la placa, así que alimentar los sensores analógicos con esa misma tensión mantiene las lecturas estables. Une siempre los GND de la fuente, del LM2596 y del Arduino.
| Carga | Línea | Corriente aprox. |
|---|---|---|
| Calefactor PTC (24 W) | 12 V | 2 A (más en el primer instante, en frío) |
| Tira LED (1 m) | 12 V | 1–1,2 A |
| Dos ventiladores de 80 mm | 12 V | 0,2–0,4 A |
| Arduino Uno y sensores (por el conector DC) | 12 V | Unos 0,06–0,08 A |
| Entrada del LM2596 (con 1 A en el lado de 5 V) | 12 V | Unos 0,5 A |
| Módulo de relés (tres relés activados) | 5 V | 0,2–0,25 A |
| Mini bomba sumergible | 5 V | 0,1–0,25 A (más al arrancar) |
| LCD I2C (retroiluminación encendida) | 5 V | 20–30 mA |
Aunque todo funcione a la vez, la corriente total ronda los 4–4,5 A, así que una fuente de 12 V 5 A (60 W) deja margen suficiente. En la práctica el calefactor y el ventilador de temperatura nunca funcionan a la vez, porque entre sus umbrales hay un hueco amplio. Antes de conectar el LM2596 al circuito, mide su salida con el multímetro y ajústala a 5,0 V con su potenciómetro; tal como viene de fábrica puede dejar pasar casi los 12 V completos. Puedes alimentar el Arduino por USB mientras cargas el código y por el conector DC una vez instalado; tener los dos conectados a la vez no es problema para el Uno. Más detalles en la guía alimentación de Arduino.
Conexiones y tabla de pines
La tabla siguiente muestra los pines que usan los tres programas de esta guía. En el Uno conectamos el MOSFET que controla la tira LED a D9, que admite PWM; en el Uno solo hay PWM en D3, D5, D6, D9, D10 y D11. El flotador y los relés no necesitan PWM. Como la LCD solo usa el bus I2C (A4 y A5), ocupa muy pocos pines; en el Uno quedan libres A2, A3 y algunos pines digitales para un segundo sensor de suelo o un pulsador. En el ESP32 conectamos los sensores analógicos a pines ADC1 (GPIO 34 y 35), que siguen funcionando con el Wi-Fi activado. Puedes ver la distribución de pines de tu placa en la herramienta Pinout.
| Conexión | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| DHT22 DATA | D2 | GPIO 4 | GP15 |
| Flotador (el otro terminal a GND) | D3 | GPIO 13 | GP14 |
| Relé IN3: bomba | D4 | GPIO 25 | GP18 |
| Relé IN1: ventilador | D7 | GPIO 26 | GP16 |
| Relé IN2: calefactor | D8 | GPIO 27 | GP17 |
| Puerta del MOSFET (con 220 Ω): LED | D9 | GPIO 18 | GP19 |
| AOUT del sensor de humedad del suelo | A0 | GPIO 34 | GP26 |
| Punto medio del divisor de la LDR | A1 | GPIO 35 (divisor desde 3V3) | GP27 (divisor desde 3V3) |
| LCD I2C SDA / SCL | A4 / A5 | GPIO 21 / 22 | GP4 / GP5 |
| Alimentación de los sensores | 5V | 3V3 | 3V3 |
Umbral e histéresis: ¿por qué traquetean los relés?
Imagina que controlas el ventilador con un único umbral: «enciéndelo si la temperatura pasa de 30 °C y, si no, apágalo». Cuando la temperatura oscila entre 29,9 y 30,1 °C (algo muy frecuente por el ruido del sensor), el relé se enciende y se apaga en cada lectura. Se desgastan los contactos del relé y el motor del ventilador, y con el calefactor el cableado y la fuente sufren en cada encendido sin necesidad. La solución es la histéresis: usas dos umbrales distintos para encender y para apagar. El ventilador arranca a 30 °C, pero solo se para cuando la temperatura baja a 27 °C. En esa zona muerta de 3 grados, el sistema mantiene su último estado. Los termostatos de las calderas y los frigoríficos funcionan con la misma lógica.
| Control | Se enciende | Se apaga | Zona muerta |
|---|---|---|---|
| Ventilador (temperatura) | ≥ 30 °C | ≤ 27 °C | 3 °C |
| Ventilador (humedad) | ≥ 85 % | ≤ 75 % | 10 % |
| Calefactor | ≤ 16 °C | ≥ 19 °C | 3 °C |
| Riego | Suelo ≤ 35 % | Suelo ≥ 50 % | 15 % |
| Iluminación LED | Luz ≤ 25 % | Luz ≥ 35 % o al cumplirse 4 horas | 10 % |
El ancho de la zona muerta es un equilibrio: si es demasiado estrecha, el relé sigue conmutando a menudo; si es demasiado ancha, la temperatura se aleja demasiado del objetivo. Unos 2–3 °C en temperatura, 8–10 % en humedad y 10–15 % en humedad del suelo son un buen punto de partida. Además, dejamos un hueco amplio entre los 19 °C a los que se apaga el calefactor y los 30 °C a los que arranca el ventilador, para que nunca trabajen uno contra otro. En el código, la histéresis vive en una pequeña función hysteresis() para no tener que escribirla de nuevo en cada control: si el umbral de encendido es mayor que el de apagado significa «enciende al subir» (ventilador); si es menor, «enciende al bajar» (calefactor, riego, LED). Si además quieres suavizar los picos de los datos del sensor, echa un vistazo al artículo filtrado de datos de sensores.
Estructura del código: medir, decidir, actuar y mostrar
La regla más importante del código del invernadero es que ninguna tarea espere a otra. Si después de la bomba esperaras un minuto entero con delay(60000), durante ese tiempo ni funcionaría el ventilador ni se actualizaría la pantalla. Por eso el código está formado por pequeñas funciones que miden el tiempo con millis(), cada una responsable de una sola tarea; loop() las llama por turnos miles de veces por segundo y cada función solo hace algo cuando le toca. El riego es una pequeña máquina de estados de tres estados: esperar (IDLE), regar (PUMPING) y dejar que el agua se absorba (SOAKING). Todos los ajustes están como constantes al principio del código, así que no tienes que buscar por todo el programa para cambiar un umbral. Encontrarás la lógica de este enfoque en los artículos millis() y delay() y máquina de estados en Arduino. El código incluye además estas medidas de seguridad:
- Arranque seguro: El valor de «apagado» se escribe en los pines de los relés antes de llamar a pinMode(); así los relés no se activan ni un instante mientras arranca el Arduino.
- Fallo del sensor: Si no se puede leer el DHT22 tres veces seguidas, el código pasa a un estado seguro: apaga el calefactor, enciende el ventilador y muestra «Sensor hatasi» (error de sensor) en la LCD. Un sistema que no conoce la temperatura nunca debe dejar el calefactor encendido.
- Protección del depósito: Cuando el flotador indica que el depósito está vacío, la bomba ni siquiera arranca y, si estaba funcionando, se para al instante.
- Protección contra desbordamiento: Si en un ciclo de riego la humedad del suelo no llega al 50 % tras seis pulsos, puede que el sensor se haya salido de la tierra o que la manguera se haya soltado; el código detiene el riego y muestra «Sulama hatasi» (fallo de riego). El aviso desaparece solo cuando la tierra vuelve a estar húmeda.
- Registro de datos: Cada 10 segundos el código escribe las lecturas en el monitor serie como una línea separada por comas (CSV); puedes copiar esas líneas y hacer gráficas en Excel.
Montaje y prueba paso a paso
- Prepara la carcasa: abre los agujeros del ventilador, la entrada de aire, la manguera y los cables, y coloca la caja de la electrónica fuera del invernadero.
- Monta las líneas de alimentación: conecta la fuente de 12 V a una borna, ajusta el LM2596 a 5,0 V con el multímetro y une todos los GND.
- Conecta la LCD I2C y escribe un texto en ella. Si no muestra nada, prueba el programa escáner y el potenciómetro de contraste del artículo LCD I2C.
- Añade los sensores de uno en uno y pruébalos en el monitor serie: apunta las lecturas del DHT22, el valor en bruto del sensor de suelo al aire y en agua, y el valor de la LDR a oscuras y al sol.
- Prueba primero los relés sin carga, fijándote en sus LED y en el clic. Después conecta el ventilador, la bomba (con el depósito lleno) y, por último, el calefactor.
- Conecta la tira LED a través del MOSFET y cambia el valor de analogWrite() para ver cómo cambia el brillo.
- Carga el programa principal, escribe tus valores de calibración en las constantes y deja el sistema funcionando unas horas bajo vigilancia: ¿riega cuando no toca, hay algún relé que no para de hacer clic o alguna pieza que se calienta demasiado?
Código completo para Arduino Uno
Para compilar el código, instala desde el Gestor de librerías la DHT sensor library de Adafruit (junto con su dependencia, Adafruit Unified Sensor) y la librería LiquidCrystal I2C de Frank de Brabander; Wire ya viene con el IDE de Arduino. La dirección de la LCD suele ser 0x27, y 0x3F en algunos módulos. Antes de cargarlo, escribe los valores de calibración que mediste en las constantes SOIL_DRY_RAW, SOIL_WET_RAW, LDR_DARK_RAW y LDR_BRIGHT_RAW. Si tu flotador se abre con el agua en lugar de cerrarse, pon TANK_OK en HIGH; si tu módulo de relés es activo a nivel alto, intercambia los valores de RELAY_ON y RELAY_OFF.
1#include <Wire.h>
2#include <LiquidCrystal_I2C.h>
3#include <DHT.h>
4
5// Pinler / Pins
6const byte DHT_PIN = 2, FLOAT_PIN = 3, PUMP_RELAY = 4, FAN_RELAY = 7, HEATER_RELAY = 8;
7const byte LED_PIN = 9; // MOSFET gate (PWM)
8const byte SOIL_PIN = A0, LDR_PIN = A1;
9
10// Kalibrasyon: kendi ölçümlerini yaz / Calibration: write your own readings
11const int SOIL_DRY_RAW = 520, SOIL_WET_RAW = 260; // havada ve suda / in air and in water
12const int LDR_DARK_RAW = 60, LDR_BRIGHT_RAW = 900; // karanlık ve güneş / dark and sunlight
13
14// Eşikler (açma, kapama) / Thresholds (on, off)
15const float FAN_ON_C = 30, FAN_OFF_C = 27; // sıcaklıkta fan / fan on heat
16const float HUM_ON = 85, HUM_OFF = 75; // nemde fan / fan on humidity
17const float HEAT_ON_C = 16, HEAT_OFF_C = 19; // ısıtıcı / heater
18const float SOIL_DRY = 35, SOIL_OK = 50; // sulama başla, dur / start, stop watering
19const float DARK_ON = 25, DARK_OFF = 35; // ışık % / light %
20
21// Zamanlar / Timing
22const unsigned long READ_MS = 2500; // DHT22: en az 2 sn / at least 2 s
23const unsigned long PUMP_MS = 3000; // tek sulama darbesi / one watering pulse
24const unsigned long SOAK_MS = 60000; // suyun yayılması / let the water soak in
25const byte MAX_PULSES = 6; // fazlası arıza / more means a fault
26const unsigned long EXTRA_LIGHT_MS = 4UL * 3600000UL; // günü 4 saat uzat / extend the day by 4 h
27const byte LED_LEVEL = 200; // LED parlaklığı 0-255 / LED brightness 0-255
28const byte RELAY_ON = LOW, RELAY_OFF = HIGH; // aktif LOW röle / active-LOW relay
29const byte TANK_OK = LOW; // şamandıra suyla kapanır / float closes with water
30
31DHT dht(DHT_PIN, DHT22);
32LiquidCrystal_I2C lcd(0x27, 16, 2);
33
34float tempC = NAN, hum = NAN, soil = 0, light = 0;
35byte dhtErrors = 0, pulses = 0, page = 0;
36bool fanByTemp = false, fanByHum = false, fanOn = false, heaterOn = false;
37bool needWater = false, pumpFault = false, isDark = false, ledOn = false, tankOk = true;
38unsigned long lastRead = 0, lastLcd = 0, lastLog = 0, pumpAt = 0, darkSince = 0;
39
40enum PumpState { IDLE, PUMPING, SOAKING };
41PumpState pumpState = IDLE;
42
43// onAt > offAt: yükselince aç (fan) / turn on when rising (fan)
44// onAt < offAt: düşünce aç (ısıtıcı) / turn on when falling (heater)
45bool hysteresis(bool state, float value, float onAt, float offAt) {
46 if (onAt > offAt) {
47 if (value >= onAt) return true;
48 if (value <= offAt) return false;
49 } else {
50 if (value <= onAt) return true;
51 if (value >= offAt) return false;
52 }
53 return state; // ölü bölgede durumu koru / keep state in the dead band
54}
55
56void setRelay(byte pin, bool on) {
57 digitalWrite(pin, on ? RELAY_ON : RELAY_OFF);
58}
59
60float readPercent(byte pin, int raw0, int raw100) {
61 long sum = 0;
62 for (byte i = 0; i < 10; i++) sum += analogRead(pin); // 10 ölçüm ortalaması / average of 10
63 long pct = map(sum / 10, raw0, raw100, 0, 100);
64 return constrain(pct, 0, 100);
65}
66
67void setup() {
68 Serial.begin(9600);
69 setRelay(PUMP_RELAY, false); pinMode(PUMP_RELAY, OUTPUT); // açılışta kapalı / off at boot
70 setRelay(FAN_RELAY, false); pinMode(FAN_RELAY, OUTPUT);
71 setRelay(HEATER_RELAY, false); pinMode(HEATER_RELAY, OUTPUT);
72 pinMode(LED_PIN, OUTPUT);
73 pinMode(FLOAT_PIN, INPUT_PULLUP);
74 dht.begin();
75 lcd.init();
76 lcd.backlight();
77 lcd.print(F("BlueGrays Sera"));
78 Serial.println(F("sn,sicaklik,nem,toprak,isik,fan,isitici,pompa,led")); // CSV başlığı / CSV header
79 delay(2000); // DHT22 açılış süresi / DHT22 start-up time
80 readSensors();
81 lastRead = millis();
82}
83
84void loop() {
85 unsigned long now = millis();
86 if (now - lastRead >= READ_MS) {
87 lastRead = now;
88 readSensors();
89 }
90 controlClimate();
91 controlLight(now);
92 controlPump(now);
93 updateLcd(now);
94 logCsv(now);
95}
96
97// 1) Ölç / Measure
98void readSensors() {
99 float t = dht.readTemperature();
100 float h = dht.readHumidity();
101 if (isnan(t) || isnan(h)) {
102 if (dhtErrors < 255) dhtErrors++; // okuma hatası / read error
103 } else {
104 tempC = t;
105 hum = h;
106 dhtErrors = 0;
107 }
108 soil = readPercent(SOIL_PIN, SOIL_DRY_RAW, SOIL_WET_RAW);
109 light = readPercent(LDR_PIN, LDR_DARK_RAW, LDR_BRIGHT_RAW);
110 tankOk = digitalRead(FLOAT_PIN) == TANK_OK;
111}
112
113// 2) Fan ve ısıtıcı / Fan and heater
114void controlClimate() {
115 if (dhtErrors >= 3) { // sensör yok: güvenli durum / no sensor: safe state
116 heaterOn = false;
117 fanByTemp = true;
118 } else {
119 fanByTemp = hysteresis(fanByTemp, tempC, FAN_ON_C, FAN_OFF_C);
120 fanByHum = hysteresis(fanByHum, hum, HUM_ON, HUM_OFF);
121 heaterOn = hysteresis(heaterOn, tempC, HEAT_ON_C, HEAT_OFF_C);
122 }
123 fanOn = fanByTemp || fanByHum;
124 setRelay(FAN_RELAY, fanOn);
125 setRelay(HEATER_RELAY, heaterOn);
126}
127
128// 3) LED aydınlatma / LED grow light
129void controlLight(unsigned long now) {
130 bool wasDark = isDark;
131 isDark = hysteresis(isDark, light, DARK_ON, DARK_OFF);
132 if (isDark && !wasDark) darkSince = now; // akşam oldu / dusk detected
133 ledOn = isDark && (now - darkSince < EXTRA_LIGHT_MS);
134 analogWrite(LED_PIN, ledOn ? LED_LEVEL : 0);
135}
136
137// 4) Sulama: darbe + bekleme / Watering: pulse + soak
138void controlPump(unsigned long now) {
139 needWater = hysteresis(needWater, soil, SOIL_DRY, SOIL_OK);
140 if (!needWater) {
141 pulses = 0;
142 pumpFault = false; // toprak ıslandı / soil is wet again
143 }
144 switch (pumpState) {
145 case IDLE:
146 if (needWater && tankOk && !pumpFault) {
147 if (pulses >= MAX_PULSES) {
148 pumpFault = true; // nem yükselmiyor / moisture is not rising
149 } else {
150 pumpState = PUMPING;
151 pumpAt = now;
152 pulses++;
153 }
154 }
155 break;
156 case PUMPING:
157 if (now - pumpAt >= PUMP_MS || !tankOk) {
158 pumpState = SOAKING;
159 pumpAt = now;
160 }
161 break;
162 case SOAKING:
163 if (now - pumpAt >= SOAK_MS) pumpState = IDLE;
164 break;
165 }
166 setRelay(PUMP_RELAY, pumpState == PUMPING);
167}
168
169// 5) LCD: üç sayfa / three pages
170void updateLcd(unsigned long now) {
171 if (now - lastLcd < 3000) return;
172 lastLcd = now;
173 page = (page + 1) % 3;
174 lcd.clear();
175 if (page == 0) {
176 lcd.print(F("Sicaklik: "));
177 if (isnan(tempC)) lcd.print(F("--")); else lcd.print(tempC, 1);
178 lcd.print('C');
179 lcd.setCursor(0, 1);
180 lcd.print(F("Hava nemi: %"));
181 if (isnan(hum)) lcd.print(F("--")); else lcd.print(hum, 0);
182 } else if (page == 1) {
183 lcd.print(F("Toprak: %"));
184 lcd.print(soil, 0);
185 lcd.setCursor(0, 1);
186 lcd.print(F("Isik: %"));
187 lcd.print(light, 0);
188 } else {
189 lcd.print(F("F:")); lcd.print(fanOn);
190 lcd.print(F(" I:")); lcd.print(heaterOn);
191 lcd.print(F(" P:")); lcd.print(pumpState == PUMPING);
192 lcd.print(F(" L:")); lcd.print(ledOn);
193 lcd.setCursor(0, 1);
194 if (dhtErrors >= 3) lcd.print(F("Sensor hatasi"));
195 else if (!tankOk) lcd.print(F("Depo bos!"));
196 else if (pumpFault) lcd.print(F("Sulama hatasi"));
197 else lcd.print(F("Sistem normal"));
198 }
199}
200
201// 6) Seri port CSV kaydı / Serial CSV log
202void logCsv(unsigned long now) {
203 if (now - lastLog < 10000) return;
204 lastLog = now;
205 Serial.print(now / 1000); Serial.print(',');
206 Serial.print(tempC, 1); Serial.print(',');
207 Serial.print(hum, 0); Serial.print(',');
208 Serial.print(soil, 0); Serial.print(',');
209 Serial.print(light, 0); Serial.print(',');
210 Serial.print(fanOn); Serial.print(',');
211 Serial.print(heaterOn); Serial.print(',');
212 Serial.print(pumpState == PUMPING); Serial.print(',');
213 Serial.println(ledOn);
214}| Función | Tarea | ¿Cada cuánto? |
|---|---|---|
| readSensors() | Lee el DHT22, la humedad del suelo, la LDR y el flotador; convierte los valores analógicos a 0–100 % con la media de 10 lecturas | Cada 2,5 segundos |
| controlClimate() | Controla el ventilador con histéresis de temperatura o de humedad y el calefactor con histéresis de temperatura; pasa a estado seguro si falla el sensor | En cada ciclo |
| controlLight() | Enciende los LED con PWM al oscurecer y los apaga como máximo 4 horas después | En cada ciclo |
| controlPump() | Máquina de estados del riego: riega 3 s, espera 60 s y vuelve a medir; con protección de depósito y de desbordamiento | En cada ciclo |
| updateLcd() | Alterna entre tres páginas: datos del aire, humedad del suelo y luz, estado de los dispositivos | Cada 3 segundos |
| logCsv() | Escribe lecturas y estados en el monitor serie como una línea CSV | Cada 10 segundos |
La primera página de la LCD muestra la temperatura y la humedad del aire; la segunda, la humedad del suelo y el nivel de luz, y la tercera, el estado de los dispositivos: «F:1 I:0 P:0 L:1» significa que el ventilador funciona, que el calefactor (I = ısıtıcı) y la bomba están apagados y que los LED están encendidos. La línea inferior muestra uno de los mensajes «Sistem normal» (todo correcto), «Depo bos!» (depósito vacío), «Sulama hatasi» (fallo de riego) o «Sensor hatasi» (error de sensor); puedes traducir fácilmente estos textos en el código. Una vez compilado, el código ocupa alrededor del 31 % de la memoria de programa del Uno y el 25 % de su RAM, así que queda mucho sitio para añadir un segundo sensor de suelo, un zumbador o un módulo de reloj RTC. Como el Uno no tiene reloj real, la regla de los LED funciona como «como máximo 4 horas después del anochecer», una buena forma de alargar los días cortos de invierno. Si quieres que los LED se enciendan cada día a horas fijas, añade un módulo RTC o pasa a la versión con ESP32 de más abajo.
Vigilar y regar desde el móvil con un ESP32

El Uno no tiene Wi-Fi. Si quieres ver el estado del invernadero en el móvil y regar a distancia, cambia el cerebro por un ESP32. El siguiente código lleva la misma lógica de control al ESP32 y arranca un pequeño servidor web. Con el móvil en la misma red Wi-Fi, abre en el navegador la dirección IP que aparece en el monitor serie y verás la temperatura, la humedad, la humedad del suelo, la luz, el estado del depósito y si cada dispositivo está encendido o apagado; la página se recarga sola cada 10 segundos. El botón Water now (regar ahora) hace funcionar la bomba una vez durante 3 segundos si hay agua en el depósito. Al arrancar, el ESP32 obtiene la hora de internet (NTP); así los LED solo se encienden entre las 06:00 y las 20:00 y solo si está oscuro, y por la noche las plantas descansan.
1#include <WiFi.h>
2#include <WebServer.h>
3#include <DHT.h>
4#include <time.h>
5
6const char* ssid = "AgAdi"; // Wi-Fi adın / your Wi-Fi name
7const char* password = "Sifre123"; // Wi-Fi şifren / your Wi-Fi password
8
9// Pinler / Pins
10const int DHT_PIN = 4, FLOAT_PIN = 13, PUMP_RELAY = 25, FAN_RELAY = 26, HEATER_RELAY = 27;
11const int LED_PIN = 18; // MOSFET gate (PWM)
12const int SOIL_PIN = 34, LDR_PIN = 35; // ADC1: Wi-Fi açıkken çalışır / works with Wi-Fi on
13
14// Kalibrasyon (0-4095): kendin ölç / Calibration (0-4095): measure your own
15const int SOIL_DRY_RAW = 2800, SOIL_WET_RAW = 1200;
16const int LDR_DARK_RAW = 200, LDR_BRIGHT_RAW = 3800;
17
18// Eşikler (açma, kapama) / Thresholds (on, off)
19const float FAN_ON_C = 30, FAN_OFF_C = 27, HUM_ON = 85, HUM_OFF = 75;
20const float HEAT_ON_C = 16, HEAT_OFF_C = 19, SOIL_DRY = 35, SOIL_OK = 50;
21const float DARK_ON = 25, DARK_OFF = 35;
22const int LIGHT_FROM = 6, LIGHT_TO = 20; // LED yalnız 06:00-20:00 / LEDs only 06:00-20:00
23
24const unsigned long READ_MS = 2500, PUMP_MS = 3000, SOAK_MS = 60000;
25const unsigned long EXTRA_LIGHT_MS = 4UL * 3600000UL; // saat yoksa yedek kural / fallback without a clock
26const int MAX_PULSES = 6, LED_LEVEL = 200;
27const int RELAY_ON = LOW, RELAY_OFF = HIGH; // aktif LOW röle / active-LOW relay
28const int TANK_OK = LOW; // şamandıra suyla kapanır / float closes with water
29
30DHT dht(DHT_PIN, DHT22);
31WebServer server(80);
32
33float tempC = NAN, hum = NAN, soil = 0, light = 0;
34int dhtErrors = 0, pulses = 0;
35bool fanByTemp = false, fanByHum = false, fanOn = false, heaterOn = false;
36bool needWater = false, pumpFault = false, isDark = false, ledOn = false, tankOk = true;
37unsigned long lastRead = 0, pumpAt = 0, darkSince = 0;
38
39enum PumpState { IDLE, PUMPING, SOAKING };
40PumpState pumpState = IDLE;
41
42bool hysteresis(bool state, float value, float onAt, float offAt) {
43 if (onAt > offAt) { // yükselince aç / turn on when rising
44 if (value >= onAt) return true;
45 if (value <= offAt) return false;
46 } else { // düşünce aç / turn on when falling
47 if (value <= onAt) return true;
48 if (value >= offAt) return false;
49 }
50 return state;
51}
52
53void setRelay(int pin, bool on) {
54 digitalWrite(pin, on ? RELAY_ON : RELAY_OFF);
55}
56
57float readPercent(int pin, int raw0, int raw100) {
58 long sum = 0;
59 for (int i = 0; i < 16; i++) sum += analogRead(pin); // 16 ölçüm ortalaması / average of 16
60 long pct = map(sum / 16, raw0, raw100, 0, 100);
61 return constrain(pct, 0, 100);
62}
63
64int hourNow() {
65 struct tm t;
66 if (!getLocalTime(&t, 0)) return -1; // saat henüz yok / time not set yet
67 return t.tm_hour;
68}
69
70void readSensors() {
71 float t = dht.readTemperature();
72 float h = dht.readHumidity();
73 if (isnan(t) || isnan(h)) {
74 dhtErrors++;
75 } else {
76 tempC = t;
77 hum = h;
78 dhtErrors = 0;
79 }
80 soil = readPercent(SOIL_PIN, SOIL_DRY_RAW, SOIL_WET_RAW);
81 light = readPercent(LDR_PIN, LDR_DARK_RAW, LDR_BRIGHT_RAW);
82 tankOk = digitalRead(FLOAT_PIN) == TANK_OK;
83}
84
85void controlClimate() {
86 if (dhtErrors >= 3) { // sensör yok: güvenli durum / no sensor: safe state
87 heaterOn = false;
88 fanByTemp = true;
89 } else {
90 fanByTemp = hysteresis(fanByTemp, tempC, FAN_ON_C, FAN_OFF_C);
91 fanByHum = hysteresis(fanByHum, hum, HUM_ON, HUM_OFF);
92 heaterOn = hysteresis(heaterOn, tempC, HEAT_ON_C, HEAT_OFF_C);
93 }
94 fanOn = fanByTemp || fanByHum;
95 setRelay(FAN_RELAY, fanOn);
96 setRelay(HEATER_RELAY, heaterOn);
97}
98
99void controlLight(unsigned long now) {
100 bool wasDark = isDark;
101 isDark = hysteresis(isDark, light, DARK_ON, DARK_OFF);
102 if (isDark && !wasDark) darkSince = now;
103 int h = hourNow();
104 if (h >= 0) ledOn = isDark && h >= LIGHT_FROM && h < LIGHT_TO; // saat kuralı / clock rule
105 else ledOn = isDark && (now - darkSince < EXTRA_LIGHT_MS); // yedek kural / fallback rule
106 analogWrite(LED_PIN, ledOn ? LED_LEVEL : 0);
107}
108
109void controlPump(unsigned long now) {
110 needWater = hysteresis(needWater, soil, SOIL_DRY, SOIL_OK);
111 if (!needWater) {
112 pulses = 0;
113 pumpFault = false;
114 }
115 if (pumpState == IDLE && needWater && tankOk && !pumpFault) {
116 if (pulses >= MAX_PULSES) {
117 pumpFault = true; // nem yükselmiyor / moisture is not rising
118 } else {
119 pumpState = PUMPING;
120 pumpAt = now;
121 pulses++;
122 }
123 } else if (pumpState == PUMPING && (now - pumpAt >= PUMP_MS || !tankOk)) {
124 pumpState = SOAKING;
125 pumpAt = now;
126 } else if (pumpState == SOAKING && now - pumpAt >= SOAK_MS) {
127 pumpState = IDLE;
128 }
129 setRelay(PUMP_RELAY, pumpState == PUMPING);
130}
131
132String row(const char* name, const String& value) {
133 return String("<tr><td>") + name + "</td><td><b>" + value + "</b></td></tr>";
134}
135
136String onOff(bool on) {
137 return on ? "ON" : "OFF";
138}
139
140void handleRoot() {
141 String html = "<!DOCTYPE html><html><head><meta charset='utf-8'>"
142 "<meta name='viewport' content='width=device-width,initial-scale=1'>"
143 "<meta http-equiv='refresh' content='10'><title>BlueGrays Sera</title>"
144 "<style>body{font-family:sans-serif;max-width:420px;margin:auto;padding:12px}"
145 "td{padding:8px;border-bottom:1px solid #ddd}"
146 "a{display:block;margin-top:16px;padding:16px;border-radius:10px;background:#2e7d32;"
147 "color:#fff;text-align:center;text-decoration:none;font-size:18px}</style>"
148 "</head><body><h2>BlueGrays Sera</h2><table>";
149 html += row("Sıcaklık / Temperature", String(tempC, 1) + " °C");
150 html += row("Hava nemi / Humidity", "%" + String(hum, 0));
151 html += row("Toprak nemi / Soil", "%" + String(soil, 0));
152 html += row("Işık / Light", "%" + String(light, 0));
153 html += row("Fan", onOff(fanOn));
154 html += row("Isıtıcı / Heater", onOff(heaterOn));
155 html += row("Pompa / Pump", onOff(pumpState == PUMPING));
156 html += row("LED", onOff(ledOn));
157 html += row("Depo / Tank", tankOk ? "OK" : "BOŞ / EMPTY");
158 if (pumpFault) html += row("Uyarı / Warning", "Sulama hatası / Watering fault");
159 html += "</table><a href='/water'>Şimdi sula / Water now</a></body></html>";
160 server.send(200, "text/html", html);
161}
162
163void handleWater() { // elle tek darbe / one manual pulse
164 if (pumpState == IDLE && tankOk) {
165 pumpState = PUMPING;
166 pumpAt = millis();
167 }
168 server.sendHeader("Location", "/");
169 server.send(303);
170}
171
172void setup() {
173 Serial.begin(115200);
174 setRelay(PUMP_RELAY, false); pinMode(PUMP_RELAY, OUTPUT);
175 setRelay(FAN_RELAY, false); pinMode(FAN_RELAY, OUTPUT);
176 setRelay(HEATER_RELAY, false); pinMode(HEATER_RELAY, OUTPUT);
177 pinMode(LED_PIN, OUTPUT);
178 pinMode(FLOAT_PIN, INPUT_PULLUP);
179 dht.begin();
180 WiFi.begin(ssid, password);
181 while (WiFi.status() != WL_CONNECTED) {
182 delay(500);
183 Serial.print('.');
184 }
185 Serial.println();
186 Serial.println(WiFi.localIP()); // bu adresi telefonda aç / open this address on your phone
187 configTime(3 * 3600, 0, "pool.ntp.org"); // Türkiye UTC+3; kendi saat dilimini yaz / set your own time zone
188 server.on("/", handleRoot);
189 server.on("/water", handleWater);
190 server.begin();
191 readSensors();
192 lastRead = millis();
193}
194
195void loop() {
196 server.handleClient();
197 unsigned long now = millis();
198 if (now - lastRead >= READ_MS) {
199 lastRead = now;
200 readSensors();
201 }
202 controlClimate();
203 controlLight(now);
204 controlPump(now);
205}Esto es lo que cambia respecto al código del Uno: el ADC del ESP32 es de 12 bits y da valores de 0 a 4095, así que tienes que volver a calibrar; como alimentas los sensores desde 3V3, los valores en bruto también serán distintos. La zona horaria se ajusta en la línea configTime(); pusimos 3 × 3600 segundos para Turquía (UTC+3). En España peninsular, sustituye esa línea por configTzTime("CET-1CEST,M3.5.0,M10.5.0/3", "pool.ntp.org"), que además tiene en cuenta el horario de verano. Si no hay internet o todavía no se ha obtenido la hora, la regla de los LED vuelve a la del Uno, «como máximo 4 horas». La página no tiene contraseña, así que cualquiera en tu red doméstica podría poner en marcha la bomba; añadir una contraseña sencilla con la función authenticate() de la librería WebServer es una buena costumbre. Si también quieres una LCD en el ESP32, puedes copiar tal cual la función updateLcd() del código del Uno; SDA es GPIO 21 y SCL es GPIO 22.
| Método | Acceso desde fuera de casa | Dificultad | Ideal para |
|---|---|---|---|
| Servidor web en el ESP32 (el código de esta guía) | No, solo en la misma red | Media | Presentación escolar, demostración sin internet |
| Blynk IoT | Sí | Fácil | Indicadores, gráficas y notificaciones listos para usar |
| Bot de Telegram | Sí | Media | Avisos de «depósito vacío» y «temperatura demasiado alta» |
| Hojas de cálculo de Google | Sí | Media | Registro de datos durante semanas y gráficas |
| MQTT y Home Assistant | Sí, requiere configuración | Avanzada | Domótica real de casa y jardín |
Para una presentación escolar, el servidor web del ESP32 es la opción más segura: no depende de ningún servicio en la nube y funciona con su propia red aunque en la sala no haya internet. Si también quieres consultarlo desde fuera de casa, Blynk IoT, con sus indicadores y gráficas listos, es el camino más fácil. Para recibir un mensaje en el móvil cuando se vacíe el depósito o la temperatura sea peligrosa, consulta la guía del bot de Telegram con ESP32; para reunir semanas de datos para tu informe, ESP32 con Hojas de cálculo de Google; y para conectar el invernadero a una domótica real, MQTT y Home Assistant.
Versión en MicroPython para Raspberry Pi Pico
También puedes gestionar el mismo invernadero con una Raspberry Pi Pico en MicroPython. El firmware MicroPython de la Pico ya incluye un controlador DHT, así que no tienes que copiar archivos adicionales. Las entradas analógicas son solo GP26, GP27 y GP28, y read_u16() devuelve valores de 0 a 65535; mide tus números de calibración en esa escala. Como la Pico trabaja a 3,3 V, alimenta los sensores desde 3V3 y el módulo de relés desde VBUS (los 5 V del USB) o desde la línea del LM2596. El código siguiente contiene todas las reglas del ventilador, el calefactor, los LED y el riego, y escribe los valores en la consola de Thonny. Si guardas el archivo en la Pico con el nombre main.py, la placa funciona aunque no esté conectada al ordenador. Si usas la Pico W con Wi-Fi, puedes añadir el método del artículo servidor web con Pico W para ver los valores desde el móvil.
1from machine import Pin, ADC, PWM
2import dht
3import time
4
5sensor = dht.DHT22(Pin(15)) # GP15: DHT22 DATA
6soil_adc = ADC(26) # GP26: toprak nem AOUT / soil moisture AOUT
7ldr_adc = ADC(27) # GP27: LDR bölücü (3V3) / LDR divider (3V3)
8tank = Pin(14, Pin.IN, Pin.PULL_UP) # şamandıra: suyla 0 / float switch: 0 with water
9fan = Pin(16, Pin.OUT, value=1) # aktif LOW röleler kapalı başlar / active-LOW relays start off
10heater = Pin(17, Pin.OUT, value=1)
11pump = Pin(18, Pin.OUT, value=1)
12led = PWM(Pin(19)) # MOSFET gate
13led.freq(1000)
14
15SOIL_DRY_RAW, SOIL_WET_RAW = 50000, 22000 # kendin ölç / measure your own
16LDR_DARK_RAW, LDR_BRIGHT_RAW = 2000, 60000
17EXTRA_LIGHT_MS = 4 * 3600 * 1000 # günü 4 saat uzat / extend the day by 4 h
18
19
20def percent(adc, raw0, raw100):
21 raw = sum(adc.read_u16() for _ in range(10)) // 10
22 pct = (raw - raw0) * 100 // (raw100 - raw0)
23 return max(0, min(100, pct))
24
25
26def hysteresis(state, value, on_at, off_at):
27 if on_at > off_at: # yükselince aç / turn on when rising
28 if value >= on_at:
29 return True
30 if value <= off_at:
31 return False
32 else: # düşünce aç / turn on when falling
33 if value <= on_at:
34 return True
35 if value >= off_at:
36 return False
37 return state # ölü bölge / dead band
38
39
40def relay(pin, on):
41 pin.value(0 if on else 1)
42
43
44t = h = None
45errors = pulses = 0
46fan_t = fan_h = heat_on = need_water = dark = led_on = False
47pump_state, pump_at, dark_since = "idle", 0, 0
48soil = light = 0
49tank_ok = True
50time.sleep(2) # DHT22 açılış süresi / DHT22 start-up time
51last_read = time.ticks_add(time.ticks_ms(), -2500)
52
53while True:
54 now = time.ticks_ms()
55 if time.ticks_diff(now, last_read) >= 2500: # 2,5 sn'de bir ölç / measure every 2.5 s
56 last_read = now
57 try:
58 sensor.measure()
59 t, h = sensor.temperature(), sensor.humidity()
60 errors = 0
61 except OSError:
62 errors += 1 # okuma hatası / read error
63 soil = percent(soil_adc, SOIL_DRY_RAW, SOIL_WET_RAW)
64 light = percent(ldr_adc, LDR_DARK_RAW, LDR_BRIGHT_RAW)
65 tank_ok = tank.value() == 0
66
67 if errors >= 3 or t is None: # güvenli durum / safe state
68 heat_on, fan_t = False, True
69 else:
70 fan_t = hysteresis(fan_t, t, 30, 27)
71 fan_h = hysteresis(fan_h, h, 85, 75)
72 heat_on = hysteresis(heat_on, t, 16, 19)
73 relay(fan, fan_t or fan_h)
74 relay(heater, heat_on)
75
76 was_dark = dark
77 dark = hysteresis(dark, light, 25, 35)
78 if dark and not was_dark:
79 dark_since = now # akşam oldu / dusk detected
80 led_on = dark and time.ticks_diff(now, dark_since) < EXTRA_LIGHT_MS
81 led.duty_u16(50000 if led_on else 0)
82
83 need_water = hysteresis(need_water, soil, 35, 50)
84 if not need_water:
85 pulses = 0
86 print("T", t, "N", h, "Toprak", soil, "Isik", light, "Pompa", pump_state,
87 "Depo OK" if tank_ok else "Depo BOS", "HATA" if pulses >= 6 else "")
88
89 # Sulama: 3 sn darbe, 60 sn bekleme / Watering: 3 s pulse, 60 s soak
90 if pump_state == "idle" and need_water and tank_ok and pulses < 6:
91 pump_state, pump_at = "pump", now
92 pulses += 1
93 elif pump_state == "pump" and (time.ticks_diff(now, pump_at) >= 3000 or not tank_ok):
94 pump_state, pump_at = "soak", now
95 elif pump_state == "soak" and time.ticks_diff(now, pump_at) >= 60000:
96 pump_state = "idle"
97 relay(pump, pump_state == "pump")
98 time.sleep_ms(100)Consejos para la presentación escolar o la feria de ciencias
El invernadero inteligente es un tema tan popular que el jurado ve decenas de maquetas parecidas. Lo que te hará destacar no es solo demostrar que el sistema funciona, sino responder a una pregunta con mediciones. En una feria de ciencias, el objetivo, el método y el resultado esperado deben estar claros; en los concursos de investigación se espera un experimento comparativo con datos. Una buena pregunta de investigación para este invernadero es: «¿Cuánta agua ahorra regar según la humedad del suelo frente a regar todos los días a la misma hora, y cómo afecta al crecimiento de la planta?»
- Forma dos grupos: Divide plantones de la misma especie y la misma altura en dos grupos. Riega uno con el sistema de sensores y el otro a mano, con una cantidad fija cada día; la luz y la temperatura deben ser iguales para los dos grupos.
- Mide y registra: Lleva una tabla con la cantidad semanal de agua (midiendo el depósito, en mL), la altura de la planta (cm) y el número de hojas. La salida CSV del código o un registro en Hojas de cálculo de Google te da la gráfica de temperatura y humedad sin esfuerzo.
- Haz un diagrama de bloques: A la izquierda los sensores, en el centro el Arduino y a la derecha los actuadores; etiqueta las flechas con frases de reglas («suelo < 35 % → bomba»). El jurado no leerá tu código, pero recordará este diagrama.
- Explica la histéresis: Dos gráficas que muestren cuántas veces se enciende y se apaga en una hora un ventilador con un solo umbral y otro con histéresis son la mejor forma de enseñar al jurado la parte de ingeniería del proyecto.
- Prepara un guion para la demostración en directo: Calienta el DHT22 con la mano para que arranque el ventilador, pasa el sensor de suelo a una maceta seca para mostrar los pulsos de la bomba y tapa la LDR con la mano para que se enciendan los LED. Cada demostración debe durar 10–20 segundos.
- Conéctalo con la vida real: Habla de problemas reales como el consumo de agua y energía de los invernaderos, las enfermedades por hongos que provoca el exceso de humedad y la carga de trabajo de los agricultores; muestra con números a cuál de ellos responde tu sistema.
- Ten un plan B: Graba un vídeo del sistema funcionando; lleva cables, una bomba y una fuente de repuesto; apunta en un papel los valores de calibración y los datos del Wi-Fi.
- Prepárate para las preguntas del jurado: Ten respuestas breves para «¿Qué pasa si falla un sensor?», «¿Qué les pasa a las plantas si se va la luz?» y «¿Cómo lo llevarías a un invernadero de verdad?». El estado seguro y la protección contra desbordamiento del código son las mejores respuestas.
Errores frecuentes y cómo solucionarlos
| Síntoma | Causa probable | Solución |
|---|---|---|
| El Arduino se reinicia o la LCD se desconfigura cuando funciona la bomba | La bomba y los relés se alimentan del pin 5V del Arduino, ruido del motor | Alimenta la bomba y las bobinas de los relés desde el LM2596, une los GND y pon en la bomba un diodo en inversa y un condensador de 470 µF |
| Los relés no paran de hacer clic | Se usa un único umbral o las lecturas tienen ruido | Usa umbrales distintos para encender y apagar (histéresis) y promedia las lecturas analógicas |
| Sigue saliendo agua después de parar la bomba | Efecto sifón: el extremo de la manguera está por debajo del nivel del depósito | Baja el depósito o haz un pequeño agujero de aire en lo alto de la manguera |
| La humedad del suelo marca siempre 0 % o siempre 100 % | Valores de calibración erróneos o AOUT en el pin equivocado | Mide el valor en bruto del sensor al aire y en agua y escríbelo en las constantes |
| Los LED se encienden y se apagan sin parar | La LDR ve la luz de los LED | Lleva la LDR fuera del invernadero, mirando al cielo |
| El DHT22 devuelve nan | Pin equivocado, sin resistencia pull-up o leído más de una vez cada 2 segundos | Revisa el pin, usa un módulo o pon 10 kΩ entre DATA y VCC |
| La temperatura parece más alta de lo normal | El sol o la luz de los LED dan directamente en el sensor | Dale sombra con una tapa blanca con agujeros y aléjalo del calefactor |
| Gotas de agua y moho en las paredes | Humedad demasiado alta, ventilación insuficiente | Baja el umbral de humedad, agranda la entrada de aire y riega menos |
| En el ESP32, la lectura del suelo falla al activarse el Wi-Fi | El sensor está en un pin ADC2 | Pásalo a un pin ADC1 entre GPIO 32 y 39 |
| Con el ESP32 el relé nunca se suelta | Un HIGH de 3,3 V no desactiva un módulo de 5 V activo a nivel bajo | Separa JD-VCC o usa un módulo de relés que se active con 3,3 V |
Un invernadero inteligente reúne en un solo proyecto la lectura de sensores, la calibración, la toma de decisiones con histéresis, el control de relés y MOSFET, la planificación de la alimentación y la supervisión inalámbrica, y al final hace crecer algo realmente vivo: una planta. Empieza por el riego y la ventilación, prueba cada parte por separado y luego añade el calefactor, los LED y el móvil. En la app BlueGrays encontrarás, paso a paso y con esquemas y código, proyectos para medir temperatura y humedad con un DHT11, leer la luz con una LDR, una estación de riego inteligente y un registrador de temperatura con DHT22.
Preguntas frecuentes
¿Qué materiales necesito para un invernadero inteligente con Arduino?
Para un invernadero inteligente básico bastan un Arduino Uno o un ESP32, un sensor de temperatura y humedad DHT22, un sensor capacitivo de humedad del suelo, una LDR con una resistencia de 10 kΩ, un módulo de relés, una mini bomba sumergible y un ventilador. La versión completa añade un calefactor PTC de 12 V, una tira LED controlada con un MOSFET, una LCD I2C, un interruptor de flotador, una fuente de 12 V 5 A y un LM2596. Como estructura, una caja de almacenaje transparente con tapa es la solución más fácil y barata.
¿Cómo funciona un invernadero inteligente?
Un invernadero inteligente funciona con un ciclo de medir, decidir, actuar y mostrar. Los sensores miden la temperatura, la humedad, la humedad del suelo y la luz; el Arduino compara esos valores con umbrales y enciende o apaga el ventilador, el calefactor, la bomba y los LED mediante relés o un MOSFET. Los resultados se muestran en una LCD y, con un ESP32, también en el móvil.
¿DHT11 o DHT22 para un invernadero?
Elige el DHT22. El DHT11 solo es preciso a ±2 °C entre 0 y 50 °C y no detecta humedades por encima del 90 %, mientras que en un invernadero la humedad puede superar el 90 % por la mañana y la temperatura bajar de cero en invierno. El DHT22 mide de −40 a 80 °C con ±0,5 °C y la humedad con unos ±2 %, y su código es casi idéntico al del DHT11.
¿Cuánto cuesta un invernadero inteligente con Arduino?
Los precios dependen de la tienda, pero sin contar la placa la versión básica (DHT22, sensor de humedad del suelo, LDR, relé, bomba y ventilador) suele costar unos 25–40 €. La versión completa, con calefactor, tira LED, LCD, fuente de 12 V y carcasa, ronda los 70–120 €. Si ya tienes un kit de inicio de Arduino, el coste baja bastante.
¿Cómo vigilo mi invernadero desde el móvil?
Lo más fácil es cambiar el cerebro por un ESP32 y montar un servidor web; desde el navegador del móvil, en la misma red Wi-Fi, ves los valores y pones en marcha la bomba. Para acceder desde fuera de casa puedes usar Blynk IoT, para recibir avisos un bot de Telegram y para registrar datos a largo plazo Hojas de cálculo de Google.
¿Cómo presento un invernadero inteligente en una feria de ciencias?
En lugar de mostrar solo una maqueta que funciona, plantea una pregunta de investigación medible; por ejemplo, mide durante varias semanas, con dos grupos de plantas, cuánta agua ahorra el riego por sensores frente al riego programado. Presenta el consumo de agua, la altura de las plantas y las gráficas de temperatura y humedad en tablas y gráficos, explica el sistema con un diagrama de bloques y prepara un guion breve para la demostración en directo.
Proyectos para hacer después de leer esto
Ver todos los proyectos
Temperatura y humedad con DHT11
¿Qué temperatura hace en tu habitación, y el aire está húmedo o seco? El sensor DHT11 mide la temperatura y la humedad, y el Arduino escribe los valores en una pantalla LCD de dos líneas, actualizándo

Medir la intensidad de luz con LDR
¿Quieres ver en números cuánta luz hay en tu habitación? El LDR, una pequeña pieza sensible a la luz, mide la luz ambiente y el Arduino escribe el valor en la pantalla del ordenador cada medio segundo

Lectura de la intensidad luminosa con el módulo LDR del ESP32
Visualización en tiempo real en el monitor serie del valor bruto del ADC del módulo LDR y del porcentaje relativo de luz calculado a partir de referencias de oscuridad y claridad. Este porcentaje no es una medición en lux. Explica el funcionamiento de la calibración del módulo. Es recomendable realizarlo antes del Proyecto 10, ya que se utiliza para determinar el valor umbral.

Estación de riego inteligente
Un jardinero que riega solo tus plantas para que no se sequen mientras estás de vacaciones. Un sensor de humedad mide lo mojada que está la tierra; cuando se seca, el Arduino enciende la bomba de agua

ESP32 DHT22 Temperatura y humedad (con pantalla LCD)
Se trata de un proyecto que mide la temperatura y la humedad ambientales mediante el sensor DHT22 y las muestra en tiempo real en una pantalla LCD I2C 16x2. Los datos del sensor se leen mediante el protocolo de un solo cable (one-wire) utilizando la biblioteca DHT de Adafruit. Este proyecto constituye la base para estaciones meteorológicas IoT, sistemas de automatización doméstica inteligente o sistemas de monitorización ambiental en invernaderos.

Registrador de Temperatura ESP32 (DHT22 + DS1307 RTC + microSD + OLED)
¡Construye un registrador de temperatura con marca de tiempo con ESP32! Lee temperatura/humedad del DHT22 cada minuto, obtén la hora real del DS1307 RTC y registra en tarjeta microSD como CSV. El OLED muestra en vivo la temperatura (fuente grande), porcentaje de humedad, marca de tiempo y contador de registros. El archivo /temp_log.csv en la tarjeta SD se abre en Excel, Google Sheets o Python pandas para análisis — perfecto para monitoreo de invernadero, seguimiento de temperatura del hog

Iluminación Inteligente ESP32 (PIR + LDR + Relé + OLED)
¡Convierte tu pasillo, baño o despensa en iluminación inteligente con ESP32! Utilizando un sensor de movimiento PIR, un sensor de luz LDR y un módulo de relé, construimos el escenario clásico "la luz se enciende cuando se detecta movimiento en la oscuridad" — si el ambiente ya está iluminado, el movimiento NO activa la luz, ahorrando energía durante el día. La luz permanece encendida durante 30 segundos; si el movimiento continúa, el temporizador se reinicia y la luz permanece encendida más tiem
Más artículos
App BlueGrays
Monta este circuito paso a paso en tu teléfono
Esquema, código y lista de piezas en una sola pantalla. Más de 200 proyectos de Arduino, ESP32 y Pico listos para montar.
Escanea con tu teléfono
Apunta la cámara al código y se abrirá la tienda de apps de tu teléfono.
o ábrela en la tienda

