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:
parent
17704a5607
commit
9c9f027bb8
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -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)
|
||||||
|
@ -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.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user