mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	gsm component
This commit is contained in:
		
							
								
								
									
										113
									
								
								esphome/components/gsm/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								esphome/components/gsm/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,113 @@ | ||||
| from esphome.const import ( | ||||
|     CONF_ID, | ||||
|     CONF_USE_ADDRESS, | ||||
|     CONF_TX_PIN, | ||||
|     CONF_RX_PIN, | ||||
|     CONF_USERNAME, | ||||
|     CONF_PASSWORD, | ||||
|     CONF_MODEL, | ||||
| ) | ||||
| 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 | ||||
|  | ||||
| CODEOWNERS = ["@oarcher"] | ||||
| DEPENDENCIES = ["esp32"] | ||||
| AUTO_LOAD = ["network"] | ||||
|  | ||||
| CONF_POWER_PIN = "power_pin" | ||||
| CONF_FLIGHT_PIN = "flight_pin" | ||||
| CONF_PIN_CODE = "pin_code" | ||||
| CONF_APN = "apn" | ||||
| CONF_STATUS_PIN = "status_pin" | ||||
| CONF_DTR_PIN = "dtr_pin" | ||||
|  | ||||
|  | ||||
| gsm_ns = cg.esphome_ns.namespace("gsm") | ||||
| GSMComponent = gsm_ns.class_("GSMComponent", cg.Component) | ||||
|  | ||||
|  | ||||
| CONFIG_SCHEMA = cv.All( | ||||
|     cv.Schema( | ||||
|         { | ||||
|             cv.GenerateID(): cv.declare_id(GSMComponent), | ||||
|             cv.Required(CONF_TX_PIN): cv.positive_int, | ||||
|             cv.Required(CONF_RX_PIN): cv.positive_int, | ||||
|             cv.Required(CONF_MODEL): cv.string, | ||||
|             cv.Required(CONF_APN): cv.string, | ||||
|             cv.Optional(CONF_FLIGHT_PIN): cv.positive_int, | ||||
|             cv.Optional(CONF_POWER_PIN): cv.positive_int, | ||||
|             cv.Optional(CONF_STATUS_PIN): cv.positive_int, | ||||
|             cv.Optional(CONF_DTR_PIN): cv.positive_int, | ||||
|             cv.Optional(CONF_PIN_CODE): cv.string_strict, | ||||
|             cv.Optional(CONF_USERNAME): cv.string, | ||||
|             cv.Optional(CONF_PASSWORD): cv.string, | ||||
|             cv.Optional(CONF_USE_ADDRESS): cv.string, | ||||
|         } | ||||
|     ).extend(cv.COMPONENT_SCHEMA), | ||||
|     cv.require_framework_version( | ||||
|         esp_idf=cv.Version(4, 0, 0),  # 5.2.0 OK | ||||
|     ), | ||||
| ) | ||||
|  | ||||
|  | ||||
| @coroutine_with_priority(60.0) | ||||
| async def to_code(config): | ||||
|     add_idf_component( | ||||
|         name="esp_modem", | ||||
|         repo="https://github.com/espressif/esp-protocols.git", | ||||
|         ref="modem-v1.1.0", | ||||
|         path="components/esp_modem", | ||||
|     ) | ||||
|  | ||||
|     add_idf_sdkconfig_option("CONFIG_LWIP_DHCPS", False) | ||||
|     add_idf_sdkconfig_option("CONFIG_ESP_TASK_WDT_TIMEOUT_S", 60) | ||||
|     add_idf_sdkconfig_option("CONFIG_PPP", True) | ||||
|     add_idf_sdkconfig_option("CONFIG_LWIP_PPP_SUPPORT", True) | ||||
|     add_idf_sdkconfig_option("CONFIG_PPP_PAP_SUPPORT", True) | ||||
|     add_idf_sdkconfig_option("CONFIG_LWIP_PPP_PAP_SUPPORT", True) | ||||
|     add_idf_sdkconfig_option("CONFIG_LOG_DEFAULT_LEVEL_VERBOSE", True) | ||||
|     # add_idf_sdkconfig_option("CONFIG_LOG_DEFAULT_LEVEL_DEBUG", True) | ||||
|     add_idf_sdkconfig_option("CONFIG_EXAMPLE_CLOSE_CMUX_AT_END", True) | ||||
|     add_idf_sdkconfig_option("CONFIG_ESP_MODEM_CMUX_DEFRAGMENT_PAYLOAD", True) | ||||
|     add_idf_sdkconfig_option("CONFIG_ESP_MODEM_CMUX_DELAY_AFTER_DLCI_SETUP", 0) | ||||
|     add_idf_sdkconfig_option("CONFIG_PPP_SUPPORT", True) | ||||
|     add_idf_sdkconfig_option("CONFIG_PPP_NOTIFY_PHASE_SUPPORT", True) | ||||
|     add_idf_sdkconfig_option("CONFIG_PPP_CHAP_SUPPORT", True) | ||||
|     add_idf_sdkconfig_option("CONFIG_LWIP_PPP_VJ_HEADER_COMPRESSION", True) | ||||
|     add_idf_sdkconfig_option("CONFIG_EXAMPLE_MODEM_PPP_APN", "orange") | ||||
|     add_idf_sdkconfig_option("CONFIG_LWIP_PPP_NOTIFY_PHASE_SUPPORT", True) | ||||
|  | ||||
|     cg.add_define("USE_GSM") | ||||
|  | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     if use_address := config.get(CONF_USE_ADDRESS, None): | ||||
|         cg.add(var.set_use_address(use_address)) | ||||
|  | ||||
|     if username := config.get(CONF_USERNAME, None): | ||||
|         cg.add(var.set_username(username)) | ||||
|  | ||||
|     if password := config.get(CONF_PASSWORD, None): | ||||
|         cg.add(var.set_password(password)) | ||||
|  | ||||
|     if pin_code := config.get(CONF_PIN_CODE, None): | ||||
|         cg.add(var.set_pin_code(pin_code)) | ||||
|  | ||||
|     cg.add(var.set_model(config[CONF_MODEL])) | ||||
|     cg.add(var.set_apn(config[CONF_APN])) | ||||
|  | ||||
|     gpio_num_t = cg.global_ns.enum("gpio_num_t") | ||||
|  | ||||
|     cg.add(var.set_rx_pin(getattr(gpio_num_t, f"GPIO_NUM_{config[CONF_RX_PIN]}"))) | ||||
|     cg.add(var.set_tx_pin(getattr(gpio_num_t, f"GPIO_NUM_{config[CONF_TX_PIN]}"))) | ||||
|     if pwr_pin := config.get(CONF_POWER_PIN, None): | ||||
|         cg.add(var.set_power_pin(getattr(gpio_num_t, f"GPIO_NUM_{pwr_pin}"))) | ||||
|     if flight_pin := config.get(CONF_FLIGHT_PIN, None): | ||||
|         cg.add(var.set_flight_pin(getattr(gpio_num_t, f"GPIO_NUM_{flight_pin}"))) | ||||
|     if pin_status := config.get(CONF_STATUS_PIN, None): | ||||
|         cg.add(var.set_status_pin(getattr(gpio_num_t, f"GPIO_NUM_{pin_status}"))) | ||||
|     if pin_dtr := config.get(CONF_DTR_PIN, None): | ||||
|         cg.add(var.set_dtr_pin(getattr(gpio_num_t, f"GPIO_NUM_{pin_dtr}"))) | ||||
|  | ||||
|     await cg.register_component(var, config) | ||||
							
								
								
									
										369
									
								
								esphome/components/gsm/gsm_component.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										369
									
								
								esphome/components/gsm/gsm_component.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,369 @@ | ||||
| #if defined(USE_ESP32) || defined(USE_ESP_IDF) | ||||
| #include "gsm_component.h" | ||||
| #include "esphome/core/log.h" | ||||
| #include "esphome/core/application.h" | ||||
| #include "esphome/core/defines.h" | ||||
| #include "esphome/components/network/util.h" | ||||
| #include <esp_netif.h> | ||||
| #include <esp_netif_ppp.h> | ||||
| #include <esp_log.h> | ||||
| #include <esp_event.h> | ||||
| #include <cxx_include/esp_modem_dte.hpp> | ||||
| #include <esp_modem_config.h> | ||||
| #include <cxx_include/esp_modem_api.hpp> | ||||
| #include <driver/gpio.h> | ||||
| #include <lwip/dns.h> | ||||
| #include <cstring> | ||||
| #include <iostream> | ||||
| #include "sdkconfig.h" | ||||
|  | ||||
| #define CONFIG_MODEM_UART_RX_BUFFER_SIZE 1024 | ||||
| #define CONFIG_MODEM_UART_TX_BUFFER_SIZE 512 | ||||
| #define CONFIG_MODEM_UART_EVENT_QUEUE_SIZE 30 | ||||
| #define CONFIG_MODEM_UART_EVENT_TASK_STACK_SIZE 2048 | ||||
| #define CONFIG_MODEM_UART_EVENT_TASK_PRIORITY 5 | ||||
|  | ||||
| namespace esphome { | ||||
| namespace gsm { | ||||
|  | ||||
| GSMComponent *global_gsm_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; \ | ||||
|   } | ||||
|  | ||||
| using namespace esp_modem; | ||||
|  | ||||
| GSMComponent::GSMComponent() { global_gsm_component = this; } | ||||
|  | ||||
| void GSMComponent::dump_config() { ESP_LOGCONFIG(TAG, "Config GSM:"); } | ||||
|  | ||||
| float GSMComponent::get_setup_priority() const { return setup_priority::WIFI; } | ||||
|  | ||||
| bool GSMComponent::can_proceed() { return this->is_connected(); } | ||||
|  | ||||
| network::IPAddresses GSMComponent::get_ip_addresses() { | ||||
|   network::IPAddresses addresses; | ||||
|   esp_netif_ip_info_t ip; | ||||
|   ESP_LOGV(TAG, "get_ip_addresses"); | ||||
|   esp_err_t err = esp_netif_get_ip_info(this->ppp_netif, &ip); | ||||
|   if (err != ESP_OK) { | ||||
|     ESP_LOGV(TAG, "esp_netif_get_ip_info failed: %s", esp_err_to_name(err)); | ||||
|     // TODO: do something smarter | ||||
|     // return false; | ||||
|   } else { | ||||
|     addresses[0] = network::IPAddress(&ip.ip); | ||||
|   } | ||||
|   return addresses; | ||||
| } | ||||
|  | ||||
| std::string GSMComponent::get_use_address() const { | ||||
|   if (this->use_address_.empty()) { | ||||
|     return App.get_name() + ".local"; | ||||
|   } | ||||
|   return this->use_address_; | ||||
| } | ||||
|  | ||||
| void GSMComponent::set_use_address(const std::string &use_address) { this->use_address_ = use_address; } | ||||
|  | ||||
| bool GSMComponent::is_connected() { return this->state_ == GSMComponentState::CONNECTED; } | ||||
|  | ||||
| void GSMComponent::setup() { | ||||
|   ESP_LOGI(TAG, "Setting up GSM..."); | ||||
|  | ||||
|   this->config_gpio_(); | ||||
|  | ||||
|   ESP_LOGI(TAG, "Status: %d", (int) this->get_status()); | ||||
|  | ||||
|   // to be sure the modem is not allready connected | ||||
|   // this->powerdown(); | ||||
|  | ||||
|   ESP_LOGV(TAG, "DTE setup"); | ||||
|   esp_modem_dte_config_t dte_config_ = ESP_MODEM_DTE_DEFAULT_CONFIG(); | ||||
|   this->dte_config = dte_config_; | ||||
|  | ||||
|   // this->dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG(); | ||||
|   this->dte_config.uart_config.tx_io_num = this->tx_pin_; | ||||
|   this->dte_config.uart_config.rx_io_num = this->rx_pin_; | ||||
|   // this->dte_config.uart_config.rts_io_num =  static_cast<gpio_num_t>( CONFIG_EXAMPLE_MODEM_UART_RTS_PIN); | ||||
|   // this->dte_config.uart_config.cts_io_num =  static_cast<gpio_num_t>( CONFIG_EXAMPLE_MODEM_UART_CTS_PIN); | ||||
|   this->dte_config.uart_config.rx_buffer_size = CONFIG_MODEM_UART_RX_BUFFER_SIZE; | ||||
|   this->dte_config.uart_config.tx_buffer_size = CONFIG_MODEM_UART_TX_BUFFER_SIZE; | ||||
|   this->dte_config.uart_config.event_queue_size = CONFIG_MODEM_UART_EVENT_QUEUE_SIZE; | ||||
|   this->dte_config.task_stack_size = CONFIG_MODEM_UART_EVENT_TASK_STACK_SIZE * 2; | ||||
|   this->dte_config.task_priority = CONFIG_MODEM_UART_EVENT_TASK_PRIORITY; | ||||
|   this->dte_config.dte_buffer_size = CONFIG_MODEM_UART_RX_BUFFER_SIZE / 2; | ||||
|  | ||||
|   this->dte = esp_modem::create_uart_dte(&this->dte_config); | ||||
|  | ||||
|   assert(this->dte); | ||||
|  | ||||
|   ESP_LOGV(TAG, "Set APN: %s", this->apn_.c_str()); | ||||
|   esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(this->apn_.c_str()); | ||||
|  | ||||
|   ESP_LOGV(TAG, "PPP netif setup"); | ||||
|   esp_err_t err; | ||||
|   err = esp_netif_init(); | ||||
|   ESPHL_ERROR_CHECK(err, "PPP netif init error"); | ||||
|   err = esp_event_loop_create_default(); | ||||
|   ESPHL_ERROR_CHECK(err, "PPP event loop init error"); | ||||
|   esp_netif_config_t netif_ppp_config = ESP_NETIF_DEFAULT_PPP(); | ||||
|  | ||||
|   this->ppp_netif = esp_netif_new(&netif_ppp_config); | ||||
|   assert(this->ppp_netif); | ||||
|   if (!this->username_.empty()) { | ||||
|     ESP_LOGV(TAG, "Set auth: username: %s password: %s", this->username_.c_str(), this->password_.c_str()); | ||||
|     ESPHL_ERROR_CHECK(esp_netif_ppp_set_auth(this->ppp_netif, NETIF_PPP_AUTHTYPE_PAP, this->username_.c_str(), | ||||
|                                              this->password_.c_str()), | ||||
|                       "ppp set auth"); | ||||
|   } | ||||
|   // dns setup not needed (perhaps fallback ?) | ||||
|   // esp_netif_dns_info_t dns_main = {}; | ||||
|   // dns_main.ip.u_addr.ip4.addr = esp_ip4addr_aton("8.8.8.8"); | ||||
|   // dns_main.ip.type = ESP_IPADDR_TYPE_V4; | ||||
|   // ESPHL_ERROR_CHECK(esp_netif_set_dns_info(this->ppp_netif, ESP_NETIF_DNS_MAIN, &dns_main), "dns_main"); | ||||
|  | ||||
|   // Register user defined event handers | ||||
|   err = esp_event_handler_register(IP_EVENT, IP_EVENT_PPP_GOT_IP, &GSMComponent::got_ip_event_handler, nullptr); | ||||
|   ESPHL_ERROR_CHECK(err, "GOT IP event handler register error"); | ||||
|  | ||||
|   ESP_LOGV(TAG, "DCE setup"); | ||||
|  | ||||
|   switch (this->model_) { | ||||
|     case GSMModel::BG96: | ||||
|       this->dce = create_BG96_dce(&dce_config, dte, this->ppp_netif); | ||||
|       break; | ||||
|     case GSMModel::SIM800: | ||||
|       this->dce = create_SIM800_dce(&dce_config, dte, this->ppp_netif); | ||||
|       break; | ||||
|     case GSMModel::SIM7000: | ||||
|       this->dce = create_SIM7000_dce(&dce_config, dte, this->ppp_netif); | ||||
|       break; | ||||
|     case GSMModel::SIM7600: | ||||
|       this->dce = create_SIM7600_dce(&dce_config, dte, this->ppp_netif); | ||||
|       break; | ||||
|     default: | ||||
|       ESP_LOGE(TAG, "Unknown model"); | ||||
|       return; | ||||
|       break; | ||||
|   } | ||||
|  | ||||
|   assert(this->dce); | ||||
|  | ||||
|   this->started_ = true; | ||||
|   ESP_LOGV(TAG, "Setup finished"); | ||||
| } | ||||
|  | ||||
| void GSMComponent::start_connect_() { | ||||
|   this->connect_begin_ = millis(); | ||||
|   this->status_set_warning(); | ||||
|  | ||||
|   ESP_LOGI(TAG, "Status: %d", (int) this->get_status()); | ||||
|  | ||||
|   this->poweron(); | ||||
|  | ||||
|   ESP_LOGI(TAG, "Status: %d", (int) this->get_status()); | ||||
|  | ||||
|   // 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_gsm_component->got_ipv4_address_ = false;  // why not this ? | ||||
|  | ||||
|   this->dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_COMMAND); | ||||
|   vTaskDelay(pdMS_TO_TICKS(2000)); | ||||
|  | ||||
|   command_result res = command_result::TIMEOUT; | ||||
|  | ||||
|   int retry = 0; | ||||
|   while (res != command_result::OK) { | ||||
|     res = this->dce->sync(); | ||||
|     if (res != command_result::OK) { | ||||
|       ESP_LOGW(TAG, "modem not responding"); | ||||
|       ESP_LOGI(TAG, "Status: %d", (int) this->get_status()); | ||||
|       this->dce->set_command_mode(); | ||||
|       App.feed_wdt(); | ||||
|       vTaskDelay(pdMS_TO_TICKS(7000)); | ||||
|     } | ||||
|     retry++; | ||||
|     if (retry > 10) | ||||
|       break; | ||||
|   } | ||||
|  | ||||
|   if (res != command_result::OK) { | ||||
|     ESP_LOGW(TAG, "Unable to sync modem. Will retry later"); | ||||
|     this->powerdown(); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   if (this->dte_config.uart_config.flow_control == ESP_MODEM_FLOW_CONTROL_HW) { | ||||
|     if (command_result::OK != dce->set_flow_control(2, 2)) { | ||||
|       ESP_LOGE(TAG, "Failed to set the set_flow_control mode"); | ||||
|       return; | ||||
|     } | ||||
|     ESP_LOGI(TAG, "set_flow_control OK"); | ||||
|   } else { | ||||
|     ESP_LOGI(TAG, "not set_flow_control, because 2-wire mode active."); | ||||
|   } | ||||
|  | ||||
|   /* Setup basic operation mode for the DCE (pin if used, CMUX mode) */ | ||||
|   if (!this->pin_code_.empty()) { | ||||
|     bool pin_ok = true; | ||||
|     ESP_LOGV(TAG, "Set pin code: %s", this->pin_code_.c_str()); | ||||
|     if (dce->read_pin(pin_ok) == command_result::OK && !pin_ok) { | ||||
|       ESP_MODEM_THROW_IF_FALSE(dce->set_pin(this->pin_code_) == command_result::OK, "Cannot set PIN!"); | ||||
|       vTaskDelay(pdMS_TO_TICKS(2000));  // Need to wait for some time after unlocking the SIM | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ESP_LOGD(TAG, "Entering CMUX mode"); | ||||
|  | ||||
|   vTaskDelay(pdMS_TO_TICKS(2000)); | ||||
|  | ||||
|   if (this->dce->set_mode(esp_modem::modem_mode::CMUX_MODE)) { | ||||
|     ESP_LOGD(TAG, "Modem has correctly entered multiplexed command/data mode"); | ||||
|   } else { | ||||
|     ESP_LOGE(TAG, "Failed to configure multiplexed command mode... exiting"); | ||||
|     return; | ||||
|   } | ||||
|   vTaskDelay(pdMS_TO_TICKS(2000)); | ||||
| } | ||||
|  | ||||
| void GSMComponent::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_gsm_component->got_ipv4_address_ = true; | ||||
|   global_gsm_component->connected_ = true; | ||||
| } | ||||
|  | ||||
| void GSMComponent::loop() { | ||||
|   const uint32_t now = millis(); | ||||
|  | ||||
|   switch (this->state_) { | ||||
|     case GSMComponentState::STOPPED: | ||||
|       if (this->started_) { | ||||
|         ESP_LOGI(TAG, "Starting gsm connection"); | ||||
|         this->state_ = GSMComponentState::CONNECTING; | ||||
|         this->start_connect_(); | ||||
|       } | ||||
|       break; | ||||
|     case GSMComponentState::CONNECTING: | ||||
|       if (!this->started_) { | ||||
|         ESP_LOGI(TAG, "Stopped ethernet connection"); | ||||
|         this->state_ = GSMComponentState::STOPPED; | ||||
|       } else if (this->connected_) { | ||||
|         // connection established | ||||
|         ESP_LOGI(TAG, "Connected via GSM"); | ||||
|         this->state_ = GSMComponentState::CONNECTED; | ||||
|  | ||||
|         this->dump_connect_params_(); | ||||
|         this->status_clear_warning(); | ||||
|       } else if (now - this->connect_begin_ > 45000) { | ||||
|         ESP_LOGW(TAG, "Connecting via GSM failed! Re-connecting..."); | ||||
|         this->start_connect_(); | ||||
|       } | ||||
|       break; | ||||
|     case GSMComponentState::CONNECTED: | ||||
|       if (!this->started_) { | ||||
|         ESP_LOGI(TAG, "Stopped GSM connection"); | ||||
|         this->state_ = GSMComponentState::STOPPED; | ||||
|       } else if (!this->connected_) { | ||||
|         ESP_LOGW(TAG, "Connection via GSM lost! Re-connecting..."); | ||||
|         this->state_ = GSMComponentState::CONNECTING; | ||||
|         this->start_connect_(); | ||||
|       } | ||||
|       break; | ||||
|   } | ||||
| } | ||||
|  | ||||
| void GSMComponent::dump_connect_params_() { | ||||
|   esp_netif_ip_info_t ip; | ||||
|   esp_netif_get_ip_info(this->ppp_netif, &ip); | ||||
|   ESP_LOGCONFIG(TAG, "  IP Address: %s", network::IPAddress(&ip.ip).str().c_str()); | ||||
|   ESP_LOGCONFIG(TAG, "  Hostname: '%s'", App.get_name().c_str()); | ||||
|   ESP_LOGCONFIG(TAG, "  Subnet: %s", network::IPAddress(&ip.netmask).str().c_str()); | ||||
|   ESP_LOGCONFIG(TAG, "  Gateway: %s", network::IPAddress(&ip.gw).str().c_str()); | ||||
|  | ||||
|   const ip_addr_t *dns_main_ip = dns_getserver(ESP_NETIF_DNS_MAIN); | ||||
|   const ip_addr_t *dns_backup_ip = dns_getserver(ESP_NETIF_DNS_BACKUP); | ||||
|   const ip_addr_t *dns_fallback_ip = dns_getserver(ESP_NETIF_DNS_FALLBACK); | ||||
|  | ||||
|   ESP_LOGCONFIG(TAG, "  DNS main: %s", network::IPAddress(dns_main_ip).str().c_str()); | ||||
|   ESP_LOGCONFIG(TAG, "  DNS backup: %s", network::IPAddress(dns_backup_ip).str().c_str()); | ||||
|   ESP_LOGCONFIG(TAG, "  DNS fallback: %s", network::IPAddress(dns_fallback_ip).str().c_str()); | ||||
| } | ||||
|  | ||||
| void GSMComponent::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 GSMComponent::poweron(void) { | ||||
|   /* Power on the modem */ | ||||
|  | ||||
|   ESP_LOGI(TAG, "Status: %d", (int) this->get_status()); | ||||
|  | ||||
|   ESP_LOGI(TAG, "Power on  modem"); | ||||
|   // 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 | ||||
|   } 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"); | ||||
|   } | ||||
|   vTaskDelay(pdMS_TO_TICKS(15000)); | ||||
|   App.feed_wdt(); | ||||
| } | ||||
|  | ||||
| void GSMComponent::powerdown(void) { | ||||
|   ESP_LOGI(TAG, "Power down modem"); | ||||
|   ESP_ERROR_CHECK(gpio_set_level(this->power_pin_, 1)); | ||||
| } | ||||
|  | ||||
| }  // namespace gsm | ||||
| }  // namespace esphome | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										99
									
								
								esphome/components/gsm/gsm_component.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								esphome/components/gsm/gsm_component.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,99 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <memory> | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/components/network/util.h" | ||||
