IoT feito simples: Captura remota de dados meteorológicos

27 27UTC maio 27UTC 2018 — 1 Comentário

Neste tutorial, capturaremos dados remotos como UV (radiação ultravioleta), temperatura e umidade do ar. Esses dados serão muito importantes e serão usados em uma futura Estação Meteorológica completa.

O diagrama de blocos mostra o que obteremos no final.

1. “BoM” – Lista de materiais

NodeMCU (ESP8266-12E) – USD 9.00

Humidity and Temperature Sensor (DHT22) – USD10.00

UV Sensor – USD 4.00

OLED USD 12.00

Breadboard – USD1.00

2. O sensor analógico de UV

Este sensor de UV gera uma saída analógica proporcional à radiação ultravioleta encontrada no espectro de detecção de luz.
Ele usa um fotodiodo UV (baseado em nitreto de gálio), que pode detectar a faixa de luz de 240 a 370 nm (que cobre a radiação UVB e a maior parte do espectro UVA). O nível de sinal do fotodiodo é muito pequeno, no nível do nano-ampere, daí o módulo ter embutido um amplificador operacional utilizado para amplificar o sinal para um nível de voltagem mais legível (0 a 1V).

O sensor e o amplificador operacional podem ser alimentados, conectando-se o VCC a 3,3VDC (ou 5VDC) e o GND ao terra (fornecidos pelo NodeMCU). O sinal analógico pode ser obtido a partir do pino OUT.

Sua saída será em milivolts e será lida pela entrada analógica de nosso NodeMCU. Uma vez lida, devemos “converter” (ou “mapear”) este sinal para que os valores sejam melhor manipulados pelo código. Nós podemos fazer isso com a função readSensorUV ():

/* Read UV Sensor in mV and call UV index calculation */
void readSensorUV()
{                    
  byte numOfReadings = 5;
  dataSensorUV = 0;
  for (int i=0; i< numOfReadings; i++) 
  {
    dataSensorUV += analogRead(sensorUVPin);
    delay (200);
  }
  dataSensorUV /= numOfReadings;
  dataSensorUV = (dataSensorUV * (3.3 / 1023.0))*1000;
  Serial.println(dataSensorUV);
  indexCalculate();
}

Uma vez obtido o dado de UV, podemos facilmente calcular o índice de UV como definido na tabela acima. A função indexCalculate () fará isso para nós:

/*  UV Index calculation */
void indexCalculate()
{
    if (dataSensorUV < 227) indexUV = 0;
    else if (227 <= dataSensorUV && dataSensorUV < 318) indexUV = 1;
    else if (318 <= dataSensorUV && dataSensorUV < 408) indexUV = 2;
    else if (408 <= dataSensorUV && dataSensorUV < 503) indexUV = 3;
    else if (503 <= dataSensorUV && dataSensorUV < 606) indexUV = 4;    
    else if (606 <= dataSensorUV && dataSensorUV < 696) indexUV = 5;
    else if (696 <= dataSensorUV && dataSensorUV < 795) indexUV = 6;
    else if (795 <= dataSensorUV && dataSensorUV < 881) indexUV = 7; 
    else if (881 <= dataSensorUV && dataSensorUV < 976) indexUV = 8;
    else if (976 <= dataSensorUV && dataSensorUV < 1079) indexUV = 9;  
    else if (1079 <= dataSensorUV && dataSensorUV < 1170) indexUV = 10;
    else indexUV = 11;       
}

3. Instalando o Display: OLED

Para fins de teste, incluiremos um OLED em nosso medidor de UV (esta etapa é completamente opcional).
Não há problemas em se utilizar o Monitor Serila durante os testes, mas o que acontecerá quando você usar seus protótipos longe de seu PC em um modo independente? Para isso, instalaremos um display OLED, o SSD1306, cujas principais características são:
  • Display size: 0.96″
  • I2C IIC SPI Serial
  • 128X64
  • White OLED LCD LED

Siga o diagrama elétrico e conecte os 4 pinos de nosso OLED:

  • VCC em 3.3V
  • GND em  ground
  • SCL no NodeMCU (GPIO 2) ==> D4
  • SDA no NodeMCU (GPIO 0) ==> D3

Assim que conectarmos o OLED,  baixaremos e instalaremos sua biblioteca em nosso Arduino IDE: o “ESP8266 OLED Driver for SSD1306 display” desenvolvido por Daniel Eichhorn (Certifique-se de usar a versão 3.0.0 ou uma mais atual!).

Instale a biblioteca no seu Arduino IDE, que pode ser encontrado em SSD1306Wire.h

