Oled and Time

https://www.geekstips.com/arduino-time-sync-ntp-server-esp8266-udp/


/*
  OLED code modified from esp8266-oled-ssd1306 demo
  The MIT License (MIT)
  Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
  Copyright (c) 2018 by Fabrice Weinberg
   See complete copyright notice at the bottom of the file

  Modified from the demo program by the above authors by Deid Reimer (Jan 2019) to:
    - Simplify to the specific application
    - Add wifi and web server
    - Add temperature and voltage
    - Add Murphy saying
    - Add Time
  ===========================

  NTP time code modified from
  - www.geekstips.com for Arduino Time Sync
    from NTP Server using ESP8266 WiFi module
*/

//  Include wifi and web server.
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266WebServer.h>

// Get time requirements
#include <WiFiUdp.h>
#include <time.h>
#include <Ticker.h>

// local port to listen for UDP packets
unsigned int localPort = 2390;

IPAddress timeServerIP;
const char* ntpServerName = "0.ca.pool.ntp.org";

// NTP time stamp is in the first 48 bytes of the message
const int NTP_PACKET_SIZE = 48;

//buffer to hold incoming and outgoing NTP packets
byte packetBuffer[ NTP_PACKET_SIZE];

// Holds returned and calculated Unix time.
time_t unixTime;
time_t unixTimeNTP;

// Tick and ntp times in seconds and create the Ticker attach objects.
int tickTime = 1;
int ntpTime = 300;
Ticker tick;
Ticker ntp;
// Set true by the ntp Ticker (it was not happy getting the time in a timer interrupt routine - duh?)
bool timeToGetNTPtime = false;

// Used in loop() {} to count up the  display period
unsigned long lastmillis = 0;

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

// Include libraries for the DS18B20 temperature sensor
#include <OneWire.h>
#include <DallasTemperature.h>

// Include the OLED display library
#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"`
// Include custom images
#include "images.h"

// Set up to read battery voltage on ADC.
ADC_MODE(ADC_VCC);

// Temperature ata wire is connected to D6/GPIO 12
#define ONE_WIRE_BUS D6

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// and pass our oneWire instance reference to the Dallas Temperature routine
DallasTemperature DS18B20(&oneWire);

// Create variables to hold the temperature and voltage measurements
char temperatureCString[8];
char voltageCString[8];

// Create a variable type to hold the display function addresses
typedef void (*Demo)(void);

//  Delay time.  This will be changed by each display function.
int delayNow = 5;

// Which of the n display functions is current
int demoMode = 0;

// Text from the web form
String inputText;

//  Which of the several display lists choose by >>... is current
int displayType = 2;

// Instantiate and tell the web server to listen on port 80
ESP8266WebServer server(80);

// Initialize the OLED display using Wire library
// 0x3C is the display address
// D3 is SDA - data line
// D5 is SCL - clock line
SSD1306Wire  display(0x3c, D3, D5);
// -----------------------------------------


// Functions
// Wifi web and time functions

// Convert IPV4 IP to String for display on the OLED
String ipToString(IPAddress ip) {
  String ips = String(ip[0]) + "." + String(ip[1]) + "." + String(ip[2]) + "." + String(ip[3]);
  return ips;
}

// Function to request WiFi IP address and show the process.
bool getIP(IPAddress ip, IPAddress gateway, IPAddress subnet, char* ssid, char* password){

  display.setTextAlignment(TEXT_ALIGN_LEFT);
  display.setFont(ArialMT_Plain_16);

  WiFi.disconnect();
  WiFi.config(ip, gateway, subnet);
  WiFi.begin(ssid, password);

  // Loop until a connection or 20 tries
  int j = 0;
  while (WiFi.status() != WL_CONNECTED and j < 20) {
    delay(500);
    Serial.print(j);
    Serial.println(ssid);
    display.clear();
    display.drawString(0, 0, String(j));
    display.drawString(0, 20, ssid);
    display.display();
    j++;
  }

  // Display the connection information if connected and return status
  if (WiFi.status() == WL_CONNECTED) {
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
    display.clear();
    display.drawString(0, 0, "IP address: ");
    display.drawString(0, 15, ipToString(WiFi.localIP()));

    Serial.print("Connected to ");
    Serial.println(ssid);

    display.drawString(0, 30, "Connected to");
    display.drawString(0, 45, ssid);

    display.display();
    delay(10000);
    return true;
  } else {
    return false;
  }
}

