#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "driver/gptimer.h"
#include "driver/ledc.h"
const gpio_num_t digitalPins[] = {13, 14, 27, 26}; // pines digitales del display
const gpio_num_t segments_pins[] = {25, 33, 5, 18, 15, 32, 16, 4}; // segmentos + puntos
#define BUZZER_PIN 23
const int numeros[10][7] = {
{1,1,1,1,1,1,0}, // 0
{0,1,1,0,0,0,0}, // 1
{1,1,0,1,1,0,1}, // 2
{1,1,1,1,0,0,1}, // 3
{0,1,1,0,0,1,1}, // 4
{1,0,1,1,0,1,1}, // 5
{1,0,1,1,1,1,1}, // 6
{1,1,1,0,0,0,0}, // 7
{1,1,1,1,1,1,1}, // 8
{1,1,1,1,0,1,1} // 9
};
static int current_hours = 0;
static int current_minutes = 0;
static int current_seconds = 0;
static int alarm_hours = 0;
static int alarm_minutes = 0;
static bool alarm_set = false;
static bool alarm_active = false;
#define LEDC_CHANNEL LEDC_CHANNEL_0
#define LEDC_TIMER LEDC_TIMER_0
#define LEDC_MODE LEDC_HIGH_SPEED_MODE
#define LEDC_DUTY_RES LEDC_TIMER_10_BIT // 10 bits => 0..1023
#define BUZZER_FREQ_HZ 416 // aprox 416 Hz
void init_gpio() {
for (int i = 0; i < 8; i++) {
gpio_reset_pin(segments_pins[i]);
gpio_set_direction(segments_pins[i], GPIO_MODE_OUTPUT);
gpio_set_level(segments_pins[i], 0);
}
for (int i = 0; i < 4; i++) {
gpio_reset_pin(digitalPins[i]);
gpio_set_direction(digitalPins[i], GPIO_MODE_OUTPUT);
gpio_set_level(digitalPins[i], 1);
}
}
void display_num(int num, int digit) {
for (int i = 0; i < 4; i++) gpio_set_level(digitalPins[i], 1);
for (int i = 0; i < 7; i++) gpio_set_level(segments_pins[i], numeros[num][i]);
gpio_set_level(digitalPins[digit], 0);
}
// hora en HH:MM
static int current_display_digit = 0;
void display_time_digit() {
int h = current_hours;
int m = current_minutes;
int digits[4] = {
h / 10,
h % 10,
m / 10,
m % 10
};
display_num(digits[current_display_digit], current_display_digit);
current_display_digit = (current_display_digit + 1) % 4;
}
static bool IRAM_ATTR display_timer_cb(gptimer_handle_t timer,
const gptimer_alarm_event_data_t *edata,
void *user_ctx) {
(void)timer; (void)edata; (void)user_ctx;
display_time_digit();
return true;
}
void init_buzzer_ledc() {
// Timer
ledc_timer_config_t ledc_timer = {
.speed_mode = LEDC_MODE,
.duty_resolution = LEDC_DUTY_RES,
.timer_num = LEDC_TIMER,
.freq_hz = BUZZER_FREQ_HZ,
.clk_cfg = LEDC_AUTO_CLK
};
ledc_timer_config(&ledc_timer);
ledc_channel_config_t ledc_channel = {
.gpio_num = BUZZER_PIN,
.speed_mode = LEDC_MODE,
.channel = LEDC_CHANNEL,
.intr_type = LEDC_INTR_DISABLE,
.timer_sel = LEDC_TIMER,
.duty = 0,
.hpoint = 0
};
ledc_channel_config(&ledc_channel);
}
static void set_buzzer_duty_percentage(int percent) {
if (percent <= 0) {
ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, 0);
ledc_update_duty(LEDC_MODE, LEDC_CHANNEL);
return;
}
if (percent > 100) percent = 100;
int max = (1 << 10) - 1;
int duty = (max * percent) / 100;
ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, duty);
ledc_update_duty(LEDC_MODE, LEDC_CHANNEL);
}
// tarea que mantiene el incremento del reloj en segundos
void clock_task(void *pv) {
while (1) {
vTaskDelay(pdMS_TO_TICKS(1000));
// aumentar un segundo
current_seconds++;
if (current_seconds >= 60) {
current_seconds = 0;
current_minutes++;
if (current_minutes >= 60) {
current_minutes = 0;
current_hours++;
if (current_hours >= 24) current_hours = 0;
}
}
int colon_state = (current_seconds % 2 == 0) ? 1 : 0;
gpio_set_level(segments_pins[7], colon_state);
if (alarm_set && current_hours == alarm_hours && current_minutes == alarm_minutes) {
alarm_active = true;
set_buzzer_duty_percentage(30);
} else {
alarm_active = false;
set_buzzer_duty_percentage(0);
}
}
}
//tarea para pedir hora actual y hora de alarma
void input_task(void *pv) {
char line[64];
int h, m;
// Pedir hora actual
while (1) {
printf("\nIntroduce la HORA ACTUAL en formato HH:MM : ");
fflush(stdout);
if (fgets(line, sizeof(line), stdin) == NULL) {
vTaskDelay(pdMS_TO_TICKS(200));
continue;
}
if (sscanf(line, "%d:%d", &h, &m) == 2) {
if (h >= 0 && h < 24 && m >= 0 && m < 60) {
current_hours = h;
current_minutes = m;
current_seconds = 0;
break;
}
}
printf("Formato o valores inválidos. Intenta de nuevo.\n");
}
// Pidir hora de alarma
while (1) {
printf("Introduce la HORA DE ALARMA en formato HH:MM, o escribe 'none' para desactivar: ");
fflush(stdout);
if (fgets(line, sizeof(line), stdin) == NULL) {
vTaskDelay(pdMS_TO_TICKS(200));
continue;
}
line[strcspn(line, "\r\n")] = 0;
if (strcasecmp(line, "none") == 0 || strcasecmp(line, "ninguna") == 0) {
alarm_set = false;
printf("Alarma desactivada.\n");
break;
}
if (sscanf(line, "%d:%d", &h, &m) == 2) {
if (h >= 0 && h < 24 && m >= 0 && m < 60) {
alarm_hours = h;
alarm_minutes = m;
alarm_set = true;
printf("Alarma programada para las %02d:%02d\n", alarm_hours, alarm_minutes);
break;
}
}
printf("Formato o valores inválidos. Intenta de nuevo.\n");
}
vTaskDelete(NULL);
}
void app_main() {
init_gpio();
init_buzzer_ledc();
gptimer_handle_t display_timer = NULL;
gptimer_config_t disp_cfg = {
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = 1000000,
};
ESP_ERROR_CHECK(gptimer_new_timer(&disp_cfg, &display_timer));
gptimer_event_callbacks_t disp_cb = {.on_alarm = display_timer_cb};
ESP_ERROR_CHECK(gptimer_register_event_callbacks(display_timer, &disp_cb, NULL));
ESP_ERROR_CHECK(gptimer_enable(display_timer));
gptimer_alarm_config_t disp_alarm = {
.alarm_count = 5000,
.reload_count = 0,
.flags = { .auto_reload_on_alarm = 1 }
};
ESP_ERROR_CHECK(gptimer_set_alarm_action(display_timer, &disp_alarm));
ESP_ERROR_CHECK(gptimer_start(display_timer));
xTaskCreate(clock_task, "clock_task", 2048, NULL, 5, NULL);
xTaskCreate(input_task, "input_task", 4096, NULL, 5, NULL);
}