Greenhouse Controller Feather Software

This code needs more documentation! I hope to add some in the fullness of time. Apologies for poor or inefficient coding! NB. This code plugin does nor wrap text. Use arrow keys, mouse, pad or scroll bars at the bottom to view text on extreme right.

// ****************************************************************************
// * greenhouse_30
// * 6.9.19
// * This program controls and monitors conditions in the greenhouse
// *
// * Since v13, removes "built in temp sensor" - releases 0x48 I2C address
// * Since v14, both watering channels on and on together, auto & manual
// * Since v15, water pressure gauge added
// * Since v16, reduction of amount of data through Serial.print
// * Since v17, temperature sent to Rpi is now from htu (as tmp102 removed - see above)
// * Since v18, more data sent through udp
// * Since v19, connection of control/switch-box/led indicators over I2C
// * Since v28, watering logic seems to have been sorted
// * In v30, pressure parameters are changed yet again as water supply pressure seems to drop
// * Also in v30, watering time has been reduced to 
// * NB: needs to be plugged it for sketch to run
// * Manual watering by Pi turns off after fixed time
// * Manual watering by switch unit does not (at present)
// * Rain gauge plus external temperature
// * Dampness monitor
// * Remote temperature and humidity sensor
// * Remote control watering, 2 channels and auto watering - both channels on together
// * Logs date and time of re-boot in log_file.txt
// * data logs rainfall
// * Now can recieve manual input from serial monitor. Menu items 1 - 9.
// * So far, inputting '1' results in a dump of the rainfall data - more to follow...
// * Next development: log wind direction & speed, solar radiation etc
// * These fanctions (above) now handled by a serarate unit
// * Gets input from serial
// * from v6, saves date/time of reboot with time stamp using String
// * Feather is "Feather 3"
// * By Julian Rogers
// *****************************************************************************
//libraries
#include <SPI.h>
#include <WiFi101.h>
#include <WiFiUdp.h>
#include "RTClib.h"
#include <SPI.h>         
#include <Wire.h>
#include <SD.h>
#include <Adafruit_MCP23017.h>
#include "Adafruit_HTU21DF.h"
#include "arduino_secrets.h"
///////please enter your sensitive data in the Secret tab/arduino_secrets.h
// adc for water pressure gauge
#define PCF8591 (0x90 >> 1)
// acd2 is connected to the pressure meter
// 0 is ldr, 1 is termistor, 3 is pot used to calibrate gauge
// see int adc0, adc1, adc2, adc3 in function getPressure()
// The following refers to the MCP23017 incorporated in the rain gauge - not in the controller!
// pin 6 (a6 on chip numbering) is connected to picaxe reset pin (needs to be high to run)
// pins 8 to 15 (b0 to b7 using chip numberings) are used to log the count from the picaxe
// The input to the Picaxe from the 556 is on input 2
// input 1 is connected to pin 7 on the MCP23017 (a7) for possible future use
// Files on SD card are: TIPS_TOT.txt (RG running total), TIPS_LOG.txt (RG current data with time stamp)
// TEMPROG1.txt (stores heater thermostat setting), DAMP.txt (when implemented, will log dampness data with time stamp)
// log_file.txt (logs time and date for each successful reboot)
char ssid[] = SECRET_SSID;        // your network SSID (name)
char pass[] = SECRET_PASS;        // your network password (use for WPA, or use as key for WEP)
int status = WL_IDLE_STATUS;      // the WiFi radio's status
bool resetRGflag = false;          // flag to indicate rain gauge has been reset
bool statFlag = true;             // heater logic
bool onFlag = false;              // heater logic
bool waterOnFlag = false;         // watering logic
bool waterFlag = false;           // watering logic
int onTime = 0;                   // watering logic
const int waterTime = 1;          // time for watering in minutes
// daily auto watering logic
int autoOnTime;                       // stores tim value at start of timed period
const int autoWaterTime = 3;          // time for watering in minutes
const int autoStartWatering = 1020;    // time watering starts in minutes (9am) NB: no auto winter/summertime switch
bool autoWaterFlag = true;            // keeps track of whether daily watering has been done
// true at startup prevents watering if time later than autoStartWatering
bool autoWaterOnFlag = false;         // keeps track of whether water is currently on
bool manWaterOnFlag = false;
bool manWaterPoss = false;
int manStartWater;                    // time manual watering time starts (minutes)
const int manWaterTime = 2;
// four flags corresponding to switches on switch/led unit
bool redSwitchFlag = false;
bool blueSwitchFlag = false;
bool yellowSwitchFlag = false;
bool greenSwitchFlag = false;
const float hysteresis = 0.5;     // heating ventilation logic
char tempSetString[4];
int setTemp = 30;              // temp x 10 - value is set from SD card
int count = 0;                  // counts temperature measurements
int countTwo = 0;               // counts delay between measurements
int counter = 0;                // counts when to check for WiFi connection
int count2 = 0;                 // counts total number of attempts to (re)connect since power up
int numAttempts = 0;            // counts number of attempts to connect to WiFi
int fanTemp = 200;              // threshold for triggering ventilation fan (to be setable later)
const int tempDelay = 5;
const int numMeasures = 9;
float x, y, a, b;                     // used in temp sensor data processing
int tempSmoother[10], tempSmoother2[10];           // used in temp sensor data processing
const float alpha = 0.5;        // temp data smoothing factor
int tempNow, tempNow2;
int extTemp;
byte b0, b1, b2, b3, b4, b5, b6, b7;    // digits received from rain gauge
int val;                        // rain gauge current count
int runtotal;                   // rain gauge running total
int damp_run_total = 0;
int waterPress;                 // water pressure
//const byte  TMP102_ADD_1=0x48;  // I2C address TMP102 A0 to GND (0x48 = 72 = 1001000 for GND, 73 for vcc)
const byte  TMP102_ADD_2=0x49;  // second tmp102 (in rain gauge) 
const byte  mcp_address=0x20;      // I2C Address of MCP23017 Chip
const byte  GPIOA=0x12;            // Register Address of Port A
const byte  GPIOB=0x13;            // Register Address of Port B
unsigned int localPort = 2390;      // local port to listen on
char packetBuffer[255]; //buffer to hold incoming packet
char  ReplyBuffer[255];       // a string to send back
int yr, mon, dofmon, dofwk, hr, mn, sec, tim;
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
char hourStr[7];
char dayStr[7];
char monthStr[7];
char clockSetString[32];
unsigned int wifiCount = 0;
unsigned long duration;
char dampString[4];
int damp;
char tipsTotString[4];
char currentTipsStr[4];
// int tipsTot;
float htuTemp;      //why not int?
float htuHumid;     //why not int?
//get input over serial
char recChar;
bool newData = false;
int menuItem;
bool serialFlag = true;
//----------------------------------------------------------------
WiFiUDP Udp;
Adafruit_MCP23017 mcp;
Adafruit_MCP23017 mcp1;
Adafruit_MCP23017 mcp2;
RTC_PCF8523 rtc;
File myFile;
Adafruit_HTU21DF htu = Adafruit_HTU21DF();
//-----------------------------------------------------------------------------------------------------------------
void setup() {
delay(1000);
Serial.begin(9600);
/*
while (!Serial) {
Serial.println("waiting for serial..."); //Needed for native USB port only but if used waits until serial monitor opened
//Also used for diagnostic purposes
}
*/
Serial.println("starting...");
Wire.begin();               // join i2c bus (address optional for master)
mcp.begin(0);               // use base address (0,0,0) for MCP23017 in unit
mcp1.begin(1);              // use address 0x21 (0,0,1) rather than default for rain gauge
mcp2.begin(2);              // this is for the plug-in switch/led unit
Serial.print("progress 1...");
// these pins are used for relays
mcp.pinMode(0, OUTPUT);     // ventilation fan
mcp.pinMode(1, OUTPUT);     // water valve 2: now water valve 1, mist
mcp.pinMode(2, OUTPUT);     // water valve 1: now water valve 2, seep hose
mcp.pinMode(3, OUTPUT);     // heater relay
mcp.pinMode(4, OUTPUT);     // RPi reset relay: check isn't battery swap out relay!!
Serial.print("progress 2...");
mcp.digitalWrite(0, LOW);
mcp.digitalWrite(1, LOW);
mcp.digitalWrite(2, LOW);
mcp.digitalWrite(3, LOW);
mcp.digitalWrite(4, LOW);
// setup pins on the "b" bank of the controller mcp23017
mcp.pinMode(8, INPUT);      //yellow -WATER
mcp.pinMode(9, INPUT);      //white  -reset rain gauge
mcp.pinMode(10, INPUT);     //green
mcp.pinMode(11, INPUT);     //blue
mcp.pinMode(12, INPUT);     //orange
mcp.pinMode(13, INPUT);     //black
mcp.pinMode(14, INPUT);     //brown
mcp.pinMode(15, INPUT);     //n/c
Serial.print("progress 3...");
// setup pins on the rain gauge
mcp1.pinMode(8, INPUT);
mcp1.pinMode(9, INPUT);
mcp1.pinMode(10, INPUT);
mcp1.pinMode(11, INPUT);
mcp1.pinMode(12, INPUT);
mcp1.pinMode(13, INPUT);
mcp1.pinMode(14, INPUT);
mcp1.pinMode(15, INPUT);
mcp1.pinMode(6, OUTPUT);
mcp1.digitalWrite(6, HIGH);
Serial.print("progress 4...");
pinMode(16, OUTPUT);   //running LED
pinMode(17, OUTPUT);     //RPi reset relay
pinMode(10, OUTPUT); // SD card chip select
// Configure pins for Adafruit ATWINC1500 Feather
WiFi.setPins(8,7,4,2);
mcp2.pinMode(7, INPUT);       // four push-button switches: red
mcp2.pinMode(6, INPUT);       // blue
mcp2.pinMode(5, INPUT);       // yellow
mcp2.pinMode(4, INPUT);       // green
mcp2.pinMode(3, OUTPUT);      // four unused lines
mcp2.pinMode(2, OUTPUT);
mcp2.pinMode(1, OUTPUT);
mcp2.pinMode(0, OUTPUT);
mcp2.pinMode(8, OUTPUT);      // nc
mcp2.pinMode(9, OUTPUT);      // red led
mcp2.pinMode(10, OUTPUT);     // nc
mcp2.pinMode(11, OUTPUT);     // blue led
mcp2.pinMode(12, OUTPUT);     // nc
mcp2.pinMode(13, OUTPUT);     // yellow led
mcp2.pinMode(14, OUTPUT);     // nc
mcp2.pinMode(15, OUTPUT);     // green led
mcp2.digitalWrite(9, LOW);
mcp2.digitalWrite(11, LOW);
mcp2.digitalWrite(13, LOW);
mcp2.digitalWrite(15, LOW);
// check for the presence of the shield:
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println("WiFi shield not present");
// don't continue:
while (true);
}
// attempt to connect to WiFi network:
while ( status != WL_CONNECTED) {
Serial.print("Attempting to connect to WPA SSID: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network:
status = WiFi.begin(ssid, pass);
// wait 10 seconds for connection:
delay(10000);
}
// you're connected now, so print out the data:
Serial.print("Connected to the network");
printCurrentNet();
printWiFiData();
Serial.println("\nStarting connection to server...");
// if you get a connection, report back via serial:
Udp.begin(localPort);
if (!SD.begin(10)) {
Serial.println("SD initialization failed!");
return;
}
Serial.println("SD initialization done.");
// open file and get values
myFile = SD.open("TEMPROG1.txt");
if (myFile) {
Serial.println("TEMPROG1.txt:");
// read from the file until there's nothing else in it:
byte index = 0;
while (myFile.available()) {
tempSetString[index] = myFile.read();
index++;
Serial.println("reading..");
}
// close the file:
myFile.close();
if(index != 3){
Serial.println("SD data error");  
}
else{
setTemp = atoi(tempSetString);
Serial.print("Set temp on SD card is ");
Serial.println(setTemp);
}
}
// open file and get values
myFile = SD.open("DAMP.txt");
if (myFile) {
Serial.println("DAMP.txt:");
// read from the file until there's nothing else in it:
byte index = 0;
while (myFile.available()) {
dampString[index] = myFile.read();
index++;
Serial.println("reading..");
}
// close the file:
myFile.close();
damp = atoi(dampString);
Serial.print("Dampness on SD card is ");
Serial.println(damp);
}
// open file and get values
myFile = SD.open("TIPS_TOT.txt");
if (myFile) {
Serial.println("TIPS_TOT.txt:");
// read from the file until there's nothing else in it:
byte index = 0;
while (myFile.available()) {
tipsTotString[index] = myFile.read();
index++;
Serial.println("reading..");
}
// close the file:
myFile.close();
runtotal = atoi(tipsTotString);
Serial.print("Tips total on SD card is ");
Serial.println(runtotal);
}
// log time and date for each re-boot
myFile = SD.open("log_file.txt" , FILE_WRITE);
DateTime now = rtc.now();
if(myFile){
myFile.print(now.year(), DEC);
myFile.print('/');
myFile.print(now.month(), DEC);
myFile.print('/');
myFile.print(now.day(), DEC);
myFile.print(" (");
myFile.print(daysOfTheWeek[now.dayOfTheWeek()]);
myFile.print(") ");
myFile.print(now.hour(), DEC);
myFile.print(':');
myFile.print(now.minute(), DEC);
myFile.print(':');
myFile.print(now.second(), DEC);
myFile.println();
myFile.close();
}
else{
Serial.println("could not open log file");
}
calcTime();         // update values representing components of date and time
// NB: thanks to new timeStamp function, calcTime could be further rationalised
tim = hr * 60 + mn;
}
// end of setup
//-----------------------------------------------------------------------------------------------------------
// Functions start here
void printWiFiData() {
// print your WiFi shield's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
//Serial.println(ip);
// print your MAC address:
byte mac[6];
WiFi.macAddress(mac);
Serial.print("MAC address: ");
Serial.print(mac[5], HEX);
Serial.print(":");
Serial.print(mac[4], HEX);
Serial.print(":");
Serial.print(mac[3], HEX);
Serial.print(":");
Serial.print(mac[2], HEX);
Serial.print(":");
Serial.print(mac[1], HEX);
Serial.print(":");
Serial.println(mac[0], HEX);
}
//--------------------------------------------------------------------------------------------------------
void printCurrentNet() {
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print the MAC address of the router you're attached to:
byte bssid[6];
WiFi.BSSID(bssid);
Serial.print("BSSID: ");
Serial.print(bssid[5], HEX);
Serial.print(":");
Serial.print(bssid[4], HEX);
Serial.print(":");
Serial.print(bssid[3], HEX);
Serial.print(":");
Serial.print(bssid[2], HEX);
Serial.print(":");
Serial.print(bssid[1], HEX);
Serial.print(":");
Serial.println(bssid[0], HEX);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.println(rssi);
// print the encryption type:
byte encryption = WiFi.encryptionType();
Serial.print("Encryption Type:");
Serial.println(encryption, HEX);
Serial.println();
}
//-----------------------------------------------------------------------------------------------
int getTemp102(byte ADD_TMP102){
byte firstbyte, secondbyte; //these are the bytes we read from the TMP102 temperature registers
int value; //an int is capable of storing two bytes, this is where we "chuck" the two bytes together.
float convertedtemp; //We then need to multiply our two bytes by a scaling factor, mentioned in the datasheet.
//float correctedtemp; 
// The sensor overreads? I don't think it does! 
//Reset the register pointer (by default it is ready to read temperatures)
//You can alter it to a writeable register and alter some of the configuration - 
//the sensor is capable of alerting you if the temperature is above or below a specified threshold.
Wire.beginTransmission(ADD_TMP102); // start talking to sensor 
Wire.write(0x00);
Wire.endTransmission();
Wire.requestFrom(ADD_TMP102, 2);
Wire.endTransmission();
firstbyte      = (Wire.read()); 
//read the TMP102 datasheet - here we read one byte from
//each of the temperature registers on the TMP102
secondbyte     = (Wire.read()); 
//The first byte contains the most significant bits, and 
//the second the less significant 
value = firstbyte;
if ((firstbyte & 0x80) > 0) {
value |= 0x0F00;
} 
value <<= 4;
//MSB
value |= (secondbyte >> 4);    
// LSB is ORed into the second 4 bits of our byte.
convertedtemp = value*0.625; // temp x 10
//correctedtemp = convertedtemp - 0;   //should be 5 according to playground author
int temp = (int)convertedtemp;
return temp;
}
//-----------------------------------------------------------------------------------------------
//void expand(byte num) {
//  Wire.beginTransmission(mcp_address);
//  Wire.write(GPIOA);      // address bank A
//  Wire.write(num);  // value to send
//  Wire.endTransmission();
//}
//-----------------------------------------------------------------------------------------------
void calcTime(){
DateTime now = rtc.now();
yr = int(now.year());
mon = int(now.month());
dofmon = int(now.day());
dofwk = int(now.dayOfTheWeek()) + 1;
hr = int(now.hour());
mn = int(now.minute());
sec = int(now.second());
}
//---------------------------------------------------------------------------------------------
//function to return a date stamp
String timeStamp() {
DateTime now = rtc.now();
String timeStampStr = "";
timeStampStr += String(now.year());
timeStampStr += "/";
timeStampStr += String(now.month());
timeStampStr += "/";
timeStampStr += String(now.day());
timeStampStr += " (";
timeStampStr += daysOfTheWeek[now.dayOfTheWeek()];
timeStampStr += ") ";
timeStampStr += String(now.hour());
timeStampStr += ":";
timeStampStr += String(now.minute());
timeStampStr += ":";
timeStampStr += String(now.second());
return timeStampStr;
}
//-----------------------------------------------------------------------------------------------
//function to extract values from clockSetString
//see function "adjClock()"
byte getClock(byte index) {
char selectorString[3];
selectorString[0] = clockSetString[index];
index++;
selectorString[1] = clockSetString[index];
int result = atoi(selectorString);
return result;
}
//------------------------------------------------------------------------------------------------
// adjusts clock
void adjClock(){
mn = getClock(0);
hr = getClock(3);
dofwk = getClock(6);
dofmon = getClock(9);
mon = getClock(12);
yr = getClock(15);
Serial.println("Adjusting clock! - New times are:");
Serial.println(mn);
Serial.println(hr);
Serial.println(dofwk);
Serial.println(dofmon);
Serial.println(mon);
yr = yr + 2000;
Serial.println(yr);
//rtc.adjust(DateTime(2016, 10, dofmon, hr, mn, 0));
rtc.adjust(DateTime(yr, mon, dofmon, hr, mn, 0));
}
//----------------------------------------------------------------------------------
void dumpRain(){
//open the file for reading:
myFile = SD.open("TIPS_LOG.txt");
if (myFile) {
Serial.println("Rain gauge data:  TIPS_LOG.txt:");
// read from the file until there's nothing else in it:
while (myFile.available()) {
Serial.write(myFile.read());
}
// close the file:
myFile.close();
} else {
// if the file didn't open, print an error:
Serial.println("error opening log_file.txt");
}
Serial.println("");
//open the file for reading:
myFile = SD.open("TIPS_TOT.txt");
if (myFile) {
Serial.print("Rain gauge running total:  TIPS_TOT.txt: ");
// read from the file until there's nothing else in it:
while (myFile.available()) {
Serial.write(myFile.read());
}
// close the file:
myFile.close();
Serial.println("");
} else {
// if the file didn't open, print an error:
Serial.println("error opening running total log_file.txt");
}
}
//----------------------------------------------------------------------
void showNewData() {
if (newData) {
menuItem = recChar - 48; // ascii 0 = 48. Deleting this gives numbers
// eliminate spurious resuls from entering letters etc
if(menuItem > 0 && menuItem < 10){ 
Serial.println(menuItem);
}
newData = false;
}
}
//------------------------------------------------------------------------
void recvOneChar() {
if (Serial.available() > 0) {
recChar = Serial.read();    //use Serial.read() as it does not block the sketch!
newData = true;
}
}
//------------------------------------------------------------------------------------------------
int getPressure() {
Wire.beginTransmission(PCF8591);
Wire.write(0x04);
Wire.endTransmission();
Wire.requestFrom(PCF8591, 5);
int adc0, adc1, adc2, adc3;
// adc2 gives pressure
adc0=Wire.read();
adc0=Wire.read();
adc1=Wire.read();
adc2=Wire.read();
adc3=Wire.read();
adc2 = adc2 - 30;
int c = adc3 * 2;
adc2 = constrain(adc2, 0, 185);
int x = map(adc2, 0, c, 0, 100);
return x; 
}
// End of functions
//-------------------------------------------------------------------------------------------------
void loop() {
calcTime();         // update values representing components of date and time
// NB: thanks to new timeStamp function, calcTime could be further rationalised
tim = hr * 60 + mn;
//Serial.println(timeStamp());
//.............................................................................
counter++;
if(counter == 75){
counter = 0;
printCurrentNet();
Serial.print("Num of attemts needed to reconnect: ");
Serial.println(count2);
//count2++;
if(WiFi.status() == WL_CONNECTED){
Serial.println("connected");
}
else{
Serial.println("disconnected");
while ( WiFi.status() != WL_CONNECTED && numAttempts < 3) {
Serial.print("Attempting to connect to WPA SSID: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network:
status = WiFi.begin(ssid, pass);
// wait 10 seconds for connection:
delay(10000);
Udp.begin(localPort);
numAttempts++;
}
numAttempts = 0;
count2++;   // total number of attempts to (re)connect since power up
}
}
//.............................................................................
// Here is where the progam checks to see if data is being sent
// and sends back status data
// if there's data available, read a packet
int packetSize = Udp.parsePacket();
if (packetSize)
{
Serial.print("Received packet of size ");
Serial.println(packetSize);
Serial.print("From ");
IPAddress remoteIp = Udp.remoteIP();
Serial.print(remoteIp);
Serial.print(", port ");
Serial.println(Udp.remotePort());
// read the packet into packetBufffer
int len = Udp.read(packetBuffer, 255);
if (len > 0) packetBuffer[len] = 0;
Serial.println("Contents:");
Serial.println(packetBuffer);
for(int x = 0; x < (len + 1); x++){
ReplyBuffer[x] = packetBuffer[x];
}
// send a reply, to the IP address and port that sent us the packet we received
char timStr[7];  
char tempNowStr[7];
char setTempStr[7];
itoa(tim, timStr, 10);
itoa(tempNow2, tempNowStr, 10);
itoa(setTemp, setTempStr, 10);
char outTempStr[7];
char fanSetStr[7];
char rHumidStr[7];
char tipsStr[7];
char wPressStr[7];
char dampStr[7];
itoa(extTemp, outTempStr, 10);
itoa(fanTemp, fanSetStr, 10);
itoa(htuHumid, rHumidStr, 10);
itoa(runtotal, tipsStr, 10);
itoa(waterPress, wPressStr, 10);
itoa(duration, dampStr, 10);
Serial.println("sending reply...");
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
Udp.write(timStr);
Udp.write(":");
Udp.write(tempNowStr);
Udp.write(":");
Udp.write(setTempStr);
Udp.write(":");
Udp.write(outTempStr);
Udp.write(":");
Udp.write(fanSetStr);
Udp.write(":");
Udp.write(rHumidStr);
Udp.write(":");
Udp.write(tipsStr);
Udp.write(":");
Udp.write(wPressStr);
Udp.write(":");
Udp.write(dampStr);
Udp.endPacket();
//-------------------------------------------------------
//get new thermostat setting
//format: ttt (TdegC x 10)
if(packetSize == 3){
setTemp = atoi(packetBuffer);
Serial.print("new thermo setting: ");
Serial.println(setTemp);
//save to SD card
SD.remove("TEMPROG1.txt");    //first delete previous file
myFile = SD.open("TEMPROG1.txt", FILE_WRITE);
if (myFile) {
//myFile.print(tempSetString);
myFile.print(packetBuffer);
myFile.close();
} 
else {
Serial.println("SD - failed to write data!");
//add error to UDP transmission
}
}
//------------------------------------------------------
//get new clock settings 
//format: mm,hh,dd,dd,mm,yy
if(packetSize == 17){
for(byte x = 0; x < 18; x++){
clockSetString[x] = packetBuffer[x];
}
Serial.println(clockSetString);
adjClock(); 
}
//--------------------------------------------------------
// reset Raspberry Pi
String(pktbuf) = String(packetBuffer);
if(pktbuf == String("reset")){
//if(packetSize == 5){
Serial.println("Resetting Pi!");
digitalWrite(17, HIGH);
delay(5000);
digitalWrite(17, LOW);
}
//--------------------------------------------------
// test battery switch
if(pktbuf == String("switch"))
{
mcp.digitalWrite(4, HIGH);
delay(2000);
mcp.digitalWrite(4,LOW);
Serial.println("tested battery switch");
}
//-------------------------------------------
// get heat instructions
if(pktbuf == String("heaton"))
{
onFlag = true;
}
// get heat instructions
if(pktbuf == String("heatoff"))
{
onFlag = false;
}
// get stat instructions
if(pktbuf == String("staton"))
{
statFlag = true;
}
// get heat instructions
if(pktbuf == String("statoff"))
{
statFlag = false;
}
} 
delay(200);
//------------------------------------------------
// *** start of measurement routines for all sensors ***
// get the internal temperature
countTwo++;
if(countTwo > tempDelay){
countTwo = 0;
htuHumid = htu.readHumidity();
htuHumid = round(htuHumid * 10);
htuTemp = htu.readTemperature();
htuTemp = round(htuTemp * 10);
tempSmoother2[count] = htuTemp;
count++;
if (count > numMeasures) {
count = 0;
calcTime();
a = tempSmoother2[0];
b = alpha * tempSmoother2[1] + (1 - alpha) * a;
a = alpha * tempSmoother2[2] + (1 - alpha) * b;
b = alpha * tempSmoother2[3] + (1 - alpha) * a;
a = alpha * tempSmoother2[4] + (1 - alpha) * b;
b = alpha * tempSmoother2[5] + (1 - alpha) * a;
a = alpha * tempSmoother2[6] + (1 - alpha) * b;
b = alpha * tempSmoother2[7] + (1 - alpha) * a;
a = alpha * tempSmoother2[8] + (1 - alpha) * b;
b = alpha * tempSmoother2[9] + (1 - alpha) * a;
tempNow2 = round(b);
Serial.println(timeStamp());
waterPress = getPressure();
Serial.print("Water pressure is ");
Serial.println(waterPress);
Serial.print("Internal htu temp X10: ");
Serial.println(tempNow2);
//Serial.print("Built in sensor temp X10: ");
//Serial.println(tempNow);
Serial.print("Set temp X10 is ");
Serial.println(setTemp);
if (b > setTemp + hysteresis && statFlag == true){
//expand(0);
Serial.println("Heater off");
mcp.digitalWrite(3, LOW);       // heater off
}
if (b <= setTemp - hysteresis && statFlag == true){
//expand(8);
Serial.println("Heater on");
mcp.digitalWrite(3, HIGH);       // heater on
}
if (statFlag == false && onFlag == true){
Serial.println("Heater on");
mcp.digitalWrite(3, HIGH);       // heater on 
}
if (statFlag == false && onFlag == false){
Serial.println("Heater off");
mcp.digitalWrite(3, LOW);       // heater off 
}
if (b > fanTemp + hysteresis){
Serial.println("vent fan on");
mcp.digitalWrite(0, HIGH);      // ventilation fan on
}
if (b <= fanTemp - hysteresis){
Serial.println("vent fan off");
mcp.digitalWrite(0, LOW);       // ventilation fan off
}
//---------------------------------------------------------------------
// get value from rain gauge
b0 = mcp1.digitalRead(15);
b1 = mcp1.digitalRead(14);
b2 = mcp1.digitalRead(13);
b3 = mcp1.digitalRead(12);
b4 = mcp1.digitalRead(11);
b5 = mcp1.digitalRead(10);
b6 = mcp1.digitalRead(9);
b7 = mcp1.digitalRead(8);
val = b7 * 128 + b6 * 64 + b5 * 32 + b4 * 16 + b3 * 8 + b2 * 4 + b1 * 2 + b0;
extTemp = getTemp102(TMP102_ADD_2);
Serial.print("External temp X10 = ");
Serial.println(extTemp);
Serial.print("Tips from remote = ");
Serial.println(val);
runtotal = runtotal + val;
Serial.print("Tips running total = ");
Serial.println(runtotal);
// if data has changed, save data to file (etesting append data with time stamp)
if(val > 0){
itoa(runtotal, tipsTotString, 10);
itoa(val, currentTipsStr, 10);
SD.remove("TIPS_TOT.txt");
myFile = SD.open("TIPS_TOT.txt" , FILE_WRITE);
if(myFile){
myFile.print(tipsTotString);
myFile.close();
}
else{
Serial.println("SD - failed to write tips total");
}
myFile = SD.open("TIPS_LOG.txt", FILE_WRITE);
if(myFile){
myFile.print(timeStamp());
myFile.print("-");
myFile.print(currentTipsStr);
myFile.println("");
myFile.close();
}
else{
Serial.println("SD - failed to write current tips");  
}
}
//reset remote counter on gauge
mcp1.digitalWrite(6, LOW);
mcp1.digitalWrite(6, HIGH); 
//-------------------------------------------------------------
htuTemp = htu.readTemperature();
Serial.println("Intantaneous htu:");
Serial.print("Temp: "); Serial.print(tempNow2);
Serial.print("\t\tHum: "); Serial.println(htu.readHumidity());
//------------------------------------------------------------
//get dampness
//trigger isolated power supply
mcp.digitalWrite(4, HIGH);
delay(500);                             //gives puses time to start
duration = pulseIn(13, HIGH, 50000000);
duration = duration/100;
//duration = 35;                          //for testing purposes
Serial.print("dampness value: ");
Serial.println(duration);
delay(500);
//turn off isolated supply
mcp.digitalWrite(4, LOW);
damp_run_total++;
Serial.print("run total: ");
Serial.println(damp_run_total);
//-------------------------------------------------------------
if (WiFi.status() == WL_CONNECTED){
Serial.println("Connected");
}
else{
Serial.println("Not connected!");
}
}
}
//--------------------------------------------------------------
// manual watering routine by Pi via Tkinter/Python interface
int valFromPi = mcp.digitalRead(8);     // button on Pi Tkinter
if(valFromPi == 1 && manWaterPoss == true){
mcp.digitalWrite(2, HIGH);          // water on (hose)
mcp.digitalWrite(1, HIGH);          // water on (mist)
Serial.println("Manual watering on (Pi)");
manStartWater = tim;
manWaterOnFlag = true;
manWaterPoss = false;
}
if(manWaterOnFlag == true && tim >= (manStartWater + manWaterTime)){
mcp.digitalWrite(2, LOW);          // water off (hose)
mcp.digitalWrite(1, LOW);          // water off (mist)
Serial.println("Manual watering off (Pi)");
manWaterOnFlag = false;
}
if(valFromPi == 0 && manWaterOnFlag == true){
mcp.digitalWrite(2, LOW);          // water off (hose)
mcp.digitalWrite(1, LOW);          // water off (mist)
manWaterOnFlag == false;
}
if(valFromPi == 0 && waterOnFlag == false){
manWaterPoss = true;
}
//-----------------------------------------------------------------------
// manual watering by switch unit
int valButRed = mcp2.digitalRead(7);    // red button on switch/led unit
if (valButRed == 0){
mcp2.digitalWrite(9, HIGH);         // red led on switch unit
mcp2.digitalWrite(11, LOW);         // blue led on switch unit
redSwitchFlag = true;
blueSwitchFlag = false;
}
int valButBlue = mcp2.digitalRead(6);    // blue button on switch/led unit
if (valButBlue == 0){
mcp2.digitalWrite(11, HIGH);         // blue led on switch unit
mcp2.digitalWrite(9, LOW);         // red led on switch unit
redSwitchFlag = false;
blueSwitchFlag = true;
}
int valButYellow = mcp2.digitalRead(5);    // blue button on switch/led unit
if (valButYellow == 0){
mcp2.digitalWrite(13, HIGH);         // yellow led on switch unit
mcp2.digitalWrite(15, LOW);         // green led on switch unit
greenSwitchFlag = false;
yellowSwitchFlag = true;
}
int valButGreen = mcp2.digitalRead(4);    // blue button on switch/led unit
if (valButGreen == 0){
mcp2.digitalWrite(15, HIGH);         // green led on switch unit
mcp2.digitalWrite(13, LOW);         // yellow led on switch unit
yellowSwitchFlag = false;
greenSwitchFlag = true;
}
if (redSwitchFlag){
mcp.digitalWrite(2, HIGH);          // water on (hose)
//mcp.digitalWrite(1, HIGH);          // water on (mist)
redSwitchFlag = false;
waterOnFlag = true;
Serial.println("water on (hose)!");
}
if (blueSwitchFlag){
mcp.digitalWrite(2, LOW);         // water off (hose)
//mcp.digitalWrite(1, LOW);         // water off (mist)
blueSwitchFlag = false;
Serial.println("water off (hose)!");
waterOnFlag = false;
}
if (yellowSwitchFlag){
//mcp.digitalWrite(2, HIGH);          // water on (hose)
mcp.digitalWrite(1, HIGH);          // water on (mist)
yellowSwitchFlag = false;
waterOnFlag = true;
Serial.println("water on (mist)!");
}
if (greenSwitchFlag){
//mcp.digitalWrite(2, LOW);         // water off (hose)
mcp.digitalWrite(1, LOW);         // water off (mist)
greenSwitchFlag = false;
Serial.println("water off (mist)!");
waterOnFlag = false;
}
//------------------------------------------------------------------------------  
// auto watering routine
// autoWaterFlag is true from setup - avoids watering if reboot after on time
// reset on daily basis
if(tim < autoStartWatering){
autoWaterFlag = false;
}
// check if time to water and if already done
if(tim >= autoStartWatering && autoWaterFlag == false){
autoWaterOnFlag = true;
mcp.digitalWrite(2, HIGH);                // turn on water (hose)
mcp.digitalWrite(1, HIGH);                // turn on water (mist)
}
//check if time's up
// previously tried not to turn off if manual watering was happening!
if(tim > (autoStartWatering + autoWaterTime) && autoWaterOnFlag == true && autoWaterFlag == false){
mcp.digitalWrite(2, LOW);                 // turn off water (hose)
mcp.digitalWrite(1, LOW);                 // turn off water (mist)
autoWaterFlag = true;                     // watering has been done!
autoWaterOnFlag = false;                  // so water is off
}
if(autoWaterOnFlag){
Serial.println("Auto water on!");
}
//-------------------------------------------------------------
//reset rain gauge running total if GPIO 24 is high on RPi
int valFromPi2 = mcp.digitalRead(9);
if(valFromPi2 == 1 && resetRGflag == false){
SD.remove("TIPS_TOT.txt");
myFile = SD.open("TIPS_TOT.txt", FILE_WRITE);
if(myFile){
myFile.print("0");
myFile.close();
resetRGflag = true;
runtotal = 0;
}
else{
Serial.println("SD - failed to reset tips total");
}
}
else if(valFromPi2 == 0 && resetRGflag == true){
resetRGflag = false;
}
recvOneChar();
showNewData();
if(menuItem == 1){
dumpRain();
menuItem = 0;
}
//---------------------------------------------------------------
//flash led and send watchdog signal to RPi and hardware watchdog
digitalWrite(16, HIGH);   // turn the LED on (HIGH is the voltage level)
delay(200);                       // wait for 1/5 second
digitalWrite(16, LOW);    // turn the LED off by making the voltage LOW
delay(200);                       // wait for 1/5 second
}
//--------------END----------------------------------------------