#include <Arduino.h>
#include "Button.h"
#include "ACI_10K_an.h"
#include "LED.h" // Biblioteca para controle de LEDs
#include <Wire.h> // Biblioteca para comunicação I2C
#include "LiquidCrystal_I2C.h" // Biblioteca para LCD I2C
// ------------------------ Pinos e Objetos ------------------------
Button button2(7); // AMARELO - alterVeloc
Button button5(8); // PRETO - alterna Modo (Manual/Auto)
Button button1(A2); // AZUL - bombeamento
Button button3(A3); // VERMELHO - ionizador
Button button4(A4); // VERDE - oscilador
const int PIN_SENSOR_NIVEL = 2; // entrada com pull-up (LOW = OK, HIGH = vazio)
const int PIN_BUZZER = 3; // buzzer (tone)
const int PIN_BOMBA = 10; // bomba
const int PIN_OSCILADOR = 11; // oscilador
const int PIN_IONIZADOR = 12; // ionizador
const int PIN_LED_ALARME = 13; // LED onboard (nível/alertas)
const int leds[] = {9, 6, 5, 4}; // Pinos dos LEDs (9/6/5 = PWM; 4 = digital)
LED led0(leds[0]);
LED led1(leds[1]);
LED led2(leds[2]);
LED led3(leds[3]);
// LCD I2C (endereço comum: 0x27 ou 0x3F)
LiquidCrystal_I2C lcd(0x27, 16, 2);
// ------------------------ Estados e Variáveis ------------------------
int alterVeloc = 1;
int nivelTanque = HIGH;
int value = HIGH;
unsigned long tempoAnterior = 0;
const unsigned long intervaloLeitura = 100; // ms
int fanSpeedPWM = 0;
bool modoAutomatico = false; // Manual=botões | Auto=temperatura
float temperaturaAtual = NAN; // atualizado periodicamente
// ------------------------ EFEITOS NÃO-BLOQUEANTES (modos 4–6) ------------------------
enum EffectPhase : uint8_t { FADE_IN, HOLD_MAX, FADE_OUT, HOLD_MIN, EFFECT_OFF };
struct LedEffect {
uint8_t pin; // pino PWM para o efeito (leds[0..2])
EffectPhase phase; // fase atual
int brilho; // brilho atual (0..255)
int brilhoMax; // teto para este ciclo (aleatório)
unsigned long lastStep; // último timestamp (millis) da fase/step
unsigned long stepIntervalMs;// intervalo entre steps (ms) -> "velocidade"
unsigned long holdMaxMs; // pausa no topo
unsigned long holdMinMs; // pausa no zero
bool active; // efeito ligado?
};
// Três efeitos: um para cada modo 4, 5, 6 (leds[0], leds[1], leds[2])
LedEffect fx[3];
// ------------------------ Protótipos ------------------------
void configurarPinos();
void velocidade();
void autaTemperatura();
void bombeamento();
void oscilador();
void ionizador();
void atualizarDisplay();
void autocontroleTermico();
float lerTemperatura();
// Utilitários de efeito
void startEffect(LedEffect &e, uint8_t pin, int brilhoMaximo, unsigned long velMs);
void stopEffect(LedEffect &e);
void updateEffect(LedEffect &e);
void updateAllEffects();
// Função utilitária: mapeia float [in_min..in_max] -> int PWM [0..255]
static inline int mapFloatToPWM(float x, float in_min, float in_max) {
if (isnan(x)) return 0;
if (x < in_min) x = in_min;
if (x > in_max) x = in_max;
float frac = (x - in_min) / (in_max - in_min); // 0..1
int pwm = (int)round(frac * 255.0f);
if (pwm < 0) pwm = 0;
if (pwm > 255) pwm = 255;
return pwm;
}
// ------------------------ Setup / Loop ------------------------
void setup() {
Serial.begin(9600);
configurarPinos();
button1.begin();
button2.begin();
button3.begin();
button4.begin();
button5.begin();
// Semente randômica
randomSeed(analogRead(A0));
// LCD
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("Iniciando...");
lcd.setCursor(0, 1);
lcd.print("Aguarde...");
delay(1500);
lcd.clear();
// Primeiras leituras para iniciar
temperaturaAtual = lerTemperatura();
value = digitalRead(PIN_SENSOR_NIVEL);
// Inicializa efeitos como inativos
for (int i = 0; i < 3; i++) {
fx[i].active = false;
fx[i].phase = EFFECT_OFF;
fx[i].pin = leds[i]; // mapeamento natural
}
}
void loop() {
// Atualiza a leitura da temperatura por tempo (não bloqueante)
if (millis() - tempoAnterior >= intervaloLeitura) {
tempoAnterior = millis();
temperaturaAtual = lerTemperatura();
Serial.print("Temperatura: ");
Serial.print(temperaturaAtual, 1);
Serial.println(" C");
}
// Alterna modo Manual/Automático (botão 5)
if (button5.pressed()) {
modoAutomatico = !modoAutomatico;
tone(PIN_BUZZER, 440, 200);
// Para efeitos e limpa PWM/LEDs visuais ao trocar de modo
for (int i = 0; i < 3; i++) stopEffect(fx[i]);
analogWrite(leds[0], 0);
analogWrite(leds[1], 0);
analogWrite(leds[2], 0);
}
// Controle principal de velocidade (um modo ou outro, não ambos)
if (modoAutomatico) {
autocontroleTermico();
} else {
velocidade(); // inclui updateAllEffects() internamente
}
// Funções auxiliares
autaTemperatura(); // mantém alarme simples (bloqueante curto)
bombeamento();
oscilador();
ionizador();
atualizarDisplay();
}
// ------------------------ Implementações ------------------------
void configurarPinos() {
// Entradas
pinMode(PIN_SENSOR_NIVEL, INPUT_PULLUP); // sensor de nível no pino 2 (pull-up interna)
// Os botões são gerenciados pela lib Button; se precisar, ative PULLUP conforme seu hardware.
// Saídas
pinMode(PIN_BUZZER, OUTPUT);
pinMode(PIN_BOMBA, OUTPUT);
pinMode(PIN_OSCILADOR, OUTPUT);
pinMode(PIN_IONIZADOR, OUTPUT);
pinMode(PIN_LED_ALARME, OUTPUT);
// LEDs
for (uint8_t i = 0; i < 4; i++) {
pinMode(leds[i], OUTPUT);
analogWrite(leds[i], 0);
}
// **NÃO** configurar A4/A5 (I2C) como saída
// **NÃO** configurar pino 1 (TX) como saída -> conflita com Serial
}
float lerTemperatura() {
// Ajuste estes parâmetros conforme sua placa/sensor:
// Aci_10K an10k(Vref, resolucao_bits)
// Em Uno, ADC é 10 bits. Se seu sensor usa 3.3V como referência real, ok.
// Se usar 5V, troque 3.3 por 5.0 e 14 por 10 (dependendo da lib).
Aci_10K an10k(3.3, 14);
delayMicroseconds(500);
int leituraADC = analogRead(A1);
return an10k.getTemp(leituraADC); // °C
}
void autaTemperatura() {
// Usa a leitura global (consistente)
float T = temperaturaAtual;
if (!isnan(T) && T >= 30.0f) {
// Aviso visual/sonoro simples (mantido como no seu código original)
analogWrite(leds[3], 255); // leds[3] = pino 4 (on/off)
tone(PIN_BUZZER, 262, 250);
delay(100);
analogWrite(leds[3], 0);
noTone(PIN_BUZZER);
delay(100);
} else {
analogWrite(leds[3], 0);
noTone(PIN_BUZZER);
}
}
void autocontroleTermico() {
// Converte 22~32 °C -> 0~255 PWM
int pwmValue = mapFloatToPWM(temperaturaAtual, 22.0f, 32.0f);
// Usa leds[2] como “fan/motor”
analogWrite(leds[2], pwmValue);
// Garante os demais apagados (somente o "canal" do motor em uso)
analogWrite(leds[0], 0);
analogWrite(leds[1], 0);
}
// ------------------------ EFEITOS NÃO-BLOQUEANTES ------------------------
void startEffect(LedEffect &e, uint8_t pin, int brilhoMaximo, unsigned long velMs) {
e.pin = pin;
e.brilho = 0;
e.brilhoMax = constrain(brilhoMaximo, 0, 255);
e.stepIntervalMs = max(1ul, velMs); // velocidade em ms por step (quanto MENOR, mais rápido)
e.holdMaxMs = 1500; // equivalente ao seu delay(1500)
e.holdMinMs = random(0, 501); // equivalente ao seu delay(random(500))
e.phase = FADE_IN;
e.lastStep = millis();
e.active = true;
analogWrite(e.pin, 0);
}
void stopEffect(LedEffect &e) {
e.active = false;
e.phase = EFFECT_OFF;
analogWrite(e.pin, 0);
}
void updateEffect(LedEffect &e) {
if (!e.active || e.phase == EFFECT_OFF) return;
unsigned long now = millis();
switch (e.phase) {
case FADE_IN:
if (now - e.lastStep >= e.stepIntervalMs) {
e.lastStep = now;
e.brilho++;
if (e.brilho >= e.brilhoMax) {
e.brilho = e.brilhoMax;
e.phase = HOLD_MAX;
e.lastStep = now; // reinicia cronômetro para hold
}
analogWrite(e.pin, e.brilho);
}
break;
case HOLD_MAX:
if (now - e.lastStep >= e.holdMaxMs) {
e.phase = FADE_OUT;
e.lastStep = now;
}
break;
case FADE_OUT:
if (now - e.lastStep >= e.stepIntervalMs) {
e.lastStep = now;
e.brilho--;
if (e.brilho <= 0) {
e.brilho = 0;
e.phase = HOLD_MIN;
e.lastStep = now;
// próxima pausa mínima varia para ficar orgânico
e.holdMinMs = random(0, 501);
}
analogWrite(e.pin, e.brilho);
}
break;
case HOLD_MIN:
if (now - e.lastStep >= e.holdMinMs) {
// Novo ciclo com novos parâmetros para variar o ritmo
e.brilhoMax = random(120, 256); // 120..255 (ajuste à vontade)
e.stepIntervalMs = random(2, 7); // 2..6 ms por step (igual ao seu)
e.phase = FADE_IN;
e.lastStep = now;
}
break;
default:
break;
}
}
void updateAllEffects() {
for (int i = 0; i < 3; i++) updateEffect(fx[i]);
}
// ------------------------ Velocidade (com efeitos NB nos modos 4–6) ------------------------
void velocidade() {
// Brilho base a partir da temperatura (18~30 °C -> 100~255)
int brightness = mapFloatToPWM(temperaturaAtual, 18.0f, 30.0f);
if (brightness < 100) brightness = 100; // piso
// Se apertar o botão, troca modo
if (button2.pressed()) {
tone(PIN_BUZZER, 262, 250);
alterVeloc++;
if (alterVeloc > 6) alterVeloc = 1;
// Ao trocar de modo, desligamos todos os efeitos em execução
for (int i = 0; i < 3; i++) stopEffect(fx[i]);
analogWrite(leds[0], 0);
analogWrite(leds[1], 0);
analogWrite(leds[2], 0);
}
// Modos:
switch (alterVeloc) {
case 1:
// Fixo no leds[0]
for (int i = 0; i < 3; i++) stopEffect(fx[i]);
analogWrite(leds[0], 255);
analogWrite(leds[1], 0);
analogWrite(leds[2], 0);
break;
case 2:
// Fixo no leds[1]
for (int i = 0; i < 3; i++) stopEffect(fx[i]);
analogWrite(leds[0], 0);
analogWrite(leds[1], 255);
analogWrite(leds[2], 0);
break;
case 3:
// Fixo no leds[2]
for (int i = 0; i < 3; i++) stopEffect(fx[i]);
analogWrite(leds[0], 0);
analogWrite(leds[1], 0);
analogWrite(leds[2], 255);
break;
case 4: {
// Fade não-bloqueante no leds[0]
if (!fx[0].active) {
int brilhoMaximo = random(brightness + 1); // 0..brightness
unsigned long velMs = random(2, 7); // 2..6 ms por step
startEffect(fx[0], leds[0], brilhoMaximo, velMs);
stopEffect(fx[1]);
stopEffect(fx[2]);
analogWrite(leds[1], 0);
analogWrite(leds[2], 0);
}
break;
}
case 5: {
// Fade não-bloqueante no leds[1]
if (!fx[1].active) {
int brilhoMaximo = random(brightness + 1);
unsigned long velMs = random(2, 7);
startEffect(fx[1], leds[1], brilhoMaximo, velMs);
stopEffect(fx[0]);
stopEffect(fx[2]);
analogWrite(leds[0], 0);
analogWrite(leds[2], 0);
}
break;
}
case 6: {
// Fade não-bloqueante no leds[2]
if (!fx[2].active) {
int brilhoMaximo = random(brightness + 1);
unsigned long velMs = random(2, 7);
startEffect(fx[2], leds[2], brilhoMaximo, velMs);
stopEffect(fx[0]);
stopEffect(fx[1]);
analogWrite(leds[0], 0);
analogWrite(leds[1], 0);
}
break;
}
default:
alterVeloc = 1;
break;
}
// **Essencial**: atualiza efeitos a cada loop
updateAllEffects();
}
// ------------------------ Demais funções ------------------------
void bombeamento() {
// Botão AZUL alterna a bomba (respeitando o nível)
if (button1.toggled() && button1.read() == Button::PRESSED) {
tone(PIN_BUZZER, 262, 250);
bool nivelOK = (digitalRead(PIN_SENSOR_NIVEL) == LOW);
if (nivelOK) {
// Toggle da bomba
digitalWrite(PIN_BOMBA, !digitalRead(PIN_BOMBA));
} else {
// Segurança: nível vazio -> força desligado
digitalWrite(PIN_BOMBA, LOW);
}
}
// Monitora mudança de nível e aplica segurança
int novoValor = digitalRead(PIN_SENSOR_NIVEL);
if (novoValor != value) {
value = novoValor;
if (value == HIGH) { // HIGH = sem água (com pull-up)
digitalWrite(PIN_LED_ALARME, HIGH);
digitalWrite(PIN_BOMBA, LOW); // segurança: desliga bomba
tone(PIN_BUZZER, 262, 250);
Serial.println("Nivel: VAZIO");
} else {
digitalWrite(PIN_LED_ALARME, LOW);
Serial.println("Nivel: OK");
}
}
}
void oscilador() {
if (button4.toggled() && button4.read() == Button::PRESSED) {
tone(PIN_BUZZER, 262, 250);
digitalWrite(PIN_OSCILADOR, !digitalRead(PIN_OSCILADOR));
}
}
void ionizador() {
if (button3.toggled() && button3.read() == Button::PRESSED) {
tone(PIN_BUZZER, 262, 250);
digitalWrite(PIN_IONIZADOR, !digitalRead(PIN_IONIZADOR));
}
}
void atualizarDisplay() {
static unsigned long ultimoUpdate = 0;
if (millis() - ultimoUpdate >= 1000) {
ultimoUpdate = millis();
lcd.clear();
// Linha 0: Temperatura e Nível de Água
lcd.setCursor(0, 0);
lcd.print("T:");
if (isnan(temperaturaAtual)) {
lcd.print("--.-");
} else {
lcd.print(temperaturaAtual, 1);
}
lcd.print("C");
lcd.setCursor(10, 0);
lcd.print((value == LOW) ? "H2O:OK" : "VAZIO!");
// Linha 1: Modo de Operação
lcd.setCursor(0, 1);
if (modoAutomatico) {
int pot = mapFloatToPWM(temperaturaAtual, 22.0f, 32.0f) * 100 / 255;
if (pot < 0) pot = 0;
if (pot > 100) pot = 100;
lcd.print("AUTO: ");
lcd.print(pot);
lcd.print("%");
} else {
lcd.print("MANUAL: M-");
lcd.print(alterVeloc);
}
}
}