From 2d19225f46989e29ab9abb98419858bec0e8a4f5 Mon Sep 17 00:00:00 2001 From: Tomas Rezucha Date: Tue, 26 May 2026 16:36:57 +0200 Subject: [PATCH] feat(usb/example): Enable NCM device example on ESP32-P4 --- examples/peripherals/.build-test-rules.yml | 6 +++-- .../peripherals/usb/device/tusb_ncm/README.md | 22 ++++++++++++++-- .../device/tusb_ncm/main/Kconfig.projbuild | 5 ++++ .../usb/device/tusb_ncm/main/hints.yml | 3 +++ .../device/tusb_ncm/main/idf_component.yml | 4 +++ .../usb/device/tusb_ncm/main/tusb_ncm_main.c | 26 +++++++++++-------- .../device/tusb_ncm/pytest_usb_device_ncm.py | 6 ++--- .../tusb_ncm/sdkconfig.defaults.esp32s3 | 1 - 8 files changed, 54 insertions(+), 19 deletions(-) create mode 100644 examples/peripherals/usb/device/tusb_ncm/main/hints.yml delete mode 100644 examples/peripherals/usb/device/tusb_ncm/sdkconfig.defaults.esp32s3 diff --git a/examples/peripherals/.build-test-rules.yml b/examples/peripherals/.build-test-rules.yml index 1700a3324af..a1fb20fc620 100644 --- a/examples/peripherals/.build-test-rules.yml +++ b/examples/peripherals/.build-test-rules.yml @@ -819,11 +819,13 @@ examples/peripherals/usb/device/cherryusb_serial_device: examples/peripherals/usb/device/tusb_ncm: disable: - - if: SOC_USB_OTG_SUPPORTED != 1 or SOC_WIFI_SUPPORTED != 1 + - if: SOC_USB_OTG_SUPPORTED != 1 + - if: (SOC_WIFI_SUPPORTED != 1) and (IDF_TARGET not in ["esp32p4"]) + reason: no integrated Wi-Fi (ESP32-P4 uses esp_wifi_remote with a co-processor) disable_test: - if: IDF_TARGET not in ["esp32s2"] temporary: true - reason: lack of runners with usb_device tag + reason: lack of runners with usb_device tag; P4 runner does not have C6 coprocessor for wifi remote test depends_components: - esp_wifi - soc diff --git a/examples/peripherals/usb/device/tusb_ncm/README.md b/examples/peripherals/usb/device/tusb_ncm/README.md index 0b910ddc105..175648b62dc 100644 --- a/examples/peripherals/usb/device/tusb_ncm/README.md +++ b/examples/peripherals/usb/device/tusb_ncm/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32-S2 | ESP32-S3 | ESP32-S31 | -| ----------------- | -------- | -------- | --------- | +| Supported Targets | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 | +| ----------------- | -------- | -------- | -------- | --------- | # TinyUSB Network Control Model Device Example @@ -17,6 +17,24 @@ As a USB stack, a TinyUSB component is used. Any ESP board that have USB-OTG supported. +#### ESP32-P4 + +ESP32-P4 does not have integrated Wi-Fi. To use this example on ESP32-P4, Wi-Fi connectivity is provided through the [esp_wifi_remote](https://components.espressif.com/components/espressif/esp_wifi_remote) component. + +On ESP32-P4 evaluation boards such as the [ESP32-P4-Function-EV-Board](https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32p4/esp32-p4-function-ev-board/index.html), an on-board slave SoC (typically ESP32-C6) provides the Wi-Fi link to the host ESP32-P4 over SDIO. See the [ESP-Hosted ESP32-P4 EV Board documentation](https://github.com/espressif/esp-hosted-mcu/blob/main/docs/esp32_p4_function_ev_board.md) for hardware details. + +> **Note:** Before running this example, the slave SoC must be running compatible ESP-Hosted co-processor firmware. Evaluation boards are usually shipped with firmware pre-flashed, but it is recommended to update the slave to the latest version. Follow the instructions in [Host Performs Slave OTA](https://github.com/espressif/esp-hosted-mcu/blob/main/examples/host_performs_slave_ota/README.md). + +To build for ESP32-P4: + +``` +idf.py set-target esp32p4 menuconfig +``` + +In menuconfig, set `Component config` → `Wi-Fi Remote` → `choose slave target` to match your co-processor (for example, `esp32c6` on the ESP32-P4-Function-EV-Board). + +If you are having issues setting up the connection between ESP32-P4 and the network (slave) coprocessor, please see [Troubleshooting ESP-Hosted](https://github.com/espressif/esp-hosted-mcu/blob/main/docs/troubleshooting.md) guide. + #### Pin Assignment _Note:_ In case your board doesn't have micro-USB connector connected to USB-OTG peripheral, you may have to DIY a cable and connect **D+** and **D-** to the pins listed below. diff --git a/examples/peripherals/usb/device/tusb_ncm/main/Kconfig.projbuild b/examples/peripherals/usb/device/tusb_ncm/main/Kconfig.projbuild index dd0b5e41b91..83352afcfde 100644 --- a/examples/peripherals/usb/device/tusb_ncm/main/Kconfig.projbuild +++ b/examples/peripherals/usb/device/tusb_ncm/main/Kconfig.projbuild @@ -12,4 +12,9 @@ menu "Example Configuration" help WiFi password (WPA or WPA2) for the example to use. + config ESP_USB_NCM_USE_WIFI_REMOTE + int + default 1 if !SOC_WIFI_SUPPORTED + default 0 + endmenu diff --git a/examples/peripherals/usb/device/tusb_ncm/main/hints.yml b/examples/peripherals/usb/device/tusb_ncm/main/hints.yml new file mode 100644 index 00000000000..b3ec605937a --- /dev/null +++ b/examples/peripherals/usb/device/tusb_ncm/main/hints.yml @@ -0,0 +1,3 @@ +- + re: "ESP-Hosted link not yet up" + hint: "Your hardware must contain WiFi co-processor and must be properly configured in menuconfig `Component config` → `Wi-Fi Remote`. See ESP-Hosted Troubleshooting Guide for more details: https://github.com/espressif/esp-hosted-mcu/blob/main/docs/troubleshooting.md" diff --git a/examples/peripherals/usb/device/tusb_ncm/main/idf_component.yml b/examples/peripherals/usb/device/tusb_ncm/main/idf_component.yml index e1a93a262ee..37b42ed69e3 100644 --- a/examples/peripherals/usb/device/tusb_ncm/main/idf_component.yml +++ b/examples/peripherals/usb/device/tusb_ncm/main/idf_component.yml @@ -2,3 +2,7 @@ dependencies: espressif/esp_tinyusb: version: "^2.0.1~1" + espressif/esp_wifi_remote: + version: "~1.6.0" + rules: + - if: "$CONFIG{ESP_USB_NCM_USE_WIFI_REMOTE} == 1" diff --git a/examples/peripherals/usb/device/tusb_ncm/main/tusb_ncm_main.c b/examples/peripherals/usb/device/tusb_ncm/main/tusb_ncm_main.c index 6f0056fefed..80478aab4f7 100644 --- a/examples/peripherals/usb/device/tusb_ncm/main/tusb_ncm_main.c +++ b/examples/peripherals/usb/device/tusb_ncm/main/tusb_ncm_main.c @@ -5,7 +5,7 @@ */ /* DESCRIPTION: - * This example demonstrates using ESP32-S2/S3 as a USB network device. It initializes WiFi in station mode, + * This example demonstrates using USB enabled target as a USB network device. It initializes WiFi in station mode, * connects and bridges the WiFi and USB networks, so the USB device acts as a standard network interface that * acquires an IP address from the AP/router which the WiFi station connects to. */ @@ -16,7 +16,6 @@ #include "esp_event.h" #include "esp_check.h" #include "nvs_flash.h" -#include "esp_mac.h" #include "esp_wifi.h" #include "esp_private/wifi.h" @@ -73,16 +72,16 @@ static void wifi_event_handler(void *arg, esp_event_base_t event_base, } } -static esp_err_t start_wifi(bool *is_connected) +static esp_err_t start_wifi(uint8_t *mac_ret) { + assert(mac_ret); ESP_RETURN_ON_ERROR(esp_event_loop_create_default(), TAG, "Cannot initialize event loop"); wifi_init_config_t wifi_cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_RETURN_ON_ERROR(esp_wifi_init(&wifi_cfg), TAG, "Failed to initialize WiFi library"); - ESP_RETURN_ON_ERROR(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, is_connected), - TAG, "Failed to register handler for wifi events"); ESP_RETURN_ON_ERROR(esp_wifi_set_mode(WIFI_MODE_STA), TAG, "Failed to set WiFi station mode"); ESP_RETURN_ON_ERROR(esp_wifi_start(), TAG, "Failed to start WiFi library"); + ESP_RETURN_ON_ERROR(esp_wifi_get_mac(WIFI_IF_STA, mac_ret), TAG, "Failed to get WiFi MAC address"); wifi_config_t wifi_config = { .sta = { @@ -98,7 +97,7 @@ static esp_err_t start_wifi(bool *is_connected) void app_main(void) { static bool s_is_wifi_connected = false; // needs to be static as it's used after we exit app_main() - + uint8_t wifi_mac[6]; /* Initialize NVS — it is used to store PHY calibration data */ esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { @@ -107,6 +106,12 @@ void app_main(void) } ESP_ERROR_CHECK(ret); + /* WiFi initialization */ + ESP_LOGI(TAG, "WiFi initialization"); + ESP_GOTO_ON_ERROR(start_wifi(wifi_mac), err, TAG, "Failed to init and start WiFi"); + ESP_LOGI(TAG, "WiFi interface MAC address: %02x:%02x:%02x:%02x:%02x:%02x", wifi_mac[0], wifi_mac[1], wifi_mac[2], wifi_mac[3], wifi_mac[4], wifi_mac[5]); + + /* USB NCM initialization */ ESP_LOGI(TAG, "USB NCM device initialization"); const tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG(); ESP_GOTO_ON_ERROR(tinyusb_driver_install(&tusb_cfg), err, TAG, "Failed to install TinyUSB driver"); @@ -116,14 +121,13 @@ void app_main(void) .free_tx_buffer = wifi_pkt_free, .user_context = &s_is_wifi_connected }; - esp_read_mac(net_config.mac_addr, ESP_MAC_WIFI_STA); - uint8_t *mac = net_config.mac_addr; - ESP_LOGI(TAG, "Network interface HW address: %02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + memcpy(net_config.mac_addr, wifi_mac, 6); ESP_GOTO_ON_ERROR(tinyusb_net_init(&net_config), err, TAG, "Failed to initialize TinyUSB NCM device class"); usb_device_set_link_state(false); - ESP_LOGI(TAG, "WiFi initialization"); - ESP_GOTO_ON_ERROR(start_wifi(&s_is_wifi_connected), err, TAG, "Failed to init and start WiFi"); + /* Connect USB NCM to WiFi via esp_event */ + ESP_GOTO_ON_ERROR(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, &s_is_wifi_connected), + err, TAG, "Failed to register handler for wifi events"); ESP_LOGI(TAG, "USB NCM and WiFi initialized and started"); return; diff --git a/examples/peripherals/usb/device/tusb_ncm/pytest_usb_device_ncm.py b/examples/peripherals/usb/device/tusb_ncm/pytest_usb_device_ncm.py index 20c6966330d..980ddbf2baf 100644 --- a/examples/peripherals/usb/device/tusb_ncm/pytest_usb_device_ncm.py +++ b/examples/peripherals/usb/device/tusb_ncm/pytest_usb_device_ncm.py @@ -8,12 +8,12 @@ from pytest_embedded import Dut from pytest_embedded_idf.utils import idf_parametrize -@pytest.mark.temp_skip_ci(targets=['esp32s3', 'esp32s31'], reason='lack of runners with usb_device tag') +@pytest.mark.temp_skip_ci(targets=['esp32s3', 'esp32s31', 'esp32p4'], reason='lack of runners with usb_device tag') @pytest.mark.usb_device -@idf_parametrize('target', ['esp32s2', 'esp32s3', 'esp32s31'], indirect=['target']) +@idf_parametrize('target', ['esp32s2', 'esp32s3', 'esp32p4', 'esp32s31'], indirect=['target']) def test_usb_device_ncm_example(dut: Dut) -> None: netif_mac = dut.expect( - r'Network interface HW address: ' + r'WiFi interface MAC address: ' r'([0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:' r'[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2})' ) diff --git a/examples/peripherals/usb/device/tusb_ncm/sdkconfig.defaults.esp32s3 b/examples/peripherals/usb/device/tusb_ncm/sdkconfig.defaults.esp32s3 deleted file mode 100644 index 65730e3edb7..00000000000 --- a/examples/peripherals/usb/device/tusb_ncm/sdkconfig.defaults.esp32s3 +++ /dev/null @@ -1 +0,0 @@ -CONFIG_TINYUSB_NET_MODE_NCM=y