Comedouro Inteligente para Gatos com IA: Nutrição Personalizada e Controle de Peso

Raspberry Pi + câmera + servo motor + IA para resolver os principais problemas de alimentação felina

Iniciante/Intermediário
Sofia Andrade

Sofia Andrade

Especialista em IA

Se você tem mais de um gato ou se preocupa com o peso do seu felino, este projeto é para você! O comedouro inteligente usa reconhecimento facial para identificar cada gato e liberar a porção exata de ração, evitando que o gato guloso coma a comida do mais tímido ou que gatos com dieta especial comam além da conta.

Visão Geral do Projeto

  • O que vamos construir: Sistema automatizado que reconhece gatos e controla alimentação individual
  • Resultado: Cada gato come apenas sua porção, no horário certo
  • Nível de dificuldade: Iniciante/Intermediário
  • Tempo de construção: 4-6 horas (perfeito para um sábado)
  • Custo estimado: R$ 300-500

Por que seu gato precisa de um comedouro inteligente?

Problemas comuns na alimentação felina:

  • Obesidade felina: 60% dos gatos domésticos estão acima do peso ideal, causando diabetes e problemas articulares
  • Competição alimentar: Em casas com múltiplos gatos, o dominante come demais e o submisso fica desnutrido
  • Dietas especiais: Gatos diabéticos, alérgicos ou idosos precisam de rações específicas
  • Horários irregulares: Alimentação fora de hora prejudica a digestão e causa ansiedade
  • Desperdício: Ração exposta resseca e perde nutrients, gerando custos extras

Benefícios do comedouro inteligente:

  • Controle de peso: Porções exatas para cada gato
  • Paz entre gatos: Cada um come sua própria comida
  • Saúde monitorada: Histórico de alimentação detecta problemas cedo
  • Ração sempre fresca: Compartimentos fechados preservam qualidade
  • Tranquilidade: Alimentação garantida mesmo quando você viaja

Material Necessário

Componentes eletrônicos:

  • Raspberry Pi 4 (2GB) - R$ 400-500
  • Câmera para Raspberry Pi - R$ 50-80
  • Servo motor MG996R (mais forte que SG90) - R$ 35-50
  • Célula de carga 5kg + HX711 - R$ 25-40
  • Display OLED 128x64 - R$ 20-35
  • Buzzer ativo 5V - R$ 5-10
  • LEDs RGB - R$ 8-15

Estrutura física:

  • Potes de ração herméticos (4x) - R$ 40-60
  • Base MDF 40x30cm - R$ 25-35
  • Tigelas inox pequenas (2x) - R$ 20-30
  • Funil de plástico - R$ 8-15
  • Parafusos e suportes - R$ 15-25

Componentes auxiliares:

  • Cartão microSD 32GB - R$ 30-40
  • Fonte 5V 3A - R$ 35-50
  • Jumpers e protoboard - R$ 15-25
  • Caixa plástica vedada - R$ 20-30

Custo total: R$ 300-500

⚠️ Importante: Este projeto é mais barato que os anteriores pois não precisa de ESP8266 (WiFi nativo do Pi) nem bomba d'água.

Como Funciona o Sistema

Fluxo de operação inteligente:

  1. Detecção: Câmera identifica gato se aproximando
  2. Reconhecimento: IA compara com fotos cadastradas
  3. Verificação: Sistema checa se já comeu hoje/horário
  4. Liberação: Servo motor libera porção exata
  5. Pesagem: Célula de carga confirma quantidade
  6. Registro: Dados salvos no histórico nutricional

Recursos avançados:

  • Múltiplos perfis: Até 6 gatos diferentes
  • Porções personalizadas: 20g a 150g por refeição
  • Horários flexíveis: 1-6 refeições por dia
  • Controle de peso: Metas de ganho/perda
  • Alertas veterinários: Mudanças no apetite
  • Modo férias: Alimentação automática por dias

Distribuição do Tempo de Construção

Total: 4-6 horas

Montagem da estrutura (1-2 horas):

  • Corte e furação do MDF: 30 minutos
  • Montagem dos compartimentos: 45 minutos
  • Fixação dos servos: 30 minutos
  • Acabamento: 15 minutos

Montagem eletrônica (1-2 horas):

  • Conexões dos servos: 30 minutos
  • Instalação da câmera: 15 minutos
  • Montagem da célula de carga: 30 minutos
  • Testes de funcionamento: 45 minutos

Programação e IA (1-2 horas):

  • Configuração do Raspberry Pi: 30 minutos
  • Treinamento da IA: 45 minutos
  • Calibração dos servos: 30 minutos
  • Testes com fotos de gatos: 15 minutos

Calibração e ajustes (1 hora):

  • Ajuste de porções: 20 minutos
  • Teste do ciclo completo: 20 minutos
  • Configuração dos perfis: 20 minutos

Esquema de Ligações

Raspberry Pi - Conexões principais:

Raspberry Pi          Componente
=============         ==================
GPIO 18            → Servo 1 (Compartimento A)
GPIO 19            → Servo 2 (Compartimento B)
GPIO 20            → Servo 3 (Compartimento C)
GPIO 21            → Servo 4 (Compartimento D)
GPIO 16            → Buzzer
GPIO 26            → LED Status
SDA (GPIO 2)       → Display OLED (SDA)
SCL (GPIO 3)       → Display OLED (SCL)
GPIO 5             → HX711 (DT)
GPIO 6             → HX711 (SCK)

