user power states

This commit is contained in:
oarcher 2024-08-01 03:35:27 +02:00
parent 5bc6cd8a17
commit 4b0ae086dc
2 changed files with 115 additions and 70 deletions

View file

@ -86,10 +86,15 @@ void ModemComponent::setup() {
if (this->power_pin_) { if (this->power_pin_) {
this->power_pin_->setup(); this->power_pin_->setup();
// as we have a power pin, we assume that the power is off
this->powered_on_ = false;
if (this->enabled_) { if (this->enabled_) {
this->poweron_(); this->poweron_();
} }
} else {
// no status pin, we assume that the power is on
this->powered_on_ = true;
} }
if (this->status_pin_) { if (this->status_pin_) {
@ -200,12 +205,14 @@ bool ModemComponent::modem_sync_() {
if (!this->watchdog_) if (!this->watchdog_)
this->watchdog_ = std::make_shared<watchdog::WatchdogManager>(60000); this->watchdog_ = std::make_shared<watchdog::WatchdogManager>(60000);
uint32_t start_ms = millis();
uint32_t elapsed_ms;
bool status = this->modem_ready(); bool status = this->modem_ready();
if (!status) { if (!status) {
// Try to exit CMUX_MANUAL_DATA or DATA_MODE, if any // Try to exit CMUX_MANUAL_DATA or DATA_MODE, if any
ESP_LOGD(TAG, "Connecting to the the modem..."); ESP_LOGD(TAG, "Connecting to the the modem...");
uint32_t start_ms = millis();
std::string result; std::string result;
auto command_mode_ = [this]() -> bool { auto command_mode_ = [this]() -> bool {
@ -228,15 +235,15 @@ bool ModemComponent::modem_sync_() {
status = command_mode_() || (cmux_command_mode_() && command_mode_()); status = command_mode_() || (cmux_command_mode_() && command_mode_());
} }
uint32_t elapsed_ms = millis() - start_ms; elapsed_ms = millis() - start_ms;
if (!status) { if (!status) {
ESP_LOGW(TAG, "modem not responding after %" PRIu32 "ms", elapsed_ms); ESP_LOGW(TAG, "modem not responding after %" PRIu32 "ms.", elapsed_ms);
if (this->power_pin_) { this->powered_on_ = false;
ESP_LOGD(TAG, "Trying to power cycle the modem");
this->poweroff_();
} }
} else { }
if (status) {
elapsed_ms = millis() - start_ms;
ESP_LOGD(TAG, "Connected to the modem in %" PRIu32 "ms", elapsed_ms); ESP_LOGD(TAG, "Connected to the modem in %" PRIu32 "ms", elapsed_ms);
this->send_init_at_(); this->send_init_at_();
if (!this->prepare_sim_()) { if (!this->prepare_sim_()) {
@ -244,7 +251,7 @@ bool ModemComponent::modem_sync_() {
this->disable(); this->disable();
} }
} }
}
return status; return status;
} }
@ -360,12 +367,64 @@ void ModemComponent::loop() {
static uint8_t network_attach_retry = 10; static uint8_t network_attach_retry = 10;
static uint8_t ip_lost_retries = 10; static uint8_t ip_lost_retries = 10;
if (this->power_transition_ || (millis() < next_loop_millis)) { if ((millis() < next_loop_millis)) {
// No loop on power transition, or if some commands need some delay // some commands need some delay
yield(); yield();
return; return;
} }
#ifdef USE_MODEM_POWER
if (this->power_transition_) {
if (!this->watchdog_)
this->watchdog_ = std::make_shared<watchdog::WatchdogManager>(60000);
// A power state is used to handle long tonuart/toffuart delay
switch (this->power_state_) {
case ModemPowerState::TON:
this->power_pin_->digital_write(false);
delay(USE_MODEM_POWER_TON);
this->power_pin_->digital_write(true);
next_loop_millis = millis() + USE_MODEM_POWER_TONUART; // delay for next loop
this->power_state_ = ModemPowerState::TONUART;
ESP_LOGD(TAG, "Will check that the modem is on in %.1fs...", float(USE_MODEM_POWER_TONUART) / 1000);
break;
case ModemPowerState::TONUART:
this->power_transition_ = false;
if (!this->modem_sync_()) {
ESP_LOGE(TAG, "Unable to power on the modem");
this->powered_on_ = false;
} else {
ESP_LOGI(TAG, "Modem powered ON");
this->powered_on_ = true;
this->watchdog_.reset();
}
break;
case ModemPowerState::TOFF:
delay(10);
this->power_pin_->digital_write(false);
delay(USE_MODEM_POWER_TOFF);
this->power_pin_->digital_write(true);
this->power_state_ = ModemPowerState::TOFFUART;
ESP_LOGD(TAG, "Will check that the modem is off in %.1fs...", float(USE_MODEM_POWER_TOFFUART) / 1000);
next_loop_millis = millis() + USE_MODEM_POWER_TOFFUART; // delay for next loop
break;
case ModemPowerState::TOFFUART:
this->power_transition_ = false;
if (this->modem_ready()) {
ESP_LOGE(TAG, "Unable to power off the modem");
this->powered_on_ = true;
} else {
ESP_LOGI(TAG, "Modem powered OFF");
this->powered_on_ = false;
this->watchdog_.reset();
}
break;
}
yield();
return;
}
#endif // USE_MODEM_POWER
switch (this->state_) { switch (this->state_) {
case ModemComponentState::NOT_RESPONDING: case ModemComponentState::NOT_RESPONDING:
if (this->start_) { if (this->start_) {
@ -373,10 +432,10 @@ void ModemComponent::loop() {
ESP_LOGI(TAG, "Modem recovered"); ESP_LOGI(TAG, "Modem recovered");
this->status_clear_warning(); this->status_clear_warning();
this->state_ = ModemComponentState::DISCONNECTED; this->state_ = ModemComponentState::DISCONNECTED;
last_state = this->state_; // disable on_disconnect cb
} else { } else {
if (!this->get_power_status()) { if (!this->powered_on_) {
// Modem is OFF. If poweron is needed, disconnect state will handle it. this->poweron_();
this->state_ = ModemComponentState::DISCONNECTED;
} else if (this->not_responding_cb_) { } else if (this->not_responding_cb_) {
if (!this->not_responding_cb_->is_action_running()) { if (!this->not_responding_cb_->is_action_running()) {
ESP_LOGD(TAG, "Calling 'on_not_responding' callback"); ESP_LOGD(TAG, "Calling 'on_not_responding' callback");
@ -392,6 +451,14 @@ void ModemComponent::loop() {
// disconnected, want to connect // disconnected, want to connect
case ModemComponentState::DISCONNECTED: case ModemComponentState::DISCONNECTED:
if (this->enabled_) { if (this->enabled_) {
if (last_state == ModemComponentState::DISABLED) {
last_state = this->state_; // disable on_disconnect cb
}
if (!this->powered_on_) {
this->poweron_();
break;
}
if (this->start_) { if (this->start_) {
if (connecting) { if (connecting) {
if (this->connected_) { if (this->connected_) {
@ -503,9 +570,11 @@ void ModemComponent::loop() {
case ModemComponentState::DISABLED: case ModemComponentState::DISABLED:
if (this->enabled_) { if (this->enabled_) {
this->state_ = ModemComponentState::DISCONNECTED; this->state_ = ModemComponentState::DISCONNECTED;
} // else if (this->get_power_status()) { // FIXME long time in loop because of get_power_status last_state = this->state_; // disable on_disconnect cb
// this->poweroff_(); ESP_LOGE(TAG, "here");
//} } else if (this->powered_on_) {
this->poweroff_();
}
next_loop_millis = millis() + 2000; // delay for next loop next_loop_millis = millis() + 2000; // delay for next loop
break; break;
} }
@ -551,30 +620,9 @@ bool ModemComponent::get_power_status() {
void ModemComponent::poweron_() { void ModemComponent::poweron_() {
#ifdef USE_MODEM_POWER #ifdef USE_MODEM_POWER
if (this->power_pin_) { this->power_state_ = ModemPowerState::TON;
App.feed_wdt();
ESP_LOGV(TAG, "Powering up modem with power_pin...");
this->power_transition_ = true; this->power_transition_ = true;
this->power_pin_->digital_write(false); return;
delay(USE_MODEM_POWER_TON);
this->power_pin_->digital_write(true);
// use a timout for long wait delay
ESP_LOGD(TAG, "Will check that the modem is on in %.1fs...", float(USE_MODEM_POWER_TONUART) / 1000);
this->set_timeout("wait_poweron", USE_MODEM_POWER_TONUART, [this]() {
// TODO: check modem is able to respond
// ESP_LOGD(TAG, "DTE/DCE init after poweron");
// this->create_dte_dce_();
delay(500); // NOLINT
App.feed_wdt();
if (!this->modem_ready()) {
ESP_LOGE(TAG, "Unable to power on the modem");
} else {
ESP_LOGD(TAG, "Modem ready after power ON");
}
this->power_transition_ = false;
yield();
});
}
#else #else
if (this->modem_ready()) { if (this->modem_ready()) {
ESP_LOGV(TAG, "Modem is already ON"); ESP_LOGV(TAG, "Modem is already ON");
@ -586,28 +634,9 @@ void ModemComponent::poweron_() {
void ModemComponent::poweroff_() { void ModemComponent::poweroff_() {
#ifdef USE_MODEM_POWER #ifdef USE_MODEM_POWER
if (this->power_pin_) { this->power_state_ = ModemPowerState::TOFF;
ESP_LOGV(TAG, "Powering off modem with power pin...");
App.feed_wdt();
this->power_transition_ = true; this->power_transition_ = true;
// watchdog::WatchdogManager wdt(60000); return;
// this->power_pin_->digital_write(true);
delay(10);
this->power_pin_->digital_write(false);
delay(USE_MODEM_POWER_TOFF);
this->power_pin_->digital_write(true);
ESP_LOGD(TAG, "Will check that the modem is off in %.1fs...", float(USE_MODEM_POWER_TOFFUART) / 1000);
this->set_timeout("wait_poweroff", USE_MODEM_POWER_TOFFUART, [this]() {
if (this->modem_ready()) {
ESP_LOGE(TAG, "Unable to power off the modem");
} else {
ESP_LOGD(TAG, "Modem OFF");
}
this->power_transition_ = false;
});
}
#endif // USE_MODEM_POWER #endif // USE_MODEM_POWER
} }
@ -701,7 +730,12 @@ bool ModemComponent::get_imei(std::string &result) {
bool ModemComponent::modem_ready() { bool ModemComponent::modem_ready() {
// check if the modem is ready to answer AT commands // check if the modem is ready to answer AT commands
std::string imei; std::string imei;
return this->get_imei(imei); if (this->get_imei(imei)) {
// we are sure that the modem is on
this->powered_on_ = true;
return true;
}
return false;
} }
void ModemComponent::add_on_state_callback(std::function<void(ModemComponentState)> &&callback) { void ModemComponent::add_on_state_callback(std::function<void(ModemComponentState)> &&callback) {

View file

@ -38,6 +38,13 @@ enum class ModemComponentState {
DISABLED, DISABLED,
}; };
enum class ModemPowerState {
TON,
TONUART,
TOFF,
TOFFUART,
};
class ModemComponent : public Component { class ModemComponent : public Component {
public: public:
ModemComponent(); ModemComponent();
@ -112,7 +119,11 @@ class ModemComponent : public Component {
// timeout for AT commands // timeout for AT commands
uint32_t command_delay_ = 500; uint32_t command_delay_ = 500;
// Will be true when power transitionning // Will be true when power transitionning
bool power_transition_ = false; bool power_transition_{false};
// guess power state
bool powered_on_{false};
// states for triggering on/off signals
ModemPowerState power_state_{ModemPowerState::TOFFUART};
// separate handler for `on_not_responding` (we want to know when it's ended) // separate handler for `on_not_responding` (we want to know when it's ended)
Trigger<> *not_responding_cb_{nullptr}; Trigger<> *not_responding_cb_{nullptr};
CallbackManager<void(ModemComponentState)> on_state_callback_; CallbackManager<void(ModemComponentState)> on_state_callback_;