diff --git a/lib/NeoESP32RmtHI/include/NeoEsp32RmtHIMethod.h b/lib/NeoESP32RmtHI/include/NeoEsp32RmtHIMethod.h
new file mode 100644
index 000000000..02e066f74
--- /dev/null
+++ b/lib/NeoESP32RmtHI/include/NeoEsp32RmtHIMethod.h
@@ -0,0 +1,469 @@
+/*-------------------------------------------------------------------------
+NeoPixel driver for ESP32 RMTs using High-priority Interrupt
+
+(NB. This cannot be mixed with the non-HI driver.)
+
+Written by Will M. Miles.
+
+I invest time and resources providing this open source code,
+please support me by donating (see https://github.com/Makuna/NeoPixelBus)
+
+-------------------------------------------------------------------------
+This file is part of the Makuna/NeoPixelBus library.
+
+NeoPixelBus is free software: you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation, either version 3 of
+the License, or (at your option) any later version.
+
+NeoPixelBus is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with NeoPixel. If not, see
+.
+-------------------------------------------------------------------------*/
+
+#pragma once
+
+#if defined(ARDUINO_ARCH_ESP32)
+
+// Use the NeoEspRmtSpeed types from the driver-based implementation
+#include
+
+
+namespace NeoEsp32RmtHiMethodDriver {
+ // Install the driver for a specific channel, specifying timing properties
+ esp_err_t Install(rmt_channel_t channel, uint32_t rmtBit0, uint32_t rmtBit1, uint32_t resetDuration);
+
+ // Remove the driver on a specific channel
+ esp_err_t Uninstall(rmt_channel_t channel);
+
+ // Write a buffer of data to a specific channel.
+ // Buffer reference is held until write completes.
+ esp_err_t Write(rmt_channel_t channel, const uint8_t *src, size_t src_size);
+
+ // Wait until transaction is complete.
+ esp_err_t WaitForTxDone(rmt_channel_t channel, TickType_t wait_time);
+};
+
+template class NeoEsp32RmtHIMethodBase
+{
+public:
+ typedef NeoNoSettings SettingsObject;
+
+ NeoEsp32RmtHIMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
+ _sizeData(pixelCount * elementSize + settingsSize),
+ _pin(pin)
+ {
+ construct();
+ }
+
+ NeoEsp32RmtHIMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize, NeoBusChannel channel) :
+ _sizeData(pixelCount* elementSize + settingsSize),
+ _pin(pin),
+ _channel(channel)
+ {
+ construct();
+ }
+
+ ~NeoEsp32RmtHIMethodBase()
+ {
+ // wait until the last send finishes before destructing everything
+ // arbitrary time out of 10 seconds
+ ESP_ERROR_CHECK_WITHOUT_ABORT(NeoEsp32RmtHiMethodDriver::WaitForTxDone(_channel.RmtChannelNumber, 10000 / portTICK_PERIOD_MS));
+
+ ESP_ERROR_CHECK(NeoEsp32RmtHiMethodDriver::Uninstall(_channel.RmtChannelNumber));
+
+ gpio_matrix_out(_pin, SIG_GPIO_OUT_IDX, false, false);
+ pinMode(_pin, INPUT);
+
+ free(_dataEditing);
+ free(_dataSending);
+ }
+
+ bool IsReadyToUpdate() const
+ {
+ return (ESP_OK == ESP_ERROR_CHECK_WITHOUT_ABORT_SILENT_TIMEOUT(NeoEsp32RmtHiMethodDriver::WaitForTxDone(_channel.RmtChannelNumber, 0)));
+ }
+
+ void Initialize()
+ {
+ rmt_config_t config = {};
+
+ config.rmt_mode = RMT_MODE_TX;
+ config.channel = _channel.RmtChannelNumber;
+ config.gpio_num = static_cast(_pin);
+ config.mem_block_num = 1;
+ config.tx_config.loop_en = false;
+
+ config.tx_config.idle_output_en = true;
+ config.tx_config.idle_level = T_SPEED::IdleLevel;
+
+ config.tx_config.carrier_en = false;
+ config.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW;
+
+ config.clk_div = T_SPEED::RmtClockDivider;
+
+ ESP_ERROR_CHECK(rmt_config(&config)); // Uses ESP library
+ ESP_ERROR_CHECK(NeoEsp32RmtHiMethodDriver::Install(_channel.RmtChannelNumber, T_SPEED::RmtBit0, T_SPEED::RmtBit1, T_SPEED::RmtDurationReset));
+ }
+
+ void Update(bool maintainBufferConsistency)
+ {
+ // wait for not actively sending data
+ // this will time out at 10 seconds, an arbitrarily long period of time
+ // and do nothing if this happens
+ if (ESP_OK == ESP_ERROR_CHECK_WITHOUT_ABORT(NeoEsp32RmtHiMethodDriver::WaitForTxDone(_channel.RmtChannelNumber, 10000 / portTICK_PERIOD_MS)))
+ {
+ // now start the RMT transmit with the editing buffer before we swap
+ ESP_ERROR_CHECK_WITHOUT_ABORT(NeoEsp32RmtHiMethodDriver::Write(_channel.RmtChannelNumber, _dataEditing, _sizeData));
+
+ if (maintainBufferConsistency)
+ {
+ // copy editing to sending,
+ // this maintains the contract that "colors present before will
+ // be the same after", otherwise GetPixelColor will be inconsistent
+ memcpy(_dataSending, _dataEditing, _sizeData);
+ }
+
+ // swap so the user can modify without affecting the async operation
+ std::swap(_dataSending, _dataEditing);
+ }
+ }
+
+ bool AlwaysUpdate()
+ {
+ // this method requires update to be called only if changes to buffer
+ return false;
+ }
+
+ bool SwapBuffers()
+ {
+ std::swap(_dataSending, _dataEditing);
+ return true;
+ }
+
+ uint8_t* getData() const
+ {
+ return _dataEditing;
+ };
+
+ size_t getDataSize() const
+ {
+ return _sizeData;
+ }
+
+ void applySettings([[maybe_unused]] const SettingsObject& settings)
+ {
+ }
+
+private:
+ const size_t _sizeData; // Size of '_data*' buffers
+ const uint8_t _pin; // output pin number
+ const T_CHANNEL _channel; // holds instance for multi channel support
+
+ // Holds data stream which include LED color values and other settings as needed
+ uint8_t* _dataEditing; // exposed for get and set
+ uint8_t* _dataSending; // used for async send using RMT
+
+
+ void construct()
+ {
+ _dataEditing = static_cast(malloc(_sizeData));
+ // data cleared later in Begin()
+
+ _dataSending = static_cast(malloc(_sizeData));
+ // no need to initialize it, it gets overwritten on every send
+ }
+};
+
+// normal
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINWs2811Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINWs2812xMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINWs2816Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINWs2805Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINSk6812Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINTm1814Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINTm1829Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINTm1914Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINApa106Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINTx1812Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINGs1903Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHIN800KbpsMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHIN400KbpsMethod;
+typedef NeoEsp32RmtHINWs2805Method NeoEsp32RmtHINWs2814Method;
+
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Ws2811Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Ws2812xMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Ws2816Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Ws2805Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Sk6812Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Tm1814Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Tm1829Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Tm1914Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Apa106Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Tx1812Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Gs1903Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0800KbpsMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0400KbpsMethod;
+typedef NeoEsp32RmtHI0Ws2805Method NeoEsp32RmtHI0Ws2814Method;
+
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Ws2811Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Ws2812xMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Ws2816Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Ws2805Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Sk6812Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Tm1814Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Tm1829Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Tm1914Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Apa106Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Tx1812Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Gs1903Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1800KbpsMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1400KbpsMethod;
+typedef NeoEsp32RmtHI1Ws2805Method NeoEsp32RmtHI1Ws2814Method;
+
+#if !defined(CONFIG_IDF_TARGET_ESP32C3)
+
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Ws2811Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Ws2812xMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Ws2816Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Ws2805Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Sk6812Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Tm1814Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Tm1829Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Tm1914Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Apa106Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Tx1812Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Gs1903Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2800KbpsMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2400KbpsMethod;
+typedef NeoEsp32RmtHI2Ws2805Method NeoEsp32RmtHI2Ws2814Method;
+
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Ws2811Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Ws2812xMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Ws2816Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Ws2805Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Sk6812Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Tm1814Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Tm1829Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Tm1914Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Apa106Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Tx1812Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Gs1903Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3800KbpsMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3400KbpsMethod;
+typedef NeoEsp32RmtHI3Ws2805Method NeoEsp32RmtHI3Ws2814Method;
+
+#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3)
+
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Ws2811Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Ws2812xMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Ws2816Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Ws2805Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Sk6812Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Tm1814Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Tm1829Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Tm1914Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Apa106Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Tx1812Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Gs1903Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4800KbpsMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4400KbpsMethod;
+typedef NeoEsp32RmtHI4Ws2805Method NeoEsp32RmtHI4Ws2814Method;
+
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Ws2811Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Ws2812xMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Ws2816Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Ws2805Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Sk6812Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Tm1814Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Tm1829Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Tm1914Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Apa106Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Tx1812Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Gs1903Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5800KbpsMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5400KbpsMethod;
+typedef NeoEsp32RmtHI5Ws2805Method NeoEsp32RmtHI5Ws2814Method;
+
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Ws2811Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Ws2812xMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Ws2816Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Ws2805Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Sk6812Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Tm1814Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Tm1829Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Tm1914Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Apa106Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Tx1812Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Gs1903Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6800KbpsMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6400KbpsMethod;
+typedef NeoEsp32RmtHI6Ws2805Method NeoEsp32RmtHI6Ws2814Method;
+
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Ws2811Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Ws2812xMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Ws2816Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Ws2805Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Sk6812Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Tm1814Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Tm1829Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Tm1914Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Apa106Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Tx1812Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Gs1903Method;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7800KbpsMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7400KbpsMethod;
+typedef NeoEsp32RmtHI7Ws2805Method NeoEsp32RmtHI7Ws2814Method;
+
+#endif // !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3)
+#endif // !defined(CONFIG_IDF_TARGET_ESP32C3)
+
+// inverted
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINWs2811InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINWs2812xInvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINWs2816InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINWs2805InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINSk6812InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINTm1814InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINTm1829InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINTm1914InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINApa106InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINTx1812InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHINGs1903InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHIN800KbpsInvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHIN400KbpsInvertedMethod;
+typedef NeoEsp32RmtHINWs2805InvertedMethod NeoEsp32RmtHINWs2814InvertedMethod;
+
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Ws2811InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Ws2812xInvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Ws2816InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Ws2805InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Sk6812InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Tm1814InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Tm1829InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Tm1914InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Apa106InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Tx1812InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0Gs1903InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0800KbpsInvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI0400KbpsInvertedMethod;
+typedef NeoEsp32RmtHI0Ws2805InvertedMethod NeoEsp32RmtHI0Ws2814InvertedMethod;
+
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Ws2811InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Ws2812xInvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Ws2816InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Ws2805InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Sk6812InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Tm1814InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Tm1829InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Tm1914InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Apa106InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Tx1812InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1Gs1903InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1800KbpsInvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI1400KbpsInvertedMethod;
+typedef NeoEsp32RmtHI1Ws2805InvertedMethod NeoEsp32RmtHI1Ws2814InvertedMethod;
+
+#if !defined(CONFIG_IDF_TARGET_ESP32C3)
+
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Ws2811InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Ws2812xInvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Ws2816InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Ws2805InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Sk6812InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Tm1814InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Tm1829InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Tm1914InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Apa106InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Tx1812InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2Gs1903InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2800KbpsInvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI2400KbpsInvertedMethod;
+typedef NeoEsp32RmtHI2Ws2805InvertedMethod NeoEsp32RmtHI2Ws2814InvertedMethod;
+
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Ws2811InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Ws2812xInvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Ws2805InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Ws2816InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Sk6812InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Tm1814InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Tm1829InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Tm1914InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Apa106InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Tx1812InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3Gs1903InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3800KbpsInvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI3400KbpsInvertedMethod;
+typedef NeoEsp32RmtHI3Ws2805InvertedMethod NeoEsp32RmtHI3Ws2814InvertedMethod;
+
+#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3)
+
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Ws2811InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Ws2812xInvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Ws2816InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Ws2805InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Sk6812InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Tm1814InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Tm1829InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Tm1914InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Apa106InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Tx1812InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4Gs1903InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4800KbpsInvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI4400KbpsInvertedMethod;
+typedef NeoEsp32RmtHI4Ws2805InvertedMethod NeoEsp32RmtHI4Ws2814InvertedMethod;
+
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Ws2811InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Ws2812xInvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Ws2816InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Ws2805InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Sk6812InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Tm1814InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Tm1829InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Tm1914InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Apa106InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Tx1812InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5Gs1903InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5800KbpsInvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI5400KbpsInvertedMethod;
+typedef NeoEsp32RmtHI5Ws2805InvertedMethod NeoEsp32RmtHI5Ws2814InvertedMethod;
+
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Ws2811InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Ws2812xInvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Ws2816InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Ws2805InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Sk6812InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Tm1814InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Tm1829InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Tm1914InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Apa106InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Tx1812InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6Gs1903InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6800KbpsInvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI6400KbpsInvertedMethod;
+typedef NeoEsp32RmtHI6Ws2805InvertedMethod NeoEsp32RmtHI6Ws2814InvertedMethod;
+
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Ws2811InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Ws2812xInvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Ws2816InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Ws2805InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Sk6812InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Tm1814InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Tm1829InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Tm1914InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Apa106InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Tx1812InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7Gs1903InvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7800KbpsInvertedMethod;
+typedef NeoEsp32RmtHIMethodBase NeoEsp32RmtHI7400KbpsInvertedMethod;
+typedef NeoEsp32RmtHI7Ws2805InvertedMethod NeoEsp32RmtHI7Ws2814InvertedMethod;
+
+#endif // !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3)
+#endif // !defined(CONFIG_IDF_TARGET_ESP32C3)
+
+#endif
diff --git a/lib/NeoESP32RmtHI/library.json b/lib/NeoESP32RmtHI/library.json
new file mode 100644
index 000000000..0608e59e1
--- /dev/null
+++ b/lib/NeoESP32RmtHI/library.json
@@ -0,0 +1,12 @@
+{
+ "name": "NeoESP32RmtHI",
+ "build": { "libArchive": false },
+ "platforms": ["espressif32"],
+ "dependencies": [
+ {
+ "owner": "makuna",
+ "name": "NeoPixelBus",
+ "version": "^2.8.3"
+ }
+ ]
+}
diff --git a/lib/NeoESP32RmtHI/src/NeoEsp32RmtHI.S b/lib/NeoESP32RmtHI/src/NeoEsp32RmtHI.S
new file mode 100644
index 000000000..0c60d2ebc
--- /dev/null
+++ b/lib/NeoESP32RmtHI/src/NeoEsp32RmtHI.S
@@ -0,0 +1,263 @@
+/* RMT ISR shim
+ * Bridges from a high-level interrupt to the C++ code.
+ *
+ * This code is largely derived from Espressif's 'hli_vector.S' Bluetooth ISR.
+ *
+ */
+
+#if defined(__XTENSA__) && defined(ESP32) && !defined(CONFIG_BTDM_CTRL_HLI)
+
+#include
+#include "sdkconfig.h"
+#include "soc/soc.h"
+
+/* If the Bluetooth driver has hooked the high-priority interrupt, we piggyback on it and don't need this. */
+#ifndef CONFIG_BTDM_CTRL_HLI
+
+/*
+ Select interrupt based on system check level
+ - Base ESP32: could be 4 or 5, depends on platform config
+ - S2: 5
+ - S3: 5
+*/
+
+#if CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5
+/* Use level 4 */
+#define RFI_X 4
+#define xt_highintx xt_highint4
+#else /* !CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */
+/* Use level 5 */
+#define RFI_X 5
+#define xt_highintx xt_highint5
+#endif /* CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */
+
+// Register map, based on interrupt level
+#define EPC_X (EPC + RFI_X)
+#define EXCSAVE_X (EXCSAVE + RFI_X)
+
+// The sp mnemonic is used all over in ESP's assembly, though I'm not sure where it's expected to be defined?
+#define sp a1
+
+/* Interrupt stack size, for C code. */
+#define RMT_INTR_STACK_SIZE 512
+
+/* Save area for the CPU state:
+ * - 64 words for the general purpose registers
+ * - 7 words for some of the special registers:
+ * - WINDOWBASE, WINDOWSTART — only WINDOWSTART is truly needed
+ * - SAR, LBEG, LEND, LCOUNT — since the C code might use these
+ * - EPC1 — since the C code might cause window overflow exceptions
+ * This is not laid out as standard exception frame structure
+ * for simplicity of the save/restore code.
+ */
+#define REG_FILE_SIZE (64 * 4)
+#define SPECREG_OFFSET REG_FILE_SIZE
+#define SPECREG_SIZE (7 * 4)
+#define REG_SAVE_AREA_SIZE (SPECREG_OFFSET + SPECREG_SIZE)
+
+ .data
+_rmt_intr_stack:
+ .space RMT_INTR_STACK_SIZE
+_rmt_save_ctx:
+ .space REG_SAVE_AREA_SIZE
+
+ .section .iram1,"ax"
+ .global xt_highintx
+ .type xt_highintx,@function
+ .align 4
+
+xt_highintx:
+
+ movi a0, _rmt_save_ctx
+ /* save 4 lower registers */
+ s32i a1, a0, 4
+ s32i a2, a0, 8
+ s32i a3, a0, 12
+ rsr a2, EXCSAVE_X /* holds the value of a0 */
+ s32i a2, a0, 0
+
+ /* Save special registers */
+ addi a0, a0, SPECREG_OFFSET
+ rsr a2, WINDOWBASE
+ s32i a2, a0, 0
+ rsr a2, WINDOWSTART
+ s32i a2, a0, 4
+ rsr a2, SAR
+ s32i a2, a0, 8
+ #if XCHAL_HAVE_LOOPS
+ rsr a2, LBEG
+ s32i a2, a0, 12
+ rsr a2, LEND
+ s32i a2, a0, 16
+ rsr a2, LCOUNT
+ s32i a2, a0, 20
+ #endif
+ rsr a2, EPC1
+ s32i a2, a0, 24
+
+ /* disable exception mode, window overflow */
+ movi a0, PS_INTLEVEL(RFI_X+1) | PS_EXCM
+ wsr a0, PS
+ rsync
+
+ /* Save the remaining physical registers.
+ * 4 registers are already saved, which leaves 60 registers to save.
+ * (FIXME: consider the case when the CPU is configured with physical 32 registers)
+ * These 60 registers are saved in 5 iterations, 12 registers at a time.
+ */
+ movi a1, 5
+ movi a3, _rmt_save_ctx + 4 * 4
+
+ /* This is repeated 5 times, each time the window is shifted by 12 registers.
+ * We come here with a1 = downcounter, a3 = save pointer, a2 and a0 unused.
+ */
+1:
+ s32i a4, a3, 0
+ s32i a5, a3, 4
+ s32i a6, a3, 8
+ s32i a7, a3, 12
+ s32i a8, a3, 16
+ s32i a9, a3, 20
+ s32i a10, a3, 24
+ s32i a11, a3, 28
+ s32i a12, a3, 32
+ s32i a13, a3, 36
+ s32i a14, a3, 40
+ s32i a15, a3, 44
+
+ /* We are about to rotate the window, so that a12-a15 will become the new a0-a3.
+ * Copy a0-a3 to a12-15 to still have access to these values.
+ * At the same time we can decrement the counter and adjust the save area pointer
+ */
+
+ /* a0 is constant (_rmt_save_ctx), no need to copy */
+ addi a13, a1, -1 /* copy and decrement the downcounter */
+ /* a2 is scratch so no need to copy */
+ addi a15, a3, 48 /* copy and adjust the save area pointer */
+ beqz a13, 2f /* have saved all registers ? */
+ rotw 3 /* rotate the window and go back */
+ j 1b
+
+ /* the loop is complete */
+2:
+ rotw 4 /* this brings us back to the original window */
+ /* a0 still points to _rmt_save_ctx */
+
+ /* Can clear WINDOWSTART now, all registers are saved */
+ rsr a2, WINDOWBASE
+ /* WINDOWSTART = (1 << WINDOWBASE) */
+ movi a3, 1
+ ssl a2
+ sll a3, a3
+ wsr a3, WINDOWSTART
+
+_highint_stack_switch:
+ movi a0, 0
+ movi sp, _rmt_intr_stack + RMT_INTR_STACK_SIZE - 16
+ s32e a0, sp, -12 /* For GDB: set null SP */
+ s32e a0, sp, -16 /* For GDB: set null PC */
+ movi a0, _highint_stack_switch /* For GDB: cosmetics, for the frame where stack switch happened */
+
+ /* Set up PS for C, disable all interrupts except NMI and debug, and clear EXCM. */
+ movi a6, PS_INTLEVEL(RFI_X) | PS_UM | PS_WOE
+ wsr a6, PS
+ rsync
+
+ /* Call C handler */
+ mov a6, sp
+ call4 NeoEsp32RmtMethodIsr
+
+ l32e sp, sp, -12 /* switch back to the original stack */
+
+ /* Done with C handler; re-enable exception mode, disabling window overflow */
+ movi a2, PS_INTLEVEL(RFI_X+1) | PS_EXCM /* TOCHECK */
+ wsr a2, PS
+ rsync
+
+ /* Restore the special registers.
+ * WINDOWSTART will be restored near the end.
+ */
+ movi a0, _rmt_save_ctx + SPECREG_OFFSET
+ l32i a2, a0, 8
+ wsr a2, SAR
+ #if XCHAL_HAVE_LOOPS
+ l32i a2, a0, 12
+ wsr a2, LBEG
+ l32i a2, a0, 16
+ wsr a2, LEND
+ l32i a2, a0, 20
+ wsr a2, LCOUNT
+ #endif
+ l32i a2, a0, 24
+ wsr a2, EPC1
+
+ /* Restoring the physical registers.
+ * This is the reverse to the saving process above.
+ */
+
+ /* Rotate back to the final window, then start loading 12 registers at a time,
+ * in 5 iterations.
+ * Again, a1 is the downcounter and a3 is the save area pointer.
+ * After each rotation, a1 and a3 are copied from a13 and a15.
+ * To simplify the loop, we put the initial values into a13 and a15.
+ */
+ rotw -4
+ movi a15, _rmt_save_ctx + 64 * 4 /* point to the end of the save area */
+ movi a13, 5
+
+1:
+ /* Copy a1 and a3 from their previous location,
+ * at the same time decrementing and adjusting the save area pointer.
+ */
+ addi a1, a13, -1
+ addi a3, a15, -48
+
+ /* Load 12 registers */
+ l32i a4, a3, 0
+ l32i a5, a3, 4
+ l32i a6, a3, 8
+ l32i a7, a3, 12
+ l32i a8, a3, 16
+ l32i a9, a3, 20
+ l32i a10, a3, 24
+ l32i a11, a3, 28 /* ensure PS and EPC written */
+ l32i a12, a3, 32
+ l32i a13, a3, 36
+ l32i a14, a3, 40
+ l32i a15, a3, 44
+
+ /* Done with the loop? */
+ beqz a1, 2f
+ /* If no, rotate the window and repeat */
+ rotw -3
+ j 1b
+
+2:
+ /* Done with the loop. Only 4 registers (a0-a3 in the original window) remain
+ * to be restored. Also need to restore WINDOWSTART, since all the general
+ * registers are now in place.
+ */
+ movi a0, _rmt_save_ctx
+
+ l32i a2, a0, SPECREG_OFFSET + 4
+ wsr a2, WINDOWSTART
+
+ l32i a1, a0, 4
+ l32i a2, a0, 8
+ l32i a3, a0, 12
+ rsr a0, EXCSAVE_X /* holds the value of a0 before the interrupt handler */
+
+ /* Return from the interrupt, restoring PS from EPS_X */
+ rfi RFI_X
+
+
+/* The linker has no reason to link in this file; all symbols it exports are already defined
+ (weakly!) in the default int handler. Define a symbol here so we can use it to have the
+ linker inspect this anyway. */
+
+ .global ld_include_hli_vectors_rmt
+ld_include_hli_vectors_rmt:
+
+
+#endif // CONFIG_BTDM_CTRL_HLI
+#endif // XTensa
\ No newline at end of file
diff --git a/lib/NeoESP32RmtHI/src/NeoEsp32RmtHIMethod.cpp b/lib/NeoESP32RmtHI/src/NeoEsp32RmtHIMethod.cpp
new file mode 100644
index 000000000..8353201f0
--- /dev/null
+++ b/lib/NeoESP32RmtHI/src/NeoEsp32RmtHIMethod.cpp
@@ -0,0 +1,507 @@
+/*-------------------------------------------------------------------------
+NeoPixel library helper functions for Esp32.
+
+A BIG thanks to Andreas Merkle for the investigation and implementation of
+a workaround to the GCC bug that drops method attributes from template methods
+
+Written by Michael C. Miller.
+
+I invest time and resources providing this open source code,
+please support me by donating (see https://github.com/Makuna/NeoPixelBus)
+
+-------------------------------------------------------------------------
+This file is part of the Makuna/NeoPixelBus library.
+
+NeoPixelBus is free software: you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation, either version 3 of
+the License, or (at your option) any later version.
+
+NeoPixelBus is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with NeoPixel. If not, see
+.
+-------------------------------------------------------------------------*/
+
+#include
+
+#if defined(ARDUINO_ARCH_ESP32)
+
+#include
+#include "esp_idf_version.h"
+#include "NeoEsp32RmtHIMethod.h"
+#include "soc/soc.h"
+#include "soc/rmt_reg.h"
+
+#ifdef __riscv
+#include "riscv/interrupt.h"
+#endif
+
+
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
+#include "hal/rmt_ll.h"
+#else
+/* Shims for older ESP-IDF v3; we can safely assume original ESP32 */
+#include "soc/rmt_struct.h"
+
+// Selected RMT API functions borrowed from ESP-IDF v4.4.8
+// components/hal/esp32/include/hal/rmt_ll.h
+// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+__attribute__((always_inline))
+static inline void rmt_ll_tx_reset_pointer(rmt_dev_t *dev, uint32_t channel)
+{
+ dev->conf_ch[channel].conf1.mem_rd_rst = 1;
+ dev->conf_ch[channel].conf1.mem_rd_rst = 0;
+}
+
+__attribute__((always_inline))
+static inline void rmt_ll_tx_start(rmt_dev_t *dev, uint32_t channel)
+{
+ dev->conf_ch[channel].conf1.tx_start = 1;
+}
+
+__attribute__((always_inline))
+static inline void rmt_ll_tx_stop(rmt_dev_t *dev, uint32_t channel)
+{
+ RMTMEM.chan[channel].data32[0].val = 0;
+ dev->conf_ch[channel].conf1.tx_start = 0;
+ dev->conf_ch[channel].conf1.mem_rd_rst = 1;
+ dev->conf_ch[channel].conf1.mem_rd_rst = 0;
+}
+
+__attribute__((always_inline))
+static inline void rmt_ll_tx_enable_pingpong(rmt_dev_t *dev, uint32_t channel, bool enable)
+{
+ dev->apb_conf.mem_tx_wrap_en = enable;
+}
+
+__attribute__((always_inline))
+static inline void rmt_ll_tx_enable_loop(rmt_dev_t *dev, uint32_t channel, bool enable)
+{
+ dev->conf_ch[channel].conf1.tx_conti_mode = enable;
+}
+
+__attribute__((always_inline))
+static inline uint32_t rmt_ll_tx_get_channel_status(rmt_dev_t *dev, uint32_t channel)
+{
+ return dev->status_ch[channel];
+}
+
+__attribute__((always_inline))
+static inline void rmt_ll_tx_set_limit(rmt_dev_t *dev, uint32_t channel, uint32_t limit)
+{
+ dev->tx_lim_ch[channel].limit = limit;
+}
+
+__attribute__((always_inline))
+static inline void rmt_ll_enable_interrupt(rmt_dev_t *dev, uint32_t mask, bool enable)
+{
+ if (enable) {
+ dev->int_ena.val |= mask;
+ } else {
+ dev->int_ena.val &= ~mask;
+ }
+}
+
+__attribute__((always_inline))
+static inline void rmt_ll_enable_tx_end_interrupt(rmt_dev_t *dev, uint32_t channel, bool enable)
+{
+ dev->int_ena.val &= ~(1 << (channel * 3));
+ dev->int_ena.val |= (enable << (channel * 3));
+}
+
+__attribute__((always_inline))
+static inline void rmt_ll_enable_tx_err_interrupt(rmt_dev_t *dev, uint32_t channel, bool enable)
+{
+ dev->int_ena.val &= ~(1 << (channel * 3 + 2));
+ dev->int_ena.val |= (enable << (channel * 3 + 2));
+}
+
+__attribute__((always_inline))
+static inline void rmt_ll_enable_tx_thres_interrupt(rmt_dev_t *dev, uint32_t channel, bool enable)
+{
+ dev->int_ena.val &= ~(1 << (channel + 24));
+ dev->int_ena.val |= (enable << (channel + 24));
+}
+
+__attribute__((always_inline))
+static inline void rmt_ll_clear_tx_end_interrupt(rmt_dev_t *dev, uint32_t channel)
+{
+ dev->int_clr.val = (1 << (channel * 3));
+}
+
+__attribute__((always_inline))
+static inline void rmt_ll_clear_tx_err_interrupt(rmt_dev_t *dev, uint32_t channel)
+{
+ dev->int_clr.val = (1 << (channel * 3 + 2));
+}
+
+__attribute__((always_inline))
+static inline void rmt_ll_clear_tx_thres_interrupt(rmt_dev_t *dev, uint32_t channel)
+{
+ dev->int_clr.val = (1 << (channel + 24));
+}
+
+
+__attribute__((always_inline))
+static inline uint32_t rmt_ll_get_tx_thres_interrupt_status(rmt_dev_t *dev)
+{
+ uint32_t status = dev->int_st.val;
+ return (status & 0xFF000000) >> 24;
+}
+#endif
+
+
+// *********************************
+// Select method for binding interrupt
+//
+// - If the Bluetooth driver has registered a high-level interrupt, piggyback on that API
+// - If we're on a modern core, allocate the interrupt with the API (old cores are bugged)
+// - Otherwise use the low-level hardware API to manually bind the interrupt
+
+
+#if defined(CONFIG_BTDM_CTRL_HLI)
+// Espressif's bluetooth driver offers a helpful sharing layer; bring in the interrupt management calls
+#include "hal/interrupt_controller_hal.h"
+extern "C" esp_err_t hli_intr_register(intr_handler_t handler, void* arg, uint32_t intr_reg, uint32_t intr_mask);
+
+#else /* !CONFIG_BTDM_CTRL_HLI*/
+
+// Declare the our high-priority ISR handler
+extern "C" void ld_include_hli_vectors_rmt(); // an object with an address, but no space
+
+#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3)
+#include "soc/periph_defs.h"
+#endif
+
+// Select level flag
+#if defined(__riscv)
+// RISCV chips don't block interrupts while scheduling; all we need to do is be higher than the WiFi ISR
+#define INT_LEVEL_FLAG ESP_INTR_FLAG_LEVEL3
+#elif defined(CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5)
+#define INT_LEVEL_FLAG ESP_INTR_FLAG_LEVEL4
+#else
+#define INT_LEVEL_FLAG ESP_INTR_FLAG_LEVEL5
+#endif
+
+// ESP-IDF v3 cannot enable high priority interrupts through the API at all;
+// and ESP-IDF v4 on XTensa cannot enable Level 5 due to incorrect interrupt descriptor tables
+#if !defined(__XTENSA__) || (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) || ((ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) && CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5))
+#define NEOESP32_RMT_CAN_USE_INTR_ALLOC
+
+// XTensa cores require the assembly bridge
+#ifdef __XTENSA__
+#define HI_IRQ_HANDLER nullptr
+#define HI_IRQ_HANDLER_ARG ld_include_hli_vectors_rmt
+#else
+#define HI_IRQ_HANDLER NeoEsp32RmtMethodIsr
+#define HI_IRQ_HANDLER_ARG nullptr
+#endif
+
+#else
+/* !CONFIG_BTDM_CTRL_HLI && !NEOESP32_RMT_CAN_USE_INTR_ALLOC */
+// This is the index of the LV5 interrupt vector - see interrupt descriptor table in idf components/hal/esp32/interrupt_descriptor_table.c
+#define ESP32_LV5_IRQ_INDEX 26
+
+#endif /* NEOESP32_RMT_CAN_USE_INTR_ALLOC */
+#endif /* CONFIG_BTDM_CTRL_HLI */
+
+
+// RMT driver implementation
+struct NeoEsp32RmtHIChannelState {
+ uint32_t rmtBit0, rmtBit1;
+ uint32_t resetDuration;
+
+ const byte* txDataStart; // data array
+ const byte* txDataEnd; // one past end
+ const byte* txDataCurrent; // current location
+ size_t rmtOffset;
+};
+
+// Global variables
+#if defined(NEOESP32_RMT_CAN_USE_INTR_ALLOC)
+static intr_handle_t isrHandle = nullptr;
+#endif
+
+static NeoEsp32RmtHIChannelState** driverState = nullptr;
+constexpr size_t rmtBatchSize = RMT_MEM_ITEM_NUM / 2;
+
+// Fill the RMT buffer memory
+// This is implemented using many arguments instead of passing the structure object to ensure we do only one lookup
+// All the arguments are passed in registers, so they don't need to be looked up again
+static void IRAM_ATTR RmtFillBuffer(uint8_t channel, const byte** src_ptr, const byte* end, uint32_t bit0, uint32_t bit1, size_t* offset_ptr, size_t reserve) {
+ // We assume that (rmtToWrite % 8) == 0
+ size_t rmtToWrite = rmtBatchSize - reserve;
+ rmt_item32_t* dest =(rmt_item32_t*) &RMTMEM.chan[channel].data32[*offset_ptr + reserve]; // write directly in to RMT memory
+ const byte* psrc = *src_ptr;
+
+ *offset_ptr ^= rmtBatchSize;
+
+ if (psrc != end) {
+ while (rmtToWrite > 0) {
+ uint8_t data = *psrc;
+ for (uint8_t bit = 0; bit < 8; bit++)
+ {
+ dest->val = (data & 0x80) ? bit1 : bit0;
+ dest++;
+ data <<= 1;
+ }
+ rmtToWrite -= 8;
+ psrc++;
+
+ if (psrc == end) {
+ break;
+ }
+ }
+
+ *src_ptr = psrc;
+ }
+
+ if (rmtToWrite > 0) {
+ // Add end event
+ rmt_item32_t bit0_val = {{.val = bit0 }};
+ *dest = rmt_item32_t {{{ .duration0 = 0, .level0 = bit0_val.level1, .duration1 = 0, .level1 = bit0_val.level1 }}};
+ }
+}
+
+static void IRAM_ATTR RmtStartWrite(uint8_t channel, NeoEsp32RmtHIChannelState& state) {
+ // Reset context state
+ state.rmtOffset = 0;
+
+ // Fill the first part of the buffer with a reset event
+ // FUTURE: we could do timing analysis with the last interrupt on this channel
+ // Use 8 words to stay aligned with the buffer fill logic
+ rmt_item32_t bit0_val = {{.val = state.rmtBit0 }};
+ rmt_item32_t fill = {{{ .duration0 = 100, .level0 = bit0_val.level1, .duration1 = 100, .level1 = bit0_val.level1 }}};
+ rmt_item32_t* dest = (rmt_item32_t*) &RMTMEM.chan[channel].data32[0];
+ for (auto i = 0; i < 7; ++i) dest[i] = fill;
+ fill.duration1 = state.resetDuration > 1400 ? (state.resetDuration - 1400) : 100;
+ dest[7] = fill;
+
+ // Fill the remaining buffer with real data
+ RmtFillBuffer(channel, &state.txDataCurrent, state.txDataEnd, state.rmtBit0, state.rmtBit1, &state.rmtOffset, 8);
+ RmtFillBuffer(channel, &state.txDataCurrent, state.txDataEnd, state.rmtBit0, state.rmtBit1, &state.rmtOffset, 0);
+
+ // Start operation
+ rmt_ll_clear_tx_thres_interrupt(&RMT, channel);
+ rmt_ll_tx_reset_pointer(&RMT, channel);
+ rmt_ll_tx_start(&RMT, channel);
+}
+
+extern "C" void IRAM_ATTR NeoEsp32RmtMethodIsr(void *arg) {
+ // Tx threshold interrupt
+ uint32_t status = rmt_ll_get_tx_thres_interrupt_status(&RMT);
+ while (status) {
+ uint8_t channel = __builtin_ffs(status) - 1;
+ if (driverState[channel]) {
+ // Normal case
+ NeoEsp32RmtHIChannelState& state = *driverState[channel];
+ RmtFillBuffer(channel, &state.txDataCurrent, state.txDataEnd, state.rmtBit0, state.rmtBit1, &state.rmtOffset, 0);
+ } else {
+ // Danger - another driver got invoked?
+ rmt_ll_tx_stop(&RMT, channel);
+ }
+ rmt_ll_clear_tx_thres_interrupt(&RMT, channel);
+ status = rmt_ll_get_tx_thres_interrupt_status(&RMT);
+ }
+};
+
+// Wrapper around the register analysis defines
+// For all currently supported chips, this is constant for all channels; but this is not true of *all* ESP32
+static inline bool _RmtStatusIsTransmitting(rmt_channel_t channel, uint32_t status) {
+ uint32_t v;
+ switch(channel) {
+#ifdef RMT_STATE_CH0
+ case 0: v = (status >> RMT_STATE_CH0_S) & RMT_STATE_CH0_V; break;
+#endif
+#ifdef RMT_STATE_CH1
+ case 1: v = (status >> RMT_STATE_CH1_S) & RMT_STATE_CH1_V; break;
+#endif
+#ifdef RMT_STATE_CH2
+ case 2: v = (status >> RMT_STATE_CH2_S) & RMT_STATE_CH2_V; break;
+#endif
+#ifdef RMT_STATE_CH3
+ case 3: v = (status >> RMT_STATE_CH3_S) & RMT_STATE_CH3_V; break;
+#endif
+#ifdef RMT_STATE_CH4
+ case 4: v = (status >> RMT_STATE_CH4_S) & RMT_STATE_CH4_V; break;
+#endif
+#ifdef RMT_STATE_CH5
+ case 5: v = (status >> RMT_STATE_CH5_S) & RMT_STATE_CH5_V; break;
+#endif
+#ifdef RMT_STATE_CH6
+ case 6: v = (status >> RMT_STATE_CH6_S) & RMT_STATE_CH6_V; break;
+#endif
+#ifdef RMT_STATE_CH7
+ case 7: v = (status >> RMT_STATE_CH7_S) & RMT_STATE_CH7_V; break;
+#endif
+ default: v = 0;
+ }
+
+ return v != 0;
+}
+
+
+esp_err_t NeoEsp32RmtHiMethodDriver::Install(rmt_channel_t channel, uint32_t rmtBit0, uint32_t rmtBit1, uint32_t reset) {
+ // Validate channel number
+ if (channel >= RMT_CHANNEL_MAX) {
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ esp_err_t err = ESP_OK;
+ if (!driverState) {
+ // First time init
+ driverState = reinterpret_cast(heap_caps_calloc(RMT_CHANNEL_MAX, sizeof(NeoEsp32RmtHIChannelState*), MALLOC_CAP_INTERNAL));
+ if (!driverState) return ESP_ERR_NO_MEM;
+
+ // Ensure all interrupts are cleared before binding
+ RMT.int_ena.val = 0;
+ RMT.int_clr.val = 0xFFFFFFFF;
+
+ // Bind interrupt handler
+#if defined(CONFIG_BTDM_CTRL_HLI)
+ // Bluetooth driver has taken the empty high-priority interrupt. Fortunately, it allows us to
+ // hook up another handler.
+ err = hli_intr_register(NeoEsp32RmtMethodIsr, nullptr, (uintptr_t) &RMT.int_st, 0xFF000000);
+ // 25 is the magic number of the bluetooth ISR on ESP32 - see soc/soc.h.
+ intr_matrix_set(cpu_hal_get_core_id(), ETS_RMT_INTR_SOURCE, 25);
+ intr_cntrl_ll_enable_interrupts(1<<25);
+#elif defined(NEOESP32_RMT_CAN_USE_INTR_ALLOC)
+ // Use the platform code to allocate the interrupt
+ // If we need the additional assembly bridge, we pass it as the "arg" to the IDF so it gets linked in
+ err = esp_intr_alloc(ETS_RMT_INTR_SOURCE, INT_LEVEL_FLAG | ESP_INTR_FLAG_IRAM, HI_IRQ_HANDLER, (void*) HI_IRQ_HANDLER_ARG, &isrHandle);
+ //err = ESP_ERR_NOT_FINISHED;
+#else
+ // Broken IDF API does not allow us to reserve the interrupt; do it manually
+ static volatile const void* __attribute__((used)) pleaseLinkAssembly = (void*) ld_include_hli_vectors_rmt;
+ intr_matrix_set(xPortGetCoreID(), ETS_RMT_INTR_SOURCE, ESP32_LV5_IRQ_INDEX);
+ ESP_INTR_ENABLE(ESP32_LV5_IRQ_INDEX);
+#endif
+
+ if (err != ESP_OK) {
+ heap_caps_free(driverState);
+ driverState = nullptr;
+ return err;
+ }
+ }
+
+ if (driverState[channel] != nullptr) {
+ return ESP_ERR_INVALID_STATE; // already in use
+ }
+
+ NeoEsp32RmtHIChannelState* state = reinterpret_cast(heap_caps_calloc(1, sizeof(NeoEsp32RmtHIChannelState), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT));
+ if (state == nullptr) {
+ return ESP_ERR_NO_MEM;
+ }
+
+ // Store timing information
+ state->rmtBit0 = rmtBit0;
+ state->rmtBit1 = rmtBit1;
+ state->resetDuration = reset;
+
+ // Initialize hardware
+ rmt_ll_tx_stop(&RMT, channel);
+ rmt_ll_tx_reset_pointer(&RMT, channel);
+ rmt_ll_enable_tx_err_interrupt(&RMT, channel, false);
+ rmt_ll_enable_tx_end_interrupt(&RMT, channel, false);
+ rmt_ll_enable_tx_thres_interrupt(&RMT, channel, false);
+ rmt_ll_clear_tx_err_interrupt(&RMT, channel);
+ rmt_ll_clear_tx_end_interrupt(&RMT, channel);
+ rmt_ll_clear_tx_thres_interrupt(&RMT, channel);
+
+ rmt_ll_tx_enable_loop(&RMT, channel, false);
+ rmt_ll_tx_enable_pingpong(&RMT, channel, true);
+ rmt_ll_tx_set_limit(&RMT, channel, rmtBatchSize);
+
+ driverState[channel] = state;
+
+ rmt_ll_enable_tx_thres_interrupt(&RMT, channel, true);
+
+ return err;
+}
+
+esp_err_t NeoEsp32RmtHiMethodDriver::Uninstall(rmt_channel_t channel) {
+ if ((channel >= RMT_CHANNEL_MAX) || !driverState || !driverState[channel]) return ESP_ERR_INVALID_ARG;
+
+ NeoEsp32RmtHIChannelState* state = driverState[channel];
+
+ WaitForTxDone(channel, 10000 / portTICK_PERIOD_MS);
+
+ // Done or not, we're out of here
+ rmt_ll_tx_stop(&RMT, channel);
+ rmt_ll_enable_tx_thres_interrupt(&RMT, channel, false);
+ driverState[channel] = nullptr;
+ heap_caps_free(state);
+
+#if !defined(CONFIG_BTDM_CTRL_HLI) /* Cannot unbind from bluetooth ISR */
+ // Turn off the driver ISR and release global state if none are left
+ for (uint8_t channelIndex = 0; channelIndex < RMT_CHANNEL_MAX; ++channelIndex) {
+ if (driverState[channelIndex]) return ESP_OK; // done
+ }
+
+#if defined(NEOESP32_RMT_CAN_USE_INTR_ALLOC)
+ esp_intr_free(isrHandle);
+#else
+ ESP_INTR_DISABLE(ESP32_LV5_IRQ_INDEX);
+#endif
+
+ heap_caps_free(driverState);
+ driverState = nullptr;
+#endif /* !defined(CONFIG_BTDM_CTRL_HLI) */
+
+ return ESP_OK;
+}
+
+esp_err_t NeoEsp32RmtHiMethodDriver::Write(rmt_channel_t channel, const uint8_t *src, size_t src_size) {
+ if ((channel >= RMT_CHANNEL_MAX) || !driverState || !driverState[channel]) return ESP_ERR_INVALID_ARG;
+
+ NeoEsp32RmtHIChannelState& state = *driverState[channel];
+ esp_err_t result = WaitForTxDone(channel, 10000 / portTICK_PERIOD_MS);
+
+ if (result == ESP_OK) {
+ state.txDataStart = src;
+ state.txDataCurrent = src;
+ state.txDataEnd = src + src_size;
+ RmtStartWrite(channel, state);
+ }
+ return result;
+}
+
+esp_err_t NeoEsp32RmtHiMethodDriver::WaitForTxDone(rmt_channel_t channel, TickType_t wait_time) {
+ if ((channel >= RMT_CHANNEL_MAX) || !driverState || !driverState[channel]) return ESP_ERR_INVALID_ARG;
+
+ NeoEsp32RmtHIChannelState& state = *driverState[channel];
+ // yield-wait until wait_time
+ esp_err_t rv = ESP_OK;
+ uint32_t status;
+ while(1) {
+ status = rmt_ll_tx_get_channel_status(&RMT, channel);
+ if (!_RmtStatusIsTransmitting(channel, status)) break;
+ if (wait_time == 0) { rv = ESP_ERR_TIMEOUT; break; };
+
+ TickType_t sleep = std::min(wait_time, (TickType_t) 5);
+ vTaskDelay(sleep);
+ wait_time -= sleep;
+ };
+
+ return rv;
+}
+
+#endif
\ No newline at end of file
diff --git a/platformio.ini b/platformio.ini
index 5aac5cb03..e75749024 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -139,7 +139,6 @@ lib_deps =
fastled/FastLED @ 3.6.0
IRremoteESP8266 @ 2.8.2
makuna/NeoPixelBus @ 2.8.3
- #https://github.com/makuna/NeoPixelBus.git#CoreShaderBeta
https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.4.2
# for I2C interface
;Wire
diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h
index 5d8f306f5..540004340 100644
--- a/wled00/bus_wrapper.h
+++ b/wled00/bus_wrapper.h
@@ -244,53 +244,61 @@
typedef NeoEsp32I2s1Tm1914Method X1Tm1914Method;
#endif
+// RMT driver selection
+#if !defined(WLED_USE_SHARED_RMT) && !defined(__riscv)
+#include
+#define NeoEsp32RmtMethod(x) NeoEsp32RmtHIN ## x ## Method
+#else
+#define NeoEsp32RmtMethod(x) NeoEsp32RmtN ## x ## Method
+#endif
+
//RGB
-#define B_32_RN_NEO_3 NeoPixelBusLg // ESP32, S2, S3, C3
+#define B_32_RN_NEO_3 NeoPixelBusLg // ESP32, S2, S3, C3
//#define B_32_IN_NEO_3 NeoPixelBusLg // ESP32 (dynamic I2S selection)
#define B_32_I2_NEO_3 NeoPixelBusLg // ESP32, S2, S3 (automatic I2S selection, see typedef above)
#define B_32_IP_NEO_3 NeoPixelBusLg // parallel I2S (ESP32, S2, S3)
//RGBW
-#define B_32_RN_NEO_4 NeoPixelBusLg
+#define B_32_RN_NEO_4 NeoPixelBusLg
#define B_32_I2_NEO_4 NeoPixelBusLg
#define B_32_IP_NEO_4 NeoPixelBusLg // parallel I2S
//400Kbps
-#define B_32_RN_400_3 NeoPixelBusLg