Projetos

Índices Ambientais com Arduino NANO

Eletrogate 5 de abril de 2022

Introdução

Calcular Índices meteorológicos é muito importante caso você faça uma estação meteorológica com Arduino. Neste post, iremos ver como calcular os principais índices meteorológicos, além de criar funções para cálculo destes no Framework Arduino. Para demonstração de execução destas funções, será desenvolvido um projeto para calcular a média semanal armazenando os resultados diários na EEPROM além de exibirmos em um display LCD os resultados das funções e da média semanal através de um menu cíclico controlado por um Joystick.


Materiais Necessários para o Projeto Índices Ambientais com Arduino NANO

Para este post, serão utilizados:


Índice de Calor

O Índice de Calor (IC), também conhecido como Heat Index (HI), é um índice que relaciona a umidade, medida em %, com a temperatura, medida em ºC. Este índice é uma medida que informa quão quente realmente se sente, ou seja, define qual a intensidade do calor que uma pessoa sente, variando em função da temperatura e da umidade do ar. Quando o corpo humano fica muito quente, começa a transpirar para reduzir sua temperatura. Quando a umidade relativa é alta, a taxa de evaporação do corpo diminui. Se a transpiração não é capaz de evaporar, o corpo não consegue regular sua temperatura. A fórmula do índice de calor se baseia no resultado dos estudos de biometeorologia realizados pelo pesquisador acadêmico têxtil Robert G. Steadman em 1979. Este investigou a percepção de calor sob uma variedade de condições meteorológicas, usando extensos dados sobre a fisiologia humana. A fórmula utilizada possui parâmetros implícitos pré assumidos como, por exemplo, as Dimensões de um ser humano, a Resistência da roupa à transferência de calor, entre outros parâmetros. Também possui parâmetros explícitos pré assumidos, como a Taxa de ventilação, a Resistência da pele à transferência de umidade, entre outros parâmetros. Todos estes parâmetros, tanto implícitos quanto explícitos, podem ser vistos nos estudos de Robert G. Steadman, de 1979 (com link de acesso abaixo), mas, não sendo necessário, pois foi realizada uma análise de regressão múltipla por meteorologistas com base nos dados do estudo de Robert G. Steadman, na qual foram obtidas versões da equação que utilizam variáveis ​​independentes mais convencionais.

A regressão múltipla realizada pelos meteorologistas não é apropriada quando as condições de Temperatura (T) e Umidade Relativa (R) garantem um valor de Índice de Calor (HI) abaixo de cerca de 80 °F. Por isso, existe outra equação mais simples para ser usado nestes casos.

Cálculo

Considere as seguintes variáveis gerais para os próximos cálculos:

  • HI (Heat Index) é o índice de calor;
  • T é a temperatura em °Fahrenheit;
  • R é a umidade relativa em %;
  • SQRT é a função de raiz quadrada;
  • ABS é a função de valor absoluto;

Siga os passos abaixo:

  • 1.  Se a temperatura estiver medida na escala Celsius, convertemos para Fahrenheit:

Equação para Converter Temperatura de Celsius para Fahrenheit

  • 2. Utilizando uma equação mais simples, verificamos se o índice de calor está acima de 80 °F:

Equação simples para verificar se o índice de calor está acima de 80 °F

Se o resultado da equação for menor que 80 °F, podemos utilizar o resultado da equação como o Índice de Calor.

  • 3.  Se o resultado da equação anterior (passo 2) for maior que 80° F, devemos utilizar a equação da análise de regressão múltipla baseada nos estudos de Steadman, juntamente com as devidas correções, para obter o índice de calor:

Uma das versões da equação de Índice de Calor feita com base na análise de regressão múltipla realizada com base nos estudos de Steadman

Onde as variáveis representam:

  • c1 a c9 são variáveis auxiliares que têm os seguintes valores:
    • c1: -42,379
    • c2: 2,04901523
    • c3: 10,14333127
    • c4: -0,22475541
    • c5: -0,00683783
    • c6: -0,05481717
    • c7: 0,00122874
    • c8: 0,00085282
    • c9: -0,00000199

Dependendo da Umidade Relativa (R) e da Temperatura (T) deve-se aplicar o item 3.1 ou o item 3.2:

  • 3.1  Se a Umidade Relativa (R) for menor que 13% e a Temperatura (T) estiver entre 80° e 112° Fahrenheit, o seguinte ajuste é subtraído do Índice de Calor (HI):

Ajuste às ser subtraído

  • 3.2 Se a Umidade Relativa (R) for maior que 85% e a Temperatura (T) estiver entre 80° e 87° Fahrenheit, o seguinte ajuste é somado do Índice de Calor (HI):

Ajuste às ser somado

  • 4.  Convertemos o resultado de Fahrenheit para Celsius utilizando a equação abaixo:

Equação para Converter Temperatura de Fahrenheit para Celsius

Exemplo de Cálculo

Para calcular o índice de calor de um local que possui temperatura ambiente de 28° Celsius e 88% UR (Umidade Relativa):

  • 1.  Como a Temperatura está na escala de Celsius, a convertemos para Fahrenheit:

