ESP8266 Internet Clock With Weather Update & Many More (No RTC)

9,936

166

24

Introduction: ESP8266 Internet Clock With Weather Update & Many More (No RTC)

About: I like to learn, like to make, like to share.

In this instructable, I am going to share how I made an ESP8266 based hackable Internat Clock. The clock is able to show:

  • Time (Using NTP),
  • Date,
  • Weather updates (e.g. temperature, humidity, pressure, clouds, sunrise, sunset),
  • Weather forecast,
  • Facebook like and notification,
  • Youtube subscriber counter
  • Customize message and many more

The firmware of the clock can be updated over wifi. No physical connection is required. Using the Network Time Protocol (NTP) for getting time. So, no RTC module or Arduino is required.

Step 1: Bill of Materials

1. 1 X ESP8266-12E Module (Gearbest.com)

2. 2 X MAX7219 Dot Matrix Module 4 in One Display (Gearbest.com)

3. 1 X AMS1117 Power Supply Module (Gearbest.com)

4. 1 X DC 12V 1A Power Supply (Gearbest.com)

5. 1 X LM7805 Voltage regulator and jumper wires

Tools Require

1. Desktop 3D Printer: Buy one from Gearbest.com for only $129

2. Soldering Station (Gearbest.com)

3. Flexible Helping Arm for Soldering (optional) (Gearbest.com)

4. Lead-Free Solder Soldering Wire Reel (Gearbest.com)

5. Wire Cutters Clipper Diagonal Plier (Gearbest.com)

Step 2: Network Time Protocol

In a normal Arduino project, you would have to get a RTC module, set the right time, sacrifice some Arduino pins for communication. And when the RTC battery runs out, you have to replace it.

On the ESP8266, all you need is an Internet connection: you can just ask a time server what time it is. To do this, the Network Time Protocol (NTP) is used. NTP is based on UDP. There are a couple of differences, but it's really easy to use, thanks to the great libraries that come with the ESP8266 Arduino Core. The main difference between TCP and UDP is that TCP needs a connection to send messages:

First a handshake is sent by the client, the server responds, and a connection is established, and the client can send its messages. After the client has received the response of the server, the connection is closed (except when using WebSockets). To send a new message, the client has to open a new connection to the server first. This introduces latency and overhead. UDP doesn't use a connection, a client can just send a message to the server directly, and the server can just send a response message back to the client when it has finished processing. There is, however, no guarantee that the messages will arrive at their destination, and there's no way to know whether they arrived or not (without sending an acknowledgement, of course). This means that we can't halt the program to wait for a response, because the request or response packet could have been lost on the Internet, and the ESP8266 will enter an infinite loop. Instead of waiting for a response, we just send multiple requests, with a fixed interval between two requests, and just regularly check if a response has been received.

Getting the time
Let's take a look at an example that uses UDP to request the time from a NTP server. Libraries, constants and globals

#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WiFiUdp.h>

ESP8266WiFiMulti wifiMulti;      // Create an instance of the ESP8266WiFiMulti class, called 'wifiMulti'

WiFiUDP UDP;                     // Create an instance of the WiFiUDP class to send and receive

IPAddress timeServerIP;          // time.nist.gov NTP server address
const char* NTPServerName = "time.nist.gov";
const int NTP_PACKET_SIZE = 48;  // NTP time stamp is in the first 48 bytes of the message

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

To use UDP, we have to include the WiFiUdp library,

and create a UDP object. We'll also need to allocate memory for a buffer to store the UDP packets. For NTP, we need a buffer of 48 bytes long. To know where to send the UDP packets to, we need the hostname of the NTP server, this is time.nist.gov.

void setup() {
  Serial.begin(115200);          // Start the Serial communication to send messages to the computer
  delay(10);
  Serial.println("\r\n");

  startWiFi();                   // Try to connect to some given access points. Then wait for a connection

  startUDP();

  if(!WiFi.hostByName(NTPServerName, timeServerIP)) { // Get the IP address of the NTP server
    Serial.println("DNS lookup failed. Rebooting.");
    Serial.flush();
    ESP.reset();
  }
  Serial.print("Time server IP:\t");
  Serial.println(timeServerIP);
  
  Serial.println("\r\nSending NTP request ...");
  sendNTPpacket(timeServerIP);  
}

In the setup, we just start our Serial and Wi-Fi, as usual, and we start UDP as well. We'll look at the implementation of this function later. We need the IP address of the NTP server, so we perform a DNS lookup with the server's hostname. There's not much we can do without the IP address of the time server, so if the lookup fails, reboot the ESP. If we do get an IP, send the first NTP request, and enter the loop.

