From 69b4e18118929c045dda5b880edc3370ba885bae Mon Sep 17 00:00:00 2001 From: Ben Nicholson Date: Thu, 24 Sep 2026 22:52:08 +1000 Subject: [PATCH] Step 1: Ethernet up (IP101/RMII), DHCP, link and IP logged - aes67_board: Waveshare ESP32-P4-ETH pin profile and EMAC + IP101 driver install (PHY addr 1, reset GPIO51, ref clock in on GPIO50). - aes67_net: Ethernet netif with DHCP client; logs link up/down (speed, duplex, MAC) and the DHCP lease. - Verified on board: 100 Mbps full duplex, DHCP lease in ~8 s, ping 0% loss, ~0.2 ms RTT. Co-Authored-By: Claude Opus 5.5 --- CLAUDE.md | 2 +- components/aes67_board/CMakeLists.txt | 3 +- components/aes67_board/aes67_board.c | 41 ++++++++++++- components/aes67_board/include/aes67_board.h | 4 +- .../include/boards/waveshare_esp32p4_eth.h | 17 ++++++ components/aes67_net/CMakeLists.txt | 3 +- components/aes67_net/aes67_net.c | 57 ++++++++++++++++++- components/aes67_net/include/aes67_net.h | 4 +- main/CMakeLists.txt | 2 +- main/main.c | 6 ++ 10 files changed, 128 insertions(+), 11 deletions(-) create mode 100644 components/aes67_board/include/boards/waveshare_esp32p4_eth.h diff --git a/CLAUDE.md b/CLAUDE.md index 9a3c222..704099e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -35,7 +35,7 @@ Repo: https://gitea.apointless.space/bsncubed/aes67-ESP32-P4 ## Build order / status - [x] 0. Check chip revision and flash size; create IDF project and empty component stubs (layout in aes67-core-base.md). Set up the OTA partition table (two app slots, no factory) and PROJECT_VER from git now, so the layout never changes later. -- [ ] 1. Ethernet: IP101 up, DHCP, IP logged. Ping works. +- [x] 1. Ethernet: IP101 up, DHCP, IP logged. Ping works. - [ ] 2. Web server + config store (cJSON in NVS) + embedded index.html; /api/config, /api/status (stub values), /api/reboot. - [ ] 2b. Firmware update: /api/ota upload + rollback self-test. Test: update to a new build, then deliberately flash a build that fails its self-test and confirm it rolls back. After this, flash over the network; keep USB for recovery. - [ ] 3. PTP TimeReceiver: lock to an existing GM (Riedel), fill `status.ptp`. Confirm EMAC hardware timestamps work. First check whether the installed ESP-IDF has a PTP example/component for the P4 before writing one. diff --git a/components/aes67_board/CMakeLists.txt b/components/aes67_board/CMakeLists.txt index 0aed686..94f4464 100644 --- a/components/aes67_board/CMakeLists.txt +++ b/components/aes67_board/CMakeLists.txt @@ -1,2 +1,3 @@ idf_component_register(SRCS "aes67_board.c" - INCLUDE_DIRS "include") + INCLUDE_DIRS "include" + REQUIRES esp_eth) diff --git a/components/aes67_board/aes67_board.c b/components/aes67_board/aes67_board.c index 445ac69..1999433 100644 --- a/components/aes67_board/aes67_board.c +++ b/components/aes67_board/aes67_board.c @@ -1,7 +1,44 @@ #include "aes67_board.h" -// Stub (build step 0). -esp_err_t aes67_board_init(void) +#include "esp_eth.h" +#include "esp_log.h" +#include "boards/waveshare_esp32p4_eth.h" + +static const char *TAG = "board"; + +esp_err_t aes67_board_eth_init(esp_eth_handle_t *out_eth) { + eth_mac_config_t mac_cfg = ETH_MAC_DEFAULT_CONFIG(); + eth_esp32_emac_config_t emac_cfg = ETH_ESP32_EMAC_DEFAULT_CONFIG(); + emac_cfg.smi_gpio.mdc_num = BOARD_ETH_MDC; + emac_cfg.smi_gpio.mdio_num = BOARD_ETH_MDIO; + emac_cfg.interface = EMAC_DATA_INTERFACE_RMII; + emac_cfg.clock_config.rmii.clock_mode = EMAC_CLK_EXT_IN; + emac_cfg.clock_config.rmii.clock_gpio = (emac_rmii_clock_gpio_t)BOARD_ETH_REF_CLK; + emac_cfg.emac_dataif_gpio.rmii.tx_en_num = BOARD_ETH_TX_EN; + emac_cfg.emac_dataif_gpio.rmii.txd0_num = BOARD_ETH_TXD0; + emac_cfg.emac_dataif_gpio.rmii.txd1_num = BOARD_ETH_TXD1; + emac_cfg.emac_dataif_gpio.rmii.crs_dv_num = BOARD_ETH_CRS_DV; + emac_cfg.emac_dataif_gpio.rmii.rxd0_num = BOARD_ETH_RXD0; + emac_cfg.emac_dataif_gpio.rmii.rxd1_num = BOARD_ETH_RXD1; + + eth_phy_config_t phy_cfg = ETH_PHY_DEFAULT_CONFIG(); + phy_cfg.phy_addr = BOARD_ETH_PHY_ADDR; + phy_cfg.reset_gpio_num = BOARD_ETH_PHY_RST; + + esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&emac_cfg, &mac_cfg); + esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_cfg); + if (!mac || !phy) { + ESP_LOGE(TAG, "EMAC/PHY create failed"); + return ESP_FAIL; + } + + esp_eth_config_t eth_cfg = ETH_DEFAULT_CONFIG(mac, phy); + esp_err_t err = esp_eth_driver_install(ð_cfg, out_eth); + if (err != ESP_OK) { + ESP_LOGE(TAG, "driver install failed: %s", esp_err_to_name(err)); + return err; + } + ESP_LOGI(TAG, "%s: IP101 at PHY addr %d, RMII", BOARD_NAME, BOARD_ETH_PHY_ADDR); return ESP_OK; } diff --git a/components/aes67_board/include/aes67_board.h b/components/aes67_board/include/aes67_board.h index 3ebc672..cdf1e12 100644 --- a/components/aes67_board/include/aes67_board.h +++ b/components/aes67_board/include/aes67_board.h @@ -3,5 +3,7 @@ #pragma once #include "esp_err.h" +#include "esp_eth_driver.h" -esp_err_t aes67_board_init(void); +// Create and install the Ethernet driver (EMAC + PHY) for this board. Does not start it. +esp_err_t aes67_board_eth_init(esp_eth_handle_t *out_eth); diff --git a/components/aes67_board/include/boards/waveshare_esp32p4_eth.h b/components/aes67_board/include/boards/waveshare_esp32p4_eth.h new file mode 100644 index 0000000..7aef478 --- /dev/null +++ b/components/aes67_board/include/boards/waveshare_esp32p4_eth.h @@ -0,0 +1,17 @@ +// Waveshare ESP32-P4-ETH (PoE): pin profile. +// IP101GRI PHY over RMII, 50 MHz ref clock from the PHY into the EMAC. +#pragma once + +#define BOARD_NAME "Waveshare ESP32-P4-ETH" + +#define BOARD_ETH_PHY_ADDR 1 +#define BOARD_ETH_PHY_RST 51 // PHY reset/power +#define BOARD_ETH_MDC 31 +#define BOARD_ETH_MDIO 52 +#define BOARD_ETH_REF_CLK 50 // EMAC_CLK_EXT_IN +#define BOARD_ETH_TX_EN 49 +#define BOARD_ETH_TXD0 34 +#define BOARD_ETH_TXD1 35 +#define BOARD_ETH_CRS_DV 28 +#define BOARD_ETH_RXD0 29 +#define BOARD_ETH_RXD1 30 diff --git a/components/aes67_net/CMakeLists.txt b/components/aes67_net/CMakeLists.txt index 2af8349..a42eb34 100644 --- a/components/aes67_net/CMakeLists.txt +++ b/components/aes67_net/CMakeLists.txt @@ -1,2 +1,3 @@ idf_component_register(SRCS "aes67_net.c" - INCLUDE_DIRS "include") + INCLUDE_DIRS "include" + REQUIRES esp_eth esp_netif esp_event) diff --git a/components/aes67_net/aes67_net.c b/components/aes67_net/aes67_net.c index 837cfb3..e3563ca 100644 --- a/components/aes67_net/aes67_net.c +++ b/components/aes67_net/aes67_net.c @@ -1,7 +1,58 @@ #include "aes67_net.h" -// Stub (build step 0). -esp_err_t aes67_net_init(void) +#include "esp_eth.h" +#include "esp_event.h" +#include "esp_log.h" +#include "esp_netif.h" + +static const char *TAG = "net"; + +static void eth_event(void *arg, esp_event_base_t base, int32_t id, void *data) { - return ESP_OK; + esp_eth_handle_t eth = *(esp_eth_handle_t *)data; + + switch (id) { + case ETHERNET_EVENT_CONNECTED: { + uint8_t mac[6]; + eth_speed_t speed; + eth_duplex_t duplex; + esp_eth_ioctl(eth, ETH_CMD_G_MAC_ADDR, mac); + esp_eth_ioctl(eth, ETH_CMD_G_SPEED, &speed); + esp_eth_ioctl(eth, ETH_CMD_G_DUPLEX_MODE, &duplex); + ESP_LOGI(TAG, "link up: %s Mbps %s duplex, MAC %02x:%02x:%02x:%02x:%02x:%02x", + speed == ETH_SPEED_100M ? "100" : "10", duplex == ETH_DUPLEX_FULL ? "full" : "half", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + break; + } + case ETHERNET_EVENT_DISCONNECTED: + ESP_LOGW(TAG, "link down"); + break; + default: + break; + } +} + +static void ip_event(void *arg, esp_event_base_t base, int32_t id, void *data) +{ + const ip_event_got_ip_t *ev = data; + ESP_LOGI(TAG, "got IP " IPSTR " mask " IPSTR " gw " IPSTR, + IP2STR(&ev->ip_info.ip), IP2STR(&ev->ip_info.netmask), IP2STR(&ev->ip_info.gw)); +} + +esp_err_t aes67_net_init(esp_eth_handle_t eth) +{ + ESP_ERROR_CHECK(esp_netif_init()); + esp_err_t err = esp_event_loop_create_default(); + if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) { + return err; + } + + esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH(); + esp_netif_t *netif = esp_netif_new(&netif_cfg); + ESP_ERROR_CHECK(esp_netif_attach(netif, esp_eth_new_netif_glue(eth))); + + ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, ip_event, NULL)); + + return esp_eth_start(eth); } diff --git a/components/aes67_net/include/aes67_net.h b/components/aes67_net/include/aes67_net.h index 10173b8..58cc097 100644 --- a/components/aes67_net/include/aes67_net.h +++ b/components/aes67_net/include/aes67_net.h @@ -3,5 +3,7 @@ #pragma once #include "esp_err.h" +#include "esp_eth_driver.h" -esp_err_t aes67_net_init(void); +// Attach a netif (DHCP client) to the Ethernet driver and start it. Logs link and IP events. +esp_err_t aes67_net_init(esp_eth_handle_t eth); diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 76ee97b..aa3939a 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,3 +1,3 @@ idf_component_register(SRCS "main.c" INCLUDE_DIRS "." - REQUIRES esp_app_format esp_hw_support) + REQUIRES esp_app_format esp_hw_support aes67_board aes67_net) diff --git a/main/main.c b/main/main.c index fcaa5ef..512b309 100644 --- a/main/main.c +++ b/main/main.c @@ -1,3 +1,5 @@ +#include "aes67_board.h" +#include "aes67_net.h" #include "esp_app_desc.h" #include "esp_chip_info.h" #include "esp_log.h" @@ -14,4 +16,8 @@ void app_main(void) app->project_name, app->version, app->idf_ver, app->date, app->time); ESP_LOGI(TAG, "chip rev v%d.%d, %d cores", chip.revision / 100, chip.revision % 100, chip.cores); + + esp_eth_handle_t eth; + ESP_ERROR_CHECK(aes67_board_eth_init(ð)); + ESP_ERROR_CHECK(aes67_net_init(eth)); }