Camera Port        → Módulo câmera Pi
5V                 → Alimentação servos
GND                → Terra comum

Layout físico sugerido:

  [Câmera]
       ↓
┌─────────────────────────┐
│  🥫A  🥫B  🥫C  🥫D     │ ← Compartimentos de ração
│                         │
│      [Display]          │
│                         │
│  🍽️ Tigela 1  🍽️ Tigela 2 │ ← Tigelas dos gatos
│     (Peso)              │
└─────────────────────────┘

Código Principal (Python)

Programa completo do comedouro:

Ver código completo
import cv2
import time
import numpy as np
from datetime import datetime, timedelta
import json
import threading
from gpiozero import Servo, Buzzer, LED
import board
import busio
import adafruit_ssd1306
from PIL import Image, ImageDraw, ImageFont
import RPi.GPIO as GPIO
from hx711 import HX711
import tensorflow as tf

# =============== CONFIGURAÇÕES DO SISTEMA ===============
class Config:
    # Pinos GPIO
    SERVOS = [18, 19, 20, 21]  # 4 compartimentos
    BUZZER_PIN = 16
    LED_PIN = 26
    HX711_DT = 5
    HX711_SCK = 6
    
    # Configurações de alimentação
    MIN_PORTION = 20  # gramas
    MAX_PORTION = 150  # gramas
    MAX_DAILY_MEALS = 6
    WEIGHT_TOLERANCE = 5  # ±5g
    
    # Configurações da câmera
    CAMERA_WIDTH = 640
    CAMERA_HEIGHT = 480
    DETECTION_CONFIDENCE = 0.75

# =============== CLASSE PRINCIPAL ===============
class SmartFeeder:
    def __init__(self):
        self.config = Config()
        self.setup_hardware()
        self.load_cat_profiles()
        self.feeding_history = self.load_history()
        self.current_cat = None
        self.system_status = "Aguardando gato..."
        
    def setup_hardware(self):
        """Inicializa todos os componentes de hardware"""
        # Servos dos compartimentos
        self.servos = []
        for pin in self.config.SERVOS:
            servo = Servo(pin)
            servo.min()  # Posição fechada
            self.servos.append(servo)
        
        # Buzzer e LED
        self.buzzer = Buzzer(self.config.BUZZER_PIN)
        self.led = LED(self.config.LED_PIN)
        
        # Display OLED
        i2c = busio.I2C(board.SCL, board.SDA)
        self.display = adafruit_ssd1306.SSD1306_I2C(128, 64, i2c)
        self.display.fill(0)
        self.display.show()
        
        # Célula de carga
        self.scale = HX711(self.config.HX711_DT, self.config.HX711_SCK)
        self.calibrate_scale()
        
        # Câmera
        self.camera = cv2.VideoCapture(0)
        self.camera.set(cv2.CAP_PROP_FRAME_WIDTH, self.config.CAMERA_WIDTH)
        self.camera.set(cv2.CAP_PROP_FRAME_HEIGHT, self.config.CAMERA_HEIGHT)
        
        # Carregar modelo de IA
        self.load_ai_model()
        
        print("✅ Hardware inicializado com sucesso!")
        
    def load_ai_model(self):
        """Carrega modelo treinado do Teachable Machine"""
        try:
            self.model = tf.keras.models.load_model('cat_recognition_model.h5')
            with open('cat_labels.txt', 'r') as f:
                self.cat_labels = [line.strip() for line in f.readlines()]
            print("✅ Modelo de IA carregado!")
        except:
            print("⚠️ Modelo não encontrado. Use modo manual.")
            self.model = None
            self.cat_labels = []
    
    def calibrate_scale(self):
        """Calibra a célula de carga"""
        print("Calibrando balança... Remova qualquer peso.")
        time.sleep(3)
        self.scale.reset()
        self.scale.tare()
        
        # Calibração com peso conhecido (ex: 100g)
        # self.scale.set_scale_ratio(100)  # Ajustar conforme necessário
        print("✅ Balança calibrada!")
    
    def load_cat_profiles(self):
        """Carrega perfis dos gatos salvos"""
        try:
            with open('cat_profiles.json', 'r') as f:
                self.cat_profiles = json.load(f)
        except FileNotFoundError:
            # Perfis padrão para demonstração
            self.cat_profiles = {
                "Mimi": {
                    "portion_size": 60,  # gramas
                    "meals_per_day": 3,
                    "meal_times": ["08:00", "14:00", "20:00"],
                    "compartment": 0,
                    "diet_type": "adulto",
                    "weight_goal": "manter"
                },
                "Felix": {
                    "portion_size": 40,  # gato menor
                    "meals_per_day": 4,
                    "meal_times": ["07:00", "12:00", "17:00", "21:00"],
                    "compartment": 1,
                    "diet_type": "light",
                    "weight_goal": "perder"
                }
            }
            self.save_cat_profiles()
        
        print(f"✅ {len(self.cat_profiles)} perfis de gatos carregados!")
    
    def save_cat_profiles(self):
        """Salva perfis dos gatos"""
        with open('cat_profiles.json', 'w') as f:
            json.dump(self.cat_profiles, f, indent=2)
    
    def load_history(self):
        """Carrega histórico de alimentação"""
        try:
            with open('feeding_history.json', 'r') as f:
                return json.load(f)
        except FileNotFoundError:
            return {}
    
    def save_history(self):
        """Salva histórico de alimentação"""
        with open('feeding_history.json', 'w') as f:
            json.dump(self.feeding_history, f, indent=2)

