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/

Дивись також:

ESP8266
Коментарі:
Valentin говорить:
20.06.2017 15:28
Добрый день! Можете предоставить, пожалуйста, тестовый вариант программы sntp (синхронизация времени)?
Я добавил в код подключение к WI-FI, однако программа не заработала :(
Спасибо!

Додати коментар
Code
* - обов'язкові поля

Архіви