Saltar al contenido
Nivel inicial

Controlar el llenado de un tanque

Preparar el tanque

Para comenzar, es necesario conseguir un recipiente que cumpla la función del tanque. Este debe tener una tapa, un agujero en la parte superior para el ingreso del agua, y otro en la parte inferior donde se colocará la llave de paso que controlará la distribución del agua.

Instalar el sensor ultrasónico y conectar la electroválvula

En este proyecto la ubicación de los distintos elementos en el prototipo es fundamental para el buen funcionamiento del mismo. Por lo tanto, organizamos la disposición de todos los componentes antes de realizar la conexión a la placa de control. Esta placa puede recibir datos de un sensor, así como enviar datos a un actuador, por medio de sus pines analógicos y digitales.

Para controlar el ingreso de agua y evitar que el tanque rebalse, colocamos en su entrada una electroválvula (válvula controlada electrónicamente, del tipo de las que suelen encontrarse en los lavarropas). Esta se abrirá cuando queramos llenar el tanque y se cerrará cuando el agua haya alcanzado un nivel determinado por nosotros. El original de 2019 usaba una válvula de 220 V; en esta reedición usamos una de 12 V, que enseña exactamente lo mismo sin meter tensión de red en la maqueta (el desvío está documentado en la ficha del proyecto).

Sujetamos el sensor ultrasónico en la parte interior de la tapa del tanque. Es importante que este apunte de manera perpendicular al fondo del tanque, para que las paredes no interfieran en la medición. Además, hay que asegurarnos de que el sensor no se moje cuando ingresa el agua. El sensor medirá la distancia a la que se encuentra el agua y, en función de eso, se activará o no la electroválvula.

Esquema del tanque: recipiente con el sensor ultrasónico fijado bajo la tapa emitiendo ondas hacia la superficie del agua, la electroválvula en la entrada de agua superior derecha y la manguera de salida en la parte inferior izquierda.
El tanque del nivel inicial: el sensor ultrasónico mide desde la tapa la distancia hasta el agua, y la electroválvula controla la entrada (figura original del proyecto).

Fijate en el esquema la lógica de la medición: el sensor mira al agua desde arriba, así que a menor distancia medida, más lleno está el tanque. Esa inversión va a aparecer todo el tiempo en el código y conviene tenerla clara desde ahora.

Establecer la altura de corte

Para evitar que el tanque rebalse, el agua no puede superar la altura de ingreso. Por lo tanto, debemos detener la entrada de agua antes de que se alcance dicho nivel. Para asegurarnos de que eso suceda, es recomendable que el tanque se deje de llenar cuando el agua esté a unos 5 o 10 cm del ingreso. Una vez determinado este nivel, hay que medir la distancia desde este punto hasta la tapa donde se encuentra el sensor, y establecer este valor como la distancia mínima de desactivación de la válvula.

Ese número es la altura de corte de tu maqueta y es el que vas a cargar en la constante DISTANCIA_MINIMA_CM del programa del paso 5. La guía original usa 40 cm como ejemplo para un tanque real; en un recipiente de aula el valor va a ser mucho menor, y por eso se mide, no se copia.

Conectar sensor, válvula y relé a la placa

Una vez que tengamos ubicados los distintos elementos del sistema, conectaremos el sensor ultrasónico, la electroválvula y el relé a la placa de control, como indica la figura.

Esquema de conexión: placa Arduino UNO unida a un sensor ultrasónico HC-SR04 rotulado Nivel Tanque 1 (VCC a 5V, GND a GND, Trig y Echo a los pines 8 y 9) y a un módulo relé cuyo pin de señal va al pin 4; en los bornes del relé, la electroválvula 1 y un enchufe de 220 V.
Esquema de conexión del nivel inicial (figura original del proyecto). Atención: el original alimentaba la válvula desde un enchufe de 220 V; en esta reedición ese cableado se reemplaza por una fuente de 12 V.

¡Atención! Agua y electricidad no se llevan bien. Todas las conexiones eléctricas van fuera del tanque y por encima del nivel del agua, los cables entran al tanque solo donde es imprescindible (el sensor, bajo la tapa), y siempre que manipules el circuito hacelo con las manos secas y con la maqueta desenchufada. El original trabajaba directamente con 220 V; en esta reedición la válvula es de 12 V, pero los hábitos de seguridad se practican igual, porque son los que te protegen cuando la instalación es real.

