#include <string.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "esp_log.h"
#define ESP_INTR_FLAG_DEFAULT 0
// GPIO Pin Definitions
#define LED_PIN_1 GPIO_NUM_4
#define LED_PIN_2 GPIO_NUM_18
#define LED_PIN_3 GPIO_NUM_19
#define LED_PIN_4 GPIO_NUM_21
#define LED_PIN_5 GPIO_NUM_22
#define LED_PIN_6 GPIO_NUM_23
#define LED_PIN_7 GPIO_NUM_25
#define LED_PIN_8 GPIO_NUM_26
#define SWITCH_PIN GPIO_NUM_27
// UART Configuration
#define UART_NUM UART_NUM_0
#define BUF_SIZE (1024)
#define RD_BUF_SIZE (BUF_SIZE * 2)
#define TD_BUF_SIZE (BUF_SIZE * 2)
// Speed Configurations (in milliseconds)
#define VEL1_DELAY 2000
#define VEL2_DELAY 1500
#define VEL3_DELAY 1000
#define VEL4_DELAY 750
#define WINDOW_TO_CHANGE_SPEED 10000
#define EVENT_CHANGE_TO_LEFT ( 1 << 0 )
#define EVENT_CHANGE_TO_RIGHT ( 1 << 1 )
#define EVENT_SWITCH_CHANGE ( 1 << 2 )
#define EVENT_ANIMATION_END ( 1 << 3 )
#define EVENT_SPEED_CHANGE ( 1 << 4 )
/* Bits used to synchronize the simultaneous start of all tasks. */
#define STAR_LEDS_TASK_BIT ( 1 << 0 )
#define UART_SPEED_TASK_BIT ( 1 << 1 )
#define MONITOR_SWITCH_TASK_BIT ( 1 << 2 )
#define ALL_TASK_SYNC_BITS ( STAR_LEDS_TASK_BIT | UART_SPEED_TASK_BIT | MONITOR_SWITCH_TASK_BIT )
// Enum for the animation states
typedef enum {
LIGHTING_UP = 1,
TURNING_OFF = 2
} animation_state_t;
// Enum for the animation steps
typedef enum {
STAR_OFF = 1,
PHASE_TWO = 2,
PHASE_THREE = 3,
STAR_ON = 4
} animation_step_t;
// Global Variables
static EventGroupHandle_t animation_events = NULL;
static EventGroupHandle_t sync_tasks_start = NULL;
static uint16_t speed_delay_ms = VEL2_DELAY;
// Function Prototypes
void Star_LEDs_Task(void*);
void UART_Speed_Task(void*);
void Monitor_Switch_Task(void*);
void setup_gpio(void);
void setup_interrupts(void);
void setup_uart(void);
animation_step_t animate_star_lighting_up(animation_step_t); // state machine
animation_step_t animate_star_turning_off(animation_step_t); // state machine
void update_leds(animation_step_t);
void print_speed(uint16_t);
// Switch Interrupt Handler
static void IRAM_ATTR switch_interrupt_handler(void* args) {
xEventGroupSetBitsFromISR(animation_events, EVENT_SWITCH_CHANGE, pdFALSE);
}
void app_main(void) {
// Initialize hardware
setup_gpio();
setup_interrupts();
setup_uart();
// Create de event group
animation_events = xEventGroupCreate();
// Create tasks
sync_tasks_start = xEventGroupCreate();
xTaskCreate(Star_LEDs_Task, "Star_LEDs_Task", 1024, NULL, 1, NULL);
xTaskCreate(UART_Speed_Task, "UART_Speed_Task", 1024, NULL, 1, NULL);
xTaskCreate(Monitor_Switch_Task, "Monitor_Switch_Task", 1024, NULL, 1, NULL);
}
// LED Animation Task
void Star_LEDs_Task(void *pvParameters)
{
animation_state_t state = TURNING_OFF;
animation_step_t step = STAR_OFF;
xEventGroupSync( sync_tasks_start, STAR_LEDS_TASK_BIT, ALL_TASK_SYNC_BITS, portMAX_DELAY );
while (true) {
if (state != LIGHTING_UP) {
state = LIGHTING_UP;
step = animate_star_lighting_up(step);
}
else {
state = TURNING_OFF;
step = animate_star_turning_off(step);
}
}
vTaskDelete(NULL);
}
// UART read input user speed
void UART_Speed_Task(void *pvParameters)
{
const char *msg_speed = "\nCambiar velocidad: ";
const char *msg_err = "\nNo se pudo cambiar la velocidad!";
uint8_t *data = (uint8_t *) malloc(BUF_SIZE);
int len = 0;
EventBits_t event_bits;
xEventGroupSync( sync_tasks_start, UART_SPEED_TASK_BIT, ALL_TASK_SYNC_BITS, portMAX_DELAY );
while (true)
{
event_bits = xEventGroupWaitBits( animation_events,
EVENT_ANIMATION_END,
pdFALSE,
pdFALSE,
100 / portTICK_PERIOD_MS);
if (event_bits & EVENT_ANIMATION_END)
{
uart_write_bytes(UART_NUM, msg_speed, strlen(msg_speed));
do {
len = uart_read_bytes(UART_NUM, data, (BUF_SIZE - 1), 100 / portTICK_PERIOD_MS);
if (len > 0) {
data[len-1] = '\0';
if (strcmp("VEL1", (const char *) data) == 0)
speed_delay_ms = VEL1_DELAY;
else if (strcmp("VEL2", (const char *) data) == 0)
speed_delay_ms = VEL2_DELAY;
else if (strcmp("VEL3", (const char *) data) == 0)
speed_delay_ms = VEL3_DELAY;
else if (strcmp("VEL4", (const char *) data) == 0)
speed_delay_ms = VEL4_DELAY;
else {
uart_write_bytes(UART_NUM, msg_err, strlen(msg_err));
continue;
}
xEventGroupSetBits(animation_events, EVENT_SPEED_CHANGE);
}
} while (xEventGroupGetBits(animation_events) & EVENT_ANIMATION_END);
print_speed(speed_delay_ms);
}
}
free(data);
vTaskDelete(NULL);
}
void Monitor_Switch_Task(void *pvParmeters)
{
const TickType_t xBounceTime = 50 / portTICK_PERIOD_MS;
xEventGroupSync( sync_tasks_start, MONITOR_SWITCH_TASK_BIT, ALL_TASK_SYNC_BITS, portMAX_DELAY );
while (true)
{
xEventGroupClearBits(animation_events, EVENT_SWITCH_CHANGE);
xEventGroupWaitBits(animation_events,
EVENT_SWITCH_CHANGE,
pdFALSE,
pdFALSE,
portMAX_DELAY);
vTaskDelay(xBounceTime);
if (gpio_get_level(SWITCH_PIN)) {
xEventGroupSetBits(animation_events, EVENT_CHANGE_TO_LEFT);
//uart_write_bytes(UART_NUM, (const char *) "LEFT", 4);
}
else {
xEventGroupSetBits(animation_events, EVENT_CHANGE_TO_RIGHT);
//uart_write_bytes(UART_NUM, (const char *) "RIGHT", 5);
}
}
vTaskDelete(NULL);
}
animation_step_t animate_star_lighting_up(animation_step_t step)
{
EventBits_t event_bits;
TickType_t xStepDelay = speed_delay_ms / portTICK_PERIOD_MS;
while (step != STAR_ON)
{
switch (step) {
case STAR_OFF:
update_leds(step = PHASE_TWO);
break;
case PHASE_TWO:
update_leds(step = PHASE_THREE);
break;
case PHASE_THREE:
update_leds(step = STAR_ON);
xStepDelay = portMAX_DELAY;
break;
default:
}
xEventGroupClearBits(animation_events, EVENT_CHANGE_TO_RIGHT);
event_bits = xEventGroupWaitBits( animation_events,
EVENT_CHANGE_TO_RIGHT,
pdFALSE,
pdFALSE,
xStepDelay);
if (event_bits & EVENT_CHANGE_TO_RIGHT)
return step;
}
return step;
}
animation_step_t animate_star_turning_off(animation_step_t step)
{
EventBits_t event_bits;
EventBits_t bits_to_wait = EVENT_CHANGE_TO_LEFT;
TickType_t xStepDelay = speed_delay_ms / portTICK_PERIOD_MS;
while (step != STAR_OFF)
{
switch (step) {
case STAR_ON:
update_leds(step = PHASE_THREE);
break;
case PHASE_THREE:
update_leds(step = PHASE_TWO);
break;
case PHASE_TWO:
update_leds(step = STAR_OFF);
xStepDelay = WINDOW_TO_CHANGE_SPEED / portTICK_PERIOD_MS;
bits_to_wait = EVENT_SPEED_CHANGE;
xEventGroupSetBits(animation_events, EVENT_ANIMATION_END);
break;
default:
break;
}
xEventGroupClearBits(animation_events, EVENT_CHANGE_TO_LEFT);
event_bits = xEventGroupWaitBits( animation_events,
bits_to_wait,
pdTRUE, // clear on exit
pdFALSE,
xStepDelay);
if (event_bits & EVENT_ANIMATION_END)
xEventGroupClearBits(animation_events, EVENT_ANIMATION_END);
if (event_bits & EVENT_CHANGE_TO_LEFT)
return step;
}
return step;
}
void update_leds(animation_step_t step)
{
const uint8_t LEDs[8] = {LED_PIN_1, LED_PIN_2, LED_PIN_3, LED_PIN_4,
LED_PIN_5, LED_PIN_6, LED_PIN_7, LED_PIN_8};
switch (step) {
case STAR_OFF:
for (uint8_t i=0; i<8; i++)
gpio_set_level(LEDs[i], 0);
break;
case PHASE_TWO:
for (uint8_t i=0; i<2; i++)
gpio_set_level(LEDs[i], 1);
for (uint8_t i=2; i<8; i++)
gpio_set_level(LEDs[i], 0);
break;
case PHASE_THREE:
for (uint8_t i=0; i<4; i++)
gpio_set_level(LEDs[i], 1);
for (uint8_t i=4; i<8; i++)
gpio_set_level(LEDs[i], 0);
break;
case STAR_ON:
for (uint8_t i=0; i<8; i++)
gpio_set_level(LEDs[i], 1);
break;
default:
break;
}
}
void print_speed(uint16_t speed)
{
uart_write_bytes(UART_NUM, (const char *) "\n", 1);
switch (speed) {
case VEL1_DELAY: uart_write_bytes(UART_NUM, (const char *) "Velocidad: VEL1", 15); break;
case VEL2_DELAY: uart_write_bytes(UART_NUM, (const char *) "Velocidad: VEL2", 15); break;
case VEL3_DELAY: uart_write_bytes(UART_NUM, (const char *) "Velocidad: VEL3", 15); break;
case VEL4_DELAY: uart_write_bytes(UART_NUM, (const char *) "Velocidad: VEL4", 15); break;
}
}
// Configure GPIO Pins
void setup_gpio(void) {
gpio_config_t io_conf;
// LED Pins Configuration
io_conf.pin_bit_mask =
(1ULL << LED_PIN_1) | (1ULL << LED_PIN_2) | (1ULL << LED_PIN_3) |
(1ULL << LED_PIN_4) | (1ULL << LED_PIN_5) | (1ULL << LED_PIN_6) |
(1ULL << LED_PIN_7) | (1ULL << LED_PIN_8);
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_conf.intr_type = GPIO_INTR_DISABLE;
gpio_config(&io_conf);
// Switch Pin Configuration
io_conf.pin_bit_mask = (1ULL << SWITCH_PIN);
io_conf.mode = GPIO_MODE_INPUT;
io_conf.intr_type = GPIO_INTR_ANYEDGE;
gpio_config(&io_conf);
}
// Configure GPIO Interrupts
void setup_interrupts(void) {
// Instalar servicio de interrupciones y asociar handlers
gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
gpio_isr_handler_add(SWITCH_PIN, switch_interrupt_handler, NULL);
}
// Configure UART
void setup_uart(void) {
const uint8_t uart_num = UART_NUM;
uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
// Configure UART parameters
uart_param_config(uart_num, &uart_config);
// Set UART pins
uart_set_pin(uart_num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
// Install UART drivers
uart_driver_install(uart_num, RD_BUF_SIZE, TD_BUF_SIZE, 0, NULL, 0);
}