Temperatura convertida em Fahrenheit: 82,4°F

  • 2.  Verificamos se HI está acima de 80° F:

Como o resultado da equação simples de HI é maior que 80°F, devemos utilizar a equação da análise de regressão múltipla (baseado em Steadman).

  • 3.  Calculamos HI com a equação da análise de regressão múltipla baseada nos estudos de Steadman:

Resultando em 92,12117638° Fahrenheit.

Verificação de que item utilizar:

  • 3.1: Umidade Relativa (R) é menor que 13% E a Temperatura (T) está entre 80° e 112° F?: Falso
  • 3.2: Umidade Relativa (R) é maior que 85% E a Temperatura (T) está entre 80° e 87° F?: Verdadeiro

Como o item 3.2 foi verdadeiro, usamos o item 3.2:

  • 3.2.  Aplicamos a devida correção (soma com o ajuste, de acordo com o item 3.2):

Somamos HI com o resultado de Ajuste

  • 4.  Convertemos o resultado de Fahrenheit para Celsius:

Resultando no Índice de Calor o valor de 33,55°C.

Influência do Índice de Calor

Faixas de valores do Índice de Calor:

  • 26°C à 32°C – Atenção: a fadiga é possível com exposição e atividade prolongadas. A atividade contínua pode resultar em cãibras de calor;
  • 32°C à 41°C – Cuidado extremo: cãibras de calor e exaustão pelo calor são possíveis. A atividade contínua pode resultar em insolação;
  • 41°C à 54°C – Perigo: cãibras de calor e exaustão pelo calor são prováveis; insolação é provável com atividade continuada;
  • acima de 54°C – Perigo extremo: a insolação é iminente.

Algoritmo Arduino para Cálculo de Índice de Calor

Utilizando o Framework Arduino, foi desenvolvida a função calculaIndiceCalor() que retorna o valor do Índice de Calor:

/**
Calcula o Índice de Calor, retornando o valor em Fahrenheit.

@param T : Valor da temperatura na escala Fahrenheit
@param R : Valor da Umidade Relativa em %
*/
double calculaIndiceCalor(double T, int R)
{
  double HI;
      
  HI = 0.5 * (T + 61.0 + ((T-68.0)*1.2)+(R*0.094));
  
  if(HI >= 80.0)
  {
    double c1 = -42.379;
    double c2 = 2.04901523;
    double c3 = 10.14333127;
    double c4 = -0.22475541;
    double c5 = -0.00683783;
    double c6 = -0.05481717;
    double c7 = 0.00122874;
    double c8 = 0.00085282;
    double c9 = -0.00000199;
    
    HI = c1 + 
      	 c2 * (T) + 
      	 c3 * R +
      	 c4 * T * R + 
      	 c5 * pow(T,2) + 
      	 c6 * pow(R,2) + 
      	 c7 * pow(T,2) * R + 
      	 c8 * T * pow(R,2) + 
      	 c9 * pow(T,2) * pow(R,2);
    
    if(R < 13 && (T >= 80.0 && T <= 112.0))
    {
      double ajuste = ((13.0 - R) / 4.0) * 
        	      sqrt((17.0 - abs(T - 95.0)) / 17.0);
      
      HI = HI - ajuste;
    }
    else if(R > 85.0 && (T >= 80.0 && T <= 87.0))
    {
      double ajuste = ((R - 85.0) / 10) * 
                      ((87.0 - T) / 5);
      
      HI = HI + ajuste;
    }
  }  
  
  return HI;
}

Esta função se utiliza das seguintes funções matemáticas do Framework Arduino:


Índice de Desconforto Térmico

O Índice de Desconforto Térmico (IDT), também conhecido como Thermal Discomfort Index (TDI),  é um índice, utilizando a Temperatura e a Umidade Relativa, que pode ser aplicado em avaliações de conforto ou estresse térmico. O TDI, é mais comumente aplicado em ambientes de trabalho humano, entretanto, também pode ser empregado em avaliações de instalações de produção animal, nas quais os parâmetros devem ser ajustados de acordo com o animal a ser avaliado. Este índice, criado em 1959 pelo climatologista do U.S. Weather Bureau (atual National Weather Service dos EUA), Earl C. Thom, está ligado à qualidade de vida e com a saúde da população. É importante para a saúde e segurança do trabalhador, produtividade industrial e desempenho atlético.

O Estudo de 1959, criado por Thom, pode ser visitado no seguinte link:

Cálculo

Siga os passos abaixo:

  • 1.  Se a Temperatura (T) estiver medida na escala Fahrenheit, convertemos para Celsius:

Equação para Converter Temperatura de Fahrenheit para Celsius

  • 2.  Calculamos o Índice de Desconforto Térmico:

Onde, na equação,

  • IDT: é o Índice de Desconforto Térmico em °Celsius;
  • T: é a Temperatura na medida na escala °Celsius;
  • R: é a Umidade Relativa do ar em %.

NOTA: O TDI não considera desconforto ocasionado pelo frio.

Exemplo de Cálculo

