Hoy vamos a hablar sobre un problema común que muchos enfrentan: cómo hacer trabajar bombas de agua de manera alternada. Como sabemos, tradicionalmente existen dos métodos para lograrlo: por temporizadores o mediante sensores de nivel. Aunque ambos funcionan adecuadamente, la realidad es que presentan algunos inconvenientes significativos. Por un lado, su instalación y el cableado es bastante laboriosa, llegando a tomar aproximadamente 6 horas . Por otro lado, estos métodos suelen incrementar considerablemente el costo de los componentes necesarios.
Sin embargo, ¿qué pasaría si pudiéramos crear una solución más eficiente? Es por eso que hemos desarrollado una tarjeta de control unificada que no solo simplifica todo el proceso, sino que además puede gestionar ambos tipos de control simultáneamente. De hecho, esta tarjeta va más allá, ya que también puede leer sensores de nivel de agua mientras mantiene las funciones manuales y automáticas tradicionales. Además de esto, permite seleccionar las bombas individualmente y cuenta con una intuitiva pantalla con pulsadores para configurar fácilmente tanto los tiempos como los modos de operación.
Si te interesa conocer paso a paso cómo hemos desarrollado este proyecto profesional, desde el diseño del esquemático electrónico hasta la implementación de la máscara personalizada, entonces no te despegues de este articulo.
DATOS TÉCNICOS

LISTA DE MATERIALES

