diff --git a/components/esp_modem/include/cxx_include/esp_modem_api.hpp b/components/esp_modem/include/cxx_include/esp_modem_api.hpp index 690d7b324..33a76ef01 100644 --- a/components/esp_modem/include/cxx_include/esp_modem_api.hpp +++ b/components/esp_modem/include/cxx_include/esp_modem_api.hpp @@ -84,7 +84,6 @@ std::unique_ptr create_SIM7070_dce(const dce_config *config, std::shared_pt */ std::unique_ptr create_SIM7000_dce(const dce_config *config, std::shared_ptr dte, esp_netif_t *netif); - /** * @brief Create DCE based on SIM800 module */ @@ -95,6 +94,11 @@ std::unique_ptr create_SIM800_dce(const dce_config *config, std::shared_ptr */ std::unique_ptr create_BG96_dce(const dce_config *config, std::shared_ptr dte, esp_netif_t *netif); +/** + * @brief Create DCE based on Sequans GM02S module + */ +std::unique_ptr create_SQNGM02S_dce(const dce_config *config, std::shared_ptr dte, esp_netif_t *netif); + /** * @brief Create generic DCE */ diff --git a/components/esp_modem/include/cxx_include/esp_modem_dce_factory.hpp b/components/esp_modem/include/cxx_include/esp_modem_dce_factory.hpp index b3cac79da..faa4b179e 100644 --- a/components/esp_modem/include/cxx_include/esp_modem_dce_factory.hpp +++ b/components/esp_modem/include/cxx_include/esp_modem_dce_factory.hpp @@ -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(cfg, std::forward(args)...); case ModemType::BG96: return build_shared_module(cfg, std::forward(args)...); + case ModemType::SQNGM02S: + return build_shared_module(cfg, std::forward(args)...); case ModemType::GenericModule: return build_shared_module(cfg, std::forward(args)...); default: @@ -207,6 +210,8 @@ public: return build_unique(cfg, std::forward(args)...); case ModemType::BG96: return build_unique(cfg, std::forward(args)...); + case ModemType::SQNGM02S: + return build_unique(cfg, std::forward(args)...); case ModemType::GenericModule: return build_unique(cfg, std::forward(args)...); default: @@ -229,6 +234,8 @@ public: return build(cfg, std::forward(args)...); case ModemType::BG96: return build(cfg, std::forward(args)...); + case ModemType::SQNGM02S: + return build(cfg, std::forward(args)...); case ModemType::GenericModule: return build(cfg, std::forward(args)...); default: 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 ad32bd601..46fc356d2 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 @@ -177,6 +177,15 @@ 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; +}; + /** * @} */ 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 b82e929b6..9b454f155 100644 --- a/components/esp_modem/include/esp_modem_c_api_types.h +++ b/components/esp_modem/include/esp_modem_c_api_types.h @@ -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; 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 0f7b122f3..a158b3395 100644 --- a/components/esp_modem/include/generate/esp_modem_command_declare.inc +++ b/components/esp_modem/include/generate/esp_modem_command_declare.inc @@ -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), STRING_IN(p4, ptw_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' diff --git a/components/esp_modem/src/esp_modem_api.cpp b/components/esp_modem/src/esp_modem_api.cpp index 832774d0a..9cc9d511f 100644 --- a/components/esp_modem/src/esp_modem_api.cpp +++ b/components/esp_modem/src/esp_modem_api.cpp @@ -66,6 +66,11 @@ std::unique_ptr create_BG96_dce(const dce_config *config, std::shared_ptr create_SQNGM02S_dce(const dce_config *config, std::shared_ptr dte, esp_netif_t *netif) +{ + return create_modem_dce(dce_factory::ModemType::SQNGM02S, config, std::move(dte), netif); +} + std::unique_ptr create_generic_dce(const dce_config *config, std::shared_ptr dte, esp_netif_t *netif) { return create_modem_dce(dce_factory::ModemType::GenericModule, config, std::move(dte), netif); diff --git a/components/esp_modem/src/esp_modem_c_api.cpp b/components/esp_modem/src/esp_modem_c_api.cpp index e9698995e..1d4d5ac3b 100644 --- a/components/esp_modem/src/esp_modem_c_api.cpp +++ b/components/esp_modem/src/esp_modem_c_api.cpp @@ -439,6 +439,74 @@ 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->configure_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, int value, const char *access_technology) +{ + +} + +extern "C" esp_err_t esp_modem_sqns_gm02s_connect(esp_modem_dce_t *dce, const esp_modem_PdpContext_t *pdp_context) +{ + if (dce_wrap == nullptr || dce_wrap->dce == nullptr || dce_wrap->modem_type != ESP_MODEM_DCE_SQNGM02S) + { + 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(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()); diff --git a/components/esp_modem/src/esp_modem_command_library.cpp b/components/esp_modem/src/esp_modem_command_library.cpp index 6f06bb7bd..c48ff60bf 100644 --- a/components/esp_modem/src/esp_modem_command_library.cpp +++ b/components/esp_modem/src/esp_modem_command_library.cpp @@ -607,6 +607,71 @@ 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", 500); + } + return generic_command_common(t, "AT+CPSMS=" + std::to_string(enabled), 500); +} + +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(state) + "\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(int mode, int access_technology, const std::string &edrx_value, const std::string &ptw_value) +{ + if (mode == 1 || enabled == 2) + { + return dce_commands::generic_command_common(dte.get(), + "AT+SQNEDRX=" + + std::to_string(mode) + + "," + + std::to_string(access_technology) + + ",\"" + + edrx_value + + "\",\"" + + ptw_value + "\"\r"); + } + return dce_commands::generic_command_common(dte.get(), "AT+SQNEDRX=" + std::to_string(mode), 500); +} command_result set_gnss_power_mode_sim76xx(CommandableIf *t, int mode) { 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 563403494..033f9c433 100644 --- a/components/esp_modem/src/esp_modem_modules.cpp +++ b/components/esp_modem/src/esp_modem_modules.cpp @@ -87,4 +87,59 @@ command_result BG96::set_pdp_context(esp_modem::PdpContext &pdp) return dce_commands::set_pdp_context(dte.get(), pdp, 300); } +command_result urc_callback(uint8_t *line, size_t len) +{ + ESP_LOGI("WalterModem", "Received: %s", line); + if (strstr((const char *)line, "+CEREG: 1")) + { + return command_result::OK; // Succesfully registered + } + else if (strstr((const char *)line, "+CEREG: 5")) + { + return command_result::OK; // Succesfully registered + } + else if (strstr((const char *)line, "+CEREG: 3")) + { + return command_result::FAIL; // Permission denied + } + return command_result::TIMEOUT; +} + +command_result SQNGM02S::connect(PdpContext &pdp) +{ + command_result res; + res = set_pdp_context(pdp); + if (res != command_result::OK) + return res; + 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. + + do + { + res = dte->command("", urc_callback, 20000, '\r'); + } while (res == command_result::TIMEOUT); + + if (res != command_result::OK) + return res; + + res = config_network_registration_urc(0); + if (res != command_result::OK) + return res; + + return command_result::OK; +} + +bool SQNGM02S::setup_data_mode() +{ + ESP_LOGI("SQNGM02S", "setyp data mode"); + return set_echo(false) == command_result::OK; + // PDP context has already been set before hand because SEQUANS modem must have already been connected/registered before setting up cmux +} }