From 963549fd1e1b16992fa6300d27cda93a258a9dce Mon Sep 17 00:00:00 2001 From: Rafiabiyyuy Date: Tue, 19 Aug 2025 13:45:11 +0700 Subject: [PATCH] main --- ESP32_CAM Code.ino | 242 ++++++++++++++++++++++++++++++ ESP8266 code.ino | 360 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 602 insertions(+) create mode 100644 ESP32_CAM Code.ino create mode 100644 ESP8266 code.ino diff --git a/ESP32_CAM Code.ino b/ESP32_CAM Code.ino new file mode 100644 index 0000000..581396a --- /dev/null +++ b/ESP32_CAM Code.ino @@ -0,0 +1,242 @@ +#include +#include +#include "esp_camera.h" +#include +#include + +// Wi-Fi credentials +const char* ssid = "Test123"; +const char* password = "a?=31@#daE313-=;l."; + +// Supabase details +const char* supabaseUrl = "https://nwlikynouhddjwyjrwnl.supabase.co"; +const char* supabaseAnonKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im53bGlreW5vdWhkZGp3eWpyd25sIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDk3Mjg3NjYsImV4cCI6MjA2NTMwNDc2Nn0.s5skPnBRa1OepnvNCX1cl3dmzLb1gEs_HnOVdKMq_dU"; +const char* bucketName = "photo"; + +// Firebase details +#define API_KEY "AIzaSyBCe-30GPJRp0p7psDH4t2D01WHPcSNIqQ" +#define DATABASE_URL "https://apkrafi-default-rtdb.firebaseio.com/" + +FirebaseData fbdo; +FirebaseAuth auth; +FirebaseConfig config; + +// ESP32-CAM pin config +#define PWDN_GPIO_NUM 32 +#define RESET_GPIO_NUM -1 +#define XCLK_GPIO_NUM 0 +#define SIOD_GPIO_NUM 26 +#define SIOC_GPIO_NUM 27 +#define Y9_GPIO_NUM 35 +#define Y8_GPIO_NUM 34 +#define Y7_GPIO_NUM 39 +#define Y6_GPIO_NUM 36 +#define Y5_GPIO_NUM 21 +#define Y4_GPIO_NUM 19 +#define Y3_GPIO_NUM 18 +#define Y2_GPIO_NUM 5 +#define VSYNC_GPIO_NUM 25 +#define HREF_GPIO_NUM 23 +#define PCLK_GPIO_NUM 22 +#define LED_GPIO_NUM 4 // Flash LED + +// Relay pin +#define RELAY_PIN 13 // Controls the lamp + +void startCamera() { + camera_config_t config; + config.ledc_channel = LEDC_CHANNEL_0; + config.ledc_timer = LEDC_TIMER_0; + config.pin_d0 = Y2_GPIO_NUM; + config.pin_d1 = Y3_GPIO_NUM; + config.pin_d2 = Y4_GPIO_NUM; + config.pin_d3 = Y5_GPIO_NUM; + config.pin_d4 = Y6_GPIO_NUM; + config.pin_d5 = Y7_GPIO_NUM; + config.pin_d6 = Y8_GPIO_NUM; + config.pin_d7 = Y9_GPIO_NUM; + config.pin_xclk = XCLK_GPIO_NUM; + config.pin_pclk = PCLK_GPIO_NUM; + config.pin_vsync = VSYNC_GPIO_NUM; + config.pin_href = HREF_GPIO_NUM; + config.pin_sscb_sda = SIOD_GPIO_NUM; + config.pin_sscb_scl = SIOC_GPIO_NUM; + config.pin_pwdn = PWDN_GPIO_NUM; + config.pin_reset = RESET_GPIO_NUM; + config.xclk_freq_hz = 20000000; + config.pixel_format = PIXFORMAT_JPEG; + + if (psramFound()) { + config.frame_size = FRAMESIZE_VGA; + config.jpeg_quality = 10; + config.fb_count = 1; + } else { + config.frame_size = FRAMESIZE_QVGA; + config.jpeg_quality = 12; + config.fb_count = 1; + } + + esp_err_t err = esp_camera_init(&config); + if (err != ESP_OK) { + Serial.printf("Camera init failed with error 0x%x", err); + } +} + +void uploadImage() { + digitalWrite(LED_GPIO_NUM, HIGH); + delay(1500); // Let flash stabilize the exposure + + // Flush old frame + camera_fb_t* fb = esp_camera_fb_get(); + if (fb) { + esp_camera_fb_return(fb); + delay(100); + } + + // Capture new frame + fb = esp_camera_fb_get(); + if (!fb) { + Serial.println("Camera capture failed"); + digitalWrite(LED_GPIO_NUM, LOW); + return; + } + + // Upload to Supabase + String fileName = "latest.jpg"; + String url = String(supabaseUrl) + "/storage/v1/object/" + bucketName + "/" + fileName; + + HTTPClient http; + http.begin(url); + http.addHeader("Authorization", "Bearer " + String(supabaseAnonKey)); + http.addHeader("Content-Type", "image/jpeg"); + + int response = http.PUT(fb->buf, fb->len); + Serial.printf("Upload response: %d\n", response); + if (response > 0) { + Serial.println(http.getString()); + } + + http.end(); + esp_camera_fb_return(fb); + digitalWrite(LED_GPIO_NUM, LOW); +} + +void setup() { + Serial.begin(115200); + pinMode(LED_GPIO_NUM, OUTPUT); + digitalWrite(LED_GPIO_NUM, LOW); + + // Relay setup + pinMode(RELAY_PIN, OUTPUT); + digitalWrite(RELAY_PIN, LOW); // Relay OFF at start + + WiFi.begin(ssid, password); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println("\nConnected to WiFi"); + + startCamera(); + delay(1000); // Let the camera stabilize + + // Sync NTP Time (UTC) + configTime(25200, 0, "time.google.com"); + Serial.print("Waiting for time sync"); + while (time(nullptr) < 100000) { + delay(500); + Serial.print("."); + } + Serial.println("\nTime synced"); + + // Firebase setup + config.api_key = API_KEY; + config.database_url = DATABASE_URL; + Firebase.begin(&config, &auth); + + if (Firebase.signUp(&config, &auth, "", "")) { + Serial.println("Firebase anonymous sign-up OK"); + } else { + Serial.printf("Sign-up error: %s\n", config.signer.signupError.message.c_str()); + } + + Firebase.reconnectWiFi(true); +} + +bool hasCaptured = false; // global flag outside loop() + +void loop() { + if (!Firebase.ready()) { + Serial.println("Firebase not ready"); + delay(2000); + return; + } + + // Read scheduled time from Firebase (e.g., "16.31") + if (Firebase.RTDB.getString(&fbdo, "/camera/schedule")) { + String scheduledTime = fbdo.stringData(); + Serial.printf("Scheduled capture time: %s\n", scheduledTime.c_str()); + + // Get current time + time_t now = time(nullptr); + struct tm* timeinfo = localtime(&now); + + char currentTime[6]; + sprintf(currentTime, "%02d.%02d", timeinfo->tm_hour, timeinfo->tm_min); + Serial.printf("Current time: %s\n", currentTime); + + // If time matches AND we haven't captured yet + if (scheduledTime == String(currentTime) && !hasCaptured) { + Serial.println("Time match! Capturing photo..."); + uploadImage(); + hasCaptured = true; // prevent re-capturing within same minute + Serial.println("Capture complete. Waiting for next time."); + } + + // Reset capture flag when the time has moved on + if (scheduledTime != String(currentTime)) { + hasCaptured = false; + } + + } else { + Serial.println("Failed to read schedule from Firebase"); + } + + // Relay control variables + bool relayShouldTurnOn = false; + + // Check temperature + if (Firebase.RTDB.getFloat(&fbdo, "/sensors/temperature")) { + float temperature = fbdo.floatData(); + Serial.printf("Temperature: %.2f°C\n", temperature); + + if (temperature < 25) { + relayShouldTurnOn = true; + } + } else { + Serial.println("Failed to read temperature from Firebase"); + } + + // Check soil moisture + if (Firebase.RTDB.getFloat(&fbdo, "/sensors/soilMoisture")) { + float soilMoisture = fbdo.floatData(); + Serial.printf("Soil Moisture: %.2f%%\n", soilMoisture); + + if (soilMoisture > 75) { + relayShouldTurnOn = true; + } + } else { + Serial.println("Failed to read soil moisture from Firebase"); + } + + // Control relay + if (relayShouldTurnOn) { + digitalWrite(RELAY_PIN, HIGH); // Relay ON + Serial.println("Relay ON (condition met)"); + } else { + digitalWrite(RELAY_PIN, LOW); // Relay OFF + Serial.println("Relay OFF (no condition met)"); + } + + delay(10000); // Check every 10 seconds +} diff --git a/ESP8266 code.ino b/ESP8266 code.ino new file mode 100644 index 0000000..50e62a7 --- /dev/null +++ b/ESP8266 code.ino @@ -0,0 +1,360 @@ +#include +#include +#include +#include +#include +#include +#include +#include // [EEPROM] + +// WiFi Credentials +const char* ssid = "Test123"; +const char* password = "a?=31@#daE313-=;l."; + +// Firebase credentials +#define API_KEY "AIzaSyBCe-30GPJRp0p7psDH4t2D01WHPcSNIqQ" +#define DATABASE_URL "https://apkrafi-default-rtdb.firebaseio.com/" + +// Firebase objects +FirebaseData fbdo; +FirebaseAuth auth; +FirebaseConfig config; + +// Relay pins for pumps +const int pumpRelayPins[4] = {D4, D5, D6, D7}; + +// I2C pins for DS3231 RTC +#define SDA_PIN D2 +#define SCL_PIN D1 + +// DHT sensor setup +#define DHTPIN D3 +#define DHTTYPE DHT11 +DHT dht(DHTPIN, DHTTYPE); + +// Soil moisture sensor +#define SOIL_MOISTURE_PIN A0 +int soilMoisturePercent = 0; +int rawMoisture = 0; + +bool pumpShouldRun[4] = {false, false, false, false}; + +unsigned long lastRTCSync = 0; +const unsigned long rtcSyncInterval = 3600000; + +bool pump3Enabled = true; + +WiFiUDP ntpUDP; +NTPClient timeClient(ntpUDP, "time.google.com", 7 * 3600, 60000); +RTC_DS3231 rtc; +bool wifiConnected = false; +unsigned long lastReconnectAttempt = 0; +const unsigned long reconnectInterval = 10000; + +// [MOD] Firebase-controlled schedule variables +uint8_t fbPumpStartHour2 = 22, fbPumpStartMinute2 = 3, fbPumpEndHour2 = 22, fbPumpEndMinute2 = 5; +uint8_t fbPumpStartHour3 = 2, fbPumpStartMinute3 = 15, fbPumpEndHour3 = 2, fbPumpEndMinute3 = 50; +uint8_t fbPumpStartHour4 = 2, fbPumpStartMinute4 = 15, fbPumpEndHour4 = 2, fbPumpEndMinute4 = 50; +bool fbPump3Enabled = true; +unsigned long lastFirebaseSync = 0; +const unsigned long firebaseSyncInterval = 2000; +bool firebaseInitialized = false; + +// [EEPROM] EEPROM setup +#define EEPROM_SIZE 64 + +void saveScheduleToEEPROM() { + EEPROM.write(0, fbPumpStartHour2); + EEPROM.write(1, fbPumpStartMinute2); + EEPROM.write(2, fbPumpEndHour2); + EEPROM.write(3, fbPumpEndMinute2); + + EEPROM.write(4, fbPumpStartHour3); + EEPROM.write(5, fbPumpStartMinute3); + EEPROM.write(6, fbPumpEndHour3); + EEPROM.write(7, fbPumpEndMinute3); + + EEPROM.write(8, fbPumpStartHour4); + EEPROM.write(9, fbPumpStartMinute4); + EEPROM.write(10, fbPumpEndHour4); + EEPROM.write(11, fbPumpEndMinute4); + + EEPROM.write(12, fbPump3Enabled); + EEPROM.commit(); +} + +void loadScheduleFromEEPROM() { + fbPumpStartHour2 = EEPROM.read(0); + fbPumpStartMinute2 = EEPROM.read(1); + fbPumpEndHour2 = EEPROM.read(2); + fbPumpEndMinute2 = EEPROM.read(3); + + fbPumpStartHour3 = EEPROM.read(4); + fbPumpStartMinute3 = EEPROM.read(5); + fbPumpEndHour3 = EEPROM.read(6); + fbPumpEndMinute3 = EEPROM.read(7); + + fbPumpStartHour4 = EEPROM.read(8); + fbPumpStartMinute4 = EEPROM.read(9); + fbPumpEndHour4 = EEPROM.read(10); + fbPumpEndMinute4 = EEPROM.read(11); + + fbPump3Enabled = EEPROM.read(12); +} + +void connectToWiFi() { + Serial.println("Connecting to WiFi..."); + WiFi.begin(ssid, password); + int attempt = 0; + while (WiFi.status() != WL_CONNECTED && attempt < 20) { + delay(500); + Serial.print("."); + attempt++; + } + wifiConnected = WiFi.status() == WL_CONNECTED; + Serial.println(wifiConnected ? "\nWiFi connected." : "\nFailed to connect to WiFi."); +} + +void syncRTCTime() { + if (wifiConnected) { + timeClient.update(); + DateTime now = DateTime(timeClient.getEpochTime()); + rtc.adjust(now); + Serial.print("RTC synced with NTP time: "); + Serial.println(now.timestamp()); + } +} + +void fetchScheduleFromFirebase() { + if (!Firebase.ready()) return; + + if (Firebase.RTDB.getInt(&fbdo, "/schedule/pump2/startHour")) fbPumpStartHour2 = fbdo.to(); + if (Firebase.RTDB.getInt(&fbdo, "/schedule/pump2/startMinute")) fbPumpStartMinute2 = fbdo.to(); + if (Firebase.RTDB.getInt(&fbdo, "/schedule/pump2/endHour")) fbPumpEndHour2 = fbdo.to(); + if (Firebase.RTDB.getInt(&fbdo, "/schedule/pump2/endMinute")) fbPumpEndMinute2 = fbdo.to(); + + if (Firebase.RTDB.getInt(&fbdo, "/schedule/pump3/startHour")) fbPumpStartHour3 = fbdo.to(); + if (Firebase.RTDB.getInt(&fbdo, "/schedule/pump3/startMinute")) fbPumpStartMinute3 = fbdo.to(); + if (Firebase.RTDB.getInt(&fbdo, "/schedule/pump3/endHour")) fbPumpEndHour3 = fbdo.to(); + if (Firebase.RTDB.getInt(&fbdo, "/schedule/pump3/endMinute")) fbPumpEndMinute3 = fbdo.to(); + + if (Firebase.RTDB.getInt(&fbdo, "/schedule/pump4/startHour")) fbPumpStartHour4 = fbdo.to(); + if (Firebase.RTDB.getInt(&fbdo, "/schedule/pump4/startMinute")) fbPumpStartMinute4 = fbdo.to(); + if (Firebase.RTDB.getInt(&fbdo, "/schedule/pump4/endHour")) fbPumpEndHour4 = fbdo.to(); + if (Firebase.RTDB.getInt(&fbdo, "/schedule/pump4/endMinute")) fbPumpEndMinute4 = fbdo.to(); + + if (Firebase.RTDB.getBool(&fbdo, "/schedule/pump3Enabled")) fbPump3Enabled = fbdo.to(); + + saveScheduleToEEPROM(); +} + +void setup() { + Serial.begin(115200); + delay(1000); + + EEPROM.begin(EEPROM_SIZE); + loadScheduleFromEEPROM(); + + for (int i = 0; i < 4; i++) { + pinMode(pumpRelayPins[i], OUTPUT); + digitalWrite(pumpRelayPins[i], HIGH); + } + + Wire.begin(SDA_PIN, SCL_PIN); + if (!rtc.begin()) Serial.println("Couldn't find RTC"); + + dht.begin(); + connectToWiFi(); + if (wifiConnected) { + syncRTCTime(); + lastRTCSync = millis(); + } + + config.api_key = API_KEY; + config.database_url = DATABASE_URL; + + if (wifiConnected) { + Firebase.begin(&config, &auth); + Firebase.reconnectWiFi(true); + + if (!Firebase.signUp(&config, &auth, "", "")) { + Serial.printf("Firebase signUp failed: %s\n", config.signer.signupError.message.c_str()); + } else { + Serial.println("Anonymous sign-in successful."); + } + + unsigned long timeout = millis(); + while (!Firebase.ready() && millis() - timeout < 10000) { + delay(100); + } + + if (Firebase.ready()) { + Serial.println("Firebase is ready."); + } else { + Serial.println("Failed to connect to Firebase."); + } + } else { + Serial.println("Skipped Firebase initialization (offline mode)."); + } +} + +void loop() { + static bool initialRTCSynced = false; + if (!initialRTCSynced && wifiConnected) { + syncRTCTime(); + initialRTCSynced = true; + } + + if (millis() - lastFirebaseSync > firebaseSyncInterval) { + fetchScheduleFromFirebase(); + lastFirebaseSync = millis(); + } + + if (WiFi.status() != WL_CONNECTED) { + if (millis() - lastReconnectAttempt > reconnectInterval) { + Serial.println("WiFi lost. Reconnecting..."); + WiFi.disconnect(); + WiFi.begin(ssid, password); + lastReconnectAttempt = millis(); + } + wifiConnected = false; + } else { + if (!wifiConnected) { + Serial.println("WiFi reconnected."); + wifiConnected = true; + } + } + + if (wifiConnected && !firebaseInitialized) { + Serial.println("Initializing Firebase..."); + Firebase.begin(&config, &auth); + Firebase.reconnectWiFi(true); + + if (!Firebase.signUp(&config, &auth, "", "")) { + Serial.printf("Firebase signUp failed: %s\n", config.signer.signupError.message.c_str()); + } else { + Serial.println("Anonymous sign-in successful."); + } + + unsigned long timeout = millis(); + while (!Firebase.ready() && millis() - timeout < 10000) { + delay(100); + } + + if (Firebase.ready()) { + Serial.println("Firebase is ready."); + firebaseInitialized = true; + } else { + Serial.println("Failed to connect to Firebase."); + } + } + + if (wifiConnected && millis() - lastRTCSync > rtcSyncInterval) { + syncRTCTime(); + lastRTCSync = millis(); + } + + DateTime nowTime; + int currentHour, currentMinute, currentSecond; + int currentMinutes; + + if (wifiConnected) { + timeClient.update(); + currentHour = timeClient.getHours(); + currentMinute = timeClient.getMinutes(); + currentSecond = timeClient.getSeconds(); + } else { + nowTime = rtc.now(); + currentHour = nowTime.hour(); + currentMinute = nowTime.minute(); + currentSecond = nowTime.second(); + } + + currentMinutes = currentHour * 60 + currentMinute; + Serial.printf("Time: %02d:%02d:%02d\n", currentHour, currentMinute, currentSecond); + + float temperature = dht.readTemperature(); + float humidity = dht.readHumidity(); + if (!isnan(temperature) && !isnan(humidity)) { + Serial.printf("Temperature: %.1f °C, Humidity: %.1f %%\n", temperature, humidity); + } else { + Serial.println("Failed to read from DHT sensor!"); + } + + rawMoisture = analogRead(SOIL_MOISTURE_PIN); + const int WET_VALUE = 450; + const int DRY_VALUE = 1024; + + soilMoisturePercent = map(rawMoisture, DRY_VALUE, WET_VALUE, 0, 100); + soilMoisturePercent = constrain(soilMoisturePercent, 0, 100); + + Serial.printf("Soil Moisture: %d %% (Raw: %d)\n", soilMoisturePercent, rawMoisture); + + if (Firebase.ready()) { + Firebase.RTDB.setFloat(&fbdo, "/sensors/temperature", temperature); + Firebase.RTDB.setFloat(&fbdo, "/sensors/humidity", humidity); + Firebase.RTDB.setInt(&fbdo, "/sensors/soilMoisture", soilMoisturePercent); + } + + pumpShouldRun[0] = (!isnan(temperature) && temperature >= 31.0); + + static bool pump2Running = false; + static bool pump2SkippedDueToMoisture = false; + const int soilMoistureThreshold = 60; + int start2 = fbPumpStartHour2 * 60 + fbPumpStartMinute2; + int end2 = fbPumpEndHour2 * 60 + fbPumpEndMinute2; + + if (currentMinutes == start2) { + if (soilMoisturePercent <= soilMoistureThreshold) { + pump2Running = true; + pump2SkippedDueToMoisture = false; + Serial.println("Pump 2 scheduled to start: soil is dry enough."); + } else { + pump2Running = false; + pump2SkippedDueToMoisture = true; + Serial.println("Pump 2 skipped due to sufficient soil moisture."); + } + } + + // Keep running only while time range AND moisture is still low + if (currentMinutes >= start2 && currentMinutes < end2) { + if (pump2Running && soilMoisturePercent > soilMoistureThreshold) { + pump2Running = false; // stop immediately + Serial.println("Pump 2 stopped early due to sufficient soil moisture."); + } + pumpShouldRun[1] = pump2Running && !pump2SkippedDueToMoisture; + } else { + pumpShouldRun[1] = false; + pump2Running = false; + pump2SkippedDueToMoisture = false; + } + // ------------------------------------------ + + int start3 = fbPumpStartHour3 * 60 + fbPumpStartMinute3; + int end3 = fbPumpEndHour3 * 60 + fbPumpEndMinute3; + int start4 = fbPumpStartHour4 * 60 + fbPumpStartMinute4; + int end4 = fbPumpEndHour4 * 60 + fbPumpEndMinute4; + + bool pump3Time = (currentMinutes >= start3 && currentMinutes < end3); + bool pump4Time = (currentMinutes >= start4 && currentMinutes < end4); + + pumpShouldRun[2] = pump3Time && fbPump3Enabled; + pumpShouldRun[3] = pump4Time && !fbPump3Enabled; + + Serial.printf("Scheduled pump: %s\n", fbPump3Enabled ? "Pump 3" : "Pump 4"); + + for (int i = 0; i < 4; i++) { + bool isOn = digitalRead(pumpRelayPins[i]) == LOW; + if (pumpShouldRun[i] && !isOn) digitalWrite(pumpRelayPins[i], LOW); + else if (!pumpShouldRun[i] && isOn) digitalWrite(pumpRelayPins[i], HIGH); + } + + for (int i = 0; i < 4; i++) { + bool isOn = digitalRead(pumpRelayPins[i]) == LOW; + const char* statusText = isOn ? "ON" : "OFF"; + Serial.printf("Pump %d: %s\n", i + 1, statusText); + } + Serial.println("--------------------------"); + + delay(1000); +}