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.
-
enumerator CLIENT
-
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
configis IGNORED and a warning is logged — the interface keeps running with its original configuration (reconfiguration is not supported).- Parameters:
config – Ethernet 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
See also
See also
- Returns:
The verbosity level of the logger
-
inline void set_log_level(espp::Logger::Verbosity level)
Set the log level for the logger
See also
See also
- 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
See also
See also
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
See also
See also
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
See also
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
-
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.
-
ServerConfig server_config = {}
-
struct ServerConfig
Static IP configuration used when operating as a DHCP server. Leave
ip_infozero-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.
-
esp_netif_ip_info_t ip_info = {}