diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 080e70d08b..8057bc70a7 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,7 +1,6 @@ { - // See http://go.microsoft.com/fwlink/?LinkId=827846 - // for the documentation about the extensions.json format "recommendations": [ + "pioarduino.pioarduino-ide", "platformio.platformio-ide" ], "unwantedRecommendations": [ diff --git a/package.json b/package.json index f3e5f4340e..c5f2a314c4 100644 --- a/package.json +++ b/package.json @@ -31,4 +31,4 @@ "engines": { "node": ">=20.0.0" } -} +} \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index bf68233465..21e077707b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -115,6 +115,7 @@ default_envs = abc_wled_controller_v43_V4_S athom_music_esp32_4MB_V4_M adafruit_matrixportal_esp32s3 ;; HUB75 supported, uses standard bootloader + WaveShare_ESP32-S3-ETH ;; Easiest ESP32-S3 with W5500 Ethernet and USB-C for programming ; adafruit_matrixportal_esp32s3_tinyUF2 ;; supports the adafruit "tinyUF2" bootloader ; Go to MoonModules environments for environments @@ -263,6 +264,7 @@ lib_deps = https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.4.2 bitbank2/AnimatedGIF@^1.4.7 https://github.com/Aircoookie/GifDecoder.git#bc3af189b6b1e06946569f6b4287f0b79a860f8e + https://github.com/troyhacks/ETHClass2.git#a260165ec495738f7fecee3a3bfb573f2a281ed7 #For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line #TFT_eSPI #For compatible OLED display uncomment following @@ -326,6 +328,7 @@ lib_deps = ${esp8266.lib8266_deps} ;; use proven library versions for 8266 lib_ignore = NeoESP32RmtHI + ETHClass2 ;; compatibilty flags - same as 0.14.0 which seems to work better on some 8266 boards. Not using PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48 build_flags_compat = @@ -412,7 +415,7 @@ board_build.flash_mode = dio ;; WLEDMM begin ;; tasmota platform - reduces firmaware size by ~280KB -platformTasmota = https://github.com/tasmota/platform-espressif32/releases/download/2023.06.02/platform-espressif32.zip ;; Tasmota Arduino Core 2.0.9 with IPv6 support, based on IDF 4.4.4 +platformTasmota = https://github.com/tasmota/platform-espressif32/releases/download/2024.04.00/platform-espressif32.zip ;; Tasmota Arduino Core 2.0.15 with IPv6 support, based on IDF 4.4.4 platform_packagesTasmota = ;; ** For compiling with latest Frameworks (IDF4.4.x and arduino-esp32 v2.0.x) ** @@ -422,7 +425,7 @@ platformV4_packages_pre = toolchain-riscv32-esp @ 8.4.0+2021r2-patch5 ; required for platform version < 5.3.0, remove this line when upgrading to platform >=5.3.0 ;;; standard V4 platform platformV4 = espressif32@ ~6.3.2 -platformV4_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) +platformV4_packages = platformio/framework-arduinoespressif32 @ 3.20011.230801 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) ;;; experimental: latest V4 platform with latest arduino-esp32 2.0.14 + ESP-IDF 4.4.6 (may or may not work) platformV4_xp = espressif32@ ~6.5.0 @@ -438,7 +441,8 @@ build_flagsV4 = -g ; -D WLEDMM_SLOWPATH ;; don't use I2S for LED bus ; -DARDUINO_USB_CDC_ON_BOOT=0 ;; mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3 -D NO_GFX ; Disable the use of Adafruit_GFX by the HUB75 driver - + -D BOARD_HAS_PSRAM + -D WLED_USE_ETHERNET ;;; V4.4.x libraries (without LOROL_LITTLEFS; with newer NeoPixelBus) lib_depsV4 = esp32async/AsyncTCP @ 3.4.7 @@ -458,12 +462,14 @@ lib_depsV4 = ;; platform = espressif32@ ~6.3.2 ;; platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) -;; Tasmota Arduino Core 2.0.9 with IPv6 support, based on IDF 4.4.4. Warning: all kernel error asserts removed +;; Tasmota Arduino Core 2.0.9 with IPv6 support, based on IDF 4.4.7. Warning: all kernel error asserts removed platform = ${esp32.platformTasmota} platform_packages = ${esp32.platform_packagesTasmota} build_unflags = ${common.build_unflags} build_flags = -g + -D BOARD_HAS_PSRAM + -D WLED_USE_ETHERNET -Wshadow=compatible-local ;; emit warning in case a local variable "shadows" another local one -DARDUINO_ARCH_ESP32 -DESP32 #-DCONFIG_LITTLEFS_FOR_IDF_3_2 @@ -473,6 +479,8 @@ build_flags = -g -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3 ; -D WLEDMM_TWOPATH ;; use I2S1 as the second bus --> slightly faster on some setups ; -D WLEDMM_SLOWPATH ;; don't use I2S for LED bus + -D BOARD_HAS_PSRAM + -D WLED_USE_ETHERNET default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv lib_deps = esp32async/AsyncTCP @ 3.4.7 @@ -511,7 +519,8 @@ build_flags = -g ;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry: ;; ARDUINO_USB_CDC_ON_BOOT ; -D WLED_USE_SHARED_RMT ;; un-comment to use the standard RMT driver instead of RMTHI - + -D BOARD_HAS_PSRAM + -D WLED_USE_ETHERNET lib_deps = esp32async/AsyncTCP @ 3.4.7 makuna/NeoPixelBus @ 2.7.9 ;; experimental - reduces LED glitches on -S2 @@ -538,7 +547,8 @@ build_flags = -g ;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry: ;; ARDUINO_USB_CDC_ON_BOOT -D WLED_USE_SHARED_RMT ;; don't use the RMTHI driver (not compatible with -C3) - + -D BOARD_HAS_PSRAM + -D WLED_USE_ETHERNET lib_deps = esp32async/AsyncTCP @ 3.4.7 makuna/NeoPixelBus @ 2.7.9 ;; experimental @@ -569,6 +579,8 @@ build_flags = -g ;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry: ;; ARDUINO_USB_MODE, ARDUINO_USB_CDC_ON_BOOT ;-D WLED_USE_SHARED_RMT ;; un-comment to use the standard RMT driver instead of RMTHI + -D BOARD_HAS_PSRAM + -D WLED_USE_ETHERNET lib_deps = esp32async/AsyncTCP @ 3.4.7 makuna/NeoPixelBus @ 2.7.9 ;; experimental @@ -1189,6 +1201,8 @@ AR_build_flags = -D USERMOD_AUDIOREACTIVE -D UM_AUDIOREACTIVE_USE_NEW_FFT ;; WLE AR_lib_deps = https://github.com/softhack007/arduinoFFT.git#develop @ 1.9.2 ;; used for USERMOD_AUDIOREACTIVE - optimized version, 10% faster on -S2/-C3 animartrix_build_flags = -D USERMOD_ANIMARTRIX ;; WLEDMM usermod: CC BY-NC 3.0 licensed effects by Stefan Petrick + -fexceptions ; Enable (needed for try/catch) + ;; @TroyHacks to @softhack007: This is just to make my builds go green with your new code. :) animartrix_lib_deps = https://github.com/netmindz/animartrix.git#af02653aaabdce08929389ca16d0d86071573dd4 ;; custom PSRAM allocator animartrix_lib_ignore = animartrix ;; to remove the animartrix lib dependancy (saves a few bytes) @@ -1197,6 +1211,8 @@ DMXin_lib_deps = https://github.com/someweisguy/esp_dmx.git#47db25d8c515e76fabcf DMXin_lib_ignore = esp_dmx ;; to remove the esp-dmx lib dependancy (saves a few bytes) HUB75_build_flags = + -Wno-shadow ;; Shush, you. + -Wno-parentheses ;; Shush, you. -D WLED_ENABLE_HUB75MATRIX ;; - requires ESP-IDF v4.4.x ;-D SPIRAM_FRAMEBUFFER ;; ONLY SUPPORTED ON ESP32-S3 VARIANTS WITH OCTAL (not quad) SPIRAM/PSRAM -D NO_GFX ;; Disable the use of Adafruit_GFX by the HUB75 driver @@ -3311,3 +3327,12 @@ build_flags = ${env:adafruit_matrixportal_esp32s3_wled.build_flags} [env:adafruit_matrixportal_esp32s3] ;; this buildenv is just an alias for the matrixportal UF2 build, to keep 3rd party build tools happy. extends = env:adafruit_matrixportal_esp32s3_wled + +[env:WaveShare_ESP32-S3-ETH] +;; This is the easiest-to-use W5500 board as it has USB-C for programming +extends = env:esp32S3_16MB_PSRAM_M_HUB75 +build_unflags = -D LEDPIN -D BTNPIN -D RLYPIN -D I2S_SDPIN -D I2S_CKPIN -D I2S_WSPIN -D ARDUINO_TTGO_T7_S3 +build_flags = ${env:esp32S3_16MB_PSRAM_M_HUB75.build_flags} + -D WLED_USE_ETHERNET + -D WLED_ETH_DEFAULT=16 + -D LEDPIN=-1 -D BTNPIN=-1 -D RLYPIN=-1 -D I2S_SDPIN=-1 -D I2S_CKPIN=-1 -D I2S_WSPIN=-1 diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 069b303aa0..5b40f4607b 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -351,6 +351,35 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { CJSON(spi_mosi, hw_if_spi[0]); CJSON(spi_sclk, hw_if_spi[1]); CJSON(spi_miso, hw_if_spi[2]); + + #ifdef CONFIG_ETH_SPI_ETHERNET_W5500 + CJSON(spi_cs, hw_if_spi[3]); + CJSON(spi_int, hw_if_spi[4]); + CJSON(spi_rst, hw_if_spi[5]); + + JsonObject hw_if_spi_use = hw[F("if")][F("spi-use")]; + if (!hw_if_spi_use[F("use-for-w5500")].isNull()) { + spi_use_for_w5500 = hw_if_spi_use[F("use-for-w5500")].as(); + if (spi_use_for_w5500) USER_PRINTLN("use-for-w5500 is TRUE"); + if (!spi_use_for_w5500) USER_PRINTLN("use-for-w5500 is FALSE"); + } else { + USER_PRINTLN("use-for-w5500 was not found"); + } + + PinManagerPinType spi[6] = { { spi_mosi, true }, { spi_miso, true }, { spi_sclk, true } , { spi_cs, true } , { spi_int, true } , { spi_rst, true } }; + if (spi_mosi >= 0 && spi_sclk >= 0 && pinManager.allocateMultiplePins(spi, 6, PinOwner::HW_SPI)) { + if (!spi_use_for_w5500) { + #ifdef ESP32 + SPI.begin(spi_sclk, spi_miso, spi_mosi); // SPI global uses VSPI on ESP32 and FSPI on C3, S3 + #else + SPI.begin(); + #endif + } + DEBUG_PRINTF("pinmgr success for global spi %d %d %d %d %d %d\n", spi_mosi, spi_miso, spi_sclk, spi_cs, spi_int, spi_rst); + } else { + DEBUG_PRINTF("pinmgr not success for global spi %d %d %d %d %d %d\n", spi_mosi, spi_miso, spi_sclk, spi_cs, spi_int, spi_rst); + } + #else PinManagerPinType spi[3] = { { spi_mosi, true }, { spi_miso, true }, { spi_sclk, true } }; if (spi_mosi >= 0 && spi_sclk >= 0 && pinManager.allocateMultiplePins(spi, 3, PinOwner::HW_SPI)) { #ifdef ESP32 @@ -362,7 +391,12 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { } else { DEBUG_PRINTF("pinmgr not success for global spi %d %d %d\n", spi_mosi, spi_miso, spi_sclk); } + #endif + // NOTE: Ethernet initialization must be deferred until after deserializeConfigFromFS() releases the JSON buffer lock + // and completes. Calling initEthernet() here can trigger async events that may try to serialize config while + // the lock is held, potentially corrupting cfg.json. The fromFS parameter signals this case. + //int hw_status_pin = hw[F("status")]["pin"]; // -1 JsonObject light = doc[F("light")]; @@ -682,6 +716,14 @@ void deserializeConfigFromFS() { bool needsSave = deserializeConfig(doc.as(), true); releaseJSONBufferLock(); + // Initialize Ethernet AFTER releasing the JSON buffer lock to prevent race conditions. + // Ethernet events can trigger async operations that may try to serialize config. + #if defined(WLED_USE_ETHERNET) && defined(CONFIG_ETH_SPI_ETHERNET_W5500) + if (spi_use_for_w5500 && spi_mosi >= 0 && spi_sclk >= 0) { + WLED::instance().initEthernet(); + } + #endif + if (needsSave) serializeConfig(); // usermods required new parameters } @@ -742,25 +784,38 @@ void serializeConfig() { wifi[F("phy")] = force802_3g; #ifdef WLED_USE_ETHERNET + + #ifndef CONFIG_ETH_SPI_ETHERNET_W5500 + #define ETH_PHY_W5500 ETH_PHY_MAX + #endif + JsonObject ethernet = doc.createNestedObject("eth"); ethernet["type"] = ethernetType; if (ethernetType != WLED_ETH_NONE && ethernetType < WLED_NUM_ETH_TYPES) { JsonArray pins = ethernet.createNestedArray("pin"); - for (uint8_t p=0; p=0) pins.add(ethernetBoards[ethernetType].eth_power); - if (ethernetBoards[ethernetType].eth_mdc>=0) pins.add(ethernetBoards[ethernetType].eth_mdc); - if (ethernetBoards[ethernetType].eth_mdio>=0) pins.add(ethernetBoards[ethernetType].eth_mdio); - switch (ethernetBoards[ethernetType].eth_clk_mode) { - case ETH_CLOCK_GPIO0_IN: - case ETH_CLOCK_GPIO0_OUT: - pins.add(0); - break; - case ETH_CLOCK_GPIO16_OUT: - pins.add(16); - break; - case ETH_CLOCK_GPIO17_OUT: - pins.add(17); - break; + for (uint8_t p = 0; p < WLED_ETH_RSVD_PINS_COUNT; p++) pins.add(esp32_nonconfigurable_ethernet_pins[p].pin); + if (ethernetBoards[ethernetType].eth_power >= 0) pins.add(ethernetBoards[ethernetType].eth_power); + if (ethernetBoards[ethernetType].eth_mdc >= 0) pins.add(ethernetBoards[ethernetType].eth_mdc); + if (ethernetBoards[ethernetType].eth_mdio >= 0) pins.add(ethernetBoards[ethernetType].eth_mdio); + if (ethernetBoards[ethernetType].eth_miso_pin >= 0) pins.add(ethernetBoards[ethernetType].eth_miso_pin); + if (ethernetBoards[ethernetType].eth_mosi_pin >= 0) pins.add(ethernetBoards[ethernetType].eth_mosi_pin); + if (ethernetBoards[ethernetType].eth_cs_pin >= 0) pins.add(ethernetBoards[ethernetType].eth_cs_pin); + if (ethernetBoards[ethernetType].eth_rst_pin >= 0) pins.add(ethernetBoards[ethernetType].eth_rst_pin); + if (ethernetBoards[ethernetType].eth_int_pin >= 0) pins.add(ethernetBoards[ethernetType].eth_int_pin); + if (ethernetBoards[ethernetType].eth_sclk_pin >= 0) pins.add(ethernetBoards[ethernetType].eth_sclk_pin); + if (ethernetBoards[ethernetType].eth_type != ETH_PHY_W5500) { + switch (ethernetBoards[ethernetType].eth_clk_mode) { + case ETH_CLOCK_GPIO0_IN: + case ETH_CLOCK_GPIO0_OUT: + pins.add(0); + break; + case ETH_CLOCK_GPIO16_OUT: + pins.add(16); + break; + case ETH_CLOCK_GPIO17_OUT: + pins.add(17); + break; + } } } #endif @@ -889,6 +944,14 @@ void serializeConfig() { hw_if_spi.add(spi_mosi); hw_if_spi.add(spi_sclk); hw_if_spi.add(spi_miso); + #ifdef CONFIG_ETH_SPI_ETHERNET_W5500 + hw_if_spi.add(spi_cs); + hw_if_spi.add(spi_int); + hw_if_spi.add(spi_rst); + + JsonObject spi_use = hw_if.createNestedObject(F("spi-use")); + spi_use[F("use-for-w5500")] = (spi_use_for_w5500) ? spi_use_for_w5500 : false; + #endif //JsonObject hw_status = hw.createNestedObject("status"); //hw_status["pin"] = -1; diff --git a/wled00/const.h b/wled00/const.h index c81854dad0..989d9422fa 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -309,7 +309,7 @@ #define BTN_TYPE_TOUCH_SWITCH 9 //WLEDMM not yet supported //Ethernet board types -#define WLED_NUM_ETH_TYPES 15 //WLEDMM +1 for Olimex ESP32-Gateway +#define WLED_NUM_ETH_TYPES 18 //WLEDMM +1 for Olimex ESP32-Gateway + 3 for W5500 #define WLED_ETH_NONE 0 #define WLED_ETH_WT32_ETH01 1 @@ -326,6 +326,9 @@ #define WLED_ETH_LILYGO_T_POE_PRO 12 #define WLED_ETH_GLEDOPTO 13 #define WLED_ETH_OLIMEX_GTW 14 +#define WLED_ETH_TTGO_T_ETH_LITE_S3 15 +#define WLED_ETH_WAVESHARE_ESP32_S3_ETH 16 +#define WLED_ETH_W5500_GENERIC 17 //Hue error codes #define HUE_ERROR_INACTIVE 0 @@ -608,6 +611,15 @@ #ifndef HW_PIN_MISOSPI #define HW_PIN_MISOSPI -1 //WLEDMM if not defined -1 will be used (not MISO/19) #endif +#ifndef HW_PIN_CSSPI + #define HW_PIN_CSSPI -1 //WLEDMM if not defined -1 will be used (not MISO/19) +#endif +#ifndef HW_PIN_INTSPI + #define HW_PIN_INTSPI -1 //WLEDMM if not defined -1 will be used (not MISO/19) +#endif +#ifndef HW_PIN_RSTSPI + #define HW_PIN_RSTSPI -1 //WLEDMM if not defined -1 will be used (not MISO/19) +#endif // WLEDMM: IRAM_ATTR for 8266 causes error: section `.text1' will not fit in region `iram1_0_seg' // error only in MM, not in upstream... tbd: find out why diff --git a/wled00/data/settings_um.htm b/wled00/data/settings_um.htm index d0dd98f3d2..c5703a5898 100644 --- a/wled00/data/settings_um.htm +++ b/wled00/data/settings_um.htm @@ -333,6 +333,13 @@ addField("if:MOSI", "pin", -1, false); addField("if:MISO", "pin", -1, false); addField("if:SCLK", "pin", -1, false); + // W5500-specific fields - only show if server supports W5500 (detected by checking if spi-use object exists) + if (json.hw?.if?.["spi-use"]) { + addField("if:CS", "pin", -1, false); + addField("if:INT", "pin", -1, false); + addField("if:RST", "pin", -1, false); + addField("if:use_for_w5500", "use", json.hw.if["spi-use"]["use-for-w5500"] ?? false); + } } if (isO(umCfg)) { //WLEDMM: read url parameter. e.g. um=AudioReactive and if set only add the usermod with the same name diff --git a/wled00/data/settings_wifi.htm b/wled00/data/settings_wifi.htm index a0b8778a1e..7635c0a549 100644 --- a/wled00/data/settings_wifi.htm +++ b/wled00/data/settings_wifi.htm @@ -195,7 +195,7 @@

Wireless Remote

Ethernet Type

- @@ -211,6 +211,9 @@

Ethernet Type

+ + +


diff --git a/wled00/network.cpp b/wled00/network.cpp index a6b4da8cc1..20745b4a15 100644 --- a/wled00/network.cpp +++ b/wled00/network.cpp @@ -5,11 +5,16 @@ #ifdef WLED_USE_ETHERNET #pragma message "Ethernet support enabled" +#ifdef CONFIG_ETH_SPI_ETHERNET_W5500 +#pragma message "W5500 support should be enabled" +#endif // The following six pins are neither configurable nor // can they be re-assigned through IOMUX / GPIO matrix. // See https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-ethernet-kit-v1.1.html#ip101gri-phy-interface -const managed_pin_type esp32_nonconfigurable_ethernet_pins[WLED_ETH_RSVD_PINS_COUNT] = { + +#if defined(CONFIG_ETH_PHY_INTERFACE_RMII) || (defined(ESP_IDF_VERSION_MAJOR) && ESP_IDF_VERSION_MAJOR == 3) +managed_pin_type esp32_nonconfigurable_ethernet_pins[6] = { { 21, true }, // RMII EMAC TX EN == When high, clocks the data on TXD0 and TXD1 to transmitter { 19, true }, // RMII EMAC TXD0 == First bit of transmitted data { 22, true }, // RMII EMAC TXD1 == Second bit of transmitted data @@ -17,6 +22,13 @@ const managed_pin_type esp32_nonconfigurable_ethernet_pins[WLED_ETH_RSVD_PINS_CO { 26, false }, // RMII EMAC RXD1 == Second bit of received data { 27, true }, // RMII EMAC CRS_DV == Carrier Sense and RX Data Valid }; +#else +managed_pin_type esp32_nonconfigurable_ethernet_pins[WLED_ETH_RSVD_PINS_COUNT] = {}; +#endif + +#ifndef CONFIG_ETH_SPI_ETHERNET_W5500 + #define ETH_PHY_W5500 ETH_PHY_MAX +#endif const ethernet_settings ethernetBoards[] = { // None @@ -33,6 +45,12 @@ const ethernet_settings ethernetBoards[] = { 16, // eth_power, 23, // eth_mdc, 18, // eth_mdio, + GPIO_NUM_NC, // eth_mosi_pin, + GPIO_NUM_NC, // eth_miso_pin, + GPIO_NUM_NC, // eth_sclk_pin, + GPIO_NUM_NC, // eth_cs_pin, + GPIO_NUM_NC, // eth_int_pin, + GPIO_NUM_NC, // eth_rst_pin, ETH_PHY_LAN8720, // eth_type, ETH_CLOCK_GPIO0_IN // eth_clk_mode }, @@ -43,6 +61,12 @@ const ethernet_settings ethernetBoards[] = { 12, // eth_power, 23, // eth_mdc, 18, // eth_mdio, + GPIO_NUM_NC, // eth_mosi_pin, + GPIO_NUM_NC, // eth_miso_pin, + GPIO_NUM_NC, // eth_sclk_pin, + GPIO_NUM_NC, // eth_cs_pin, + GPIO_NUM_NC, // eth_int_pin, + GPIO_NUM_NC, // eth_rst_pin, ETH_PHY_LAN8720, // eth_type, ETH_CLOCK_GPIO17_OUT // eth_clk_mode }, @@ -53,6 +77,12 @@ const ethernet_settings ethernetBoards[] = { -1, // eth_power, 16, // eth_mdc, 17, // eth_mdio, + GPIO_NUM_NC, // eth_mosi_pin, + GPIO_NUM_NC, // eth_miso_pin, + GPIO_NUM_NC, // eth_sclk_pin, + GPIO_NUM_NC, // eth_cs_pin, + GPIO_NUM_NC, // eth_int_pin, + GPIO_NUM_NC, // eth_rst_pin, ETH_PHY_LAN8720, // eth_type, ETH_CLOCK_GPIO0_IN // eth_clk_mode }, @@ -63,6 +93,12 @@ const ethernet_settings ethernetBoards[] = { 5, // eth_power, 23, // eth_mdc, 18, // eth_mdio, + GPIO_NUM_NC, // eth_mosi_pin, + GPIO_NUM_NC, // eth_miso_pin, + GPIO_NUM_NC, // eth_sclk_pin, + GPIO_NUM_NC, // eth_cs_pin, + GPIO_NUM_NC, // eth_int_pin, + GPIO_NUM_NC, // eth_rst_pin, ETH_PHY_LAN8720, // eth_type, ETH_CLOCK_GPIO17_OUT // eth_clk_mode }, @@ -73,6 +109,12 @@ const ethernet_settings ethernetBoards[] = { 5, // eth_power, 23, // eth_mdc, 18, // eth_mdio, + GPIO_NUM_NC, // eth_mosi_pin, + GPIO_NUM_NC, // eth_miso_pin, + GPIO_NUM_NC, // eth_sclk_pin, + GPIO_NUM_NC, // eth_cs_pin, + GPIO_NUM_NC, // eth_int_pin, + GPIO_NUM_NC, // eth_rst_pin, ETH_PHY_LAN8720, // eth_type, ETH_CLOCK_GPIO17_OUT // eth_clk_mode }, @@ -83,6 +125,12 @@ const ethernet_settings ethernetBoards[] = { -1, // eth_power, 23, // eth_mdc, 18, // eth_mdio, + GPIO_NUM_NC, // eth_mosi_pin, + GPIO_NUM_NC, // eth_miso_pin, + GPIO_NUM_NC, // eth_sclk_pin, + GPIO_NUM_NC, // eth_cs_pin, + GPIO_NUM_NC, // eth_int_pin, + GPIO_NUM_NC, // eth_rst_pin, ETH_PHY_LAN8720, // eth_type, ETH_CLOCK_GPIO17_OUT // eth_clk_mode }, @@ -93,6 +141,12 @@ const ethernet_settings ethernetBoards[] = { 5, // eth_power, 23, // eth_mdc, 18, // eth_mdio, + GPIO_NUM_NC, // eth_mosi_pin, + GPIO_NUM_NC, // eth_miso_pin, + GPIO_NUM_NC, // eth_sclk_pin, + GPIO_NUM_NC, // eth_cs_pin, + GPIO_NUM_NC, // eth_int_pin, + GPIO_NUM_NC, // eth_rst_pin, ETH_PHY_IP101, // eth_type, ETH_CLOCK_GPIO0_IN // eth_clk_mode }, @@ -103,6 +157,12 @@ const ethernet_settings ethernetBoards[] = { -1, // eth_power, 23, // eth_mdc, 18, // eth_mdio, + GPIO_NUM_NC, // eth_mosi_pin, + GPIO_NUM_NC, // eth_miso_pin, + GPIO_NUM_NC, // eth_sclk_pin, + GPIO_NUM_NC, // eth_cs_pin, + GPIO_NUM_NC, // eth_int_pin, + GPIO_NUM_NC, // eth_rst_pin, ETH_PHY_LAN8720, // eth_type, ETH_CLOCK_GPIO17_OUT // eth_clk_mode }, @@ -113,6 +173,12 @@ const ethernet_settings ethernetBoards[] = { 5, // eth_power, 23, // eth_mdc, 33, // eth_mdio, + GPIO_NUM_NC, // eth_mosi_pin, + GPIO_NUM_NC, // eth_miso_pin, + GPIO_NUM_NC, // eth_sclk_pin, + GPIO_NUM_NC, // eth_cs_pin, + GPIO_NUM_NC, // eth_int_pin, + GPIO_NUM_NC, // eth_rst_pin, ETH_PHY_LAN8720, // eth_type, ETH_CLOCK_GPIO17_OUT // eth_clk_mode }, @@ -123,6 +189,12 @@ const ethernet_settings ethernetBoards[] = { 5, // eth_power, 23, // eth_mdc, 18, // eth_mdio, + GPIO_NUM_NC, // eth_mosi_pin, + GPIO_NUM_NC, // eth_miso_pin, + GPIO_NUM_NC, // eth_sclk_pin, + GPIO_NUM_NC, // eth_cs_pin, + GPIO_NUM_NC, // eth_int_pin, + GPIO_NUM_NC, // eth_rst_pin, ETH_PHY_LAN8720, // eth_type, ETH_CLOCK_GPIO17_OUT // eth_clk_mode }, @@ -133,6 +205,12 @@ const ethernet_settings ethernetBoards[] = { 12, // eth_power, 23, // eth_mdc, 18, // eth_mdio, + GPIO_NUM_NC, // eth_mosi_pin, + GPIO_NUM_NC, // eth_miso_pin, + GPIO_NUM_NC, // eth_sclk_pin, + GPIO_NUM_NC, // eth_cs_pin, + GPIO_NUM_NC, // eth_int_pin, + GPIO_NUM_NC, // eth_rst_pin, ETH_PHY_LAN8720, // eth_type, ETH_CLOCK_GPIO0_OUT // eth_clk_mode }, @@ -144,6 +222,12 @@ const ethernet_settings ethernetBoards[] = { 5, // eth_power, 23, // eth_mdc, 18, // eth_mdio, + GPIO_NUM_NC, // eth_mosi_pin, + GPIO_NUM_NC, // eth_miso_pin, + GPIO_NUM_NC, // eth_sclk_pin, + GPIO_NUM_NC, // eth_cs_pin, + GPIO_NUM_NC, // eth_int_pin, + GPIO_NUM_NC, // eth_rst_pin, ETH_PHY_LAN8720, // eth_type, ETH_CLOCK_GPIO0_OUT // eth_clk_mode }, @@ -154,19 +238,78 @@ const ethernet_settings ethernetBoards[] = { 5, // eth_power, 23, // eth_mdc, 33, // eth_mdio, + GPIO_NUM_NC, // eth_mosi_pin, + GPIO_NUM_NC, // eth_miso_pin, + GPIO_NUM_NC, // eth_sclk_pin, + GPIO_NUM_NC, // eth_cs_pin, + GPIO_NUM_NC, // eth_int_pin, + GPIO_NUM_NC, // eth_rst_pin, ETH_PHY_LAN8720, // eth_type, ETH_CLOCK_GPIO0_IN // eth_clk_mode }, - //WLEDMM: Olimex-ESP32-Gateway (like QuinLed-ESP32-Ethernet { 0, // eth_address, 5, // eth_power, 23, // eth_mdc, 18, // eth_mdio, + GPIO_NUM_NC, // eth_mosi_pin, + GPIO_NUM_NC, // eth_miso_pin, + GPIO_NUM_NC, // eth_sclk_pin, + GPIO_NUM_NC, // eth_cs_pin, + GPIO_NUM_NC, // eth_int_pin, + GPIO_NUM_NC, // eth_rst_pin, ETH_PHY_LAN8720, // eth_type, ETH_CLOCK_GPIO17_OUT // eth_clk_mode + }, + + // WLEDMM: TTGO T-ETH-Lite S3 (W5500) ☾ + { + 1, // eth_address, + GPIO_NUM_NC, // eth_power, + GPIO_NUM_NC, // eth_mdc, + GPIO_NUM_NC, // eth_mdio, + 12, // eth_mosi_pin, + 11, // eth_miso_pin, + 10, // eth_sclk_pin, + 9, // eth_cs_pin, + 13, // eth_int_pin, + 14, // eth_rst_pin, + ETH_PHY_W5500, // eth_type, + ETH_CLOCK_GPIO0_OUT // eth_clk_mode (ignored for W5500) + }, + + // WaveShare ESP32-S3-ETH (W5500) ☾ + { + 1, // eth_address, + GPIO_NUM_NC, // eth_power, + GPIO_NUM_NC, // eth_mdc, + GPIO_NUM_NC, // eth_mdio, + 11, // eth_mosi_pin, + 12, // eth_miso_pin, + 13, // eth_sclk_pin, + 14, // eth_cs_pin, + 10, // eth_int_pin, + 9, // eth_rst_pin, + ETH_PHY_W5500, // eth_type, + ETH_CLOCK_GPIO0_OUT // eth_clk_mode (ignored for W5500) + }, + + // W5500 Generic, based on some ESP32 Troy had lying around ☾ + { + 1, // eth_address, + GPIO_NUM_NC, // eth_power, + GPIO_NUM_NC, // eth_mdc, + GPIO_NUM_NC, // eth_mdio, + 13, // eth_mosi_pin, + 12, // eth_miso_pin, + 14, // eth_sclk_pin, + 25, // eth_cs_pin, + 27, // eth_int_pin, + 26, // eth_rst_pin, + ETH_PHY_W5500, // eth_type, + ETH_CLOCK_GPIO0_OUT // eth_clk_mode (ignored for W5500) } }; @@ -219,6 +362,7 @@ void WiFiEvent(WiFiEvent_t event) } else { DEBUG_PRINTLN(F("WiFi Connected. No ETH")); } + interfacesInited = false; break; case SYSTEM_EVENT_ETH_CONNECTED: { @@ -233,6 +377,12 @@ void WiFiEvent(WiFiEvent_t event) prepareHostname(hostname); ETH.setHostname(hostname); showWelcomePage = false; + USER_PRINTF("Ethernet link is %sup. Speed is %u mbit and link is %sfull duplex! (MAC: ", ETH.linkUp() ? "" : "not ", ETH.linkSpeed(), ETH.fullDuplex() ? "" : "not "); + USER_PRINT(ETH.macAddress()); + USER_PRINTLN(")"); + escapedMac = ETH.macAddress(); + escapedMac.replace(":", ""); + escapedMac.toLowerCase(); break; } case SYSTEM_EVENT_ETH_DISCONNECTED: @@ -246,6 +396,7 @@ void WiFiEvent(WiFiEvent_t event) break; #endif default: + DEBUG_PRINTF("Unhandled Network event: %d\n", (int)event); break; } } diff --git a/wled00/pin_manager.cpp b/wled00/pin_manager.cpp index 9ccdf1e02c..47d2132dd6 100644 --- a/wled00/pin_manager.cpp +++ b/wled00/pin_manager.cpp @@ -105,9 +105,18 @@ String PinManagerClass::getPinSpecialText(int gpio) { // special purpose PIN in if (isPinAllocated(gpio)) { if ((gpio == i2c_sda) && (getPinOwner(gpio) == PinOwner::HW_I2C)) return(F("I2C SDA")); if ((gpio == i2c_scl) && (getPinOwner(gpio) == PinOwner::HW_I2C)) return(F("I2C SCL")); - if ((gpio == spi_sclk) && (getPinOwner(gpio) == PinOwner::HW_SPI)) return(F("SPI SLK / SCK")); - if ((gpio == spi_mosi) && (getPinOwner(gpio) == PinOwner::HW_SPI)) return(F("SPI PICO / MOSI")); - if ((gpio == spi_miso) && (getPinOwner(gpio) == PinOwner::HW_SPI)) return(F("SPI POCI / MISO")); + #if defined(CONFIG_ETH_SPI_ETHERNET_W5500) + if ((gpio == spi_mosi) && (getPinOwner(gpio) == PinOwner::HW_SPI)) return spi_use_for_w5500 ? F("SPI Ethernet MOSI / PICO") : F("SPI MOSI / PICO"); + if ((gpio == spi_miso) && (getPinOwner(gpio) == PinOwner::HW_SPI)) return spi_use_for_w5500 ? F("SPI Ethernet MISO / POCI") : F("SPI MISO / POCI"); + if ((gpio == spi_sclk) && (getPinOwner(gpio) == PinOwner::HW_SPI)) return spi_use_for_w5500 ? F("SPI Ethernet SCLK / SCK") : F("SPI SCLK / SCK"); + if ((gpio == spi_cs) && (getPinOwner(gpio) == PinOwner::HW_SPI)) return spi_use_for_w5500 ? F("SPI Ethernet CS / SS") : F("SPI CS / SS"); + if ((gpio == spi_int) && (getPinOwner(gpio) == PinOwner::HW_SPI)) return spi_use_for_w5500 ? F("SPI Ethernet INT / IRQ") : F("SPI INT / IRQ"); + if ((gpio == spi_rst) && (getPinOwner(gpio) == PinOwner::HW_SPI)) return spi_use_for_w5500 ? F("SPI Ethernet RST / RSET") : F("SPI RST / RSET"); + #else + if ((gpio == spi_mosi) && (getPinOwner(gpio) == PinOwner::HW_SPI)) return F("SPI MOSI / PICO"); + if ((gpio == spi_miso) && (getPinOwner(gpio) == PinOwner::HW_SPI)) return F("SPI MISO / POCI"); + if ((gpio == spi_sclk) && (getPinOwner(gpio) == PinOwner::HW_SPI)) return F("SPI SCLK / SCK"); + #endif } // MCU special PINS #ifdef ARDUINO_ARCH_ESP32 @@ -167,9 +176,9 @@ String PinManagerClass::getPinSpecialText(int gpio) { // special purpose PIN in // hardware special purpose PINS. part2 - default pins if (gpio == i2c_sda) return(F("(default) I2C SDA")); if (gpio == i2c_scl) return(F("(default) I2C SCL")); - if (gpio == spi_sclk) return(F("(default) SPI SLK / SCK")); - if (gpio == spi_mosi) return(F("(default) SPI PICO / MOSI")); - if (gpio == spi_miso) return(F("(default) SPI POCI / MISO")); + if (gpio == spi_mosi) return(F("(default) SPI MOSI / PICO")); + if (gpio == spi_miso) return(F("(default) SPI MISO / POCI")); + if (gpio == spi_sclk) return(F("(default) SPI SCLK / SCK")); //if ((gpio == spi_cs) || ((gpio == HW_PIN_CS) && (spi_cs < 0))) return(F("(default) SPI CS / SS")); #if defined(WLED_USE_SD_MMC) || defined(WLED_USE_SD_SPI) || defined(SD_ADAPTER) if ((gpio == HW_PIN_CSSPI)) return(F("(default) SPI CS / SS")); // no part of usermod default settings, currently only needed by SD_CARD usermod diff --git a/wled00/set.cpp b/wled00/set.cpp index 560de9b653..c574f030b6 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -59,6 +59,9 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) #ifdef WLED_USE_ETHERNET ethernetType = request->arg(F("ETH")).toInt(); + #ifdef CONFIG_ETH_SPI_ETHERNET_W5500 + DEBUG_PRINTF("spi_use_for_w5500 is %s\n", spi_use_for_w5500 ? "true" : "false"); + #endif WLED::instance().initEthernet(); #endif @@ -593,21 +596,39 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) int8_t hw_miso_pin = -2;//!request->arg(F("MISOpin")).length() ? -1 : (int)request->arg(F("MISOpin")).toInt(); int8_t hw_sclk_pin = -2;//!request->arg(F("SCLKpin")).length() ? -1 : (int)request->arg(F("SCLKpin")).toInt(); + #ifdef CONFIG_ETH_SPI_ETHERNET_W5500 + int8_t hw_cs_pin = -2; + int8_t hw_int_pin = -2; + int8_t hw_rst_pin = -2; + bool use_spi_for_w5500 = false; + #endif + //WLEDMM: :pin values have 2 occurrences: the type and the value, we need the value int paramsNr = request->params(); AsyncWebParameter* p_prev = nullptr; - for (int i=0;igetParam(i); - if (p_prev != nullptr && p->name() == p_prev->name()) - { + if (p_prev != nullptr && p->name() == p_prev->name()) { + USER_PRINT(p->name()); + USER_PRINT("="); + USER_PRINTLN(p->value()); if (p->name() == "if:SDA:pin") hw_sda_pin = p->value().toInt(); if (p->name() == "if:SCL:pin") hw_scl_pin = p->value().toInt(); if (p->name() == "if:MOSI:pin") hw_mosi_pin = p->value().toInt(); if (p->name() == "if:MISO:pin") hw_miso_pin = p->value().toInt(); if (p->name() == "if:SCLK:pin") hw_sclk_pin = p->value().toInt(); + #ifdef CONFIG_ETH_SPI_ETHERNET_W5500 + if (p->name() == "if:CS:pin") hw_cs_pin = p->value().toInt(); + if (p->name() == "if:INT:pin") hw_int_pin = p->value().toInt(); + if (p->name() == "if:RST:pin") hw_rst_pin = p->value().toInt(); + if (p->name() == "if:use_for_w5500:use") { + use_spi_for_w5500 = p->value(); + USER_PRINTF("**** use_spi_for_w5500 == %d\n", use_spi_for_w5500); + } + #endif } p_prev = p; - } + } #ifdef ESP8266 // cannot change pins on ESP8266 --> actually we can @@ -643,11 +664,41 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) if (hw_miso_pin >= 0 && hw_miso_pin != HW_PIN_MISOSPI) hw_mosi_pin = HW_PIN_MISOSPI; if (hw_sclk_pin >= 0 && hw_sclk_pin != HW_PIN_CLOCKSPI) hw_sclk_pin = HW_PIN_CLOCKSPI; #endif + + spi_mosi = hw_mosi_pin; + spi_miso = hw_miso_pin; + spi_sclk = hw_sclk_pin; + + #ifdef CONFIG_ETH_SPI_ETHERNET_W5500 + PinManagerPinType spi[6] = { { hw_mosi_pin, true }, { hw_miso_pin, true }, { hw_sclk_pin, true }, { hw_cs_pin, true }, { hw_int_pin, false }, { hw_rst_pin, true } }; + + USER_PRINTF("spi_use_for_w5500 = %d use_spi_for_w5500 = %d\n", spi_use_for_w5500, use_spi_for_w5500); + + spi_cs = hw_cs_pin; + spi_int = hw_int_pin; + spi_rst = hw_rst_pin; + + if (hw_mosi_pin >= 0 && hw_sclk_pin >= 0 && use_spi_for_w5500 == true) { + spi_use_for_w5500 = use_spi_for_w5500; + USER_PRINTLN("Trying to start W5500 SPI Ethernet"); + WLED::instance().initEthernet(); + } else if (hw_mosi_pin >= 0 && hw_sclk_pin >= 0 && use_spi_for_w5500 == false) { + pinManager.allocateMultiplePins(spi, 6, PinOwner::HW_SPI); + spi_use_for_w5500 = false; + } else { + //SPI.end(); + if (hw_mosi_pin == -1 || hw_sclk_pin == -1) { // WLEDMM bugfix allow pin = -1 + spi_use_for_w5500 = false; + } + DEBUG_PRINTLN(F("Could not allocate SPI pins.")); + uint8_t spi[6] = { static_cast(spi_mosi), static_cast(spi_miso), static_cast(spi_sclk), static_cast(spi_cs), static_cast(spi_int), static_cast(spi_rst) }; + pinManager.deallocateMultiplePins(spi, 6, PinOwner::HW_SPI); // just in case deallocation of old pins + DEBUG_PRINTF("pinmgr not success for global spi %d %d %d %d %d %d\n", spi_mosi, spi_miso, spi_sclk, spi_cs, spi_int, spi_rst); + spi_use_for_w5500 = false; + } + #else PinManagerPinType spi[3] = { { hw_mosi_pin, true }, { hw_miso_pin, true }, { hw_sclk_pin, true } }; if (hw_mosi_pin >= 0 && hw_sclk_pin >= 0 && pinManager.allocateMultiplePins(spi, 3, PinOwner::HW_SPI)) { - spi_mosi = hw_mosi_pin; - spi_miso = hw_miso_pin; - spi_sclk = hw_sclk_pin; // no bus re-initialisation as usermods do not get any notification //SPI.end(); #ifdef ESP32 @@ -667,6 +718,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) pinManager.deallocateMultiplePins(spi, 3, PinOwner::HW_SPI); // just in case deallocation of old pins DEBUG_PRINTF("pinmgr not success for global spi %d %d %d\n", spi_mosi, spi_miso, spi_sclk); } + #endif JsonObject um = doc.createNestedObject("um"); diff --git a/wled00/src/dependencies/network/Network.h b/wled00/src/dependencies/network/Network.h index 9201d514ea..ac9ae24081 100644 --- a/wled00/src/dependencies/network/Network.h +++ b/wled00/src/dependencies/network/Network.h @@ -2,9 +2,16 @@ #include #else // ESP32 #include - #include + #if defined(CONFIG_ETH_SPI_ETHERNET_W5500) + #include "ETHClass2.h" + extern ETHClass2 ETH; + #else + #include + #endif #endif + + #ifndef Network_h #define Network_h diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 1c3195853f..ce6fe24bfb 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -1009,98 +1009,228 @@ void WLED::initAP(bool resetAP) bool WLED::initEthernet() { -#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET) - + #if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET) + static bool successfullyConfiguredEthernet = false; + ethernet_settings es = {}; - if (successfullyConfiguredEthernet) { - // DEBUG_PRINTLN(F("initE: ETH already successfully configured, ignoring")); - return false; - } - if (ethernetType == WLED_ETH_NONE) { - return false; - } - if (ethernetType >= WLED_NUM_ETH_TYPES) { - DEBUG_PRINT(F("initE: Ignoring attempt for invalid ethernetType ")); DEBUG_PRINTLN(ethernetType); - return false; + #ifdef CONFIG_ETH_SPI_ETHERNET_W5500 + #pragma message "ETHClass2 in use?" + if (!spi_use_for_w5500 || ethernetType > 0) { + #endif + + if (successfullyConfiguredEthernet) { + // DEBUG_PRINTLN(F("initE: ETH already successfully configured, ignoring")); + return false; + } + if (ethernetType == WLED_ETH_NONE) { + return false; + } + if (ethernetType >= WLED_NUM_ETH_TYPES) { + DEBUG_PRINT(F("initE: Ignoring attempt for invalid ethernetType ")); DEBUG_PRINTLN(ethernetType); + return false; + } + + DEBUG_PRINT(F("initE: Attempting ETH config: ")); DEBUG_PRINTLN(ethernetType); + + // Ethernet initialization should only succeed once -- else reboot required + es = ethernetBoards[ethernetType]; + + /* + For LAN8720 the most correct way is to perform clean reset each time before init + applying LOW to power or nRST pin for at least 100 us (please refer to datasheet, page 59) + ESP_IDF > V4 implements it (150 us, lan87xx_reset_hw(esp_eth_phy_t *phy) function in + /components/esp_eth/src/esp_eth_phy_lan87xx.c, line 280) + but ESP_IDF < V4 does not. Lets do it: + [not always needed, might be relevant in some EMI situations at startup and for hot resets] + */ + #if ESP_IDF_VERSION_MAJOR==3 + if (es.eth_power > 0 && es.eth_type == ETH_PHY_LAN8720) { + pinMode(es.eth_power, OUTPUT); + digitalWrite(es.eth_power, 0); + delayMicroseconds(150); + digitalWrite(es.eth_power, 1); + delayMicroseconds(10); + } + #endif + + #ifdef CONFIG_ETH_SPI_ETHERNET_W5500 } - DEBUG_PRINT(F("initE: Attempting ETH config: ")); DEBUG_PRINTLN(ethernetType); - - // Ethernet initialization should only succeed once -- else reboot required - ethernet_settings es = ethernetBoards[ethernetType]; - managed_pin_type pinsToAllocate[10] = { - // first six pins are non-configurable - esp32_nonconfigurable_ethernet_pins[0], - esp32_nonconfigurable_ethernet_pins[1], - esp32_nonconfigurable_ethernet_pins[2], - esp32_nonconfigurable_ethernet_pins[3], - esp32_nonconfigurable_ethernet_pins[4], - esp32_nonconfigurable_ethernet_pins[5], - { (int8_t)es.eth_mdc, true }, // [6] = MDC is output and mandatory - { (int8_t)es.eth_mdio, true }, // [7] = MDIO is bidirectional and mandatory - { (int8_t)es.eth_power, true }, // [8] = optional pin, not all boards use - { ((int8_t)0xFE), false }, // [9] = replaced with eth_clk_mode, mandatory - }; - // update the clock pin.... - if (es.eth_clk_mode == ETH_CLOCK_GPIO0_IN) { - pinsToAllocate[9].pin = 0; - pinsToAllocate[9].isOutput = false; - } else if (es.eth_clk_mode == ETH_CLOCK_GPIO0_OUT) { - pinsToAllocate[9].pin = 0; - pinsToAllocate[9].isOutput = true; - } else if (es.eth_clk_mode == ETH_CLOCK_GPIO16_OUT) { - pinsToAllocate[9].pin = 16; - pinsToAllocate[9].isOutput = true; - } else if (es.eth_clk_mode == ETH_CLOCK_GPIO17_OUT) { - pinsToAllocate[9].pin = 17; - pinsToAllocate[9].isOutput = true; + if (spi_use_for_w5500 && ethernetType == WLED_ETH_NONE) { + es.eth_type = ETH_PHY_W5500; + es.eth_address = 1; + es.eth_miso_pin = spi_miso; + es.eth_mosi_pin = spi_mosi; + es.eth_cs_pin = spi_cs; + es.eth_rst_pin = spi_rst; + es.eth_int_pin = spi_int; + es.eth_sclk_pin = spi_sclk; } else { - DEBUG_PRINT(F("initE: Failing due to invalid eth_clk_mode (")); - DEBUG_PRINT(es.eth_clk_mode); - DEBUG_PRINTLN(")"); - return false; + spi_use_for_w5500 = false; } - if (!pinManager.allocateMultiplePins(pinsToAllocate, 10, PinOwner::Ethernet)) { - DEBUG_PRINTLN(F("initE: Failed to allocate ethernet pins")); - return false; - } + #if !defined(SPI3_HOST) + #define SPI3_HOST SPI2_HOST // at a minimum there are 2 SPI Hosts + #endif - /* - For LAN8720 the most correct way is to perform clean reset each time before init - applying LOW to power or nRST pin for at least 100 us (please refer to datasheet, page 59) - ESP_IDF > V4 implements it (150 us, lan87xx_reset_hw(esp_eth_phy_t *phy) function in - /components/esp_eth/src/esp_eth_phy_lan87xx.c, line 280) - but ESP_IDF < V4 does not. Lets do it: - [not always needed, might be relevant in some EMI situations at startup and for hot resets] - */ - #if ESP_IDF_VERSION_MAJOR==3 - if(es.eth_power>0 && es.eth_type==ETH_PHY_LAN8720) { - pinMode(es.eth_power, OUTPUT); - digitalWrite(es.eth_power, 0); - delayMicroseconds(150); - digitalWrite(es.eth_power, 1); - delayMicroseconds(10); - } - #endif + if (es.eth_type == ETH_PHY_W5500) { + managed_pin_type pinsToAllocate[6] = { + { (int8_t)es.eth_miso_pin, false }, // MISO is input + { (int8_t)es.eth_mosi_pin, true }, // MOSI is output + { (int8_t)es.eth_cs_pin, true }, // CS is output + { (int8_t)es.eth_rst_pin, true }, // RST is output + { (int8_t)es.eth_int_pin, false }, // INT is input + { (int8_t)es.eth_sclk_pin, true }, // SCLK is output + }; + + if (spi_use_for_w5500 == false) { + if (!pinManager.allocateMultiplePins(pinsToAllocate, 6, PinOwner::Ethernet)) { + DEBUG_PRINTLN(F("initE: Failed to allocate ethernet pins")); + return false; + } + } else { + for (int i = 0; i < 6; i++) { + int8_t pin = pinsToAllocate[i].pin; + if (pinManager.getPinOwner(pin) == PinOwner::Ethernet || pinManager.getPinOwner(pin) == PinOwner::HW_SPI) { + // noop + } else { + USER_PRINTF("initEthernet: FAIL: pin %d is not owned by Ethernet or SPI\n", pin); + return false; + } + } + } - if (!ETH.begin( - (uint8_t) es.eth_address, - (int) es.eth_power, - (int) es.eth_mdc, - (int) es.eth_mdio, - (eth_phy_type_t) es.eth_type, - (eth_clock_mode_t) es.eth_clk_mode - )) { - DEBUG_PRINTLN(F("initC: ETH.begin() failed")); - // de-allocate the allocated pins - for (managed_pin_type mpt : pinsToAllocate) { - pinManager.deallocatePin(mpt.pin, PinOwner::Ethernet); + if (!ETH.begin(ETH_PHY_W5500, es.eth_address, es.eth_cs_pin, es.eth_int_pin, es.eth_rst_pin, SPI3_HOST, es.eth_sclk_pin, es.eth_miso_pin, es.eth_mosi_pin)) { + DEBUG_PRINTLN(F("initC: ETHClass2 SPI ETH.begin() failed")); + // de-allocate the allocated pins + if (!spi_use_for_w5500) { + for (managed_pin_type mpt : pinsToAllocate) { + pinManager.deallocatePin(mpt.pin, PinOwner::Ethernet); + } + } + return false; + } else { + Serial.println("ETH initialized W5500!"); + } + } else { + #ifdef CONFIG_ETH_PHY_INTERFACE_RMII + managed_pin_type pinsToAllocate[10] = { + // first six pins are non-configurable + esp32_nonconfigurable_ethernet_pins[0], + esp32_nonconfigurable_ethernet_pins[1], + esp32_nonconfigurable_ethernet_pins[2], + esp32_nonconfigurable_ethernet_pins[3], + esp32_nonconfigurable_ethernet_pins[4], + esp32_nonconfigurable_ethernet_pins[5], + { (int8_t)es.eth_mdc, true }, // [6] = MDC is output and mandatory + { (int8_t)es.eth_mdio, true }, // [7] = MDIO is bidirectional and mandatory + { (int8_t)es.eth_power, true }, // [8] = optional pin, not all boards use + { ((int8_t)0xFE), false }, // [9] = replaced with eth_clk_mode, mandatory + }; + // update the clock pin.... + if (es.eth_clk_mode == ETH_CLOCK_GPIO0_IN) { + pinsToAllocate[9].pin = 0; + pinsToAllocate[9].isOutput = false; + } else if (es.eth_clk_mode == ETH_CLOCK_GPIO0_OUT) { + pinsToAllocate[9].pin = 0; + pinsToAllocate[9].isOutput = true; + } else if (es.eth_clk_mode == ETH_CLOCK_GPIO16_OUT) { + pinsToAllocate[9].pin = 16; + pinsToAllocate[9].isOutput = true; + } else if (es.eth_clk_mode == ETH_CLOCK_GPIO17_OUT) { + pinsToAllocate[9].pin = 17; + pinsToAllocate[9].isOutput = true; + } else { + DEBUG_PRINT(F("initE: Failing due to invalid eth_clk_mode (")); + DEBUG_PRINT(es.eth_clk_mode); + DEBUG_PRINTLN(")"); + return false; + } + if (!pinManager.allocateMultiplePins(pinsToAllocate, 10, PinOwner::Ethernet)) { + DEBUG_PRINTLN(F("initE: Failed to allocate ethernet pins")); + return false; + } + + if (!ETH.begin( + (eth_phy_type_t)es.eth_type, + (uint8_t)es.eth_address, + (int)es.eth_mdc, + (int)es.eth_mdio, + (int)es.eth_power, + (eth_clock_mode_t)es.eth_clk_mode + )) { + DEBUG_PRINTLN(F("initC: ETHClass2 RMII ETH.begin() failed")); + // de-allocate the allocated pins + for (managed_pin_type mpt : pinsToAllocate) { + pinManager.deallocatePin(mpt.pin, PinOwner::Ethernet); + } + return false; + } + #else + return false; + #endif + } + #else + #ifdef CONFIG_ETH_PHY_INTERFACE_RMII + // Ethernet initialization should only succeed once -- else reboot required + managed_pin_type pinsToAllocate[10] = { + // first six pins are non-configurable + esp32_nonconfigurable_ethernet_pins[0], + esp32_nonconfigurable_ethernet_pins[1], + esp32_nonconfigurable_ethernet_pins[2], + esp32_nonconfigurable_ethernet_pins[3], + esp32_nonconfigurable_ethernet_pins[4], + esp32_nonconfigurable_ethernet_pins[5], + { (int8_t)es.eth_mdc, true }, // [6] = MDC is output and mandatory + { (int8_t)es.eth_mdio, true }, // [7] = MDIO is bidirectional and mandatory + { (int8_t)es.eth_power, true }, // [8] = optional pin, not all boards use + { ((int8_t)0xFE), false }, // [9] = replaced with eth_clk_mode, mandatory + }; + // update the clock pin.... + if (es.eth_clk_mode == ETH_CLOCK_GPIO0_IN) { + pinsToAllocate[9].pin = 0; + pinsToAllocate[9].isOutput = false; + } else if (es.eth_clk_mode == ETH_CLOCK_GPIO0_OUT) { + pinsToAllocate[9].pin = 0; + pinsToAllocate[9].isOutput = true; + } else if (es.eth_clk_mode == ETH_CLOCK_GPIO16_OUT) { + pinsToAllocate[9].pin = 16; + pinsToAllocate[9].isOutput = true; + } else if (es.eth_clk_mode == ETH_CLOCK_GPIO17_OUT) { + pinsToAllocate[9].pin = 17; + pinsToAllocate[9].isOutput = true; + } else { + DEBUG_PRINT(F("initE: Failing due to invalid eth_clk_mode (")); + DEBUG_PRINT(es.eth_clk_mode); + DEBUG_PRINTLN(")"); + return false; + } + + if (!pinManager.allocateMultiplePins(pinsToAllocate, 10, PinOwner::Ethernet)) { + DEBUG_PRINTLN(F("initE: Failed to allocate ethernet pins")); + return false; } - return false; - } + if (!ETH.begin( + (uint8_t)es.eth_address, + (int)es.eth_power, + (int)es.eth_mdc, + (int)es.eth_mdio, + (eth_phy_type_t)es.eth_type, + (eth_clock_mode_t)es.eth_clk_mode + )) { + DEBUG_PRINTLN(F("initC: original ETH.begin() failed")); + // de-allocate the allocated pins + for (managed_pin_type mpt : pinsToAllocate) { + pinManager.deallocatePin(mpt.pin, PinOwner::Ethernet); + } + return false; + } + #else + return false; + #endif + #endif successfullyConfiguredEthernet = true; USER_PRINTLN(F("initC: *** Ethernet successfully configured! ***")); // WLEDMM return true; diff --git a/wled00/wled.h b/wled00/wled.h index e5047aa590..648de53c43 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -112,7 +112,11 @@ #else // ESP32 #include // ensure we have the correct "Serial" on new MCUs (depends on ARDUINO_USB_MODE and ARDUINO_USB_CDC_ON_BOOT) #include - #include + #if defined(CONFIG_ETH_SPI_ETHERNET_W5500) + #include "ETHClass2.h" + #else + #include + #endif #include "esp_wifi.h" #include #include @@ -356,7 +360,11 @@ WLED_GLOBAL int8_t irPin _INIT(IRPIN); #endif //WLED_GLOBAL byte presetToApply _INIT(0); - +#ifndef ESP8266 + #if defined(CONFIG_ETH_SPI_ETHERNET_W5500) && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 5) + WLED_GLOBAL ETHClass2 ETH; + #endif +#endif WLED_GLOBAL char ntpServerName[33] _INIT("0.wled.pool.ntp.org"); // NTP server to use // WiFi CONFIG (all these can be changed via web UI, no need to set them here) @@ -839,12 +847,14 @@ WLED_GLOBAL int8_t spi_mosi _INIT(-1); #else WLED_GLOBAL int8_t spi_mosi _INIT(HW_PIN_MOSISPI); #endif + // global SPI DATA/MISO pin (used for usermods) #ifndef HW_PIN_MISOSPI //WLEDMM not SPIMISOPIN WLED_GLOBAL int8_t spi_miso _INIT(-1); #else WLED_GLOBAL int8_t spi_miso _INIT(HW_PIN_MISOSPI); #endif + // global SPI CLOCK/SCLK pin (used for usermods) #ifndef HW_PIN_CLOCKSPI //WLEDMM not SPISCLKPIN WLED_GLOBAL int8_t spi_sclk _INIT(-1); @@ -852,6 +862,32 @@ WLED_GLOBAL int8_t spi_sclk _INIT(-1); WLED_GLOBAL int8_t spi_sclk _INIT(HW_PIN_CLOCKSPI); #endif +#ifdef CONFIG_ETH_SPI_ETHERNET_W5500 +#ifndef HW_PIN_CSSPI +WLED_GLOBAL int8_t spi_cs _INIT(-1); +#else +WLED_GLOBAL int8_t spi_cs _INIT(HW_PIN_CSSPI); +#endif + +#ifndef HW_PIN_INTSPI +WLED_GLOBAL int8_t spi_int _INIT(-1); +#else +WLED_GLOBAL int8_t spi_int _INIT(HW_PIN_INTSPI); +#endif + +#ifndef HW_PIN_RSTSPI +WLED_GLOBAL int8_t spi_rst _INIT(-1); +#else +WLED_GLOBAL int8_t spi_rst _INIT(HW_PIN_RSTSPI); +#endif + +#ifndef USE_HW_SPI_FOR_W5500 +WLED_GLOBAL bool spi_use_for_w5500 _INIT(0); +#else +WLED_GLOBAL BOOL spi_use_for_w5500 _INIT(USE_HW_SPI_FOR_W5500); +#endif +#endif // CONFIG_ETH_SPI_ETHERNET_W5500 + // global ArduinoJson buffer #if defined(ALL_JSON_TO_PSRAM) && (defined(WLED_USE_PSRAM_JSON) || defined(WLED_USE_PSRAM)) // WLEDMM experimental : always use dynamic JSON diff --git a/wled00/wled_ethernet.h b/wled00/wled_ethernet.h index 6b8f0ba56f..e09cdf4f8a 100644 --- a/wled00/wled_ethernet.h +++ b/wled00/wled_ethernet.h @@ -17,19 +17,56 @@ // ETH_CLOCK_GPIO0_OUT == ESP32 provides 50MHz clock output via GPIO0 // ETH_CLOCK_GPIO16_OUT == ESP32 provides 50MHz clock output via GPIO16 // ETH_CLOCK_GPIO17_OUT == ESP32 provides 50MHz clock output via GPIO17 + +#ifndef GPIO_NUM_NC + #define GPIO_NUM_NC -1 +#endif + +#if defined(CONFIG_ETH_PHY_INTERFACE_RMII) || ESP_IDF_VERSION_MAJOR == 3 +#define WLED_ETH_RSVD_PINS_COUNT 6 +extern managed_pin_type esp32_nonconfigurable_ethernet_pins[WLED_ETH_RSVD_PINS_COUNT]; typedef struct EthernetSettings { uint8_t eth_address; int eth_power; int eth_mdc; int eth_mdio; + int eth_mosi_pin; + int eth_miso_pin; + int eth_sclk_pin; + int eth_cs_pin; + int eth_int_pin; + int eth_rst_pin; eth_phy_type_t eth_type; eth_clock_mode_t eth_clk_mode; } ethernet_settings; +#else +#define ETH_PHY_LAN8720 0 +#define ETH_CLOCK_GPIO0_IN 0 +#define ETH_CLOCK_GPIO0_OUT 1 +#define ETH_CLOCK_GPIO17_OUT 3 +#define ETH_CLOCK_GPIO16_OUT 2 +#define ETH_PHY_IP101 1 + +typedef struct EthernetSettings { + uint8_t eth_address; + int eth_power; + int eth_mdc; + int eth_mdio; + int eth_mosi_pin; + int eth_miso_pin; + int eth_sclk_pin; + int eth_cs_pin; + int eth_int_pin; + int eth_rst_pin; + int eth_type; + int eth_clk_mode; +} ethernet_settings; +#define WLED_ETH_RSVD_PINS_COUNT 0 +extern managed_pin_type esp32_nonconfigurable_ethernet_pins[WLED_ETH_RSVD_PINS_COUNT]; +#endif extern const ethernet_settings ethernetBoards[]; -#define WLED_ETH_RSVD_PINS_COUNT 6 -extern const managed_pin_type esp32_nonconfigurable_ethernet_pins[WLED_ETH_RSVD_PINS_COUNT]; -#endif +#endif // WLED_USE_ETHERNET -#endif \ No newline at end of file +#endif // WLED_ETHERNET_H diff --git a/wled00/xml.cpp b/wled00/xml.cpp index 71f3340f93..5ee6c2ae1e 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -240,22 +240,35 @@ void appendGPIOinfo() { #endif #ifdef WLED_USE_ETHERNET - if (ethernetType != WLED_ETH_NONE && ethernetType < WLED_NUM_ETH_TYPES) { - for (uint8_t p=0; p=0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_power,nS,10)); } - if (ethernetBoards[ethernetType].eth_mdc>=0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_mdc,nS,10)); } - if (ethernetBoards[ethernetType].eth_mdio>=0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_mdio,nS,10)); } - switch (ethernetBoards[ethernetType].eth_clk_mode) { - case ETH_CLOCK_GPIO0_IN: - case ETH_CLOCK_GPIO0_OUT: - oappend(SET_F(",0")); - break; - case ETH_CLOCK_GPIO16_OUT: - oappend(SET_F(",16")); - break; - case ETH_CLOCK_GPIO17_OUT: - oappend(SET_F(",17")); - break; + + #ifndef CONFIG_ETH_SPI_ETHERNET_W5500 + #define ETH_PHY_W5500 ETH_PHY_MAX + #endif + + if (ethernetType != WLED_ETH_NONE && ethernetType < WLED_NUM_ETH_TYPES) { + for (uint8_t p = 0; p < WLED_ETH_RSVD_PINS_COUNT; p++) { oappend(","); oappend(itoa(esp32_nonconfigurable_ethernet_pins[p].pin, nS, 10)); } + if (ethernetBoards[ethernetType].eth_power >= 0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_power, nS, 10)); } + if (ethernetBoards[ethernetType].eth_mdc >= 0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_mdc, nS, 10)); } + if (ethernetBoards[ethernetType].eth_mdio >= 0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_mdio, nS, 10)); } + if (ethernetBoards[ethernetType].eth_miso_pin >= 0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_miso_pin, nS, 10)); } + if (ethernetBoards[ethernetType].eth_mosi_pin >= 0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_mosi_pin, nS, 10)); } + if (ethernetBoards[ethernetType].eth_cs_pin >= 0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_cs_pin, nS, 10)); } + if (ethernetBoards[ethernetType].eth_rst_pin >= 0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_rst_pin, nS, 10)); } + if (ethernetBoards[ethernetType].eth_int_pin >= 0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_int_pin, nS, 10)); } + if (ethernetBoards[ethernetType].eth_sclk_pin >= 0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_sclk_pin, nS, 10)); } + if (ethernetBoards[ethernetType].eth_type != ETH_PHY_W5500) { + switch (ethernetBoards[ethernetType].eth_clk_mode) { + case ETH_CLOCK_GPIO0_IN: + case ETH_CLOCK_GPIO0_OUT: + oappend(SET_F(",0")); + break; + case ETH_CLOCK_GPIO16_OUT: + oappend(SET_F(",16")); + break; + case ETH_CLOCK_GPIO17_OUT: + oappend(SET_F(",17")); + break; + } } } #endif @@ -370,6 +383,13 @@ void getSettingsJS(AsyncWebServerRequest* request, byte subPage, char* dest) //W oappend(SET_F("document.getElementById('ethd').style.display='none';")); #endif + #ifdef WLED_USE_ETHERNET + #ifndef CONFIG_ETH_SPI_ETHERNET_W5500 + // Remove W5500 ethernet board options when W5500 support is not compiled + oappend(SET_F("var s=gId('eth_boards');for(var i=s.options.length-1;i>=0;i--){if(s.options[i].text.indexOf('W5500')>=0)s.remove(i);}")); + #endif + #endif + if (Network.isConnected()) //is connected { char s[32]; @@ -837,6 +857,7 @@ void getSettingsJS(AsyncWebServerRequest* request, byte subPage, char* dest) //W oappend(SET_F("d.getElementsByName(\"if:MOSI:pin\")[1].value=")); oappendi(spi_mosi); oappend(";"); oappend(SET_F("d.getElementsByName(\"if:MISO:pin\")[1].value=")); oappendi(spi_miso); oappend(";"); oappend(SET_F("d.getElementsByName(\"if:SCLK:pin\")[1].value=")); oappendi(spi_sclk); oappend(";"); + //WLEDMM: add help info showing defaults oappend(SET_F("addInfo('if:SDA:pin',0,'', 'SDA');")); oappend(SET_F("xOpt('if:SDA:pin',1,' ⍼',")); oappendi(SDA); oappend(");"); @@ -865,6 +886,29 @@ void getSettingsJS(AsyncWebServerRequest* request, byte subPage, char* dest) //W #ifdef HW_PIN_CLOCKSPI oappend(SET_F("xOpt('if:SCLK:pin',1,' ⎌',")); oappendi(HW_PIN_CLOCKSPI); oappend(");"); #endif + #if defined(CONFIG_ETH_SPI_ETHERNET_W5500) + oappend(SET_F("d.getElementsByName(\"if:CS:pin\")[1].value=")); oappendi(spi_cs); oappend(";"); + oappend(SET_F("d.getElementsByName(\"if:INT:pin\")[1].value=")); oappendi(spi_int); oappend(";"); + oappend(SET_F("d.getElementsByName(\"if:RST:pin\")[1].value=")); oappendi(spi_rst); oappend(";"); + oappend(SET_F("addInfo('if:use_for_w5500:use',0,'','SPI for W5500 Ethernet');")); + oappend(SET_F("addInfo('if:CS:pin',0,'', 'CS');")); + oappend(SET_F("xOpt('if:CS:pin',1,' ⍼',")); oappendi(HW_PIN_CSSPI); oappend(");"); + oappend(SET_F("dRO('if:CS:pin',1);")); // disable read only pins + #ifdef HW_PIN_CSSPI + oappend(SET_F("xOpt('if:CS:pin',1,' ⎌',")); oappendi(HW_PIN_CSSPI); oappend(");"); + #endif + oappend(SET_F("addInfo('if:INT:pin',0,'', 'INT');")); + oappend(SET_F("xOpt('if:INT:pin',1,' ⍼',")); oappendi(HW_PIN_INTSPI); oappend(");"); + #ifdef HW_PIN_INTSPI + oappend(SET_F("xOpt('if:INT:pin',1,' ⎌',")); oappendi(HW_PIN_INTSPI); oappend(");"); + #endif + oappend(SET_F("addInfo('if:RST:pin',0,'', 'RST');")); + oappend(SET_F("xOpt('if:RST:pin',1,' ⍼',")); oappendi(HW_PIN_RSTSPI); oappend(");"); + oappend(SET_F("dRO('if:RST:pin',1);")); // disable read only pins + #ifdef HW_PIN_RSTSPI + oappend(SET_F("xOpt('if:RST:pin',1,' ⎌',")); oappendi(HW_PIN_RSTSPI); oappend(");"); + #endif + #endif } else { Usermod *usermod = usermods.lookupName(request->getParam("um")->value().c_str());