Depois de reiniciar o IDE, a biblioteca já deverá estar instalada.

A biblioteca suporta o protocolo I2C para acessar o display OLED, usando-se da biblioteca Wire.h:

/* OLED */
#include "SSD1306Wire.h"
#include "Wire.h"
const int I2C_DISPLAY_ADDRESS = 0x3c;
const int SDA_PIN = 0;
const int SCL_PIN = 2;
SSD1306Wire display(I2C_DISPLAY_ADDRESS, SDA_PIN, SCL_PIN);

Listemos algumas APIs importantes que serão usadas com nosso display OLED. A lista completa pode ser encontrada no GITHub fornecido acima.

A. Controle de exibição:

void init(); // Initialise the display
void displayOn(void); // Turn the display on
void displayOff(void); // Turn the display offs
void clear(void); // Clear the local pixel buffer
void flipScreenVertically(); // Turn the display upside down

B. Operação com texto:

void drawString(int16_t x, int16_t y, String text); // (xpos, ypos, "Text")
void setFont(const char* fontData);  // Sets the current font.

Fontes default disponíveis:

  • ArialMT_Plain_10,
  • ArialMT_Plain_16,
  • ArialMT_Plain_24

Depois que o OLED e sua biblioteca estiverem instalados, escreveremos um programa simples para testá-lo.

Entre com o código abaixo em seu IDE, o resultado deverá ser semelhante ao mostrado na foto acima:

* OLED */
#include "SSD1306Wire.h"
#include "Wire.h"
const int I2C_DISPLAY_ADDRESS = 0x3c;
const int SDA_PIN = 0;
const int SCL_PIN = 2;
SSD1306Wire display(I2C_DISPLAY_ADDRESS, SDA_PIN, SCL_PIN);

void setup () 
{
  Serial.begin(115200); 
  displaySetup();
}

void loop() 
{ 
}

/* Initiate and display setup data on OLED */
void displaySetup()
{
  display.init();         // initialize display
  display.clear();        // Clear display
  display.flipScreenVertically(); // Turn the display upside down
  display.display();      // Put data on display
  
  Serial.println("Initiating Display Test");
  
  display.setFont(ArialMT_Plain_24);
  display.drawString(30, 0, "OLED");  // (xpos, ypos, "Text")
  display.setFont(ArialMT_Plain_16);
  display.drawString(18, 29, "Test initiated");
  display.setFont(ArialMT_Plain_10);
  display.drawString(10, 52, "Serial BaudRate:");
  display.drawString(90, 52, String(11500));
  display.display();  // Put data on display
  delay (3000);
}

O programa acima poderá ser baixado de meu GitHub:

NodeMCU_OLED_Test

4. Um medidor local de UV

Uma vez o display OLED esteja instalado, poderemos conectar uma bateria e fazer alguns testes remotos utilizando-se de nosso “medidor de UV”
#define SW_VERSION "UV_Sensor_V.1" 

/* UV Sensor */
#define sensorUVPin A0
int dataSensorUV = 0;
int indexUV = 0;

/* OLED */
#include "SSD1306Wire.h"
#include "Wire.h"
const int I2C_DISPLAY_ADDRESS = 0x3c;
const int SDA_PIN = 0;
const int SCL_PIN = 2;
SSD1306Wire display(I2C_DISPLAY_ADDRESS, SDA_PIN, SCL_PIN);

void setup()
{
  Serial.begin(115200);
  displaySetup();
}

void loop()
{                    
  readSensorUV();
  displayUV();
  delay (1000);
}

/* Initiate and display setup data on OLED */
void displaySetup()
{
  display.init();                 // initialize display
  display.clear();                // Clear display
  display.flipScreenVertically(); // Turn the display upside down
  display.display();              // Put data on display

  Serial.println("Initiating UV Sensor Test");
  display.setFont(ArialMT_Plain_24);
  display.drawString(10, 0, "MJRoBot");  
  display.setFont(ArialMT_Plain_16);
  display.drawString(0, 29, "UV Sensor Test");
  display.setFont(ArialMT_Plain_10);
  display.drawString(0, 52, "SW Ver.:");
  display.drawString(45, 52, SW_VERSION);
  display.display();
  delay (3000);
}

/* Read UV Sensor in mV and call UV index calculation */
void readSensorUV()
{                    
  byte numOfReadings = 5;
  dataSensorUV = 0;
  for (int i=0; i< numOfReadings; i++) 
  {
    dataSensorUV += analogRead(sensorUVPin);
    delay (200);
  }
  dataSensorUV /= numOfReadings;
  dataSensorUV = (dataSensorUV * (3.3 / 1023.0))*1000;
  Serial.println(dataSensorUV);
  indexCalculate();
}

