From 5c50acb05007db8b0d0ebc7cb78c8cc85af0a7ef Mon Sep 17 00:00:00 2001 From: cckilator <58052501+cckilator@users.noreply.github.com> Date: Sat, 22 Aug 2026 13:03:59 +0200 Subject: [PATCH 1/3] Add support for ESP32-S3 board configuration --- platformio.ini | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/platformio.ini b/platformio.ini index 1c4b1104..a368ed36 100644 --- a/platformio.ini +++ b/platformio.ini @@ -138,3 +138,22 @@ platform = ${esp32_lolin_s2_mini.platform} lib_deps = ${esp32_lolin_s2_mini.lib_deps} test_ignore = ${esp32_lolin_s2_mini.test_ignore} + +;========================================================== +; ESP32-S3 board (native USB CDC) - experimental +;========================================================== +[esp32_s3] +platform = espressif32@6.3.2 +lib_deps = https://github.com/awawa-dev/NeoPixelBus.git#HyperSerialESP32 +test_ignore = * +build_flags = -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 + +[env:s3_WS281x_RGB] +build_flags = -DNEOPIXEL_RGB -DDATA_PIN=4 ${env.build_flags} ${esp32_s3.build_flags} +custom_prog_version = esp32_s3_WS281x_RGB + +board = esp32-s3-devkitc-1 +board_build.mcu = esp32s3 +platform = ${esp32_s3.platform} +lib_deps = ${esp32_s3.lib_deps} +test_ignore = ${esp32_s3.test_ignore} From cec3a35b065f6191c7a66bd3b9aa86195d9b3927 Mon Sep 17 00:00:00 2001 From: cckilator <58052501+cckilator@users.noreply.github.com> Date: Sat, 22 Aug 2026 13:06:31 +0200 Subject: [PATCH 2/3] Add ESP32-S3 native USB CDC fixes to main.cpp Updated comments and formatting for clarity and consistency. Adjusted task handling for ESP32-S3 to avoid conflicts with native USB CDC driver. --- src/main.cpp | 128 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 83 insertions(+), 45 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 60dfae94..74f35c23 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,29 +1,29 @@ /* main.cpp + * + * MIT License + * + * Copyright (c) 2021-2026 awawa-dev + * + * https://github.com/awawa-dev/HyperSerialESP32 * -* MIT License -* -* Copyright (c) 2021-2026 awawa-dev -* -* https://github.com/awawa-dev/HyperSerialESP32 -* -* Permission is hereby granted, free of charge, to any person obtaining a copy + * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * -* The above copyright notice and this permission notice shall be included in all + * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. - */ + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ #include #include @@ -65,6 +65,14 @@ #elif NEOPIXEL_RGB #define LED_DRIVER NeoPixelBus #endif +#elif defined(CONFIG_IDF_TARGET_ESP32S3) + /* ESP32-S3: NeoPixelBus has no I2S driver for this target, RMT only. + Added by Cedric/Claude to support native-USB S3 boards. */ + #ifdef NEOPIXEL_RGBW + #define LED_DRIVER NeoPixelBus + #elif NEOPIXEL_RGB + #define LED_DRIVER NeoPixelBus + #endif #else #ifdef NEOPIXEL_RGBW #define LED_DRIVER NeoPixelBus @@ -155,7 +163,7 @@ public: bool CanShow() {return true;} void Show(bool safe) {} - }; +}; #endif #define SerialPort Serial @@ -176,36 +184,56 @@ /** * @brief separete thread for handling incoming data using cyclic buffer * - * @param parameters + * @param parameters */ void processDataTask(void * parameters) { - for(;;) - { + for(;;) +{ xSemaphoreTake(base.i2sXSemaphore, portMAX_DELAY); processData(); - } +} } void processSerialTask(void * parameters) { - for(;;) - { + for(;;) +{ if (serialTaskHandler() || base.queueCurrent != base.queueEnd) - xSemaphoreGive(base.i2sXSemaphore); + xSemaphoreGive(base.i2sXSemaphore); yield(); - } +} } void setup() { - bool multicore = true; + bool multicore = true; + + #if defined(CONFIG_IDF_TARGET_ESP32S3) + // On ESP32-S3, processSerialTask now runs pinned to core 0 (see below) + // to avoid a conflict with the native USB CDC driver's internal task. + // But its tight for(;;){...; yield();} loop starves the IDLE0 task, + // which triggers the Task Watchdog Timer and aborts/reboots after + // ~10s. Disabling the watchdog for core 0's idle task avoids that. + // Added by Cedric/Claude. + disableCore0WDT(); + #endif // Init serial port Serial.setRxBufferSize(MAX_BUFFER - 1); Serial.setTimeout(50); Serial.begin(SERIALCOM_SPEED); - while (!Serial) continue; + #if !defined(CONFIG_IDF_TARGET_ESP32S3) + while (!Serial) continue; + #else + // On ESP32-S3 native USB CDC, "Serial" can stay false until the host + // actively opens the port with the right DTR handshake, which some + // terminal tools never send -> this would hang boot forever. + // Wait a bounded time instead, then continue regardless. + // Added by Cedric/Claude. + unsigned long serialWaitStart = millis(); + while (!Serial && (millis() - serialWaitStart) < 3000) continue; + #endif #if defined(NEOPIXEL_RGBW) || defined(NEOPIXEL_RGB) #ifdef NEOPIXEL_RGBW @@ -217,8 +245,11 @@ void setup() #endif #endif - #if !defined(CONFIG_IDF_TARGET_ESP32S2) + #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3) // Display config + // NOTE (Cedric/Claude): disabled on ESP32-S3 too (like S2) because + // Serial.println() here can block indefinitely on native USB CDC + // before a host terminal has actively opened/drained the port. Serial.println(HELLO_MESSAGE); #if defined(SECOND_SEGMENT_START_INDEX) SerialPort.write("SECOND_SEGMENT_START_INDEX = "); @@ -254,7 +285,7 @@ void setup() #endif if (multicore) - { +{ // create a semaphore to synchronize threads base.i2sXSemaphore = xSemaphoreCreateBinary(); @@ -263,29 +294,36 @@ void setup() xTaskCreatePinnedToCore( processDataTask, "processDataTask", - 5096, - NULL, - 5, - &base.processDataHandle, - 0); - // serial handler on core 1 + 5096, + NULL, + 5, + &base.processDataHandle, + 0); + // serial handler on core 0 (changed from core 1 - Cedric/Claude: + // on ESP32-S3 the native USB CDC driver's internal task conflicts + // with a user task pinned to core 1, causing a hang after a few + // seconds of runtime. Pinning both tasks to core 0 avoids it.) + #if defined(CONFIG_IDF_TARGET_ESP32S3) + int serialTaskCore = 0; + #else + int serialTaskCore = 1; + #endif xTaskCreatePinnedToCore( processSerialTask, "processSerialTask", - 4096, - NULL, - 2, - &base.processSerialHandle, - 1); - } + 4096, + NULL, + 2, + &base.processSerialHandle, + serialTaskCore); +} } void loop() { - if (base.processDataHandle == nullptr && base.processSerialHandle == nullptr) - { + if (base.processDataHandle == nullptr && base.processSerialHandle == nullptr) +{ serialTaskHandler(); processData(); - } } - +} From c90d6f27612aa1923e115c6b082f4a1e6bc050fa Mon Sep 17 00:00:00 2001 From: cckilator <58052501+cckilator@users.noreply.github.com> Date: Sat, 22 Aug 2026 13:14:03 +0200 Subject: [PATCH 3/3] Fix formatting: restore original indentation in main.cpp --- src/main.cpp | 85 ++++++++++++++++++++++++++-------------------------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 74f35c23..8011cef1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,29 +1,29 @@ /* main.cpp - * - * MIT License - * - * Copyright (c) 2021-2026 awawa-dev - * - * https://github.com/awawa-dev/HyperSerialESP32 * - * Permission is hereby granted, free of charge, to any person obtaining a copy +* MIT License +* +* Copyright (c) 2021-2026 awawa-dev +* +* https://github.com/awawa-dev/HyperSerialESP32 +* +* Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all +* The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. + */ #include #include @@ -163,7 +163,7 @@ public: bool CanShow() {return true;} void Show(bool safe) {} -}; + }; #endif #define SerialPort Serial @@ -184,30 +184,30 @@ /** * @brief separete thread for handling incoming data using cyclic buffer * - * @param parameters + * @param parameters */ void processDataTask(void * parameters) { - for(;;) -{ + for(;;) + { xSemaphoreTake(base.i2sXSemaphore, portMAX_DELAY); processData(); -} + } } void processSerialTask(void * parameters) { - for(;;) -{ + for(;;) + { if (serialTaskHandler() || base.queueCurrent != base.queueEnd) - xSemaphoreGive(base.i2sXSemaphore); + xSemaphoreGive(base.i2sXSemaphore); yield(); -} + } } void setup() { - bool multicore = true; + bool multicore = true; #if defined(CONFIG_IDF_TARGET_ESP32S3) // On ESP32-S3, processSerialTask now runs pinned to core 0 (see below) @@ -233,7 +233,7 @@ void setup() // Added by Cedric/Claude. unsigned long serialWaitStart = millis(); while (!Serial && (millis() - serialWaitStart) < 3000) continue; - #endif +#endif #if defined(NEOPIXEL_RGBW) || defined(NEOPIXEL_RGB) #ifdef NEOPIXEL_RGBW @@ -285,7 +285,7 @@ void setup() #endif if (multicore) -{ + { // create a semaphore to synchronize threads base.i2sXSemaphore = xSemaphoreCreateBinary(); @@ -294,11 +294,11 @@ void setup() xTaskCreatePinnedToCore( processDataTask, "processDataTask", - 5096, - NULL, - 5, - &base.processDataHandle, - 0); + 5096, + NULL, + 5, + &base.processDataHandle, + 0); // serial handler on core 0 (changed from core 1 - Cedric/Claude: // on ESP32-S3 the native USB CDC driver's internal task conflicts // with a user task pinned to core 1, causing a hang after a few @@ -311,19 +311,20 @@ void setup() xTaskCreatePinnedToCore( processSerialTask, "processSerialTask", - 4096, - NULL, - 2, - &base.processSerialHandle, - serialTaskCore); -} + 4096, + NULL, + 2, + &base.processSerialHandle, + serialTaskCore); + } } void loop() { - if (base.processDataHandle == nullptr && base.processSerialHandle == nullptr) -{ + if (base.processDataHandle == nullptr && base.processSerialHandle == nullptr) + { serialTaskHandler(); processData(); + } } -} +