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.

sábado, 1 de junio de 2019

Anemómetro con sensor efecto Hall 44E402 Introducción

Cuando decidí hacerme la estación meteorológica, consideré comenzar por este elemento ya que me parecía sencillo...menudo patinazo!!!

El modelo de anemómetro que aquí vamos a tratar emplea un sistema ampliamente utilizado en las estaciones meteorológicas comerciales.
Se trata de un sistema formado por tres o más cazoletas que son empujadas por el viento haciendo rodar el conjunto sobre un eje. En un punto del sistema giratorio, se coloca un imán que girará solidariamente con el eje, paralelo al eje se fija un sensor de efecto Hall (en nuestro caso el 44E402 ó A3144) de manera que al encararse el imán frente al sensor, éste nos dará una señal, lo que interpretamos como una revolución, y aplicando el factor tiempo obtenemos la velocidad.

Pero el sistema de cazoletas ó cubetas rotativas no es el único sistema para medir la velocidad del viento ni tampoco el mas eficiente como veremos mas adelante.

Anemómetro rotativo
Otro sistema que se suele aplicar a las veletas comerciales últimamente es el sistema ultrasónico.
Este sistema consta de uno o dos emisores ultrasónicos y sus correspondientes receptores.

Anemómetro ultrasónico
Anemómetro ultrasonico
fuente: www.darrera.com

Esencialmente su funcionamiento es el siguiente, el emisor emite una frecuencia constantemente que es captada por el receptor, pero esta señal se demora cierto tiempo, en ausencia de viento la demora es la mínima posible, mientras que a medida que el viento aumenta (y que atraviesa el espacio entre emisor y receptor) la señal tarda mas tiempo en alcanzar el receptor.
Estos sistemas suelen emplear dos emisores y receptores promediando las dos señales para obtener la intensidad. Sería perfectamente posible y mas sencillo de programar, un sistema con un solo emisor-receptor pero habría que mantenerlo encarado al viento (aprovechando quizás el sistema de la veleta)

También existen otros sistemas como serían, el Anemómetro de compresión, el Anemómetro de hilo caliente y el Anemómetro de empuje.

De todos ellos, el Anemómetro de empuje considero que es el más adecuado y eficiente para la medición de la intensidad del viento, y más aún aquí en Menorca, dada su particularidad racheada.

La intensidad del viento no es ni de lejos constante, y este comportamiento varia mucho en función del lugar. En el diseño de implantaciones de energía eólica, se debe tener muy en cuenta este comportamiento, existen lugares como La muela en Zaragoza donde la intensidad del viento se comporta de manera muy estable.

Menorca se encuentra en una zona altamente inestable, tal es así que en ciertas tormentas las "rachas" pueden alcanzar el triple de la intensidad "sostenida" y con una duración de uno a tres segundos.

Si deseamos, la mas alta precisión, para registrar este comportamiento aquí en Menorca, no nos queda otra que diseñar un sistema de medición (Anemómetro de empuje) capaz de reaccionar ¡al segundo! y que la aplicación permita registrar los datos a esa velocidad.

Imagino que ya vais entendiendo lo de mi patinazo.

Para la Estación Meteorológica que nos concierne os propongo el sistema tradicional (rotativo) que es relativamente sencillo de realizar y programar, con ello podemos obtener picos de intensidad y viento sostenido con una precisión baja para lugares "Poco estables" y alta para lugares "Estables"

Las dos Estaciones Meteorológicas que yo he fabricado emplean el módulo Keyes 44E402 como sensor de revoluciones que integra el transistor 44E402.



Este módulo es muy eficiente, duradero (2 años aprox.) y económico, entre 0,30 y 1€

En su lugar podemos emplear el transistor A3144 que se comporta igual que el anterior, con la siguiente conexión:


En la implantación física necesitamos un imán que haga reaccionar el transistor a su paso, yo compré imanes de neodimio en Aliexpress, de tipo botón y perforado, son muy potentes y fáciles de fijar
(por el agujero).

Os dejo unas imágenes para que veáis como he colocado estos dos elementos. La primera es una vista general...


A continuación os muestro el módulo fuera de su caja protectora...


Para que el módulo este protegido de las inclemencias climáticas he tenido que vaciar un pedazo de metacrilato, lo prefiero transparente por que este módulo posee un led rojo que se enciende al paso del imán, y me permite visualizar su funcionamiento...


Ha sido bastante entretenido vaciar el pedacito de metacrilato así como hacerle las ranuras laterales para que el modulo encaje...



