From c7385a7ae9d776e68705522a153c6ca55f1e7080 Mon Sep 17 00:00:00 2001 From: Awawa <69086569+awawa-dev@users.noreply.github.com> Date: Sun, 26 Apr 2026 01:40:49 +0200 Subject: [PATCH] Replace FastLED with Espressif LED SPI driver --- data/gpio.js | 7 +- include/calibration.h | 10 +- include/led_bridge/espressif_bridge.h | 237 ++++++++++++++++ include/led_bridge/fastled_bridge.h | 188 +++++++++++++ include/led_bridge/led_bridge.h | 43 +++ include/led_bridge/neopixelbus_bridge.h | 213 ++++++++++++++ platformio.ini | 26 +- src/calibration.cpp | 6 +- src/leds.cpp | 356 ++---------------------- version | 2 +- 10 files changed, 738 insertions(+), 350 deletions(-) create mode 100644 include/led_bridge/espressif_bridge.h create mode 100644 include/led_bridge/fastled_bridge.h create mode 100644 include/led_bridge/led_bridge.h create mode 100644 include/led_bridge/neopixelbus_bridge.h diff --git a/data/gpio.js b/data/gpio.js index afc2fb4..c430160 100644 --- a/data/gpio.js +++ b/data/gpio.js @@ -44,11 +44,6 @@ function setupPinValidator() { if (isSel) { opts.forEach(p => el.add(new Option(`GPIO ${p}`, p))); el.value = opts.includes(parseInt(old.value)) ? old.value : opts[0]; - - if (opts.length === 1) { - el.disabled = true; - el.title = "Only one valid GPIO for this mode/architecture"; - } } else { el.value = old.value || 0; } @@ -71,7 +66,7 @@ function setupPinValidator() { const clockPinEditor = setField('clockPin', ((autoClk !== null) ? [autoClk] : null)); els.clkLabel.style.display = isSpi ? 'block' : 'none'; - clockPinEditor.disabled = clockPinEditor.disabled || !isSpi; + clockPinEditor.disabled = !isSpi; } els.type.onchange = updateUI; diff --git a/include/calibration.h b/include/calibration.h index 319c219..e990c15 100644 --- a/include/calibration.h +++ b/include/calibration.h @@ -1,13 +1,11 @@ // File: include/calibration.h #pragma once -#ifdef USE_FASTLED - struct RgbwColor { - uint8_t R, G, B, W; - }; -#endif +struct ColorRgbw { + uint8_t R, G, B, W; +}; void setParamsAndPrepareCalibration(uint8_t _gain, uint8_t _red, uint8_t _green, uint8_t _blue); void deleteCalibration(); -RgbwColor rgb2rgbw(uint8_t r, uint8_t g, uint8_t b); +ColorRgbw rgb2rgbw(uint8_t r, uint8_t g, uint8_t b); diff --git a/include/led_bridge/espressif_bridge.h b/include/led_bridge/espressif_bridge.h new file mode 100644 index 0000000..8d85373 --- /dev/null +++ b/include/led_bridge/espressif_bridge.h @@ -0,0 +1,237 @@ +/* espressif_bridge.h +* +* MIT License +* +* Copyright (c) 2026 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/Hyperk +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#pragma once + +#include "led_strip.h" +#include "driver/spi_master.h" +#include "led_bridge.h" + +struct espressif_bridge : public led_bridge +{ + led_strip_handle_t _handle = nullptr; + uint16_t _totalLedsNumber = 0; + LedType _ledsType = LedType::WS2812; + + const spi_host_device_t SPI_HOST = SPI2_HOST; + spi_device_handle_t _spi_handle = nullptr; + size_t _spi_buffer_size = 0; + uint8_t* _spi_led_buffer = nullptr; + + int getLedsNumber() override + { + return _totalLedsNumber; + } + + void clearAll() override + { + if (_handle != nullptr) + { + led_strip_clear(_handle); + } + else if (_spi_handle) + { + for (int i = 0; i < _totalLedsNumber; i++) { + setLedRgb(i, 0 ,0, 0); + } + executeRenderLed(true); + } + } + + bool canRender() override + { + return true; + } + + bool executeRenderLed(bool isNewFrame) override + { + if (_handle != nullptr) + { + led_strip_refresh(_handle); + } + else if (_spi_handle) + { + spi_transaction_t t; + memset(&t, 0, sizeof(t)); + + t.length = _spi_buffer_size * 8; + t.tx_buffer = _spi_led_buffer; + + spi_device_transmit(_spi_handle, &t); + } + return true; + } + + void releaseDriverResources() override + { + delay(50); + + if (_handle != nullptr) + { + led_strip_del(_handle); + _handle = nullptr; + } + + if (_spi_handle) { + spi_bus_remove_device(_spi_handle); + spi_bus_free(SPI_HOST); + } + + if (_spi_led_buffer) { + heap_caps_free(_spi_led_buffer); + _spi_led_buffer = nullptr; + _spi_buffer_size = 0; + } + + delay(50); + } + + void initializeLedDriver(LedType cfgLedType, uint16_t cfgLedNumLeds, uint8_t cfgLedDataPin, uint8_t cfgLedClockPin, + uint8_t calGain, uint8_t calRed, uint8_t calGreen, uint8_t calBlue) override + { + _totalLedsNumber = cfgLedNumLeds; + _ledsType = cfgLedType; + + if (_ledsType == LedType::SK6812) + { + setParamsAndPrepareCalibration(calGain, calRed, calGreen, calBlue); + } + + if (_ledsType == LedType::WS2812 || _ledsType == LedType::SK6812) + { + led_strip_config_t strip_config = { + .strip_gpio_num = cfgLedDataPin, + .max_leds = cfgLedNumLeds, + .led_model = (_ledsType == LedType::SK6812) ? LED_MODEL_SK6812 : LED_MODEL_WS2812, + .color_component_format = (_ledsType == LedType::SK6812) ? LED_STRIP_COLOR_COMPONENT_FMT_GRBW : LED_STRIP_COLOR_COMPONENT_FMT_GRB, + .flags = { + .invert_out = false, + } + }; + + led_strip_spi_config_t spi_config = { + .clk_src = SPI_CLK_SRC_DEFAULT, + .spi_bus = SPI2_HOST, + .flags = { + .with_dma = true, + } + }; + + led_strip_new_spi_device(&strip_config, &spi_config, &_handle); + } + else + { // SPI (APA102 / SK9822) + _spi_buffer_size = 4 + (cfgLedNumLeds * 4) + ((cfgLedNumLeds / 16) + 1); + + spi_bus_config_t buscfg = { + .mosi_io_num = cfgLedDataPin, + .miso_io_num = -1, + .sclk_io_num = cfgLedClockPin, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .max_transfer_sz = static_cast(_spi_buffer_size) + }; + + spi_device_interface_config_t devcfg = { + .mode = 0, + .clock_speed_hz = 10 * 1000 * 1000, + .spics_io_num = -1, + .queue_size = 7, + }; + + if (spi_bus_initialize(SPI_HOST, &buscfg, SPI_DMA_CH_AUTO) == ESP_OK) { + if (spi_bus_add_device(SPI_HOST, &devcfg, &_spi_handle) == ESP_OK) { + + _spi_led_buffer = (uint8_t*)heap_caps_malloc(_spi_buffer_size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA); + if (_spi_led_buffer == nullptr) { + spi_bus_remove_device(_spi_handle); + _spi_handle = nullptr; + spi_bus_free(SPI_HOST); + } else { + memset(_spi_led_buffer, 0, _spi_buffer_size); + for (size_t i = 4 + (cfgLedNumLeds * 4); i < _spi_buffer_size; i++) { + _spi_led_buffer[i] = 0xFF; + } + } + } + } + + if (_spi_handle == nullptr) { + _spi_buffer_size = 0; + } + } + } + + inline void setLedRgb(int index, uint8_t r, uint8_t g, uint8_t b) override + { + if (_ledsType == LedType::SK6812) + { + if (index >= _totalLedsNumber || _handle == nullptr) return; + const ColorRgbw calibrated = rgb2rgbw(r, g, b); + led_strip_set_pixel_rgbw(_handle, index, calibrated.R, calibrated.G, calibrated.B, calibrated.W); + } + else if (_ledsType == LedType::WS2812) + { + if (index >= _totalLedsNumber || _handle == nullptr) return; + led_strip_set_pixel(_handle, index, r, g, b); + } + else if (_ledsType == LedType::APA102) + { + if (index >= _totalLedsNumber || _spi_led_buffer == nullptr) return; + + int offset = 4 + (index * 4); + _spi_led_buffer[offset] = 0xFF; + _spi_led_buffer[offset + 1] = b; + _spi_led_buffer[offset + 2] = g; + _spi_led_buffer[offset + 3] = r; + } + } + + inline void setLedRgbw(int index, uint8_t r, uint8_t g, uint8_t b, uint8_t w) override + { + if (_ledsType == LedType::SK6812) + { + if (index >= _totalLedsNumber || _handle == nullptr) return; + led_strip_set_pixel_rgbw(_handle, index, r, g, b, w); + } + else if (_ledsType == LedType::WS2812) + { + if (index >= _totalLedsNumber || _handle == nullptr) return; + led_strip_set_pixel(_handle, index, r, g, b); + } + else if (_ledsType == LedType::APA102) + { + if (index >= _totalLedsNumber || _spi_led_buffer == nullptr) return; + + int offset = 4 + (index * 4); + _spi_led_buffer[offset] = 0xFF; + _spi_led_buffer[offset + 1] = b; + _spi_led_buffer[offset + 2] = g; + _spi_led_buffer[offset + 3] = r; + } + } +}; \ No newline at end of file diff --git a/include/led_bridge/fastled_bridge.h b/include/led_bridge/fastled_bridge.h new file mode 100644 index 0000000..71591df --- /dev/null +++ b/include/led_bridge/fastled_bridge.h @@ -0,0 +1,188 @@ +/* fastled_bridge.h +* +* MIT License +* +* Copyright (c) 2026 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/Hyperk +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#pragma once + +#include +#include "led_bridge.h" + +struct fastled_bridge : public led_bridge +{ + CRGB* leds = nullptr; + uint16_t fastLedsNumber = 0; + LedType fastLedsType = LedType::WS2812; + + int getLedsNumber() override + { + return fastLedsNumber; + } + + void clearAll() override + { + FastLED.clear(true); + } + + bool canRender() override + { + return true; + } + + bool executeRenderLed(bool isNewFrame) override + { + FastLED.show(); + return true; + } + + void releaseDriverResources() override + { + if (leds != nullptr) + { + delete[] leds; + leds = nullptr; + } + } + + void initializeLedDriver(LedType cfgLedType, uint16_t cfgLedNumLeds, uint8_t cfgLedDataPin, uint8_t cfgLedClockPin, + uint8_t calGain, uint8_t calRed, uint8_t calGreen, uint8_t calBlue) override + { + fastLedsNumber = cfgLedNumLeds; + fastLedsType = cfgLedType; + int virtualLedsNumber = fastLedsNumber; + + if (fastLedsType == LedType::SK6812) + { + setParamsAndPrepareCalibration(calGain, calRed, calGreen, calBlue); + virtualLedsNumber = (fastLedsNumber * 4 + 2) / 3; + leds = new CRGB[virtualLedsNumber]; + } + else + { + leds = new CRGB[virtualLedsNumber]; + } + + if (fastLedsType == LedType::WS2812 || fastLedsType == LedType::SK6812) + { + switch (cfgLedDataPin) { + #if !defined(CONFIG_IDF_TARGET_ESP32S3) + case 0: FastLED.addLeds(leds, virtualLedsNumber); break; + #endif + case 1: FastLED.addLeds(leds, virtualLedsNumber); break; + case 2: FastLED.addLeds(leds, virtualLedsNumber); break; + #if !defined(CONFIG_IDF_TARGET_ESP32S3) + case 3: FastLED.addLeds(leds, virtualLedsNumber); break; + #endif + case 4: FastLED.addLeds(leds, virtualLedsNumber); break; + case 5: FastLED.addLeds(leds, virtualLedsNumber); break; + case 6: FastLED.addLeds(leds, virtualLedsNumber); break; + case 7: FastLED.addLeds(leds, virtualLedsNumber); break; + #if !defined(CONFIG_IDF_TARGET_ESP32C2) + case 8: FastLED.addLeds(leds, virtualLedsNumber); break; + #endif + case 10: FastLED.addLeds(leds, virtualLedsNumber); break; + #if defined(CONFIG_IDF_TARGET_ESP32C6) + case 15: FastLED.addLeds(leds, virtualLedsNumber); break; + case 18: FastLED.addLeds(leds, virtualLedsNumber); break; + case 19: FastLED.addLeds(leds, virtualLedsNumber); break; + case 20: FastLED.addLeds(leds, virtualLedsNumber); break; + case 21: FastLED.addLeds(leds, virtualLedsNumber); break; + case 22: FastLED.addLeds(leds, virtualLedsNumber); break; + #elif defined(CONFIG_IDF_TARGET_ESP32S3) + case 16: FastLED.addLeds(leds, virtualLedsNumber); break; + case 17: FastLED.addLeds(leds, virtualLedsNumber); break; + case 18: FastLED.addLeds(leds, virtualLedsNumber); break; + case 48: FastLED.addLeds(leds, virtualLedsNumber); break; + #elif defined(CONFIG_IDF_TARGET_ESP32C3) + case 20: FastLED.addLeds(leds, virtualLedsNumber); break; + case 21: FastLED.addLeds(leds, virtualLedsNumber); break; + #elif defined(CONFIG_IDF_TARGET_ESP32C5) + case 11: FastLED.addLeds(leds, virtualLedsNumber); break; + case 27: FastLED.addLeds(leds, virtualLedsNumber); break; + #endif + default: + FastLED.addLeds(leds, virtualLedsNumber); + break; + } + } + else + { // SPI (APA102 / SK9822) + switch (cfgLedDataPin) { + #if defined(CONFIG_IDF_TARGET_ESP32S3) + case 5: FastLED.addLeds(leds, virtualLedsNumber); break; + #elif defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) + case 7: FastLED.addLeds(leds, virtualLedsNumber); break; + #elif defined(CONFIG_IDF_TARGET_ESP32C6) + case 5: FastLED.addLeds(leds, virtualLedsNumber); break; + #endif + + default: + Log::debug("!!! FATAL ERROR: Invalid LED Data Pin. Must use Hardware SPI pins. !!!"); + FastLED.addLeds(leds, virtualLedsNumber); + } + } + + FastLED.setBrightness(255); + } + + inline void setLedRgb(int index, uint8_t r, uint8_t g, uint8_t b) override + { + if (fastLedsType == LedType::SK6812) + { + if (index >= fastLedsNumber) return; + const ColorRgbw calibrated = rgb2rgbw(r, g, b); + uint16_t i = index * 4; + auto raw = reinterpret_cast(leds); + raw[i] = calibrated.G; + raw[i + 1] = calibrated.R; + raw[i + 2] = calibrated.B; + raw[i + 3] = calibrated.W; + } + else + { + if (index >= fastLedsNumber) return; + leds[index] = CRGB(g, r, b); + } + } + + inline void setLedRgbw(int index, uint8_t r, uint8_t g, uint8_t b, uint8_t w) override + { + if (fastLedsType == LedType::SK6812) + { + if (index >= fastLedsNumber) return; + uint16_t i = index * 4; + auto raw = reinterpret_cast(leds); + raw[i] = g; + raw[i + 1] = r; + raw[i + 2] = b; + raw[i + 3] = w; + } + else + { + if (index >= fastLedsNumber) return; + leds[index] = CRGB(g, r, b); + } + } +}; \ No newline at end of file diff --git a/include/led_bridge/led_bridge.h b/include/led_bridge/led_bridge.h new file mode 100644 index 0000000..0f66586 --- /dev/null +++ b/include/led_bridge/led_bridge.h @@ -0,0 +1,43 @@ +/* led_bridge.h +* +* MIT License +* +* Copyright (c) 2026 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/Hyperk +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#pragma once +#include "config.h" + +struct led_bridge +{ + virtual int getLedsNumber() = 0; + virtual void clearAll() = 0; + virtual bool canRender() = 0; + virtual bool executeRenderLed(bool isNewFrame) = 0; + virtual void releaseDriverResources() = 0; + virtual void initializeLedDriver(LedType cfgLedType, uint16_t cfgLedNumLeds, uint8_t cfgLedDataPin, uint8_t cfgLedClockPin, + uint8_t calGain, uint8_t calRed, uint8_t calGreen, uint8_t calBlue) = 0; + + virtual inline void setLedRgb(int index, uint8_t r, uint8_t g, uint8_t b) = 0; + virtual inline void setLedRgbw(int index, uint8_t r, uint8_t g, uint8_t b, uint8_t w) = 0; +}; \ No newline at end of file diff --git a/include/led_bridge/neopixelbus_bridge.h b/include/led_bridge/neopixelbus_bridge.h new file mode 100644 index 0000000..4551571 --- /dev/null +++ b/include/led_bridge/neopixelbus_bridge.h @@ -0,0 +1,213 @@ +/* neopixelbus_bridge.h +* +* MIT License +* +* Copyright (c) 2026 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/Hyperk +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#pragma once +#include +#include "led_bridge.h" + +#if defined(CONFIG_IDF_TARGET_ESP32C6) + typedef NeoPixelBus DotStar; + typedef NeoPixelBus NeoPixel; + typedef NeoPixelBus NeoPixelRgbw; +#elif defined(CONFIG_IDF_TARGET_ESP32C3) + typedef NeoPixelBus DotStar; + typedef NeoPixelBus NeoPixel; + typedef NeoPixelBus NeoPixelRgbw; +#elif defined(CONFIG_IDF_TARGET_ESP32S3) + typedef NeoPixelBus DotStar; + typedef NeoPixelBus NeoPixel; + typedef NeoPixelBus NeoPixelRgbw; +#elif defined(CONFIG_IDF_TARGET_ESP32S2) + typedef NeoPixelBus DotStar; + typedef NeoPixelBus NeoPixel; + typedef NeoPixelBus NeoPixelRgbw; +#elif defined(ARDUINO_ARCH_ESP32) + #if defined(ETH_PHY_TYPE) && (ETH_PHY_TYPE == ETH_PHY_LAN8720) + typedef NeoPixelBus DotStar; + #else + typedef NeoPixelBus DotStar; + #endif + typedef NeoPixelBus NeoPixel; + typedef NeoPixelBus NeoPixelRgbw; +#elif defined(ARDUINO_ARCH_ESP8266) + typedef NeoPixelBus DotStar; + typedef NeoPixelBus NeoPixel; + typedef NeoPixelBus NeoPixelRgbw; +#else // Raspberry Pi Pico + typedef NeoPixelBus DotStar; + typedef NeoPixelBus NeoPixel; + typedef NeoPixelBus NeoPixelRgbw; +#endif + +struct neopixelbus_bridge : public led_bridge +{ + DotStar* dotstar = nullptr; + NeoPixel* neopixel = nullptr; + NeoPixelRgbw* neopixelRgbw = nullptr; + + int getLedsNumber() override + { + if (dotstar != nullptr) + return dotstar->PixelCount(); + else if (neopixel != nullptr) + return neopixel->PixelCount(); + else if (neopixelRgbw != nullptr) + return neopixelRgbw->PixelCount(); + + return 0; + } + + void clearAll() override + { + if (dotstar == nullptr && neopixel == nullptr && neopixelRgbw == nullptr) + return; + + if (dotstar != nullptr) + {dotstar->ClearTo(RgbColor(0, 0, 0)); dotstar->Show();} + else if (neopixel != nullptr) + {neopixel->ClearTo(RgbColor(0, 0, 0)); neopixel->Show();} + else if (neopixelRgbw != nullptr) + {neopixelRgbw->ClearTo(RgbwColor(0, 0, 0, 0)); neopixelRgbw->Show();} + } + + bool canRender() override + { + if (dotstar != nullptr) + { + return dotstar->CanShow(); + } + else if (neopixel != nullptr) + { + return neopixel->CanShow(); + } + else if (neopixelRgbw != nullptr) + { + return neopixelRgbw->CanShow(); + } + return true; + } + + bool executeRenderLed(bool isNewFrame) override + { + if (dotstar != nullptr) + { + if (!dotstar->CanShow()) + { + return false; + } + dotstar->Show(); + } + else if (neopixel != nullptr) + { + if (!neopixel->CanShow()) + { + return false; + } + neopixel->Show(); + } + else if (neopixelRgbw != nullptr) + { + if (!neopixelRgbw->CanShow()) + { + return false; + } + neopixelRgbw->Show(); + } + return true; + } + + void releaseDriverResources() override + { + delay(50); + if (dotstar != nullptr) {delete dotstar; dotstar = nullptr;} + if (neopixel != nullptr) {delete neopixel; neopixel = nullptr;} + if (neopixelRgbw != nullptr) {delete neopixelRgbw; neopixelRgbw = nullptr;} + delay(100); + } + + void initializeLedDriver(LedType cfgLedType, uint16_t cfgLedNumLeds, uint8_t cfgLedDataPin, uint8_t cfgLedClockPin, + uint8_t calGain, uint8_t calRed, uint8_t calGreen, uint8_t calBlue) override + { + if (cfgLedType == LedType::WS2812 || cfgLedType == LedType::SK6812) + { // clockless + switch (cfgLedType) + { + case LedType::SK6812: + setParamsAndPrepareCalibration(calGain, calRed, calGreen, calBlue); + neopixelRgbw = new NeoPixelRgbw(cfgLedNumLeds, cfgLedDataPin); + neopixelRgbw->Begin(); + break; + default: + neopixel = new NeoPixel(cfgLedNumLeds, cfgLedDataPin); + neopixel->Begin(); + } + } + else + { // SPI (APA102 / SK9822) + dotstar = new DotStar(cfgLedNumLeds, cfgLedClockPin, cfgLedDataPin); + dotstar->Begin(); + } + } + + inline void setLedRgb(int index, uint8_t r, uint8_t g, uint8_t b) override + { + if (dotstar != nullptr) + { + RgbColor col(r, g, b); + dotstar->SetPixelColor(index, col); + } + else if (neopixel != nullptr) + { + RgbColor col(r, g, b); + neopixel->SetPixelColor(index, col); + } + else if (neopixelRgbw != nullptr) + { + ColorRgbw col = rgb2rgbw(r, g, b); + neopixelRgbw->SetPixelColor(index, RgbwColor(col.R, col.G, col.B, col.W)); + } + } + + inline void setLedRgbw(int index, uint8_t r, uint8_t g, uint8_t b, uint8_t w) override + { + if (dotstar != nullptr) + { + RgbColor col(r, g, b); + dotstar->SetPixelColor(index, col); + } + else if (neopixel != nullptr) + { + RgbColor col(r, g, b); + neopixel->SetPixelColor(index, col); + } + else if (neopixelRgbw != nullptr) + { + RgbwColor col(r, g, b, w); + neopixelRgbw->SetPixelColor(index, col); + } + } +}; \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 7ed5b00..ed4d528 100644 --- a/platformio.ini +++ b/platformio.ini @@ -39,6 +39,7 @@ lib_deps = build_flags = ${env.build_flags} -DMAX_LEDS=${env.custom_max_leds} + -DUSE_NEOPIXELBUS -std=gnu++17 build_unflags = @@ -61,12 +62,13 @@ build_flags = extends = env:esp32 board = esp32-s2-saola-1 -; ================================================== -; ESP32-S3, C3, C6 (Core 3.x, FastLED) -; ================================================== +; ========================================================= +; ESP32-S3, C2, C3, C5, C6 (Core 3.x, Espressif LED driver) +; ========================================================= [env:esp32c3] -platform = https://github.com/pioarduino/platform-espressif32.git#55.03.37 +platform = https://github.com/pioarduino/platform-espressif32.git#55.03.38-1 + board = esp32-c3-devkitm-1 board_build.partitions = partitions_4mb.csv board_build.filesystem = littlefs @@ -75,7 +77,8 @@ framework = arduino lib_deps = ${env.lib_deps} - fastled/FastLED @ 3.10.3 + ;fastled/FastLED @ 3.10.3 + led_strip=https://components.espressif.com/api/downloads/?object_type=component&object_id=6628801c-aa0f-4af3-9a34-c8edcc5d2aa3 extra_scripts = ${env.extra_scripts} @@ -83,8 +86,12 @@ extra_scripts = build_flags = ${env.build_flags} -DMAX_LEDS=${env.custom_max_leds} - -DUSE_FASTLED - -DFASTLED_RMT_MAX_CHANNELS=1 + ;-DUSE_FASTLED + ;-DFASTLED_RMT_MAX_CHANNELS=1 + -DUSE_ESPRESSIF_LED_STRIP + -I.pio/libdeps/$PIOENV/led_strip/include + -I.pio/libdeps/$PIOENV/led_strip/src + -I.pio/libdeps/$PIOENV/led_strip/interface ;-DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MODE=1 [env:esp32s3] @@ -98,6 +105,9 @@ board = esp32-c6-devkitc-1 [env:esp32c2] extends = env:esp32c3 board = esp32-c2-devkitm-1 +build_flags = + ${env:esp32c3.build_flags} + -DRMT_CLK_SRC_DEFAULT=0 [env:esp32c5] extends = env:esp32c3 @@ -120,6 +130,7 @@ lib_deps = build_flags = ${env.build_flags} -DARDUINO_ARCH_ESP8266 + -DUSE_NEOPIXELBUS -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY -DMAX_LEDS=1200 @@ -140,6 +151,7 @@ lib_deps = build_flags = ${env.build_flags} -DPICO_STACK_SIZE=8192 + -DUSE_NEOPIXELBUS -DMAX_LEDS=${env.custom_max_leds} ; ================================================== diff --git a/src/calibration.cpp b/src/calibration.cpp index 75bf3bd..7564ae8 100644 --- a/src/calibration.cpp +++ b/src/calibration.cpp @@ -25,7 +25,7 @@ * SOFTWARE. */ -#if !defined(USE_FASTLED) +#if defined(USE_NEOPIXELBUS) #include #endif @@ -131,9 +131,9 @@ void setParamsAndPrepareCalibration(uint8_t _gain, uint8_t _red, uint8_t _green, } } -RgbwColor rgb2rgbw(uint8_t r, uint8_t g, uint8_t b) +ColorRgbw rgb2rgbw(uint8_t r, uint8_t g, uint8_t b) { - RgbwColor color; + ColorRgbw color; color.W = min(channelCorrection.red[r], min(channelCorrection.green[g], diff --git a/src/leds.cpp b/src/leds.cpp index 4eced3e..2828271 100644 --- a/src/leds.cpp +++ b/src/leds.cpp @@ -25,61 +25,6 @@ * SOFTWARE. */ -#ifdef USE_FASTLED - #include - #include "config.h" - - namespace Leds - { - CRGB* leds = nullptr; - uint16_t fastLedsNumber = 0; - LedType fastLedsType = LedType::WS2812; - } -#else - #include - - #if defined(CONFIG_IDF_TARGET_ESP32C6) - typedef NeoPixelBus DotStar; - typedef NeoPixelBus NeoPixel; - typedef NeoPixelBus NeoPixelRgbw; - #elif defined(CONFIG_IDF_TARGET_ESP32C3) - typedef NeoPixelBus DotStar; - typedef NeoPixelBus NeoPixel; - typedef NeoPixelBus NeoPixelRgbw; - #elif defined(CONFIG_IDF_TARGET_ESP32S3) - typedef NeoPixelBus DotStar; - typedef NeoPixelBus NeoPixel; - typedef NeoPixelBus NeoPixelRgbw; - #elif defined(CONFIG_IDF_TARGET_ESP32S2) - typedef NeoPixelBus DotStar; - typedef NeoPixelBus NeoPixel; - typedef NeoPixelBus NeoPixelRgbw; - #elif defined(ARDUINO_ARCH_ESP32) - #if defined(ETH_PHY_TYPE) && (ETH_PHY_TYPE == ETH_PHY_LAN8720) - typedef NeoPixelBus DotStar; - #else - typedef NeoPixelBus DotStar; - #endif - typedef NeoPixelBus NeoPixel; - typedef NeoPixelBus NeoPixelRgbw; - #elif defined(ARDUINO_ARCH_ESP8266) - typedef NeoPixelBus DotStar; - typedef NeoPixelBus NeoPixel; - typedef NeoPixelBus NeoPixelRgbw; - #else // Raspberry Pi Pico - typedef NeoPixelBus DotStar; - typedef NeoPixelBus NeoPixel; - typedef NeoPixelBus NeoPixelRgbw; - #endif - - namespace Leds - { - DotStar* dotstar = nullptr; - NeoPixel* neopixel = nullptr; - NeoPixelRgbw* neopixelRgbw = nullptr; - } -#endif - #include "leds.h" #include "config.h" #include "storage.h" @@ -89,66 +34,30 @@ ////////////////////////////////////////////////////////////////////////////////////////////////// +#ifdef USE_FASTLED + #include "led_bridge/fastled_bridge.h" + namespace Leds{ fastled_bridge renderer; } +#elif defined(USE_ESPRESSIF_LED_STRIP) + #include "led_bridge/espressif_bridge.h" + namespace Leds{ espressif_bridge renderer; } +#else + #include "led_bridge/neopixelbus_bridge.h" + namespace Leds{ neopixelbus_bridge renderer; } +#endif + namespace Leds{ bool ledDriverInitialized = false; volatile bool delayedRender = false; - uint16_t briPlus = 256; + uint16_t briPlus = 256; int getLedsNumber() { - #ifdef USE_FASTLED - return fastLedsNumber; - #else - if (dotstar != nullptr) - return dotstar->PixelCount(); - else if (neopixel != nullptr) - return neopixel->PixelCount(); - else if (neopixelRgbw != nullptr) - return neopixelRgbw->PixelCount(); - - return 0; - #endif - } - - void clearAll() - { - #ifdef USE_FASTLED - FastLED.clear(true); - #else - if (dotstar == nullptr && neopixel == nullptr && neopixelRgbw == nullptr) - return; - - if (dotstar != nullptr) - {dotstar->ClearTo(RgbColor(0, 0, 0)); dotstar->Show();} - else if (neopixel != nullptr) - {neopixel->ClearTo(RgbColor(0, 0, 0)); neopixel->Show();} - else if (neopixelRgbw != nullptr) - {neopixelRgbw->ClearTo(RgbwColor(0, 0, 0, 0)); neopixelRgbw->Show();} - #endif - } - - bool canRender() - { - #ifndef USE_FASTLED - if (dotstar != nullptr) - { - return dotstar->CanShow(); - } - else if (neopixel != nullptr) - { - return neopixel->CanShow(); - } - else if (neopixelRgbw != nullptr) - { - return neopixelRgbw->CanShow(); - } - #endif - return true; + return renderer.getLedsNumber(); } void synchronizeLedsToVolatileStateBeforeDelayedRender() { - if (delayedRender || !canRender()) + if (delayedRender || !renderer.canRender()) return; bool updated = false; @@ -190,7 +99,7 @@ namespace Leds{ void initLEDs(LedType cfgLedType, uint16_t cfgLedNumLeds, uint8_t cfgLedDataPin, uint8_t cfgLedClockPin, uint8_t calGain, uint8_t calRed, uint8_t calGreen, uint8_t calBlue) { - clearAll(); + renderer.clearAll(); #ifndef LEDS_NOT_REQUIRE_RESTART if (ledDriverInitialized) @@ -202,22 +111,7 @@ namespace Leds{ } #endif - #ifdef USE_FASTLED - if (leds != nullptr) - { - delete[] leds; - leds = nullptr; - } - #else - if (dotstar != nullptr || neopixel != nullptr || neopixelRgbw != nullptr) - { - delay(50); - delete dotstar; dotstar = nullptr; - delete neopixel; neopixel = nullptr; - delete neopixelRgbw; neopixelRgbw = nullptr; - delay(100); - } - #endif + renderer.releaseDriverResources(); if (cfgLedType != LedType::SK6812) { @@ -227,107 +121,9 @@ namespace Leds{ delayedRender = false; // LED controller setup - #ifdef USE_FASTLED - fastLedsNumber = cfgLedNumLeds; - fastLedsType = cfgLedType; - int virtualLedsNumber = fastLedsNumber; - - if (fastLedsType == LedType::SK6812) - { - setParamsAndPrepareCalibration(calGain, calRed, calGreen, calBlue); - virtualLedsNumber = (fastLedsNumber * 4 + 2) / 3; - leds = new CRGB[virtualLedsNumber]; - } - else - { - leds = new CRGB[virtualLedsNumber]; - } - - if (fastLedsType == LedType::WS2812 || fastLedsType == LedType::SK6812) - { - switch (cfgLedDataPin) { - #if !defined(CONFIG_IDF_TARGET_ESP32S3) - case 0: FastLED.addLeds(leds, virtualLedsNumber); break; - #endif - case 1: FastLED.addLeds(leds, virtualLedsNumber); break; - case 2: FastLED.addLeds(leds, virtualLedsNumber); break; - #if !defined(CONFIG_IDF_TARGET_ESP32S3) - case 3: FastLED.addLeds(leds, virtualLedsNumber); break; - #endif - case 4: FastLED.addLeds(leds, virtualLedsNumber); break; - case 5: FastLED.addLeds(leds, virtualLedsNumber); break; - case 6: FastLED.addLeds(leds, virtualLedsNumber); break; - case 7: FastLED.addLeds(leds, virtualLedsNumber); break; - #if !defined(CONFIG_IDF_TARGET_ESP32C2) - case 8: FastLED.addLeds(leds, virtualLedsNumber); break; - #endif - case 10: FastLED.addLeds(leds, virtualLedsNumber); break; - #if defined(CONFIG_IDF_TARGET_ESP32C6) - case 15: FastLED.addLeds(leds, virtualLedsNumber); break; - case 18: FastLED.addLeds(leds, virtualLedsNumber); break; - case 19: FastLED.addLeds(leds, virtualLedsNumber); break; - case 20: FastLED.addLeds(leds, virtualLedsNumber); break; - case 21: FastLED.addLeds(leds, virtualLedsNumber); break; - case 22: FastLED.addLeds(leds, virtualLedsNumber); break; - #elif defined(CONFIG_IDF_TARGET_ESP32S3) - case 16: FastLED.addLeds(leds, virtualLedsNumber); break; - case 17: FastLED.addLeds(leds, virtualLedsNumber); break; - case 18: FastLED.addLeds(leds, virtualLedsNumber); break; - case 48: FastLED.addLeds(leds, virtualLedsNumber); break; - #elif defined(CONFIG_IDF_TARGET_ESP32C3) - case 20: FastLED.addLeds(leds, virtualLedsNumber); break; - case 21: FastLED.addLeds(leds, virtualLedsNumber); break; - #elif defined(CONFIG_IDF_TARGET_ESP32C5) - case 11: FastLED.addLeds(leds, virtualLedsNumber); break; - case 27: FastLED.addLeds(leds, virtualLedsNumber); break; - #endif - default: - FastLED.addLeds(leds, virtualLedsNumber); - break; - } - } - else - { // SPI (APA102 / SK9822) - switch (cfgLedDataPin) { - #if defined(CONFIG_IDF_TARGET_ESP32S3) - case 5: FastLED.addLeds(leds, virtualLedsNumber); break; - #elif defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) - case 7: FastLED.addLeds(leds, virtualLedsNumber); break; - #elif defined(CONFIG_IDF_TARGET_ESP32C6) - case 5: FastLED.addLeds(leds, virtualLedsNumber); break; - #endif - - default: - Log::debug("!!! FATAL ERROR: Invalid LED Data Pin. Must use Hardware SPI pins. !!!"); - FastLED.addLeds(leds, virtualLedsNumber); - } - } + renderer.initializeLedDriver(cfgLedType, cfgLedNumLeds, cfgLedDataPin, cfgLedClockPin, calGain, calRed, calGreen, calBlue); - FastLED.setBrightness(255); - - #else - if (cfgLedType == LedType::WS2812 || cfgLedType == LedType::SK6812) - { // clockless - switch (cfgLedType) - { - case LedType::SK6812: - setParamsAndPrepareCalibration(calGain, calRed, calGreen, calBlue); - neopixelRgbw = new NeoPixelRgbw(cfgLedNumLeds, cfgLedDataPin); - neopixelRgbw->Begin(); - break; - default: - neopixel = new NeoPixel(cfgLedNumLeds, cfgLedDataPin); - neopixel->Begin(); - } - } - else - { // SPI (APA102 / SK9822) - dotstar = new DotStar(cfgLedNumLeds, cfgLedClockPin, cfgLedDataPin); - dotstar->Begin(); - } - #endif - - clearAll(); + renderer.clearAll(); ledDriverInitialized = true; } @@ -354,41 +150,8 @@ namespace Leds{ g = scaleBri(g); b = scaleBri(b); } - #ifdef USE_FASTLED - if (fastLedsType == LedType::SK6812) - { - if (index >= fastLedsNumber) return; - const RgbwColor calibrated = rgb2rgbw(r, g, b); - uint16_t i = index * 4; - auto raw = reinterpret_cast(leds); - raw[i] = calibrated.G; - raw[i + 1] = calibrated.R; - raw[i + 2] = calibrated.B; - raw[i + 3] = calibrated.W; - } - else - { - if (index >= fastLedsNumber) return; - leds[index] = CRGB(g, r, b); - } - #else - if (dotstar != nullptr) - { - RgbColor col(r, g, b); - dotstar->SetPixelColor(index, col); - } - else if (neopixel != nullptr) - { - RgbColor col(r, g, b); - neopixel->SetPixelColor(index, col); - } - else if (neopixelRgbw != nullptr) - { - RgbwColor col = rgb2rgbw(r, g, b); - neopixelRgbw->SetPixelColor(index, col); - } - #endif + renderer.setLedRgb(index, r, g, b); } template @@ -401,41 +164,7 @@ namespace Leds{ w = scaleBri(w); } - #ifdef USE_FASTLED - - - if (fastLedsType == LedType::SK6812) - { - if (index >= fastLedsNumber) return; - uint16_t i = index * 4; - auto raw = reinterpret_cast(leds); - raw[i] = g; - raw[i + 1] = r; - raw[i + 2] = b; - raw[i + 3] = w; - } - else - { - if (index >= fastLedsNumber) return; - leds[index] = CRGB(g, r, b); - } - #else - if (dotstar != nullptr) - { - RgbColor col(r, g, b); - dotstar->SetPixelColor(index, col); - } - else if (neopixel != nullptr) - { - RgbColor col(r, g, b); - neopixel->SetPixelColor(index, col); - } - else if (neopixelRgbw != nullptr) - { - RgbwColor col(r, g, b, w); - neopixelRgbw->SetPixelColor(index, col); - } - #endif + renderer.setLedRgbw(index, r, g, b, w); } void checkDelayedRender() @@ -457,42 +186,15 @@ namespace Leds{ void renderLed(bool isNewFrame) { - #ifdef USE_FASTLED - FastLED.show(); - #else - if (dotstar != nullptr) - { - if (!dotstar->CanShow()) - { - queueRender(isNewFrame); - return; - } - dotstar->Show(); - } - else if (neopixel != nullptr) - { - if (!neopixel->CanShow()) - { - queueRender(isNewFrame); - return; - } - neopixel->Show(); - } - else if (neopixelRgbw != nullptr) - { - if (!neopixelRgbw->CanShow()) - { - queueRender(isNewFrame); - return; - } - neopixelRgbw->Show(); - } - else - return; - #endif - - delayedRender = false; - stats.renderedFrames = stats.renderedFrames + 1; + if (!renderer.executeRenderLed(isNewFrame)) + { + queueRender(isNewFrame); + } + else + { + delayedRender = false; + stats.renderedFrames = stats.renderedFrames + 1; + } } template void setLed(int, uint8_t, uint8_t, uint8_t); diff --git a/version b/version index 4e379d2..4c98a1d 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.0.2 +0.0.3-beta.1