unsigned long intervalNTP = 60000; // Request NTP time every minute
unsigned long prevNTP = 0;
unsigned long lastNTPResponse = millis();
uint32_t timeUNIX = 0;

unsigned long prevActualTime = 0;

void loop() {
  unsigned long currentMillis = millis();

  if (currentMillis - prevNTP > intervalNTP) { // If a minute has passed since last NTP request
    prevNTP = currentMillis;
    Serial.println("\r\nSending NTP request ...");
    sendNTPpacket(timeServerIP);               // Send an NTP request
  }

  uint32_t time = getTime();                   // Check if an NTP response has arrived and get the (UNIX) time
  if (time) {                                  // If a new timestamp has been received
    timeUNIX = time;
    Serial.print("NTP response:\t");
    Serial.println(timeUNIX);
    lastNTPResponse = currentMillis;
  } else if ((currentMillis - lastNTPResponse) > 3600000) {
    Serial.println("More than 1 hour since last NTP response. Rebooting.");
    Serial.flush();
    ESP.reset();
  }

  uint32_t actualTime = timeUNIX + (currentMillis - lastNTPResponse)/1000;
  if (actualTime != prevActualTime && timeUNIX != 0) { // If a second has passed since last print
    prevActualTime = actualTime;
    Serial.printf("\rUTC time:\t%d:%d:%d   ", getHours(actualTime), getMinutes(actualTime), getSeconds(actualTime));
  }  
}

The first part of the loop sends a new NTP request to the time server every minute. This is based on Blink Without Delay. Then we call the getTime function to check if we've got a new response from the server. If this is the case, we update the timeUNIX variable with the new timestamp from the server. If we don't get any responses for an hour, then there's something wrong, so we reboot the ESP. The last part prints the actual time. The actual time is just the last NTP time plus the time since we received that NTP message.

Step 3: Circuit Connection & Testing

The main unit of the Clock is an ESP8266-12E chip. This chip is responsible to access time, date, weather and other relevant information from the server and display the data to the matrix display. For displaying the information I used 2, 4 in one dot matrix display connected as daisy-chain to form an 8 dot matrix display. On the display module, each matrix is driven by the MAX7219, a very popular led driver. One main advantage of having this display is that you can drive multiple displays using only three pins of any microcontroller. Another advantage is that a lot of Arduino library is available for MAX7219 and most of them support ESP8266.

The MAX7219 does all the control and refresh work for you in driving either an 8x8 matrix display or 8 x 7-segment displays (usually these also have a dot so it's really an 8-segment display) - 64 LEDs total. All you have to do is send it serial commands via the 4-pin SPI interface and it will automatically take care of the rest. Wiring is simplified as well, you only need to set the current level for all LEDs with a single resistor instead of 8 and you can also dim the entire display digitally. It's a thru-hole chip so you can use it in any breadboard, perfboard or other projects, although if you're soldering it in, we suggest using a socket.

One disadvantage of the circuit is that two voltage level is required for the clock. For stable operation, ESP8266 needs 3.3V supply and MAX7219 needs 5V supply. But it is not a big issue. A 3.3V regulator (AMS1117) and a 5V regulator LM7805 can solve the issue. ESP8266 is powered from 3.3V regulator and the display is powered from 5V regulator. Both the regulator is connected to a 12V DC supply.

I am assuming that you have some previous experience with ESP8266. If you are new in ESP8266 please take a look on getting started guide of esp8266. You may browse the following links:

https://randomnerdtutorials.com/getting-started-with-esp8266-wifi-transceiver-review/

https://openhomeautomation.net/getting-started-esp...

https://www.instructables.com/id/Getting-Started-Wi...

https://www.electronicshub.org/esp8266-arduino-interface/

I hope you are now confident enough to go forward. After making the circuit according to the above schematic upload the following Arduino sketch to the ESP8266-12E board. For uploading the first program a physical connection is required to your PC.

For compiling the sketch you will be required following libraries:

https://github.com/nickgammon/MAX7219_Dot_Matrix

https://github.com/nickgammon/bitBangedSPI

// Demo of MAX7219_Dot_Matrix library - sideways scrolling
// Author: Md. Khairul Alam
// Date: 5 July 2018
<p><br></p>

#include 
#include 
#include 

#include "Arduino.h"
#include 
#include 
const byte chips = 8; //number of matrix in the display

// for NodeMCU 1.0
#define DIN_PIN 15  // D8
#define CS_PIN  13  // D7
#define CLK_PIN 12  // D6

MAX7219_Dot_Matrix display (chips, CS_PIN, DIN_PIN, CLK_PIN);  // 2 chips, then specify the LOAD, DIN, CLK pins

WiFiClient client;

String weatherMain = "";
String weatherDescription = "";
String weatherLocation = "";
String country;
int humidity;
int pressure;
float temp;
float tempMin, tempMax;
int clouds;
float windSpeed;
String date;
int h,m,s;
int sunrise, sunset;

String currencyRates;
String weatherString;

// =======================================================================
// CHANGE YOUR CONFIG HERE:
// =======================================================================
const char* ssid     = "taifur&mafi";     // SSID of local network
const char* password = "University";   // Password on network
String weatherKey = "deb88f35850ad1216f787bc647c4939f";
String weatherLang = "&lang=en";
String cityID = "1185241"; //Dhaka
// read OpenWeather api description for more info
// =======================================================================

char message1 [] = "This is a testing display. Designed by Md. Khairul Alam";
char weather_message[300];
char time_message[50];
char date_message[50];

void setup ()
  {
  display.begin ();
  display.setIntensity (1);

  Serial.begin(115200);
  Serial.print("Connecting WiFi ");
  WiFi.begin(ssid, password);
  //printStringWithShift("Connecting",15);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected: "); Serial.println(WiFi.localIP());

  getWeatherData();
  String str = weatherString;
  int str_len = str.length() + 1; 
  str.toCharArray(weather_message, str_len) ;   

  getTime();
  
}  // end of setup

unsigned long lastMoved = 0;
unsigned long MOVE_INTERVAL = 2;  // mS
int  messageOffset;

void updateDisplay (char *msg)
  {
  //String message = msg;
  display.sendSmooth (msg, messageOffset);
  
  // next time show one pixel onwards
  if (messageOffset++ >= (int) (strlen (msg) * 8))
    messageOffset = - chips * 8;
  }  // end of updateDisplay
  
long last = millis();
long lastTime = millis();

void loop () 
  { 
  if((millis()- last)>1000*60*15){  //update weather every 15 minutes 
    getWeatherData();
    getTime();
    String str = weatherString;
    int str_len = str.length() + 1;
    str.toCharArray(weather_message, str_len) ;
    last = millis();
   }
   
  // update display if time is up
  if(s>0 && s<40){
  if (millis () - lastMoved >= MOVE_INTERVAL)
    {
      updateDisplay(weather_message);
      lastMoved = millis ();
    }  
  }

  if(s>40 && s<55){
  if (millis () - lastMoved >= MOVE_INTERVAL)
    {
      updateDisplay(date_message);
      lastMoved = millis ();
    }  
  }
  /*
  // do other stuff here    
  if(millis()-lastTime>65000){ 
    long now = millis();
    do{
      updateTime();
      showTime();
      }while(millis()-now<40000);
    now = millis();
    lastTime = millis();
  }  
   */
   if(s>55){
      showTime(); 
    }
   
 updateTime();
       
}  // end of loop


// =======================================================================
// retrive weather data

const char *weatherHost = "api.openweathermap.org";

void getWeatherData()
{
  Serial.print("connecting to "); Serial.println(weatherHost);
  if (client.connect(weatherHost, 80)) {
    client.println(String("GET /data/2.5/weather?id=") + cityID + "&units=metric&appid=" + weatherKey + weatherLang + "\r\n" +
                "Host: " + weatherHost + "\r\nUser-Agent: ArduinoWiFi/1.1\r\n" +
                "Connection: close\r\n\r\n");
  } else {
    Serial.println("connection failed");
    return;
  }
  String line;
  int repeatCounter = 0;
  while (!client.available() && repeatCounter < 10) {
    delay(500);
    Serial.println("w.");
    repeatCounter++;
  }
  while (client.connected() && client.available()) {
    char c = client.read(); 
    if (c == '[' || c == ']') c = ' ';
    line += c;
  }

  client.stop();

  DynamicJsonBuffer jsonBuf;
  JsonObject &root = jsonBuf.parseObject(line);
  if (!root.success())
  {
    Serial.println("parseObject() failed");
    return;
  }
  //weatherMain = root["weather"]["main"].as();
  weatherDescription = root["weather"]["description"].as();
  weatherDescription.toLowerCase();
  Serial.println(weatherDescription);
  //  weatherLocation = root["name"].as();
  //  country = root["sys"]["country"].as();
  sunrise = root["sys"]["sunrise"];
  sunset = root["sys"]["sunset"];
  temp = root["main"]["temp"];
  humidity = root["main"]["humidity"];
  pressure = root["main"]["pressure"];
  tempMin = root["main"]["temp_min"];
  tempMax = root["main"]["temp_max"];
  windSpeed = root["wind"]["speed"];
  clouds = root["clouds"]["all"];
  //String deg = String(char('~')+131);
  //weatherString = "  Temp: " + String(temp,1) /*+ deg*/ + "C (" + String(tempMin,1) /*+ deg*/ + "-" + String(tempMax,1) /*+ deg*/ + ")  ";
  weatherString = "  Temp: " + String(temp,1) /*+ deg*/ + "C";
  //weatherString += weatherDescription;
  weatherString += "  Humidity: " + String(humidity) + "% ";
  weatherString += "  Pressure: " + String(pressure) + "hPa ";
  weatherString += "  Clouds: " + String(clouds) + "%  ";
  weatherString += "  Wind: " + String(windSpeed,1) + "m/s   ";
  Serial.println(weatherString);
}

// =======================================================================
// retrive curency rate

const char* currencyHost = "cinkciarz.pl";

void getCurrencyRates()
{
  WiFiClientSecure client;
  Serial.print("connecting to "); Serial.println(currencyHost);
  if (!client.connect(currencyHost, 443)) {
    Serial.println("connection failed");
    return;
  }
  client.print(String("GET / HTTP/1.1\r\n") +
               "Host: " + currencyHost + "\r\nConnection: close\r\n\r\n");

  //Serial.print("request sent");
  int repeatCounter = 0;
  while (!client.available() && repeatCounter < 10) {
    delay(500);
    Serial.println("c.");
    repeatCounter++;
  }
  Serial.println("connected");
  while (client.connected() && client.available()) {
    String line = client.readStringUntil('\n');
    //      Serial.println(line);
    int currIdx = line.indexOf("/kantor/kursy-walut-cinkciarz-pl/usd");
    if (currIdx > 0) {
      String curr = line.substring(currIdx + 33, currIdx + 33 + 3);
      curr.toUpperCase();
      line = client.readStringUntil('\n');
      int rateIdx = line.indexOf("\">");
      if (rateIdx <= 0) {
        Serial.println("Found rate but wrong structure!");
        return;
      }
      currencyRates = "        PLN/" + curr + ": ";
      if (line[rateIdx - 1] == 'n') currencyRates += char('~'+24); else currencyRates += char('~'+23); // down/up
      currencyRates += line.substring(rateIdx + 2, rateIdx + 8) + " ";

      line = client.readStringUntil('\n');
      rateIdx = line.indexOf("\">");
      if (rateIdx <= 0) {
        Serial.println("Found rate but wrong structure!");
        return;
      }
      if (line[rateIdx - 1] == 'n') currencyRates += char('~'+24); else currencyRates += char('~'+23); // down/up
      currencyRates += line.substring(rateIdx + 2, rateIdx + 8);
      currencyRates.replace(',', '.');
      break;
    }
  }
  client.stop();
}

// =======================================================================

float utcOffset = 2;
long localEpoc = 0;
long localMillisAtUpdate = 0;

void getTime()
{
  WiFiClient client;
  if (!client.connect("www.google.com", 80)) {
    Serial.println("connection to google failed");
    return;
  }

  client.print(String("GET / HTTP/1.1\r\n") +
               String("Host: <a> <a> <a>  www.google.com\r\n")  </a> </a> </a> +
               String("Connection: close\r\n\r\n"));
  int repeatCounter = 0;
  while (!client.available() && repeatCounter < 10) {
    delay(500);
    //Serial.println(".");
    repeatCounter++;
  }

  String line;
  client.setNoDelay(false);
  while(client.connected() && client.available()) {
    line = client.readStringUntil('\n');
    line.toUpperCase();
    if (line.startsWith("DATE: ")) {
      date = ""+line.substring(6, 22);
      h = line.substring(23, 25).toInt();
      m = line.substring(26, 28).toInt();
      s = line.substring(29, 31).toInt();
      localMillisAtUpdate = millis();
      localEpoc = (h * 60 * 60 + m * 60 + s);

      String clock_date = date;
      int date_len = clock_date.length() + 1; 
      clock_date.toCharArray(date_message, date_len) ;
    
    }
  }
  client.stop();
}

// =======================================================================

void updateTime()
{
  long curEpoch = localEpoc + ((millis() - localMillisAtUpdate) / 1000);
  long epoch = round(curEpoch + 3600 * utcOffset + 86400L) % 86400L;
  h = ((epoch  % 86400L) / 3600) % 24;
  m = (epoch % 3600) / 60;
  s = epoch % 60;
}

// =======================================================================
long lastmillis=millis();
void showTime()
{
    String timeString = "";
    timeString+= h/10 ? h/10 : 0;
    timeString+= h%10;
    timeString+= ":";
    timeString+= m/10;
    timeString+= m%10;
    timeString+= ":";
    timeString+= s/10;
    timeString+= s%10;   
    String clock_time = timeString;
    int clock_len = clock_time.length() + 1; 
    clock_time.toCharArray(time_message, clock_len) ;
    display.sendString(time_message);
}

Arduino Sketch with OTA Support
#include 
#include 
#include 
#include 

#include 
#include 
#include 

#include "Arduino.h"
#include 

const byte chips = 8; //number of matrix in the display

// for NodeMCU 1.0
#define DIN_PIN 15  // D8
#define CS_PIN  13  // D7
#define CLK_PIN 12  // D6

MAX7219_Dot_Matrix display (chips, CS_PIN, DIN_PIN, CLK_PIN);  // 2 chips, then specify the LOAD, DIN, CLK pins

WiFiClient client;

String weatherMain = "";
String weatherDescription = "";
String weatherLocation = "";
String country;
int humidity;
int pressure;
float temp;
float tempMin, tempMax;
int clouds;
float windSpeed;
String date;
int h,m,s;
int sunrise, sunset;

String currencyRates;
String weatherString;

const char* ssid = "taifur&mafi";
const char* password = "University";

String weatherKey = "deb88f35850ad1216f787bc647c4939f";
String weatherLang = "&lang=en";
String cityID = "1185241"; //Dhaka
// read OpenWeather api description for more info
// =======================================================================

char message1 [] = "This is a testing display. Designed by Md. Khairul Alam";
char weather_message[300];
char time_message[50];
char date_message[50];

int LED = 2;

void setup() {
  Serial.begin(115200);

  display.begin ();
  display.setIntensity (1);
  Serial.print("Connecting WiFi ");
  Serial.println("Booting");
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("Connection Failed! Rebooting...");
    delay(5000);
    ESP.restart();
  }

  // Port defaults to 8266
  // ArduinoOTA.setPort(8266);

  // Hostname defaults to esp8266-[ChipID]
  // ArduinoOTA.setHostname("myesp8266");

  // No authentication by default
  // ArduinoOTA.setPassword("admin");

  // Password can be set with it's md5 value as well
  // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
  // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");

  ArduinoOTA.onStart([]() {
    String type;
    if (ArduinoOTA.getCommand() == U_FLASH)
      type = "sketch";
    else // U_SPIFFS
      type = "filesystem";

    // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
    Serial.println("Start updating " + type);
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
  });
  ArduinoOTA.begin();
  Serial.println("Ready");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  pinMode(LED, OUTPUT);     // Initialize the LED_BUILTIN pin as an output

  getWeatherData();
  String str = weatherString;
  int str_len = str.length() + 1; 
  str.toCharArray(weather_message, str_len) ;   

  getTime();
}

unsigned long lastMoved = 0;
unsigned long MOVE_INTERVAL = 2;  // mS
int  messageOffset;

void updateDisplay (char *msg)
  {
  //String message = msg;
  display.sendSmooth (msg, messageOffset);
  
  // next time show one pixel onwards
  if (messageOffset++ >= (int) (strlen (msg) * 8))
    messageOffset = - chips * 8;
  }  // end of updateDisplay
  
long last = millis();
long lastTime = millis();

void loop() {
  ArduinoOTA.handle();

  if((millis()- last)>1000*60*15){  //update weather every 15 minutes 
    getWeatherData();
    getTime();
    String str = weatherString;
    int str_len = str.length() + 1;
    str.toCharArray(weather_message, str_len) ;
    last = millis();
   }
   
  // update display if time is up
  if(s>0 && s<40){
  if (millis () - lastMoved >= MOVE_INTERVAL)
    {
      updateDisplay(weather_message);
      lastMoved = millis ();
    }  
  }

  if(s>40 && s<55){
  if (millis () - lastMoved >= MOVE_INTERVAL)
    {
      updateDisplay(date_message);
      lastMoved = millis ();
    }  
  }
  /*
  // do other stuff here    
  if(millis()-lastTime>65000){ 
    long now = millis();
    do{
      updateTime();
      showTime();
      }while(millis()-now<40000);
    now = millis();
    lastTime = millis();
  }  
   */
   if(s>55){
      showTime(); 
    }
   
 updateTime();                  // Wait for two seconds (to demonstrate the active low LED)
}

// =======================================================================
// retrive weather data

const char *weatherHost = "api.openweathermap.org";

void getWeatherData()
{
  Serial.print("connecting to "); Serial.println(weatherHost);
  if (client.connect(weatherHost, 80)) {
    client.println(String("GET /data/2.5/weather?id=") + cityID + "&units=metric&appid=" + weatherKey + weatherLang + "\r\n" +
                "Host: " + weatherHost + "\r\nUser-Agent: ArduinoWiFi/1.1\r\n" +
                "Connection: close\r\n\r\n");
  } else {
    Serial.println("connection failed");
    return;
  }
  String line;
  int repeatCounter = 0;
  while (!client.available() && repeatCounter < 10) {
    delay(500);
    Serial.println("w.");
    repeatCounter++;
  }
  while (client.connected() && client.available()) {
    char c = client.read(); 
    if (c == '[' || c == ']') c = ' ';
    line += c;
  }

  client.stop();

  DynamicJsonBuffer jsonBuf;
  JsonObject &root = jsonBuf.parseObject(line);
  if (!root.success())
  {
    Serial.println("parseObject() failed");
    return;
  }
  //weatherMain = root["weather"]["main"].as();
  weatherDescription = root["weather"]["description"].as();
  weatherDescription.toLowerCase();
  Serial.println(weatherDescription);
  //  weatherLocation = root["name"].as();
  //  country = root["sys"]["country"].as();
  sunrise = root["sys"]["sunrise"];
  sunset = root["sys"]["sunset"];
  temp = root["main"]["temp"];
  humidity = root["main"]["humidity"];
  pressure = root["main"]["pressure"];
  tempMin = root["main"]["temp_min"];
  tempMax = root["main"]["temp_max"];
  windSpeed = root["wind"]["speed"];
  clouds = root["clouds"]["all"];
  //String deg = String(char('~')+131);
  //weatherString = "  Temp: " + String(temp,1) /*+ deg*/ + "C (" + String(tempMin,1) /*+ deg*/ + "-" + String(tempMax,1) /*+ deg*/ + ")  ";
  weatherString = "  Temp: " + String(temp,1) /*+ deg*/ + "C";
  //weatherString += weatherDescription;
  weatherString += "  Humidity: " + String(humidity) + "% ";
  weatherString += "  Pressure: " + String(pressure) + "hPa ";
  weatherString += "  Clouds: " + String(clouds) + "%  ";
  weatherString += "  Wind: " + String(windSpeed,1) + "m/s   ";
  Serial.println(weatherString);
}

