Categorías: Arduino

Controlador de nivel de agua en tanque con sensor ultrasonico y Arduino.

Este será el primer tutorial de muchos de la serie Arduino. El propósito del mismo es dar a conocer que está realizando la comunidad de arduineros en cuba. El siguiente tutorial fue realizado por Leonardo Navarro (AKA @LeoNavarro95) miembro de la comunidad cubana de Arduino (https://t.me/ArduinoCuba).

Resumen:

En el presente tutorial se muestra cómo implementar un sistema de control de nivel para tanque elevado basado en ESP32. Como elemento sensor de nivel se escogió un sensor ultrasónico impermeable. El sistema es capaz de detectar si la bomba hidráulica está funcionando correctamente gracias a un flujómetro. La información del nivel y del flujo a la salida de la bomba es mostrada por una pantalla LCD. Un buzzer sirve como interfaz sonora para conocer de posibles eventos en el sistema.

Introducción:

El agua es un recurso de gran valor para la sociedad, de más está decir la necesidad de la misma para realizar las actividades hogareñas. Cuando en una casa falta este recurso nada funciona bien todo depende del agua, desde la limpieza hasta la elaboración de los alimentos. Por lo tanto, resulta incuestionable que este tesoro natural no se derroche y que se aproveche al máximo. En Cuba son muchos los casos en los que nos encontramos con casas que disponen de tanques elevados o cisternas, los cuales se llenan a través de una bomba que succiona agua de un pozo o cisterna.

En mi casa contamos con un pozo y un tanque elevado que se encarga de almacenar el agua que es bombeada. La idea de este proyecto vino principalmente de la molestia que resulta tener que estar al tanto del encendido y apagado de la bomba hidráulica de la casa, además de que cuando el tanque se llena se salpican de agua las casas de los vecinos y también se derrocha el preciado líquido. Por otro lado, nunca nos sucedió, pero si se te olvida que habías puesto la “turbina” (término mal usado en la población cubana, debe ser bomba) y te ibas para la calle por mucho tiempo, se le podía poner el cartel de EPD a la bomba que se iba a quemar. En muchas ocasiones los vecinos eran quienes nos avisaban de que el tanque se estaba desbordando, lo que constituye un sistema sensorial muy primitivo jejeje, y es muy molesto para ellos. Cuidar la bomba hidráulica es un aspecto importantísimo pues estas no son muy baratas que digamos.

Cuando conocí el mundo de Arduino me di cuenta que no era tan complejo hacer un sistema automático para el control del llenado/vaciado del tanque. Y me puse manos a la obra, desarrollé uno que lleva más de un mes funcionando a la perfección, entonces pensé ¿por qué no compartirlo con todos? Así que en el presente tutorial les muestro cómo lo hice.

Listado de materiales para el proyecto:

  • Placa basada en microcontrolador: DOIT ESP32 DevKit V1 (Cantidad x1)

  • Sensor ultrasónico impermeable: JSN SR04T (Cantidad x1)

  • Módulo relé de un canal : (Cantidad x1)

  • Sensor de flujo: Yf-s201 (Cantidad x1)

  • Pantalla LCD 16×2: (Cantidad x1)

  • Placa para prototipo de bakelita (perfboard): (Cantidad x1)

  • Buzzer: (Cantidad x1)

  • Fuente de alimentación de 5V a 500mA (Cantidad x1)

Diagrama de conexiones:

¿Cómo funciona el sistema?

El sistema va a medir el nivel del tanque y a partir de dos parámetros prefijados para el nivel alto y el nivel bajo, va a tomar la decisión de encender o apagar la bomba. En otras palabras, si por ejemplo, se toma como nivel bajo el 40% del total de la capacidad del tanque y como nivel alto el 90%, el sistema va a mandar a encender la bomba (por medio del relé) cuando el nivel sea inferior a 40% hasta que llegue al 90% donde será apagada. Paralelamente a este proceso, mientras esté encendida la bomba, el sistema va a chequear que exista flujo a su salida (que la bomba esté “halando” agua) y en caso de que no exista flujo a la salida de la bomba, la va a mandar a parar, esto es para evitar que se queme, pues una bomba encendida sin bombear agua un tiempo relativamente corto se quema, pues se basa en el agua para su propia lubricación y enfriamiento.

Adicionado a todo esto, en la pantalla LCD se mostrará todo el tiempo los datos del % de agua existente en el tanque, el flujo de agua y notificará en caso de algún error como cuando la bomba no bombea. Como un apoyo a la pantalla, el sistema contará con un buzzer a forma de señal sonora, el que producirá sonidos ante la ocurrencia de eventos como: encendido de la bomba, parada de la bomba y error.

A continuación se presentan un número de pasos que servirán de guía para la realización de este proyecto:

Paso 1: Instalación del sensor ultrasónico.

El sistema se basa principalmente en medir el nivel del tanque por medio del sensor ultrasónico impermeable JSN SR-04T el cual va a estar ubicado en la tapa del tanque apuntando hacia el agua. En la figura que se muestra se aprecia cómo se logra medir el nivel de agua (N) con este sensor, a partir de tener la altura del tanque (H) (que es constante) y la distancia medida por el sensor (S), el nivel de agua sería la resta de la altura del tanque menos la distancia medida por el sensor: N = H – S. Entonces con esa sencilla ecuación es posible conocer el nivel existente en nuestro tanque. Este debería ser el primer paso, colocar el sensor en el tanque y visualizar por la pantalla LCD el nivel de agua existente en el tanque.

Si la distancia existente entre la placa de control del sensor y el microcontrolador es grande no se preocupe, el sistema que monté tiene 13m de cable entre ambos y solo hay una pérdida de 0.1V en tal distancia. Para el cableado de los sensores se empleó cable UTP (conocido también como cable de red o par trenzado). En la imagen que sigue se muestra como quedaría conectado el cable UTP.

Durante estas pruebas con solo el sensor ultrasónico conectado tuve que emplear filtrado digital pues las lecturas del sensor esporádicamente se hacían cero (esto pasa cuando se dan lecturas fuera de rango, sospecho que se deba al eco que se puede producir en el interior del tanque pues es una estructura cerrada). Además, las lecturas se volvían muy ruidosas cuando se encendía la bomba, seguro porque el cable que alimenta a la bomba pasaba junto al del sensor. Con un filtro promedio se resolvió el problema este y las lecturas del sensor se ven en la LCD con estabilidad.

Como el sensor ultrasónico trae separado el sensor de la electrónica es necesario idear alguna forma de que la placa de control quede bien aislada del medio ambiente. En la imagen siguiente se muestra una cajita que se desarrolló para proteger a la placa electrónica que controla al sensor.

Luego de probar tal sensor por unos días y comprobar que todo funcione correctamente se procede al siguiente paso:

Paso 2: Instalación del sensor de flujo y comprobación de su correcto funcionamiento.

Si se quiere que este sensor posea buena precisión es requerida su calibración, como en este caso especial no nos interesa conocer con exactitud la cuantía de flujo, sino su existencia, no se cubrirá tal aspecto. En la imagen se muestra cómo queda el sensor instalado en la tubería.

En la imagen siguiente se muestra el display LCD mostrando los datos de las lecturas del flujómetro. Para comprobar el correcto funcionamiento de tal sensor se simuló como si la bomba hubiese dejado de funcionar, quitándole la alimentación, en todos los casos el sensor se comportó correctamente.

Paso 3: Empleo del módulo relé para controlar el encendido y apagado de la bomba.

En la imagen anterior también ya se estaba haciendo uso del módulo relé, pues se mandaba a encender la bomba un tiempo predeterminado el cual se muestra en la LCD en la primera fila. Por otro lado en estos experimentos el sistema tomaba la decisión de apagar la bomba si el flujómetro no detectaba caudal por la tubería y si todo marchaba bien al terminarse el tiempo programado se apagaba la bomba.

Paso 4: Unión de todo para conformar el sistema completo.

En la siguiente figura se muestra todo el sistema en una caja plástica. Como fuente de alimentación (esquina derecha superior dentro de la caja) se escogió una fuente de un DVD que se encontraba obsoleto, pero se puede emplear cualquier fuente de cargador de teléfono móvil que de una salida de corriente mayor que 250mA.

La pantalla LCD mostrando los datos de los sensores. La flecha indica que se está bombeando, si estuviese hacia abajo indica lo contrario.

Programación del microcontrolador

Para programar el microcontrolador se emplea el IDE de Arduino, dado que se empleará una placa ESP32, hay que instalarla en el IDE, seguir este tutorial para ello. Una vez instalada la placa ESP32 en el IDE de Arduino se selecciona el modelo de la placa y el puerto al cual se encuentra conectada:

Aquí les dejo el código del proyecto:

//##############################PANTALLA DE CRISTAL LIQUIDO#############################
#include<LiquidCrystal.h>
const int rs = 13, en = 12, d4 = 14, d5 = 27, d6 = 26, d7 = 25;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

byte bombaOn[8] = {
  0b00100,
  0b01110,
  0b11111,
  0b00100,
  0b00100,
  0b00100,
  0b00100,
  0b11111
};
byte bombaOff[8] = {
  0b11111,
  0b00100,
  0b00100,
  0b00100,
  0b00100,
  0b11111,
  0b01110,
  0b00100
};

//#########################BUZZER CONTROL#############################################################
#define BUZZER_PIN 4 //Buzzer BUZZER_PIN

double demora = 0;

void  buzzer_warning() {
  for (int i = 0; i < 2; i++) {
    demora = 200;
    for (int i = 0; i < 1000; i++) {
      digitalWrite(BUZZER_PIN, HIGH);
      delayMicroseconds(demora);
      digitalWrite(BUZZER_PIN, LOW);
      delayMicroseconds(demora);
    }

    digitalWrite(BUZZER_PIN, LOW);
    delay(50);
  }
}
void  buzzer_finish() {
  // put your main code here, to run repeatedly:
  for (int i = 0; i < 2; i++) {
    demora = 200;
    for (int i = 0; i < 1000; i++) {
      digitalWrite(BUZZER_PIN, HIGH);
      delayMicroseconds(demora);
      digitalWrite(BUZZER_PIN, LOW);
      delayMicroseconds(demora);
    }

    digitalWrite(BUZZER_PIN, LOW);
    delay(50);

    demora = 300;
    for (int i = 0; i < 1000; i++) {
      digitalWrite(BUZZER_PIN, HIGH);
      delayMicroseconds(demora);
      digitalWrite(BUZZER_PIN, LOW);
      delayMicroseconds(demora);
    }

    digitalWrite(BUZZER_PIN, LOW);
    delay(50);

    demora = 600;
    for (int i = 0; i < 1000; i++) {
      digitalWrite(BUZZER_PIN, HIGH);
      delayMicroseconds(demora);
      digitalWrite(BUZZER_PIN, LOW);
      delayMicroseconds(demora);
    }
    digitalWrite(BUZZER_PIN, LOW);
    delay(100);

  }
}

#define CTROL_RELAY_GPIO 23 //GPIO DESTINADO A CONRTROLAR EL RELAY QUE GOVIERNA LA BOMBA

//#################filtro promedio###################################
const int numReadings = 20;

double readings[numReadings];      // the readings from the analog input
int readIndex = 0;              // the index of the current reading
double total = 0;                  // the running total
double average = 0;                // the average

double filtroProm(double entrada) {
  // subtract the last reading:
  total = total - readings[readIndex];
  // read from the sensor:
  readings[readIndex] = entrada;
  // add the reading to the total:
  total = total + readings[readIndex];
  // advance to the next position in the array:
  readIndex++;

  // if we're at the end of the array...
  if (readIndex >= numReadings) {
    // ...wrap around to the beginning:
    readIndex = 0;
  }

  // calculate the average:
  average = total / numReadings;
  return average;
}

//#############MEDICION CON EL SENSOR ULTRASONICO#########################
#define DIST_TOPE 120// nivel maximo, medida con el tanque vacio en cm
const int trigPin = 2;
const int echoPin = 5;

float distancia = 0;
int nivel = 0; //nivel en porciento

long duration = 0;
float distance = 0;
float distanciaPromedio = 0;

float get_dist() {
  // Clears the trigPin
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);

  // Sets the trigPin on HIGH state for 10 micro seconds
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  // Reads the echoPin, returns the sound wave travel time in microseconds
  duration = pulseIn(echoPin, HIGH, 200000);//timeout de 200000 microsegundos que es 200ms

  // Calculating the distance
  //distance = (float)(duration * 0.03432 )/ 2;
  return (float)(duration * 0.03432 ) / 2;
}