La conexión tiene dos partes:

  • El circuito de potencia (12 V): la fuente de 12 V, el relé y la electroválvula. Conectamos un cable de la fuente de 12 V a la bornera COM del relé, un segundo cable desde la bornera NO del relé hasta un borne de la electroválvula, y un tercer cable que vuelve del otro borne de la electroválvula al otro polo de la fuente. Así, la válvula solo recibe energía cuando el relé cierra el contacto.
  • El circuito de control (5 V): el sensor y la señal del relé hacia la placa, con cables dupont macho-hembra.

En el Arduino UNO, seguimos el esquema original: conectamos los pines Vcc, GND, Trig y Echo del sensor a los pines 5V, GND, 8 y 9 de la placa, respectivamente (en el código, los pines 8 y 9 son las constantes PIN_TRIG1 y PIN_ECHO1). Del módulo relé, el pin + va a 5V, el pin - a GND y el pin S al pin digital 4 (la constante PIN_RELE_VALVULA).

En el ESP32 el armado es el mismo, pero los pines se llaman GPIO y usamos otros números (los GPIO 0, 2, 12 y 15 se reservan porque intervienen en el arranque de la placa). Los números exactos están en las constantes PIN_TRIG1, PIN_ECHO1 y PIN_RELE_VALVULA al comienzo del código del paso siguiente: conectá cada cable al GPIO que indica su constante. Dos cuidados propios de esta placa: el pin Echo del HC-SR04 entrega 5 V, así que interponé un divisor de dos resistencias antes del GPIO; y alimentá el módulo relé desde el pin VIN (5 V del USB) si no conmuta con la señal de 3,3 V.

Programar el funcionamiento del dispositivo

