9.16. เวลาและ NTP

กล้องที่เพิ่งเปิดใช้งานใหม่ไม่ทราบว่าเวลาปัจจุบันคืออะไร นาฬิกาภายในเครื่องเริ่มต้นจากจุดที่กำหนดโดยพลการ (1970-01-01 บนบอร์ดส่วนใหญ่) และนับเพิ่มขึ้นจากนั้นจนกว่าจะมีสิ่งใดมาบอกให้เปลี่ยนแปลง NTP -- Network Time Protocol -- คือวิธีที่กล้องขอเวลานาฬิกาจริงจากเครือข่ายและตั้งนาฬิกาของตัวเองตามคำตอบที่ได้รับ

9.16.1. เหตุใดกล้องจึงต้องรู้เวลา

สำหรับสคริปต์หลายอย่าง นาฬิกาของกล้องไม่สำคัญ -- ลูปจับภาพเฟรมไม่สนใจว่าวันนี้เป็นวันอะไร แต่สำหรับบางสิ่งที่พบบ่อย เวลาสำคัญมาก:

  • การประทับเวลาในล็อกหรือข้อมูลที่อัปโหลด รายการที่ระบุว่า 1970-01-01 ทั้งหมดนั้นเข้าใจยากในภายหลัง

  • งานตามตารางเวลา "รันเวลา 03:00" ต้องให้กล้องรู้ว่าเวลา 03:00 คือเมื่อไหร่

9.16.2. NTP ทำงานอย่างไร

NTP เป็นบริการสาธารณะขนาดเล็ก ประกอบด้วยเครือข่ายเซิร์ฟเวอร์ที่ตอบคำถาม "เวลาเท่าไหร่แล้ว?" ผ่านการแลกเปลี่ยน UDP ครั้งเดียว กล้องส่งคำขอสั้น ๆ ไปยังเซิร์ฟเวอร์ NTP ที่รู้จัก เซิร์ฟเวอร์ตอบกลับพร้อมการประทับเวลาที่แม่นยำ (แม่นยำถึงไม่กี่มิลลิวินาทีสำหรับเซิร์ฟเวอร์สาธารณะทั่วไป) จากนั้นกล้องใช้ข้อมูลนั้นตั้งนาฬิกาของตัวเอง เซิร์ฟเวอร์เริ่มต้นที่กล้องใช้คือ pool.ntp.org ซึ่งเป็นกลุ่มเซิร์ฟเวอร์ที่กระจายโหลดทั่วโลก ออกแบบมาเพื่อไคลเอนต์ประเภทนี้โดยเฉพาะ

9.16.3. Python API: ntptime

MicroPython รวมโปรโตคอลไว้ในการเรียกใช้ครั้งเดียว รูปแบบที่ใช้ทั่วไปคือเชื่อมต่อลิงก์ก่อน จากนั้นจึงขอเวลาจาก NTP:

import network
import ntptime
import time

wlan = network.WLAN(network.WLAN.IF_STA)
wlan.active(True)
wlan.connect("my-network", "my-password")

while not wlan.isconnected():
    time.sleep_ms(100)

ntptime.settime()                 # cam's clock is now UTC
print(time.localtime())

หลังจาก ntptime.settime() คืนค่าแล้ว นาฬิกาเรียลไทม์ภายในเครื่องและ time.localtime() จะสะท้อนเวลา UTC ปัจจุบัน มีตัวปรับสองตัวสำหรับค่าเริ่มต้น:

  • ntptime.host คือชื่อเซิร์ฟเวอร์ที่จะสอบถาม กำหนดค่าใหม่ (ntptime.host = "time.google.com") ก่อนเรียก settime() เพื่อชี้ไปยังเซิร์ฟเวอร์อื่น

  • ntptime.timeout คือจำนวนวินาทีที่รอการตอบกลับก่อนยกเลิก ค่าเริ่มต้นสั้นมาก

9.16.4. ควรเรียกใช้เมื่อใด

  • หลังจากลิงก์เครือข่ายทำงานแล้ว NTP ทำงานบน UDP ซึ่งทำงานบน IP setup ที่ตั้งค่าไว้แล้ว รอให้ isconnected() คืนค่า True ก่อน

  • เป็นระยะสำหรับกล้องที่ทำงานนาน นาฬิกาภายในเครื่องจะเดินช้าลงตามชั่วโมงและวัน การเรียก settime() รายวันหรือรายสัปดาห์จะรักษาความแม่นยำ

9.16.5. เขตเวลา

NTP คืนเวลา UTC MicroPython ไม่มีฐานข้อมูลเขตเวลา ดังนั้นการแปลง UTC เป็นเวลาท้องถิ่นจึงเป็นหน้าที่ของสคริปต์ วิธีที่ใช้บ่อยคือใช้ค่าชดเชยคงที่สำหรับเขตเวลาของการใช้งาน:

import time

offset = -5 * 3600                  # hours -> seconds, US Eastern
local = time.localtime(time.time() + offset)
print(local)

วิธีนี้ไม่รองรับการปรับเวลาตามฤดูกาล (Daylight Saving Time) วินาทีอธิกสุรทิน และการเปลี่ยนแปลงเขตเวลาในประวัติศาสตร์ สำหรับการใช้งานกล้องส่วนใหญ่ ค่าชดเชยคงที่ก็เพียงพอแล้ว หากสคริปต์ต้องการเวลาพลเรือนพร้อมการปรับเวลาตามฤดูกาลจริง ๆ ให้ทำการแปลงฝั่งเซิร์ฟเวอร์แทน

9.16.6. สิ่งที่อาจผิดพลาด

  • ยังไม่มีเครือข่าย ntptime.settime() จะส่ง OSError หากไม่สามารถเข้าถึงเซิร์ฟเวอร์ได้ ไม่ว่าจะเป็นเพราะลิงก์ยังไม่ทำงาน การค้นหาชื่อล้มเหลว เซิร์ฟเวอร์ไม่สามารถเข้าถึงได้ หรือไม่มีการตอบกลับภายใน ntptime.timeout ลองใหม่เมื่อลิงก์เสถียร

  • Captive portals เครือข่าย Wi-Fi ที่ดักจับ DNS อาจตอบชื่อเซิร์ฟเวอร์ NTP ด้วย IP ของพอร์ทัลเอง และคำขอ NTP ที่ส่งไปจะได้รับข้อมูลที่ผิดพลาด กล้องจะคิดว่าเครือข่ายทำงานปกติแต่การตั้งเวลาจะล้มเหลวหรือผิดพลาดอย่างมาก ให้เปลี่ยนไปใช้เครือข่ายที่ไม่มีการดักจับหรือระบุ IP แบบตายตัว

  • การส่งคำขอถี่เกินไปไปยัง public pools NTP pools สาธารณะมีการจำกัดอัตราสำหรับไคลเอนต์ที่ใช้งานมากเกินไป การส่งคำขอหนึ่งครั้งต่อชั่วโมงเพียงพอแล้ว หากส่งทุกนาทีกล้องอาจถูกแบน

สำหรับข้อมูลอ้างอิง ntptime ฉบับสมบูรณ์ โปรดดู ntptime --- ไคลเอนต์ NTP แบบง่าย