mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +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 <cstdio> | ||||||
| #include <array> | #include <array> | ||||||
| #include "esphome/core/macros.h" | #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) | #if defined(USE_ESP_IDF) || defined(USE_LIBRETINY) || USE_ARDUINO_VERSION_CODE > VERSION_CODE(3, 0, 0) | ||||||
| #include <lwip/ip_addr.h> | #include <lwip/ip_addr.h> | ||||||
| @@ -116,7 +117,7 @@ struct IPAddress { | |||||||
|   bool is_set() { return !ip_addr_isany(&ip_addr_); } |   bool is_set() { return !ip_addr_isany(&ip_addr_); } | ||||||
|   bool is_ip4() { return IP_IS_V4(&ip_addr_); } |   bool is_ip4() { return IP_IS_V4(&ip_addr_); } | ||||||
|   bool is_ip6() { return IP_IS_V6(&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_); } | ||||||
|   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) { |   IPAddress &operator+=(uint8_t increase) { | ||||||
|   | |||||||
| @@ -9,6 +9,10 @@ | |||||||
| #include "esphome/components/ethernet/ethernet_component.h" | #include "esphome/components/ethernet/ethernet_component.h" | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | #ifdef USE_GSM | ||||||
|  | #include "esphome/components/gsm/gsm_component.h" | ||||||
|  | #endif | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace network { | namespace network { | ||||||
|  |  | ||||||
| @@ -23,6 +27,11 @@ bool is_connected() { | |||||||
|     return wifi::global_wifi_component->is_connected(); |     return wifi::global_wifi_component->is_connected(); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | #ifdef USE_GSM | ||||||
|  |   if (gsm::global_gsm_component != nullptr) | ||||||
|  |     return gsm::global_gsm_component->is_connected(); | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #ifdef USE_HOST | #ifdef USE_HOST | ||||||
|   return true;  // Assume its connected |   return true;  // Assume its connected | ||||||
| #endif | #endif | ||||||
| @@ -46,6 +55,7 @@ network::IPAddresses get_ip_addresses() { | |||||||
|   if (wifi::global_wifi_component != nullptr) |   if (wifi::global_wifi_component != nullptr) | ||||||
|     return wifi::global_wifi_component->get_ip_addresses(); |     return wifi::global_wifi_component->get_ip_addresses(); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|   return {}; |   return {}; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -57,6 +67,10 @@ std::string get_use_address() { | |||||||
| #ifdef USE_WIFI | #ifdef USE_WIFI | ||||||
|   if (wifi::global_wifi_component != nullptr) |   if (wifi::global_wifi_component != nullptr) | ||||||
|     return wifi::global_wifi_component->get_use_address(); |     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 | #endif | ||||||
|   return ""; |   return ""; | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user