Add alarms component

This commit is contained in:
Camden Dixie O'Brien 2023-05-18 01:29:17 +01:00
parent d7dd2025b8
commit 0d12bb7445
8 changed files with 227 additions and 4 deletions

View File

@ -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

View File

@ -0,0 +1,5 @@
idf_component_register(
SRCS "alarms.c"
INCLUDE_DIRS "."
REQUIRES console_wrapper sound time
)

181
components/alarms/alarms.c Normal file
View File

@ -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 <stdbool.h>
#include <stdio.h>
#include <string.h>
#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 <hh:mm> OR alarms remove <index>",
command_func);
}
void alarm_snooze(void)
{
}
void alarm_dismiss(void)
{
if (active_count > 0)
dismiss(0);
}

View File

@ -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

View File

@ -1,5 +1,5 @@
idf_component_register(
SRCS "buttons.c"
INCLUDE_DIRS "."
REQUIRES driver esp_timer fatal
REQUIRES alarms driver esp_timer fatal
)

View File

@ -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;
}
}

View File

@ -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

View File

@ -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();
}