1
0
mirror of https://github.com/esphome/esphome.git synced 2025-02-22 12:58:15 +00:00

handle bad connection and reboot_timeout

This commit is contained in:
oarcher 2024-08-15 12:18:22 +02:00
parent 3102f6bfd5
commit 2c9c6aea30
4 changed files with 107 additions and 75 deletions

View File

@ -14,6 +14,7 @@ from esphome.const import (
CONF_ON_CONNECT, CONF_ON_CONNECT,
CONF_ON_DISCONNECT, CONF_ON_DISCONNECT,
CONF_PASSWORD, CONF_PASSWORD,
CONF_REBOOT_TIMEOUT,
CONF_RX_PIN, CONF_RX_PIN,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_TX_PIN, CONF_TX_PIN,
@ -88,6 +89,9 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_ENABLE_ON_BOOT, default=True): cv.boolean, cv.Optional(CONF_ENABLE_ON_BOOT, default=True): cv.boolean,
cv.Optional(CONF_ENABLE_CMUX, default=False): cv.boolean, cv.Optional(CONF_ENABLE_CMUX, default=False): cv.boolean,
cv.Optional(CONF_DEBUG, default=False): cv.boolean, cv.Optional(CONF_DEBUG, default=False): cv.boolean,
cv.Optional(
CONF_REBOOT_TIMEOUT, default="10min"
): cv.positive_time_period_milliseconds,
cv.Optional(CONF_ON_NOT_RESPONDING): automation.validate_automation( cv.Optional(CONF_ON_NOT_RESPONDING): automation.validate_automation(
{ {
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
@ -168,6 +172,9 @@ async def to_code(config):
cg.add_define("USE_MODEM") cg.add_define("USE_MODEM")
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
if use_address := config.get(CONF_USE_ADDRESS, None): if use_address := config.get(CONF_USE_ADDRESS, None):
cg.add(var.set_use_address(use_address)) cg.add(var.set_use_address(use_address))

View File

@ -54,6 +54,21 @@ void ModemComponent::enable_debug() {
// esp_log_level_set("CMUX Received", ESP_LOG_VERBOSE); // esp_log_level_set("CMUX Received", ESP_LOG_VERBOSE);
} }
bool ModemComponent::is_modem_connected(bool verbose) {
float rssi, ber;
int network_mode = 0;
bool network_attached = this->is_network_attached_();
this->get_signal_quality(rssi, ber);
this->dce->get_network_system_mode(network_mode);
bool connected = (network_mode != 0) && (!std::isnan(rssi)) && network_attached;
ESP_LOGD(TAG, "Modem internal network status: %s (attached: %s, type: %s, rssi: %.0fdB %s, ber: %.0f%%)",
connected ? "Good" : "BAD", network_attached ? "Yes" : "NO",
network_system_mode_to_string(network_mode).c_str(), rssi, get_signal_bars(rssi).c_str(), ber);
return connected;
}
AtCommandResult ModemComponent::send_at(const std::string &cmd, uint32_t timeout) { AtCommandResult ModemComponent::send_at(const std::string &cmd, uint32_t timeout) {
AtCommandResult at_command_result; AtCommandResult at_command_result;
at_command_result.success = false; at_command_result.success = false;
@ -141,13 +156,13 @@ void ModemComponent::enable() {
if (this->component_state_ == ModemComponentState::DISABLED) { if (this->component_state_ == ModemComponentState::DISABLED) {
this->component_state_ = ModemComponentState::DISCONNECTED; this->component_state_ = ModemComponentState::DISCONNECTED;
} }
this->internal_state_.start = true;
this->internal_state_.enabled = true; this->internal_state_.enabled = true;
} }
void ModemComponent::disable() { void ModemComponent::disable() {
ESP_LOGD(TAG, "Disabling modem"); ESP_LOGD(TAG, "Disabling modem");
this->internal_state_.enabled = false; this->internal_state_.enabled = false;
this->internal_state_.starting = false;
if (this->component_state_ != ModemComponentState::CONNECTED) { if (this->component_state_ != ModemComponentState::CONNECTED) {
this->component_state_ = ModemComponentState::DISCONNECTED; this->component_state_ = ModemComponentState::DISCONNECTED;
} }
@ -156,14 +171,32 @@ void ModemComponent::disable() {
void ModemComponent::reconnect() { void ModemComponent::reconnect() {
if (!this->internal_state_.reconnect) { if (!this->internal_state_.reconnect) {
this->internal_state_.reconnect = true; this->internal_state_.reconnect = true;
this->component_state_ = ModemComponentState::NOT_RESPONDING; this->internal_state_.connected = false;
this->component_state_ = ModemComponentState::DISCONNECTED;
// if reconnect fail, let some time before retry // if reconnect fail, let some time before retry
set_timeout(120000, [this]() { this->internal_state_.reconnect = false; }); set_timeout("retry_reconnect", this->reconnect_grace_period_,
[this]() { this->internal_state_.reconnect = false; });
} else { } else {
ESP_LOGD(TAG, "Reconnecting already in progress."); ESP_LOGD(TAG, "Reconnecting already in progress.");
} }
} }
bool ModemComponent::get_signal_quality(float &rssi, float &ber) {
rssi = NAN;
ber = NAN;
int modem_rssi = 99;
int modem_ber = 99;
if (this->modem_ready() &&
(global_modem_component->dce->get_signal_quality(modem_rssi, modem_ber) == command_result::OK)) {
if (modem_rssi != 99)
rssi = -113 + (modem_rssi * 2);
if (modem_ber != 99)
ber = 0.1f * (modem_ber * modem_ber);
return true;
}
return false;
}
network::IPAddresses ModemComponent::get_ip_addresses() { network::IPAddresses ModemComponent::get_ip_addresses() {
network::IPAddresses addresses; network::IPAddresses addresses;
esp_netif_ip_info_t ip; esp_netif_ip_info_t ip;
@ -243,7 +276,6 @@ void ModemComponent::loop() {
static uint32_t next_loop_millis = millis(); static uint32_t next_loop_millis = millis();
static uint32_t last_health_check = millis(); static uint32_t last_health_check = millis();
static bool connecting = false; static bool connecting = false;
static uint8_t network_attach_retry = 10;
if ((millis() < next_loop_millis)) { if ((millis() < next_loop_millis)) {
// some commands need some delay // some commands need some delay
@ -265,13 +297,13 @@ void ModemComponent::loop() {
ESP_LOGD(TAG, "Will check that the modem is on in %.1fs...", float(this->power_tonuart_) / 1000); ESP_LOGD(TAG, "Will check that the modem is on in %.1fs...", float(this->power_tonuart_) / 1000);
break; break;
case ModemPowerState::TONUART: case ModemPowerState::TONUART:
this->internal_state_.power_transition = false;
ESP_LOGD(TAG, "TONUART check sync"); ESP_LOGD(TAG, "TONUART check sync");
if (!this->modem_sync_()) { if (!this->modem_sync_()) {
ESP_LOGE(TAG, "Unable to power on the modem"); ESP_LOGE(TAG, "Unable to power on the modem");
} else { } else {
ESP_LOGI(TAG, "Modem powered ON"); ESP_LOGI(TAG, "Modem powered ON");
} }
this->internal_state_.power_transition = false;
break; break;
case ModemPowerState::TOFF: case ModemPowerState::TOFF:
delay(10); delay(10);
@ -301,23 +333,14 @@ void ModemComponent::loop() {
switch (this->component_state_) { switch (this->component_state_) {
case ModemComponentState::NOT_RESPONDING: case ModemComponentState::NOT_RESPONDING:
if (this->internal_state_.start) { if (this->internal_state_.starting) {
if (this->modem_ready(true) && !this->internal_state_.connected) { ESP_LOGW(TAG, "Modem not responding, resetting...");
ESP_LOGI(TAG, "Modem recovered"); this->internal_state_.connected = false;
this->status_clear_warning(); // this->modem_lazy_init_();
this->component_state_ = ModemComponentState::DISCONNECTED; if (!this->modem_sync_()) {
ESP_LOGE(TAG, "Unable to recover modem");
} else { } else {
ESP_LOGI(TAG, "Resetting modem"); this->component_state_ = ModemComponentState::DISCONNECTED;
this->internal_state_.connected = false;
this->modem_lazy_init_();
if (!this->modem_sync_()) {
ESP_LOGE(TAG, "Unable to recover modem");
} else {
this->component_state_ = ModemComponentState::DISCONNECTED;
}
// if (!this->internal_state_.powered_on) {
// this->poweron_();
// }
} }
} }
break; break;
@ -330,31 +353,30 @@ void ModemComponent::loop() {
break; break;
} else if (!this->internal_state_.modem_synced) { } else if (!this->internal_state_.modem_synced) {
if (!this->modem_sync_()) { if (!this->modem_sync_()) {
ESP_LOGE(TAG, "Modem not responding");
this->component_state_ = ModemComponentState::NOT_RESPONDING; this->component_state_ = ModemComponentState::NOT_RESPONDING;
} }
} }
if (this->internal_state_.start) { if (this->internal_state_.starting) {
float time_left_s = float(this->timeout_ - (millis() - this->internal_state_.startms)) / 1000;
// want to connect // want to connect
if ((millis() - this->internal_state_.startms) > this->timeout_) {
this->abort_("Timeout while trying to connect");
}
if (!connecting) { if (!connecting) {
// wait for the modem be attached to a network, start ppp, and set connecting=true // wait for the modem be attached to a network, start ppp, and set connecting=true
if (is_network_attached_()) { if (this->is_modem_connected()) {
network_attach_retry = 10;
if (this->start_ppp_()) { if (this->start_ppp_()) {
connecting = true; connecting = true;
next_loop_millis = millis() + 2000; // delay for next loop } else {
ESP_LOGE(TAG, "modem is unable to enter PPP (time left before abort: %.0fs)", time_left_s);
this->stop_ppp_();
this->is_modem_connected();
} }
} else { } else {
ESP_LOGD(TAG, "Waiting for the modem to be attached to a network (left retries: %" PRIu8 ")", ESP_LOGW(TAG, "Waiting for the modem to be attached to a network (time left before abort: %.0fs)",
network_attach_retry); time_left_s);
network_attach_retry--; next_loop_millis = millis() + 5000; // delay to retry
if (network_attach_retry == 0) {
ESP_LOGE(TAG, "modem is unable to attach to a network");
network_attach_retry = 10;
this->component_state_ = ModemComponentState::NOT_RESPONDING;
}
next_loop_millis = millis() + 1000; // delay to retry
} }
} else { } else {
// connecting // connecting
@ -378,7 +400,8 @@ void ModemComponent::loop() {
} }
} }
} else { } else {
this->internal_state_.start = true; this->internal_state_.starting = true;
this->internal_state_.startms = millis();
} }
} else { } else {
this->component_state_ = ModemComponentState::DISABLED; this->component_state_ = ModemComponentState::DISABLED;
@ -386,21 +409,19 @@ void ModemComponent::loop() {
break; break;
case ModemComponentState::CONNECTED: case ModemComponentState::CONNECTED:
this->internal_state_.starting = false;
if (this->internal_state_.enabled) { if (this->internal_state_.enabled) {
if (!this->internal_state_.connected) { if (!this->internal_state_.connected) {
this->status_set_warning("Connection via Modem lost!"); this->status_set_warning("Connection via Modem lost!");
this->component_state_ = ModemComponentState::DISCONNECTED; this->component_state_ = ModemComponentState::DISCONNECTED;
break; break;
} }
// clear flags if previously set by this->reconnect()
this->internal_state_.reconnect = false;
if (this->cmux_ && (millis() - last_health_check) > 30000) { if (this->cmux_ && (millis() - last_health_check) > 30000) {
ESP_LOGD(TAG, "modem health check");
last_health_check = millis(); last_health_check = millis();
if (!this->get_imei()) { if (!this->is_modem_connected()) {
ESP_LOGW(TAG, "modem health check failed"); ESP_LOGW(TAG, "Reconnecting...");
this->component_state_ = ModemComponentState::NOT_RESPONDING; this->reconnect();
} }
} }
} else { } else {
@ -626,7 +647,9 @@ bool ModemComponent::is_network_attached_() {
bool ModemComponent::start_ppp_() { bool ModemComponent::start_ppp_() {
this->internal_state_.connect_begin = millis(); this->internal_state_.connect_begin = millis();
this->status_set_warning("Starting connection"); this->status_set_warning("Starting connection");
watchdog::WatchdogManager wdt(10000); watchdog::WatchdogManager wdt(15000); // mini 10000
uint32_t now = millis();
// will be set to true on event IP_EVENT_PPP_GOT_IP // will be set to true on event IP_EVENT_PPP_GOT_IP
this->internal_state_.got_ipv4_address = false; this->internal_state_.got_ipv4_address = false;
@ -643,7 +666,9 @@ bool ModemComponent::start_ppp_() {
} }
if (!status) { if (!status) {
ESP_LOGE(TAG, "Unable to change modem mode to PPP"); ESP_LOGE(TAG, "Unable to change modem mode to PPP after %" PRIu32 "ms", millis() - now);
} else {
ESP_LOGD(TAG, "Entered PPP after %" PRIu32 "ms", millis() - now);
} }
return status; return status;
@ -658,7 +683,7 @@ bool ModemComponent::stop_ppp_() {
status = this->dce->set_mode(modem_mode::COMMAND_MODE); status = this->dce->set_mode(modem_mode::COMMAND_MODE);
} }
if (!status) { if (!status) {
ESP_LOGE(TAG, "Error exiting PPP"); ESP_LOGW(TAG, "Error exiting PPP");
} }
return status; return status;
} }
@ -709,6 +734,12 @@ void ModemComponent::poweroff_() {
} }
} }
void ModemComponent::abort_(const std::string &message) {
ESP_LOGE(TAG, "Aborting: %s", message.c_str());
this->send_at("AT+CFUN=1,1");
App.reboot();
}
void ModemComponent::dump_connect_params_() { void ModemComponent::dump_connect_params_() {
if (!this->internal_state_.connected) { if (!this->internal_state_.connected) {
ESP_LOGCONFIG(TAG, "Modem connection: Not connected"); ESP_LOGCONFIG(TAG, "Modem connection: Not connected");
@ -716,11 +747,11 @@ void ModemComponent::dump_connect_params_() {
} }
esp_netif_ip_info_t ip; esp_netif_ip_info_t ip;
esp_netif_get_ip_info(this->ppp_netif_, &ip); esp_netif_get_ip_info(this->ppp_netif_, &ip);
ESP_LOGCONFIG(TAG, "Modem connection:"); ESP_LOGI(TAG, "Modem connection:");
ESP_LOGCONFIG(TAG, " IP Address : %s", network::IPAddress(&ip.ip).str().c_str()); ESP_LOGI(TAG, " IP Address : %s", network::IPAddress(&ip.ip).str().c_str());
ESP_LOGCONFIG(TAG, " Hostname : '%s'", App.get_name().c_str()); ESP_LOGI(TAG, " Hostname : '%s'", App.get_name().c_str());
ESP_LOGCONFIG(TAG, " Subnet : %s", network::IPAddress(&ip.netmask).str().c_str()); ESP_LOGI(TAG, " Subnet : %s", network::IPAddress(&ip.netmask).str().c_str());
ESP_LOGCONFIG(TAG, " Gateway : %s", network::IPAddress(&ip.gw).str().c_str()); ESP_LOGI(TAG, " Gateway : %s", network::IPAddress(&ip.gw).str().c_str());
const ip_addr_t *dns_main_ip = dns_getserver(ESP_NETIF_DNS_MAIN); const ip_addr_t *dns_main_ip = dns_getserver(ESP_NETIF_DNS_MAIN);
const ip_addr_t *dns_backup_ip = dns_getserver(ESP_NETIF_DNS_BACKUP); const ip_addr_t *dns_backup_ip = dns_getserver(ESP_NETIF_DNS_BACKUP);

View File

@ -52,6 +52,7 @@ struct AtCommandResult {
class ModemComponent : public Component { class ModemComponent : public Component {
public: public:
void set_reboot_timeout(uint16_t timeout) { this->timeout_ = timeout; }
void set_use_address(const std::string &use_address) { this->use_address_ = use_address; } void set_use_address(const std::string &use_address) { this->use_address_ = use_address; }
void set_rx_pin(InternalGPIOPin *rx_pin) { this->rx_pin_ = rx_pin; } void set_rx_pin(InternalGPIOPin *rx_pin) { this->rx_pin_ = rx_pin; }
void set_tx_pin(InternalGPIOPin *tx_pin) { this->tx_pin_ = tx_pin; } void set_tx_pin(InternalGPIOPin *tx_pin) { this->tx_pin_ = tx_pin; }
@ -67,8 +68,10 @@ class ModemComponent : public Component {
void enable_cmux() { this->cmux_ = true; } void enable_cmux() { this->cmux_ = true; }
void enable_debug(); void enable_debug();
void add_init_at_command(const std::string &cmd) { this->init_at_commands_.push_back(cmd); } void add_init_at_command(const std::string &cmd) { this->init_at_commands_.push_back(cmd); }
bool is_connected() { return this->component_state_ == ModemComponentState::CONNECTED; } bool is_connected() { return this->internal_state_.connected; }
bool is_disabled() { return this->component_state_ == ModemComponentState::DISABLED; } bool is_disabled() { return this->component_state_ == ModemComponentState::DISABLED; }
bool is_modem_connected(bool verbose); // this if for modem only, not PPP
bool is_modem_connected() { return this->is_modem_connected(true); }
AtCommandResult send_at(const std::string &cmd, uint32_t timeout); AtCommandResult send_at(const std::string &cmd, uint32_t timeout);
AtCommandResult send_at(const std::string &cmd); AtCommandResult send_at(const std::string &cmd);
AtCommandResult get_imei(); AtCommandResult get_imei();
@ -78,6 +81,7 @@ class ModemComponent : public Component {
void enable(); void enable();
void disable(); void disable();
void reconnect(); void reconnect();
bool get_signal_quality(float &rssi, float &ber);
network::IPAddresses get_ip_addresses(); network::IPAddresses get_ip_addresses();
std::string get_use_address() const; std::string get_use_address() const;
@ -90,12 +94,7 @@ class ModemComponent : public Component {
void loop() override; void loop() override;
void dump_config() override { this->dump_connect_params_(); } void dump_config() override { this->dump_connect_params_(); }
float get_setup_priority() const override { return setup_priority::WIFI + 1; } // just before WIFI float get_setup_priority() const override { return setup_priority::WIFI + 1; } // just before WIFI
bool can_proceed() override { bool can_proceed() override { return network::is_disabled() || this->is_connected(); };
if (!this->internal_state_.enabled) {
return true;
}
return this->is_connected();
};
void add_on_state_callback(std::function<void(ModemComponentState, ModemComponentState)> &&callback) { void add_on_state_callback(std::function<void(ModemComponentState, ModemComponentState)> &&callback) {
this->on_state_callback_.add(std::move(callback)); this->on_state_callback_.add(std::move(callback));
} }
@ -113,10 +112,12 @@ class ModemComponent : public Component {
bool stop_ppp_(); bool stop_ppp_();
void poweron_(); void poweron_();
void poweroff_(); void poweroff_();
void abort_(const std::string &message);
static void ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); static void ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
void dump_connect_params_(); void dump_connect_params_();
// Attributes from yaml config // Attributes from yaml config
uint16_t timeout_;
InternalGPIOPin *tx_pin_; InternalGPIOPin *tx_pin_;
InternalGPIOPin *rx_pin_; InternalGPIOPin *rx_pin_;
std::string model_; std::string model_;
@ -142,7 +143,7 @@ class ModemComponent : public Component {
size_t uart_event_task_stack_size_ = 4096; // 2000-6000 size_t uart_event_task_stack_size_ = 4096; // 2000-6000
uint8_t uart_event_task_priority_ = 5; // 3-22 uint8_t uart_event_task_priority_ = 5; // 3-22
uint32_t command_delay_ = 1000; // timeout for AT commands uint32_t command_delay_ = 1000; // timeout for AT commands
uint32_t update_interval_ = 60 * 1000; uint32_t reconnect_grace_period_ = 30000; // let some time to mqtt or api to reconnect before retry
// Changes will trigger user callback // Changes will trigger user callback
ModemComponentState component_state_{ModemComponentState::DISABLED}; ModemComponentState component_state_{ModemComponentState::DISABLED};
@ -153,7 +154,9 @@ class ModemComponent : public Component {
esp_netif_t *ppp_netif_{nullptr}; esp_netif_t *ppp_netif_{nullptr};
struct InternalState { struct InternalState {
bool start{false}; bool starting{false};
// trying to connect timestamp (for timeout)
uint32_t startms;
bool enabled{false}; bool enabled{false};
bool connected{false}; bool connected{false};
bool got_ipv4_address{false}; bool got_ipv4_address{false};

View File

@ -47,23 +47,14 @@ void ModemSensor::update() {
} }
void ModemSensor::update_signal_sensors_() { void ModemSensor::update_signal_sensors_() {
float rssi = NAN;
float ber = NAN;
if (this->rssi_sensor_ || this->ber_sensor_) { if (this->rssi_sensor_ || this->ber_sensor_) {
int modem_rssi = 99; float rssi;
int modem_ber = 99; float ber;
if (global_modem_component->modem_ready() && global_modem_component->get_signal_quality(rssi, ber);
(global_modem_component->dce->get_signal_quality(modem_rssi, modem_ber) == command_result::OK)) { if (this->rssi_sensor_)
if (modem_rssi != 99) this->rssi_sensor_->publish_state(rssi);
rssi = -113 + (modem_rssi * 2); if (this->ber_sensor_)
if (modem_ber != 99) this->ber_sensor_->publish_state(ber);
ber = 0.1f * (modem_ber * modem_ber);
if (this->rssi_sensor_)
this->rssi_sensor_->publish_state(rssi);
if (this->ber_sensor_)
this->ber_sensor_->publish_state(ber);
}
} }
} }