4. 13/09/2016 Pluviômetro de Báscula

Começamos a pensar em um caminho alternativo e decidimos comprar um pluviômetro de báscula comercial (Em Inglês: tipping bucket rain gauge).

Providenciamos a compra de um modelo fabricado pela empresa http://wrfcomercial.com.br. (Figura 8)

Figura 8. Pluviômetro de báscula (Fonte: http://wrfcomercial.com.br)

Pluviômetro de báscula (Fonte: http://wrfcomercial.com.br)

O volume é medido pela oscilação de dois reservatórios (báscula) que ao oscilarem geram um pulso que é registrado por um sistema de aquisição, como mostra a figura 9.

Figura 9. Princípio de funcionamento do pluviômetro

Princípio de funcionamento do pluviômetro

Montamos um reservatório do tipo Mariotte para ter uma vazão constante e controlável e poder fazer os testes com o pluviômetro e o desenvolvimento do programa para aquisição.

Dica

A garrafa de Mariotte também pode ser montada com um sifão. (Ver: Vaso de Mariotte 2 - Versão sifão)

Mas antes vamos entender como é medido o volume de chuvas em mm.

4.1. Como Medir a Chuva em milímetros?

O volume de chuva é expresso em milímetros (mm), o que corresponde à altura da lâmina d'água formada em um reservatório com 1 m2 de área.

Qual seria então o volume em litros (L) correspondente a 1 mm chuva?

Para saber, vamos calcular o volume de um paralelepípedo com área (A) de 1 m2 e altura (h) de 1 mm.

Primeiro vamos converter as unidades para dm:

1 m2 = 100 dm2

1 mm = 0,1 cm = 0,01 dm

Lembrando que o volume é a área da base multiplicada pela altura:

V = A x h

V = 100 dm2 x 0,01 dm = 1 dm3

E como 1 dm3 = 1 L descobrimos que 1 mm de chuva em uma área de 1 m2 corresponde ao volume de 1L.

E como converter os volumes em mililitros (ml) medidos em um pluviômetro com área de coleta A cm2 para a unidade padronizada em milimetros (mm)?

Essa conversão se baseia no princípio de que o volume de chuva captado é diretamente proporcional à área de captação.

Ou seja se uma área A coleta um volume V de chuva, podemos dizer que metade da área (A/2) irá coletar metade do volume (V/2).

Portanto se foi coletado 1mm (1L) de chuva em uma área de 1m2, podemos dizer que seria coletado 0,5 L (V/2) em uma área de coleta de 0,5 m2 (A/2). Ou 0,25 L (V/4) em uma área de coleta de 0,25 m2 (A/4). Figura 10

Figura 10. Proporcionalidade entre a área de coleta e o volume coletado de chuva.

Proporcionalidade entre a área de coleta e o volume coletado de chuva.

E portanto:

Equação 1. Para um dado volume de chuva, o volume total coletado por diferentes pluviômetros varia na mesma proporção da área de coleta mas a altura permanece constante.


Atenção

Estamos considerando neste raciocínio os pluviômetros com a forma de cilindro ou paralelepípedo que diferem apenas na área de coleta.

Ou seja, durante um dado período de chuva diferentes pluviômetros com diferentes áreas de coleta, irão coletar diferentes volumes de chuva mas terão a mesma altura do volume coletado.

Com base nessa propriedade podemos expressar o volume coletado (em mm) por um pluviômetro com qualquer geometria dividindo o volume coletado (em mm3) pela área de coleta (em mm2).

Equação 2. Equação que permite calcular o volume de chuva em milímetros (mm) a partir da área de coleta (mm2) e do volume coletado (mm3) independente da geometria do pluviômetro.


E se a área da base for igual à área de coleta basta medir a altura (em mm) da coluna de água coletada.

Mas se a área da base do reservatório não for igual à área de coleta, basta medir o volume coletado (em mm3) e dividir pela área de coleta ( em mm2).

Dica

Lembrar que 1 mm3 = 1x10-3 cm3 = 1x10-6 dm3 = 1x10-6 L = 1x10-3 mL

Ou seja, para converter o volume de mL para mm3 basta multiplicar o volume em mL por 1000.

4.2. Circuito (Hardware)

Montamos um circuito na protoboard, com a placa Arduino, para monitorar as oscilações da báscula usando o circuito da figura 11:

Figura 11. Montagem na protoboard e diagrama esquemático do circuito para monitoramento das oscilações da báscula do pluviômetro.

Montagem na protoboard e diagrama esquemático do circuito para monitoramento das oscilações da báscula do pluviômetro.

Mais tarde os amigos da lista SJC HackerClube sugeriram o uso de um acoplador óptico (Figuras 12 e 13) para isolar o circuito do pluviômetro do circuito do Arduino evitando o risco de correntes induzidas por raios durante tempestades.

Figura 12. Diagrama esquemático do circuito do pluviômetro com o uso de um acoplador óptico 4N35.

Diagrama esquemático do circuito do pluviômetro com o uso de um acoplador óptico 4N35.

Figura 13. Montagem em protoboard do circuito do pluviômetro com o uso de um acoplador óptico..

Montagem em protoboard do circuito do pluviômetro com o uso de um acoplador óptico..

E para fazer as leituras no Arduino implementamos o seguinte Sketch:

/*
  Pluviometro OO
 
  */
  
int reed_switch_pin = 8;
boolean last_reed_switch_state = LOW;
unsigned long tc = millis();
unsigned long ts = millis();
int clicks;
float volume;

void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);

  pinMode(reed_switch_pin, INPUT);
}