//#############OBTENCION DEL NIVEL EN PORCIENTO###########################
int ultrasonic_fail = 0;//contador para determinar fallo del sensor ultrasonico

void get_level() {
  distance = get_dist();
  if (distance == 0)ultrasonic_fail++; //se incrementa el contador

  if (ultrasonic_fail == 5) { //si llega a 5 veces concecutivas con lecturas a cero es que algo anda mal con el sensor
    digitalWrite(CTROL_RELAY_GPIO, LOW);
    lcd.clear();
    lcd.print("Error de sensor");
    while (1) {
    }
  }

  if (distance > 0) {//descarta errores del sensor

    ultrasonic_fail = 0; //se resetea el contador

    distanciaPromedio = filtroProm(distance);

    Serial.print(distance); Serial.print(" ; ");
    Serial.println(distanciaPromedio);

    nivel = DIST_TOPE - distanciaPromedio;
    nivel = map(nivel, 0, DIST_TOPE - 30, 0, 100);//90 seria el nivel maximo por seguridad que del sensor (30cm)
    // Serial.println("    Nivel " + String(nivel));

    lcd.setCursor(8, 1);
    lcd.print("   ");
    lcd.setCursor(8, 1);
    lcd.print(nivel);
    lcd.display();
  }
}

//#####################ALGORITMO PARA CONTROL AUTOMATICO DE BOMBA#################
boolean llenando = true;
int bombaFail_counter = 0;
//OJO ESTOS NIVELES ESTAN EN PORCIENTO
#define NIVEL_BAJO 70 //nivel bajo porcentual a partir del cual enciende la bomba 
#define NIVEL_ALTO 100//nivel alto porcentual a partir del cual apaga la bomba

