#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "esp_timer.h"
/* Macro para obtener el tiempo desde inicio del programa*/
#define current_time esp_timer_get_time()
/* Macros para identificar los GPIOS como entrada */
#define INPUT1 GPIO_NUM_18
#define INPUT2 GPIO_NUM_19
#define INPUT3 GPIO_NUM_21
#define INPUT4 GPIO_NUM_22
/* Macros para identificar los GPIOS como salida */
#define OUTPUT1 GPIO_NUM_23
#define OUTPUT2 GPIO_NUM_25
#define OUTPUT3 GPIO_NUM_26
#define OUTPUT4 GPIO_NUM_27
/* Tiempo de cada led prendido y apagado tambien entre cambio de estados */
#define LED_CHANGE_DELAY_MS 1000
/* El delay del debouncer, en este caso, como el boton tiene doble rebote (al principio y final)
decidi hacerlo de 150ms por un analisis que hice en pulseview con el logic analyzer
*/
#define DEBOUNCER_DELAY_uS 150000
/* Declaracion manejador de la cola */
QueueHandle_t handlerQueue;
/* Se declara la variable auxiliar de tiempo para el debouncer */
static int64_t past_time = 0;
/* Se declara el saldo del usuario, lo iniciamos en 0 */
static uint8_t balance = 0;
/* Inicializacion de los gpios, en este caso utilizamos los botones con pullups activado
para no hacer configuraciones extras, ya que despues de la funcion gpio_reset_pin()
los gpios quedan con pullups activos */
void init_gpio () {
gpio_reset_pin(INPUT1);
gpio_reset_pin(INPUT2);
gpio_reset_pin(INPUT3);
gpio_reset_pin(INPUT4);
gpio_reset_pin(OUTPUT1);
gpio_reset_pin(OUTPUT2);
gpio_reset_pin(OUTPUT3);
gpio_reset_pin(OUTPUT4);
gpio_set_direction(INPUT1, GPIO_MODE_INPUT);
gpio_set_direction(INPUT2, GPIO_MODE_INPUT);
gpio_set_direction(INPUT3, GPIO_MODE_INPUT);
gpio_set_direction(INPUT4, GPIO_MODE_INPUT);
gpio_set_direction(OUTPUT1, GPIO_MODE_OUTPUT);
gpio_set_direction(OUTPUT2, GPIO_MODE_OUTPUT);
gpio_set_direction(OUTPUT3, GPIO_MODE_OUTPUT);
gpio_set_direction(OUTPUT4, GPIO_MODE_OUTPUT);
}
/* Manejo de interrupcion, a este manejador le abjuntamos todos los pines de entrada,
de esta forma con un mismo manejador obtenemos el boton que la activo */
static void IRAM_ATTR gpio_interrupt_handler(void *args) {
int pinNumber = (int)args;
if (((current_time-past_time)>=DEBOUNCER_DELAY_uS)) {
past_time = current_time;
xQueueSendFromISR(handlerQueue, &pinNumber, NULL);
}
}
/* Esta tarea es la que se encarga de sumar cada entrada de los botones, y tambien en entregar el cambio
una vez que se alcanza o supera el saldo en 15*/
void LED_Control_Task(void *params) {
int pinNumber, count = 0;
while (1) {
/* Si hay algo en la cola, entonces entra, y lo que tiene en la cola (que debe de ser un numero de gpio)
se coloca en pinNumber, para despues, dependiendo del boton presionada, se le agrega al saldo un valor*/
if (xQueueReceive(handlerQueue, &pinNumber, portMAX_DELAY)) {
switch (pinNumber) {
case INPUT1:
balance += 1;
break;
case INPUT2:
balance += 5;
break;
case INPUT3:
balance += 10;
break;
case INPUT4:
balance += 20;
break;
}
/* Cada que se ingresa una moneda, se muestra en pantalla el saldo */
printf("Su saldo es: %d.\n", (int)balance);
/* Si el saldo es mayor o igual a 15, entonces se entrega cambio o ticket*/
if (balance >= 15) {
/* Se le resta 15 para saber el cambio*/
balance -= 15;
/* Aqui es simple, si se puede divir entre n entonces se regresa esa moneda y se le resta
al cambio, se imprime que moneda se esta regresando y se prende el led, espera un momento,
lo apaga y espera otro momento para que no se vea uno tras otro */
while (balance / 10 > 0) {
balance -= 10;
printf("Moneda de $10.\n");
gpio_set_level(OUTPUT3, 1);
vTaskDelay(pdMS_TO_TICKS(LED_CHANGE_DELAY_MS));
gpio_set_level(OUTPUT3, 0);
vTaskDelay(pdMS_TO_TICKS(LED_CHANGE_DELAY_MS));
}
while (balance / 5 > 0) {
balance -= 5;
printf("Moneda de $5.\n");
gpio_set_level(OUTPUT2, 1);
vTaskDelay(pdMS_TO_TICKS(LED_CHANGE_DELAY_MS));
gpio_set_level(OUTPUT2, 0);
vTaskDelay(pdMS_TO_TICKS(LED_CHANGE_DELAY_MS));
}
while (balance / 1 > 0) {
balance -= 1;
printf("Moneda de $1.\n");
gpio_set_level(OUTPUT1, 1);
vTaskDelay(pdMS_TO_TICKS(LED_CHANGE_DELAY_MS));
gpio_set_level(OUTPUT1, 0);
vTaskDelay(pdMS_TO_TICKS(LED_CHANGE_DELAY_MS));
}
/* Al final de entregar las monedas (puede ser que no haya entregado ninguna)
Siempre se entrega el ticket con el mismo sistema del LED*/
printf("Tome su recibo.\n");
gpio_set_level(OUTPUT4, 1);
vTaskDelay(pdMS_TO_TICKS(LED_CHANGE_DELAY_MS));
gpio_set_level(OUTPUT4, 0);
vTaskDelay(pdMS_TO_TICKS(LED_CHANGE_DELAY_MS));
}
}
}
}
/*
void LED_Control_Change_Task(void *params) {
if (balance >= 15) {
balance -= 15;
gpio_uninstall_isr_service();
while (balance / 10 > 0) {
balance -= 10;
printf("Moneda de $10.\n");
gpio_set_level(OUTPUT1, 1);
vTaskDelay(pdMS_TO_TICKS(500));
gpio_set_level(OUTPUT1, 0);
}
while (balance / 5 > 0) {
balance -= 5;
printf("Moneda de $5.\n");
gpio_set_level(OUTPUT2, 1);
vTaskDelay(pdMS_TO_TICKS(500));
gpio_set_level(OUTPUT2, 0);
}
while (balance / 1 > 0) {
balance -= 1;
printf("Moneda de $1.\n");
gpio_set_level(OUTPUT3, 1);
vTaskDelay(pdMS_TO_TICKS(500));
gpio_set_level(OUTPUT3, 0);
}
printf("Tome su recibo.\n");
gpio_set_level(OUTPUT4, 1);
vTaskDelay(pdMS_TO_TICKS(500));
gpio_set_level(OUTPUT4, 0);
gpio_install_isr_service(0);
}
}
*/
/* Aqui declaramos que tipo de interrupcion tendran los gpios de entrada, se le asigna a la variable
del manejado de cola una cola de 10 items de enteros, se crea la tarea que se utilizara, se activan interrupciones
de los gpios, y se les agrega estas interrupciones al mismo manejador*/
void app_main () {
init_gpio();
gpio_set_intr_type(INPUT1, GPIO_INTR_NEGEDGE);
gpio_set_intr_type(INPUT2, GPIO_INTR_NEGEDGE);
gpio_set_intr_type(INPUT3, GPIO_INTR_NEGEDGE);
gpio_set_intr_type(INPUT4, GPIO_INTR_NEGEDGE);
handlerQueue = xQueueCreate(10, sizeof(int));
xTaskCreate(LED_Control_Task, "LED_Control_Task", 2048, NULL, 1, NULL);
// xTaskCreate(LED_Control_Change_Task, "LED_Control_Change_Task", 2048, NULL, 1, NULL);
gpio_install_isr_service(0);
gpio_isr_handler_add(INPUT1, gpio_interrupt_handler, (void *) INPUT1);
gpio_isr_handler_add(INPUT2, gpio_interrupt_handler, (void *) INPUT2);
gpio_isr_handler_add(INPUT3, gpio_interrupt_handler, (void *) INPUT3);
gpio_isr_handler_add(INPUT4, gpio_interrupt_handler, (void *) INPUT4);
}