Guias · 28 min de leitura

Projeto de estufa inteligente com Arduino: sensores, irrigação, ventoinha e código

Monte uma estufa inteligente com Arduino: DHT22, umidade do solo e LDR, controle de ventoinha, aquecedor, bomba e LEDs com relé e MOSFET, histerese, LCD e código para ESP32.

Um projeto de estufa inteligente com Arduino é um sistema de automação em que um Arduino ou ESP32 mede a temperatura do ar, a umidade, a umidade do solo e a luz de uma estufa e aciona sozinho a ventoinha, o aquecedor, a bomba de água e as luzes de cultivo. É um dos temas mais populares em feiras de ciências, competições escolares e aulas de tecnologia porque o resultado é fácil de ver: a bomba liga quando o solo seca, a ventoinha gira quando esquenta lá dentro e os LEDs acendem à noite. Melhor ainda, quase não há diferença de circuito ou de código entre uma maquete de estufa feita com uma caixa organizadora e uma miniestufa de verdade na sua varanda.

Neste guia começamos pelo que as plantas realmente precisam e depois montamos a maquete de estufa passo a passo: a lista de peças e o custo aproximado, as ligações dos sensores (DHT22, sensor capacitivo de umidade do solo, LDR) e dos atuadores (ventoinha, aquecedor, bomba e LEDs por relés e um MOSFET), o plano de alimentação e a tabela de pinos. Em seguida explicamos a lógica de limite e histerese que impede os relés de ficarem estalando o tempo todo, o código Arduino completo sem delay() e o LCD 16x2. Nas últimas seções você encontra como acompanhar a estufa pelo celular com um ESP32, uma versão em MicroPython para o Raspberry Pi Pico, dicas para apresentações na escola e em feiras de ciências e os erros mais comuns.

DHT22 (AM2302)
DHT22 (AM2302)
LDR
LDR
LCD I2C 16x2
LCD I2C 16x2
LM2596
LM2596
Arduino Uno
Arduino Uno

Como funciona uma estufa inteligente?

Toda estufa inteligente roda o mesmo ciclo de quatro passos: medir, decidir, agir, mostrar. Os sensores medem o ambiente: quanto está quente lá dentro, quão úmido está o ar, o solo secou, há luz suficiente? O Arduino compara esses valores com as regras que você definiu: “se a temperatura passar de 30 °C, ligue a ventoinha”, “se a umidade do solo cair abaixo de 35%, irrigue”. Os atuadores executam a decisão: os relés ligam e desligam a ventoinha, o aquecedor e a bomba, e um MOSFET ajusta o brilho da fita de LED. Por fim, o LCD e o celular mostram o estado atual da estufa. Os computadores de clima das estufas comerciais de verdade funcionam exatamente com esse ciclo; eles só usam mais sensores e motores maiores.

O que é medido?SensorAtuadorRegra (exemplo)
Temperatura do arDHT22Ventoinha e aquecedorA ventoinha liga a 30 °C, o aquecedor liga a 16 °C
Umidade relativaDHT22VentoinhaA ventoinha liga a 85% e desliga a 75%
Umidade do soloSensor capacitivo de umidade do soloMinibomba submersívelIrriga em pulsos de 3 segundos abaixo de 35%
LuzLDRFita de LED (MOSFET)Quando escurece, os LEDs ficam acesos por no máximo 4 horas
Água no reservatórioChave de nível tipo boiaBloqueio da bombaSe o reservatório estiver vazio, a bomba nunca liga

Do que as plantas precisam? Temperatura, umidade e luz ideais

Antes de escrever qualquer regra, você precisa saber qual é o alvo. A maioria das plantas quer temperaturas diferentes de dia e de noite; esfriar um pouco à noite ajuda a planta a descansar. Se a umidade fica acima de 85% por muito tempo, a água condensa nas folhas e doenças fúngicas como mofo e míldio se espalham rápido; é por isso que a ventoinha da estufa não serve só para resfriar, mas também para tirar a umidade. Quanto à luz, a maioria das mudas de hortaliças quer 14–16 horas por dia, mas também precisa de um período escuro de pelo menos 6–8 horas; deixar os LEDs acesos a noite toda faz mais mal do que bem à planta. Os valores abaixo são um bom guia inicial; ajuste-os para o que você estiver cultivando.

PlantaDiaNoiteUmidade relativaObservação
Tomate21–27 °C15–18 °C60–75%As flores podem cair acima de 32 °C
Pimentão21–29 °C16–20 °C60–75%Mais sensível ao frio que o tomate
Alface e espinafre15–20 °C10–15 °C60–70%Pendoa cedo com calor e dias longos
Morango18–24 °C10–13 °C60–75%O risco de mofo-cinzento aumenta com umidade alta
Mudas e germinação20–25 °C18–20 °C70–85%A temperatura do solo importa mais que a do ar
Se a sala de aula estiver a 22 °C no dia da apresentação, uma ventoinha programada para ligar a 30 °C nunca vai girar. Para a apresentação, ponha os limites 2–3 graus acima da temperatura ambiente, de modo que, ao aquecer o DHT22 na palma da mão, os avaliadores vejam a ventoinha ligar em segundos. Para o uso real, volte aos valores da tabela.

Montando a maquete da estufa

O gabinete mais fácil e barato é uma caixa organizadora de plástico transparente com tampa: uma caixa de 40–60 litros basta para dois ou três vasos, é à prova d'água e fácil de furar com um estilete ou uma ponta aquecida. Se quiser um visual mais realista, monte uma estrutura com sarrafos de madeira e revista com chapas transparentes de acrílico de 2–3 mm ou de PETG; uma estufa com telhado de duas águas fica muito mais bonita na apresentação. Uns 50×35×40 cm são um bom tamanho inicial. Seja qual for o gabinete, a tampa ou uma das paredes laterais precisa abrir; você vai pôr a mão lá dentro para regar as plantas, mudar o sensor de lugar e arrumar os cabos.

  • Eletrônica do lado de fora: o interior de uma estufa é úmido, e de manhã a água condensa nas paredes. Coloque o Arduino, o módulo relé e a fonte em uma caixa separada, fora da estufa; só os sensores, a ventoinha, o aquecedor, a mangueira da bomba e os LEDs ficam dentro.
  • Ventilação: o ar quente sobe, então monte a ventoinha na parte de cima de uma das paredes menores, soprando o ar para fora. Recorte uma entrada de ar do tamanho da ventoinha na parte de baixo da parede oposta e cubra com tela contra insetos; o ar fresco entra por baixo e o ar quente e úmido sai por cima.
  • Aquecedor: monte o aquecedor perto do piso, sobre uma chapa de alumínio ou um azulejo, a pelo menos 5 cm das paredes de plástico e das plantas. O ar quente circula pela estufa inteira enquanto sobe.
  • Iluminação de LED: cole a fita de LED na parte de dentro da tampa, 15–30 cm acima das plantas. Se você montar a fita em um perfil fino de alumínio, ela dissipa melhor o calor e o adesivo não descola com o tempo.
  • Sensores: ponha o DHT22 na altura das plantas, onde ele não receba sol direto nem a luz dos LEDs. Enterre o sensor de umidade do solo na zona das raízes, longe da borda do vaso. O LDR não pode ver os LEDs: coloque-o fora da estufa, no telhado, virado para o céu.
  • Reservatório de água: um recipiente de 1–2 litros basta para a bomba e a chave de boia. Ponha pratinhos embaixo dos vasos; o excesso de água se acumula no pratinho em vez de inundar o fundo da caixa.
Se a ponta da mangueira ficar mais baixa que o nível da água no reservatório, a água continua correndo depois que a bomba para por causa do efeito sifão, e o vaso transborda. Mantenha o reservatório mais baixo que os vasos, ou faça um furinho de ar com uma agulha no ponto mais alto da mangueira; quando a bomba para, o ar entra pelo furo e o fluxo para na hora.

Lista de peças e custo

PeçaQtd.FunçãoFaixa de preço
Arduino Uno R3 ou ESP32 DevKit1O cérebro da estufaMédia
Módulo de temperatura e umidade DHT22 (AM2302)1Temperatura e umidade do arBaixa
Sensor capacitivo de umidade do solo (v1.2)1 (um por vaso)Umidade do soloBaixa
LDR (GL5528) e resistor de 10 kΩ1 + 1Luz ambienteMuito baixa
Chave de nível de água tipo boia1Bloqueia a bomba quando o reservatório está vazioBaixa
Módulo relé de 4 canais 5 V (com optoacoplador e JD-VCC)1Ventoinha, aquecedor e bombaBaixa
MOSFET canal N de nível lógico (IRLZ44N), resistores de 220 Ω e 100 kΩ1 conjuntoBrilho da fita de LED (PWM)Muito baixa
Minibomba submersível de 3–6 V e 1 m de mangueira de silicone1IrrigaçãoBaixa
Ventoinha de computador de 12 V e 80 mm1–2Ventilação e retirada de umidadeBaixa
Aquecedor PTC de 12 V (10–30 W) e fusível térmico1 + 1AquecimentoBaixa
Fita de LED de 12 V (luz de cultivo ou branco frio), cerca de 1 m1Iluminação complementarBaixa
LCD I2C 16x21Exibição do estadoBaixa – média
Adaptador de 12 V 5 A e conversor buck LM25961 + 1AlimentaçãoMédia
Diodo 1N5819 ou 1N4007, capacitor de 470 µF, bornes de parafuso, jumpers–Proteção e ligaçõesMuito baixa
Caixa organizadora transparente com tampa ou chapa de acrílico, vasos, pratinhos–Corpo da estufaMédia

