A construção de projetos de monitoramento ambiental é fundamental para diversas aplicações, desde a agricultura até residências inteligentes. Hoje, quero explorar a construção de um protótipo de um sistema de visualização de temperatura e umidade usando o microcontrolador STM32 Nucleo L476RG. Este projeto envolve a integração de sensores, um display TFT e o uso da programação em C++ com o framework Arduino.
Objetivo do Projeto
O código proposto permite monitorar continuamente a temperatura e a umidade do ambiente, exibindo os resultados em tempo real através de um display gráfico. Além disso, o sistema registra os valores máximos e mínimos atingidos nas últimas 24h e apresenta um gráfico simples que ajuda a visualizar as tendências ao longo do tempo.
Hardware Necessário
- STM32 Nucleo L476RG: Uma plataforma de prototipagem que oferece um equilíbrio entre desempenho e custo.
- DHT22: Sensor de temperatura e umidade, conhecido por sua precisão e durabilidade.
- MCUFRIEND_kbv: Display TFT compatível com diversos controladores gráficos.
Pinagem

Sensor DHT22
- VCC: O pino de alimentação (VCC) do DHT22 é conectado ao pino de 3.3V da placa STM32 Nucleo L476RG.
- GND: O pino de terra (GND) do DHT22 é conectado ao pino GND da placa STM32.
- DHTPIN (PB7): Este pino é utilizado para a comunicação de dados entre o sensor DHT22 e a placa STM32. .
Conexão do Display MCUFRIEND_kbv
- LCD_CS (A3): Pino de Chip Select, que é usado para ativar e desativar o sinal do display no bus de dados.
- LCD_CD (A2): Pino de Command/Data.
- LCD_WR (A1): Pino de escrita.
- LCD_RD (A0): Pino de leitura.
- LCD_RESET (A4): Pino de reset, utilizado para redefinir o display. Um pulso neste pino reinicializa o display.
Configuração do Projeto no VSCode com PlatformIO
O arquivo platformio.ini
define as bibliotecas necessárias e a configuração do ambiente:
[env:nucleo_l476rg]
platform = ststm32
board = nucleo_l476rg
framework = arduino
lib_deps =
adafruit/DHT sensor library@^1.4.6
adafruit/Adafruit GFX Library@^1.11.9
prenticedavid/MCUFRIEND_kbv@^3.1.0-Beta
adafruit/Adafruit ST7735 and ST7789 Library@^1.10.3
INIAnálise do código fonte
Armazenamento de Dados das Últimas 24 Horas
Para monitorar as condições ambientais ao longo de um dia, o código emprega arrays para armazenar as leituras de temperatura e umidade:
const int NUM_READINGS = 1440; // Total de leituras para 24 horas com intervalos de um minuto
float tempReadings[NUM_READINGS]; // Armazena as leituras de temperatura
float humidityReadings[NUM_READINGS]; // Armazena as leituras de umidade
int readingIndex = 0; // Índice atual para inserção de nova leitura
C++Visualização de Dados com Barras de Progresso
Para facilitar a visualização das leituras atuais em relação aos valores mínimos e máximos, o código utiliza barras de progresso dinâmicas:
void drawProgressBar(int x, int y, int width, int height, int value, int max, uint16_t barColor) {
int filledWidth = (int)((width * value) / max);
tft.fillRect(x, y, filledWidth, height, barColor); // Área preenchida
tft.fillRect(x + filledWidth, y, width - filledWidth, height, BLACK); // Área não preenchida
}
C++Mapeamento de Cores de Acordo com os Valores
As cores das barras de progresso mudam de acordo com os valores de temperatura e umidade para uma interpretação visual imediata:
uint16_t getTemperatureColor(int temp) {
if (temp >= 30) return RED;
if (temp < 10) return BLUE;
int red = map(temp, 10, 30, 0, 255);
int blue = map(temp, 10, 30, 255, 0);
return tft.color565(red, 0, blue);
}
uint16_t getHumidityColor(int humidity) {
if (humidity <= 30) return RED;
if (humidity > 60) return BLUE;
int blue = map(humidity, 20, 90, 0, 255);
int red = map(humidity, 20, 90, 255, 0);
return tft.color565(red, 0, blue);
}
C++Uso do Indicador para Agrupar Elementos
O código utiliza uma função chamada drawIndicador
para agrupar a exibição dos elementos no display, reduzindo a repetição de código:
void drawIndicador(int x, int y, float value, float min, float max, String indicador, String unidade, uint16_t cor) {
tft.fillRect(x, y, 320, 70, BLACK); // Limpa a área do indicador
tft.setTextColor(cor);
tft.print(indicador + ": " + String(value) + " " + unidade); // Valor atual
tft.setTextColor(WHITE);
tft.print("Min: " + String(min) + " | Max: " + String(max)); // Valores mínimo e máximo
// Desenha a barra de progresso correspondente
if (indicador == "Temperatura") {
drawProgressBar(x, y + 45, 300, 20, value, 40, getTemperatureColor(value));
} else if (indicador == "Umidade") {
drawProgressBar(x, y + 45, 300, 20, value, 100, getHumidityColor(value));
}
}
C++Visualização de Dados com um gráfico
A visualização gráfica é uma parte fundamental deste projeto, permitindo uma compreensão rápida das tendências de temperatura e umidade ao longo das últimas 24h.
void drawTemperatureChart(int x, int y, int width, int height, float vector[], int tamanho, int iterator, uint16_t cor) {
for (int i = 0; i < iterator; i++) {
int y_ponto = static_cast<int>(round((vector[i] - 15) / (40 - 15) * height));
int x_ponto = static_cast<int>(round(i / float(tamanho) * width));
tft.drawPixel(x + x_ponto, y + height - y_ponto, cor);
}
}
void drawHumidityChart(int x, int y, int width, int height, float vector[], int tamanho, int iterator, uint16_t cor) {
for (int i = 0; i < iterator; i++) {
int y_ponto = static_cast<int>(round(vector[i] / 100.0 * height));
int x_ponto = static_cast<int>(round(i / float(tamanho) * width));
tft.drawPixel(x + x_ponto, y + height - y_ponto, cor);
}
}
C++Estas funções mapeiam as leituras armazenadas nos arrays em pontos que são desenhados no display TFT. O ajuste dos valores de y_ponto e x_ponto assegura que os gráficos se ajustem ao espaço disponível no display, enquanto preservam as relações de proporção adequadas.
Código completo
Você pode clonar o repositório do meu gitlab aqui
#include <Arduino.h>
#include <Adafruit_Sensor.h>
#include <DHT.h>
#include <DHT_U.h>
#include <Adafruit_GFX.h> // Biblioteca gráfica base
#include <MCUFRIEND_kbv.h>
#define DHTPIN PB7
#define DHTTYPE DHT22
// Define os pinos conforme seu hardware
#define LCD_CS A3
#define LCD_CD A2
#define LCD_WR A1
#define LCD_RD A0
#define LCD_RESET A4
#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF
#define RGB(r, g, b) (((r&0xF8)<<8)|((g&0xFC)<<3)|(b>>3))
#define GREY RGB(127, 127, 127)
#define DARKGREY RGB(64, 64, 64)
#define TURQUOISE RGB(0, 128, 128)
#define PINK RGB(255, 128, 192)
#define OLIVE RGB(128, 128, 0)
#define PURPLE RGB(128, 0, 128)
#define AZURE RGB(0, 128, 255)
#define ORANGE RGB(255,128,64)
//DHT dht(DHTPIN, DHTTYPE);
DHT_Unified dht(DHTPIN, DHTTYPE);
MCUFRIEND_kbv tft;
uint32_t delayMS;
const int NUM_READINGS = 1440;
float tempReadings[NUM_READINGS];
float humidityReadings[NUM_READINGS];
int readingIndex = 0;
int readingsCount = 0;
float tempMin = 500;
float tempMax = -100;
float humidityMin = 100;
float humidityMax = 0;
// Função para obter a cor baseada na temperatura
uint16_t getTemperatureColor(int temp) {
if (temp >= 30) return RED;
if (temp < 10) return BLUE;
// Interpolação linear de azul para vermelho entre 10°C e 30°C
int red = map(temp, 10, 30, 0, 255);
int blue = map(temp, 10, 30, 255, 0);
return tft.color565(red, 0, blue);
}
// Função para obter a cor baseada na umidade
uint16_t getHumidityColor(int humidity) {
if (humidity <= 30) return RED;
if (humidity > 60) return BLUE;
// Interpolação linear de vermelho para azul entre 20% e 90%
int blue = map(humidity, 20, 90, 0, 255);
int red = map(humidity, 20, 90, 255, 0);
return tft.color565(red, 0, blue);
}
// Função para desenhar a barra de progresso
void drawProgressBar(int x, int y, int width, int height, int value, int max, uint16_t barColor) {
int filledWidth = (int)((width * value) / max);
tft.fillRect(x, y, filledWidth, height, barColor); // Área preenchida
tft.fillRect(x + filledWidth, y, width - filledWidth, height, BLACK); // Área não preenchida
}
// Função para desenhar a barra de progresso
void drawIndicador(int x, int y, float value, float min, float max,String indicador, String unidade, uint16_t cor ) {
tft.fillRect(x, y, 320, 70, BLACK); // tft.fillRect(10, 5, 320, 70, BLUE);
tft.setTextSize(2);
tft.setCursor(x, y+5);
tft.setTextColor(cor);
tft.print(indicador+": "+String(value)+" "+unidade);
tft.setTextColor(WHITE);
tft.setCursor(x, y+30);
tft.setTextSize(1);
tft.print("Min: "+String(min)+" | Max: "+String(max));
if(indicador == "Temperatura"){
drawProgressBar(x, y+45, 300, 20, value, 40, getTemperatureColor(value));
}else if(indicador == "Umidade"){
drawProgressBar(x, y+45, 300, 20, value, 100, getHumidityColor(value));
}
}
void drawTemperatureChart(int x, int y, int width, int height,float vector[], int tamanho, int iterator,uint16_t cor){
int y_ponto, x_ponto = 0;
//tft.fillRect(x, y, width, height, BLACK); // tft.fillRect(10, 5, 320, 70, BLUE);
for(int i=0;i<iterator;i++){
y_ponto = static_cast<int>(round((static_cast<float>(vector[i]-15) / (40-15))*height));
x_ponto = static_cast<int>(round((static_cast<float>(i) / static_cast<float>(tamanho)) * static_cast<float>(width)));
tft.drawPixel(x+x_ponto, y+height-y_ponto, cor);
}
}
void drawHumidityChart(int x, int y, int width, int height,float vector[], int tamanho, int iterator,uint16_t cor){
int y_ponto, x_ponto = 0;
//tft.fillRect(x, y, width, height, BLACK);
for(int i=0;i<iterator;i++){
y_ponto = static_cast<int>(round((static_cast<float>(vector[i]) / (100))*height));
x_ponto = static_cast<int>(round((static_cast<float>(i) / static_cast<float>(tamanho)) * static_cast<float>(width)));
tft.drawPixel(x+x_ponto, y+height-y_ponto, cor);
}
}
void setup() {
tft.reset();
uint16_t identifier = tft.readID();
tft.begin(identifier);
tft.setRotation(1); // Ajuste conforme a orientação desejada
tft.fillScreen(BLACK);
tft.setTextColor(WHITE);
tft.setTextSize(2);
Serial.begin(9600);
while (!Serial); // Espera a conexão do serial
delay(1000); // Pequeno atraso após abrir a conexão serial
dht.begin();
Serial.println(F("Iniciando DHT22"));
sensor_t sensor;
dht.temperature().getSensor(&sensor);
Serial.println(F("------------------------------------"));
Serial.println(F("Temperature Sensor"));
Serial.print (F("Sensor Type: ")); Serial.println(sensor.name);
Serial.print (F("Driver Ver: ")); Serial.println(sensor.version);
Serial.print (F("Unique ID: ")); Serial.println(sensor.sensor_id);
Serial.print (F("Max Value: ")); Serial.print(sensor.max_value); Serial.println(F("°C"));
Serial.print (F("Min Value: ")); Serial.print(sensor.min_value); Serial.println(F("°C"));
Serial.print (F("Resolution: ")); Serial.print(sensor.resolution); Serial.println(F("°C"));
Serial.println(F("------------------------------------"));
// Print humidity sensor details.
dht.humidity().getSensor(&sensor);
Serial.println(F("Humidity Sensor"));
Serial.print (F("Sensor Type: ")); Serial.println(sensor.name);
Serial.print (F("Driver Ver: ")); Serial.println(sensor.version);
Serial.print (F("Unique ID: ")); Serial.println(sensor.sensor_id);
Serial.print (F("Max Value: ")); Serial.print(sensor.max_value); Serial.println(F("%"));
Serial.print (F("Min Value: ")); Serial.print(sensor.min_value); Serial.println(F("%"));
Serial.print (F("Resolution: ")); Serial.print(sensor.resolution); Serial.println(F("%"));
Serial.println(F("------------------------------------"));
Serial.print (F("Min Delay: ")); Serial.print(sensor.min_delay/1000/1000); Serial.println(F("s"));
Serial.println(F("------------------------------------"));
Serial.print (F("Display Height: ")); Serial.print(tft.height()); Serial.println(F(""));
Serial.print (F("Display Width: ")); Serial.print(tft.width()); Serial.println(F(""));
delayMS = 60 * 1000;
}
void loop() {
sensors_event_t event;
dht.temperature().getEvent(&event);
float tempValue = isnan(event.temperature) ? 0 : event.temperature;
dht.humidity().getEvent(&event);
float humidityValue = isnan(event.relative_humidity) ? 0 : event.relative_humidity;
tempReadings[readingIndex] = tempValue;
humidityReadings[readingIndex] = humidityValue;
readingIndex = (readingIndex + 1) % NUM_READINGS;
readingsCount = min(readingsCount + 1, NUM_READINGS);
tempMin = tempMax = tempReadings[0];
humidityMin = humidityMax = humidityReadings[0];
for (int i = 0; i < readingsCount; i++) {
tempMin = min(tempMin, tempReadings[i]);
tempMax = max(tempMax, tempReadings[i]);
humidityMin = min(humidityMin, humidityReadings[i]);
humidityMax = max(humidityMax, humidityReadings[i]);
}
if (!isnan(event.temperature)) {
drawIndicador(10,5,tempValue,tempMin,tempMax,"Temperatura","C", BLUE);
drawTemperatureChart(10,160, 300, 60,tempReadings,NUM_READINGS, readingsCount, BLUE);
}
if (!isnan(event.relative_humidity)) {
drawIndicador(10,80,humidityValue,humidityMin,humidityMax,"Umidade","%", GREEN);
drawHumidityChart(10,160, 300, 60,humidityReadings,NUM_READINGS, readingsCount, GREEN);
}
delay(delayMS);
}
C++