#include <WiFi.h>
#include <HTTPClient.h>
#include <time.h>
#define BTN_START_PIN 33
#define BTN_STOP_PIN 32
#define BTN_MANUAL_PIN 34
#define OUT_PIN 21
const char* ssid = "Wokwi-GUEST";
const char* password = "";
// Google Apps Script endpoint для поливу
const char* GAS_URL = "https://script.google.com/macros/s/AKfycbz07KCNYahzYgHBy8ETcvTLSSDBhWGuehJa-xKVm7M4_n4O2BZnGeFrII4fajcdrsThNw/exec";
const char* API_KEY_APP = "APP_SECURE_KEY_2024_AbC1@dE2";
int polivCount = 0;
bool dailyWateringEnabled = true;
time_t lastWateringTime = 0;
time_t disableUntilTime = 0;
int lastDay = -1;
bool timeInitialized = false;
bool prevManual = false;
bool prevStart = false;
bool prevStop = false;
bool watering = false;
unsigned long wateringStart = 0;
const unsigned long wateringDuration = 5000; // 5 секунд поливу
struct tm timeinfo;
void setup() {
Serial.begin(115200);
pinMode(BTN_START_PIN, INPUT_PULLDOWN);
pinMode(BTN_STOP_PIN, INPUT_PULLDOWN);
pinMode(BTN_MANUAL_PIN, INPUT_PULLDOWN);
pinMode(OUT_PIN, OUTPUT);
digitalWrite(OUT_PIN, LOW);
initializeTime();
}
void loop() {
time_t now;
time(&now);
if (!getLocalTime(&timeinfo)) {
initializeTime();
time(&now);
}
// Перевірка нової доби
if (lastDay != -1 && timeinfo.tm_mday != lastDay) {
polivCount = 0;
Serial.println("🌅 New day - counter reset");
}
lastDay = timeinfo.tm_mday;
// Відновлення авто-поливу
if (!dailyWateringEnabled && disableUntilTime > 0 && now >= disableUntilTime) {
dailyWateringEnabled = true;
disableUntilTime = 0;
Serial.println("Daily watering auto-enabled after 24h timeout");
}
// ===== Перевірка кнопок =====
bool manualNow = digitalRead(BTN_MANUAL_PIN);
bool startNow = digitalRead(BTN_START_PIN);
bool stopNow = digitalRead(BTN_STOP_PIN);
if (manualNow && !prevManual) startWatering("MANUAL button");
if (startNow && !prevStart) startWatering("START button");
if (stopNow && !prevStop) stopWatering();
prevManual = manualNow;
prevStart = startNow;
prevStop = stopNow;
// ===== Автополив =====
if (dailyWateringEnabled && polivCount == 0 &&
timeinfo.tm_hour >= 8 && timeinfo.tm_hour < 10 &&
!watering) {
startWatering("AUTOMATIC morning");
}
// ===== Керування тривалістю поливу =====
if (watering && millis() - wateringStart >= wateringDuration) {
stopWatering();
}
delay(50);
}
void initializeTime() {
Serial.println("Initializing WiFi for time sync...");
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi connected!");
configTime(2 * 3600, 3600, "pool.ntp.org", "time.nist.gov");
int timeouts = 0;
while (!getLocalTime(&timeinfo) && timeouts < 10) {
delay(1000);
timeouts++;
}
if (getLocalTime(&timeinfo)) {
Serial.print("Time synchronized: ");
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
timeInitialized = true;
lastDay = timeinfo.tm_mday;
} else {
Serial.println("Failed to obtain time");
}
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
} else {
Serial.println("\nFailed to connect to WiFi");
}
}
// ===== Функції керування поливом =====
void startWatering(const char* source) {
if (polivCount < 3 && !watering) {
polivCount++;
wateringStart = millis();
watering = true;
time(&lastWateringTime);
digitalWrite(OUT_PIN, HIGH);
Serial.printf("🚿 WATERING START: %s (%d/3 today)\n", source, polivCount);
} else if (watering) {
Serial.println("⚠ Watering already in progress");
} else {
Serial.println("⚠ Maximum waterings (3) reached today - sending email");
sendPolivLimitEmail();
}
}
void stopWatering() {
if (watering) {
watering = false;
digitalWrite(OUT_PIN, LOW);
Serial.println("🛑 WATERING STOP");
}
}
void sendPolivLimitEmail() {
if (WiFi.status() != WL_CONNECTED) {
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
attempts++;
}
}
if (WiFi.status() == WL_CONNECTED) {
// Отримуємо поточну дату та час
String currentDate = getCurrentDate();
String currentTime = getCurrentTime();
HTTPClient http;
// Формуємо URL аналогічно до прикладу з вологістю
String url = String(GAS_URL) + "?api_key=" + API_KEY_APP;
url += "&date=" + currentDate;
url += "&time=" + currentTime;
url += "&water=MAX";
url += "&pump=OFF";
Serial.println("Sending request to: " + url);
http.begin(url.c_str());
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
int httpResponseCode = http.GET();
if (httpResponseCode > 0) {
Serial.printf("✓ Email sent successfully! HTTP response: %d\n", httpResponseCode);
String response = http.getString();
Serial.println("Response: " + response);
// Вимикаємо автополив на 24 години
dailyWateringEnabled = false;
time(&disableUntilTime);
disableUntilTime += 24 * 3600; // +24 години
Serial.println("Daily watering disabled for 24 hours");
} else {
Serial.printf("✗ HTTP request failed: %s\n", http.errorToString(httpResponseCode).c_str());
}
http.end();
} else {
Serial.println("✗ WiFi not connected - cannot send email");
}
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
}
String getCurrentDate() {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
return "N/A";
}
char buffer[11];
strftime(buffer, sizeof(buffer), "%d/%m/%Y", &timeinfo);
return String(buffer);
}
String getCurrentTime() {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
return "N/A";
}
char buffer[9];
strftime(buffer, sizeof(buffer), "%H:%M:%S", &timeinfo);
return String(buffer);
}