// Function to build amd display the web page.
void showPage() {
  String webPage00 = "<html><head><meta charset=\"UTF-8\"><meta name=viewport content=\"width=device-width, initial-scale=1.5\"><title>OLDisplay</title>";
  String webPage000 = "<meta name = \"viewport\" content = \"width = device-width, initial-scale = 1.0, maximum-scale = 1.0, user-scalable=0\">";
  String webPage01 = "</head><body>";
  String webPage0 = "<h3>Message for OLED ...</h3>";
  String webPage02 = "<FORM METHOD=\"get\" action=\"/getForm\"> <input type=\"text\" name=\"getText\">";
  String webPage03 = "<input type=\"submit\" value=\"Submit\"></form>" ;
  String webPage99 = "</body></html>";

  String webPage =  webPage00 + webPage000 + webPage01 + webPage0 + webPage02 + webPage03 + webPage99;
  server.send(200, "text/html", webPage);
  Serial.println("sent new page");
  delay(2000);
}

// Function to get the data from the web form
void handleForm() {
  showPage();
  String formText  = server.arg("getText");
  if (formText.length() >= 150) {
    formText = formText.substring(0, 149);
  }
  // Check for short/long etc. command, or just new text.
  if (formText == ">>Short") {
    displayType = 1;
    demoMode = 0;
  } else if (formText == ">>Long") {
    displayType = 2;
    demoMode = 0;
  } else if (formText == ">>Rand") {
    displayType = 3;
    demoMode = 0;
  } else if (formText == ">>Time") {
    displayType = 4;
  } else if (formText == ">>Mods") {
    displayType = 5;
    demoMode = 0;
  } else {
    inputText = formText;
    demoMode = 0;
  }
  Serial.println("Text received. Contents: ");
  Serial.println(formText);
  Serial.println(inputText);
  //delay(2);
}


// Function to get the current Unix time from an NTP server
void getNTPTime() {
  //get a random server from the pool
  WiFi.hostByName(ntpServerName, timeServerIP);

  // send an NTP packet to a time server
  sendNTPpacket(timeServerIP);
  // wait to see if a reply is available
  delay(1000);

  int cb = udp.parsePacket();
  if (!cb) {
    Serial.println("no packet yet");
  } else {
    Serial.println("Time packet received.");
    // We've received a packet, read the data from it into the buffer
    udp.read(packetBuffer, NTP_PACKET_SIZE);

    // The timestamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. Extract the two words:
    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);

    // Combine the four bytes (two words) into a long integer
    // this is NTP time (seconds since Jan 1 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord;

    // Convert NTP time into a Unix timestamp:
    Serial.print("NTP time = ");

    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
    const unsigned long seventyYears = 2208988800UL;
    // subtract seventy years and save as Unix time. Then convert to PST
    unixTime = time_t(secsSince1900 - seventyYears);
    unixTime = unixTime - 3600 * 8;
    unixTimeNTP = unixTime;
    Serial.println(displayUnixTime(unixTime));
   
  }
}

// Function to send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress & address) {
  Serial.println("sending NTP packet to: ");
  Serial.println(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
  // [4] - [11] are 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 to port 123 requesting a timestamp:
  udp.beginPacket(address, 123);
  udp.write(packetBuffer, NTP_PACKET_SIZE);
  udp.endPacket();
}

// Function to return a formatted date and time String.
  String displayUnixTime(time_t t) {
  struct tm *mytime;
  mytime = localtime(&t);
  int year = mytime->tm_year + 1900;
  int month = mytime->tm_mon + 1;
  char stringBuf[20];
  sprintf(stringBuf, "%4d/%02d/%02d\n%02d:%02d:%02d", year, month, mytime->tm_mday, mytime->tm_hour, mytime->tm_min, mytime->tm_sec);
  return stringBuf;
}

// Ticker interrupt routine to count seconds
void countSeconds() {
  unixTime++;
  Serial.println (displayUnixTime(unixTime));
}

// Ticker interrupt routine to set request to get NTP time.
void setGetNTPTime() {
  timeToGetNTPtime = true;
}



// ------------------------------------------------------------
// Oled display functions

// Set up the display function lists
Demo longDemos[] = {drawImageDemo, drawImageDemo1, drawTempVolt, drawTimeDemo, drawFontFaceDemo, drawTextFlowDemo, drawMurphy, drawTextAlignmentDemo, drawRectDemo, drawCircleDemo, drawProgressBarDemo};
Demo shortDemos[] = {drawTempVolt, drawTextFlowDemo};
Demo randDemos[] = {drawTempVolt, drawMurphy};
Demo timeDemos[] = {drawImageDemo, drawTempVolt, drawTimeDemo};
Demo modsDemos[] = {drawImageDemo1, drawTempVolt, drawMurphy, drawTextFlowDemo, drawTimeDemo};
int longDemoLength = (sizeof(longDemos) / sizeof(Demo));
int shortDemoLength = (sizeof(shortDemos) / sizeof(Demo));
int randDemoLength = (sizeof(randDemos) / sizeof(Demo));
int timeDemoLength = (sizeof(timeDemos) / sizeof(Demo));
int modsDemoLength = (sizeof(modsDemos) / sizeof(Demo));

