JAndrassy / WiFiEspAT

Arduino networking library. Standard Arduino WiFi networking API over ESP8266 or ESP32 AT commands.
GNU Lesser General Public License v2.1
288 stars 44 forks source link

Implementation of SNTP based on ESP8266 AT Firmware, officially launched by Espressif #8

Closed heinemannj closed 4 years ago

heinemannj commented 4 years ago

I have modified EspAtDrv.cpp with the command sets by ESP8266 AT Firmware, officially launched by Espressif.

But I#m runnig into below issue:

// Initialize NTP client WiFi.sntp(ntpTimeZone, ntpServer);

15:23:49.265 -> esp INFO: SNTP config 15:23:49.265 -> esp> AT+CIPSNTPCFG=1,1,"192.168.178.1" ...sent 15:23:49.265 -> esp> OK ...matched

for (int i = 0; i < 5; i++) { setTime(WiFi.getTime()); }

15:23:49.265 -> esp INFO: SNTP time 15:23:49.265 -> esp> AT+CIPSNTPTIME? ...sent 15:23:49.265 -> esp> +CIPSNTPTIME:Thu Jan 01 00:00:00 1970 ...matched 15:23:49.265 -> esp> OK ...matched 15:23:49.265 -> esp INFO: SNTP time 15:23:49.265 -> esp> AT+CIPSNTPTIME? ...sent 15:23:49.299 -> esp> +CIPSNTPTIME:Thu Jan 01 00:00:00 1970 ...matched 15:23:49.299 -> esp> OK ...matched 15:23:49.299 -> esp INFO: SNTP time 15:23:49.299 -> esp> AT+CIPSNTPTIME? ...sent 15:23:49.299 -> esp> +CIPSNTPTIME:Thu Feb 06 15:23:50 2020 ...matched 15:23:49.299 -> esp> OK ...matched 15:23:49.299 -> esp INFO: SNTP time 15:23:49.299 -> esp> AT+CIPSNTPTIME? ...sent 15:23:49.299 -> esp> +CIPSNTPTIME:Thu Feb 06 15:23:50 2020 ...matched 15:23:49.335 -> esp> OK ...matched

Serial.println(WiFi.getTime()); Serial.println(now());

char buff[20]; sprintf(buff, "%02d-%02d-%02d %02d:%02d:%02d", year(), month(), day(), hour(), minute(), second()); Serial.println(buff);

15:32:05.210 -> 0 15:32:05.210 -> 0 15:32:05.210 -> 1970-00-00 00:00:00

Can you please adapt SNTP functions to the official commands? ESP8266 NonOS AT Bin V1.7.1

JAndrassy commented 4 years ago

see SNTPTime.ino example it works only with AT Lobo builds

heinemannj commented 4 years ago

Hi Juraj, yes for sure - actually it works not with the official Espressif versions ... But I'm pretty sure you are able to implement ...

I have already a workarrounf by using library. But using WiFiEspAT functions is much more faster and easier to maintain.

Cheers Joerg

JAndrassy commented 4 years ago

the official AT firmware doesn't have a command to return time in epoch seconds. parsing the display format of AT+CIPSNTPTIME? is something I am not willing to code. It would make the library larger. the AT 2 has AT+SYSTIMESTAMP?. you could ask Espressif here to add it to 1.7.3 too

heinemannj commented 4 years ago

OK - Your view is fully understandable.

I have open an issue at Espressif: https://github.com/espressif/ESP8266_NONOS_SDK/issues/297

heinemannj commented 4 years ago

As a workarround library can be used:

#include <WiFiEspAT.h>
#include <NTPClient.h>
#include <TimeLib.h>

// Baud rate for communication with Monitor Port
#define MONITOR_BAUD_RATE 115200      // Works reliable over USB

// Baud rate for communication with AT firmware
#define AT_BAUD_RATE 500000           // For Arduino Mega over Hardware Serial "500000" baud works good
//#define AT_BAUD_RATE 200000
//#define AT_BAUD_RATE 115200         // Over Hardware Serial AT Firmware's Default "115200" baud works reliably

// Serial port for communication with AT firmware
//#define SerialAT Serial1
//#define SerialAT Serial2
#define SerialAT Serial3

// Number of the LED pin
//#define LedPin LED_BUILTIN
#define LedPin 13

// Time server pool
// Offset in seconds - Can be changed later with setTimeOffset()
// Update interval in milliseconds - Can be changed using setUpdateInterval()
const char* ntpServer = "us.pool.ntp.org";
const long ntpTimeOffset = 3600;
const unsigned long ntpUpdateInterval = 3600000;

//=======================================================================
//                             LED blink
//=======================================================================
void blink(int interval) {
  digitalWrite(LedPin, HIGH);
  if (interval > 0) {
    delay(interval);
    digitalWrite(LedPin, LOW);
    delay(interval);
  } else {
    digitalWrite(LedPin, LOW);
  }
}

//=======================================================================
//                             Setup Wifi
//=======================================================================
IPAddress wifiIP;
uint8_t wifiMac[6];
char wifiMacStr[18];
char wifiHostname[33];
long wifiRssi;

void setup_wifi() {
  // Initialize Serial port for communication with the WiFi module
  SerialAT.begin(AT_BAUD_RATE);
  WiFi.init(SerialAT);

  // Check if Serial port for communication with the WiFi module is working
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println();
    Serial.println("Communication with WiFi module failed!");
    // don't continue
    while (true) {
      blink(100);
    }
  }

  // Disable ESP deep sleep mode
  Serial.println("Sending AT deep sleep change (disable deep sleep) ...");
  SerialAT.println("AT+SLEEP=0");
  blink(500);

  // Disable default automatic start of persistent AP
  WiFi.endAP(true);

  // Waiting for connection to Wifi network set with the SetupWiFiConnection sketch
  Serial.println("Waiting for connection to WiFi");
  while (WiFi.status() != WL_CONNECTED) {
     blink(500);
    Serial.print('.');
  }
  Serial.println();
  Serial.println("Connected to WiFi network.");

  // SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // Your WiFi shield's IP address:
  wifiIP = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(wifiIP);

  // Your WiFi shield's MAC address
  WiFi.macAddress(wifiMac);
  snprintf(wifiMacStr, sizeof(wifiMacStr), "%02X:%02X:%02X:%02X:%02X:%02X", wifiMac[5], wifiMac[4], wifiMac[3], wifiMac[2], wifiMac[1], wifiMac[0]);
  Serial.print("MAC Address: ");
  Serial.println(wifiMacStr);

  // Your WiFi shield's hostname:
  WiFi.hostname(wifiHostname);
  Serial.print("Hostname: ");
  Serial.println(wifiHostname);

  // The received WiFi signal strength:
  wifiRssi = WiFi.RSSI();
  Serial.print("Signal Strength (RSSI): ");
  Serial.print(wifiRssi);
  Serial.println(" dBm");
}

//=======================================================================
//                           Setup (Main)
//=======================================================================
// For NTP client
WiFiUDP UDP;
NTPClient timeClient(UDP, ntpServer, ntpTimeOffset, ntpUpdateInterval);

void setup() {
  // Initial LED state
  pinMode(LedPin, OUTPUT);
  digitalWrite(LedPin, LOW);

  // Initialize Serial Monitor
  Serial.begin(MONITOR_BAUD_RATE);

  // Initialize WiFi
  setup_wifi();

  // Initialize NTP client
  timeClient.begin();
  setSyncProvider(&ntpSyncProvider);
}

//=======================================================================
//          Synchronization with NTP server / Update localtime
//=======================================================================
time_t ntpSyncProvider() {
  // Waiting for synchronization with NTP server
  for (int i = 0; i < 5; i++) {
    if (timeClient.forceUpdate()) {
      return timeClient.getEpochTime();
    } else {
      blink(5);
    }
  }
  return timeClient.getEpochTime();
}

//=======================================================================
//                            Loop (Main)
//=======================================================================

void loop() {
  char buff[20];
  sprintf(buff, "%02d-%02d-%02d %02d:%02d:%02d", year(), month(), day(), hour(), minute(), second());
  Serial.println(buff);

  delay(1000);
}
thn80 commented 3 years ago

I ran into the same problem while using an ESP8285.

As a quick solution I hacked the functions sntpCfg() and sntpTime() in my EspAtDrv.cpp as shown below. To activate the new parts of code, the macro WIFIESP8285 has to be defined.

