From 096601b93b1c5aa7befc8012d6002921ec169341 Mon Sep 17 00:00:00 2001 From: Jacob Gelman <3182119+ladvoc@users.noreply.github.com> Date: Wed, 19 Nov 2025 11:26:19 +1100 Subject: [PATCH 1/3] Split event handlers --- .../example_utils/src/livekit_example_net.c | 72 +++++++++++-------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/components/example_utils/src/livekit_example_net.c b/components/example_utils/src/livekit_example_net.c index bf15878..2631df4 100644 --- a/components/example_utils/src/livekit_example_net.c +++ b/components/example_utils/src/livekit_example_net.c @@ -40,15 +40,19 @@ static network_connect_t state = {}; // MARK: - Event handler -static void event_handler(void* arg, - esp_event_base_t event_base, - int32_t event_id, - void* event_data) -{ - if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { +static void wifi_event_handler( + void* arg, + esp_event_base_t event_base, + int32_t event_id, + void* event_data +) { + switch (event_id) { + case WIFI_EVENT_STA_START: esp_wifi_connect(); - } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { - if (CONFIG_LK_EXAMPLE_NETWORK_MAX_RETRIES < 0 || state.retry_attempt < CONFIG_LK_EXAMPLE_NETWORK_MAX_RETRIES) { + break; + case WIFI_EVENT_STA_DISCONNECTED: + if (CONFIG_LK_EXAMPLE_NETWORK_MAX_RETRIES < 0 || + state.retry_attempt < CONFIG_LK_EXAMPLE_NETWORK_MAX_RETRIES) { ESP_LOGI(TAG, "Retry: attempt=%d", state.retry_attempt + 1); esp_wifi_connect(); state.retry_attempt++; @@ -56,18 +60,29 @@ static void event_handler(void* arg, } ESP_LOGE(TAG, "Unable to establish connection"); xEventGroupSetBits(state.event_group, NETWORK_EVENT_FAILED); - } else if (event_base == IP_EVENT && - event_id == IP_EVENT_STA_GOT_IP) { - - ip_event_got_ip_t* event = (ip_event_got_ip_t *)event_data; - ESP_LOGI(TAG, "Connected: ip=" IPSTR ", gateway=" IPSTR, - IP2STR(&event->ip_info.ip), IP2STR(&event->ip_info.gw)); - - state.retry_attempt = 0; - xEventGroupSetBits(state.event_group, NETWORK_EVENT_CONNECTED); + break; + default: + break; } } +static void ip_event_handler( + void* arg, + esp_event_base_t event_base, + int32_t event_id, + void* event_data +) { + if (event_id != IP_EVENT_STA_GOT_IP) return; + ip_event_got_ip_t* event = (ip_event_got_ip_t *)event_data; + ESP_LOGI(TAG, "Connected: ip=" IPSTR ", gateway=" IPSTR, + IP2STR(&event->ip_info.ip), IP2STR(&event->ip_info.gw)); + +#if LK_EXAMPLE_USE_WIFI + state.retry_attempt = 0; +#endif + xEventGroupSetBits(state.event_group, NETWORK_EVENT_CONNECTED); +} + // MARK: - Initialization & connection static inline void init_common(void) @@ -80,13 +95,17 @@ static inline void init_common(void) ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); - esp_event_handler_instance_t instance_got_ip; - ESP_ERROR_CHECK(esp_event_handler_instance_register( + ESP_ERROR_CHECK(esp_event_handler_register( + WIFI_EVENT, + ESP_EVENT_ANY_ID, + &wifi_event_handler, + NULL + )); + ESP_ERROR_CHECK(esp_event_handler_register( IP_EVENT, IP_EVENT_STA_GOT_IP, - &event_handler, - NULL, - &instance_got_ip + &ip_event_handler, + NULL )); } @@ -107,15 +126,6 @@ static inline bool connect_wifi(void) wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&wifi_init_config)); - esp_event_handler_instance_t instance_any_id; - ESP_ERROR_CHECK(esp_event_handler_instance_register( - WIFI_EVENT, - ESP_EVENT_ANY_ID, - &event_handler, - NULL, - &instance_any_id - )); - wifi_config_t wifi_config = {}; strlcpy((char *)wifi_config.sta.ssid, CONFIG_LK_EXAMPLE_WIFI_SSID, sizeof(wifi_config.sta.ssid)); strlcpy((char *)wifi_config.sta.password, CONFIG_LK_EXAMPLE_WIFI_PASSWORD, sizeof(wifi_config.sta.password)); From 78174a1fc51c3f50a4e7b1568b131185ce38b1b0 Mon Sep 17 00:00:00 2001 From: Jacob Gelman <3182119+ladvoc@users.noreply.github.com> Date: Wed, 19 Nov 2025 11:52:19 +1100 Subject: [PATCH 2/3] Ethernet support --- components/example_utils/CMakeLists.txt | 8 +- components/example_utils/Kconfig.projbuild | 4 + components/example_utils/README.md | 2 +- components/example_utils/idf_component.yml | 7 +- .../example_utils/src/livekit_example_net.c | 217 +++++++++++++----- 5 files changed, 174 insertions(+), 64 deletions(-) diff --git a/components/example_utils/CMakeLists.txt b/components/example_utils/CMakeLists.txt index 1580b3e..7a3b6aa 100644 --- a/components/example_utils/CMakeLists.txt +++ b/components/example_utils/CMakeLists.txt @@ -1,5 +1,11 @@ +set(component_requires esp_netif nvs_flash esp_wifi) + +if("${IDF_TARGET}" STREQUAL "esp32p4") + list(APPEND component_requires ethernet_init esp_eth) +endif() + idf_component_register( SRC_DIRS ./src INCLUDE_DIRS ./include - PRIV_REQUIRES esp_netif esp_wifi nvs_flash + PRIV_REQUIRES ${component_requires} ) \ No newline at end of file diff --git a/components/example_utils/Kconfig.projbuild b/components/example_utils/Kconfig.projbuild index b985efd..b1ecef2 100644 --- a/components/example_utils/Kconfig.projbuild +++ b/components/example_utils/Kconfig.projbuild @@ -11,6 +11,10 @@ menu "LiveKit Example Utilities" bool "WiFi" help Connect to the internet using WiFi. + config LK_EXAMPLE_USE_ETHERNET + bool "Ethernet" + help + Connect to the internet using Ethernet (supported boards only). endchoice config LK_EXAMPLE_WIFI_SSID depends on LK_EXAMPLE_USE_WIFI diff --git a/components/example_utils/README.md b/components/example_utils/README.md index 49b7f98..59a481e 100644 --- a/components/example_utils/README.md +++ b/components/example_utils/README.md @@ -11,7 +11,7 @@ Establish a network connection with a single function call, configured via _Kcon ### Supported Interfaces - [x] WiFi -- [ ] Ethernet (*TODO*) +- [x] Ethernet (ESP32-P4 only) ### Usage diff --git a/components/example_utils/idf_component.yml b/components/example_utils/idf_component.yml index 3fba0a6..1242c9e 100644 --- a/components/example_utils/idf_component.yml +++ b/components/example_utils/idf_component.yml @@ -8,5 +8,10 @@ discussion: https://livekit.io/join-slack/ license: Apache-2.0 dependencies: idf: ">=5.4" + espressif/ethernet_init: + version: "1.2.0" + rules: + - if: "idf_version >=5.4.3,!=5.5.0,!=5.5.1 && target in [esp32p4]" + # IDF version restriction comes from the ethernet_init component files: - use_gitignore: true \ No newline at end of file + use_gitignore: true diff --git a/components/example_utils/src/livekit_example_net.c b/components/example_utils/src/livekit_example_net.c index 2631df4..77d4a46 100644 --- a/components/example_utils/src/livekit_example_net.c +++ b/components/example_utils/src/livekit_example_net.c @@ -14,11 +14,19 @@ * limitations under the License. */ -#include -#include +#include "esp_event.h" #include "esp_log.h" #include "esp_netif.h" +#include "freertos/FreeRTOS.h" +#include +#include + +#if CONFIG_LK_EXAMPLE_USE_WIFI #include "esp_wifi.h" +#elif CONFIG_LK_EXAMPLE_USE_ETHERNET +#include "esp_eth.h" +#include "ethernet_init.h" +#endif #include "livekit_example_net.h" @@ -38,7 +46,53 @@ typedef struct { static network_connect_t state = {}; -// MARK: - Event handler +// MARK: - Common + +static void ip_event_handler( + void* arg, + esp_event_base_t event_base, + int32_t event_id, + void* event_data +) { + ip_event_got_ip_t* event = (ip_event_got_ip_t *)event_data; + ESP_LOGI(TAG, "Connected: ip=" IPSTR ", gateway=" IPSTR, + IP2STR(&event->ip_info.ip), IP2STR(&event->ip_info.gw)); + + state.retry_attempt = 0; + xEventGroupSetBits(state.event_group, NETWORK_EVENT_CONNECTED); +} + +static inline void init_common(void) +{ + if (!state.event_group) { + state.event_group = xEventGroupCreate(); + } + ESP_ERROR_CHECK(nvs_flash_init()); + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); +} + +static inline bool wait_for_connection_or_failure(void) +{ + EventBits_t bits; + do { + bits = xEventGroupWaitBits( + state.event_group, + NETWORK_EVENT_CONNECTED | NETWORK_EVENT_FAILED, + pdFALSE, + pdFALSE, + portMAX_DELAY + ); + if (bits & NETWORK_EVENT_CONNECTED) { + return true; + } + } while (!(bits & NETWORK_EVENT_FAILED)); + return false; +} + + +// MARK: - WiFi +#if CONFIG_LK_EXAMPLE_USE_WIFI static void wifi_event_handler( void* arg, @@ -66,49 +120,6 @@ static void wifi_event_handler( } } -static void ip_event_handler( - void* arg, - esp_event_base_t event_base, - int32_t event_id, - void* event_data -) { - if (event_id != IP_EVENT_STA_GOT_IP) return; - ip_event_got_ip_t* event = (ip_event_got_ip_t *)event_data; - ESP_LOGI(TAG, "Connected: ip=" IPSTR ", gateway=" IPSTR, - IP2STR(&event->ip_info.ip), IP2STR(&event->ip_info.gw)); - -#if LK_EXAMPLE_USE_WIFI - state.retry_attempt = 0; -#endif - xEventGroupSetBits(state.event_group, NETWORK_EVENT_CONNECTED); -} - -// MARK: - Initialization & connection - -static inline void init_common(void) -{ - if (!state.event_group) { - state.event_group = xEventGroupCreate(); - } - - ESP_ERROR_CHECK(nvs_flash_init()); - ESP_ERROR_CHECK(esp_netif_init()); - ESP_ERROR_CHECK(esp_event_loop_create_default()); - - ESP_ERROR_CHECK(esp_event_handler_register( - WIFI_EVENT, - ESP_EVENT_ANY_ID, - &wifi_event_handler, - NULL - )); - ESP_ERROR_CHECK(esp_event_handler_register( - IP_EVENT, - IP_EVENT_STA_GOT_IP, - &ip_event_handler, - NULL - )); -} - static inline bool connect_wifi(void) { if (strlen(CONFIG_LK_EXAMPLE_WIFI_SSID) == 0) { @@ -126,6 +137,19 @@ static inline bool connect_wifi(void) wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&wifi_init_config)); + ESP_ERROR_CHECK(esp_event_handler_register( + WIFI_EVENT, + ESP_EVENT_ANY_ID, + &wifi_event_handler, + NULL + )); + ESP_ERROR_CHECK(esp_event_handler_register( + IP_EVENT, + IP_EVENT_STA_GOT_IP, + &ip_event_handler, + NULL + )); + wifi_config_t wifi_config = {}; strlcpy((char *)wifi_config.sta.ssid, CONFIG_LK_EXAMPLE_WIFI_SSID, sizeof(wifi_config.sta.ssid)); strlcpy((char *)wifi_config.sta.password, CONFIG_LK_EXAMPLE_WIFI_PASSWORD, sizeof(wifi_config.sta.password)); @@ -140,30 +164,101 @@ static inline bool connect_wifi(void) return true; } -static inline bool wait_for_connection_or_failure(void) +// MARK: - Ethernet +#elif CONFIG_LK_EXAMPLE_USE_ETHERNET + +static void eth_event_handler( + void* arg, + esp_event_base_t event_base, + int32_t event_id, + void* event_data +) { + uint8_t mac_addr[6] = { 0 }; + esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data; + + switch (event_id) { + case ETHERNET_EVENT_CONNECTED: + esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr); + ESP_LOGD(TAG, "Ethernet Link Up"); + break; + case ETHERNET_EVENT_DISCONNECTED: + if (CONFIG_LK_EXAMPLE_NETWORK_MAX_RETRIES < 0 || + state.retry_attempt < CONFIG_LK_EXAMPLE_NETWORK_MAX_RETRIES) { + ESP_LOGI(TAG, "Retry: attempt=%d", state.retry_attempt + 1); + state.retry_attempt++; + return; + } + ESP_LOGE(TAG, "Unable to establish connection"); + xEventGroupSetBits(state.event_group, NETWORK_EVENT_FAILED); + break; + default: + break; + } +} + +static inline bool connect_ethernet(void) { - EventBits_t bits; - do { - bits = xEventGroupWaitBits( - state.event_group, - NETWORK_EVENT_CONNECTED | NETWORK_EVENT_FAILED, - pdFALSE, - pdFALSE, - portMAX_DELAY - ); - if (bits & NETWORK_EVENT_CONNECTED) { - return true; + static esp_eth_handle_t *handles = NULL; + + uint8_t port_count = 0; + ESP_ERROR_CHECK(ethernet_init_all(&handles, &port_count)); + + if (port_count == 1) { + esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH(); + esp_netif_t *eth_netif = esp_netif_new(&cfg); + ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(handles[0]))); + } else { + esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_ETH(); + esp_netif_config_t cfg_spi = { + .base = &esp_netif_config, + .stack = ESP_NETIF_NETSTACK_DEFAULT_ETH + }; + char if_key_str[10]; + char if_desc_str[10]; + char num_str[3]; + for (int i = 0; i < port_count; i++) { + itoa(i, num_str, 10); + strcat(strcpy(if_key_str, "ETH_"), num_str); + strcat(strcpy(if_desc_str, "eth"), num_str); + esp_netif_config.if_key = if_key_str; + esp_netif_config.if_desc = if_desc_str; + esp_netif_config.route_prio -= i * 5; + esp_netif_t *eth_netif = esp_netif_new(&cfg_spi); + ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(handles[i]))); } - } while (!(bits & NETWORK_EVENT_FAILED)); - return false; + } + ESP_ERROR_CHECK(esp_event_handler_register( + ETH_EVENT, + ESP_EVENT_ANY_ID, + ð_event_handler, + NULL + )); + ESP_ERROR_CHECK(esp_event_handler_register( + IP_EVENT, + IP_EVENT_ETH_GOT_IP, + &ip_event_handler, + NULL + )); + for (int i = 0; i < port_count; i++) { + ESP_ERROR_CHECK(esp_eth_start(handles[i])); + } + ESP_LOGI(TAG, "Connecting Ethernet"); + return true; } +#endif // MARK: - Public API bool lk_example_network_connect() { init_common(); - if (!connect_wifi()) { + bool success = false; +#if CONFIG_LK_EXAMPLE_USE_WIFI + success = connect_wifi(); +#elif CONFIG_LK_EXAMPLE_USE_ETHERNET + success = connect_ethernet(); +#endif + if (!success) { return false; } return wait_for_connection_or_failure(); From f3866f83322e3b12133715a7035992e68d599fbe Mon Sep 17 00:00:00 2001 From: Jacob Gelman <3182119+ladvoc@users.noreply.github.com> Date: Wed, 19 Nov 2025 13:55:51 +1100 Subject: [PATCH 3/3] Document ethernet usage --- components/livekit/examples/minimal/README.md | 6 ++++++ components/livekit/examples/minimal_video/README.md | 6 ++++++ components/livekit/examples/voice_agent/README.md | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/components/livekit/examples/minimal/README.md b/components/livekit/examples/minimal/README.md index ba2d126..f555b75 100644 --- a/components/livekit/examples/minimal/README.md +++ b/components/livekit/examples/minimal/README.md @@ -34,6 +34,12 @@ CONFIG_LK_EXAMPLE_WIFI_SSID="" CONFIG_LK_EXAMPLE_WIFI_PASSWORD="" ``` +Or using Ethernet (ESP32-P4 only): + +```ini +CONFIG_LK_EXAMPLE_USE_ETHERNET=y +``` + ### Development Board This example uses the Espressif [*codec_board*](https://components.espressif.com/components/tempotian/codec_board/) component to access board-specific peripherals for media capture and rendering. Supported boards are [defined here](https://github.com/espressif/esp-webrtc-solution/blob/65d13427dd83c37264b6cff966d60af0f84f649c/components/codec_board/board_cfg.txt). Locate the name of your board, and set it as follows: diff --git a/components/livekit/examples/minimal_video/README.md b/components/livekit/examples/minimal_video/README.md index 5f450d9..84b9da6 100644 --- a/components/livekit/examples/minimal_video/README.md +++ b/components/livekit/examples/minimal_video/README.md @@ -34,6 +34,12 @@ CONFIG_LK_EXAMPLE_WIFI_SSID="" CONFIG_LK_EXAMPLE_WIFI_PASSWORD="" ``` +Or using Ethernet (ESP32-P4 only): + +```ini +CONFIG_LK_EXAMPLE_USE_ETHERNET=y +``` + ### Development Board This example uses the Espressif [*codec_board*](https://components.espressif.com/components/tempotian/codec_board/) component to access board-specific peripherals for media capture and rendering. Supported boards are [defined here](https://github.com/espressif/esp-webrtc-solution/blob/65d13427dd83c37264b6cff966d60af0f84f649c/components/codec_board/board_cfg.txt). Locate the name of your board, and set it as follows: diff --git a/components/livekit/examples/voice_agent/README.md b/components/livekit/examples/voice_agent/README.md index 545858c..a02f3fb 100644 --- a/components/livekit/examples/voice_agent/README.md +++ b/components/livekit/examples/voice_agent/README.md @@ -48,6 +48,12 @@ CONFIG_LK_EXAMPLE_WIFI_SSID="" CONFIG_LK_EXAMPLE_WIFI_PASSWORD="" ``` +Or using Ethernet (ESP32-P4 only): + +```ini +CONFIG_LK_EXAMPLE_USE_ETHERNET=y +``` + ### Development Board By default, this example targets the [ESP32-S3-Korvo-2](https://docs.espressif.com/projects/esp-adf/en/latest/design-guide/dev-boards/user-guide-esp32-s3-korvo-2.html) development board, using its corresponding [board support package](https://components.espressif.com/components/espressif/esp32_s3_korvo_2/) (BSP) to access the LED peripherals for the agent to control. If you wish to target a different board, this dependency can be easily removed or replaced.