#include "light_driver.h" #include #include "driver/ledc.h" #include "esp_log.h" #include "esp_err.h" #include #include static const char* TAG = "light_driver"; /* tweakable stuff */ static const uint8_t light_gpio = 2; static const uint16_t light_pwm_freq = 65000; static const ledc_timer_bit_t light_pwm_resolution = LEDC_TIMER_10_BIT; static const double light_lowest_duty_pct = 0.05; // lowest duty percentage where the LEDs still light up nice /* no need to tweak this stuff */ static const ledc_channel_t light_ledc_channel = LEDC_CHANNEL_0; static const ledc_mode_t light_ledc_speed_mode = LEDC_LOW_SPEED_MODE; static const uint32_t light_brightness_steps = (1 << light_pwm_resolution) - 1; /* global vars */ static bool light_init_done = false; static uint8_t light_current_level = 0; static uint16_t gamma_lut[ZIGBEE_LIGHT_LEVELS + 1]; esp_err_t light_driver_set_level(const uint8_t level, const uint16_t transition_ms) { if (!light_init_done) { ESP_LOGW(TAG, "light driver not ready!"); return ESP_FAIL; } light_current_level = level; const uint16_t duty = gamma_lut[light_current_level]; ESP_ERROR_CHECK(ledc_fade_stop(light_ledc_speed_mode, light_ledc_channel)); ESP_ERROR_CHECK(ledc_set_fade_with_time(light_ledc_speed_mode, light_ledc_channel, duty, transition_ms)); ESP_ERROR_CHECK(ledc_fade_start(light_ledc_speed_mode, light_ledc_channel, LEDC_FADE_NO_WAIT)); // ESP_LOGI(TAG, "led set to %d, max is %d", duty, light_brightness_steps); return ESP_OK; } esp_err_t light_driver_set_power(const bool power, const uint16_t transition_ms) { return light_driver_set_level(power ? light_current_level : 0, transition_ms); } esp_err_t light_driver_init(const bool default_state) { const ledc_timer_config_t ledc_timer = { .speed_mode = light_ledc_speed_mode, .duty_resolution = light_pwm_resolution, .timer_num = LEDC_TIMER_2, .freq_hz = light_pwm_freq, .clk_cfg = LEDC_AUTO_CLK }; const ledc_channel_config_t ledc_channel = { .speed_mode = ledc_timer.speed_mode, .channel = light_ledc_channel, .timer_sel = ledc_timer.timer_num, .intr_type = LEDC_INTR_DISABLE, .gpio_num = light_gpio, .duty = 0, // Set duty to 0% .hpoint = 0 }; for (uint8_t i = 0; i <= ZIGBEE_LIGHT_LEVELS; i++) { const double brightness_pct = (double)i / (double)ZIGBEE_LIGHT_LEVELS; const double gamma_corrected = pow(brightness_pct, 2.2); const double scaled = (double)light_brightness_steps * gamma_corrected * (1 - light_lowest_duty_pct); gamma_lut[i] = (uint16_t)scaled + (uint16_t)((double)light_brightness_steps * light_lowest_duty_pct) + 1; } gamma_lut[0] = 0; // 0 is always completely off if (gamma_lut[ZIGBEE_LIGHT_LEVELS] > light_brightness_steps) gamma_lut[ZIGBEE_LIGHT_LEVELS] = light_brightness_steps; ESP_LOGI(TAG, "pre-computed brightness lookup table"); ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer)); ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel)); ESP_ERROR_CHECK(ledc_fade_func_install(0)); ESP_LOGI(TAG, "ledc driver initialized, f=%d Hz, %d step resolution", light_pwm_freq, light_brightness_steps); light_init_done = true; light_driver_set_power(default_state, 100); return ESP_OK; }