Para calcular o Índice de Desconforto Térmico de um local que possui temperatura ambiente de 28° Celsius e 88% UR (Umidade Relativa):

  • 1.  Como a Temperatura (T) está medida na escala Celsius, não precisamos fazer nenhuma conversão;
  • 2. Calculamos o Índice de Desconforto Térmico:

Resultando no Índice de Desconforto Térmico de 27,10 °Celsius.

Influência do Índice de Desconforto Térmico

  • menor que 14.9°C – Desconfortável;
  • 15°C à 19.9°C – Confortável;
  • 20°C à 26.4°C – Conforto parcial;
  • maior que 26.5°C – Desconfortável.

Algoritmo Arduino para Cálculo de Índice de Desconforto Térmico

Utilizando o Framework Arduino, foi desenvolvida a seguinte função que retorna o valor do Índice de Desconforto Térmico:

/**
Calcula o Índice de Desconforto Térmico, retornando o valor em Fahrenheit.

@param T : Valor da temperatura na escala Fahrenheit
@param R : Valor da Umidade Relativa em %
*/
double calculaIndiceDesconfortoTermico(double T, int R)
{
  double IDT;
  T = (T - 32) * 5/9; // converte T de fahrenheit para celsius

  IDT = T - (0.55 - 0.0055 * R) * (T - 14.5);
  IDT = (IDT * 9/5)+ 32; // converte IDT de celsius para fahrenheit
  
  return IDT;
}

Índice de Temperatura e Umidade

O Índice de Temperatura e Umidade (ITU), também conhecido como Temperature-Humidity Index (THI), é um índice que, utilizando a Temperatura e a Umidade Relativa, pode ser aplicado em avaliações de nível de estresse térmico em ambientes. O THI foi desenvolvido como um índice de segurança climática para monitorar e reduzir as perdas relacionadas ao estresse térmico. Diferentes espécies animais e humanos têm diferentes sensibilidades à temperatura ambiente e à quantidade de umidade no ar, o que, então, pode ser utilizado em avaliações de estresse térmico de animais, mas devendo a fórmula ser alterada em alguns parâmetros.

Cálculo

Siga os passos abaixo:

  • 1.  Se a Temperatura (T) estiver medida na escala Fahrenheit, convertemos para Celsius:

Equação para Converter Temperatura de Fahrenheit para Celsius

  • 2.  Calculamos o Índice de Temperatura e Umidade:

Onde, na equação,

  • THI: é o Índice de Temperatura e Umidade em °Celsius;
  • T: é a Temperatura na medida na escala °Celsius;
  • R: é a Umidade Relativa do ar em %.

Exemplo de Cálculo

Para calcular o Índice de Desconforto Térmico de um local que possui temperatura ambiente de 28° Celsius e 88% UR (Umidade Relativa):

  • 1.  Como a Temperatura (T) está medida na escala Celsius, não precisamos fazer nenhuma conversão;
  • 2. Calculamos o Índice de Temperatura e Umidade:

Resultando no Índice de Temperatura e Umidade de 27,328 °Celsius.

Influência do Índice de Temperatura e Umidade

  • menor que 14.9°C – Desconfortável;
  • 15°C à 19.9°C – Confortável;
  • 20°C à 26.4°C – Conforto parcial;
  • maior que 26.5°C – Desconfortável.

Algoritmo Arduino para Cálculo de Índice de Temperatura e Umidade

Utilizando o Framework Arduino, foi desenvolvida a seguinte função que retorna o valor do Índice de Temperatura e Umidade:

/**
Calcula o Índice de Temperatura e Umidade, retornando o valor em Fahrenheit.

@param T : Valor da temperatura na escala Fahrenheit
@param R : Valor da Umidade Relativa em %
*/
double calculaIndiceTemperaturaUmidade(double T, int R)
{
  double THI;
  T = (T - 32) * 5/9; // converte T de fahrenheit para celsius
  
  THI = 0.8 * T + ((R*T)/500);
  THI = (THI * 9/5)+ 32; // converte THI de celsius para fahrenheit
  
  return THI;
}

Hardware

Monte o circuito de acordo com o seguinte esquemático:

Esquemático do projeto: Arduino, DS3231, Buzzer, DHT11, Display LCD


Software

Após abrir a IDE Arduino, deverá ser feito o download de algumas bibliotecas no Gerenciador de Bibliotecas do Arduino, que pode ser aberto em Ferramentas -> Gerenciar Bibliotecas… (Ctrl + Shift + I). Para isso, siga as instruções abaixo:

  • Biblioteca DHT
    • Digite DHT na caixa de pesquisa do Gerenciador e clique em instalar na biblioteca DHT sensor library de autoria de Adafruit.
  • LiquidCrystal_I2C
    • Digite LiquidCrystal_I2C na caixa de pesquisa do Gerenciador e clique em instalar na biblioteca LiquidCrystal I2C  de autoria de Marco Schwartz.
  • RTClib
    • Digite RTClib na caixa de pesquisa do Gerenciador e clique em instalar na biblioteca RTClib de autoria de Adafruit.

