Kaynağa Gözat

Update smartled driver (#3500)

* update-SmartLeds

* Update sdkconfig.defaults
SybexX 11 ay önce
ebeveyn
işleme
7de18753d9

+ 116 - 61
code/components/jomjol_controlGPIO/Color.cpp

@@ -1,27 +1,53 @@
+/********************************************************************************
+ * https://github.com/RoboticsBrno/SmartLeds
+ *
+ * MIT License
+ * 
+ * Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
+ * 
+ * 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.
+ *******************************************************************************/
+
 #include "Color.h"
 #include <algorithm>
-#include <cmath>
 #include <cassert>
+#include <cmath>
 
 namespace {
 
 // Int -> fixed point
-int up( int x ) { return x * 255; }
+int up(int x) { return x * 255; }
 
 } // namespace
 
-int iRgbSqrt( int num ) {
+int iRgbSqrt(int num) {
     // https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_numeral_system_.28base_2.29
-    assert( "sqrt input should be non-negative" && num >= 0 );
-    assert( "sqrt input should no exceed 16 bits" && num <= 0xFFFF );
+    assert("sqrt input should be non-negative" && num >= 0);
+    assert("sqrt input should no exceed 16 bits" && num <= 0xFFFF);
     int res = 0;
     int bit = 1 << 16;
-    while ( bit > num )
+    while (bit > num)
         bit >>= 2;
-    while ( bit != 0 ) {
-        if ( num >= res + bit ) {
+    while (bit != 0) {
+        if (num >= res + bit) {
             num -= res + bit;
-            res = ( res >> 1 ) + bit;
+            res = (res >> 1) + bit;
         } else
             res >>= 1;
         bit >>= 2;
@@ -29,104 +55,133 @@ int iRgbSqrt( int num ) {
     return res;
 }
 
-Rgb::Rgb( Hsv y ) {
+Rgb::Rgb(const Hsv& y) {
     // https://stackoverflow.com/questions/24152553/hsv-to-rgb-and-back-without-floating-point-math-in-python
     // greyscale
-    if( y.s == 0 ) {
+    if (y.s == 0) {
         r = g = b = y.v;
         return;
     }
 
     const int region = y.h / 43;
-    const int remainder = ( y.h - ( region * 43 ) ) * 6;
-
-    const int p = ( y.v * ( 255 - y.s ) ) >> 8;
-    const int q = ( y.v * ( 255 - ( ( y.s * remainder ) >> 8 ) ) ) >> 8;
-    const int t = ( y.v * ( 255 - ( ( y.s * (255 -remainder ) ) >> 8 ) ) ) >> 8;
-
-    switch( region ) {
-        case 0: r = y.v; g = t; b = p; break;
-        case 1: r = q; g = y.v; b = p; break;
-        case 2: r = p; g = y.v; b = t; break;
-        case 3: r = p; g = q; b = y.v; break;
-        case 4: r = t; g = p; b = y.v; break;
-        case 5: r = y.v; g = p; b = q; break;
-        default: __builtin_trap();
+    const int remainder = (y.h - (region * 43)) * 6;
+
+    const int p = (y.v * (255 - y.s)) >> 8;
+    const int q = (y.v * (255 - ((y.s * remainder) >> 8))) >> 8;
+    const int t = (y.v * (255 - ((y.s * (255 - remainder)) >> 8))) >> 8;
+
+    switch (region) {
+    case 0:
+        r = y.v;
+        g = t;
+        b = p;
+        break;
+    case 1:
+        r = q;
+        g = y.v;
+        b = p;
+        break;
+    case 2:
+        r = p;
+        g = y.v;
+        b = t;
+        break;
+    case 3:
+        r = p;
+        g = q;
+        b = y.v;
+        break;
+    case 4:
+        r = t;
+        g = p;
+        b = y.v;
+        break;
+    case 5:
+        r = y.v;
+        g = p;
+        b = q;
+        break;
+    default:
+        __builtin_trap();
     }
 
     a = y.a;
 }
 
-Rgb& Rgb::operator=( Hsv hsv ) {
-    Rgb r{ hsv };
-    swap( r );
+Rgb& Rgb::operator=(const Hsv& hsv) {
+    Rgb r { hsv };
+    swap(r);
     return *this;
 }
 
-Rgb Rgb::operator+( Rgb in ) const {
+Rgb Rgb::operator+(const Rgb& in) const {
     auto copy = *this;
     copy += in;
     return copy;
 }
 
-Rgb& Rgb::operator+=( Rgb in ) {
+Rgb& Rgb::operator+=(const Rgb& in) {
     unsigned int red = r + in.r;
-    r = ( red < 255 ) ? red : 255;
+    r = (red < 255) ? red : 255;
     unsigned int green = g + in.g;
-    g = ( green < 255 ) ? green : 255;
+    g = (green < 255) ? green : 255;
     unsigned int blue = b + in.b;
-    b = ( blue < 255 ) ? blue : 255;
+    b = (blue < 255) ? blue : 255;
     return *this;
 }
 
-Rgb& Rgb::blend( Rgb in ) {
-    unsigned int inAlpha = in.a * ( 255 - a );
-    unsigned int alpha = a + inAlpha;
-    r = iRgbSqrt( ( ( r * r * a ) + ( in.r * in.r * inAlpha ) ) / alpha );
-    g = iRgbSqrt( ( ( g * g * a ) + ( in.g * in.g * inAlpha ) ) / alpha );
-    b = iRgbSqrt( ( ( b * b * a ) + ( in.b * in.b * inAlpha ) ) / alpha );
-    a = alpha;
+Rgb Rgb::operator-(const Rgb& in) const {
+    auto copy = *this;
+    copy -= in;
+    return copy;
+}
+
+Rgb& Rgb::operator-=(const Rgb& in) {
+    r = (in.r > r) ? 0 : r - in.r;
+    g = (in.g > g) ? 0 : g - in.g;
+    b = (in.b > b) ? 0 : b - in.b;
     return *this;
 }
 
-uint8_t IRAM_ATTR Rgb::getGrb( int idx ) {
-    switch ( idx ) {
-        case 0: return g;
-        case 1: return r;
-        case 2: return b;
-    }
-    __builtin_unreachable();
+Rgb& Rgb::blend(const Rgb& in) {
+    unsigned int inAlpha = in.a * (255 - a);
+    unsigned int alpha = a + inAlpha;
+    r = iRgbSqrt(((r * r * a) + (in.r * in.r * inAlpha)) / alpha);
+    g = iRgbSqrt(((g * g * a) + (in.g * in.g * inAlpha)) / alpha);
+    b = iRgbSqrt(((b * b * a) + (in.b * in.b * inAlpha)) / alpha);
+    a = alpha;
+    return *this;
 }
 
-Hsv::Hsv( Rgb r ) {
-    int min = std::min( r.r, std::min( r.g, r.b ) );
-    int max = std::max( r.r, std::max( r.g, r.b ) );
+Hsv::Hsv(const Rgb& r) {
+    int min = std::min(r.r, std::min(r.g, r.b));
+    int max = std::max(r.r, std::max(r.g, r.b));
     int chroma = max - min;
 
     v = max;
-    if ( chroma == 0 ) {
+    if (chroma == 0) {
         h = s = 0;
         return;
     }
 
-    s = up( chroma ) / max;
+    s = up(chroma) / max;
     int hh;
-    if ( max == r.r )
-        hh = ( up( int( r.g ) - int( r.b ) ) ) / chroma / 6;
-    else if ( max == r.g )
-        hh = 255 / 3 + ( up( int( r.b ) - int( r.r ) ) ) / chroma / 6;
+    if (max == r.r)
+        hh = (up(int(r.g) - int(r.b))) / chroma / 6;
+    else if (max == r.g)
+        hh = 255 / 3 + (up(int(r.b) - int(r.r))) / chroma / 6;
     else
-        hh = 2 * 255 / 3 + ( up( int( r.r ) - int( r.g ) ) ) / chroma / 6;
+        hh = 2 * 255 / 3 + (up(int(r.r) - int(r.g))) / chroma / 6;
 
-    if ( hh < 0 )
+    if (hh < 0)
         hh += 255;
     h = hh;
 
     a = r.a;
 }
 
-Hsv& Hsv::operator=( Rgb rgb ) {
-    Hsv h{ rgb };
-    swap( h );
+Hsv& Hsv::operator=(const Rgb& rgb) {
+    Hsv h { rgb };
+    swap(h);
     return *this;
 }

+ 81 - 37
code/components/jomjol_controlGPIO/Color.h

@@ -1,51 +1,90 @@
-#pragma once
+/********************************************************************************
+ * https://github.com/RoboticsBrno/SmartLeds
+ *
+ * MIT License
+ * 
+ * Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
+ * 
+ * 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.
+ *******************************************************************************/
 
-#ifndef COLOR_H
-#define COLOR_H
+#pragma once
 
-#include <cstdint>
 #include "esp_attr.h"
+#include <cstdint>
 union Hsv;
 
 union Rgb {
-    struct __attribute__ ((packed)) {
-        uint8_t r, g, b, a;
+    struct __attribute__((packed)) {
+        uint8_t g, r, b, a;
     };
     uint32_t value;
 
-    Rgb( uint8_t r = 0, uint8_t g = 0, uint8_t b = 0, uint8_t a = 255 ) : r( r ), g( g ), b( b ), a( a ) {}
-    Rgb( Hsv c );
-    Rgb& operator=( Rgb rgb ) { swap( rgb ); return *this; }
-    Rgb& operator=( Hsv hsv );
-    Rgb operator+( Rgb in ) const;
-    Rgb& operator+=( Rgb in );
-    bool operator==( Rgb in ) const { return in.value == value; }
-    Rgb& blend( Rgb in );
-    void swap( Rgb& o ) {  value = o.value; }
+    Rgb(uint8_t r = 0, uint8_t g = 0, uint8_t b = 0, uint8_t a = 255)
+        : g(g)
+        , r(r)
+        , b(b)
+        , a(a) {}
+    Rgb(const Hsv& c);
+    Rgb(const Rgb&) = default;
+    Rgb& operator=(const Rgb& rgb) {
+        swap(rgb);
+        return *this;
+    }
+    Rgb& operator=(const Hsv& hsv);
+    Rgb operator+(const Rgb& in) const;
+    Rgb& operator+=(const Rgb& in);
+    Rgb operator-(const Rgb& in) const;
+    Rgb& operator-=(const Rgb& in);
+    bool operator==(const Rgb& in) const { return in.value == value; }
+    Rgb& blend(const Rgb& in);
+    void swap(const Rgb& o) { value = o.value; }
     void linearize() {
         r = channelGamma(r);
         g = channelGamma(g);
         b = channelGamma(b);
     }
 
-    uint8_t IRAM_ATTR getGrb( int idx );
-
-    void stretchChannels( uint8_t maxR, uint8_t maxG, uint8_t maxB ) {
-        r = stretch( r, maxR );
-        g = stretch( g, maxG );
-        b = stretch( b, maxB );
+    inline uint8_t IRAM_ATTR getGrb(int idx) {
+        switch (idx) {
+        case 0:
+            return g;
+        case 1:
+            return r;
+        case 2:
+            return b;
+        }
+        __builtin_unreachable();
     }
 
-    void stretchChannelsEvenly( uint8_t max ) {
-        stretchChannels( max, max, max );
+    void stretchChannels(uint8_t maxR, uint8_t maxG, uint8_t maxB) {
+        r = stretch(r, maxR);
+        g = stretch(g, maxG);
+        b = stretch(b, maxB);
     }
 
+    void stretchChannelsEvenly(uint8_t max) { stretchChannels(max, max, max); }
+
 private:
-    uint8_t stretch( int value, uint8_t max ) {
-        return ( value * max ) >> 8;
-    }
+    uint8_t stretch(int value, uint8_t max) { return (value * max) >> 8; }
 
-    uint8_t channelGamma( int channel ) {
+    uint8_t channelGamma(int channel) {
         /* The optimal gamma correction is x^2.8. However, this is expensive to
          * compute. Therefore, we use x^3 for gamma correction. Also, we add a
          * bias as the WS2812 LEDs do not turn on for values less than 4. */
@@ -53,22 +92,27 @@ private:
             return channel;
         channel = channel * channel * channel * 251;
         channel >>= 24;
-        return static_cast< uint8_t >( 4 + channel );
+        return static_cast<uint8_t>(4 + channel);
     }
 };
 
 union Hsv {
-    struct __attribute__ ((packed)) {
+    struct __attribute__((packed)) {
         uint8_t h, s, v, a;
     };
     uint32_t value;
 
-    Hsv( uint8_t h, uint8_t s = 0, uint8_t v = 0, uint8_t a = 255 ) : h( h ), s( s ), v( v ), a( a ) {}
-    Hsv( Rgb r );
-    Hsv& operator=( Hsv h ) { swap( h ); return *this; }
-    Hsv& operator=( Rgb rgb );
-    bool operator==( Hsv in ) const { return in.value == value; }
-    void swap( Hsv& o ) { value = o.value; }
+    Hsv(uint8_t h, uint8_t s = 0, uint8_t v = 0, uint8_t a = 255)
+        : h(h)
+        , s(s)
+        , v(v)
+        , a(a) {}
+    Hsv(const Rgb& r);
+    Hsv& operator=(const Hsv& h) {
+        swap(h);
+        return *this;
+    }
+    Hsv& operator=(const Rgb& rgb);
+    bool operator==(const Hsv& in) const { return in.value == value; }
+    void swap(const Hsv& o) { value = o.value; }
 };
-
-#endif //COLOR_H

+ 60 - 0
code/components/jomjol_controlGPIO/RmtDriver.h

@@ -0,0 +1,60 @@
+/********************************************************************************
+ * https://github.com/RoboticsBrno/SmartLeds
+ *
+ * MIT License
+ * 
+ * Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
+ * 
+ * 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 <esp_system.h>
+#include <stdint.h>
+
+#if defined(ESP_IDF_VERSION)
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
+#define SMARTLEDS_NEW_RMT_DRIVER 1
+#else
+#define SMARTLEDS_NEW_RMT_DRIVER 0
+#endif
+#else
+#define SMARTLEDS_NEW_RMT_DRIVER 0
+#endif
+
+namespace detail {
+
+struct TimingParams {
+    uint32_t T0H;
+    uint32_t T1H;
+    uint32_t T0L;
+    uint32_t T1L;
+    uint32_t TRS;
+};
+
+using LedType = TimingParams;
+
+} // namespace detail
+
+#if SMARTLEDS_NEW_RMT_DRIVER
+#include "RmtDriver5.h"
+#else
+#include "RmtDriver4.h"
+#endif

+ 143 - 0
code/components/jomjol_controlGPIO/RmtDriver4.cpp

@@ -0,0 +1,143 @@
+/********************************************************************************
+ * https://github.com/RoboticsBrno/SmartLeds
+ *
+ * MIT License
+ * 
+ * Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
+ * 
+ * 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.
+ *******************************************************************************/
+
+#include "RmtDriver4.h"
+
+#if !SMARTLEDS_NEW_RMT_DRIVER
+#include "SmartLeds.h"
+
+namespace detail {
+
+// 8 still seems to work, but timings become marginal
+static const int DIVIDER = 4;
+// minimum time of a single RMT duration based on clock ns
+static const double RMT_DURATION_NS = 12.5;
+
+RmtDriver::RmtDriver(const LedType& timing, int count, int pin, int channel_num, SemaphoreHandle_t finishedFlag)
+    : _timing(timing)
+    , _count(count)
+    , _pin((gpio_num_t)pin)
+    , _finishedFlag(finishedFlag)
+    , _channel((rmt_channel_t)channel_num) {
+    _bitToRmt[0].level0 = 1;
+    _bitToRmt[0].level1 = 0;
+    _bitToRmt[0].duration0 = _timing.T0H / (RMT_DURATION_NS * DIVIDER);
+    _bitToRmt[0].duration1 = _timing.T0L / (RMT_DURATION_NS * DIVIDER);
+
+    _bitToRmt[1].level0 = 1;
+    _bitToRmt[1].level1 = 0;
+    _bitToRmt[1].duration0 = _timing.T1H / (RMT_DURATION_NS * DIVIDER);
+    _bitToRmt[1].duration1 = _timing.T1L / (RMT_DURATION_NS * DIVIDER);
+}
+
+esp_err_t RmtDriver::init() {
+    rmt_config_t config = RMT_DEFAULT_CONFIG_TX(_pin, _channel);
+    config.rmt_mode = RMT_MODE_TX;
+    config.clk_div = DIVIDER;
+    config.mem_block_num = 1;
+
+    return rmt_config(&config);
+}
+
+esp_err_t RmtDriver::registerIsr(bool isFirstRegisteredChannel) {
+    auto err = rmt_driver_install(_channel, 0,
+#if defined(CONFIG_RMT_ISR_IRAM_SAFE)
+        ESP_INTR_FLAG_IRAM
+#else
+        0
+#endif
+    );
+    if (err != ESP_OK) {
+        return err;
+    }
+
+    if (isFirstRegisteredChannel) {
+        rmt_register_tx_end_callback(txEndCallback, NULL);
+    }
+
+    err = rmt_translator_init(_channel, translateSample);
+    if (err != ESP_OK) {
+        return err;
+    }
+    return rmt_translator_set_context(_channel, this);
+}
+
+esp_err_t RmtDriver::unregisterIsr() { return rmt_driver_uninstall(_channel); }
+
+void IRAM_ATTR RmtDriver::txEndCallback(rmt_channel_t channel, void* arg) {
+    xSemaphoreGiveFromISR(SmartLed::ledForChannel(channel)->_finishedFlag, nullptr);
+}
+
+void IRAM_ATTR RmtDriver::translateSample(const void* src, rmt_item32_t* dest, size_t src_size,
+    size_t wanted_rmt_items_num, size_t* out_consumed_src_bytes, size_t* out_used_rmt_items) {
+    RmtDriver* self;
+    ESP_ERROR_CHECK(rmt_translator_get_context(out_used_rmt_items, (void**)&self));
+
+    const auto& _bitToRmt = self->_bitToRmt;
+    const auto src_offset = self->_translatorSourceOffset;
+
+    auto* src_components = (const uint8_t*)src;
+    size_t consumed_src_bytes = 0;
+    size_t used_rmt_items = 0;
+
+    while (consumed_src_bytes < src_size && used_rmt_items + 7 < wanted_rmt_items_num) {
+        uint8_t val = *src_components;
+
+        // each bit, from highest to lowest
+        for (uint8_t j = 0; j != 8; j++, val <<= 1) {
+            dest->val = _bitToRmt[val >> 7].val;
+            ++dest;
+        }
+
+        used_rmt_items += 8;
+        ++src_components;
+        ++consumed_src_bytes;
+
+        // skip alpha byte
+        if (((src_offset + consumed_src_bytes) % 4) == 3) {
+            ++src_components;
+            ++consumed_src_bytes;
+
+            // TRST delay after last pixel in strip
+            if (consumed_src_bytes == src_size) {
+                (dest - 1)->duration1 = self->_timing.TRS / (detail::RMT_DURATION_NS * detail::DIVIDER);
+            }
+        }
+    }
+
+    self->_translatorSourceOffset = src_offset + consumed_src_bytes;
+    *out_consumed_src_bytes = consumed_src_bytes;
+    *out_used_rmt_items = used_rmt_items;
+}
+
+esp_err_t RmtDriver::transmit(const Rgb* buffer) {
+    static_assert(sizeof(Rgb) == 4); // The translator code above assumes RGB is 4 bytes
+
+    _translatorSourceOffset = 0;
+    return rmt_write_sample(_channel, (const uint8_t*)buffer, _count * 4, false);
+}
+};
+#endif // !SMARTLEDS_NEW_RMT_DRIVER

+ 68 - 0
code/components/jomjol_controlGPIO/RmtDriver4.h

@@ -0,0 +1,68 @@
+/********************************************************************************
+ * https://github.com/RoboticsBrno/SmartLeds
+ *
+ * MIT License
+ * 
+ * Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
+ * 
+ * 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 "RmtDriver.h"
+
+#if !SMARTLEDS_NEW_RMT_DRIVER
+#include "Color.h"
+#include <driver/rmt.h>
+#include <freertos/FreeRTOS.h>
+#include <freertos/semphr.h>
+
+namespace detail {
+
+constexpr const int CHANNEL_COUNT = RMT_CHANNEL_MAX;
+
+class RmtDriver {
+public:
+    RmtDriver(const LedType& timing, int count, int pin, int channel_num, SemaphoreHandle_t finishedFlag);
+    RmtDriver(const RmtDriver&) = delete;
+
+    esp_err_t init();
+    esp_err_t registerIsr(bool isFirstRegisteredChannel);
+    esp_err_t unregisterIsr();
+    esp_err_t transmit(const Rgb* buffer);
+
+private:
+    static void IRAM_ATTR txEndCallback(rmt_channel_t channel, void* arg);
+
+    static void IRAM_ATTR translateSample(const void* src, rmt_item32_t* dest, size_t src_size,
+        size_t wanted_rmt_items_num, size_t* out_consumed_src_bytes, size_t* out_used_rmt_items);
+
+    const LedType& _timing;
+    int _count;
+    gpio_num_t _pin;
+    SemaphoreHandle_t _finishedFlag;
+
+    rmt_channel_t _channel;
+    rmt_item32_t _bitToRmt[2];
+    size_t _translatorSourceOffset;
+};
+
+};
+#endif // !SMARTLEDS_NEW_RMT_DRIVER

+ 202 - 0
code/components/jomjol_controlGPIO/RmtDriver5.cpp

@@ -0,0 +1,202 @@
+/********************************************************************************
+ * https://github.com/RoboticsBrno/SmartLeds
+ *
+ * MIT License
+ * 
+ * Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
+ * 
+ * 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.
+ *******************************************************************************/
+
+#include "RmtDriver5.h"
+
+#if SMARTLEDS_NEW_RMT_DRIVER
+#include <cstddef>
+
+#include "SmartLeds.h"
+
+namespace detail {
+
+static constexpr const uint32_t RMT_RESOLUTION_HZ = 20 * 1000 * 1000; // 20 MHz
+static constexpr const uint32_t RMT_NS_PER_TICK = 1000000000LLU / RMT_RESOLUTION_HZ;
+
+static RmtEncoderWrapper* IRAM_ATTR encSelf(rmt_encoder_t* encoder) {
+    return (RmtEncoderWrapper*)(((intptr_t)encoder) - offsetof(RmtEncoderWrapper, base));
+}
+
+static size_t IRAM_ATTR encEncode(rmt_encoder_t* encoder, rmt_channel_handle_t tx_channel, const void* primary_data,
+    size_t data_size, rmt_encode_state_t* ret_state) {
+    auto* self = encSelf(encoder);
+
+    // Delay after last pixel
+    if ((self->last_state & RMT_ENCODING_COMPLETE) && self->frame_idx == data_size) {
+        *ret_state = (rmt_encode_state_t)0;
+        return self->copy_encoder->encode(
+            self->copy_encoder, tx_channel, (const void*)&self->reset_code, sizeof(self->reset_code), ret_state);
+    }
+
+    if (self->last_state & RMT_ENCODING_COMPLETE) {
+        Rgb* pixel = ((Rgb*)primary_data) + self->frame_idx;
+        self->buffer_len = sizeof(self->buffer);
+        for (size_t i = 0; i < sizeof(self->buffer); ++i) {
+            self->buffer[i] = pixel->getGrb(self->component_idx);
+            if (++self->component_idx == 3) {
+                self->component_idx = 0;
+                if (++self->frame_idx == data_size) {
+                    self->buffer_len = i + 1;
+                    break;
+                }
+                ++pixel;
+            }
+        }
+    }
+
+    self->last_state = (rmt_encode_state_t)0;
+    auto encoded_symbols = self->bytes_encoder->encode(
+        self->bytes_encoder, tx_channel, (const void*)&self->buffer, self->buffer_len, &self->last_state);
+    if (self->last_state & RMT_ENCODING_MEM_FULL) {
+        *ret_state = RMT_ENCODING_MEM_FULL;
+    } else {
+        *ret_state = (rmt_encode_state_t)0;
+    }
+
+    return encoded_symbols;
+}
+
+static esp_err_t encReset(rmt_encoder_t* encoder) {
+    auto* self = encSelf(encoder);
+    rmt_encoder_reset(self->bytes_encoder);
+    rmt_encoder_reset(self->copy_encoder);
+    self->last_state = RMT_ENCODING_COMPLETE;
+    self->frame_idx = 0;
+    self->component_idx = 0;
+    return ESP_OK;
+}
+
+static esp_err_t encDelete(rmt_encoder_t* encoder) {
+    auto* self = encSelf(encoder);
+    rmt_del_encoder(self->bytes_encoder);
+    rmt_del_encoder(self->copy_encoder);
+    return ESP_OK;
+}
+
+RmtDriver::RmtDriver(const LedType& timing, int count, int pin, int channel_num, SemaphoreHandle_t finishedFlag)
+    : _timing(timing)
+    , _count(count)
+    , _pin(pin)
+    , _finishedFlag(finishedFlag)
+    , _channel(nullptr)
+    , _encoder {} {}
+
+esp_err_t RmtDriver::init() {
+    _encoder.base.encode = encEncode;
+    _encoder.base.reset = encReset;
+    _encoder.base.del = encDelete;
+
+    _encoder.reset_code.duration0 = _timing.TRS / RMT_NS_PER_TICK;
+
+    rmt_bytes_encoder_config_t bytes_cfg = {
+        .bit0 = {
+            .duration0 = uint16_t(_timing.T0H  / RMT_NS_PER_TICK),
+            .level0 = 1,
+            .duration1 = uint16_t(_timing.T0L  / RMT_NS_PER_TICK),
+            .level1 = 0,
+        },
+        .bit1 = {
+            .duration0 = uint16_t(_timing.T1H / RMT_NS_PER_TICK),
+            .level0 = 1,
+            .duration1 = uint16_t(_timing.T1L  / RMT_NS_PER_TICK),
+            .level1 = 0,
+        },
+        .flags = {
+            .msb_first = 1,
+        },
+    };
+
+    auto err = rmt_new_bytes_encoder(&bytes_cfg, &_encoder.bytes_encoder);
+    if (err != ESP_OK) {
+        return err;
+    }
+
+    rmt_copy_encoder_config_t copy_cfg = {};
+    err = rmt_new_copy_encoder(&copy_cfg, &_encoder.copy_encoder);
+    if (err != ESP_OK) {
+        return err;
+    }
+
+    // The config must be in registerIsr, because rmt_new_tx_channel
+    // registers the ISR
+    return ESP_OK;
+}
+
+esp_err_t RmtDriver::registerIsr(bool isFirstRegisteredChannel) {
+    rmt_tx_channel_config_t conf = {
+        .gpio_num = (gpio_num_t)_pin,
+        .clk_src = RMT_CLK_SRC_DEFAULT, //.clk_src = RMT_CLK_SRC_APB,
+        .resolution_hz = RMT_RESOLUTION_HZ,
+        .mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL,
+        .trans_queue_depth = 1,
+        .flags = {},
+    };
+
+    auto err = rmt_new_tx_channel(&conf, &_channel);
+    if (err != ESP_OK) {
+        return err;
+    }
+
+    rmt_tx_event_callbacks_t callbacks_cfg = {};
+    callbacks_cfg.on_trans_done = txDoneCallback;
+
+    err = rmt_tx_register_event_callbacks(_channel, &callbacks_cfg, this);
+    if (err != ESP_OK) {
+        return err;
+    }
+
+    return rmt_enable(_channel);
+}
+
+esp_err_t RmtDriver::unregisterIsr() {
+    auto err = rmt_del_encoder(&_encoder.base);
+    if (err != ESP_OK) {
+        return err;
+    }
+
+    err = rmt_disable(_channel);
+    if (err != ESP_OK) {
+        return err;
+    }
+
+    return rmt_del_channel(_channel);
+}
+
+bool IRAM_ATTR RmtDriver::txDoneCallback(
+    rmt_channel_handle_t tx_chan, const rmt_tx_done_event_data_t* edata, void* user_ctx) {
+    auto* self = (RmtDriver*)user_ctx;
+    auto taskWoken = pdTRUE;
+    xSemaphoreGiveFromISR(self->_finishedFlag, &taskWoken);
+    return taskWoken == pdTRUE;
+}
+
+esp_err_t RmtDriver::transmit(const Rgb* buffer) {
+    rmt_encoder_reset(&_encoder.base);
+    rmt_transmit_config_t cfg = {};
+    return rmt_transmit(_channel, &_encoder.base, buffer, _count, &cfg);
+}
+};
+#endif // !SMARTLEDS_NEW_RMT_DRIVER

+ 91 - 0
code/components/jomjol_controlGPIO/RmtDriver5.h

@@ -0,0 +1,91 @@
+/********************************************************************************
+ * https://github.com/RoboticsBrno/SmartLeds
+ *
+ * MIT License
+ * 
+ * Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
+ * 
+ * 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 "RmtDriver.h"
+
+#if SMARTLEDS_NEW_RMT_DRIVER
+#include <driver/rmt_tx.h>
+#include <freertos/FreeRTOS.h>
+#include <freertos/semphr.h>
+#include <type_traits>
+
+#include "Color.h"
+
+#if !defined(CONFIG_RMT_ISR_IRAM_SAFE) && !defined(SMARTLEDS_DISABLE_IRAM_WARNING)
+#warning "Please enable CONFIG_RMT_ISR_IRAM_SAFE IDF option." \
+    "without it, the IDF driver is not able to supply data fast enough."
+#endif
+
+namespace detail {
+
+constexpr const int CHANNEL_COUNT = SOC_RMT_GROUPS * SOC_RMT_CHANNELS_PER_GROUP;
+
+class RmtDriver;
+
+// This is ridiculous
+struct RmtEncoderWrapper {
+    struct rmt_encoder_t base;
+    struct rmt_encoder_t* bytes_encoder;
+    struct rmt_encoder_t* copy_encoder;
+    RmtDriver* driver;
+    rmt_symbol_word_t reset_code;
+
+    uint8_t buffer[SOC_RMT_MEM_WORDS_PER_CHANNEL / 8];
+    rmt_encode_state_t last_state;
+    size_t frame_idx;
+    uint8_t component_idx;
+    uint8_t buffer_len;
+};
+
+static_assert(std::is_standard_layout<RmtEncoderWrapper>::value == true);
+
+class RmtDriver {
+public:
+    RmtDriver(const LedType& timing, int count, int pin, int channel_num, SemaphoreHandle_t finishedFlag);
+    RmtDriver(const RmtDriver&) = delete;
+
+    esp_err_t init();
+    esp_err_t registerIsr(bool isFirstRegisteredChannel);
+    esp_err_t unregisterIsr();
+    esp_err_t transmit(const Rgb* buffer);
+
+private:
+    static bool IRAM_ATTR txDoneCallback(
+        rmt_channel_handle_t tx_chan, const rmt_tx_done_event_data_t* edata, void* user_ctx);
+
+    const LedType& _timing;
+    int _count;
+    int _pin;
+    SemaphoreHandle_t _finishedFlag;
+
+    rmt_channel_handle_t _channel;
+    RmtEncoderWrapper _encoder;
+};
+
+};
+#endif // !SMARTLEDS_NEW_RMT_DRIVER

+ 30 - 85
code/components/jomjol_controlGPIO/SmartLeds.cpp

@@ -1,90 +1,35 @@
-#include "SmartLeds.h"
-
-
-/* PlatformIO 6 (ESP IDF 5) does no longer allow access to RMTMEM,
-   see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/migration-guides/release-5.x/5.0/peripherals.html?highlight=rmtmem#id5 
-   As a dirty workaround, we copy the needed structures from rmt_struct.h
-   In the long run, this should be replaced! */
-typedef struct rmt_item32_s {
-    union {
-        struct {
-            uint32_t duration0 :15;
-            uint32_t level0 :1;
-            uint32_t duration1 :15;
-            uint32_t level1 :1;
-        };
-        uint32_t val;
-    };
-} rmt_item32_t;
-
-//Allow access to RMT memory using RMTMEM.chan[0].data32[8]
-typedef volatile struct rmt_mem_s {
-    struct {
-        rmt_item32_t data32[64];
-    } chan[8];
-} rmt_mem_t;
-extern rmt_mem_t RMTMEM;
-
+/********************************************************************************
+ * https://github.com/RoboticsBrno/SmartLeds
+ *
+ * MIT License
+ * 
+ * Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
+ * 
+ * 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.
+ *******************************************************************************/
 
+#include "SmartLeds.h"
 
 IsrCore SmartLed::_interruptCore = CoreCurrent;
-intr_handle_t SmartLed::_interruptHandle = NULL;
-
-SmartLed*& IRAM_ATTR SmartLed::ledForChannel( int channel ) {
-    static SmartLed* table[8] = { nullptr };
-    assert( channel < 8 );
-    return table[ channel ];
-}
-
-void IRAM_ATTR SmartLed::interruptHandler(void*) {
-    for (int channel = 0; channel != 8; channel++) {
-        auto self = ledForChannel( channel );
-
-        if ( RMT.int_st.val & (1 << (24 + channel ) ) ) { // tx_thr_event
-            if ( self )
-                self->copyRmtHalfBlock();
-            RMT.int_clr.val |= 1 << ( 24 + channel );
-        } else if ( RMT.int_st.val & ( 1 << (3 * channel ) ) ) { // tx_end
-            if ( self )
-                xSemaphoreGiveFromISR( self->_finishedFlag, nullptr );
-            RMT.int_clr.val |= 1 << ( 3 * channel );
-        }
-    }
-}
-
-void IRAM_ATTR SmartLed::copyRmtHalfBlock() {
-    int offset = detail::MAX_PULSES * _halfIdx;
-    _halfIdx = !_halfIdx;
-    int len = 3 - _componentPosition + 3 * ( _count - 1 );
-    len = std::min( len, detail::MAX_PULSES / 8 );
-
-    if ( !len ) {
-        for ( int i = 0; i < detail::MAX_PULSES; i++) {
-            RMTMEM.chan[ _channel].data32[i + offset ].val = 0;
-        }
-    }
-
-    int i;
-    for ( i = 0; i != len && _pixelPosition != _count; i++ ) {
-        uint8_t val = _buffer[ _pixelPosition ].getGrb( _componentPosition );
-        for ( int j = 0; j != 8; j++, val <<= 1 ) {
-            int bit = val >> 7;
-            int idx = i * 8 + offset + j;
-            RMTMEM.chan[ _channel ].data32[ idx ].val = _bitToRmt[ bit & 0x01 ].value;
-        }
-        if ( _pixelPosition == _count - 1 && _componentPosition == 2 ) {
-            RMTMEM.chan[ _channel ].data32[ i * 8 + offset + 7 ].duration1 =
-                _timing.TRS / ( detail::RMT_DURATION_NS * detail::DIVIDER );
-        }
-
-        _componentPosition++;
-        if ( _componentPosition == 3 ) {
-            _componentPosition = 0;
-            _pixelPosition++;
-        }
-    }
 
-    for ( i *= 8; i != detail::MAX_PULSES; i++ ) {
-        RMTMEM.chan[ _channel ].data32[ i + offset ].val = 0;
-    }
+SmartLed*& IRAM_ATTR SmartLed::ledForChannel(int channel) {
+    static SmartLed* table[detail::CHANNEL_COUNT] = {};
+    assert(channel < detail::CHANNEL_COUNT);
+    return table[channel];
 }

+ 238 - 331
code/components/jomjol_controlGPIO/SmartLeds.h

@@ -1,7 +1,30 @@
-#pragma once
+/********************************************************************************
+ * https://github.com/RoboticsBrno/SmartLeds
+ *
+ * MIT License
+ * 
+ * Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
+ * 
+ * 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.
+ *******************************************************************************/
 
-#ifndef SMARTLEDS_H
-#define SMARTLEDS_H
+#pragma once
 
 /*
  * A C++ driver for the WS2812 LEDs using the RMT peripheral on the ESP32.
@@ -31,305 +54,196 @@
  * THE SOFTWARE.
  */
 
-#include <memory>
 #include <cassert>
 #include <cstring>
+#include <memory>
 
-#include "esp_idf_version.h"
-#if (ESP_IDF_VERSION_MAJOR >= 5)
-#include "soc/periph_defs.h"
-#include "esp_private/periph_ctrl.h"
-#include "soc/gpio_sig_map.h"
-#include "soc/gpio_periph.h"
-#include "soc/io_mux_reg.h"
-#include "esp_rom_gpio.h"
-#define gpio_pad_select_gpio esp_rom_gpio_pad_select_gpio
-#define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c)
-#define gpio_matrix_out(a,b,c,d) esp_rom_gpio_connect_out_signal(a,b,c,d)
-#define ets_delay_us(a) esp_rom_delay_us(a)
-#endif
-
-#if defined ( ARDUINO )
-    extern "C" { // ...someone forgot to put in the includes...
-        #include "esp32-hal.h"
-        #include "esp_intr_alloc.h"
-        #include "esp_ipc.h"
-        #include "driver/gpio.h"
-        #include "driver/periph_ctrl.h"
-        #include "freertos/semphr.h"
-        #include "soc/rmt_struct.h"
-        #include <driver/spi_master.h>
-        #include "esp_idf_version.h"
-#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL( 4, 0, 0 )
-        #include "soc/dport_reg.h"
-#endif
-    }
-#elif defined ( ESP_PLATFORM )
-    extern "C" { // ...someone forgot to put in the includes...
-        #include <esp_intr_alloc.h>
-        #include <esp_ipc.h>
-        #include <driver/gpio.h>
-        #include <freertos/FreeRTOS.h>
-        #include <freertos/semphr.h>
-        #include <soc/dport_reg.h>
-        #include <soc/gpio_sig_map.h>
-        #include <soc/rmt_struct.h>
-        #include <driver/spi_master.h>
-    }
-    #include <stdio.h>
-#endif
-
-#if (ESP_IDF_VERSION_MAJOR >= 4) && (ESP_IDF_VERSION_MINOR > 1)
-#include "hal/gpio_ll.h"
-#else
-#include "soc/gpio_periph.h"
-#define esp_rom_delay_us ets_delay_us
-static inline int gpio_ll_get_level(gpio_dev_t *hw, int gpio_num)
-{
-    if (gpio_num < 32) {
-        return (hw->in >> gpio_num) & 0x1;
-    } else {
-        return (hw->in1.data >> (gpio_num - 32)) & 0x1;
-    }
-}
-#endif
-
-#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
-#if !(configENABLE_BACKWARD_COMPATIBILITY == 1)
-#define xSemaphoreHandle SemaphoreHandle_t
-#endif
-#endif
+#include <driver/gpio.h>
+#include <driver/spi_master.h>
+#include <esp_intr_alloc.h>
+#include <esp_ipc.h>
+#include <freertos/FreeRTOS.h>
+#include <freertos/semphr.h>
 
 #include "Color.h"
 
-namespace detail {
-
-struct TimingParams {
-    uint32_t T0H;
-    uint32_t T1H;
-    uint32_t T0L;
-    uint32_t T1L;
-    uint32_t TRS;
-};
-
-union RmtPulsePair {
-    struct {
-        int duration0:15;
-        int level0:1;
-        int duration1:15;
-        int level1:1;
-    };
-    uint32_t value;
-};
-
-static const int DIVIDER = 4; // 8 still seems to work, but timings become marginal
-static const int MAX_PULSES = 32; // A channel has a 64 "pulse" buffer - we use half per pass
-static const double RMT_DURATION_NS = 12.5; // minimum time of a single RMT duration based on clock ns
-
-} // namespace detail
+#include "RmtDriver.h"
 
 using LedType = detail::TimingParams;
 
-static const LedType LED_WS2812  = { 350, 700, 800, 600, 50000 };
-static const LedType LED_WS2812B = { 400, 850, 850, 400, 50100 };
-static const LedType LED_SK6812  = { 300, 600, 900, 600, 80000 };
-static const LedType LED_WS2813  = { 350, 800, 350, 350, 300000 };
-
+// Times are in nanoseconds,
+// The RMT driver runs at 20MHz, so minimal representable time is 50 nanoseconds
+static const LedType LED_WS2812 = { 350, 700, 800, 600, 50000 };
+// longer reset time because https://blog.adafruit.com/2017/05/03/psa-the-ws2812b-rgb-led-has-been-revised-will-require-code-tweak/
+static const LedType LED_WS2812B = { 400, 800, 850, 450, 300000 }; // universal
+static const LedType LED_WS2812B_NEWVARIANT = { 200, 750, 750, 200, 300000 };
+static const LedType LED_WS2812B_OLDVARIANT = { 400, 800, 850, 450, 50000 };
+// This is timing from datasheet, but does not seem to actually work - try LED_WS2812B
+static const LedType LED_WS2812C = { 250, 550, 550, 250, 280000 };
+static const LedType LED_SK6812 = { 300, 600, 900, 600, 80000 };
+static const LedType LED_WS2813 = { 350, 800, 350, 350, 300000 };
+
+// Single buffer == can't touch the Rgbs between show() and wait()
 enum BufferType { SingleBuffer = 0, DoubleBuffer };
 
-enum IsrCore { CoreFirst = 0, CoreSecond = 1, CoreCurrent = 2};
+enum IsrCore { CoreFirst = 0, CoreSecond = 1, CoreCurrent = 2 };
 
 class SmartLed {
 public:
+    friend class detail::RmtDriver;
+
     // The RMT interrupt must not run on the same core as WiFi interrupts, otherwise SmartLeds
     // can't fill the RMT buffer fast enough, resulting in rendering artifacts.
     // Usually, that means you have to set isrCore == CoreSecond.
     //
     // If you use anything other than CoreCurrent, the FreeRTOS scheduler MUST be already running,
     // so you can't use it if you define SmartLed as global variable.
-    SmartLed( const LedType& type, int count, int pin, int channel = 0, BufferType doubleBuffer = SingleBuffer, IsrCore isrCore = CoreCurrent)
-        : _timing( type ),
-          _channel( channel ),
-          _count( count ),
-          _firstBuffer( new Rgb[ count ] ),
-          _secondBuffer( doubleBuffer ? new Rgb[ count ] : nullptr ),
-          _finishedFlag( xSemaphoreCreateBinary() )
-    {
-        assert( channel >= 0 && channel < 8 );
-        assert( ledForChannel( channel ) == nullptr );
-
-        xSemaphoreGive( _finishedFlag );
-
-        DPORT_SET_PERI_REG_MASK( DPORT_PERIP_CLK_EN_REG, DPORT_RMT_CLK_EN );
-        DPORT_CLEAR_PERI_REG_MASK( DPORT_PERIP_RST_EN_REG, DPORT_RMT_RST );
-
-        PIN_FUNC_SELECT( GPIO_PIN_MUX_REG[ pin ], 2 );
-        gpio_set_direction( static_cast< gpio_num_t >( pin ), GPIO_MODE_OUTPUT );
-        gpio_matrix_out( static_cast< gpio_num_t >( pin ), RMT_SIG_OUT0_IDX + _channel, 0, 0 );
-        initChannel( _channel );
-
-        RMT.tx_lim_ch[ _channel ].limit = detail::MAX_PULSES;
-        RMT.int_ena.val |= 1 << ( 24 + _channel );
-        RMT.int_ena.val |= 1 << ( 3 * _channel );
-
-        _bitToRmt[ 0 ].level0 = 1;
-        _bitToRmt[ 0 ].level1 = 0;
-        _bitToRmt[ 0 ].duration0 = _timing.T0H / ( detail::RMT_DURATION_NS * detail::DIVIDER );
-        _bitToRmt[ 0 ].duration1 = _timing.T0L / ( detail::RMT_DURATION_NS * detail::DIVIDER );
-
-        _bitToRmt[ 1 ].level0 = 1;
-        _bitToRmt[ 1 ].level1 = 0;
-        _bitToRmt[ 1 ].duration0 = _timing.T1H / ( detail::RMT_DURATION_NS * detail::DIVIDER );
-        _bitToRmt[ 1 ].duration1 = _timing.T1L / ( detail::RMT_DURATION_NS * detail::DIVIDER );
-
-        if ( !anyAlive() ) {
+    //
+    // Does nothing on chips that only have one core.
+    SmartLed(const LedType& type, int count, int pin, int channel = 0, BufferType doubleBuffer = DoubleBuffer,
+        IsrCore isrCore = CoreCurrent)
+        : _finishedFlag(xSemaphoreCreateBinary())
+        , _driver(type, count, pin, channel, _finishedFlag)
+        , _channel(channel)
+        , _count(count)
+        , _firstBuffer(new Rgb[count])
+        , _secondBuffer(doubleBuffer ? new Rgb[count] : nullptr) {
+        assert(channel >= 0 && channel < detail::CHANNEL_COUNT);
+        assert(ledForChannel(channel) == nullptr);
+
+        xSemaphoreGive(_finishedFlag);
+
+        _driver.init();
+
+#if !defined(SOC_CPU_CORES_NUM) || SOC_CPU_CORES_NUM > 1
+        if (!anyAlive() && isrCore != CoreCurrent) {
             _interruptCore = isrCore;
-            if(isrCore != CoreCurrent) {
-                ESP_ERROR_CHECK(esp_ipc_call_blocking(isrCore, registerInterrupt, NULL));
-            } else {
-                registerInterrupt(NULL);
-            }
+            ESP_ERROR_CHECK(esp_ipc_call_blocking(isrCore, registerInterrupt, (void*)this));
+        } else
+#endif
+        {
+            registerInterrupt((void*)this);
         }
 
-        ledForChannel( channel ) = this;
+        ledForChannel(channel) = this;
     }
 
     ~SmartLed() {
-        ledForChannel( _channel ) = nullptr;
-        if ( !anyAlive() ) {
-            if(_interruptCore != CoreCurrent) {
-                ESP_ERROR_CHECK(esp_ipc_call_blocking(_interruptCore, unregisterInterrupt, NULL));
-            } else {
-                unregisterInterrupt(NULL);
-            }
+        ledForChannel(_channel) = nullptr;
+#if !defined(SOC_CPU_CORES_NUM) || SOC_CPU_CORES_NUM > 1
+        if (!anyAlive() && _interruptCore != CoreCurrent) {
+            ESP_ERROR_CHECK(esp_ipc_call_blocking(_interruptCore, unregisterInterrupt, (void*)this));
+        } else
+#endif
+        {
+            unregisterInterrupt((void*)this);
         }
-        vSemaphoreDelete( _finishedFlag );
+        vSemaphoreDelete(_finishedFlag);
     }
 
-    Rgb& operator[]( int idx ) {
-        return _firstBuffer[ idx ];
-    }
+    Rgb& operator[](int idx) { return _firstBuffer[idx]; }
 
-    const Rgb& operator[]( int idx ) const {
-        return _firstBuffer[ idx ];
-    }
+    const Rgb& operator[](int idx) const { return _firstBuffer[idx]; }
 
-    void show() {
-        _buffer = _firstBuffer.get();
-        startTransmission();
+    esp_err_t show() {
+        esp_err_t err = startTransmission();
         swapBuffers();
+        return err;
     }
 
-    bool wait( TickType_t timeout = portMAX_DELAY ) {
-        if( xSemaphoreTake( _finishedFlag, timeout ) == pdTRUE ) {
-            xSemaphoreGive( _finishedFlag );
+    bool wait(TickType_t timeout = portMAX_DELAY) {
+        if (xSemaphoreTake(_finishedFlag, timeout) == pdTRUE) {
+            xSemaphoreGive(_finishedFlag);
             return true;
         }
         return false;
     }
 
-    int size() const {
-        return _count;
-    }
+    int size() const { return _count; }
+    int channel() const { return _channel; }
 
-    Rgb *begin() { return _firstBuffer.get(); }
-    const Rgb *begin() const { return _firstBuffer.get(); }
-    const Rgb *cbegin() const { return _firstBuffer.get(); }
+    Rgb* begin() { return _firstBuffer.get(); }
+    const Rgb* begin() const { return _firstBuffer.get(); }
+    const Rgb* cbegin() const { return _firstBuffer.get(); }
 
-    Rgb *end() { return _firstBuffer.get() + _count; }
-    const Rgb *end() const { return _firstBuffer.get() + _count; }
-    const Rgb *cend() const { return _firstBuffer.get() + _count; }
+    Rgb* end() { return _firstBuffer.get() + _count; }
+    const Rgb* end() const { return _firstBuffer.get() + _count; }
+    const Rgb* cend() const { return _firstBuffer.get() + _count; }
 
 private:
-    static intr_handle_t _interruptHandle;
     static IsrCore _interruptCore;
 
-    static void initChannel( int channel ) {
-        RMT.apb_conf.fifo_mask = 1;  //enable memory access, instead of FIFO mode.
-        RMT.apb_conf.mem_tx_wrap_en = 1; //wrap around when hitting end of buffer
-        RMT.conf_ch[ channel ].conf0.div_cnt = detail::DIVIDER;
-        RMT.conf_ch[ channel ].conf0.mem_size = 1;
-        RMT.conf_ch[ channel ].conf0.carrier_en = 0;
-        RMT.conf_ch[ channel ].conf0.carrier_out_lv = 1;
-        RMT.conf_ch[ channel ].conf0.mem_pd = 0;
-
-        RMT.conf_ch[ channel ].conf1.rx_en = 0;
-        RMT.conf_ch[ channel ].conf1.mem_owner = 0;
-        RMT.conf_ch[ channel ].conf1.tx_conti_mode = 0;    //loop back mode.
-        RMT.conf_ch[ channel ].conf1.ref_always_on = 1;    // use apb clock: 80M
-        RMT.conf_ch[ channel ].conf1.idle_out_en = 1;
-        RMT.conf_ch[ channel ].conf1.idle_out_lv = 0;
-    }
-
-    static void registerInterrupt(void *) {
-        ESP_ERROR_CHECK(esp_intr_alloc( ETS_RMT_INTR_SOURCE, 0, interruptHandler, nullptr, &_interruptHandle));
+    static void registerInterrupt(void* selfVoid) {
+        auto* self = (SmartLed*)selfVoid;
+        ESP_ERROR_CHECK(self->_driver.registerIsr(!anyAlive()));
     }
 
-    static void unregisterInterrupt(void*) {
-        esp_intr_free( _interruptHandle );
+    static void unregisterInterrupt(void* selfVoid) {
+        auto* self = (SmartLed*)selfVoid;
+        ESP_ERROR_CHECK(self->_driver.unregisterIsr());
     }
 
-    static SmartLed*& IRAM_ATTR ledForChannel( int channel );
-    static void IRAM_ATTR interruptHandler( void* );
+    static SmartLed*& IRAM_ATTR ledForChannel(int channel);
 
-    void IRAM_ATTR copyRmtHalfBlock();
+    static bool anyAlive() {
+        for (int i = 0; i != detail::CHANNEL_COUNT; i++)
+            if (ledForChannel(i) != nullptr)
+                return true;
+        return false;
+    }
 
     void swapBuffers() {
-        if ( _secondBuffer )
-            _firstBuffer.swap( _secondBuffer );
+        if (_secondBuffer)
+            _firstBuffer.swap(_secondBuffer);
     }
 
-    void startTransmission() {
-        // Invalid use of the library
-        if( xSemaphoreTake( _finishedFlag, 0 ) != pdTRUE )
+    esp_err_t startTransmission() {
+        // Invalid use of the library, you must wait() fir previous frame to get processed first
+        if (xSemaphoreTake(_finishedFlag, 0) != pdTRUE)
             abort();
 
-        _pixelPosition = _componentPosition = _halfIdx = 0;
-        copyRmtHalfBlock();
-        if ( _pixelPosition < _count )
-            copyRmtHalfBlock();
-
-        RMT.conf_ch[ _channel ].conf1.mem_rd_rst = 1;
-        RMT.conf_ch[ _channel ].conf1.tx_start = 1;
-    }
+        auto err = _driver.transmit(_firstBuffer.get());
+        if (err != ESP_OK) {
+            return err;
+        }
 
-    static bool anyAlive() {
-        for ( int i = 0; i != 8; i++ )
-            if ( ledForChannel( i ) != nullptr ) return true;
-        return false;
+        return ESP_OK;
     }
 
-    const LedType& _timing;
+    SemaphoreHandle_t _finishedFlag;
+    detail::RmtDriver _driver;
     int _channel;
-    detail::RmtPulsePair _bitToRmt[ 2 ];
     int _count;
-    std::unique_ptr< Rgb[] > _firstBuffer;
-    std::unique_ptr< Rgb[] > _secondBuffer;
-    Rgb *_buffer;
-
-    xSemaphoreHandle _finishedFlag;
-
-    int _pixelPosition;
-    int _componentPosition;
-    int _halfIdx;
+    std::unique_ptr<Rgb[]> _firstBuffer;
+    std::unique_ptr<Rgb[]> _secondBuffer;
 };
 
+#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3)
+#define _SMARTLEDS_SPI_HOST SPI2_HOST
+#define _SMARTLEDS_SPI_DMA_CHAN SPI_DMA_CH_AUTO
+#else
+#define _SMARTLEDS_SPI_HOST HSPI_HOST
+#define _SMARTLEDS_SPI_DMA_CHAN 1
+#endif
+
 class Apa102 {
 public:
     struct ApaRgb {
-        ApaRgb( uint8_t r = 0, uint8_t g = 0, uint32_t b = 0, uint32_t v = 0xFF )
-            : v( 0xE0 | v ), b( b ), g( g ), r( r )
-        {}
+        ApaRgb(uint8_t r = 0, uint8_t g = 0, uint32_t b = 0, uint32_t v = 0xFF)
+            : v(0xE0 | v)
+            , b(b)
+            , g(g)
+            , r(r) {}
 
-        ApaRgb& operator=( const Rgb& o ) {
+        ApaRgb& operator=(const Rgb& o) {
             r = o.r;
             g = o.g;
             b = o.b;
             return *this;
         }
 
-        ApaRgb& operator=( const Hsv& o ) {
-            *this = Rgb{ o };
+        ApaRgb& operator=(const Hsv& o) {
+            *this = Rgb { o };
             return *this;
         }
 
@@ -339,14 +253,14 @@ public:
     static const int FINAL_FRAME_SIZE = 4;
     static const int TRANS_COUNT = 2 + 8;
 
-    Apa102( int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer )
-        : _count( count ),
-          _firstBuffer( new ApaRgb[ count ] ),
-          _secondBuffer( doubleBuffer ? new ApaRgb[ count ] : nullptr ),
-          _initFrame( 0 )
-    {
+    Apa102(int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer, int clock_speed_hz = 1000000)
+        : _count(count)
+        , _firstBuffer(new ApaRgb[count])
+        , _secondBuffer(doubleBuffer ? new ApaRgb[count] : nullptr)
+        , _transCount(0)
+        , _initFrame(0) {
         spi_bus_config_t buscfg;
-        memset( &buscfg, 0, sizeof( buscfg ) );
+        memset(&buscfg, 0, sizeof(buscfg));
         buscfg.mosi_io_num = datapin;
         buscfg.miso_io_num = -1;
         buscfg.sclk_io_num = clkpin;
@@ -355,33 +269,29 @@ public:
         buscfg.max_transfer_sz = 65535;
 
         spi_device_interface_config_t devcfg;
-        memset( &devcfg, 0, sizeof( devcfg ) );
-        devcfg.clock_speed_hz = 1000000;
+        memset(&devcfg, 0, sizeof(devcfg));
+        devcfg.clock_speed_hz = clock_speed_hz;
         devcfg.mode = 0;
         devcfg.spics_io_num = -1;
         devcfg.queue_size = TRANS_COUNT;
         devcfg.pre_cb = nullptr;
 
-        auto ret = spi_bus_initialize( HSPI_HOST, &buscfg, 1 );
-        assert( ret == ESP_OK );
+        auto ret = spi_bus_initialize(_SMARTLEDS_SPI_HOST, &buscfg, _SMARTLEDS_SPI_DMA_CHAN);
+        assert(ret == ESP_OK);
 
-        ret = spi_bus_add_device( HSPI_HOST, &devcfg, &_spi );
-        assert( ret == ESP_OK );
+        ret = spi_bus_add_device(_SMARTLEDS_SPI_HOST, &devcfg, &_spi);
+        assert(ret == ESP_OK);
 
-        std::fill_n( _finalFrame, FINAL_FRAME_SIZE, 0xFFFFFFFF );
+        std::fill_n(_finalFrame, FINAL_FRAME_SIZE, 0xFFFFFFFF);
     }
 
     ~Apa102() {
         // ToDo
     }
 
-    ApaRgb& operator[]( int idx ) {
-        return _firstBuffer[ idx ];
-    }
+    ApaRgb& operator[](int idx) { return _firstBuffer[idx]; }
 
-    const ApaRgb& operator[]( int idx ) const {
-        return _firstBuffer[ idx ];
-    }
+    const ApaRgb& operator[](int idx) const { return _firstBuffer[idx]; }
 
     void show() {
         _buffer = _firstBuffer.get();
@@ -390,93 +300,95 @@ public:
     }
 
     void wait() {
-        for ( int i = 0; i != _transCount; i++ ) {
-            spi_transaction_t *t;
-            spi_device_get_trans_result( _spi, &t, portMAX_DELAY );
+        for (int i = 0; i != _transCount; i++) {
+            spi_transaction_t* t;
+            spi_device_get_trans_result(_spi, &t, portMAX_DELAY);
         }
     }
+
 private:
     void swapBuffers() {
-        if ( _secondBuffer )
-            _firstBuffer.swap( _secondBuffer );
+        if (_secondBuffer)
+            _firstBuffer.swap(_secondBuffer);
     }
 
     void startTransmission() {
-        for ( int i = 0; i != TRANS_COUNT; i++ ) {
-            _transactions[ i ].cmd = 0;
-            _transactions[ i ].addr = 0;
-            _transactions[ i ].flags = 0;
-            _transactions[ i ].rxlength = 0;
-            _transactions[ i ].rx_buffer = nullptr;
+        for (int i = 0; i != TRANS_COUNT; i++) {
+            _transactions[i].cmd = 0;
+            _transactions[i].addr = 0;
+            _transactions[i].flags = 0;
+            _transactions[i].rxlength = 0;
+            _transactions[i].rx_buffer = nullptr;
         }
         // Init frame
-        _transactions[ 0 ].length = 32;
-        _transactions[ 0 ].tx_buffer = &_initFrame;
-        spi_device_queue_trans( _spi, _transactions + 0, portMAX_DELAY );
+        _transactions[0].length = 32;
+        _transactions[0].tx_buffer = &_initFrame;
+        spi_device_queue_trans(_spi, _transactions + 0, portMAX_DELAY);
         // Data
-        _transactions[ 1 ].length = 32 * _count;
-        _transactions[ 1 ].tx_buffer = _buffer;
-        spi_device_queue_trans( _spi, _transactions + 1, portMAX_DELAY );
+        _transactions[1].length = 32 * _count;
+        _transactions[1].tx_buffer = _buffer;
+        spi_device_queue_trans(_spi, _transactions + 1, portMAX_DELAY);
         _transCount = 2;
         // End frame
-        for ( int i = 0; i != 1 + _count / 32 / FINAL_FRAME_SIZE; i++ ) {
-            _transactions[ 2 + i ].length = 32 * FINAL_FRAME_SIZE;
-            _transactions[ 2 + i ].tx_buffer = _finalFrame;
-            spi_device_queue_trans( _spi, _transactions + 2 + i, portMAX_DELAY );
+        for (int i = 0; i != 1 + _count / 32 / FINAL_FRAME_SIZE; i++) {
+            _transactions[2 + i].length = 32 * FINAL_FRAME_SIZE;
+            _transactions[2 + i].tx_buffer = _finalFrame;
+            spi_device_queue_trans(_spi, _transactions + 2 + i, portMAX_DELAY);
             _transCount++;
         }
     }
 
     spi_device_handle_t _spi;
     int _count;
-    std::unique_ptr< ApaRgb[] > _firstBuffer, _secondBuffer;
-    ApaRgb *_buffer;
+    std::unique_ptr<ApaRgb[]> _firstBuffer, _secondBuffer;
+    ApaRgb* _buffer;
 
-    spi_transaction_t _transactions[ TRANS_COUNT ];
+    spi_transaction_t _transactions[TRANS_COUNT];
     int _transCount;
 
     uint32_t _initFrame;
-    uint32_t _finalFrame[ FINAL_FRAME_SIZE ];
+    uint32_t _finalFrame[FINAL_FRAME_SIZE];
 };
 
 class LDP8806 {
 public:
     struct LDP8806_GRB {
 
-        LDP8806_GRB( uint8_t g_7bit = 0, uint8_t r_7bit = 0, uint32_t b_7bit = 0 )
-            : g( g_7bit ), r( r_7bit ), b( b_7bit )
-        {
-        }
+        LDP8806_GRB(uint8_t g_7bit = 0, uint8_t r_7bit = 0, uint32_t b_7bit = 0)
+            : g(g_7bit)
+            , r(r_7bit)
+            , b(b_7bit) {}
 
-        LDP8806_GRB& operator=( const Rgb& o ) {
+        LDP8806_GRB& operator=(const Rgb& o) {
             //Convert 8->7bit colour
-            r = ( o.r * 127 / 256 ) | 0x80;
-            g = ( o.g * 127 / 256 ) | 0x80;
-            b = ( o.b * 127 / 256 ) | 0x80;
+            r = (o.r * 127 / 256) | 0x80;
+            g = (o.g * 127 / 256) | 0x80;
+            b = (o.b * 127 / 256) | 0x80;
             return *this;
         }
 
-        LDP8806_GRB& operator=( const Hsv& o ) {
-            *this = Rgb{ o };
+        LDP8806_GRB& operator=(const Hsv& o) {
+            *this = Rgb { o };
             return *this;
         }
 
         uint8_t g, r, b;
     };
 
-    static const int LED_FRAME_SIZE_BYTES = sizeof( LDP8806_GRB );
+    static const int LED_FRAME_SIZE_BYTES = sizeof(LDP8806_GRB);
     static const int LATCH_FRAME_SIZE_BYTES = 3;
-    static const int TRANS_COUNT_MAX = 20;//Arbitrary, supports up to 600 LED
-
-    LDP8806( int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer, uint32_t clock_speed_hz = 2000000 )
-        : _count( count ),
-          _firstBuffer( new LDP8806_GRB[ count ] ),
-          _secondBuffer( doubleBuffer ? new LDP8806_GRB[ count ] : nullptr ),
-          // one 'latch'/start-of-data mark frame for every 32 leds
-          _latchFrames( ( count + 31 ) / 32 )
-    {
+    static const int TRANS_COUNT_MAX = 20; //Arbitrary, supports up to 600 LED
+
+    LDP8806(
+        int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer, uint32_t clock_speed_hz = 2000000)
+        : _count(count)
+        , _firstBuffer(new LDP8806_GRB[count])
+        , _secondBuffer(doubleBuffer ? new LDP8806_GRB[count] : nullptr)
+        ,
+        // one 'latch'/start-of-data mark frame for every 32 leds
+        _latchFrames((count + 31) / 32) {
         spi_bus_config_t buscfg;
-        memset( &buscfg, 0, sizeof( buscfg ) );
+        memset(&buscfg, 0, sizeof(buscfg));
         buscfg.mosi_io_num = datapin;
         buscfg.miso_io_num = -1;
         buscfg.sclk_io_num = clkpin;
@@ -485,33 +397,29 @@ public:
         buscfg.max_transfer_sz = 65535;
 
         spi_device_interface_config_t devcfg;
-        memset( &devcfg, 0, sizeof( devcfg ) );
+        memset(&devcfg, 0, sizeof(devcfg));
         devcfg.clock_speed_hz = clock_speed_hz;
         devcfg.mode = 0;
         devcfg.spics_io_num = -1;
         devcfg.queue_size = TRANS_COUNT_MAX;
         devcfg.pre_cb = nullptr;
 
-        auto ret = spi_bus_initialize( HSPI_HOST, &buscfg, 1 );
-        assert( ret == ESP_OK );
+        auto ret = spi_bus_initialize(_SMARTLEDS_SPI_HOST, &buscfg, _SMARTLEDS_SPI_DMA_CHAN);
+        assert(ret == ESP_OK);
 
-        ret = spi_bus_add_device( HSPI_HOST, &devcfg, &_spi );
-        assert( ret == ESP_OK );
+        ret = spi_bus_add_device(_SMARTLEDS_SPI_HOST, &devcfg, &_spi);
+        assert(ret == ESP_OK);
 
-        std::fill_n( _latchBuffer, LATCH_FRAME_SIZE_BYTES, 0x0 );
+        std::fill_n(_latchBuffer, LATCH_FRAME_SIZE_BYTES, 0x0);
     }
 
     ~LDP8806() {
         // noop
     }
 
-    LDP8806_GRB& operator[]( int idx ) {
-        return _firstBuffer[ idx ];
-    }
+    LDP8806_GRB& operator[](int idx) { return _firstBuffer[idx]; }
 
-    const LDP8806_GRB& operator[]( int idx ) const {
-        return _firstBuffer[ idx ];
-    }
+    const LDP8806_GRB& operator[](int idx) const { return _firstBuffer[idx]; }
 
     void show() {
         _buffer = _firstBuffer.get();
@@ -520,51 +428,50 @@ public:
     }
 
     void wait() {
-        while ( _transCount-- ) {
-            spi_transaction_t *t;
-            spi_device_get_trans_result( _spi, &t, portMAX_DELAY );
+        while (_transCount--) {
+            spi_transaction_t* t;
+            spi_device_get_trans_result(_spi, &t, portMAX_DELAY);
         }
     }
+
 private:
     void swapBuffers() {
-        if ( _secondBuffer )
-            _firstBuffer.swap( _secondBuffer );
+        if (_secondBuffer)
+            _firstBuffer.swap(_secondBuffer);
     }
 
     void startTransmission() {
         _transCount = 0;
-        for ( int i = 0; i != TRANS_COUNT_MAX; i++ ) {
-            _transactions[ i ].cmd = 0;
-            _transactions[ i ].addr = 0;
-            _transactions[ i ].flags = 0;
-            _transactions[ i ].rxlength = 0;
-            _transactions[ i ].rx_buffer = nullptr;
+        for (int i = 0; i != TRANS_COUNT_MAX; i++) {
+            _transactions[i].cmd = 0;
+            _transactions[i].addr = 0;
+            _transactions[i].flags = 0;
+            _transactions[i].rxlength = 0;
+            _transactions[i].rx_buffer = nullptr;
         }
         // LED Data
-        _transactions[ 0 ].length = ( LED_FRAME_SIZE_BYTES * 8 ) * _count;
-        _transactions[ 0 ].tx_buffer = _buffer;
-        spi_device_queue_trans( _spi, _transactions + _transCount, portMAX_DELAY );
+        _transactions[0].length = (LED_FRAME_SIZE_BYTES * 8) * _count;
+        _transactions[0].tx_buffer = _buffer;
+        spi_device_queue_trans(_spi, _transactions + _transCount, portMAX_DELAY);
         _transCount++;
 
         // 'latch'/start-of-data marker frames
-        for ( int i = 0; i < _latchFrames; i++ ) {
-            _transactions[ _transCount ].length = ( LATCH_FRAME_SIZE_BYTES * 8 );
-            _transactions[ _transCount ].tx_buffer = _latchBuffer;
-            spi_device_queue_trans( _spi, _transactions + _transCount, portMAX_DELAY );
+        for (int i = 0; i < _latchFrames; i++) {
+            _transactions[_transCount].length = (LATCH_FRAME_SIZE_BYTES * 8);
+            _transactions[_transCount].tx_buffer = _latchBuffer;
+            spi_device_queue_trans(_spi, _transactions + _transCount, portMAX_DELAY);
             _transCount++;
         }
     }
 
     spi_device_handle_t _spi;
     int _count;
-    std::unique_ptr< LDP8806_GRB[] > _firstBuffer, _secondBuffer;
-    LDP8806_GRB *_buffer;
+    std::unique_ptr<LDP8806_GRB[]> _firstBuffer, _secondBuffer;
+    LDP8806_GRB* _buffer;
 
-    spi_transaction_t _transactions[ TRANS_COUNT_MAX ];
+    spi_transaction_t _transactions[TRANS_COUNT_MAX];
     int _transCount;
 
     int _latchFrames;
-    uint8_t _latchBuffer[ LATCH_FRAME_SIZE_BYTES ];
+    uint8_t _latchBuffer[LATCH_FRAME_SIZE_BYTES];
 };
-
-#endif //SMARTLEDS_H

+ 20 - 1
code/sdkconfig.defaults

@@ -127,7 +127,25 @@ CONFIG_MQTT_USE_CUSTOM_CONFIG=y
 #CONFIG_MQTT_OUTBOX_EXPIRED_TIMEOUT_MS=5000
 #CONFIG_MQTT_CUSTOM_OUTBOX=y # -> Use custom outbox in components/jomjol_mqtt/mqtt_outbox.h/cpp. If USE_PSRAM is enabled in there, it will save 10 kBytes of internal RAM. How ever it also leads to memory fragmentation, see https://github.com/jomjol/AI-on-the-edge-device/issues/2200
 
-CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=n
+#
+# ESP-Driver:LEDC Configurations
+#
+CONFIG_LEDC_CTRL_FUNC_IN_IRAM=y
+# end of ESP-Driver:LEDC Configurations
+
+#
+# Legacy RMT Driver Configurations
+#
+CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y
+# end of Legacy RMT Driver Configurations
+
+#
+# ESP-Driver:RMT Configurations
+#
+CONFIG_RMT_ISR_IRAM_SAFE=y
+CONFIG_RMT_RECV_FUNC_IN_IRAM=y
+# CONFIG_RMT_ENABLE_DEBUG_LOG is not set
+# end of ESP-Driver:RMT Configurations
 
 CONFIG_CAMERA_CORE0=n
 CONFIG_CAMERA_CORE1=y
@@ -156,6 +174,7 @@ CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=4864
 #CONFIG_FREERTOS_USE_TRACE_FACILITY=1
 #CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y
 #CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y
+CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=n
 
 #force disable HIMEM as not used in default config, can be enabled with [env:esp32cam-dev-himem]
 #free 256kb of internal memory :