ESP8266 NodeMCU timer, rtc, SNTP, cron


18.05.2017

Рассмотрим работу таймеров, счетчиков, Watchdog, часов реального времени, синхронизацию времени через Интернет, и планировщик задач cron. Словом все, что имеет отношение ко времени. В NodeMCU можно использовать 7 таймеров, с помощью которых запускать нужные функции через определенное время. Работу таймеров обеспечивает модуль tmr.

Инициализация таймеров выполняется функцией tmr.register([id/ref], interval_ms, mode, func())

[id/ref] - id таймера (0-6), или объект таймера созданный с помощью функции tmr.create() interval_ms - интервал в миллисекундах. Максимальное значение 6870947 mode - режим таймера:

  • tmr.ALARM_SINGLE - срабатывает только один раз. Не надо вызывать tmr.unregister()
  • tmr.ALARM_SEMI - срабатывает один раз. Для повтора надо вызвать tmr.start()
  • tmr.ALARM_AUTO - циклический перезапуск таймера

func(timer) - функция обратного вызова, которая вызывается с объектом таймера в качестве аргумента.

Функция может быть описана отдельно (как это сделано в примере для таймера 0), или описана в функции tmr.register() (как это сделано в примере для таймера 1).

Пример инициализация таймеров:


function timer_do()
    print(`timer 0`)
end

-- Start timer
tmr.register(0, 1000, tmr.ALARM_AUTO, timer_do)
tmr.start(0)

-- Start timer
tmr.register(1, 3000, tmr.ALARM_AUTO, function()  print("timer 1") end)
tmr.start(1)

-- tmr.register + tmr.start = tmr.alarm
-- Just once
tmr.alarm(2, 5000, tmr.ALARM_SINGLE, function() print("timer 2") end)

-- Just once. Repeat manually by tmr.start(3)
tmr.alarm(3, 1000, tmr.ALARM_SEMI, function() print("timer 3") end)

Функция tmr.alarm сочетает в себе tmr.register и tmr.start. То есть, она выполняет инициализацию и запуск таймера.

Остановить таймер можно командой tmr.stop ([id/ref]) [id/ref] - id таймера (0-6), или объект таймера созданного с помощью функции tmr.create()

Пример создания и использования таймера как объекта:


-- Creates a dynamic timer object
local mytimer = tmr.create()

-- oo calling
mytimer:register(5000, tmr.ALARM_SINGLE, function (t) print("expired"); t:unregister() end)
mytimer:start()

Или:


-- Creates a dynamic timer object
local mytimer = tmr.create()

-- with self parameter
tmr.register(mytimer, 5000, tmr.ALARM_SINGLE, function (t) print("expired"); tmr.unregister(t) end)
tmr.start(mytimer)

Задержка

Если в программе нужно использовать задержку, Вам поможет функция tmr.delay(us) us - микросекунды

Пример задержки на 2 секунды:


print(`Begin`)
-- 2sec delay
tmr.delay(2000000)
print(`End`)

Системный счетчик

После старта NodeMCU запускает системный счетчик. Он считает микросекунды и имеет разрядность 31 бит. Пример считывания системного счетчика:


--return system counter, which counts in microseconds. (31-bit)
print(tmr.now())

Узнать Uptime можно следующей командой:


--Print uptime
print("Uptime (probably):", tmr.time())

Watchdog

Модуль tmr также обеспечивает работу программного watchdog. Задача watchdog перезагрузить систему при зависании или во время сбоев в программе. Watchdog-у задается время, через которое он должен перезагрузить систему, и если за это время watchdog не отключить, тогда произойдет перезагрузка. Обычно делают так. Перед операциями, которые могут вызвать зависание, включают watchdog, а по завершению выключают. Если операция не закончилась за указанное watchdog-у время, считается что процесс завис, и watchdog вызывает перезагрузку системы.

Время watchdog-у задается функцией tmr.softwd(s) в секундах. После чего watchdog сразу начинает работу. Чтобы выключить watchdog, надо выполнить команду tmr.softwd (-1)

Пример:


-- Wait 2 seconds. If the watchdog is not disabled, then reboot.
tmr.softwd(2)
print("Soft watchdog enabled!")

