Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/upload_components.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions components/esp32-p4-eth/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
idf_component_register(
INCLUDE_DIRS "include"
SRC_DIRS "src"
REQUIRES
"base_component"
"ethernet"
"esp_netif"
REQUIRED_IDF_TARGETS "esp32p4"
)
15 changes: 15 additions & 0 deletions components/esp32-p4-eth/README.md
Original file line number Diff line number Diff line change
@@ -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.
19 changes: 19 additions & 0 deletions components/esp32-p4-eth/example/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
9 changes: 9 additions & 0 deletions components/esp32-p4-eth/example/README.md
Original file line number Diff line number Diff line change
@@ -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
```
4 changes: 4 additions & 0 deletions components/esp32-p4-eth/example/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
idf_component_register(
SRC_DIRS "."
INCLUDE_DIRS "."
)
35 changes: 35 additions & 0 deletions components/esp32-p4-eth/example/main/esp32_p4_eth_example.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include <chrono>
#include <thread>

#include <esp_netif.h>

#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);
}
}
7 changes: 7 additions & 0 deletions components/esp32-p4-eth/example/sdkconfig.defaults
Original file line number Diff line number Diff line change
@@ -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
24 changes: 24 additions & 0 deletions components/esp32-p4-eth/idf_component.yml
Original file line number Diff line number Diff line change
@@ -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 <waemfinger@gmail.com>
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
125 changes: 125 additions & 0 deletions components/esp32-p4-eth/include/esp32-p4-eth.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#pragma once

#include <array>
#include <functional>
#include <memory>

#include <sdkconfig.h>

#include <esp_netif.h>

#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<void(esp_ip4_addr_t ip, std::array<uint8_t, 6> 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<void()>;

/// 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<void(esp_ip4_addr_t ip)>;

/// 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<espp::Ethernet> ethernet_;
}; // class Esp32P4Eth
} // namespace espp
49 changes: 49 additions & 0 deletions components/esp32-p4-eth/src/esp32-p4-eth.cpp
Original file line number Diff line number Diff line change
@@ -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},
};
Comment on lines +19 to +33
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<espp::Ethernet>(eth_config);
return ethernet_->initialize();
}

} // namespace espp
9 changes: 9 additions & 0 deletions components/esp32-p4-nano/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
idf_component_register(
INCLUDE_DIRS "include"
SRC_DIRS "src"
REQUIRES
"base_component"
"ethernet"
"esp_netif"
REQUIRED_IDF_TARGETS "esp32p4"
)
15 changes: 15 additions & 0 deletions components/esp32-p4-nano/README.md
Original file line number Diff line number Diff line change
@@ -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.
19 changes: 19 additions & 0 deletions components/esp32-p4-nano/example/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
9 changes: 9 additions & 0 deletions components/esp32-p4-nano/example/README.md
Original file line number Diff line number Diff line change
@@ -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
```
4 changes: 4 additions & 0 deletions components/esp32-p4-nano/example/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
idf_component_register(
SRC_DIRS "."
INCLUDE_DIRS "."
)
Loading
Loading