Os preços variam muito entre vendedores, então aqui vai um orçamento aproximado em vez de valores exatos. Sem contar a placa, a versão básica (DHT22, sensor de umidade do solo, LDR, relé de 2 canais, bomba e uma ventoinha de 5 V) costuma custar uns 25–40 dólares. A versão completa, com aquecedor, fita de LED, LCD, adaptador de 12 V, LM2596, chave de boia e gabinete, costuma chegar a uns 70–120 dólares; a maior parte do orçamento vai para a placa, o adaptador, o display e o gabinete. Se você tem um kit Arduino para iniciantes, a placa, a protoboard, os fios, o LDR, os resistores e muitas vezes o LCD já estão resolvidos. Com o orçamento apertado, deixe o aquecedor e os LEDs para uma segunda etapa; irrigação e ventilação sozinhas já formam um projeto significativo e funcional.

Sensores: DHT22, sensor de umidade do solo e LDR

Para uma estufa, recomendamos o DHT22 (AM2302). Ele custa um pouco mais que o DHT11, mas mede temperatura de −40 a 80 °C com ±0,5 °C e umidade de 0 a 100% com cerca de ±2%; o DHT11 só é preciso a ±2 °C entre 0 e 50 °C e não enxerga umidade acima de 90%. Como uma estufa pode passar de 90% de umidade de manhã e ficar abaixo de zero no inverno, o DHT22 serve muito melhor. O sensor faz no máximo uma leitura a cada 2 segundos, então o código o lê a cada 2,5 segundos. Se você puser o sensor onde o sol ou os LEDs batem direto, o corpo dele esquenta e ele marca alguns graus acima da temperatura do ar; sombreá-lo com uma cobertura branca furada evita isso. Os detalhes de ligação e de biblioteca estão no artigo sobre o sensor de temperatura e umidade DHT11 e DHT22.

Módulo sensor de umidade do solo
Sensor de umidade do solo: a sonda enterrada na zona das raízes informa quão úmido está o solo por uma tensão analógica; escolha o modelo capacitivo para uma vida longa na estufa.

Para a umidade do solo, escolha um sensor capacitivo. Os sensores resistivos baratos (YL-69, FC-28) têm duas hastes metálicas em contato direto com o solo e, se ficarem alimentados o tempo todo, corroem em poucas semanas por causa da eletrólise; não servem para um sistema que vai funcionar por meses. O eletrodo de um sensor capacitivo é revestido, então nenhum metal toca o solo. A saída dele é analógica e cai à medida que o solo fica mais úmido: em um Uno, um sensor no ar marca uns 500–600, e mergulhado na água uns 250–300. Esses números mudam de sensor para sensor, então, antes de carregar o código principal, leia no Monitor Serial os valores do seu sensor no ar e na água e escreva-os nas constantes SOIL_DRY_RAW e SOIL_WET_RAW. O código converte esses dois valores para 0–100% com map(). Mais detalhes no artigo sobre o sensor de umidade do solo.

Para a luz, o LDR (GL5528) do seu kit basta. Ligue o LDR como divisor de tensão com um resistor de 10 kΩ: 5V → LDR → A1 → 10 kΩ → GND. Quando a luz aumenta, a resistência do LDR cai e o valor em A1 sobe; o código também converte esse valor para 0–100%, com base em dois números que você mede no escuro e no sol. Um LDR não é um luxímetro preciso, mas responde com segurança “escureceu, está nublado, está ensolarado?”. Se quiser leituras de luz mais precisas, você pode passar para um sensor de luz I2C como o BH1750. Use a ferramenta Divisor de tensão para as contas do divisor e o artigo sobre o sensor de luz LDR para os detalhes.

Se o LDR enxergar a luz dos LEDs, o sistema cai em um ciclo em que liga e desliga sozinho: escurece, os LEDs acendem, o LDR acha que está claro e os apaga, e então vê escuro de novo. Para evitar isso, coloque o LDR fora da estufa, longe dos LEDs, e use histerese no código em vez de um limite único. A eletrônica em cima de um sensor capacitivo de solo também não é à prova d'água; não o enterre acima da linha marcada nele e vede as bordas da placa com cola quente ou esmalte transparente.

Atuadores: relé ou MOSFET?

Um pino do Arduino só fornece cerca de 20 mA, enquanto a ventoinha, o aquecedor, a bomba e a fita de LED consomem centenas de miliamperes ou até vários amperes. Por isso cada carga é acionada por um relé ou por um MOSFET. Um relé é uma chave mecânica: ele liga e desliga uma carga em um circuito totalmente separado, CA ou CC, mas só consegue ligar ou desligar, estala toda vez e os contatos têm vida limitada. Um MOSFET é uma chave eletrônica silenciosa; ele só funciona com cargas CC, mas, como consegue chavear centenas de vezes por segundo, ajusta brilho e velocidade com PWM. Neste projeto usamos um módulo relé de 4 canais para o aquecedor, a ventoinha e a bomba, e um MOSFET para o brilho da fita de LED.

CargaValor típicoDriverPor quê?
Aquecedor PTC de 12 V10–30 W, cerca de 1–2,5 ARelé (IN2)Corrente alta, só precisa de liga/desliga
Ventoinha de 12 V e 80 mm0,1–0,2 ARelé (IN1) ou MOSFETO relé é simples; o MOSFET também permite controlar a velocidade por PWM
Minibomba submersível de 3–6 V0,1–0,25 A, mais na partidaRelé (IN3) ou MOSFET com diodoFunciona em pulsos curtos; o ruído do motor precisa ser suprimido
Fita de LED de 12 V (5050, 60 LEDs/m)Cerca de 1,2 A por metroMOSFET de nível lógicoControle de brilho por PWM e funcionamento silencioso
Minibomba de água submersível
Minibomba submersível de 3–6 V: ela fica no reservatório de água, consome cerca de 100–200 mA e empurra a água por uma mangueira até o vaso; nunca a deixe funcionar a seco.

Dependendo da altura da mangueira, uma minibomba submersível entrega cerca de 1–1,5 litro por minuto, então um pulso de 3 segundos dá ao vaso 50–75 mL de água. É por isso que não ligamos a bomba sem parar “até o solo ficar úmido”, e sim em pulsos curtos: irrigar por 3 segundos, esperar 60 segundos para a água penetrar e medir de novo. Se o sensor fosse lido de novo antes de a água chegar até ele, o solo continuaria parecendo seco e a bomba funcionaria até o vaso transbordar. Uma bomba funcionando a seco esquenta e queima em minutos; a chave de boia no reservatório bloqueia a bomba quando a água acaba. Toda vez que o motor para, a bobina gera um pico de tensão reversa; um diodo polarizado reversamente nos terminais da bomba (1N5819 ou 1N4007, com a faixa no lado +) protege os contatos do relé e o Arduino desse ruído. A escolha da bomba e os métodos de acionamento estão no artigo sobre a minibomba de água.

Use um aquecedor PTC de 12 V para o aquecimento. A resistência dos elementos cerâmicos PTC (coeficiente de temperatura positivo) aumenta à medida que esquentam, então a temperatura da superfície se limita sozinha a um valor definido; isso é muito mais seguro do que um fio de resistência nu incandescente. Em um gabinete de plástico, escolha um modelo de baixa temperatura, com a superfície limitada a uns 80 °C. Um aquecedor de 24 W consome 2 A em 12 V, segundo P = V × I; nos primeiros segundos, enquanto está frio, a corrente é um pouco maior. Para a iluminação, você pode usar uma fita de LED de cultivo vermelho-azul ou uma fita de LED branco frio; as plantas usam principalmente luz vermelha (por volta de 660 nm) e azul (por volta de 450 nm) para a fotossíntese. Acione a fita com um MOSFET de nível lógico como o IRLZ44N: o gate liga ao D9 por um resistor de 220 Ω e ao GND por um resistor de 100 kΩ; o + da fita vai aos 12 V, o − ao dreno do MOSFET, e a fonte (source) ao GND. Módulos MOSFET comuns como o IRF520 não conduzem totalmente com 5 V no gate e esquentam com cargas de vários amperes; os detalhes estão no artigo sobre como usar um MOSFET com Arduino. Você pode usar a ferramenta Lei de Ohm para calcular corrente e potência.

O aquecedor é o único risco real de incêndio da estufa. Monte-o em uma base metálica onde ele não encoste nas paredes de plástico, acrescente um fusível térmico em série com o aquecedor na linha de 12 V, encostado na chapa em que ele está montado, e teste o sistema por horas antes de deixá-lo sem supervisão. O fusível deve abrir um pouco acima da temperatura-limite do aquecedor (por exemplo, 92 °C para um PTC de 80 °C); um valor menor vai queimar à toa durante o funcionamento normal. Tapetes térmicos para mudas e aquecedores maiores costumam funcionar na tensão da rede, e a rede elétrica mata. Não use a rede em uma maquete de estufa; um aquecedor PTC de 12 V basta. Se você realmente precisar de um aparelho ligado à rede, um eletricista ou um adulto capacitado deve fazer as ligações, e a placa de relés deve ficar em uma caixa fechada e isolada. As regras de ligação segura estão no artigo O que é um módulo relé?.

