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
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
- 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:
4. Um medidor local 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
O DHT22 possúi 4 pinos (olhando-se o sensor de frente, o pino 1 é o mais à esquerda):
- VCC (conectado ao 3.3V do NodeMCU);
- Data – saída;
- Não conectado
- 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
Vamos lá!
- Abra uma conta no ThinkSpeak.com
- Siga as instruções para criar um canal e tome nota do seu Channel ID e Write API Key
- Atualize o código abaixo com as credenciais de sua rede WiFi e do Thinkspeak
- 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
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
Exelente, muito bom trabalho, parabéns Marcelo …
CurtirCurtir
I found the simple timer. I built this and have issues with it. It is not reading the DHT11 sensor even though I changed it in the code. Serial monitor displays UV as 12 and failure to connect to dht sensor. The OLED display shows 13 for temperature and 145 for humidity, displays mV12 and index 0. Actual temperature is 23 and humidity in 43. UV doesn’t bother me as I’m testing inside to get everything working before going outside.
I really like this project and want to use it. Can you help me out.
CurtirCurtir
I am getting an error in compiling NodeMCU_UV_DHT_Sensor.ino.
Arduino: 1.8.9 (Windows Store 1.8.21.0) (Windows 10), Board: “NodeMCU 1.0 (ESP-12E Module), 80 MHz, Flash, Disabled, All SSL ciphers (most compatible), 4M (no SPIFFS), v2 Lower Memory, Disabled, None, Only Sketch, 115200”
NodeMCU_UV_DHT_Sensor_OLED:44:25: error: SimpleTimer.h: No such file or directory
#include
^
compilation terminated.
exit status 1
SimpleTimer.h: No such file or directory
This report would have more information with
“Show verbose output during compilation”
option enabled in File -> Preferences.
Can you please help me out? I’m in process of building your station.
Thanks
Bob Bock
PS English language please.
CurtirCurtir
I really like this weather station. I may build it. I already have a DHT11 sensor which should be easy to use by replacing the DHT22 call out for the DHT11. I would like to know how to change the temperature reading to degrees F as I live in the USA. I still need to get the UV sensor. Thank you for including a link to it.
CurtirCurtir