//Get and display a murphy saying
void drawMurphy() {
  HTTPClient http;
  http.begin("http://drsol.com/~deid/murphy/murphy.php");
  int stat = http.GET();
  if (stat > 0) {
    String text = http.getString();
    delayNow = 10;
    display.setFont(ArialMT_Plain_10);
    display.setTextAlignment(TEXT_ALIGN_LEFT);
    display.drawStringMaxWidth(0, 0, 128, text);
  } else {
    display.setFont(ArialMT_Plain_16);
    display.setTextAlignment(TEXT_ALIGN_LEFT);
    display.drawStringMaxWidth(0, 0, 128, "Oops - No Web!");
  }
  http.end();
}

// Display temperature and voltage.
void drawTempVolt() {
  delayNow = 3;
  float tempC;
  int battVolt;
  //float tempF;

  // Loop until the temperature is not out of range
  do {
    DS18B20.requestTemperatures();
    tempC = DS18B20.getTempCByIndex(0);

    delay(1000);
  } while (tempC == (-127.0));
  tempC = tempC + 1;
  dtostrf(tempC, 6, 2, temperatureCString);
  //  tempF = DS18B20.getTempFByIndex(0);
  //  dtostrf(tempF, 6, 2, temperatureFString)
  String t = temperatureCString;

  display.setTextAlignment(TEXT_ALIGN_LEFT);
  display.setFont(ArialMT_Plain_16);
  display.drawString(0, 0, t + " C");

  int voltage = analogRead(A0);
  float v = voltage / 65536.0 * 3.3;
  display.drawString(16, 18, String(v) + " V");
}

// Display different font sizes
void drawFontFaceDemo() {
  delayNow = 5;
  // create more fonts at http://oleddisplay.squix.ch/
  display.setTextAlignment(TEXT_ALIGN_LEFT);
  display.setFont(ArialMT_Plain_10);
  display.drawString(0, 0, "Hello PiMakers");
  display.setFont(ArialMT_Plain_16);
  display.drawString(0, 10, "Hello World");
  display.setFont(ArialMT_Plain_24);
  display.drawString(0, 26, "Nice Day!");
}

// Display the text retrieved from the "web"
void drawTextFlowDemo() {
  delayNow = 9;
  display.setFont(ArialMT_Plain_10);
  display.setTextAlignment(TEXT_ALIGN_LEFT);
  display.drawStringMaxWidth(0, 0, 128, inputText);
}

// Display text in different horizontal alignments
void drawTextAlignmentDemo() {
  delayNow = 5;
  // Text alignment demo
  display.setFont(ArialMT_Plain_10);

  // The coordinates define the left starting point of the text
  display.setTextAlignment(TEXT_ALIGN_LEFT);
  display.drawString(0, 10, "Left aligned (0,10)");

  // The coordinates define the center of the text
  display.setTextAlignment(TEXT_ALIGN_CENTER);
  display.drawString(64, 22, "Center aligned (64,22)");

  // The coordinates define the right end of the text
  display.setTextAlignment(TEXT_ALIGN_RIGHT);
  display.drawString(128, 33, "Right aligned (128,33)");
}

// Display drawing on the screen - a rectangle.
void drawRectDemo() {
  delayNow = 5;
  // Draw a pixel at given position
  for (int i = 0; i < 10; i++) {
    display.setPixel(i, i);
    display.setPixel(10 - i, i);
  }
  display.drawRect(12, 12, 20, 20);

  // Fill the rectangle
  display.fillRect(14, 14, 17, 17);

  // Draw a line horizontally
  display.drawHorizontalLine(0, 40, 20);

  // Draw a line vertically
  display.drawVerticalLine(40, 0, 20);
}

// Display a couple of circles.
void drawCircleDemo() {
  delayNow = 5;
  for (int i = 1; i < 8; i++) {
    display.setColor(WHITE);
    display.drawCircle(32, 32, i * 3);
    if (i % 2 == 0) {
      display.setColor(BLACK);
    }
    display.fillCircle(96, 32, 32 - i * 3);
  }
}

// Display a progress bar
void drawProgressBarDemo() {
  int counter = 1;
  int progress;
  delayNow = 0;
  while (counter < 100) {
    display.clear();
    progress = (counter) % 100;
    // draw the progress bar
    display.drawProgressBar(0, 32, 120, 10, progress);
    // draw the percentage as String
    display.setTextAlignment(TEXT_ALIGN_CENTER);
    display.drawString(64, 15, String(progress) + "%");
    display.display();
    // Count for the progress bar
    counter++;
    delay(60);
  }
}

// Display images.
// Raspberry
void drawImageDemo() {
  delayNow = 3;
  // see http://blog.squix.org/2015/05/esp8266-nodemcu-how-to-create-xbm.html
  // on how to create xbm files
  // display.drawXbm(34, 14, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits);
  display.drawXbm(17, 0, noobs_width, noobs_height, noobs_bits);
}

