El verano está próximo y con los calores llegan los problemas con la presión en la red de agua y ya sea que tengamos una cisterna a nivel del suelo o elevada sobre el techo de la vivienda siempre es útil saber cuanta agua tiene el depósito.
El ejemplo propuesto mide la cantidad de agua disponible y envía un mensaje al móvil mediante una aplicación escrita para el sistema Android.
El sistema es muy sencillo de construir como se puede ver en el diagrama electrónico. Usando como base un procesador Arduino, un Bluetooth HC-06 y un circuito integrado SN7404N.
El gran problema que presentan los sistema en base a electrodos es la descomposición de los mismos por efectos de la electrolisis. El trabajo propuesto corrige esto energizando el electrodo que lleva el potencial a los electrodos sensores durante unos mili-segundos por lo tanto los electrodos están todo el tiempo sin potencial.

El SN7404N oficia como interfaz para llevar la señal eléctrica al Arduino, una señal que se envía por el pin 19 de Arduino cada vez que se piden datos se encarga de enviar potencial en un conductor común que energiza el sensor.
El funcionamiento es muy simple, se envía un potencial de +12 voltios por un conductor común y según en que pin del sensor se recoja señal será la cantidad de agua disponible en la cisterna.
Para el sensor de medición se utilizó un trozo de caño de plástico rectangular, en el interior corre el común con la señal que sale del pin 19 y en unos orificios se se colocan los cables sensores de tal forma que queden próximos al cable común pero sin posibilidad de tocarse entre si y como está sumergido el contacto lo hace el agua.

En la imagen anterior se puede ver el proceso de armado del sensor que finalmente fue pintado para darle seguridad de que nada se desprenda en el tanque.
Lo ideal sería ensamblar el sensor usando varas de acero inoxidable de 3 mm que tienen un costo razonable, en la siguiente imagen se puede ver el sensor ensamblado con acero inoxidable.

El largo del tubo con los sensores dependerá de la profundidad del tanque, en el ejemplo que se encuentra funcionando el tanque tiene capacidad para 1000 litros de agua y el tubo con los sensores tiene 1,20 mts, pero esto depende de la geometría del tanque.
Otro detalle interesante que se puede ver en el código es que el Arduino permanece en reposo en modo bajo consumo incluso con su CPU dormida.
Esto si bien tiene como resultado un consumo muy bajo para alimentar el sistema con baterías, genera un problema puesto que en ese nivel de inactividad y con la CPU dormida la única forma de activar el sistema es con una interrupción, para esto se programa una interrupción por flanco de bajada en el pin 2 y se conecta este pin con el pin de recepción de tal forma que cuando llega un dato desde el Bluetooth este pin es puesto a nivel bajo el tiempo del bit de inicio de la trama serial. El sistema se despierta, toma la lectura del nivel, envía el dato y retorna al modo bajo consumo. También se podría usar solo la interrupción del puerto UART sin embargo de la forma propuesta se asegura la correcta recepción de los datos.
El siguiente es el código completo del ejemplo.
/*************************************************************
Monitor de nivel de cisterna de agua por medio de Bluetooth
La sonda detectora trabaja con 12 voltios por lo que se usa
un SN7404N como interfaz acoplada a un divisor resistivo para
adecuar los niveles lógicos que se presentan al Arduino.
Autor: Daniel Schmidt
**************************************************************/
#include <avr/sleep.h>
#include <SoftwareSerial.h>
// Pines de los sensores de nivel
#define S1 3
#define S2 4
#define S3 5
#define S4 6
#define S5 7
#define led 13
#define comun_5 19 // Pin que corresponde al ADC5
volatile unsigned char bandera =0;
unsigned char control =0;
SoftwareSerial mySerial(11, 12); // RX al TX, TX al RX
void Medir();
void ISR_Despertar(){
bandera =1;
}
void setup() {
pinMode(led, OUTPUT);
digitalWrite(led,LOW );
// --- Indicadores de Nivel ------
pinMode(comun_5, OUTPUT); // Pin que activa los 12 Voltios
digitalWrite(comun_5,LOW );
//----- Sensores de Entrada --------
pinMode(S1,INPUT_PULLUP);
pinMode(S2,INPUT_PULLUP);
pinMode(S3,INPUT_PULLUP);
pinMode(S4,INPUT_PULLUP);
pinMode(S5,INPUT_PULLUP);
Serial.begin(9600);
mySerial.begin(9600);
delay(2000); // Esperar electrónica OK
}
void loop() {
sleep_enable();
attachInterrupt( 0, ISR_Despertar, LOW);
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_cpu();
// Cuando se despierta ejecuta la siguiente linea !!!
delay(2000);
Medir();
}
/************************************************************************************************
* Esta es la función lee los sensores y envía la información al Bluetooth
* 1- Conecta energía para los sensores.
* 2- Lee el nivel de agua y envía una trama por Bluetooth.
* 3- Si el sensor no lee ningún nivel envía cartel -TANQUE VACIO-
************************************************************************************************/
void Medir(){
sleep_disable();
if(bandera == 1){
digitalWrite(comun_5,HIGH ); // Envía 12 voltios al al sensor
delay(2000); // Esperar para estabilizar el sensor con los 12V.
// -------------------------------- Evalúa los niveles en los electrodos --------------------------------------
if (digitalRead(S1) == LOW & digitalRead(S2) == LOW & digitalRead(S3) == LOW & digitalRead(S4) == LOW & digitalRead(S5) == LOW) {
mySerial.print("TANQUE LLENO!!");
control++;
}
if (digitalRead(S1) == HIGH & digitalRead(S2) == LOW & digitalRead(S3) == LOW & digitalRead(S4) == LOW & digitalRead(S5) == LOW) {
mySerial.print("3/4 TANQUE CON AGUA");
control++;
}
if (digitalRead(S1) == HIGH & digitalRead(S2) == HIGH & digitalRead(S3) == LOW & digitalRead(S4) == LOW & digitalRead(S5) == LOW) {
mySerial.print("1/2 TANQUE CON AGUA");
control++;
}
if (digitalRead(S1) == HIGH & digitalRead(S2) == HIGH & digitalRead(S3) == HIGH & digitalRead(S4) == LOW & digitalRead(S5) == LOW){
mySerial.print("1/4 TANQUE CON AGUA");
control++;
}
if (digitalRead(S1) == HIGH & digitalRead(S2) == HIGH & digitalRead(S3) == HIGH & digitalRead(S4) == HIGH & digitalRead(S5) == LOW) {
mySerial.print("TANQUE VACIO!!");
control++;
}
//-------------------------------------------------------------------------------------------------------------------------
if(control == 0){ // Control tiene que se > a 0 si no lo es el sensor tiene un error!!!
mySerial.print("-SONDA ERROR-");
control =0;
}
digitalWrite(comun_5,LOW ); // Asegura que los 12 voltios de los sensores estén apagados
bandera = 0; // Borra la bandera de interrupción
control =0;
}
} // Cuando termina en esta linea regresa al bucle principal y se prepara para pasar a sleep nuevamente
El ejemplo se encuentra en funcionamiento desde hace varios meses y sin presentar problemas.
La aplicación Cisterna.apk para instalar en Android se puede solicitar por correo electrónico a Esta dirección de correo electrónico está siendo protegida contra los robots de spam. Necesita tener JavaScript habilitado para poder verlo..
El presente ejemplo ha sido extraído de nuestro libro "Hágalo Usted Mismo" .
Haciendo unas reformas en casa me doy cuenta que la sirena inalámbrica instalada con el sistema de alarma no tiene el alcance con la señal de radio para llegar el sitio donde será instalada.
Viendo que tengo una sirena clásica cableada que nunca instalé por lo incomodo de colocar tantos metros de cable decidí convertirla en inalámbrica mediante dos enlaces de Radio controlados con Arduinos Nano.
En la siguiente imagen se puede ver la sirena con el receptor ya instalado en su interior.

