add deep sleep

This commit is contained in:
Tomasz Duda 2024-01-19 19:12:50 +01:00
parent 793e90a0e0
commit c5b70bad84
11 changed files with 157 additions and 9 deletions

View File

@ -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){

View File

@ -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]),
)

View File

@ -0,0 +1,54 @@
#ifdef USE_NRF52
#include "deep_sleep_backend_nrf52.h"
#include "nrf_power.h"
#include <cassert>
#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<uint64_t>& 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

View File

@ -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<uint64_t>& sleep_duration);
void dump_config();
protected:
optional<uint32_t> last_sleep_duration_;
};
} // namespace deep_sleep
} // namespace esphome
#endif

View File

@ -7,6 +7,10 @@
#include <Esp.h>
#endif
#ifdef USE_NRF52
#include "deep_sleep_backend_nrf52.h"
#endif
namespace esphome {
namespace deep_sleep {
@ -33,10 +37,9 @@ optional<uint32_t> 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<uint32_t> 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<esp_deepsleep_gpio_wake_up_mode_t>(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; }

View File

@ -14,6 +14,10 @@
#include "esphome/core/time.h"
#endif
#ifdef USE_NRF52
#include "deep_sleep_backend_nrf52.h"
#endif
#include <cinttypes>
namespace esphome {
@ -106,6 +110,8 @@ class DeepSleepComponent : public Component {
// duration before entering deep sleep.
optional<uint32_t> get_run_duration_() const;
void setup_deep_sleep_();
optional<uint64_t> sleep_duration_;
#ifdef USE_ESP32
InternalGPIOPin *wakeup_pin_;
@ -117,6 +123,9 @@ class DeepSleepComponent : public Component {
optional<uint32_t> 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)

View File

@ -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__))
)

View File

@ -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)
{

View File

@ -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
};

View File

@ -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