-->
Page 1 of 1

After using OLED on Wemos D1 Mini, WiFi won't connect

PostPosted: Sat Sep 09, 2017 12:58 am
by MikeyMoMo
Here's the code. The subject says it all. To find the start of the problem, search for "asdfasdf". Any idea why? Yes, I could restructure but I could never show WiFi connection progress unless I can use the OLED first. There is a BMP280, a DHT22, an SD flash board and the OLED board as well as the D1 mini.

Code: Select all/*
 * This program reads data from a BMP280 and a DHT22 and presents that data on an OLED screen while also
 * recording the data, once per minute, on an SD card.  A 4TB SD card can only hold some 150 years of data so be
 * sure to have another one handy for when it runs out of space.
 *
 * The LED_BUILTIN is turned on at the start of readings and also on NTP fetch actions.  Turn turn it on, set
 * it to HIGH.  On needs a LOW.  Why?  Don't know!  Designed by committee?
 *
 * This program is in the public domain.  Be sure it suits your need before using it. All responsibility for
 * use and resulting actions is on you.  Read the source code carefully before compiling and executing any of it.
 *
 * Time is from an NTP server. Temperature is from both sensors and averaged. Humidity is from DHT22.
 * BP is from the BMP/E280.  The thing is marked as both.  Not sure what that really means.  The two temps
 * tend to disagree by several degrees.  Not sure which is right, if either, so I average them.
 *
*/
#include <SPI.h>
#include <SD.h>
extern "C" {
#include "user_interface.h"
}
#include <Time.h>
#include <TimeLib.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_BMP280.h>
#define ALTITUDE 269 *.3048 // Altitude of where you are in meters (converted from feet).
#include <DHT.h>

Adafruit_BMP280 bmp; // I2C
//Adafruit_BMP280 bmp(BMP_CS); // hardware SPI
//Adafruit_BMP280 bmp(BMP_CS, BMP_MOSI, BMP_MISO,  BMP_SCK);

char ssid[] = "something";
char pass[] = "something";

IPAddress ntpServerIP;       // The place to put the results of the DNS query.
const char* ntpServerName = "www.iliketheinternet.com";  // Using my NTP server.
const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets

// A UDP instance to let us send and receive packets over UDP
WiFiUDP udp;

#define OLED_RESET 0
#define AcOrigin 32
#define AcWidth  64
#define DnOrigin 16
#define DnHeight 48
#define line1S1 20  // Line 1 for character Size 1 (all 4 line numbers are fudged a little bit.)
#define line2S1 31
#define line3S1 42
#define line4S1 53
#define line1S2 16  // Line 1 for character Size 2
#define line2S2 40

#define chipSelect D8  // Pin used for CS for the SD shield.

Adafruit_SSD1306 display (OLED_RESET);

#define DHTPIN D4
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);

////The following are the four switch mid values for reading A0 with 1K resistors or higher.
//int switches[4] = {4, 330, 650, 990};  // Values read off of A0 to select display type.
//#define SWITCH_JITTER 50  // The amount of slop allowable and still read the right value.
//#define LOCAL_TIME 0
//#define GMT_TIME   1
//#define NEXT_HACK  2
//#define HIGH_POT   3
//#define UNDEF     10
//int WhichSwitchSet;

// Drift is the difference between the local clock and the NTP answer.  There may be other ways to do this.  Checking...
#define    DriftThreshold     1  // Drift has to be more than this amt of seconds to cause backing off of the hack interval
#define    AdvanceHackAmt 60*60  // Add this many seconds to the next hack interval when the drift is 0
#define    RetardHackAmt  15*60  // Subtract this many seconds from the next hack interval if drift detected (plus or minus)

#define    MAX_HACK    24*60*60  // Maximum time between hacks.  86400 = 1 day in seconds.
#define    MIN_HACK       10*60  // Minimin time between hacks.    600 = 10 minutes.
#define    ReCheckDelay    60-1  // Wait 59 seconds (plus the 1 already waited) to refetch time in a minute.

