定時執行!在 NodeMCU (ESP8266) 這樣做!

用 nodemcu-timer 模組,輕鬆開發你的定時機制!

原作者 : simen・編輯修改: Ivan Chang

一、前言

今天要跟大家介紹一個可使用在「NodeMCU (on ESP8266 WiFi SoC)」的定時模組。不過在這之前,先跟各位簡單介紹一下NodeMCU與ESP8266,ESP8266是在最近超夯的MediaTek LinkIt Smart 7688推出之前,Maker最愛用的 WiFi 模組。而 NodeMCU 則是一個開源式物聯網平台,NodeMCU 包含了可以跑在 ESP8266 Wi-Fi SoC 晶片之上的韌體,以及基於 ESP-12 模組的硬體。讓開發者可以利用簡單的Lua Script Language在ESP8266上編寫韌體,更快速的實作出有趣的物聯網應用。

回到本文要介紹的主角是一個定時模組(Timer Module),它的API 使用了 Javascript 風格,也就是 setTimeout(), setInterval() 這樣的介面,相信這對寫 Javascript 或 Node.js 的朋友,應該是再熟悉不過啦~

其實 NodeMCU 的 tmr 模組所提供的 7 個定時器,在一般的定時應用已經蠻足夠了,只不過,用起來還是會有一點小煩阿。如果現在同時只有 2, 3 個定時工作要跑,那還OK。但如果你現在同時有10個、20個定時工作要跑,那你肯定是要GG一陣子,你必須得好好管理這7個定時器,誰在重複執行、誰在timeout後釋放了。接著,你就需要在定時工作中,用一、兩個變數充當子計數器,結果搞得程式碼完全爛掉,而且還不能重用,你說痛不痛苦。
 
這個模組的出現,其實是我在另一個專案上為了要解決多重 requests 的逾時控制而誕生的,我深深地被 tmr 折磨了好一下子,只好那個捏著,自己寫一個通用型的定時模組,以解決我所遇到的痛處。我不知道是不是早有類似的模組存在,如果有的話,那很好!表示不只我會遇到這種痛苦。
 
至於nodemcu-timer的實作細節,我就不贅述啦!如果對此模組細節有興趣的朋友,我在這裡小小提醒一下,雖然風格是仿照 Node.js,但是此函式庫中的 setImmediate() 的行為跟 Node.js 還是有一點小小的差異。畢竟,那可是有事件迴圈的node.js啊!這只是一個小小的定時器啊!科科~

二、nodemcu-timer 簡短介紹

重點只有4支APIs:setTimeout(), clearTimeout(), setInterval(), clearInterval()
他們分別用於啟動、取消單次定時跟重複定時
 
當目前沒有任何定時工作時,timer 會自動停止內部的計數器,一旦你用setTimeout與setInterval將工作推入定時器,內部定時器會自動啟用,你不用擔心定時器什麼時候會動、什麼時候不會動。

三、下載 nodemcu-timer module

  1. nodemcu-timer github,看你要 clone 還是下載 .zip 檔回來都可以
  2. timer.lua 跟 timer_min.lua (minify過的版本) 自己挑一支用,因為等一下會 compile 成 bytecode,所以用哪一支都沒差。如果你不想 compile,那我是建議使用 minify 過的那支script。
  3. 下載到 ESP8266

4. 寫一支 app.js,等一下下載到 ESP8266

記得在 init.lua 中,do 你的 app.js

四、app.lua 示範

這裡還是拿大家用到爛掉的 LED 來做示範,你手邊若沒有 LED,那用 print() 在 terminal 觀察文字提示也可以啦!

4.1 LED定時亮滅

這支程式大家一定看得懂,LED會亮1秒、暗1秒,一直重複。API 的介面是 timer.setInterval(callback, delay),callback 是你的task,delay是重複週期,單位是ms。我的LED是接成 active-low。

local timer = require 'timer'
local LED_PIN1 = 0
gpio.mode(LED_PIN1, gpio.OUTPUT)
local sw1 = true
timer.setInterval(function ()
if (sw1) then
gpio.write(LED_PIN1, gpio.LOW)
else
gpio.write(LED_PIN1, gpio.HIGH)
end
sw1 = not sw1
end, 1000)

4.2 LED提示燈

現在這支程式的目標是設計一支負責點亮LED的函式,介面是 blinkLED(led, times, interval),你可以選擇要哪一顆 LED (led) 以多快的速度 (interval) 閃爍個幾次 (times)。
 
現在我要用它來點亮閃爍速率跟次數都不一樣的 3 顆 LED。這邊值得一提的是,blinkLED 使用了 Lua 的閉包(closure)性質,然後 blinkLED() 是可重進入的,你不需要自己管理 timer id 來管理定時器資源。一些觀念上的東西,我也不在此唬爛一拖拉庫啦~ 其實有在用的人應該都知道啦~我還是把重點放在模組的使用。
 
在你執行 app.lua 之後,程式會分別在 1234ms, 3528ms, 5104ms 之後分別觸發三顆 LED 的閃爍。他們各以不同的速度閃爍了 5 次、3 次與 10 次。

local timer = require 'timer'
local LED_PIN1, LED_PIN2, LED_PIN3 = 0, 1, 2
gpio.mode(LED_PIN1, gpio.OUTPUT)
gpio.mode(LED_PIN2, gpio.OUTPUT)
gpio.mode(LED_PIN3, gpio.OUTPUT)
function blinkLED(led, times, interval)
local sw, count, tobj = true, 0
    tobj = timer.setInterval(function ()
if (sw) then
gpio.write(led, gpio.LOW)
else
gpio.write(led, gpio.HIGH)
count = count + 1
end
sw = not sw

if (count == times) then
timer.clearInterval(tobj)
gpio.write(led, gpio.HIGH)
end
end, interval)
end
timer.setTimeout(function ()
blinkLED(LED_PIN1, 5, 560)
end, 1234)
timer.setTimeout(function ()
blinkLED(LED_PIN2, 3, 1024)
end, 3528)
timer.setTimeout(function ()
blinkLED(LED_PIN3, 10, 200)
end, 5104)

示範結果:

五、後記

你可能會認為,不過就是閃爍個 LED 有啥了不起。科科~ 你可以試著用以上的條件,自己拿 tmr 模組寫寫看,假如你寫出來了,你可以再寫一支同時有 20 顆 LEDs 在活動的情況,你一定會覺得 nodemcu-timer 真好用,這正是定時功能被抽象化之後帶來的好處。
 
試想,當你希望在某種條件發生時,快速閃爍一顆紅色LED燈;在某種情況,又要慢速閃爍某顆LED燈;在另一種狀況又要….,恩,真的很煩~ 現在把閃爍LED想像成某一個工作,道理也是一樣的。
 
有了 nodemcu-timer,你的程式可以用比較簡潔的風格寫出比較複雜的邏輯。如果,你希望寫事件驅動式的應用程式,你可以在某種狀況時,發射某個事件,然後執行對應的監聽器,你的程式可以鋪陳地更有段落、更有組織、更婀娜多姿、更花枝招展哦!下次我再來介紹 lua-events 這個模組,跟 nodemcu-timer 搭配服用,效果更好!
 
(ps. 你可能會問說,啊 NodeMCU 不是都標榜它是事件驅動風格嗎? 恩~ 關於這點,請自行體會囉~大多時間你都是在 listen 內建模組的事件,要用這些事件在你的 app-level 撰寫像樣的 event-driven 程式碼,恩…. 再說一次,請自行體會~科科)

【本文同步刊登於EE狂想曲】

http://simeneer.blogspot.tw/2016/01/nodemcu-esp8266.html

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.