La programación la realizaremos directamente en el lenguaje de Arduino (C++), usando el Arduino IDE en su versión 2.x, que se descarga gratis desde arduino.cc/en/software. Un programa de Arduino se llama sketch y tiene dos bloques: setup(), que se ejecuta una sola vez al inicializarse el dispositivo, y loop(), que se ejecuta repetidamente después. Los comentarios (todo lo que sigue a //) no tienen efecto en el programa: están para que el código sea más fácil de interpretar.

La lógica que queremos programar es la que definimos en los pasos anteriores: medir la distancia entre el sensor y la superficie del agua y, mientras esa distancia sea mayor que la altura de corte, mantener abierta la válvula para que se cargue el tanque; cuando el agua llega a la altura de corte, cerrarla.

Para medir, el programa dispara desde el pin Trig un pulso de sonido ultrasónico y mide en el pin Echo cuánto tarda en volver el rebote: como el sonido recorre ida y vuelta unos 58 microsegundos por cada centímetro, dividiendo el tiempo por ese valor se obtiene la distancia. Esa cuenta vive en la función medirDistanciaCm() del código, comentada línea por línea.

Ver el código completo · control-llenado.ino
control-llenado.ino Arduino UNO
// Control de llenado de un tanque: el sensor ultrasónico HC-SR04 mide la
// distancia entre la tapa y la superficie del agua; cuando el agua llega
// a la altura de corte, un relé cierra la electroválvula de entrada.
// Menor distancia = tanque más lleno: el sensor mira al agua desde arriba.
//
// SEGURIDAD: agua y electricidad no se llevan bien. Todas las conexiones
// eléctricas van fuera y POR ENCIMA del nivel del agua, y el sensor no se
// debe mojar. La práctica se realiza con una electroválvula de baja
// tensión (12 V); la conexión a 220 V de la guía original queda reservada
// al docente o a una instalación supervisada, y nunca sobre la protoboard
// (desvío pedagógico acordado).
//
// Plataforma: Arduino UNO.
// Conexión (la del esquema original): TRIG del sensor → pin 8, ECHO → pin 9,
// entrada "S" del módulo relé de la válvula → pin 4.
//
// Basado en: Fragmento de código 1 — Sistema de Tanques,
// Saberes Digitales (INET/EDUCAR 2019)
// Modernización: (1) se reemplazó el código generado por mBlock3 (_delay,
// _loop, pinMode dentro de la función de medición) por C++ idiomático con
// constantes nombradas y pinMode en setup(). (2) Se agregó una histéresis
// de 5 cm: el original abría y cerraba la válvula en el mismo valor exacto
// (40 cm), y el ruido de medición del HC-SR04 (±1 cm típico) haría
// traquetear el relé justo en el borde.

// Sensor ultrasónico del tanque (TRIG dispara el pulso, ECHO lo recibe).
const int PIN_TRIG1 = 8;
const int PIN_ECHO1 = 9;

// Relé que controla la electroválvula de entrada de agua.
const int PIN_RELE_VALVULA = 4;

// Módulo relé activo-alto, como en el original (HIGH = válvula abierta).
// Si tu módulo es activo-bajo, invertí solo estas dos constantes.
const int RELE_ENCENDIDO = HIGH; // válvula abierta: entra agua
const int RELE_APAGADO   = LOW;  // válvula cerrada

// Altura de corte del original: si el agua queda a menos de 40 cm del
// sensor, el tanque está lleno. Este valor NO se inventa: sale de medir
// en tu maqueta la distancia entre el sensor y el nivel máximo deseado
// (5 a 10 cm por debajo del ingreso de agua, dice la guía).
const float DISTANCIA_MINIMA_CM = 40.0;

// Colchón de la histéresis: la válvula cierra a 40 cm pero recién vuelve
// a abrir cuando el agua baja más allá de 45 cm. Sin este margen, una
// medición que oscile entre 39.5 y 40.5 abriría y cerraría el relé sin
// parar (modernización, ver encabezado).
const float MARGEN_HISTERESIS_CM = 5.0;

// El original medía una vez por segundo (_delay(1)).
const unsigned long INTERVALO_MEDICION_MS = 1000;

// El eco tarda 58 microsegundos por cada centímetro de distancia (ida y
// vuelta del sonido a ~343 m/s). El timeout evita quedar esperando para
// siempre un eco que no llega.
const float MICROSEGUNDOS_POR_CM = 58.0;
const unsigned long TIMEOUT_ECO_US = 30000;

// Mide la distancia en cm entre el sensor y la superficie del agua.
// Si en 30 ms no llega el eco (sensor desconectado o fuera de rango),
// pulseIn devuelve 0 y la distancia da 0: el programa lo interpreta como
// "tanque lleno" y cierra la válvula. Es una falla SEGURA: ante la duda,
// que no entre agua.
float medirDistanciaCm(int pinTrig, int pinEcho) {
  digitalWrite(pinTrig, LOW);
  delayMicroseconds(2);
  digitalWrite(pinTrig, HIGH); // pulso de disparo de 10 microsegundos
  delayMicroseconds(10);
  digitalWrite(pinTrig, LOW);
  return pulseIn(pinEcho, HIGH, TIMEOUT_ECO_US) / MICROSEGUNDOS_POR_CM;
}

void setup() {
  pinMode(PIN_TRIG1, OUTPUT);
  pinMode(PIN_ECHO1, INPUT);
  pinMode(PIN_RELE_VALVULA, OUTPUT);

  // Arrancamos con la válvula cerrada hasta la primera medición:
  // el estado seguro de un tanque es "no entra agua".
  digitalWrite(PIN_RELE_VALVULA, RELE_APAGADO);
}

void loop() {
  float distancia = medirDistanciaCm(PIN_TRIG1, PIN_ECHO1);

  if (distancia < DISTANCIA_MINIMA_CM) {
    // El agua llegó a la altura de corte: cerrar la válvula.
    digitalWrite(PIN_RELE_VALVULA, RELE_APAGADO);
  } else if (distancia > DISTANCIA_MINIMA_CM + MARGEN_HISTERESIS_CM) {
    // Hay lugar de sobra: abrir la válvula para cargar el tanque.
    digitalWrite(PIN_RELE_VALVULA, RELE_ENCENDIDO);
  }
  // Entre 40 y 45 cm no escribimos nada: el relé conserva su último
  // estado. Esa franja "muerta" es la histéresis que evita el traqueteo.

  delay(INTERVALO_MEDICION_MS);
}
control-llenado.ino ESP32
// Control de llenado de un tanque: el sensor ultrasónico HC-SR04 mide la
// distancia entre la tapa y la superficie del agua; cuando el agua llega
// a la altura de corte, un relé cierra la electroválvula de entrada.
// Menor distancia = tanque más lleno: el sensor mira al agua desde arriba.
//
// SEGURIDAD: agua y electricidad no se llevan bien. Todas las conexiones
// eléctricas van fuera y POR ENCIMA del nivel del agua, y el sensor no se
// debe mojar. La práctica se realiza con una electroválvula de baja
// tensión (12 V); la conexión a 220 V de la guía original queda reservada
// al docente o a una instalación supervisada, y nunca sobre la protoboard
// (desvío pedagógico acordado).
//
// Plataforma: ESP32 (DevKit v1).
// Conexión: TRIG → GPIO 5, ECHO → GPIO 18 (¡leer nota!), relé de la
// válvula → GPIO 25.
// NOTA ELÉCTRICA (¡importante!): el ESP32 trabaja a 3,3 V. El HC-SR04
// clásico se alimenta con 5 V y su pin ECHO devuelve el eco a 5 V: eso
// dañaría el GPIO. Es OBLIGATORIO un divisor de tensión en ECHO
// (ECHO —1 kΩ— GPIO 18 —2 kΩ— GND), que baja los 5 V a ~3,3 V. Es la
// misma idea del divisor del LDR: dos resistencias que reparten la
// tensión. TRIG no necesita nada: los 3,3 V del GPIO alcanzan para
// dispararlo. Alternativa sin divisor: el modelo HC-SR04P, que puede
// alimentarse directamente con 3,3 V.
//
// Basado en: Fragmento de código 1 — Sistema de Tanques,
// Saberes Digitales (INET/EDUCAR 2019)
// Modernización: (1) se reemplazó el código generado por mBlock3 (_delay,
// _loop, pinMode dentro de la función de medición) por C++ idiomático con
// constantes nombradas y pinMode en setup(); se portó a ESP32 (el original
// usaba los pines 8/9/4 del UNO). (2) Se agregó una histéresis de 5 cm:
// el original abría y cerraba la válvula en el mismo valor exacto (40 cm),
// y el ruido de medición del HC-SR04 (±1 cm típico) haría traquetear el
// relé justo en el borde.

// Sensor ultrasónico del tanque (TRIG dispara el pulso, ECHO lo recibe).
const int PIN_TRIG1 = 5;
const int PIN_ECHO1 = 18; // con divisor de tensión 1 kΩ / 2 kΩ (ver nota)

// Relé que controla la electroválvula de entrada de agua.
const int PIN_RELE_VALVULA = 25;

// Módulo relé activo-alto, como en el original (HIGH = válvula abierta).
// Si tu módulo es activo-bajo, invertí solo estas dos constantes.
const int RELE_ENCENDIDO = HIGH; // válvula abierta: entra agua
const int RELE_APAGADO   = LOW;  // válvula cerrada

// Altura de corte del original: si el agua queda a menos de 40 cm del
// sensor, el tanque está lleno. Este valor NO se inventa: sale de medir
// en tu maqueta la distancia entre el sensor y el nivel máximo deseado
// (5 a 10 cm por debajo del ingreso de agua, dice la guía).
const float DISTANCIA_MINIMA_CM = 40.0;

// Colchón de la histéresis: la válvula cierra a 40 cm pero recién vuelve
// a abrir cuando el agua baja más allá de 45 cm. Sin este margen, una
// medición que oscile entre 39.5 y 40.5 abriría y cerraría el relé sin
// parar (modernización, ver encabezado).
const float MARGEN_HISTERESIS_CM = 5.0;

// El original medía una vez por segundo (_delay(1)).
const unsigned long INTERVALO_MEDICION_MS = 1000;

// El eco tarda 58 microsegundos por cada centímetro de distancia (ida y
// vuelta del sonido a ~343 m/s). El timeout evita quedar esperando para
// siempre un eco que no llega.
const float MICROSEGUNDOS_POR_CM = 58.0;
const unsigned long TIMEOUT_ECO_US = 30000;

// Mide la distancia en cm entre el sensor y la superficie del agua.
// Si en 30 ms no llega el eco (sensor desconectado o fuera de rango),
// pulseIn devuelve 0 y la distancia da 0: el programa lo interpreta como
// "tanque lleno" y cierra la válvula. Es una falla SEGURA: ante la duda,
// que no entre agua.
float medirDistanciaCm(int pinTrig, int pinEcho) {
  digitalWrite(pinTrig, LOW);
  delayMicroseconds(2);
  digitalWrite(pinTrig, HIGH); // pulso de disparo de 10 microsegundos
  delayMicroseconds(10);
  digitalWrite(pinTrig, LOW);
  return pulseIn(pinEcho, HIGH, TIMEOUT_ECO_US) / MICROSEGUNDOS_POR_CM;
}

void setup() {
  pinMode(PIN_TRIG1, OUTPUT);
  pinMode(PIN_ECHO1, INPUT);
  pinMode(PIN_RELE_VALVULA, OUTPUT);

  // Arrancamos con la válvula cerrada hasta la primera medición:
  // el estado seguro de un tanque es "no entra agua".
  digitalWrite(PIN_RELE_VALVULA, RELE_APAGADO);
}

void loop() {
  float distancia = medirDistanciaCm(PIN_TRIG1, PIN_ECHO1);

  if (distancia < DISTANCIA_MINIMA_CM) {
    // El agua llegó a la altura de corte: cerrar la válvula.
    digitalWrite(PIN_RELE_VALVULA, RELE_APAGADO);
  } else if (distancia > DISTANCIA_MINIMA_CM + MARGEN_HISTERESIS_CM) {
    // Hay lugar de sobra: abrir la válvula para cargar el tanque.
    digitalWrite(PIN_RELE_VALVULA, RELE_ENCENDIDO);
  }
  // Entre 40 y 45 cm no escribimos nada: el relé conserva su último
  // estado. Esa franja "muerta" es la histéresis que evita el traqueteo.

  delay(INTERVALO_MEDICION_MS);
}
El control de llenado: si el agua está más cerca que DISTANCIA_MINIMA_CM, la válvula se cierra; si está más lejos que la altura de corte más el margen de histéresis, se abre.

Antes de cargarlo, acordate de reemplazar el valor de DISTANCIA_MINIMA_CM por la altura de corte que mediste en el paso 3.

Hay un detalle del código que la guía original no tenía: la constante MARGEN_HISTERESIS_CM. El original abría y cerraba la válvula comparando contra el mismo valor exacto (40 cm), pero las mediciones del sensor tienen un pequeño ruido (±1 cm es típico): justo en el borde, el relé quedaría abriendo y cerrando sin parar. Con el margen, la válvula corta al llegar a la altura de corte y recién reabre cuando el agua bajó 5 cm más: esa franja “muerta” es la histéresis, y es la respuesta al desafío del paso 3.

Podés ver el comportamiento completo —el tanque llenándose, el sensor midiendo y la válvula cortando en la altura de corte— en el simulador, antes de tocar el agua real:

Simulación del comportamiento

La misma máquina de estados del firmware, portada a TypeScript, maneja el comportamiento del dispositivo. No se ejecuta el binario real: la ejecución del .hex con avr8js está en el roadmap.

Simulación lista. Presioná «Iniciar» para arrancar la secuencia.

Subir el código a la placa

Para subir el código de nuestro programa a la placa, necesitamos:

  1. Conectar la placa a la computadora con el cable USB.
  2. Chequear que en el selector de placa del Arduino IDE esté seleccionada la placa correcta.
  3. Seleccionar el puerto al que está conectada la placa.
  4. Cliquear el botón Subir (la flecha →) en la parte superior de la interfaz.

En el Arduino IDE 2.x, la placa y el puerto se eligen juntos en el menú desplegable de la barra superior: al conectar la placa suele aparecer directamente como opción. Si no aparece, entrá a “Select other board and port” y buscala por nombre.

Para el Arduino UNO: usá el cable USB tipo B, elegí la placa Arduino UNO y el puerto que se agregó al conectarla (en Windows es un puerto COM numerado; en Linux suele llamarse /dev/ttyACM0 o similar).

Para el ESP32 hay dos pasos previos que se hacen una sola vez: instalar el soporte de placas ESP32 desde el gestor de placas del IDE (buscá “esp32” e instalá el paquete de Espressif) y, si el puerto no aparece, instalar el driver del conversor USB-serie de tu placa (generalmente CP210x o CH340). Después, elegí la placa ESP32 Dev Module (o “DOIT ESP32 DEVKIT V1”) y el puerto correspondiente. Si al subir el IDE queda esperando en “Connecting…”, mantené apretado el botón BOOT de la placa hasta que empiece la carga.

Al terminar de subir nuestro código, veremos el mensaje de carga completada en la barra inferior del IDE.

Cierre del nivel

Al terminar este nivel, el tanque de Camilo se llena solo y sin rebalsar: el sensor ultrasónico mide desde la tapa la distancia hasta el agua, y la placa cierra la electroválvula cuando se alcanza la altura de corte que vos mismo mediste, con el margen de histéresis evitando el traqueteo del relé. Además, ya sabés subir programas desde el Arduino IDE 2.x, una herramienta que te va a acompañar el resto del proyecto. En el nivel intermedio el sistema crece a la escala de un edificio: un segundo tanque, una bomba sumergible y una regla nueva —la bomba jamás trabaja en seco—.

Evidencia de cierre

Al terminar este nivel, esto tiene que estar funcionando:

  • El tanque tiene el sensor ultrasónico fijado bajo la tapa, apuntando perpendicular al fondo y protegido del agua, y la electroválvula instalada en su entrada.
  • Mediste la altura de corte de tu tanque y ese valor está cargado en la constante DISTANCIA_MINIMA_CM del programa.
  • La placa cierra la válvula cuando el agua llega a la altura de corte y la reabre cuando el nivel baja más allá del margen de histéresis.
  • Sabés subir un programa desde el Arduino IDE 2.x: elegir la placa, elegir el puerto y verificar que el código quedó cargado.