This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm whether you accept or reject these cookies being set.

A cookie will be stored in your browser regardless of choice to prevent you being asked this question again. You will be able to change your cookie settings at any time using the link in the footer.

  • 1 voto(s) - 4 Media
  • 1
  • 2
  • 3
  • 4
  • 5
Estacion Meteorologica
#41
Jorge escribió:Respecto a los encoders: http://webdelcire.com/wordpress/archives/1545

Me lei hace tiempo ese articulo, esta mas que bien, es tuya la web verdad?, me podrias comentar si has implementado algun econder en programas largos? El resultado sigue siendo igual de favorable?

Bien es cierto que el encoder que yo tengo no es de muy buena calidad la verdad, y le dejo al arduino toda la labor de debounce, tendria que probar a usar los condensadores de filtrado y demas..., igual me compro ese de adafruit y lo pruebo.

El problema de no realizar un buen filtrado de las señales, me da a mi la impresion que hace que se activen muchas veces las interrupciones lo que demora al micro y causa estos problemas....es una suposicion vamos.

EDITO: Acabo de probar el encoder con el condensador de filtrado (100nf) y la resistencia en serie(10K), y me funciona a la perfeccion, parece ser que las rutinas no son suficientes para solucionar el problema del rebote, y trastornan al micro.....
  Responder
#42
Que un programa sea más o menos largo no debería ser un problema.

Por lo general el código de una interrupción tiene que ejecutarse lo más rápido posible, ya que si no se usan interrupciones anidadas ni con preferencia (por defecto en AVR una interrupción no puede interrumpir a otra) si estás atendiendo una interrupción y ocurre otra distinta, hasta que no se termina de ejecutar el código de la primera no se pasa al código de la segunda.

Por ejemplo, usando el delay de 200 ms que tienes para que te funcione, pon que además del encoder tienes una comunicación por usb, puerto serie, etc.. que usa una de las UART del microcontrolador y la recepción de datos es gestionada por una interrupción de esa UART.

Si estás en la interrupción del encoder, esperando 200 ms de delay, en esos 200 ms el pc te puede mandar varios bytes de datos y perderías todos excepto el último recibido. Cuando se sale de la interrupción del encoder el micro entraría en la interrupción de la UART, pero solo vería el último byte recibido ya que en el hardware de la UART del micro no hay ningún buffer, pila.. perdiendo todos los bytes anteriores.

Ya te lo comenté una vez en otro hilo si no recuerdo mal (no sé si te lo comenté a tí o a otra persona, fue hace tiempo), con pulsadores usa siempre un filtro pasobajo para quitar los rebotes por hardware, añadir un condensador y una resistencia no cuesta nada y te facilita la programación, además de lo anteriormente dicho que meter delays en las ISR no es lo correcto.

Por ejemplo una resistencia de 10k de pull-up, una resistencia de 39K la que va entre el pulsador y el pin del micro, y un condensador de 220 nF van bien para quitar los rebotes en los típicos pulsadores de 4 pines que se pinchan en las pcbs, cuadrados con un botón circular en medio, en la descarga del condensador estaría la R de 39K y el condensador, y en la carga del condesador las dos R 49K y el condensador, con esos datos y mirando los umbrales de 1 y 0 lógico del micro puedes calcular el tiempo de carga y descarga del condensador, este tiempo ha de ser siempre mayor que la duración máxima de los rebotes que puedes tener. La limitación de tiempo máximo en este caso es el tiempo que tarda la mano en girar el encoder y llegar al siguiente paso, pero será un tiempo muy alto respecto a la longitud máxima del rebote.

Ese encoder no lo he añadido en un proyecto, hice solo esa entrada del blog para probarlo ya que iba a usarlo para una placa, pero al final no lo utilicé. Pero lo dicho que si filtras bien no deberías tener problemas.

Las interrupciones de Arduino y como las implementan en código ya son otro tema, según el pin al que lo conectes tendrás interrupciones por cambio de valor en el pin e interrupciones activas por flanco, por lo que al final si vas a hacer cosas de electrónica, trabajas o estás estudiando estos temas te va a merecer más la pena aprender a usar micros de 8 bits de AVR sin el entorno de Arduino:

http://webdelcire.com/wordpress/archives/2866
http://webdelcire.com/wordpress/archives/2905
http://webdelcire.com/wordpress/archives/2973

Leo ahora que has editado y te ha funcionado, lo anteriormente dicho.

Saludos

Edito: http://spainlabs.com/foro/viewtopic.php?...otes#p7612
  Responder
#43
Triggerr, has pensado en usar un joystick ps3¿? el típico módulo para arduino digo..
  Responder
#44
Jorge escribió:http://webdelcire.com/wordpress/archives/2866
http://webdelcire.com/wordpress/archives/2905
http://webdelcire.com/wordpress/archives/2973

Leo ahora que has editado y te ha funcionado, lo anteriormente dicho.

Saludos

Edito: http://spainlabs.com/foro/viewtopic.php?...otes#p7612

Muchas Gracias Jorge, voy a ver si me empapo de los AVR, ya que en algunas partes de su funcionamiento me pierdo, aun no hemos dado nada de micros en la Uni, y aunque me defiendo en algunos aspectos estoy perdido. Voy a leerme la info y articulos que me has citado.

Respecto a los delay, procuro no emplearlos en el codigo, de echo en la insoladora para la temporizacion no los use, use millis(), pese a que por internet abundan los codigos basados en delay(). En este tengo que evitarlos a toda costa porque va a ser un codigo bastante largo....y ademas voy a tener recepcion de datos inalambricos y demas.

El evitar rebotes, en la insoaldora use debounce por soft, ya que no era critica esa parte del codigo no tenia comunicacion de ningun tipo.

Voy a ver si me agencio algun libro, tengo muchos frentes abiertos jajaja