Plano de alimentação: linhas de 12 V e 5 V

Os problemas mais comuns das estufas inteligentes (“o Arduino reinicia quando a bomba liga”, “aparecem caracteres estranhos no LCD”) vêm da alimentação. A solução mais limpa é tirar duas linhas de um único adaptador de 12 V 5 A. A linha de 12 V alimenta direto o aquecedor, as ventoinhas e a fita de LED, e também vai ao conector DC do Arduino Uno. Os mesmos 12 V também alimentam um conversor buck LM2596 ajustado para 5 V; as bobinas dos relés, a bomba e o LCD funcionam nessa linha de 5 V. Se você tirar o jumper JD-VCC do módulo relé, a corrente das bobinas vem toda do LM2596 e o lado do Arduino só acende os LEDs dos optoacopladores. Alimente os sensores (DHT22, sensor de umidade do solo e divisor do LDR) pelo próprio pino 5V do Arduino: analogRead() mede em relação aos 5 V da própria placa, então alimentar os sensores analógicos com a mesma tensão mantém as leituras estáveis. Ligue sempre juntos os GNDs do adaptador, do LM2596 e do Arduino.

CargaLinhaCorrente aprox.
Aquecedor PTC (24 W)12 V2 A (mais no primeiro instante, com ele frio)
Fita de LED (1 m)12 V1–1,2 A
Duas ventoinhas de 80 mm12 V0,2–0,4 A
Arduino Uno e sensores (pelo conector DC)12 VCerca de 0,06–0,08 A
Entrada do LM2596 (com o lado de 5 V puxando 1 A)12 VCerca de 0,5 A
Módulo relé (três relés energizados)5 V0,2–0,25 A
Minibomba submersível5 V0,1–0,25 A (mais na partida)
LCD I2C (luz de fundo acesa)5 V20–30 mA

Mesmo que tudo funcione ao mesmo tempo, a corrente total fica em torno de 4–4,5 A, então um adaptador de 12 V 5 A (60 W) deixa folga suficiente. Na prática, o aquecedor e a ventoinha de temperatura nunca funcionam juntos, porque há uma diferença grande entre os limites deles. Antes de ligar o LM2596 ao circuito, meça a saída com um multímetro e ajuste para 5,0 V com o trimpot da placa; de fábrica, ele pode deixar passar quase os 12 V inteiros. Você pode alimentar o Arduino pelo USB enquanto carrega o código e pelo conector DC no uso; ter os dois ligados ao mesmo tempo não é problema para o Uno. Os detalhes estão no guia de alimentação do Arduino.

Ligações e tabela de pinos

A tabela abaixo mostra os pinos usados pelos três programas deste guia. No Uno, ligamos o MOSFET que aciona a fita de LED ao D9, que suporta PWM; no Uno, o PWM só existe em D3, D5, D6, D9, D10 e D11. A chave de boia e os relés não precisam de PWM. Como o LCD usa só o barramento I2C (A4 e A5), ele ocupa pouquíssimos pinos; no Uno, A2, A3 e alguns pinos digitais continuam livres para um segundo sensor de solo ou um botão. No ESP32, ligamos os sensores analógicos a pinos do ADC1 (GPIO 34 e 35), que continuam funcionando com o Wi-Fi ligado. Você pode ver a disposição dos pinos da sua placa na ferramenta Pinagem.

LigaçãoArduino UnoESP32Raspberry Pi Pico
DATA do DHT22D2GPIO 4GP15
Chave de boia (outro terminal ao GND)D3GPIO 13GP14
Relé IN3: bombaD4GPIO 25GP18
Relé IN1: ventoinhaD7GPIO 26GP16
Relé IN2: aquecedorD8GPIO 27GP17
Gate do MOSFET (por 220 Ω): LEDD9GPIO 18GP19
AOUT do sensor de umidade do soloA0GPIO 34GP26
Ponto médio do divisor do LDRA1GPIO 35 (divisor a partir do 3V3)GP27 (divisor a partir do 3V3)
SDA / SCL do LCD I2CA4 / A5GPIO 21 / 22GP4 / GP5
Alimentação dos sensores5V3V33V3
Os pinos do ESP32 e do Pico funcionam em 3,3 V e não toleram 5 V. Nessas placas, alimente o DHT22, o sensor de umidade do solo e o divisor do LDR pelo pino 3V3, para que não chegue mais de 3,3 V ao pino analógico. Um módulo relé de 5 V ativo em LOW às vezes não solta totalmente com um sinal HIGH de 3,3 V; se um relé ficar energizado, alimente o VCC com 3,3 V e o JD-VCC com 5 V em um módulo com JD-VCC, ou compre um módulo com acionamento em 3,3 V. Para a fita de LED, escolha um MOSFET que conduza totalmente com 3,3 V no gate (por exemplo, o IRLB8721).

Limites e histerese: por que os relés ficam estalando?

Imagine controlar a ventoinha com um único limite: “ligue se a temperatura passar de 30 °C, senão desligue”. Enquanto a temperatura oscila entre 29,9 e 30,1 °C (o que acontece muito por causa do ruído do sensor), o relé liga e desliga a cada leitura. Os contatos do relé e o motor da ventoinha se desgastam, e com o aquecedor a fiação e o adaptador levam um tranco desnecessário a cada vez que ele liga. A solução é a histerese: você usa dois limites separados para ligar e desligar. A ventoinha liga a 30 °C, mas só desliga quando a temperatura cai para 27 °C. Na zona morta de 3 graus entre eles, o sistema mantém o último estado. Os termostatos ambiente de caldeiras e as geladeiras funcionam pelo mesmo princípio.

ControleLigaDesligaZona morta
Ventoinha (temperatura)≥ 30 °C≤ 27 °C3 °C
Ventoinha (umidade)≥ 85%≤ 75%10%
Aquecedor≤ 16 °C≥ 19 °C3 °C
IrrigaçãoSolo ≤ 35%Solo ≥ 50%15%
Iluminação de LEDLuz ≤ 25%Luz ≥ 35% ou depois de 4 horas10%

A largura da zona morta é um meio-termo: estreita demais e o relé continua chaveando com frequência; larga demais e a temperatura se afasta muito do alvo. Uns 2–3 °C para temperatura, 8–10% para umidade e 10–15% para umidade do solo são um bom começo. Também deixamos uma folga grande entre os 19 °C em que o aquecedor desliga e os 30 °C em que a ventoinha liga, para que o aquecedor e a ventoinha de temperatura nunca trabalhem um contra o outro. No código, a histerese fica em uma pequena função hysteresis(), para você não ter de reescrevê-la em cada controle: se o limite de ligar for maior que o de desligar, significa “ligar quando sobe” (ventoinha); se for menor, “ligar quando desce” (aquecedor, irrigação, LEDs). Se quiser também suavizar picos nos dados dos sensores, dê uma olhada no artigo sobre filtragem de dados de sensores.

Estrutura do código: medir, decidir, agir, mostrar

A regra mais importante do código da estufa é que nenhuma tarefa espera pela outra. Se você esperasse um minuto inteiro com delay(60000) depois da bomba, a ventoinha não funcionaria e o display não se atualizaria nesse tempo. Por isso o código é feito de pequenas funções que medem o tempo com millis(), cada uma responsável por um único trabalho; o loop() as chama em sequência milhares de vezes por segundo, e cada função só faz algo quando chega a hora dela. A irrigação é uma pequena máquina de estados de três estados: esperar (IDLE), irrigar (PUMPING) e deixar a água penetrar (SOAKING). Todas as configurações ficam como constantes no topo do código, então você não precisa vasculhar o código para mudar um limite. O raciocínio por trás dessa abordagem está nos artigos sobre millis() e delay() e sobre máquinas de estados no Arduino. O código também tem estas medidas de segurança:

  • Partida segura: o valor de “desligado” é escrito nos pinos dos relés antes de chamar pinMode(), então os relés não estalam nem por um instante enquanto o Arduino inicia.
  • Falha do sensor: se o DHT22 não puder ser lido três vezes seguidas, o código passa para um estado seguro: desliga o aquecedor, liga a ventoinha e mostra “Sensor hatasi” (erro no sensor) no LCD. Um sistema que não sabe a temperatura nunca pode deixar o aquecedor ligado.
  • Proteção do reservatório: quando a chave de boia indica que o reservatório está vazio, a bomba nem liga e, se estiver funcionando, para na hora.
  • Proteção contra transbordamento: se a umidade do solo não chegar a 50% depois de seis pulsos em um ciclo de irrigação, o sensor pode ter saído do solo ou a mangueira pode ter se soltado; o código para a irrigação e mostra “Sulama hatasi” (falha na irrigação). O aviso some sozinho quando o solo volta a ficar úmido.
  • Registro de dados: a cada 10 segundos, o código escreve as leituras no Monitor Serial como uma linha separada por vírgulas (CSV); você pode copiar essas linhas e fazer gráficos no Excel.