Em seguida, cole o seguinte código:

/******************************************************************************
    Índice de calor, Índice de Desconforto Térmico e Índice de Temperatura
                       e Umidade com Arduino NANO
                              Sketch Principal

                            Criado em 08 mar. 2022
                              por Michel Galvão

  Eletrogate - Loja de Arduino \\ Robótica \\ Automação \\ Apostilas \\ Kits
                            https://www.eletrogate.com/
******************************************************************************/

// Inclusão da(s) biblioteca(s)
#include <DHT.h> // Sensor de Temp./Umid. DHT11
#include <Wire.h> // Comunicação I2C
#include <LiquidCrystal_I2C.h> // Display LCD I2C 16x2
#include <EEPROM.h> // Mémoria EEPROM
#include <RTClib.h> // Real Time Clock DS3231

// Definição dos pinos
#define DHTPIN 2
#define JOY_Y_PIN A2
#define JOY_X_PIN A1
#define BUZZER_PIN 4

// Defnição do Tipo do DHT
#define DHTTYPE DHT11

// Protótipo das Funções
double calculaIndiceTemperaturaUmidade(double T, int R);
double calculaIndiceDesconfortoTermico(double T, int R);
double calculaIndiceCalor(double T, int R);
double converteCelsiusToFahrenheit(double valor);
double converteFahrenheitToCelsius(double valor);

// Instanciação dos objetos das classes
LiquidCrystal_I2C lcd(0x27, 16, 2);
DHT dht(DHTPIN, DHTTYPE);
RTC_DS3231 rtc;

// Variáveis Globais
unsigned long timeDHT; // Timer para leituras do sensor DHT
unsigned long timeLeituraJoy; // Timer para leituras do módulo Joystick
unsigned long timeEEPROM; // Timer para verificações/execuções da memória EEPROM

static int idMenu = 0; // identificador atual da tela do menu ciclíco
const int qtdMenus = 5; // quantidade máxima de telas do menu
double temp; // armazena temperatura
int umid; // armazena umidade

struct mediaTemperatura { // estrutura para manobras na EEPROM
  double media; // média de temperatura semanal
  int qtdDiasDaMedia; // quantidade de dias feitas as médias


  int horaUltimaAtualizacao; // última hora feita a atualização
  int minutoUltimaAtualizacao; // último minuto feita a atualização
  int segundoUltimaAtualizacao; // último segundo feita a atualização
  int diaUltimaAtualizacao; // último dia feita a atualização
  int mesUltimaAtualizacao; // último mês feita a atualização
  int anoUltimaAtualizacao; // último ano feita a atualização
  int primeiraVez; // especifica se é a primeira vez se o software foi executado
  //                    ou não
};

void setup() { // Configurações Iniciais
  // Inicializações necessárias
  Serial.begin(9600);
  dht.begin();
  lcd.init();

  lcd.backlight(); // ativa a luz de fundo do display LCD

  // Splash Screen
  lcd.setCursor(0, 0);
  lcd.print("Indices do Tempo");
  lcd.setCursor(0, 1);
  lcd.print(" eletrogate.com ");
  delay(1000);

  // Configuração do(s) pino(s)
  pinMode(BUZZER_PIN, OUTPUT);

  // Inicialização do RTC e testa se a conexão foi bem-sucedida
  if (!rtc.begin()) {
    lcd.setCursor(0, 0);
    lcd.print("Erro RTC:       ");
    lcd.setCursor(0, 1);
    lcd.print("nao encontrado   ");
    while (1); // loop infinito
  }

  // Verifica o sinalizador de parada do oscilador do registrador de status para
  //    ver se o DS3231 parou devido à perda de energia.
  if (rtc.lostPower()) {
    lcd.setCursor(0, 0);
    lcd.print("Erro RTC:       ");
    lcd.setCursor(0, 1);
    lcd.print("Acerte o Horario ");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // Ajusta o horário para a
    //                                                  data/hora de compilação.

    // rtc.adjust(DateTime(2022, 3, 5, 16, 41, 0)); // Ajusta o horário para a
    //                                                  data/hora definido
    //                                                  nos parâmetros.
    delay(2000);
  }
  lcd.clear(); // limpa o display LCD
}

