#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);
}