2017年9月10日 星期日

MicroPython on ESP8266 的看門狗 WDT 無法設定時限問題

昨晚透過手機 ThingView 軟體觀察我的小型太陽能測候站, 發現資中午 11 點多就沒收到資料了, 晚飯後走到舊豬舍檢查, 發現電源燈有亮, 但狀態燈停住不閃了, 判斷應該是當機, 按一下 Reset 鈕就恢復正常了.

這讓我想到 Watchdog 功能, 程式須週期性地在 Watchdog 計時器時限內重設旗標, 否則系統就會被 Watchdog 重新啟動, 這樣系統當機時就會自動重啟, 不至於當在那邊不動. 我查詢 MicroPython 文件, 發現 pyboard 文件裡的 machine 模組有一個 WDT 看們狗物件, 參考 :

class WDT – watchdog timer

只要呼叫 WDT() 建構式即可建立一個 WDT 物件, 可傳入 timeout 參數指定看門狗計時器時限 (最低為 1 秒) :

from machine import WDT   
wdt=WDT(timeout=2000)        #計時器時限 2 秒 (timeout 參數單位 ms)

然後在程式的無限迴圈中必須在看門狗計時器時限內呼叫 WDT 物件的 feed() 方法重設旗標, 讓看門狗知道系統正常運作, 才能避免系統被它重置 :

wdt.feed()      #重設 WDT 旗標

但很可惜地, 我在 ESP8266 模組上測試, 用 dir(machine) 可以看到 WDT 模組, 但是無法傳入 timeout 參數設定計時器時限, 會出現 "does not take keword arguments", 如果不傳入參數則可以順利建立 WDT 物件與呼叫 feed(), 只是看起來沒作用而已 :

>>> import machine   
>>> dir(machine)   
['__name__', 'mem8', 'mem16', 'mem32', 'freq', 'reset', 'reset_cause', 'unique_id', 'idle', 'sleep', 'deepsleep', 'disable_irq', 'enable_irq', 'time_pulse_us', 'RTC', 'Timer', 'WDT', 'Pin', 'Signal', 'PWM', 'ADC', 'UART', 'I2C', 'SPI', 'DEEPSLEEP', 'PWRON_RESET', 'HARD_RESET', 'DEEPSLEEP_RESET', 'WDT_RESET', 'SOFT_RESET']
>>> from machine import WDT      
>>> wdt=WDT(timeout=2000) 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: function does not take keyword arguments  
>>> wdt=WDT(2000) 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError:
>>> wdt=WDT()              #不傳參數可建立物件
>>> wdt.feed()                 #也可呼叫 feed(), 但沒作用
>>> wdt.timeout=2000 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'WDT' object has no attribute 'timeout'

後來在下面這篇文章看到, 原來可設參數的 WDT 目前僅支援 WiPy 而已, 參考 :

"The docs show a WDT but that's only implemented for the WiPy (cc3200 port). In 64c5a94 I added a note to this effect."

WDT support #esp8266 #2154

後來我在下面這篇文章看到有人問為何在呼叫 machine.time_pulse_us() 時傳入參數 2 秒沒問題, 但傳入 3 秒卻會導致系統重啟? 參考 :

ESP8266 - Board auto-reset when using overlong delay machine.time_pulse_us() #2775

此函數以前沒用過, 測試我的 ESP-01 模組確實如此. 這函數是用來測量指定的 GPIO 輸入脈波的寬度用的, 咦, 這不就是之前測試超音波時想找的跟 Arduino 的 pulseIn() 一樣功能的函數嗎? 當時因為找不到, 所以只好自己寫一個來用. 參考 :

 machine.time_pulse_us() 

除了回過頭去用 time_pulse_us() 改寫超音波程式外, 這裡我關心的是 time_pulse_us() 的重啟現象是否可用在 WDT 上呢? 仔細測試結果發現, time_pulse_us() 的重啟界線似乎不穩定, 有時 2.52 秒就重啟, 有時卻超過 3 秒也沒問題, 所以沒辦法用.

後來我在下面這篇文章讀到, 原來 machine 模組本身就有一個 reset() 函數可用來在程式中透過控制來重啟系統, 參考 :

How can I reset ESP8266 MicroPython after main.py crashes?

這給了我一個靈感, time_pulse_us() 的重啟現象雖然不能用, 但是不是可用它的傳回值來控制 machine.reset() 重啟系統呢? 另一個想法是用 IRQ 來控制旗標, 根據旗標狀態來決定是否要重啟. 可行與否還要再測試看看, 這樣做與 WDT 是否意義不同呢? 等我幫二哥整理完 C 語言再回頭研究看看.

參考 :

Crash recovery, stack usage & watchdog #264
ESP8266 - Board auto-reset when using overlong delay machine.time_pulse_us() #2775


2018-04-26 補充 :

其實 ESP8266 也支持 WDT 才對, 我找到其說明文檔如下 :

class WDT – watchdog timer

有空再來試試.


2018-05-14 補充 :

有一個模擬 WDT 的方法如下 :

How can I reset ESP8266 MicroPython after main.py crashes?

2019-07-13 補充 :

MicroPython on ESP8266/ESP32 已於 v1.10 版韌體開始支援 WDT 功能了, 參考 :

MicroPython on ESP32 學習筆記 (六) : 看門狗 WDT

沒有留言 :