#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <RTClib.h>
// Pin definitions
const int WATER_PUMP_PIN = 13;
const int FERTILIZER_A_PUMP_PIN = 12;
const int FERTILIZER_B_PUMP_PIN = 14;
const int MIXING_PUMP_PIN = 27;
const int DISTRIBUTION_PUMP_PIN = 26;
const int WATER_LEVEL_TRIG_PIN = 2;
const int WATER_LEVEL_ECHO_PIN = 15;
const int FERTILIZER_A_LEVEL_TRIG_PIN = 5;
const int FERTILIZER_A_LEVEL_ECHO_PIN = 4;
const int FERTILIZER_B_LEVEL_TRIG_PIN = 19;
const int FERTILIZER_B_LEVEL_ECHO_PIN = 18;
// OLED display
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// RTC
RTC_DS3231 rtc;
// Plant data structure
struct PlantData {
const char* name;
int fertilizer_a_cc;
int fertilizer_b_cc;
int growth_days;
};
// Plant types
const PlantData PLANTS[] = {
{"Green Oak", 3, 3, 30},
{"Red Oak", 3, 3, 30},
{"Lettuce", 3, 3, 30},
{"Chinese Kale", 3, 3, 25}
};
class HydroponicsSystem {
private:
bool isAuto;
int selectedPlantIndex;
unsigned long harvestCountdown;
unsigned long lastWaterChangeTime;
void pumpControl(int pin, int duration) {
digitalWrite(pin, HIGH);
delay(duration);
digitalWrite(pin, LOW);
}
float getDistance(int trigPin, int echoPin) {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
long duration = pulseIn(echoPin, HIGH);
return duration * 0.034 / 2;
}
void checkWaterLevels() {
float waterLevel = getDistance(WATER_LEVEL_TRIG_PIN, WATER_LEVEL_ECHO_PIN);
float fertilizerALevel = getDistance(FERTILIZER_A_LEVEL_TRIG_PIN, FERTILIZER_A_LEVEL_ECHO_PIN);
float fertilizerBLevel = getDistance(FERTILIZER_B_LEVEL_TRIG_PIN, FERTILIZER_B_LEVEL_ECHO_PIN);
// Send water level data to serial monitor
Serial.print("Water level: ");
Serial.print(waterLevel);
Serial.println(" cm");
Serial.print("Fertilizer A level: ");
Serial.print(fertilizerALevel);
Serial.println(" cm");
Serial.print("Fertilizer B level: ");
Serial.print(fertilizerBLevel);
Serial.println(" cm");
}
void mixNutrients() {
pumpControl(MIXING_PUMP_PIN, 300000); // 5 minutes
}
void distributeNutrients() {
pumpControl(DISTRIBUTION_PUMP_PIN, 60000); // 1 minute, adjust as needed
}
void updateDisplay() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("Hydroponic System");
display.println("Mode: " + String(isAuto ? "Auto" : "Manual"));
if (isAuto) {
display.println("Plant: " + String(PLANTS[selectedPlantIndex].name));
}
display.println("Harvest in: " + String(harvestCountdown / 86400) + " days");
display.display();
}
public:
HydroponicsSystem() : isAuto(false), selectedPlantIndex(-1), harvestCountdown(0), lastWaterChangeTime(0) {}
void setup() {
pinMode(WATER_PUMP_PIN, OUTPUT);
pinMode(FERTILIZER_A_PUMP_PIN, OUTPUT);
pinMode(FERTILIZER_B_PUMP_PIN, OUTPUT);
pinMode(MIXING_PUMP_PIN, OUTPUT);
pinMode(DISTRIBUTION_PUMP_PIN, OUTPUT);
pinMode(WATER_LEVEL_TRIG_PIN, OUTPUT);
pinMode(WATER_LEVEL_ECHO_PIN, INPUT);
pinMode(FERTILIZER_A_LEVEL_TRIG_PIN, OUTPUT);
pinMode(FERTILIZER_A_LEVEL_ECHO_PIN, INPUT);
pinMode(FERTILIZER_B_LEVEL_TRIG_PIN, OUTPUT);
pinMode(FERTILIZER_B_LEVEL_ECHO_PIN, INPUT);
if (!display.begin(0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;);
}
if (!rtc.begin()) {
Serial.println("Couldn't find RTC");
while (1);
}
if (rtc.lostPower()) {
Serial.println("RTC lost power, lets set the time!");
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
Serial.begin(9600);
Serial.println("Hydroponic System Ready");
}
void setMode(bool autoMode) {
isAuto = autoMode;
}
void selectPlant(int index) {
if (index >= 0 && index < 4) {
selectedPlantIndex = index;
harvestCountdown = PLANTS[index].growth_days * 86400UL; // Convert days to seconds
}
}
void startCycle() {
if (isAuto && selectedPlantIndex != -1) {
int fertilizer_a_duration = PLANTS[selectedPlantIndex].fertilizer_a_cc * 1000; // Convert CC to milliseconds
int fertilizer_b_duration = PLANTS[selectedPlantIndex].fertilizer_b_cc * 1000;
pumpControl(WATER_PUMP_PIN, 60000); // 1 minute for water, adjust as needed
pumpControl(FERTILIZER_A_PUMP_PIN, fertilizer_a_duration);
pumpControl(FERTILIZER_B_PUMP_PIN, fertilizer_b_duration);
Serial.println("Cycle started.");
} else {
Serial.println("Manual mode, cycle not started.");
}
mixNutrients();
distributeNutrients();
lastWaterChangeTime = millis();
}
void processCommand(String command) {
if (command == "start") {
startCycle();
} else if (command == "auto") {
setMode(true);
Serial.println("Auto mode selected.");
} else if (command == "manual") {
setMode(false);
Serial.println("Manual mode selected.");
} else if (command.startsWith("select ")) {
int index = command.substring(7).toInt();
selectPlant(index);
Serial.print("Plant ");
Serial.print(index);
Serial.println(" selected.");
} else {
Serial.println("Unknown command: " + command);
}
}
void loop() {
checkWaterLevels();
if (millis() - lastWaterChangeTime >= 604800000) { // 7 days in milliseconds
// Send notification to change water
Serial.println("Time to change water!");
}
if (harvestCountdown > 0) {
harvestCountdown -= 1;
if (harvestCountdown == 0) {
// Send harvest notification
Serial.println("Time to harvest!");
}
}
updateDisplay();
// Check for incoming commands from Serial Monitor
if (Serial.available() > 0) {
String command = Serial.readStringUntil('\n');
processCommand(command);
}
delay(1000); // Update every second
}
};
HydroponicsSystem hydroponics;
void setup() {
hydroponics.setup();
}
void loop() {
hydroponics.loop();
}