دریافت ساعت و تاریخ از سرور NTP با ESP32 و ESP8266

فهرست مطالب

مقدمه

در پروژه های الکترونیکی انجام عملیات در یک زمان خاص یا نشان دادن زمان به کاربر همیشه یکی از چالش های پیش رو بوده است. معمولا اولین راه حل برای عبور از این چالش استفاده از آیسی های RTC مانند DS1307 می باشد (برای اطلاعات بیشتر می توانید به “چگونه از DS1307 با آردوینو استفاده کنیم و یک یادآور هوشمند بسازیم؟” مراجعه کنید)، که معمولا این آیسی ها دقت خیلی بالایی ندارند. یکی دیگر از راه حل های عبور از این چالش که نسبت به مورد قبلی جدیدتر و البته خیلی دقیق تر است، دریافت ساعت و تاریخ از یک سرور مرکزی از طریق اینترنت می باشد.

آنچه در این آموزش یاد می گیرید

NTP چیست و چگونه کار می کند؟

کلمه NTP مخفف Network Time Protocol است و یک استاندارد جهانی اینترنتی برای همگام سازی ساعت دستگاه ها می باشد. NTP ساعت دستگاه ها را بر اساس ساعت UTC با 50 میلی ثانیه خطا بر اساس موقعیت جغرافیایی و قانون ذخیره روشنایی روز (day light saving)، تنظیم می کند.
اما NTP چگونه کار می کند؟
NTP یک ساختار سلسله مراتبی دارد، در سطح اول ساعت دقیق از ابزارهای فوق دقیقی مانند ساعت GMT یا ساعت های اتمی دریافت می شود، سطح دوم بعنوان یک سطح واسط ساعت دریافت شده از سطح اول را در بستر اینترنت قرار می دهد، سطح سوم هم دستگاه های درخواست کننده می باشند که از سطح دو ساعت و تاریخ را طلب می کنند.

پکیج ارسالی از سمت سطح دو به سطح سه را با توجه به جدول زیر می توان ترجمه کرد:

لوازمی که به آن احتیاج دارید

قطعات مورد نیاز

ماژول وای فای و بلوتوث دو هسته ای ESP32S-WROOM × 1
ماژول نمایشگر 0.96 اینچ OLED دارای ارتباط I2C × 1
برد توسعه NodeMcu به همراه ماژول وای فای ESP8266 × 1
سیم جامپر نری به مادگی × 1

نرم افزار های مورد نیاز

Arduino IDE

دریافت ساعت و تاریخ از سرور NTP با ESP8266

حال کد زیر را روی برد ESP8266 خود ریخته و نتیجه را در Serial Monitor مشاهده کنید:

/*
  NTP Clock with NodeMCU 
  modified on 18 DEC 2019
  by Saeed Hosseini @ Electropeak
  
Home
*/ #include <NTPClient.h> #include <ESP8266WiFi.h> #include <WiFiUdp.h> const char *ssid = "YOUR SSID"; const char *password = "YOUR SSID PASSWORD"; const long utcOffsetInSeconds = 12600; char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; // Define NTP Client to get time WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, "pool.ntp.org", utcOffsetInSeconds); void setup(){ Serial.begin(115200); WiFi.begin(ssid, password); while ( WiFi.status() != WL_CONNECTED ) { delay ( 500 ); Serial.print ( "." ); } timeClient.begin(); } void loop() { timeClient.update(); Serial.print(daysOfTheWeek[timeClient.getDay()]); Serial.print(", "); Serial.print(timeClient.getHours()); Serial.print(":"); Serial.print(timeClient.getMinutes()); Serial.print(":"); Serial.println(timeClient.getSeconds()); //Serial.println(timeClient.getFormattedTime()); delay(1000); }

در ابتدا کتابخانه های لازم را به کد اضافه می کنیم، سپس نام و رمز روتر وایفای خود را درج می کنیم.

متغیر utcOffsetInSeconds را با توجه به موقعیت مکانی خود تغییر دهید، می توانید ضریب UTC کشور خود را در اینجا پیدا کنید.

بعنوان مثال ضریب UTC برای کشور آمریکا به این صورت محاسبه می شود:

UTC = -11:00

utcOffsetInSeconds = -11*60*60 = -39600

سرور ما برای دریافت NTP سرور pool.ntp.org می باشد.

در ادامه پس از اتصال به اینترنت با شی timeclient می توانید تاریخ و ساعت را دریافت کنیم.

دریافت ساعت و تاریخ از سرور NTP با ESP32