bool aviso = true;
bool pump_on = false;

void control_Pump(unsigned int ControlPin) {

  if (nivel <= NIVEL_BAJO && llenando == true) {
    digitalWrite(ControlPin, HIGH);
    lcd.setCursor(6, 1);
    lcd.write(byte(1));//bomba encendida
    pump_on = true;
  }
  if (nivel <= NIVEL_ALTO && llenando == true) {
    digitalWrite(ControlPin, HIGH);
    lcd.setCursor(6, 1);
    lcd.write(byte(1));//bomba encendida
    pump_on = true;
  }

  if (nivel >= NIVEL_ALTO) {
    digitalWrite(ControlPin, LOW);
    llenando = false;
    lcd.setCursor(6, 1);
    lcd.write(byte(0));//bomba apagada
    pump_on = false;

    if (aviso) { //bandera para que suene una sola vez
      buzzer_finish();//solo hace falta que suene una sola vez
      aviso = false;
    }
    bombaFail_counter = 0;//se resetea
  }
  if (nivel <= NIVEL_BAJO && llenando == false) {
    digitalWrite(ControlPin, HIGH);
    lcd.setCursor(6, 1);
    lcd.write(byte(1));//bomba encendida
    pump_on = true;
    aviso = true;
  }
}
//##############Sensor de Flujo #################################################################
byte sensorPin       = 18;
float calibrationFactor = 7;//7.5;
volatile byte pulseCount;

