Merge branch 'dev' of https://github.com/esphome/esphome into templatable-value-specialize

This commit is contained in:
J. Nick Koston
2026-04-07 17:09:39 -10:00
11 changed files with 143 additions and 26 deletions
+12 -4
View File
@@ -123,6 +123,8 @@ ETHERNET_TYPES = {
"DM9051": EthernetType.ETHERNET_TYPE_DM9051, "DM9051": EthernetType.ETHERNET_TYPE_DM9051,
"LAN8670": EthernetType.ETHERNET_TYPE_LAN8670, "LAN8670": EthernetType.ETHERNET_TYPE_LAN8670,
"ENC28J60": EthernetType.ETHERNET_TYPE_ENC28J60, "ENC28J60": EthernetType.ETHERNET_TYPE_ENC28J60,
"W6100": EthernetType.ETHERNET_TYPE_W6100,
"W6300": EthernetType.ETHERNET_TYPE_W6300,
} }
# PHY types that need compile-time defines for conditional compilation # PHY types that need compile-time defines for conditional compilation
@@ -140,6 +142,8 @@ _PHY_TYPE_TO_DEFINE = {
"DM9051": "USE_ETHERNET_DM9051", "DM9051": "USE_ETHERNET_DM9051",
"LAN8670": "USE_ETHERNET_LAN8670", "LAN8670": "USE_ETHERNET_LAN8670",
"ENC28J60": "USE_ETHERNET_ENC28J60", "ENC28J60": "USE_ETHERNET_ENC28J60",
"W6100": "USE_ETHERNET_W6100",
"W6300": "USE_ETHERNET_W6300",
} }
@@ -170,12 +174,14 @@ _ALWAYS_EXTERNAL_IDF_COMPONENTS = {"LAN8670", "ENC28J60"}
# ESP32-only SPI ethernet types (W5100 is RP2040-only, no ESP-IDF driver) # ESP32-only SPI ethernet types (W5100 is RP2040-only, no ESP-IDF driver)
SPI_ETHERNET_TYPES = {"W5500", "DM9051", "ENC28J60"} SPI_ETHERNET_TYPES = {"W5500", "DM9051", "ENC28J60"}
# RP2040-supported SPI ethernet types # RP2040-supported ethernet types (SPI and PIO QSPI)
RP2040_SPI_ETHERNET_TYPES = {"W5100", "W5500", "ENC28J60"} RP2040_ETHERNET_TYPES = {"W5100", "W5500", "W6100", "W6300", "ENC28J60"}
_RP2040_SPI_LIBRARIES = { _RP2040_SPI_LIBRARIES = {
"W5100": "lwIP_w5100", "W5100": "lwIP_w5100",
"W5500": "lwIP_w5500", "W5500": "lwIP_w5500",
"ENC28J60": "lwIP_enc28j60", "ENC28J60": "lwIP_enc28j60",
"W6100": "lwIP_w6100",
"W6300": "lwIP_w6300",
} }
SPI_ETHERNET_DEFAULT_POLLING_INTERVAL = TimePeriodMilliseconds(milliseconds=10) SPI_ETHERNET_DEFAULT_POLLING_INTERVAL = TimePeriodMilliseconds(milliseconds=10)
@@ -328,9 +334,9 @@ def _validate(config):
f"{config[CONF_TYPE]} PHY requires RMII interface and is only supported " f"{config[CONF_TYPE]} PHY requires RMII interface and is only supported "
f"on ESP32 classic and ESP32-P4, not {variant}" f"on ESP32 classic and ESP32-P4, not {variant}"
) )
elif CORE.is_rp2040 and config[CONF_TYPE] not in RP2040_SPI_ETHERNET_TYPES: elif CORE.is_rp2040 and config[CONF_TYPE] not in RP2040_ETHERNET_TYPES:
raise cv.Invalid( raise cv.Invalid(
f"Only {', '.join(sorted(RP2040_SPI_ETHERNET_TYPES))} are supported on RP2040, " f"Only {', '.join(sorted(RP2040_ETHERNET_TYPES))} are supported on RP2040, "
f"not {config[CONF_TYPE]}" f"not {config[CONF_TYPE]}"
) )
return config return config
@@ -427,6 +433,8 @@ CONFIG_SCHEMA = cv.All(
"OPENETH": cv.All(BASE_SCHEMA, cv.only_on([Platform.ESP32])), "OPENETH": cv.All(BASE_SCHEMA, cv.only_on([Platform.ESP32])),
"DM9051": SPI_SCHEMA, "DM9051": SPI_SCHEMA,
"ENC28J60": SPI_SCHEMA, "ENC28J60": SPI_SCHEMA,
"W6100": cv.All(SPI_SCHEMA, cv.only_on([Platform.RP2040])),
"W6300": cv.All(SPI_SCHEMA, cv.only_on([Platform.RP2040])),
"LAN8670": RMII_SCHEMA, "LAN8670": RMII_SCHEMA,
}, },
upper=True, upper=True,
@@ -30,6 +30,20 @@ extern "C" eth_esp32_emac_config_t eth_esp32_emac_default_config(void);
#include <W5500lwIP.h> #include <W5500lwIP.h>
#elif defined(USE_ETHERNET_W5100) #elif defined(USE_ETHERNET_W5100)
#include <W5100lwIP.h> #include <W5100lwIP.h>
#elif defined(USE_ETHERNET_W6100)
#include <W6100lwIP.h>
#elif defined(USE_ETHERNET_W6300)
#include <W6300lwIP.h>
// W6300 uses PIO QSPI, not Arduino SPI. The upstream Wiznet6300 class
// incorrectly returns needsSPI()=true, causing LwipIntfDev::begin() to
// call SPI.begin() which claims GPIOs that PIO QSPI needs.
// This wrapper hides needsSPI() with a version returning false.
class Wiznet6300NoSPI : public Wiznet6300 {
public:
using Wiznet6300::Wiznet6300;
constexpr bool needsSPI() const { return false; }
};
using Wiznet6300lwIPFixed = LwipIntfDev<Wiznet6300NoSPI>;
#elif defined(USE_ETHERNET_ENC28J60) #elif defined(USE_ETHERNET_ENC28J60)
#include <ENC28J60lwIP.h> #include <ENC28J60lwIP.h>
#else #else
@@ -70,6 +84,8 @@ enum EthernetType : uint8_t {
ETHERNET_TYPE_DM9051, ETHERNET_TYPE_DM9051,
ETHERNET_TYPE_LAN8670, ETHERNET_TYPE_LAN8670,
ETHERNET_TYPE_ENC28J60, ETHERNET_TYPE_ENC28J60,
ETHERNET_TYPE_W6100,
ETHERNET_TYPE_W6300,
}; };
struct ManualIP { struct ManualIP {
@@ -232,6 +248,8 @@ class EthernetComponent final : public Component {
static constexpr uint32_t LINK_CHECK_INTERVAL = 500; // ms between link/IP polls static constexpr uint32_t LINK_CHECK_INTERVAL = 500; // ms between link/IP polls
#if defined(USE_ETHERNET_W5100) #if defined(USE_ETHERNET_W5100)
static constexpr uint32_t RESET_DELAY_MS = 150; // W5100S PLL lock time static constexpr uint32_t RESET_DELAY_MS = 150; // W5100S PLL lock time
#elif defined(USE_ETHERNET_W6300)
static constexpr uint32_t RESET_DELAY_MS = 100; // W6300 needs 100ms after hardware reset
#else #else
static constexpr uint32_t RESET_DELAY_MS = 10; static constexpr uint32_t RESET_DELAY_MS = 10;
#endif #endif
@@ -239,6 +257,10 @@ class EthernetComponent final : public Component {
Wiznet5500lwIP *eth_{nullptr}; Wiznet5500lwIP *eth_{nullptr};
#elif defined(USE_ETHERNET_W5100) #elif defined(USE_ETHERNET_W5100)
Wiznet5100lwIP *eth_{nullptr}; Wiznet5100lwIP *eth_{nullptr};
#elif defined(USE_ETHERNET_W6100)
Wiznet6100lwIP *eth_{nullptr};
#elif defined(USE_ETHERNET_W6300)
Wiznet6300lwIPFixed *eth_{nullptr};
#elif defined(USE_ETHERNET_ENC28J60) #elif defined(USE_ETHERNET_ENC28J60)
ENC28J60lwIP *eth_{nullptr}; ENC28J60lwIP *eth_{nullptr};
#else #else
@@ -18,9 +18,14 @@ static const char *const TAG = "ethernet";
void EthernetComponent::setup() { void EthernetComponent::setup() {
// Configure SPI pins // Configure SPI pins
#if !defined(USE_ETHERNET_W6300)
SPI.setRX(this->miso_pin_); SPI.setRX(this->miso_pin_);
SPI.setTX(this->mosi_pin_); SPI.setTX(this->mosi_pin_);
SPI.setSCK(this->clk_pin_); SPI.setSCK(this->clk_pin_);
#endif
// W6300 uses PIO QSPI with hardcoded pins, not Arduino SPI.
// SPI pin config is skipped; Wiznet6300lwIPFixed (needsSPI()=false)
// prevents LwipIntfDev::begin() from calling SPI.begin().
// Toggle reset pin if configured // Toggle reset pin if configured
if (this->reset_pin_ >= 0) { if (this->reset_pin_ >= 0) {
@@ -40,6 +45,10 @@ void EthernetComponent::setup() {
this->eth_ = new Wiznet5500lwIP(this->cs_pin_, SPI, this->interrupt_pin_); // NOLINT this->eth_ = new Wiznet5500lwIP(this->cs_pin_, SPI, this->interrupt_pin_); // NOLINT
#elif defined(USE_ETHERNET_W5100) #elif defined(USE_ETHERNET_W5100)
this->eth_ = new Wiznet5100lwIP(this->cs_pin_, SPI, this->interrupt_pin_); // NOLINT this->eth_ = new Wiznet5100lwIP(this->cs_pin_, SPI, this->interrupt_pin_); // NOLINT
#elif defined(USE_ETHERNET_W6100)
this->eth_ = new Wiznet6100lwIP(this->cs_pin_, SPI, this->interrupt_pin_); // NOLINT
#elif defined(USE_ETHERNET_W6300)
this->eth_ = new Wiznet6300lwIPFixed(this->cs_pin_, SPI, this->interrupt_pin_); // NOLINT
#elif defined(USE_ETHERNET_ENC28J60) #elif defined(USE_ETHERNET_ENC28J60)
this->eth_ = new ENC28J60lwIP(this->cs_pin_, SPI, this->interrupt_pin_); // NOLINT this->eth_ = new ENC28J60lwIP(this->cs_pin_, SPI, this->interrupt_pin_); // NOLINT
#endif #endif
@@ -183,9 +192,23 @@ void EthernetComponent::dump_config() {
type_str = "W5500"; type_str = "W5500";
#elif defined(USE_ETHERNET_W5100) #elif defined(USE_ETHERNET_W5100)
type_str = "W5100"; type_str = "W5100";
#elif defined(USE_ETHERNET_W6100)
type_str = "W6100";
#elif defined(USE_ETHERNET_W6300)
type_str = "W6300";
#elif defined(USE_ETHERNET_ENC28J60) #elif defined(USE_ETHERNET_ENC28J60)
type_str = "ENC28J60"; type_str = "ENC28J60";
#endif #endif
#if defined(USE_ETHERNET_W6300)
// W6300 uses PIO QSPI with hardcoded pins — SPI pin fields are not used
ESP_LOGCONFIG(TAG,
"Ethernet:\n"
" Type: %s (PIO QSPI)\n"
" Connected: %s\n"
" IRQ Pin: %d\n"
" Reset Pin: %d",
type_str, YESNO(this->is_connected()), this->interrupt_pin_, this->reset_pin_);
#else
ESP_LOGCONFIG(TAG, ESP_LOGCONFIG(TAG,
"Ethernet:\n" "Ethernet:\n"
" Type: %s\n" " Type: %s\n"
@@ -198,6 +221,7 @@ void EthernetComponent::dump_config() {
" Reset Pin: %d", " Reset Pin: %d",
type_str, YESNO(this->is_connected()), this->clk_pin_, this->miso_pin_, this->mosi_pin_, this->cs_pin_, type_str, YESNO(this->is_connected()), this->clk_pin_, this->miso_pin_, this->mosi_pin_, this->cs_pin_,
this->interrupt_pin_, this->reset_pin_); this->interrupt_pin_, this->reset_pin_);
#endif
this->dump_connect_params_(); this->dump_connect_params_();
} }
+40 -20
View File
@@ -6,9 +6,15 @@ namespace esphome {
namespace gdk101 { namespace gdk101 {
static const char *const TAG = "gdk101"; static const char *const TAG = "gdk101";
static const uint8_t NUMBER_OF_READ_RETRIES = 5; static constexpr uint8_t NUMBER_OF_READ_RETRIES = 5;
static constexpr uint8_t NUMBER_OF_RESET_RETRIES = 10;
static constexpr uint32_t RESET_INTERVAL_ID = 0;
static constexpr uint32_t RESET_INTERVAL_MS = 1000;
void GDK101Component::update() { void GDK101Component::update() {
if (!this->reset_complete_)
return;
uint8_t data[2]; uint8_t data[2];
if (!this->read_dose_1m_(data)) { if (!this->read_dose_1m_(data)) {
this->status_set_warning(LOG_STR("Failed to read dose 1m")); this->status_set_warning(LOG_STR("Failed to read dose 1m"));
@@ -33,26 +39,45 @@ void GDK101Component::update() {
} }
void GDK101Component::setup() { void GDK101Component::setup() {
uint8_t data[2]; if (!this->try_reset_()) {
// first, reset the sensor // Sensor MCU boots slowly after power cycle — retry on a short interval
this->reset_retries_remaining_ = NUMBER_OF_RESET_RETRIES;
this->set_interval(RESET_INTERVAL_ID, RESET_INTERVAL_MS, [this]() {
if (this->try_reset_()) {
if (this->reset_complete_) {
this->update();
}
return;
}
if (--this->reset_retries_remaining_ == 0) {
this->cancel_interval(RESET_INTERVAL_ID);
this->mark_failed(LOG_STR("Reset failed after retries"));
}
});
}
}
/// Attempt to reset the sensor and read firmware version. Returns true on success or hard failure.
bool GDK101Component::try_reset_() {
uint8_t data[2] = {0};
if (!this->reset_sensor_(data)) { if (!this->reset_sensor_(data)) {
this->status_set_error(LOG_STR("Reset failed!")); this->status_set_warning(LOG_STR("Sensor not answering reset, will retry"));
this->mark_failed(); return false;
return;
} }
// sensor should acknowledge success of the reset procedure
if (data[0] != 1) { if (data[0] != 1) {
this->status_set_error(LOG_STR("Reset not acknowledged!")); this->status_set_warning(LOG_STR("Reset not acknowledged, will retry"));
this->mark_failed(); return false;
return;
} }
delay(10); delay(10);
// read firmware version
if (!this->read_fw_version_(data)) { if (!this->read_fw_version_(data)) {
this->status_set_error(LOG_STR("Failed to read firmware version")); this->cancel_interval(RESET_INTERVAL_ID);
this->mark_failed(); this->mark_failed(LOG_STR("Failed to read firmware version"));
return; return true;
} }
this->reset_complete_ = true;
this->status_clear_warning();
this->cancel_interval(RESET_INTERVAL_ID);
return true;
} }
void GDK101Component::dump_config() { void GDK101Component::dump_config() {
@@ -92,12 +117,7 @@ bool GDK101Component::reset_sensor_(uint8_t *data) {
// After sending reset command it looks that sensor start performing reset and is unresponsible during read // After sending reset command it looks that sensor start performing reset and is unresponsible during read
// after a while we can send another reset command and read "0x01" as confirmation // after a while we can send another reset command and read "0x01" as confirmation
// Documentation not going in to such details unfortunately // Documentation not going in to such details unfortunately
if (!this->read_bytes_with_retry_(GDK101_REG_RESET, data, 2)) { return this->read_bytes_with_retry_(GDK101_REG_RESET, data, 2);
ESP_LOGE(TAG, "Updating GDK101 failed!");
return false;
}
return true;
} }
bool GDK101Component::read_dose_1m_(uint8_t *data) { bool GDK101Component::read_dose_1m_(uint8_t *data) {
+3
View File
@@ -44,12 +44,15 @@ class GDK101Component : public PollingComponent, public i2c::I2CDevice {
protected: protected:
bool read_bytes_with_retry_(uint8_t a_register, uint8_t *data, uint8_t len); bool read_bytes_with_retry_(uint8_t a_register, uint8_t *data, uint8_t len);
bool try_reset_();
bool reset_sensor_(uint8_t *data); bool reset_sensor_(uint8_t *data);
bool read_dose_1m_(uint8_t *data); bool read_dose_1m_(uint8_t *data);
bool read_dose_10m_(uint8_t *data); bool read_dose_10m_(uint8_t *data);
bool read_status_(uint8_t *data); bool read_status_(uint8_t *data);
bool read_fw_version_(uint8_t *data); bool read_fw_version_(uint8_t *data);
bool read_measurement_duration_(uint8_t *data); bool read_measurement_duration_(uint8_t *data);
bool reset_complete_{false};
uint8_t reset_retries_remaining_{0};
}; };
} // namespace gdk101 } // namespace gdk101
+2
View File
@@ -300,6 +300,8 @@
#define USE_ETHERNET_OPENETH #define USE_ETHERNET_OPENETH
#define USE_ETHERNET_W5100 #define USE_ETHERNET_W5100
#define USE_ETHERNET_W5500 #define USE_ETHERNET_W5500
#define USE_ETHERNET_W6100
#define USE_ETHERNET_W6300
#define USE_ETHERNET_DM9051 #define USE_ETHERNET_DM9051
#define CONFIG_ETH_SPI_ETHERNET_W5500 1 #define CONFIG_ETH_SPI_ETHERNET_W5500 1
#define CONFIG_ETH_SPI_ETHERNET_DM9051 1 #define CONFIG_ETH_SPI_ETHERNET_DM9051 1
+2 -2
View File
@@ -1,4 +1,4 @@
cryptography==46.0.6 cryptography==46.0.7
voluptuous==0.16.0 voluptuous==0.16.0
PyYAML==6.0.3 PyYAML==6.0.3
paho-mqtt==1.6.1 paho-mqtt==1.6.1
@@ -11,7 +11,7 @@ pyserial==3.5
platformio==6.1.19 platformio==6.1.19
esptool==5.2.0 esptool==5.2.0
click==8.3.2 click==8.3.2
esphome-dashboard==20260210.0 esphome-dashboard==20260408.1
aioesphomeapi==44.12.0 aioesphomeapi==44.12.0
zeroconf==0.148.0 zeroconf==0.148.0
puremagic==1.30 puremagic==1.30
@@ -0,0 +1,18 @@
ethernet:
type: W6100
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,18 @@
ethernet:
type: W6300
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-w6100-rp2040.yaml
@@ -0,0 +1 @@
<<: !include common-w6300-rp2040.yaml