Calificación:
  • 1 voto(s) - 4 Media
  • 1
  • 2
  • 3
  • 4
  • 5
Estacion Meteorologica
#1
HOJA DE RUTA

FINALIZADO

CODIGOS AQUI:

http://spainlabs.com/foro/viewtopic.php?...537#p10783





resim

resim

resim

resim

resim

resim

resim

resim

resim

resim

Ultimando Detalles y Peparando Carcasas:

resim

resim

resim

resim

Fotos con la placa propia ya acabada:

resim

resim

resim

resim


Caracteristicas finales de la estacion interior (Ya finalizada en cuanto a programacion)

-Reloj
-Fecha (Con dia de la semana)
-Alarma diaria
-Iconos de alarma activada y alarma sonando en todos los menus (Excepto estando en ajustes)
-Temperatura y Humedad (Exterior Interior)
-Presion (Exterior)
-Luminosidad (Exterior)
-Maximos y minimos de los parametros anteriores
-Posibilidad de resetear estos valores
-Prediccion estadistica de Borrasca/Anticiclon
-Deteccion de fallas en sensores externos
-Auto apagado de luz del LCD (Tiempo de 1min a 15min, ajustable en menu opciones)
-Apagado de luz a traves de pulsacion en encoder
-Imposiblidad de dejar cursor en menu de ajustes, retorno automatico a pantalla principal
-Ajuste de fecha-hora-alarma
-Comunicacion Inalambrica
-Unica interface con usuario a traves de encoder

DESCRIPCION:

El proyecto, consistira en realizar dos estaciones, una externa y una interna, conectadas de forma inalambrica, la estacion externa enviara los datos atmosfericos a la estacion interna, la cual los representara a traves de una pantalla LCD 20x4. La estacion interna la controlaremos a traves de un encoder rotativo, evitando el uso de botones.

La distribucion sera por menus, por los cuales nos iremos desplazando con el encoder, pudiendo mostrar los diferentes datos, atmosfericos, minimos, maximos, interiores, exteriores, el reloj, las opciones, y todo lo que vayamos implementando.

La estacion externa funcionara con una bateria y un panel solar, aun se esta viendo la viabilidad de este metodo. Pero esperemos que funcione ya que no nos interesa tener cables a la estacion exterior que sera totalmente inalambrica.

Tendremos datos de temperatura, humedad, presion y luminosidad en la estacion exterior, y de temperatura y humedad en la interior. Registrando de igual modo valores maximos y minimos. Puediendo reinicializarlos cuando se quiera.

Sensores Utilizados:
-DHT22 (Temperatura Humedad)
-BH1750 (Sensor Luminosidad)
-BMP180 (Sensor de Presion)
-DS3231 (Real Time)

Comunicacion
-Modulos NRF24L01

Control
-Arduino NANO
-Encoder Rotativo
-12F508 (Melodia de Alarma)

EXPLICACIONES

ENCODER

BH1750 (Sensor de Luz)

BMP180 (Sensor de Presion)

DHT22 (Sensor Temp y Humedad)

OBSERVACIONES

- Sustitui el modulo de reloj DS1307 por el actual, debido a que era bastante poco preciso. Con el que tengo ahora por ahora no tengo problemas, ademas que tiene mas funcionalidades, aunque en mi caso no las use

-Estoy a la espera de recibir el paner solar para ver si es viable para el proyecto.

VIDEOS

[spoiler]





























[/spoiler]
Citar
#2
Giltesa se ha hecho una muy recientemente, podria servirte para empezar
Citar
#3
La estación meteorológica siempre fue un proyecto muy interesante para hacer con arduino. Giltesa sabe de ello, revisa su blog.

Lo mejor que puedes hacer es lo que comentas, ir pidiendo los módulos poco a poco e ir probando. Así es como voy haciendo yo con mi proyecto. El único módulo del que no he leído nada es del detector de lluvia ese, no lo conocía.

Si vas sobrado de conexiones, puedes añadirle algún sensor para medir la calidad del aire. Y ya si montas un LCD a color la lías parda Confundidois1:
Citar
#4
El arduino normal creo que te llega para todo
Citar
#5
CODIGO

Estacion Interior

[spoiler]
Código:
//Librerias
#include <dht.h>
#include <DS3231.h>
#include <Wire.h>
#include <EEPROM.h>
#include <LiquidCrystal_I2C.h>
#include <Bounce.h>
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

//Inicializacion LCD
LiquidCrystal_I2C lcd(0x20,20,4);  //Direccion y tamaño

//Inicializacion del DS3231
DS3231 RTC; //Creamos el objeto

//Inicializacion DHT22
dht DHT;            //Creamos la clase
#define DHT22_PIN 7 //Asignamos al pin 7

//Inicializacion Inalambrica
#define CE_PIN   9  //Definicion de pines
#define CSN_PIN 10
const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; //Definicion de canales
RF24 radio(CE_PIN, CSN_PIN); // Creamos Radio

//Estructura (Payload del NRF)
struct estacion {
  //Prediccion
  byte pred;
  //Error
  byte error;
  //Datos
  int temp;
  int rh;
  int lux;
  int32_t pa;
  //Maximos
  int maxtemp;
  int maxrh;
  int maxlux;
  int32_t maxpa;
  //Minimos
  int mintemp;
  int minrh;
  int minlux;
  int32_t minpa;
}Datos;

//Temporales para conversion
float temp;
float rh;
float maxtemp;
float mintemp;
float maxrh;
float minrh;
float pa;
float maxpa;
float minpa;

//Variables DHT22 Interior
float tempin;
float rhin;

//Caracteres
byte flecha_dech[8] = { 0x0,0x8,0xc,0xe,0xc,0x8,0x0,0x0 };        //Flecha derecha
byte alarma[8] = { 0x4,0xe,0xe,0xe,0x1f,0x0,0x4,0x0 };            //Señal Alarma
byte altavoz_1[8] = { 0x1,0x3,0xf,0xf,0xf,0x3,0x1,0x0 };          //Altavoz1
byte altavoz_2[8] = { 0x8,0x10,0x0,0x18,0x0,0x10,0x8,0x0 };       //Altavoz2
byte linea_inf[8] = { 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f };         //Linea Inferior
byte cir_vacio[8] = { 0x0,0xe,0x11,0x11,0x11,0xe,0x0,0x0 };       //Circulo Vacio
byte cir_lleno[8] = { 0x0,0xe,0x1f,0x1f,0x1f,0xe,0x0,0x0 };       //Circulo Lleno
byte flecha_aba[8] = { 0x0,0x0,0x0,0x0,0x0,0x1f,0xe,0x4 };         //Flecha abajo
byte barra_sup[8] = { 0x1f,0x1f,0x1f,0x1f,0x0,0x0,0x0,0x0 };      //Barra arriba
byte barra_inf[8] = { 0x0,0x0,0x0,0x0,0x1f,0x1f,0x1f,0x1f };      //Barra abajo
byte barra_llena[8] = { 0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f };//Barra llena


//Pines del encoder
enum PinAssignments {
  encoderPinA = 2,   // right (DT)
  encoderPinB = 3,   // left (CLK)
};

//Asignacion de pines
const int encoderSW = 8;                  //Pin 8 para pulsador del encoder
const int pin_alarma = 5;                 //Pin 5 para activacion alarma
const int pulsador = 4;                   //Pin 4 para detener alarma
Bounce encoderSW_bounce = Bounce(8,10);   //Asignamos el debounce
Bounce pulsador_bounce = Bounce(4,10);    //Asignamos el debounce

//Variables del encoder
volatile int menu = 1;              //Contador para el encoder
int lastmenu = 0;                   //Ultima posicion del encoder
static boolean rotating=false;      //Debounce
boolean A_set = false;              //Variables de seteo
boolean B_set = false;  

//Dias de la semana
char *diasemana[] = {"Lunes", "Martes", "Miercoles", "Jueves", "Viernes", "Sabado", "Domingo"};

