From ac5d43882b99d7fa8e833772f6bb9add762b2db2 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Tue, 25 Oct 2022 15:41:48 +0200 Subject: [PATCH 1/2] feat(esp-modem): Add support for manual CMUX operations Closes https://github.com/espressif/esp-protocols/issues/168 --- .../modem_console/main/modem_console_main.cpp | 12 +- .../include/cxx_include/esp_modem_dte.hpp | 4 +- .../include/cxx_include/esp_modem_types.hpp | 7 +- .../esp_modem/include/esp_modem_c_api_types.h | 5 + components/esp_modem/src/esp_modem_c_api.cpp | 25 +++- components/esp_modem/src/esp_modem_dce.cpp | 138 ++++++++++++------ components/esp_modem/src/esp_modem_dte.cpp | 77 ++++++---- 7 files changed, 183 insertions(+), 85 deletions(-) diff --git a/components/esp_modem/examples/modem_console/main/modem_console_main.cpp b/components/esp_modem/examples/modem_console/main/modem_console_main.cpp index 795a365248..b5e5b7de5d 100644 --- a/components/esp_modem/examples/modem_console/main/modem_console_main.cpp +++ b/components/esp_modem/examples/modem_console/main/modem_console_main.cpp @@ -210,7 +210,17 @@ extern "C" void app_main(void) if (c->get_count_of(&SetModeArgs::mode)) { auto mode = c->get_string_of(&SetModeArgs::mode); modem_mode dev_mode; - if (mode == "CMD") { + if (mode == "CMUX1") { + dev_mode = esp_modem::modem_mode::CMUX_MANUAL_MODE; + } else if (mode == "CMUX2") { + dev_mode = esp_modem::modem_mode::CMUX_MANUAL_EXIT; + } else if (mode == "CMUX3") { + dev_mode = esp_modem::modem_mode::CMUX_MANUAL_SWAP; + } else if (mode == "CMUX4") { + dev_mode = esp_modem::modem_mode::CMUX_MANUAL_DATA; + } else if (mode == "CMUX5") { + dev_mode = esp_modem::modem_mode::CMUX_MANUAL_COMMAND; + } else if (mode == "CMD") { dev_mode = esp_modem::modem_mode::COMMAND_MODE; } else if (mode == "PPP") { dev_mode = esp_modem::modem_mode::DATA_MODE; diff --git a/components/esp_modem/include/cxx_include/esp_modem_dte.hpp b/components/esp_modem/include/cxx_include/esp_modem_dte.hpp index eba9797f59..902f46fa29 100644 --- a/components/esp_modem/include/cxx_include/esp_modem_dte.hpp +++ b/components/esp_modem/include/cxx_include/esp_modem_dte.hpp @@ -116,8 +116,8 @@ class DTE : public CommandableIf { Lock internal_lock{}; /*!< Locks DTE operations */ unique_buffer buffer; /*!< DTE buffer */ std::shared_ptr cmux_term; /*!< Primary terminal for this DTE */ - std::shared_ptr command_term; /*!< Reference to the terminal used for sending commands */ - std::shared_ptr data_term; /*!< Secondary terminal for this DTE */ + std::shared_ptr term; /*!< Reference to the primary terminal (mostly for sending commands) */ + std::shared_ptr other_term; /*!< Secondary terminal for this DTE */ modem_mode mode; /*!< DTE operation mode */ SignalGroup signal; /*!< Event group used to signal request-response operations */ command_result result; /*!< Command result of the currently exectuted command */ 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 e9be501a55..8cb88ba588 100644 --- a/components/esp_modem/include/cxx_include/esp_modem_types.hpp +++ b/components/esp_modem/include/cxx_include/esp_modem_types.hpp @@ -29,8 +29,13 @@ enum class modem_mode { UNDEF, COMMAND_MODE, /*!< Command mode -- the modem is supposed to send AT commands in this mode */ DATA_MODE, /*!< Data mode -- the modem communicates with network interface on PPP protocol */ - CMUX_MODE /*!< CMUX (Multiplex mode) -- Simplified CMUX mode, which creates two virtual terminals, + CMUX_MODE, /*!< CMUX (Multiplex mode) -- Simplified CMUX mode, which creates two virtual terminals, * assigning one solely to command interface and the other to the data mode */ + CMUX_MANUAL_MODE, /*!< Enter CMUX mode manually -- just creates two virtual terminals */ + CMUX_MANUAL_EXIT, /*!< Exits CMUX mode manually -- just destroys two virtual terminals */ + CMUX_MANUAL_DATA, /*!< Sets the primary terminal to DATA mode in manual CMUX */ + CMUX_MANUAL_COMMAND, /*!< Sets the primary terminal to COMMAND mode in manual CMUX */ + CMUX_MANUAL_SWAP, /*!< Swaps virtual terminals in manual CMUX mode (primary <-> secondary) */ }; /** 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 b0388076b6..66591df435 100644 --- a/components/esp_modem/include/esp_modem_c_api_types.h +++ b/components/esp_modem/include/esp_modem_c_api_types.h @@ -36,6 +36,11 @@ typedef enum esp_modem_dce_mode { ESP_MODEM_MODE_COMMAND, /**< Default mode after modem startup, used for sending AT commands */ ESP_MODEM_MODE_DATA, /**< Used for switching to PPP mode for the modem to connect to a network */ ESP_MODEM_MODE_CMUX, /**< Multiplexed terminal mode */ + ESP_MODEM_MODE_CMUX_MANUAL, /**< CMUX manual mode */ + ESP_MODEM_MODE_CMUX_MANUAL_EXIT, /**< Exit CMUX manual mode */ + ESP_MODEM_MODE_CMUX_MANUAL_SWAP, /**< Swap terminals in CMUX manual mode */ + ESP_MODEM_MODE_CMUX_MANUAL_DATA, /**< Set DATA mode in CMUX manual mode */ + ESP_MODEM_MODE_CMUX_MANUAL_COMMAND, /**< Set COMMAND mode in CMUX manual mode */ } esp_modem_dce_mode_t; /** diff --git a/components/esp_modem/src/esp_modem_c_api.cpp b/components/esp_modem/src/esp_modem_c_api.cpp index d17a9c78ba..c9ebdda2e6 100644 --- a/components/esp_modem/src/esp_modem_c_api.cpp +++ b/components/esp_modem/src/esp_modem_c_api.cpp @@ -93,14 +93,23 @@ extern "C" esp_err_t esp_modem_set_mode(esp_modem_dce_t *dce_wrap, esp_modem_dce if (dce_wrap == nullptr || dce_wrap->dce == nullptr) { return ESP_ERR_INVALID_ARG; } - if (mode == ESP_MODEM_MODE_DATA) { - return dce_wrap->dce->set_mode(modem_mode::DATA_MODE) ? ESP_OK : ESP_FAIL; - } - if (mode == ESP_MODEM_MODE_COMMAND) { - return dce_wrap->dce->set_mode(modem_mode::COMMAND_MODE) ? ESP_OK : ESP_FAIL; - } - if (mode == ESP_MODEM_MODE_CMUX) { - return dce_wrap->dce->set_mode(modem_mode::CMUX_MODE) ? ESP_OK : ESP_FAIL; + switch (mode) { + case ESP_MODEM_MODE_DATA: + return dce_wrap->dce->set_mode(modem_mode::DATA_MODE) ? ESP_OK : ESP_FAIL; + case ESP_MODEM_MODE_COMMAND: + return dce_wrap->dce->set_mode(modem_mode::COMMAND_MODE) ? ESP_OK : ESP_FAIL; + case ESP_MODEM_MODE_CMUX: + return dce_wrap->dce->set_mode(modem_mode::CMUX_MODE) ? ESP_OK : ESP_FAIL; + case ESP_MODEM_MODE_CMUX_MANUAL: + return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_MODE) ? ESP_OK : ESP_FAIL; + case ESP_MODEM_MODE_CMUX_MANUAL_EXIT: + return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_EXIT) ? ESP_OK : ESP_FAIL; + case ESP_MODEM_MODE_CMUX_MANUAL_SWAP: + return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_SWAP) ? ESP_OK : ESP_FAIL; + case ESP_MODEM_MODE_CMUX_MANUAL_DATA: + return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_DATA) ? ESP_OK : ESP_FAIL; + case ESP_MODEM_MODE_CMUX_MANUAL_COMMAND: + return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_COMMAND) ? ESP_OK : ESP_FAIL; } return ESP_ERR_NOT_SUPPORTED; } diff --git a/components/esp_modem/src/esp_modem_dce.cpp b/components/esp_modem/src/esp_modem_dce.cpp index c82fed0ede..5cd5895e6b 100644 --- a/components/esp_modem/src/esp_modem_dce.cpp +++ b/components/esp_modem/src/esp_modem_dce.cpp @@ -14,6 +14,59 @@ namespace esp_modem { +namespace transitions { + +static bool exit_data(DTE &dte, ModuleIf &device, Netif &netif) +{ + netif.stop(); + auto signal = std::make_shared(); + std::weak_ptr weak_signal = signal; + dte.set_read_cb([weak_signal](uint8_t *data, size_t len) -> bool { + if (memchr(data, '\n', len)) + { + ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data", data, len, ESP_LOG_DEBUG); + const auto pass = std::list({"NO CARRIER", "DISCONNECTED"}); + std::string_view response((char *) data, len); + for (auto &it : pass) + if (response.find(it) != std::string::npos) { + if (auto signal = weak_signal.lock()) { + signal->set(1); + } + return true; + } + } + return false; + }); + netif.wait_until_ppp_exits(); + if (!signal->wait(1, 2000)) { + if (!device.set_mode(modem_mode::COMMAND_MODE)) { + return false; + } + } + dte.set_read_cb(nullptr); + if (!dte.set_mode(modem_mode::COMMAND_MODE)) { + return false; + } + return true; +} + +static bool enter_data(DTE &dte, ModuleIf &device, Netif &netif) +{ + if (!device.setup_data_mode()) { + return false; + } + if (!device.set_mode(modem_mode::DATA_MODE)) { + return false; + } + if (!dte.set_mode(modem_mode::DATA_MODE)) { + return false; + } + netif.start(); + return true; +} + +} // namespace transitions + /** * Set mode while the entire DTE is locked */ @@ -36,8 +89,8 @@ bool DCE_Mode::set_unsafe(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m switch (m) { case modem_mode::UNDEF: break; - case modem_mode::COMMAND_MODE: { - if (mode == modem_mode::COMMAND_MODE) { + case modem_mode::COMMAND_MODE: + if (mode == modem_mode::COMMAND_MODE || mode >= modem_mode::CMUX_MANUAL_MODE) { return false; } if (mode == modem_mode::CMUX_MODE) { @@ -49,59 +102,23 @@ bool DCE_Mode::set_unsafe(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m mode = m; return true; } - netif.stop(); - auto signal = std::make_shared(); - std::weak_ptr weak_signal = signal; - dte->set_read_cb([weak_signal](uint8_t *data, size_t len) -> bool { - if (memchr(data, '\n', len)) - { - ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data", data, len, ESP_LOG_DEBUG); - const auto pass = std::list({"NO CARRIER", "DISCONNECTED"}); - std::string_view response((char *) data, len); - for (auto &it : pass) - if (response.find(it) != std::string::npos) { - if (auto signal = weak_signal.lock()) { - signal->set(1); - } - return true; - } - } - return false; - }); - netif.wait_until_ppp_exits(); - if (!signal->wait(1, 2000)) { - if (!device->set_mode(modem_mode::COMMAND_MODE)) { - mode = modem_mode::UNDEF; - return false; - } - } - dte->set_read_cb(nullptr); - if (!dte->set_mode(modem_mode::COMMAND_MODE)) { + if (!transitions::exit_data(*dte, *device, netif)) { mode = modem_mode::UNDEF; return false; } mode = m; return true; - } - break; case modem_mode::DATA_MODE: - if (mode == modem_mode::DATA_MODE || mode == modem_mode::CMUX_MODE) { - return false; - } - if (!device->setup_data_mode()) { + if (mode == modem_mode::DATA_MODE || mode == modem_mode::CMUX_MODE || mode >= modem_mode::CMUX_MANUAL_MODE) { return false; } - if (!device->set_mode(modem_mode::DATA_MODE)) { + if (!transitions::enter_data(*dte, *device, netif)) { return false; } - if (!dte->set_mode(modem_mode::DATA_MODE)) { - return false; - } - netif.start(); mode = m; return true; case modem_mode::CMUX_MODE: - if (mode == modem_mode::DATA_MODE || mode == modem_mode::CMUX_MODE) { + if (mode == modem_mode::DATA_MODE || mode == modem_mode::CMUX_MODE || mode >= modem_mode::CMUX_MANUAL_MODE) { return false; } device->set_mode(modem_mode::CMUX_MODE); // switch the device into CMUX mode @@ -111,17 +128,46 @@ bool DCE_Mode::set_unsafe(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m return false; } mode = modem_mode::CMUX_MODE; - if (!device->setup_data_mode()) { + return transitions::enter_data(*dte, *device, netif); + case modem_mode::CMUX_MANUAL_MODE: + if (mode != modem_mode::COMMAND_MODE) { + return false; + } + device->set_mode(modem_mode::CMUX_MODE); + usleep(100'000); + + if (!dte->set_mode(m)) { return false; } - if (!device->set_mode(modem_mode::DATA_MODE)) { + mode = modem_mode::CMUX_MANUAL_MODE; + return true; + case modem_mode::CMUX_MANUAL_EXIT: + if (mode != modem_mode::CMUX_MANUAL_MODE) { + return false; + } + if (!dte->set_mode(m)) { + return false; + } + mode = modem_mode::COMMAND_MODE; + return true; + case modem_mode::CMUX_MANUAL_SWAP: + if (mode != modem_mode::CMUX_MANUAL_MODE) { return false; } - if (!dte->set_mode(modem_mode::DATA_MODE)) { + if (!dte->set_mode(m)) { return false; } - netif.start(); return true; + case modem_mode::CMUX_MANUAL_DATA: + if (mode != modem_mode::CMUX_MANUAL_MODE) { + return false; + } + return transitions::enter_data(*dte, *device, netif); + case modem_mode::CMUX_MANUAL_COMMAND: + if (mode != modem_mode::CMUX_MANUAL_MODE) { + return false; + } + return transitions::exit_data(*dte, *device, netif); } return false; } diff --git a/components/esp_modem/src/esp_modem_dte.cpp b/components/esp_modem/src/esp_modem_dte.cpp index 142cae468d..b962b97ada 100644 --- a/components/esp_modem/src/esp_modem_dte.cpp +++ b/components/esp_modem/src/esp_modem_dte.cpp @@ -15,24 +15,24 @@ using namespace esp_modem; static const size_t dte_default_buffer_size = 1000; DTE::DTE(const esp_modem_dte_config *config, std::unique_ptr terminal): - buffer(config->dte_buffer_size), - cmux_term(nullptr), command_term(std::move(terminal)), data_term(command_term), - mode(modem_mode::UNDEF) {} + buffer(config->dte_buffer_size), + cmux_term(nullptr), term(std::move(terminal)), other_term(term), + mode(modem_mode::UNDEF) {} DTE::DTE(std::unique_ptr terminal): - buffer(dte_default_buffer_size), - cmux_term(nullptr), command_term(std::move(terminal)), data_term(command_term), - mode(modem_mode::UNDEF) {} + buffer(dte_default_buffer_size), + cmux_term(nullptr), term(std::move(terminal)), other_term(term), + mode(modem_mode::UNDEF) {} command_result DTE::command(const std::string &command, got_line_cb got_line, uint32_t time_ms, const char separator) { Scoped l(internal_lock); result = command_result::TIMEOUT; signal.clear(GOT_LINE); - command_term->set_read_cb([this, got_line, separator](uint8_t *data, size_t len) { + term->set_read_cb([this, got_line, separator](uint8_t *data, size_t len) { if (!data) { data = buffer.get(); - len = command_term->read(data + buffer.consumed, buffer.size - buffer.consumed); + len = term->read(data + buffer.consumed, buffer.size - buffer.consumed); } else { buffer.consumed = 0; // if the underlying terminal contains data, we cannot fragment } @@ -46,13 +46,13 @@ command_result DTE::command(const std::string &command, got_line_cb got_line, ui buffer.consumed += len; return false; }); - command_term->write((uint8_t *)command.c_str(), command.length()); + term->write((uint8_t *)command.c_str(), command.length()); auto got_lf = signal.wait(GOT_LINE, time_ms); if (got_lf && result == command_result::TIMEOUT) { ESP_MODEM_THROW_IF_ERROR(ESP_ERR_INVALID_STATE); } buffer.consumed = 0; - command_term->set_read_cb(nullptr); + term->set_read_cb(nullptr); return result; } @@ -68,26 +68,26 @@ bool DTE::exit_cmux() } auto ejected = cmux_term->detach(); // return the ejected terminal and buffer back to this DTE - command_term = std::move(ejected.first); + term = std::move(ejected.first); buffer = std::move(ejected.second); - data_term = command_term; + other_term = term; return true; } bool DTE::setup_cmux() { - cmux_term = std::make_shared(command_term, std::move(buffer)); + cmux_term = std::make_shared(term, std::move(buffer)); if (cmux_term == nullptr) { return false; } if (!cmux_term->init()) { return false; } - command_term = std::make_unique(cmux_term, 0); - if (command_term == nullptr) { + term = std::make_unique(cmux_term, 0); + if (term == nullptr) { return false; } - data_term = std::make_unique(cmux_term, 1); + other_term = std::make_unique(cmux_term, 1); return true; } @@ -106,14 +106,12 @@ bool DTE::set_mode(modem_mode m) } // transitions (COMMAND|CMUX|UNDEF) -> DATA if (m == modem_mode::DATA_MODE) { - if (mode == modem_mode::CMUX_MODE) { - // mode stays the same, but need to swap terminals (as command has been switch) - data_term.swap(command_term); + if (mode == modem_mode::CMUX_MODE || mode == modem_mode::CMUX_MANUAL_MODE) { + // mode stays the same, but need to swap terminals (as command has been switched) + other_term.swap(term); } else { mode = m; } - // prepare the data terminal's callback to the configured std::function (used by netif) - data_term->set_read_cb(on_data); return true; } // transitions (DATA|CMUX|UNDEF) -> COMMAND @@ -125,21 +123,46 @@ bool DTE::set_mode(modem_mode m) } mode = modem_mode::UNDEF; return false; + } if (mode == modem_mode::CMUX_MANUAL_MODE) { + return true; } else { mode = m; return true; } } + // manual CMUX transitions: Enter CMUX + if (m == modem_mode::CMUX_MANUAL_MODE) { + if (setup_cmux()) { + mode = m; + return true; + } + mode = modem_mode::UNDEF; + return false; + } + // manual CMUX transitions: Exit CMUX + if (m == modem_mode::CMUX_MANUAL_EXIT && mode == modem_mode::CMUX_MANUAL_MODE) { + if (exit_cmux()) { + mode = modem_mode::COMMAND_MODE; + return true; + } + mode = modem_mode::UNDEF; + return false; + } + // manual CMUX transitions: Swap terminals + if (m == modem_mode::CMUX_MANUAL_SWAP && mode == modem_mode::CMUX_MANUAL_MODE) { + other_term.swap(term); + return true; + } return true; } void DTE::set_read_cb(std::function f) { on_data = std::move(f); - data_term->set_read_cb([this](uint8_t *data, size_t len) { + other_term->set_read_cb([this](uint8_t *data, size_t len) { if (!data) { // if no data available from terminal callback -> need to explicitly read some data = buffer.get(); - len = data_term->read(buffer.get(), buffer.size); + len = other_term->read(buffer.get(), buffer.size); } if (on_data) { return on_data(data, len); @@ -150,22 +173,22 @@ void DTE::set_read_cb(std::function f) void DTE::set_error_cb(std::function f) { - data_term->set_error_cb(f); - command_term->set_error_cb(f); + other_term->set_error_cb(f); + term->set_error_cb(f); } int DTE::read(uint8_t **d, size_t len) { auto data_to_read = std::min(len, buffer.size); auto data = buffer.get(); - auto actual_len = data_term->read(data, data_to_read); + auto actual_len = other_term->read(data, data_to_read); *d = data; return actual_len; } int DTE::write(uint8_t *data, size_t len) { - return data_term->write(data, len); + return other_term->write(data, len); } /** From 2180ab17d8d90743ee2943e8d1d975e0598502df Mon Sep 17 00:00:00 2001 From: David Cermak Date: Tue, 25 Oct 2022 16:50:34 +0200 Subject: [PATCH 2/2] fix(esp-modem): Support AT with callback in C-API Closes https://github.com/espressif/esp-protocols/issues/143 --- .../include/cxx_include/esp_modem_dte.hpp | 4 +- .../esp_modem/include/esp_modem_c_api_types.h | 2 + components/esp_modem/src/esp_modem_c_api.cpp | 50 ++++++++++++------ components/esp_modem/src/esp_modem_dce.cpp | 2 +- components/esp_modem/src/esp_modem_dte.cpp | 51 ++++++++++--------- 5 files changed, 65 insertions(+), 44 deletions(-) diff --git a/components/esp_modem/include/cxx_include/esp_modem_dte.hpp b/components/esp_modem/include/cxx_include/esp_modem_dte.hpp index 902f46fa29..3d592ea02c 100644 --- a/components/esp_modem/include/cxx_include/esp_modem_dte.hpp +++ b/components/esp_modem/include/cxx_include/esp_modem_dte.hpp @@ -116,8 +116,8 @@ class DTE : public CommandableIf { Lock internal_lock{}; /*!< Locks DTE operations */ unique_buffer buffer; /*!< DTE buffer */ std::shared_ptr cmux_term; /*!< Primary terminal for this DTE */ - std::shared_ptr term; /*!< Reference to the primary terminal (mostly for sending commands) */ - std::shared_ptr other_term; /*!< Secondary terminal for this DTE */ + std::shared_ptr primary_term; /*!< Reference to the primary terminal (mostly for sending commands) */ + std::shared_ptr secondary_term; /*!< Secondary terminal for this DTE */ modem_mode mode; /*!< DTE operation mode */ SignalGroup signal; /*!< Event group used to signal request-response operations */ command_result result; /*!< Command result of the currently exectuted command */ 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 66591df435..f47e2a064c 100644 --- a/components/esp_modem/include/esp_modem_c_api_types.h +++ b/components/esp_modem/include/esp_modem_c_api_types.h @@ -118,6 +118,8 @@ esp_err_t esp_modem_set_error_cb(esp_modem_dce_t *dce, esp_modem_terminal_error_ */ esp_err_t esp_modem_set_mode(esp_modem_dce_t *dce, esp_modem_dce_mode_t mode); +esp_err_t esp_modem_command(esp_modem_dce_t *dce, const char *command, esp_err_t(*got_line_cb)(uint8_t *data, size_t len), uint32_t timeout_ms); + /** * @} */ diff --git a/components/esp_modem/src/esp_modem_c_api.cpp b/components/esp_modem/src/esp_modem_c_api.cpp index c9ebdda2e6..fa1bbdbb2b 100644 --- a/components/esp_modem/src/esp_modem_c_api.cpp +++ b/components/esp_modem/src/esp_modem_c_api.cpp @@ -94,22 +94,22 @@ extern "C" esp_err_t esp_modem_set_mode(esp_modem_dce_t *dce_wrap, esp_modem_dce return ESP_ERR_INVALID_ARG; } switch (mode) { - case ESP_MODEM_MODE_DATA: - return dce_wrap->dce->set_mode(modem_mode::DATA_MODE) ? ESP_OK : ESP_FAIL; - case ESP_MODEM_MODE_COMMAND: - return dce_wrap->dce->set_mode(modem_mode::COMMAND_MODE) ? ESP_OK : ESP_FAIL; - case ESP_MODEM_MODE_CMUX: - return dce_wrap->dce->set_mode(modem_mode::CMUX_MODE) ? ESP_OK : ESP_FAIL; - case ESP_MODEM_MODE_CMUX_MANUAL: - return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_MODE) ? ESP_OK : ESP_FAIL; - case ESP_MODEM_MODE_CMUX_MANUAL_EXIT: - return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_EXIT) ? ESP_OK : ESP_FAIL; - case ESP_MODEM_MODE_CMUX_MANUAL_SWAP: - return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_SWAP) ? ESP_OK : ESP_FAIL; - case ESP_MODEM_MODE_CMUX_MANUAL_DATA: - return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_DATA) ? ESP_OK : ESP_FAIL; - case ESP_MODEM_MODE_CMUX_MANUAL_COMMAND: - return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_COMMAND) ? ESP_OK : ESP_FAIL; + case ESP_MODEM_MODE_DATA: + return dce_wrap->dce->set_mode(modem_mode::DATA_MODE) ? ESP_OK : ESP_FAIL; + case ESP_MODEM_MODE_COMMAND: + return dce_wrap->dce->set_mode(modem_mode::COMMAND_MODE) ? ESP_OK : ESP_FAIL; + case ESP_MODEM_MODE_CMUX: + return dce_wrap->dce->set_mode(modem_mode::CMUX_MODE) ? ESP_OK : ESP_FAIL; + case ESP_MODEM_MODE_CMUX_MANUAL: + return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_MODE) ? ESP_OK : ESP_FAIL; + case ESP_MODEM_MODE_CMUX_MANUAL_EXIT: + return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_EXIT) ? ESP_OK : ESP_FAIL; + case ESP_MODEM_MODE_CMUX_MANUAL_SWAP: + return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_SWAP) ? ESP_OK : ESP_FAIL; + case ESP_MODEM_MODE_CMUX_MANUAL_DATA: + return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_DATA) ? ESP_OK : ESP_FAIL; + case ESP_MODEM_MODE_CMUX_MANUAL_COMMAND: + return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_COMMAND) ? ESP_OK : ESP_FAIL; } return ESP_ERR_NOT_SUPPORTED; } @@ -400,3 +400,21 @@ extern "C" esp_err_t esp_modem_set_pdp_context(esp_modem_dce_t *dce_wrap, esp_mo pdp.protocol_type = c_api_pdp->protocol_type; return command_response_to_esp_err(dce_wrap->dce->set_pdp_context(pdp)); } + +extern "C" esp_err_t esp_modem_command(esp_modem_dce_t *dce_wrap, const char *command, esp_err_t(*got_line_fn)(uint8_t *data, size_t len), uint32_t timeout_ms) +{ + if (dce_wrap == nullptr || dce_wrap->dce == nullptr || command == nullptr || got_line_fn == nullptr) { + return ESP_ERR_INVALID_ARG; + } + std::string cmd(command); + return command_response_to_esp_err(dce_wrap->dce->command(cmd, [got_line_fn](uint8_t *data, size_t len) { + switch (got_line_fn(data, len)) { + case ESP_OK: + return command_result::OK; + case ESP_FAIL: + return command_result::FAIL; + default: + return command_result::TIMEOUT; + } + }, timeout_ms)); +} diff --git a/components/esp_modem/src/esp_modem_dce.cpp b/components/esp_modem/src/esp_modem_dce.cpp index 5cd5895e6b..afd41ffa6e 100644 --- a/components/esp_modem/src/esp_modem_dce.cpp +++ b/components/esp_modem/src/esp_modem_dce.cpp @@ -130,7 +130,7 @@ bool DCE_Mode::set_unsafe(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m mode = modem_mode::CMUX_MODE; return transitions::enter_data(*dte, *device, netif); case modem_mode::CMUX_MANUAL_MODE: - if (mode != modem_mode::COMMAND_MODE) { + if (mode != modem_mode::COMMAND_MODE && mode != modem_mode::UNDEF) { return false; } device->set_mode(modem_mode::CMUX_MODE); diff --git a/components/esp_modem/src/esp_modem_dte.cpp b/components/esp_modem/src/esp_modem_dte.cpp index b962b97ada..f8eec55798 100644 --- a/components/esp_modem/src/esp_modem_dte.cpp +++ b/components/esp_modem/src/esp_modem_dte.cpp @@ -15,24 +15,24 @@ using namespace esp_modem; static const size_t dte_default_buffer_size = 1000; DTE::DTE(const esp_modem_dte_config *config, std::unique_ptr terminal): - buffer(config->dte_buffer_size), - cmux_term(nullptr), term(std::move(terminal)), other_term(term), - mode(modem_mode::UNDEF) {} + buffer(config->dte_buffer_size), + cmux_term(nullptr), primary_term(std::move(terminal)), secondary_term(primary_term), + mode(modem_mode::UNDEF) {} DTE::DTE(std::unique_ptr terminal): - buffer(dte_default_buffer_size), - cmux_term(nullptr), term(std::move(terminal)), other_term(term), - mode(modem_mode::UNDEF) {} + buffer(dte_default_buffer_size), + cmux_term(nullptr), primary_term(std::move(terminal)), secondary_term(primary_term), + mode(modem_mode::UNDEF) {} command_result DTE::command(const std::string &command, got_line_cb got_line, uint32_t time_ms, const char separator) { Scoped l(internal_lock); result = command_result::TIMEOUT; signal.clear(GOT_LINE); - term->set_read_cb([this, got_line, separator](uint8_t *data, size_t len) { + primary_term->set_read_cb([this, got_line, separator](uint8_t *data, size_t len) { if (!data) { data = buffer.get(); - len = term->read(data + buffer.consumed, buffer.size - buffer.consumed); + len = primary_term->read(data + buffer.consumed, buffer.size - buffer.consumed); } else { buffer.consumed = 0; // if the underlying terminal contains data, we cannot fragment } @@ -46,13 +46,13 @@ command_result DTE::command(const std::string &command, got_line_cb got_line, ui buffer.consumed += len; return false; }); - term->write((uint8_t *)command.c_str(), command.length()); + primary_term->write((uint8_t *)command.c_str(), command.length()); auto got_lf = signal.wait(GOT_LINE, time_ms); if (got_lf && result == command_result::TIMEOUT) { ESP_MODEM_THROW_IF_ERROR(ESP_ERR_INVALID_STATE); } buffer.consumed = 0; - term->set_read_cb(nullptr); + primary_term->set_read_cb(nullptr); return result; } @@ -68,26 +68,26 @@ bool DTE::exit_cmux() } auto ejected = cmux_term->detach(); // return the ejected terminal and buffer back to this DTE - term = std::move(ejected.first); + primary_term = std::move(ejected.first); buffer = std::move(ejected.second); - other_term = term; + secondary_term = primary_term; return true; } bool DTE::setup_cmux() { - cmux_term = std::make_shared(term, std::move(buffer)); + cmux_term = std::make_shared(primary_term, std::move(buffer)); if (cmux_term == nullptr) { return false; } if (!cmux_term->init()) { return false; } - term = std::make_unique(cmux_term, 0); - if (term == nullptr) { + primary_term = std::make_unique(cmux_term, 0); + if (primary_term == nullptr) { return false; } - other_term = std::make_unique(cmux_term, 1); + secondary_term = std::make_unique(cmux_term, 1); return true; } @@ -108,7 +108,7 @@ bool DTE::set_mode(modem_mode m) if (m == modem_mode::DATA_MODE) { if (mode == modem_mode::CMUX_MODE || mode == modem_mode::CMUX_MANUAL_MODE) { // mode stays the same, but need to swap terminals (as command has been switched) - other_term.swap(term); + secondary_term.swap(primary_term); } else { mode = m; } @@ -150,19 +150,20 @@ bool DTE::set_mode(modem_mode m) } // manual CMUX transitions: Swap terminals if (m == modem_mode::CMUX_MANUAL_SWAP && mode == modem_mode::CMUX_MANUAL_MODE) { - other_term.swap(term); + secondary_term.swap(primary_term); return true; } - return true; + mode = modem_mode::UNDEF; + return false; } void DTE::set_read_cb(std::function f) { on_data = std::move(f); - other_term->set_read_cb([this](uint8_t *data, size_t len) { + secondary_term->set_read_cb([this](uint8_t *data, size_t len) { if (!data) { // if no data available from terminal callback -> need to explicitly read some data = buffer.get(); - len = other_term->read(buffer.get(), buffer.size); + len = secondary_term->read(buffer.get(), buffer.size); } if (on_data) { return on_data(data, len); @@ -173,22 +174,22 @@ void DTE::set_read_cb(std::function f) void DTE::set_error_cb(std::function f) { - other_term->set_error_cb(f); - term->set_error_cb(f); + secondary_term->set_error_cb(f); + primary_term->set_error_cb(f); } int DTE::read(uint8_t **d, size_t len) { auto data_to_read = std::min(len, buffer.size); auto data = buffer.get(); - auto actual_len = other_term->read(data, data_to_read); + auto actual_len = secondary_term->read(data, data_to_read); *d = data; return actual_len; } int DTE::write(uint8_t *data, size_t len) { - return other_term->write(data, len); + return secondary_term->write(data, len); } /**