float flowRate = 0;

unsigned long oldTime = 0;
/*
  Insterrupt Service Routine
*/
void IRAM_ATTR pulseCounter()
{
  // Increment the pulse counter
  pulseCount++;
}

void check_flujo() {

  if (pump_on) {
    if ((millis() - oldTime) > 1000)   // Only process counters once per second
    {

      detachInterrupt(sensorPin);
      flowRate = ((1000.0 / (millis() - oldTime)) * pulseCount) / calibrationFactor;//flujo en litros por minuto
      oldTime = millis();

      //    unsigned int frac;

      // Print the flow rate for this second in litres / minute
      Serial.print("Flow rate: ");
      Serial.print(int(flowRate));  // Print the integer part of the variable
      Serial.println("L/min");

      lcd.setCursor(6, 0);
      lcd.print("  ");
      lcd.setCursor(6, 0);
      lcd.print(flowRate, 0);

      // Reset the pulse counter so we can start incrementing again
      pulseCount = 0;

      // Enable the interrupt again now that we've finished sending output
      attachInterrupt(sensorPin, pulseCounter, FALLING);
    }
    // se chequea que el flujo no sea menor que 5
    if (int(flowRate) <= 5) { //no esta bombeando eficientemente o no lo esta haciendo
      buzzer_warning();
      bombaFail_counter ++;
      if (bombaFail_counter > 5) {
        digitalWrite(CTROL_RELAY_GPIO, LOW);
        lcd.clear();
        lcd.print("Error de bomba");
        lcd.display();
        while (1) {}
      }

    }
  }
  else { //si la bomba esta apagada
    lcd.setCursor(6, 0);
    lcd.print("  ");
    lcd.setCursor(6, 0);
    lcd.print("0");//no hay flujo, esta apagada la bomba
  }
}