//VARIABLES
//Variables temporales para ajuste de hora
int fut_hora = 0;
int fut_min = 0;
int fut_seg = 0;
//Variables temporales para ajuste de fecha
int fut_ano = 0;
int fut_mes = 0;
int fut_dia = 0;
int fut_diaw = 0;
//Variables alarma
int hora_alarma = 0;
int min_alarma = 0;
int seg_alarma = 0;
int alarma_on = 0;
int fut_hora_alarma = 0;
int fut_min_alarma = 0;
int fut_seg_alarma = 0;  
int fut_alarma_on = 0;        //Variable de activacion
boolean ok_alarma = false;  //Si llegamos a la hora activamos esta variable
unsigned long off_alarma;   //Variable de tiempo para apagado de alarma
//Variables para los menus
boolean menu_ajustes =false;
boolean salir = false;    
boolean cambio = false;
int maxmin = 0;
//Variables
int dia_semana = 10;  //Variable para dia de la semana
boolean back = true;  //Variable de retroiluminacion activa
//Variables de cuenta de tiempo
unsigned long temp_past;
unsigned long tiempo_res;
unsigned long time_past;
//Variable reinicio de maximos y minimos
boolean reinicio = true;
//Variable para primer inicio
boolean inicio = false;
//Variables timeout del menu
boolean datos_ok = true;
boolean time_out = false;
unsigned long temp_out;
//Variable timeout de backlight
unsigned long temp_margen;
unsigned long fut_luz;
boolean luz_activated = false;

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void setup() {
    lcd.init();                //Iniciamos el LCD
  lcd.backlight();                      //Iniciamos la retroiluminacion    
    //ASIGNACION DE TIPO DE PINES
  pinMode(encoderPinA, INPUT_PULLUP);   //Pines del encoder
  pinMode(encoderPinB, INPUT_PULLUP);
  pinMode(encoderSW, INPUT_PULLUP);     //Interruptor del encoder
  pinMode(pin_alarma, OUTPUT);          //Salida de la alarma activa
  pinMode(pulsador, INPUT);             //Entrada detener alarma
  //Configuracion inicial de pin
  digitalWrite(pin_alarma,LOW);
  //ASIGNACION DE INTERRUPCIONES
  attachInterrupt(0, doEncoderA, CHANGE); //Interrupcion 0 para el pin 2
  attachInterrupt(1, doEncoderB, CHANGE); //Interrupcion 1 para el pin 3
  //CREACION CARACTERES
  lcd.createChar(1,flecha_dech);  // 1 -> Flecha Derecha
  lcd.createChar(2,alarma);       // 2 -> Alarma
  lcd.createChar(3,altavoz_1);    // 3 -> Altavoz1
  lcd.createChar(4,linea_inf);    // 4 -> Linea Inferior
  lcd.createChar(5,altavoz_2);    // 5 -> Altavoz2
  lcd.createChar(6,cir_vacio);    // 6 -> Circulo Vacio
  lcd.createChar(7,cir_lleno);    // 7 -> Circulo Vacio
  lcd.createChar(8,flecha_aba);   // 8 -> Flecha abajo

  //Leemos valores de la alarma
  hora_alarma=EEPROM.read(1);
  min_alarma=EEPROM.read(2);
  seg_alarma=EEPROM.read(3);
  alarma_on=EEPROM.read(4);
  //Comunicacion
  radio.begin();
  radio.openWritingPipe(pipes[0]);
  radio.openReadingPipe(1,pipes[1]);
  radio.startListening();
  //Reloj
  RTC.begin();
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void loop() {
    if(menu != lastmenu){  //Si la variable menu cambia
    menu = menu > 5 ? 1 : menu ;         //Si menu es mayor que 5, volvemos a la posicion 1      
    menu = menu < 1 ? 5 : menu ;         //Si menu es menor que 1, volvemos a la posicion 5
    lastmenu=menu;       //Igualamos situamos lastmenu como actual
    lcd.backlight();
    lcd.clear();         //Limpiamos pantalla
    print_margen();      //Dibujamos el margen superior
    back=true;
    cambio = true;
    maxmin = 0;
    }
    switch (menu) {         //Switch para mostrar menus
        case 1:               //Opcion 1 RELOJ
      lcd.setCursor(7,0);  
      lcd.print("Reloj");
      lcd.setCursor(9,0);
      printhour();                  //Imprimimos la hora
      fun_alarma();                 //Comprobamos alarma
      onbacklight();                //Apagar luz LCD
      func_time_out();              //Temporizacion luz LCD
          break;    
        case 2:               //Opcion 2 TIEMPO
          lcd.setCursor(6,0);
          lcd.print("Tiempo");
      fun_alarma();       //Comprobamos alarma
        comunicacion();     //Recibimos datos
      adaptacion();       //Adaptamos los datos
      //Representamos los datos por pantalla
        lcd.setCursor(0,1);
        lcd.print("Ext:");
        lcd.print(temp,1);
        lcd.print("C - ");
        lcd.print(rh,1);
        lcd.print("%");
        lcd.setCursor(0,2);
        lcd.print("Presion:");
        lcd.print(pa);
        lcd.print(" mbar");
        lcd.setCursor(0,3);
        lcd.print("Luz: ");
        lcd.print(Datos.lux);
        lcd.print(" Lux    ");
      onbacklight();                //Apagar luz LCD
      func_time_out();              //Temporizacion luz LCD
          break;
    case 3:                //Opcion 3 MAXIMOS
      fun_alarma();   //Comprobamos alarma
      comunicacion(); //Recibimos datos
      adaptacion();   //Adaptamos los datos
      func_time_out();//Temporizacion luz LCD
      switch (maxmin) {
        case 0:   //MENU DE MAXIMOS
          lcd.setCursor(6,0);
          lcd.print("Maximos");
          lcd.setCursor(0,1);
          lcd.print("Ext:");
          lcd.print(maxtemp,1);
          lcd.print("C - ");
          lcd.print(maxrh,1);
          lcd.print("%");
          lcd.setCursor(0,2);
          lcd.print("Presion:");
          lcd.print(maxpa);
          lcd.print(" mbar");
          lcd.setCursor(0,3);
          lcd.print("Luz: ");
          lcd.print(Datos.maxlux);
          lcd.print(" Lux    ");
          break;
        case 1:   //MENU DE MINIMOS
          lcd.setCursor(6,0);
          lcd.print("Minimos");
          lcd.setCursor(0,1);
          lcd.print("Ext:");
          lcd.print(mintemp,1);
          lcd.print("C - ");
          lcd.print(minrh,1);
          lcd.print("%");
          lcd.setCursor(0,2);
          lcd.print("Presion:");
          lcd.print(minpa);
          lcd.print(" mbar");
          lcd.setCursor(0,3);
          lcd.print("Luz: ");
          lcd.print(Datos.minlux);
          lcd.print(" Lux    ");
        break;
      }
      //Si pulsamos alternamos entre menu maximos o minimos (Ampliable)
      encoderSW_bounce.update();
      if (encoderSW_bounce.fallingEdge()){
        temp_out = millis();
        lcd.backlight();
        back = true;
        maxmin=maxmin++;
        maxmin = maxmin > 1 ? 0 : maxmin ;    
        maxmin = maxmin < 0 ? 1 : maxmin ;
        lcd.clear();
        print_margen();    
      }
      break;
        case 5:                   //Opcion 5 OPCIONES
          lcd.setCursor(6,0);
          lcd.print("Opciones");
      lcd.setCursor(1,1);
      lcd.print("Ajustar Hora");
      lcd.setCursor(1,2);
      lcd.print("Ajustar Fecha");
      lcd.setCursor(1,3);
      lcd.print("Ajustar Alarma");
      encoderSW_bounce.update();    //Actualizamos situacion del pulsador
      if (encoderSW_bounce.fallingEdge()){ //Si pulsamos entramos en opciones
        temp_out = millis();
        datos_ok = true;                    //Reiniciamos variable de sobrepaso de tiempo
        salir = false ;                     //Seteamos la variable de salida a false
        menu= 1 ;                           //Reinicializamos las variables del encoder
        lastmenu = 1;
        while ( salir == false ){             //Mientras salir sea FALSE, mantenemos el menu
          rotating = true;                    //Establecemos el debounce
          if(menu != lastmenu){               //Si menu ha cambiado
            menu=constrain(menu,1,6);         //Establecemos los limites del menu
            delete_cursor(lastmenu);          //Borramos el cursor anterior al cambiar el valor de menu
            lastmenu=menu;                    //Seteamos como actual lastmenu
          }
          if (menu > 3 && !menu_ajustes){   //Si sobrepasamos por debajo
            menu_ajustes=!menu_ajustes;     //Seteamos que ya hemos limpiado pantalla
            lcd.clear();
            lcd.setCursor(1,0);              
            lcd.print("Reset Max-Min");
            lcd.setCursor(1,1);
            lcd.print("Tiempo LUZ");
            lcd.setCursor(1,2);
            lcd.print("Salir");
          }
          if (menu < 4 && menu_ajustes){    //Si volvemos al menu anterior
            menu_ajustes=!menu_ajustes;     //Seteamos que ya hemos limpiado pantalla
            lcd.clear();      
            print_margen();                 //Dibujamos el margen superior              
            lcd.setCursor(6,0);
            lcd.print("Opciones");
            lcd.setCursor(1,1);
            lcd.print("Ajustar Hora");
            lcd.setCursor(1,2);
            lcd.print("Ajustar Fecha");
            lcd.setCursor(1,3);
            lcd.print("Ajustar Alarma");
          }
          switch (menu) {           //Switch para el menu de ajustes
            case 1:                 //Ajuste de HORA
              lcd.setCursor(0,1);   //Situamos el indicador en la linea 1
              lcd.write(1);
              encoderSW_bounce.update();              //Actualizamos pulsador
              if (encoderSW_bounce.fallingEdge()){    //Si pulsamos entramos a ajustar la hora
              set_hour();                             //Funcion ajustar hora
              salir = true ;                          //Una vez ajustada salimos
              lcd.clear();
              menu=1;                                 //Volvemos al menu RELOJ
              lastmenu = 0;
              }
            break;
            case 2:                 //Ajuste de FECHA
              lcd.setCursor(0,2);   //Situamos el indicador en la linea 2
              lcd.write(1);
              encoderSW_bounce.update();              //Actualizamos pulsador
              if (encoderSW_bounce.fallingEdge()){    //Si pulsamos entramos a ajustar la hora
              set_date();                             //Funcion para ajustar la fecha
              salir = true ;                          //Una vez ajustada salimos del menu
              lcd.clear();  
              menu=1;                                 //Volvemos al menu reloj
              lastmenu = 0;
              }
            break;
            case 3:                 //Ajuste ALARMA
              lcd.setCursor(0,3);   //Situamos el indicador en la linea 3
              lcd.write(1);
              encoderSW_bounce.update();              //Actualizamos pulsador
              if (encoderSW_bounce.fallingEdge()){    //Si pulsamos entramos a ajustar la hora
                set_alarma();                         //Funcion de ajuste de alarma
                salir = true ;                        //Una vez ajustada salimos del menu
                lcd.clear();
                menu=1;                               //Volvemos al menu RELOJ
                lastmenu = 0;
              }
            break;
            case 4:                 //Reiniciar Maximo Minimos
              lcd.setCursor(0,0);   //Situamos el indicador en la linea 0
              lcd.write(1);
              encoderSW_bounce.update();            //Actualizamos pulsador
              if (encoderSW_bounce.risingEdge()){   //Si reiniciamos valores
                lcd.clear();
                lcd.setCursor(6,2);
                lcd.print("BORRANDO");
                reset();            //Reiniciamos valores
                lcd.clear();
                lcd.setCursor(4,2);
                lcd.print("ACTUALIZANDO");  //Esperamos hasta recibir nuevos valores
                tiempo_res=millis();
                while (tiempo_res+5000 > millis() )
                comunicacion();
                salir = true;     //Seteamos variable salir a TRUE (Salimos del while)
                lcd.clear();      //Limpiamos pantalla
                menu=1;           //Seteamos menu a 1 (Volvemos a la pantalla reloj)
                lastmenu = 0;
              }
            break;
            case 5:                 //Ajustar tiempo de pantalla encendida
              lcd.setCursor(0,1);   //Situamos el indicador en la linea 1
              lcd.write(1);
              encoderSW_bounce.update();            //Actualizamos pulsador
              if (encoderSW_bounce.risingEdge()){   //Si pulsamos salimos de los menus
                time_lcd_on();
                salir = true ;                        //Una vez ajustada salimos del menu
                lcd.clear();
                menu=1;                               //Volvemos al menu RELOJ
                lastmenu = 0;
              }
            break;
            case 6:                 //SALIR
              lcd.setCursor(0,2);   //Situamos el indicador en la linea 1
              lcd.write(1);
              encoderSW_bounce.update();            //Actualizamos pulsador
              if (encoderSW_bounce.risingEdge()){   //Si pulsamos salimos de los menus
                salir = true;     //Seteamos variable salir a TRUE (Salimos del while)
                lcd.clear();      //Limpiamos pantalla
                menu=1;           //Seteamos menu a 1 (Volvemos a la pantalla reloj)
                lastmenu = 0;
              }
            break;
          }
          func_time_out();
          if (datos_ok == false){
            salir = true;                      //Si para el tiempo sin setear salimos
            datos_ok = true;
            lcd.clear();      //Limpiamos pantalla
            menu=1;           //Seteamos menu a 1 (Volvemos a la pantalla reloj)
            lastmenu = 0;
          }
        }
      }
    break;
    case 4:                       //Opcion 4 INFORMACION
      lcd.setCursor(4,0);
      lcd.print("Informacion");
      comunicacion();   //Recibimos datos
      fun_alarma();     //Comprobamos alarma
      temp_rh();        //Leemos temperatura interior
      error();          //Comprobamos si hay errores
      prediccion();     //Mostramos la prediccion
      //Imprimimos los datos
      if (tempin < -300 || rhin < -300){  //Si detectamos que los valores no son correctos mostramos error
        lcd.setCursor(0,1);
        lcd.print("ERR DHT");
        borrar(7,19,1);                   //Limpiamos pantalla
      }else{                              //Si todo esta OK, mostramos la informacion
        lcd.setCursor(0,1);
        lcd.print("Int:");
        lcd.print(tempin,1);
        lcd.print("C - ");
        lcd.print(rhin,1);
        lcd.print("%");
        borrar(17,19,1);
      }
      onbacklight();
      func_time_out();              //Temporizacion luz LCD
    break;
    }
}

//FUNCIONES+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//Interrupcion al cambio de A---------------------------------------------------------------------------------------------------------------
void doEncoderA(){
  //Comprobamos que haya cambiado
    if( digitalRead(encoderPinA) != A_set ) {  
    A_set = !A_set;
    // adjust counter + if A leads B
    if ( A_set && !B_set ){
      time_out = false;
      menu ++;
    }
    rotating = false; //Suprimible
  }
}

//Interrupcion al cambio de B---------------------------------------------------------------------------------------------------------------
void doEncoderB(){
  if( digitalRead(encoderPinB) != B_set ) {
    B_set = !B_set;
    //  adjust counter - 1 if B leads A
    if( B_set && !A_set ){
      time_out = false;
      menu --;
    }
    rotating = false; //Suprimible
  }
}

//Impresion de menu RELOJ-------------------------------------------------------------------------------------------------------------------
void printhour(){
  DateTime now = RTC.now();
  //Establecemos la hora
  lcd.setCursor(6,1);
  if(now.hour() >= 0 && now.hour() < 10){ //Si es mayor o igual que cero, y menor de 10, escribimos un cero a la izquierda
    lcd.print('0');
  }
  lcd.print(now.hour());
  lcd.print(':');
  if(now.minute() >= 0 && now.minute() < 10){ //Si es mayor o igual que cero, y menor de 10, escribimos un cero a la izquierda
    lcd.print('0');
  }
  lcd.print(now.minute());
  lcd.print(':');
  if(now.second() >= 0 && now.second() < 10){ //Si es mayor o igual que cero, y menor de 10, escribimos un cero a la izquierda
    lcd.print('0');
  }
  lcd.print(now.second());
  //Establecemos la fecha (Solo en caso de cambio del dia de la semana o cambio de menu)
  if (dia_semana != now.dayOfWeek() || cambio == true){
    cambio = false;
    dia_semana = now.dayOfWeek();
    //Imprimimos la fecha
    lcd.setCursor(10,2);
    if (now.date()<10) lcd.print('0');     //Si el dia es menor de 10 imprimimos cero a la izquierda
    lcd.print(now.date());
    lcd.print('/');
    if (now.month()<10) lcd.print('0');   //Si el mes es menor de 10 imprimimos cero a la izquierda
    lcd.print(now.month());
    lcd.print('/');
    lcd.print(now.year());
    lcd.setCursor(0,2);
    borrar(0,9,2);
    lcd.setCursor(0,2);
    lcd.print(diasemana[(now.dayOfWeek()-1)]);
    //Mostramos la posicion de la alarma
    lcd.setCursor(0,3);
    lcd.print("Alarma: ");
    if(hora_alarma >= 0 && hora_alarma < 10){ //Si es mayor o igual que cero, y menor de 10, escribimos un cero a la izquierda
      lcd.print('0');
    }
    lcd.print(hora_alarma);
    lcd.print(':');
    if(min_alarma >= 0 && min_alarma < 10){ //Si es mayor o igual que cero, y menor de 10, escribimos un cero a la izquierda
      lcd.print('0');
    }
    lcd.print(min_alarma);
    lcd.print(':');
    if(seg_alarma >= 0 && seg_alarma < 10){ //Si es mayor o igual que cero, y menor de 10, escribimos un cero a la izquierda
      lcd.print('0');
    }
    lcd.print(seg_alarma);
  }
}

//Imprimir hora al ajustarla----------------------------------------------------------------------------------------------------------------
void adj_print_hora(int fut_hora,int fut_min, int fut_seg){
  lcd.setCursor(0,3);
  if(fut_hora >= 0 && fut_hora  < 10){ //Si es mayor o igual que cero, y menor de 10, escribimos un cero a la izquierda
    lcd.print('0');
  }
  lcd.print(fut_hora);
  lcd.print(':');
  if(fut_min >= 0 && fut_min  < 10){ //Si es mayor o igual que cero, y menor de 10, escribimos un cero a la izquierda
    lcd.print('0');
  }
  lcd.print(fut_min);
  lcd.print(':');
  if(fut_seg >= 0 && fut_seg  < 10){ //Si es mayor o igual que cero, y menor de 10, escribimos un cero a la izquierda
    lcd.print('0');
  }
  lcd.print(fut_seg);
  }

//Imprimir fecha al ajustarla---------------------------------------------------------------------------------------------------------------
void adj_print_date(int fut_dia, int fut_mes, int fut_ano, int fut_diaw){
  lcd.setCursor(0,3);
  if(fut_dia < 10){   //Si el dia es menor que 10, ponemos 0 a la izquierda
    lcd.print('0');
  }
  lcd.print(fut_dia);
  lcd.print('/');
  if(fut_mes < 10){   //Si el mes es menor que 10 ponemos 0 a la izquierda
    lcd.print('0');
  }
  lcd.print(fut_mes);
  lcd.print('/');
  lcd.print(fut_ano);
  lcd.setCursor(11,3);
  lcd.print(diasemana[fut_diaw]);
}

//Establecer la hora------------------------------------------------------------------------------------------------------------------------
void set_hour(){
  DateTime now = RTC.now();
  //Variables para los menus
  boolean set_hora = false;
  boolean set_min = false;
  boolean set_seg = false;
  //Variables de seteo
  fut_hora = now.hour();
  fut_min = now.minute();
  fut_seg = now.second();
  //Variables de sobrepaso de tiempo
  temp_out=millis();
  datos_ok = true;
  //Menu ajustes de hora
  lcd.clear();
  lcd.setCursor(0,1);
  lcd.print("Set Hora:");
  adj_print_hora(fut_hora,fut_min,fut_seg); //Dibujamos la hora
  menu= 0 ;                                 //Inicializamos variables para ENCODER
  lastmenu = 0;
  while (set_hora == false){                //Mientras no pulsemos ajustamos la hora
    lcd.setCursor(0,2);                       //Apuntamos para saber que cambiamos
    lcd.write(8);
    lcd.write(8);
    func_time_out();
    if (datos_ok == false) set_hora = true;  //Si para el tiempo sin setear salimos
    if(menu > lastmenu){                    //Si menu es mayor que last menu sumamos valor
      fut_hora++;
      fut_hora=constrain(fut_hora,0,23);    //Limite de variable 0-23
      lastmenu=menu;
      }else if (menu < lastmenu){             //Si menu es menor que last menu restamos valor
        fut_hora--;
        fut_hora=constrain(fut_hora,0,23);    //Limite de variable 0-23
        lastmenu=menu;
      }else if (menu == lastmenu){            //Si nada cambia mantenemos
        fut_hora=fut_hora;
    }
    adj_print_hora(fut_hora,fut_min,fut_seg); //Dibujamos nuevo valor de hora
    encoderSW_bounce.update();                //Consultamos si hemos pulsado
    if (encoderSW_bounce.fallingEdge()){      //Si pulsamos el boton salimos de ajustar hora y pasamos a minutos
      set_hora = true ;
      borrar(0,2,2);                          //Borramos apuntador
    }
  }
  menu= 0 ;                                   //Inicializamos variables ENCODER
  lastmenu = 0;
  while (set_min == false){                   //Mientras no pulsemos mantenemos en menu minutos
    lcd.setCursor(3,2);                       //Apuntamos para saber que cambiamos
    lcd.write(8);
    lcd.write(8);
    func_time_out();
    if (datos_ok == false) set_min = true;  //Si para el tiempo sin setear salimos
    if(menu > lastmenu){                      //Si menu es mayor que lastmenu sumamos valor
      fut_min++;
      fut_min=constrain(fut_min,0,59);        //Limite variable 0-59
      lastmenu=menu;
      }else if (menu < lastmenu){             //Si menu es menor que lastmenu restamos valor
        fut_min--;
        fut_min=constrain(fut_min,0,59);      //Limite variable 0-59
        lastmenu=menu;                        
      }else if (menu == lastmenu){            //Si nada cambia mantenemos
        fut_min=fut_min;
    }
    adj_print_hora(fut_hora,fut_min,fut_seg); //Dibujamos nuevo valor de minutos
    encoderSW_bounce.update();                //Consultamos si hemos pulsado
    if (encoderSW_bounce.fallingEdge()){      //Si pulsamos el boton salimos de ajustar minutos y pasamos a segundos
      set_min = true ;
      borrar(3,4,2);                          //Borramos apuntador
    }
  }
  menu= 0 ;                                   //Inicializamos variables ENCODER
  lastmenu = 0;
  while (set_seg == false){                   //Mientras no pulsemos mantenemos en menu segundos
    lcd.setCursor(6,2);                       //Apuntamos para saber que cambiamos
    lcd.write(8);
    lcd.write(8);
    func_time_out();
    if (datos_ok == false) set_seg = true;  //Si para el tiempo sin setear salimos
    if(menu > lastmenu){                      //Si menu es mayor que lastmenu sumamos valor
      fut_seg++;
      fut_seg=constrain(fut_seg,0,59);        //Limite variable 0-59
      lastmenu=menu;                          
      }else if (menu < lastmenu){             //Si menu es menor que lastmenu restamos valor
        fut_seg--;          
        fut_seg=constrain(fut_seg,0,59);      //Limite variable 0-59
        lastmenu=menu;
      }else if (menu == lastmenu){            //Si nada cambia mantenemos
        fut_seg=fut_seg;
    }
    adj_print_hora(fut_hora,fut_min,fut_seg); //Dibujamos nuevo valor de segundos
    encoderSW_bounce.update();                //Consultamos si hemos pulsado
    if (encoderSW_bounce.fallingEdge()){      //Si pulsamos el boton salimos de ajustar segundos
      set_seg = true ;
    }
  }
  if (datos_ok == true){
    RTC.adjust(DateTime(now.year(),now.month(), now.date(), fut_hora, fut_min, fut_seg, now.dayOfWeek()));  //Introducimos la hora en el DS3231
  }else{
    datos_ok = true;
  }
}

//Funcion Ajustar Fecha---------------------------------------------------------------------------------------------------------------------
void set_date(){
  DateTime now = RTC.now();
  //Variables para los menus
  boolean set_ano = false;
  boolean set_mes = false;
  boolean set_dia = false;
  boolean set_diaw = false;
  //Variables de seteo
  fut_ano = now.year();
  fut_mes = now.month();
  fut_dia = now.date();
  fut_diaw= (now.dayOfWeek()-1);
  //Reiniciamos variables sobrepaso de tiempo
  temp_out=millis();
  datos_ok = true;
  //Menu ajustes de hora
  lcd.clear();
  lcd.setCursor(0,1);
  lcd.print("Set Fecha:");
  adj_print_date(fut_dia,fut_mes,fut_ano, fut_diaw);    //Dibujamos la fecha
  menu= 0 ;                                   //Inicializamos variables para ENCODER
  lastmenu = 0;
  while (set_dia == false){                   //Mientras no pulsemos mantenemos en menu ajustar dia
    lcd.setCursor(0,2);                       //Apuntamos para saber que cambiamos
    lcd.write(8);
    lcd.write(8);
    func_time_out();
    if (datos_ok == false) set_dia = true;    //Si para el tiempo sin setear salimos
    if(menu > lastmenu){                      //Si menu es mayor que lastmenu sumamos valor
        fut_dia++;
        fut_dia=constrain(fut_dia,1,31);        //Limite variable 1-31
        lastmenu=menu;
      }else if (menu < lastmenu){               //Si menu es menor que lastmenu restamos valor
        fut_dia--;
        fut_dia=constrain(fut_dia,1,31);        //Limite variable 1-31
        lastmenu=menu;
      }else if (menu == lastmenu){              //Si nada cambia mantenemos
        fut_dia=fut_dia;
    }
    adj_print_date(fut_dia,fut_mes,fut_ano,fut_diaw);  //Dibujamos nuevo valor de dia
    encoderSW_bounce.update();                //Consultamos si hemos pulsado
    if (encoderSW_bounce.fallingEdge()){      //Si pulsamos pasamos a ajustar los meses
      set_dia = true;
      borrar(0,2,2);
    }
  }
  menu= 0 ;                                   //Inicializamos variables para encoder
  lastmenu = 0;        
  while (set_mes == false){                   //Mientras no pulsemos mantenemos ajustando mes
    lcd.setCursor(3,2);                       //Apuntamos para saber que cambiamos
    lcd.write(8);
    lcd.write(8);
    func_time_out();
    if (datos_ok == false) set_mes = true;  //Si para el tiempo sin setear salimos
    if(menu > lastmenu){                      //Si menu es mayor que lastmenu sumamos valor
      fut_mes++;
      fut_mes=constrain(fut_mes,1,12);        //Limite variable 1-12
      lastmenu=menu;            
      }else if (menu < lastmenu){               //Si menu es menor que lastmenu restamos valor
        fut_mes--;
        fut_mes=constrain(fut_mes,1,12);        //Limite variable 1-12
        lastmenu=menu;
      }else if (menu == lastmenu){              //Si nada cambia mantenemos
        fut_mes=fut_mes;
    }
    adj_print_date(fut_dia,fut_mes,fut_ano,fut_diaw);  //Imprimos nuevo valor de mes
    encoderSW_bounce.update();                //Consultamos si hemos pulsado
    if (encoderSW_bounce.fallingEdge()){      //Si pulsamos pasamos a ajustar el año
      set_mes = true ;
      borrar(3,4,2);                          //Borramos apuntador
    }
  }
  menu= 0 ;                                   //Incializamos variables para encoder
  lastmenu = 0;                      
  while (set_ano == false){                   //Mientras no pulsemos mantenemos ajustando año
    lcd.setCursor(6,2);                       //Apuntamos para saber que cambiamos
    lcd.write(8);
    lcd.write(8);
    lcd.write(8);
    lcd.write(8);
    func_time_out();
    if (datos_ok == false) set_ano = true;  //Si para el tiempo sin setear salimos
    if(menu > lastmenu){                      //Si menu es mayor que lastmenu sumamos valor
      fut_ano++;
      fut_ano=constrain(fut_ano,2000,2099);   //Limitamos variable 2000-2099
      lastmenu=menu;                                
    }else if (menu < lastmenu){               //Si menu es menor que lastmenu restamos valor
      fut_ano--;                            
      fut_ano=constrain(fut_ano,2000,2099);   //Limitamos variable 2000-2099
      lastmenu=menu;              
    }else if (menu == lastmenu){              //Si nada cambia mantenemos
      fut_ano=fut_ano;
    }
    adj_print_date(fut_dia,fut_mes,fut_ano,fut_diaw);  //Imprimimos nuevo valor de año
    encoderSW_bounce.update();                //Consultamos si hemos pulsado
    if (encoderSW_bounce.fallingEdge()){      //Si hemos pulsado salirmos de ajustar año
      set_ano = true;
      borrar(6,9,2);                          //Borramos apuntador
    }
  }
  menu= 0 ;                                   //Incializamos variables para encoder
  lastmenu = 0;                      
  while (set_diaw == false){                   //Mientras no pulsemos mantenemos ajustando año
    lcd.setCursor(11,2);                      //Apuntamos para saber que cambiamos
    lcd.write(8);
    func_time_out();
    if (datos_ok == false) set_diaw = true;  //Si para el tiempo sin setear salimos
    if(menu > lastmenu){                      //Si menu es mayor que lastmenu sumamos valor
      fut_diaw++;
      fut_diaw=constrain(fut_diaw,0,6);   //Limitamos variable 0-6
      lastmenu=menu;              
      lcd.setCursor(11,3);                //Limpiamos para evitar superponer letras en los dias de la semana
      lcd.print("         ");                
    }else if (menu < lastmenu){               //Si menu es menor que lastmenu restamos valor
      fut_diaw--;                            
      fut_diaw=constrain(fut_diaw,0,6);   //Limitamos variable 0-6
      lastmenu=menu;              
      lcd.setCursor(11,3);                //Limpiamos para evitar superponer letras en los dias de la semana
      lcd.print("         ");
    }else if (menu == lastmenu){              //Si nada cambia mantenemos
      fut_diaw=fut_diaw;
    }
    adj_print_date(fut_dia,fut_mes,fut_ano,fut_diaw);  //Imprimimos nuevo valor de año
    encoderSW_bounce.update();                //Consultamos si hemos pulsado
    if (encoderSW_bounce.fallingEdge()){      //Si hemos pulsado salirmos de ajustar año
      set_diaw = true;
    }
  }
  fut_diaw=fut_diaw+1;
  if (datos_ok == true){
    RTC.adjust(DateTime(fut_ano, fut_mes, fut_dia, now.hour(), now.minute(), now.second(), fut_diaw));   //Introducimos fecha en DS1307
  }else{
    datos_ok = true;
  }
}

//Funcion Guardar Alarma (EEPROM)----------------------------------------------------------------------------------------------------------
void save_alarma(int fut_hora_alarma,int fut_min_alarma,int fut_seg_alarma, int alarma_on){
  //Leemos los valores guardados
  int savehora=EEPROM.read(1);
  int savemin=EEPROM.read(2);
  int saveseg=EEPROM.read(3);
  int saveseton=EEPROM.read(4);
  if (savehora!=hora_alarma)  //Si las horas guardadas no coinciden con las nuevas volvemos a guardar
  {
    EEPROM.write(1,hora_alarma); //Guardamos
  }
  if (savemin!=min_alarma)  //Si los minutos guardados no coinciden con los nuevos volvemos a guardar
  {
    EEPROM.write(2,min_alarma); //Guardamos
  }
  if (saveseg!=seg_alarma)  //Si los segundos guardados no coinciden con los nuevos volvemos a guardar
  {
    EEPROM.write(3,seg_alarma); //Guardamos
  }
  if (saveseton!=alarma_on)     //SI cambia el estado de la alarma guardamos
  {
    EEPROM.write(4,alarma_on);  //Guardamos
  }
}

//Funcion Setear Alarma--------------------------------------------------------------------------------------------------------------------
void set_alarma(){
  //Variables para los menus
  boolean set_hora_alarma = false;
  boolean set_min_alarma = false;
  boolean set_seg_alarma = false;
  boolean set_alarma_on = false;
  //Seteamos variables futuras
  fut_hora_alarma = hora_alarma;
  fut_min_alarma = min_alarma;
  fut_seg_alarma = seg_alarma;
  fut_alarma_on = alarma_on;
  //Seteamos variables sobrepaso de tiempo
  temp_out=millis();
  datos_ok = true;
  //Menu ajustes de hora
  lcd.clear();
  lcd.setCursor(0,1);
  lcd.print("Set Alarma:");
  adj_print_hora(fut_hora_alarma,fut_min_alarma,fut_seg_alarma);  //Dibujamos los valores que tengamos guardados
  if (fut_alarma_on == 1){      //Dibujamos el estado actual de la alarma, ACTIVADA
    lcd.setCursor(15,3);
    lcd.print("ON ");      
    }else{                  //DESACTIVADA
      lcd.setCursor(15,3);
      lcd.print("OFF");
  }
  menu= 0 ;                             //Inicializamos variables ENCODER
  lastmenu = 0;
  while (set_hora_alarma == false){     //Mientras no pulsemos mantenemos en ajustar hora
    lcd.setCursor(0,2);                       //Apuntamos para saber que cambiamos
    lcd.write(8);
    lcd.write(8);
    func_time_out();
    if (datos_ok == false) set_hora_alarma = true;  //Si para el tiempo sin setear salimos
    if(menu > lastmenu){                //Si menu es mayor que lastmenu sumamos valor a la variable
      fut_hora_alarma++;                                
      fut_hora_alarma=constrain(fut_hora_alarma,0,23);  //Limite variable 0-23
      lastmenu=menu;
    }else if (menu < lastmenu){         //Si menu es menor que lastmenu restamos valor a la variable
      fut_hora_alarma--;                                
      fut_hora_alarma=constrain(fut_hora_alarma,0,23);  //Limite variable 0-23
      lastmenu=menu;
    }else if (menu == lastmenu){        //Si nada cambia mantenemos
      fut_hora_alarma=fut_hora_alarma;                                
    }
    adj_print_hora(fut_hora_alarma,fut_min_alarma,fut_seg_alarma);  //Dibujamos el nuevo valor de la hora
    encoderSW_bounce.update();                                      //Consultamos si hemos pulsado
    if (encoderSW_bounce.fallingEdge()){                            //Si hemos pulsado pasamos a menu ajuste minutos
      set_hora_alarma = true ;
      borrar(0,2,2);                          //Borramos apuntador
    }
  }
  menu= 0 ;                             //Inicializamos variables ENCODER
  lastmenu = 0;
  while (set_min_alarma == false){      //Mientras no pulsemos mantenemos en ajustar hora
    lcd.setCursor(3,2);                       //Apuntamos para saber que cambiamos
    lcd.write(8);
    lcd.write(8);
    func_time_out();
    if (datos_ok == false) set_min_alarma = true;  //Si para el tiempo sin setear salimos
    if(menu > lastmenu){                //Si menu es mayor que lastmenu sumamos valor
      fut_min_alarma++;  
      fut_min_alarma=constrain(fut_min_alarma,0,59);  //Limite varianle 0-59
      lastmenu=menu;
      }else if (menu < lastmenu){         //Si menu es menor que lastmenu restamos valor
        fut_min_alarma--;
        fut_min_alarma=constrain(fut_min_alarma,0,59);  //Limite variable 0-59
        lastmenu=menu;
      }else if (menu == lastmenu){        //Si nada cambia mantenemos
        fut_min_alarma=fut_min_alarma;
    }
    adj_print_hora(fut_hora_alarma,fut_min_alarma,fut_seg_alarma);  //Dibujamos nuevo valor de minutos
    encoderSW_bounce.update();                                      //Consultamos si hemos pulsado
    if (encoderSW_bounce.fallingEdge()){                            //Si hemos pulsado pasamos a menu ajuste segundos
      set_min_alarma = true ;
      borrar(3,4,2);                          //Borramos apuntador
    }
  }
  menu= 0 ;                             //Inicializamos variables ENCODER
  lastmenu = 0;
  while (set_seg_alarma == false){      //Mientras no pulsemos mantenemos en ajustar segundos
    lcd.setCursor(6,2);                       //Apuntamos para saber que cambiamos
    lcd.write(8);
    lcd.write(8);
    func_time_out();
    if (datos_ok == false) set_seg_alarma = true;  //Si para el tiempo sin setear salimos
    if(menu > lastmenu){                //Si menu es mayor que lastmenu sumamos valor
      fut_seg_alarma++;
      fut_seg_alarma=constrain(fut_seg_alarma,0,59);  //Limite variable 0-59
      lastmenu=menu;
      }else if (menu < lastmenu){        //Si menu es menor que lastmenu restamos valor
        fut_seg_alarma--;
        fut_seg_alarma=constrain(fut_seg_alarma,0,59);  //Limite variable 0-59
        lastmenu=menu;
      }else if (menu == lastmenu){       //Si nada cambia mantenemos
        fut_seg_alarma=fut_seg_alarma;
    }
    adj_print_hora(fut_hora_alarma,fut_min_alarma,fut_seg_alarma);  //Dibujamos nuevo valor de segundos
    encoderSW_bounce.update();                                      //COnsultamos si hemos pulsado
    if (encoderSW_bounce.fallingEdge()){                            //Si hemos pulsado, pasamos a menu activar/descativar alarma
      set_seg_alarma = true ;
      borrar(6,8,2);
    }
  }
  menu= 0 ;                              //Inicializamos variables ENCODER
  lastmenu = 0;
  while ( set_alarma_on == false){      //Mientras no pulsemos mantenemos en el menu de activacion
    func_time_out();
    if (datos_ok == false) set_alarma_on = true;  //Si para el tiempo sin setear salimos
    if(menu > lastmenu){                  //Si menu mayor que lastmenu sumamos valor
        fut_alarma_on++;
        fut_alarma_on=constrain(fut_alarma_on,0,1); //Limite 0-1
        lastmenu=menu;
        }else if (menu < lastmenu){         //Si menu es menor que lastmenu restamos valor
          fut_alarma_on--;
          fut_alarma_on=constrain(fut_alarma_on,0,1); //Limite 0-1
          lastmenu=menu;
        }else if (menu == lastmenu){        //Si nada cambia mantenemos
          fut_alarma_on=fut_alarma_on;
      }
    //Si alarma_ON es 1 dibujamos ON, de lo contrario dibujamos OFF
    if (fut_alarma_on == 1){
      lcd.setCursor(15,3);
      lcd.print("ON ");
      }else{
        lcd.setCursor(15,3);
        lcd.print("OFF");
      }
      encoderSW_bounce.update();
      if (encoderSW_bounce.fallingEdge()){
        set_alarma_on = true ;
      }
    }
  if (datos_ok == true){                  //Si todo esta correcto, guardamos
    hora_alarma = fut_hora_alarma;
    min_alarma = fut_min_alarma;
    seg_alarma = fut_seg_alarma;
    alarma_on = fut_alarma_on;
    save_alarma(hora_alarma,min_alarma,seg_alarma,alarma_on); //Llamamos a la funcion para guardar en la EEPROM
  }else{
    datos_ok = true;
  }
}

//Borrar cursores--------------------------------------------------------------------------------------------------------------------------
//Cada vez que cambia el cursor de posicion en el menu de opciones, limpiamos el anterior
void delete_cursor(int posicion){
  if (posicion < 3){
    lcd.setCursor(0,posicion);
    lcd.print(" ");
  }else{
    lcd.setCursor(0,posicion-4);
    lcd.print(" ");
  }
}
//Dibujar margen menu----------------------------------------------------------------------------------------------------------------------
//Cada vez que cambiamos de menu, volvemos a dibujar la linea de margen superior
void print_margen(){
  lcd.setCursor(0,0);
  for (int x = 0; x<=19; x++){
    lcd.write(4);
  }
}

//Funcion para alarma----------------------------------------------------------------------------------------------------------------------
void fun_alarma(){
  DateTime now = RTC.now();
  //Si coinciden horas minutos y segundos, a su vez que alarma_on esta TRUE, activamos la alarma
  if (hora_alarma == now.hour() && min_alarma == now.minute() && seg_alarma == now.second() && alarma_on == 1){
    ok_alarma = true;   //Alarma encendida
    temp_out = millis();
    lcd.backlight();                    //Encendemos la luz
    back = true;
    digitalWrite(pin_alarma, HIGH);     //Activamos pin de alarma
    off_alarma = millis();          
  }
  if (off_alarma+120000 < millis() && ok_alarma == true){
    ok_alarma = false;                  //Si lleva sonando la alarma mas de 2 minutos paramos automaticamente
    digitalWrite(pin_alarma, LOW);
  }
  pulsador_bounce.update();             //Comprobamos si hemos pulsado el boton
  if (pulsador_bounce.risingEdge()){    //Si hemos pulsado paramos la alarma
    temp_out = millis();
    lcd.backlight();
    back = true;
    ok_alarma = false;
    digitalWrite(pin_alarma, LOW);                            
  }
  if (ok_alarma){         //Si tenemos sonando la alarma mostramos un icono
      lcd.setCursor(18,0);
      lcd.write(3);
      lcd.write(5);
  }else{                  //Sino dibujamos el guion
    lcd.setCursor(18,0);
    lcd.write(4);
    lcd.write(4);
  }
  if (alarma_on == 1){    //Si tenemos la alarma activa mostramos un icono
      lcd.setCursor(17,0);
      lcd.write(2);
  }else{                  //Sino dibujamos el guion
    lcd.setCursor(17,0);
    lcd.write(4);
  }
}

//Funcion de comunicacion------------------------------------------------------------------------------------------------------------------
void comunicacion(){
  //Si tenemos radio aviable, leemos los datos
  if (radio.available()){
      radio.read( &Datos, sizeof(Datos) );
    }
}

//Funcion reset de valores MAX/MIN---------------------------------------------------------------------------------------------------------
void reset(){
  //Mandamos los datos durante 3 segundos, para asegurar su correcta recepcion
  tiempo_res=millis();
  radio.stopListening();
  while (tiempo_res+3000 > millis())
  radio.write( &reinicio, sizeof(reinicio) );
  radio.startListening(); //Volvemos a ponernos en modo escucha
}

//Funcion de adaptacion de valores---------------------------------------------------------------------------------------------------------
void adaptacion(){
  //Dada la necesidad de marcar un limite de payload de 32 bits, nos vemos obligados a mandar los datos "codificados", en esta funcion los descodificamos
  temp = Datos.temp;
  rh = Datos.rh;
  maxtemp = Datos.maxtemp;
  maxrh = Datos.maxrh;
  mintemp = Datos.mintemp;
  minrh = Datos.minrh;
  pa = Datos.pa;
  maxpa = Datos.maxpa;
  minpa = Datos.minpa;
  temp /=10;
  rh /=10;
  maxtemp /=10;
  maxrh /=10;
  mintemp /=10;
  minrh /=10;
  pa *=0.01;
  maxpa *= 0.01;
  minpa *= 0.01;
}

//Funcion de lectura temperatura interior--------------------------------------------------------------------------------------------------
void temp_rh(){
  if (inicio == true){  //En caso de primer inicio, ponemos a cero el contador de tiempo
    time_past = millis();
    inicio = false;
    }
    if (time_past + 5000 < millis()){ //Leemos cada cinco segundos
    DHT.read22(DHT22_PIN);
    time_past = millis();
    }
  tempin = DHT.temperature;
  rhin = DHT.humidity;
}

//Funcion de errores-----------------------------------------------------------------------------------------------------------------------
void error(){
  switch (Datos.error){
    case 0: //Todo correcto
      lcd.setCursor(0,3);
      lcd.print("Running");
      break;
    case 1: //Error de sensor luz
      lcd.setCursor(0,3);
      lcd.print("Err L  ");
      break;
    case 2: //Error sensor presion
      lcd.setCursor(0,3);
      lcd.print("Err P  ");
      break;
    case 3: //Error sensor temperatura
      lcd.setCursor(0,3);
      lcd.print("Err T  ");
      break;
    case 4: //Error sensores luz y presion
      lcd.setCursor(0,3);
      lcd.print("Err L/P");
      break;
    case 5: //Error sensores luz y temperatura
      lcd.setCursor(0,3);
      lcd.print("Err L/T");
      break;
    case 6: //Error sensores presion y temperatura
      lcd.setCursor(0,3);
      lcd.print("Err P/T");
      break;
    case 7: //Error total
      lcd.setCursor(0,3);
      lcd.print("ERR ALL");
      break;
  }
}

//Funcion de Prediccion--------------------------------------------------------------------------------------------------------------------
void prediccion(){
  switch (Datos.pred){
    case 0: //A la espera de tener datos para el calculo
    lcd.setCursor(0,2);
    lcd.print("Datos-");
    lcd.write(6);
    lcd.write(6);
    lcd.write(6);
    lcd.write(6);
    lcd.write(6);
    lcd.write(6);
    break;
    case 1:
    lcd.setCursor(0,2);
    lcd.print("Datos-");
    lcd.write(7);
    lcd.write(6);
    lcd.write(6);
    lcd.write(6);
    lcd.write(6);
    lcd.write(6);
    break;
    case 2:
    lcd.setCursor(0,2);
    lcd.print("Datos-");
    lcd.write(7);
    lcd.write(7);
    lcd.write(6);
    lcd.write(6);
    lcd.write(6);
    lcd.write(6);
    break;
    case 3:
    lcd.setCursor(0,2);
    lcd.print("Datos-");
    lcd.write(7);
    lcd.write(7);
    lcd.write(7);
    lcd.write(6);
    lcd.write(6);
    lcd.write(6);
    break;
    case 4:
    lcd.setCursor(0,2);
    lcd.print("Datos-");
    lcd.write(7);
    lcd.write(7);
    lcd.write(7);
    lcd.write(7);
    lcd.write(6);
    lcd.write(6);
    break;
    case 5:
    lcd.setCursor(0,2);
    lcd.print("Datos-");
    lcd.write(7);
    lcd.write(7);
    lcd.write(7);
    lcd.write(7);
    lcd.write(7);
    lcd.write(6);
    break;
    case 6: //Si la pendiente es negativa prediccion de borrasca
    lcd.setCursor(0,2);
    borrar(8,15,2);
    lcd.setCursor(0,2);
    lcd.print("Borrasca");
    break;
    case 7: //Si la pendiente es positiva prediccion de anticiclon
    lcd.setCursor(0,2);
    borrar(10,15,2);
    lcd.setCursor(0,2);
    lcd.print("Anticiclon");
  }
}

//Funcion apagar retroiluminacion----------------------------------------------------------------------------------------------------------
void onbacklight(){
  encoderSW_bounce.update();    //Actualizamos situacion del pulsador
    if (encoderSW_bounce.fallingEdge()){ //Si pulsamos apagamos retroiluminacion
      temp_out = millis();
      if (back==true){
        lcd.noBacklight();
        back=false;
      }else{
        lcd.backlight();
        back=true;
      }
    }
}

//Funcion borrado Caracteres---------------------------------------------------------------------------------------------------------------
void borrar(int first, int last, int row){
  for (first ; first < last+1 ; first++){
    lcd.setCursor(first,row);
    lcd.print(" ");
  }
}

//Funcion para la inactividad--------------------------------------------------------------------------------------------------------------
void func_time_out(){
  if (time_out == false){     //Comprobamos si hemos movido el encoder
    temp_out = millis();      //Reiniciamos la variable de tiempo
    time_out = true;
  }
  if ((temp_out+10000) < millis()){ //Esperamos 10 segundos de inactividad
    datos_ok = false;               //Seteamos para salir
  }
  if (luz_activated == true){       //En caso de tener activada la temporizacion
    if ((temp_out+temp_margen) < millis() ){  //Esperamos el tiempo seteado
      lcd.noBacklight();            //Apagamos la iluminacion
      back = false;                 //Seteamos como apagada
    }
  }
}

//Funcion para setear el tiempo de luz encendida-------------------------------------------------------------------------------------------
void time_lcd_on(){
  //Variable seteo menu
  boolean set_luz = false;
  //Mostramos info en el menu
  lcd.clear();
  lcd.setCursor(0,1);
  lcd.print("Tiempo:");
  menu= 0 ;                                   //Inicializamos variables para ENCODER
  lastmenu = 0;
  while (set_luz == false){                   //Mientras no pulsemos mantenemos en menu ajustar tiempo
    func_time_out();
    if (datos_ok == false) set_luz = true;    //Si para el tiempo sin setear salimos
    if(menu > lastmenu){                      //Si menu es mayor que lastmenu sumamos valor
        fut_luz++;
        fut_luz=constrain(fut_luz,0,15);        //Limite variable 0-15
        lastmenu=menu;
      }else if (menu < lastmenu){               //Si menu es menor que lastmenu restamos valor
        fut_luz--;
        fut_luz=constrain(fut_luz,0,15);        //Limite variable 0-15
        lastmenu=menu;
      }else if (menu == lastmenu){              //Si nada cambia mantenemos
        fut_luz=fut_luz;
    }
    if (fut_luz == 0){                          //Si el tiempo es 0, tenemos que no se apaga nunca, siempre ON
      borrar(14,17,1);
      lcd.setCursor(12,1);
      lcd.print("ON");                          //Mostramos ON
    }else{                                      //Si el tiempo no es 0, mostramos que tiempo tenemos
      lcd.setCursor(12,1);                
      if (fut_luz < 10) lcd.print("0");         //Si es menor de 10, ponemos un 0 a la izquierda
      lcd.print(fut_luz);
      lcd.print("min");
    }
    encoderSW_bounce.update();                //Consultamos si hemos pulsado
    if (encoderSW_bounce.fallingEdge()){      //Si pulsamos el boton salimos de ajustar luz
      set_luz = true ;                        //Seteamos para finalizar
    }
  }
  if (datos_ok == true){                      //Si no pasamos del tiempo de inactividad
    temp_margen = fut_luz*60000;              //Pasamos el tiempo a milisegundos
    if (fut_luz > 0){                         //Si es mayor que 0, tenemos que esta activado
    luz_activated = true;
    }else{                                    //Si es menor que 0, tenemos que esta desactivado
    luz_activated = false;
    }
  }else{
    datos_ok = true;
  }
  set_luz = false;
}
[/spoiler]
Citar
#6
Estacion Exterior

[spoiler]
Código:
//Librerias
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Wire.h>
#include <Adafruit_BMP085.h>
#include <BH1750.h>
#include <dht.h>

//Inicializacion DHT22
dht DHT;        //Declaracion del objeto
#define DHT22_PIN 5 //Configuracion de pin

//Inicializacion Inalambrica
#define CE_PIN   9  //Configuracion de pines
#define CSN_PIN 10
const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; //Canales de transmision
RF24 radio(CE_PIN, CSN_PIN); // Creando la radio

//Inicializacion sensor LUZ
BH1750 lightMeter;

//Inicializacion sensor PRESION
Adafruit_BMP085 bmp;
uint8_t mode = BMP085_ULTRALOWPOWER;
//Estructura (Payload del NRF)
struct estacion {
  //Prediccion
  byte pred;
  //Error
  byte error;
  //Datos
  int temp;
  int rh;
  int lux;
  int32_t pa;
  //Maximos
  int maxtemp;
  int maxrh;
  int maxlux;
  int32_t maxpa;
  //Minimos
  int mintemp;
  int minrh;
  int minlux;
  int32_t minpa;
}Datos;

//Temporales de operacion
float temp = 0;
float rh = 0;
float maxtemp = 0;
float mintemp = 0;
float maxrh = 0;
float minrh = 0;

//Errores
boolean error_luz = false;
boolean error_presion = false;
boolean error_temprh = false;
int chk;

//Prediccion
int32_t presure[5];     //Array para los valores
unsigned long time_pred = 0;
boolean first_pred = true;
byte i = 0;

//Variables conteo de tiempo e inicializacion
unsigned long time_past;
unsigned long tiempo_pas = 0;
unsigned long tiempo_envio = 0;
boolean reinicio = true;
boolean inicio = true;
boolean initiatemaxmin = false;

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void setup() {
    //Iniciamos los sensores y comunicacion
  lightMeter.begin();
    bmp.begin(mode);
    radio.begin();
  radio.openWritingPipe(pipes[1]);
  radio.openReadingPipe(1,pipes[0]);
  radio.startListening();   //Comenzamos a escuchar
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void loop() {
  temp_rh();      //Medimos la temperatura
    luz();          //Medimos la luz
    presion();      //Medimos la presion
  prediccion();   //Calculamos para la prediccion
  reset();        //Consultamos si debemos reiniciar datos
  maxmin();       //Calculamos maximos y minimos
  error();        //Comprobamos si tenemos algun error en los sensores
  if (inicio == true){  //Si es la primera vez que iniciamos
    tiempo_envio = millis();
    inicio = false;
  }
  if (tiempo_envio+10000 < millis()){  //Enviamos cada 10 segundos (MODIFICABLE)
  comunicacion();
  tiempo_envio = millis();
  }
}

//FUNCIONES+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//Funcion leer temperatura y humedad--------------------------------------------------------------------------------------------------------
void temp_rh(){
  if (inicio == true){
    time_past = millis();
    }
    Serial.print("DHT22, \t");
    if (time_past + 5000 < millis()){
    chk =DHT.read22(DHT22_PIN);
    time_past = millis();
    error_temprh = chk /= 0 ? true : false ;
    }
  Datos.rh = DHT.humidity*10;
  Datos.temp = DHT.temperature*10;
  //Variables de operacion
  temp = DHT.temperature;
  rh = DHT.humidity;
}

//Funcion leer luz--------------------------------------------------------------------------------------------------------------------------
void luz(){
    Datos.lux=lightMeter.readLightLevel();     //Leemos el valor del sensor de luz
  error_luz = Datos.lux < 0 ? true : false ; //Comprobamos si la medida es correcta
}

//Funcion leer presion----------------------------------------------------------------------------------------------------------------------
void presion(){
    Datos.pa=bmp.readPressure();               //Leemos el sensor de presion
  error_presion = Datos.pa < 50000 ? true : false;  //Comprobamos si la medida es correcta
}

//Funcion comunicacion----------------------------------------------------------------------------------------------------------------------
void comunicacion(void){  
  tiempo_pas=millis();
  radio.stopListening();  //Paramos de escuchar para pasar a emitir
  while (tiempo_pas+500 > millis())  //Enviamos durante 1.5seg para asegurar la recepcion correcta
  radio.write( &Datos, sizeof(Datos) );
  radio.startListening(); //Pasamos a escuchar
}

//Funcion maximos/minimos-------------------------------------------------------------------------------------------------------------------
void maxmin(){
  //Inicializacion (Seteamos los valores sobre los que operaremos)
  if (reinicio == true && (temp != 0 && rh != 0)){
  maxtemp=temp;
  maxrh=rh;
  Datos.maxlux=Datos.lux;
  Datos.maxpa=Datos.pa;
  mintemp=temp;
  minrh=rh;
  Datos.minlux=Datos.lux;
  Datos.minpa=Datos.pa;
  reinicio = false;
  initiatemaxmin = true;
  }
  //Una vez inicializamos, calculamos
  if (initiatemaxmin == true){
  //Maximos
  if (maxtemp < temp) maxtemp=temp;
  if (maxrh < rh) maxrh=rh;
  if (Datos.maxlux < Datos.lux) Datos.maxlux=Datos.lux;
  if (Datos.maxpa < Datos.pa) Datos.maxpa=Datos.pa;
  //Minimos
  if (mintemp > temp) mintemp=temp;
  if (minrh > rh) minrh=rh;
  if (Datos.minlux > Datos.lux) Datos.minlux=Datos.lux;
  if (Datos.minpa > Datos.pa) Datos.minpa=Datos.pa;
  }
  //Convertimos a INT
  Datos.maxtemp = maxtemp*10;
  Datos.maxrh = maxrh*10;
  Datos.mintemp = mintemp*10;
  Datos.minrh = minrh*10;
}

//Funcion reinicio max/min-----------------------------------------------------------------------------------------------------------------
void reset(){
  if (radio.available()){
      radio.read( &reinicio, sizeof(reinicio) );
  }
}

//Funcion comprobacion de errores----------------------------------------------------------------------------------------------------------
void error(){
  // 0: Sin errores
  Datos.error = error_luz == false && error_presion == false && error_temprh == false ? 0 : Datos.error ;
  // 1: Error Sensor Luz
  Datos.error = error_luz == true ? 1 : Datos.error ;
  // 2: Error Sensor Presion
  Datos.error = error_presion == true ? 2 : Datos.error ;
  // 3: Error temp/rh
  Datos.error = error_temprh == true ? 3 : Datos.error ;
  // 4: Error Sensor Luz && Presion
  Datos.error = error_luz == true && error_presion == true ? 4 : Datos.error;
  // 5: Error Sensor Luz && Temp/rh
  Datos.error = error_luz == true && error_temprh == true ? 5 : Datos.error;
  // 6: Error sensor Pesion && Temp/rh
  Datos.error = error_temprh == true && error_presion == true ? 6 : Datos.error;
  // 7: Error todos sensores
  Datos.error = error_temprh == true && error_presion == true && error_luz == true ? 7 : Datos.error;
}

//Funcion prediccion-----------------------------------------------------------------------------------------------------------------------
void prediccion(){
  float a,b,c,d,m; //Variables de calculo
  if (inicio == true){
    presure[i]=Datos.pa;
    i=i+1;
    time_pred = millis();
  }
  if (time_pred+1800000 < millis()){  //Registramos datos cada media hora
  presure[i]=Datos.pa;
  i=i+1;
  time_pred = millis();
  }
  if (i == 6){  //Cuando tenemos 6 datos, calculamos
    a = 6*((1*presure[0])+(2*presure[1])+(3*presure[2])+(4*presure[3])+(5*presure[4])+(6*presure[5]));
    b = (720)*(presure[0]+presure[1]+presure[2]+presure[3]+presure[4]+presure[5]);
    c = 546;
    d = 441;
    m = (a-b)/(c-d);
    if (m < 0){
      Datos.pred = 6; //Si m menor que cero, BORRASCA
    }else{
      Datos.pred = 7; //Si m mayor que cero, ANTICICLON
    }
    i = 0;  //Reiniciamos cuenta de datos
    first_pred = false;
  }
  if (first_pred == true) Datos.pred = i; //Si es la primera vez que iniciamos la estacion, esperamos hasta tener los 6 datos.
}
[/spoiler]
Citar
#7
ME acaba de llegar el DHT11 y me queda comparar temperaturas con uno que sea comercial pero de momento no iba mal
Citar
#8
cansi22 escribió:ME acaba de llegar el DHT11 y me queda comparar temperaturas con uno que sea comercial pero de momento no iba mal

Espero esos resultados Gran sonrisa
Citar
#9
Todo mola más con GPS. Ponle un módulo GPS para tiempo de alta precisión y un ethernet shield para dar los datos por red (y ya que estás, un servidor NTP Gran sonrisa )
Los u-blox de portátil en formato mini-pcie son muy baratos y tienen salida serie en unos pads bastante accesibles.
Citar
#10
n0p escribió:Todo mola más con GPS. Ponle un módulo GPS para tiempo de alta precisión y un ethernet shield para dar los datos por red (y ya que estás, un servidor NTP Gran sonrisa )
Los u-blox de portátil en formato mini-pcie son muy baratos y tienen salida serie en unos pads bastante accesibles.

Para el nivel actual que controlo de arduino (Segundo Proyecto) Se me escapa un poco de las manos ese nivel jajajaja.

Por ahora ire probando los sensores y la comunicacion con los mismos, e ire poniendo los resultados por aqui
Citar
#11
La tasa de refresco no es algo que te vaya a importar mucho. No tienes que llevar un seguimiento muy continuo. Y por el rango tres cuartos de lo mismo, podrías usar hasta un LM35 o un DS18B20, ya que no vas a tener temperaturas muy extremas y su funcionamiento es más que bueno en ambos casos, quizás algo mejor el DS, pero para lo que es... Ahora ya la precisión que tu quieras será la que decida qué componentes pilar.

Si investigas algo sobres los sensores de gases, calidad del aire o similares, postealo, que yo tengo mis dudas con los MQ, xD.
Citar
#12
n0p escribió:Todo mola más con GPS. Ponle un módulo GPS para tiempo de alta precisión y un ethernet shield para dar los datos por red (y ya que estás, un servidor NTP Gran sonrisa )
Los u-blox de portátil en formato mini-pcie son muy baratos y tienen salida serie en unos pads bastante accesibles.


Eso ya es pasarse :elrisas: Hay que saber hasta donde llegar
Citar
#13
Grafi, ya sabes que aquí a la gente se le va de las manos rápidamente, xDDD.

Otra cosa, lo de conectarlo al móvil quizás puedas apañar algo con la app del compañero r0bert0: http://www.spainlabs.com/foro/viewtopic.php?f=9&t=505
Citar
#14
El de casa se actualiza cada minuto y este en el ejemplo cada medio segundo. No creo que cambie mucho la tempera tuda en 1 minuto
jajajaaj
Citar
#15
Lo que tengo en el blog es algo antiguo, un año ya, así que aunque esta bien se puede mejorar y mucho. Hace poco tenia intención de montar una estación 2.0 pero al final la he dejado estar por el momento ya que no he encontrado solución a los cuelgues de la shield de ethernet.
El caso es que los sensores ya los pille y he probado bastantes...

Para la humedad y la temperatura te recomiendo un DHT22, va muy bien y con un solo sensor ya tienes los dos datos. El DHT11 es muy rancio, el LM35 es rancio también, el DS18B20 esta bien pero con el DHT22 sobra.

Para la presión atmosférica yo he probado un BMP180 que es el modelo siguiente al que has puesto y se supone que es mejor, el problema es que se alimenta a 3.3V, el tuyo puedes elegir a 5V y 3.3V.
Lo he probado con una librería (creo que solo hay una para el mio) y te saca la presión, altitud y temperatura, aunque no sé si va muy bien porque la presión no coincide para nada con la del reloj (un casio de montañismo), y la altitud ni con la del reloj ni con la que hay en mi ciudad... eso lo tengo que investigar mas.

Para el sensor de luz tengo ese mismo que has puesto, parece ir bien aunque no tengo con que comparar xD

Los transmisores RF también van muy bien aunque es un coñazo realizar las conexiones ya que no puedes pincharlo directamente en la protoboard.

En vez del arduino nano yo quizás pondría mejor un arduino pro micro, tendrás mas memoria sram y es mas actual:
http://www.ebay.es/itm/Leonardo-Pro-Micr...232f240fb6

El sensor de dióxido de carbono aun no lo he probado.

El sensor de lluvia no sé como de bien ira, imagino que las gotas harán "corto" y según cuantas gotas haya y como tengas regulado la sensibilidad pues te dirá que esta lloviendo o no. La gracia si tienes espacio seria que te midiera la cantidad de lluvia pero es mas caro claro.

Si lo quieres complicar un poco mas puedes poner un lector de tarjetas SD y guardar los datos por si un dia quieres hacerte gráficas con el excel Confundidois1:


PD: Te dejo una lista de la compra que hice aunque básicamente son los componentes que has puesto ya http://goo.gl/InOAh5
giltesa.com Mi blog personal sobre informática, electrónica, Arduino, bricolaje, etc.
Citar
#16
Si sin duda, empleare el DHT22, luego el de presion sera probar...aver que resultados da, ya lo comentare por aqui, el tema es que la presion la da en Hectopascales, por tanto habra que pasarla a bares.

Los NRF funcionan a 3,3V tambien

El sensor de gases efectivamente seria el MQ7, que mide dioxido de carbono...etc etc que es lo que nos interesa

Y el sensor de lluvia, lo mismo todo sera cuestion de probarlo, estaria bien saber que cantidad de agua ha llovido, pero me parece mucho armatroste....
Citar
#17
El que es majo es este, pero no tengo sitio para montarlo al aire libre :/

https://www.sparkfun.com/products/8942
giltesa.com Mi blog personal sobre informática, electrónica, Arduino, bricolaje, etc.
Citar
#18
giltesa escribió:El que es majo es este, pero no tengo sitio para montarlo al aire libre :/

https://www.sparkfun.com/products/8942


Bufff, yo tampoco, mi idea es una cajita como la tuya nomas. Vivo en un piso y es lo que hay....jajajaj

Tendre problemas con el rango de los NRF? dentro de una caja pasando una ventana a unos 5 metros?
Citar
#19
Estaba mirando sensores de Gases, hay de muchos tipos, todos con la denominacion MQ, creo que el mas adecuado para mirar la calidad del aire es:

http://www.futurlec.com/Datasheet/Sensor/MQ-135.pdf

Ya que el MQ7 solo mide CO.

Echarle un ojo a :

http://playground.arduino.cc/Main/MQGasSensors
Citar
#20
Buena lista la del playground, no la había visto. Me la apunto.
Citar


Temas similares...
Tema Autor Respuestas Vistas Último mensaje
  Estación meteorológica v2 - giltesa giltesa 68 9,669 18-10-2014, 06:08 PM
Último mensaje: giltesa