сайт для палких паяльників

Рассмотрим работу таймеров, счетчиков, 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/

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

4 комментария: ESP8266 NodeMCU timer, rtc, SNTP, cron

  • Сергей говорить:

    Добрый день, Андрей.
    Вопросы по tmr.register:
    1) func(timer) ~ function (t) в “Пример создания и использования таймера как объекта”? Что здесь делает параметр “t”, где он описан?
    2) “tmr.ALARM_SINGLE – срабатывает только один раз. Не надо вызывать tmr.unregister()” – зачем тогда в этом же примере tmr.unregister() вызывается?
    3) “tmr.ALARM_AUTO – циклический перезапуск таймера” – а сколько раз таймер перезапускается, с какой переодичностью?

    Сергей

    • andre говорить:

      Добрый день. Отвечу задом на перед.
      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 получаем доступ к таймеру.

      • Сергей говорить:

        Увы, не всегда вижу очевидное или почти очевидное.
        Четко и ясно, спасибо, andre, но другие вопросы у меня, скорее всего, еще будут.
        Довольно интересная тема “Интернет вещей” и Lua нравится

Translate
Архіви

© 2011-2018 Андрій Корягін, Кременчук - Київ, Україна