MEC. FLUIDOS - Análisis Diferencial del Movimiento de un Fluido -GRUPO5 sergi...
Arduino: Reporte de diseño controlador acuario con Arduino
1. Diseño Controlador Acuario con Arduino
Un controlador de acuario, donde se irán agregando partes del código hasta completarlo,
constara menú para modificar parámetros luz, Hora, fecha, etc. El LCD para visualizar
información, sensor de temperatura y relés. La idea es tener una base y que cualquiera
pueda realizar su controlador y porque no ampliarlo.
Materiales:
Arduino Puede ser Nano, Uno o Mega
Reloj DS3231
Lcd 20x4 con I2C
Sensor temperatura DS18B20
Encoder rotatorio con pulsador
Resistencia 4,7k - 1/4w =1
Resistencia 1k - 1/4w =4
Led Blanco, Rojo, Azul
Fuente de poder 9v
Reles Arduino 5v 2 canales
Transistor 2N2222
Protoboard
Cables conexión
Diagrama esquemático:
2.
3. Desarrollo
Procedimiento con el código:
Hola voy a hacer mi aporte:
En esta parte del código:
void loop() {
unsigned long currentMillis = millis();
button.tick();
LeeEncoder(0, 7, 1, 1); // Lee el enconder de 0 a 7, de a 1 paso y con
tope final.
if (currentMillis % 6000 < 5) { // Si el resto de dividir
currentMillis por 6000 da menos que 5...
ImprimeFecha();
ImprimeHora();
ImprimeTemp();
}
}
Se va a cumplir cuando sea por ejemplo 6000, 6001, 6002, 6003, 6004 y en el "mejor de
los casos" pero en el transcurso se cumple y se va a actualizar como 20 veces aquí un
ejemplo de lo que digo:
Ahora así como muestra por serial va actualizar en la pantalla para lo cual presentó algo
que puede ser mejor tendrías que añadir las siguientes líneas y borra en el que tú tienes,
espero se entienda.
4. #define tiempoPresentarDatos 6000UL // Tiempo al que se quiera realizar
las acciones ejemplo 6s (1000ms = 1s)
unsigned long tiempoActualDatos = 0;
void loop() {
if (millis() - tiempoActualDatos >= tiempoPresentarDatos) { // Si el
tiempo actual menos el tiempo anterior es mayor o igual a 6 segundos
tiempoActualDatos = millis(); // Actualiza el tiempo Actual
//Resto del código que quieras que se cumpla cada 6 segundos
}
}
Ahora también por ejemplo en esta parte utilizas una función y en el cual en una condición
la utilizas para para comprobar si tiene tope o no.
void LeeEncoder(int ROTARYMIN, int ROTARYMAX, int ROTARYSTEPS, int
ROTARYTOPE)
{
encoder.tick(); // Lee el encoder y actualiza el puntero. La librería
lleva la cuenta desde 0.
newPos = encoder.getPosition() * ROTARYSTEPS; // Asigna a newPos el
puntero del encoder multiplicado por los pasos.
if (ROTARYTOPE == 1) { // Si el rotor tiene tope entonces no se pasa
de valor
if (newPos < ROTARYMIN) { // Solo si el puntero es menor a Valor
5. Mínimo ->
encoder.setPosition(ROTARYMIN / ROTARYSTEPS); // Se fija el
puntero al valor mínimo por los pasos
newPos = ROTARYMIN;
}
if (newPos > ROTARYMAX) { // Solo si el puntero es mayor a Valor
Máximo ->
encoder.setPosition(ROTARYMAX / ROTARYSTEPS); // Se fija el
puntero al valor mínimo por los pasos
newPos = ROTARYMAX;
}
}
if (ROTARYTOPE == 0) { // Si el rotor SI tiene tope entonces se pasa
del valor máximo al mínimo y viceversa.
if (newPos < ROTARYMIN) { // Solo si el puntero es menor a Valor
Mínimo ->
encoder.setPosition(ROTARYMAX / ROTARYSTEPS);
newPos = ROTARYMAX; // Entonces el puntero saltará del mínimo al
máximo
}
if (newPos > ROTARYMAX) { // Solo si el puntero es mayor al Valor
Máximo ->
encoder.setPosition(ROTARYMIN / ROTARYSTEPS);
newPos = ROTARYMIN; // Entonces el puntero saltará del máximo
al mínimo.
}
}
}
Lo que veo un poco innecesario ya que ese no va a cambiar en el transcurso del
funcionamiento si no solo cuando se configura por lo que creo que se podría utilizar de esta
forma como ejemplo
#define encoderTope 1
void loop() {
#if encoderTope == 1
if (newPos < ROTARYMIN) { // Solo si el puntero es menor a Valor Mínimo
->
encoder.setPosition(ROTARYMIN / ROTARYSTEPS); // Se fija el puntero al
valor mínimo por los pasos
newPos = ROTARYMIN;
}
if (newPos > ROTARYMAX) // Solo si el puntero es mayor a Valor Máximo->
6. encoder.setPosition(ROTARYMAX / ROTARYSTEPS); // Se fija el puntero al
valor mínimo por los pasos
newPos = ROTARYMAX;
}
#else
if (newPos < ROTARYMIN) { //Solo si el puntero es menor a Valor Mínimo->
encoder.setPosition(ROTARYMAX / ROTARYSTEPS);
newPos = ROTARYMAX; // Entonces el puntero saltará del mínimo al máximo
}
if (newPos > ROTARYMAX) {//Solo si el puntero es mayor al Valor Máximo->
encoder.setPosition(ROTARYMIN / ROTARYSTEPS);
newPos = ROTARYMIN; // Entonces el puntero saltará del máximo
al mínimo.
}
#endif
}
Ok, funcionando
Aquí en este código muestra con la configuración de RTC de FECHA-HORAS
usando con la librería
7. void ImprimeFecha() {
DateTime now = rtc.now(); // Consulta al RTC
lcd.setCursor(0,0); // Fija el cursor en la primera línea
lcd.print(DiaDeSemana[now.dayOfTheWeek()]); // Imprime el día de la
semana
lcd.setCursor(10,0);
printDigits(now.day(), DEC); // Imprime el día
lcd.print('/'); // Separador
printDigits(now.month(), DEC); // Imprime el mes
lcd.print('/'); // Separador
lcd.print(now.year(), DEC); // Imprime el año
}
void ImprimeHora() {
DateTime now = rtc.now(); // Consulta el RTC
lcd.setCursor(13,1); // Fija cursor en 13,1
lcd.write(1); // Imprime el gráfico
lcd.print(':');
printDigits(now.hour()); // Imprime hora
lcd.print(':');
printDigits(now.minute()); // Imprime minuto
MinActual = now.hour()*60+now.minute(); // Se actualiza la variable
MinActual que será usada para encender y apagar las luces del acuario. Su
valor va de 0 a 1439.
}
Otra forma que evita el problema de los días/meses/horas/minutos de 1 dígitos es esta.
void ImprimeFecha() {
char buffer[20];
DateTime now = rtc.now(); // Consulta al RTC
lcd.setCursor(0,0); // Fija el cursor en la primera línea
lcd.print(DiaDeSemana[now.dayOfTheWeek()]); // Imprime el día de la
semana
lcd.setCursor(10,0);
sprintf(buffer,"%02d/%02d/%04d", now.day(), now.month(), now.year());
lcd.print(buffer);
}
void ImprimeHora() {
char buffer[20];
DateTime now = rtc.now(); // Consulta el RTC
lcd.setCursor(13,1); // Fija cursor en 13,1
lcd.write(1); // Imprime el gráfico
sprintf(buffer,"%02d:%02d:%02d", now.hout(), now.minute(),
now.second());
lcd.print(buffer);
8. MinActual = now.hour()*60+now.minute(); // Se actualiza la variable
MinActual que será usada para encender y apagar las luces del acuario. Su
valor va de 0 a 1439.
}
Pruebas con sensor temperatura DS18B20
La actualización de la información de corte de un cable demora aproximadamente 20seg
al desconectar cable rojo muestra información diferente no el de falla
La actualización de la información de temperatura demora aprox. 50 seg ver forma de bajar
tiempo.
Prueba de temperatura de RTC3231
9. Se toma temperatura ambiente y luego se acerca un cautín.
La actualización de temperatura con demora
El calefactor tiene que estar siempre encendido y trabajar así, si lo seteo a 25 grados se
mantiene si sube se apaga si baja se enciende puede haber una variación de un grado, no
entiendo porque en el día una temperatura, horas que esta encendido, noche otra
temperatura, la idea es tener una temperatura pareja y que no por un cambio de
temperatura los peces se enfermen
Esta es la configuración para prueba donde el calefactor en este horario estará a 25° y el
relé no se activa
10. Esta es la pantalla principal con la temperatura actual
Estamos realizado la prueba y al conectar la señal del relé a negativo se activa, en ningún
momento se activó ni con 9° ni con 60° que llego en un momento.
11. es lo mismo que tengo, la única forma que se activa el relé es si no dejo activa esta línea
digitalWrite en el void setup pero no se desactiva el calefactor al pasar de los 26 o 35 o
65°
void setup() {
pinMode(SFetAzul, OUTPUT);
pinMode(SFetRojo, OUTPUT);
pinMode(SFetBlanco, OUTPUT);
pinMode(Relay1, OUTPUT);
// digitalWrite(Relay1, HIGH); // Subimos el pin a HIGH porque el relay
se dispara con LOW
pinMode(Relay2, OUTPUT);
// digitalWrite(Relay2, HIGH); // Subimos el pin a HIGH porque el relay
se dispara con LOW
Pruebas memoria:
12. Se carga programa para blanquear la memoria luego se carga archivo CADUSA y se
chequean valores guardados en calefactor por defecto correspondiendo a estos día 24°
hora 09:00 a 21:40 noche 20°
en el caso del CO2 datos aparecen como en la imagen no siendo legibles y no activando
relé
Se carga programa para blanquear la memoria luego se carga archivo CADUSA y se
chequean valores
En calefactor valores por defecto correspondiendo a estos día 24° hora 09:00 a 21:40 noche
20°.
En el caso del CO2 valores por defecto hora 09:00 a 21:40. En ambos casos se visualiza
correctamente al resetear y cortar la energía al modificar valor CO2 o Calefactor estos se
guardan sin perderse Todo OK.
CADUSA2 en prueba
Se limpia la eeprom se carga CADUSA2
pantalla inicio OK
pantalla información hora. Fecha, temperaturas OK
menús: calefactor valores por defecto aparecen, funciona relé al activar ON OK
fecha y hora Ok
control CO2 se agrega read_but(); en líneas 482, 513, 531, 548, 566, al faltar solo
mostraba ON OFF y no entrega información en pantalla de valores por defecto, al
seleccionar ON u OFF aparece leyenda Hecho y sale del menú.
Al borrar un símbolo! línea 476
if (!Co2Var.Status == 0)
13. aparece información por defecto y se activa rele
en cada prueba se limpio eeprom
se adjunta archivo con agregado de lineas faltantes y en estado original linea 476 para
análisis
Ya que estamos mostrado este era mi acuario de agua dulce de 170 cm de largo luego
cambie al actual marino
Actual en proceso
14. Se limpia la eeprom se carga CADUSA2
pantalla inicio OK
pantalla información hora. Fecha, temperaturas OK
menús:
Reloj y fecha OK
calefactor valores por defecto aparecen, relé se activa después de 1 minutos OK
control CO2 entrega información en pantalla de valores por defecto, al seleccionar ON
activa relé después de 1 minuto.
se mantiene la configuración puesta por usuario al resetear y desconectar energía
Prueba de sobre calentamiento a 72° mensaje Alta temperatura en pantalla y se
reestablece bajo los 50°
en cada prueba se limpió eeprom
Lo otro hay posibilidad de agregar este control de temperatura en la parte del void
overheating(), que se accione con el termómetro del rtc como una acción antes que la
temperatura llegue al sobre calentamiento y mantener una temperatura y si falla pasaría al
modo sobre calentamiento.
El código a modificar para usar el sensor de temperatura del rtc.
#include <Wire.h>
#include <OneWire.h> //libreria temperatura
#include <DallasTemperature.h>
OneWire ourWire(12); // Se establece el pin D12 como bus
OneWire
DallasTemperature sensors(&ourWire); //Se declara una variable u objeto
para nuestro sensor
int PinVent = 5; //puerto digital D11 dodne
conectar el ventildor
int Temperatura; //variable calculo temperatura
int Restriccion_de_Temp; //Variable para mapear la
temperatura
int pwm; //Variable para generar pwm
void setup() {
sensors.begin();
pinMode(5,OUTPUT);
// Serial.begin(9600);
}
void loop() {
15. /////// Regulador Veloc Ventilador ////////////
sensors.requestTemperatures();
Temperatura = (sensors.getTempCByIndex(0));
Restriccion_de_Temp = constrain(Temperatura, 35, 45);
pwm = map(Restriccion_de_Temp, 35, 45, 0, 255);
analogWrite(PinVent,pwm);
// Serial.println(sensors.getTempCByIndex(0));
// Serial.println (pwm);
delay(1000);
}
Una idea para la iluminación del display has probado con una resistencia de 10K donde
está el jumper del lcd atenúa arto y es visible la información y no es molesto a la vista
y solo ver que se apague la pantalla cuando termine el periodo de luz y seencienda cuando
comience el periodo.
Se ve mejor en vivo que en la foto
Prueba de ventilador activándose a los 50° y desactivando a los 45 ...........OK
prueba de atenuación iluminación y activación iluminación LCD .................OK
en lo personal use resistencia de 10k
Con la resistencia de 10k ilumina justo para que se vea y como dices a gusto del lector y
se probó con un valor bajo y uno alto de resistencia.
Se ve muy genial, esa era la idea, al mover el encoder recobra la iluminación genial.
Lo único diferente indicador de dirección de menú ( > ) al cargar por primera vez, reiniciar
o apagar y prender no aparece asta mover el encoder en la dirección de la flecha.
OK, el cambio ( > ) funciona al cargar, reset y apagado y encendido
16. la (>) parte al cargar y al entrar a un menú y salir este no vuelve automáticamente
línea 1182 se borra 35; se deja rtc.getTemperatura
Prueba iluminación
se pone horario modo sol finalizando 19:23 se apaga iluminación y modo luna enciende a
las 19:25 según reloj
RTC, demora de 1:59 seg según cronometro en encender, no es inmediato el paso a luna
en modo mantenimiento se encienden solo luz azul y rojas solamente o las blancas igual,
solo se me encienden las azul y rojas
Consulta habría forma de poner en el LCD información del estado en que se está como:
Amanecer, Sol, Atardecer, Noche
Según horario programado
a (>) parte al cargar el programa pero al entrar a un menú y salir esta (>) no vuelve
automáticamente
- Transición a modo Luna
Si el horario sol finaliza a las 17:00 y luego dentro a menu luna y pongo que inicie a las
17:00 y finalice 18:00 salgo e ingreso nuevamente este cambia iniciando a las 17:01
provocando un lapso de casi 2 minutos con las luces apagadas y luego enciende luz azul,
la transición debiese ser continua sin espacio apagado
-----Creo que entendí el espacio sin luz es una transición a la salida de la luna
El termino lo tome de la misma pagina " En el lenguaje común, al orto se le denomina
amanecer, alba o aurora. En ocasiones se diferencia la aurora, que sería el primer
resplandor del cielo, del amanecer, que correspondería a la salida del sol"
mañana veré bien lo de las fases del día justo es este Alba, Luna y Ocaso me estan
dando problemas sino dejare los básicos Noche, Amanecer, Sol, Atardecer y Luna que
son los más significativos
Sigue igual, para que vuelva el signo se tiene que mover (girar) el encoder avanzar y
luego retroceder y aparece
Test faces del día
Noche OK
Amanecer OK
Sol OK
Atardecer OK,
se apagan las luces espera 2min y pasa a Sale luna
Ocaso No aparece
Sale luna OK
Luna OK
si es que modifique mi anterior circuito los 10v eran porque las fuentes MEANWELL
necesitan un voltaje de referencia y las recomendaban, no los uso se quedo hay .
los 8v alimento arduino.
los relés que tengo son de 5v hay los uso.
son Tip141 los tengo en uso en mi pantalla actual y son baratos en comparación al fet,
17. cuando vi precios no se en estos momentos.
voy a usar tu recomendación del transistor para el ventilador.
la fuente uso 5 de estas soportan hasta 18 led de 3w en serie, claro que estuve
revisando y parece que la descontinuaron.
Esta conversación sobre led es algo para largo algunos ven los watt otros los lumens
otros el par led y nunca están de acuerdo yo opte luego de leer mucho por watt y lumens
donde uso led con temp de color
blanco calido 6000-6500k
blanco frio 10000-15000k
azul 460-470nm
royal blue 445nm
uv 420-425nm
para un marino
esta es mi pantalla cuando la estaba armando hace un año placa de aluminio 5mm de
espesor algo gruesa y lentes de 90°
18. Dudas:
1- linea 235
es el tiempo de amanecer a sol, es de 30min la duracion definido en (+30)
2- en la linea 238
0,30,0,255 quiere decir que va a subir de 0min a 30min y la luz (pmw) iría
aumentando de 0 a 255 según lo programado claro
if (MinActual < (ModoSol.HoraInicio + 30)) { // Periodo Amanecer
lcd.setCursor(0,1); lcd.print(" ");
lcd.setCursor(0,1); lcd.print(Periodo_name[1]);
brillo = map((MinActual - ModoSol.HoraInicio),0,30,0,255);
19. No olvidar para la atenuación de la pantalla después de un minuto una resistencia entre
1k a 10k
23. int newPos = 0;
int MinActual = 0;
byte brillo = 0;
byte spwm[5];
float Temp; // Sensor DS18B20 para el agua del acuario
float Tlamp; // Sensor interno del RTC para la temperatura del
plafón.
byte OverTemp = 65; // Límite superior de temeperatura para el
artefato
byte UnderTemp = 50; // Temperatura a la cual el artefacto retoma el
funcionamiento.
byte Mod_Menu = 0;
byte arrow_symbol[8] = {B00000, B00100, B01110, B11111, B00100,
B00100, B00100, B00100};
byte reloj_symbol[8] = {B00000, B01110, B10101, B10101, B10011,
B10001, B01110, B00000}; // Símbolo reloj
byte thermometer_symbol[8] = {B00100, B01010, B01010, B01110, B01110,
B11111, B11111, B01110}; // Símbolo termómetro
byte lamp_symbol[8] = {B00100, B10101, B01110, B11011, B01110,
B10101, B00100, B00000}; // Símbolo lámpara
struct MyTermo {
byte Status; // Estado 0 apagado y 1 encendido
byte TempHorario; // temepratura durante el rango de horas fijado
byte TempResto; // temperatura para el horario fuera del rango
int HoraInicio; // Hora inicio del rango
int HoraFin; // Hora fin del rango
};
MyTermo TermoVar;
struct MyCo2 {
byte Status; // Co2 On y Off,
int HoraInicio; // Hora inicio
int HoraFin; // Hora Fin
24. };
MyCo2 Co2Var;
struct MyModeSun { // Modo Sol
byte Azul; // Nivel salida rojo
byte Rojo; // Nivel salida azul
byte Blanco; // Nivel salida blanco
int HoraInicio; // Hora inicio del rango
int HoraFin; // Hora fin del rango
};
MyModeSun ModoSol;
struct MyModeMoon { // Modo Luna
byte Status; // Estado apagado o encendido del modo luna
byte Azul; // Nivel salida rojo
byte Rojo; // Nivel salida azul
byte Blanco; // Nivel salida blanco
int HoraInicio; // Hora inicio del rango
int HoraFin; // Hora fin del rango
};
MyModeMoon ModoLuna;
LiquidCrystal_I2C lcd (0x27, 20,4); // DIR, E, RW, RS, D4, D5, D6, D7
RTC_DS3231 rtc; // Se declara el reloj en tiempo real
OneButton button(A2, true); // Botón del encoder puesto a la librería
OneButton
RotaryEncoder encoder(A1, A0);
OneWire ourWire(12); //Se establece el pin 12 como bus
OneWire
DallasTemperature sensors(&ourWire); //Se declara una variable u objeto
para nuestro sensor
DeviceAddress outsideThermometer;
25. void setup() {
lcd.init();
pinMode(SFetAzul, OUTPUT);
pinMode(SFetRojo, OUTPUT);
pinMode(SFetBlanco, OUTPUT);
pinMode(Relay1, OUTPUT);
digitalWrite(Relay1, HIGH); // Subimos el pin a HIGH porque el relay se
dispara con LOW
pinMode(Relay2, OUTPUT);
digitalWrite(Relay2, HIGH); // Subimos el pin a HIGH porque el relay se
dispara con LOW
pinMode(enc_a, INPUT_PULLUP);
pinMode(enc_b, INPUT_PULLUP);
pinMode(enc_c, INPUT_PULLUP);
pinMode(13, OUTPUT); // Pin 13 para controlar el cooler
digitalWrite(13, LOW); // Iniciamos su estado en bajo.
button.attachClick(clickbutton);
button.setDebounceTicks(80);
lcd.backlight();
lcd.createChar(0, arrow_symbol);
lcd.createChar(1, reloj_symbol);
lcd.createChar(2, thermometer_symbol);
lcd.createChar(3, lamp_symbol);
if (EEPROM.read(30)>1) { // Si es la primera vez que se inicia el
programa graba los parámetros por defaul
ModoSol.Azul=127; ModoSol.Rojo=127; ModoSol.Blanco=127;
ModoSol.HoraInicio=540; ModoSol.HoraFin=1080;
// Nivel de azul / Nivel de rojo / Nivel de blanco / HoraInicio a
9:00 hs / HoraFin 18:00 hs
EEPROM.put(0,ModoSol); // guarda modo sol
26. TermoVar.Status=0; TermoVar.TempHorario=24; TermoVar.TempResto=20;
TermoVar.HoraInicio=540; TermoVar.HoraFin=1300;
// 0 Apagado, 1 Ecendido - 24° Por defecto / 20° por defecto /
540 minutos = 09:00 hs / 1300 minutos = 21:40 hs
EEPROM.put(10, TermoVar); // guarda termostato
ModoLuna.Status=0, ModoLuna.Azul=0; ModoLuna.Rojo=0;
ModoLuna.Blanco=0; ModoLuna.HoraInicio=1140; ModoLuna.HoraFin=1380;
//Apagado o Ecendido/ Nivel de azul/ Nivel de rojo/ Nivel de
blanco/ HoraInicio a 19:00 hs / HoraFin 23:00 hs
EEPROM.put(20,ModoLuna); // guarda modo luna
Co2Var.Status = 0; Co2Var.HoraInicio = 540; Co2Var.HoraFin =
1300;
// 0 Apagado, 1 Ecendido // 540 minutos o 09:00 AM // 1300 minutos
= 21:40 hs
EEPROM.put(30, Co2Var); // guarda control Co2
}
EEPROM.get( 0, ModoSol );
EEPROM.get(10, TermoVar);
EEPROM.get(20, ModoLuna);
EEPROM.get(30, Co2Var );
lcd.setCursor(0, 0);
lcd.print("Control de Acuario");
lcd.setCursor(6, 1);
lcd.print("CADUSA");
lcd.setCursor(0,4);
for( int i=0 ; i < 20 ; i++ ) {
lcd.print(".");
delay(150);
}
// delay(500);
lcd.clear();
27. }
void loop() {
unsigned long currentMillis = millis();
button.tick();
LeeEncoder(0,7,1,1); // Lee el enconder de 0 a 7, de a 1 paso y con
tope final.
if (currentMillis % 6000 < 5) { // Si el resto de dividir
currentMillis por 6000 da menos que 5...
ImprimeFecha(); ImprimeHora(); ImprimeTemp();
if (currentMillis - backlightMillis > 60000) {
lcd.setBacklight(LOW); } // Apaga el lcd a los 60s de inactividad
}
if (currentMillis % 3000 < 5) {
Termostato();
ControlCo2();
}
if (lastPos != newPos) {
lcd.setCursor(0,3);
switch (newPos) {
case 0:
lcd.print("> ");
break;
case 1:
lcd.print("Modo Sol ");
break;
case 2:
lcd.print("Modo Luna ");
break;
case 3:
lcd.print("Calefactor ");
break;
28. case 4:
lcd.print("Control Co2 ");
break;
case 5:
lcd.print("Mantener ");
break;
case 6:
lcd.print("Fecha y Hora");
break;
case 7:
lcd.print("< ");
break;
}
lastPos = newPos;
Mod_Menu = newPos;
}
// Aquí el código para el control de la iluminación
if (currentMillis % 7000 < 5) { // Cada 7 segundos
/********* Modo SOL *****************
if (MinActual >= ModoSol.HoraInicio && MinActual <=
ModoSol.HoraFin){ // Este es el rango de hora de Sol
if (MinActual < (ModoSol.HoraInicio + 30)) { // Periodo Amanecer
lcd.setCursor(0,1); lcd.print(" ");
lcd.setCursor(0,1); lcd.print(Periodo_name[1]);
brillo = map((MinActual - ModoSol.HoraInicio),0,30,0,255);
}
if ((ModoSol.HoraInicio+30) < MinActual and (ModoSol.HoraFin-30) >
MinActual ) { // Periodo pleno sol
lcd.setCursor(0,1); lcd.print(" ");
lcd.setCursor(0,1); lcd.print(Periodo_name[2]);
29. if (brillo < 255) { lcd.clear(); lcd.setCursor(6,1);
lcd.print("Subiendo"); newPos=0; lastPos=1;}
while (brillo < 255) {
brillo++; Set_pwmSol(); delay(25);
if(brillo==255) {lcd.clear();}
}
}
if (MinActual >= (ModoSol.HoraFin-30)) { // Periodo Atardecer
lcd.setCursor(0,1); lcd.print(" ");
lcd.setCursor(0,1); lcd.print(Periodo_name[3]);
brillo = map((ModoSol.HoraFin - MinActual),30,0,255,0) ;
}
Set_pwmSol();
} // Fin rango de hora de Sol
/********* Modo LUNA *****************/
if (ModoLuna.Status == 1){ // Si el modo Luna está activado
if (MinActual > ModoLuna.HoraInicio && MinActual <
ModoLuna.HoraFin){ // Este es el rango de hora de Luna
if (MinActual < (ModoLuna.HoraInicio+30)) { // Saliendo Luna
lcd.setCursor(0,1); lcd.print(" ");
lcd.setCursor(0,1); lcd.print(Periodo_name[5]);
brillo = map((MinActual - ModoLuna.HoraInicio),0,30,0,255);
}
if (MinActual > (ModoLuna.HoraInicio+31) and MinActual <
(ModoLuna.HoraFin-31)) { // Luna plena
lcd.setCursor(0,1); lcd.print(" ");
lcd.setCursor(0,1); lcd.print(Periodo_name[4]);
if (brillo < 255) { lcd.clear(); lcd.setCursor(6,1);
lcd.print("Subiendo");}
while (brillo < 255) {
brillo++; Set_pwmLuna(); delay(25);
30. if(brillo==255) {lcd.clear();}
}
}
if (MinActual > (ModoLuna.HoraFin-30)) { // Ocultando Luna
brillo = map((ModoLuna.HoraFin -
MinActual),30,0,255,0);
}
Set_pwmLuna();
} // Fin rango de hora de Luna
} // Fin si el modo Luna está activado
/********* Periodos de Oscuridad *****************/
if (MinActual > ModoLuna.HoraFin or MinActual < ModoSol.HoraInicio){
// Periodo Noche
lcd.setCursor(0,1); lcd.print(" ");
lcd.setCursor(0,1); lcd.print(Periodo_name[0]);
if (brillo > 0) { lcd.clear(); lcd.setCursor(6,1);
lcd.print("Apagando"); newPos=0; lastPos=1;}
while (brillo > 0) {
brillo--; Set_pwmNoche(); delay(25);
if(brillo==0) {lcd.clear();}
}
}
if (MinActual > ModoSol.HoraFin && MinActual < ModoLuna.HoraInicio){
// Periodo entre Sol y Luna
lcd.setCursor(0,1); lcd.print(" ");
lcd.setCursor(0,1); lcd.print(Periodo_name[6]);
if (brillo > 0) { lcd.clear(); lcd.setCursor(6,1);
lcd.print("Apagando");}
while (brillo > 0) {
brillo--; Set_pwmNoche(); delay(25);
if(brillo==0) {lcd.clear();}
31. }
}
delay(5);
}
// Fin del código para el control de la iluminación
} // FIN LOOP
void clickbutton() { // Si se hace click porque en el loop esta:
button.tick();
if (Mod_Menu > 0) {
delay(100);
switch (Mod_Menu) { // Según s hizo clik durante el menú que
aparece en la pantalla principal del loop
case 1:
Set_Modo_Sol(); // Si Mod_Menu vale 1 entonces se ejecuta la
función Set_Modo_Sol() y break detiene.
break;
case 2:
Set_Modo_Luna();
break;
case 3:
Set_Calefactor();
break;
case 4:
Set_Co2();
break;
case 5:
Mantenimiento();
break;
case 6:
32. setupTime();
break;
}
}
}
void LeeEncoder(int ROTARYMIN, int ROTARYMAX, int ROTARYSTEPS, int
ROTARYTOPE )
{
encoder.tick(); // Lee el encoder y actualiza el puntero. La librería
lleva la cuenta desde 0.
newPos = encoder.getPosition() * ROTARYSTEPS; // Asigna a newPos el
puntero del encoder multiplicado por los pasos.
if (newPos != lastPos) { // Enciende el display al mover el
encoder
lcd.setBacklight(HIGH);
backlightMillis = millis();
}
if (ROTARYTOPE == 1) { // Si el rotor tiene tope entonces no se pasa
de valor
if (newPos < ROTARYMIN) { // Solo si el puntero es menor a Valor
Mínimo ->
encoder.setPosition(ROTARYMIN / ROTARYSTEPS); // Se fija el
puntero al valor mínimo por los pasos
newPos = ROTARYMIN;
}
if (newPos > ROTARYMAX) { // Solo si el puntero es mayor a Valor
Máximo ->
encoder.setPosition(ROTARYMAX / ROTARYSTEPS); // Se fija el
puntero al valor mínimo por los pasos
newPos = ROTARYMAX;
}
}
33. if (ROTARYTOPE == 0) { // Si el rotor SI tiene tope entonces se pasa
del valor máximo al mínimo y viceversa.
if (newPos < ROTARYMIN) { // Solo si el puntero es menor a Valor
Mínimo ->
encoder.setPosition(ROTARYMAX / ROTARYSTEPS);
newPos = ROTARYMAX; // Entonces el puntero saltará del mínimo al
máximo
}
if (newPos > ROTARYMAX) { // Solo si el puntero es mayor al Valor
Máximo ->
encoder.setPosition(ROTARYMIN / ROTARYSTEPS);
newPos = ROTARYMIN; // Entonces el puntero saltará del máximo al
mínimo.
}
}
}
void read_but() // Función que devuele enc_but = 1 al soltar el botón
del encoder.
{
bool pin_status;
delay(5);
pin_status = digitalRead(enc_c); //Se asigna a pin_status el valor
del botón
if (pin_status == LOW && enc_but != 128) { // Si esta pulsado y enc_but
no vale 128
enc_but = 128; // Ahora enc_but si vale 128
}
if (pin_status == HIGH && enc_but == 128) { // Si enc_but que ahora
vale 128 y se suelta el pulsador
enc_but = 1; // ahora enc_but vale 1;
}
}
34. void Set_Modo_Sol(){
int salir=1;
int x=0;
int hh1 = ModoSol.HoraInicio/60;
int mm1 = ModoSol.HoraInicio - (hh1*60);
int hh2 = ModoSol.HoraFin/60;
int mm2 = ModoSol.HoraFin - (hh2*60);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("M-Sol");
lcd.setCursor(1,1); printDigits(hh1);lcd.print(":");
printDigits(mm1);lcd.print("hs");
lcd.setCursor(1,3); printDigits(hh2);lcd.print(":");
printDigits(mm2);lcd.print("hs");
lcd.setCursor(3,2); lcd.print("a");
lcd.setCursor(9,0);
lcd.print("Azul :");
lcd.print(map(ModoSol.Azul,255,0,100,0));
lcd.print("%");
lcd.setCursor(9,1);
lcd.print("Rojo :");
lcd.print(map(ModoSol.Rojo,255,0,100,0));
lcd.print("%");
lcd.setCursor(9,2);
lcd.print("Blanco:");
lcd.print(map(ModoSol.Blanco,255,0,100,0));
lcd.print("%");
lcd.setCursor(6,0);
46. int mm1 = TermoVar.HoraInicio%60; // Obtener los minutos de
TermoVar.HoraInicio
int hh2 = TermoVar.HoraFin/60; // Obterner las horas de
TermoVar.HoraFin
int mm2 = TermoVar.HoraFin%60; // Obetener los minutos de
TermoVar.HoraFin
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Calefactor");
lcd.setCursor(14,0);
if (!TermoVar.Status)
lcd.print("Off");
else
lcd.print("On ");
lcd.setCursor(13,0); lcd.blink();
encoder.setPosition(TermoVar.Status);
lastPos = newPos ; delay(250);
while (enc_but!=1) { // Ciclo ON OFF del termostato
LeeEncoder(0,1,1,1); // Lee el enconder de 0 a 1
if (lastPos != newPos) { // Si se gira el encoder, entonces...
lastPos = newPos ; // Vovemos a igualar lastPos
TermoVar.Status = newPos; // Fijamos TermoVar.Status al nuevo
valor,0 o 1.
lcd.setCursor(14,0);
if (!TermoVar.Status)
lcd.print("Off");
else
lcd.print("On ");
lcd.setCursor(13,0);
}
47. read_but(); // Lee si opromimos el botón del encoder y enc_but pasa
a valor 1.
} // Fin Ciclo ON OFF del termostato
enc_but=0; // Habiendo opromido el encoder se sale del while y
volvemos a poner a enc_but en valor 0.
if (!TermoVar.Status) { // Si TermoVar.Status quedó en Off interrumpe
el resto y dirige hacia la etiqueta setout
goto setout;
}
lcd.setCursor(0,1);
lcd.print("Dia: ");
lcd.print(TermoVar.TempHorario);lcd.print("337");
lcd.setCursor(3,1);
encoder.setPosition(TermoVar.TempHorario);
lastPos = newPos; delay(250);
while (enc_but!=1) { // Ciclo fija temperatura durante el día
LeeEncoder(10,34,1,1); // Cambiar aquí si se necesita una
temperatura inferior a 10 o superior a 34.
if (lastPos != newPos){
lastPos = newPos ;
TermoVar.TempHorario = newPos;
lcd.setCursor(5,1);
lcd.print(" ");
lcd.setCursor(5,1);
lcd.print(TermoVar.TempHorario);
lcd.print("337");
lcd.setCursor(3,1);
if (TermoVar.TempResto > TermoVar.TempHorario)
TermoVar.TempResto = TermoVar.TempHorario;
48. }
read_but();
}
enc_but=0; // Fin Ciclo fija temperatura durante el día
lcd.setCursor(0,2);
lcd.print("Hora: ");
printDigits(hh1);
lcd.print(":");
printDigits(mm1);
lcd.print(" a ");
printDigits(hh2);
lcd.print(":");
printDigits(mm2);
lcd.setCursor(5,2);
encoder.setPosition(hh1);
lastPos = newPos ; delay(250);
while (enc_but!=1) {
LeeEncoder(0,(hh2-1),1,1);
if (lastPos != newPos) {
lastPos = newPos ;
hh1 = newPos;
lcd.setCursor(6,2);
lcd.print(" ");
lcd.setCursor(6,2);
printDigits(hh1);
lcd.setCursor(5,2);
}
52. void Set_Co2() { // Creamos 4 variales temporales para
horas y minutos
int hh1 = Co2Var.HoraInicio/60; // Obtener las horas de
Co2Var.HoraInicio
int mm1 = Co2Var.HoraInicio%60; // Obtener los minutos de
Co2Var.HoraInicio
int hh2 = Co2Var.HoraFin/60; // Obterner las horas de Co2Var.HoraFin
int mm2 = Co2Var.HoraFin%60; // Obetener los minutos de
Co2Var.HoraFin
lcd.clear();
lcd.setCursor(0,0); lcd.print("Control Co2");
lcd.setCursor(14,0);
if (Co2Var.Status == 0) lcd.print("Off");
if (Co2Var.Status == 1) lcd.print("On ");
lcd.setCursor(13,0); lcd.blink();
encoder.setPosition(Co2Var.Status);
lastPos = newPos ; delay(250);
while (enc_but!=1) { // Ciclo ON OFF del control Co2
LeeEncoder(0,1,1,1);
if (lastPos != newPos) {
lastPos = newPos ;
Co2Var.Status = newPos;
lcd.setCursor(14,0);
if (Co2Var.Status == 0)
lcd.print("Off");
else
lcd.print("On ");
lcd.setCursor(13,0);
}
read_but();
53. }
enc_but=0; // Fin Ciclo ON OFF del control Co2
if (!Co2Var.Status) { goto set2out; } // Si apaga el Co2 interrumpe el
resto y dirige a grabar
lcd.setCursor(0,2);
lcd.print("Hora: ");
printDigits(hh1);
lcd.print(":");
printDigits(mm1);
lcd.print(" a ");
printDigits(hh2);
lcd.print(":");
printDigits(mm2);
lcd.setCursor(5,2);
encoder.setPosition(hh1);
lastPos = newPos ; delay(250);
while (enc_but!=1) {
LeeEncoder(0,(hh2-1),1,1);
if (lastPos != newPos){
lastPos = newPos ;
hh1 = newPos;
lcd.setCursor(6,2);
lcd.print(" ");
lcd.setCursor(6,2);
printDigits(hh1);
lcd.setCursor(5,2);
}
61. void ImprimeTemp() {
if (sensors.getAddress(outsideThermometer, 0)) { // Si hay
comunicación con el sensor DS18B20
sensors.requestTemperatures(); //Se envía el comando para leer la
temperatura
Temp= sensors.getTempCByIndex(0); //Se obtiene la temperatura en ºC
lcd.setCursor(13,2);
lcd.write(2); // Imprime el símbolo termómetro
lcd.print(":");
if (Temp < 10) {
lcd.print(' ');
} // Si la temperatura es menor de 10 deja un espacio en blanco
lcd.print(Temp,1); // Imprime la temperatura del DS18B20 con un
decimal.
lcd.print("337"); // Imprime °
}
else { // Si no hay comunicación con el sensor porque se ha cortado
el cable, etc.
lcd.setCursor(13,2);
lcd.write(2); // Imprime el símbolo termómetro
lcd.print(":Falla"); // Comunica la falla del sensor
}
Tlamp = rtc.getTemperature(); // Asigna a Tlamp la temperatura del RTC
lcd.setCursor(13,3);
lcd.write(3); // Imprime el símbolo lámpara
lcd.print(":");
if (Tlamp < 10) { lcd.print(' ');} // Si la temperatura es menor de 10
deja un espacio en blanco
lcd.print(Tlamp,1); // Imprime la temperatura del RTC con un decimal.
lcd.print("337"); // Imprime °
62. if (Tlamp > UnderTemp) { digitalWrite(13, HIGH); } // Activa el cooler
a los 50°
if (Tlamp < UnderTemp-5) { digitalWrite(13, LOW); } // Lo apaga a los
45°
if (Tlamp > OverTemp){
overheating();
} // Si la temperatura es mayor a OverTemp llama a la función
overheating
}
void MideTemperatura() {
sensors.requestTemperatures(); //Se envía el comando para leer la
temperatura
Temp = sensors.getTempCByIndex(0); //Se asigna a la variable Temp,
la temperatura en ºC del sensor ds18B20
Tlamp = rtc.getTemperature(); // Se asigna a la variable Tlamp la
temperatura del RTC
}
void ImprimeFecha() {
DateTime now = rtc.now(); // Consulta al RTC
lcd.setCursor(0,0); // Fija el cursor en la primera línea
lcd.print(DiaDeSemana[now.dayOfTheWeek()]); // Imprime el día de la
semana
lcd.setCursor(10,0);
printDigits(now.day()); // Imprime el día
lcd.print('/'); // Separador
printDigits(now.month()); // Imprime el mes
lcd.print('/'); // Separador
63. lcd.print(now.year(), DEC); // Imprime el año
}
void ImprimeHora() {
DateTime now = rtc.now(); // Consulta el RTC
lcd.setCursor(13,1); // Fija cursor en 13,1
lcd.write(1); // Imprime el gráfico
lcd.print(':');
printDigits(now.hour()); // Imprime hora
lcd.print(':');
printDigits(now.minute()); // Imprime minuto
MinActual = now.hour()*60+now.minute(); // Se actualiza la variable
MinActual que será usada para encender y apagar las luces del acuario. Su
valor va de 0 a 1439.
}
void Hecho() {
currentMillis = millis();
previousMillis = millis();
lcd.setBacklight(HIGH);
encoder.setPosition(0);
delay(100);
newPos = 0;
Mod_Menu = newPos;
lastPos = 1;
lcd.noBlink();
lcd.clear();
lcd.setCursor(6, 1);
lcd.print("Hecho !!");
64. delay(2000);
lcd.clear();
RefreshLCD();
}
void overheating() {
analogWrite(SFetAzul,0);
analogWrite(SFetRojo,0);
analogWrite(SFetBlanco,0);
byte cursor=20;
lcd.clear();
lcd.setBacklight(HIGH);
lcd.setCursor(0, 0);
lcd.print("Alta Temperatura!!");
lcd.setCursor(0,3);
lcd.print("3773773773773773773773773773773773773773773773
77377377377377"); // Imprime relleno
while (Tlamp > UnderTemp) { // Entra en un ciclo mientras la
temperatura sea excesiva.
MideTemperatura(); // Mide ambas temperaturas
Termostato(); // Corrobora el termostato para el
calefactor
ControlCo2(); // Comprueba si el Co2 debe estar activo
lcd.setCursor(7, 1);
lcd.print(Tlamp,2);
lcd.print("337"); // Imprime °
65. delay(1000);
cursor--;
lcd.setCursor(cursor,3);
lcd.print("376"); // Imprime en Blanco
if (cursor ==0) {cursor =20; delay(1000); lcd.setCursor(0,3);
lcd.print("3773773773773773773773773773773773773773773773
77377377377377");
}
} // Sale del ciclo de alta temperatura
lcd.clear(); // Limpia la pantalla
RefreshLCD(); // Actualiza el lcd con los datos normales.
}
void Termostato(){
if (TermoVar.Status) { // Si el termostato esta encendido
if ((MinActual >= TermoVar.HoraInicio) && (MinActual <=
TermoVar.HoraFin)) { // Rango de hora de día
if (Temp < TermoVar.TempHorario) {
digitalWrite(Relay1, LOW);
} else {
digitalWrite(Relay1, HIGH);
}
}
else
if (Temp < TermoVar.TempResto) {
digitalWrite(Relay1, LOW);
} else {
digitalWrite(Relay1, HIGH);
66. }
}
if (!TermoVar.Status)
digitalWrite(Relay1, HIGH);
delay(5);
}
void ControlCo2(){
if (Co2Var.Status == 1) { // Si el termostato esta encendido
if ((MinActual >= Co2Var.HoraInicio) && (MinActual <=
Co2Var.HoraFin)) { // Rango de hora de día
digitalWrite(Relay2, LOW);
}
else {
digitalWrite(Relay2, HIGH);
} // Apagado para el resto del día
}
if (Co2Var.Status == 0) {
digitalWrite(Relay2, HIGH);
} // Apagado para Co2 Off
delay(5);
}
void Set_pwm(){
analogWrite(SFetAzul, spwm[1]);
analogWrite(SFetRojo, spwm[2]);
analogWrite(SFetBlanco, spwm[3]);
}