Waveshare ESP32-P4-WIFI6-POE-ETH

ESP32-P4-WIFI6-POE-ETH

The Waveshare ESP32-P4-WIFI6-POE-ETH pairs an ESP32-P4 (dual-core 360 MHz RISC-V, 32 MB stacked PSRAM, 32 MB NOR flash) with an on-board ESP32-C6-MINI-1 Wi-Fi 6 / Bluetooth 5 LE co-processor and a 10/100 RJ45 Ethernet port with a reserved header for Waveshare’s plug-in PoE power module.

The espp::Esp32P4Wifi6PoeEth component provides a singleton hardware abstraction for bringing up the board’s peripherals:

  • Ethernet: 10/100 via the internal EMAC and an IP101GRI RMII PHY, delegating to the reusable espp::Ethernet component and supplying the board-specific RMII pins (DHCP client / server). PoE is purely a power-supply feature — no GPIO is involved, so a PoE-powered board looks identical to a USB-powered one in software.

  • Internal I2C bus: shared by the on-board ES8311 audio codec and the MIPI-DSI / MIPI-CSI connectors and 40-pin header.

Note

The board’s BOOT key shares GPIO35 with the RMII TXD1 line (GPIO35 is the ESP32-P4’s boot-strap pin, sampled only at reset), so it cannot be used as a runtime input and the BSP does not expose a button API.

Wi-Fi 6 / BLE are provided by the on-board ESP32-C6 over SDIO using Espressif’s ESP-Hosted + esp_wifi_remote components; the BSP documents the board’s SDIO wiring (which matches the ESP-Hosted defaults for the ESP32-P4) rather than wrapping it — see the example README for how to enable Wi-Fi in your own application.

API Reference

Header File

Classes

class Esp32P4Wifi6PoeEth : public espp::BaseComponent

Board Support Package (BSP) for the Waveshare ESP32-P4-WIFI6-POE-ETH board.

The ESP32-P4-WIFI6-POE-ETH pairs an ESP32-P4 (dual-core 360 MHz RISC-V, 32 MB stacked PSRAM, 32 MB NOR flash) with an on-board ESP32-C6-MINI-1 (Wi-Fi 6 + Bluetooth 5 LE co-processor over SDIO) and a 10/100 RJ45 Ethernet port with a reserved header for a plug-in PoE power module.

This class provides a singleton interface to the board’s peripherals:

  • 10/100 Ethernet via the ESP32-P4 internal EMAC and an IP101GRI RMII PHY. PoE is purely a power-supply feature (the RJ45 center taps feed bridge rectifiers and a 5-pin header for Waveshare’s PoE module, which produces the board’s 5 V rail) — no GPIO is involved, so a PoE-powered board looks identical to a USB-powered one in software.

  • The internal I2C bus (SDA=GPIO7, SCL=GPIO8), shared by the on-board ES8311 audio codec (0x18) and the DSI/CSI connectors + 40-pin header.

Wi-Fi 6 / BLE are provided by the on-board ESP32-C6 over SDIO using Espressif’s ESP-Hosted + esp_wifi_remote components; espp does not (yet) wrap that pattern in a BSP API, so this BSP documents the wiring (see the pin constants below and the example README) instead of wrapping it:

Signal

ESP32-P4 GPIO

ESP32-C6 pin

SDIO CLK

18

IO19 (SDIO_CLK)

SDIO CMD

19

IO18 (SDIO_CMD)

SDIO D0

14

IO20 (SDIO_DATA0)

SDIO D1

15

IO21 (SDIO_DATA1)

SDIO D2

16

IO22 (SDIO_DATA2)

SDIO D3

17

IO23 (SDIO_DATA3)

C6 reset (CHIP_PU / EN)

54

EN

Spare cross-connect (0 Ω)

6

IO2

RMII pin mapping (ESP32-P4 routable EMAC pins; identical to the Waveshare ESP32-P4-ETH / ESP32-P4-NANO). REF_CLK carries a 50 MHz reference clock generated by the IP101GRI from its 25 MHz crystal:

Signal

GPIO

REF_CLK

50

TX_EN

49

TXD0

34

TXD1

35 (also the BOOT key / boot-strap pin, see below)

CRS_DV

28

RXD0

29

RXD1

30

MDC

31

MDIO

52

PHY_RST

51

Other on-board hardware not (yet) wrapped by this BSP (pin data from the Waveshare schematic, see the component README):

  • ES8311 audio codec + NS4150B amplifier + analog microphone (I2S: MCLK=13, BCLK=12, WS/LRCK=10, DSDIN=9, ASDOUT=11; PA enable=53).

  • microSD / TF slot on the fixed 4-bit SDMMC pins (CLK=43, CMD=44, D0..D3=39/40/41/42; pull-ups powered from LDO_VO4).

  • MIPI-DSI and MIPI-CSI 15-pin (Raspberry-Pi-style) connectors; panels and cameras are sold separately (the Kit-C / Kit-D bundles include them).

  • USB-A: native USB 2.0 OTG HS. USB-C: CH343P USB-UART console on UART0 (TX=GPIO37, RX=GPIO38) with auto-reset/boot circuitry.

  • The red LED next to the RJ45 is a 5 V power indicator, and the RJ45 green/yellow LEDs are driven by the PHY — none are GPIO-controllable.