Para activar la sirena remota uso la señal de activación que la propia alarma publica para activar una sirena interna (+12V), esta señal de 12 voltios se conecta a un opto-acoplador PC817 que dispara una interrupción en el Arduino Nano.
Una vez se detecta la interrupción se envía un comando de activación a la sirena remota.
El siguiente es el diagrama electrónico del emisor.
El siguiente es el programa para el emisor, se puede ver que en la rutina de interrupción se envía el comando para activar la sirena remota también se puede ver el uso de un delay() en la rutina de la interrupción lo que se puede entender como una interrupción anidada situación prohibida en Arduino sin embargo en este caso y debido a la forma en que el código ha sido diagramado esto es funcional.
Si se detecta que los 12 voltios de la sirena interna desaparecen se entiende que la alarma se ha desactivado y se envía el comando para apagar la sirena remota y todo el sistema pasa a bajo consumo deteniendo la CPU hasta que una nueva interrupción lo despierte.
/**********************************************************************
** Descripción : Control de una sirena por enlace de Radio
** Envia el 5 veces el comando activación a la sirena.
**
** Target : Arduino NANO
** ToolChain : Arduino IDE 1.8.1´9 bajo Linux Debian
** Daniel Schmidt
**********************************************************************/
#include <avr/sleep.h>
#define alarma 3 // IRQ_1 Detecta el disparo de la alarma
volatile unsigned char bandera_1 = 0;
unsigned char control_1 = 0;
unsigned char control_2 = 0;
void setup() {
Serial.begin(9600);
while (!Serial) {
; // Espera que se establezca la conexion
}
pinMode(3,INPUT_PULLUP); // Pin que detecta el disparo de la alarma
}
void loop() {
// Si el pin 3 esta a nivel alto enviar comando APAGAR SIRENA
if (digitalRead(alarma) == HIGH && control_2 <=4){// Envía el comando 5 veces
do{
Serial.write('B'); // Envía el comando stop (Sirena desactivada)
control_2++; // Incrementa el contador de muestras
control_1 = 0;
delay(1000); // Espera un segundo en cada envío de comando
}while(control_2 <=4);
}
sleep_enable(); // Activar el modo sleep
attachInterrupt( 1, ISR_Alarma_SI, LOW); // Define que ISR lo despierta
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Modo en que entra al sleep
sleep_cpu(); // Detiene la CPU
}
/*************************************************
Rutina para detectar el disparo de la alarma
*************************************************/
void ISR_Alarma_SI(){
sleep_disable();
delay(2000); // IMPORTANTE: Tiempo de espera para que toda
// la electrónica se active.
// Si el pin esta bajo luego de dos segundo enviar comando ACTIVAR SIRENA
if(digitalRead(alarma) == LOW && control_1 <=4){ // Envía el comando 5 veces
do{
Serial.write('A'); // Envía el comando de disparo (Sirena activada)
control_1++; // Incrementa el contador de muestras
control_2 = 0;
delay(1000);
}while(control_1 <=4);
}
}
El diagrama electrónico del receptor que controla la sirena remota es el siguiente.