//Offset from UTC (from NTP response packet) to get local standard time.  West is negative, East is positive.
//Pacific Standard time is -8 hours (west) from GMT.
#define ST_OFFSET_MINUTES -480  // Standard Time Minutes from UTC.  8 hours = 480 minutes.
//Offset from local standard time to get Daylight Savings time
#define DT_OFFSET_MINUTES   60  // Daylight Time Minutes from local time.  Usually add 1 hour.

#define LED_BI_ON LOW
#define LED_BI_OFF HIGH

os_timer_t
        myTimer;
const unsigned long
        seventyYears = 2208988800UL;
unsigned int
        localPort = 2390;      // local port to listen for UDP packets
long unsigned int
        LocalStdTime,
        DisplayTime,
        tempEpoch,
        epoch;
long
        DOW,
        nextHack = 60 * 60, // First interval is 1 hour.  We will trust the crystal is good enough for this length.
        DriftSeconds;
bool
        SD_GOOD         = true,   // Until proven otherwise.
        File_Exists     = false,  // To know if the data file exists and, if not, write out header before first data.
        tickOccured     = false,  // Interrupt routine will set this true when it "ticks".
        GotFirstTimeSet = false;  // First time through time setting.  Stay till good NTP packet received.
int
        Prev_Hr = 99,          // Previous hour for hourshow (inverting display)
        HumBPSwitch = 0,       // Switch between Humidity and Barometric Pressure on 4th row.
        iTemp,                 // Work variable
        Time4Hack,             // Keep track of time for next time hack
        iYear,                 // Work area for year
        iHour,                 // Work area for hour
        iCurrSec;              // Set each second for testing and firing temp/hum to OLED and SD.
char
        status,
        cWork[16];             // For building strings to display.
String
        sWork, SD_FN;
double
        T, P, p0, a;
float
        Pa = 0.,       // Barometric pressure in Pascals
        tC = 0.,       // Temperature in Celcius/Centigrade
        tF = 0.,       // Temperature in Farenheit
        hum = 0.,      // Humidity from DHT22.
        bP = 0.;       // Barometric pressure at altitude

/****************************************************************************************/
void setup()
/****************************************************************************************/
{
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 64x48)
  display.clearDisplay(); yield();
  display.setTextSize(2);

  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LED_BI_ON);
  Serial.begin(115200); while (!Serial) ;
  Serial.println("\n\nNTPClient Time/Day/Date/Temp/Humidity");
  Serial.println("to D1 Mini OLED Display and SD Card.");
  Serial.println("Features: NTP Time fetch, Auto DST, Time/Temp/BP Logging."); yield();

// asdfasdf //
// For some reason, unknown at the moment, if the OLED display is used before the WiFi
// is established, it will not connect.  So, for you, this is commented out.  I wish
// it would work.  Maybe, some day...

//  display.clearDisplay(); yield();
//  display.setCursor(AcOrigin, line1S2); display.print("Start");
//  display.setCursor(AcOrigin, line2S2); display.print("DHT22 ");
//  display.display(); delayY(1000);

  Serial.println("Initializing DHT22 for temperature and humidity on D1 Mini OLED.");
  dht.begin();

//  display.clearDisplay(); yield();
//  display.setCursor(AcOrigin, line1S2); display.print("Start");
//  display.setCursor(AcOrigin, line2S2); display.print("BMP280");
//  display.display(); delayY(1000);

  //Initialize and instantiate BMP280
  if (!bmp.begin()) {
    Serial.println("Could not find a valid BMP280 sensor, check wiring!");
    while (1) yield();
  }

//  display.clearDisplay(); yield();
//  display.setCursor(AcOrigin, line1S2); display.print("Start");
//  display.setCursor(AcOrigin, line2S2); display.print("WiFi  ");
//  display.display(); delayY(1000);

  WiFi.setAutoConnect(true); yield();

  //We start by connecting to a WiFi network
  Serial.print("Connecting to "); Serial.print(ssid);
  WiFi.begin(ssid, pass);
  yield();

  iTemp = 0;
  while (WiFi.status() != WL_CONNECTED) {
    delayY(500);
    Serial.print(".");
    iTemp++;
    if ((iTemp % 15) == 0) {
      Serial.print("\nRetrying connection to "); Serial.print(ssid);
      WiFi.begin(ssid, pass);
    }
  }

  Serial.println("WiFi connected");
  Serial.print("My IP address: ");
  Serial.println(WiFi.localIP()); yield();

//  display.clearDisplay(); yield();
//  display.setCursor(AcOrigin, line1S2); display.print("Start");
//  display.setCursor(AcOrigin, line2S2); display.print("UDP  ");
//  display.display(); delayY(1000);

  Serial.print("Starting UDP... ");
  udp.begin(localPort);
  Serial.print("Local port: ");
  Serial.println(udp.localPort()); yield();

//  display.clearDisplay(); yield();
//  display.setCursor(AcOrigin, line1S2); display.print("Get IP");
//  display.setCursor(AcOrigin, line2S2); display.print("Addr  ");
//  display.display(); delayY(1000);

  Serial.print("DNS Lookup for: "); Serial.println(ntpServerName);
  WiFi.hostByName(ntpServerName, ntpServerIP); // Do the DNS shuffle.
  Serial.print("DNS Lookup returned: "); Serial.println(ntpServerIP); yield();

//  display.clearDisplay(); yield();
//  display.setCursor(AcOrigin, line1S2); display.print("Start");
//  display.setCursor(AcOrigin, line2S2); display.print("SD  ");
//  display.display(); delayY(1000);

  Serial.print("Initializing SD card...");
  //See if the SD card is present and can be initialized.
  if (!SD.begin(chipSelect)) {
    Serial.println("\nSD card not usable or not present");
    SD_GOOD = false;
  } else {
    Serial.println("card initialized."); yield();
  }

  tickOccured = false;

//  display.clearDisplay();
//  display.setCursor(AcOrigin, line1S2); display.print("Fetch");
//  display.setCursor(AcOrigin, line2S2); display.print("Time ");
//  display.display(); delayY(1000);

  GetNTPTime();
  Start_Timer(); yield();  // The one second tick timer.