//####################----SETUP----###############################################
void setup() {

  Serial.begin(115200); // Starts the serial communication
  pinMode(CTROL_RELAY_GPIO, OUTPUT);//salida bomba
  digitalWrite(CTROL_RELAY_GPIO, LOW);
  pinMode(BUZZER_PIN, OUTPUT);//salida del buzzer
  pinMode(sensorPin, INPUT_PULLUP);
  attachInterrupt(sensorPin, pulseCounter, FALLING);

  lcd.begin(16, 2);
  // create a new character
  lcd.createChar(0, bombaOff);
  // create a new character
  lcd.createChar(1, bombaOn);

  lcd.setCursor(0, 0);
  lcd.print("Tank System");
  lcd.setCursor(0, 1);
  lcd.print("Inicializando");
  lcd.display();


  pinMode(trigPin, OUTPUT); // Sets the trigPin as an Output
  pinMode(echoPin, INPUT); // Sets the echoPin as an Input

  //inicializando el arreglo para el filtro promedidiador
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings[thisReading] = 0;
  }
  //para evitar la demora que adiciona el filtro al inicio
  for (int i = 0; i < 20; ) {
    distance = get_dist();
    if (distance > 0) {
      filtroProm(distance);
      i++;
    }

    delay(50);
  }
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Flujo:");
  lcd.setCursor(9, 0);
  lcd.print("L/min");
  lcd.setCursor(0, 1);
  lcd.print("Nivel:");
  lcd.setCursor(11, 1);
  lcd.print("%");

}


void loop() {
  //se obtiene el nivel
  get_level();
  // a partir de las lecturas de nivel se decide si encender o apagar la bomba 
  control_Pump(CTROL_RELAY_GPIO);
  //delay para estabilidad de las medidas de los sensores
  delay(1000);
  check_flujo(); //si la bomba esta encendida revisar que exista flujo a su salida
}

Para los que les interese mejorar y participar en este proyecto mejore el código usando VSCODE y PLATFORMIO y lo subí a github.Las mejoras en esta versión son:

    • Código más organizado por módulos.
    • Actualización via OTA
    • Uso de tareas para aprovechar los núcleos del ESP32
    • Implementación de una web para el control del proyecto