void loop() { // Loop Infinito
  delay(1); // atraso mínimo para não travamento desconhecido

  // Timer será executado de 4 em 4 segundos ou quando o valor de
  //  timeDHT (Valor do Timer) for igual à zero
  if (millis() - timeDHT >= 4000 || timeDHT == 0) {
    temp = dht.readTemperature(true); // Lê temperatura em Fahrenheit
    umid = dht.readHumidity(); // Lê a umidade
    timeDHT = millis(); // Atualiza o valor do Timer com millis()
  }

  mediaTemperatura mT; // cria um "objeto" do tipo da estrutura mediaTemperatura

  // Timer será executado de 60 em 60 segundos ou quando o valor de
  //  timeEEPROM (Valor do Timer) for igual à zero
  if (millis() - timeEEPROM >= 60000 || timeEEPROM == 0) {

    EEPROM.get(0, mT); //Pega os dados da estrutura mT na posição 0
    Serial.println("get EEPROM executado");

    // Cria um objeto da classe DateTime para armazenar os dados de hora/tempo
    //    em que ocorreu o último registro de média semanal
    DateTime ultimaAtualizacao = DateTime(mT.anoUltimaAtualizacao,
                                          mT.mesUltimaAtualizacao,
                                          mT.diaUltimaAtualizacao,
                                          mT.horaUltimaAtualizacao,
                                          mT.minutoUltimaAtualizacao,
                                          mT.segundoUltimaAtualizacao);

    // Instruções que mostram na Serial a diferença de tempo entre
    //    agora e a última atualização
    String message = "Diferença entre tempo atual e do tempo ";
    message += "da última atualzação da média (HH:MM:SS): ";
    Serial.print(message);
    // a função unixtime() retorna os segundos desde a meia-noite de 01/01/1970,
    //    então, a diferença entre unixtime deste momento com unixtime da última
    //    atualização irá retornar a quantidade de segundos passados desde o
    //    último registro de média semanal.
    unsigned long seconds = (rtc.now().unixtime() - ultimaAtualizacao.unixtime());
    unsigned long s, m, h; // variáveis para armazenar o tempo passado no
    //                          formato HH:MM:SS.
    s = seconds % 60;
    seconds = (seconds - s) / 60;
    m = seconds % 60;
    seconds = (seconds - m) / 60;
    h = seconds;
    Serial.print(h);
    Serial.print(":");
    Serial.print(m);
    Serial.print(":");
    Serial.println(s);

    // Relatório na Serial que mostra os dados da média semanal
    Serial.print("soma das temperaturas da semana (°C): ");
    Serial.println(mT.media);
    Serial.print("qtdDiasDaMedia: ");
    Serial.println(mT.qtdDiasDaMedia);
    Serial.print("media (°C): ");
    Serial.print(mT.media);
    Serial.print(" / ");
    Serial.print(mT.qtdDiasDaMedia);
    Serial.print(" = ");
    Serial.println(mT.media / mT.qtdDiasDaMedia);
    Serial.println();

    // se a váriavel que verifica que o software iniciou a EEPROM pela
    //    primera vez indicar que não houve nicialização (valor diferente de 999)
    //    OU se a diferença entre unixtime deste momento com unixtime da última
    //    atualização convertido para horas for maior ou igual à 24h
    //    irá ser executado o bloco de código do if.
    if (mT.primeiraVez != 999 ||
        ((rtc.now().unixtime() - ultimaAtualizacao.unixtime()) / 3600) >= 24) {

      // Algoritmo da média
      if (mT.qtdDiasDaMedia < 7) {
        // se a quantidade de dias feitas as médias for menor que 7 (1 semana), ...

        // adiciona o valor da soma da média com o valor da temperatura atual
        //   convertido de Fahrenheit para Celsius
        mT.media += converteFahrenheitToCelsius(temp);
        mT.qtdDiasDaMedia++; // auto-incrementa a quantidade de dias feitas as médias
        Serial.print("soma total das temperaturas: ");
        Serial.println(mT.media);
        Serial.print("mT.qtdDiasDaMedia: ");
        Serial.println(mT.qtdDiasDaMedia);
        Serial.print("média: ");
        Serial.println(mT.media / mT.qtdDiasDaMedia);
      } else if (mT.qtdDiasDaMedia >= 7) {
        // se não, se a quantidade de dias feitas as médias for >= à 7, ...

        // atribui o valor da temperatura atual à variável da média
        mT.media = converteFahrenheitToCelsius(temp);
        // atribui o valor 1 à variável de quantidade de dias feitas as médias
        mT.qtdDiasDaMedia = 1;
      }

      //atribui os valores aos membros
      mT.horaUltimaAtualizacao = rtc.now().hour();
      mT.minutoUltimaAtualizacao = rtc.now().minute();
      mT.segundoUltimaAtualizacao = rtc.now().second();
      mT.diaUltimaAtualizacao = rtc.now().day();
      mT.mesUltimaAtualizacao = rtc.now().month();
      mT.anoUltimaAtualizacao = rtc.now().year();
      mT.primeiraVez = 999;

      EEPROM.put(0, mT); // atualiza os dados da EEPROM no endereço 0
      //                      com os dados de mT (a struct)

      Serial.println("put EEPROM executado");
    }
    timeEEPROM = millis(); // Atualiza o valor do Timer com millis()
  }

  switch (idMenu) { // fluxo de controle das telas do menu
    case 0: // Caso o identificador atual da tela do menu ciclíco seja 0, exibe a
      //          tela com dados atuais: temperatura e umidade atmosférica
      lcd.setCursor(0, 0);
      if (temp < 0) {
        lcd.print("T");
      } else {
        lcd.print("T:");
      }
      if (temp <= 10 && temp >= 0) {
        lcd.print("0");
      }
      lcd.print(converteFahrenheitToCelsius(temp)); // converte a temperatura
      //                                                atual de Fahrenheit em
      //                                                Celsius e a exibe no display.
      lcd.write(223); // símbolo grau (11011111) da tabela ascii
      // Tabela ASCII: https://www.google.com/search?q=tabela+ASCII+lcd+16x2&tbm=isch
      lcd.print("C");

      lcd.setCursor(11, 0);
      lcd.print("U:");
      if (umid <= 10 && umid >= 0) {
        lcd.print("0");
      }
      lcd.print(umid); // exibe a umidade no display.
      lcd.print("% ");
      lcd.setCursor(0, 1);
      lcd.print("< 1. Atuais    >");
      break;

    case 1: // Caso o identificador atual da tela do menu ciclíco seja 1, exibe a
      //          tela com o Índice de Temperatura e Umidade
      lcd.setCursor(4, 0);

      //calcula e exibe o Índice de Temperatura e Umidade em Celsius
      lcd.print(converteFahrenheitToCelsius(calculaIndiceTemperaturaUmidade(
                                              temp,
                                              umid)));
      lcd.print(" ");
      lcd.write(223);
      lcd.print("C");
      lcd.setCursor(0, 1);
      lcd.print("< 2. Indi. T/U >");
      break;

    case 2: // Caso o identificador atual da tela do menu ciclíco seja 2, exibe a
      //          tela com o Índice de Desconforto Térmico
      lcd.setCursor(4, 0);

      //calcula e exibe o Índice de Desconforto Térmico em Celsius
      lcd.print(converteFahrenheitToCelsius(calculaIndiceDesconfortoTermico(
                                              temp,
                                              umid)));
      lcd.print(" ");
      lcd.write(223);
      lcd.print("C");
      lcd.setCursor(0, 1);
      lcd.print("< 3. Indi. IDT >");
      break;

    case 3: // Caso o identificador atual da tela do menu ciclíco seja 3, exibe a
      //          tela com o Índice de Calor
      lcd.setCursor(4, 0);
      //calcula e exibe o Índice de Calor em Celsius
      lcd.print(converteFahrenheitToCelsius(calculaIndiceCalor(
                                              temp,
                                              umid)));
      lcd.print(" ");
      lcd.write(223);
      lcd.print("C");
      lcd.setCursor(0, 1);
      lcd.print("< 4. Indi. IC  >");
      break;

    case 4: // Caso o identificador atual da tela do menu ciclíco seja 4, exibe a
      //          tela com a média Semanal
      lcd.setCursor(4, 0);

      // exibe a média semanal
      lcd.print(mT.media / mT.qtdDiasDaMedia);
      lcd.print(" ");
      lcd.write(223);
      lcd.print("C");
      lcd.setCursor(0, 1);
      lcd.print("< 5. Media Sem.>");
      break;
  }

  // Timer será executado de 0,2 em 0,2 segundos
  if (millis() - timeLeituraJoy > 200) {

    // se a leitura analógica da porta conectada ao eixo X do JoyStick
    //    for maior que 900, ...
    if (analogRead(JOY_X_PIN) > 900) {// Para Direita
      // Efetua o Feedback do buzzer ao usuário
      digitalWrite(BUZZER_PIN, HIGH);
      delay(20);
      digitalWrite(BUZZER_PIN, LOW);
      // incrementa o identificador da tela do menu ciclíco
      idMenu++;
      if (idMenu > (qtdMenus - 1)) { // se idMenu for maior que a quantidade de
        //                                telas do menu (convertido para índice
        //                                começado no 0), ...
        idMenu = 0; // atribui o valor 0 ao identificador da tela do menu ciclíco
      }

      lcd.clear(); // limpa o display LCD
    }
    // se não, se leitura analógica da porta conectada ao eixo X do JoyStick
    //    for menor que 20, ...
    else if (analogRead(JOY_X_PIN) < 20)// Para Esquerda
    {
      // Efetua o Feedback do buzzer ao usuário
      digitalWrite(BUZZER_PIN, HIGH);
      delay(20);
      digitalWrite(BUZZER_PIN, LOW);
      // decrementa o identificador da tela do menu ciclíco
      idMenu--;

      if (idMenu < 0) { // se idMenu for menor que 0, ...
        idMenu = (qtdMenus - 1); // atribui o valor da quantidade de telas
        //                            do menu (convertido para índice começado
        //                            no 0) ao identificador da tela do menu ciclíco
      }

      lcd.clear(); // limpa o display LCD
    }

    timeLeituraJoy = millis();// Atualiza o valor do Timer com millis()
  }

}