# =============== RECONHECIMENTO DE GATOS ===============
    def recognize_cat(self, frame):
        """Usa IA para reconhecer qual gato está na imagem"""
        if self.model is None:
            return None, 0.0
        
        # Pré-processar imagem
        img = cv2.resize(frame, (224, 224))
        img = np.expand_dims(img, axis=0)
        img = img.astype('float32') / 255.0
        
        # Fazer predição
        predictions = self.model.predict(img)
        confidence = np.max(predictions)
        cat_index = np.argmax(predictions)
        
        if confidence > self.config.DETECTION_CONFIDENCE:
            cat_name = self.cat_labels[cat_index]
            return cat_name, confidence
        
        return None, confidence
    
    def detect_cat_presence(self):
        """Detecta se há um gato na frente da câmera"""
        ret, frame = self.camera.read()
        if not ret:
            return None, None
        
        # Detector simples de movimento/presença
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        
        # Usar detector de gato (versão simplificada)
        # Em uma implementação real, usaria YOLO ou similar
        
        # Por agora, detectar se há movimento significativo
        if hasattr(self, 'previous_frame'):
            diff = cv2.absdiff(self.previous_frame, gray)
            motion = np.sum(diff) > 50000  # Threshold de movimento
            
            if motion:
                cat_name, confidence = self.recognize_cat(frame)
                self.previous_frame = gray
                return cat_name, confidence
        
        self.previous_frame = gray
        return None, 0.0

# =============== CONTROLE DE ALIMENTAÇÃO ===============
    def can_cat_eat(self, cat_name):
        """Verifica se o gato pode comer agora"""
        if cat_name not in self.cat_profiles:
            return False, "Gato não cadastrado"
        
        profile = self.cat_profiles[cat_name]
        today = datetime.now().strftime('%Y-%m-%d')
        
        # Verificar histórico de hoje
        if today in self.feeding_history:
            today_meals = self.feeding_history[today].get(cat_name, [])
            
            # Verificar se já atingiu limite diário
            if len(today_meals) >= profile['meals_per_day']:
                return False, f"Já comeu {len(today_meals)} vezes hoje"
            
            # Verificar se passou tempo suficiente desde última refeição
            if today_meals:
                last_meal = datetime.strptime(today_meals[-1]['time'], '%H:%M:%S')
                now = datetime.now()
                time_diff = now - last_meal.replace(year=now.year, month=now.month, day=now.day)
                
                if time_diff.seconds < 3600:  # Mínimo 1 hora entre refeições
                    minutes_left = 60 - (time_diff.seconds // 60)
                    return False, f"Aguarde {minutes_left} minutos"
        
        return True, "Pode comer!"
    
    def dispense_food(self, cat_name):
        """Libera comida para o gato específico"""
        if cat_name not in self.cat_profiles:
            return False
        
        profile = self.cat_profiles[cat_name]
        compartment = profile['compartment']
        portion_size = profile['portion_size']
        
        print(f"🍽️ Liberando {portion_size}g para {cat_name}")
        
        # Tocar som de alimentação
        self.play_feeding_sound()
        
        # Abrir compartimento
        servo = self.servos[compartment]
        servo.max()  # Abrir
        
        # Aguardar tempo baseado na porção (aprox. 1 segundo por 20g)
        dispense_time = (portion_size / 20) * 1.0
        time.sleep(dispense_time)
        
        # Fechar compartimento
        servo.min()
        
        # Verificar peso dispensado
        weight_dispensed = self.check_dispensed_weight()
        
        # Registrar no histórico
        self.record_feeding(cat_name, portion_size, weight_dispensed)
        
        print(f"✅ Alimentação concluída! Peso: {weight_dispensed}g")
        return True
    
    def check_dispensed_weight(self):
        """Verifica peso da ração dispensada"""
        time.sleep(1)  # Aguardar estabilizar
        weight = self.scale.get_weight(5)  # Média de 5 leituras
        return max(0, weight)  # Não retornar peso negativo
    
    def record_feeding(self, cat_name, target_portion, actual_weight):
        """Registra alimentação no histórico"""
        today = datetime.now().strftime('%Y-%m-%d')
        current_time = datetime.now().strftime('%H:%M:%S')
        
        if today not in self.feeding_history:
            self.feeding_history[today] = {}
        
        if cat_name not in self.feeding_history[today]:
            self.feeding_history[today][cat_name] = []
        
        feeding_record = {
            'time': current_time,
            'target_portion': target_portion,
            'actual_weight': actual_weight,
            'accuracy': abs(target_portion - actual_weight) <= self.config.WEIGHT_TOLERANCE
        }
        
        self.feeding_history[today][cat_name].append(feeding_record)
        self.save_history()

# =============== INTERFACE E DISPLAY ===============
    def update_display(self):
        """Atualiza informações no display OLED"""
        # Limpar display
        image = Image.new('1', (128, 64))
        draw = ImageDraw.Draw(image)
        
        # Fonte pequena
        try:
            font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 10)
            font_small = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 8)
        except:
            font = ImageFont.load_default()
            font_small = ImageFont.load_default()
        
        # Título
        draw.text((0, 0), "Comedouro Smart", font=font, fill=255)
        
        # Status atual
        draw.text((0, 15), f"Status: {self.system_status}", font=font_small, fill=255)
        
        # Gato atual (se detectado)
        if self.current_cat:
            draw.text((0, 25), f"Gato: {self.current_cat}", font=font_small, fill=255)
        
        # Próximas refeições
        draw.text((0, 40), "Próximas refeições:", font=font_small, fill=255)
        y_pos = 50
        for cat_name in list(self.cat_profiles.keys())[:2]:  # Mostrar só 2
            next_meal = self.get_next_meal_time(cat_name)
            if next_meal:
                draw.text((0, y_pos), f"{cat_name}: {next_meal}", font=font_small, fill=255)
                y_pos += 8
        
        # Mostrar no display
        self.display.image(image)
        self.display.show()
    
    def get_next_meal_time(self, cat_name):
        """Calcula próximo horário de refeição para o gato"""
        if cat_name not in self.cat_profiles:
            return None
        
        profile = self.cat_profiles[cat_name]
        meal_times = profile['meal_times']
        current_time = datetime.now().time()
        
        for meal_time_str in meal_times:
            meal_time = datetime.strptime(meal_time_str, '%H:%M').time()
            if meal_time > current_time:
                return meal_time_str
        
        # Se passou de todos os horários de hoje, retornar primeiro de amanhã
        return meal_times[0] + " (amanhã)"

