arduino-libraries / NTPClient

Connect to a NTP server
539 stars 371 forks source link

Bad Reliability of Response #114

Open v11 opened 4 years ago

v11 commented 4 years ago

Hello

I am using NTP Client to get the current time for my ESP2866 / Node MCU 1.0. Sometimes i get the time and often i get no time and then my app fails.

Can somebody tell me if I am doing something wrong at all or how can i retrieve data more reliable?

Thank you very much! Lukas

// Wifi Setup: PreProcessor: Chech if SSID was already defined
#ifndef STASSID
#define STASSID "*****"
#define STAPSK "*****"
#endif

// Wifi: Library: esp8266 Wifi support. Based on WiFi.h from Arduino WiFi shield library.
#include <ESP8266WiFi.h>

// Wifi: Library: Variant of WiFiClient with Transport Layer Security (TLS) support. Deprecated predecessor: Secure Sockets Layer (SSL)
#include <WiFiClientSecure.h>

// NTP: Library: Library to get the current time. Needs internet connection.
#include <NTPClient.h>
#include <WiFiUdp.h>

// JSON: Library: Include JSON Library https://arduinojson.org
#include <ArduinoJson.h>

// Wifi: Set WiFi Credentials, Host and Port (443)
const char *ssid = STASSID;
const char *password = STAPSK;
// const char *host = "www.strava.com";
// const int httpsPort = 443;

// Wifi: SHA1 fingerprint of the certificate, rendered as a hexadecimal number, 40 digits long
// Wifi: Use the web browser to show the certficate and read it from there
const char fingerprint[] PROGMEM = "1C 84 A1 6C E9 33 B1 2C 94 23 4D D5 69 EC 95 6A 53 68 82 69";

// GxEPD2: Base class GxEPD2_GFX can be used to pass references or pointers to the display instance as parameter, uses ~1.2k more code.
// GxEPD2: Enable or disable GxEPD2_GFX base class:
#define ENABLE_GxEPD2_GFX 0

// GxEPD2: Library: Arduino Display Library for SPI E-Paper Displays for black and white displays https://github.com/ZinggJM/GxEPD2
#include <GxEPD2_BW.h>
#include <GxEPD2_3C.h>

// Adafruit_GFX: Library: Adafruit_GFX library
#include <Adafruit_GFX.h>

// Adafruit_GFX: Fonts from Adafruit_GFX
#include <Fonts/FreeMono18pt7b.h>
#include <Fonts/FreeMono24pt7b.h>
#include <Fonts/FreeMonoBold18pt7b.h>
#include <Fonts/FreeMonoBold24pt7b.h>
#include <Fonts/FreeSans9pt7b.h>
#include <Fonts/FreeSans12pt7b.h>
#include <Fonts/FreeSans18pt7b.h>
#include <Fonts/FreeSans24pt7b.h>
#include <Fonts/FreeSansBold9pt7b.h>
#include <Fonts/FreeSansBold12pt7b.h>
#include <Fonts/FreeSansBold18pt7b.h>
#include <Fonts/FreeSansBold24pt7b.h>

// Bitmaps
// Online tool for converting images to byte arrays:
// https://javl.github.io/image2cpp/
// #include "bitmap.h"

// GxEPD2: Micro-controller: Waveshare e-Paper ESP8266 Driver Board
// GxEPD2: Display: 800×480, 7.5inch E-Ink display with HAT, 2-color

//GxEPD2_BW<GxEPD2_750, GxEPD2_750::HEIGHT / 4> display(GxEPD2_750(/*CS=15*/ SS, /*DC=4*/ 4, /*RST=5*/ 5, /*BUSY=16*/ 16));
GxEPD2_BW<GxEPD2_750_T7, GxEPD2_750_T7::HEIGHT / 4> display(GxEPD2_750_T7(/*CS=15*/ SS, /*DC=4*/ 4, /*RST=5*/ 5, /*BUSY=16*/ 16));

// JSON Data
// Message of the current day
const char *message1Char;
String message1;
const char *message2Char;
String message2;
const char *message3Char;
String message3;
const char *message4Char;
String message4;
// Current day
String day;

// UDP (Time-Server)
// Day in Seconds: 86400
const long utcOffsetInSeconds = 7200;
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", utcOffsetInSeconds);

