From c5b70bad849b466132dec414fe121740cc9b2fe4 Mon Sep 17 00:00:00 2001 From: Tomasz Duda Date: Fri, 19 Jan 2024 19:12:50 +0100 Subject: [PATCH] add deep sleep --- esphome/components/debug/debug_component.cpp | 1 + esphome/components/deep_sleep/__init__.py | 3 +- .../deep_sleep/deep_sleep_backend_nrf52.cpp | 54 +++++++++++++++++++ .../deep_sleep/deep_sleep_backend_nrf52.h | 21 ++++++++ .../deep_sleep/deep_sleep_component.cpp | 32 ++++++++--- .../deep_sleep/deep_sleep_component.h | 9 ++++ esphome/components/nrf52/__init__.py | 9 ++++ esphome/components/nrf52/core.cpp | 6 ++- esphome/components/nrf52/nrf52840/variant.cpp | 15 ++++++ esphome/components/nrf52/nrf52840/variant.h | 16 ++++++ .../{nrf52 => nrf52_nrfx}/nrfx_wdt.c | 0 11 files changed, 157 insertions(+), 9 deletions(-) create mode 100644 esphome/components/deep_sleep/deep_sleep_backend_nrf52.cpp create mode 100644 esphome/components/deep_sleep/deep_sleep_backend_nrf52.h create mode 100644 esphome/components/nrf52/nrf52840/variant.cpp create mode 100644 esphome/components/nrf52/nrf52840/variant.h rename esphome/components/{nrf52 => nrf52_nrfx}/nrfx_wdt.c (100%) diff --git a/esphome/components/debug/debug_component.cpp b/esphome/components/debug/debug_component.cpp index 6378d5a82f..f72a77ce0a 100644 --- a/esphome/components/debug/debug_component.cpp +++ b/esphome/components/debug/debug_component.cpp @@ -438,6 +438,7 @@ void DebugComponent::dump_config() { void DebugComponent::loop() { //TODO move to logger + //TOOD do not print when exit from deep sleep static bool d = false; if (d != Serial){ if(false == d){ diff --git a/esphome/components/deep_sleep/__init__.py b/esphome/components/deep_sleep/__init__.py index fd7ef6fcce..35b847ef3d 100644 --- a/esphome/components/deep_sleep/__init__.py +++ b/esphome/components/deep_sleep/__init__.py @@ -16,6 +16,7 @@ from esphome.const import ( CONF_WAKEUP_PIN, PLATFORM_ESP32, PLATFORM_ESP8266, + PLATFORM_NRF52, ) from esphome.components.esp32 import get_esp32_variant @@ -198,7 +199,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_TOUCH_WAKEUP): cv.All(cv.only_on_esp32, cv.boolean), } ).extend(cv.COMPONENT_SCHEMA), - cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266]), + cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_NRF52]), ) diff --git a/esphome/components/deep_sleep/deep_sleep_backend_nrf52.cpp b/esphome/components/deep_sleep/deep_sleep_backend_nrf52.cpp new file mode 100644 index 0000000000..5563bceac5 --- /dev/null +++ b/esphome/components/deep_sleep/deep_sleep_backend_nrf52.cpp @@ -0,0 +1,54 @@ +#ifdef USE_NRF52 +#include "deep_sleep_backend_nrf52.h" +#include "nrf_power.h" +#include +#include "Adafruit_TinyUSB.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace deep_sleep { + +#define DFU_MAGIC_SKIP 0x6d + +static const char *const TAG = "deep_sleep.nrf52"; + +void Nrf52DeepSleepBackend::begin_sleep(const optional& sleep_duration) { + // RTC works only during System On + if (sleep_duration.has_value()) + { + // TinyUSBDevice.detach(); + // TODO deinit USB + // TOOD and the rest of peripherals + uint32_t start_time = millis(); + portSUPPRESS_TICKS_AND_SLEEP(ms2tick(*sleep_duration/1000)); + last_sleep_duration_ = millis() - start_time; + // TinyUSBDevice.attach(); + } else { + NRF_POWER->GPREGRET = DFU_MAGIC_SKIP; + // Enter System OFF. +#ifdef SOFTDEVICE_PRESENT + uint8_t sd_en = 0; + (void) sd_softdevice_is_enabled(&sd_en); + if (sd_en) + { + uint32_t ret_code = sd_power_system_off(); + assert((ret_code == NRF_SUCCESS) || (ret_code == NRF_ERROR_SOFTDEVICE_NOT_ENABLED)); + } +#endif // SOFTDEVICE_PRESENT + nrf_power_system_off(NRF_POWER); + // it should never reach here... + } +} + +void Nrf52DeepSleepBackend::dump_config() { + if (last_sleep_duration_.has_value()) { + ESP_LOGD(TAG, "Last sleep duration: %lu ms", *last_sleep_duration_ ); + } else { + ESP_LOGD(TAG, "Last sleep duration: unknown"); + } +} + +} // namespace deep_sleep +} // namespace esphome + +#endif diff --git a/esphome/components/deep_sleep/deep_sleep_backend_nrf52.h b/esphome/components/deep_sleep/deep_sleep_backend_nrf52.h new file mode 100644 index 0000000000..216092002f --- /dev/null +++ b/esphome/components/deep_sleep/deep_sleep_backend_nrf52.h @@ -0,0 +1,21 @@ +#pragma once +#ifdef USE_NRF52 + +#include "esphome/core/optional.h" +#include "Arduino.h" + +namespace esphome { +namespace deep_sleep { + +class Nrf52DeepSleepBackend { + public: + void begin_sleep(const optional& sleep_duration); + void dump_config(); + protected: + optional last_sleep_duration_; +}; + +} // namespace deep_sleep +} // namespace esphome + +#endif diff --git a/esphome/components/deep_sleep/deep_sleep_component.cpp b/esphome/components/deep_sleep/deep_sleep_component.cpp index 328e972e6b..9848cb3d8d 100644 --- a/esphome/components/deep_sleep/deep_sleep_component.cpp +++ b/esphome/components/deep_sleep/deep_sleep_component.cpp @@ -7,6 +7,10 @@ #include #endif +#ifdef USE_NRF52 +#include "deep_sleep_backend_nrf52.h" +#endif + namespace esphome { namespace deep_sleep { @@ -33,10 +37,9 @@ optional DeepSleepComponent::get_run_duration_() const { return this->run_duration_; } -void DeepSleepComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up Deep Sleep..."); - global_has_deep_sleep = true; - +void DeepSleepComponent::setup_deep_sleep_() +{ + this->next_enter_deep_sleep_ = false; const optional run_duration = get_run_duration_(); if (run_duration.has_value()) { ESP_LOGI(TAG, "Scheduling Deep Sleep to start in %" PRIu32 " ms", *run_duration); @@ -45,6 +48,14 @@ void DeepSleepComponent::setup() { ESP_LOGD(TAG, "Not scheduling Deep Sleep, as no run duration is configured."); } } + +void DeepSleepComponent::setup() { + ESP_LOGCONFIG(TAG, "Setting up Deep Sleep..."); + global_has_deep_sleep = true; + + setup_deep_sleep_(); +} + void DeepSleepComponent::dump_config() { ESP_LOGCONFIG(TAG, "Setting up Deep Sleep..."); if (this->sleep_duration_.has_value()) { @@ -65,6 +76,9 @@ void DeepSleepComponent::dump_config() { ESP_LOGCONFIG(TAG, " GPIO Wakeup Run Duration: %" PRIu32 " ms", this->wakeup_cause_to_run_duration_->gpio_cause); } #endif +#ifdef USE_NRF52 + backend_.dump_config(); +#endif } void DeepSleepComponent::loop() { if (this->next_enter_deep_sleep_) @@ -139,7 +153,7 @@ void DeepSleepComponent::begin_sleep(bool manual) { esp_sleep_enable_touchpad_wakeup(); esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); } -#endif +#endif // USE_ESP32_VARIANT_ESP32C3 #ifdef USE_ESP32_VARIANT_ESP32C3 if (this->sleep_duration_.has_value()) esp_sleep_enable_timer_wakeup(*this->sleep_duration_); @@ -151,13 +165,17 @@ void DeepSleepComponent::begin_sleep(bool manual) { esp_deep_sleep_enable_gpio_wakeup(1 << this->wakeup_pin_->get_pin(), static_cast(level)); } -#endif +#endif // USE_ESP32_VARIANT_ESP32C3 esp_deep_sleep_start(); -#endif +#endif // USE_ESP32 #ifdef USE_ESP8266 ESP.deepSleep(*this->sleep_duration_); // NOLINT(readability-static-accessed-through-instance) #endif +#ifdef USE_NRF52 + backend_.begin_sleep(this->sleep_duration_); + setup_deep_sleep_(); +#endif } float DeepSleepComponent::get_setup_priority() const { return setup_priority::LATE; } void DeepSleepComponent::prevent_deep_sleep() { this->prevent_ = true; } diff --git a/esphome/components/deep_sleep/deep_sleep_component.h b/esphome/components/deep_sleep/deep_sleep_component.h index e97d8300c4..715305ecdc 100644 --- a/esphome/components/deep_sleep/deep_sleep_component.h +++ b/esphome/components/deep_sleep/deep_sleep_component.h @@ -14,6 +14,10 @@ #include "esphome/core/time.h" #endif +#ifdef USE_NRF52 +#include "deep_sleep_backend_nrf52.h" +#endif + #include namespace esphome { @@ -106,6 +110,8 @@ class DeepSleepComponent : public Component { // duration before entering deep sleep. optional get_run_duration_() const; + void setup_deep_sleep_(); + optional sleep_duration_; #ifdef USE_ESP32 InternalGPIOPin *wakeup_pin_; @@ -117,6 +123,9 @@ class DeepSleepComponent : public Component { optional run_duration_; bool next_enter_deep_sleep_{false}; bool prevent_{false}; +#ifdef USE_NRF52 + Nrf52DeepSleepBackend backend_; +#endif }; extern bool global_has_deep_sleep; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) diff --git a/esphome/components/nrf52/__init__.py b/esphome/components/nrf52/__init__.py index 1817a40117..f8b3ffedd7 100644 --- a/esphome/components/nrf52/__init__.py +++ b/esphome/components/nrf52/__init__.py @@ -1,3 +1,4 @@ +import os import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import ( @@ -13,6 +14,8 @@ from esphome.core import CORE, coroutine_with_priority # force import gpio to register pin schema from .gpio import nrf52_pin_to_code # noqa +AUTO_LOAD = ["nrf52_nrfx"] + def set_core_data(config): CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = PLATFORM_NRF52 @@ -84,6 +87,12 @@ async def to_code(config): cg.add_platformio_option("board_upload.use_1200bps_touch", "true") cg.add_platformio_option("board_upload.require_upload_port", "true") cg.add_platformio_option("board_upload.wait_for_upload_port", "true") + # watchdog cg.add_build_flag("-DNRFX_WDT_ENABLED=1") cg.add_build_flag("-DNRFX_WDT0_ENABLED=1") cg.add_build_flag("-DNRFX_WDT_CONFIG_NO_IRQ=1") + # prevent setting up GPIO PINs + cg.add_platformio_option("board_build.variant", "nrf52840") + cg.add_platformio_option( + "board_build.variants_dir", os.path.dirname(os.path.realpath(__file__)) + ) diff --git a/esphome/components/nrf52/core.cpp b/esphome/components/nrf52/core.cpp index 2653a84cda..8d66add85f 100644 --- a/esphome/components/nrf52/core.cpp +++ b/esphome/components/nrf52/core.cpp @@ -16,6 +16,7 @@ struct nrf5x_wdt_obj nrfx_wdt_t wdt; nrfx_wdt_channel_id ch; }; +//TODO what value for watchdog? static nrfx_wdt_config_t nrf5x_wdt_cfg = NRFX_WDT_DEFAULT_CONFIG; static struct nrf5x_wdt_obj nrf5x_wdt = { @@ -24,6 +25,7 @@ static struct nrf5x_wdt_obj nrf5x_wdt = { void arch_init() { //Configure WDT. + nrf5x_wdt_cfg.behaviour = NRF_WDT_BEHAVIOUR_PAUSE_SLEEP_HALT; nrfx_wdt_init(&nrf5x_wdt.wdt, &nrf5x_wdt_cfg, nullptr); nrfx_wdt_channel_alloc(&nrf5x_wdt.wdt, &nrf5x_wdt.ch); nrfx_wdt_enable(&nrf5x_wdt.wdt); @@ -42,7 +44,9 @@ void arch_feed_wdt() { nrfx_wdt_feed(&nrf5x_wdt.wdt); } -void arch_restart() { /* TODO */ } +void arch_restart() { + NVIC_SystemReset(); +} void nrf52GetMacAddr(uint8_t *mac) { diff --git a/esphome/components/nrf52/nrf52840/variant.cpp b/esphome/components/nrf52/nrf52840/variant.cpp new file mode 100644 index 0000000000..4c977f7edd --- /dev/null +++ b/esphome/components/nrf52/nrf52840/variant.cpp @@ -0,0 +1,15 @@ +#include "variant.h" + +const uint32_t g_ADigitalPinMap[] = +{ + // P0 + 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , + 8 , 9 , 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + + // P1 + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47 +}; + diff --git a/esphome/components/nrf52/nrf52840/variant.h b/esphome/components/nrf52/nrf52840/variant.h new file mode 100644 index 0000000000..d6bd85f7bb --- /dev/null +++ b/esphome/components/nrf52/nrf52840/variant.h @@ -0,0 +1,16 @@ +#pragma once + +#include "WVariant.h" + +#define PINS_COUNT (48) +#define LED_BUILTIN (64) +#if PINS_COUNT > LED_BUILTIN +#error LED_BUILTIN should be bigger than PINS_COUNT. To ignore settings. +#endif +#define LED_BLUE (LED_BUILTIN) +// TODO other are also needed? +#define USE_LFXO // Board uses 32khz crystal for LF +#define LED_STATE_ON (1) +#define PIN_SERIAL1_RX (33) // P1.01 +#define PIN_SERIAL1_TX (34) // P1.02 + diff --git a/esphome/components/nrf52/nrfx_wdt.c b/esphome/components/nrf52_nrfx/nrfx_wdt.c similarity index 100% rename from esphome/components/nrf52/nrfx_wdt.c rename to esphome/components/nrf52_nrfx/nrfx_wdt.c