//  display.clearDisplay();
//  display.setCursor(AcOrigin, line1S2); display.print("Fetch");
//  display.setCursor(AcOrigin, line2S2); display.print("Data ");
//  display.display(); delayY(1000);

  FetchNewTempBP_BMP280(); yield();  // Get temp but don't write card yet.
  // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
  digitalWrite(LED_BUILTIN, LED_BI_OFF);  // HIGH means off
}
/****************************************************************************************/
void loop()
/****************************************************************************************/
{
  if (tickOccured) {
    LocalStdTime = epoch + (ST_OFFSET_MINUTES * 60);      // Adjust UTC to Local Standard Time.
    DisplayTime = LocalStdTime + (IsDST() * DT_OFFSET_MINUTES * 60);  // If in DST, adjust the time.
    iCurrSec = second(DisplayTime);
    //    ShowTimeOnSerial();
    display.clearDisplay();
    display.drawRect(AcOrigin, DnOrigin, AcWidth, DnHeight, WHITE);
    HumBPSwitch++;  // Show the Humidity for 4 seconds then the Barometric Pressure for 4 seconds.
    ShowTimeDateOnOLED(); yield();
    ShowTempHumBPOnOLED(); yield();
    display.display();
    // Getting Temp/RH is slow and makes a slight pause in the time display.
    // Doing it after the screen update lets the time tick by smoothly.
    if (hour(DisplayTime) != Prev_Hr) {
      Prev_Hr = hour(DisplayTime);
      for (int id = 0; id < 4; id++) {
        display.invertDisplay(true); delayY(200);
        display.invertDisplay(false); delayY(200);
      }
    }
    if (iCurrSec == 30) {
      digitalWrite(LED_BUILTIN, LED_BI_ON);
      FetchNewTempBP_BMP280();
      WriteDataFile();
      digitalWrite(LED_BUILTIN, LED_BI_OFF);
    }
    tickOccured = false;
  } else {
    ESP.wdtFeed();
    delayY(0);

    if (Time4Hack > nextHack) {
      Time4Hack = 0;
      GetNTPTime();  // Update epoch, if possible.  If not, will retry in 60 ticks. Set in RequestNTP_Packet.
    }
  }
}
/****************************************************************************************/
void ShowTempHumBPOnOLED()
/****************************************************************************************/
{
  if (isnan(bP) || isnan(tF)) {
    Serial.println("Failed to read from BMP280 sensor.");
    return;
  } else {
    display.setTextSize(1);
    display.setTextColor(WHITE);

    if (tF > 99.9)
      display.setCursor(AcOrigin + 10, line3S1);
    else
      display.setCursor(AcOrigin + 14, line3S1);

    display.print(tF, 1);
    display.print((char)247);
    display.print("F");

    display.setCursor(AcOrigin + 4, line4S1);
    if (HumBPSwitch < 5) {
      display.print("BP: "); display.print(bP, 2);
    } else {
      display.print("RH: ");
      display.print(hum, 1); display.print("%");
    }
    if (HumBPSwitch == 8) HumBPSwitch = 0;
  }
}
/****************************************************************************************/
void FetchNewTempBP_BMP280()
/****************************************************************************************/
{
  tC = bmp.readTemperature();
  tF = (9.0 / 5.0) * tC + 32.0;

  Pa = bmp.readPressure();
  bP = Pa / 3386.389;

  FetchNewTempHum();
  DebugPrintInfo();
}
/****************************************************************************************/
void DebugPrintInfo()
/****************************************************************************************/
{
  Serial.print("Current Time: ");
  Serial.print(hour(DisplayTime)); Serial.print(F(":"));
  if (minute(DisplayTime) < 10) Serial.print(F("0"));
  Serial.print(minute(DisplayTime));
  //  Serial.print(F(":"));
  //  if (second(DisplayTime) < 10) Serial.print(F("0"));
  //  Serial.print(second(DisplayTime));
  Serial.print(", Temp: "); Serial.print(tF); yield();
  Serial.print(", RH: "); Serial.print(hum, 1); yield();
  Serial.print(", BP: "); Serial.println(bP); yield();
}
/****************************************************************************************/
void FetchNewTempHum()
/****************************************************************************************/
{
  hum = dht.readHumidity();
  //  tc  = dht.readTemperature();      // Celcius/Centigrade not used in this version.
  float tf1  = dht.readTemperature(true);  // Farenheit
  Serial.print("DHT22 says "); Serial.print(tf1); Serial.print(", BMP280 says "); Serial.println(tF);
  if (isnan(hum)) hum = 0;
  if (!isnan(tf1)) tF = (tF + tf1) / 2.;

}  // FetchNewTempHum
/****************************************************************************************/
void WriteDataFile()
/****************************************************************************************/
{
  if ((tF) > 0.) {
    sWork = String(year(DisplayTime)); yield();
    sWork = String(sWork += "/"); yield();
    sWork = String(sWork += month(DisplayTime)); yield();
    sWork = String(sWork += "/"); yield();
    sWork = String(sWork += day(DisplayTime)); yield();
    sWork = String(sWork += " "); yield();
    sWork = String(sWork += hour(DisplayTime)); yield();
    sWork = String(sWork += ":"); yield();
    sWork = String(sWork += minute(DisplayTime)); yield();
    sWork = String(sWork += ":"); yield();
    sWork = String(sWork += second(DisplayTime)); yield();
    sWork = String(sWork += ", "); yield();
    sWork = String(sWork += tF); yield();
    sWork = String(sWork += ", "); yield();
    sWork = String(sWork += hum); yield();
    sWork = String(sWork += ", "); yield();
    sWork = String(sWork += bP); yield();
    iYear = year(DisplayTime); // - 2000;
    sprintf(cWork, "%d%02d%02d.csv", year(DisplayTime), month(DisplayTime), day(DisplayTime));
    //Open file named for today's date.  That way, the largest file will be 1440 lines. Auto changeover!
    if (SD_GOOD) {  // Don't waste time unless we can actually write to the SD card.
      File_Exists = SD.exists(cWork);  // True means file is there.  False means we need a header in new file.
      File dataFile = SD.open(cWork, FILE_WRITE);
      if (dataFile) {
        if (!File_Exists) dataFile.write("Date, Temp, RH, BP\n");
        dataFile.println(sWork);
        //Serial.println(dataFile.size());
        dataFile.close();
      } else {
        Serial.print("Error opening "); Serial.println(SD_FN);
        ShowTimeOnSerial();
      }  // if (datafile)
    }  // if (SD_GOOD)
  }  // if ((tf + hum) > 0.)
}  // WriteDataFile
/***************************************************************************/
void ShowTimeDateOnOLED()
/***************************************************************************/
{
  display.setTextSize(1);  // Text is 1 row high
  display.setTextColor(WHITE);
  iHour = hour(DisplayTime);
  //  if (iHour > 12) iHour -= 12; if (iHour == 0) iHour = 12;
  if (iHour < 10) {
    display.setCursor(AcOrigin + 11, line1S1);
  } else {
    display.setCursor(AcOrigin + 8, line1S1);
  }
  display.print(iHour); display.print(F(":"));
  if (minute(DisplayTime) < 10) display.print(F("0"));
  display.print(minute(DisplayTime)); display.print(F(":"));
  if (second(DisplayTime) < 10) display.print(F("0"));
  display.println(second(DisplayTime));

  if (HumBPSwitch < 5) {
    display.setCursor(AcOrigin + 2, line2S1);
    if (month(DisplayTime) < 10) display.print(F("0"));
    display.print(month(DisplayTime)); display.print(F("/"));
    if (day(DisplayTime) < 10) display.print(F("0"));
    display.print(day(DisplayTime)); display.print(F("/"));
    display.print(year(DisplayTime));
  } else {
    display.setCursor(AcOrigin, line2S1); // Default positioning for DOW.
    DOW = DayOfWeek(year(DisplayTime), month(DisplayTime), day(DisplayTime));
    switch (DOW) {
      case 0: display.print("  Sunday  "); break; // Slow
      case 1: display.print("  Monday  "); break; // Manic
      case 2: display.print("  Tuesday "); break; // Tense
      case 3: display.setCursor(AcOrigin + 5, line2S1); display.print("Wednesday"); break; // Wonderful
      case 4: display.setCursor(AcOrigin + 8, line2S1); display.print("Thursday"); break; // Tired
      case 5: display.print("  Friday  "); break; // Frantic
      case 6: display.setCursor(AcOrigin + 8, line2S1); display.print("Saturday"); break; // Satisfying
    }
  }
}
/****************************************************************************************/
void ShowTimeOnSerial()
/****************************************************************************************/
{
  //Print the hour, minute and second to Serial.
  Serial.print("UTC: "); Serial.print(year(epoch)); Serial.print("/");  yield();
  if (month(epoch) < 10) Serial.print("0"); Serial.print(month(epoch)); Serial.print("/");  yield();
  if (day(epoch) < 10) Serial.print("0"); Serial.print(day(epoch)); Serial.print(" "); yield();
  if ((epoch  % 86400L) / 3600 < 10) Serial.print("0"); yield();
  Serial.print((epoch  % 86400L) / 3600);  Serial.print(':'); // print the hour (86400 equals secs per day)
  if (((epoch % 3600) / 60) < 10 ) Serial.print('0'); Serial.print((epoch  % 3600) / 60); Serial.print(':');
  if ((epoch % 60) < 10 ) Serial.print('0'); Serial.print(epoch % 60); // print the second
  yield();
  //Now, local time
  //DisplayTime is the local time adjusted for timezone and DST.
  Serial.print(", Local: "); Serial.print(year(DisplayTime)); Serial.print("/");
  if (month(DisplayTime)  < 10) Serial.print("0"); Serial.print(month(DisplayTime)); Serial.print("/");
  if (day(DisplayTime)    < 10) Serial.print("0"); Serial.print(day(DisplayTime)); Serial.print(" ");
  if (hour(DisplayTime)   < 10) Serial.print("0"); Serial.print(hour(DisplayTime)); Serial.print(":");
  if (minute(DisplayTime) < 10) Serial.print("0"); Serial.print(minute(DisplayTime)); Serial.print(":");
  if (second(DisplayTime) < 10) Serial.print("0"); Serial.println(second(DisplayTime));
}
/****************************************************************************************/
void delayY(long int dTime)
/****************************************************************************************/
{
  for (int i = 0; i < dTime; i++) {
#ifdef ESP8266
    yield();  // for ESP8266 only
#endif
    delay(1);
  }
}
/***************************************************************************/
void GetNTPTime() {
  /***************************************************************************/
  digitalWrite(LED_BUILTIN, LED_BI_ON);
  if (!RequestNTP_Packet()) return;  // No packet received.  Keep using previous computed time.
  //We've received a packet, read the data from it
  delayY(10);
  udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer

  //The timestamp starts at byte 40 of the received packet and is four bytes, or two words, long.
  //First, extract the two words.
  unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
  //  Serial.println(highWord);
  unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
  //  Serial.println(lowWord);
  //Now, combine the four bytes (two words) into a long integer.
  //This is NTP time (seconds since Jan 1 1900).
  unsigned long secsSince1900 = highWord << 16 | lowWord;
  Serial.print("Seconds since Jan 1 1900 = " );
  Serial.println(secsSince1900); yield();
  //Now convert NTP time into Unix epoch.
  //Unix time starts on Jan 1 1970. In seconds, that's 2208988800.
  tempEpoch = secsSince1900 - seventyYears;  // Subtract seventy years.
  DriftSeconds = abs(tempEpoch - epoch);
  //  Serial.print("tempEpoch: "); Serial.println(tempEpoch);
  if (epoch > 0) {  // First time bypass for the following lines.
    Serial.print("Drift seconds: "); Serial.print(DriftSeconds);
    Serial.print(", "); yield();
    if (DriftSeconds > DriftThreshold) {  // There is the old +/- 1 count problem with digital...
      nextHack -= RetardHackAmt;
      if (nextHack < MIN_HACK) nextHack = MIN_HACK;
    } else {
      nextHack += AdvanceHackAmt;
      if (nextHack > MAX_HACK) nextHack = MAX_HACK;
    }
  }
  //  Serial.print(Time4Hack); Serial.print(" "); Serial.println(nextHack); yield();
  Serial.print("Next time-to-hack: "); Serial.print(nextHack);
  Serial.println(" seconds."); yield();
  epoch = tempEpoch;
  Serial.print("At end of GetNTPTime, epoch is "); Serial.println(epoch); yield();
  LocalStdTime = epoch + (ST_OFFSET_MINUTES * 60);      // Adjust UTC to Local Standard Time.
  DisplayTime = LocalStdTime + (IsDST() * DT_OFFSET_MINUTES * 60);  // If in DST, adjust the time.
  ShowTimeOnSerial();
  ShowFutureHack(nextHack + DisplayTime);
  digitalWrite(LED_BUILTIN, LED_BI_OFF);
} // End of GetNTPTime
/***************************************************************************/
void ShowFutureHack(unsigned long theTime) {
  /***************************************************************************/
  //DisplayTime is the local time adjusted for timezone and DST.
  Serial.print("Next Time Hack at (Local) ");  Serial.print(year(DisplayTime)); Serial.print("/"); yield();
  if (month(theTime)  < 10) Serial.print("0"); Serial.print(month(theTime));    Serial.print("/"); yield();
  if (day(theTime)    < 10) Serial.print("0"); Serial.print(day(theTime));      Serial.print(" "); yield();
  if (hour(theTime)   < 10) Serial.print("0"); Serial.print(hour(theTime));     Serial.print(":"); yield();
  if (minute(theTime) < 10) Serial.print("0"); Serial.print(minute(theTime));   Serial.print(":"); yield();
  if (second(theTime) < 10) Serial.print("0"); Serial.println(second(theTime));
}
/***************************************************************************/
int DayOfWeek(int y, byte m, byte d) { /* 1 <= m <= 12,  y > 1752 (in the U.K.) */
  /***************************************************************************/
  static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};

  y -= m < 3;
  return (y + y / 4 - y / 100 + y / 400 + t[m - 1] + d) % 7;
} // End of DayOfWeek
/***************************************************************************/
bool IsDST() {
  /***************************************************************************/
  //This routine is based on the assumption that DOW of 1 = Sunday.  This is the way Time.h/Time.cpp
  //does it so I must honor that convention here since I call several routines in Time.cpp.
  /*
      In the U.S., clocks change at 2:00 a.m. local time.
       In spring, clocks spring forward from 1:59 a.m. to 3:00 a.m..
       In fall, clocks fall back from 1:59 a.m. to 1:00 a.m.
  */
  byte Sunday1, Sunday2;
  int iHour  = hour(LocalStdTime);      // hour of the day (0-23)
  int iMonth = month(LocalStdTime);
  int iToday = day(LocalStdTime);       // date of the month (1-31)

  if (iMonth < 3 || iMonth > 11) return false; //January, February, and December are out.  Can't be DST
  if (iMonth > 3 && iMonth < 11) return true;  //April to October are all in.  Must be DST

  DOW = DayOfWeek(year(LocalStdTime), iMonth, 1); // Get DOW of first of the current month

  if (DOW == 0)
    Sunday1 = 1;
  else
    Sunday1 = 8 - DOW;
  Sunday2 = Sunday1 + 7;

  //Now split the two affected months and see where we are.

  //In March, we are DST if our previous Sunday was on or after the 8th and it's after 2 am.
  if (iMonth == 3) {
    if (iToday < Sunday2) return false; // Before second Sunday
    if (iToday > Sunday2) return true;  // Past second Sunday
    //Otherwise, we are on a 2nd Sunday (6 < Indicator < 14 and iDOW == 1)
    if (iHour > 1)  // Starting at 2 am, DST in effect.
      return true;
    else
      return false;
  }
  //In November we must be before the first sunday to be DST.
  //That means the previous Sunday must be before the 1st.
  if (iToday < Sunday1) return true;    // Before first Sunday
  if (iToday > Sunday1) return false;   // Past first Sunday

  if (iHour < 2) // Before 2 am, still DST.  Afterwards, nope.
    return true;
  else
    return false;
} // End of IsDST
/***************************************************************************/
unsigned long sendNTPpacket(IPAddress & address) {
  /***************************************************************************/
  // send an NTP request to the time server at the given address
  //Set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  //Initialize values needed to form NTP request (see URL above for details on the packets)
  packetBuffer[0]  = 0b11100011;   // LI, Version, Mode
  packetBuffer[1]  = 0;     // Stratum, or type of clock
  packetBuffer[2]  = 6;     // Polling Interval
  packetBuffer[3]  = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12] = 49;
  packetBuffer[13] = 0x4E;
  packetBuffer[14] = 49;
  packetBuffer[15] = 52;
  //All NTP fields have been given values, now you can send a packet requesting a timestamp:
  Serial.println("---------------------------------------------"); yield();
  Serial.print("Sending NTP Request Packet to "); Serial.println(ntpServerIP);
  udp.beginPacket(address, 123); //NTP requests are to port 123
  udp.write(packetBuffer, NTP_PACKET_SIZE);
  udp.endPacket();
} // End of sendNTPpacket
///***************************************************************************/
//int whichSwitch() {
///***************************************************************************/
//  int a = analogRead(A0);
//  Serial.print("Read "); Serial.println(a); yield();
//  if (a < SWITCH_JITTER)
//    return(LOCAL_TIME);
//  else if (a > (switches[GMT_TIME] - SWITCH_JITTER) && (a < (switches[GMT_TIME] + SWITCH_JITTER)))
//    return(GMT_TIME);
//  else if (a > (switches[NEXT_HACK] - SWITCH_JITTER) && (a < (switches[NEXT_HACK] + SWITCH_JITTER)))
//    return(NEXT_HACK);
//  else if (a > (switches[HIGH_POT] - SWITCH_JITTER))
//    return(HIGH_POT);
//  else return(UNDEF);
//}
/***************************************************************************/
bool RequestNTP_Packet()
/***************************************************************************/
{
  int cb = true;
  unsigned long myMilli;

  /* I noticed a problem where a packet was declared missing but, apparently, was actually there when
      another one was requested.  Then, the first one was read and used.  Then, on the next read cycle,
      that old (possibly very old) packet was read and it threw the clock off forever. So this is an
      attempt to clear any incoming packets out of the pipe before requesting a new one.  I hope it works...
  */
  while (cb) {
    cb = udp.parsePacket();
    if (cb) udp.read(packetBuffer, NTP_PACKET_SIZE);
  }

  cb = false;
  while (!cb) {
    sendNTPpacket(ntpServerIP);   // send an NTP packet to a time server
    myMilli = millis();
    while (!cb) {  // Wait for packet from NTP Server.
      delayY(1);  // "delay" 1 ms and "Y"ield then check to find if the time is back from the server.
      cb = udp.parsePacket();
      if ((unsigned long)(millis() - myMilli) > 1000) {  // Wait 1000 ms for response then give up for this time around.
        if (GotFirstTimeSet) {
          Serial.print("No NTP response.\nWill retry in "); yield(); Serial.print(ReCheckDelay); yield();
          Serial.println(" seconds.");
          ShowTimeOnSerial();
          Time4Hack = nextHack - ReCheckDelay;  // This will try to re-fetch the time in "ReCheckDelay"+1 seconds.
          return (false);
        } else {
          Serial.println("Initial NTP Response not received.\nTrying again in 5 seconds...");
          delayY(5000);
          Serial.println("Re-requesting time from from NTP Server."); yield();
          sendNTPpacket(ntpServerIP); // send an NTP packet to a time server
          myMilli = millis();
          //          Serial.print("Starting wait time at: (millis()) "); Serial.println(myMilli);
        }
      }
    }  // End while (!cb) (second one)
  }  // End while (!cb) (first one)
  GotFirstTimeSet = true;
  Serial.print("NTP Packet roundtrip time: "); Serial.print(millis() - myMilli);     
  Serial.println(" ms"); yield();
  Serial.print("Received packet length: "); Serial.print(cb); Serial.println(" bytes.");
  return (true);
}
#ifdef ESP8266
/***************************************************************************/
void Start_Timer(void) {  // See documetation in the expanded, original version, below.
  /***************************************************************************/
  os_timer_setfn(&myTimer, Increment_Timer, NULL);
  os_timer_arm(&myTimer, 1000, true);
} // End of Start_Timer
/***************************************************************************/
void Increment_Timer(void *pArg) {   // start of Increment_Timer
  /***************************************************************************/
  //  Serial.println("Tick");
  tickOccured = true;
  epoch++;
  Time4Hack++;
} // End of Increment_Timer