jukillo escribió:Triggerr, has pensado en usar un joystick ps3¿? el típico módulo para arduino digo..

Estilo:

http://dx.com/es/p/qj-xm1121-new-arduino...tAodcAsAVw

La verdad es que no se me habia ocurrido jajaja, pero no estaria nada mas, el desplazamiento seria bastante comodo, lo que pasa es que desde un momento pense en un encoder...

Estoy subiendo un video de lo que voy avanzando en el tema de menus y demas...., ahora os lo pongo

  Responder
#45
Unos avances de los menus, el de la hora esta casi acabado, en la ultima linea ira una alarma.

[Imagen: agetu8y3.jpg]
[Imagen: eba8u3aj.jpg]

Ahora tengo que pelearme con la libreria Wire y RTC para comprender el funcionamiento del modulo de reloj y setear la hora. Asi como hacer una funcion para asignar una alarma
  Responder
#46


Tengo ya la parte de reloj casi acabada, a falta de acabar la alarma, para que suene, nose si pondre para poder configurar el tiempo que esta sonando...o la frecuencia con la que sonara la alarma hasta desactivarla....tendre que meditarlo. Contra mas menus y submenus tengo mas largo se vuelve el codigo....

Cuando tenga el codigo comentado indexado bien y demas os lo dejo por aqui, preparados que sera largo.

Incorporare al circuito un buzzer para el sonido y un pulsador para desactivar la alarma.
  Responder
#47
Esperando ese código para leerlo me hallo.
  Responder
#48
va fluido ese menú.... Guiño

no soy muy partidario de mostrar la hora en el lcd. si es para un reloj no queda otra, lógicamente.... pero para un sistema domótico puede producir delays en las órdenes por que está en ese momento actualizando la pantalla...

no te podría dar una explicación muy técnica, pero básicamente que al no ser multihilo, si está haciendo una cosa hasta que acabe no hará la otra, y con el reloj lo tienes ocupado cada segundo al estar borrando y pintando la pantalla de nuevo.

por lo menos en lo que se refiere a las pruebas que he hecho a título personal. Puede que esté diciendo una burrada, pero yo es algo que he notado ya desde hace tiempo, cuidado con los tiempos a los que vas refrescando cada cosa si el sistema está "a la espera de órdenes" Guiño
  Responder
#49
biketrial981 escribió:va fluido ese menú.... Guiño

no soy muy partidario de mostrar la hora en el lcd. si es para un reloj no queda otra, lógicamente.... pero para un sistema domótico puede producir delays en las órdenes por que está en ese momento actualizando la pantalla...

no te podría dar una explicación muy técnica, pero básicamente que al no ser multihilo, si está haciendo una cosa hasta que acabe no hará la otra, y con el reloj lo tienes ocupado cada segundo al estar borrando y pintando la pantalla de nuevo.

por lo menos en lo que se refiere a las pruebas que he hecho a título personal. Puede que esté diciendo una burrada, pero yo es algo que he notado ya desde hace tiempo, cuidado con los tiempos a los que vas refrescando cada cosa si el sistema está "a la espera de órdenes" Guiño

Lo he distribuido de manera de que cada menu tenga una ejecucion independiente, quiero decir, cuando estas en el menu hora, no estas a la espera de ninguna otra ejecucion (El encoder funciona con interrupciones), simplemente vas muestreando la hora, que es realmente lo que podria ocasionar problemas, pero le da tiempo al micro de sobra para refrescar y abarcar cada segundo que transcurre.

Al estar separado en funciones, aligeras la carga del programa. Solo realizo las operaciones necesarias e independientes para cada menu.

Cuando este en el menu TIEMPO, no estare preocupandome de mostrar la hora, sino de recibir los datos del exterior y mostrarlos, lo que tendra una tasa de refresco tampoco demasiado elevada, vamos no creo que tenga problema con ello....

Estoy peleandome ahora con la alarma, porque necesito controlar un altavoz, controlado con PWM, pero tengo problemas con el transistor que lo controla. Seguramente me vea en la necesidad de poner un MOS en lugar de un BJT, porque no me funciona el PWM correctamente, es un tema que tengo que analizar. Ya que me interesa poder regular el volumen de la alarma, y con un buzzer estoy bastante limitado.

Otro tema que tengo que mirar es la funcion que haga sonar la alarma, para hacer el sonido (Cuatro pitidos cortos, espacio, Cuatro pitidos cortos....), tendre que recurrir a millis() ya que no quiero emplear delay(), pero tengo que tener en cuenta no penalizar el refreco de la hora, y demas funciones, ya que en principio la alarma debe de sonar estando en el menu que estemos....sin penalizar el desarrollo del mismo, eso o paralizar todas las tareas y mostrar un mensaje por pantalla en plan "ALARMA" y pulsar el pusador para cancelar y volver a la situacion iniciar.
  Responder
#50
Vamos con el codigo, por ahora quedan muchas cosas por hacer y frentes abiertos que luego expondre:
[spoiler]
Código:
//Libreriaswgw
#include <Wire.h>
#include <EEPROM.h>
#include <RTClib.h>
#include <LiquidCrystal_I2C.h>
#include <Bounce.h>


//INICIALIZACION
LiquidCrystal_I2C lcd(0x20,20,4);  // set the LCD address to 0x20 for a 20 chars and 4 line display
RTC_DS1307 RTC; //Establecemos el tipo de RTC

//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 barradech[8] = { 0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3 };          //Barra a la derecha
byte linea_inf[8] = { 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f };         //Linea Inferior

