Skip to content

Instantly share code, notes, and snippets.

@GloryFish
Created November 22, 2022 20:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GloryFish/f5a242b268d07255aab5ad64daf0c2aa to your computer and use it in GitHub Desktop.
Save GloryFish/f5a242b268d07255aab5ad64daf0c2aa to your computer and use it in GitHub Desktop.
Timer control software for an Arduino-based automatic plant watering system.
//
// 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