sábado, 6 de julio de 2019

Anemómetro, Programa Final

El programa final es una versión totalmente independiente, se requiere un LCD para visualizar la información, una SD para guardar los datos, además se requiere la carga previa de un programa que establece las posiciones en la EPROM para los datos de fecha y hora. En este programa previo, también establece el valor de la constante "puntero", que permite calcular el día de la semana durante todo un año a través de su método diaSemana(); puedes consultar la entrada Calcular día de la Semana para conocer su funcionamiento y ver otra alternativa mas práctica. A continuación os dejo es Sketch previo:

// carga de hora y fecha para Anemometro, Compilado con Arduino 1.8.1, 15/03/2019
#include <EEPROM.h>
void setup() {
Serial.begin(9600);
// estos valores deben indicarse solo al cargar el programa por primera vez. Para actualizar la hora y/o minuto hay que comentar el resto de lineas
// ya que el programa principal se autogestiona 
  EEPROM.write(0,0);//seg
  EEPROM.write(1,23);//hora
  EEPROM.write(2,59);//minuto
  EEPROM.write(3,31);//dia
  EEPROM.write(4,12);//mes
  EEPROM.write(5,19);//any
  EEPROM.write(6,1);//puntero dia de la Semana
  /*                                                                                                                       posicion=  0   1   2   3   4   5   6
   para el año 2019 le corresponde 1; ya que el 1/01/2019 fué Martes,     y se corresponde con la posision 1 de la tabla tSemana[7]={'L','M','X','J','V','S','D'};
   para el año 2020 le corresponde 2; ya que el 1/01/2020 será Miercoles, y se corresponde con la posision 2 de la tabla tSemana[7]={'L','M','X','J','V','S','D'};
   y así sucesivamente hasta seis, luego comienza de cero
   */
  Serial.print(EEPROM.read(1));Serial.print(":");Serial.print(EEPROM.read(2));Serial.print(":");Serial.println(EEPROM.read(0));// imprime la hora establecida
  Serial.print(EEPROM.read(3));Serial.print("/");Serial.print(EEPROM.read(4));Serial.print("/");Serial.println(EEPROM.read(5)+2000);// imprime la fecha establecida
  Serial.print("Puntero: ");Serial.println(EEPROM.read(6));// imprime el valor de puntero
}
void loop() {}

Y el Sketch final, fuertemente comentado, es un modelo resultón y muy similar al que estoy empleando en la actualidad, determina la velocidad del viento dado en un minuto, o sea, ofrece las revoluciones por minuto, literalmente hablando. De manera que si un día de viento racheado nuestro anemómetro nos calcula una velocidad de 60km/h., nos esta dando un promedio por minuto ya que ha multiplicado las revoluciones que se han sucedido por la distancia y lo ha dividido entre 60seg.

