РАБОТАЕМ ПО ТАЙМЕРУ В ARDUINO
Я думаю все знают классический алгоритм создания таймера на millis() – счётчике аптайма:
// Данный код выполняет действия периодически за указанный период // Нам нужно задать период таймера В МИЛЛИСЕКУНДАХ // дней*(24 часов в сутках)*(60 минут в часе)*(60 секунд в минуте)*(1000 миллисекунд в секунде) // (long) обязательно для больших чисел, иначе не посчитает // можно посчитать на калькуляторе, но какбэ ардуино и есть калькулятор, пусть считает... unsigned long period_time = (long)5*24*60*60*1000; // переменная таймера, максимально большой целочисленный тип (он же uint32_t) unsigned long my_timer; void setup() { my_timer = millis(); // "сбросить" таймер } void loop() { if (millis() - my_timer >= period_time) { my_timer = millis(); // "сбросить" таймер // дейтвия, которые хотим выполнить один раз за период } }
/* Делаем "параллельное" выполнение нескольких задач с разным периодом выполнения */ #define PERIOD_1 100 // период первой задачи #define PERIOD_2 2000 // период второй задачи #define PERIOD_3 666 // ... unsigned long timer_1, timer_2, timer_3; void setup() { } void loop() { if (millis() - timer_1 >= PERIOD_1) { // условие таймера timer_1 = millis(); // сброс таймера // выполняем блок №1 каждые PERIOD_1 миллисекунд } if (millis() - timer_2 >= PERIOD_2) { timer_2 = millis(); // выполняем блок №2 каждые PERIOD_2 миллисекунд } if (millis() - timer_3 >= PERIOD_3) { timer_3 = millis(); // выполняем блок №3 каждые PERIOD_3 миллисекунд } }
Данный алгоритм позволяет спокойно переходить через переполнение millis() без потери периода, но имеет один большой минус – время считается с момента последнего вызова таймера, и при наличии задержек в коде таймер будет накапливать погрешность, отклоняясь от “ритма”. Недавно я придумал более точный алгоритм таймера на миллис, который соблюдает свой период даже после пропуска хода!
#define PERIOD 500 uint32_t timer = 0; void loop() { if (millis() - timer >= PERIOD) { // ваше действие do { timer += PERIOD; if (timer < PERIOD) break; // переполнение uint32_t } while (timer < millis() - PERIOD); // защита от пропуска шага } }
Данный таймер имеет механику классического таймера с хранением переменной таймера, а его период всегда кратен PERIOD и не сбивается. Эту конструкцию можно упростить до
#define PERIOD 500 uint32_t timer = 0; void loop() { if (millis() - timer >= PERIOD) { // ваше действие timer += PERIOD; } }
В этом случае алгоритм получается короче, кратность периодов сохраняется, но теряется защита от пропуска вызова и переполнения millis().
В этой библиотеке реализован полноценный таймер на счётчике аптайма, позволяющий даже “приостановить” счёт (не приостанавливая сам аптайм).
Примечание: таких таймеров можно создать сколько душе угодно (пока хватит памяти), что позволяет создавать сложные программы с кучей подзадач, но для функционирования данного таймера нужен “чистый” loop с минимальным количеством задержек, или вообще без них. Всё таки таймер “опрашивается” в ручном режиме. Таймер, который не боится задержек, делается на прерывании таймера, смотрите вот эту библиотеку.
БИБЛИОТЕКА GYVERTIMER
GyverTimer v3.2

