From 0d12bb7445d7ab3319044bedf3e33f217af9c633 Mon Sep 17 00:00:00 2001 From: Camden Dixie O'Brien Date: Thu, 18 May 2023 01:29:17 +0100 Subject: [PATCH] Add alarms component --- README.md | 2 +- components/alarms/CMakeLists.txt | 5 + components/alarms/alarms.c | 181 ++++++++++++++++++++++++++++++ components/alarms/alarms.h | 25 +++++ components/buttons/CMakeLists.txt | 2 +- components/buttons/buttons.c | 5 +- main/Kconfig.projbuild | 9 ++ main/main.c | 2 + 8 files changed, 227 insertions(+), 4 deletions(-) create mode 100644 components/alarms/CMakeLists.txt create mode 100644 components/alarms/alarms.c create mode 100644 components/alarms/alarms.h diff --git a/README.md b/README.md index 9484c9b..9d1b887 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Networked bedside clock utilising an ESP32-SOLO-1. - [ ] TCP API for configuration - [x] SNTP synchronisation - [x] Snooze and dismiss buttons -- [ ] Multiple alarms +- [x] Multiple alarms - [ ] Alarm conditions (e.g. weekday / weekend) - [ ] IPv6 support diff --git a/components/alarms/CMakeLists.txt b/components/alarms/CMakeLists.txt new file mode 100644 index 0000000..47ec2eb --- /dev/null +++ b/components/alarms/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS "alarms.c" + INCLUDE_DIRS "." + REQUIRES console_wrapper sound time +) diff --git a/components/alarms/alarms.c b/components/alarms/alarms.c new file mode 100644 index 0000000..b09f239 --- /dev/null +++ b/components/alarms/alarms.c @@ -0,0 +1,181 @@ +/* + * SPDX-License-Identifier: AGPL-3.0-only + * Copyright (c) Camden Dixie O'Brien + */ + +#include "alarms.h" + +#include "console.h" +#include "sound.h" +#include "time_manager.h" + +#include "esp_log.h" +#include +#include +#include + +#define TAG "Alarms" + +typedef struct { + bool set; + Time time; +} Alarm; + +typedef struct { + const Alarm *alarm; + Time end; +} ActiveAlarm; + +static Alarm alarms[CONFIG_MAX_ALARMS]; +static ActiveAlarm active[CONFIG_MAX_ALARMS]; +static unsigned active_count; +static Time last_check; + +static bool add_alarm(Time time) +{ + for (unsigned i = 0; i < CONFIG_MAX_ALARMS; ++i) { + if (!alarms[i].set) { + alarms[i].set = true; + alarms[i].time = time; + return true; + } + } + return false; +} + +static void remove_alarm(unsigned index) +{ + alarms[index].set = false; +} + +static void activate(unsigned index, Time end) +{ + if (active_count == 0) + sound_alert_on(); + + active[active_count].alarm = &alarms[index]; + active[active_count].end = end; + ++active_count; + + ESP_LOGI(TAG, "Alarm %u activated", index); +} + +static void dismiss(unsigned index) +{ + 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) +{ + 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; + } + + return new_time; +} + +static void check_alarms(const Time *now) +{ + 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)); + } + + for (unsigned i = 0; i != active_count; ++i) { + if (passed_since_last_check(active[i].end, now)) + dismiss(i); + } + + last_check = *now; +} + +static int command_func(int argc, char **argv) +{ + if (argc == 1) { + for (unsigned i = 0; i < CONFIG_MAX_ALARMS; ++i) { + if (!alarms[i].set) + continue; + printf( + "[%2u] %02u:%02u\n", i, alarms[i].time.hour, + alarms[i].time.minute); + } + return 0; + } else if (argc >= 3) { + if (strcmp(argv[1], "add") == 0) { + Time time = { .second = 0 }; + int n = sscanf(argv[2], "%02u:%02u", &time.hour, &time.minute); + if (n != 2) { + printf("Invalid time\n"); + return 1; + } + if (!add_alarm(time)) { + printf("Max number of alarms already set.\n"); + return 1; + } + return 0; + } else if (strcmp(argv[1], "remove") == 0) { + unsigned index; + int n = sscanf(argv[2], "%u", &index); + if (n != 1 || !alarms[index].set) { + printf("Invalid index\n"); + return 1; + } + remove_alarm(index); + return 0; + } else { + printf("Unrecognised subcommand\n"); + return 1; + } + } else { + printf("Invalid number of arguments\n"); + return 1; + } +} + +void alarms_init(void) +{ + memset(alarms, 0, sizeof(alarms)); + memset(active, 0, sizeof(active)); + active_count = 0; + last_check = get_time(); + + add_time_callback(check_alarms); + + console_register( + "alarms", "List, add and remove alarms", + "alarms OR alarms add OR alarms remove ", + command_func); +} + +void alarm_snooze(void) +{ +} + +void alarm_dismiss(void) +{ + if (active_count > 0) + dismiss(0); +} diff --git a/components/alarms/alarms.h b/components/alarms/alarms.h new file mode 100644 index 0000000..7437845 --- /dev/null +++ b/components/alarms/alarms.h @@ -0,0 +1,25 @@ +/* + * SPDX-License-Identifier: AGPL-3.0-only + * Copyright (c) Camden Dixie O'Brien + */ + +#ifndef ALARMS_H +#define ALARMS_H + +/** + * Intialize the alarms subsystem, loading any saved alarms from + * storage. + */ +void alarms_init(void); + +/** + * Snooze the current alarm (if any). + */ +void alarm_snooze(void); + +/** + * Dismiss the current alarm (if any). + */ +void alarm_dismiss(void); + +#endif diff --git a/components/buttons/CMakeLists.txt b/components/buttons/CMakeLists.txt index 926ada3..03b0223 100644 --- a/components/buttons/CMakeLists.txt +++ b/components/buttons/CMakeLists.txt @@ -1,5 +1,5 @@ idf_component_register( SRCS "buttons.c" INCLUDE_DIRS "." - REQUIRES driver esp_timer fatal + REQUIRES alarms driver esp_timer fatal ) diff --git a/components/buttons/buttons.c b/components/buttons/buttons.c index 14e802f..bf692cc 100644 --- a/components/buttons/buttons.c +++ b/components/buttons/buttons.c @@ -5,6 +5,7 @@ #include "buttons.h" +#include "alarms.h" #include "fatal.h" #include "driver/gpio.h" @@ -39,11 +40,11 @@ static void handle_presses(void *arg) { (void)arg; if (snooze_pressed) { - ESP_LOGI(TAG, "Snooze pressed"); + alarm_snooze(); snooze_pressed = false; } if (dismiss_pressed) { - ESP_LOGI(TAG, "Dismiss pressed"); + alarm_dismiss(); dismiss_pressed = false; } } diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 97afd46..8e67516 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -27,6 +27,12 @@ menu "Bedside clock settings" int "Toggle period of alert sound in milliseconds" default 300 endmenu + config ALERT_MINUTES + int "Number of minutes before alarm turned off automatically" + default 2 + config SNOOZE_MINUTES + int "Number of minutes to snooze alarms for" + default 5 config WIFI_MAX_RETRIES int "Maximum number of times to retry connecting to WiFi network" default 10 @@ -36,4 +42,7 @@ menu "Bedside clock settings" config DEFAULT_TASK_STACK int "Default task stack size (in words)" default 4096 + config MAX_ALARMS + int "Maximum number of alarms" + default 16 endmenu diff --git a/main/main.c b/main/main.c index a3fd129..893d710 100644 --- a/main/main.c +++ b/main/main.c @@ -3,6 +3,7 @@ * Copyright (c) Camden Dixie O'Brien */ +#include "alarms.h" #include "buttons.h" #include "console.h" #include "display.h" @@ -19,5 +20,6 @@ void app_main(void) time_manager_init(); display_init(); sound_init(); + alarms_init(); buttons_init(); }