//ASIGNACION DE PINES ENCODER
enum PinAssignments {
  encoderPinA = 2,   // right (DT)
  encoderPinB = 3,   // left (CLK)
};
//ASIGNACION DE PINES AUXILIAREs
const int encoderSW = 8;                  //Pin 8 para pulsador del encoder
const int buzz = 5;
const int pulsador = 4;
Bounce encoderSW_bounce = Bounce(8,10);  //Asignamos el debounce
Bounce pulsador_bounce = Bounce(4,10);  //Asignamos el debounce

//VARIABLES 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;  


//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;
//Variables alarma
int fut_hora_alarma = 0;
int fut_min_alarma = 0;
int fut_seg_alarma = 0;  
int alarma_on = 0;        //Variable de activacion
int vol = 20;             //Volumen de la alarma
long temp = 0;            //Variable para melodia
boolean ok_alarma = false;  //Si llegamos a la hora activamos esta variable
boolean sonido = false;     //Variable para la melodia
int i = 0;                  //Variable para la melodia
//Variables para los menus
boolean menu_ajustes =false;
boolean salir = false;    
//Variables
int dia_semana = 0;

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

void setup() {
    lcd.init();                //Iniciamos el LCD
  lcd.backlight();                      //Iniciamos la retroiluminacion
  lcd.setCursor(6,0);                 //Situamos el cursor al inicio
  lcd.print("Estacion");         //Imprimimos mensaje inicial
  lcd.setCursor(3,1);
  lcd.print("Meteorologica");
  lcd.setCursor(7,3);                
  lcd.print("L1llo5");            
  delay(2000);                           //Esperamos 2 segundos con el mensaje
  lcd.clear();                           //Limpiamos la pantalla
    //ASIGNACION DE TIPO DE PINES
  pinMode(encoderPinA, INPUT_PULLUP);
  pinMode(encoderPinB, INPUT_PULLUP);
  pinMode(encoderSW, INPUT_PULLUP);
  pinMode(buzz, OUTPUT);
  pinMode(pulsador, INPUT);
  //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,barradech);    // 3 -> Barra Derecha
  lcd.createChar(4,linea_inf);    // 4 -> Linea Inferior
  //Leemos valores de la alarma
  fut_hora_alarma=EEPROM.read(1);
  fut_min_alarma=EEPROM.read(2);
  fut_seg_alarma=EEPROM.read(3);
  alarma_on=EEPROM.read(4);
}

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

void loop() {
    rotating = true;       //Seteamos el debounce
    if(menu != lastmenu){  //Si la variable menu cambia
        lastmenu=menu;       //Igualamos situamos lastmenu como actual
    menu = menu > 4 ? 1 : menu ;         //Si menu es mayor que 4, volvemos a la posicion 1      
    menu = menu < 1 ? 4 : menu ;         //Si menu es menor que 1, volvemos a la posicion 4
    lcd.clear();         //Limpiamos pantalla
    print_margen();      //Dibujamos el margen superior
    }
    switch (menu) {         //Switch para mostrar menus
        case 1:               //Opcion 1 RELOJ
          lcd.setCursor(7,0);  
          lcd.print("Reloj");
      fun_alarma();           //Comprobamos alarma
      if (RTC.isrunning()) {  //Si no recibimos datos del RTC mostramos error
      printhour();            //Imprimimos la hora
      }else{
        lcd.setCursor(0,1);
        lcd.print("Error Reloj");
      }
          break;    
        case 2:               //Opcion 2 TIEMPO
          lcd.setCursor(6,0);
          lcd.print("Tiempo");
          break;
        case 3:               //Opcion 3 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
        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,5);         //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("Ajustes");
          lcd.setCursor(1,1);
          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:                 //(PROVISIONAL)
            lcd.setCursor(0,0);   //Situamos el indicador en la linea 0
            lcd.write(1);
            break;
          case 5:                 //SALIR
            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
              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 4:                   //Opcion 4 ALARMAS (POSIBLE MODIFICACION POR TIEMPO EXTERIOR)
          lcd.setCursor(6,0);
          lcd.print("Alarmas");
          break;
    }
}

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

//Interrupcion al cambio de A
void doEncoderA(){
  // debounce
  if ( rotating ) delay (1);
  //Comprobamos que haya cambiado
    if( digitalRead(encoderPinA) != A_set ) {  
    A_set = !A_set;
    // adjust counter + if A leads B
    if ( A_set && !B_set )
      menu ++;
    rotating = false;  // no more debouncing until loop() hits again
  }
}

//Interrupcion al cambio de B
void doEncoderB(){
  if ( rotating ) delay (1);
  if( digitalRead(encoderPinB) != B_set ) {
    B_set = !B_set;
    //  adjust counter - 1 if B leads A
    if( B_set && !A_set )
      menu --;
    rotating = false; // no more debouncing until loop() hits again
  }
}
//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
  if (dia_semana != now.dayOfWeek()){
    lcd.clear();
    print_margen();
    dia_semana = now.dayOfWeek();
  }
  switch (now.dayOfWeek()){ //Seleccionamos el dia de la semana
  case 0:
    lcd.setCursor(0,2);
    lcd.print("Domingo");
    break;
  case 1:
    lcd.setCursor(0,2);
    lcd.print("Lunes");
    break;
  case 2:
    lcd.setCursor(0,2);
    lcd.print("Martes");
    break;
  case 3:
    lcd.setCursor(0,2);
    lcd.print("Miercoles");
    break;
  case 4:
    lcd.setCursor(0,2);
    lcd.print("Jueves");
    break;
  case 5:
    lcd.setCursor(0,2);
    lcd.print("Viernes");
    break;
  case 6:
    lcd.setCursor(0,2);
    lcd.print("Sabado");
    break;
  }
  //Imprimimos la fecha
  lcd.setCursor(10,2);
  lcd.print(now.day());
  lcd.print('/');
  lcd.print(now.month());
  lcd.print('/');
  lcd.print(now.year());
  if (ok_alarma){         //Si tenemos sonando la alarma mostramos un icono
    lcd.setCursor(19,0);
    lcd.write(2);
    }else{                  //Sino dibujamos el guion
      lcd.setCursor(19,0);
      lcd.write(4);
  }
  if (alarma_on == 1){    //Si tenemos la alarma activa mostramos un icono
    lcd.setCursor(18,0);
    lcd.write(2);
    }else{                  //Sino dibujamos el guion
      lcd.setCursor(18,0);
      lcd.write(4);
  }
  //Mostramos la posicion de la alarma
  lcd.setCursor(0,3);
  lcd.print("Alarma: ");
  if(fut_hora_alarma >= 0 && fut_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(fut_hora_alarma);
  lcd.print(':');
  if(fut_min_alarma >= 0 && fut_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(fut_min_alarma);
  lcd.print(':');
  if(fut_seg_alarma >= 0 && fut_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(fut_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){
  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);
}

//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();
  //Menu ajustes de hora
  lcd.clear();
  lcd.setCursor(0,1);
  lcd.print("Establecer 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
    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 ;
    }
  }
  menu= 0 ;                                   //Inicializamos variables ENCODER
  lastmenu = 0;
  while (set_min == false){                   //Mientras no pulsemos mantenemos en menu minutos
    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 ;
    }
  }
  menu= 0 ;                                   //Inicializamos variables ENCODER
  lastmenu = 0;
  while (set_seg == false){                   //Mientras no pulsemos mantenemos en menu segundos
    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 ;
    }
  }
  RTC.adjust(DateTime(now.year(),now.month(), now.day(), fut_hora, fut_min, fut_seg));  //Introducimos la hora en el DS1307
}

//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;
  //Variables de seteo
  fut_ano = now.year();
  fut_mes = now.month();
  fut_dia = now.day();
  //Menu ajustes de hora
  lcd.clear();
  lcd.setCursor(0,1);
  lcd.print("Establecer Fecha:");
  adj_print_date(fut_dia,fut_mes,fut_ano);    //Dibujamos la fecha
  menu= 0 ;                                   //Inicializamos variables para ENCODER
  lastmenu = 0;
  while (set_dia == false){                   //Mientras no pulsemos mantenemos en menu ajustar dia
    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);  //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;
    }
  }
  menu= 0 ;                                   //Inicializamos variables para encoder
  lastmenu = 0;        
  while (set_mes == false){                   //Mientras no pulsemos mantenemos ajustando mes
    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);  //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 ;
    }
  }
  menu= 0 ;                                   //Incializamos variables para encoder
  lastmenu = 0;                      
  while (set_ano == false){                   //Mientras no pulsemos mantenemos ajustando año
    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);  //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;
    }
  }
  RTC.adjust(DateTime(fut_ano, fut_mes, fut_dia, now.hour(), now.minute(), now.second()));  //Introducimos fecha en DS1307
}
//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!=fut_hora_alarma)  //Si las horas guardadas no coinciden con las nuevas volvemos a guardar
  {
    EEPROM.write(1,fut_hora_alarma); //Guardamos
  }
  if (savemin!=fut_min_alarma)  //Si los minutos guardados no coinciden con los nuevos volvemos a guardar
  {
    EEPROM.write(2,fut_min_alarma); //Guardamos
  }
  if (saveseg!=fut_seg_alarma)  //Si los segundos guardados no coinciden con los nuevos volvemos a guardar
  {
    EEPROM.write(3,fut_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;
  //Variables de seteo
  //Menu ajustes de hora
  lcd.clear();
  lcd.setCursor(0,1);
  lcd.print("Establecer Alarma:");
  adj_print_hora(fut_hora_alarma,fut_min_alarma,fut_seg_alarma);  //Dibujamos los valores que tengamos guardados
  if (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
    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 ;
    }
  }
  menu= 0 ;                             //Inicializamos variables ENCODER
  lastmenu = 0;
  while (set_min_alarma == false){      //Mientras no pulsemos mantenemos en ajustar hora
    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 ;
    }
  }
  menu= 0 ;                             //Inicializamos variables ENCODER
  lastmenu = 0;
  while (set_seg_alarma == false){      //Mientras no pulsemos mantenemos en ajustar segundos
    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 ;
    }
  }
  menu= 0 ;                              //Inicializamos variables ENCODER
  lastmenu = 0;
  while ( set_alarma_on == false){      //Mientras no pulsemos mantenemos en el menu de activacion
  if(menu > lastmenu){                  //Si menu mayor que lastmenu sumamos valor
      alarma_on++;
      alarma_on=constrain(alarma_on,0,1); //Limite 0-1
      lastmenu=menu;
      }else if (menu < lastmenu){         //Si menu es menor que lastmenu restamos valor
        alarma_on--;
        alarma_on=constrain(alarma_on,0,1); //Limite 0-1
        lastmenu=menu;
      }else if (menu == lastmenu){        //Si nada cambia mantenemos
        alarma_on=alarma_on;
    }
  //Si alarma_ON es 1 dibujamos ON, de lo contrario dibujamos OFF
  if (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 ;
    }
  }
  save_alarma(fut_hora_alarma,fut_min_alarma,fut_seg_alarma,alarma_on); //Llamamos a la funcion para guardar en la EEPROM
}

//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 (fut_hora_alarma == now.hour() && fut_min_alarma == now.minute() && fut_seg_alarma == now.second() && alarma_on){  
    ok_alarma = true;   //Alarma encendida
    temp=0;
  }
  //Rutina para generar el sonido sin delay(), cuatro pitidos cortos, un espacio de 300ms y repetimos
  if (ok_alarma == true){
    if (millis() < temp && sonido){
      analogWrite(buzz,vol);
    }else if (millis() > temp){
      digitalWrite(buzz,LOW);
      temp=millis()+90;
      sonido=!sonido;
      i++;
      if (i>8){
        temp=millis()+300;
        sonido=!sonido;
        i=0;
      }
    }
  }
  pulsador_bounce.update();             //Comprobamos si hemos pulsado el boton
  if (pulsador_bounce.risingEdge()){    //Si hemos pulsado paramos la alarma
    ok_alarma = false;                
    digitalWrite(buzz,LOW);              
  }
}
[/spoiler]
Bien, todo funciona correctamente, a falta de probar con mas rigurosidad todo.

