diff --git a/components/esp_modem/examples/modem_console/main/my_module_dce.hpp b/components/esp_modem/examples/modem_console/main/my_module_dce.hpp index 43205bfb2..fe9fb4450 100644 --- a/components/esp_modem/examples/modem_console/main/my_module_dce.hpp +++ b/components/esp_modem/examples/modem_console/main/my_module_dce.hpp @@ -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 */ @@ -17,26 +17,20 @@ #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()` + * @brief Definition of a custom DCE uses GenericModule and all its methods + * but could override command processing. Here, for demonstration purposes only, + * we "inject" URC handler to the actual command processing. + * This is possible since we inherit from `CommandableIf` and redefine `command()` method. + * Then we're able to use declare all common methods from the command library + * to be processed using "our" `command()` method (with custom URC handler). */ -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; - } -}; - namespace Shiny { using namespace esp_modem; -class DCE : public esp_modem::DCE_T, public CommandableIf { +class DCE : public esp_modem::DCE_T, public CommandableIf { public: - using DCE_T::DCE_T; + using DCE_T::DCE_T; command_result command(const std::string &cmd, got_line_cb got_line, uint32_t time_ms) override @@ -97,7 +91,7 @@ public: std::shared_ptr dte, esp_netif_t *netif) { - return build_generic_DCE>(config, std::move(dte), netif); + return build_generic_DCE>(config, std::move(dte), netif); } }; diff --git a/components/esp_modem/include/cxx_include/esp_modem_command_library.hpp b/components/esp_modem/include/cxx_include/esp_modem_command_library.hpp index 9b732fe50..d0e133eeb 100644 --- a/components/esp_modem/include/cxx_include/esp_modem_command_library.hpp +++ b/components/esp_modem/include/cxx_include/esp_modem_command_library.hpp @@ -54,7 +54,7 @@ command_result power_down_sim76xx(CommandableIf *t); command_result power_down_sim70xx(CommandableIf *t); command_result set_network_bands_sim76xx(CommandableIf *t, const std::string &mode, const int *bands, int size); command_result power_down_sim8xx(CommandableIf *t); -command_result set_data_mode_sim8xx(CommandableIf *t); +command_result set_data_mode_alt(CommandableIf *t); command_result set_pdp_context(CommandableIf *t, PdpContext &pdp, uint32_t timeout_ms); /** diff --git a/components/esp_modem/include/cxx_include/esp_modem_dce_module.hpp b/components/esp_modem/include/cxx_include/esp_modem_dce_module.hpp index cc08c537d..6ae36e63e 100644 --- a/components/esp_modem/include/cxx_include/esp_modem_dce_module.hpp +++ b/components/esp_modem/include/cxx_include/esp_modem_dce_module.hpp @@ -144,6 +144,8 @@ class SIM7070: public GenericModule { using GenericModule::GenericModule; public: command_result power_down() override; + command_result set_data_mode() override; + }; /** @@ -162,7 +164,6 @@ class SIM800: public GenericModule { using GenericModule::GenericModule; public: command_result power_down() override; - command_result set_data_mode() override; }; /** diff --git a/components/esp_modem/include/generate/esp_modem_command_declare.inc b/components/esp_modem/include/generate/esp_modem_command_declare.inc index 70f3a83be..909d71603 100644 --- a/components/esp_modem/include/generate/esp_modem_command_declare.inc +++ b/components/esp_modem/include/generate/esp_modem_command_declare.inc @@ -44,6 +44,18 @@ ESP_MODEM_DECLARE_DCE_COMMAND(store_profile, command_result, 0) \ */\ ESP_MODEM_DECLARE_DCE_COMMAND(set_pin, command_result, 1, STRING_IN(p1, pin)) \ \ +/** + * @brief Execute the supplied AT command in raw mode (doesn't append '\r' to command, returns everything) + * @param[in] cmd String command that's send to DTE + * @param[out] out Raw output from DTE + * @param[in] pass Pattern in response for the API to return OK + * @param[in] fail Pattern in response for the API to return FAIL + * @param[in] cmd String command that's send to DTE + * @param[in] timeout AT command timeout in milliseconds + * @return OK, FAIL or TIMEOUT + */\ +ESP_MODEM_DECLARE_DCE_COMMAND(at_raw, command_result, 5, STRING_IN(p1, cmd), STRING_OUT(p2, out), STRING_IN(p3, pass), STRING_IN(p4, fail), INT_IN(p5, timeout)) \ + \ /** * @brief Execute the supplied AT command * @param[in] cmd AT command diff --git a/components/esp_modem/src/esp_modem_c_api.cpp b/components/esp_modem/src/esp_modem_c_api.cpp index 7178bda1a..6c32cf8c6 100644 --- a/components/esp_modem/src/esp_modem_c_api.cpp +++ b/components/esp_modem/src/esp_modem_c_api.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -17,7 +17,7 @@ #include "esp_private/c_api_wrapper.hpp" #ifndef ESP_MODEM_C_API_STR_MAX -#define ESP_MODEM_C_API_STR_MAX 64 +#define ESP_MODEM_C_API_STR_MAX 128 #endif #ifndef HAVE_STRLCPY @@ -206,6 +206,20 @@ extern "C" esp_err_t esp_modem_get_imsi(esp_modem_dce_t *dce_wrap, char *p_imsi) return ret; } +extern "C" esp_err_t esp_modem_at_raw(esp_modem_dce_t *dce_wrap, const char *cmd, char *p_out, const char *pass, const char *fail, int timeout) +{ + if (dce_wrap == nullptr || dce_wrap->dce == nullptr) { + return ESP_ERR_INVALID_ARG; + } + std::string out; + auto ret = command_response_to_esp_err(dce_wrap->dce->at_raw(cmd, out, pass, fail, timeout)); + if ((p_out != NULL) && (!out.empty())) { + strlcpy(p_out, out.c_str(), ESP_MODEM_C_API_STR_MAX); + } + return ret; +} + + extern "C" esp_err_t esp_modem_set_flow_control(esp_modem_dce_t *dce_wrap, int dce_flow, int dte_flow) { if (dce_wrap == nullptr || dce_wrap->dce == nullptr) { diff --git a/components/esp_modem/src/esp_modem_command_library.cpp b/components/esp_modem/src/esp_modem_command_library.cpp index 976869e51..46e005db0 100644 --- a/components/esp_modem/src/esp_modem_command_library.cpp +++ b/components/esp_modem/src/esp_modem_command_library.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -277,13 +277,13 @@ command_result set_pdp_context(CommandableIf *t, PdpContext &pdp) command_result set_data_mode(CommandableIf *t) { ESP_LOGV(TAG, "%s", __func__ ); - return generic_command(t, "ATD*99##\r", "CONNECT", "ERROR", 5000); + return generic_command(t, "ATD*99#\r", "CONNECT", "ERROR", 5000); } -command_result set_data_mode_sim8xx(CommandableIf *t) +command_result set_data_mode_alt(CommandableIf *t) { ESP_LOGV(TAG, "%s", __func__ ); - return generic_command(t, "ATD*99#\r", "CONNECT", "ERROR", 5000); + return generic_command(t, "ATD*99##\r", "CONNECT", "ERROR", 5000); } command_result resume_data_mode(CommandableIf *t) @@ -394,6 +394,22 @@ command_result at(CommandableIf *t, const std::string &cmd, std::string &out, in return generic_get_string(t, at_command, out, timeout); } +command_result at_raw(CommandableIf *t, const std::string &cmd, std::string &out, const std::string &pass, const std::string &fail, int timeout = 500) +{ + ESP_LOGV(TAG, "%s", __func__ ); + return t->command(cmd, [&](uint8_t *data, size_t len) { + out.assign(reinterpret_cast(data), len); + + if (out.find(pass) != std::string::npos) { + return command_result::OK; + } else if (out.find(fail) != std::string::npos) { + return command_result::FAIL; + } + + return command_result::TIMEOUT; + }, timeout); +} + command_result get_signal_quality(CommandableIf *t, int &rssi, int &ber) { ESP_LOGV(TAG, "%s", __func__ ); diff --git a/components/esp_modem/src/esp_modem_modules.cpp b/components/esp_modem/src/esp_modem_modules.cpp index a3c6801e3..563403494 100644 --- a/components/esp_modem/src/esp_modem_modules.cpp +++ b/components/esp_modem/src/esp_modem_modules.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -67,6 +67,11 @@ command_result SIM7070::power_down() return dce_commands::power_down_sim70xx(dte.get()); } +command_result SIM7070::set_data_mode() +{ + return dce_commands::set_data_mode_alt(dte.get()); +} + command_result SIM7000::power_down() { return dce_commands::power_down_sim70xx(dte.get()); @@ -77,11 +82,6 @@ command_result SIM800::power_down() return dce_commands::power_down_sim8xx(dte.get()); } -command_result SIM800::set_data_mode() -{ - return dce_commands::set_data_mode_sim8xx(dte.get()); -} - command_result BG96::set_pdp_context(esp_modem::PdpContext &pdp) { return dce_commands::set_pdp_context(dte.get(), pdp, 300); diff --git a/components/esp_modem/test/host_test/main/LoopbackTerm.cpp b/components/esp_modem/test/host_test/main/LoopbackTerm.cpp index e230fea2c..935c634fa 100644 --- a/components/esp_modem/test/host_test/main/LoopbackTerm.cpp +++ b/components/esp_modem/test/host_test/main/LoopbackTerm.cpp @@ -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 */ @@ -36,7 +36,7 @@ int LoopbackTerm::write(uint8_t *data, size_t len) } else if (command == "ATO\r") { response = "ERROR\r\n"; } else if (command.find("ATD") != std::string::npos) { - response = "CONNECT\r\n"; + response = "CONNECT\n"; } else if (command.find("AT+CSQ\r") != std::string::npos) { response = "+CSQ: 123,456\n\r\nOK\r\n"; } else if (command.find("AT+CGMM\r") != std::string::npos) { @@ -74,6 +74,9 @@ int LoopbackTerm::write(uint8_t *data, size_t len) } if (len > 2 && data[0] == 0xf9) { // Simple CMUX responder // turn the request into a reply -> implements CMUX loopback + // Note: This simple CMUX responder only updates CMUX headers and replaces payload. + // It means that all responses (that we test) must be shorter or equal to the requests + // For example ATD (dial command): sizeof("ATD*99#") >= sizeof("CONNECT"); if (data[2] == 0x3f || data[2] == 0x53) { // SABM command data[2] = 0x73; } else if (data[2] == 0xef) { // Generic request diff --git a/docs/esp_modem/en/advanced_api.rst b/docs/esp_modem/en/advanced_api.rst index a487398b3..4df349f8f 100644 --- a/docs/esp_modem/en/advanced_api.rst +++ b/docs/esp_modem/en/advanced_api.rst @@ -26,15 +26,17 @@ Create custom module Creating a custom module is necessary if the application needs to use a specific device that is not supported and their commands differ from any of the supported devices. In this case it is recommended to define a new class -representing this specific device and derive from the :cpp:class:`esp_modem::GenericModule`. In order to instantiate -the appropriate DCE of this module, application could use :ref:`the DCE factory`, and build the DCE with -the specific module, using :cpp:func:`esp_modem::dce_factory::Factory::build`. +representing this specific device and derive from the :cpp:class:`esp_modem::GenericModule` (or any other available +module, that's close to your custom device). Then you can create the DCE using :ref:`the DCE factory` +public method :cpp:func:`esp_modem::dce_factory::Factory::create_unique_dce_from`. -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, +Please note that the ``pppos_client`` example defines a trivial custom DCE which overrides one command, and adds a new command for demonstration purposes only. +It is also possible to create a specific DCE class that would conform to the generic ``DCE`` API and use all generic commands, +work with commands differently. This might be useful to add some custom preprocessing of commands or replies. +Please check the ``modem_console`` example with ``CONFIG_EXAMPLE_MODEM_DEVICE_SHINY=y`` configuration which demonstrates +overriding default ``command()`` method to implement URC processing in user space. Create new communication interface ----------------------------------