/* dogpound.pde - fishing camp status monitor */ #include "SPI.h" #include "avr/pgmspace.h" #include "Ethernet.h" #include "WebServer.h" #include #include #include "pingcode.h" #include #include #include /* Pin Usage Summary: a0 - battery bank (~24v thru resistor) a1 - generator battery (~12v a2 - generator pilot light a3 - house power 2 - (interupt 0) inspeed / vortex anonometer 3 - (interupt 1) unused 4 - chip select for SD card 5 - one-wire data 6 - generator on led / signal 7 - tank depth ping data 10 - hardware CS pin - leave as output for SD funtions to work (note - I have seen that this is 53 on the Mega board, but for me to get it to work on my Mega, I need to keep it at 10) 22 - temp generator pilot 24 - pump on led 26 - uv light on led 28 - snow depth ping data */ // Ethernet and SD card const int chipSelect = 4; int statusSD = 0; // if == 0, not initialized, skip write /* EGL ToDos: - add watchdog - display server records nicely - integrate x10 lib - verify that clock is updated - investigate bootup hang */ // Status Variables float temperatureCurrent[6]={0,0,0,0,0,0}; // 6 thermometers, current, high, low for each float temperatureMax[6]= {-99,-99,-99,-99,-99,-99}; float temperatureMin[6]= {320,320,320,320,320,320}; float windSpeed[3] = {0,0,0}; // MPH: Current, Avg, high long windClicksToday = 0; // used to calc avg mpg long windSecondsToday = 0; // used to calc avg mph int windStrata[3] = {0,0,0}; // Minutes Over 5, 7, 9 mph int genMinutes[2] = {0,0}; // Minutes generator on w/auto, w/ manual request char alarmGen = 'N'; // Generator alarm on (out of gas??) char alarmFridge = 'N'; // Fridge over temp alarm char statusGenOn = 'N'; // Generator Status on/off char statusGenAuto = 'N'; // Generator Auto on/off char statusGenRequest = 'N'; float batteryVolts[3] = {0, 99, -99}; // battery bank volts - current / min / max float genBatteryVolts = 0; // Current generator starter battery volts char statusHousePower = 'N'; // House power (inverter or generator) on char statusPumpOn = 'N'; // Pump Status on/off float currentGallons = 0; const int maxGallons = 4; // Turn the pump off if we get there float currentSnowInches = 0; // showdepth String yesterdaysLogMsg = "no message"; char yesterdaysReady = 'N'; // Y = log ready to post char missedUpload = 'N'; // Y = went a whole day without connecting to server String displayTime = "2011-11-11 11:11"; String bootTime = "2011-11-11 11:11"; String lastPostTime = "2011-11-11 11:11"; String lastUpdateDate = "2011-11-11 11:11"; String yesterdaysLogDate = "2011-11-11 11:11"; int yesterdaysLogDay = 0; // when <> day(), then save yesterdays log fields and clear todays log fields String yesterdaysLog = ""; // holds yesterdays status until it can be logged // Data wire is plugged into port 5 on the Arduino #define ONE_WIRE_BUS 5 #define TEMPERATURE_PRECISION 9 // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs) OneWire oneWire(ONE_WIRE_BUS); // Pass our oneWire reference to Dallas Temperature. DallasTemperature sensors(&oneWire); int oneWireThermometerCount = 6; DeviceAddress oneWireThermometer[6]; char* oneWireThermometerNames[6] = {"1st floor", "2nd floor", "Outside", "Under Cabin", "Fridge", "Freezer"}; DeviceAddress firstfloorThermometer = { 0x28, 0x64, 0xA9, 0x60, 0x3, 0x0, 0x0, 0xB5 }; DeviceAddress secondfloorThermometer = { 0x28, 0x58, 0x2B, 0x61, 0x3, 0x0, 0x0, 0xE2 }; DeviceAddress freezerThermometer = { 0x28, 0x24, 0x27, 0x61, 0x3, 0x0, 0x0, 0x26 }; DeviceAddress outsideThermometer = { 0x28, 0xD7, 0x2B, 0x61, 0x3, 0x0, 0x0, 0x2C }; DeviceAddress undercabinThermometer = { 0x28, 0x44, 0x78, 0x60, 0x3, 0x0, 0x0, 0x90 }; DeviceAddress fridgeThermometer = { 0x28, 0xFA, 0x71, 0x60, 0x3, 0x0, 0x0, 0x77 }; uint8_t* oneWireAddresses[6] = {firstfloorThermometer, secondfloorThermometer, outsideThermometer, undercabinThermometer, fridgeThermometer, freezerThermometer}; const int houseVoltagePin = A0; // select the input pin to read battery voltage const float houseVoltageFactor = 35.06144; const int genVoltagePin = A1; // select the input pin to read battery voltage const float genVoltageFactor = 76.8341; int voltageValue = 0; // variable to store the value coming from the sensor (0 to 1023) const int genPilotPin = A2; // select the output pin to turn on the generator int genPilotValue = 0; // will be 0 for off, 1 for on const int housePowerPin = A3; // select the output pin to turn on the generator int housePowerValue = 0; // will be 0 for off, 1 for on const int genOnPin = 6; const int genRequestPin = 22; const int gscmRunPin = A4; const int gscmFaultPin = A5; #define pumpOnPin 24 // select the output pin to turn on the generator #define statusPin 26 // select the output pin to flash led when updating status unsigned long previousClicksMillis; unsigned volatile int clicks; /* CHANGE THIS TO YOUR OWN UNIQUE VALUE. The MAC number should be * different from any other devices on your network or you'll have * problems receiving packets. */ static byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; /* CHANGE THIS TO MATCH YOUR HOST NETWORK. Most home networks are in * the 192.168.0.XXX or 192.168.1.XXX subrange. Pick an address * that's not in use and isn't going to be automatically allocated by * DHCP from your router. */ static byte myip[] = { 192, 168, 1, 177 }; static byte gateway[] = {192,168,1,12}; static byte subnet[] = {255, 255, 255, 0 }; // The address of the server you want to connect to (doglakecamp.com): byte server[] = {209,236,71,112 }; // initialize the library instance for client for upload: Client client(server, 80); /* all URLs on this server will start with /pound because of how we * define the PREFIX value. We also will listen on port 80, the * standard HTTP service port */ #define PREFIX "" WebServer webserver(PREFIX, 81); /* variables for upload control. */ long unsigned nextPostingTime = 0; long lastConnectionTime = 0; // last time you connected to the server, in milliseconds boolean lastConnected = false; // state of the connection last time through the main loop const long postingInterval = 300000; //delay between updates to the server (1 minutes) long connects = 0; long fails = 0; long seqfails = 0; long maxfails = 0; int currentDay = 0; // when day() changes, reset status summaries /* variables for each minute status timing */ long unsigned nextStatusTime = 0; // last time you performed the status update, in milliseconds const long unsigned statusInterval = 60000; //delay between updates to the server (1 minutes) /* variables for Real Time Clock */ const unsigned int localPort = 8888; // local port to listen for UDP packets byte timeServer[] = { 192, 43, 244, 18}; // time.nist.gov NTP server time_t prevDisplay = 0; // when the digital clock was displayed const long timeZoneOffset = 5L *60L * 60L; // set this to the offset in seconds to your local time; const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets /* This command is set as the default command for the server. It * handles both GET and POST requests. For a GET, it returns a simple * page with some buttons. For a POST, it saves the value posted to * the buzzDelay variable, affecting the output of the speaker */ void incomingWebCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete) { if (type == WebServer::POST) { bool repeat; char name[16], value[16]; Serial.println("found post"); digitalClockDisplay(); do { /* readPOSTparam returns false when there are no more parameters * to read from the input. We pass in buffers for it to store * the name and value strings along with the length of those * buffers. */ repeat = server.readPOSTparam(name, 16, value, 16); Serial.println(name); Serial.println(value); if (strcmp(name, "GenOn") == 0) { Serial.println("Turning Generator On"); digitalWrite(genOnPin, HIGH); } if (strcmp(name, "GenOff") == 0) { Serial.println("Turning Generator Off"); digitalWrite(genOnPin, LOW); } if (strcmp(name, "PumpOn") == 0) { turnPumpOn(); } if (strcmp(name, "PumpOff") == 0) { turnPumpOff(); } } while (repeat); // after procesing the POST data, tell the web browser to reload // the page using a GET method. server.httpSeeOther(PREFIX); return; } /* for a GET or HEAD, send the standard "it's all OK headers" */ server.httpSuccess(); /* we don't output the body for a HEAD request */ if (type == WebServer::GET) { Serial.println("Get Request"); // Display web status screen server.print(""); server.print("Dog Pound Status"); server.print(""); server.print("
"); server.print(""); sensors.requestTemperatures(); server.print("
"); server.print("
"); // server.print("

