Robótica com C — Bloco 3: Projetos Práticos
Um monitor ambiental que lê temperatura e umidade do ar em tempo real e envia os dados para o celular via Bluetooth. O Arduino funciona de forma autônoma — envia leituras a cada 10 segundos automaticamente e também responde a pedidos sob demanda.
TEMP:24.5 HUM:62
"R"Componente novo: sensor DHT11 / DHT22. Este projeto apresenta o sensor de temperatura e umidade mais popular da plataforma Arduino. É o primeiro sensor deste curso que usa um protocolo de comunicação proprietário de 1 fio e requer uma biblioteca específica — um padrão que você vai encontrar em muitos outros sensores.
O DHT11 e o DHT22 são sensores digitais de temperatura e umidade relativa. Apesar de terem aparência similar, diferem significativamente em precisão e faixa:
| Especificação | DHT11 | DHT22 (AM2302) |
|---|---|---|
| Faixa de temperatura | 0°C a 50°C | -40°C a 80°C |
| Precisão de temperatura | ±2°C | ±0,5°C |
| Faixa de umidade | 20% a 80% UR | 0% a 100% UR |
| Precisão de umidade | ±5% UR | ±2% UR |
| Intervalo mínimo entre leituras | 1 segundo | 0,5 segundo |
| Protocolo | 1-Wire proprietário | 1-Wire proprietário |
| Resistor pull-up | 10kΩ entre DATA e VCC | 10kΩ entre DATA e VCC |
| Preço estimado (BR) | R$ 8–15 | R$ 20–35 |
| Para este projeto | ✅ Suficiente | ✅ Melhor precisão |
DHT11 ou DHT22? Para projetos de aprendizado e monitoramento residencial, o DHT11 é suficiente. Para projetos que exigem precisão (estufa, sala de servidor, laboratório), use o DHT22. O código é idêntico — só muda a constante DHT11 ou DHT22 na declaração do objeto.
O DHT11/22 tem 4 pinos, mas apenas 3 são usados. Sempre use um resistor pull-up de 10kΩ entre DATA e VCC:
| Pino do DHT | Conectar em | Observação |
|---|---|---|
| VCC (pino 1) | 5V | DHT11 aceita 3,3V a 5V |
| DATA (pino 2) | Pino 4 do Arduino + resistor 10kΩ → VCC | Resistor pull-up obrigatório |
| NC (pino 3) | Não conectar | Not Connected — ignorar |
| GND (pino 4) | GND | — |
Resistor pull-up não é opcional: o protocolo 1-Wire do DHT requer que a linha DATA fique em HIGH quando ninguém está transmitindo. Sem o pull-up de 10kΩ, a linha fica flutuante e as leituras retornam NaN ou -999 mesmo com o sensor funcionando. Muitos módulos DHT já vêm com o resistor soldado na placa — verifique antes de adicionar um segundo.
O sensor usa um protocolo proprietário temporalmente crítico — a biblioteca encapsula toda essa complexidade e expõe apenas duas funções simples:
No Wokwi: a biblioteca DHT da Adafruit já está disponível nativamente. Adicione o componente DHT22 ou DHT11 no diagrama, conecte ao pino desejado e use normalmente — sem instalar nada.
// Uso básico da biblioteca: #include <DHT.h> #define PINO_DHT 4 #define TIPO_DHT DHT11 // ou DHT22 DHT dht(PINO_DHT, TIPO_DHT); // cria o objeto sensor void setup() { dht.begin(); // inicializa o sensor } void loop() { float temp = dht.readTemperature(); // graus Celsius float hum = dht.readHumidity(); // porcentagem // Verifica leitura inválida (sensor desconectado ou erro) if (isnan(temp) || isnan(hum)) { Serial.println("ERR:leitura_falhou"); return; } Serial.print("Temp: "); Serial.print(temp); Serial.println(" C"); Serial.print("Hum: "); Serial.print(hum); Serial.println(" %"); }
isnan() — verificando leitura inválida: quando o sensor falha (desconectado, pull-up ausente, leitura fora do intervalo), readTemperature() retorna NaN (Not a Number). A função isnan() detecta esse caso — sem essa verificação, você exibiria "Temp: nan C" no celular e não saberia se é problema de hardware ou de código.
Boa notícia: diferente dos projetos com HC-05, o sensor DHT11/DHT22 é simulado nativamente no Wokwi. Você pode adicionar o componente, girar um slider virtual para mudar temperatura e umidade durante a simulação, e ver as leituras chegando no Monitor Serial em tempo real.
A única limitação continua sendo o HC-05 — use o Monitor Serial como substituto do celular para validar a lógica completa antes de passar para o hardware físico.
Roteiro de simulação no Wokwi:
"R" no Monitor Serial para forçar uma leitura imediata| Componente | Conexão | Função |
|---|---|---|
| DHT11/22 DATA | Pino 4 + 10kΩ → 5V | Sensor de temperatura e umidade |
| HC-05 TX → pino 10 | Direto | Bluetooth — recebe comandos do celular |
| HC-05 RX ← pino 11 | Via divisor R1=1kΩ, R2=2kΩ | Bluetooth — envia dados ao celular |
| LED alerta + 220Ω | Pino 8 → R → LED → GND | Indica alerta de temperatura ou umidade |
/* * ===================================================== * Projeto 04 — Monitor de temperatura e umidade * DHT11/DHT22 + Bluetooth HC-05 * ===================================================== * Envio automático a cada 10 segundos * Comandos: R=leitura imediata | I=intervalo * A=alertas on/off | ?=ajuda * Formato: TEMP:xx.x HUM:xx.x STATUS:ok/alerta * ===================================================== */ #include <DHT.h> #include <SoftwareSerial.h> // ── Sensor ───────────────────────────────────────── #define PINO_DHT 4 #define TIPO_DHT DHT11 DHT dht(PINO_DHT, TIPO_DHT); // ── Bluetooth ────────────────────────────────────── SoftwareSerial bluetooth(10, 11); // ── Pinos ────────────────────────────────────────── const int PINO_ALERTA = 8; // ── Configuração de alertas ──────────────────────── float limiarTempMax = 30.0; // °C — acima disso: alerta float limiarHumMin = 30.0; // % — abaixo disso: alerta bool alertasAtivos = true; // ── Temporização ─────────────────────────────────── unsigned long intervaloEnvio = 10000; // 10s padrão unsigned long ultimoEnvio = 0; // ════════════════════════════════════════════════════ // Lê o sensor e retorna true se leitura for válida // ════════════════════════════════════════════════════ bool lerSensor(float* temp, float* hum) { *temp = dht.readTemperature(); *hum = dht.readHumidity(); return (!isnan(*temp) && !isnan(*hum)); } // ════════════════════════════════════════════════════ // Envia leitura formatada pelo Bluetooth // ════════════════════════════════════════════════════ void enviarLeitura() { float temp, hum; if (!lerSensor(&temp, &hum)) { bluetooth.println("ERR:sensor"); Serial.println("ERR:sensor"); return; } // Verifica condições de alerta bool emAlerta = alertasAtivos && (temp > limiarTempMax || hum < limiarHumMin); digitalWrite(PINO_ALERTA, emAlerta ? HIGH : LOW); // Envia dados formatados bluetooth.print("TEMP:"); bluetooth.print(temp, 1); bluetooth.print(" HUM:"); bluetooth.print(hum, 1); bluetooth.print(" ST:"); bluetooth.println(emAlerta ? "ALERTA" : "OK"); Serial.print("TEMP:"); Serial.print(temp, 1); Serial.print(" HUM:"); Serial.print(hum, 1); Serial.print(" ST:"); Serial.println(emAlerta ? "ALERTA" : "OK"); } // ════════════════════════════════════════════════════ // Processa comandos do celular // ════════════════════════════════════════════════════ void processarMensagem(String msg) { if (msg.length() == 0) return; char cmd = msg[0]; switch (cmd) { case 'R': case 'r': // Leitura imediata sob demanda enviarLeitura(); break; case 'I': case 'i': // "I30" → muda intervalo para 30 segundos if (msg.length() > 1) { int seg = constrain(msg.substring(1).toInt(), 5, 300); intervaloEnvio = (unsigned long)seg * 1000UL; bluetooth.print("INTERVALO:"); bluetooth.println(seg); } break; case 'A': case 'a': // Toggle de alertas alertasAtivos = !alertasAtivos; bluetooth.print("ALERTAS:"); bluetooth.println(alertasAtivos ? "ON" : "OFF"); if (!alertasAtivos) digitalWrite(PINO_ALERTA, LOW); break; case '?': bluetooth.println("Cmds: R=ler I5-300=intervalo A=alertas"); break; case '\n': case '\r': return; default: bluetooth.print("ERR:"); bluetooth.println(cmd); } } // ════════════════════════════════════════════════════ // SETUP e LOOP // ════════════════════════════════════════════════════ void setup() { pinMode(PINO_ALERTA, OUTPUT); dht.begin(); Serial.begin(9600); bluetooth.begin(9600); Serial.println("=== Monitor Ambiental ==="); delay(2000); // DHT precisa de ~2s para estabilizar após ligar enviarLeitura(); // envia leitura inicial imediatamente ultimoEnvio = millis(); } void loop() { // Envio periódico automático if (millis() - ultimoEnvio >= intervaloEnvio) { ultimoEnvio = millis(); enviarLeitura(); } // Processa comandos recebidos if (bluetooth.available() > 0) { String msg = bluetooth.readStringUntil('\n'); msg.trim(); processarMensagem(msg); } // Espelho para debug if (Serial.available() > 0) { bluetooth.write(Serial.read()); } }
=== Monitor Ambiental ===
TEMP:24.5 HUM:62.0 ST:OK
... 10s depois ...
TEMP:24.7 HUM:61.5 ST:OK
[celular envia "R"] TEMP:24.8 HUM:61.0 ST:OK
[celular envia "I30"] INTERVALO:30
... temperatura sobe para 31°C ...
TEMP:31.2 HUM:60.0 ST:ALERTA
[celular envia "A"] ALERTAS:OFF
Substitua bluetooth por Serial. O sensor DHT funciona normalmente no Wokwi — ajuste temperatura e umidade com os sliders do componente durante a simulação:
// ── Versão WOKWI ─────────────────────────────────── // Remove SoftwareSerial — usa Serial direto // DHT22 funciona normalmente no Wokwi // Envie pelo Monitor Serial: R I30 A ? #include <DHT.h> #define PINO_DHT 4 #define TIPO_DHT DHT22 // Wokwi usa DHT22 por padrão DHT dht(PINO_DHT, TIPO_DHT); const int PINO_ALERTA = 8; float limiarTempMax = 30.0; float limiarHumMin = 30.0; bool alertasAtivos = true; unsigned long intervaloEnvio = 10000; unsigned long ultimoEnvio = 0; bool lerSensor(float* temp, float* hum) { *temp = dht.readTemperature(); *hum = dht.readHumidity(); return (!isnan(*temp) && !isnan(*hum)); } void enviarLeitura() { float temp, hum; if (!lerSensor(&temp, &hum)) { Serial.println("ERR:sensor"); return; } bool emAlerta = alertasAtivos && (temp > limiarTempMax || hum < limiarHumMin); digitalWrite(PINO_ALERTA, emAlerta ? HIGH : LOW); Serial.print("TEMP:"); Serial.print(temp, 1); Serial.print(" HUM:"); Serial.print(hum, 1); Serial.print(" ST:"); Serial.println(emAlerta ? "ALERTA" : "OK"); } void processarMensagem(String msg) { if (msg.length() == 0) return; char cmd = msg[0]; switch (cmd) { case 'R': case 'r': enviarLeitura(); break; case 'I': case 'i': if (msg.length() > 1) { int seg = constrain(msg.substring(1).toInt(), 5, 300); intervaloEnvio = (unsigned long)seg * 1000UL; Serial.print("INTERVALO:"); Serial.println(seg); } break; case 'A': case 'a': alertasAtivos = !alertasAtivos; Serial.print("ALERTAS:"); Serial.println(alertasAtivos ? "ON" : "OFF"); if (!alertasAtivos) digitalWrite(PINO_ALERTA, LOW); break; case '?': Serial.println("Cmds: R I5-300 A ?"); break; case '\n': case '\r': return; default: Serial.print("ERR:"); Serial.println(cmd); } } void setup() { pinMode(PINO_ALERTA, OUTPUT); dht.begin(); Serial.begin(9600); Serial.println("=== Monitor Wokwi — R I30 A ?"); delay(2000); enviarLeitura(); ultimoEnvio = millis(); } void loop() { if (millis() - ultimoEnvio >= intervaloEnvio) { ultimoEnvio = millis(); enviarLeitura(); } if (Serial.available() > 0) { String msg = Serial.readStringUntil('\n'); msg.trim(); processarMensagem(msg); } }
Testando alertas no Wokwi: durante a simulação, clique no componente DHT22 e arraste o slider de temperatura para acima de 30°C. Você deve ver ST:ALERTA no Monitor Serial e o LED de alerta acender. Arraste de volta para menos de 30°C e confirme que o alerta desaparece. Isso valida toda a lógica de limiar antes de usar o hardware físico.
lerSensor(float*, float*) usa ponteiros?A função precisa retornar dois valores (temperatura e umidade) e ainda indicar se a leitura foi válida — três informações em uma única chamada. A solução é passar os dois floats por ponteiro (saída) e usar o bool de retorno para indicar sucesso ou falha. É exatamente o padrão que você aprendeu na Aula 14 com minMax().
delay(2000) no setup()?O DHT11/22 precisa de pelo menos 1–2 segundos para estabilizar após ser alimentado. Chamar readTemperature() antes disso retorna NaN. O delay() no setup() é aceitável aqui — acontece apenas uma vez e não bloqueia nenhuma tarefa crítica.
Em um monitor ambiental, o usuário quer ser notificado de condições adversas — não ficar pedindo leituras manualmente. O envio periódico garante que o celular sempre tem dados atualizados e que alertas chegam mesmo quando o usuário não está interagindo com o app.
Padrão push vs pull: envio automático é o padrão "push" — o Arduino empurra dados pro celular. Resposta a comando é o padrão "pull" — o celular puxa os dados. Este projeto usa os dois simultaneamente, o que é o comportamento esperado de um sistema de monitoramento real.
| Conceito | Aula de origem | Onde aparece neste projeto |
|---|---|---|
| Passagem por ponteiro — dois retornos | Aula 14 | lerSensor(float* temp, float* hum) |
| Verificação de NaN | Conceito novo | isnan(temp) — detecta leitura inválida do DHT |
Envio periódico com millis() |
Aula 41b / 45 | Leitura automática a cada intervaloEnvio ms |
| Intervalo configurável em runtime | Projeto 01 | Comando "I30" muda o intervalo sem reiniciar |
| Bluetooth HC-05 + protocolo | Projetos 02 e 03 | Canal de comunicação — substituível por Serial no Wokwi |
| Feedback estruturado chave:valor | Aula 46 | "TEMP:24.5 HUM:62.0 ST:OK" |
| Biblioteca externa com #include | Aula 41a | #include <DHT.h> — instala pelo Gerenciador |
Teste sua compreensão do sensor DHT e do monitor ambiental: