// Lorenzo Nunes Bunecker
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Servo.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// Configuração do Display I2C 20x4
LiquidCrystal_I2C lcd(0x27, 20, 4);
// Pinos dos botões - lógica invertida (0=solto, 1=apertado)
#define BOTAO_CIMA 37
#define BOTAO_ESQUERDA 39
#define BOTAO_BAIXO 41
#define BOTAO_DIREITA 43
#define BOTAO_OK 5
#define SENSOR_NIVEL 6
#define DIR_MOTOR 3
#define STEP_MOTOR 4
#define SERVO_PIN 2
#define ENABLE_MOTOR 8
#define PINO_ONEWIRE 33
#define LED_AZUL 12
#define LED_VERMELHO 11
#define LED_VERDE 13
OneWire oneWire(PINO_ONEWIRE);
DallasTemperature sensors(&oneWire);
Servo servoMotor;
// Estrutura para configuração das etapas
struct ConfigEtapa {
float temperatura;
unsigned long tempo;
bool adicionaIngrediente;
int ingrediente;
int posicaoMotor; // 250, 500 ou 750
};
struct SistemaConfig {
ConfigEtapa etapa1;
ConfigEtapa etapa2;
ConfigEtapa etapa3;
ConfigEtapa etapa4;
float histerese;
} config;
// Variáveis de menu
int menuAtual = 0;
int cursorPos = 0;
bool editando = false;
// Variáveis de processo
int posicaoAtualMotor = 0;
bool processoIniciado = false;
int etapaProcesso = 0;
int subEtapa = 0;
unsigned long etapaStartMillis = 0;
unsigned long ultimoSegundo = 9999;
bool temperaturaAtingida = false;
float temperatura = 0;
bool ledVermelhoLigado = false;
float temperaturaReferenciaAtual = 0;
const unsigned long DELAY_PASSO_DEVAGAR = 800;
void setup() {
Serial.begin(115200);
// Inicializar display
lcd.init();
lcd.backlight();
lcd.clear();
// Configurar pinos dos botões - SEM PULLUP
pinMode(BOTAO_CIMA, INPUT);
pinMode(BOTAO_ESQUERDA, INPUT);
pinMode(BOTAO_BAIXO, INPUT);
pinMode(BOTAO_DIREITA, INPUT);
pinMode(BOTAO_OK, INPUT);
// Configurar outros pinos
pinMode(LED_AZUL, OUTPUT);
pinMode(LED_VERMELHO, OUTPUT);
pinMode(LED_VERDE, OUTPUT);
pinMode(SENSOR_NIVEL, INPUT);
pinMode(DIR_MOTOR, OUTPUT);
pinMode(STEP_MOTOR, OUTPUT);
pinMode(ENABLE_MOTOR, OUTPUT);
digitalWrite(STEP_MOTOR, LOW);
digitalWrite(DIR_MOTOR, LOW);
habilitarMotor(false);
servoMotor.attach(SERVO_PIN);
servoMotor.write(0);
sensors.begin();
// Configurações padrão
config.etapa1 = {60.0, 10000, true, 1, 250};
config.etapa2 = {70.0, 12000, true, 2, 500};
config.etapa3 = {100.0, 5000, true, 3, 750};
config.etapa4 = {0.0, 5000, false, 0, 0};
config.histerese = 2.0;
mostrarMenuInicio();
}
void loop() {
if (!processoIniciado) {
gerenciarMenu();
} else {
executarProcesso();
}
}
bool botaoPressionado(int pin) {
// Ler o botão com debounce
int leitura = digitalRead(pin);
if (leitura == HIGH) {
delay(50); // Debounce
// Confirmar que ainda está pressionado após o debounce
if (digitalRead(pin) == HIGH) {
// Esperar até que o botão seja solto
while(digitalRead(pin) == HIGH) {
delay(10);
}
return true;
}
}
return false;
}
void gerenciarMenu() {
// Usar função com debounce para cada botão
bool cima = botaoPressionado(BOTAO_CIMA);
bool esquerda = botaoPressionado(BOTAO_ESQUERDA);
bool baixo = botaoPressionado(BOTAO_BAIXO);
bool direita = botaoPressionado(BOTAO_DIREITA);
bool ok = botaoPressionado(BOTAO_OK);
if (editando) {
if (ok) {
editando = false;
mostrarMenuAtual();
} else if (cima) {
cursorPos--;
if (cursorPos < 0) cursorPos = 3;
mostrarMenuEdicao();
} else if (baixo) {
cursorPos++;
if (cursorPos > 3) cursorPos = 0;
mostrarMenuEdicao();
} else if (direita) {
incrementarValor();
mostrarMenuEdicao();
} else if (esquerda) {
decrementarValor();
mostrarMenuEdicao();
}
} else {
if (ok) {
if (menuAtual == 0) {
// Inicia processo direto
processoIniciado = true;
etapaProcesso = 0;
lcd.clear();
lcd.print("Iniciando processo");
temperaturaAtingida = false;
subEtapa = 0;
delay(1000);
return;
} else {
editando = true;
cursorPos = 0;
mostrarMenuEdicao();
}
} else if (cima) {
menuAtual--;
if (menuAtual < 0) menuAtual = 5;
mostrarMenuAtual();
} else if (baixo) {
menuAtual++;
if (menuAtual > 5) menuAtual = 0;
mostrarMenuAtual();
}
}
}
void mostrarMenuInicio() {
lcd.clear();
lcd.print("> Iniciar Processo");
lcd.setCursor(0, 1);
lcd.print(" Config. Etapa 1");
lcd.setCursor(0, 2);
lcd.print(" Config. Etapa 2");
lcd.setCursor(0, 3);
lcd.print(" Config. Etapa 3");
menuAtual = 0;
}
void mostrarMenuAtual() {
lcd.clear();
switch(menuAtual) {
case 0:
lcd.print("> Iniciar Processo");
lcd.setCursor(0, 1);
lcd.print(" Config. Etapa 1");
lcd.setCursor(0, 2);
lcd.print(" Config. Etapa 2");
lcd.setCursor(0, 3);
lcd.print(" Config. Etapa 3");
break;
case 1:
lcd.print(" Iniciar Processo");
lcd.setCursor(0, 1);
lcd.print("> Config. Etapa 1");
lcd.setCursor(0, 2);
lcd.print(" Config. Etapa 2");
lcd.setCursor(0, 3);
lcd.print(" Config. Etapa 3");
break;
case 2:
lcd.print(" Iniciar Processo");
lcd.setCursor(0, 1);
lcd.print(" Config. Etapa 1");
lcd.setCursor(0, 2);
lcd.print("> Config. Etapa 2");
lcd.setCursor(0, 3);
lcd.print(" Config. Etapa 3");
break;
case 3:
lcd.print(" Iniciar Processo");
lcd.setCursor(0, 1);
lcd.print(" Config. Etapa 1");
lcd.setCursor(0, 2);
lcd.print(" Config. Etapa 2");
lcd.setCursor(0, 3);
lcd.print("> Config. Etapa 3");
break;
case 4:
lcd.print("> Config. Etapa 4");
lcd.setCursor(0, 1);
lcd.print(" Config.Histerese");
lcd.setCursor(0, 2);
lcd.print(" Voltar Inicio");
lcd.setCursor(0, 3);
lcd.print("");
break;
case 5:
lcd.print(" Config. Etapa 4");
lcd.setCursor(0, 1);
lcd.print("> Config.Histerese");
lcd.setCursor(0, 2);
lcd.print(" Voltar Inicio");
lcd.setCursor(0, 3);
lcd.print("");
break;
}
}
void mostrarMenuEdicao() {
lcd.clear();
switch(menuAtual) {
case 1: // Etapa 1
case 2: // Etapa 2
case 3: // Etapa 3
ConfigEtapa* etapa;
if (menuAtual == 1) etapa = &config.etapa1;
else if (menuAtual == 2) etapa = &config.etapa2;
else etapa = &config.etapa3;
switch(cursorPos) {
case 0:
lcd.print("ETAPA " + String(menuAtual) + " - TEMP");
lcd.setCursor(0, 1);
lcd.print("Valor: " + String(etapa->temperatura, 1) + " C");
lcd.setCursor(0, 2);
lcd.print("Direita: +0.5");
lcd.setCursor(0, 3);
lcd.print("Esquerda: -0.5");
break;
case 1:
lcd.print("ETAPA " + String(menuAtual) + " - TEMPO");
lcd.setCursor(0, 1);
lcd.print("Valor: " + String(etapa->tempo/1000) + " seg");
lcd.setCursor(0, 2);
lcd.print("Direita: +1s");
lcd.setCursor(0, 3);
lcd.print("Esquerda: -1s");
break;
case 2:
lcd.print("ETAPA " + String(menuAtual) + " - MOTOR");
lcd.setCursor(0, 1);
lcd.print("Posicao: " + String(etapa->posicaoMotor) + " passos");
lcd.setCursor(0, 2);
lcd.print("Direita: Proximo");
lcd.setCursor(0, 3);
lcd.print("Esquerda: Anterior");
break;
case 3:
lcd.print("ETAPA " + String(menuAtual) + " - INGRED");
lcd.setCursor(0, 1);
lcd.print("Add: " + String(etapa->adicionaIngrediente ? "SIM" : "NAO"));
lcd.setCursor(0, 2);
lcd.print("Ingred: " + String(etapa->ingrediente));
lcd.setCursor(0, 3);
lcd.print("Dir/Esq: Alterar");
break;
}
break;
case 5: // Histerese
lcd.print("CONFIGURAR HISTERESE");
lcd.setCursor(0, 1);
lcd.print("Valor: " + String(config.histerese, 1) + " C");
lcd.setCursor(0, 2);
lcd.print("Direita: +0.5");
lcd.setCursor(0, 3);
lcd.print("Esquerda: -0.5");
break;
}
}
void incrementarValor() {
switch(menuAtual) {
case 1:
case 2:
case 3:
ConfigEtapa* etapa;
if (menuAtual == 1) etapa = &config.etapa1;
else if (menuAtual == 2) etapa = &config.etapa2;
else etapa = &config.etapa3;
switch(cursorPos) {
case 0: etapa->temperatura += 0.5; break;
case 1: etapa->tempo += 1000; break;
case 2:
// Alternar entre 250, 500 e 750
if (etapa->posicaoMotor == 250) etapa->posicaoMotor = 500;
else if (etapa->posicaoMotor == 500) etapa->posicaoMotor = 750;
else etapa->posicaoMotor = 250;
break;
case 3:
etapa->adicionaIngrediente = !etapa->adicionaIngrediente;
break;
}
break;
case 5:
config.histerese += 0.5;
break;
}
}
void decrementarValor() {
switch(menuAtual) {
case 1:
case 2:
case 3:
ConfigEtapa* etapa;
if (menuAtual == 1) etapa = &config.etapa1;
else if (menuAtual == 2) etapa = &config.etapa2;
else etapa = &config.etapa3;
switch(cursorPos) {
case 0: etapa->temperatura = max(0.0, etapa->temperatura - 0.5); break;
case 1: etapa->tempo = max(1000, etapa->tempo - 1000); break;
case 2:
// Alternar entre 250, 500 e 750 (inverso)
if (etapa->posicaoMotor == 750) etapa->posicaoMotor = 500;
else if (etapa->posicaoMotor == 500) etapa->posicaoMotor = 250;
else etapa->posicaoMotor = 750;
break;
case 3:
etapa->adicionaIngrediente = !etapa->adicionaIngrediente;
break;
}
break;
case 5:
config.histerese = max(0.5, config.histerese - 0.5);
break;
}
}
void executarProcesso() {
temperatura = lerTemperatura();
// ✅ ATUALIZADO: Controlar LED vermelho ANTES de tudo
controlarLedVermelho();
// Verificar se botão OK foi pressionado para cancelar
if (botaoPressionado(BOTAO_OK)) {
lcd.clear();
lcd.print("Processo cancelado");
processoIniciado = false;
etapaProcesso = 0;
subEtapa = 0;
temperaturaAtingida = false;
digitalWrite(LED_AZUL, LOW);
digitalWrite(LED_VERMELHO, LOW);
digitalWrite(LED_VERDE, LOW);
delay(2000);
mostrarMenuInicio();
return;
}
switch(etapaProcesso) {
case 0: // Aguardar nível
executarEtapaEnchimento();
break;
case 1: // Etapa 1
executarEtapaComTempo(1, config.etapa1);
break;
case 2: // Etapa 2
executarEtapaComTempo(2, config.etapa2);
break;
case 3: // Etapa 3
executarEtapaComTempo(3, config.etapa3);
break;
case 4: // Etapa 4
executarEtapaFinal();
break;
}
}
void executarEtapaEnchimento() {
lcd.clear();
lcd.print("ETAPA 0: ENCHIMENTO");
lcd.setCursor(0, 1);
lcd.print("Aguardando nivel...");
lcd.setCursor(0, 2);
lcd.print("Temp: " + String(temperatura, 1) + " C");
lcd.setCursor(0, 3);
lcd.print("OK para cancelar");
digitalWrite(LED_AZUL, HIGH);
digitalWrite(LED_VERMELHO, LOW);
digitalWrite(LED_VERDE, LOW);
if (digitalRead(SENSOR_NIVEL) == HIGH) {
etapaProcesso = 1;
temperaturaReferenciaAtual = config.etapa1.temperatura;
// Desligar LED azul ao sair do enchimento
digitalWrite(LED_AZUL, LOW);
digitalWrite(LED_VERMELHO, HIGH); // Ligar vermelho para aquecimento
lcd.clear();
lcd.print("Nivel detectado!");
lcd.setCursor(0, 1);
lcd.print("Indo para Etapa 1");
delay(1000);
}
}
void executarEtapaComTempo(int numEtapa, ConfigEtapa configEtapa) {
lcd.clear();
lcd.print("ETAPA " + String(numEtapa));
lcd.setCursor(0, 1);
lcd.print("Temp: " + String(temperatura, 1) + "C/" + String(configEtapa.temperatura, 1) + "C");
if (!temperaturaAtingida) {
lcd.setCursor(0, 2);
lcd.print("Aquecendo...");
lcd.setCursor(0, 3);
lcd.print("LED: " + String(ledVermelhoLigado ? "LIGADO" : "DESLIGADO"));
if (temperatura >= configEtapa.temperatura) {
temperaturaAtingida = true;
// ✅ CORREÇÃO: O LED já foi desligado pela função controlarLedVermelho()
lcd.setCursor(0, 2);
lcd.print("Temp. atingida! ");
// Executar movimento do motor primeiro
if (configEtapa.adicionaIngrediente) {
executarMovimentoMotor(numEtapa, configEtapa);
}
// Iniciar contagem do tempo APÓS o movimento do motor
etapaStartMillis = millis();
subEtapa = 1; // Marcar que o movimento foi executado e tempo iniciado
}
} else {
// CONTROLE DE TEMPO APÓS ATINGIR TEMPERATURA E EXECUTAR MOTOR
unsigned long tempoDecorrido = millis() - etapaStartMillis;
unsigned long segundos = tempoDecorrido / 1000;
lcd.setCursor(0, 2);
if (configEtapa.adicionaIngrediente) {
lcd.print("Ingrediente adicionado");
} else {
lcd.print("Processando... ");
}
lcd.setCursor(0, 3);
lcd.print("Tempo: " + String(segundos) + "s/" + String(configEtapa.tempo/1000) + "s");
// Aguardar o tempo configurado antes de ir para próxima etapa
if (tempoDecorrido >= configEtapa.tempo) {
etapaProcesso++;
subEtapa = 0;
temperaturaAtingida = false;
// Atualizar referência de temperatura para próxima etapa
if (etapaProcesso == 2) temperaturaReferenciaAtual = config.etapa2.temperatura;
else if (etapaProcesso == 3) temperaturaReferenciaAtual = config.etapa3.temperatura;
lcd.clear();
lcd.print("Etapa " + String(numEtapa) + " concluida");
lcd.setCursor(0, 1);
lcd.print("Indo para Etapa " + String(etapaProcesso));
delay(2000);
}
}
}
void executarMovimentoMotor(int numEtapa, ConfigEtapa configEtapa) {
lcd.clear();
lcd.print("ETAPA " + String(numEtapa));
lcd.setCursor(0, 1);
lcd.print("Add ingrediente " + String(configEtapa.ingrediente));
lcd.setCursor(0, 2);
lcd.print("Movendo motor...");
lcd.setCursor(0, 3);
lcd.print("Aguarde...");
// Executar movimento do motor baseado na etapa
int posicaoConfigurada = configEtapa.posicaoMotor;
int passosRestantes = 1000 - posicaoConfigurada;
Serial.println("Iniciando movimento - Etapa " + String(numEtapa));
Serial.println("Posicao: " + String(posicaoConfigurada) + " passos");
moverMotor(posicaoConfigurada, true);
ativarServo();
moverMotor(passosRestantes, true);
desativarServo();
moverMotor(1000, false); // Voltar à posição inicial
Serial.println("Movimento concluido - Etapa " + String(numEtapa));
}
void executarEtapaFinal() {
lcd.clear();
lcd.print("ETAPA 4: FINAL");
lcd.setCursor(0, 1);
lcd.print("Aguardando...");
if (subEtapa == 0) {
etapaStartMillis = millis();
subEtapa = 1;
}
unsigned long segundos = (millis() - etapaStartMillis) / 1000;
lcd.setCursor(0, 2);
lcd.print("Temp: " + String(temperatura, 1) + " C");
lcd.setCursor(0, 3);
lcd.print("Tempo: " + String(segundos) + "s/" + String(config.etapa4.tempo/1000) + "s");
if (segundos >= config.etapa4.tempo/1000) {
lcd.clear();
lcd.print("PROCESSO FINALIZADO");
digitalWrite(LED_VERDE, HIGH);
digitalWrite(LED_VERMELHO, LOW);
digitalWrite(LED_AZUL, LOW);
delay(3000);
processoIniciado = false;
etapaProcesso = 0;
subEtapa = 0;
temperaturaAtingida = false;
mostrarMenuInicio();
}
}
float lerTemperatura() {
sensors.requestTemperatures();
float temp = sensors.getTempCByIndex(0);
return temp != DEVICE_DISCONNECTED_C ? temp : -127;
}
void controlarLedVermelho() {
bool deveEstarLigado = false;
if (etapaProcesso == 1 || etapaProcesso == 2 || etapaProcesso == 3) {
if (!temperaturaAtingida) {
// ✅ Antes de atingir: LED ligado
deveEstarLigado = true;
} else {
// ✅ Depois de atingir: LED desligado, só reacende se temperatura cair 2°C abaixo
if (temperatura < (temperaturaReferenciaAtual - config.histerese)) {
deveEstarLigado = true;
}
}
}
// ✅ Aplica o estado do LED imediatamente
if (deveEstarLigado != ledVermelhoLigado) {
digitalWrite(LED_VERMELHO, deveEstarLigado);
ledVermelhoLigado = deveEstarLigado;
// Debug no Serial
if (deveEstarLigado) {
Serial.println("LED Vermelho: LIGADO - Temp: " + String(temperatura) + "C");
} else {
Serial.println("LED Vermelho: DESLIGADO - Temp: " + String(temperatura) + "C");
}
}
}
void habilitarMotor(bool habilitar) {
digitalWrite(ENABLE_MOTOR, !habilitar);
}
void ativarServo() {
servoMotor.write(180);
delay(1000);
}
void desativarServo() {
servoMotor.write(0);
delay(1000);
}
void moverMotor(int passos, bool sentidoHorario) {
digitalWrite(DIR_MOTOR, sentidoHorario);
habilitarMotor(true);
Serial.println("Motor: " + String(passos) + " passos, sentido: " + String(sentidoHorario ? "Horario" : "Anti-horario"));
for(int i = 0; i < passos; i++) {
digitalWrite(STEP_MOTOR, HIGH);
delayMicroseconds(5);
digitalWrite(STEP_MOTOR, LOW);
delayMicroseconds(DELAY_PASSO_DEVAGAR);
if (sentidoHorario) {
posicaoAtualMotor++;
} else {
posicaoAtualMotor--;
}
// Debug a cada 100 passos
if (i % 100 == 0 && i > 0) {
Serial.println("Passo: " + String(i) + "/" + String(passos) + " - Posicao: " + String(posicaoAtualMotor));
}
}
habilitarMotor(false);
Serial.println("Motor concluido - Posicao final: " + String(posicaoAtualMotor));
}