void loop() {
  unsigned long int_click = millis() - tc;
  
  if ( int_click > 50 ) {
    readPin();
  } 
  
  unsigned long int_send = millis() - ts;
  
  if ( int_send > 5000 ) {
    sendData();
  }

//Outras futuras atividades

}

void readPin () {
  
  int reed_switch_state = digitalRead(reed_switch_pin);

  if (last_reed_switch_state == LOW && reed_switch_state == HIGH) {
    clicks++;
//    Serial.print("Click - ");
//    Serial.println(clicks);
  }
  last_reed_switch_state = reed_switch_state;
  tc = millis();
   
    
}

//7.02 é o volume de cada báscula medido com o frasco de Mariotte
void sendData () {
  volume = clicks * 7.02;
  
//  Serial.print("Clicks em 10 segundos - ");
//  Serial.println(clicks);
//  Serial.print("Volume em 10 segundos - ");
//  Serial.println(volume);
  clicks = random(0, 10);
  Serial.print("pluviometro;");
  Serial.print(clicks);
  Serial.println(";D");
  clicks = 0;
  ts = millis();
}

Neste código a função readPin é chamada a cada 50 ms e esse intervalo deve ser regulado para se ajustar ao intervalo de tempo em que o reed-switch está fechado (estado HIGH).

Fiz algumas medidas com um osciloscópio para determinar a duração de cada pulso, ou seja, o tempo em que o sensor reed-switch permanece fechado, e foi possível medir que o pulso tem duração de ~100 ms. (Figura 14)

Figura 14. Medida da duração de cada pulso do pluviômetro.

Medida da duração de cada pulso do pluviômetro.

Isso indica que com uma frequência de leitura de 20KHz (intervalo de leitura de 50 ms) de um pulso com duração de 100 ms, não há o risco de perder os pulsos gerados pela oscilação da báscula.

O teste if faz a seguinte comparação:

if (reed_switch_state == LOW && last_reed_switch_state == HIGH) {
    clicks++;
    Serial.print("Click - ");
    Serial.println(clicks);
  }

Ou seja é uma forma detectar uma queda na tensão, análogo à opção "FALLING" no recurso de interrupção.

Considerando a hipótese de que o Arduino possa estar ligado a vários sensores, criamos um formato de dado que permita identificar o sensor a que o dado pertence.

[nome_do_sensor];N;[unidade]

Por exemplo, para o pluviômetro seria:

pluviometro;N;D