en la siguiente vemos el Led encendido al tener encarado el imán...


Bueno, una vez vista la configuración física, vamos a pasar a la programación, el siguiente Sketch es un programa básico para determinar el funcionamiento del módulo, pero antes os muestro el diagrama de conexiones para el módulo Keyes 44E402:


y este con el transistor A3344:



/* Sensor Hall digital A3144 o modulo 44E402, Compilacion Arduino 1.8.1, 09/03/2019
ESQUEMA DE CONEXIONES
                                          +-----+
              ____[PWR]___________________| USB |__
             |                            +-----+  |
             |         GND/RST2  [ ][ ]            |
             |       MOSI2/SCK2  [ ][ ]  A5/SCL[ ] |  
             |          5V/MISO2 [ ][ ]  A4/SDA[ ] |      
             |                             AREF[ ] |
             |                              GND[ ] |
             | [ ]N/C                    SCK/13[ ] | 
             | [ ]IOREF                 MISO/12[ ] |
             | [ ]RST                   MOSI/11[ ]~|
             | [ ]3V3    +---+               10[ ]~|
Positivo-->  | [ ]5v    -| A |-               9[ ]~|
Negativo-->  | [ ]GND   -| R |-               8[ ] |   
             | [ ]GND   -| D |-                    |
             | [ ]Vin   -| U |-               7[ ] |
             |          -| I |-               6[ ]~|
             | [ ]A0    -| N |-               5[ ]~|
             | [ ]A1    -| O |-               4[ ] |   
             | [ ]A2     +---+           INT1/3[ ]~|  
             | [ ]A3                     INT0/2[ ] | <-- Señal transistor A3144 o modulo 44E402 
             | [ ]A4/SDA  RST SCK MISO     TX>1[ ] |   
             | [ ]A5/SCL  [ ] [ ] [ ]      RX<0[ ] |   
             |            [ ] [ ] [ ]              |
             |  UNO_R3    GND MOSI 5V  ____________/
              \_______________________/
     */
#define Hall_pin 2 // Definimos el pin 2 como receptor de la señal
int revolucion; // variable que acumulará las revoluciones
void setup(){
    Serial.begin(9600);// Iniciamos la consola                    
    pinMode(Hall_pin,INPUT); // declaramos el pin de la señal como entrada
}

void loop(){
    byte magnetico = digitalRead(Hall_pin); // Asignamos a la variable magnetico el valor del pin 2
    if(magnetico == LOW){ // Cuidado! nos devuelve TRUE al no percibir nada y False al detectar el campo magnetico. Imagino que será por el PullUp-PullDdown
        Serial.print("Revolucion: "); Serial.println(revolucion); // imprimimos por consola el texto Revolucion junto con el valor 
        revolucion++; // incrementamos en una la variable a cada revolucion
        delay(250); // esperamos cierto tiempo; por el rebote electrico que será nuestro mayor problema al programar el anemómetro
    }
}

Este código nos servirá para comprobar el funcionamiento del sensor, nos basta con pasarle el imán frente al sensor para que vaya sumando una revolución, nosotros debemos convertir eso en velocidad, y para ello debemos conocer el radio ó diámetro de las cazoletas y así establecer la distancia recorrida, además la fórmula de velocidad, requiere el tiempo transcurrido, en general se define por la formula Velocidad=Espacio/Tiempo, y digo en general por que habría que contemplar otros aspectos, por ejemplo si el sistema de cazoletas que has diseñado solo tiene tres de ellas se deben aplicar fórmulas mas concretas, y además habría que tener en cuenta la densidad del aire en cada momento, yo no me voy a meter en berenjenales de momento...

La circunferencia que dibuja nuestro sistema de cazoletas tiene una distancia que conoceremos con su formula: L=2x π x R; ó Pi por Radio al cuadrado.
Y para establecer el tiempo vamos ha echar mano de nuestro diseño final de RelojBasico con LCD

Ahora hay que hacer funcionar el conjunto con una LCD, y realizar las pruebas iniciales de nuestro anemómetro
Las cazoletas deben instalarse sobre un eje con al menos un cojinete y este conjunto debe girar lo más fino posible, en función de esa finura obtendremos nuestra velocidad minina y que difícilmente detectará valores entre 0,5 km/h. y 1 km/h. aunque estaremos de acuerdo que esas velocidades poco nos importan...
Como decía cada uno debe ir afinando su velocidad mediante prueba-ensayo, y modificar la fórmula de velocidad (yo modifico la longitud) hasta que nuestros resultados se asemejen a los ofrecidos por ejemplo por la meteo del lugar, o con una estación comercial de algún conocido,  también existen anemómetros que se conectan al smartphone, ó anemómetros portátiles  (que son mas económicos) no necesitamos que sea demasiado precisos, pero nos serviría para establecer un punto de partida.

Aunque el problema mas espinoso con el que me topé fue crear el algoritmo "perfecto" para la obtención de las revoluciones en todas las circunstancias, viento fuerte, flojo o nulo, sólo que para vosotros este aspecto ya os lo doy resuelto...

Los problemas básicamente fueron dos:
   - Cada vez que el imán pasa frente al sensor, se produce un rebote eléctrico también conocido                como debouncing, Luis Llamas lo explica muy bien AQUI.
   - En función de la intensidad del viento, el imán permanece mas o menos tiempo frente al sensor
   - Si el viento es nulo o prácticamente nulo y el imán se queda parado frente al sensor, fua!

después de muchos días y unas cuantas versiones conseguí salvarlo todo con un par de While's...en fin...,

El siguiente Sketch nos permite comprobar el funcionamiento del Anemómetro, su salida es por Serial, de manera que se requiere un ordenador para comprobar si su salida es correcta:


// Version compilada con Arduino 1.8.1, 15/03/2019
/* ESQUEMA DE CONEXIONES
                                          +-----+
              ____[PWR]___________________| USB |__
             |                            +-----+  |
             |         GND/RST2  [ ][ ]            |
             |       MOSI2/SCK2  [ ][ ]  A5/SCL[ ] |  
             |          5V/MISO2 [ ][ ]  A4/SDA[ ] |      
             |                             AREF[ ] |
             |                              GND[ ] |
             | [ ]N/C                    SCK/13[ ] | 
             | [ ]IOREF                 MISO/12[ ] |
             | [ ]RST                   MOSI/11[ ]~|
             | [ ]3V3    +---+               10[ ]~|
Positivo-->  | [ ]5v    -| A |-               9[ ]~|
Negativo-->  | [ ]GND   -| R |-               8[ ] |   
             | [ ]GND   -| D |-                    |
             | [ ]Vin   -| U |-               7[ ] | <-- Señal transistor A3144 o modulo 44E402 
             |          -| I |-               6[ ]~|
             | [ ]A0    -| N |-               5[ ]~|
             | [ ]A1    -| O |-               4[ ] |   
             | [ ]A2     +---+           INT1/3[ ]~|  
             | [ ]A3                     INT0/2[ ] | 
             | [ ]A4/SDA  RST SCK MISO     TX>1[ ] |   
             | [ ]A5/SCL  [ ] [ ] [ ]      RX<0[ ] |   
             |            [ ] [ ] [ ]              |
             |  UNO_R3    GND MOSI 5V  ____________/
              \_______________________/
     */

boolean sh, fileWrite=true;// Sentecia Anemometro
unsigned int rev,kmh;// Sentecia Anemometro
float D=1.60,e,v,Cf=(D*PI);// Sentecia Anemometro.
const byte sensHall= 7; // Sentecia Anemometro. 
long newMilli; // Control de tiempo
void setup(){
  Serial.begin(9600);
}

void loop() {
  newMilli=millis()/999; // Asignamos a nuestra varible newMillis() el valor que contiene millis(); contador interno de Arduino
  while(newMilli+60>millis()/999){ // 
    sh = digitalRead(sensHall);// Leemos el valor del sensor y se lo asignamos a nuestra variable sh
    if (sh){ // si el valor es 1 o true se accede al condicional
      rev++; // incrementa en uno el valor de rev.
      while(sh && newMilli+60>millis()/999){//este while evita que contador aumente si el Iman ha quedado frente al sensor,
        sh = digitalRead(sensHall); //cuando sh sea false ó se superen los 999 ms saldrá del bucle
      }//Cierra while sh
    }// cierra if sh
  } // Cierra While Millis 
  Serial.print("rev: ");Serial.println(rev);
  kmh=calcVel(rev);
  rev=0;
Serial.print("kmh: ");Serial.println(kmh);
}

int calcVel(int z){ // recibe las rev
    return ((Cf*z)/60)*3.6;// calcula la la velocidad, dadas las caracteristicas de nuestro anem. establecidas en la declaracion de variables 
}

sábado, 4 de mayo de 2019