/**
  Calcula o Índice de Calor, retornando o valor em Fahrenheit.

  @param T : Valor da temperatura na escala Fahrenheit
  @param R : Valor da Umidade Relativa em %
*/
double calculaIndiceCalor(double T, int R)
{
  double HI;

  HI = 0.5 * (T + 61.0 + ((T - 68.0) * 1.2) + (R * 0.094));

  if (HI >= 80.0)
  {
    double c1 = -42.379;
    double c2 = 2.04901523;
    double c3 = 10.14333127;
    double c4 = -0.22475541;
    double c5 = -0.00683783;
    double c6 = -0.05481717;
    double c7 = 0.00122874;
    double c8 = 0.00085282;
    double c9 = -0.00000199;

    HI = c1 +
         c2 * (T) +
         c3 * R +
         c4 * T * R +
         c5 * pow(T, 2) +
         c6 * pow(R, 2) +
         c7 * pow(T, 2) * R +
         c8 * T * pow(R, 2) +
         c9 * pow(T, 2) * pow(R, 2);

    if (R < 13 && (T >= 80.0 && T <= 112.0))
    {
      double ajuste = ((13.0 - R) / 4.0) *
                      sqrt((17.0 - abs(T - 95.0)) / 17.0);

      HI = HI - ajuste;
    }
    else if (R > 85.0 && (T >= 80.0 && T <= 87.0))
    {
      double ajuste = ((R - 85.0) / 10) *
                      ((87.0 - T) / 5);

      HI = HI + ajuste;
    }
  }

  return HI;
}