The class is a singleton and can be accessed via get().

Example

Get Instance
  auto &board = espp::Esp32P4Wifi6PoeEth::get();
DHCP Server
  espp::Esp32P4Wifi6PoeEth::ServerConfig srv_cfg;
  if (esp_netif_str_to_ip4(CONFIG_EXAMPLE_ETH_SERVER_IP, &srv_cfg.ip_info.ip) != ESP_OK ||
      esp_netif_str_to_ip4(CONFIG_EXAMPLE_ETH_SERVER_NETMASK, &srv_cfg.ip_info.netmask) != ESP_OK ||
      esp_netif_str_to_ip4(CONFIG_EXAMPLE_ETH_SERVER_GW, &srv_cfg.ip_info.gw) != ESP_OK) {
    logger.error("Invalid DHCP-server address in menuconfig (ip='{}' netmask='{}' gw='{}'); "
                 "aborting Ethernet init",
                 CONFIG_EXAMPLE_ETH_SERVER_IP, CONFIG_EXAMPLE_ETH_SERVER_NETMASK,
                 CONFIG_EXAMPLE_ETH_SERVER_GW);
    return;
  }
  srv_cfg.on_client_assigned = [&](esp_ip4_addr_t ip, std::array<uint8_t, 6> mac) {
    logger.info("Client assigned {}.{}.{}.{} (mac {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x})",
                esp_ip4_addr1_16(&ip), esp_ip4_addr2_16(&ip), esp_ip4_addr3_16(&ip),
                esp_ip4_addr4_16(&ip), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  };
  bool eth_ok = board.initialize_ethernet({
      .mode = DhcpMode::SERVER,
      .server_config = srv_cfg,
      .on_link_up = [&]() { logger.info("Ethernet link up"); },
      .on_link_down = [&]() { logger.warn("Ethernet link down"); },
      .on_got_ip =
          [&](esp_ip4_addr_t ip) {
            logger.info("DHCP server up at {}.{}.{}.{}", esp_ip4_addr1_16(&ip),
                        esp_ip4_addr2_16(&ip), esp_ip4_addr3_16(&ip), esp_ip4_addr4_16(&ip));
          },
      .on_lost_ip = [&]() { logger.warn("Ethernet lost IP"); },
  });
DHCP Client
  bool eth_ok = board.initialize_ethernet({
      .mode = DhcpMode::CLIENT,
      .on_link_up = [&]() { logger.info("Ethernet link up"); },
      .on_link_down = [&]() { logger.warn("Ethernet link down"); },
      .on_got_ip =
          [&](esp_ip4_addr_t ip) {
            logger.info("DHCP lease acquired: {}.{}.{}.{}", esp_ip4_addr1_16(&ip),
                        esp_ip4_addr2_16(&ip), esp_ip4_addr3_16(&ip), esp_ip4_addr4_16(&ip));
          },
      .on_lost_ip = [&]() { logger.warn("Ethernet lost IP"); },
  });

Note

The BOOT key (Key1) is wired to GPIO35, which per the schematic is also the RMII TXD1 line to the IP101GRI. GPIO35 is the ESP32-P4’s boot-strap pin, sampled only at reset — once the EMAC is running the pin is a TXD1 output, so the BOOT key cannot be used as a runtime input. Like the other ESP32-P4 RMII BSPs (ESP32-P4-ETH / ESP32-P4-NANO, identical pinout) this BSP therefore does not expose a button API; the key is only useful for entering the serial bootloader at reset.

Note

Assumption pending hardware verification: PHY address 1 (PHY_AD0 is strapped high through a 5.1 kΩ pull-up like the ESP32-P4-ETH).

Public Types

enum class DhcpMode

DHCP operating mode for the Ethernet interface.

Values:

enumerator CLIENT

DHCP client — acquire an IP from an upstream server (default)

enumerator SERVER

DHCP server — assign IPs to hosts connected to this interface.

using client_ip_callback_t = std::function<void(esp_ip4_addr_t ip, std::array<uint8_t, 6> mac)>

Callback invoked (SERVER mode only) each time the DHCP server assigns an IP address to a connected client.

using EthernetLinkCallback = std::function<void()>

Callback invoked when the Ethernet link state changes (comes up or goes down) or when the IP address is lost.

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)>

Callback invoked when the interface obtains an IPv4 address.

