#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <RTClib.h> // RTC DS1307 Library
// OLED Display Configuration
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 128 // OLED display height, in pixels
Adafruit_SH1107 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire);
// DS18B20 Sensor Pin
#define ONE_WIRE_BUS GPIO_NUM_27 // Pin for DS18B20 (GPIO27 on ESP32)
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
// RTC Initialization
RTC_DS1307 rtc;
bool rtcAvailable = true; // Flag to indicate if RTC is functional
// Solar Panel Voltage Pin
#define SOLAR_PANEL_PIN GPIO_NUM_39 // Analog pin for solar panel voltage
// GPIO17 Pin Configuration
#define TP_Solar_Stop GPIO_NUM_17
// Select parameters based on mode (Simulation or Real)
bool isSimulationMode = true; // Change this to false for Real Setup
// Simulation Parameters
DeviceAddress simSolarSensorAddress = {0x10, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x7B}; // T1
DeviceAddress simSanitarSensorAddress = {0x10, 0x01, 0x02, 0x03, 0x04, 0x05, 0x07, 0x25}; // T2
const long simP1_ON_TIME = 5000; // Pump 1 cycle ON time
const long simP1_OFF_TIME = 5000; // Pump 1 cycle OFF time
const float simCLOUD_THRESHOLD = 2.0; // Voltage threshold for cloud detection
const float simTempT1Modifier = 0; // modifier for Simulated Solar temperature
// Real Setup Parameters
DeviceAddress realSolarSensorAddress = {0x28, 0xe7, 0xef, 0x07, 0xd6, 0x01, 0x3c, 0x90}; // T1
DeviceAddress realSanitarSensorAddress = {0x28, 0xFF, 0x64, 0x02, 0xeb, 0xf3, 0xaa, 0x5a}; // T2
const long realP1_ON_TIME = 15000; // Pump 1 cycle ON time
const long realP1_OFF_TIME = 60000; // Pump 1 cycle OFF time
const float realCLOUD_THRESHOLD = 2.0; // Voltage threshold for cloud detection
const float realTempT1Modifier = 2; // modifier for Simulated Solar temperature
// Selected Parameters (default to simulation)
DeviceAddress solarSensorAddress;
DeviceAddress sanitarSensorAddress;
long P1_ON_TIME;
long P1_OFF_TIME;
float CLOUD_THRESHOLD;
float TempT1Modifier;
// Timing Variables
unsigned long previousMillis = 0;
unsigned long relayMillisP1 = 0;
unsigned long systemStartTime = 0; // System start time in milliseconds
const long interval = 1000; // Display update interval (1 second)
// Additional timing variables to track pump off times
unsigned long lastPump1OffTime = 0;
unsigned long lastPump2OffTime = 0;
const long PUMP_OFF_DELAY = 2000; // 2s delay between turning off pumps
// Pump Timing Variables
unsigned long pump2RuntimeToday = 0; // Total runtime of pump P2 for the current day
unsigned long pump2LastOnTime = 0;
const unsigned long P2DelayTime = 1000; // 1000 ms odlaganja za isključivanje Pumpe P2
unsigned long p1LastOffTime = 0;
// Thresholds
const int P1_CutOFF_TEMP = 35; // Cutoff temp for Pump P1
const int DeltaT1_T2_ON = 12; // Delta temp for Pump P2 ON
const int DeltaT1_T2_OFF = 8; // Delta temp for Pump P1/2 OFF
// Status Strings
String pump1Status = "OFF";
String pump2Status = "OFF";
// Relay States
bool pump1Active = false; // State of Relay P1
bool pump2Active = false; // State of Relay P2
bool cloudDetected = false; // Flag for cloud detection
// GPIO Pin Configuration
#define RELAY_PUMP1 GPIO_NUM_32
#define RELAY_PUMP2 GPIO_NUM_23 //Pokusavam da vidim zasto se resetuje proc
// Last valid temperature values
float lastValidTempSolar = 0.00;
float lastValidTempSanitar = 0.00;
// Debounce logic variables
unsigned long lastCloudDetectedTime = 0;
const long cloudDebounceDelay = 10000; // 10 seconds debounce delay
unsigned long cloudDebounceMillis = 0; // Vreme kada je poslednji put detektovan oblak
bool cloudStable = false; // Flag koji označava da li je stanje oblaka stabilno
void setup() {
Serial.begin(115200);
// Initialize I2C with custom SDA and SCL pins (GPIO13 and GPIO14)
Wire.begin(13, 14); // SDA = GPIO13, SCL = GPIO14
// RTC Initialization
if (!rtc.begin()) {
Serial.println("RTC not found, falling back to system time.");
rtcAvailable = false;
}
if (rtcAvailable && !rtc.isrunning()) {
Serial.println("RTC is not running, setting the time.");
rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // Set RTC to compile time
}
// OLED Display Initialization
if (!display.begin(0x3C)) {
Serial.println(F("SH110X allocation failed"));
for (;;);
}
display.setRotation(0);
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(SH110X_WHITE);
display.print("Sim_Real_10");
display.display();
delay(1000);
display.clearDisplay();
// DS18B20 Initialization
sensors.begin();
// Pin Configuration
pinMode(RELAY_PUMP1, OUTPUT);
pinMode(RELAY_PUMP2, OUTPUT);
pinMode(TP_Solar_Stop, OUTPUT); // Initialize GPIO17 as output
digitalWrite(RELAY_PUMP1, HIGH);
digitalWrite(RELAY_PUMP2, HIGH);
digitalWrite(TP_Solar_Stop, LOW); // Set GPIO17 to LOW initially
// Record system start time
systemStartTime = millis();
if (isSimulationMode) {
memcpy(solarSensorAddress, simSolarSensorAddress, sizeof(DeviceAddress));
memcpy(sanitarSensorAddress, simSanitarSensorAddress, sizeof(DeviceAddress));
P1_ON_TIME = simP1_ON_TIME;
P1_OFF_TIME = simP1_OFF_TIME;
CLOUD_THRESHOLD = simCLOUD_THRESHOLD;
TempT1Modifier = simTempT1Modifier;
} else {
memcpy(solarSensorAddress, realSolarSensorAddress, sizeof(DeviceAddress));
memcpy(sanitarSensorAddress, realSanitarSensorAddress, sizeof(DeviceAddress));
P1_ON_TIME = realP1_ON_TIME;
P1_OFF_TIME = realP1_OFF_TIME;
CLOUD_THRESHOLD = realCLOUD_THRESHOLD;
TempT1Modifier = realTempT1Modifier;
}
}
float validateTemperature(float temp, float &lastValidTemp) {
if (temp != -127 && temp >= -20 && temp <= 120) {
lastValidTemp = temp; // Update last valid temperature
} else {
temp = lastValidTemp; // Use last valid temperature
}
return temp;
}
void loop() {
unsigned long currentMillis = millis();
// Temperature Readings
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
sensors.requestTemperatures();
float TempSolar = sensors.getTempC(solarSensorAddress);
float TempSanitar = sensors.getTempC(sanitarSensorAddress);
TempSolar = validateTemperature(TempSolar, lastValidTempSolar);
TempSolar = TempSolar + TempT1Modifier; // Reality correction
TempSanitar = validateTemperature(TempSanitar, lastValidTempSanitar);
// Read solar panel voltage
int rawReading = analogRead(SOLAR_PANEL_PIN); // Analog value (0-4095)
float solarVoltage = (rawReading / 4095.0) * 3.55; // Convert to voltage (0-3.3V)
// Check if voltage is between 0.5V and 0.6V
if (solarVoltage >= 0.5 && solarVoltage <= 0.6) {
// Reset elapsedTime and pump2RuntimeToday
systemStartTime = millis(); // Reset system start time
pump2RuntimeToday = 0; // Reset pump 2 runtime
Serial.println("Voltage between 0.5V and 0.6V - resetting counters");
}
// Cloud detection logic with debounce
bool currentCloudDetected = (solarVoltage < CLOUD_THRESHOLD);
if (currentCloudDetected != cloudDetected && (currentMillis - lastCloudDetectedTime >= cloudDebounceDelay)) {
cloudDetected = currentCloudDetected;
lastCloudDetectedTime = currentMillis;
}
if (cloudDetected) {
// Turn off pumps immediately
if (pump1Active) {
digitalWrite(RELAY_PUMP1, HIGH);
pump1Active = false;
pump1Status = "OFF";
Serial.println("Cloud detected, Pump 1 OFF");
}
if (pump2Active) {
digitalWrite(RELAY_PUMP2, HIGH);
pump2Active = false;
pump2Status = "OFF";
Serial.println("Cloud detected, Pump 2 OFF");
}
}
// Pump 1 Logic
if (TempSolar <= P1_CutOFF_TEMP || cloudDetected) {
if (pump1Active) {
digitalWrite(RELAY_PUMP1, HIGH); // Turn off pump
pump1Active = false;
pump1Status = "OFF";
p1LastOffTime = currentMillis; // Zabeleži vreme isključenja Pumpe P1
Serial.println("Pump 1 OFF: Low TempSolar or Cloud");
}
} else if (TempSolar >= TempSanitar + DeltaT1_T2_OFF && !cloudDetected) {
if (!pump1Active) {
digitalWrite(RELAY_PUMP1, LOW); // Turn on pump
pump1Active = true;
pump1Status = "ON";
Serial.println("Pump 1 ON: TempSolar > TempSanitar + Delta");
}
} else if (!cloudDetected) {
if (pump1Active && (currentMillis - relayMillisP1 >= P1_ON_TIME)) {
digitalWrite(RELAY_PUMP1, HIGH); // Turn off pump
pump1Active = false;
relayMillisP1 = currentMillis;
pump1Status = "CYC OFF";
p1LastOffTime = currentMillis; // Zabeleži vreme isključenja Pumpe P1
Serial.println("Pump 1 CYC OFF");
} else if (!pump1Active && (currentMillis - relayMillisP1 >= P1_OFF_TIME)) {
digitalWrite(RELAY_PUMP1, LOW); // Turn on pump
pump1Active = true;
relayMillisP1 = currentMillis;
pump1Status = "CYC ON";
Serial.println("Pump 1 CYC ON");
}
}
// Pump 2 Logic
if (pump1Active && TempSolar > TempSanitar + DeltaT1_T2_ON && !cloudDetected) {
if (!pump2Active) {
digitalWrite(RELAY_PUMP2, LOW); // Turn on Pump 2
pump2Active = true;
pump2Status = "ON";
pump2LastOnTime = currentMillis;
Serial.println("Pump 2 ON");
}
} else if ((TempSolar < TempSanitar + DeltaT1_T2_OFF || cloudDetected) &&
(currentMillis - p1LastOffTime >= P2DelayTime)) {
// Proveri da li je prošlo dovoljno vremena nakon isključenja Pumpe P1
if (pump2Active) {
digitalWrite(RELAY_PUMP2, HIGH); // Turn off Pump 2
pump2Active = false;
pump2Status = "OFF";
pump2RuntimeToday += (currentMillis - pump2LastOnTime) / 1000; // Add runtime
Serial.println("Pump 2 OFF");
}
}
if (pump2Active) {
// If Pump 2 is active, update the runtime and keep track of the time Pump 2 has been running
pump2RuntimeToday += (currentMillis - pump2LastOnTime) / 1000; // Add the runtime in seconds
pump2LastOnTime = currentMillis; // Update the last time Pump 2 was active
}
// Set GPIO17 based on pump2RuntimeToday
if (pump2RuntimeToday >= 10 && pump2RuntimeToday <= 20) {
digitalWrite(TP_Solar_Stop, HIGH); // Set GPIO17 to HIGH
} else {
digitalWrite(TP_Solar_Stop, LOW); // Set GPIO17 to LOW
}
// Display Update
display.clearDisplay();
// Temperature Display
display.setCursor(0, 0);
display.print("T1:");
display.print(TempSolar, 1);
display.setCursor(0, 15);
display.print("T2:");
display.print(TempSanitar, 1);
// Pump 1 Display with Inverted Colors if Active
if (pump1Active) {
display.fillRect(0, 0, 20, 15, SH110X_WHITE); // Draw a white rectangle
display.setTextColor(SH110X_BLACK); // Set text color to black
} else {
display.setTextColor(SH110X_WHITE); // Set text color to white
}
display.setCursor(0, 0);
display.print("T1");
// Pump 2 Display with Inverted Colors if Active
if (pump2Active) {
display.fillRect(0, 15, 20, 15, SH110X_WHITE); // Draw a white rectangle
display.setTextColor(SH110X_BLACK); // Set text color to black
} else {
display.setTextColor(SH110X_WHITE); // Set text color to white
}
display.setCursor(0, 15);
display.print("T2");
// Reset text color for other elements
display.setTextColor(SH110X_WHITE);
// Solar Voltage Display
display.setCursor(0, 60);
display.printf("PV:%.2fV", solarVoltage);
// Time Display
if (rtcAvailable) {
DateTime now = rtc.now();
display.setCursor(0, 30);
display.printf("%02d:%02d:%02d", now.hour(), now.minute(), now.second());
} else {
unsigned long elapsedTime = currentMillis - systemStartTime;
unsigned long hours = (elapsedTime / 3600000) % 24;
unsigned long minutes = (elapsedTime / 60000) % 60;
unsigned long seconds = (elapsedTime / 1000) % 60;
display.setCursor(0, 30);
display.printf("%02lu:%02lu:%02lu", hours, minutes, seconds);
}
// Pump Runtime Display
display.setCursor(0, 45);
display.printf("%ld s", pump2RuntimeToday);
display.display();
}
}Loading
grove-oled-sh1107
grove-oled-sh1107