diff --git a/zephcore/CMakeLists.txt b/zephcore/CMakeLists.txt index 3ea2eca..95b74ab 100644 --- a/zephcore/CMakeLists.txt +++ b/zephcore/CMakeLists.txt @@ -551,6 +551,16 @@ elseif(CONFIG_ZEPHCORE_ROLE_ROOM_SERVER) helpers/TransportKeyStore.cpp helpers/CommonCLI.cpp ) + # Headless build (e.g. native-Linux SBC): no display/buttons/buzzer, so the + # real ui_* implementation (helpers/ui-button or helpers/ui-joystick) isn't + # compiled. main_room_server.cpp calls ui_* unconditionally — pull in the weak + # no-op stubs to satisfy the link. Mirrors the repeater/observer branches. + if(NOT CONFIG_ZEPHCORE_UI_DESIGN_BUTTON AND NOT CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK) + target_sources(app PRIVATE helpers/ui/ui_headless_stubs.c) + target_include_directories(app PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/helpers/ui + ) + endif() # Shared USBD CDC ACM init + 1200-baud DFU + DTR event/callback module. if(NOT CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT AND (CONFIG_USB_CDC_ACM OR CONFIG_USBD_CDC_ACM_CLASS)) target_sources(app PRIVATE diff --git a/zephcore/adapters/usb/ZephyrCompanionUSB.cpp b/zephcore/adapters/usb/ZephyrCompanionUSB.cpp index f535b6b..e96dcdf 100644 --- a/zephcore/adapters/usb/ZephyrCompanionUSB.cpp +++ b/zephcore/adapters/usb/ZephyrCompanionUSB.cpp @@ -221,7 +221,13 @@ void zephcore_usb_companion_init(struct k_event *mesh_events, s_rx_work = rx_work; -#if DT_HAS_COMPAT_STATUS_OKAY(zephyr_cdc_acm_uart) + /* The cdc_acm_uart DT node may be present without the class driver compiled + * (shared esp32s3_usb_otg.dtsi exposes the node unconditionally; the class is + * only enabled with esp32s3_usb.conf). Gate the device-get on the class too, + * else DEVICE_DT_GET_ONE references an undefined device ordinal on, e.g., a + * debug ESP32-S3 companion (CONFIG_LOG=y) built without esp32s3_usb.conf. */ +#if DT_HAS_COMPAT_STATUS_OKAY(zephyr_cdc_acm_uart) && \ + (IS_ENABLED(CONFIG_USB_CDC_ACM) || IS_ENABLED(CONFIG_USBD_CDC_ACM_CLASS)) usb_dev = DEVICE_DT_GET_ONE(zephyr_cdc_acm_uart); if (device_is_ready(usb_dev)) { LOG_INF("USB CDC device ready: %s", usb_dev->name); diff --git a/zephcore/app/main_observer.cpp b/zephcore/app/main_observer.cpp index d3288a1..b36c034 100644 --- a/zephcore/app/main_observer.cpp +++ b/zephcore/app/main_observer.cpp @@ -20,6 +20,14 @@ #define CLI_REPLY_SIZE 256 +/* The CDC-ACM device object exists only when the class driver is compiled. + * A board overlay may expose a cdc_acm_uart DT node unconditionally (the shared + * esp32s3_usb_otg.dtsi does), so gate the device-get on the Kconfig class, not + * on DT_HAS_COMPAT_STATUS_OKAY alone, or DEVICE_DT_GET_ONE references an + * undefined device ordinal (e.g. an ESP32-S3 observer without esp32s3_usb.conf). */ +#define ZEPHCORE_USB_STACK \ + (IS_ENABLED(CONFIG_USB_CDC_ACM) || IS_ENABLED(CONFIG_USBD_CDC_ACM_CLASS)) + LOG_MODULE_REGISTER(zephcore_observer_main, CONFIG_ZEPHCORE_MAIN_LOG_LEVEL); #include @@ -301,10 +309,11 @@ int main(void) } /* Initialize USB serial for CLI */ -#if DT_HAS_COMPAT_STATUS_OKAY(zephyr_cdc_acm_uart) +#if ZEPHCORE_USB_STACK && DT_HAS_COMPAT_STATUS_OKAY(zephyr_cdc_acm_uart) usb_dev = DEVICE_DT_GET_ONE(zephyr_cdc_acm_uart); #else - /* ESP32 native USB or other console UART */ + /* ESP32 native USB / other console UART, or a cdc_acm DT node present + * without the class driver compiled — fall back to the chosen console. */ usb_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_console)); #endif if (device_is_ready(usb_dev)) { diff --git a/zephcore/src/main_repeater.cpp b/zephcore/src/main_repeater.cpp index 2478240..cf16f55 100644 --- a/zephcore/src/main_repeater.cpp +++ b/zephcore/src/main_repeater.cpp @@ -33,8 +33,18 @@ extern "C" void bt_ctlr_assert_handle(char *file, uint32_t line) } #endif -/* USB CDC ACM init + 1200-baud DFU + DTR callbacks (shared with companion) */ -#if !IS_ENABLED(CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT) +/* USB CDC ACM init + 1200-baud DFU + DTR callbacks (shared with companion). + * + * Gate on the CDC-ACM class driver, NOT on DT_HAS_COMPAT_STATUS_OKAY alone: a + * board overlay may expose a cdc_acm_uart DT node unconditionally (the shared + * esp32s3_usb_otg.dtsi does), so the node can be present while the class driver + * — and therefore zephcore_usbd_* and the device object — is not compiled + * (e.g. an ESP32-S3 repeater built without esp32s3_usb.conf). This mirrors the + * repeater CMake condition that compiles ZephyrUSBCDC.cpp. */ +#define ZEPHCORE_USB_STACK \ + (IS_ENABLED(CONFIG_USB_CDC_ACM) || IS_ENABLED(CONFIG_USBD_CDC_ACM_CLASS)) + +#if ZEPHCORE_USB_STACK && !IS_ENABLED(CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT) #include #endif @@ -388,10 +398,9 @@ int main(void) * host to open the port (DTR asserted) — event-driven via the usbd * message callback. Unplugged → 2 s timeout, no banner; attached → * banner reaches the user the moment the port opens. */ -#if !IS_ENABLED(CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT) && DT_HAS_COMPAT_STATUS_OKAY(zephyr_cdc_acm_uart) +#if ZEPHCORE_USB_STACK && DT_HAS_COMPAT_STATUS_OKAY(zephyr_cdc_acm_uart) && \ + !IS_ENABLED(CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT) zephcore_usbd_init(); -#endif -#if DT_HAS_COMPAT_STATUS_OKAY(zephyr_cdc_acm_uart) zephcore_usbd_wait_dtr(2000); #endif LOG_INF("=== ZephCore Repeater starting ==="); @@ -521,10 +530,12 @@ int main(void) /* USB CDC was initialized earlier (right after clearBootloaderMagic). * Just acquire the device handle for the CLI's UART IRQ binding below. */ -#if DT_HAS_COMPAT_STATUS_OKAY(zephyr_cdc_acm_uart) +#if ZEPHCORE_USB_STACK && DT_HAS_COMPAT_STATUS_OKAY(zephyr_cdc_acm_uart) usb_dev = DEVICE_DT_GET_ONE(zephyr_cdc_acm_uart); #else - /* No CDC ACM (e.g. ESP32 usb_serial) — use chosen console UART */ + /* No CDC ACM class driver (e.g. ESP32 usb_serial, or an ESP32-S3 repeater + * built without esp32s3_usb.conf where the cdc_acm DT node exists but the + * class isn't compiled) — use the chosen console UART. */ usb_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_console)); #endif if (device_is_ready(usb_dev)) { diff --git a/zephcore/src/main_room_server.cpp b/zephcore/src/main_room_server.cpp index 714161a..b4d90c6 100644 --- a/zephcore/src/main_room_server.cpp +++ b/zephcore/src/main_room_server.cpp @@ -33,8 +33,18 @@ extern "C" void bt_ctlr_assert_handle(char *file, uint32_t line) } #endif -/* USB CDC ACM init + 1200-baud DFU + DTR callbacks (shared with companion) */ -#if !IS_ENABLED(CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT) +/* USB CDC ACM init + 1200-baud DFU + DTR callbacks (shared with companion). + * + * Gate on the CDC-ACM class driver, NOT on DT_HAS_COMPAT_STATUS_OKAY alone: a + * board overlay may expose a cdc_acm_uart DT node unconditionally (the shared + * esp32s3_usb_otg.dtsi does), so the node can be present while the class driver + * — and therefore zephcore_usbd_* and the device object — is not compiled + * (e.g. an ESP32-S3 build without esp32s3_usb.conf). This mirrors the CMake + * condition that compiles ZephyrUSBCDC.cpp. */ +#define ZEPHCORE_USB_STACK \ + (IS_ENABLED(CONFIG_USB_CDC_ACM) || IS_ENABLED(CONFIG_USBD_CDC_ACM_CLASS)) + +#if ZEPHCORE_USB_STACK && !IS_ENABLED(CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT) #include #endif @@ -406,10 +416,9 @@ int main(void) * host to open the port (DTR asserted) — event-driven via the usbd * message callback. Unplugged → 2 s timeout, no banner; attached → * banner reaches the user the moment the port opens. */ -#if !IS_ENABLED(CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT) && DT_HAS_COMPAT_STATUS_OKAY(zephyr_cdc_acm_uart) +#if ZEPHCORE_USB_STACK && DT_HAS_COMPAT_STATUS_OKAY(zephyr_cdc_acm_uart) && \ + !IS_ENABLED(CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT) zephcore_usbd_init(); -#endif -#if DT_HAS_COMPAT_STATUS_OKAY(zephyr_cdc_acm_uart) zephcore_usbd_wait_dtr(2000); #endif LOG_INF("=== ZephCore Room Server starting ==="); @@ -539,10 +548,12 @@ int main(void) /* USB CDC was initialized earlier (right after clearBootloaderMagic). * Just acquire the device handle for the CLI's UART IRQ binding below. */ -#if DT_HAS_COMPAT_STATUS_OKAY(zephyr_cdc_acm_uart) +#if ZEPHCORE_USB_STACK && DT_HAS_COMPAT_STATUS_OKAY(zephyr_cdc_acm_uart) usb_dev = DEVICE_DT_GET_ONE(zephyr_cdc_acm_uart); #else - /* No CDC ACM (e.g. ESP32 usb_serial) — use chosen console UART */ + /* No CDC ACM class driver (e.g. ESP32 usb_serial, or an ESP32-S3 build + * without esp32s3_usb.conf where the cdc_acm DT node exists but the class + * isn't compiled) — use the chosen console UART. */ usb_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_console)); #endif if (device_is_ready(usb_dev)) {