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.
This commit is contained in:
Camden Dixie O'Brien 2023-05-19 14:01:40 +01:00
parent 17704a5607
commit 9c9f027bb8
4 changed files with 108 additions and 33 deletions

View File

@ -10,6 +10,8 @@
#include "time_manager.h" #include "time_manager.h"
#include "esp_log.h" #include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@ -29,7 +31,21 @@ typedef struct {
static Alarm alarms[CONFIG_MAX_ALARMS]; static Alarm alarms[CONFIG_MAX_ALARMS];
static ActiveAlarm active[CONFIG_MAX_ALARMS]; static ActiveAlarm active[CONFIG_MAX_ALARMS];
static unsigned active_count; static unsigned active_count;
static ActiveAlarm snoozed[CONFIG_MAX_ALARMS];
static unsigned snoozed_count;
static Time last_check; 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) static bool add_alarm(Time time)
{ {
@ -48,68 +64,107 @@ static void remove_alarm(unsigned index)
alarms[index].set = false; 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) if (active_count == 0)
sound_alert_on(); sound_alert_on();
active[active_count].alarm = &alarms[index]; active[active_count].alarm = alarm;
active[active_count].end = end; active[active_count].end = in_minutes(CONFIG_ALERT_MINUTES);
++active_count; ++active_count;
ESP_LOGI(TAG, "Alarm %u activated", index);
} }
static void dismiss(unsigned 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) if (active_count == 1)
sound_alert_off(); sound_alert_off();
for (unsigned i = index + 1; i < active_count; ++i) for (unsigned i = index + 1; i < active_count; ++i)
active[i - 1] = active[i]; active[i - 1] = active[i];
--active_count; --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 if (index >= active_count) {
&& last_check.minute <= time.minute && time.minute <= now->minute ESP_LOGE(
&& last_check.second <= time.second && time.second <= now->second; TAG, "Invalid active index %u passed to %s()", index, __func__);
} return;
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;
} }
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) { for (unsigned i = 0; i < CONFIG_MAX_ALARMS; ++i) {
if (!alarms[i].set) if (!alarms[i].set)
continue; continue;
if (passed_since_last_check(alarms[i].time, now)) 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)) if (passed_since_last_check(active[i].end, now))
dismiss(i); 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) static int command_func(int argc, char **argv)
@ -160,6 +215,8 @@ void alarms_init(void)
memset(alarms, 0, sizeof(alarms)); memset(alarms, 0, sizeof(alarms));
memset(active, 0, sizeof(active)); memset(active, 0, sizeof(active));
active_count = 0; active_count = 0;
memset(snoozed, 0, sizeof(snoozed));
snoozed_count = 0;
last_check = get_time(); last_check = get_time();
add_time_callback(check_alarms); add_time_callback(check_alarms);
@ -168,14 +225,32 @@ void alarms_init(void)
"alarms", "List, add and remove alarms", "alarms", "List, add and remove alarms",
"alarms OR alarms add <hh:mm> OR alarms remove <index>", "alarms OR alarms add <hh:mm> OR alarms remove <index>",
command_func); command_func);
state_mutex = xSemaphoreCreateMutex();
} }
void alarm_snooze(void) 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) 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) if (active_count > 0)
dismiss(0); dismiss(0);
xSemaphoreGive(state_mutex);
} }

View File

@ -51,9 +51,9 @@ static void show_time(unsigned hour, unsigned minute)
show_digit(DISPLAY_DIGIT_4, minute % 10); 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() void display_init()

View File

@ -38,7 +38,7 @@ static void run_callbacks(void *arg)
{ {
const Time time = get_time(); const Time time = get_time();
for (unsigned i = 0; i < callback_count; ++i) for (unsigned i = 0; i < callback_count; ++i)
callbacks[i](&time); callbacks[i](time);
} }
static int time_command_func(int argc, char **argv) static int time_command_func(int argc, char **argv)

View File

@ -28,7 +28,7 @@ typedef enum {
WEEK_DAY_SUNDAY, WEEK_DAY_SUNDAY,
} WeekDay; } WeekDay;
typedef void (*TimeCallback)(const Time *time); typedef void (*TimeCallback)(Time now);
/** /**
* Initialize the time module. * Initialize the time module.