Community project

Control de Cultivo Aeropónico

ESP32
Photo of Control de Cultivo Aeropónico
Generated with AI

Fernanda Marquez

Published September 28, 2026

This aeroponic cultivation controller uses an ESP32 to monitor and automate a hydroponic growing system. It reads environmental conditions through light, humidity, air temperature, water temperature, pH, turbidity, and TDS sensors, while tracking water flow and reservoir level with a flow meter and ultrasonic distance sensor. The guide provides a complete wiring diagram, parts list, and step-by-step assembly instructions for connecting all sensors to the microcontroller, configuring the three-channel relay module for pump, filter, and fan control, and uploading the monitoring firmware.

Builders will learn how to integrate multiple sensor types with different voltage requirements using voltage dividers, set up I2C communication for the light sensor, configure interrupt-driven flow pulse counting, and implement relay logic for system automation. The assembly checklist ensures proper power distribution across four rails and includes pre-power verification steps to prevent component damage.

Wiring diagram

Wiring diagram for Control de Cultivo Aeropónico

Gather all the parts

QtyComponent
1

BH1750

Rohm BH1750FVI 16-bit digital ambient light sensor with I2C interface. Measures illuminance from 1 to 65535 lux. Two selectable I2C addresses (0x23 / 0x5C) via ADDR pin.

1

DHT11

Digital temperature and humidity sensor (lower accuracy than DHT22)

1

DS18B20

Digital temperature sensor using OneWire protocol

1

4.7k Pull Up Resistor

4.7 kΩ

4.7kΩ pull-up resistor used in 1-Wire (OneWire) communication buses. Essential passive component that pulls the single data line to 3.3V or 5V when idle. Works with 1-Wire protocol devices (DS18B20, DS18S20, iButton, etc.) to ensure proper bus communication at standard 16.3 kbit/s data rates. Supports both 3.3V and 5V systems.

1

Ph Probe

DFRobot Gravity Analog pH Sensor (SEN0161/SEN0169). Outputs 0-3V on PO pin scaled to pH 0-14. Needs two-point calibration (typically pH 4 and pH 7 buffer solutions). Probe is the consumable; the BNC module is reusable. Read with analogRead and convert via linear calibration constants.

1

Sensor de turbidez analógico de 5 V

El módulo que entrega un voltaje analógico aproximado según la turbidez del agua.

1

Sensor TDS analógico de 5 V

El módulo que entrega un voltaje analógico relacionado con los sólidos disueltos en el agua.

1

YF-S201 caudalímetro

El sensor de caudal de agua que produce pulsos mientras el agua circula.

1

HC-SR04

Ultrasonic distance measurement sensor

1

Módulo de relés de 3 canales, 5 V, con optoacopladores

El módulo que conmuta por separado el filtro, la bomba de riego y el ventilador con contactos de relé.

1

LED

Verde

Standard 3mm/5mm through-hole LED. A current-limiting series resistor is added automatically.

1

LED

Rojo

Standard 3mm/5mm through-hole LED. A current-limiting series resistor is added automatically.

1

Resistor

10 kΩ

Through-hole resistor (current-limiting in series with an LED)

1

Resistor

20 kΩ

Through-hole resistor (current-limiting in series with an LED)

1

Resistor

10 kΩ

Through-hole resistor (current-limiting in series with an LED)

1

Resistor

20 kΩ

Through-hole resistor (current-limiting in series with an LED)

1

Resistor

10 kΩ

Through-hole resistor (current-limiting in series with an LED)

1

Resistor

20 kΩ

Through-hole resistor (current-limiting in series with an LED)

1

Resistor

10 kΩ

Through-hole resistor (current-limiting in series with an LED)

1

Resistor

20 kΩ

Through-hole resistor (current-limiting in series with an LED)

1

Resistor

10 kΩ

Through-hole resistor (current-limiting in series with an LED)

1

Resistor

20 kΩ

Through-hole resistor (current-limiting in series with an LED)

1

Resistor

330 Ω

Through-hole resistor (current-limiting in series with an LED)

1

Resistor

330 Ω

Through-hole resistor (current-limiting in series with an LED)

1

Fuente externa regulada de 5 V / 2 A

La fuente que entrega energía separada a las bombas, el filtro y el ventilador mediante los contactos de relé.

1

Filtro AquaJet de 5 V

La carga de filtración de agua conmutada por el primer relé.

1

Bomba de riego de 5 V

La bomba de riego conmutada por el segundo relé.

1

Ventilador de 5 V

El ventilador conmutado por el tercer relé.

Assemble it in 7 steps

1. Prepara los cuatro raíles de alimentación

Con la placa sin conectar, reserva una fila negra para GND, una naranja para 3.3 V, una roja para los 5 V del USB/VBUS y otra roja separada para el positivo de la fuente externa de 5 V. Une el negativo V− de la fuente externa al rail negro GND: esta unión es obligatoria para que las señales de control tengan la misma referencia.

  • Usa cable negro para todas las uniones de tierra, naranja para 3.3 V, rojo para 5 V y azul para señales.
  • La fila roja externa no se debe unir al VBUS USB; solamente se unen sus negativos en GND.
  • No conectes todavía la fuente externa ni las cargas: un positivo externo unido por error al rail USB puede dañar el ordenador o la placa.

2. Conecta los sensores de 3.3 V

Desde el rail naranja conecta BH1750 VCC, DHT11 VCC y DS18B20 VCC; desde el rail negro conecta sus tres GND. Con cables azules conecta BH1750 SDA a GPIO4 (datos), BH1750 SCL a GPIO5 (reloj), DHT11 DATA a GPIO7 (señal) y DS18B20 DATA a GPIO6 (señal). Conecta una resistencia de 4.7 kΩ entre 3.3 V y la misma unión de DATA/GPIO6 del DS18B20.

  • Conecta ADDR del BH1750 a GND para que use su dirección habitual.
  • La resistencia de 4.7 kΩ no tiene polaridad; sus dos patas solamente deben quedar entre 3.3 V y la línea DATA del DS18B20.
  • No alimentes estos tres sensores desde 5 V: hacerlo puede estropearlos.

3. Monta los cinco divisores de tensión

Para cada salida de 5 V, coloca una resistencia de 10 kΩ desde la salida del módulo al nodo de señal y una de 20 kΩ desde ese mismo nodo al rail negro GND. Lleva cada nodo con cable azul al ESP32: pH PO a GPIO1 (señal), turbidez AO a GPIO2 (señal), TDS AO a GPIO3 (señal), YF-S201 PULSE a GPIO8 (señal) y HC-SR04 ECHO a GPIO10 (señal). Alimenta pH, turbidez, TDS, YF-S201 y HC-SR04 con VCC al rail rojo USB de 5 V (alimentación) y GND al rail negro (tierra).

  • Una resistencia no tiene sentido de montaje; confirma el valor por sus bandas o con multímetro antes de insertarla.
  • Los dos terminales centrales de cada divisor —la salida de 10 kΩ y la entrada de 20 kΩ— forman el único nodo que llega al GPIO.
  • Nunca conectes PO, AO, PULSE o ECHO de un módulo de 5 V directamente a un GPIO: una señal de 5 V puede dañar la entrada de 3.3 V del ESP32.
  • El divisor 10 kΩ/20 kΩ queda muy cerca de 3.3 V con una salida de 5 V; verifica que el módulo no supere 5 V antes de alimentarlo.

4. Conecta el disparo ultrasónico y las entradas de relé

Con un cable azul conecta GPIO9 a TRIG del HC-SR04 (señal de disparo). Conecta IN1 del módulo de relés a GPIO11 (control del filtro), IN2 a GPIO12 (control de la bomba) e IN3 a GPIO13 (control del ventilador). Conecta VCC del módulo de relés al rail rojo USB de 5 V (alimentación) y su GND al rail negro (tierra).

  • Al encender, el programa deja los tres relés apagados para que ninguna carga arranque por accidente.
  • Si el HC-SR04 no devuelve medidas, coloca un convertidor de nivel entre GPIO9 y TRIG; algunos módulos económicos necesitan un disparo de 5 V.
  • No alimentes las bombas ni el ventilador desde VBUS USB: el cable USB normalmente no puede entregar la corriente que necesitan.