Onde N é um número inteiro que corresponde ao número de descargas (escoamento) do reservatório e D é uma unidade arbitrária que significa Descarga (Discharge) do reservatório (báscula).

Outros sensores que forem instalados no futuro poderão ser identificados pelos respectivos nomes:

barometro;N;unidade (bar, mmHg etc)

termometro;N;C

Dessa forma, os dados poderão ser devidamente interpretados pelo programa de aquisição para o processamento adequado.

4.3. Sistema de Monitoramento Pluviométrico

Podemos estabelecer a comunicação entre a placa Arduino e o PC de duas formas:

  1. Amostragem passiva ou orientada a eventos (event driven): o Arduino envia em intervalos regulares (Ex: 5 segundos) o número de descargas do reservatório e o programa no PC fica esperando passivamente o envio das mensagens e interpreta as leituras quando elas são enviadas. (Fonte: Programação Orientada a Eventos).

  2. Amostragem ativa (polling): o programa no PC envia comandos, em intervalos regulares, para o Arduino solicitando o envio de dados. (Fonte: Polling)

Identificamos os seguintes blocos funcionais que podem ser úteis para um sistema de monitoramento dos dados pluviométricos:

  1. data read - lê os dados que chegam por um canal (serial ou TCP/IP)

  2. data parse - interpreta os dados e identifica o instrumento, valor e unidade

  3. data storage - armazena os dados em um banco de dados

  4. data process - processa os dados (totalizando, calculando médias etc)

  5. data report - gera relatórios

  6. data show - exibe os dados em gráficos

  7. data analysis - faz análises mais elaboradas podendo combinar outros dados

Dentro do paradigma da Programação Orientada a Objetos essas funcionalidades poderiam ser implementadas em classes específicas facilitando a organização e manutenção do código.

Essas funcionalidades podem ser visualizadas no diagrama de fluxo da figura 15.

Figura 15. Blocos funcionais para implementar as atividades necessárias de um sistema pluviométrico

Blocos funcionais para implementar as atividades necessárias de um sistema pluviométrico

Os dados recebidos através de um canal (serial ou TCP/IP) são armazenados em uma variável (read) e em seguida interpretados (parse) identificando o instrumento e o tipo de dado.

Periodicamente os dados interpretados são processados (process) onde podem ser feitas operações básicas de totalização, estimativa de média etc.

Após o processamento podem ser exibidos (show) ou armazenados em banco de dados (storage).

Mas após o processamento os dados podem ainda passar por uma análise estatística mais elaborada (analysis) na qual podem ser combinados com dados históricos armazenados no banco de dados.

Os resultados da análise podem ser armazenados (storage) ou exibidos (show).

4.4. Interface Gráfica em Tcl/Tk

Para visualizar em gráfico as leituras do pluviômetro desenvolvemos inicialmente uma interface gráfica em Tcl/Tk e utilizamos a biblioteca Plotchart para exibir os volumes de chuva (mm) em função do tempo no formato de gráfico de barras (Barchart).

Escolhemos o gráfico de barras para exibição de dados pluviométricos seguindo sugestão do site: www.weatherforschools.me.uk/html/weatherdata.html.

Código inicial da interface:

  #!/bin/sh
#A proxima linha reinicia usando wish \
    exec wish "$0" "$@"

package require Plotchart

#==========================================================
#Versão 04

#Incluindo 3 frames para exibir 3 gráficos
#Frame 1 - registro da chuva a cada 10 segundos (0 - 60 segundos)
#Frame 2 - registro da chuva a cada 1 minuto (0 - 30 minutos)
#Frame 3 - registro da chuva a cada 1 hora (0 - 12 horas)
#Incluindo os tempos
#tempo_inicial_descarga - tempo 0 (timestamp) de uma descarga do reservatório
#tempo_final_descarga - instante de uma descarga do reservatório (t0_descarga para a próxima descarga)
#intervalo_descarga - intervalo de tempo entre cada descarga do reservatório (t_descarga - t0_descarga)
#tempo_inicial_programa - tempo 0 de execução do programa
#tempo_total_programa - tempo total deste o início de execução do programa  t - t0
#==========================================================


