Alarm Scheduling
The alarm manager stores alarm configurations in NVS flash and checks every 30 seconds whether any alarm should fire.
Alarm Data Model
// components/alarm_manager/include/alarm_manager.h
#pragma once
#include <stdint.h>
#include <stdbool.h>
#define ALARM_MAX_COUNT 8
#define ALARM_FILE_LEN 64
typedef struct {
uint8_t hour;
uint8_t minute;
bool enabled;
bool repeat;
uint8_t days_of_week; // bitmask: bit 0 = Sunday, bit 6 = Saturday
char audio_file[ALARM_FILE_LEN]; // e.g. "/lfs/alarms/alarm.wav"
} alarm_t;
typedef struct {
alarm_t alarms[ALARM_MAX_COUNT];
uint8_t count;
} alarm_config_t;
esp_err_t alarm_manager_init(void);
esp_err_t alarm_manager_get_all(alarm_config_t *out);
esp_err_t alarm_manager_set(uint8_t index, const alarm_t *alarm);
esp_err_t alarm_manager_delete(uint8_t index);
bool alarm_manager_check_now(alarm_t *fired_alarm); // returns true if alarm fires
time_t alarm_manager_next_trigger(void); // time_t of next alarm
NVS Storage
Serialize the entire alarm_config_t struct as a binary blob:
#define NVS_NAMESPACE "alarms"
#define NVS_KEY "config"
esp_err_t alarm_manager_save(const alarm_config_t *cfg) {
nvs_handle_t h;
ESP_ERROR_CHECK(nvs_open(NVS_NAMESPACE, NVS_READWRITE, &h));
ESP_ERROR_CHECK(nvs_set_blob(h, NVS_KEY, cfg, sizeof(alarm_config_t)));
ESP_ERROR_CHECK(nvs_commit(h));
nvs_close(h);
return ESP_OK;
}
esp_err_t alarm_manager_load(alarm_config_t *cfg) {
nvs_handle_t h;
if (nvs_open(NVS_NAMESPACE, NVS_READONLY, &h) != ESP_OK) {
memset(cfg, 0, sizeof(*cfg)); // no saved config — start empty
return ESP_OK;
}
size_t len = sizeof(alarm_config_t);
esp_err_t ret = nvs_get_blob(h, NVS_KEY, cfg, &len);
nvs_close(h);
return ret;
}
Scheduler Loop
The alarm check runs in the main task every 30 seconds:
static void alarm_check_tick(void) {
alarm_t fired;
if (alarm_manager_check_now(&fired)) {
// Transition to ALARM_FIRING state
alarm_event_t evt = { .alarm = fired };
xQueueSend(alarm_event_queue, &evt, 0);
}
}
alarm_manager_check_now compares the current local time against each enabled alarm:
bool alarm_manager_check_now(alarm_t *fired) {
time_t now = time(NULL);
struct tm *t = localtime(&now);
for (int i = 0; i < config.count; i++) {
alarm_t *a = &config.alarms[i];
if (!a->enabled) continue;
if (a->hour != t->tm_hour || a->minute != t->tm_min) continue;
if (t->tm_sec > 30) continue; // only fire in first 30s of the minute
// Check day of week if repeat alarm
if (a->repeat) {
uint8_t dow = t->tm_wday; // 0=Sunday
if (!(a->days_of_week & (1 << dow))) continue;
}
*fired = *a;
// Disable one-shot alarms after firing
if (!a->repeat) a->enabled = false;
alarm_manager_save(&config);
return true;
}
return false;
}
Snooze
Snooze adds 9 minutes to the current time and re-arms a one-shot timer:
static time_t snooze_target = 0;
void alarm_snooze(void) {
snooze_target = time(NULL) + 9 * 60;
}
// In the alarm_check_tick, also check:
if (snooze_target > 0 && time(NULL) >= snooze_target) {
snooze_target = 0;
// Re-fire the snoozed alarm
xQueueSend(alarm_event_queue, &last_fired_alarm, 0);
}