HUGE update! V2!!!

HUGE update! V2!!!

Rewritten from the ground up. Logic has been greatly simplified. HVAC control now centers around "whole-house control:" all temp sensors contributing to controlling heating and cooling. Just cooling for now... will add heat as the seasons require. Did away with Phant for now. Sending data beat up bandwidth and really wasn't used.

--- hvacMonitor.ino
+++ hvacMonitor.ino
@@ -4,13 +4,12 @@
 #include <ESP8266mDNS.h>        // Required for OTA
 #include <WiFiUdp.h>            // Required for OTA
 #include <ArduinoOTA.h>         // Required for OTA
-#include <BlynkSimpleEsp8266.h> // 6/22/16 Rev: Includes edit of this file to expand virtual pins
+#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
+#define ONE_WIRE_BUS 12         // WeMos pin D6. RA/SA temp sensor array.
 OneWire oneWire(ONE_WIRE_BUS);
 DallasTemperature sensors(&oneWire);

@@ -19,102 +18,66 @@

 char auth[] = "fromBlynkApp";
 char ssid[] = "ssid";
-char pass[] = "pw";
-
-char myEmail[] = "email";
-
-char* hostSF = "192.168.0.168";
-char* streamId   = "publicKey";
-char* privateKey = "privateKey";
+char pass[] = "ssid";

 SimpleTimer timer;
-
 WidgetRTC rtc;
 WidgetTerminal terminal(V26);

-const int blowerPin = 13;  // WeMos pin D7.
-const int fanPin = 14;     // Future fan only pin.
-bool daySet = FALSE;       // Sets the day once after reset and RTC is set correctly.
-
-bool runOnce = TRUE;
-bool resetFlag = TRUE;        // TRUE when the hardware has been reset.
-bool startFlag = FALSE;       // TRUE when the A/C has started.
-bool stopFlag = FALSE;        // TRUE when the A/C has stopped.
-bool sensorFailFlag = FALSE;  // TRUE when RA or SA temp sensor are returning bad values.
-
-int offHour, offHour24,  // All used to send an HVAC run status string to Blynk's app.
-    onHour, onHour24, offMinute, onMinute, offMonth, onMonth, offDay, onDay;
-
-int tempSplit;                    // Difference between return and supply air.
-const int tempSplitSetpoint = 20; // Split lower than this value will send alarm notification.
-int alarmFor = 0;                 // Provides "latching" for high split alarm counting & functionality. 0 is normal. 1 is prealarm. 5 is alarmed and lockout.
-const int splitAlarmTime = 180;   // If a high split persists for this duration (in seconds), notification is sent.
-int alarmTime;                    // Seconds that high split alarm has been active.
-
-long todaysAccumRuntimeSec;           // Today's accumulated runtime in seconds - displayed in app as minutes.
-int currentCycleSec, currentCycleMin; // If HVAC is running, the duration of the current cycle (non-accumulating).
-int yesterdayRuntime;                 // Sum of yesterday's runtime in minutes - displays in app.
-int hvacTodaysStartCount;             // Records how many times the unit has started today.
-int todaysDate;                       // Sets today's date related to things that reset at EOD.
-String currentTimeDate;               // Time formatted as "0:00AM on 0/0"
-String startTimeDate, stopTimeDate, resetTimeDate;
-int runStatus;                        // Used for graphing. 40 == running. NULL == off.
-String currentStatus;                 // What displays in app. Used solely for sync and recover purposes.
-
-long currentFilterSec;                // Filter age based on unit runtime in seconds (stored in vPin). Seconds used for finer resolution.
-long currentFilterHours;              // Filter age based on unit runtime in hours.
-long timeFilterChanged;               // Unix time that filter was last changed.
-bool filterConfirmFlag = FALSE;       // Used to enter/exit the filter change mode.
-const int filterChangeHours = 300L;   // Duration in hours between filter changes. Up for debate and experimentation!
-bool changeFilterAlarm = FALSE;       // TRUE if filter needs to be changed, until reset.
-
-long todaysAccumRuntimeSecCooling;    // Seconds that system has been cooling.
-long todaysAccumRuntimeSecHeating;    // Seconds that system has been heating.
-bool heatingMode;                     // FALSE = Cooling. TRUE = Heating.
-bool hvacMode;                        // FALSE = Cooling. TRUE = Heating.
-
-double tempRA, tempSA;                // Return and supply air temperatures
-
-// HVAC control pins
-int coolingRelay = 5;   // WeMos pin D1.
-int heatingRelay = 4;   // WeMos pin D2.
-int fanRelay = 16;      // WeMos pin D0.
-int bypassRelay = 0;    // WeMos pin D3.
-
-int controlSplit = 2;   // Predefined value of how much I want to heat or cool above/below setpoint.
-int controllingTemp;
-int setpointTemp;
-String controllingSpace;
-bool coolingMode;       // 1 = cooling. 0 = heating.
-bool controlMode;
-int tempKK, tempLK, tempAT;
-
-long controlStart;
-long controlRuntime;
-long heatControlTimeout = 600000; // 600000 millis = 10 minutes
-long coolControlTimeout = 120000000; // 120000000 millis = 20 minutes
-bool controlLatch = FALSE;
-
-bool blynkFlag;
-int blynkDisconnectCount;
+const int fanRelay = 16;      // WeMos pin D0. Set LOW to start fan-only.
+const int coolingRelay = 5;   // WeMos pin D1. Set LOW to start cooling.
+const int heatingRelay = 4;   // WeMos pin D2. Set LOW to start heating.
+const int bypassRelay = 0;    // WeMos pin D3. Set LOW to bypass house t-stat.
+const int fanPin = 14;        // WeMos pin D5. LOW when fan-only is running.
+const int blowerPin = 13;     // WeMos pin D7. LOW when cool or heat is running.
+
+double tempRA, tempSA;        // Return air and supply air temperatures.
+
+bool fanOnly;         // true when fan-only mode is running
+bool fanCooling;      // true when cooling mode is running
+bool fanHeating;      // true when heating mode is running
+
+long todaysRuntimeSec;  // How many seconds the HVAC ran today
+int todaysRuntimeMin;   // How many minutes the HVAC ran today
+int currentRunSec;      // How many seconds the HVAC has ran in it's current cycle (resets after HVAC turns off)
+long filterUseSec;      // How many seconds the current filter has been used. Can be reset manually after filter change.
+int yesterdaysRuntimeSec, yesterdaysRuntimeMin;
+int todaysRuns;         // How many times the HVAC has ran today (counted when it shuts off).
+int coolingCounter, heatingCounter;   // Count approx how many minutes unit cooled/heated. Used for Tweeting.
+String currentStatus;   // For the sole purpose of syncing back the status label after a hardware reset
+int todaysDate;         // Today's date (15 or 16, etc)
+
+String currentTime;
+String currentTimeDate;
+String changeTimeDate;
+bool changeFlag;
+
 bool ranOnce;
+bool filterChangeMode;
+bool startupSynced;
+
+int fanStatusTimer, setClockTimeTimer, tempDisplayTimer, statusDisplayTimer, longStatusDisplayTimer, tempWatcherTimer;
+
+double tempAT, tempKK, tempLK, tempMK;  // Temperatures bridged in from other devices to use for control.
+int controllingRoom;
+int setpointTemp;     // The HVAC target temperature
+int triggerTemp = 1;  // The number of degrees above/below setpoint that the HVAC will start
+int tempDiff = 1;     // Temperature differential. You can add 1 to whatever is here because of the way the code compared a double and an int.
+bool coolingMode, heatingMode;  // True when cooling or heating is on
+int i, maxT, highRoom;    // Used to evaluate all room temperatures in whole-house cooling mode

 void setup()
 {
   Serial.begin(9600);
   WiFi.mode(WIFI_STA);

-  //WiFi.softAPdisconnect(true); // Per https://github.com/esp8266/Arduino/issues/676 this turns off AP
   Blynk.begin(auth, ssid, pass);

   sensors.begin();
   sensors.setResolution(ds18b20RA, 10);
   sensors.setResolution(ds18b20SA, 10);

-  while (Blynk.connect() == false) {
-    // Wait until connected
-  }
-
+  // Set pin modes used to control HVAC
   pinMode(coolingRelay, OUTPUT);
   pinMode(heatingRelay, OUTPUT);
   pinMode(fanRelay, OUTPUT);
@@ -126,8 +89,12 @@
   digitalWrite(fanRelay, HIGH);
   digitalWrite(bypassRelay, HIGH);

+  while (Blynk.connect() == false) {
+    // Wait until connected
+  }
+
   // START OTA ROUTINE
-  ArduinoOTA.setHostname("hvacMonitor");     // Name that is displayed in the Arduino IDE.
+  ArduinoOTA.setHostname("hvacMonitor");     // Port name that is displayed in the Arduino IDE.

   ArduinoOTA.onStart([]() {
     Serial.println("Start");
@@ -155,26 +122,19 @@
   // 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
-
+
+  fanStatusTimer = timer.setInterval(1000, fanStatus);
+  setClockTimeTimer = timer.setInterval(1000, setClockTime);
+  tempDisplayTimer = timer.setInterval(3210, tempDisplay);
+  statusDisplayTimer = timer.setInterval(3543, statusDisplay);
+  tempWatcherTimer = timer.setInterval(5000, tempWatcher);
+  longStatusDisplayTimer = timer.setInterval(600000, longStatusDisplay);  // 10 minutes
+  timer.setInterval(65432, updateVpinData1);
+
+  timer.disable(fanStatusTimer);
+  timer.disable(tempDisplayTimer);
+  timer.disable(statusDisplayTimer);
+  timer.disable(tempWatcherTimer);
 }

 void loop()  // To keep Blynk happy, keep most tasks out of the loop.
@@ -183,405 +143,451 @@
   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();

@@ -595,418 +601,18 @@

     if (minDur < 121)
     {
-      terminal.print(String("HVAC: ") + minDur + " mins @ ");
+      terminal.print(String("HVAC: ") + minDur + " min/");
+      terminal.print(WiFi.RSSI());
+      terminal.print("/");
       terminal.println(WiFi.localIP());
     }
     else if (minDur > 120)
     {
-      terminal.print(String("HVAC: ") + hourDur + " hrs @ ");
+      terminal.print(String("HVAC: ") + hourDur + " hr/");
+      terminal.print(WiFi.RSSI());
+      terminal.print("/");
       terminal.println(WiFi.localIP());
     }
     terminal.flush();
   }
 }
-
-BLYNK_WRITE(V26)
-{
-  if (String("FILTER") == param.asStr() || String("filter") == param.asStr()) {
-    terminal.println(""); terminal.println("");
-    terminal.println("       ~~ A/C Filter Status Mode ~~"); terminal.println(" ");
-
-    if ( (filterChangeHours - currentFilterHours ) > 0) {
-      terminal.print(filterChangeHours - currentFilterHours);
-      terminal.println(" hours until next filter change (last");
-      terminal.print("filter change was ");
-      terminal.println(String("") + month(timeFilterChanged) + "/" + day(timeFilterChanged) + "/" + year(timeFilterChanged) + ")."); terminal.println("");    // More on this at http://www.pjrc.com/teensy/td_libs_Time.html
-      terminal.println("Type 'f' if filter was just changed with");
-      terminal.print("a 20x25x1 1085 microparticle/MERV 11.");
-    }
-    else {
-      terminal.print("Filter change is ");
-      terminal.print( -1 * (filterChangeHours - currentFilterHours) );
-      terminal.println(" hrs overdue.");
-      terminal.print("Last filter change was ");
-      terminal.println(String("") + month(timeFilterChanged) + "/" + day(timeFilterChanged) + "/" + year(timeFilterChanged) + ")."); terminal.println("");    // More on this at http://www.pjrc.com/teensy/td_libs_Time.html
-      terminal.println("Type 'f' if filter was just changed with");
-      terminal.print("a 20x25x1 1085 microparticle/MERV 11.");
-    }
-  }
-
-  if ( (String("F") == param.asStr() || String("f") == param.asStr()) && filterConfirmFlag == FALSE) {
-    filterConfirmFlag = TRUE;
-    terminal.println(" "); terminal.println("Enter 'y' to confirm filter was changed.");
-    terminal.println("Enter 'n' to cancel.");
-    terminal.println(" ");
-  }
-  else if ( (String("y") == param.asStr() || String("Y") == param.asStr()) && filterConfirmFlag == TRUE) {
-    currentFilterHours = 0;
-    currentFilterSec = 0;
-    Blynk.virtualWrite(11, currentFilterSec);
-    timeFilterChanged = now();
-    Blynk.virtualWrite(17, timeFilterChanged);
-    filterConfirmFlag = FALSE;
-    Blynk.virtualWrite(10, String( filterChangeHours - currentFilterHours ) + " hours");
-    terminal.println(" "); terminal.println("Filter change acknowledged!");
-    changeFilterAlarm = FALSE;
-  }
-  else if ( (String("n") == param.asStr() || String("N") == param.asStr()) && filterConfirmFlag == TRUE) {
-    filterConfirmFlag = FALSE;
-    terminal.println(" "); terminal.println("Filter change cancelled.");
-  }
-
-  terminal.flush();
-}
-
-BLYNK_WRITE(V20) // App button to reset hardware
-{
-  int pinData = param.asInt();
-
-  if (pinData == 0)
-  {
-    ESP.restart();
-  }
-}
-
-void sendTemps()
-{
-  sensors.requestTemperatures();                                          // Polls the sensors
-  tempRA = sensors.getTempF(ds18b20RA);
-  tempSA = sensors.getTempF(ds18b20SA);
-  tempSplit = tempRA - tempSA;
-
-  // RETURN AIR
-  if (digitalRead(blowerPin) == LOW)    // If HVAC blower running...
-  {
-    Blynk.virtualWrite(0, tempRA);      // ...display the temp...
-  }
-  else if (digitalRead(fanPin) == LOW)  // ...if fan-only running, display FAN, unless...
-  {
-    Blynk.virtualWrite(0, "FAN");
-  }
-  else if (tempRA <= 0 || tempRA > 150)
-  {
-    Blynk.virtualWrite(0, "ERR");       // ...there's an error, then display ERR, otherwise...
-  }
-  else {
-    Blynk.virtualWrite(0, "OFF");       // ...display OFF.
-  }
-
-  // SUPPLY AIR
-  if (digitalRead(blowerPin) == LOW)
-  {
-    Blynk.virtualWrite(1, tempSA);
-  }
-  else if (digitalRead(fanPin) == LOW)
-  {
-    Blynk.virtualWrite(1, "FAN");
-  }
-  else if (tempSA <= 0 || tempSA > 150)
-  {
-    Blynk.virtualWrite(1, "ERR");
-  }
-  else {
-    Blynk.virtualWrite(1, "OFF");
-  }
-
-  if (digitalRead(blowerPin) == LOW) {    // If HVAC is running and...
-    if (tempSA > tempRA && hvacMode == FALSE)       // supply air is warmer than return air...
-    {
-      Blynk.setProperty(V1, "color", "#ff0000");    // Supply is RED
-      Blynk.setProperty(V0, "color", "#04C0F8");    // Return is BLUE
-      hvacMode = TRUE;                              // FALSE = cooling, TRUE =  heating.
-    }
-    else if (tempSA <= tempRA && hvacMode == TRUE)
-    {
-      Blynk.setProperty(V1, "color", "#04C0F8");    // Supply is BLUE
-      Blynk.setProperty(V0, "color", "#ff0000");    // Return is RED
-      hvacMode = FALSE;                             // FALSE = cooling, TRUE =  heating.
-    }
-  }
-}
-
-void modeTracking()
-{
-  if (digitalRead(blowerPin) == LOW)  // If the blower is running...
-  {
-    if (tempSA > tempRA)              // and it looks like we're heating, then...
-    {
-      hvacMode = TRUE;                // TRUE = heating, and...
-      ++todaysAccumRuntimeSecHeating; // count each heating second, but...
-    }
-    else if (tempSA < tempRA)         // if it looks like we're coolng, then...
-    {
-      hvacMode = FALSE;               // FALSE = cooling, and...
-      ++todaysAccumRuntimeSecCooling; // count each cooling second.
-    }
-  }
-
-  if (todaysAccumRuntimeSecHeating == 0 && todaysAccumRuntimeSecCooling == 0)
-  {
-    Blynk.virtualWrite(38, "NONE");
-  }
-  else if (todaysAccumRuntimeSecHeating < todaysAccumRuntimeSecCooling)
-  {
-    Blynk.virtualWrite(38, "COOL");
-    heatingMode = FALSE;
-  }
-  else if (todaysAccumRuntimeSecHeating > todaysAccumRuntimeSecCooling)
-  {
-    Blynk.virtualWrite(38, "HEAT");
-    heatingMode = TRUE;
-  }
-}
-
-void sendStatus()
-{
-  if (resetFlag == TRUE && year() != 1970) {                       // Runs once following Arduino reset/start.
-    resetTimeDate = currentTimeDate;
-    Blynk.virtualWrite(16, currentStatus);
-    Blynk.setProperty(V16, "label", String("Current Status:                    SYSTEM RESET at ") + resetTimeDate);
-  }
-
-  // Purpose: To keep the display showing a system reset state until the HVAC has turned on or off.
-  if (digitalRead(blowerPin) == LOW && resetFlag == TRUE) {        // Runs once following Arduino reset/start if fan is ON.
-    startFlag = TRUE;                                              // Tells the next set of IFs that fan changed state.
-    resetFlag = FALSE;                                             // Locks out this IF and reset IF above.
-  }
-  else if (digitalRead(blowerPin) == HIGH && resetFlag == TRUE) {  // Runs once following Arduino reset/start if fan is OFF.
-    stopFlag = TRUE;
-    resetFlag = FALSE;
-  }
-
-  // Purpose: To swap between ON and OFF display once per fan state change.
-  if (digitalRead(blowerPin) == HIGH && startFlag == TRUE) {          // OFF, but was running
-    stopTimeDate = currentTimeDate;                                   // Set off time.
-    Blynk.virtualWrite(16, String("HVAC OFF since ") + stopTimeDate); // Write to app.
-    currentStatus = String("HVAC OFF since ") + stopTimeDate;
-    Blynk.setProperty(V0, "color", "#808080");
-    Blynk.setProperty(V1, "color", "#808080");
-
-    if ( (filterChangeHours - currentFilterHours ) <= 0 && changeFilterAlarm == FALSE) {
-      Blynk.email(myEmail, "CHANGE A/C FILTER", "Change the A/C filter (20x25x1 1085 microparticle/MERV 11) and reset the timer.");
-      changeFilterAlarm = TRUE;
-    }
-
-    stopFlag = TRUE;                                                  // Ready flag when fan turns on.
-    startFlag = FALSE;                                                // Keep everything locked out until the next cycle.
-  }
-  else if (digitalRead(blowerPin) == LOW && stopFlag == TRUE) {    // RUNNING, but was off
-    startTimeDate = currentTimeDate;
-    Blynk.virtualWrite(16, String("HVAC ON since ") + startTimeDate);
-    currentStatus = String("HVAC ON since ") + startTimeDate;
-
-
-    if (tempSA > tempRA)  // Assumed HEATING mode
-    {
-      Blynk.setProperty(V1, "color", "#ff0000");    // Supply is RED
-      Blynk.setProperty(V0, "color", "#04C0F8");    // Return is BLUE
-      hvacMode = FALSE;                             // FALSE = cooling, TRUE =  heating. Logic swapped on purpose.
-    }
-    else                  // Assumed COOLING mode
-    {
-      Blynk.setProperty(V1, "color", "#04C0F8");    // Supply is BLUE
-      Blynk.setProperty(V0, "color", "#ff0000");    // Return is RED
-      hvacMode = TRUE;                              // FALSE = cooling, TRUE =  heating. Logic swapped on purpose.
-    }
-
-    startFlag = TRUE;
-    stopFlag = FALSE;
-  }
-}
-
-void totalRuntime()
-{
-  if (digitalRead(blowerPin) == LOW) // If fan is running...
-  {
-    ++currentCycleSec;               // accumulate the running time, however...
-    currentCycleMin = (currentCycleSec / 60);
-  }
-  else if (digitalRead(blowerPin) == HIGH && currentCycleSec > 0) // if fan is off but recorded some runtime...
-  {
-    Blynk.virtualWrite(110, ++hvacTodaysStartCount);              // Stores how many times the unit has started today... adds one start.
-
-    currentCycleSec = 0;                                          // Reset the current cycle clock
-    currentCycleMin = 0;
-  }
-}
-
-void alarmTimer() // Does the timing for the RA/SA split temperature alarm.
-{
-  if (digitalRead(blowerPin) == LOW) // Blower is running.
-  {
-    int secondsCount = (millis() / 1000); // Running count that the alarm will reference after the first high split is noticed.
-
-    if (tempSplit > tempSplitSetpoint) // Condition normal. Reset any previous alarm if there was one.
-    {
-      alarmFor = 0;
-      alarmTime = 0;
-    }
-    else // Condition abnormal.
-    {
-      if (alarmFor == 0)
-      {
-        alarmTime = secondsCount + splitAlarmTime; // This sets the alarm time (from split out of spec to notification being sent)
-        ++alarmFor; // Locks out alarm clock reset until alarmFor == 0.
-      }
-    }
-    if (tempSplit <= tempSplitSetpoint && secondsCount > alarmTime && alarmFor == 1 && (tempRA > 0 && tempSA > 0))
-    {
-      Blynk.tweet(String("Low split (") + tempSplit + "°F) " + "recorded at " + hour() + ":" + minute() + ":" + second() + " " + month() + "/" + day() + "/" + year());
-      Blynk.notify(String("Low HVAC split: ") + tempSplit + "°F. Call Wolfgang's"); // ALT + 248 = °
-      alarmFor = 5; // Arbitrary value indicating notification sent and locking out repetitive notifications.
-    }
-    else if (tempSplit <= tempSplitSetpoint && secondsCount > alarmTime && alarmFor == 1 && (tempRA <= 0 && tempSA <= 0) && sensorFailFlag == FALSE)
-    {
-      Blynk.notify("Multiple temp sensor errors recorded. Please check.");
-      sensorFailFlag = TRUE;
-    }
-  }
-  else
-  {
-    alarmFor = 0;     // Resets alarm counting "latch."
-    alarmTime = 0;    // Resets splitAlarmTime alarm.
-  }
-}
-
-void setClockTime()
-{
-  if (year() != 1970) // Doesn't start until RTC is set correctly.
-  {
-
-    // 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();
-    }
-  }
-}
-
-void countRuntime()
-{
-  if (year() != 1970) // Doesn't start until RTC is set correctly.
-  {
-
-    if (daySet == FALSE) {              // Sets the date (once per hardware restart) now that RTC is correct.
-      todaysDate = day();
-      daySet = TRUE;
-    }
-
-    if (digitalRead(blowerPin) == LOW && todaysDate == day()) // Accumulates seconds unit is running today.
-    {
-      ++todaysAccumRuntimeSec;                          // Counts today's AC runtime in seconds.
-      Blynk.virtualWrite(111, todaysAccumRuntimeSec);
-
-    }
-    else if (todaysDate != day())
-    {
-      yesterdayRuntime = (todaysAccumRuntimeSec / 60);  // Moves today's runtime to yesterday for the app display.
-      Blynk.virtualWrite(112, yesterdayRuntime);        // Record yesterday's runtime to vPin.
-      todaysAccumRuntimeSec = 0;                        // Reset today's sec timer.
-      todaysAccumRuntimeSecHeating = 0;                 // Reset today's heating and cooling timers.
-      todaysAccumRuntimeSecCooling = 0;
-      Blynk.virtualWrite(111, todaysAccumRuntimeSec);
-      hvacTodaysStartCount = 0;                         // Reset how many times unit has started today.
-      todaysDate = day();
-    }
-
-    if (digitalRead(blowerPin) == LOW || digitalRead(fanPin) == LOW) {
-      ++currentFilterSec;                               // Counts how many seconds filter is used for HVAC or fan-only.
-      Blynk.virtualWrite(11, currentFilterSec);
-      currentFilterHours = (currentFilterSec / 3600);   // Converts those seconds to hours for display and other uses.
-    }
-
-    if (yesterdayRuntime < 1)               // Displays yesterday's runtime in app, or 'None' is there's none.
-    {
-      Blynk.virtualWrite(14, "None");
-    }
-    else
-    {
-      Blynk.virtualWrite(14, String(yesterdayRuntime) + " minutes");
-    }
-
-    if (todaysAccumRuntimeSec == 0)          // Displays today's runtime in app, or 'None' is there's none.
-    {
-      Blynk.virtualWrite(15, "None");
-    }
-    else if (todaysAccumRuntimeSec > 0 && hvacTodaysStartCount == 0)
-    {
-      Blynk.virtualWrite(15, String(todaysAccumRuntimeSec / 60) + " minutes");
-    }
-    else if (todaysAccumRuntimeSec > 0 && hvacTodaysStartCount > 0)
-    {
-      Blynk.virtualWrite(15, String(todaysAccumRuntimeSec / 60) + " mins (" + (hvacTodaysStartCount) + " runs)");
-    }
-  }
-
-  Blynk.virtualWrite(10, String( filterChangeHours - (currentFilterSec / 3600) ) + " hours"); // Displays how many hours are left on the filter.
-
-  if (second() >= 0 && second() <= 5)     // Used to monitor uptime. All devices report minute. Another device confirms all reported minutes match.
-  {
-    Blynk.virtualWrite(100, minute());
-  }
-}
-
-/*
-  int todayTrends[96];  // 0 represents the first 15 minutes of the day, and so on. Will have 96 elements.
-  int trackTrendIndex;
-  int yesterdayTrends[96];
-  bool trackTrendResetFlag;
-  bool trackTrendMinuteFlag;
-
-  void trackTrends()
-  {
-  if (hour() == 0 && trackTrendResetFlag  == 0)   // At midnight...
-  {
-    int i;
-    for (i = 0; i < 97; i++) {                    // ...transfer todayTrends to yesterdayTrends.
-      yesterdayTrends[i] = todayTrends[i];
-    }
-
-    trackTrendIndex = 0;                          // Reset array index to 0.
-    trackTrendResetFlag = 1;
-  }
-  else if (hour() == 1)                           // Resets flag for the next midnight "reset"
-  {
-    trackTrendResetFlag = 0;
-  }
-
-  if (minute() == 0 && hour() == 0)
-  {
-    // Do nothing... this would record the previous day runtime (23:45 - 00:00) and we don't want that here.
-  }
-  else if ( (minute() == 0 || minute() == 15 || minute() == 30 || minute() == 45) && trackTrendMinuteFlag == 0)
-  {
-    todayTrends[trackTrendIndex] = (todaysAccumRuntimeSec / 60);
-
-    if (todayTrends[trackTrendIndex] = yesterdayTrends[trackTrendIndex])
-    {
-      // "Trending same runtime as yesterday"
-    }
-    else if (todayTrends[trackTrendIndex] > yesterdayTrends[trackTrendIndex])
-    {
-      // "Trending higher runtime today"
-    }
-    else if (todayTrends[trackTrendIndex] < yesterdayTrends[trackTrendIndex])
-    {
-      // "Trending lower runtime today"
-    }
-
-    ++trackTrendIndex;
-    trackTrendMinuteFlag = 1;
-  }
-
-  if (minute() == 1 || minute() == 16 || minute() == 31 || minute() == 46)
-  {
-    trackTrendMinuteFlag = 0;
-  }
-
-  }
-*/