اگر با بردهای ESP32 و نحوه اضافه کردن و برنامه نویسی آنها با Arduino IDE آشنا نیستید به ” راه اندازی ماژول وای فای و بلوتوث ESP32 و نصب آن بر IDE آردوینو” مراجعه کنید.

حال کد زیر را روی ESP32 خود ریخته و نتیجه را Serial Monitor مشاهده کنید.

#include <WiFi.h>
#include "time.h"

const char* ssid       = "YOUR_SSID";
const char* password   = "YOUR_PASS";

const char* ntpServer = "pool.ntp.org";
const long  gmtOffset_sec = 12600;
const int   daylightOffset_sec = 3600;

void printLocalTime()
{
  struct tm timeinfo;
  if(!getLocalTime(&timeinfo)){
    Serial.println("Failed to obtain time");
    return;
  }
  Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
}

void setup()
{
  Serial.begin(115200);
  
  //connect to WiFi
  Serial.printf("Connecting to %s ", ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
  }
  Serial.println(" CONNECTED");
  
  //init and get the time
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  printLocalTime();

  //disconnect WiFi as it's no longer needed
  WiFi.disconnect(true);
  WiFi.mode(WIFI_OFF);
}

void loop()
{
  delay(1000);
  printLocalTime();
}

در اینجا نیز مانند پروژه NodeMcu ابتدا تنظیمات وایفای را انجام می دهیم.

مانند قبل gmtOffset_sec را محاسبه کرده و تغییر می دهیم، درمورد متغیر daylightOffset_sec اگر کشور شما جزو کشورهایی است که در آن قانون تغییر ساعت وجود دارد (می توانید لیست این کشورها را در اینجا ببینید) مقدار آن را 3600 قرار دهید و درغیر اینصورت مقدار 0 قرار دهید. سرور NTP نیز مانند قبل pool.ntp.org می باشد. تابع printLocalTime() نیز طبق جدول ساختار NTP اطلاعات لازم را از شی timeinfo استخراج می کند و نمایش می دهد.

ساخت ساعت دقیق با استفاده از OLED و ESP32

برای زیباتر و عملی تر شدن پروژه ساعت با NTP آنرا روی نمایشگر OLED 0.96” I2C نمایش می دهیم.

سیم بندی

کد

کد زیر را روی ESP32 خود آپلود کنید  و نتیجه را روی نمایشگر ملاحظه کنید.

logo.h

/*
  NTP Clock with ESP32 and OLED 
  modified on 18 DEC 2019
  by Saeed Hosseini @ Electropeak
  
Home
*/ #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #include "logo.h" #include <WiFi.h> #include "time.h" #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); const char* ssid = "CafeRobot Inc."; const char* password = "caferobot.ir"; const char* ntpServer = "pool.ntp.org"; const long gmtOffset_sec = 12600; const int daylightOffset_sec = 3600; void diplay_logo(int x, int y, const uint8_t *bitmap, int w, int h) { display.drawBitmap(x, y, bitmap, w, h, WHITE); } void display_text(int sz, int x, int y, String str) { display.setTextSize(sz); display.setTextColor(WHITE); display.setCursor(x, y); display.println(str); } void display_number(int sz, int x, int y, double num) { display.setTextSize(sz); display.setTextColor(WHITE); display.setCursor(x, y); display.println(num); } void system_setup() { Serial.begin(115200); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); display.clearDisplay(); diplay_logo(13, 20, Electropeak, 128, 32); display.display(); delay(1000); display.clearDisplay(); // while (WiFi.status() != WL_CONNECTED) { // loading(); // } } void loading() { display_text(2, 0, 25, "Loading"); display_text(2, 85, 25, "."); display.display(); delay(500); display_text(2, 95, 25, "."); display.display(); delay(500); display_text(2, 105, 25, "."); display.display(); delay(500); display.fillRect(85, 25, 40, 20, BLACK); display.display(); delay(500); } void printLocalTime() { String s = ""; char h [80]; char m [80]; char p [80]; struct tm timeinfo; if (!getLocalTime(&timeinfo)) { Serial.println("Failed to obtain time"); return; } Serial.println(&timeinfo, "%H:%M:%S"); strftime (h, 80, "%I", &timeinfo); strftime (m, 80, "%M", &timeinfo); strftime (p, 80, "%p", &timeinfo); display_text(3, 15, 30, h); display_text(3, 50, 30, ":"); display_text(3, 65, 30, m); display_text(1, 108, 45, p); display.display(); } void setup() { system_setup(); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { loading(); } display.clearDisplay(); diplay_logo(0, 0, Wifi, 16, 16); display.display(); configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); printLocalTime(); //disconnect WiFi as it's no longer needed WiFi.disconnect(true); WiFi.mode(WIFI_OFF); } void loop() { printLocalTime(); delay(10000); display.clearDisplay(); diplay_logo(0, 0, Wifi, 16, 16); display.display(); }