Montagem e teste passo a passo

  1. Prepare o gabinete: recorte os furos da ventoinha, da entrada de ar, da mangueira e dos cabos, e ponha a caixa da eletrônica fora da estufa.
  2. Monte as linhas de alimentação: ligue o adaptador de 12 V a um borne de parafuso, ajuste o LM2596 para 5,0 V com um multímetro e una todos os GNDs.
  3. Ligue o LCD I2C e escreva um texto nele. Se o display não mostrar nada, experimente o sketch scanner e o trimpot de contraste do artigo sobre o LCD I2C.
  4. Acrescente os sensores um por vez e teste no Monitor Serial: anote as leituras do DHT22, o valor bruto do sensor de solo no ar e na água e o valor do LDR no escuro e no sol.
  5. Teste primeiro os relés sem carga, observando os LEDs deles e ouvindo o estalo. Depois ligue a ventoinha, a bomba (com o reservatório cheio) e, por último, o aquecedor.
  6. Ligue a fita de LED pelo MOSFET e mude o valor de analogWrite() para ver o brilho mudar.
  7. Carregue o código principal, ponha os seus valores de calibração nas constantes e deixe o sistema funcionando por algumas horas sob supervisão: alguma irrigação errada, um relé que fica estalando, uma peça que esquenta demais?
Não tente ligar tudo de uma vez e fazer tudo funcionar de primeira. Quando seis módulos não funcionam juntos, achar a falha pode levar horas; módulos testados um por vez costumam funcionar de primeira quando juntados.

Código completo para o Arduino Uno

Para compilar o código, instale pelo Gerenciador de Bibliotecas a DHT sensor library da Adafruit (junto com a dependência dela, Adafruit Unified Sensor) e a biblioteca LiquidCrystal I2C de Frank de Brabander; a Wire já vem com o Arduino IDE. O endereço do LCD costuma ser 0x27, e 0x3F em alguns módulos. Antes de carregar, escreva os valores de calibração que você mediu nas constantes SOIL_DRY_RAW, SOIL_WET_RAW, LDR_DARK_RAW e LDR_BRIGHT_RAW. Se a sua chave de boia abre em vez de fechar com a água, ponha TANK_OK em HIGH; se o seu módulo relé é ativo em HIGH, troque os valores de RELAY_ON e RELAY_OFF.

C++ (Arduino IDE)
1#include <Wire.h>
2#include <LiquidCrystal_I2C.h>
3#include <DHT.h>
4
5// Pinler / Pins
6const byte DHT_PIN = 2, FLOAT_PIN = 3, PUMP_RELAY = 4, FAN_RELAY = 7, HEATER_RELAY = 8;
7const byte LED_PIN = 9;                          // MOSFET gate (PWM)
8const byte SOIL_PIN = A0, LDR_PIN = A1;
9
10// Kalibrasyon: kendi ölçümlerini yaz / Calibration: write your own readings
11const int SOIL_DRY_RAW = 520, SOIL_WET_RAW = 260;   // havada ve suda / in air and in water
12const int LDR_DARK_RAW = 60, LDR_BRIGHT_RAW = 900;  // karanlık ve güneş / dark and sunlight
13
14// Eşikler (açma, kapama) / Thresholds (on, off)
15const float FAN_ON_C = 30, FAN_OFF_C = 27;       // sıcaklıkta fan / fan on heat
16const float HUM_ON = 85, HUM_OFF = 75;           // nemde fan / fan on humidity
17const float HEAT_ON_C = 16, HEAT_OFF_C = 19;     // ısıtıcı / heater
18const float SOIL_DRY = 35, SOIL_OK = 50;         // sulama başla, dur / start, stop watering
19const float DARK_ON = 25, DARK_OFF = 35;         // ışık % / light %
20
21// Zamanlar / Timing
22const unsigned long READ_MS = 2500;              // DHT22: en az 2 sn / at least 2 s
23const unsigned long PUMP_MS = 3000;              // tek sulama darbesi / one watering pulse
24const unsigned long SOAK_MS = 60000;             // suyun yayılması / let the water soak in
25const byte MAX_PULSES = 6;                       // fazlası arıza / more means a fault
26const unsigned long EXTRA_LIGHT_MS = 4UL * 3600000UL;  // günü 4 saat uzat / extend the day by 4 h
27const byte LED_LEVEL = 200;                      // LED parlaklığı 0-255 / LED brightness 0-255
28const byte RELAY_ON = LOW, RELAY_OFF = HIGH;     // aktif LOW röle / active-LOW relay
29const byte TANK_OK = LOW;                        // şamandıra suyla kapanır / float closes with water
30
31DHT dht(DHT_PIN, DHT22);
32LiquidCrystal_I2C lcd(0x27, 16, 2);
33
34float tempC = NAN, hum = NAN, soil = 0, light = 0;
35byte dhtErrors = 0, pulses = 0, page = 0;
36bool fanByTemp = false, fanByHum = false, fanOn = false, heaterOn = false;
37bool needWater = false, pumpFault = false, isDark = false, ledOn = false, tankOk = true;
38unsigned long lastRead = 0, lastLcd = 0, lastLog = 0, pumpAt = 0, darkSince = 0;
39
40enum PumpState { IDLE, PUMPING, SOAKING };
41PumpState pumpState = IDLE;
42
43// onAt > offAt: yükselince aç (fan) / turn on when rising (fan)
44// onAt < offAt: düşünce aç (ısıtıcı) / turn on when falling (heater)
45bool hysteresis(bool state, float value, float onAt, float offAt) {
46  if (onAt > offAt) {
47    if (value >= onAt) return true;
48    if (value <= offAt) return false;
49  } else {
50    if (value <= onAt) return true;
51    if (value >= offAt) return false;
52  }
53  return state;                                  // ölü bölgede durumu koru / keep state in the dead band
54}
55
56void setRelay(byte pin, bool on) {
57  digitalWrite(pin, on ? RELAY_ON : RELAY_OFF);
58}
59
60float readPercent(byte pin, int raw0, int raw100) {
61  long sum = 0;
62  for (byte i = 0; i < 10; i++) sum += analogRead(pin);   // 10 ölçüm ortalaması / average of 10
63  long pct = map(sum / 10, raw0, raw100, 0, 100);
64  return constrain(pct, 0, 100);
65}
66
67void setup() {
68  Serial.begin(9600);
69  setRelay(PUMP_RELAY, false);   pinMode(PUMP_RELAY, OUTPUT);    // açılışta kapalı / off at boot
70  setRelay(FAN_RELAY, false);    pinMode(FAN_RELAY, OUTPUT);
71  setRelay(HEATER_RELAY, false); pinMode(HEATER_RELAY, OUTPUT);
72  pinMode(LED_PIN, OUTPUT);
73  pinMode(FLOAT_PIN, INPUT_PULLUP);
74  dht.begin();
75  lcd.init();
76  lcd.backlight();
77  lcd.print(F("BlueGrays Sera"));
78  Serial.println(F("sn,sicaklik,nem,toprak,isik,fan,isitici,pompa,led"));  // CSV başlığı / CSV header
79  delay(2000);                                   // DHT22 açılış süresi / DHT22 start-up time
80  readSensors();
81  lastRead = millis();
82}
83
84void loop() {
85  unsigned long now = millis();
86  if (now - lastRead >= READ_MS) {
87    lastRead = now;
88    readSensors();
89  }
90  controlClimate();
91  controlLight(now);
92  controlPump(now);
93  updateLcd(now);
94  logCsv(now);
95}
96
97// 1) Ölç / Measure
98void readSensors() {
99  float t = dht.readTemperature();
100  float h = dht.readHumidity();
101  if (isnan(t) || isnan(h)) {
102    if (dhtErrors < 255) dhtErrors++;            // okuma hatası / read error
103  } else {
104    tempC = t;
105    hum = h;
106    dhtErrors = 0;
107  }
108  soil = readPercent(SOIL_PIN, SOIL_DRY_RAW, SOIL_WET_RAW);
109  light = readPercent(LDR_PIN, LDR_DARK_RAW, LDR_BRIGHT_RAW);
110  tankOk = digitalRead(FLOAT_PIN) == TANK_OK;
111}
112
113// 2) Fan ve ısıtıcı / Fan and heater
114void controlClimate() {
115  if (dhtErrors >= 3) {                          // sensör yok: güvenli durum / no sensor: safe state
116    heaterOn = false;
117    fanByTemp = true;
118  } else {
119    fanByTemp = hysteresis(fanByTemp, tempC, FAN_ON_C, FAN_OFF_C);
120    fanByHum = hysteresis(fanByHum, hum, HUM_ON, HUM_OFF);
121    heaterOn = hysteresis(heaterOn, tempC, HEAT_ON_C, HEAT_OFF_C);
122  }
123  fanOn = fanByTemp || fanByHum;
124  setRelay(FAN_RELAY, fanOn);
125  setRelay(HEATER_RELAY, heaterOn);
126}
127
128// 3) LED aydınlatma / LED grow light
129void controlLight(unsigned long now) {
130  bool wasDark = isDark;
131  isDark = hysteresis(isDark, light, DARK_ON, DARK_OFF);
132  if (isDark && !wasDark) darkSince = now;      // akşam oldu / dusk detected
133  ledOn = isDark && (now - darkSince < EXTRA_LIGHT_MS);
134  analogWrite(LED_PIN, ledOn ? LED_LEVEL : 0);
135}
136
137// 4) Sulama: darbe + bekleme / Watering: pulse + soak
138void controlPump(unsigned long now) {
139  needWater = hysteresis(needWater, soil, SOIL_DRY, SOIL_OK);
140  if (!needWater) {
141    pulses = 0;
142    pumpFault = false;                           // toprak ıslandı / soil is wet again
143  }
144  switch (pumpState) {
145    case IDLE:
146      if (needWater && tankOk && !pumpFault) {
147        if (pulses >= MAX_PULSES) {
148          pumpFault = true;                      // nem yükselmiyor / moisture is not rising
149        } else {
150          pumpState = PUMPING;
151          pumpAt = now;
152          pulses++;
153        }
154      }
155      break;
156    case PUMPING:
157      if (now - pumpAt >= PUMP_MS || !tankOk) {
158        pumpState = SOAKING;
159        pumpAt = now;
160      }
161      break;
162    case SOAKING:
163      if (now - pumpAt >= SOAK_MS) pumpState = IDLE;
164      break;
165  }
166  setRelay(PUMP_RELAY, pumpState == PUMPING);
167}
168
169// 5) LCD: üç sayfa / three pages
170void updateLcd(unsigned long now) {
171  if (now - lastLcd < 3000) return;
172  lastLcd = now;
173  page = (page + 1) % 3;
174  lcd.clear();
175  if (page == 0) {
176    lcd.print(F("Sicaklik: "));
177    if (isnan(tempC)) lcd.print(F("--")); else lcd.print(tempC, 1);
178    lcd.print('C');
179    lcd.setCursor(0, 1);
180    lcd.print(F("Hava nemi: %"));
181    if (isnan(hum)) lcd.print(F("--")); else lcd.print(hum, 0);
182  } else if (page == 1) {
183    lcd.print(F("Toprak: %"));
184    lcd.print(soil, 0);
185    lcd.setCursor(0, 1);
186    lcd.print(F("Isik: %"));
187    lcd.print(light, 0);
188  } else {
189    lcd.print(F("F:"));  lcd.print(fanOn);
190    lcd.print(F(" I:")); lcd.print(heaterOn);
191    lcd.print(F(" P:")); lcd.print(pumpState == PUMPING);
192    lcd.print(F(" L:")); lcd.print(ledOn);
193    lcd.setCursor(0, 1);
194    if (dhtErrors >= 3) lcd.print(F("Sensor hatasi"));
195    else if (!tankOk) lcd.print(F("Depo bos!"));
196    else if (pumpFault) lcd.print(F("Sulama hatasi"));
197    else lcd.print(F("Sistem normal"));
198  }
199}
200
201// 6) Seri port CSV kaydı / Serial CSV log
202void logCsv(unsigned long now) {
203  if (now - lastLog < 10000) return;
204  lastLog = now;
205  Serial.print(now / 1000);  Serial.print(',');
206  Serial.print(tempC, 1);    Serial.print(',');
207  Serial.print(hum, 0);      Serial.print(',');
208  Serial.print(soil, 0);     Serial.print(',');
209  Serial.print(light, 0);    Serial.print(',');
210  Serial.print(fanOn);       Serial.print(',');
211  Serial.print(heaterOn);    Serial.print(',');
212  Serial.print(pumpState == PUMPING); Serial.print(',');
213  Serial.println(ledOn);
214}
FunçãoTrabalhoCom que frequência?
readSensors() (leitura dos sensores)Lê o DHT22, a umidade do solo, o LDR e a chave de boia; converte os valores analógicos para 0–100% usando a média de 10 leiturasA cada 2,5 segundos
controlClimate() (controle do clima)Aciona a ventoinha com histerese de temperatura ou de umidade e o aquecedor com histerese de temperatura; passa para um estado seguro se o sensor falharA cada volta do loop
controlLight() (controle da luz)Acende os LEDs com PWM quando escurece e os apaga depois de no máximo 4 horasA cada volta do loop
controlPump() (controle da bomba)Máquina de estados da irrigação: irrigar 3 s, esperar 60 s, medir de novo; com proteção do reservatório e contra transbordamentoA cada volta do loop
updateLcd() (atualização do LCD)Alterna entre três páginas: leituras do ar, umidade do solo e luz, estado dos dispositivosA cada 3 segundos
logCsv() (registro em CSV)Escreve leituras e estados no Monitor Serial como uma linha CSVA cada 10 segundos

A primeira página do LCD mostra a temperatura e a umidade do ar, a segunda a umidade do solo e o nível de luz, e a terceira o estado dos dispositivos: “F:1 I:0 P:0 L:1” significa que a ventoinha está ligada, o aquecedor (I = ısıtıcı, aquecedor em turco) e a bomba estão desligados e os LEDs estão acesos. A linha de baixo mostra uma das mensagens “Sistem normal” (tudo certo), “Depo bos!” (reservatório vazio), “Sulama hatasi” (falha na irrigação) ou “Sensor hatasi” (erro no sensor); você pode traduzir facilmente esses textos no código. Compilado, este código usa cerca de 31% da memória de programa do Uno e 25% da RAM, então sobra bastante espaço para acrescentar um segundo sensor de solo, um buzzer ou um módulo de relógio RTC. Como o Uno não tem relógio de verdade, a regra dos LEDs funciona como “no máximo 4 horas depois do anoitecer”, o que é um bom jeito de prolongar os dias curtos de inverno. Se quiser que os LEDs acendam em horários definidos todo dia, acrescente um módulo RTC ou passe para a versão com ESP32 abaixo.

Acompanhando e irrigando pelo celular com um ESP32

Placa de desenvolvimento ESP32
ESP32: o Wi-Fi permite acompanhar a estufa pelo celular, e a hora que ele pega da internet permite controlar os LEDs pelo relógio real.

O Uno não tem Wi-Fi. Se quiser ver o estado da estufa no celular e irrigar a distância, troque o cérebro por um ESP32. O código abaixo leva a mesma lógica de controle para o ESP32 e inicia um pequeno servidor web. Com o celular na mesma rede Wi-Fi, abra no navegador o endereço IP mostrado no Monitor Serial e você verá a temperatura, a umidade, a umidade do solo, a luz, o estado do reservatório e se cada dispositivo está ligado ou desligado; a página se atualiza sozinha a cada 10 segundos. O botão Water now (irrigar agora) liga a bomba uma vez por 3 segundos se houver água no reservatório. Ao iniciar, o ESP32 pega a hora na internet (NTP), então os LEDs só acendem entre 06:00 e 20:00 e só quando está escuro lá fora, e as plantas descansam à noite.

C++ (Arduino IDE)
1#include <WiFi.h>
2#include <WebServer.h>
3#include <DHT.h>
4#include <time.h>
5
6const char* ssid = "AgAdi";                      // Wi-Fi adın / your Wi-Fi name
7const char* password = "Sifre123";               // Wi-Fi şifren / your Wi-Fi password
8
9// Pinler / Pins
10const int DHT_PIN = 4, FLOAT_PIN = 13, PUMP_RELAY = 25, FAN_RELAY = 26, HEATER_RELAY = 27;
11const int LED_PIN = 18;                          // MOSFET gate (PWM)
12const int SOIL_PIN = 34, LDR_PIN = 35;           // ADC1: Wi-Fi açıkken çalışır / works with Wi-Fi on
13
14// Kalibrasyon (0-4095): kendin ölç / Calibration (0-4095): measure your own
15const int SOIL_DRY_RAW = 2800, SOIL_WET_RAW = 1200;
16const int LDR_DARK_RAW = 200, LDR_BRIGHT_RAW = 3800;
17
18// Eşikler (açma, kapama) / Thresholds (on, off)
19const float FAN_ON_C = 30, FAN_OFF_C = 27, HUM_ON = 85, HUM_OFF = 75;
20const float HEAT_ON_C = 16, HEAT_OFF_C = 19, SOIL_DRY = 35, SOIL_OK = 50;
21const float DARK_ON = 25, DARK_OFF = 35;
22const int LIGHT_FROM = 6, LIGHT_TO = 20;         // LED yalnız 06:00-20:00 / LEDs only 06:00-20:00
23
24const unsigned long READ_MS = 2500, PUMP_MS = 3000, SOAK_MS = 60000;
25const unsigned long EXTRA_LIGHT_MS = 4UL * 3600000UL;  // saat yoksa yedek kural / fallback without a clock
26const int MAX_PULSES = 6, LED_LEVEL = 200;
27const int RELAY_ON = LOW, RELAY_OFF = HIGH;      // aktif LOW röle / active-LOW relay
28const int TANK_OK = LOW;                         // şamandıra suyla kapanır / float closes with water
29
30DHT dht(DHT_PIN, DHT22);
31WebServer server(80);
32
33float tempC = NAN, hum = NAN, soil = 0, light = 0;
34int dhtErrors = 0, pulses = 0;
35bool fanByTemp = false, fanByHum = false, fanOn = false, heaterOn = false;
36bool needWater = false, pumpFault = false, isDark = false, ledOn = false, tankOk = true;
37unsigned long lastRead = 0, pumpAt = 0, darkSince = 0;
38
39enum PumpState { IDLE, PUMPING, SOAKING };
40PumpState pumpState = IDLE;
41
42bool hysteresis(bool state, float value, float onAt, float offAt) {
43  if (onAt > offAt) {                            // yükselince aç / turn on when rising
44    if (value >= onAt) return true;
45    if (value <= offAt) return false;
46  } else {                                       // düşünce aç / turn on when falling
47    if (value <= onAt) return true;
48    if (value >= offAt) return false;
49  }
50  return state;
51}
52
53void setRelay(int pin, bool on) {
54  digitalWrite(pin, on ? RELAY_ON : RELAY_OFF);
55}
56
57float readPercent(int pin, int raw0, int raw100) {
58  long sum = 0;
59  for (int i = 0; i < 16; i++) sum += analogRead(pin);    // 16 ölçüm ortalaması / average of 16
60  long pct = map(sum / 16, raw0, raw100, 0, 100);
61  return constrain(pct, 0, 100);
62}
63
64int hourNow() {
65  struct tm t;
66  if (!getLocalTime(&t, 0)) return -1;           // saat henüz yok / time not set yet
67  return t.tm_hour;
68}
69
70void readSensors() {
71  float t = dht.readTemperature();
72  float h = dht.readHumidity();
73  if (isnan(t) || isnan(h)) {
74    dhtErrors++;
75  } else {
76    tempC = t;
77    hum = h;
78    dhtErrors = 0;
79  }
80  soil = readPercent(SOIL_PIN, SOIL_DRY_RAW, SOIL_WET_RAW);
81  light = readPercent(LDR_PIN, LDR_DARK_RAW, LDR_BRIGHT_RAW);
82  tankOk = digitalRead(FLOAT_PIN) == TANK_OK;
83}
84
85void controlClimate() {
86  if (dhtErrors >= 3) {                          // sensör yok: güvenli durum / no sensor: safe state
87    heaterOn = false;
88    fanByTemp = true;
89  } else {
90    fanByTemp = hysteresis(fanByTemp, tempC, FAN_ON_C, FAN_OFF_C);
91    fanByHum = hysteresis(fanByHum, hum, HUM_ON, HUM_OFF);
92    heaterOn = hysteresis(heaterOn, tempC, HEAT_ON_C, HEAT_OFF_C);
93  }
94  fanOn = fanByTemp || fanByHum;
95  setRelay(FAN_RELAY, fanOn);
96  setRelay(HEATER_RELAY, heaterOn);
97}
98
99void controlLight(unsigned long now) {
100  bool wasDark = isDark;
101  isDark = hysteresis(isDark, light, DARK_ON, DARK_OFF);
102  if (isDark && !wasDark) darkSince = now;
103  int h = hourNow();
104  if (h >= 0) ledOn = isDark && h >= LIGHT_FROM && h < LIGHT_TO;   // saat kuralı / clock rule
105  else ledOn = isDark && (now - darkSince < EXTRA_LIGHT_MS);       // yedek kural / fallback rule
106  analogWrite(LED_PIN, ledOn ? LED_LEVEL : 0);
107}
108
109void controlPump(unsigned long now) {
110  needWater = hysteresis(needWater, soil, SOIL_DRY, SOIL_OK);
111  if (!needWater) {
112    pulses = 0;
113    pumpFault = false;
114  }
115  if (pumpState == IDLE && needWater && tankOk && !pumpFault) {
116    if (pulses >= MAX_PULSES) {
117      pumpFault = true;                          // nem yükselmiyor / moisture is not rising
118    } else {
119      pumpState = PUMPING;
120      pumpAt = now;
121      pulses++;
122    }
123  } else if (pumpState == PUMPING && (now - pumpAt >= PUMP_MS || !tankOk)) {
124    pumpState = SOAKING;
125    pumpAt = now;
126  } else if (pumpState == SOAKING && now - pumpAt >= SOAK_MS) {
127    pumpState = IDLE;
128  }
129  setRelay(PUMP_RELAY, pumpState == PUMPING);
130}
131
132String row(const char* name, const String& value) {
133  return String("<tr><td>") + name + "</td><td><b>" + value + "</b></td></tr>";
134}
135
136String onOff(bool on) {
137  return on ? "ON" : "OFF";
138}
139
140void handleRoot() {
141  String html = "<!DOCTYPE html><html><head><meta charset='utf-8'>"
142                "<meta name='viewport' content='width=device-width,initial-scale=1'>"
143                "<meta http-equiv='refresh' content='10'><title>BlueGrays Sera</title>"
144                "<style>body{font-family:sans-serif;max-width:420px;margin:auto;padding:12px}"
145                "td{padding:8px;border-bottom:1px solid #ddd}"
146                "a{display:block;margin-top:16px;padding:16px;border-radius:10px;background:#2e7d32;"
147                "color:#fff;text-align:center;text-decoration:none;font-size:18px}</style>"
148                "</head><body><h2>BlueGrays Sera</h2><table>";
149  html += row("Sıcaklık / Temperature", String(tempC, 1) + " °C");
150  html += row("Hava nemi / Humidity", "%" + String(hum, 0));
151  html += row("Toprak nemi / Soil", "%" + String(soil, 0));
152  html += row("Işık / Light", "%" + String(light, 0));
153  html += row("Fan", onOff(fanOn));
154  html += row("Isıtıcı / Heater", onOff(heaterOn));
155  html += row("Pompa / Pump", onOff(pumpState == PUMPING));
156  html += row("LED", onOff(ledOn));
157  html += row("Depo / Tank", tankOk ? "OK" : "BOŞ / EMPTY");
158  if (pumpFault) html += row("Uyarı / Warning", "Sulama hatası / Watering fault");
159  html += "</table><a href='/water'>Şimdi sula / Water now</a></body></html>";
160  server.send(200, "text/html", html);
161}
162
163void handleWater() {                             // elle tek darbe / one manual pulse
164  if (pumpState == IDLE && tankOk) {
165    pumpState = PUMPING;
166    pumpAt = millis();
167  }
168  server.sendHeader("Location", "/");
169  server.send(303);
170}
171
172void setup() {
173  Serial.begin(115200);
174  setRelay(PUMP_RELAY, false);   pinMode(PUMP_RELAY, OUTPUT);
175  setRelay(FAN_RELAY, false);    pinMode(FAN_RELAY, OUTPUT);
176  setRelay(HEATER_RELAY, false); pinMode(HEATER_RELAY, OUTPUT);
177  pinMode(LED_PIN, OUTPUT);
178  pinMode(FLOAT_PIN, INPUT_PULLUP);
179  dht.begin();
180  WiFi.begin(ssid, password);
181  while (WiFi.status() != WL_CONNECTED) {
182    delay(500);
183    Serial.print('.');
184  }
185  Serial.println();
186  Serial.println(WiFi.localIP());                // bu adresi telefonda aç / open this address on your phone
187  configTime(3 * 3600, 0, "pool.ntp.org");       // Türkiye UTC+3; kendi saat dilimini yaz / set your own time zone
188  server.on("/", handleRoot);
189  server.on("/water", handleWater);
190  server.begin();
191  readSensors();
192  lastRead = millis();
193}
194
195void loop() {
196  server.handleClient();
197  unsigned long now = millis();
198  if (now - lastRead >= READ_MS) {
199    lastRead = now;
200    readSensors();
201  }
202  controlClimate();
203  controlLight(now);
204  controlPump(now);
205}

Isto é o que muda em relação ao código do Uno: o ADC do ESP32 é de 12 bits e retorna valores de 0 a 4095, então você precisa refazer a calibração; como você alimenta os sensores pelo 3V3, os valores brutos também serão diferentes. O fuso horário é definido na linha configTime(); escrevemos 3 × 3600 segundos para a Turquia (UTC+3). Para o seu fuso com horário de verão, troque essa linha por configTzTime() e uma string de fuso POSIX, por exemplo configTzTime("GMT0BST,M3.5.0/1,M10.5.0", "pool.ntp.org") para o Reino Unido; para o horário de Brasília (UTC−3, sem horário de verão), use a string "BRT3". Se não houver internet ou a hora ainda não tiver sido obtida, a regra dos LEDs volta para a regra do Uno de “no máximo 4 horas”. A página não tem senha, então qualquer pessoa na sua rede doméstica poderia ligar a bomba; acrescentar uma senha simples com a função authenticate() da biblioteca WebServer é um bom hábito. Se quiser um LCD também no ESP32, você pode copiar a função updateLcd() do código do Uno do jeito que está; o SDA é o GPIO 21 e o SCL é o GPIO 22.

Se não houver Wi-Fi na escola, troque a linha WiFi.begin() e o laço de espera depois dela por WiFi.softAP("BlueGrays-Sera", "12345678"), para que o ESP32 crie a própria rede; o celular entra nessa rede e abre 192.168.4.1. Nesse caso não há internet, então a hora não pode ser obtida e os LEDs usam a regra reserva.
MétodoAcesso de fora de casaDificuldadeIdeal para
Servidor web no ESP32 (o código deste guia)Não, só na mesma redeMédiaApresentações na escola, demonstrações sem internet
Blynk IoTSimFácilMedidores, gráficos e notificações prontos
Bot do TelegramSimMédiaAvisos de “reservatório vazio” e “temperatura alta demais”
Google SheetsSimMédiaSemanas de registro de dados e gráficos
MQTT e Home AssistantSim, exige configuraçãoAvançadaAutomação real de casa e jardim