#Variáveis globais

#Volume do reservatório em mm (V_mm) = V / A = 0.397 mm
set V_mm 0.397

#Chuva acumulada em 1 min
set chuva_acumulada(1m) 0

wm title . "Pluviômetro IVR"

set tempo_inicial_programa [clock seconds]

set tempo_inicial_descarga [clock seconds]

set title_font {Times 18}

#==========================================================
#Criação dos frames
set frame_1 [frame .f1]
set frame_2 [frame .f2]
set frame_3 [frame .f3]
#==========================================================

#==========================================================
#Criação do label para o gráfico 1
set title_1 [label $frame_1.t1 -font $title_font  -justify center -text "Chuva a cada 5 segundos"]

pack $title_1

#==========================================================

#==========================================================
#Criação do canvas para exibição do gráfico 1
set canvas_graph_1 [canvas $frame_1.c -background white -width 1000 -height 200]

pack $canvas_graph_1

set list_xlabels_graph_1 {60 55 50 45 40 35 30 25 20 15 10 5 0}

set list_yaxis_graph_1 {0 0 0 0 0 0 0 0 0 0 0 0 0}

set count_loop 0

#==========================================================

#==========================================================
#Criação do gráfico 1

#Formato do comando para Barchart
#::Plotchart::createBarchart w xlabels yaxis noseries args

set grafico_pluviometro_1 [::Plotchart::createBarchart $canvas_graph_1 $list_xlabels_graph_1 {0 1 0.25} 2.5]

$grafico_pluviometro_1 xtext "Tempo (minuto)"
$grafico_pluviometro_1 ytext "Volume (milímetro)"

puts "grafico_pluviometro_1: $grafico_pluviometro_1"

$grafico_pluviometro_1 plot serie_leitura $list_yaxis_graph_1 blue

#==========================================================


#==========================================================
#Criação do label para o gráfico 2
set title_2 [label $frame_2.t1 -font $title_font  -justify center -text "Chuva a cada 1 minuto"]

pack $title_2

#==========================================================

#==========================================================
#Criação do canvas para exibição do gráfico 2
set canvas_graph_2 [canvas $frame_2.c -background white -width 1000 -height 200]

pack $canvas_graph_2

set list_xlabels_graph_2 {30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0}

set list_yaxis_graph_2 {0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0}

#==========================================================

#==========================================================
#Criação do gráfico 2

#Formato do comando para Barchart
#::Plotchart::createBarchart w xlabels yaxis noseries args

set grafico_pluviometro_2 [::Plotchart::createBarchart $canvas_graph_2 $list_xlabels_graph_2 {0 2 0.5} 2.5]

$grafico_pluviometro_2 xtext "Tempo (minuto)"
$grafico_pluviometro_2 ytext "Volume (milímetro)"

puts "grafico_pluviometro_2: $grafico_pluviometro_2"

$grafico_pluviometro_2 plot serie_leitura $list_yaxis_graph_2 blue

#==========================================================

#==========================================================
#Criação do label para o gráfico 3
set title_3 [label $frame_3.t1 -font $title_font  -justify center -text "Chuva a cada 1 hora"]

pack $title_3

#==========================================================

#==========================================================
#Criação do canvas para exibição do gráfico 3
set canvas_graph_3 [canvas $frame_3.c -background white -width 1000 -height 200]

pack $canvas_graph_3

set list_xlabels_graph_3 {12 11 10 9 8 7 6 5 4 3 2 1 0}

set list_yaxis_graph_3 {0 0 0 0 0 0 0 0 0 0 0 0}

#==========================================================

#==========================================================
#Criação do gráfico 3

#Formato do comando para Barchart
#::Plotchart::createBarchart w xlabels yaxis noseries args

set grafico_pluviometro_3 [::Plotchart::createBarchart $canvas_graph_3 $list_xlabels_graph_3 {0 3 0.5} 2.5]