/* ESQUEMA DE CONEXIONES. Compilado con Arduino 1.8.1 el 15/03/2019       
                      7-14v.                  +-----+
                  ____[PWR]___________________| USB |__
                 |                            +-----+  |
                 |         GND/RST2  [ ][ ]            |
                 |       MOSI2/SCK2  [ ][ ]  A5/SCL[ ] | <-- pin SCL del LCD
                 |          5V/MISO2 [ ][ ]  A4/SDA[ ] | <-- pin SDA del LCD
                 |                             AREF[ ] |
                 |                              GND[ ] |
                 | [ ]N/C                    SCK/13[ ] | <-- pin SCK microSD  
                 | [ ]IOREF                 MISO/12[ ] | <-- pin MISO microSD 
                 | [ ]RST                   MOSI/11[ ]~| <-- pin MOSI microSD 
                 | [ ]3V3    +---+               10[ ]~|   
+ Anem,SD,LCD--> | [ ]5v    -| A |-               9[ ]~| 
- Anem,SD,LCD--> | [ ]GND   -| R |-               8[ ] |  
                 | [ ]GND   -| D |-                    |
                 | [ ]Vin   -| U |-               7[ ] | 
                 |          -| I |-               6[ ]~|   
                 | [ ]A0    -| N |-               5[ ]~|   
                 | [ ]A1    -| 0 |-               4[ ] |<-- pin CS microSD                     
                 | [ ]A2     +---+           INT1/3[ ]~| 
                 | [ ]A3                     INT0/2[ ] |<-- Señal Anemómetro
                 | [ ]A4/SDA  RST SCK MISO     TX>1[ ] |   
                 | [ ]A5/SCL  [ ] [ ] [ ]      RX<0[ ] |   
                 |            [ ] [ ] [ ]              |
                 |  UNO_R3    GND MOSI 5V  ____________/
                  \_______________________/
*/
#include <LiquidCrystal_I2C.h>// Incluye la libreria LiquidCrystal_I2C para hacer uso del LCD
LiquidCrystal_I2C lcd(0x27,16,2);// declaramos nuestra variable lcd(direccion,nº celdas,nº filas)
#include <EEPROM.h>// Incluye la libreria EEPROM para hacer uso de la EEPROM
#include <SD.h> // Incluye la libreria para la SD
File DatAnem;// creamos un objeto de tipo FILE
boolean sh;// Sentecia Anemometro
unsigned int rev,kmh;// Sentecia Anemometro
float D=1.60,e,v,Cf=(D*PI);// formula del Anemometro
const byte sensHall= 7; // pin de la señal sensor hall. 
long newMilli; // Control de tiempo
byte seg=EEPROM.read(0)+2;// cada vez que
byte hora=EEPROM.read(1); // se inicie nuestra 
byte minuto=EEPROM.read(2);// Arduino toma 
byte dia=EEPROM.read(3);// los datos 
byte mes=EEPROM.read(4); // de la EEPROM
int any=EEPROM.read(5)+2000; // Hay que sumarle 2000 para ver el año en su formato completo
//byte puntDiaSemana=EEPROM.read(6);
byte Mes[12]={31,28,31,30,31,30,31,31,30,31,30,31}; // Tabla de los dias del mes
char tSemana[7]={'L','M','X','J','V','S','D'}; // Tabla de los dias de la semana
char dSemana; // Variable que almacena el dia de la semana
boolean shAnterior;
void setup() {
  Serial.begin(9600);
  lcd.init(); // Inicializamos el LCD
  lcd.backlight(); // Encedemos la iluimnacion del LCD
  lcd.setCursor(0,0);lcd.print("Iniciando...");lcd.clear();
  esbisiesto(any); // determina si el año corriente es o no bisiesto
  diaSemana(); // determina el dia de la semana
  InitSD(); // inicializa la SD 
}

void loop() {
  newMilli=millis()/999; // Asignamos a nuestra varible newMillis() el valor que contiene millis(); contador interno de Arduino
  while((newMilli+1)>(millis()/999)){ // retenemos el flujo un segundo 
    sh = digitalRead(sensHall);// mientras leemos el valor del sensor y se lo asignamos a nuestra variable sh
    if(sh){shAnterior=sh;}// si se detecta el paso del iman igualamos las dos variables para determinar si hay movimiento
    if (!sh && shAnterior){ // si las variables son distintas es que efectivamente hay movimiento
      rev++;// e incrementamos una revolucion
      shAnterior=sh;// volvemos a igualar las variables. Si el iman se detiene frente al
    }// sensor, iria entrando en el if anterior sumando revoluciones falsas a cada segungo
  } // Cierra While Millis 
  actualiza(); // actualizamos los datos
  imprimirLCD();// imprime los datos actualizados a la LCD
 }// Cierra Loop

void actualiza(){
  seg++;//EEPROM.write(0,seg);// sumamos un segundo. Si guardamos cada segundo en la EEPROM no tardara mucho en quemar esa posicion!!!
  if (seg > 59){ // al superar los 60 segundos
    seg=0;EEPROM.write(0,seg);minuto++;EEPROM.write(2,minuto);// Actualizamos seg y minutos y los guardamos en la EEPROM
    kmh=calcVel(rev);//Llama a la funcion calcVel, enviando las rev registradas en un minuto y asignamos el calculo a nuestra variable kmh
    rev=0; // ponemos las revoluciones a cero
    if(minuto<60){Traspas_SD();}// tras cada minuto, exceptuando las :00 salvamos los datos en la SD
  }   
  if (minuto > 59){// al superar la hora
      if (hora < 23){// si son menos de las 23 horas
        minuto=0;EEPROM.write(2,minuto);hora++;EEPROM.write(1,hora);// sumamos una hora y actualizamos la EEPROM
        Traspas_SD();//salvamos los datos en la SD en cada :00 exceptuando la del fin del año
      }else{ // en caso contrario es que estamos en el siguiente dia  
          dia++;EEPROM.write(3,dia);minuto=0;EEPROM.write(2,minuto);hora=0;EEPROM.write(1,hora);//incrementamos un dia y lo guardamos en la EEPROM
          if(dia > Mes[mes-1]){//determinamos en que mes nos encontramos
            dia=1;EEPROM.write(3,dia);mes++;EEPROM.write(4,mes);// incrementamos un mes y lo guardamos en la EEPROM
           }
          if (mes > 12){mes=1;EEPROM.write(4,mes);any++;EEPROM.write(5,any-2000);EEPROM.write(6,EEPROM.read(6)+1);esbisiesto(any);}// cambio de año
          diaSemana();// actualizamos el dia de la semana 
          Traspas_SD();//salvamos los datos en la SD al cambiar de dia y año
        } // cierra else
  }
}
 
