Некоторые регулярно выполняемые задачи, например управление освещением, отоплением, поливом и т.п, отнимают у нас довольно много времени, которое можно использовать с гораздо большей пользой. Имея в распоряжении какой-нибудь микроконтроллер совсем нетрудно организовать автоматическое выполнение указанных задач, причем делать в оптимальное для работы время. Можно например автоматически включать свет днём, отопление в летние месяцы и даже в зимние.
Любой пользователь Linux когда-нибудь слышал о cron – сервисе (демоне), который можно использовать для запуска повторяющихся заданий по расписанию, задаваемого по календарному времени. Предусмотрен также неоднократный запуск заданий, т.е. задание можно запускать периодически в определенное время или через определенный промежуток времени. Демон имеет простой, понятный и удивительно гибкий для своей компактности синтаксис, который де-факто является своего рода стандартом в мире Unix и как следствие большое количество обучающих материалов с примерами. Было бы здорово использовать такой же синтаксис при планировании задач в микроконтроллере, чтобы не заниматься изобретением своего велосипеда.
В Linux каждый пользователь системы имеет свой файл заданий crontab, в котором описано, в какое время и какие программы запускать от имени этого пользователя, а также переменные, настраивающие среду окружения, в котором будут работать задачи cron. В микроконтроллере часть этих настроек выглядит явно избыточной – скорее всего там нет такого понятия, как пользователи и переменные среды окружения да и программы запускать тоже навряд ли получится. Можно упростить себе жизнь и просто ограничится каким-нибудь контейнером вроде связного списка, чтобы иметь возможность пройтись по контейнеру и динамически добавлять / удалять задачи cron в него например из командной строки. Ну а для демонстрации возможностей cron сойдёт даже обычный статический массив (нельзя изменять размер во время выполнения программы), заданный на этапе компиляции. Элемент контейнера состоит из события – выражения cron, и действия. Действие описывает устройство периферии (например светодиод) и в какое состояние его необходимо перевести.
crond.c
static struct crond_job jobs[] = {
{ .expr = "*/6 */2 * * * *", .led = {0b0000011111, 1, 0, 0} },
{ .expr = "2/6 */2 * * * *", .led = {0b1111100000, 0, 1, 0} },
{ .expr = "4/6 */2 * * * *", .led = {0b1111111111, 0, 0, 1} },
{ .expr = "*/6 1/2 * * * *", .led = {0b1010101010, 1, 1, 0} },
{ .expr = "2/6 1/2 * * * *", .led = {0b0101010101, 0, 1, 1} },
{ .expr = "4/6 1/2 * * * *", .led = {0b1111111111, 1, 0, 1} },
};
Как несложно убедиться каждое выражение cron состоит из шести полей, разделяемых пробелами или табуляторами. В этих полях задают время выполнения (Секунда, Минута, Час, День, Месяц, День недели), в них может находиться число, список чисел, разделённых запятыми, символы *
или /
и диапазон чисел, разделённых дефисом. Месяцы и дни недели можно также задавать в 3-х буквенном варианте:
* * * * * * Действие (установить цвет RGB светодиодов с 1 по 10)
| | | | | |
| | | | | ----- День недели (0 - 6) (0 это воскресенье)
| | | | ------- Месяц (1 - 12)
| | | --------- День (1 - 31)
| | ----------- Час (0 - 23)
| ------------- Минута (0 - 59)
--------------- Секунда (0 - 59)
Символ *
подразумевает любое значение. Каждую секунду:
* * * * * *
Каждый день в 03:15:00:
0 15 3 * * *
Каждый понедельник:
0 0 0 * * 1
Каждый четный час:
0 0 */2 * * *
Тоже что и выше, но расписанное полностью:
0 0 0,2,4,6,8,10,12,14,16,18,20,22 * * *
Каждый будний день в 22:00:
0 0 22 * * 1-5
Новый год:
59 59 23 31 dec *
Все условия (времени запуска) проверяются по «логическому И», кроме условий «день недели» и «день месяца» – указанные совместно, они обрабатываются по «логическому ИЛИ», то есть «по любому из дней», что отражено в документации (Ubuntu, Debian, FreeBSD). Однако такая логика неочевидна и не позволяет создать условие типа «первый понедельник каждого месяца» или «каждую пятницу в 13 число». Разработчики не изменяют данное поведение, считая его стандартом.
Также стоит помнить о переходе на летнее / зимнее время. Тут нет единого мнения и различные реализации cron могут вести себя по разному. В Unix системах есть альтернатива Anacron, который в отличие от cron не поддерживает запуск заданий по расписанию, вместо этого задания запускаются с заданным интервалом времени. Это очень удобно для систем которые работают не регулярно, например домашние рабочие станции или ноутбуки.