Quantity | Comment | Designator | Footprint | Value | Manufacturer Part | Manufacturer | Supplier Part |
1 | BS-2-1 | B1 | BAT-TH_BS-2-1 | BS-2-1 | Q&J | C70376 | |
1 | 3kHz | BUZZER1 | BUZ-TH_BD9.0-P5.00-D0.6-FD | 3kHz | HNB09A03 | 华能 | C96102 |
9 | 100nF | C1,C3,C4,C5,C9,C10,C13,C15,C17 | C0603 | 100nF | CC0603KRX7R9BB104 | YAGEO(国巨) | C14663 |
2 | 10uF | C2,C11 | CAP-SMD_L3.2-W1.6-RD-C7171 | 10uF | TAJA106K016RNJ | Kyocera AVX | C7171 |
2 | 22pF | C6,C7 | C0603 | 22pF | CL10C220JB8NNNC | SAMSUNG(三星) | C1653 |
3 | 47uF | C8,C12,C14 | C1206 | 47uF | CL31A476MPHNNNE | SAMSUNG(三星) | dC96123 |
1 | 470uF | C16 | CAP-SMD_BD8.0-L8.3-W8.3-LS8.9-FD | 470uF | CK1C471M-CRF10 | ROQANG(容强) | C5162359 |
2 | WJ2EDGR-5.08-3P | CN1,CN2 | CONN-TH_3P-P5.08_WJ2EDGR-5.08-3P | WJ2EDGR-5.08-3P | KANGNEX(康奈克斯电气) | C70914 | |
1 | WJ2EDGR-5.08-2P | CN3 | CONN-TH_WJ2EDGR-5.08-2P | WJ2EDGR-5.08-2P | KANGNEX(康奈克斯电气) | C8383 | |
4 | 1N5819WS | D1,D7,D8,D9 | SOD-323_L1.8-W1.3-LS2.5-RD | 1N5819WS | Hottech(合科泰) | C191023 | |
5 | 1N4148WS | D2,D3,D4,D5,D6 | SOD-323_L1.8-W1.3-LS2.5-RD | 1N4148WS | CJ(江苏长电/长晶) | C2128 | |
1 | 2010T1A250V | F1 | FUSE-TH_L8.5-W4.0-P5.08-D0.6 | 2010T1A250V | WALTER(华德) | C354900 | |
1 | 2.54-1x6P直针 | H1 | HDR-TH_6P-P2.54-V-M-1 | 2.54-1x6P直针 | BOOMELE(博穆精密) | C37208 | |
1 | LCD-16X2 | LCD1 | LCD-16X2 | ||||
5 | XL-302UGD | LED1,LED2,LED3,LED7,LED8 | LED-TH_BD3.8-P2.54-FD_GREEN | XL-302UGD | XINGLIGHT(成兴光) | C2895476 | |
3 | XL-302SURD | LED4,LED5,LED6 | LED-TH_BD3.8-P2.54-RD | XL-302SURD | XINGLIGHT(成兴光) | C2895470 | |
7 | MMBT3904 | Q1,Q2,Q3,Q4,Q5,Q6,Q7 | SOT-23-3_L2.9-W1.3-P1.90-LS2.4-BR | MMBT3904 | CJ(江苏长电/长晶) | C20526 | |
1 | 200Ω | R1 | R0805 | 200Ω | 0805W8F2000T5E | UNI-ROYAL(厚声) | C17540 |
3 | 4.7kΩ | R2,R3,R4 | R0603 | 4.7kΩ | 0603WAF4701T5E | UNI-ROYAL(厚声) | C23162 |
1 | 510kΩ | R5 | R0603 | 510kΩ | 0603WAF5103T5E | UNI-ROYAL(厚声) | C23192 |
11 | 10kΩ | R6,R16,R21,R24,R25,R30,R31, R34,R37,R40,R43 |
R0603 | 10kΩ | 0603WAF1002T5E | UNI-ROYAL(厚声) | C25804 |
1 | 1.5MΩ | R7 | R0603 | 1.5MΩ | 0603WAF1504T5E | UNI-ROYAL(厚声) | C4172 |
1 | 1MΩ | R8 | R0603 | 1MΩ | 0603WAF1004T5E | UNI-ROYAL(厚声) | C22935 |
11 | 330Ω | R9,R10,R11,R12,R13,R14,R19,R26,R27,R35,R38 | R0805 | 330Ω | 0805W8F3300T5E | UNI-ROYAL(厚声) | C17630 |
6 | 47kΩ | R15,R17,R29,R32,R39,R41 | R1206 | 47kΩ | 1206W4F4702T5E | UNI-ROYAL(厚声) | C25833 |
7 | 1kΩ | R18,R20,R23,R28,R33,R36,R42 | R0603 | 1kΩ | 0603WAF1001T5E | UNI-ROYAL(厚声) | C21190 |
1 | 10kΩ | R22 | RES-ADJ-SMD_3P-L3.0-W3.8-P1.75-BR | 10kΩ | TC33X-2-103E | BOURNS | C719176 |
3 | G5NB-1A-E-DC5V | RLY1,RLY2,RLY3 | RELAY-TH_G5NB-1A-E-DCXX | G5NB-1A-E-DC5V | OMRON(欧姆龙) | C48746 | |
10 | TS665CJ | SW1,SW2,SW3,SW4,SW5, SW6,SW7,SW8,SW9,SW10 |
SW-TH_4P-L6.0-W6.0-P4.50-LS6.5 | TS665CJ | SHOU HAN(首韩) | C393938 | |
1 | ATMEGA328P-AU | U1 | TQFP-32_L7.0-W7.0-P0.80-LS9.0-BL | ATMEGA328P-AU | MICROCHIP(美国微芯) | C14877 | |
1 | DS1307ZN+T&R | U2 | SOP-8_L4.9-W3.9-P1.27-LS6.0-BL | DS1307ZN+T&R | ADI(亚德诺)/MAXIM(美信) | C26858 | |
1 | AT24C02 | U3 | SOP-8_L4.9-W3.9-P1.27-LS6.0-BL | AT24C02 | HXY MOSFET(华轩阳电子) | C7470930 | |
6 | EL357N(C)(TA)-G | U4,U6,U8,U9,U10,U11 | OPTO-SMD-4_L4.4-W4.1-P2.54-LS7.0-TL | EL357N(C)(TA)-G | EVERLIGHT(亿光) | C42379244 | |
1 | ULN2003ADR | U5 | SOIC-16_L9.9-W3.9-P1.27-LS6.0-BL | ULN2003ADR | TI(德州仪器) | C7512 | |
1 | PCF8574T/TR | U7 | SO-16_L10.3-W7.5-P1.27-LS10.3-BL | PCF8574T/TR | HGSEMI(华冠) | C2987288 | |
1 | TAS5-5-WEDT | U12 | PWRM-TH_TAS5-X-WEDT | TAS5-5-WEDT | TDPOWER(腾达电源) | C2687651 | |
1 | 10D561K | VR1 | RES-TH_L13.0-W6.0-P7.50-D0.9-S6.00 | 10D561K | HEL(鸿志) | C113236 | |
1 | 32.768kHz | X1 | FC-135R_L3.2-W1.5 | 32.768kHz | Q13FC13500004 | EPSON(爱普生) | C32346 |
1 | 16MHz | X2 | CRYSTAL-SMD_4P-L3.2-W2.5-BL | 16MHz | TAXM16M4RLBCCT2T | YJX(雅晶鑫) | C164049 |
CODIGO

