Browse Source

system: before and after sleep callbacks

pull/2584/head
Maxim Prokhorov 1 year ago
parent
commit
4f72c60b59
6 changed files with 185 additions and 29 deletions
  1. +9
    -0
      code/espurna/led.cpp
  2. +26
    -0
      code/espurna/light.cpp
  3. +105
    -29
      code/espurna/sensor.cpp
  4. +7
    -0
      code/espurna/sensors/BaseSensor.h
  5. +34
    -0
      code/espurna/system.cpp
  6. +4
    -0
      code/espurna/system.h

+ 9
- 0
code/espurna/led.cpp View File

@ -773,6 +773,12 @@ bool status(size_t id, bool value) {
return status(internal::leds[id], value);
}
void turn_off() {
for (auto& led : internal::leds) {
status(led, false);
}
}
[[gnu::unused]]
void pattern(Led& led, Pattern&& other) {
led.pattern(std::move(other));
@ -1076,6 +1082,9 @@ void setup() {
terminal::setup();
#endif
systemBeforeSleep(turn_off);
systemAfterSleep(schedule);
::espurnaRegisterLoop(loop);
::espurnaRegisterReload(configure);


+ 26
- 0
code/espurna/light.cpp View File

@ -3484,6 +3484,30 @@ void _lightConfigure() {
}
}
void _lightSleepSetup() {
systemBeforeSleep(
[]() {
size_t id = 0;
for (auto& channel : _light_channels) {
_lightProviderHandleValue(id, 0);
++id;
channel.value = 0;
}
_lightProviderHandleState(false);
_lightProviderHandleUpdate();
espurna::time::blockingDelay(
espurna::duration::Milliseconds{ 100 });
});
systemAfterSleep(
[]() {
_lightUpdate(false);
_light_state_changed = true;
});
}
void _lightBoot() {
const size_t Channels { _light_channels.size() };
if (Channels) {
@ -3684,6 +3708,8 @@ void lightSetup() {
_lightInitCommands();
#endif
_lightSleepSetup();
espurnaRegisterReload(_lightConfigure);
espurnaRegisterLoop([]() {
_lightSequenceCheck();


+ 105
- 29
code/espurna/sensor.cpp View File

@ -297,9 +297,9 @@ void forEachError(T&& callback) {
}
struct ReadValue {
double raw;
double processed;
double filtered;
double raw; // as the sensor returns it
double processed; // after applying units and decimals
double filtered; // after applying filters, units and decimals
};
enum class Filter : int {
@ -1908,10 +1908,21 @@ Value safe_value_reported(size_t index) {
} // namespace magnitude
using TimeSource = espurna::time::CoreClock;
enum class State {
None,
Initial,
Idle,
Resume,
Ready,
Reading,
};
namespace internal {
std::vector<BaseSensorPtr> sensors;
bool ready { false };
size_t read_count;
bool real_time { build::realTimeValues() };
size_t report_every { build::reportEvery() };
@ -3780,8 +3791,38 @@ void setup() {
// Sensor initialization
// -----------------------------------------------------------------------------
void init() {
internal::ready = true;
namespace internal {
State state;
TimeSource::time_point last_init;
TimeSource::time_point last_reading;
} // namespace internal
void suspend() {
for (auto& sensor : internal::sensors) {
sensor->suspend();
}
}
void resume() {
internal::last_init = TimeSource::now();
internal::last_reading = TimeSource::now();
internal::read_count = 1;
magnitude::forEachInstance(
[](sensor::Magnitude& instance) {
instance.filter->reset();
});
for (auto& sensor : internal::sensors) {
sensor->resume();
}
}
bool init() {
bool out { true };
for (auto sensor : internal::sensors) {
// Do not process an already initialized sensor
@ -3800,7 +3841,7 @@ void init() {
DEBUG_MSG_P(PSTR("[SENSOR] -> ERROR %s (%hhu)\n"),
sensor::error(error).c_str(), error);
}
internal::ready = false;
out = false;
break;
}
@ -3818,43 +3859,73 @@ void init() {
}
}
if (internal::ready) {
if (out) {
internal::state = State::Ready;
DEBUG_MSG_P(PSTR("[SENSOR] Finished initialization for %zu sensor(s) and %zu magnitude(s)\n"),
sensor::count(), magnitude::count());
}
return out;
}
void loop() {
// Continiously repeat initialization if there are still some un-initialized sensors after setup()
using TimeSource = espurna::time::CoreClock;
static auto last_init = TimeSource::now();
bool try_init() {
const auto timestamp = TimeSource::now();
if (timestamp - internal::last_init > initInterval()) {
internal::last_init = timestamp;
return init();
}
return false;
}
auto timestamp = TimeSource::now();
if (!internal::ready && (timestamp - last_init > initInterval())) {
last_init = timestamp;
sensor::init();
bool ready_to_read() {
const auto timestamp = TimeSource::now();
if (timestamp - internal::last_reading > readInterval()) {
internal::last_reading = timestamp;
internal::read_count = (internal::read_count + 1) % reportEvery();
return true;
}
if (!magnitude::internal::magnitudes.size()) {
return false;
}
bool ready_to_report() {
return internal::read_count == 0;
}
void loop() {
// TODO: allow to do nothing
if (internal::state == State::Idle) {
return;
}
static auto last_update = TimeSource::now();
static size_t report_count { 0 };
// Continiously repeat initialization if there are still some un-initialized sensors after setup()
if (internal::state == State::None) {
internal::state = State::Initial;
}
sensor::tick();
// General initialization, generate magnitudes from available sensors
if (internal::state == State::Initial) {
if (try_init()) {
internal::state = State::Ready;
}
}
if (timestamp - last_update > readInterval()) {
last_update = timestamp;
report_count = (report_count + 1) % reportEvery();
// If magnitudes were initialized and we are ready, prepare to read sensor data
if (internal::state == State::Ready) {
if (magnitude::internal::magnitudes.size() != 0) {
internal::state = State::Reading;
}
}
sensor::ReadValue value {
.raw = 0.0, // as the sensor returns it
.processed = 0.0, // after applying units and decimals
.filtered = 0.0 // after applying filters, units and decimals
};
if (internal::state != State::Reading) {
return;
}
// Tick hook, called every loop()
sensor::tick();
if (ready_to_read()) {
// Pre-read hook, called every reading
sensor::pre();
@ -3863,6 +3934,8 @@ void loop() {
const bool relay_off = (relayCount() == 1) && (relayStatus(0) == 0);
#endif
auto value = sensor::ReadValue{};
for (size_t index = 0; index < magnitude::count(); ++index) {
auto& magnitude = magnitude::get(index);
if (!magnitude.sensor->status()) {
@ -3913,7 +3986,7 @@ void loop() {
// -------------------------------------------------------------------
// Initial status or after report counter overflows
bool report { 0 == report_count };
bool report { ready_to_report() };
// In case magnitude was configured with ${name}MaxDelta, override report check
// when the value change is greater than the delta
@ -4104,6 +4177,9 @@ void setup() {
terminal::setup();
#endif
systemBeforeSleep(sensor::suspend);
systemAfterSleep(sensor::resume);
espurnaRegisterLoop(sensor::loop);
espurnaRegisterReload(sensor::configure);
}


+ 7
- 0
code/espurna/sensors/BaseSensor.h View File

@ -137,6 +137,13 @@ public:
virtual void begin() {
}
// Suspend / resume sensor operation
virtual void suspend() {
}
virtual void resume() {
}
// Loop-like method, call it in your main loop
virtual void tick() {
}


+ 34
- 0
code/espurna/system.cpp View File

@ -143,6 +143,13 @@ String serialize(espurna::duration::ClockCycles value) {
namespace sleep {
namespace {
namespace internal {
std::forward_list<SleepCallback> before;
std::forward_list<SleepCallback> after;
} // namespace internal
constexpr auto DeepSleepWakeupPin = uint8_t{ 16 };
namespace build {
@ -268,6 +275,9 @@ private:
FpmLightSleep::~FpmLightSleep() {
if (_ok) {
wifi_fpm_close();
for (auto callback : internal::after) {
callback();
}
}
wifi_fpm_auto_sleep_set_in_null_mode(1);
@ -293,6 +303,10 @@ FpmLightSleep::FpmLightSleep() {
wifi_fpm_set_sleep_type(LIGHT_SLEEP_T);
wifi_fpm_open();
for (auto callback : internal::before) {
callback();
}
_ok = true;
}
@ -396,6 +410,10 @@ bool deep_sleep(sleep::Microseconds time) {
system_deep_sleep_set_option(RF_DEFAULT);
if (system_deep_sleep(time.count())) {
for (auto callback : internal::before) {
callback();
}
yield();
return true;
}
@ -403,6 +421,14 @@ bool deep_sleep(sleep::Microseconds time) {
return false;
}
void before(SleepCallback callback) {
internal::before.push_front(callback);
}
void after(SleepCallback callback) {
internal::after.push_front(callback);
}
// Force WiFi RF peripheral to power down when NULL opmode is selected
void init() {
wifi_fpm_auto_sleep_set_in_null_mode(1);
@ -1627,6 +1653,14 @@ bool wakeupModemForcedSleep() {
return espurna::sleep::forced_wakeup();
}
void systemBeforeSleep(SleepCallback callback) {
espurna::sleep::before(callback);
}
void systemAfterSleep(SleepCallback callback) {
espurna::sleep::after(callback);
}
bool instantLightSleep() {
return espurna::sleep::forced_light_sleep();
}


+ 4
- 0
code/espurna/system.h View File

@ -412,6 +412,10 @@ bool pendingDeferredReset();
bool wakeupModemForcedSleep();
bool prepareModemForcedSleep();
using SleepCallback = void (*)();
void systemBeforeSleep(SleepCallback);
void systemAfterSleep(SleepCallback);
bool instantLightSleep();
bool instantLightSleep(espurna::sleep::Microseconds);
bool instantLightSleep(uint8_t pin, espurna::sleep::Interrupt);


Loading…
Cancel
Save