From 7e18a5c44f70a5eea4bff4d94e4c05a2311f8f0e Mon Sep 17 00:00:00 2001
From: Angel Nunez Mencias <github@angelnu.com>
Date: Tue, 27 Aug 2024 03:26:01 +0200
Subject: [PATCH] Add reset to esp32_rmt_led_strip (#7354)

---
 .../esp32_rmt_led_strip/led_strip.cpp         | 16 +++++++++++--
 .../esp32_rmt_led_strip/led_strip.h           |  5 ++--
 .../components/esp32_rmt_led_strip/light.py   | 24 +++++++++++++++----
 3 files changed, 37 insertions(+), 8 deletions(-)

diff --git a/esphome/components/esp32_rmt_led_strip/led_strip.cpp b/esphome/components/esp32_rmt_led_strip/led_strip.cpp
index 7727b64f29..71ab099de5 100644
--- a/esphome/components/esp32_rmt_led_strip/led_strip.cpp
+++ b/esphome/components/esp32_rmt_led_strip/led_strip.cpp
@@ -38,7 +38,8 @@ void ESP32RMTLEDStripLightOutput::setup() {
   }
 
   ExternalRAMAllocator<rmt_item32_t> rmt_allocator(ExternalRAMAllocator<rmt_item32_t>::ALLOW_FAILURE);
-  this->rmt_buf_ = rmt_allocator.allocate(buffer_size * 8);  // 8 bits per byte, 1 rmt_item32_t per bit
+  this->rmt_buf_ = rmt_allocator.allocate(buffer_size * 8 +
+                                          1);  // 8 bits per byte, 1 rmt_item32_t per bit + 1 rmt_item32_t for reset
 
   rmt_config_t config;
   memset(&config, 0, sizeof(config));
@@ -66,7 +67,7 @@ void ESP32RMTLEDStripLightOutput::setup() {
 }
 
 void ESP32RMTLEDStripLightOutput::set_led_params(uint32_t bit0_high, uint32_t bit0_low, uint32_t bit1_high,
-                                                 uint32_t bit1_low) {
+                                                 uint32_t bit1_low, uint32_t reset_time_high, uint32_t reset_time_low) {
   float ratio = (float) RMT_CLK_FREQ / RMT_CLK_DIV / 1e09f;
 
   // 0-bit
@@ -79,6 +80,11 @@ void ESP32RMTLEDStripLightOutput::set_led_params(uint32_t bit0_high, uint32_t bi
   this->bit1_.level0 = 1;
   this->bit1_.duration1 = (uint32_t) (ratio * bit1_low);
   this->bit1_.level1 = 0;
+  // reset
+  this->reset_.duration0 = (uint32_t) (ratio * reset_time_high);
+  this->reset_.level0 = 1;
+  this->reset_.duration1 = (uint32_t) (ratio * reset_time_low);
+  this->reset_.level1 = 0;
 }
 
 void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) {
@@ -118,6 +124,12 @@ void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) {
     psrc++;
   }
 
+  if (this->reset_.duration0 > 0 || this->reset_.duration1 > 0) {
+    pdest->val = this->reset_.val;
+    pdest++;
+    len++;
+  }
+
   if (rmt_write_items(this->channel_, this->rmt_buf_, len, false) != ESP_OK) {
     ESP_LOGE(TAG, "RMT TX error");
     this->status_set_warning();
diff --git a/esphome/components/esp32_rmt_led_strip/led_strip.h b/esphome/components/esp32_rmt_led_strip/led_strip.h
index e9b19c9399..43215cf12b 100644
--- a/esphome/components/esp32_rmt_led_strip/led_strip.h
+++ b/esphome/components/esp32_rmt_led_strip/led_strip.h
@@ -49,7 +49,8 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight {
   /// Set a maximum refresh rate in µs as some lights do not like being updated too often.
   void set_max_refresh_rate(uint32_t interval_us) { this->max_refresh_rate_ = interval_us; }
 
-  void set_led_params(uint32_t bit0_high, uint32_t bit0_low, uint32_t bit1_high, uint32_t bit1_low);
+  void set_led_params(uint32_t bit0_high, uint32_t bit0_low, uint32_t bit1_high, uint32_t bit1_low,
+                      uint32_t reset_time_high, uint32_t reset_time_low);
 
   void set_rgb_order(RGBOrder rgb_order) { this->rgb_order_ = rgb_order; }
   void set_rmt_channel(rmt_channel_t channel) { this->channel_ = channel; }
@@ -75,7 +76,7 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight {
   bool is_rgbw_;
   bool is_wrgb_;
 
-  rmt_item32_t bit0_, bit1_;
+  rmt_item32_t bit0_, bit1_, reset_;
   RGBOrder rgb_order_;
   rmt_channel_t channel_;
 
diff --git a/esphome/components/esp32_rmt_led_strip/light.py b/esphome/components/esp32_rmt_led_strip/light.py
index 4c8472b8d2..4a04918275 100644
--- a/esphome/components/esp32_rmt_led_strip/light.py
+++ b/esphome/components/esp32_rmt_led_strip/light.py
@@ -43,13 +43,15 @@ class LEDStripTimings:
     bit0_low: int
     bit1_high: int
     bit1_low: int
+    reset_high: int
+    reset_low: int
 
 
 CHIPSETS = {
-    "WS2812": LEDStripTimings(400, 1000, 1000, 400),
-    "SK6812": LEDStripTimings(300, 900, 600, 600),
-    "APA106": LEDStripTimings(350, 1360, 1360, 350),
-    "SM16703": LEDStripTimings(300, 900, 900, 300),
+    "WS2812": LEDStripTimings(400, 1000, 1000, 400, 0, 0),
+    "SK6812": LEDStripTimings(300, 900, 600, 600, 0, 0),
+    "APA106": LEDStripTimings(350, 1360, 1360, 350, 0, 0),
+    "SM16703": LEDStripTimings(300, 900, 900, 300, 0, 0),
 }
 
 
@@ -58,6 +60,8 @@ CONF_BIT0_HIGH = "bit0_high"
 CONF_BIT0_LOW = "bit0_low"
 CONF_BIT1_HIGH = "bit1_high"
 CONF_BIT1_LOW = "bit1_low"
+CONF_RESET_HIGH = "reset_high"
+CONF_RESET_LOW = "reset_low"
 
 
 CONFIG_SCHEMA = cv.All(
@@ -88,6 +92,14 @@ CONFIG_SCHEMA = cv.All(
                 CONF_BIT1_LOW,
                 "custom",
             ): cv.positive_time_period_nanoseconds,
+            cv.Optional(
+                CONF_RESET_HIGH,
+                default="0 us",
+            ): cv.positive_time_period_nanoseconds,
+            cv.Optional(
+                CONF_RESET_LOW,
+                default="0 us",
+            ): cv.positive_time_period_nanoseconds,
         }
     ),
     cv.has_exactly_one_key(CONF_CHIPSET, CONF_BIT0_HIGH),
@@ -113,6 +125,8 @@ async def to_code(config):
                 chipset.bit0_low,
                 chipset.bit1_high,
                 chipset.bit1_low,
+                chipset.reset_high,
+                chipset.reset_low,
             )
         )
     else:
@@ -122,6 +136,8 @@ async def to_code(config):
                 config[CONF_BIT0_LOW],
                 config[CONF_BIT1_HIGH],
                 config[CONF_BIT1_LOW],
+                config[CONF_RESET_HIGH],
+                config[CONF_RESET_LOW],
             )
         )