$grafico_pluviometro_3 xtext "Tempo (hora)"
$grafico_pluviometro_3 ytext "Volume (milímetro)"

puts "grafico_pluviometro_3: $grafico_pluviometro_3"

$grafico_pluviometro_3 plot serie_leitura $list_yaxis_graph_3 blue

#==========================================================


#==========================================================
#Exibição dos frames
pack $frame_1 $frame_2 $frame_3
#==========================================================


proc criarCanal { } {
    
    global canal tempo_inicial
    
    set canal [ open /dev/ttyACM0 r+]
	    
    fconfigure $canal -mode 9600,n,8,1

    fconfigure $canal -blocking 0
    
    set tempo_inicial [clock seconds]

    fileevent $canal readable [list lerCanal $canal]

    puts "criarCanal: canal -> $canal tempo_inicial $tempo_inicial"
}

proc lerCanal { canal } {
    
    global tempo_inicial

    global canvas_graph_1 grafico_pluviometro_1

    global list_xlabels_graph_1 list_yaxis_graph_1

    global count_loop

    global V_mm
    
    set tempo_final [clock seconds]

    set tempo_leitura [expr $tempo_final - $tempo_inicial]

    #puts "tempo_inicial: $tempo_inicial tempo_final: $tempo_final tempo_leitura: $tempo_leitura"

    set leitura [gets $canal]

    if {$leitura == ""} { return }
    
    if { [eof $canal ] } {
	   puts stderr "Fechando $canal"
	   catch { close $canal }
	   return }

       
       puts "leitura: $leitura"
       
       set lista_leitura [split $leitura ";"]

       puts "lista_leitura: $lista_leitura"
       
       set N [lindex $lista_leitura 1]

       #Tratamento de exceção para evitar os erros gerados de mensagens quebradas acumuladas no buffer
       if {[catch {set V_total_mm [expr $N * $V_mm]}]} { return }

       puts "N: $N V_total_mm: $V_total_mm"

       lappend list_yaxis_graph_1 $V_total_mm

       set new_list_yaxis_graph_1 [ lrange $list_yaxis_graph_1 1 end ]

       set list_yaxis_graph_1 $new_list_yaxis_graph_1

       puts "list_yaxis_graph_1: $list_yaxis_graph_1"
       puts "new_list_yaxis_graph_1: $new_list_yaxis_graph_1"

       incr count_loop
       puts "count_loop -> $count_loop" 

       $canvas_graph_1 delete all

#::Plotchart::plotstyle configure default xyplot leftaxis font "Times 18 bold" 
       
       set grafico_pluviometro_1 [::Plotchart::createBarchart $canvas_graph_1 $list_xlabels_graph_1 {0 1 0.25} 1]

       
$grafico_pluviometro_1 xtext "Tempo (segundo)"
$grafico_pluviometro_1 vtext "Volume (milímetro)"

       
       puts "Inside pluviometro_02_barchart.tcl -> canvas_graph_1: $canvas_graph_1 grafico_pluviometro_1: $grafico_pluviometro_1"
       
       $grafico_pluviometro_1 plot serie_leitura $new_list_yaxis_graph_1 blue
       
       update

       if { $N != 0 } {
	      
	      calcChuvaInstantanea
	      
	  }
}

#Procedimento para o cálculo do intervalo de descarga
#O argumento t é o instante atual e unidade indica a unidade em que o intervalo deve ser expresso
#h - hora
#m - minuto
#s - segundo