// Peanut
void drawImageDemo1() {
  delayNow = 3;
  display.drawXbm(20, 0, peanut_width, peanut_height, peanut_bits);
}


// Display the date and time
void drawTimeDemo() {
  //display.clear();
  delayNow = 3;
  display.setFont(ArialMT_Plain_16);
  display.setTextAlignment(TEXT_ALIGN_LEFT);
  // Display the current time
  display.drawStringMaxWidth(0, 0, 128, displayUnixTime(unixTime));
  display.setFont(ArialMT_Plain_10);
  // Display the time of the last NTP request in smaller text at bottom.
  display.drawStringMaxWidth(0, 52, 128, displayUnixTime(unixTimeNTP));
  Serial.println(displayUnixTime(unixTime));
}




// -----------------------------------------------------------
// Setup
void setup() {
  // Reserve space for the web form input variable
  inputText.reserve(150);
  inputText = "Computer science is no more about computers than astronomy is about telescopes.";

  // Set up serial interface
  Serial.begin(9600);

  // Initialise the UI, this will init the display too.
  display.init();
  display.flipScreenVertically();
  display.setFont(ArialMT_Plain_10);
  delay(500);

  //  Connect to WiFi.   Try each Access Point below and give up if none.
  WiFi.mode(WIFI_STA);
  bool stat = false;
  //bool stat = getIP ({0,0,0,0},{0,0,0,0},{0,0,0,0}, "Steele", "Piwehavelotsof.");
  stat = getIP ({10, 10, 45, 10}, {10, 10, 45, 254}, {255, 255, 255, 0}, "Steele", "Bazbarfoo");
  if (!stat) {
    Serial.println("try makiki");
    stat = getIP ({0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, "Makiki", "foobarbaz");
  } if (!stat) {
    Serial.println("try lalala6");
    stat = getIP ({192, 168, 78, 39}, {192, 168, 78, 1}, {255, 255, 255, 0}, "lalala6", "fibblefabblefoo");
  } if (!stat) {
    Serial.println("try lalala");
    stat = getIP ({192, 168, 0, 39}, {192, 168, 0, 1}, {255, 255, 255, 0}, "lalala", "foofabblefibble");
  }

  if (!stat) {
    // Continue without an an internet connection.
    Serial.println("Continuing without IP");
    display.clear();
    display.drawString(0, 10, "No IP");
    display.display();
    delay(5000);
  }

  // Turn of the AP
  WiFi.softAPdisconnect (true);

  // Start the web server.
  server.begin();

  // Set up the responder for web requests.
  server.on("/getForm", handleForm);
  server.on("/", showPage);
  server.onNotFound(showPage);

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

  // Get the initial time.
  getNTPTime();
  Serial.println(unixTime);
  Serial.println(displayUnixTime(unixTime));

  // Start the timer interrupt routines
  tick.attach(tickTime, countSeconds);
  ntp.attach(ntpTime, setGetNTPTime);
}


//-----------------------------------
// Loop
void loop() {


  // Get time from an NTP server if it is time to do that.
  if (timeToGetNTPtime) {
    getNTPTime();
    timeToGetNTPtime = false;
  }


  //  Create a display and show it it it is time to do that.
  if (millis() > (delayNow * 1000) + lastmillis) {
    lastmillis = millis();
    display.clear();

    // Choose which set of displays to run one of.
    if ( displayType == 1) {
      shortDemos[demoMode]();
      demoMode = (demoMode + 1)  % shortDemoLength;
    } else if (displayType == 2) {
      longDemos[demoMode]();
      demoMode = (demoMode + 1)  % longDemoLength;
    } else if (displayType == 3) {
      randDemos[demoMode]();
      demoMode = (demoMode + 1) % randDemoLength;
    } else if (displayType == 4) {
      timeDemos[demoMode]();
      demoMode = (demoMode + 1) % timeDemoLength;
    } else if (displayType == 5) {
      modsDemos[demoMode]();
      demoMode = (demoMode + 1) % modsDemoLength;
    }
    display.setTextAlignment(TEXT_ALIGN_RIGHT);
    display.display();

    // Look for and handle a web request.
    server.handleClient();
  }
  // Just in case there is someting else to do - like watchdog timer.
  yield();
}

/**
   The MIT License (MIT)

   Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
   Copyright (c) 2018 by Fabrice Weinberg

   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated documentation files (the "Software"), to deal
   in the Software without restriction, including without limitation the rights
   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   copies of the Software, and to permit persons to whom the Software is
   furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in all
   copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER 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.

   ThingPulse invests considerable time and money to develop these open source libraries.
   Please support us by buying our products (and not the clones) from
   https://thingpulse.com

*/