5. Cablea los contactos de potencia de los relés

Con el cable de la fuente externa desconectado, conecta V+ de la fuente externa a COM1, COM2 y COM3. Conecta NO1 al positivo V+ del filtro AquaJet, NO2 al positivo V+ de la bomba y NO3 al positivo V+ del ventilador. Conecta el GND de cada carga al rail negro, que ya está unido a V− de la fuente externa. Deja todos los terminales NC sin conectar.

  • Cada contacto COM–NO actúa como un interruptor: solo entrega el positivo externo a su carga cuando el relé está activo.
  • Para bombas o ventiladores con cables finos, usa conectores firmes; no dejes cobre pelado al alcance del agua.
  • Confirma el consumo real de cada carga: la suma no debe superar 2 A y tampoco debe superar la corriente nominal de los contactos del módulo de relés.
  • Mantén conexiones y empalmes de 5 V lejos de salpicaduras; agua en un terminal puede causar un cortocircuito.

6. Añade los LED de estado

Conecta el ánodo —la pata larga— del LED verde a GPIO43 (señal) y su cátodo a una resistencia de 330 Ω que termina en GND. Haz lo mismo con el LED rojo: ánodo a GPIO44 (señal), cátodo a otra resistencia de 330 Ω que termina en GND.

  • El LED verde se enciende cuando las lecturas principales son válidas; el rojo avisa si falta una lectura.
  • Las resistencias de 330 Ω limitan la corriente y no tienen polaridad.
  • GPIO43 y GPIO44 son también pines de puerto serie externo en esta placa. Si usas ese puerto para otro dispositivo, mueve los LED a dos GPIO libres y actualiza el proyecto.

7. Haz la comprobación antes de encender

Con multímetro, comprueba continuidad entre GND de la placa, relé y fuente externa. Mide VBUS para confirmar 5 V y mide la fuente externa para confirmar 5 V. Revisa que cada divisor tenga 10 kΩ en serie y 20 kΩ hacia GND. Primero conecta solo el USB y revisa las lecturas serie; después conecta la fuente externa y prueba un relé y una carga cada vez.

  • El programa informa lecturas cada dos segundos y mantiene los relés apagados de inicio.
  • El diagrama usa azul para señales, naranja para 3.3 V, rojo para 5 V y negro para GND.
  • Si mides más de 3.3 V en cualquier nodo que entra al ESP32, desconecta la alimentación y corrige el divisor antes de conectar la placa.

Review all connections

1. Connections between "bh1750_1" and "ESP32"

Functionbh1750_1ESP32
powerVCC3V3
groundGNDGND
i2cSDAGPIO 4
i2cSCLGPIO 5
groundADDRGND

2. Connections between "dht11_1" and "ESP32"

Functiondht11_1ESP32
powerVCC3V3
groundGNDGND
dataDATAGPIO 7

3. Connections between "ds18b20_1" and "ESP32"

Functionds18b20_1ESP32
powerVCC3V3
groundGNDGND
dataDATAGPIO 6

4. Connections between "onewire_pullup" and "ESP32"

Functiononewire_pullupESP32
powerVCC3V3
dataDATA_LINE → DS18B20 DATAEXT
groundGNDGND

5. Connections between "ph_sensor" and "ESP32"

Functionph_sensorESP32
powerVCC5V
groundGNDGND
analogPO → Resistor P1EXT

6. Connections between "ph_series_resistor" and "ESP32"

Functionph_series_resistorESP32
digitalP2GPIO 1

7. Connections between "ph_ground_resistor" and "ESP32"

Functionph_ground_resistorESP32
groundP2GND
digitalP1 → Resistor P2EXT

8. Connections between "turbidity_sensor" and "ESP32"

Functionturbidity_sensorESP32
powerVCC5V
groundGNDGND
analogAO → Resistor P1EXT

9. Connections between "turbidity_series_resistor" and "ESP32"

Functionturbidity_series_resistorESP32
digitalP2GPIO 2