/**
  Calcula o Índice de Desconforto Térmico, retornando o valor em Fahrenheit.

  @param T : Valor da temperatura na escala Fahrenheit
  @param R : Valor da Umidade Relativa em %
*/
double calculaIndiceDesconfortoTermico(double T, int R)
{
  double IDT;
  T = (T - 32) * 5 / 9; // converte T de fahrenheit para celsius

  IDT = T - (0.55 - 0.0055 * R) * (T - 14.5);
  IDT = (IDT * 9 / 5) + 32; // converte IDT de celsius para fahrenheit

  return IDT;
}

/**
  Calcula o Índice de Temperatura e Umidade, retornando o valor em Fahrenheit.

  @param T : Valor da temperatura na escala Fahrenheit
  @param R : Valor da Umidade Relativa em %
*/
double calculaIndiceTemperaturaUmidade(double T, int R)
{
  double THI;
  T = (T - 32) * 5 / 9; // converte T de fahrenheit para celsius

  THI = 0.8 * T + ((R * T) / 500);
  THI = (THI * 9 / 5) + 32; // converte THI de celsius para fahrenheit

  return THI;
}

double converteCelsiusToFahrenheit(double valor) {
  return (valor * 9 / 5) + 32;
}

double converteFahrenheitToCelsius(double valor) {
  return (valor - 32) * 5 / 9;
}

Já com o código na IDE, faça a compilação e o upload para a placa Arduino NANO.

Explicação do Código

O código está totalmente comentado, mais aqui estão destacadas suas principais partes:

  • Antes de void setup,
    • Para usar as funcionalidades da EEPROM, devemos incluir a biblioteca que inclui as funcionalidades necessárias (EEPROM.h).
    • Declaramos a estrutura mediaTemperatura. Uma estrutura é um tipo composto que é definido pelo usuário. Ela é composta por campos ou membros que podem ter diferentes tipos. Veja mais sobre struct em playground.arduino.cc/Code/Struct. Essa estrutura contém todos os dados necessários para guardar os dados na EEPROM do arduino Nano.

  • Dentro de void setup,
    • verificamos se o RTC parou devido à perda de energia. Se houve perda de energia, informamos isso através do display LCD e configuramos o horário para o horário em que foi feita a compilação do firmware.
  • Dentro de void loop,
    • verificamos se o tempo do timer chamado timeDHT está apto a executar o bloco de código de leitura de umidade e temperatura do sensor DHT11. Esse timer é executado de 4 em 4 segundos ou quando o valor de timeDHT é zero (ocorre quando o programa é inicializado). Dentro do bloco de código, fazemos as operações necessárias e atualizamos o valor do timer com o valor de millis().
    • criamos um “objeto” chamado da estrutura mediaTemperatura.
    • em seguida, verificamos se o tempo do timer chamado timeEEPROM está apto a executar o bloco de código dele. Esse timer é executado de 60 em 60 segundos ou quando o valor de timeEEPROM é zero (ocorre quando o programa é inicializado).
    • dentro do bloco de código deste timer, obtemos os dados armazenados na memória EEPROM e passamos para a estrutura. Na memória EEPROM, os dados serão armazenados na memória a partir do endereço 0 até o endereço em que caber o tamanho da estrutura (este tamanho é obtido através de sizeof()).
    • também criamos o objeto do tipo DateTime em que poderemos manipular os dados de tempo da última vez em que foi atualizada a média semanal. Estes dados foram obtidos através do objeto mT.
    • caso a variável, obtida da estrutura mT, que controla se o software já foi iniciado alguma vez (lê da EEPROM), seja diferente de 999, o bloco de código será executado. Esse valor (999) foi escolhido aleatoriamente, devendo ser diferente de 0. O bloco de código também será executado se a diferença em segundos entre o tempo atual e o tempo em que ocorreu a última atualização da média convertidos para horas for maior ou igual à 24 horas (1 dia). O tempo desejado em segundos é obtido através da função unixtime(), que retorna os segundos desde 1º de janeiro de 1970 do objeto DateTime ao qual ele está relacionado.
      • dentro do bloco de código, manipularemos as variáveis responsáveis pela média semanal, de acordo com a quantidade de dias já armazenados a média:
        • menor que 7 dias: adicionamos ao valor da média, já existente, o valor da temperatura convertida para a escala Celsius e atribuímos à variável que armazena a quantidade de dias um incremento;
        • maior ou igual que 7 dias: setamos o valor da média com o valor da temperatura convertida para a escala Celsius e atribuímos à variável que armazena a quantidade de dias das medidos o valor 1.
      • Ainda dentro do bloco de código, atribuimos às variáveis que armazenam o tempo da última atualização da média com os atuais valores obtidos do RTC, além de atrbuirmos o valor 999 à variável que controla se o software já foi iniciado alguma vez.
      • Ainda dentro do bloco de código, atualizamos a estrutura armazenada na EEPROM com os valores atuais. Após o bloco de código, atualizamos o valor do timer com o valor de millis().
    • Já novamente no void loop, testamos através do fluxo de controle switch() qual o valor do identificador de tela do menu (chamado de idMenu).
    • dentro de cada bloco testador do idMenu (case),  executamos as instruções necessárias para mostrar as indformações em cada tela do menu  que será exibido no display LCD.

      Exemplo do case 1

    • Já novamente no void loop, verificamos se o tempo do timer chamado timeLeituraJoy está apto a executar o bloco de código dele. Esse timer é executado de 200 em 200 milissegundos.
      • dentro do bloco de código do timer timeLeituraJoy, verificamos se houve alterações nas posições do joystick efetuado pelo usuário. Essas alterações podem ser analisadas pela função analogRead(), que retorna o valor lido do pino analógico ao qual o eixo X (eixo horizontal) do joySytick está conectado. Se a leitura analógica for maior que 900, incrementamos a variável idMenu e executamos um som de feedback para o usuário através de  um buzzer. Caso a variável idMenu tenha um valor maior que a quantidade máxima de menus (qtdMenus), damos o valor zero à variável idMenu.
      • caso a leitura analógica for menor que 20, decrementamos a variável idMenu e executamos um som de feedback para o usuário através de  um buzzer. Caso a variável idMenu tenha um valor menor que 0, damos o valor da quantidade máxima de menus (qtdMenus) à ela.
      • após as testagens com as estruturas de condição, atualizamos o valor do timer com o valor de millis().

