diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 27beec99c3..6104922bed 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,9 +18,17 @@ jobs: - uses: actions/download-artifact@v4 with: merge-multiple: true + - name: "✏️ Generate release changelog" + id: changelog + uses: janheinrichmerker/action-github-changelog-generator@v2.3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + sinceTag: v0.15.0 + releaseBranch: 0_15_x - name: Create draft release uses: softprops/action-gh-release@v1 with: + body: ${{ steps.changelog.outputs.changelog }} draft: True files: | *.bin diff --git a/.gitignore b/.gitignore index 8f083e3f6a..6c80b8e8e5 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ wled-update.sh /build_output/ /node_modules/ +/logs/ /wled00/extLibs /wled00/LittleFS @@ -22,3 +23,4 @@ wled-update.sh /wled00/Release /wled00/wled00.ino.cpp /wled00/html_*.h +_codeql_detected_source_root diff --git a/CHANGELOG.md b/CHANGELOG.md index c570ac1f7e..b04ce71f20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ ## WLED changelog +#### Build 2412100 +- WLED 0.15.0 release +- Usermod BME280: Fix "Unit of Measurement" for temperature +- WiFi reconnect bugfix (@blazoncek) + +#### Build 2411250 +- WLED 0.15.0-rc1 release +- Add support for esp32S3_wroom2 (#4243 by @softhack007) +- Fix mixed LED SK6812 and ws2812b booloop (#4301 by @willmmiles) +- Improved FPS calculation (by DedeHai) +- Fix crashes when using HTTP API within MQTT (#4269 by @willmmiles) +- Fix array overflow in exploding_fireworks (#4120 by @willmmiles) +- Fix MQTT topic buffer length (#4293 by @WouterGritter) +- Fix SparkFunDMX fix for possible array bounds violation in DMX.write (by @softhack007) +- Allow TV Simulator on single LED segments (by @softhack007) +- Fix WLED_RELEASE_NAME (by @netmindz) + + #### Build 2410270 - WLED 0.15.0-b7 release - Re-license the WLED project from MIT to EUPL (#4194 by @Aircoookie) diff --git a/HSVTicker_Dokumentation_1.pdf b/HSVTicker_Dokumentation_1.pdf new file mode 100644 index 0000000000..42e628547b Binary files /dev/null and b/HSVTicker_Dokumentation_1.pdf differ diff --git a/lib/NeoESP32RmtHI/include/NeoEsp32RmtHIMethod.h b/lib/NeoESP32RmtHI/include/NeoEsp32RmtHIMethod.h new file mode 100644 index 0000000000..02e066f741 --- /dev/null +++ b/lib/NeoESP32RmtHI/include/NeoEsp32RmtHIMethod.h @@ -0,0 +1,469 @@ +/*------------------------------------------------------------------------- +NeoPixel driver for ESP32 RMTs using High-priority Interrupt + +(NB. This cannot be mixed with the non-HI driver.) + +Written by Will M. Miles. + +I invest time and resources providing this open source code, +please support me by donating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +#if defined(ARDUINO_ARCH_ESP32) + +// Use the NeoEspRmtSpeed types from the driver-based implementation +#include + + +namespace NeoEsp32RmtHiMethodDriver { + // Install the driver for a specific channel, specifying timing properties + esp_err_t Install(rmt_channel_t channel, uint32_t rmtBit0, uint32_t rmtBit1, uint32_t resetDuration); + + // Remove the driver on a specific channel + esp_err_t Uninstall(rmt_channel_t channel); + + // Write a buffer of data to a specific channel. + // Buffer reference is held until write completes. + esp_err_t Write(rmt_channel_t channel, const uint8_t *src, size_t src_size); + + // Wait until transaction is complete. + esp_err_t WaitForTxDone(rmt_channel_t channel, TickType_t wait_time); +}; + +template class NeoEsp32RmtHIMethodBase +{ +public: + typedef NeoNoSettings SettingsObject; + + NeoEsp32RmtHIMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + _sizeData(pixelCount * elementSize + settingsSize), + _pin(pin) + { + construct(); + } + + NeoEsp32RmtHIMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize, NeoBusChannel channel) : + _sizeData(pixelCount* elementSize + settingsSize), + _pin(pin), + _channel(channel) + { + construct(); + } + + ~NeoEsp32RmtHIMethodBase() + { + // wait until the last send finishes before destructing everything + // arbitrary time out of 10 seconds + ESP_ERROR_CHECK_WITHOUT_ABORT(NeoEsp32RmtHiMethodDriver::WaitForTxDone(_channel.RmtChannelNumber, 10000 / portTICK_PERIOD_MS)); + + ESP_ERROR_CHECK(NeoEsp32RmtHiMethodDriver::Uninstall(_channel.RmtChannelNumber)); + + gpio_matrix_out(_pin, SIG_GPIO_OUT_IDX, false, false); + pinMode(_pin, INPUT); + + free(_dataEditing); + free(_dataSending); + } + + bool IsReadyToUpdate() const + { + return (ESP_OK == ESP_ERROR_CHECK_WITHOUT_ABORT_SILENT_TIMEOUT(NeoEsp32RmtHiMethodDriver::WaitForTxDone(_channel.RmtChannelNumber, 0))); + } + + void Initialize() + { + rmt_config_t config = {}; + + config.rmt_mode = RMT_MODE_TX; + config.channel = _channel.RmtChannelNumber; + config.gpio_num = static_cast(_pin); + config.mem_block_num = 1; + config.tx_config.loop_en = false; + + config.tx_config.idle_output_en = true; + config.tx_config.idle_level = T_SPEED::IdleLevel; + + config.tx_config.carrier_en = false; + config.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW; + + config.clk_div = T_SPEED::RmtClockDivider; + + ESP_ERROR_CHECK(rmt_config(&config)); // Uses ESP library + ESP_ERROR_CHECK(NeoEsp32RmtHiMethodDriver::Install(_channel.RmtChannelNumber, T_SPEED::RmtBit0, T_SPEED::RmtBit1, T_SPEED::RmtDurationReset)); + } + + void Update(bool maintainBufferConsistency) + { + // wait for not actively sending data + // this will time out at 10 seconds, an arbitrarily long period of time + // and do nothing if this happens + if (ESP_OK == ESP_ERROR_CHECK_WITHOUT_ABORT(NeoEsp32RmtHiMethodDriver::WaitForTxDone(_channel.RmtChannelNumber, 10000 / portTICK_PERIOD_MS))) + { + // now start the RMT transmit with the editing buffer before we swap + ESP_ERROR_CHECK_WITHOUT_ABORT(NeoEsp32RmtHiMethodDriver::Write(_channel.RmtChannelNumber, _dataEditing, _sizeData)); + + if (maintainBufferConsistency) + { + // copy editing to sending, + // this maintains the contract that "colors present before will + // be the same after", otherwise GetPixelColor will be inconsistent + memcpy(_dataSending, _dataEditing, _sizeData); + } + + // swap so the user can modify without affecting the async operation + std::swap(_dataSending, _dataEditing); + } + } + + bool AlwaysUpdate() + { + // this method requires update to be called only if changes to buffer + return false; + } + + bool SwapBuffers() + { + std::swap(_dataSending, _dataEditing); + return true; + } + + uint8_t* getData() const + { + return _dataEditing; + }; + + size_t getDataSize() const + { + return _sizeData; + } + + void applySettings([[maybe_unused]] const SettingsObject& settings) + { + } + +private: + const size_t _sizeData; // Size of '_data*' buffers + const uint8_t _pin; // output pin number + const T_CHANNEL _channel; // holds instance for multi channel support + + // Holds data stream which include LED color values and other settings as needed + uint8_t* _dataEditing; // exposed for get and set + uint8_t* _dataSending; // used for async send using RMT + + + void construct() + { + _dataEditing = static_cast(malloc(_sizeData)); + // data cleared later in Begin() + + _dataSending = static_cast(malloc(_sizeData)); + // no need to initialize it, it gets overwritten on every send + } +}; + +// normal +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINWs2811Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINWs2812xMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINWs2816Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINWs2805Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINSk6812Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINTm1814Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINTm1829Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINTm1914Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINApa106Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINTx1812Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINGs1903Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHIN800KbpsMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHIN400KbpsMethod; +typedef NeoEsp32RmtHINWs2805Method NeoEsp32RmtHINWs2814Method; + +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Ws2811Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Ws2812xMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Ws2816Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Ws2805Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Sk6812Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Tm1814Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Tm1829Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Tm1914Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Apa106Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Tx1812Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Gs1903Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0800KbpsMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0400KbpsMethod; +typedef NeoEsp32RmtHI0Ws2805Method NeoEsp32RmtHI0Ws2814Method; + +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Ws2811Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Ws2812xMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Ws2816Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Ws2805Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Sk6812Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Tm1814Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Tm1829Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Tm1914Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Apa106Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Tx1812Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Gs1903Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1800KbpsMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1400KbpsMethod; +typedef NeoEsp32RmtHI1Ws2805Method NeoEsp32RmtHI1Ws2814Method; + +#if !defined(CONFIG_IDF_TARGET_ESP32C3) + +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Ws2811Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Ws2812xMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Ws2816Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Ws2805Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Sk6812Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Tm1814Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Tm1829Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Tm1914Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Apa106Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Tx1812Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Gs1903Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2800KbpsMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2400KbpsMethod; +typedef NeoEsp32RmtHI2Ws2805Method NeoEsp32RmtHI2Ws2814Method; + +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Ws2811Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Ws2812xMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Ws2816Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Ws2805Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Sk6812Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Tm1814Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Tm1829Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Tm1914Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Apa106Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Tx1812Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Gs1903Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3800KbpsMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3400KbpsMethod; +typedef NeoEsp32RmtHI3Ws2805Method NeoEsp32RmtHI3Ws2814Method; + +#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3) + +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Ws2811Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Ws2812xMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Ws2816Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Ws2805Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Sk6812Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Tm1814Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Tm1829Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Tm1914Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Apa106Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Tx1812Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Gs1903Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4800KbpsMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4400KbpsMethod; +typedef NeoEsp32RmtHI4Ws2805Method NeoEsp32RmtHI4Ws2814Method; + +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Ws2811Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Ws2812xMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Ws2816Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Ws2805Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Sk6812Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Tm1814Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Tm1829Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Tm1914Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Apa106Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Tx1812Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Gs1903Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5800KbpsMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5400KbpsMethod; +typedef NeoEsp32RmtHI5Ws2805Method NeoEsp32RmtHI5Ws2814Method; + +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Ws2811Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Ws2812xMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Ws2816Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Ws2805Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Sk6812Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Tm1814Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Tm1829Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Tm1914Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Apa106Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Tx1812Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Gs1903Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6800KbpsMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6400KbpsMethod; +typedef NeoEsp32RmtHI6Ws2805Method NeoEsp32RmtHI6Ws2814Method; + +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Ws2811Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Ws2812xMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Ws2816Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Ws2805Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Sk6812Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Tm1814Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Tm1829Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Tm1914Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Apa106Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Tx1812Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Gs1903Method; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7800KbpsMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7400KbpsMethod; +typedef NeoEsp32RmtHI7Ws2805Method NeoEsp32RmtHI7Ws2814Method; + +#endif // !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3) +#endif // !defined(CONFIG_IDF_TARGET_ESP32C3) + +// inverted +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINWs2811InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINWs2812xInvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINWs2816InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINWs2805InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINSk6812InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINTm1814InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINTm1829InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINTm1914InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINApa106InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINTx1812InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINGs1903InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHIN800KbpsInvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHIN400KbpsInvertedMethod; +typedef NeoEsp32RmtHINWs2805InvertedMethod NeoEsp32RmtHINWs2814InvertedMethod; + +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Ws2811InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Ws2812xInvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Ws2816InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Ws2805InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Sk6812InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Tm1814InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Tm1829InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Tm1914InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Apa106InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Tx1812InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Gs1903InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0800KbpsInvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0400KbpsInvertedMethod; +typedef NeoEsp32RmtHI0Ws2805InvertedMethod NeoEsp32RmtHI0Ws2814InvertedMethod; + +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Ws2811InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Ws2812xInvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Ws2816InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Ws2805InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Sk6812InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Tm1814InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Tm1829InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Tm1914InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Apa106InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Tx1812InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Gs1903InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1800KbpsInvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1400KbpsInvertedMethod; +typedef NeoEsp32RmtHI1Ws2805InvertedMethod NeoEsp32RmtHI1Ws2814InvertedMethod; + +#if !defined(CONFIG_IDF_TARGET_ESP32C3) + +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Ws2811InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Ws2812xInvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Ws2816InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Ws2805InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Sk6812InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Tm1814InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Tm1829InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Tm1914InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Apa106InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Tx1812InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Gs1903InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2800KbpsInvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2400KbpsInvertedMethod; +typedef NeoEsp32RmtHI2Ws2805InvertedMethod NeoEsp32RmtHI2Ws2814InvertedMethod; + +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Ws2811InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Ws2812xInvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Ws2805InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Ws2816InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Sk6812InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Tm1814InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Tm1829InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Tm1914InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Apa106InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Tx1812InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Gs1903InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3800KbpsInvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3400KbpsInvertedMethod; +typedef NeoEsp32RmtHI3Ws2805InvertedMethod NeoEsp32RmtHI3Ws2814InvertedMethod; + +#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3) + +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Ws2811InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Ws2812xInvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Ws2816InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Ws2805InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Sk6812InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Tm1814InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Tm1829InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Tm1914InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Apa106InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Tx1812InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Gs1903InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4800KbpsInvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4400KbpsInvertedMethod; +typedef NeoEsp32RmtHI4Ws2805InvertedMethod NeoEsp32RmtHI4Ws2814InvertedMethod; + +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Ws2811InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Ws2812xInvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Ws2816InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Ws2805InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Sk6812InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Tm1814InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Tm1829InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Tm1914InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Apa106InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Tx1812InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Gs1903InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5800KbpsInvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5400KbpsInvertedMethod; +typedef NeoEsp32RmtHI5Ws2805InvertedMethod NeoEsp32RmtHI5Ws2814InvertedMethod; + +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Ws2811InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Ws2812xInvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Ws2816InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Ws2805InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Sk6812InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Tm1814InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Tm1829InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Tm1914InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Apa106InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Tx1812InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Gs1903InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6800KbpsInvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6400KbpsInvertedMethod; +typedef NeoEsp32RmtHI6Ws2805InvertedMethod NeoEsp32RmtHI6Ws2814InvertedMethod; + +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Ws2811InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Ws2812xInvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Ws2816InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Ws2805InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Sk6812InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Tm1814InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Tm1829InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Tm1914InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Apa106InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Tx1812InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Gs1903InvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7800KbpsInvertedMethod; +typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7400KbpsInvertedMethod; +typedef NeoEsp32RmtHI7Ws2805InvertedMethod NeoEsp32RmtHI7Ws2814InvertedMethod; + +#endif // !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3) +#endif // !defined(CONFIG_IDF_TARGET_ESP32C3) + +#endif diff --git a/lib/NeoESP32RmtHI/library.json b/lib/NeoESP32RmtHI/library.json new file mode 100644 index 0000000000..0608e59e12 --- /dev/null +++ b/lib/NeoESP32RmtHI/library.json @@ -0,0 +1,12 @@ +{ + "name": "NeoESP32RmtHI", + "build": { "libArchive": false }, + "platforms": ["espressif32"], + "dependencies": [ + { + "owner": "makuna", + "name": "NeoPixelBus", + "version": "^2.8.3" + } + ] +} diff --git a/lib/NeoESP32RmtHI/src/NeoEsp32RmtHI.S b/lib/NeoESP32RmtHI/src/NeoEsp32RmtHI.S new file mode 100644 index 0000000000..0c60d2ebc9 --- /dev/null +++ b/lib/NeoESP32RmtHI/src/NeoEsp32RmtHI.S @@ -0,0 +1,263 @@ +/* RMT ISR shim + * Bridges from a high-level interrupt to the C++ code. + * + * This code is largely derived from Espressif's 'hli_vector.S' Bluetooth ISR. + * + */ + +#if defined(__XTENSA__) && defined(ESP32) && !defined(CONFIG_BTDM_CTRL_HLI) + +#include +#include "sdkconfig.h" +#include "soc/soc.h" + +/* If the Bluetooth driver has hooked the high-priority interrupt, we piggyback on it and don't need this. */ +#ifndef CONFIG_BTDM_CTRL_HLI + +/* + Select interrupt based on system check level + - Base ESP32: could be 4 or 5, depends on platform config + - S2: 5 + - S3: 5 +*/ + +#if CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 +/* Use level 4 */ +#define RFI_X 4 +#define xt_highintx xt_highint4 +#else /* !CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */ +/* Use level 5 */ +#define RFI_X 5 +#define xt_highintx xt_highint5 +#endif /* CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */ + +// Register map, based on interrupt level +#define EPC_X (EPC + RFI_X) +#define EXCSAVE_X (EXCSAVE + RFI_X) + +// The sp mnemonic is used all over in ESP's assembly, though I'm not sure where it's expected to be defined? +#define sp a1 + +/* Interrupt stack size, for C code. */ +#define RMT_INTR_STACK_SIZE 512 + +/* Save area for the CPU state: + * - 64 words for the general purpose registers + * - 7 words for some of the special registers: + * - WINDOWBASE, WINDOWSTART — only WINDOWSTART is truly needed + * - SAR, LBEG, LEND, LCOUNT — since the C code might use these + * - EPC1 — since the C code might cause window overflow exceptions + * This is not laid out as standard exception frame structure + * for simplicity of the save/restore code. + */ +#define REG_FILE_SIZE (64 * 4) +#define SPECREG_OFFSET REG_FILE_SIZE +#define SPECREG_SIZE (7 * 4) +#define REG_SAVE_AREA_SIZE (SPECREG_OFFSET + SPECREG_SIZE) + + .data +_rmt_intr_stack: + .space RMT_INTR_STACK_SIZE +_rmt_save_ctx: + .space REG_SAVE_AREA_SIZE + + .section .iram1,"ax" + .global xt_highintx + .type xt_highintx,@function + .align 4 + +xt_highintx: + + movi a0, _rmt_save_ctx + /* save 4 lower registers */ + s32i a1, a0, 4 + s32i a2, a0, 8 + s32i a3, a0, 12 + rsr a2, EXCSAVE_X /* holds the value of a0 */ + s32i a2, a0, 0 + + /* Save special registers */ + addi a0, a0, SPECREG_OFFSET + rsr a2, WINDOWBASE + s32i a2, a0, 0 + rsr a2, WINDOWSTART + s32i a2, a0, 4 + rsr a2, SAR + s32i a2, a0, 8 + #if XCHAL_HAVE_LOOPS + rsr a2, LBEG + s32i a2, a0, 12 + rsr a2, LEND + s32i a2, a0, 16 + rsr a2, LCOUNT + s32i a2, a0, 20 + #endif + rsr a2, EPC1 + s32i a2, a0, 24 + + /* disable exception mode, window overflow */ + movi a0, PS_INTLEVEL(RFI_X+1) | PS_EXCM + wsr a0, PS + rsync + + /* Save the remaining physical registers. + * 4 registers are already saved, which leaves 60 registers to save. + * (FIXME: consider the case when the CPU is configured with physical 32 registers) + * These 60 registers are saved in 5 iterations, 12 registers at a time. + */ + movi a1, 5 + movi a3, _rmt_save_ctx + 4 * 4 + + /* This is repeated 5 times, each time the window is shifted by 12 registers. + * We come here with a1 = downcounter, a3 = save pointer, a2 and a0 unused. + */ +1: + s32i a4, a3, 0 + s32i a5, a3, 4 + s32i a6, a3, 8 + s32i a7, a3, 12 + s32i a8, a3, 16 + s32i a9, a3, 20 + s32i a10, a3, 24 + s32i a11, a3, 28 + s32i a12, a3, 32 + s32i a13, a3, 36 + s32i a14, a3, 40 + s32i a15, a3, 44 + + /* We are about to rotate the window, so that a12-a15 will become the new a0-a3. + * Copy a0-a3 to a12-15 to still have access to these values. + * At the same time we can decrement the counter and adjust the save area pointer + */ + + /* a0 is constant (_rmt_save_ctx), no need to copy */ + addi a13, a1, -1 /* copy and decrement the downcounter */ + /* a2 is scratch so no need to copy */ + addi a15, a3, 48 /* copy and adjust the save area pointer */ + beqz a13, 2f /* have saved all registers ? */ + rotw 3 /* rotate the window and go back */ + j 1b + + /* the loop is complete */ +2: + rotw 4 /* this brings us back to the original window */ + /* a0 still points to _rmt_save_ctx */ + + /* Can clear WINDOWSTART now, all registers are saved */ + rsr a2, WINDOWBASE + /* WINDOWSTART = (1 << WINDOWBASE) */ + movi a3, 1 + ssl a2 + sll a3, a3 + wsr a3, WINDOWSTART + +_highint_stack_switch: + movi a0, 0 + movi sp, _rmt_intr_stack + RMT_INTR_STACK_SIZE - 16 + s32e a0, sp, -12 /* For GDB: set null SP */ + s32e a0, sp, -16 /* For GDB: set null PC */ + movi a0, _highint_stack_switch /* For GDB: cosmetics, for the frame where stack switch happened */ + + /* Set up PS for C, disable all interrupts except NMI and debug, and clear EXCM. */ + movi a6, PS_INTLEVEL(RFI_X) | PS_UM | PS_WOE + wsr a6, PS + rsync + + /* Call C handler */ + mov a6, sp + call4 NeoEsp32RmtMethodIsr + + l32e sp, sp, -12 /* switch back to the original stack */ + + /* Done with C handler; re-enable exception mode, disabling window overflow */ + movi a2, PS_INTLEVEL(RFI_X+1) | PS_EXCM /* TOCHECK */ + wsr a2, PS + rsync + + /* Restore the special registers. + * WINDOWSTART will be restored near the end. + */ + movi a0, _rmt_save_ctx + SPECREG_OFFSET + l32i a2, a0, 8 + wsr a2, SAR + #if XCHAL_HAVE_LOOPS + l32i a2, a0, 12 + wsr a2, LBEG + l32i a2, a0, 16 + wsr a2, LEND + l32i a2, a0, 20 + wsr a2, LCOUNT + #endif + l32i a2, a0, 24 + wsr a2, EPC1 + + /* Restoring the physical registers. + * This is the reverse to the saving process above. + */ + + /* Rotate back to the final window, then start loading 12 registers at a time, + * in 5 iterations. + * Again, a1 is the downcounter and a3 is the save area pointer. + * After each rotation, a1 and a3 are copied from a13 and a15. + * To simplify the loop, we put the initial values into a13 and a15. + */ + rotw -4 + movi a15, _rmt_save_ctx + 64 * 4 /* point to the end of the save area */ + movi a13, 5 + +1: + /* Copy a1 and a3 from their previous location, + * at the same time decrementing and adjusting the save area pointer. + */ + addi a1, a13, -1 + addi a3, a15, -48 + + /* Load 12 registers */ + l32i a4, a3, 0 + l32i a5, a3, 4 + l32i a6, a3, 8 + l32i a7, a3, 12 + l32i a8, a3, 16 + l32i a9, a3, 20 + l32i a10, a3, 24 + l32i a11, a3, 28 /* ensure PS and EPC written */ + l32i a12, a3, 32 + l32i a13, a3, 36 + l32i a14, a3, 40 + l32i a15, a3, 44 + + /* Done with the loop? */ + beqz a1, 2f + /* If no, rotate the window and repeat */ + rotw -3 + j 1b + +2: + /* Done with the loop. Only 4 registers (a0-a3 in the original window) remain + * to be restored. Also need to restore WINDOWSTART, since all the general + * registers are now in place. + */ + movi a0, _rmt_save_ctx + + l32i a2, a0, SPECREG_OFFSET + 4 + wsr a2, WINDOWSTART + + l32i a1, a0, 4 + l32i a2, a0, 8 + l32i a3, a0, 12 + rsr a0, EXCSAVE_X /* holds the value of a0 before the interrupt handler */ + + /* Return from the interrupt, restoring PS from EPS_X */ + rfi RFI_X + + +/* The linker has no reason to link in this file; all symbols it exports are already defined + (weakly!) in the default int handler. Define a symbol here so we can use it to have the + linker inspect this anyway. */ + + .global ld_include_hli_vectors_rmt +ld_include_hli_vectors_rmt: + + +#endif // CONFIG_BTDM_CTRL_HLI +#endif // XTensa \ No newline at end of file diff --git a/lib/NeoESP32RmtHI/src/NeoEsp32RmtHIMethod.cpp b/lib/NeoESP32RmtHI/src/NeoEsp32RmtHIMethod.cpp new file mode 100644 index 0000000000..8353201f08 --- /dev/null +++ b/lib/NeoESP32RmtHI/src/NeoEsp32RmtHIMethod.cpp @@ -0,0 +1,507 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for Esp32. + +A BIG thanks to Andreas Merkle for the investigation and implementation of +a workaround to the GCC bug that drops method attributes from template methods + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by donating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#include + +#if defined(ARDUINO_ARCH_ESP32) + +#include +#include "esp_idf_version.h" +#include "NeoEsp32RmtHIMethod.h" +#include "soc/soc.h" +#include "soc/rmt_reg.h" + +#ifdef __riscv +#include "riscv/interrupt.h" +#endif + + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) +#include "hal/rmt_ll.h" +#else +/* Shims for older ESP-IDF v3; we can safely assume original ESP32 */ +#include "soc/rmt_struct.h" + +// Selected RMT API functions borrowed from ESP-IDF v4.4.8 +// components/hal/esp32/include/hal/rmt_ll.h +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +__attribute__((always_inline)) +static inline void rmt_ll_tx_reset_pointer(rmt_dev_t *dev, uint32_t channel) +{ + dev->conf_ch[channel].conf1.mem_rd_rst = 1; + dev->conf_ch[channel].conf1.mem_rd_rst = 0; +} + +__attribute__((always_inline)) +static inline void rmt_ll_tx_start(rmt_dev_t *dev, uint32_t channel) +{ + dev->conf_ch[channel].conf1.tx_start = 1; +} + +__attribute__((always_inline)) +static inline void rmt_ll_tx_stop(rmt_dev_t *dev, uint32_t channel) +{ + RMTMEM.chan[channel].data32[0].val = 0; + dev->conf_ch[channel].conf1.tx_start = 0; + dev->conf_ch[channel].conf1.mem_rd_rst = 1; + dev->conf_ch[channel].conf1.mem_rd_rst = 0; +} + +__attribute__((always_inline)) +static inline void rmt_ll_tx_enable_pingpong(rmt_dev_t *dev, uint32_t channel, bool enable) +{ + dev->apb_conf.mem_tx_wrap_en = enable; +} + +__attribute__((always_inline)) +static inline void rmt_ll_tx_enable_loop(rmt_dev_t *dev, uint32_t channel, bool enable) +{ + dev->conf_ch[channel].conf1.tx_conti_mode = enable; +} + +__attribute__((always_inline)) +static inline uint32_t rmt_ll_tx_get_channel_status(rmt_dev_t *dev, uint32_t channel) +{ + return dev->status_ch[channel]; +} + +__attribute__((always_inline)) +static inline void rmt_ll_tx_set_limit(rmt_dev_t *dev, uint32_t channel, uint32_t limit) +{ + dev->tx_lim_ch[channel].limit = limit; +} + +__attribute__((always_inline)) +static inline void rmt_ll_enable_interrupt(rmt_dev_t *dev, uint32_t mask, bool enable) +{ + if (enable) { + dev->int_ena.val |= mask; + } else { + dev->int_ena.val &= ~mask; + } +} + +__attribute__((always_inline)) +static inline void rmt_ll_enable_tx_end_interrupt(rmt_dev_t *dev, uint32_t channel, bool enable) +{ + dev->int_ena.val &= ~(1 << (channel * 3)); + dev->int_ena.val |= (enable << (channel * 3)); +} + +__attribute__((always_inline)) +static inline void rmt_ll_enable_tx_err_interrupt(rmt_dev_t *dev, uint32_t channel, bool enable) +{ + dev->int_ena.val &= ~(1 << (channel * 3 + 2)); + dev->int_ena.val |= (enable << (channel * 3 + 2)); +} + +__attribute__((always_inline)) +static inline void rmt_ll_enable_tx_thres_interrupt(rmt_dev_t *dev, uint32_t channel, bool enable) +{ + dev->int_ena.val &= ~(1 << (channel + 24)); + dev->int_ena.val |= (enable << (channel + 24)); +} + +__attribute__((always_inline)) +static inline void rmt_ll_clear_tx_end_interrupt(rmt_dev_t *dev, uint32_t channel) +{ + dev->int_clr.val = (1 << (channel * 3)); +} + +__attribute__((always_inline)) +static inline void rmt_ll_clear_tx_err_interrupt(rmt_dev_t *dev, uint32_t channel) +{ + dev->int_clr.val = (1 << (channel * 3 + 2)); +} + +__attribute__((always_inline)) +static inline void rmt_ll_clear_tx_thres_interrupt(rmt_dev_t *dev, uint32_t channel) +{ + dev->int_clr.val = (1 << (channel + 24)); +} + + +__attribute__((always_inline)) +static inline uint32_t rmt_ll_get_tx_thres_interrupt_status(rmt_dev_t *dev) +{ + uint32_t status = dev->int_st.val; + return (status & 0xFF000000) >> 24; +} +#endif + + +// ********************************* +// Select method for binding interrupt +// +// - If the Bluetooth driver has registered a high-level interrupt, piggyback on that API +// - If we're on a modern core, allocate the interrupt with the API (old cores are bugged) +// - Otherwise use the low-level hardware API to manually bind the interrupt + + +#if defined(CONFIG_BTDM_CTRL_HLI) +// Espressif's bluetooth driver offers a helpful sharing layer; bring in the interrupt management calls +#include "hal/interrupt_controller_hal.h" +extern "C" esp_err_t hli_intr_register(intr_handler_t handler, void* arg, uint32_t intr_reg, uint32_t intr_mask); + +#else /* !CONFIG_BTDM_CTRL_HLI*/ + +// Declare the our high-priority ISR handler +extern "C" void ld_include_hli_vectors_rmt(); // an object with an address, but no space + +#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3) +#include "soc/periph_defs.h" +#endif + +// Select level flag +#if defined(__riscv) +// RISCV chips don't block interrupts while scheduling; all we need to do is be higher than the WiFi ISR +#define INT_LEVEL_FLAG ESP_INTR_FLAG_LEVEL3 +#elif defined(CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5) +#define INT_LEVEL_FLAG ESP_INTR_FLAG_LEVEL4 +#else +#define INT_LEVEL_FLAG ESP_INTR_FLAG_LEVEL5 +#endif + +// ESP-IDF v3 cannot enable high priority interrupts through the API at all; +// and ESP-IDF v4 on XTensa cannot enable Level 5 due to incorrect interrupt descriptor tables +#if !defined(__XTENSA__) || (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) || ((ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) && CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5)) +#define NEOESP32_RMT_CAN_USE_INTR_ALLOC + +// XTensa cores require the assembly bridge +#ifdef __XTENSA__ +#define HI_IRQ_HANDLER nullptr +#define HI_IRQ_HANDLER_ARG ld_include_hli_vectors_rmt +#else +#define HI_IRQ_HANDLER NeoEsp32RmtMethodIsr +#define HI_IRQ_HANDLER_ARG nullptr +#endif + +#else +/* !CONFIG_BTDM_CTRL_HLI && !NEOESP32_RMT_CAN_USE_INTR_ALLOC */ +// This is the index of the LV5 interrupt vector - see interrupt descriptor table in idf components/hal/esp32/interrupt_descriptor_table.c +#define ESP32_LV5_IRQ_INDEX 26 + +#endif /* NEOESP32_RMT_CAN_USE_INTR_ALLOC */ +#endif /* CONFIG_BTDM_CTRL_HLI */ + + +// RMT driver implementation +struct NeoEsp32RmtHIChannelState { + uint32_t rmtBit0, rmtBit1; + uint32_t resetDuration; + + const byte* txDataStart; // data array + const byte* txDataEnd; // one past end + const byte* txDataCurrent; // current location + size_t rmtOffset; +}; + +// Global variables +#if defined(NEOESP32_RMT_CAN_USE_INTR_ALLOC) +static intr_handle_t isrHandle = nullptr; +#endif + +static NeoEsp32RmtHIChannelState** driverState = nullptr; +constexpr size_t rmtBatchSize = RMT_MEM_ITEM_NUM / 2; + +// Fill the RMT buffer memory +// This is implemented using many arguments instead of passing the structure object to ensure we do only one lookup +// All the arguments are passed in registers, so they don't need to be looked up again +static void IRAM_ATTR RmtFillBuffer(uint8_t channel, const byte** src_ptr, const byte* end, uint32_t bit0, uint32_t bit1, size_t* offset_ptr, size_t reserve) { + // We assume that (rmtToWrite % 8) == 0 + size_t rmtToWrite = rmtBatchSize - reserve; + rmt_item32_t* dest =(rmt_item32_t*) &RMTMEM.chan[channel].data32[*offset_ptr + reserve]; // write directly in to RMT memory + const byte* psrc = *src_ptr; + + *offset_ptr ^= rmtBatchSize; + + if (psrc != end) { + while (rmtToWrite > 0) { + uint8_t data = *psrc; + for (uint8_t bit = 0; bit < 8; bit++) + { + dest->val = (data & 0x80) ? bit1 : bit0; + dest++; + data <<= 1; + } + rmtToWrite -= 8; + psrc++; + + if (psrc == end) { + break; + } + } + + *src_ptr = psrc; + } + + if (rmtToWrite > 0) { + // Add end event + rmt_item32_t bit0_val = {{.val = bit0 }}; + *dest = rmt_item32_t {{{ .duration0 = 0, .level0 = bit0_val.level1, .duration1 = 0, .level1 = bit0_val.level1 }}}; + } +} + +static void IRAM_ATTR RmtStartWrite(uint8_t channel, NeoEsp32RmtHIChannelState& state) { + // Reset context state + state.rmtOffset = 0; + + // Fill the first part of the buffer with a reset event + // FUTURE: we could do timing analysis with the last interrupt on this channel + // Use 8 words to stay aligned with the buffer fill logic + rmt_item32_t bit0_val = {{.val = state.rmtBit0 }}; + rmt_item32_t fill = {{{ .duration0 = 100, .level0 = bit0_val.level1, .duration1 = 100, .level1 = bit0_val.level1 }}}; + rmt_item32_t* dest = (rmt_item32_t*) &RMTMEM.chan[channel].data32[0]; + for (auto i = 0; i < 7; ++i) dest[i] = fill; + fill.duration1 = state.resetDuration > 1400 ? (state.resetDuration - 1400) : 100; + dest[7] = fill; + + // Fill the remaining buffer with real data + RmtFillBuffer(channel, &state.txDataCurrent, state.txDataEnd, state.rmtBit0, state.rmtBit1, &state.rmtOffset, 8); + RmtFillBuffer(channel, &state.txDataCurrent, state.txDataEnd, state.rmtBit0, state.rmtBit1, &state.rmtOffset, 0); + + // Start operation + rmt_ll_clear_tx_thres_interrupt(&RMT, channel); + rmt_ll_tx_reset_pointer(&RMT, channel); + rmt_ll_tx_start(&RMT, channel); +} + +extern "C" void IRAM_ATTR NeoEsp32RmtMethodIsr(void *arg) { + // Tx threshold interrupt + uint32_t status = rmt_ll_get_tx_thres_interrupt_status(&RMT); + while (status) { + uint8_t channel = __builtin_ffs(status) - 1; + if (driverState[channel]) { + // Normal case + NeoEsp32RmtHIChannelState& state = *driverState[channel]; + RmtFillBuffer(channel, &state.txDataCurrent, state.txDataEnd, state.rmtBit0, state.rmtBit1, &state.rmtOffset, 0); + } else { + // Danger - another driver got invoked? + rmt_ll_tx_stop(&RMT, channel); + } + rmt_ll_clear_tx_thres_interrupt(&RMT, channel); + status = rmt_ll_get_tx_thres_interrupt_status(&RMT); + } +}; + +// Wrapper around the register analysis defines +// For all currently supported chips, this is constant for all channels; but this is not true of *all* ESP32 +static inline bool _RmtStatusIsTransmitting(rmt_channel_t channel, uint32_t status) { + uint32_t v; + switch(channel) { +#ifdef RMT_STATE_CH0 + case 0: v = (status >> RMT_STATE_CH0_S) & RMT_STATE_CH0_V; break; +#endif +#ifdef RMT_STATE_CH1 + case 1: v = (status >> RMT_STATE_CH1_S) & RMT_STATE_CH1_V; break; +#endif +#ifdef RMT_STATE_CH2 + case 2: v = (status >> RMT_STATE_CH2_S) & RMT_STATE_CH2_V; break; +#endif +#ifdef RMT_STATE_CH3 + case 3: v = (status >> RMT_STATE_CH3_S) & RMT_STATE_CH3_V; break; +#endif +#ifdef RMT_STATE_CH4 + case 4: v = (status >> RMT_STATE_CH4_S) & RMT_STATE_CH4_V; break; +#endif +#ifdef RMT_STATE_CH5 + case 5: v = (status >> RMT_STATE_CH5_S) & RMT_STATE_CH5_V; break; +#endif +#ifdef RMT_STATE_CH6 + case 6: v = (status >> RMT_STATE_CH6_S) & RMT_STATE_CH6_V; break; +#endif +#ifdef RMT_STATE_CH7 + case 7: v = (status >> RMT_STATE_CH7_S) & RMT_STATE_CH7_V; break; +#endif + default: v = 0; + } + + return v != 0; +} + + +esp_err_t NeoEsp32RmtHiMethodDriver::Install(rmt_channel_t channel, uint32_t rmtBit0, uint32_t rmtBit1, uint32_t reset) { + // Validate channel number + if (channel >= RMT_CHANNEL_MAX) { + return ESP_ERR_INVALID_ARG; + } + + esp_err_t err = ESP_OK; + if (!driverState) { + // First time init + driverState = reinterpret_cast(heap_caps_calloc(RMT_CHANNEL_MAX, sizeof(NeoEsp32RmtHIChannelState*), MALLOC_CAP_INTERNAL)); + if (!driverState) return ESP_ERR_NO_MEM; + + // Ensure all interrupts are cleared before binding + RMT.int_ena.val = 0; + RMT.int_clr.val = 0xFFFFFFFF; + + // Bind interrupt handler +#if defined(CONFIG_BTDM_CTRL_HLI) + // Bluetooth driver has taken the empty high-priority interrupt. Fortunately, it allows us to + // hook up another handler. + err = hli_intr_register(NeoEsp32RmtMethodIsr, nullptr, (uintptr_t) &RMT.int_st, 0xFF000000); + // 25 is the magic number of the bluetooth ISR on ESP32 - see soc/soc.h. + intr_matrix_set(cpu_hal_get_core_id(), ETS_RMT_INTR_SOURCE, 25); + intr_cntrl_ll_enable_interrupts(1<<25); +#elif defined(NEOESP32_RMT_CAN_USE_INTR_ALLOC) + // Use the platform code to allocate the interrupt + // If we need the additional assembly bridge, we pass it as the "arg" to the IDF so it gets linked in + err = esp_intr_alloc(ETS_RMT_INTR_SOURCE, INT_LEVEL_FLAG | ESP_INTR_FLAG_IRAM, HI_IRQ_HANDLER, (void*) HI_IRQ_HANDLER_ARG, &isrHandle); + //err = ESP_ERR_NOT_FINISHED; +#else + // Broken IDF API does not allow us to reserve the interrupt; do it manually + static volatile const void* __attribute__((used)) pleaseLinkAssembly = (void*) ld_include_hli_vectors_rmt; + intr_matrix_set(xPortGetCoreID(), ETS_RMT_INTR_SOURCE, ESP32_LV5_IRQ_INDEX); + ESP_INTR_ENABLE(ESP32_LV5_IRQ_INDEX); +#endif + + if (err != ESP_OK) { + heap_caps_free(driverState); + driverState = nullptr; + return err; + } + } + + if (driverState[channel] != nullptr) { + return ESP_ERR_INVALID_STATE; // already in use + } + + NeoEsp32RmtHIChannelState* state = reinterpret_cast(heap_caps_calloc(1, sizeof(NeoEsp32RmtHIChannelState), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)); + if (state == nullptr) { + return ESP_ERR_NO_MEM; + } + + // Store timing information + state->rmtBit0 = rmtBit0; + state->rmtBit1 = rmtBit1; + state->resetDuration = reset; + + // Initialize hardware + rmt_ll_tx_stop(&RMT, channel); + rmt_ll_tx_reset_pointer(&RMT, channel); + rmt_ll_enable_tx_err_interrupt(&RMT, channel, false); + rmt_ll_enable_tx_end_interrupt(&RMT, channel, false); + rmt_ll_enable_tx_thres_interrupt(&RMT, channel, false); + rmt_ll_clear_tx_err_interrupt(&RMT, channel); + rmt_ll_clear_tx_end_interrupt(&RMT, channel); + rmt_ll_clear_tx_thres_interrupt(&RMT, channel); + + rmt_ll_tx_enable_loop(&RMT, channel, false); + rmt_ll_tx_enable_pingpong(&RMT, channel, true); + rmt_ll_tx_set_limit(&RMT, channel, rmtBatchSize); + + driverState[channel] = state; + + rmt_ll_enable_tx_thres_interrupt(&RMT, channel, true); + + return err; +} + +esp_err_t NeoEsp32RmtHiMethodDriver::Uninstall(rmt_channel_t channel) { + if ((channel >= RMT_CHANNEL_MAX) || !driverState || !driverState[channel]) return ESP_ERR_INVALID_ARG; + + NeoEsp32RmtHIChannelState* state = driverState[channel]; + + WaitForTxDone(channel, 10000 / portTICK_PERIOD_MS); + + // Done or not, we're out of here + rmt_ll_tx_stop(&RMT, channel); + rmt_ll_enable_tx_thres_interrupt(&RMT, channel, false); + driverState[channel] = nullptr; + heap_caps_free(state); + +#if !defined(CONFIG_BTDM_CTRL_HLI) /* Cannot unbind from bluetooth ISR */ + // Turn off the driver ISR and release global state if none are left + for (uint8_t channelIndex = 0; channelIndex < RMT_CHANNEL_MAX; ++channelIndex) { + if (driverState[channelIndex]) return ESP_OK; // done + } + +#if defined(NEOESP32_RMT_CAN_USE_INTR_ALLOC) + esp_intr_free(isrHandle); +#else + ESP_INTR_DISABLE(ESP32_LV5_IRQ_INDEX); +#endif + + heap_caps_free(driverState); + driverState = nullptr; +#endif /* !defined(CONFIG_BTDM_CTRL_HLI) */ + + return ESP_OK; +} + +esp_err_t NeoEsp32RmtHiMethodDriver::Write(rmt_channel_t channel, const uint8_t *src, size_t src_size) { + if ((channel >= RMT_CHANNEL_MAX) || !driverState || !driverState[channel]) return ESP_ERR_INVALID_ARG; + + NeoEsp32RmtHIChannelState& state = *driverState[channel]; + esp_err_t result = WaitForTxDone(channel, 10000 / portTICK_PERIOD_MS); + + if (result == ESP_OK) { + state.txDataStart = src; + state.txDataCurrent = src; + state.txDataEnd = src + src_size; + RmtStartWrite(channel, state); + } + return result; +} + +esp_err_t NeoEsp32RmtHiMethodDriver::WaitForTxDone(rmt_channel_t channel, TickType_t wait_time) { + if ((channel >= RMT_CHANNEL_MAX) || !driverState || !driverState[channel]) return ESP_ERR_INVALID_ARG; + + NeoEsp32RmtHIChannelState& state = *driverState[channel]; + // yield-wait until wait_time + esp_err_t rv = ESP_OK; + uint32_t status; + while(1) { + status = rmt_ll_tx_get_channel_status(&RMT, channel); + if (!_RmtStatusIsTransmitting(channel, status)) break; + if (wait_time == 0) { rv = ESP_ERR_TIMEOUT; break; }; + + TickType_t sleep = std::min(wait_time, (TickType_t) 5); + vTaskDelay(sleep); + wait_time -= sleep; + }; + + return rv; +} + +#endif \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 0f73bff0c4..2b8eb0a2f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,21 @@ { "name": "wled", - "version": "0.15.0-b7", + "version": "0.15.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "wled", - "version": "0.15.0-b7", + "version": "0.15.3", "license": "ISC", "dependencies": { "clean-css": "^5.3.3", "html-minifier-terser": "^7.2.0", "inliner": "^1.13.1", - "nodemon": "^3.0.2" + "nodemon": "^3.1.7" + }, + "engines": { + "node": ">=20.0.0" } }, "node_modules/@jridgewell/gen-mapping": { diff --git a/package.json b/package.json index a11a135539..68369b8f8a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wled", - "version": "0.15.0-b7", + "version": "0.15.3", "description": "Tools for WLED project", "main": "tools/cdata.js", "directories": { diff --git a/pio-scripts/build_ui.py b/pio-scripts/build_ui.py index f3688a5d4a..047fac442c 100644 --- a/pio-scripts/build_ui.py +++ b/pio-scripts/build_ui.py @@ -1,3 +1,21 @@ -Import('env') +Import("env") +import shutil -env.Execute("npm run build") \ No newline at end of file +node_ex = shutil.which("node") +# Check if Node.js is installed and present in PATH if it failed, abort the build +if node_ex is None: + print('\x1b[0;31;43m' + 'Node.js is not installed or missing from PATH html css js will not be processed check https://kno.wled.ge/advanced/compiling-wled/' + '\x1b[0m') + exitCode = env.Execute("null") + exit(exitCode) +else: + # Install the necessary node packages for the pre-build asset bundling script + print('\x1b[6;33;42m' + 'Installing node packages' + '\x1b[0m') + env.Execute("npm install") + + # Call the bundling script + exitCode = env.Execute("npm run build") + + # If it failed, abort the build + if (exitCode): + print('\x1b[0;31;43m' + 'npm run build fails check https://kno.wled.ge/advanced/compiling-wled/' + '\x1b[0m') + exit(exitCode) \ No newline at end of file diff --git a/pio-scripts/output_bins.py b/pio-scripts/output_bins.py index 4d1594d843..d369ed7927 100644 --- a/pio-scripts/output_bins.py +++ b/pio-scripts/output_bins.py @@ -2,6 +2,7 @@ import os import shutil import gzip +import json OUTPUT_DIR = "build_output{}".format(os.path.sep) #OUTPUT_DIR = os.path.join("build_output") @@ -22,7 +23,8 @@ def create_release(source): release_name_def = _get_cpp_define_value(env, "WLED_RELEASE_NAME") if release_name_def: release_name = release_name_def.replace("\\\"", "") - version = _get_cpp_define_value(env, "WLED_VERSION") + with open("package.json", "r") as package: + version = json.load(package)["version"] release_file = os.path.join(OUTPUT_DIR, "release", f"WLED_{version}_{release_name}.bin") release_gz_file = release_file + ".gz" print(f"Copying {source} to {release_file}") diff --git a/pio-scripts/set_metadata.py b/pio-scripts/set_metadata.py new file mode 100644 index 0000000000..7c8c223038 --- /dev/null +++ b/pio-scripts/set_metadata.py @@ -0,0 +1,116 @@ +Import('env') +import subprocess +import json +import re + +def get_github_repo(): + """Extract GitHub repository name from git remote URL. + + Uses the remote that the current branch tracks, falling back to 'origin'. + This handles cases where repositories have multiple remotes or where the + main remote is not named 'origin'. + + Returns: + str: Repository name in 'owner/repo' format for GitHub repos, + 'unknown' for non-GitHub repos, missing git CLI, or any errors. + """ + try: + remote_name = 'origin' # Default fallback + + # Try to get the remote for the current branch + try: + # Get current branch name + branch_result = subprocess.run(['git', 'rev-parse', '--abbrev-ref', 'HEAD'], + capture_output=True, text=True, check=True) + current_branch = branch_result.stdout.strip() + + # Get the remote for the current branch + remote_result = subprocess.run(['git', 'config', f'branch.{current_branch}.remote'], + capture_output=True, text=True, check=True) + tracked_remote = remote_result.stdout.strip() + + # Use the tracked remote if we found one + if tracked_remote: + remote_name = tracked_remote + except subprocess.CalledProcessError: + # If branch config lookup fails, continue with 'origin' as fallback + pass + + # Get the remote URL for the determined remote + result = subprocess.run(['git', 'remote', 'get-url', remote_name], + capture_output=True, text=True, check=True) + remote_url = result.stdout.strip() + + # Check if it's a GitHub URL + if 'github.com' not in remote_url.lower(): + return None + + # Parse GitHub URL patterns: + # https://github.com/owner/repo.git + # git@github.com:owner/repo.git + # https://github.com/owner/repo + + # Remove .git suffix if present + if remote_url.endswith('.git'): + remote_url = remote_url[:-4] + + # Handle HTTPS URLs + https_match = re.search(r'github\.com/([^/]+/[^/]+)', remote_url, re.IGNORECASE) + if https_match: + return https_match.group(1) + + # Handle SSH URLs + ssh_match = re.search(r'github\.com:([^/]+/[^/]+)', remote_url, re.IGNORECASE) + if ssh_match: + return ssh_match.group(1) + + return None + + except FileNotFoundError: + # Git CLI is not installed or not in PATH + return None + except subprocess.CalledProcessError: + # Git command failed (e.g., not a git repo, no remote, etc.) + return None + except Exception: + # Any other unexpected error + return None + +# WLED version is managed by package.json; this is picked up in several places +# - It's integrated in to the UI code +# - Here, for wled_metadata.cpp +# - The output_bins script +# We always take it from package.json to ensure consistency +with open("package.json", "r") as package: + WLED_VERSION = json.load(package)["version"] + +def has_def(cppdefs, name): + """ Returns true if a given name is set in a CPPDEFINES collection """ + for f in cppdefs: + if isinstance(f, tuple): + f = f[0] + if f == name: + return True + return False + + +def add_wled_metadata_flags(env, node): + cdefs = env["CPPDEFINES"].copy() + + if not has_def(cdefs, "WLED_REPO"): + repo = get_github_repo() + if repo: + cdefs.append(("WLED_REPO", f"\\\"{repo}\\\"")) + + cdefs.append(("WLED_VERSION", WLED_VERSION)) + + # This transforms the node in to a Builder; it cannot be modified again + return env.Object( + node, + CPPDEFINES=cdefs + ) + +env.AddBuildMiddleware( + add_wled_metadata_flags, + "*/wled_metadata.cpp" +) diff --git a/pio-scripts/set_version.py b/pio-scripts/set_version.py deleted file mode 100644 index 1d8e076ea8..0000000000 --- a/pio-scripts/set_version.py +++ /dev/null @@ -1,8 +0,0 @@ -Import('env') -import json - -PACKAGE_FILE = "package.json" - -with open(PACKAGE_FILE, "r") as package: - version = json.load(package)["version"] - env.Append(BUILD_FLAGS=[f"-DWLED_VERSION={version}"]) diff --git a/platformio.ini b/platformio.ini index fe02213ff2..4164a4c186 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,343 +1,212 @@ ; PlatformIO Project Configuration File -; Please visit documentation: https://docs.platformio.org/page/projectconf.html +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html [platformio] -# ------------------------------------------------------------------------------ -# ENVIRONMENTS -# -# Please uncomment one of the lines below to select your board(s) -# (use `platformio_override.ini` when building for your own board; see `platformio_override.ini.sample` for an example) -# ------------------------------------------------------------------------------ - -# CI/release binaries -default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, nodemcuv2_160, esp8266_2m_160, esp01_1m_full_160, nodemcuv2_compat, esp8266_2m_compat, esp01_1m_full_compat, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_16MB_opi, esp32s3dev_8MB_opi, esp32s3_4M_qspi, esp32_wrover - -src_dir = ./wled00 +default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, nodemcuv2_160, esp8266_2m_160, esp01_1m_full_160, nodemcuv2_compat, esp8266_2m_compat, esp01_1m_full_compat, esp32dev, esp32dev_V4, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_16MB_opi, esp32s3dev_8MB_opi, esp32s3_4M_qspi, esp32_wrover +src_dir = ./wled00 data_dir = ./wled00/data build_cache_dir = ~/.buildcache -extra_configs = - platformio_override.ini +extra_configs = + platformio_override.ini [common] -# ------------------------------------------------------------------------------ -# PLATFORM: -# !! DO NOT confuse platformio's ESP8266 development platform with Arduino core for ESP8266 -# -# arduino core 2.6.3 = platformIO 2.3.2 -# arduino core 2.7.0 = platformIO 2.5.0 -# ------------------------------------------------------------------------------ arduino_core_2_6_3 = espressif8266@2.3.3 arduino_core_2_7_4 = espressif8266@2.6.2 arduino_core_3_0_0 = espressif8266@3.0.0 arduino_core_3_0_2 = espressif8266@3.2.0 arduino_core_3_1_0 = espressif8266@4.1.0 arduino_core_3_1_2 = espressif8266@4.2.1 - -# Development platforms arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop arduino_core_git = https://github.com/platformio/platform-espressif8266#feature/stage - -# Platform to use for ESP8266 platform_wled_default = ${common.arduino_core_3_1_2} -# We use 2.7.4.7 for all, includes PWM flicker fix and Wstring optimization -#platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7 -platform_packages = platformio/toolchain-xtensa @ ~2.100300.220621 #2.40802.200502 - platformio/tool-esptool #@ ~1.413.0 - platformio/tool-esptoolpy #@ ~1.30000.0 - -## previous platform for 8266, in case of problems with the new one -## you'll need makuna/NeoPixelBus@ 2.6.9 for arduino_core_3_0_2, which does not support Ucs890x -;; platform_wled_default = ${common.arduino_core_3_0_2} -;; platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7 -;; platformio/toolchain-xtensa @ ~2.40802.200502 -;; platformio/tool-esptool @ ~1.413.0 -;; platformio/tool-esptoolpy @ ~1.30000.0 - -# ------------------------------------------------------------------------------ -# FLAGS: DEBUG -# esp8266 : see https://docs.platformio.org/en/latest/platforms/espressif8266.html#debug-level -# esp32 : see https://docs.platformio.org/en/latest/platforms/espressif32.html#debug-level -# ------------------------------------------------------------------------------ +platform_packages = platformio/toolchain-xtensa @ ~2.100300.220621 + platformio/tool-esptool + platformio/tool-esptoolpy debug_flags = -D DEBUG=1 -D WLED_DEBUG - -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_TLS_MEM ;; for esp8266 - # if needed (for memleaks etc) also add; -DDEBUG_ESP_OOM -include "umm_malloc/umm_malloc_cfg.h" - # -DDEBUG_ESP_CORE is not working right now - -# ------------------------------------------------------------------------------ -# FLAGS: ldscript (available ldscripts at https://github.com/esp8266/Arduino/tree/master/tools/sdk/ld) -# ldscript_2m1m (2048 KB) = 1019 KB sketch, 4 KB eeprom, 1004 KB spiffs, 16 KB reserved -# ldscript_4m1m (4096 KB) = 1019 KB sketch, 4 KB eeprom, 1002 KB spiffs, 16 KB reserved, 2048 KB empty/ota? -# -# Available lwIP variants (macros): -# -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH = v1.4 Higher Bandwidth (default) -# -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY = v2 Lower Memory -# -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH = v2 Higher Bandwidth -# -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH -# -# BearSSL performance: -# When building with -DSECURE_CLIENT=SECURE_CLIENT_BEARSSL, please add `board_build.f_cpu = 160000000` to the environment configuration -# -# BearSSL ciphers: -# When building on core >= 2.5, you can add the build flag -DBEARSSL_SSL_BASIC in order to build BearSSL with a limited set of ciphers: -# TLS_RSA_WITH_AES_128_CBC_SHA256 / AES128-SHA256 -# TLS_RSA_WITH_AES_256_CBC_SHA256 / AES256-SHA256 -# TLS_RSA_WITH_AES_128_CBC_SHA / AES128-SHA -# TLS_RSA_WITH_AES_256_CBC_SHA / AES256-SHA -# This reduces the OTA size with ~45KB, so it's especially useful on low memory boards (512k/1m). -# ------------------------------------------------------------------------------ -build_flags = - -DMQTT_MAX_PACKET_SIZE=1024 - -DSECURE_CLIENT=SECURE_CLIENT_BEARSSL - -DBEARSSL_SSL_BASIC - -D CORE_DEBUG_LEVEL=0 - -D NDEBUG - -Wno-attributes ;; silence warnings about unknown attribute 'maybe_unused' in NeoPixelBus - #build_flags for the IRremoteESP8266 library (enabled decoders have to appear here) - -D _IR_ENABLE_DEFAULT_=false - -D DECODE_HASH=true - -D DECODE_NEC=true - -D DECODE_SONY=true - -D DECODE_SAMSUNG=true - -D DECODE_LG=true - -DWLED_USE_MY_CONFIG - -build_unflags = - + -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_TLS_MEM +build_flags = + -DMQTT_MAX_PACKET_SIZE=1024 + -DSECURE_CLIENT=SECURE_CLIENT_BEARSSL + -DBEARSSL_SSL_BASIC + -D CORE_DEBUG_LEVEL=0 + -D NDEBUG + -Wno-attributes + -D _IR_ENABLE_DEFAULT_=false + -D DECODE_HASH=true + -D DECODE_NEC=true + -D DECODE_SONY=true + -D DECODE_SAMSUNG=true + -D DECODE_LG=true + -DWLED_USE_MY_CONFIG +build_unflags = ldscript_1m128k = eagle.flash.1m128.ld ldscript_2m512k = eagle.flash.2m512.ld ldscript_2m1m = eagle.flash.2m1m.ld ldscript_4m1m = eagle.flash.4m1m.ld [scripts_defaults] -extra_scripts = - pre:pio-scripts/set_version.py - post:pio-scripts/output_bins.py - post:pio-scripts/strip-floats.py - pre:pio-scripts/user_config_copy.py - pre:pio-scripts/build_ui.py - ; post:pio-scripts/obj-dump.py ;; convenience script to create a disassembly dump of the firmware (hardcore debugging) +extra_scripts = + pre:pio-scripts/set_metadata.py + post:pio-scripts/output_bins.py + post:pio-scripts/strip-floats.py + pre:pio-scripts/user_config_copy.py + pre:pio-scripts/build_ui.py -# ------------------------------------------------------------------------------ -# COMMON SETTINGS: -# ------------------------------------------------------------------------------ [env] framework = arduino board_build.flash_mode = dout monitor_speed = 115200 -# slow upload speed but most compatible (use platformio_override.ini to use faster speed) upload_speed = 115200 - -# ------------------------------------------------------------------------------ -# LIBRARIES: required dependencies -# Please note that we don't always use the latest version of a library. -# -# The following libraries have been included (and some of them changed) in the source: -# ArduinoJson@5.13.5, E131@1.0.0(changed), Time@1.5, Timezone@1.2.1 -# ------------------------------------------------------------------------------ lib_compat_mode = strict -lib_deps = - fastled/FastLED @ 3.6.0 - IRremoteESP8266 @ 2.8.2 - makuna/NeoPixelBus @ 2.8.0 - #https://github.com/makuna/NeoPixelBus.git#CoreShaderBeta - https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.2.1 - # for I2C interface - ;Wire - # ESP-NOW library - ;gmag11/QuickESPNow @ ~0.7.0 - https://github.com/blazoncek/QuickESPNow.git#optional-debug - #For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line - #TFT_eSPI - #For compatible OLED display uncomment following - #olikraus/U8g2 #@ ~2.33.15 - #For Dallas sensor uncomment following - #paulstoffregen/OneWire @ ~2.3.8 - #For BME280 sensor uncomment following - #BME280 @ ~3.0.0 - ;adafruit/Adafruit BMP280 Library @ 2.1.0 - ;adafruit/Adafruit CCS811 Library @ 1.0.4 - ;adafruit/Adafruit Si7021 Library @ 1.4.0 - #For ADS1115 sensor uncomment following - ;adafruit/Adafruit BusIO @ 1.13.2 - ;adafruit/Adafruit ADS1X15 @ 2.4.0 - #For MAX1704x Lipo Monitor / Fuel Gauge uncomment following - ; https://github.com/adafruit/Adafruit_BusIO @ 1.14.5 - ; https://github.com/adafruit/Adafruit_MAX1704X @ 1.0.2 - #For MPU6050 IMU uncomment follwoing - ;electroniccats/MPU6050 @1.0.1 - # For -D USERMOD_ANIMARTRIX - # CC BY-NC 3.0 licensed effects by Stefan Petrick, include this usermod only if you accept the terms! - ;https://github.com/netmindz/animartrix.git#18bf17389e57c69f11bc8d04ebe1d215422c7fb7 - # SHT85 - ;robtillaart/SHT85@~0.3.3 - # Audioreactive usermod - ;kosme/arduinoFFT @ 2.0.1 - +lib_deps = + fastled/FastLED @ 3.6.0 + IRremoteESP8266 @ 2.8.2 + https://github.com/Makuna/NeoPixelBus.git#a0919d1c10696614625978dd6fb750a1317a14ce + https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.4.2 + https://github.com/blazoncek/QuickESPNow.git#optional-debug extra_scripts = ${scripts_defaults.extra_scripts} [esp8266] -build_flags = - -DESP8266 - -DFP_IN_IROM - ;-Wno-deprecated-declarations - ;-Wno-register ;; leaves some warnings when compiling C files: command-line option '-Wno-register' is valid for C++/ObjC++ but not for C - ;-Dregister= # remove warnings in C++17 due to use of deprecated register keyword by the FastLED library ;; warning: this can be dangerous - -Wno-misleading-indentation - ; NONOSDK22x_190703 = 2.2.2-dev(38a443e) - -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703 - ; lwIP 2 - Higher Bandwidth no Features - ; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH - ; lwIP 1.4 - Higher Bandwidth (Aircoookie has) - -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH - ; VTABLES in Flash - -DVTABLES_IN_FLASH - ; restrict to minimal mime-types - -DMIMETYPE_MINIMAL - ; other special-purpose framework flags (see https://docs.platformio.org/en/latest/platforms/espressif8266.html) - ; decrease code cache size and increase IRAM to fit all pixel functions - -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48 ;; in case of linker errors like "section `.text1' will not fit in region `iram1_0_seg'" - ; -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED ;; (experimental) adds some extra heap, but may cause slowdown - -D NON32XFER_HANDLER ;; ask forgiveness for PROGMEM misuse - -lib_deps = - #https://github.com/lorol/LITTLEFS.git - ESPAsyncTCP @ 1.2.2 - ESPAsyncUDP - ESP8266PWM - ${env.lib_deps} - -;; compatibilty flags - same as 0.14.0 which seems to work better on some 8266 boards. Not using PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48 -build_flags_compat = - -DESP8266 - -DFP_IN_IROM - ;;-Wno-deprecated-declarations - -Wno-misleading-indentation - ;;-Wno-attributes ;; silence warnings about unknown attribute 'maybe_unused' in NeoPixelBus - -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703 - -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH - -DVTABLES_IN_FLASH - -DMIMETYPE_MINIMAL - -DWLED_SAVE_IRAM ;; needed to prevent linker error - -;; this platform version was used for WLED 0.14.0 +build_unflags = ${common.build_unflags} +build_flags = + -DESP8266 + -DFP_IN_IROM + -Wno-misleading-indentation + -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703 + -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH + -DVTABLES_IN_FLASH + -DMIMETYPE_MINIMAL + -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48 + -D NON32XFER_HANDLER +lib_deps = + ESPAsyncTCP @ 1.2.2 + ESPAsyncUDP + ESP8266PWM + ${env.lib_deps} +build_flags_compat = + -DESP8266 + -DFP_IN_IROM + -Wno-misleading-indentation + -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703 + -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH + -DVTABLES_IN_FLASH + -DMIMETYPE_MINIMAL + -DWLED_SAVE_IRAM platform_compat = espressif8266@4.2.0 -platform_packages_compat = - platformio/toolchain-xtensa @ ~2.100300.220621 #2.40802.200502 - platformio/tool-esptool #@ ~1.413.0 - platformio/tool-esptoolpy #@ ~1.30000.0 - -;; experimental - for using older NeoPixelBus 2.7.9 -lib_deps_compat = - ESPAsyncTCP @ 1.2.2 - ESPAsyncUDP - ESP8266PWM - fastled/FastLED @ 3.6.0 - IRremoteESP8266 @ 2.8.2 - makuna/NeoPixelBus @ 2.7.9 - https://github.com/blazoncek/QuickESPNow.git#optional-debug - https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.2.1 - +platform_packages_compat = + platformio/toolchain-xtensa @ ~2.100300.220621 + platformio/tool-esptool + platformio/tool-esptoolpy +lib_deps_compat = + ESPAsyncTCP @ 1.2.2 + ESPAsyncUDP + ESP8266PWM + fastled/FastLED @ 3.6.0 + IRremoteESP8266 @ 2.8.2 + makuna/NeoPixelBus @ 2.7.9 + https://github.com/blazoncek/QuickESPNow.git#optional-debug + https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.2.1 [esp32] -#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.3/platform-espressif32-2.0.2.3.zip platform = espressif32@3.5.0 platform_packages = framework-arduinoespressif32 @ https://github.com/Aircoookie/arduino-esp32.git#1.0.6.4 +build_unflags = ${common.build_unflags} build_flags = -g - -DARDUINO_ARCH_ESP32 - #-DCONFIG_LITTLEFS_FOR_IDF_3_2 - -D CONFIG_ASYNC_TCP_USE_WDT=0 - #use LITTLEFS library by lorol in ESP32 core 1.x.x instead of built-in in 2.x.x - -D LOROL_LITTLEFS - ; -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3 + -DARDUINO_ARCH_ESP32 + -D CONFIG_ASYNC_TCP_USE_WDT=0 + -D CONFIG_ASYNC_TCP_STACK_SIZE=8192 + -D LOROL_LITTLEFS tiny_partitions = tools/WLED_ESP32_2MB_noOTA.csv default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv extended_partitions = tools/WLED_ESP32_4MB_700k_FS.csv -big_partitions = tools/WLED_ESP32_4MB_256KB_FS.csv ;; 1.8MB firmware, 256KB filesystem, coredump support +big_partitions = tools/WLED_ESP32_4MB_256KB_FS.csv large_partitions = tools/WLED_ESP32_8MB.csv extreme_partitions = tools/WLED_ESP32_16MB_9MB_FS.csv -lib_deps = - https://github.com/lorol/LITTLEFS.git - https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 - ${env.lib_deps} -# additional build flags for audioreactive -AR_build_flags = -D USERMOD_AUDIOREACTIVE - -D sqrt_internal=sqrtf ;; -fsingle-precision-constant ;; forces ArduinoFFT to use float math (2x faster) -AR_lib_deps = kosme/arduinoFFT @ 2.0.1 +lib_deps = + https://github.com/lorol/LITTLEFS.git + esp32async/AsyncTCP @ 3.4.7 + ${env.lib_deps} +ar_build_flags = -D USERMOD_AUDIOREACTIVE + -D sqrt_internal=sqrtf +ar_lib_deps = kosme/arduinoFFT @ 2.0.1 +board_build.partitions = ${esp32.default_partitions} [esp32_idf_V4] -;; experimental build environment for ESP32 using ESP-IDF 4.4.x / arduino-esp32 v2.0.5 -;; very similar to the normal ESP32 flags, but omitting Lorol LittleFS, as littlefs is included in the new framework already. -;; -;; please note that you can NOT update existing ESP32 installs with a "V4" build. Also updating by OTA will not work properly. -;; You need to completely erase your device (esptool erase_flash) first, then install the "V4" build from VSCode+platformio. platform = espressif32@ ~6.3.2 -platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) +platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 +build_unflags = ${common.build_unflags} build_flags = -g - -Wshadow=compatible-local ;; emit warning in case a local variable "shadows" another local one - -DARDUINO_ARCH_ESP32 -DESP32 - -D CONFIG_ASYNC_TCP_USE_WDT=0 - -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3 -lib_deps = - https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 - ${env.lib_deps} + -Wshadow=compatible-local + -DARDUINO_ARCH_ESP32 -DESP32 + -D CONFIG_ASYNC_TCP_USE_WDT=0 + -D CONFIG_ASYNC_TCP_STACK_SIZE=8192 + -DARDUINO_USB_CDC_ON_BOOT=0 + -D WLED_ENABLE_DMX_INPUT +lib_deps = + esp32async/AsyncTCP @ 3.4.7 + https://github.com/someweisguy/esp_dmx.git#47db25d + ${env.lib_deps} +board_build.partitions = ${esp32.default_partitions} [esp32s2] -;; generic definitions for all ESP32-S2 boards platform = espressif32@ ~6.3.2 -platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) +platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 +build_unflags = ${common.build_unflags} build_flags = -g - -DARDUINO_ARCH_ESP32 - -DARDUINO_ARCH_ESP32S2 - -DCONFIG_IDF_TARGET_ESP32S2=1 - -D CONFIG_ASYNC_TCP_USE_WDT=0 - -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 - -DCO - -DARDUINO_USB_MODE=0 ;; this flag is mandatory for ESP32-S2 ! - ;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry: - ;; ARDUINO_USB_CDC_ON_BOOT -lib_deps = - https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 - ${env.lib_deps} + -DARDUINO_ARCH_ESP32 + -DARDUINO_ARCH_ESP32S2 + -DCONFIG_IDF_TARGET_ESP32S2=1 + -D CONFIG_ASYNC_TCP_USE_WDT=0 + -D CONFIG_ASYNC_TCP_STACK_SIZE=8192 + -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 + -DCO + -DARDUINO_USB_MODE=0 +lib_deps = + esp32async/AsyncTCP @ 3.4.7 + ${env.lib_deps} +board_build.partitions = ${esp32.default_partitions} [esp32c3] -;; generic definitions for all ESP32-C3 boards platform = espressif32@ ~6.3.2 -platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) +platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 +build_unflags = ${common.build_unflags} build_flags = -g - -DARDUINO_ARCH_ESP32 - -DARDUINO_ARCH_ESP32C3 - -DCONFIG_IDF_TARGET_ESP32C3=1 - -D CONFIG_ASYNC_TCP_USE_WDT=0 - -DCO - -DARDUINO_USB_MODE=1 ;; this flag is mandatory for ESP32-C3 - ;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry: - ;; ARDUINO_USB_CDC_ON_BOOT -lib_deps = - https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 - ${env.lib_deps} + -DARDUINO_ARCH_ESP32 + -DARDUINO_ARCH_ESP32C3 + -DCONFIG_IDF_TARGET_ESP32C3=1 + -D CONFIG_ASYNC_TCP_USE_WDT=0 + -D CONFIG_ASYNC_TCP_STACK_SIZE=8192 + -DCO + -DARDUINO_USB_MODE=1 +lib_deps = + esp32async/AsyncTCP @ 3.4.7 + ${env.lib_deps} +board_build.partitions = ${esp32.default_partitions} [esp32s3] -;; generic definitions for all ESP32-S3 boards platform = espressif32@ ~6.3.2 -platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) +platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 +build_unflags = ${common.build_unflags} build_flags = -g - -DESP32 - -DARDUINO_ARCH_ESP32 - -DARDUINO_ARCH_ESP32S3 - -DCONFIG_IDF_TARGET_ESP32S3=1 - -D CONFIG_ASYNC_TCP_USE_WDT=0 - -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_DFU_ON_BOOT=0 - -DCO - ;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry: - ;; ARDUINO_USB_MODE, ARDUINO_USB_CDC_ON_BOOT -lib_deps = - https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 - ${env.lib_deps} - - -# ------------------------------------------------------------------------------ -# WLED BUILDS -# ------------------------------------------------------------------------------ + -DESP32 + -DARDUINO_ARCH_ESP32 + -DARDUINO_ARCH_ESP32S3 + -DCONFIG_IDF_TARGET_ESP32S3=1 + -D CONFIG_ASYNC_TCP_USE_WDT=0 + -D CONFIG_ASYNC_TCP_STACK_SIZE=8192 + -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_DFU_ON_BOOT=0 + -DCO +lib_deps = + esp32async/AsyncTCP @ 3.4.7 + ${env.lib_deps} +board_build.partitions = ${esp32.large_partitions} [env:nodemcuv2] board = nodemcuv2 @@ -345,23 +214,21 @@ platform = ${common.platform_wled_default} platform_packages = ${common.platform_packages} board_build.ldscript = ${common.ldscript_4m1m} build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP8266\" #-DWLED_DISABLE_2D +build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP8266\" lib_deps = ${esp8266.lib_deps} monitor_filters = esp8266_exception_decoder [env:nodemcuv2_compat] extends = env:nodemcuv2 -;; using platform version and build options from WLED 0.14.0 platform = ${esp8266.platform_compat} platform_packages = ${esp8266.platform_packages_compat} -build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP8266_compat\" #-DWLED_DISABLE_2D -;; lib_deps = ${esp8266.lib_deps_compat} ;; experimental - use older NeoPixelBus 2.7.9 +build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP8266_compat\" [env:nodemcuv2_160] extends = env:nodemcuv2 board_build.f_cpu = 160000000L -build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP8266_160\" #-DWLED_DISABLE_2D - -D USERMOD_AUDIOREACTIVE +build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP8266_160\" + -D USERMOD_AUDIOREACTIVE [env:esp8266_2m] board = esp_wroom_02 @@ -374,16 +241,15 @@ lib_deps = ${esp8266.lib_deps} [env:esp8266_2m_compat] extends = env:esp8266_2m -;; using platform version and build options from WLED 0.14.0 platform = ${esp8266.platform_compat} platform_packages = ${esp8266.platform_packages_compat} -build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP02_compat\" #-DWLED_DISABLE_2D +build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP02_compat\" [env:esp8266_2m_160] extends = env:esp8266_2m board_build.f_cpu = 160000000L build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP02_160\" - -D USERMOD_AUDIOREACTIVE + -D USERMOD_AUDIOREACTIVE [env:esp01_1m_full] board = esp01_1m @@ -392,32 +258,43 @@ platform_packages = ${common.platform_packages} board_build.ldscript = ${common.ldscript_1m128k} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP01\" -D WLED_DISABLE_OTA - ; -D WLED_USE_REAL_MATH ;; may fix wrong sunset/sunrise times, at the cost of 7064 bytes FLASH and 975 bytes RAM lib_deps = ${esp8266.lib_deps} [env:esp01_1m_full_compat] extends = env:esp01_1m_full -;; using platform version and build options from WLED 0.14.0 platform = ${esp8266.platform_compat} platform_packages = ${esp8266.platform_packages_compat} -build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP01_compat\" -D WLED_DISABLE_OTA #-DWLED_DISABLE_2D +build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP01_compat\" -D WLED_DISABLE_OTA [env:esp01_1m_full_160] extends = env:esp01_1m_full board_build.f_cpu = 160000000L build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP01_160\" -D WLED_DISABLE_OTA - -D USERMOD_AUDIOREACTIVE - ; -D WLED_USE_REAL_MATH ;; may fix wrong sunset/sunrise times, at the cost of 7064 bytes FLASH and 975 bytes RAM + -D USERMOD_AUDIOREACTIVE [env:esp32dev] board = esp32dev platform = ${esp32.platform} platform_packages = ${esp32.platform_packages} build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32\" #-D WLED_DISABLE_BROWNOUT_DET - ${esp32.AR_build_flags} -lib_deps = ${esp32.lib_deps} - ${esp32.AR_lib_deps} +build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32\" + ${esp32.AR_build_flags} +lib_deps = + ${esp32.lib_deps} + ${esp32.AR_lib_deps} +monitor_filters = esp32_exception_decoder +board_build.partitions = ${esp32.default_partitions} + +[env:esp32dev_V4] +board = esp32dev +platform = ${esp32_idf_V4.platform} +platform_packages = ${esp32_idf_V4.platform_packages} +build_unflags = ${common.build_unflags} +build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_V4\" + ${esp32.AR_build_flags} +lib_deps = + ${esp32_idf_V4.lib_deps} + ${esp32.AR_lib_deps} monitor_filters = esp32_exception_decoder board_build.partitions = ${esp32.default_partitions} @@ -426,26 +303,26 @@ board = esp32dev platform = ${esp32_idf_V4.platform} platform_packages = ${esp32_idf_V4.platform_packages} build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_8M\" #-D WLED_DISABLE_BROWNOUT_DET - ${esp32.AR_build_flags} -lib_deps = ${esp32_idf_V4.lib_deps} - ${esp32.AR_lib_deps} +build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_8M\" + ${esp32.AR_build_flags} +lib_deps = + ${esp32_idf_V4.lib_deps} + ${esp32.AR_lib_deps} monitor_filters = esp32_exception_decoder board_build.partitions = ${esp32.large_partitions} board_upload.flash_size = 8MB board_upload.maximum_size = 8388608 -; board_build.f_flash = 80000000L -; board_build.flash_mode = qio [env:esp32dev_16M] board = esp32dev platform = ${esp32_idf_V4.platform} platform_packages = ${esp32_idf_V4.platform_packages} build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_16M\" #-D WLED_DISABLE_BROWNOUT_DET - ${esp32.AR_build_flags} -lib_deps = ${esp32_idf_V4.lib_deps} - ${esp32.AR_lib_deps} +build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_16M\" + ${esp32.AR_build_flags} +lib_deps = + ${esp32_idf_V4.lib_deps} + ${esp32.AR_lib_deps} monitor_filters = esp32_exception_decoder board_build.partitions = ${esp32.extreme_partitions} board_upload.flash_size = 16MB @@ -453,20 +330,6 @@ board_upload.maximum_size = 16777216 board_build.f_flash = 80000000L board_build.flash_mode = dio -;[env:esp32dev_audioreactive] -;board = esp32dev -;platform = ${esp32.platform} -;platform_packages = ${esp32.platform_packages} -;build_unflags = ${common.build_unflags} -;build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32_audioreactive\" #-D WLED_DISABLE_BROWNOUT_DET -; ${esp32.AR_build_flags} -;lib_deps = ${esp32.lib_deps} -; ${esp32.AR_lib_deps} -;monitor_filters = esp32_exception_decoder -;board_build.partitions = ${esp32.default_partitions} -;; board_build.f_flash = 80000000L -;; board_build.flash_mode = dio - [env:esp32_eth] board = esp32-poe platform = ${esp32.platform} @@ -474,10 +337,10 @@ platform_packages = ${esp32.platform_packages} upload_speed = 921600 build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32_Ethernet\" -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1 -; -D WLED_DISABLE_ESPNOW ;; ESP-NOW requires wifi, may crash with ethernet only - ${esp32.AR_build_flags} -lib_deps = ${esp32.lib_deps} - ${esp32.AR_lib_deps} + ${esp32.AR_build_flags} +lib_deps = + ${esp32.lib_deps} + ${esp32.AR_lib_deps} board_build.partitions = ${esp32.default_partitions} [env:esp32_wrover] @@ -490,11 +353,12 @@ board_build.flash_mode = qio board_build.partitions = ${esp32.extended_partitions} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_WROVER\" - -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue ;; Older ESP32 (rev.<3) need a PSRAM fix (increases static RAM used) https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/external-ram.html - -D DATA_PINS=25 - ${esp32.AR_build_flags} -lib_deps = ${esp32_idf_V4.lib_deps} - ${esp32.AR_lib_deps} + -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue + -D DATA_PINS=25 + ${esp32.AR_build_flags} +lib_deps = + ${esp32_idf_V4.lib_deps} + ${esp32.AR_lib_deps} [env:esp32c3dev] extends = esp32c3 @@ -504,30 +368,28 @@ framework = arduino board = esp32-c3-devkitm-1 board_build.partitions = ${esp32.default_partitions} build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-C3\" - -D WLED_WATCHDOG_TIMEOUT=0 - -DLOLIN_WIFI_FIX ; seems to work much better with this - -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB - ;-DARDUINO_USB_CDC_ON_BOOT=0 ;; for serial-to-USB chip + -D WLED_WATCHDOG_TIMEOUT=0 + -DLOLIN_WIFI_FIX + -DARDUINO_USB_CDC_ON_BOOT=1 upload_speed = 460800 build_unflags = ${common.build_unflags} lib_deps = ${esp32c3.lib_deps} [env:esp32s3dev_16MB_opi] -;; ESP32-S3 development board, with 16MB FLASH and >= 8MB PSRAM (memory_type: qio_opi) -board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support -board_build.arduino.memory_type = qio_opi ;; use with PSRAM: 8MB or 16MB +board = esp32-s3-devkitc-1 +board_build.arduino.memory_type = qio_opi platform = ${esp32s3.platform} platform_packages = ${esp32s3.platform_packages} upload_speed = 921600 build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_16MB_opi\" - -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 - ;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip - -D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB") - -DBOARD_HAS_PSRAM - ${esp32.AR_build_flags} -lib_deps = ${esp32s3.lib_deps} - ${esp32.AR_lib_deps} + -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 + -D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 + -DBOARD_HAS_PSRAM + ${esp32.AR_build_flags} +lib_deps = + ${esp32s3.lib_deps} + ${esp32.AR_lib_deps} board_build.partitions = ${esp32.extreme_partitions} board_upload.flash_size = 16MB board_upload.maximum_size = 16777216 @@ -536,68 +398,64 @@ board_build.flash_mode = qio monitor_filters = esp32_exception_decoder [env:esp32s3dev_8MB_opi] -;; ESP32-S3 development board, with 8MB FLASH and >= 8MB PSRAM (memory_type: qio_opi) -board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support -board_build.arduino.memory_type = qio_opi ;; use with PSRAM: 8MB or 16MB +board = esp32-s3-devkitc-1 +board_build.arduino.memory_type = qio_opi platform = ${esp32s3.platform} platform_packages = ${esp32s3.platform_packages} upload_speed = 921600 build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_8MB_opi\" - -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 - ;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip - -D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB") - -DBOARD_HAS_PSRAM - ${esp32.AR_build_flags} -lib_deps = ${esp32s3.lib_deps} - ${esp32.AR_lib_deps} + -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 + -D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 + -DBOARD_HAS_PSRAM + ${esp32.AR_build_flags} +lib_deps = + ${esp32s3.lib_deps} + ${esp32.AR_lib_deps} board_build.partitions = ${esp32.large_partitions} board_build.f_flash = 80000000L board_build.flash_mode = qio monitor_filters = esp32_exception_decoder [env:esp32S3_wroom2] -;; For ESP32-S3 WROOM-2, a.k.a. ESP32-S3 DevKitC-1 v1.1 -;; with >= 16MB FLASH and >= 8MB PSRAM (memory_type: opi_opi) platform = ${esp32s3.platform} platform_packages = ${esp32s3.platform_packages} -board = esp32s3camlcd ;; this is the only standard board with "opi_opi" +board = esp32s3camlcd board_build.arduino.memory_type = opi_opi upload_speed = 921600 build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_WROOM-2\" - -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 - -D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip - ;; -D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB") - -DBOARD_HAS_PSRAM - -D LEDPIN=38 -D DATA_PINS=38 ;; buildin WS2812b LED - -D BTNPIN=0 -D RLYPIN=16 -D IRPIN=17 -D AUDIOPIN=-1 - -D WLED_DEBUG - ${esp32.AR_build_flags} - -D SR_DMTYPE=1 -D I2S_SDPIN=13 -D I2S_CKPIN=14 -D I2S_WSPIN=15 -D MCLK_PIN=4 ;; I2S mic -lib_deps = ${esp32s3.lib_deps} - ${esp32.AR_lib_deps} - + -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 + -D ARDUINO_USB_CDC_ON_BOOT=0 + -DBOARD_HAS_PSRAM + -D LEDPIN=38 -D DATA_PINS=38 + -D BTNPIN=0 -D RLYPIN=16 -D IRPIN=17 -D AUDIOPIN=-1 + -D WLED_DEBUG + ${esp32.AR_build_flags} + -D SR_DMTYPE=1 -D I2S_SDPIN=13 -D I2S_CKPIN=14 -D I2S_WSPIN=15 -D MCLK_PIN=4 +lib_deps = + ${esp32s3.lib_deps} + ${esp32.AR_lib_deps} board_build.partitions = ${esp32.extreme_partitions} board_upload.flash_size = 16MB board_upload.maximum_size = 16777216 monitor_filters = esp32_exception_decoder [env:esp32s3_4M_qspi] -;; ESP32-S3, with 4MB FLASH and <= 4MB PSRAM (memory_type: qio_qspi) -board = lolin_s3_mini ;; -S3 mini, 4MB flash 2MB PSRAM +board = lolin_s3_mini platform = ${esp32s3.platform} platform_packages = ${esp32s3.platform_packages} upload_speed = 921600 build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_4M_qspi\" - -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB") - -DBOARD_HAS_PSRAM - -DLOLIN_WIFI_FIX ; seems to work much better with this - -D WLED_WATCHDOG_TIMEOUT=0 - ${esp32.AR_build_flags} -lib_deps = ${esp32s3.lib_deps} - ${esp32.AR_lib_deps} + -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MODE=1 + -DBOARD_HAS_PSRAM + -DLOLIN_WIFI_FIX + -D WLED_WATCHDOG_TIMEOUT=0 + ${esp32.AR_build_flags} +lib_deps = + ${esp32s3.lib_deps} + ${esp32.AR_lib_deps} board_build.partitions = ${esp32.default_partitions} board_build.f_flash = 80000000L board_build.flash_mode = qio @@ -612,20 +470,21 @@ board_build.flash_mode = qio board_build.f_flash = 80000000L build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S2\" - -DARDUINO_USB_CDC_ON_BOOT=1 - -DARDUINO_USB_MSC_ON_BOOT=0 - -DARDUINO_USB_DFU_ON_BOOT=0 - -DBOARD_HAS_PSRAM - -DLOLIN_WIFI_FIX ; seems to work much better with this - -D WLED_WATCHDOG_TIMEOUT=0 - -D CONFIG_ASYNC_TCP_USE_WDT=0 - -D DATA_PINS=16 - -D HW_PIN_SCL=35 - -D HW_PIN_SDA=33 - -D HW_PIN_CLOCKSPI=7 - -D HW_PIN_DATASPI=11 - -D HW_PIN_MISOSPI=9 -; -D STATUSLED=15 - ${esp32.AR_build_flags} -lib_deps = ${esp32s2.lib_deps} - ${esp32.AR_lib_deps} + -DARDUINO_USB_CDC_ON_BOOT=1 + -DARDUINO_USB_MSC_ON_BOOT=0 + -DARDUINO_USB_DFU_ON_BOOT=0 + -DBOARD_HAS_PSRAM + -DLOLIN_WIFI_FIX + -D WLED_WATCHDOG_TIMEOUT=0 + -D CONFIG_ASYNC_TCP_USE_WDT=0 + -D CONFIG_ASYNC_TCP_STACK_SIZE=8192 + -D DATA_PINS=16 + -D HW_PIN_SCL=35 + -D HW_PIN_SDA=33 + -D HW_PIN_CLOCKSPI=7 + -D HW_PIN_DATASPI=11 + -D HW_PIN_MISOSPI=9 + ${esp32.AR_build_flags} +lib_deps = + ${esp32s2.lib_deps} + ${esp32.AR_lib_deps} diff --git a/platformio_override.sample.ini b/platformio_override.sample.ini index dd3630d82c..7bfeae5e41 100644 --- a/platformio_override.sample.ini +++ b/platformio_override.sample.ini @@ -1,519 +1,6 @@ -# Example PlatformIO Project Configuration Override -# ------------------------------------------------------------------------------ -# Copy to platformio_override.ini to activate overrides -# ------------------------------------------------------------------------------ -# Please visit documentation: https://docs.platformio.org/page/projectconf.html - -[platformio] -default_envs = WLED_tasmota_1M # define as many as you need - -#---------- -# SAMPLE -#---------- -[env:WLED_generic8266_1M] -extends = env:esp01_1m_full # when you want to extend the existing environment (define only updated options) -; board = esp01_1m # uncomment when ou need different board -; platform = ${common.platform_wled_default} # uncomment and change when you want particular platform -; platform_packages = ${common.platform_packages} -; board_build.ldscript = ${common.ldscript_1m128k} -; upload_speed = 921600 # fast upload speed (remove ';' if your board supports fast upload speed) -# Sample libraries used for various usermods. Uncomment when using particular usermod. -lib_deps = ${esp8266.lib_deps} -; olikraus/U8g2 # @~2.33.15 -; paulstoffregen/OneWire@~2.3.8 -; adafruit/Adafruit Unified Sensor@^1.1.4 -; adafruit/DHT sensor library@^1.4.1 -; adafruit/Adafruit BME280 Library@^2.2.2 -; Wire -; robtillaart/SHT85@~0.3.3 -; ;gmag11/QuickESPNow @ ~0.7.0 # will also load QuickDebug -; https://github.com/blazoncek/QuickESPNow.git#optional-debug ;; exludes debug library -; ${esp32.AR_lib_deps} ;; used for USERMOD_AUDIOREACTIVE -; bitbank2/PNGdec@^1.0.1 ;; used for POV display uncomment following - -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp8266.build_flags} -; -; *** To use the below defines/overrides, copy and paste each onto it's own line just below build_flags in the section above. -; -; Set a release name that may be used to distinguish required binary for flashing -; -D WLED_RELEASE_NAME=\"ESP32_MULTI_USREMODS\" -; -; disable specific features -; -D WLED_DISABLE_OTA -; -D WLED_DISABLE_ALEXA -; -D WLED_DISABLE_HUESYNC -; -D WLED_DISABLE_LOXONE -; -D WLED_DISABLE_INFRARED -; -D WLED_DISABLE_WEBSOCKETS -; -D WLED_DISABLE_MQTT -; -D WLED_DISABLE_ADALIGHT -; -D WLED_DISABLE_2D -; -D WLED_DISABLE_PXMAGIC -; -D WLED_DISABLE_ESPNOW -; -D WLED_DISABLE_BROWNOUT_DET -; -; enable optional built-in features -; -D WLED_ENABLE_PIXART -; -D WLED_ENABLE_USERMOD_PAGE # if created -; -D WLED_ENABLE_DMX -; -; PIN defines - uncomment and change, if needed: -; -D DATA_PINS=2 -; or use this for multiple outputs -; -D DATA_PINS=1,3 -; -D BTNPIN=0 -; -D IRPIN=4 -; -D RLYPIN=12 -; -D RLYMDE=1 -; -D RLYODRAIN=0 -; -D LED_BUILTIN=2 # GPIO of built-in LED -; -; Limit max buses -; -D WLED_MAX_BUSSES=2 -; -D WLED_MAX_ANALOG_CHANNELS=3 # only 3 PWM HW pins available -; -D WLED_MAX_DIGITAL_CHANNELS=2 # only 2 HW accelerated pins available -; -; Configure default WiFi -; -D CLIENT_SSID='"MyNetwork"' -; -D CLIENT_PASS='"Netw0rkPassw0rd"' -; -; Configure and use Ethernet -; -D WLED_USE_ETHERNET -; -D WLED_ETH_DEFAULT=5 -; do not use pins 5, (16,) 17, 18, 19, 21, 22, 23, 25, 26, 27 for anything but ethernet -; -D PHY_ADDR=0 -D ETH_PHY_POWER=5 -D ETH_PHY_MDC=23 -D ETH_PHY_MDIO=18 -; -D ETH_CLK_MODE=ETH_CLOCK_GPIO17_OUT -; -; NTP time configuration -; -D WLED_NTP_ENABLED=true -; -D WLED_TIMEZONE=2 -; -D WLED_LAT=48.86 -; -D WLED_LON=2.33 -; -; Use Watchdog timer with 10s guard -; -D WLED_WATCHDOG_TIMEOUT=10 -; -; Create debug build (with remote debug) -; -D WLED_DEBUG -; -D WLED_DEBUG_HOST='"192.168.0.100"' -; -D WLED_DEBUG_PORT=7868 -; -; Use Autosave usermod and set it to do save after 90s -; -D USERMOD_AUTO_SAVE -; -D AUTOSAVE_AFTER_SEC=90 -; -; Use AHT10/AHT15/AHT20 usermod -; -D USERMOD_AHT10 -; -; Use INA226 usermod -; -D USERMOD_INA226 -; -; Use 4 Line Display usermod with SPI display -; -D USERMOD_FOUR_LINE_DISPLAY -; -DFLD_SPI_DEFAULT -; -D FLD_TYPE=SSD1306_SPI64 -; -D FLD_PIN_CLOCKSPI=14 -; -D FLD_PIN_DATASPI=13 -; -D FLD_PIN_DC=26 -; -D FLD_PIN_CS=15 -; -D FLD_PIN_RESET=27 -; -; Use Rotary encoder usermod (in conjunction with 4LD) -; -D USERMOD_ROTARY_ENCODER_UI -; -D ENCODER_DT_PIN=5 -; -D ENCODER_CLK_PIN=18 -; -D ENCODER_SW_PIN=19 -; -; Use Dallas DS18B20 temperature sensor usermod and configure it to use GPIO13 -; -D USERMOD_DALLASTEMPERATURE -; -D TEMPERATURE_PIN=13 -; -; Use Multi Relay usermod and configure it to use 6 relays and appropriate GPIO -; -D USERMOD_MULTI_RELAY -; -D MULTI_RELAY_MAX_RELAYS=6 -; -D MULTI_RELAY_PINS=12,23,22,21,24,25 -; -; Use PIR sensor usermod and configure it to use GPIO4 and timer of 60s -; -D USERMOD_PIRSWITCH -; -D PIR_SENSOR_PIN=4 # use -1 to disable usermod -; -D PIR_SENSOR_OFF_SEC=60 -; -D PIR_SENSOR_MAX_SENSORS=2 # max allowable sensors (uses OR logic for triggering) -; -; Use Audioreactive usermod and configure I2S microphone -; -D USERMOD_AUDIOREACTIVE -; -D AUDIOPIN=-1 -; -D DMTYPE=1 # 0-analog/disabled, 1-I2S generic, 2-ES7243, 3-SPH0645, 4-I2S+mclk, 5-I2S PDM -; -D I2S_SDPIN=36 -; -D I2S_WSPIN=23 -; -D I2S_CKPIN=19 -; -; Use PWM fan usermod -; -D USERMOD_PWM_FAN -; -D TACHO_PIN=33 -; -D PWM_PIN=32 -; -; Use POV Display usermod -; -D USERMOD_POV_DISPLAY -; Use built-in or custom LED as a status indicator (assumes LED is connected to GPIO16) -; -D STATUSLED=16 -; -; set the name of the module - make sure there is a quote-backslash-quote before the name and a backslash-quote-quote after the name -; -D SERVERNAME="\"WLED\"" -; -; set the number of LEDs -; -D DEFAULT_LED_COUNT=30 -; or this for multiple outputs -; -D PIXEL_COUNTS=30,30 -; -; set the default LED type -; -D DEFAULT_LED_TYPE=22 # see const.h (TYPE_xxxx) -; -; set milliampere limit when using ESP power pin (or inadequate PSU) to power LEDs -; -D ABL_MILLIAMPS_DEFAULT=850 -; -D LED_MILLIAMPS_DEFAULT=55 -; -; enable IR by setting remote type -; -D IRTYPE=0 # 0 Remote disabled | 1 24-key RGB | 2 24-key with CT | 3 40-key blue | 4 40-key RGB | 5 21-key RGB | 6 6-key black | 7 9-key red | 8 JSON remote -; -; set default color order of your led strip -; -D DEFAULT_LED_COLOR_ORDER=COL_ORDER_GRB -; -; use PSRAM on classic ESP32 rev.1 (rev.3 or above has no issues) -; -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue # needed only for classic ESP32 rev.1 -; -; configure I2C and SPI interface (for various hardware) -; -D I2CSDAPIN=33 # initialise interface -; -D I2CSCLPIN=35 # initialise interface -; -D HW_PIN_SCL=35 -; -D HW_PIN_SDA=33 -; -D HW_PIN_CLOCKSPI=7 -; -D HW_PIN_DATASPI=11 -; -D HW_PIN_MISOSPI=9 - - - -# ------------------------------------------------------------------------------ -# PRE-CONFIGURED DEVELOPMENT BOARDS AND CONTROLLERS -# ------------------------------------------------------------------------------ - -[env:esp07] -board = esp07 -platform = ${common.platform_wled_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_4m1m} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp8266.build_flags} -lib_deps = ${esp8266.lib_deps} - -[env:d1_mini] -board = d1_mini -platform = ${common.platform_wled_default} -platform_packages = ${common.platform_packages} -upload_speed = 921600 -board_build.ldscript = ${common.ldscript_4m1m} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp8266.build_flags} -lib_deps = ${esp8266.lib_deps} -monitor_filters = esp8266_exception_decoder - -[env:heltec_wifi_kit_8] -board = d1_mini -platform = ${common.platform_wled_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_4m1m} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp8266.build_flags} -lib_deps = ${esp8266.lib_deps} - -[env:h803wf] -board = d1_mini -platform = ${common.platform_wled_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_4m1m} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=1 -D WLED_DISABLE_INFRARED -lib_deps = ${esp8266.lib_deps} - -[env:esp32dev_qio80] -board = esp32dev -platform = ${esp32.platform} -platform_packages = ${esp32.platform_packages} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32.build_flags} #-D WLED_DISABLE_BROWNOUT_DET -lib_deps = ${esp32.lib_deps} -monitor_filters = esp32_exception_decoder -board_build.partitions = ${esp32.default_partitions} -board_build.f_flash = 80000000L -board_build.flash_mode = qio - -[env:esp32dev_V4_dio80] -;; experimental ESP32 env using ESP-IDF V4.4.x -;; Warning: this build environment is not stable!! -;; please erase your device before installing. -board = esp32dev -platform = ${esp32_idf_V4.platform} -platform_packages = ${esp32_idf_V4.platform_packages} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} #-D WLED_DISABLE_BROWNOUT_DET -lib_deps = ${esp32_idf_V4.lib_deps} -monitor_filters = esp32_exception_decoder -board_build.partitions = ${esp32_idf_V4.default_partitions} -board_build.f_flash = 80000000L -board_build.flash_mode = dio - -[env:esp32s2_saola] -board = esp32-s2-saola-1 -platform = ${esp32s2.platform} -platform_packages = ${esp32s2.platform_packages} -framework = arduino -board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv -board_build.flash_mode = qio -upload_speed = 460800 -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32s2.build_flags} - ;-DLOLIN_WIFI_FIX ;; try this in case Wifi does not work - -DARDUINO_USB_CDC_ON_BOOT=1 -lib_deps = ${esp32s2.lib_deps} - -[env:esp32s3dev_8MB_PSRAM_qspi] -;; ESP32-TinyS3 development board, with 8MB FLASH and PSRAM (memory_type: qio_qspi) -extends = env:esp32s3dev_8MB_PSRAM_opi -;board = um_tinys3 ; -> needs workaround from https://github.com/Aircoookie/WLED/pull/2905#issuecomment-1328049860 -board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support -board_build.arduino.memory_type = qio_qspi ;; use with PSRAM: 2MB or 4MB - -[env:esp8285_4CH_MagicHome] -board = esp8285 -platform = ${common.platform_wled_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_1m128k} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_DISABLE_OTA -lib_deps = ${esp8266.lib_deps} - -[env:esp8285_H801] -board = esp8285 -platform = ${common.platform_wled_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_1m128k} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_DISABLE_OTA -lib_deps = ${esp8266.lib_deps} - -[env:d1_mini_5CH_Shojo_PCB] -board = d1_mini -platform = ${common.platform_wled_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_4m1m} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_USE_SHOJO_PCB -lib_deps = ${esp8266.lib_deps} - -[env:d1_mini_debug] -board = d1_mini -build_type = debug -platform = ${common.platform_wled_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_4m1m} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp8266.build_flags} ${common.debug_flags} -lib_deps = ${esp8266.lib_deps} - -[env:d1_mini_ota] -board = d1_mini -upload_protocol = espota -# exchange for your WLED IP -upload_port = "10.10.1.27" -platform = ${common.platform_wled_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_4m1m} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp8266.build_flags} -lib_deps = ${esp8266.lib_deps} - -[env:anavi_miracle_controller] -board = d1_mini -platform = ${common.platform_wled_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_4m1m} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=12 -D IRPIN=-1 -D RLYPIN=2 -lib_deps = ${esp8266.lib_deps} - -[env:esp32c3dev_2MB] -;; for ESP32-C3 boards with 2MB flash (instead of 4MB). -;; this board need a specific partition file. OTA not possible. -extends = esp32c3 -platform = ${esp32c3.platform} -platform_packages = ${esp32c3.platform_packages} -board = esp32-c3-devkitm-1 -build_flags = ${common.build_flags} ${esp32c3.build_flags} - -D WLED_WATCHDOG_TIMEOUT=0 - -D WLED_DISABLE_OTA - ; -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB - -DARDUINO_USB_CDC_ON_BOOT=0 ;; for serial-to-USB chip -build_unflags = ${common.build_unflags} -upload_speed = 115200 -lib_deps = ${esp32c3.lib_deps} -board_build.partitions = tools/WLED_ESP32_2MB_noOTA.csv -board_build.flash_mode = dio -board_upload.flash_size = 2MB -board_upload.maximum_size = 2097152 - -[env:wemos_shield_esp32] -board = esp32dev -platform = ${esp32.platform} -platform_packages = ${esp32.platform_packages} -upload_speed = 460800 -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32.build_flags} - -D DATA_PINS=16 - -D RLYPIN=19 - -D BTNPIN=17 - -D IRPIN=18 - -D UWLED_USE_MY_CONFIG - -D USERMOD_DALLASTEMPERATURE - -D USERMOD_FOUR_LINE_DISPLAY - -D TEMPERATURE_PIN=23 - -D USERMOD_AUDIOREACTIVE -lib_deps = ${esp32.lib_deps} - OneWire@~2.3.5 - olikraus/U8g2 @ ^2.28.8 - https://github.com/blazoncek/arduinoFFT.git -board_build.partitions = ${esp32.default_partitions} - -[env:m5atom] -board = esp32dev -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32.build_flags} -D DATA_PINS=27 -D BTNPIN=39 -lib_deps = ${esp32.lib_deps} -platform = ${esp32.platform} -platform_packages = ${esp32.platform_packages} -board_build.partitions = ${esp32.default_partitions} - -[env:sp501e] -board = esp_wroom_02 -platform = ${common.platform_wled_default} -board_build.ldscript = ${common.ldscript_2m512k} -build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=3 -D BTNPIN=1 -lib_deps = ${esp8266.lib_deps} - -[env:sp511e] -board = esp_wroom_02 -platform = ${common.platform_wled_default} -board_build.ldscript = ${common.ldscript_2m512k} -build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=3 -D BTNPIN=2 -D IRPIN=5 -D WLED_MAX_BUTTONS=3 -lib_deps = ${esp8266.lib_deps} - -[env:Athom_RGBCW] ;7w and 5w(GU10) bulbs -board = esp8285 -platform = ${common.platform_wled_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_2m512k} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,13,5 - -D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 -lib_deps = ${esp8266.lib_deps} - -[env:Athom_15w_RGBCW] ;15w bulb -board = esp8285 -platform = ${common.platform_wled_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_2m512k} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,5,13 - -D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 -D WLED_USE_IC_CCT -lib_deps = ${esp8266.lib_deps} - -[env:Athom_3Pin_Controller] ;small controller with only data -board = esp8285 -platform = ${common.platform_wled_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_2m512k} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=0 -D RLYPIN=-1 -D DATA_PINS=1 -D WLED_DISABLE_INFRARED -lib_deps = ${esp8266.lib_deps} - -[env:Athom_4Pin_Controller] ; With clock and data interface -board = esp8285 -platform = ${common.platform_wled_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_2m512k} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=0 -D RLYPIN=12 -D DATA_PINS=1 -D WLED_DISABLE_INFRARED -lib_deps = ${esp8266.lib_deps} - -[env:Athom_5Pin_Controller] ;Analog light strip controller -board = esp8285 -platform = ${common.platform_wled_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_2m512k} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=0 -D RLYPIN=-1 DATA_PINS=4,12,14,13 -D WLED_DISABLE_INFRARED -lib_deps = ${esp8266.lib_deps} - -[env:MY9291] -board = esp01_1m -platform = ${common.platform_wled_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_1m128k} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_DISABLE_OTA -D USERMOD_MY9291 -lib_deps = ${esp8266.lib_deps} - -# ------------------------------------------------------------------------------ -# codm pixel controller board configurations -# codm-controller-0_6 can also be used for the TYWE3S controller -# ------------------------------------------------------------------------------ - -[env:codm-controller-0_6] -board = esp_wroom_02 -platform = ${common.platform_wled_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_2m512k} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp8266.build_flags} -lib_deps = ${esp8266.lib_deps} - -[env:codm-controller-0_6-rev2] -board = esp_wroom_02 -platform = ${common.platform_wled_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_4m1m} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp8266.build_flags} -lib_deps = ${esp8266.lib_deps} - -# ------------------------------------------------------------------------------ -# EleksTube-IPS -# ------------------------------------------------------------------------------ -[env:elekstube_ips] -board = esp32dev -platform = ${esp32.platform} -platform_packages = ${esp32.platform_packages} -upload_speed = 921600 -build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED - -D USERMOD_RTC - -D USERMOD_ELEKSTUBE_IPS - -D DATA_PINS=12 - -D RLYPIN=27 - -D BTNPIN=34 - -D DEFAULT_LED_COUNT=6 - # Display config - -D ST7789_DRIVER - -D TFT_WIDTH=135 - -D TFT_HEIGHT=240 - -D CGRAM_OFFSET - -D TFT_SDA_READ - -D TFT_MOSI=23 - -D TFT_SCLK=18 - -D TFT_DC=25 - -D TFT_RST=26 - -D SPI_FREQUENCY=40000000 - -D USER_SETUP_LOADED -monitor_filters = esp32_exception_decoder -lib_deps = - ${esp32.lib_deps} - TFT_eSPI @ ^2.3.70 -board_build.partitions = ${esp32.default_partitions} +[env:esp32dev] +build_flags = + ${env.build_flags} + -D DEFAULT_LED_COUNT=1 + -D DEFAULT_LED_TYPE=TYPE_AN1615 + -D DEFAULT_LED_PIN=32 diff --git a/tools/AutoCubeMap.xlsx b/tools/AutoCubeMap.xlsx new file mode 100644 index 0000000000..b3f5cee2ad Binary files /dev/null and b/tools/AutoCubeMap.xlsx differ diff --git a/tools/cdata.js b/tools/cdata.js index c5d3c6aa52..1381cd240f 100644 --- a/tools/cdata.js +++ b/tools/cdata.js @@ -370,12 +370,6 @@ const char PAGE_dmxmap[] PROGMEM = R"=====()====="; name: "PAGE_update", method: "gzip", filter: "html-minify", - mangle: (str) => - str - .replace( - /function GetV().*\<\/script\>/gms, - "" - ) }, { file: "welcome.htm", diff --git a/usermods/BME280_v2/usermod_bme280.h b/usermods/BME280_v2/usermod_bme280.h index 9168f42291..aa6d003f3d 100644 --- a/usermods/BME280_v2/usermod_bme280.h +++ b/usermods/BME280_v2/usermod_bme280.h @@ -444,6 +444,7 @@ class UsermodBME280 : public Usermod configComplete &= getJsonValue(top[F("PublishAlways")], PublishAlways, false); configComplete &= getJsonValue(top[F("UseCelsius")], UseCelsius, true); configComplete &= getJsonValue(top[F("HomeAssistantDiscovery")], HomeAssistantDiscovery, false); + tempScale = UseCelsius ? "°C" : "°F"; DEBUG_PRINT(FPSTR(_name)); if (!initDone) { diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index ad449fc83a..ea8d214455 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -7,10 +7,6 @@ #include #include -#ifdef WLED_ENABLE_DMX - #error This audio reactive usermod is not compatible with DMX Out. -#endif - #endif #if defined(ARDUINO_ARCH_ESP32) && (defined(WLED_DEBUG) || defined(SR_DEBUG)) @@ -75,7 +71,7 @@ static uint8_t soundAgc = 0; // Automagic gain control: 0 - n //static float volumeSmth = 0.0f; // either sampleAvg or sampleAgc depending on soundAgc; smoothed sample static float FFT_MajorPeak = 1.0f; // FFT: strongest (peak) frequency static float FFT_Magnitude = 0.0f; // FFT: volume (magnitude) of peak frequency -static bool samplePeak = false; // Boolean flag for peak - used in effects. Responding routine may reset this flag. Auto-reset after strip.getMinShowDelay() +static bool samplePeak = false; // Boolean flag for peak - used in effects. Responding routine may reset this flag. Auto-reset after strip.getFrameTime() static bool udpSamplePeak = false; // Boolean flag for peak. Set at the same time as samplePeak, but reset by transmitAudioData static unsigned long timeOfPeak = 0; // time of last sample peak detection. static uint8_t fftResult[NUM_GEQ_CHANNELS]= {0};// Our calculated freq. channel result table to be used by effects @@ -536,8 +532,8 @@ static void detectSamplePeak(void) { #endif static void autoResetPeak(void) { - uint16_t MinShowDelay = MAX(50, strip.getMinShowDelay()); // Fixes private class variable compiler error. Unsure if this is the correct way of fixing the root problem. -THATDONFC - if (millis() - timeOfPeak > MinShowDelay) { // Auto-reset of samplePeak after a complete frame has passed. + uint16_t peakDelay = max(uint16_t(50), strip.getFrameTime()); + if (millis() - timeOfPeak > peakDelay) { // Auto-reset of samplePeak after at least one complete frame has passed. samplePeak = false; if (audioSyncEnabled == 0) udpSamplePeak = false; // this is normally reset by transmitAudioData } @@ -1225,7 +1221,6 @@ class AudioReactive : public Usermod { #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) // ADC over I2S is only possible on "classic" ESP32 case 0: - default: DEBUGSR_PRINTLN(F("AR: Analog Microphone (left channel only).")); audioSource = new I2SAdcSource(SAMPLE_RATE, BLOCK_SIZE); delay(100); @@ -1233,6 +1228,13 @@ class AudioReactive : public Usermod { if (audioSource) audioSource->initialize(audioPin); break; #endif + + case 255: // 255 = -1 = no audio source + // falls through to default + default: + if (audioSource) delete audioSource; audioSource = nullptr; + enabled = false; + break; } delay(250); // give microphone enough time to initialise @@ -1319,7 +1321,7 @@ class AudioReactive : public Usermod { disableSoundProcessing = true; } else { #if defined(ARDUINO_ARCH_ESP32) && defined(WLED_DEBUG) - if ((disableSoundProcessing == true) && (audioSyncEnabled == 0) && audioSource->isInitialized()) { // we just switched to "enabled" + if ((disableSoundProcessing == true) && (audioSyncEnabled == 0) && audioSource && audioSource->isInitialized()) { // we just switched to "enabled" DEBUG_PRINTLN(F("[AR userLoop] realtime mode ended - audio processing resumed.")); DEBUG_PRINTF_P(PSTR(" RealtimeMode = %d; RealtimeOverride = %d\n"), int(realtimeMode), int(realtimeOverride)); } @@ -1331,7 +1333,7 @@ class AudioReactive : public Usermod { if (audioSyncEnabled & 0x02) disableSoundProcessing = true; // make sure everything is disabled IF in audio Receive mode if (audioSyncEnabled & 0x01) disableSoundProcessing = false; // keep running audio IF we're in audio Transmit mode #ifdef ARDUINO_ARCH_ESP32 - if (!audioSource->isInitialized()) disableSoundProcessing = true; // no audio source + if (!audioSource || !audioSource->isInitialized()) disableSoundProcessing = true; // no audio source // Only run the sampling code IF we're not in Receive mode or realtime mode diff --git a/usermods/rgb-rotary-encoder/readme.md b/usermods/rgb-rotary-encoder/readme.md index ba5aad4df7..6531791799 100644 --- a/usermods/rgb-rotary-encoder/readme.md +++ b/usermods/rgb-rotary-encoder/readme.md @@ -9,7 +9,7 @@ The actual / original code that controls the LED modes is from Adam Zeloof. I ta It was quite a bit more work than I hoped, but I got there eventually :) ## Requirements -* "ESP Rotary" by Lennart Hennigs, v1.5.0 or higher: https://github.com/LennartHennigs/ESPRotary +* "ESP Rotary" by Lennart Hennigs, v2.1.1 or higher: https://github.com/LennartHennigs/ESPRotary ## Usermod installation Simply copy the below block (build task) to your `platformio_override.ini` and compile WLED using this new build task. Or use an existing one and add the buildflag `-D RGB_ROTARY_ENCODER`. @@ -20,7 +20,7 @@ ESP32: extends = env:esp32dev build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 -D RGB_ROTARY_ENCODER lib_deps = ${esp32.lib_deps} - lennarthennigs/ESP Rotary@^1.5.0 + lennarthennigs/ESP Rotary@^2.1.1 ``` ESP8266 / D1 Mini: @@ -29,7 +29,7 @@ ESP8266 / D1 Mini: extends = env:d1_mini build_flags = ${common.build_flags_esp8266} -D RGB_ROTARY_ENCODER lib_deps = ${esp8266.lib_deps} - lennarthennigs/ESP Rotary@^1.5.0 + lennarthennigs/ESP Rotary@^2.1.1 ``` ## How to connect the board to your ESP diff --git a/usermods/usermod_rotary_brightness_color/usermod_rotary_brightness_color.h b/usermods/usermod_rotary_brightness_color/usermod_rotary_brightness_color.h index 85a9a16054..c3e1ce17b2 100644 --- a/usermods/usermod_rotary_brightness_color/usermod_rotary_brightness_color.h +++ b/usermods/usermod_rotary_brightness_color/usermod_rotary_brightness_color.h @@ -95,9 +95,9 @@ class RotaryEncoderBrightnessColor : public Usermod } else { - fastled_col.red = col[0]; - fastled_col.green = col[1]; - fastled_col.blue = col[2]; + fastled_col.red = colPri[0]; + fastled_col.green = colPri[1]; + fastled_col.blue = colPri[2]; prim_hsv = rgb2hsv_approximate(fastled_col); new_val = (int16_t)prim_hsv.h + fadeAmount; if (new_val > 255) @@ -106,9 +106,9 @@ class RotaryEncoderBrightnessColor : public Usermod new_val += 255; // roll-over if smaller than 0 prim_hsv.h = (byte)new_val; hsv2rgb_rainbow(prim_hsv, fastled_col); - col[0] = fastled_col.red; - col[1] = fastled_col.green; - col[2] = fastled_col.blue; + colPri[0] = fastled_col.red; + colPri[1] = fastled_col.green; + colPri[2] = fastled_col.blue; } } else if (Enc_B == LOW) @@ -120,9 +120,9 @@ class RotaryEncoderBrightnessColor : public Usermod } else { - fastled_col.red = col[0]; - fastled_col.green = col[1]; - fastled_col.blue = col[2]; + fastled_col.red = colPri[0]; + fastled_col.green = colPri[1]; + fastled_col.blue = colPri[2]; prim_hsv = rgb2hsv_approximate(fastled_col); new_val = (int16_t)prim_hsv.h - fadeAmount; if (new_val > 255) @@ -131,9 +131,9 @@ class RotaryEncoderBrightnessColor : public Usermod new_val += 255; // roll-over if smaller than 0 prim_hsv.h = (byte)new_val; hsv2rgb_rainbow(prim_hsv, fastled_col); - col[0] = fastled_col.red; - col[1] = fastled_col.green; - col[2] = fastled_col.blue; + colPri[0] = fastled_col.red; + colPri[1] = fastled_col.green; + colPri[2] = fastled_col.blue; } } //call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification) diff --git a/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h b/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h index 383c1193eb..9533fa21bf 100644 --- a/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h +++ b/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h @@ -518,7 +518,7 @@ void RotaryEncoderUIUsermod::setup() loopTime = millis(); - currentCCT = (approximateKelvinFromRGB(RGBW32(col[0], col[1], col[2], col[3])) - 1900) >> 5; + currentCCT = (approximateKelvinFromRGB(RGBW32(colPri[0], colPri[1], colPri[2], colPri[3])) - 1900) >> 5; if (!initDone) sortModesAndPalettes(); @@ -920,17 +920,17 @@ void RotaryEncoderUIUsermod::changeHue(bool increase){ display->updateRedrawTime(); #endif currentHue1 = max(min((increase ? currentHue1+fadeAmount : currentHue1-fadeAmount), 255), 0); - colorHStoRGB(currentHue1*256, currentSat1, col); + colorHStoRGB(currentHue1*256, currentSat1, colPri); stateChanged = true; if (applyToAll) { for (unsigned i=0; iupdateRedrawTime(); #endif currentSat1 = max(min((increase ? currentSat1+fadeAmount : currentSat1-fadeAmount), 255), 0); - colorHStoRGB(currentHue1*256, currentSat1, col); + colorHStoRGB(currentHue1*256, currentSat1, colPri); if (applyToAll) { for (unsigned i=0; i> 15; - unsigned rem = 0; - rem = (prog * SEGLEN) * 2; //mod 0xFFFF + uint16_t rem = (prog * SEGLEN) * 2; //mod 0xFFFF by truncating rem /= (SEGMENT.intensity +1); if (rem > 255) rem = 255; @@ -581,7 +580,7 @@ uint16_t mode_twinkle(void) { SEGENV.step = it; } - unsigned PRNG16 = SEGENV.aux1; + uint16_t PRNG16 = SEGENV.aux1; for (unsigned i = 0; i < SEGENV.aux0; i++) { @@ -600,11 +599,12 @@ static const char _data_FX_MODE_TWINKLE[] PROGMEM = "Twinkle@!,!;!,!;!;;m12=0"; * Dissolve function */ uint16_t dissolve(uint32_t color) { - unsigned dataSize = (SEGLEN+7) >> 3; //1 bit per LED + unsigned dataSize = sizeof(uint32_t) * SEGLEN; if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + uint32_t* pixels = reinterpret_cast(SEGENV.data); if (SEGENV.call == 0) { - memset(SEGMENT.data, 0xFF, dataSize); // start by fading pixels up + for (unsigned i = 0; i < SEGLEN; i++) pixels[i] = SEGCOLOR(1); SEGENV.aux0 = 1; } @@ -612,33 +612,26 @@ uint16_t dissolve(uint32_t color) { if (random8() <= SEGMENT.intensity) { for (size_t times = 0; times < 10; times++) { //attempt to spawn a new pixel 10 times unsigned i = random16(SEGLEN); - unsigned index = i >> 3; - unsigned bitNum = i & 0x07; - bool fadeUp = bitRead(SEGENV.data[index], bitNum); if (SEGENV.aux0) { //dissolve to primary/palette - if (fadeUp) { - if (color == SEGCOLOR(0)) { - SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); - } else { - SEGMENT.setPixelColor(i, color); - } - bitWrite(SEGENV.data[index], bitNum, false); + if (pixels[i] == SEGCOLOR(1)) { + pixels[i] = color == SEGCOLOR(0) ? SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0) : color; break; //only spawn 1 new pixel per frame per 50 LEDs } } else { //dissolve to secondary - if (!fadeUp) { - SEGMENT.setPixelColor(i, SEGCOLOR(1)); break; - bitWrite(SEGENV.data[index], bitNum, true); + if (pixels[i] != SEGCOLOR(1)) { + pixels[i] = SEGCOLOR(1); + break; } } } } } + // fix for #4401 + for (unsigned i = 0; i < SEGLEN; i++) SEGMENT.setPixelColor(i, pixels[i]); if (SEGENV.step > (255 - SEGMENT.speed) + 15U) { SEGENV.aux0 = !SEGENV.aux0; SEGENV.step = 0; - memset(SEGMENT.data, (SEGENV.aux0 ? 0xFF : 0), dataSize); // switch fading } else { SEGENV.step++; } @@ -1097,7 +1090,7 @@ uint16_t mode_running_random(void) { unsigned z = it % zoneSize; bool nzone = (!z && it != SEGENV.aux1); - for (unsigned i=SEGLEN-1; i > 0; i--) { + for (int i=SEGLEN-1; i >= 0; i--) { if (nzone || z >= zoneSize) { unsigned lastrand = PRNG16 >> 8; int16_t diff = 0; @@ -1441,7 +1434,7 @@ uint16_t mode_fairy() { if (z == zones-1) flashersInZone = numFlashers-(flashersInZone*(zones-1)); for (unsigned f = firstFlasher; f < firstFlasher + flashersInZone; f++) { - unsigned stateTime = now16 - flashers[f].stateStart; + unsigned stateTime = uint16_t(now16 - flashers[f].stateStart); //random on/off time reached, switch state if (stateTime > flashers[f].stateDur * 10) { flashers[f].stateOn = !flashers[f].stateOn; @@ -1500,7 +1493,7 @@ uint16_t mode_fairytwinkle() { unsigned maxDur = riseFallTime/100 + ((255 - SEGMENT.intensity) >> 2) + 13 + ((255 - SEGMENT.intensity) >> 1); for (int f = 0; f < SEGLEN; f++) { - unsigned stateTime = now16 - flashers[f].stateStart; + uint16_t stateTime = now16 - flashers[f].stateStart; //random on/off time reached, switch state if (stateTime > flashers[f].stateDur * 100) { flashers[f].stateOn = !flashers[f].stateOn; @@ -1653,8 +1646,8 @@ static const char _data_FX_MODE_TRICOLOR_WIPE[] PROGMEM = "Tri Wipe@!;1,2,3;!"; * Modified by Aircoookie */ uint16_t mode_tricolor_fade(void) { - unsigned counter = strip.now * ((SEGMENT.speed >> 3) +1); - uint16_t prog = (counter * 768) >> 16; + uint16_t counter = strip.now * ((SEGMENT.speed >> 3) +1); + uint32_t prog = (counter * 768) >> 16; uint32_t color1 = 0, color2 = 0; unsigned stage = 0; @@ -1745,7 +1738,7 @@ uint16_t mode_random_chase(void) { uint32_t color = SEGENV.step; random16_set_seed(SEGENV.aux0); - for (unsigned i = SEGLEN -1; i > 0; i--) { + for (int i = SEGLEN -1; i >= 0; i--) { uint8_t r = random8(6) != 0 ? (color >> 16 & 0xFF) : random8(); uint8_t g = random8(6) != 0 ? (color >> 8 & 0xFF) : random8(); uint8_t b = random8(6) != 0 ? (color & 0xFF) : random8(); @@ -1798,7 +1791,7 @@ uint16_t mode_oscillate(void) { // if the counter has increased, move the oscillator by the random step if (it != SEGENV.step) oscillators[i].pos += oscillators[i].dir * oscillators[i].speed; oscillators[i].size = SEGLEN/(3+SEGMENT.intensity/8); - if((oscillators[i].dir == -1) && (oscillators[i].pos <= 0)) { + if((oscillators[i].dir == -1) && (oscillators[i].pos > SEGLEN << 1)) { // use integer overflow oscillators[i].pos = 0; oscillators[i].dir = 1; // make bigger steps for faster speeds @@ -1814,8 +1807,8 @@ uint16_t mode_oscillate(void) { for (unsigned i = 0; i < SEGLEN; i++) { uint32_t color = BLACK; for (unsigned j = 0; j < numOscillators; j++) { - if(i >= (unsigned)oscillators[j].pos - oscillators[j].size && i <= oscillators[j].pos + oscillators[j].size) { - color = (color == BLACK) ? SEGCOLOR(j) : color_blend(color, SEGCOLOR(j), 128); + if((int)i >= (int)oscillators[j].pos - oscillators[j].size && i <= oscillators[j].pos + oscillators[j].size) { + color = (color == BLACK) ? SEGCOLOR(j) : color_blend(color, SEGCOLOR(j), uint8_t(128)); } } SEGMENT.setPixelColor(i, color); @@ -2003,7 +1996,7 @@ uint16_t mode_palette() { const mathType sourceX = xtSinTheta + ytCosTheta + centerX; // The computation was scaled just right so that the result should always be in range [0, maxXOut], but enforce this anyway // to account for imprecision. Then scale it so that the range is [0, 255], which we can use with the palette. - int colorIndex = (std::min(std::max(sourceX, mathType(0)), maxXOut * sInt16Scale) * 255) / (sInt16Scale * maxXOut); + int colorIndex = (std::min(std::max(sourceX, mathType(0)), maxXOut * sInt16Scale) * wideMathType(255)) / (sInt16Scale * maxXOut); // inputSize determines by how much we want to scale the palette: // values < 128 display a fraction of a palette, // values > 128 display multiple palettes. @@ -2565,11 +2558,11 @@ static CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat) { // Overall twinkle speed (changed) unsigned ticks = ms / SEGENV.aux0; - unsigned fastcycle8 = ticks; - unsigned slowcycle16 = (ticks >> 8) + salt; + unsigned fastcycle8 = uint8_t(ticks); + uint16_t slowcycle16 = (ticks >> 8) + salt; slowcycle16 += sin8_t(slowcycle16); slowcycle16 = (slowcycle16 * 2053) + 1384; - unsigned slowcycle8 = (slowcycle16 & 0xFF) + (slowcycle16 >> 8); + uint8_t slowcycle8 = (slowcycle16 & 0xFF) + (slowcycle16 >> 8); // Overall twinkle density. // 0 (NONE lit) to 8 (ALL lit at once). @@ -3920,7 +3913,7 @@ uint16_t mode_percent(void) { return FRAMETIME; } -static const char _data_FX_MODE_PERCENT[] PROGMEM = "Percent@,% of fill,,,,One color;!,!;!"; +static const char _data_FX_MODE_PERCENT[] PROGMEM = "Percent@!,% of fill,,,,One color;!,!;!"; /* @@ -5170,7 +5163,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: neighbors++; bool colorFound = false; int k; - for (k=0; k<9 && colorsCount[i].count != 0; k++) + for (k=0; k<9 && colorsCount[k].count != 0; k++) if (colorsCount[k].color == prevLeds[xy]) { colorsCount[k].count++; colorFound = true; @@ -6639,14 +6632,16 @@ static const char _data_FX_MODE_JUGGLES[] PROGMEM = "Juggles@!,# of balls;!,!;!; // * MATRIPIX // ////////////////////// uint16_t mode_matripix(void) { // Matripix. By Andrew Tuline. - if (SEGLEN == 1) return mode_static(); - // even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment + // effect can work on single pixels, we just lose the shifting effect + unsigned dataSize = sizeof(uint32_t) * SEGLEN; + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + uint32_t* pixels = reinterpret_cast(SEGENV.data); um_data_t *um_data = getAudioData(); int volumeRaw = *(int16_t*)um_data->u_data[1]; if (SEGENV.call == 0) { - SEGMENT.fill(BLACK); + for (unsigned i = 0; i < SEGLEN; i++) pixels[i] = BLACK; // may not be needed as resetIfRequired() clears buffer } uint8_t secondHand = micros()/(256-SEGMENT.speed)/500 % 16; @@ -6654,8 +6649,14 @@ uint16_t mode_matripix(void) { // Matripix. By Andrew Tuline. SEGENV.aux0 = secondHand; int pixBri = volumeRaw * SEGMENT.intensity / 64; - for (int i = 0; i < SEGLEN-1; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // shift left - SEGMENT.setPixelColor(SEGLEN-1, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(strip.now, false, PALETTE_SOLID_WRAP, 0), pixBri)); + unsigned k = SEGLEN-1; + // loop will not execute if SEGLEN equals 1 + for (unsigned i = 0; i < k; i++) { + pixels[i] = pixels[i+1]; // shift left + SEGMENT.setPixelColor(i, pixels[i]); + } + pixels[k] = color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(strip.now, false, PALETTE_SOLID_WRAP, 0), pixBri); + SEGMENT.setPixelColor(k, pixels[k]); } return FRAMETIME; @@ -7283,8 +7284,11 @@ static const char _data_FX_MODE_ROCKTAVES[] PROGMEM = "Rocktaves@;!,!;!;01f;m12= // Combines peak detection with FFT_MajorPeak and FFT_Magnitude. uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tuline // effect can work on single pixels, we just lose the shifting effect - - um_data_t *um_data = getAudioData(); + unsigned dataSize = sizeof(uint32_t) * SEGLEN; + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + uint32_t* pixels = reinterpret_cast(SEGENV.data); + + um_data_t *um_data = getAudioData(); uint8_t samplePeak = *(uint8_t*)um_data->u_data[3]; float FFT_MajorPeak = *(float*) um_data->u_data[4]; uint8_t *maxVol = (uint8_t*)um_data->u_data[6]; @@ -7294,7 +7298,7 @@ uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tulin if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception) if (SEGENV.call == 0) { - SEGMENT.fill(BLACK); + for (unsigned i = 0; i < SEGLEN; i++) pixels[i] = BLACK; // may not be needed as resetIfRequired() clears buffer SEGENV.aux0 = 255; SEGMENT.custom1 = *binNum; SEGMENT.custom2 = *maxVol * 2; @@ -7311,13 +7315,18 @@ uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tulin uint8_t pixCol = (log10f(FFT_MajorPeak) - 2.26f) * 150; // 22Khz sampling - log10 frequency range is from 2.26 (182hz) to 3.967 (9260hz). Let's scale accordingly. if (FFT_MajorPeak < 182.0f) pixCol = 0; // handle underflow + unsigned k = SEGLEN-1; if (samplePeak) { - SEGMENT.setPixelColor(SEGLEN-1, CHSV(92,92,92)); + pixels[k] = (uint32_t)CRGB(CHSV(92,92,92)); } else { - SEGMENT.setPixelColor(SEGLEN-1, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(pixCol+SEGMENT.intensity, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude)); + pixels[k] = color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(pixCol+SEGMENT.intensity, false, PALETTE_SOLID_WRAP, 0), (uint8_t)my_magnitude); } + SEGMENT.setPixelColor(k, pixels[k]); // loop will not execute if SEGLEN equals 1 - for (int i = 0; i < SEGLEN-1; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // shift left + for (unsigned i = 0; i < k; i++) { + pixels[i] = pixels[i+1]; // shift left + SEGMENT.setPixelColor(i, pixels[i]); + } } return FRAMETIME; diff --git a/wled00/FX.h b/wled00/FX.h index 5451615464..e9d447e6ff 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -1,3 +1,4 @@ +#pragma once /* WS2812FX.h - Library for WS2812 LED effects. Harm Aldick - 2016 @@ -8,12 +9,15 @@ Adapted from code originally licensed under the MIT license Modified for WLED + + Segment class/struct (c) 2022 Blaz Kristan (@blazoncek) */ #ifndef WS2812FX_h #define WS2812FX_h #include +#include "wled.h" #include "const.h" @@ -46,6 +50,14 @@ #define WLED_FPS 42 #define FRAMETIME_FIXED (1000/WLED_FPS) #define FRAMETIME strip.getFrameTime() +#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2) + #define MIN_FRAME_DELAY 2 // minimum wait between repaints, to keep other functions like WiFi alive +#elif defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) + #define MIN_FRAME_DELAY 3 // S2/C3 are slower than normal esp32, and only have one core +#else + #define MIN_FRAME_DELAY 8 // 8266 legacy MIN_SHOW_DELAY +#endif +#define FPS_UNLIMITED 0 // FPS calculation (can be defined as compile flag for debugging) #ifndef FPS_CALC_AVG @@ -59,26 +71,21 @@ /* each segment uses 82 bytes of SRAM memory, so if you're application fails because of insufficient memory, decreasing MAX_NUM_SEGMENTS may help */ #ifdef ESP8266 - #define MAX_NUM_SEGMENTS 16 + #define MAX_NUM_SEGMENTS 16 /* How much data bytes all segments combined may allocate */ #define MAX_SEGMENT_DATA 5120 +#elif defined(CONFIG_IDF_TARGET_ESP32S2) + #define MAX_NUM_SEGMENTS 20 + #define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*512) // 10k by default (S2 is short on free RAM) #else - #ifndef MAX_NUM_SEGMENTS - #define MAX_NUM_SEGMENTS 32 - #endif - #if defined(ARDUINO_ARCH_ESP32S2) - #define MAX_SEGMENT_DATA MAX_NUM_SEGMENTS*768 // 24k by default (S2 is short on free RAM) - #else - #define MAX_SEGMENT_DATA MAX_NUM_SEGMENTS*1280 // 40k by default - #endif + #define MAX_NUM_SEGMENTS 32 // warning: going beyond 32 may consume too much RAM for stable operation + #define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*1280) // 40k by default #endif /* How much data bytes each segment should max allocate to leave enough space for other segments, assuming each segment uses the same amount of data. 256 for ESP8266, 640 for ESP32. */ #define FAIR_DATA_PER_SEG (MAX_SEGMENT_DATA / strip.getMaxSegments()) -#define MIN_SHOW_DELAY (_frametime < 16 ? 8 : 15) - #define NUM_COLORS 3 /* number of colors per segment */ #define SEGMENT strip._segments[strip.getCurrSegmentId()] #define SEGENV strip._segments[strip.getCurrSegmentId()] @@ -524,6 +531,9 @@ typedef struct Segment { inline uint16_t length() const { return width() * height(); } // segment length (count) in physical pixels inline uint16_t groupLength() const { return grouping + spacing; } inline uint8_t getLightCapabilities() const { return _capabilities; } + inline void deactivate() { setGeometry(0,0); } + inline Segment &clearName() { if (name) free(name); name = nullptr; return *this; } + inline Segment &setName(const String &name) { return setName(name.c_str()); } inline static uint16_t getUsedSegmentData() { return _usedSegmentData; } inline static void addUsedSegmentData(int len) { _usedSegmentData += len; } @@ -533,14 +543,15 @@ typedef struct Segment { static void handleRandomPalette(); inline static const CRGBPalette16 &getCurrentPalette() { return Segment::_currentPalette; } - void setUp(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1); + void setGeometry(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1, uint8_t m12 = 0); Segment &setColor(uint8_t slot, uint32_t c); Segment &setCCT(uint16_t k); Segment &setOpacity(uint8_t o); Segment &setOption(uint8_t n, bool val); Segment &setMode(uint8_t fx, bool loadDefaults = false); Segment &setPalette(uint8_t pal); - uint8_t differs(Segment& b) const; + Segment &setName(const char* name); + uint8_t differs(const Segment& b) const; void refreshLightCapabilities(); // runtime data functions @@ -748,6 +759,7 @@ class WS2812FX { // 96 bytes customMappingTable(nullptr), customMappingSize(0), _lastShow(0), + _lastServiceShow(0), _segment_index(0), _mainSegment(0) { @@ -846,7 +858,7 @@ class WS2812FX { // 96 bytes getMappedPixelIndex(uint16_t index) const; inline uint16_t getFrameTime() const { return _frametime; } // returns amount of time a frame should take (in ms) - inline uint16_t getMinShowDelay() const { return MIN_SHOW_DELAY; } // returns minimum amount of time strip.service() can be delayed (constant) + inline uint16_t getMinShowDelay() const { return MIN_FRAME_DELAY; } // returns minimum amount of time strip.service() can be delayed (constant) inline uint16_t getLength() const { return _length; } // returns actual amount of LEDs on a strip (2D matrix may have less LEDs than W*H) inline uint16_t getTransition() const { return _transitionDur; } // returns currently set transition time (in ms) @@ -958,6 +970,7 @@ class WS2812FX { // 96 bytes uint16_t customMappingSize; unsigned long _lastShow; + unsigned long _lastServiceShow; uint8_t _segment_index; uint8_t _mainSegment; diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 7177ca89e8..ce6f53a2cd 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -438,37 +438,44 @@ void Segment::setCurrentPalette() { // relies on WS2812FX::service() to call it for each frame void Segment::handleRandomPalette() { + uint16_t time_ms = millis(); + uint16_t time_s = millis()/1000U; // is it time to generate a new palette? - if ((uint16_t)((uint16_t)(millis() / 1000U) - _lastPaletteChange) > randomPaletteChangeTime){ - _newRandomPalette = useHarmonicRandomPalette ? generateHarmonicRandomPalette(_randomPalette) : generateRandomPalette(); - _lastPaletteChange = (uint16_t)(millis() / 1000U); - _lastPaletteBlend = (uint16_t)((uint16_t)millis() - 512); // starts blending immediately + if ((uint16_t)(time_s - _lastPaletteChange) > randomPaletteChangeTime) { + _newRandomPalette = useHarmonicRandomPalette ? generateHarmonicRandomPalette(_randomPalette) : generateRandomPalette(); + _lastPaletteChange = time_s; + _lastPaletteBlend = time_ms - 512; // starts blending immediately } // if palette transitions is enabled, blend it according to Transition Time (if longer than minimum given by service calls) if (strip.paletteFade) { // assumes that 128 updates are sufficient to blend a palette, so shift by 7 (can be more, can be less) // in reality there need to be 255 blends to fully blend two entirely different palettes - if ((uint16_t)((uint16_t)millis() - _lastPaletteBlend) < strip.getTransition() >> 7) return; // not yet time to fade, delay the update + if ((uint16_t)(time_ms - _lastPaletteBlend) < strip.getTransition() >> 7) return; // not yet time to fade, delay the update _lastPaletteBlend = (uint16_t)millis(); } nblendPaletteTowardPalette(_randomPalette, _newRandomPalette, 48); } // segId is given when called from network callback, changes are queued if that segment is currently in its effect function -void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y) { +void Segment::setGeometry(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y, uint8_t m12) { // return if neither bounds nor grouping have changed bool boundsUnchanged = (start == i1 && stop == i2); #ifndef WLED_DISABLE_2D if (Segment::maxHeight>1) boundsUnchanged &= (startY == i1Y && stopY == i2Y); // 2D #endif + + m12 = constrain(m12, 0, 7); + if (stop && (spc > 0 || m12 != map1D2D)) fill(BLACK); + if (m12 != map1D2D) map1D2D = m12; +/* if (boundsUnchanged && (!grp || (grouping == grp && spacing == spc)) - && (ofs == UINT16_MAX || ofs == offset)) return; - + && (m12 == map1D2D) + ) return; +*/ stateChanged = true; // send UDP/WS broadcast - if (stop) fill(BLACK); // turn old segment range off (clears pixels if changing spacing) if (grp) { // prevent assignment of 0 grouping = grp; spacing = spc; @@ -478,12 +485,9 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t } if (ofs < UINT16_MAX) offset = ofs; - DEBUG_PRINT(F("setUp segment: ")); DEBUG_PRINT(i1); - DEBUG_PRINT(','); DEBUG_PRINT(i2); - DEBUG_PRINT(F(" -> ")); DEBUG_PRINT(i1Y); - DEBUG_PRINT(','); DEBUG_PRINTLN(i2Y); - markForReset(); + DEBUG_PRINTF_P(PSTR("Segment geometry: %d,%d -> %d,%d\n"), (int)i1, (int)i2, (int)i1Y, (int)i2Y); if (boundsUnchanged) return; + markForReset(); // apply change immediately if (i2 <= i1) { //disable segment @@ -601,6 +605,20 @@ Segment &Segment::setPalette(uint8_t pal) { return *this; } +Segment &Segment::setName(const char *newName) { + if (newName) { + const int newLen = min(strlen(newName), (size_t)WLED_MAX_SEGNAME_LEN); + if (newLen) { + if (name) name = static_cast(realloc(name, newLen+1)); + else name = static_cast(malloc(newLen+1)); + if (name) strlcpy(name, newName, newLen+1); + name[newLen] = 0; + return *this; + } + } + return clearName(); +} + // 2D matrix unsigned IRAM_ATTR Segment::virtualWidth() const { unsigned groupLen = groupLength(); @@ -951,7 +969,7 @@ uint32_t IRAM_ATTR_YN Segment::getPixelColor(int i) const return strip.getPixelColor(i); } -uint8_t Segment::differs(Segment& b) const { +uint8_t Segment::differs(const Segment& b) const { uint8_t d = 0; if (start != b.start) d |= SEG_DIFFERS_BOUNDS; if (stop != b.stop) d |= SEG_DIFFERS_BOUNDS; @@ -1047,36 +1065,25 @@ void Segment::fade_out(uint8_t rate) { const int cols = is2D() ? virtualWidth() : virtualLength(); const int rows = virtualHeight(); // will be 1 for 1D - rate = (255-rate) >> 1; - float mappedRate = 1.0f / (float(rate) + 1.1f); - - uint32_t color = colors[1]; // SEGCOLOR(1); // target color - int w2 = W(color); - int r2 = R(color); - int g2 = G(color); - int b2 = B(color); + rate = (256-rate) >> 1; + const int mappedRate = 256 / (rate + 1); for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) { - color = is2D() ? getPixelColorXY(x, y) : getPixelColor(x); + uint32_t color = is2D() ? getPixelColorXY(x, y) : getPixelColor(x); if (color == colors[1]) continue; // already at target color - int w1 = W(color); - int r1 = R(color); - int g1 = G(color); - int b1 = B(color); - - int wdelta = (w2 - w1) * mappedRate; - int rdelta = (r2 - r1) * mappedRate; - int gdelta = (g2 - g1) * mappedRate; - int bdelta = (b2 - b1) * mappedRate; - - // if fade isn't complete, make sure delta is at least 1 (fixes rounding issues) - wdelta += (w2 == w1) ? 0 : (w2 > w1) ? 1 : -1; - rdelta += (r2 == r1) ? 0 : (r2 > r1) ? 1 : -1; - gdelta += (g2 == g1) ? 0 : (g2 > g1) ? 1 : -1; - bdelta += (b2 == b1) ? 0 : (b2 > b1) ? 1 : -1; - - if (is2D()) setPixelColorXY(x, y, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta); - else setPixelColor(x, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta); + for (int i = 0; i < 32; i += 8) { + uint8_t c2 = (colors[1]>>i); // get background channel + uint8_t c1 = (color>>i); // get foreground channel + // we can't use bitshift since we are using int + int delta = (c2 - c1) * mappedRate / 256; + // if fade isn't complete, make sure delta is at least 1 (fixes rounding issues) + if (delta == 0) delta += (c2 == c1) ? 0 : (c2 > c1) ? 1 : -1; + // stuff new value back into color + color &= ~(0xFF< maxLedsOnBus) maxLedsOnBus = bus.count; + } + } + DEBUG_PRINTF_P(PSTR("Maximum LEDs on a bus: %u\nDigital buses: %u\n"), maxLedsOnBus, digitalCount); + // we may remove 600 LEDs per bus limit when NeoPixelBus is updated beyond 2.8.3 + if (digitalCount > 1 && maxLedsOnBus <= 600 && useParallelI2S) BusManager::useParallelOutput(); // must call before creating buses + else useParallelI2S = false; // enforce single I2S + digitalCount = 0; + #endif + + // create buses/outputs + unsigned mem = 0; + for (const auto &bus : busConfigs) { + mem += bus.memUsage(Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type) ? digitalCount++ : 0); // includes global buffer + if (mem <= MAX_LED_MEMORY) { + if (BusManager::add(bus) == -1) break; + } else DEBUG_PRINTF_P(PSTR("Out of LED memory! Bus %d (%d) #%u not created."), (int)bus.type, (int)bus.count, digitalCount); + } + busConfigs.clear(); + busConfigs.shrink_to_fit(); + //if busses failed to load, add default (fresh install, FS issue, ...) if (BusManager::getNumBusses() == 0) { DEBUG_PRINTLN(F("No busses, init default")); @@ -1207,6 +1248,7 @@ void WS2812FX::finalizeInit() { unsigned prevLen = 0; unsigned pinsIndex = 0; + digitalCount = 0; for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) { uint8_t defPin[OUTPUT_MAX_PINS]; // if we have less types than requested outputs and they do not align, use last known type to set current type @@ -1271,9 +1313,11 @@ void WS2812FX::finalizeInit() { if (Bus::isPWM(dataType) || Bus::isOnOff(dataType)) count = 1; prevLen += count; BusConfig defCfg = BusConfig(dataType, defPin, start, count, DEFAULT_LED_COLOR_ORDER, false, 0, RGBW_MODE_MANUAL_ONLY, 0, useGlobalLedBuffer); + mem += defCfg.memUsage(Bus::isDigital(dataType) && !Bus::is2Pin(dataType) ? digitalCount++ : 0); if (BusManager::add(defCfg) == -1) break; } } + DEBUG_PRINTF_P(PSTR("LED buffer size: %uB/%uB\n"), mem, BusManager::memUsage()); _length = 0; for (int i=0; ibegin(); } + DEBUG_PRINTF_P(PSTR("Heap after buses: %d\n"), ESP.getFreeHeap()); Segment::maxWidth = _length; Segment::maxHeight = 1; @@ -1304,14 +1349,21 @@ void WS2812FX::finalizeInit() { void WS2812FX::service() { unsigned long nowUp = millis(); // Be aware, millis() rolls over every 49 days now = nowUp + timebase; - if (nowUp - _lastShow < MIN_SHOW_DELAY || _suspend) return; + if (_suspend) return; + unsigned long elapsed = nowUp - _lastServiceShow; + + if (elapsed <= MIN_FRAME_DELAY) return; // keep wifi alive - no matter if triggered or unlimited + if ( !_triggered && (_targetFps != FPS_UNLIMITED)) { // unlimited mode = no frametime + if (elapsed < _frametime) return; // too early for service + } + bool doShow = false; _isServicing = true; _segment_index = 0; for (segment &seg : _segments) { - if (_suspend) return; // immediately stop processing segments if suspend requested during service() + if (_suspend) break; // immediately stop processing segments if suspend requested during service() // process transition (mode changes in the middle of transition) seg.handleTransition(); @@ -1321,7 +1373,7 @@ void WS2812FX::service() { if (!seg.isActive()) continue; // last condition ensures all solid segments are updated at the same time - if (nowUp > seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC)) + if (nowUp >= seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC)) { doShow = true; unsigned frameDelay = FRAMETIME; @@ -1371,15 +1423,16 @@ void WS2812FX::service() { _triggered = false; #ifdef WLED_DEBUG - if (millis() - nowUp > _frametime) DEBUG_PRINTF_P(PSTR("Slow effects %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime); + if ((_targetFps != FPS_UNLIMITED) && (millis() - nowUp > _frametime)) DEBUG_PRINTF_P(PSTR("Slow effects %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime); #endif if (doShow) { yield(); Segment::handleRandomPalette(); // slowly transition random palette; move it into for loop when each segment has individual random palette - show(); + _lastServiceShow = nowUp; // update timestamp, for precise FPS control + if (!_suspend) show(); } #ifdef WLED_DEBUG - if (millis() - nowUp > _frametime) DEBUG_PRINTF_P(PSTR("Slow strip %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime); + if ((_targetFps != FPS_UNLIMITED) && (millis() - nowUp > _frametime)) DEBUG_PRINTF_P(PSTR("Slow strip %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime); #endif } @@ -1399,13 +1452,13 @@ void WS2812FX::show() { // avoid race condition, capture _callback value show_callback callback = _callback; if (callback) callback(); + unsigned long showNow = millis(); // some buses send asynchronously and this method will return before // all of the data has been sent. // See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods BusManager::show(); - unsigned long showNow = millis(); size_t diff = showNow - _lastShow; if (diff > 0) { // skip calculation if no time has passed @@ -1433,8 +1486,9 @@ uint16_t WS2812FX::getFps() const { } void WS2812FX::setTargetFps(uint8_t fps) { - if (fps > 0 && fps <= 120) _targetFps = fps; - _frametime = 1000 / _targetFps; + if (fps <= 250) _targetFps = fps; + if (_targetFps > 0) _frametime = 1000 / _targetFps; + else _frametime = MIN_FRAME_DELAY; // unlimited mode } void WS2812FX::setMode(uint8_t segid, uint8_t m) { @@ -1482,7 +1536,7 @@ void WS2812FX::setBrightness(uint8_t b, bool direct) { BusManager::setBrightness(b); if (!direct) { unsigned long t = millis(); - if (_segments[0].next_time > t + 22 && t - _lastShow > MIN_SHOW_DELAY) trigger(); //apply brightness change immediately if no refresh soon + if (_segments[0].next_time > t + 22 && t - _lastShow > MIN_FRAME_DELAY) trigger(); //apply brightness change immediately if no refresh soon } } @@ -1592,7 +1646,7 @@ void WS2812FX::setSegment(uint8_t segId, uint16_t i1, uint16_t i2, uint8_t group segId = getSegmentsNum()-1; // segments are added at the end of list } suspend(); - _segments[segId].setUp(i1, i2, grouping, spacing, offset, startY, stopY); + _segments[segId].setGeometry(i1, i2, grouping, spacing, offset, startY, stopY); resume(); if (segId > 0 && segId == getSegmentsNum()-1 && i2 <= i1) _segments.pop_back(); // if last segment was deleted remove it from vector } diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 5b031bebbf..0e72b28383 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -18,10 +18,12 @@ #endif #include "const.h" #include "pin_manager.h" -#include "bus_wrapper.h" #include "bus_manager.h" +#include "bus_wrapper.h" +#include extern bool cctICused; +extern bool useParallelI2S; //colors.cpp uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb); @@ -29,28 +31,6 @@ uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb); //udp.cpp uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte *buffer, uint8_t bri=255, bool isRGBW=false); -// enable additional debug output -#if defined(WLED_DEBUG_HOST) - #include "net_debug.h" - #define DEBUGOUT NetDebug -#else - #define DEBUGOUT Serial -#endif - -#ifdef WLED_DEBUG - #ifndef ESP8266 - #include - #endif - #define DEBUG_PRINT(x) DEBUGOUT.print(x) - #define DEBUG_PRINTLN(x) DEBUGOUT.println(x) - #define DEBUG_PRINTF(x...) DEBUGOUT.printf(x) - #define DEBUG_PRINTF_P(x...) DEBUGOUT.printf_P(x) -#else - #define DEBUG_PRINT(x) - #define DEBUG_PRINTLN(x) - #define DEBUG_PRINTF(x...) - #define DEBUG_PRINTF_P(x...) -#endif //color mangling macros #define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b)))) @@ -63,6 +43,7 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte bool ColorOrderMap::add(uint16_t start, uint16_t len, uint8_t colorOrder) { if (count() >= WLED_MAX_COLOR_ORDER_MAPPINGS || len == 0 || (colorOrder & 0x0F) > COL_ORDER_MAX) return false; // upper nibble contains W swap information _mappings.push_back({start,len,colorOrder}); + DEBUGBUS_PRINTF_P(PSTR("Bus: Add COM (%d,%d,%d)\n"), (int)start, (int)len, (int)colorOrder); return true; } @@ -116,12 +97,16 @@ uint32_t Bus::autoWhiteCalc(uint32_t c) const { } uint8_t *Bus::allocateData(size_t size) { - if (_data) free(_data); // should not happen, but for safety + freeData(); // should not happen, but for safety return _data = (uint8_t *)(size>0 ? calloc(size, sizeof(uint8_t)) : nullptr); } +void Bus::freeData() { + if (_data) free(_data); + _data = nullptr; +} -BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com) +BusDigital::BusDigital(const BusConfig &bc, uint8_t nr, const ColorOrderMap &com) : Bus(bc.type, bc.start, bc.autoWhite, bc.count, bc.reversed, (bc.refreshReq || bc.type == TYPE_TM1814)) , _skip(bc.skipAmount) //sacrificial pixels , _colorOrder(bc.colorOrder) @@ -129,42 +114,43 @@ BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com) , _milliAmpsMax(bc.milliAmpsMax) , _colorOrderMap(com) { - if (!isDigital(bc.type) || !bc.count) return; - if (!PinManager::allocatePin(bc.pins[0], true, PinOwner::BusDigital)) return; + DEBUGBUS_PRINTLN(F("Bus: Creating digital bus.")); + if (!isDigital(bc.type) || !bc.count) { DEBUGBUS_PRINTLN(F("Not digial or empty bus!")); return; } + if (!PinManager::allocatePin(bc.pins[0], true, PinOwner::BusDigital)) { DEBUGBUS_PRINTLN(F("Pin 0 allocated!")); return; } _frequencykHz = 0U; _pins[0] = bc.pins[0]; if (is2Pin(bc.type)) { if (!PinManager::allocatePin(bc.pins[1], true, PinOwner::BusDigital)) { cleanup(); + DEBUGBUS_PRINTLN(F("Pin 1 allocated!")); return; } _pins[1] = bc.pins[1]; _frequencykHz = bc.frequency ? bc.frequency : 2000U; // 2MHz clock if undefined } _iType = PolyBus::getI(bc.type, _pins, nr); - if (_iType == I_NONE) return; + if (_iType == I_NONE) { DEBUGBUS_PRINTLN(F("Incorrect iType!")); return; } _hasRgb = hasRGB(bc.type); _hasWhite = hasWhite(bc.type); _hasCCT = hasCCT(bc.type); - if (bc.doubleBuffer && !allocateData(bc.count * Bus::getNumberOfChannels(bc.type))) return; + if (bc.doubleBuffer && !allocateData(bc.count * Bus::getNumberOfChannels(bc.type))) { DEBUGBUS_PRINTLN(F("Buffer allocation failed!")); return; } //_buffering = bc.doubleBuffer; uint16_t lenToCreate = bc.count; if (bc.type == TYPE_WS2812_1CH_X3) lenToCreate = NUM_ICS_WS2812_1CH_3X(bc.count); // only needs a third of "RGB" LEDs for NeoPixelBus _busPtr = PolyBus::create(_iType, _pins, lenToCreate + _skip, nr); _valid = (_busPtr != nullptr); - DEBUG_PRINTF_P(PSTR("%successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u). mA=%d/%d\n"), _valid?"S":"Uns", nr, bc.count, bc.type, _pins[0], is2Pin(bc.type)?_pins[1]:255, _iType, _milliAmpsPerLed, _milliAmpsMax); + DEBUGBUS_PRINTF_P(PSTR("Bus: %successfully inited #%u (len:%u, type:%u (RGB:%d, W:%d, CCT:%d), pins:%u,%u [itype:%u] mA=%d/%d)\n"), + _valid?"S":"Uns", + (int)nr, + (int)bc.count, + (int)bc.type, + (int)_hasRgb, (int)_hasWhite, (int)_hasCCT, + (unsigned)_pins[0], is2Pin(bc.type)?(unsigned)_pins[1]:255U, + (unsigned)_iType, + (int)_milliAmpsPerLed, (int)_milliAmpsMax + ); } -//fine tune power estimation constants for your setup -//you can set it to 0 if the ESP is powered by USB and the LEDs by external -#ifndef MA_FOR_ESP - #ifdef ESP8266 - #define MA_FOR_ESP 80 //how much mA does the ESP use (Wemos D1 about 80mA) - #else - #define MA_FOR_ESP 120 //how much mA does the ESP use (ESP32 about 120mA) - #endif -#endif - //DISCLAIMER //The following function attemps to calculate the current LED power usage, //and will limit the brightness to stay below a set amperage threshold. @@ -173,7 +159,7 @@ BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com) //I am NOT to be held liable for burned down garages or houses! // To disable brightness limiter we either set output max current to 0 or single LED current to 0 -uint8_t BusDigital::estimateCurrentAndLimitBri() { +uint8_t BusDigital::estimateCurrentAndLimitBri() const { bool useWackyWS2815PowerModel = false; byte actualMilliampsPerLed = _milliAmpsPerLed; @@ -186,7 +172,7 @@ uint8_t BusDigital::estimateCurrentAndLimitBri() { actualMilliampsPerLed = 12; // from testing an actual strip } - size_t powerBudget = (_milliAmpsMax - MA_FOR_ESP/BusManager::getNumBusses()); //80/120mA for ESP power + unsigned powerBudget = (_milliAmpsMax - MA_FOR_ESP/BusManager::getNumBusses()); //80/120mA for ESP power if (powerBudget > getLength()) { //each LED uses about 1mA in standby, exclude that from power budget powerBudget -= getLength(); } else { @@ -211,26 +197,25 @@ uint8_t BusDigital::estimateCurrentAndLimitBri() { } // powerSum has all the values of channels summed (max would be getLength()*765 as white is excluded) so convert to milliAmps - busPowerSum = (busPowerSum * actualMilliampsPerLed) / 765; - _milliAmpsTotal = busPowerSum * _bri / 255; + BusDigital::_milliAmpsTotal = (busPowerSum * actualMilliampsPerLed * _bri) / (765*255); uint8_t newBri = _bri; - if (busPowerSum * _bri / 255 > powerBudget) { //scale brightness down to stay in current limit - float scale = (float)(powerBudget * 255) / (float)(busPowerSum * _bri); - if (scale >= 1.0f) return _bri; - _milliAmpsTotal = ceilf((float)_milliAmpsTotal * scale); - uint8_t scaleB = min((int)(scale * 255), 255); - newBri = unsigned(_bri * scaleB) / 256 + 1; + if (BusDigital::_milliAmpsTotal > powerBudget) { + //scale brightness down to stay in current limit + unsigned scaleB = powerBudget * 255 / BusDigital::_milliAmpsTotal; + newBri = (_bri * scaleB) / 256 + 1; + BusDigital::_milliAmpsTotal = powerBudget; + //_milliAmpsTotal = (busPowerSum * actualMilliampsPerLed * newBri) / (765*255); } return newBri; } void BusDigital::show() { - _milliAmpsTotal = 0; + BusDigital::_milliAmpsTotal = 0; if (!_valid) return; uint8_t cctWW = 0, cctCW = 0; - unsigned newBri = estimateCurrentAndLimitBri(); // will fill _milliAmpsTotal + unsigned newBri = estimateCurrentAndLimitBri(); // will fill _milliAmpsTotal (TODO: could use PolyBus::CalcTotalMilliAmpere()) if (newBri < _bri) PolyBus::setBrightness(_busPtr, _iType, newBri); // limit brightness to stay within current limits if (_data) { @@ -256,6 +241,7 @@ void BusDigital::show() { // TODO: there is an issue if CCT is calculated from RGB value (_cct==-1), we cannot do that with double buffer Bus::_cct = _data[offset+channels-1]; Bus::calculateCCT(c, cctWW, cctCW); + if (_type == TYPE_WS2812_WWA) c = RGBW32(cctWW, cctCW, 0, W(c)); // may need swapping } unsigned pix = i; if (_reversed) pix = _len - pix -1; @@ -306,9 +292,8 @@ void BusDigital::setStatusPixel(uint32_t c) { } } -void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) { +void IRAM_ATTR BusDigital::setPixelColor(unsigned pix, uint32_t c) { if (!_valid) return; - uint8_t cctWW = 0, cctCW = 0; if (hasWhite()) c = autoWhiteCalc(c); if (Bus::_cct >= 1900) c = colorBalanceFromKelvin(Bus::_cct, c); //color correction from CCT if (_data) { @@ -336,13 +321,19 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) { case 2: c = RGBW32(R(cOld), G(cOld), W(c) , 0); break; } } - if (hasCCT()) Bus::calculateCCT(c, cctWW, cctCW); - PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, (cctCW<<8) | cctWW); + uint16_t wwcw = 0; + if (hasCCT()) { + uint8_t cctWW = 0, cctCW = 0; + Bus::calculateCCT(c, cctWW, cctCW); + wwcw = (cctCW<<8) | cctWW; + if (_type == TYPE_WS2812_WWA) c = RGBW32(cctWW, cctCW, 0, W(c)); // may need swapping + } + PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, wwcw); } } // returns original color if global buffering is enabled, else returns lossly restored color from bus -uint32_t IRAM_ATTR BusDigital::getPixelColor(uint16_t pix) const { +uint32_t IRAM_ATTR BusDigital::getPixelColor(unsigned pix) const { if (!_valid) return 0; if (_data) { size_t offset = pix * getNumberOfChannels(); @@ -368,16 +359,24 @@ uint32_t IRAM_ATTR BusDigital::getPixelColor(uint16_t pix) const { case 2: c = RGBW32(b, b, b, b); break; } } + if (_type == TYPE_WS2812_WWA) { + uint8_t w = R(c) | G(c); + c = RGBW32(w, w, 0, w); + } return c; } } -uint8_t BusDigital::getPins(uint8_t* pinArray) const { +unsigned BusDigital::getPins(uint8_t* pinArray) const { unsigned numPins = is2Pin(_type) + 1; if (pinArray) for (unsigned i = 0; i < numPins; i++) pinArray[i] = _pins[i]; return numPins; } +unsigned BusDigital::getBusSize() const { + return sizeof(BusDigital) + (isOk() ? PolyBus::getDataSize(_busPtr, _iType) + (_data ? _len * getNumberOfChannels() : 0) : 0); +} + void BusDigital::setColorOrder(uint8_t colorOrder) { // upper nibble contains W swap information if ((colorOrder & 0x0F) > 5) return; @@ -400,8 +399,8 @@ std::vector BusDigital::getLEDTypes() { {TYPE_WS2805, "D", PSTR("WS2805 RGBCW")}, {TYPE_SM16825, "D", PSTR("SM16825 RGBCW")}, {TYPE_WS2812_1CH_X3, "D", PSTR("WS2811 White")}, - //{TYPE_WS2812_2CH_X3, "D", PSTR("WS2811 CCT")}, // not implemented - //{TYPE_WS2812_WWA, "D", PSTR("WS2811 WWA")}, // not implemented + //{TYPE_WS2812_2CH_X3, "D", PSTR("WS281x CCT")}, // not implemented + {TYPE_WS2812_WWA, "D", PSTR("WS281x WWA")}, // amber ignored {TYPE_WS2801, "2P", PSTR("WS2801")}, {TYPE_APA102, "2P", PSTR("APA102")}, {TYPE_LPD8806, "2P", PSTR("LPD8806")}, @@ -416,12 +415,13 @@ void BusDigital::begin() { } void BusDigital::cleanup() { - DEBUG_PRINTLN(F("Digital Cleanup.")); + DEBUGBUS_PRINTLN(F("Digital Cleanup.")); PolyBus::cleanup(_busPtr, _iType); _iType = I_NONE; _valid = false; _busPtr = nullptr; - if (_data != nullptr) freeData(); + freeData(); + //PinManager::deallocateMultiplePins(_pins, 2, PinOwner::BusDigital); PinManager::deallocatePin(_pins[1], PinOwner::BusDigital); PinManager::deallocatePin(_pins[0], PinOwner::BusDigital); } @@ -452,7 +452,7 @@ void BusDigital::cleanup() { #endif #endif -BusPwm::BusPwm(BusConfig &bc) +BusPwm::BusPwm(const BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed, bc.refreshReq) // hijack Off refresh flag to indicate usage of dithering { if (!isPWM(bc.type)) return; @@ -496,12 +496,12 @@ BusPwm::BusPwm(BusConfig &bc) _hasRgb = hasRGB(bc.type); _hasWhite = hasWhite(bc.type); _hasCCT = hasCCT(bc.type); - _data = _pwmdata; // avoid malloc() and use stack + _data = _pwmdata; // avoid malloc() and use already allocated memory _valid = true; - DEBUG_PRINTF_P(PSTR("%successfully inited PWM strip with type %u, frequency %u, bit depth %u and pins %u,%u,%u,%u,%u\n"), _valid?"S":"Uns", bc.type, _frequency, _depth, _pins[0], _pins[1], _pins[2], _pins[3], _pins[4]); + DEBUGBUS_PRINTF_P(PSTR("%successfully inited PWM strip with type %u, frequency %u, bit depth %u and pins %u,%u,%u,%u,%u\n"), _valid?"S":"Uns", bc.type, _frequency, _depth, _pins[0], _pins[1], _pins[2], _pins[3], _pins[4]); } -void BusPwm::setPixelColor(uint16_t pix, uint32_t c) { +void BusPwm::setPixelColor(unsigned pix, uint32_t c) { if (pix != 0 || !_valid) return; //only react to first pixel if (_type != TYPE_ANALOG_3CH) c = autoWhiteCalc(c); if (Bus::_cct >= 1900 && (_type == TYPE_ANALOG_3CH || _type == TYPE_ANALOG_4CH)) { @@ -538,7 +538,7 @@ void BusPwm::setPixelColor(uint16_t pix, uint32_t c) { } //does no index check -uint32_t BusPwm::getPixelColor(uint16_t pix) const { +uint32_t BusPwm::getPixelColor(unsigned pix) const { if (!_valid) return 0; // TODO getting the reverse from CCT is involved (a quick approximation when CCT blending is ste to 0 implemented) switch (_type) { @@ -567,19 +567,15 @@ void BusPwm::show() { const unsigned maxBri = (1<<_depth); // possible values: 16384 (14), 8192 (13), 4096 (12), 2048 (11), 1024 (10), 512 (9) and 256 (8) [[maybe_unused]] const unsigned bitShift = dithering * 4; // if dithering, _depth is 12 bit but LEDC channel is set to 8 bit (using 4 fractional bits) - // use CIE brightness formula (cubic) to fit (or approximate linearity of) human eye perceived brightness - // the formula is based on 12 bit resolution as there is no need for greater precision + // use CIE brightness formula (linear + cubic) to approximate human eye perceived brightness // see: https://en.wikipedia.org/wiki/Lightness - unsigned pwmBri = (unsigned)_bri * 100; // enlarge to use integer math for linear response - if (pwmBri < 2040) { - // linear response for values [0-20] - pwmBri = ((pwmBri << 12) + 115043) / 230087; //adding '0.5' before division for correct rounding - } else { - // cubic response for values [21-255] - pwmBri += 4080; - float temp = (float)pwmBri / 29580.0f; - temp = temp * temp * temp * (float)maxBri; - pwmBri = (unsigned)temp; // pwmBri is in range [0-maxBri] + unsigned pwmBri = _bri; + if (pwmBri < 21) { // linear response for values [0-20] + pwmBri = (pwmBri * maxBri + 2300 / 2) / 2300 ; // adding '0.5' before division for correct rounding, 2300 gives a good match to CIE curve + } else { // cubic response for values [21-255] + float temp = float(pwmBri + 41) / float(255 + 41); // 41 is to match offset & slope to linear part + temp = temp * temp * temp * (float)maxBri; + pwmBri = (unsigned)temp; // pwmBri is in range [0-maxBri] C } [[maybe_unused]] unsigned hPoint = 0; // phase shift (0 - maxBri) @@ -618,7 +614,7 @@ void BusPwm::show() { } } -uint8_t BusPwm::getPins(uint8_t* pinArray) const { +unsigned BusPwm::getPins(uint8_t* pinArray) const { if (!_valid) return 0; unsigned numPins = numPWMPins(_type); if (pinArray) for (unsigned i = 0; i < numPins; i++) pinArray[i] = _pins[i]; @@ -654,7 +650,7 @@ void BusPwm::deallocatePins() { } -BusOnOff::BusOnOff(BusConfig &bc) +BusOnOff::BusOnOff(const BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed) , _onoffdata(0) { @@ -671,10 +667,10 @@ BusOnOff::BusOnOff(BusConfig &bc) _hasCCT = false; _data = &_onoffdata; // avoid malloc() and use stack _valid = true; - DEBUG_PRINTF_P(PSTR("%successfully inited On/Off strip with pin %u\n"), _valid?"S":"Uns", _pin); + DEBUGBUS_PRINTF_P(PSTR("%successfully inited On/Off strip with pin %u\n"), _valid?"S":"Uns", _pin); } -void BusOnOff::setPixelColor(uint16_t pix, uint32_t c) { +void BusOnOff::setPixelColor(unsigned pix, uint32_t c) { if (pix != 0 || !_valid) return; //only react to first pixel c = autoWhiteCalc(c); uint8_t r = R(c); @@ -684,7 +680,7 @@ void BusOnOff::setPixelColor(uint16_t pix, uint32_t c) { _data[0] = bool(r|g|b|w) && bool(_bri) ? 0xFF : 0; } -uint32_t BusOnOff::getPixelColor(uint16_t pix) const { +uint32_t BusOnOff::getPixelColor(unsigned pix) const { if (!_valid) return 0; return RGBW32(_data[0], _data[0], _data[0], _data[0]); } @@ -694,7 +690,7 @@ void BusOnOff::show() { digitalWrite(_pin, _reversed ? !(bool)_data[0] : (bool)_data[0]); } -uint8_t BusOnOff::getPins(uint8_t* pinArray) const { +unsigned BusOnOff::getPins(uint8_t* pinArray) const { if (!_valid) return 0; if (pinArray) pinArray[0] = _pin; return 1; @@ -707,7 +703,7 @@ std::vector BusOnOff::getLEDTypes() { }; } -BusNetwork::BusNetwork(BusConfig &bc) +BusNetwork::BusNetwork(const BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite, bc.count) , _broadcastLock(false) { @@ -731,10 +727,10 @@ BusNetwork::BusNetwork(BusConfig &bc) _UDPchannels = _hasWhite + 3; _client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]); _valid = (allocateData(_len * _UDPchannels) != nullptr); - DEBUG_PRINTF_P(PSTR("%successfully inited virtual strip with type %u and IP %u.%u.%u.%u\n"), _valid?"S":"Uns", bc.type, bc.pins[0], bc.pins[1], bc.pins[2], bc.pins[3]); + DEBUGBUS_PRINTF_P(PSTR("%successfully inited virtual strip with type %u and IP %u.%u.%u.%u\n"), _valid?"S":"Uns", bc.type, bc.pins[0], bc.pins[1], bc.pins[2], bc.pins[3]); } -void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) { +void BusNetwork::setPixelColor(unsigned pix, uint32_t c) { if (!_valid || pix >= _len) return; if (_hasWhite) c = autoWhiteCalc(c); if (Bus::_cct >= 1900) c = colorBalanceFromKelvin(Bus::_cct, c); //color correction from CCT @@ -745,7 +741,7 @@ void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) { if (_hasWhite) _data[offset+3] = W(c); } -uint32_t BusNetwork::getPixelColor(uint16_t pix) const { +uint32_t BusNetwork::getPixelColor(unsigned pix) const { if (!_valid || pix >= _len) return 0; unsigned offset = pix * _UDPchannels; return RGBW32(_data[offset], _data[offset+1], _data[offset+2], (hasWhite() ? _data[offset+3] : 0)); @@ -758,7 +754,7 @@ void BusNetwork::show() { _broadcastLock = false; } -uint8_t BusNetwork::getPins(uint8_t* pinArray) const { +unsigned BusNetwork::getPins(uint8_t* pinArray) const { if (pinArray) for (unsigned i = 0; i < 4; i++) pinArray[i] = _client[i]; return 4; } @@ -779,6 +775,7 @@ std::vector BusNetwork::getLEDTypes() { } void BusNetwork::cleanup() { + DEBUGBUS_PRINTLN(F("Virtual Cleanup.")); _type = I_NONE; _valid = false; freeData(); @@ -786,43 +783,66 @@ void BusNetwork::cleanup() { //utility to get the approx. memory usage of a given BusConfig -uint32_t BusManager::memUsage(BusConfig &bc) { - if (Bus::isOnOff(bc.type) || Bus::isPWM(bc.type)) return OUTPUT_MAX_PINS; - - unsigned len = bc.count + bc.skipAmount; - unsigned channels = Bus::getNumberOfChannels(bc.type); - unsigned multiplier = 1; - if (Bus::isDigital(bc.type)) { // digital types - if (Bus::is16bit(bc.type)) len *= 2; // 16-bit LEDs - #ifdef ESP8266 - if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem - multiplier = 5; - } - #else //ESP32 RMT uses double buffer, parallel I2S uses 8x buffer (3 times) - multiplier = PolyBus::isParallelI2S1Output() ? 24 : 2; - #endif +unsigned BusConfig::memUsage(unsigned nr) const { + if (Bus::isVirtual(type)) { + return sizeof(BusNetwork) + (count * Bus::getNumberOfChannels(type)); + } else if (Bus::isDigital(type)) { + return sizeof(BusDigital) + PolyBus::memUsage(count + skipAmount, PolyBus::getI(type, pins, nr)) + doubleBuffer * (count + skipAmount) * Bus::getNumberOfChannels(type); + } else if (Bus::isOnOff(type)) { + return sizeof(BusOnOff); + } else { + return sizeof(BusPwm); } - return (len * multiplier + bc.doubleBuffer * (bc.count + bc.skipAmount)) * channels; } -uint32_t BusManager::memUsage(unsigned maxChannels, unsigned maxCount, unsigned minBuses) { - //ESP32 RMT uses double buffer, parallel I2S uses 8x buffer (3 times) - unsigned multiplier = PolyBus::isParallelI2S1Output() ? 3 : 2; - return (maxChannels * maxCount * minBuses * multiplier); + +unsigned BusManager::memUsage() { + // when ESP32, S2 & S3 use parallel I2S only the largest bus determines the total memory requirements for back buffers + // front buffers are always allocated per bus + unsigned size = 0; + unsigned maxI2S = 0; + #if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(ESP8266) + unsigned digitalCount = 0; + #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) + #define MAX_RMT 4 + #else + #define MAX_RMT 8 + #endif + #endif + for (const auto &bus : busses) { + unsigned busSize = bus->getBusSize(); + #if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(ESP8266) + if (bus->isDigital() && !bus->is2Pin()) digitalCount++; + if (PolyBus::isParallelI2S1Output() && digitalCount > MAX_RMT) { + unsigned i2sCommonSize = 3 * bus->getLength() * bus->getNumberOfChannels() * (bus->is16bit()+1); + if (i2sCommonSize > maxI2S) maxI2S = i2sCommonSize; + busSize -= i2sCommonSize; + } + #endif + size += busSize; + } + return size + maxI2S; } -int BusManager::add(BusConfig &bc) { +int BusManager::add(const BusConfig &bc) { + DEBUGBUS_PRINTF_P(PSTR("Bus: Adding bus (%d - %d >= %d)\n"), getNumBusses(), getNumVirtualBusses(), WLED_MAX_BUSSES); if (getNumBusses() - getNumVirtualBusses() >= WLED_MAX_BUSSES) return -1; + unsigned numDigital = 0; + for (const auto &bus : busses) if (bus->isDigital() && !bus->is2Pin()) numDigital++; if (Bus::isVirtual(bc.type)) { - busses[numBusses] = new BusNetwork(bc); + //busses.push_back(std::make_unique(bc)); // when C++ >11 + busses.push_back(new BusNetwork(bc)); } else if (Bus::isDigital(bc.type)) { - busses[numBusses] = new BusDigital(bc, numBusses, colorOrderMap); + //busses.push_back(std::make_unique(bc, numDigital, colorOrderMap)); + busses.push_back(new BusDigital(bc, numDigital, colorOrderMap)); } else if (Bus::isOnOff(bc.type)) { - busses[numBusses] = new BusOnOff(bc); + //busses.push_back(std::make_unique(bc)); + busses.push_back(new BusOnOff(bc)); } else { - busses[numBusses] = new BusPwm(bc); + //busses.push_back(std::make_unique(bc)); + busses.push_back(new BusPwm(bc)); } - return numBusses++; + return busses.size(); } // credit @willmmiles @@ -851,18 +871,21 @@ String BusManager::getLEDTypesJSONString() { } void BusManager::useParallelOutput() { - _parallelOutputs = 8; // hardcoded since we use NPB I2S x8 methods + DEBUGBUS_PRINTLN(F("Bus: Enabling parallel I2S.")); PolyBus::setParallelI2S1Output(); } +bool BusManager::hasParallelOutput() { + return PolyBus::isParallelI2S1Output(); +} + //do not call this method from system context (network callback) void BusManager::removeAll() { - DEBUG_PRINTLN(F("Removing all.")); + DEBUGBUS_PRINTLN(F("Removing all.")); //prevents crashes due to deleting busses while in use. while (!canAllShow()) yield(); - for (unsigned i = 0; i < numBusses; i++) delete busses[i]; - numBusses = 0; - _parallelOutputs = 1; + for (auto &bus : busses) delete bus; // needed when not using std::unique_ptr C++ >11 + busses.clear(); PolyBus::setParallelI2S1Output(false); } @@ -873,7 +896,9 @@ void BusManager::removeAll() { void BusManager::esp32RMTInvertIdle() { bool idle_out; unsigned rmt = 0; - for (unsigned u = 0; u < numBusses(); u++) { + unsigned u = 0; + for (auto &bus : busses) { + if (bus->getLength()==0 || !bus->isDigital() || bus->is2Pin()) continue; #if defined(CONFIG_IDF_TARGET_ESP32C3) // 2 RMT, only has 1 I2S but NPB does not support it ATM if (u > 1) return; rmt = u; @@ -884,11 +909,11 @@ void BusManager::esp32RMTInvertIdle() { if (u > 3) return; rmt = u; #else - if (u < _parallelOutputs) continue; - if (u >= _parallelOutputs + 8) return; // only 8 RMT channels - rmt = u - _parallelOutputs; + unsigned numI2S = !PolyBus::isParallelI2S1Output(); // if using parallel I2S, RMT is used 1st + if (numI2S > u) continue; + if (u > 7 + numI2S) return; + rmt = u - numI2S; #endif - if (busses[u]->getLength()==0 || !busses[u]->isDigital() || busses[u]->is2Pin()) continue; //assumes that bus number to rmt channel mapping stays 1:1 rmt_channel_t ch = static_cast(rmt); rmt_idle_level_t lvl; @@ -897,6 +922,7 @@ void BusManager::esp32RMTInvertIdle() { else if (lvl == RMT_IDLE_LEVEL_LOW) lvl = RMT_IDLE_LEVEL_HIGH; else continue; rmt_set_idle_level(ch, idle_out, lvl); + u++ } } #endif @@ -905,12 +931,12 @@ void BusManager::on() { #ifdef ESP8266 //Fix for turning off onboard LED breaking bus if (PinManager::getPinOwner(LED_BUILTIN) == PinOwner::BusDigital) { - for (unsigned i = 0; i < numBusses; i++) { + for (auto &bus : busses) { uint8_t pins[2] = {255,255}; - if (busses[i]->isDigital() && busses[i]->getPins(pins)) { + if (bus->isDigital() && bus->getPins(pins)) { if (pins[0] == LED_BUILTIN || pins[1] == LED_BUILTIN) { - BusDigital *bus = static_cast(busses[i]); - bus->begin(); + BusDigital *b = static_cast(bus); + b->begin(); break; } } @@ -927,7 +953,7 @@ void BusManager::off() { // turn off built-in LED if strip is turned off // this will break digital bus so will need to be re-initialised on On if (PinManager::getPinOwner(LED_BUILTIN) == PinOwner::BusDigital) { - for (unsigned i = 0; i < numBusses; i++) if (busses[i]->isOffRefreshRequired()) return; + for (const auto &bus : busses) if (bus->isOffRefreshRequired()) return; pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, HIGH); } @@ -939,31 +965,26 @@ void BusManager::off() { void BusManager::show() { _milliAmpsUsed = 0; - for (unsigned i = 0; i < numBusses; i++) { - busses[i]->show(); - _milliAmpsUsed += busses[i]->getUsedCurrent(); + for (auto &bus : busses) { + bus->show(); + _milliAmpsUsed += bus->getUsedCurrent(); } - if (_milliAmpsUsed) _milliAmpsUsed += MA_FOR_ESP; } void BusManager::setStatusPixel(uint32_t c) { - for (unsigned i = 0; i < numBusses; i++) { - busses[i]->setStatusPixel(c); - } + for (auto &bus : busses) bus->setStatusPixel(c); } -void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c) { - for (unsigned i = 0; i < numBusses; i++) { - unsigned bstart = busses[i]->getStart(); - if (pix < bstart || pix >= bstart + busses[i]->getLength()) continue; - busses[i]->setPixelColor(pix - bstart, c); +void IRAM_ATTR BusManager::setPixelColor(unsigned pix, uint32_t c) { + for (auto &bus : busses) { + unsigned bstart = bus->getStart(); + if (pix < bstart || pix >= bstart + bus->getLength()) continue; + bus->setPixelColor(pix - bstart, c); } } void BusManager::setBrightness(uint8_t b) { - for (unsigned i = 0; i < numBusses; i++) { - busses[i]->setBrightness(b); - } + for (auto &bus : busses) bus->setBrightness(b); } void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) { @@ -975,35 +996,33 @@ void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) { Bus::setCCT(cct); } -uint32_t BusManager::getPixelColor(uint16_t pix) { - for (unsigned i = 0; i < numBusses; i++) { - unsigned bstart = busses[i]->getStart(); - if (!busses[i]->containsPixel(pix)) continue; - return busses[i]->getPixelColor(pix - bstart); +uint32_t BusManager::getPixelColor(unsigned pix) { + for (auto &bus : busses) { + unsigned bstart = bus->getStart(); + if (!bus->containsPixel(pix)) continue; + return bus->getPixelColor(pix - bstart); } return 0; } bool BusManager::canAllShow() { - for (unsigned i = 0; i < numBusses; i++) { - if (!busses[i]->canShow()) return false; - } + for (const auto &bus : busses) if (!bus->canShow()) return false; return true; } Bus* BusManager::getBus(uint8_t busNr) { - if (busNr >= numBusses) return nullptr; + if (busNr >= busses.size()) return nullptr; return busses[busNr]; } //semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit()) uint16_t BusManager::getTotalLength() { unsigned len = 0; - for (unsigned i=0; igetLength(); + for (const auto &bus : busses) len += bus->getLength(); return len; } -bool PolyBus::useParallelI2S = false; +bool PolyBus::_useParallelI2S = false; // Bus static member definition int16_t Bus::_cct = -1; @@ -1012,9 +1031,8 @@ uint8_t Bus::_gAWM = 255; uint16_t BusDigital::_milliAmpsTotal = 0; -uint8_t BusManager::numBusses = 0; -Bus* BusManager::busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES]; +//std::vector> BusManager::busses; +std::vector BusManager::busses; ColorOrderMap BusManager::colorOrderMap = {}; uint16_t BusManager::_milliAmpsUsed = 0; uint16_t BusManager::_milliAmpsMax = ABL_MILLIAMPS_DEFAULT; -uint8_t BusManager::_parallelOutputs = 1; diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index ecebc120e8..7ea8276c51 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -1,3 +1,4 @@ +#pragma once #ifndef BusManager_h #define BusManager_h @@ -7,6 +8,30 @@ #include "const.h" #include +#include + +// enable additional debug output +#if defined(WLED_DEBUG_HOST) + #include "net_debug.h" + #define DEBUGOUT NetDebug +#else + #define DEBUGOUT Serial +#endif + +#ifdef WLED_DEBUG_BUS + #ifndef ESP8266 + #include + #endif + #define DEBUGBUS_PRINT(x) DEBUGOUT.print(x) + #define DEBUGBUS_PRINTLN(x) DEBUGOUT.println(x) + #define DEBUGBUS_PRINTF(x...) DEBUGOUT.printf(x) + #define DEBUGBUS_PRINTF_P(x...) DEBUGOUT.printf_P(x) +#else + #define DEBUGBUS_PRINT(x) + #define DEBUGBUS_PRINTLN(x) + #define DEBUGBUS_PRINTF(x...) + #define DEBUGBUS_PRINTF_P(x...) +#endif //colors.cpp uint16_t approximateKelvinFromRGB(uint32_t rgb); @@ -77,50 +102,51 @@ class Bus { _autoWhiteMode = Bus::hasWhite(type) ? aw : RGBW_MODE_MANUAL_ONLY; }; - virtual ~Bus() {} //throw the bus under the bus + virtual ~Bus() {} //throw the bus under the bus (derived class needs to freeData()) - virtual void begin() {}; + virtual void begin() {}; virtual void show() = 0; - virtual bool canShow() const { return true; } - virtual void setStatusPixel(uint32_t c) {} - virtual void setPixelColor(uint16_t pix, uint32_t c) = 0; - virtual void setBrightness(uint8_t b) { _bri = b; }; - virtual void setColorOrder(uint8_t co) {} - virtual uint32_t getPixelColor(uint16_t pix) const { return 0; } - virtual uint8_t getPins(uint8_t* pinArray = nullptr) const { return 0; } - virtual uint16_t getLength() const { return isOk() ? _len : 0; } - virtual uint8_t getColorOrder() const { return COL_ORDER_RGB; } - virtual uint8_t skippedLeds() const { return 0; } - virtual uint16_t getFrequency() const { return 0U; } - virtual uint16_t getLEDCurrent() const { return 0; } - virtual uint16_t getUsedCurrent() const { return 0; } - virtual uint16_t getMaxCurrent() const { return 0; } - - inline bool hasRGB() const { return _hasRgb; } - inline bool hasWhite() const { return _hasWhite; } - inline bool hasCCT() const { return _hasCCT; } - inline bool isDigital() const { return isDigital(_type); } - inline bool is2Pin() const { return is2Pin(_type); } - inline bool isOnOff() const { return isOnOff(_type); } - inline bool isPWM() const { return isPWM(_type); } - inline bool isVirtual() const { return isVirtual(_type); } - inline bool is16bit() const { return is16bit(_type); } - inline bool mustRefresh() const { return mustRefresh(_type); } - inline void setReversed(bool reversed) { _reversed = reversed; } - inline void setStart(uint16_t start) { _start = start; } - inline void setAutoWhiteMode(uint8_t m) { if (m < 5) _autoWhiteMode = m; } - inline uint8_t getAutoWhiteMode() const { return _autoWhiteMode; } - inline uint8_t getNumberOfChannels() const { return hasWhite() + 3*hasRGB() + hasCCT(); } - inline uint16_t getStart() const { return _start; } - inline uint8_t getType() const { return _type; } - inline bool isOk() const { return _valid; } - inline bool isReversed() const { return _reversed; } - inline bool isOffRefreshRequired() const { return _needsRefresh; } - inline bool containsPixel(uint16_t pix) const { return pix >= _start && pix < _start + _len; } - - static inline std::vector getLEDTypes() { return {{TYPE_NONE, "", PSTR("None")}}; } // not used. just for reference for derived classes - static constexpr uint8_t getNumberOfPins(uint8_t type) { return isVirtual(type) ? 4 : isPWM(type) ? numPWMPins(type) : is2Pin(type) + 1; } // credit @PaoloTK - static constexpr uint8_t getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); } + virtual bool canShow() const { return true; } + virtual void setStatusPixel(uint32_t c) {} + virtual void setPixelColor(unsigned pix, uint32_t c) = 0; + virtual void setBrightness(uint8_t b) { _bri = b; }; + virtual void setColorOrder(uint8_t co) {} + virtual uint32_t getPixelColor(unsigned pix) const { return 0; } + virtual unsigned getPins(uint8_t* pinArray = nullptr) const { return 0; } + virtual uint16_t getLength() const { return isOk() ? _len : 0; } + virtual uint8_t getColorOrder() const { return COL_ORDER_RGB; } + virtual unsigned skippedLeds() const { return 0; } + virtual uint16_t getFrequency() const { return 0U; } + virtual uint16_t getLEDCurrent() const { return 0; } + virtual uint16_t getUsedCurrent() const { return 0; } + virtual uint16_t getMaxCurrent() const { return 0; } + virtual unsigned getBusSize() const { return sizeof(Bus); } + + inline bool hasRGB() const { return _hasRgb; } + inline bool hasWhite() const { return _hasWhite; } + inline bool hasCCT() const { return _hasCCT; } + inline bool isDigital() const { return isDigital(_type); } + inline bool is2Pin() const { return is2Pin(_type); } + inline bool isOnOff() const { return isOnOff(_type); } + inline bool isPWM() const { return isPWM(_type); } + inline bool isVirtual() const { return isVirtual(_type); } + inline bool is16bit() const { return is16bit(_type); } + inline bool mustRefresh() const { return mustRefresh(_type); } + inline void setReversed(bool reversed) { _reversed = reversed; } + inline void setStart(uint16_t start) { _start = start; } + inline void setAutoWhiteMode(uint8_t m) { if (m < 5) _autoWhiteMode = m; } + inline uint8_t getAutoWhiteMode() const { return _autoWhiteMode; } + inline unsigned getNumberOfChannels() const { return hasWhite() + 3*hasRGB() + hasCCT(); } + inline uint16_t getStart() const { return _start; } + inline uint8_t getType() const { return _type; } + inline bool isOk() const { return _valid; } + inline bool isReversed() const { return _reversed; } + inline bool isOffRefreshRequired() const { return _needsRefresh; } + inline bool containsPixel(uint16_t pix) const { return pix >= _start && pix < _start + _len; } + + static inline std::vector getLEDTypes() { return {{TYPE_NONE, "", PSTR("None")}}; } // not used. just for reference for derived classes + static constexpr unsigned getNumberOfPins(uint8_t type) { return isVirtual(type) ? 4 : isPWM(type) ? numPWMPins(type) : is2Pin(type) + 1; } // credit @PaoloTK + static constexpr unsigned getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); } static constexpr bool hasRGB(uint8_t type) { return !((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_ANALOG_1CH || type == TYPE_ANALOG_2CH || type == TYPE_ONOFF); } @@ -152,7 +178,7 @@ class Bus { static inline uint8_t getGlobalAWMode() { return _gAWM; } static inline void setCCT(int16_t cct) { _cct = cct; } static inline uint8_t getCCTBlend() { return _cctBlend; } - static inline void setCCTBlend(uint8_t b) { + static inline void setCCTBlend(uint8_t b) { _cctBlend = (std::min((int)b,100) * 127) / 100; //compile-time limiter for hardware that can't power both white channels at max #ifdef WLED_MAX_CCT_BLEND @@ -191,29 +217,30 @@ class Bus { uint32_t autoWhiteCalc(uint32_t c) const; uint8_t *allocateData(size_t size = 1); - void freeData() { if (_data != nullptr) free(_data); _data = nullptr; } + void freeData(); }; class BusDigital : public Bus { public: - BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com); + BusDigital(const BusConfig &bc, uint8_t nr, const ColorOrderMap &com); ~BusDigital() { cleanup(); } void show() override; bool canShow() const override; void setBrightness(uint8_t b) override; void setStatusPixel(uint32_t c) override; - [[gnu::hot]] void setPixelColor(uint16_t pix, uint32_t c) override; + [[gnu::hot]] void setPixelColor(unsigned pix, uint32_t c) override; void setColorOrder(uint8_t colorOrder) override; - [[gnu::hot]] uint32_t getPixelColor(uint16_t pix) const override; + [[gnu::hot]] uint32_t getPixelColor(unsigned pix) const override; uint8_t getColorOrder() const override { return _colorOrder; } - uint8_t getPins(uint8_t* pinArray = nullptr) const override; - uint8_t skippedLeds() const override { return _skip; } + unsigned getPins(uint8_t* pinArray = nullptr) const override; + unsigned skippedLeds() const override { return _skip; } uint16_t getFrequency() const override { return _frequencykHz; } uint16_t getLEDCurrent() const override { return _milliAmpsPerLed; } uint16_t getUsedCurrent() const override { return _milliAmpsTotal; } uint16_t getMaxCurrent() const override { return _milliAmpsMax; } + unsigned getBusSize() const override; void begin() override; void cleanup(); @@ -243,21 +270,22 @@ class BusDigital : public Bus { return c; } - uint8_t estimateCurrentAndLimitBri(); + uint8_t estimateCurrentAndLimitBri() const; }; class BusPwm : public Bus { public: - BusPwm(BusConfig &bc); + BusPwm(const BusConfig &bc); ~BusPwm() { cleanup(); } - void setPixelColor(uint16_t pix, uint32_t c) override; - uint32_t getPixelColor(uint16_t pix) const override; //does no index check - uint8_t getPins(uint8_t* pinArray = nullptr) const override; + void setPixelColor(unsigned pix, uint32_t c) override; + uint32_t getPixelColor(unsigned pix) const override; //does no index check + unsigned getPins(uint8_t* pinArray = nullptr) const override; uint16_t getFrequency() const override { return _frequency; } + unsigned getBusSize() const override { return sizeof(BusPwm); } void show() override; - void cleanup() { deallocatePins(); } + inline void cleanup() { deallocatePins(); _data = nullptr; } static std::vector getLEDTypes(); @@ -276,14 +304,15 @@ class BusPwm : public Bus { class BusOnOff : public Bus { public: - BusOnOff(BusConfig &bc); + BusOnOff(const BusConfig &bc); ~BusOnOff() { cleanup(); } - void setPixelColor(uint16_t pix, uint32_t c) override; - uint32_t getPixelColor(uint16_t pix) const override; - uint8_t getPins(uint8_t* pinArray) const override; + void setPixelColor(unsigned pix, uint32_t c) override; + uint32_t getPixelColor(unsigned pix) const override; + unsigned getPins(uint8_t* pinArray) const override; + unsigned getBusSize() const override { return sizeof(BusOnOff); } void show() override; - void cleanup() { PinManager::deallocatePin(_pin, PinOwner::BusOnOff); } + inline void cleanup() { PinManager::deallocatePin(_pin, PinOwner::BusOnOff); _data = nullptr; } static std::vector getLEDTypes(); @@ -295,13 +324,14 @@ class BusOnOff : public Bus { class BusNetwork : public Bus { public: - BusNetwork(BusConfig &bc); + BusNetwork(const BusConfig &bc); ~BusNetwork() { cleanup(); } bool canShow() const override { return !_broadcastLock; } // this should be a return value from UDP routine if it is still sending data out - void setPixelColor(uint16_t pix, uint32_t c) override; - uint32_t getPixelColor(uint16_t pix) const override; - uint8_t getPins(uint8_t* pinArray = nullptr) const override; + [[gnu::hot]] void setPixelColor(unsigned pix, uint32_t c) override; + [[gnu::hot]] uint32_t getPixelColor(unsigned pix) const override; + unsigned getPins(uint8_t* pinArray = nullptr) const override; + unsigned getBusSize() const override { return sizeof(BusNetwork) + (isOk() ? _len * _UDPchannels : 0); } void show() override; void cleanup(); @@ -347,6 +377,16 @@ struct BusConfig { type = busType & 0x7F; // bit 7 may be/is hacked to include refresh info (1=refresh in off state, 0=no refresh) size_t nPins = Bus::getNumberOfPins(type); for (size_t i = 0; i < nPins; i++) pins[i] = ppins[i]; + DEBUGBUS_PRINTF_P(PSTR("Bus: Config (%d-%d, type:%d, CO:%d, rev:%d, skip:%d, AW:%d kHz:%d, mA:%d/%d)\n"), + (int)start, (int)(start+len), + (int)type, + (int)colorOrder, + (int)reversed, + (int)skipAmount, + (int)autoWhite, + (int)frequency, + (int)milliAmpsPerLed, (int)milliAmpsMax + ); } //validates start and length and extends total if needed @@ -360,21 +400,32 @@ struct BusConfig { if (start + count > total) total = start + count; return true; } + + unsigned memUsage(unsigned nr = 0) const; }; +//fine tune power estimation constants for your setup +//you can set it to 0 if the ESP is powered by USB and the LEDs by external +#ifndef MA_FOR_ESP + #ifdef ESP8266 + #define MA_FOR_ESP 80 //how much mA does the ESP use (Wemos D1 about 80mA) + #else + #define MA_FOR_ESP 120 //how much mA does the ESP use (ESP32 about 120mA) + #endif +#endif + class BusManager { public: BusManager() {}; - //utility to get the approx. memory usage of a given BusConfig - static uint32_t memUsage(BusConfig &bc); - static uint32_t memUsage(unsigned channels, unsigned count, unsigned buses = 1); - static uint16_t currentMilliamps() { return _milliAmpsUsed; } + static unsigned memUsage(); + static uint16_t currentMilliamps() { return _milliAmpsUsed + MA_FOR_ESP; } static uint16_t ablMilliampsMax() { return _milliAmpsMax; } - static int add(BusConfig &bc); + static int add(const BusConfig &bc); static void useParallelOutput(); // workaround for inaccessible PolyBus + static bool hasParallelOutput(); // workaround for inaccessible PolyBus //do not call this method from system context (network callback) static void removeAll(); @@ -385,38 +436,37 @@ class BusManager { static void show(); static bool canAllShow(); static void setStatusPixel(uint32_t c); - [[gnu::hot]] static void setPixelColor(uint16_t pix, uint32_t c); + [[gnu::hot]] static void setPixelColor(unsigned pix, uint32_t c); static void setBrightness(uint8_t b); // for setSegmentCCT(), cct can only be in [-1,255] range; allowWBCorrection will convert it to K // WARNING: setSegmentCCT() is a misleading name!!! much better would be setGlobalCCT() or just setCCT() static void setSegmentCCT(int16_t cct, bool allowWBCorrection = false); static inline void setMilliampsMax(uint16_t max) { _milliAmpsMax = max;} - static uint32_t getPixelColor(uint16_t pix); + static uint32_t getPixelColor(unsigned pix); static inline int16_t getSegmentCCT() { return Bus::getCCT(); } static Bus* getBus(uint8_t busNr); //semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit()) static uint16_t getTotalLength(); - static inline uint8_t getNumBusses() { return numBusses; } + static inline uint8_t getNumBusses() { return busses.size(); } static String getLEDTypesJSONString(); static inline ColorOrderMap& getColorOrderMap() { return colorOrderMap; } private: - static uint8_t numBusses; - static Bus* busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES]; + //static std::vector> busses; // we'd need C++ >11 + static std::vector busses; static ColorOrderMap colorOrderMap; static uint16_t _milliAmpsUsed; static uint16_t _milliAmpsMax; - static uint8_t _parallelOutputs; #ifdef ESP32_DATA_IDLE_HIGH static void esp32RMTInvertIdle() ; #endif static uint8_t getNumVirtualBusses() { int j = 0; - for (int i=0; iisVirtual()) j++; + for (const auto &bus : busses) j += bus->isVirtual(); return j; } }; diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h index d2a18c9d87..9d63aa0447 100644 --- a/wled00/bus_wrapper.h +++ b/wled00/bus_wrapper.h @@ -1,23 +1,9 @@ +#pragma once #ifndef BusWrapper_h #define BusWrapper_h +//#define NPB_CONF_4STEP_CADENCE #include "NeoPixelBusLg.h" -#include "bus_manager.h" - -// temporary - these defines should actually be set in platformio.ini -// C3: I2S0 and I2S1 methods not supported (has one I2S bus) -// S2: I2S1 methods not supported (has one I2S bus) -// S3: I2S0 and I2S1 methods not supported yet (has two I2S buses) -// https://github.com/Makuna/NeoPixelBus/blob/b32f719e95ef3c35c46da5c99538017ef925c026/src/internal/Esp32_i2s.h#L4 -// https://github.com/Makuna/NeoPixelBus/blob/b32f719e95ef3c35c46da5c99538017ef925c026/src/internal/NeoEsp32RmtMethod.h#L857 - -#if !defined(WLED_NO_I2S0_PIXELBUS) && (defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3)) -#define WLED_NO_I2S0_PIXELBUS -#endif -#if !defined(WLED_NO_I2S1_PIXELBUS) && (defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2)) -#define WLED_NO_I2S1_PIXELBUS -#endif -// temporary end //Hardware SPI Pins #define P_8266_HS_MOSI 13 @@ -55,110 +41,98 @@ #define I_8266_DM_TM2_3 19 #define I_8266_BB_TM2_3 20 //UCS8903 (RGB) -#define I_8266_U0_UCS_3 49 -#define I_8266_U1_UCS_3 50 -#define I_8266_DM_UCS_3 51 -#define I_8266_BB_UCS_3 52 +#define I_8266_U0_UCS_3 21 +#define I_8266_U1_UCS_3 22 +#define I_8266_DM_UCS_3 23 +#define I_8266_BB_UCS_3 24 //UCS8904 (RGBW) -#define I_8266_U0_UCS_4 53 -#define I_8266_U1_UCS_4 54 -#define I_8266_DM_UCS_4 55 -#define I_8266_BB_UCS_4 56 +#define I_8266_U0_UCS_4 25 +#define I_8266_U1_UCS_4 26 +#define I_8266_DM_UCS_4 27 +#define I_8266_BB_UCS_4 28 //FW1906 GRBCW -#define I_8266_U0_FW6_5 66 -#define I_8266_U1_FW6_5 67 -#define I_8266_DM_FW6_5 68 -#define I_8266_BB_FW6_5 69 +#define I_8266_U0_FW6_5 29 +#define I_8266_U1_FW6_5 30 +#define I_8266_DM_FW6_5 31 +#define I_8266_BB_FW6_5 32 //ESP8266 APA106 -#define I_8266_U0_APA106_3 81 -#define I_8266_U1_APA106_3 82 -#define I_8266_DM_APA106_3 83 -#define I_8266_BB_APA106_3 84 +#define I_8266_U0_APA106_3 33 +#define I_8266_U1_APA106_3 34 +#define I_8266_DM_APA106_3 35 +#define I_8266_BB_APA106_3 36 //WS2805 (RGBCW) -#define I_8266_U0_2805_5 89 -#define I_8266_U1_2805_5 90 -#define I_8266_DM_2805_5 91 -#define I_8266_BB_2805_5 92 +#define I_8266_U0_2805_5 37 +#define I_8266_U1_2805_5 38 +#define I_8266_DM_2805_5 39 +#define I_8266_BB_2805_5 40 //TM1914 (RGB) -#define I_8266_U0_TM1914_3 99 -#define I_8266_U1_TM1914_3 100 -#define I_8266_DM_TM1914_3 101 -#define I_8266_BB_TM1914_3 102 +#define I_8266_U0_TM1914_3 41 +#define I_8266_U1_TM1914_3 42 +#define I_8266_DM_TM1914_3 43 +#define I_8266_BB_TM1914_3 44 //SM16825 (RGBCW) -#define I_8266_U0_SM16825_5 103 -#define I_8266_U1_SM16825_5 104 -#define I_8266_DM_SM16825_5 105 -#define I_8266_BB_SM16825_5 106 +#define I_8266_U0_SM16825_5 45 +#define I_8266_U1_SM16825_5 46 +#define I_8266_DM_SM16825_5 47 +#define I_8266_BB_SM16825_5 48 /*** ESP32 Neopixel methods ***/ //RGB -#define I_32_RN_NEO_3 21 -#define I_32_I0_NEO_3 22 -#define I_32_I1_NEO_3 23 +#define I_32_RN_NEO_3 1 +#define I_32_I2_NEO_3 2 //RGBW -#define I_32_RN_NEO_4 25 -#define I_32_I0_NEO_4 26 -#define I_32_I1_NEO_4 27 +#define I_32_RN_NEO_4 5 +#define I_32_I2_NEO_4 6 //400Kbps -#define I_32_RN_400_3 29 -#define I_32_I0_400_3 30 -#define I_32_I1_400_3 31 +#define I_32_RN_400_3 9 +#define I_32_I2_400_3 10 //TM1814 (RGBW) -#define I_32_RN_TM1_4 33 -#define I_32_I0_TM1_4 34 -#define I_32_I1_TM1_4 35 +#define I_32_RN_TM1_4 13 +#define I_32_I2_TM1_4 14 //TM1829 (RGB) -#define I_32_RN_TM2_3 36 -#define I_32_I0_TM2_3 37 -#define I_32_I1_TM2_3 38 +#define I_32_RN_TM2_3 17 +#define I_32_I2_TM2_3 18 //UCS8903 (RGB) -#define I_32_RN_UCS_3 57 -#define I_32_I0_UCS_3 58 -#define I_32_I1_UCS_3 59 +#define I_32_RN_UCS_3 21 +#define I_32_I2_UCS_3 22 //UCS8904 (RGBW) -#define I_32_RN_UCS_4 60 -#define I_32_I0_UCS_4 61 -#define I_32_I1_UCS_4 62 +#define I_32_RN_UCS_4 25 +#define I_32_I2_UCS_4 26 //FW1906 GRBCW -#define I_32_RN_FW6_5 63 -#define I_32_I0_FW6_5 64 -#define I_32_I1_FW6_5 65 +#define I_32_RN_FW6_5 29 +#define I_32_I2_FW6_5 30 //APA106 -#define I_32_RN_APA106_3 85 -#define I_32_I0_APA106_3 86 -#define I_32_I1_APA106_3 87 +#define I_32_RN_APA106_3 33 +#define I_32_I2_APA106_3 34 //WS2805 (RGBCW) -#define I_32_RN_2805_5 93 -#define I_32_I0_2805_5 94 -#define I_32_I1_2805_5 95 +#define I_32_RN_2805_5 37 +#define I_32_I2_2805_5 38 //TM1914 (RGB) -#define I_32_RN_TM1914_3 96 -#define I_32_I0_TM1914_3 97 -#define I_32_I1_TM1914_3 98 +#define I_32_RN_TM1914_3 41 +#define I_32_I2_TM1914_3 42 //SM16825 (RGBCW) -#define I_32_RN_SM16825_5 107 -#define I_32_I0_SM16825_5 108 -#define I_32_I1_SM16825_5 109 +#define I_32_RN_SM16825_5 45 +#define I_32_I2_SM16825_5 46 //APA102 -#define I_HS_DOT_3 39 //hardware SPI -#define I_SS_DOT_3 40 //soft SPI +#define I_HS_DOT_3 101 //hardware SPI +#define I_SS_DOT_3 102 //soft SPI //LPD8806 -#define I_HS_LPD_3 41 -#define I_SS_LPD_3 42 +#define I_HS_LPD_3 103 +#define I_SS_LPD_3 104 //WS2801 -#define I_HS_WS1_3 43 -#define I_SS_WS1_3 44 +#define I_HS_WS1_3 105 +#define I_SS_WS1_3 106 //P9813 -#define I_HS_P98_3 45 -#define I_SS_P98_3 46 +#define I_HS_P98_3 107 +#define I_SS_P98_3 108 //LPD6803 -#define I_HS_LPO_3 47 -#define I_SS_LPO_3 48 +#define I_HS_LPO_3 109 +#define I_SS_LPO_3 110 // In the following NeoGammaNullMethod can be replaced with NeoGammaWLEDMethod to perform Gamma correction implicitly @@ -230,66 +204,103 @@ /*** ESP32 Neopixel methods ***/ #ifdef ARDUINO_ARCH_ESP32 +// C3: I2S0 and I2S1 methods not supported (has one I2S bus) +// S2: I2S0 methods supported (single & parallel), I2S1 methods not supported (has one I2S bus) +// S3: I2S0 methods not supported, I2S1 supports LCD parallel methods (has two I2S buses) +// https://github.com/Makuna/NeoPixelBus/blob/b32f719e95ef3c35c46da5c99538017ef925c026/src/internal/Esp32_i2s.h#L4 +// https://github.com/Makuna/NeoPixelBus/blob/b32f719e95ef3c35c46da5c99538017ef925c026/src/internal/NeoEsp32RmtMethod.h#L857 +#if defined(CONFIG_IDF_TARGET_ESP32S3) + // S3 will always use LCD parallel output + typedef X8Ws2812xMethod X1Ws2812xMethod; + typedef X8Sk6812Method X1Sk6812Method; + typedef X8400KbpsMethod X1400KbpsMethod; + typedef X8800KbpsMethod X1800KbpsMethod; + typedef X8Tm1814Method X1Tm1814Method; + typedef X8Tm1829Method X1Tm1829Method; + typedef X8Apa106Method X1Apa106Method; + typedef X8Ws2805Method X1Ws2805Method; + typedef X8Tm1914Method X1Tm1914Method; +#elif defined(CONFIG_IDF_TARGET_ESP32S2) + // S2 will use I2S0 + typedef NeoEsp32I2s0Ws2812xMethod X1Ws2812xMethod; + typedef NeoEsp32I2s0Sk6812Method X1Sk6812Method; + typedef NeoEsp32I2s0400KbpsMethod X1400KbpsMethod; + typedef NeoEsp32I2s0800KbpsMethod X1800KbpsMethod; + typedef NeoEsp32I2s0Tm1814Method X1Tm1814Method; + typedef NeoEsp32I2s0Tm1829Method X1Tm1829Method; + typedef NeoEsp32I2s0Apa106Method X1Apa106Method; + typedef NeoEsp32I2s0Ws2805Method X1Ws2805Method; + typedef NeoEsp32I2s0Tm1914Method X1Tm1914Method; +#elif !defined(CONFIG_IDF_TARGET_ESP32C3) + // regular ESP32 will use I2S1 + typedef NeoEsp32I2s1Ws2812xMethod X1Ws2812xMethod; + typedef NeoEsp32I2s1Sk6812Method X1Sk6812Method; + typedef NeoEsp32I2s1400KbpsMethod X1400KbpsMethod; + typedef NeoEsp32I2s1800KbpsMethod X1800KbpsMethod; + typedef NeoEsp32I2s1Tm1814Method X1Tm1814Method; + typedef NeoEsp32I2s1Tm1829Method X1Tm1829Method; + typedef NeoEsp32I2s1Apa106Method X1Apa106Method; + typedef NeoEsp32I2s1Ws2805Method X1Ws2805Method; + typedef NeoEsp32I2s1Tm1914Method X1Tm1914Method; +#endif + +// RMT driver selection +#if !defined(WLED_USE_SHARED_RMT) && !defined(__riscv) +#include +#define NeoEsp32RmtMethod(x) NeoEsp32RmtHIN ## x ## Method +#else +#define NeoEsp32RmtMethod(x) NeoEsp32RmtN ## x ## Method +#endif + //RGB -#define B_32_RN_NEO_3 NeoPixelBusLg -#define B_32_I0_NEO_3 NeoPixelBusLg -#define B_32_I1_NEO_3 NeoPixelBusLg -#define B_32_I1_NEO_3P NeoPixelBusLg // parallel I2S +#define B_32_RN_NEO_3 NeoPixelBusLg // ESP32, S2, S3, C3 +//#define B_32_IN_NEO_3 NeoPixelBusLg // ESP32 (dynamic I2S selection) +#define B_32_I2_NEO_3 NeoPixelBusLg // ESP32, S2, S3 (automatic I2S selection, see typedef above) +#define B_32_IP_NEO_3 NeoPixelBusLg // parallel I2S (ESP32, S2, S3) //RGBW -#define B_32_RN_NEO_4 NeoPixelBusLg -#define B_32_I0_NEO_4 NeoPixelBusLg -#define B_32_I1_NEO_4 NeoPixelBusLg -#define B_32_I1_NEO_4P NeoPixelBusLg // parallel I2S +#define B_32_RN_NEO_4 NeoPixelBusLg +#define B_32_I2_NEO_4 NeoPixelBusLg +#define B_32_IP_NEO_4 NeoPixelBusLg // parallel I2S //400Kbps -#define B_32_RN_400_3 NeoPixelBusLg -#define B_32_I0_400_3 NeoPixelBusLg -#define B_32_I1_400_3 NeoPixelBusLg -#define B_32_I1_400_3P NeoPixelBusLg // parallel I2S +#define B_32_RN_400_3 NeoPixelBusLg +#define B_32_I2_400_3 NeoPixelBusLg +#define B_32_IP_400_3 NeoPixelBusLg // parallel I2S //TM1814 (RGBW) -#define B_32_RN_TM1_4 NeoPixelBusLg -#define B_32_I0_TM1_4 NeoPixelBusLg -#define B_32_I1_TM1_4 NeoPixelBusLg -#define B_32_I1_TM1_4P NeoPixelBusLg // parallel I2S +#define B_32_RN_TM1_4 NeoPixelBusLg +#define B_32_I2_TM1_4 NeoPixelBusLg +#define B_32_IP_TM1_4 NeoPixelBusLg // parallel I2S //TM1829 (RGB) -#define B_32_RN_TM2_3 NeoPixelBusLg -#define B_32_I0_TM2_3 NeoPixelBusLg -#define B_32_I1_TM2_3 NeoPixelBusLg -#define B_32_I1_TM2_3P NeoPixelBusLg // parallel I2S +#define B_32_RN_TM2_3 NeoPixelBusLg +#define B_32_I2_TM2_3 NeoPixelBusLg +#define B_32_IP_TM2_3 NeoPixelBusLg // parallel I2S //UCS8903 -#define B_32_RN_UCS_3 NeoPixelBusLg -#define B_32_I0_UCS_3 NeoPixelBusLg -#define B_32_I1_UCS_3 NeoPixelBusLg -#define B_32_I1_UCS_3P NeoPixelBusLg // parallel I2S +#define B_32_RN_UCS_3 NeoPixelBusLg +#define B_32_I2_UCS_3 NeoPixelBusLg +#define B_32_IP_UCS_3 NeoPixelBusLg // parallel I2S //UCS8904 -#define B_32_RN_UCS_4 NeoPixelBusLg -#define B_32_I0_UCS_4 NeoPixelBusLg -#define B_32_I1_UCS_4 NeoPixelBusLg -#define B_32_I1_UCS_4P NeoPixelBusLg// parallel I2S +#define B_32_RN_UCS_4 NeoPixelBusLg +#define B_32_I2_UCS_4 NeoPixelBusLg +#define B_32_IP_UCS_4 NeoPixelBusLg// parallel I2S //APA106 -#define B_32_RN_APA106_3 NeoPixelBusLg -#define B_32_I0_APA106_3 NeoPixelBusLg -#define B_32_I1_APA106_3 NeoPixelBusLg -#define B_32_I1_APA106_3P NeoPixelBusLg // parallel I2S +#define B_32_RN_APA106_3 NeoPixelBusLg +#define B_32_I2_APA106_3 NeoPixelBusLg +#define B_32_IP_APA106_3 NeoPixelBusLg // parallel I2S //FW1906 GRBCW -#define B_32_RN_FW6_5 NeoPixelBusLg -#define B_32_I0_FW6_5 NeoPixelBusLg -#define B_32_I1_FW6_5 NeoPixelBusLg -#define B_32_I1_FW6_5P NeoPixelBusLg // parallel I2S +#define B_32_RN_FW6_5 NeoPixelBusLg +#define B_32_I2_FW6_5 NeoPixelBusLg +#define B_32_IP_FW6_5 NeoPixelBusLg // parallel I2S //WS2805 RGBWC -#define B_32_RN_2805_5 NeoPixelBusLg -#define B_32_I0_2805_5 NeoPixelBusLg -#define B_32_I1_2805_5 NeoPixelBusLg -#define B_32_I1_2805_5P NeoPixelBusLg // parallel I2S +#define B_32_RN_2805_5 NeoPixelBusLg +#define B_32_I2_2805_5 NeoPixelBusLg +#define B_32_IP_2805_5 NeoPixelBusLg // parallel I2S //TM1914 (RGB) -#define B_32_RN_TM1914_3 NeoPixelBusLg -#define B_32_I0_TM1914_3 NeoPixelBusLg -#define B_32_I1_TM1914_3 NeoPixelBusLg -#define B_32_I1_TM1914_3P NeoPixelBusLg // parallel I2S +#define B_32_RN_TM1914_3 NeoPixelBusLg +#define B_32_I2_TM1914_3 NeoPixelBusLg +#define B_32_IP_TM1914_3 NeoPixelBusLg // parallel I2S //Sm16825 (RGBWC) -#define B_32_RN_SM16825_5 NeoPixelBusLg -#define B_32_I0_SM16825_5 NeoPixelBusLg -#define B_32_I1_SM16825_5 NeoPixelBusLg -#define B_32_I1_SM16825_5P NeoPixelBusLg // parallel I2S +#define B_32_RN_SM16825_5 NeoPixelBusLg +#define B_32_I2_SM16825_5 NeoPixelBusLg +#define B_32_IP_SM16825_5 NeoPixelBusLg // parallel I2S #endif //APA102 @@ -328,11 +339,11 @@ //handles pointer type conversion for all possible bus types class PolyBus { private: - static bool useParallelI2S; + static bool _useParallelI2S; public: - static inline void setParallelI2S1Output(bool b = true) { useParallelI2S = b; } - static inline bool isParallelI2S1Output(void) { return useParallelI2S; } + static inline void setParallelI2S1Output(bool b = true) { _useParallelI2S = b; } + static inline bool isParallelI2S1Output(void) { return _useParallelI2S; } // initialize SPI bus speed for DotStar methods template @@ -436,34 +447,19 @@ class PolyBus { case I_32_RN_TM1914_3: beginTM1914(busPtr); break; case I_32_RN_SM16825_5: (static_cast(busPtr))->Begin(); break; // I2S1 bus or parellel buses - #ifndef WLED_NO_I2S1_PIXELBUS - case I_32_I1_NEO_3: if (useParallelI2S) (static_cast(busPtr))->Begin(); else (static_cast(busPtr))->Begin(); break; - case I_32_I1_NEO_4: if (useParallelI2S) (static_cast(busPtr))->Begin(); else (static_cast(busPtr))->Begin(); break; - case I_32_I1_400_3: if (useParallelI2S) (static_cast(busPtr))->Begin(); else (static_cast(busPtr))->Begin(); break; - case I_32_I1_TM1_4: if (useParallelI2S) beginTM1814(busPtr); else beginTM1814(busPtr); break; - case I_32_I1_TM2_3: if (useParallelI2S) (static_cast(busPtr))->Begin(); else (static_cast(busPtr))->Begin(); break; - case I_32_I1_UCS_3: if (useParallelI2S) (static_cast(busPtr))->Begin(); else (static_cast(busPtr))->Begin(); break; - case I_32_I1_UCS_4: if (useParallelI2S) (static_cast(busPtr))->Begin(); else (static_cast(busPtr))->Begin(); break; - case I_32_I1_FW6_5: if (useParallelI2S) (static_cast(busPtr))->Begin(); else (static_cast(busPtr))->Begin(); break; - case I_32_I1_APA106_3: if (useParallelI2S) (static_cast(busPtr))->Begin(); else (static_cast(busPtr))->Begin(); break; - case I_32_I1_2805_5: if (useParallelI2S) (static_cast(busPtr))->Begin(); else (static_cast(busPtr))->Begin(); break; - case I_32_I1_TM1914_3: if (useParallelI2S) beginTM1914(busPtr); else beginTM1914(busPtr); break; - case I_32_I1_SM16825_5: if (useParallelI2S) (static_cast(busPtr))->Begin(); else (static_cast(busPtr))->Begin(); break; - #endif - // I2S0 bus - #ifndef WLED_NO_I2S0_PIXELBUS - case I_32_I0_NEO_3: (static_cast(busPtr))->Begin(); break; - case I_32_I0_NEO_4: (static_cast(busPtr))->Begin(); break; - case I_32_I0_400_3: (static_cast(busPtr))->Begin(); break; - case I_32_I0_TM1_4: beginTM1814(busPtr); break; - case I_32_I0_TM2_3: (static_cast(busPtr))->Begin(); break; - case I_32_I0_UCS_3: (static_cast(busPtr))->Begin(); break; - case I_32_I0_UCS_4: (static_cast(busPtr))->Begin(); break; - case I_32_I0_FW6_5: (static_cast(busPtr))->Begin(); break; - case I_32_I0_APA106_3: (static_cast(busPtr))->Begin(); break; - case I_32_I0_2805_5: (static_cast(busPtr))->Begin(); break; - case I_32_I0_TM1914_3: beginTM1914(busPtr); break; - case I_32_I0_SM16825_5: (static_cast(busPtr))->Begin(); break; + #ifndef CONFIG_IDF_TARGET_ESP32C3 + case I_32_I2_NEO_3: if (_useParallelI2S) (static_cast(busPtr))->Begin(); else (static_cast(busPtr))->Begin(); break; + case I_32_I2_NEO_4: if (_useParallelI2S) (static_cast(busPtr))->Begin(); else (static_cast(busPtr))->Begin(); break; + case I_32_I2_400_3: if (_useParallelI2S) (static_cast(busPtr))->Begin(); else (static_cast(busPtr))->Begin(); break; + case I_32_I2_TM1_4: if (_useParallelI2S) beginTM1814(busPtr); else beginTM1814(busPtr); break; + case I_32_I2_TM2_3: if (_useParallelI2S) (static_cast(busPtr))->Begin(); else (static_cast(busPtr))->Begin(); break; + case I_32_I2_UCS_3: if (_useParallelI2S) (static_cast(busPtr))->Begin(); else (static_cast(busPtr))->Begin(); break; + case I_32_I2_UCS_4: if (_useParallelI2S) (static_cast(busPtr))->Begin(); else (static_cast(busPtr))->Begin(); break; + case I_32_I2_FW6_5: if (_useParallelI2S) (static_cast(busPtr))->Begin(); else (static_cast(busPtr))->Begin(); break; + case I_32_I2_APA106_3: if (_useParallelI2S) (static_cast(busPtr))->Begin(); else (static_cast(busPtr))->Begin(); break; + case I_32_I2_2805_5: if (_useParallelI2S) (static_cast(busPtr))->Begin(); else (static_cast(busPtr))->Begin(); break; + case I_32_I2_TM1914_3: if (_useParallelI2S) beginTM1914(busPtr); else beginTM1914(busPtr); break; + case I_32_I2_SM16825_5: if (_useParallelI2S) (static_cast(busPtr))->Begin(); else (static_cast(busPtr))->Begin(); break; #endif // ESP32 can (and should, to avoid inadvertantly driving the chip select signal) specify the pins used for SPI, but only in begin() case I_HS_DOT_3: beginDotStar(busPtr, pins[1], -1, pins[0], -1, clock_kHz); break; @@ -481,12 +477,20 @@ class PolyBus { } static void* create(uint8_t busType, uint8_t* pins, uint16_t len, uint8_t channel) { - #if defined(ARDUINO_ARCH_ESP32) && !(defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3)) // NOTE: "channel" is only used on ESP32 (and its variants) for RMT channel allocation + + #if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) + if (_useParallelI2S && (channel >= 8)) { + // Parallel I2S channels are to be used first, so subtract 8 to get the RMT channel number + channel -= 8; + } + #endif + + #if defined(ARDUINO_ARCH_ESP32) && !(defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3)) // since 0.15.0-b3 I2S1 is favoured for classic ESP32 and moved to position 0 (channel 0) so we need to subtract 1 for correct RMT allocation - if (useParallelI2S && channel > 7) channel -= 8; // accommodate parallel I2S1 which is used 1st on classic ESP32 - else if (channel > 0) channel--; // accommodate I2S1 which is used as 1st bus on classic ESP32 + if (!_useParallelI2S && channel > 0) channel--; // accommodate I2S1 which is used as 1st bus on classic ESP32 #endif + void* busPtr = nullptr; switch (busType) { case I_NONE: break; @@ -555,34 +559,19 @@ class PolyBus { case I_32_RN_TM1914_3: busPtr = new B_32_RN_TM1914_3(len, pins[0], (NeoBusChannel)channel); break; case I_32_RN_SM16825_5: busPtr = new B_32_RN_SM16825_5(len, pins[0], (NeoBusChannel)channel); break; // I2S1 bus or paralell buses - #ifndef WLED_NO_I2S1_PIXELBUS - case I_32_I1_NEO_3: if (useParallelI2S) busPtr = new B_32_I1_NEO_3P(len, pins[0]); else busPtr = new B_32_I1_NEO_3(len, pins[0]); break; - case I_32_I1_NEO_4: if (useParallelI2S) busPtr = new B_32_I1_NEO_4P(len, pins[0]); else busPtr = new B_32_I1_NEO_4(len, pins[0]); break; - case I_32_I1_400_3: if (useParallelI2S) busPtr = new B_32_I1_400_3P(len, pins[0]); else busPtr = new B_32_I1_400_3(len, pins[0]); break; - case I_32_I1_TM1_4: if (useParallelI2S) busPtr = new B_32_I1_TM1_4P(len, pins[0]); else busPtr = new B_32_I1_TM1_4(len, pins[0]); break; - case I_32_I1_TM2_3: if (useParallelI2S) busPtr = new B_32_I1_TM2_3P(len, pins[0]); else busPtr = new B_32_I1_TM2_3(len, pins[0]); break; - case I_32_I1_UCS_3: if (useParallelI2S) busPtr = new B_32_I1_UCS_3P(len, pins[0]); else busPtr = new B_32_I1_UCS_3(len, pins[0]); break; - case I_32_I1_UCS_4: if (useParallelI2S) busPtr = new B_32_I1_UCS_4P(len, pins[0]); else busPtr = new B_32_I1_UCS_4(len, pins[0]); break; - case I_32_I1_APA106_3: if (useParallelI2S) busPtr = new B_32_I1_APA106_3P(len, pins[0]); else busPtr = new B_32_I1_APA106_3(len, pins[0]); break; - case I_32_I1_FW6_5: if (useParallelI2S) busPtr = new B_32_I1_FW6_5P(len, pins[0]); else busPtr = new B_32_I1_FW6_5(len, pins[0]); break; - case I_32_I1_2805_5: if (useParallelI2S) busPtr = new B_32_I1_2805_5P(len, pins[0]); else busPtr = new B_32_I1_2805_5(len, pins[0]); break; - case I_32_I1_TM1914_3: if (useParallelI2S) busPtr = new B_32_I1_TM1914_3P(len, pins[0]); else busPtr = new B_32_I1_TM1914_3(len, pins[0]); break; - case I_32_I1_SM16825_5: if (useParallelI2S) busPtr = new B_32_I1_SM16825_5P(len, pins[0]); else busPtr = new B_32_I1_SM16825_5(len, pins[0]); break; - #endif - // I2S0 bus - #ifndef WLED_NO_I2S0_PIXELBUS - case I_32_I0_NEO_3: busPtr = new B_32_I0_NEO_3(len, pins[0]); break; - case I_32_I0_NEO_4: busPtr = new B_32_I0_NEO_4(len, pins[0]); break; - case I_32_I0_400_3: busPtr = new B_32_I0_400_3(len, pins[0]); break; - case I_32_I0_TM1_4: busPtr = new B_32_I0_TM1_4(len, pins[0]); break; - case I_32_I0_TM2_3: busPtr = new B_32_I0_TM2_3(len, pins[0]); break; - case I_32_I0_UCS_3: busPtr = new B_32_I0_UCS_3(len, pins[0]); break; - case I_32_I0_UCS_4: busPtr = new B_32_I0_UCS_4(len, pins[0]); break; - case I_32_I0_APA106_3: busPtr = new B_32_I0_APA106_3(len, pins[0]); break; - case I_32_I0_FW6_5: busPtr = new B_32_I0_FW6_5(len, pins[0]); break; - case I_32_I0_2805_5: busPtr = new B_32_I0_2805_5(len, pins[0]); break; - case I_32_I0_TM1914_3: busPtr = new B_32_I0_TM1914_3(len, pins[0]); break; - case I_32_I0_SM16825_5: busPtr = new B_32_I0_SM16825_5(len, pins[0]); break; + #ifndef CONFIG_IDF_TARGET_ESP32C3 + case I_32_I2_NEO_3: if (_useParallelI2S) busPtr = new B_32_IP_NEO_3(len, pins[0]); else busPtr = new B_32_I2_NEO_3(len, pins[0]); break; + case I_32_I2_NEO_4: if (_useParallelI2S) busPtr = new B_32_IP_NEO_4(len, pins[0]); else busPtr = new B_32_I2_NEO_4(len, pins[0]); break; + case I_32_I2_400_3: if (_useParallelI2S) busPtr = new B_32_IP_400_3(len, pins[0]); else busPtr = new B_32_I2_400_3(len, pins[0]); break; + case I_32_I2_TM1_4: if (_useParallelI2S) busPtr = new B_32_IP_TM1_4(len, pins[0]); else busPtr = new B_32_I2_TM1_4(len, pins[0]); break; + case I_32_I2_TM2_3: if (_useParallelI2S) busPtr = new B_32_IP_TM2_3(len, pins[0]); else busPtr = new B_32_I2_TM2_3(len, pins[0]); break; + case I_32_I2_UCS_3: if (_useParallelI2S) busPtr = new B_32_IP_UCS_3(len, pins[0]); else busPtr = new B_32_I2_UCS_3(len, pins[0]); break; + case I_32_I2_UCS_4: if (_useParallelI2S) busPtr = new B_32_IP_UCS_4(len, pins[0]); else busPtr = new B_32_I2_UCS_4(len, pins[0]); break; + case I_32_I2_APA106_3: if (_useParallelI2S) busPtr = new B_32_IP_APA106_3(len, pins[0]); else busPtr = new B_32_I2_APA106_3(len, pins[0]); break; + case I_32_I2_FW6_5: if (_useParallelI2S) busPtr = new B_32_IP_FW6_5(len, pins[0]); else busPtr = new B_32_I2_FW6_5(len, pins[0]); break; + case I_32_I2_2805_5: if (_useParallelI2S) busPtr = new B_32_IP_2805_5(len, pins[0]); else busPtr = new B_32_I2_2805_5(len, pins[0]); break; + case I_32_I2_TM1914_3: if (_useParallelI2S) busPtr = new B_32_IP_TM1914_3(len, pins[0]); else busPtr = new B_32_I2_TM1914_3(len, pins[0]); break; + case I_32_I2_SM16825_5: if (_useParallelI2S) busPtr = new B_32_IP_SM16825_5(len, pins[0]); else busPtr = new B_32_I2_SM16825_5(len, pins[0]); break; #endif #endif // for 2-wire: pins[1] is clk, pins[0] is dat. begin expects (len, clk, dat) @@ -669,34 +658,19 @@ class PolyBus { case I_32_RN_TM1914_3: (static_cast(busPtr))->Show(consistent); break; case I_32_RN_SM16825_5: (static_cast(busPtr))->Show(consistent); break; // I2S1 bus or paralell buses - #ifndef WLED_NO_I2S1_PIXELBUS - case I_32_I1_NEO_3: if (useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; - case I_32_I1_NEO_4: if (useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; - case I_32_I1_400_3: if (useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; - case I_32_I1_TM1_4: if (useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; - case I_32_I1_TM2_3: if (useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; - case I_32_I1_UCS_3: if (useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; - case I_32_I1_UCS_4: if (useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; - case I_32_I1_APA106_3: if (useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; - case I_32_I1_FW6_5: if (useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; - case I_32_I1_2805_5: if (useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; - case I_32_I1_TM1914_3: if (useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; - case I_32_I1_SM16825_5: if (useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; - #endif - // I2S0 bus - #ifndef WLED_NO_I2S0_PIXELBUS - case I_32_I0_NEO_3: (static_cast(busPtr))->Show(consistent); break; - case I_32_I0_NEO_4: (static_cast(busPtr))->Show(consistent); break; - case I_32_I0_400_3: (static_cast(busPtr))->Show(consistent); break; - case I_32_I0_TM1_4: (static_cast(busPtr))->Show(consistent); break; - case I_32_I0_TM2_3: (static_cast(busPtr))->Show(consistent); break; - case I_32_I0_UCS_3: (static_cast(busPtr))->Show(consistent); break; - case I_32_I0_UCS_4: (static_cast(busPtr))->Show(consistent); break; - case I_32_I0_APA106_3: (static_cast(busPtr))->Show(consistent); break; - case I_32_I0_FW6_5: (static_cast(busPtr))->Show(consistent); break; - case I_32_I0_2805_5: (static_cast(busPtr))->Show(consistent); break; - case I_32_I0_TM1914_3: (static_cast(busPtr))->Show(consistent); break; - case I_32_I0_SM16825_5: (static_cast(busPtr))->Show(consistent); break; + #ifndef CONFIG_IDF_TARGET_ESP32C3 + case I_32_I2_NEO_3: if (_useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; + case I_32_I2_NEO_4: if (_useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; + case I_32_I2_400_3: if (_useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; + case I_32_I2_TM1_4: if (_useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; + case I_32_I2_TM2_3: if (_useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; + case I_32_I2_UCS_3: if (_useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; + case I_32_I2_UCS_4: if (_useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; + case I_32_I2_APA106_3: if (_useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; + case I_32_I2_FW6_5: if (_useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; + case I_32_I2_2805_5: if (_useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; + case I_32_I2_TM1914_3: if (_useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; + case I_32_I2_SM16825_5: if (_useParallelI2S) (static_cast(busPtr))->Show(consistent); else (static_cast(busPtr))->Show(consistent); break; #endif #endif case I_HS_DOT_3: (static_cast(busPtr))->Show(consistent); break; @@ -743,6 +717,7 @@ class PolyBus { case I_8266_U0_UCS_4: return (static_cast(busPtr))->CanShow(); break; case I_8266_U1_UCS_4: return (static_cast(busPtr))->CanShow(); break; case I_8266_DM_UCS_4: return (static_cast(busPtr))->CanShow(); break; + case I_8266_BB_UCS_4: return (static_cast(busPtr))->CanShow(); break; case I_8266_U0_APA106_3: return (static_cast(busPtr))->CanShow(); break; case I_8266_U1_APA106_3: return (static_cast(busPtr))->CanShow(); break; case I_8266_DM_APA106_3: return (static_cast(busPtr))->CanShow(); break; @@ -779,34 +754,19 @@ class PolyBus { case I_32_RN_TM1914_3: return (static_cast(busPtr))->CanShow(); break; case I_32_RN_SM16825_5: return (static_cast(busPtr))->CanShow(); break; // I2S1 bus or paralell buses - #ifndef WLED_NO_I2S1_PIXELBUS - case I_32_I1_NEO_3: if (useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; - case I_32_I1_NEO_4: if (useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; - case I_32_I1_400_3: if (useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; - case I_32_I1_TM1_4: if (useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; - case I_32_I1_TM2_3: if (useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; - case I_32_I1_UCS_3: if (useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; - case I_32_I1_UCS_4: if (useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; - case I_32_I1_APA106_3: if (useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; - case I_32_I1_FW6_5: if (useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; - case I_32_I1_2805_5: if (useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; - case I_32_I1_TM1914_3: if (useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; - case I_32_I1_SM16825_5: if (useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; - #endif - // I2S0 bus - #ifndef WLED_NO_I2S0_PIXELBUS - case I_32_I0_NEO_3: return (static_cast(busPtr))->CanShow(); break; - case I_32_I0_NEO_4: return (static_cast(busPtr))->CanShow(); break; - case I_32_I0_400_3: return (static_cast(busPtr))->CanShow(); break; - case I_32_I0_TM1_4: return (static_cast(busPtr))->CanShow(); break; - case I_32_I0_TM2_3: return (static_cast(busPtr))->CanShow(); break; - case I_32_I0_UCS_3: return (static_cast(busPtr))->CanShow(); break; - case I_32_I0_UCS_4: return (static_cast(busPtr))->CanShow(); break; - case I_32_I0_APA106_3: return (static_cast(busPtr))->CanShow(); break; - case I_32_I0_FW6_5: return (static_cast(busPtr))->CanShow(); break; - case I_32_I0_2805_5: return (static_cast(busPtr))->CanShow(); break; - case I_32_I0_TM1914_3: return (static_cast(busPtr))->CanShow(); break; - case I_32_I0_SM16825_5: return (static_cast(busPtr))->CanShow(); break; + #ifndef CONFIG_IDF_TARGET_ESP32C3 + case I_32_I2_NEO_3: if (_useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; + case I_32_I2_NEO_4: if (_useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; + case I_32_I2_400_3: if (_useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; + case I_32_I2_TM1_4: if (_useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; + case I_32_I2_TM2_3: if (_useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; + case I_32_I2_UCS_3: if (_useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; + case I_32_I2_UCS_4: if (_useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; + case I_32_I2_APA106_3: if (_useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; + case I_32_I2_FW6_5: if (_useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; + case I_32_I2_2805_5: if (_useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; + case I_32_I2_TM1914_3: if (_useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; + case I_32_I2_SM16825_5: if (_useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break; #endif #endif case I_HS_DOT_3: return (static_cast(busPtr))->CanShow(); break; @@ -823,7 +783,7 @@ class PolyBus { return true; } - static void setPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint32_t c, uint8_t co, uint16_t wwcw = 0) { + [[gnu::hot]] static void setPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint32_t c, uint8_t co, uint16_t wwcw = 0) { uint8_t r = c >> 16; uint8_t g = c >> 8; uint8_t b = c >> 0; @@ -916,34 +876,19 @@ class PolyBus { case I_32_RN_TM1914_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_32_RN_SM16825_5: (static_cast(busPtr))->SetPixelColor(pix, Rgbww80Color(col.R*257, col.G*257, col.B*257, cctWW*257, cctCW*257)); break; // I2S1 bus or paralell buses - #ifndef WLED_NO_I2S1_PIXELBUS - case I_32_I1_NEO_3: if (useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; - case I_32_I1_NEO_4: if (useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast(busPtr))->SetPixelColor(pix, col); break; - case I_32_I1_400_3: if (useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; - case I_32_I1_TM1_4: if (useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast(busPtr))->SetPixelColor(pix, col); break; - case I_32_I1_TM2_3: if (useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; - case I_32_I1_UCS_3: if (useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break; - case I_32_I1_UCS_4: if (useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break; - case I_32_I1_APA106_3: if (useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; - case I_32_I1_FW6_5: if (useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); else (static_cast(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break; - case I_32_I1_2805_5: if (useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); else (static_cast(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break; - case I_32_I1_TM1914_3: if (useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; - case I_32_I1_SM16825_5: if (useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, Rgbww80Color(col.R*257, col.G*257, col.B*257, cctWW*257, cctCW*257)); else (static_cast(busPtr))->SetPixelColor(pix, Rgbww80Color(col.R*257, col.G*257, col.B*257, cctWW*257, cctCW*257)); break; - #endif - // I2S0 bus - #ifndef WLED_NO_I2S0_PIXELBUS - case I_32_I0_NEO_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; - case I_32_I0_NEO_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; - case I_32_I0_400_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; - case I_32_I0_TM1_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; - case I_32_I0_TM2_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; - case I_32_I0_UCS_3: (static_cast(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break; - case I_32_I0_UCS_4: (static_cast(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break; - case I_32_I0_APA106_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; - case I_32_I0_FW6_5: (static_cast(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break; - case I_32_I0_2805_5: (static_cast(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break; - case I_32_I0_TM1914_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; - case I_32_I0_SM16825_5: (static_cast(busPtr))->SetPixelColor(pix, Rgbww80Color(col.R*257, col.G*257, col.B*257, cctWW*257, cctCW*257)); break; + #ifndef CONFIG_IDF_TARGET_ESP32C3 + case I_32_I2_NEO_3: if (_useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; + case I_32_I2_NEO_4: if (_useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, col); else (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_32_I2_400_3: if (_useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; + case I_32_I2_TM1_4: if (_useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, col); else (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_32_I2_TM2_3: if (_useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; + case I_32_I2_UCS_3: if (_useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); else (static_cast(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break; + case I_32_I2_UCS_4: if (_useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); else (static_cast(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break; + case I_32_I2_APA106_3: if (_useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; + case I_32_I2_FW6_5: if (_useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); else (static_cast(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break; + case I_32_I2_2805_5: if (_useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); else (static_cast(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break; + case I_32_I2_TM1914_3: if (_useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); else (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; + case I_32_I2_SM16825_5: if (_useParallelI2S) (static_cast(busPtr))->SetPixelColor(pix, Rgbww80Color(col.R*257, col.G*257, col.B*257, cctWW*257, cctCW*257)); else (static_cast(busPtr))->SetPixelColor(pix, Rgbww80Color(col.R*257, col.G*257, col.B*257, cctWW*257, cctCW*257)); break; #endif #endif case I_HS_DOT_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; @@ -1027,34 +972,19 @@ class PolyBus { case I_32_RN_TM1914_3: (static_cast(busPtr))->SetLuminance(b); break; case I_32_RN_SM16825_5: (static_cast(busPtr))->SetLuminance(b); break; // I2S1 bus or paralell buses - #ifndef WLED_NO_I2S1_PIXELBUS - case I_32_I1_NEO_3: if (useParallelI2S) (static_cast(busPtr))->SetLuminance(b); else (static_cast(busPtr))->SetLuminance(b); break; - case I_32_I1_NEO_4: if (useParallelI2S) (static_cast(busPtr))->SetLuminance(b); else (static_cast(busPtr))->SetLuminance(b); break; - case I_32_I1_400_3: if (useParallelI2S) (static_cast(busPtr))->SetLuminance(b); else (static_cast(busPtr))->SetLuminance(b); break; - case I_32_I1_TM1_4: if (useParallelI2S) (static_cast(busPtr))->SetLuminance(b); else (static_cast(busPtr))->SetLuminance(b); break; - case I_32_I1_TM2_3: if (useParallelI2S) (static_cast(busPtr))->SetLuminance(b); else (static_cast(busPtr))->SetLuminance(b); break; - case I_32_I1_UCS_3: if (useParallelI2S) (static_cast(busPtr))->SetLuminance(b); else (static_cast(busPtr))->SetLuminance(b); break; - case I_32_I1_UCS_4: if (useParallelI2S) (static_cast(busPtr))->SetLuminance(b); else (static_cast(busPtr))->SetLuminance(b); break; - case I_32_I1_APA106_3: if (useParallelI2S) (static_cast(busPtr))->SetLuminance(b); else (static_cast(busPtr))->SetLuminance(b); break; - case I_32_I1_FW6_5: if (useParallelI2S) (static_cast(busPtr))->SetLuminance(b); else (static_cast(busPtr))->SetLuminance(b); break; - case I_32_I1_2805_5: if (useParallelI2S) (static_cast(busPtr))->SetLuminance(b); else (static_cast(busPtr))->SetLuminance(b); break; - case I_32_I1_TM1914_3: if (useParallelI2S) (static_cast(busPtr))->SetLuminance(b); else (static_cast(busPtr))->SetLuminance(b); break; - case I_32_I1_SM16825_5: if (useParallelI2S) (static_cast(busPtr))->SetLuminance(b); else (static_cast(busPtr))->SetLuminance(b); break; - #endif - // I2S0 bus - #ifndef WLED_NO_I2S0_PIXELBUS - case I_32_I0_NEO_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_32_I0_NEO_4: (static_cast(busPtr))->SetLuminance(b); break; - case I_32_I0_400_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_32_I0_TM1_4: (static_cast(busPtr))->SetLuminance(b); break; - case I_32_I0_TM2_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_32_I0_UCS_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_32_I0_UCS_4: (static_cast(busPtr))->SetLuminance(b); break; - case I_32_I0_APA106_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_32_I0_FW6_5: (static_cast(busPtr))->SetLuminance(b); break; - case I_32_I0_2805_5: (static_cast(busPtr))->SetLuminance(b); break; - case I_32_I0_TM1914_3: (static_cast(busPtr))->SetLuminance(b); break; - case I_32_I0_SM16825_5: (static_cast(busPtr))->SetLuminance(b); break; + #ifndef CONFIG_IDF_TARGET_ESP32C3 + case I_32_I2_NEO_3: if (_useParallelI2S) (static_cast(busPtr))->SetLuminance(b); else (static_cast(busPtr))->SetLuminance(b); break; + case I_32_I2_NEO_4: if (_useParallelI2S) (static_cast(busPtr))->SetLuminance(b); else (static_cast(busPtr))->SetLuminance(b); break; + case I_32_I2_400_3: if (_useParallelI2S) (static_cast(busPtr))->SetLuminance(b); else (static_cast(busPtr))->SetLuminance(b); break; + case I_32_I2_TM1_4: if (_useParallelI2S) (static_cast(busPtr))->SetLuminance(b); else (static_cast(busPtr))->SetLuminance(b); break; + case I_32_I2_TM2_3: if (_useParallelI2S) (static_cast(busPtr))->SetLuminance(b); else (static_cast(busPtr))->SetLuminance(b); break; + case I_32_I2_UCS_3: if (_useParallelI2S) (static_cast(busPtr))->SetLuminance(b); else (static_cast(busPtr))->SetLuminance(b); break; + case I_32_I2_UCS_4: if (_useParallelI2S) (static_cast(busPtr))->SetLuminance(b); else (static_cast(busPtr))->SetLuminance(b); break; + case I_32_I2_APA106_3: if (_useParallelI2S) (static_cast(busPtr))->SetLuminance(b); else (static_cast(busPtr))->SetLuminance(b); break; + case I_32_I2_FW6_5: if (_useParallelI2S) (static_cast(busPtr))->SetLuminance(b); else (static_cast(busPtr))->SetLuminance(b); break; + case I_32_I2_2805_5: if (_useParallelI2S) (static_cast(busPtr))->SetLuminance(b); else (static_cast(busPtr))->SetLuminance(b); break; + case I_32_I2_TM1914_3: if (_useParallelI2S) (static_cast(busPtr))->SetLuminance(b); else (static_cast(busPtr))->SetLuminance(b); break; + case I_32_I2_SM16825_5: if (_useParallelI2S) (static_cast(busPtr))->SetLuminance(b); else (static_cast(busPtr))->SetLuminance(b); break; #endif #endif case I_HS_DOT_3: (static_cast(busPtr))->SetLuminance(b); break; @@ -1070,7 +1000,7 @@ class PolyBus { } } - static uint32_t getPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint8_t co) { + [[gnu::hot]] static uint32_t getPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint8_t co) { RgbwColor col(0,0,0,0); switch (busType) { case I_NONE: break; @@ -1139,34 +1069,19 @@ class PolyBus { case I_32_RN_TM1914_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; case I_32_RN_SM16825_5: { Rgbww80Color c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R/257,c.G/257,c.B/257,max(c.WW,c.CW)/257); } break; // will not return original W // I2S1 bus or paralell buses - #ifndef WLED_NO_I2S1_PIXELBUS - case I_32_I1_NEO_3: col = (useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); break; - case I_32_I1_NEO_4: col = (useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); break; - case I_32_I1_400_3: col = (useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); break; - case I_32_I1_TM1_4: col = (useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); break; - case I_32_I1_TM2_3: col = (useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); break; - case I_32_I1_UCS_3: { Rgb48Color c = (useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R/257,c.G/257,c.B/257,0); } break; - case I_32_I1_UCS_4: { Rgbw64Color c = (useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R/257,c.G/257,c.B/257,c.W/257); } break; - case I_32_I1_APA106_3: col = (useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); break; - case I_32_I1_FW6_5: { RgbwwColor c = (useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W - case I_32_I1_2805_5: { RgbwwColor c = (useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W - case I_32_I1_TM1914_3: col = (useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); break; - case I_32_I1_SM16825_5: { Rgbww80Color c = (useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R/257,c.G/257,c.B/257,max(c.WW,c.CW)/257); } break; // will not return original W - #endif - // I2S0 bus - #ifndef WLED_NO_I2S0_PIXELBUS - case I_32_I0_NEO_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; - case I_32_I0_NEO_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; - case I_32_I0_400_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; - case I_32_I0_TM1_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; - case I_32_I0_TM2_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; - case I_32_I0_UCS_3: { Rgb48Color c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R/257,c.G/257,c.B/257,0); } break; - case I_32_I0_UCS_4: { Rgbw64Color c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R/257,c.G/257,c.B/257,c.W/257); } break; - case I_32_I0_APA106_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; - case I_32_I0_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W - case I_32_I0_2805_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W - case I_32_I0_TM1914_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; - case I_32_I0_SM16825_5: { Rgbww80Color c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R/257,c.G/257,c.B/257,max(c.WW,c.CW)/257); } break; // will not return original W + #ifndef CONFIG_IDF_TARGET_ESP32C3 + case I_32_I2_NEO_3: col = (_useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_I2_NEO_4: col = (_useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_I2_400_3: col = (_useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_I2_TM1_4: col = (_useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_I2_TM2_3: col = (_useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_I2_UCS_3: { Rgb48Color c = (_useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R/257,c.G/257,c.B/257,0); } break; + case I_32_I2_UCS_4: { Rgbw64Color c = (_useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R/257,c.G/257,c.B/257,c.W/257); } break; + case I_32_I2_APA106_3: col = (_useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_I2_FW6_5: { RgbwwColor c = (_useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W + case I_32_I2_2805_5: { RgbwwColor c = (_useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W + case I_32_I2_TM1914_3: col = (_useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_I2_SM16825_5: { Rgbww80Color c = (_useParallelI2S) ? (static_cast(busPtr))->GetPixelColor(pix) : (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R/257,c.G/257,c.B/257,max(c.WW,c.CW)/257); } break; // will not return original W #endif #endif case I_HS_DOT_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; @@ -1269,34 +1184,19 @@ class PolyBus { case I_32_RN_TM1914_3: delete (static_cast(busPtr)); break; case I_32_RN_SM16825_5: delete (static_cast(busPtr)); break; // I2S1 bus or paralell buses - #ifndef WLED_NO_I2S1_PIXELBUS - case I_32_I1_NEO_3: if (useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; - case I_32_I1_NEO_4: if (useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; - case I_32_I1_400_3: if (useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; - case I_32_I1_TM1_4: if (useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; - case I_32_I1_TM2_3: if (useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; - case I_32_I1_UCS_3: if (useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; - case I_32_I1_UCS_4: if (useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; - case I_32_I1_APA106_3: if (useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; - case I_32_I1_FW6_5: if (useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; - case I_32_I1_2805_5: if (useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; - case I_32_I1_TM1914_3: if (useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; - case I_32_I1_SM16825_5: if (useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; - #endif - // I2S0 bus - #ifndef WLED_NO_I2S0_PIXELBUS - case I_32_I0_NEO_3: delete (static_cast(busPtr)); break; - case I_32_I0_NEO_4: delete (static_cast(busPtr)); break; - case I_32_I0_400_3: delete (static_cast(busPtr)); break; - case I_32_I0_TM1_4: delete (static_cast(busPtr)); break; - case I_32_I0_TM2_3: delete (static_cast(busPtr)); break; - case I_32_I0_UCS_3: delete (static_cast(busPtr)); break; - case I_32_I0_UCS_4: delete (static_cast(busPtr)); break; - case I_32_I0_APA106_3: delete (static_cast(busPtr)); break; - case I_32_I0_FW6_5: delete (static_cast(busPtr)); break; - case I_32_I0_2805_5: delete (static_cast(busPtr)); break; - case I_32_I0_TM1914_3: delete (static_cast(busPtr)); break; - case I_32_I0_SM16825_5: delete (static_cast(busPtr)); break; + #ifndef CONFIG_IDF_TARGET_ESP32C3 + case I_32_I2_NEO_3: if (_useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; + case I_32_I2_NEO_4: if (_useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; + case I_32_I2_400_3: if (_useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; + case I_32_I2_TM1_4: if (_useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; + case I_32_I2_TM2_3: if (_useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; + case I_32_I2_UCS_3: if (_useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; + case I_32_I2_UCS_4: if (_useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; + case I_32_I2_APA106_3: if (_useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; + case I_32_I2_FW6_5: if (_useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; + case I_32_I2_2805_5: if (_useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; + case I_32_I2_TM1914_3: if (_useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; + case I_32_I2_SM16825_5: if (_useParallelI2S) delete (static_cast(busPtr)); else delete (static_cast(busPtr)); break; #endif #endif case I_HS_DOT_3: delete (static_cast(busPtr)); break; @@ -1312,8 +1212,175 @@ class PolyBus { } } + static unsigned getDataSize(void* busPtr, uint8_t busType) { + unsigned size = 0; + switch (busType) { + case I_NONE: break; + #ifdef ESP8266 + case I_8266_U0_NEO_3: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_U1_NEO_3: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_DM_NEO_3: size = (static_cast(busPtr))->PixelsSize()*5; break; + case I_8266_BB_NEO_3: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_U0_NEO_4: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_U1_NEO_4: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_DM_NEO_4: size = (static_cast(busPtr))->PixelsSize()*5; break; + case I_8266_BB_NEO_4: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_U0_400_3: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_U1_400_3: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_DM_400_3: size = (static_cast(busPtr))->PixelsSize()*5; break; + case I_8266_BB_400_3: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_U0_TM1_4: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_U1_TM1_4: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_DM_TM1_4: size = (static_cast(busPtr))->PixelsSize()*5; break; + case I_8266_BB_TM1_4: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_U0_TM2_3: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_U1_TM2_3: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_DM_TM2_3: size = (static_cast(busPtr))->PixelsSize()*5; break; + case I_8266_BB_TM2_3: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_U0_UCS_3: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_U1_UCS_3: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_DM_UCS_3: size = (static_cast(busPtr))->PixelsSize()*5; break; + case I_8266_BB_UCS_3: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_U0_UCS_4: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_U1_UCS_4: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_DM_UCS_4: size = (static_cast(busPtr))->PixelsSize()*5; break; + case I_8266_BB_UCS_4: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_U0_APA106_3: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_U1_APA106_3: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_DM_APA106_3: size = (static_cast(busPtr))->PixelsSize()*5; break; + case I_8266_BB_APA106_3: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_U0_FW6_5: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_U1_FW6_5: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_DM_FW6_5: size = (static_cast(busPtr))->PixelsSize()*5; break; + case I_8266_BB_FW6_5: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_U0_2805_5: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_U1_2805_5: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_DM_2805_5: size = (static_cast(busPtr))->PixelsSize()*5; break; + case I_8266_BB_2805_5: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_U0_TM1914_3: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_U1_TM1914_3: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_DM_TM1914_3: size = (static_cast(busPtr))->PixelsSize()*5; break; + case I_8266_BB_TM1914_3: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_U0_SM16825_5: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_U1_SM16825_5: size = (static_cast(busPtr))->PixelsSize(); break; + case I_8266_DM_SM16825_5: size = (static_cast(busPtr))->PixelsSize()*5; break; + case I_8266_BB_SM16825_5: size = (static_cast(busPtr))->PixelsSize(); break; + #endif + #ifdef ARDUINO_ARCH_ESP32 + // RMT buses (front + back + small system managed RMT) + case I_32_RN_NEO_3: size = (static_cast(busPtr))->PixelsSize()*2; break; + case I_32_RN_NEO_4: size = (static_cast(busPtr))->PixelsSize()*2; break; + case I_32_RN_400_3: size = (static_cast(busPtr))->PixelsSize()*2; break; + case I_32_RN_TM1_4: size = (static_cast(busPtr))->PixelsSize()*2; break; + case I_32_RN_TM2_3: size = (static_cast(busPtr))->PixelsSize()*2; break; + case I_32_RN_UCS_3: size = (static_cast(busPtr))->PixelsSize()*2; break; + case I_32_RN_UCS_4: size = (static_cast(busPtr))->PixelsSize()*2; break; + case I_32_RN_APA106_3: size = (static_cast(busPtr))->PixelsSize()*2; break; + case I_32_RN_FW6_5: size = (static_cast(busPtr))->PixelsSize()*2; break; + case I_32_RN_2805_5: size = (static_cast(busPtr))->PixelsSize()*2; break; + case I_32_RN_TM1914_3: size = (static_cast(busPtr))->PixelsSize()*2; break; + case I_32_RN_SM16825_5: size = (static_cast(busPtr))->PixelsSize()*2; break; + // I2S1 bus or paralell buses (front + DMA; DMA = front * cadence, aligned to 4 bytes) + #ifndef CONFIG_IDF_TARGET_ESP32C3 + case I_32_I2_NEO_3: size = (_useParallelI2S) ? (static_cast(busPtr))->PixelsSize()*4 : (static_cast(busPtr))->PixelsSize()*4; break; + case I_32_I2_NEO_4: size = (_useParallelI2S) ? (static_cast(busPtr))->PixelsSize()*4 : (static_cast(busPtr))->PixelsSize()*4; break; + case I_32_I2_400_3: size = (_useParallelI2S) ? (static_cast(busPtr))->PixelsSize()*4 : (static_cast(busPtr))->PixelsSize()*4; break; + case I_32_I2_TM1_4: size = (_useParallelI2S) ? (static_cast(busPtr))->PixelsSize()*4 : (static_cast(busPtr))->PixelsSize()*4; break; + case I_32_I2_TM2_3: size = (_useParallelI2S) ? (static_cast(busPtr))->PixelsSize()*4 : (static_cast(busPtr))->PixelsSize()*4; break; + case I_32_I2_UCS_3: size = (_useParallelI2S) ? (static_cast(busPtr))->PixelsSize()*4 : (static_cast(busPtr))->PixelsSize()*4; break; + case I_32_I2_UCS_4: size = (_useParallelI2S) ? (static_cast(busPtr))->PixelsSize()*4 : (static_cast(busPtr))->PixelsSize()*4; break; + case I_32_I2_APA106_3: size = (_useParallelI2S) ? (static_cast(busPtr))->PixelsSize()*4 : (static_cast(busPtr))->PixelsSize()*4; break; + case I_32_I2_FW6_5: size = (_useParallelI2S) ? (static_cast(busPtr))->PixelsSize()*4 : (static_cast(busPtr))->PixelsSize()*4; break; + case I_32_I2_2805_5: size = (_useParallelI2S) ? (static_cast(busPtr))->PixelsSize()*4 : (static_cast(busPtr))->PixelsSize()*4; break; + case I_32_I2_TM1914_3: size = (_useParallelI2S) ? (static_cast(busPtr))->PixelsSize()*4 : (static_cast(busPtr))->PixelsSize()*4; break; + case I_32_I2_SM16825_5: size = (_useParallelI2S) ? (static_cast(busPtr))->PixelsSize()*4 : (static_cast(busPtr))->PixelsSize()*4; break; + #endif + #endif + case I_HS_DOT_3: size = (static_cast(busPtr))->PixelsSize()*2; break; + case I_SS_DOT_3: size = (static_cast(busPtr))->PixelsSize()*2; break; + case I_HS_LPD_3: size = (static_cast(busPtr))->PixelsSize()*2; break; + case I_SS_LPD_3: size = (static_cast(busPtr))->PixelsSize()*2; break; + case I_HS_LPO_3: size = (static_cast(busPtr))->PixelsSize()*2; break; + case I_SS_LPO_3: size = (static_cast(busPtr))->PixelsSize()*2; break; + case I_HS_WS1_3: size = (static_cast(busPtr))->PixelsSize()*2; break; + case I_SS_WS1_3: size = (static_cast(busPtr))->PixelsSize()*2; break; + case I_HS_P98_3: size = (static_cast(busPtr))->PixelsSize()*2; break; + case I_SS_P98_3: size = (static_cast(busPtr))->PixelsSize()*2; break; + } + return size; + } + + static unsigned memUsage(unsigned count, unsigned busType) { + unsigned size = count*3; // let's assume 3 channels, we will add count or 2*count below for 4 channels or 5 channels + switch (busType) { + case I_NONE: size = 0; break; + #ifdef ESP8266 + // UART methods have front + back buffers + small UART + case I_8266_U0_NEO_4 : // fallthrough + case I_8266_U1_NEO_4 : // fallthrough + case I_8266_BB_NEO_4 : // fallthrough + case I_8266_U0_TM1_4 : // fallthrough + case I_8266_U1_TM1_4 : // fallthrough + case I_8266_BB_TM1_4 : size = (size + count); break; // 4 channels + case I_8266_U0_UCS_3 : // fallthrough + case I_8266_U1_UCS_3 : // fallthrough + case I_8266_BB_UCS_3 : size *= 2; break; // 16 bit + case I_8266_U0_UCS_4 : // fallthrough + case I_8266_U1_UCS_4 : // fallthrough + case I_8266_BB_UCS_4 : size = (size + count)*2; break; // 16 bit 4 channels + case I_8266_U0_FW6_5 : // fallthrough + case I_8266_U1_FW6_5 : // fallthrough + case I_8266_BB_FW6_5 : // fallthrough + case I_8266_U0_2805_5 : // fallthrough + case I_8266_U1_2805_5 : // fallthrough + case I_8266_BB_2805_5 : size = (size + 2*count); break; // 5 channels + case I_8266_U0_SM16825_5: // fallthrough + case I_8266_U1_SM16825_5: // fallthrough + case I_8266_BB_SM16825_5: size = (size + 2*count)*2; break; // 16 bit 5 channels + // DMA methods have front + DMA buffer = ((1+(3+1)) * channels; exact value is a bit of mistery - needs a dig into NPB) + case I_8266_DM_NEO_3 : // fallthrough + case I_8266_DM_400_3 : // fallthrough + case I_8266_DM_TM2_3 : // fallthrough + case I_8266_DM_APA106_3 : // fallthrough + case I_8266_DM_TM1914_3 : size *= 5; break; + case I_8266_DM_NEO_4 : // fallthrough + case I_8266_DM_TM1_4 : size = (size + count)*5; break; + case I_8266_DM_UCS_3 : size *= 2*5; break; + case I_8266_DM_UCS_4 : size = (size + count)*2*5; break; + case I_8266_DM_FW6_5 : // fallthrough + case I_8266_DM_2805_5 : size = (size + 2*count)*5; break; + case I_8266_DM_SM16825_5: size = (size + 2*count)*2*5; break; + #else + // RMT buses (1x front and 1x back buffer, does not include small RMT buffer) + case I_32_RN_NEO_4 : // fallthrough + case I_32_RN_TM1_4 : size = (size + count)*2; break; // 4 channels + case I_32_RN_UCS_3 : size *= 2*2; break; // 16bit + case I_32_RN_UCS_4 : size = (size + count)*2*2; break; // 16bit, 4 channels + case I_32_RN_FW6_5 : // fallthrough + case I_32_RN_2805_5 : size = (size + 2*count)*2; break; // 5 channels + case I_32_RN_SM16825_5: size = (size + 2*count)*2*2; break; // 16bit, 5 channels + // I2S1 bus or paralell I2S1 buses (1x front, does not include DMA buffer which is front*cadence, a bit(?) more for LCD) + #ifndef CONFIG_IDF_TARGET_ESP32C3 + case I_32_I2_NEO_3 : // fallthrough + case I_32_I2_400_3 : // fallthrough + case I_32_I2_TM2_3 : // fallthrough + case I_32_I2_APA106_3 : break; // do nothing, I2S uses single buffer + DMA buffer + case I_32_I2_NEO_4 : // fallthrough + case I_32_I2_TM1_4 : size = (size + count); break; // 4 channels + case I_32_I2_UCS_3 : size *= 2; break; // 16 bit + case I_32_I2_UCS_4 : size = (size + count)*2; break; // 16 bit, 4 channels + case I_32_I2_FW6_5 : // fallthrough + case I_32_I2_2805_5 : size = (size + 2*count); break; // 5 channels + case I_32_I2_SM16825_5: size = (size + 2*count)*2; break; // 16 bit, 5 channels + #endif + default : size *= 2; break; // everything else uses 2 buffers + #endif + } + return size; + } + //gives back the internal type index (I_XX_XXX_X above) for the input - static uint8_t getI(uint8_t busType, uint8_t* pins, uint8_t num = 0) { + static uint8_t getI(uint8_t busType, const uint8_t* pins, uint8_t num = 0) { if (!Bus::isDigital(busType)) return I_NONE; if (Bus::is2Pin(busType)) { //SPI LED chips bool isHSPI = false; @@ -1369,29 +1436,37 @@ class PolyBus { return I_8266_U0_SM16825_5 + offset; } #else //ESP32 - uint8_t offset = 0; // 0 = RMT (num 1-8), 1 = I2S0 (used by Audioreactive), 2 = I2S1 + uint8_t offset = 0; // 0 = RMT (num 1-8), 1 = I2S1 [I2S0 is used by Audioreactive] #if defined(CONFIG_IDF_TARGET_ESP32S2) // ESP32-S2 only has 4 RMT channels - if (num > 4) return I_NONE; - if (num > 3) offset = 1; // only one I2S (use last to allow Audioreactive) + if (_useParallelI2S) { + if (num > 11) return I_NONE; + if (num < 8) offset = 1; // use x8 parallel I2S0 channels followed by RMT + // Note: conflicts with AudioReactive if enabled + } else { + if (num > 4) return I_NONE; + if (num > 3) offset = 1; // only one I2S0 (use last to allow Audioreactive) + } #elif defined(CONFIG_IDF_TARGET_ESP32C3) // On ESP32-C3 only the first 2 RMT channels are usable for transmitting if (num > 1) return I_NONE; //if (num > 1) offset = 1; // I2S not supported yet (only 1 I2S) #elif defined(CONFIG_IDF_TARGET_ESP32S3) // On ESP32-S3 only the first 4 RMT channels are usable for transmitting - if (num > 3) return I_NONE; - //if (num > 3) offset = num -4; // I2S not supported yet + if (_useParallelI2S) { + if (num > 11) return I_NONE; + if (num < 8) offset = 1; // use x8 parallel I2S LCD channels, followed by RMT + } else { + if (num > 3) return I_NONE; // do not use single I2S (as it is not supported) + } #else - // standard ESP32 has 8 RMT and 2 I2S channels - if (useParallelI2S) { - if (num > 16) return I_NONE; - if (num < 8) offset = 2; // prefer 8 parallel I2S1 channels - if (num == 16) offset = 1; + // standard ESP32 has 8 RMT and x1/x8 I2S1 channels + if (_useParallelI2S) { + if (num > 15) return I_NONE; + if (num < 8) offset = 1; // 8 I2S followed by 8 RMT } else { if (num > 9) return I_NONE; - if (num > 8) offset = 1; - if (num == 0) offset = 2; // prefer I2S1 for 1st bus (less flickering but more RAM needed) + if (num == 0) offset = 1; // prefer I2S1 for 1st bus (less flickering but more RAM needed) } #endif switch (busType) { diff --git a/wled00/button.cpp b/wled00/button.cpp index 4d6f954f60..41b2141ce2 100644 --- a/wled00/button.cpp +++ b/wled00/button.cpp @@ -40,7 +40,7 @@ void longPressAction(uint8_t b) { if (!macroLongPress[b]) { switch (b) { - case 0: setRandomColor(col); colorUpdated(CALL_MODE_BUTTON); break; + case 0: setRandomColor(colPri); colorUpdated(CALL_MODE_BUTTON); break; case 1: if(buttonBriDirection) { if (bri == 255) break; // avoid unnecessary updates to brightness @@ -230,7 +230,7 @@ void handleAnalog(uint8_t b) effectPalette = constrain(effectPalette, 0, strip.getPaletteCount()-1); // map is allowed to "overshoot", so we need to contrain the result } else if (macroDoublePress[b] == 200) { // primary color, hue, full saturation - colorHStoRGB(aRead*256,255,col); + colorHStoRGB(aRead*256,255,colPri); } else { // otherwise use "double press" for segment selection Segment& seg = strip.getSegment(macroDoublePress[b]); @@ -375,6 +375,7 @@ void handleIO() if (rlyPin>=0) { pinMode(rlyPin, rlyOpenDrain ? OUTPUT_OPEN_DRAIN : OUTPUT); digitalWrite(rlyPin, rlyMde); + delay(50); // wait for relay to switch and power to stabilize } offMode = false; } diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 3f6cfbacb6..90470d2caf 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -118,6 +118,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { Bus::setCCTBlend(strip.cctBlending); strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS CJSON(useGlobalLedBuffer, hw_led[F("ld")]); + #if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) + CJSON(useParallelI2S, hw_led[F("prl")]); + #endif #ifndef WLED_DISABLE_2D // 2D Matrix Settings @@ -162,34 +165,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { DEBUG_PRINTF_P(PSTR("Heap before buses: %d\n"), ESP.getFreeHeap()); int s = 0; // bus iterator if (fromFS) BusManager::removeAll(); // can't safely manipulate busses directly in network callback - unsigned mem = 0; - - // determine if it is sensible to use parallel I2S outputs on ESP32 (i.e. more than 5 outputs = 1 I2S + 4 RMT) - bool useParallel = false; - #if defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ARCH_ESP32S2) && !defined(ARDUINO_ARCH_ESP32S3) && !defined(ARDUINO_ARCH_ESP32C3) - unsigned digitalCount = 0; - unsigned maxLedsOnBus = 0; - unsigned maxChannels = 0; - for (JsonObject elm : ins) { - unsigned type = elm["type"] | TYPE_WS2812_RGB; - unsigned len = elm["len"] | DEFAULT_LED_COUNT; - if (!Bus::isDigital(type)) continue; - if (!Bus::is2Pin(type)) { - digitalCount++; - unsigned channels = Bus::getNumberOfChannels(type); - if (len > maxLedsOnBus) maxLedsOnBus = len; - if (channels > maxChannels) maxChannels = channels; - } - } - DEBUG_PRINTF_P(PSTR("Maximum LEDs on a bus: %u\nDigital buses: %u\n"), maxLedsOnBus, digitalCount); - // we may remove 300 LEDs per bus limit when NeoPixelBus is updated beyond 2.9.0 - if (maxLedsOnBus <= 300 && digitalCount > 5) { - DEBUG_PRINTLN(F("Switching to parallel I2S.")); - useParallel = true; - BusManager::useParallelOutput(); - mem = BusManager::memUsage(maxChannels, maxLedsOnBus, 8); // use alternate memory calculation - } - #endif for (JsonObject elm : ins) { if (s >= WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES) break; @@ -220,24 +195,11 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { maMax = 0; } ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh - if (fromFS) { - BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax); - if (useParallel && s < 8) { - // if for some unexplained reason the above pre-calculation was wrong, update - unsigned memT = BusManager::memUsage(bc); // includes x8 memory allocation for parallel I2S - if (memT > mem) mem = memT; // if we have unequal LED count use the largest - } else - mem += BusManager::memUsage(bc); // includes global buffer - if (mem <= MAX_LED_MEMORY) if (BusManager::add(bc) == -1) break; // finalization will be done in WLED::beginStrip() - } else { - if (busConfigs[s] != nullptr) delete busConfigs[s]; - busConfigs[s] = new BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax); - doInitBusses = true; // finalization done in beginStrip() - } + + busConfigs.push_back(std::move(BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax))); + doInitBusses = true; // finalization done in beginStrip() s++; } - DEBUG_PRINTF_P(PSTR("LED buffer size: %uB\n"), mem); - DEBUG_PRINTF_P(PSTR("Heap after buses: %d\n"), ESP.getFreeHeap()); } if (hw_led["rev"]) BusManager::getBus(0)->setReversed(true); //set 0.11 global reversed setting for first bus @@ -674,19 +636,42 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { return (doc["sv"] | true); } - static const char s_cfg_json[] PROGMEM = "/cfg.json"; -void deserializeConfigFromFS() { - bool success = deserializeConfigSec(); +bool backupConfig() { + return backupFile(s_cfg_json); +} + +bool restoreConfig() { + return restoreFile(s_cfg_json); +} + +bool verifyConfig() { + return validateJsonFile(s_cfg_json); +} + +// rename config file and reboot +// if the cfg file doesn't exist, such as after a reset, do nothing +void resetConfig() { + if (WLED_FS.exists(s_cfg_json)) { + DEBUG_PRINTLN(F("Reset config")); + char backupname[32]; + snprintf_P(backupname, sizeof(backupname), PSTR("/rst.%s"), &s_cfg_json[1]); + WLED_FS.rename(s_cfg_json, backupname); + doReboot = true; + } +} + +bool deserializeConfigFromFS() { + [[maybe_unused]] bool success = deserializeConfigSec(); #ifdef WLED_ADD_EEPROM_SUPPORT if (!success) { //if file does not exist, try reading from EEPROM deEEPSettings(); - return; + return true; } #endif - if (!requestJSONBufferLock(1)) return; + if (!requestJSONBufferLock(1)) return false; DEBUG_PRINTLN(F("Reading settings from /cfg.json...")); @@ -696,17 +681,11 @@ void deserializeConfigFromFS() { #ifdef WLED_ADD_EEPROM_SUPPORT deEEPSettings(); #endif - - // save default values to /cfg.json - // call readFromConfig() with an empty object so that usermods can initialize to defaults prior to saving - JsonObject empty = JsonObject(); - UsermodManager::readFromConfig(empty); - serializeConfig(); // init Ethernet (in case default type is set at compile time) #ifdef WLED_USE_ETHERNET WLED::instance().initEthernet(); #endif - return; + return true; // config does not exist (we will need to save it once strip is initialised) } // NOTE: This routine deserializes *and* applies the configuration @@ -715,11 +694,12 @@ void deserializeConfigFromFS() { bool needsSave = deserializeConfig(root, true); releaseJSONBufferLock(); - if (needsSave) serializeConfig(); // usermods required new parameters + return needsSave; } void serializeConfig() { serializeConfigSec(); + backupConfig(); // backup before writing new config DEBUG_PRINTLN(F("Writing settings to /cfg.json...")); @@ -824,6 +804,9 @@ void serializeConfig() { hw_led["fps"] = strip.getTargetFps(); hw_led[F("rgbwm")] = Bus::getGlobalAWMode(); // global auto white mode override hw_led[F("ld")] = useGlobalLedBuffer; + #if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) + hw_led[F("prl")] = BusManager::hasParallelOutput(); + #endif #ifndef WLED_DISABLE_2D // 2D Matrix Settings @@ -848,8 +831,19 @@ void serializeConfig() { JsonArray hw_led_ins = hw_led.createNestedArray("ins"); for (size_t s = 0; s < BusManager::getNumBusses(); s++) { + DEBUG_PRINTF_P(PSTR("Cfg: Saving bus #%u\n"), s); Bus *bus = BusManager::getBus(s); if (!bus || bus->getLength()==0) break; + DEBUG_PRINTF_P(PSTR(" (%d-%d, type:%d, CO:%d, rev:%d, skip:%d, AW:%d kHz:%d, mA:%d/%d)\n"), + (int)bus->getStart(), (int)(bus->getStart()+bus->getLength()), + (int)(bus->getType() & 0x7F), + (int)bus->getColorOrder(), + (int)bus->isReversed(), + (int)bus->skippedLeds(), + (int)bus->getAutoWhiteMode(), + (int)bus->getFrequency(), + (int)bus->getLEDCurrent(), (int)bus->getMaxCurrent() + ); JsonObject ins = hw_led_ins.createNestedObject(); ins["start"] = bus->getStart(); ins["len"] = bus->getLength(); diff --git a/wled00/const.h b/wled00/const.h index 07873deca1..ff152beb62 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -37,7 +37,7 @@ #endif #ifndef WLED_MAX_USERMODS - #ifdef ESP8266 + #if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32S2) #define WLED_MAX_USERMODS 4 #else #define WLED_MAX_USERMODS 6 @@ -49,31 +49,31 @@ #define WLED_MAX_DIGITAL_CHANNELS 3 #define WLED_MAX_ANALOG_CHANNELS 5 #define WLED_MAX_BUSSES 4 // will allow 3 digital & 1 analog RGB - #define WLED_MIN_VIRTUAL_BUSSES 2 + #define WLED_MIN_VIRTUAL_BUSSES 3 #else #define WLED_MAX_ANALOG_CHANNELS (LEDC_CHANNEL_MAX*LEDC_SPEED_MODE_MAX) #if defined(CONFIG_IDF_TARGET_ESP32C3) // 2 RMT, 6 LEDC, only has 1 I2S but NPB does not support it ATM #define WLED_MAX_BUSSES 6 // will allow 2 digital & 2 analog RGB or 6 PWM white #define WLED_MAX_DIGITAL_CHANNELS 2 //#define WLED_MAX_ANALOG_CHANNELS 6 - #define WLED_MIN_VIRTUAL_BUSSES 3 + #define WLED_MIN_VIRTUAL_BUSSES 4 #elif defined(CONFIG_IDF_TARGET_ESP32S2) // 4 RMT, 8 LEDC, only has 1 I2S bus, supported in NPB // the 5th bus (I2S) will prevent Audioreactive usermod from functioning (it is last used though) - #define WLED_MAX_BUSSES 7 // will allow 5 digital & 2 analog RGB - #define WLED_MAX_DIGITAL_CHANNELS 5 - //#define WLED_MAX_ANALOG_CHANNELS 8 - #define WLED_MIN_VIRTUAL_BUSSES 3 - #elif defined(CONFIG_IDF_TARGET_ESP32S3) // 4 RMT, 8 LEDC, has 2 I2S but NPB does not support them ATM - #define WLED_MAX_BUSSES 6 // will allow 4 digital & 2 analog RGB - #define WLED_MAX_DIGITAL_CHANNELS 4 + #define WLED_MAX_BUSSES 14 // will allow 12 digital & 2 analog RGB + #define WLED_MAX_DIGITAL_CHANNELS 12 // x4 RMT + x1/x8 I2S0 //#define WLED_MAX_ANALOG_CHANNELS 8 #define WLED_MIN_VIRTUAL_BUSSES 4 + #elif defined(CONFIG_IDF_TARGET_ESP32S3) // 4 RMT, 8 LEDC, has 2 I2S but NPB supports parallel x8 LCD on I2S1 + #define WLED_MAX_BUSSES 14 // will allow 12 digital & 2 analog RGB + #define WLED_MAX_DIGITAL_CHANNELS 12 // x4 RMT + x8 I2S-LCD + //#define WLED_MAX_ANALOG_CHANNELS 8 + #define WLED_MIN_VIRTUAL_BUSSES 6 #else // the last digital bus (I2S0) will prevent Audioreactive usermod from functioning - #define WLED_MAX_BUSSES 20 // will allow 17 digital & 3 analog RGB - #define WLED_MAX_DIGITAL_CHANNELS 17 + #define WLED_MAX_BUSSES 19 // will allow 16 digital & 3 analog RGB + #define WLED_MAX_DIGITAL_CHANNELS 16 // x1/x8 I2S1 + x8 RMT //#define WLED_MAX_ANALOG_CHANNELS 16 - #define WLED_MIN_VIRTUAL_BUSSES 4 + #define WLED_MIN_VIRTUAL_BUSSES 6 #endif #endif #else @@ -115,7 +115,7 @@ #endif #endif -#ifdef ESP8266 +#if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32S2) #define WLED_MAX_COLOR_ORDER_MAPPINGS 5 #else #define WLED_MAX_COLOR_ORDER_MAPPINGS 10 @@ -125,7 +125,7 @@ #undef WLED_MAX_LEDMAPS #endif #ifndef WLED_MAX_LEDMAPS - #ifdef ESP8266 + #if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32S2) #define WLED_MAX_LEDMAPS 10 #else #define WLED_MAX_LEDMAPS 16 @@ -473,6 +473,8 @@ #ifndef MAX_LEDS #ifdef ESP8266 #define MAX_LEDS 1664 //can't rely on memory limit to limit this to 1600 LEDs +#elif defined(CONFIG_IDF_TARGET_ESP32S2) +#define MAX_LEDS 2048 //due to memory constraints #else #define MAX_LEDS 8192 #endif @@ -482,7 +484,9 @@ #ifdef ESP8266 #define MAX_LED_MEMORY 4000 #else - #if defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32C3) + #if defined(ARDUINO_ARCH_ESP32S2) + #define MAX_LED_MEMORY 16000 + #elif defined(ARDUINO_ARCH_ESP32C3) #define MAX_LED_MEMORY 32000 #else #define MAX_LED_MEMORY 64000 diff --git a/wled00/data/common.js b/wled00/data/common.js index 9378ef07a8..eddaf6548d 100644 --- a/wled00/data/common.js +++ b/wled00/data/common.js @@ -16,7 +16,7 @@ function isI(n) { return n === +n && n === (n|0); } // isInteger function toggle(el) { gId(el).classList.toggle("hide"); gId('No'+el).classList.toggle("hide"); } function tooltip(cont=null) { d.querySelectorAll((cont?cont+" ":"")+"[title]").forEach((element)=>{ - element.addEventListener("mouseover", ()=>{ + element.addEventListener("pointerover", ()=>{ // save title element.setAttribute("data-title", element.getAttribute("title")); const tooltip = d.createElement("span"); @@ -41,7 +41,7 @@ function tooltip(cont=null) { tooltip.classList.add("visible"); }); - element.addEventListener("mouseout", ()=>{ + element.addEventListener("pointerout", ()=>{ d.querySelectorAll('.tooltip').forEach((tooltip)=>{ tooltip.classList.remove("visible"); d.body.removeChild(tooltip); diff --git a/wled00/data/index.css b/wled00/data/index.css index 0e36ff08c9..3da9d86511 100644 --- a/wled00/data/index.css +++ b/wled00/data/index.css @@ -793,7 +793,7 @@ input[type=range]::-moz-range-thumb { /* buttons */ .btn { padding: 8px; - /*margin: 10px 4px;*/ + margin: 10px 4px; width: 230px; font-size: 19px; color: var(--c-d); diff --git a/wled00/data/index.htm b/wled00/data/index.htm index e74ea00764..e15a547e87 100644 --- a/wled00/data/index.htm +++ b/wled00/data/index.htm @@ -128,7 +128,7 @@
- +

Color palette

diff --git a/wled00/data/index.js b/wled00/data/index.js index 1482c27b55..3560e8763b 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -688,6 +688,8 @@ function parseInfo(i) { // gId("filterVol").classList.add("hide"); hideModes(" ♪"); // hide volume reactive effects // gId("filterFreq").classList.add("hide"); hideModes(" ♫"); // hide frequency reactive effects // } + // Check for version upgrades on page load + checkVersionUpgrade(i); } //https://stackoverflow.com/questions/2592092/executing-script-elements-inserted-with-innerhtml @@ -773,8 +775,8 @@ function populateSegments(s) } let segp = `
`+ - ``+ - `
`+ + ``+ + `
`+ ``+ `
`+ `
`+ @@ -810,7 +812,7 @@ function populateSegments(s) cn += `
`+ ``+ `
`+ `&#x${inst.frz ? (li.live && li.liveseg==i?'e410':'e0e8') : 'e325'};`+ @@ -1659,13 +1661,17 @@ function setEffectParameters(idx) paOnOff[0] = paOnOff[0].substring(0,dPos); } if (paOnOff.length>0 && paOnOff[0] != "!") text = paOnOff[0]; + gId("adPal").classList.remove("hide"); + if (lastinfo.cpalcount>0) gId("rmPal").classList.remove("hide"); } else { // disable palette list text += ' not used'; palw.style.display = "none"; + gId("adPal").classList.add("hide"); + gId("rmPal").classList.add("hide"); // Close palette dialog if not available - if (gId("palw").lastElementChild.tagName == "DIALOG") { - gId("palw").lastElementChild.close(); + if (palw.lastElementChild.tagName == "DIALOG") { + palw.lastElementChild.close(); } } pall.innerHTML = icon + text; @@ -1879,7 +1885,7 @@ function makeSeg() function resetUtil(off=false) { gId('segutil').innerHTML = `
` - + '' + + '' + `
Add segment
` + '
' + `` @@ -2649,28 +2655,28 @@ function fromRgb() var g = gId('sliderG').value; var b = gId('sliderB').value; setPicker(`rgb(${r},${g},${b})`); - let cd = gId('csl').children; // color slots - cd[csel].dataset.r = r; - cd[csel].dataset.g = g; - cd[csel].dataset.b = b; - setCSL(cd[csel]); + let cd = gId('csl').children[csel]; // color slots + cd.dataset.r = r; + cd.dataset.g = g; + cd.dataset.b = b; + setCSL(cd); } function fromW() { let w = gId('sliderW'); - let cd = gId('csl').children; // color slots - cd[csel].dataset.w = w.value; - setCSL(cd[csel]); + let cd = gId('csl').children[csel]; // color slots + cd.dataset.w = w.value; + setCSL(cd); updateTrail(w); } // sr 0: from RGB sliders, 1: from picker, 2: from hex function setColor(sr) { - var cd = gId('csl').children; // color slots - let cdd = cd[csel].dataset; - let w = 0, r,g,b; + var cd = gId('csl').children[csel]; // color slots + let cdd = cd.dataset; + let w = parseInt(cdd.w), r = parseInt(cdd.r), g = parseInt(cdd.g), b = parseInt(cdd.b); if (sr == 1 && isRgbBlack(cdd)) cpick.color.setChannel('hsv', 'v', 100); if (sr != 2 && hasWhite) w = parseInt(gId('sliderW').value); var col = cpick.color.rgb; @@ -2678,7 +2684,7 @@ function setColor(sr) cdd.g = g = hasRGB ? col.g : w; cdd.b = b = hasRGB ? col.b : w; cdd.w = w; - setCSL(cd[csel]); + setCSL(cd); var obj = {"seg": {"col": [[],[],[]]}}; obj.seg.col[csel] = [r, g, b, w]; requestJson(obj); @@ -3114,10 +3120,9 @@ function mergeDeep(target, ...sources) return mergeDeep(target, ...sources); } -function tooltip(cont=null) -{ +function tooltip(cont=null) { d.querySelectorAll((cont?cont+" ":"")+"[title]").forEach((element)=>{ - element.addEventListener("mouseover", ()=>{ + element.addEventListener("pointerover", ()=>{ // save title element.setAttribute("data-title", element.getAttribute("title")); const tooltip = d.createElement("span"); @@ -3142,7 +3147,7 @@ function tooltip(cont=null) tooltip.classList.add("visible"); }); - element.addEventListener("mouseout", ()=>{ + element.addEventListener("pointerout", ()=>{ d.querySelectorAll('.tooltip').forEach((tooltip)=>{ tooltip.classList.remove("visible"); d.body.removeChild(tooltip); @@ -3245,6 +3250,191 @@ function simplifyUI() { gId("btns").style.display = "none"; } +// Version reporting feature +var versionCheckDone = false; + +function checkVersionUpgrade(info) { + // Only check once per page load + if (versionCheckDone) return; + versionCheckDone = true; + + // Suppress feature if in AP mode (no internet connection available) + if (info.wifi && info.wifi.ap) return; + + // Fetch version-info.json using existing /edit endpoint + fetch(getURL('/edit?edit=/version-info.json'), { + method: 'get' + }) + .then(res => { + if (res.status === 404) { + // File doesn't exist - first install, show install prompt + showVersionUpgradePrompt(info, null, info.ver); + return null; + } + if (!res.ok) { + throw new Error('Failed to fetch version-info.json'); + } + return res.json(); + }) + .then(versionInfo => { + if (!versionInfo) return; // 404 case already handled + + // Check if user opted out + if (versionInfo.neverAsk) return; + + // Check if version has changed + const currentVersion = info.ver; + const storedVersion = versionInfo.version || ''; + + if (storedVersion && storedVersion !== currentVersion) { + // Version has changed, show upgrade prompt + showVersionUpgradePrompt(info, storedVersion, currentVersion); + } else if (!storedVersion) { + // Empty version in file, show install prompt + showVersionUpgradePrompt(info, null, currentVersion); + } + }) + .catch(e => { + console.log('Failed to load version-info.json', e); + }); +} + +function showVersionUpgradePrompt(info, oldVersion, newVersion) { + // Determine if this is an install or upgrade + const isInstall = !oldVersion; + + // Create overlay and dialog + const overlay = d.createElement('div'); + overlay.id = 'versionUpgradeOverlay'; + overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);z-index:10000;display:flex;align-items:center;justify-content:center;'; + + const dialog = d.createElement('div'); + dialog.style.cssText = 'background:var(--c-1);border-radius:10px;padding:25px;max-width:500px;margin:20px;box-shadow:0 4px 6px rgba(0,0,0,0.3);'; + + // Build contextual message based on install vs upgrade + const title = isInstall + ? '🎉 Thank you for installing WLED!' + : '🎉 WLED Upgrade Detected!'; + + const description = isInstall + ? `You are now running WLED ${newVersion}.` + : `Your WLED has been upgraded from ${oldVersion} to ${newVersion}.`; + + const question = 'Help make WLED better with a one-time hardware report? It includes only device details like chip type, LED count, etc. — never personal data or your activities.' + + dialog.innerHTML = ` +

${title}

+

${description}

+

${question}

+

+ Learn more about what data is collected and why +

+
+ + + +
+ `; + + overlay.appendChild(dialog); + d.body.appendChild(overlay); + + // Add event listeners + gId('versionReportYes').addEventListener('click', () => { + reportUpgradeEvent(info, oldVersion); + d.body.removeChild(overlay); + }); + + gId('versionReportNo').addEventListener('click', () => { + // Don't update version, will ask again on next load + d.body.removeChild(overlay); + }); + + gId('versionReportNever').addEventListener('click', () => { + updateVersionInfo(newVersion, true); + d.body.removeChild(overlay); + showToast('You will not be asked again.'); + }); +} + +function reportUpgradeEvent(info, oldVersion) { + showToast('Reporting upgrade...'); + + // Fetch fresh data from /json/info endpoint as requested + fetch(getURL('/json/info'), { + method: 'get' + }) + .then(res => res.json()) + .then(infoData => { + // Map to UpgradeEventRequest structure per OpenAPI spec + // Required fields: deviceId, version, previousVersion, releaseName, chip, ledCount, isMatrix, bootloaderSHA256 + const upgradeData = { + deviceId: infoData.deviceId, // Use anonymous unique device ID + version: infoData.ver || '', // Current version string + previousVersion: oldVersion || '', // Previous version from version-info.json + releaseName: infoData.release || '', // Release name (e.g., "WLED 0.15.0") + chip: infoData.arch || '', // Chip architecture (esp32, esp8266, etc) + ledCount: infoData.leds ? infoData.leds.count : 0, // Number of LEDs + isMatrix: !!(infoData.leds && infoData.leds.matrix), // Whether it's a 2D matrix setup + bootloaderSHA256: infoData.bootloaderSHA256 || '', // Bootloader SHA256 hash + brand: infoData.brand, // Device brand (always present) + product: infoData.product, // Product name (always present) + flashSize: infoData.flash // Flash size (always present) + }; + + // Add optional fields if available + if (infoData.psram !== undefined) upgradeData.psramSize = Math.round(infoData.psram / (1024 * 1024)); // convert bytes to MB + // Note: partitionSizes not currently available in /json/info endpoint + + // Make AJAX call to postUpgradeEvent API + return fetch('https://usage.wled.me/api/usage/upgrade', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(upgradeData) + }); + }) + .then(res => { + if (res.ok) { + showToast('Thank you for reporting!'); + updateVersionInfo(info.ver, false); + } else { + showToast('Report failed. Please try again later.', true); + // Do NOT update version info on failure - user will be prompted again + } + }) + .catch(e => { + console.log('Failed to report upgrade', e); + showToast('Report failed. Please try again later.', true); + // Do NOT update version info on error - user will be prompted again + }); +} + +function updateVersionInfo(version, neverAsk) { + const versionInfo = { + version: version, + neverAsk: neverAsk + }; + + // Create a Blob with JSON content and use /upload endpoint + const blob = new Blob([JSON.stringify(versionInfo)], {type: 'application/json'}); + const formData = new FormData(); + formData.append('data', blob, 'version-info.json'); + + fetch(getURL('/upload'), { + method: 'POST', + body: formData + }) + .then(res => res.text()) + .then(data => { + console.log('Version info updated', data); + }) + .catch(e => { + console.log('Failed to update version-info.json', e); + }); +} + size(); _C.style.setProperty('--n', N); diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index baf80a5d79..6066062466 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -6,8 +6,7 @@ LED Settings @@ -771,8 +813,11 @@

LED & Hardware setup

Hardware setup

-
LED outputs:
-
+
+ LED outputs:
+
+ Only last output can be changed. Remove to edit others.
+

LED memory usage: 0 / ? B
@@ -782,6 +827,7 @@

Hardware setup

Use less than 800 LEDs per output for the best experience!

+
Use parallel I2S:
Make a segment for each output:
Custom bus start indices:
Use global LED buffer:
@@ -869,7 +915,10 @@

Advanced


- Target refresh rate: FPS + Target refresh rate: FPS + + +
Config template:

diff --git a/wled00/data/settings_sec.htm b/wled00/data/settings_sec.htm index 7fe0fe62ee..1945762320 100644 --- a/wled00/data/settings_sec.htm +++ b/wled00/data/settings_sec.htm @@ -57,7 +57,7 @@

Security & Update setup

Software Update


Enable ArduinoOTA: -
+

Backup & Restore

⚠ Restoring presets/configuration will OVERWRITE your current presets/configuration.
Incorrect upload or configuration may require a factory reset or re-flashing of your ESP.
@@ -73,7 +73,7 @@

About

A huge thank you to everyone who helped me create WLED!

(c) 2016-2024 Christian Schwinne
Licensed under the EUPL v1.2 license

- Server message: Response error!
+ Installed version: WLED ##VERSION##
diff --git a/wled00/data/update.htm b/wled00/data/update.htm index b68645a527..267f2b63bc 100644 --- a/wled00/data/update.htm +++ b/wled00/data/update.htm @@ -6,7 +6,26 @@