// === PINOS DE RELÉS ===
#define REL_VALVULA 14
#define REL_DRENAGEM 27
#define REL_RESIST 26
#define REL_CIRCULA 25
#define REL_DOSADOR 33
// === PINOS DE SENSORES ===
#define SENSOR_NIVEL_ALTO 32
#define SENSOR_NIVEL_BAIXO 19
#define SENSOR_TEMP 34
#define SENSOR_TRANSBORDO 4
// === BOTÃO INÍCIO/PAUSA ===
#define BOTAO_START_PAUSE 13
bool emExecucao = false;
bool cicloFinalizado = false;
// Estados dos relés para retomar
bool estadoValvula = false;
bool estadoDreno = false;
bool estadoCircula = false;
bool estadoResist = false;
bool estadoDosador = false;
// === TEMPOS DE ETAPAS (em milissegundos) ===
#define TEMPO_CIRCULA_FRIO (8 * 60 * 100UL)
#define TEMPO_AQUECIMENTO_MAX (15 * 60 * 100L)
#define TEMPO_CIRCULA_QUENTE (40 * 60 * 100UL)
#define TEMPO_ENXAGUE_QUENTE (2 * 60 * 100UL)
#define TEMPO_SECAGEM (30 * 60 * 100UL)
#define TEMP_LIMITE_CIRCULA 60.0 // Celsius
#define TEMPO_ABASTECER 12000
int erroCodigo = 0; // 0 = sem erro, 1 = falha drenagem, 2 = transbordo
void setup() {
Serial.begin(115200);
pinMode(REL_VALVULA, OUTPUT);
pinMode(REL_DRENAGEM, OUTPUT);
pinMode(REL_RESIST, OUTPUT);
pinMode(REL_CIRCULA, OUTPUT);
pinMode(REL_DOSADOR, OUTPUT);
pinMode(SENSOR_NIVEL_ALTO, INPUT_PULLUP);
pinMode(SENSOR_NIVEL_BAIXO, INPUT_PULLUP);
pinMode(SENSOR_TEMP, INPUT);
pinMode(BOTAO_START_PAUSE, INPUT_PULLUP);
pinMode(SENSOR_TRANSBORDO, INPUT_PULLUP);
desligarTudo();
Serial.println("=== Sistema pronto. Pressione o botão para iniciar. ===");
}
void loop() {
aguardarExecucao();
if (emExecucao && !cicloFinalizado) {
lavarI();
lavarII();
enxague(1);
enxague(2);
enxagueComSecante();
secagemFinal();
Serial.println("=== CICLO CONCLUÍDO ===");
desligarTudo();
cicloFinalizado = true;
emExecucao = false;
}
delay(100);
}
void aguardarExecucao() {
static bool ultimoEstadoBotao = HIGH;
bool estadoAtualBotao = digitalRead(BOTAO_START_PAUSE);
// Detecta borda de descida (botão pressionado)
if (estadoAtualBotao == LOW && ultimoEstadoBotao == HIGH) {
// Só alterna se não houver erro ativo (para não despausar automaticamente durante erro)
if (erroCodigo == 0) {
emExecucao = !emExecucao;
if (!emExecucao) {
Serial.println("\n⏸️ Pausado. Relés desligados.");
desligarTudo();
} else {
Serial.println("▶️ Retomando execução e restaurando relés.");
restaurarReleAnterior();
}
} else {
// Não mostra aviso se já estiver pausado por erro
if (emExecucao) {
Serial.printf("⚠️ Erro %d ativo. Corrija o problema antes de retomar.\n", erroCodigo);
}
}
delay(300); // debounce
}
ultimoEstadoBotao = estadoAtualBotao;
// Monitoramento contínuo de transbordo
if (digitalRead(SENSOR_TRANSBORDO) == LOW && erroCodigo == 0) {
erroCodigo = 2;
Serial.println("❌ ERRO 2: Transbordo detectado! Pausando sistema imediatamente.");
emExecucao = false;
desligarTudo();
}
}
void desligarTudo() {
estadoValvula = digitalRead(REL_VALVULA);
estadoDreno = digitalRead(REL_DRENAGEM);
estadoCircula = digitalRead(REL_CIRCULA);
estadoResist = digitalRead(REL_RESIST);
estadoDosador = digitalRead(REL_DOSADOR);
digitalWrite(REL_VALVULA, LOW);
digitalWrite(REL_DRENAGEM, LOW);
digitalWrite(REL_RESIST, LOW);
digitalWrite(REL_CIRCULA, LOW);
digitalWrite(REL_DOSADOR, LOW);
}
void restaurarReleAnterior() {
digitalWrite(REL_VALVULA, estadoValvula);
digitalWrite(REL_DRENAGEM, estadoDreno);
digitalWrite(REL_CIRCULA, estadoCircula);
//digitalWrite(REL_RESIST, estadoResist);
digitalWrite(REL_DOSADOR, estadoDosador);
}
// Pausa centralizada com tratamento de erro específico.
// codigo: número do erro (ex: 1,2,4). motivo: texto para log.
void pausarEEsperar(int codigo, const char* motivo) {
erroCodigo = codigo;
emExecucao = false;
desligarTudo();
Serial.printf("❌ ERRO %d: %s\n", codigo, motivo);
Serial.println("⏸️ Sistema pausado por segurança. Pressione o botão START/PAUSE para retomar.");
static bool ultimoEstadoBotaoLocal = HIGH;
while (!emExecucao) {
aguardarExecucao();
bool estadoAtualBotao = digitalRead(BOTAO_START_PAUSE);
if (estadoAtualBotao == LOW && ultimoEstadoBotaoLocal == HIGH) {
Serial.println("▶️ Botão START/PAUSE pressionado — tentativa de retomada...");
switch (erroCodigo) {
case 4: // Falha ao encher água
Serial.println("🔁 Tentando novamente encher água...");
erroCodigo = 0;
encherAgua();
break;
case 1: // Falha ao drenar água
Serial.println("🔁 Tentando novamente drenar água...");
erroCodigo = 0;
drenarAgua(); // essa função já trata erro novamente se falhar
break;
case 2: // Transbordo — exige intervenção
Serial.println("⚠️ Transbordo detectado. Retomada não permitida.");
delay(300);
ultimoEstadoBotaoLocal = estadoAtualBotao;
continue;
default:
Serial.println("ℹ️ Erro desconhecido — retomada não permitida.");
delay(300);
ultimoEstadoBotaoLocal = estadoAtualBotao;
continue;
}
// Se chegou aqui, a recuperação foi bem-sucedida
emExecucao = true;
//restaurarReleAnterior();
Serial.println("✅ Retomando execução normalmente após correção.");
delay(300);
break;
}
ultimoEstadoBotaoLocal = estadoAtualBotao;
delay(100);
}
}
void delayComPausa(unsigned long duracao, const char* nomeEtapa) {
Serial.printf("Iniciando etapa: %s (%.1f segundos)\n", nomeEtapa, duracao / 1000.0);
unsigned long tempoExecutado = 0;
unsigned long tickAnterior = millis();
bool estavaPausado = false;
while (tempoExecutado < duracao) {
aguardarExecucao();
if (emExecucao) {
if (estavaPausado) {
tickAnterior = millis(); // Reinicia referência ao retomar
estavaPausado = false;
Serial.println("▶️ Retomando execução e restaurando relés.");
}
unsigned long agora = millis();
tempoExecutado += (agora - tickAnterior);
tickAnterior = agora;
float restante = (duracao - tempoExecutado) / 1000.0;
Serial.printf("\u23f3 [%s] Restante: %.1f segundos\n", nomeEtapa, restante);
} else {
if (!estavaPausado) {
Serial.println("\u23f8 Pausado. Relés desligados.");
estavaPausado = true;
}
tickAnterior = millis(); // Atualiza referência sem acumular tempo
}
delay(100);
}
Serial.println(); // Finaliza etapa
}
bool encherAgua() {
Serial.println("⏳ Enchendo água...");
digitalWrite(REL_VALVULA, HIGH);
unsigned long inicio = millis();
while (digitalRead(SENSOR_NIVEL_ALTO) == HIGH) {
aguardarExecucao();
if (millis() - inicio > TEMPO_ABASTECER) {
digitalWrite(REL_VALVULA, LOW);
pausarEEsperar(4, "Nível alto NÃO detectado em até 4 minutos");
return false;
}
delay(100);
}
digitalWrite(REL_VALVULA, LOW);
Serial.println("✅ Boia de nível alto detectada.");
return true;
}
void drenarAgua() {
Serial.println("⏳ Drenando...");
digitalWrite(REL_DRENAGEM, HIGH);
unsigned long inicio = millis();
bool erroDetectado = false;
while (digitalRead(SENSOR_NIVEL_BAIXO) == HIGH) {
aguardarExecucao();
// Verifica se o tempo de drenagem ultrapassou 2 minutos
if (millis() - inicio > 120000) {
erroDetectado = true;
break;
}
delay(100);
}
digitalWrite(REL_DRENAGEM, LOW);
if (erroDetectado) {
Serial.println("❌ Erro 1: Tempo de drenagem excedeu 2 minutos.");
pausarEEsperar(1, "Drenagem excedeu o tempo máximo");
return;
} else {
Serial.println("✅ Boia de nível baixo detectada.");
}
}
void circularFrio(unsigned long tempo) {
Serial.println("♻️ Circulando frio...");
digitalWrite(REL_CIRCULA, HIGH);
delayComPausa(tempo, "Circulação Fria");
digitalWrite(REL_CIRCULA, LOW);
}
void circularQuente(unsigned long tempoTotal, float tempLimiteC) {
Serial.println("🔥 Circulação com aquecimento...");
unsigned long inicio = millis();
unsigned long tempoExecutado = 0;
unsigned long tickAnterior = millis();
bool estavaPausado = false;
digitalWrite(REL_CIRCULA, HIGH); // Bomba sempre ligada
bool resistLigada = false;
while (tempoExecutado < tempoTotal) {
aguardarExecucao();
if (emExecucao) {
if (estavaPausado) {
tickAnterior = millis(); // ressincroniza o tempo ao retomar
estavaPausado = false;
Serial.println("▶️ Retomando execução e restaurando relés.");
}
float temp = lerTemperatura();
Serial.printf("🌡️ Temp atual: %.2f°C\n", temp);
// Controle da resistência com histerese
if (temp < tempLimiteC - 1) {
if (!resistLigada) {
digitalWrite(REL_RESIST, HIGH);
resistLigada = true;
Serial.println("⚡ Resistência LIGADA para aquecer");
}
} else if (temp > tempLimiteC + 1) {
if (resistLigada) {
digitalWrite(REL_RESIST, LOW);
resistLigada = false;
Serial.println("🧊 Resistência DESLIGADA para manter");
}
}
// Atualiza tempo
unsigned long agora = millis();
tempoExecutado += (agora - tickAnterior);
tickAnterior = agora;
float restante = (tempoTotal - tempoExecutado) / 1000.0;
Serial.printf("⏳ [Circulação Quente] Restante: %.1f segundos\n", restante);
} else {
if (!estavaPausado) {
Serial.println("⏸️ Pausado. Relés desligados.");
estavaPausado = true;
digitalWrite(REL_RESIST, LOW); // segurança
}
tickAnterior = millis(); // mantém referência atualizada
}
delay(100);
}
digitalWrite(REL_RESIST, LOW);
digitalWrite(REL_CIRCULA, LOW);
Serial.println("✅ Etapa de circulação quente finalizada.");
}
void aplicarSecante() {
Serial.println("💧 Aplicando secante...");
digitalWrite(REL_DOSADOR, HIGH);
delayComPausa(1000, "Dosador");
digitalWrite(REL_DOSADOR, LOW);
}
float lerTemperatura() {
int leitura = analogRead(SENSOR_TEMP);
//float tensao = leitura * (3.3 / 4095.0);
//float celsius = 1 / (log(1 / (1023. / leitura - 1)) / 3950 + 1.0 / 298.15) - 273.15;
//return tensao * 100.0;
return leitura/51.42;
//return 60;
//return (tensao - 0.5) * (100.0 / 2.5);
}
// === ETAPAS ===
void lavarI() {
Serial.println("[LAVAR I]");
aguardarExecucao();
encherAgua();
aguardarExecucao(); circularFrio(TEMPO_CIRCULA_FRIO);
aguardarExecucao(); drenarAgua();
}
void lavarII() {
Serial.println("[LAVAR II]");
aguardarExecucao();
encherAgua();
aguardarExecucao(); circularQuente(TEMPO_AQUECIMENTO_MAX, TEMP_LIMITE_CIRCULA);
aguardarExecucao(); drenarAgua();
}
void enxague(int numero) {
Serial.printf("[ENXAGUE %d]\n", numero);
aguardarExecucao();
if (!encherAgua()) return;
aguardarExecucao(); circularQuente(TEMPO_ENXAGUE_QUENTE, TEMP_LIMITE_CIRCULA);
aguardarExecucao(); drenarAgua();
}
void enxagueComSecante() {
Serial.println("[ENXAGUE 3 COM SECANTE]");
aguardarExecucao();
if (!encherAgua()) return;
aguardarExecucao(); aplicarSecante();
aguardarExecucao(); circularQuente(TEMPO_ENXAGUE_QUENTE, TEMP_LIMITE_CIRCULA);
aguardarExecucao(); drenarAgua();
}
void secagemFinal() {
Serial.println("[SECAGEM FINAL]");
//digitalWrite(REL_RESIST, HIGH);
delayComPausa(TEMPO_SECAGEM, "Secagem");
digitalWrite(REL_RESIST, LOW);
}
VALVULA
RESISTENCIA
DRENAGEM
CIRCULAÇÃO
INICIO/PAUSE
DOSADOR
BOIA TRANSBORDO