|  | ||||
| #if defined(USE_ESP32) || defined(USE_ESP_IDF) | ||||
|  | ||||
| #include <esp_log.h> | ||||
| #include <cxx_include/esp_modem_api.hpp> | ||||
| #include <esp_modem_config.h> | ||||
| #include <driver/gpio.h> | ||||
| #include <unordered_map> | ||||
|  | ||||
| namespace esphome { | ||||
| namespace gsm { | ||||
|  | ||||
| static const char *const TAG = "gsm"; | ||||
|  | ||||
| enum class GSMComponentState { | ||||
|   STOPPED, | ||||
|   CONNECTING, | ||||
|   CONNECTED, | ||||
| }; | ||||
|  | ||||
| enum class GSMModel { BG96, SIM800, SIM7000, SIM7070, SIM7070_GNSS, SIM7600, UNKNOWN }; | ||||
|  | ||||
| class GSMComponent : public Component { | ||||
|  public: | ||||
|   GSMComponent(); | ||||
|   void dump_config() override; | ||||
|   void setup() override; | ||||
|   void loop() override; | ||||
|   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(std::string username) { this->username_ = username; } | ||||
|   void set_password(std::string password) { this->password_ = password; } | ||||
|   void set_pin_code(std::string pin_code) { this->pin_code_ = pin_code; } | ||||
|   void set_apn(std::string apn) { this->apn_ = 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(std::string model) { | ||||
|     this->model_ = this->gsm_model_map_.count(model) ? gsm_model_map_[model] : GSMModel::UNKNOWN; | ||||
|   } | ||||
|   bool get_status() { return gpio_get_level(this->status_pin_); } | ||||
|  | ||||
|  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; | ||||
|  | ||||
|   std::string pin_code_; | ||||
|   std::string username_; | ||||
|   std::string password_; | ||||
|   std::string apn_; | ||||
|   GSMModel model_; | ||||
|   std::unordered_map<std::string, GSMModel> gsm_model_map_ = {{"BG96", GSMModel::BG96}, | ||||
|                                                               {"SIM800", GSMModel::SIM800}, | ||||
|                                                               {"SIM7000", GSMModel::SIM7000}, | ||||
|                                                               {"SIM7070", GSMModel::SIM7070}, | ||||
|                                                               {"SIM7070_GNSS", GSMModel::SIM7070_GNSS}, | ||||
|                                                               {"SIM7600", GSMModel::SIM7600}}; | ||||
|   std::shared_ptr<esp_modem::DTE> dte; | ||||
|   std::unique_ptr<esp_modem::DCE> dce;  // public ? | ||||
|   esp_modem::esp_netif_t *ppp_netif{nullptr}; | ||||
|   esp_modem_dte_config_t dte_config; | ||||
|   GSMComponentState state_{GSMComponentState::STOPPED}; | ||||
|   void start_connect_(); | ||||
|   bool started_{false}; | ||||
|   bool connected_{false}; | ||||
|   bool got_ipv4_address_{false}; | ||||
|   uint32_t connect_begin_; | ||||
|   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) | ||||
| extern GSMComponent *global_gsm_component; | ||||
|  | ||||
| }  // namespace gsm | ||||
| }  // namespace esphome | ||||
|  | ||||
| #endif | ||||
| @@ -4,6 +4,7 @@ | ||||
| #include <cstdio> | ||||
| #include <array> | ||||
| #include "esphome/core/macros.h" | ||||
| #include "esphome/core/helpers.h" | ||||
|  | ||||
| #if defined(USE_ESP_IDF) || defined(USE_LIBRETINY) || USE_ARDUINO_VERSION_CODE > VERSION_CODE(3, 0, 0) | ||||
| #include <lwip/ip_addr.h> | ||||
| @@ -116,7 +117,7 @@ struct IPAddress { | ||||
|   bool is_set() { return !ip_addr_isany(&ip_addr_); } | ||||
|   bool is_ip4() { return IP_IS_V4(&ip_addr_); } | ||||
|   bool is_ip6() { return IP_IS_V6(&ip_addr_); } | ||||
|   std::string str() const { return ipaddr_ntoa(&ip_addr_); } | ||||
|   std::string str() const { return str_lower_case(ipaddr_ntoa(&ip_addr_)); } | ||||
|   bool operator==(const IPAddress &other) const { return ip_addr_cmp(&ip_addr_, &other.ip_addr_); } | ||||
|   bool operator!=(const IPAddress &other) const { return !ip_addr_cmp(&ip_addr_, &other.ip_addr_); } | ||||
|   IPAddress &operator+=(uint8_t increase) { | ||||
|   | ||||
| @@ -9,6 +9,10 @@ | ||||
| #include "esphome/components/ethernet/ethernet_component.h" | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_GSM | ||||
| #include "esphome/components/gsm/gsm_component.h" | ||||
| #endif | ||||
|  | ||||
| namespace esphome { | ||||
| namespace network { | ||||
|  | ||||
| @@ -23,6 +27,11 @@ bool is_connected() { | ||||
|     return wifi::global_wifi_component->is_connected(); | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_GSM | ||||
|   if (gsm::global_gsm_component != nullptr) | ||||
|     return gsm::global_gsm_component->is_connected(); | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_HOST | ||||
|   return true;  // Assume its connected | ||||
| #endif | ||||
| @@ -46,6 +55,7 @@ network::IPAddresses get_ip_addresses() { | ||||
|   if (wifi::global_wifi_component != nullptr) | ||||
|     return wifi::global_wifi_component->get_ip_addresses(); | ||||
| #endif | ||||
|  | ||||
|   return {}; | ||||
| } | ||||
|  | ||||
| @@ -57,6 +67,10 @@ std::string get_use_address() { | ||||
| #ifdef USE_WIFI | ||||
|   if (wifi::global_wifi_component != nullptr) | ||||
|     return wifi::global_wifi_component->get_use_address(); | ||||
| #endif | ||||
| #ifdef USE_GSM | ||||
|   if (gsm::global_gsm_component != nullptr) | ||||
|     return gsm::global_gsm_component->get_use_address(); | ||||
| #endif | ||||
|   return ""; | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user