bool EspAtDrvClass::sntpCfg(int8_t timezone, const char* server1, const char* server2) {
  maintain();

  LOG_INFO_PRINT_PREFIX();
  LOG_INFO_PRINTLN(F("SNTP config"));

  #ifdef WIFIESP8285
  timezone = 0;     // The ESP8285 prints a timestamp including the timezone offset. We don't want to have this for our Unixtime.
  #endif

  cmd->print(F("AT+CIPSNTPCFG=1,"));
  cmd->print(timezone);
  cmd->print(",\"");
  cmd->print(server1);
  if (server2) {
    cmd->print((FSH_P) QOUT_COMMA_QOUT);
    cmd->print(server2);
  }
  cmd->print('"');
  return sendCommand();
}

unsigned long EspAtDrvClass::sntpTime() {
  maintain();

  LOG_INFO_PRINT_PREFIX();
  LOG_INFO_PRINTLN(F("SNTP time"));

#ifdef WIFIESP8285
  // Output of AT+CIPSNTPTIME? is +CIPSNTPTIME:Sun Sep 26 13:24:51 2021
  cmd->print(F("AT+CIPSNTPTIME?"));
  if (!sendCommand(PSTR("+CIPSNTPTIME")))
    return 0;

  uint32_t y, m, d, hh, mm, ss;     // We use 32 bits for all variables for easier calculations later.

  // Name of Day (not used)
  char* tok = strtok(buffer + strlen("+CIPSNTPTIME:"), " ");

  // Month
  tok = strtok(NULL, " ");
  switch( *tok )
  {
      case 'J':     // Jan, Jun, Jul
        if( *(tok+1) == 'a' )       // Jan
        {
            m = 1;
        }
        else if( *(tok+2) == 'n' )  // Jun
        {
            m = 6;
        }
        else                        // Jul
        {
            m = 7;
        }
        break;
      case 'F':     // Feb
        m = 2;
        break;
      case 'M':     // Mar
        m = 3;
        break;
      case 'A':     // Apr
        m = 8;
        break;
      case 'S':     // Sep
        m = 9;
        break;
      case 'O':     // Oct
        m = 10;
        break;
      case 'N':     // Nov
        m = 11;
        break;
      case 'D':     // Dec
        m = 12;
        break;
      default:      // Error
        m = 0;
        break;
  }

  // Day
  tok = strtok(NULL, " ");
  d = strtoul(tok, NULL, 10);

  // Time
  tok = strtok(NULL, " ");
  hh = 10 * (*(tok+0) - '0') + (*(tok+1) - '0');
  mm = 10 * (*(tok+3) - '0') + (*(tok+4) - '0');
  ss = 10 * (*(tok+6) - '0') + (*(tok+7) - '0');

  // Year
  tok = strtok(NULL, " ");
  //yOff = strtoul(tok, NULL, 10) - 2000;
  y = strtoul(tok, NULL, 10);

  // Convert to Epoch
  uint8_t daysInMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  uint32_t yday;
  uint8_t i;  

  yday = d;

  for (i = 1; i < m; i++)
    yday += daysInMonth[i-1];

  if(m > 2)
  {
    // Detect leap years
    if( ((y % 4 == 0) && (y % 100!= 0)) || (y%400 == 0) )
    {
      yday++;
    }
  }

  y = y - 1900;
  yday = yday - 1;  // The following formula uses "days since January 1 of the year".

  // Formula from https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_15
  unsigned long res = ss + mm*60 + hh*3600 + yday*86400 + (y-70)*31536000 + ((y-69)/4)*86400 - ((y-1)/100)*86400 + ((y+299)/400)*86400;
#elif defined WIFIESPAT1
  cmd->print(F("AT+SNTPTIME?")); // AT LoBo firmware command
  if (!sendCommand(PSTR("+SNTPTIME")))
    return 0;
  char* tok = strtok(buffer + strlen("+SNTPTIME:"), ",");
  unsigned long res = strtoul(tok, NULL, 10);  
#else
  cmd->print(F("AT+SYSTIMESTAMP?"));
  if (!sendCommand(PSTR("+SYSTIMESTAMP")))
    return 0;
  unsigned long res = strtoul(buffer + strlen("+SYSTIMESTAMP:"), NULL, 10);
#endif
  readOK();
  return res;
}

The code is a first quick & dirty version and could - for sure - be optimized. Maybe this helps someone.

preionip commented 1 year ago

There is a important bug: case 'A': // Apr m = 8; break

Must be = 4 and miss Aug