proc calc_intervalo_descarga { t unidade } {
    
    global tempo_inicial_descarga tempo_inicial_programa

    puts "calc_intervalo_descarga t:$t tempo_inicial_descarga:$tempo_inicial_descarga"
    puts "intervalo:[expr ($t - $tempo_inicial_descarga)]"
    puts "intervalo:[expr ($t - $tempo_inicial_descarga)/1000.0]"
    puts "intervalo: [format "%.1f" [expr ($t - $tempo_inicial_descarga)/1000.0]] "
    
    if {$tempo_inicial_descarga == $tempo_inicial_programa} {
	   set tempo_inicial_descarga $t
	   puts "PRIMEIRA DESCARGA"
	   return
       }
       
       switch $unidade {
	   s { set intervalo [format "%.1f" [expr $t - $tempo_inicial_descarga]] }
	   m { set intervalo [format "%.1f" [expr ($t - $tempo_inicial_descarga)/60.0]] }
	   h { set intervalo [format "%.1f" [expr ($t - $tempo_inicial_descarga)/3600.0]] }
       }
       set tempo_inicial_descarga $t
       return $intervalo
       
}


#Procedimento para o cálculo da chuva instantânea que corresponde ao volume de descarga do reservatório dividido
#pelo tempo decorrido desde o último descarregamento do reservatório
#A placa Arduino envia leituras a cada 5 segundos e estamos considerando "impossível" que haja mais de um descarregamento
#no intervalo de 5 segundos
#Por isso o cálculo do intervalo só é feito a cada envio de dados da placa Arduino
#e o volume de descarga é o volume do reservatório 

proc calcChuvaInstantanea { } {

    global V_mm
    
    set tempo_final_descarga [clock seconds]

    set intervalo_m [calc_intervalo_descarga $tempo_final_descarga m]

    if { $intervalo_m != ""} {
    set chuva_inst [format "%.1f" [expr $V_mm / $intervalo_m]]

    puts "Intervalo de descarga em minutos $intervalo_m"
    puts "Chuva instantânea: $chuva_inst"

    updateChuvaAcumulada $V_mm 1 m

       }
}

proc updateChuvaAcumulada { volume tempo unidade } {

global chuva_acumulada

if {$volume} {
       set chuva_acumulada($tempo$unidade) [format "%.1f" [expr $chuva_acumulada($tempo$unidade) + $volume]]
   } else {
       set chuva_acumulada($tempo$unidade) 0
   }
}

#Procedimento para o cálculo da chuva acumulada que corresponde ao volume total dividido pelo intervalo de tempo

proc exibirChuvaAcumulada { intervalo } {

    global chuva_acumulada

    global canvas_graph_2 grafico_pluviometro_2

    global list_xlabels_graph_2 list_yaxis_graph_2

    
    
    puts "Exibindo a chuva acumulada após $intervalo -> $chuva_acumulada(1m)"

    lappend list_yaxis_graph_2 $chuva_acumulada(1m)

    set new_list_yaxis_graph_2 [ lrange $list_yaxis_graph_2 1 end ]

    set list_yaxis_graph_2 $new_list_yaxis_graph_2

    puts "list_yaxis_graph_2: $list_yaxis_graph_2"
    puts "new_list_yaxis_graph_2: $new_list_yaxis_graph_2"

    $canvas_graph_2 delete all

    set grafico_pluviometro_2 [::Plotchart::createBarchart $canvas_graph_2 $list_xlabels_graph_2 {0 2 0.5} 1]

    $grafico_pluviometro_2 xtext "Tempo (minuto)"
    $grafico_pluviometro_2 vtext "Volume (milímetro)"

    $grafico_pluviometro_2 plot serie_leitura $new_list_yaxis_graph_2 blue

    update
    
    #Zerar conteúdo de chuva_acumulada
    updateChuvaAcumulada 0 1 m

    puts "Chuva acumulada zerada -> $chuva_acumulada(1m)"

    after $intervalo [list exibirChuvaAcumulada $intervalo]

}

criarCanal

after 60000 {exibirChuvaAcumulada 60000}

update

4.5. Gateway em Tcl

No Wiki da comunidade Tcl/Tk encontrei uma dica interessante para implementar um servidor TCP/IP funcionando como um gateway entre a porta serial e um cliente TCP/IP. (http://wiki.tcl.tk/10042)

ver exemplo02_plotchart.tcl plotdemos2.tcl scope.tcl plotdemos7.tcl (barchart)

4.6. Links

Gráfico que mostra as chuvas acumuladas nos Estados Unidos (NOAA)