mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-07-30 10:47:29 +02:00
Merge pull request #477 from david-cermak/feat/modem_at_raw
feat(modem): Added support for at_raw() command
This commit is contained in:
@ -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
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
*/
|
*/
|
||||||
@ -17,26 +17,20 @@
|
|||||||
#include "cxx_include/esp_modem_dce_module.hpp"
|
#include "cxx_include/esp_modem_dce_module.hpp"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Definition of a custom modem which inherits from the GenericModule, uses all its methods
|
* @brief Definition of a custom DCE uses GenericModule and all its methods
|
||||||
* and could override any of them. Here, for demonstration purposes only, we redefine just `get_module_name()`
|
* 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 {
|
namespace Shiny {
|
||||||
|
|
||||||
using namespace esp_modem;
|
using namespace esp_modem;
|
||||||
|
|
||||||
class DCE : public esp_modem::DCE_T<MyShinyModem>, public CommandableIf {
|
class DCE : public esp_modem::DCE_T<GenericModule>, public CommandableIf {
|
||||||
public:
|
public:
|
||||||
using DCE_T<MyShinyModem>::DCE_T;
|
using DCE_T<GenericModule>::DCE_T;
|
||||||
|
|
||||||
command_result
|
command_result
|
||||||
command(const std::string &cmd, got_line_cb got_line, uint32_t time_ms) override
|
command(const std::string &cmd, got_line_cb got_line, uint32_t time_ms) override
|
||||||
@ -97,7 +91,7 @@ public:
|
|||||||
std::shared_ptr<esp_modem::DTE> dte,
|
std::shared_ptr<esp_modem::DTE> dte,
|
||||||
esp_netif_t *netif)
|
esp_netif_t *netif)
|
||||||
{
|
{
|
||||||
return build_generic_DCE<MyShinyModem, DCE, std::unique_ptr<DCE>>(config, std::move(dte), netif);
|
return build_generic_DCE<GenericModule, DCE, std::unique_ptr<DCE>>(config, std::move(dte), netif);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -54,7 +54,7 @@ command_result power_down_sim76xx(CommandableIf *t);
|
|||||||
command_result power_down_sim70xx(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 set_network_bands_sim76xx(CommandableIf *t, const std::string &mode, const int *bands, int size);
|
||||||
command_result power_down_sim8xx(CommandableIf *t);
|
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);
|
command_result set_pdp_context(CommandableIf *t, PdpContext &pdp, uint32_t timeout_ms);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -144,6 +144,8 @@ class SIM7070: public GenericModule {
|
|||||||
using GenericModule::GenericModule;
|
using GenericModule::GenericModule;
|
||||||
public:
|
public:
|
||||||
command_result power_down() override;
|
command_result power_down() override;
|
||||||
|
command_result set_data_mode() override;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -162,7 +164,6 @@ class SIM800: public GenericModule {
|
|||||||
using GenericModule::GenericModule;
|
using GenericModule::GenericModule;
|
||||||
public:
|
public:
|
||||||
command_result power_down() override;
|
command_result power_down() override;
|
||||||
command_result set_data_mode() override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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)) \
|
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
|
* @brief Execute the supplied AT command
|
||||||
* @param[in] cmd AT command
|
* @param[in] cmd AT command
|
||||||
|
@ -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
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -17,7 +17,7 @@
|
|||||||
#include "esp_private/c_api_wrapper.hpp"
|
#include "esp_private/c_api_wrapper.hpp"
|
||||||
|
|
||||||
#ifndef ESP_MODEM_C_API_STR_MAX
|
#ifndef ESP_MODEM_C_API_STR_MAX
|
||||||
#define ESP_MODEM_C_API_STR_MAX 64
|
#define ESP_MODEM_C_API_STR_MAX 128
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef HAVE_STRLCPY
|
#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;
|
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)
|
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) {
|
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
|
||||||
|
@ -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
|
* 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)
|
command_result set_data_mode(CommandableIf *t)
|
||||||
{
|
{
|
||||||
ESP_LOGV(TAG, "%s", __func__ );
|
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__ );
|
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)
|
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);
|
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<char *>(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)
|
command_result get_signal_quality(CommandableIf *t, int &rssi, int &ber)
|
||||||
{
|
{
|
||||||
ESP_LOGV(TAG, "%s", __func__ );
|
ESP_LOGV(TAG, "%s", __func__ );
|
||||||
|
@ -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
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -67,6 +67,11 @@ command_result SIM7070::power_down()
|
|||||||
return dce_commands::power_down_sim70xx(dte.get());
|
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()
|
command_result SIM7000::power_down()
|
||||||
{
|
{
|
||||||
return dce_commands::power_down_sim70xx(dte.get());
|
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());
|
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)
|
command_result BG96::set_pdp_context(esp_modem::PdpContext &pdp)
|
||||||
{
|
{
|
||||||
return dce_commands::set_pdp_context(dte.get(), pdp, 300);
|
return dce_commands::set_pdp_context(dte.get(), pdp, 300);
|
||||||
|
@ -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
|
* 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") {
|
} else if (command == "ATO\r") {
|
||||||
response = "ERROR\r\n";
|
response = "ERROR\r\n";
|
||||||
} else if (command.find("ATD") != std::string::npos) {
|
} 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) {
|
} else if (command.find("AT+CSQ\r") != std::string::npos) {
|
||||||
response = "+CSQ: 123,456\n\r\nOK\r\n";
|
response = "+CSQ: 123,456\n\r\nOK\r\n";
|
||||||
} else if (command.find("AT+CGMM\r") != std::string::npos) {
|
} 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
|
if (len > 2 && data[0] == 0xf9) { // Simple CMUX responder
|
||||||
// turn the request into a reply -> implements CMUX loopback
|
// 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
|
if (data[2] == 0x3f || data[2] == 0x53) { // SABM command
|
||||||
data[2] = 0x73;
|
data[2] = 0x73;
|
||||||
} else if (data[2] == 0xef) { // Generic request
|
} else if (data[2] == 0xef) { // Generic request
|
||||||
|
@ -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
|
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
|
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
|
representing this specific device and derive from the :cpp:class:`esp_modem::GenericModule` (or any other available
|
||||||
the appropriate DCE of this module, application could use :ref:`the DCE factory<dce_factory>`, and build the DCE with
|
module, that's close to your custom device). Then you can create the DCE using :ref:`the DCE factory<dce_factory>`
|
||||||
the specific module, using :cpp:func:`esp_modem::dce_factory::Factory::build`.
|
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 ``pppos_client`` example defines a trivial custom DCE which overrides one command, and adds a new command
|
||||||
|
|
||||||
Please note that the ``modem_console`` example defines a trivial custom modem DCE which overrides one command,
|
|
||||||
for demonstration purposes only.
|
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
|
Create new communication interface
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
Reference in New Issue
Block a user