Note

Runs in the ESP-IDF event-loop task context — return quickly, do not block.

Param ip:

The assigned IPv4 address.

Public Functions

inline I2c &internal_i2c()

Get a reference to the internal I2C bus (SDA=GPIO7, SCL=GPIO8)

Note

Shared by the on-board ES8311 audio codec (0x18) and the DSI / CSI connectors and 40-pin header

Returns:

A reference to the internal I2C bus

bool initialize_ethernet(const EthernetConfig &config)

Initialize the Ethernet interface (EMAC + IP101GRI RMII PHY).

Note

Requires the ESP-IDF TCP/IP stack and default event loop. The underlying espp::Ethernet component calls esp_netif_init() and esp_event_loop_create_default() during its initialize(), so they are created here if the application has not already done so.

Warning

If Ethernet is already initialized, the provided config is IGNORED and a warning is logged &#8212; the interface keeps running with its original configuration (reconfiguration is not supported).

Parameters:

configEthernet configuration (DHCP mode, callbacks). All fields have defaults so EthernetConfig{} gives a plain DHCP-client interface with no callbacks.

Returns:

True if Ethernet was successfully initialized and started, or if it was already initialized (the call is idempotent). False only on initialization failure.

bool initialize_ethernet()

Initialize Ethernet with default configuration (DHCP client mode).

Returns:

True if Ethernet was successfully initialized and started, or if it was already initialized (the call is idempotent). False only on initialization failure.

inline bool is_ethernet_connected() const

Check whether the interface has a usable IP address (DHCP lease granted in CLIENT mode, or link is up in SERVER mode).

Returns:

True if the interface is connected with a valid IP.

inline esp_ip4_addr_t ethernet_ip() const

Get the most recently acquired IPv4 address (0 if none).

Returns:

The IPv4 address.

inline const std::string &get_name() const

Get the name of the component

Note

This is the tag of the logger

Returns:

A const reference to the name of the component

inline void set_log_tag(const std::string_view &tag)

Set the tag for the logger

Parameters:

tag – The tag to use for the logger

inline espp::Logger::Verbosity get_log_level() const

Get the log level for the logger

Returns:

The verbosity level of the logger

inline void set_log_level(espp::Logger::Verbosity level)

Set the log level for the logger

Parameters:

level – The verbosity level to use for the logger

inline void set_log_verbosity(espp::Logger::Verbosity level)

Set the log verbosity for the logger

See also

set_log_level

Note

This is a convenience method that calls set_log_level

Parameters:

level – The verbosity level to use for the logger

inline espp::Logger::Verbosity get_log_verbosity() const

Get the log verbosity for the logger

See also

get_log_level

Note

This is a convenience method that calls get_log_level

Returns:

The verbosity level of the logger

inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)

Set the rate limit for the logger

Note

Only calls to the logger that have _rate_limit suffix will be rate limited

Parameters:

rate_limit – The rate limit to use for the logger

Public Static Functions

static inline Esp32P4Wifi6PoeEth &get()

Access the singleton instance.

Returns:

Reference to the singleton instance

Public Static Attributes

static constexpr int c6_sdio_clk_io = 18

P4 SDIO CLK -> C6 IO19 (SDIO_CLK)

static constexpr int c6_sdio_cmd_io = 19

P4 SDIO CMD -> C6 IO18 (SDIO_CMD)

static constexpr int c6_sdio_d0_io = 14

P4 SDIO D0 -> C6 IO20 (SDIO_DATA0)

static constexpr int c6_sdio_d1_io = 15

P4 SDIO D1 -> C6 IO21 (SDIO_DATA1)

static constexpr int c6_sdio_d2_io = 16

P4 SDIO D2 -> C6 IO22 (SDIO_DATA2)

static constexpr int c6_sdio_d3_io = 17

P4 SDIO D3 -> C6 IO23 (SDIO_DATA3)

static constexpr int c6_reset_io = 54

P4 GPIO54 -> C6 EN (CHIP_PU) via 0 Ω

static constexpr int c6_spare_io = 6

P4 GPIO6 -> C6 IO2 via 0 Ω (unused spare)

struct EthernetConfig

Configuration for the Ethernet interface.

Public Members

DhcpMode mode = {DhcpMode::CLIENT}

DHCP operating mode.

ServerConfig server_config = {}

Only used when mode == SERVER.

Physical link came up.

Physical link went down.

EthernetIpCallback on_got_ip = {nullptr}

Interface obtained an IPv4 address.

EthernetLinkCallback on_lost_ip = {nullptr}

Interface lost its IPv4 address.

struct ServerConfig

Static IP configuration used when operating as a DHCP server. Leave ip_info zero-initialised to use the built-in defaults (192.168.4.1 / 255.255.255.0 / gw 192.168.4.1).

Public Members

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.