   timer.run();
   ArduinoOTA.handle();
 
-  if (ranOnce == false && year() != 1970) {
-    Blynk.setProperty(V107, "label", String(hour()) + ":" + minute() + " " + month() + "/" + day() + " HVAC");
-    ranOnce = true;
-  }
-}
-
-void disconnectWatcher() {
-  if (Blynk.connected() == false && blynkFlag == false) {
-    ++blynkDisconnectCount;
-    blynkFlag = true;
-  }
-  else if (Blynk.connected() == true && blynkFlag == true) {
-    blynkFlag = false;
-  }
-}
-
-void disconnectReporter() {
-  Blynk.virtualWrite(V107, blynkDisconnectCount);
-}
-
-
-// ******************************************************** HVAC CTRL START **********************************************
+  if (startupSynced == false && year() != 1970) {
+    startupSynced = true;
+    startupSync();
+    todaysDate = day();
+    timer.setTimeout(5000, startTimers);
+  }
+}
+
+// BEGIN CONTROL SECTION
+
+//int tempATreportMin, tempKKreportMin, tempLKreportMin, tempMKreportMin;
+double tempATlast, tempKKlast, tempLKlast, tempMKlast;
+int tempATmatch, tempKKmatch, tempLKmatch, tempMKmatch;
+
+BLYNK_WRITE(V127) {   // Collect temperature data from sensors (sensors push data) and notify if sensor is not reporting
+  int nodeData = param[0].asInt();
+  double tempData = param[1].asDouble();
+
+  if (nodeData == 1) {
+    tempAT = tempData;
+    //tempATreportMin = minute();
+
+    if (tempAT == tempATlast) {     // If temps match...
+      ++tempATmatch;                // count how many times they matched.
+    }
+    else {                      // if they didn't
+      tempATmatch = 0;          // reset the match counter
+      tempATlast = tempAT;      // and "reset" tempATlast.
+    }
+    //Serial.println(String("[") + currentTime + "] AT:" + tempAT + "F (" + tempATmatch + "/" + tempATreportMin + "m)");
+    Serial.println(String("[") + currentTime + "] AT:" + tempAT + "F (" + tempATmatch + ")");
+  }
+  else if (nodeData == 2) {
+    tempKK = tempData;
+    //tempKKreportMin = minute();
+
+    if (tempKK == tempKKlast) {
+      ++tempKKmatch;
+    }
+    else {
+      tempKKmatch = 0;
+      tempKKlast = tempKK;
+    }
+    //Serial.println(String("[") + currentTime + "] KK:" + tempKK + "F (" + tempKKmatch + "/" + tempKKreportMin + "m)");
+    Serial.println(String("[") + currentTime + "] KK:" + tempKK + "F (" + tempKKmatch + ")");
+  }
+  else if (nodeData == 3) {
+    tempLK = tempData;
+    //tempLKreportMin = minute();
+
+    if (tempLK == tempLKlast) {
+      ++tempLKmatch;
+    }
+    else {
+      tempLKmatch = 0;
+      tempLKlast = tempLK;
+    }
+    //Serial.println(String("[") + currentTime + "] LK:" + tempLK + "F (" + tempLKmatch + "/" + tempLKreportMin + "m)");
+    Serial.println(String("[") + currentTime + "] LK:" + tempLK + "F (" + tempLKmatch + ")");
+  }
+  else if (nodeData == 4) {
+    tempMK = tempData;
+    //tempMKreportMin = minute();
+
+    if (tempMK == tempMKlast) {
+      ++tempMKmatch;
+    }
+    else {
+      tempMKmatch = 0;
+      tempMKlast = tempMK;
+    }
+    //Serial.println(String("[") + currentTime + "] MK:" + tempMK + "F (" + tempMKmatch + "/" + tempMKreportMin + "m)");
+    Serial.println(String("[") + currentTime + "] MK:" + tempMK + "F (" + tempMKmatch + ")");
+  }
+}
+
+BLYNK_WRITE(V39)  // Input from slider in app to set setpoint
+{
+  setpointTemp = param.asInt();
+  /*DEBUG*/Serial.println(String("[") + millis() + "] setpointTemp = " + setpointTemp);
+}
+
+void tempWatcher() {
+  double tempArray[5] = {0, tempAT, tempKK, tempLK, tempMK};  // This starts with 0, since AT is Node01, KK is Node02, etc.
+  // Evaluates which room is the hottest
+  for (i = 0; i < 5; i++) {         // If 0 skip, then look at tempAT and other rooms.
+    if (tempArray[i] > maxT) {      // Compare each room to maxT and if higher,
+      maxT = tempArray[i];          // store that room's temp as maxT.
+      if (coolingMode == false) {   // Now if cooling is off,
+        highRoom = i;               // set the index number (i) as the high room. This will setup this particular room as the target to cool (control watches this room to determine when to turn off).
+      }
+    }
+    if (i == 0) {                   // When we're back to the top of the order, reset maxT, otherwise it would just always hold the highest temperature since the WeMos was reset!
+      maxT = 0;
+    }
+  }
+
+  String highRoomName;          // Gives a "proper" room name in lieu of node for use in app labels, etc.
+
+  if (highRoom == 1) {
+    highRoomName = "House";
+  }
+  else if (highRoom == 2) {
+    highRoomName = "KK";
+  }
+  else if (highRoom == 3) {
+    highRoomName = "LK";
+  }
+  else if (highRoom == 4) {
+    highRoomName = "MK";
+  }
+
+  if (controllingRoom == 1 && coolingMode == false && setpointTemp != 0 && ( tempArray[highRoom] >= setpointTemp + triggerTemp) ) {
+    /*DEBUG*/Serial.println(String("[") + currentTime + "] Cooling started by " + highRoomName + " when it reached " + tempArray[highRoom] + "F. Shuts off at " + (setpointTemp - tempDiff) + "F.");
+    Blynk.setProperty(V16, "label", String("Status: Started by ") + highRoomName + " when it reached " + tempArray[highRoom] + "F. Shuts off at " + (setpointTemp - tempDiff) + "F.") ;
+    digitalWrite(coolingRelay, LOW);
+    coolingMode = true;
+  }
+  else if (controllingRoom == 1 && coolingMode == true && setpointTemp != 0 && ( tempArray[highRoom] <= setpointTemp - tempDiff) ) {
+    /*DEBUG*/Serial.println(String("[") + currentTime + "] Cooling stopped by " + highRoomName + " b/c " + tempArray[highRoom] + " <= " + (setpointTemp - tempDiff));
+    Blynk.setProperty(V16, "label", "Status:");
+    digitalWrite(coolingRelay, HIGH);
+    coolingMode = false;
+  }
+
+  // SAFETY: Turn off HVAC is it runs over 1 hour
+  if (currentRunSec > 3600 && coolingMode == true) {
+    shutdownHVACoverrun();
+  }
+}
+
+void shutdownHVACoverrun() {
+  Blynk.setProperty(V16, "label", String("Status: ") + currentTime + " HVAC SHUTDOWN: Ran longer than 45 minutes.") ;
+  coolingMode = false;
+  Blynk.virtualWrite(V40, "pick", 2);
+  Blynk.virtualWrite(V40, 2);
+  Blynk.syncVirtual(V40);
+  Blynk.notify(String(currentTime) + " HVAC SHUTDOWN: Ran longer than 60 minutes. Now on fan-only.");
+}
+
+// Set which room is controlling HVAC via the app's menu
 BLYNK_WRITE(V40) {
   switch (param.asInt())
   {
-    // **************************************** IN ALL CASES BELOW: LOW == ON/CLOSED, HIGH == OFF/OPEN
-    case 1: {                         // House t-stat controlling (bypass off)
-        controlMode = 0;
-        controllingSpace = "";
-        Blynk.setProperty(V39, "label", "Setpoint");
+    case 1: {                             // House t-stat in control
+        /*DEBUG*/Serial.println(String("[") + currentTime + "] House tstat in control model selected");
+        controllingRoom = 0;              // This number is node number
+        /*DEBUG*/Serial.println(String("[") + currentTime + "] controllingRoom = 0");
+        timer.disable(tempWatcherTimer);
+        /*DEBUG*/Serial.println(String("[") + currentTime + "] tempWatcherTimer disabled");
         digitalWrite(fanRelay, HIGH);
         digitalWrite(coolingRelay, HIGH);
         digitalWrite(heatingRelay, HIGH);
         digitalWrite(bypassRelay, HIGH);
         break;
       }
-    case 2: {                         // House t-stat bypass (Blynk-controlling, but nothing running)
-        controlMode = 0;
-        controllingSpace = "";
-        Blynk.setProperty(V39, "label", "Setpoint");
+    case 2: {                             // Manual fan-only
+        /*DEBUG*/Serial.println(String("[") + currentTime + "] Manual fan only mode selected");
+        controllingRoom = 0;
+        /*DEBUG*/Serial.println(String("[") + currentTime + "] controllingRoom = 0");
+        timer.disable(tempWatcherTimer);
+        /*DEBUG*/Serial.println(String("[") + currentTime + "] tempWatcherTimer disabled");
+        digitalWrite(fanRelay, LOW);
+        digitalWrite(coolingRelay, HIGH);
+        digitalWrite(heatingRelay, HIGH);
+        digitalWrite(bypassRelay, LOW);
+        break;
+      }
+    case 3: {                             // Whole-house cooling
+        /*DEBUG*/Serial.println(String("[") + currentTime + "] Whole house cooling mode selected");
+        controllingRoom = 1;
+        /*DEBUG*/Serial.println(String("[") + currentTime + "] controllingRoom = 1 (whole-house)");
+        timer.enable(tempWatcherTimer);
+        /*DEBUG*/Serial.println(String("[") + currentTime + "] tempWatcherTimer enabled");
         digitalWrite(fanRelay, HIGH);
         digitalWrite(coolingRelay, HIGH);
         digitalWrite(heatingRelay, HIGH);
         digitalWrite(bypassRelay, LOW);
         break;
       }
-    case 3: {                         // Manual fan (blower only)
-        controlMode = 0;
-        controllingSpace = "";
-        Blynk.setProperty(V39, "label", "Setpoint");
-        digitalWrite(fanRelay, LOW);
-        digitalWrite(coolingRelay, HIGH);
-        digitalWrite(heatingRelay, HIGH);
-        digitalWrite(bypassRelay, LOW);
-        break;
-      }
-    case 4: {                         // House t-stat-controlled cooling
-        controlMode = 1;
-        controllingSpace = "AT";
-        coolingMode = 1;
-        controllingTemp = tempAT;
-        Blynk.setProperty(V39, "label", String("Setpoint: ") + setpointTemp + ". Controlled by AT (currently " + tempAT + "F).");
+    case 4: {                             // KK cooling
+        controllingRoom = 2;
+        timer.enable(tempWatcherTimer);
         digitalWrite(fanRelay, HIGH);
         digitalWrite(coolingRelay, HIGH);
         digitalWrite(heatingRelay, HIGH);
         digitalWrite(bypassRelay, LOW);
         break;
       }
-    case 5: {                         // House t-stat controlled heating
-        /*
-          controlMode = 1;
-          controllingSpace = "AT";
-          coolingMode = 0;
-          controllingTemp = tempAT;
-          Blynk.setProperty(V39, "label", String("Setpoint: ") + setpointTemp + ". Controlled by AT (currently " + tempAT + "F).");
-          digitalWrite(fanRelay, HIGH);
-          digitalWrite(coolingRelay, HIGH);
-          digitalWrite(heatingRelay, HIGH);
-          digitalWrite(bypassRelay, LOW);
-          break;
-        */
-      }
-    case 6: {                                         // Keaton-controlled cooling
-        controlMode = 1;
-        controllingSpace = "KK";                      // Here's the room that we're watching
-        coolingMode = 1;                              // This sets the HVAC in correct mode and decides tstat logic
-        controllingTemp = tempKK;
-        Blynk.setProperty(V39, "label", String("Setpoint: ") + setpointTemp + ". Controlled by KK (currently " + tempKK + "F).");
+    case 5: {                             // LK cooling
+        controllingRoom = 3;
+        timer.enable(tempWatcherTimer);
         digitalWrite(fanRelay, HIGH);
         digitalWrite(coolingRelay, HIGH);
         digitalWrite(heatingRelay, HIGH);
         digitalWrite(bypassRelay, LOW);
         break;
       }
-    case 7: {                             // Keaton-controlled heating
-        /*
-          Blynk.syncVirtual(V39);
-          controlMode = 1;
-          controllingSpace = "KK";
-          coolingMode = 0;
-          controllingTemp = tempKK;
-          Blynk.setProperty(V39, "label", String("Setpoint: ") + setpointTemp + "F. Controlled by KK (currently " + tempKK + "F).");
-          digitalWrite(fanRelay, HIGH);
-          digitalWrite(coolingRelay, HIGH);
-          digitalWrite(heatingRelay, HIGH);
-          digitalWrite(bypassRelay, LOW);
-          break;
-        */
-      }
-    case 8: {                             // Liv-controlled cooling
-        controlMode = 1;
-        controllingSpace = "LK";
-        coolingMode = 1;
-        controllingTemp = tempLK;
-        Blynk.setProperty(V39, "label", String("Setpoint: ") + setpointTemp + ". Controlled by LK (currently " + tempLK + "F).");
-        digitalWrite(fanRelay, HIGH);
-        digitalWrite(coolingRelay, HIGH);
-        digitalWrite(heatingRelay, HIGH);
-        digitalWrite(bypassRelay, LOW);
-        break;
-      }
-    case 9: {                             // Liv-controlled heating
-        /*
-          controlMode = 1;
-          controllingSpace = "LK";
-          coolingMode = 0;
-          controllingTemp = tempLK;
-          Blynk.setProperty(V39, "label", String("Setpoint: ") + setpointTemp + ". Controlled by LK (currently " + tempLK + "F).");
-          digitalWrite(fanRelay, HIGH);
-          digitalWrite(coolingRelay, HIGH);
-          digitalWrite(heatingRelay, HIGH);
-          digitalWrite(bypassRelay, LOW);
-          break;
-        */
-      }
-    default: {
-        break;
-      }
-  }
-}
-
-void tempControl() {
-
-  if (controlMode == 1 && setpointTemp != 0 && controllingTemp != 0)     // If we're in bypass mode, and established setpoint and control temps, then proceed:
+  }
+}
+
+// END CONTROL SECTION
+
+// BEGIN STARTUP SEQUENCE
+void startupSync() {
+  Blynk.virtualWrite(V40, "pick", 1);
+  Blynk.syncVirtual(V11, V39, V40, V110, V111, V112);
+}
+
+BLYNK_WRITE(V11)
+{
+  int filterUseMin = param.asInt();   // Doing this because there is no param.asLong
+  filterUseSec = filterUseMin * 60;
+}
+BLYNK_WRITE(V110)
+{
+  todaysRuns = param.asInt();
+}
+BLYNK_WRITE(V111)
+{
+  todaysRuntimeMin = param.asInt();   // Doing this because there is no param.asLong
+  todaysRuntimeSec = todaysRuntimeMin * 60L;
+}
+BLYNK_WRITE(V112)
+{
+  yesterdaysRuntimeMin = param.asInt();
+  yesterdaysRuntimeSec = yesterdaysRuntimeMin * 60L;
+
+  // Updates the "Yesterday's runtime" display (at startup)
+  if (yesterdaysRuntimeSec == 0) {
+    Blynk.virtualWrite(V14, "None");
+  }
+  else {
+    yesterdaysRuntimeMin = yesterdaysRuntimeSec / 60;
+    Blynk.virtualWrite(V14, String(yesterdaysRuntimeMin) + " minutes");
+  }
+}
+
+void startTimers() {
+  timer.enable(fanStatusTimer);
+  timer.enable(setClockTimeTimer);
+  timer.enable(tempDisplayTimer);
+  timer.enable(statusDisplayTimer);
+}
+// END STARTUP SEQUENCE
+
+void fanStatus() {
+  if (digitalRead(fanPin) == LOW) {  // If fan-only mode is running
+    fanOnly = true;                  // set that mode and
+    ++filterUseSec;                  // count seconds of filter use.
+  }
+  else {
+    fanOnly = false;
+  }
+
+  if (digitalRead(blowerPin) == LOW) {  // If heating or cooling is running
+    fanCooling = true;                  // set those modes,
+    fanHeating = true;
+    ++todaysRuntimeSec;                 // accumulated seconds it's ran today,
+    ++filterUseSec;                     // count seconds of filter use,
+    ++currentRunSec;                    // count seconds of current run (SAFETY),
+  }
+  else {                                // otherwise "turn off" those modes,
+    fanCooling = false;
+    fanHeating = false;
+    currentRunSec = 0;                  // and reset current run counter.
+  }
+
+  // Sets the start/end time and date when the blower starts or stops
+  if (digitalRead(blowerPin) == LOW && ( changeFlag == true || ranOnce == false) ) {  // If heating or cooling is running, and was previously off (or the WeMos just started up)
+    changeTimeDate = currentTimeDate;                                                // set start time,
+    changeFlag = false;                                                               // use a flag to keep this process from happening again ("locking" the time/date for display), and
+    ranOnce = true;                                                                   // indicate that ranOnce... ran... once!
+  }
+  else if (digitalRead(blowerPin) == HIGH && changeFlag == false) {                   // If heating/cooling is off, and was previously off (or the WeMos just started up (no need for ranOnce as changeFlag is false at startup))
+    changeTimeDate = currentTimeDate;                                                // set end time and
+    changeFlag = true;                                                                // lock out this process with flag. Now,
+
+    if (ranOnce == true) {                                                            // since I'm counting a run during an end event, I don't want a WeMos startup to register as a run, so I'm locking that out here, but
+      ++todaysRuns;
+    }
+    else {
+      ranOnce = true;                                                                 // unlocking here.
+    }
+  }
+
+  if (todaysDate != day()) {                    // At the end of the day
+    yesterdaysRuntimeSec = todaysRuntimeSec;    // move today's runcount to yesterday,
+    todaysRuntimeSec = 0;                       // reset today's runtime, and
+    todaysRuns = 0;                             // reset today's number of runs.
+    coolingCounter = 0;
+    heatingCounter = 0;
+    todaysDate = day();
+  }
+}
+
+void tempDisplay() {
+  sensors.requestTemperatures();            // Polls the temperature sensors
+  tempRA = sensors.getTempF(ds18b20RA);
+  tempSA = sensors.getTempF(ds18b20SA);
+
+  if (fanCooling == true || fanHeating == true) {
+    Blynk.virtualWrite(V0, tempRA);          // Display temperatures in app
+    Blynk.virtualWrite(V1, tempSA);
+  }
+  else if (fanOnly == true) {
+    Blynk.virtualWrite(V0, "FAN");
+    Blynk.virtualWrite(V1, "FAN");
+  }
+  else {
+    Blynk.virtualWrite(V0, "OFF");
+    Blynk.virtualWrite(V1, "OFF");
+  }
+}
+
+void statusDisplay() {
+  // Updates the primary "Status" display
+  if (fanCooling == true || fanHeating == true) {
+    currentStatus = String("HVAC ON since ") + changeTimeDate;  // I'm using this string because I need to sync this back after startup. I need to sync it because status is not static, it's refreshed at interval.
+    Blynk.virtualWrite(V16, currentStatus);
+  }
+  else if (fanCooling == false || fanHeating == false) {
+    currentStatus = String("HVAC OFF since ") + changeTimeDate;
+    Blynk.virtualWrite(V16, currentStatus);
+  }
+
+  // Updates the "Today's runtime" display
+  if (todaysRuntimeSec == 0) {
+    Blynk.virtualWrite(V15, "None");                     // Yesterday runtime label
+  }
+  else {
+    todaysRuntimeMin = todaysRuntimeSec / 60;
+    Blynk.virtualWrite(V15, String(todaysRuntimeMin) + " mins (" + todaysRuns + " runs)");
+  }
+}
+
+void longStatusDisplay() {
+  int filterUseHr = filterUseSec / 3600;
+  Blynk.virtualWrite(V10, String(300 - filterUseHr) + " hours");
+
+  // Updates the "Yesterday's runtime" display
+  if (yesterdaysRuntimeSec == 0) {
+    Blynk.virtualWrite(V14, "None");                     // Yesterday runtime label
+  }
+  else {
+    yesterdaysRuntimeMin = yesterdaysRuntimeSec / 60;
+    Blynk.virtualWrite(V14, String(yesterdaysRuntimeMin) + " minutes");
+  }
+}
+
+void setClockTime() {
+  if (year() != 1970) // Doesn't start until RTC is set correctly.
   {
 
-    if (coolingMode == 0 && controllingTemp < setpointTemp)       // If mode is heat and room falls equal or below the target temp...
-    {
-      digitalWrite(heatingRelay, LOW);                            // turn on the heat. But when
-      controlRuntime = controlRuntime + 30;
-    }
-    else if (coolingMode == 0 && controllingTemp >= (setpointTemp + controlSplit))   // If mode is heat and room is warmer then target temp...
-    {
-      digitalWrite(heatingRelay, HIGH);                           // everything is turned off.
-      controlRuntime = 0;
-      controlLatch = FALSE;
-    }
-    else if (coolingMode == 1 && controllingTemp > setpointTemp)  // If mode is cool and room rises equal or above the target temp...
-    {
-      digitalWrite(coolingRelay, LOW);                            // turn on cooling. Then,
-      controlRuntime = controlRuntime + 30;
-    }
-    else if (coolingMode == 1 && controllingTemp <= (setpointTemp - controlSplit))   // when mode is cool and room is cooler then target temp...
-    {
-      digitalWrite(coolingRelay, HIGH);                           // turn everything off.
-      controlRuntime = 0;
-      controlLatch = FALSE;
-    }
-
-  }
-}
-
-void tempControlSafety() {      // Safeties running every second
-  if (controlMode == 1 && digitalRead(blowerPin) == LOW && (setpointTemp <= 0 || controllingTemp  <= 0) )  // If HVAC is running and the setpoint or controlling temp are 0, then open relays. Control will be locked out until those temps are != 0.
-  {
-    safetyShutoff();
-
-    Blynk.notify(hour() + String(":") + minute() + ":" + second() + " " + month() + "/" + day() + " ERROR: Ctrl attempted with no setpointTemp or controllingTemp");
-  }
-
-  if (controlMode == 1 && digitalRead(blowerPin) == LOW && controlLatch == FALSE) {  // If Blynk is controlling, and fan starts:
-    controlStart = millis();            // Records start time of unit.
-    controlLatch = TRUE;
-  }
-
-  if (controlMode == 1 && digitalRead(blowerPin) == LOW && coolingMode == 0 && millis() >= controlStart + heatControlTimeout) {
-    safetyShutoff();
-
-    Blynk.notify(hour() + String(":") + minute() + ":" + second() + " " + month() + "/" + day() + " ERROR: Heater ran for more than " + (heatControlTimeout / 60000) + " minutes.");
-  }
-  else if (controlMode == 1 && digitalRead(blowerPin) == LOW && coolingMode == 1 && millis() >= controlStart + coolControlTimeout) {
-    safetyShutoff();
-
-    Blynk.notify(hour() + String(":") + minute() + ":" + second() + " " + month() + "/" + day() + " ERROR: Cooler ran for more than " + (coolControlTimeout / 60000) + " minutes.");
-  }
-
-}
-
-void safetyShutoff() {
-  controlMode = 0;
-  controllingSpace = "";
-  controlLatch = FALSE;
-  Blynk.setProperty(V39, "label", "Setpoint");
-  digitalWrite(fanRelay, HIGH);
-  digitalWrite(coolingRelay, HIGH);
-  digitalWrite(heatingRelay, HIGH);
-  digitalWrite(bypassRelay, HIGH);
-}
-
-void tempUpdater() {
-  Blynk.syncVirtual(V3);  // AT
-  Blynk.syncVirtual(V4);  // KK
-  Blynk.syncVirtual(V6);  // LK
-
-  if (controllingSpace == "KK")
-  {
-    controllingTemp = tempKK;
-    Blynk.setProperty(V39, "label", String("Setpoint: ") + setpointTemp + "F. Controlled by KK (currently " + tempKK + "F).");
-  }
-  else if (controllingSpace == "LK")
-  {
-    controllingTemp = tempLK;
-    Blynk.setProperty(V39, "label", String("Setpoint: ") + setpointTemp + "F. Controlled by LK (currently " + tempLK + "F).");
-  }
-  else if (controllingSpace == "AT")
-  {
-    controllingTemp = tempAT;
-    Blynk.setProperty(V39, "label", String("Setpoint: ") + setpointTemp + "F. Controlled by AT (currently " + tempAT + "F).");
-  }
-}
-
-BLYNK_WRITE(V3) {
-  tempAT = param.asInt();
-}
-
-BLYNK_WRITE(V4) {
-  tempKK = param.asInt();
-}
-
-BLYNK_WRITE(V6) {
-  tempLK = param.asInt();
-}
-
-BLYNK_WRITE(V39)  // Input from slider in app to set setpoint
-{
-  setpointTemp = param.asInt();
-}
-
-void controlReset() {
-  controlMode = 0;
-  controllingSpace = "";
-  Blynk.setProperty(V39, "label", "Setpoint");
-  digitalWrite(fanRelay, HIGH);
-  digitalWrite(coolingRelay, HIGH);
-  digitalWrite(heatingRelay, HIGH);
-  digitalWrite(bypassRelay, HIGH);
-  Blynk.virtualWrite(V40, 1);
-
-}
-// ******************************************************** HVAC CTRL END **********************************************
-
-void vsync1()
-{
-  Blynk.syncVirtual(V16);    // currentStatus
-  Blynk.syncVirtual(V39);    // setpointTemp
-}
-
-void vsync2()
-{
-  Blynk.syncVirtual(V110);    // hvacTodaysStartCount
-  Blynk.syncVirtual(V111);    // todaysAccumRuntimeSec
-}
-
-void vsync3()
-{
-  Blynk.syncVirtual(V112);    // yesterdayRuntime
-  Blynk.syncVirtual(V11);     // currentFilterSec
-}
-
-BLYNK_WRITE(V110)
-{
-  hvacTodaysStartCount = param.asInt();
-}
-
-BLYNK_WRITE(V111)
-{
-  todaysAccumRuntimeSec = param.asLong();
-}
-
-BLYNK_WRITE(V112)
-{
-  yesterdayRuntime = param.asInt();
-}
-
-BLYNK_WRITE(V11)
-{
-  currentFilterSec = param.asLong();
-}
-
-BLYNK_WRITE(V16)
-{
-  currentStatus = param.asStr();
-}
-
-void phantSend() {
-  if (digitalRead(blowerPin) == LOW)        // If HVAC blower motor is running...
-  {
-    runStatus = 40;
-  }
-  else if (digitalRead(blowerPin) == HIGH)  // If HVAC blower motor if off...
-  {
-    runStatus = NULL;
-  }
-
-  // The following changes the color of Time Until Filter Change in app... placed here because the timing.
-  if (filterChangeHours - (currentFilterSec / 3600) > 24 )
-  {
-    Blynk.setProperty(V10, "color", "#23C48E");   // Green
-  }
-  else if (filterChangeHours - (currentFilterSec / 3600) < 24 &&  filterChangeHours - (currentFilterSec / 3600) > 8)
-  {
-    Blynk.setProperty(V10, "color", "#ED9D00");   // Yellow
-  }
-  else if (filterChangeHours - (currentFilterSec / 3600) < 8)
-  {
-    Blynk.setProperty(V10, "color", "#D3435C");   // Red
-  }
-
-  //Blynk.virtualWrite(67, String("returntemp=") + tempRA + "&runstatus=" + runStatus + "&supplytemp=" + tempSA);
-
-  Serial.print("connecting to ");
-  Serial.println(hostSF);
-
-  // Use WiFiClient class to create TCP connections
-  WiFiClient client;
-  const int httpPort = 8080;
-  if (!client.connect(hostSF, httpPort)) {
-    Serial.println("connection failed");
-    return;
-  }
-
-  Serial.print("Requesting...");
-
-  // This will send the request to the server
-  client.print(String("GET ") + "/input/" + streamId + "?private_key=" + privateKey + "&returntemp=" + tempRA + "&runstatus=" + runStatus + "&supplytemp=" + tempSA + " HTTP/1.1\r\n" +
-               "Host: " + hostSF + "\r\n" +
-               "Connection: close\r\n\r\n");
-  unsigned long timeout = millis();
-  while (client.available() == 0) {
-    if (millis() - timeout > 15000) {
-      Serial.println(">>> Client Timeout !");
-      client.stop();
-      return;
-    }
-  }
-
-  // Read all the lines of the reply from server and print them to Serial
-  while (client.available()) {
-    String line = client.readStringUntil('\r');
-    Serial.print(line);
-  }
-
-  Serial.println();
-  Serial.println("closing connection");
-}
-
-BLYNK_WRITE(V19)
-{
-  int pinData = param.asInt();
-
-  if (pinData == 0)
-  {
-    Blynk.setProperty(V16, "label", "Current Status:                    Hi/Lo temps above are last 24h");
-  }
-
-  if (pinData == 1)
-  {
-    Blynk.setProperty(V16, "label", "Current Status:                    Hi/Lo temps above are since midnight");
-  }
-}
-
-BLYNK_WRITE(V18)              // App button to display all terminal commands available
+    // Below gives me leading zeros on minutes and AM/PM.
+    if (minute() > 9 && hour() > 11) {
+      currentTimeDate = String(hourFormat12()) + ":" + minute() + "PM on " + month() + "/" + day();
+    }
+    else if (minute() < 10 && hour() > 11) {
+      currentTimeDate = String(hourFormat12()) + ":0" + minute() + "PM on " + month() + "/" + day();
+    }
+    else if (minute() > 9 && hour() < 12) {
+      currentTimeDate = String(hourFormat12()) + ":" + minute() + "AM on " + month() + "/" + day();
+    }
+    else if (minute() < 10 && hour() < 12) {
+      currentTimeDate = String(hourFormat12()) + ":0" + minute() + "AM on " + month() + "/" + day();
+    }
+
+    if (minute() > 9 && hour() > 11) {
+      currentTime = String(hourFormat12()) + ":" + minute() + "PM";
+    }
+    else if (minute() < 10 && hour() > 11) {
+      currentTime = String(hourFormat12()) + ":0" + minute() + "PM";
+    }
+    else if (minute() > 9 && hour() < 12) {
+      currentTime = String(hourFormat12()) + ":" + minute() + "AM";
+    }
+    else if (minute() < 10 && hour() < 12) {
+      currentTime = String(hourFormat12()) + ":0" + minute() + "AM";
+    }
+  }
+}
+
+void updateVpinData1() {
+  Blynk.virtualWrite(V110, todaysRuns);            // Stores how many times the unit has started today
+  Blynk.virtualWrite(V111, todaysRuntimeMin);      // Stores many minutes the HVAC ran today
+  timer.setTimeout(5002, updateVpinData2);
+}
+
+void updateVpinData2() {
+  Blynk.virtualWrite(V112, yesterdaysRuntimeMin);  // Yesterday's runtime in minutes
+  Blynk.virtualWrite(V11, filterUseSec / 60);      // How many minutes the installed air filter has been used
+
+
+  // Counts minutes of cooling or heating to support the language used in the nightly tweet
+  if (fanCooling == true || fanHeating == true) {
+    if (tempRA > tempSA) {
+      ++coolingCounter;
+    }
+    else {
+      ++heatingCounter;
+    }
+  }
+
+  if (coolingCounter == 0 && heatingCounter == 0) {
+    Blynk.virtualWrite(V38, "NONE");
+  }
+  else if (coolingCounter > heatingCounter) {
+    Blynk.virtualWrite(V38, "COOL");
+  }
+  else if (coolingCounter < heatingCounter) {
+    Blynk.virtualWrite(V38, "HEAT");
+  }
+}
+
+BLYNK_WRITE(V18) // Filter change mode
 {
   int pinData = param.asInt();
 
   if (pinData == 0) // Triggers when button is released only
   {
-    terminal.println(" "); terminal.println(" "); terminal.println(" "); terminal.println(" ");
-    terminal.println(" "); terminal.println(" "); terminal.println(" "); terminal.println(" ");
-    terminal.println("  --------- AVAILABLE COMMANDS ----------");
-    terminal.println(" ");
-    terminal.println(" filter : HVAC filter menu");
-    terminal.println(" WU : Change WU API station");
-    terminal.println(" ");
+    filterChangeMode = true;
+
+    terminal.println(""); terminal.println("");
+    terminal.println("       ~~ A/C Filter Status Mode ~~"); terminal.println(" ");
+    terminal.println("Was the filter just changed (y/n)?"); terminal.println(" ");
     terminal.flush();
   }
 }
 
-BLYNK_WRITE(V27) // App button to report uptime
+BLYNK_WRITE(V26)  // Resets filter timer
+{
+  if ( (String("y") == param.asStr() || String("Y") == param.asStr()) && filterChangeMode == true ) {
+    filterUseSec = 0;
+    terminal.println(" "); terminal.println("Filter timer reset!"); terminal.println(" ");
+    filterChangeMode = false;
+  }
+  else if ( (String("n") == param.asStr() || String("N") == param.asStr()) && filterChangeMode == true ) {
+    terminal.println(" "); terminal.println("Filter change cancelled."); terminal.println(" ");
+    filterChangeMode = false;
+  }
+  terminal.flush();
+}
+
+BLYNK_WRITE(V27)  // App button to report uptime
 {
   int pinData = param.asInt();
 

  // END OTA ROUTINE

  rtc.begin();
  //setSyncInterval(120);

  timer.setInterval(2500L, sendTemps);        // Temperature sensor polling and app display refresh interval.
  timer.setInterval(1234L, sendStatus);       // Unit run status polling interval (for app Current Status display only).
  timer.setInterval(1000L, countRuntime);     // Counts blower runtime for daily accumulation displays.
  timer.setInterval(1000L, totalRuntime);     // Counts blower runtime.
  timer.setInterval(1000L, modeTracking);     // Tracks if unit has been mostly cooling or heating during the day.
  timer.setInterval(1000L, setClockTime);     // Creates a current time with leading zeros.
  //timer.setInterval(500L, alarmTimer);      // Alarms for low SA/RA split in heating or cooling.
  timer.setInterval(15432L, phantSend);       // ~15 sec run status updates to Phant runnting on a local Raspberry Pi.
  timer.setTimeout(100, vsync1);              // Syncs back vPins to survive hardware reset.
  timer.setTimeout(2000, vsync2);             // Syncs back vPins to survive hardware reset.
  timer.setTimeout(4000, vsync3);             // Syncs back vPins to survive hardware reset.
  timer.setTimeout(8000, controlReset);
  timer.setInterval(30000L, tempUpdater);      // When bypass on, this evaluates controlling space against setpoint.
  timer.setInterval(30000L, tempControl);     // Temps (controlling space) that can control HVAC are synced.
  timer.setInterval(1000L, tempControlSafety);
  timer.setInterval(250L, disconnectWatcher);     // Watches for a disconnect event
  timer.setInterval(5000L, disconnectReporter);   // Reports disconnects to the app every 5 seconds

}

void loop()  // To keep Blynk happy, keep most tasks out of the loop.

#include <ESP8266mDNS.h>        // Required for OTA
#include <WiFiUdp.h>            // Required for OTA
#include <ArduinoOTA.h>         // Required for OTA
#include <BlynkSimpleEsp8266.h>
#include <TimeLib.h>            // Used by WidgetRTC.h
#include <WidgetRTC.h>          // Blynk's RTC
#include <OneWire.h>            // Temperature sensor library
#include <DallasTemperature.h>  // Temperature sensor library
#define ONE_WIRE_BUS 12         // WeMos pin D6. RA/SA temp sensor array.
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

