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.....
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
Triggerr, has pensado en usar un joystick ps3¿? el típico módulo para arduino digo..
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
Unos avances de los menus, el de la hora esta casi acabado, en la ultima linea ira una alarma.
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
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.
Esperando ese código para leerlo me hallo.
va fluido ese menú....
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"
biketrial981 escribió:va fluido ese menú....
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"
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.
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
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.
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¡
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:
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:
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.
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
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:
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:
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:
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:
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:
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
Chapó. Nunca se me había ocurrido buscar esto, xD. Da para entrada del blog.
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
Pues menos mal que se te escapan....
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:
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:
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:
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.
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.
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:
Y por ultimo la informacion sobre como enviamos la trama de datos, que viene a ser lo que explicamos antes.
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:
Si por el contrario, empleamos el Modo Muy Alta Resolucion, tenemos un tiempo de unos 25.5ms:
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:
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
Menudo tutorial
Yo tengo que empeza a documentar el DHT11 para el proyecto final.
cansi22 escribió:Menudo tutorial
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
|