/*  UV Index calculation */
void indexCalculate()
{
    if (dataSensorUV < 227) indexUV = 0;
    else if (227 <= dataSensorUV && dataSensorUV < 318) indexUV = 1;
    else if (318 <= dataSensorUV && dataSensorUV < 408) indexUV = 2;
    else if (408 <= dataSensorUV && dataSensorUV < 503) indexUV = 3;
    else if (503 <= dataSensorUV && dataSensorUV < 606) indexUV = 4;    
    else if (606 <= dataSensorUV && dataSensorUV < 696) indexUV = 5;
    else if (696 <= dataSensorUV && dataSensorUV < 795) indexUV = 6;
    else if (795 <= dataSensorUV && dataSensorUV < 881) indexUV = 7; 
    else if (881 <= dataSensorUV && dataSensorUV < 976) indexUV = 8;
    else if (976 <= dataSensorUV && dataSensorUV < 1079) indexUV = 9;  
    else if (1079 <= dataSensorUV && dataSensorUV < 1170) indexUV = 10;
    else indexUV = 11;       
}

/*  Display UV Values on local OLED*/
void displayUV()
{
  display.clear();
  display.setFont(ArialMT_Plain_16);
  display.drawString(20, 0, "UV Sensor");
  display.drawString(0, 23, "UV (mV):" );
  display.drawString(80, 23, String(dataSensorUV));
  display.drawString(0, 48, "UV Index:" );
  display.setFont(ArialMT_Plain_24);
  display.drawString(82, 42, String(indexUV));
  display.display();
}

O programa acima poderá ser baixado de meu GitHub: NodeMCU_UV_Sensor_OLED.ino

5. Instalando o DHT22

Um dos sensores mais utilizados para a captura de dados meteorológicos é o DHT22 (ou seu irmão DHT11), um sensor digital de umidade relativa e temperatura. Ele usa um sensor capacitivo de umidade e um termistor para medir o ar ao seu redor e envia um sinal digital ao pino de dados (não é necessário o uso da entrada analógica, o que é importante, pois o NodeMCU só possui uma disponível e já utilizada com o sensor de UV).
O sensor deve ser alimentado entre 3,3V e 5V e funcionará entre -40oC e +80oC com uma precisão de +/- 0,5oC para temperatura e +/- 2% para umidade relativa. Também é importante ter em mente que seu período de detecção dura em média 2 segundos (tempo mínimo entre leituras).
O site da Adafruit fornece muitas informações sobre o DHT22 e seu irmão DHT11. Para mais detalhes, por favor visite: DHT22/11 Tutorial page .

 

O DHT22 possúi 4 pinos (olhando-se o sensor de frente, o pino 1 é o mais à esquerda):

  1. VCC (conectado ao 3.3V do NodeMCU);
  2. Data – saída;
  3. Não conectado
  4. Terra.

Uma vez que você normalmente usará o sensor em distâncias menores que 20m, um resistor de 10K deve ser conectado entre os pinos de dados e VCC. O pino de saída será conectado ao pino D3 do NodeMCU (veja o diagrama acima).

Uma vez que o sensor é instalado em nosso módulo, baixe a biblioteca DHT do Adafruit GitHub repository e a instale em seu Arduino IDE.

No início do código, devemos incluir as linhas:

/* DHT22*/
#include "DHT.h"
#define DHTPIN D2  
#define DHTTYPE DHT22 
DHT dht(DHTPIN, DHTTYPE);
float hum = 0;
float temp = 0;

Criaremos uma nova função para ler os dados do sensor:

/* Get DHT data */
void getDhtData(void)
{
  float tempIni = temp;
  float humIni = hum;
  temp = dht.readTemperature();
  hum = dht.readHumidity();
  if (isnan(hum) || isnan(temp))   // Check if any reads failed and exit early (to try again).
  {
    Serial.println("Failed to read from DHT sensor!");
    temp = tempIni;
    hum = humIni;
    return;
  }
}

O código completo, incluindo os sensores UV e DHT, pode ser baixado de meu GitHub: NodeMCU_UV_DHT_Sensor_OLED

6. Enviando dados ao ThingSpeak.com

Até agora, tudo o que fizemos com o NodeMCU ESP12-E podería ter sido obtido com um Arduino UNO. É claro, nós apenas “arranhamos” o potencial real deste chip espetacular e agora é a hora de decolar para o espaço! Ou melhor para as estrelas! Ehr… para a nuvem! 😉

 

