ดึงเวลาจาก ntp server ไม่เสถียรแล้วระบบ interrupt บ่อย ลองวิธีนี้

mrBov (Kritskon) November 28, 2020, 12:42pm #1

ใครเคยมีปัญหาการดึงเวลาจาก NTP ผ่าน wifi ที่ปล่อยจากแหล่งที่ไม่ค่อยเสถียรและหลุดบ่อย จนทำให้ interrupt ลองใช้วิธีนี้ดูครับ
หลักการคือการกำหนดให้มีการต่อ wifi และดึงค่าเวลาจาก ntp server ในครั้งแรกที่เริ่มต้นโปรแกรมเพื่อเป็นการเทียบเวลาในปัจจุบันและนำไปนับต่อด้วยโค๊ดจาก TimeKeeping No RTC หลังจาก nodemcu รันเวลาได้แล้ว เราสามารถตัดการเชื่อมต่อ wifi ได้ โดยที่ระบบยังทำงานตามรอบเวลาต่อไปอยู่ แต่อาจจะมีความคลาดเคลื่อนของเวลาบ้างเล็กน้อยในหลักวินาที

#include <ESP8266WiFi.h>
#include <time.h>

#define nodeMCU_LED D4 // Nodemcu v2 ใช้ D0 , Nodemcu v3 ใช้ D4

const char* ssid = "xxxxxxxxxxx";             // SSID is set
const char* password = "xxxxxxxxxx";     // Password is set 

char ntp_server1[20] = "1.asia.pool.ntp.org";
char ntp_server2[20] = "1.th.pool.ntp.org";
char ntp_server3[20] = "time.navy.mi.th";

int timezone = 7 * 3600;  //timezone for Thailand is +7
int dst = 0;

//Written by Ruben Marc Speybrouck
unsigned long timeNow = 0;
unsigned long timeLast = 0;

//Time start Settings:
int startingHour = 14;  // set your starting hour here, not below at int hour. This ensures accurate daily correction of time
int seconds = 0;
int minutes = 14;
int hours = startingHour;
int days = 0;

//Accuracy settings
int dailyErrorFast = 0; // set the average number of milliseconds your microcontroller's time is fast on a daily basis
int dailyErrorBehind = 0; // set the average number of milliseconds your microcontroller's time is behind on a daily basis
int correctedToday = 1; // do not change this variable, one means that the time has already been corrected today for the error in your boards crystal. This is true for the first day because you just set the time when you uploaded the sketch.

void setup() { // put your setup code here, to run once:
  
  Serial.begin(115200);
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println(WiFi.localIP());
  Serial.println("");
 
  pinMode(nodeMCU_LED,OUTPUT);

  for (int i=0; i <= 20; i++){
    configTime(timezone, dst, ntp_server1, ntp_server2, ntp_server3);
    Serial.println("\nWaiting for time");
    while (!time(nullptr)) {
      Serial.print(".");
      delay(30000);
    }   
    time_t now = time(nullptr);
    struct tm* p_tm = localtime(&now);
    //Serial.print(".");
    delay(1000);
    if(i==20){
      Serial.println("");
      Serial.print(p_tm->tm_hour); Serial.print(":");
      Serial.print(p_tm->tm_min); Serial.print(":");
      Serial.println(p_tm->tm_sec);
      Serial.println(ctime(&now));
      hours = p_tm->tm_hour;
      minutes = p_tm->tm_min;
      seconds = p_tm->tm_sec;     
    }
  }
}
void loop() { // put your main code here, to run repeatedly:


  timeNow = millis()/1000; // the number of milliseconds that have passed since boot
  seconds = timeNow - timeLast;
  
  //the number of seconds that have passed since the last time 60 seconds was reached.
  if (seconds >= 60) {
  timeLast = timeNow;
  minutes = minutes + 1; }
  
  //if one minute has passed, start counting milliseconds from zero again and add one minute to the clock.
  if (minutes >= 60){
  minutes = 0;
  hours = hours + 1; }
  
  // if one hour has passed, start counting minutes from zero and add one hour to the clock
  if (hours == 24){
  hours = 0;
  days = days + 1;
  }
  
  //if 24 hours have passed, add one day
  if (hours ==(24 - startingHour) && correctedToday == 0){
  delay(dailyErrorFast*1000);
  seconds = seconds + dailyErrorBehind;
  correctedToday = 1; }
  
  //every time 24 hours have passed since the initial starting time and it has not been reset this day before, add milliseconds or delay the program with some milliseconds.
  //Change these varialbes according to the error of your board.
  // The only way to find out how far off your boards internal clock is, is by uploading this sketch at exactly the same time as the real time, letting it run for a few days
  // and then determining how many seconds slow/fast your boards internal clock is on a daily average. (24 hours).
  if (hours == 24 - startingHour + 2) {
  correctedToday = 0; }
  
  //let the sketch know that a new day has started for what concerns correction, if this line was not here the arduiono // would continue to correct for an entire hour that is 24 - startingHour.
  Serial.print("The time is: ");
  Serial.print(days);
  Serial.print(":");
  Serial.print(hours);
  Serial.print(":");
  Serial.print(minutes);
  Serial.print(":");
  Serial.println(seconds);

  digitalWrite(nodeMCU_LED,HIGH);
  delay(500);
  digitalWrite(nodeMCU_LED,LOW);
  delay(500);
}

โห้เยี่ยมไปเลยสามารถนำไปต่อยอดให้อัพเดทเวลาที่อาจจะคลาดเคลื่อนไป ทุก 3 วัน ก็ดีนะนิ ได้ไอเดียร์เลย ขอบคุณครับ

ขอบคุณครับ ตอนนี้ผมใช้งานโค๊ดนี้แบบจริงจังที่บ้านสวนครับ ที่ไม่มีไฟฟ้าพื้นฐาน และอินเตอร์เน็ต ใช้พลังงานจากโซล่าเซลล์ และมือถือ 4G ปล่อย Wifi เดี๋ยวจะขออนุญาติเอาโค๊ดที่ใช้ทำงาน สั่งเปิดปั๊มรดน้ำต้นไม้ เปิดปิดไฟตามเวลา สั่งงานผ่านแอพ Blynk และแจ้งเตือนผ่าน Line Notify มารบกวนให้เพื่อนสมาชิกช่วย Optimize Code ให้ครับ

โอเคครับผมมาช่วยกัน ครับ

อ่านแล้วเข้าใจเลยครับ​ ขอบคุณครับ