Referências

HEAT INDEX. In: WIKIPÉDIA, The Free Encyclopedia. Flórida: Wikimedia Foundation, 14 out. 2021. Disponível em: <https://en.wikipedia.org/w/index.php?title=Heat_index&oldid=1049923857>. Acesso em: 31 jan. 2022.

SUOR. In: WIKIPÉDIA, a enciclopédia livre. Flórida: Wikimedia Foundation, 28 abr. 2020. Disponível em: <https://pt.wikipedia.org/w/index.php?title=Suor&oldid=58140659>. Acesso em: 31 jan. 2022.

DHT-SENSOR-LIBRARY. In: GITHUB, Where the world builds software. [S.L.]: adafruit, 25 out 2021. Disponível em: <https://github.com/adafruit/DHT-sensor-library>. Acesso em: 31 jan. 2022.

CAVALCANTE, Fagna Maria Silva et al.. Análise do índice de calor e desconforto térmico na cidade de caicó-rn. Anais II CONIDIS… Campina Grande: Realize Editora, 2017. Disponível em: <https://www.editorarealize.com.br/index.php/artigo/visualizar/33120>. Acesso em: 6 fev. 2022.

BARBOZA, Eliezio Nascimento et al. Análise do Índice de Calor (IC), Índice de Conforto Térmico (IDT) e Índice de Temperatura e Umidade (ITU) na cidade de Iguatu/CE a partir de dados históricos. In: Revista Brasileira de Gestão Ambiental. Pombal, PB-Brasil: Grupo Verde de Agroecologia e Abelhas-GVAA, jul./set. 2019. Disponível em:<https://www.gvaa.com.br/revista/index.php/RBGA/article/view/7434>. Acesso em: 6 fev. 2022.

 


Conclusão

Com os conceitos aqui apresentados, além do projeto, pode-se criar projetos mais avançados, como uma estação meteorológica ou um indicador de conforto térmico. Curtiu o post? Avalie e deixe um comentário! Siga-nos também no Instagram e nos marque quando fizer algum projeto nosso: @eletrogate.

Até a próxima!

Conheça a Metodologia Eletrogate e ofereça aulas de robótica em sua escola!


Sobre o Autor


Michel Galvão

Hobbysta em Sistemas Embarcados e IoT. Tem experiência em Automação Residencial e Agrícola.


Eletrogate

5 de abril de 2022

A Eletrogate é uma loja virtual de componentes eletrônicos do Brasil e possui diversos produtos relacionados à Arduino, Automação, Robótica e Eletrônica em geral.

Tenha a Metodologia Eletrogate dentro da sua Escola! Conheça nosso Programa de Robótica nas Escolas!

Eletrogate Robô

Cadastre-se e fique por
dentro de novidades!