Vamos lá!

  1. Abra uma conta no ThinkSpeak.com
  2. Siga as instruções para criar um canal e tome nota do seu Channel ID e Write API Key
  3. Atualize o código abaixo com as credenciais de sua rede WiFi e do Thinkspeak
  4. Execute o programa em seu IDE

Comentemos as partes mais importantes do código:

Primeiro, chamemos a biblioteca ESP8266, em seguida definamos o cliente WiFi, suas credenciais locais (Roteador e Thinkspeak):

/* ESP12-E & Thinkspeak*/
#include<ESP8266WiFi.h>
WiFiClient client; 
const char* MY_SSID = "YOUR SSD ID HERE"; 
const char* MY_PWD = "YOUR PASSWORD HERE"; 
const char* TS_SERVER = "api.thingspeak.com"; 
String TS_API_KEY ="YOUR CHANNEL WRITE API KEY";

Em segundo lugar, incluiremos uma biblioteca muito importante para projetos de IoT: SimpleTimer.h:

/* TIMER */
#include <SimpleTimer.h>
SimpleTimer timer;

Terceiro, durante o setup(), iniciaremos a comunicação serial, chamaremos a função connectWiFi () e definiremos os timers.

Observe que a linha de código:

timer.setInterval (60000L, sendDataTS);

irá chamar a função sendDataTS () a cada 60 segundos, para fazer o upload de dados para o canal ThinkSpeak.

void setup() 
{
  ...
  Serial.begin(115200);
  delay(10);
  ...
  connectWifi();
  timer.setInterval(60000L, sendDataTS);
  ...
}

Por último, mas não menos importante, durante o loop (), o único comando necessário é iniciar o temporizador e pronto!

void loop() 
{
  ...
  timer.run(); // Initiates SimpleTimer
}

Abaixo, você encontrará as duas funções mais importantes utilizadas para lidar com a comunicação com o Thingspeak:

Conexão ESP12E com sua rede WiFi:

/***************************************************
 * Connecting WiFi
 **************************************************/
void connectWifi()
{
  Serial.print("Connecting to "+ *MY_SSID);
  WiFi.begin(MY_SSID, MY_PWD);
  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(1000);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi Connected");
  Serial.println("");  
}

ESP12-E enviando dados ao ThinkSpeak:

/***************************************************
 * Sending Data to Thinkspeak Channel
 **************************************************/
void sendDataTS(void)
{
   if (client.connect(TS_SERVER, 80)) 
   { 
     String postStr = TS_API_KEY;
     postStr += "&field1=";
     postStr += String(dataSensorUV);
     postStr += "&field2=";
     postStr += String(indexUV);
     postStr += "&field3=";
     postStr += String(temp);
     postStr += "&field4=";
     postStr += String(hum);

     postStr += "\r\n\r\n";
   
     client.print("POST /update HTTP/1.1\n");
     client.print("Host: api.thingspeak.com\n");
     client.print("Connection: close\n");
     client.print("X-THINGSPEAKAPIKEY: " + TS_API_KEY + "\n");
     client.print("Content-Type: application/x-www-form-urlencoded\n");
     client.print("Content-Length: ");
     client.print(postStr.length());
     client.print("\n\n");
     client.print(postStr);
     delay(1000); 
   }
   sent++;
   client.stop();
}

O código completo poderá ser baixado desde meu  GitHub: NodeMCU_UV_DHT_Sensor_OLED_TS_EXT

Depois de ter o código carregado em seu NodeMCU, conectemos uma bateria externa e façamos algumas medições sob o sol.

Coloquei a Estação Remota no telhado e começei a capturar dados no ThingSpeak.com como mostrado na foto acima.

7. Conclusão

Como sempre, espero que este projeto possa ajudar que outros  encontrem o caminho para o emocionante mundo da eletrônica e do IoT!
Para detalhes e código final, por favor visite o meu depositário GitHub: RPi-NodeMCU-Weather-Station

 

Fique ligado! Em um próximo tutorial, enviaremos dados de uma estação meteorológica remota para uma estação central, com base em um servidor da Web Raspberry Pi:

Saludos desde el sur del mundo!

Até o próximo tutorial!

Obrigado,

Marcelo

 

Anúncios

Trackbacks e Pingbacks:

  1. Programando MicroPython no ESP8266 « - junho 6, 2018

    […] sensor analógico, como temperatura (LM35), radiação ultravioleta (UV) (conforme mostrado neste tutorial ) ou luminosidade usando um LDR (“Resistor variável por […]

    Curtir

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s