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.





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? | Sensor | Atuador | Regra (exemplo) |
|---|---|---|---|
| Temperatura do ar | DHT22 | Ventoinha e aquecedor | A ventoinha liga a 30 °C, o aquecedor liga a 16 °C |
| Umidade relativa | DHT22 | Ventoinha | A ventoinha liga a 85% e desliga a 75% |
| Umidade do solo | Sensor capacitivo de umidade do solo | Minibomba submersível | Irriga em pulsos de 3 segundos abaixo de 35% |
| Luz | LDR | Fita de LED (MOSFET) | Quando escurece, os LEDs ficam acesos por no máximo 4 horas |
| Água no reservatório | Chave de nível tipo boia | Bloqueio da bomba | Se 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.
| Planta | Dia | Noite | Umidade relativa | Observação |
|---|---|---|---|---|
| Tomate | 21–27 °C | 15–18 °C | 60–75% | As flores podem cair acima de 32 °C |
| Pimentão | 21–29 °C | 16–20 °C | 60–75% | Mais sensível ao frio que o tomate |
| Alface e espinafre | 15–20 °C | 10–15 °C | 60–70% | Pendoa cedo com calor e dias longos |
| Morango | 18–24 °C | 10–13 °C | 60–75% | O risco de mofo-cinzento aumenta com umidade alta |
| Mudas e germinação | 20–25 °C | 18–20 °C | 70–85% | A temperatura do solo importa mais que a do ar |
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.
Lista de peças e custo
| Peça | Qtd. | Função | Faixa de preço |
|---|---|---|---|
| Arduino Uno R3 ou ESP32 DevKit | 1 | O cérebro da estufa | Média |
| Módulo de temperatura e umidade DHT22 (AM2302) | 1 | Temperatura e umidade do ar | Baixa |
| Sensor capacitivo de umidade do solo (v1.2) | 1 (um por vaso) | Umidade do solo | Baixa |
| LDR (GL5528) e resistor de 10 kΩ | 1 + 1 | Luz ambiente | Muito baixa |
| Chave de nível de água tipo boia | 1 | Bloqueia a bomba quando o reservatório está vazio | Baixa |
| Módulo relé de 4 canais 5 V (com optoacoplador e JD-VCC) | 1 | Ventoinha, aquecedor e bomba | Baixa |
| MOSFET canal N de nível lógico (IRLZ44N), resistores de 220 Ω e 100 kΩ | 1 conjunto | Brilho da fita de LED (PWM) | Muito baixa |
| Minibomba submersível de 3–6 V e 1 m de mangueira de silicone | 1 | Irrigação | Baixa |
| Ventoinha de computador de 12 V e 80 mm | 1–2 | Ventilação e retirada de umidade | Baixa |
| Aquecedor PTC de 12 V (10–30 W) e fusível térmico | 1 + 1 | Aquecimento | Baixa |
| Fita de LED de 12 V (luz de cultivo ou branco frio), cerca de 1 m | 1 | Iluminação complementar | Baixa |
| LCD I2C 16x2 | 1 | Exibição do estado | Baixa – média |
| Adaptador de 12 V 5 A e conversor buck LM2596 | 1 + 1 | Alimentação | Média |
| Diodo 1N5819 ou 1N4007, capacitor de 470 µF, bornes de parafuso, jumpers | – | Proteção e ligações | Muito baixa |
| Caixa organizadora transparente com tampa ou chapa de acrílico, vasos, pratinhos | – | Corpo da estufa | Mé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.

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.
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.
| Carga | Valor típico | Driver | Por quê? |
|---|---|---|---|
| Aquecedor PTC de 12 V | 10–30 W, cerca de 1–2,5 A | Relé (IN2) | Corrente alta, só precisa de liga/desliga |
| Ventoinha de 12 V e 80 mm | 0,1–0,2 A | Relé (IN1) ou MOSFET | O relé é simples; o MOSFET também permite controlar a velocidade por PWM |
| Minibomba submersível de 3–6 V | 0,1–0,25 A, mais na partida | Relé (IN3) ou MOSFET com diodo | Funciona 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 metro | MOSFET de nível lógico | Controle de brilho por PWM e funcionamento silencioso |

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.
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.
| Carga | Linha | Corrente aprox. |
|---|---|---|
| Aquecedor PTC (24 W) | 12 V | 2 A (mais no primeiro instante, com ele frio) |
| Fita de LED (1 m) | 12 V | 1–1,2 A |
| Duas ventoinhas de 80 mm | 12 V | 0,2–0,4 A |
| Arduino Uno e sensores (pelo conector DC) | 12 V | Cerca de 0,06–0,08 A |
| Entrada do LM2596 (com o lado de 5 V puxando 1 A) | 12 V | Cerca de 0,5 A |
| Módulo relé (três relés energizados) | 5 V | 0,2–0,25 A |
| Minibomba submersível | 5 V | 0,1–0,25 A (mais na partida) |
| LCD I2C (luz de fundo acesa) | 5 V | 20–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ção | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| DATA do DHT22 | D2 | GPIO 4 | GP15 |
| Chave de boia (outro terminal ao GND) | D3 | GPIO 13 | GP14 |
| Relé IN3: bomba | D4 | GPIO 25 | GP18 |
| Relé IN1: ventoinha | D7 | GPIO 26 | GP16 |
| Relé IN2: aquecedor | D8 | GPIO 27 | GP17 |
| Gate do MOSFET (por 220 Ω): LED | D9 | GPIO 18 | GP19 |
| AOUT do sensor de umidade do solo | A0 | GPIO 34 | GP26 |
| Ponto médio do divisor do LDR | A1 | GPIO 35 (divisor a partir do 3V3) | GP27 (divisor a partir do 3V3) |
| SDA / SCL do LCD I2C | A4 / A5 | GPIO 21 / 22 | GP4 / GP5 |
| Alimentação dos sensores | 5V | 3V3 | 3V3 |
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.
| Controle | Liga | Desliga | Zona morta |
|---|---|---|---|
| Ventoinha (temperatura) | ≥ 30 °C | ≤ 27 °C | 3 °C |
| Ventoinha (umidade) | ≥ 85% | ≤ 75% | 10% |
| Aquecedor | ≤ 16 °C | ≥ 19 °C | 3 °C |
| Irrigação | Solo ≤ 35% | Solo ≥ 50% | 15% |
| Iluminação de LED | Luz ≤ 25% | Luz ≥ 35% ou depois de 4 horas | 10% |
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
- 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.
- 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.
- 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.
- 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.
- 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.
- Ligue a fita de LED pelo MOSFET e mude o valor de analogWrite() para ver o brilho mudar.
- 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?
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.
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ção | Trabalho | Com 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 leituras | A 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 falhar | A cada volta do loop |
| controlLight() (controle da luz) | Acende os LEDs com PWM quando escurece e os apaga depois de no máximo 4 horas | A 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 transbordamento | A 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 dispositivos | A cada 3 segundos |
| logCsv() (registro em CSV) | Escreve leituras e estados no Monitor Serial como uma linha CSV | A 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

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.
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.
| Método | Acesso de fora de casa | Dificuldade | Ideal para |
|---|---|---|---|
| Servidor web no ESP32 (o código deste guia) | Não, só na mesma rede | Média | Apresentações na escola, demonstrações sem internet |
| Blynk IoT | Sim | Fácil | Medidores, gráficos e notificações prontos |
| Bot do Telegram | Sim | Média | Avisos de “reservatório vazio” e “temperatura alta demais” |
| Google Sheets | Sim | Média | Semanas de registro de dados e gráficos |
| MQTT e Home Assistant | Sim, exige configuração | Avançada | Automaçã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.
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.
Erros comuns e como corrigir
| Sintoma | Causa provável | Solução |
|---|---|---|
| O Arduino reinicia ou o LCD embaralha quando a bomba liga | Bomba e relés alimentados pelo pino 5V do Arduino, ruído do motor | Alimente 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 estalando | Foi usado um limite único ou as leituras têm ruído | Use 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 para | Efeito sifão: a ponta da mangueira está abaixo do nível da água do reservatório | Ponha 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 errado | Meça o valor bruto do sensor no ar e na água e escreva nas constantes |
| Os LEDs ficam piscando | O LDR enxerga a luz dos LEDs | Leve o LDR para fora da estufa, virado para o céu |
| O DHT22 retorna nan | Pino errado, sem resistor de pull-up, ou lido com mais frequência que a cada 2 segundos | Confira o pino, use um módulo ou acrescente 10 kΩ entre DATA e VCC |
| A temperatura marca mais do que deveria | O sol ou a luz dos LEDs bate direto no sensor | Sombreie com uma cobertura branca furada e afaste-o do aquecedor |
| Gotas de água e mofo nas paredes | Umidade alta demais, ventilação fraca demais | Baixe o limite de umidade, aumente a entrada de ar, irrigue menos |
| No ESP32, a leitura do solo erra quando o Wi-Fi liga | O sensor está em um pino do ADC2 | Passe para um pino do ADC1, entre o GPIO 32 e o 39 |
| Com um ESP32, o relé nunca solta | Um HIGH de 3,3 V não consegue desligar um módulo de 5 V ativo em LOW | Separe 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.
Projetos para montar depois desta leitura
Ver todos os projetos
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

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

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.

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

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.

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…

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.
Mais artigos
App BlueGrays
Monte este circuito passo a passo pelo celular
Esquema do circuito, código e lista de componentes em uma só tela. Mais de 200 projetos com Arduino, ESP32 e Pico prontos para montar.
Escaneie com seu celular
Aponte a câmera para o código para abrir a loja de aplicativos do seu celular.
ou abrir a loja

