Created
November 22, 2022 20:41
-
-
Save GloryFish/f5a242b268d07255aab5ad64daf0c2aa to your computer and use it in GitHub Desktop.
Timer control software for an Arduino-based automatic plant watering system.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Automatic Watering System mkII | |
// | |
// Copyright Jay Roberts 2022 | |
// | |
// Toggle switch pins | |
const int pinWaterManual = 8; | |
const int pinWaterAuto = 9; | |
typedef enum { | |
OperatingModeOff, | |
OperatingModeAuto, | |
OperatingModeManual, | |
OperatingModeUnknown, | |
} OperatingMode; | |
OperatingMode currentMode = OperatingModeUnknown; | |
const int pumps[] = { | |
A1, | |
A2, | |
A3, | |
A4, | |
A5, | |
}; | |
const int waterDurations[] = { | |
8, | |
8, | |
12, | |
12, | |
60, | |
}; | |
const unsigned int wateringIntervalMinutes = 60; // Minutes between watering. | |
const unsigned long wateringIntervalMillis = wateringIntervalMinutes * 60 * 1000L; // Minutes between watering. | |
unsigned long nextWateringTime = 0; // Millis that next watering should start | |
unsigned long currentMillis = 0; | |
unsigned long previousToggleReadMillis = 0; | |
unsigned long previousNotificationMillis = 0; | |
bool pumpsRunning = false; | |
void setup() { | |
Serial.begin(9600); | |
Serial.println("Jay's amazing watering robot!"); | |
pinMode(pinWaterAuto, INPUT_PULLUP) ; | |
pinMode(pinWaterManual, INPUT_PULLUP) ; | |
for (int i = 0; i < (sizeof(pumps) / sizeof(pumps[0])); i++) { | |
pinMode(pumps[i], OUTPUT); | |
digitalWrite(pumps[i], HIGH); | |
} | |
readToggle(); | |
delay(500); | |
} | |
void loop() { | |
currentMillis = millis(); | |
readToggle(); | |
runPumps(); | |
notifyAutoWateringTime(); | |
} | |
/** | |
* Check state of toggle switch and set operating mode. | |
*/ | |
void readToggle() { | |
if (currentMillis - previousToggleReadMillis < 100) { | |
return; | |
} | |
OperatingMode readMode = currentMode; | |
if (digitalRead(pinWaterManual) == LOW) { | |
readMode = OperatingModeAuto; | |
} else if (digitalRead(pinWaterAuto) == LOW) { | |
readMode = OperatingModeManual; | |
} else { | |
readMode = OperatingModeOff; | |
} | |
if (readMode != currentMode) { | |
currentMode = readMode; | |
if (currentMode == OperatingModeOff) { | |
Serial.println("Mode OFF"); | |
allPumpsOff(); | |
} else if (currentMode == OperatingModeManual) { | |
Serial.println("Mode Manual"); | |
allPumpsOn(); | |
} else { | |
Serial.println("Mode Auto"); | |
// Start our first watering cycle right now. | |
nextWateringTime = currentMillis; | |
} | |
} | |
previousToggleReadMillis = currentMillis; | |
} | |
/** | |
* Turn all water pumps on. | |
*/ | |
void allPumpsOn() { | |
for (int i = 0; i < (sizeof(pumps) / sizeof(pumps[0])); i++) { | |
digitalWrite(pumps[i], LOW); | |
} | |
} | |
/** | |
* Turn all water pumps OFF. | |
*/ | |
void allPumpsOff() { | |
for (int i = 0; i < (sizeof(pumps) / sizeof(pumps[0])); i++) { | |
digitalWrite(pumps[i], HIGH); | |
} | |
} | |
/** | |
* Check schedule and, when necessary, run pumps. | |
*/ | |
void runPumps() { | |
// Only run when in Auto mode. | |
if (currentMode == OperatingModeAuto) { | |
// currentMillis increases as time goes on. | |
// nextWatering time is the scheduled time to run the pumps in auto mode. | |
// If currentMillis is greater than nextWateringTime then it's time to run | |
// the pumps. | |
if (nextWateringTime < currentMillis) { | |
// We'll keep track if any pumps are running. | |
pumpsRunning = false; | |
// Evaluate all of the pumps. | |
for (int i = 0; i < (sizeof(pumps) / sizeof(pumps[0])); i++) { | |
if (currentMillis - nextWateringTime < waterDurations[i] * 1000L) { | |
// The difference between currentMillis and nextWateringTime is the | |
// duration that the pumps have been running for. | |
// | |
// If that value is less than the duration for the current pump then | |
// the pump should be ON. | |
digitalWrite(pumps[i], LOW); | |
pumpsRunning = true; | |
} else { | |
// Otherwise, the pump should be OFF. | |
digitalWrite(pumps[i], HIGH); | |
} | |
} | |
if (!pumpsRunning) { | |
// We've evaluated all pumps and none are running, the current cycle is done. | |
Serial.println("All pumps done"); | |
// Schedule the next watering cycle. | |
nextWateringTime = currentMillis + wateringIntervalMillis; | |
} | |
} | |
} | |
} | |
/** | |
* Emits a serial message every 5 seconds alerting the time until the next auto- | |
* watering cycle. | |
*/ | |
void notifyAutoWateringTime() { | |
if (currentMode != OperatingModeAuto) { | |
return; | |
} | |
if (pumpsRunning) { | |
return; | |
} | |
if (currentMillis - previousNotificationMillis > 5 * 1000) { | |
unsigned long remainingMillis = nextWateringTime - currentMillis; | |
unsigned long seconds = remainingMillis / 1000; | |
unsigned long minutes = seconds / 60; | |
unsigned long hours = minutes / 60; | |
unsigned long days = hours / 24; | |
remainingMillis %= 1000; | |
hours %= 24; | |
minutes %= 60; | |
seconds %= 60; | |
Serial.print("Next auto-watering cycle in "); | |
Serial.print(hours); | |
Serial.print(" hours, "); | |
Serial.print(minutes); | |
Serial.print(" minutes, "); | |
Serial.print(seconds); | |
Serial.print(" seconds.\n"); | |
previousNotificationMillis = currentMillis; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment