Created
August 29, 2011 04:12
-
-
Save tanmaykm/1177762 to your computer and use it in GitHub Desktop.
Arduino Sprinkler Controller
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
// Arduino Sprinkler | |
// Version: 0.4 (beta) | |
// Updated: 03 Oct 2011 | |
#include <LiquidCrystal.h> | |
#include <EEPROM.h> | |
// SWITCHES | |
// Pin 1 (Learn/Auto) | |
// Pin 2 (Manual trigger) | |
// EEPROM USE | |
// 1 : Mode: Learn (resets readings)/Auto | |
// 2 : Switch on interval (minutes) | |
// 3 : Trigger threshold (ADC reading) | |
// digital pins | |
// interSWITCH_PIN_1rupts (switches) | |
#define SWITCH_PIN_1 2 | |
#define SWITCH_PIN_2 3 | |
// leds | |
#define ATTN_LED_PIN 11 | |
// Sprinkler Pin Constants | |
#define SP_VALVE_PIN 12 | |
#define SP_SENSOR_POW_PIN 13 | |
// analog pin5 | |
#define SP_SENSOR_PIN A5 | |
#define DEBUG_ON false | |
// Sprinkler Modes | |
#define SP_MODE_AUTO 0 | |
#define SP_MODE_LEARN 1 | |
#define MAX_VALVE_ON_MINS 15 | |
boolean justStarted = true; | |
int spTriggerThreshold = 0; // stored average of learn mode data (value in EEPROM if in auto mode) | |
long spLastRunTime = 0; | |
long spIntervalTime = 0; | |
long spLastSensorReadTime = -9999; | |
long spLastFlushTime = 0; | |
boolean spValveOn = false; //whether valve is on (initialized on setup) | |
boolean spValveFlushing = false; // whether valve is on for flushing | |
boolean spLearnMode; | |
long switchOnInterval = 0; // minutes | |
int lastSensorReading = 0; | |
int sensorPin = 0; | |
int powerPin = 2; | |
int programSwitchPin = 3; | |
#define LOOP_ACTION_SWITCH_1 1 | |
#define LOOP_ACTION_SWITCH_2 2 | |
volatile int spISRAction = 0; | |
#define SECS_IN_MS 1000 | |
#define MINS_IN_MS 60000L | |
#define HR_IN_MS 3600000L | |
// EEPROM Stuff | |
#define EE_SIG_1_POS 0 | |
#define EE_SIG_2_POS 1 | |
#define EE_SIG_3_POS 2 | |
#define EE_SIG_4_POS 3 | |
#define EE_LRN_MODE_POS 4 | |
#define EE_ON_INTV_POS 5 | |
#define EE_TRIG_THRES_POS 6 // and 7 | |
#define EE_SIG_1_VAL 84 | |
#define EE_SIG_2_VAL 85 | |
#define EE_SIG_3_VAL 84 | |
#define EE_SIG_4_VAL 85 | |
// LCD DISPLAY | |
// | |
// 1234567890123456 | |
// METS V9.9 LEARN / AUTO | |
// HUMIDITY: 1234 | |
// | |
// TRIGGER < 1234 | |
// OPEN MINS: 15 | |
// | |
// CHECK IN: 1234 MINS / 24 HR | |
// FLUSH IN: 1234 MINS / 24 HR | |
String lcdlines[8] = { | |
"METS V0.4 ", | |
"Humidity: ", | |
"Trigger < ", | |
"Open Mins: ", | |
"Check In: ", | |
"Flush In: ", | |
"Last Message:", | |
"" | |
}; | |
int lcdStartLine = 0; | |
String attn = "All is well"; | |
boolean blinkOn = true; | |
// initialize the library with the numbers of the interface pins | |
//LiquidCrystal lcd(12, 11, 5, 4, 3, 2); | |
// yellow, blue, green, black, yellow, blue | |
LiquidCrystal lcd(10, 9, 8, 7, 6, 5); | |
////////////////////////////////////////////////////////////////////////// | |
// Sprinkler | |
////////////////////////////////////////////////////////////////////////// | |
void flushValve(long minsNow) { | |
if(spValveFlushing) { | |
// handling time overflow | |
if(minsNow < spLastFlushTime) spLastFlushTime = 0; | |
// if flushing is on for more than 2 mins | |
if((minsNow - spLastFlushTime) > 2) { | |
// set flush off and close valve | |
switchOnValve(false, minsNow); | |
spValveFlushing = false; | |
} | |
} | |
else if(!spValveOn) { | |
// else if valve is not on and valve has not been switched on since last 24hrs | |
if((minsNow - spLastFlushTime) > (24*60)) { | |
// set flush on and open valve | |
spValveFlushing = true; | |
switchOnValve(true, minsNow); | |
} | |
} | |
} | |
void switchOnValve(boolean on, long minsNow) { | |
digitalWrite(SP_VALVE_PIN, on ? HIGH : LOW); | |
spValveOn = on; | |
if(!spValveFlushing) spLastRunTime = minsNow; | |
spLastFlushTime = minsNow; | |
if(spValveFlushing) { | |
attn = (spValveOn ? "Flushing on" : "Flushing done"); | |
} | |
else { | |
attn = (spValveOn ? "Tap switched on" : "Tap switched off"); | |
} | |
if(DEBUG_ON) { | |
Serial.print("Valve set to "); | |
Serial.println(spValveOn ? "on" : "off"); | |
if(spValveFlushing) Serial.println("For flushing"); | |
} | |
blinkOn = false; | |
} | |
int readSensor(long minsNow) { | |
// handling time overflow | |
if(minsNow < spLastSensorReadTime) spLastSensorReadTime = 0; | |
// read sensor only once every 5 minutes | |
if((minsNow - spLastSensorReadTime) < 5) return lastSensorReading; | |
digitalWrite(SP_SENSOR_POW_PIN, HIGH); | |
delay(100); | |
lastSensorReading = analogRead(SP_SENSOR_PIN); | |
digitalWrite(SP_SENSOR_POW_PIN, LOW); | |
delay(100); | |
if(DEBUG_ON) { | |
Serial.print("Sensor reading: "); | |
Serial.println(lastSensorReading); | |
} | |
spLastSensorReadTime = minsNow; | |
return lastSensorReading; | |
} | |
void setup() { | |
if(DEBUG_ON == true) Serial.begin(9600); // debug | |
pinMode(ATTN_LED_PIN, OUTPUT); | |
pinMode(SWITCH_PIN_1, INPUT); | |
pinMode(SWITCH_PIN_2, INPUT); | |
pinMode(SP_VALVE_PIN, OUTPUT); | |
pinMode(SP_SENSOR_POW_PIN, OUTPUT); | |
digitalWrite(SP_VALVE_PIN, LOW); | |
digitalWrite(SP_SENSOR_POW_PIN, LOW); | |
spValveOn = false; | |
// read signature from EEPROM to find whether it was initialized by us | |
if(!verifyEEPROM()) writeEEPROMSignature(); | |
spLearnMode = (EEPROM.read(EE_LRN_MODE_POS) > 0) ? true : false; | |
switchOnInterval = constrain(EEPROM.read(EE_ON_INTV_POS), 1, MAX_VALVE_ON_MINS); | |
spTriggerThreshold = constrain(readEEPROM_Int(EE_TRIG_THRES_POS), 200, 800); | |
attachInterrupt(0, switch1ISR, FALLING); | |
attachInterrupt(1, switch2ISR, FALLING); | |
lcd.begin(16,2); | |
} | |
void loop() { | |
long secsNow = millis()/1000; | |
long minsNow = secsNow/60; | |
long elapsedTime = (minsNow - spLastRunTime); | |
if(elapsedTime < 0) { // handling overflow. don't bother about the loss in time | |
spLastRunTime = 0; | |
return; | |
} | |
if(blinkOn) digitalWrite(ATTN_LED_PIN, ((secsNow % 2) == 0) ? HIGH : LOW); | |
else digitalWrite(ATTN_LED_PIN, LOW); | |
// if flush mode do only flush mode operations | |
flushValve(minsNow); | |
// display LCD | |
int loopsForDisplay = DEBUG_ON ? 8 : 40; | |
if((lcdStartLine % loopsForDisplay) == 0) { | |
if(lcdStartLine == (loopsForDisplay*4)) lcdStartLine = 0; | |
displayLCD(lcdStartLine / loopsForDisplay, minsNow, elapsedTime); | |
} | |
lcdStartLine++; | |
if(spValveFlushing) return; | |
doISRActions(secsNow, minsNow, elapsedTime); | |
//dbgPrintConfig(secsNow); | |
if(spLearnMode) learnModeCheckInLoop(secsNow, minsNow, elapsedTime); | |
else autoModeCheckInLoop(secsNow, minsNow, elapsedTime); | |
if(justStarted) resetIntervalTime(minsNow); | |
delay(DEBUG_ON ? 500 : 100); | |
} | |
void autoModeCheckInLoop(long secsNow, long minsNow, long elapsedTime) { | |
// if valve on for more than switchOnInterval, switch it off | |
if(spValveOn) { | |
if (elapsedTime > switchOnInterval) { | |
dbgPrintConfig(secsNow); | |
switchOnValve(false, minsNow); | |
} | |
} | |
else if (elapsedTime > spIntervalTime) { | |
dbgPrintConfig(secsNow); | |
int reading = readSensor(minsNow); | |
if(reading < spTriggerThreshold) switchOnValve(true, minsNow); | |
else spLastRunTime = minsNow; | |
} | |
} | |
void learnModeCheckInLoop(long secsNow, long minsNow, long elapsedTime) { | |
// don't allow more than 30 mins valve open in any case | |
if(spValveOn && (elapsedTime > MAX_VALVE_ON_MINS)) { | |
dbgPrintConfig(secsNow); | |
switchOnValve(false, minsNow); | |
attn = "Auto Off Done"; | |
} | |
// blink for suggestions | |
if(spValveOn && (elapsedTime > switchOnInterval)) { | |
dbgPrintConfig(secsNow); | |
blinkOn = true; | |
attn = "Switch Off"; | |
} | |
else if(!spValveOn && ((elapsedTime > spIntervalTime) || blinkOn)) { | |
dbgPrintConfig(secsNow); | |
int reading = readSensor(minsNow); | |
if(reading < spTriggerThreshold) { | |
blinkOn = true; | |
attn = "Dry. Switch On"; | |
} | |
else { | |
blinkOn = false; | |
attn = "All is well"; | |
} | |
spLastRunTime = minsNow; | |
} | |
else { | |
blinkOn = false; | |
} | |
} | |
void resetIntervalTime(long minsNow) { | |
if(minsNow < 10) spIntervalTime = 1; | |
else if(minsNow < 60) spIntervalTime = 30; | |
else if(minsNow < (2*60)) spIntervalTime = 60; | |
else if(spIntervalTime != (6*60)) { | |
spIntervalTime = 6*60; | |
justStarted = false; | |
} | |
} | |
////////////////////////////////////////////////////////////////////////// | |
// LCD DISPLAY | |
////////////////////////////////////////////////////////////////////////// | |
void displayLCD(int line, long minsNow, long elapsedTime) { | |
lcd.clear(); | |
lcd.setCursor(0, 0); | |
switch(line) { | |
case 0: | |
lcd.print(lcdlines[0] + (spLearnMode ? "LEARN" : "AUTO")); | |
lcd.setCursor(0, 1); | |
lcd.print(lcdlines[1] + readSensor(minsNow)); | |
break; | |
case 1: | |
lcd.print(lcdlines[2] + spTriggerThreshold); | |
lcd.setCursor(0, 1); | |
lcd.print(lcdlines[3] + switchOnInterval); | |
break; | |
case 2: | |
lcd.print(lcdlines[4] + max(spIntervalTime - elapsedTime, 0)); | |
lcd.setCursor(0, 1); | |
lcd.print(lcdlines[5] + max(24*60 - (minsNow - spLastFlushTime), 0)); | |
break; | |
case 3: | |
lcd.print(lcdlines[6]); | |
lcd.setCursor(0, 1); | |
lcd.print(attn); | |
break; | |
} | |
} | |
////////////////////////////////////////////////////////////////////////// | |
// Misc | |
////////////////////////////////////////////////////////////////////////// | |
boolean verifyEEPROM() { | |
int sig[] = {0,0,0,0}; | |
if(EE_SIG_1_VAL != EEPROM.read(EE_SIG_1_POS)) return false; | |
if(EE_SIG_2_VAL != EEPROM.read(EE_SIG_2_POS)) return false; | |
if(EE_SIG_3_VAL != EEPROM.read(EE_SIG_3_POS)) return false; | |
if(EE_SIG_4_VAL != EEPROM.read(EE_SIG_4_POS)) return false; | |
return true; | |
} | |
void writeEEPROMSignature() { | |
EEPROM.write(EE_SIG_1_POS, EE_SIG_1_VAL); | |
EEPROM.write(EE_SIG_2_POS, EE_SIG_2_VAL); | |
EEPROM.write(EE_SIG_3_POS, EE_SIG_3_VAL); | |
EEPROM.write(EE_SIG_4_POS, EE_SIG_4_VAL); | |
// write the default values as well | |
EEPROM.write(EE_LRN_MODE_POS, 1); | |
EEPROM.write(EE_ON_INTV_POS, MAX_VALVE_ON_MINS); | |
writeEEPROM_Int(EE_TRIG_THRES_POS, 390); | |
if(DEBUG_ON) Serial.println("Wrote fresh EEPROM signature"); | |
} | |
void dbgPrintConfig(long secsNow) { | |
if(DEBUG_ON) { | |
Serial.println("Loop "); | |
Serial.print(blinkOn ? "Y" : "N"); | |
Serial.print(" Now: "); | |
Serial.print(secsNow); | |
Serial.print(" Last Run: "); | |
Serial.print(spLastRunTime); | |
Serial.print(" Learn:"); | |
Serial.print(spLearnMode ? "Y" : "N"); | |
Serial.print(" ValveOn:"); | |
Serial.print(spValveOn ? "Y" : "N"); | |
Serial.print(" switchOnInterval:"); | |
Serial.print(switchOnInterval); | |
Serial.print(" spIntervalTime:"); | |
Serial.print(spIntervalTime); | |
Serial.print(" spTriggerThreshold:"); | |
Serial.println(spTriggerThreshold); | |
} | |
} | |
void writeEEPROM_Int(int p_address, int p_value) { | |
byte lowByte = ((p_value >> 0) & 0xFF); | |
byte highByte = ((p_value >> 8) & 0xFF); | |
EEPROM.write(p_address, lowByte); | |
EEPROM.write(p_address + 1, highByte); | |
} | |
//This function will read a 2 byte integer from the eeprom at the specified address and address + 1 | |
unsigned int readEEPROM_Int(int p_address) { | |
byte lowByte = EEPROM.read(p_address); | |
byte highByte = EEPROM.read(p_address + 1); | |
return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
// Switch Handlers | |
////////////////////////////////////////////////////////////////////////// | |
void doISRActions(long secsNow, long minsNow, long elapsedTime) { | |
if(spISRAction == LOOP_ACTION_SWITCH_1) switchLearnAutoModeFn(secsNow, minsNow, elapsedTime); | |
else if(spISRAction == LOOP_ACTION_SWITCH_2) switchValveOnOffFn(secsNow, minsNow, elapsedTime); | |
spISRAction = 0; | |
} | |
// switch between learn and auto modes | |
void switch1ISR() { | |
spISRAction = LOOP_ACTION_SWITCH_1; | |
} | |
void switchLearnAutoModeFn(long secsNow, long minsNow, long elapsedTime) { | |
if(DEBUG_ON) Serial.print("In switch 1 fn. New mode: "); | |
spLearnMode = !spLearnMode; | |
EEPROM.write(EE_LRN_MODE_POS, spLearnMode ? 1 : 0); | |
if(DEBUG_ON) Serial.println(spLearnMode ? "learn" : "auto"); | |
lcdStartLine = 0; | |
} | |
// toggle valve manually | |
void switch2ISR() { | |
spISRAction = LOOP_ACTION_SWITCH_2; | |
} | |
void switchValveOnOffFn(long secsNow, long minsNow, long elapsedTime) { | |
if(DEBUG_ON) Serial.println("In switch 2 fn"); | |
switchOnValve(!spValveOn, minsNow); | |
// if learn mode, measure time and do stuff | |
if(spLearnMode) { | |
if(!spValveOn) { // valve was on before and was switched off now | |
switchOnInterval = constrain((switchOnInterval + elapsedTime)/2, 1, MAX_VALVE_ON_MINS); | |
EEPROM.write(EE_ON_INTV_POS, switchOnInterval); | |
} | |
else { // valve was off before and was switched on now | |
spTriggerThreshold = (spTriggerThreshold + readSensor(minsNow)) / 2; | |
writeEEPROM_Int(EE_TRIG_THRES_POS, spTriggerThreshold); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment