mirror of
https://github.com/esphome/esphome.git
synced 2026-05-13 03:30:34 +08:00
[ethernet] Add W5100 support for RP2040 (#15131)
This commit is contained in:
@@ -115,6 +115,7 @@ ETHERNET_TYPES = {
|
||||
"JL1101": EthernetType.ETHERNET_TYPE_JL1101,
|
||||
"KSZ8081": EthernetType.ETHERNET_TYPE_KSZ8081,
|
||||
"KSZ8081RNA": EthernetType.ETHERNET_TYPE_KSZ8081RNA,
|
||||
"W5100": EthernetType.ETHERNET_TYPE_W5100,
|
||||
"W5500": EthernetType.ETHERNET_TYPE_W5500,
|
||||
"OPENETH": EthernetType.ETHERNET_TYPE_OPENETH,
|
||||
"DM9051": EthernetType.ETHERNET_TYPE_DM9051,
|
||||
@@ -132,6 +133,7 @@ _PHY_TYPE_TO_DEFINE = {
|
||||
"JL1101": "USE_ETHERNET_JL1101",
|
||||
"KSZ8081": "USE_ETHERNET_KSZ8081",
|
||||
"KSZ8081RNA": "USE_ETHERNET_KSZ8081",
|
||||
"W5100": "USE_ETHERNET_W5100",
|
||||
"W5500": "USE_ETHERNET_W5500",
|
||||
"DM9051": "USE_ETHERNET_DM9051",
|
||||
"LAN8670": "USE_ETHERNET_LAN8670",
|
||||
@@ -164,9 +166,15 @@ _IDF6_ETHERNET_COMPONENTS: dict[str, IDFRegistryComponent] = {
|
||||
# These types are always external IDF components (never built-in to ESP-IDF)
|
||||
_ALWAYS_EXTERNAL_IDF_COMPONENTS = {"LAN8670", "ENC28J60"}
|
||||
|
||||
SPI_ETHERNET_TYPES = ["W5500", "DM9051", "ENC28J60"]
|
||||
# ESP32-only SPI ethernet types (W5100 is RP2040-only, no ESP-IDF driver)
|
||||
SPI_ETHERNET_TYPES = {"W5500", "DM9051", "ENC28J60"}
|
||||
# RP2040-supported SPI ethernet types
|
||||
RP2040_SPI_ETHERNET_TYPES = ["W5500", "ENC28J60"]
|
||||
RP2040_SPI_ETHERNET_TYPES = {"W5100", "W5500", "ENC28J60"}
|
||||
_RP2040_SPI_LIBRARIES = {
|
||||
"W5100": "lwIP_w5100",
|
||||
"W5500": "lwIP_w5500",
|
||||
"ENC28J60": "lwIP_enc28j60",
|
||||
}
|
||||
SPI_ETHERNET_DEFAULT_POLLING_INTERVAL = TimePeriodMilliseconds(milliseconds=10)
|
||||
|
||||
emac_rmii_clock_mode_t = cg.global_ns.enum("emac_rmii_clock_mode_t")
|
||||
@@ -295,7 +303,7 @@ def _validate(config):
|
||||
)
|
||||
elif CORE.is_rp2040 and config[CONF_TYPE] not in RP2040_SPI_ETHERNET_TYPES:
|
||||
raise cv.Invalid(
|
||||
f"Only {', '.join(RP2040_SPI_ETHERNET_TYPES)} are supported on RP2040, "
|
||||
f"Only {', '.join(sorted(RP2040_SPI_ETHERNET_TYPES))} are supported on RP2040, "
|
||||
f"not {config[CONF_TYPE]}"
|
||||
)
|
||||
return config
|
||||
@@ -382,6 +390,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
"JL1101": RMII_SCHEMA,
|
||||
"KSZ8081": RMII_SCHEMA,
|
||||
"KSZ8081RNA": RMII_SCHEMA,
|
||||
"W5100": cv.All(SPI_SCHEMA, cv.only_on([Platform.RP2040])),
|
||||
"W5500": SPI_SCHEMA,
|
||||
"OPENETH": cv.All(BASE_SCHEMA, cv.only_on([Platform.ESP32])),
|
||||
"DM9051": SPI_SCHEMA,
|
||||
@@ -574,10 +583,7 @@ async def _to_code_rp2040(var: cg.Pvariable, config: ConfigType) -> None:
|
||||
cg.add(var.set_reset_pin(config[CONF_RESET_PIN]))
|
||||
|
||||
cg.add_define("USE_ETHERNET_SPI")
|
||||
if config[CONF_TYPE] == "ENC28J60":
|
||||
cg.add_library("lwIP_enc28j60", None)
|
||||
else:
|
||||
cg.add_library("lwIP_w5500", None)
|
||||
cg.add_library(_RP2040_SPI_LIBRARIES[config[CONF_TYPE]], None)
|
||||
|
||||
|
||||
def _final_validate_rmii_pins(config: ConfigType) -> None:
|
||||
|
||||
@@ -25,6 +25,8 @@ extern "C" eth_esp32_emac_config_t eth_esp32_emac_default_config(void);
|
||||
#ifdef USE_RP2040
|
||||
#if defined(USE_ETHERNET_W5500)
|
||||
#include <W5500lwIP.h>
|
||||
#elif defined(USE_ETHERNET_W5100)
|
||||
#include <W5100lwIP.h>
|
||||
#elif defined(USE_ETHERNET_ENC28J60)
|
||||
#include <ENC28J60lwIP.h>
|
||||
#else
|
||||
@@ -59,6 +61,7 @@ enum EthernetType : uint8_t {
|
||||
ETHERNET_TYPE_JL1101,
|
||||
ETHERNET_TYPE_KSZ8081,
|
||||
ETHERNET_TYPE_KSZ8081RNA,
|
||||
ETHERNET_TYPE_W5100,
|
||||
ETHERNET_TYPE_W5500,
|
||||
ETHERNET_TYPE_OPENETH,
|
||||
ETHERNET_TYPE_DM9051,
|
||||
@@ -222,8 +225,15 @@ class EthernetComponent final : public Component {
|
||||
|
||||
#ifdef USE_RP2040
|
||||
static constexpr uint32_t LINK_CHECK_INTERVAL = 500; // ms between link/IP polls
|
||||
#if defined(USE_ETHERNET_W5100)
|
||||
static constexpr uint32_t RESET_DELAY_MS = 150; // W5100S PLL lock time
|
||||
#else
|
||||
static constexpr uint32_t RESET_DELAY_MS = 10;
|
||||
#endif
|
||||
#if defined(USE_ETHERNET_W5500)
|
||||
Wiznet5500lwIP *eth_{nullptr};
|
||||
#elif defined(USE_ETHERNET_W5100)
|
||||
Wiznet5100lwIP *eth_{nullptr};
|
||||
#elif defined(USE_ETHERNET_ENC28J60)
|
||||
ENC28J60lwIP *eth_{nullptr};
|
||||
#else
|
||||
|
||||
@@ -31,12 +31,15 @@ void EthernetComponent::setup() {
|
||||
reset_pin.digital_write(false);
|
||||
delay(1); // NOLINT
|
||||
reset_pin.digital_write(true);
|
||||
delay(10); // NOLINT - wait for chip to initialize after reset
|
||||
// W5100S needs 150ms for PLL lock; W5500/ENC28J60 need ~10ms
|
||||
delay(RESET_DELAY_MS); // NOLINT
|
||||
}
|
||||
|
||||
// Create the SPI Ethernet device instance
|
||||
#if defined(USE_ETHERNET_W5500)
|
||||
this->eth_ = new Wiznet5500lwIP(this->cs_pin_, SPI, this->interrupt_pin_); // NOLINT
|
||||
#elif defined(USE_ETHERNET_W5100)
|
||||
this->eth_ = new Wiznet5100lwIP(this->cs_pin_, SPI, this->interrupt_pin_); // NOLINT
|
||||
#elif defined(USE_ETHERNET_ENC28J60)
|
||||
this->eth_ = new ENC28J60lwIP(this->cs_pin_, SPI, this->interrupt_pin_); // NOLINT
|
||||
#endif
|
||||
@@ -80,8 +83,8 @@ void EthernetComponent::setup() {
|
||||
// or via GPIO interrupt when one is provided.
|
||||
|
||||
// Don't set started_ here — let the link polling in loop() set it
|
||||
// when the W5500 link is actually up. Setting it prematurely causes
|
||||
// a "Starting → Stopped → Starting" log sequence because the W5500
|
||||
// when the link is actually up. Setting it prematurely causes
|
||||
// a "Starting → Stopped → Starting" log sequence because the chip
|
||||
// needs time after begin() before the PHY link is ready.
|
||||
}
|
||||
|
||||
@@ -89,14 +92,21 @@ void EthernetComponent::loop() {
|
||||
// On RP2040, we need to poll connection state since there are no events.
|
||||
const uint32_t now = App.get_loop_component_start_time();
|
||||
|
||||
// Throttle link/IP polling to avoid excessive SPI transactions from linkStatus()
|
||||
// which reads the W5500 PHY register via SPI on every call.
|
||||
// Throttle link/IP polling to avoid excessive SPI transactions.
|
||||
// W5500/ENC28J60 read PHY register via SPI on every linkStatus() call.
|
||||
// W5100 can't detect link state, so we skip the SPI read and assume link-up.
|
||||
// connected() reads netif->ip_addr without LwIPLock, but this is a single
|
||||
// 32-bit aligned read (atomic on ARM) — worst case is a one-iteration-stale
|
||||
// value, which is benign for polling.
|
||||
if (this->eth_ != nullptr && now - this->last_link_check_ >= LINK_CHECK_INTERVAL) {
|
||||
this->last_link_check_ = now;
|
||||
#if defined(USE_ETHERNET_W5100)
|
||||
// W5100 can't detect link (isLinkDetectable() returns false), so linkStatus()
|
||||
// returns Unknown — assume link is up after successful begin()
|
||||
bool link_up = true;
|
||||
#else
|
||||
bool link_up = this->eth_->linkStatus() == LinkON;
|
||||
#endif
|
||||
bool has_ip = this->eth_->connected();
|
||||
|
||||
if (!link_up) {
|
||||
@@ -171,6 +181,8 @@ void EthernetComponent::dump_config() {
|
||||
const char *type_str = "Unknown";
|
||||
#if defined(USE_ETHERNET_W5500)
|
||||
type_str = "W5500";
|
||||
#elif defined(USE_ETHERNET_W5100)
|
||||
type_str = "W5100";
|
||||
#elif defined(USE_ETHERNET_ENC28J60)
|
||||
type_str = "ENC28J60";
|
||||
#endif
|
||||
@@ -226,7 +238,7 @@ const char *EthernetComponent::get_eth_mac_address_pretty_into_buffer(
|
||||
}
|
||||
|
||||
eth_duplex_t EthernetComponent::get_duplex_mode() {
|
||||
// Both W5500 and ENC28J60 are full-duplex on RP2040
|
||||
// W5100, W5500, and ENC28J60 are full-duplex on RP2040
|
||||
return ETH_DUPLEX_FULL;
|
||||
}
|
||||
|
||||
@@ -235,7 +247,7 @@ eth_speed_t EthernetComponent::get_link_speed() {
|
||||
// ENC28J60 is 10Mbps only
|
||||
return ETH_SPEED_10M;
|
||||
#else
|
||||
// W5500 is always 100Mbps
|
||||
// W5100 and W5500 are 100Mbps
|
||||
return ETH_SPEED_100M;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -284,6 +284,7 @@
|
||||
#define USE_ETHERNET_SPI
|
||||
#define USE_ETHERNET_SPI_POLLING_SUPPORT
|
||||
#define USE_ETHERNET_OPENETH
|
||||
#define USE_ETHERNET_W5100
|
||||
#define USE_ETHERNET_W5500
|
||||
#define USE_ETHERNET_DM9051
|
||||
#define CONFIG_ETH_SPI_ETHERNET_W5500 1
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
ethernet:
|
||||
type: W5100
|
||||
clk_pin: 18
|
||||
mosi_pin: 19
|
||||
miso_pin: 16
|
||||
cs_pin: 17
|
||||
interrupt_pin: 21
|
||||
reset_pin: 20
|
||||
manual_ip:
|
||||
static_ip: 192.168.178.56
|
||||
gateway: 192.168.178.1
|
||||
subnet: 255.255.255.0
|
||||
domain: .local
|
||||
mac_address: "02:AA:BB:CC:DD:01"
|
||||
on_connect:
|
||||
- logger.log: "Ethernet connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Ethernet disconnected!"
|
||||
@@ -0,0 +1 @@
|
||||
<<: !include common-w5100-rp2040.yaml
|
||||
Reference in New Issue
Block a user