Para uma apresentação na escola, o servidor web no ESP32 é a escolha mais segura: ele não depende de serviço na nuvem e funciona na própria rede mesmo que o salão não tenha internet. Se também quiser acompanhar de fora de casa, o Blynk IoT, com medidores e gráficos prontos, é o caminho mais fácil. Para receber uma mensagem no celular quando o reservatório secar ou a temperatura ficar perigosa, veja o guia do bot do Telegram com ESP32; para juntar semanas de dados para o seu relatório, ESP32 com Google Sheets; e para ligar a estufa a uma automação residencial de verdade, MQTT e Home Assistant.

Versão em MicroPython para o Raspberry Pi Pico

Você também pode rodar a mesma estufa com um Raspberry Pi Pico em MicroPython. O firmware MicroPython do Pico já vem com um driver de DHT, então você não precisa copiar nenhum arquivo extra. As entradas analógicas são só GP26, GP27 e GP28, e read_u16() retorna valores de 0 a 65535; meça os seus números de calibração de acordo. Como o Pico funciona com 3,3 V, alimente os sensores pelo 3V3 e o módulo relé pelo VBUS (os 5 V do USB) ou pela linha do LM2596. O código abaixo tem todas as regras de ventoinha, aquecedor, LEDs e irrigação e mostra os valores no shell do Thonny. Se você salvar o arquivo no Pico como main.py, a placa funciona mesmo sem estar ligada a um computador. Se você usa o Pico W, com Wi-Fi, pode acrescentar o método do artigo sobre o servidor web no Pico W para acompanhar os valores pelo celular.

MicroPython
1from machine import Pin, ADC, PWM
2import dht
3import time
4
5sensor = dht.DHT22(Pin(15))              # GP15: DHT22 DATA
6soil_adc = ADC(26)                       # GP26: toprak nem AOUT / soil moisture AOUT
7ldr_adc = ADC(27)                        # GP27: LDR bölücü (3V3) / LDR divider (3V3)
8tank = Pin(14, Pin.IN, Pin.PULL_UP)      # şamandıra: suyla 0 / float switch: 0 with water
9fan = Pin(16, Pin.OUT, value=1)          # aktif LOW röleler kapalı başlar / active-LOW relays start off
10heater = Pin(17, Pin.OUT, value=1)
11pump = Pin(18, Pin.OUT, value=1)
12led = PWM(Pin(19))                       # MOSFET gate
13led.freq(1000)
14
15SOIL_DRY_RAW, SOIL_WET_RAW = 50000, 22000    # kendin ölç / measure your own
16LDR_DARK_RAW, LDR_BRIGHT_RAW = 2000, 60000
17EXTRA_LIGHT_MS = 4 * 3600 * 1000             # günü 4 saat uzat / extend the day by 4 h
18
19
20def percent(adc, raw0, raw100):
21    raw = sum(adc.read_u16() for _ in range(10)) // 10
22    pct = (raw - raw0) * 100 // (raw100 - raw0)
23    return max(0, min(100, pct))
24
25
26def hysteresis(state, value, on_at, off_at):
27    if on_at > off_at:                   # yükselince aç / turn on when rising
28        if value >= on_at:
29            return True
30        if value <= off_at:
31            return False
32    else:                                # düşünce aç / turn on when falling
33        if value <= on_at:
34            return True
35        if value >= off_at:
36            return False
37    return state                         # ölü bölge / dead band
38
39
40def relay(pin, on):
41    pin.value(0 if on else 1)
42
43
44t = h = None
45errors = pulses = 0
46fan_t = fan_h = heat_on = need_water = dark = led_on = False
47pump_state, pump_at, dark_since = "idle", 0, 0
48soil = light = 0
49tank_ok = True
50time.sleep(2)                            # DHT22 açılış süresi / DHT22 start-up time
51last_read = time.ticks_add(time.ticks_ms(), -2500)
52
53while True:
54    now = time.ticks_ms()
55    if time.ticks_diff(now, last_read) >= 2500:      # 2,5 sn'de bir ölç / measure every 2.5 s
56        last_read = now
57        try:
58            sensor.measure()
59            t, h = sensor.temperature(), sensor.humidity()
60            errors = 0
61        except OSError:
62            errors += 1                  # okuma hatası / read error
63        soil = percent(soil_adc, SOIL_DRY_RAW, SOIL_WET_RAW)
64        light = percent(ldr_adc, LDR_DARK_RAW, LDR_BRIGHT_RAW)
65        tank_ok = tank.value() == 0
66
67        if errors >= 3 or t is None:     # güvenli durum / safe state
68            heat_on, fan_t = False, True
69        else:
70            fan_t = hysteresis(fan_t, t, 30, 27)
71            fan_h = hysteresis(fan_h, h, 85, 75)
72            heat_on = hysteresis(heat_on, t, 16, 19)
73        relay(fan, fan_t or fan_h)
74        relay(heater, heat_on)
75
76        was_dark = dark
77        dark = hysteresis(dark, light, 25, 35)
78        if dark and not was_dark:
79            dark_since = now             # akşam oldu / dusk detected
80        led_on = dark and time.ticks_diff(now, dark_since) < EXTRA_LIGHT_MS
81        led.duty_u16(50000 if led_on else 0)
82
83        need_water = hysteresis(need_water, soil, 35, 50)
84        if not need_water:
85            pulses = 0
86        print("T", t, "N", h, "Toprak", soil, "Isik", light, "Pompa", pump_state,
87              "Depo OK" if tank_ok else "Depo BOS", "HATA" if pulses >= 6 else "")
88
89    # Sulama: 3 sn darbe, 60 sn bekleme / Watering: 3 s pulse, 60 s soak
90    if pump_state == "idle" and need_water and tank_ok and pulses < 6:
91        pump_state, pump_at = "pump", now
92        pulses += 1
93    elif pump_state == "pump" and (time.ticks_diff(now, pump_at) >= 3000 or not tank_ok):
94        pump_state, pump_at = "soak", now
95    elif pump_state == "soak" and time.ticks_diff(now, pump_at) >= 60000:
96        pump_state = "idle"
97    relay(pump, pump_state == "pump")
98    time.sleep_ms(100)

Dicas para apresentações na escola e em feiras de ciências

Estufas inteligentes são um tema tão popular que os avaliadores veem dezenas de maquetes parecidas. O que vai fazer você se destacar não é só mostrar que o sistema funciona, mas responder a uma pergunta com medições. Em uma feira de ciências (como o TÜBİTAK 4006, na Turquia), o objetivo, o método e o resultado esperado precisam estar claros; competições de pesquisa (como o TÜBİTAK 2204) esperam um experimento comparativo com dados. Uma boa pergunta de pesquisa para esta estufa é: “Quanta água a irrigação baseada na umidade do solo economiza em comparação com regar no mesmo horário todo dia, e como isso afeta o crescimento das plantas?”

  • Monte dois grupos: divida mudas da mesma espécie e da mesma altura em dois grupos. Irrigue um grupo com o sistema de sensores e o outro à mão, com uma quantidade fixa por dia; mantenha a luz e a temperatura iguais para os dois grupos.
  • Meça e anote: mantenha uma tabela com a quantidade semanal de água (medindo o reservatório, em mL), a altura das plantas (cm) e o número de folhas. A saída CSV do código ou um registro no Google Sheets dá de graça o gráfico de temperatura e umidade.
  • Desenhe um diagrama de blocos: sensores à esquerda, o Arduino no meio, atuadores à direita; identifique as setas com frases de regra (“solo < 35% → bomba”). Os avaliadores não vão ler o seu código, mas vão lembrar desse diagrama.
  • Explique a histerese: dois gráficos mostrando quantas vezes uma ventoinha com limite único e uma com histerese ligam e desligam em uma hora são o melhor jeito de mostrar aos avaliadores o lado de engenharia do projeto.
  • Escreva um roteiro de demonstração ao vivo: aqueça o DHT22 na palma da mão para ligar a ventoinha, passe o sensor de solo para um vaso seco para mostrar os pulsos da bomba, cubra o LDR com a mão para mostrar os LEDs acendendo. Mantenha cada demonstração em 10–20 segundos.
  • Ligue à vida real: fale de problemas reais, como o consumo de água e energia nas estufas, as doenças fúngicas causadas pelo excesso de umidade e a carga de trabalho dos produtores; mostre com números quais deles o seu sistema resolve.
  • Tenha um plano B: filme o sistema funcionando; leve fios, uma bomba e um adaptador de reserva; anote em um papel os seus valores de calibração e as configurações de Wi-Fi.
  • Prepare-se para as perguntas dos avaliadores: tenha respostas curtas prontas para “O que acontece se um sensor falhar?”, “O que acontece com as plantas em uma queda de energia?” e “Como você levaria isso para uma estufa de verdade?”. O estado seguro e a proteção contra transbordamento do código são as melhores respostas para essas perguntas.
Se você for participar de uma competição com temas, agricultura e tecnologia de alimentos é o encaixe mais natural para este projeto (no TÜBİTAK 4006, é o tema Tecnologias de Agricultura e Pecuária). Para mais ideias, tabelas de custo e critérios de avaliação, veja o artigo sobre ideias de projetos Arduino para feira de ciências.

Erros comuns e como corrigir

SintomaCausa provávelSolução
O Arduino reinicia ou o LCD embaralha quando a bomba ligaBomba e relés alimentados pelo pino 5V do Arduino, ruído do motorAlimente a bomba e as bobinas dos relés pelo LM2596, una os GNDs, acrescente um diodo reverso e um capacitor de 470 µF na bomba
Os relés ficam estalandoFoi usado um limite único ou as leituras têm ruídoUse limites separados para ligar e desligar (histerese) e faça a média das leituras analógicas
A água continua correndo depois que a bomba paraEfeito sifão: a ponta da mangueira está abaixo do nível da água do reservatórioPonha o reservatório mais baixo ou faça um furinho de ar no alto da mangueira
A umidade do solo fica sempre em 0% ou sempre em 100%Valores de calibração errados, ou AOUT no pino erradoMeça o valor bruto do sensor no ar e na água e escreva nas constantes
Os LEDs ficam piscandoO LDR enxerga a luz dos LEDsLeve o LDR para fora da estufa, virado para o céu
O DHT22 retorna nanPino errado, sem resistor de pull-up, ou lido com mais frequência que a cada 2 segundosConfira o pino, use um módulo ou acrescente 10 kΩ entre DATA e VCC
A temperatura marca mais do que deveriaO sol ou a luz dos LEDs bate direto no sensorSombreie com uma cobertura branca furada e afaste-o do aquecedor
Gotas de água e mofo nas paredesUmidade alta demais, ventilação fraca demaisBaixe o limite de umidade, aumente a entrada de ar, irrigue menos
No ESP32, a leitura do solo erra quando o Wi-Fi ligaO sensor está em um pino do ADC2Passe para um pino do ADC1, entre o GPIO 32 e o 39
Com um ESP32, o relé nunca soltaUm HIGH de 3,3 V não consegue desligar um módulo de 5 V ativo em LOWSepare o JD-VCC ou use um módulo relé com acionamento em 3,3 V

Um projeto de estufa inteligente junta em uma só montagem leitura de sensores, calibração, decisão com histerese, acionamento de relés e MOSFETs, planejamento de alimentação e monitoramento sem fio, e no fim faz crescer algo vivo de verdade: uma planta. Comece pela irrigação e pela ventilação, teste cada parte sozinha e depois acrescente o aquecedor, os LEDs e o celular. No app BlueGrays você encontra projetos passo a passo, com esquemas de circuito e código, para medir temperatura e umidade com um DHT11, ler a luz com um LDR, uma estação de irrigação inteligente e um registrador de temperatura com DHT22.

Perguntas frequentes

Do que preciso para um projeto de estufa inteligente?

Para uma estufa inteligente básica, bastam um Arduino Uno ou ESP32, um sensor de temperatura e umidade DHT22, um sensor capacitivo de umidade do solo, um LDR e um resistor de 10 kΩ, um módulo relé, uma minibomba submersível e uma ventoinha. A versão completa acrescenta um aquecedor PTC de 12 V, uma fita de LED acionada por MOSFET, um LCD I2C, uma chave de nível tipo boia, um adaptador de 12 V 5 A e um LM2596. Para o corpo, uma caixa organizadora transparente com tampa é a solução mais fácil e barata.

Como funciona uma estufa inteligente?

Uma estufa inteligente funciona em um ciclo de medir, decidir, agir e mostrar. Os sensores medem temperatura, umidade, umidade do solo e luz; o Arduino compara esses valores com limites e liga e desliga a ventoinha, o aquecedor, a bomba e os LEDs por relés ou por um MOSFET. Os resultados aparecem em um LCD e, com um ESP32, no celular.

Devo usar um DHT11 ou um DHT22 na estufa?

Escolha o DHT22. O DHT11 só é preciso a ±2 °C entre 0 e 50 °C e não enxerga umidade acima de 90%, enquanto uma estufa pode passar de 90% de umidade de manhã e ficar abaixo de zero no inverno. O DHT22 mede de −40 a 80 °C com ±0,5 °C e a umidade com cerca de ±2%, e o código dele é quase idêntico ao do DHT11.

Quanto custa um projeto de estufa com Arduino?

Os preços variam conforme o vendedor, mas, sem contar a placa, a versão básica (DHT22, sensor de umidade do solo, LDR, relé, bomba e ventoinha) costuma custar uns 25–40 dólares. A versão completa, com aquecedor, fita de LED, LCD, adaptador de 12 V e gabinete, chega a uns 70–120 dólares. Se você já tem um kit Arduino para iniciantes, o custo cai bastante.

Como acompanho a minha estufa pelo celular?

O jeito mais fácil é trocar o cérebro por um ESP32 e rodar um servidor web; você vê os valores e liga a bomba pelo navegador do celular na mesma rede Wi-Fi. Para acessar de fora de casa, você pode usar o Blynk IoT; para mensagens de aviso, um bot do Telegram; e para registro de dados de longo prazo, o Google Sheets.

Como apresento uma estufa inteligente em uma feira de ciências?

Em vez de só mostrar uma maquete funcionando, defina uma pergunta de pesquisa mensurável; por exemplo, meça por várias semanas, com dois grupos de plantas, quanta água a irrigação baseada em sensores economiza em comparação com a irrigação programada. Apresente o consumo de água, a altura das plantas e os gráficos de temperatura e umidade em tabelas e gráficos, explique o sistema com um diagrama de blocos e prepare um roteiro curto para a demonstração ao vivo.

Ver todos os projetos
Temperatura e umidade com DHT11
#55Intermediário

Temperatura e umidade com DHT11

Qual é a temperatura do seu quarto? O ar está úmido ou seco? O sensor DHT11 mede a temperatura e a umidade, e o Arduino mostra os valores em um display LCD de duas linhas, atualizando a cada poucos se

4.7(47)
73
Medição da intensidade da luz com LDR
#11Iniciante

Medição da intensidade da luz com LDR

Quer ver a luminosidade do seu quarto em números? O LDR, um pequeno componente sensível à luz, mede a luz ambiente, e o Arduino exibe o valor na tela do computador a cada meio segundo. Cubra o sensor

4.6(81)
139
Leitura da intensidade da luz com módulo LDR e ESP32
#113Iniciante

Leitura da intensidade da luz com módulo LDR e ESP32

Acompanhe em tempo real no Monitor Serial o valor bruto do ADC do módulo LDR e a porcentagem relativa de luz calculada a partir de referências de escuridão e claridade. Essa porcentagem não é uma medida em lux. O projeto ensina a lógica de calibração do módulo. É ideal fazê-lo antes do projeto 10, pois ajuda a determinar o valor do limiar.

4.6(36)
55
Estação de irrigação inteligente
#83Avançado
Premium

Estação de irrigação inteligente

Um jardineiro que rega suas plantas sozinho para que elas não sequem enquanto você está de férias. Um sensor de umidade do solo mede o quanto a terra está úmida; quando ela seca, o Arduino aciona a bo

5.0(42)
57
Temperatura e umidade com ESP32 e DHT22 (com display LCD)
#117Intermediário

Temperatura e umidade com ESP32 e DHT22 (com display LCD)

Este projeto mede a temperatura e a umidade do ambiente com um sensor DHT22 e exibe os dados em tempo real em um LCD I2C 16x2. Os dados são lidos do sensor pelo protocolo de um fio (one-wire) com a biblioteca Adafruit DHT. Este projeto serve de base para estações meteorológicas IoT, automação residencial ou sistemas de monitoramento ambiental em estufas. A comunicação com o DHT22 é feita pelo pino GPIO 4 do ESP32.

4.8(38)
56
Registrador de temperatura com ESP32 (DHT22 + DS1307 RTC + microSD + OLED)
#144Avançado
Premium

Registrador de temperatura com ESP32 (DHT22 + DS1307 RTC + microSD + OLED)

Monte um registrador de temperatura com data e hora usando ESP32! Leia a temperatura e a umidade do DHT22 a cada minuto, obtenha a hora real do DS1307 RTC e grave os dados em CSV no cartão microSD. O OLED exibe a temperatura atual em letras grandes, a porcentagem de umidade, a data e a hora e o contador de registros. O arquivo /temp_log.csv no cartão SD pode ser aberto no Excel, Google Sheets ou pandas do Python para análise — ideal para monitorar estufas, acompanhar a temperatura de casa ou…

4.7(7)
12
Iluminação inteligente com ESP32 (PIR + LDR + relé + OLED)
#141Avançado
Premium

Iluminação inteligente com ESP32 (PIR + LDR + relé + OLED)

Transforme a iluminação do corredor, banheiro ou despensa em iluminação inteligente com ESP32! Com um sensor de movimento PIR, um sensor de luz LDR e um módulo relé, montamos o cenário clássico de "acender a luz ao detectar movimento no escuro" — se o ambiente já estiver claro, o movimento NÃO aciona a luz, economizando energia durante o dia. A luz fica acesa por 30 segundos; se o movimento continuar, o temporizador reinicia e a luz permanece acesa por mais tempo.

4.3(6)
11