void setup()
{

    // GxEPD2: Initiate the display
    // This should always be at the Beginning of Setup()
    display.init(115200);

    // GxEPD2: Set orientation. Goes from 0, 1, 2 or 3
    display.setRotation(0);

    // GxEPD2: Set TextWrap
    display.setTextWrap(false);

    // Open Serial Connection with Baudrate
    Serial.begin(115200);
    Serial.println();
    Serial.print("connecting to ");
    Serial.println(ssid);

    // WiFi: Start WiFi Connection in Station Mode (Connect as a Station to a Access Point)
    WiFi.mode(WIFI_STA);
    WiFi.begin(ssid, password);

    // WiFi: Connecting WiFi and check the WiFi Status
    while (WiFi.status() != WL_CONNECTED)
    {
        delay(500);
        Serial.print(".");
    }
    Serial.println("");

    // WiFi: WiFi Connected
    Serial.println("WiFi connected");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());

    getTime();
    delay(1000);
    getJSONData();
    renderPage();

    // Update every Hour
    // Serial.println("Start Delay 20 Seconds");
    // delay(10000);

    // Deep Sleep Mode (in Micro Seconds)
    // Connect the RST pin to GPIO 16 (D0) on the ESP8266.
    // Upload new Sketch during Deep Sleep
    // 1. Remove the cable from RST to GPIO 16 (D0)
    // 2. Hold down the RST button
    // 3. Hold down the FLASH button
    // 4. Release the Reset Button
    // 5. ESP is in Programming Mode: The built in bootloader is running instead of your sketch
    // 6. Upload Sketch
    // ToDo: Find Jumper or Button to remove cable when ESP is fully wired up later.
    Serial.println("Updated");
    Serial.println("Start Deep Sleep for 60 Seconds");
    // In this case, 30e6 corresponds to 30000000 microseconds which is equal to 30 seconds.
    //ESP.deepSleep(60000000);
}

void getTime()
{
    // Get Time
    Serial.println(("get times"));
    timeClient.begin();
    timeClient.update();
}

void getJSONData()
{

    // HTTPS
    const char *host = "www.vision11.ch";
    const int port = 443;

    // HTTPS Connection: Use WiFiClientSecure class to create TLS connection
    WiFiClientSecure client;
    client.setNoDelay(false);

    Serial.print("connecting to: ");
    Serial.println(host);

    // HTTPS Fingerprint
    client.setFingerprint(fingerprint);

    // HTTPS/HTTP Connection: Check if Connection is successfull
    if (!client.connect(host, port))
    {
        Serial.println("connection failed");
        return;
    }

    // HTTPS Connection: Request URL with
    String url = "/share/koni/data.json";
    Serial.print("requesting URL: ");
    Serial.println(host + url);

    client.print(String("GET ") + url + " HTTP/1.0\r\n" +
                 "Host: " + host + "\r\n" +
                 "User-Agent: BuildFailureDetectorESP8266\r\n" +
                 "Connection: close\r\n\r\n");

    Serial.println("request sent");

    // HTTPS Connection: Client is connected. Read Headers
    // HTTPS Connection: Header is the first response we get from the Server
    // HTTPS Connection: Example: HTTP/1.1 200 OK: Standard response for successful HTTP requests.
    while (client.connected() || client.available())
    {
        if (client.available())
        {
            String line = client.readStringUntil('\n');

            if (line == "\r")
            {
                Serial.println("headers received");
                break;
            }
        }
    }

    // HTTPS Connection: Once server sends all requested data it will disconnect, then once all received data are read, program will exit the while loop.

    // JSON Parse: ArduinoJson
    // JSON Parse: Allocate the JSON document
    // JSON Parse: Use https://arduinojson.org/v6/assistant/ to calculate the capacity
    // Configured: 140 Characters
    const size_t capacity = JSON_ARRAY_SIZE(7) + 7 * JSON_OBJECT_SIZE(5) + 1060;
    DynamicJsonDocument doc(capacity);

    // JSON Parse: Parse JSON object and receive possible errors
    DeserializationError error = deserializeJson(doc, client);

    // JSON Parse: Print error and stop the program
    if (error)
    {
        Serial.print(("deserializeJson() failed: "));
        Serial.println(error.c_str());
        return;
    }
    {
        Serial.println("serialization successful");
    }

    // Calculate Seconds from the current day
    long timeStampDayBegin = timeClient.getSeconds();
    timeStampDayBegin += timeClient.getMinutes() * 60;
    timeStampDayBegin += timeClient.getHours() * 3600;

    // Get the 00:00:00 of the current day in epoch time.
    timeStampDayBegin = timeClient.getEpochTime() - timeStampDayBegin;

    Serial.print("Current Day: ");
    Serial.println(timeStampDayBegin);

    // Find the current day
    for (int i = 0; i < doc.size(); i++)
    {

        if (timeStampDayBegin == doc[i]["date"])
        {
            // .. and get the message
            message1Char = doc[i]["message1"];
            message1 = message1Char;
            Serial.println(message1Char);

            message2Char = doc[i]["message2"];
            message2 = message2Char;
            Serial.println(message2Char);

            message3Char = doc[i]["message3"];
            message3 = message3Char;
            Serial.println(message3Char);

            message4Char = doc[i]["message4"];
            message4 = message4Char;
            Serial.println(message4Char);
        }
    }
}

