diff --git a/pio-scripts/conditional_usb_mode.py b/pio-scripts/conditional_usb_mode.py new file mode 100644 index 00000000..2d34126e --- /dev/null +++ b/pio-scripts/conditional_usb_mode.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +""" +Conditional USB Mode Script for WLED-MM + +This script automatically manages ARDUINO_USB_MODE based on the build context: +- For development builds: ARDUINO_USB_MODE=1 (allows USB debugging) +- For release builds (CI): ARDUINO_USB_MODE is removed (prevents hanging, allows normal boot) + +The script detects release builds by checking for the WLED_RELEASE environment variable +which is set to True in the GitHub Actions CI workflow. + +CRITICAL: This change only applies to boards with USB-OTG (ARDUINO_USB_CDC_ON_BOOT=1). +For boards with classical UART-to-USB chips (ARDUINO_USB_CDC_ON_BOOT=0), +ARDUINO_USB_MODE=1 is harmless and left unchanged. + +IMPORTANT: We remove ARDUINO_USB_MODE entirely for release builds rather than setting +it to 0, because ARDUINO_USB_MODE=0 breaks Serial functionality when CDC_ON_BOOT=1. +When ARDUINO_USB_MODE is undefined, the framework uses appropriate defaults. +""" +## This script was created with the help of an AI, reviewed and tested by @softhack007 + +Import('env') +import os + +def has_cdc_on_boot_enabled(env): + """ + Check if ARDUINO_USB_CDC_ON_BOOT is set to 1 in the build configuration. + + Returns True if CDC_ON_BOOT=1, False otherwise. + This is used to identify boards with USB-OTG (native USB) vs UART-to-USB chips. + """ + cpp_defines = env.get('CPPDEFINES', []) + build_flags = env.get('BUILD_FLAGS', []) + + # Check in CPPDEFINES + for define in cpp_defines: + if isinstance(define, (list, tuple)) and len(define) == 2: + if define[0] == 'ARDUINO_USB_CDC_ON_BOOT' and define[1] == '1': + return True + + # Check in raw build flags + for flag in build_flags: + if isinstance(flag, str) and 'ARDUINO_USB_CDC_ON_BOOT=1' in flag: + return True + + return False + +def has_usb_mode_enabled(env): + """ + Check if ARDUINO_USB_MODE is set to 1 in the build configuration. + + Returns True if USB_MODE=1, False otherwise. + """ + cpp_defines = env.get('CPPDEFINES', []) + build_flags = env.get('BUILD_FLAGS', []) + + # Check in CPPDEFINES + for define in cpp_defines: + if isinstance(define, (list, tuple)) and len(define) == 2: + if define[0] == 'ARDUINO_USB_MODE' and define[1] == '1': + return True + + # Check in raw build flags + for flag in build_flags: + if isinstance(flag, str) and 'ARDUINO_USB_MODE=1' in flag: + return True + + return False + +def conditional_usb_mode(env): + """ + Conditionally manage ARDUINO_USB_MODE based on build context. + + For ESP32-C3, ESP32-S2, and ESP32-S3 variants with USB-OTG (CDC_ON_BOOT=1): + - Development builds: ARDUINO_USB_MODE=1 (default, good for debugging) + - Release builds: ARDUINO_USB_MODE removed (prevents hanging, preserves Serial functionality) + + For boards with classical UART-to-USB chip (CDC_ON_BOOT=0): + - ARDUINO_USB_MODE=1 is harmless and left unchanged + + Note: We remove the flag entirely rather than setting to 0, because + ARDUINO_USB_MODE=0 breaks Serial functionality with CDC_ON_BOOT=1. + """ + + # Check if this is a release build (CI sets WLED_RELEASE=True) + is_release_build = os.environ.get('WLED_RELEASE', '').lower() in ('true', '1', 'yes') + + if is_release_build: + # Check if this board uses USB-OTG (CDC_ON_BOOT=1) + if not has_cdc_on_boot_enabled(env): + print("WLED Release build detected - board uses UART-to-USB chip (CDC_ON_BOOT=0)") + print(" Keeping ARDUINO_USB_MODE=1 (harmless for UART-to-USB boards)") + return + + print("WLED Release build detected - board uses USB-OTG (CDC_ON_BOOT=1)") + print(" Removing ARDUINO_USB_MODE definition for production") + + # Check if ARDUINO_USB_MODE=1 is present + if has_usb_mode_enabled(env): + # Remove ARDUINO_USB_MODE entirely - don't set it to 0 as that breaks Serial + # When undefined, the framework uses appropriate defaults based on CDC_ON_BOOT + env.Append(BUILD_UNFLAGS=["-DARDUINO_USB_MODE=1"]) + print(f" Removed ARDUINO_USB_MODE definition (was 1)") + + else: + # Development build + has_usb_mode = has_usb_mode_enabled(env) + has_cdc_boot = has_cdc_on_boot_enabled(env) + + if has_usb_mode and has_cdc_boot: + print("Development build detected - keeping ARDUINO_USB_MODE=1 for USB-OTG debugging") + # Warning in orange/yellow using ANSI color codes + print("\033[93m WARNING: This build is NOT suitable for production devices!\033[0m") + print("\033[93m Production builds require WLED_RELEASE=True environment variable.\033[0m") + elif has_cdc_boot: + # CDC_ON_BOOT=1 present but not USB_MODE=1 + print("Development build detected - USB-OTG enabled, but ARDUINO_USB_MODE=1 missing for debugging") + elif has_usb_mode: + # USB_MODE=1 present but not CDC_ON_BOOT=1 (UART-to-USB board) + print("Development build detected - board uses UART-to-USB chip") + # If neither flag is present, don't print anything + +# Apply the conditional USB mode logic +conditional_usb_mode(env) diff --git a/pio-scripts/output_bins.py b/pio-scripts/output_bins.py index 3a55ced8..6b5ed337 100644 --- a/pio-scripts/output_bins.py +++ b/pio-scripts/output_bins.py @@ -49,7 +49,7 @@ def wledmm_print_build_info(env): found = False for item in all_flags: - if 'WLED_RELEASE_NAME' in item[0] or 'WLED_VERSION' in item[0] or 'ARDUINO_USB_CDC_ON_BOOT' in item[0]: + if 'WLED_RELEASE_NAME' in item[0] or 'WLED_VERSION' in item[0] or 'ARDUINO_USB_CDC_ON_BOOT' in item[0] or 'ARDUINO_USB_MODE' in item[0]: if first: print("\nUsermods and Features:") print_my_item(item) first = False diff --git a/platformio.ini b/platformio.ini index f5e96051..e87678e0 100644 --- a/platformio.ini +++ b/platformio.ini @@ -220,6 +220,7 @@ ldscript_16m14m = eagle.flash.16m14m.ld extra_scripts = pre:pio-scripts/set_version.py pre:pio-scripts/build-html.py + pre:pio-scripts/conditional_usb_mode.py post:pio-scripts/output_bins.py post:pio-scripts/strip-floats.py pre:pio-scripts/user_config_copy.py @@ -2506,11 +2507,12 @@ upload_speed = 460800 build_unflags = ${env:esp32c3dev_4MB_M.build_unflags} -DWLEDMM_FASTPATH ;; needs more testing on -C3 -D WLED_ENABLE_HUB75MATRIX ;; not enough pins + -DARDUINO_USB_CDC_ON_BOOT=1 ;; hangs on boot build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0 ${common_mm.build_flags_S} -Wno-misleading-indentation -Wno-format-truncation -D WLED_RELEASE_NAME=seeed_esp32c3_4MB_S - -DARDUINO_USB_CDC_ON_BOOT=1 ;; enable CDC USB -> needed for debugging over serial USB + -DARDUINO_USB_CDC_ON_BOOT=0 ;; disable CDC USB, as the older framework has problems with it -D WLED_DISABLE_ADALIGHT ;; to disable serial protocols when using CDC USB (Serial RX pin will receive junk commands, unless its pulled down by resistor) -D WLED_DISABLE_INFRARED ;; save flash space ;-D WLED_DISABLE_ALEXA ;; save flash space