Correct prescalar for timer2.

Correct prescalar for timer2.

--- arduino/giantsketchy/timer_isr.ino
+++ arduino/giantsketchy/timer_isr.ino
@@ -96,8 +96,8 @@
   // CTC mode: clear counter and interrupt when OCR0A is matched
   TCCR2A = (1 << WGM21);

-  // prescaler divides clk by 64 (250 kHz tick)
-  TCCR2B = (1 << CS21) | (1 << CS20);
+  // prescaler divides clk by 64 (250 kHz tick) (timer0 differs here)
+  TCCR2B = (1 << CS22);

   // set compare match register for frequency (value must be <256)
   OCR2A = (uint8_t)(16000000L / ((uint32_t) INTERRUPT_FREQUENCY * 64L) - 1L);


Remove test lines

Remove test lines

--- galileo_matlab_serial_term.ino
+++ galileo_matlab_serial_term.ino
@@ -10,7 +10,6 @@
 byte angle;
 Robot *r;
 Protocol *p;
-String st;

 void setup() {
   Peripheral *hw = new Peripheral();
@@ -34,10 +33,4 @@
     angle = p->requestAngle(motorNum, angle);  // requesting new angle
     r->setAngle(motorNum, angle);  // setting new angle
   }
-
-  if (Serial.available() > 0) {
-    st = Serial.readStringUntil('\n');
-    Serial.println(st);
-    Serial.println(st.length());
-  }
 }