تنها تفاوت این کد با کد قبل در این است که با شی strftime مقادیر ساعت، دقیقه و AM/PM را از timeinfo جدا کرده و در رشته ای ذخیره می کنیم و سپس آنها را با فرمت ساعت در نمایشگر نشان می دهیم.

یک گام جلوتر

  • سعی کنید تاریخ را نیز درنمایشگر نشان دهید.
  • سعی کنید با ساعت NTP یک آلارم هوشمند بسازید.

آموزش های مشابه

Comments (19)

  • آرمین Reply

    سلام
    مممنون پروژه خیلی بدرد بخوری بود فقط ، یک راهنمایی می کنید چطور UTC ایران رو از همون ویکی پدیا برداریم . و لینک <> هم کار نمی کنه .

    ممنون از کافه ربات 🙂

    آگوست 17, 2021 at 11:29 ب.ظ
    • مهران ملکی Reply

      سلام.
      UTC به این معناست که منطقه و کشوری که شخص زندگی میکنه چند ساعت با مبدا جهانی که گرینویچ هست اختلاف داره. برای ایران این عدد 3:30 هست. به این معنا که ما 3ساعت و نیم از گرینویچ جلوتر هستیم. این رو میتونید با سرچ عبارت iran UTC در گوگل هم ببینید. در لینک ویکیپدیا گذاشته شده در آموزش هم این عدد پیدا میشه. برای محاسبه اون متغیر utcOffsetInSeconds هم کافیه که 3ساعت و نیم رو به ثانیه تبدیل کنید: 3.5 * 60 * 60 که همون 12600 میشه. منظورتون از لینک <> رو متوجه نشدم.

      آگوست 21, 2021 at 12:52 ب.ظ
  • مهدی Reply

    در خط 79 برای نمایش در oled دستور getlocaltime ارور not declared داده میشه میخواستم راهنمایی کنید

    آوریل 30, 2022 at 5:26 ب.ظ
    • مهران ملکی Reply

      سلام،
      مشکلی در کد وجود نداره. این ایراد میتونه از اشتباه انتخاب شدن بُرد باشه، بررسی کنید که بردتون رو به درستی برد ESP32 انتخاب کرده باشید.

      آوریل 30, 2022 at 10:33 ب.ظ
      • مهدی Reply

        بله برد esp 32است
        چگونه میشه برد را ریست کرد؟

        آوریل 30, 2022 at 10:41 ب.ظ
        • مهران ملکی Reply

          منظور از انتخاب درست برد، انتخاب اون در نرم‌افزار Arduino IDE هست. برد رو میتونید در قسمت Tools -> Board -> ESP32 Arduino انتخاب کنید. اگر در این حالت هم مشکلتون برطرف نشد، میتونید برای بررسی بیشتر مشکلتون رو همراه با فرستادن عکس و اطلاعات لازم در قسمت انجمن مطرح کنید. آدرس انجمن: https://thecaferobot.com/forum/
          برای ریست کردن برد ESP32 یک دکمه روی خود برد تعبیه شده که میتونید اون رو فشار بدید.

          می 3, 2022 at 11:18 ق.ظ
  • مانی Reply

    سلام وقت بخیر
    ممنون بابت این پروژه عالی
    چطور میشه با همین تایم سرور یا سرور ایرانیش (ir.pool.ntp.org) تاریخ شمسی رو دریافت کرد؟
    و اینکه چطور میشه از اعداد ساعت و دقیقه برای دستورات if استفاده کرد؟
    با تشکر.

    ژوئن 9, 2022 at 1:33 ب.ظ
    • مهران ملکی Reply

      سلام.
      متاسفانه تجربه‌ای در رابطه با دریافت تاریخ شمسی با ماژول ESP32 ندارم، اما روش‌هایی برای تبدیل تاریخ میلادی به شمسی وجود داره که میتونید در صورتی که نتونستید تاریخ شمسی رو به طور مستقیم دریافت کنید خودتون اون رو محاسبه کنید.
      برای استفاده از اعداد ساعت و دقیقه هم، موقعی که زمان رو از سرور دریافت میکنید به صورت یک struct دریافت میشه و داخل اون به مقدار ساعت و دقیقه هم دسترسی دارید. همونطوری که از اون‌ها در کد دوم برای نوشتن روی نمایشگر استفاده شده. با دسترسی به این مقادیر میتونید به راحتی ازشون در دستورات if یا سایر دستورات استفاده کنید.

      ژوئن 10, 2022 at 1:11 ب.ظ
  • مانی Reply

    سلام ممنون از راهنماییتون.
    وقتی میخوام m رو در دستور if قرار بدم که اگر برابر با 20 بود یک ال ای دی روشن کنه با این ارور مواجه میشم

    ISO C++ forbids comparison between pointer and integer [-fpermissive]

    در کد هایی که قرار دادید m به این صورت تعریف شده strftime (m, 80, “%M”, &timeinfo);
    خیلی از راه ها رو امتحان کردم اما باز هم با ارور اینکه char با int قابل مقایسه نیست برخورد کردم.
    اعداد رو داخل “20” و ’20’ گذاشتم فقط ارور رو برطرف کرد اما همچنان برنامه به درستی اجرا نشد.
    بنظرتون از چه روش دیگری می تونم دقیقه و ساعت رو به صورت int و یا عدد تعریف کنم که بتونم در دستورات if قرار بدم؟

    ژوئن 15, 2022 at 12:41 ق.ظ
    • مهران ملکی Reply

      سلام.
      خواهش میکنم.
      از لینک زیر میتونید کمک بگیرید احتمالا مشکتون رو برطرف کنه.
      https://www.delftstack.com/howto/arduino/arduino-char-to-int/
      با این روش ابتدا char رو به string و بعد از اون string رو میتونید به int تبدیل کنید.

      ژوئن 19, 2022 at 10:45 ب.ظ
  • حسن نیک خو Reply

    با سلام / احتراما برنامه دریافت زمان و تاریخ از اینترنت برای برد esp-32 رو اجرا کردم و به وای فای نیز متصل میشود ولی متاسفانه پیام خطای Failed to obtain time رو می دهد . میشه راهنمایی بفرمائید آیا باید در مودم با تنظیمات خاصی انجام داد یا خیر . ممنون

    جولای 20, 2022 at 10:36 ب.ظ
  • دلیر Reply

    با سلام
    برای استفاده ساعت و دقیقه در شرط مشکلی ندارم
    ولی استفاده از روز در شرط مشکلی هست به نظر شما اگر خواسته باشیم روز را در شرط استفاده کنیم به صورت این کار رو انجام بدم
    با تشکر

    دسامبر 16, 2022 at 11:07 ب.ظ
    • علی عبدالملکی Reply

      سلام
      شما از همون زیر تابع getDay میتونید استفاده کنید

      فوریه 27, 2023 at 6:26 ب.ظ
  • ابوالفضل درویشی Reply

    سلام میتونم کد برد ESP32 را برای node mcu esp8266 استفاده کنم

    جولای 10, 2023 at 5:24 ب.ظ
    • محمد دمیرچی Reply

      با سلام
      کد های شبکه wifi در ماژول های esp8266 یا esp32 نزدیک به هم میباشند اما فرق دارند از این رو نمیتوانید مستقیم اجرا کنید.
      آموزش را توجه کنید کد را برای هر دو مدل قرار داده شده است.

      جولای 12, 2023 at 11:10 ق.ظ
  • ۰۰۰۰۰ Reply

    چطوری میتونم به ساعت تاریخی هم اضافه کنم

    جولای 10, 2023 at 5:28 ب.ظ
    • محمد دمیرچی Reply

      دیتا های دریافت تاریخ نیز به صورت سریال نمایش داده می شود.
      در صورتی که میخواهید در نمایشگر نیز نمایش داده شود کافی است مشابه رقم های ساعت آنها را نیز در جایی از صفحه قرار بدهید و نمایش بدهید.

      جولای 12, 2023 at 11:13 ق.ظ
  • مهدی نویسنده Reply

    سلام
    می‌شه مقدارهای دریافتی رو به انواع دیگه مثل uint8_t تبدیل کرد؟ می‌خوام روی ماژول rtc ست کنم

    فوریه 25, 2024 at 12:56 ب.ظ
    • محمد دمیرچی Reply

      با سلام
      بله می توانید تغییر بدهید. البته این را نیز در نظر داشته باشید که برای ست کردن در RTC شما می توانید از مقدار unix Time یا همون Epoch نیز استفاده کنید.

      فوریه 26, 2024 at 10:31 ق.ظ

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد.