Monitor de Plantas

Sistema doméstico de rega automática: sensores e câmera avaliam as plantas em tempo real, um ESP32 rega sozinho quando necessário, e um dashboard PWA mostra tudo sem depender de nuvem

2026
Capa do projeto Monitor de Plantas
Monitor de Plantas - dashboard
Samambaia● saudável
62%umidade

Regada há 3h

Suculenta● atenção
45%umidade

Regada há 9h

Jiboia● seca
28%umidade

Regada há 1d

Visão Geral

O Monitor de Plantas cuida das plantas de casa quando não dá tempo: mede umidade da terra, temperatura, umidade do ar e luz, rega automaticamente quando necessário, tira fotos periódicas e avisa no celular se algo estiver errado, tudo isso sem depender da nuvem, rodando na própria rede Wi-Fi de casa. São três peças com deploy independente falando HTTP/JSON e um WebSocket cru na rede local: um ESP32 por vaso lê os sensores e aciona a bomba, um ESP32-CAM opcional fotografa as plantas, e um backend em Java (Javalin) guarda o histórico em SQLite, avalia as fotos e empurra atualizações ao vivo para o dashboard.

O que o sistema faz

Rega automática por planta

Cada ESP32 decide sozinho quando abrir o relé da bomba comparando a umidade do solo ao limiar configurado, com um tempo mínimo entre regas para não encharcar o vaso.

Fotos periódicas + saúde por cor

Um ESP32-CAM opcional fotografa as plantas; um heurístico de cor (verde vs. amarelo/marrom) estima saúde, sem depender de nuvem nem de um modelo de ML.

Notificação push mesmo com o app fechado

Web Push com VAPID avisa no celular quando a terra está seca há muito tempo, a bomba ligou e não resolveu, uma foto indica problema, ou um sensor parou de responder.

Tempo real por WebSocket, sem nuvem

Dashboard PWA instalável no celular, atualizado ao vivo por um WebSocket próprio (sem Socket.IO), rodando inteiro na rede Wi-Fi de casa.

O Desafio

  • Se o backend caísse (o computador desligou, por exemplo), as plantas não podiam ficar sem rega até alguém notar.
  • A ESP32-CAM tem pouquíssima RAM livre além do framebuffer da própria imagem, bufferizar a foto inteira antes de enviar arriscava estourar memória.
  • Timestamps chegavam em formatos diferentes dependendo da origem, o SQLite grava UTC sem 'Z', eventos de WebSocket vêm com 'Z', e isso já gerou um bug real: leituras ao vivo mostrando 'há NaN min'.

A Solução

  • A lógica de rega automática mora inteira no firmware do ESP32, não no backend: ele decide sozinho quando abrir o relé comparando a leitura ao limiar guardado em memória. O dashboard só ajusta esse limiar remotamente; se o servidor cair, a planta continua sendo regada com o último valor configurado.
  • O upload da foto é multipart, escrito em três chamadas HTTP separadas (cabeçalho, bytes JPEG crus, rodapé) em vez de concatenar tudo num buffer só, evitando dobrar o uso de RAM em cima do framebuffer da câmera.
  • Todo timestamp que passa pelo frontend segue por um único helper (toUtcDate()) em vez de cada tela tratar o formato à sua maneira, a causa raiz do bug virou regra para qualquer código novo que toque em data.

Arquitetura do Sistema

Três peças com deploy independente, sem fila de mensagens nem broker: o ESP32 de sensores fala HTTP direto com o backend, a ESP32-CAM sobe fotos por multipart, e o backend empurra tudo em tempo real para o dashboard por um WebSocket próprio.

[ESP32 + bombas] lê sensores por planta, decide e executa a rega sozinho
[ESP32-CAM] fotos periódicas, upload multipart em streaming
↓ HTTP POST leituras / foto
[Backend Java/Javalin] guarda histórico em SQLite, avalia saúde por cor, agenda alerta de dispositivo offline
↓ WebSocket + Web Push
[Dashboard PWA] tempo real no navegador, instalável no celular, recebe push mesmo fechado

Sem Nuvem, Sem Diagnóstico Mágico

O sistema roda inteiro na rede de casa, de propósito, e é honesto sobre o que ainda não faz:

  • Não identifica doenças específicas nem espécies de plantas, a avaliação da foto é um heurístico de cor (mais verde = melhor), não um diagnóstico.
  • Não funciona fora de casa "pronto para uso": acessar de fora da rede Wi-Fi exige configurar acesso remoto à parte.
  • Sem ORM, sem pool de conexão: um único Connection JDBC direto, de propósito, para manter o backend simples de auditar.
  • Ainda sem suíte de testes ou linter configurado no backend.