Skip to content

Commit

Permalink
wifi: experiment with forced light sleep
Browse files Browse the repository at this point in the history
see #2578

make TurnOff action select sleep mode from available settings
wifi.off and wifi.on commands in addition to existing AP & STA

overriding buttons is... problematic?
e.g. selecting LO trigger and existing hw pushbutton GPIO,
we are most likely to trigger long-click event
  • Loading branch information
mcspr committed Feb 4, 2023
1 parent 0db47f5 commit bb4e74d
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 43 deletions.
1 change: 0 additions & 1 deletion code/espurna/relay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1630,7 +1630,6 @@ void _relayProcessActivePulse(const Relay& relay, size_t id, bool status) {
}

// start pulse for the current status as 'target'
// TODO: special suffixes for minutes, hours and days
[[gnu::unused]]
bool _relayHandlePulsePayload(size_t id, espurna::StringView payload) {
const auto status = relayStatus(id);
Expand Down
183 changes: 141 additions & 42 deletions code/espurna/wifi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,18 @@ constexpr WiFiSleepType_t sleep() {
return WIFI_SLEEP_MODE;
}

constexpr sleep_type_t forcedSleep() {
return MODEM_SLEEP_T;
}

constexpr uint8_t forcedSleepPin() {
return GPIO_NONE;
}

constexpr GPIO_INT_TYPE forcedSleepLevel() {
return GPIO_PIN_INTR_LOLEVEL;
}

} // namespace build

namespace ap {
Expand Down Expand Up @@ -89,12 +101,25 @@ PROGMEM_STRING(None, "none");
PROGMEM_STRING(Modem, "modem");
PROGMEM_STRING(Light, "light");

PROGMEM_STRING(Low, "low");
PROGMEM_STRING(High, "high");

static constexpr espurna::settings::options::Enumeration<WiFiSleepType_t> WiFiSleepTypeOptions[] PROGMEM {
{WIFI_NONE_SLEEP, None},
{WIFI_MODEM_SLEEP, Modem},
{WIFI_LIGHT_SLEEP, Light},
};

static constexpr espurna::settings::options::Enumeration<sleep_type_t> ForcedSleepTypeOptions[] PROGMEM {
{MODEM_SLEEP_T, Modem},
{LIGHT_SLEEP_T, Light},
};

static constexpr espurna::settings::options::Enumeration<GPIO_INT_TYPE> ForcedSleepLevelOptions[] PROGMEM {
{GPIO_PIN_INTR_LOLEVEL, Low},
{GPIO_PIN_INTR_HILEVEL, High},
};

} // namespace options
} // namespace settings

Expand Down Expand Up @@ -133,6 +158,24 @@ String serialize(WiFiSleepType_t sleep) {
return serialize(wifi::settings::options::WiFiSleepTypeOptions, sleep);
}

template <>
sleep_type_t convert(const String& value) {
return convert(wifi::settings::options::ForcedSleepTypeOptions, value, wifi::build::forcedSleep());
}

String serialize(sleep_type_t sleep) {
return serialize(wifi::settings::options::ForcedSleepTypeOptions, sleep);
}

template <>
GPIO_INT_TYPE convert(const String& value) {
return convert(wifi::settings::options::ForcedSleepLevelOptions, value, wifi::build::forcedSleepLevel());
}

String serialize(GPIO_INT_TYPE interrupt) {
return serialize(wifi::settings::options::ForcedSleepLevelOptions, interrupt);
}

template <>
IPAddress convert(const String& value) {
IPAddress out;
Expand Down Expand Up @@ -230,7 +273,7 @@ enum class Action {
AccessPointStart,
AccessPointStop,
TurnOff,
TurnOn
TurnOn,
};

using Actions = std::list<Action>;
Expand Down Expand Up @@ -328,44 +371,6 @@ ActionsQueue& actions() {
return internal::actions;
}

// ::forceSleepBegin() remembers the previous mode and ::forceSleepWake() calls station connect when it has STA in it :/
// while we *do* set opmode to 0 to avoid this uncertainty, preper to call wake through SDK instead of the Arduino wrapper
//
// 0xFFFFFFF is a magic number per the NONOS API reference, 3.7.5 wifi_fpm_do_sleep:
// > If sleep_time_in_us is 0xFFFFFFF, the ESP8266 will sleep till be woke up as below:
// > • If wifi_fpm_set_sleep_type is set to be LIGHT_SLEEP_T, ESP8266 can wake up by GPIO.
// > • If wifi_fpm_set_sleep_type is set to be MODEM_SLEEP_T, ESP8266 can wake up by wifi_fpm_do_wakeup.
//
// In our case, wake-up is software driven, so the MODEM sleep is the only choice available.
// This version can *only* work from CONT context, since the only consumer atm is wifi::Action handler
// TODO(esp32): Null mode turns off radio, no need for these

bool sleep() {
if (opmode() == OpmodeNull) {
wifi_fpm_set_sleep_type(MODEM_SLEEP_T);
yield();
wifi_fpm_open();
yield();
if (0 == wifi_fpm_do_sleep(0xFFFFFFF)) {
delay(10);
return true;
}
}

return false;
}

bool wakeup() {
if (wifi_fpm_get_sleep_type() != NONE_SLEEP_T) {
wifi_fpm_do_wakeup();
wifi_fpm_close();
delay(10);
return true;
}

return false;
}

namespace debug {

String error(wifi::ScanError error) {
Expand Down Expand Up @@ -467,6 +472,9 @@ namespace keys {

PROGMEM_STRING(TxPower, "wifiTxPwr");
PROGMEM_STRING(Sleep, "wifiSleep");
PROGMEM_STRING(ForcedSleep, "wifiForcedSleep");
PROGMEM_STRING(ForcedSleepPin, "wifiForcedSleepPin");
PROGMEM_STRING(ForcedSleepLevel, "wifiForcedSleepIntr");

} // namespace keys

Expand All @@ -478,6 +486,18 @@ WiFiSleepType_t sleep() {
return getSetting(keys::Sleep, wifi::build::sleep());
}

sleep_type_t forcedSleep() {
return getSetting(keys::ForcedSleep, wifi::build::forcedSleep());
}

uint8_t forcedSleepPin() {
return getSetting(keys::ForcedSleepPin, wifi::build::forcedSleepPin());
}

GPIO_INT_TYPE forcedSleepLevel() {
return getSetting(keys::ForcedSleepLevel, wifi::build::forcedSleepLevel());
}

namespace query {
namespace internal {

Expand All @@ -493,11 +513,64 @@ String NAME (size_t id) {\

EXACT_VALUE(sleep, settings::sleep)
EXACT_VALUE(txPower, settings::txPower)
EXACT_VALUE(forcedSleep, settings::forcedSleep)
EXACT_VALUE(forcedSleepPin, settings::forcedSleepPin)
EXACT_VALUE(forcedSleepLevel, settings::forcedSleepLevel)

} // namespace internal
} // namespace query
} // namespace settings

// ::forceSleepBegin() remembers the previous mode and ::forceSleepWake() calls station connect when it has STA in it :/
// while we *do* set opmode to 0 to avoid this uncertainty, preper to call wake through SDK instead of the Arduino wrapper
//
// 0xFFFFFFF is a magic number per the NONOS API reference, 3.7.5 wifi_fpm_do_sleep:
// > If sleep_time_in_us is 0xFFFFFFF, the ESP8266 will sleep till be woke up as below:
// > • If wifi_fpm_set_sleep_type is set to be LIGHT_SLEEP_T, ESP8266 can wake up by GPIO.
// > • If wifi_fpm_set_sleep_type is set to be MODEM_SLEEP_T, ESP8266 can wake up by wifi_fpm_do_wakeup.
//
// In our case, wake-up is software driven, so the MODEM sleep is the only choice available.
// This version can *only* work from CONT context, since the only consumer atm is wifi::Action handler
// TODO(esp32): Null mode turns off radio, no need for these

bool sleep(sleep_type_t type) {
if (!enabled() && (opmode() == OpmodeNull)) {
if (type == LIGHT_SLEEP_T) {
const auto pin = settings::forcedSleepPin();
if (pin == GPIO_NONE) {
return false;
}

pinMode(pin, INPUT);
gpio_pin_wakeup_enable(pin, settings::forcedSleepLevel());
}

wifi_fpm_set_sleep_type(type);
yield();
wifi_fpm_open();
yield();

if (0 == wifi_fpm_do_sleep(0xFFFFFFF)) {
delay(10);
return true;
}
}

return false;
}

bool wakeup() {
if (wifi_fpm_get_sleep_type() != NONE_SLEEP_T) {
wifi_fpm_do_wakeup();
wifi_fpm_close();
delay(10);
return true;
}

return false;
}


// We are guaranteed to have '\0' when <32 b/c the SDK zeroes out the data
// But, these are byte arrays, not C strings. When ssid_len is available, use it.
// When not, we are still expecting the <32 arrays to have '\0' at the end and we manually
Expand Down Expand Up @@ -2161,7 +2234,7 @@ void configure() {
namespace settings {
namespace query {

static constexpr std::array<espurna::settings::query::Setting, 10> Settings PROGMEM {
static constexpr std::array<espurna::settings::query::Setting, 13> Settings PROGMEM {
{{wifi::ap::settings::keys::Ssid, wifi::ap::settings::ssid},
{wifi::ap::settings::keys::Passphrase, wifi::ap::settings::passphrase},
{wifi::ap::settings::keys::Captive, wifi::ap::settings::query::internal::captive},
Expand All @@ -2171,7 +2244,10 @@ static constexpr std::array<espurna::settings::query::Setting, 10> Settings PROG
{wifi::sta::scan::settings::keys::Enabled, wifi::sta::scan::settings::query::enabled},
{wifi::sta::scan::periodic::settings::keys::Threshold, wifi::sta::scan::periodic::settings::query::threshold},
{wifi::settings::keys::TxPower, espurna::wifi::settings::query::internal::txPower},
{wifi::settings::keys::Sleep, espurna::wifi::settings::query::internal::sleep}}
{wifi::settings::keys::Sleep, espurna::wifi::settings::query::internal::sleep},
{wifi::settings::keys::ForcedSleep, espurna::wifi::settings::query::internal::forcedSleep},
{wifi::settings::keys::ForcedSleepPin, espurna::wifi::settings::query::internal::forcedSleepPin},
{wifi::settings::keys::ForcedSleepLevel, espurna::wifi::settings::query::internal::forcedSleepLevel}}
};

// indexed settings for 'sta' connections
Expand Down Expand Up @@ -2369,6 +2445,20 @@ void access_point(::terminal::CommandContext&& ctx) {
terminalOK(ctx);
}

PROGMEM_STRING(Off, "WIFI.OFF");

void off(::terminal::CommandContext&& ctx) {
wifi::action(Action::TurnOff);
terminalOK(ctx);
}

PROGMEM_STRING(On, "WIFI.ON");

void on(::terminal::CommandContext&& ctx) {
wifi::action(Action::TurnOn);
terminalOK(ctx);
}

PROGMEM_STRING(Scan, "WIFI.SCAN");

void scan(::terminal::CommandContext&& ctx) {
Expand Down Expand Up @@ -2396,6 +2486,8 @@ static constexpr ::terminal::Command List[] PROGMEM {
{Station, commands::station},
{AccessPoint, commands::access_point},
{Scan, commands::scan},
{Off, commands::off},
{On, commands::on},
};

} // namespace commands
Expand Down Expand Up @@ -2698,7 +2790,14 @@ State handleAction(State& state, Action action) {
wifi::sta::disable();
wifi::disable();
publish(wifi::Event::Mode);
if (!wifi::sleep()) {

const auto type = settings::forcedSleep();
if (!wifi::sleep(type)) {
wifi::action(wifi::Action::TurnOn);
break;
}

if (type == LIGHT_SLEEP_T) {
wifi::action(wifi::Action::TurnOn);
break;
}
Expand Down

0 comments on commit bb4e74d

Please sign in to comment.