From b8d10a62c2436430cc235342062215a6f77b3494 Mon Sep 17 00:00:00 2001
From: Tyler Bules <tylerbules@gmail.com>
Date: Sat, 19 Feb 2022 09:13:48 -0500
Subject: [PATCH] ESP32-C3 deep sleep fix (#3066)

---
 esphome/components/deep_sleep/__init__.py     | 40 ++++++++++++++++++-
 .../deep_sleep/deep_sleep_component.cpp       | 14 ++++++-
 .../deep_sleep/deep_sleep_component.h         |  5 ++-
 tests/test1.yaml                              |  2 +-
 tests/test2.yaml                              |  2 +-
 5 files changed, 58 insertions(+), 5 deletions(-)

diff --git a/esphome/components/deep_sleep/__init__.py b/esphome/components/deep_sleep/__init__.py
index ba4c2c0d7e..2a74d0c1bb 100644
--- a/esphome/components/deep_sleep/__init__.py
+++ b/esphome/components/deep_sleep/__init__.py
@@ -11,9 +11,39 @@ from esphome.const import (
     CONF_WAKEUP_PIN,
 )
 
+from esphome.components.esp32 import get_esp32_variant
+from esphome.components.esp32.const import (
+    VARIANT_ESP32,
+    VARIANT_ESP32C3,
+)
+
+WAKEUP_PINS = {
+    VARIANT_ESP32: [
+        0,
+        2,
+        4,
+        12,
+        13,
+        14,
+        15,
+        25,
+        26,
+        27,
+        32,
+        33,
+        34,
+        35,
+        36,
+        37,
+        38,
+        39,
+    ],
+    VARIANT_ESP32C3: [0, 1, 2, 3, 4, 5],
+}
+
 
 def validate_pin_number(value):
-    valid_pins = [0, 2, 4, 12, 13, 14, 15, 25, 26, 27, 32, 33, 34, 35, 36, 37, 38, 39]
+    valid_pins = WAKEUP_PINS.get(get_esp32_variant(), WAKEUP_PINS[VARIANT_ESP32])
     if value[CONF_NUMBER] not in valid_pins:
         raise cv.Invalid(
             f"Only pins {', '.join(str(x) for x in valid_pins)} support wakeup"
@@ -21,6 +51,14 @@ def validate_pin_number(value):
     return value
 
 
+def validate_config(config):
+    if get_esp32_variant() == VARIANT_ESP32C3 and CONF_ESP32_EXT1_WAKEUP in config:
+        raise cv.Invalid("ESP32-C3 does not support wakeup from touch.")
+    if get_esp32_variant() == VARIANT_ESP32C3 and CONF_TOUCH_WAKEUP in config:
+        raise cv.Invalid("ESP32-C3 does not support wakeup from ext1")
+    return config
+
+
 deep_sleep_ns = cg.esphome_ns.namespace("deep_sleep")
 DeepSleepComponent = deep_sleep_ns.class_("DeepSleepComponent", cg.Component)
 EnterDeepSleepAction = deep_sleep_ns.class_("EnterDeepSleepAction", automation.Action)
diff --git a/esphome/components/deep_sleep/deep_sleep_component.cpp b/esphome/components/deep_sleep/deep_sleep_component.cpp
index 7774014d3d..82751b538b 100644
--- a/esphome/components/deep_sleep/deep_sleep_component.cpp
+++ b/esphome/components/deep_sleep/deep_sleep_component.cpp
@@ -104,7 +104,7 @@ void DeepSleepComponent::begin_sleep(bool manual) {
 
   App.run_safe_shutdown_hooks();
 
-#ifdef USE_ESP32
+#if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C3)
   if (this->sleep_duration_.has_value())
     esp_sleep_enable_timer_wakeup(*this->sleep_duration_);
   if (this->wakeup_pin_ != nullptr) {
@@ -126,6 +126,18 @@ void DeepSleepComponent::begin_sleep(bool manual) {
   esp_deep_sleep_start();
 #endif
 
+#ifdef USE_ESP32_VARIANT_ESP32C3
+  if (this->sleep_duration_.has_value())
+    esp_sleep_enable_timer_wakeup(*this->sleep_duration_);
+  if (this->wakeup_pin_ != nullptr) {
+    bool level = !this->wakeup_pin_->is_inverted();
+    if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) {
+      level = !level;
+    }
+    esp_deep_sleep_enable_gpio_wakeup(gpio_num_t(this->wakeup_pin_->get_pin()), level);
+  }
+#endif
+
 #ifdef USE_ESP8266
   ESP.deepSleep(*this->sleep_duration_);  // NOLINT(readability-static-accessed-through-instance)
 #endif
diff --git a/esphome/components/deep_sleep/deep_sleep_component.h b/esphome/components/deep_sleep/deep_sleep_component.h
index 59df199a9f..057d992427 100644
--- a/esphome/components/deep_sleep/deep_sleep_component.h
+++ b/esphome/components/deep_sleep/deep_sleep_component.h
@@ -57,13 +57,16 @@ class DeepSleepComponent : public Component {
  public:
   /// Set the duration in ms the component should sleep once it's in deep sleep mode.
   void set_sleep_duration(uint32_t time_ms);
-#ifdef USE_ESP32
+#if defined(USE_ESP32)
   /** Set the pin to wake up to on the ESP32 once it's in deep sleep mode.
    * Use the inverted property to set the wakeup level.
    */
   void set_wakeup_pin(InternalGPIOPin *pin) { this->wakeup_pin_ = pin; }
 
   void set_wakeup_pin_mode(WakeupPinMode wakeup_pin_mode);
+#endif
+
+#if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C3)
 
   void set_ext1_wakeup(Ext1Wakeup ext1_wakeup);
 
diff --git a/tests/test1.yaml b/tests/test1.yaml
index 7eb1b457db..4e82d06485 100644
--- a/tests/test1.yaml
+++ b/tests/test1.yaml
@@ -262,7 +262,7 @@ power_supply:
 deep_sleep:
   run_duration: 20s
   sleep_duration: 50s
-  wakeup_pin: GPIO39
+  wakeup_pin: GPIO2
   wakeup_pin_mode: INVERT_WAKEUP
 
 ads1115:
diff --git a/tests/test2.yaml b/tests/test2.yaml
index 2a122b971f..76b9775c54 100644
--- a/tests/test2.yaml
+++ b/tests/test2.yaml
@@ -60,7 +60,7 @@ deep_sleep:
     gpio_wakeup_reason: 10s
     touch_wakeup_reason: 15s
   sleep_duration: 50s
-  wakeup_pin: GPIO39
+  wakeup_pin: GPIO2
   wakeup_pin_mode: INVERT_WAKEUP
 
 as3935_i2c: