From 348b2e42d62376561e397863ce06b4415c3dd85c Mon Sep 17 00:00:00 2001 From: Chelios Date: Tue, 27 Aug 2024 10:11:51 +0300 Subject: [PATCH] add modem component --- dependencies.lock | 37 ++ esphome/components/modem/__init__.py | 147 +++++++ esphome/components/modem/modem_component.cpp | 403 +++++++++++++++++++ esphome/components/modem/modem_component.h | 83 ++++ esphome/const.py | 1 + esphome/core/__init__.py | 4 + esphome/core/component.cpp | 1 + esphome/core/component.h | 1 + esphome/idf_component.yml | 3 + 9 files changed, 680 insertions(+) create mode 100644 dependencies.lock create mode 100644 esphome/components/modem/__init__.py create mode 100644 esphome/components/modem/modem_component.cpp create mode 100644 esphome/components/modem/modem_component.h diff --git a/dependencies.lock b/dependencies.lock new file mode 100644 index 0000000000..a96aed4479 --- /dev/null +++ b/dependencies.lock @@ -0,0 +1,37 @@ +dependencies: + esp-tflite-micro: + component_hash: 09acb9889f98d0e7b5d9d1aa7184d455cfd30268d627b43371b7593106d0fc4b + source: + git: https://github.com/espressif/esp-tflite-micro.git + path: . + type: git + version: c5d97c9577de0eb9b8f05078da84bbb9ff0f748f + esp32_camera: + component_hash: 659f1353ee3dd6db885667c61e45fad5574c6c4d832fbe6797ea224d6f61d1e3 + source: + git: https://github.com/espressif/esp32-camera.git + path: . + type: git + version: 30aeeeed61e2b0182d673fe2cfedb1b7aa43b1b9 + esp_modem: + component_hash: ece3d43c4541d88eba1484cce471e9c148052df49388dcd11bee8d453064af9a + source: + git: https://github.com/espressif/esp-protocols.git + path: components/esp_modem + type: git + version: 73c48307a367565f69e724df8641f8cbac45251d + espressif/esp-nn: + component_hash: b32869798bdb40dec6bc99caca48cd65d42f8a9f506b9ab9c598a076f891ede9 + source: + pre_release: true + service_url: https://api.components.espressif.com/ + type: service + version: 1.0.2 + idf: + component_hash: null + source: + type: idf + version: 4.4.7 +manifest_hash: 9e758d00f6141ced5cecfa8032dab873feba457ffaf6cdc3fcb3337947a3c8ed +target: esp32 +version: 1.0.0 diff --git a/esphome/components/modem/__init__.py b/esphome/components/modem/__init__.py new file mode 100644 index 0000000000..dc8e9c2f96 --- /dev/null +++ b/esphome/components/modem/__init__.py @@ -0,0 +1,147 @@ +from esphome import pins +import esphome.config_validation as cv +import esphome.codegen as cg +from esphome.const import ( + CONF_DOMAIN, + CONF_ID, + CONF_MANUAL_IP, + CONF_STATIC_IP, + CONF_TYPE, + CONF_USE_ADDRESS, + CONF_GATEWAY, + CONF_SUBNET, + CONF_DNS1, + CONF_DNS2, +) +from esphome.core import CORE, coroutine_with_priority +from esphome.components.esp32 import add_idf_component +from esphome.components.network import IPAddress + +CONFLICTS_WITH = ["wifi"] +DEPENDENCIES = ["esp32"] +AUTO_LOAD = ["network"] + +modem_ns = cg.esphome_ns.namespace("modem") +# CONF_PHY_ADDR = "phy_addr" +# CONF_MDC_PIN = "mdc_pin" +# CONF_MDIO_PIN = "mdio_pin" +# CONF_CLK_MODE = "clk_mode" +# CONF_POWER_PIN = "power_pin" + +ModemType = modem_ns.enum("ModemType") +MODEM_TYPES = { + "BG96": ModemType.MODEM_TYPE_BG96, + "SIM800": ModemType.MODEM_TYPE_SIM800, + "SIM7000": ModemType.MODEM_TYPE_SIM7000, + "SIM7070": ModemType.MODEM_TYPE_SIM7070, +} + +# emac_rmii_clock_mode_t = cg.global_ns.enum("emac_rmii_clock_mode_t") +# emac_rmii_clock_gpio_t = cg.global_ns.enum("emac_rmii_clock_gpio_t") +# CLK_MODES = { +# "GPIO0_IN": ( +# emac_rmii_clock_mode_t.EMAC_CLK_EXT_IN, +# emac_rmii_clock_gpio_t.EMAC_CLK_IN_GPIO, +# ), +# "GPIO0_OUT": ( +# emac_rmii_clock_mode_t.EMAC_CLK_OUT, +# emac_rmii_clock_gpio_t.EMAC_APPL_CLK_OUT_GPIO, +# ), +# "GPIO16_OUT": ( +# emac_rmii_clock_mode_t.EMAC_CLK_OUT, +# emac_rmii_clock_gpio_t.EMAC_CLK_OUT_GPIO, +# ), +# "GPIO17_OUT": ( +# emac_rmii_clock_mode_t.EMAC_CLK_OUT, +# emac_rmii_clock_gpio_t.EMAC_CLK_OUT_180_GPIO, +# ), +# } + + +# MANUAL_IP_SCHEMA = cv.Schema( +# { +# cv.Required(CONF_STATIC_IP): cv.ipv4, +# cv.Required(CONF_GATEWAY): cv.ipv4, +# cv.Required(CONF_SUBNET): cv.ipv4, +# cv.Optional(CONF_DNS1, default="0.0.0.0"): cv.ipv4, +# cv.Optional(CONF_DNS2, default="0.0.0.0"): cv.ipv4, +# } +# ) + +ModemComponent = modem_ns.class_("ModemComponent", cg.Component) +#ManualIP = ethernet_ns.struct("ManualIP") + + +def _validate(config): + if CONF_USE_ADDRESS not in config: + use_address = CORE.name + config[CONF_DOMAIN] + config[CONF_USE_ADDRESS] = use_address + return config + + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(ModemComponent), + cv.Required(CONF_TYPE): cv.enum(MODEM_TYPES, upper=True), + #cv.Required(CONF_MDC_PIN): pins.internal_gpio_output_pin_number, + # cv.Required(CONF_MDIO_PIN): pins.internal_gpio_output_pin_number, + # cv.Optional(CONF_CLK_MODE, default="GPIO0_IN"): cv.enum( + # CLK_MODES, upper=True, space="_" + # ), + # cv.Optional(CONF_PHY_ADDR, default=0): cv.int_range(min=0, max=31), + # cv.Optional(CONF_POWER_PIN): pins.internal_gpio_output_pin_number, + # cv.Optional(CONF_MANUAL_IP): MANUAL_IP_SCHEMA, + cv.Optional(CONF_DOMAIN, default=".local"): cv.domain_name, + cv.Optional(CONF_USE_ADDRESS): cv.string_strict, + # cv.Optional("enable_mdns"): cv.invalid( + # "This option has been removed. Please use the [disabled] option under the " + # "new mdns component instead." + # ), + } + ).extend(cv.COMPONENT_SCHEMA), + _validate, +) + + +# def manual_ip(config): +# return cg.StructInitializer( +# ManualIP, +# ("static_ip", IPAddress(*config[CONF_STATIC_IP].args)), +# ("gateway", IPAddress(*config[CONF_GATEWAY].args)), +# ("subnet", IPAddress(*config[CONF_SUBNET].args)), +# ("dns1", IPAddress(*config[CONF_DNS1].args)), +# ("dns2", IPAddress(*config[CONF_DNS2].args)), +# ) + + +@coroutine_with_priority(60.0) +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + + #cg.add(var.set_phy_addr(config[CONF_PHY_ADDR])) + #cg.add(var.set_mdc_pin(config[CONF_MDC_PIN])) + #cg.add(var.set_mdio_pin(config[CONF_MDIO_PIN])) + cg.add(var.set_type(config[CONF_TYPE])) + #cg.add(var.set_clk_mode(*CLK_MODES[config[CONF_CLK_MODE]])) + cg.add(var.set_use_address(config[CONF_USE_ADDRESS])) + + # if CONF_POWER_PIN in config: + # cg.add(var.set_power_pin(config[CONF_POWER_PIN])) + + # if CONF_MANUAL_IP in config: + # cg.add(var.set_manual_ip(manual_ip(config[CONF_MANUAL_IP]))) + + cg.add_define("USE_MODEM") + + if CORE.using_arduino: + cg.add_library("WiFi", None) + + if CORE.using_esp_idf: + add_idf_component( + name="esp_modem", + repo="https://github.com/espressif/esp-protocols.git", + ref="^1.1.0", + path="components/esp_modem", + ) diff --git a/esphome/components/modem/modem_component.cpp b/esphome/components/modem/modem_component.cpp new file mode 100644 index 0000000000..6f2581412d --- /dev/null +++ b/esphome/components/modem/modem_component.cpp @@ -0,0 +1,403 @@ +#define CONFIG_EXAMPLE_MODEM_PPP_APN "internet" +#define CONFIG_EXAMPLE_MODEM_PPP_AUTH_USERNAME "espressif" +#define CONFIG_EXAMPLE_MODEM_PPP_AUTH_PASSWORD "esp32" +#define CONFIG_EXAMPLE_MODEM_UART_TX_PIN 25 +#define CONFIG_EXAMPLE_MODEM_UART_RX_PIN 26 +#define CONFIG_EXAMPLE_MODEM_UART_RTS_PIN 27 +#define CONFIG_EXAMPLE_MODEM_UART_CTS_PIN 23 +#define CONFIG_EXAMPLE_MODEM_UART_EVENT_TASK_STACK_SIZE 2048 +#define CONFIG_EXAMPLE_MODEM_UART_EVENT_TASK_PRIORITY 5 +#define CONFIG_EXAMPLE_MODEM_UART_EVENT_QUEUE_SIZE 30 +#define CONFIG_EXAMPLE_MODEM_UART_PATTERN_QUEUE_SIZE 20 +#define CONFIG_EXAMPLE_MODEM_UART_TX_BUFFER_SIZE 512 +#define CONFIG_EXAMPLE_MODEM_UART_RX_BUFFER_SIZE 1024 + + +#include "esp_modem_c_api_types.h" +#include "esp_netif_ppp.h" +#include "cxx_include/esp_modem_api.hpp" +#include "cxx_include/esp_modem_dte.hpp" +#include "cxx_include/esp_modem_dce.hpp" + +#include "modem_component.h" +#include "esphome/core/log.h" +#include "esphome/core/util.h" +#include "esphome/core/application.h" + +#ifdef USE_ESP32 + +#include +#include +#include "esp_event.h" + + +std::shared_ptr dte{nullptr}; +std::unique_ptr dce{nullptr}; + +uint32_t time_info_print = 0; +uint32_t time_hard_reset_modem = 0; +uint32_t time_check_rssi = 0; + +#define TIME_TO_NEXT_HARD_RESET 30000 +#define TIME_TO_START_MODEM 9000 + +namespace esphome { +namespace modem { + +static EventGroupHandle_t event_group = NULL; +static const int CONNECT_BIT = BIT0; +static const int GOT_DATA_BIT = BIT2; +static const int USB_DISCONNECTED_BIT = BIT3; +static const char *const TAG = "modem"; + +ModemComponent *global_modem_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + +#define ESPHL_ERROR_CHECK(err, message) \ + if ((err) != ESP_OK) { \ + ESP_LOGE(TAG, message ": (%d) %s", err, esp_err_to_name(err)); \ + this->mark_failed(); \ + return; \ + } + +ModemComponent::ModemComponent() { global_modem_component = this; } +void print_netif_flags(esp_netif_flags_t flags) { + // Check each flag and print its presecommand_lib%s", (flags & ESP_NETIF_DHCP_CLIENT) ? "true" : "false"); + ESP_LOGI(TAG, "ESP_NETIF_DHCP_SERVER: %s", (flags & ESP_NETIF_DHCP_SERVER) ? "true" : "false"); + ESP_LOGI(TAG, "ESP_NETIF_FLAG_AUTOUP: %s", (flags & ESP_NETIF_FLAG_AUTOUP) ? "true" : "false"); + ESP_LOGI(TAG, "ESP_NETIF_FLAG_GARP: %s", (flags & ESP_NETIF_FLAG_GARP) ? "true" : "false"); + ESP_LOGI(TAG, "ESP_NETIF_FLAG_EVENT_IP_MODIFIED: %s", (flags & ESP_NETIF_FLAG_EVENT_IP_MODIFIED) ? "true" : "false"); + ESP_LOGI(TAG, "ESP_NETIF_FLAG_IS_PPP: %s", (flags & ESP_NETIF_FLAG_IS_PPP) ? "true" : "false"); + ESP_LOGI(TAG, "ESP_NETIF_FLAG_IS_SLIP: %s", (flags & ESP_NETIF_FLAG_IS_SLIP) ? "true" : "false"); + ESP_LOGI(TAG, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); +} + +#include "driver/gpio.h" +void esp_modem_hard_reset() +{ + gpio_set_direction(GPIO_NUM_23, GPIO_MODE_OUTPUT); + //gpio_pullup_en(GPIO_NUM_23); + gpio_set_level(GPIO_NUM_23, 0); + ESP_LOGI(TAG, "GPIO_NUM_23 0"); + vTaskDelay(50); + gpio_set_level(GPIO_NUM_23, 1); + ESP_LOGI(TAG, "GPIO_NUM_23 1"); + vTaskDelay(2000); + time_hard_reset_modem = millis(); +} + +int get_rssi(){ + int rssi=0, ber=0; + esp_modem::command_result errr = dce->get_signal_quality(rssi, ber); + //esp_err_t err = esp_modem::esp_modem_get_signal_quality(dce, &rssi, &ber); + if (errr != esp_modem::command_result::OK) { + ESP_LOGE(TAG, "esp_modem_get_signal_quality failed with"); + } + return rssi; +} + +int get_modem_voltage(){ + int voltage=0, bcs=0, bcl=0; + dce->set_cmux(); + esp_modem::command_result errr = dce->get_battery_status(voltage, bcs, bcl); + if (errr != esp_modem::command_result::OK) { + ESP_LOGE(TAG, "get_battery_status failed with"); + } + return voltage; +} + +//setup +void ModemComponent::setup() { + + esp_log_level_set("esp-netif_lwip-ppp", ESP_LOG_VERBOSE); + esp_log_level_set("esp-netif_lwip", ESP_LOG_VERBOSE); + esp_log_level_set("modem", ESP_LOG_VERBOSE); + esp_log_level_set("mqtt", ESP_LOG_VERBOSE); + esp_log_level_set("command_lib", ESP_LOG_VERBOSE); + + ESP_LOGCONFIG(TAG, "Setting up modem..."); + esp_modem_hard_reset(); + if (esp_reset_reason() != ESP_RST_DEEPSLEEP) { + // Delay here to allow power to stabilise before Modem is initialized. + delay(300); // NOLINT + } + + esp_err_t err; + err = esp_netif_init(); + ESPHL_ERROR_CHECK(err, "modem netif init error"); + err = esp_event_loop_create_default(); + ESPHL_ERROR_CHECK(err, "modem event loop error"); + ESP_LOGCONFIG(TAG, "Initing netif"); + + esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &ModemComponent::got_ip_event_handler, NULL); + //esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, &on_ppp_changed, NULL); + + /* Configure the PPP netif */ + + esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_EXAMPLE_MODEM_PPP_APN); + esp_netif_config_t netif_ppp_config = ESP_NETIF_DEFAULT_PPP(); + this->modem_netif_ = esp_netif_new(&netif_ppp_config); + //esp_netif_t *esp_netif = esp_netif_new(&netif_ppp_config); + assert(this->modem_netif_); + ESP_LOGI(TAG, "netif create succes"); + event_group = xEventGroupCreate(); + + /* Configure the DTE */ + esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG(); + /* setup UART specific configuration based on kconfig options */ + dte_config.uart_config.tx_io_num = CONFIG_EXAMPLE_MODEM_UART_TX_PIN; + dte_config.uart_config.rx_io_num = CONFIG_EXAMPLE_MODEM_UART_RX_PIN; + dte_config.uart_config.rts_io_num = CONFIG_EXAMPLE_MODEM_UART_RTS_PIN; + dte_config.uart_config.cts_io_num = CONFIG_EXAMPLE_MODEM_UART_CTS_PIN; + dte_config.uart_config.rx_buffer_size = CONFIG_EXAMPLE_MODEM_UART_RX_BUFFER_SIZE; + dte_config.uart_config.tx_buffer_size = CONFIG_EXAMPLE_MODEM_UART_TX_BUFFER_SIZE; + dte_config.uart_config.event_queue_size = CONFIG_EXAMPLE_MODEM_UART_EVENT_QUEUE_SIZE; + dte_config.task_stack_size = CONFIG_EXAMPLE_MODEM_UART_EVENT_TASK_STACK_SIZE; + dte_config.task_priority = CONFIG_EXAMPLE_MODEM_UART_EVENT_TASK_PRIORITY; + dte_config.dte_buffer_size = CONFIG_EXAMPLE_MODEM_UART_RX_BUFFER_SIZE / 2; + + ESP_LOGI(TAG, "Initializing esp_modem for the SIM800 module..."); + dte = esp_modem::create_uart_dte(&dte_config); + //esp_modem_dce_t *dce = esp_modem_new_dev(ESP_MODEM_DCE_SIM800, &dte_config, &dce_config, this->modem_netif_); + dce = esp_modem::create_SIM800_dce(&dce_config, dte, this->modem_netif_); + + + xEventGroupClearBits(event_group, CONNECT_BIT | GOT_DATA_BIT | USB_DISCONNECTED_BIT); + /* Run the modem demo app */ + //return; + + esp_netif_flags_t flags = esp_netif_get_flags(this->modem_netif_); + print_netif_flags(flags); + + + //set data mode + //dce->set_data(); + + //this->modem_netif_-> + /* Wait for IP address */ + //ESP_LOGI(TAG, "Waiting for IP address"); + //xEventGroupWaitBits(event_group, CONNECT_BIT | USB_DISCONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY); + //vTaskDelay(15000); +} + + +void ModemComponent::loop() { + const uint32_t now = millis(); + if (time_info_print < now){ + + //ESP_LOGI(TAG, "voltage %dV.", get_modem_voltage()/1000); + ESP_LOGI(TAG, "esp_netif_is_netif_UP"); + if (esp_netif_is_netif_up(this->modem_netif_)){ + ESP_LOGI(TAG, "esp_netif_is_netif_UP"); + } + else{ + ESP_LOGI(TAG, "esp_netif_is_netif_DOWN"); + } + time_info_print = now + 5000; + switch (this->state_) + { + case ModemComponentState::STOPPED: + ESP_LOGI(TAG, "modem STOPPED"); + break; + case ModemComponentState::CONNECTING: + ESP_LOGI(TAG, "modem CONNECTING"); + break; + case ModemComponentState::CONNECTED: + dce->set_data(); + ESP_LOGI(TAG, "modem CONNECTED"); + break; + default: + break; + } + //ESP_LOGI(TAG, "SIgnal quality: rssi=%d", get_rssi()); + } + + this->started_ = true; + + + switch (this->state_) { + case ModemComponentState::STOPPED: + if (time_check_rssi + TIME_TO_START_MODEM < now){ + time_check_rssi = now; + dce->set_command_mode(); + if (get_rssi()) { + ESP_LOGI(TAG, "Starting modem connection"); + ESP_LOGI(TAG, "SIgnal quality: rssi=%d", get_rssi()); + this->state_ = ModemComponentState::CONNECTING; + dce->set_data(); + //this->start_connect_(); + } + if (time_hard_reset_modem + TIME_TO_NEXT_HARD_RESET < now){ + time_hard_reset_modem = now; + esp_modem_hard_reset(); + } + } + break; + case ModemComponentState::CONNECTING: + break; + case ModemComponentState::CONNECTED: + break; + + // if (!this->started_) { + // ESP_LOGI(TAG, "Stopped modem connection"); + // this->state_ = ModemComponentState::STOPPED; + // } else if (this->connected_) { + // // connection established + // ESP_LOGI(TAG, "Connected via Modem!"); + // this->state_ = ModemComponentState::CONNECTED; + + // //this->dump_connect_params_(); + // this->status_clear_warning(); + // } else if (now - this->connect_begin_ > 40000) { + // ESP_LOGW(TAG, "Connecting via modem failed! Re-connecting..."); + // this->start_connect_(); + // dce->set_data_mode(); + // } + // break; + // case ModemComponentState::CONNECTED: + // if (!this->started_) { + // ESP_LOGI(TAG, "Stopped modem connection"); + // this->state_ = ModemComponentState::STOPPED; + // } else if (!this->connected_) { + // ESP_LOGW(TAG, "Connection via Modem lost! Re-connecting..."); + // this->state_ = ModemComponentState::CONNECTING; + // this->start_connect_(); + // } + // break; + } +} + + +float ModemComponent::get_setup_priority() const { return setup_priority::WIFI; } + +bool ModemComponent::can_proceed() { return this->is_connected(); } + +network::IPAddress ModemComponent::get_ip_address() { + esp_netif_ip_info_t ip; + esp_netif_get_ip_info(this->modem_netif_, &ip); + return network::IPAddress(&ip.ip); +} + +void ModemComponent::modem_event_handler(void *arg, esp_event_base_t event_base, int32_t event, void *event_data) { + const char *event_name; + + switch (event) { + case ETHERNET_EVENT_START: + event_name = "ETH started"; + global_modem_component->started_ = true; + break; + case ETHERNET_EVENT_STOP: + event_name = "ETH stopped"; + global_modem_component->started_ = false; + global_modem_component->connected_ = false; + break; + case ETHERNET_EVENT_CONNECTED: + event_name = "ETH connected"; + break; + case ETHERNET_EVENT_DISCONNECTED: + event_name = "ETH disconnected"; + global_modem_component->connected_ = false; + break; + default: + return; + } + + ESP_LOGV(TAG, "[Modem event] %s (num=%" PRId32 ")", event_name, event); +} + +void ModemComponent::got_ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, + void *event_data) { + xEventGroupSetBits(event_group, CONNECT_BIT); + global_modem_component->connected_ = true; + global_modem_component->state_ = ModemComponentState::CONNECTED; + + ESP_LOGD(TAG, "IP event! %" PRIu32, event_id); + if (event_id == IP_EVENT_PPP_GOT_IP) { + esp_netif_dns_info_t dns_info; + + ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; + esp_netif_t *netif = event->esp_netif; + + ESP_LOGI(TAG, "Modem Connect to PPP Server"); + ESP_LOGI(TAG, "~~~~~~~~~~~~~~"); + ESP_LOGI(TAG, "IP : " IPSTR, IP2STR(&event->ip_info.ip)); + ESP_LOGI(TAG, "Netmask : " IPSTR, IP2STR(&event->ip_info.netmask)); + ESP_LOGI(TAG, "Gateway : " IPSTR, IP2STR(&event->ip_info.gw)); + esp_netif_get_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns_info); + ESP_LOGI(TAG, "Name Server1: " IPSTR, IP2STR(&dns_info.ip.u_addr.ip4)); + esp_netif_get_dns_info(netif, ESP_NETIF_DNS_BACKUP, &dns_info); + ESP_LOGI(TAG, "Name Server2: " IPSTR, IP2STR(&dns_info.ip.u_addr.ip4)); + ESP_LOGI(TAG, "~~~~~~~~~~~~~~"); + xEventGroupSetBits(event_group, CONNECT_BIT); + + ESP_LOGI(TAG, "GOT ip event!!!"); + } else if (event_id == IP_EVENT_PPP_LOST_IP) { + ESP_LOGI(TAG, "Modem Disconnect from PPP Server"); + global_modem_component->state_ = ModemComponentState::STOPPED; + } + +} + + +void ModemComponent::start_connect_() { + this->connect_begin_ = millis(); + this->status_set_warning(); + esp_modem_hard_reset(); + esp_err_t err; + err = esp_netif_set_hostname(this->modem_netif_, App.get_name().c_str()); + if (err != ERR_OK) { + ESP_LOGW(TAG, "esp_netif_set_hostname failed: %s", esp_err_to_name(err)); + } + + + // esp_netif_dhcp_status_t status = ESP_NETIF_DHCP_INIT; + + //restart ppp connection + dce->exit_data(); + int rssi, ber; + esp_modem::command_result errr = dce->get_signal_quality(rssi, ber); + //esp_err_t err = esp_modem::esp_modem_get_signal_quality(dce, &rssi, &ber); + if (errr != esp_modem::command_result::OK) { + ESP_LOGE(TAG, "esp_modem_get_signal_quality failed with"); + return; + } + ESP_LOGI(TAG, "Signal quality: rssi=%d, ber=%d", rssi, ber); + dce->set_data(); + + vTaskDelay(15000); + // this->status_set_warning(); +} + +bool ModemComponent::is_connected() { return this->state_ == ModemComponentState::CONNECTED; } + +void ModemComponent::set_power_pin(int power_pin) { this->power_pin_ = power_pin; } +void ModemComponent::set_type(ModemType type) { this->type_ = type; } + + +std::string ModemComponent::get_use_address() const { + if (this->use_address_.empty()) { + return App.get_name() + ".local"; + } + return this->use_address_; +} + +void ModemComponent::set_use_address(const std::string &use_address) { this->use_address_ = use_address; } + +bool ModemComponent::powerdown() { + // ESP_LOGI(TAG, "Powering down modem PHY"); + // if (this->phy_ == nullptr) { + // ESP_LOGE(TAG, "Modem PHY not assigned"); + // return false; + // } + // this->connected_ = false; + // this->started_ = false; + // if (this->phy_->pwrctl(this->phy_, false) != ESP_OK) { + // ESP_LOGE(TAG, "Error powering down modem PHY"); + // return false; + // } + return true; +} + +} // namespace modem +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/modem/modem_component.h b/esphome/components/modem/modem_component.h new file mode 100644 index 0000000000..456c62c0c4 --- /dev/null +++ b/esphome/components/modem/modem_component.h @@ -0,0 +1,83 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/defines.h" +#include "esphome/core/hal.h" +#include "esphome/components/network/ip_address.h" + +#ifdef USE_ESP32 + +#include "esp_netif.h" + +namespace esphome { +namespace modem { + +enum ModemType { + MODEM_TYPE_UNKNOWN = 0, + MODEM_TYPE_BG96, + MODEM_TYPE_SIM800, + MODEM_TYPE_SIM7000, + MODEM_TYPE_SIM7070, +}; + +struct ManualIP { + network::IPAddress static_ip; + network::IPAddress gateway; + network::IPAddress subnet; + network::IPAddress dns1; ///< The first DNS server. 0.0.0.0 for default. + network::IPAddress dns2; ///< The second DNS server. 0.0.0.0 for default. +}; + +enum class ModemComponentState { + STOPPED, + CONNECTING, + CONNECTED, +}; + +class ModemComponent : public Component { + public: + ModemComponent(); + void setup() override; + void loop() override; + float get_setup_priority() const override; + bool can_proceed() override; + void on_shutdown() override { powerdown(); } + bool is_connected(); + + void set_power_pin(int power_pin); + void set_type(ModemType type); + //void set_clk_mode(emac_rmii_clock_mode_t clk_mode, emac_rmii_clock_gpio_t clk_gpio); + //void set_manual_ip(const ManualIP &manual_ip); + + network::IPAddress get_ip_address(); + std::string get_use_address() const; + void set_use_address(const std::string &use_address); + bool powerdown(); + + protected: + static void modem_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); + static void got_ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); + + void start_connect_(); + + std::string use_address_; + int power_pin_{-1}; + ModemType type_{MODEM_TYPE_UNKNOWN}; + optional manual_ip_{}; + + bool started_{false}; + bool connected_{false}; + + ModemComponentState state_{ModemComponentState::STOPPED}; + uint32_t connect_begin_; + esp_netif_t *modem_netif_{nullptr}; + //esp_eth_phy_t *phy_{nullptr}; +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +extern ModemComponent *global_modem_component; + +} // namespace modem +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/const.py b/esphome/const.py index b9c37a53a8..45eb08a452 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -262,6 +262,7 @@ CONF_EQUATION = "equation" CONF_ESP8266_DISABLE_SSL_SUPPORT = "esp8266_disable_ssl_support" CONF_ESPHOME = "esphome" CONF_ETHERNET = "ethernet" +CONF_MODEM = "modem" CONF_EVENT = "event" CONF_EVENT_TYPE = "event_type" CONF_EVENT_TYPES = "event_types" diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index a97c3b18c9..958664ef8c 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -8,6 +8,7 @@ from esphome.const import ( CONF_COMMENT, CONF_ESPHOME, CONF_ETHERNET, + CONF_MODEM, CONF_PORT, CONF_USE_ADDRESS, CONF_WEB_SERVER, @@ -564,6 +565,9 @@ class EsphomeCore: if CONF_ETHERNET in self.config: return self.config[CONF_ETHERNET][CONF_USE_ADDRESS] + + if CONF_MODEM in self.config: + return self.config[CONF_MODEM][CONF_USE_ADDRESS] return None diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index ae73a451d9..8a6b86c188 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -22,6 +22,7 @@ const float BLUETOOTH = 350.0f; const float AFTER_BLUETOOTH = 300.0f; const float WIFI = 250.0f; const float ETHERNET = 250.0f; +const float MODEM = 250.0f; const float BEFORE_CONNECTION = 220.0f; const float AFTER_WIFI = 200.0f; const float AFTER_CONNECTION = 100.0f; diff --git a/esphome/core/component.h b/esphome/core/component.h index a6bd8f81ac..c68560bf5f 100644 --- a/esphome/core/component.h +++ b/esphome/core/component.h @@ -31,6 +31,7 @@ extern const float BLUETOOTH; extern const float AFTER_BLUETOOTH; extern const float WIFI; extern const float ETHERNET; +extern const float MODEM; /// For components that should be initialized after WiFi and before API is connected. extern const float BEFORE_CONNECTION; /// For components that should be initialized after WiFi is connected. diff --git a/esphome/idf_component.yml b/esphome/idf_component.yml index 5f4701b5a3..00530826ef 100644 --- a/esphome/idf_component.yml +++ b/esphome/idf_component.yml @@ -11,3 +11,6 @@ dependencies: path: components/mdns rules: - if: "idf_version >=5.0" + esp_modem: + git: https://github.com/espressif/esp-protocols.git + path: components/esp_modem \ No newline at end of file