Estoy teniendo problemas a la hora de ejecutar la funcion alarma, ya que empleo la funcion millis(), para hacer el sonido, que son cuatro pitidos de 80ms cada uno, en repeticion y separacion de unos 250ms, el problema esta que al no querer molestar a las demas funciones con el funcionamiento de la alarma, no funciona correctamente, pasan mas de 80ms en la ejecucion de muestreo por pantalla de la hora al parecer y no suena, el codigo no se ejecuta como debe..., soluciones que me planteo para esto, no dejar todo en manos del NANO, y recurrir a un ATiny o un 12F de Microchip, para el volumen, tono y melodia de la alarma, mandando del NANO unicamente una señal de activacion.

Otra cosa que me planteo es reducir el tiempo de ejecucion de imprimir la hora, no actualizando siempre la fecha, ya que solo cambia una vez al dia, lo que ahorraria tiempo de ejecucion, pero no estoy del todo seguro que no siga teniendo problemas con el tiempo y la funcion de sonar la alarma la verdad...., es un tema que tengo que revisar a fondo...

Por lo demas la alarma es totalmente funcional, y desactivable con un pulsador, por ahora esta solo sonaria si nos situamos en el menu RELOJ, pero en un futuro sonara independiente en el menu que nos situemos, por eso me planteo el usan un micro pequeño para que realice la funcion de la alarma. Pense en basar la misma en NE555 pero me parece bastante arcaico....

Cosas pendientes, poder seleccionar el volumen de la alarma, seguramente quite la libreria BOUNCE, y ponga antirebote por HW para aligerar el codigo.

Poder seleccionar un rango de horas para el cual se apague la retroiluminacion, o estando en el menu reloj al pulsar el boton del encoder apagar la misma...por ejemplo.

Y con esto creo que tendria todo lo que es el bloque de reloj operativo para poder pasar a otros menus.

Se me queda algo? jajajajaja
  Responder
#51
Impresionante ese menú, que ganas de ver esa estación terminada :O
giltesa.com Mi blog personal sobre informática, electrónica, Arduino, bricolaje, etc.
  Responder
#52
giltesa escribió:Impresionante ese menú, que ganas de ver esa estación terminada :O

Vamos a ver si esta semana puedo avanzar un poquito mas, y acabar ya el menu y todas sus opciones.

Intentare optimizar el funcionamiento de muestreo de la hora, imprimiendo la fecha solo cuando cambie del dia, al igual que imprimir la alarma cuando se establezca una nueva alarma, asi aglizar un poco el cambio de menu, aunque funciona perfectamente.

La programacion de un Attiny varia mucho respecto de una placa Arduino?

Un Saludo¡
  Responder
#53
Bueno, volvemos al trabajo, queria dejar ya finalizado el menu de RELOJ, para testearlo y poder pasar con el menu de tiempo, comunicaciones y demas, que es un bacalao curioso.

Logre optimizar el menu reloj, reduciendo el tiempo de ejecucion del mismo, agilizando asi la actualizacion de la hora sin problemas, si como la alarma y demas. Comprobe el tiempo de ejecucion inicial del menu reloj, y teniamos lo siguiente:

[Imagen: SDS00001_zps0e91d1a1.png]

91.20ms aproximadamente de ejecucion del menu que muestra la hora, la verdad es que estaba muy bien, pero se podia agilizar mas, y asi fue.

Tras reorganizar algunas funciones, actualizar solo la fecha y alarma cuando cambiamos de menu o cambia el dia, hemos llegado a lo siguiente:

[Imagen: SDS00003_zps594675a0.png]

33.20ms, lo que no esta nada mal.

Y bueno, os dejo un video del dicho menu, funcionamiento de alarma y demas. El sonido de la alarma como bien dije seguramente vaya en un 12F o similar (Dare programacion de pics este cuatrimestre en la UNI asique no tendre problema espero jajajaja)



Ahora me pondre con la comunicacion inalambrica, en principio queria probar con el modulo de 2.5Ghz, aver si logro enviar y recibir, y luego ya enviar estructuras y demas. Os mantendre al tanto.

Necesitaba una manita de los gurus del RC y baterias, porque tenia idea de alimentar la estacion exterior con una bateria y mantenerla con una pequeña placa solar. Intentando reducir el consumo de la misma al minimo. Como lo veis? Que tipo de bateria usariais?, algun articulillo que trate del tema?, en este campo ando perdidisimo la verdad.
  Responder
#54
Bueno, debido a que estoy a falta de arduinos NANOS, con el leonardo establecer la comunicacion es un poco lio por el cambio de pines y demas, tendria que meterme a fondo. He preferido ponerme por ahora con los sensores y su utilizacion.

BH1750 (Sensor de luz)

Bien, para empezar comentar que es un sensor muy sencillo, con el podemos medir los Lux, a traves de una comunicacion I2C. Para empezar necesitamos ver el datasheet y sus caracteristicas:

DATASHEET

EBAY

[Imagen: Sintiacutetulo_zps963e6831.jpg]

Las caracteristicas estan bien claras, tanto los limites de medida, como la comunicacion, como las demas caracteristicas. En mi caso lo alimente a 5V, y las conexiones son como cualquier modulo conectado por I2C al arduino.

La libreria que emplee es la siguiente:

LIBRERIA