// There needs to be an else here with the __AVR__ code in it using its native timer.

/***************************************************************************/
//void user_init(void) { // Documentation on the timer Initialization code, seen above.
/***************************************************************************/
/*
  os_timer_setfn - Define a function to be called when the timer fires

  void os_timer_setfn(
     os_timer_t *pTimer,
     os_timer_func_t *pFunction,
     void *pArg)

  Define the callback function that will be called when the timer reaches zero.
  The pTimer parameters is a pointer to the timer control structure.

  The pFunction parameters is a pointer to the callback function.

  The pArg parameter is a value that will be passed into the called back function.
  The callback function should have the signature:
  void (*functionName)(void *pArg)

  The pArg parameter is the value registered with the callback function.
*/

//      os_timer_setfn(&amp;myTimer, timerCallback, NULL);

/*
      os_timer_arm -  Enable a millisecond granularity timer.

  void os_timer_arm(
      os_timer_t *pTimer,
      uint32_t milliseconds,
      bool repeat)

  Arm a timer such that is starts ticking and fires when the clock reaches zero.

  The pTimer parameter is a pointed to a timer control structure.
  The milliseconds parameter is the duration of the timer measured in milliseconds.
  The repeat parameter is whether or not the timer will restart once it has reached zero.

*/

//      os_timer_arm(&amp;myTimer, 1000, true);
//} // End of user_init
#endif