8 years, 11 months ago.  This question has been closed. Reason: Off Topic

ESP8266 - How to get NTP time?

Hi mbed friends,

can I get NTP time using an ESP8266 ESP-01 Serial Wireless WIFI Module?

In order to connect the NUCLEO-F746ZG to the internet via wifi, I use this ESP8266 module: https://nurdspace.nl/ESP8266 Some useful documentation was found here: https://alselectro.wordpress.com/2015/05/05/wifi-module-esp8266-1-getting-started-with-at-commands/

Mine use firmware (AT+GMR):

AT version:1.1.0.0(May 11 2016 18:09:56)
SDK version:1.5.4(baaeaebb)
Ai-Thinker Technology Co. Ltd.

If I use below library it connects to wifi: https://developer.mbed.org/teams/components/code/ESP8266/

I do something like this:

#define ESP8266_TX      PC_6        // PC_6  Serial6_TX
#define ESP8266_RX      PC_7        // PC_7  Serial6_RX
#define ESP8266_RST     PE_11       // D5  Reset

ESP8266 esp(ESP8266_TX, ESP8266_RX, true);    // tx, rx, debug

    esp.startup(ESP8266_AP_STATION_MODE);
    esp.connect("SSID", "Passwd");

    printf("IP %s\n", esp.getIPAddress());

That works! Then I attempt to get NTP time, like this, but the return size = -1 (time-out, notting received):

//    esp.open("UDP", 1, "1.nl.pool.ntp.org", 123);
    esp.open("UDP", 1, "129.250.35.250", 123);

    const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
    unsigned char packetBuffer[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]: 8 bytes of zero for Root Delay & Root Dispersion
    packetBuffer[12] = 49;
    packetBuffer[13] = 0x4E;
    packetBuffer[14] = 49;
    packetBuffer[15] = 52;

    esp.send(1, packetBuffer, NTP_PACKET_SIZE);

    wait(1.0);

    uint32_t recv_size = esp.recv(1, packetBuffer, NTP_PACKET_SIZE);
    printf("Size %d\n", recv_size);
    esp.close(1);
    // combine the four bytes (two words) into a long integer
    // this is NTP time (seconds since Jan 1 1900):
    uint32_t secsSince1900 = (packetBuffer[40] << 24) | (packetBuffer[41] << 16) | (packetBuffer[42] << 8) | packetBuffer[43];
    printf("TS: %ul\n", secsSince1900);

Update: The returned size of -1 is probably a bug in ESP8266 Library. I "fixed" it (-1 should be reported when failing to receive a package, I think):

int32_t ESP8266::recv(int id, void *data, uint32_t amount)
{
    // Receive network data:
    // +IPD
    // Receive network data from single connection:
    // +IPD,len:data
    // Receive network data from multiple connection:
    // +IPD,id,len:data
    // Parameters:
    // id: id no. of connection
    // len: data length
    // data: data received
    uint32_t recv_amount;
    int recv_id;

    if (!(_parser.recv("+IPD,%d,%d:", &recv_id, &recv_amount)
        && recv_id == id
        && recv_amount <= amount
        && _parser.read((char*)data, recv_amount)
        && _parser.recv("OK"))) {
        return recv_amount;
//        return -1;
    }

    return recv_amount;
}

The timestamp value is much higher than I expected: 3687018995 instead of 1478030195. This is because NTP counts from 1900 and the Unix time stamp counts from 1970. NTP uses an epoch of 1900-01-01 00:00:00, so the offset should be 2208988800 seconds to 1970-01-01 00:00:00. And the difference in seconds will be calculated like: (70*365 + 17)*86400 = 2208988800.

ESP8266Interface looked more promising, it possibly can be combined with the NTPClient library. Si I tried ESP8266Interface, but it hangs at init and does not connect to wifi. It is possibly that doesn't work with the Ai-Thinker firmware.

ESP8266Interface esp(ESP8266_TX, ESP8266_RX, ESP8266_RST, "SSID", "Passwd", 115200);    // tx, rx, reset, ssid, phrase, baud

    printf("Init... ");
    esp.init(); //Reset   // <--- Hangs here
    printf("OK\n");
    printf("Connecting... ");
    esp.connect(); //Use DHCP
    printf("OK\n");
    printf("IP %s\n", esp.getIPAddress());

Has anybody done this before?

In the mean time, I continued troubleshooting, and got the first method working. I shares above how it was done.

Kind regards, Jack.

Now, I have a bit more time to play with mbed stuff. :-)

I'm playing with the ESP8266 and NTP, and added a function in a ESP8266 Library to get the NTP time. This seems to work very well, picks up fast and sure:

bool ESP8266::getNTP(uint32_t *secsSince1970, char * NTPpool)
{
    unsigned char packetBuffer[NTP_PACKET_SIZE];
    
    memset(packetBuffer, 0x00, 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]: 8 bytes of zero for Root Delay & Root Dispersion
    packetBuffer[12] = 49;
    packetBuffer[13] = 0x4E;
    packetBuffer[14] = 49;
    packetBuffer[15] = 52;

    setTimeout(50);
    uint32_t recv_size = 0;
    int tries1 = 10;
    while ((recv_size != NTP_PACKET_SIZE) && (tries1 > 0)) {
        tries1--;
        
        open("UDP", 1, NTPpool, 123);   // NTPpool = "1.nl.pool.ntp.org"
        send(1, packetBuffer, NTP_PACKET_SIZE);
        int tries2 = 100;
        while ((recv_size != NTP_PACKET_SIZE) && (tries2 > 0)) {
            tries2--;
            recv_size = recv(1, packetBuffer, NTP_PACKET_SIZE);
        }
    }
    close(1);
    *secsSince1970 = ((packetBuffer[40] << 24) | (packetBuffer[41] << 16) | (packetBuffer[42] << 8) | packetBuffer[43]) - NTP_OFFSET;
    return recv_size == NTP_PACKET_SIZE ? true : false;
}

Note: NTP uses an epoch of 1900-01-01 00:00:00, so the offset should be 2208988800 seconds to 1970-01-01 00:00:00. (70*365 + 17)*86400 = 2208988800

#define NTP_OFFSET                          2208988800ULL

And for the RTC:

void DS3231::setDateTimeSecsSince1970(uint32_t secsSince1970)
{
    time_t secondsEpoch = (time_t)(secsSince1970 + timeZoneOffset);
    t = *localtime(&secondsEpoch);
    t.tm_year += 1900;    // adjust for tm structure required values
    t.tm_mon  += 1;        // adjust for tm structure required values
    setDateTime(t.tm_mday, t.tm_mon, t.tm_year, t.tm_hour, t.tm_min, t.tm_sec);
    set_time(secsSince1970);
}

In main, I did this:

    oled.printf("Get NTP: ");
    uint32_t secsSince1970 = 0;
    bool succes = esp.getNTP(&secsSince1970, "1.nl.pool.ntp.org");
    if (succes)
        oled.printf("Succes!\n");
    else
        oled.printf("FAILED!\n");
    ds3231.setDateTimeSecsSince1970(secsSince1970);

    // Get updated time using timezone
    secsSince1970 = ds3231.getDateTimeSecsSince1970();
    strftime(buffer, 32, "%A\n%d-%m-%Y %H:%M:%S", localtime(&secsSince1970));
    oled.gotoxy(0, 5);
    oled.printf("%s\n", buffer);

I hope I help somebody with this. Any comments or recommendations are welcome, I'm not yet an mbed expert. :-)

Regards, Jack.

posted by Jack Berkhout 04 Nov 2016