Próximamente les compartiré el mismo código con muchísimas mejoras más. Como usar el modo 3 del sensor el cual manda el dato por serial es más exacto y económico. También el uso de 2 sensores ultrasónicos uno para el tanque elevado y el otro para la cisterna. Código más modular aun, permitiendo activar o desactivar módulos de software sino usa determinado sensor o actuador. Así como animaciones en el display.

Asi es como quedo el proyecto finalmente

Espero que este humilde aporte les sirva a muchos en sus casas. Siéntanse libre de modificar el código a su gusto y de darme recomendaciones para su mejora.

 

¿De cuánta utilidad te ha parecido este contenido?

Alexander Rivas Alpizar

Administrador de Redes EMPRESTUR Cienfuegos

Ver comentarios

  • Saludos,
    Muy buen proyecto! Bien detallado e interesante! Es una pena que se haya dificultado tanto la adquisición de módulos de arduino por estos tiempos.
    De paso les pregunto: alguien me puede recomendar un canal de telegram para Arduino en Cuba? El que me aparece está como "grupo privado".
    Gracias!

  • Hola, tendrán el esquematico en archivo? Para fritzing o proteus? no encuentro el esquemático para este sensor de distancia.

    • Estas pidiendo el diagrama del Sensor ultrasónico impermeable: JSN SR04T?? Para que lo quiere??

  • gracias por la informacion, queria preguntarte si tienes este proyecto en un informe de investigacion?

  • Hola soy cubano pero trabajo en mexico, y de hecho actualmente estoy trabajando con ese sensor en varios proyectos y te recomiendo usar una funcion diferente para computar el average de las mediciones, pues no es la mejor aunque se que la copiaste de de internet, de hecho si graficas en el monitor serie del IDE de aurduino vas a ver que no estan exacta es funcion comparada con la distancia medida, te voy a hacer una gran recomendacion usa el algoritmo de Kalman o filtro de Kalman que te da una aproximacion mas exacta de la distancia medida sin procesar y te protege de los efectos de eco que te puediara dar el sensor ultrasonico que es muy frecuente, adeemas para hacer mas ciencia tienes que tener en cuenta que las mediciones ultrasonicas son afectadas por la temperatura pues la velocidad del sonido varia con la temperatura por lo cual deberias hacer una correccion en el calculo de la distancia , saludos por lo demas muy buen trabajo y mas en cuba que no hay nada

  • estimado muy bueno su tutorial, pero será posible que tenga como va el circuito conectado a la bomba y la tubería que pasa por el sensor de flujo donde nace y hacia donde se dirige además de la conexión a la fuente de poder, estoy construyendo mi casa y cualquier ayuda para disminuir los gastos es bueno y si lo puedo hacer , agradezco si me echas una mano

    • Hola, el sensor de flujo va de maravilla, lo que no te puedo comentar de su exactitud en las mediciones, pues solo lo uso para detectar la existencia de flujo; El sistema lleva un poco más de 1 año funcionando y todo va perfecto

Compartir
Publicado por
Alexander Rivas Alpizar

Entradas recientes

Alta disponibilidad de sus base de datos con Percona XtraDB Cluster en Kubernetes

Uno de los grandes retos al que nos podemos enfrentar cuando una aplicación crece, es…

8 meses hace

Home automation (Parte 3) – ESPHome

Qué es lo que deseo hacer en este capítulo? Básicamente un sonoff, quiero encender/apagar las…

1 año hace

Home automation (Parte 2) – Home Assistant

Hace algunos meses estoy escuchando hablar del proyecto Home Assistant (HA). En palabras literales del…

1 año hace

Home automation (Parte 1)

Desde hace varios meses vengo con la idea de automatizar la casa donde vivo. Poco…

1 año hace

Cocinando una imagen personalizada de OpenWRT

El artículo describe el uso para un caso particular de OpenWRT y la creación de…

1 año hace