mirror of
https://github.com/esphome/esphome.git
synced 2024-12-24 14:34:54 +01:00
handle bad connection and reboot_timeout
This commit is contained in:
parent
3102f6bfd5
commit
2c9c6aea30
4 changed files with 107 additions and 75 deletions
|
@ -14,6 +14,7 @@ from esphome.const import (
|
|||
CONF_ON_CONNECT,
|
||||
CONF_ON_DISCONNECT,
|
||||
CONF_PASSWORD,
|
||||
CONF_REBOOT_TIMEOUT,
|
||||
CONF_RX_PIN,
|
||||
CONF_TRIGGER_ID,
|
||||
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_CMUX, 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.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
|
@ -168,6 +172,9 @@ async def to_code(config):
|
|||
cg.add_define("USE_MODEM")
|
||||
|
||||
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):
|
||||
cg.add(var.set_use_address(use_address))
|
||||
|
||||
|
|
|
@ -54,6 +54,21 @@ void ModemComponent::enable_debug() {
|
|||
// 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 at_command_result;
|
||||
at_command_result.success = false;
|
||||
|
@ -141,13 +156,13 @@ void ModemComponent::enable() {
|
|||
if (this->component_state_ == ModemComponentState::DISABLED) {
|
||||
this->component_state_ = ModemComponentState::DISCONNECTED;
|
||||
}
|
||||
this->internal_state_.start = true;
|
||||
this->internal_state_.enabled = true;
|
||||
}
|
||||
|
||||
void ModemComponent::disable() {
|
||||
ESP_LOGD(TAG, "Disabling modem");
|
||||
this->internal_state_.enabled = false;
|
||||
this->internal_state_.starting = false;
|
||||
if (this->component_state_ != ModemComponentState::CONNECTED) {
|
||||
this->component_state_ = ModemComponentState::DISCONNECTED;
|
||||
}
|
||||
|
@ -156,14 +171,32 @@ void ModemComponent::disable() {
|
|||
void ModemComponent::reconnect() {
|
||||
if (!this->internal_state_.reconnect) {
|
||||
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
|
||||
set_timeout(120000, [this]() { this->internal_state_.reconnect = false; });
|
||||
set_timeout("retry_reconnect", this->reconnect_grace_period_,
|
||||
[this]() { this->internal_state_.reconnect = false; });
|
||||
} else {
|
||||
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 addresses;
|
||||
esp_netif_ip_info_t ip;
|
||||
|
@ -243,7 +276,6 @@ void ModemComponent::loop() {
|
|||
static uint32_t next_loop_millis = millis();
|
||||
static uint32_t last_health_check = millis();
|
||||
static bool connecting = false;
|
||||
static uint8_t network_attach_retry = 10;
|
||||
|
||||
if ((millis() < next_loop_millis)) {
|
||||
// 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);
|
||||
break;
|
||||
case ModemPowerState::TONUART:
|
||||
this->internal_state_.power_transition = false;
|
||||
ESP_LOGD(TAG, "TONUART check sync");
|
||||
if (!this->modem_sync_()) {
|
||||
ESP_LOGE(TAG, "Unable to power on the modem");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Modem powered ON");
|
||||
}
|
||||
this->internal_state_.power_transition = false;
|
||||
break;
|
||||
case ModemPowerState::TOFF:
|
||||
delay(10);
|
||||
|
@ -301,23 +333,14 @@ void ModemComponent::loop() {
|
|||
|
||||
switch (this->component_state_) {
|
||||
case ModemComponentState::NOT_RESPONDING:
|
||||
if (this->internal_state_.start) {
|
||||
if (this->modem_ready(true) && !this->internal_state_.connected) {
|
||||
ESP_LOGI(TAG, "Modem recovered");
|
||||
this->status_clear_warning();
|
||||
this->component_state_ = ModemComponentState::DISCONNECTED;
|
||||
if (this->internal_state_.starting) {
|
||||
ESP_LOGW(TAG, "Modem not responding, resetting...");
|
||||
this->internal_state_.connected = false;
|
||||
// this->modem_lazy_init_();
|
||||
if (!this->modem_sync_()) {
|
||||
ESP_LOGE(TAG, "Unable to recover modem");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Resetting modem");
|
||||
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_();
|
||||
// }
|
||||
this->component_state_ = ModemComponentState::DISCONNECTED;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -330,31 +353,30 @@ void ModemComponent::loop() {
|
|||
break;
|
||||
} else if (!this->internal_state_.modem_synced) {
|
||||
if (!this->modem_sync_()) {
|
||||
ESP_LOGE(TAG, "Modem 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
|
||||
if ((millis() - this->internal_state_.startms) > this->timeout_) {
|
||||
this->abort_("Timeout while trying to connect");
|
||||
}
|
||||
if (!connecting) {
|
||||
// wait for the modem be attached to a network, start ppp, and set connecting=true
|
||||
if (is_network_attached_()) {
|
||||
network_attach_retry = 10;
|
||||
if (this->is_modem_connected()) {
|
||||
if (this->start_ppp_()) {
|
||||
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 {
|
||||
ESP_LOGD(TAG, "Waiting for the modem to be attached to a network (left retries: %" PRIu8 ")",
|
||||
network_attach_retry);
|
||||
network_attach_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
|
||||
ESP_LOGW(TAG, "Waiting for the modem to be attached to a network (time left before abort: %.0fs)",
|
||||
time_left_s);
|
||||
next_loop_millis = millis() + 5000; // delay to retry
|
||||
}
|
||||
} else {
|
||||
// connecting
|
||||
|
@ -378,7 +400,8 @@ void ModemComponent::loop() {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
this->internal_state_.start = true;
|
||||
this->internal_state_.starting = true;
|
||||
this->internal_state_.startms = millis();
|
||||
}
|
||||
} else {
|
||||
this->component_state_ = ModemComponentState::DISABLED;
|
||||
|
@ -386,21 +409,19 @@ void ModemComponent::loop() {
|
|||
break;
|
||||
|
||||
case ModemComponentState::CONNECTED:
|
||||
this->internal_state_.starting = false;
|
||||
if (this->internal_state_.enabled) {
|
||||
if (!this->internal_state_.connected) {
|
||||
this->status_set_warning("Connection via Modem lost!");
|
||||
this->component_state_ = ModemComponentState::DISCONNECTED;
|
||||
break;
|
||||
}
|
||||
// clear flags if previously set by this->reconnect()
|
||||
this->internal_state_.reconnect = false;
|
||||
|
||||
if (this->cmux_ && (millis() - last_health_check) > 30000) {
|
||||
ESP_LOGD(TAG, "modem health check");
|
||||
last_health_check = millis();
|
||||
if (!this->get_imei()) {
|
||||
ESP_LOGW(TAG, "modem health check failed");
|
||||
this->component_state_ = ModemComponentState::NOT_RESPONDING;
|
||||
if (!this->is_modem_connected()) {
|
||||
ESP_LOGW(TAG, "Reconnecting...");
|
||||
this->reconnect();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -626,7 +647,9 @@ bool ModemComponent::is_network_attached_() {
|
|||
bool ModemComponent::start_ppp_() {
|
||||
this->internal_state_.connect_begin = millis();
|
||||
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
|
||||
this->internal_state_.got_ipv4_address = false;
|
||||
|
@ -643,7 +666,9 @@ bool ModemComponent::start_ppp_() {
|
|||
}
|
||||
|
||||
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;
|
||||
|
@ -658,7 +683,7 @@ bool ModemComponent::stop_ppp_() {
|
|||
status = this->dce->set_mode(modem_mode::COMMAND_MODE);
|
||||
}
|
||||
if (!status) {
|
||||
ESP_LOGE(TAG, "Error exiting PPP");
|
||||
ESP_LOGW(TAG, "Error exiting PPP");
|
||||
}
|
||||
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_() {
|
||||
if (!this->internal_state_.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_get_ip_info(this->ppp_netif_, &ip);
|
||||
ESP_LOGCONFIG(TAG, "Modem connection:");
|
||||
ESP_LOGCONFIG(TAG, " IP Address : %s", network::IPAddress(&ip.ip).str().c_str());
|
||||
ESP_LOGCONFIG(TAG, " Hostname : '%s'", App.get_name().c_str());
|
||||
ESP_LOGCONFIG(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, "Modem connection:");
|
||||
ESP_LOGI(TAG, " IP Address : %s", network::IPAddress(&ip.ip).str().c_str());
|
||||
ESP_LOGI(TAG, " Hostname : '%s'", App.get_name().c_str());
|
||||
ESP_LOGI(TAG, " Subnet : %s", network::IPAddress(&ip.netmask).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_backup_ip = dns_getserver(ESP_NETIF_DNS_BACKUP);
|
||||
|
|
|
@ -52,6 +52,7 @@ struct AtCommandResult {
|
|||
|
||||
class ModemComponent : public Component {
|
||||
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_rx_pin(InternalGPIOPin *rx_pin) { this->rx_pin_ = rx_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_debug();
|
||||
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_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);
|
||||
AtCommandResult get_imei();
|
||||
|
@ -78,6 +81,7 @@ class ModemComponent : public Component {
|
|||
void enable();
|
||||
void disable();
|
||||
void reconnect();
|
||||
bool get_signal_quality(float &rssi, float &ber);
|
||||
|
||||
network::IPAddresses get_ip_addresses();
|
||||
std::string get_use_address() const;
|
||||
|
@ -90,12 +94,7 @@ class ModemComponent : public Component {
|
|||
void loop() override;
|
||||
void dump_config() override { this->dump_connect_params_(); }
|
||||
float get_setup_priority() const override { return setup_priority::WIFI + 1; } // just before WIFI
|
||||
bool can_proceed() override {
|
||||
if (!this->internal_state_.enabled) {
|
||||
return true;
|
||||
}
|
||||
return this->is_connected();
|
||||
};
|
||||
bool can_proceed() override { return network::is_disabled() || this->is_connected(); };
|
||||
void add_on_state_callback(std::function<void(ModemComponentState, ModemComponentState)> &&callback) {
|
||||
this->on_state_callback_.add(std::move(callback));
|
||||
}
|
||||
|
@ -113,10 +112,12 @@ class ModemComponent : public Component {
|
|||
bool stop_ppp_();
|
||||
void poweron_();
|
||||
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);
|
||||
void dump_connect_params_();
|
||||
|
||||
// Attributes from yaml config
|
||||
uint16_t timeout_;
|
||||
InternalGPIOPin *tx_pin_;
|
||||
InternalGPIOPin *rx_pin_;
|
||||
std::string model_;
|
||||
|
@ -142,7 +143,7 @@ class ModemComponent : public Component {
|
|||
size_t uart_event_task_stack_size_ = 4096; // 2000-6000
|
||||
uint8_t uart_event_task_priority_ = 5; // 3-22
|
||||
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
|
||||
ModemComponentState component_state_{ModemComponentState::DISABLED};
|
||||
|
@ -153,7 +154,9 @@ class ModemComponent : public Component {
|
|||
esp_netif_t *ppp_netif_{nullptr};
|
||||
|
||||
struct InternalState {
|
||||
bool start{false};
|
||||
bool starting{false};
|
||||
// trying to connect timestamp (for timeout)
|
||||
uint32_t startms;
|
||||
bool enabled{false};
|
||||
bool connected{false};
|
||||
bool got_ipv4_address{false};
|
||||
|
|
|
@ -47,23 +47,14 @@ void ModemSensor::update() {
|
|||
}
|
||||
|
||||
void ModemSensor::update_signal_sensors_() {
|
||||
float rssi = NAN;
|
||||
float ber = NAN;
|
||||
if (this->rssi_sensor_ || this->ber_sensor_) {
|
||||
int modem_rssi = 99;
|
||||
int modem_ber = 99;
|
||||
if (global_modem_component->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);
|
||||
|
||||
if (this->rssi_sensor_)
|
||||
this->rssi_sensor_->publish_state(rssi);
|
||||
if (this->ber_sensor_)
|
||||
this->ber_sensor_->publish_state(ber);
|
||||
}
|
||||
float rssi;
|
||||
float ber;
|
||||
global_modem_component->get_signal_quality(rssi, ber);
|
||||
if (this->rssi_sensor_)
|
||||
this->rssi_sensor_->publish_state(rssi);
|
||||
if (this->ber_sensor_)
|
||||
this->ber_sensor_->publish_state(ber);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue