// GPIO 2 -----> LED1 ----> GND
// GPIO 5 -----> LED2 ----> GND
// GPIO 0 -----> Button1 ----> GND
// GPIO 4 -----> Button2 ----> GND
// UART0 -------> Serial Monitor (Simulated)
//Version 1
// // main/app_main.c
// #include <string.h>
// #include <stdio.h>
// #include "freertos/FreeRTOS.h"
// #include "freertos/task.h"
// #include "freertos/timers.h"
// #include "driver/gpio.h"
// #include "driver/ledc.h"
// #include "driver/uart.h"
// #include "cJSON.h"
// #include "esp_log.h"
// #define LED1_GPIO GPIO_NUM_2
// #define LED2_GPIO GPIO_NUM_5
// #define BTN1_GPIO GPIO_NUM_0
// #define BTN2_GPIO GPIO_NUM_4
// static const char *TAG = "BLINK_PWM";
// typedef struct {
// bool running;
// int last_freq_hz;
// ledc_timer_t timer;
// ledc_channel_t ch;
// gpio_num_t gpio;
// TimerHandle_t tmr;
// const char *name; // "LED1"/"LED2"
// } led_state_t;
// static led_state_t L1 = {
// .running=false,.last_freq_hz=10,
// .timer=LEDC_TIMER_0,.ch=LEDC_CHANNEL_0,
// .gpio=LED1_GPIO,.name="LED1"
// };
// static led_state_t L2 = {
// .running=false,.last_freq_hz=7,
// .timer=LEDC_TIMER_1,.ch=LEDC_CHANNEL_1,
// .gpio=LED2_GPIO,.name="LED2"
// };
// static void led_start(led_state_t *s) {
// ledc_set_freq(LEDC_HIGH_SPEED_MODE, s->timer, s->last_freq_hz);
// ledc_set_duty(LEDC_HIGH_SPEED_MODE, s->ch, 512);
// ledc_update_duty(LEDC_HIGH_SPEED_MODE, s->ch);
// s->running = true;
// ESP_LOGI(TAG, "%s START @ %d Hz", s->name, s->last_freq_hz);
// xTimerStop(s->tmr,0);
// xTimerChangePeriod(s->tmr, pdMS_TO_TICKS(10000), 0); // 10s
// xTimerStart(s->tmr,0);
// }
// static void led_stop(led_state_t *s) {
// ledc_stop(LEDC_HIGH_SPEED_MODE, s->ch, 0);
// s->running = false;
// ESP_LOGI(TAG, "%s STOP", s->name);
// xTimerStop(s->tmr,0);
// }
// static void led_toggle(led_state_t *s) { s->running ? led_stop(s) : led_start(s); }
// static void tmr_cb(TimerHandle_t xTimer){
// led_state_t *s = (led_state_t*) pvTimerGetTimerID(xTimer);
// if (s->running) led_stop(s); // auto-stop after 10s
// }
// /* ---------- Init ---------- */
// static void init_pwm(void){
// ledc_timer_config_t t = {
// .speed_mode = LEDC_HIGH_SPEED_MODE,
// .duty_resolution = LEDC_TIMER_10_BIT,
// .freq_hz = 1000,
// .clk_cfg = LEDC_AUTO_CLK
// };
// t.timer_num = LEDC_TIMER_0; ledc_timer_config(&t);
// t.timer_num = LEDC_TIMER_1; ledc_timer_config(&t);
// ledc_channel_config_t c = {
// .speed_mode = LEDC_HIGH_SPEED_MODE,
// .duty = 0, .hpoint = 0
// };
// c.channel = L1.ch; c.gpio_num = L1.gpio; c.timer_sel = L1.timer; ledc_channel_config(&c);
// c.channel = L2.ch; c.gpio_num = L2.gpio; c.timer_sel = L2.timer; ledc_channel_config(&c);
// }
// static void init_uart(void){
// const uart_config_t cfg = {
// .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
// };
// uart_driver_install(UART_NUM_0, 2048, 0, 0, NULL, 0);
// uart_param_config(UART_NUM_0, &cfg);
// }
// static void init_buttons(void){
// gpio_config_t io = {
// .pin_bit_mask = (1ULL<<BTN1_GPIO) | (1ULL<<BTN2_GPIO),
// .mode = GPIO_MODE_INPUT,
// .pull_up_en = GPIO_PULLUP_ENABLE,
// .pull_down_en = GPIO_PULLDOWN_DISABLE,
// .intr_type = GPIO_INTR_DISABLE
// };
// gpio_config(&io);
// }
// /* ---------- Tasks ---------- */
// static bool debounced_falling(gpio_num_t pin, uint32_t period_ms){
// // called periodically at 'period_ms' interval (here: 10ms)
// static int prev0=1, prev4=1; // for buttons 0 and 4
// static uint32_t st0=0, st4=0;
// int *prev = (pin==BTN1_GPIO)? &prev0 : &prev4;
// uint32_t *st = (pin==BTN1_GPIO)? &st0 : &st4;
// int lvl = gpio_get_level(pin);
// if (lvl != *prev){ *prev=lvl; *st=0; }
// else if (*st < 20) *st += period_ms;
// if (*prev==0 && *st>=20){ *st=0; return true; }
// return false;
// }
// static void btn_task(void *arg){
// const TickType_t dt = pdMS_TO_TICKS(10);
// while (1){
// if (debounced_falling(BTN1_GPIO, 10)) led_toggle(&L1);
// if (debounced_falling(BTN2_GPIO, 10)) led_toggle(&L2);
// vTaskDelay(dt);
// }
// }
// static void cmd_task(void *arg){
// uint8_t buf[256];
// const char *help = "Send JSON: {\"MOD\":\"LED1\",\"COM\":\"START\",\"FREQ\":\"10\"}\r\n";
// uart_write_bytes(UART_NUM_0, help, strlen(help));
// while (1){
// int n = uart_read_bytes(UART_NUM_0, buf, sizeof(buf)-1, pdMS_TO_TICKS(50));
// if (n<=0) continue;
// buf[n]=0;
// cJSON *root = cJSON_Parse((char*)buf);
// if (!root) { uart_write_bytes(UART_NUM_0, "Bad JSON\r\n",10); continue; }
// const cJSON *mod = cJSON_GetObjectItem(root,"MOD");
// const cJSON *com = cJSON_GetObjectItem(root,"COM");
// const cJSON *freq = cJSON_GetObjectItem(root,"FREQ");
// led_state_t *S = NULL;
// if (mod && strcmp(mod->valuestring,"LED1")==0) S=&L1;
// else if (mod && strcmp(mod->valuestring,"LED2")==0) S=&L2;
// if (S && com){
// if (strcmp(com->valuestring,"START")==0){
// if (freq && freq->valuestring) S->last_freq_hz = atoi(freq->valuestring);
// led_start(S);
// } else if (strcmp(com->valuestring,"STOP")==0){
// led_stop(S);
// }
// }
// cJSON_Delete(root);
// }
// }
// /* ---------- app_main ---------- */
// void app_main(void){
// init_pwm();
// init_uart();
// init_buttons();
// L1.tmr = xTimerCreate("t1", pdMS_TO_TICKS(10000), pdFALSE, &L1, tmr_cb);
// L2.tmr = xTimerCreate("t2", pdMS_TO_TICKS(10000), pdFALSE, &L2, tmr_cb);
// xTaskCreate(btn_task, "btn", 2048, NULL, 5, NULL);
// xTaskCreate(cmd_task, "cmd", 4096, NULL, 6, NULL);
// }
// version 2 by gemini
// this running fine
// #include <stdio.h>
// #include <string.h>
// #include "freertos/FreeRTOS.h"
// #include "freertos/task.h"
// #include "freertos/queue.h"
// #include "driver/gpio.h"
// #include "driver/ledc.h"
// #include "driver/uart.h"
// #include "esp_log.h"
// #include "cJSON.h"
// #include "esp_timer.h"
// // Hardware connection definitions
// // GPIO 2 -----> LED1 ----> GND
// // GPIO 5 -----> LED2 ----> GND
// // GPIO 0 -----> Button1 ----> GND
// // GPIO 4 -----> Button2 ----> GND
// // UART0 -------> Serial Monitor (Simulated)
// // --- Define GPIOs for LEDs and Buttons ---
// #define LED1_GPIO_PIN GPIO_NUM_2
// #define LED2_GPIO_PIN GPIO_NUM_5
// #define BTN1_GPIO_PIN GPIO_NUM_0
// #define BTN2_GPIO_PIN GPIO_NUM_4
// // --- UART and JSON configuration ---
// #define UART_PORT UART_NUM_0
// #define UART_BAUD_RATE 115200
// #define BUF_SIZE (1024)
// // --- PWM configuration ---
// #define LEDC_TIMER LEDC_TIMER_0
// #define LEDC_MODE LEDC_LOW_SPEED_MODE
// #define LEDC_DUTY_RESOLUTION LEDC_TIMER_13_BIT // 8192 levels
// #define LEDC_DUTY 4096 // 50% duty cycle
// // --- Debounce time for buttons ---
// #define DEBOUNCE_TIME_MS 20
// // --- Auto-stop time in milliseconds ---
// #define AUTO_STOP_TIME_MS 10000
// // --- Structure for an LED command ---
// typedef enum {
// CMD_START,
// CMD_STOP,
// CMD_TOGGLE
// } command_t;
// typedef struct {
// command_t command;
// int frequency; // Used only for CMD_START
// } led_command_t;
// // --- LED State Structure ---
// typedef struct {
// gpio_num_t gpio_pin;
// ledc_channel_t ledc_channel;
// ledc_timer_t ledc_timer;
// int last_freq;
// bool is_on;
// esp_timer_handle_t auto_stop_timer;
// QueueHandle_t command_queue;
// } led_state_t;
// static const char *TAG = "MAIN";
// static led_state_t led1_state;
// static led_state_t led2_state;
// // --- Function to initialize the LEDC PWM for a specific LED ---
// static void pwm_init(led_state_t *led_state) {
// ledc_timer_config_t ledc_timer = {
// .speed_mode = LEDC_MODE,
// .duty_resolution = LEDC_DUTY_RESOLUTION,
// .timer_num = led_state->ledc_timer,
// .freq_hz = 100, // Initial frequency, will be updated
// .clk_cfg = LEDC_AUTO_CLK,
// };
// ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
// ledc_channel_config_t ledc_channel = {
// .gpio_num = led_state->gpio_pin,
// .speed_mode = LEDC_MODE,
// .channel = led_state->ledc_channel,
// .timer_sel = led_state->ledc_timer,
// .duty = 0, // Initially off
// .hpoint = 0
// };
// ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
// }
// // --- Callback for the auto-stop timer ---
// static void IRAM_ATTR auto_stop_timer_cb(void* arg) {
// led_state_t* led_state = (led_state_t*) arg;
// ESP_LOGI(TAG, "Auto-stop timer expired for LED on GPIO %d", led_state->gpio_pin);
// led_command_t cmd = { .command = CMD_STOP };
// xQueueSendFromISR(led_state->command_queue, &cmd, NULL);
// }
// // --- Task to control an LED's PWM based on commands from its queue ---
// static void led_task(void *pvParameter) {
// led_state_t *led_state = (led_state_t *)pvParameter;
// led_command_t cmd;
// // Create the auto-stop timer for this LED
// const esp_timer_create_args_t timer_args = {
// .callback = &auto_stop_timer_cb,
// .arg = led_state,
// .name = "auto-stop-timer"
// };
// ESP_ERROR_CHECK(esp_timer_create(&timer_args, &led_state->auto_stop_timer));
// while (1) {
// if (xQueueReceive(led_state->command_queue, &cmd, portMAX_DELAY) == pdPASS) {
// ESP_LOGI(TAG, "Received command for LED on GPIO %d", led_state->gpio_pin);
// // Cancel any running auto-stop timer before processing a new command
// if (esp_timer_is_active(led_state->auto_stop_timer)) {
// esp_timer_stop(led_state->auto_stop_timer);
// }
// if (cmd.command == CMD_START) {
// if (!led_state->is_on) {
// // Start the LED with the new or last saved frequency
// int new_freq = (cmd.frequency > 0) ? cmd.frequency : led_state->last_freq;
// led_state->last_freq = new_freq;
// ESP_LOGI(TAG, "Starting LED on GPIO %d at %dHz", led_state->gpio_pin, new_freq);
// ledc_set_freq(LEDC_MODE, led_state->ledc_timer, new_freq);
// ledc_set_duty(LEDC_MODE, led_state->ledc_channel, LEDC_DUTY);
// ledc_update_duty(LEDC_MODE, led_state->ledc_channel);
// led_state->is_on = true;
// // Start the 10-second auto-stop timer
// esp_timer_start_once(led_state->auto_stop_timer, AUTO_STOP_TIME_MS * 1000);
// } else {
// ESP_LOGI(TAG, "LED on GPIO %d is already running.", led_state->gpio_pin);
// // If a new command is received while running, restart the timer
// esp_timer_start_once(led_state->auto_stop_timer, AUTO_STOP_TIME_MS * 1000);
// }
// } else if (cmd.command == CMD_STOP) {
// if (led_state->is_on) {
// // Stop the LED
// ESP_LOGI(TAG, "Stopping LED on GPIO %d", led_state->gpio_pin);
// ledc_set_duty(LEDC_MODE, led_state->ledc_channel, 0);
// ledc_update_duty(LEDC_MODE, led_state->ledc_channel);
// led_state->is_on = false;
// }
// } else if (cmd.command == CMD_TOGGLE) {
// // This command is primarily for the buttons
// if (led_state->is_on) {
// led_command_t stop_cmd = { .command = CMD_STOP };
// xQueueSend(led_state->command_queue, &stop_cmd, 0);
// } else {
// led_command_t start_cmd = { .command = CMD_START, .frequency = led_state->last_freq };
// xQueueSend(led_state->command_queue, &start_cmd, 0);
// }
// }
// }
// }
// }
// // --- Task to monitor button presses with debouncing ---
// static void button_task(void *pvParameter) {
// gpio_num_t button_pin = (gpio_num_t)pvParameter;
// led_state_t *led_state = NULL;
// if (button_pin == BTN1_GPIO_PIN) {
// led_state = &led1_state;
// } else if (button_pin == BTN2_GPIO_PIN) {
// led_state = &led2_state;
// }
// if (led_state == NULL) {
// vTaskDelete(NULL); // Should not happen
// }
// // Configure GPIO for button input with pull-up resistor
// gpio_config_t io_conf;
// io_conf.intr_type = GPIO_INTR_DISABLE;
// io_conf.pin_bit_mask = (1ULL << button_pin);
// io_conf.mode = GPIO_MODE_INPUT;
// io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
// io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
// gpio_config(&io_conf);
// bool last_state = gpio_get_level(button_pin);
// TickType_t last_press_time = 0;
// while (1) {
// bool current_state = gpio_get_level(button_pin);
// TickType_t current_time = xTaskGetTickCount();
// // Check for state change and debounce
// if (current_state != last_state) {
// if (current_time - last_press_time > pdMS_TO_TICKS(DEBOUNCE_TIME_MS)) {
// if (current_state == 0) { // Button pressed (active low)
// ESP_LOGI(TAG, "Button on GPIO %d pressed", button_pin);
// led_command_t cmd = { .command = CMD_TOGGLE };
// xQueueSend(led_state->command_queue, &cmd, 0);
// }
// }
// last_press_time = current_time;
// }
// last_state = current_state;
// vTaskDelay(pdMS_TO_TICKS(10)); // Poll every 10ms
// }
// }
// // --- Task to read and parse JSON commands from UART ---
// static void uart_task(void *pvParameter) {
// uint8_t *data = (uint8_t *)malloc(BUF_SIZE);
// if (data == NULL) {
// ESP_LOGE(TAG, "Failed to allocate UART buffer.");
// vTaskDelete(NULL);
// }
// // Configure UART parameters
// const uart_config_t uart_config = {
// .baud_rate = UART_BAUD_RATE,
// .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_APB,
// };
// ESP_ERROR_CHECK(uart_param_config(UART_PORT, &uart_config));
// ESP_ERROR_CHECK(uart_driver_install(UART_PORT, BUF_SIZE * 2, 0, 0, NULL, 0));
// // --- Main UART loop ---
// while (1) {
// int len = uart_read_bytes(UART_PORT, data, (BUF_SIZE - 1), 20 / portTICK_PERIOD_MS);
// if (len > 0) {
// data[len] = '\0'; // Null-terminate the string
// ESP_LOGI(TAG, "Received UART data: %s", (char *)data);
// cJSON *root = cJSON_Parse((char *)data);
// if (root == NULL) {
// const char *error_ptr = cJSON_GetErrorPtr();
// if (error_ptr != NULL) {
// ESP_LOGE(TAG, "cJSON parse error before: %s", error_ptr);
// }
// continue;
// }
// cJSON *mod = cJSON_GetObjectItemCaseSensitive(root, "MOD");
// cJSON *com = cJSON_GetObjectItemCaseSensitive(root, "COM");
// cJSON *freq = cJSON_GetObjectItemCaseSensitive(root, "FREQ");
// if (cJSON_IsString(mod) && cJSON_IsString(com)) {
// led_command_t cmd;
// QueueHandle_t target_queue = NULL;
// if (strcmp(mod->valuestring, "LED1") == 0) {
// target_queue = led1_state.command_queue;
// } else if (strcmp(mod->valuestring, "LED2") == 0) {
// target_queue = led2_state.command_queue;
// }
// if (target_queue != NULL) {
// if (strcmp(com->valuestring, "START") == 0) {
// cmd.command = CMD_START;
// if (cJSON_IsString(freq) || cJSON_IsNumber(freq)) {
// cmd.frequency = atoi(freq->valuestring);
// } else {
// cmd.frequency = 0; // Use last saved frequency
// }
// xQueueSend(target_queue, &cmd, 0);
// } else if (strcmp(com->valuestring, "STOP") == 0) {
// cmd.command = CMD_STOP;
// xQueueSend(target_queue, &cmd, 0);
// } else {
// ESP_LOGW(TAG, "Invalid command received.");
// }
// } else {
// ESP_LOGW(TAG, "Invalid module received.");
// }
// }
// cJSON_Delete(root);
// }
// }
// free(data);
// vTaskDelete(NULL);
// }
// // --- Main application entry point ---
// void app_main(void) {
// ESP_LOGI(TAG, "Initializing system...");
// // Initialize LED state variables
// led1_state = (led_state_t){
// .gpio_pin = LED1_GPIO_PIN,
// .ledc_channel = LEDC_CHANNEL_0,
// .ledc_timer = LEDC_TIMER,
// .last_freq = 10, // Default frequency
// .is_on = false,
// .command_queue = xQueueCreate(10, sizeof(led_command_t))
// };
// led2_state = (led_state_t){
// .gpio_pin = LED2_GPIO_PIN,
// .ledc_channel = LEDC_CHANNEL_1,
// .ledc_timer = LEDC_TIMER,
// .last_freq = 20, // Default frequency
// .is_on = false,
// .command_queue = xQueueCreate(10, sizeof(led_command_t))
// };
// if (!led1_state.command_queue || !led2_state.command_queue) {
// ESP_LOGE(TAG, "Failed to create command queues.");
// return;
// }
// // Initialize PWM for both LEDs
// pwm_init(&led1_state);
// pwm_init(&led2_state);
// // Create FreeRTOS tasks
// xTaskCreate(led_task, "led1_task", 4096, (void *)&led1_state, 5, NULL);
// xTaskCreate(led_task, "led2_task", 4096, (void *)&led2_state, 5, NULL);
// xTaskCreate(button_task, "btn1_task", 2048, (void *)BTN1_GPIO_PIN, 5, NULL);
// xTaskCreate(button_task, "btn2_task", 2048, (void *)BTN2_GPIO_PIN, 5, NULL);
// xTaskCreate(uart_task, "uart_task", 4096, NULL, 6, NULL);
// }
//version 3 updated version of version 2
// #include <stdio.h>
// #include <string.h>
// #include "freertos/FreeRTOS.h"
// #include "freertos/task.h"
// #include "freertos/queue.h"
// #include "driver/gpio.h"
// #include "driver/ledc.h"
// #include "driver/uart.h"
// #include "esp_log.h"
// #include "cJSON.h"
// #include "esp_timer.h"
// // Hardware connection definitions
// // GPIO 2 -----> LED1 ----> GND
// // GPIO 5 -----> LED2 ----> GND
// // GPIO 0 -----> Button1 ----> GND
// // GPIO 4 -----> Button2 ----> GND
// // UART0 -------> Serial Monitor (Simulated)
// // --- Define GPIOs for LEDs and Buttons ---
// #define LED1_GPIO_PIN GPIO_NUM_2
// #define LED2_GPIO_PIN GPIO_NUM_5
// #define BTN1_GPIO_PIN GPIO_NUM_0
// #define BTN2_GPIO_PIN GPIO_NUM_4
// // --- UART and JSON configuration ---
// #define UART_PORT UART_NUM_0
// #define UART_BAUD_RATE 115200
// #define BUF_SIZE (1024)
// // --- PWM configuration ---
// #define LEDC_TIMER LEDC_TIMER_0
// #define LEDC_MODE LEDC_LOW_SPEED_MODE
// #define LEDC_DUTY_RESOLUTION LEDC_TIMER_13_BIT // 8192 levels
// #define LEDC_DUTY 4096 // 50% duty cycle
// #define MAX_FREQUENCY 1000 // New define to cap max frequency
// // --- Debounce time for buttons ---
// #define DEBOUNCE_TIME_MS 20
// // --- Auto-stop time in milliseconds ---
// #define AUTO_STOP_TIME_MS 10000
// // --- Structure for an LED command ---
// typedef enum {
// CMD_START,
// CMD_STOP,
// CMD_TOGGLE
// } command_t;
// typedef struct {
// command_t command;
// int frequency; // Used only for CMD_START
// } led_command_t;
// // --- LED State Structure ---
// typedef struct {
// gpio_num_t gpio_pin;
// ledc_channel_t ledc_channel;
// ledc_timer_t ledc_timer;
// int last_freq;
// bool is_on;
// esp_timer_handle_t auto_stop_timer;
// QueueHandle_t command_queue;
// } led_state_t;
// static const char *TAG = "MAIN";
// static led_state_t led1_state;
// static led_state_t led2_state;
// // --- Function to initialize the LEDC PWM for a specific LED ---
// static void pwm_init(led_state_t *led_state) {
// ledc_timer_config_t ledc_timer = {
// .speed_mode = LEDC_MODE,
// .duty_resolution = LEDC_DUTY_RESOLUTION,
// .timer_num = led_state->ledc_timer,
// .freq_hz = 100, // Initial frequency, will be updated
// .clk_cfg = LEDC_AUTO_CLK,
// };
// ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
// ledc_channel_config_t ledc_channel = {
// .gpio_num = led_state->gpio_pin,
// .speed_mode = LEDC_MODE,
// .channel = led_state->ledc_channel,
// .timer_sel = led_state->ledc_timer,
// .duty = 0, // Initially off
// .hpoint = 0
// };
// ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
// }
// // --- Callback for the auto-stop timer ---
// static void IRAM_ATTR auto_stop_timer_cb(void* arg) {
// led_state_t* led_state = (led_state_t*) arg;
// ESP_LOGI(TAG, "Auto-stop timer expired for LED on GPIO %d", led_state->gpio_pin);
// led_command_t cmd = { .command = CMD_STOP };
// xQueueSendFromISR(led_state->command_queue, &cmd, NULL);
// }
// // --- Task to control an LED's PWM based on commands from its queue ---
// static void led_task(void *pvParameter) {
// led_state_t *led_state = (led_state_t *)pvParameter;
// led_command_t cmd;
// // Create the auto-stop timer for this LED
// const esp_timer_create_args_t timer_args = {
// .callback = &auto_stop_timer_cb,
// .arg = led_state,
// .name = "auto-stop-timer"
// };
// ESP_ERROR_CHECK(esp_timer_create(&timer_args, &led_state->auto_stop_timer));
// while (1) {
// if (xQueueReceive(led_state->command_queue, &cmd, portMAX_DELAY) == pdPASS) {
// ESP_LOGI(TAG, "Received command for LED on GPIO %d", led_state->gpio_pin);
// // Cancel any running auto-stop timer before processing a new command
// if (esp_timer_is_active(led_state->auto_stop_timer)) {
// esp_timer_stop(led_state->auto_stop_timer);
// }
// if (cmd.command == CMD_START) {
// if (!led_state->is_on) {
// // Start the LED with the new or last saved frequency
// int new_freq = (cmd.frequency > 0) ? cmd.frequency : led_state->last_freq;
// // Cap the frequency to prevent errors
// if (new_freq > MAX_FREQUENCY) {
// ESP_LOGW(TAG, "Requested frequency %d is too high, capping at %dHz", new_freq, MAX_FREQUENCY);
// new_freq = MAX_FREQUENCY;
// }
// led_state->last_freq = new_freq;
// ESP_LOGI(TAG, "Starting LED on GPIO %d at %dHz", led_state->gpio_pin, new_freq);
// ledc_set_freq(LEDC_MODE, led_state->ledc_timer, new_freq);
// ledc_set_duty(LEDC_MODE, led_state->ledc_channel, LEDC_DUTY);
// ledc_update_duty(LEDC_MODE, led_state->ledc_channel);
// led_state->is_on = true;
// // Start the 10-second auto-stop timer
// esp_timer_start_once(led_state->auto_stop_timer, AUTO_STOP_TIME_MS * 1000);
// } else {
// ESP_LOGI(TAG, "LED on GPIO %d is already running.", led_state->gpio_pin);
// // If a new command is received while running, restart the timer
// esp_timer_start_once(led_state->auto_stop_timer, AUTO_STOP_TIME_MS * 1000);
// }
// } else if (cmd.command == CMD_STOP) {
// if (led_state->is_on) {
// // Stop the LED
// ESP_LOGI(TAG, "Stopping LED on GPIO %d", led_state->gpio_pin);
// ledc_set_duty(LEDC_MODE, led_state->ledc_channel, 0);
// ledc_update_duty(LEDC_MODE, led_state->ledc_channel);
// led_state->is_on = false;
// }
// } else if (cmd.command == CMD_TOGGLE) {
// // This command is primarily for the buttons
// if (led_state->is_on) {
// led_command_t stop_cmd = { .command = CMD_STOP };
// xQueueSend(led_state->command_queue, &stop_cmd, 0);
// } else {
// led_command_t start_cmd = { .command = CMD_START, .frequency = led_state->last_freq };
// xQueueSend(led_state->command_queue, &start_cmd, 0);
// }
// }
// }
// }
// }
// // --- Task to monitor button presses with debouncing ---
// static void button_task(void *pvParameter) {
// gpio_num_t button_pin = (gpio_num_t)pvParameter;
// led_state_t *led_state = NULL;
// if (button_pin == BTN1_GPIO_PIN) {
// led_state = &led1_state;
// } else if (button_pin == BTN2_GPIO_PIN) {
// led_state = &led2_state;
// }
// if (led_state == NULL) {
// vTaskDelete(NULL); // Should not happen
// }
// // Configure GPIO for button input with pull-up resistor
// gpio_config_t io_conf;
// io_conf.intr_type = GPIO_INTR_DISABLE;
// io_conf.pin_bit_mask = (1ULL << button_pin);
// io_conf.mode = GPIO_MODE_INPUT;
// io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
// io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
// gpio_config(&io_conf);
// bool last_state = gpio_get_level(button_pin);
// TickType_t last_press_time = 0;
// while (1) {
// bool current_state = gpio_get_level(button_pin);
// TickType_t current_time = xTaskGetTickCount();
// // Check for state change and debounce
// if (current_state != last_state) {
// if (current_time - last_press_time > pdMS_TO_TICKS(DEBOUNCE_TIME_MS)) {
// if (current_state == 0) { // Button pressed (active low)
// ESP_LOGI(TAG, "Button on GPIO %d pressed", button_pin);
// led_command_t cmd = { .command = CMD_TOGGLE };
// xQueueSend(led_state->command_queue, &cmd, 0);
// }
// }
// // --- FIX ---
// // The last_press_time variable must only be updated when a state change is detected.
// last_press_time = current_time;
// }
// last_state = current_state;
// vTaskDelay(pdMS_TO_TICKS(10)); // Poll every 10ms
// }
// }
// // --- Task to read and parse JSON commands from UART ---
// static void uart_task(void *pvParameter) {
// uint8_t *data = (uint8_t *)malloc(BUF_SIZE);
// if (data == NULL) {
// ESP_LOGE(TAG, "Failed to allocate UART buffer.");
// vTaskDelete(NULL);
// }
// // Configure UART parameters
// const uart_config_t uart_config = {
// .baud_rate = UART_BAUD_RATE,
// .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_APB,
// };
// ESP_ERROR_CHECK(uart_param_config(UART_PORT, &uart_config));
// ESP_ERROR_CHECK(uart_driver_install(UART_PORT, BUF_SIZE * 2, 0, 0, NULL, 0));
// // --- Main UART loop ---
// while (1) {
// int len = uart_read_bytes(UART_PORT, data, (BUF_SIZE - 1), 20 / portTICK_PERIOD_MS);
// if (len > 0) {
// data[len] = '\0'; // Null-terminate the string
// ESP_LOGI(TAG, "Received UART data: %s", (char *)data);
// cJSON *root = cJSON_Parse((char *)data);
// if (root == NULL) {
// const char *error_ptr = cJSON_GetErrorPtr();
// if (error_ptr != NULL) {
// ESP_LOGE(TAG, "cJSON parse error before: %s", error_ptr);
// }
// continue;
// }
// cJSON *mod = cJSON_GetObjectItemCaseSensitive(root, "MOD");
// cJSON *com = cJSON_GetObjectItemCaseSensitive(root, "COM");
// cJSON *freq = cJSON_GetObjectItemCaseSensitive(root, "FREQ");
// if (cJSON_IsString(mod) && cJSON_IsString(com)) {
// led_command_t cmd;
// QueueHandle_t target_queue = NULL;
// if (strcmp(mod->valuestring, "LED1") == 0) {
// target_queue = led1_state.command_queue;
// } else if (strcmp(mod->valuestring, "LED2") == 0) {
// target_queue = led2_state.command_queue;
// }
// if (target_queue != NULL) {
// if (strcmp(com->valuestring, "START") == 0) {
// cmd.command = CMD_START;
// if (cJSON_IsNumber(freq)) {
// cmd.frequency = freq->valueint;
// } else {
// cmd.frequency = 0; // Use last saved frequency
// }
// xQueueSend(target_queue, &cmd, 0);
// } else if (strcmp(com->valuestring, "STOP") == 0) {
// cmd.command = CMD_STOP;
// xQueueSend(target_queue, &cmd, 0);
// } else {
// ESP_LOGW(TAG, "Invalid command received.");
// }
// } else {
// ESP_LOGW(TAG, "Invalid module received.");
// }
// } else {
// ESP_LOGW(TAG, "JSON format error: MOD or COM not found or not a string.");
// }
// cJSON_Delete(root);
// }
// }
// free(data);
// vTaskDelete(NULL);
// }
// // --- Main application entry point ---
// void app_main(void) {
// ESP_LOGI(TAG, "Initializing system...");
// // Initialize LED state variables
// led1_state = (led_state_t){
// .gpio_pin = LED1_GPIO_PIN,
// .ledc_channel = LEDC_CHANNEL_0,
// .ledc_timer = LEDC_TIMER_0, // Changed to a different timer to allow independent control if needed
// .last_freq = 10, // Default frequency
// .is_on = false,
// .command_queue = xQueueCreate(10, sizeof(led_command_t))
// };
// led2_state = (led_state_t){
// .gpio_pin = LED2_GPIO_PIN,
// .ledc_channel = LEDC_CHANNEL_1,
// .ledc_timer = LEDC_TIMER_1, // Changed to a different timer
// .last_freq = 20, // Default frequency
// .is_on = false,
// .command_queue = xQueueCreate(10, sizeof(led_command_t))
// };
// if (!led1_state.command_queue || !led2_state.command_queue) {
// ESP_LOGE(TAG, "Failed to create command queues.");
// return;
// }
// // Initialize PWM for both LEDs
// pwm_init(&led1_state);
// pwm_init(&led2_state);
// // Create FreeRTOS tasks
// xTaskCreate(led_task, "led1_task", 4096, (void *)&led1_state, 5, NULL);
// xTaskCreate(led_task, "led2_task", 4096, (void *)&led2_state, 5, NULL);
// xTaskCreate(button_task, "btn1_task", 2048, (void *)BTN1_GPIO_PIN, 5, NULL);
// xTaskCreate(button_task, "btn2_task", 2048, (void *)BTN2_GPIO_PIN, 5, NULL);
// xTaskCreate(uart_task, "uart_task", 4096, NULL, 6, NULL);
// }
// version5
// #include <stdio.h>
// #include <string.h>
// #include "freertos/FreeRTOS.h"
// #include "freertos/task.h"
// #include "freertos/queue.h"
// #include "driver/gpio.h"
// #include "driver/ledc.h"
// #include "driver/uart.h"
// #include "esp_log.h"
// #include "cJSON.h"
// #include "esp_timer.h"
// // Hardware connection definitions
// // GPIO 2 -----> LED1 ----> GND
// // GPIO 5 -----> LED2 ----> GND
// // GPIO 0 -----> Button1 ----> GND
// // GPIO 4 -----> Button2 ----> GND
// // UART0 -------> Serial Monitor (Simulated)
// // --- Define GPIOs for LEDs and Buttons ---
// #define LED1_GPIO_PIN GPIO_NUM_2
// #define LED2_GPIO_PIN GPIO_NUM_5
// #define BTN1_GPIO_PIN GPIO_NUM_0
// #define BTN2_GPIO_PIN GPIO_NUM_4
// // --- UART and JSON configuration ---
// #define UART_PORT UART_NUM_0
// #define UART_BAUD_RATE 115200
// #define BUF_SIZE (1024)
// // --- PWM configuration ---
// #define LEDC_TIMER LEDC_TIMER_0
// #define LEDC_MODE LEDC_LOW_SPEED_MODE
// #define LEDC_DUTY_RESOLUTION LEDC_TIMER_13_BIT // 8192 levels
// #define LEDC_DUTY 4096 // 50% duty cycle
// #define MAX_FREQUENCY 1000 // New define to cap max frequency
// // --- Debounce time for buttons ---
// #define DEBOUNCE_TIME_MS 20
// // --- Auto-stop time in milliseconds ---
// #define AUTO_STOP_TIME_MS 10000
// // --- Structure for an LED command ---
// typedef enum {
// CMD_START,
// CMD_STOP,
// CMD_TOGGLE
// } command_t;
// typedef struct {
// command_t command;
// int frequency; // Used only for CMD_START
// } led_command_t;
// // --- LED State Structure ---
// typedef struct {
// gpio_num_t gpio_pin;
// ledc_channel_t ledc_channel;
// ledc_timer_t ledc_timer;
// int last_freq;
// bool is_on;
// esp_timer_handle_t auto_stop_timer;
// QueueHandle_t command_queue;
// } led_state_t;
// static const char *TAG = "MAIN";
// static led_state_t led1_state;
// static led_state_t led2_state;
// // --- Function to initialize the LEDC PWM for a specific LED ---
// static void pwm_init(led_state_t *led_state) {
// ledc_timer_config_t ledc_timer = {
// .speed_mode = LEDC_MODE,
// .duty_resolution = LEDC_DUTY_RESOLUTION,
// .timer_num = led_state->ledc_timer,
// .freq_hz = 100, // Initial frequency, will be updated
// .clk_cfg = LEDC_AUTO_CLK,
// };
// ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
// ledc_channel_config_t ledc_channel = {
// .gpio_num = led_state->gpio_pin,
// .speed_mode = LEDC_MODE,
// .channel = led_state->ledc_channel,
// .timer_sel = led_state->ledc_timer,
// .duty = 0, // Initially off
// .hpoint = 0
// };
// ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
// }
// // --- Callback for the auto-stop timer ---
// static void IRAM_ATTR auto_stop_timer_cb(void* arg) {
// led_state_t* led_state = (led_state_t*) arg;
// ESP_LOGI(TAG, "Auto-stop timer expired for LED on GPIO %d", led_state->gpio_pin);
// led_command_t cmd = { .command = CMD_STOP };
// xQueueSendFromISR(led_state->command_queue, &cmd, NULL);
// }
// // --- Task to control an LED's PWM based on commands from its queue ---
// static void led_task(void *pvParameter) {
// led_state_t *led_state = (led_state_t *)pvParameter;
// led_command_t cmd;
// // Create the auto-stop timer for this LED
// const esp_timer_create_args_t timer_args = {
// .callback = &auto_stop_timer_cb,
// .arg = led_state,
// .name = "auto-stop-timer"
// };
// ESP_ERROR_CHECK(esp_timer_create(&timer_args, &led_state->auto_stop_timer));
// while (1) {
// if (xQueueReceive(led_state->command_queue, &cmd, portMAX_DELAY) == pdPASS) {
// ESP_LOGI(TAG, "Received command for LED on GPIO %d", led_state->gpio_pin);
// // Cancel any running auto-stop timer before processing a new command
// if (esp_timer_is_active(led_state->auto_stop_timer)) {
// esp_timer_stop(led_state->auto_stop_timer);
// }
// if (cmd.command == CMD_START) {
// if (!led_state->is_on) {
// // Start the LED with the new or last saved frequency
// int new_freq = (cmd.frequency > 0) ? cmd.frequency : led_state->last_freq;
// // Cap the frequency to prevent errors
// if (new_freq > MAX_FREQUENCY) {
// ESP_LOGW(TAG, "Requested frequency %d is too high, capping at %dHz", new_freq, MAX_FREQUENCY);
// new_freq = MAX_FREQUENCY;
// }
// led_state->last_freq = new_freq;
// ESP_LOGI(TAG, "Starting LED on GPIO %d at %dHz", led_state->gpio_pin, new_freq);
// ledc_set_freq(LEDC_MODE, led_state->ledc_timer, new_freq);
// ledc_set_duty(LEDC_MODE, led_state->ledc_channel, LEDC_DUTY);
// ledc_update_duty(LEDC_MODE, led_state->ledc_channel);
// led_state->is_on = true;
// // Start the 10-second auto-stop timer
// esp_timer_start_once(led_state->auto_stop_timer, AUTO_STOP_TIME_MS * 1000);
// } else {
// ESP_LOGI(TAG, "LED on GPIO %d is already running. Resetting timer.", led_state->gpio_pin);
// // If a new command is received while running, restart the timer
// esp_timer_start_once(led_state->auto_stop_timer, AUTO_STOP_TIME_MS * 1000);
// }
// } else if (cmd.command == CMD_STOP) {
// if (led_state->is_on) {
// // Stop the LED
// ESP_LOGI(TAG, "Stopping LED on GPIO %d", led_state->gpio_pin);
// ledc_set_duty(LEDC_MODE, led_state->ledc_channel, 0);
// ledc_update_duty(LEDC_MODE, led_state->ledc_channel);
// led_state->is_on = false;
// } else {
// ESP_LOGI(TAG, "LED on GPIO %d is already off.", led_state->gpio_pin);
// }
// } else if (cmd.command == CMD_TOGGLE) {
// // This command is primarily for the buttons
// if (led_state->is_on) {
// led_command_t stop_cmd = { .command = CMD_STOP };
// xQueueSend(led_state->command_queue, &stop_cmd, 0);
// } else {
// led_command_t start_cmd = { .command = CMD_START, .frequency = led_state->last_freq };
// xQueueSend(led_state->command_queue, &start_cmd, 0);
// }
// }
// }
// }
// }
// // --- Task to monitor button presses with debouncing ---
// static void button_task(void *pvParameter) {
// gpio_num_t button_pin = (gpio_num_t)pvParameter;
// led_state_t *led_state = NULL;
// if (button_pin == BTN1_GPIO_PIN) {
// led_state = &led1_state;
// } else if (button_pin == BTN2_GPIO_PIN) {
// led_state = &led2_state;
// }
// if (led_state == NULL) {
// vTaskDelete(NULL); // Should not happen
// }
// // Configure GPIO for button input with pull-up resistor
// gpio_config_t io_conf;
// io_conf.intr_type = GPIO_INTR_DISABLE;
// io_conf.pin_bit_mask = (1ULL << button_pin);
// io_conf.mode = GPIO_MODE_INPUT;
// io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
// io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
// gpio_config(&io_conf);
// bool last_state = gpio_get_level(button_pin);
// TickType_t last_press_time = 0;
// while (1) {
// bool current_state = gpio_get_level(button_pin);
// TickType_t current_time = xTaskGetTickCount();
// // Check for state change and debounce
// if (current_state != last_state) {
// if (current_time - last_press_time > pdMS_TO_TICKS(DEBOUNCE_TIME_MS)) {
// if (current_state == 0) { // Button pressed (active low)
// ESP_LOGI(TAG, "Button on GPIO %d pressed", button_pin);
// led_command_t cmd = { .command = CMD_TOGGLE };
// xQueueSend(led_state->command_queue, &cmd, 0);
// }
// }
// // --- FIX ---
// // The last_press_time variable must only be updated when a state change is detected.
// last_press_time = current_time;
// }
// last_state = current_state;
// vTaskDelay(pdMS_TO_TICKS(10)); // Poll every 10ms
// }
// }
// // --- Task to read and parse JSON commands from UART ---
// static void uart_task(void *pvParameter) {
// uint8_t *data = (uint8_t *)malloc(BUF_SIZE);
// if (data == NULL) {
// ESP_LOGE(TAG, "Failed to allocate UART buffer.");
// vTaskDelete(NULL);
// }
// // Configure UART parameters
// const uart_config_t uart_config = {
// .baud_rate = UART_BAUD_RATE,
// .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_APB,
// };
// ESP_ERROR_CHECK(uart_param_config(UART_PORT, &uart_config));
// ESP_ERROR_CHECK(uart_driver_install(UART_PORT, BUF_SIZE * 2, 0, 0, NULL, 0));
// // --- Main UART loop ---
// while (1) {
// int len = uart_read_bytes(UART_PORT, data, (BUF_SIZE - 1), 20 / portTICK_PERIOD_MS);
// if (len > 0) {
// data[len] = '\0'; // Null-terminate the string
// ESP_LOGI(TAG, "Received UART data: %s", (char *)data);
// cJSON *root = cJSON_Parse((char *)data);
// if (root == NULL) {
// const char *error_ptr = cJSON_GetErrorPtr();
// if (error_ptr != NULL) {
// ESP_LOGE(TAG, "cJSON parse error before: %s", error_ptr);
// }
// continue;
// }
// cJSON *mod = cJSON_GetObjectItemCaseSensitive(root, "MOD");
// cJSON *com = cJSON_GetObjectItemCaseSensitive(root, "COM");
// cJSON *freq = cJSON_GetObjectItemCaseSensitive(root, "FREQ");
// if (cJSON_IsString(mod) && cJSON_IsString(com)) {
// led_command_t cmd;
// QueueHandle_t target_queue = NULL;
// if (strcmp(mod->valuestring, "LED1") == 0) {
// target_queue = led1_state.command_queue;
// } else if (strcmp(mod->valuestring, "LED2") == 0) {
// target_queue = led2_state.command_queue;
// }
// if (target_queue != NULL) {
// if (strcmp(com->valuestring, "START") == 0) {
// cmd.command = CMD_START;
// if (cJSON_IsNumber(freq)) {
// cmd.frequency = freq->valueint;
// } else {
// cmd.frequency = 0; // Use last saved frequency
// }
// xQueueSend(target_queue, &cmd, 0);
// } else if (strcmp(com->valuestring, "STOP") == 0) {
// cmd.command = CMD_STOP;
// xQueueSend(target_queue, &cmd, 0);
// } else {
// ESP_LOGW(TAG, "Invalid command received.");
// }
// } else {
// ESP_LOGW(TAG, "Invalid module received.");
// }
// } else {
// ESP_LOGW(TAG, "JSON format error: MOD or COM not found or not a string.");
// }
// cJSON_Delete(root);
// }
// }
// free(data);
// vTaskDelete(NULL);
// }
// // --- Main application entry point ---
// void app_main(void) {
// ESP_LOGI(TAG, "Initializing system...");
// // Initialize LED state variables
// led1_state = (led_state_t){
// .gpio_pin = LED1_GPIO_PIN,
// .ledc_channel = LEDC_CHANNEL_0,
// .ledc_timer = LEDC_TIMER_0, // Changed to a different timer to allow independent control if needed
// .last_freq = 10, // Default frequency
// .is_on = false,
// .command_queue = xQueueCreate(10, sizeof(led_command_t))
// };
// led2_state = (led_state_t){
// .gpio_pin = LED2_GPIO_PIN,
// .ledc_channel = LEDC_CHANNEL_1,
// .ledc_timer = LEDC_TIMER_1, // Changed to a different timer
// .last_freq = 20, // Default frequency
// .is_on = false,
// .command_queue = xQueueCreate(10, sizeof(led_command_t))
// };
// if (!led1_state.command_queue || !led2_state.command_queue) {
// ESP_LOGE(TAG, "Failed to create command queues.");
// return;
// }
// // Initialize PWM for both LEDs
// pwm_init(&led1_state);
// pwm_init(&led2_state);
// // Create FreeRTOS tasks
// xTaskCreate(led_task, "led1_task", 4096, (void *)&led1_state, 5, NULL);
// xTaskCreate(led_task, "led2_task", 4096, (void *)&led2_state, 5, NULL);
// xTaskCreate(button_task, "btn1_task", 2048, (void *)BTN1_GPIO_PIN, 5, NULL);
// xTaskCreate(button_task, "btn2_task", 2048, (void *)BTN2_GPIO_PIN, 5, NULL);
// xTaskCreate(uart_task, "uart_task", 4096, NULL, 6, NULL);
// }
///////////////////////////////////////////////////////////////////////////////////////
// Human Language
// #include <stdio.h>
// #include <string.h>
// #include "freertos/FreeRTOS.h"
// #include "freertos/task.h"
// #include "freertos/queue.h"
// #include "driver/gpio.h"
// #include "driver/ledc.h"
// #include "driver/uart.h"
// #include "esp_log.h"
// #include "cJSON.h"
// #include "esp_timer.h"
// // Hardware connection definitions
// // GPIO 2 -----> LED1 ----> GND
// // GPIO 5 -----> LED2 ----> GND
// // GPIO 0 -----> Button1 ----> GND
// // GPIO 4 -----> Button2 ----> GND
// // UART0 -------> Serial Monitor (Simulated)
// // --- Define GPIOs for LEDs and Buttons ---
// #define LED1_GPIO_PIN GPIO_NUM_2
// #define LED2_GPIO_PIN GPIO_NUM_5
// #define BTN1_GPIO_PIN GPIO_NUM_0
// #define BTN2_GPIO_PIN GPIO_NUM_4
// // --- UART and JSON configuration ---
// #define UART_PORT UART_NUM_0
// #define UART_BAUD_RATE 115200
// #define BUF_SIZE (1024)
// // --- PWM configuration ---
// #define LEDC_MODE LEDC_LOW_SPEED_MODE
// #define LEDC_DUTY_RESOLUTION LEDC_TIMER_13_BIT // 8192 levels
// #define LEDC_DUTY 4096 // 50% duty cycle
// #define MAX_FREQUENCY 1000 // New define to cap max frequency
// // --- Debounce time for buttons ---
// #define DEBOUNCE_TIME_MS 20
// // --- Auto-stop time in milliseconds ---
// #define AUTO_STOP_TIME_MS 10000
// // --- Structure for an LED command ---
// typedef enum {
// CMD_START,
// CMD_STOP,
// CMD_TOGGLE
// } command_t;
// typedef struct {
// command_t command;
// int frequency; // Used only for CMD_START
// } led_command_t;
// // --- LED State Structure ---
// typedef struct {
// gpio_num_t gpio_pin;
// ledc_channel_t ledc_channel;
// ledc_timer_t ledc_timer;
// int last_freq;
// bool is_on;
// esp_timer_handle_t auto_stop_timer;
// QueueHandle_t command_queue;
// } led_state_t;
// // Static variables to hold the state of each LED
// static const char *TAG = "MAIN";
// static led_state_t led1_state;
// static led_state_t led2_state;
// // --- Function to initialize the LEDC PWM for a specific LED ---
// // This function sets up the timer and channel for a given LED,
// // ensuring the PWM is ready to be controlled.
// static void pwm_init(led_state_t *led_state) {
// ledc_timer_config_t ledc_timer_config_local = {
// .speed_mode = LEDC_MODE,
// .duty_resolution = LEDC_DUTY_RESOLUTION,
// .timer_num = led_state->ledc_timer,
// .freq_hz = 100, // Initial frequency, will be updated
// .clk_cfg = LEDC_AUTO_CLK,
// };
// ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer_config_local));
// ledc_channel_config_t ledc_channel = {
// .gpio_num = led_state->gpio_pin,
// .speed_mode = LEDC_MODE,
// .channel = led_state->ledc_channel,
// .timer_sel = led_state->ledc_timer,
// .duty = 0, // Initially off
// .hpoint = 0
// };
// ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
// }
// // --- Callback for the auto-stop timer ---
// // This function is executed when the auto-stop timer expires.
// // It sends a CMD_STOP command to the corresponding LED's command queue.
// static void IRAM_ATTR auto_stop_timer_cb(void* arg) {
// led_state_t* led_state = (led_state_t*) arg;
// ESP_LOGI(TAG, "Auto-stop timer expired for LED on GPIO %d", led_state->gpio_pin);
// led_command_t cmd = { .command = CMD_STOP };
// // Use xQueueSendFromISR to send the command from an ISR
// xQueueSendFromISR(led_state->command_queue, &cmd, NULL);
// }
// // --- Task to control an LED's PWM based on commands from its queue ---
// // Each LED has its own task, ensuring independent operation.
// // The task waits for a command and then acts on it.
// // It also manages the 10-second auto-stop timer.
// static void led_task(void *pvParameter) {
// led_state_t *led_state = (led_state_t *)pvParameter;
// led_command_t cmd;
// // Create the auto-stop timer for this LED
// const esp_timer_create_args_t timer_args = {
// .callback = &auto_stop_timer_cb,
// .arg = led_state,
// .name = "auto-stop-timer"
// };
// ESP_ERROR_CHECK(esp_timer_create(&timer_args, &led_state->auto_stop_timer));
// while (1) {
// // Wait indefinitely for a command from the queue
// if (xQueueReceive(led_state->command_queue, &cmd, portMAX_DELAY) == pdPASS) {
// ESP_LOGI(TAG, "Received command for LED on GPIO %d", led_state->gpio_pin);
// // Cancel any running auto-stop timer before processing a new command
// if (esp_timer_is_active(led_state->auto_stop_timer)) {
// esp_timer_stop(led_state->auto_stop_timer);
// }
// if (cmd.command == CMD_START) {
// // If the LED is already on, just restart the timer.
// // If it's off, start it with the new frequency.
// if (!led_state->is_on || (cmd.frequency > 0 && cmd.frequency != led_state->last_freq)) {
// // Start the LED with the new or last saved frequency
// int new_freq = (cmd.frequency > 0) ? cmd.frequency : led_state->last_freq;
// // Cap the frequency to prevent errors
// if (new_freq > MAX_FREQUENCY) {
// ESP_LOGW(TAG, "Requested frequency %d is too high, capping at %dHz", new_freq, MAX_FREQUENCY);
// new_freq = MAX_FREQUENCY;
// }
// led_state->last_freq = new_freq;
// ESP_LOGI(TAG, "Starting LED on GPIO %d at %dHz", led_state->gpio_pin, new_freq);
// ledc_set_freq(LEDC_MODE, led_state->ledc_timer, new_freq);
// ledc_set_duty(LEDC_MODE, led_state->ledc_channel, LEDC_DUTY);
// ledc_update_duty(LEDC_MODE, led_state->ledc_channel);
// led_state->is_on = true;
// } else {
// ESP_LOGI(TAG, "LED on GPIO %d is already running at %dHz. Resetting timer.", led_state->gpio_pin, led_state->last_freq);
// }
// // Start or restart the 10-second auto-stop timer
// esp_timer_start_once(led_state->auto_stop_timer, AUTO_STOP_TIME_MS * 1000);
// } else if (cmd.command == CMD_STOP) {
// if (led_state->is_on) {
// // Stop the LED
// ESP_LOGI(TAG, "Stopping LED on GPIO %d", led_state->gpio_pin);
// ledc_set_duty(LEDC_MODE, led_state->ledc_channel, 0);
// ledc_update_duty(LEDC_MODE, led_state->ledc_channel);
// led_state->is_on = false;
// } else {
// ESP_LOGI(TAG, "LED on GPIO %d is already off.", led_state->gpio_pin);
// }
// } else if (cmd.command == CMD_TOGGLE) {
// // This command is used by the button handler
// if (led_state->is_on) {
// led_command_t stop_cmd = { .command = CMD_STOP };
// xQueueSend(led_state->command_queue, &stop_cmd, 0);
// } else {
// led_command_t start_cmd = { .command = CMD_START, .frequency = led_state->last_freq };
// xQueueSend(led_state->command_queue, &start_cmd, 0);
// }
// }
// }
// }
// }
// // --- Task to monitor button presses with debouncing ---
// // This task handles button presses for a single button and sends a
// // CMD_TOGGLE command to the appropriate LED queue.
// static void button_task(void *pvParameter) {
// gpio_num_t button_pin = (gpio_num_t)pvParameter;
// led_state_t *led_state = NULL;
// if (button_pin == BTN1_GPIO_PIN) {
// led_state = &led1_state;
// } else if (button_pin == BTN2_GPIO_PIN) {
// led_state = &led2_state;
// }
// if (led_state == NULL) {
// vTaskDelete(NULL); // Should not happen
// }
// // Configure GPIO for button input with pull-up resistor
// gpio_config_t io_conf;
// io_conf.intr_type = GPIO_INTR_DISABLE;
// io_conf.pin_bit_mask = (1ULL << button_pin);
// io_conf.mode = GPIO_MODE_INPUT;
// io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
// io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
// gpio_config(&io_conf);
// // Initial state and time for debouncing
// bool last_state = gpio_get_level(button_pin);
// TickType_t last_press_time = xTaskGetTickCount();
// while (1) {
// bool current_state = gpio_get_level(button_pin);
// TickType_t current_time = xTaskGetTickCount();
// // Check for state change and debounce
// if (current_state != last_state) {
// // Check if enough time has passed since the last state change
// if (current_time - last_press_time > pdMS_TO_TICKS(DEBOUNCE_TIME_MS)) {
// if (current_state == 0) { // Button pressed (active low)
// ESP_LOGI(TAG, "Button on GPIO %d pressed", button_pin);
// led_command_t cmd = { .command = CMD_TOGGLE };
// xQueueSend(led_state->command_queue, &cmd, 0);
// }
// }
// // Update last_press_time regardless of debounce check
// last_press_time = current_time;
// }
// last_state = current_state;
// vTaskDelay(pdMS_TO_TICKS(10)); // Poll every 10ms
// }
// }
// // --- Task to read and parse JSON commands from UART ---
// // This task continuously reads data from the UART,
// // parses it as a JSON string, and sends commands to the
// // appropriate LED task's queue.
// static void uart_task(void *pvParameter) {
// uint8_t *data = (uint8_t *)malloc(BUF_SIZE);
// if (data == NULL) {
// ESP_LOGE(TAG, "Failed to allocate UART buffer.");
// vTaskDelete(NULL);
// }
// // Configure UART parameters
// const uart_config_t uart_config = {
// .baud_rate = UART_BAUD_RATE,
// .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_APB,
// };
// ESP_ERROR_CHECK(uart_param_config(UART_PORT, &uart_config));
// ESP_ERROR_CHECK(uart_driver_install(UART_PORT, BUF_SIZE * 2, 0, 0, NULL, 0));
// // --- Main UART loop ---
// while (1) {
// int len = uart_read_bytes(UART_PORT, data, (BUF_SIZE - 1), 20 / portTICK_PERIOD_MS);
// if (len > 0) {
// data[len] = '\0'; // Null-terminate the string
// ESP_LOGI(TAG, "Received UART data: %s", (char *)data);
// cJSON *root = cJSON_Parse((char *)data);
// if (root == NULL) {
// const char *error_ptr = cJSON_GetErrorPtr();
// if (error_ptr != NULL) {
// ESP_LOGE(TAG, "cJSON parse error before: %s", error_ptr);
// }
// continue;
// }
// cJSON *mod = cJSON_GetObjectItemCaseSensitive(root, "MOD");
// cJSON *com = cJSON_GetObjectItemCaseSensitive(root, "COM");
// cJSON *freq = cJSON_GetObjectItemCaseSensitive(root, "FREQ");
// if (cJSON_IsString(mod) && cJSON_IsString(com)) {
// led_command_t cmd;
// QueueHandle_t target_queue = NULL;
// if (strcmp(mod->valuestring, "LED1") == 0) {
// target_queue = led1_state.command_queue;
// } else if (strcmp(mod->valuestring, "LED2") == 0) {
// target_queue = led2_state.command_queue;
// }
// if (target_queue != NULL) {
// if (strcmp(com->valuestring, "START") == 0) {
// cmd.command = CMD_START;
// if (cJSON_IsNumber(freq)) {
// cmd.frequency = freq->valueint;
// } else {
// cmd.frequency = 0; // Use last saved frequency
// }
// xQueueSend(target_queue, &cmd, 0);
// } else if (strcmp(com->valuestring, "STOP") == 0) {
// cmd.command = CMD_STOP;
// xQueueSend(target_queue, &cmd, 0);
// } else {
// ESP_LOGW(TAG, "Invalid command received: %s", com->valuestring);
// }
// } else {
// ESP_LOGW(TAG, "Invalid module received: %s", mod->valuestring);
// }
// } else {
// ESP_LOGW(TAG, "JSON format error: MOD or COM not found or not a string.");
// }
// cJSON_Delete(root);
// }
// }
// free(data);
// vTaskDelete(NULL);
// }
// // --- Main application entry point ---
// void app_main(void) {
// ESP_LOGI(TAG, "Initializing system...");
// // Initialize LED state variables
// led1_state = (led_state_t){
// .gpio_pin = LED1_GPIO_PIN,
// .ledc_channel = LEDC_CHANNEL_0,
// .ledc_timer = LEDC_TIMER_0,
// .last_freq = 10, // Default frequency
// .is_on = false,
// .command_queue = xQueueCreate(10, sizeof(led_command_t))
// };
// led2_state = (led_state_t){
// .gpio_pin = LED2_GPIO_PIN,
// .ledc_channel = LEDC_CHANNEL_1,
// .ledc_timer = LEDC_TIMER_1,
// .last_freq = 20, // Default frequency
// .is_on = false,
// .command_queue = xQueueCreate(10, sizeof(led_command_t))
// };
// if (!led1_state.command_queue || !led2_state.command_queue) {
// ESP_LOGE(TAG, "Failed to create command queues.");
// return;
// }
// // Initialize PWM timers before configuring channels
// ledc_timer_config_t ledc_timer_config_local;
// ledc_timer_config_local = (ledc_timer_config_t){
// .speed_mode = LEDC_MODE,
// .duty_resolution = LEDC_DUTY_RESOLUTION,
// .timer_num = LEDC_TIMER_0,
// .freq_hz = 100,
// .clk_cfg = LEDC_AUTO_CLK,
// };
// ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer_config_local));
// ledc_timer_config_local.timer_num = LEDC_TIMER_1;
// ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer_config_local));
// // Now initialize channels for both LEDs
// pwm_init(&led1_state);
// pwm_init(&led2_state);
// // Create FreeRTOS tasks
// xTaskCreate(led_task, "led1_task", 4096, (void *)&led1_state, 5, NULL);
// xTaskCreate(led_task, "led2_task", 4096, (void *)&led2_state, 5, NULL);
// xTaskCreate(button_task, "btn1_task", 2048, (void *)BTN1_GPIO_PIN, 5, NULL);
// xTaskCreate(button_task, "btn2_task", 2048, (void *)BTN2_GPIO_PIN, 5, NULL);
// xTaskCreate(uart_task, "uart_task", 4096, NULL, 6, NULL);
// }
/////////////////////////////////////
// #include <stdio.h>
// #include <string.h>
// #include "freertos/FreeRTOS.h"
// #include "freertos/task.h"
// #include "freertos/queue.h"
// #include "driver/gpio.h"
// #include "driver/ledc.h"
// #include "driver/uart.h"
// #include "esp_log.h"
// #include "cJSON.h"
// #include "esp_timer.h"
// // Hardware connection definitions
// // GPIO 2 -----> LED1 ----> GND
// // GPIO 5 -----> LED2 ----> GND
// // GPIO 0 -----> Button1 ----> GND
// // GPIO 4 -----> Button2 ----> GND
// // UART0 -------> Serial Monitor (Simulated)
// // --- Define GPIOs for LEDs and Buttons ---
// #define LED1_GPIO_PIN GPIO_NUM_2
// #define LED2_GPIO_PIN GPIO_NUM_5
// #define BTN1_GPIO_PIN GPIO_NUM_0
// #define BTN2_GPIO_PIN GPIO_NUM_4
// // --- UART and JSON configuration ---
// #define UART_PORT UART_NUM_0
// #define UART_BAUD_RATE 115200
// #define BUF_SIZE (1024)
// // --- PWM configuration ---
// #define LEDC_MODE LEDC_LOW_SPEED_MODE
// #define LEDC_DUTY_RESOLUTION LEDC_TIMER_13_BIT // 8192 levels
// #define LEDC_DUTY 4096 // 50% duty cycle
// #define MAX_FREQUENCY 1000 // New define to cap max frequency
// // --- Debounce time for buttons ---
// #define DEBOUNCE_TIME_MS 20
// // --- Auto-stop time in milliseconds ---
// #define AUTO_STOP_TIME_MS 10000
// // --- Structure for an LED command ---
// typedef enum {
// CMD_START,
// CMD_STOP,
// CMD_TOGGLE
// } command_t;
// typedef struct {
// command_t command;
// int frequency; // Used only for CMD_START
// } led_command_t;
// // --- LED State Structure ---
// typedef struct {
// gpio_num_t gpio_pin;
// ledc_channel_t ledc_channel;
// ledc_timer_t ledc_timer;
// int last_freq;
// bool is_on;
// esp_timer_handle_t auto_stop_timer;
// QueueHandle_t command_queue;
// } led_state_t;
// // Static variables to hold the state of each LED
// static const char *TAG = "MAIN";
// static led_state_t led1_state;
// static led_state_t led2_state;
// // --- Function to initialize the LEDC PWM for a specific LED ---
// // This function sets up the timer and channel for a given LED,
// // ensuring the PWM is ready to be controlled.
// static void pwm_init(led_state_t *led_state) {
// ledc_timer_config_t ledc_timer_config_local = {
// .speed_mode = LEDC_MODE,
// .duty_resolution = LEDC_DUTY_RESOLUTION,
// .timer_num = led_state->ledc_timer,
// .freq_hz = 100, // Initial frequency, will be updated
// .clk_cfg = LEDC_AUTO_CLK,
// };
// ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer_config_local));
// ledc_channel_config_t ledc_channel = {
// .gpio_num = led_state->gpio_pin,
// .speed_mode = LEDC_MODE,
// .channel = led_state->ledc_channel,
// .timer_sel = led_state->ledc_timer,
// .duty = 0, // Initially off
// .hpoint = 0
// };
// ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
// }
// // --- Callback for the auto-stop timer ---
// // This function is executed when the auto-stop timer expires.
// // It sends a CMD_STOP command to the corresponding LED's command queue.
// static void IRAM_ATTR auto_stop_timer_cb(void* arg) {
// led_state_t* led_state = (led_state_t*) arg;
// ESP_LOGI(TAG, "Auto-stop timer expired for LED on GPIO %d", led_state->gpio_pin);
// led_command_t cmd = { .command = CMD_STOP };
// // Use xQueueSendFromISR to send the command from an ISR
// xQueueSendFromISR(led_state->command_queue, &cmd, NULL);
// }
// // --- Task to control an LED's PWM based on commands from its queue ---
// // Each LED has its own task, ensuring independent operation.
// // The task waits for a command and then acts on it.
// // It also manages the 10-second auto-stop timer.
// static void led_task(void *pvParameter) {
// led_state_t *led_state = (led_state_t *)pvParameter;
// led_command_t cmd;
// // Create the auto-stop timer for this LED
// const esp_timer_create_args_t timer_args = {
// .callback = &auto_stop_timer_cb,
// .arg = led_state,
// .name = "auto-stop-timer"
// };
// ESP_ERROR_CHECK(esp_timer_create(&timer_args, &led_state->auto_stop_timer));
// while (1) {
// // Wait indefinitely for a command from the queue
// if (xQueueReceive(led_state->command_queue, &cmd, portMAX_DELAY) == pdPASS) {
// ESP_LOGI(TAG, "Received command for LED on GPIO %d", led_state->gpio_pin);
// // Cancel any running auto-stop timer before processing a new command
// if (esp_timer_is_active(led_state->auto_stop_timer)) {
// esp_timer_stop(led_state->auto_stop_timer);
// }
// if (cmd.command == CMD_START) {
// // If the LED is already on, just restart the timer.
// // If it's off, start it with the new frequency.
// if (!led_state->is_on || (cmd.frequency > 0 && cmd.frequency != led_state->last_freq)) {
// // Start the LED with the new or last saved frequency
// int new_freq = (cmd.frequency > 0) ? cmd.frequency : led_state->last_freq;
// // Cap the frequency to prevent errors
// if (new_freq > MAX_FREQUENCY) {
// ESP_LOGW(TAG, "Requested frequency %d is too high, capping at %dHz", new_freq, MAX_FREQUENCY);
// new_freq = MAX_FREQUENCY;
// }
// led_state->last_freq = new_freq;
// ESP_LOGI(TAG, "Starting LED on GPIO %d at %dHz", led_state->gpio_pin, new_freq);
// ledc_set_freq(LEDC_MODE, led_state->ledc_timer, new_freq);
// ledc_set_duty(LEDC_MODE, led_state->ledc_channel, LEDC_DUTY);
// ledc_update_duty(LEDC_MODE, led_state->ledc_channel);
// led_state->is_on = true;
// } else {
// ESP_LOGI(TAG, "LED on GPIO %d is already running at %dHz. Resetting timer.", led_state->gpio_pin, led_state->last_freq);
// }
// // Start or restart the 10-second auto-stop timer
// esp_timer_start_once(led_state->auto_stop_timer, AUTO_STOP_TIME_MS * 1000);
// } else if (cmd.command == CMD_STOP) {
// if (led_state->is_on) {
// // Stop the LED
// ESP_LOGI(TAG, "Stopping LED on GPIO %d", led_state->gpio_pin);
// ledc_set_duty(LEDC_MODE, led_state->ledc_channel, 0);
// ledc_update_duty(LEDC_MODE, led_state->ledc_channel);
// led_state->is_on = false;
// } else {
// ESP_LOGI(TAG, "LED on GPIO %d is already off.", led_state->gpio_pin);
// }
// } else if (cmd.command == CMD_TOGGLE) {
// // This command is used by the button handler
// if (led_state->is_on) {
// led_command_t stop_cmd = { .command = CMD_STOP };
// xQueueSend(led_state->command_queue, &stop_cmd, 0);
// } else {
// led_command_t start_cmd = { .command = CMD_START, .frequency = led_state->last_freq };
// xQueueSend(led_state->command_queue, &start_cmd, 0);
// }
// }
// }
// }
// }
// // --- Task to monitor button presses with debouncing ---
// // This task handles button presses for a single button and sends a
// // CMD_TOGGLE command to the appropriate LED queue.
// static void button_task(void *pvParameter) {
// gpio_num_t button_pin = (gpio_num_t)pvParameter;
// led_state_t *led_state = NULL;
// if (button_pin == BTN1_GPIO_PIN) {
// led_state = &led1_state;
// } else if (button_pin == BTN2_GPIO_PIN) {
// led_state = &led2_state;
// }
// if (led_state == NULL) {
// vTaskDelete(NULL); // Should not happen
// }
// // Configure GPIO for button input with pull-up resistor
// gpio_config_t io_conf;
// io_conf.intr_type = GPIO_INTR_DISABLE;
// io_conf.pin_bit_mask = (1ULL << button_pin);
// io_conf.mode = GPIO_MODE_INPUT;
// io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
// io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
// gpio_config(&io_conf);
// // Initial state and time for debouncing
// bool last_state = gpio_get_level(button_pin);
// TickType_t last_press_time = xTaskGetTickCount();
// while (1) {
// bool current_state = gpio_get_level(button_pin);
// TickType_t current_time = xTaskGetTickCount();
// // Check for state change and debounce
// if (current_state != last_state) {
// // Check if enough time has passed since the last state change
// if (current_time - last_press_time > pdMS_TO_TICKS(DEBOUNCE_TIME_MS)) {
// if (current_state == 0) { // Button pressed (active low)
// ESP_LOGI(TAG, "Button on GPIO %d pressed", button_pin);
// led_command_t cmd = { .command = CMD_TOGGLE };
// xQueueSend(led_state->command_queue, &cmd, 0);
// }
// }
// // Update last_press_time regardless of debounce check
// last_press_time = current_time;
// }
// last_state = current_state;
// vTaskDelay(pdMS_TO_TICKS(10)); // Poll every 10ms
// }
// }
// // --- Task to read and parse JSON commands from UART ---
// // This task continuously reads data from the UART,
// // parses it as a JSON string, and sends commands to the
// // appropriate LED task's queue.
// static void uart_task(void *pvParameter) {
// uint8_t *data = (uint8_t *)malloc(BUF_SIZE);
// if (data == NULL) {
// ESP_LOGE(TAG, "Failed to allocate UART buffer.");
// vTaskDelete(NULL);
// }
// // Configure UART parameters
// const uart_config_t uart_config = {
// .baud_rate = UART_BAUD_RATE,
// .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_APB,
// };
// ESP_ERROR_CHECK(uart_param_config(UART_PORT, &uart_config));
// ESP_ERROR_CHECK(uart_driver_install(UART_PORT, BUF_SIZE * 2, 0, 0, NULL, 0));
// // --- Main UART loop ---
// while (1) {
// int len = uart_read_bytes(UART_PORT, data, (BUF_SIZE - 1), 20 / portTICK_PERIOD_MS);
// if (len > 0) {
// data[len] = '\0'; // Null-terminate the string
// ESP_LOGI(TAG, "Received UART data: %s", (char *)data);
// cJSON *root = cJSON_Parse((char *)data);
// if (root == NULL) {
// const char *error_ptr = cJSON_GetErrorPtr();
// if (error_ptr != NULL) {
// ESP_LOGE(TAG, "cJSON parse error before: %s", error_ptr);
// }
// continue;
// }
// cJSON *mod = cJSON_GetObjectItemCaseSensitive(root, "MOD");
// cJSON *com = cJSON_GetObjectItemCaseSensitive(root, "COM");
// cJSON *freq = cJSON_GetObjectItemCaseSensitive(root, "FREQ");
// if (cJSON_IsString(mod) && cJSON_IsString(com)) {
// led_command_t cmd;
// QueueHandle_t target_queue = NULL;
// if (strcmp(mod->valuestring, "LED1") == 0) {
// target_queue = led1_state.command_queue;
// } else if (strcmp(mod->valuestring, "LED2") == 0) {
// target_queue = led2_state.command_queue;
// }
// if (target_queue != NULL) {
// if (strcmp(com->valuestring, "START") == 0) {
// cmd.command = CMD_START;
// if (cJSON_IsNumber(freq)) {
// cmd.frequency = freq->valueint;
// } else {
// cmd.frequency = 0; // Use last saved frequency
// }
// xQueueSend(target_queue, &cmd, 0);
// } else if (strcmp(com->valuestring, "STOP") == 0) {
// cmd.command = CMD_STOP;
// xQueueSend(target_queue, &cmd, 0);
// } else {
// printf("Invalid command received: %s\n", com->valuestring);
// }
// } else {
// printf("Invalid module received: %s\n", mod->valuestring);
// }
// } else {
// printf("JSON format error: MOD or COM not found or not a string.\n");
// }
// cJSON_Delete(root);
// }
// }
// free(data);
// vTaskDelete(NULL);
// }
// // --- Main application entry point ---
// void app_main(void) {
// ESP_LOGI(TAG, "Initializing system...");
// // Initialize LED state variables
// led1_state = (led_state_t){
// .gpio_pin = LED1_GPIO_PIN,
// .ledc_channel = LEDC_CHANNEL_0,
// .ledc_timer = LEDC_TIMER_0,
// .last_freq = 10, // Default frequency
// .is_on = false,
// .command_queue = xQueueCreate(10, sizeof(led_command_t))
// };
// led2_state = (led_state_t){
// .gpio_pin = LED2_GPIO_PIN,
// .ledc_channel = LEDC_CHANNEL_1,
// .ledc_timer = LEDC_TIMER_1,
// .last_freq = 20, // Default frequency
// .is_on = false,
// .command_queue = xQueueCreate(10, sizeof(led_command_t))
// };
// if (!led1_state.command_queue || !led2_state.command_queue) {
// ESP_LOGE(TAG, "Failed to create command queues.");
// return;
// }
// // Initialize PWM timers before configuring channels
// ledc_timer_config_t ledc_timer_config_local;
// ledc_timer_config_local = (ledc_timer_config_t){
// .speed_mode = LEDC_MODE,
// .duty_resolution = LEDC_DUTY_RESOLUTION,
// .timer_num = LEDC_TIMER_0,
// .freq_hz = 100,
// .clk_cfg = LEDC_AUTO_CLK,
// };
// ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer_config_local));
// ledc_timer_config_local.timer_num = LEDC_TIMER_1;
// ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer_config_local));
// // Now initialize channels for both LEDs
// pwm_init(&led1_state);
// pwm_init(&led2_state);
// // Create FreeRTOS tasks
// xTaskCreate(led_task, "led1_task", 4096, (void *)&led1_state, 5, NULL);
// xTaskCreate(led_task, "led2_task", 4096, (void *)&led2_state, 5, NULL);
// xTaskCreate(button_task, "btn1_task", 2048, (void *)BTN1_GPIO_PIN, 5, NULL);
// xTaskCreate(button_task, "btn2_task", 2048, (void *)BTN2_GPIO_PIN, 5, NULL);
// xTaskCreate(uart_task, "uart_task", 4096, NULL, 6, NULL);
// }
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "driver/ledc.h"
#include "driver/uart.h"
#include "esp_log.h"
#include "cJSON.h"
#include "esp_timer.h"
// Hardware connection definitions
// GPIO 2 -----> LED1 ----> GND
// GPIO 5 -----> LED2 ----> GND
// GPIO 0 -----> Button1 ----> GND
// GPIO 4 -----> Button2 ----> GND
// UART0 -------> Serial Monitor (Simulated)
// --- Define GPIOs for LEDs and Buttons ---
#define LED1_GPIO_PIN GPIO_NUM_2
#define LED2_GPIO_PIN GPIO_NUM_5
#define BTN1_GPIO_PIN GPIO_NUM_0
#define BTN2_GPIO_PIN GPIO_NUM_4
// --- UART and JSON configuration ---
#define UART_PORT UART_NUM_0
#define UART_BAUD_RATE 115200
#define BUF_SIZE (1024)
// --- PWM configuration ---
#define LEDC_MODE LEDC_LOW_SPEED_MODE
#define LEDC_DUTY_RESOLUTION LEDC_TIMER_13_BIT // 8192 levels
#define LEDC_DUTY 4096 // 50% duty cycle
#define MAX_FREQUENCY 1000 // New define to cap max frequency
// --- Debounce time for buttons ---
#define DEBOUNCE_TIME_MS 20
// --- Auto-stop time in milliseconds ---
#define AUTO_STOP_TIME_MS 10000
// --- Structure for an LED command ---
typedef enum {
CMD_START,
CMD_STOP,
CMD_TOGGLE
} command_t;
typedef struct {
command_t command;
int frequency; // Used only for CMD_START
} led_command_t;
// --- LED State Structure ---
typedef struct {
gpio_num_t gpio_pin;
ledc_channel_t ledc_channel;
ledc_timer_t ledc_timer;
int last_freq;
bool is_on;
esp_timer_handle_t auto_stop_timer;
QueueHandle_t command_queue;
} led_state_t;
// Static variables to hold the state of each LED
static const char *TAG = "MAIN";
static led_state_t led1_state;
static led_state_t led2_state;
// --- Function to initialize the LEDC PWM for a specific LED ---
// This function sets up the timer and channel for a given LED,
// ensuring the PWM is ready to be controlled.
static void pwm_init(led_state_t *led_state) {
ledc_timer_config_t ledc_timer_config_local = {
.speed_mode = LEDC_MODE,
.duty_resolution = LEDC_DUTY_RESOLUTION,
.timer_num = led_state->ledc_timer,
.freq_hz = 100, // Initial frequency, will be updated
.clk_cfg = LEDC_AUTO_CLK,
};
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer_config_local));
ledc_channel_config_t ledc_channel = {
.gpio_num = led_state->gpio_pin,
.speed_mode = LEDC_MODE,
.channel = led_state->ledc_channel,
.timer_sel = led_state->ledc_timer,
.duty = 0, // Initially off
.hpoint = 0
};
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
}
// --- Callback for the auto-stop timer ---
// This function is executed when the auto-stop timer expires.
// It sends a CMD_STOP command to the corresponding LED's command queue.
static void IRAM_ATTR auto_stop_timer_cb(void* arg) {
led_state_t* led_state = (led_state_t*) arg;
ESP_LOGI(TAG, "Auto-stop timer expired for LED on GPIO %d", led_state->gpio_pin);
led_command_t cmd = { .command = CMD_STOP };
// Use xQueueSendFromISR to send the command from an ISR
xQueueSendFromISR(led_state->command_queue, &cmd, NULL);
}
// --- Task to control an LED's PWM based on commands from its queue ---
// Each LED has its own task, ensuring independent operation.
// The task waits for a command and then acts on it.
// It also manages the 10-second auto-stop timer.
static void led_task(void *pvParameter) {
led_state_t *led_state = (led_state_t *)pvParameter;
led_command_t cmd;
// Create the auto-stop timer for this LED
const esp_timer_create_args_t timer_args = {
.callback = &auto_stop_timer_cb,
.arg = led_state,
.name = "auto-stop-timer"
};
ESP_ERROR_CHECK(esp_timer_create(&timer_args, &led_state->auto_stop_timer));
while (1) {
// Wait indefinitely for a command from the queue
if (xQueueReceive(led_state->command_queue, &cmd, portMAX_DELAY) == pdPASS) {
ESP_LOGI(TAG, "Received command for LED on GPIO %d", led_state->gpio_pin);
// Cancel any running auto-stop timer before processing a new command
if (esp_timer_is_active(led_state->auto_stop_timer)) {
esp_timer_stop(led_state->auto_stop_timer);
}
if (cmd.command == CMD_START) {
// If the LED is already on, just restart the timer.
// If it's off, start it with the new frequency.
if (!led_state->is_on || (cmd.frequency > 0 && cmd.frequency != led_state->last_freq)) {
// Start the LED with the new or last saved frequency
int new_freq = (cmd.frequency > 0) ? cmd.frequency : led_state->last_freq;
// Cap the frequency to prevent errors
if (new_freq > MAX_FREQUENCY) {
ESP_LOGW(TAG, "Requested frequency %d is too high, capping at %dHz", new_freq, MAX_FREQUENCY);
new_freq = MAX_FREQUENCY;
}
led_state->last_freq = new_freq;
ESP_LOGI(TAG, "Starting LED on GPIO %d at %dHz", led_state->gpio_pin, new_freq);
ledc_set_freq(LEDC_MODE, led_state->ledc_timer, new_freq);
ledc_set_duty(LEDC_MODE, led_state->ledc_channel, LEDC_DUTY);
ledc_update_duty(LEDC_MODE, led_state->ledc_channel);
led_state->is_on = true;
} else {
ESP_LOGI(TAG, "LED on GPIO %d is already running at %dHz. Resetting timer.", led_state->gpio_pin, led_state->last_freq);
}
// Start or restart the 10-second auto-stop timer
esp_timer_start_once(led_state->auto_stop_timer, AUTO_STOP_TIME_MS * 1000);
} else if (cmd.command == CMD_STOP) {
if (led_state->is_on) {
// Stop the LED
ESP_LOGI(TAG, "Stopping LED on GPIO %d", led_state->gpio_pin);
ledc_set_duty(LEDC_MODE, led_state->ledc_channel, 0);
ledc_update_duty(LEDC_MODE, led_state->ledc_channel);
led_state->is_on = false;
} else {
ESP_LOGI(TAG, "LED on GPIO %d is already off.", led_state->gpio_pin);
}
} else if (cmd.command == CMD_TOGGLE) {
// This command is used by the button handler
if (led_state->is_on) {
led_command_t stop_cmd = { .command = CMD_STOP };
xQueueSend(led_state->command_queue, &stop_cmd, 0);
} else {
led_command_t start_cmd = { .command = CMD_START, .frequency = led_state->last_freq };
xQueueSend(led_state->command_queue, &start_cmd, 0);
}
}
}
}
}
// --- Task to monitor button presses with debouncing ---
// This task handles button presses for a single button and sends a
// CMD_TOGGLE command to the appropriate LED queue.
static void button_task(void *pvParameter) {
gpio_num_t button_pin = (gpio_num_t)pvParameter;
led_state_t *led_state = NULL;
if (button_pin == BTN1_GPIO_PIN) {
led_state = &led1_state;
} else if (button_pin == BTN2_GPIO_PIN) {
led_state = &led2_state;
}
if (led_state == NULL) {
vTaskDelete(NULL); // Should not happen
}
// Configure GPIO for button input with pull-up resistor
gpio_config_t io_conf;
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.pin_bit_mask = (1ULL << button_pin);
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
gpio_config(&io_conf);
// Initial state and time for debouncing
bool last_state = gpio_get_level(button_pin);
TickType_t last_press_time = xTaskGetTickCount();
while (1) {
bool current_state = gpio_get_level(button_pin);
TickType_t current_time = xTaskGetTickCount();
// Check for state change and debounce
if (current_state != last_state) {
// Check if enough time has passed since the last state change
if (current_time - last_press_time > pdMS_TO_TICKS(DEBOUNCE_TIME_MS)) {
if (current_state == 0) { // Button pressed (active low)
ESP_LOGI(TAG, "Button on GPIO %d pressed", button_pin);
led_command_t cmd = { .command = CMD_TOGGLE };
xQueueSend(led_state->command_queue, &cmd, 0);
}
}
// Update last_press_time regardless of debounce check
last_press_time = current_time;
}
last_state = current_state;
vTaskDelay(pdMS_TO_TICKS(10)); // Poll every 10ms
}
}
// --- Task to read and parse JSON commands from UART ---
// This task continuously reads data from the UART,
// parses it as a JSON string, and sends commands to the
// appropriate LED task's queue.
static void uart_task(void *pvParameter) {
uint8_t *data = (uint8_t *)malloc(BUF_SIZE);
if (data == NULL) {
printf("Failed to allocate UART buffer.\n");
vTaskDelete(NULL);
}
// Configure UART parameters
const uart_config_t uart_config = {
.baud_rate = UART_BAUD_RATE,
.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_APB,
};
ESP_ERROR_CHECK(uart_param_config(UART_PORT, &uart_config));
ESP_ERROR_CHECK(uart_driver_install(UART_PORT, BUF_SIZE * 2, 0, 0, NULL, 0));
// --- Main UART loop ---
while (1) {
int len = uart_read_bytes(UART_PORT, data, (BUF_SIZE - 1), 20 / portTICK_PERIOD_MS);
if (len > 0) {
data[len] = '\0'; // Null-terminate the string
printf("Received UART data: %s\n", (char *)data);
cJSON *root = cJSON_Parse((char *)data);
if (root == NULL) {
const char *error_ptr = cJSON_GetErrorPtr();
if (error_ptr != NULL) {
printf("cJSON parse error before: %s\n", error_ptr);
}
continue;
}
cJSON *mod = cJSON_GetObjectItemCaseSensitive(root, "MOD");
cJSON *com = cJSON_GetObjectItemCaseSensitive(root, "COM");
cJSON *freq = cJSON_GetObjectItemCaseSensitive(root, "FREQ");
if (cJSON_IsString(mod) && cJSON_IsString(com)) {
led_command_t cmd;
QueueHandle_t target_queue = NULL;
if (strcmp(mod->valuestring, "LED1") == 0) {
target_queue = led1_state.command_queue;
} else if (strcmp(mod->valuestring, "LED2") == 0) {
target_queue = led2_state.command_queue;
}
if (target_queue != NULL) {
if (strcmp(com->valuestring, "START") == 0) {
cmd.command = CMD_START;
if (cJSON_IsNumber(freq)) {
cmd.frequency = freq->valueint;
} else {
cmd.frequency = 0; // Use last saved frequency
}
xQueueSend(target_queue, &cmd, 0);
} else if (strcmp(com->valuestring, "STOP") == 0) {
cmd.command = CMD_STOP;
xQueueSend(target_queue, &cmd, 0);
} else {
printf("Invalid command received: %s\n", com->valuestring);
}
} else {
printf("Invalid module received: %s\n", mod->valuestring);
}
} else {
printf("JSON format error: MOD or COM not found or not a string.\n");
}
cJSON_Delete(root);
}
}
free(data);
vTaskDelete(NULL);
}
// --- Main application entry point ---
void app_main(void) {
ESP_LOGI(TAG, "Initializing system...");
// Initialize LED state variables
led1_state = (led_state_t){
.gpio_pin = LED1_GPIO_PIN,
.ledc_channel = LEDC_CHANNEL_0,
.ledc_timer = LEDC_TIMER_0,
.last_freq = 10, // Default frequency
.is_on = false,
.command_queue = xQueueCreate(10, sizeof(led_command_t))
};
led2_state = (led_state_t){
.gpio_pin = LED2_GPIO_PIN,
.ledc_channel = LEDC_CHANNEL_1,
.ledc_timer = LEDC_TIMER_1,
.last_freq = 20, // Default frequency
.is_on = false,
.command_queue = xQueueCreate(10, sizeof(led_command_t))
};
if (!led1_state.command_queue || !led2_state.command_queue) {
ESP_LOGE(TAG, "Failed to create command queues.");
return;
}
// Initialize PWM timers before configuring channels
ledc_timer_config_t ledc_timer_config_local;
ledc_timer_config_local = (ledc_timer_config_t){
.speed_mode = LEDC_MODE,
.duty_resolution = LEDC_DUTY_RESOLUTION,
.timer_num = LEDC_TIMER_0,
.freq_hz = 100,
.clk_cfg = LEDC_AUTO_CLK,
};
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer_config_local));
ledc_timer_config_local.timer_num = LEDC_TIMER_1;
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer_config_local));
// Now initialize channels for both LEDs
pwm_init(&led1_state);
pwm_init(&led2_state);
// Create FreeRTOS tasks
xTaskCreate(led_task, "led1_task", 4096, (void *)&led1_state, 5, NULL);
xTaskCreate(led_task, "led2_task", 4096, (void *)&led2_state, 5, NULL);
xTaskCreate(button_task, "btn1_task", 2048, (void *)BTN1_GPIO_PIN, 5, NULL);
xTaskCreate(button_task, "btn2_task", 2048, (void *)BTN2_GPIO_PIN, 5, NULL);
xTaskCreate(uart_task, "uart_task", 4096, NULL, 6, NULL);
}
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "driver/ledc.h"
#include "driver/uart.h"
#include "esp_log.h"
#include "cJSON.h"
#include "esp_timer.h"
// Hardware connection definitions
// GPIO 2 -----> LED1 ----> GND
// GPIO 5 -----> LED2 ----> GND
// GPIO 0 -----> Button1 ----> GND
// GPIO 4 -----> Button2 ----> GND
// UART0 -------> Serial Monitor (Simulated)
// --- Define GPIOs for LEDs and Buttons ---
#define LED1_GPIO_PIN GPIO_NUM_2
#define LED2_GPIO_PIN GPIO_NUM_5
#define BTN1_GPIO_PIN GPIO_NUM_0
#define BTN2_GPIO_PIN GPIO_NUM_4
// --- UART and JSON configuration ---
#define UART_PORT UART_NUM_0
#define UART_BAUD_RATE 115200
#define BUF_SIZE (1024)
// --- PWM configuration ---
#define LEDC_MODE LEDC_LOW_SPEED_MODE
#define LEDC_DUTY_RESOLUTION LEDC_TIMER_13_BIT // 8192 levels
#define LEDC_DUTY 4096 // 50% duty cycle
#define MAX_FREQUENCY 1000 // New define to cap max frequency
// --- Debounce time for buttons ---
#define DEBOUNCE_TIME_MS 20
// --- Auto-stop time in milliseconds ---
#define AUTO_STOP_TIME_MS 10000
// --- Structure for an LED command ---
typedef enum {
CMD_START,
CMD_STOP,
CMD_TOGGLE
} command_t;
typedef struct {
command_t command;
int frequency; // Used only for CMD_START
} led_command_t;
// --- LED State Structure ---
typedef struct {
gpio_num_t gpio_pin;
ledc_channel_t ledc_channel;
ledc_timer_t ledc_timer;
int last_freq;
bool is_on;
esp_timer_handle_t auto_stop_timer;
QueueHandle_t command_queue;
} led_state_t;
// Static variables to hold the state of each LED
static const char *TAG = "MAIN";
static led_state_t led1_state;
static led_state_t led2_state;
// --- Function to initialize the LEDC PWM for a specific LED ---
// This function sets up the timer and channel for a given LED,
// ensuring the PWM is ready to be controlled.
static void pwm_init(led_state_t *led_state) {
ledc_timer_config_t ledc_timer_config_local = {
.speed_mode = LEDC_MODE,
.duty_resolution = LEDC_DUTY_RESOLUTION,
.timer_num = led_state->ledc_timer,
.freq_hz = 100, // Initial frequency, will be updated
.clk_cfg = LEDC_AUTO_CLK,
};
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer_config_local));
ledc_channel_config_t ledc_channel = {
.gpio_num = led_state->gpio_pin,
.speed_mode = LEDC_MODE,
.channel = led_state->ledc_channel,
.timer_sel = led_state->ledc_timer,
.duty = 0, // Initially off
.hpoint = 0
};
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
}
// --- Callback for the auto-stop timer ---
// This function is executed when the auto-stop timer expires.
// It sends a CMD_STOP command to the corresponding LED's command queue.
static void IRAM_ATTR auto_stop_timer_cb(void* arg) {
led_state_t* led_state = (led_state_t*) arg;
// printf("Auto-stop timer expired for LED on GPIO %d\n", led_state->gpio_pin); // No need
led_command_t cmd = { .command = CMD_STOP };
// Use xQueueSendFromISR to send the command from an ISR
xQueueSendFromISR(led_state->command_queue, &cmd, NULL);
}
// --- Task to control an LED's PWM based on commands from its queue ---
// Each LED has its own task, ensuring independent operation.
// The task waits for a command and then acts on it.
// It also manages the 10-second auto-stop timer.
static void led_task(void *pvParameter) {
led_state_t *led_state = (led_state_t *)pvParameter;
led_command_t cmd;
// Create the auto-stop timer for this LED
const esp_timer_create_args_t timer_args = {
.callback = &auto_stop_timer_cb,
.arg = led_state,
.name = "auto-stop-timer"
};
ESP_ERROR_CHECK(esp_timer_create(&timer_args, &led_state->auto_stop_timer));
while (1) {
// Wait indefinitely for a command from the queue
if (xQueueReceive(led_state->command_queue, &cmd, portMAX_DELAY) == pdPASS) {
printf("Received command for LED on GPIO %d\n", led_state->gpio_pin);
// Cancel any running auto-stop timer before processing a new command
if (esp_timer_is_active(led_state->auto_stop_timer)) {
esp_timer_stop(led_state->auto_stop_timer);
}
if (cmd.command == CMD_START) {
// If the LED is already on, just restart the timer.
// If it's off, start it with the new frequency.
if (!led_state->is_on || (cmd.frequency > 0 && cmd.frequency != led_state->last_freq)) {
// Start the LED with the new or last saved frequency
int new_freq = (cmd.frequency > 0) ? cmd.frequency : led_state->last_freq;
// Cap the frequency to prevent errors
if (new_freq > MAX_FREQUENCY) {
printf("Requested frequency %d is too high, capping at %dHz\n", new_freq, MAX_FREQUENCY);
new_freq = MAX_FREQUENCY;
}
led_state->last_freq = new_freq;
printf("Starting LED on GPIO %d at %dHz\n", led_state->gpio_pin, new_freq);
ledc_set_freq(LEDC_MODE, led_state->ledc_timer, new_freq);
ledc_set_duty(LEDC_MODE, led_state->ledc_channel, LEDC_DUTY);
ledc_update_duty(LEDC_MODE, led_state->ledc_channel);
led_state->is_on = true;
} else {
printf("LED on GPIO %d is already running at %dHz. Resetting timer.\n", led_state->gpio_pin, led_state->last_freq);
}
// Start or restart the 10-second auto-stop timer
esp_timer_start_once(led_state->auto_stop_timer, AUTO_STOP_TIME_MS * 1000);
} else if (cmd.command == CMD_STOP) {
if (led_state->is_on) {
// Stop the LED
printf("Stopping LED on GPIO %d\n", led_state->gpio_pin);
ledc_set_duty(LEDC_MODE, led_state->ledc_channel, 0);
ledc_update_duty(LEDC_MODE, led_state->ledc_channel);
led_state->is_on = false;
} else {
printf("LED on GPIO %d is already off.\n", led_state->gpio_pin);
}
} else if (cmd.command == CMD_TOGGLE) {
// This command is used by the button handler
if (led_state->is_on) {
led_command_t stop_cmd = { .command = CMD_STOP };
xQueueSend(led_state->command_queue, &stop_cmd, 0);
} else {
led_command_t start_cmd = { .command = CMD_START, .frequency = led_state->last_freq };
xQueueSend(led_state->command_queue, &start_cmd, 0);
}
}
}
}
}
// --- Task to monitor button presses with debouncing ---
// This task handles button presses for a single button and sends a
// CMD_TOGGLE command to the appropriate LED queue.
static void button_task(void *pvParameter) {
gpio_num_t button_pin = (gpio_num_t)pvParameter;
led_state_t *led_state = NULL;
if (button_pin == BTN1_GPIO_PIN) {
led_state = &led1_state;
} else if (button_pin == BTN2_GPIO_PIN) {
led_state = &led2_state;
}
if (led_state == NULL) {
vTaskDelete(NULL); // Should not happen
}
// Configure GPIO for button input with pull-up resistor
gpio_config_t io_conf;
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.pin_bit_mask = (1ULL << button_pin);
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
gpio_config(&io_conf);
// Initial state and time for debouncing
bool last_state = gpio_get_level(button_pin);
TickType_t last_press_time = xTaskGetTickCount();
while (1) {
bool current_state = gpio_get_level(button_pin);
TickType_t current_time = xTaskGetTickCount();
// Check for state change and debounce
if (current_state != last_state) {
// Check if enough time has passed since the last state change
if (current_time - last_press_time > pdMS_TO_TICKS(DEBOUNCE_TIME_MS)) {
if (current_state == 0) { // Button pressed (active low)
printf("Button on GPIO %d pressed\n", button_pin);
led_command_t cmd = { .command = CMD_TOGGLE };
xQueueSend(led_state->command_queue, &cmd, 0);
}
}
// Update last_press_time regardless of debounce check
last_press_time = current_time;
}
last_state = current_state;
vTaskDelay(pdMS_TO_TICKS(10)); // Poll every 10ms
}
}
// --- Task to read and parse JSON commands from UART ---
// This task continuously reads data from the UART,
// parses it as a JSON string, and sends commands to the
// appropriate LED task's queue.
static void uart_task(void *pvParameter) {
uint8_t *data = (uint8_t *)malloc(BUF_SIZE);
if (data == NULL) {
printf("Failed to allocate UART buffer.\n");
vTaskDelete(NULL);
}
// Configure UART parameters
const uart_config_t uart_config = {
.baud_rate = UART_BAUD_RATE,
.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_APB,
};
ESP_ERROR_CHECK(uart_param_config(UART_PORT, &uart_config));
ESP_ERROR_CHECK(uart_driver_install(UART_PORT, BUF_SIZE * 2, 0, 0, NULL, 0));
// --- Main UART loop ---
while (1) {
int len = uart_read_bytes(UART_PORT, data, (BUF_SIZE - 1), 20 / portTICK_PERIOD_MS);
if (len > 0) {
data[len] = '\0'; // Null-terminate the string
printf("Received UART data: %s\n", (char *)data);
cJSON *root = cJSON_Parse((char *)data);
if (root == NULL) {
const char *error_ptr = cJSON_GetErrorPtr();
if (error_ptr != NULL) {
printf("cJSON parse error before: %s\n", error_ptr);
}
continue;
}
cJSON *mod = cJSON_GetObjectItemCaseSensitive(root, "MOD");
cJSON *com = cJSON_GetObjectItemCaseSensitive(root, "COM");
cJSON *freq = cJSON_GetObjectItemCaseSensitive(root, "FREQ");
if (cJSON_IsString(mod) && cJSON_IsString(com)) {
led_command_t cmd;
QueueHandle_t target_queue = NULL;
if (strcmp(mod->valuestring, "LED1") == 0) {
target_queue = led1_state.command_queue;
} else if (strcmp(mod->valuestring, "LED2") == 0) {
target_queue = led2_state.command_queue;
}
if (target_queue != NULL) {
if (strcmp(com->valuestring, "START") == 0) {
cmd.command = CMD_START;
if (cJSON_IsNumber(freq)) {
cmd.frequency = freq->valueint;
} else {
cmd.frequency = 0; // Use last saved frequency
}
xQueueSend(target_queue, &cmd, 0);
} else if (strcmp(com->valuestring, "STOP") == 0) {
cmd.command = CMD_STOP;
xQueueSend(target_queue, &cmd, 0);
} else {
printf("Invalid command received: %s\n", com->valuestring);
}
} else {
printf("Invalid module received: %s\n", mod->valuestring);
}
} else {
printf("JSON format error: MOD or COM not found or not a string.\n");
}
cJSON_Delete(root);
}
}
free(data);
vTaskDelete(NULL);
}
// --- Main application entry point ---
void app_main(void) {
// printf("Initializing system...\n");
// Initialize LED state variables
led1_state = (led_state_t){
.gpio_pin = LED1_GPIO_PIN,
.ledc_channel = LEDC_CHANNEL_0,
.ledc_timer = LEDC_TIMER_0,
.last_freq = 10, // Default frequency
.is_on = false,
.command_queue = xQueueCreate(10, sizeof(led_command_t))
};
led2_state = (led_state_t){
.gpio_pin = LED2_GPIO_PIN,
.ledc_channel = LEDC_CHANNEL_1,
.ledc_timer = LEDC_TIMER_1,
.last_freq = 20, // Default frequency
.is_on = false,
.command_queue = xQueueCreate(10, sizeof(led_command_t))
};
if (!led1_state.command_queue || !led2_state.command_queue) {
printf("Failed to create command queues.\n");
return;
}
// Initialize PWM timers before configuring channels
ledc_timer_config_t ledc_timer_config_local;
ledc_timer_config_local = (ledc_timer_config_t){
.speed_mode = LEDC_MODE,
.duty_resolution = LEDC_DUTY_RESOLUTION,
.timer_num = LEDC_TIMER_0,
.freq_hz = 100,
.clk_cfg = LEDC_AUTO_CLK,
};
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer_config_local));
ledc_timer_config_local.timer_num = LEDC_TIMER_1;
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer_config_local));
// Now initialize channels for both LEDs
pwm_init(&led1_state);
pwm_init(&led2_state);
// Create FreeRTOS tasks
xTaskCreate(led_task, "led1_task", 4096, (void *)&led1_state, 5, NULL);
xTaskCreate(led_task, "led2_task", 4096, (void *)&led2_state, 5, NULL);
xTaskCreate(button_task, "btn1_task", 2048, (void *)BTN1_GPIO_PIN, 5, NULL);
xTaskCreate(button_task, "btn2_task", 2048, (void *)BTN2_GPIO_PIN, 5, NULL);
xTaskCreate(uart_task, "uart_task", 4096, NULL, 6, NULL);
}