From 302ba2874e12960c76a624fa6066bf5df1ffac2e Mon Sep 17 00:00:00 2001 From: Satoshi YAMADA Date: Tue, 29 Oct 2024 12:08:08 +0900 Subject: [PATCH] Support W5500 SPI-Ethernet polling mode if framework is supported (#7503) --- esphome/components/ethernet/__init__.py | 56 ++++++++++++++++++- .../ethernet/ethernet_component.cpp | 15 ++++- .../components/ethernet/ethernet_component.h | 8 ++- esphome/const.py | 1 + 4 files changed, 77 insertions(+), 3 deletions(-) diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index 475d60df53..dca37b8dc2 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -1,3 +1,4 @@ +import logging from esphome import pins import esphome.codegen as cg from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant @@ -23,6 +24,7 @@ from esphome.const import ( CONF_MISO_PIN, CONF_MOSI_PIN, CONF_PAGE_ID, + CONF_POLLING_INTERVAL, CONF_RESET_PIN, CONF_SPI, CONF_STATIC_IP, @@ -30,13 +32,16 @@ from esphome.const import ( CONF_TYPE, CONF_USE_ADDRESS, CONF_VALUE, + KEY_CORE, + KEY_FRAMEWORK_VERSION, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, TimePeriodMilliseconds, coroutine_with_priority import esphome.final_validate as fv CONFLICTS_WITH = ["wifi"] DEPENDENCIES = ["esp32"] AUTO_LOAD = ["network"] +LOGGER = logging.getLogger(__name__) ethernet_ns = cg.esphome_ns.namespace("ethernet") PHYRegister = ethernet_ns.struct("PHYRegister") @@ -63,6 +68,7 @@ ETHERNET_TYPES = { } SPI_ETHERNET_TYPES = ["W5500"] +SPI_ETHERNET_DEFAULT_POLLING_INTERVAL = TimePeriodMilliseconds(milliseconds=10) emac_rmii_clock_mode_t = cg.global_ns.enum("emac_rmii_clock_mode_t") emac_rmii_clock_gpio_t = cg.global_ns.enum("emac_rmii_clock_gpio_t") @@ -100,6 +106,24 @@ EthernetComponent = ethernet_ns.class_("EthernetComponent", cg.Component) ManualIP = ethernet_ns.struct("ManualIP") +def _is_framework_spi_polling_mode_supported(): + # SPI Ethernet without IRQ feature is added in + # esp-idf >= (5.3+ ,5.2.1+, 5.1.4) and arduino-esp32 >= 3.0.0 + framework_version = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] + if CORE.using_esp_idf: + if framework_version >= cv.Version(5, 3, 0): + return True + if cv.Version(5, 3, 0) > framework_version >= cv.Version(5, 2, 1): + return True + if cv.Version(5, 2, 0) > framework_version >= cv.Version(5, 1, 4): + return True + return False + if CORE.using_arduino: + return framework_version >= cv.Version(3, 0, 0) + # fail safe: Unknown framework + return False + + def _validate(config): if CONF_USE_ADDRESS not in config: if CONF_MANUAL_IP in config: @@ -107,6 +131,27 @@ def _validate(config): else: use_address = CORE.name + config[CONF_DOMAIN] config[CONF_USE_ADDRESS] = use_address + if config[CONF_TYPE] in SPI_ETHERNET_TYPES: + if _is_framework_spi_polling_mode_supported(): + if CONF_POLLING_INTERVAL in config and CONF_INTERRUPT_PIN in config: + raise cv.Invalid( + f"Cannot specify more than one of {CONF_INTERRUPT_PIN}, {CONF_POLLING_INTERVAL}" + ) + if CONF_POLLING_INTERVAL not in config and CONF_INTERRUPT_PIN not in config: + config[CONF_POLLING_INTERVAL] = SPI_ETHERNET_DEFAULT_POLLING_INTERVAL + else: + if CONF_POLLING_INTERVAL in config: + raise cv.Invalid( + "In this version of the framework " + f"({CORE.target_framework} {CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION]}), " + f"'{CONF_POLLING_INTERVAL}' is not supported." + ) + if CONF_INTERRUPT_PIN not in config: + raise cv.Invalid( + "In this version of the framework " + f"({CORE.target_framework} {CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION]}), " + f"'{CONF_INTERRUPT_PIN}' is a required option for [ethernet]." + ) return config @@ -157,6 +202,11 @@ SPI_SCHEMA = BASE_SCHEMA.extend( cv.Optional(CONF_CLOCK_SPEED, default="26.67MHz"): cv.All( cv.frequency, cv.int_range(int(8e6), int(80e6)) ), + # Set default value (SPI_ETHERNET_DEFAULT_POLLING_INTERVAL) at _validate() + cv.Optional(CONF_POLLING_INTERVAL): cv.All( + cv.positive_time_period_milliseconds, + cv.Range(min=TimePeriodMilliseconds(milliseconds=1)), + ), } ), ) @@ -234,6 +284,10 @@ async def to_code(config): cg.add(var.set_cs_pin(config[CONF_CS_PIN])) if CONF_INTERRUPT_PIN in config: cg.add(var.set_interrupt_pin(config[CONF_INTERRUPT_PIN])) + else: + cg.add(var.set_polling_interval(config[CONF_POLLING_INTERVAL])) + if _is_framework_spi_polling_mode_supported(): + cg.add_define("USE_ETHERNET_SPI_POLLING_SUPPORT") if CONF_RESET_PIN in config: cg.add(var.set_reset_pin(config[CONF_RESET_PIN])) cg.add(var.set_clock_speed(config[CONF_CLOCK_SPEED])) diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 00c7ae4ab8..08f5fa6642 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -116,6 +116,9 @@ void EthernetComponent::setup() { eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(spi_handle); #endif w5500_config.int_gpio_num = this->interrupt_pin_; +#ifdef USE_ETHERNET_SPI_POLLING_SUPPORT + w5500_config.poll_period_ms = this->polling_interval_; +#endif phy_config.phy_addr = this->phy_addr_spi_; phy_config.reset_gpio_num = this->reset_pin_; @@ -327,7 +330,14 @@ void EthernetComponent::dump_config() { ESP_LOGCONFIG(TAG, " MISO Pin: %u", this->miso_pin_); ESP_LOGCONFIG(TAG, " MOSI Pin: %u", this->mosi_pin_); ESP_LOGCONFIG(TAG, " CS Pin: %u", this->cs_pin_); - ESP_LOGCONFIG(TAG, " IRQ Pin: %u", this->interrupt_pin_); +#ifdef USE_ETHERNET_SPI_POLLING_SUPPORT + if (this->polling_interval_ != 0) { + ESP_LOGCONFIG(TAG, " Polling Interval: %lu ms", this->polling_interval_); + } else +#endif + { + ESP_LOGCONFIG(TAG, " IRQ Pin: %d", this->interrupt_pin_); + } ESP_LOGCONFIG(TAG, " Reset Pin: %d", this->reset_pin_); ESP_LOGCONFIG(TAG, " Clock Speed: %d MHz", this->clock_speed_ / 1000000); #else @@ -536,6 +546,9 @@ void EthernetComponent::set_cs_pin(uint8_t cs_pin) { this->cs_pin_ = cs_pin; } void EthernetComponent::set_interrupt_pin(uint8_t interrupt_pin) { this->interrupt_pin_ = interrupt_pin; } void EthernetComponent::set_reset_pin(uint8_t reset_pin) { this->reset_pin_ = reset_pin; } void EthernetComponent::set_clock_speed(int clock_speed) { this->clock_speed_ = clock_speed; } +#ifdef USE_ETHERNET_SPI_POLLING_SUPPORT +void EthernetComponent::set_polling_interval(uint32_t polling_interval) { this->polling_interval_ = polling_interval; } +#endif #else void EthernetComponent::set_phy_addr(uint8_t phy_addr) { this->phy_addr_ = phy_addr; } void EthernetComponent::set_power_pin(int power_pin) { this->power_pin_ = power_pin; } diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index 5ee430c046..fb178431d5 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -67,6 +67,9 @@ class EthernetComponent : public Component { void set_interrupt_pin(uint8_t interrupt_pin); void set_reset_pin(uint8_t reset_pin); void set_clock_speed(int clock_speed); +#ifdef USE_ETHERNET_SPI_POLLING_SUPPORT + void set_polling_interval(uint32_t polling_interval); +#endif #else void set_phy_addr(uint8_t phy_addr); void set_power_pin(int power_pin); @@ -108,10 +111,13 @@ class EthernetComponent : public Component { uint8_t miso_pin_; uint8_t mosi_pin_; uint8_t cs_pin_; - uint8_t interrupt_pin_; + int interrupt_pin_{-1}; int reset_pin_{-1}; int phy_addr_spi_{-1}; int clock_speed_; +#ifdef USE_ETHERNET_SPI_POLLING_SUPPORT + uint32_t polling_interval_{0}; +#endif #else uint8_t phy_addr_{0}; int power_pin_{-1}; diff --git a/esphome/const.py b/esphome/const.py index 54ebb2815f..c39061631b 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -666,6 +666,7 @@ CONF_PMC_1_0 = "pmc_1_0" CONF_PMC_10_0 = "pmc_10_0" CONF_PMC_2_5 = "pmc_2_5" CONF_PMC_4_0 = "pmc_4_0" +CONF_POLLING_INTERVAL = "polling_interval" CONF_PORT = "port" CONF_POSITION = "position" CONF_POSITION_ACTION = "position_action"