#include #include #include #include // ==== PIN CONFIGURATION ==== #define DS18B20_PIN_1 2 #define DS18B20_PIN_2 3 #define RELAY_PIN 7 // ==== TEMPERATURE CONTROL CONFIG ==== #define TARGET_TEMP 39.0 #define HYSTERESIS 0.5 #define TEMP_MIN (TARGET_TEMP - HYSTERESIS) #define TEMP_MAX (TARGET_TEMP + HYSTERESIS) #define SENSOR1_OFFSET 0.61 #define SENSOR2_OFFSET 1.43 // ==== SCREEN CONTROL ==== #define SCREEN_SENSORS 0 #define SCREEN_CONTROL 1 #define SCREEN_GRADIENT 2 #define SCREEN_COUNT 3 #define SCREEN_TIME_MS 3000 // ==== GRADIENT CONFIG ==== #define GRADIENT_INTERVAL_MS 30000 // ==== CUSTOM CHARACTER (°C) ==== byte celsiusChar[8] = { B01110, B01010, B01110, B00000, B00000, B00000, B00000, B00000 }; // ==== INSTANCES ==== OneWire oneWire1(DS18B20_PIN_1); OneWire oneWire2(DS18B20_PIN_2); DallasTemperature sensor1(&oneWire1); DallasTemperature sensor2(&oneWire2); LiquidCrystal_I2C lcd(0x27, 16, 2); // ==== GLOBAL VARIABLES ==== float calibratedTemp1 = NAN, calibratedTemp2 = NAN, controlTemp = NAN; bool relayIsOn = false; unsigned long relayOnCycles = 0; int activeScreen = 0; unsigned long lastScreenSwitch = 0; // Gradient vars float lastTemp = NAN; unsigned long lastGradientTime = 0; float gradientCelsiusPerMin = 0.0; bool isFirstGradient = true; float gradientSamples[5] = {0}; int gradientIdx = 0; unsigned long lastLogTime = 0; // ==== SETUP ==== void setup() { Serial.begin(9600); Serial.println("=== AQUARIO CONTROLLER ==="); sensor1.begin(); sensor2.begin(); sensor1.setResolution(11); sensor2.setResolution(11); pinMode(RELAY_PIN, OUTPUT); digitalWrite(RELAY_PIN, LOW); relayIsOn = false; lcd.init(); lcd.backlight(); lcd.createChar(0, celsiusChar); // Splash screen lcd.clear(); lcd.setCursor(0, 0); lcd.print("AQUARIO CTRL"); lcd.setCursor(0, 1); lcd.print("Diff: corrigido"); delay(2000); lastScreenSwitch = millis(); lastGradientTime = millis(); lastLogTime = millis(); } // ==== MAIN LOOP ==== void loop() { unsigned long now = millis(); updateSensors(); updateGradient(now); controlHeater(); // Switch screens if (now - lastScreenSwitch >= SCREEN_TIME_MS) { activeScreen = (activeScreen + 1) % SCREEN_COUNT; lastScreenSwitch = now; } showScreen(); logSerialStatus(now); } // ==== SENSOR UPDATE ==== void updateSensors() { sensor1.requestTemperatures(); sensor2.requestTemperatures(); float raw1 = sensor1.getTempCByIndex(0); float raw2 = sensor2.getTempCByIndex(0); if (raw1 != DEVICE_DISCONNECTED_C) { calibratedTemp1 = round2(raw1 + SENSOR1_OFFSET); } else { calibratedTemp1 = NAN; } if (raw2 != DEVICE_DISCONNECTED_C) { calibratedTemp2 = round2(raw2 + SENSOR2_OFFSET); } else { calibratedTemp2 = NAN; } if (!isnan(calibratedTemp1)) { controlTemp = calibratedTemp1; } else if (!isnan(calibratedTemp2)) { controlTemp = calibratedTemp2; } else { controlTemp = NAN; } } float round2(float val) { return round(val * 100.0) / 100.0; } // ==== GRADIENT UPDATE ==== void updateGradient(unsigned long now) { if (now - lastGradientTime < GRADIENT_INTERVAL_MS) return; if (!isnan(controlTemp)) { if (!isFirstGradient && !isnan(lastTemp)) { float dT = controlTemp - lastTemp; float dTime = (now - lastGradientTime) / 1000.0; if (dTime > 0) { float grad = constrain(dT / dTime * 60.0, -5.0, 5.0); gradientSamples[gradientIdx] = grad; gradientIdx = (gradientIdx + 1) % 5; float sum = 0; for (int i = 0; i < 5; ++i) sum += gradientSamples[i]; gradientCelsiusPerMin = round(sum / 5.0 * 4) / 4.0; Serial.print("*** GRADIENTE: "); Serial.print(gradientCelsiusPerMin, 2); Serial.println(" °C/min ***"); } } else { isFirstGradient = false; for (int i = 0; i < 5; ++i) gradientSamples[i] = 0.0; gradientCelsiusPerMin = 0.0; Serial.println("*** GRADIENTE INICIALIZADO ***"); } lastTemp = controlTemp; } lastGradientTime = now; } // ==== RELAY CONTROL ==== void controlHeater() { if (!isnan(controlTemp)) { if (!relayIsOn && controlTemp < TEMP_MIN) { digitalWrite(RELAY_PIN, HIGH); relayIsOn = true; relayOnCycles++; Serial.println("🔥 Aquecedor LIGADO"); } else if (relayIsOn && controlTemp > TEMP_MAX) { digitalWrite(RELAY_PIN, LOW); relayIsOn = false; Serial.println("❄️ Aquecedor DESLIGADO"); } } } // ==== SCREEN HANDLING ==== void showScreen() { switch (activeScreen) { case SCREEN_SENSORS: showSensorScreen(); break; case SCREEN_CONTROL: showControlScreen(); break; case SCREEN_GRADIENT: showGradientScreen(); break; } } void showSensorScreen() { lcd.clear(); float diff = 0.0; bool showDiff = (!isnan(calibratedTemp1) && !isnan(calibratedTemp2)); if (showDiff) diff = calibratedTemp1 - calibratedTemp2; lcd.setCursor(0, 0); lcd.print("1:"); if (!isnan(calibratedTemp1)) { lcd.print(calibratedTemp1, 2); lcd.write(0); } else { lcd.print("ERR"); } lcd.setCursor(11, 0); lcd.print("Diff:"); lcd.setCursor(0, 1); lcd.print("2:"); if (!isnan(calibratedTemp2)) { lcd.print(calibratedTemp2, 2); lcd.write(0); } else { lcd.print("ERR"); } lcd.setCursor(11, 1); if (showDiff) { lcd.print((diff >= 0 ? "+" : "-")); lcd.print(abs(diff), 2); } else { lcd.print("---.--"); } } void showControlScreen() { lcd.clear(); lcd.setCursor(0, 0); lcd.print("Aquecedor: "); lcd.print(relayIsOn ? "ON" : "OFF"); lcd.setCursor(0, 1); lcd.print("Alvo: "); lcd.print(TARGET_TEMP, 2); lcd.write(0); lcd.print("C"); } void showGradientScreen() { lcd.clear(); lcd.setCursor(0, 0); lcd.print("GRADIENTE TEMP"); lcd.setCursor(0, 1); if (gradientCelsiusPerMin >= 0) lcd.print("+"); lcd.print(gradientCelsiusPerMin, 2); lcd.write(0); lcd.print("C/min"); } // ==== SERIAL LOGGING ==== void logSerialStatus(unsigned long now) { if (now - lastLogTime < 2000) return; float diff = (!isnan(calibratedTemp1) && !isnan(calibratedTemp2)) ? calibratedTemp1 - calibratedTemp2 : 0.0; Serial.print("Tela: "); Serial.print(activeScreen + 1); Serial.print("/3 | S1: "); Serial.print(!isnan(calibratedTemp1) ? calibratedTemp1 : -999, 2); Serial.print("°C | S2: "); Serial.print(!isnan(calibratedTemp2) ? calibratedTemp2 : -999, 2); Serial.print("°C | Diff: "); if (diff >= 0) Serial.print("+"); Serial.print(diff, 2); Serial.print("°C | Aquec: "); Serial.print(relayIsOn ? "ON" : "OFF"); Serial.print(" | Grad: "); Serial.print(gradientCelsiusPerMin, 2); Serial.println("°C/min"); lastLogTime = now; }