Por ahora funciona bien, y es una libreria muy sencilla de usar, aunque es necesario conocer algunos parametros del sensor.

Este sensor tiene varios modos de operacion que podemos definir previamente, al inicializarlo. Los modos son los siquientes:

[Imagen: Sintiacutetulo2_zps4ecd1557.jpg]

Dependiento de la instruccion que mandemos al sensor, tenedremos los siguientes modos de operacion:

Apagado -> 0000_0000

Encendido -> 0000_0001

Reset -> 0000_0111
Este reset sirve para eliminar los registros de funcionamiento anteriormente enviados

Continuo Alta Resolucion ->0001_0000
Medida continua de datos, con una resolucion de 1lx, tiempo de medida 120ms

Continuo Alta Resolucion 2 -> 0001_00001
Medida continua de datos, con una resolucion de 0.5lx, tiempo de medida 120ms

Continuo Baja Resolucion -> 0001_0011
Medida continua de datos, con una resolucion de 4lx, tiempo de medida 16ms

Medida Unica Alta Resolucion -> 0010_0000
Medida unica (Cuando se requiere), con una resolucion de 1lx y 120ms, se "apaga" tras la medida

Medida Unica Alta Resolucion 2-> 0010_0001
Medida unica (Cuando se requiere), con una resolucion de 0.5lx y 120ms, se "apaga" tras la medida

Medida Unica Baja Resolucion -> 0010_0011
Medida unica (Cuando se requiere), con una resolucion de 4lx y 16ms, se "apaga" tras la medida

Una vez sabemos los diferentes modos, podemos elegir cual nos conviene mas, en mi caso empleare Medida Unica Alta Resolucion, ya que el tiempo de medida no es critico y nos interesa tener el menor consumo posible.

Pero nos preguntamos, como introducimos estos codigos?, con la libreria podemos introducirlos de manera sencilla, pero vamos a ir un poco mas lejos, y vamos a ver como los comunica el Arduino por I2C al iniciar la comunicacion.

La estructura para enviar o recibir la informacion del sensor es la siguiente:

[Imagen: Captura_zps3e505bd4.jpg]

Tenemos ST, que es el inicio de la comunicacion, los 7 Bits siguientes son los de la direccion del sensor, el BIT siguiente es la indicacion de lectura "1" o escritura "0", a continuacion tenemos la confirmacion del sensor Acknowledge "0", los 8 bits del modo en el que queremos poner el sensor, y la finalizacion de la comunicacion.

En nuestro caso, queremos entrar en modo unico Alta Resolucion, por lo tanto tendriamos lo siguiente:

[Imagen: entradaH05x_zpsea401510.png]

Iniciamos la comunicacion, flanco de bajada de SDA con SCL en valor alto, y empezamos a tomar valores en fada flanco de subida,vemos que tenemos los 7 bits de direccion, "0100011", tras este establecemos escritura "0", a continuacion tenemos el Acknowledge "0". Ahora viene la seleccion de modo, donde vemos que tenemos 0010_0000, por lo que establecemos medida unica alta resolucion. Y tras esto el STOP de la comunicacion.

Vemos que el I2C es una comunicacion bastante sencilla y asequible, ahora para seleccionar este modo en la libreria (Por defecto viene alta resolucion continuo), debemos incluir lo siguiente:

Sabiendo las instrucciones, incluidas en la libreria:

Código:
#define BH1750_DEBUG 0

#define BH1750_I2CADDR 0x23

// No active state
#define BH1750_POWER_DOWN 0x00

// Wating for measurment command
#define BH1750_POWER_ON 0x01

// Reset data register value - not accepted in POWER_DOWN mode
#define BH1750_RESET 0x07

// Start measurement at 1lx resolution. Measurement time is approx 120ms.
#define BH1750_CONTINUOUS_HIGH_RES_MODE  0x10

// Start measurement at 0.5lx resolution. Measurement time is approx 120ms.
#define BH1750_CONTINUOUS_HIGH_RES_MODE_2  0x11

// Start measurement at 4lx resolution. Measurement time is approx 16ms.
#define BH1750_CONTINUOUS_LOW_RES_MODE  0x13

// Start measurement at 1lx resolution. Measurement time is approx 120ms.
// Device is automatically set to Power Down after measurement.
#define BH1750_ONE_TIME_HIGH_RES_MODE  0x20

// Start measurement at 0.5lx resolution. Measurement time is approx 120ms.
// Device is automatically set to Power Down after measurement.
#define BH1750_ONE_TIME_HIGH_RES_MODE_2  0x21

// Start measurement at 4lx resolution. Measurement time is approx 120ms.
// Device is automatically set to Power Down after measurement.
#define BH1750_ONE_TIME_LOW_RES_MODE  0x23

Inicializamos como:

Código:
void begin(0x20);

Siendo tambien valido:

Código:
void configure(0x20);

Una vez que tenemos el modulo configurado correctamente, solo nos queda obtener la medida, que en el ejemplo de la libreria se ve sin ningun tipo de problema, pero vamos a observar como realizamos la recepcion de datos por I2C

La trama va en orden de las fotografias, ya que no entra de un pantallazo:

[Imagen: 1_zps969b4948.png]

[Imagen: 2_zps33fe6160.png]

[Imagen: 3_zps999a0d70.png]

Como vemos, iniciamos la comunicacion como en el caso anterior, introducimos la dirección, y en este caso el siguiente bit sera un "1" ya que queremos modo lectura, a continuacion, obtenemos el Acknowledge, y mandamos los bits [15:8], de la lectura "00000110", el sensor recibe un Acknowledge del mastro diciendo que la emision ha sido correcta, y mandamos los bits [7:0] de la lectura: "01111000". Nos llega un Acknowledge negado del maestro verificando la transmision y para cerrar la comunicacion, donde tenemos ya la condicion de STOP, flando de subida de SDA con SCL en estado alto.

