From 9c9f027bb8b85c13d6bc37683fd419c1f6124f2f Mon Sep 17 00:00:00 2001 From: Camden Dixie O'Brien Date: Fri, 19 May 2023 14:01:40 +0100 Subject: [PATCH] Add support for snoozing alarms Also modified time_manager to pass time by value to time callbacks as it's a fairly small struct and it will probably be more robust. --- components/alarms/alarms.c | 133 ++++++++++++++++++++++++++------- components/display/display.c | 4 +- components/time/time_manager.c | 2 +- components/time/time_manager.h | 2 +- 4 files changed, 108 insertions(+), 33 deletions(-) diff --git a/components/alarms/alarms.c b/components/alarms/alarms.c index b09f239..e504664 100644 --- a/components/alarms/alarms.c +++ b/components/alarms/alarms.c @@ -10,6 +10,8 @@ #include "time_manager.h" #include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" #include #include #include @@ -29,7 +31,21 @@ typedef struct { static Alarm alarms[CONFIG_MAX_ALARMS]; static ActiveAlarm active[CONFIG_MAX_ALARMS]; static unsigned active_count; +static ActiveAlarm snoozed[CONFIG_MAX_ALARMS]; +static unsigned snoozed_count; static Time last_check; +static SemaphoreHandle_t state_mutex; + +static Time in_minutes(unsigned minutes) +{ + Time time = get_time(); + time.minute += minutes; + if (time.minute >= 60) { + time.minute %= 60; + ++time.hour; + } + return time; +} static bool add_alarm(Time time) { @@ -48,68 +64,107 @@ static void remove_alarm(unsigned index) alarms[index].set = false; } -static void activate(unsigned index, Time end) +static void activate(const Alarm *alarm) { + if (alarm < &alarms[0] || alarm >= &alarms[CONFIG_MAX_ALARMS] + || !alarm->set) { + ESP_LOGE(TAG, "Invalid alarm passed to %s()", __func__); + return; + } + if (active_count == 0) sound_alert_on(); - active[active_count].alarm = &alarms[index]; - active[active_count].end = end; + active[active_count].alarm = alarm; + active[active_count].end = in_minutes(CONFIG_ALERT_MINUTES); ++active_count; - - ESP_LOGI(TAG, "Alarm %u activated", index); } static void dismiss(unsigned index) { + if (index >= active_count) { + ESP_LOGE( + TAG, "Invalid active index %u passed to %s()", index, __func__); + return; + } + if (active_count == 1) sound_alert_off(); for (unsigned i = index + 1; i < active_count; ++i) active[i - 1] = active[i]; --active_count; - - ESP_LOGI(TAG, "Alarm %u dismissed", index); } -static bool passed_since_last_check(Time time, const Time *now) +static void snooze(unsigned index) { - return last_check.hour <= time.hour && time.hour <= now->hour - && last_check.minute <= time.minute && time.minute <= now->minute - && last_check.second <= time.second && time.second <= now->second; -} - -static Time add_minutes(const Time *time, unsigned minutes) -{ - Time new_time = { - .hour = time->hour, - .minute = time->minute + minutes, - .second = time->second, - }; - - if (new_time.minute >= 60) { - new_time.minute %= 60; - ++new_time.hour; + if (index >= active_count) { + ESP_LOGE( + TAG, "Invalid active index %u passed to %s()", index, __func__); + return; } - return new_time; + const Alarm *alarm = active[index].alarm; + dismiss(index); + + snoozed[snoozed_count].alarm = alarm; + snoozed[snoozed_count].end = in_minutes(CONFIG_SNOOZE_MINUTES); + ++snoozed_count; + + ESP_LOGI(TAG, "Alarm %u snoozed", alarm - &alarms[0]); } -static void check_alarms(const Time *now) +static void unsnooze(unsigned index) { + if (index >= snoozed_count) { + ESP_LOGE( + TAG, "Invalid snooze index %u passed to %s()", index, __func__); + return; + } + + activate(snoozed[index].alarm); + + for (unsigned i = index + 1; i < snoozed_count; ++i) + snoozed[i - 1] = snoozed[i]; + --snoozed_count; +} + +static bool passed_since_last_check(Time time, Time now) +{ + return last_check.hour <= time.hour && time.hour <= now.hour + && last_check.minute <= time.minute && time.minute <= now.minute + && last_check.second <= time.second && time.second <= now.second; +} + +static void check_alarms(Time now) +{ + // Skip if time hasn't changed since last check + if (last_check.hour == now.hour && last_check.minute == now.minute + && last_check.second == now.second) + return; + + if (xSemaphoreTake(state_mutex, (TickType_t)10) == pdFALSE) + return; + for (unsigned i = 0; i < CONFIG_MAX_ALARMS; ++i) { if (!alarms[i].set) continue; if (passed_since_last_check(alarms[i].time, now)) - activate(i, add_minutes(now, CONFIG_ALERT_MINUTES)); + activate(&alarms[i]); } - for (unsigned i = 0; i != active_count; ++i) { + for (unsigned i = 0; i < active_count; ++i) { if (passed_since_last_check(active[i].end, now)) dismiss(i); } - last_check = *now; + for (unsigned i = 0; i < snoozed_count; ++i) { + if (passed_since_last_check(snoozed[i].end, now)) + unsnooze(i); + } + + last_check = now; + xSemaphoreGive(state_mutex); } static int command_func(int argc, char **argv) @@ -160,6 +215,8 @@ void alarms_init(void) memset(alarms, 0, sizeof(alarms)); memset(active, 0, sizeof(active)); active_count = 0; + memset(snoozed, 0, sizeof(snoozed)); + snoozed_count = 0; last_check = get_time(); add_time_callback(check_alarms); @@ -168,14 +225,32 @@ void alarms_init(void) "alarms", "List, add and remove alarms", "alarms OR alarms add OR alarms remove ", command_func); + + state_mutex = xSemaphoreCreateMutex(); } void alarm_snooze(void) { + if (xSemaphoreTake(state_mutex, (TickType_t)10) == pdFALSE) { + ESP_LOGE(TAG, "Unable to aquire state semaphore"); + return; + } + + if (active_count > 0) + snooze(0); + + xSemaphoreGive(state_mutex); } void alarm_dismiss(void) { + if (xSemaphoreTake(state_mutex, (TickType_t)10) == pdFALSE) { + ESP_LOGE(TAG, "Unable to aquire state semaphore"); + return; + } + if (active_count > 0) dismiss(0); + + xSemaphoreGive(state_mutex); } diff --git a/components/display/display.c b/components/display/display.c index 1016b22..2160810 100644 --- a/components/display/display.c +++ b/components/display/display.c @@ -51,9 +51,9 @@ static void show_time(unsigned hour, unsigned minute) show_digit(DISPLAY_DIGIT_4, minute % 10); } -static void update_time(const Time *time) +static void update_time(Time now) { - show_time(time->hour, time->minute); + show_time(now.hour, now.minute); } void display_init() diff --git a/components/time/time_manager.c b/components/time/time_manager.c index 0add748..66b4084 100644 --- a/components/time/time_manager.c +++ b/components/time/time_manager.c @@ -38,7 +38,7 @@ static void run_callbacks(void *arg) { const Time time = get_time(); for (unsigned i = 0; i < callback_count; ++i) - callbacks[i](&time); + callbacks[i](time); } static int time_command_func(int argc, char **argv) diff --git a/components/time/time_manager.h b/components/time/time_manager.h index aec111b..cafabda 100644 --- a/components/time/time_manager.h +++ b/components/time/time_manager.h @@ -28,7 +28,7 @@ typedef enum { WEEK_DAY_SUNDAY, } WeekDay; -typedef void (*TimeCallback)(const Time *time); +typedef void (*TimeCallback)(Time now); /** * Initialize the time module.