diff --git a/esp_modem/Kconfig b/esp_modem/Kconfig deleted file mode 100644 index e441e4cf5..000000000 --- a/esp_modem/Kconfig +++ /dev/null @@ -1,18 +0,0 @@ -menu "ESP-MODEM" - - config MODEM_LEGACY_API - bool "Enable Legacy API" - default y - help - Set to true if the original pppos_client API from the example's internal - component should be used..... - - config MODEM_PPP_APN - string "Local netif hostname" - default 'internet' - depends on MODEM_LEGACY_API - help - APN (Access Point Name), a logical name of a network - the modem connects to in the PPP mode - -endmenu diff --git a/esp_modem/README.md b/esp_modem/README.md index 6d46e0bbc..b0b37b3c4 100644 --- a/esp_modem/README.md +++ b/esp_modem/README.md @@ -10,6 +10,8 @@ Get started with one of the examples: * `examples/modem_console` -- C++ example implementing simple terminal console with DCE commands * `examples/ap_to_pppos` -- this example focuses on the network connectivity of the esp-modem and provides a WiFi AP that forwards packets (and uses NAT) to and from the PPPoS connection. +* `examples/simple_cmux_client` -- this example sets up the CMUX mode to talk to the modem using AT commands + with simultaneously maintaining active network connection. ## Documentation diff --git a/esp_modem/component.mk b/esp_modem/component.mk deleted file mode 100644 index d3afe7768..000000000 --- a/esp_modem/component.mk +++ /dev/null @@ -1,3 +0,0 @@ -COMPONENT_ADD_INCLUDEDIRS := include -COMPONENT_PRIV_INCLUDEDIRS := private_include -COMPONENT_SRCDIRS := src diff --git a/esp_modem/docs/advanced_api.rst b/esp_modem/docs/advanced_api.rst index 1967fc0cc..fd7e7af39 100644 --- a/esp_modem/docs/advanced_api.rst +++ b/esp_modem/docs/advanced_api.rst @@ -31,11 +31,14 @@ the specific module, using :cpp:func:`esp_modem::dce_factory::Factory::build`. Please refer to the implementation of the existing modules. +Please note that the ``modem_console`` example defines a trivial custom modem DCE which overrides one command, +for demonstration purposes only. + Create new communication interface ---------------------------------- -In order to connect to a device using an unsuppoeted interface (e.g. SPI or I2C), it is necessary to implement +In order to connect to a device using an unsupported interface (e.g. SPI or I2C), it is necessary to implement a custom DTE object and supply it into :ref:`the DCE factory`. The DCE is typically created in two steps: - Define and create the corresponding terminal, which can communicate on the custom interface. This terminal should support basic IO methods defined in :cpp:class:`esp_modem::Terminal` and derive from it. diff --git a/esp_modem/examples/ap_to_pppos/Makefile b/esp_modem/examples/ap_to_pppos/Makefile deleted file mode 100644 index 06822ceac..000000000 --- a/esp_modem/examples/ap_to_pppos/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# -# This is a project Makefile. It is assumed the directory this Makefile resides in is a -# project subdirectory. -# - -PROJECT_NAME := ap-to-pppos - -include $(IDF_PATH)/make/project.mk - diff --git a/esp_modem/examples/ap_to_pppos/README.md b/esp_modem/examples/ap_to_pppos/README.md index 04f8b2dcd..8a22a730d 100644 --- a/esp_modem/examples/ap_to_pppos/README.md +++ b/esp_modem/examples/ap_to_pppos/README.md @@ -1,11 +1,17 @@ -# PPPoS simple client example - -(See the README.md file in the upper level 'examples' directory for more information about examples.) +# AP to PPPoS example ## Overview + This example focuses on the networking part, enables forwarding packets between network interfaces. It creates a WiFi soft AP, which uses NAT to forward packets to and from the PPP network interface. -## How to use this example +### Specific network DCE -See the README.md file in the upper level `pppos` directory for more information about the PPPoS examples. +Note, that this example could use of a minimal Network DCE, defined in the `network_dce.cpp` file. +Please set `EXAMPLE_USE_MINIMAL_DCE` to `y` in order to demonstrate this functionality. + +By default, this example simply connects to the PPP server using a supported device with C-API modem interface. + +This example however, doesn't rely on sending specific AT commands, just the bare minimum to setup the cellular network. +Thus, if the `EXAMPLE_USE_MINIMAL_DCE` option is enabled, we directly inherit from the `ModuleIf` and implement only the basic commands. +Also, to demonstrate the dce_factory functionality, a new `NetDCE_Factory` is implemented for creating the network module and the DCE. diff --git a/esp_modem/examples/ap_to_pppos/main/CMakeLists.txt b/esp_modem/examples/ap_to_pppos/main/CMakeLists.txt index 7a59f37cf..0aa789788 100644 --- a/esp_modem/examples/ap_to_pppos/main/CMakeLists.txt +++ b/esp_modem/examples/ap_to_pppos/main/CMakeLists.txt @@ -1,5 +1,9 @@ -idf_component_register(SRCS "ap2pppos_example_main.c" - "NetworkDCE.cpp" - INCLUDE_DIRS ".") +if(CONFIG_EXAMPLE_USE_MINIMAL_DCE) + set(NETWORK_DCE "network_dce.cpp") +else() + set(NETWORK_DCE "network_dce.c") +endif() -#target_compile_features(${COMPONENT_LIB} PRIVATE cxx_std_17) \ No newline at end of file +idf_component_register(SRCS "ap_to_pppos.c" + ${NETWORK_DCE} + INCLUDE_DIRS ".") diff --git a/esp_modem/examples/ap_to_pppos/main/Kconfig.projbuild b/esp_modem/examples/ap_to_pppos/main/Kconfig.projbuild index 19e9f86e0..c757f8778 100644 --- a/esp_modem/examples/ap_to_pppos/main/Kconfig.projbuild +++ b/esp_modem/examples/ap_to_pppos/main/Kconfig.projbuild @@ -11,6 +11,7 @@ menu "Example Configuration" default "mypassword" help WiFi password (WPA or WPA2) for the example to use. + config ESP_WIFI_CHANNEL int "WiFi Channel" range 1 13 @@ -23,4 +24,24 @@ menu "Example Configuration" default 4 help Max number of the STA connects to AP. + + config EXAMPLE_MODEM_PPP_APN + string "Set MODEM APN" + default "internet" + help + Set APN (Access Point Name), a logical name to choose data network + + config EXAMPLE_SIM_PIN + string "Set SIM PIN" + default "1234" + help + Pin to unlock the SIM + + config EXAMPLE_USE_MINIMAL_DCE + bool "Use minimal network DCE" + default n + help + Define a specific network only DCE class and + create it using DCE factory + endmenu diff --git a/esp_modem/examples/ap_to_pppos/main/ap2pppos_example_main.c b/esp_modem/examples/ap_to_pppos/main/ap_to_pppos.c similarity index 86% rename from esp_modem/examples/ap_to_pppos/main/ap2pppos_example_main.c rename to esp_modem/examples/ap_to_pppos/main/ap_to_pppos.c index 848abcc38..31b326156 100644 --- a/esp_modem/examples/ap_to_pppos/main/ap2pppos_example_main.c +++ b/esp_modem/examples/ap_to_pppos/main/ap_to_pppos.c @@ -15,13 +15,16 @@ #include "lwip/lwip_napt.h" #include "freertos/FreeRTOS.h" #include "freertos/event_groups.h" +#include "network_dce.h" #define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID #define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD #define EXAMPLE_ESP_WIFI_CHANNEL CONFIG_ESP_WIFI_CHANNEL #define EXAMPLE_MAX_STA_CONN CONFIG_ESP_MAX_STA_CONN -static const char *TAG = "ap-2-pppos"; + +static const char *TAG = "ap_to_pppos"; + static EventGroupHandle_t event_group = NULL; static const int CONNECT_BIT = BIT0; static const int DISCONNECT_BIT = BIT1; @@ -62,7 +65,6 @@ static void on_ip_event(void *arg, esp_event_base_t event_base, } } - static esp_err_t set_dhcps_dns(esp_netif_t *netif, uint32_t addr) { esp_netif_dns_info_t dns; @@ -88,6 +90,7 @@ static void wifi_event_handler(void* arg, esp_event_base_t event_base, } } + void wifi_init_softap(void) { @@ -122,13 +125,9 @@ void wifi_init_softap(void) EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS, EXAMPLE_ESP_WIFI_CHANNEL); } -esp_err_t modem_init_network(esp_netif_t *netif); -void modem_start_network(); -void modem_stop_network(); - void app_main(void) { - //Initialize NVS + // Initialize NVS esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); @@ -136,29 +135,33 @@ void app_main(void) } ESP_ERROR_CHECK(ret); + // Initialize esp_netif and default event loop ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); event_group = xEventGroupCreate(); - // init the DTE + // Initialize lwip network interface in PPP mode esp_netif_config_t ppp_netif_config = ESP_NETIF_DEFAULT_PPP(); esp_netif_t *ppp_netif = esp_netif_new(&ppp_netif_config); assert(ppp_netif); + // Initialize the PPP network and register for IP event ESP_ERROR_CHECK(modem_init_network(ppp_netif)); ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, on_ip_event, NULL)); - /* Init and start the modem network */ + // Start the PPP network and wait for connection modem_start_network(); - /* Wait for the first connection */ EventBits_t bits; do { bits = xEventGroupWaitBits(event_group, (CONNECT_BIT | DISCONNECT_BIT), pdTRUE, pdFALSE, portMAX_DELAY); - if (bits&DISCONNECT_BIT) { + if (bits & DISCONNECT_BIT) { + ESP_LOGW(TAG, "Modem got disconnected from the PPP server: retrying..."); + modem_stop_network(); + modem_start_network(); } - } while ((bits&CONNECT_BIT) == 0); + } while ((bits & CONNECT_BIT) == 0); - /* Init the AP with NAT enabled */ + // Initialize the AP and setup the NAT esp_netif_t *ap_netif = esp_netif_create_default_wifi_ap(); assert(ap_netif); esp_netif_dns_info_t dns; @@ -168,11 +171,13 @@ void app_main(void) wifi_init_softap(); ip_napt_enable(_g_esp_netif_soft_ap_ip.ip.addr, 1); - /* Provide recovery if disconnection of some kind registered */ - while (DISCONNECT_BIT&xEventGroupWaitBits(event_group, DISCONNECT_BIT, pdTRUE, pdFALSE, portMAX_DELAY)) { - // restart the modem PPP mode - modem_stop_network(); - modem_start_network(); + // Provide a recovery if disconnection of some kind registered + while (true) { + bits = xEventGroupWaitBits(event_group, DISCONNECT_BIT, pdTRUE, pdFALSE, portMAX_DELAY); + if (bits & DISCONNECT_BIT) { + ESP_LOGW(TAG, "Modem got disconnected from the PPP server: restarting the network..."); + modem_stop_network(); + modem_start_network(); + } } - } diff --git a/esp_modem/examples/ap_to_pppos/main/component.mk b/esp_modem/examples/ap_to_pppos/main/component.mk deleted file mode 100644 index 61f8990c3..000000000 --- a/esp_modem/examples/ap_to_pppos/main/component.mk +++ /dev/null @@ -1,8 +0,0 @@ -# -# Main component makefile. -# -# This Makefile can be left empty. By default, it will take the sources in the -# src/ directory, compile them and link them into lib(subdirectory_name).a -# in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# diff --git a/esp_modem/examples/ap_to_pppos/main/idf_component.yml b/esp_modem/examples/ap_to_pppos/main/idf_component.yml deleted file mode 100644 index 12c900f0b..000000000 --- a/esp_modem/examples/ap_to_pppos/main/idf_component.yml +++ /dev/null @@ -1,7 +0,0 @@ -version: "0.0.2" -targets: - - esp32 -description: pppos_client to AP -dependencies: - espressif/esp_modem: - version: "~0.0.2" diff --git a/esp_modem/examples/ap_to_pppos/main/modem_board.c b/esp_modem/examples/ap_to_pppos/main/modem_board.c deleted file mode 100644 index d52987086..000000000 --- a/esp_modem/examples/ap_to_pppos/main/modem_board.c +++ /dev/null @@ -1,162 +0,0 @@ -/* softAP to PPPoS Example (modem_board) - - This example code is in the Public Domain (or CC0 licensed, at your option.) - - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. -*/ -#include -#include -#include "esp_log.h" -#include "esp_modem_recov_helper.h" -#include "esp_modem_dce.h" -#include "esp_modem_dce_common_commands.h" - -#define ESP_MODEM_EXAMPLE_CHECK(a, str, goto_tag, ...) \ - do \ - { \ - if (!(a)) \ - { \ - ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ - goto goto_tag; \ - } \ - } while (0) - - -static const char *TAG = "sim7600_board"; - -typedef struct { - esp_modem_dce_t parent; - esp_modem_recov_gpio_t *power_pin; - esp_modem_recov_gpio_t *reset_pin; - esp_err_t (*reset)(esp_modem_dce_t *dce); - esp_err_t (*power_down)(esp_modem_dce_t *dce); - esp_modem_recov_resend_t *re_sync; - esp_modem_recov_resend_t *re_store_profile; -} sim7600_board_t; - -esp_err_t sim7600_board_handle_powerup(esp_modem_dce_t *dce, const char *line) -{ - if (strstr(line, "PB DONE")) { - ESP_LOGI(TAG, "Board ready after hard reset/power-cycle"); - } - return ESP_OK; -} - -esp_err_t sim7600_board_deinit(esp_modem_dce_t *dce) -{ - sim7600_board_t *board = __containerof(dce, sim7600_board_t, parent); - board->power_pin->destroy(board->power_pin); - board->power_pin->destroy(board->reset_pin); - esp_err_t err = esp_modem_command_list_deinit(&board->parent); - if (err == ESP_OK) { - free(dce); - } - return err; -} - -esp_err_t sim7600_board_reset(esp_modem_dce_t *dce) -{ - sim7600_board_t *board = __containerof(dce, sim7600_board_t, parent); - ESP_LOGI(TAG, "sim7600_board_reset!"); - dce->handle_line = sim7600_board_handle_powerup; - board->power_pin->pulse(board->reset_pin); - return ESP_OK; -} - -esp_err_t sim7600_board_power_up(esp_modem_dce_t *dce) -{ - sim7600_board_t *board = __containerof(dce, sim7600_board_t, parent); - ESP_LOGI(TAG, "sim7600_board_power_up!"); - dce->handle_line = sim7600_board_handle_powerup; - board->power_pin->pulse(board->power_pin); - return ESP_OK; -} - - -esp_err_t sim7600_board_power_down(esp_modem_dce_t *dce) -{ - sim7600_board_t *board = __containerof(dce, sim7600_board_t, parent); - ESP_LOGI(TAG, "sim7600_board_power_down!"); - /* power down sequence (typical values for SIM7600 Toff=min2.5s, Toff-status=26s) */ - dce->handle_line = sim7600_board_handle_powerup; - board->power_pin->pulse_special(board->power_pin, 3000, 26000); - return ESP_OK; -} - -static esp_err_t my_recov(esp_modem_recov_resend_t *retry_cmd, esp_err_t err, int timeouts, int errors) -{ - esp_modem_dce_t *dce = retry_cmd->dce; - ESP_LOGI(TAG, "Current timeouts: %d and errors: %d", timeouts, errors); - if (err == ESP_ERR_TIMEOUT) { - if (timeouts < 2) { - // first timeout, try to exit data mode and sync again - dce->set_command_mode(dce, NULL, NULL); - esp_modem_dce_sync(dce, NULL, NULL); - } else if (timeouts < 3) { - // try to reset with GPIO if resend didn't help - sim7600_board_t *board = __containerof(dce, sim7600_board_t, parent); - board->reset(dce); - } else { - // otherwise power-cycle the board - sim7600_board_t *board = __containerof(dce, sim7600_board_t, parent); - board->power_down(dce); - esp_modem_dce_sync(dce, NULL, NULL); - } - } else { - // check if a PIN needs to be supplied in case of a failure - bool ready = false; - esp_modem_dce_read_pin(dce, NULL, &ready); - if (!ready) { - esp_modem_dce_set_pin(dce, "1234", NULL); - } - vTaskDelay(1000 / portTICK_RATE_MS); - esp_modem_dce_read_pin(dce, NULL, &ready); - if (!ready) { - return ESP_FAIL; - } - } - return ESP_OK; -} - -static DEFINE_RETRY_CMD(re_sync_fn, re_sync, sim7600_board_t) - -static DEFINE_RETRY_CMD(re_store_profile_fn, re_store_profile, sim7600_board_t) - -esp_err_t sim7600_board_start_up(esp_modem_dce_t *dce) -{ -// sim7600_board_t *board = __containerof(dce, sim7600_board_t, parent); - ESP_MODEM_EXAMPLE_CHECK(re_sync_fn(dce, NULL, NULL) == ESP_OK, "sending sync failed", err); - ESP_MODEM_EXAMPLE_CHECK(dce->set_echo(dce, (void*)false, NULL) == ESP_OK, "set_echo failed", err); - ESP_MODEM_EXAMPLE_CHECK(dce->set_flow_ctrl(dce, (void*)ESP_MODEM_FLOW_CONTROL_NONE, NULL) == ESP_OK, "set_flow_ctrl failed", err); - ESP_MODEM_EXAMPLE_CHECK(dce->store_profile(dce, NULL, NULL) == ESP_OK, "store_profile failed", err); - return ESP_OK; -err: - return ESP_FAIL; - -} - -esp_modem_dce_t *sim7600_board_create(esp_modem_dce_config_t *config) -{ - sim7600_board_t *board = calloc(1, sizeof(sim7600_board_t)); - ESP_MODEM_EXAMPLE_CHECK(board, "failed to allocate board-sim7600 object", err); - ESP_MODEM_EXAMPLE_CHECK(esp_modem_dce_init(&board->parent, config) == ESP_OK, "Failed to init sim7600", err); - /* power on sequence (typical values for SIM7600 Ton=500ms, Ton-status=16s) */ - board->power_pin = esp_modem_recov_gpio_new( /*gpio_num*/ 12, /*inactive_level*/ 1, /*active_width*/ - 500, /*inactive_width*/ 16000); - /* reset sequence (typical values for SIM7600 Treset=200ms, wait 10s after reset */ - board->reset_pin = esp_modem_recov_gpio_new( /*gpio_num*/ 13, /*inactive_level*/ 1, /*active_width*/ - 200, /*inactive_width*/ 10000); - board->parent.deinit = sim7600_board_deinit; - board->reset = sim7600_board_reset; - board->power_down = sim7600_board_power_down; - board->re_sync = esp_modem_recov_resend_new(&board->parent, board->parent.sync, my_recov, 5, 1); - board->parent.start_up = sim7600_board_start_up; - board->re_store_profile = esp_modem_recov_resend_new(&board->parent, board->parent.store_profile, my_recov, 2, 3); - board->parent.store_profile = re_store_profile_fn; - - return &board->parent; -err: - return NULL; -} \ No newline at end of file diff --git a/esp_modem/examples/ap_to_pppos/main/network_dce.c b/esp_modem/examples/ap_to_pppos/main/network_dce.c new file mode 100644 index 000000000..499593aa2 --- /dev/null +++ b/esp_modem/examples/ap_to_pppos/main/network_dce.c @@ -0,0 +1,56 @@ +/* softAP to PPPoS Example (network_dce) + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include "esp_netif.h" +#include "esp_modem_api.h" + + +static esp_modem_dce_t *dce = NULL; + + +esp_err_t modem_init_network(esp_netif_t *netif) +{ + // setup the DCE + esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG(); + esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_EXAMPLE_MODEM_PPP_APN); + dce = esp_modem_new(&dte_config, &dce_config, netif); + if (!dce) { + return ESP_FAIL; + } + + // configure the PIN + bool pin_ok = false; + if (esp_modem_read_pin(dce, &pin_ok) == ESP_OK && pin_ok == false) { + if (esp_modem_set_pin(dce, CONFIG_EXAMPLE_SIM_PIN) == ESP_OK) { + vTaskDelay(pdMS_TO_TICKS(1000)); + } else { + abort(); + } + } + return ESP_OK; +} + +void modem_deinit_network(void) +{ + if (dce) { + esp_modem_destroy(dce); + dce = NULL; + } +} + +void modem_start_network() +{ + esp_modem_set_mode(dce, ESP_MODEM_MODE_DATA); +} + +void modem_stop_network() +{ + esp_modem_set_mode(dce, ESP_MODEM_MODE_COMMAND); +} diff --git a/esp_modem/examples/ap_to_pppos/main/NetworkDCE.cpp b/esp_modem/examples/ap_to_pppos/main/network_dce.cpp similarity index 92% rename from esp_modem/examples/ap_to_pppos/main/NetworkDCE.cpp rename to esp_modem/examples/ap_to_pppos/main/network_dce.cpp index f3da04831..bdcd44a1b 100644 --- a/esp_modem/examples/ap_to_pppos/main/NetworkDCE.cpp +++ b/esp_modem/examples/ap_to_pppos/main/network_dce.cpp @@ -1,4 +1,4 @@ -/* softAP to PPPoS Example (modem_board) +/* softAP to PPPoS Example (network_dce) This example code is in the Public Domain (or CC0 licensed, at your option.) @@ -13,6 +13,7 @@ #include "cxx_include/esp_modem_dce_factory.hpp" #include #include +#include "network_dce.h" using namespace esp_modem; using namespace esp_modem::dce_factory; @@ -26,6 +27,7 @@ typedef DCE_T NetDCE; class PPPNetwork { public: esp_err_t init(esp_netif_t *netif, const std::string& apn, const std::string &pin_number); + void deinit(); NetDCE * get_dce(); private: NetDCE *dce; @@ -120,7 +122,6 @@ esp_err_t PPPNetwork::init(esp_netif_t *netif, const std::string& apn, const std { // configure esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG(); - dte_config.uart_config.event_task_stack_size = 4096; dte_config.uart_config.rx_buffer_size = 16384; dte_config.uart_config.tx_buffer_size = 2048; esp_modem_dce_config dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(apn.c_str()); @@ -140,14 +141,23 @@ esp_err_t PPPNetwork::init(esp_netif_t *netif, const std::string& apn, const std return ESP_OK; } +void PPPNetwork::deinit() +{ + free(dce); + dce = nullptr; +} + NetDCE *PPPNetwork::get_dce() { return dce; } +/** + * @brief Implement the C-API for the AP-2-PPP functionality + */ extern "C" esp_err_t modem_init_network(esp_netif_t *netif) { - return ppp_network.init(netif, "internet", "1234"); + return ppp_network.init(netif, CONFIG_EXAMPLE_MODEM_PPP_APN, CONFIG_EXAMPLE_SIM_PIN); } extern "C" void modem_start_network() @@ -159,3 +169,8 @@ extern "C" void modem_stop_network() { ppp_network.get_dce()->set_mode(esp_modem::modem_mode::COMMAND_MODE); } + +extern "C" void modem_deinit_network() +{ + ppp_network.deinit(); +} diff --git a/esp_modem/examples/ap_to_pppos/main/network_dce.h b/esp_modem/examples/ap_to_pppos/main/network_dce.h new file mode 100644 index 000000000..54cd6718e --- /dev/null +++ b/esp_modem/examples/ap_to_pppos/main/network_dce.h @@ -0,0 +1,47 @@ +/* softAP to PPPoS Example (network_dce) + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#ifndef _NETWORK_DCE_H_ +#define _NETWORK_DCE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @brief Initialize a singleton covering the PPP network provided by the connected modem device + * + * @param netif Already created network interface in PPP mode + * + * @return ESP_OK on success + */ +esp_err_t modem_init_network(esp_netif_t *netif); + +/** + * @brief Destroys the single network DCE + */ +void modem_deinit_network(); + +/** + * @brief Starts the PPP network + */ +void modem_start_network(); + +/** + * @brief Stops the PPP network + */ +void modem_stop_network(); + + +#ifdef __cplusplus +} +#endif + +#endif //_NETWORK_DCE_H_ diff --git a/esp_modem/examples/ap_to_pppos/sdkconfig.defaults b/esp_modem/examples/ap_to_pppos/sdkconfig.defaults index 03925c80d..cf6d03c04 100644 --- a/esp_modem/examples/ap_to_pppos/sdkconfig.defaults +++ b/esp_modem/examples/ap_to_pppos/sdkconfig.defaults @@ -1,12 +1,8 @@ -# Override some defaults to enable PPP CONFIG_LWIP_PPP_SUPPORT=y CONFIG_LWIP_PPP_PAP_SUPPORT=y CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=4096 -# Do not enable IPV6 in dte<->dce link local CONFIG_LWIP_PPP_ENABLE_IPV6=n -# Disable legacy API -CONFIG_MODEM_LEGACY_API=n +CONFIG_UART_ISR_IN_IRAM=y # Enable NAPT CONFIG_LWIP_IP_FORWARD=y CONFIG_LWIP_IPV4_NAPT=y - diff --git a/esp_modem/examples/modem_console/README.md b/esp_modem/examples/modem_console/README.md index 1219bce0e..0dbf5c4cb 100644 --- a/esp_modem/examples/modem_console/README.md +++ b/esp_modem/examples/modem_console/README.md @@ -3,8 +3,15 @@ (See the README.md file in the upper level 'examples' directory for more information about examples.) ## Overview + This example is mainly targets experimenting with a modem device, sending custom commands and switching to PPP mode using esp-console, command line API. +Please check the list of supported commands using `help` command. -## How to use this example +This example implements two very simple network commands to demonstrate and test basic network functionality. +* `httpget`: Connect and get http content +* `ping`: Send ICMP pings -See the README.md file in the upper level `pppos` directory for more information about the PPPoS examples. +To demonstrate creating custom modem devices, this example creates a DCE object using a locally defined create method, +that sets up the DCE based on a custom module implemented in the `my_module_dce.hpp` file. The module class only overrides +`get_module_name()` method supplying a user defined name, but keeps all other commands the same as defined in the `GenericModule` +class. \ No newline at end of file diff --git a/esp_modem/examples/modem_console/main/modem_console_main.cpp b/esp_modem/examples/modem_console/main/modem_console_main.cpp index 0306835c9..e36a5cda9 100644 --- a/esp_modem/examples/modem_console/main/modem_console_main.cpp +++ b/esp_modem/examples/modem_console/main/modem_console_main.cpp @@ -20,15 +20,7 @@ #include "cxx_include/esp_modem_api.hpp" #include "esp_log.h" #include "console_helper.hpp" - -extern "C" void modem_console_register_http(void); -extern "C" void modem_console_register_ping(void); - -static const char *TAG = "modem_console"; - -static esp_console_repl_t *s_repl = nullptr; - -using namespace esp_modem; +#include "my_module_dce.hpp" #define CHECK_ERR(cmd, success_action) do { \ auto err = cmd; \ @@ -40,63 +32,67 @@ using namespace esp_modem; return 1; \ } } while (0) +/** + * Please update the default APN name here (this could be updated runtime) + */ +#define DEFAULT_APN "my_apn" + +extern "C" void modem_console_register_http(void); +extern "C" void modem_console_register_ping(void); + +static const char *TAG = "modem_console"; +static esp_console_repl_t *s_repl = nullptr; + +using namespace esp_modem; + + extern "C" void app_main(void) { ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); - // init the DTE + // init the netif, DTE and DCE respectively esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG(); - esp_modem_dte_config_t dte_config2 = { - .dte_buffer_size = 512, - .vfs_config = {.port_num = UART_NUM_1, - .dev_name = "/dev/uart/1", - .rx_buffer_size = 1024, - .tx_buffer_size = 1024, - .baud_rate = 115200, - .tx_io_num = 25, - .rx_io_num = 26, - .task_stack_size = 4096, - .task_prio = 5} - }; - esp_netif_config_t ppp_netif_config = ESP_NETIF_DEFAULT_PPP(); - esp_netif_t *esp_netif = esp_netif_new(&ppp_netif_config); assert(esp_netif); - auto uart_dte = create_vfs_dte(&dte_config2); -// auto uart_dte = create_uart_dte(&dte_config); - - esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("internet"); - - auto dce = create_SIM7600_dce(&dce_config, uart_dte, esp_netif); + auto uart_dte = create_uart_dte(&dte_config); + esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(DEFAULT_APN); + auto dce = create_shiny_dce(&dce_config, uart_dte, esp_netif); assert(dce != nullptr); + // init console REPL environment esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); - // init console REPL environment ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &s_repl)); modem_console_register_http(); modem_console_register_ping(); const struct SetModeArgs { - SetModeArgs(): mode(STR1, nullptr, nullptr, "", "PPP or CMD") {} + SetModeArgs(): mode(STR1, nullptr, nullptr, "", "PPP, CMD or CMUX") {} CommandArgs mode; } set_mode_args; const ConsoleCommand SetModeParser("set_mode", "sets modem mode", &set_mode_args, sizeof(set_mode_args), [&](ConsoleCommand *c){ if (c->get_count_of(&SetModeArgs::mode)) { auto mode = c->get_string_of(&SetModeArgs::mode); + modem_mode dev_mode; if (mode == "CMD") { - ESP_LOGI(TAG, "Switching to command mode..."); - dce->exit_data(); + dev_mode = esp_modem::modem_mode::COMMAND_MODE; } else if (mode == "PPP") { - ESP_LOGI(TAG, "Switching to data mode..."); - dce->set_data(); + dev_mode = esp_modem::modem_mode::DATA_MODE; + } else if (mode == "CMUX") { + dev_mode = esp_modem::modem_mode::CMUX_MODE; } else { ESP_LOGE(TAG, "Unsupported mode: %s", mode.c_str()); return 1; } + ESP_LOGI(TAG, "Switching to %s name...", mode.c_str()); + if (!dce->set_mode(dev_mode)) { + ESP_LOGE(TAG, "Failed to set the desired mode"); + return 1; + } + ESP_LOGI(TAG, "OK"); } return 0; }); @@ -151,7 +147,7 @@ extern "C" void app_main(void) cmd(STR1, nullptr, nullptr, "", "AT command to send to the modem"), timeout(INT0, "t", "timeout", "", "command timeout"), pattern(STR0, "p", "pattern", "", "command response to wait for"), - no_cr(LIT0, "n", "no-cr", "not add trailing CR to the command") {} + no_cr(LIT0, "n", "no-cr", "do not add trailing CR to the command") {} CommandArgs cmd; CommandArgs timeout; CommandArgs pattern; @@ -193,6 +189,20 @@ extern "C" void app_main(void) ESP_LOGI(TAG, "Resetting the module..."); CHECK_ERR(dce->reset(), ESP_LOGI(TAG, "OK")); }); + const struct SetApn { + SetApn(): apn(STR1, nullptr, nullptr, "", "APN (Access Point Name)") {} + CommandArgs apn; + } set_apn; + const ConsoleCommand SetApnParser("set_apn", "sets APN", &set_apn, sizeof(set_apn), [&](ConsoleCommand *c){ + if (c->get_count_of(&SetApn::apn)) { + auto apn = c->get_string_of(&SetApn::apn); + ESP_LOGI(TAG, "Setting the APN=%s...", apn.c_str()); + auto new_pdp = std::unique_ptr(new PdpContext(apn)); + dce->get_module()->configure_pdp_context(std::move(new_pdp)); + ESP_LOGI(TAG, "OK"); + } + return 0; + }); SignalGroup exit_signal; const ConsoleCommand ExitConsole("exit", "exit the console application", no_args, [&](ConsoleCommand *c){ diff --git a/esp_modem/examples/modem_console/main/my_module_dce.hpp b/esp_modem/examples/modem_console/main/my_module_dce.hpp new file mode 100644 index 000000000..e111b7112 --- /dev/null +++ b/esp_modem/examples/modem_console/main/my_module_dce.hpp @@ -0,0 +1,43 @@ +/* Modem console example: Custom DCE + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#ifndef __MY_MODULE_DCE_HPP__ +#define __MY_MODULE_DCE_HPP__ + + +#include "cxx_include/esp_modem_dce_factory.hpp" +#include "cxx_include/esp_modem_dce_module.hpp" + +/** + * @brief Definition of a custom modem which inherits from the GenericModule, uses all its methods + * and could override any of them. Here, for demonstration purposes only, we redefine just `get_module_name()` + */ +class MyShinyModem: public esp_modem::GenericModule { + using GenericModule::GenericModule; +public: + esp_modem::command_result get_module_name(std::string& name) override + { + name = "Custom Shiny Module"; + return esp_modem::command_result::OK; + } +}; + +/** + * @brief Helper create method which employs the DCE factory for creating DCE objects templated by a custom module + * @return unique pointer of the resultant DCE + */ +std::unique_ptr create_shiny_dce(const esp_modem::dce_config *config, + std::shared_ptr dte, + esp_netif_t *netif) +{ + return esp_modem::dce_factory::Factory::build_unique(config, std::move(dte), netif); +} + + +#endif //__MY_MODULE_DCE_HPP__ \ No newline at end of file diff --git a/esp_modem/examples/pppos_client/Makefile b/esp_modem/examples/pppos_client/Makefile deleted file mode 100644 index 25571d087..000000000 --- a/esp_modem/examples/pppos_client/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# -# This is a project Makefile. It is assumed the directory this Makefile resides in is a -# project subdirectory. -# - -PROJECT_NAME := pppos_client - -include $(IDF_PATH)/make/project.mk - diff --git a/esp_modem/examples/pppos_client/main/Kconfig.projbuild b/esp_modem/examples/pppos_client/main/Kconfig.projbuild index 68b4e2048..ee63e1383 100644 --- a/esp_modem/examples/pppos_client/main/Kconfig.projbuild +++ b/esp_modem/examples/pppos_client/main/Kconfig.projbuild @@ -26,14 +26,6 @@ menu "Example Configuration" help Set APN (Access Point Name), a logical name to choose data network - config EXAMPLE_MODEM_LEGACY_API - bool "Use Modem legacy API" - default y - select MODEM_LEGACY_API - help - Set this to true to use backward compatible API to the original modem - component in example/protocol folder in IDFv4.2 and below - config EXAMPLE_MODEM_PPP_AUTH_USERNAME string "Set username for authentication" default "espressif" @@ -68,6 +60,19 @@ menu "Example Configuration" Enter the peer phone number that you want to send message to. endif + config EXAMPLE_NEED_SIM_PIN + bool "SIM PIN needed" + default n + help + Enable to set SIM PIN before starting the example + + config EXAMPLE_SIM_PIN + string "Set SIM PIN" + default "1234" + depends on EXAMPLE_NEED_SIM_PIN + help + Pin to unlock the SIM + menu "UART Configuration" config EXAMPLE_MODEM_UART_TX_PIN int "TXD Pin Number" diff --git a/esp_modem/examples/pppos_client/main/component.mk b/esp_modem/examples/pppos_client/main/component.mk deleted file mode 100644 index a98f634ea..000000000 --- a/esp_modem/examples/pppos_client/main/component.mk +++ /dev/null @@ -1,4 +0,0 @@ -# -# "main" pseudo-component makefile. -# -# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/esp_modem/examples/pppos_client/main/pppos_client_main.c b/esp_modem/examples/pppos_client/main/pppos_client_main.c index 8bef9701e..5244170a8 100644 --- a/esp_modem/examples/pppos_client/main/pppos_client_main.c +++ b/esp_modem/examples/pppos_client/main/pppos_client_main.c @@ -20,10 +20,8 @@ static const char *TAG = "pppos_example"; static EventGroupHandle_t event_group = NULL; static const int CONNECT_BIT = BIT0; -//static const int STOP_BIT = BIT1; static const int GOT_DATA_BIT = BIT2; - static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event) { esp_mqtt_client_handle_t client = event->client; @@ -131,8 +129,8 @@ void app_main(void) 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.uart_config.event_task_stack_size = CONFIG_EXAMPLE_MODEM_UART_EVENT_TASK_STACK_SIZE; - dte_config.uart_config.event_task_priority = CONFIG_EXAMPLE_MODEM_UART_EVENT_TASK_PRIORITY; + 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; /* Configure the DCE */ @@ -147,6 +145,7 @@ void app_main(void) assert(esp_netif); esp_modem_dce_t *dce = esp_modem_new(&dte_config, &dce_config, esp_netif); +#if CONFIG_EXAMPLE_NEED_SIM_PIN == 1 // check if PIN needed bool pin_ok = false; if (esp_modem_read_pin(dce, &pin_ok) == ESP_OK && pin_ok == false) { @@ -156,6 +155,8 @@ void app_main(void) abort(); } } +#endif + int rssi, ber; esp_err_t err = esp_modem_get_signal_quality(dce, &rssi, &ber); if (err != ESP_OK) { @@ -164,7 +165,7 @@ void app_main(void) } ESP_LOGI(TAG, "Signal quality: rssi=%d, ber=%d", rssi, ber); - #if CONFIG_EXAMPLE_SEND_MSG +#if CONFIG_EXAMPLE_SEND_MSG if (esp_modem_sms_txt_mode(dce, true) != ESP_OK || esp_modem_sms_character_set(dce) != ESP_OK) { ESP_LOGE(TAG, "Setting text mode or GSM character set failed"); return; @@ -175,7 +176,6 @@ void app_main(void) ESP_LOGE(TAG, "esp_modem_send_sms() failed with %d", err); return; } - #endif err = esp_modem_set_mode(dce, ESP_MODEM_MODE_DATA); diff --git a/esp_modem/examples/pppos_client/sdkconfig.defaults b/esp_modem/examples/pppos_client/sdkconfig.defaults index 19f2ce6cd..122dec9ff 100644 --- a/esp_modem/examples/pppos_client/sdkconfig.defaults +++ b/esp_modem/examples/pppos_client/sdkconfig.defaults @@ -3,5 +3,4 @@ CONFIG_LWIP_PPP_SUPPORT=y CONFIG_LWIP_PPP_NOTIFY_PHASE_SUPPORT=y CONFIG_LWIP_PPP_PAP_SUPPORT=y CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=4096 -# Do not enable IPV6 in dte<->dce link local CONFIG_LWIP_PPP_ENABLE_IPV6=n diff --git a/esp_modem/idf_component.yml b/esp_modem/idf_component.yml index 8e0ac4a4f..d42ecc2e8 100644 --- a/esp_modem/idf_component.yml +++ b/esp_modem/idf_component.yml @@ -1,4 +1,4 @@ -version: "0.1.4" +version: "0.1.5" targets: - esp32 description: esp modem diff --git a/esp_modem/include/cxx_include/esp_modem_api.hpp b/esp_modem/include/cxx_include/esp_modem_api.hpp index 5ff6e496a..87d25a668 100644 --- a/esp_modem/include/cxx_include/esp_modem_api.hpp +++ b/esp_modem/include/cxx_include/esp_modem_api.hpp @@ -49,7 +49,13 @@ using dte_config = ::esp_modem_dte_config; */ std::shared_ptr create_uart_dte(const dte_config *config); - +/** + * @brief Create VFS DTE + * @param config DTE configuration + * @return shared ptr to DTE on success + * nullptr on failure (either due to insufficient memory or wrong dte configuration) + * if exceptions are disabled the API abort()'s on error + */ std::shared_ptr create_vfs_dte(const dte_config *config); diff --git a/esp_modem/include/cxx_include/esp_modem_dce.hpp b/esp_modem/include/cxx_include/esp_modem_dce.hpp index af297b3ce..5666b8fa2 100644 --- a/esp_modem/include/cxx_include/esp_modem_dce.hpp +++ b/esp_modem/include/cxx_include/esp_modem_dce.hpp @@ -53,8 +53,8 @@ template class DCE_T { static_assert(std::is_base_of::value, "DCE must be instantiated with Module class only"); public: - explicit DCE_T(const std::shared_ptr& dte, std::shared_ptr device, esp_netif_t * netif): - dte(dte), module(std::move(device)), netif(dte, netif) + explicit DCE_T(const std::shared_ptr& dte, std::shared_ptr dev, esp_netif_t * netif): + dte(dte), device(std::move(dev)), netif(dte, netif) { } ~DCE_T() = default; @@ -68,21 +68,18 @@ public: void set_cmux() { set_mode(modem_mode::CMUX_MODE); } - ModuleIf* get_module() { return module.get(); } - + SpecificModule* get_module() { return device.get(); } command_result command(const std::string& command, got_line_cb got_line, uint32_t time_ms) { return dte->command(command, std::move(got_line), time_ms); } - bool set_mode(modem_mode m) { return mode.set(dte.get(), module.get(), netif, m); } + bool set_mode(modem_mode m) { return mode.set(dte.get(), device.get(), netif, m); } protected: - - std::shared_ptr dte; - std::shared_ptr module; + std::shared_ptr device; Netif netif; DCE_Mode mode; }; @@ -99,7 +96,7 @@ public: template \ return_type name(Agrs&&... args) \ { \ - return module->name(std::forward(args)...); \ + return device->name(std::forward(args)...); \ } DECLARE_ALL_COMMAND_APIS(forwards name(...) { device->name(...); } ) diff --git a/esp_modem/include/cxx_include/esp_modem_dce_factory.hpp b/esp_modem/include/cxx_include/esp_modem_dce_factory.hpp index 5be6d1fb8..9c8ad84ba 100644 --- a/esp_modem/include/cxx_include/esp_modem_dce_factory.hpp +++ b/esp_modem/include/cxx_include/esp_modem_dce_factory.hpp @@ -64,19 +64,19 @@ template class Builder { static_assert(std::is_base_of::value, "Builder must be used only for Module classes"); public: - Builder(std::shared_ptr x, esp_netif_t* esp_netif): dte(std::move(x)), module(nullptr), netif(esp_netif) + Builder(std::shared_ptr x, esp_netif_t* esp_netif): dte(std::move(x)), device(nullptr), netif(esp_netif) { throw_if_false(netif != nullptr, "Null netif"); } - Builder(std::shared_ptr dte, esp_netif_t* esp_netif, std::shared_ptr dev): dte(std::move(dte)), module(std::move(dev)), netif(esp_netif) + Builder(std::shared_ptr dte, esp_netif_t* esp_netif, std::shared_ptr dev): dte(std::move(dte)), device(std::move(dev)), netif(esp_netif) { throw_if_false(netif != nullptr, "Null netif"); } ~Builder() { - throw_if_false(module == nullptr, "module was captured or created but never used"); + throw_if_false(device == nullptr, "module was captured or created but never used"); } template @@ -90,17 +90,17 @@ public: { if (dte == nullptr) return nullptr; - if (module == nullptr) { - module = create_module(config); - if (module == nullptr) + if (device == nullptr) { + device = create_module(config); + if (device == nullptr) return nullptr; } - return FactoryHelper::make(std::move(dte), std::move(module), netif); + return FactoryHelper::make(std::move(dte), std::move(device), netif); } private: std::shared_ptr dte; - std::shared_ptr module; + std::shared_ptr device; esp_netif_t *netif; }; diff --git a/esp_modem/include/cxx_include/esp_modem_dce_module.hpp b/esp_modem/include/cxx_include/esp_modem_dce_module.hpp index d11e18da6..62e826dc3 100644 --- a/esp_modem/include/cxx_include/esp_modem_dce_module.hpp +++ b/esp_modem/include/cxx_include/esp_modem_dce_module.hpp @@ -43,10 +43,20 @@ struct PdpContext; */ class GenericModule: public ModuleIf { public: + /** + * @brief We can construct a generic device with an existent DTE and it's configuration + * The configuration could be either the dce-config struct or just a pdp context + */ explicit GenericModule(std::shared_ptr dte, std::unique_ptr pdp): dte(std::move(dte)), pdp(std::move(pdp)) {} explicit GenericModule(std::shared_ptr dte, const esp_modem_dce_config* config); + /** + * @brief This is a mandatory method for ModuleIf class, which sets up the device + * to be able to connect to the network. This typically consists of setting basic + * communication parameters and setting the PDP (defining logical access point + * to cellular network) + */ bool setup_data_mode() override { if (set_echo(false) != command_result::OK) @@ -56,6 +66,10 @@ public: return true; } + /** + * @brief This is a mandatory method of ModuleIf class, which defines + * basic commands for switching between DATA, COMMAND and CMUX modes + */ bool set_mode(modem_mode mode) override { if (mode == modem_mode::DATA_MODE) { @@ -70,7 +84,17 @@ public: return true; } + /** + * @brief Additional method providing runtime configuration of PDP context + */ + void configure_pdp_context(std::unique_ptr new_pdp) + { + pdp = std::move(new_pdp); + } + /** + * @brief Common DCE commands generated from the API AT list + */ #define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, num, ...) \ virtual return_type name(__VA_ARGS__); @@ -80,8 +104,8 @@ public: protected: - std::shared_ptr dte; - std::unique_ptr pdp; + std::shared_ptr dte; /*!< Generic device needs the DTE as a channel talk to the module using AT commands */ + std::unique_ptr pdp; /*!< It also needs a PDP data, const information used for setting up cellular network */ }; // Definitions of other supported modules with some specific commands overwritten diff --git a/esp_modem/include/esp_modem_api.h b/esp_modem/include/esp_modem_api.h index ba134abcf..b56362c9e 100644 --- a/esp_modem/include/esp_modem_api.h +++ b/esp_modem/include/esp_modem_api.h @@ -15,6 +15,7 @@ #ifndef _ESP_MODEM_API_H_ #define _ESP_MODEM_API_H_ +#include "esp_err.h" #include "generate/esp_modem_command_declare.inc" #include "esp_modem_c_api_types.h"