GTimer – полноценный таймер на базе системных millis() / micros(), обеспечивающий удобную мультизадачность и работу с временем, используя всего одно родное прерывание таймера (Timer 0)
- Миллисекундный и микросекундный таймер
- Два режима работы:
- Режим интервала: таймер “срабатывает” каждый заданный интервал времени
- Режим таймаута: таймер “срабатывает” один раз по истечении времени (до следующего перезапуска)
- Служебные функции:
- Старт
- Стоп
- Сброс
- Продолжить
Поддерживаемые платформы: все Arduino (используются стандартные Wiring-функции)
ДОКУМЕНТАЦИЯ
Конструктор
Класс GTimer позволяет работать как с миллисекундным, так и с микросекундным таймером. В общем виде пример выглядит так:
GTimer myTimer(type, period);
Где type это MS (,мс, миллисекундный таймер) или US (мкс, микросекундный), period – период в мс или мкс соответственно.
Настройки по умолчанию
- При создании таймера можно ничего не указывать: GTimer myTimer; , тогда таймер будет сконфигурирован как миллисекундный и не запустится
- Если указать только тип таймера (MS/US) GTimer myTimer(MS); , таймер настроится на выбранный режим (мс/мкс) и не запустится
- Если указать тип таймера и интервал GTimer myTimer(US, 5000); , таймер настроится на выбранный режим (мс/мкс) и запустится в режиме интервала
Режимы работы
Таймер может работать в режиме интервалов и в режиме таймаута:
- Интервалы. Запуск – метод setInterval(время) с указанием времени. В режиме интервалов таймер срабатывает (метод isReady() возвращает true) каждый раз при достижении указанного периода и автоматически перезапускается. Удобно для периодических действий
- Таймаут. Запуск – метод setTimeout(время) с указанием времени. В режиме таймаута таймер срабатывает (метод isReady() возвращает true) только один раз при достижении указанного периода и автоматически отключается. Для повторного запуска нужно вызвать .setTimeout() с указанием периода, или просто .start() – запустит таймер на новый круг с прежним периодом
Управление таймером
Для управления состоянием таймера есть следующие методы:
- start() – запускает (перезапускает) таймер с последним установленным временем
- stop() – останавливает таймер
- resume() – продолжает отсчёт таймера с момента остановки
- reset() – сбрасывает таймер (отсчёт периода/таймаута начинается заново)
- isEnabled() – возвращает true, если таймер работает (если он не stop() или не вышел таймаут)
GTimer(timerType type, uint32_t interval); // объявление таймера с указанием типа и интервала (таймер не запущен, если не указывать) void setInterval(uint32_t interval); // установка интервала работы таймера (также запустит и сбросит таймер) - режим интервала void setTimeout(uint32_t timeout); // установка таймаута работы таймера (также запустит и сбросит таймер) - режим таймаута boolean isReady(); // возвращает true, когда пришло время boolean isEnabled(); // вернуть состояние таймера (остановлен/запущен) void reset(); // сброс таймера на установленный период работы void start(); // запустить/перезапустить (со сбросом счёта) void stop(); // остановить таймер (без сброса счёта) void resume(); // продолжить (без сброса счёта)
ПРИМЕРЫ
// пример работы в режиме интервалов #include "GyverTimer.h" // подключаем библиотеку GTimer myTimer(MS); // создать миллисекундный таймер (ms) (по умолч. в режиме интервала) //GTimer myTimer(MS, 1000); // можно сразу указать период (по умолч. в режиме интервала) //GTimer myTimer(US, 5000); // или микросекундный (us), на 5000 мкс (по умолч. в режиме интервала) // без указания периода таймер автоматически не запустится! void setup() { Serial.begin(9600); myTimer.setInterval(500); // запуск в режиме интервала 500 мс // myTimer.stop(); // "остановить" таймер // myTimer.start(); // запустить (перезапустить) таймер // myTimer.reset(); // сбросить период // myTimer.resume(); // продолжить работу после stop } void loop() { if (myTimer.isReady()) Serial.println("Timer!"); // 2 раза в секунду }
// Пример работы в режиме таймаута #include "GyverTimer.h" // подключаем библиотеку GTimer myTimer(MS); // создать миллисекундный таймер //GTimer myTimer(US); // US - микросекундный void setup() { Serial.begin(9600); myTimer.setTimeout(3000); // настроить таймаут 3 сек Serial.println("Start"); } void loop() { // выведет Timeout 3 sec! через 3 секунды после вызова setTimeout(3000) if (myTimer.isReady()) Serial.println("Timeout 3 sec!"); // после срабатывания остановит счёт // можно перезапустить при помощи setTimeout(время) или start() }
// пример параллельной работы нескольких таймеров #include "GyverTimer.h" // подключаем библиотеку // создаём таймеры в миллисекундах GTimer myTimer1(MS, 500); GTimer myTimer2(MS, 600); GTimer myTimer3(MS, 1000); void setup() { Serial.begin(9600); } void loop() { if (myTimer1.isReady()) Serial.println("Timer 1!"); if (myTimer2.isReady()) Serial.println("Timer 2!"); if (myTimer3.isReady()) Serial.println("Timer 3!"); }
// пример с кнопкой, кнопка сбрасывает таймер #include "GyverButton.h" // библиотека кнопки GButton btn(3); // кнопка на D3 #include "GyverTimer.h" // библиотека таймера GTimer myTimer(MS); // создать таймер (по умолч. в режиме интервала) void setup() { Serial.begin(9600); myTimer.setTimeout(1000); // запуск в режиме таймаута 1 секунда } void loop() { btn.tick(); // опрос кнопки // при нажатии кнопки задаём таймаут 1 секунду if (btn.isClick()) myTimer.setTimeout(1000); // через секунду после последнего нажатия выведет Timeout! if (myTimer.isReady()) Serial.println("Timeout!"); }
// пример с кнопкой, которая приостанавливает таймер #include "GyverButton.h" // библиотека кнопки GButton btn(3); // кнопка на D3 #include "GyverTimer.h" // библиотека таймера GTimer myTimer(MS); // создать таймер (по умолч. в режиме интервала) void setup() { Serial.begin(9600); myTimer.setInterval(5000); // запуск в режиме таймаута 5 секунд } void loop() { btn.tick(); // опрос кнопки // при нажатии кнопки останавливаем и продолжаем таймер if (btn.isClick()) { static bool flag = false; flag = !flag; if (flag) myTimer.stop(); else myTimer.resume(); } // оповещение о таймауте if (myTimer.isReady()) Serial.println("Timeout!"); }
ОСТАЛЬНЫЕ БИБЛИОТЕКИ

У меня есть ещё очень много всего интересного! Смотрите полный список библиотек вот здесь.