# =============== SONS E ALERTAS ===============
    def play_feeding_sound(self):
        """Toca som durante alimentação"""
        for _ in range(3):
            self.buzzer.on()
            time.sleep(0.1)
            self.buzzer.off()
            time.sleep(0.1)
    
    def play_error_sound(self):
        """Toca som de erro"""
        for _ in range(2):
            self.buzzer.on()
            time.sleep(0.3)
            self.buzzer.off()
            time.sleep(0.2)
    
    def play_recognition_sound(self):
        """Toca som quando reconhece gato"""
        self.buzzer.on()
        time.sleep(0.05)
        self.buzzer.off()

# =============== LOOP PRINCIPAL ===============
    def run(self):
        """Loop principal do sistema"""
        print("🐱 Comedouro iniciado! Aguardando gatos...")
        self.led.on()  # Sistema ativo
        
        while True:
            try:
                # Detectar presença de gato
                cat_name, confidence = self.detect_cat_presence()
                
                if cat_name and confidence > self.config.DETECTION_CONFIDENCE:
                    if cat_name != self.current_cat:
                        self.current_cat = cat_name
                        self.system_status = f"Gato detectado: {cat_name}"
                        print(f"🐱 {cat_name} detectado! (confiança: {confidence:.2f})")
                        self.play_recognition_sound()
                    
                    # Verificar se pode comer
                    can_eat, reason = self.can_cat_eat(cat_name)
                    
                    if can_eat:
                        self.system_status = f"Alimentando {cat_name}..."
                        success = self.dispense_food(cat_name)
                        
                        if success:
                            self.system_status = f"{cat_name} alimentado!"
                        else:
                            self.system_status = "Erro na alimentação"
                            self.play_error_sound()
                    else:
                        self.system_status = f"{cat_name}: {reason}"
                        print(f"🚫 {cat_name} não pode comer: {reason}")
                        if "Aguarde" not in reason:  # Não tocar som para "aguarde"
                            self.play_error_sound()
                
                else:
                    if self.current_cat is not None:
                        self.current_cat = None
                        self.system_status = "Aguardando gato..."
                
                # Atualizar display
                self.update_display()
                
                # Aguardar um pouco antes da próxima verificação
                time.sleep(0.5)
                
            except KeyboardInterrupt:
                print("
🛑 Sistema interrompido pelo usuário")
                break
            except Exception as e:
                print(f"❌ Erro: {e}")
                self.system_status = f"Erro: {str(e)[:20]}"
                time.sleep(1)
        
        self.cleanup()
    
    def cleanup(self):
        """Limpa recursos ao encerrar"""
        self.camera.release()
        self.led.off()
        self.display.fill(0)
        self.display.show()
        print("✅ Recursos liberados. Sistema encerrado.")

# =============== FUNÇÕES AUXILIARES ===============
def create_cat_profile():
    """Função para criar novo perfil de gato"""
    print("=== Criando novo perfil de gato ===")
    
    name = input("Nome do gato: ")
    portion_size = int(input("Porção por refeição (g): "))
    meals_per_day = int(input("Refeições por dia: "))
    
    meal_times = []
    for i in range(meals_per_day):
        time_str = input(f"Horário da refeição {i+1} (HH:MM): ")
        meal_times.append(time_str)
    
    compartment = int(input("Compartimento (0-3): "))
    diet_type = input("Tipo de dieta (adulto/light/premium): ")
    weight_goal = input("Meta de peso (manter/ganhar/perder): ")
    
    profile = {
        "portion_size": portion_size,
        "meals_per_day": meals_per_day,
        "meal_times": meal_times,
        "compartment": compartment,
        "diet_type": diet_type,
        "weight_goal": weight_goal
    }
    
    return name, profile

def train_cat_recognition():
    """Função para treinar reconhecimento de gatos"""
    print("=== Treinamento de IA para reconhecimento ===")
    print("1. Acesse teachablemachine.withgoogle.com")
    print("2. Escolha 'Image Project'")
    print("3. Crie uma classe para cada gato")
    print("4. Tire 30-50 fotos de cada gato em diferentes ângulos")
    print("5. Treine o modelo")
    print("6. Baixe como 'Keras' (.h5)")
    print("7. Renomeie para 'cat_recognition_model.h5'")
    print("8. Crie arquivo 'cat_labels.txt' com nomes dos gatos")

# =============== PROGRAMA PRINCIPAL ===============
if __name__ == "__main__":
    print("🐱 Comedouro Inteligente para Gatos v1.0")
    print("========================================")
    
    try:
        feeder = SmartFeeder()
        feeder.run()
    except Exception as e:
        print(f"💥 Erro fatal: {e}")
        import traceback
        traceback.print_exc()

Interface Web Simples

Servidor web para monitoramento:

Ver código completo
from flask import Flask, render_template, jsonify, request
import json
from datetime import datetime

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('dashboard.html')

@app.route('/api/status')
def get_status():
    """Retorna status atual do sistema"""
    try:
        with open('feeding_history.json', 'r') as f:
            history = json.load(f)
        
        with open('cat_profiles.json', 'r') as f:
            profiles = json.load(f)
        
        today = datetime.now().strftime('%Y-%m-%d')
        today_feedings = history.get(today, {})
        
        return jsonify({
            'status': 'online',
            'cats': list(profiles.keys()),
            'today_feedings': today_feedings,
            'profiles': profiles
        })
    except:
        return jsonify({'status': 'error'})

@app.route('/api/feed/<cat_name>')
def manual_feed(cat_name):
    """Alimentação manual via web"""
    # Aqui você adicionaria comando para alimentar manualmente
    return jsonify({'status': 'feeding', 'cat': cat_name})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

Template HTML (dashboard.html):

Ver código completo
<!DOCTYPE html>
<html>
<head>
    <title>🐱 Comedouro Smart</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        body {
            font-family: Arial, sans-serif;
            background: linear-gradient(135deg, #FF6B6B, #4ECDC4);
            color: white;
            margin: 0;
            padding: 20px;
        }
        .container {
            max-width: 800px;
            margin: 0 auto;
            background: rgba(0,0,0,0.3);
            padding: 30px;
            border-radius: 20px;
            backdrop-filter: blur(10px
    </>);
        }
        .cat-card {
            background: rgba(255,255,255,0.1);
            margin: 15px 0;
            padding: 20px;
            border-radius: 15px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .cat-info h3 { margin: 0; font-size: 1.5em; }
        .cat-stats { font-size: 0.9em; opacity: 0.8; }
        .feed-btn {
            background: linear-gradient(45deg, #FF6B6B, #FF8E8E);
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 25px;
            cursor: pointer;
            transition: transform 0.3s;
        }
        .feed-btn:hover { transform: scale(1.05); }
        .stats-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 15px;
            margin: 20px 0;
        }
        .stat-card {
            background: rgba(255,255,255,0.1);
            padding: 15px;
            border-radius: 10px;
            text-align: center;
        }
        .stat-value { font-size: 2em; font-weight: bold; }
        .stat-label { font-size: 0.9em; opacity: 0.8; }
    </style>
</head>
<body>
    <div class="container">
        <h1>🐱 Comedouro Inteligente</h1>
        
        <div class="stats-grid">
            <div class="stat-card">
                <div class="stat-value" id="totalCats">--</div>
                <div class="stat-label">Gatos cadastrados</div>
            </div>
            <div class="stat-card">
                <div class="stat-value" id="todayFeedings">--</div>
                <div class="stat-label">Refeições hoje</div>
            </div>
            <div class="stat-card">
                <div class="stat-value" id="systemStatus">--</div>
                <div class="stat-label">Status do sistema</div>
            </div>
        </div>
        
        <div id="catsList"></div>
    </div>

    <script>
        function updateDashboard() {
            fetch('/api/status')
                .then(response => response.json())
                .then(data => {
                    document.getElementById('totalCats').textContent = data.cats.length;
                    document.getElementById('systemStatus').textContent = data.status;
                    
                    // Contar refeições de hoje
                    let totalFeedings = 0;
                    Object.values(data.today_feedings).forEach(catFeedings => {
                        totalFeedings += catFeedings.length;
                    });
                    document.getElementById('todayFeedings').textContent = totalFeedings;
                    
                    // Mostrar gatos
                    const catsList = document.getElementById('catsList');
                    catsList.innerHTML = '';
                    
                    data.cats.forEach(catName => {
                        const profile = data.profiles[catName];
                        const todayMeals = data.today_feedings[catName] || [];
                        
                        const catCard = document.createElement('div');
                        catCard.className = 'cat-card';
                        catCard.innerHTML = `
                            <div class="cat-info">
                                <h3>🐱 ${catName}</h3>
                                <div class="cat-stats">
                                    Porção: ${profile.portion_size}g | 
                                    Refeições hoje: ${todayMeals.length}/${profile.meals_per_day} |
                                    Dieta: ${profile.diet_type}
                                </div>
                            </div>
                            <button class="feed-btn" onclick="feedCat('${catName}')">
                                🍽️ Alimentar
                            </button>
                        `;
                        catsList.appendChild(catCard);
                    });
                })
                .catch(error => {
                    document.getElementById('systemStatus').textContent = 'Erro';
                });
        }
        
        function feedCat(catName) {
            fetch(`/api/feed/${catName}`)
                .then(response => response.json())
                .then(data => {
                    alert(`Alimentando ${catName}...`);
                    setTimeout(updateDashboard, 2000);
                });
        }
        
        // Atualizar a cada 10 segundos
        setInterval(updateDashboard, 10000);
        updateDashboard();
    </script>
</body>
</html>

Montagem Física Detalhada

Estrutura principal:

Vista Superior:

┌─────────────────────────────────────┐
│ [CAM]              [DISPLAY]        │
│                                     │
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐     │
│ │ A   │ │ B   │ │ C   │ │ D   │     │ ← Compartimentos
│ │🥫   │ │🥫   │ │🥫   │ │🥫   │     │
│ └─────┘ └─────┘ └─────┘ └─────┘     │
│    ↓       ↓       ↓       ↓        │
│ ┌─────────────────────────────────┐ │
│ │         Área de distribuição    │ │
│ └─────────────────────────────────┘ │
│                                     │
│    🍽️ Tigela 1      🍽️ Tigela 2      │
│     [PESO]                          │
│                                     │
│ [Eletrônica] [Fonte]               │
└─────────────────────────────────────┘

Mecanismo de distribuição:

Vista Lateral de um Compartimento:

    🥫 Ração
    ┌─────────┐
    │         │
    │    🌾    │ ← Ração armazenada
    │    🌾    │
    └─────┬───┘
          │
    ┌─────▼─────┐ ← Servo motor com "porta"
    │ [SERVO]   │
    └─────┬─────┘
          │
     ┌────▼────┐ ← Funil direcionador
     │  /\_/\  │
     └─────────┘
          │
          ▼
        🍽️ Tigela

Lista de cortes MDF:

  • Base principal: 40x30cm
  • Divisórias compartimentos: 4x (8x8x10cm)
  • Suporte câmera: 10x5cm
  • Caixa eletrônica: 15x10x8cm

Configuração Passo a Passo

1. Preparação do Raspberry Pi:

Ver código completo
# Atualizar sistema
sudo apt update && sudo apt upgrade -y

# Instalar dependências
sudo apt install python3-pip python3-opencv -y
pip3 install tensorflow gpiozero adafruit-circuitpython-ssd1306
pip3 install flask pillow hx711 RPi.GPIO

# Habilitar câmera
sudo raspi-config
# Interface Options > Camera > Enable

# Reiniciar
sudo reboot

2. Treinamento da IA:

Ver código completo
# Script para capturar fotos de treinamento
import cv2
import os

def capture_training_photos(cat_name, num_photos=50):
    """Captura fotos para treinar a IA"""
    camera = cv2.VideoCapture(0)
    
    # Criar pasta para o gato
    os.makedirs(f'training_data/{cat_name}', exist_ok=True)
    
    print(f"Capturando fotos do {cat_name}...")
    print("Pressione ESPAÇO para tirar foto, ESC para sair")
    
    photo_count = 0
    
    while photo_count < num_photos:
        ret, frame = camera.read()
        if not ret:
            break
        
        cv2.imshow(f'Treinamento - {cat_name}', frame)
        key = cv2.waitKey(1) & 0xFF
        
        if key == ord(' '):  # Espaço para capturar
            filename = f'training_data/{cat_name}/photo_{photo_count:03d}.jpg'
            cv2.imwrite(filename, frame)
            photo_count += 1
            print(f"Foto {photo_count}/{num_photos} capturada!")
            
        elif key == 27:  # ESC para sair
            break
    
    camera.release()
    cv2.destroyAllWindows()
    print(f"✅ {photo_count} fotos capturadas para {cat_name}")

# Usar assim:
capture_training_photos("Mimi", 50)
capture_training_photos("Felix", 50)

3. Calibração da célula de carga:

Ver código completo
def calibrate_scale_interactive():
    """Calibração interativa da balança"""
    from hx711 import HX711
    
    scale = HX711(5, 6)  # DT, SCK pins
    
    print("=== Calibração da Balança ===")
    print("1. Remova qualquer peso da balança")
    input("Pressione ENTER quando pronto...")
    
    scale.reset()
    scale.tare()
    print("✅ Zero definido!")
    
    print("2. Coloque um peso conhecido (ex: 100g)")
    known_weight = float(input("Digite o peso em gramas: "))
    input("Pressione ENTER quando o peso estiver na balança...")
    
    reading = scale.get_weight(10)
    ratio = reading / known_weight
    scale.set_scale_ratio(ratio)
    
    print(f"✅ Calibração concluída! Ratio: {ratio}")
    
    # Teste
    print("3. Teste com diferentes pesos")
    for _ in range(5):
        weight = scale.get_weight(5)
        print(f"Peso atual: {weight:.1f}g")
        time.sleep(2)

calibrate_scale_interactive()

Recursos Avançados

1. Análise nutricional:

Ver código completo
class NutritionalAnalyzer:
    def __init__(self):
        self.load_food_database()
    
    def load_food_database(self):
        """Carrega base de dados nutricionais"""
        self.food_db = {
            "adulto": {"calories_per_g": 3.5, "protein": 32, "fat": 12},
            "light": {"calories_per_g": 3.0, "protein": 35, "fat": 8},
            "premium": {"calories_per_g": 4.0, "protein": 38, "fat": 15}
        }
    
    def calculate_daily_nutrition(self, cat_name, date):
        """Calcula nutrição diária do gato"""
        # Buscar histórico do dia
        with open('feeding_history.json', 'r') as f:
            history = json.load(f)
        
        if date not in history or cat_name not in history[date]:
            return None
        
        # Calcular totais
        total_food = sum(meal['actual_weight'] for meal in history[date][cat_name])
        
        # Buscar tipo de ração do gato
        with open('cat_profiles.json', 'r') as f:
            profiles = json.load(f)
        
        diet_type = profiles[cat_name]['diet_type']
        nutrition = self.food_db[diet_type]
        
        return {
            'total_food_g': total_food,
            'total_calories': total_food * nutrition['calories_per_g'],
            'protein_g': total_food * (nutrition['protein'] / 100),
            'fat_g': total_food * (nutrition['fat'] / 100)
        }

2. Integração com veterinário:

Ver código completo
def generate_health_report(cat_name, days=30):
    """Gera relatório de saúde para o veterinário"""
    with open('feeding_history.json', 'r') as f:
        history = json.load(f)
    
    # Analisar últimos 30 dias
    from datetime import datetime, timedelta
    
    end_date = datetime.now()
    start_date = end_date - timedelta(days=days)
    
    report = {
        'cat_name': cat_name,
        'period': f"{start_date.strftime('%d/%m/%Y')} - {end_date.strftime('%d/%m/%Y')}",
        'daily_consumption': [],
        'feeding_regularity': [],
        'appetite_changes': []
    }
    
    # Analisar cada dia
    current_date = start_date
    while current_date <= end_date:
        date_str = current_date.strftime('%Y-%m-%d')
        
        if date_str in history and cat_name in history[date_str]:
            daily_meals = history[date_str][cat_name]
            daily_total = sum(meal['actual_weight'] for meal in daily_meals)
            report['daily_consumption'].append({
                'date': date_str,
                'total_g': daily_total,
                'meals_count': len(daily_meals)
            })
        
        current_date += timedelta(days=1)
    
    # Detectar mudanças no apetite
    if len(report['daily_consumption']) > 7:
        recent_avg = sum(day['total_g'] for day in report['daily_consumption'][-7:]) / 7
        older_avg = sum(day['total_g'] for day in report['daily_consumption'][:7]) / 7
        
        change_percent = ((recent_avg - older_avg) / older_avg) * 100
        
        if abs(change_percent) > 20:
            report['appetite_changes'].append({
                'type': 'increase' if change_percent > 0 else 'decrease',
                'percentage': abs(change_percent),
                'recommendation': 'Consultar veterinário' if abs(change_percent) > 30 else 'Monitorar'
            })
    
    return report

3. Integração com outros dispositivos:

Ver código completo
def integrate_with_smart_home():
    """Integração com sistemas de casa inteligente"""
    import paho.mqtt.client as mqtt
    
    # Configurar MQTT para Home Assistant
    client = mqtt.Client()
    client.connect("localhost", 1883, 60)
    
    def publish_feeding_event(cat_name, portion):
        """Publica evento de alimentação no MQTT"""
        topic = f"pet_feeder/{cat_name}/fed"
        payload = json.dumps({
            'timestamp': datetime.now().isoformat(),
            'cat_name': cat_name,
            'portion_g': portion,
            'device': 'smart_feeder'
        })
        client.publish(topic, payload)
    
    def publish_low_food_alert(compartment):
        """Alerta de ração baixa"""
        topic = "pet_feeder/alerts/low_food"
        payload = json.dumps({
            'compartment': compartment,
            'timestamp': datetime.now().isoformat(),
            'message': f'Ração baixa no compartimento {compartment}'
        })
        client.publish(topic, payload)

Solução de Problemas

Problemas comuns:

IA não reconhece gatos:

  • Verificar iluminação: Precisa de luz adequada
  • Retreinar modelo: Adicionar mais fotos variadas
  • Ajustar confiança: Diminuir threshold de 0.75 para 0.6
  • Limpar lente: Câmera suja afeta reconhecimento

Servo não abre/fecha:

  • Verificar alimentação: Servos precisam de 5V estável
  • Testar manualmente: servo.max() e servo.min()
  • Calibrar posições: Ajustar ângulos no código
  • Verificar obstruções: Ração pode estar travando

Balança lendo errado:

  • Recalibrar: Usar peso conhecido
  • Verificar conexões: HX711 é sensível
  • Estabilizar base: Vibração afeta leitura
  • Aguardar estabilização: 2-3 segundos após movimento

Gatos não usam o comedouro:

  • Período de adaptação: 1-2 semanas é normal
  • Misturar com antigo: Transição gradual
  • Verificar altura: Deve estar confortável
  • Limpar regularmente: Gatos são sensíveis a odores

Código de diagnóstico:

Ver código completo
def run_system_diagnostics():
    """Diagnóstico completo do sistema"""
    print("=== DIAGNÓSTICO COMEDOURO ===")
    
    # Teste da câmera
    camera = cv2.VideoCapture(0)
    ret, frame = camera.read()
    print(f"Câmera: {'✅ OK' if ret else '❌ ERRO'}")
    camera.release()
    
    # Teste dos servos
    from gpiozero import Servo
    servos_ok = 0
    for i, pin in enumerate([18, 19, 20, 21]):
        try:
            servo = Servo(pin)
            servo.max()
            time.sleep(0.5)
            servo.min()
            servos_ok += 1
            print(f"Servo {i}: ✅ OK")
        except:
            print(f"Servo {i}: ❌ ERRO")
    
    # Teste da balança
    try:
        from hx711 import HX711
        scale = HX711(5, 6)
        weight = scale.get_weight(3)
        print(f"Balança: ✅ OK ({weight:.1f}g)")
    except:
        print("Balança: ❌ ERRO")
    
    # Teste do display
    try:
        import board
        import busio
        import adafruit_ssd1306
        i2c = busio.I2C(board.SCL, board.SDA)
        display = adafruit_ssd1306.SSD1306_I2C(128, 64, i2c)
        display.fill(1)
        display.show()
        time.sleep(1)
        display.fill(0)
        display.show()
        print("Display: ✅ OK")
    except:
        print("Display: ❌ ERRO")
    
    print("==============================")
    return servos_ok == 4

run_system_diagnostics()

Manutenção e Cuidados

Rotina de manutenção:

Diariamente:

  • Verificar nível de ração nos compartimentos
  • Limpar tigelas dos gatos
  • Conferir funcionamento via app

Semanalmente:

  • Limpeza completa dos compartimentos
  • Verificação dos servos (lubrificar se necessário)
  • Backup do histórico de alimentação
  • Limpeza da lente da câmera

Mensalmente:

  • Calibração da balança
  • Atualização do software
  • Verificação das conexões elétricas
  • Análise do histórico nutricional

Trimestralmente:

  • Retreinamento da IA (novas fotos)
  • Limpeza profunda da estrutura
  • Verificação do desgaste dos servos
  • Backup completo do sistema

Comparação Final dos 3 Projetos

AspectoPorta PetBebedouroComedouro
DificuldadeIniciante/Inter.Inter./AvançadoIniciante/Inter.
Tempo4-6 horas8-12 horas4-6 horas
CustoR$ 600-900R$ 400-700R$ 300-500
IA ComplexidadeSimplesBásicaIntermediária
RiscosBaixosÁgua + energiaBaixíssimos
Impacto SaúdeExercícioHidrataçãoNutrição
ManutençãoBaixaMédiaBaixa
ROIAltoMuito AltoAltíssimo

Conclusão

O Comedouro Inteligente para Gatos é o projeto perfeito para completar sua jornada em automação pet tech! Com investimento de apenas R$ 300-500, você cria um sistema que:

Benefícios Imediatos:

  • Controle total da alimentação de cada gato
  • Prevenção da obesidade felina
  • Paz entre gatos (cada um come sua porção)
  • Monitoramento nutricional 24/7

Por que é ideal para iniciantes:

  • Sem riscos elétricos (apenas 5V)
  • Sem sistemas hidráulicos complexos
  • IA acessível (Teachable Machine)
  • Resultados imediatos e visíveis

Evolução natural:

  • Mais simples que o bebedouro (sem água)
  • Mais útil que a porta (impacto na saúde)
  • Base para projetos futuros (alimentador + bebedouro integrados)

Próximos passos:

  • Comece com 1 gato para testar
  • Expanda para múltiplos gatos conforme confiança
  • Integre com outros projetos da série
  • Considere versão comercial (potencial de negócio!)

Este projeto fecha com chave de ouro sua trilogia DIY pet tech. Seus gatos terão nutrição controlada, você terá tranquilidade, e ainda aprenderá conceitos valiosos de IA e automação. É o investimento perfeito em tecnologia, saúde e felicidade felina!

Série Completa: Automação Inteligente para Pets

Este comedouro faz parte de uma série completa de projetos para criar um ecossistema inteligente para seu pet. Confira os outros projetos:

💧 Bebedouro Automático Inteligente

Sistema completo que mantém seu pet sempre hidratado com água fresca, monitoramento remoto e reabastecimento automático.

  • • Reabastecimento automático
  • • Monitoramento via app
  • • Controle de consumo
  • • R$ 400-700 | 8-12 horas
Ver Projeto Completo →

🚪 Porta Inteligente com IA

Porta automática que reconhece especificamente seu pet, permitindo entrada e saída seguras e controladas.

  • • Reconhecimento visual do pet
  • • Abertura automática
  • • Controle de horários
  • • R$ 600-900 | 4-6 horas
Ver Projeto Completo →

🎯 Por que começar pelo comedouro?

Mais fácil e seguro: Sem riscos elétricos (apenas 5V), sem sistemas hidráulicos, e resultados imediatos. É o projeto perfeito para desenvolver confiança antes de partir para os mais complexos bebedouro e porta automática.