diff --git a/esphome/components/ledc/ledc_output.cpp b/esphome/components/ledc/ledc_output.cpp index 34cf4cd1f6..d6241dd5b0 100644 --- a/esphome/components/ledc/ledc_output.cpp +++ b/esphome/components/ledc/ledc_output.cpp @@ -8,6 +8,20 @@ #endif #include +#define CLOCK_FREQUENCY 80e6f + +#ifdef USE_ARDUINO +#ifdef SOC_LEDC_SUPPORT_XTAL_CLOCK +#undef CLOCK_FREQUENCY +// starting with ESP32 Arduino 2.0.2, the 40MHz crystal is used as clock by default if supported +#define CLOCK_FREQUENCY 40e6f +#endif +#else +#define DEFAULT_CLK LEDC_USE_APB_CLK +#endif + +static const uint8_t SETUP_ATTEMPT_COUNT_MAX = 5; + namespace esphome { namespace ledc { @@ -26,11 +40,11 @@ inline ledc_mode_t get_speed_mode(uint8_t) { return LEDC_LOW_SPEED_MODE; } #endif #endif -float ledc_max_frequency_for_bit_depth(uint8_t bit_depth) { return 80e6f / float(1 << bit_depth); } +float ledc_max_frequency_for_bit_depth(uint8_t bit_depth) { return CLOCK_FREQUENCY / float(1 << bit_depth); } float ledc_min_frequency_for_bit_depth(uint8_t bit_depth, bool low_frequency) { const float max_div_num = ((1 << MAX_RES_BITS) - 1) / (low_frequency ? 32.0f : 256.0f); - return 80e6f / (max_div_num * float(1 << bit_depth)); + return CLOCK_FREQUENCY / (max_div_num * float(1 << bit_depth)); } optional ledc_bit_depth_for_frequency(float frequency) { @@ -46,6 +60,38 @@ optional ledc_bit_depth_for_frequency(float frequency) { return {}; } +#ifdef USE_ESP_IDF +esp_err_t configure_timer_frequency(ledc_mode_t speed_mode, ledc_timer_t timer_num, ledc_channel_t chan_num, + uint8_t channel, uint8_t &bit_depth, float frequency) { + bit_depth = *ledc_bit_depth_for_frequency(frequency); + if (bit_depth < 1) { + ESP_LOGE(TAG, "Frequency %f can't be achieved with any bit depth", frequency); + } + + ledc_timer_config_t timer_conf{}; + timer_conf.speed_mode = speed_mode; + timer_conf.duty_resolution = static_cast(bit_depth); + timer_conf.timer_num = timer_num; + timer_conf.freq_hz = (uint32_t) frequency; + timer_conf.clk_cfg = DEFAULT_CLK; + + // Configure the time with fallback in case of error + int attempt_count_max = SETUP_ATTEMPT_COUNT_MAX; + esp_err_t init_result = ESP_FAIL; + while (attempt_count_max > 0 && init_result != ESP_OK) { + init_result = ledc_timer_config(&timer_conf); + if (init_result != ESP_OK) { + ESP_LOGW(TAG, "Unable to initialize timer with frequency %.1f and bit depth of %u", frequency, bit_depth); + // try again with a lower bit depth + timer_conf.duty_resolution = static_cast(--bit_depth); + } + attempt_count_max--; + } + + return init_result; +} +#endif + void LEDCOutput::write_state(float state) { if (!initialized_) { ESP_LOGW(TAG, "LEDC output hasn't been initialized yet!"); @@ -61,6 +107,7 @@ void LEDCOutput::write_state(float state) { auto duty = static_cast(duty_rounded); #ifdef USE_ARDUINO + ESP_LOGV(TAG, "Setting duty: %u on channel %u", duty, this->channel_); ledcWrite(this->channel_, duty); #endif #ifdef USE_ESP_IDF @@ -72,6 +119,7 @@ void LEDCOutput::write_state(float state) { } void LEDCOutput::setup() { + ESP_LOGV(TAG, "Entering setup..."); #ifdef USE_ARDUINO this->update_frequency(this->frequency_); this->turn_off(); @@ -83,19 +131,16 @@ void LEDCOutput::setup() { auto timer_num = static_cast((channel_ % 8) / 2); auto chan_num = static_cast(channel_ % 8); - bit_depth_ = *ledc_bit_depth_for_frequency(frequency_); - if (bit_depth_ < 1) { - ESP_LOGW(TAG, "Frequency %f can't be achieved with any bit depth", frequency_); - this->status_set_warning(); + esp_err_t timer_init_result = + configure_timer_frequency(speed_mode, timer_num, chan_num, this->channel_, this->bit_depth_, this->frequency_); + + if (timer_init_result != ESP_OK) { + ESP_LOGE(TAG, "Frequency %f can't be achieved with computed bit depth %u", this->frequency_, this->bit_depth_); + this->status_set_error(); + return; } - ledc_timer_config_t timer_conf{}; - timer_conf.speed_mode = speed_mode; - timer_conf.duty_resolution = static_cast(bit_depth_); - timer_conf.timer_num = timer_num; - timer_conf.freq_hz = (uint32_t) frequency_; - timer_conf.clk_cfg = LEDC_AUTO_CLK; - ledc_timer_config(&timer_conf); + ESP_LOGV(TAG, "Configured frequency %f with a bit depth of %u bits", this->frequency_, this->bit_depth_); ledc_channel_config_t chan_conf{}; chan_conf.gpio_num = pin_->get_pin(); @@ -107,6 +152,7 @@ void LEDCOutput::setup() { chan_conf.hpoint = 0; ledc_channel_config(&chan_conf); initialized_ = true; + this->status_clear_error(); #endif } @@ -114,36 +160,80 @@ void LEDCOutput::dump_config() { ESP_LOGCONFIG(TAG, "LEDC Output:"); LOG_PIN(" Pin ", this->pin_); ESP_LOGCONFIG(TAG, " LEDC Channel: %u", this->channel_); - ESP_LOGCONFIG(TAG, " Frequency: %.1f Hz", this->frequency_); + ESP_LOGCONFIG(TAG, " PWM Frequency: %.1f Hz", this->frequency_); + ESP_LOGCONFIG(TAG, " Bit depth: %u", this->bit_depth_); + ESP_LOGV(TAG, " Max frequency for bit depth: %f", ledc_max_frequency_for_bit_depth(this->bit_depth_)); + ESP_LOGV(TAG, " Min frequency for bit depth: %f", + ledc_min_frequency_for_bit_depth(this->bit_depth_, (this->frequency_ < 100))); + ESP_LOGV(TAG, " Max frequency for bit depth-1: %f", ledc_max_frequency_for_bit_depth(this->bit_depth_ - 1)); + ESP_LOGV(TAG, " Min frequency for bit depth-1: %f", + ledc_min_frequency_for_bit_depth(this->bit_depth_ - 1, (this->frequency_ < 100))); + ESP_LOGV(TAG, " Max frequency for bit depth+1: %f", ledc_max_frequency_for_bit_depth(this->bit_depth_ + 1)); + ESP_LOGV(TAG, " Min frequency for bit depth+1: %f", + ledc_min_frequency_for_bit_depth(this->bit_depth_ + 1, (this->frequency_ < 100))); + ESP_LOGV(TAG, " Max res bits: %d", MAX_RES_BITS); + ESP_LOGV(TAG, " Clock frequency: %f", CLOCK_FREQUENCY); } void LEDCOutput::update_frequency(float frequency) { auto bit_depth_opt = ledc_bit_depth_for_frequency(frequency); if (!bit_depth_opt.has_value()) { - ESP_LOGW(TAG, "Frequency %f can't be achieved with any bit depth", frequency); - this->status_set_warning(); + ESP_LOGE(TAG, "Frequency %f can't be achieved with any bit depth", this->frequency_); + this->status_set_error(); } this->bit_depth_ = bit_depth_opt.value_or(8); this->frequency_ = frequency; #ifdef USE_ARDUINO - ledcSetup(this->channel_, frequency, this->bit_depth_); - initialized_ = true; + ESP_LOGV(TAG, "Using Arduino API - Trying to define channel, frequency and bit depth..."); + u_int32_t configured_frequency = 0; + + // Configure LEDC channel, frequency and bit depth with fallback + int attempt_count_max = SETUP_ATTEMPT_COUNT_MAX; + while (attempt_count_max > 0 && configured_frequency == 0) { + ESP_LOGV(TAG, "Trying initialize channel %u with frequency %.1f and bit depth of %u...", this->channel_, + this->frequency_, this->bit_depth_); + configured_frequency = ledcSetup(this->channel_, frequency, this->bit_depth_); + if (configured_frequency != 0) { + initialized_ = true; + this->status_clear_error(); + ESP_LOGV(TAG, "Configured frequency: %u with bit depth: %u", configured_frequency, this->bit_depth_); + } else { + ESP_LOGW(TAG, "Unable to initialize channel %u with frequency %.1f and bit depth of %u", this->channel_, + this->frequency_, this->bit_depth_); + // try again with a lower bit depth + this->bit_depth_--; + } + attempt_count_max--; + } + + if (configured_frequency == 0) { + ESP_LOGE(TAG, "Permanently failed to initialize channel %u with frequency %.1f and bit depth of %u", this->channel_, + this->frequency_, this->bit_depth_); + this->status_set_error(); + return; + } + #endif // USE_ARDUINO #ifdef USE_ESP_IDF if (!initialized_) { ESP_LOGW(TAG, "LEDC output hasn't been initialized yet!"); return; } + auto speed_mode = get_speed_mode(channel_); auto timer_num = static_cast((channel_ % 8) / 2); + auto chan_num = static_cast(channel_ % 8); - ledc_timer_config_t timer_conf{}; - timer_conf.speed_mode = speed_mode; - timer_conf.duty_resolution = static_cast(bit_depth_); - timer_conf.timer_num = timer_num; - timer_conf.freq_hz = (uint32_t) frequency_; - timer_conf.clk_cfg = LEDC_AUTO_CLK; - ledc_timer_config(&timer_conf); + esp_err_t timer_init_result = + configure_timer_frequency(speed_mode, timer_num, chan_num, this->channel_, this->bit_depth_, this->frequency_); + + if (timer_init_result != ESP_OK) { + ESP_LOGE(TAG, "Frequency %f can't be achieved with computed bit depth %u", this->frequency_, this->bit_depth_); + this->status_set_error(); + return; + } + + this->status_clear_error(); #endif // re-apply duty this->write_state(this->duty_); diff --git a/esphome/components/midea/air_conditioner.cpp b/esphome/components/midea/air_conditioner.cpp index 1ad5ade53d..b5bf43b64f 100644 --- a/esphome/components/midea/air_conditioner.cpp +++ b/esphome/components/midea/air_conditioner.cpp @@ -84,18 +84,18 @@ ClimateTraits AirConditioner::traits() { traits.set_supported_custom_presets(this->supported_custom_presets_); traits.set_supported_custom_fan_modes(this->supported_custom_fan_modes_); /* + MINIMAL SET OF CAPABILITIES */ - traits.add_supported_mode(ClimateMode::CLIMATE_MODE_OFF); - traits.add_supported_mode(ClimateMode::CLIMATE_MODE_FAN_ONLY); traits.add_supported_fan_mode(ClimateFanMode::CLIMATE_FAN_AUTO); traits.add_supported_fan_mode(ClimateFanMode::CLIMATE_FAN_LOW); traits.add_supported_fan_mode(ClimateFanMode::CLIMATE_FAN_MEDIUM); traits.add_supported_fan_mode(ClimateFanMode::CLIMATE_FAN_HIGH); - traits.add_supported_swing_mode(ClimateSwingMode::CLIMATE_SWING_OFF); - traits.add_supported_swing_mode(ClimateSwingMode::CLIMATE_SWING_VERTICAL); - traits.add_supported_preset(ClimatePreset::CLIMATE_PRESET_NONE); - traits.add_supported_preset(ClimatePreset::CLIMATE_PRESET_SLEEP); if (this->base_.getAutoconfStatus() == dudanov::midea::AUTOCONF_OK) Converters::to_climate_traits(traits, this->base_.getCapabilities()); + if (!traits.get_supported_modes().empty()) + traits.add_supported_mode(ClimateMode::CLIMATE_MODE_OFF); + if (!traits.get_supported_swing_modes().empty()) + traits.add_supported_swing_mode(ClimateSwingMode::CLIMATE_SWING_OFF); + if (!traits.get_supported_presets().empty()) + traits.add_supported_preset(ClimatePreset::CLIMATE_PRESET_NONE); return traits; } diff --git a/esphome/const.py b/esphome/const.py index d62c4689ad..ce3eac9ed6 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.2.0b4" +__version__ = "2023.2.0b5" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index 0ff5362670..f7d471586d 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -545,6 +545,14 @@ class DownloadBinaryRequestHandler(BaseHandler): self.finish() +class EsphomeVersionHandler(BaseHandler): + @authenticated + def get(self): + self.set_header("Content-Type", "application/json") + self.write(json.dumps({"version": const.__version__})) + self.finish() + + def _list_dashboard_entries(): files = settings.list_yaml_files() return [DashboardEntry(file) for file in files] @@ -1000,8 +1008,14 @@ class SafeLoaderIgnoreUnknown(yaml.SafeLoader): def ignore_unknown(self, node): return f"{node.tag} {node.value}" + def construct_yaml_binary(self, node) -> str: + return super().construct_yaml_binary(node).decode("ascii") + SafeLoaderIgnoreUnknown.add_constructor(None, SafeLoaderIgnoreUnknown.ignore_unknown) +SafeLoaderIgnoreUnknown.add_constructor( + "tag:yaml.org,2002:binary", SafeLoaderIgnoreUnknown.construct_yaml_binary +) class JsonConfigRequestHandler(BaseHandler): @@ -1135,6 +1149,7 @@ def make_app(debug=get_bool_env(ENV_DEV)): (f"{rel}rename", EsphomeRenameHandler), (f"{rel}prometheus-sd", PrometheusServiceDiscoveryHandler), (f"{rel}boards/([a-z0-9]+)", BoardsRequestHandler), + (f"{rel}version", EsphomeVersionHandler), ], **app_settings, ) diff --git a/requirements.txt b/requirements.txt index 9d59e0a1d2..9f4d34528c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ pyserial==3.5 platformio==6.1.5 # When updating platformio, also update Dockerfile esptool==4.4 click==8.1.3 -esphome-dashboard==20230120.0 +esphome-dashboard==20230214.0 aioesphomeapi==13.1.0 zeroconf==0.47.1