#include <EEPROM.h> #include <Wire.h> #include "RTClib.h" RTC_DS1307 rtc; char daysOfTheWeek[7][12] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; //PINES DE ENTRADA DE SENSORES DE NIVEL DE AGUA const byte sensor_poso = 4; // Sensor poso const byte sensor_tanque = 3; // Sensor tanque const byte entrada_auxiliar = 2; //auxiliar //PINES DE SALIDA PARA CONTROLAR LOS LEDS const byte led_auto = 11; // Led Auto const byte led_manual = 12; // Led Manual //PINES DE SALIDA PARA CONTROLAR LOS MOTORES const byte motor1 = 14; // motor1 const byte motor2 = 15; // motor2 const byte motor3 = 16; //motor3 //PINES DE SALIDA PARA Buzzer const byte Buzzer = 13; //Buzzer // Configuración de buzzer const int tonoTecla = 1000; // Frecuencia en Hz para pulsación normal const int tonoGuardado = 2000; // Frecuencia en Hz para guardado const int duracionCorta = 50; // Duración en ms para tecla normal const int duracionLarga = 200; // Duración en ms para guardado ////////////////////////DIRECCION PARA ALMACENAR EN LA MENORIA EEPROM const byte direc_control = 0; // const byte direc_motores = 3; // Direccion EEPROM para almacenar los valores de total Motores const byte direc_mode = 6; bool act_sensonrs = false; bool control = false; byte contador_Motores_M = 2; // cantidad de motores activas (mínimo 2, máximo 3) byte mode = 0; byte motors = 0; byte read_sensor_tanque = 1; struct ConfigMotor { byte horas; byte minutos; byte segundos; }; ConfigMotor motores[3] = { { 0, 0, 0 }, // Motor 1 { 0, 0, 0 }, // Motor 2 { 0, 0, 0 } // Motor 3 }; const int dirEEPROM[3] = { 60, 70, 80 }; // Direcciones base EEPROM para cada motor // Variables para almacenar hora y fecha byte hora, minuto, segundo, dia, mes, anio = 0; unsigned long tiempoAnterior = 0; // ====== VARIABLES GLOBALES ADICIONALES ====== byte motorActivo = 255; // 255=ninguno, 0=motor1, 1=motor2, 2=motor3 DateTime horaInicioMotor; const byte pinesMotores[3] = { motor1, motor2, motor3 }; void cargarConfigMotores() { for (int m = 0; m < 3; m++) { EEPROM.get(dirEEPROM[m], motores[m]); // Si es la primera lectura (valores en 255) if (motores[m].horas == 255) motores[m] = { 0, 0, 0 }; } } void setup() { Serial.begin(9600); // Iniciar comunicación serial para depuración Serial.println("Sistema iniciado"); rtc.begin(); // rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // solo activa esta linea para ajustar la hora y la fecha, luego bloquealo EEPROM.get(direc_motores, contador_Motores_M); control = EEPROM.read(direc_control); EEPROM.get(direc_mode, mode); cargarConfigMotores(); pinMode(sensor_poso, INPUT); pinMode(sensor_tanque, INPUT); pinMode(entrada_auxiliar, INPUT); attachInterrupt(digitalPinToInterrupt(sensor_tanque), tanque, FALLING); pinMode(led_auto, OUTPUT); pinMode(led_manual, OUTPUT); pinMode(motor1, OUTPUT); pinMode(motor2, OUTPUT); pinMode(motor3, OUTPUT); pinMode(Buzzer, OUTPUT); // Mostrar configuración inicial mostrarEstadoActual(); } void loop() { // Verificar sensores y controlar motores según el modo if (digitalRead(sensor_poso) == LOW) act_sensonrs = true; else { digitalWrite(motor1, LOW); digitalWrite(motor2, LOW); digitalWrite(motor3, LOW); act_sensonrs = false; } switch (mode) { case 0: digitalWrite(led_auto, LOW); digitalWrite(led_manual, LOW); digitalWrite(motor1, LOW); digitalWrite(motor2, LOW); digitalWrite(motor3, LOW); break; case 1: digitalWrite(led_auto, HIGH); digitalWrite(led_manual, LOW); break; case 2: digitalWrite(led_auto, LOW); digitalWrite(led_manual, HIGH); break; } if (mode == 2) { switch (motors) { case 0: digitalWrite(motor1, LOW); digitalWrite(motor2, LOW); digitalWrite(motor3, LOW); break; case 1: digitalWrite(motor1, HIGH); digitalWrite(motor2, LOW); digitalWrite(motor3, LOW); break; case 2: digitalWrite(motor1, LOW); digitalWrite(motor2, HIGH); digitalWrite(motor3, LOW); break; case 3: digitalWrite(motor1, LOW); digitalWrite(motor2, LOW); digitalWrite(motor3, HIGH); break; } } else { motors = 0; } if (mode == 1) { if (control == false && act_sensonrs == true) { if (digitalRead(sensor_tanque) == LOW) { if (read_sensor_tanque == 1) { digitalWrite(motor1, HIGH); digitalWrite(motor2, LOW); } if (read_sensor_tanque == 2) { digitalWrite(motor1, LOW); digitalWrite(motor2, HIGH); } } else { digitalWrite(motor1, LOW); digitalWrite(motor2, LOW); } } else if (control == true && act_sensonrs == true) { controlarMotoresPorTurnos(); } } // Verificar comunicación serial para comandos if (Serial.available() > 0) { procesarComandoSerial(); } // Mostrar estado cada cierto tiempo static unsigned long ultimoTiempoMostrado = 0; if (millis() - ultimoTiempoMostrado > 5000) { // Cada 5 segundos mostrarEstadoActual(); ultimoTiempoMostrado = millis(); } } void procesarComandoSerial() { String comando = Serial.readStringUntil('\n'); comando.trim(); if (comando == "status") { mostrarEstadoActual(); } else if (comando == "mode0") { mode = 0; EEPROM.put(direc_mode, mode); Serial.println("Modo 0: Apagado"); sonidoGuardado(); } else if (comando == "mode1") { mode = 1; EEPROM.put(direc_mode, mode); Serial.println("Modo 1: Auto"); sonidoGuardado(); } else if (comando == "mode2") { mode = 2; EEPROM.put(direc_mode, mode); Serial.println("Modo 2: Manual"); sonidoGuardado(); } else if (comando == "motor1") { motors = 1; Serial.println("Motor 1 activado"); sonidoTecla(); } else if (comando == "motor2") { motors = 2; Serial.println("Motor 2 activado"); sonidoTecla(); } else if (comando == "motor3") { motors = 3; Serial.println("Motor 3 activado"); sonidoTecla(); } else if (comando == "motoroff") { motors = 0; Serial.println("Motores apagados"); sonidoTecla(); } else if (comando == "control_sensor") { control = false; EEPROM.update(direc_control, control); Serial.println("Control por sensores activado"); sonidoGuardado(); } else if (comando == "control_tiempo") { control = true; EEPROM.update(direc_control, control); Serial.println("Control por tiempos activado"); sonidoGuardado(); } else if (comando.startsWith("motores ")) { int cantidad = comando.substring(8).toInt(); if (cantidad >= 2 && cantidad <= 3) { contador_Motores_M = cantidad; EEPROM.put(direc_motores, contador_Motores_M); Serial.print("Cantidad de motores: "); Serial.println(contador_Motores_M); sonidoGuardado(); } else { Serial.println("Error: cantidad debe ser 2 o 3"); } } else if (comando.startsWith("config_motor")) { // Formato: config_motor,motor#,horas,minutos,segundos // Ejemplo: config_motor,1,0,30,0 int posiciones[4]; int parametro = 0; int inicio = 12; // Posición después de "config_motor," for (int i = inicio; i < comando.length(); i++) { if (comando.charAt(i) == ',') { posiciones[parametro] = comando.substring(inicio, i).toInt(); inicio = i + 1; parametro++; } } // Último valor if (parametro < 4) { posiciones[parametro] = comando.substring(inicio).toInt(); parametro++; } if (parametro == 4) { int motor = posiciones[0] - 1; // Motor 1 sería índice 0 if (motor >= 0 && motor < 3) { motores[motor].horas = posiciones[1]; motores[motor].minutos = posiciones[2]; motores[motor].segundos = posiciones[3]; guardarConfigMotor(motor); Serial.print("Motor "); Serial.print(motor + 1); Serial.print(" configurado: "); Serial.print(motores[motor].horas); Serial.print(":"); Serial.print(motores[motor].minutos); Serial.print(":"); Serial.println(motores[motor].segundos); sonidoGuardado(); } else { Serial.println("Error: Motor debe ser 1, 2 o 3"); } } else { Serial.println("Error: formato debe ser config_motor,motor#,horas,minutos,segundos"); } } else if (comando.startsWith("set_time")) { // Formato: set_time,hora,minuto,segundo,dia,mes,año int valores[6]; int parametro = 0; int inicio = 9; // Posición después de "set_time," for (int i = inicio; i < comando.length(); i++) { if (comando.charAt(i) == ',') { valores[parametro] = comando.substring(inicio, i).toInt(); inicio = i + 1; parametro++; } } // Último valor if (parametro < 6) { valores[parametro] = comando.substring(inicio).toInt(); parametro++; } if (parametro == 6) { rtc.adjust(DateTime(2000 + valores[5], valores[4], valores[3], valores[0], valores[1], valores[2])); Serial.println("Fecha y hora actualizadas"); sonidoGuardado(); } else { Serial.println("Error: formato debe ser set_time,hora,minuto,segundo,dia,mes,año"); } } else if (comando == "help") { mostrarAyuda(); } else { Serial.println("Comando desconocido. Escribe 'help' para ver los comandos disponibles."); } } void mostrarEstadoActual() { DateTime now = rtc.now(); Serial.println("==== ESTADO ACTUAL ===="); Serial.print("Fecha: "); Serial.print(now.day()); Serial.print("/"); Serial.print(now.month()); Serial.print("/"); Serial.println(now.year()); Serial.print("Hora: "); Serial.print(now.hour()); Serial.print(":"); if (now.minute() < 10) Serial.print("0"); Serial.print(now.minute()); Serial.print(":"); if (now.second() < 10) Serial.print("0"); Serial.println(now.second()); Serial.print("Modo: "); switch (mode) { case 0: Serial.println("Apagado"); break; case 1: Serial.println("Automático"); break; case 2: Serial.println("Manual"); break; } Serial.print("Control: "); Serial.println(control ? "Por tiempos" : "Por sensores"); Serial.print("Nivel agua pozo: "); Serial.println(digitalRead(sensor_poso) == LOW ? "Disponible" : "No disponible"); Serial.print("Sensor tanque: "); Serial.println(digitalRead(sensor_tanque) == LOW ? "Activo" : "Inactivo"); Serial.print("Total motores: "); Serial.println(contador_Motores_M); Serial.println("Tiempos motores:"); for (int i = 0; i < contador_Motores_M; i++) { Serial.print("Motor "); Serial.print(i + 1); Serial.print(": "); Serial.print(motores[i].horas); Serial.print(":"); if (motores[i].minutos < 10) Serial.print("0"); Serial.print(motores[i].minutos); Serial.print(":"); if (motores[i].segundos < 10) Serial.print("0"); Serial.println(motores[i].segundos); } Serial.println("======================"); } void mostrarAyuda() { Serial.println("==== COMANDOS DISPONIBLES ===="); Serial.println("status - Muestra el estado actual"); Serial.println("mode0 - Modo apagado"); Serial.println("mode1 - Modo automático"); Serial.println("mode2 - Modo manual"); Serial.println("motor1 - Activa motor 1 (solo en modo manual)"); Serial.println("motor2 - Activa motor 2 (solo en modo manual)"); Serial.println("motor3 - Activa motor 3 (solo en modo manual)"); Serial.println("motoroff - Apaga todos los motores"); Serial.println("control_sensor - Activa control por sensores"); Serial.println("control_tiempo - Activa control por tiempos"); Serial.println("motores N - Establece número de motores (2-3)"); Serial.println("config_motor,M,h,m,s - Configura tiempo del motor M"); Serial.println("set_time,h,m,s,d,M,a - Configura hora y fecha"); Serial.println("help - Muestra esta ayuda"); Serial.println("============================="); } void tanque() { read_sensor_tanque++; if (read_sensor_tanque > 2) read_sensor_tanque = 1; } void controlarMotoresPorTurnos() { // Si el control está activo pero no hay motor activo, iniciar ciclo if (control && motorActivo == 255) { motorActivo = 0; digitalWrite(pinesMotores[motorActivo], HIGH); horaInicioMotor = rtc.now(); return; } // Si el control está inactivo pero hay motor activo, detener todo if (!control && motorActivo != 255) { for (byte i = 0; i < 3; i++) { digitalWrite(pinesMotores[i], LOW); } motorActivo = 255; return; } // Si hay motor activo y control activo, verificar tiempo if (control && motorActivo != 255) { DateTime ahora = rtc.now(); unsigned long segundosTranscurridos = ahora.unixtime() - horaInicioMotor.unixtime(); unsigned long segundosProgramados = motores[motorActivo].horas * 3600UL + motores[motorActivo].minutos * 60UL + motores[motorActivo].segundos; if (segundosTranscurridos >= segundosProgramados) { // Apagar motor actual digitalWrite(pinesMotores[motorActivo], LOW); // Pasar al siguiente motor (solo los configurados) motorActivo = (motorActivo + 1) % contador_Motores_M; // Encender nuevo motor digitalWrite(pinesMotores[motorActivo], HIGH); horaInicioMotor = ahora; } } } void guardarConfigMotor(byte numMotor) { EEPROM.put(dirEEPROM[numMotor], motores[numMotor]); } void sonidoTecla() { tone(Buzzer, tonoTecla, duracionCorta); delay(duracionCorta); // Pequeña pausa para evitar solapamientos } void sonidoGuardado() { tone(Buzzer, tonoGuardado, duracionLarga); delay(duracionLarga); // Pausa más larga para el sonido de confirmación }