# MicroPython on ESP8266 (七) : 時間日期測試
在 v.11 版韌體與 ESP32 上其實跟 ESP8266 一樣, 即 CPython 上的 datetime 與 calendar 模組都沒有移植過來, 只實作了 time 模組而已, 用 dir() 檢視 time 模組內容可知 time 模組甚至也沒有實作 ctime() 方法, 與日期時間操作有關的方法只有 time(), mktime() 與 localtime() 三個方法可用而已 :
>>> import time
>>> dir(time)
['__class__', '__name__', 'localtime', 'mktime', 'sleep', 'sleep_ms', 'sleep_us', 'ticks_add', 'ticks_cpu', 'ticks_diff', 'ticks_ms', 'ticks_us', 'time']
其中 time() 預設傳回 2000 年 1 月 1 日 0 時 0 分 0 秒起算的秒數 (稱為 epoch), localtime() 方法則傳回其 tuple 型態之日期時間; 而 mktime() 則反過來將 tuple 型態之日期時間轉回 epoch (秒數). 而 sleep() 則是最常用的延遲函數, 其傳入參數為秒數, 同理 sleep_ms() 為毫秒 (ms), 而 sleep_us() 為微秒 (us).
>>> time.time() #傳回 epoch 秒數
294
>>> time.localtime() #傳回 RTC 的日期時間 tuple
(2000, 1, 1, 0, 5, 4, 5, 1)
>>> time.mktime(time.localtime()) #將日期時間 tuple 轉成 epoch 秒數
314
注意, time.localtime() 的傳回值定義如下 :
(年, 月, 日, 時, 分, 秒, 星期, 年日,日光節約時間)
其中年日為一年中的第幾天 (1~365/366); 星期為 0~6 的整數, 0 代表星期一, 1 代表星期二, .... 6 代表星期日,日光節約時間=0 表示無.
關於 time 模組之方法參考 :
# https://docs.micropython.org/en/latest/library/utime.html#functions
# Python time localtime()方法
# http://www.runoob.com/python/python-date-time.html
# https://docs.micropython.org/en/latest/library/machine.RTC.html
>>> import machine
>>> dir(machine)
['__class__', '__name__', 'ADC', 'DAC', 'DEEPSLEEP', 'DEEPSLEEP_RESET', 'EXT0_WAKE', 'EXT1_WAKE', 'HARD_RESET', 'I2C', 'PIN_WAKE', 'PWM', 'PWRON_RESET', 'Pin', 'RTC', 'SDCard', 'SLEEP', 'SOFT_RESET', 'SPI', 'Signal', 'TIMER_WAKE', 'TOUCHPAD_WAKE', 'Timer', 'TouchPad', 'UART', 'ULP_WAKE', 'WDT', 'WDT_RESET', 'deepsleep', 'disable_irq', 'enable_irq', 'freq', 'idle', 'lightsleep', 'mem16', 'mem32', 'mem8', 'reset', 'reset_cause', 'sleep', 'time_pulse_us', 'unique_id', 'wake_reason']
>>> machine.RTC # RTC 是一個類別
<class 'RTC'>
>>> dir(machine.RTC) # RTC 的成員
['__class__', '__name__', 'datetime', 'init', 'memory']
可見 RTC 類別有三個方法 :
machine.RTC 物件方法 | 說明 |
datetime([datetime]) | 查詢或設定 RTC 日期時間 (傳入 list 或 tuple) |
init(datetime) | 設定 RTC 日期時間 (傳入 list 或 tuple) |
memory([bytes]) | 查詢或設定 RTC 記憶體儲存之資料 (bytes 類型) |
注意, init() 與 datetime() 都可以傳入一個 8 元素的 tuple 或 list 來設定 RTC, 其定義為 :
(年, 月, 日, 星期, 時, 分, 秒, 毫秒)
其中星期為 0~6 的整數, 0 代表星期一, 1 代表星期二, .... 6 代表星期日, 例如 :
>>> rtc = machine.RTC() #建立 RTC 物件
>>> rtc.init((2019, 7, 10, 2, 14, 50, 0, 0)) #初始化 RTC>>> rtc.datetime() #查詢 RTC
(2019, 7, 10, 2, 14, 50, 1, 519033)
>>> rtc.datetime() #查詢 RTC
(2019, 7, 10, 2, 14, 50, 6, 408877)
>>> rtc.datetime() #查詢 RTC
(2019, 7, 10, 2, 14, 50, 9, 538456)
>>> rtc.datetime([2019, 7, 10, 2, 14, 50, 0, 0]) #設定 RTC
>>> rtc.datetime() #查詢 RTC
(2019, 7, 10, 2, 14, 50, 2, 339379)
>>> rtc.datetime() #查詢 RTC
(2019, 7, 10, 2, 14, 50, 8, 349071)
RTC 有一個 ROM 記憶體 (512 bytes) 可用 memory() 方法來儲存暫時的資料 (重啟會保留, 但拔電源會消失), 注意, 此記憶體要用二進位 bytes 型態儲存 :
>>> rtc.memory() #查詢 RTC 記憶體
b''
>>> rtc.memory(b'hello') #設定 RTC 記憶體
>>> rtc.memory() #查詢 RTC 記憶體
b'hello'
# Insight Into ESP32 Sleep Modes & Their Power Consumption
由上可知, ESP32 的 RTC 模組必須經過初始化校準時間才能使用. 使 ESP32 擁有正確日期時間的方法是利用 MicroPython 內建的 ntptime 模組來與 NTP 伺服器同步, 此模組有一個 settime() 方法讓內部時鐘與網路上的 NTP 伺服器時間同步, 因此在呼叫 ntptime.settime() 之前要先讓 ESP32 的 STA 介面連上 Internet, 我將上一篇測試中的 main.py 程式加上 pre0() 與 now() 函數如下 :
#main.py
import network
import ubinascii
import time
import ntptime
def connect(ssid, pwd):
sta.connect(ssid, pwd)
print('Connecting to WiFi AP=', ssid, ' ...')
time.sleep(8)
if sta.isconnected():
print('Connected: ', sta.ifconfig()[0])
else:
print('Can not connect to AP=' + ssid)
def disconnect():
sta.disconnect()
return True
def scan():
aps=sta.scan()
for ap in aps:
ssid=ap[0].decode()
mac=ubinascii.hexlify(ap[1], ':').decode()
rssi=str(ap[3]) + 'dBm'
print('{:>20} {:>20} {:>10}'.format(ssid, mac, rssi))
def ip():
return sta.ifconfig()[0]
def pre0(n):
if n<10:
return '0' + str(n)
else:
return str(n)
def now():
ntptime.settime()
utc_epoch=time.mktime(time.localtime())
Y,M,D,H,m,S,ms,W=time.localtime(utc_epoch + 28800)
t='%s-%s-%s %s:%s:%s' % (str(Y),pre0(M),pre0(D),pre0(H),pre0(m),pre0(S))
return t
ap=network.WLAN(network.AP_IF)
ap.active(True)
ap.config(authmode=4, password='micropythoN')
sta=network.WLAN(network.STA_IF)
sta.active(True)
上面新增的 pre0() 函數用來將個位數之月, 日, 時, 分, 秒前面冠上 '0' 補足為兩個字元以使日期時間格式整齊劃一, 而 now() 函數則用來把與 NTP 伺服器同步後之內部時鐘的日期時間以 "YYYY-MM-DD HH:mm:SS" 格式字串傳回. 這裡傳回的是台灣時間, 因為台灣時間是 UTC+8, 8 小時是 28800 秒 (8*60*60=28800), 所以 now() 函數中傳入 localtime() 的參數是 utc_epoch + 28800, 如果要傳回東京時間就要改為 utc_epoch + 32400, 因為東京時間是 UTC + 9.
將此修改後的 main.py 用 ampy 上傳到 ESP32 後按 Ctrl+D 重啟, 用 dir() 檢視記憶體中的物件如下 :
>>> dir()
['now', 'ap', 'connect', 'ntptime', 'gc', 'sta', 'scan', 'bdev', 'time', 'ubinascii', '__name__', 'disconnect', 'webrepl', 'network', 'ip', 'pre0', 'uos']
>>> connect('TonyNote8','blablabla')
Connecting to WiFi AP= TonyNote8 ...
I (92904) wifi: new:<11,2>, old:<11,0>, ap:<11,2>, sta:<11,0>, prof:11
I (92904) wifi: state: init -> auth (b0)
I (92904) wifi: state: auth -> assoc (0)
I (92914) wifi: state: assoc -> run (10)
I (93094) wifi: connected with TonyNote8, channel 11, bssid = 06:d6:aa:04:fc:4a
I (93094) wifi: pm start, type: 1
I (93094) network: CONNECTED
I (94154) event: sta ip: 192.168.43.177, mask: 255.255.255.0, gw: 192.168.43.1
I (94154) network: GOT_IP
Connected: 192.168.43.177
>>> now() #取得與網路時鐘同步之日期時間
'2019-07-09 23:32:41'
>>> disconnect() #離線
I (853654) wifi: state: run -> init (0)
I (853654) wifi: pm stop, total sleep time: 710885777 us / 760555821 us
I (853654) wifi: new:<11,0>, old:<11,2>, ap:<11,2>, sta:<11,0>, prof:11
True
>>> I (853664) wifi: STA_DISCONNECTED, reason:8
>>> now() #離線狀態下無法取得網路時鐘
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "main.py", line 37, in getTime
File "ntptime.py", line 30, in settime
File "ntptime.py", line 18, in time
IndexError: list index out of range
# [錦囊XV] NTP (網路校時) 服務時好時壞怎麼辦?
# Python machine.RTC() Examples
# http://www.1zlab.com/wiki/micropython-esp32/rtc/
# Setting RTC From Internet?
2019-07-15 補充 :
bpi:bit 關於 ntptime 的說明文件 :
# https://bpi-steam-docs.readthedocs.io/zh_CN/latest/mPython/docs/library/micropython/ntptime.html
2019-07-20 補充 :
由於 NTP 伺服器的 UDP 協定不保證能收到回應封包, 呼叫 ntptime.settime() 有時會出現錯誤, 因此要放在 try-except 裡面做例外處理, now() 函數修改如下 :
def now():
from ntptime import settime
try:
settime()
except:
pass
utc_epoch=time.mktime(time.localtime())
Y,M,D,H,m,S,ms,W=time.localtime(utc_epoch + 28800)
t='%s-%s-%s %s:%s:%s' % (str(Y),pre0(M),pre0(D),pre0(H),pre0(m),pre0(S))
return t
參考下面這篇最底下的補充 :
# MicroPython on ESP32 學習筆記 (七) : socket 與網頁伺服器
沒有留言:
張貼留言