void renderPage()
{

    // Set full window mode, meaning is going to update the entire screen
    display.setFullWindow();

    // Clear previous graphics to start over to print new things.
    display.fillScreen(0xFFFF);

    display.firstPage();

    do
    {
        int marginLeft = 40;
        int marginTop = 40;
        int lineHeight = 30;

        int font9ptHeight = 12;
        int font24ptHeight = 24;

        // Set color for text
        display.setTextColor(0x0000);
        display.setTextSize(1);
        display.setTextWrap(true);

        // Set the position to start printing text (x,y)
        // The x,y for fonts is on the left bottom, not left top

        // Parse message
        // Convert String to Char

        // Split message into array based on Spacing
        // Fill words into Line and check if can fit
        // Otherwise go to next line

        // JSON Data
        display.setFont(&FreeMonoBold18pt7b);
        display.setCursor(marginLeft, marginTop + font24ptHeight);
        display.println(message1);

        display.setFont(&FreeMonoBold18pt7b);
        display.setCursor(marginLeft, marginTop + font24ptHeight + 50);
        display.println(message2);

        display.setFont(&FreeMonoBold18pt7b);
        display.setCursor(marginLeft, marginTop + font24ptHeight + 100);
        display.println(message3);

        display.setFont(&FreeMonoBold18pt7b);
        display.setCursor(marginLeft, marginTop + font24ptHeight + 150);
        display.println(message4);

        // Last Update
        // display.setFont(&FreeSans9pt7b);
        // display.setCursor(610, marginTop + font9ptHeight);
        // display.print("Updated: ");
        // display.println(timeClient.getFormattedTime());

        // Draw Bike Icon
        // display.drawInvertedBitmap(40, 25, icnbike, 100, 100, GxEPD_BLACK);

    } while (display.nextPage());
}

void loop()
{
    // // Update every Hour
    // delay(3600000);

    // // Deep Sleep Mode (in Micro Seconds) We need to tie the RST pin to GPIO 16 on the ESP8266. On the NodeMCU, GPIO 16 is represented as D0.
    // //Serial.println("Starting Deep Sleep 30 Seconds");
    // ESP.deepSleep(30e6);
}

char *string2char(String command)
{
    if (command.length() != 0)
    {
        char *p = const_cast<char *>(command.c_str());
        return p;
    }
}
ddTech commented 3 years ago

Grüezi Lukas,

Can somebody tell me if I am doing something wrong at all or how can i retrieve data more reliable?

I also found that calls to the global "pool.ntp.org" tend to time out and sometimes return corrupt results. I played with different providers and used direct IP-Addresses from servers that worked for me. But then I found out that our local router provides NTP-service as well.
Check my post #127 . This might help.

regards

Frank

v11 commented 3 years ago

Grüezi! :-)

Thanks for your response. In the meantime i have moved to an ESP32 and the "time.h" library. It works very reliable.

Lukas

ddTech commented 3 years ago

yes, I'm also using the time.h library and have my own implementation for the actual NTP request. Just fell over this library and found the issue mentioned.

Frank