diff --git a/esphome/components/modem/__init__.py b/esphome/components/modem/__init__.py index a6d7fe5d6b..fb5b7cc9fe 100644 --- a/esphome/components/modem/__init__.py +++ b/esphome/components/modem/__init__.py @@ -42,7 +42,7 @@ CONF_ON_NOT_RESPONDING = "on_not_responding" CONF_ENABLE_CMUX = "enable_cmux" CONF_ENABLE_GNSS = "enable_gnss" -MODEM_MODELS = ["BG96", "SIM800", "SIM7000", "SIM7600", "GENERIC"] +MODEM_MODELS = ["BG96", "SIM800", "SIM7000", "SIM7600", "SIM7670", "GENERIC"] MODEM_MODELS_POWER = { "BG96": {"ton": 600, "tonuart": 4900, "toff": 650, "toffuart": 2000}, "SIM800": {"ton": 1300, "tonuart": 3000, "toff": 200, "toffuart": 3000}, @@ -50,6 +50,12 @@ MODEM_MODELS_POWER = { "SIM7600": {"ton": 500, "tonuart": 12000, "toff": 2800, "toffuart": 25000}, } +MODEM_MODELS_POWER["SIM7670"] = MODEM_MODELS_POWER["SIM7600"] + +# SIM70xx doesn't support AT+CGNSSINFO, so gnss is not available +MODEM_MODELS_GNSS_POWER = {"SIM7600": "AT+CGPS=1", "SIM7670": "AT+CGNSSPWR=1"} + + modem_ns = cg.esphome_ns.namespace("modem") ModemComponent = modem_ns.class_("ModemComponent", cg.Component) ModemComponentState = modem_ns.enum("ModemComponentState") @@ -119,19 +125,12 @@ def final_validate_platform(config): def _final_validate(config): - # if config.get(CONF_POWER_PIN, None) and not config.get(CONF_STATUS_PIN, None): - # raise cv.Invalid( - # f"'{CONF_STATUS_PIN}' must be declared if using '{CONF_POWER_PIN}'" - # ) - # uncomment after PR#4091 merged # if wifi_config := fv.full_config.get().get(CONF_WIFI, None): # if wifi_has_sta(wifi_config): # raise cv.Invalid("Wifi must be AP only when using ethernet") if config.get(CONF_STATUS_PIN, None): _LOGGER.warning("Using '%s' is experimental", CONF_STATUS_PIN) - if config[CONF_ENABLE_CMUX]: - _LOGGER.warning("Using '%s: True' is experimental", CONF_ENABLE_CMUX) if not config[CONF_ENABLE_ON_BOOT]: _LOGGER.warning("Using '%s: False' is experimental", CONF_ENABLE_ON_BOOT) if config.get(CONF_POWER_PIN, None): @@ -139,6 +138,11 @@ def _final_validate(config): raise cv.Invalid( f"Modem model '{config[CONF_MODEL]}' has no power power specs." ) + if config.get(CONF_ENABLE_GNSS, None): + if config[CONF_MODEL] not in MODEM_MODELS_GNSS_POWER: + raise cv.Invalid( + f"Modem model '{config[CONF_MODEL]}' has no GNSS support with AT+CGNSSINFO." + ) FINAL_VALIDATE_SCHEMA = _final_validate @@ -185,10 +189,6 @@ async def to_code(config): if config[CONF_ENABLE_CMUX]: cg.add(var.enable_cmux()) - if config[CONF_ENABLE_GNSS]: - cg.add_define("USE_MODEM_GNSS") - cg.add(var.enable_gnss()) - if config[CONF_DEBUG]: cg.add(var.enable_debug()) @@ -200,6 +200,10 @@ async def to_code(config): cg.add_define("USE_MODEM_MODEL", modem_model) cg.add_define(f"USE_MODEM_MODEL_{modem_model}") + if config[CONF_ENABLE_GNSS]: + cg.add_define("USE_MODEM_GNSS") + cg.add(var.set_gnss_power_command(MODEM_MODELS_GNSS_POWER[modem_model])) + if power_spec := MODEM_MODELS_POWER.get(modem_model, None): cg.add_define("USE_MODEM_POWER") for spec, value in power_spec.items(): diff --git a/esphome/components/modem/modem_component.cpp b/esphome/components/modem/modem_component.cpp index 19d7c52726..fcd10e7c71 100644 --- a/esphome/components/modem/modem_component.cpp +++ b/esphome/components/modem/modem_component.cpp @@ -473,7 +473,6 @@ void ModemComponent::modem_lazy_init_() { if (this->dte_->set_mode(modem_mode::COMMAND_MODE)) { ESP_LOGD(TAG, "dte in command mode"); } - esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(this->apn_.c_str()); #if defined(USE_MODEM_MODEL_GENERIC) @@ -484,12 +483,11 @@ void ModemComponent::modem_lazy_init_() { this->dce = create_SIM800_dce(&dce_config, this->dte_, this->ppp_netif_); #elif defined(USE_MODEM_MODEL_SIM7000) this->dce = create_SIM7000_dce(&dce_config, this->dte_, this->ppp_netif_); -#elif defined(USE_MODEM_MODEL_SIM7600) +#elif defined(USE_MODEM_MODEL_SIM7600) || defined(USE_MODEM_MODEL_SIM7670) this->dce = create_SIM7600_dce(&dce_config, this->dte_, this->ppp_netif_); #else #error Modem model not known #endif - // flow control not fully implemented, but kept here for future work // if (dte_config.uart_config.flow_control == ESP_MODEM_FLOW_CONTROL_HW) { // if (command_result::OK != this->dce->set_flow_control(2, 2)) { @@ -508,6 +506,7 @@ bool ModemComponent::modem_sync_() { uint32_t start_ms = millis(); uint32_t elapsed_ms; + std::string result; ESP_LOGV(TAG, "Checking if the modem is synced..."); bool status = this->modem_ready(true); @@ -516,8 +515,6 @@ bool ModemComponent::modem_sync_() { // Try to exit CMUX_MANUAL_DATA or DATA_MODE, if any ESP_LOGD(TAG, "Connecting to the the modem..."); - std::string result; - auto command_mode = [this]() -> bool { ESP_LOGVV(TAG, "trying command mode"); this->dce->set_mode(modem_mode::UNDEF); @@ -553,16 +550,15 @@ bool ModemComponent::modem_sync_() { // First time the modem is synced, or modem recovered this->internal_state_.modem_synced = true; - // Fail on 7600, because esp_modem use internally AT+CGNSPWR? that is unsupported (should be AT+CGPS?) - // int gnss_power; - // ESPMODEM_ERROR_CHECK(this->dce->get_gnss_power_mode(gnss_power), "Getting GNSS power state"); - // ESP_LOGD(TAG, "GNSS power mode: %d", gnss_power); - - // enabling GNSS seems to return an error, if already enabled - // Fail on 7670, because esp_modem use internally AT+CGPS=1 that is unsupported (should be AT+CGNSSPWR=1 not - // (AT+CGNSPWR?)) - // So SIM7670 should add AT+CGNSSPWR=1 to init_at - ESPMODEM_ERROR_CHECK(this->dce->set_gnss_power_mode(this->gnss_), "Enabling/disabling GNSS"); + if (!this->gnss_power_command_.empty()) { + command_result err; + ESP_LOGD(TAG, "Enabling GNSS with command: %s", this->gnss_power_command_.c_str()); + err = this->dce->at(this->gnss_power_command_, result, this->command_delay_); + if (err == command_result::FAIL) { + // AT+CGPS=1 for SIM7600 or AT+CGNSSPWR=1 for SIM7670 often fail, but seems to be working anyway + ESP_LOGD(TAG, "GNSS power command failed. Ignoring, as the status is often FAIL, while it works later."); + } + } // ESPMODEM_ERROR_CHECK(this->dce->set_gnss_power_mode(0), "Enabling/disabling GNSS"); // delay(200); // NOLINT @@ -678,7 +674,6 @@ bool ModemComponent::stop_ppp_() { if (this->cmux_) { status = this->dce->set_mode(modem_mode::CMUX_MANUAL_COMMAND); } else { - // assert(this->dce->set_mode(modem_mode::COMMAND_MODE)); // OK on 7600, nok on 7670... status = this->dce->set_mode(modem_mode::COMMAND_MODE); } if (!status) { diff --git a/esphome/components/modem/modem_component.h b/esphome/components/modem/modem_component.h index 6c0d3ce0dc..380c8153a6 100644 --- a/esphome/components/modem/modem_component.h +++ b/esphome/components/modem/modem_component.h @@ -58,9 +58,9 @@ class ModemComponent : public Component { void set_password(const std::string &password) { this->password_ = password; } void set_pin_code(const std::string &pin_code) { this->pin_code_ = pin_code; } void set_apn(const std::string &apn) { this->apn_ = apn; } + void set_gnss_power_command(const std::string &at_command) { this->gnss_power_command_ = at_command; } void set_not_responding_cb(Trigger<> *not_responding_cb) { this->not_responding_cb_ = not_responding_cb; } void enable_cmux() { this->cmux_ = true; } - void enable_gnss() { this->gnss_ = true; } void enable_debug() { esp_log_level_set("command_lib", ESP_LOG_VERBOSE); } 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; } @@ -122,7 +122,7 @@ class ModemComponent : public Component { std::vector init_at_commands_; std::string use_address_; bool cmux_{false}; - bool gnss_{false}; + std::string gnss_power_command_; // separate handler for `on_not_responding` (we want to know when it's ended) Trigger<> *not_responding_cb_{nullptr}; CallbackManager on_state_callback_; diff --git a/esphome/components/modem/sensor/modem_sensor.cpp b/esphome/components/modem/sensor/modem_sensor.cpp index ba966000c4..d87dfd04b6 100644 --- a/esphome/components/modem/sensor/modem_sensor.cpp +++ b/esphome/components/modem/sensor/modem_sensor.cpp @@ -39,6 +39,7 @@ void ModemSensor::update() { ESP_LOGD(TAG, "Modem sensor update"); if (modem::global_modem_component->dce && modem::global_modem_component->modem_ready()) { this->update_signal_sensors_(); + App.feed_wdt(); this->update_gnss_sensors_(); } } @@ -182,7 +183,7 @@ void ModemSensor::update_gnss_sensors_() { float alt = std::stof(parts["altitude"]); float speed_knots = std::stof(parts["speed"]); float speed_kmh = speed_knots * 1.852; // Convert speed from knots to km/h - float cog = std::stof(parts["cog"]); + float cog = parts["cog"].empty() ? NAN : std::stof(parts["cog"]); float pdop = std::stof(parts["pdop"]); float hdop = std::stof(parts["hdop"]); float vdop = std::stof(parts["vdop"]);