10. Connections between "turbidity_ground_resistor" and "ESP32"

Functionturbidity_ground_resistorESP32
groundP2GND
digitalP1 → Resistor P2EXT

11. Connections between "tds_sensor" and "ESP32"

Functiontds_sensorESP32
powerVCC5V
groundGNDGND
analogAO → Resistor P1EXT

12. Connections between "tds_series_resistor" and "ESP32"

Functiontds_series_resistorESP32
digitalP2GPIO 3

13. Connections between "tds_ground_resistor" and "ESP32"

Functiontds_ground_resistorESP32
groundP2GND
digitalP1 → Resistor P2EXT

14. Connections between "flow_sensor" and "ESP32"

Functionflow_sensorESP32
powerVCC5V
groundGNDGND
digitalPULSE → Resistor P1EXT

15. Connections between "flow_series_resistor" and "ESP32"

Functionflow_series_resistorESP32
digitalP2GPIO 8

16. Connections between "flow_ground_resistor" and "ESP32"

Functionflow_ground_resistorESP32
groundP2GND
digitalP1 → Resistor P2EXT

17. Connections between "hcsr04_1" and "ESP32"

Functionhcsr04_1ESP32
powerVCC5V
groundGNDGND
digitalTRIGGPIO 9
digitalECHO → Resistor P1EXT

18. Connections between "echo_series_resistor" and "ESP32"

Functionecho_series_resistorESP32
digitalP2GPIO 10

19. Connections between "echo_ground_resistor" and "ESP32"

Functionecho_ground_resistorESP32
groundP2GND
digitalP1 → Resistor P2EXT

20. Connections between "relay_module" and "ESP32"

Functionrelay_moduleESP32
powerVCC5V
groundGNDGND
digitalIN1GPIO 11
digitalIN2GPIO 12
digitalIN3GPIO 13
powerCOM2 → Fuente externa regulada de 5 V / 2 A V+EXT
powerCOM3 → Fuente externa regulada de 5 V / 2 A V+EXT
powerNO1 → Filtro AquaJet de 5 V V+EXT
powerNO2 → Bomba de riego de 5 V V+EXT
powerNO3 → Ventilador de 5 V V+EXT

21. Connections between "external_supply" and "ESP32"

Functionexternal_supplyESP32
powerV+ → Módulo de relés de 3 canales, 5 V, con optoacopladores COM1EXT
groundV-GND

22. Connections between "filter_load" and "ESP32"

Functionfilter_loadESP32
groundGNDGND

23. Connections between "pump_load" and "ESP32"

Functionpump_loadESP32
groundGNDGND

24. Connections between "fan_load" and "ESP32"

Functionfan_loadESP32
groundGNDGND

25. Connections between "green_led" and "ESP32"

Functiongreen_ledESP32
digitalANODEGPIO 43
groundGND → Resistor P1EXT

26. Connections between "green_led_resistor" and "ESP32"

Functiongreen_led_resistorESP32
groundP2GND

27. Connections between "red_led" and "ESP32"

Functionred_ledESP32
digitalANODEGPIO 44
groundGND → Resistor P1EXT

28. Connections between "red_led_resistor" and "ESP32"

Functionred_led_resistorESP32
groundP2GND

Deploy the firmware

#include <Arduino.h>
#include <Wire.h>
#include <BH1750.h>
#include <DHT.h>
#include <OneWire.h>
#include <DallasTemperature.h>


// Forward declarations
void IRAM_ATTR countFlowPulse();
void setRelay(int pin, bool on);
float readDistanceCm();

constexpr int I2C_SDA_PIN = 4;
constexpr int I2C_SCL_PIN = 5;
constexpr int DHT_PIN = 7;
constexpr int ONEWIRE_PIN = 6;
constexpr int PH_PIN = 1;
constexpr int TURBIDITY_PIN = 2;
constexpr int TDS_PIN = 3;
constexpr int FLOW_PIN = 8;
constexpr int ULTRASONIC_TRIG_PIN = 9;
constexpr int ULTRASONIC_ECHO_PIN = 10;
constexpr int RELAY_FILTER_PIN = 11;
constexpr int RELAY_PUMP_PIN = 12;
constexpr int RELAY_FAN_PIN = 13;
constexpr int GREEN_LED_PIN = 43;
constexpr int RED_LED_PIN = 44;

constexpr bool RELAY_ACTIVE_LOW = true;
constexpr unsigned long REPORT_INTERVAL_MS = 2000;

BH1750 lightMeter;
DHT dht(DHT_PIN, DHT11);
OneWire oneWire(ONEWIRE_PIN);
DallasTemperature waterTemperature(&oneWire);
volatile uint32_t flowPulses = 0;
unsigned long lastReportMs = 0;

void IRAM_ATTR countFlowPulse() {
  flowPulses++;
}

void setRelay(int pin, bool on) {
  digitalWrite(pin, (on == RELAY_ACTIVE_LOW) ? LOW : HIGH);
}

float readDistanceCm() {
  digitalWrite(ULTRASONIC_TRIG_PIN, LOW);
  delayMicroseconds(3);
  digitalWrite(ULTRASONIC_TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(ULTRASONIC_TRIG_PIN, LOW);
  unsigned long duration = pulseIn(ULTRASONIC_ECHO_PIN, HIGH, 30000UL);
  return duration == 0 ? -1.0f : duration * 0.0343f / 2.0f;
}

void setup() {
  Serial.begin(115200);
  Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
  lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE, 0x23, &Wire);
  dht.begin();
  waterTemperature.begin();

  pinMode(FLOW_PIN, INPUT);
  attachInterrupt(digitalPinToInterrupt(FLOW_PIN), countFlowPulse, RISING);
  pinMode(ULTRASONIC_TRIG_PIN, OUTPUT);
  pinMode(ULTRASONIC_ECHO_PIN, INPUT);
  digitalWrite(ULTRASONIC_TRIG_PIN, LOW);

  pinMode(RELAY_FILTER_PIN, OUTPUT);
  pinMode(RELAY_PUMP_PIN, OUTPUT);
  pinMode(RELAY_FAN_PIN, OUTPUT);
  setRelay(RELAY_FILTER_PIN, false);
  setRelay(RELAY_PUMP_PIN, false);
  setRelay(RELAY_FAN_PIN, false);

  pinMode(GREEN_LED_PIN, OUTPUT);
  pinMode(RED_LED_PIN, OUTPUT);
  digitalWrite(GREEN_LED_PIN, HIGH);
  digitalWrite(RED_LED_PIN, LOW);
  Serial.println("Sistema aeroponico: sensores activos; reles apagados.");
}

void loop() {
  unsigned long now = millis();
  if (now - lastReportMs < REPORT_INTERVAL_MS) return;
  lastReportMs = now;

  float humidity = dht.readHumidity();
  float airTemp = dht.readTemperature();
  waterTemperature.requestTemperatures();
  float waterTemp = waterTemperature.getTempCByIndex(0);
  float lux = lightMeter.readLightLevel();
  int phRaw = analogRead(PH_PIN);
  int turbidityRaw = analogRead(TURBIDITY_PIN);
  int tdsRaw = analogRead(TDS_PIN);
  float distanceCm = readDistanceCm();

  noInterrupts();
  uint32_t pulses = flowPulses;
  flowPulses = 0;
  interrupts();
  float flowLitresPerMinute = pulses / 7.5f / (REPORT_INTERVAL_MS / 1000.0f);

  bool valid = !isnan(humidity) && !isnan(airTemp) && waterTemp != DEVICE_DISCONNECTED_C && lux >= 0;
  digitalWrite(GREEN_LED_PIN, valid ? HIGH : LOW);
  digitalWrite(RED_LED_PIN, valid ? LOW : HIGH);

  Serial.printf("lux=%.1f, aire=%.1f C, humedad=%.1f %%, agua=%.1f C, pHraw=%d, turbidezRaw=%d, TDSraw=%d, caudal=%.2f L/min, nivel=%.1f cm\n",
                lux, airTemp, humidity, waterTemp, phRaw, turbidityRaw, tdsRaw,
                flowLitresPerMinute, distanceCm);
}

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik