Merge pull request #786 from robbedptechnics/support-sqn-gm02s

feat(module): add support for sequans GM02S modem (DPTechnics) (IDFGH-14891)
This commit is contained in:
david-cermak
2025-03-25 12:24:51 +01:00
committed by GitHub
11 changed files with 339 additions and 59 deletions

View File

@ -0,0 +1,17 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string>
#include <list>
#include "esp_log.h"
#include "cxx_include/esp_modem_dte.hpp"
#include "cxx_include/esp_modem_dce_module.hpp"
namespace esp_modem::dce_commands {
command_result generic_command(CommandableIf *t, const std::string &command,
const std::list<std::string_view> &pass_phrase,
const std::list<std::string_view> &fail_phrase,
uint32_t timeout_ms);
}

View File

@ -84,7 +84,6 @@ std::unique_ptr<DCE> create_SIM7070_dce(const dce_config *config, std::shared_pt
*/
std::unique_ptr<DCE> create_SIM7000_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif);
/**
* @brief Create DCE based on SIM800 module
*/
@ -95,6 +94,11 @@ std::unique_ptr<DCE> create_SIM800_dce(const dce_config *config, std::shared_ptr
*/
std::unique_ptr<DCE> create_BG96_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif);
/**
* @brief Create DCE based on Sequans GM02S module
*/
std::unique_ptr<DCE> create_SQNGM02S_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif);
/**
* @brief Create generic DCE
*/

View File

@ -118,6 +118,7 @@ enum class ModemType {
SIM7000, /*!< Derived from the GenericModule, specifics applied to SIM7000 model */
BG96, /*!< Derived from the GenericModule, specifics applied to BG69 model */
SIM800, /*!< Derived from the GenericModule with specifics applied to SIM800 model */
SQNGM02S, /*!< Derived from the GenericModule, specifics applied to GM02S model */
};
/**
@ -178,6 +179,8 @@ public:
return build_shared_module<SIM7000>(cfg, std::forward<Args>(args)...);
case ModemType::BG96:
return build_shared_module<BG96>(cfg, std::forward<Args>(args)...);
case ModemType::SQNGM02S:
return build_shared_module<SQNGM02S>(cfg, std::forward<Args>(args)...);
case ModemType::GenericModule:
return build_shared_module<GenericModule>(cfg, std::forward<Args>(args)...);
default:
@ -207,6 +210,8 @@ public:
return build_unique<SIM7000>(cfg, std::forward<Args>(args)...);
case ModemType::BG96:
return build_unique<BG96>(cfg, std::forward<Args>(args)...);
case ModemType::SQNGM02S:
return build_unique<SQNGM02S>(cfg, std::forward<Args>(args)...);
case ModemType::GenericModule:
return build_unique<GenericModule>(cfg, std::forward<Args>(args)...);
default:
@ -229,6 +234,8 @@ public:
return build<SIM7000>(cfg, std::forward<Args>(args)...);
case ModemType::BG96:
return build<BG96>(cfg, std::forward<Args>(args)...);
case ModemType::SQNGM02S:
return build<SQNGM02S>(cfg, std::forward<Args>(args)...);
case ModemType::GenericModule:
return build<GenericModule>(cfg, std::forward<Args>(args)...);
default:

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -115,7 +115,7 @@ public:
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, num, ...) \
virtual return_type name(__VA_ARGS__);
DECLARE_ALL_COMMAND_APIS(virtual return_type name(...); )
DECLARE_ALL_COMMAND_APIS(virtual return_type name(...);)
#undef ESP_MODEM_DECLARE_DCE_COMMAND
@ -177,6 +177,14 @@ public:
command_result set_pdp_context(PdpContext &pdp) override;
};
class SQNGM02S : public GenericModule {
using GenericModule::GenericModule;
public:
command_result connect(PdpContext &pdp);
bool setup_data_mode() override;
};
/**
* @}
*/

View File

@ -60,6 +60,7 @@ typedef enum esp_modem_dce_device {
ESP_MODEM_DCE_BG96,
ESP_MODEM_DCE_EC20,
ESP_MODEM_DCE_SIM800,
ESP_MODEM_DCE_SQNGM02S,
ESP_MODEM_DCE_CUSTOM
} esp_modem_dce_device_t;
@ -162,6 +163,8 @@ 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
esp_err_t esp_modem_sqn_gm02s_connect(esp_modem_dce_t *dce, const esp_modem_PdpContext_t *pdp_context);
/**
* @brief This API provides support for temporarily pausing networking in order
* to send/receive AT commands and resume networking afterwards.

View File

@ -43,6 +43,8 @@ inline dce_factory::ModemType convert_modem_enum(esp_modem_dce_device_t module)
return esp_modem::dce_factory::ModemType::BG96;
case ESP_MODEM_DCE_SIM800:
return esp_modem::dce_factory::ModemType::SIM800;
case ESP_MODEM_DCE_SQNGM02S:
return esp_modem::dce_factory::ModemType::SQNGM02S;
default:
case ESP_MODEM_DCE_GENERIC:
return esp_modem::dce_factory::ModemType::GenericModule;

View File

@ -290,7 +290,69 @@ ESP_MODEM_DECLARE_DCE_COMMAND(set_gnss_power_mode, command_result, 1, INT_IN(p1,
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(get_gnss_power_mode, command_result, 1, INT_OUT(p1, mode)) \
\
/**
* @brief Configure PSM
* @param[in] mode psm mode (0 - off, 1 - on, 2 - off & discard stored params)
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(config_psm, command_result, 3, INT_IN(p1, mode), STRING_IN(p2, tau), STRING_IN(p3, active_time)) \
\
/**
* @brief Configure CEREG urc
* @param[in] value
* value = 0 - Disable network URC
* value = 1 - Enable network URC
* value = 2 - Enable network URC with location information
* value = 3 - Enable network URC with location information and EMM cause
* value = 4 - Enable network URC with location information and PSM value
* value = 5 - Enable network URC with location information and PSM value, EMM cause
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(config_network_registration_urc, command_result, 1, INT_IN(p1, value)) \
\
/**
* @brief Gets the current network registration state
* @param[out] state The current network registration state
* state = 0 - Not registered, MT is not currently searching an operator to register to
* state = 1 - Registered, home network
* state = 2 - Not registered, but MT is currently trying to attach or searching an operator to register to
* state = 3 - Registration denied
* state = 4 - Unknown
* state = 5 - Registered, Roaming
* state = 6 - Registered, for SMS only, home network (NB-IoT only)
* state = 7 - Registered, for SMS only, roaming (NB-IoT only)
* state = 8 - Attached for emergency bearer services only
* state = 9 - Registered for CSFB not preferred, home network
* state = 10 - Registered for CSFB not preferred, roaming
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(get_network_registration_state, command_result, 1, INT_OUT(p1,state)) \
\
/**
* @brief Configures the mobile termination error (+CME ERROR)
* @param[in] mode The form of the final result code
* mode = 0 - Disable, use and send ERROR instead
* mode = 1 - Enable, use numeric error values
* mode = 2 - Enable, result code and use verbose error values
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(config_mobile_termination_error, command_result, 1, INT_IN(p1, mode)) \
\
/**
* @brief Configure eDRX
* @param[in] mode
* mode = 0 - Disable
* mode = 1 - Enable
* mode = 2 - Enable + URC
* mode = 3 - Disable + Reset parameter.
* @param[in] access_technology
* act = 0 - ACT is not using eDRX (used in URC)
* act = 1 - EC-GSM-IoT (A/Gb mode)
* act = 2 - GSM (A/Gb mode)
* act = 3 - UTRAN (Iu mode)
* act = 4 - E-UTRAN (WB-S1 mode)
* act = 5 - E-UTRAN (NB-S1 mode)
* @param[in] edrx_value nible string containing encoded eDRX time
* @param[in] ptw_value nible string containing encoded Paging Time Window
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(config_edrx, command_result, 3, INT_IN(p1, mode), INT_IN(p2, access_technology), STRING_IN(p3, edrx_value)) \
#ifdef GENERATE_DOCS
// cat ../include/generate/esp_modem_command_declare.inc | clang++ -E -P -CC -xc++ -I../include -DGENERATE_DOCS - | sed -n '1,/DCE command documentation/!p'

View File

@ -66,6 +66,11 @@ std::unique_ptr<DCE> create_BG96_dce(const dce_config *config, std::shared_ptr<D
return create_modem_dce(dce_factory::ModemType::BG96, config, std::move(dte), netif);
}
std::unique_ptr<DCE> create_SQNGM02S_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif)
{
return create_modem_dce(dce_factory::ModemType::SQNGM02S, config, std::move(dte), netif);
}
std::unique_ptr<DCE> create_generic_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif)
{
return create_modem_dce(dce_factory::ModemType::GenericModule, config, std::move(dte), netif);

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -439,6 +439,77 @@ extern "C" esp_err_t esp_modem_get_gnss_power_mode(esp_modem_dce_t *dce_wrap, in
return ret;
}
extern "C" esp_err_t esp_modem_config_psm(esp_modem_dce_t *dce_wrap, int mode, const char *tau, const char *active_time)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr || mode > 3) {
return ESP_ERR_INVALID_ARG;
}
if (mode == 1 && (strlen(tau) != 8 || strlen(active_time) != 8)) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->config_psm(mode, std::string(tau), std::string(active_time)));
}
extern "C" esp_err_t esp_modem_config_network_registration_urc(esp_modem_dce_t *dce_wrap, int value)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr || value > 5) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->config_network_registration_urc(value));
}
extern "C" esp_err_t esp_modem_get_network_registration_state(esp_modem_dce_t *dce_wrap, int *p_state)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr || p_state == nullptr) {
return ESP_ERR_INVALID_ARG;
}
int state;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_network_registration_state(state));
if (ret == ESP_OK) {
*p_state = state;
}
return ret;
}
extern "C" esp_err_t esp_modem_config_mobile_termination_error(esp_modem_dce_t *dce_wrap, int mode)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr || mode > 2) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->config_mobile_termination_error(mode));
}
extern "C" esp_err_t esp_modem_config_edrx(esp_modem_dce_t *dce_wrap, int mode, int access_technology, const char *edrx_value)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr || mode > 3 || access_technology > 5) {
return ESP_ERR_INVALID_ARG;
}
if ((mode == 1 || mode == 2) && strlen(edrx_value) != 4) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->config_edrx(mode, access_technology, std::string(edrx_value)));
}
extern "C" esp_err_t esp_modem_sqn_gm02s_connect(esp_modem_dce_t *dce_wrap, const esp_modem_PdpContext_t *pdp_context)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
esp_modem::PdpContext pdp{pdp_context->apn};
pdp.context_id = pdp_context->context_id;
pdp.protocol_type = pdp_context->protocol_type;
return command_response_to_esp_err(static_cast<SQNGM02S *>(dce_wrap->dce->get_module())->connect(pdp));
}
extern "C" esp_err_t esp_modem_reset(esp_modem_dce_t *dce_wrap)
{
return command_response_to_esp_err(dce_wrap->dce->reset());

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -12,14 +12,16 @@
#include "cxx_include/esp_modem_command_library.hpp"
#include "cxx_include/esp_modem_command_library_utils.hpp"
#include "cxx17_include/esp_modem_command_library_17.hpp"
namespace esp_modem::dce_commands {
static const char *TAG = "command_lib";
static command_result generic_command(CommandableIf *t, const std::string &command,
const std::list<std::string_view> &pass_phrase,
const std::list<std::string_view> &fail_phrase,
uint32_t timeout_ms)
command_result generic_command(CommandableIf *t, const std::string &command,
const std::list<std::string_view> &pass_phrase,
const std::list<std::string_view> &fail_phrase,
uint32_t timeout_ms)
{
ESP_LOGD(TAG, "%s command %s\n", __func__, command.c_str());
return t->command(command, [&](uint8_t *data, size_t len) {
@ -45,7 +47,7 @@ command_result generic_command(CommandableIf *t, const std::string &command,
const std::string &pass_phrase,
const std::string &fail_phrase, uint32_t timeout_ms)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
const auto pass = std::list<std::string_view>({pass_phrase});
const auto fail = std::list<std::string_view>({fail_phrase});
return generic_command(t, command, pass, fail, timeout_ms);
@ -81,7 +83,7 @@ bool set(std::span<char> &dest, std::string_view &src)
template <typename T> command_result generic_get_string(CommandableIf *t, const std::string &command, T &output, uint32_t timeout_ms)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return t->command(command, [&](uint8_t *data, size_t len) {
size_t pos = 0;
std::string_view response((char *)data, len);
@ -110,67 +112,67 @@ template <typename T> command_result generic_get_string(CommandableIf *t, const
command_result generic_command_common(CommandableIf *t, const std::string &command, uint32_t timeout_ms)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return generic_command(t, command, "OK", "ERROR", timeout_ms);
}
command_result sync(CommandableIf *t)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return generic_command_common(t, "AT\r");
}
command_result store_profile(CommandableIf *t)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return generic_command_common(t, "AT&W\r");
}
command_result power_down(CommandableIf *t)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return generic_command(t, "AT+QPOWD=1\r", "POWERED DOWN", "ERROR", 1000);
}
command_result power_down_sim76xx(CommandableIf *t)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return generic_command_common(t, "AT+CPOF\r", 1000);
}
command_result power_down_sim70xx(CommandableIf *t)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return generic_command(t, "AT+CPOWD=1\r", "POWER DOWN", "ERROR", 1000);
}
command_result power_down_sim8xx(CommandableIf *t)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return generic_command(t, "AT+CPOWD=1\r", "POWER DOWN", "ERROR", 1000);
}
command_result reset(CommandableIf *t)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return generic_command(t, "AT+CRESET\r", "PB DONE", "ERROR", 60000);
}
command_result set_baud(CommandableIf *t, int baud)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return generic_command_common(t, "AT+IPR=" + std::to_string(baud) + "\r");
}
command_result hang_up(CommandableIf *t)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return generic_command_common(t, "ATH\r", 90000);
}
command_result get_battery_status(CommandableIf *t, int &voltage, int &bcs, int &bcl)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
std::string out;
auto ret = generic_get_string(t, "AT+CBC\r", out);
if (ret != command_result::OK) {
@ -206,7 +208,7 @@ command_result get_battery_status(CommandableIf *t, int &voltage, int &bcs, int
command_result get_battery_status_sim7xxx(CommandableIf *t, int &voltage, int &bcs, int &bcl)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
std::string out;
auto ret = generic_get_string(t, "AT+CBC\r", out);
if (ret != command_result::OK) {
@ -235,13 +237,13 @@ command_result get_battery_status_sim7xxx(CommandableIf *t, int &voltage, int &b
command_result set_flow_control(CommandableIf *t, int dce_flow, int dte_flow)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return generic_command_common(t, "AT+IFC=" + std::to_string(dce_flow) + "," + std::to_string(dte_flow) + "\r");
}
command_result get_operator_name(CommandableIf *t, std::string &operator_name, int &act)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
std::string out;
auto ret = generic_get_string(t, "AT+COPS?\r", out, 75000);
if (ret != command_result::OK) {
@ -272,7 +274,7 @@ command_result get_operator_name(CommandableIf *t, std::string &operator_name, i
command_result set_echo(CommandableIf *t, bool on)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
if (on) {
return generic_command_common(t, "ATE1\r");
}
@ -281,7 +283,7 @@ command_result set_echo(CommandableIf *t, bool on)
command_result set_pdp_context(CommandableIf *t, PdpContext &pdp, uint32_t timeout_ms)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
std::string pdp_command = "AT+CGDCONT=" + std::to_string(pdp.context_id) +
",\"" + pdp.protocol_type + "\",\"" + pdp.apn + "\"\r";
return generic_command_common(t, pdp_command, timeout_ms);
@ -294,25 +296,25 @@ command_result set_pdp_context(CommandableIf *t, PdpContext &pdp)
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);
}
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);
}
command_result resume_data_mode(CommandableIf *t)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return generic_command(t, "ATO\r", "CONNECT", "ERROR", 5000);
}
command_result set_command_mode(CommandableIf *t)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
const auto pass = std::list<std::string_view>({"NO CARRIER", "OK"});
const auto fail = std::list<std::string_view>({"ERROR"});
return generic_command(t, "+++", pass, fail, 5000);
@ -320,25 +322,25 @@ command_result set_command_mode(CommandableIf *t)
command_result get_imsi(CommandableIf *t, std::string &imsi_number)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return generic_get_string(t, "AT+CIMI\r", imsi_number, 5000);
}
command_result get_imei(CommandableIf *t, std::string &out)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return generic_get_string(t, "AT+CGSN\r", out, 5000);
}
command_result get_module_name(CommandableIf *t, std::string &out)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return generic_get_string(t, "AT+CGMM\r", out, 5000);
}
command_result sms_txt_mode(CommandableIf *t, bool txt = true)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
if (txt) {
return generic_command_common(t, "AT+CMGF=1\r"); // Text mode (default)
}
@ -348,13 +350,13 @@ command_result sms_txt_mode(CommandableIf *t, bool txt = true)
command_result sms_character_set(CommandableIf *t)
{
// Sets the default GSM character set
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return generic_command_common(t, "AT+CSCS=\"GSM\"\r");
}
command_result send_sms(CommandableIf *t, const std::string &number, const std::string &message)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
auto ret = t->command("AT+CMGS=\"" + number + "\"\r", [&](uint8_t *data, size_t len) {
std::string_view response((char *)data, len);
ESP_LOGD(TAG, "Send SMS response %.*s", static_cast<int>(response.size()), response.data());
@ -372,13 +374,13 @@ command_result send_sms(CommandableIf *t, const std::string &number, const std::
command_result set_cmux(CommandableIf *t)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return generic_command_common(t, "AT+CMUX=0\r");
}
command_result read_pin(CommandableIf *t, bool &pin_ok)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
std::string out;
auto ret = generic_get_string(t, "AT+CPIN?\r", out);
if (ret != command_result::OK) {
@ -400,21 +402,21 @@ command_result read_pin(CommandableIf *t, bool &pin_ok)
command_result set_pin(CommandableIf *t, const std::string &pin)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
std::string set_pin_command = "AT+CPIN=" + pin + "\r";
return generic_command_common(t, set_pin_command);
}
command_result at(CommandableIf *t, const std::string &cmd, std::string &out, int timeout = 500)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
std::string at_command = cmd + "\r";
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__ );
ESP_LOGV(TAG, "%s", __func__);
return t->command(cmd, [&](uint8_t *data, size_t len) {
out.assign(reinterpret_cast<char *>(data), len);
@ -430,7 +432,7 @@ command_result at_raw(CommandableIf *t, const std::string &cmd, std::string &out
command_result get_signal_quality(CommandableIf *t, int &rssi, int &ber)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
std::string out;
auto ret = generic_get_string(t, "AT+CSQ\r", out);
if (ret != command_result::OK) {
@ -456,19 +458,19 @@ command_result get_signal_quality(CommandableIf *t, int &rssi, int &ber)
command_result set_operator(CommandableIf *t, int mode, int format, const std::string &oper)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return generic_command_common(t, "AT+COPS=" + std::to_string(mode) + "," + std::to_string(format) + ",\"" + oper + "\"\r", 90000);
}
command_result set_network_attachment_state(CommandableIf *t, int state)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return generic_command_common(t, "AT+CGATT=" + std::to_string(state) + "\r");
}
command_result get_network_attachment_state(CommandableIf *t, int &state)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
std::string out;
auto ret = generic_get_string(t, "AT+CGATT?\r", out);
if (ret != command_result::OK) {
@ -489,13 +491,13 @@ command_result get_network_attachment_state(CommandableIf *t, int &state)
command_result set_radio_state(CommandableIf *t, int state)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return generic_command_common(t, "AT+CFUN=" + std::to_string(state) + "\r", 15000);
}
command_result get_radio_state(CommandableIf *t, int &state)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
std::string out;
auto ret = generic_get_string(t, "AT+CFUN?\r", out);
if (ret != command_result::OK) {
@ -516,19 +518,19 @@ command_result get_radio_state(CommandableIf *t, int &state)
command_result set_network_mode(CommandableIf *t, int mode)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return generic_command_common(t, "AT+CNMP=" + std::to_string(mode) + "\r");
}
command_result set_preferred_mode(CommandableIf *t, int mode)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return generic_command_common(t, "AT+CMNB=" + std::to_string(mode) + "\r");
}
command_result set_network_bands(CommandableIf *t, const std::string &mode, const int *bands, int size)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
std::string band_string = "";
for (int i = 0; i < size - 1; ++i) {
band_string += std::to_string(bands[i]) + ",";
@ -542,7 +544,7 @@ command_result set_network_bands(CommandableIf *t, const std::string &mode, cons
// any_mode = "0xFFFFFFFF7FFFFFFF";
command_result set_network_bands_sim76xx(CommandableIf *t, const std::string &mode, const int *bands, int size)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
static const char *hexDigits = "0123456789ABCDEF";
uint64_t band_bits = 0;
int hex_len = 16;
@ -560,7 +562,7 @@ command_result set_network_bands_sim76xx(CommandableIf *t, const std::string &mo
command_result get_network_system_mode(CommandableIf *t, int &mode)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
std::string out;
auto ret = generic_get_string(t, "AT+CNSMOD?\r", out);
if (ret != command_result::OK) {
@ -582,13 +584,13 @@ command_result get_network_system_mode(CommandableIf *t, int &mode)
command_result set_gnss_power_mode(CommandableIf *t, int mode)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return generic_command_common(t, "AT+CGNSPWR=" + std::to_string(mode) + "\r");
}
command_result get_gnss_power_mode(CommandableIf *t, int &mode)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
std::string out;
auto ret = generic_get_string(t, "AT+CGNSPWR?\r", out);
if (ret != command_result::OK) {
@ -607,9 +609,70 @@ command_result get_gnss_power_mode(CommandableIf *t, int &mode)
return command_result::OK;
}
command_result config_psm(CommandableIf *t, int enabled, const std::string &TAU, const std::string &activeTime)
{
ESP_LOGV(TAG, "%s", __func__);
if (enabled == true) {
return generic_command_common(t, "AT+CPSMS=1,,,\"" + TAU + "\"" + ",\"" + activeTime + "\"\r", 5000);
}
return generic_command_common(t, "AT+CPSMS=" + std::to_string(enabled) + "\r", 5000);
}
command_result config_network_registration_urc(CommandableIf *t, int value)
{
ESP_LOGV(TAG, "%s", __func__);
return generic_command_common(t, "AT+CEREG=" + std::to_string(value) + "\r", 500);
}
command_result get_network_registration_state(CommandableIf *t, int &state)
{
ESP_LOGV(TAG, "%s", __func__);
std::string out;
auto ret = generic_get_string(t, "AT+CEREG?\r", out, 500);
if (ret != command_result::OK) {
return ret;
}
constexpr std::string_view pattern = "+CEREG: ";
int state_pos_start;
int state_pos_end;
if (out.find(pattern) == std::string::npos || (state_pos_start = out.find(',')) == std::string::npos) {
return command_result::FAIL;
}
if (out.find(pattern) == std::string::npos || (state_pos_end = out.find(',', state_pos_start)) == std::string::npos) {
if (std::from_chars(out.data() + state_pos_start, out.data() + out.size(), state).ec == std::errc::invalid_argument) {
return command_result::FAIL;
}
} else if (std::from_chars(out.data() + state_pos_start, out.data() + state_pos_end, state).ec == std::errc::invalid_argument) {
return command_result::FAIL;
}
return command_result::OK;
}
command_result config_mobile_termination_error(CommandableIf *t, int value)
{
ESP_LOGV(TAG, "%s", __func__);
return generic_command_common(t, "AT+CMEE=" + std::to_string(value) + "\r");
}
command_result config_edrx(CommandableIf *t, int mode, int access_technology, const std::string &edrx_value)
{
if (mode == 1 || mode == 2) {
return dce_commands::generic_command_common(t,
"AT+CEDRXS=" +
std::to_string(mode) +
"," +
std::to_string(access_technology) +
",\"" +
edrx_value + "\"\r");
}
return dce_commands::generic_command_common(t, "AT+SQNEDRX=" + std::to_string(mode), 500);
}
command_result set_gnss_power_mode_sim76xx(CommandableIf *t, int mode)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return generic_command_common(t, "AT+CGPS=" + std::to_string(mode) + "\r");
}

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -7,6 +7,7 @@
#include "cxx_include/esp_modem_api.hpp"
#include "cxx_include/esp_modem_dce_module.hpp"
#include "generate/esp_modem_command_declare.inc"
#include "cxx17_include/esp_modem_command_library_17.hpp"
namespace esp_modem {
@ -35,7 +36,7 @@ GenericModule::GenericModule(std::shared_ptr<DTE> dte, const dce_config *config)
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, arg_nr, ...) \
return_type GenericModule::name(__VA_ARGS__) { return esp_modem::dce_commands::name(dte.get() ARGS(arg_nr) ); }
DECLARE_ALL_COMMAND_APIS(return_type name(...) )
DECLARE_ALL_COMMAND_APIS(return_type name(...))
#undef ESP_MODEM_DECLARE_DCE_COMMAND
@ -87,4 +88,41 @@ command_result BG96::set_pdp_context(esp_modem::PdpContext &pdp)
return dce_commands::set_pdp_context(dte.get(), pdp, 300);
}
bool SQNGM02S::setup_data_mode()
{
return true;
}
command_result SQNGM02S::connect(PdpContext &pdp)
{
command_result res;
configure_pdp_context(std::make_unique<PdpContext>(pdp));
set_pdp_context(*this->pdp);
res = config_network_registration_urc(1);
if (res != command_result::OK) {
return res;
}
res = set_radio_state(1);
if (res != command_result::OK) {
return res;
}
//wait for +CEREG: 5 or +CEREG: 1.
const auto pass = std::list<std::string_view>({"+CEREG: 1", "+CEREG: 5"});
const auto fail = std::list<std::string_view>({"ERROR"});
res = esp_modem::dce_commands::generic_command(dte.get(), "", pass, fail, 1200000);
if (res != command_result::OK) {
config_network_registration_urc(0);
return res;
}
res = config_network_registration_urc(0);
if (res != command_result::OK) {
return res;
}
return command_result::OK;
}
}