Estacao Meteorologica: mudanças entre as edições
Linha 163: | Linha 163: | ||
==Código para Arduíno== | ==Código para Arduíno== | ||
<source lang="c"> | |||
/* | |||
Estação Meteorológica com Microcontrolador Arduino | |||
2015-2016 - Instituto Federal do Paraná | |||
Programadores: José Sallet | |||
Thiago Henrique Ribeiro | |||
Orientador: Evandro Cantú | |||
Mudanças em 2016: | |||
12/04/2016 - Inclusão da função de leitura de vento por pulseIn() | |||
14/04/2016 - Carregar strings literais da memória flash -> Serial.println(F("String literal)); | |||
- Inclusão dos sensores LM35 (temperatura) e DHT11 (temperatura, umidade) | |||
- Testes com anemômetro e biruta com soprador de ar | |||
*/ | |||
//Estacao de Testes - Baseado no codigo EstacaoMeteoCompleta | |||
//Teste de adição de sensores LM35, DHT11 e nova rotina de leitura de vento baseada em PulseIn | |||
#include<string.h> | |||
//incluindo biblioteca do sensor de temperatura e pressao DHT11 | |||
#include <dht11.h> | |||
//incluindo biblioteca do sensor de pressão | |||
#include <SFE_BMP180.h> | |||
//biblioteca do protocolo I2C | |||
#include <Wire.h> // biblioteca de comunicação I2C - necessária para o sensor barométrico BMP180 | |||
#define ALTITUDE 180.0 // Altitude de Foz do Iguaçu, Parana, Brasil | |||
#define BASETEMPO 10 // define o tempo de repeticao | |||
SFE_BMP180 sensorPressao; //inicializando sensor de temperatura e pressão | |||
dht11 sensorUmidade; //inicializando sensor DHT11 | |||
const int PINO_BIRUTA = A0; //pino analógico A0 - Leitura da tensão da biruta | |||
const int PINO_LM35 = A1; //pino analógico A1 - Leitura do sensor LM35 (temperatura) | |||
const int PINO_DHT11 = A2; //pino analógico A2 - Leitura do sensor DHT11 (temperatura + umidade) | |||
//pino analógico A3 livre | |||
//pino analógico A4 usado no BMP180 (temperatura + pressão) | |||
//pino analógico A5 usado no BMP180 (temperatura + pressão) | |||
const int BUFFER_SIZE = 1000; //quantidade de leituras do sensor LM35 para fazer a média | |||
//para reduzir a margem de erro | |||
const float CONVERSAO_GRAUS_CELSIUS = 0.488758553; //constante para calcular a temperatura do sensor LM35 | |||
//int Tensao; //tensão de leitura da biruta - comentado para invocar em loop() | |||
float mmChuva = 0; //pluviômetro - Isto deveria ser gravado em EEPROM | |||
//int contVento = 0; //contador de pulsos de vento - substituído por pulseIn | |||
//float velVento; //velocidade do vento - comentado para invocar em loop | |||
//-------------------------------------------------------------------------------------------------------------- | |||
// ROTINA DE INTERRUPÇÃO 0 - ANEMÔMETRO (obsoleta) | |||
//-------------------------------------------------------------------------------------------------------------- | |||
//void vento(){ | |||
// contVento++; | |||
// } | |||
//-------------------------------------------------------------------------------------------------------------- | |||
// ROTINA DE INTERRUPÇÃO 1 - PLUVIÔMETRO | |||
//-------------------------------------------------------------------------------------------------------------- | |||
void choveu() { | |||
mmChuva = mmChuva + 0.2794; | |||
if (mmChuva > 1000) | |||
{ | |||
mmChuva = mmChuva - 1000; | |||
} | |||
} | |||
//-------------------------------------------------------------------------------------------------------------- | |||
// ANEMÔMETRO (versão nova sem interrupção) | |||
//-------------------------------------------------------------------------------------------------------------- | |||
float velVento(){ | |||
//Medição de largura do pulso em microsegundos | |||
unsigned long larguraPulso = pulseIn(2, LOW, 2400000); //um pulso de 2400000 ms equivale a um vento de 1km/h | |||
float _velVento = 0; | |||
//calcular Velocidade de acordo com a largura do pulso; Largura de pulso mínima a ser calculada | |||
if (larguraPulso <= 5000) { | |||
byte retry = 2; | |||
//se houver uma leitura inválida (valores absurdos devido a erros), mais duas tentativas são feitas. | |||
while((retry > 0) && (larguraPulso <= 5000)){ | |||
retry--; | |||
larguraPulso = pulseIn(2,LOW, 2400000); | |||
} | |||
} | |||
if(larguraPulso > 5000){ | |||
_velVento = 2.4 * (1000000 / (float) larguraPulso); | |||
}else{ | |||
_velVento = 0; | |||
} | |||
return _velVento; | |||
} | |||
//-------------------------------------------------------------------------------------------------------------- | |||
// BIRUTA (Sparkfun) | |||
//-------------------------------------------------------------------------------------------------------------- | |||
String biruta(int Tensao) | |||
{ | |||
String direcao; | |||
if (Tensao >= 744 && Tensao <= 805) { | |||
//Serial.println(F("Direcao do Vento: Leste")); | |||
direcao = "L"; | |||
} | |||
else if (Tensao >= 346 && Tensao <= 432) { | |||
//Serial.println(F("Direcao do Vento: Leste-Norderste")); | |||
direcao = "LNE"; | |||
} | |||
else if (Tensao >= 433 && Tensao <= 529) { | |||
//Serial.println(F("Direcao do Vento: Nordeste")); | |||
direcao = "NE"; | |||
} | |||
else if (Tensao >= 75 && Tensao <= 87) { | |||
//Serial.println(F("Direcao do Vento: Norte-Nordeste")); | |||
direcao = "NNE"; | |||
} | |||
else if (Tensao >= 88 && Tensao <= 109) { | |||
//Serial.println(F("Direcao do Vento: Norte")); | |||
direcao = "N"; | |||
} | |||
else if (Tensao >= 0 && Tensao <= 74) { | |||
//Serial.println(F("Direcao do Vento: Norte-Noroeste")); | |||
direcao = "NNO"; | |||
} | |||
else if (Tensao >= 156 && Tensao <= 213) { | |||
//Serial.println(F("Direcao do Vento: Noroeste")); | |||
direcao = "NO"; | |||
} | |||
else if (Tensao >= 110 && Tensao <= 155) { | |||
//Serial.println(F("Direcao do Vento: Oeste-Noroeste")); | |||
direcao = "ONO"; | |||
} | |||
else if (Tensao >= 265 && Tensao <= 345) { | |||
//Serial.println(F("Direcao do Vento: Oeste")); | |||
direcao = "O"; | |||
} | |||
else if (Tensao >= 214 && Tensao <= 264) { | |||
//Serial.println(F("Direcao do Vento: Oeste-Sudoeste")); | |||
direcao = "OSO"; | |||
} | |||
else if (Tensao >= 615 && Tensao <= 665) { | |||
//Serial.println(F("Direcao do Vento: Sudoeste")); | |||
direcao = "SO"; | |||
} | |||
else if (Tensao >= 530 && Tensao <= 614) { | |||
//Serial.println(F("Direcao do Vento: Sul-Sudoeste")); | |||
direcao = "SSO"; | |||
} | |||
else if (Tensao >= 886 && Tensao <= 961) { | |||
//Serial.println(F("Direcao do Vento: Sul")); | |||
direcao = "S"; | |||
} | |||
else if (Tensao >= 806 && Tensao <= 885) { | |||
//Serial.println(F("Direcao do Vento: Sul-Sudeste")); | |||
direcao = "SSE"; | |||
} | |||
else if (Tensao >= 962 && Tensao <= 1023) { | |||
//Serial.println(F("Direcao do Vento: Sudeste")); | |||
direcao = "SE"; | |||
} | |||
else if (Tensao >= 666 && Tensao <= 743) { | |||
//Serial.println(F("Direcao do Vento: Leste-Sudeste")); | |||
direcao = "LSE"; | |||
} | |||
else{ | |||
Serial.println(F("[ERRO] Leitura na biruta fora da faixa!")); | |||
direcao = "ERR"; | |||
} | |||
return direcao; | |||
} | |||
//-------------------------------------------------------------------------------------------------------------- | |||
// SENSOR DE TEMPERATURA LM35 | |||
//-------------------------------------------------------------------------------------------------------------- | |||
//ler temperatura do sensor LM35 uma única vez | |||
float temperaturaLM35() { | |||
return analogRead(PINO_LM35) * CONVERSAO_GRAUS_CELSIUS; | |||
} | |||
//ler temperatura do sensor LM35 (BUFFER_SIZE) vezes e calcular a média para obter uma medição mais precisa | |||
//aumentar o valor de BUFFER_SIZE aumenta a precisão mas aumenta a carga do sistema | |||
float temperaturaLM35Buffer() { | |||
float buffer = 0; | |||
for (int i = 0; i < BUFFER_SIZE ; i++) { | |||
buffer += analogRead(PINO_LM35); | |||
} | |||
return ((buffer) / BUFFER_SIZE) * CONVERSAO_GRAUS_CELSIUS; | |||
} | |||
//-------------------------------------------------------------------------------------------------------------- | |||
// CÁLCULO DE PONTO DE ORVALHO | |||
// Funções de ponto de orvalho obtidas em http://playground.arduino.cc/Main/DHT11Lib | |||
// A fazer: Verificar e referenciar o código apropriadamente | |||
// O código abaixo foi copiado e usado sem alterações | |||
//-------------------------------------------------------------------------------------------------------------- | |||
// dewPoint function NOAA | |||
// reference (1) : http://wahiduddin.net/calc/density_algorithms.htm | |||
// reference (2) : http://www.colorado.edu/geography/weather_station/Geog_site/about.htm | |||
// | |||
double dewPoint(double celsius, double humidity) | |||
{ | |||
// (1) Saturation Vapor Pressure = ESGG(T) | |||
double RATIO = 373.15 / (273.15 + celsius); | |||
double RHS = -7.90298 * (RATIO - 1); | |||
RHS += 5.02808 * log10(RATIO); | |||
RHS += -1.3816e-7 * (pow(10, (11.344 * (1 - 1 / RATIO ))) - 1) ; | |||
RHS += 8.1328e-3 * (pow(10, (-3.49149 * (RATIO - 1))) - 1) ; | |||
RHS += log10(1013.246); | |||
// factor -3 is to adjust units - Vapor Pressure SVP * humidity | |||
double VP = pow(10, RHS - 3) * humidity; | |||
// (2) DEWPOINT = F(Vapor Pressure) | |||
double T = log(VP / 0.61078); // temp var | |||
return (241.88 * T) / (17.558 - T); | |||
} | |||
// delta max = 0.6544 wrt dewPoint() | |||
// 6.9 x faster than dewPoint() | |||
// reference: http://en.wikipedia.org/wiki/Dew_point | |||
double dewPointFast(double celsius, double humidity) | |||
{ | |||
double a = 17.271; | |||
double b = 237.7; | |||
double temp = (a * celsius) / (b + celsius) + log(humidity * 0.01); | |||
double Td = (b * temp) / (a - temp); | |||
return Td; | |||
} | |||
// **Fim das funções de orvalho copiadas** | |||
//-------------------------------------------------------------------------------------------------------------- | |||
// SENSOR DE PRESSÃO E TEMPERATURA (BMP180) | |||
//-------------------------------------------------------------------------------------------------------------- | |||
double temperaturaBMP180(SFE_BMP180 sensor) { | |||
double T; | |||
char status = sensor.startTemperature(); | |||
if (status != 0) { | |||
delay(status); | |||
status = sensor.getTemperature(T); | |||
if (status == 0) { | |||
Serial.println(F("[AVISO] Falha na aquisição de temperatura do sensor BMP180. Tentando novamente...")); | |||
return temperaturaBMP180(sensor); // recursivamente faz uma nova tentativa | |||
} | |||
} else { | |||
Serial.println(F("[AVISO] Falha na preparação do sensor de temperatura BMP180. Tentando novamente...")); | |||
return temperaturaBMP180(sensor); // recursivamente faz uma nova tentativa | |||
} | |||
return T; | |||
} | |||
double pressaoBMP180(double temperatura, SFE_BMP180 sensor) { | |||
double pressao; | |||
char status = sensor.startPressure(3); | |||
if (status != 0) { | |||
delay(status); | |||
status = sensor.getPressure(pressao, temperatura); | |||
if (status == 0) { | |||
//erro em getPressure | |||
Serial.println(F("[ERRO] Falha na aquisição da temperatura do sensor BMP180. Tentando novamente...")); | |||
return pressaoBMP180(temperatura, sensor); //nova tentativa usando recursão | |||
} | |||
} else { | |||
//erro em startPressure | |||
Serial.println(F("[ERRO] Falha na preparação do sensor de pressão BMP180. Tentando novamente...")); | |||
return pressaoBMP180(temperatura, sensor); // nova tentativa usando recursão | |||
} | |||
return pressao; | |||
} | |||
// cálculo da pressão relativa ao nível do mar | |||
double pressaoNivelMar(double pressao, double altura, SFE_BMP180 sensor) { | |||
return sensor.sealevel(pressao, altura); | |||
} | |||
// cálculo da altitude | |||
double altitude(double pressao, double pressao_nivel_mar, SFE_BMP180 sensor) { | |||
return sensor.altitude(pressao, pressao_nivel_mar); | |||
} | |||
//-------------------------------------------------------------------------------------------------------------- | |||
// TELEMETRIA - MEMÓRIA LIVRE NO SISTEMA | |||
//-------------------------------------------------------------------------------------------------------------- | |||
int freeRam () { | |||
extern int __heap_start, *__brkval; | |||
int v; | |||
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); | |||
} | |||
//-------------------------------------------------------------------------------------------------------------- | |||
// SETUP (Preparação) | |||
//-------------------------------------------------------------------------------------------------------------- | |||
void setup() | |||
{ | |||
Serial.begin(9600); //define a frequencia | |||
attachInterrupt(1, choveu, RISING); // interrupcao do pluviometro | |||
Serial.print(F("\nInicializando sensor barometrico BMP180... ")); | |||
// Initialize the sensor (it is important to get calibration values stored on the device). | |||
if (sensorPressao.begin()) | |||
Serial.println(F("[OK]")); | |||
else | |||
{ | |||
// Oops, something went wrong, this is usually a connection problem, | |||
// see the comments at the top of this sketch for the proper connections. | |||
Serial.println(F("[FAIL]\n\nA estação não pode ser inicializada. Verifique o sensor barométrico e reinicie o aparelho.")); | |||
while (1); // Pause forever. | |||
} | |||
} | |||
//-------------------------------------------------------------------------------------------------------------- | |||
// PROGRAMA PRINCIPAL | |||
//-------------------------------------------------------------------------------------------------------------- | |||
//repeticao que imprime as informacoes do programa ao usuario | |||
//TODO: organizar a ordem de execução levando em consideração o tempo tolerado por cada sensor | |||
void loop() | |||
{ | |||
/* | |||
* Ações do programa: | |||
* 1. Obter leitura do sensor de umidade (só pode ser lido a cada 2 segundos, são dois valores inteiros) | |||
* 2. Obter leituras de tensão da biruta | |||
* 3. Obter leituras do anemômetro | |||
* 4. Obter leituras de temperatura do sensor LM35 | |||
* 5. Obter leituras de temperatura e pressão do sensor BMP180 | |||
*/ | |||
unsigned long inicio = millis(); // millis() e micros() - medem o tempo corrido em milisegundos e microsegundos | |||
sensorUmidade.read(PINO_DHT11); | |||
double altitude_local = (double)ALTITUDE; //adquire a altitude do local | |||
float temperatura1 = (float) sensorUmidade.temperature; //adquire temperatura do sensor DHT11 | |||
float umidade = (float) sensorUmidade.humidity; //adquire umidade do sensor DHT11 | |||
int tensaoBiruta = analogRead(PINO_BIRUTA); //adquire o valor da tensão na biruta | |||
String direcao = biruta(tensaoBiruta); //calcula a direção da biruta | |||
float velocidadeVento = velVento(); //adquire a velocidade do vento | |||
float temperatura2 = temperaturaLM35Buffer(); //adquire temperatura do sensor LM35 | |||
double temperatura3 = temperaturaBMP180(sensorPressao); //adquire temperatura do sensor BMP180 | |||
double pressaoAbsoluta = pressaoBMP180(temperatura3, sensorPressao); //adquire pressão absolutado sensor BMP180 | |||
double pressaoRelativa = pressaoNivelMar(pressaoAbsoluta, altitude_local, sensorPressao); //calcula pressão relativa | |||
double altitudeCalculada = altitude(pressaoAbsoluta, pressaoRelativa, sensorPressao); //calcula altitude | |||
double pontoOrvalho = dewPoint(temperatura2, umidade); //calcula ponto de orvalho | |||
unsigned long fim = millis(); //fim da cronometração da função | |||
unsigned long tempo = fim - inicio; //cálculo do tempo de execução | |||
if(tempo < 2000){ | |||
delay(2000 - tempo); //cálculo do tempo de espera | |||
} //(não pode ser menor que 2s) | |||
// exibir dados na tela ou terminal serial | |||
Serial.println(F("================================================================================================")); | |||
Serial.print(F("Temperatura [DHT11]:\t\t\t")); | |||
Serial.print(temperatura1); | |||
Serial.println(F(" graus Celsius")); | |||
Serial.print(F("Temperatura [LM35]\t\t\t")); | |||
Serial.print(temperatura2); | |||
Serial.println(F(" graus Celsius")); | |||
Serial.print(F("Temperatura [BMP180]\t\t\t")); | |||
Serial.print(temperatura3); | |||
Serial.println(F(" graus Celsius")); | |||
Serial.print(F("Velocidade do vento:\t\t\t")); | |||
Serial.print(velocidadeVento); | |||
Serial.println(F(" km/h")); | |||
Serial.print(F("Direcao do vento:\t\t\t")); | |||
Serial.print(direcao); | |||
Serial.println(F("")); | |||
Serial.print(F("Pressao absoluta (local):\t\t")); | |||
Serial.print(pressaoAbsoluta); | |||
Serial.println(F(" milibar")); | |||
Serial.print(F("Pressao relativa (nivel do mar):\t")); | |||
Serial.print(pressaoRelativa); | |||
Serial.println(F(" milibar")); | |||
Serial.print(F("Altitude:\t\t\t\t")); | |||
Serial.print(altitudeCalculada); | |||
Serial.println(F(" metros")); | |||
Serial.print(F("Chuva acumulada:\t\t\t")); | |||
Serial.print(mmChuva); | |||
Serial.println(F(" mm")); | |||
Serial.print(F("Umidade relativa do ar:\t\t\t")); | |||
Serial.print(umidade); | |||
Serial.println(F("%")); | |||
Serial.print(F("Ponto de orvalho:\t\t\t")); | |||
Serial.print(pontoOrvalho); | |||
Serial.println(F(" graus Celsius")); | |||
//exibir tempo de execução | |||
Serial.print(F("\nTempo de execucao total: ")); | |||
Serial.print(tempo); | |||
Serial.println(F("ms\n")); | |||
// exibir memória livre pra determinar estabilidade | |||
Serial.print(F("\nMemoria Livre: ")); | |||
Serial.print(freeRam()); | |||
Serial.println(F(" bytes\n")); | |||
/* | |||
* Versão anterior do programa principal | |||
* | |||
velVento = (contVento / BASETEMPO) * 2.4; | |||
Tensao = analogRead(A0); //variavel tensao recebe valor digital do dispositivo de direcao de vento | |||
Serial.print("Chuva acumalada (mm): "); | |||
Serial.println(mmChuv1000a); | |||
Serial.print("Velocidade do Vento (km/h): "); | |||
Serial.println(velVento); | |||
biruta(Tensao); | |||
altitudeEPressao(); | |||
contVento = 0; | |||
Serial.println(""); | |||
// PARA MODIFICAR O DELAY MODIFICAR A VARIAVEL "BASETEMPO" | |||
// NO CABECARIO DO PROGRAMA | |||
delay(BASETEMPO*1000); //Tempo entre as leituras em ms | |||
*/ | |||
} | |||
</source> | |||
--[[Usuário:Thiago.ribeiro|Thiago.ribeiro]] ([[Usuário Discussão:Thiago.ribeiro|discussão]]) 17h53min de 27 de abril de 2016 (BRT) | |||
<!-- | |||
<source lang="c"> | <source lang="c"> | ||
Linha 341: | Linha 790: | ||
</source> | </source> | ||
--Jade Mathias 17h47min de 2 de dezembro de 2015 (BRST) | --Jade Mathias 17h47min de 2 de dezembro de 2015 (BRST) | ||
--> | |||
===Formato CSV dos dados=== | ===Formato CSV dos dados=== |
Edição das 20h53min de 27 de abril de 2016
Estação hidrometeorológica automatizada com microcontrolador Arduíno
Objetivo
O objetivo deste projeto é construir uma estação hidrometeorológica automatizada no Campus Foz do Iguaçu, com o objetivo de dispor um banco de dados dinâmico de informações para uso em pesquisas e estudos sobre manejo da água, integrando as áreas de conhecimento de Informática, Eletrônica, Física, Hidrologia e Aquicultura.
- Projeto da Estação Hidrometeorológica, aprovado no programa PIBIC/CNPq IFPR 2016.
Equipe
- Professores orientadores
- Evandro Cantú
- Humberto Martins Beneduzzi
- Bolsista PIBIC/IFPR
- Thiago Henrique Ribeiro
- Alunos voluntários
- Jade Mathias da Silva
- José Antonio Kazienko Sallet
- Frederik Nazario Moschkowich
Estudos sobre Hidrometeorologia
- Tópicos para pesquisa
- Escolha do local para instalação de uma estação hidrometeorologia.
- Normas de localização e instalação de sensores.
- Dados a serem coletados e determinados.
- Ponto de orvalho.
- Escudo de radiação para sensores.
- Materiais de referência
- BLAINSKI, E.; POSPISSIL, L. H.; ANTUNES, G. E. N. Estações hidrometeorológicas automáticas: recomendações técnicas para instalação, Epagri, Florianópolis, 2012.
- INMET. Rede de Estações Meteorológicas Automáticas do INMET, Nota Técnica No. 001, Instituto Nacional de Meteorologia, 2011.
- Grupo de Meteorologia e Climatologia - Universidade de Aveiro CliM@UA
- Artigos com trabalhos de aplicação relacionados
- CARVALHO, L. R. M; AMORIM, H. S. Observando as marés atmosféricas: Uma aplicação da placa Arduino com sensores de pressão barométrica e temperatura, Revista Brasileira de Ensino de Física, v. 36, n. 3, 2014.
Estação Meteorológica Sparkfun
Sensor de Direção do Vento
O sensor de direção do vento é fisicamente composto por oito chaves, ligadas a resistores variados. Quando o vento muda de direção um imã fecha um ou dois contatos, permitindo a medição de 16 resistências diferentes.
Na ligação em hardware, um resistor de 10k ohms é utilizado como divisor de tensão, produzindo uma tensão de saída que pode ser medido por um conversor analógico-digital.
No Arduíno a tensão medida nas entradas analógicas (variando de 0V a 5V) é convertida internamente em um número digital de 10 bits (variando de 0 a 1023).
Para o sensor de direção do vento os 16 valores de resistência no divisor de tensão produzem as seguintes entradas no Arduino:
Direção (Graus) | Resistencia (Ohms) | Tensão (Entrada=5V Resistencia=10ohms) | Binário (Saída porta Analógica) | Saída Leitura em Binário de 10 bits (0 - 1023) | |
---|---|---|---|---|---|
Mínimo | Máximo | ||||
0 | 33k | 3,84 | 786 | 744 | 805 |
22,5 | 6,57k | 1,98 | 405 | 346 | 432 |
45 | 8,2k | 2,25 | 460 | 433 | 529 |
67,5 | 891 | 0,41 | 84 | 75 | 87 |
90 | 1k | 0,45 | 92 | 88 | 109 |
112,5 | 688 | 0,32 | 65 | 0 | 74 |
135 | 2,2k | 0,9 | 184 | 156 | 213 |
157,5 | 1,41k | 0,62 | 127 | 110 | 115 |
180 | 3,9k | 1,4 | 286 | 265 | 345 |
202,5 | 3,14k | 1,19 | 243 | 214 | 264 |
225 | 16k | 3,08 | 630 | 615 | 665 |
247,5 | 14,12k | 2,93 | 599 | 530 | 614 |
270 | 120k | 4,62 | 945 | 886 | 961 |
292,5 | 42,12k | 4,04 | 827 | 806 | 885 |
315 | 64,9k | 4,78 | 978 | 962 | 1023 |
337,5 | 21,88k | 3,43 | 702 | 666 | 743 |
--José Sallet 18h52min de 18 de novembro de 2015 (BRST)--
Pluviômetro
O pluviômetro é um tipo de balde de auto esvaziamento. A cada 0.2794 mm de chuva causa um fechamento de contato momentâneo que pode ser gravado com um contador digital ou acionar uma interrupção de microcontrolador (Arduíno).
O interruptor do pluviômetro é conectado com dois condutores centrais anexados em um cabo RJ11.
--Jade Mathias 16h17min de 17 de novembro de 2015 (BRST)
- Pluviômetro no Arduíno
- A cada 0.2794 mm de chuva uma interrupção é gerada. A rotina de interrupção, portanto, apenas deverá soma 0.2794 a uma variável que guarda a chuva acumulada em mm.
- O contador de chuva acumulada em mm foi construído com uma variável circular, de 0 a 999. Isto é, uma vez chegado a 999 mm a contagem da chuva acumulada volta a zero.
- Interrupções no Arduíno
- No Arduíno UNO as interrupções externas são acionadas pelo comando AttachInterrupt e são acionadas pelo pino 2 (interrupção 0) e/ou pino 3 (interrupção 1).
- No Arduíno as interrupções externas podem ser acionadas de diferentes modos:
- LOW: Quando o pino está baixo;
- CHANGE: Quando o valor do pino muda;
- RISING: Quando o valor do pino sobe de baixo para alto;
- FALLING: Quando o valor do pino desce de alto para baixo;
- HIGH: Quando o pino está alto.
- A cada fechamento de contados ocasionado pelos sensores uma rotina de tratamento de interrupção será acionada.
- Conforme ilustrado no tópico abaixo sobre o hardware para a chave do pluviômetro, será utilizado a borda de subida do pino (RISING) para acionar a interrupção e contar a chuva acumulada.
--Evandro.cantu (discussão) 22h16min de 17 de abril de 2016 (BRT)
Anemômetro
O anemômetro mede a velocidade do vento, fechando um contato com a ajuda de um ímã. Para uma velocidade do vento de 2.4 km/h o interruptor fecha uma vez por segundo.
O interruptor anemômetro está ligado a dois condutores internos do cabo RJ11 compartilhada pelo anemômetro e cata-vento (pinos 2 e 3).
--Jade Mathias 18h28min de 18 de novembro de 2015 (BRST)
- Anemômetro no Arduíno
- A largura dos pulsos gerados pelo anemômetro serão medidos pelo Arduíno através da função pulseln(). O tempo da largura de pulso retornado pela função pulseln() é medido em microsegundos, portando a velocidade do vento será determinada pela expressão:
velocidadeVento = 2,4 * 106 / larguraPulso
- Conforme o hardware do anemômetro mostrado abaixo, a largura dos pulsos do anemômetro será medida durante o tempo que estiver em nível lógico baixo (LOW), entre dois fechamentos de contato.
--Evandro.cantu (discussão) 22h16min de 17 de abril de 2016 (BRT)
Hardware para chaves do pluviômetro e anemômetro
Circuito para conectar as chaves do pluviômetro e anemômetro nas entradas de Interrupção do Arduíno:
--Evandro.cantu (discussão) 22h17min de 17 de abril de 2016 (BRT)
Sensor de Pressão Barométrica BMP180
Sensor de Pressão Barométrica BMP180
Código para Arduíno
/*
Estação Meteorológica com Microcontrolador Arduino
2015-2016 - Instituto Federal do Paraná
Programadores: José Sallet
Thiago Henrique Ribeiro
Orientador: Evandro Cantú
Mudanças em 2016:
12/04/2016 - Inclusão da função de leitura de vento por pulseIn()
14/04/2016 - Carregar strings literais da memória flash -> Serial.println(F("String literal));
- Inclusão dos sensores LM35 (temperatura) e DHT11 (temperatura, umidade)
- Testes com anemômetro e biruta com soprador de ar
*/
//Estacao de Testes - Baseado no codigo EstacaoMeteoCompleta
//Teste de adição de sensores LM35, DHT11 e nova rotina de leitura de vento baseada em PulseIn
#include<string.h>
//incluindo biblioteca do sensor de temperatura e pressao DHT11
#include <dht11.h>
//incluindo biblioteca do sensor de pressão
#include <SFE_BMP180.h>
//biblioteca do protocolo I2C
#include <Wire.h> // biblioteca de comunicação I2C - necessária para o sensor barométrico BMP180
#define ALTITUDE 180.0 // Altitude de Foz do Iguaçu, Parana, Brasil
#define BASETEMPO 10 // define o tempo de repeticao
SFE_BMP180 sensorPressao; //inicializando sensor de temperatura e pressão
dht11 sensorUmidade; //inicializando sensor DHT11
const int PINO_BIRUTA = A0; //pino analógico A0 - Leitura da tensão da biruta
const int PINO_LM35 = A1; //pino analógico A1 - Leitura do sensor LM35 (temperatura)
const int PINO_DHT11 = A2; //pino analógico A2 - Leitura do sensor DHT11 (temperatura + umidade)
//pino analógico A3 livre
//pino analógico A4 usado no BMP180 (temperatura + pressão)
//pino analógico A5 usado no BMP180 (temperatura + pressão)
const int BUFFER_SIZE = 1000; //quantidade de leituras do sensor LM35 para fazer a média
//para reduzir a margem de erro
const float CONVERSAO_GRAUS_CELSIUS = 0.488758553; //constante para calcular a temperatura do sensor LM35
//int Tensao; //tensão de leitura da biruta - comentado para invocar em loop()
float mmChuva = 0; //pluviômetro - Isto deveria ser gravado em EEPROM
//int contVento = 0; //contador de pulsos de vento - substituído por pulseIn
//float velVento; //velocidade do vento - comentado para invocar em loop
//--------------------------------------------------------------------------------------------------------------
// ROTINA DE INTERRUPÇÃO 0 - ANEMÔMETRO (obsoleta)
//--------------------------------------------------------------------------------------------------------------
//void vento(){
// contVento++;
// }
//--------------------------------------------------------------------------------------------------------------
// ROTINA DE INTERRUPÇÃO 1 - PLUVIÔMETRO
//--------------------------------------------------------------------------------------------------------------
void choveu() {
mmChuva = mmChuva + 0.2794;
if (mmChuva > 1000)
{
mmChuva = mmChuva - 1000;
}
}
//--------------------------------------------------------------------------------------------------------------
// ANEMÔMETRO (versão nova sem interrupção)
//--------------------------------------------------------------------------------------------------------------
float velVento(){
//Medição de largura do pulso em microsegundos
unsigned long larguraPulso = pulseIn(2, LOW, 2400000); //um pulso de 2400000 ms equivale a um vento de 1km/h
float _velVento = 0;
//calcular Velocidade de acordo com a largura do pulso; Largura de pulso mínima a ser calculada
if (larguraPulso <= 5000) {
byte retry = 2;
//se houver uma leitura inválida (valores absurdos devido a erros), mais duas tentativas são feitas.
while((retry > 0) && (larguraPulso <= 5000)){
retry--;
larguraPulso = pulseIn(2,LOW, 2400000);
}
}
if(larguraPulso > 5000){
_velVento = 2.4 * (1000000 / (float) larguraPulso);
}else{
_velVento = 0;
}
return _velVento;
}
//--------------------------------------------------------------------------------------------------------------
// BIRUTA (Sparkfun)
//--------------------------------------------------------------------------------------------------------------
String biruta(int Tensao)
{
String direcao;
if (Tensao >= 744 && Tensao <= 805) {
//Serial.println(F("Direcao do Vento: Leste"));
direcao = "L";
}
else if (Tensao >= 346 && Tensao <= 432) {
//Serial.println(F("Direcao do Vento: Leste-Norderste"));
direcao = "LNE";
}
else if (Tensao >= 433 && Tensao <= 529) {
//Serial.println(F("Direcao do Vento: Nordeste"));
direcao = "NE";
}
else if (Tensao >= 75 && Tensao <= 87) {
//Serial.println(F("Direcao do Vento: Norte-Nordeste"));
direcao = "NNE";
}
else if (Tensao >= 88 && Tensao <= 109) {
//Serial.println(F("Direcao do Vento: Norte"));
direcao = "N";
}
else if (Tensao >= 0 && Tensao <= 74) {
//Serial.println(F("Direcao do Vento: Norte-Noroeste"));
direcao = "NNO";
}
else if (Tensao >= 156 && Tensao <= 213) {
//Serial.println(F("Direcao do Vento: Noroeste"));
direcao = "NO";
}
else if (Tensao >= 110 && Tensao <= 155) {
//Serial.println(F("Direcao do Vento: Oeste-Noroeste"));
direcao = "ONO";
}
else if (Tensao >= 265 && Tensao <= 345) {
//Serial.println(F("Direcao do Vento: Oeste"));
direcao = "O";
}
else if (Tensao >= 214 && Tensao <= 264) {
//Serial.println(F("Direcao do Vento: Oeste-Sudoeste"));
direcao = "OSO";
}
else if (Tensao >= 615 && Tensao <= 665) {
//Serial.println(F("Direcao do Vento: Sudoeste"));
direcao = "SO";
}
else if (Tensao >= 530 && Tensao <= 614) {
//Serial.println(F("Direcao do Vento: Sul-Sudoeste"));
direcao = "SSO";
}
else if (Tensao >= 886 && Tensao <= 961) {
//Serial.println(F("Direcao do Vento: Sul"));
direcao = "S";
}
else if (Tensao >= 806 && Tensao <= 885) {
//Serial.println(F("Direcao do Vento: Sul-Sudeste"));
direcao = "SSE";
}
else if (Tensao >= 962 && Tensao <= 1023) {
//Serial.println(F("Direcao do Vento: Sudeste"));
direcao = "SE";
}
else if (Tensao >= 666 && Tensao <= 743) {
//Serial.println(F("Direcao do Vento: Leste-Sudeste"));
direcao = "LSE";
}
else{
Serial.println(F("[ERRO] Leitura na biruta fora da faixa!"));
direcao = "ERR";
}
return direcao;
}
//--------------------------------------------------------------------------------------------------------------
// SENSOR DE TEMPERATURA LM35
//--------------------------------------------------------------------------------------------------------------
//ler temperatura do sensor LM35 uma única vez
float temperaturaLM35() {
return analogRead(PINO_LM35) * CONVERSAO_GRAUS_CELSIUS;
}
//ler temperatura do sensor LM35 (BUFFER_SIZE) vezes e calcular a média para obter uma medição mais precisa
//aumentar o valor de BUFFER_SIZE aumenta a precisão mas aumenta a carga do sistema
float temperaturaLM35Buffer() {
float buffer = 0;
for (int i = 0; i < BUFFER_SIZE ; i++) {
buffer += analogRead(PINO_LM35);
}
return ((buffer) / BUFFER_SIZE) * CONVERSAO_GRAUS_CELSIUS;
}
//--------------------------------------------------------------------------------------------------------------
// CÁLCULO DE PONTO DE ORVALHO
// Funções de ponto de orvalho obtidas em http://playground.arduino.cc/Main/DHT11Lib
// A fazer: Verificar e referenciar o código apropriadamente
// O código abaixo foi copiado e usado sem alterações
//--------------------------------------------------------------------------------------------------------------
// dewPoint function NOAA
// reference (1) : http://wahiduddin.net/calc/density_algorithms.htm
// reference (2) : http://www.colorado.edu/geography/weather_station/Geog_site/about.htm
//
double dewPoint(double celsius, double humidity)
{
// (1) Saturation Vapor Pressure = ESGG(T)
double RATIO = 373.15 / (273.15 + celsius);
double RHS = -7.90298 * (RATIO - 1);
RHS += 5.02808 * log10(RATIO);
RHS += -1.3816e-7 * (pow(10, (11.344 * (1 - 1 / RATIO ))) - 1) ;
RHS += 8.1328e-3 * (pow(10, (-3.49149 * (RATIO - 1))) - 1) ;
RHS += log10(1013.246);
// factor -3 is to adjust units - Vapor Pressure SVP * humidity
double VP = pow(10, RHS - 3) * humidity;
// (2) DEWPOINT = F(Vapor Pressure)
double T = log(VP / 0.61078); // temp var
return (241.88 * T) / (17.558 - T);
}
// delta max = 0.6544 wrt dewPoint()
// 6.9 x faster than dewPoint()
// reference: http://en.wikipedia.org/wiki/Dew_point
double dewPointFast(double celsius, double humidity)
{
double a = 17.271;
double b = 237.7;
double temp = (a * celsius) / (b + celsius) + log(humidity * 0.01);
double Td = (b * temp) / (a - temp);
return Td;
}
// **Fim das funções de orvalho copiadas**
//--------------------------------------------------------------------------------------------------------------
// SENSOR DE PRESSÃO E TEMPERATURA (BMP180)
//--------------------------------------------------------------------------------------------------------------
double temperaturaBMP180(SFE_BMP180 sensor) {
double T;
char status = sensor.startTemperature();
if (status != 0) {
delay(status);
status = sensor.getTemperature(T);
if (status == 0) {
Serial.println(F("[AVISO] Falha na aquisição de temperatura do sensor BMP180. Tentando novamente..."));
return temperaturaBMP180(sensor); // recursivamente faz uma nova tentativa
}
} else {
Serial.println(F("[AVISO] Falha na preparação do sensor de temperatura BMP180. Tentando novamente..."));
return temperaturaBMP180(sensor); // recursivamente faz uma nova tentativa
}
return T;
}
double pressaoBMP180(double temperatura, SFE_BMP180 sensor) {
double pressao;
char status = sensor.startPressure(3);
if (status != 0) {
delay(status);
status = sensor.getPressure(pressao, temperatura);
if (status == 0) {
//erro em getPressure
Serial.println(F("[ERRO] Falha na aquisição da temperatura do sensor BMP180. Tentando novamente..."));
return pressaoBMP180(temperatura, sensor); //nova tentativa usando recursão
}
} else {
//erro em startPressure
Serial.println(F("[ERRO] Falha na preparação do sensor de pressão BMP180. Tentando novamente..."));
return pressaoBMP180(temperatura, sensor); // nova tentativa usando recursão
}
return pressao;
}
// cálculo da pressão relativa ao nível do mar
double pressaoNivelMar(double pressao, double altura, SFE_BMP180 sensor) {
return sensor.sealevel(pressao, altura);
}
// cálculo da altitude
double altitude(double pressao, double pressao_nivel_mar, SFE_BMP180 sensor) {
return sensor.altitude(pressao, pressao_nivel_mar);
}
//--------------------------------------------------------------------------------------------------------------
// TELEMETRIA - MEMÓRIA LIVRE NO SISTEMA
//--------------------------------------------------------------------------------------------------------------
int freeRam () {
extern int __heap_start, *__brkval;
int v;
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}
//--------------------------------------------------------------------------------------------------------------
// SETUP (Preparação)
//--------------------------------------------------------------------------------------------------------------
void setup()
{
Serial.begin(9600); //define a frequencia
attachInterrupt(1, choveu, RISING); // interrupcao do pluviometro
Serial.print(F("\nInicializando sensor barometrico BMP180... "));
// Initialize the sensor (it is important to get calibration values stored on the device).
if (sensorPressao.begin())
Serial.println(F("[OK]"));
else
{
// Oops, something went wrong, this is usually a connection problem,
// see the comments at the top of this sketch for the proper connections.
Serial.println(F("[FAIL]\n\nA estação não pode ser inicializada. Verifique o sensor barométrico e reinicie o aparelho."));
while (1); // Pause forever.
}
}
//--------------------------------------------------------------------------------------------------------------
// PROGRAMA PRINCIPAL
//--------------------------------------------------------------------------------------------------------------
//repeticao que imprime as informacoes do programa ao usuario
//TODO: organizar a ordem de execução levando em consideração o tempo tolerado por cada sensor
void loop()
{
/*
* Ações do programa:
* 1. Obter leitura do sensor de umidade (só pode ser lido a cada 2 segundos, são dois valores inteiros)
* 2. Obter leituras de tensão da biruta
* 3. Obter leituras do anemômetro
* 4. Obter leituras de temperatura do sensor LM35
* 5. Obter leituras de temperatura e pressão do sensor BMP180
*/
unsigned long inicio = millis(); // millis() e micros() - medem o tempo corrido em milisegundos e microsegundos
sensorUmidade.read(PINO_DHT11);
double altitude_local = (double)ALTITUDE; //adquire a altitude do local
float temperatura1 = (float) sensorUmidade.temperature; //adquire temperatura do sensor DHT11
float umidade = (float) sensorUmidade.humidity; //adquire umidade do sensor DHT11
int tensaoBiruta = analogRead(PINO_BIRUTA); //adquire o valor da tensão na biruta
String direcao = biruta(tensaoBiruta); //calcula a direção da biruta
float velocidadeVento = velVento(); //adquire a velocidade do vento
float temperatura2 = temperaturaLM35Buffer(); //adquire temperatura do sensor LM35
double temperatura3 = temperaturaBMP180(sensorPressao); //adquire temperatura do sensor BMP180
double pressaoAbsoluta = pressaoBMP180(temperatura3, sensorPressao); //adquire pressão absolutado sensor BMP180
double pressaoRelativa = pressaoNivelMar(pressaoAbsoluta, altitude_local, sensorPressao); //calcula pressão relativa
double altitudeCalculada = altitude(pressaoAbsoluta, pressaoRelativa, sensorPressao); //calcula altitude
double pontoOrvalho = dewPoint(temperatura2, umidade); //calcula ponto de orvalho
unsigned long fim = millis(); //fim da cronometração da função
unsigned long tempo = fim - inicio; //cálculo do tempo de execução
if(tempo < 2000){
delay(2000 - tempo); //cálculo do tempo de espera
} //(não pode ser menor que 2s)
// exibir dados na tela ou terminal serial
Serial.println(F("================================================================================================"));
Serial.print(F("Temperatura [DHT11]:\t\t\t"));
Serial.print(temperatura1);
Serial.println(F(" graus Celsius"));
Serial.print(F("Temperatura [LM35]\t\t\t"));
Serial.print(temperatura2);
Serial.println(F(" graus Celsius"));
Serial.print(F("Temperatura [BMP180]\t\t\t"));
Serial.print(temperatura3);
Serial.println(F(" graus Celsius"));
Serial.print(F("Velocidade do vento:\t\t\t"));
Serial.print(velocidadeVento);
Serial.println(F(" km/h"));
Serial.print(F("Direcao do vento:\t\t\t"));
Serial.print(direcao);
Serial.println(F(""));
Serial.print(F("Pressao absoluta (local):\t\t"));
Serial.print(pressaoAbsoluta);
Serial.println(F(" milibar"));
Serial.print(F("Pressao relativa (nivel do mar):\t"));
Serial.print(pressaoRelativa);
Serial.println(F(" milibar"));
Serial.print(F("Altitude:\t\t\t\t"));
Serial.print(altitudeCalculada);
Serial.println(F(" metros"));
Serial.print(F("Chuva acumulada:\t\t\t"));
Serial.print(mmChuva);
Serial.println(F(" mm"));
Serial.print(F("Umidade relativa do ar:\t\t\t"));
Serial.print(umidade);
Serial.println(F("%"));
Serial.print(F("Ponto de orvalho:\t\t\t"));
Serial.print(pontoOrvalho);
Serial.println(F(" graus Celsius"));
//exibir tempo de execução
Serial.print(F("\nTempo de execucao total: "));
Serial.print(tempo);
Serial.println(F("ms\n"));
// exibir memória livre pra determinar estabilidade
Serial.print(F("\nMemoria Livre: "));
Serial.print(freeRam());
Serial.println(F(" bytes\n"));
/*
* Versão anterior do programa principal
*
velVento = (contVento / BASETEMPO) * 2.4;
Tensao = analogRead(A0); //variavel tensao recebe valor digital do dispositivo de direcao de vento
Serial.print("Chuva acumalada (mm): ");
Serial.println(mmChuv1000a);
Serial.print("Velocidade do Vento (km/h): ");
Serial.println(velVento);
biruta(Tensao);
altitudeEPressao();
contVento = 0;
Serial.println("");
// PARA MODIFICAR O DELAY MODIFICAR A VARIAVEL "BASETEMPO"
// NO CABECARIO DO PROGRAMA
delay(BASETEMPO*1000); //Tempo entre as leituras em ms
*/
}
--Thiago.ribeiro (discussão) 17h53min de 27 de abril de 2016 (BRT)
Formato CSV dos dados
Os dados lidos pelos sensores da estação hidrometeorológica serão concatenados em uma string em formato CSV e transmitidos de forma serial para o servidor que armazenará os dados, utilizando os módulos Xbee.
Id da estação; direçao do vento; chuva acumulada; velocidade do vento; temperatura; pressao atmosferica 1;N;0.5;13.3;32.8;990 1;S;0.7;17.2;30;1020 1;NO;0.4;12;19.7;980 1;NE;0.54;13.6;42.3;900
--Jade Mathias 17h59min de 9 de dezembro de 2015 (BRST)
Material técnico de referência
Materiais na Wiki
Tutoriais no Site Oficial do Arduíno
- Bibliotecas e exemplos
- Ethernet e Cartão SD:
- Pressão Barométrica Web Server
- Guardar dados de sensores no cartão SD
- Ler e escrever dados em arquivos no cartão SD
- Criar e remover arquivos no cartão SD
Referências
- ↑ Weather Meters https://www.sparkfun.com/products/8942
- ↑ Manual Weather Sensor Assembly https://www.sparkfun.com/datasheets/Sensors/Weather/Weather%20Sensor%20Assembly..pdf