mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	refactor
This commit is contained in:
		| @@ -51,15 +51,82 @@ ModemComponent::ModemComponent() { | |||||||
|   global_modem_component = this; |   global_modem_component = this; | ||||||
| } | } | ||||||
|  |  | ||||||
| void ModemComponent::dump_config() { this->dump_connect_params_(); } | std::string ModemComponent::send_at(const std::string &cmd) { | ||||||
|  |   std::string result; | ||||||
|  |   command_result status; | ||||||
|  |   ESP_LOGV(TAG, "Sending command: %s", cmd.c_str()); | ||||||
|  |   status = this->dce->at(cmd, result, this->command_delay_ * 5); | ||||||
|  |   ESP_LOGV(TAG, "Result for command %s: %s (status %s)", cmd.c_str(), result.c_str(), | ||||||
|  |            command_result_to_string(status).c_str()); | ||||||
|  |   if (status != esp_modem::command_result::OK) { | ||||||
|  |     result = "ERROR"; | ||||||
|  |   } | ||||||
|  |   return result; | ||||||
|  | } | ||||||
|  |  | ||||||
| float ModemComponent::get_setup_priority() const { return setup_priority::WIFI + 1; }  // just before WIFI | bool ModemComponent::get_imei(std::string &result) { | ||||||
|  |   // wrapper around this->dce->get_imei() that check that the result is valid | ||||||
|  |   // (so it can be used to check if the modem is responding correctly (a simple 'AT' cmd is sometime not enough)) | ||||||
|  |   command_result status; | ||||||
|  |   status = this->dce->get_imei(result); | ||||||
|  |   bool success = true; | ||||||
|  |  | ||||||
| bool ModemComponent::can_proceed() { |   if (status == command_result::OK && result.length() == 15) { | ||||||
|   if (!this->enabled_) { |     for (char c : result) { | ||||||
|  |       if (!isdigit(static_cast<unsigned char>(c))) { | ||||||
|  |         success = false; | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } else { | ||||||
|  |     success = false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (!success) { | ||||||
|  |     result = "UNAVAILABLE"; | ||||||
|  |   } | ||||||
|  |   return success; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool ModemComponent::get_power_status() { | ||||||
|  | #ifdef USE_MODEM_STATUS | ||||||
|  |   // This code is not fully checked. The status pin seems to be flickering on Lilygo T-SIM7600 | ||||||
|  |   return this->status_pin_->digital_read(); | ||||||
|  | #else | ||||||
|  |   if (!this->cmux_ && this->internal_state_.connected) { | ||||||
|  |     // Data mode, connected:  assume power is OK | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|   return this->is_connected(); |   return this->modem_ready(); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool ModemComponent::modem_ready() { | ||||||
|  |   // check if the modem is ready to answer AT commands | ||||||
|  |   std::string imei; | ||||||
|  |   if (this->dce && this->get_imei(imei)) { | ||||||
|  |     // we are sure that the modem is on | ||||||
|  |     this->internal_state_.powered_on = true; | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |   return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ModemComponent::enable() { | ||||||
|  |   ESP_LOGD(TAG, "Enabling modem"); | ||||||
|  |   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; | ||||||
|  |   if (this->component_state_ != ModemComponentState::CONNECTED) { | ||||||
|  |     this->component_state_ = ModemComponentState::DISCONNECTED; | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| network::IPAddresses ModemComponent::get_ip_addresses() { | network::IPAddresses ModemComponent::get_ip_addresses() { | ||||||
| @@ -78,9 +145,80 @@ std::string ModemComponent::get_use_address() const { | |||||||
|   return this->use_address_; |   return this->use_address_; | ||||||
| } | } | ||||||
|  |  | ||||||
| void ModemComponent::set_use_address(const std::string &use_address) { this->use_address_ = use_address; } | void ModemComponent::dump_dce_status() { | ||||||
|  |   bool watchdog = false; | ||||||
|  |   if (!this->watchdog_) { | ||||||
|  |     this->watchdog_ = std::make_shared<watchdog::WatchdogManager>(60000); | ||||||
|  |     watchdog = true; | ||||||
|  |   } | ||||||
|  |  | ||||||
| bool ModemComponent::is_connected() { return this->state_ == ModemComponentState::CONNECTED; } |   if (this->internal_state_.connected && !this->cmux_) { | ||||||
|  |     if (!this->dce->set_mode(modem_mode::COMMAND_MODE)) { | ||||||
|  |       ESP_LOGW(TAG, "Unable to enter temporary command mode"); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   if (this->internal_state_.modem_synced && this->modem_ready()) { | ||||||
|  |     ESP_LOGCONFIG(TAG, "Modem status:"); | ||||||
|  |     command_result err; | ||||||
|  |     std::string result; | ||||||
|  |     // err = this->dce->get_module_name(result); | ||||||
|  |     // if (err != command_result::OK) { | ||||||
|  |     //   result = "command " + command_result_to_string(err); | ||||||
|  |     // } | ||||||
|  |     // ESP_LOGCONFIG(TAG, "  Module name        : %s", result.c_str()); | ||||||
|  |     int rssi; | ||||||
|  |     int ber; | ||||||
|  |     err = this->dce->get_signal_quality(rssi, ber); | ||||||
|  |     if (err != command_result::OK) { | ||||||
|  |       result = "command " + command_result_to_string(err); | ||||||
|  |     } else { | ||||||
|  |       float ber_f; | ||||||
|  |       if (ber != 99) { | ||||||
|  |         ber_f = float(ber); | ||||||
|  |       } else | ||||||
|  |         ber_f = {}; | ||||||
|  |       std::ostringstream oss; | ||||||
|  |       oss << "rssi " << rssi << "(" << (rssi * 100) / 31 << "%), ber " << ber << "(" << (ber_f * 100) / 7 << "%)"; | ||||||
|  |       result = oss.str(); | ||||||
|  |     } | ||||||
|  |     ESP_LOGCONFIG(TAG, "  Signal quality     : %s", result.c_str()); | ||||||
|  |  | ||||||
|  |     // int network_attachment_state; | ||||||
|  |     // err = this->dce->get_network_attachment_state(network_attachment_state); | ||||||
|  |     // if (err == command_result::OK) { | ||||||
|  |     //   result = network_attachment_state ? "Yes" : "No"; | ||||||
|  |     // } else { | ||||||
|  |     //   result = "command " + command_result_to_string(err); | ||||||
|  |     // } | ||||||
|  |     // ESP_LOGCONFIG(TAG, "  Attached to network: %s", result.c_str()); | ||||||
|  |     // | ||||||
|  |     // err = this->dce->get_operator_name(result); | ||||||
|  |     // if (err != command_result::OK) { | ||||||
|  |     //   result = "command " + command_result_to_string(err); | ||||||
|  |     // } | ||||||
|  |     // ESP_LOGCONFIG(TAG, "  Operator           : %s", result.c_str()); | ||||||
|  |     // | ||||||
|  |     // int act; | ||||||
|  |     // err = this->dce->get_network_system_mode(act); | ||||||
|  |     // if (err != command_result::OK) { | ||||||
|  |     //   result = "command " + command_result_to_string(err); | ||||||
|  |     // } else { | ||||||
|  |     //   std::ostringstream oss; | ||||||
|  |     //   oss << act; | ||||||
|  |     //   result = oss.str(); | ||||||
|  |     // } | ||||||
|  |     // ESP_LOGCONFIG(TAG, "  Access technology   : %s", result.c_str()); | ||||||
|  |   } | ||||||
|  |   if (this->internal_state_.connected && !this->cmux_) { | ||||||
|  |     if (this->dce->resume_data_mode() != command_result::OK) { | ||||||
|  |       ESP_LOGW(TAG, "Unable to resume data mode. Reconnecting..."); | ||||||
|  |       this->internal_state_.connected = false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   if (watchdog) | ||||||
|  |     this->watchdog_.reset(); | ||||||
|  | } | ||||||
|  |  | ||||||
| void ModemComponent::setup() { | void ModemComponent::setup() { | ||||||
|   ESP_LOGI(TAG, "Setting up Modem..."); |   ESP_LOGI(TAG, "Setting up Modem..."); | ||||||
| @@ -88,14 +226,14 @@ 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 |     // as we have a power pin, we assume that the power is off | ||||||
|     this->powered_on_ = false; |     this->internal_state_.powered_on = false; | ||||||
|  |  | ||||||
|     if (this->enabled_) { |     if (this->internal_state_.enabled) { | ||||||
|       this->poweron_(); |       this->poweron_(); | ||||||
|     } |     } | ||||||
|   } else { |   } else { | ||||||
|     // no status pin, we assume that the power is on |     // no status pin, we assume that the power is on | ||||||
|     this->powered_on_ = true; |     this->internal_state_.powered_on = true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (this->status_pin_) { |   if (this->status_pin_) { | ||||||
| @@ -116,7 +254,7 @@ void ModemComponent::setup() { | |||||||
|   } else { |   } else { | ||||||
|     ESP_LOGCONFIG(TAG, "  Status pin: Not defined"); |     ESP_LOGCONFIG(TAG, "  Status pin: Not defined"); | ||||||
|   } |   } | ||||||
|   ESP_LOGCONFIG(TAG, "  Enabled   : %s", this->enabled_ ? "Yes" : "No"); |   ESP_LOGCONFIG(TAG, "  Enabled   : %s", this->internal_state_.enabled ? "Yes" : "No"); | ||||||
|   ESP_LOGCONFIG(TAG, "  Use CMUX  : %s", this->cmux_ ? "Yes" : "No"); |   ESP_LOGCONFIG(TAG, "  Use CMUX  : %s", this->cmux_ ? "Yes" : "No"); | ||||||
|  |  | ||||||
|   ESP_LOGV(TAG, "PPP netif setup"); |   ESP_LOGV(TAG, "PPP netif setup"); | ||||||
| @@ -147,6 +285,232 @@ void ModemComponent::setup() { | |||||||
|   ESP_LOGV(TAG, "Setup finished"); |   ESP_LOGV(TAG, "Setup finished"); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void ModemComponent::loop() { | ||||||
|  |   static ModemComponentState last_state = this->component_state_; | ||||||
|  |   static uint32_t next_loop_millis = millis(); | ||||||
|  |   static bool connecting = false; | ||||||
|  |   static bool disconnecting = false; | ||||||
|  |   static uint8_t network_attach_retry = 10; | ||||||
|  |   static uint8_t ip_lost_retries = 10; | ||||||
|  |  | ||||||
|  |   if ((millis() < next_loop_millis)) { | ||||||
|  |     // some commands need some delay | ||||||
|  |     yield(); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | #ifdef USE_MODEM_POWER | ||||||
|  |   if (this->internal_state_.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->internal_state_.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->internal_state_.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->internal_state_.power_transition = false; | ||||||
|  |         if (!this->modem_sync_()) { | ||||||
|  |           ESP_LOGE(TAG, "Unable to power on the modem"); | ||||||
|  |           this->internal_state_.powered_on = false; | ||||||
|  |         } else { | ||||||
|  |           ESP_LOGI(TAG, "Modem powered ON"); | ||||||
|  |           this->internal_state_.powered_on = true; | ||||||
|  |           this->internal_state_.modem_synced = false; | ||||||
|  |           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->internal_state_.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->internal_state_.power_transition = false; | ||||||
|  |         if (this->modem_ready()) { | ||||||
|  |           ESP_LOGE(TAG, "Unable to power off the modem"); | ||||||
|  |           this->internal_state_.powered_on = true; | ||||||
|  |         } else { | ||||||
|  |           ESP_LOGI(TAG, "Modem powered OFF"); | ||||||
|  |           this->internal_state_.powered_on = false; | ||||||
|  |           this->internal_state_.modem_synced = false; | ||||||
|  |           this->watchdog_.reset(); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     yield(); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | #endif  // USE_MODEM_POWER | ||||||
|  |  | ||||||
|  |   switch (this->component_state_) { | ||||||
|  |     case ModemComponentState::NOT_RESPONDING: | ||||||
|  |       if (this->internal_state_.start) { | ||||||
|  |         if (this->modem_ready()) { | ||||||
|  |           ESP_LOGI(TAG, "Modem recovered"); | ||||||
|  |           this->status_clear_warning(); | ||||||
|  |           this->component_state_ = ModemComponentState::DISCONNECTED; | ||||||
|  |         } else { | ||||||
|  |           if (!this->internal_state_.powered_on) { | ||||||
|  |             this->poweron_(); | ||||||
|  |           } else if (this->not_responding_cb_) { | ||||||
|  |             if (!this->not_responding_cb_->is_action_running()) { | ||||||
|  |               ESP_LOGD(TAG, "Calling 'on_not_responding' callback"); | ||||||
|  |               this->not_responding_cb_->trigger(); | ||||||
|  |             } | ||||||
|  |           } else { | ||||||
|  |             ESP_LOGW(TAG, "Modem not responding, and no 'on_not_responding' action defined"); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       break; | ||||||
|  |  | ||||||
|  |     case ModemComponentState::DISCONNECTED: | ||||||
|  |       if (this->internal_state_.enabled) { | ||||||
|  |         // be sure the modem is on and synced | ||||||
|  |         if (!this->internal_state_.powered_on) { | ||||||
|  |           this->poweron_(); | ||||||
|  |           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) { | ||||||
|  |           // want to connect | ||||||
|  |           if (!connecting) { | ||||||
|  |             // wait for the modem be attached to a network, start ppp, and set connecting=true | ||||||
|  |             if (!this->watchdog_) | ||||||
|  |               this->watchdog_ = std::make_shared<watchdog::WatchdogManager>(60000); | ||||||
|  |             if (is_network_attached_()) { | ||||||
|  |               network_attach_retry = 10; | ||||||
|  |               if (this->start_ppp_()) { | ||||||
|  |                 connecting = true; | ||||||
|  |                 next_loop_millis = millis() + 2000;  // delay for next loop | ||||||
|  |               } | ||||||
|  |             } 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"); | ||||||
|  |                 if (this->power_pin_) { | ||||||
|  |                   this->poweroff_(); | ||||||
|  |                 } else { | ||||||
|  |                   this->component_state_ = ModemComponentState::NOT_RESPONDING; | ||||||
|  |                 } | ||||||
|  |               } | ||||||
|  |               next_loop_millis = millis() + 1000;  // delay to retry | ||||||
|  |             } | ||||||
|  |           } else { | ||||||
|  |             // connecting | ||||||
|  |             if (!this->internal_state_.connected) { | ||||||
|  |               // wait until this->internal_state_.connected set to true by IP_EVENT_PPP_GOT_IP | ||||||
|  |               next_loop_millis = millis() + 1000;  // delay for next loop | ||||||
|  |  | ||||||
|  |               // connecting timeout | ||||||
|  |               if (millis() - this->internal_state_.connect_begin > 15000) { | ||||||
|  |                 ESP_LOGW(TAG, "Connecting via Modem failed! Re-connecting..."); | ||||||
|  |                 // TODO: exit data/cmux without error check | ||||||
|  |                 connecting = false; | ||||||
|  |               } | ||||||
|  |             } else { | ||||||
|  |               connecting = false; | ||||||
|  |               ESP_LOGI(TAG, "Connected via Modem"); | ||||||
|  |               this->component_state_ = ModemComponentState::CONNECTED; | ||||||
|  |  | ||||||
|  |               this->dump_connect_params_(); | ||||||
|  |               this->status_clear_warning(); | ||||||
|  |               this->watchdog_.reset(); | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         } else { | ||||||
|  |           this->internal_state_.start = true; | ||||||
|  |         } | ||||||
|  |       } else { | ||||||
|  |         this->component_state_ = ModemComponentState::DISABLED; | ||||||
|  |         this->watchdog_.reset(); | ||||||
|  |       } | ||||||
|  |       break; | ||||||
|  |  | ||||||
|  |     case ModemComponentState::CONNECTED: | ||||||
|  |       if (this->internal_state_.enabled) { | ||||||
|  |         if (!this->internal_state_.connected) { | ||||||
|  |           this->status_set_warning("Connection via Modem lost!"); | ||||||
|  |           this->component_state_ = ModemComponentState::DISCONNECTED; | ||||||
|  |         } | ||||||
|  |         disconnecting = false; | ||||||
|  |       } else { | ||||||
|  |         if (this->internal_state_.connected) { | ||||||
|  |           // connected but disbled, so disconnect | ||||||
|  |           if (!disconnecting) { | ||||||
|  |             disconnecting = true; | ||||||
|  |             ip_lost_retries = 10; | ||||||
|  |             this->watchdog_ = std::make_shared<watchdog::WatchdogManager>(60000); | ||||||
|  |             ESP_LOGD(TAG, "Disconnecting..."); | ||||||
|  |             this->stop_ppp_(); | ||||||
|  |             delay(200);  // NOLINT | ||||||
|  |             ESP_LOGD(TAG, "Disconnected after %.1fmin", float(this->internal_state_.connect_begin) / (1000 * 60)); | ||||||
|  |           } else { | ||||||
|  |             // disconnecting | ||||||
|  |             // Waiting for IP_EVENT_PPP_LOST_IP. | ||||||
|  |             // This can take a long time, so we ckeck the IP addr, and trigger the event manualy if it's null. | ||||||
|  |             esp_netif_ip_info_t ip_info; | ||||||
|  |             esp_netif_get_ip_info(this->ppp_netif_, &ip_info); | ||||||
|  |             if (ip_info.ip.addr == 0) { | ||||||
|  |               // lost IP | ||||||
|  |               esp_event_post(IP_EVENT, IP_EVENT_PPP_LOST_IP, nullptr, 0, 0); | ||||||
|  |             } else { | ||||||
|  |               ESP_LOGD(TAG, "Waiting for lost IP... (retries %" PRIu8 ")", ip_lost_retries); | ||||||
|  |               ip_lost_retries--; | ||||||
|  |               if (ip_lost_retries == 0) { | ||||||
|  |                 // Something goes wrong, we have still an IP | ||||||
|  |                 ESP_LOGE(TAG, "No IP lost event recieved. Sending one manually"); | ||||||
|  |                 esp_event_post(IP_EVENT, IP_EVENT_PPP_LOST_IP, nullptr, 0, 0); | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |             next_loop_millis = millis() + 2000;  // delay for next loop | ||||||
|  |           } | ||||||
|  |         } else {  // if (this->internal_state_.connected) | ||||||
|  |           // ip lost as expected | ||||||
|  |           ESP_LOGI(TAG, "PPPoS disconnected"); | ||||||
|  |           this->component_state_ = ModemComponentState::DISCONNECTED; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       break; | ||||||
|  |  | ||||||
|  |     case ModemComponentState::DISABLED: | ||||||
|  |       if (this->internal_state_.enabled) { | ||||||
|  |         this->component_state_ = ModemComponentState::DISCONNECTED; | ||||||
|  |         ESP_LOGE(TAG, "here"); | ||||||
|  |       } else if (this->internal_state_.powered_on) { | ||||||
|  |         this->poweroff_(); | ||||||
|  |       } | ||||||
|  |       next_loop_millis = millis() + 2000;  // delay for next loop | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (this->component_state_ != last_state) { | ||||||
|  |     ESP_LOGV(TAG, "State changed: %s -> %s", state_to_string(last_state).c_str(), | ||||||
|  |              state_to_string(this->component_state_).c_str()); | ||||||
|  |     this->on_state_callback_.call(last_state, this->component_state_); | ||||||
|  |  | ||||||
|  |     last_state = this->component_state_; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
| void ModemComponent::modem_lazy_init_() { | void ModemComponent::modem_lazy_init_() { | ||||||
|   // destroy previous dte/dce, and recreate them. |   // destroy previous dte/dce, and recreate them. | ||||||
|   // no communication is done with the modem. |   // no communication is done with the modem. | ||||||
| @@ -239,21 +603,22 @@ bool ModemComponent::modem_sync_() { | |||||||
|  |  | ||||||
|     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); | ||||||
|       this->powered_on_ = false; |       this->internal_state_.powered_on = false; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   if (status) { |   if (status) { | ||||||
|     elapsed_ms = millis() - start_ms; |     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_(); |  | ||||||
|     if (!this->prepare_sim_()) { |     if (!this->prepare_sim_()) { | ||||||
|       // fatal error |       // fatal error | ||||||
|       this->disable(); |       this->disable(); | ||||||
|       status = false; |       status = false; | ||||||
|     } |     } | ||||||
|  |     this->send_init_at_(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   this->modem_synced_ = status; |   this->internal_state_.modem_synced = status; | ||||||
|  |  | ||||||
|   return status; |   return status; | ||||||
| } | } | ||||||
| @@ -296,19 +661,27 @@ void ModemComponent::send_init_at_() { | |||||||
|     } else { |     } else { | ||||||
|       ESP_LOGI(TAG, "'init_at' '%s' result: %s", cmd.c_str(), result.c_str()); |       ESP_LOGI(TAG, "'init_at' '%s' result: %s", cmd.c_str(), result.c_str()); | ||||||
|     } |     } | ||||||
|  |     delay(this->command_delay_); | ||||||
|     yield(); |     yield(); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | bool ModemComponent::is_network_attached_() { | ||||||
|  |   int network_attachment_state; | ||||||
|  |   command_result err = this->dce->get_network_attachment_state(network_attachment_state); | ||||||
|  |   if (err == command_result::OK) { | ||||||
|  |     return network_attachment_state; | ||||||
|  |   } else | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  |  | ||||||
| bool ModemComponent::start_ppp_() { | bool ModemComponent::start_ppp_() { | ||||||
|   this->connect_begin_ = millis(); |   this->internal_state_.connect_begin = millis(); | ||||||
|   this->status_set_warning("Starting connection"); |   this->status_set_warning("Starting connection"); | ||||||
|   this->watchdog_ = std::make_shared<watchdog::WatchdogManager>(60000); |   this->watchdog_ = std::make_shared<watchdog::WatchdogManager>(60000); | ||||||
|  |  | ||||||
|   // 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->got_ipv4_address_ = false; |   this->internal_state_.got_ipv4_address = false; | ||||||
|  |  | ||||||
|   this->dump_dce_status_(); |  | ||||||
|  |  | ||||||
|   ESP_LOGD(TAG, "Asking the modem to enter PPP"); |   ESP_LOGD(TAG, "Asking the modem to enter PPP"); | ||||||
|  |  | ||||||
| @@ -351,282 +724,25 @@ void ModemComponent::ip_event_handler(void *arg, esp_event_base_t event_base, in | |||||||
|       event = (ip_event_got_ip_t *) event_data; |       event = (ip_event_got_ip_t *) event_data; | ||||||
|       ip_info = &event->ip_info; |       ip_info = &event->ip_info; | ||||||
|       ESP_LOGD(TAG, "[IP event] Got IP " IPSTR, IP2STR(&ip_info->ip)); |       ESP_LOGD(TAG, "[IP event] Got IP " IPSTR, IP2STR(&ip_info->ip)); | ||||||
|       global_modem_component->got_ipv4_address_ = true; |       global_modem_component->internal_state_.got_ipv4_address = true; | ||||||
|       global_modem_component->connected_ = true; |       global_modem_component->internal_state_.connected = true; | ||||||
|       break; |       break; | ||||||
|  |  | ||||||
|     case IP_EVENT_PPP_LOST_IP: |     case IP_EVENT_PPP_LOST_IP: | ||||||
|       if (global_modem_component->connected_) { |       if (global_modem_component->internal_state_.connected) { | ||||||
|         // do not log message if we are not connected |         // do not log message if we are not connected | ||||||
|         ESP_LOGD(TAG, "[IP event] Lost IP"); |         ESP_LOGD(TAG, "[IP event] Lost IP"); | ||||||
|       } |       } | ||||||
|       global_modem_component->got_ipv4_address_ = false; |       global_modem_component->internal_state_.got_ipv4_address = false; | ||||||
|       global_modem_component->connected_ = false; |       global_modem_component->internal_state_.connected = false; | ||||||
|       break; |       break; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| void ModemComponent::loop() { |  | ||||||
|   static ModemComponentState last_state = this->state_; |  | ||||||
|   static uint32_t next_loop_millis = millis(); |  | ||||||
|   static bool connecting = false; |  | ||||||
|   static bool disconnecting = false; |  | ||||||
|   static uint8_t network_attach_retry = 10; |  | ||||||
|   static uint8_t ip_lost_retries = 10; |  | ||||||
|  |  | ||||||
|   if ((millis() < next_loop_millis)) { |  | ||||||
|     // some commands need some delay |  | ||||||
|     yield(); |  | ||||||
|     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->modem_synced_ = false; |  | ||||||
|           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->modem_synced_ = false; |  | ||||||
|           this->watchdog_.reset(); |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
|     yield(); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
| #endif  // USE_MODEM_POWER |  | ||||||
|  |  | ||||||
|   switch (this->state_) { |  | ||||||
|     case ModemComponentState::NOT_RESPONDING: |  | ||||||
|       if (this->start_) { |  | ||||||
|         if (this->modem_ready()) { |  | ||||||
|           ESP_LOGI(TAG, "Modem recovered"); |  | ||||||
|           this->status_clear_warning(); |  | ||||||
|           this->state_ = ModemComponentState::DISCONNECTED; |  | ||||||
|         } else { |  | ||||||
|           if (!this->powered_on_) { |  | ||||||
|             this->poweron_(); |  | ||||||
|           } else if (this->not_responding_cb_) { |  | ||||||
|             if (!this->not_responding_cb_->is_action_running()) { |  | ||||||
|               ESP_LOGD(TAG, "Calling 'on_not_responding' callback"); |  | ||||||
|               this->not_responding_cb_->trigger(); |  | ||||||
|             } |  | ||||||
|           } else { |  | ||||||
|             ESP_LOGW(TAG, "Modem not responding, and no 'on_not_responding' action defined"); |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|       break; |  | ||||||
|  |  | ||||||
|     case ModemComponentState::DISCONNECTED: |  | ||||||
|       if (this->enabled_) { |  | ||||||
|         // be sure the modem is on and synced |  | ||||||
|         if (!this->powered_on_) { |  | ||||||
|           this->poweron_(); |  | ||||||
|           break; |  | ||||||
|         } else if (!this->modem_synced_) { |  | ||||||
|           if (!this->modem_sync_()) { |  | ||||||
|             ESP_LOGE(TAG, "Modem not responding"); |  | ||||||
|             this->state_ = ModemComponentState::NOT_RESPONDING; |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (this->start_) { |  | ||||||
|           // want to connect |  | ||||||
|           if (!connecting) { |  | ||||||
|             // wait for the modem be attached to a network, start ppp, and set connecting=true |  | ||||||
|             if (!this->watchdog_) |  | ||||||
|               this->watchdog_ = std::make_shared<watchdog::WatchdogManager>(60000); |  | ||||||
|             if (is_network_attached_()) { |  | ||||||
|               network_attach_retry = 10; |  | ||||||
|               if (this->start_ppp_()) { |  | ||||||
|                 connecting = true; |  | ||||||
|                 next_loop_millis = millis() + 2000;  // delay for next loop |  | ||||||
|               } |  | ||||||
|             } 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"); |  | ||||||
|                 if (this->power_pin_) { |  | ||||||
|                   this->poweroff_(); |  | ||||||
|                 } else { |  | ||||||
|                   this->state_ = ModemComponentState::NOT_RESPONDING; |  | ||||||
|                 } |  | ||||||
|               } |  | ||||||
|               next_loop_millis = millis() + 1000;  // delay to retry |  | ||||||
|             } |  | ||||||
|           } else { |  | ||||||
|             // connecting |  | ||||||
|             if (!this->connected_) { |  | ||||||
|               // wait until this->connected_ set to true by IP_EVENT_PPP_GOT_IP |  | ||||||
|               next_loop_millis = millis() + 1000;  // delay for next loop |  | ||||||
|  |  | ||||||
|               // connecting timeout |  | ||||||
|               if (millis() - this->connect_begin_ > 15000) { |  | ||||||
|                 ESP_LOGW(TAG, "Connecting via Modem failed! Re-connecting..."); |  | ||||||
|                 // TODO: exit data/cmux without error check |  | ||||||
|                 connecting = false; |  | ||||||
|               } |  | ||||||
|             } else { |  | ||||||
|               connecting = false; |  | ||||||
|               ESP_LOGI(TAG, "Connected via Modem"); |  | ||||||
|               this->state_ = ModemComponentState::CONNECTED; |  | ||||||
|  |  | ||||||
|               this->dump_connect_params_(); |  | ||||||
|               this->status_clear_warning(); |  | ||||||
|               this->watchdog_.reset(); |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|         } else { |  | ||||||
|           this->start_ = true; |  | ||||||
|         } |  | ||||||
|       } else { |  | ||||||
|         this->state_ = ModemComponentState::DISABLED; |  | ||||||
|         this->watchdog_.reset(); |  | ||||||
|       } |  | ||||||
|       break; |  | ||||||
|  |  | ||||||
|     case ModemComponentState::CONNECTED: |  | ||||||
|       if (this->enabled_) { |  | ||||||
|         if (!this->connected_) { |  | ||||||
|           this->status_set_warning("Connection via Modem lost!"); |  | ||||||
|           this->state_ = ModemComponentState::DISCONNECTED; |  | ||||||
|         } |  | ||||||
|         disconnecting = false; |  | ||||||
|       } else { |  | ||||||
|         if (this->connected_) { |  | ||||||
|           // connected but disbled, so disconnect |  | ||||||
|           if (!disconnecting) { |  | ||||||
|             disconnecting = true; |  | ||||||
|             ip_lost_retries = 10; |  | ||||||
|             this->watchdog_ = std::make_shared<watchdog::WatchdogManager>(60000); |  | ||||||
|             ESP_LOGD(TAG, "Disconnecting..."); |  | ||||||
|             this->stop_ppp_(); |  | ||||||
|             delay(200);  // NOLINT |  | ||||||
|             ESP_LOGD(TAG, "Disconnected after %.1fmin", float(this->connect_begin_) / (1000 * 60)); |  | ||||||
|           } else { |  | ||||||
|             // disconnecting |  | ||||||
|             // Waiting for IP_EVENT_PPP_LOST_IP. |  | ||||||
|             // This can take a long time, so we ckeck the IP addr, and trigger the event manualy if it's null. |  | ||||||
|             esp_netif_ip_info_t ip_info; |  | ||||||
|             esp_netif_get_ip_info(this->ppp_netif_, &ip_info); |  | ||||||
|             if (ip_info.ip.addr == 0) { |  | ||||||
|               // lost IP |  | ||||||
|               esp_event_post(IP_EVENT, IP_EVENT_PPP_LOST_IP, nullptr, 0, 0); |  | ||||||
|             } else { |  | ||||||
|               ESP_LOGD(TAG, "Waiting for lost IP... (retries %" PRIu8 ")", ip_lost_retries); |  | ||||||
|               ip_lost_retries--; |  | ||||||
|               if (ip_lost_retries == 0) { |  | ||||||
|                 // Something goes wrong, we have still an IP |  | ||||||
|                 ESP_LOGE(TAG, "No IP lost event recieved. Sending one manually"); |  | ||||||
|                 esp_event_post(IP_EVENT, IP_EVENT_PPP_LOST_IP, nullptr, 0, 0); |  | ||||||
|               } |  | ||||||
|             } |  | ||||||
|             next_loop_millis = millis() + 2000;  // delay for next loop |  | ||||||
|           } |  | ||||||
|         } else {  // if (this->connected_) |  | ||||||
|           // ip lost as expected |  | ||||||
|           ESP_LOGI(TAG, "PPPoS disconnected"); |  | ||||||
|           this->state_ = ModemComponentState::DISCONNECTED; |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|       break; |  | ||||||
|  |  | ||||||
|     case ModemComponentState::DISABLED: |  | ||||||
|       if (this->enabled_) { |  | ||||||
|         this->state_ = ModemComponentState::DISCONNECTED; |  | ||||||
|         ESP_LOGE(TAG, "here"); |  | ||||||
|       } else if (this->powered_on_) { |  | ||||||
|         this->poweroff_(); |  | ||||||
|       } |  | ||||||
|       next_loop_millis = millis() + 2000;  // delay for next loop |  | ||||||
|       break; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (this->state_ != last_state) { |  | ||||||
|     ESP_LOGV(TAG, "State changed: %s -> %s", state_to_string(last_state).c_str(), |  | ||||||
|              state_to_string(this->state_).c_str()); |  | ||||||
|     this->on_state_callback_.call(last_state, this->state_); |  | ||||||
|  |  | ||||||
|     last_state = this->state_; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ModemComponent::enable() { |  | ||||||
|   ESP_LOGD(TAG, "Enabling modem"); |  | ||||||
|   if (this->state_ == ModemComponentState::DISABLED) { |  | ||||||
|     this->state_ = ModemComponentState::DISCONNECTED; |  | ||||||
|   } |  | ||||||
|   this->start_ = true; |  | ||||||
|   this->enabled_ = true; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ModemComponent::disable() { |  | ||||||
|   ESP_LOGD(TAG, "Disabling modem"); |  | ||||||
|   this->enabled_ = false; |  | ||||||
|   if (this->state_ != ModemComponentState::CONNECTED) { |  | ||||||
|     this->state_ = ModemComponentState::DISCONNECTED; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool ModemComponent::get_power_status() { |  | ||||||
| #ifdef USE_MODEM_STATUS |  | ||||||
|   // This code is not fully checked. The status pin seems to be flickering on Lilygo T-SIM7600 |  | ||||||
|   return this->status_pin_->digital_read(); |  | ||||||
| #else |  | ||||||
|   if (!this->cmux_ && this->connected_) { |  | ||||||
|     // Data mode, connected:  assume power is OK |  | ||||||
|     return true; |  | ||||||
|   } |  | ||||||
|   return this->modem_ready(); |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ModemComponent::poweron_() { | void ModemComponent::poweron_() { | ||||||
| #ifdef USE_MODEM_POWER | #ifdef USE_MODEM_POWER | ||||||
|   this->power_state_ = ModemPowerState::TON; |   this->internal_state_.power_state = ModemPowerState::TON; | ||||||
|   this->power_transition_ = true; |   this->internal_state_.power_transition = true; | ||||||
|   return; |  | ||||||
| #else | #else | ||||||
|   if (this->modem_ready()) { |   if (this->modem_ready()) { | ||||||
|     ESP_LOGV(TAG, "Modem is already ON"); |     ESP_LOGV(TAG, "Modem is already ON"); | ||||||
| @@ -638,14 +754,13 @@ void ModemComponent::poweron_() { | |||||||
|  |  | ||||||
| void ModemComponent::poweroff_() { | void ModemComponent::poweroff_() { | ||||||
| #ifdef USE_MODEM_POWER | #ifdef USE_MODEM_POWER | ||||||
|   this->power_state_ = ModemPowerState::TOFF; |   this->internal_state_.power_state = ModemPowerState::TOFF; | ||||||
|   this->power_transition_ = true; |   this->internal_state_.power_transition = true; | ||||||
|   return; |  | ||||||
| #endif  // USE_MODEM_POWER | #endif  // USE_MODEM_POWER | ||||||
| } | } | ||||||
|  |  | ||||||
| void ModemComponent::dump_connect_params_() { | void ModemComponent::dump_connect_params_() { | ||||||
|   if (!this->connected_) { |   if (!this->internal_state_.connected) { | ||||||
|     ESP_LOGCONFIG(TAG, "Modem connection: Not connected"); |     ESP_LOGCONFIG(TAG, "Modem connection: Not connected"); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| @@ -666,104 +781,6 @@ void ModemComponent::dump_connect_params_() { | |||||||
|   ESP_LOGCONFIG(TAG, "  DNS fallback: %s", network::IPAddress(dns_fallback_ip).str().c_str()); |   ESP_LOGCONFIG(TAG, "  DNS fallback: %s", network::IPAddress(dns_fallback_ip).str().c_str()); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool ModemComponent::is_network_attached_() { |  | ||||||
|   int network_attachment_state; |  | ||||||
|   command_result err = this->dce->get_network_attachment_state(network_attachment_state); |  | ||||||
|   if (err == command_result::OK) { |  | ||||||
|     return network_attachment_state; |  | ||||||
|   } else |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ModemComponent::dump_dce_status_() { |  | ||||||
|   ESP_LOGCONFIG(TAG, "Modem status:"); |  | ||||||
|   command_result err; |  | ||||||
|   std::string result; |  | ||||||
|   err = this->dce->get_module_name(result); |  | ||||||
|   if (err != command_result::OK) { |  | ||||||
|     result = "command " + command_result_to_string(err); |  | ||||||
|   } |  | ||||||
|   ESP_LOGCONFIG(TAG, "  Module name        : %s", result.c_str()); |  | ||||||
|  |  | ||||||
|   int rssi; |  | ||||||
|   int ber; |  | ||||||
|   err = this->dce->get_signal_quality(rssi, ber); |  | ||||||
|   if (err != command_result::OK) { |  | ||||||
|     result = "command " + command_result_to_string(err); |  | ||||||
|   } else { |  | ||||||
|     float ber_f; |  | ||||||
|     if (ber != 99) { |  | ||||||
|       ber_f = float(ber); |  | ||||||
|     } else |  | ||||||
|       ber_f = {}; |  | ||||||
|     std::ostringstream oss; |  | ||||||
|     oss << "rssi " << rssi << "(" << (rssi * 100) / 31 << "%), ber " << ber << "(" << (ber_f * 100) / 7 << "%)"; |  | ||||||
|     result = oss.str(); |  | ||||||
|   } |  | ||||||
|   ESP_LOGCONFIG(TAG, "  Signal quality     : %s", result.c_str()); |  | ||||||
|  |  | ||||||
|   int network_attachment_state; |  | ||||||
|   err = this->dce->get_network_attachment_state(network_attachment_state); |  | ||||||
|   if (err == command_result::OK) { |  | ||||||
|     result = network_attachment_state ? "Yes" : "No"; |  | ||||||
|   } else { |  | ||||||
|     result = "command " + command_result_to_string(err); |  | ||||||
|   } |  | ||||||
|   ESP_LOGCONFIG(TAG, "  Attached to network: %s", result.c_str()); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::string ModemComponent::send_at(const std::string &cmd) { |  | ||||||
|   std::string result; |  | ||||||
|   command_result status; |  | ||||||
|   ESP_LOGV(TAG, "Sending command: %s", cmd.c_str()); |  | ||||||
|   status = this->dce->at(cmd, result, this->command_delay_); |  | ||||||
|   ESP_LOGV(TAG, "Result for command %s: %s (status %s)", cmd.c_str(), result.c_str(), |  | ||||||
|            command_result_to_string(status).c_str()); |  | ||||||
|   if (status != esp_modem::command_result::OK) { |  | ||||||
|     result = "ERROR"; |  | ||||||
|   } |  | ||||||
|   return result; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool ModemComponent::get_imei(std::string &result) { |  | ||||||
|   // wrapper around this->dce->get_imei() that check that the result is valid |  | ||||||
|   // (so it can be used to check if the modem is responding correctly (a simple 'AT' cmd is sometime not enough)) |  | ||||||
|   command_result status; |  | ||||||
|   status = this->dce->get_imei(result); |  | ||||||
|   bool success = true; |  | ||||||
|  |  | ||||||
|   if (status == command_result::OK && result.length() == 15) { |  | ||||||
|     for (char c : result) { |  | ||||||
|       if (!isdigit(static_cast<unsigned char>(c))) { |  | ||||||
|         success = false; |  | ||||||
|         break; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } else { |  | ||||||
|     success = false; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (!success) { |  | ||||||
|     result = "UNAVAILABLE"; |  | ||||||
|   } |  | ||||||
|   return success; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool ModemComponent::modem_ready() { |  | ||||||
|   // check if the modem is ready to answer AT commands |  | ||||||
|   std::string 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, ModemComponentState)> &&callback) { |  | ||||||
|   this->on_state_callback_.add(std::move(callback)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| }  // namespace modem | }  // namespace modem | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|  |  | ||||||
|   | |||||||
| @@ -49,16 +49,7 @@ enum class ModemPowerState { | |||||||
|  |  | ||||||
| class ModemComponent : public Component { | class ModemComponent : public Component { | ||||||
|  public: |  public: | ||||||
|   ModemComponent(); |   void set_use_address(const std::string &use_address) { this->use_address_ = use_address; } | ||||||
|   void setup() override; |  | ||||||
|   void loop() override; |  | ||||||
|   void dump_config() override; |  | ||||||
|   bool is_connected(); |  | ||||||
|   float get_setup_priority() const override; |  | ||||||
|   bool can_proceed() override; |  | ||||||
|   network::IPAddresses get_ip_addresses(); |  | ||||||
|   std::string get_use_address() const; |  | ||||||
|   void set_use_address(const std::string &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; } | ||||||
|   void set_power_pin(GPIOPin *power_pin) { this->power_pin_ = power_pin; } |   void set_power_pin(GPIOPin *power_pin) { this->power_pin_ = power_pin; } | ||||||
| @@ -70,13 +61,38 @@ class ModemComponent : public Component { | |||||||
|   void set_not_responding_cb(Trigger<> *not_responding_cb) { this->not_responding_cb_ = not_responding_cb; } |   void set_not_responding_cb(Trigger<> *not_responding_cb) { this->not_responding_cb_ = not_responding_cb; } | ||||||
|   void enable_cmux() { this->cmux_ = true; } |   void enable_cmux() { this->cmux_ = true; } | ||||||
|   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; } | ||||||
|   std::string send_at(const std::string &cmd); |   std::string send_at(const std::string &cmd); | ||||||
|   bool get_imei(std::string &result); |   bool get_imei(std::string &result); | ||||||
|   bool get_power_status(); |   bool get_power_status(); | ||||||
|   bool modem_ready(); |   bool modem_ready(); | ||||||
|   void enable(); |   void enable(); | ||||||
|   void disable(); |   void disable(); | ||||||
|   void add_on_state_callback(std::function<void(ModemComponentState, ModemComponentState)> &&callback); |  | ||||||
|  |   network::IPAddresses get_ip_addresses(); | ||||||
|  |   std::string get_use_address() const; | ||||||
|  |  | ||||||
|  |   void dump_dce_status(); | ||||||
|  |  | ||||||
|  |   // ========== INTERNAL METHODS ========== | ||||||
|  |   // (In most use cases you won't need these) | ||||||
|  |  | ||||||
|  |   ModemComponent(); | ||||||
|  |   void setup() override; | ||||||
|  |   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(); | ||||||
|  |   }; | ||||||
|  |   void add_on_state_callback(std::function<void(ModemComponentState, ModemComponentState)> &&callback) { | ||||||
|  |     this->on_state_callback_.add(std::move(callback)); | ||||||
|  |   } | ||||||
|  |   // main esp_modem object | ||||||
|  |   // https://docs.espressif.com/projects/esp-protocols/esp_modem/docs/latest/internal_docs.html#dce-internal-implementation | ||||||
|   std::unique_ptr<DCE> dce{nullptr}; |   std::unique_ptr<DCE> dce{nullptr}; | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
| @@ -91,7 +107,8 @@ class ModemComponent : public Component { | |||||||
|   void poweroff_(); |   void poweroff_(); | ||||||
|   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_(); | ||||||
|   void dump_dce_status_(); |  | ||||||
|  |   // Attributes from yaml config | ||||||
|   InternalGPIOPin *tx_pin_; |   InternalGPIOPin *tx_pin_; | ||||||
|   InternalGPIOPin *rx_pin_; |   InternalGPIOPin *rx_pin_; | ||||||
|   GPIOPin *status_pin_{nullptr}; |   GPIOPin *status_pin_{nullptr}; | ||||||
| @@ -101,38 +118,50 @@ class ModemComponent : public Component { | |||||||
|   std::string password_; |   std::string password_; | ||||||
|   std::string apn_; |   std::string apn_; | ||||||
|   std::vector<std::string> init_at_commands_; |   std::vector<std::string> init_at_commands_; | ||||||
|  |   std::string use_address_; | ||||||
|  |   bool cmux_{false}; | ||||||
|  |   // separate handler for `on_not_responding` (we want to know when it's ended) | ||||||
|  |   Trigger<> *not_responding_cb_{nullptr}; | ||||||
|  |   CallbackManager<void(ModemComponentState, ModemComponentState)> on_state_callback_; | ||||||
|  |  | ||||||
|  |   // Allow changes from yaml ? | ||||||
|   size_t uart_rx_buffer_size_ = 2048;         // 256-2048 |   size_t uart_rx_buffer_size_ = 2048;         // 256-2048 | ||||||
|   size_t uart_tx_buffer_size_ = 1024;         // 256-2048 |   size_t uart_tx_buffer_size_ = 1024;         // 256-2048 | ||||||
|   uint8_t uart_event_queue_size_ = 30;        // 10-40 |   uint8_t uart_event_queue_size_ = 30;        // 10-40 | ||||||
|   size_t uart_event_task_stack_size_ = 2048;  // 2000-6000 |   size_t uart_event_task_stack_size_ = 2048;  // 2000-6000 | ||||||
|   uint8_t uart_event_task_priority_ = 5;      // 3-22 |   uint8_t uart_event_task_priority_ = 5;      // 3-22 | ||||||
|  |   uint32_t command_delay_ = 500;              // timeout for AT commands | ||||||
|  |  | ||||||
|  |   // Changes will trigger user callback | ||||||
|  |   ModemComponentState component_state_{ModemComponentState::DISABLED}; | ||||||
|  |  | ||||||
|  |   // the uart DTE | ||||||
|  |   // https://docs.espressif.com/projects/esp-protocols/esp_modem/docs/latest/internal_docs.html#_CPPv4N9esp_modem3DCEE | ||||||
|   std::shared_ptr<DTE> dte_{nullptr}; |   std::shared_ptr<DTE> dte_{nullptr}; | ||||||
|   esp_netif_t *ppp_netif_{nullptr}; |   esp_netif_t *ppp_netif_{nullptr}; | ||||||
|   ModemComponentState state_{ModemComponentState::DISABLED}; |  | ||||||
|  |   // Many operation blocks a long time. | ||||||
|   std::shared_ptr<watchdog::WatchdogManager> watchdog_; |   std::shared_ptr<watchdog::WatchdogManager> watchdog_; | ||||||
|   bool cmux_{false}; |  | ||||||
|   bool start_{false}; |   struct InternalState { | ||||||
|   bool enabled_{false}; |     bool start{false}; | ||||||
|   bool connected_{false}; |     bool enabled{false}; | ||||||
|   bool got_ipv4_address_{false}; |     bool connected{false}; | ||||||
|  |     bool got_ipv4_address{false}; | ||||||
|     // true if modem_sync_ was sucessfull |     // true if modem_sync_ was sucessfull | ||||||
|   bool modem_synced_{false}; |     bool modem_synced{false}; | ||||||
|     // date start (millis()) |     // date start (millis()) | ||||||
|   uint32_t connect_begin_; |     uint32_t connect_begin; | ||||||
|   std::string use_address_; |  | ||||||
|   // timeout for AT commands |  | ||||||
|   uint32_t command_delay_ = 500; |  | ||||||
|     // guess power state |     // guess power state | ||||||
|   bool powered_on_{false}; |     bool powered_on{false}; | ||||||
| #ifdef USE_MODEM_POWER | #ifdef USE_MODEM_POWER | ||||||
|     // Will be true when power transitionning |     // Will be true when power transitionning | ||||||
|   bool power_transition_{false}; |     bool power_transition{false}; | ||||||
|     // states for triggering on/off signals |     // states for triggering on/off signals | ||||||
|   ModemPowerState power_state_{ModemPowerState::TOFFUART}; |     ModemPowerState power_state{ModemPowerState::TOFFUART}; | ||||||
| #endif  // USE_MODEM_POWER | #endif  // USE_MODEM_POWER | ||||||
|   // separate handler for `on_not_responding` (we want to know when it's ended) |   }; | ||||||
|   Trigger<> *not_responding_cb_{nullptr}; |   InternalState internal_state_; | ||||||
|   CallbackManager<void(ModemComponentState, ModemComponentState)> on_state_callback_; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) | // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user