Todays Temperatures(F):"); server.print("

"); int i; for (i=0; i"); } server.print("
Temperatures(F): Current   Min     Max  
 "); server.print(oneWireThermometerNames[i]); server.print("   "); server.print(temperatureCurrent[i]); server.print(" "); server.print(temperatureMin[i]); server.print("  "); server.print(temperatureMax[i]); server.print("

"); server.print("

"); server.print("
"); getWindValues(); server.print("

Windspeed:
"); server.print("   Now/Max/Avg(MPH): "); server.print(windSpeed[0]); server.print(" / "); server.print(windSpeed[1]); server.print(" / "); server.print(windSpeed[2]); server.print("
"); server.print("   Minutes over 5/7/9mph: "); server.print(windStrata[0]); server.print(" / "); server.print(windStrata[1]); server.print(" / "); server.print(windStrata[2]); server.print("

"); server.print("

Snowdepth (inches): "); currentSnowInches = getSnowInches(); server.print(currentSnowInches); server.print("

"); server.print("
"); server.print("
"); server.print("
"); server.print("
"); // Power section voltageValue = analogRead(houseVoltagePin); Serial.print("housevoltagepin="); Serial.println(voltageValue); batteryVolts[0] = voltageValue / houseVoltageFactor; server.print("

Power Status:
"); server.print("   Battery Volts (cur/min/max): "); server.print(batteryVolts[0]); server.print(" / "); server.print(batteryVolts[1]); server.print(" / "); server.print(batteryVolts[2]); server.print("
"); server.print("   House Power: "); if (statusHousePower == 'N') { server.print("off"); } else { server.print("on"); } server.print("    Generator: "); // should check statusGenOn = 'N' and statusGenAuto = 'N'; updateGenStatus(); if (statusGenOn == 'N') { server.print("off"); } else { if (statusGenOn == 'A') { server.print("on (Auto)"); } else { server.print("on (Manual)"); } } server.print("
"); server.print("   Generator Run Minutes (auto/manual): "); server.print(genMinutes[0]); server.print(" / "); server.print(genMinutes[1]); server.print("
"); server.print("   Generator Alarm (out of gas?): "); server.print(alarmGen); server.print("
"); server.print("   Gen Start Battery Volts: "); voltageValue = analogRead(genVoltagePin); genBatteryVolts = voltageValue / genVoltageFactor; server.print(genBatteryVolts); // server.print("
"); // server.print("

"); server.print("

"); } else { server.print("'GenOff' type='submit' value='GenOff'>

"); } server.print("
"); server.print("
"); server.print("

Water Tank Gallons: "); float gallons; gallons = getPingGallons(); server.print(gallons); server.print("
  Water Pump: "); if (statusPumpOn == 'N') { server.print("off

"); } else { server.print("on

"); } server.print("

"); } else { server.print("'PumpOff' type='submit' value='PumpOff'>

"); } server.print("

"); server.print(""); server.print("
"); } } void setup() { // set the PWM output for the buzzer to out pinMode(genOnPin, OUTPUT); pinMode(pumpOnPin, OUTPUT); pinMode(statusPin, OUTPUT); digitalWrite(statusPin, HIGH); pinMode(genRequestPin, INPUT); Serial.begin(9600); Serial.println("in setup"); // setup the Ehternet library to talk to the Wiznet board Ethernet.begin(mac, myip, gateway, subnet); // set the clock Serial.println("..setup - set the clock"); Udp.begin(localPort); setSyncProvider(getNtpTime); while(timeStatus()== timeNotSet) ; // wait until the time is set by the sync provider setSyncInterval(72000); // go out and reset the clock every 20 hours or so // save the startup time update_displayTime(); bootTime = displayTime; yesterdaysLogDay = day(); yesterdaysLogDate = displayTime; // Start up the one-wire library sensors.begin(); // locate devices on the bus Serial.print("Locating devices..."); Serial.print("Found "); Serial.print(sensors.getDeviceCount(), DEC); Serial.println(" devices."); // report parasite power requirements Serial.print("Parasite power is: "); if (sensors.isParasitePowerMode()) Serial.println("ON"); else Serial.println("OFF"); // set the resolution to 9 bit int i; for (i=0; i= 0){ Serial.println("Time for status update"); nextStatusTime = millis() + statusInterval; updateStatusVariables(); } // if you're not connected to the server, and the specified # seconds have passed since // your last connection, then connect again and send data: if(!client.connected() && (( (long)( millis() - nextPostingTime ) >= 0))) { sendData(23); nextPostingTime = millis() + postingInterval; } // store the state of the connection for next time through // the loop: lastConnected = client.connected(); } void updateStatusVariables(void) { // if we are starting a new day, save the previous days status values for upload and // reinitialize the summary values, if same day, keep track of last time of update digitalWrite(statusPin, HIGH); if (yesterdaysLogDay != day()) { Serial.println("Starting a new log day"); yesterdaysLogDay = day(); yesterdaysLogDate = lastUpdateDate; Serial.println(lastUpdateDate); Serial.println(yesterdaysLogDate); udpateYesterdaysVariables(); // go do the actual update, save to card, and clear yesterdaysReady = 'Y'; // set flag for posting routine to pick up the variables } update_displayTime(); lastUpdateDate = displayTime; // Water currentGallons = getPingGallons(); if (statusPumpOn = 'Y') { if (currentGallons > maxGallons) { turnPumpOff(); } } // Wind getWindValues(); //update wind strata if (windSpeed[0] > 5) windStrata[0]++; if (windSpeed[0] > 7) windStrata[1]++; if (windSpeed[0] > 9) windStrata[2]++; // snow currentSnowInches = getSnowInches(); // Battery Status voltageValue = analogRead(houseVoltagePin); Serial.print("house VoltageValue="); Serial.println(voltageValue); batteryVolts[0] = voltageValue / houseVoltageFactor; if (batteryVolts[0] < batteryVolts[1]) batteryVolts[1] = batteryVolts[0]; if (batteryVolts[0] > batteryVolts[2]) batteryVolts[2] = batteryVolts[0]; // Temperatures sensors.requestTemperatures(); // update max and min temps int i; for (i=0; i temperatureMax[i]) temperatureMax[i] = temperatureCurrent[i]; } // check for fridge problems - fridge over 45 or freezer over 32 (turned off after daily log written) if (temperatureMax[4] > 40) alarmFridge = 'Y'; if (temperatureMax[5] > 25) alarmFridge = 'Y'; // check on the generator voltageValue = analogRead(genVoltagePin); Serial.print("gen VoltageValue="); Serial.println(voltageValue); genBatteryVolts = voltageValue / genVoltageFactor; updateGenStatus(); // update generator run minutes if (statusGenOn == 'Y') { if (statusGenAuto == 'Y') { genMinutes[0]++; } else { genMinutes[1]++; } } housePowerValue = analogRead(housePowerPin); Serial.print("House Power value "); Serial.println(housePowerValue); if (housePowerValue < 100) { statusHousePower = 'N'; } else { statusHousePower = 'Y'; } digitalWrite(statusPin, LOW); } // end of updateStatusVariables() void udpateYesterdaysVariables(void) { int i; int tempint; // note floating variables stored as integers with 2 implied decimals yesterdaysLog = yesterdaysLogDate; yesterdaysLog += ","; for (i=0; i 100) statusGenOn = 'Y'; if (gscmRun == 0) statusGenOn = 'A'; // check to see if we have switched the relay on statusGenRequest = 'N'; int genRequest = digitalRead(genRequestPin); if (genRequest != 0) { statusGenRequest = 'Y'; } Serial.print("Generator status: "); Serial.println(statusGenOn); // if the gscm has the fault led on, report out of gas int gscmFault = analogRead(gscmFaultPin); alarmGen = 'N'; if (gscmFault == 0) alarmGen = 'Y'; } void digitalClockDisplay(){ // digital clock display of the time Serial.print(hour()); printDigits(minute()); printDigits(second()); Serial.print(" "); Serial.print(weekday()); Serial.print(" "); Serial.print(day()); Serial.print(" "); Serial.print(month()); Serial.print(" "); Serial.print(year()); Serial.println(); } void printDigits(int digits){ // utility function for digital clock display: prints preceding colon and leading 0 Serial.print(":"); if(digits < 10) Serial.print('0'); Serial.print(digits); } unsigned long getNtpTime() { Serial.println("sending Ntp packet to timeserver"); sendNTPpacket(timeServer); // send an NTP packet to a time server // wait to see if a reply is available delay(3000); if ( Udp.available() ) { Serial.println("found reply"); Udp.readPacket(packetBuffer,NTP_PACKET_SIZE); // read the packet into the buffer //the timestamp starts at byte 40 of the received packet and is four bytes, // or two words, long. First, esxtract the two words: unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); // combine the four bytes (two words) into a long integer // this is NTP time (seconds since Jan 1 1900): unsigned long secsSince1900 = highWord << 16 | lowWord; Serial.print("Seconds since Jan 1 1900 = " ); Serial.println(secsSince1900); // now convert NTP time into everyday time: Serial.print("Unix time = "); // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: const unsigned long seventyYears = 2208988800UL; // subtract seventy years: unsigned long epoch = secsSince1900 - seventyYears; // print Unix time: Serial.println(epoch); // print the hour, minute and second: Serial.print("The UTC time is "); // UTC is the time at Greenwich Meridian (GMT) Serial.print((epoch % 86400L) / 3600); // print the hour (86400 equals secs per day) Serial.print(':'); Serial.print((epoch % 3600) / 60); // print the minute (3600 equals secs per minute) Serial.print(':'); Serial.println(epoch %60); // print the second // set RTC for EST unsigned long EST = epoch - timeZoneOffset; return EST; } Serial.println(".. no reply found"); return 0; // return 0 if unable to get the time } // this method makes a HTTP connection to the server: void sendData(int thisData) { // if there's a successful connection: float gallons; gallons = getPingGallons(); connects = connects +1; Serial.print("connecting for upload...attempt:"); Serial.print(connects); Serial.print(" failures:"); Serial.print(fails); Serial.print(" maxfails:"); Serial.println(maxfails); if (client.connect()) { Serial.println("connected..."); // build the post request String PostBuff; // the RTC clock is in EST, convert back to UTC for posting PostBuff = String("LogTime="); long unsigned UTC_Time = now() + timeZoneOffset; // not sure why, but add two hours UTC_Time = UTC_Time + (2 *60 * 60); PostBuff += UTC_Time; PostBuff += "&LogItem1=Water&LogValue1="; float tempfloat = gallons * 100.00; int tempint = tempfloat; PostBuff += tempint; PostBuff += "&LogItem2=InTemp&LogValue2="; tempint = temperatureCurrent[0] * 100.00; PostBuff += tempint; PostBuff += "&LogItem3=OutTemp&LogValue3="; tempint = temperatureCurrent[2] * 100.00; PostBuff += tempint; PostBuff += "&LogItem4=Volts&LogValue4="; Serial.println(batteryVolts[0]); tempint = batteryVolts[0] * 100.00; Serial.println(tempint); PostBuff += tempint; PostBuff += "&LogItem5=WindSpeed&LogValue5="; tempint = windSpeed[0] * 100.00; PostBuff += tempint; PostBuff += "&LogItem6=StatMsg&LogValue6="; String statMsg = updateLogMsg(); PostBuff += statMsg; PostBuff += "&LogItem7=SnowDepth&LogValue7="; tempfloat = currentSnowInches * 100.00; tempint = tempfloat; PostBuff += tempint; Serial.println(PostBuff); if (yesterdaysReady == 'Y') { // first post of a day Serial.println("Sending daily log records"); Serial.println(yesterdaysLog); PostBuff += "&yesterdaysLog="; PostBuff += yesterdaysLog; yesterdaysReady = 'N'; } int PostBuffLen = PostBuff.length(); Serial.println(PostBuffLen); seqfails = 0; // send the HTTP PUT request. // fill in your feed address here: client.print("POST http://doglakecamp.com/recievedata.php HTTP/1.1\n"); Serial.print("POST http://doglakecamp.com/recievedata.php HTTP/1.1\n"); client.print("Host: www.doglakecamp.com\n"); Serial.print("Host: www.doglakecamp.com\n"); // fill in your Pachube API key here: // client.print("X-PachubeApiKey: YOUR_KEY_HERE\n"); client.print("Content-Length: "); client.println(PostBuffLen); Serial.print("Content-Length: "); Serial.println(PostBuffLen); // calculate the length of the sensor reading in bytes: // int thisLength = getLength(thisData); // client.println(thisLength, DEC); // last pieces of the HTTP PUT request: client.print("Content-Type: application/x-www-form-urlencoded\n"); Serial.print("Content-Type: application/x-www-form-urlencoded\n"); client.println("Connection: close\n"); Serial.println("Connection: close\n"); // here's the actual content of the PUT request: client.println(PostBuff); Serial.println(PostBuff); // note the time that the connection was made: update_displayTime(); lastPostTime = displayTime; } else { // if you couldn't make a connection: Serial.println("upload connection failed"); fails = fails +1; seqfails = seqfails + 1; if (seqfails > maxfails) maxfails = seqfails; nextPostingTime = millis() + 60000; // try again in a minute } } // function to generate a string with alarm info String updateLogMsg(void) { String tempMsg = ""; if (alarmGen == 'Y') tempMsg += "-Generator (out of gas?)-"; if (alarmFridge == 'Y') tempMsg += "-Fridge Temp Alert-"; if (statusSD == 0) tempMsg += "-SD Card on Fritz-"; if (missedUpload == 'Y') tempMsg += "-Daily Upload Failed-"; if (statusHousePower == 'N') tempMsg += "-House Power Off-"; if (tempMsg.length() < 1) tempMsg = "All is good at the Pound"; return(tempMsg); } // function to print the temperature for a device void printTemperature(DeviceAddress deviceAddress) { float tempC = sensors.getTempC(deviceAddress); Serial.print("Temp C: "); Serial.print(tempC); Serial.print(" Temp F: "); Serial.print(DallasTemperature::toFahrenheit(tempC)); } // function to print a device's resolution void printResolution(DeviceAddress deviceAddress) { Serial.print("Resolution: "); Serial.print(sensors.getResolution(deviceAddress)); Serial.println(); } // main function to print information about a device void printData(DeviceAddress deviceAddress) { Serial.print("Device Address: "); printAddress(deviceAddress); Serial.print(" "); printTemperature(deviceAddress); Serial.println(); } // function to print a device address void printAddress(DeviceAddress deviceAddress) { for (uint8_t i = 0; i < 8; i++) { // zero pad the address if necessary if (deviceAddress[i] < 16) Serial.print("0"); Serial.print(deviceAddress[i], HEX); } } void getWindValues(){ unsigned long now = millis(); float windCountTime = (now - previousClicksMillis) / 1000.0; // note - need to deal with millis() rollover here windSpeed[0] = 2.5 * ((float) clicks / windCountTime); // update avg mph windSecondsToday = windSecondsToday + windCountTime; windClicksToday = windClicksToday + clicks; windSpeed[2] = 2.5 * ((float) windClicksToday / (float) windSecondsToday); // reset counters for next reading clicks = 0; previousClicksMillis = now; // check to see if we have a new high windspeed if (windSpeed[0] > windSpeed[1]) { windSpeed[1] = windSpeed[0]; } } void clicker(){ clicks++; } void update_displayTime() { displayTime = year(); displayTime = displayTime + "/" + month() + "/" + day() + " " + hour() + ":"; String tempMin = minute(); int tempMinLength = tempMin.length(); if (tempMinLength == 2) { displayTime = displayTime + tempMin; } else { displayTime = displayTime + "0" + tempMin; } } // function to turn water pump on void turnPumpOn() { Serial.println("Turning Pump On"); digitalWrite(pumpOnPin, HIGH); statusPumpOn = 'Y'; } // function to turn water pump off void turnPumpOff() { Serial.println("Turning Pump Off"); digitalWrite(pumpOnPin, LOW); statusPumpOn = 'N'; }