From 247f1681e823816cf1d6ace78a20bd6d77ef8aac Mon Sep 17 00:00:00 2001 From: David Cermak Date: Thu, 21 Nov 2024 08:11:15 +0100 Subject: [PATCH 1/5] feat(modem): Add support for pausing netif Closes https://github.com/espressif/esp-protocols/issues/699 --- components/esp_modem/Kconfig | 7 ++++++ .../modem_console/main/modem_console_main.cpp | 11 ++++++++++ .../include/cxx_include/esp_modem_dce.hpp | 22 +++++++++++++++++++ .../include/cxx_include/esp_modem_dte.hpp | 12 +++++----- .../include/cxx_include/esp_modem_netif.hpp | 12 +++++++++- components/esp_modem/src/esp_modem_netif.cpp | 14 ++++++++++++ components/esp_modem/src/esp_modem_uart.cpp | 11 ++++++++-- 7 files changed, 80 insertions(+), 9 deletions(-) diff --git a/components/esp_modem/Kconfig b/components/esp_modem/Kconfig index f4b47fb787..4e6fb88332 100644 --- a/components/esp_modem/Kconfig +++ b/components/esp_modem/Kconfig @@ -85,4 +85,11 @@ menu "esp-modem" mode more robust for some devices (e.g. Quectel), but might cause trouble for other devices (e.g. SIMCOM). + config ESP_MODEM_ADD_DEBUG_LOGS + bool "Add UART Tx/Rx logs" + default n + help + If enabled, the library dumps all transmitted and received data. + This option is only used for debugging. + endmenu diff --git a/components/esp_modem/examples/modem_console/main/modem_console_main.cpp b/components/esp_modem/examples/modem_console/main/modem_console_main.cpp index 42dd6a76e4..3c3184f5a9 100644 --- a/components/esp_modem/examples/modem_console/main/modem_console_main.cpp +++ b/components/esp_modem/examples/modem_console/main/modem_console_main.cpp @@ -385,6 +385,17 @@ extern "C" void app_main(void) return 0; }); #endif + const ConsoleCommand PauseNetwork("pause_net", "toggle network pause", no_args, [&](ConsoleCommand * c) { + static int cnt = 0; + if (++cnt % 2) { + ESP_LOGI(TAG, "Pausing netif"); + dce->pause_netif(true); + } else { + ESP_LOGI(TAG, "Unpausing netif"); + dce->pause_netif(false); + } + return 0; + }); const struct SetApn { SetApn(): apn(STR1, nullptr, nullptr, "", "APN (Access Point Name)") {} diff --git a/components/esp_modem/include/cxx_include/esp_modem_dce.hpp b/components/esp_modem/include/cxx_include/esp_modem_dce.hpp index 045783c2dd..25ca1e4113 100644 --- a/components/esp_modem/include/cxx_include/esp_modem_dce.hpp +++ b/components/esp_modem/include/cxx_include/esp_modem_dce.hpp @@ -103,6 +103,28 @@ class DCE_T { } #endif + /** + * @brief Pauses/Unpauses network temporarily + * @param do_pause true to pause, false to unpause + * @param force true to ignore command failures and continue + * @return command_result of the underlying commands + */ + command_result pause_netif(bool do_pause, bool force = false) + { + command_result result; + if (do_pause) { + netif.pause(); + dte->set_command_callbacks(); + result = device->set_command_mode(); + } else { + result = device->resume_data_mode(); + if (result == command_result::OK || force) { + netif.resume(); + } + } + return result; + } + protected: std::shared_ptr dte; std::shared_ptr device; diff --git a/components/esp_modem/include/cxx_include/esp_modem_dte.hpp b/components/esp_modem/include/cxx_include/esp_modem_dte.hpp index f576e75f1e..4e11515ff7 100644 --- a/components/esp_modem/include/cxx_include/esp_modem_dte.hpp +++ b/components/esp_modem/include/cxx_include/esp_modem_dte.hpp @@ -145,6 +145,12 @@ class DTE : public CommandableIf { */ bool recover(); + /** + * @brief Set internal command callbacks to the underlying terminal. + * Here we capture command replies to be processed by supplied command callbacks in struct command_cb. + */ + void set_command_callbacks(); + protected: /** * @brief Allows for locking the DTE @@ -204,12 +210,6 @@ class DTE : public CommandableIf { } inflatable; #endif // CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED - /** - * @brief Set internal command callbacks to the underlying terminal. - * Here we capture command replies to be processed by supplied command callbacks in struct command_cb. - */ - void set_command_callbacks(); - /** * @brief This abstracts command callback processing and implements its locking, signaling of completion and timeouts. */ diff --git a/components/esp_modem/include/cxx_include/esp_modem_netif.hpp b/components/esp_modem/include/cxx_include/esp_modem_netif.hpp index 05a6a1ddf5..4aa3c151cd 100644 --- a/components/esp_modem/include/cxx_include/esp_modem_netif.hpp +++ b/components/esp_modem/include/cxx_include/esp_modem_netif.hpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -54,6 +54,16 @@ class Netif { */ void stop(); + /** + * @brief Pause the network interface + */ + void pause(); + + /** + * @brief Resume the network interface + */ + void resume(); + void receive(uint8_t *data, size_t len); private: diff --git a/components/esp_modem/src/esp_modem_netif.cpp b/components/esp_modem/src/esp_modem_netif.cpp index fbb75c37c5..3bcb158e75 100644 --- a/components/esp_modem/src/esp_modem_netif.cpp +++ b/components/esp_modem/src/esp_modem_netif.cpp @@ -99,6 +99,20 @@ void Netif::stop() signal.clear(PPP_STARTED); } +void Netif::resume() +{ + ppp_dte->set_read_cb([this](uint8_t *data, size_t len) -> bool { + receive(data, len); + return true; + }); + signal.set(PPP_STARTED); +} + +void Netif::pause() +{ + signal.clear(PPP_STARTED); +} + Netif::~Netif() { if (signal.is_any(PPP_STARTED)) { diff --git a/components/esp_modem/src/esp_modem_uart.cpp b/components/esp_modem/src/esp_modem_uart.cpp index 945fb2889f..ebe31fdded 100644 --- a/components/esp_modem/src/esp_modem_uart.cpp +++ b/components/esp_modem/src/esp_modem_uart.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -176,13 +176,20 @@ int UartTerminal::read(uint8_t *data, size_t len) uart_get_buffered_data_len(uart.port, &length); length = std::min(len, length); if (length > 0) { - return uart_read_bytes(uart.port, data, length, portMAX_DELAY); + int read_len = uart_read_bytes(uart.port, data, length, portMAX_DELAY); +#if CONFIG_ESP_MODEM_ADD_DEBUG_LOGS + ESP_LOG_BUFFER_HEXDUMP("uart-rx", data, read_len, ESP_LOG_DEBUG); +#endif + return read_len; } return 0; } int UartTerminal::write(uint8_t *data, size_t len) { +#if CONFIG_ESP_MODEM_ADD_DEBUG_LOGS + ESP_LOG_BUFFER_HEXDUMP("uart-tx", data, len, ESP_LOG_DEBUG); +#endif return uart_write_bytes_compat(uart.port, data, len); } From 1db83cd1ca03c64bbe2e939c2214e03e86c3c348 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Thu, 5 Dec 2024 10:30:22 +0100 Subject: [PATCH 2/5] feat(modem): Support for pausing network in C-API also adds a demo of this feature to pppos client example --- .../examples/pppos_client/main/Kconfig.projbuild | 10 ++++++++++ .../pppos_client/main/pppos_client_main.c | 16 +++++++++++++++- .../examples/pppos_client/sdkconfig.ci.sim800_c3 | 1 + .../esp_modem/include/esp_modem_c_api_types.h | 11 +++++++++++ components/esp_modem/src/esp_modem_c_api.cpp | 8 ++++++++ 5 files changed, 45 insertions(+), 1 deletion(-) diff --git a/components/esp_modem/examples/pppos_client/main/Kconfig.projbuild b/components/esp_modem/examples/pppos_client/main/Kconfig.projbuild index e4ccc451db..d5d6381b08 100644 --- a/components/esp_modem/examples/pppos_client/main/Kconfig.projbuild +++ b/components/esp_modem/examples/pppos_client/main/Kconfig.projbuild @@ -201,4 +201,14 @@ menu "Example Configuration" help MQTT data message, which we publish and expect to receive. + config EXAMPLE_PAUSE_NETIF_TO_CHECK_SIGNAL + bool "Demonstrate netif pause" + default n + help + Set this to true to demonstrate network pausing. + If enabled, the example waits for an MQTT data, then temporarily + drops network to check signal quality, resumes networking and + publishes another MQTT message. + Connection to the MQTT broker should be kept. + endmenu diff --git a/components/esp_modem/examples/pppos_client/main/pppos_client_main.c b/components/esp_modem/examples/pppos_client/main/pppos_client_main.c index 3529965406..b128844f6b 100644 --- a/components/esp_modem/examples/pppos_client/main/pppos_client_main.c +++ b/components/esp_modem/examples/pppos_client/main/pppos_client_main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -317,6 +317,20 @@ void app_main(void) esp_mqtt_client_handle_t mqtt_client = esp_mqtt_client_init(&mqtt_config); esp_mqtt_client_register_event(mqtt_client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL); esp_mqtt_client_start(mqtt_client); + +#if CONFIG_EXAMPLE_PAUSE_NETIF_TO_CHECK_SIGNAL + xEventGroupWaitBits(event_group, GOT_DATA_BIT, pdTRUE, pdFALSE, portMAX_DELAY); + esp_modem_pause_net(dce, true); + err = esp_modem_get_signal_quality(dce, &rssi, &ber); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_modem_get_signal_quality failed with %d", err); + return; + } + ESP_LOGI(TAG, "Signal quality: rssi=%d, ber=%d", rssi, ber); + esp_modem_pause_net(dce, false); + esp_mqtt_client_publish(mqtt_client, CONFIG_EXAMPLE_MQTT_TEST_TOPIC, CONFIG_EXAMPLE_MQTT_TEST_DATA, 0, 0, 0); +#endif + ESP_LOGI(TAG, "Waiting for MQTT data"); xEventGroupWaitBits(event_group, GOT_DATA_BIT | USB_DISCONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY); CHECK_USB_DISCONNECTION(event_group); diff --git a/components/esp_modem/examples/pppos_client/sdkconfig.ci.sim800_c3 b/components/esp_modem/examples/pppos_client/sdkconfig.ci.sim800_c3 index c82ef823fc..a7832bc0ad 100644 --- a/components/esp_modem/examples/pppos_client/sdkconfig.ci.sim800_c3 +++ b/components/esp_modem/examples/pppos_client/sdkconfig.ci.sim800_c3 @@ -11,5 +11,6 @@ CONFIG_EXAMPLE_MODEM_DEVICE_SIM800=y CONFIG_EXAMPLE_MODEM_DEVICE_BG96=n CONFIG_EXAMPLE_MODEM_PPP_APN="lpwa.vodafone.com" CONFIG_EXAMPLE_MQTT_TEST_TOPIC="/ci/esp-modem/pppos-client" +CONFIG_EXAMPLE_PAUSE_NETIF_TO_CHECK_SIGNAL=y CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y CONFIG_ESP32_PANIC_PRINT_HALT=y diff --git a/components/esp_modem/include/esp_modem_c_api_types.h b/components/esp_modem/include/esp_modem_c_api_types.h index d061045e9a..d3d9718d69 100644 --- a/components/esp_modem/include/esp_modem_c_api_types.h +++ b/components/esp_modem/include/esp_modem_c_api_types.h @@ -160,6 +160,17 @@ esp_err_t esp_modem_set_apn(esp_modem_dce_t *dce, const char *apn); esp_err_t esp_modem_set_urc(esp_modem_dce_t *dce, esp_err_t(*got_line_cb)(uint8_t *data, size_t len)); #endif +/** + * @brief This API provides support for temporarily pausing networking in order + * to send/receive AT commands and resume networking afterwards. + * @note This function does not switch modes, the modem is still in data mode. + * + * @param dce Modem DCE handle + * @param pause true to pause the network interface, false to resume networking + * @return ESP_OK on success + */ +esp_err_t esp_modem_pause_net(esp_modem_dce_t *dce, bool pause); + /** * @} */ diff --git a/components/esp_modem/src/esp_modem_c_api.cpp b/components/esp_modem/src/esp_modem_c_api.cpp index 4d1fa62919..edd8ff379f 100644 --- a/components/esp_modem/src/esp_modem_c_api.cpp +++ b/components/esp_modem/src/esp_modem_c_api.cpp @@ -475,3 +475,11 @@ extern "C" esp_err_t esp_modem_set_urc(esp_modem_dce_t *dce_wrap, esp_err_t(*got return ESP_OK; } #endif + +extern "C" esp_err_t esp_modem_pause_net(esp_modem_dce_t *dce_wrap, bool pause) +{ + if (dce_wrap == nullptr || dce_wrap->dce == nullptr) { + return ESP_ERR_INVALID_ARG; + } + return command_response_to_esp_err(dce_wrap->dce->pause_netif(pause)); +} From 18f196fa1e9dbc834f0baae86ce44f1606442bb3 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Fri, 6 Dec 2024 09:29:35 +0100 Subject: [PATCH 3/5] feat(modem): Add mode detection to the example --- .../pppos_client/main/pppos_client_main.c | 20 +++++++++-- .../pppos_client/sdkconfig.ci.sim800_c3 | 1 + .../include/cxx_include/esp_modem_dce.hpp | 8 ++++- .../esp_modem/include/esp_modem_c_api_types.h | 3 ++ components/esp_modem/src/esp_modem_c_api.cpp | 33 +++++++++++++++++++ 5 files changed, 62 insertions(+), 3 deletions(-) diff --git a/components/esp_modem/examples/pppos_client/main/pppos_client_main.c b/components/esp_modem/examples/pppos_client/main/pppos_client_main.c index b128844f6b..7c0212eefb 100644 --- a/components/esp_modem/examples/pppos_client/main/pppos_client_main.c +++ b/components/esp_modem/examples/pppos_client/main/pppos_client_main.c @@ -177,7 +177,7 @@ 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.task_stack_size = CONFIG_EXAMPLE_MODEM_UART_EVENT_TASK_STACK_SIZE; + dte_config.task_stack_size = CONFIG_EXAMPLE_MODEM_UART_EVENT_TASK_STACK_SIZE * 2; dte_config.task_priority = CONFIG_EXAMPLE_MODEM_UART_EVENT_TASK_PRIORITY; dte_config.dte_buffer_size = CONFIG_EXAMPLE_MODEM_UART_RX_BUFFER_SIZE / 2; @@ -248,6 +248,22 @@ void app_main(void) xEventGroupClearBits(event_group, CONNECT_BIT | GOT_DATA_BIT | USB_DISCONNECTED_BIT); + esp_err_t err = esp_modem_set_mode(dce, ESP_MODEM_MODE_DETECT); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_modem_set_mode(ESP_MODEM_MODE_DETECT) failed with %d", err); + return; + } + ESP_LOGI(TAG, "Mode detected!"); + esp_modem_dce_mode_t mode = esp_modem_get_mode(dce); + ESP_LOGI(TAG, "Current mode is : %d", mode); + if (mode != ESP_MODEM_MODE_COMMAND) { + err = esp_modem_set_mode(dce, ESP_MODEM_MODE_COMMAND); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_modem_set_mode(ESP_MODEM_MODE_COMMAND) failed with %d", err); + return; + } + } + /* Run the modem demo app */ #if CONFIG_EXAMPLE_NEED_SIM_PIN == 1 // check if PIN needed @@ -262,7 +278,7 @@ void app_main(void) #endif int rssi, ber; - esp_err_t err = esp_modem_get_signal_quality(dce, &rssi, &ber); + err = esp_modem_get_signal_quality(dce, &rssi, &ber); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_modem_get_signal_quality failed with %d %s", err, esp_err_to_name(err)); return; diff --git a/components/esp_modem/examples/pppos_client/sdkconfig.ci.sim800_c3 b/components/esp_modem/examples/pppos_client/sdkconfig.ci.sim800_c3 index a7832bc0ad..a537afeb22 100644 --- a/components/esp_modem/examples/pppos_client/sdkconfig.ci.sim800_c3 +++ b/components/esp_modem/examples/pppos_client/sdkconfig.ci.sim800_c3 @@ -14,3 +14,4 @@ CONFIG_EXAMPLE_MQTT_TEST_TOPIC="/ci/esp-modem/pppos-client" CONFIG_EXAMPLE_PAUSE_NETIF_TO_CHECK_SIGNAL=y CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y CONFIG_ESP32_PANIC_PRINT_HALT=y +CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y diff --git a/components/esp_modem/include/cxx_include/esp_modem_dce.hpp b/components/esp_modem/include/cxx_include/esp_modem_dce.hpp index 25ca1e4113..06f0507291 100644 --- a/components/esp_modem/include/cxx_include/esp_modem_dce.hpp +++ b/components/esp_modem/include/cxx_include/esp_modem_dce.hpp @@ -91,6 +91,11 @@ class DCE_T { return mode.set(dte.get(), device.get(), netif, m); } + modem_mode get_mode() + { + return mode.get(); + } + bool recover() { return dte->recover(); @@ -109,11 +114,12 @@ class DCE_T { * @param force true to ignore command failures and continue * @return command_result of the underlying commands */ - command_result pause_netif(bool do_pause, bool force = false) + command_result pause_netif(bool do_pause, bool force = false, int delay = 1000) { command_result result; if (do_pause) { netif.pause(); + Task::Delay(delay); // Mandatory 1s pause before dte->set_command_callbacks(); result = device->set_command_mode(); } else { diff --git a/components/esp_modem/include/esp_modem_c_api_types.h b/components/esp_modem/include/esp_modem_c_api_types.h index d3d9718d69..b82e929b6c 100644 --- a/components/esp_modem/include/esp_modem_c_api_types.h +++ b/components/esp_modem/include/esp_modem_c_api_types.h @@ -45,6 +45,8 @@ typedef enum esp_modem_dce_mode { ESP_MODEM_MODE_CMUX_MANUAL_SWAP, /**< Swap terminals in CMUX manual mode */ ESP_MODEM_MODE_CMUX_MANUAL_DATA, /**< Set DATA mode in CMUX manual mode */ ESP_MODEM_MODE_CMUX_MANUAL_COMMAND, /**< Set COMMAND mode in CMUX manual mode */ + ESP_MODEM_MODE_DETECT, /**< Detect the mode and resume it (if sucessfully detected) */ + ESP_MODEM_MODE_UNDEF, } esp_modem_dce_mode_t; /** @@ -171,6 +173,7 @@ esp_err_t esp_modem_set_urc(esp_modem_dce_t *dce, esp_err_t(*got_line_cb)(uint8_ */ esp_err_t esp_modem_pause_net(esp_modem_dce_t *dce, bool pause); +esp_modem_dce_mode_t esp_modem_get_mode(esp_modem_dce_t *dce); /** * @} */ diff --git a/components/esp_modem/src/esp_modem_c_api.cpp b/components/esp_modem/src/esp_modem_c_api.cpp index edd8ff379f..036e013630 100644 --- a/components/esp_modem/src/esp_modem_c_api.cpp +++ b/components/esp_modem/src/esp_modem_c_api.cpp @@ -95,12 +95,43 @@ extern "C" esp_err_t esp_modem_sync(esp_modem_dce_t *dce_wrap) return command_response_to_esp_err(dce_wrap->dce->sync()); } +extern "C" esp_modem_dce_mode_t esp_modem_get_mode(esp_modem_dce_t *dce_wrap) +{ + if (dce_wrap == nullptr || dce_wrap->dce == nullptr) { + return ESP_MODEM_MODE_UNDEF; + } + auto mode = dce_wrap->dce->get_mode(); + switch (mode) { + default: + case modem_mode::UNDEF: + return ESP_MODEM_MODE_UNDEF; + case modem_mode::COMMAND_MODE: + return ESP_MODEM_MODE_COMMAND; + case modem_mode::DATA_MODE: + return ESP_MODEM_MODE_DATA; + case modem_mode::CMUX_MODE: + return ESP_MODEM_MODE_CMUX; + case modem_mode::CMUX_MANUAL_MODE: + return ESP_MODEM_MODE_CMUX_MANUAL; + case modem_mode::CMUX_MANUAL_EXIT: + return ESP_MODEM_MODE_CMUX_MANUAL_EXIT; + case modem_mode::CMUX_MANUAL_DATA: + return ESP_MODEM_MODE_CMUX_MANUAL_DATA; + case modem_mode::CMUX_MANUAL_COMMAND: + return ESP_MODEM_MODE_CMUX_MANUAL_COMMAND; + case modem_mode::CMUX_MANUAL_SWAP: + return ESP_MODEM_MODE_CMUX_MANUAL_SWAP; + } +} + extern "C" esp_err_t esp_modem_set_mode(esp_modem_dce_t *dce_wrap, esp_modem_dce_mode_t mode) { if (dce_wrap == nullptr || dce_wrap->dce == nullptr) { return ESP_ERR_INVALID_ARG; } switch (mode) { + case ESP_MODEM_MODE_UNDEF: + return dce_wrap->dce->set_mode(modem_mode::UNDEF) ? ESP_OK : ESP_FAIL; case ESP_MODEM_MODE_DATA: return dce_wrap->dce->set_mode(modem_mode::DATA_MODE) ? ESP_OK : ESP_FAIL; case ESP_MODEM_MODE_COMMAND: @@ -117,6 +148,8 @@ extern "C" esp_err_t esp_modem_set_mode(esp_modem_dce_t *dce_wrap, esp_modem_dce return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_DATA) ? ESP_OK : ESP_FAIL; case ESP_MODEM_MODE_CMUX_MANUAL_COMMAND: return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_COMMAND) ? ESP_OK : ESP_FAIL; + case ESP_MODEM_MODE_DETECT: + return dce_wrap->dce->set_mode(modem_mode::AUTODETECT) ? ESP_OK : ESP_FAIL; } return ESP_ERR_NOT_SUPPORTED; } From c989c6adaedc1a53462f22ca8e1951c972b61622 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Fri, 6 Dec 2024 10:12:25 +0100 Subject: [PATCH 4/5] fix(modem): Fix PPP mode detection to accept LCP/conf --- components/esp_modem/src/esp_modem_dce.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/components/esp_modem/src/esp_modem_dce.cpp b/components/esp_modem/src/esp_modem_dce.cpp index 0a0ca14acd..f105516325 100644 --- a/components/esp_modem/src/esp_modem_dce.cpp +++ b/components/esp_modem/src/esp_modem_dce.cpp @@ -328,12 +328,19 @@ modem_mode DCE_Mode::guess_unsafe(DTE *dte, bool with_cmux) if (reply_pos >= sizeof(probe::ppp::lcp_echo_reply_head)) { // check for initial 2 bytes auto *ptr = static_cast(memmem(reply, reply_pos, probe::ppp::lcp_echo_reply_head.data(), 2)); - // and check the other two bytes for protocol ID: LCP + // and check the other two bytes for protocol ID: + // * either LCP reply if (ptr && ptr[3] == probe::ppp::lcp_echo_reply_head[3] && ptr[4] == probe::ppp::lcp_echo_reply_head[4]) { if (auto signal = weak_signal.lock()) { signal->set(probe::ppp::mode); } } + // * or LCP conf request + if (ptr && ptr[3] == probe::ppp::lcp_echo_request[3] && ptr[4] == probe::ppp::lcp_echo_request[4]) { + if (auto signal = weak_signal.lock()) { + signal->set(probe::ppp::mode); + } + } } if (reply_pos >= 4 && memmem(reply, reply_pos, probe::cmd::reply, sizeof(probe::cmd::reply))) { if (reply[0] != 0xf9) { // double check that the reply is not wrapped in CMUX headers From 5e929902c731a87b99124b285e388ebacb0ba9a7 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Fri, 6 Dec 2024 10:56:06 +0100 Subject: [PATCH 5/5] fix(modem): Minor cleanup of pppos example * Use CONFIG_EXAMPLE_DETECT_MODE_BEFORE_CONNECT to demonstrate mode detect * Use disconnection flag to indicate conneciton issue and gracefully degrade to command mode * Remove IDF-verion < v5.0 code --- .../pppos_client/main/Kconfig.projbuild | 9 ++++ .../pppos_client/main/pppos_client_main.c | 43 ++++++++++++------- .../pppos_client/pytest_pppos_client.py | 2 +- .../pppos_client/sdkconfig.ci.sim800_c3 | 2 +- .../sdkconfig.ci.sim800_cmux | 1 + components/esp_modem/src/esp_modem_c_api.cpp | 8 ++++ 6 files changed, 48 insertions(+), 17 deletions(-) diff --git a/components/esp_modem/examples/pppos_client/main/Kconfig.projbuild b/components/esp_modem/examples/pppos_client/main/Kconfig.projbuild index d5d6381b08..2bb3e4c551 100644 --- a/components/esp_modem/examples/pppos_client/main/Kconfig.projbuild +++ b/components/esp_modem/examples/pppos_client/main/Kconfig.projbuild @@ -211,4 +211,13 @@ menu "Example Configuration" publishes another MQTT message. Connection to the MQTT broker should be kept. + config EXAMPLE_DETECT_MODE_BEFORE_CONNECT + bool "Detect mode before connect" + default n + help + Set this to true to demonstrate mode auto-detection. + If enabled, the example tries to recognize the actual mode. + If mode is detected correctly and it is not a command mode, + then the example switches to command mode. + endmenu diff --git a/components/esp_modem/examples/pppos_client/main/pppos_client_main.c b/components/esp_modem/examples/pppos_client/main/pppos_client_main.c index 7c0212eefb..457a8ebf1e 100644 --- a/components/esp_modem/examples/pppos_client/main/pppos_client_main.c +++ b/components/esp_modem/examples/pppos_client/main/pppos_client_main.c @@ -34,6 +34,7 @@ static const char *TAG = "pppos_example"; static EventGroupHandle_t event_group = NULL; static const int CONNECT_BIT = BIT0; +static const int DISCONNECT_BIT = BIT1; static const int GOT_DATA_BIT = BIT2; static const int USB_DISCONNECTED_BIT = BIT3; // Used only with USB DTE but we define it unconditionally, to avoid too many #ifdefs in the code @@ -55,6 +56,7 @@ static void usb_terminal_error_handler(esp_modem_terminal_error_t err) } #define CHECK_USB_DISCONNECTION(event_group) \ if ((xEventGroupGetBits(event_group) & USB_DISCONNECTED_BIT) == USB_DISCONNECTED_BIT) { \ + ESP_LOGE(TAG, "USB_DISCONNECTED_BIT destroying modem dce"); \ esp_modem_destroy(dce); \ continue; \ } @@ -140,6 +142,7 @@ static void on_ip_event(void *arg, esp_event_base_t event_base, ESP_LOGI(TAG, "GOT ip event!!!"); } else if (event_id == IP_EVENT_PPP_LOST_IP) { ESP_LOGI(TAG, "Modem Disconnect from PPP Server"); + xEventGroupSetBits(event_group, DISCONNECT_BIT); } else if (event_id == IP_EVENT_GOT_IP6) { ESP_LOGI(TAG, "GOT IPv6 event!"); @@ -158,6 +161,7 @@ void app_main(void) ESP_ERROR_CHECK(esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, &on_ppp_changed, NULL)); /* Configure the PPP netif */ + esp_err_t err; esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_EXAMPLE_MODEM_PPP_APN); esp_netif_config_t netif_ppp_config = ESP_NETIF_DEFAULT_PPP(); esp_netif_t *esp_netif = esp_netif_new(&netif_ppp_config); @@ -177,7 +181,7 @@ 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.task_stack_size = CONFIG_EXAMPLE_MODEM_UART_EVENT_TASK_STACK_SIZE * 2; + 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; @@ -205,7 +209,7 @@ void app_main(void) #endif assert(dce); if (dte_config.uart_config.flow_control == ESP_MODEM_FLOW_CONTROL_HW) { - esp_err_t err = esp_modem_set_flow_control(dce, 2, 2); //2/2 means HW Flow Control. + err = esp_modem_set_flow_control(dce, 2, 2); //2/2 means HW Flow Control. if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to set the set_flow_control mode"); return; @@ -246,23 +250,27 @@ void app_main(void) #error Invalid serial connection to modem. #endif - xEventGroupClearBits(event_group, CONNECT_BIT | GOT_DATA_BIT | USB_DISCONNECTED_BIT); +#if CONFIG_EXAMPLE_DETECT_MODE_BEFORE_CONNECT + xEventGroupClearBits(event_group, CONNECT_BIT | GOT_DATA_BIT | USB_DISCONNECTED_BIT | DISCONNECT_BIT); - esp_err_t err = esp_modem_set_mode(dce, ESP_MODEM_MODE_DETECT); + err = esp_modem_set_mode(dce, ESP_MODEM_MODE_DETECT); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_modem_set_mode(ESP_MODEM_MODE_DETECT) failed with %d", err); return; } - ESP_LOGI(TAG, "Mode detected!"); esp_modem_dce_mode_t mode = esp_modem_get_mode(dce); - ESP_LOGI(TAG, "Current mode is : %d", mode); - if (mode != ESP_MODEM_MODE_COMMAND) { + ESP_LOGI(TAG, "Mode detection completed: current mode is: %d", mode); + if (mode == ESP_MODEM_MODE_DATA) { // set back to command mode err = esp_modem_set_mode(dce, ESP_MODEM_MODE_COMMAND); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_modem_set_mode(ESP_MODEM_MODE_COMMAND) failed with %d", err); return; } + ESP_LOGI(TAG, "Command mode restored"); } +#endif // CONFIG_EXAMPLE_DETECT_MODE_BEFORE_CONNECT + + xEventGroupClearBits(event_group, CONNECT_BIT | GOT_DATA_BIT | USB_DISCONNECTED_BIT | DISCONNECT_BIT); /* Run the modem demo app */ #if CONFIG_EXAMPLE_NEED_SIM_PIN == 1 @@ -317,19 +325,24 @@ void app_main(void) } /* Wait for IP address */ ESP_LOGI(TAG, "Waiting for IP address"); - xEventGroupWaitBits(event_group, CONNECT_BIT | USB_DISCONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY); + xEventGroupWaitBits(event_group, CONNECT_BIT | USB_DISCONNECTED_BIT | DISCONNECT_BIT, pdFALSE, pdFALSE, + pdMS_TO_TICKS(60000)); CHECK_USB_DISCONNECTION(event_group); + if ((xEventGroupGetBits(event_group) & CONNECT_BIT) != CONNECT_BIT) { + ESP_LOGW(TAG, "Modem not connected, switching back to the command mode"); + err = esp_modem_set_mode(dce, ESP_MODEM_MODE_COMMAND); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_modem_set_mode(ESP_MODEM_MODE_COMMAND) failed with %d", err); + return; + } + ESP_LOGI(TAG, "Command mode restored"); + return; + } /* Config MQTT */ -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) esp_mqtt_client_config_t mqtt_config = { .broker.address.uri = CONFIG_EXAMPLE_MQTT_BROKER_URI, }; -#else - esp_mqtt_client_config_t mqtt_config = { - .uri = CONFIG_EXAMPLE_MQTT_BROKER_URI, - }; -#endif esp_mqtt_client_handle_t mqtt_client = esp_mqtt_client_init(&mqtt_config); esp_mqtt_client_register_event(mqtt_client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL); esp_mqtt_client_start(mqtt_client); @@ -345,7 +358,7 @@ void app_main(void) ESP_LOGI(TAG, "Signal quality: rssi=%d, ber=%d", rssi, ber); esp_modem_pause_net(dce, false); esp_mqtt_client_publish(mqtt_client, CONFIG_EXAMPLE_MQTT_TEST_TOPIC, CONFIG_EXAMPLE_MQTT_TEST_DATA, 0, 0, 0); -#endif +#endif // CONFIG_EXAMPLE_PAUSE_NETIF_TO_CHECK_SIGNAL ESP_LOGI(TAG, "Waiting for MQTT data"); xEventGroupWaitBits(event_group, GOT_DATA_BIT | USB_DISCONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY); diff --git a/components/esp_modem/examples/pppos_client/pytest_pppos_client.py b/components/esp_modem/examples/pppos_client/pytest_pppos_client.py index 2ffb42cabc..6c823c5aa9 100644 --- a/components/esp_modem/examples/pppos_client/pytest_pppos_client.py +++ b/components/esp_modem/examples/pppos_client/pytest_pppos_client.py @@ -12,7 +12,7 @@ def test_pppos_connect(dut): 4. checks that the client cleanly disconnects """ # Check the sequence of connecting, publishing, disconnecting - dut.expect('Modem Connect to PPP Server') + dut.expect('Modem Connect to PPP Server', timeout=90) # Check for MQTT connection and the data event dut.expect('MQTT_EVENT_CONNECTED') dut.expect('MQTT_EVENT_DATA') diff --git a/components/esp_modem/examples/pppos_client/sdkconfig.ci.sim800_c3 b/components/esp_modem/examples/pppos_client/sdkconfig.ci.sim800_c3 index a537afeb22..23b0fc200f 100644 --- a/components/esp_modem/examples/pppos_client/sdkconfig.ci.sim800_c3 +++ b/components/esp_modem/examples/pppos_client/sdkconfig.ci.sim800_c3 @@ -14,4 +14,4 @@ CONFIG_EXAMPLE_MQTT_TEST_TOPIC="/ci/esp-modem/pppos-client" CONFIG_EXAMPLE_PAUSE_NETIF_TO_CHECK_SIGNAL=y CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y CONFIG_ESP32_PANIC_PRINT_HALT=y -CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y +CONFIG_EXAMPLE_DETECT_MODE_BEFORE_CONNECT=y diff --git a/components/esp_modem/examples/simple_cmux_client/sdkconfig.ci.sim800_cmux b/components/esp_modem/examples/simple_cmux_client/sdkconfig.ci.sim800_cmux index 6bf623113c..6a22a89f12 100644 --- a/components/esp_modem/examples/simple_cmux_client/sdkconfig.ci.sim800_cmux +++ b/components/esp_modem/examples/simple_cmux_client/sdkconfig.ci.sim800_cmux @@ -16,3 +16,4 @@ CONFIG_COMPILER_CXX_EXCEPTIONS=y CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192 CONFIG_EXAMPLE_CLOSE_CMUX_AT_END=y CONFIG_EXAMPLE_MQTT_TEST_TOPIC="/ci/esp-modem/pppos-client" +CONFIG_BROKER_URI="mqtt://mqtt.eclipseprojects.io" diff --git a/components/esp_modem/src/esp_modem_c_api.cpp b/components/esp_modem/src/esp_modem_c_api.cpp index 036e013630..e9698995e1 100644 --- a/components/esp_modem/src/esp_modem_c_api.cpp +++ b/components/esp_modem/src/esp_modem_c_api.cpp @@ -516,3 +516,11 @@ extern "C" esp_err_t esp_modem_pause_net(esp_modem_dce_t *dce_wrap, bool pause) } return command_response_to_esp_err(dce_wrap->dce->pause_netif(pause)); } + +extern "C" esp_err_t esp_modem_hang_up(esp_modem_dce_t *dce_wrap) +{ + if (dce_wrap == nullptr || dce_wrap->dce == nullptr) { + return ESP_ERR_INVALID_ARG; + } + return command_response_to_esp_err(dce_wrap->dce->hang_up()); +}