diff --git a/.github/workflows/arduino_library_lint.yml b/.github/workflows/arduino_library_lint.yml index 282b565cec..ecc4d198ea 100644 --- a/.github/workflows/arduino_library_lint.yml +++ b/.github/workflows/arduino_library_lint.yml @@ -2,7 +2,7 @@ on: push: - pull_request: + pull_request_target: name: Arduino Library Lint jobs: lint: diff --git a/.github/workflows/build_adafruit_feather_nrf52840_sense.yml b/.github/workflows/build_adafruit_feather_nrf52840_sense.yml index caa42d9e59..602f37f7c5 100644 --- a/.github/workflows/build_adafruit_feather_nrf52840_sense.yml +++ b/.github/workflows/build_adafruit_feather_nrf52840_sense.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_adafruit_xiaoblesense.yml b/.github/workflows/build_adafruit_xiaoblesense.yml index 7d15ae5670..dc39452cd9 100644 --- a/.github/workflows/build_adafruit_xiaoblesense.yml +++ b/.github/workflows/build_adafruit_xiaoblesense.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_attiny1616.yml b/.github/workflows/build_attiny1616.yml index cdf16101a4..8d03029f6a 100644 --- a/.github/workflows/build_attiny1616.yml +++ b/.github/workflows/build_attiny1616.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_attiny85.yml b/.github/workflows/build_attiny85.yml index e0b8afc777..b05e371fe5 100644 --- a/.github/workflows/build_attiny85.yml +++ b/.github/workflows/build_attiny85.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_attiny88.yml b/.github/workflows/build_attiny88.yml index c6964131b0..15b2e9d558 100644 --- a/.github/workflows/build_attiny88.yml +++ b/.github/workflows/build_attiny88.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_bluepill.yml b/.github/workflows/build_bluepill.yml index 695f75f2a6..41d3ba1342 100644 --- a/.github/workflows/build_bluepill.yml +++ b/.github/workflows/build_bluepill.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_digix.yml b/.github/workflows/build_digix.yml index c3048d283f..7bd13742e9 100644 --- a/.github/workflows/build_digix.yml +++ b/.github/workflows/build_digix.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_due.yml b/.github/workflows/build_due.yml new file mode 100644 index 0000000000..e55d3fd554 --- /dev/null +++ b/.github/workflows/build_due.yml @@ -0,0 +1,15 @@ +name: due + +on: + push: + branches: + - master + pull_request_target: + branches: + - master + +jobs: + build: + uses: ./.github/workflows/build_template.yml + with: + args: due diff --git a/.github/workflows/build_esp32c3.yml b/.github/workflows/build_esp32c3.yml index dd4d47d4a1..775acbaeac 100644 --- a/.github/workflows/build_esp32c3.yml +++ b/.github/workflows/build_esp32c3.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_esp32c6.yml b/.github/workflows/build_esp32c6.yml index 50af0ce03d..ad58960c59 100644 --- a/.github/workflows/build_esp32c6.yml +++ b/.github/workflows/build_esp32c6.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_esp32dev.yml b/.github/workflows/build_esp32dev.yml index 20ea6e067d..5964542fed 100644 --- a/.github/workflows/build_esp32dev.yml +++ b/.github/workflows/build_esp32dev.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_esp32dev_idf3.3.yml b/.github/workflows/build_esp32dev_idf3.3.yml index 97d46a6035..57bd9d0472 100644 --- a/.github/workflows/build_esp32dev_idf3.3.yml +++ b/.github/workflows/build_esp32dev_idf3.3.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_esp32dev_idf4.4.yml b/.github/workflows/build_esp32dev_idf4.4.yml index 43885e877a..485b163b36 100644 --- a/.github/workflows/build_esp32dev_idf4.4.yml +++ b/.github/workflows/build_esp32dev_idf4.4.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_esp32dev_namespace.yml b/.github/workflows/build_esp32dev_namespace.yml new file mode 100644 index 0000000000..86f4d1e792 --- /dev/null +++ b/.github/workflows/build_esp32dev_namespace.yml @@ -0,0 +1,15 @@ +name: esp32dev_namespace + +on: + push: + branches: + - master + pull_request_target: + branches: + - master + +jobs: + build: + uses: ./.github/workflows/build_template.yml + with: + args: esp32dev --defines FASTLED_FORCE_NAMESPACE=1,FASTLED_FORCE_USE_NAMESPACE=1 diff --git a/.github/workflows/build_esp32s3.yml b/.github/workflows/build_esp32s3.yml index 6decb7effd..501d830711 100644 --- a/.github/workflows/build_esp32s3.yml +++ b/.github/workflows/build_esp32s3.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_esp32wroom.yml b/.github/workflows/build_esp32wroom.yml index 5bc836f12a..7e7f5f1115 100644 --- a/.github/workflows/build_esp32wroom.yml +++ b/.github/workflows/build_esp32wroom.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_esp8622.yml b/.github/workflows/build_esp8622.yml index 20d9a56792..53e7650589 100644 --- a/.github/workflows/build_esp8622.yml +++ b/.github/workflows/build_esp8622.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_linux.yml index 2f5346aaca..a4ff4b9c55 100644 --- a/.github/workflows/build_linux.yml +++ b/.github/workflows/build_linux.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_maple_map.yml b/.github/workflows/build_maple_map.yml index 2da84c9336..600ded6662 100644 --- a/.github/workflows/build_maple_map.yml +++ b/.github/workflows/build_maple_map.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_nano_every.yml b/.github/workflows/build_nano_every.yml index 9a8bc58381..7037f3f671 100644 --- a/.github/workflows/build_nano_every.yml +++ b/.github/workflows/build_nano_every.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_nrf52840_dk.yml b/.github/workflows/build_nrf52840_dk.yml index 63d348377b..65d9cbbd6a 100644 --- a/.github/workflows/build_nrf52840_dk.yml +++ b/.github/workflows/build_nrf52840_dk.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_rp2040.yml b/.github/workflows/build_rp2040.yml index 5937fd16c1..6207c97762 100644 --- a/.github/workflows/build_rp2040.yml +++ b/.github/workflows/build_rp2040.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_rp2040_earle.yml b/.github/workflows/build_rp2040_earle.yml index de1171984b..5d46e695f7 100644 --- a/.github/workflows/build_rp2040_earle.yml +++ b/.github/workflows/build_rp2040_earle.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_rp2350.yml b/.github/workflows/build_rp2350.yml index d2ce2d09b3..11eb03d28e 100644 --- a/.github/workflows/build_rp2350.yml +++ b/.github/workflows/build_rp2350.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_teensy30.yml b/.github/workflows/build_teensy30.yml index 258dfcf677..fad87e8281 100644 --- a/.github/workflows/build_teensy30.yml +++ b/.github/workflows/build_teensy30.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_teensy31.yml b/.github/workflows/build_teensy31.yml index 8e12d297c2..29959f7259 100644 --- a/.github/workflows/build_teensy31.yml +++ b/.github/workflows/build_teensy31.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_teensy40.yml b/.github/workflows/build_teensy40.yml index 82d9bf5bf7..1d7164f71f 100644 --- a/.github/workflows/build_teensy40.yml +++ b/.github/workflows/build_teensy40.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_teensy41.yml b/.github/workflows/build_teensy41.yml index 97c859b1bb..43298a5700 100644 --- a/.github/workflows/build_teensy41.yml +++ b/.github/workflows/build_teensy41.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_teensy41_ofled.yml b/.github/workflows/build_teensy41_ofled.yml index 546ff42e6a..5beea758a7 100644 --- a/.github/workflows/build_teensy41_ofled.yml +++ b/.github/workflows/build_teensy41_ofled.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_teensyLC.yml b/.github/workflows/build_teensyLC.yml index 5c205be651..d78893b953 100644 --- a/.github/workflows/build_teensyLC.yml +++ b/.github/workflows/build_teensyLC.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_teensy_octo.yml b/.github/workflows/build_teensy_octo.yml index c346cf4c5e..8c20ef4f80 100644 --- a/.github/workflows/build_teensy_octo.yml +++ b/.github/workflows/build_teensy_octo.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_template.yml b/.github/workflows/build_template.yml index 2ef330f3ac..223db4968d 100644 --- a/.github/workflows/build_template.yml +++ b/.github/workflows/build_template.yml @@ -23,10 +23,6 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - - name: Install Python - uses: actions/setup-python@v5 - with: - python-version: '3.11' # disabled for now. @@ -38,8 +34,21 @@ jobs: # restore-keys: | # ${{ runner.os }}-build- - - name: Install UV - run: pip install uv + - name: Pin python version + run: | + echo "3.11" >> .python-version + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + cache-dependency-glob: "**/pyproject.toml" + + - name: "Set up Python" + uses: actions/setup-python@v5 + with: + python-version-file: ".python-version" + - name: Install Platform if: inputs.platform != '' diff --git a/.github/workflows/build_unit_test.yml b/.github/workflows/build_unit_test.yml index 5d33d2a5dc..61083ce601 100644 --- a/.github/workflows/build_unit_test.yml +++ b/.github/workflows/build_unit_test.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_uno.yml b/.github/workflows/build_uno.yml index 7307eecc96..0490d3d9e2 100644 --- a/.github/workflows/build_uno.yml +++ b/.github/workflows/build_uno.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_uno_r4_wifif.yml b/.github/workflows/build_uno_r4_wifif.yml index 022b609e7f..653bf18689 100644 --- a/.github/workflows/build_uno_r4_wifif.yml +++ b/.github/workflows/build_uno_r4_wifif.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/build_wasm.yml b/.github/workflows/build_wasm.yml index 61bc7b73a2..a274426292 100644 --- a/.github/workflows/build_wasm.yml +++ b/.github/workflows/build_wasm.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master @@ -41,7 +41,7 @@ jobs: - name: Install UV for python and npm packages run: | - pip install uv + pip install uv fastled npm install -g live-server - name: Verify live-server installation @@ -51,7 +51,7 @@ jobs: - name: Compile fastled.js, fastled.wasm, and index.html run: | - uv run ci/wasm_compile.py -b examples/wasm + fastled --just-compile examples/wasm continue-on-error: ${{ matrix.os != 'ubuntu-latest' }} shell: bash diff --git a/.github/workflows/build_yun.yml b/.github/workflows/build_yun.yml index aefb7cfbcf..e3be6d3cc1 100644 --- a/.github/workflows/build_yun.yml +++ b/.github/workflows/build_yun.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/check_attiny85.yml b/.github/workflows/check_attiny85.yml index f1d0c785ba..3ce42c465a 100644 --- a/.github/workflows/check_attiny85.yml +++ b/.github/workflows/check_attiny85.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/check_esp32_size.yml b/.github/workflows/check_esp32_size.yml index b25a021cbd..d130c83841 100644 --- a/.github/workflows/check_esp32_size.yml +++ b/.github/workflows/check_esp32_size.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/check_teensy41_size.yml b/.github/workflows/check_teensy41_size.yml index d67d45c28d..7db9a9b1e3 100644 --- a/.github/workflows/check_teensy41_size.yml +++ b/.github/workflows/check_teensy41_size.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/.github/workflows/check_uno_size.yml b/.github/workflows/check_uno_size.yml index e903f92b0e..0d02ce75cc 100644 --- a/.github/workflows/check_uno_size.yml +++ b/.github/workflows/check_uno_size.yml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master diff --git a/CMakeLists.txt b/CMakeLists.txt index 34d3423eb6..25f37ed481 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,18 +4,22 @@ cmake_minimum_required(VERSION 3.5) +# Collect all source files file(GLOB FastLED_SRCS "src/*.cpp") -file(GLOB FastLED_SRCS "src/fl/**/*.cpp") -file(GLOB FastLED_SRCS "src/sensors/**/*.cpp") -file(GLOB FastLED_SRCS "src/fx/**/*.cpp") +file(GLOB FastLED_FL_SRCS "src/fl/*.cpp") +file(GLOB FastLED_SENSORS_SRCS "src/sensors/*.cpp") +file(GLOB FastLED_FX_SRCS "src/fx/*.cpp" "src/fx/**/*.cpp") -file(GLOB ESP32_SRCS "src/platforms/esp/32/*.cpp") -file(GLOB ESP32_THIRD_PARTY "src/third_party/*.c") -file(GLOB ESP32_THIRD_PARTY "src/third_party/*.cpp") -list(APPEND FastLED_SRCS ${ESP32_SRCS}) +file(GLOB ESP32_SRCS "src/platforms/esp/32/*.cpp" "src/platforms/esp/32/rmt_5/*.cpp") +file(GLOB ESP32_THIRD_PARTY_SRCS "src/third_party/**/src/*.c" "src/third_party/**/src/*.cpp") +file(GLOB ESP32_LED_STRIP_SRCS "src/third_party/espressif/led_strip/src/*.c") -idf_component_register(SRCS ${FastLED_SRCS} ${ESP32_LED_STRIP_COMPONENT_SRCS} - INCLUDE_DIRS "src" +# Combine all source files into a single list +list(APPEND FastLED_SRCS ${FastLED_FL_SRCS} ${FastLED_SENSORS_SRCS} ${FastLED_FX_SRCS} ${ESP32_SRCS} ${ESP32_THIRD_PARTY_SRCS} ${ESP32_LED_STRIP_SRCS}) + +# Register the component with ESP-IDF +idf_component_register(SRCS ${FastLED_SRCS} + INCLUDE_DIRS "src" "src/third_party/espressif/led_strip/src" REQUIRES arduino-esp32 esp_driver_rmt driver) project(FastLED) diff --git a/README.md b/README.md index 21555eb03c..0c9820cbb9 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,6 @@ FastLED is a robust and massively parallel-led driver for Arduino, Esp32, Raspbe -*Hey everyone, Zach here. If you use or like this library then give us a star. I'm trying to make FastLED the #2 most popular library for Arduino. It's just one click, and you'll benefit from continued updates to FastLED.* - ## About @@ -85,6 +83,7 @@ For more examples see this [link](examples). Web compiled [examples](https://zac ![perpetualmaniac_an_led_display_in_a_room_lots_of_refaction_of_t_eb7c170a-7b2c-404a-b114-d33794b4954b](https://github.com/user-attachments/assets/982571fc-9b8d-4e58-93be-5bed76a0c53d) +*Note some users find that newer versions of the ESP32 arduino core (3.10) don't work very well, but older versions do, see [issue 1903](https://github.com/FastLED/FastLED/issues/1903) ## New in 3.9.8 - Massive Teensy 4.1 & 4.0 LED output ![New Project](https://github.com/user-attachments/assets/79dc2801-5161-4d5a-90a2-0126403e215f) @@ -121,7 +120,7 @@ Update: max overclock has been reported at +70%: https://www.reddit.com/r/FastLE [![attiny4313](https://github.com/FastLED/FastLED/actions/workflows/build_attiny4313.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_attiny4313.yml) -*Now compiles in the upcoming FastLED 3.9.14! - Very memory limited, so only tested against examples WS2812 Blink and APA102* +*New FastLED 3.9.14! - Very memory limited, so only tested against examples WS2812 Blink and APA102* [![yun](https://github.com/FastLED/FastLED/actions/workflows/build_yun.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_yun.yml) @@ -136,7 +135,7 @@ Update: max overclock has been reported at +70%: https://www.reddit.com/r/FastLE [![nano_every](https://github.com/FastLED/FastLED/actions/workflows/build_nano_every.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_nano_every.yml) [![Arduino Giga-R1](https://github.com/FastLED/FastLED/actions/workflows/build_giga_r1.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_giga_r1.yml) -*Multiple issues with this board. Pins aren't defined, F_CPU is not defined nor is it constant. And lots of other issues. Help wanted!* +*Now works in 3.9.14!* ### Teensy @@ -175,7 +174,7 @@ Update: max overclock has been reported at +70%: https://www.reddit.com/r/FastLE [![apollo3_red](https://github.com/FastLED/FastLED/actions/workflows/build_apollo3_red.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_apollo3_red.yml) *Board needs pin definitions.* -[![apollo3_thing_explorable](https://github.com/FastLED/FastLED/actions/workflows/build_apollo3_thing_explorable.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_apollo3_thing_explorable.yml) *Now Supported in the upcoming FastLED 3.9.12!* +[![apollo3_thing_explorable](https://github.com/FastLED/FastLED/actions/workflows/build_apollo3_thing_explorable.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_apollo3_thing_explorable.yml) ### STM @@ -227,6 +226,8 @@ Update: max overclock has been reported at +70%: https://www.reddit.com/r/FastLE [![esp32 extra libs](https://github.com/FastLED/FastLED/actions/workflows/build_esp_extra_libs.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_esp_extra_libs.yml) +[![esp32dev_namespace](https://github.com/FastLED/FastLED/actions/workflows/build_esp32dev_namespace.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_esp32dev_namespace.yml) + *Legacy Toolchains* [![esp32dev-idf3.3-lts](https://github.com/FastLED/FastLED/actions/workflows/build_esp32dev_idf3.3.yml/badge.svg)](https://github.com/FastLED/FastLED/actions/workflows/build_esp32dev_idf3.3.yml) @@ -295,6 +296,8 @@ I2S needs special setup as of 3.9.11 and earlier (current version of this writin The S3 is a CPU beast, but has half the RMT tx channels (4) and 2/3rds the I2S channels (16) of ESPDev. The S3 requires a special driver for I2S which you can find in this [example](https://github.com/FastLED/FastLED/blob/master/examples/Esp32S3I2SDemo/Esp32S3I2SDemo.ino) +*Note some users find that newer versions of the ESP32 arduino core (3.10) don't work very well, but older versions do, see [issue 1903](https://github.com/FastLED/FastLED/issues/1903) + ### RaspberriPi I (Zach Vorhies) don't use this platform. Help wanted on what the limits of this chip is. @@ -361,6 +364,7 @@ If you are looking for documentation on how something in the library works, plea If you run into bugs with the library, or if you'd like to request support for a particular platform or LED chipset, please submit an issue at http://fastled.io/issues. + ## Supported LED Chipsets Here's a list of all the LED chipsets are supported. More details on the LED chipsets are included [on our wiki page](https://github.com/FastLED/FastLED/wiki/Chipset-reference) @@ -457,6 +461,7 @@ Zach has this to say about FastLED: *"The true power of FastLED lies in its ability to transform programmers into LED artists. Free space becomes their canvas; bending light is their medium. FastLED is a collective effort by programmers who want to manifest the world that science fiction writers promised us. -- To contribute code to FastLED is to leave behind a piece of something immortal."* + ## Contributing See our easy to use guide here: diff --git a/RELEASE.md b/RELEASE.md index 179033aa6e..4998ba6af4 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -24,10 +24,10 @@ Release notes should list highlight changes (not necessarily all minor bug fixes Git commands to commit and tag release' ```bash -$ git commit -m "Rev 3.9.13 - HD107 turbo LED support" -$ git tag 3.9.13 master +$ git commit -am "Rev 3.9.14 - Fixes for mqtt, Giga R1, Sin32 interpretation" +$ git tag 3.9.14 master $ git push -$ git push origin 3.9.13 +$ git push origin 3.9.14 ``` Then use the GitHub UI to make a new “Release”: diff --git a/ci/ci/boards.py b/ci/ci/boards.py index f6c44842fb..abb417e80d 100644 --- a/ci/ci/boards.py +++ b/ci/ci/boards.py @@ -83,6 +83,11 @@ def __hash__(self) -> int: board_name="web", ) +DUE = Board( + board_name="due", + platform="atmelsam", +) + APOLLO3_RED_BOARD = Board( board_name="apollo3_red", real_board_name="SparkFun_RedBoard_Artemis_ATP", @@ -271,6 +276,7 @@ def __hash__(self) -> int: ALL: list[Board] = [ + DUE, WEBTARGET, APOLLO3_RED_BOARD, APOLLO3_SPARKFUN_THING_PLUS_EXPLORERABLE, diff --git a/ci/run_test_wasm_local_compile.py b/ci/run_test_wasm_local_compile.py index f19a34aca4..7a7c41f680 100644 --- a/ci/run_test_wasm_local_compile.py +++ b/ci/run_test_wasm_local_compile.py @@ -13,8 +13,16 @@ def test_build_all_examples(self) -> None: from fastled import Api, Test # type: ignore with Api.server(auto_updates=True) as server: - out = Test.test_examples(host=server) - self.assertEqual(0, len(out), f"Failed tests: {out}") + + exception_map = Test.test_examples(host=server) + if len(exception_map) > 0: + exception: Exception + msg: str = "" + for example, exception in exception_map.items(): + msg += f"Failed to compile example: {example}, error: {exception}\n" + self.fail(msg) + + # self.assertEqual(0, len(out), f"Failed tests: {out}") if __name__ == "__main__": diff --git a/ci/tests/test_no_using_namespace_fl_in_headers.py b/ci/tests/test_no_using_namespace_fl_in_headers.py index b73c8f5364..a782541982 100644 --- a/ci/tests/test_no_using_namespace_fl_in_headers.py +++ b/ci/tests/test_no_using_namespace_fl_in_headers.py @@ -13,6 +13,8 @@ class NoUsingNamespaceFlInHeaderTester(unittest.TestCase): def check_file(self, file_path) -> list[str]: + if "FastLED.h" in file_path: + return [] failings: list[str] = [] with open(file_path, "r", encoding="utf-8") as f: for line_number, line in enumerate(f, 1): diff --git a/ci/wasm_compile.py b/ci/wasm_compile.py index 1abd82c247..7028ebefba 100644 --- a/ci/wasm_compile.py +++ b/ci/wasm_compile.py @@ -1,8 +1,10 @@ import argparse import subprocess +import sys +from typing import List, Tuple -def parse_args() -> tuple[argparse.Namespace, list]: +def parse_args() -> Tuple[argparse.Namespace, list[str]]: parser = argparse.ArgumentParser(description="Compile wasm") parser.add_argument( "sketch_dir", @@ -15,12 +17,30 @@ def parse_args() -> tuple[argparse.Namespace, list]: return known_args, unknown_args -def main() -> None: +def run_command(cmd_list: List[str]) -> int: + """Run a command and return its exit code.""" + cmd_str = subprocess.list2cmdline(cmd_list) + print(f"Running command: {cmd_str}") + rtn = subprocess.call(cmd_list) + if rtn != 0: + print(f"ERROR: Command {cmd_str} failed with return code {rtn}") + return rtn + + +def main() -> int: args, unknown_args = parse_args() - cmd_list = ["fastled", args.sketch_dir, "--build", "--just-compile"] - cmd_list.extend(unknown_args) - subprocess.check_call(cmd_list) + + # First run the build command + build_cmd = ["fastled", args.sketch_dir, "--build"] + unknown_args + build_result = run_command(build_cmd) + + # Then run the compile command + compile_cmd = ["fastled", args.sketch_dir, "--just-compile"] + unknown_args + compile_result = run_command(compile_cmd) + + # Return non-zero if either command failed + return build_result if build_result != 0 else compile_result if __name__ == "__main__": - main() + sys.exit(main()) diff --git a/docs/Doxyfile b/docs/Doxyfile index 3457a7f54c..e0832c2055 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = FastLED # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 3.9.13 +PROJECT_NUMBER = 3.9.14 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/examples/Apa102HDOverride/Apa102HDOverride.ino b/examples/Apa102HDOverride/Apa102HDOverride.ino index f649dcc1a1..4b43aa9158 100644 --- a/examples/Apa102HDOverride/Apa102HDOverride.ino +++ b/examples/Apa102HDOverride/Apa102HDOverride.ino @@ -15,11 +15,11 @@ #define STRIP_1_DATA_PIN 3 #define STRIP_1_CLOCK_PIN 4 -void five_bit_hd_gamma_bitshift(CRGB colors, - CRGB scale, - uint8_t global_brightness, - CRGB* out_colors, - uint8_t *out_power_5bit) { +void fl::five_bit_hd_gamma_bitshift(CRGB colors, + CRGB scale, + uint8_t global_brightness, + CRGB* out_colors, + uint8_t *out_power_5bit) { // all 0 values for output *out_colors = CRGB(0, 0, 0); *out_power_5bit = 0; diff --git a/examples/Fire2023/Fire2023.ino b/examples/Fire2023/Fire2023.ino new file mode 100644 index 0000000000..04681e99cb --- /dev/null +++ b/examples/Fire2023/Fire2023.ino @@ -0,0 +1,250 @@ +/*This is a fire effect based on the famous Fire2012; but with various small improvements. +Perlin noise is being used to make a fire layer and a smoke layer; +and the overlay of both can make a quite realistic effect. + +The speed of both need to be adapted to the matrix size and width: +* Super small matrices (like 3x3 led) don't need the smoke +* medium sized matrices (8x8 for example) profit from fine tuning both Fire Speed/scale as well as Smoke speed/scale + +This code was adapted for a matrix with just four LED columns in 90° around a core and a height of 28. + +Right at the bottom of the code, you find a translation matrix that needs to be adapted to your set up. I included +a link to a helpful page for this. + +@repo https://github.com/Anderas2/Fire2023 +@author https://github.com/Anderas2 +*/ + + +#include "FastLED.h" +#include "fl/xymap.h" +#include "fl/screenmap.h" +#include "fl/vector.h" + +using namespace fl; + + +// matrix size +#define WIDTH 4 +#define HEIGHT 28 +#define CentreX (WIDTH / 2) - 1 +#define CentreY (HEIGHT / 2) - 1 + +// NUM_LEDS = WIDTH * HEIGHT +#define PIXELPIN 18 +#define NUM_LEDS 120 +#define LAST_VISIBLE_LED 119 + + +// Fire properties +#define BRIGHTNESS 255 +#define FIRESPEED 17 +#define FLAMEHEIGHT 3.8 // the higher the value, the higher the flame +#define FIRENOISESCALE 125 // small values, softer fire. Big values, blink fire. 0-255 + +// Smoke screen properties +// The smoke screen works best for big fire effects. It effectively cuts of a part of the flames +// from the rest, sometimes; which looks very much fire-like. For small fire effects with low +// LED count in the height, it doesn't help +// speed must be a little different and faster from Firespeed, to be visible. +// Dimmer should be somewhere in the middle for big fires, and low for small fires. +#define SMOKESPEED 25 // how fast the perlin noise is parsed for the smoke +#define SMOKENOISE_DIMMER 250 // thickness of smoke: the lower the value, the brighter the flames. 0-255 +#define SMOKENOISESCALE 125 // small values, softer smoke. Big values, blink smoke. 0-255 + +CRGB leds[NUM_LEDS]; + +// fire palette roughly like matlab "hot" colormap +// This was one of the most important parts to improve - fire color makes fire impression. +// position, r, g, b value. +// max value for "position" is BRIGHTNESS +DEFINE_GRADIENT_PALETTE(hot_gp) { + 27, 0, 0, 0, // black + 28, 140, 40, 0, // red + 30, 205, 80, 0, // orange + 155, 255, 100, 0, + 210, 255, 200, 0, // yellow + 255, 255, 255, 255 // white +}; +CRGBPalette32 hotPalette = hot_gp; + +// Map XY coordinates to numbers on the LED strip +uint8_t XY (uint8_t x, uint8_t y); + + +// parameters and buffer for the noise array +#define NUM_LAYERS 2 +// two layers of perlin noise make the fire effect +#define FIRENOISE 0 +#define SMOKENOISE 1 +uint32_t x[NUM_LAYERS]; +uint32_t y[NUM_LAYERS]; +uint32_t z[NUM_LAYERS]; +uint32_t scale_x[NUM_LAYERS]; +uint32_t scale_y[NUM_LAYERS]; + +uint8_t noise[NUM_LAYERS][WIDTH][HEIGHT]; +uint8_t noise2[NUM_LAYERS][WIDTH][HEIGHT]; + +uint8_t heat[NUM_LEDS]; + + +ScreenMap makeScreenMap(); + +void setup() { + + //Serial.begin(115200); + // Adjust this for you own setup. Use the hardware SPI pins if possible. + // On Teensy 3.1/3.2 the pins are 11 & 13 + // Details here: https://github.com/FastLED/FastLED/wiki/SPI-Hardware-or-Bit-banging + // In case you see flickering / glitching leds, reduce the data rate to 12 MHZ or less + auto screenMap = makeScreenMap(); + FastLED.addLeds(leds, NUM_LEDS).setScreenMap(screenMap); // Pin für Neopixel + FastLED.setBrightness(BRIGHTNESS); + FastLED.setDither(DISABLE_DITHER); +} + +void Fire2023(uint32_t now); + +void loop() { + EVERY_N_MILLISECONDS(8) { + Fire2023(millis()); + } + FastLED.show(); +} + +ScreenMap makeScreenMap() { + fl::HeapVector lut; + for (uint16_t y = 0; y < WIDTH; y++) { + for (uint16_t x = 0; x < HEIGHT; x++) { + pair_xy_float xy = {float(x) * 3, float(y) * 20}; + lut.push_back(xy); + } + } + return ScreenMap(lut.data(), lut.size(), 1); +} + +void Fire2023(uint32_t now) { + // some changing values + // these values are produced by perlin noise to add randomness and smooth transitions + uint16_t ctrl1 = inoise16(11 * now, 0, 0); + uint16_t ctrl2 = inoise16(13 * now, 100000, 100000); + uint16_t ctrl = ((ctrl1 + ctrl2) >> 1); + + // parameters for the fire heat map + x[FIRENOISE] = 3 * ctrl * FIRESPEED; + y[FIRENOISE] = 20 * now * FIRESPEED; + z[FIRENOISE] = 5 * now * FIRESPEED; + scale_x[FIRENOISE] = scale8(ctrl1, FIRENOISESCALE); + scale_y[FIRENOISE] = scale8(ctrl2, FIRENOISESCALE); + + //calculate the perlin noise data for the fire + for (uint8_t x_count = 0; x_count < WIDTH; x_count++) { + uint32_t xoffset = scale_x[FIRENOISE] * (x_count - CentreX); + for (uint8_t y_count = 0; y_count < HEIGHT; y_count++) { + uint32_t yoffset = scale_y[FIRENOISE] * (y_count - CentreY); + uint16_t data = ((inoise16(x[FIRENOISE] + xoffset, y[FIRENOISE] + yoffset, z[FIRENOISE])) + 1); + noise[FIRENOISE][x_count][y_count] = data >> 8; + } + } + + // parameters for the smoke map + x[SMOKENOISE] = 3 * ctrl * SMOKESPEED; + y[SMOKENOISE] = 20 * now * SMOKESPEED; + z[SMOKENOISE] = 5 * now * SMOKESPEED; + scale_x[SMOKENOISE] = scale8(ctrl1, SMOKENOISESCALE); + scale_y[SMOKENOISE] = scale8(ctrl2, SMOKENOISESCALE); + + //calculate the perlin noise data for the smoke + for (uint8_t x_count = 0; x_count < WIDTH; x_count++) { + uint32_t xoffset = scale_x[SMOKENOISE] * (x_count - CentreX); + for (uint8_t y_count = 0; y_count < HEIGHT; y_count++) { + uint32_t yoffset = scale_y[SMOKENOISE] * (y_count - CentreY); + uint16_t data = ((inoise16(x[SMOKENOISE] + xoffset, y[SMOKENOISE] + yoffset, z[SMOKENOISE])) + 1); + noise[SMOKENOISE][x_count][y_count] = data / SMOKENOISE_DIMMER; + } + } + + //copy everything one line up + for (uint8_t y = 0; y < HEIGHT - 1; y++) { + for (uint8_t x = 0; x < WIDTH; x++) { + heat[XY(x, y)] = heat[XY(x, y + 1)]; + } + } + + // draw lowest line - seed the fire where it is brightest and hottest + for (uint8_t x = 0; x < WIDTH; x++) { + heat[XY(x, HEIGHT-1)] = noise[FIRENOISE][WIDTH - x][CentreX]; + //if (heat[XY(x, HEIGHT-1)] < 200) heat[XY(x, HEIGHT-1)] = 150; + } + + // dim the flames based on FIRENOISE noise. + // if the FIRENOISE noise is strong, the led goes out fast + // if the FIRENOISE noise is weak, the led stays on stronger. + // once the heat is gone, it stays dark. + for (uint8_t y = 0; y < HEIGHT - 1; y++) { + for (uint8_t x = 0; x < WIDTH; x++) { + uint8_t dim = noise[FIRENOISE][x][y]; + // high value in FLAMEHEIGHT = less dimming = high flames + dim = dim / FLAMEHEIGHT; + dim = 255 - dim; + heat[XY(x, y)] = scale8(heat[XY(x, y)] , dim); + + // map the colors based on heatmap + // use the heat map to set the color of the LED from the "hot" palette + // whichpalette position brightness blend or not + leds[XY(x, y)] = ColorFromPalette(hotPalette, heat[XY(x, y)], heat[XY(x, y)], LINEARBLEND); + + // dim the result based on SMOKENOISE noise + // this is not saved in the heat map - the flame may dim away and come back + // next iteration. + leds[XY(x, y)].nscale8(noise[SMOKENOISE][x][y]); + + } + } +} + +/* Physical layout of LED strip ****************************/ +uint8_t XY (uint8_t x, uint8_t y) { + // any out of bounds address maps to the first hidden pixel + // https://macetech.github.io/FastLED-XY-Map-Generator/ + if ( (x >= WIDTH) || (y >= HEIGHT) ) { + return (LAST_VISIBLE_LED + 1); + } + const uint8_t XYTable[] = { + 25, 26, 81, 82, + 25, 27, 81, 83, + 25, 28, 80, 84, + 24, 29, 79, 85, + 23, 30, 78, 86, + 22, 31, 77, 87, + 21, 32, 76, 88, + 20, 33, 75, 89, + 19, 34, 74, 90, + 18, 35, 73, 91, + 17, 36, 72, 92, + 16, 37, 71, 93, + 15, 38, 70, 94, + 14, 39, 69, 95, + 13, 40, 68, 96, + 12, 41, 67, 97, + 11, 42, 66, 98, + 10, 43, 65, 99, + 9, 44, 64, 100, + 8, 45, 63, 101, + 7, 46, 62, 102, + 6, 47, 61, 103, + 5, 48, 60, 104, + 4, 49, 59, 105, + 3, 50, 58, 106, + 2, 51, 57, 107, + 1, 52, 56, 108, + 0, 53, 55, 109 + }; + + uint8_t i = (y * WIDTH) + x; + uint8_t j = XYTable[i]; + return j; +} + + diff --git a/examples/LuminescentGrand/arduino/LedRopeTCL.h b/examples/LuminescentGrand/arduino/LedRopeTCL.h index 156f16f020..d8016f5842 100644 --- a/examples/LuminescentGrand/arduino/LedRopeTCL.h +++ b/examples/LuminescentGrand/arduino/LedRopeTCL.h @@ -15,6 +15,8 @@ #include "crgb.h" #include "fl/screenmap.h" +using namespace fl; + // LedRopeTCL is a C++ wrapper around the Total Control Lighting LED rope // device driver (TCL.h). This wrapper includes automatic setup of the LED // rope and allows the user to use a graphics-state like interface for diff --git a/library.json b/library.json index 788f4a06cf..d1cae870f6 100644 --- a/library.json +++ b/library.json @@ -38,7 +38,7 @@ "type": "git", "url": "https://github.com/FastLED/FastLED.git" }, - "version": "3.9.13", + "version": "3.9.14", "license": "MIT", "homepage": "http://fastled.io", "frameworks": "arduino", diff --git a/library.properties b/library.properties index 481e667a21..8946e86999 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=FastLED -version=3.9.13 +version=3.9.14 author=Daniel Garcia maintainer=Daniel Garcia sentence=Multi-platform library for controlling dozens of different types of LEDs along with optimized math, effect, and noise functions. diff --git a/lint b/lint index d0412e163f..0dc7fdb14b 100755 --- a/lint +++ b/lint @@ -50,24 +50,16 @@ EOL # Linting the Python code. echo "Running ruff check" uvx ruff check --fix ci --exclude ci/tmp/ --exclude ci/wasm/ -uvx ruff check --fix src/platforms/wasm/compiler/compile.py -uvx ruff check --fix src/platforms/wasm/compiler/server.py UV run ruff check --fix dev/dev.py echo Running black uvx black ci --exclude ci/tmp/ --exclude ci/wasm/ -uvx black src/platforms/wasm/compiler/compile.py -uvx black src/platforms/wasm/compiler/server.py uvx black dev/dev.py uvx echo Running isort uvx isort --profile black ci --skip ci/tmp/ --skip ci/wasm/ -uvx isort --profile black src/platforms/wasm/compiler/compile.py -uvx isort --profile black src/platforms/wasm/compiler/server.py uvx isort --profile black dev/dev.py echo "Running mypy" uvx echo yes | mypy --install-type uvx mypy ci --exclude ci/tmp/ --exclude ci/wasm/ -uvx mypy src/platforms/wasm/compiler/compile.py -uvx mypy src/platforms/wasm/compiler/server.py uvx mypy dev/dev.py diff --git a/platformio.ini b/platformio.ini index b1f91f682a..fe41597145 100644 --- a/platformio.ini +++ b/platformio.ini @@ -31,6 +31,11 @@ platform = atmelavr board = uno framework = arduino +[env:giga_r1_m7] +platform = ststm32 +board = giga_r1_m7 +framework = arduino + [env:esp32s3] extends = env:generic-esp board = seeed_xiao_esp32s3 @@ -59,7 +64,7 @@ build_flags = ${env:generic-esp.build_flags} [env:esp32c2] extends = env:generic-esp platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF5 -board = esp32-c2-devkitm-1 +board = esp32-c2-devkitm-1bjec build_flags = ${env:generic-esp.build_flags} diff --git a/pyproject.toml b/pyproject.toml index 5aa8e29682..7cd63e37e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "Add your description here" readme = "README.md" requires-python = ">=3.11" dependencies = [ - "platformio==6.1.16", + "platformio==6.1.17", "fastled-wasm", "python-dateutil", "ruff", diff --git a/release_notes.md b/release_notes.md index 172b6aadc5..8a111483c5 100644 --- a/release_notes.md +++ b/release_notes.md @@ -1,8 +1,15 @@ +FastLED 3.9.15 +============== +* ESP32 series now supports FORCE_FASTLED_NAMESPACE=1 + FastLED 3.9.14 ============== -* Attiny13 and Attiny4343 now works +* Attiny4343 now works * https://github.com/FastLED/FastLED/pull/1874 * Thanks https://github.com/sutaburosu! +* Arduino GIGA Now working + * Thank you [@RubixCubix!](https://github.com/RubiCubix) +* Fix for mqtt build modes: https://github.com/FastLED/FastLED/issues/1884 FastLED 3.9.13 ============== diff --git a/src/FastLED.cpp b/src/FastLED.cpp index d4471b0f77..0372b3d169 100644 --- a/src/FastLED.cpp +++ b/src/FastLED.cpp @@ -23,14 +23,15 @@ volatile uint32_t fuckit; #endif -#ifndef FASTLED_DEFINE_WEAK_YEILD_FUNCTION -#if defined(__AVR_ATtiny13__) -// Arduino.h also defines this as a weak function on this platform. -#define FASTLED_DEFINE_WEAK_YEILD_FUNCTION 0 -#else -#define FASTLED_DEFINE_WEAK_YEILD_FUNCTION 1 -#endif -#endif +// Disable to fix build breakage. +// #ifndef FASTLED_DEFINE_WEAK_YEILD_FUNCTION +// #if defined(__AVR_ATtiny13__) +// // Arduino.h also defines this as a weak function on this platform. +// #define FASTLED_DEFINE_WEAK_YEILD_FUNCTION 0 +// #else +// #define FASTLED_DEFINE_WEAK_YEILD_FUNCTION 1 +// #endif +// #endif /// Has to be declared outside of any namespaces. /// Called at program exit when run in a desktop environment. @@ -38,8 +39,8 @@ volatile uint32_t fuckit; /// @returns 0 to indicate success extern "C" __attribute__((weak)) int atexit(void (* /*func*/ )()) { return 0; } -#if FASTLED_DEFINE_WEAK_YEILD_FUNCTION -extern "C" __attribute__((weak)) void yield(void) { } +#ifdef FASTLED_NEEDS_YIELD +extern "C" void yield(void) { } #endif FASTLED_NAMESPACE_BEGIN diff --git a/src/FastLED.h b/src/FastLED.h index 2ebb6d4f27..3ef72518fb 100644 --- a/src/FastLED.h +++ b/src/FastLED.h @@ -16,13 +16,13 @@ /// * 1 digit for the major version /// * 3 digits for the minor version /// * 3 digits for the patch version -#define FASTLED_VERSION 3009013 +#define FASTLED_VERSION 3009014 #ifndef FASTLED_INTERNAL # ifdef FASTLED_SHOW_VERSION # ifdef FASTLED_HAS_PRAGMA_MESSAGE -# pragma message "FastLED version 3.009.013" +# pragma message "FastLED version 3.009.014" # else -# warning FastLED version 3.009.013 (Not really a warning, just telling you here.) +# warning FastLED version 3.009.014 (Not really a warning, just telling you here.) # endif # endif #endif @@ -846,11 +846,18 @@ FASTLED_NAMESPACE_END #ifdef FASTLED_UI // As a convenience, include the UI headers and bring them into the global namespace #include "fl/ui.h" +#include "fl/xymap.h" using fl::UIButton; using fl::UICheckbox; using fl::UINumberField; using fl::UISlider; +using fl::XYMap; #define FASTLED_TITLE(text) fl::UITitle g_title(text) #define FASTLED_DESCRIPTION(text) fl::UIDescription g_description(text) #endif // FASTLED_UI +#if defined(FASTLED_FORCE_USE_NAMESPACE) && FASTLED_FORCE_USE_NAMESPACE==1 +using namespace fl; +#endif + + diff --git a/src/chipsets.h b/src/chipsets.h index 7ad834518d..42210406be 100644 --- a/src/chipsets.h +++ b/src/chipsets.h @@ -2,11 +2,12 @@ #define __INC_CHIPSETS_H #include "pixeltypes.h" -#include "five_bit_hd_gamma.h" +#include "fl/five_bit_hd_gamma.h" #include "fl/force_inline.h" #include "pixel_iterator.h" #include "crgb.h" #include "eorder.h" +#include "fl/namespace.h" @@ -103,6 +104,7 @@ FASTLED_NAMESPACE_END // a side buffer dedicated for the RGBW data. The RGB data is then converted to RGBW // and sent to the delegate controller for rendering as if it were RGB data. FASTLED_NAMESPACE_BEGIN + template < typename CONTROLLER, EOrder RGB_ORDER = GRB> // Default on WS2812> @@ -364,7 +366,7 @@ template < // "just works" over "fastest possible" here. // https://www.pjrc.com/why-apa102-leds-have-trouble-at-24-mhz/ uint32_t SPI_SPEED = DATA_RATE_MHZ(6), - FiveBitGammaCorrectionMode GAMMA_CORRECTION_MODE = kFiveBitGammaCorrectionMode_Null, + fl::FiveBitGammaCorrectionMode GAMMA_CORRECTION_MODE = fl::kFiveBitGammaCorrectionMode_Null, uint32_t START_FRAME = 0x00000000, uint32_t END_FRAME = 0xFF000000 > @@ -425,11 +427,11 @@ class APA102Controller : public CPixelLEDController { /// @copydoc CPixelLEDController::showPixels() virtual void showPixels(PixelController & pixels) override { switch (GAMMA_CORRECTION_MODE) { - case kFiveBitGammaCorrectionMode_Null: { + case fl::kFiveBitGammaCorrectionMode_Null: { showPixelsDefault(pixels); break; } - case kFiveBitGammaCorrectionMode_BitShift: { + case fl::kFiveBitGammaCorrectionMode_BitShift: { showPixelsGammaBitShift(pixels); break; } @@ -531,7 +533,7 @@ class APA102ControllerHD : public APA102Controller< CLOCK_PIN, RGB_ORDER, SPI_SPEED, - kFiveBitGammaCorrectionMode_BitShift, + fl::kFiveBitGammaCorrectionMode_BitShift, uint32_t(0x00000000), uint32_t(0x00000000)> { public: @@ -555,7 +557,7 @@ class SK9822Controller : public APA102Controller< CLOCK_PIN, RGB_ORDER, SPI_SPEED, - kFiveBitGammaCorrectionMode_Null, + fl::kFiveBitGammaCorrectionMode_Null, 0x00000000, 0x00000000 > { @@ -577,7 +579,7 @@ class SK9822ControllerHD : public APA102Controller< CLOCK_PIN, RGB_ORDER, SPI_SPEED, - kFiveBitGammaCorrectionMode_BitShift, + fl::kFiveBitGammaCorrectionMode_BitShift, 0x00000000, 0x00000000 > { @@ -596,7 +598,7 @@ class HD107Controller : public APA102Controller< CLOCK_PIN, RGB_ORDER, SPI_SPEED, - kFiveBitGammaCorrectionMode_Null, + fl::kFiveBitGammaCorrectionMode_Null, 0x00000000, 0x00000000 > {}; diff --git a/src/five_bit_hd_gamma.cpp b/src/fl/five_bit_hd_gamma.cpp similarity index 99% rename from src/five_bit_hd_gamma.cpp rename to src/fl/five_bit_hd_gamma.cpp index 72092c622e..f7f7917119 100644 --- a/src/five_bit_hd_gamma.cpp +++ b/src/fl/five_bit_hd_gamma.cpp @@ -14,8 +14,7 @@ // Author: Zach Vorhies - -FASTLED_NAMESPACE_BEGIN +namespace fl { namespace { template T mymax(T a, T b) { return (a > b) ? a : b; } @@ -159,4 +158,4 @@ void __builtin_five_bit_hd_gamma_bitshift(CRGB colors, CRGB colors_scale, out_power_5bit); } -FASTLED_NAMESPACE_END +} // namespace fl diff --git a/src/five_bit_hd_gamma.h b/src/fl/five_bit_hd_gamma.h similarity index 98% rename from src/five_bit_hd_gamma.h rename to src/fl/five_bit_hd_gamma.h index 59e2679611..4c4174e607 100644 --- a/src/five_bit_hd_gamma.h +++ b/src/fl/five_bit_hd_gamma.h @@ -9,7 +9,7 @@ #include "fl/force_inline.h" #include "crgb.h" -FASTLED_NAMESPACE_BEGIN +namespace fl { enum FiveBitGammaCorrectionMode { kFiveBitGammaCorrectionMode_Null = 0, @@ -90,5 +90,5 @@ void five_bit_hd_gamma_function(CRGB color, uint16_t *r16, uint16_t *g16, uint16_t *b16); #endif // FASTLED_FIVE_BIT_HD_GAMMA_FUNCTION_OVERRIDE -FASTLED_NAMESPACE_END +} // namespace fl diff --git a/src/fl/sin32.cpp b/src/fl/sin32.cpp new file mode 100644 index 0000000000..ad83fac112 --- /dev/null +++ b/src/fl/sin32.cpp @@ -0,0 +1,35 @@ + + +#include + +namespace fl { + +const int16_t sinLut[] = +{ + 0, 804, 1608, 2410, 3212, 4011, 4808, 5602, 6393, 7179, 7962, 8739, 9512, 10278, 11039, 11793, + 12539, 13279, 14010, 14732, 15446, 16151, 16846, 17530, 18204, 18868, 19519, 20159, 20787, 21403, 22005, 22594, + 23170, 23731, 24279, 24811, 25329, 25832, 26319, 26790, 27245, 27683, 28105, 28510, 28898, 29268, 29621, 29956, + 30273, 30571, 30852, 31113, 31356, 31580, 31785, 31971, 32137, 32285, 32412, 32521, 32609, 32678, 32728, 32757, + 32767, 32757, 32728, 32678, 32609, 32521, 32412, 32285, 32137, 31971, 31785, 31580, 31356, 31113, 30852, 30571, + 30273, 29956, 29621, 29268, 28898, 28510, 28105, 27683, 27245, 26790, 26319, 25832, 25329, 24811, 24279, 23731, + 23170, 22594, 22005, 21403, 20787, 20159, 19519, 18868, 18204, 17530, 16846, 16151, 15446, 14732, 14010, 13279, + 12539, 11793, 11039, 10278, 9512, 8739, 7962, 7179, 6393, 5602, 4808, 4011, 3212, 2410, 1608, 804, + 0, -804, -1608, -2410, -3212, -4011, -4808, -5602, -6393, -7179, -7962, -8739, -9512, -10278, -11039, -11793, +-12539, -13279, -14010, -14732, -15446, -16151, -16846, -17530, -18204, -18868, -19519, -20159, -20787, -21403, -22005, -22594, +-23170, -23731, -24279, -24811, -25329, -25832, -26319, -26790, -27245, -27683, -28105, -28510, -28898, -29268, -29621, -29956, +-30273, -30571, -30852, -31113, -31356, -31580, -31785, -31971, -32137, -32285, -32412, -32521, -32609, -32678, -32728, -32757, +-32767, -32757, -32728, -32678, -32609, -32521, -32412, -32285, -32137, -31971, -31785, -31580, -31356, -31113, -30852, -30571, +-30273, -29956, -29621, -29268, -28898, -28510, -28105, -27683, -27245, -26790, -26319, -25832, -25329, -24811, -24279, -23731, +-23170, -22594, -22005, -21403, -20787, -20159, -19519, -18868, -18204, -17530, -16846, -16151, -15446, -14732, -14010, -13279, +-12539, -11793, -11039, -10278, -9512, -8739, -7962, -7179, -6393, -5602, -4808, -4011, -3212, -2410, -1608, -804, + 0, 804, 1608, 2410, 3212, 4011, 4808, 5602, 6393, 7179, 7962, 8739, 9512, 10278, 11039, 11793, + 12539, 13279, 14010, 14732, 15446, 16151, 16846, 17530, 18204, 18868, 19519, 20159, 20787, 21403, 22005, 22594, + 23170, 23731, 24279, 24811, 25329, 25832, 26319, 26790, 27245, 27683, 28105, 28510, 28898, 29268, 29621, 29956, + 30273, 30571, 30852, 31113, 31356, 31580, 31785, 31971, 32137, 32285, 32412, 32521, 32609, 32678, 32728, 32757, + 32767}; + +const int16_t *sinArray = &sinLut[0]; + +const int16_t *cosArray = &sinLut[64]; + +} // namespace fl diff --git a/src/fl/sin32.h b/src/fl/sin32.h new file mode 100644 index 0000000000..d877a03338 --- /dev/null +++ b/src/fl/sin32.h @@ -0,0 +1,70 @@ +#pragma once + + +#include + +#include "namespace.h" +#include "force_inline.h" + +namespace fl { + +// fast and accurate sin and cos approximations +// they differ only 0.01 % from actual sinf and cosf funtions +// sin32 and cos32 use 24 bit unsigned integer arguments +// 0 to 16777216 is one cycle +// they output an integer between -2147418112 and 2147418112 +// that's 32767 * 65536 +// this is because I use int16_t look up table to interpolate the results +// +// sin16 and cos16 are faster and more accurate funtions that take uint16_t as arguments +// and return int16_t as output +// they can replace the older implementation and are 62 times more accurate and twice as fast +// the downside with these funtions is that they use 640 bytes for the look up table +// thats a lot for old microcontrollers but nothing for modern ones +// +// sin32 and cos32 take about 13 cyces to execute on an esp32 +// they're 32 times faster than sinf and cosf +// you can use choose to use these new by writing +// #define USE_SIN_32 before #include "FastLED.h" + +extern const int16_t *sinArray; + +extern const int16_t *cosArray; + +// 0 to 16777216 is a full circle +// output is between -2147418112 and 2147418112 +FASTLED_FORCE_INLINE static int32_t sin32(uint32_t angle) +{ + uint8_t angle256 = angle / 65536; + int32_t subAngle = angle % 65536; + return sinArray[angle256] * (65536 - subAngle) + sinArray[angle256 + 1] * subAngle; +} + +// 0 to 16777216 is a full circle +// output is between -2147418112 and 2147418112 +FASTLED_FORCE_INLINE static int32_t cos32(uint32_t angle) +{ + uint8_t angle256 = angle / 65536; + int32_t subAngle = angle % 65536; + return cosArray[angle256] * (65536 - subAngle) + cosArray[angle256 + 1] * subAngle; +} + +// 0 to 65536 is a full circle +// output is between -32767 and 32767 +FASTLED_FORCE_INLINE static int16_t sin16lut(uint16_t angle) +{ + uint8_t angle256 = angle / 256; + int32_t subAngle = angle % 256; + return (sinArray[angle256] * (256 - subAngle) + sinArray[angle256 + 1] * subAngle) / 256; +} + +// 0 to 65536 is a full circle +// output is between -32767 and 32767 +FASTLED_FORCE_INLINE static int16_t cos16lut(uint16_t angle) +{ + uint8_t angle256 = angle / 256; + int32_t subAngle = angle % 256; + return (cosArray[angle256] * (256 - subAngle) + cosArray[angle256 + 1] * subAngle) / 256; +} + +} // namespace fl diff --git a/src/fx/2d/noisepalette.cpp b/src/fx/2d/noisepalette.cpp index e2bdf02fd0..450239e61e 100644 --- a/src/fx/2d/noisepalette.cpp +++ b/src/fx/2d/noisepalette.cpp @@ -33,10 +33,10 @@ NoisePalette::NoisePalette(XYMap xyMap, float fps) setPalettePreset(0); // Allocate memory for the noise array using scoped_ptr - noise = scoped_ptr(new uint8_t[width * height]); + noise = scoped_array(new uint8_t[width * height]); } -void NoisePalette::setPalettePreset(int paletteIndex) { +void NoisePalette::setPalettePreset(int paletteIndex) { currentPaletteIndex = paletteIndex % 12; // Ensure the index wraps around switch (currentPaletteIndex) { case 0: diff --git a/src/fx/2d/noisepalette.h b/src/fx/2d/noisepalette.h index 150215162f..5ff6dc6b9b 100644 --- a/src/fx/2d/noisepalette.h +++ b/src/fx/2d/noisepalette.h @@ -60,7 +60,7 @@ class NoisePalette : public Fx2d { uint16_t width, height; uint16_t speed = 0; uint16_t scale = 0; - fl::scoped_ptr noise; + fl::scoped_array noise; CRGBPalette16 currentPalette; bool colorLoop = 0; int currentPaletteIndex = 0; diff --git a/src/fx/readme b/src/fx/readme index 5025438146..30d5b11a89 100644 --- a/src/fx/readme +++ b/src/fx/readme @@ -15,18 +15,15 @@ As a result, FX components can be "heavyweight," meaning they may include a larg *.hpp are somewhere between a standard header and a *.cpp file. While a standard header typically only attempts to declare data layout, functions and classes, *.hpp files are typically much more heavy weight and will happy inject global data into your compilation unit. Because of this, *.hpp files should only ever be included once. -Another reason is licensing. Starting with FastLED 4.0, we now distribute some amazing code that has restrictive commercial licensing requirements. If -we included these by default into the core driver then that would be problematic for the user. Therefore such files are NOT compiled by default and -must be included by the user explicitly as an *.hpp file. ## Licensing -Some FX components are public domain and can be freely used or copied in commercial applications without the need for a license or permission. +Everything in this library is under FastLED standard license except the following: -However, not all FX components come with permissive licenses for commercial use. If you include an FX component with restrictions, you will see a compilation warning indicating its non-commercial license. It is your responsibility to contact the creator for proper licensing if used in commercial products. + * Animartrix -For non-commercial, artistic projects where no financial transactions are involved, you generally don't need to worry about these restrictions. +Animartrix is free for non commercial use, paid license otherwise. -Certain higher-end FX components, such as the fx component "animatrix," have more restrictive licensing, but they remain free for non-commercial use. +Optional code modules are tagged as *.hpp. -Happy coding! \ No newline at end of file +Happy coding! diff --git a/src/hsv2rgb.cpp b/src/hsv2rgb.cpp index 9799137c7c..a4cf5a521f 100644 --- a/src/hsv2rgb.cpp +++ b/src/hsv2rgb.cpp @@ -422,8 +422,11 @@ void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb) //nscale8x3_video( r, g, b, sat); #if (FASTLED_SCALE8_FIXED==1) r = scale8_LEAVING_R1_DIRTY( r, satscale); + asm volatile(""); // Fixes jumping red pixel: https://github.com/FastLED/FastLED/pull/943 g = scale8_LEAVING_R1_DIRTY( g, satscale); + asm volatile(""); b = scale8_LEAVING_R1_DIRTY( b, satscale); + asm volatile(""); cleanup_R1(); #else if( r ) r = scale8( r, satscale) + 1; @@ -447,8 +450,11 @@ void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb) // nscale8x3_video( r, g, b, val); #if (FASTLED_SCALE8_FIXED==1) r = scale8_LEAVING_R1_DIRTY( r, val); + asm volatile(""); // Fixes jumping red pixel: https://github.com/FastLED/FastLED/pull/943 g = scale8_LEAVING_R1_DIRTY( g, val); + asm volatile(""); b = scale8_LEAVING_R1_DIRTY( b, val); + asm volatile(""); cleanup_R1(); #else if( r ) r = scale8( r, val) + 1; diff --git a/src/led_sysdefs.h b/src/led_sysdefs.h index 9d245accb2..b7528f5468 100644 --- a/src/led_sysdefs.h +++ b/src/led_sysdefs.h @@ -32,7 +32,7 @@ #elif defined(__SAM3X8E__) // Include sam/due headers #include "platforms/arm/sam/led_sysdefs_arm_sam.h" -#elif defined(STM32F10X_MD) || defined(__STM32F1__) || defined(STM32F2XX) || defined(STM32F1) +#elif defined(STM32F10X_MD) || defined(__STM32F1__) || defined(STM32F2XX) || defined(STM32F1) || defined(STM32F407xx) #include "platforms/arm/stm32/led_sysdefs_arm_stm32.h" #elif defined(__SAMD21G18A__) || defined(__SAMD21J18A__) || defined(__SAMD21E17A__) || defined(__SAMD21E18A__) #include "platforms/arm/d21/led_sysdefs_arm_d21.h" @@ -53,6 +53,8 @@ #include "platforms/apollo3/led_sysdefs_apollo3.h" #elif defined(ARDUINO_ARCH_RENESAS) || defined(ARDUINO_ARCH_RENESAS_UNO) || defined(ARDUINO_ARCH_RENESAS_PORTENTA) #include "platforms/arm/renesas/led_sysdef_arm_renesas.h" +#elif defined(ARDUINO_GIGA)|| defined(ARDUINO_GIGA_M7) +#include "platforms/arm/giga/led_sysdef_arm_giga.h" #elif defined(__x86_64__) || defined(FASTLED_STUB_IMPL) || defined(__APPLE__) || defined(__linux__) || defined(__unix__) || defined(__EMSCRIPTEN__) // Not on a microcontroller //# ifdef FASTLED_HAS_PRAGMA_MESSAGE diff --git a/src/lib8tion/trig8.h b/src/lib8tion/trig8.h index 8af033369e..fe42dbfa5f 100644 --- a/src/lib8tion/trig8.h +++ b/src/lib8tion/trig8.h @@ -20,7 +20,14 @@ /// the 8-bit approximation is more than 20X faster. /// @{ -#if defined(__AVR__) +#if defined(USE_SIN_32) + +#define sin16 fl::sin16lut +#define cos16 fl::cos16lut + +#include "fl/sin32.h" + +#elif defined(__AVR__) /// Platform-independent alias of the fast sin implementation #define sin16 sin16_avr @@ -132,7 +139,9 @@ LIB8STATIC int16_t sin16_C(uint16_t theta) { /// /// @param theta input angle from 0-65535 /// @returns cos of theta, value between -32767 to 32767. +#ifndef USE_SIN_32 LIB8STATIC int16_t cos16(uint16_t theta) { return sin16(theta + 16384); } +#endif /////////////////////////////////////////////////////////////////////// // sin8() and cos8() diff --git a/src/pixel_controller.h b/src/pixel_controller.h index ee8cbce6ef..8c5d49553f 100644 --- a/src/pixel_controller.h +++ b/src/pixel_controller.h @@ -13,7 +13,7 @@ #include "FastLED.h" #include "rgbw.h" -#include "five_bit_hd_gamma.h" +#include "fl/five_bit_hd_gamma.h" #include "fl/force_inline.h" #include "fl/namespace.h" #include "eorder.h" @@ -493,7 +493,7 @@ struct PixelController { brightness = 255; CRGB scale = mColorAdjustment.premixed; #endif - five_bit_hd_gamma_bitshift( + fl::five_bit_hd_gamma_bitshift( rgb, scale, brightness, diff --git a/src/platforms.h b/src/platforms.h index 87f651fb0c..f77aaa3781 100644 --- a/src/platforms.h +++ b/src/platforms.h @@ -29,7 +29,7 @@ #elif defined(__SAM3X8E__) // Include sam/due headers #include "platforms/arm/sam/fastled_arm_sam.h" -#elif defined(STM32F10X_MD) || defined(__STM32F1__) || defined(STM32F2XX) || defined(STM32F1) +#elif defined(STM32F10X_MD) || defined(__STM32F1__) || defined(STM32F2XX) || defined(STM32F1) || defined(STM32F407xx) #include "platforms/arm/stm32/fastled_arm_stm32.h" #elif defined(__SAMD21G18A__) || defined(__SAMD21J18A__) || defined(__SAMD21E17A__) || defined(__SAMD21E18A__) #include "platforms/arm/d21/fastled_arm_d21.h" @@ -46,6 +46,8 @@ #include "platforms/apollo3/fastled_apollo3.h" #elif defined(ARDUINO_ARCH_RENESAS) || defined(ARDUINO_ARCH_RENESAS_UNO) || defined(ARDUINO_ARCH_RENESAS_PORTENTA) #include "platforms/arm/renesas/fastled_arm_renesas.h" +#elif defined(ARDUINO_GIGA) || defined(ARDUINO_GIGA_M7) +#include "platforms/arm/giga/fastled_arm_giga.h" #elif defined(__x86_64__) || defined(FASTLED_STUB_IMPL) // stub platform for testing (on cpu) diff --git a/src/platforms/arm/giga/armpin.h b/src/platforms/arm/giga/armpin.h new file mode 100644 index 0000000000..b36931902e --- /dev/null +++ b/src/platforms/arm/giga/armpin.h @@ -0,0 +1,50 @@ +#pragma once +#include + +#include "fl/namespace.h" + +FASTLED_NAMESPACE_BEGIN + +#define _R(T) struct __gen_struct_ ## T +#define _FL_DEFPIN(PIN, BIT, L) template<> class FastPin : public _ARMPIN {}; + +/// Template definition for STM32 style ARM pins, providing direct access to the various GPIO registers. Note that this +/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found +/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning. +/// The registers are data output, set output, clear output, toggle output, input, and direction + +template class _ARMPIN { + +public: + typedef volatile uint32_t * port_ptr_t; + typedef uint32_t port_t; + + inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; } + inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; } + + inline static void hi() __attribute__ ((always_inline)) { _GPIO::r()->BSRR = _MASK; } + inline static void lo() __attribute__ ((always_inline)) { _GPIO::r()->BSRR = (_MASK<<16); } + + inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { _GPIO::r()->ODR = val; } + + inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + + inline static void toggle() __attribute__ ((always_inline)) { if(_GPIO::r()->ODR & _MASK) { lo(); } else { hi(); } } + + inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *port = val; } + + inline static port_t hival() __attribute__ ((always_inline)) { return _GPIO::r()->ODR | _MASK; } + inline static port_t loval() __attribute__ ((always_inline)) { return _GPIO::r()->ODR & ~_MASK; } + inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_GPIO::r()->ODR; } + + inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_GPIO::r()->BSRR = (_MASK<<16); } + inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_GPIO::r()->BSRR = _MASK; } + + inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; } +}; + + + +FASTLED_NAMESPACE_END diff --git a/src/platforms/arm/giga/clockless_arm_giga.h b/src/platforms/arm/giga/clockless_arm_giga.h new file mode 100644 index 0000000000..a8c5b81aea --- /dev/null +++ b/src/platforms/arm/giga/clockless_arm_giga.h @@ -0,0 +1,128 @@ +#ifndef __INC_CLOCKLESS_ARM_GIGA +#define __INC_CLOCKLESS_ARM_GIGA + +FASTLED_NAMESPACE_BEGIN + +// Definition for a single channel clockless controller for GIGA M7 +// See clockless.h for detailed info on how the template parameters are used. +#define ARM_DEMCR (*(volatile uint32_t *)0xE000EDFC) // Debug Exception and Monitor Control +#define ARM_DEMCR_TRCENA (1 << 24) // Enable debugging & monitoring blocks +#define ARM_DWT_CTRL (*(volatile uint32_t *)0xE0001000) // DWT control register +#define ARM_DWT_CTRL_CYCCNTENA (1 << 0) // Enable cycle count +#define ARM_DWT_CYCCNT (*(volatile uint32_t *)0xE0001004) // Cycle count register + + +#define FASTLED_HAS_CLOCKLESS 1 + +template +class ClocklessController : public CPixelLEDController { + typedef typename FastPin::port_ptr_t data_ptr_t; + typedef typename FastPin::port_t data_t; + + data_t mPinMask; + data_ptr_t mPort; + CMinWait mWait; + +public: + virtual void init() { + FastPin::setOutput(); + mPinMask = FastPin::mask(); + mPort = FastPin::port(); + } + + virtual uint16_t getMaxRefreshRate() const { return 400; } + +protected: + virtual void showPixels(PixelController & pixels) { + mWait.wait(); + if(!showRGBInternal(pixels)) { + sei(); delayMicroseconds(WAIT_TIME); cli(); + showRGBInternal(pixels); + } + mWait.mark(); + } + + template __attribute__ ((always_inline)) inline static void writeBits(FASTLED_REGISTER uint32_t & next_mark, FASTLED_REGISTER data_ptr_t port, FASTLED_REGISTER data_t hi, FASTLED_REGISTER data_t lo, FASTLED_REGISTER uint8_t & b) { + for(FASTLED_REGISTER uint32_t i = BITS-1; i > 0; --i) { + while(ARM_DWT_CYCCNT < next_mark); + next_mark = ARM_DWT_CYCCNT + (T1+T2+T3); + FastPin::fastset(port, hi); + if(b&0x80) { + while((next_mark - ARM_DWT_CYCCNT) > (T3+(2*(F_CPU/24000000)))); + FastPin::fastset(port, lo); + } else { + while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000)))); + FastPin::fastset(port, lo); + } + b <<= 1; + } + + while(ARM_DWT_CYCCNT < next_mark); + next_mark = ARM_DWT_CYCCNT + (T1+T2+T3); + FastPin::fastset(port, hi); + + if(b&0x80) { + while((next_mark - ARM_DWT_CYCCNT) > (T3+(2*(F_CPU/24000000)))); + FastPin::fastset(port, lo); + } else { + while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000)))); + FastPin::fastset(port, lo); + } + } + + // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then + // gcc will use register Y for the this pointer. + static uint32_t showRGBInternal(PixelController pixels) { + // Get access to the clock + ARM_DEMCR |= ARM_DEMCR_TRCENA; + ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA; + ARM_DWT_CYCCNT = 0; + + FASTLED_REGISTER data_ptr_t port = FastPin::port(); + FASTLED_REGISTER data_t hi = *port | FastPin::mask(); + FASTLED_REGISTER data_t lo = *port & ~FastPin::mask(); + *port = lo; + + // Setup the pixel controller and load/scale the first byte + pixels.preStepFirstByteDithering(); + FASTLED_REGISTER uint8_t b = pixels.loadAndScale0(); + + cli(); + uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3); + + while(pixels.has(1)) { + pixels.stepDithering(); + #if (FASTLED_ALLOW_INTERRUPTS == 1) + cli(); + // if interrupts took longer than 45µs, punt on the current frame + if(ARM_DWT_CYCCNT > next_mark) { + if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return 0; } + } + + hi = *port | FastPin::mask(); + lo = *port & ~FastPin::mask(); + #endif + // Write first byte, read next byte + writeBits<8+XTRA0>(next_mark, port, hi, lo, b); + b = pixels.loadAndScale1(); + + // Write second byte, read 3rd byte + writeBits<8+XTRA0>(next_mark, port, hi, lo, b); + b = pixels.loadAndScale2(); + + // Write third byte, read 1st byte of next pixel + writeBits<8+XTRA0>(next_mark, port, hi, lo, b); + b = pixels.advanceAndLoadAndScale0(); + #if (FASTLED_ALLOW_INTERRUPTS == 1) + sei(); + #endif + }; + + sei(); + return ARM_DWT_CYCCNT; + } +}; + +FASTLED_NAMESPACE_END + +#endif diff --git a/src/platforms/arm/giga/fastled_arm_giga.h b/src/platforms/arm/giga/fastled_arm_giga.h new file mode 100644 index 0000000000..a6d4753db8 --- /dev/null +++ b/src/platforms/arm/giga/fastled_arm_giga.h @@ -0,0 +1,8 @@ +#ifndef __INC_FASTLED_ARM_GIGA_H +#define __INC_FASTLED_ARM_GIGA_H + +#include "fastpin_arm_giga.h" +#include "../../fastspi_ardunio_core.h" +#include "clockless_arm_giga.h" + +#endif diff --git a/src/platforms/arm/giga/fastpin_arm_giga.h b/src/platforms/arm/giga/fastpin_arm_giga.h new file mode 100644 index 0000000000..b4ee357d88 --- /dev/null +++ b/src/platforms/arm/giga/fastpin_arm_giga.h @@ -0,0 +1,208 @@ +#ifndef __FASTPIN_ARM_GIGA_H +#define __FASTPIN_ARM_GIGA_H + +#include "fl/force_inline.h" +#include "fl/namespace.h" +#include "armpin.h" + +FASTLED_NAMESPACE_BEGIN + +#if defined(ARDUINO_GIGA) || defined(ARDUINO_GIGA_M7) +#define _RD32(T) struct __gen_struct_ ## T { static FASTLED_FORCE_INLINE volatile GPIO_TypeDef * r() { return T; } }; +#define _FL_IO(L,C) _RD32(GPIO ## L); + +#else +#error "Platform not supported" +#endif + +_FL_IO(A,0); +_FL_IO(B,1); +_FL_IO(C,2); +_FL_IO(D,3); +_FL_IO(E,4); +_FL_IO(F,5); +_FL_IO(G,6); +_FL_IO(H,7); +_FL_IO(I,8); +_FL_IO(J,9); +_FL_IO(K,10); + +// Actual pin definitions +#if defined(ARDUINO_GIGA) || defined(ARDUINO_GIGA_M7) +#define MAX_PIN 102 + +// PA0-PA15 +_FL_DEFPIN(83, 0, A); +_FL_DEFPIN(66, 1, A); +_FL_DEFPIN(3, 2, A); +_FL_DEFPIN(2, 3, A); +_FL_DEFPIN(84, 4, A); +_FL_DEFPIN(85, 5, A); +_FL_DEFPIN(56, 6, A); +_FL_DEFPIN(5, 7, A); +//_FL_DEFPIN(UART7_RX, 8, A); +_FL_DEFPIN(1, 9, A); +//_FL_DEFPIN(BT_ON, 10, A); +//_FL_DEFPIN(USB-OTG-FS_DM, 11, A); +//_FL_DEFPIN(USB-OTG-FS_DP, 12, A); +//_FL_DEFPIN(SWDIO, 13, A); +//_FL_DEFPIN(SWCLK, 14, A); +//_FL_DEFPIN(U9 Power Switch, 15, A); + +// PB0-PB13 +_FL_DEFPIN(78, 0, B); +_FL_DEFPIN(79, 1, B); +_FL_DEFPIN(47, 2, B); +_FL_DEFPIN(91, 3, B); +_FL_DEFPIN(7, 4, B); +_FL_DEFPIN(93, 5, B); +_FL_DEFPIN(101, 6, B); +_FL_DEFPIN(0, 7, B); +_FL_DEFPIN(8, 8, B); +_FL_DEFPIN(9, 9, B); +//_FL_DEFPIN(WL_ON, 10, B); +_FL_DEFPIN(20, 11, B); +_FL_DEFPIN(74, 12, B); +_FL_DEFPIN(94, 13, B); + +// PC0-PC15 +_FL_DEFPIN(82, 0, C); +_FL_DEFPIN(73, 1, C); +_FL_DEFPIN(81, 2, C); +_FL_DEFPIN(80, 3, C); +_FL_DEFPIN(76, 4, C); +_FL_DEFPIN(77, 5, C); +_FL_DEFPIN(68, 6, C); +_FL_DEFPIN(15, 7, C); +//_FL_DEFPIN(D0, 8, C); +//_FL_DEFPIN(D1, 9, C); +//_FL_DEFPIN(D2, 10, C); +//_FL_DEFPIN(D3, 11, C); +//_FL_DEFPIN(CLK, 12, C); +//_FL_DEFPIN(BOOT0_BUTTON, 13, C); + +// PD0-PD13 +//_FL_DEFPIN(FMC_D3, 0, D); +//_FL_DEFPIN(FMC_D2, 1, D); +//_FL_DEFPIN(SDMMC1_CMD, 2, D); +_FL_DEFPIN(75, 3, D); +_FL_DEFPIN(67, 4, D); +_FL_DEFPIN(18, 5, D); +_FL_DEFPIN(19, 6, D); +_FL_DEFPIN(90, 7, D); +//_FL_DEFPIN(FMC_DQ13, 8, D); +//_FL_DEFPIN(FMC_DQ14, 9, D); +//_FL_DEFPIN(FMC_DQ15, 10, D); +//_FL_DEFPIN(SI/IO0, 11, D); +//_FL_DEFPIN(SO/IO1, 12, D); +_FL_DEFPIN(6, 13, D); +//_FL_DEFPIN(FMC_D0, 14, D); +//_FL_DEFPIN(FMC_D1, 14, D); + +// PE0-PE15 +//_FL_DEFPIN(FMC_DQML, 0, E); +//_FL_DEFPIN(FMC_DQMH, 1, E); +//_FL_DEFPIN(QUADSPI_BK2-IO1, 2, E); +_FL_DEFPIN(88, 3, E); +_FL_DEFPIN(49, 4, E); +_FL_DEFPIN(51, 5, E); +_FL_DEFPIN(40, 6, E); +//_FL_DEFPIN(FMC_DQ4, 7, E); +//_FL_DEFPIN(FMC_DQ5, 8, E); +//_FL_DEFPIN(FMC_DQ6, 9, E); +//_FL_DEFPIN(FMC_DQ7, 10, E); +//_FL_DEFPIN(FMC_DQ8, 11, E); +//_FL_DEFPIN(FMC_DQ9, 12, E); +//_FL_DEFPIN(FMC_DQ10, 13, E); +//_FL_DEFPIN(FMC_DQ11, 14, E); +//_FL_DEFPIN(FMC_DQ2, 15, E); + +// PG0-PG15 +//_FL_DEFPIN(FMC_A10, 0, G); +//_FL_DEFPIN(FMC_A11, 1, G); +//_FL_DEFPIN(FMC_A12, 2, G); +//_FL_DEFPIN(BT_WAKE_H, 3, G); +//_FL_DEFPIN(FMC_BA0, 4, G); +//_FL_DEFPIN(FMC_BA1, 5, G); +//_FL_DEFPIN(CS, 6, G); +_FL_DEFPIN(53, 7, G); +_FL_DEFPIN(89, 9, G); +_FL_DEFPIN(44, 10, G); +_FL_DEFPIN(62, 11, G); +_FL_DEFPIN(24, 12, G); +_FL_DEFPIN(14, 14, G); +//_FL_DEFPIN(FMC_SDNCAS, 15, G); + +// PH3-PH15 +//_FL_DEFPIN(FMC_SDCKE0, 2, H); +//_FL_DEFPIN(FMC_SDNCS, 3, H); +_FL_DEFPIN(21, 4, H); +//_FL_DEFPIN(FMC_SDNWE, 5, H); +_FL_DEFPIN(13, 6, H); +//_FL_DEFPIN(BT_WAKE_D, 7, H); +_FL_DEFPIN(55, 8, H); +_FL_DEFPIN(65, 9, H); +_FL_DEFPIN(64, 10, H); +_FL_DEFPIN(63, 11, H); +_FL_DEFPIN(102, 12, H); +_FL_DEFPIN(16, 13, H); +_FL_DEFPIN(61, 14, H); +_FL_DEFPIN(46, 15, H); + +// PI0-PI15 +_FL_DEFPIN(69, 0, I); +_FL_DEFPIN(70, 1, I); +_FL_DEFPIN(71, 2, I); +_FL_DEFPIN(72, 3, I); +_FL_DEFPIN(60, 4, I); +_FL_DEFPIN(54, 5, I); +_FL_DEFPIN(59, 6, I); +_FL_DEFPIN(58, 7, I); +_FL_DEFPIN(17, 9, I); +_FL_DEFPIN(43, 10, I); +_FL_DEFPIN(50, 11, I); +_FL_DEFPIN(86, 12, I); +_FL_DEFPIN(45, 13, I); +_FL_DEFPIN(39, 14, I); +_FL_DEFPIN(42, 15, I); + +// PJ0-PJ15 +_FL_DEFPIN(25, 0, J); +_FL_DEFPIN(27, 1, J); +_FL_DEFPIN(29, 2, J); +_FL_DEFPIN(31, 3, J); +_FL_DEFPIN(33, 4, J); +_FL_DEFPIN(35, 5, J); +_FL_DEFPIN(37, 6, J); +_FL_DEFPIN(38, 7, J); +_FL_DEFPIN(4, 8, J); +_FL_DEFPIN(57, 9, J); +_FL_DEFPIN(11, 10, J); +_FL_DEFPIN(12, 11, J); +_FL_DEFPIN(22, 12, J); +_FL_DEFPIN(87, 13, J); +_FL_DEFPIN(26, 14, J); +_FL_DEFPIN(28, 15, J); + +// PK0-PK7 +_FL_DEFPIN(48, 0, K); +_FL_DEFPIN(10, 1, K); +_FL_DEFPIN(52, 2, K); +_FL_DEFPIN(30, 3, K); +_FL_DEFPIN(32, 4, K); +_FL_DEFPIN(34, 5, K); +_FL_DEFPIN(36, 6, K); +_FL_DEFPIN(41, 7, K); + +// SPI2 MOSI +#define SPI_DATA 90 +// SPI2 SCK +#define SPI_CLOCK 91 + +#define HAS_HARDWARE_PIN_SUPPORT + +#endif // ARDUINO_GIGA || ARDUINO_GIGA_M7 + +FASTLED_NAMESPACE_END + +#endif // __INC_FASTPIN_ARM_STM32 diff --git a/src/platforms/arm/giga/led_sysdef_arm_giga.h b/src/platforms/arm/giga/led_sysdef_arm_giga.h new file mode 100644 index 0000000000..6c0e2b426f --- /dev/null +++ b/src/platforms/arm/giga/led_sysdef_arm_giga.h @@ -0,0 +1,31 @@ +#ifndef __INC_LED_SYSDEFS_ARM_GIGA_H +#define __INC_LED_SYSDEFS_ARM_GIGA_H + +#define FASTLED_ARM + +#ifndef INTERRUPT_THRESHOLD +#define INTERRUPT_THRESHOLD 1 +#endif + +// Default to allowing interrupts +#ifndef FASTLED_ALLOW_INTERRUPTS +#define FASTLED_ALLOW_INTERRUPTS 1 +#endif + +#if FASTLED_ALLOW_INTERRUPTS == 1 +#define FASTLED_ACCURATE_CLOCK +#endif + +// reusing/abusing cli/sei defs for due +#define cli() __disable_irq(); +#define sei() __enable_irq(); + +#define FASTLED_NO_PINMAP + +typedef volatile uint32_t RoReg; +typedef volatile uint32_t RwReg; + +#define F_CPU 480000000 + +#endif + diff --git a/src/platforms/arm/k20/clockless_block_arm_k20.h b/src/platforms/arm/k20/clockless_block_arm_k20.h index d6e35e08b5..44176229a6 100644 --- a/src/platforms/arm/k20/clockless_block_arm_k20.h +++ b/src/platforms/arm/k20/clockless_block_arm_k20.h @@ -14,7 +14,6 @@ #define PORT_MASK (((1< diff --git a/src/platforms/arm/k20/fastspi_arm_k20.h b/src/platforms/arm/k20/fastspi_arm_k20.h index b860bf78bc..5c6d6407a9 100644 --- a/src/platforms/arm/k20/fastspi_arm_k20.h +++ b/src/platforms/arm/k20/fastspi_arm_k20.h @@ -37,8 +37,6 @@ template class BitWork { static int highestBit() __attribute__((always_inline)) { return 0; } }; -#define MAX(A, B) (( (A) > (B) ) ? (A) : (B)) - #define USE_CONT 0 // intra-frame backup data struct SPIState { diff --git a/src/platforms/arm/stm32/fastpin_arm_stm32.h b/src/platforms/arm/stm32/fastpin_arm_stm32.h index c5a4f1b8e7..a013969564 100644 --- a/src/platforms/arm/stm32/fastpin_arm_stm32.h +++ b/src/platforms/arm/stm32/fastpin_arm_stm32.h @@ -7,7 +7,7 @@ #else #ifndef USE_NEW_STM32_PIN_DEFINITIONS -#if defined(ARDUINO_BLUEPILL_F103C8) || defined(ARDUINO_MAPLE_MINI) +#if defined(ARDUINO_BLUEPILL_F103C8) || defined(ARDUINO_MAPLE_MINI) //|| defined(STM32F407xx) #define USE_NEW_STM32_PIN_DEFINITIONS #endif #endif diff --git a/src/platforms/arm/stm32/fastpin_arm_stm_legacy.h b/src/platforms/arm/stm32/fastpin_arm_stm_legacy.h index 65965f5718..831338e0b8 100644 --- a/src/platforms/arm/stm32/fastpin_arm_stm_legacy.h +++ b/src/platforms/arm/stm32/fastpin_arm_stm_legacy.h @@ -15,7 +15,7 @@ FASTLED_NAMESPACE_BEGIN #define _RD32(T) struct __gen_struct_ ## T { static FASTLED_FORCE_INLINE gpio_reg_map* r() { return T->regs; } }; #define _FL_IO(L,C) _RD32(GPIO ## L); _FL_DEFINE_PORT3(L, C, _R(GPIO ## L)); -#elif defined(STM32F2XX) +#elif defined(STM32F2XX) || defined(STM32F407xx) #define _RD32(T) struct __gen_struct_ ## T { static FASTLED_FORCE_INLINE volatile GPIO_TypeDef * r() { return T; } }; #define _FL_IO(L,C) _RD32(GPIO ## L); @@ -153,7 +153,7 @@ _FL_DEFPIN(4, 15, C); #define HAS_HARDWARE_PIN_SUPPORT -#elif defined(ARDUINO_GENERIC_F103C8TX) // stm32duino generic STM32F103C8TX +#elif defined(ARDUINO_GENERIC_F103C8TX) || defined(STM32F407xx) // stm32duino generic STM32F103C8TX #define MAX_PIN 36 // PA0-PA15 diff --git a/src/platforms/arm/stm32/fastpin_arm_stm_new.h b/src/platforms/arm/stm32/fastpin_arm_stm_new.h index 22a2bae555..16707f660d 100644 --- a/src/platforms/arm/stm32/fastpin_arm_stm_new.h +++ b/src/platforms/arm/stm32/fastpin_arm_stm_new.h @@ -152,6 +152,73 @@ _DEFPIN_ARM(PB1, 1, B); #define HAS_HARDWARE_PIN_SUPPORT + +#elif defined(STM32F407xx) +//intended for OpenFFBoard-main board that uses STM32F407VGT6 MCU +// Only using one output for LED strip support for now. +//https://github.com/Ultrawipf/OpenFFBoard/wiki/ + +//Register mapping +#define _RD32_GPIO_MAP(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) FASTLED_FORCE_INLINE volatile GPIO_TypeDef* r() { return T; } }; +#define _RD32 _RD32_GPIO_MAP +#define _IO32(L) _RD32(GPIO ## L) + +_IO32(A); _IO32(B); _IO32(C); _IO32(D); _IO32(E); + +#define MAX_PIN PB1 + _DEFPIN_ARM(PE2, 2, E); // DIN 8 + _DEFPIN_ARM(PE3, 3, E); // DIN 7 + _DEFPIN_ARM(PE4, 4, E); // DIN 6 + _DEFPIN_ARM(PE5, 5, E); // DIN 5 + _DEFPIN_ARM(PE6, 6, E); // DIN 4 + _DEFPIN_ARM(PC13, 13, C); // DIN 3 + _DEFPIN_ARM(PC14, 14, C); // DIN 2 + _DEFPIN_ARM(PC15, 15, C); // DIN 1 + //INSERT OTHERS HERE + + +// _DEFPIN_ARM(PB11, 11, B); +// _DEFPIN_ARM(PB10, 10, B); +// _DEFPIN_ARM(PB2, 2, B); +// _DEFPIN_ARM(PB0, 0, B); +// _DEFPIN_ARM(PA7, 7, A); +// _DEFPIN_ARM(PA6, 6, A); +// _DEFPIN_ARM(PA5, 5, A); +// _DEFPIN_ARM(PA4, 4, A); +// _DEFPIN_ARM(PA3, 3, A); +// _DEFPIN_ARM(PA2, 2, A); +// _DEFPIN_ARM(PA1, 1, A); +// _DEFPIN_ARM(PA0, 0, A); +// _DEFPIN_ARM(PC15, 15, C); +// _DEFPIN_ARM(PC14, 14, C); +// _DEFPIN_ARM(PC13, 13, C); +// _DEFPIN_ARM(PB7, 7, B); +// _DEFPIN_ARM(PB6, 6, B); +// _DEFPIN_ARM(PB5, 5, B); +// _DEFPIN_ARM(PB4, 4, B); +// _DEFPIN_ARM(PB3, 3, B); +// _DEFPIN_ARM(PA15, 15, A); +// _DEFPIN_ARM(PA14, 14, A); +// _DEFPIN_ARM(PA13, 13, A); +// _DEFPIN_ARM(PA12, 12, A); +// _DEFPIN_ARM(PA11, 11, A); +// _DEFPIN_ARM(PA10, 10, A); +// _DEFPIN_ARM(PA9, 9, A); +// _DEFPIN_ARM(PA8, 8, A); + _DEFPIN_ARM(PB15, 15, B); +// _DEFPIN_ARM(PB14, 14, B); +// _DEFPIN_ARM(PB13, 13, B); +// _DEFPIN_ARM(PB12, 12, B); +// _DEFPIN_ARM(PB8, 8, B); + _DEFPIN_ARM(PB1, 1, B); + +// SPI2 MOSI +#define SPI_DATA PB15 +// SPI2 SCK +#define SPI_CLOCK PB13 + +#define HAS_HARDWARE_PIN_SUPPORT + #else #error "Please define pin map for this board" diff --git a/src/platforms/arm/stm32/led_sysdefs_arm_stm32.h b/src/platforms/arm/stm32/led_sysdefs_arm_stm32.h index 90cef900a0..b556bdac81 100644 --- a/src/platforms/arm/stm32/led_sysdefs_arm_stm32.h +++ b/src/platforms/arm/stm32/led_sysdefs_arm_stm32.h @@ -3,7 +3,8 @@ #if defined(STM32F10X_MD) || defined(STM32F2XX) -#include +//#include + #include #include "fl/namespace.h" @@ -33,6 +34,13 @@ FASTLED_USING_NAMESPACE #define cli() noInterrupts() #define sei() interrupts() +#elif defined(STM32F407xx) +// stm32duino + +#define cli() noInterrupts() +#define sei() interrupts() + + #else #error "Platform not supported" #endif @@ -55,7 +63,7 @@ FASTLED_USING_NAMESPACE // pgmspace definitions #define PROGMEM -#if !defined(STM32F1) +#if !defined(STM32F1) && !defined(STM32F407xx) // The stm32duino core already defines these #define pgm_read_dword(addr) (*(const unsigned long *)(addr)) #define pgm_read_dword_near(addr) pgm_read_dword(addr) @@ -78,6 +86,9 @@ typedef volatile uint8_t RwReg; /**< Read-Write 8-bit register (volatile u // F_CPU is already defined on stm32duino, but it's not constant. #undef F_CPU #define F_CPU 72000000 +#elif defined(STM32F407xx) +#undef F_CPU +#define F_CPU 168000000 #else #define F_CPU 72000000 #endif diff --git a/src/platforms/esp/32/rmt_5/idf5_clockless_rmt_esp32.h b/src/platforms/esp/32/rmt_5/idf5_clockless_rmt_esp32.h index efdf431b0e..e179c7efbd 100644 --- a/src/platforms/esp/32/rmt_5/idf5_clockless_rmt_esp32.h +++ b/src/platforms/esp/32/rmt_5/idf5_clockless_rmt_esp32.h @@ -9,7 +9,9 @@ #include "eorder.h" #include "pixel_iterator.h" #include "idf5_rmt.h" +#include "fl/namespace.h" +FASTLED_NAMESPACE_BEGIN template class ClocklessController : public CPixelLEDController @@ -53,4 +55,6 @@ class ClocklessController : public CPixelLEDController CPixelLEDController::endShowLeds(data); mRMTController.showPixels(); } -}; \ No newline at end of file +}; + +FASTLED_NAMESPACE_END diff --git a/src/platforms/esp/32/rmt_5/idf5_rmt.cpp b/src/platforms/esp/32/rmt_5/idf5_rmt.cpp index 2a7b1617c3..633fe0fddb 100644 --- a/src/platforms/esp/32/rmt_5/idf5_rmt.cpp +++ b/src/platforms/esp/32/rmt_5/idf5_rmt.cpp @@ -17,10 +17,14 @@ #include "fl/assert.h" #include "fl/convert.h" // for convert_fastled_timings_to_timedeltas(...) +#include "fl/namespace.h" #include "strip_rmt.h" + #define TAG "idf5_rmt.cpp" +FASTLED_NAMESPACE_BEGIN + RmtController5::RmtController5(int DATA_PIN, int T1, int T2, int T3, RmtController5::DmaMode dma_mode) : mPin(DATA_PIN), mT1(T1), mT2(T2), mT3(T3), mDmaMode(dma_mode) { @@ -68,6 +72,8 @@ void RmtController5::showPixels() { mLedStrip->drawAsync(); } +FASTLED_NAMESPACE_END + #endif // FASTLED_RMT5 #endif // ESP32 diff --git a/src/platforms/esp/32/rmt_5/idf5_rmt.h b/src/platforms/esp/32/rmt_5/idf5_rmt.h index 5671aa3396..eaa4a9173c 100644 --- a/src/platforms/esp/32/rmt_5/idf5_rmt.h +++ b/src/platforms/esp/32/rmt_5/idf5_rmt.h @@ -6,6 +6,9 @@ #include "pixel_iterator.h" #include +#include "fl/namespace.h" + +FASTLED_NAMESPACE_BEGIN class IRmtStrip; @@ -40,6 +43,8 @@ class RmtController5 DmaMode mDmaMode; }; +FASTLED_NAMESPACE_END + #endif // FASTLED_RMT5 diff --git a/src/platforms/esp/32/rmt_5/strip_rmt.cpp b/src/platforms/esp/32/rmt_5/strip_rmt.cpp index 57cbe2bc21..77b833cde4 100644 --- a/src/platforms/esp/32/rmt_5/strip_rmt.cpp +++ b/src/platforms/esp/32/rmt_5/strip_rmt.cpp @@ -13,6 +13,9 @@ #include "esp_log.h" #include "esp_err.h" #include "esp_check.h" +#include "fl/namespace.h" + +FASTLED_NAMESPACE_BEGIN namespace { // anonymous namespace @@ -169,6 +172,7 @@ IRmtStrip *IRmtStrip::Create( ); } +FASTLED_NAMESPACE_END #endif // FASTLED_RMT5 diff --git a/src/platforms/esp/32/rmt_5/strip_rmt.h b/src/platforms/esp/32/rmt_5/strip_rmt.h index 76b3a93694..97b2a52fbb 100644 --- a/src/platforms/esp/32/rmt_5/strip_rmt.h +++ b/src/platforms/esp/32/rmt_5/strip_rmt.h @@ -2,6 +2,9 @@ #pragma once #include +#include "fl/namespace.h" + +FASTLED_NAMESPACE_BEGIN class IRmtStrip { @@ -32,3 +35,5 @@ class IRmtStrip virtual void fillRGBW(uint8_t red, uint8_t green, uint8_t blue, uint8_t white) = 0; virtual uint32_t numPixels() = 0; }; + +FASTLED_NAMESPACE_END diff --git a/src/platforms/esp/32/spi_ws2812/strip_spi.cpp b/src/platforms/esp/32/spi_ws2812/strip_spi.cpp index 2f7ccf1db2..efc1bbda51 100644 --- a/src/platforms/esp/32/spi_ws2812/strip_spi.cpp +++ b/src/platforms/esp/32/spi_ws2812/strip_spi.cpp @@ -21,6 +21,9 @@ #include "rgbw.h" #include "fl/warn.h" +#include "fl/namespace.h" + +FASTLED_NAMESPACE_BEGIN static const char *TAG = "strip_spi"; @@ -262,6 +265,8 @@ ISpiStripWs2812* ISpiStripWs2812::Create(int pin, uint32_t led_count, bool is_rg return new SpiStripWs2812(pin, size_as_rgb, spi_bus, dma_mode); } +FASTLED_NAMESPACE_END + #endif // FASTLED_ESP32_HAS_CLOCKLESS_SPI #endif // ESP32 diff --git a/src/platforms/esp/32/spi_ws2812/strip_spi.h b/src/platforms/esp/32/spi_ws2812/strip_spi.h index dbed977739..e45b5e6e52 100644 --- a/src/platforms/esp/32/spi_ws2812/strip_spi.h +++ b/src/platforms/esp/32/spi_ws2812/strip_spi.h @@ -1,6 +1,9 @@ #pragma once #include +#include "fl/namespace.h" + +FASTLED_NAMESPACE_BEGIN class ISpiStripWs2812 { public: @@ -62,3 +65,5 @@ class ISpiStripWs2812 { virtual void setPixel(uint32_t index, uint8_t red, uint8_t green, uint8_t blue) = 0; }; + +FASTLED_NAMESPACE_END diff --git a/src/platforms/wasm/compiler/.dockerignore b/src/platforms/wasm/compiler/.dockerignore deleted file mode 100644 index e81dc79c32..0000000000 --- a/src/platforms/wasm/compiler/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -**/.mypy_cache/ -**/.ruff_cache/ \ No newline at end of file diff --git a/src/platforms/wasm/compiler/CMakeLists.txt b/src/platforms/wasm/compiler/CMakeLists.txt deleted file mode 100644 index 84ed7cce5c..0000000000 --- a/src/platforms/wasm/compiler/CMakeLists.txt +++ /dev/null @@ -1,139 +0,0 @@ -cmake_minimum_required(VERSION 3.10) -project(FastLED_WASM) - -# Enable verbose makefile output for all compiled sources -set(CMAKE_VERBOSE_MAKEFILE ON) - -# --- Set Emscripten as the Compiler --- -set(CMAKE_C_COMPILER "emcc") -set(CMAKE_CXX_COMPILER "em++") -set(CMAKE_AR "emar") -set(CMAKE_RANLIB "emranlib") - -# Use ccache as a compiler launcher (requires CMake 3.4 or newer) -set(CMAKE_C_COMPILER_LAUNCHER "ccache") -set(CMAKE_CXX_COMPILER_LAUNCHER "ccache") - -# --- Build Mode and Optimization Level --- -if(NOT DEFINED ENV{BUILD_MODE}) - set(BUILD_MODE "QUICK") -else() - set(BUILD_MODE $ENV{BUILD_MODE}) -endif() - -if(BUILD_MODE STREQUAL "QUICK") - set(OPT_LEVEL "-O0") -elseif(BUILD_MODE STREQUAL "DEBUG") - set(OPT_LEVEL "-Og") -else() # RELEASE - set(OPT_LEVEL "-Oz") -endif() - -# --- Common Compiler and Linker Flags --- -set(COMMON_COMPILE_FLAGS - -DFASTLED_ENGINE_EVENTS_MAX_LISTENERS=50 - -DFASTLED_FORCE_NAMESPACE=1 - -DFASTLED_NO_FORCE_INLINE - -DFASTLED_USE_PROGMEM=0 - -DDISABLE_EXCEPTION_CATCHING=1 - -fno-exceptions - -fno-rtti - -DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=0 - -std=gnu++17 - -fpermissive - -fno-inline - -Wno-constant-logical-operand - -Wnon-c-typedef-for-linkage - -Werror=bad-function-cast - -Werror=cast-function-type - -sUSE_PTHREADS=0 # Disable threading support - ${OPT_LEVEL} -) - -# (Optional: PROFILE flags can be added here if needed) - -set(COMMON_LINK_FLAGS - -sALLOW_MEMORY_GROWTH=0 - -sDISABLE_EXCEPTION_CATCHING=1 - -sDISABLE_EXCEPTION_THROWING=0 - -sWASM_BIGINT - --bind - -DUSE_OFFSET_CONVERTER=0 - -sINITIAL_MEMORY=134217728 - -fuse-ld=lld - --no-entry - -sMODULARIZE=1 -) - -set(LIBRARY_LINK_FLAGS - ${COMMON_LINK_FLAGS} -) - -set(SKETCH_LINK_FLAGS - ${COMMON_LINK_FLAGS} - -sEXPORTED_RUNTIME_METHODS=['ccall','cwrap','stringToUTF8','lengthBytesUTF8'] - -sEXPORTED_FUNCTIONS=['_malloc','_free','_extern_setup','_extern_loop','_fastled_declare_files'] - -sEXPORT_NAME=fastled - -o fastled.js -) - -if(BUILD_MODE STREQUAL "DEBUG") - list(APPEND COMMON_COMPILE_FLAGS - -g3 - -fsanitize=address - -fsanitize=undefined - ) - set(DEBUG_LINK_FLAGS - -gsource-map - --emit-symbol-map - -sSTACK_OVERFLOW_CHECK=2 - -ASSERTIONS=1 - ) - list(APPEND LIBRARY_LINK_FLAGS ${DEBUG_LINK_FLAGS}) - list(APPEND SKETCH_LINK_FLAGS ${DEBUG_LINK_FLAGS}) -endif() - -# --- FASTLED LIBRARY SETUP --- -set(LIBFASTLED_PATH /precompiled/libfastled.a) - -if(EXISTS ${LIBFASTLED_PATH}) - message(STATUS "Found prebuilt libfastled.a at ${LIBFASTLED_PATH}, skipping build of FastLED library.") - add_library(fastled STATIC IMPORTED) - set_target_properties(fastled PROPERTIES IMPORTED_LOCATION ${LIBFASTLED_PATH}) -else() - message(FATAL_ERROR "Did not find precompiled libfastled.a at ${LIBFASTLED_PATH}. Please build the library first.") -endif() - -if(TARGET fastled) - set_target_properties(fastled PROPERTIES - CXX_STANDARD 17 - CXX_STANDARD_REQUIRED ON - POSITION_INDEPENDENT_CODE ON - ) -endif() - -# --- LIBRARY TARGET: sketch_lib --- -file(GLOB_RECURSE ALL_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") -message(STATUS "All sources: ${ALL_SOURCES}") - -add_library(sketch_lib STATIC ${ALL_SOURCES}) - -target_compile_options(sketch_lib PRIVATE ${COMMON_COMPILE_FLAGS}) -target_compile_definitions(sketch_lib PRIVATE SKETCH_COMPILE) -target_include_directories(sketch_lib PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/src - "/precompiled/include" -) -set_target_properties(sketch_lib PROPERTIES - CXX_STANDARD 17 - CXX_STANDARD_REQUIRED ON -) - -# ── FINAL LINKING STEP: Produce fastled.js ── -# This custom target invokes em++ to link the static library (sketch_lib) -# with the precompiled libfastled.a into the final binary fastled.js. -add_custom_target(final_wasm ALL - COMMAND em++ ${SKETCH_LINK_FLAGS} -Wl,--whole-archive $ -Wl,--no-whole-archive /precompiled/libfastled.a - DEPENDS sketch_lib fastled - COMMENT "Linking final wasm binary to fastled.js" -) diff --git a/src/platforms/wasm/compiler/Dockerfile b/src/platforms/wasm/compiler/Dockerfile deleted file mode 100644 index e3188a2682..0000000000 --- a/src/platforms/wasm/compiler/Dockerfile +++ /dev/null @@ -1,197 +0,0 @@ -# Overview of this docker file -# This is a emscripten docker file that is used to compile FastLED for the web assembly platform. -# The goals of this docker are: -# * Compile the inputs as quickly as possible. -# * Be very fast to rebuild -# Container size is important, but not the primary goal. -# -# The emscripten build is based on emscripten. We create a custom `wasm_compiler_flags.py`` -# that performs the necessary transformations to make this work. -# -# Maximizing compiler speed -# First let's talk about why platformio is typically slow -# * Network checking to see if the repo's are up to date -# * Unnecessary recompilation of cpp files that haven't changed. -# -# We address these slow compiler speeds via a few tricks -# * The emscripten compiler is wrapped in ccache so that identical compilations are cached. -# * We prewarm the ccache with a pre-compile of an example -# * This will generate the initial cache and also install the necessary tools. -# -# Pre-warming the cache -# * We have two pre-warm cycles. We do this for speed reasons. -# * The first pre-warm is done with a copy of fastled downloaded from the github repo -# * Docker is very relaxed on invalidating a cache layer for statements cloning a github repo. We want this. -# * So we clone the repo early in the docker image build cycle. Then we run a pre-warm compile. -# * This is the slowest pre-warm, and once built, tends to always be cached. However, it tends to go out of date -# quickly, which will make the cache be less effective. -# * The second pre-warm is done with a fresh copy of the fastled repo from the host machine that is copied over the -# git hub repo. We copy directories piece by piece to maximize the cache hit rate. Core files then platform files. -# * During developement when many files are changed, this second pre-warm will almost always be invalidated, however -# it is very fast to rebuild because the initial tool download of platformio has already been done with the first pre-warm. -# * After this second pre-warm, the ccache will have the exact state of the repo in it's cache. -# -# Final state -# * The final state of the docker image is a pre-warmed cached with the entire instance ready to compile. -# * The calling code is expected to provide volume mapped directory that will be inserted into the docker image when it runs. -# * However, this is not true when using this in server mode (see server.py) where the code to be compiled is injected into the -# container instance via a FastAPI route, the compilation is performed. Typically the web compiler is much better at holding -# on the cached files. Server.py will also periodically git pull the fastled repo to keep in sync with the latest changes. - - -# This will be set to arm64 to support MacOS M1+ devices (and Linux-based arm64 devices) -ARG PLATFORM_TAG="" - -# Use only Emscripten base image -FROM emscripten/emsdk:3.1.70${PLATFORM_TAG} - - -ENV DEBIAN_FRONTEND=noninteractive - -# Update the apt-get package list. This takes a long time, so we do it first to maximize cache hits. -# Also install apt-fast first -RUN apt-get update - -RUN apt-get install -y software-properties-common - -RUN add-apt-repository ppa:apt-fast/stable -y && \ - apt-get update && \ - apt-get -y install apt-fast - -# Update apt and install required packages -RUN apt-fast install -y \ - git \ - gawk \ - nano \ - ca-certificates \ - python3 \ - python3-pip \ - dos2unix \ - tar \ - wget \ - unzip \ - make \ - cmake \ - ninja-build \ - ccache \ - zlib1g \ - zlib1g-dev \ - gcc \ - g++ \ - rsync \ - && rm -rf /var/lib/apt/lists/* - - -# /container/bin contains symbolic links to python3 and pip3 as python and pip that we use for the compiler. -RUN mkdir -p /container/bin && \ - ln -s /usr/bin/python3 /container/bin/python && \ - ln -s /usr/bin/pip3 /container/bin/pip - - -# Add Python and Emscripten to PATH -ENV PATH="/container/bin:/usr/local/bin:/usr/bin:/emsdk:/emsdk/upstream/emscripten:${PATH}" -ENV CCACHE_DIR=/ccache -ENV CCACHE_MAXSIZE=1G - -# Create a custom print script -RUN echo '#!/bin/sh' > /usr/bin/print && \ - echo 'echo "$@"' >> /usr/bin/print && \ - chmod +x /usr/bin/print - -# Add print function (which seems to be missing, strangly) and PATH modifications to /etc/profile -RUN echo 'print() { echo "$@"; }' >> /etc/profile && \ - echo 'export -f print' >> /etc/profile && \ - echo 'export PATH="/container/bin:/usr/bin:/emsdk:/emsdk/upstream/emscripten:$PATH"' >> /etc/profile && \ - echo 'source /emsdk/emsdk_env.sh' >> /etc/profile - -# This was added to try and fix formatting issues. It didn't fix it but it's better to force everything to use -# utf-8, as god intended it. -ENV LANG=en_US.UTF-8 -ENV LC_CTYPE=UTF-8 -RUN echo 'export LANG=en_US.UTF-8' >> /etc/profile && \ - echo 'export LC_CTYPE=UTF-8' >> /etc/profile - -# This is disabled code to try and use the arduino-cli to transform an *.ino file into a cpp file -# to enable the auto-predeclaration of missing functions. However, Arduino-cli is absolutely gigantic -# and requires that we compile it against one of the platforms. This doesn't work for us because -# we don't want a compile failure because of something weird in the platform we are compiling against, which -# isn't wasm. -#COPY src/platforms/wasm/compiler/install-arduino-cli.sh /install-arduino-cli.sh -#RUN chmod +x /install-arduino-cli.sh && /install-arduino-cli.sh || echo "Failed to install Arduino CLI" - -RUN pip install uv==0.5.6 - -# Get the compiler requirements and install them. -COPY src/platforms/wasm/compiler/requirements.txt /install/requirements.txt -RUN uv pip install --system -r /install/requirements.txt - -RUN pio settings set check_platformio_interval 9999 -RUN pio settings set enable_telemetry 0 - -# FIRST PRE-WARM CYCLE and initial setup: Download the fastled repo from the github and pre-warm the cache with a compilation. -# This is by far the most expensive part of the build, because platformio needs to download initial tools. This -# pre-warm cycle is "sticky" and tends to stay in the cache for a long time since docker is very relaxed about -# invalidating cache layers that clone a github repo. - -ARG FASTLED_VERSION=master -ENV FASTLED_VERSION=${FASTLED_VERSION} -RUN git clone -b ${FASTLED_VERSION} https://github.com/fastled/FastLED.git --depth 1 /git/fastled && \ - mkdir -p /js/fastled && \ - rsync -a /git/fastled/ /js/fastled/ --exclude='.git' - -# Create symlinks for wasm platform files. -COPY src/platforms/wasm/compiler/init_runtime.py /js/init_runtime.py -COPY src/platforms/wasm/compiler/prewarm.sh /js/prewarm.sh - -WORKDIR /js - -ARG NO_PREWARM=0 -ENV NO_PREWARM=${NO_PREWARM} - -RUN python /js/init_runtime.py || true - - -# First pre-warm cycle - always do it as part of the build. -RUN mkdir -p /logs - -# Force a build if the compiler flags change. -COPY src/platforms/wasm/compiler/wasm_compiler_flags.py /trash/wasm_compiler_flags.py -COPY src/platforms/wasm/compiler/CMakeLists.txt /trash/CMakeLists.txt -RUN rm -rf /trash - -RUN chmod +x /js/prewarm.sh && \ - cat /js/prewarm.sh >> /logs/prewarm.log.0 -RUN /js/prewarm.sh --force >> /logs/prewarm.log.1 || true - - -# Copy the fastled repo from the host machine and prepare for pre-warm -# Make sure and delete files that have been removed so that we don't get -# duplicate symbols from stale files. -COPY *.json /host/fastled/ -COPY src/*.* /host/fastled/src/ -COPY examples /host/fastled/examples -COPY src/fx /host/fastled/src/fx -COPY src/fl /host/fastled/src/fl -COPY src/lib8tion /host/fastled/src/lib8tion -COPY src/third_party /host/fastled/src/third_party -COPY src/sensors /host/fastled/src/sensors -COPY src/platforms /host/fastled/src/platforms - -COPY src/platforms/wasm/compiler/entrypoint.sh /entrypoint.sh -RUN chmod +x /entrypoint.sh && dos2unix /entrypoint.sh - -# now sync local to the source directory. -RUN rsync -av /host/fastled/ /js/fastled/ && rm -rf /host/fastled - -RUN python /js/init_runtime.py || true - - -# SECOND PRE-WARM CYCLE: Copy the fastled repo from the host machine and pre-warm the cache with that compilation. This will -# be much quicker than the first pre-warm cycle. -RUN /js/prewarm.sh --force > /logs/prewarm.log.2 || true - -# Now timestamp the image and store it at the end of the build. -RUN date > /image_timestamp.txt - -ENTRYPOINT ["/entrypoint.sh"] -CMD ["python", "/js/run.py", "server"] diff --git a/src/platforms/wasm/compiler/__init__.py b/src/platforms/wasm/compiler/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/platforms/wasm/compiler/arduino-pre-process.sh b/src/platforms/wasm/compiler/arduino-pre-process.sh deleted file mode 100755 index eccd60056d..0000000000 --- a/src/platforms/wasm/compiler/arduino-pre-process.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -arduino-cli compile --preprocess --fqbn arduino:avr:uno /pre-warm/Blink/Blink.ino \ No newline at end of file diff --git a/src/platforms/wasm/compiler/build.sh b/src/platforms/wasm/compiler/build.sh deleted file mode 100644 index bea9d5f1c1..0000000000 --- a/src/platforms/wasm/compiler/build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -python compile.py --no-platformio \ No newline at end of file diff --git a/src/platforms/wasm/compiler/build_archive.sh b/src/platforms/wasm/compiler/build_archive.sh deleted file mode 100755 index 9556a504d9..0000000000 --- a/src/platforms/wasm/compiler/build_archive.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -set -e # Exit immediately if a command exits with a non-zero status -set -x - -# if /include does not exist -if [ ! -d "/precompiled/include" ]; then - echo "include directory does not exist, copying header include tree" - # echo "include directory does not exist, copying header include tree" - # copy *.h,*.hpp files from fastled/src/** to /include - mkdir -p /precompiled/include - cd fastled/src - find . -name "*.h*" -exec cp --parents {} /precompiled/include \; - cd ../../ -fi - -# if /precompiled/libfastled.a does not exist -if [ ! -f "/precompiled/libfastled.a" ]; then - # echo "libfastled.a does not exist, compiling static library" - echo "libfastled.a does not exist, compiling static library" - cd fastled/src/platforms/wasm/compiler/lib - mkdir -p build - cd build - emcmake cmake -DCMAKE_VERBOSE_MAKEFILE=ON .. - emmake cmake --build . -v -j - cp fastled/libfastled.a /precompiled/libfastled.a - cd ../../../../../ -fi - diff --git a/src/platforms/wasm/compiler/build_fast.sh b/src/platforms/wasm/compiler/build_fast.sh deleted file mode 100644 index ff37208684..0000000000 --- a/src/platforms/wasm/compiler/build_fast.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash -set -e # Exit immediately if a command exits with a non-zero status -set -x - -################################# -# Compile fastled lib -################################# - - -./build_archive.sh - -# first compile if build doesn't exist -first_compile=false - -# if build directory doesn't exist -if [ ! -d "build" ]; then - # Create the build directory if it doesn't exist and mark as first compile. - mkdir -p build - # if /js/src/CMakeCache.txt exists then delete it - - first_compile=true -fi - -cd build - -# Set build mode: QUICK, DEBUG, or RELEASE (default is QUICK) -export BUILD_MODE=${1:-QUICK} - -# Check for an optional second parameter to activate PROFILE -# PROFILE_FLAG="-DPROFILE=ON" - -# touch CMakeLists.txt to force reconfigure -touch /js/CMakeLists.txt - - -# always delete CMakeCache.txt -if [ -f "/js/fastled/src/CMakeCache.txt" ]; then - rm -rf /js/fastled/src/CMakeCache.txt -fi - - -if [ "$first_compile" = true ]; then - # Configure with CMake - # If you want verbose build output, you can enable CMAKE_VERBOSE_MAKEFILE. - echo "Configuring build with CMake..." - emcmake cmake -DCMAKE_VERBOSE_MAKEFILE=ON .. -else - # If the build directory already exists, reconfigure with CMake. - echo "Skipping CMake configuration as build directory already exists." -fi - -# Build the project using CMake -emmake cmake --build . -v -j - diff --git a/src/platforms/wasm/compiler/code_sync.py b/src/platforms/wasm/compiler/code_sync.py deleted file mode 100644 index 9aacb60c25..0000000000 --- a/src/platforms/wasm/compiler/code_sync.py +++ /dev/null @@ -1,76 +0,0 @@ - -import os -from pathlib import Path -from typing import Callable -import subprocess -import time - -from compile_lock import COMPILE_LOCK - -VOLUME_MAPPED_SRC = Path("/host/fastled/src") -RSYNC_DEST = Path("/js/fastled/src") -TIME_START = time.time() - -def sync_src_to_target( - src: Path, dst: Path, callback: Callable[[], None] | None = None -) -> bool: - """Sync the volume mapped source directory to the FastLED source directory.""" - suppress_print = ( - TIME_START + 30 > time.time() - ) # Don't print during initial volume map. - if not src.exists(): - # Volume is not mapped in so we don't rsync it. - print(f"Skipping rsync, as fastled src at {src} doesn't exist") - return False - try: - exclude_hidden = "--exclude=.*/" # suppresses folders like .mypy_cache/ - print("\nSyncing source directories...") - with COMPILE_LOCK: - # Use rsync to copy files, preserving timestamps and deleting removed files - cp: subprocess.CompletedProcess = subprocess.run( - ["rsync", "-av", "--info=NAME", "--delete", f"{src}/", f"{dst}/", exclude_hidden], - check=True, - text=True, - capture_output=True, - ) - if cp.returncode == 0: - changed = False - changed_lines: list[str] = [] - lines = cp.stdout.split("\n") - for line in lines: - suffix = line.strip().split(".")[-1] - if suffix in ["cpp", "h", "hpp", "ino", "py", "js", "html", "css"]: - if not suppress_print: - print(f"Changed file: {line}") - changed = True - changed_lines.append(line) - if changed: - if not suppress_print: - print(f"FastLED code had updates: {changed_lines}") - if callback: - callback() - return True - print("Source directory synced successfully with no changes") - return False - else: - print(f"Error syncing directories: {cp.stdout}\n\n{cp.stderr}") - return False - - except subprocess.CalledProcessError as e: - print(f"Error syncing directories: {e.stdout}\n\n{e.stderr}") - except Exception as e: - print(f"Error syncing directories: {e}") - return False - - - -def sync_source_directory_if_volume_is_mapped( - callback: Callable[[], None] | None = None -) -> bool: - """Sync the volume mapped source directory to the FastLED source directory.""" - if not VOLUME_MAPPED_SRC.exists(): - # Volume is not mapped in so we don't rsync it. - print("Skipping rsync, as fastled src volume not mapped") - return False - print("Syncing source directories because host is mapped in") - return sync_src_to_target(VOLUME_MAPPED_SRC, RSYNC_DEST, callback=callback) \ No newline at end of file diff --git a/src/platforms/wasm/compiler/compile.py b/src/platforms/wasm/compiler/compile.py deleted file mode 100644 index b618a47af8..0000000000 --- a/src/platforms/wasm/compiler/compile.py +++ /dev/null @@ -1,659 +0,0 @@ -# A compilation script specific to fastled's docker compiler. -# This script will pull the users code from a mapped directory, -# then do some processing to convert the *.ino files to *.cpp and -# insert certain headers like "Arduino.h" (pointing to a fake implementation). -# After this, the code is compiled, and the output files are copied back -# to the users mapped directory in the fastled_js folder. -# There are a few assumptions for this script: -# 1. The mapped directory will contain only one directory with the users code, this is -# enforced by the script that sets up the docker container. -# 2. The docker container has installed compiler dependencies in the /js directory. - -import argparse -import hashlib -import json -import os -import re -import shutil -import subprocess -import sys -from dataclasses import dataclass -from datetime import datetime -from enum import Enum -from pathlib import Path -from typing import List - - -@dataclass -class DateLine: - dt: datetime - line: str - - -class BuildMode(Enum): - DEBUG = "DEBUG" - QUICK = "QUICK" - RELEASE = "RELEASE" - - @classmethod - def from_string(cls, mode_str: str) -> "BuildMode": - try: - return cls[mode_str.upper()] - except KeyError: - valid_modes = [mode.name for mode in cls] - raise ValueError(f"BUILD_MODE must be one of {valid_modes}, got {mode_str}") - - -@dataclass -class SyntaxCheckResult: - file_path: Path - is_valid: bool - message: str - - -_CHECK_SYNTAX = False -_COMPILER_PATH = "em++" - -JS_DIR = Path("/js") -FASTLED_DIR = JS_DIR / "fastled" -FASTLED_SRC = FASTLED_DIR / "src" -FASTLED_SRC_PLATFORMS = FASTLED_SRC / "platforms" -FASTLED_SRC_PLATFORMS_WASM = FASTLED_SRC_PLATFORMS / "wasm" -FASTLED_SRC_PLATFORMS_WASM_COMPILER = FASTLED_SRC_PLATFORMS_WASM / "compiler" - - -JS_SRC = JS_DIR / "src" - -FASTLED_DIR = JS_DIR / "fastled" -FASTLED_SRC_DIR = FASTLED_DIR / "src" -FASTLED_PLATFORMS_DIR = FASTLED_SRC_DIR / "platforms" -FASTLED_WASM_DIR = FASTLED_PLATFORMS_DIR / "wasm" -FASTLED_COMPILER_DIR = FASTLED_WASM_DIR / "compiler" -FASTLED_MODULES_DIR = FASTLED_COMPILER_DIR / "modules" - -PIO_BUILD_DIR = JS_DIR / ".pio/build" -ARDUINO_H_SRC = JS_DIR / "Arduino.h" -INDEX_HTML_SRC = FASTLED_COMPILER_DIR / "index.html" -INDEX_CSS_SRC = FASTLED_COMPILER_DIR / "index.css" -INDEX_JS_SRC = FASTLED_COMPILER_DIR / "index.js" - - -WASM_COMPILER_SETTTINGS = JS_DIR / "wasm_compiler_flags.py" -OUTPUT_FILES = ["fastled.js", "fastled.wasm"] -HEADERS_TO_INSERT = ["#include ", '#include "platforms/wasm/js.h"'] -FILE_EXTENSIONS = [".ino", ".h", ".hpp", ".cpp"] -MAX_COMPILE_ATTEMPTS = 1 # Occasionally the compiler fails for unknown reasons, but disabled because it increases the build time on failure. -FASTLED_OUTPUT_DIR_NAME = "fastled_js" - -assert JS_DIR.exists() -assert ARDUINO_H_SRC.exists() -assert INDEX_HTML_SRC.exists() -assert INDEX_CSS_SRC.exists(), f"Index CSS not found at {INDEX_CSS_SRC}" -assert INDEX_JS_SRC.exists() -assert WASM_COMPILER_SETTTINGS.exists() -assert FASTLED_SRC_PLATFORMS_WASM_COMPILER.exists() -assert JS_DIR.exists(), f"JS_DIR does not exist: {JS_DIR}" -assert ARDUINO_H_SRC.exists(), f"ARDUINO_H_SRC does not exist: {ARDUINO_H_SRC}" -assert INDEX_HTML_SRC.exists(), f"INDEX_HTML_SRC does not exist: {INDEX_HTML_SRC}" -assert INDEX_CSS_SRC.exists(), f"INDEX_CSS_SRC does not exist: {INDEX_CSS_SRC}" -assert INDEX_JS_SRC.exists(), f"INDEX_JS_SRC does not exist: {INDEX_JS_SRC}" -assert ( - WASM_COMPILER_SETTTINGS.exists() -), f"WASM_COMPILER_SETTTINGS does not exist: {WASM_COMPILER_SETTTINGS}" - - -def copy_files(src_dir: Path, js_src: Path) -> None: - print("Copying files from mapped directory to container...") - for item in src_dir.iterdir(): - if item.is_dir(): - print(f"Copying directory: {item}") - shutil.copytree(item, js_src / item.name, dirs_exist_ok=True) - else: - print(f"Copying file: {item}") - shutil.copy2(item, js_src / item.name) - - -def compile( - js_dir: Path, build_mode: BuildMode, auto_clean: bool, no_platformio: bool -) -> int: - print("Starting compilation process...") - max_attempts = 1 - env = os.environ.copy() - env["BUILD_MODE"] = build_mode.name - print(f"Build mode: {build_mode.name}") - cmd_list: list[str] = [] - if no_platformio: - # execute build_archive.syh - cmd_list = [ - "/bin/bash", - "-c", - "/js/build_fast.sh", - ] - else: - cmd_list.extend(["pio", "run"]) - if not auto_clean: - cmd_list.append("--disable-auto-clean") - - def _open_process(cmd_list: list[str] = cmd_list) -> subprocess.Popen: - out = subprocess.Popen( - cmd_list, - cwd=js_dir, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True, - env=env, - ) - return out - - output_lines = [] - for attempt in range(1, max_attempts + 1): - try: - print(f"Attempting compilation (attempt {attempt}/{max_attempts})...") - process = _open_process() - assert process.stdout is not None - for line in process.stdout: - processed_line = line.replace("fastled/src", "src") - timestamped_line = _timestamp_output(processed_line) - output_lines.append(timestamped_line) - process.wait() - relative_output = _make_timestamps_relative("\n".join(output_lines)) - print(relative_output) - if process.returncode == 0: - print(f"Compilation successful on attempt {attempt}") - return 0 - else: - raise subprocess.CalledProcessError(process.returncode, ["pio", "run"]) - except subprocess.CalledProcessError: - print(f"Compilation failed on attempt {attempt}") - if attempt == max_attempts: - print("Max attempts reached. Compilation failed.") - return 1 - print("Retrying...") - return 1 - - -def insert_header(file: Path) -> None: - print(f"Inserting header in file: {file}") - with open(file, "r") as f: - content = f.read() - - # Remove existing includes - for header in HEADERS_TO_INSERT: - content = re.sub( - rf"^.*{re.escape(header)}.*\n", "", content, flags=re.MULTILINE - ) - - # Remove both versions of Arduino.h include - arduino_pattern = r'^\s*#\s*include\s*[<"]Arduino\.h[>"]\s*.*\n' - content = re.sub(arduino_pattern, "", content, flags=re.MULTILINE) - - # Add new headers at the beginning - content = "\n".join(HEADERS_TO_INSERT) + "\n" + content - - with open(file, "w") as f: - f.write(content) - print(f"Processed: {file}") - - -def transform_to_cpp(src_dir: Path) -> None: - print("Transforming files to cpp...") - ino_files = list(src_dir.glob("*.ino")) - - if ino_files: - ino_file = ino_files[0] - print(f"Found .ino file: {ino_file}") - main_cpp = src_dir / "main.cpp" - if main_cpp.exists(): - print("main.cpp already exists, renaming to main2.hpp") - main_cpp.rename(src_dir / "main2.hpp") - - new_cpp_file = ino_file.with_suffix(".ino.cpp") - print(f"Renaming {ino_file} to {new_cpp_file.name}") - ino_file.rename(new_cpp_file) - - if (src_dir / "main2.hpp").exists(): - print(f"Including main2.hpp in {new_cpp_file.name}") - with open(new_cpp_file, "a") as f: - f.write('#include "main2.hpp"\n') - - -def insert_headers( - src_dir: Path, exclusion_folders: List[Path], file_extensions: List[str] -) -> None: - print("Inserting headers in source files...") - for file in src_dir.rglob("*"): - if ( - file.suffix in file_extensions - and not any(folder in file.parents for folder in exclusion_folders) - and file.name != "Arduino.h" - ): - insert_header(file) - - -def process_ino_files(src_dir: Path) -> None: - transform_to_cpp(src_dir) - exclusion_folders: List[Path] = [] - insert_headers(src_dir, exclusion_folders, FILE_EXTENSIONS) - print("Transform to cpp and insert header operations completed.") - - -def check_syntax_with_gcc(file_path, gcc_path="gcc"): - """ - Perform syntax checking on a C or C++ source file using GCC. - - Parameters: - file_path (str): Path to the source file to check. - gcc_path (str): Path to the GCC executable (default is 'gcc'). - - Returns: - bool: True if syntax is correct, False otherwise. - str: Output or error message from GCC. - """ - try: - # Run GCC with -fsyntax-only flag for syntax checking - cmd_list = [ - gcc_path, - "-fsyntax-only", - "-std=gnu++20", - "-fpermissive", - "-Wno-everything", # Suppress all warnings - "-I", - "/js/src/", # Add /js/src/ to the include path - "-I", - "/js/fastled/src/", # Add /js/fastled/src/ to the include path - "-I", - "/emsdk/upstream/emscripten/system/include", - file_path, - ] - cmd_str = subprocess.list2cmdline(cmd_list) - print(f"Running command: {cmd_str}") - result = subprocess.run( - cmd_list, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - ) - - # Check the return code to determine if syntax is valid - if result.returncode == 0: - return True, "Syntax check passed successfully." - else: - return False, result.stderr - except FileNotFoundError: - return False, f"GCC not found at {gcc_path}." - except Exception as e: - return False, str(e) - - -def check_syntax( - directory_path: Path, gcc_path: str = "gcc" -) -> list[SyntaxCheckResult]: - # os walk - out: list[SyntaxCheckResult] = [] - exclusion_list = set("fastled_js") - for root, dirs, files in os.walk(directory_path): - # if sub directory is in exclusion list, skip - dirs[:] = [d for d in dirs if d not in exclusion_list] - for file in files: - if file.endswith(".cpp") or file.endswith(".ino"): - file_path = os.path.join(root, file) - is_valid, message = check_syntax_with_gcc(file_path, gcc_path) - if not is_valid: - print(f"Syntax check failed for file: {file_path}") - print(f"Error message: {message}") - out.append( - SyntaxCheckResult( - file_path=Path(file_path), is_valid=False, message=message - ) - ) - else: - print(f"Syntax check passed for file: {file_path}") - out.append( - SyntaxCheckResult( - file_path=Path(file_path), - is_valid=True, - message="Syntax check passed successfully.", - ) - ) - return out - - -def _make_timestamps_relative(stdout: str) -> str: - def parse(line: str) -> DateLine: - parts = line.split(" ") - if len(parts) < 2: - raise ValueError(f"Invalid line: {line}") - - date_str, time_str = parts[:2] - rest = " ".join(parts[2:]) - # Parse with microsecond precision - dt = datetime.strptime(f"{date_str} {time_str}", "%Y-%m-%d %H:%M:%S.%f") - return DateLine(dt, rest) - - lines = stdout.split("\n") - if not lines: - return stdout - parsed: list[DateLine] = [] - for line in lines: - if not line.strip(): # Skip empty lines - continue - try: - parsed.append(parse(line)) - except ValueError: - print(f"Failed to parse line: {line}") - continue - - if not parsed: - return stdout - - outlines: list[str] = [] - start_time = parsed[0].dt - - # Calculate relative times with - for p in parsed: - delta = p.dt - start_time - seconds = delta.total_seconds() - line_str = f"{seconds:3.2f} {p.line}" - outlines.append(line_str) - - return "\n".join(outlines) - - -def _timestamp_output(line: str) -> str: - now = datetime.now() - timestamp = now.strftime("%Y-%m-%d %H:%M:%S.%f") - return f"{timestamp} {line.rstrip()}" - - -def parse_args() -> argparse.Namespace: - parser = argparse.ArgumentParser(description="Compile FastLED for WASM") - parser.add_argument( - "--mapped-dir", - type=Path, - default="/mapped", - help="Directory containing source files (default: /mapped)", - ) - parser.add_argument( - "--keep-files", action="store_true", help="Keep source files after compilation" - ) - parser.add_argument( - "--only-copy", - action="store_true", - help="Only copy files from mapped directory to container", - ) - parser.add_argument( - "--only-insert-header", - action="store_true", - help="Only insert headers in source files", - ) - parser.add_argument( - "--only-compile", action="store_true", help="Only compile the project" - ) - parser.add_argument( - "--profile", - action="store_true", - help="Enable profiling for compilation to see what's taking so long.", - ) - - parser.add_argument( - "--disable-auto-clean", - action="store_true", - help="Massaive speed improvement to not have to rebuild everything, but flakes out sometimes.", - default=os.getenv("DISABLE_AUTO_CLEAN", "0") == "1", - ) - parser.add_argument( - "--no-platformio", - action="store_true", - help="Don't use platformio to compile the project, use the new system of direct emcc calls.", - ) - # Add mutually exclusive build mode group - build_mode = parser.add_mutually_exclusive_group() - build_mode.add_argument("--debug", action="store_true", help="Build in debug mode") - build_mode.add_argument( - "--quick", - action="store_true", - default=True, - help="Build in quick mode (default)", - ) - build_mode.add_argument( - "--release", action="store_true", help="Build in release mode" - ) - - return parser.parse_args() - - -def find_project_dir(mapped_dir: Path) -> Path: - mapped_dirs: List[Path] = list(mapped_dir.iterdir()) - if len(mapped_dirs) > 1: - raise ValueError( - f"Error: More than one directory found in {mapped_dir}, which are {mapped_dirs}" - ) - - src_dir: Path = mapped_dirs[0] - return src_dir - - -def process_compile( - js_dir: Path, build_mode: BuildMode, auto_clean: bool, no_platformio: bool -) -> None: - print("Starting compilation...") - rtn = compile(js_dir, build_mode, auto_clean, no_platformio=no_platformio) - print(f"Compilation return code: {rtn}") - if rtn != 0: - print("Compilation failed.") - raise RuntimeError("Compilation failed.") - print("Compilation successful.") - - -def cleanup(args: argparse.Namespace, js_src: Path) -> None: - if not args.keep_files and not (args.only_copy or args.only_insert_header): - print("Removing temporary source files") - shutil.rmtree(js_src) - else: - print("Keeping temporary source files") - - -def hash_file(file_path: Path) -> str: - hasher = hashlib.md5() - with open(file_path, "rb") as f: - for chunk in iter(lambda: f.read(4096), b""): - hasher.update(chunk) - return hasher.hexdigest() - - -def main() -> int: - print("Starting FastLED WASM compilation script...") - args = parse_args() - print(f"Keep files flag: {args.keep_files}") - print(f"Using mapped directory: {args.mapped_dir}") - - if args.profile: - print("Enabling profiling for compilation.") - # Profile linking - os.environ["EMPROFILE"] = "2" - - try: - if JS_SRC.exists(): - shutil.rmtree(JS_SRC) - JS_SRC.mkdir(parents=True, exist_ok=True) - src_dir = find_project_dir(args.mapped_dir) - - any_only_flags = args.only_copy or args.only_insert_header or args.only_compile - - do_copy = not any_only_flags or args.only_copy - do_insert_header = not any_only_flags or args.only_insert_header - do_compile = not any_only_flags or args.only_compile - - if do_copy: - copy_files(src_dir, JS_SRC) - print("Copying Arduino.h to src/Arduino.h") - shutil.copy(ARDUINO_H_SRC, JS_SRC / "Arduino.h") - if args.only_copy: - return 0 - - if do_insert_header: - process_ino_files(JS_SRC) - if args.only_insert_header: - print("Transform to cpp and insert header operations completed.") - return 0 - - if _CHECK_SYNTAX: - print("Performing syntax check...") - syntax_results = check_syntax( - directory_path=JS_SRC, gcc_path=_COMPILER_PATH - ) - failed_checks = [r for r in syntax_results if not r.is_valid] - if failed_checks: - print("\nSyntax check failed!") - for result in failed_checks: - print(f"\nFile: {result.file_path}") - print(f"Error: {result.message}") - return 1 - print("Syntax check passed for all files.") - - no_platformio: bool = args.no_platformio - - if do_compile: - try: - # Determine build mode from args - if args.debug: - build_mode = BuildMode.DEBUG - elif args.release: - build_mode = BuildMode.RELEASE - else: - # Default to QUICK mode if neither debug nor release specified - build_mode = BuildMode.QUICK - - process_compile( - js_dir=JS_DIR, - build_mode=build_mode, - auto_clean=not args.disable_auto_clean, - no_platformio=no_platformio, - ) - except Exception as e: - print(f"Error: {str(e)}") - return 1 - - def _get_build_dir_platformio() -> Path: - build_dirs = [d for d in PIO_BUILD_DIR.iterdir() if d.is_dir()] - if len(build_dirs) != 1: - raise RuntimeError( - f"Expected exactly one build directory in {PIO_BUILD_DIR}, found {len(build_dirs)}: {build_dirs}" - ) - build_dir: Path = build_dirs[0] - return build_dir - - def _get_build_dir_cmake() -> Path: - return JS_DIR / "build" - - if no_platformio: - build_dir = _get_build_dir_cmake() - else: - build_dir = _get_build_dir_platformio() - - print("Copying output files...") - fastled_js_dir: Path = src_dir / FASTLED_OUTPUT_DIR_NAME - fastled_js_dir.mkdir(parents=True, exist_ok=True) - - for file in ["fastled.js", "fastled.wasm"]: - _src = build_dir / file - _dst = fastled_js_dir / file - print(f"Copying {_src} to {_dst}") - shutil.copy2(_src, _dst) - - print(f"Copying {INDEX_HTML_SRC} to output directory") - shutil.copy2(INDEX_HTML_SRC, fastled_js_dir / "index.html") - print(f"Copying {INDEX_CSS_SRC} to output directory") - shutil.copy2(INDEX_CSS_SRC, fastled_js_dir / "index.css") - - # copy all js files in FASTLED_COMPILER_DIR to output directory - Path(fastled_js_dir / "modules").mkdir(parents=True, exist_ok=True) - for _file in FASTLED_MODULES_DIR.iterdir(): - if _file.suffix == ".js": - print(f"Copying {_file} to output directory") - shutil.copy2(_file, fastled_js_dir / "modules" / _file.name) - - fastled_js_mem = build_dir / "fastled.js.mem" - fastled_wasm_map = build_dir / "fastled.wasm.map" - fastled_js_symbols = build_dir / "fastled.js.symbols" - if fastled_js_mem.exists(): - print(f"Copying {fastled_js_mem} to output directory") - shutil.copy2(fastled_js_mem, fastled_js_dir / fastled_js_mem.name) - if fastled_wasm_map.exists(): - print(f"Copying {fastled_wasm_map} to output directory") - shutil.copy2(fastled_wasm_map, fastled_js_dir / fastled_wasm_map.name) - if fastled_js_symbols.exists(): - print(f"Copying {fastled_js_symbols} to output directory") - shutil.copy2( - fastled_js_symbols, fastled_js_dir / fastled_js_symbols.name - ) - print("Copying index.js to output directory") - shutil.copy2(INDEX_JS_SRC, fastled_js_dir / "index.js") - optional_input_data_dir = src_dir / "data" - output_data_dir = fastled_js_dir / optional_input_data_dir.name - - # Handle data directory if it exists - manifest: list[dict] = [] - if optional_input_data_dir.exists(): - # Clean up existing output data directory - if output_data_dir.exists(): - for _file in output_data_dir.iterdir(): - _file.unlink() - - # Create output data directory and copy files - output_data_dir.mkdir(parents=True, exist_ok=True) - for _file in optional_input_data_dir.iterdir(): - if _file.is_file(): # Only copy files, not directories - filename: str = _file.name - if filename.endswith(".embedded.json"): - print("Embedding data file") - filename_no_embedded = filename.replace( - ".embedded.json", "" - ) - # read json file - with open(_file, "r") as f: - data = json.load(f) - hash_value = data["hash"] - size = data["size"] - manifest.append( - { - "name": filename_no_embedded, - "path": f"data/{filename_no_embedded}", - "size": size, - "hash": hash_value, - } - ) - else: - print(f"Copying {_file.name} -> {output_data_dir}") - shutil.copy2(_file, output_data_dir / _file.name) - hash = hash_file(_file) - manifest.append( - { - "name": _file.name, - "path": f"data/{_file.name}", - "size": _file.stat().st_size, - "hash": hash, - } - ) - - # Write manifest file even if empty - print("Writing manifest files.json") - manifest_json_str = json.dumps(manifest, indent=2, sort_keys=True) - with open(fastled_js_dir / "files.json", "w") as f: - f.write(manifest_json_str) - cleanup(args, JS_SRC) - - print("Compilation process completed successfully") - return 0 - - except Exception as e: - import traceback - - stacktrace = traceback.format_exc() - print(stacktrace) - print(f"Error: {str(e)}") - return 1 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/src/platforms/wasm/compiler/compile_lock.py b/src/platforms/wasm/compiler/compile_lock.py deleted file mode 100644 index 637c02b0e7..0000000000 --- a/src/platforms/wasm/compiler/compile_lock.py +++ /dev/null @@ -1,3 +0,0 @@ -import threading - -COMPILE_LOCK = threading.Lock() \ No newline at end of file diff --git a/src/platforms/wasm/compiler/entrypoint.sh b/src/platforms/wasm/compiler/entrypoint.sh deleted file mode 100644 index 82defab844..0000000000 --- a/src/platforms/wasm/compiler/entrypoint.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -source /emsdk/emsdk_env.sh -export PATH=$PATH:/emsdk/upstream/bin -python init_runtime.py -./final_prewarm.sh -exec "$@" \ No newline at end of file diff --git a/src/platforms/wasm/compiler/extra/100dots.html b/src/platforms/wasm/compiler/extra/100dots.html deleted file mode 100644 index 384393ed02..0000000000 --- a/src/platforms/wasm/compiler/extra/100dots.html +++ /dev/null @@ -1,186 +0,0 @@ - - - - - - LED Grid Demo - - - - - - - - - - - - - diff --git a/src/platforms/wasm/compiler/extra/demo_threejs.html b/src/platforms/wasm/compiler/extra/demo_threejs.html deleted file mode 100644 index 3f074f77b0..0000000000 --- a/src/platforms/wasm/compiler/extra/demo_threejs.html +++ /dev/null @@ -1,216 +0,0 @@ - - - - three.js webgl - framebuffer - texture - - - - - - - -
- three.js framebuffer to texture -
- -
-
-
- - - - - - - diff --git a/src/platforms/wasm/compiler/extra/webgl_postprocessing_unreal_bloom.html b/src/platforms/wasm/compiler/extra/webgl_postprocessing_unreal_bloom.html deleted file mode 100644 index 0e94132f40..0000000000 --- a/src/platforms/wasm/compiler/extra/webgl_postprocessing_unreal_bloom.html +++ /dev/null @@ -1,191 +0,0 @@ - - - - three.js webgl - postprocessing - unreal bloom - - - - - - - -
- -
- three.js - Bloom pass by Prashant Sharma and Ben Houston -
- Model: Primary Ion Drive by - Mike Murdock, CC Attribution. -
- - - - - - - - diff --git a/src/platforms/wasm/compiler/final_prewarm.sh b/src/platforms/wasm/compiler/final_prewarm.sh deleted file mode 100755 index 8feb7caea4..0000000000 --- a/src/platforms/wasm/compiler/final_prewarm.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -if [ "$RENDER" != "true" ]; then - echo "Skipping finalprewarm..." - exit 0 -fi - -git_path=/git/fastled -fastled_path=/js/fastled - -# update the fastled git repo -cd $git_path - -git fetch origin -git reset --hard origin/master -# ["rsync", "-av", "--info=NAME", "--delete", f"{src}/", f"{dst}/"], - -cd /js - -rsync -av --info=NAME --delete "$git_path/" "$fastled_path/" --exclude ".git" -# rsync -av --info=NAME --delete "$git_path/examples" "$fastled_path/src" - -# now print out all the files in the current directory -ls -la - - diff --git a/src/platforms/wasm/compiler/init_runtime.py b/src/platforms/wasm/compiler/init_runtime.py deleted file mode 100644 index 96b9d8265d..0000000000 --- a/src/platforms/wasm/compiler/init_runtime.py +++ /dev/null @@ -1,63 +0,0 @@ - -import os -from pathlib import Path -import glob -import warnings -from concurrent.futures import ThreadPoolExecutor - -HERE = Path(__file__).parent - - -_COMPILER_DIR = Path("/js/fastled/src/platforms/wasm/compiler") - - -def task(src: str | Path) -> None: - src = Path(src) - if "entrypoint.sh" in str(src): - return - link_dst = Path("/js") / src.name - - # Handle shell scripts - if src.suffix == '.sh': - os.system(f"dos2unix {src} && chmod +x {src}") - - # if link exists, remove it - if link_dst.exists(): - print(f"Removing existing link {link_dst}") - try: - os.remove(link_dst) - except Exception as e: - warnings.warn(f"Failed to remove {link_dst}: {e}") - - - if not link_dst.exists(): - print(f"Linking {src} to {link_dst}") - try: - os.symlink(str(src), str(link_dst)) - except FileExistsError: - print(f"Target {link_dst} already exists") - else: - print(f"Target {link_dst} already exists") - - -def make_links() -> None: - # Define file patterns to include - patterns = ['*.h', '*.py', '*.css', '*.sh', "*.ino", "*.hpp", "*.cpp", "*.ini", "*.txt"] - - # Get all matching files in compiler directory - files = [] - for pattern in patterns: - files.extend(glob.glob(str(_COMPILER_DIR / pattern))) - - # Process files in parallel using ThreadPoolExecutor - with ThreadPoolExecutor(max_workers=16) as executor: - executor.map(task, files) - - -def init_runtime() -> None: - os.chdir(str(HERE)) - make_links() - - -if __name__ == "__main__": - init_runtime() diff --git a/src/platforms/wasm/compiler/install-arduino-cli.sh b/src/platforms/wasm/compiler/install-arduino-cli.sh deleted file mode 100755 index 7bd1bbb3d2..0000000000 --- a/src/platforms/wasm/compiler/install-arduino-cli.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash - -# this is a work in progress for install the arduino-cli tool so that -# ino sketches can be converted into cpp code in preparation for the -# wasm compilation. - -wget https://github.com/arduino/arduino-cli/releases/download/v1.0.4/arduino-cli_1.0.4_Linux_64bit.tar.gz && \ - tar -xvf arduino-cli_1.0.4_Linux_64bit.tar.gz && \ - mv arduino-cli /usr/bin/arduino-cli && \ - rm -rf arduino-cli_1.0.4_Linux_64bit* - -# COPY examples/Blink/Blink.ino /pre-warm/Blink/Blink.ino -arduino-cli core update-index -arduino-cli core install arduino:avr - -mkdir -p /pre-warm/Blink && \ - cat < /pre-warm/Blink/Blink.ino -void setup() { - // initialize digital pin LED_BUILTIN as an output. - pinMode(LED_BUILTIN, OUTPUT); -} - -void loop() { - do_it(); - // turn the LED on (HIGH is the voltage level) - digitalWrite(LED_BUILTIN, HIGH); - // wait for a second - delay(1000); - // turn the LED off by making the voltage LOW - digitalWrite(LED_BUILTIN, LOW); - // wait for a second - delay(1000); -} - -void do_it() { - int i = 0; - i++; -} -EOF - -arduino-cli compile --preprocess --fqbn arduino:avr:uno /pre-warm/Blink/Blink.ino \ No newline at end of file diff --git a/src/platforms/wasm/compiler/lib/CMakeLists.txt b/src/platforms/wasm/compiler/lib/CMakeLists.txt deleted file mode 100644 index 6462526d0e..0000000000 --- a/src/platforms/wasm/compiler/lib/CMakeLists.txt +++ /dev/null @@ -1,152 +0,0 @@ -cmake_minimum_required(VERSION 3.10) -project(FastLED_WASM) - -# Enable verbose makefile output for all compiled sources -set(CMAKE_VERBOSE_MAKEFILE ON) - - -# projenv.Replace(CC=CC, CXX=CXX, LINK=LINK, AR="emar", RANLIB="emranlib") -# env.Replace(CC=CC, CXX=CXX, LINK=LINK, AR="emar", RANLIB="emranlib") - -# --- Set Emscripten as the Compiler --- -set(CMAKE_C_COMPILER "emcc") -set(CMAKE_CXX_COMPILER "em++") -set(CMAKE_AR "emar") -set(CMAKE_RANLIB "emranlib") - -# Use ccache as a compiler launcher (requires CMake 3.4 or newer) -set(CMAKE_C_COMPILER_LAUNCHER "ccache") -set(CMAKE_CXX_COMPILER_LAUNCHER "ccache") - - -# --- Build Mode and Optimization Level --- -if(NOT DEFINED ENV{BUILD_MODE}) - set(BUILD_MODE "QUICK") -else() - set(BUILD_MODE $ENV{BUILD_MODE}) -endif() - -# if(BUILD_MODE STREQUAL "QUICK") -# set(OPT_LEVEL "-O1") -# elseif(BUILD_MODE STREQUAL "DEBUG") -# set(OPT_LEVEL "-Og") -# else() # RELEASE -# set(OPT_LEVEL "-Oz") -# endif() - - -set(OPT_LEVEL "-Oz") - -# --- Common Compiler and Linker Flags --- -set(COMMON_COMPILE_FLAGS - -DFASTLED_ENGINE_EVENTS_MAX_LISTENERS=50 - -DFASTLED_FORCE_NAMESPACE=1 - -DFASTLED_USE_PROGMEM=0 - -DDISABLE_EXCEPTION_CATCHING=1 - -fno-exceptions - -fno-rtti - -DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=0 - -std=gnu++17 - -fpermissive - ${OPT_LEVEL} -) - -# If PROFILE flag is passed (e.g. via -DPROFILE), add Clang statistics flags. -# if(DEFINED PROFILE) -# message(STATUS "PROFILE flag passed: enabling Clang compiler stats output") -# list(APPEND COMMON_COMPILE_FLAGS -Xclang -print-stats -mllvm -stats -DLLVM_FORCE_ENABLE_STATS=1) -# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DLLVM_FORCE_ENABLE_STATS") -# else() -# message(STATUS "PROFILE flag not passed: skipping Clang compiler stats output") -# endif() - -set(COMMON_LINK_FLAGS - -sALLOW_MEMORY_GROWTH=0 - -sDISABLE_EXCEPTION_CATCHING=1 - -sDISABLE_EXCEPTION_THROWING=0 - --bind - -DUSE_OFFSET_CONVERTER=0 - -sINITIAL_MEMORY=134217728 - -fuse-ld=lld - --no-entry - -sMODULARIZE=1 - -sUSE_PTHREADS=0 # Disable threading support -) - -set(LIBRARY_LINK_FLAGS - ${COMMON_LINK_FLAGS} - -) - - -if(BUILD_MODE STREQUAL "DEBUG") - list(APPEND COMMON_COMPILE_FLAGS - -g3 - -fsanitize=address - -fsanitize=undefined - ) - set(DEBUG_LINK_FLAGS - -gsource-map - --emit-symbol-map - -sSTACK_OVERFLOW_CHECK=2 - -ASSERTIONS=1 - ) - list(APPEND LIBRARY_LINK_FLAGS ${DEBUG_LINK_FLAGS}) -endif() - -# src\platforms\wasm\compiler\lib\CMakeLists.txt - -# --- FASTLED LIBRARY SETUP --- -# Set the FastLED source directory (assumes your FastLED sources are in fastled/src) -set(FASTLED_SOURCE_DIR ../../../../) -# set(FASTLED_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/fastled/src) -message(STATUS "fastled source directory: ${FASTLED_SOURCE_DIR}") - - -# Enable header copying mode. -set(FASTLED_COPY_HEADERS ON CACHE BOOL "Copy FastLED headers to the include path") -set(FASTLED_INCLUDE_DEST /include CACHE PATH "Destination path for FastLED headers") - -# (Optional) Add the FastLED headers to the global include path. -include_directories(${FASTLED_SOURCE_DIR}) - -# Delegate building the FastLED library (and header copying) to the subdirectory. -add_subdirectory(${FASTLED_SOURCE_DIR} ${CMAKE_BINARY_DIR}/fastled) -# Add FastLED headers to the global include path. - - - - - - - - -# Set target properties for fastled (if it exists) -if(TARGET fastled) - set_target_properties(fastled PROPERTIES - CXX_STANDARD 17 - CXX_STANDARD_REQUIRED ON - POSITION_INDEPENDENT_CODE ON - ) -endif() - -# # --- EXECUTABLE (SKETCH) SETUP --- -# # Instead of using the FastLED sources directly, we build an executable from local sketch sources. -# file(GLOB_RECURSE EXECUTABLE_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") -# message(STATUS "Executable sources: ${EXECUTABLE_SOURCES}") - -# add_executable(sketch ${EXECUTABLE_SOURCES}) - -# # Set compile options and definitions for the sketch executable. -# target_compile_options(sketch PRIVATE ${COMMON_COMPILE_FLAGS}) -# target_compile_definitions(sketch PRIVATE SKETCH_COMPILE) -# target_include_directories(sketch PRIVATE -# ${CMAKE_CURRENT_SOURCE_DIR}/fastled/src -# ${CMAKE_CURRENT_SOURCE_DIR}/src -# ) -# target_link_options(sketch PRIVATE ${SKETCH_LINK_FLAGS}) -# set_target_properties(sketch PROPERTIES -# CXX_STANDARD 17 -# CXX_STANDARD_REQUIRED ON -# ) -# target_link_libraries(sketch PRIVATE fastled) \ No newline at end of file diff --git a/src/platforms/wasm/compiler/modules/graphics_manager_threejs.js b/src/platforms/wasm/compiler/modules/graphics_manager_threejs.js index a8c02dac8a..0aa26c114d 100644 --- a/src/platforms/wasm/compiler/modules/graphics_manager_threejs.js +++ b/src/platforms/wasm/compiler/modules/graphics_manager_threejs.js @@ -63,6 +63,7 @@ export class GraphicsManagerThreeJS { this.previousTotalLeds = 0; this.bloom_stength = 1; this.bloom_radius = 16; + this.outside_bounds_warning_count = 0; } reset() { @@ -307,8 +308,17 @@ export class GraphicsManagerThreeJS { const y_array = stripData.map.y; const length = Math.min(x_array.length, y_array.length); + const WARNING_COUNT = 10; + for (let j = 0; j < pixelCount; j++) { if (j >= length) { + this.outside_bounds_warning_count++; + if (this.outside_bounds_warning_count < WARNING_COUNT) { + console.warn(`Strip ${strip_id}: Pixel ${j} is outside the screen map ${map.length}, skipping update`); + if (this.outside_bounds_warning_count === WARNING_COUNT) { + console.warn('Suppressing further warnings about pixels outside the screen map'); + } + } console.warn(`Strip ${strip_id}: Pixel ${j} is outside the screen map ${map.length}, skipping update`); continue; } diff --git a/src/platforms/wasm/compiler/platformio.ini b/src/platforms/wasm/compiler/platformio.ini index f0d042323b..f004978955 100644 --- a/src/platforms/wasm/compiler/platformio.ini +++ b/src/platforms/wasm/compiler/platformio.ini @@ -11,9 +11,11 @@ build_flags = -DFASTLED_NO_PINMAP -DHAS_HARDWARE_PIN_SUPPORT -DFASTLED_FORCE_SOFTWARE_SPI + -I/js/fastled/src/platforms/wasm/compiler custom_wasm_export_name = fastled lib_deps = FastLED=symlink://fastled -build_dir=build/wasm \ No newline at end of file +build_dir=build/wasm +force_verbose=yes \ No newline at end of file diff --git a/src/platforms/wasm/compiler/pre-process.sh b/src/platforms/wasm/compiler/pre-process.sh deleted file mode 100755 index 2de77b51ac..0000000000 --- a/src/platforms/wasm/compiler/pre-process.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -set -e -set -x - -python compile.py --only-copy - -# Find the .ino file in the src directory -ino_file=$(find src -name "*.ino" -type f) - -if [ -z "$ino_file" ]; then - echo "Error: No .ino file found in the src directory" - exit 1 -fi - -# Extract the filename without extension -ino_filename=$(basename "$ino_file") -ino_name="${ino_filename%.*}" - -# Copy and process the .ino file -cp "$ino_file" "src/${ino_name}.ino.cpp" -python process-ino.py "src/${ino_name}.ino.cpp" - -# Remove the original .ino file -rm "$ino_file" - -python compile.py --only-compile diff --git a/src/platforms/wasm/compiler/prewarm.sh b/src/platforms/wasm/compiler/prewarm.sh deleted file mode 100755 index c55fe20881..0000000000 --- a/src/platforms/wasm/compiler/prewarm.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash - -set -x -set -e - -# --force flag to force prewarm -#test to see if --force flag is set -forced=0 -if [ "$1" == "--force" ]; then - forced=1 -fi - -echo "Forced: $forced" -echo "NO_PREWARM: $NO_PREWARM" - -# if os env has PREWARM == "true" -if [ "$forced" == "0" ]; then - if [ "$NO_PREWARM" != "1" ]; then - echo "Prewarming..." - else - echo "Skipping prewarm..." - exit 0 - fi -fi - -rm -rf /prewarm -mkdir -p /prewarm && cp -r /js/fastled/examples/wasm /prewarm/wasm -python /js/compile.py --debug --mapped-dir /prewarm && rm -rf /js/.pio/build/wasm/src/wasm.ino.o || echo "failed to delete wasm.ino.o" -rm -rf /prewarm - -mkdir -p /prewarm && cp -r /js/fastled/examples/wasm /prewarm/wasm -python /js/compile.py --quick --mapped-dir /prewarm && rm -rf /js/.pio/build/wasm/src/wasm.ino.o || echo "failed to delete wasm.ino.o" -rm -rf /prewarm - -mkdir -p /prewarm && cp -r /js/fastled/examples/wasm /prewarm/wasm -python /js/compile.py --release --mapped-dir /prewarm && rm -rf /js/.pio/build/wasm/src/wasm.ino.o || echo "failed to delete wasm.ino.o" -rm -rf /prewarm - -mkdir -p /prewarm && cp -r /js/fastled/examples/wasm /prewarm/wasm -cd /js -python compile.py --no-platformio --mapped-dir /prewarm # 60 seconds -> 5 seconds -python compile.py --no-platformio --mapped-dir /prewarm # 5 seconds -> 0.5 seconds -rm -rf /prewarm diff --git a/src/platforms/wasm/compiler/process-ino.py b/src/platforms/wasm/compiler/process-ino.py deleted file mode 100644 index 21e4be2e14..0000000000 --- a/src/platforms/wasm/compiler/process-ino.py +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/env python3 - -# This is experimental code to process an INO file and output -# a C++ version like the arduino IDE does, where all the declarations -# of function prototypes are at the top of the file. -# However, clang ast dump will not correctly identify the integer types -# of the arguments. For examples, uint8_t was being identified as "int". -# This could be because we are using clang 11 from emscripten and this -# might have been fixed in a newer version version of clang. However, -# the amount of work to get this to work is massive so I'm stopping here. - -import os -import sys -import subprocess -import argparse -import re -from dataclasses import dataclass - -@dataclass -class FunctionPrototype: - return_type: str - name: str - args: list[str] - - def __repr__(self) -> str: - return f"{self.return_type} {self.name}({', '.join(self.args)})" - -def parse_arguments() -> argparse.Namespace: - parser = argparse.ArgumentParser(description="Process INO file and dump AST.") - parser.add_argument("input_file", help="Input source file (.cpp)") - return parser.parse_args() - -def run_command(command): - result = subprocess.run(command, capture_output=True, text=True) - return result.stdout, result.stderr, result.returncode - -def parse_ast_output(ast_output): - function_pattern = re.compile(r'FunctionDecl.*?line:\d+:\d+\s+(.*?)\s+\'(.*?)\'') - functions = function_pattern.findall(ast_output) - return [f"{name} {prototype}" for name, prototype in functions] - -def main() -> None: - # Parse command-line arguments - args = parse_arguments() - - - # Ensure the file exists - if not os.path.isfile(args.input_file): - print(f"Error: File '{args.input_file}' not found!") - sys.exit(1) - - # Add Emscripten's Clang to PATH - os.environ['PATH'] = "/emsdk_portable/upstream/bin:" + os.environ['PATH'] - - # Print Clang version - stdout, stderr, returncode = run_command(["clang", "--version"]) - print(stdout) - if returncode != 0: - print(f"Error: {stderr}", file=sys.stderr) - sys.exit(returncode) - - # Dump the AST - stdout, stderr, returncode = run_command(["clang", "-Xclang", "-ast-dump", "-fsyntax-only", "-nostdinc", args.input_file]) - - # Parse the AST output, even if there was an error - function_prototypes = parse_ast_output(stdout) - - # Print warning if there was an error, but continue processing - if returncode != 0: - print("Warning: Clang reported an error (possibly due to missing stdio.h), but continuing to process output.", file=sys.stderr) - print(f"Clang error: {stderr}", file=sys.stderr) - - # Read the contents of the input file - with open(args.input_file, 'r') as f: - file_contents = f.read() - - # Find the position of setup() function - setup_pos = file_contents.find('void setup()') - if setup_pos == -1: - print("Error: setup() function not found in the file.") - sys.exit(1) - - def predicate(name: str) -> bool: - if name.startswith('loop ') or name.startswith('setup '): - return False - return True - - - - def parse(name: str) -> FunctionPrototype | None: - if name.startswith('invalid '): - name = name[len('invalid '):] - parts = name.split() - if len(parts) < 2: - return None - name, type, *rest = parts - args = " ".join(rest) - return FunctionPrototype(type, name, args.split(',')) - - - prototypes = [parse(name) for name in function_prototypes if predicate(name)] - prototypes = [str(proto) for proto in prototypes if proto] - - # Insert the prototypes before setup() - prototypes_text = "\n".join(prototypes) - prototypes_text_cleaned = prototypes_text.replace('\n', '\n') # This line effectively does nothing, but ensures prototypes_text_cleaned is a string - new_contents = file_contents[:setup_pos] + prototypes_text_cleaned + "\n\n" + file_contents[setup_pos:] - - # Write the modified contents back to the file - with open(args.input_file, 'w') as f: - f.write(new_contents) - - print(f"Function prototypes have been inserted into {args.input_file}") - - # Write the function prototypes to protos.txt as well - with open('protos.txt', 'w') as f: - f.write("Function Prototypes:\n") - for prototype in function_prototypes: - f.write(f"{prototype}\n") - - print("Function prototypes have also been written to protos.txt") - -if __name__ == "__main__": - main() diff --git a/src/platforms/wasm/compiler/requirements.txt b/src/platforms/wasm/compiler/requirements.txt deleted file mode 100644 index 553bdf042f..0000000000 --- a/src/platforms/wasm/compiler/requirements.txt +++ /dev/null @@ -1,12 +0,0 @@ -platformio==6.1.16 - -# API dependencies -fastapi==0.109.2 -uvicorn[standard]==0.30.6 -python-multipart==0.0.19 -disklru==2.0.0 -psutil==6.1.1 - -# File transfer utility -# https://github.com/zackees/tx -wormhole-tx diff --git a/src/platforms/wasm/compiler/run.py b/src/platforms/wasm/compiler/run.py deleted file mode 100644 index eadf4b96b9..0000000000 --- a/src/platforms/wasm/compiler/run.py +++ /dev/null @@ -1,125 +0,0 @@ -import argparse -import subprocess -import os -import sys -from typing import Tuple -import warnings -from pathlib import Path - -from code_sync import sync_source_directory_if_volume_is_mapped - -_PORT = os.environ.get("PORT", 80) - -_CHOICES = [ - "compile", - "server" -] - -HERE = Path(__file__).parent - -# if [ "$RENDER" != "true" ]; then -# echo "Skipping finalprewarm..." -# exit 0 -# fi - -# git_path=/git/fastled -# fastled_path=/js/fastled - -# # update the fastled git repo -# cd $git_path - -# git fetch origin -# git reset --hard origin/master -# # ["rsync", "-av", "--info=NAME", "--delete", f"{src}/", f"{dst}/"], - -# cd /js - -# rsync -av --info=NAME --delete "$git_path/" "$fastled_path/" --exclude ".git" - -def _update_fastled() -> None: - # NOT ENABLED YET - if True: - return - is_render = os.environ.get("RENDER", "false") == "true" - if not is_render: - print("Skipping finalprewarm...") - return - git_path = "/git/fastled" - fastled_path = "/js/fastled" - subprocess.run(["git", "fetch", "origin"], cwd=git_path) - subprocess.run(["git", "reset", "--hard", "origin/master"], cwd=git_path) - subprocess.run(["rsync", "-av", "--info=NAME", "--delete", f"{git_path}/", f"{fastled_path}/", "--exclude", ".git"], cwd="/js") - - - - -def _parse_args() -> Tuple[argparse.Namespace, list[str]]: - parser = argparse.ArgumentParser(description="Run compile.py with additional arguments") - parser.add_argument("mode", help="Which mode does this script run in", choices=_CHOICES) - return parser.parse_known_args() - -def _run_server(unknown_args: list[str]) -> int: - env = os.environ.copy() - if "--disable-auto-clean" in unknown_args: - env["DISABLE_AUTO_CLEAN"] = "1" - unknown_args.remove("--disable-auto-clean") - if "--allow-shutdown" in unknown_args: - env["ALLOW_SHUTDOWN"] = "1" - unknown_args.remove("--allow-shutdown") - if "--no-auto-update" in unknown_args: - env["NO_AUTO_UPDATE"] = "1" - unknown_args.remove("--no-auto-update") - if "--no-sketch-cache" in unknown_args: - env["NO_SKETCH_CACHE"] = "1" - unknown_args.remove("--no-sketch-cache") - if unknown_args: - warnings.warn(f"Unknown arguments: {unknown_args}") - unknown_args = [] - cmd_list = [ - "uvicorn", - "server:app", - "--host", - "0.0.0.0", - "--workers", - "1", - "--port", - f"{_PORT}" - ] - cp: subprocess.CompletedProcess = subprocess.run(cmd_list, cwd=str(HERE), env=env) - return cp.returncode - -def _run_compile(unknown_args: list[str]) -> int: - - # Construct the command to call compile.py with unknown arguments - command = [sys.executable, 'compile.py'] + unknown_args - - # Call compile.py with the unknown arguments - result = subprocess.run(command, text=True, cwd=str(HERE)) - - # Print the output from compile.py - #print(result.stdout) - #if result.stderr: - # print(result.stderr, file=sys.stderr) - return result.returncode - -def main() -> int: - print("Running...") - args, unknown_args = _parse_args() - _update_fastled() - sync_source_directory_if_volume_is_mapped() - - try: - if args.mode == "compile": - rtn = _run_compile(unknown_args) - return rtn - elif args.mode == "server": - rtn = _run_server(unknown_args) - return rtn - raise ValueError(f"Unknown mode: {args.mode}") - except KeyboardInterrupt: - print("Exiting...") - return 1 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/src/platforms/wasm/compiler/server.py b/src/platforms/wasm/compiler/server.py deleted file mode 100644 index c8cf70c6d9..0000000000 --- a/src/platforms/wasm/compiler/server.py +++ /dev/null @@ -1,775 +0,0 @@ -import json -import os -import shutil -import subprocess -import tempfile -import threading -import time -import warnings -import zipfile -import zlib -from contextlib import asynccontextmanager -from pathlib import Path -from tempfile import NamedTemporaryFile -from threading import Timer - -import psutil # type: ignore -from code_sync import ( # type: ignore - sync_source_directory_if_volume_is_mapped, - sync_src_to_target, -) -from compile_lock import COMPILE_LOCK # type: ignore -from disklru import DiskLRUCache # type: ignore -from fastapi import ( # type: ignore - BackgroundTasks, - Body, - FastAPI, - File, - Header, - HTTPException, - UploadFile, -) -from fastapi.responses import FileResponse, RedirectResponse, Response # type: ignore -from sketch_hasher import generate_hash_of_project_files # type: ignore -from starlette.middleware.base import BaseHTTPMiddleware # type: ignore -from starlette.requests import Request # type: ignore - -_EXAMPLES: list[str] = [ - "Chromancer", - "LuminescentGrand", - "wasm", - "FxAnimartrix", - "FxCylon", - "FxDemoReel100", - "FxFire2012", - "FxEngine", - "FxGfx2Video", - "FxNoisePlusPalette", - "FxNoiseRing", - "FxSdCard", - "FxWater", -] -_VOLUME_MAPPED_SRC = Path("/host/fastled/src") -_RSYNC_DEST = Path("/js/fastled/src") - -_TEMP_DIR = Path("/tmp") - -_TEST = False -_UPLOAD_LIMIT = 10 * 1024 * 1024 -_MEMORY_LIMIT_MB = int(os.environ.get("MEMORY_LIMIT_MB", "0")) # 0 means disabled -_MEMORY_CHECK_INTERVAL = 0.1 # Check every 100ms -_MEMORY_EXCEEDED_EXIT_CODE = 137 # Standard OOM kill code -# Protect the endpoints from random bots. -# Note that that the wasm_compiler.py greps for this string to get the URL of the server. -# Changing the name could break the compiler. -_AUTH_TOKEN = "oBOT5jbsO4ztgrpNsQwlmFLIKB" - -_LIVE_GIT_UPDATES_INTERVAL = int( - os.environ.get("LIVE_GIT_UPDATE_INTERVAL", 60 * 60 * 24) -) # Update every 24 hours -_ALLOW_SHUTDOWN = os.environ.get("ALLOW_SHUTDOWN", "false").lower() in ["true", "1"] -_NO_SKETCH_CACHE = os.environ.get("NO_SKETCH_CACHE", "false").lower() in ["true", "1"] -_LIVE_GIT_FASTLED_DIR = Path("/git/fastled") - -# TODO - cleanup -_NO_AUTO_UPDATE = ( - os.environ.get("NO_AUTO_UPDATE", "0") in ["1", "true"] - or _VOLUME_MAPPED_SRC.exists() -) -# This feature is broken. To fix, issue a git update, THEN invoke the compiler command to re-warm the cache. -# otherwise you get worst case scenario on a new compile. -# _LIVE_GIT_UPDATES_ENABLED = (not _NO_AUTO_UPDATE) or ( -# os.environ.get("LIVE_GIT_UPDATES", "0") in ["1", "true"] -# ) -_LIVE_GIT_UPDATES_ENABLED = False - - -if _NO_SKETCH_CACHE: - print("Sketch caching disabled") - -UPLOAD_DIR = Path("/uploads") -UPLOAD_DIR.mkdir(exist_ok=True) -COMPILE_COUNT = 0 -COMPILE_FAILURES = 0 -COMPILE_SUCCESSES = 0 -START_TIME = time.time() - -OUTPUT_DIR = Path("/output") -OUTPUT_DIR.mkdir(exist_ok=True) - -# Initialize disk cache -SKETCH_CACHE_FILE = OUTPUT_DIR / "compile_cache.db" -SKETCH_CACHE_MAX_ENTRIES = 50 -SKETCH_CACHE = DiskLRUCache(str(SKETCH_CACHE_FILE), SKETCH_CACHE_MAX_ENTRIES) - - -class UploadSizeMiddleware(BaseHTTPMiddleware): - def __init__(self, app, max_upload_size: int): - super().__init__(app) - self.max_upload_size = max_upload_size - - async def dispatch(self, request: Request, call_next): - if request.method == "POST" and "/compile/wasm" in request.url.path: - print( - f"Upload request with content-length: {request.headers.get('content-length')}" - ) - content_length = request.headers.get("content-length") - if content_length: - content_length = int(content_length) # type: ignore - if content_length > self.max_upload_size: # type: ignore - return Response( - status_code=413, - content=f"File size exceeds {self.max_upload_size} byte limit, for large assets please put them in data/ directory to avoid uploading them to the server.", - ) - return await call_next(request) - - -@asynccontextmanager # type: ignore -async def lifespan(app: FastAPI): # type: ignore - print("Starting FastLED wasm compiler server...") - try: - print(f"Settings: {json.dumps(get_settings(), indent=2)}") - except Exception as e: - print(f"Error getting settings: {e}") - - if _MEMORY_LIMIT_MB > 0: - print(f"Starting memory watchdog (limit: {_MEMORY_LIMIT_MB}MB)") - memory_watchdog() - - sync_source_directory_if_volume_is_mapped() - if _LIVE_GIT_UPDATES_ENABLED: - Timer( - _LIVE_GIT_UPDATES_INTERVAL, sync_live_git_to_target - ).start() # Start the periodic git update - else: - print("Auto updates disabled") - yield # end startup - return # end shutdown - - -app = FastAPI(lifespan=lifespan) - -app.add_middleware(UploadSizeMiddleware, max_upload_size=_UPLOAD_LIMIT) - - -def update_live_git_repo() -> None: - if not _LIVE_GIT_UPDATES_ENABLED: - return - try: - if not _LIVE_GIT_FASTLED_DIR.exists(): - subprocess.run( - [ - "git", - "clone", - "https://github.com/fastled/fastled.git", - str(_LIVE_GIT_FASTLED_DIR), - "--depth=1", - ], - check=True, - ) - print("Cloned live FastLED repository") - else: - print("Updating live FastLED repository") - subprocess.run( - ["git", "fetch", "origin"], - check=True, - capture_output=True, - cwd=_LIVE_GIT_FASTLED_DIR, - ) - subprocess.run( - ["git", "reset", "--hard", "origin/master"], - check=True, - capture_output=True, - cwd=_LIVE_GIT_FASTLED_DIR, - ) - print("Live FastLED repository updated successfully") - except subprocess.CalledProcessError as e: - warnings.warn( - f"Error updating live FastLED repository: {e.stdout}\n\n{e.stderr}" - ) - - -def try_get_cached_zip(hash: str) -> bytes | None: - if _NO_SKETCH_CACHE: - print("Sketch caching disabled, skipping cache get") - return None - return SKETCH_CACHE.get_bytes(hash) - - -def cache_put(hash: str, data: bytes) -> None: - if _NO_SKETCH_CACHE: - print("Sketch caching disabled, skipping cache put") - return - SKETCH_CACHE.put_bytes(hash, data) - - -def sync_live_git_to_target() -> None: - if not _LIVE_GIT_UPDATES_ENABLED: - return - update_live_git_repo() # no lock - - def on_files_changed() -> None: - print("FastLED source changed from github repo, clearing disk cache.") - SKETCH_CACHE.clear() - - sync_src_to_target( - _LIVE_GIT_FASTLED_DIR / "src", _RSYNC_DEST, callback=on_files_changed - ) - sync_src_to_target( - _LIVE_GIT_FASTLED_DIR / "examples", - _RSYNC_DEST.parent / "examples", - callback=on_files_changed, - ) - # Basically a setTimeout() in JS. - Timer( - _LIVE_GIT_UPDATES_INTERVAL, sync_live_git_to_target - ).start() # Start the periodic git update - - -def compile_source( - temp_src_dir: Path, - file_path: Path, - background_tasks: BackgroundTasks, - build_mode: str, - profile: bool, - hash_value: str | None = None, -) -> FileResponse | HTTPException: - """Compile source code and return compiled artifacts as a zip file.""" - epoch = time.time() - - def _print(msg) -> None: - diff = time.time() - epoch - print(f" = SERVER {diff:.2f}s = {msg}") - - _print("Starting compile_source") - global COMPILE_COUNT - global COMPILE_FAILURES - global COMPILE_SUCCESSES - COMPILE_COUNT += 1 - temp_zip_dir = None - try: - # Find the first directory in temp_src_dir - src_dir = next(Path(temp_src_dir).iterdir()) - _print(f"\nFound source directory: {src_dir}") - except StopIteration: - return HTTPException( - status_code=500, - detail=f"No files found in extracted directory: {temp_src_dir}", - ) - - _print("Files are ready, waiting for compile lock...") - COMPILE_LOCK_start = time.time() - - with COMPILE_LOCK: - COMPILE_LOCK_end = time.time() - - is_debug = build_mode.lower() == "debug" - - _print("\nRunning compiler...") - cmd = [ - "python", - "run.py", - "compile", - f"--mapped-dir={temp_src_dir}", - ] - if is_debug: - cmd += ["--no-platformio"] # fast path that doesn't need a lock. - cmd.append(f"--{build_mode.lower()}") - if profile: - cmd.append("--profile") - # cp = subprocess.run(cmd, cwd="/js", capture_output=True, text=True) - # cp = subprocess.run(cmd, cwd="/js", stdout=subprocess.STDOUT, stderr=subprocess.STDOUT, text=True) - proc = subprocess.Popen( - cmd, - cwd="/js", - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - text=True, - ) - assert proc.stdout is not None - stdout_lines: list[str] = [] - - for line in iter(proc.stdout.readline, ""): - print(line, end="") - stdout_lines.append(line) - _print("Compiler finished.") - stdout = "".join(stdout_lines) - proc.stdout.close() - return_code = proc.wait() - if return_code != 0: - COMPILE_FAILURES += 1 - print(f"Compilation failed with return code {return_code}:\n{stdout}") - return HTTPException( - status_code=400, - detail=f"Compilation failed with return code {return_code}:\n{stdout}", - ) - COMPILE_SUCCESSES += 1 - compile_time = time.time() - COMPILE_LOCK_end - COMPILE_LOCK_time = COMPILE_LOCK_end - COMPILE_LOCK_start - - print(f"\nCompiler output:\nstdout:\n{stdout}") - print(f"Compile lock time: {COMPILE_LOCK_time:.2f}s") - print(f"Compile time: {compile_time:.2f}s") - - # Find the fastled_js directory - fastled_js_dir = src_dir / "fastled_js" - print(f"\nLooking for fastled_js directory at: {fastled_js_dir}") - - _print("Looking for fastled_js directory...") - if not fastled_js_dir.exists(): - print(f"Directory contents of {src_dir}:") - for path in src_dir.rglob("*"): - print(f" {path}") - return HTTPException( - status_code=500, - detail=f"Compilation artifacts not found at {fastled_js_dir}", - ) - _print("Found fastled_js directory, zipping...") - - # Replace separate stdout/stderr files with single out.txt - out_txt = fastled_js_dir / "out.txt" - perf_txt = fastled_js_dir / "perf.txt" - hash_txt = fastled_js_dir / "hash.txt" - print(f"\nSaving combined output to: {out_txt}") - out_txt.write_text(stdout) - perf_txt.write_text( - f"Compile lock time: {COMPILE_LOCK_time:.2f}s\nCompile time: {compile_time:.2f}s" - ) - if hash_value is not None: - hash_txt.write_text(hash_value) - - OUTPUT_DIR.mkdir(exist_ok=True) # Ensure output directory exists - output_zip_path = OUTPUT_DIR / f"fastled_output_{hash(str(file_path))}.zip" - _print(f"\nCreating output zip at: {output_zip_path}") - - start_zip = time.time() - try: - with zipfile.ZipFile( - output_zip_path, "w", zipfile.ZIP_DEFLATED, compresslevel=1 - ) as zip_out: - _print("\nAdding files to output zip:") - for file_path in fastled_js_dir.rglob("*"): - if file_path.is_file(): - arc_path = file_path.relative_to(fastled_js_dir) - _print(f" Adding: {arc_path}") - zip_out.write(file_path, arc_path) - except zipfile.BadZipFile as e: - _print(f"Error creating zip file: {e}") - return HTTPException(status_code=500, detail=f"Failed to create zip file: {e}") - except zlib.error as e: - _print(f"Compression error: {e}") - return HTTPException( - status_code=500, detail=f"Zip compression failed - zlib error: {e}" - ) - except Exception as e: - _print(f"Unexpected error creating zip: {e}") - return HTTPException(status_code=500, detail=f"Failed to create zip file: {e}") - zip_time = time.time() - start_zip - print(f"Zip file created in {zip_time:.2f}s") - - def cleanup_files(): - if output_zip_path.exists(): - output_zip_path.unlink() - if temp_zip_dir: - shutil.rmtree(temp_zip_dir, ignore_errors=True) - if temp_src_dir: - shutil.rmtree(temp_src_dir, ignore_errors=True) - - background_tasks.add_task(cleanup_files) - _print(f"\nReturning output zip: {output_zip_path}") - return FileResponse( - path=output_zip_path, - media_type="application/zip", - filename="fastled_output.zip", - background=background_tasks, - ) - - -def memory_watchdog() -> None: - """Monitor memory usage and kill process if it exceeds limit.""" - if _MEMORY_LIMIT_MB <= 0: - return - - def check_memory() -> None: - while True: - process = psutil.Process(os.getpid()) - memory_mb = process.memory_info().rss / 1024 / 1024 - if memory_mb > _MEMORY_LIMIT_MB: - print( - f"Memory limit exceeded! Using {memory_mb:.1f}MB > {_MEMORY_LIMIT_MB}MB limit" - ) - os._exit(_MEMORY_EXCEEDED_EXIT_CODE) - time.sleep(_MEMORY_CHECK_INTERVAL) - - watchdog_thread = threading.Thread(target=check_memory, daemon=True) - watchdog_thread.start() - - -def get_settings() -> dict: - settings = { - "ALLOW_SHUTDOWN": _ALLOW_SHUTDOWN, - "NO_AUTO_UPDATE": os.environ.get("NO_AUTO_UPDATE", "0"), - "NO_SKETCH_CACHE": _NO_SKETCH_CACHE, - "LIVE_GIT_UPDATES_ENABLED": _LIVE_GIT_UPDATES_ENABLED, - "LIVE_GIT_UPDATES_INTERVAL": _LIVE_GIT_UPDATES_INTERVAL, - "UPLOAD_LIMIT": _UPLOAD_LIMIT, - "VOLUME_MAPPED_SRC": str(_VOLUME_MAPPED_SRC), - "VOLUME_MAPPED_SRC_EXISTS": _VOLUME_MAPPED_SRC.exists(), - } - return settings - - -def startup() -> None: - print("Starting FastLED wasm compiler server...") - try: - print(f"Settings: {json.dumps(get_settings(), indent=2)}") - except Exception as e: - print(f"Error getting settings: {e}") - - if _MEMORY_LIMIT_MB > 0: - print(f"Starting memory watchdog (limit: {_MEMORY_LIMIT_MB}MB)") - memory_watchdog() - - sync_source_directory_if_volume_is_mapped() - if _LIVE_GIT_UPDATES_ENABLED: - Timer( - _LIVE_GIT_UPDATES_INTERVAL, sync_live_git_to_target - ).start() # Start the periodic git update - else: - print("Auto updates disabled") - - -@app.get("/", include_in_schema=False) -async def read_root() -> RedirectResponse: - """Redirect to the /docs endpoint.""" - - print("Endpoint accessed: / (root redirect to docs)") - return RedirectResponse(url="/docs") - - -@app.get("/healthz") -async def healthz() -> dict: - """Health check endpoint.""" - print("Endpoint accessed: /healthz") - return {"status": "ok"} - - -if _ALLOW_SHUTDOWN: - - @app.get("/shutdown") - async def shutdown() -> dict: - """Shutdown the server.""" - print("Endpoint accessed: /shutdown") - print("Shutting down server...") - SKETCH_CACHE.close() - os._exit(0) - return {"status": "ok"} - - -@app.get("/settings") -async def settings() -> dict: - """Get the current settings.""" - print("Endpoint accessed: /settings") - settings = { - "ALLOW_SHUTDOWN": _ALLOW_SHUTDOWN, - "NO_AUTO_UPDATE": os.environ.get("NO_AUTO_UPDATE", "0"), - "NO_SKETCH_CACHE": _NO_SKETCH_CACHE, - "LIVE_GIT_UPDATES_ENABLED": _LIVE_GIT_UPDATES_ENABLED, - "LIVE_GIT_UPDATES_INTERVAL": _LIVE_GIT_UPDATES_INTERVAL, - "UPLOAD_LIMIT": _UPLOAD_LIMIT, - "VOLUME_MAPPED_SRC": str(_VOLUME_MAPPED_SRC), - "VOLUME_MAPPED_SRC_EXISTS": _VOLUME_MAPPED_SRC.exists(), - } - return settings - - -@app.get("/compile/wasm/inuse") -async def compiler_in_use() -> dict: - """Check if the compiler is in use.""" - print("Endpoint accessed: /compile/wasm/inuse") - return {"in_use": COMPILE_LOCK.locked()} - - -def zip_example_to_file(example: str, dst_zip_file: Path) -> None: - examples_dir = Path(f"/js/fastled/examples/{example}") - if not examples_dir.exists(): - raise HTTPException( - status_code=404, detail=f"Example {example} not found at {examples_dir}" - ) - - try: - print(f"Creating zip file at: {dst_zip_file}") - with zipfile.ZipFile(str(dst_zip_file), "w", zipfile.ZIP_DEFLATED) as zip_out: - for file_path in examples_dir.rglob("*"): - if file_path.is_file(): - if "fastled_js" in file_path.parts: - continue - arc_path = file_path.relative_to(Path("/js/fastled/examples")) - zip_out.write(file_path, arc_path) - print(f"Zip file created at: {dst_zip_file}") - except Exception as e: - warnings.warn(f"Error: {e}") - raise - - -def make_random_path_string(digits: int) -> str: - """Generate a random number.""" - import random - import string - - return "".join(random.choices(string.ascii_lowercase + string.digits, k=digits)) - - -@app.get("/project/init") -def project_init(background_tasks: BackgroundTasks) -> FileResponse: - """Archive /js/fastled/examples/wasm into a zip file and return it.""" - print("Endpoint accessed: /project/init") - # tmp_zip_file = NamedTemporaryFile(delete=False) - # tmp_zip_path = Path(tmp_zip_file.name) - - tmp_zip_path = _TEMP_DIR / f"wasm-{make_random_path_string(16)}.zip" - zip_example_to_file("wasm", tmp_zip_path) - - # assert tmp_zip_path.exists() - if not tmp_zip_path.exists(): - warnings.warn("Failed to create zip file for wasm example.") - raise HTTPException( - status_code=500, detail="Failed to create zip file for wasm example." - ) - - def cleanup() -> None: - try: - os.unlink(tmp_zip_path) - except Exception as e: - warnings.warn(f"Error cleaning up: {e}") - - background_tasks.add_task(cleanup) - return FileResponse( - path=tmp_zip_path, - media_type="application/zip", - filename="fastled_example.zip", - background=background_tasks, - ) - - -@app.post("/project/init") -def project_init_example( - background_tasks: BackgroundTasks, example: str = Body(...) -) -> FileResponse: - """Archive /js/fastled/examples/{example} into a zip file and return it.""" - print(f"Endpoint accessed: /project/init/example with example: {example}") - if ".." in example: - raise HTTPException(status_code=400, detail="Invalid example name.") - name = Path("example").name - tmp_file_path = _TEMP_DIR / f"{name}-{make_random_path_string(16)}.zip" - zip_example_to_file(example, Path(tmp_file_path)) - - if not tmp_file_path.exists(): - warnings.warn(f"Failed to create zip file for {example} example.") - raise HTTPException( - status_code=500, detail=f"Failed to create zip file for {example} example." - ) - - def cleanup() -> None: - try: - os.unlink(tmp_file_path) - except Exception as e: - warnings.warn(f"Error cleaning up: {e}") - raise - - background_tasks.add_task(cleanup) - return FileResponse( - path=tmp_file_path, - media_type="application/zip", - filename="fastled_example.zip", - background=background_tasks, - ) - - -@app.get("/info") -def info_examples() -> dict: - """Get a list of examples.""" - print("Endpoint accessed: /info") - uptime = time.time() - START_TIME - uptime_fmtd = time.strftime("%H:%M:%S", time.gmtime(uptime)) - try: - build_timestamp = ( - Path("/image_timestamp.txt").read_text(encoding="utf-8").strip() - ) - except Exception as e: - import warnings - - warnings.warn(f"Error reading build timestamp: {e}") - build_timestamp = "unknown" - - # ARG FASTLED_VERSION=3.9.11 - # ENV FASTLED_VERSION=${FASTLED_VERSION} - - fastled_version = os.environ.get("FASTLED_VERSION", "unknown") - out = { - "examples": _EXAMPLES, - "compile_count": COMPILE_COUNT, - "compile_failures": COMPILE_FAILURES, - "compile_successes": COMPILE_SUCCESSES, - "uptime": uptime_fmtd, - "build_timestamp": build_timestamp, - "fastled_version": fastled_version, - } - return out - - -# THIS MUST NOT BE ASYNC!!!! -@app.post("/compile/wasm") -def compile_wasm( - file: UploadFile = File(...), - authorization: str = Header(None), - build: str = Header(None), - profile: str = Header(None), - background_tasks: BackgroundTasks = BackgroundTasks(), -) -> FileResponse: - """Upload a file into a temporary directory.""" - print(f"Endpoint accessed: /compile/wasm with file: {file.filename}") - if build is not None: - build = build.lower() - - if build not in ["quick", "release", "debug", None]: - raise HTTPException( - status_code=400, - detail="Invalid build mode. Must be one of 'quick', 'release', or 'debug' or omitted", - ) - do_profile: bool = False - if profile is not None: - do_profile = profile.lower() == "true" or profile.lower() == "1" - print(f"Build mode is {build}") - build = build or "quick" - print(f"Starting upload process for file: {file.filename}") - - if not _TEST and authorization != _AUTH_TOKEN: - raise HTTPException(status_code=401, detail="Unauthorized") - - if file is None: - raise HTTPException(status_code=400, detail="No file uploaded.") - - if file.filename is None: - raise HTTPException(status_code=400, detail="No filename provided.") - - if not file.filename.endswith(".zip"): - raise HTTPException( - status_code=400, detail="Uploaded file must be a zip archive." - ) - - temp_zip_dir = None - temp_src_dir = None - - try: - # Create temporary directories - one for zip, one for source - temp_zip_dir = tempfile.mkdtemp() - temp_src_dir = tempfile.mkdtemp() - print( - f"Created temporary directories:\nzip_dir: {temp_zip_dir}\nsrc_dir: {temp_src_dir}" - ) - - file_path = Path(temp_zip_dir) / file.filename - print(f"Saving uploaded file to: {file_path}") - - # Simple file save since size is already checked by middleware - with open(file_path, "wb") as f: - shutil.copyfileobj(file.file, f) - - print("extracting zip file...") - hash_value: str | None = None - with zipfile.ZipFile(file_path, "r") as zip_ref: - # Extract everything first - zip_ref.extractall(temp_src_dir) - - # Then find and remove any platformio.ini files - platform_files = list(Path(temp_src_dir).rglob("*platformio.ini")) - if platform_files: - warnings.warn(f"Removing platformio.ini files: {platform_files}") - for p in platform_files: - p.unlink() - - try: - hash_value = generate_hash_of_project_files(Path(temp_src_dir)) - except Exception as e: - warnings.warn( - f"Error generating hash: {e}, fast cache access is disabled for this build." - ) - - def on_files_changed() -> None: - print("Source files changed, clearing cache") - SKETCH_CACHE.clear() - - sync_source_directory_if_volume_is_mapped(callback=on_files_changed) - - entry: bytes | None = None - if hash_value is not None: - print(f"Hash of source files: {hash_value}") - entry = try_get_cached_zip(hash_value) - if entry is not None: - print("Returning cached zip file") - # Create a temporary file for the cached data - tmp_file = NamedTemporaryFile(delete=False) - tmp_file.write(entry) - tmp_file.close() - - def cleanup_temp(): - try: - os.unlink(tmp_file.name) - except: # noqa: E722 - pass - - background_tasks.add_task(cleanup_temp) - - return FileResponse( - path=tmp_file.name, - media_type="application/zip", - filename="fastled_output.zip", - background=background_tasks, - ) - - print("\nContents of source directory:") - for path in Path(temp_src_dir).rglob("*"): - print(f" {path}") - out = compile_source( - Path(temp_src_dir), - file_path, - background_tasks, - build, - do_profile, - hash_value, - ) - if isinstance(out, HTTPException): - print("Raising HTTPException") - txt = out.detail - json_str = json.dumps(txt) - warnings.warn(f"Error compiling source: {json_str}") - raise out - # Cache the compiled zip file - out_path = Path(out.path) - data = out_path.read_bytes() - if hash_value is not None: - cache_put(hash_value, data) - return out - except HTTPException as e: - import traceback - - stacktrace = traceback.format_exc() - print(f"HTTPException in upload process: {str(e)}\n{stacktrace}") - raise e - - except Exception as e: - import traceback - - stack_trace = traceback.format_exc() - print(f"Error in upload process: {stack_trace}") - raise HTTPException( - status_code=500, - detail=f"Upload process failed: {str(e)}\nTrace: {e.__traceback__}", - ) - finally: - # Clean up in case of error - if temp_zip_dir: - shutil.rmtree(temp_zip_dir, ignore_errors=True) - if temp_src_dir: - shutil.rmtree(temp_src_dir, ignore_errors=True) diff --git a/src/platforms/wasm/compiler/sketch_hasher.py b/src/platforms/wasm/compiler/sketch_hasher.py deleted file mode 100644 index 7befbf3b25..0000000000 --- a/src/platforms/wasm/compiler/sketch_hasher.py +++ /dev/null @@ -1,254 +0,0 @@ - -""" -This module provides functions to generate a hash of all files in a directory. -Source files like ino,cpp,h,hpp are concatenated and preprocessed with GCC. -Data date files are hashed as is. -""" - - -__all__ = ["generate_hash_of_project_files"] - -from dataclasses import dataclass -from pathlib import Path -from tempfile import TemporaryDirectory -from typing import List -import hashlib -import os -import re -import subprocess -import warnings - -_SOURCE_EXTENSIONS = [".cpp", ".hpp", ".h", ".ino"] - -@dataclass -class ProjectFiles: - """A class to represent the project files.""" - - src_files: list[Path] - other_files: list[Path] - - -@dataclass -class SrcFileHashResult: - hash: str - stdout: str - error: bool - - -def hash_string(s: str) -> str: - return hashlib.sha256(s.encode()).hexdigest() - - -def collect_files(directory: Path, src_file_extensions: list[str] | None = None) -> ProjectFiles: - """Collect files from a directory and separate them into source and other files. - - Args: - directory (Path): The directory to scan for files. - - Returns: - ProjectFiles: Object containing lists of source and other files. - """ - src_file_extensions = src_file_extensions or _SOURCE_EXTENSIONS - print(f"Collecting files from {directory}") - - src_files: list[Path] = [] - other_files: list[Path] = [] - - def is_source_file(filename: str) -> bool: - return any(filename.endswith(ext) for ext in src_file_extensions) - - for root, _, filenames in os.walk(str(directory)): - for filename in filenames: - print(f"Checking file: {filename}") - file_path = Path(os.path.join(root, filename)) - - if is_source_file(filename): - src_files.append(file_path) - else: - other_files.append(file_path) - - return ProjectFiles(src_files=src_files, other_files=other_files) - - -def concatenate_files(file_list: List[Path], output_file: Path) -> None: - """Concatenate files into a single output file. - - Args: - file_list (List[str]): List of file paths to concatenate. - output_file (str): Path to the output file. - """ - with open(str(output_file), "w", encoding="utf-8") as outfile: - for file_path in file_list: - outfile.write(f"// File: {file_path}\n") - with open(file_path, "r", encoding="utf-8") as infile: - outfile.write(infile.read()) - outfile.write("\n\n") - - -def collapse_spaces_preserve_cstrings(line: str): - def replace_outside_cstrings(match): - # This function processes the part outside of C strings - content = match.group(0) - if content.startswith('"') or content.startswith("'"): - return content # It's inside a C string, keep as is - else: - # Collapse spaces outside of C strings - return " ".join(content.split()) - - # Regular expression to match C strings and non-C string parts - pattern = r'\"(?:\\.|[^\"])*\"|\'.*?\'|[^"\']+' - processed_line = "".join( - replace_outside_cstrings(match) for match in re.finditer(pattern, line) - ) - return processed_line - - -# return a hash -def preprocess_with_gcc(input_file: Path, output_file: Path) -> None: - """Preprocess a file with GCC, leaving #include directives intact. - - Args: - input_file (str): Path to the input file. - output_file (str): Path to the preprocessed output file. - """ - # Convert paths to absolute paths - # input_file = os.path.abspath(str(input_file)) - input_file = input_file.absolute() - output_file = output_file.absolute() - temp_input = str(input_file) + ".tmp" - - try: - # Create modified version of input that comments out includes - with open(str(input_file), "r") as fin, open(str(temp_input), "w") as fout: - for line in fin: - if line.strip().startswith("#include"): - fout.write(f"// PRESERVED: {line}") - else: - fout.write(line) - - # Run GCC preprocessor with explicit output path in order to remove - # comments. This is necessary to ensure that the hash - # of the preprocessed file is consistent without respect to formatting - # and whitespace. - gcc_command: list[str] = [ - "gcc", - "-E", # Preprocess only - "-P", # No line markers - "-fdirectives-only", - "-fpreprocessed", # Handle preprocessed input - "-x", - "c++", # Explicitly treat input as C++ source - "-o", - str(output_file), # Explicit output file - temp_input, - ] - - result = subprocess.run(gcc_command, check=True, capture_output=True, text=True) - - if not os.path.exists(output_file): - raise FileNotFoundError( - f"GCC failed to create output file. stderr: {result.stderr}" - ) - - # Restore include lines - with open(output_file, "r") as f: - content = f.read() - - content = content.replace("// PRESERVED: #include", "#include") - out_lines: list[str] = [] - # now preform minification to further strip out horizontal whitespace and // File: comments. - for line in content.split("\n"): - # Skip file marker comments and empty lines - line = line.strip() - if not line: # skip empty line - continue - if line.startswith( - "// File:" - ): # these change because of the temp file, so need to be removed. - continue - # Collapse multiple spaces into single space and strip whitespace - # line = ' '.join(line.split()) - line = collapse_spaces_preserve_cstrings(line) - out_lines.append(line) - # Join with new lines - content = "\n".join(out_lines) - with open(output_file, "w") as f: - f.write(content) - - print(f"Preprocessed file saved to {output_file}") - - except subprocess.CalledProcessError as e: - print(f"GCC preprocessing failed: {e.stderr}") - raise - except Exception as e: - print(f"Preprocessing error: {str(e)}") - raise - finally: - # Clean up temporary file - try: - if os.path.exists(temp_input): - os.remove(temp_input) - except: # noqa: E722 - warnings.warn(f"Failed to remove temporary file: {temp_input}") - pass - - -def generate_hash_of_src_files(src_files: list[Path]) -> SrcFileHashResult: - """Generate a hash of all source files in a directory. - - Args: - src_files (list[Path]): List of source files to hash. - - Returns: - SrcFileHashResult: Object containing hash, stdout and error status. - """ - try: - with TemporaryDirectory() as temp_dir: - temp_file = Path(temp_dir) / "concatenated_output.cpp" - preprocessed_file = Path(temp_dir) / "preprocessed_output.cpp" - concatenate_files(src_files, Path(temp_file)) - preprocess_with_gcc(temp_file, preprocessed_file) - contents = preprocessed_file.read_text() - - # strip the last line in it: - parts = contents.split("\n") - out_lines: list[str] = [] - for line in parts: - if "concatenated_output.cpp" not in line: - out_lines.append(line) - - contents = "\n".join(out_lines) - return SrcFileHashResult( - hash=hash_string(contents), - stdout="", # No stdout in success case - error=False, - ) - except Exception: - import traceback - - stack_trace = traceback.format_exc() - print(stack_trace) - return SrcFileHashResult(hash="", stdout=stack_trace, error=True) - - -def generate_hash_of_project_files(root_dir: Path) -> str: - """Generate a hash of all files in a directory. - - Args: - root_dir (Path): The root directory to hash. - - Returns: - str: The hash of all files in the directory. - """ - project_files = collect_files(root_dir) - src_result = generate_hash_of_src_files(project_files.src_files) - if src_result.error: - raise Exception(f"Error hashing source files: {src_result.stdout}") - - other_files = project_files.other_files - # for all other files, don't pre-process them, just hash them - hash_object = hashlib.sha256() - for file in other_files: - hash_object.update(file.read_bytes()) - other_files_hash = hash_object.hexdigest() - return hash_string(src_result.hash + other_files_hash) diff --git a/src/platforms/wasm/compiler/wasm_compiler_flags.py b/src/platforms/wasm/compiler/wasm_compiler_flags.py index 4c2d2ea459..1aff6b9234 100644 --- a/src/platforms/wasm/compiler/wasm_compiler_flags.py +++ b/src/platforms/wasm/compiler/wasm_compiler_flags.py @@ -1,3 +1,7 @@ +# pylint: skip-file +# flake8: noqa +# type: ignore + import os USE_CCACHE = True if "NO_CCACHE" not in os.environ else False @@ -12,7 +16,7 @@ # Set flags based on build mode DEBUG = BUILD_MODE == "DEBUG" -QUICK_BUILD = BUILD_MODE == "QUICK" +QUICK_BUILD = BUILD_MODE == "QUICK" OPTIMIZED = BUILD_MODE == "RELEASE" # Global variable to control WASM output (0 for asm.js, 1 for WebAssembly) @@ -21,7 +25,7 @@ USE_WASM = 2 if DEBUG or QUICK_BUILD: - USE_WASM=1 # disable wasm2js on these builds. + USE_WASM = 1 # disable wasm2js on these builds. build_mode = "-O1" if QUICK_BUILD else "-Oz" @@ -49,13 +53,13 @@ "-DFASTLED_ENGINE_EVENTS_MAX_LISTENERS=50", "-DFASTLED_FORCE_NAMESPACE=1", "-DFASTLED_USE_PROGMEM=0", - #"-DDISABLE_EXCEPTION_CATCHING=1", + # "-DDISABLE_EXCEPTION_CATCHING=1", "-sALLOW_MEMORY_GROWTH=0", - #"-fno-exceptions", - #"-fno-rtti", - #"-DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=0", - #"-sDISABLE_EXCEPTION_CATCHING=1", - #"-sDISABLE_EXCEPTION_THROWING=0", + # "-fno-exceptions", + # "-fno-rtti", + # "-DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=0", + # "-sDISABLE_EXCEPTION_CATCHING=1", + # "-sDISABLE_EXCEPTION_THROWING=0", build_mode, "--bind", "-DUSE_OFFSET_CONVERTER=0", @@ -66,12 +70,14 @@ "-Wnon-c-typedef-for-linkage", f"-sWASM={USE_WASM}", "-fuse-ld=lld", - #-Wbad-function-cast -Wcast-function + # -Wbad-function-cast -Wcast-function "-Werror=bad-function-cast", "-Werror=cast-function-type", # add /js/src/ to the include path "-I", "src", + # add /js/fastled/src/platforms/wasm/compiler to the include path + "-I/js/fastled/src/platforms/wasm/compiler", ] if QUICK_BUILD: @@ -83,13 +89,13 @@ if opt in sketch_flags: sketch_flags.remove(opt) sketch_flags += [ - '-g3', - '-gsource-map', - '--emit-symbol-map', - '-sSTACK_OVERFLOW_CHECK=2', - '-ASSERTIONS=1', - '-fsanitize=address', - '-fsanitize=undefined', + "-g3", + "-gsource-map", + "--emit-symbol-map", + "-sSTACK_OVERFLOW_CHECK=2", + "-ASSERTIONS=1", + "-fsanitize=address", + "-fsanitize=undefined", ] @@ -97,11 +103,11 @@ "-sEXPORTED_RUNTIME_METHODS=['ccall','cwrap','stringToUTF8','lengthBytesUTF8']", "-sEXPORTED_FUNCTIONS=['_malloc','_free','_extern_setup','_extern_loop','_fastled_declare_files']", "--no-entry", - #"-fno-exceptions", - #"-fno-rtti", - #"-DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=0", - #"-sDISABLE_EXCEPTION_CATCHING=1", - #"-sDISABLE_EXCEPTION_THROWING=0", + # "-fno-exceptions", + # "-fno-rtti", + # "-DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=0", + # "-sDISABLE_EXCEPTION_CATCHING=1", + # "-sDISABLE_EXCEPTION_THROWING=0", ] if OPTIMIZED: @@ -124,18 +130,17 @@ fastled_compile_cc_flags = [ "-Werror=bad-function-cast", "-Werror=cast-function-type", - #"-fno-exceptions", - #"-fno-rtti", + # "-fno-exceptions", + # "-fno-rtti", build_mode, - #"-DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=0", - #"-fno-exceptions", - #"-sDISABLE_EXCEPTION_CATCHING=1", - #"-sDISABLE_EXCEPTION_THROWING=0", + # "-DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=0", + # "-fno-exceptions", + # "-sDISABLE_EXCEPTION_CATCHING=1", + # "-sDISABLE_EXCEPTION_THROWING=0", + "-I/js/fastled/src/platforms/wasm/compiler", ] - - fastled_compile_link_flags = [ "-Wl,--whole-archive,-fuse-ld=lld", "-Werror=bad-function-cast", @@ -150,5 +155,3 @@ # for final linking. lb.env.Append(CCFLAGS=fastled_compile_cc_flags) lb.env.Append(LINKFLAGS=fastled_compile_link_flags) - - diff --git a/src/platforms/wasm/timer.cpp b/src/platforms/wasm/timer.cpp index d9d7f68586..c895df8939 100644 --- a/src/platforms/wasm/timer.cpp +++ b/src/platforms/wasm/timer.cpp @@ -36,6 +36,7 @@ extern "C" { return uint32_t(out & 0xFFFFFFFF); } + // Replacement for 'delay' in WebAssembly context EMSCRIPTEN_KEEPALIVE void delay(int ms) { // Keep in mind this is NOT ASYNC as of 2024-12, and will block the main thread. @@ -47,6 +48,12 @@ extern "C" { // upload times back to the client. std::this_thread::sleep_for(std::chrono::milliseconds(ms)); } + + // Replacement for 'yield' in WebAssembly context + EMSCRIPTEN_KEEPALIVE void yield() { + // Use emscripten_yield to allow the browser to perform other tasks + delay(0); + } } #endif // __EMSCRIPTEN__ diff --git a/src/third_party/espressif/led_strip/src/led_strip_rmt_dev.c b/src/third_party/espressif/led_strip/src/led_strip_rmt_dev.c index 346a5b5065..2bdc51547f 100644 --- a/src/third_party/espressif/led_strip/src/led_strip_rmt_dev.c +++ b/src/third_party/espressif/led_strip/src/led_strip_rmt_dev.c @@ -6,6 +6,8 @@ #if FASTLED_RMT5 +#include "fl/unused.h" + /* * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD @@ -142,6 +144,7 @@ esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const l { led_strip_rmt_obj *rmt_strip = NULL; esp_err_t ret = ESP_OK; + FASTLED_UNUSED(ret); ESP_GOTO_ON_FALSE(led_config && rmt_config && ret_strip, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); led_color_component_format_t component_fmt = led_config->color_component_format; // If R/G/B order is not specified, set default GRB order as fallback diff --git a/src/third_party/espressif/led_strip/src/led_strip_rmt_encoder.c b/src/third_party/espressif/led_strip/src/led_strip_rmt_encoder.c index f6fb226d3b..e5c1fc540b 100644 --- a/src/third_party/espressif/led_strip/src/led_strip_rmt_encoder.c +++ b/src/third_party/espressif/led_strip/src/led_strip_rmt_encoder.c @@ -5,6 +5,8 @@ #if FASTLED_RMT5 +#include "fl/unused.h" + /* * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD @@ -83,6 +85,7 @@ static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder) esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder) { esp_err_t ret = ESP_OK; + FASTLED_UNUSED(ret); ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); ESP_GOTO_ON_FALSE(config->led_model < LED_MODEL_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid led model"); // Create a temporary config with the same base values @@ -131,6 +134,7 @@ esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rm esp_err_t rmt_new_led_strip_encoder_with_timings(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder) { esp_err_t ret = ESP_OK; + FASTLED_UNUSED(ret); rmt_led_strip_encoder_t *led_encoder = NULL; ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); diff --git a/src/third_party/espressif/led_strip/src/led_strip_spi_dev.c b/src/third_party/espressif/led_strip/src/led_strip_spi_dev.c index 3df6094351..bdba37474f 100644 --- a/src/third_party/espressif/led_strip/src/led_strip_spi_dev.c +++ b/src/third_party/espressif/led_strip/src/led_strip_spi_dev.c @@ -8,6 +8,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ + #include #include #include @@ -17,6 +18,7 @@ #include "soc/spi_periph.h" #include "led_strip.h" #include "led_strip_interface.h" +#include "fl/unused.h" #define LED_STRIP_SPI_DEFAULT_RESOLUTION (2.5 * 1000 * 1000) // 2.5MHz resolution #define LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE 4 @@ -151,6 +153,7 @@ esp_err_t led_strip_new_spi_device(const led_strip_config_t *led_config, const l { led_strip_spi_obj *spi_strip = NULL; esp_err_t ret = ESP_OK; + FASTLED_UNUSED(ret); ESP_GOTO_ON_FALSE(led_config && spi_config && ret_strip, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); led_color_component_format_t component_fmt = led_config->color_component_format; // If R/G/B order is not specified, set default GRB order as fallback diff --git a/tests/test_apa102_hd.cpp b/tests/test_apa102_hd.cpp index 000b7964d0..2af05dcb3b 100644 --- a/tests/test_apa102_hd.cpp +++ b/tests/test_apa102_hd.cpp @@ -5,7 +5,7 @@ #include "test.h" #include "FastLED.h" -#include "five_bit_hd_gamma.h" +#include "fl/five_bit_hd_gamma.h" #include "assert.h" #include "math.h" #include diff --git a/tests/test_arduino_wasm.cpp b/tests/test_arduino_wasm.cpp deleted file mode 100644 index 3bd516c1f2..0000000000 --- a/tests/test_arduino_wasm.cpp +++ /dev/null @@ -1,20 +0,0 @@ - -// g++ --std=c++11 test.cpp - -#include "test.h" - -#include "test.h" -#include "platforms/wasm/compiler/Arduino.h" - -#include "fl/namespace.h" -FASTLED_USING_NAMESPACE - -TEST_CASE("arduino_wasm") { - SUBCASE("random") { - for (int i = 0; i < 100; i++) { - long r = random(0, 1); - CHECK_EQ(0, r); - } - } -} - diff --git a/tests/test_sin32.cpp b/tests/test_sin32.cpp new file mode 100644 index 0000000000..489080c5fb --- /dev/null +++ b/tests/test_sin32.cpp @@ -0,0 +1,33 @@ + +// g++ --std=c++11 test.cpp + +#include "test.h" + +#include "test.h" +#include "fl/sin32.h" + +#include "fl/namespace.h" +FASTLED_USING_NAMESPACE + +// 16777216 is 1 cycle +const uint32_t _360 = 16777216; +const uint32_t _ONE = 2147418112; +const uint32_t _NEG_ONE = -2147418112; + +TEST_CASE("compile test") { + int32_t result = sin32(0); + REQUIRE(result == 0); + + result = sin32(_360); + REQUIRE(result == 0); + + result = sin32(_360 / 4); + REQUIRE(result == _ONE); + + result = sin32(_360 / 2); + REQUIRE(result == 0); + + result = sin32(_360 / 4 * 3); + REQUIRE(result == _NEG_ONE); + +}