Calcular el día de la Semana

En esta breve entrada veremos dos formas para determinar el día de la semana...
la primera forma es un Sketch de diseño propio, que requiere un dato de referencia para calcular el día de la semana durante el año-
Este primer Sketch es poco útil como tal, si bien contiene conversiones de valores numéricos a String y viceversa  que nos podrán valer como referencia en caso de requerir este tipo de Casting.

// Compilado con Arduino 1.8.1, 1/03/2019
byte Mes[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // Array de tipo byte, con los dias totales de cada mes del año
char tSemana[7] = {'L', 'M', 'X', 'J', 'V', 'S', 'D'}; // Array de tipo char, para visualizar el dia de la semana
char dSemana;// Variable tipo char, almacena el char correspondiente del array anterior
byte dia = 1;// Variable tipo byte, indicamos el dia del mes que deseamos conocer su equivalente en dia de semana
byte mes = 6;// Variable de tipo byte, indicamos el mes para el dia que deseamos conocer el dia de la semana
void setup() {
  Serial.begin(9600);
  diaSemana();// llamada a la funcion que calcula el dia de la semana
  Serial.println(dSemana);// Imprimimos el dia de la semana resultante
}

void loop() {}

void diaSemana() {
  byte puntero = 1; //Esta variable señala la pocicion de tSemana, debe coincidir con el primer dia del año a consultar.
                    //0=Lunes, 1=Martes, 2=Miercoles... el primer dia del año 2019 fue Martes, por tanto puntero debe valer 1
                    //El primer dia del año 2020 será Miércoles y puntero debera actualizarse a 3 ...
  byte d = 1;// variable para el dia de punto de partida para el calculo del dia de la semana (deberia ser una constante) 
  byte m = 1;//variable para el mes de partida para el calculo del dia de la semana (deberia ser una constante) 
  String stfecha = String(dia) + "_" + String(mes); // se realizan dos castings de byte a string y se concatenan los dos en una nueva String
  String stF = String(d) + "_" + String(m);// al igual que el anterior, las dos Strings resultantes podran ser comparadas en el While
  while (!stF.equals(stfecha)) {// Mientras la fecha de partida no coincida con la fecha introducida el bucle se repite
    Serial.print("stF: "); Serial.print(stF); Serial.print(" stfecha: "); Serial.println(stfecha); // imprime lo comparado en cada iteración
    if (puntero > 5) {// si puntero vale mas de 5, o sea 6, es que ha llegado al final del array tSemana 
      puntero = 0;// entonces puntero se situa nuevamente al principio del array
    } else {
      puntero++; // de lo contrario, la variable puntero va tomando un nuevo valor del array tSemana en cada iteración
    }
    d++; // se suma uno a cada iteracion
    if (d > Mes[m - 1]) {//si el valor de la variable supera el valor del Array Mes...
      d = 1;// dia se establece en 1
      m++; // y mes se incrementa
    }
    stF = String(d) + "_" + String(m);// se actualiza el valor de la String stF para ser comparada nuevamente en la siguiente iteracion
  }
  dSemana = tSemana[puntero];// la variable dSemana toma el valor del Array tSemana con la posicion que le indica puntero.
}

El resultado para el 1 de Junio de 2019 es S (Sábado):




Si bien conocemos la "Congruencia de Zeller"  de la que se puede establecer un algoritmo que  nos permita realizar dicho calculo de manera mas sencilla y práctica para nuestros proyectos, el siguiente Sketch así lo demuestra:

// Algorito basado en la Congruencia de Zeller, Compilado con Arduino 1.8.1, 1/03/2019
// by @quark https://forum.processing.org/two/discussion/3350/getting-the-day-of-the-week-without-calendar
char tSemana[7] = {'D', 'L', 'M', 'X', 'J', 'V', 'S'}; // Array de tipo char, para visualizar el dia de la semana
void setup(){ 
  Serial.begin(9600);
  Serial.println(tSemana[dow(1,7,2019)]);// Llama a la funcion al tiempo que imprime el resultado
}

void loop() {
}

int dow(int d, int m, int y) { 
  if (m < 3) {
    m += 12;
    y--;
  }
  return ((d + int((m+1)*2.6) +  y + int(y/4) + 6*int(y/100) + int(y/400) + 6) % 7);
}

Atención, este algoritmo ordena los días de la semana en plan Americano, de manera que el primer día de la semana es Domingo!!! y así debe establecerse en el Array tSemana.