diff --git a/components/esp_modem/command/include/cxx_include/esp_modem_command_library.hpp b/components/esp_modem/command/include/cxx_include/esp_modem_command_library.hpp index 28d922d68..0ebbb0b11 100644 --- a/components/esp_modem/command/include/cxx_include/esp_modem_command_library.hpp +++ b/components/esp_modem/command/include/cxx_include/esp_modem_command_library.hpp @@ -57,6 +57,13 @@ command_result store_profile(CommandableIf *t); * @return OK, FAIL or TIMEOUT */ command_result set_pin(CommandableIf *t, const std::string &pin); +/** + * @brief Use PUK to set a new SIM PIN (AT+CPIN=,) + * @param[in] puk PUK code + * @param[in] pin New PIN code + * @return OK, FAIL or TIMEOUT + */ +command_result reset_pin(CommandableIf *t, const std::string &puk, const std::string &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 @@ -81,6 +88,12 @@ command_result at(CommandableIf *t, const std::string &cmd, std::string &out, in * @return OK, FAIL or TIMEOUT */ command_result read_pin(CommandableIf *t, bool &pin_ok); +/** + * @brief Reads the SIM PIN status from AT+CPIN? + * @param[out] state CPIN state (READY, NEED_PIN, NEED_PUK, ...) + * @return OK, FAIL or TIMEOUT + */ +command_result read_pin_state(CommandableIf *t, esp_modem::sim_pin_state &state); /** * @brief Sets echo mode * @param[in] echo_on true if echo mode on (repeats the commands) diff --git a/components/esp_modem/command/include/cxx_include/esp_modem_dce_generic.hpp b/components/esp_modem/command/include/cxx_include/esp_modem_dce_generic.hpp index 0b6100813..c70637a15 100644 --- a/components/esp_modem/command/include/cxx_include/esp_modem_dce_generic.hpp +++ b/components/esp_modem/command/include/cxx_include/esp_modem_dce_generic.hpp @@ -60,6 +60,16 @@ public: { return device->set_pin(pin); } + /** + * @brief Use PUK to set a new SIM PIN (AT+CPIN=,) + * @param[in] puk PUK code + * @param[in] pin New PIN code + * @return OK, FAIL or TIMEOUT + */ + command_result reset_pin(const std::string &puk, const std::string &pin) + { + return device->reset_pin(puk, 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 @@ -93,6 +103,15 @@ public: { return device->read_pin(pin_ok); } + /** + * @brief Reads the SIM PIN status from AT+CPIN? + * @param[out] state CPIN state (READY, NEED_PIN, NEED_PUK, ...) + * @return OK, FAIL or TIMEOUT + */ + command_result read_pin_state(esp_modem::sim_pin_state &state) + { + return device->read_pin_state(state); + } /** * @brief Sets echo mode * @param[in] echo_on true if echo mode on (repeats the commands) diff --git a/components/esp_modem/command/include/cxx_include/esp_modem_dce_module.hpp b/components/esp_modem/command/include/cxx_include/esp_modem_dce_module.hpp index c145dc85d..5f498f68e 100644 --- a/components/esp_modem/command/include/cxx_include/esp_modem_dce_module.hpp +++ b/components/esp_modem/command/include/cxx_include/esp_modem_dce_module.hpp @@ -128,6 +128,13 @@ public: * @return OK, FAIL or TIMEOUT */ virtual command_result set_pin(const std::string &pin); + /** + * @brief Use PUK to set a new SIM PIN (AT+CPIN=,) + * @param[in] puk PUK code + * @param[in] pin New PIN code + * @return OK, FAIL or TIMEOUT + */ + virtual command_result reset_pin(const std::string &puk, const std::string &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 @@ -152,6 +159,12 @@ public: * @return OK, FAIL or TIMEOUT */ virtual command_result read_pin(bool &pin_ok); + /** + * @brief Reads the SIM PIN status from AT+CPIN? + * @param[out] state CPIN state (READY, NEED_PIN, NEED_PUK, ...) + * @return OK, FAIL or TIMEOUT + */ + virtual command_result read_pin_state(esp_modem::sim_pin_state &state); /** * @brief Sets echo mode * @param[in] echo_on true if echo mode on (repeats the commands) diff --git a/components/esp_modem/command/include/esp_modem_api.h b/components/esp_modem/command/include/esp_modem_api.h index 5ec951fd8..1f0eb3178 100644 --- a/components/esp_modem/command/include/esp_modem_api.h +++ b/components/esp_modem/command/include/esp_modem_api.h @@ -37,6 +37,13 @@ esp_err_t esp_modem_store_profile(esp_modem_dce_t *dce); * @return OK, FAIL or TIMEOUT */ esp_err_t esp_modem_set_pin(esp_modem_dce_t *dce, const char *pin); +/** + * @brief Use PUK to set a new SIM PIN (AT+CPIN=,) + * @param[in] puk PUK code + * @param[in] pin New PIN code + * @return OK, FAIL or TIMEOUT + */ +esp_err_t esp_modem_reset_pin(esp_modem_dce_t *dce, const char *puk, const char *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 @@ -61,6 +68,12 @@ esp_err_t esp_modem_at(esp_modem_dce_t *dce, const char *cmd, char *out, int tim * @return OK, FAIL or TIMEOUT */ esp_err_t esp_modem_read_pin(esp_modem_dce_t *dce, bool *pin_ok); +/** + * @brief Reads the SIM PIN status from AT+CPIN? + * @param[out] state CPIN state (READY, NEED_PIN, NEED_PUK, ...) + * @return OK, FAIL or TIMEOUT + */ +esp_err_t esp_modem_read_pin_state(esp_modem_dce_t *dce, esp_modem_sim_pin_state_t *state); /** * @brief Sets echo mode * @param[in] echo_on true if echo mode on (repeats the commands) diff --git a/components/esp_modem/command/src/esp_modem_modules.cpp b/components/esp_modem/command/src/esp_modem_modules.cpp index 2bc39f9b2..3d1e43f59 100644 --- a/components/esp_modem/command/src/esp_modem_modules.cpp +++ b/components/esp_modem/command/src/esp_modem_modules.cpp @@ -47,6 +47,16 @@ command_result GenericModule::set_pin(const std::string &pin) { return esp_modem::dce_commands::set_pin(dte.get(), pin); } +/** + * @brief Use PUK to set a new SIM PIN (AT+CPIN=,) + * @param[in] puk PUK code + * @param[in] pin New PIN code + * @return OK, FAIL or TIMEOUT + */ +command_result GenericModule::reset_pin(const std::string &puk, const std::string &pin) +{ + return esp_modem::dce_commands::reset_pin(dte.get(), puk, 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 @@ -80,6 +90,15 @@ command_result GenericModule::read_pin(bool &pin_ok) { return esp_modem::dce_commands::read_pin(dte.get(), pin_ok); } +/** + * @brief Reads the SIM PIN status from AT+CPIN? + * @param[out] state CPIN state (READY, NEED_PIN, NEED_PUK, ...) + * @return OK, FAIL or TIMEOUT + */ +command_result GenericModule::read_pin_state(esp_modem::sim_pin_state &state) +{ + return esp_modem::dce_commands::read_pin_state(dte.get(), state); +} /** * @brief Sets echo mode * @param[in] echo_on true if echo mode on (repeats the commands) diff --git a/components/esp_modem/examples/modem_console/main/command/my_module_dce.cpp b/components/esp_modem/examples/modem_console/main/command/my_module_dce.cpp index 9af8603ef..886f9660a 100644 --- a/components/esp_modem/examples/modem_console/main/command/my_module_dce.cpp +++ b/components/esp_modem/examples/modem_console/main/command/my_module_dce.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -61,6 +61,16 @@ command_result Shiny::DCE::set_pin(const std::string &pin) { return esp_modem::dce_commands::set_pin(this, pin); } +/** + * @brief Use PUK to set a new SIM PIN (AT+CPIN=,) + * @param[in] puk PUK code + * @param[in] pin New PIN code + * @return OK, FAIL or TIMEOUT + */ +command_result Shiny::DCE::reset_pin(const std::string &puk, const std::string &pin) +{ + return esp_modem::dce_commands::reset_pin(this, puk, 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 @@ -94,6 +104,15 @@ command_result Shiny::DCE::read_pin(bool &pin_ok) { return esp_modem::dce_commands::read_pin(this, pin_ok); } +/** + * @brief Reads the SIM PIN status from AT+CPIN? + * @param[out] state CPIN state (READY, NEED_PIN, NEED_PUK, ...) + * @return OK, FAIL or TIMEOUT + */ +command_result Shiny::DCE::read_pin_state(esp_modem::sim_pin_state &state) +{ + return esp_modem::dce_commands::read_pin_state(this, state); +} /** * @brief Sets echo mode * @param[in] echo_on true if echo mode on (repeats the commands) diff --git a/components/esp_modem/examples/modem_console/main/command/my_module_dce.hpp b/components/esp_modem/examples/modem_console/main/command/my_module_dce.hpp index 9a1da23b9..5b4a64c99 100644 --- a/components/esp_modem/examples/modem_console/main/command/my_module_dce.hpp +++ b/components/esp_modem/examples/modem_console/main/command/my_module_dce.hpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -68,6 +68,13 @@ public: * @return OK, FAIL or TIMEOUT */ esp_modem::command_result set_pin(const std::string &pin); + /** + * @brief Use PUK to set a new SIM PIN (AT+CPIN=,) + * @param[in] puk PUK code + * @param[in] pin New PIN code + * @return OK, FAIL or TIMEOUT + */ + esp_modem::command_result reset_pin(const std::string &puk, const std::string &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 @@ -92,6 +99,12 @@ public: * @return OK, FAIL or TIMEOUT */ esp_modem::command_result read_pin(bool &pin_ok); + /** + * @brief Reads the SIM PIN status from AT+CPIN? + * @param[out] state CPIN state (READY, NEED_PIN, NEED_PUK, ...) + * @return OK, FAIL or TIMEOUT + */ + esp_modem::command_result read_pin_state(esp_modem::sim_pin_state &state); /** * @brief Sets echo mode * @param[in] echo_on true if echo mode on (repeats the commands) diff --git a/components/esp_modem/examples/modem_console/main/generate/my_module_dce.cpp b/components/esp_modem/examples/modem_console/main/generate/my_module_dce.cpp index 3665ddffb..3b9be082b 100644 --- a/components/esp_modem/examples/modem_console/main/generate/my_module_dce.cpp +++ b/components/esp_modem/examples/modem_console/main/generate/my_module_dce.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ diff --git a/components/esp_modem/examples/modem_console/main/generate/my_module_dce.hpp b/components/esp_modem/examples/modem_console/main/generate/my_module_dce.hpp index 78bafc3bb..75f2ffa9a 100644 --- a/components/esp_modem/examples/modem_console/main/generate/my_module_dce.hpp +++ b/components/esp_modem/examples/modem_console/main/generate/my_module_dce.hpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ diff --git a/components/esp_modem/examples/simple_cmux_client/components/SIM7070_gnss/command/SIM7070_gnss.hpp b/components/esp_modem/examples/simple_cmux_client/components/SIM7070_gnss/command/SIM7070_gnss.hpp index 48380ff4d..bf5300b2f 100644 --- a/components/esp_modem/examples/simple_cmux_client/components/SIM7070_gnss/command/SIM7070_gnss.hpp +++ b/components/esp_modem/examples/simple_cmux_client/components/SIM7070_gnss/command/SIM7070_gnss.hpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -73,6 +73,16 @@ public: { return device->set_pin(pin); } + /** + * @brief Use PUK to set a new SIM PIN (AT+CPIN=,) + * @param[in] puk PUK code + * @param[in] pin New PIN code + * @return OK, FAIL or TIMEOUT + */ + esp_modem::command_result reset_pin(const std::string &puk, const std::string &pin) + { + return device->reset_pin(puk, 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 @@ -106,6 +116,15 @@ public: { return device->read_pin(pin_ok); } + /** + * @brief Reads the SIM PIN status from AT+CPIN? + * @param[out] state CPIN state (READY, NEED_PIN, NEED_PUK, ...) + * @return OK, FAIL or TIMEOUT + */ + esp_modem::command_result read_pin_state(esp_modem::sim_pin_state &state) + { + return device->read_pin_state(state); + } /** * @brief Sets echo mode * @param[in] echo_on true if echo mode on (repeats the commands) diff --git a/components/esp_modem/examples/simple_cmux_client/components/SIM7070_gnss/generate/SIM7070_gnss.hpp b/components/esp_modem/examples/simple_cmux_client/components/SIM7070_gnss/generate/SIM7070_gnss.hpp index 78abcf2e1..a772ffed7 100644 --- a/components/esp_modem/examples/simple_cmux_client/components/SIM7070_gnss/generate/SIM7070_gnss.hpp +++ b/components/esp_modem/examples/simple_cmux_client/components/SIM7070_gnss/generate/SIM7070_gnss.hpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/components/esp_modem/generate/include/esp_modem_command_declare.inc b/components/esp_modem/generate/include/esp_modem_command_declare.inc index 922b0e67a..de618e83f 100644 --- a/components/esp_modem/generate/include/esp_modem_command_declare.inc +++ b/components/esp_modem/generate/include/esp_modem_command_declare.inc @@ -25,6 +25,14 @@ ESP_MODEM_DECLARE_DCE_COMMAND(store_profile, command_result) */ ESP_MODEM_DECLARE_DCE_COMMAND(set_pin, command_result, STR_IN(pin)) +/** + * @brief Use PUK to set a new SIM PIN (AT+CPIN=,) + * @param[in] puk PUK code + * @param[in] pin New PIN code + * @return OK, FAIL or TIMEOUT + */ +ESP_MODEM_DECLARE_DCE_COMMAND(reset_pin, command_result, STR_IN(puk), STR_IN(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 @@ -52,6 +60,13 @@ ESP_MODEM_DECLARE_DCE_COMMAND(at, command_result, STR_IN(cmd), STR_OUT(out), INT */ ESP_MODEM_DECLARE_DCE_COMMAND(read_pin, command_result, BOOL_OUT(pin_ok)) +/** + * @brief Reads the SIM PIN status from AT+CPIN? + * @param[out] state CPIN state (READY, NEED_PIN, NEED_PUK, ...) + * @return OK, FAIL or TIMEOUT + */ +ESP_MODEM_DECLARE_DCE_COMMAND(read_pin_state, command_result, ENUM_OUT(sim_pin_state, state)) + /** * @brief Sets echo mode * @param[in] echo_on true if echo mode on (repeats the commands) diff --git a/components/esp_modem/generate/include/esp_modem_command_declare_helper.inc b/components/esp_modem/generate/include/esp_modem_command_declare_helper.inc index dbd6c0fe8..31264bc47 100644 --- a/components/esp_modem/generate/include/esp_modem_command_declare_helper.inc +++ b/components/esp_modem/generate/include/esp_modem_command_declare_helper.inc @@ -13,6 +13,7 @@ #define PARAM_INT_IN(name) int name #define PARAM_BOOL_IN(name) const bool name #define PARAM_BOOL_OUT(name) bool& name +#define PARAM_ENUM_OUT(enum_name, name) esp_modem::enum_name& name #define PARAM_STRUCT_OUT(struct_name, name) struct_name& name #define PARAM_INT_LIST_IN(name) const int* name #else @@ -22,6 +23,7 @@ #define PARAM_INT_IN(name) int name #define PARAM_BOOL_IN(name) const bool name #define PARAM_BOOL_OUT(name) bool* name +#define PARAM_ENUM_OUT(enum_name, name) esp_modem_ ## enum_name ## _t* name #define PARAM_STRUCT_OUT(struct_name, name) esp_modem_ ## struct_name ## _t* name #define PARAM_INT_LIST_IN(name) const int* name #endif @@ -32,6 +34,7 @@ #define FORWARD_INT_IN(name) name #define FORWARD_BOOL_IN(name) name #define FORWARD_BOOL_OUT(name) name +#define FORWARD_ENUM_OUT(enum_name, name) name #define FORWARD_STRUCT_OUT(struct_name, name) name #define FORWARD_INT_LIST_IN(name) name diff --git a/components/esp_modem/include/cxx_include/esp_modem_types.hpp b/components/esp_modem/include/cxx_include/esp_modem_types.hpp index 1387ee33b..9c50eb6ba 100644 --- a/components/esp_modem/include/cxx_include/esp_modem_types.hpp +++ b/components/esp_modem/include/cxx_include/esp_modem_types.hpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -59,6 +59,17 @@ enum class command_result { TIMEOUT /*!< The device didn't respond in the specified timeline */ }; +/** + * @brief SIM PIN status reported by AT+CPIN? + */ +enum class sim_pin_state { + UNKNOWN, /*!< Response could not be parsed */ + READY, /*!< SIM is unlocked */ + NEED_PIN, /*!< SIM PIN (or PIN2) required */ + NEED_PUK, /*!< SIM PUK (or PUK2) required */ + OTHER, /*!< Other CPIN state (e.g. PH-SIM PIN) */ +}; + typedef std::function got_line_cb; /** 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 c74076418..1f2b04126 100644 --- a/components/esp_modem/include/esp_modem_c_api_types.h +++ b/components/esp_modem/include/esp_modem_c_api_types.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -25,6 +25,17 @@ typedef struct esp_modem_PdpContext_t { const char *apn; } esp_modem_PdpContext_t; +/** + * @brief SIM PIN status reported by AT+CPIN? + */ +typedef enum esp_modem_sim_pin_state { + ESP_MODEM_SIM_PIN_STATE_UNKNOWN = 0, + ESP_MODEM_SIM_PIN_STATE_READY, + ESP_MODEM_SIM_PIN_STATE_NEED_PIN, + ESP_MODEM_SIM_PIN_STATE_NEED_PUK, + ESP_MODEM_SIM_PIN_STATE_OTHER, +} esp_modem_sim_pin_state_t; + /** * @defgroup ESP_MODEM_C_API ESP_MODEM C API * @brief Set of basic C API for ESP-MODEM diff --git a/components/esp_modem/src/esp_modem_c_api.cpp b/components/esp_modem/src/esp_modem_c_api.cpp index 869a17c6c..9e4a14941 100644 --- a/components/esp_modem/src/esp_modem_c_api.cpp +++ b/components/esp_modem/src/esp_modem_c_api.cpp @@ -209,6 +209,27 @@ extern "C" esp_err_t esp_modem_set_pin(esp_modem_dce_t *dce_wrap, const char *pi return command_response_to_esp_err(dce_wrap->dce->set_pin(pin_str)); } +extern "C" esp_err_t esp_modem_reset_pin(esp_modem_dce_t *dce_wrap, const char *puk, const char *pin) +{ + if (dce_wrap == nullptr || dce_wrap->dce == nullptr || puk == nullptr || pin == nullptr) { + return ESP_ERR_INVALID_ARG; + } + std::string puk_str(puk); + std::string pin_str(pin); + return command_response_to_esp_err(dce_wrap->dce->reset_pin(puk_str, pin_str)); +} + +extern "C" esp_err_t esp_modem_read_pin_state(esp_modem_dce_t *dce_wrap, esp_modem_sim_pin_state_t *state) +{ + if (dce_wrap == nullptr || dce_wrap->dce == nullptr || state == nullptr) { + return ESP_ERR_INVALID_ARG; + } + sim_pin_state cpp_state = sim_pin_state::UNKNOWN; + auto ret = command_response_to_esp_err(dce_wrap->dce->read_pin_state(cpp_state)); + *state = static_cast(cpp_state); + return ret; +} + extern "C" esp_err_t esp_modem_at(esp_modem_dce_t *dce_wrap, const char *at, char *p_out, int timeout) { if (dce_wrap == nullptr || dce_wrap->dce == nullptr || at == nullptr) { diff --git a/components/esp_modem/src/esp_modem_command_library.cpp b/components/esp_modem/src/esp_modem_command_library.cpp index 0d8a8b129..ed1249537 100644 --- a/components/esp_modem/src/esp_modem_command_library.cpp +++ b/components/esp_modem/src/esp_modem_command_library.cpp @@ -380,26 +380,58 @@ command_result set_cmux(CommandableIf *t) return generic_command_common(t, "AT+CMUX=0\r"); } -command_result read_pin(CommandableIf *t, bool &pin_ok) +static sim_pin_state parse_cpin_response(const std::string &out) +{ + if (out.find("+CPIN:") == std::string::npos) { + return sim_pin_state::UNKNOWN; + } + if (out.find("READY") != std::string::npos) { + return sim_pin_state::READY; + } + if (out.find("SIM PUK2") != std::string::npos || out.find("SIM PUK") != std::string::npos) { + return sim_pin_state::NEED_PUK; + } + if (out.find("SIM PIN2") != std::string::npos || out.find("SIM PIN") != std::string::npos) { + return sim_pin_state::NEED_PIN; + } + return sim_pin_state::OTHER; +} + +command_result read_pin_state(CommandableIf *t, sim_pin_state &state) { ESP_LOGV(TAG, "%s", __func__); std::string out; auto ret = generic_get_string(t, "AT+CPIN?\r", out); if (ret != command_result::OK) { + state = sim_pin_state::UNKNOWN; return ret; } - if (out.find("+CPIN:") == std::string::npos) { + state = parse_cpin_response(out); + if (state == sim_pin_state::UNKNOWN) { return command_result::FAIL; } - if (out.find("SIM PIN") != std::string::npos || out.find("SIM PUK") != std::string::npos) { - pin_ok = false; - return command_result::OK; + return command_result::OK; +} + +command_result read_pin(CommandableIf *t, bool &pin_ok) +{ + ESP_LOGV(TAG, "%s", __func__); + sim_pin_state state; + auto ret = read_pin_state(t, state); + if (ret != command_result::OK) { + return ret; } - if (out.find("READY") != std::string::npos) { + switch (state) { + case sim_pin_state::READY: pin_ok = true; return command_result::OK; + case sim_pin_state::NEED_PIN: + case sim_pin_state::NEED_PUK: + pin_ok = false; + return command_result::OK; + default: + return command_result::FAIL; } - return command_result::FAIL; // Neither pin-ok, nor waiting for pin/puk -> mark as error } command_result set_pin(CommandableIf *t, const std::string &pin) @@ -409,6 +441,13 @@ command_result set_pin(CommandableIf *t, const std::string &pin) return generic_command_common(t, set_pin_command); } +command_result reset_pin(CommandableIf *t, const std::string &puk, const std::string &pin) +{ + ESP_LOGV(TAG, "%s", __func__); + std::string reset_pin_command = "AT+CPIN=" + puk + "," + pin + "\r"; + return generic_command_common(t, reset_pin_command); +} + command_result at(CommandableIf *t, const std::string &cmd, std::string &out, int timeout = 500) { ESP_LOGV(TAG, "%s", __func__); diff --git a/components/esp_modem/test/host_test/main/LoopbackTerm.cpp b/components/esp_modem/test/host_test/main/LoopbackTerm.cpp index 935c634fa..01344a71f 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-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -26,7 +26,7 @@ int LoopbackTerm::write(uint8_t *data, size_t len) async_results.push_back(std::move(ret)); return len; } - if (len > 2 && (data[len - 1] == '\r' || data[len - 1] == '+') ) { // Simple AT responder + if (len > 2 && (data[len - 1] == '\r' || data[len - 1] == '+')) { // Simple AT responder std::string command((char *)data, len); std::string response; if (command == "+++") { @@ -46,11 +46,29 @@ int LoopbackTerm::write(uint8_t *data, size_t len) } else if (command.find("AT+CBC\r") != std::string::npos) { response = is_bg96 ? "+CBC: 1,20,123456\r\r\n\r\nOK\r\n\n\r\n" : "+CBC: 123.456V\r\r\n\r\nOK\r\n\n\r\n"; - } else if (command.find("AT+CPIN=1234\r") != std::string::npos) { - response = "OK\r\n"; - pin_ok = true; + } else if (command.find("AT+CPIN=") != std::string::npos) { + if (command.find(',') != std::string::npos) { + response = "OK\r\n"; + pin_ok = true; + needs_puk = false; + } else if (command.find("AT+CPIN=1234\r") != std::string::npos) { + response = "OK\r\n"; + pin_ok = true; + needs_puk = false; + } else if (needs_puk) { + response = "ERROR\r\n"; + } else { + response = "OK\r\n"; + pin_ok = true; + } } else if (command.find("AT+CPIN?\r") != std::string::npos) { - response = pin_ok ? "+CPIN: READY\r\nOK\r\n" : "+CPIN: SIM PIN\r\nOK\r\n"; + if (pin_ok) { + response = "+CPIN: READY\r\nOK\r\n"; + } else if (needs_puk) { + response = "+CPIN: SIM PUK\r\nOK\r\n"; + } else { + response = "+CPIN: SIM PIN\r\nOK\r\n"; + } } else if (command.find("AT") != std::string::npos) { if (command.length() > 4) { response = command; @@ -108,12 +126,12 @@ int LoopbackTerm::read(uint8_t *data, size_t len) return read_len; } -LoopbackTerm::LoopbackTerm(bool is_bg96): loopback_data(), data_len(0), pin_ok(false), is_bg96(is_bg96), inject_by(0) +LoopbackTerm::LoopbackTerm(bool is_bg96): loopback_data(), data_len(0), pin_ok(false), needs_puk(false), is_bg96(is_bg96), inject_by(0) { init_signal(); } -LoopbackTerm::LoopbackTerm(): loopback_data(), data_len(0), pin_ok(false), is_bg96(false), inject_by(0) +LoopbackTerm::LoopbackTerm(): loopback_data(), data_len(0), pin_ok(false), needs_puk(false), is_bg96(false), inject_by(0) { init_signal(); } diff --git a/components/esp_modem/test/host_test/main/LoopbackTerm.h b/components/esp_modem/test/host_test/main/LoopbackTerm.h index 23482f0ea..155b2cf43 100644 --- a/components/esp_modem/test/host_test/main/LoopbackTerm.h +++ b/components/esp_modem/test/host_test/main/LoopbackTerm.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -33,6 +33,12 @@ public: void set_read_cb(std::function f) override; + void set_sim_puk_locked(bool locked) + { + needs_puk = locked; + pin_ok = false; + } + private: enum class status_t { STARTED, @@ -46,6 +52,7 @@ private: std::vector loopback_data; size_t data_len; bool pin_ok; + bool needs_puk; bool is_bg96; size_t inject_by; size_t delay_before_inject; diff --git a/components/esp_modem/test/host_test/main/test_modem.cpp b/components/esp_modem/test/host_test/main/test_modem.cpp index 7f15e94dc..22057dcee 100644 --- a/components/esp_modem/test/host_test/main/test_modem.cpp +++ b/components/esp_modem/test/host_test/main/test_modem.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -110,6 +110,29 @@ TEST_CASE("DCE AT parser", "[esp_modem]") CHECK(act == 5); } +TEST_CASE("SIM PUK recovery", "[esp_modem]") +{ + auto term = std::make_unique(true); + term->set_sim_puk_locked(true); + auto dte = std::make_shared(std::move(term)); + esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("APN"); + esp_netif_t netif{}; + auto dce = create_BG96_dce(&dce_config, dte, &netif); + CHECK(dce != nullptr); + + sim_pin_state state; + CHECK(dce->read_pin_state(state) == command_result::OK); + CHECK(state == sim_pin_state::NEED_PUK); + + bool pin_ok = true; + CHECK(dce->read_pin(pin_ok) == command_result::OK); + CHECK(pin_ok == false); + + CHECK(dce->reset_pin("87654321", "1234") == command_result::OK); + CHECK(dce->read_pin_state(state) == command_result::OK); + CHECK(state == sim_pin_state::READY); +} + TEST_CASE("DTE send/receive command", "[esp_modem]") {