El receptor es quien activa la sirena remota al recibir el comando 'A', en este caso la mantiene activa durante un minuto y pasa en modo bajo consumo al recibir el comando 'B' que indica que la alarma ha sido desactivada.
En este ejemplo se uso un enlace de radio lora simplemente por la distancia a cubrir pero evidentemente cualquier enlace funcionará ya que esto es solo el medio de transporte del protocolo de comunicación que hace que todo funcione.
El siguiente es el código del receptor.
/*************************************************************************************
** Descripción : Control de una sirena por enlace Lora
** La sirena se activa con el comando A y se desactiva
** con el comando B o transcurridos 1 minuto de estar activada.
** NOTA : Recordar que el contador interno de millis es de 32 bits es decir
** que va desde 0 a 4294967296 milisegundos.
** Target : Arduino NANO
** ToolChain : Arduino IDE 1.8.19 bajo Linux Debian
** Daniel Schmidt
**************************************************************************************/
#include <avr/sleep.h>
#define pin_ISR 2 // Pin IRQ para ATmega 328
#define sirena 9
unsigned long milisegundos = 0;
volatile char dato;
unsigned int segundos = 0;
unsigned char bandera = 0;
unsigned char bandera_timer = 0;
volatile char tmp;
void sleep(void);
void ISR_Sirena_SI(){ // Esta ISR se ejecuta primero que la UART (Vector 2)
sleep_disable();
dato = '0';
}
void setup() {
Serial.begin(9600);
while (!Serial) {
; // Espera que se establezca la conexión
}
milisegundos = millis();
pinMode(sirena, OUTPUT);
digitalWrite(sirena, LOW); // Sirena en silencio
sleep_enable(); // Modo sleep habilitado
attachInterrupt( 0, ISR_Sirena_SI, LOW); // ISR para salir del sleep
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Modo del sleep agresivo
sleep_cpu(); // CPU pasa al modo sleep
}
void loop() {
while (Serial.available()>0){ // Usar Inte_Vector 19
dato = Serial.read();
}
if((dato == 'A') || (dato == 'B')){
if(dato == 'A' && bandera == 0 ){// Control para activar
digitalWrite(sirena, HIGH); // Sirena ACTIVADA!!
bandera = 1;
dato = '0';
segundos = 0;
}
if(dato == 'B'){
digitalWrite(sirena, LOW); // Sirena DESACTIVADA con B!!
segundos = 0;
bandera = 0;
dato = '0';
bandera_timer = 0;
sleep(); // Nuevamente sleep solo si la alarma envía el comando B
}
if(dato == 'A' && bandera_timer == 0 ){
if ( millis() - milisegundos >= 1000 ) { // Ya pasaron 1000 milisegundos (Un segundo)?
segundos++;
milisegundos = millis(); // Actualiza el valor actual de millis
if(segundos >= 60){ // Si pasaron 60 segundos desactivar sirena
digitalWrite(sirena, LOW);
segundos = 0;
bandera_timer = 1;
}
}
}
}
// ############ Por si acaso el programa llega hasta este punto pasar a sleep #########
// ############ (El programa NO debería de llegar hasta este punto) #########
if(segundos == 120){ // Si pasaron 120 segundos pasar a sleep nuvamente
digitalWrite(sirena, LOW);
segundos = 0;
bandera = 0;
bandera_timer = 0;
sleep();
}
}
void sleep()
{
while (Serial.available()>0)
tmp = Serial.read(); // Limpiar buffer de RX
digitalWrite(sirena, LOW);
segundos = 0;
bandera = 0;
bandera_timer = 0;
delay(100);
sleep_enable(); // Activar el modo sleep
attachInterrupt( 0, ISR_Sirena_SI, LOW); // ISR que lo hace despertarí
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Modo en que entra al sleep
sleep_cpu(); // Detiene la CPU
}