#include

#include "freertos/FreeRTOS.h"

#include "freertos/task.h"

#include "driver/gpio.h"

#include "driver/uart.h"

#include "esp_intr_alloc.h"

#include "esp_rom_sys.h"
 

#define RELAY_GPIO     23   // Relay IN

#define TRIAC_GPIO     19   // MOC3021 input

#define ZC_GPIO        34   // Zero cross detector (H11AA1 OUT)
 

#define UART_PORT      UART_NUM_1

#define BUF_SIZE       128
 

static int brightness = 0;   // 0–100%

static int relay_state = 0;  // 0=OFF, 1=ON
 

//--------------------------------------------------

// UART init for USB-TTL (CH340/CP2102, etc.)

//--------------------------------------------------

void uart_init() {

    const uart_config_t uart_config = {

        .baud_rate = 9600,

        .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_PORT, BUF_SIZE * 2, 0, 0, NULL, 0);

    uart_param_config(UART_PORT, &uart_config);
 

    // Use UART1 with GPIO17 (TX) and GPIO16 (RX)

    uart_set_pin(UART_PORT, 17, 16, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);

}
 

//--------------------------------------------------

// Handle UART frames: 0x02 [cmd] [val] 0x03

//--------------------------------------------------

void uart_task(void *arg) {

    uint8_t data[BUF_SIZE];

    while (1) {

        int len = uart_read_bytes(UART_PORT, data, BUF_SIZE, 20 / portTICK_PERIOD_MS);

        if (len >= 4 && data[0] == 0x02 && data[3] == 0x03) {

            uint8_t cmd = data[1];

            uint8_t val = data[2];
 

            if (cmd == 0x01) { // Relay control

                relay_state = (val > 0) ? 1 : 0;

                gpio_set_level(RELAY_GPIO, relay_state);

            }

            else if (cmd == 0x02) { // Brightness control

                if (val > 100) val = 100;

                brightness = val;

            }
 

            // Echo back to PC (ACK)

            uart_write_bytes(UART_PORT, (const char*)data, 4);

        }

    }

}
 

//--------------------------------------------------

// Send status every second: 0x02 [relay] [brightness] 0x03

//--------------------------------------------------

void status_task(void *arg) {

    uint8_t frame[4];

    while (1) {

        frame[0] = 0x02;

        frame[1] = (uint8_t)relay_state;

        frame[2] = (uint8_t)brightness;

        frame[3] = 0x03;
 

        uart_write_bytes(UART_PORT, (const char*)frame, 4);
 

        vTaskDelay(1000 / portTICK_PERIOD_MS); // every 1 sec

    }

}
 

//--------------------------------------------------

// Zero-cross interrupt: control TRIAC firing angle

//--------------------------------------------------

static void IRAM_ATTR zc_isr_handler(void* arg) {

    if (!relay_state) return; // Skip if relay OFF
 

    int delay_us = (100 - brightness) * 100;

    esp_rom_delay_us(delay_us);
 

    gpio_set_level(TRIAC_GPIO, 1);

    esp_rom_delay_us(100); // Gate pulse ~100us

    gpio_set_level(TRIAC_GPIO, 0);

}
 

//--------------------------------------------------

// Main Application

//--------------------------------------------------

void app_main(void) {

    // Relay GPIO setup

    gpio_reset_pin(RELAY_GPIO);

    gpio_set_direction(RELAY_GPIO, GPIO_MODE_OUTPUT);

    gpio_set_level(RELAY_GPIO, 0);
 

    // TRIAC GPIO setup

    gpio_reset_pin(TRIAC_GPIO);

    gpio_set_direction(TRIAC_GPIO, GPIO_MODE_OUTPUT);

    gpio_set_level(TRIAC_GPIO, 0);
 

    // Zero cross input

    gpio_config_t io_conf = {

        .intr_type = GPIO_INTR_NEGEDGE,

        .mode = GPIO_MODE_INPUT,

        .pin_bit_mask = (1ULL << ZC_GPIO),

        .pull_up_en = GPIO_PULLUP_DISABLE,

        .pull_down_en = GPIO_PULLDOWN_DISABLE

    };

    gpio_config(&io_conf);
 

    gpio_install_isr_service(0);

    gpio_isr_handler_add(ZC_GPIO, zc_isr_handler, NULL);
 

    // Init UART for PC communication

    uart_init();
 

    // Start UART RX handler

    xTaskCreate(uart_task, "uart_task", 4096, NULL, 5, NULL);
 

    // Start periodic status sender

    xTaskCreate(status_task, "status_task", 2048, NULL, 5, NULL);

}