// =======================================================================
// retrive curency rate

const char* currencyHost = "cinkciarz.pl";

void getCurrencyRates()
{
  WiFiClientSecure client;
  Serial.print("connecting to "); Serial.println(currencyHost);
  if (!client.connect(currencyHost, 443)) {
    Serial.println("connection failed");
    return;
  }
  client.print(String("GET / HTTP/1.1\r\n") +
               "Host: " + currencyHost + "\r\nConnection: close\r\n\r\n");

  //Serial.print("request sent");
  int repeatCounter = 0;
  while (!client.available() && repeatCounter < 10) {
    delay(500);
    Serial.println("c.");
    repeatCounter++;
  }
  Serial.println("connected");
  while (client.connected() && client.available()) {
    String line = client.readStringUntil('\n');
    //      Serial.println(line);
    int currIdx = line.indexOf("/kantor/kursy-walut-cinkciarz-pl/usd");
    if (currIdx > 0) {
      String curr = line.substring(currIdx + 33, currIdx + 33 + 3);
      curr.toUpperCase();
      line = client.readStringUntil('\n');
      int rateIdx = line.indexOf("\">");
      if (rateIdx <= 0) {
        Serial.println("Found rate but wrong structure!");
        return;
      }
      currencyRates = "        PLN/" + curr + ": ";
      if (line[rateIdx - 1] == 'n') currencyRates += char('~'+24); else currencyRates += char('~'+23); // down/up
      currencyRates += line.substring(rateIdx + 2, rateIdx + 8) + " ";

      line = client.readStringUntil('\n');
      rateIdx = line.indexOf("\">");
      if (rateIdx <= 0) {
        Serial.println("Found rate but wrong structure!");
        return;
      }
      if (line[rateIdx - 1] == 'n') currencyRates += char('~'+24); else currencyRates += char('~'+23); // down/up
      currencyRates += line.substring(rateIdx + 2, rateIdx + 8);
      currencyRates.replace(',', '.');
      break;
    }
  }
  client.stop();
}

// =======================================================================

float utcOffset = 2;
long localEpoc = 0;
long localMillisAtUpdate = 0;

void getTime()
{
  WiFiClient client;
  if (!client.connect("www.google.com", 80)) {
    Serial.println("connection to google failed");
    return;
  }

  client.print(String("GET / HTTP/1.1\r\n") +
               String("Host: <a> <a>  www.google.com\r\n")  </a> </a> +
               String("Connection: close\r\n\r\n"));
  int repeatCounter = 0;
  while (!client.available() && repeatCounter < 10) {
    delay(500);
    //Serial.println(".");
    repeatCounter++;
  }

  String line;
  client.setNoDelay(false);
  while(client.connected() && client.available()) {
    line = client.readStringUntil('\n');
    line.toUpperCase();
    if (line.startsWith("DATE: ")) {
      date = ""+line.substring(6, 22);
      h = line.substring(23, 25).toInt();
      m = line.substring(26, 28).toInt();
      s = line.substring(29, 31).toInt();
      localMillisAtUpdate = millis();
      localEpoc = (h * 60 * 60 + m * 60 + s);

      String clock_date = date;
      int date_len = clock_date.length() + 1; 
      clock_date.toCharArray(date_message, date_len) ;
    
    }
  }
  client.stop();
}

// =======================================================================

void updateTime()
{
  long curEpoch = localEpoc + ((millis() - localMillisAtUpdate) / 1000);
  long epoch = round(curEpoch + 3600 * utcOffset + 86400L) % 86400L;
  h = ((epoch  % 86400L) / 3600) % 24;
  m = (epoch % 3600) / 60;
  s = epoch % 60;
}

// =======================================================================
long lastmillis=millis();
void showTime()
{
    String timeString = "";
    timeString+= h/10 ? h/10 : 0;
    timeString+= h%10;
    timeString+= ":";
    timeString+= m/10;
    timeString+= m%10;
    timeString+= ":";
    timeString+= s/10;
    timeString+= s%10;   
    String clock_time = timeString;
    int clock_len = clock_time.length() + 1; 
    clock_time.toCharArray(time_message, clock_len) ;
    display.sendString(time_message);
}

If you want to use the code with NTP server you will need the following additional libraries to compile the code:

Time.h & TimeLib.h:https://github.com/PaulStoffregen/Time

Timezone.h:https://github.com/JChristensen/Timezone

NTPClient.h:https://github.com/arduino-libraries/NTPClient

To show your Facebook information in the display follow the link:

https://www.instructables.com/id/Facebook-Fan-Count/

https://www.hackster.io/helge-johnsen/facebook-fan-count-faf13e

To count your youtube subscriber please follow the instructable:

https://www.instructables.com/id/YouTube-Subscriber-Counter-With-ESP8266-V2/

Step 4: 3D Printing

To give the display a professional look and make it long lasting I designed a 3D casing for the display. The box is designed in Tinkercad, a free online tool for 3D designing. You design is separated into four parts. Double print the bottom cover. Single print left and right side and attached two sides with super glue. Files for 3D printing is attached with the step. No support is required for printing.

Step 5: Preparing the Display

The first thing to do with the display modules is to connect two display modules together. The connection is very simple. Just attached the two displays and solder the identical pin together. Before soldering fix both the display together with some hot glue. After soldering both the display together connect jumper wire to the first display. These wires will be used to connect the display with the ESP8266 board.

Step 6: Preparing the ESP8266-12E

The ESP8266-12E chip is the main brain and controller of the clock. At the same time, the chip collects the information from the server and drive the dot matrix display. For interfacing the display with the esp8266 module only three GPIO is required. I used GPIO 12, 13 and 15 of esp chip to connect the display with the chip.

A typical 8x8 Dot Matrix unit has 64 LEDs arranged in a plane. You can get your hands on two types of Dot Matrices. One which comes as a plain single matrix which has 16 pins to control the rows and columns of the array. This one would use a lot of wires and things can get a lot messier.

