Я не знаю, используете ли вы ESP IDF или какую-то платформу Arduino для ESP32 (с которой я не знаком). Этот ответ для ESP IDF. Если вы не используете ESP-IDF и не имеете доступа к соответствующим заголовочным файлам и библиотекам, некоторые идеи все еще могут быть актуальны.
Активировать SNTP
Убедитесь, что часы на контроллере обновлены, следуя примеру SNTP на https://github.com/espressif/esp-idf/tree/master/examples/protocols/sntp.
Использование localtime()
Если вы используете ESP-IDF, у вас будет доступ к функции localtime_r()
. Передайте это (указатель на) число «секунд с начала эпохи», которое возвращается time(NULL)
, и оно заполнит struct tm
днем недели, днем месяца, часом, минутой и секундой.
Общая процедура (обработка ошибок пропущена) ...
#include <time.h>
// ...
struct tm now = {};
time_t tnow = time(NULL);
localtime_r(&tnow, &now);
// Compare current day, hour, minute, etc. against pre-defined alarms.
Примечание: вы можете использовать gmtime_r()
вместо localtime_r()
, если вы предпочитаете использовать UTC.
Вы можете периодически вызывать это из основного цикла while()
, как описано ниже.
Сигнальные сооружения
Определите структуру для вашей информации о тревоге. Вы можете использовать что-то вроде следующего - настройка в соответствии с уровнем детализации, который вам нужен. Обратите внимание, что он включает элемент callback
- для указания того, что программа должна делать при срабатывании тревоги.
typedef struct {
int week_day; // -1 for every day of week
int hour; // -1 for every hour
int minute; // -1 for every minute
void (*callback)(void);
void *callback_arg;
} alarm_t;
Определение сигналов тревоги и обратных вызовов
Определите ваш массив аварийных сигналов, функций обратного вызова и аргументов.
static void trigger_relay(const void *arg) {
uint32_t num = *(uint32_t *)arg;
printf("Activating relay %u ...\n", num);
// Handle GPIO etc.
}
static uint32_t relay_1 = 0;
static uint32_t relay_2 = 1;
static alarm_t alarms[] = {
// Trigger relay 1 at 9:00 on Sunday
{0, 9, 0, trigger_relay, (void *)&relay_1},
// Relay 2 at 7:30 every day
{-1, 7, 30, trigger_relay, (void *)&relay_2},
};
Периодическое сканирование
Определите подпрограмму check_alarms()
, которая загружает структуру struct tm
с текущим временем и сравнивает ее с каждой из заданных вами аварийных сигналов. Если текущее время соответствует сигналу тревоги, вызывается обратный вызов.
static void check_alarms(void) {
time_t tnow = time(NULL);
struct tm now = {};
localtime_r(&tnow, &now);
ESP_LOGI(TAG, "Time: %u:%u:%u", now.tm_hour, now.tm_min, now.tm_sec);
size_t n_alarms = sizeof(alarms) / sizeof(alarms[0]);
for (int i = 0; i < n_alarms; i++) {
alarm_t *alrm = &alarms[i];
if (alrm->week_day != -1 && alrm->week_day != now.tm_wday) {
continue;
}
if (alrm->hour != -1 && alrm->hour != now.tm_hour) {
continue;
}
if (alrm->minute != -1 && alrm->minute != now.tm_min) {
continue;
}
alrm->callback(alrm->callback_arg);
}
}
Затем он будет вызываться из вашего основного while()
контура с частотой, зависящей от степени детализации вашего сигнала тревоги. Поскольку alarm_t
, определенный выше, имеет гранулярность 1 минуту, именно так я буду звонить check_alarms()
. Этот пример предназначен для FreeRTOS, но может быть адаптирован при необходимости.
while (1) {
TickType_t tick = xTaskGetTickCount();
if (tick - g_time_last_check > pdMS_TO_TICKS(60000)) {
g_time_last_check = tick;
check_alarms();
}
}
Эффективность * * тысяча пятьдесят-одна
Вышеупомянутое не очень эффективно, если у вас много сигналов тревоги и / или каждый сигнал должен срабатывать с высокой частотой. В таком случае вы могли бы рассмотреть алгоритмы, которые сортируют сигналы тревоги по времени до истечения времени, и - вместо того, чтобы каждый раз перебирать весь массив, - вместо этого использовать программный таймер однократного запуска для вызова соответствующего обратного вызова. Это, вероятно, не стоит хлопот, если у вас очень много таймеров.
Альтернативы
И FreeRTOS, и ESP-IDF включают API для программных таймеров - но их нельзя (легко) использовать для аварийных сигналов в определенное время дня - которое вы заявляете как требование С другой стороны, как вы, возможно, уже знаете, система ESP32 на кристалле имеет встроенный RTC, но я думаю, что лучше использовать интерфейс более высокого уровня, чем напрямую общаться с ним.