Los datos recibidos son los siguiente:

"0000011001111000", donde tenemos en la imagen anterior con un sencillo calculo como obtener los LX, en este caso:

(2^3+2^4+2^5+2^6+2^9+2^10)/1.2=1380lx

Y esto es todo sobre dicho sensor, hemos tocado un poco de comunicacion I2C porque tambien resulta interesante, espero que a alguien le valga de ayuda para escoger este sensor en sus proyectos.

Un Saludo¡ (Cualquier error en el articulillo comentarmelo)

Proximamente el sensor de presiòn
  Responder
#55
Chapó. Nunca se me había ocurrido buscar esto, xD. Da para entrada del blog.
  Responder
#56
jukillo escribió:Chapó. Nunca se me había ocurrido buscar esto, xD. Da para entrada del blog.

Al fin y al cabo, la mayoria de modulos para arduino lo unico que tienen es facilidad de conexion, porque los propios CI tienen comunicacion I2C y viene todo bien visto en los datasheet.

Me gustaria aprender un poco mas sobre la libreria wire y demas, pero tiempo al tiempo, hay cierta sintaxis de las mismas que no entiendo, porque hay cosas de C que se me escapan..., veremos si me leo algo mas sobre C
  Responder
#57
Pues menos mal que se te escapan.... Mola
Me lo guardo para cuando tenga que usar algún sensor de luminosidad, así ya no me como la cabeza jajaja.

Parece sencillo cuando te lo explican así :elrisas:
  Responder
#58
BMP180 (Sensor de Presion)

Como sensor de presion, usaremos el BMP180 de BOSCH.

Al igual que el anterior funciona con conexion I2C, lo unico que en este caso lo alimentaremos a 3.3V y tendremos una medida de presion y temperatura.

EBAY

DATASHEET

Y una foto de la plaquita en cuestion:

[Imagen: IMG_20140202_190418_zps8udj5a15.jpg]

En mi caso, la placa ya viene con las resistencias PULL-UP para la conexion I2C, y un transistor 662 SMD para los 3.3V, pese a que lo conectamos a 3.3V.

Las caracteristicas del sensor son las siguientes:

Cita:Key features

Pressure range: 300 ... 1100hPa (+9000m ... -500m relating to sea level)
Supply voltage: 1.8 ... 3.6V (VDD)
1.62V ... 3.6V (VDDIO)

Package: LGA package with metal lid
Small footprint: 3.6mm x 3.8mm
Super-flat: 0.93mm height

Low power: 5µA at 1 sample / sec. in standard mode

Low noise: 0.06hPa (0.5m) in ultra low power mode
0.02hPa (0.17m) advanced resolution mode

- Temperature measurement included
- I
2C interface
- Fully calibrated
- Pb-free, halogen-free and RoHS compliant,
- MSL 1

Este sensor es un sensor piezoresistivo, que toma tanto medida de temperatura como de presion, con una resolucion de 16BITS para la temperatura y entre 16-19 para la presion, a traves de la presion podemos calcular la altura a la que nos encontramos, aunque en mi caso dudo que lo emplee, ya que no me acaba de convencer la fidelidad de la altura con la presion.

Tenemos en el mismo una memoria EPROM en la que vienen guardados una serie de parametros de calibracion que vienen guardados a la hora en la que BOSCH comprueba estos sensores, dichos parametros se cargan al inicializarlo para realizar los calculos.

Tenemos 4 modos de operacion que podemos modificar con un parametro de seteo:

[Imagen: Captura_zps046c10d4.jpg]

Modo 0: Modo de bajo consumo, tomamos una medida, el tiempo de conversion es de 4.5ms, y la corriente de 3ua

Modo 1: Modo normal, tomamos dos medidas y tenemos una corriente de 5uA. Tiempo de conversion 7.5ms

Modo 2: Modo Alta Resolucion, tomamos 4 medidas con una corriente de 7ua. Tiempo de conversion 13.5ms

Modo 4: Modo Muy Alta Resolucion, tomamos 8 medidas con una corriente de 12uS. Tiempo de conversion 25.5ms

Una ves sabemos esto podemos adaptar el funcionamiento a nuestras necesidades, ahora veremos como modificar dicho parametro.

Para saber el funcionamiento del sensor es importante mirar esta tabla, que podemos ver mas claramente en el PDF del datasheet, y que intentare explicar un poco.

[Imagen: Sintiacutetulo_zpsbda42076.jpg]

Lo primero, inicializamos el sensor, cargando las variables de calibracion, que mas tarde nos valdran para calcular presion y temperatura.

Tras cargar estos parametros, pasamos a leer la temperatura sin compensar. Solicitamos acceso a temperatura con "0x2E", codigo en hexadecimal para leer temperatura, al registro 0xF4. Esperamos 4.5ms. Leemos el registro con el valor de temperatura, 0xF6 para el MSB (Bits mas significativos) y 0xF7 para el LSB (Bits menos significativos) Esto es porque trabajamos con paquetes de 8bits, y los datos son grupos de 16bits.

Ahora leeriamos la presion sin compensar, mandamos 0x34, lo que nos indica que estamos en el Modo 0, o de bajo consumo (Mas adelante podre la tabla de modos), en el registro 0xF4, esperamos y pasamos a leer los valores, 0xF6 para el MSB (Bits mas significativos) y 0xF7 para el LSB (Bits menos significativos), y en este caso 0xF8 (XLSB), que reyenaria los bits que quedan en caso de usar los 19 para la presion.

Tras esto y con los parametros de calibracion, podemos calcular tanto temperatura como presion, a traves de las formulas que vienen en los dos ultimos cuadros del diagrama de bloques. Una vez mostrada la temperatura volveriamos arriba y cerrariamos el bucle.

En la practica es sencillo, pero en este caso no puedo mostraros las tramas de datos de temperatura y de presion, ya que de inicio tenemos que cargar toda la EPROM, y el osciloscopio no es capaz de almacenar señales tan largas, y luego el problema esta en que cada vez que accedemos al sensor para luego tomar la medida, tenemos una espera de unos ms, por lo que nos vamos a escalas muy grande comparadas con el tiempo en el que transmitimos, por tanto omitiremos en este caso esta informacion, mas que nada por facilidad.

[Imagen: 55_zps2f23e014.jpg]

En esta tabla podemos ver otros registros, que nos indican si la conversion ha sido realizada, el control de la cantidad de muestras tomadas, un registro de reinicio y por ultimo uno del ID del chip, para verificar si tenemos un BMP180.

La direccion del chip, para mandar al inicio de la trama en este caso es:

1110111/(0/1)

El ultimo bit, lo tendremos a 0 en caso de realizar una escritura o en 1 en caso de realizar una lectura.

En hexadecimal tendriamos 0xEF para lectura y 0xEE para escritura

Como hablamos antes, tenemos que enviar un paquete para seleccionar el modo, y aqui tenemos la tabla para saber que mandar:

[Imagen: Sintiacutetulo2_zpsa40cbb98.jpg]

Y por ultimo la informacion sobre como enviamos la trama de datos, que viene a ser lo que explicamos antes.

[Imagen: Sintiacutetulo3_zps2590a6df.png]

Como vemos, iniciamos la comunicacion, mandamos la direccion del modulo, tras un ACK, mandamos el registro en el que queremos entrar y otro ACK.

Reiniciamos la conexion, mandamos de nuevo la direccion del modulo pero esta vez en lectura, y tras el ACK, recibimos los paquetes de datos, existiendo entre ambos paquetes un ACK y un NACK para finalizar la comunicacion.

Una cosa que se puede observar, es como varia el tiempo de lectura segun el modo que queramos. En las siguientes imagenes lo veremos:

Si empleamos el Modo Bajo Consumo, tenemos un tiempo de unos 4.5ms, y aqui podemos observarlo:

[Imagen: TiempoCorto_zps2882ca9d.png]

Si por el contrario, empleamos el Modo Muy Alta Resolucion, tenemos un tiempo de unos 25.5ms:

[Imagen: TiempoLargo_zps456fd873.png]

Para realizar todo esto, tenemos la libreria en arduino:

BMP180 LIBRERIA

Donde vemos, que el el .h, tenemos definidas todas las direcciones y modos:

Código:
#define BMP085_DEBUG 0

#define BMP085_I2CADDR 0x77

#define BMP085_ULTRALOWPOWER 0
#define BMP085_STANDARD      1
#define BMP085_HIGHRES       2
#define BMP085_ULTRAHIGHRES  3
#define BMP085_CAL_AC1           0xAA  // R   Calibration data (16 bits)
#define BMP085_CAL_AC2           0xAC  // R   Calibration data (16 bits)
#define BMP085_CAL_AC3           0xAE  // R   Calibration data (16 bits)    
#define BMP085_CAL_AC4           0xB0  // R   Calibration data (16 bits)
#define BMP085_CAL_AC5           0xB2  // R   Calibration data (16 bits)
#define BMP085_CAL_AC6           0xB4  // R   Calibration data (16 bits)
#define BMP085_CAL_B1            0xB6  // R   Calibration data (16 bits)
#define BMP085_CAL_B2            0xB8  // R   Calibration data (16 bits)
#define BMP085_CAL_MB            0xBA  // R   Calibration data (16 bits)
#define BMP085_CAL_MC            0xBC  // R   Calibration data (16 bits)
#define BMP085_CAL_MD            0xBE  // R   Calibration data (16 bits)

#define BMP085_CONTROL           0xF4
#define BMP085_TEMPDATA          0xF6
#define BMP085_PRESSUREDATA      0xF6
#define BMP085_READTEMPCMD          0x2E
#define BMP085_READPRESSURECMD            0x34

Donde en:

Código:
bmp.begin()

Podemos establecer el modo, incluyendo en el parentesis el numero del modo que queremos que emplee, vale tanto el numero como la denominacion del #define.

Recomiendo a todo el mundo, leer la libreria, porque no es nada dificil su sintaxis, y se aprende bastante. En ella encontramos las formulas para obtener la altura, igual que en el datasheet.

OBSERVACIONES:

Realmente me es dificil comprobar la fiabilidad de dicho sensor, a falta de tener con que comparar. La temperatura y presion parecen correctas, aunque no me acaban de convencer los calculos de altura, en mi caso no los empleare

Con la presion podremos obtener una prediccion sobre el tiempo que va a hacer, trataremos de ello mas adelante, a la hora de programar como manejaremos todos los datos.

El BMP085 es el predecesor de el BMP180, por lo que las librerias son 100% compatibles.

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Proximamente analizaremos el funcionamiento del DHT22, y del sensor de gases cuando me llege. Pocos sensores quedan de ver, asique esto avanza. El consumo por ahora en la estacion exterior es ridiculo, los sensores dudo que superen el mA
  Responder
#59
Menudo tutorial Mola
Yo tengo que empeza a documentar el DHT11 para el proyecto final.
  Responder
#60
cansi22 escribió:Menudo tutorial Mola
Yo tengo que empeza a documentar el DHT11 para el proyecto final.

Tendremos que mirar como manda este la informacion, el datasheet es algo escasillo....jajajajaj.

Estabas haciendo un proyecto de estacion o similar verdad?

Un saludo
  Responder


Posibles temas similares…
Tema Autor Respuestas Vistas Último mensaje
  Estación meteorológica v2 - giltesa giltesa 69 18,880 25-01-2024, 08:31 PM
Último mensaje: sermosderendert