Skip to content

Commit 1b4baae

Browse files
authored
Merge pull request #786 from robbedptechnics/support-sqn-gm02s
feat(module): add support for sequans GM02S modem (DPTechnics) (IDFGH-14891)
2 parents e9d7350 + 74b7d85 commit 1b4baae

11 files changed

+339
-59
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
#include <string>
7+
#include <list>
8+
#include "esp_log.h"
9+
#include "cxx_include/esp_modem_dte.hpp"
10+
#include "cxx_include/esp_modem_dce_module.hpp"
11+
12+
namespace esp_modem::dce_commands {
13+
command_result generic_command(CommandableIf *t, const std::string &command,
14+
const std::list<std::string_view> &pass_phrase,
15+
const std::list<std::string_view> &fail_phrase,
16+
uint32_t timeout_ms);
17+
}

components/esp_modem/include/cxx_include/esp_modem_api.hpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ std::unique_ptr<DCE> create_SIM7070_dce(const dce_config *config, std::shared_pt
8484
*/
8585
std::unique_ptr<DCE> create_SIM7000_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif);
8686

87-
8887
/**
8988
* @brief Create DCE based on SIM800 module
9089
*/
@@ -95,6 +94,11 @@ std::unique_ptr<DCE> create_SIM800_dce(const dce_config *config, std::shared_ptr
9594
*/
9695
std::unique_ptr<DCE> create_BG96_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif);
9796

97+
/**
98+
* @brief Create DCE based on Sequans GM02S module
99+
*/
100+
std::unique_ptr<DCE> create_SQNGM02S_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif);
101+
98102
/**
99103
* @brief Create generic DCE
100104
*/

components/esp_modem/include/cxx_include/esp_modem_dce_factory.hpp

+7
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ enum class ModemType {
118118
SIM7000, /*!< Derived from the GenericModule, specifics applied to SIM7000 model */
119119
BG96, /*!< Derived from the GenericModule, specifics applied to BG69 model */
120120
SIM800, /*!< Derived from the GenericModule with specifics applied to SIM800 model */
121+
SQNGM02S, /*!< Derived from the GenericModule, specifics applied to GM02S model */
121122
};
122123

123124
/**
@@ -178,6 +179,8 @@ class Factory {
178179
return build_shared_module<SIM7000>(cfg, std::forward<Args>(args)...);
179180
case ModemType::BG96:
180181
return build_shared_module<BG96>(cfg, std::forward<Args>(args)...);
182+
case ModemType::SQNGM02S:
183+
return build_shared_module<SQNGM02S>(cfg, std::forward<Args>(args)...);
181184
case ModemType::GenericModule:
182185
return build_shared_module<GenericModule>(cfg, std::forward<Args>(args)...);
183186
default:
@@ -207,6 +210,8 @@ class Factory {
207210
return build_unique<SIM7000>(cfg, std::forward<Args>(args)...);
208211
case ModemType::BG96:
209212
return build_unique<BG96>(cfg, std::forward<Args>(args)...);
213+
case ModemType::SQNGM02S:
214+
return build_unique<SQNGM02S>(cfg, std::forward<Args>(args)...);
210215
case ModemType::GenericModule:
211216
return build_unique<GenericModule>(cfg, std::forward<Args>(args)...);
212217
default:
@@ -229,6 +234,8 @@ class Factory {
229234
return build<SIM7000>(cfg, std::forward<Args>(args)...);
230235
case ModemType::BG96:
231236
return build<BG96>(cfg, std::forward<Args>(args)...);
237+
case ModemType::SQNGM02S:
238+
return build<SQNGM02S>(cfg, std::forward<Args>(args)...);
232239
case ModemType::GenericModule:
233240
return build<GenericModule>(cfg, std::forward<Args>(args)...);
234241
default:

components/esp_modem/include/cxx_include/esp_modem_dce_module.hpp

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -115,7 +115,7 @@ class GenericModule: public ModuleIf {
115115
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, num, ...) \
116116
virtual return_type name(__VA_ARGS__);
117117

118-
DECLARE_ALL_COMMAND_APIS(virtual return_type name(...); )
118+
DECLARE_ALL_COMMAND_APIS(virtual return_type name(...);)
119119

120120
#undef ESP_MODEM_DECLARE_DCE_COMMAND
121121

@@ -177,6 +177,14 @@ class BG96: public GenericModule {
177177
command_result set_pdp_context(PdpContext &pdp) override;
178178
};
179179

180+
class SQNGM02S : public GenericModule {
181+
using GenericModule::GenericModule;
182+
183+
public:
184+
command_result connect(PdpContext &pdp);
185+
bool setup_data_mode() override;
186+
};
187+
180188
/**
181189
* @}
182190
*/

components/esp_modem/include/esp_modem_c_api_types.h

+3
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ typedef enum esp_modem_dce_device {
6060
ESP_MODEM_DCE_BG96,
6161
ESP_MODEM_DCE_EC20,
6262
ESP_MODEM_DCE_SIM800,
63+
ESP_MODEM_DCE_SQNGM02S,
6364
ESP_MODEM_DCE_CUSTOM
6465
} esp_modem_dce_device_t;
6566

@@ -162,6 +163,8 @@ esp_err_t esp_modem_set_apn(esp_modem_dce_t *dce, const char *apn);
162163
esp_err_t esp_modem_set_urc(esp_modem_dce_t *dce, esp_err_t(*got_line_cb)(uint8_t *data, size_t len));
163164
#endif
164165

166+
esp_err_t esp_modem_sqn_gm02s_connect(esp_modem_dce_t *dce, const esp_modem_PdpContext_t *pdp_context);
167+
165168
/**
166169
* @brief This API provides support for temporarily pausing networking in order
167170
* to send/receive AT commands and resume networking afterwards.

components/esp_modem/include/esp_private/c_api_wrapper.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ inline dce_factory::ModemType convert_modem_enum(esp_modem_dce_device_t module)
4343
return esp_modem::dce_factory::ModemType::BG96;
4444
case ESP_MODEM_DCE_SIM800:
4545
return esp_modem::dce_factory::ModemType::SIM800;
46+
case ESP_MODEM_DCE_SQNGM02S:
47+
return esp_modem::dce_factory::ModemType::SQNGM02S;
4648
default:
4749
case ESP_MODEM_DCE_GENERIC:
4850
return esp_modem::dce_factory::ModemType::GenericModule;

components/esp_modem/include/generate/esp_modem_command_declare.inc

+63-1
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,69 @@ ESP_MODEM_DECLARE_DCE_COMMAND(set_gnss_power_mode, command_result, 1, INT_IN(p1,
290290
*/ \
291291
ESP_MODEM_DECLARE_DCE_COMMAND(get_gnss_power_mode, command_result, 1, INT_OUT(p1, mode)) \
292292
\
293-
293+
/**
294+
* @brief Configure PSM
295+
* @param[in] mode psm mode (0 - off, 1 - on, 2 - off & discard stored params)
296+
* @return OK, FAIL or TIMEOUT
297+
*/ \
298+
ESP_MODEM_DECLARE_DCE_COMMAND(config_psm, command_result, 3, INT_IN(p1, mode), STRING_IN(p2, tau), STRING_IN(p3, active_time)) \
299+
\
300+
/**
301+
* @brief Configure CEREG urc
302+
* @param[in] value
303+
* value = 0 - Disable network URC
304+
* value = 1 - Enable network URC
305+
* value = 2 - Enable network URC with location information
306+
* value = 3 - Enable network URC with location information and EMM cause
307+
* value = 4 - Enable network URC with location information and PSM value
308+
* value = 5 - Enable network URC with location information and PSM value, EMM cause
309+
*/ \
310+
ESP_MODEM_DECLARE_DCE_COMMAND(config_network_registration_urc, command_result, 1, INT_IN(p1, value)) \
311+
\
312+
/**
313+
* @brief Gets the current network registration state
314+
* @param[out] state The current network registration state
315+
* state = 0 - Not registered, MT is not currently searching an operator to register to
316+
* state = 1 - Registered, home network
317+
* state = 2 - Not registered, but MT is currently trying to attach or searching an operator to register to
318+
* state = 3 - Registration denied
319+
* state = 4 - Unknown
320+
* state = 5 - Registered, Roaming
321+
* state = 6 - Registered, for SMS only, home network (NB-IoT only)
322+
* state = 7 - Registered, for SMS only, roaming (NB-IoT only)
323+
* state = 8 - Attached for emergency bearer services only
324+
* state = 9 - Registered for CSFB not preferred, home network
325+
* state = 10 - Registered for CSFB not preferred, roaming
326+
*/ \
327+
ESP_MODEM_DECLARE_DCE_COMMAND(get_network_registration_state, command_result, 1, INT_OUT(p1,state)) \
328+
\
329+
/**
330+
* @brief Configures the mobile termination error (+CME ERROR)
331+
* @param[in] mode The form of the final result code
332+
* mode = 0 - Disable, use and send ERROR instead
333+
* mode = 1 - Enable, use numeric error values
334+
* mode = 2 - Enable, result code and use verbose error values
335+
*/ \
336+
ESP_MODEM_DECLARE_DCE_COMMAND(config_mobile_termination_error, command_result, 1, INT_IN(p1, mode)) \
337+
\
338+
/**
339+
* @brief Configure eDRX
340+
* @param[in] mode
341+
* mode = 0 - Disable
342+
* mode = 1 - Enable
343+
* mode = 2 - Enable + URC
344+
* mode = 3 - Disable + Reset parameter.
345+
* @param[in] access_technology
346+
* act = 0 - ACT is not using eDRX (used in URC)
347+
* act = 1 - EC-GSM-IoT (A/Gb mode)
348+
* act = 2 - GSM (A/Gb mode)
349+
* act = 3 - UTRAN (Iu mode)
350+
* act = 4 - E-UTRAN (WB-S1 mode)
351+
* act = 5 - E-UTRAN (NB-S1 mode)
352+
* @param[in] edrx_value nible string containing encoded eDRX time
353+
* @param[in] ptw_value nible string containing encoded Paging Time Window
354+
*/ \
355+
ESP_MODEM_DECLARE_DCE_COMMAND(config_edrx, command_result, 3, INT_IN(p1, mode), INT_IN(p2, access_technology), STRING_IN(p3, edrx_value)) \
294356

295357
#ifdef GENERATE_DOCS
296358
// cat ../include/generate/esp_modem_command_declare.inc | clang++ -E -P -CC -xc++ -I../include -DGENERATE_DOCS - | sed -n '1,/DCE command documentation/!p'

components/esp_modem/src/esp_modem_api.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ std::unique_ptr<DCE> create_BG96_dce(const dce_config *config, std::shared_ptr<D
6666
return create_modem_dce(dce_factory::ModemType::BG96, config, std::move(dte), netif);
6767
}
6868

69+
std::unique_ptr<DCE> create_SQNGM02S_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif)
70+
{
71+
return create_modem_dce(dce_factory::ModemType::SQNGM02S, config, std::move(dte), netif);
72+
}
73+
6974
std::unique_ptr<DCE> create_generic_dce(const dce_config *config, std::shared_ptr<DTE> dte, esp_netif_t *netif)
7075
{
7176
return create_modem_dce(dce_factory::ModemType::GenericModule, config, std::move(dte), netif);

components/esp_modem/src/esp_modem_c_api.cpp

+72-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -439,6 +439,77 @@ extern "C" esp_err_t esp_modem_get_gnss_power_mode(esp_modem_dce_t *dce_wrap, in
439439
return ret;
440440
}
441441

442+
extern "C" esp_err_t esp_modem_config_psm(esp_modem_dce_t *dce_wrap, int mode, const char *tau, const char *active_time)
443+
{
444+
if (dce_wrap == nullptr || dce_wrap->dce == nullptr || mode > 3) {
445+
return ESP_ERR_INVALID_ARG;
446+
}
447+
448+
if (mode == 1 && (strlen(tau) != 8 || strlen(active_time) != 8)) {
449+
return ESP_ERR_INVALID_ARG;
450+
}
451+
452+
return command_response_to_esp_err(dce_wrap->dce->config_psm(mode, std::string(tau), std::string(active_time)));
453+
}
454+
455+
extern "C" esp_err_t esp_modem_config_network_registration_urc(esp_modem_dce_t *dce_wrap, int value)
456+
{
457+
if (dce_wrap == nullptr || dce_wrap->dce == nullptr || value > 5) {
458+
return ESP_ERR_INVALID_ARG;
459+
}
460+
461+
return command_response_to_esp_err(dce_wrap->dce->config_network_registration_urc(value));
462+
}
463+
464+
extern "C" esp_err_t esp_modem_get_network_registration_state(esp_modem_dce_t *dce_wrap, int *p_state)
465+
{
466+
if (dce_wrap == nullptr || dce_wrap->dce == nullptr || p_state == nullptr) {
467+
return ESP_ERR_INVALID_ARG;
468+
}
469+
470+
int state;
471+
auto ret = command_response_to_esp_err(dce_wrap->dce->get_network_registration_state(state));
472+
473+
if (ret == ESP_OK) {
474+
*p_state = state;
475+
}
476+
return ret;
477+
}
478+
479+
extern "C" esp_err_t esp_modem_config_mobile_termination_error(esp_modem_dce_t *dce_wrap, int mode)
480+
{
481+
if (dce_wrap == nullptr || dce_wrap->dce == nullptr || mode > 2) {
482+
return ESP_ERR_INVALID_ARG;
483+
}
484+
485+
return command_response_to_esp_err(dce_wrap->dce->config_mobile_termination_error(mode));
486+
}
487+
488+
extern "C" esp_err_t esp_modem_config_edrx(esp_modem_dce_t *dce_wrap, int mode, int access_technology, const char *edrx_value)
489+
{
490+
if (dce_wrap == nullptr || dce_wrap->dce == nullptr || mode > 3 || access_technology > 5) {
491+
return ESP_ERR_INVALID_ARG;
492+
}
493+
494+
if ((mode == 1 || mode == 2) && strlen(edrx_value) != 4) {
495+
return ESP_ERR_INVALID_ARG;
496+
}
497+
498+
return command_response_to_esp_err(dce_wrap->dce->config_edrx(mode, access_technology, std::string(edrx_value)));
499+
}
500+
501+
extern "C" esp_err_t esp_modem_sqn_gm02s_connect(esp_modem_dce_t *dce_wrap, const esp_modem_PdpContext_t *pdp_context)
502+
{
503+
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
504+
return ESP_ERR_INVALID_ARG;
505+
}
506+
507+
esp_modem::PdpContext pdp{pdp_context->apn};
508+
pdp.context_id = pdp_context->context_id;
509+
pdp.protocol_type = pdp_context->protocol_type;
510+
return command_response_to_esp_err(static_cast<SQNGM02S *>(dce_wrap->dce->get_module())->connect(pdp));
511+
}
512+
442513
extern "C" esp_err_t esp_modem_reset(esp_modem_dce_t *dce_wrap)
443514
{
444515
return command_response_to_esp_err(dce_wrap->dce->reset());

0 commit comments

Comments
 (0)