diff --git a/esphome/components/modem/__init__.py b/esphome/components/modem/__init__.py index ebc3661f26..9d59e43fca 100644 --- a/esphome/components/modem/__init__.py +++ b/esphome/components/modem/__init__.py @@ -11,6 +11,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.core import coroutine_with_priority from esphome.components.esp32 import add_idf_component, add_idf_sdkconfig_option +from esphome.components.binary_sensor import BinarySensor CODEOWNERS = ["@oarcher"] DEPENDENCIES = ["esp32"] @@ -23,6 +24,7 @@ CONF_APN = "apn" CONF_STATUS_PIN = "status_pin" CONF_DTR_PIN = "dtr_pin" CONF_INIT_AT = "init_at" +CONF_READY = "ready" modem_ns = cg.esphome_ns.namespace("modem") @@ -37,6 +39,7 @@ CONFIG_SCHEMA = cv.All( cv.Required(CONF_RX_PIN): cv.positive_int, cv.Required(CONF_MODEL): cv.string, cv.Required(CONF_APN): cv.string, + cv.Optional(CONF_READY): cv.use_id(BinarySensor), cv.Optional(CONF_FLIGHT_PIN): cv.positive_int, cv.Optional(CONF_POWER_PIN): cv.positive_int, cv.Optional(CONF_STATUS_PIN): cv.positive_int, @@ -96,6 +99,10 @@ async def to_code(config): for cmd in init_at: cg.add(var.add_init_at_command(cmd)) + if modem_ready := config.get(CONF_READY, None): + modem_ready_sensor = await cg.get_variable(modem_ready) + cg.add(var.set_ready_bsensor(modem_ready_sensor)) + cg.add(var.set_model(config[CONF_MODEL])) cg.add(var.set_apn(config[CONF_APN])) diff --git a/esphome/components/modem/modem_component.cpp b/esphome/components/modem/modem_component.cpp index 1f7622f4f5..51d14ccc4d 100644 --- a/esphome/components/modem/modem_component.cpp +++ b/esphome/components/modem/modem_component.cpp @@ -74,13 +74,6 @@ bool ModemComponent::is_connected() { return this->state_ == ModemComponentState void ModemComponent::setup() { ESP_LOGI(TAG, "Setting up Modem..."); - this->config_gpio_(); - - if (this->get_status()) { - // at setup, the modem must be down - this->powerdown(); - } - ESP_LOGV(TAG, "DTE setup"); esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG(); this->dte_config_ = dte_config; @@ -152,30 +145,6 @@ void ModemComponent::setup() { assert(this->dce); - this->poweron(); - - esp_modem::command_result res; - res = this->dce->sync(); - int retry = 0; - while (res != command_result::OK) { - res = this->dce->sync(); - if (res != command_result::OK) { - App.feed_wdt(); - vTaskDelay(pdMS_TO_TICKS(1000)); - } - retry++; - if (retry > 20) - break; - } - - // send initial AT commands from yaml - for (const auto &cmd : this->init_at_commands_) { - std::string result; - command_result err = this->dce->at(cmd.c_str(), result, 1000); - delay(100); - ESP_LOGI(TAG, "Init AT command: %s (status %d) -> %s", cmd.c_str(), (int) err, result.c_str()); - } - this->started_ = true; ESP_LOGV(TAG, "Setup finished"); } @@ -184,19 +153,9 @@ void ModemComponent::start_connect_() { this->connect_begin_ = millis(); this->status_set_warning("Starting connection"); - if (!this->get_status()) { - this->poweron(); - } - - // esp_err_t err; - // err = esp_netif_set_hostname(this->ppp_netif_, App.get_name().c_str()); - // if (err != ERR_OK) { - // ESP_LOGW(TAG, "esp_netif_set_hostname failed: %s", esp_err_to_name(err)); - // } - global_modem_component->got_ipv4_address_ = false; // why not this ? - this->dce->set_mode(modem_mode::CMUX_MANUAL_COMMAND); + this->dce->set_mode(modem_mode::COMMAND_MODE); vTaskDelay(pdMS_TO_TICKS(2000)); command_result res = command_result::TIMEOUT; @@ -205,7 +164,6 @@ void ModemComponent::start_connect_() { if (res != command_result::OK) { ESP_LOGW(TAG, "Unable to sync modem. Will retry later"); - this->powerdown(); return; } @@ -240,13 +198,20 @@ void ModemComponent::start_connect_() { return; } vTaskDelay(pdMS_TO_TICKS(2000)); + + // send initial AT commands from yaml + for (const auto &cmd : this->init_at_commands_) { + std::string result; + command_result err = this->dce->at(cmd.c_str(), result, 1000); + delay(100); + ESP_LOGI(TAG, "Init AT command: %s (status %d) -> %s", cmd.c_str(), (int) err, result.c_str()); + } } void ModemComponent::got_ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data; const esp_netif_ip_info_t *ip_info = &event->ip_info; ESP_LOGW(TAG, "[IP event] Got IP " IPSTR, IP2STR(&ip_info->ip)); - vTaskDelay(pdMS_TO_TICKS(1000)); // FIXME tmp global_modem_component->got_ipv4_address_ = true; global_modem_component->connected_ = true; } @@ -257,14 +222,18 @@ void ModemComponent::loop() { switch (this->state_) { case ModemComponentState::STOPPED: if (this->started_) { - ESP_LOGI(TAG, "Starting modem connection"); - this->state_ = ModemComponentState::CONNECTING; - this->start_connect_(); + if (!this->modem_ready()) { + break; + } else { + ESP_LOGI(TAG, "Starting modem connection"); + this->state_ = ModemComponentState::CONNECTING; + this->start_connect_(); + } } break; case ModemComponentState::CONNECTING: if (!this->started_) { - ESP_LOGI(TAG, "Stopped ethernet connection"); + ESP_LOGI(TAG, "Stopped modem connection"); this->state_ = ModemComponentState::STOPPED; } else if (this->connected_) { // connection established @@ -308,101 +277,6 @@ void ModemComponent::dump_connect_params_() { ESP_LOGCONFIG(TAG, " DNS fallback: %s", network::IPAddress(dns_fallback_ip).str().c_str()); } -void ModemComponent::config_gpio_() { - ESP_LOGV(TAG, "Configuring GPIOs..."); - gpio_config_t io_conf = {}; - io_conf.intr_type = GPIO_INTR_DISABLE; - io_conf.mode = GPIO_MODE_OUTPUT; - io_conf.pin_bit_mask = 0ULL; - if (this->power_pin_ != gpio_num_t::GPIO_NUM_NC) { - io_conf.pin_bit_mask = io_conf.pin_bit_mask | (1ULL << this->power_pin_); - } - if (this->flight_pin_ != gpio_num_t::GPIO_NUM_NC) { - io_conf.pin_bit_mask = io_conf.pin_bit_mask | (1ULL << this->flight_pin_); - } - if (this->dtr_pin_ != gpio_num_t::GPIO_NUM_NC) { - io_conf.pin_bit_mask = io_conf.pin_bit_mask | (1ULL << this->dtr_pin_); - } - // io_conf.pin_bit_mask = ((1ULL << this->power_pin_) | (1ULL << this->flight_pin_)); - - io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; - io_conf.pull_up_en = GPIO_PULLUP_DISABLE; - - gpio_config(&io_conf); - - io_conf.pin_bit_mask = 0ULL; - if (this->status_pin_ != gpio_num_t::GPIO_NUM_NC) { - io_conf.pin_bit_mask = io_conf.pin_bit_mask | (1ULL << this->status_pin_); - } - io_conf.mode = GPIO_MODE_INPUT; - gpio_config(&io_conf); -} - -void ModemComponent::poweron() { - ESP_LOGI(TAG, "Power on modem"); - - if (this->get_status()) { - ESP_LOGW(TAG, "modem is already on"); - } else { - // https://github.com/Xinyuan-LilyGO/LilyGO-T-SIM7000G/issues/251 - if (this->power_pin_ != gpio_num_t::GPIO_NUM_NC) { - vTaskDelay(pdMS_TO_TICKS(1000)); - ESP_ERROR_CHECK(gpio_set_level(this->power_pin_, 0)); // low = on, high = off - vTaskDelay(pdMS_TO_TICKS(10)); - ESP_ERROR_CHECK(gpio_set_level(this->power_pin_, 1)); - vTaskDelay(pdMS_TO_TICKS(1010)); - ESP_ERROR_CHECK(gpio_set_level(this->power_pin_, 0)); - vTaskDelay(pdMS_TO_TICKS(4050)); // Ton uart 4.5sec but seems to need ~7sec after hard (button) reset - int retry = 0; - while (!this->get_status()) { - vTaskDelay(pdMS_TO_TICKS(1000)); - retry++; - if (retry > 10) { - ESP_LOGW(TAG, "Unable to power on modem"); - break; - } - } - - } else { - ESP_LOGW(TAG, "No power pin defined. Trying to continue"); - } - } - - if (this->flight_pin_ != gpio_num_t::GPIO_NUM_NC) { - ESP_ERROR_CHECK(gpio_set_level(this->flight_pin_, 1)); // need to be high - } else { - ESP_LOGW(TAG, "No flight pin defined. Trying to continue"); - } - if (this->dtr_pin_ != gpio_num_t::GPIO_NUM_NC) { - ESP_ERROR_CHECK(gpio_set_level(this->dtr_pin_, 1)); - } else { - ESP_LOGW(TAG, "No dtr pin defined. Trying to continue"); - } - vTaskDelay(pdMS_TO_TICKS(15000)); - App.feed_wdt(); -} - -void ModemComponent::powerdown() { - ESP_LOGI(TAG, "Power down modem"); - if (this->get_status()) { - // https://github.com/Xinyuan-LilyGO/T-SIM7600X/blob/master/examples/PowefOffModem/PowefOffModem.ino#L69-L71 - ESP_ERROR_CHECK(gpio_set_level(this->power_pin_, 1)); - vTaskDelay(pdMS_TO_TICKS(2600)); - ESP_ERROR_CHECK(gpio_set_level(this->power_pin_, 0)); - int retry = 0; - while (this->get_status()) { - vTaskDelay(pdMS_TO_TICKS(1000)); - retry++; - if (retry > 20) { - ESP_LOGW(TAG, "Unable to power down modem"); - break; - } - } - } else { - ESP_LOGW(TAG, "modem is already down"); - } -} - } // namespace modem } // namespace esphome diff --git a/esphome/components/modem/modem_component.h b/esphome/components/modem/modem_component.h index f747784d00..520b61143b 100644 --- a/esphome/components/modem/modem_component.h +++ b/esphome/components/modem/modem_component.h @@ -4,11 +4,14 @@ #include "esphome/core/component.h" #include "esphome/core/log.h" #include "esphome/components/network/util.h" +#include "esphome/components/binary_sensor/binary_sensor.h" +#include "esphome/components/template/binary_sensor/template_binary_sensor.h" #ifdef USE_ESP_IDF -using esphome::esp_log_printf_; // esp_modem will use esphome logger (needed if other components include - // esphome/core/log.h) +// esp_modem will use esphome logger (needed if other components include esphome/core/log.h) +// We need to do this because "cxx_include/esp_modem_api.hpp" is not a pure C++ header, and use logging. +using esphome::esp_log_printf_; #include <cxx_include/esp_modem_api.hpp> #include <driver/gpio.h> #include <esp_modem_config.h> @@ -39,37 +42,27 @@ class ModemComponent : public Component { bool is_connected(); float get_setup_priority() const override; bool can_proceed() override; - void on_shutdown() override { powerdown(); } network::IPAddresses get_ip_addresses(); std::string get_use_address() const; void set_use_address(const std::string &use_address); - void poweron(); - void powerdown(); void set_rx_pin(gpio_num_t rx_pin) { this->rx_pin_ = rx_pin; } void set_tx_pin(gpio_num_t tx_pin) { this->tx_pin_ = tx_pin; } - void set_power_pin(gpio_num_t power_pin) { this->power_pin_ = power_pin; } - void set_flight_pin(gpio_num_t flight_pin) { this->flight_pin_ = flight_pin; } void set_username(const std::string username) { this->username_ = std::move(username); } void set_password(const std::string password) { this->password_ = std::move(password); } void set_pin_code(const std::string pin_code) { this->pin_code_ = std::move(pin_code); } void set_apn(const std::string apn) { this->apn_ = std::move(apn); } - void set_status_pin(gpio_num_t status_pin) { this->status_pin_ = status_pin; } - void set_dtr_pin(gpio_num_t dtr_pin) { this->dtr_pin_ = dtr_pin; } void set_model(const std::string &model) { this->model_ = this->modem_model_map_.count(model) ? modem_model_map_[model] : ModemModel::UNKNOWN; } + void set_ready_bsensor(binary_sensor::BinarySensor *modem_ready) { this->modem_ready_ = modem_ready; } void add_init_at_command(const std::string &cmd) { this->init_at_commands_.push_back(cmd); } - bool get_status() { return gpio_get_level(this->status_pin_); } + bool modem_ready() { return this->modem_ready_->state; } std::unique_ptr<DCE> dce; protected: gpio_num_t rx_pin_ = gpio_num_t::GPIO_NUM_NC; gpio_num_t tx_pin_ = gpio_num_t::GPIO_NUM_NC; - gpio_num_t power_pin_ = gpio_num_t::GPIO_NUM_NC; - gpio_num_t flight_pin_ = gpio_num_t::GPIO_NUM_NC; - gpio_num_t status_pin_ = gpio_num_t::GPIO_NUM_NC; - gpio_num_t dtr_pin_ = gpio_num_t::GPIO_NUM_NC; - + binary_sensor::BinarySensor *modem_ready_; std::string pin_code_; std::string username_; std::string password_; @@ -94,7 +87,6 @@ class ModemComponent : public Component { static void got_ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); void dump_connect_params_(); std::string use_address_; - void config_gpio_(); }; // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)