-- It`s OK (1 sec)
tmr.register(0, 1000, tmr.ALARM_SINGLE, function()  tmr.softwd(-1) print("Soft watchdog disabled!") end)
tmr.start(0)

-- It`s NOT OK. Watchdog reboots system before it`s will disabled
--tmr.register(0, 3000, tmr.ALARM_SINGLE, function()  tmr.softwd(-1) print("Soft watchdog disabled!") end)
--tmr.start(0)

В примере отключение watchdog-а выполняется по таймеру. Это сделано исключительно для демонстрации.

Часы реального времени

За работу часов реального времени отвечает модуль rtctime. Узнать текущее время можно следующим образом:



tm = rtctime.epoch2cal(rtctime.get())
print(string.format("%04d/%02d/%02d %02d:%02d:%02d", tm["year"], tm["mon"], tm["day"], tm["hour"], tm["min"], tm["sec"]))

Сразу после старта NodeMCU, часы стоят. Чтобы часы начали идти следует задать время в секундах от 1970 года. Пример:


-- Set time to 2015 July 9, 18:29:49
rtctime.set(1436430589, 0)

-- Set time to 2017 Jan 11, 00:00:00
rtctime.set(1484092800, 0)

SNTP - синхронизация времени через интернет

Если Ваш NodeMCU подключен к WiFi и имеет доступ к Интернет, тогда можно синхронизировать время с помощью протокола SNTP. За реализацию этого протокола отвечает модуль sntp.

Можно просто запустить команду:


sntp.sync()

Синхронизация запустится с использованием NTP серверов, жестко прописанных в NodeMCU. Пример скрипта, который выводит результат синхронизации:


-- Use the nodemcu specific pool servers
sntp.sync(nil,
  function(sec, usec, server, info)
    print(`sync`, sec, usec, server)
    tm = rtctime.epoch2cal(rtctime.get())
    print(string.format("%04d/%02d/%02d %02d:%02d:%02d", tm["year"], tm["mon"], tm["day"], tm["hour"], tm["min"], tm["sec"]))
  end,
  function()
   print(`failed!`)
  end
)

Если в локальной сети есть собственный NTP сервер, например с адресом 192.168.1.1, можно указать его для синхронизации:


-- Single shot sync time with a server on the local network.
sntp.sync(`192.168.1.1`,
  function(sec, usec, server, info)
    print(`sync`, sec, usec, server)
    tm = rtctime.epoch2cal(rtctime.get())
    print(string.format("%04d/%02d/%02d %02d:%02d:%02d", tm["year"], tm["mon"], tm["day"], tm["hour"], tm["min"], tm["sec"]))
  end,
  function()
   print(`failed!`)
  end
)

Cron (scheduler)

Существуют задачи, которые нужно запускать периодически и синхронно с реальным временем. Например, включать будильник каждый день в 7:00, кормить рыбок каждые 6 часов. Через день вечером поливать цветочки, и так далее.

Для этого обычные таймеры не подходят. С помощью модуля cron, совместно с модулем rtctime, можно организовать планировщик задач (scheduler).

Пример:


function my_schedule()
  tm = rtctime.epoch2cal(rtctime.get())
  print("It`s Cron.")
  print(string.format("%04d/%02d/%02d %02d:%02d:%02d", tm["year"], tm["mon"], tm["day"], tm["hour"], tm["min"], tm["sec"]))
end

-- Cron. Every minute
cron.schedule("* * * * *", my_schedule)

В примере функция my_schedule вызывается каждую минуту. То есть, в момент когда на часах реального времени начинается новая минута. Маска пишется так же как и в crontab.

Примеры:


cron.schedule("*/5 * * * *", function(e)
  print("Every 5 minutes")
end)

cron.schedule("0 * * * *", function(e)
  print("Every hours")
end)

cron.schedule("0 */2 * * *", function(e)
  print("Every 2 hours")
end)

cron.schedule("0 0 * * *", function(e)
  print("Every midnight")
end)

cron.schedule("0 7 * * *", function(e)
  print("Every day at 7 o`clock")
end)

cron.schedule("0 0 1 * *", function(e)
  print("Every midnight of first day of month")
end)

Желаю успехов.

P.S. Напомню, что собрать NodeMCU с нужными модулями можно на сайте https://nodemcu-build.com/

Смотри также:

Коментарі:
Valentin говорить:
15.06.2017 15:20
Очень полезно, спасибо!
Сергей говорить:
02.04.2018 14:49
Добрый день, Андрей.
Вопросы по tmr.register:
1) func(timer) ~ function (t) в "Пример создания и использования таймера как объекта"? Что здесь делает параметр "t", где он описан?
2) "tmr.ALARM_SINGLE – срабатывает только один раз. Не надо вызывать tmr.unregister()" - зачем тогда в этом же примере tmr.unregister() вызывается?
3) "tmr.ALARM_AUTO – циклический перезапуск таймера" - а сколько раз таймер перезапускается, с какой переодичностью?
-
Сергей
andre говорить:
02.04.2018 20:03
Добрый день. Отвечу задом на перед.
3) tmr.ALARM_AUTO перезапускает таймер циклически бесконечно. Эта настройка не влияет на периодичность (т.е. время) только на режим перезапуска таймера. Периодичность задается отдельным параметром.
2) я стараюсь всегда явно "пристрелить" таймер командой unregister, даже если это делать не обязательно.
1) mytimer:register(5000, tmr.ALARM_SINGLE, function (t) print("expired"); t:unregister() end)
Эта функия задает время (5000) через которое таймер вызовет функцию описанную в последнем параметре. Т.е. функцию:
function (t) print("expired"); t:unregister() end
Этой функции в качестве параметра t будет передан "указатель" на таймер, который вызвал функцию, на случай если нам с ним что-то надо сделать. В данном примере мы просто ему делаем unregister(), но иногда в зависимости от задачи требуется перезапустить таймер или еще что-то с ним сделать. В общем, через параметр t получаем доступ к таймеру.
Сергей говорить:
05.04.2018 13:08
Увы, не всегда вижу очевидное или почти очевидное.
Четко и ясно, спасибо, andre, но другие вопросы у меня, скорее всего, еще будут.
Довольно интересная тема "Интернет вещей" и Lua нравится

Додати коментар

* - обов'язкові поля

Архіви