diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0c7b7e591..03d189b8f 100755 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -89,8 +89,12 @@ jobs: target: esp32 - path: 'components/esp32-timer-cam/example' target: esp32 + - path: 'components/esp32-p4-eth/example' + target: esp32p4 - path: 'components/esp32-p4-function-ev-board/example' target: esp32p4 + - path: 'components/esp32-p4-nano/example' + target: esp32p4 - path: 'components/esp-box/example' target: esp32s3 - path: 'components/ethernet/example' diff --git a/.github/workflows/upload_components.yml b/.github/workflows/upload_components.yml index 06aebb8f1..193278f04 100755 --- a/.github/workflows/upload_components.yml +++ b/.github/workflows/upload_components.yml @@ -68,7 +68,9 @@ jobs: # (esp32-p4-eth / esp32-p4-function-ev-board / esp32-p4-nano), which # depend on espp/ethernet, so it is uploaded to the registry first. components/ethernet + components/esp32-p4-eth components/esp32-p4-function-ev-board + components/esp32-p4-nano components/esp32-timer-cam components/event_manager components/expressive_eyes diff --git a/components/esp32-p4-eth/CMakeLists.txt b/components/esp32-p4-eth/CMakeLists.txt new file mode 100644 index 000000000..c843ac359 --- /dev/null +++ b/components/esp32-p4-eth/CMakeLists.txt @@ -0,0 +1,9 @@ +idf_component_register( + INCLUDE_DIRS "include" + SRC_DIRS "src" + REQUIRES + "base_component" + "ethernet" + "esp_netif" + REQUIRED_IDF_TARGETS "esp32p4" + ) diff --git a/components/esp32-p4-eth/README.md b/components/esp32-p4-eth/README.md new file mode 100644 index 000000000..5fa2cc957 --- /dev/null +++ b/components/esp32-p4-eth/README.md @@ -0,0 +1,15 @@ +# ESP32-P4-ETH Board Support Package (BSP) + +[![Badge](https://components.espressif.com/components/espp/esp32-p4-eth/badge.svg)](https://components.espressif.com/components/espp/esp32-p4-eth) + +Board Support Package for the [Waveshare ESP32-P4-ETH](https://www.waveshare.com/wiki/ESP32-P4-ETH) +board, exposing its 10/100 Ethernet (ESP32-P4 internal EMAC + IP101GRI RMII PHY). + +The Ethernet bring-up is delegated to the reusable `espp::Ethernet` component; +this BSP just supplies the board-specific RMII pin mapping. It supports DHCP +client and DHCP-server modes with link/IP callbacks. + +## Example + +The [example](./example) brings up a DHCP-client interface and logs link / IP +state. diff --git a/components/esp32-p4-eth/example/CMakeLists.txt b/components/esp32-p4-eth/example/CMakeLists.txt new file mode 100644 index 000000000..f557b9954 --- /dev/null +++ b/components/esp32-p4-eth/example/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.20) + +set(ENV{IDF_COMPONENT_MANAGER} "0") +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +set(EXTRA_COMPONENT_DIRS + "../../../components/" +) + +set( + COMPONENTS + "main esptool_py esp32-p4-eth" + CACHE STRING + "List of components to include" + ) + +project(esp32_p4_eth_example) + +set(CMAKE_CXX_STANDARD 20) diff --git a/components/esp32-p4-eth/example/README.md b/components/esp32-p4-eth/example/README.md new file mode 100644 index 000000000..1cf97e70a --- /dev/null +++ b/components/esp32-p4-eth/example/README.md @@ -0,0 +1,9 @@ +# ESP32-P4-ETH Example + +Brings up the board's Ethernet (EMAC + IP101GRI RMII PHY) as a DHCP client via +the `espp::Esp32P4Eth` BSP and logs link / IP state. + +``` +idf.py set-target esp32p4 +idf.py build flash monitor +``` diff --git a/components/esp32-p4-eth/example/main/CMakeLists.txt b/components/esp32-p4-eth/example/main/CMakeLists.txt new file mode 100644 index 000000000..7840fb139 --- /dev/null +++ b/components/esp32-p4-eth/example/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRC_DIRS "." + INCLUDE_DIRS "." + ) diff --git a/components/esp32-p4-eth/example/main/esp32_p4_eth_example.cpp b/components/esp32-p4-eth/example/main/esp32_p4_eth_example.cpp new file mode 100644 index 000000000..5849d012a --- /dev/null +++ b/components/esp32-p4-eth/example/main/esp32_p4_eth_example.cpp @@ -0,0 +1,35 @@ +#include +#include + +#include + +#include "esp32-p4-eth.hpp" +#include "logger.hpp" + +using namespace std::chrono_literals; + +extern "C" void app_main(void) { + espp::Logger logger({.tag = "ESP32-P4-ETH Example", .level = espp::Logger::Verbosity::INFO}); + + //! [esp32 p4 eth example] + auto &board = espp::Esp32P4Eth::get(); + bool ok = board.initialize_ethernet({ + .on_link_up = [&]() { logger.info("Link up"); }, + .on_link_down = [&]() { logger.warn("Link down"); }, + .on_got_ip = + [&](esp_ip4_addr_t ip) { + char buf[16] = {0}; + esp_ip4addr_ntoa(&ip, buf, sizeof(buf)); + logger.info("Got IP: {}", buf); + }, + }); + if (!ok) { + logger.error("Failed to initialize Ethernet"); + } + //! [esp32 p4 eth example] + + while (true) { + logger.info("connected={} ip={}", board.is_ethernet_connected(), board.ethernet_ip().addr != 0); + std::this_thread::sleep_for(5s); + } +} diff --git a/components/esp32-p4-eth/example/sdkconfig.defaults b/components/esp32-p4-eth/example/sdkconfig.defaults new file mode 100644 index 000000000..bcc795835 --- /dev/null +++ b/components/esp32-p4-eth/example/sdkconfig.defaults @@ -0,0 +1,7 @@ +CONFIG_IDF_TARGET="esp32p4" + +# Ethernet (internal EMAC + RMII) +CONFIG_ETH_ENABLED=y +CONFIG_ETH_USE_ESP32_EMAC=y + +CONFIG_FREERTOS_HZ=1000 diff --git a/components/esp32-p4-eth/idf_component.yml b/components/esp32-p4-eth/idf_component.yml new file mode 100644 index 000000000..c7ac4c00f --- /dev/null +++ b/components/esp32-p4-eth/idf_component.yml @@ -0,0 +1,24 @@ +## IDF Component Manager Manifest File +license: "MIT" +description: "Waveshare ESP32-P4-ETH Board Support Package (BSP) component in C++ (Ethernet)" +url: "https://github.com/esp-cpp/espp/tree/main/components/esp32-p4-eth" +repository: "git://github.com/esp-cpp/espp.git" +maintainers: + - William Emfinger +documentation: "https://esp-cpp.github.io/espp/dev_boards/waveshare/esp32_p4_eth.html" +examples: + - path: example +tags: + - cpp + - Component + - BSP + - ESP32P4 + - Ethernet + - Waveshare +dependencies: + idf: + version: ">=5.3" + espp/base_component: ">=1.0" + espp/ethernet: ">=1.0" +targets: + - esp32p4 diff --git a/components/esp32-p4-eth/include/esp32-p4-eth.hpp b/components/esp32-p4-eth/include/esp32-p4-eth.hpp new file mode 100644 index 000000000..4b94d2d4d --- /dev/null +++ b/components/esp32-p4-eth/include/esp32-p4-eth.hpp @@ -0,0 +1,125 @@ +#pragma once + +#include +#include +#include + +#include + +#include + +#include "base_component.hpp" +#include "ethernet.hpp" + +namespace espp { +/// @brief Board Support Package (BSP) for the Waveshare ESP32-P4-ETH board. +/// +/// This class provides a singleton interface to the board's Ethernet peripheral: +/// - 10/100 Ethernet via the ESP32-P4 internal EMAC and an IP101GRI RMII PHY. +/// +/// The Ethernet bring-up is delegated to the reusable espp::Ethernet component; +/// this BSP just supplies the board-specific RMII pin mapping. +/// +/// RMII pin mapping (ESP32-P4 routable EMAC pins): +/// | Signal | GPIO | +/// |----------|------| +/// | REF_CLK | 50 | ← 50 MHz (25 MHz crystal x2) +/// | TX_EN | 49 | +/// | TXD0 | 34 | +/// | TXD1 | 35 | +/// | CRS_DV | 28 | +/// | RXD0 | 29 | +/// | RXD1 | 30 | +/// | MDC | 31 | +/// | MDIO | 52 | +/// | PHY_RST | 51 | +/// +/// The class is a singleton and can be accessed via get(). +/// +/// \section esp32_p4_eth_example Example +/// \snippet esp32_p4_eth_example.cpp esp32 p4 eth example +class Esp32P4Eth : public BaseComponent { +public: + /// Callback invoked (SERVER mode only) each time the DHCP server assigns an + /// IP address to a connected client. + using client_ip_callback_t = std::function mac)>; + + /// Callback invoked when the Ethernet link state changes or the IP is lost. + /// \note Runs in the ESP-IDF event-loop task context — return quickly, do not block. + using EthernetLinkCallback = std::function; + + /// Callback invoked when the interface obtains an IPv4 address. + /// \note Runs in the ESP-IDF event-loop task context — return quickly, do not block. + using EthernetIpCallback = std::function; + + /// DHCP operating mode for the Ethernet interface. + enum class DhcpMode { + CLIENT, ///< DHCP client — acquire an IP from an upstream server (default). + SERVER, ///< DHCP server — assign IPs to hosts connected to this interface. + }; + + /// Static IP configuration used when operating as a DHCP server. + /// Leave \c ip_info zero-initialised to use the built-in defaults + /// (192.168.4.1 / 255.255.255.0 / gw 192.168.4.1). + struct ServerConfig { + esp_netif_ip_info_t ip_info{}; ///< zero-initialised → 192.168.4.1/24 + client_ip_callback_t on_client_assigned{nullptr}; ///< Called for each assigned client IP. + }; + + /// Configuration for the Ethernet interface. + struct EthernetConfig { + DhcpMode mode{DhcpMode::CLIENT}; ///< DHCP operating mode. + ServerConfig server_config{}; ///< Only used when mode == SERVER. + EthernetLinkCallback on_link_up{nullptr}; ///< Physical link came up. + EthernetLinkCallback on_link_down{nullptr}; ///< Physical link went down. + EthernetIpCallback on_got_ip{nullptr}; ///< Interface obtained an IPv4 address. + EthernetLinkCallback on_lost_ip{nullptr}; ///< Interface lost its IPv4 address. + }; + + /// @brief Access the singleton instance. + static Esp32P4Eth &get() { + static Esp32P4Eth instance; + return instance; + } + + Esp32P4Eth(const Esp32P4Eth &) = delete; + Esp32P4Eth &operator=(const Esp32P4Eth &) = delete; + Esp32P4Eth(Esp32P4Eth &&) = delete; + Esp32P4Eth &operator=(Esp32P4Eth &&) = delete; + + /// Initialize the Ethernet interface (EMAC + IP101GRI RMII PHY). + /// \param config Ethernet configuration (DHCP mode, callbacks). All fields + /// have defaults, so \c EthernetConfig{} gives a plain DHCP client. + /// \return True if Ethernet was successfully initialized and started. + bool initialize_ethernet(const EthernetConfig &config); + + /// Initialize Ethernet with default configuration (DHCP client). + /// \return True if Ethernet was successfully initialized and started. + bool initialize_ethernet(); + + /// \return True if the interface is connected with a valid IP. + bool is_ethernet_connected() const { return ethernet_ && ethernet_->is_connected(); } + + /// \return The most recently acquired IPv4 address (0 if none). + esp_ip4_addr_t ethernet_ip() const { return ethernet_ ? ethernet_->ip() : esp_ip4_addr_t{}; } + +protected: + Esp32P4Eth(); + + // RMII pin mapping for the ESP32-P4-ETH (IP101GRI PHY). + static constexpr int eth_mdc_io = 31; + static constexpr int eth_mdio_io = 52; + static constexpr int eth_ref_clk_io = 50; + static constexpr int eth_phy_reset_gpio = 51; + static constexpr int eth_phy_addr = 1; + static constexpr int eth_tx_en_io = 49; + static constexpr int eth_txd0_io = 34; + static constexpr int eth_txd1_io = 35; + static constexpr int eth_crs_dv_io = 28; + static constexpr int eth_rxd0_io = 29; + static constexpr int eth_rxd1_io = 30; + + // The board's RMII Ethernet is driven by the reusable espp::Ethernet component. + std::unique_ptr ethernet_; +}; // class Esp32P4Eth +} // namespace espp diff --git a/components/esp32-p4-eth/src/esp32-p4-eth.cpp b/components/esp32-p4-eth/src/esp32-p4-eth.cpp new file mode 100644 index 000000000..e8fc85d27 --- /dev/null +++ b/components/esp32-p4-eth/src/esp32-p4-eth.cpp @@ -0,0 +1,49 @@ +#include "esp32-p4-eth.hpp" + +namespace espp { + +Esp32P4Eth::Esp32P4Eth() + : BaseComponent("Esp32P4Eth") {} + +bool Esp32P4Eth::initialize_ethernet() { return initialize_ethernet(EthernetConfig{}); } + +bool Esp32P4Eth::initialize_ethernet(const EthernetConfig &config) { + if (ethernet_ && ethernet_->is_initialized()) { + logger_.warn("Ethernet already initialized"); + return true; + } + + logger_.info("Initializing Ethernet (EMAC + IP101GRI RMII, DHCP {})", + config.mode == DhcpMode::SERVER ? "server" : "client"); + + espp::Ethernet::Config eth_config{}; + eth_config.interface = espp::Ethernet::RmiiConfig{ + .mdc_gpio = eth_mdc_io, + .mdio_gpio = eth_mdio_io, + .phy_addr = eth_phy_addr, + .phy_reset_gpio = eth_phy_reset_gpio, + .clock_ext_in = true, + .clock_gpio = eth_ref_clk_io, + .data_pins = espp::Ethernet::RmiiConfig::DataPins{.tx_en = eth_tx_en_io, + .txd0 = eth_txd0_io, + .txd1 = eth_txd1_io, + .crs_dv = eth_crs_dv_io, + .rxd0 = eth_rxd0_io, + .rxd1 = eth_rxd1_io}, + }; + eth_config.mode = (config.mode == DhcpMode::SERVER) ? espp::Ethernet::DhcpMode::SERVER + : espp::Ethernet::DhcpMode::CLIENT; + if (config.mode == DhcpMode::SERVER) { + eth_config.ip_info = config.server_config.ip_info; + eth_config.on_client_assigned = config.server_config.on_client_assigned; + } + eth_config.on_link_up = config.on_link_up; + eth_config.on_link_down = config.on_link_down; + eth_config.on_got_ip = config.on_got_ip; + eth_config.on_lost_ip = config.on_lost_ip; + + ethernet_ = std::make_unique(eth_config); + return ethernet_->initialize(); +} + +} // namespace espp diff --git a/components/esp32-p4-nano/CMakeLists.txt b/components/esp32-p4-nano/CMakeLists.txt new file mode 100644 index 000000000..c843ac359 --- /dev/null +++ b/components/esp32-p4-nano/CMakeLists.txt @@ -0,0 +1,9 @@ +idf_component_register( + INCLUDE_DIRS "include" + SRC_DIRS "src" + REQUIRES + "base_component" + "ethernet" + "esp_netif" + REQUIRED_IDF_TARGETS "esp32p4" + ) diff --git a/components/esp32-p4-nano/README.md b/components/esp32-p4-nano/README.md new file mode 100644 index 000000000..11baf7aef --- /dev/null +++ b/components/esp32-p4-nano/README.md @@ -0,0 +1,15 @@ +# ESP32-P4-NANO Board Support Package (BSP) + +[![Badge](https://components.espressif.com/components/espp/esp32-p4-nano/badge.svg)](https://components.espressif.com/components/espp/esp32-p4-nano) + +Board Support Package for the [Waveshare ESP32-P4-NANO](https://www.waveshare.com/wiki/ESP32-P4-NANO) +board, exposing its 10/100 Ethernet (ESP32-P4 internal EMAC + IP101GRI RMII PHY). + +The Ethernet bring-up is delegated to the reusable `espp::Ethernet` component; +this BSP just supplies the board-specific RMII pin mapping. It supports DHCP +client and DHCP-server modes with link/IP callbacks. + +## Example + +The [example](./example) brings up a DHCP-client interface and logs link / IP +state. diff --git a/components/esp32-p4-nano/example/CMakeLists.txt b/components/esp32-p4-nano/example/CMakeLists.txt new file mode 100644 index 000000000..84e452605 --- /dev/null +++ b/components/esp32-p4-nano/example/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.20) + +set(ENV{IDF_COMPONENT_MANAGER} "0") +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +set(EXTRA_COMPONENT_DIRS + "../../../components/" +) + +set( + COMPONENTS + "main esptool_py esp32-p4-nano" + CACHE STRING + "List of components to include" + ) + +project(esp32_p4_nano_example) + +set(CMAKE_CXX_STANDARD 20) diff --git a/components/esp32-p4-nano/example/README.md b/components/esp32-p4-nano/example/README.md new file mode 100644 index 000000000..f48b81c45 --- /dev/null +++ b/components/esp32-p4-nano/example/README.md @@ -0,0 +1,9 @@ +# ESP32-P4-NANO Example + +Brings up the board's Ethernet (EMAC + IP101GRI RMII PHY) as a DHCP client via +the `espp::Esp32P4Nano` BSP and logs link / IP state. + +``` +idf.py set-target esp32p4 +idf.py build flash monitor +``` diff --git a/components/esp32-p4-nano/example/main/CMakeLists.txt b/components/esp32-p4-nano/example/main/CMakeLists.txt new file mode 100644 index 000000000..7840fb139 --- /dev/null +++ b/components/esp32-p4-nano/example/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRC_DIRS "." + INCLUDE_DIRS "." + ) diff --git a/components/esp32-p4-nano/example/main/esp32_p4_nano_example.cpp b/components/esp32-p4-nano/example/main/esp32_p4_nano_example.cpp new file mode 100644 index 000000000..e68e34a24 --- /dev/null +++ b/components/esp32-p4-nano/example/main/esp32_p4_nano_example.cpp @@ -0,0 +1,35 @@ +#include +#include + +#include + +#include "esp32-p4-nano.hpp" +#include "logger.hpp" + +using namespace std::chrono_literals; + +extern "C" void app_main(void) { + espp::Logger logger({.tag = "ESP32-P4-NANO Example", .level = espp::Logger::Verbosity::INFO}); + + //! [esp32 p4 nano example] + auto &board = espp::Esp32P4Nano::get(); + bool ok = board.initialize_ethernet({ + .on_link_up = [&]() { logger.info("Link up"); }, + .on_link_down = [&]() { logger.warn("Link down"); }, + .on_got_ip = + [&](esp_ip4_addr_t ip) { + char buf[16] = {0}; + esp_ip4addr_ntoa(&ip, buf, sizeof(buf)); + logger.info("Got IP: {}", buf); + }, + }); + if (!ok) { + logger.error("Failed to initialize Ethernet"); + } + //! [esp32 p4 nano example] + + while (true) { + logger.info("connected={} ip={}", board.is_ethernet_connected(), board.ethernet_ip().addr != 0); + std::this_thread::sleep_for(5s); + } +} diff --git a/components/esp32-p4-nano/example/sdkconfig.defaults b/components/esp32-p4-nano/example/sdkconfig.defaults new file mode 100644 index 000000000..bcc795835 --- /dev/null +++ b/components/esp32-p4-nano/example/sdkconfig.defaults @@ -0,0 +1,7 @@ +CONFIG_IDF_TARGET="esp32p4" + +# Ethernet (internal EMAC + RMII) +CONFIG_ETH_ENABLED=y +CONFIG_ETH_USE_ESP32_EMAC=y + +CONFIG_FREERTOS_HZ=1000 diff --git a/components/esp32-p4-nano/idf_component.yml b/components/esp32-p4-nano/idf_component.yml new file mode 100644 index 000000000..305ebe052 --- /dev/null +++ b/components/esp32-p4-nano/idf_component.yml @@ -0,0 +1,24 @@ +## IDF Component Manager Manifest File +license: "MIT" +description: "Waveshare ESP32-P4-NANO Board Support Package (BSP) component in C++ (Ethernet)" +url: "https://github.com/esp-cpp/espp/tree/main/components/esp32-p4-nano" +repository: "git://github.com/esp-cpp/espp.git" +maintainers: + - William Emfinger +documentation: "https://esp-cpp.github.io/espp/dev_boards/waveshare/esp32_p4_nano.html" +examples: + - path: example +tags: + - cpp + - Component + - BSP + - ESP32P4 + - Ethernet + - Waveshare +dependencies: + idf: + version: ">=5.3" + espp/base_component: ">=1.0" + espp/ethernet: ">=1.0" +targets: + - esp32p4 diff --git a/components/esp32-p4-nano/include/esp32-p4-nano.hpp b/components/esp32-p4-nano/include/esp32-p4-nano.hpp new file mode 100644 index 000000000..7f9a83481 --- /dev/null +++ b/components/esp32-p4-nano/include/esp32-p4-nano.hpp @@ -0,0 +1,125 @@ +#pragma once + +#include +#include +#include + +#include + +#include + +#include "base_component.hpp" +#include "ethernet.hpp" + +namespace espp { +/// @brief Board Support Package (BSP) for the Waveshare ESP32-P4-NANO board. +/// +/// This class provides a singleton interface to the board's Ethernet peripheral: +/// - 10/100 Ethernet via the ESP32-P4 internal EMAC and an IP101GRI RMII PHY. +/// +/// The Ethernet bring-up is delegated to the reusable espp::Ethernet component; +/// this BSP just supplies the board-specific RMII pin mapping. +/// +/// RMII pin mapping (ESP32-P4 routable EMAC pins): +/// | Signal | GPIO | +/// |----------|------| +/// | REF_CLK | 50 | ← 50 MHz (25 MHz crystal x2) +/// | TX_EN | 49 | +/// | TXD0 | 34 | +/// | TXD1 | 35 | +/// | CRS_DV | 28 | +/// | RXD0 | 29 | +/// | RXD1 | 30 | +/// | MDC | 31 | +/// | MDIO | 52 | +/// | PHY_RST | 51 | +/// +/// The class is a singleton and can be accessed via get(). +/// +/// \section esp32_p4_nano_example Example +/// \snippet esp32_p4_nano_example.cpp esp32 p4 nano example +class Esp32P4Nano : public BaseComponent { +public: + /// Callback invoked (SERVER mode only) each time the DHCP server assigns an + /// IP address to a connected client. + using client_ip_callback_t = std::function mac)>; + + /// Callback invoked when the Ethernet link state changes or the IP is lost. + /// \note Runs in the ESP-IDF event-loop task context — return quickly, do not block. + using EthernetLinkCallback = std::function; + + /// Callback invoked when the interface obtains an IPv4 address. + /// \note Runs in the ESP-IDF event-loop task context — return quickly, do not block. + using EthernetIpCallback = std::function; + + /// DHCP operating mode for the Ethernet interface. + enum class DhcpMode { + CLIENT, ///< DHCP client — acquire an IP from an upstream server (default). + SERVER, ///< DHCP server — assign IPs to hosts connected to this interface. + }; + + /// Static IP configuration used when operating as a DHCP server. + /// Leave \c ip_info zero-initialised to use the built-in defaults + /// (192.168.4.1 / 255.255.255.0 / gw 192.168.4.1). + struct ServerConfig { + esp_netif_ip_info_t ip_info{}; ///< zero-initialised → 192.168.4.1/24 + client_ip_callback_t on_client_assigned{nullptr}; ///< Called for each assigned client IP. + }; + + /// Configuration for the Ethernet interface. + struct EthernetConfig { + DhcpMode mode{DhcpMode::CLIENT}; ///< DHCP operating mode. + ServerConfig server_config{}; ///< Only used when mode == SERVER. + EthernetLinkCallback on_link_up{nullptr}; ///< Physical link came up. + EthernetLinkCallback on_link_down{nullptr}; ///< Physical link went down. + EthernetIpCallback on_got_ip{nullptr}; ///< Interface obtained an IPv4 address. + EthernetLinkCallback on_lost_ip{nullptr}; ///< Interface lost its IPv4 address. + }; + + /// @brief Access the singleton instance. + static Esp32P4Nano &get() { + static Esp32P4Nano instance; + return instance; + } + + Esp32P4Nano(const Esp32P4Nano &) = delete; + Esp32P4Nano &operator=(const Esp32P4Nano &) = delete; + Esp32P4Nano(Esp32P4Nano &&) = delete; + Esp32P4Nano &operator=(Esp32P4Nano &&) = delete; + + /// Initialize the Ethernet interface (EMAC + IP101GRI RMII PHY). + /// \param config Ethernet configuration (DHCP mode, callbacks). All fields + /// have defaults, so \c EthernetConfig{} gives a plain DHCP client. + /// \return True if Ethernet was successfully initialized and started. + bool initialize_ethernet(const EthernetConfig &config); + + /// Initialize Ethernet with default configuration (DHCP client). + /// \return True if Ethernet was successfully initialized and started. + bool initialize_ethernet(); + + /// \return True if the interface is connected with a valid IP. + bool is_ethernet_connected() const { return ethernet_ && ethernet_->is_connected(); } + + /// \return The most recently acquired IPv4 address (0 if none). + esp_ip4_addr_t ethernet_ip() const { return ethernet_ ? ethernet_->ip() : esp_ip4_addr_t{}; } + +protected: + Esp32P4Nano(); + + // RMII pin mapping for the ESP32-P4-NANO (IP101GRI PHY). + static constexpr int eth_mdc_io = 31; + static constexpr int eth_mdio_io = 52; + static constexpr int eth_ref_clk_io = 50; + static constexpr int eth_phy_reset_gpio = 51; + static constexpr int eth_phy_addr = 1; + static constexpr int eth_tx_en_io = 49; + static constexpr int eth_txd0_io = 34; + static constexpr int eth_txd1_io = 35; + static constexpr int eth_crs_dv_io = 28; + static constexpr int eth_rxd0_io = 29; + static constexpr int eth_rxd1_io = 30; + + // The board's RMII Ethernet is driven by the reusable espp::Ethernet component. + std::unique_ptr ethernet_; +}; // class Esp32P4Nano +} // namespace espp diff --git a/components/esp32-p4-nano/src/esp32-p4-nano.cpp b/components/esp32-p4-nano/src/esp32-p4-nano.cpp new file mode 100644 index 000000000..054e267a1 --- /dev/null +++ b/components/esp32-p4-nano/src/esp32-p4-nano.cpp @@ -0,0 +1,49 @@ +#include "esp32-p4-nano.hpp" + +namespace espp { + +Esp32P4Nano::Esp32P4Nano() + : BaseComponent("Esp32P4Nano") {} + +bool Esp32P4Nano::initialize_ethernet() { return initialize_ethernet(EthernetConfig{}); } + +bool Esp32P4Nano::initialize_ethernet(const EthernetConfig &config) { + if (ethernet_ && ethernet_->is_initialized()) { + logger_.warn("Ethernet already initialized"); + return true; + } + + logger_.info("Initializing Ethernet (EMAC + IP101GRI RMII, DHCP {})", + config.mode == DhcpMode::SERVER ? "server" : "client"); + + espp::Ethernet::Config eth_config{}; + eth_config.interface = espp::Ethernet::RmiiConfig{ + .mdc_gpio = eth_mdc_io, + .mdio_gpio = eth_mdio_io, + .phy_addr = eth_phy_addr, + .phy_reset_gpio = eth_phy_reset_gpio, + .clock_ext_in = true, + .clock_gpio = eth_ref_clk_io, + .data_pins = espp::Ethernet::RmiiConfig::DataPins{.tx_en = eth_tx_en_io, + .txd0 = eth_txd0_io, + .txd1 = eth_txd1_io, + .crs_dv = eth_crs_dv_io, + .rxd0 = eth_rxd0_io, + .rxd1 = eth_rxd1_io}, + }; + eth_config.mode = (config.mode == DhcpMode::SERVER) ? espp::Ethernet::DhcpMode::SERVER + : espp::Ethernet::DhcpMode::CLIENT; + if (config.mode == DhcpMode::SERVER) { + eth_config.ip_info = config.server_config.ip_info; + eth_config.on_client_assigned = config.server_config.on_client_assigned; + } + eth_config.on_link_up = config.on_link_up; + eth_config.on_link_down = config.on_link_down; + eth_config.on_got_ip = config.on_got_ip; + eth_config.on_lost_ip = config.on_lost_ip; + + ethernet_ = std::make_unique(eth_config); + return ethernet_->initialize(); +} + +} // namespace espp diff --git a/doc/Doxyfile b/doc/Doxyfile index 4d5e2357c..18773f1b8 100755 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -105,7 +105,9 @@ EXAMPLE_PATH = \ $(PROJECT_PATH)/components/drv2605/example/main/drv2605_example.cpp \ $(PROJECT_PATH)/components/encoder/example/main/encoder_example.cpp \ $(PROJECT_PATH)/components/esp32-ethernet-kit/example/main/esp32_ethernet_kit_example.cpp \ + $(PROJECT_PATH)/components/esp32-p4-eth/example/main/esp32_p4_eth_example.cpp \ $(PROJECT_PATH)/components/esp32-p4-function-ev-board/example/main/esp32_p4_function_ev_board_example.cpp \ + $(PROJECT_PATH)/components/esp32-p4-nano/example/main/esp32_p4_nano_example.cpp \ $(PROJECT_PATH)/components/esp32-timer-cam/example/main/esp_timer_cam_example.cpp \ $(PROJECT_PATH)/components/esp-box/example/main/esp_box_example.cpp \ $(PROJECT_PATH)/components/ethernet/example/main/ethernet_example.cpp \ @@ -264,7 +266,9 @@ INPUT = \ $(PROJECT_PATH)/components/encoder/include/abi_encoder.hpp \ $(PROJECT_PATH)/components/encoder/include/encoder_types.hpp \ $(PROJECT_PATH)/components/esp32-ethernet-kit/include/esp32-ethernet-kit.hpp \ + $(PROJECT_PATH)/components/esp32-p4-eth/include/esp32-p4-eth.hpp \ $(PROJECT_PATH)/components/esp32-p4-function-ev-board/include/esp32-p4-function-ev-board.hpp \ + $(PROJECT_PATH)/components/esp32-p4-nano/include/esp32-p4-nano.hpp \ $(PROJECT_PATH)/components/esp32-timer-cam/include/esp32-timer-cam.hpp \ $(PROJECT_PATH)/components/esp-box/include/esp-box.hpp \ $(PROJECT_PATH)/components/ethernet/include/ethernet.hpp \ diff --git a/doc/en/dev_boards/waveshare/esp32_p4_eth.rst b/doc/en/dev_boards/waveshare/esp32_p4_eth.rst new file mode 100644 index 000000000..948f8c0b3 --- /dev/null +++ b/doc/en/dev_boards/waveshare/esp32_p4_eth.rst @@ -0,0 +1,25 @@ +Waveshare ESP32-P4-ETH +********************** + +ESP32-P4-ETH +------------ + +The Waveshare ESP32-P4-ETH is an ESP32-P4 development board with 10/100 +Ethernet (internal EMAC + IP101GRI RMII PHY). + +The ``espp::Esp32P4Eth`` component provides a singleton hardware abstraction for +bringing up the board's Ethernet, delegating to the reusable :doc:`espp::Ethernet +<../../network/ethernet>` component and supplying the board-specific RMII pins. + +.. ------------------------------- Example ------------------------------------- + +.. toctree:: + + esp32_p4_eth_example + +.. ---------------------------- API Reference ---------------------------------- + +API Reference +------------- + +.. include-build-file:: inc/esp32-p4-eth.inc diff --git a/doc/en/dev_boards/waveshare/esp32_p4_eth_example.md b/doc/en/dev_boards/waveshare/esp32_p4_eth_example.md new file mode 100644 index 000000000..c929f8d1d --- /dev/null +++ b/doc/en/dev_boards/waveshare/esp32_p4_eth_example.md @@ -0,0 +1,2 @@ +```{include} ../../../../components/esp32-p4-eth/example/README.md +``` diff --git a/doc/en/dev_boards/waveshare/esp32_p4_nano.rst b/doc/en/dev_boards/waveshare/esp32_p4_nano.rst new file mode 100644 index 000000000..5c607dd4e --- /dev/null +++ b/doc/en/dev_boards/waveshare/esp32_p4_nano.rst @@ -0,0 +1,25 @@ +Waveshare ESP32-P4-NANO +*********************** + +ESP32-P4-NANO +------------- + +The Waveshare ESP32-P4-NANO is an ESP32-P4 development board with 10/100 +Ethernet (internal EMAC + IP101GRI RMII PHY). + +The ``espp::Esp32P4Nano`` component provides a singleton hardware abstraction for +bringing up the board's Ethernet, delegating to the reusable :doc:`espp::Ethernet +<../../network/ethernet>` component and supplying the board-specific RMII pins. + +.. ------------------------------- Example ------------------------------------- + +.. toctree:: + + esp32_p4_nano_example + +.. ---------------------------- API Reference ---------------------------------- + +API Reference +------------- + +.. include-build-file:: inc/esp32-p4-nano.inc diff --git a/doc/en/dev_boards/waveshare/esp32_p4_nano_example.md b/doc/en/dev_boards/waveshare/esp32_p4_nano_example.md new file mode 100644 index 000000000..562b1092f --- /dev/null +++ b/doc/en/dev_boards/waveshare/esp32_p4_nano_example.md @@ -0,0 +1,2 @@ +```{include} ../../../../components/esp32-p4-nano/example/README.md +``` diff --git a/doc/en/dev_boards/waveshare/index.rst b/doc/en/dev_boards/waveshare/index.rst index 86c295e17..985116b1b 100644 --- a/doc/en/dev_boards/waveshare/index.rst +++ b/doc/en/dev_boards/waveshare/index.rst @@ -4,6 +4,8 @@ Waveshare Boards .. toctree:: :maxdepth: 1 + esp32_p4_eth + esp32_p4_nano ws_s3_geek ws_s3_lcd_1_47 ws_s3_touch