//------------------------------------------------------------------------------------------------------------------------------- Metodo Calcula Velocidad en Km/h.
int calcVel(int z){ // recibe las rev
    return ((Cf*z)/60)*3.6;// calcula la la velocidad, dados las caracteristicas  de nuestro anem, establecidas en la declaracion de variables 
}
//------------------------------------------------------------------------------------------------------------------------------- Metodo Impresion a la LCD
void imprimirLCD(){
  lcd.clear(); // Limpiamos la LCD
  if (hora<10){lcd.print("0");} // Añadimos un cero si la hora solo contiene un digito
  lcd.print(hora); // imprime la hora
  lcd.print(':'); // imprime pues eso... dos puntos
  if (minuto<10){lcd.print("0");}// Añadimos un cero si los minutos solo contienen un digito
  lcd.print(minuto); // imprime el minuto
  lcd.print(':');
  if (seg<10){lcd.print("0");}// Añadimos un cero si los segundos solo contiene un digito
  lcd.print(seg); // imprime el minuto
  lcd.print(" "); // imprime la etiqueta para la intensidad
  lcd.print(kmh);// imprime la intensidad
  lcd.print("km/h");
  lcd.setCursor(0,1);// pasamos a la segunda linea del LCD
  lcd.print(dSemana);// imprime el char correspondiente al dia de la semana
  lcd.print(" ");
  if (dia<10){lcd.print("0");} // añadimos un cero si dia solo contiene un digito 
  lcd.print(dia);// imprime el dia
  lcd.print('/');
  if (mes<10){lcd.print("0");}// añadimos un cero si mes solo contiene un digito 
  lcd.print(mes);// imprime el mes
  lcd.print('/');
  lcd.print(any);// imprime el año
  lcd.print(" ");lcd.print(rev);
}
//------------------------------------------------------------------------------------------------------------------------------- Metodo si es bisiesto
void esbisiesto(int any){ // Comprueba si el año que recibe es bisiesto 
  if (any %4==0 && (any%100!=0 || any%400==0)){ // en caso de serlo le asigna al mes de febrero 29 dias
    Mes[1]=29;lcd.print("Bisiesto");lcd.setCursor(0,1);lcd.print("Febrero ");lcd.print(Mes[1]);lcd.print(" dias");//Imprime si es bisiesto el nº de dias de Febrero
  }else{ // en caso de no ser bisiesto asigna a febrero 28 dias
     Mes[1]=28;lcd.print("No Bisiesto");lcd.setCursor(0,1);lcd.print("Febrero ");lcd.print(Mes[1]);lcd.print(" dias");//Imprime si no es bisiesto el nº de dias de Febrero   
     }
  delay(500);lcd.clear();// retiene el flujo 1/2 segundo para poder leer el texto y borra la pantalla  
}
//------------------------------------------------------------------------------------------------------------------------------- Metodo diaSemana
void diaSemana(){// Determina el dia de la semana
  byte puntero=EEPROM.read(6); // sabemos que el 1/1/2019 fue Martes, de ahí que puntero valga 1, que es la posicion de Martes en la tabla tSemana
  byte d=1; // establecemos una variable dia para ir sumando los dias uno a uno
  byte m=1; // establecemos una variable mes para ir sumando los meses uno a uno
  String stfecha=String(dia)+"_"+String(mes);// hacemos un cast y covertimos el d y el m en una string 
  String stF=String(d)+"_"+String(m);// hacemos un cast y covertimos el dia y el mes en una string para ser comparada en el while
  if(stF.equals(stfecha)){dSemana=tSemana[EEPROM.read(6)];// si la fecha es 1/01 se toma el valor para puntero de la EEPROM que ha sido actualizado al cambio de año
  }else{ // de lo contrario hay que buscar el dia y reasignarle el nuevo dia de la semana
     lcd.print("buscando dia ...");
     while(!stF.equals(stfecha)){ // mientras la cadena que irá variando el bucle sea distinta a la fecha actual se sumaran dias
       Serial.print(stF);Serial.print(" ");Serial.print(tSemana[puntero]);Serial.print(" ");Serial.print(stfecha);Serial.print(" ");Serial.println(puntero);
       if(puntero>5){puntero=0;}else{puntero++;}// controlamos el valor de puntero, éste nos señalará la posicion final
       d++;// vamos sumando uno a cada iteracion a la variable d
       if (d > Mes[m-1]){d=1;m++;} // comprobacion para sumar un mes
       if (m>12){m=1;}// comprobacion para que m no supere el 12
       stF=String(d)+"_"+String(m); // a cada iteracion debemos actualizar la string stF para que el bucle la compare con la fecha actual
     }// cierra while
     lcd.clear();  
     dSemana=tSemana[puntero];// establece el dia de la semana en funcion de donde haya quedado el puntero.
     }//cierra else
}// cierra metodo diaSemana

