#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
#include <OneWire.h> // Library for Temp sensor
#include <DallasTemperature.h> // Library for Temp conversion
#include <PID_v1.h>
#define ONE_WIRE_BUS A0
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress tempDeviceAddress;
int idle = 0;
int resolution = 12;
LiquidCrystal_I2C lcd(0x27, 20, 4); // Endereço do LCD
static int pinA = 2; // pino A do encoder
static int pinB = 3; // pino B do encoder
const int selectSwitch = 4; // pino do botão do encoder
const int RELAY = 5; //Relay
const int Sensor = 12; //Relay
const int motorPin = 8; // Defina o pino do motor
const int pumpPin = 10; // Defina o pino da bomba
const int buzzerPin = 13;
#define PWM_PIN 9 // Pino PWM
const int pinoSensorAgua = 7; // Pino do sensor de nível de água
// Variáveis de controle
unsigned long lastTempRequest = 0;
unsigned long delayInMillis = 750;
// Etapas do processo
enum Stage { RAMP_UP, PROTEASE, BETA, ALFA, MASH_OUT, SPARGE };
unsigned long motorTimer = 0; // Timer para o motor
unsigned long bombaTimer = 0; // Timer para a bomba
unsigned long currentMillis = 0;
// Armazena o tempo de início
unsigned long tempoInicial = millis();
static unsigned long timer = 0; // Timer para controle
// Variáveis de controle
int execMenu = 0;
unsigned long tempo = 0;
unsigned long timeCountDown = 0;
int seg = 0; // Contador de segundos
int min = 0; // Contador de minutos
// Variáveis para debounce e controle de estado do botão
bool buttonPressed = false;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 100; // Tempo de debounce
//Variáveis cronometro Crescente
unsigned long startTime = 0; // Variável para armazenar o tempo de início
unsigned long previousMillis = 0; // Armazena o último tempo em que o cronômetro foi atualizado
const long intervalo = 1000; // Intervalo de 1 segundo (1000 milissegundos)
unsigned long totalTime = 0; // Tempo total decorrido em segundos
int lastButtonState = HIGH; // Para debouncing
int lastEncoderState = HIGH; // Inicializa o estado anterior como HIGH
int phaseIndex = 0;
bool isStarted = false; // Estado inicial do sistema
bool isPaused = false; // Estado de pausa
bool isRunning = false;
// Variaveis cronometro decrescente
int currentTimeSeconds = 0; // Tempo restante em segundos
int currentTemp = 0;
int elapsedSeconds = 0;
// Protótipo da função genérica
void ajustarParametro(const char* nome, int& valor, int coluna, int linha, int limiteMin, int limiteMax);
// Variável de controle de pulsação para diferentes valores
bool pulsou[15] = {false}; // Para até 15 valores que precisam ser controlados
byte grau[8] = {0b00110, 0b01001,0b01001, 0b00110, 0b00000, 0b00000,0b00000, 0b00000
};
//lcd.write(byte(0));: Exibe o caractere na tela.
byte timeSymbol[8] = {B00100,B01010,B10001,B00010,B01010,B00100,B00000,B00000};
//lcd.write(byte(1));: Exibe o caractere na tela.
byte termometro[8] = {B00100,B00100,B00100,B00100,B01110,B11111,B01110,B00100};
//lcd.createChar(0, termometro);: Cria o caractere na posição 0 da memória do LCD.
//lcd.write(byte(2));: Exibe o caractere na tela.
byte powerSymbol[8] = {B00100,B01110,B11111,B11111,B11111,B01110,B00100,B00010};
//lcd.write(byte(3));: Exibe o caractere na tela.
//lcd.write(byte(4));: Exibe o caractere na tela.
byte bombaSymbol[8] = {0b00000,0b00100,0b01110,0b11111,0b01110,0b00100,0b00000,0b00000};
//lcd.write(byte(5));: Exibe o caractere na tela.
byte motorSymbol[8] = { 0b00111, 0b01001,0b01111,0b01001, 0b01001,0b00111, 0b00000,0b00000
};
// Definição do caractere personalizado para a flecha esquerda
byte ArrowLeft[8] = {0b00100, 0b00110, 0b11111,0b00110,0b00100,0b00000,0b00000,0b00000
};
//lcd.write(byte(6));: Exibe o caractere na tela.
int estadoEnter = 0;
volatile byte aFlag = 0;
volatile byte bFlag = 0;
// has arrived at a detent (opposite direction to when aFlag is set)
int encoderPos = 0;
volatile byte oldEncPos = 0;
volatile byte reading = 0;
// Menus principais e seus submenus
const char* mainMenu[] = {"Configur","Monitorar", "Cadastrar", "Executar"};
const int mainMenuSize = sizeof(mainMenu) / sizeof(mainMenu[0]);
const char* settingsMenu[] = {"PID", "Fuzzy", "Sensor", "Back"};
const int settingsMenuSize = sizeof(settingsMenu) / sizeof(settingsMenu[0]);
const char* monitorarMenu[] = {"Resistencia: ", "Recipe: ","Pump" ,"Back"};
const int monitorarMenuSize = sizeof(monitorarMenu) / sizeof(monitorarMenu[0]);
int currentMenu = 0; // Menu atual
int currentSubMenu = 0; // Índice do submenu atual, se necessário
int currentIndex = 0; // Índice do item atualmente selecionado
// Variáveis globais para controlar o cronômetro
unsigned long startMillis = 0; // Tempo de início da fase
int phaseDuration = 0; // Duração da fase em segundos
// Declaração global para controle de ajuste de tempo
bool ajusteCompletado = false; // Flag que indica se o ajuste foi completado
int Kp = 50; // Valor inicial de 10 a 50
float Ki = 0.5; //valor inicial 0.0 a 1.0
float Kd = 1.0; //valor inicial 0.0 a 1.0
float fuzzyStart = 0.0;
float fuzzyAmplitude = 0.0;
int powerStep = 0;
int powerRamp = 0;
float sensorF1 = 0.0;
float sensorF2 = 0.0;
int menuValue = 0;
int seconds = 59;
int secondsCount;
unsigned long time_countdown = 0;
static bool mensagemFPExibida = false; // Variável para controlar se a mensagem do pH já foi exibida
static bool mensagemTIExibida = false;
static bool mensagemTMExibida = false;
static bool mensagemAAExibida = false;
static bool mensagemPHExibida = false;
bool isAACheckPaused = false;
bool isFPCheckPaused = false; // Novo sinalizador para pausa do teste de pH
bool isTICheckPaused = false; // Novo sinalizador para pausa do teste de iodo
bool isTMCheckPaused = false; // Novo sinalizador para pausa transf do mosto
bool isPHCheckPaused = false; // Novo sinalizador para pausa transf do mosto
unsigned long lastButtonPress = 0; // Para controle de debounce do botão
// Índice da etapa atual
bool emExecucao = false; // Flag de execução
bool isPause = false; // Flag de pausa
unsigned long inicioEtapa; // Marca o início da etapa
int tempoRestante; // Tempo restante da etapa atual
// Variáveis do botão
unsigned long buttonPressTime = 0; // Tempo em que o botão foi pressionado
bool buttonHeld = false;
// Variáveis globais
float Temperature, currentTemperature;
// Parâmetros Fuzzy
float integral = 0, previousError = 0, error;
// Potências para Step e Ramp
int menuIndex = 0; // Índice do menu principal
int subMenuIndex = 0; // Índice do submenu
// Definições das receitas
struct Phase {
float temperature;
int time;
};
// Definição das fases e suas temperaturas/tempos
#define NUM_PHASES 6
const char* phaseNames[NUM_PHASES] = {"Fitase", "Protease", "Beta Amilase", "Alfa Amilase", "MashOut","Sparge"};
#define RECIPE_NAME_LENGTH 10 // Define o tamanho máximo do nome da receita
struct Recipe {
char name[RECIPE_NAME_LENGTH]; // Nome da receita
Phase phases[NUM_PHASES]; // Fases da receita
};
#define NUM_RECIPES 9
Recipe recipes[NUM_RECIPES]; // Armazena até NUM_RECIPES receitas
const int EEPROM_START_ADDRESS = 0; // Endereço inicial na EEPROM
// Exemplo de como chamar a função corretamente com o índice da receita
int selectedRecipeIndex = 0; // Para salvar a primeira receita (Bier1)
// Variáveis PID
double Setpoint, Input, Output;
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);
void setup() {
lcd.begin(20,4);
lcd.backlight();
sensors.begin();
Serial.begin(9600);
sensors.getAddress(tempDeviceAddress, 0);
sensors.setResolution(tempDeviceAddress, resolution);
sensors.setWaitForConversion(false);
sensors.requestTemperatures();
delayInMillis = 750 / (1 << (12 - resolution));
lastTempRequest = millis();
pinMode(selectSwitch, INPUT_PULLUP);
pinMode(pinA, INPUT);
pinMode(pinB, INPUT);
pinMode(RELAY, OUTPUT);
pinMode(motorPin, OUTPUT);
pinMode(pumpPin, OUTPUT);
digitalWrite(motorPin, LOW);
digitalWrite(pumpPin, LOW);
attachInterrupt(0, PinA, RISING);
attachInterrupt(1, PinB, RISING);
myPID.SetMode(AUTOMATIC);
// Carregar o valor da configuração da EEPROM
// Inicializa as receitas com valores padrão
lcd.setCursor (4, 0);
lcd.print("Welcome....");
lcd.setCursor (6, 2);
lcd.print("BREWBEER!");
delay(2500);
lcd.clear();
// Chama a função para verificar e corrigir os valores das variáveis
// Cria os caracteres personalizados
lcd.createChar(0, grau);
lcd.createChar(1, termometro);
lcd.createChar(2, timeSymbol);
lcd.createChar(3, powerSymbol);
lcd.createChar(4, bombaSymbol);
lcd.createChar(5, motorSymbol);
lcd.createChar(6, ArrowLeft);
int selectedRecipeIndex = 0; // Inicializando com o valor da receita 1 (índice 0)
initializeRecipes();
loadRecipeFromEEPROM(selectedRecipeIndex); // Carrega a receita da EEPROM
displayMenu();
}
void PinA() {
cli();
reading = PIND & 0xC;
if (reading == B00001100 && aFlag) {
encoderPos ++;
bFlag = 0;
aFlag = 0;
}
else if (reading == B00000100) bFlag = 1;
sei();
}
void PinB() {
cli();
reading = PIND & 0xC;
if (reading == B00001100 && bFlag) {
encoderPos --;
//increment the encoder's position count
bFlag = 0;
aFlag = 0;
}
else if (reading == B00001000) aFlag = 1;
sei();
}
// Função para inicializar as receitas padrão
void initializeRecipes() {
// Receita padrão Bier1
strcpy(recipes[0].name, "Bier1"); // Usando strcpy para atribuir o nome
recipes[0].phases[0] = {51.0, 10}; // Infusão: 50°C por 10 minutos
recipes[0].phases[1] = {62.0, 30}; // Protease: 62°C por 30 minutos
recipes[0].phases[2] = {68.0, 20}; // Beta Amilase: 68°C por 20 minutos
recipes[0].phases[3] = {72.0, 15}; // Alfa Amilase: 72°C por 15 minutos
recipes[0].phases[4] = {78.0, 10}; // MashOut: 78°C por 10 minutos
recipes[0].phases[5] = {78.0, 10}; // Sparge: 78°C por 10 minutos
// Repita o mesmo para as outras receitas (Bier2 a Bier9)
for (int i = 1; i < 9; i++) {
snprintf(recipes[i].name, sizeof(recipes[i].name), "Bier%d", i + 1); // Usando snprintf para atribuir o nome com o índice
recipes[i].phases[0] = {51.0, 10}; // Infusão: 50°C por 10 minutos
recipes[i].phases[1] = {62.0, 30}; // Protease: 62°C por 30 minutos
recipes[i].phases[2] = {68.0, 20}; // Beta Amilase: 68°C por 20 minutos
recipes[i].phases[3] = {72.0, 15}; // Alfa Amilase: 72°C por 15 minutos
recipes[i].phases[4] = {78.0, 10}; // MashOut: 78°C por 10 minutos
recipes[0].phases[5] = {78.0, 10}; // Sparge: 78°C por 10 minutos
}
// Salva as receitas padrão na EEPROM
for (int i = 0; i < 9; i++) {
saveSingleRecipeToEEPROM(i);
}
}
void saveSingleRecipeToEEPROM(int recipeIndex) {
int addr = EEPROM_START_ADDRESS + (recipeIndex * sizeof(Recipe)); // Calcula o endereço da receita
EEPROM.put(addr, recipes[recipeIndex]);
// Debugging: Verifique o que está sendo salvo
}
Recipe loadRecipeFromEEPROM(int recipeIndex) {
int address = recipeIndex * sizeof(Recipe); // Calcular o endereço correto para o índice
Recipe loadedRecipe; // Criar uma variável para armazenar a receita carregada
EEPROM.get(address, loadedRecipe); // Carrega a receita da EEPROM
return loadedRecipe; // Retorna a receita carregada
}
void loop() {
checkButton();
navigateMenu(); // Verifica a navegação no menu
}
void checkButton() {
if (digitalRead(selectSwitch) == LOW && !buttonPressed) {
buttonPressed = true;
selectMenu();
delay(300); // Debounce
} else if (digitalRead(selectSwitch) == HIGH) {
buttonPressed = false; // Reseta o estado
}
}
bool encoderButtonPressed() {
static unsigned long lastPressTime = 0; // Para evitar rebotes
unsigned long currentTime = millis();
if (digitalRead(selectSwitch) == LOW) { // Verifica se o botão está pressionado
if (currentTime - lastPressTime > 200) { // Delay de debounce
lastPressTime = currentTime;
return true; // Indica que o botão foi pressionado
}
}
return false; // Nenhuma pressão detectada
}
void navigateMenu() {
static int lastStateA = HIGH;
int stateA = digitalRead(pinA);
int stateB = digitalRead(pinB);
if (lastStateA == HIGH && stateA == LOW) {
if (stateB == HIGH) { // Girando no sentido horário
currentIndex++;
if (currentIndex >= getCurrentMenuSize()) {
currentIndex = 0;
}
} else { // Girando no sentido anti-horário
currentIndex--;
if (currentIndex < 0) {
currentIndex = getCurrentMenuSize() - 1;
}
}
delay(100); // Delay para evitar movimentos rápidos
displayMenu();
}
lastStateA = stateA;
}
void selectMenu() {
if (currentMenu == 0) { // Menu principal
if (currentIndex == 0) {
currentMenu = 1; // Acessa Configurações
currentIndex = 0; // Reseta o índice
} else if (currentIndex == 1) {
currentMenu = 2; // Acessa Monitorar
currentIndex = 0; // Reseta o índice
} else if (currentIndex == 2) {
registerRecipe(); // Chama a função de Cadastro
currentMenu = 0; // Retorna ao menu principal após cadastro
currentIndex = 0; // Reseta o índice
} else if (currentIndex == 3) {
selectRecipe(); // Chama a função de Execução
currentMenu = 0; // Retorna ao menu principal após execução
currentIndex = 0; // Reseta o índice
}
} else if (currentMenu == 1) { // Menu de Configurações
if (currentIndex == 0) {
currentMenu = 2; // Acessa submenu PID
currentIndex = 0; // Reseta o índice
} else if (currentIndex == 1) {
currentMenu = 3; // Acessa submenu Fuzzy
currentIndex = 0; // Reseta o índice
} else if (currentIndex == 2) {
currentMenu = 4; // Acessa submenu Sensor
currentIndex = 0; // Reseta o índice
} else {
currentMenu = 0; // Retorna ao menu principal
currentIndex = 0; // Reseta o índice
}
}
else if (currentMenu == 2) { // Submenu Monitorar
if (currentIndex == 0) {
currentMenu = 6; // Acessa submenu Power
currentIndex = 0; // Reseta o índice
} else if (currentIndex == 1) {
currentMenu = 7; // Acessa submenu Recipe
currentIndex = 0; // Reseta o índice
} else if (currentIndex == 2) {
currentMenu = 8; // Acessa submenu Bomba
currentIndex = 0; // Reseta o índice
} else {
currentMenu = 0; // Retorna ao menu principal
currentIndex = 0; // Reseta o índice
}
} else {
currentMenu = 0; // Retorna ao menu principal
currentIndex = 0; // Reseta o índice
}
displayMenu(); // Atualiza a exibição do menu
}
void displayMenu() {
lcd.clear();
for (int i = 0; i < getCurrentMenuSize(); i++) {
lcd.setCursor(0, i);
if (i == currentIndex) {
lcd.write(byte(6));
lcd.print(" ");
} else {
lcd.print(" ");
}
switch (currentMenu) {
case 0: // Menu Principal
lcd.print(mainMenu[i]);
break;
case 1: // Configurações
lcd.print(settingsMenu[i]);
break;
case 2: // Monitorar
lcd.print(monitorarMenu[i]);
break;
}
}
}
int getCurrentMenuSize() {
switch (currentMenu) {
case 0: return mainMenuSize; // Menu Principal
case 1: return settingsMenuSize; // Menu de Configurações
case 2: return monitorarMenuSize; // Menu Monitorar
default: return 0;
}
}
void checkPause() {
unsigned long currentMillis = millis();
// Imprime no serial sempre que checkPause for chamada
Serial.println("checkPause");
// Se estivermos em menu de ajustes, a pausa não deve ocorrer
if (execMenu == 2) {
return; // Evita que a pausa seja ativada enquanto estamos no menu de ajustes
}
// Verifica se o botão de pausa foi pressionado e se o tempo de debounce foi atingido
if (digitalRead(selectSwitch) == LOW && (currentMillis - lastDebounceTime) > debounceDelay) {
lastDebounceTime = currentMillis; // Atualiza o tempo do último clique de debounce
// Alterna o estado de pausa
isPaused = !isPaused;
// Exibe o estado de pausa no LCD
if (isPaused) {
lcd.clear();
lcd.print("Pausado..."); // Exibe a mensagem "Pausado"
delay(1000);
digitalWrite(RELAY, LOW); // Desliga a bomba ou outros dispositivos enquanto pausado
} else {
// isto nunca é executado
lcd.clear();
lcd.print("Retomando..."); // Exibe a mensagem "Retomando" ao sair da pausa
// inicioEtapa = millis() - (tempos[etapaAtual] - tempoRestante) * 1000;
delay(100); // Exibe por 1 segundo e volta
}
}
}
void pauseScreen(int phaseIndex) {
int opcaoSelecionada = 0; // 0: Ajustar Parâmetros, 1: Pular Etapa, 2: Retomar
int opcaoAnterior = -1; // Inicializado com valor inválido
bool escolhaConfirmada = false;
// Exibe o menu de pausa
while (!escolhaConfirmada) {
// Apenas atualiza o LCD se a opção mudou
if (opcaoSelecionada != opcaoAnterior) {
opcaoAnterior = opcaoSelecionada; // Atualiza o estado anterior
// Atualiza o display LCD com o menu
lcd.clear();
lcd.setCursor(0, 0);
// Exibe o símbolo da flecha para a opção selecionada
lcd.write(opcaoSelecionada == 0 ? byte(6) : ' ');
lcd.print("Ajustar Param.");
lcd.setCursor(0, 1);
lcd.write(opcaoSelecionada == 1 ? byte(6) : ' ');
lcd.print("Pular Etapa");
lcd.setCursor(0, 2);
lcd.write(opcaoSelecionada == 2 ? byte(6) : ' ');
lcd.print("Back");
}
// Lê o encoder para mudar a opção selecionada
if (digitalRead(pinA) == LOW) { // Gira no sentido horário
opcaoSelecionada = (opcaoSelecionada + 1) % 3; // Incrementa e reseta após 2
delay(200); // Debounce
}
if (digitalRead(pinB) == LOW) { // Gira no sentido anti-horário
opcaoSelecionada = (opcaoSelecionada + 2) % 3; // Decrementa (mod 3 evita negativo)
delay(200); // Debounce
}
// Confirmação da escolha com o botão
if (digitalRead(selectSwitch) == LOW) {
delay(200); // Debounce
escolhaConfirmada = true;
}
}
// Executa a ação correspondente à opção escolhida
switch (opcaoSelecionada) {
case 0: // Ajustar parâmetros
lcd.clear();
// ajustarTemperatura(indiceEtapa); // Chama a função de ajuste de parâmetros
break;
case 1: // Pular etapa
lcd.clear();
lcd.print("Pulando etapa...");
delay(1000);
lcd.clear();
// Verifique o valor de phaseIndex antes de pular
Serial.print("Fase atual antes de pular: ");
Serial.println(phaseIndex);
phaseIndex++; // Avança para a próxima etapa
// Verifica se ultrapassou o número total de etapas
if (phaseIndex >= NUM_PHASES ) { // Limite de etapas (0-5)
lcd.clear();
lcd.print("Processo fim!");
delay(2000);
isPaused = false;
execMenu = 1; // Retorna ao estado inicial
executePhase;
return; // Sai do loop de execução
}
// Modificações:
// Verifique o valor de phaseIndex após o incremento
Serial.print("Fase atual depois de pular: ");
Serial.println(phaseIndex);
// Acessa a receita correta do array 'recipes' com base no 'phaseIndex'
isPaused = false; // Garante que a execução não está pausada após pular a etapa
ajusteCompletado = true; // Marca que o ajuste foi feito
execMenu = 1 ; // Retorna ao estado de execução
executeRecipe(selectedRecipeIndex);
break;
case 2: // Retomar execução
lcd.clear();
lcd.print("Return...");
delay(1000);
isPaused = false;
lcd.clear();
execMenu = 1; // Retorna ao estado inicia
break;
}
}
// Função de controle de temperatura
int controleTemperatura(float setpoint,int powerRamp, int powerStep) {
sensors.requestTemperatures();
float currentTemperature = sensors.getTempCByIndex(0);
float error = setpoint - currentTemperature;
float integral = 0, derivative = 0, previousError = 0;
integral += error;
derivative = error - previousError;
int powerOutput = (Kp * error) + (Ki * integral) + (Kd * derivative);
previousError = error;
if (error > fuzzyStart) {
float fuzzyAdjustment = fuzzyAmplitude * (error - fuzzyStart);
powerOutput += fuzzyAdjustment;
}
powerOutput = (currentTemperature < setpoint) ? min(powerRamp, powerOutput) : min(powerStep, powerOutput);
if (currentTemperature < setpoint) {
powerOutput = min(powerRamp, powerOutput);
} else {
powerOutput = min(powerStep, powerOutput);
}
digitalWrite(RELAY, powerOutput > 0 ? HIGH : LOW);
return powerOutput; // Retorna a potência aplicada
}
void displayTotalTime() {
if (startTime == 0) { // Verifica se o cronômetro ainda não foi iniciado
startTime = millis(); // Inicializa o tempo de início com o tempo atual (primeira execução da função)
}
// Cálculo do tempo total de brassagem
// Verifica se passou 1 segundo desde a última atualização
unsigned long currentMillis = millis(); // Tempo atual
if (currentMillis - previousMillis >= intervalo) {
// Salva o tempo da última atualização
previousMillis = currentMillis;
// Incrementa o tempo total de 1 segundo
totalTime++; // Incrementa 1 segundo por vez
}
// Cálculo de minutos e segundos
int totalMins = totalTime / 60;
int totalSecs = totalTime % 60;
// Exibir no LCD
lcd.setCursor(1, 2); // Defina a linha e coluna desejadas
lcd.write(byte(2));// Exibe o caractere na tela.
lcd.print(": ");
if (totalMins < 10) lcd.print("0");
lcd.print(totalMins);
lcd.print(":");
if (totalSecs < 10) lcd.print("0");
lcd.print(totalSecs);
}
void controlaBomba() {
// EEPROM.get(Pump_Oper, pumpOper);
// EEPROM.get(Pump_Pause, pumpPause);
int pumpPause = 1;
int pumpOper = 1;
if (bombaTimer < pumpPause*60) { // Bomba ligada por 5 minutos (300 segundos)
digitalWrite(pumpPin, LOW); // bomba desligada
bombaTimer++;
} else if (bombaTimer >= pumpPause*60 && bombaTimer < (pumpOper*60 + pumpPause*60)) { // Bomba desligada por 1 minuto (60 segundos)
digitalWrite(pumpPin, HIGH); // Desliga a bomba
displayBomba(10, 3); // Exibe o símbolo de "Power"
bombaTimer++;
}
else {
bombaTimer = 0; // Reinicia o timer da bomba
}
}
void controlaMotor() {
// EEPROM.get(Pump_Oper, pumpOper);
// EEPROM.get(Pump_Pause, pumpPause);
int pumpPause = 1;
int pumpOper = 1;
if (motorTimer < pumpPause*60) { //
digitalWrite(motorPin, HIGH); // motor ligado
displayMotor(12, 3); // Exibe o símbolo de "Power"
motorTimer++;
} else if (motorTimer >= pumpPause*60 && motorTimer <(pumpOper*60+ pumpPause*60)) { // Motor desligada por 5 minuto (60 segundos)
digitalWrite(motorPin, LOW); // Desliga o motor
motorTimer++;
}
else {
motorTimer = 0; // Reinicia o timer
}
}
void actionBuzzer() {
for (int i = 0; i < 5; i++) { // Toca o buzzer 5 vezes
digitalWrite(buzzerPin, HIGH); // Ativa o buzzer
delay(1000); // Duração do beep
digitalWrite(buzzerPin, LOW); // Desativa o buzzer
delay(1000); // Pausa entre os beeps
}
}
void mostrarMensagemAdAgua() {
isAACheckPaused = true;
lcd.clear();
lcd.setCursor(3, 1);
lcd.print("Adicionou Agua?");
lcd.setCursor(5, 3);
lcd.print("Press Enter");
delay(3000); // Mostra a mensagem por 3 segundos
mensagemAAExibida = true; // Marca que a mensagem foi exibida
// Espera até que o botão seja pressionado
while (digitalRead(selectSwitch) == HIGH ) {
// Aguarda o botão ser pressionado
}
// Opcional: Aguarde um pequeno tempo para evitar múltiplas leituras
delay(200); // Delay para debouncing
lcd.clear(); // Limpa a tela ao sair da mensagem
isAACheckPaused = false; // Retira a pausa de teste de pH
}
void mostrarMensagemTesteIodo() {
isTICheckPaused = true;
lcd.clear();
lcd.setCursor(2, 0);
lcd.print("Teste de Iodo");
unsigned long tempoAjuste = 3000; // Tempo padrão inicial (3 segundos)
unsigned long tempoInicial = millis(); // Marca o tempo de início
mensagemTIExibida = true; // Marca que a mensagem foi exibida
// Variável de controle para navegação entre as opções
int opcaoSelecionada = 0; // 0: Ajustar Parâmetros, 1: Pular Etapa, 2: Retomar
int opcaoAnterior = -1; // Inicializado com valor inválido
bool escolhaConfirmada = false;
// Exibe o menu de pausa
while (!escolhaConfirmada) {
// Apenas atualiza o LCD se a opção mudou
if (opcaoSelecionada != opcaoAnterior) {
opcaoAnterior = opcaoSelecionada; // Atualiza o estado anterior
// Atualiza o display LCD com o menu
lcd.clear();
lcd.setCursor(2, 0);
lcd.print("Teste de Iodo");
lcd.setCursor(0, 1);
lcd.write(opcaoSelecionada == 0 ? byte(6) : ' ');
lcd.print("Time...");
lcd.setCursor(0, 2);
lcd.write(opcaoSelecionada == 1 ? byte(6) : ' ');
lcd.print("Back...");
}
// Lê o encoder para mudar a opção selecionada
if (digitalRead(pinA) == LOW) { // Gira no sentido horário
opcaoSelecionada = (opcaoSelecionada + 1) % 3; // Incrementa e reseta após 2
delay(200); // Debounce
}
if (digitalRead(pinB) == LOW) { // Gira no sentido anti-horário
opcaoSelecionada = (opcaoSelecionada + 2) % 3; // Decrementa (mod 3 evita negativo)
delay(200); // Debounce
}
// Confirmação da escolha com o botão
if (digitalRead(selectSwitch) == LOW) {
delay(200); // Debounce
escolhaConfirmada = true;
}
}
// Executa a ação correspondente à opção escolhida
switch (opcaoSelecionada) {
case 0: // Ajustar parâmetros
lcd.clear();
// ajustarTempoAA(phaseIndex); // Chama a função de ajuste de parâmetros
break;
case 1: // Retomar execução
lcd.clear();
lcd.print("Retomando...");
delay(1000);
isPaused = false;
execMenu = 1; // Retorna ao estado inicia
executePhase;
lcd.clear();
break;
}
// Limpa a tela ao sair da mensagem
lcd.clear();
isTICheckPaused = false; // Retira a pausa do teste de pH
}
void mostrarMensagemTransferirMosto() {
isTMCheckPaused = true; //
lcd.clear();
lcd.setCursor(0, 1);
lcd.print(" Transferir o mosto");
lcd.setCursor(6, 3);
lcd.print("Press Enter");
delay(3000); // Mostra a mensagem por 3 segundos
mensagemTMExibida = true; // Marca que a mensagem foi exibida
// Espera até que o botão seja pressionado
while (digitalRead(selectSwitch) == HIGH ) {
// Aguarda o botão ser pressionado
delay(100);
}
// Opcional: Aguarde um pequeno tempo para evitar múltiplas leituras
delay(200); // Delay para debouncing
lcd.clear(); // Limpa a tela ao sair da mensagem
isTMCheckPaused = false; // Retira a pausa de teste de pH
}
void mostrarMensagemTestePH() {
isPHCheckPaused = true; //
lcd.clear();
lcd.setCursor(5, 1);
lcd.print("Avaliar o pH");
delay(10000); // Mostra a mensagem por 3 segundos
mensagemPHExibida = true; // Marca que a mensagem foi exibida
lcd.clear(); // Limpa a tela ao sair da mensagem
isPHCheckPaused = false; // Retira a pausa de teste de pH
}
bool verificarAgua() {
// Lê o estado do sensor de nível de água
int estadoSensor = digitalRead(pinoSensorAgua);
// Lê o estado do botão "Enter" do encoder
int reading = digitalRead(selectSwitch);
// Aplica debounce no botão
if (reading != lastButtonState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
if (reading == LOW) { // Botão pressionado
lastButtonState = reading; // Atualiza o estado do botão
return true; // Água foi confirmada como adicionada
}
}
lastButtonState = reading;
// Exibe mensagem na tela
exibirMensagem("Adicione água e pressione Enter.");
// Retorna verdadeiro se a água foi adicionada (sensor) ou se o botão foi pressionado
return (estadoSensor == HIGH);
}
void exibirMensagem(const char* mensagem) {
lcd.clear(); // Limpa a tela
lcd.setCursor(0, 0); // Define o cursor na primeira linha
lcd.print(mensagem); // Exibe a mensagem
}
// Função para verificar se o botão "Enter" foi pressionado (exemplo)
bool botaoEnterPressionado() {
return digitalRead(selectSwitch) == HIGH; // Altere para o seu pino de entrada do botão
}
// Função para criar e exibir o símbolo de power
void displayPower(int x, int y) {
lcd.createChar(3, powerSymbol); // Cria o caractere customizado
lcd.setCursor(x, y); // Define a posição do cursor
lcd.write(byte(3)); // Exibe o caractere
}
// Função para criar e exibir o símbolo de bomba
void displayBomba(int x, int y) {
lcd.createChar(4, bombaSymbol); // Cria o caractere customizado
lcd.setCursor(x, y); // Define a posição do cursor
lcd.write(byte(4)); // Exibe o caractere
}
// Função para criar e exibir o símbolo de bomba
void displayMotor(int x, int y) {
lcd.createChar(5, motorSymbol); // Cria o caractere customizado
lcd.setCursor(x, y); // Define a posição do cursor
lcd.write(byte(5)); // Exibe o caractere
}
// Função para criar e exibir o símbolo de bomba
void displayArrowLeft(int x, int y) {
lcd.createChar(6, ArrowLeft);// Cria o caractere customizado
lcd.setCursor(x, y); // Define a posição do cursor
lcd.write(byte(6)); // Exibe o caractere
}
void pulsarValor(int coluna, int linha, int valor, int index) {
// Verifica se o valor ainda não foi pulsado
if (!pulsou[index]) {
// Exibe o valor inicialmente
lcd.setCursor(coluna, linha);
lcd.print(valor); // Exibe o valor
// Pulsando o valor 3 vezes
for (int i = 0; i < 5; i++) {
lcd.setCursor(coluna, linha);
lcd.print(" "); // Limpa o valor
delay(500); // Aguarda 500ms para apagar
lcd.setCursor(coluna, linha);
lcd.print(valor); // Exibe o valor novamente
delay(500); // Aguarda 500ms para exibir
}
// Após o efeito, o valor fica fixo na tela
lcd.setCursor(coluna, linha);
lcd.print(valor); // Exibe o valor novamente
// Marca que o valor já foi pulsado
pulsou[index] = true;
}
}
void ajustarParametro(int coluna, int linha, int& valor, int min, int max) {
// Exibe continuamente o valor ajustável na tela
lcd.setCursor(coluna, linha);
lcd.print(" "); // Limpa a área do valor
lcd.setCursor(coluna, linha);
lcd.print(valor); // Mostra o valor atualizado
// Ajuste o valor com o encoder
if (encoderPos > max) {
encoderPos = max;
} else if (encoderPos < min) {
encoderPos = min;
}
valor = encoderPos; // Atualiza o valor ajustado
}
void ajustarParametroDecimal(int coluna, int linha, float& valor, float min, float max, float passo) {
// Exibe continuamente o valor ajustável na tela
lcd.setCursor(coluna, linha);
lcd.print(" "); // Limpa a área do valor (espaço maior para acomodar números com casas decimais)
lcd.setCursor(coluna, linha);
lcd.print(valor, 1); // Mostra o valor atualizado com 1 casa decimal
// Ajuste o valor com o encoder
int movimento = getEncoderMovement(); // Função que retorna o movimento do encoder (-1, 0, ou +1)
if (movimento != 0) {
valor += movimento * passo; // Incrementa ou decrementa conforme o movimento do encoder
if (valor > max) {
valor = max;
} else if (valor < min) {
valor = min;
}
}
}
int getEncoderMovement() {
static int lastPosition = 0;
int newPosition = encoderPos; // Variável global que armazena a posição do encoder
int movimento = newPosition - lastPosition;
lastPosition = newPosition;
return movimento;
}
void ajustarTempoAA(int selectedRecipeIndex, int phaseIndex) {
int ajusteTempo = 5;
int setpoint = 72;
String nomeEtapa = "Alfa_Amilase";
bool ajusteTempoAA = false;
// Carregar a receita selecionada da EEPROM usando o índice
Recipe selectedRecipe = loadRecipeFromEEPROM(selectedRecipeIndex);
// Exibe o tempo atual no LCD
lcd.clear();
lcd.setCursor(2, 0);
lcd.print(nomeEtapa);
lcd.setCursor(1, 1);
lcd.write(byte(1)); // Exibe o caractere na tela.
lcd.print(": ");
lcd.print(currentTemperature, 1);
lcd.setCursor(12, 1);
lcd.print("Sp: ");
lcd.print(setpoint);
lcd.write(byte(0));
lcd.print("C");
displayPower(1, 3); // Exibe o símbolo de "Power"
lcd.print(": ");
lcd.print(powerRamp);
lcd.setCursor(8, 3);
lcd.print("%");
lcd.setCursor(12, 2);
lcd.print("Tp: ");
// Resetando a flag de pulsação para garantir que a pulsação ocorra novamente
pulsou[13] = false; // Resetando o controle para o index 13, onde o valor de ajusteTempo é exibido
pulsarValor(16, 2, ajusteTempo, 13);
unsigned long lastEncoderPress = 0; // Controle de debounce do encoder
while (true) {
// Aumenta o tempo com o encoder (pinA)
if (digitalRead(pinA) == LOW && millis() - lastEncoderPress > 200) {
ajusteTempo++; // Aumenta o tempo
lastEncoderPress = millis();
}
// Diminui o tempo com o encoder (pinB)
if (digitalRead(pinB) == LOW && millis() - lastEncoderPress > 200) {
ajusteTempo--; // Diminui o tempo
lastEncoderPress = millis();
}
// Garante que o valor de ajusteTemperatura esteja entre 0 e 100
if (ajusteTempo > 60) {
ajusteTempo = 60;
} else if (ajusteTempo < 0) {
ajusteTempo = 0;
}
// Atualiza a exibição no LCD para refletir o novo tempo
lcd.setCursor(12, 2);
lcd.print("Tp: ");
lcd.print(ajusteTempo);
lcd.print(" "); // Limpa caracteres extras
// Lê o estado do botão "Enter"
if (digitalRead(selectSwitch) == LOW && !buttonPressed && millis() - lastButtonPress > 200) {
buttonPressed = true; // Marca que o botão foi pressionado
lastButtonPress = millis(); // Atualiza o tempo do último pressionamento
// Salva o ajuste na EEPROM e na variável global
// mashAATime = ajusteTempo;
// EEPROM.put(mashTime_AA, mashAATime);
// Confirmação no LCD
lcd.setCursor(14, 3);
lcd.print("Save!");
delay(2000); // Aguarda 2 segundos para exibir a confirmação
// Limpa a linha onde a mensagem foi exibida
lcd.setCursor(14, 3);
lcd.print(" ");
unsigned long startMillis = millis();
while (millis() - startMillis < 2000) {
}
// Depois de salvar o ajuste, evite que pauseScreen seja chamada
isPaused = false; // Certifique-se de que não estamos em pausa
execMenu = 1;
ajusteTempoAA = true; // Marca que o ajuste foi feito
break; // Sai do loop de ajuste
}
// Se o botão for solto, reseta a variável de controle
if (digitalRead(selectSwitch) == HIGH) {
buttonPressed = false;
}
delay(10); // Reduz a frequência de leitura para evitar sobrecarga de CPU
}
// Chama a função executarEtapa apenas uma vez após o ajuste
if (ajusteTempoAA) {
// Agora, passamos o objeto da receita (selectedRecipe), não o índice
executePhase(selectedRecipe, phaseIndex); // Chama a função com a receita e a fase específica
}
}
void registerRecipe() {
// int selectedRecipeIndex = 0;
int lastSelectedRecipeIndex = -1; // Inicializa com valor inválido para forçar a atualização inicial
bool buttonPressed = false;
unsigned long buttonDebounceTime = 50; // Para controlar o debounce do botão de seleção
unsigned long buttonDebounceDelay = 500; // Delay do debounce para o botão (em milissegundos)
unsigned long lastEncoderPress = 0; // Controle de debounce do encoder
lcd.clear();
lcd.print("Cadastro de Receita");
delay(1000);
lcd.clear();
lcd.print("Selecionar Receita:");
lcd.setCursor(2, 1);
lcd.print("Bier");
lcd.print(selectedRecipeIndex + 1);
// Menu de seleção de receita
while (!buttonPressed) {
if (digitalRead(pinA) == LOW && millis() - lastEncoderPress > 200) {
selectedRecipeIndex++; // Aumenta o tempo
lastEncoderPress = millis();
if(selectedRecipeIndex > 9){selectedRecipeIndex =1;}
}
// Diminui o tempo com o encoder (pinB)
if (digitalRead(pinB) == LOW && millis() - lastEncoderPress > 200) {
selectedRecipeIndex--; // Diminui o tempo
lastEncoderPress = millis();
if(selectedRecipeIndex < 1){selectedRecipeIndex = 9;}
}
lcd.setCursor(0,0);
lcd.print("Selecionar Receita:");
lcd.setCursor(2, 1);
lcd.print("Bier");
lcd.print(selectedRecipeIndex + 1); // Exibe Bier1, Bier2, ..., Bier9
// Verifica se o botão do encoder foi pressionado
if (digitalRead(selectSwitch) == LOW) {
// Debounce para o botão de seleção
if ((millis() - buttonDebounceTime) > buttonDebounceDelay) {
buttonPressed = true;
buttonDebounceTime = millis(); // Atualiza o tempo do debounce
delay(300); // Delay adicional para evitar múltiplos cliques rápidos
}
}
delay(20); // Um pequeno delay para melhorar a estabilidade da leitura do encoder
}
// Agora que a receita foi selecionada, ajustamos os parâmetros dela
for (int i = 0; i < NUM_PHASES; i++) {
lcd.clear();
lcd.print("Fase: ");
lcd.print(phaseNames[i]);
delay(1000);
// Ajusta a temperatura
adjustTemperature(selectedRecipeIndex, i);
// Ajusta o tempo
adjustTime(selectedRecipeIndex, i);
}
// Salva a receita na EEPROM
saveSingleRecipeToEEPROM(selectedRecipeIndex);
lcd.clear();
lcd.print("Receita Salva!");
delay(1000);
}
void adjustRecipeParameters(int selectedRecipeIndex) {
bool exit = false;
int menuSelection = 0;
int phaseIndex = 0; // Define a fase selecionada como 0 inicialmente
// Nomes das fases
const char* phaseNames[] = {
"Fitase", "Protease", "Beta Amilase", "Alfa Amilase", "MashOut", "Sparge"
};
while (!exit) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Ajustar Receita");
lcd.setCursor(0, 1);
lcd.print("Bier ");
lcd.print(selectedRecipeIndex + 1); // Exibe o nome da receita
delay(1000);
// Menu de seleção da fase
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("1. Ajuste Temp");
lcd.setCursor(0, 1);
lcd.print("2. Ajuste Tempo");
lcd.setCursor(0, 2);
lcd.print("3. Salvar e Sair");
lcd.setCursor(0, 3);
lcd.print("Fase: ");
lcd.print(phaseNames[phaseIndex]); // Exibe a fase atual selecionada
// Navegar com o encoder
while (digitalRead(selectSwitch) != LOW) {
int currentEncoderState = digitalRead(pinA);
if (currentEncoderState != lastEncoderState) {
if (digitalRead(pinB) != currentEncoderState) {
menuSelection++; // Avança no menu
if (menuSelection > 2) {
menuSelection = 0; // Reseta para o primeiro item do menu
}
} else {
menuSelection--; // Retrocede no menu
if (menuSelection < 0) {
menuSelection = 2; // Vai para o último item do menu
}
}
lastEncoderState = currentEncoderState;
}
// Atualiza a tela com a seleção do menu
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("1. Ajuste Temp");
lcd.setCursor(0, 1);
lcd.print("2. Ajuste Tempo");
lcd.setCursor(0, 2);
lcd.print("3. Salvar e Sair");
lcd.setCursor(0, 3);
lcd.print("Fase: ");
lcd.print(phaseNames[phaseIndex]); // Exibe a fase atual selecionada
// Marca o item selecionado com um '>' (seta)
if (menuSelection == 0) {
lcd.setCursor(0, 0);
lcd.print("> Ajuste Temp");
} else if (menuSelection == 1) {
lcd.setCursor(0, 1);
lcd.print("> Ajuste Tempo");
} else {
lcd.setCursor(0, 2);
lcd.print("> Salvar e Sair");
}
delay(200); // Delay para evitar leituras rápidas demais
}
// Ação baseada na opção selecionada
switch (menuSelection) {
case 0: // Ajuste de temperatura
adjustTemperature(selectedRecipeIndex, phaseIndex); // Passa o índice da fase diretamente
break;
case 1: // Ajuste de tempo
adjustTime(selectedRecipeIndex, phaseIndex); // Passa o índice da fase diretamente
break;
case 2: // Salvar e sair
saveSingleRecipeToEEPROM(selectedRecipeIndex);
exit = true; // Sai da função de ajuste
break;
}
// Quando o usuário terminar o ajuste em uma fase, ele pode avançar para a próxima fase
if (exit == false) {
phaseIndex++;
if (phaseIndex >= sizeof(phaseNames) / sizeof(phaseNames[0])) {
phaseIndex = 0; // Reseta para a primeira fase
}
}
}
}
void adjustTemperature(int selectedRecipeIndex, int phaseIndex) {
// lcd.clear();
lcd.setCursor(4,2);
lcd.print("Temp: ");
// Carregar a temperatura da receita armazenada na EEPROM
loadRecipeFromEEPROM(selectedRecipeIndex); // Garante que os dados da receita estão atualizados
// Variável que armazena o valor ajustado
int valorTemp = recipes[selectedRecipeIndex].phases[phaseIndex].temperature;
Serial.println(valorTemp);
lcd.setCursor(10,2);
lcd.print(valorTemp);
// Loop para ajustar a temperatura até o botão ser pressionado
unsigned long lastEncoderPress = 0; // Controle de debounce do encoder
while (true) {
// Aumenta o tempo com o encoder (pinA)
if (digitalRead(pinA) == LOW && millis() - lastEncoderPress > 200) {
valorTemp++; // Aumenta o tempo
lastEncoderPress = millis();
}
// Diminui o tempo com o encoder (pinB)
if (digitalRead(pinB) == LOW && millis() - lastEncoderPress > 200) {
valorTemp--; // Diminui o tempo
lastEncoderPress = millis();
}
// Garante que o valor de ajusteTemperatura esteja entre 0 e 100
if (valorTemp > 99) {
valorTemp = 99;
} else if (valorTemp < 0) {
valorTemp = 0;
}
// Atualiza a exibição no LCD para refletir o novo tempo
lcd.setCursor(4,2);
lcd.print("Temp: ");
lcd.setCursor(10, 2);
lcd.print(valorTemp);
lcd.print(" "); // Limpa caracteres extras
// Lê o estado do botão "Enter" (selectSwitch) com debounce
if (digitalRead(selectSwitch) == LOW && millis() - lastButtonPress > debounceDelay) {
lastButtonPress = millis(); // Atualiza o tempo do último pressionamento do botão
// Atualiza o valor da temperatura na receita
recipes[selectedRecipeIndex].phases[phaseIndex].temperature = valorTemp;
// Salvar a alteração na EEPROM
saveSingleRecipeToEEPROM(selectedRecipeIndex); // Função de salvamento após o ajuste
// Quando o botão for pressionado, o ajuste é finalizado
break; // Sai do loop e finaliza o ajuste
}
}
lcd.setCursor(15,3);
lcd.print("Save");
delay(1000); // Exibe mensagem de confirmação por 1 segundo
lcd.setCursor(4,2);
lcd.print(" ");
lcd.setCursor(15,3);
lcd.print(" ");
}
void adjustTime(int selectedRecipeIndex, int phaseIndex) {
// lcd.clear();
lcd.setCursor(4,2);
lcd.print("Time: ");
// Carregar a temperatura da receita armazenada na EEPROM
loadRecipeFromEEPROM(selectedRecipeIndex); // Garante que os dados da receita estão atualizados
// Variável que armazena o valor ajustado
int valorTime = recipes[selectedRecipeIndex].phases[phaseIndex].time;
Serial.println(valorTime);
lcd.setCursor(10,2);
lcd.print(valorTime);
// Loop para ajustar a temperatura até o botão ser pressionado
unsigned long lastEncoderPress = 0; // Controle de debounce do encoder
while (true) {
// Aumenta o tempo com o encoder (pinA)
if (digitalRead(pinA) == LOW && millis() - lastEncoderPress > 200) {
valorTime++; // Aumenta o tempo
lastEncoderPress = millis();
}
// Diminui o tempo com o encoder (pinB)
if (digitalRead(pinB) == LOW && millis() - lastEncoderPress > 200) {
valorTime--; // Diminui o tempo
lastEncoderPress = millis();
}
// Garante que o valor de ajusteTemperatura esteja entre 0 e 100
if (valorTime > 59) {
valorTime = 59;
} else if (valorTime < 0) {
valorTime = 0;
}
// Atualiza a exibição no LCD para refletir o novo tempo
lcd.setCursor(10, 2);
lcd.print(valorTime);
lcd.print(" "); // Limpa caracteres extras
// Lê o estado do botão "Enter" (selectSwitch) com debounce
if (digitalRead(selectSwitch) == LOW && millis() - lastButtonPress > debounceDelay) {
lastButtonPress = millis(); // Atualiza o tempo do último pressionamento do botão
// Atualiza o valor da temperatura na receita
recipes[selectedRecipeIndex].phases[phaseIndex].time = valorTime;
// Salvar a alteração na EEPROM
saveSingleRecipeToEEPROM(selectedRecipeIndex); // Função de salvamento após o ajuste
// Quando o botão for pressionado, o ajuste é finalizado
break; // Sai do loop e finaliza o ajuste
}
}
lcd.setCursor(15,3);
lcd.print("save");
delay(1000); // Exibe mensagem de confirmação por 1 segundo
lcd.setCursor(4, 2);
lcd.print(" ");
lcd.setCursor(15,3);
lcd.print(" ");
}
int selectRecipe() {
int selectedRecipeIndex = 0; // Índice da receita selecionada
int lastSelectedRecipeIndex = -1; // Último índice selecionado (para evitar repetição)
bool buttonPressed = false; // Controle do estado do botão
unsigned long buttonDebounceTime = 50; // Controle do debounce do botão
unsigned long buttonDebounceDelay = 500; // Delay de debounce para o botão (em milissegundos)
unsigned long lastEncoderPress = 0; // Controle de debounce do encoder
lcd.clear();
lcd.print("Selecionar Receita");
lcd.setCursor(2, 1);
lcd.print("Bier");
lcd.print(selectedRecipeIndex + 1); // Exibe a primeira receita (Bier1, Bier2, ..., Bier9)
delay(1000); // Exibe a mensagem por 1 segundo
// Menu de seleção de receita
while (!buttonPressed) {
if (digitalRead(pinA) == LOW && millis() - lastEncoderPress > 200) {
selectedRecipeIndex++; // Aumenta o tempo
lastEncoderPress = millis();
if(selectedRecipeIndex > 9){selectedRecipeIndex =1;}
}
// Diminui o tempo com o encoder (pinB)
if (digitalRead(pinB) == LOW && millis() - lastEncoderPress > 200) {
selectedRecipeIndex--; // Diminui o tempo
lastEncoderPress = millis();
if(selectedRecipeIndex < 1){selectedRecipeIndex = 9;}
}
// Exibe a receita selecionada no LCD
lcd.setCursor(0, 0);
lcd.print("Selecionar Receita:");
lcd.setCursor(2, 1);
lcd.print("Bier");
lcd.print(selectedRecipeIndex + 1); // Exibe o nome da receita (Bier1, Bier2, ..., Bier9)
// Verifica se o botão do encoder foi pressionado
if (digitalRead(selectSwitch) == LOW) {
if ((millis() - buttonDebounceTime) > buttonDebounceDelay) {
buttonPressed = true; // Marca o botão como pressionado
buttonDebounceTime = millis(); // Atualiza o tempo de debounce
// executRecipe(selectedRecipeIndex); // Chama a função executeRecipe com o índice da receita
delay(300); // Delay adicional para evitar múltiplos cliques rápidos
}
}
delay(20); // Um pequeno delay para melhorar a estabilidade da leitura do encoder
}
// Após a seleção da receita, executa a receita selecionada
executeRecipe(selectedRecipeIndex); // Passa o índice da receita p
}
void executeRecipe(int selectedRecipeIndex) {
// Carregar a receita selecionada da EEPROM
Recipe selectedRecipe = loadRecipeFromEEPROM(selectedRecipeIndex);
// Exibe o nome da receita no LCD
unsigned long startTime = millis(); // Tempo de início do processo
while (execMenu == 0) { // Aguardando para iniciar a execução
// Exibe mensagem de adição de água
mostrarMensagemAdAgua(); // Exibe a mensagem de adição de água
lcd.print("Executando:");
lcd.setCursor(0, 1);
lcd.print(selectedRecipe.name); // Exibe o nome da receita
delay(1000); // Exibe a mensagem por 1 segundo
lcd.clear();
execMenu = 1; // Avança para o estado de execução
delay(250); // Pequeno delay antes de iniciar a execução
}
while (execMenu == 1) { // Início da execução do processo
// Executa a fase atual
if (phaseIndex >= 0 && phaseIndex < NUM_PHASES) { // Verifica se o índice da fase é válido
// Executa as fases da receita
lcd.clear();
lcd.print("Fase: ");
lcd.print(phaseNames[phaseIndex]); // Exibe o nome da fase atual
executePhase(selectedRecipe, phaseIndex) ; // Executa a fase
}
else {
// Se o índice da fase não for válido, exibe uma mensagem de erro
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Fase desconhecida!");
delay(2000);
break; // Interrompe o processo
}
// Verifica se a fase foi concluída para avançar para a próxima
if (timeCountDown <= 0) {
phaseIndex++; // Avança para a próxima fase
if (phaseIndex >= NUM_PHASES) { // Limite de fases
lcd.clear();
lcd.setCursor(1, 1);
lcd.print("Processo finaliz!");
delay(2000); // Exibe mensagem de conclusão por 5 segundos
break; // Sai do loop de execução
}
}
delay(1000); // Atraso de 1 segundo antes de atualizar o LCD
}
}
void executePhase(Recipe recipe, int phaseIndex) {
unsigned long startTime = millis(); // Marca o tempo inicial
unsigned long elapsedTime = 0; // Tempo decorrido
bool etapaConcluida = false; // Flag para verificar se a etapa foi concluída
bool reachedSetPoint = false;
int powerRamp = 0;
int powerStep = 0;
EEPROM.get(32, powerRamp); //Rever posições na memoria EEPROM
EEPROM.get(36, powerStep);
// Obtém a temperatura e o tempo da fase
int setPoint = recipe.phases[phaseIndex].temperature;
int phaseTimeInMinutes = recipe.phases[phaseIndex].time;
int timeCountDown = phaseTimeInMinutes * 60; // Converte o tempo de fase de minutos para segundos
checkPause(); // Verifica se a pausa foi acionada
if (isPaused) { // Verifica se está em pausado
pauseScreen( phaseIndex);
}
while (timeCountDown > 0) {
float currentTemperature = sensors.getTempCByIndex(0);
// Atualiza a exibição
lcd.setCursor(1, 1);
lcd.write(byte(1));// Exibe o caractere na tela.
lcd.print(": ");
lcd.print(currentTemperature, 1);
lcd.setCursor(11, 1);
lcd.print("sp: ");
lcd.print(setPoint); // Exibe a temperatura da fase
lcd.write(byte(0));
lcd.print("C");
// Ajusta a potência de acordo com a etapa (rampa ou manutenção)
int powerOutput;
if (!reachedSetPoint) {
// Fase de subida de rampa: utiliza powerRamp
powerOutput = controleTemperatura(setPoint, powerRamp, powerStep);
if (currentTemperature >= setPoint) {
reachedSetPoint = true; // Marca que o setpoint foi atingido
}
} else {
// Fase de manutenção: utiliza powerStep
powerOutput = controleTemperatura(setPoint, powerRamp, powerStep);
}
// lcd.setCursor(1, 3);
displayPower(1, 3); // Exibe o símbolo de "Power"
lcd.print(": ");
lcd.print(reachedSetPoint ? powerStep : powerRamp);
lcd.setCursor(8,3);
lcd.print("%");
// Inicia o cronômetro quando a temperatura atinge o setpoint
//if (currentTemperature >= (setpoint - 1) && !timerStarted) {
// timerStarted = true;
// localSeconds = 0;
// }
elapsedTime = (millis() - startTime) / 1000; // Elapsed time in seconds
timeCountDown = phaseTimeInMinutes * 60 - elapsedTime; // Atualiza o tempo restante
// Calcula minutos e segundos restantes
int minutes = timeCountDown / 60;
int seconds = timeCountDown % 60;
// Exibe no LCD no formato "Minutos:Segundos"
if (timeCountDown < 0) {
timeCountDown = 0; // Evita valores negativos
break;
}
// timeCountDown -= 50; // Reduz o tempo restante em 1 segundo
lcd.setCursor(11, 2);
lcd.print("Tp: ");
if (minutes < 10) {lcd.print("0"); }
lcd.print(minutes); // Exibe os minutos
lcd.print(":");
if (seconds < 10) {lcd.print("0"); }
lcd.print(seconds); // Exibe os segundos
delay(1000); // Aguarda 1 segundo antes de atualizar o display
displayTotalTime();
controlaBomba();
if (phaseIndex != 4) {
controlaMotor();
}
if (phaseIndex == 1 && !mensagemPHExibida) {
mostrarMensagemTestePH(); // Exibe a mensagem
// Quando o ENTER for pressionado, volta à execução normal
}
if (phaseIndex == 3 && timeCountDown <= 60000 && !mensagemTIExibida) {
mostrarMensagemTesteIodo(); // Exibe a mensagem
//mensagemTIExibida = true; // Garante que a mensagem será exibida apenas uma vez
}
if (phaseIndex == 5 && !mensagemTMExibida ) {
mostrarMensagemTransferirMosto();
}
}
// Após o tempo da fase terminar, pode exibir uma mensagem de "Fim de fase"
lcd.clear();
lcd.print("Fase Concluida!");
actionBuzzer();
delay(2000); // Exibe por 2 segundos antes de passar para a próxima fase
// Após a fase ser concluída, a função termina
// Fase concluída, você pode adicionar código adicional aqui, se necessário
}
void executePhase1() {
loadRecipeFromEEPROM(selectedRecipeIndex); // Garante que os dados da receita estão atualizados
// Variável que armazena o valor ajustado
int valorTime = recipes[selectedRecipeIndex].phases[phaseIndex].time;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Etapa ");
lcd.print(phaseIndex + 1);
inicioEtapa = millis();
tempoRestante = valorTime;
emExecucao = true;
isPause = false;
}
void pularEtapa() {
lcd.setCursor(0, 2);
lcd.print("Pulando Etapa...");
delay(1000);
phaseIndex++;
if (phaseIndex >= NUM_PHASES) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Processo Finalizado");
emExecucao = false;
return;
}
executePhase1();
}
void checkPause1() {
loadRecipeFromEEPROM(selectedRecipeIndex); // Garante que os dados da receita estão atualizados
// Variável que armazena o valor ajustado
int valorTime = recipes[selectedRecipeIndex].phases[phaseIndex].time;
isPause = !isPause;
if (isPause) {
lcd.setCursor(0, 2);
lcd.print("Sistema Pausado ");
} else {
lcd.setCursor(0, 2);
lcd.print("Executando ");
delay(1000);
lcd.setCursor(0,2);
lcd.print(" ");
inicioEtapa = millis() - (valorTime - tempoRestante) * 1000;
}
}