From 280f4618f237dcf7b8a32ae6e7ff6dc9d3078539 Mon Sep 17 00:00:00 2001 From: Jeroen van Oort Date: Sat, 4 Nov 2023 19:22:22 +0100 Subject: [PATCH 1/7] Added support for SPI based W5500 ethernet --- esphome/components/ethernet/__init__.py | 96 ++++++++++++--- .../ethernet/ethernet_component.cpp | 116 +++++++++++++++++- .../components/ethernet/ethernet_component.h | 26 +++- tests/test12.yaml | 34 +++++ tests/test4.yaml | 24 ++-- 5 files changed, 264 insertions(+), 32 deletions(-) create mode 100644 tests/test12.yaml diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index 6f0f3741dd5..d8dcc4c6d84 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -1,6 +1,7 @@ from esphome import pins import esphome.config_validation as cv import esphome.codegen as cg +from esphome.components.esp32 import add_idf_sdkconfig_option from esphome.const import ( CONF_DOMAIN, CONF_ID, @@ -12,6 +13,12 @@ CONF_SUBNET, CONF_DNS1, CONF_DNS2, + CONF_CLK_PIN, + CONF_MISO_PIN, + CONF_MOSI_PIN, + CONF_CS_PIN, + CONF_INTERRUPT_PIN, + CONF_RESET_PIN, ) from esphome.core import CORE, coroutine_with_priority from esphome.components.network import IPAddress @@ -27,6 +34,8 @@ CONF_CLK_MODE = "clk_mode" CONF_POWER_PIN = "power_pin" +CONF_CLOCK_SPEED = "clock_speed" + EthernetType = ethernet_ns.enum("EthernetType") ETHERNET_TYPES = { "LAN8720": EthernetType.ETHERNET_TYPE_LAN8720, @@ -36,6 +45,7 @@ "JL1101": EthernetType.ETHERNET_TYPE_JL1101, "KSZ8081": EthernetType.ETHERNET_TYPE_KSZ8081, "KSZ8081RNA": EthernetType.ETHERNET_TYPE_KSZ8081RNA, + "W5500": EthernetType.ETHERNET_TYPE_W5500, } emac_rmii_clock_mode_t = cg.global_ns.enum("emac_rmii_clock_mode_t") @@ -84,11 +94,22 @@ def _validate(config): return config -CONFIG_SCHEMA = cv.All( +BASE_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(EthernetComponent), + cv.Optional(CONF_MANUAL_IP): MANUAL_IP_SCHEMA, + cv.Optional(CONF_DOMAIN, default=".local"): cv.domain_name, + cv.Optional(CONF_USE_ADDRESS): cv.string_strict, + cv.Optional("enable_mdns"): cv.invalid( + "This option has been removed. Please use the [disabled] option under the " + "new mdns component instead." + ), + } +).extend(cv.COMPONENT_SCHEMA) + +RMII_SCHEMA = BASE_SCHEMA.extend( cv.Schema( { - cv.GenerateID(): cv.declare_id(EthernetComponent), - cv.Required(CONF_TYPE): cv.enum(ETHERNET_TYPES, upper=True), cv.Required(CONF_MDC_PIN): pins.internal_gpio_output_pin_number, cv.Required(CONF_MDIO_PIN): pins.internal_gpio_output_pin_number, cv.Optional(CONF_CLK_MODE, default="GPIO0_IN"): cv.enum( @@ -96,15 +117,36 @@ def _validate(config): ), cv.Optional(CONF_PHY_ADDR, default=0): cv.int_range(min=0, max=31), cv.Optional(CONF_POWER_PIN): pins.internal_gpio_output_pin_number, - cv.Optional(CONF_MANUAL_IP): MANUAL_IP_SCHEMA, - cv.Optional(CONF_DOMAIN, default=".local"): cv.domain_name, - cv.Optional(CONF_USE_ADDRESS): cv.string_strict, - cv.Optional("enable_mdns"): cv.invalid( - "This option has been removed. Please use the [disabled] option under the " - "new mdns component instead." - ), } - ).extend(cv.COMPONENT_SCHEMA), + ) +) + +SPI_SCHEMA = BASE_SCHEMA.extend( + cv.Schema( + { + cv.Required(CONF_CLK_PIN): pins.internal_gpio_output_pin_number, + cv.Required(CONF_MISO_PIN): pins.internal_gpio_input_pin_number, + cv.Required(CONF_MOSI_PIN): pins.internal_gpio_output_pin_number, + cv.Required(CONF_CS_PIN): pins.internal_gpio_output_pin_number, + cv.Optional(CONF_INTERRUPT_PIN): pins.internal_gpio_input_pin_number, + cv.Optional(CONF_RESET_PIN): pins.internal_gpio_output_pin_number, + cv.Optional(CONF_CLOCK_SPEED, default=30): cv.int_range(1, 80), # type: ignore[arg-type] + } + ), +) + +CONFIG_SCHEMA = cv.All( + cv.typed_schema( + { + "LAN8720": RMII_SCHEMA, + "RTL8201": RMII_SCHEMA, + "DP83848": RMII_SCHEMA, + "IP101": RMII_SCHEMA, + "JL1101": RMII_SCHEMA, + "W5500": SPI_SCHEMA, + }, + upper=True, + ), _validate, ) @@ -125,16 +167,32 @@ async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) - cg.add(var.set_phy_addr(config[CONF_PHY_ADDR])) - cg.add(var.set_mdc_pin(config[CONF_MDC_PIN])) - cg.add(var.set_mdio_pin(config[CONF_MDIO_PIN])) - cg.add(var.set_type(config[CONF_TYPE])) - cg.add(var.set_clk_mode(*CLK_MODES[config[CONF_CLK_MODE]])) + if config[CONF_TYPE] == "W5500": + cg.add(var.set_clk_pin(config[CONF_CLK_PIN])) + cg.add(var.set_miso_pin(config[CONF_MISO_PIN])) + cg.add(var.set_mosi_pin(config[CONF_MOSI_PIN])) + 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])) + 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])) + + cg.add_define("USE_ETHERNET_SPI") + if CORE.using_esp_idf: + add_idf_sdkconfig_option("CONFIG_ETH_USE_SPI_ETHERNET", True) + add_idf_sdkconfig_option("CONFIG_ETH_SPI_ETHERNET_W5500", True) + else: + cg.add(var.set_phy_addr(config[CONF_PHY_ADDR])) + cg.add(var.set_mdc_pin(config[CONF_MDC_PIN])) + cg.add(var.set_mdio_pin(config[CONF_MDIO_PIN])) + cg.add(var.set_clk_mode(*CLK_MODES[config[CONF_CLK_MODE]])) + if CONF_POWER_PIN in config: + cg.add(var.set_power_pin(config[CONF_POWER_PIN])) + + cg.add(var.set_type(ETHERNET_TYPES[config[CONF_TYPE]])) cg.add(var.set_use_address(config[CONF_USE_ADDRESS])) - if CONF_POWER_PIN in config: - cg.add(var.set_power_pin(config[CONF_POWER_PIN])) - if CONF_MANUAL_IP in config: cg.add(var.set_manual_ip(manual_ip(config[CONF_MANUAL_IP]))) diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 50d5855e6a0..b83c09163d1 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -9,6 +9,11 @@ #include #include "esp_event.h" +#ifdef USE_ETHERNET_SPI +#include +#include +#endif + namespace esphome { namespace ethernet { @@ -33,6 +38,38 @@ void EthernetComponent::setup() { } esp_err_t err; + +#ifdef USE_ETHERNET_SPI + // Install GPIO ISR handler to be able to service SPI Eth modules interrupts + gpio_install_isr_service(0); + + spi_bus_config_t buscfg = { + .mosi_io_num = this->mosi_pin_, + .miso_io_num = this->miso_pin_, + .sclk_io_num = this->clk_pin_, + .quadwp_io_num = -1, + .quadhd_io_num = -1, +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0) + .data4_io_num = -1, + .data5_io_num = -1, + .data6_io_num = -1, + .data7_io_num = -1, +#endif + .max_transfer_sz = 0, + .flags = 0, + .intr_flags = 0, + }; + +#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) + auto host = SPI2_HOST; +#else + auto host = SPI3_HOST; +#endif + + err = spi_bus_initialize(host, &buscfg, SPI_DMA_CH_AUTO); + ESPHL_ERROR_CHECK(err, "SPI bus initialize error"); +#endif + err = esp_netif_init(); ESPHL_ERROR_CHECK(err, "ETH netif init error"); err = esp_event_loop_create_default(); @@ -43,10 +80,40 @@ void EthernetComponent::setup() { // Init MAC and PHY configs to default eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); + eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); + +#ifdef USE_ETHERNET_SPI // Configure SPI interface and Ethernet driver for specific SPI module + spi_device_interface_config_t devcfg = { + .command_bits = 16, // Actually it's the address phase in W5500 SPI frame + .address_bits = 8, // Actually it's the control phase in W5500 SPI frame + .dummy_bits = 0, + .mode = 0, + .duty_cycle_pos = 0, + .cs_ena_pretrans = 0, + .cs_ena_posttrans = 0, + .clock_speed_hz = this->clock_speed_, + .input_delay_ns = 0, + .spics_io_num = this->cs_pin_, + .flags = 0, + .queue_size = 20, + .pre_cb = nullptr, + .post_cb = nullptr, + }; + + spi_device_handle_t spi_handle = nullptr; + err = spi_bus_add_device(host, &devcfg, &spi_handle); + ESPHL_ERROR_CHECK(err, "SPI bus add device error"); + + eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(spi_handle); + w5500_config.int_gpio_num = this->interrupt_pin_; + phy_config.phy_addr = this->phy_addr_spi_; + phy_config.reset_gpio_num = this->reset_pin_; + + esp_eth_mac_t *mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config); +#else phy_config.phy_addr = this->phy_addr_; phy_config.reset_gpio_num = this->power_pin_; - eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); #if ESP_IDF_VERSION_MAJOR >= 5 eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG(); esp32_emac_config.smi_mdc_gpio_num = this->mdc_pin_; @@ -62,9 +129,11 @@ void EthernetComponent::setup() { mac_config.clock_config.rmii.clock_gpio = this->clk_gpio_; esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config); +#endif #endif switch (this->type_) { +#if CONFIG_ETH_USE_ESP32_EMAC case ETHERNET_TYPE_LAN8720: { this->phy_ = esp_eth_phy_new_lan87xx(&phy_config); break; @@ -94,6 +163,13 @@ void EthernetComponent::setup() { #endif break; } +#endif +#ifdef USE_ETHERNET_SPI + case ETHERNET_TYPE_W5500: { + this->phy_ = esp_eth_phy_new_w5500(&phy_config); + break; + } +#endif default: { this->mark_failed(); return; @@ -105,10 +181,18 @@ void EthernetComponent::setup() { err = esp_eth_driver_install(ð_config, &this->eth_handle_); ESPHL_ERROR_CHECK(err, "ETH driver install error"); +#ifndef USE_ETHERNET_SPI if (this->type_ == ETHERNET_TYPE_KSZ8081RNA && this->clk_mode_ == EMAC_CLK_OUT) { // KSZ8081RNA default is incorrect. It expects a 25MHz clock instead of the 50MHz we provide. this->ksz8081_set_clock_reference_(mac); } +#endif + + // use ESP internal eth mac + uint8_t mac_addr[6]; + esp_read_mac(mac_addr, ESP_MAC_ETH); + err = esp_eth_ioctl(this->eth_handle_, ETH_CMD_S_MAC_ADDR, mac_addr); + ESPHL_ERROR_CHECK(err, "set mac address error"); /* attach Ethernet driver to TCP/IP stack */ err = esp_netif_attach(this->eth_netif_, esp_eth_new_netif_glue(this->eth_handle_)); @@ -214,6 +298,10 @@ void EthernetComponent::dump_config() { eth_type = "KSZ8081RNA"; break; + case ETHERNET_TYPE_W5500: + eth_type = "W5500"; + break; + default: eth_type = "Unknown"; break; @@ -221,13 +309,23 @@ void EthernetComponent::dump_config() { ESP_LOGCONFIG(TAG, "Ethernet:"); this->dump_connect_params_(); +#ifdef USE_ETHERNET_SPI + ESP_LOGCONFIG(TAG, " CLK Pin: %u", this->clk_pin_); + 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_); + ESP_LOGCONFIG(TAG, " Reset Pin: %d", this->reset_pin_); + ESP_LOGCONFIG(TAG, " Clock Speed: %d MHz", this->clock_speed_ / 1000000); +#else if (this->power_pin_ != -1) { ESP_LOGCONFIG(TAG, " Power Pin: %u", this->power_pin_); } ESP_LOGCONFIG(TAG, " MDC Pin: %u", this->mdc_pin_); ESP_LOGCONFIG(TAG, " MDIO Pin: %u", this->mdio_pin_); - ESP_LOGCONFIG(TAG, " Type: %s", eth_type); ESP_LOGCONFIG(TAG, " PHY addr: %u", this->phy_addr_); +#endif + ESP_LOGCONFIG(TAG, " Type: %s", eth_type); } float EthernetComponent::get_setup_priority() const { return setup_priority::WIFI; } @@ -393,15 +491,25 @@ void EthernetComponent::dump_connect_params_() { ESP_LOGCONFIG(TAG, " Link Speed: %u", speed == ETH_SPEED_100M ? 100 : 10); } +#ifdef USE_ETHERNET_SPI +void EthernetComponent::set_clk_pin(uint8_t clk_pin) { this->clk_pin_ = clk_pin; } +void EthernetComponent::set_miso_pin(uint8_t miso_pin) { this->miso_pin_ = miso_pin; } +void EthernetComponent::set_mosi_pin(uint8_t mosi_pin) { this->mosi_pin_ = mosi_pin; } +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(uint8_t clock_speed) { this->clock_speed_ = clock_speed * 1000000; } +#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; } void EthernetComponent::set_mdc_pin(uint8_t mdc_pin) { this->mdc_pin_ = mdc_pin; } void EthernetComponent::set_mdio_pin(uint8_t mdio_pin) { this->mdio_pin_ = mdio_pin; } -void EthernetComponent::set_type(EthernetType type) { this->type_ = type; } void EthernetComponent::set_clk_mode(emac_rmii_clock_mode_t clk_mode, emac_rmii_clock_gpio_t clk_gpio) { this->clk_mode_ = clk_mode; this->clk_gpio_ = clk_gpio; } +#endif +void EthernetComponent::set_type(EthernetType type) { this->type_ = type; } void EthernetComponent::set_manual_ip(const ManualIP &manual_ip) { this->manual_ip_ = manual_ip; } std::string EthernetComponent::get_use_address() const { @@ -428,6 +536,7 @@ bool EthernetComponent::powerdown() { return true; } +#ifndef USE_ETHERNET_SPI void EthernetComponent::ksz8081_set_clock_reference_(esp_eth_mac_t *mac) { #define KSZ80XX_PC2R_REG_ADDR (0x1F) @@ -458,6 +567,7 @@ void EthernetComponent::ksz8081_set_clock_reference_(esp_eth_mac_t *mac) { #undef KSZ80XX_PC2R_REG_ADDR } +#endif } // namespace ethernet } // namespace esphome diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index 11f50af966f..12fd50f19c5 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -23,6 +23,7 @@ enum EthernetType { ETHERNET_TYPE_JL1101, ETHERNET_TYPE_KSZ8081, ETHERNET_TYPE_KSZ8081RNA, + ETHERNET_TYPE_W5500, }; struct ManualIP { @@ -50,12 +51,22 @@ class EthernetComponent : public Component { void on_shutdown() override { powerdown(); } bool is_connected(); +#ifdef USE_ETHERNET_SPI + void set_clk_pin(uint8_t clk_pin); + void set_miso_pin(uint8_t miso_pin); + void set_mosi_pin(uint8_t mosi_pin); + void set_cs_pin(uint8_t cs_pin); + void set_interrupt_pin(uint8_t interrupt_pin); + void set_reset_pin(uint8_t reset_pin); + void set_clock_speed(uint8_t clock_speed); +#else void set_phy_addr(uint8_t phy_addr); void set_power_pin(int power_pin); void set_mdc_pin(uint8_t mdc_pin); void set_mdio_pin(uint8_t mdio_pin); - void set_type(EthernetType type); void set_clk_mode(emac_rmii_clock_mode_t clk_mode, emac_rmii_clock_gpio_t clk_gpio); +#endif + void set_type(EthernetType type); void set_manual_ip(const ManualIP &manual_ip); network::IPAddress get_ip_address(); @@ -76,13 +87,24 @@ class EthernetComponent : public Component { void ksz8081_set_clock_reference_(esp_eth_mac_t *mac); std::string use_address_; +#ifdef USE_ETHERNET_SPI + uint8_t clk_pin_; + uint8_t miso_pin_; + uint8_t mosi_pin_; + uint8_t cs_pin_; + uint8_t interrupt_pin_; + int reset_pin_{-1}; + int phy_addr_spi_{-1}; + int clock_speed_{30 * 1000000}; +#else uint8_t phy_addr_{0}; int power_pin_{-1}; uint8_t mdc_pin_{23}; uint8_t mdio_pin_{18}; - EthernetType type_{ETHERNET_TYPE_UNKNOWN}; emac_rmii_clock_mode_t clk_mode_{EMAC_CLK_EXT_IN}; emac_rmii_clock_gpio_t clk_gpio_{EMAC_CLK_IN_GPIO}; +#endif + EthernetType type_{ETHERNET_TYPE_UNKNOWN}; optional manual_ip_{}; bool started_{false}; diff --git a/tests/test12.yaml b/tests/test12.yaml new file mode 100644 index 00000000000..e6dba0fd2af --- /dev/null +++ b/tests/test12.yaml @@ -0,0 +1,34 @@ +# Testing W5500 SPI Ethernet on esp-idf +--- +esphome: + name: test12 + build_path: build/test12 + project: + name: esphome.test12_project + version: "1.0.0" + +esp32: + board: nodemcu-32s + framework: + type: esp-idf + +ethernet: + type: W5500 + clk_pin: GPIO19 + mosi_pin: GPIO21 + miso_pin: GPIO23 + cs_pin: GPIO18 + interrupt_pin: GPIO36 + reset_pin: GPIO22 + clock_speed: 30 + manual_ip: + static_ip: 192.168.178.56 + gateway: 192.168.178.1 + subnet: 255.255.255.0 + domain: .local + +api: + +ota: + +logger: diff --git a/tests/test4.yaml b/tests/test4.yaml index e46102e88aa..f8deda47900 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -9,18 +9,26 @@ substitutions: devicename: test-4 ethernet: - type: LAN8720 - mdc_pin: + type: W5500 + clk_pin: + allow_other_uses: true + number: GPIO19 + mosi_pin: + allow_other_uses: true + number: GPIO21 + miso_pin: allow_other_uses: true number: GPIO23 - mdio_pin: + cs_pin: allow_other_uses: true - number: GPIO25 - clk_mode: GPIO0_IN - phy_addr: 0 - power_pin: + number: GPIO18 + interrupt_pin: allow_other_uses: true - number: GPIO25 + number: GPIO36 + reset_pin: + allow_other_uses: true + number: GPIO22 + clock_speed: 30 manual_ip: static_ip: 192.168.178.56 gateway: 192.168.178.1 From 238350bed769c9ab157a81c7609203cd12ca802c Mon Sep 17 00:00:00 2001 From: Jeroen van Oort Date: Mon, 6 Nov 2023 14:10:10 +0100 Subject: [PATCH 2/7] Processed feedback --- esphome/components/ethernet/__init__.py | 5 ++-- .../ethernet/ethernet_component.cpp | 28 +++++++++---------- .../components/ethernet/ethernet_component.h | 7 +++-- tests/test12.yaml | 2 +- tests/test4.yaml | 2 +- 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index d8dcc4c6d84..10876d70f45 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -22,10 +22,11 @@ ) from esphome.core import CORE, coroutine_with_priority from esphome.components.network import IPAddress +from esphome.components.spi import SPI_DATA_RATE_SCHEMA CONFLICTS_WITH = ["wifi"] DEPENDENCIES = ["esp32"] -AUTO_LOAD = ["network"] +AUTO_LOAD = ["network", "spi"] ethernet_ns = cg.esphome_ns.namespace("ethernet") CONF_PHY_ADDR = "phy_addr" @@ -130,7 +131,7 @@ def _validate(config): cv.Required(CONF_CS_PIN): pins.internal_gpio_output_pin_number, cv.Optional(CONF_INTERRUPT_PIN): pins.internal_gpio_input_pin_number, cv.Optional(CONF_RESET_PIN): pins.internal_gpio_output_pin_number, - cv.Optional(CONF_CLOCK_SPEED, default=30): cv.int_range(1, 80), # type: ignore[arg-type] + cv.Optional(CONF_CLOCK_SPEED, default="20MHz"): SPI_DATA_RATE_SCHEMA, } ), ) diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index b83c09163d1..d3d129a0199 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -44,20 +44,18 @@ void EthernetComponent::setup() { gpio_install_isr_service(0); spi_bus_config_t buscfg = { - .mosi_io_num = this->mosi_pin_, - .miso_io_num = this->miso_pin_, - .sclk_io_num = this->clk_pin_, - .quadwp_io_num = -1, - .quadhd_io_num = -1, -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0) - .data4_io_num = -1, - .data5_io_num = -1, - .data6_io_num = -1, - .data7_io_num = -1, -#endif - .max_transfer_sz = 0, - .flags = 0, - .intr_flags = 0, + .mosi_io_num = this->mosi_pin_, + .miso_io_num = this->miso_pin_, + .sclk_io_num = this->clk_pin_, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .data4_io_num = -1, + .data5_io_num = -1, + .data6_io_num = -1, + .data7_io_num = -1, + .max_transfer_sz = 0, + .flags = 0, + .intr_flags = 0, }; #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) @@ -498,7 +496,7 @@ void EthernetComponent::set_mosi_pin(uint8_t mosi_pin) { this->mosi_pin_ = mosi_ 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(uint8_t clock_speed) { this->clock_speed_ = clock_speed * 1000000; } +void EthernetComponent::set_clock_speed(int clock_speed) { this->clock_speed_ = clock_speed; } #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 12fd50f19c5..a7995328451 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -4,6 +4,9 @@ #include "esphome/core/defines.h" #include "esphome/core/hal.h" #include "esphome/components/network/ip_address.h" +#ifdef USE_ETHERNET_SPI +#include "esphome/components/spi/spi.h" +#endif #ifdef USE_ESP32 @@ -58,7 +61,7 @@ class EthernetComponent : public Component { void set_cs_pin(uint8_t cs_pin); void set_interrupt_pin(uint8_t interrupt_pin); void set_reset_pin(uint8_t reset_pin); - void set_clock_speed(uint8_t clock_speed); + void set_clock_speed(int clock_speed); #else void set_phy_addr(uint8_t phy_addr); void set_power_pin(int power_pin); @@ -95,7 +98,7 @@ class EthernetComponent : public Component { uint8_t interrupt_pin_; int reset_pin_{-1}; int phy_addr_spi_{-1}; - int clock_speed_{30 * 1000000}; + int clock_speed_; #else uint8_t phy_addr_{0}; int power_pin_{-1}; diff --git a/tests/test12.yaml b/tests/test12.yaml index e6dba0fd2af..461510bc5aa 100644 --- a/tests/test12.yaml +++ b/tests/test12.yaml @@ -20,7 +20,7 @@ ethernet: cs_pin: GPIO18 interrupt_pin: GPIO36 reset_pin: GPIO22 - clock_speed: 30 + clock_speed: 10Mhz manual_ip: static_ip: 192.168.178.56 gateway: 192.168.178.1 diff --git a/tests/test4.yaml b/tests/test4.yaml index f8deda47900..4926943c1e9 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -28,7 +28,7 @@ ethernet: reset_pin: allow_other_uses: true number: GPIO22 - clock_speed: 30 + clock_speed: 10Mhz manual_ip: static_ip: 192.168.178.56 gateway: 192.168.178.1 From 4815ae992491aaf85a4068bf03775efc1512cce6 Mon Sep 17 00:00:00 2001 From: Jeroen van Oort Date: Thu, 22 Feb 2024 19:54:33 +0100 Subject: [PATCH 3/7] Simplyfied clock speed validation --- esphome/components/ethernet/__init__.py | 7 ++++--- esphome/components/ethernet/ethernet_component.h | 3 --- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index 10876d70f45..51b84628e80 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -22,11 +22,10 @@ ) from esphome.core import CORE, coroutine_with_priority from esphome.components.network import IPAddress -from esphome.components.spi import SPI_DATA_RATE_SCHEMA CONFLICTS_WITH = ["wifi"] DEPENDENCIES = ["esp32"] -AUTO_LOAD = ["network", "spi"] +AUTO_LOAD = ["network"] ethernet_ns = cg.esphome_ns.namespace("ethernet") CONF_PHY_ADDR = "phy_addr" @@ -131,7 +130,9 @@ def _validate(config): cv.Required(CONF_CS_PIN): pins.internal_gpio_output_pin_number, cv.Optional(CONF_INTERRUPT_PIN): pins.internal_gpio_input_pin_number, cv.Optional(CONF_RESET_PIN): pins.internal_gpio_output_pin_number, - cv.Optional(CONF_CLOCK_SPEED, default="20MHz"): SPI_DATA_RATE_SCHEMA, + cv.Optional(CONF_CLOCK_SPEED, default="26.67MHz"): cv.All( + cv.frequency, cv.int_range(int(8e6), int(80e6)) + ), } ), ) diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index a7995328451..121d4756230 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -4,9 +4,6 @@ #include "esphome/core/defines.h" #include "esphome/core/hal.h" #include "esphome/components/network/ip_address.h" -#ifdef USE_ETHERNET_SPI -#include "esphome/components/spi/spi.h" -#endif #ifdef USE_ESP32 From e9a8c35cc02ee283988debb2be816cbe388e7e74 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 1 Mar 2024 11:22:07 +1300 Subject: [PATCH 4/7] Validate SPI interface is not going to conflict --- esphome/components/ethernet/__init__.py | 34 ++++++++++++++++++++++++- esphome/const.py | 1 + 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index 51b84628e80..de6040339ad 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -1,7 +1,13 @@ from esphome import pins import esphome.config_validation as cv +import esphome.final_validate as fv import esphome.codegen as cg -from esphome.components.esp32 import add_idf_sdkconfig_option +from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant +from esphome.components.esp32.const import ( + VARIANT_ESP32C3, + VARIANT_ESP32S2, + VARIANT_ESP32S3, +) from esphome.const import ( CONF_DOMAIN, CONF_ID, @@ -19,9 +25,11 @@ CONF_CS_PIN, CONF_INTERRUPT_PIN, CONF_RESET_PIN, + CONF_SPI, ) from esphome.core import CORE, coroutine_with_priority from esphome.components.network import IPAddress +from esphome.components.spi import get_spi_interface, CONF_INTERFACE_INDEX CONFLICTS_WITH = ["wifi"] DEPENDENCIES = ["esp32"] @@ -48,6 +56,8 @@ "W5500": EthernetType.ETHERNET_TYPE_W5500, } +SPI_ETHERNET_TYPES = ["W5500"] + 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") CLK_MODES = { @@ -153,6 +163,28 @@ def _validate(config): ) +def _final_validate(config): + if config[CONF_TYPE] not in SPI_ETHERNET_TYPES: + return + if spi_configs := fv.full_config.get().get(CONF_SPI): + variant = get_esp32_variant() + if variant in (VARIANT_ESP32C3, VARIANT_ESP32S2, VARIANT_ESP32S3): + spi_host = "SPI2_HOST" + else: + spi_host = "SPI3_HOST" + for spi_conf in spi_configs: + if (index := spi_conf.get(CONF_INTERFACE_INDEX)) is not None: + interface = get_spi_interface(index) + if interface == spi_host: + raise cv.Invalid( + f"`spi` component is using interface '{interface}'. " + f"To use {config[CONF_TYPE]}, you must change the `interface` on the `spi` component.", + ) + + +FINAL_VALIDATE_SCHEMA = _final_validate + + def manual_ip(config): return cg.StructInitializer( ManualIP, diff --git a/esphome/const.py b/esphome/const.py index 1d7f43d8418..41c5db7f846 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -730,6 +730,7 @@ CONF_SPEED_LEVEL_COMMAND_TOPIC = "speed_level_command_topic" CONF_SPEED_LEVEL_STATE_TOPIC = "speed_level_state_topic" CONF_SPEED_STATE_TOPIC = "speed_state_topic" +CONF_SPI = "spi" CONF_SPI_ID = "spi_id" CONF_SPIKE_REJECTION = "spike_rejection" CONF_SSID = "ssid" From d71f3d96cbae7af367f25274c12ec3d9527f955a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 1 Mar 2024 12:43:24 +1300 Subject: [PATCH 5/7] Add ethernet component tests --- tests/components/ethernet/lan8720.esp32-idf.yaml | 12 ++++++++++++ tests/components/ethernet/lan8720.esp32.yaml | 12 ++++++++++++ tests/components/ethernet/w5500.esp32-idf.yaml | 14 ++++++++++++++ tests/components/ethernet/w5500.esp32.yaml | 14 ++++++++++++++ 4 files changed, 52 insertions(+) create mode 100644 tests/components/ethernet/lan8720.esp32-idf.yaml create mode 100644 tests/components/ethernet/lan8720.esp32.yaml create mode 100644 tests/components/ethernet/w5500.esp32-idf.yaml create mode 100644 tests/components/ethernet/w5500.esp32.yaml diff --git a/tests/components/ethernet/lan8720.esp32-idf.yaml b/tests/components/ethernet/lan8720.esp32-idf.yaml new file mode 100644 index 00000000000..b9ed9cb036a --- /dev/null +++ b/tests/components/ethernet/lan8720.esp32-idf.yaml @@ -0,0 +1,12 @@ +ethernet: + type: LAN8720 + mdc_pin: 23 + mdio_pin: 25 + clk_mode: GPIO0_IN + phy_addr: 0 + power_pin: 26 + manual_ip: + static_ip: 192.168.178.56 + gateway: 192.168.178.1 + subnet: 255.255.255.0 + domain: .local diff --git a/tests/components/ethernet/lan8720.esp32.yaml b/tests/components/ethernet/lan8720.esp32.yaml new file mode 100644 index 00000000000..b9ed9cb036a --- /dev/null +++ b/tests/components/ethernet/lan8720.esp32.yaml @@ -0,0 +1,12 @@ +ethernet: + type: LAN8720 + mdc_pin: 23 + mdio_pin: 25 + clk_mode: GPIO0_IN + phy_addr: 0 + power_pin: 26 + manual_ip: + static_ip: 192.168.178.56 + gateway: 192.168.178.1 + subnet: 255.255.255.0 + domain: .local diff --git a/tests/components/ethernet/w5500.esp32-idf.yaml b/tests/components/ethernet/w5500.esp32-idf.yaml new file mode 100644 index 00000000000..6fdccb36e3f --- /dev/null +++ b/tests/components/ethernet/w5500.esp32-idf.yaml @@ -0,0 +1,14 @@ +ethernet: + type: W5500 + clk_pin: GPIO19 + mosi_pin: GPIO21 + miso_pin: GPIO23 + cs_pin: GPIO18 + interrupt_pin: GPIO36 + reset_pin: GPIO22 + clock_speed: 10Mhz + manual_ip: + static_ip: 192.168.178.56 + gateway: 192.168.178.1 + subnet: 255.255.255.0 + domain: .local diff --git a/tests/components/ethernet/w5500.esp32.yaml b/tests/components/ethernet/w5500.esp32.yaml new file mode 100644 index 00000000000..6fdccb36e3f --- /dev/null +++ b/tests/components/ethernet/w5500.esp32.yaml @@ -0,0 +1,14 @@ +ethernet: + type: W5500 + clk_pin: GPIO19 + mosi_pin: GPIO21 + miso_pin: GPIO23 + cs_pin: GPIO18 + interrupt_pin: GPIO36 + reset_pin: GPIO22 + clock_speed: 10Mhz + manual_ip: + static_ip: 192.168.178.56 + gateway: 192.168.178.1 + subnet: 255.255.255.0 + domain: .local From a1ecec82f39048e4912249f2db7dcf071424faaf Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 1 Mar 2024 13:36:58 +1300 Subject: [PATCH 6/7] Discard changes to tests/test12.yaml --- tests/test12.yaml | 34 ---------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 tests/test12.yaml diff --git a/tests/test12.yaml b/tests/test12.yaml deleted file mode 100644 index 461510bc5aa..00000000000 --- a/tests/test12.yaml +++ /dev/null @@ -1,34 +0,0 @@ -# Testing W5500 SPI Ethernet on esp-idf ---- -esphome: - name: test12 - build_path: build/test12 - project: - name: esphome.test12_project - version: "1.0.0" - -esp32: - board: nodemcu-32s - framework: - type: esp-idf - -ethernet: - type: W5500 - clk_pin: GPIO19 - mosi_pin: GPIO21 - miso_pin: GPIO23 - cs_pin: GPIO18 - interrupt_pin: GPIO36 - reset_pin: GPIO22 - clock_speed: 10Mhz - manual_ip: - static_ip: 192.168.178.56 - gateway: 192.168.178.1 - subnet: 255.255.255.0 - domain: .local - -api: - -ota: - -logger: From dd06b69a9eba477fa49419f621b58f04d9fe818d Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 1 Mar 2024 13:37:02 +1300 Subject: [PATCH 7/7] Discard changes to tests/test4.yaml --- tests/test4.yaml | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/tests/test4.yaml b/tests/test4.yaml index 4926943c1e9..e46102e88aa 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -9,26 +9,18 @@ substitutions: devicename: test-4 ethernet: - type: W5500 - clk_pin: - allow_other_uses: true - number: GPIO19 - mosi_pin: - allow_other_uses: true - number: GPIO21 - miso_pin: + type: LAN8720 + mdc_pin: allow_other_uses: true number: GPIO23 - cs_pin: - allow_other_uses: true - number: GPIO18 - interrupt_pin: + mdio_pin: allow_other_uses: true - number: GPIO36 - reset_pin: + number: GPIO25 + clk_mode: GPIO0_IN + phy_addr: 0 + power_pin: allow_other_uses: true - number: GPIO22 - clock_speed: 10Mhz + number: GPIO25 manual_ip: static_ip: 192.168.178.56 gateway: 192.168.178.1