update SIM800 example

update SIM800 example

--- examples/Modules/SIM800L/HTTP/HTTP.ino
+++ examples/Modules/SIM800L/HTTP/HTTP.ino
@@ -55,42 +55,44 @@
     // while(!Serial2.available());
     delay(5000);
     sendATCMDAndRevice("AT+HTTPREAD\r\n");
+    delay(1000);
+    String recvStr;
     while(Serial2.available()){
-      String recvStr;
-      recvStr = Serial2.readStringUntil('\n');
-      Serial.println(recvStr);
-      terminal.println(recvStr);
+      recvStr+= Serial2.readString();
     }
+    Serial.println(recvStr);
+    terminal.println(recvStr);
     sendATCMDAndRevice("AT+HTTPTERM\r\n");
 }

-// void POST() {
-//     terminal.println("POST Request");
-//     sendATCMD("AT?\r\n");
-//     delay(100);
-//     Serial2.flush();
-//     sendATCMDAndRevice("AT+CGATT?\r\n");
-//     sendATCMDAndRevice("AT+SAPBR=3,1,\"CONTYPE\",\"GPRS\"\r\n");
-//     sendATCMDAndRevice("AT+SAPBR=3,1,\"APN\",\"CMNET\"\r\n");
-//     sendATCMDAndRevice("AT+SAPBR=0,1\r\n");
-//     sendATCMDAndRevice("AT+SAPBR=1,1\r\n");
-//     sendATCMDAndRevice("AT+HTTPINIT\r\n");
-//     sendATCMDAndRevice("AT+HTTPPARA=\"CID\",1\r\n");
-//     sendATCMDAndRevice("AT+HTTPPARA=\"CONTENT\",\"application/x-www-form-urlencoded\"\r\n");
-//     sendATCMDAndRevice("AT+HTTPPARA=\"URL\",\"https://getman.cn/api/request\"\r\n");
-//     sendATCMD("AT+HTTPDATA=10,10000\r\n");
-//     sendATCMD("M5STACK,GO\r\n");
-//     sendATCMDAndRevice("AT+HTTPACTION=1\r\n");
-//     delay(5000);
-//     sendATCMDAndRevice("AT+HTTPREAD\r\n");
-//     while(Serial2.available()){
-//       String recvStr;
-//       recvStr = Serial2.readStringUntil('\n');
-//       Serial.println(recvStr);
-//       terminal.println(recvStr);
-//     }
-//     sendATCMDAndRevice("AT+HTTPTERM\r\n");
-// }
+void POST() {
+    terminal.println("POST Request");
+    sendATCMD("AT?\r\n");
+    delay(100);
+    Serial2.flush();
+    sendATCMDAndRevice("AT+CGATT?\r\n");
+    sendATCMDAndRevice("AT+SAPBR=3,1,\"CONTYPE\",\"GPRS\"\r\n");
+    sendATCMDAndRevice("AT+SAPBR=3,1,\"APN\",\"CMNET\"\r\n");
+    sendATCMDAndRevice("AT+SAPBR=0,1\r\n");
+    sendATCMDAndRevice("AT+SAPBR=1,1\r\n");
+    sendATCMDAndRevice("AT+HTTPINIT\r\n");
+    sendATCMDAndRevice("AT+HTTPPARA=\"CID\",1\r\n");
+    sendATCMDAndRevice("AT+HTTPPARA=\"CONTENT\",\"application/x-www-form-urlencoded\"\r\n");
+    sendATCMDAndRevice("AT+HTTPPARA=\"URL\",\"http://header.json-json.com/\"\r\n");
+    sendATCMD("AT+HTTPDATA=10,3000\r\n");
+    sendATCMD("M5STACK,GO\r\n");
+    sendATCMDAndRevice("AT+HTTPACTION=1\r\n");
+    delay(5000);
+    sendATCMDAndRevice("AT+HTTPREAD\r\n");
+    delay(1000);
+    String recvStr;
+    while(Serial2.available()){
+      recvStr+= Serial2.readString();
+    }
+    Serial.println(recvStr);
+    terminal.println(recvStr);
+    sendATCMDAndRevice("AT+HTTPTERM\r\n");
+}

 void setup()
 {
@@ -117,6 +119,7 @@
 {
   M5.update();
   if(M5.BtnA.wasPressed()) GET();
+  if(M5.BtnB.wasPressed()) POST();
   if(M5.BtnC.wasPressed()) {
     String recvStr;
     recvStr = Serial2.readStringUntil('\n');


Fixes typo in comment

Fixes typo in comment

--- examples/mqtt_esp8266/mqtt_esp8266.ino
+++ examples/mqtt_esp8266/mqtt_esp8266.ino
@@ -35,7 +35,7 @@

 // Create an ESP8266 WiFiClient class to connect to the MQTT server.
 WiFiClient client;
-// or... use WiFiFlientSecure for SSL
+// or... use WiFiClientSecure for SSL
 //WiFiClientSecure client;

 // Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.


Delete robo_JS_setup.ino

Delete robo_JS_setup.ino

--- robo_JS_setup.ino
+++ robo_JS_setup.ino
@@ -1,87 +0,0 @@
-
-int forwardInPin = A0;               // variable forwardInPin to analog input 0
-int backInPin = A2;                  // variable backInPin to analog input 2
-int leftOutPin = 8;                  // variable forwardInPin to analog input 0
-int rightOutPin = 12;                // variable forwardInPin to analog input 0
-int led = 13;
-
-
-void setup()
-{
-  pinMode(forwardInPin, INPUT);      // sets the analog pin as input
-  pinMode(leftOutPin, OUTPUT);       // sets the pin as output
-  pinMode(rightOutPin, OUTPUT);      // sets the pin as output
-  pinMode(led, OUTPUT);
-}
-
-void loop() //forward for 1 sec
-{
-    //forward
-    analogWrite(forwardInPin, 255);       // turns on forward power
-    digitalWrite(leftOutPin, LOW);     // left pulls in
-    digitalWrite(rightOutPin, LOW);    // right pulls in
-    delay(500);                         // waits for a second
-
-    // indecator light
-    digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
-    delay(1000);               // wait for a second
-    digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
-
-        //right 90
-    analogWrite(forwardInPin, 255);       // turns on forward power
-    digitalWrite(leftOutPin, HIGH);     // left pulls in
-    digitalWrite(rightOutPin, LOW);    // right pulls in
-    delay(428);                         // time of turn
-
-        // indecator light X2 to indicate rest
-    digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
-    delay(1000);               // wait for a second
-    digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
-    delay(1000);               // wait for a second
-    digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
-    delay(1000);               // wait for a second
-    digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
-
-
-        //    wait
-    analogWrite(forwardInPin, 0);       // turns on forward power
-    digitalWrite(leftOutPin, LOW);     // left pulls in
-    digitalWrite(rightOutPin, LOW);    // right pulls in
-    delay(5000);                         // waits for 5 seconds
-
-    // indecator light
-    digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
-    delay(1000);               // wait for a second
-    digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
-
-
-        //    left 90
-    analogWrite(forwardInPin, 255);
-    digitalWrite(leftOutPin, LOW);     // left does not pull in
-    digitalWrite(rightOutPin, HIGH);    // right pulls in
-    delay(428);                         // time of turn
-
-        // indecator light X2 to indicate rest
-    digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
-    delay(1000);               // wait for a second
-    digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
-    delay(1000);               // wait for a second
-    digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
-    delay(1000);               // wait for a second
-    digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
-    delay(1000);               // wait for a second
-
-        //    wait
-    analogWrite(forwardInPin, 0);       // turns on forward power
-    digitalWrite(leftOutPin, LOW);     // left dosen't pulls in
-    digitalWrite(rightOutPin, LOW);    // right dosen't pulls in
-    delay(5000);                       // waits for 5 seconds
-
-    // indecator light
-    digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
-    delay(1000);               // wait for a second
-    digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
-    delay(1000);               // wait for a second
-
-}
-


Fix timing issues with USB controller.

Fix timing issues with USB controller.

--- ArduinoPondArea/ArduinoPondArea.ino
+++ ArduinoPondArea/ArduinoPondArea.ino
@@ -225,8 +225,6 @@
   usbDeviceWrite("IPA\r");
   usbDeviceWrite("ECS\r");
   delay(100);
-
-
 }

 void setupUsbStickForWrite()
@@ -248,6 +246,7 @@
   writeCommand += '\r';
   writeCommand += data;
   usbDeviceWrite(writeCommand);
+  delay(150);

   if (++write2fileCount >= USB_WRITE_MANUAL_FLUSH_RATE) {
     Serial.println("Manually closing & reopening datafile.");
@@ -259,7 +258,7 @@

 String dumpUsbOutput()
 {
-    if (usbDev.available() && Serial) {
+    if (usbDev.available()) {
     String fromUsb = "";
     while (usbDev.available()) {
       char readC = usbDev.read();
@@ -293,7 +292,7 @@
 void usbOpenFile4Write(const String& filename)
 {
   usbDeviceWrite("OPW " + filename + "\r");
-  delay(100);
+  delay(200);
   dumpUsbOutput();
 }


removed unused variable

removed unused variable

--- ArduinoRF24-LED/ArduinoRF24-LED.ino
+++ ArduinoRF24-LED/ArduinoRF24-LED.ino
@@ -109,7 +109,6 @@

         uint8_t led_num = 0;
         uint8_t pwm_val = 0;
-        bool current_led_state = 0;
         bool answer = false;
         switch(read_buffer[0])
         {


copying of button functions

copying of button functions

--- vibrate_alarm_clock_2.ino
+++ vibrate_alarm_clock_2.ino
@@ -55,6 +55,9 @@
 unsigned long timeLastPressedMiddleButton;
 unsigned long timeLastPressedRightButton;

+
+boolean vibrate1 = false;
+boolean vibrate2 = false;

 boolean previousBlinkState;
 unsigned long previousBlinkTime;
@@ -138,7 +141,7 @@
     int voltageADCReading = getVoltageADCReading();

     long vccVoltage = getVcc();
-    Serial.println(vccVoltage);
+    //Serial.println(vccVoltage);
     int batteryMilliVolt = ((long) voltageADCReading * vccVoltage) / ADC_PRECISION;

     writeTimeToDisplayBuffer(now, blinkOn, secondsBlink);
@@ -159,6 +162,15 @@
 int getVoltageADCReading(){
   return analogRead(VOLTAGE_MEASURE_PIN);
 }
+
+
+void turnOffLCD(){
+  if(!showLCD){
+    uView.clear(PAGE);
+    uView.display();
+  }
+}
+
 void processLeftButtonPressed(){
   unsigned long currentMillis = millis();

@@ -169,6 +181,84 @@
   timeLastPressedLeftButton = currentMillis;

   Serial.println("Left Button Pressed");
+
+  switch(currentState)
+  {
+  case ALARM: stopAlarm();
+  break;
+  case NORMAL :
+  {
+    showLCD = !showLCD;
+    turnOffLCD();
+
+  }
+  break;
+  case SETTING_ALARM:
+  {
+   if(settingAlarmProcess == A_HOUR){
+       alarmHour = getNextHourFromCurrentHour(alarmHour, false);
+    } else if(settingAlarmProcess == A_MINUTE){
+       alarmMinute = getNextMinSecFromCurrentMinSec(alarmMinute, false);
+    } else if(settingAlarmProcess == A_TYPE){
+
+       if(!vibrate1 && !vibrate2){
+          vibrate1 = true;
+          vibrate2 = true;
+       } else if(!vibrate1 && vibrate2){
+          vibrate1 = false;
+          vibrate2 = false;
+       } else if(vibrate1 && !vibrate2){
+         vibrate1 = false;
+         vibrate2 = true;
+       } else {
+          vibrate1 = true;
+          vibrate2 = false;
+      }
+   }
+
+  }
+  break;
+  case SETTING_TIME:
+  {
+    DateTime now = RTC.now();
+    DateTime newTime;
+    switch(settingTimeProcess){
+      case T_HOUR:
+        newTime = DateTime(now.year(), now.month(), now.day(), getNextHourFromCurrentHour(now.hour(), false), now.minute(), now.second());
+        break;
+      case T_MINUTE:
+        newTime = DateTime(now.year(), now.month(), now.day(), now.hour(), getNextMinSecFromCurrentMinSec(now.minute(), false), now.second());
+        break;
+      case T_SECOND:
+        newTime = DateTime(now.year(), now.month(), now.day(), now.hour(), now.minute(), getNextMinSecFromCurrentMinSec(now.second(), false));
+        break;
+      case T_DAY:
+        newTime = DateTime(now.year(), now.month(), now.day() - 1, now.hour(), now.minute(), now.second());
+        break;
+      case T_MONTH:
+      {
+        int month = now.month();
+        if(month == 1){
+          month = 12;
+        } else {
+          month--;
+        }
+
+        newTime = DateTime(now.year(), month, now.day(), now.hour(), now.minute(), now.second());
+      }
+        break;
+      case T_YEAR:
+        newTime = DateTime(now.year() - 1, now.month(), now.day(), now.hour(), now.minute(), now.second());
+        break;
+      default: break;
+
+    }
+
+    RTC.adjust(newTime);
+  }
+  break;
+  default: break;
+  }

 }
@@ -184,6 +274,54 @@
   timeLastPressedMiddleButton = currentMillis;

   Serial.println("Middle Button Pressed");
+
+    switch(currentState)
+  {
+    case ALARM: stopAlarm();
+    break;
+    case NORMAL :
+    {
+      if(showLCD){
+        currentState = SETTING_ALARM;
+        settingAlarmProcess = A_HOUR;
+      } else {
+        showLCD = true;
+      }
+
+    }
+    break;
+    case SETTING_ALARM:
+    {
+      if(settingAlarmProcess == A_HOUR){
+         settingAlarmProcess = A_MINUTE;
+      } else if(settingAlarmProcess == A_MINUTE){
+         settingAlarmProcess = A_TYPE;
+      } else {
+        currentState = NORMAL;
+      }
+
+    }
+    break;
+    case SETTING_TIME:
+    {
+      if(settingTimeProcess == T_HOUR){
+         settingTimeProcess = T_MINUTE;
+      } else if(settingTimeProcess == T_MINUTE){
+         settingTimeProcess = T_SECOND;
+      } else if(settingTimeProcess == T_SECOND){
+         settingTimeProcess = T_DAY;
+      } else if(settingTimeProcess == T_DAY){
+        settingTimeProcess = T_MONTH;
+      } else if(settingTimeProcess == T_MONTH){
+         settingTimeProcess = T_YEAR;
+      } else {
+        currentState = NORMAL;
+      }
+
+    }
+    break;
+    default: break;
+    }

 }
@@ -200,6 +338,88 @@

   Serial.println("Right Button Pressed");

+
+  switch(currentState)
+  {
+    case ALARM: stopAlarm();
+    break;
+    case NORMAL :
+   {
+      if(showLCD){
+        currentState = SETTING_TIME;
+        settingTimeProcess = T_HOUR;
+      } else {
+        showLCD = true;
+      }
+
+   }
+    break;
+    case SETTING_ALARM:
+    {
+        if(settingAlarmProcess == A_HOUR){
+          alarmHour = getNextHourFromCurrentHour(alarmHour, true);
+        } else if(settingAlarmProcess == A_MINUTE){
+          alarmMinute = getNextMinSecFromCurrentMinSec(alarmMinute, true);
+        } else if(settingAlarmProcess == A_TYPE){
+
+          if(!vibrate1 && !vibrate2){
+                vibrate1 = false;
+                vibrate2 = true;
+              } else if(!vibrate1 && vibrate2){
+                vibrate1 = true;
+                vibrate2 = false;
+              } else if(vibrate1 && !vibrate2){
+                vibrate1 = true;
+                vibrate2 = true;
+              } else {
+                vibrate1 = false;
+                vibrate2 = false;
+              }
+        }
+    }
+    break;
+    case SETTING_TIME:
+      {
+    DateTime now = RTC.now();
+    DateTime newTime;
+    switch(settingTimeProcess){
+      case T_HOUR:
+        newTime = DateTime(now.year(), now.month(), now.day(), getNextHourFromCurrentHour(now.hour(), true), now.minute(), now.second());
+        break;
+      case T_MINUTE:
+        newTime = DateTime(now.year(), now.month(), now.day(), now.hour(), getNextMinSecFromCurrentMinSec(now.minute(), true), now.second());
+        break;
+      case T_SECOND:
+        newTime = DateTime(now.year(), now.month(), now.day(), now.hour(), now.minute(), getNextMinSecFromCurrentMinSec(now.second(), true));
+        break;
+      case T_DAY:
+        newTime = DateTime(now.year(), now.month(), now.day() + 1, now.hour(), now.minute(), now.second());
+        break;
+      case T_MONTH:
+      {
+        int month = now.month();
+        if(month == 12){
+          month = 1;
+        } else {
+          month++;
+        }
+        newTime = DateTime(now.year(), month, now.day(), now.hour(), now.minute(), now.second());
+      }
+        break;
+      case T_YEAR:
+        newTime = DateTime(now.year() + 1, now.month(), now.day(), now.hour(), now.minute(), now.second());
+        break;
+      default: break;
+
+    }
+
+    RTC.adjust(newTime);
+  }
+
+
+    break;
+    default: break;
+  }

 }

@@ -225,20 +445,44 @@
 void writeTimeToDisplayBuffer(DateTime now, boolean blinkOn, boolean secondsBlinkOn){
   uView.setCursor(0,0);
   uView.setFontType(7);
-
-  uView.print(now.hour());
-  if(secondsBlinkOn){
-    uView.print(":");
+
+  String timeString = generateTimeString(now.hour(), now.minute(), secondsBlinkOn);
+  uView.println(timeString);
+}
+
+String generateTimeString(int hour, int minute, boolean blinkOn){
+  char buff[3];
+
+  String hourString;
+
+  if(currentState == SETTING_TIME && settingTimeProcess == T_HOUR && !blinkOn){
+    hourString = "  ";
+  } else {
+    sprintf(buff, "%02d", hour);
+    hourString = buff;
+  }
+
+  String minuteString;
+  if(currentState == SETTING_TIME && settingTimeProcess == T_MINUTE && !blinkOn){
+    minuteString = "  ";
+  } else {
+    sprintf(buff, "%02d", minute);
+    minuteString = buff;
+  }
+
+  String secondString;
+
+ if(blinkOn){
+    secondString = ":";
   }
   else {
-    uView.print(" ");
-  }
-
-
-  uView.println(now.minute());
-
-
-
+    secondString = " ";
+  }
+
+
+  String result = hourString + secondString + minuteString;
+
+  return result;
 }

 void writeAlarmToDisplayBuffer(boolean blinkOn){
@@ -328,10 +572,39 @@
 }

-
-
-
-
-
-
-
+int getNextHourFromCurrentHour(int current, boolean increment){
+  int change;
+  if(increment){
+    change = 1;
+  } else {
+    change = -1;
+  }
+
+  int newValue = current + change;
+
+  //To produce positive modulo result
+  return (newValue % 24 + 24) % 24;
+}
+
+int getNextMinSecFromCurrentMinSec(int current, boolean increment){
+    int change;
+  if(increment){
+    change = 1;
+  } else {
+    change = -1;
+  }
+
+  int newValue = current + change;
+
+  //To produce positive modulo result
+  return (newValue % 60 + 60) % 60;
+}
+
+
+
+
+
+
+
+
+


Menus mostly working

Menus mostly working

--- DigitalVFO.ino
+++ DigitalVFO.ino
@@ -20,7 +20,7 @@

 // Digital VFO program name & version
 const char *ProgramName = "Digital VFO";
-const char *Version = "0.3";
+const char *Version = "0.4";
 const char *Callsign = "vk4fawr";
 const char *Callsign16 = "vk4fawr         ";

@@ -108,7 +108,7 @@
 ////////////////////////////////////////////////////////////////////////////////

 unsigned long VfoFrequency;   // VFO frequency (Hz)
-int VfoSelectDigit;           // selected column index, zero at the right
+byte VfoSelectDigit;          // selected column index, zero at the right

 ////////////////////////////////////////////////////////////////////////////////
@@ -118,16 +118,16 @@
 //----------------------------------------------------------
 // Abort the program.
 // Tries to tell the world what went wrong, then just loops.
-//
-// msg  address of error string
-//
-// The message may be truncated at display width.
+//     msg  address of error string
 //----------------------------------------------------------

 void abort(const char *msg)
 {
+  // print error on console (maybe)
   Serial.println(msg);
   Serial.println("Teensy is paused!");
+
+  // show what we can on the display, forever
   while (1)
   {
     lcd.clear();
@@ -139,7 +139,6 @@

     lcd.clear();
     lcd.write("Teensy is paused");
-    lcd.cursor();
     delay(2000);
   }
 }
@@ -148,6 +147,7 @@
 // Convert an event number to a display string
 //----------------------------------------------------------

+#ifdef DEBUG
 const char * event2display(byte event)
 {
   switch (event)
@@ -162,6 +162,7 @@
     default:            return "UNKNOWN!";
   }
 }
+#endif

 // display a simple banner
 void banner(void)
@@ -177,7 +178,7 @@
   lcd.write(Callsign);
   delay(2000);    // wait a bit

-  for (int i = 0; i <= 16; ++i)
+  for (int i = 0; i <= NUM_COLS; ++i)
   {
     lcd.clear();
     lcd.setCursor(i, 0);
@@ -190,6 +191,59 @@
   }

   delay(500);
+}
+
+//----------------------------------------------------------
+// Function to convert an unsigned long into an array of decimal digit values.
+//     buf      address of buffer for byte results
+//     bufsize  size of the 'buf' buffer
+//     value    the unsigned long value to convert
+// The function won't overflow the given buffer, it will truncate at the left.
+// Leading zeros are suppressed.
+//
+// For example, given the value 1234 and a buffer of length 7, will fill the
+// buffer with "   1234".  Given 123456789 it will fill with "3456789".
+//----------------------------------------------------------
+
+void ltochbuff(char *buf, int bufsize, unsigned long value)
+{
+  char *ptr = buf + bufsize - 1;    // rightmost char in 'buf'
+
+  for (int i = 0; i < bufsize; ++i)
+  {
+    int rem = value % 10;
+    char ch = char(rem + '0');
+
+    value = value / 10;
+    if (value == 0L && ch == '0')
+      *ptr-- = ' ';
+    else
+      *ptr-- = ch;
+  }
+}
+
+//----------------------------------------------------------
+// Function to convert an unsigned long into an array of byte digit values.
+//     buf      address of buffer for byte results
+//     bufsize  size of the 'buf' buffer
+//     value    the unsigned long value to convert
+// The function won't overflow the given buffer, it will truncate at the left.
+//
+// For example, given the value 1234 and a buffer of length 7, will fill the
+// buffer with 0001234.  Given 123456789 it will fill with 3456789.
+//----------------------------------------------------------
+
+void ltobbuff(char *buf, int bufsize, unsigned long value)
+{
+  char *ptr = buf + bufsize - 1;    // rightmost char in 'buf'
+
+  for (int i = 0; i < bufsize; ++i)
+  {
+    int rem = value % 10;
+
+    value = value / 10;
+    *ptr-- = char(rem);
+  }
 }

@@ -226,10 +280,12 @@
   Serial.println(item_index);
   menu->display(menu, item_index);

+  flush_events();
+
   while (true)
   {
     // handle any pending event
-    if (queue_len() > 0)
+    if (events_pending() > 0)
     {
       // get next event and handle it
       byte event = pop_event();
@@ -260,11 +316,11 @@
           Serial.println("Menu: vfo_Click");
           if (menu->action)
             (*menu->action)(menu, item_index);
-          flush_queue();
-          return;
+          flush_events();
+          break;
         case vfo_HoldClick:
           Serial.println("Menu: vfo_HoldClick, exit menu");
-          flush_queue();
+          flush_events();
           return;
         default:
           Serial.print("Menu: unrecognized event "); Serial.println(event);
@@ -333,7 +389,7 @@
   return event;
 }

-int queue_len(void)
+int events_pending(void)
 {
   // Must protect from RE code fiddling with queue
   noInterrupts();
@@ -350,7 +406,7 @@
   return result;
 }

-void flush_queue(void)
+void flush_events(void)
 {
   queue_fore = 0;
   queue_aft = 0;
@@ -367,7 +423,7 @@
     Serial.print(" "); Serial.print(event_queue[i]);
   }
   Serial.println("");
-  Serial.print("Queue length="); Serial.println(queue_len());
+  Serial.print("Queue length="); Serial.println(events_pending());
   Serial.print("queue_aft="); Serial.print(queue_aft);
   Serial.print(", queue_fore="); Serial.println(queue_fore);

@@ -378,30 +434,6 @@
 ////////////////////////////////////////////////////////////////////////////////
 // Utility routines for the display.
 ////////////////////////////////////////////////////////////////////////////////
-
-//----------------------------------------------------------
-// Function to convert an unsigned long into an array of byte digit values.
-//     buf      address of buffer for byte results
-//     bufsize  size of the 'buf' buffer
-//     value    the unsigned long value to convert
-// The function won't overflow the given buffer, it will truncate at the left.
-//
-// For example, given the value 1234 and a buffer of length 7, will fill the
-// buffer with 0001234.  Given 123456789 it will fill with 3456789.
-//----------------------------------------------------------
-
-void ltobbuff(char *buf, int bufsize, unsigned long value)
-{
-  char *ptr = buf + bufsize - 1;
-
-  for (int i = 0; i < bufsize; ++i)
-  {
-    int rem = value % 10;
-
-    value = value / 10;
-    *ptr-- = char(rem);
-  }
-}

 //----------------------------------------------------------
 // Print the frequency on the display with selected colum underlined.
@@ -455,7 +487,7 @@
 ////////////////////////////////////////////////////////////////////////////////

 // time when click becomes a "hold click" (milliseconds)
-#define HoldClickTime 700
+#define HoldClickTime 500

 // internal variables
 bool re_rotation = false;       // true if rotation occurred while knob down
@@ -577,14 +609,26 @@

 // address for unsigned long 'frequency'
 const int AddressFreq = 0;
+const int AddressFreqSize = sizeof(AddressFreq);

 // address for int 'selected digit'
-const int AddressSelDigit = AddressFreq + sizeof(AddressFreq);
+const int AddressSelDigit = AddressFreq + AddressFreqSize;
+const int AddressSelDigitSize = sizeof(int);

 // number of frequency save slots in EEPROM
 const int NumSaveSlots = 10;
-const int SaveSlotBase = AddressSelDigit + sizeof(AddressSelDigit);
-
+
+const int SaveFreqSize = sizeof(long);
+const int SaveFreqBase = AddressSelDigit + AddressSelDigitSize;
+const int SaveFreqBaseSize = NumSaveSlots * SaveFreqSize;
+
+//also save the offset for each frequency
+const int SaveOffsetSize = sizeof(byte);
+const int SaveOffsetBase = SaveFreqBase + SaveFreqBaseSize;
+const int SaveOffsetBaseSize = NumSaveSlots * SaveOffsetSize;
+
+// free slot address
+const int NextFreeAddress = SaveOffsetBase + SaveOffsetBaseSize;

 // save VFO state to EEPROM
 void save_to_eeprom(void)
@@ -623,9 +667,14 @@
 {
   for (int i = 0; i < NumSaveSlots; ++i)
   {
-    int address = SaveSlotBase + i * sizeof(unsigned long);
-    EEPROM.put(address, 0L);
-    Serial.print("Zeroing address "); Serial.println(address);
+    int freq_address = SaveFreqBase + i * SaveFreqSize;
+    int offset_address = SaveOffsetBase + i * SaveOffsetSize;
+
+    EEPROM.put(freq_address, (long) 0L);
+    Serial.print("Zeroing address "); Serial.println(freq_address);
+
+    EEPROM.put(offset_address, (byte) 0);
+    Serial.print("Zeroing address "); Serial.println(offset_address);
   }
   Serial.println("End of zero_save_slots()");
 }
@@ -659,6 +708,8 @@
 void show_main_screen(void)
 {
   lcd.clear();
+  lcd.setCursor(0, 0);
+  lcd.write("Freq:");
   print_freq(VfoFrequency, VfoSelectDigit, 0);
 }

@@ -709,7 +760,7 @@
   lcd.setCursor(0, 1);
   lcd.setCursor(NUM_COLS - max_len, 1);
   lcd.write(menu->items[slot_num]);
-  Serial.print("Setting cursor to column "); Serial.println(NUM_COLS - max_len);
+  Serial.print("Set cursor to column "); Serial.println(NUM_COLS - max_len);
   Serial.print("item text="); Serial.println(menu->items[slot_num]);
 }

@@ -739,11 +790,11 @@
   char buf[MAX_FREQ_CHARS + 1];

   // get saved frequency, convert to a display buffer
-  int address = SaveSlotBase + slot_num * sizeof(unsigned long);
+  int address = SaveFreqBase + slot_num * sizeof(unsigned long);
   unsigned long freq;

   EEPROM.get(address, freq);
-  ltobbuff(buf, MAX_FREQ_CHARS, freq);
+  ltochbuff(buf, MAX_FREQ_CHARS, freq);
   buf[MAX_FREQ_CHARS] = 0;
   Serial.print("save_restore_display: freq="); Serial.println(freq);
   Serial.print("save_restore_display: buf="); Serial.write(buf, MAX_FREQ_CHARS);
@@ -766,23 +817,32 @@

 void save_select(Menu *menu, int slot_num)
 {
-  int address = SaveSlotBase + slot_num * sizeof(unsigned long);
+  int freq_address = SaveFreqBase + slot_num * SaveFreqSize;
+  int offset_address = SaveOffsetBase + slot_num * SaveOffsetSize;

   Serial.print("save_select: saving Frequency "); Serial.print(VfoFrequency);
-  Serial.print(" to slot "); Serial.print(slot_num);
-  Serial.print("="); Serial.println(address, HEX);
-  EEPROM.put(address, VfoFrequency);
+  Serial.print(" to slot "); Serial.println(slot_num);
+
+  EEPROM.put(freq_address, VfoFrequency);
+
+  Serial.print("save_select: saving Offset "); Serial.print(VfoSelectDigit);
+  Serial.print(" to slot "); Serial.println(slot_num);
+
+  EEPROM.put(offset_address, VfoSelectDigit);
 }

 void restore_select(Menu *menu, int slot_num)
 {
   Serial.print("restore_select: slot_num="); Serial.println(slot_num);
-  int address = SaveSlotBase + slot_num * sizeof(unsigned long);
-
-  EEPROM.get(address, VfoFrequency);
+  int freq_address = SaveFreqBase + slot_num * SaveFreqSize;
+  int offset_address = SaveOffsetBase + slot_num * SaveOffsetSize;
+
+  EEPROM.get(freq_address, VfoFrequency);
+  EEPROM.get(offset_address, VfoSelectDigit);
+
   Serial.print("restore_select: restoring frequency "); Serial.print(VfoFrequency);
-  Serial.print(" from slot "); Serial.print(slot_num);
-  Serial.print("="); Serial.println(address, HEX);
+  Serial.print(" and offset "); Serial.print(VfoSelectDigit);
+  Serial.print(" from slot "); Serial.println(slot_num);
 }

 //----------------------------------------------------------
@@ -796,7 +856,7 @@
   int old_position = VfoSelectDigit;

   // handle all events in the queue
-  while (queue_len() > 0)
+  while (events_pending() > 0)
   {
     // get next event and handle it
     byte event = pop_event();


added 74HC95

added 74HC95

--- tictactoe/tictactoe.ino
+++ tictactoe/tictactoe.ino
@@ -1,18 +1,23 @@
+#include <Shift.h>

-int ledRed1 = 50;
-int ledGreen1 = 52;
+Shift shift;

-int ledRed2 = 22;
-int ledGreen2 = 24;
+int red1 = 4;
+int green1 = 3;
+int red2 = 2;
+int green2 = 1;

-void setup() {
+float volRedLow= 0.0;
+float volRedHigh = 1.0;
+
+float volGreenLow= 1.0;
+float volGreenHigh = 1.8;
+
+
+void setup() {
   Serial.begin(9600);

-  pinMode(ledRed1, OUTPUT);
-  pinMode(ledGreen1, OUTPUT);
-
-  pinMode(ledRed2, OUTPUT);
-  pinMode(ledGreen2, OUTPUT);
+  shift.initialize();
 }

 // the loop routine runs over and over again forever:
@@ -32,12 +37,12 @@
 }

 void adjust1(float v){
-  if (v < 1){
+  if (v < volRedHigh && v > volRedLow){
     showRed1();
-  }else if (v > 1.01 && v < 2.0) {
+  }else if (v > volGreenLow && v < volGreenHigh) {
     showGreen1();
   } else {
-    turnOff1();
+    turnOff();
   }
 }

@@ -47,37 +52,36 @@
   }else if (v > 1.01 && v < 2.0) {
     showGreen2();
   } else {
-    turnOff2();
+    turnOff();
   }
 }

 void showRed1(){
-  digitalWrite(ledGreen1, LOW);
-  digitalWrite(ledRed1, HIGH);
+  shift.setRegisterPin(green1, LOW);
+  shift.setRegisterPin(red1, HIGH);
+  shift.writeRegisters();
 }

 void showGreen1(){
-  digitalWrite(ledRed1, LOW);
-  digitalWrite(ledGreen1, HIGH);
+  shift.setRegisterPin(green1, HIGH);
+  shift.setRegisterPin(red1, LOW);
+  shift.writeRegisters();
 }

-void turnOff1(){
-  digitalWrite(ledRed1, LOW);
-  digitalWrite(ledGreen1, LOW);
-}
-
-
 void showRed2(){
-  digitalWrite(ledGreen2, LOW);
-  digitalWrite(ledRed2, HIGH);
+  shift.setRegisterPin(green2, LOW);
+  shift.setRegisterPin(red2, HIGH);
+  shift.writeRegisters();
 }

 void showGreen2(){
-  digitalWrite(ledRed2, LOW);
-  digitalWrite(ledGreen2, HIGH);
+  shift.setRegisterPin(green2, HIGH);
+  shift.setRegisterPin(red2, LOW);
+  shift.writeRegisters();
 }

-void turnOff2(){
-  digitalWrite(ledRed2, LOW);
-  digitalWrite(ledGreen2, LOW);
+void turnOff(){
+  shift.clearRegisters();
+  shift.writeRegisters();
 }
+


Fixed issue where it thought the door was open at night time.

--- chicken_door_timer_v2.ino
+++ chicken_door_timer_v2.ino
@@ -563,7 +563,7 @@

     // if up switch is pressed at day time
     digitalWrite(PIN_DOOR_UP, HIGH);
-    if(digitalRead(PIN_LIMSW_UP) == LOW){
+    if(digitalRead(PIN_LIMSW_UP) == HIGH){
       hit_top = true;
       Serial.print("Hit bottom!");
     }


atualizando para xbee

atualizando para xbee

--- src/robo/robo.ino
+++ src/robo/robo.ino
@@ -3,36 +3,56 @@
   Authors:
   * Marino Hohenheim
   * Pedro Marques
-  Version: 0.1.0
+  Version: 0.2.0
   Boards Supported:
   * Arduino UNO*
   Dependencies:
+  * XBee-Arduino Library, 0.6.0
+  * XCTU, 6.3.8 (usado externamente para monitorar comunicação entre xbee e arduino)
   Description:
   Descrição do projeto
 */

 #include <string.h>
+#include <SoftwareSerial.h>

 #define MEM 50
+#define PORTARX 2
+#define PORTATX 3
 String msg;
 int mem[MEM];

+SoftwareSerial XBee(PORTARX, PORTATX);
+
 void setup(){
+    XBee.begin(9600);
     Serial.begin(9600);
     for(int i=0; i<MEM; i++)
         mem[i]=-1;
         /*
         Seta as coisa reservada
         */
+    delay(500);
+    if(XBee.isListening()) {
+      Serial.println("XBee está conectado!");
+    }
 }

 int parse_msg(String msg);
+int parse_msg_xbee(String msg);

 void loop(){

+    //Se tiver data sendo mandada pelo serial monitor
     if(Serial.available()){
         msg = String(Serial.readString());
         parse_msg(msg);
+    }
+
+    //Se tiver data vindo do XBee
+    if(XBee.available()) {
+        msg = String(XBee.readString());
+        parse_msg_xbee(msg);
     }
 }

@@ -62,3 +82,29 @@
     }
     return 0;
 }
+
+int parse_msg_xbee(String msg){
+    char c = msg[0];
+    int p, value;
+
+    switch(c){
+        case 'G':
+            p = atoi(&msg[1]) -1;
+            XBee.print("R");//Response prefix
+            XBee.println(mem[p]);
+            break;
+        case 'S':
+            p = atoi(&msg[1]) -1;
+            value = atoi(&msg[msg.indexOf('=')+1]);
+            mem[p] = value;
+            XBee.print("R");//Response prefix
+            XBee.println(mem[p]);
+            break;
+        case 'M':
+            XBee.println(&msg[1]);
+            break;
+        default:
+        break;
+    }
+    return 0;
+}


Mikroe: Transmit over XBee

Mikroe: Transmit over XBee

Serial -> Serial1

--- Weather_mikroe_SAM3X/Weather_mikroe_SAM3X.ino
+++ Weather_mikroe_SAM3X/Weather_mikroe_SAM3X.ino
@@ -19,6 +19,7 @@
  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  *
+ * Slot: A: XBee Click
  * Slot: B: Thunder Click
  * Slot: C: Weather Click
  * Slot: D: Air Quality 4 Click
@@ -90,9 +91,10 @@

 void setup()
 {
-	Serial.begin(57600);
-	Serial.println("MIKROESAM3X_DEBUG:MIKROE SAM3X WeatherDemo:#");
-
+	/* Serial = Board USB, Serial1 = Slot A Tx/Rx */
+	Serial1.begin(57600);
+	Serial1.println("MIKROESAM3X_DEBUG:MIKROE SAM3X WeatherDemo:#");
+
 	bme_init();
 	sgp_init();

@@ -108,21 +110,21 @@
 		if(digitalRead(AS3935_INT) == HIGH) {
 			AS3935_intVal = lightning.readInterruptReg();
 			if(AS3935_intVal == AS3935_NOISE_INT) {
-				Serial.println("MIKROESAM3X_DEBUG: Noise:#");
+				Serial1.println("MIKROESAM3X_DEBUG: Noise:#");
 			}
 			else if(AS3935_intVal == AS3935_DISTURBER_INT) {
-				Serial.println("MIKROESAM3X_DEBUG: Disturber:#");
+				Serial1.println("MIKROESAM3X_DEBUG: Disturber:#");
 			}
 			else if(AS3935_intVal == AS3935_LIGHTNING_INT) {
 				bump_counter();
-				Serial.print("MIKROESAM3X_AS3935:");
-				Serial.print(counter);
-				Serial.print(":Lightning:");
+				Serial1.print("MIKROESAM3X_AS3935:");
+				Serial1.print(counter);
+				Serial1.print(":Lightning:");
 				// Lightning! Now how far away is it? Distance estimation takes into
 				// account any previously seen events in the last 15 seconds.
 				byte distance = lightning.distanceToStorm();
-				Serial.print(distance);
-				Serial.println("km:#");
+				Serial1.print(distance);
+				Serial1.println("km:#");
 			}
 		}
 	}
@@ -140,7 +142,7 @@

 void bme_init() {
 	if (! bme.begin(0x76, &Wire)) {
-		Serial.println("MIKROESAM3X_DEBUG:BME280: Sensor not found!:#");
+		Serial1.println("MIKROESAM3X_DEBUG:BME280: Sensor not found!:#");
 		has_bme=false;
 	}
 }
@@ -148,30 +150,30 @@
 void bme_read() {
 	bump_counter();
 	bme.takeForcedMeasurement(); // has no effect in normal mode
-	Serial.print("MIKROESAM3X_WC:");
-	Serial.print(counter);
-	Serial.print(":Temp:");
+	Serial1.print("MIKROESAM3X_WC:");
+	Serial1.print(counter);
+	Serial1.print(":Temp:");
 	bme_temperature = bme.readTemperature();
 	float bme_temp_f = ((bme_temperature * 1.8) + 32);
-	Serial.print(bme_temp_f);
-	Serial.print("F:Press:");
-	Serial.print(bme.readPressure() / 100.0F);
-	Serial.print("mB:Humidity:");
+	Serial1.print(bme_temp_f);
+	Serial1.print("F:Press:");
+	Serial1.print(bme.readPressure() / 100.0F);
+	Serial1.print("mB:Humidity:");
 	bme_humidty = bme.readHumidity();
-	Serial.print(bme_humidty);
-	Serial.println("%:#");
+	Serial1.print(bme_humidty);
+	Serial1.println("%:#");
 }

 void sgp_init() {
 	if (! sgp.begin()) {
-		Serial.println("MIKROESAM3X_DEBUG:SGP30: Sensor not found!:#");
+		Serial1.println("MIKROESAM3X_DEBUG:SGP30: Sensor not found!:#");
         has_sgp=false;
     }
-	Serial.print("MIKROESAM3X_DEBUG:Found SGP30 serial:");
-	Serial.print(sgp.serialnumber[0], HEX);
-	Serial.print(sgp.serialnumber[1], HEX);
-	Serial.print(sgp.serialnumber[2], HEX);
-	Serial.println(":#");
+	Serial1.print("MIKROESAM3X_DEBUG:Found SGP30 serial:");
+	Serial1.print(sgp.serialnumber[0], HEX);
+	Serial1.print(sgp.serialnumber[1], HEX);
+	Serial1.print(sgp.serialnumber[2], HEX);
+	Serial1.println(":#");
 }

 void sgp_read() {
@@ -183,13 +185,13 @@
 		sgp.setHumidity(getAbsoluteHumidity(bme_temperature, bme_humidty));
 	}

-	Serial.print("MIKROESAM3X_AQC:");
-	Serial.print(counter);
-	Serial.print(":TVOC:");
-	Serial.print(sgp.TVOC);
-	Serial.print(":ppb:eCO2:");
-	Serial.print(sgp.eCO2);
-	Serial.println(":ppm:#");
+	Serial1.print("MIKROESAM3X_AQC:");
+	Serial1.print(counter);
+	Serial1.print(":TVOC:");
+	Serial1.print(sgp.TVOC);
+	Serial1.print(":ppb:eCO2:");
+	Serial1.print(sgp.eCO2);
+	Serial1.println(":ppm:#");

 	delay(1000);

@@ -199,14 +201,14 @@

 		uint16_t TVOC_base, eCO2_base;
 		if (! sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) {
-			Serial.println("MIKROESAM3X_DEBUG:Failed to get baseline readings:#");
+			Serial1.println("MIKROESAM3X_DEBUG:Failed to get baseline readings:#");
 			return;
 		}
-		Serial.print("MIKROESAM3X_DEBUG:Baseline values: eCO2: 0x");
-		Serial.print(eCO2_base, HEX);
-		Serial.print(" & TVOC: 0x");
-		Serial.print(TVOC_base, HEX);
-		Serial.println(":#");
+		Serial1.print("MIKROESAM3X_DEBUG:Baseline values: eCO2: 0x");
+		Serial1.print(eCO2_base, HEX);
+		Serial1.print(" & TVOC: 0x");
+		Serial1.print(TVOC_base, HEX);
+		Serial1.println(":#");
 	}
 }

@@ -217,11 +219,11 @@
 	pinMode(AS3935_INT, INPUT);

 	if( !lightning.beginSPI(AS3935_CS, 2000000) ){
-		Serial.println ("MIKROESAM3X_DEBUG:Lightning Detector did not start up, freezing:#");;
+		Serial1.println ("MIKROESAM3X_DEBUG:Lightning Detector did not start up, freezing:#");;
 		has_thunder=false;
 	}
 	else
-		Serial.println("MIKROESAM3X_DEBUG:Lightning Detector Ready:#");
+		Serial1.println("MIKROESAM3X_DEBUG:Lightning Detector Ready:#");

 	if (has_thunder) {
 		lightning.setIndoorOutdoor(AS3935_OUTDOOR);