//------------------------------------------------------------------------------------------------------------------------------- Metodo conexion SD
void InitSD(){
    if (!SD.begin(4)){ // si no detecta la SD
      lcd.setCursor(0,0);lcd.print("SD no conectada");delay(500);lcd.clear();// imprime que no la ha detectado
    }else{lcd.setCursor(0,0);lcd.print("SD en Conexion  ");delay(500);lcd.clear();// en caso contrario imprime lo pertinente
      }
    if (!SD.exists("DtAnem")) {//comprueba si el archivo DtAnem existe en la SD
      lcd.setCursor(0, 0); lcd.print("Archivo NO Existe"); delay(500); lcd.clear();// en caso de no existir imprime lo pertinente
    } else {
      lcd.setCursor(0, 0);lcd.print("Archivo OK");// si existe imprime lo propio
      delay(500);
      lcd.clear();// Vacia la pantalla
  }  
}
//------------------------------------------------------------------------------------------------------------------------------- Metodo traspaso a la SD
void Traspas_SD(){ 
     SD.begin(4);//cada vez que saquemos la tarjeta se pierde la conexion, al iniciar la SD en cada llamada nos aseguramos que los datos se guardaran 
     DatAnem = SD.open("DtAnem",FILE_WRITE); // Abre el archivo DtAnem en modo lectura-escritura
     DatAnem.print(dSemana);DatAnem.print(" ");// imprime en el archivo el dia de la semana
     DatAnem.print(dia);DatAnem.print("/");DatAnem.print(mes);DatAnem.print("/");DatAnem.print(any);DatAnem.print(" ");// seguido de la fecha
     DatAnem.print(hora);DatAnem.print(":");DatAnem.print(minuto);DatAnem.print(":");DatAnem.print("0");DatAnem.print(seg);DatAnem.print(" ");// la hora
     DatAnem.println(kmh);// y el dato de velocidad, todo en la misma linea. 
     DatAnem.close();// cierra el archivo
     lcd.clear();// limpia el LCD
}

El valor mas alto que mi anemómetro ha registrado ha sido de 91km/h. ¿Cual habrá sido la racha en ese minuto?

Para que nuestro anemómetro pueda obtener el detalle de las rachas, será necesario realizar alguna modificación, que puede hacer peligrar su fiabilidad. Por si no te has percatado para obtener la medición de la intensidad del viento, el factor tiempo es tan vital como destructivo, el programa debe  ser capaz de leer decenas de veces por segundo el tiempo transcurrido entre una revolución y otra, almacenar esa información y procesarla. Esto como digo, va a requerir realizar pruebas de velocidad sobre el flujo, el volumen de datos que se va a generar va a ser importante dadas las características de una Arduino, aunque te anticipo que tengo una versión que lo resuelve... una pista... Arduinos en TX RX.