/*
* ============================================================================
* RELOJ DIGITAL ESP32S3 CON ALARMA Y BUZZER PWM
* ============================================================================
*
* DESCRIPCIÓN:
* Este proyecto implementa un reloj digital de 4 dígitos con display de 7 segmentos,
* funcionalidad de alarma y buzzer con señal PWM de frecuencia audible.
*
* CARACTERÍSTICAS:
* - Display multiplexado de 4 dígitos (HH:MM formato 24 horas)
* - Configuración de hora y alarma mediante botones
* - Buzzer con patrón PWM (2.4ms ON / 2.4ms OFF = 208Hz)
* - Indicador de dos puntos parpadeante
* - Detección precisa de flancos en botones
*
* AUTORES: Julian Rosas
* FECHA: Agosto 2025
* ============================================================================
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h" // Sistema operativo en tiempo real
#include "freertos/task.h" // Gestión de tareas
#include "driver/gpio.h" // Control de pines GPIO
#include "driver/gptimer.h" // Timer de alta precisión para tiempo principal
#include "esp_timer.h" // Timer de sistema para display y buzzer
/*
* ============================================================================
* CONFIGURACIÓN DE HARDWARE - PINES GPIO
* ============================================================================
*/
// Definición de pines para display de 7 segmentos (ánodo común)
#define SEG_A GPIO_NUM_2 // Segmento A del display
#define SEG_B GPIO_NUM_4 // Segmento B del display
#define SEG_C GPIO_NUM_5 // Segmento C del display
#define SEG_D GPIO_NUM_18 // Segmento D del display
#define SEG_E GPIO_NUM_19 // Segmento E del display
#define SEG_F GPIO_NUM_21 // Segmento F del display
#define SEG_G GPIO_NUM_47 // Segmento G del display
// Pines para selección de dígito (multiplexación de 4 displays)
#define DIG_1 GPIO_NUM_48 // Decenas de hora (ej: 1 en 12:34)
#define DIG_2 GPIO_NUM_45 // Unidades de hora (ej: 2 en 12:34)
#define DIG_3 GPIO_NUM_0 // Decenas de minuto (ej: 3 en 12:34)
#define DIG_4 GPIO_NUM_35 // Unidades de minuto (ej: 4 en 12:34)
// Pin para el separador de tiempo (dos puntos entre HH:MM)
#define COLON_PIN GPIO_NUM_37
// Pines para interfaz de usuario (botones con pull-up interno)
#define BTN_SET_TIME GPIO_NUM_9 // Entrar/salir modo configurar hora
#define BTN_SET_ALARM GPIO_NUM_10 // Entrar/salir modo configurar alarma
#define BTN_INC GPIO_NUM_11 // Incrementar valores durante configuración
#define BTN_TOGGLE GPIO_NUM_12 // Cambiar posición de edición / ON-OFF alarma
// Pin para salida de audio
#define BUZZER_PIN GPIO_NUM_13 // Buzzer activo con señal PWM
/*
* ============================================================================
* CONFIGURACIÓN DEL BUZZER PWM - VALORES MODIFICABLES
* ============================================================================
*
* IMPORTANTE: Para cambiar el comportamiento del buzzer, modificar estos valores:
*
* 1. PERÍODO DEL TIMER (línea ~410):
* ESP_ERROR_CHECK(esp_timer_start_periodic(buzzer_timer, 2400));
* - 2400 = 2.4ms (valor actual)
* - Para cambiar: usar microsegundos (ej: 1000 = 1ms, 5000 = 5ms)
*
* 2. PATRÓN ON/OFF (líneas ~240-250):
* if (buzzer_cycle_count % 2) // Alternar cada ciclo
* - % 2 = alterna cada ciclo (2.4ms ON, 2.4ms OFF)
* - % 4 = alterna cada 2 ciclos (4.8ms ON, 4.8ms OFF)
* - % 6 = alterna cada 3 ciclos (7.2ms ON, 7.2ms OFF)
*
* 3. RESULTADO ACTUAL:
* - Período: 2.4ms ON + 2.4ms OFF = 4.8ms total
* - Frecuencia audible: 1/4.8ms ≈ 208Hz
* - Duty cycle: 50% (tiempo ON = tiempo OFF)
*
* ============================================================================
*/
/*
* ============================================================================
* DEFINICIÓN DE TIPOS Y VARIABLES GLOBALES
* ============================================================================
*/
// Estados del sistema para máquina de estados principal
typedef enum {
STATE_SHOW_TIME, // Mostrar hora actual (modo normal)
STATE_SET_TIME, // Configurar hora actual
STATE_SET_ALARM // Configurar hora de alarma
} system_state_t;
// Estados de edición para saber qué dígito se está modificando
typedef enum {
EDIT_HOUR_TENS, // Decenas de hora (primer dígito)
EDIT_HOUR_UNITS, // Unidades de hora (segundo dígito)
EDIT_MIN_TENS, // Decenas de minuto (tercer dígito)
EDIT_MIN_UNITS // Unidades de minuto (cuarto dígito)
} edit_state_t;
// Variables de estado del sistema
static system_state_t current_state = STATE_SHOW_TIME;
static edit_state_t current_edit_position = EDIT_HOUR_TENS;
// Variables de tiempo (formato 24 horas)
static int current_hour = 0; // Hora actual (0-23)
static int current_min = 0; // Minutos actuales (0-59)
static int alarm_hour = 0; // Hora de alarma (0-23)
static int alarm_min = 0; // Minutos de alarma (0-59)
// Variables de control
static bool alarm_enabled = false; // Estado de activación de alarma
static bool alarm_ringing = false; // Estado de sonido de alarma
static bool blink_colon = false; // Control de parpadeo de dos puntos
static bool time_set = false; // Indica si se ha configurado la hora inicial
static int blink_counter = 0; // Contador para parpadeo de dígitos durante edición
// Handles de timers del sistema
static gptimer_handle_t time_timer = NULL; // Timer principal de 1 segundo
static esp_timer_handle_t display_timer = NULL; // Timer de multiplexación (5ms)
static esp_timer_handle_t buzzer_timer = NULL; // Timer del buzzer PWM (2.4ms)
// Variables específicas del buzzer PWM
static int buzzer_cycle_count = 0; // Contador de ciclos para patrón PWM
// Patrones para dígitos 0-9 en display de 7 segmentos (ánodo común)
// Cada bit representa un segmento: G F E D C B A
// Lógica: 0 = segmento encendido, 1 = segmento apagado (ánodo común)
static const uint8_t digit_patterns[10] = {
0b11000000, // 0: segmentos A,B,C,D,E,F encendidos
0b11111001, // 1: segmentos B,C encendidos
0b10100100, // 2: segmentos A,B,G,E,D encendidos
0b10110000, // 3: segmentos A,B,G,C,D encendidos
0b10011001, // 4: segmentos F,G,B,C encendidos
0b10010010, // 5: segmentos A,F,G,C,D encendidos
0b10000010, // 6: segmentos A,F,G,E,D,C encendidos
0b11111000, // 7: segmentos A,B,C encendidos
0b10000000, // 8: todos los segmentos encendidos
0b10010000 // 9: segmentos A,B,C,D,F,G encendidos
};
/*
* ============================================================================
* FUNCIÓN DE CONFIGURACIÓN INICIAL DE GPIO
* ============================================================================
*/
void configure_gpio_pins() {
printf("=== CONFIGURANDO HARDWARE GPIO ===\n");
// Configurar pines de segmentos como salida
gpio_set_direction(SEG_A, GPIO_MODE_OUTPUT);
gpio_set_direction(SEG_B, GPIO_MODE_OUTPUT);
gpio_set_direction(SEG_C, GPIO_MODE_OUTPUT);
gpio_set_direction(SEG_D, GPIO_MODE_OUTPUT);
gpio_set_direction(SEG_E, GPIO_MODE_OUTPUT);
gpio_set_direction(SEG_F, GPIO_MODE_OUTPUT);
gpio_set_direction(SEG_G, GPIO_MODE_OUTPUT);
// Configurar pines de dígitos como salida
gpio_set_direction(DIG_1, GPIO_MODE_OUTPUT);
gpio_set_direction(DIG_2, GPIO_MODE_OUTPUT);
gpio_set_direction(DIG_3, GPIO_MODE_OUTPUT);
gpio_set_direction(DIG_4, GPIO_MODE_OUTPUT);
// Configurar pin de los dos puntos como salida
gpio_set_direction(COLON_PIN, GPIO_MODE_OUTPUT);
// Configurar pin del buzzer como salida
gpio_set_direction(BUZZER_PIN, GPIO_MODE_OUTPUT);
// Configurar botones como entrada con pull-up
printf("Configurando botones GPIO 9,10,11,12 con pull-up...\n");
gpio_set_direction(BTN_SET_TIME, GPIO_MODE_INPUT);
gpio_set_pull_mode(BTN_SET_TIME, GPIO_PULLUP_ONLY);
gpio_set_direction(BTN_SET_ALARM, GPIO_MODE_INPUT);
gpio_set_pull_mode(BTN_SET_ALARM, GPIO_PULLUP_ONLY);
gpio_set_direction(BTN_INC, GPIO_MODE_INPUT);
gpio_set_pull_mode(BTN_INC, GPIO_PULLUP_ONLY);
gpio_set_direction(BTN_TOGGLE, GPIO_MODE_INPUT);
gpio_set_pull_mode(BTN_TOGGLE, GPIO_PULLUP_ONLY);
// Verificar estado inicial de botones
printf("Estado inicial botones: GPIO9=%d GPIO10=%d GPIO11=%d GPIO12=%d\n",
gpio_get_level(BTN_SET_TIME), gpio_get_level(BTN_SET_ALARM),
gpio_get_level(BTN_INC), gpio_get_level(BTN_TOGGLE));
// Inicializar todos los pines en nivel bajo
gpio_set_level(SEG_A, 0);
gpio_set_level(SEG_B, 0);
gpio_set_level(SEG_C, 0);
gpio_set_level(SEG_D, 0);
gpio_set_level(SEG_E, 0);
gpio_set_level(SEG_F, 0);
gpio_set_level(SEG_G, 0);
gpio_set_level(DIG_1, 0);
gpio_set_level(DIG_2, 0);
gpio_set_level(DIG_3, 0);
gpio_set_level(DIG_4, 0);
gpio_set_level(COLON_PIN, 0);
gpio_set_level(BUZZER_PIN, 0);
printf("GPIO configurado correctamente\n");
}
// Función para mostrar un dígito específico
void display_digit(int digit, int position) {
// Apagar todos los dígitos primero
gpio_set_level(DIG_1, 0);
gpio_set_level(DIG_2, 0);
gpio_set_level(DIG_3, 0);
gpio_set_level(DIG_4, 0);
if (digit < 0 || digit > 9) return;
uint8_t pattern = digit_patterns[digit];
// Configurar segmentos directamente (los patrones ya están correctos para ánodo común)
gpio_set_level(SEG_A, (pattern & 0x01) ? 1 : 0);
gpio_set_level(SEG_B, (pattern & 0x02) ? 1 : 0);
gpio_set_level(SEG_C, (pattern & 0x04) ? 1 : 0);
gpio_set_level(SEG_D, (pattern & 0x08) ? 1 : 0);
gpio_set_level(SEG_E, (pattern & 0x10) ? 1 : 0);
gpio_set_level(SEG_F, (pattern & 0x20) ? 1 : 0);
gpio_set_level(SEG_G, (pattern & 0x40) ? 1 : 0);
// Activar el dígito correspondiente (ánodo común: 1 = ON)
switch (position) {
case 1: gpio_set_level(DIG_1, 1); break;
case 2: gpio_set_level(DIG_2, 1); break;
case 3: gpio_set_level(DIG_3, 1); break;
case 4: gpio_set_level(DIG_4, 1); break;
}
}
// Callback del timer principal (incrementa el tiempo cada segundo)
static bool IRAM_ATTR time_timer_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx) {
// Solo incrementar el tiempo si ya se configuró la hora inicial
if (time_set) {
// Incrementar segundos internos (no se muestran en display)
static int seconds = 0;
seconds++;
// Solo incrementar minutos cada 60 segundos
if (seconds >= 60) {
seconds = 0;
current_min++;
// Solo incrementar horas cada 60 minutos
if (current_min >= 60) {
current_min = 0;
current_hour++;
// Resetear día cada 24 horas
if (current_hour >= 24) {
current_hour = 0;
}
}
// Verificar si es hora de la alarma (solo cuando cambian los minutos)
if (alarm_enabled && current_hour == alarm_hour && current_min == alarm_min) {
if (!alarm_ringing) {
alarm_ringing = true;
// No usar ESP_LOGI en ISR, se imprimirá en el bucle principal
}
}
}
}
// Alternar dos puntos cada segundo para indicar funcionamiento
blink_colon = !blink_colon;
return false; // No necesita yield desde ISR
}
/*
* ============================================================================
* CALLBACK DEL BUZZER PWM - CONFIGURACIÓN MODIFICABLE
* ============================================================================
*
* Esta función se ejecuta cada 2.4ms cuando el timer del buzzer está activo.
* Genera una señal PWM con duty cycle del 50% para crear un tono audible.
*
* PARÁMETROS MODIFICABLES:
* - Línea "buzzer_cycle_count % 2": controla el patrón ON/OFF
* % 2 = 2.4ms ON, 2.4ms OFF (frecuencia ~208Hz)
* % 4 = 4.8ms ON, 4.8ms OFF (frecuencia ~104Hz)
* % 6 = 7.2ms ON, 7.2ms OFF (frecuencia ~69Hz)
*
* ============================================================================
*/
static void buzzer_timer_callback(void* arg) {
if (!alarm_ringing) {
// Si no hay alarma activa, mantener buzzer apagado y resetear contadores
gpio_set_level(BUZZER_PIN, 0);
buzzer_cycle_count = 0;
return;
}
// Incrementar contador de ciclos (cada 2.4ms)
buzzer_cycle_count++;
// *** PATRÓN PWM MODIFICABLE ***
// Alternar cada ciclo para generar 50% duty cycle
if (buzzer_cycle_count % 2) {
// Ciclo impar: Buzzer ON durante 2.4ms
gpio_set_level(BUZZER_PIN, 1);
} else {
// Ciclo par: Buzzer OFF durante 2.4ms
gpio_set_level(BUZZER_PIN, 0);
}
// Resultado: 2.4ms ON + 2.4ms OFF = 4.8ms período total
// Frecuencia audible: 1 / 4.8ms ≈ 208Hz
}
// Callback del timer de display (multiplexación rápida)
static void display_timer_callback(void* arg) {
static int digit_position = 1;
int hour_to_show, min_to_show;
// Incrementar contador para parpadeo
blink_counter++;
// Determinar qué mostrar según el estado
switch (current_state) {
case STATE_SHOW_TIME:
hour_to_show = current_hour;
min_to_show = current_min;
break;
case STATE_SET_TIME:
hour_to_show = current_hour;
min_to_show = current_min;
break;
case STATE_SET_ALARM:
hour_to_show = alarm_hour;
min_to_show = alarm_min;
break;
default:
hour_to_show = current_hour;
min_to_show = current_min;
break;
}
// Controlar los dos puntos según el estado
if (current_state == STATE_SHOW_TIME && time_set) {
// Parpadear solo cuando el reloj está funcionando
gpio_set_level(COLON_PIN, blink_colon ? 1 : 0);
} else if (current_state == STATE_SET_TIME || current_state == STATE_SET_ALARM) {
// Encendido fijo durante configuración
gpio_set_level(COLON_PIN, 1);
} else {
// Apagado si no se ha configurado la hora
gpio_set_level(COLON_PIN, 0);
}
// Verificar si el dígito actual debe parpadear (durante edición)
bool should_show_digit = true;
if (current_state == STATE_SET_TIME || current_state == STATE_SET_ALARM) {
// Hacer parpadear el dígito que se está editando
bool is_editing_position = false;
switch (current_edit_position) {
case EDIT_HOUR_TENS: is_editing_position = (digit_position == 1); break;
case EDIT_HOUR_UNITS: is_editing_position = (digit_position == 2); break;
case EDIT_MIN_TENS: is_editing_position = (digit_position == 3); break;
case EDIT_MIN_UNITS: is_editing_position = (digit_position == 4); break;
}
if (is_editing_position) {
// Parpadear más rápido (cada ~400ms)
should_show_digit = (blink_counter / 20) % 2;
}
}
// Mostrar el dígito correspondiente solo si debe mostrarse
if (should_show_digit) {
switch (digit_position) {
case 1: // Decenas de hora
display_digit(hour_to_show / 10, 1);
break;
case 2: // Unidades de hora
display_digit(hour_to_show % 10, 2);
break;
case 3: // Decenas de minuto
display_digit(min_to_show / 10, 3);
break;
case 4: // Unidades de minuto
display_digit(min_to_show % 10, 4);
break;
}
} else {
// No mostrar nada (apagar todos los dígitos)
gpio_set_level(DIG_1, 0);
gpio_set_level(DIG_2, 0);
gpio_set_level(DIG_3, 0);
gpio_set_level(DIG_4, 0);
}
digit_position++;
if (digit_position > 4) {
digit_position = 1;
}
}
// Configurar timer principal (1 segundo)
void configure_time_timer() {
gptimer_config_t timer_config = {
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = 1000000, // 1MHz, 1 tick = 1µs
};
ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &time_timer));
gptimer_alarm_config_t alarm_config = {
.alarm_count = 1000000, // 1 segundo
.reload_count = 0,
.flags.auto_reload_on_alarm = true,
};
ESP_ERROR_CHECK(gptimer_set_alarm_action(time_timer, &alarm_config));
gptimer_event_callbacks_t cbs = {
.on_alarm = time_timer_callback,
};
ESP_ERROR_CHECK(gptimer_register_event_callbacks(time_timer, &cbs, NULL));
ESP_ERROR_CHECK(gptimer_enable(time_timer));
ESP_ERROR_CHECK(gptimer_start(time_timer));
}
// Configurar timer de display (esp_timer para 5ms)
void configure_display_timer() {
const esp_timer_create_args_t display_timer_args = {
.callback = &display_timer_callback,
.name = "display_timer"
};
ESP_ERROR_CHECK(esp_timer_create(&display_timer_args, &display_timer));
ESP_ERROR_CHECK(esp_timer_start_periodic(display_timer, 5000)); // 5ms = 5000 µs
printf("Display timer (ESP Timer) configurado: 5ms\n");
}
/*
* ============================================================================
* CONFIGURACIÓN DE TIMER DEL BUZZER - VALORES MODIFICABLES
* ============================================================================
*
* Esta función configura el timer que controla el buzzer PWM.
*
* VALOR CRÍTICO MODIFICABLE:
* - Línea "esp_timer_start_periodic(buzzer_timer, 2400)"
* - 2400 = período de 2.4ms (valor actual)
* - Para cambiar frecuencia, modificar este valor en microsegundos:
* * 1000 = 1ms → frecuencia más alta (~500Hz)
* * 5000 = 5ms → frecuencia más baja (~100Hz)
* * 10000 = 10ms → frecuencia muy baja (~50Hz)
*
* ============================================================================
*/
void configure_buzzer_timer() {
const esp_timer_create_args_t buzzer_timer_args = {
.callback = &buzzer_timer_callback,
.name = "buzzer_timer"
};
ESP_ERROR_CHECK(esp_timer_create(&buzzer_timer_args, &buzzer_timer));
// *** VALOR MODIFICABLE: período del timer en microsegundos ***
ESP_ERROR_CHECK(esp_timer_start_periodic(buzzer_timer, 2400)); // 2.4ms = 2400 µs
printf("=== BUZZER PWM CONFIGURADO ===\n");
printf(" Período del timer: 2.4ms (2,400 µs)\n");
printf(" Patrón PWM: 2.4ms ON + 2.4ms OFF = 4.8ms ciclo completo\n");
printf(" Duty cycle: 50%% exacto\n");
printf(" Frecuencia audible resultante: ~208Hz (1 / 4.8ms)\n");
printf(" Para modificar: cambiar valor 2400 en línea ESP_ERROR_CHECK\n");
}
// Función para incrementar valor según posición de edición
void increment_current_value() {
if (current_state == STATE_SET_TIME) {
switch (current_edit_position) {
case EDIT_HOUR_TENS:
current_hour = (current_hour + 10) % 30; // Incrementar decenas
if (current_hour >= 24) current_hour = current_hour % 10; // Limitar a 0-2 para decenas de hora
printf("DEBUG: Incrementando decenas de hora - Hora actual: %02d:%02d\n", current_hour, current_min);
break;
case EDIT_HOUR_UNITS:
if ((current_hour / 10) == 2) {
// Si estamos en 20-23, solo permitir 0-3
current_hour = (current_hour / 10) * 10 + ((current_hour % 10 + 1) % 4);
} else {
// Si estamos en 00-19, permitir 0-9
current_hour = (current_hour / 10) * 10 + ((current_hour % 10 + 1) % 10);
}
printf("DEBUG: Incrementando unidades de hora - Hora actual: %02d:%02d\n", current_hour, current_min);
break;
case EDIT_MIN_TENS:
current_min = (current_min % 10) + (((current_min / 10 + 1) % 6) * 10);
printf("DEBUG: Incrementando decenas de minutos - Hora actual: %02d:%02d\n", current_hour, current_min);
break;
case EDIT_MIN_UNITS:
current_min = (current_min / 10) * 10 + ((current_min % 10 + 1) % 10);
printf("DEBUG: Incrementando unidades de minutos - Hora actual: %02d:%02d\n", current_hour, current_min);
break;
}
} else if (current_state == STATE_SET_ALARM) {
switch (current_edit_position) {
case EDIT_HOUR_TENS:
alarm_hour = (alarm_hour + 10) % 30;
if (alarm_hour >= 24) alarm_hour = alarm_hour % 10;
printf("DEBUG: Incrementando decenas de hora alarma - Alarma: %02d:%02d\n", alarm_hour, alarm_min);
break;
case EDIT_HOUR_UNITS:
if ((alarm_hour / 10) == 2) {
alarm_hour = (alarm_hour / 10) * 10 + ((alarm_hour % 10 + 1) % 4);
} else {
alarm_hour = (alarm_hour / 10) * 10 + ((alarm_hour % 10 + 1) % 10);
}
printf("DEBUG: Incrementando unidades de hora alarma - Alarma: %02d:%02d\n", alarm_hour, alarm_min);
break;
case EDIT_MIN_TENS:
alarm_min = (alarm_min % 10) + (((alarm_min / 10 + 1) % 6) * 10);
printf("DEBUG: Incrementando decenas de minutos alarma - Alarma: %02d:%02d\n", alarm_hour, alarm_min);
break;
case EDIT_MIN_UNITS:
alarm_min = (alarm_min / 10) * 10 + ((alarm_min % 10 + 1) % 10);
printf("DEBUG: Incrementando unidades de minutos alarma - Alarma: %02d:%02d\n", alarm_hour, alarm_min);
break;
}
}
}
// Tarea para manejar botones
void button_task(void *pvParameters) {
bool btn_set_time_prev = true;
bool btn_set_alarm_prev = true;
bool btn_inc_prev = true;
bool btn_toggle_prev = true;
printf("Button task iniciada - Monitoreando GPIO 9,10,11,12\n");
while (1) {
bool btn_set_time = gpio_get_level(BTN_SET_TIME);
bool btn_set_alarm = gpio_get_level(BTN_SET_ALARM);
bool btn_inc = gpio_get_level(BTN_INC);
bool btn_toggle = gpio_get_level(BTN_TOGGLE);
// Botón SET TIME (GPIO 9) - flanco descendente
if (btn_set_time_prev && !btn_set_time) {
printf("*** BOTON SET_TIME PRESIONADO ***\n");
if (current_state == STATE_SHOW_TIME) {
current_state = STATE_SET_TIME;
current_edit_position = EDIT_HOUR_TENS;
printf("Modo configurar hora - Editando: Decenas Hora\n");
printf("Hora actual a editar: %02d:%02d\n", current_hour, current_min);
} else if (current_state == STATE_SET_TIME) {
current_state = STATE_SHOW_TIME;
time_set = true; // Activar el reloj después de configurar la hora
printf("Hora configurada: %02d:%02d - Reloj iniciado\n", current_hour, current_min);
}
vTaskDelay(200 / portTICK_PERIOD_MS); // Anti-rebote adicional
}
// Botón SET ALARM (GPIO 10) - flanco descendente
if (btn_set_alarm_prev && !btn_set_alarm) {
printf("*** BOTON SET_ALARM PRESIONADO ***\n");
if (current_state == STATE_SHOW_TIME) {
current_state = STATE_SET_ALARM;
current_edit_position = EDIT_HOUR_TENS;
alarm_hour = 0; // Reiniciar alarma a 00:00
alarm_min = 0;
printf("Modo configurar alarma - Editando: Decenas Hora\n");
printf("Alarma a editar: %02d:%02d\n", alarm_hour, alarm_min);
} else if (current_state == STATE_SET_ALARM) {
current_state = STATE_SHOW_TIME;
alarm_enabled = true;
printf("Alarma configurada: %02d:%02d (ACTIVADA)\n", alarm_hour, alarm_min);
}
vTaskDelay(200 / portTICK_PERIOD_MS);
}
// Botón INCREMENT (GPIO 11) - flanco descendente
if (btn_inc_prev && !btn_inc) {
printf("*** BOTON INCREMENT PRESIONADO ***\n");
if (current_state == STATE_SET_TIME || current_state == STATE_SET_ALARM) {
printf("Estado actual - Editando posicion: %d\n", current_edit_position);
increment_current_value();
if (current_state == STATE_SET_TIME) {
printf("Resultado - Hora actual: %02d:%02d\n", current_hour, current_min);
} else {
printf("Resultado - Alarma: %02d:%02d\n", alarm_hour, alarm_min);
}
} else {
printf("INCREMENT ignorado - No estamos en modo edicion\n");
}
vTaskDelay(200 / portTICK_PERIOD_MS);
}
// Botón TOGGLE (GPIO 12) - flanco descendente
if (btn_toggle_prev && !btn_toggle) {
printf("*** BOTON TOGGLE PRESIONADO ***\n");
if (alarm_ringing) {
// Si la alarma está sonando, apagarla
alarm_ringing = false;
printf("Alarma apagada por usuario\n");
// Asegurar que el buzzer se apague inmediatamente al detener alarma
gpio_set_level(BUZZER_PIN, 0);
buzzer_cycle_count = 0;
} else if (current_state == STATE_SET_TIME || current_state == STATE_SET_ALARM) {
// Cambiar posición de edición
printf("Posicion anterior: %d\n", current_edit_position);
current_edit_position = (current_edit_position + 1) % 4;
printf("Nueva posicion: %d\n", current_edit_position);
const char* positions[] = {"Decenas Hora", "Unidades Hora", "Decenas Min", "Unidades Min"};
printf("Ahora editando: %s\n", positions[current_edit_position]);
} else if (current_state == STATE_SHOW_TIME) {
// Activar/desactivar alarma
alarm_enabled = !alarm_enabled;
printf("Alarma %s\n", alarm_enabled ? "activada" : "desactivada");
// Si se desactiva la alarma y estaba sonando, pararla
if (!alarm_enabled && alarm_ringing) {
alarm_ringing = false;
printf("Alarma detenida al desactivarla\n");
// Asegurar que el buzzer se apague inmediatamente al desactivar alarma
gpio_set_level(BUZZER_PIN, 0);
buzzer_cycle_count = 0;
}
}
vTaskDelay(200 / portTICK_PERIOD_MS);
}
// Guardar estados anteriores
btn_set_time_prev = btn_set_time;
btn_set_alarm_prev = btn_set_alarm;
btn_inc_prev = btn_inc;
btn_toggle_prev = btn_toggle;
vTaskDelay(50 / portTICK_PERIOD_MS); // Debounce de 50ms
}
}
void app_main() {
printf("Iniciando reloj digital con alarma ESP32S3\n");
// Configurar pines GPIO
configure_gpio_pins();
// Configurar timers
configure_time_timer();
configure_display_timer();
configure_buzzer_timer();
// Crear tarea para manejar botones
xTaskCreate(button_task, "button_task", 4096, NULL, 10, NULL);
printf("Sistema iniciado correctamente\n");
printf("Botones:\n");
printf(" GPIO9 (SET TIME): Entrar/salir modo configurar hora\n");
printf(" GPIO10 (SET ALARM): Entrar/salir modo configurar alarma\n");
printf(" GPIO11 (INCREMENT): Incrementar valores\n");
printf(" GPIO12 (TOGGLE): Cambiar posicion de edicion\n");
printf("Reloj iniciara en 00:00 hasta configurar la hora manualmente\n");
// Variable para detectar cuando se activa la alarma
static bool alarm_was_ringing = false;
// Bucle principal
while (1) {
vTaskDelay(1000 / portTICK_PERIOD_MS);
// Detectar cuando se activa la alarma
if (alarm_ringing && !alarm_was_ringing) {
printf("*** ALARMA ACTIVADA - SONANDO ***\n");
printf("Hora actual: %02d:%02d coincide con alarma: %02d:%02d\n",
current_hour, current_min, alarm_hour, alarm_min);
printf("INICIANDO BUZZER - Patron 2.4ms ON / 2.4ms OFF (~208Hz)\n");
alarm_was_ringing = true;
} else if (!alarm_ringing && alarm_was_ringing) {
printf("*** ALARMA DESACTIVADA ***\n");
printf("DETENIENDO BUZZER\n");
alarm_was_ringing = false;
}
// Log periódico del estado (cada segundo cuando está funcionando)
if (current_state == STATE_SHOW_TIME && time_set) {
printf("Hora: %02d:%02d | Alarma: %02d:%02d (%s) %s\n",
current_hour, current_min, alarm_hour, alarm_min,
alarm_enabled ? "ON" : "OFF",
alarm_ringing ? "- SONANDO!!!" : "");
}
}
}Loading
esp32-s3-devkitc-1
esp32-s3-devkitc-1