To simplify these things, it is also available integrated with MAX7219 Driver, which has 24 pins. In the end, you have 5 pins to connect to your I/O which makes your job a lot more easier. There are 16 output lines from the 7219 driving 64 individual LEDs. Persistence of vision is exploited to make the LEDs appear to be on all the time when in fact they are not. You can also control the brightness of the LEDs through the code.

The connection between ESP8266 and display module is as follows:

  • Vcc to 5V Pin of Display Module.
  • Gnd to Gnd Pin of the Display Module.
  • DIN to Digital Pin 15 of the ESP8266.
  • CS to Digital Pin 12of the ESP8266.
  • CLK to Digital Pin 13 of the ESP8266.

For first time programming of ESP module, I soldered jumpers wires to TX, RX pin of ESP module. VCC and CH_PD pin should be connected to VCC.

Step 7: Inside the Box

In this stage, I will put all the circuits and modules inside the box. First, I place the display module in the box. Then I soldered ESP8266 to the display module according to the schematic. After that, I connect a 5V regulator LM7805 to the power pins of the display module. I also connect a 3.3V regulator module to the power pins of the ESP8266 module. Then I put hot glue to fix all the components in place. Finally, I place the bottom cover of the box.

Step 8: Final Output

After completing all the steps described above my clock is ready to hang. Before hanging the clock in the wall I tested it by powering it from a 12V wall adapter. After successfully testing and founding it ok I place the clock on the wall of my drawing room. The clock supports OTA update so I can update the firmware keeping the clock untouched.

Share

    Recommendations

    • Tiny Home Contest

      Tiny Home Contest
    • Fix It! Contest

      Fix It! Contest
    • Metalworking Contest

      Metalworking Contest

    24 Discussions

    bro how can i contacat you...

    i have some doubts about your smart bulb project...

    0
    None
    ПИ

    26 days ago

    Nice.. But.... I have an weird Idea for an cheap advertising screen ( for car )... Is it possible with this wifi module to do this:

    1. Making on my website or free host plus domain etc a empty web page onl containing plain text without code.

    2. read it and put it on the screen.

    Why? Because it would be cheaper than getting an rasberry or arduino board. Its mych easyer to make an hotspot in the car or if it is in office, and leave it, he will automatically connect and read the text from this ''website''...

    If it does this, it could do the website rading instead of using boards memory cards etc

    0
    None
    dajian

    4 weeks ago

    The little things are very cute, haha~ I also made a clock before, but there is no good way to hide the cable, which blocks my next step, you know that lithium battery is not the right choice.

    What do you mean? He gets the time using this call:
    sendNTPpacket(timeServerIP); // Send an NTP request
    Is there a better way?

    No, it is ok.

    However, in the attached sketches I've found nothing about NTP. There is getTime() function which parses the response from google.com (date header).

    That's why I sed about the weird way to sync time.

    StrangeRand makes a good point. If you look at the code for internet-clock.ino it would appear that the NTP server is not used. Rather it seems to use Google as the time server. So, here we have a tutorial that goes into detail about using the NTP time server but the final code uses an entirely different method and offers no explanation. I agree...weird.

    But don't get me wrong, I still appreciate the effort that the author has put into this tutorial. I am learning from it and I'm going to give it a try.

    Wouldn't it be better to have the input to the 3.3 volt regulator the output of the 5.0 volt regulator. Less voltage drop and less power loss and heat?

    1 reply

    You are absolutely right. Thanks for your feedback.

    Cool stuff, i will build one! But why haven't you used something like a Wemos or NodeMCU? No worries about voltage and easier to connect:)

    1 reply

    Yes, you can use Wemos or NodeMCU.

    Nice work my friend. One question the AMS1117 you linked to on Gearbest says its a 5 volt regulator not a 3.3 volt. Is this the correct part?

    1 reply

    Yes AMS1117 has 5v version also. Be sure you selected 3.3v version.

    I just hardcoded the offset value.

    I'm not able to get your code to compile. I was able to find the nonstandard libraries and install them into my Arduino IDE but it would be helpful if you were to include links to those libraries in your instructions. Even with all the required libraries installed I still can't get the sketches to fully compile. And yes, I'm sure that I selected the correct board. None the less, the compiler hitches up on the ArduinoJson and ArduinoOTA classes. I suspect that this may have something to do with conflicts between versions of one or more of the libraries and/or board packages that I have installed that probably don't match the versions that you may have used. I just can't seem to resolve it. Any ideas?