From 0d9b5dd8b7f6c18df814096fa4ab00d09921d78f Mon Sep 17 00:00:00 2001 From: David Cermak Date: Thu, 13 May 2021 07:28:05 +0200 Subject: [PATCH] Corrections on fragmenting/buffering for vfs/cmux --- esp_modem/CMakeLists.txt | 1 + .../modem_console/main/modem_console_main.cpp | 12 +- .../simple_cxx_client/main/simple_client.cpp | 23 +- .../include/cxx_include/esp_modem_cmux.hpp | 6 +- .../include/cxx_include/esp_modem_dte.hpp | 4 +- .../include/cxx_include/esp_modem_netif.hpp | 6 +- .../cxx_include/esp_modem_primitives.hpp | 38 +++- esp_modem/src/esp_modem_cmux.cpp | 73 +++++-- esp_modem/src/esp_modem_command_library.cpp | 198 ++++++++++-------- esp_modem/src/esp_modem_dte.cpp | 16 +- .../src/esp_modem_primitives_freertos.cpp | 44 ++-- esp_modem/src/esp_modem_primitives_linux.cpp | 35 +++- esp_modem/src/esp_modem_term_fs.cpp | 113 +++------- esp_modem/src/esp_modem_term_uart.cpp | 77 +++++++ esp_modem/src/esp_modem_uart.cpp | 2 +- esp_modem/test/host_test/main/CMakeLists.txt | 2 +- .../test/host_test/main/LoopbackTerm.cpp | 82 ++++++++ esp_modem/test/host_test/main/LoopbackTerm.h | 36 ++++ esp_modem/test/host_test/main/test_modem.cpp | 99 +++------ esp_modem/test/host_test/my.log | 31 --- esp_modem/test/target/main/NetworkDCE.cpp | 15 +- 21 files changed, 578 insertions(+), 335 deletions(-) create mode 100644 esp_modem/src/esp_modem_term_uart.cpp create mode 100644 esp_modem/test/host_test/main/LoopbackTerm.cpp create mode 100644 esp_modem/test/host_test/main/LoopbackTerm.h delete mode 100644 esp_modem/test/host_test/my.log diff --git a/esp_modem/CMakeLists.txt b/esp_modem/CMakeLists.txt index a5ee3a5e2..fdec81715 100644 --- a/esp_modem/CMakeLists.txt +++ b/esp_modem/CMakeLists.txt @@ -8,6 +8,7 @@ if(${target} STREQUAL "linux") else() set(platform_srcs src/esp_modem_primitives_freertos.cpp src/esp_modem_uart.cpp + src/esp_modem_term_uart.cpp src/esp_modem_netif.cpp) set(dependencies driver) endif() diff --git a/esp_modem/examples/modem_console/main/modem_console_main.cpp b/esp_modem/examples/modem_console/main/modem_console_main.cpp index b47ac8354..0306835c9 100644 --- a/esp_modem/examples/modem_console/main/modem_console_main.cpp +++ b/esp_modem/examples/modem_console/main/modem_console_main.cpp @@ -47,12 +47,13 @@ extern "C" void app_main(void) ESP_ERROR_CHECK(esp_event_loop_create_default()); // init the DTE - esp_modem_dte_config_t dte_config = { + esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG(); + esp_modem_dte_config_t dte_config2 = { .dte_buffer_size = 512, .vfs_config = {.port_num = UART_NUM_1, .dev_name = "/dev/uart/1", - .rx_buffer_size = 512, - .tx_buffer_size = 512, + .rx_buffer_size = 1024, + .tx_buffer_size = 1024, .baud_rate = 115200, .tx_io_num = 25, .rx_io_num = 26, @@ -64,7 +65,8 @@ extern "C" void app_main(void) esp_netif_t *esp_netif = esp_netif_new(&ppp_netif_config); assert(esp_netif); - auto uart_dte = create_vfs_dte(&dte_config); + auto uart_dte = create_vfs_dte(&dte_config2); +// auto uart_dte = create_uart_dte(&dte_config); esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("internet"); @@ -192,7 +194,7 @@ extern "C" void app_main(void) CHECK_ERR(dce->reset(), ESP_LOGI(TAG, "OK")); }); - signal_group exit_signal; + SignalGroup exit_signal; const ConsoleCommand ExitConsole("exit", "exit the console application", no_args, [&](ConsoleCommand *c){ ESP_LOGI(TAG, "Exiting..."); exit_signal.set(1); diff --git a/esp_modem/examples/simple_cxx_client/main/simple_client.cpp b/esp_modem/examples/simple_cxx_client/main/simple_client.cpp index 855caab77..c6a7f6f1c 100644 --- a/esp_modem/examples/simple_cxx_client/main/simple_client.cpp +++ b/esp_modem/examples/simple_cxx_client/main/simple_client.cpp @@ -128,6 +128,18 @@ extern "C" void app_main(void) /* Configure the DTE */ esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG(); + esp_modem_dte_config_t dte_config2 = { + .dte_buffer_size = 512, + .vfs_config = {.port_num = UART_NUM_1, + .dev_name = "/dev/uart/1", + .rx_buffer_size = 512, + .tx_buffer_size = 512, + .baud_rate = 115200, + .tx_io_num = 25, + .rx_io_num = 26, + .task_stack_size = 4096, + .task_prio = 5} + }; /* Configure the DCE */ esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_EXAMPLE_MODEM_PPP_APN); @@ -135,7 +147,8 @@ extern "C" void app_main(void) /* Configure the PPP netif */ esp_netif_config_t netif_ppp_config = ESP_NETIF_DEFAULT_PPP(); - auto uart_dte = create_uart_dte(&dte_config); +// auto uart_dte = create_uart_dte(&dte_config); + auto uart_dte = create_vfs_dte(&dte_config2); esp_netif_t *esp_netif = esp_netif_new(&netif_ppp_config); assert(esp_netif); @@ -160,10 +173,18 @@ extern "C" void app_main(void) throw_if_false(dce->set_pin(CONFIG_EXAMPLE_SIM_PIN) == command_result::OK, "Cannot set PIN!"); } vTaskDelay(pdMS_TO_TICKS(1000)); +// dce->get_imsi(str); +// std::cout << "Modem IMSI number:" << str << "|" << std::endl; +// std::cout << "|" << str << "|" << std::endl; dce->set_mode(esp_modem::modem_mode::CMUX_MODE); dce->get_imsi(str); std::cout << "Modem IMSI number:" << str << "|" << std::endl; + dce->get_imei(str); + std::cout << "Modem IMEI number:" << str << "|" << std::endl; + dce->get_operator_name(str); + std::cout << "Operator name:" << str << "|" << std::endl; +// return; dce->set_data(); diff --git a/esp_modem/include/cxx_include/esp_modem_cmux.hpp b/esp_modem/include/cxx_include/esp_modem_cmux.hpp index 2622fd8ab..85e5bf390 100644 --- a/esp_modem/include/cxx_include/esp_modem_cmux.hpp +++ b/esp_modem/include/cxx_include/esp_modem_cmux.hpp @@ -56,7 +56,7 @@ class CMuxInstance; class CMux { public: explicit CMux(std::unique_ptr t, std::unique_ptr b, size_t buff_size): - term(std::move(t)), buffer_size(buff_size), buffer(std::move(b)) {} + term(std::move(t)), buffer_size(buff_size), buffer(std::move(b)), payload_start(nullptr), total_payload_size(0) {} ~CMux() = default; [[nodiscard]] bool init(); void set_read_cb(int inst, std::function f); @@ -64,7 +64,7 @@ public: int write(int i, uint8_t *data, size_t len); private: std::function read_cb[max_terms]; - void data_available(uint8_t *data, size_t len); + void data_available(uint8_t *data, size_t len, size_t payload_offset); void send_sabm(size_t i); std::unique_ptr term; cmux_state state; @@ -80,6 +80,8 @@ private: Lock lock; int instance; int sabm_ack; + uint8_t *payload_start; + size_t total_payload_size; }; diff --git a/esp_modem/include/cxx_include/esp_modem_dte.hpp b/esp_modem/include/cxx_include/esp_modem_dte.hpp index 724a3c727..65839c0ac 100644 --- a/esp_modem/include/cxx_include/esp_modem_dte.hpp +++ b/esp_modem/include/cxx_include/esp_modem_dte.hpp @@ -96,7 +96,7 @@ public: command_result command(const std::string &command, got_line_cb got_line, uint32_t time_ms, char separator) override; private: - static const size_t GOT_LINE = signal_group::bit0; /*!< Bit indicating response available */ + static const size_t GOT_LINE = SignalGroup::bit0; /*!< Bit indicating response available */ [[nodiscard]] bool setup_cmux(); /*!< Internal setup of CMUX mode */ @@ -108,7 +108,7 @@ private: Terminal *command_term; /*!< Reference to the terminal used for sending commands */ std::unique_ptr other_term; /*!< Secondary terminal for this DTE */ modem_mode mode; /*!< DTE operation mode */ - signal_group signal; /*!< Event group used to signal request-response operations */ + SignalGroup signal; /*!< Event group used to signal request-response operations */ std::function on_data; /*!< on data callback for current terminal */ }; diff --git a/esp_modem/include/cxx_include/esp_modem_netif.hpp b/esp_modem/include/cxx_include/esp_modem_netif.hpp index 85ffd9f41..d44f55c4e 100644 --- a/esp_modem/include/cxx_include/esp_modem_netif.hpp +++ b/esp_modem/include/cxx_include/esp_modem_netif.hpp @@ -75,9 +75,9 @@ private: std::shared_ptr ppp_dte; esp_netif_t *netif; struct ppp_netif_driver driver{}; - signal_group signal; - static const size_t PPP_STARTED = signal_group::bit0; - static const size_t PPP_EXIT = signal_group::bit1; + SignalGroup signal; + static const size_t PPP_STARTED = SignalGroup::bit0; + static const size_t PPP_EXIT = SignalGroup::bit1; }; /** diff --git a/esp_modem/include/cxx_include/esp_modem_primitives.hpp b/esp_modem/include/cxx_include/esp_modem_primitives.hpp index 03a01d950..3c11e97c9 100644 --- a/esp_modem/include/cxx_include/esp_modem_primitives.hpp +++ b/esp_modem/include/cxx_include/esp_modem_primitives.hpp @@ -18,6 +18,8 @@ #if defined(CONFIG_IDF_TARGET_LINUX) #include +#include + #else // forward declarations of FreeRTOS primitives struct QueueDefinition; @@ -27,10 +29,12 @@ typedef void * EventGroupHandle_t; namespace esp_modem { +// Forward declaration for both linux/FreeRTOS targets +// +using TaskFunction_t = void (*)(void*); #if !defined(CONFIG_IDF_TARGET_LINUX) struct Lock { using MutexT = QueueDefinition*; - explicit Lock(); ~Lock(); void lock(); @@ -38,13 +42,14 @@ struct Lock { private: MutexT m{}; }; - -using Signal = void*; - +using TaskT = void*; +using SignalT = void*; #else using Lock = std::mutex; -struct SignalGroup; -using Signal = std::unique_ptr; +struct SignalGroupInternal; +using SignalT = std::unique_ptr; +using TaskT = std::thread; +static constexpr uint32_t portMAX_DELAY = UINT32_MAX; #endif template @@ -57,13 +62,26 @@ private: T& lock; }; -struct signal_group { +class Task { +public: + explicit Task(size_t stack_size, size_t priority, void *task_param, TaskFunction_t task_function); + ~Task(); + + static void Delete(); + static void Relinquish(); +private: + TaskT task_handle; +}; + + +class SignalGroup { +public: static constexpr size_t bit0 = 1 << 0; static constexpr size_t bit1 = 1 << 1; static constexpr size_t bit2 = 1 << 2; static constexpr size_t bit3 = 1 << 3; - explicit signal_group(); + explicit SignalGroup(); void set(uint32_t bits); @@ -77,10 +95,10 @@ struct signal_group { // waiting for any bit, not clearing them bool wait_any(uint32_t flags, uint32_t time_ms); - ~signal_group(); + ~SignalGroup(); private: - Signal event_group; + SignalT event_group; }; } // namespace esp_modem diff --git a/esp_modem/src/esp_modem_cmux.cpp b/esp_modem/src/esp_modem_cmux.cpp index f9b8d9d21..7cfdd29a6 100644 --- a/esp_modem/src/esp_modem_cmux.cpp +++ b/esp_modem/src/esp_modem_cmux.cpp @@ -85,23 +85,39 @@ void CMux::send_sabm(size_t dlci) } -void CMux::data_available(uint8_t *data, size_t len) +void CMux::data_available(uint8_t *data, size_t len, size_t payload_offset) { if (type == 0xFF && len > 0 && dlci > 0) { int virtual_term = dlci - 1; - if (virtual_term < max_terms && read_cb[virtual_term]) - read_cb[virtual_term](data, len); + if (virtual_term < max_terms && read_cb[virtual_term]) { + if (payload_start == nullptr) { + payload_start = data; + } + total_payload_size += len; + // Post partial data (if configured) +// read_cb[virtual_term](payload_start, total_payload_size); + + } } else if (type == 0x73 && len == 0) { // notify the initial SABM command Scoped l(lock); sabm_ack = dlci; + } else if (payload_offset == -1) { + int virtual_term = dlci - 1; + if (virtual_term < max_terms && read_cb[virtual_term]) { + read_cb[virtual_term](payload_start, total_payload_size); + } + } } bool CMux::on_cmux(uint8_t *data, size_t actual_len) { if (!data) { - auto data_to_read = std::min(actual_len, buffer_size); - data = buffer.get(); + auto data_to_read = buffer_size; + if (payload_start) + data = payload_start + total_payload_size; + else + data = buffer.get(); actual_len = term->read(data, data_to_read); } ESP_LOG_BUFFER_HEXDUMP("Received", data, actual_len, ESP_LOG_DEBUG); @@ -135,7 +151,7 @@ bool CMux::on_cmux(uint8_t *data, size_t actual_len) return false; case cmux_state::INIT: if (frame[0] != SOF_MARKER) { - ESP_LOGI("CMUX", "Protocol mismatch!"); + ESP_LOGE("CMUX", "Protocol mismatch!"); state = cmux_state::RECOVER; break; } @@ -151,6 +167,13 @@ bool CMux::on_cmux(uint8_t *data, size_t actual_len) frame++; break; case cmux_state::HEADER: + if (available_len > 0 && frame_header_offset == 1) { + if (frame[0] == SOF_MARKER) { + available_len--; + frame++; + break; + } + } if (available_len + frame_header_offset < 4) { memcpy(frame_header + frame_header_offset, frame, available_len); frame_header_offset += available_len; @@ -168,14 +191,15 @@ bool CMux::on_cmux(uint8_t *data, size_t actual_len) state = cmux_state::PAYLOAD; break; case cmux_state::PAYLOAD: +// ESP_LOGD("PAYLOAD", "CMUX FR: A:%02x T:%02x available:%d payloadLen:%d", dlci, type, available_len, payload_len); if (available_len < payload_len) { // payload state = cmux_state::PAYLOAD; - data_available(frame, available_len); // partial read + data_available(frame, available_len, 0); // partial read payload_len -= available_len; return false; } else { // complete if (payload_len > 0) { - data_available(&frame[0], payload_len); // rest read + data_available(&frame[0], payload_len, 0); // rest read } available_len -= payload_len; frame += payload_len; @@ -183,25 +207,42 @@ bool CMux::on_cmux(uint8_t *data, size_t actual_len) } break; case cmux_state::FOOTER: - if (available_len + frame_header_offset < 6) { - memcpy(frame_header + frame_header_offset, frame, available_len); - frame_header_offset += available_len; + if (available_len == 0) { + return false; // need read more + } else if (available_len == 1) { + frame_header[4] = frame[0]; + frame ++; + available_len --; return false; // need read more } else { - footer_offset = std::min(available_len, 6 - frame_header_offset); - memcpy(frame_header + frame_header_offset, frame, footer_offset); +// +// } +// if (available_len + frame_header_offset < 6) { +// memcpy(frame_header + frame_header_offset, frame, available_len); +// frame_header_offset += available_len; +// return false; // need read more +// } else { + frame_header[4] = frame[0]; + frame_header[5] = frame[1]; + footer_offset = 2; +// footer_offset = std::min(available_len, 6 - frame_header_offset); +// memcpy(frame_header + frame_header_offset, frame, footer_offset); if (frame_header[5] != SOF_MARKER) { - ESP_LOGI("CMUX", "Protocol mismatch!"); + ESP_LOGE("CMUX", "Footer Protocol mismatch!"); state = cmux_state::RECOVER; break; } if (payload_len == 0) { - data_available(frame_header, 0); // Null payload + data_available(frame_header, 0, 0); // Null payload } frame += footer_offset; available_len -= footer_offset; state = cmux_state::INIT; frame_header_offset = 0; + if (payload_len) + data_available(nullptr, 0, -1); // Null payload + payload_start = nullptr; + total_payload_size = 0; } break; } @@ -223,7 +264,7 @@ bool CMux::init() { int timeout = 0; send_sabm(i); - while (1) { + while (true) { usleep(10'000); Scoped l(lock); if (sabm_ack == i) { diff --git a/esp_modem/src/esp_modem_command_library.cpp b/esp_modem/src/esp_modem_command_library.cpp index 5987fb010..491714a62 100644 --- a/esp_modem/src/esp_modem_command_library.cpp +++ b/esp_modem/src/esp_modem_command_library.cpp @@ -12,50 +12,62 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include +#include +#include "esp_log.h" #include "cxx_include/esp_modem_dte.hpp" #include "cxx_include/esp_modem_dce_module.hpp" #include "cxx_include/esp_modem_command_library.hpp" -#include -// TODO: Remove iostream -#include namespace esp_modem::dce_commands { -static inline command_result generic_command(CommandableIf* t, const std::string& command, - const std::string& pass_phrase, - const std::string& fail_phrase, uint32_t timeout_ms) +command_result generic_command(CommandableIf* t, const std::string &command, + const std::list& pass_phrase, + const std::list& fail_phrase, + uint32_t timeout_ms) { - std::cout << command << std::endl; + printf("Command %s\n", command.c_str()); return t->command(command, [&](uint8_t *data, size_t len) { - std::string response((char*)data, len); - std::cout << response << std::endl; - - if (response.find(pass_phrase) != std::string::npos) { - return command_result::OK; - } else if (response.find(fail_phrase) != std::string::npos) { - return command_result::FAIL; - } + std::string_view response((char*)data, len); + printf("Response: %.*s\n", (int)response.length(), response.data()); + for (auto it : pass_phrase) + if (response.find(it) != std::string::npos) + return command_result::OK; + for (auto it : fail_phrase) + if (response.find(it) != std::string::npos) + return command_result::FAIL; return command_result::TIMEOUT; }, timeout_ms); + } -static inline command_result generic_get_string(CommandableIf* t, const std::string& command, std::string& output, uint32_t timeout_ms = 500) +static inline command_result generic_command(CommandableIf* t, const std::string& command, + const std::string& pass_phrase, + const std::string& fail_phrase, uint32_t timeout_ms) +{ + const auto pass = std::list({pass_phrase}); + const auto fail = std::list({fail_phrase}); + return generic_command(t, command, pass, fail, timeout_ms); +} + +static inline command_result generic_get_string(CommandableIf* t, const std::string& command, std::string_view& output, uint32_t timeout_ms = 500) { return t->command(command, [&](uint8_t *data, size_t len) { size_t pos = 0; - std::string response((char*)data, len); + std::string_view response((char*)data, len); while ((pos = response.find('\n')) != std::string::npos) { - std::string token = response.substr(0, pos); - for (auto i = 0; i<2; ++i) // trip trailing \n\r of last two chars - if (pos >= 1 && (token[pos-1] == '\r' || token[pos-1] == '\n')) - token.pop_back(); + std::string_view token = response.substr(0, pos); + for (auto it = token.end() - 1; it > token.begin(); it--) // strip trailing CR or LF + if (*it == '\r' || *it == '\n') + token.remove_suffix(1); + printf("{%.*s}\n", static_cast(token.size()), token.data()); if (token.find("OK") != std::string::npos) { return command_result::OK; } else if (token.find("ERROR") != std::string::npos) { return command_result::FAIL; - } else if (token.length() > 2) { + } else if (token.size() > 2) { output = token; } response = response.substr(pos+1); @@ -64,7 +76,17 @@ static inline command_result generic_get_string(CommandableIf* t, const std::str }, timeout_ms); } -static inline command_result generic_command_common(CommandableIf* t, std::string command, uint32_t timeout = 500) +static inline command_result generic_get_string(CommandableIf* t, const std::string& command, std::string& output, uint32_t timeout_ms = 500) +{ + std::string_view out; + auto ret = generic_get_string(t, command, out, timeout_ms); + if (ret == command_result::OK) + output = out; + return ret; +} + + +static inline command_result generic_command_common(CommandableIf* t, const std::string &command, uint32_t timeout = 500) { return generic_command(t, command, "OK", "ERROR", timeout); } @@ -111,28 +133,54 @@ command_result hang_up(CommandableIf* t) command_result get_battery_status(CommandableIf* t, int& voltage, int &bcs, int &bcl) { - std::string out; + std::string_view out; auto ret = generic_get_string(t, "AT+CBC\r", out); if (ret != command_result::OK) return ret; - if (out.find("+CBC") == std::string::npos) + + constexpr std::string_view pattern = "+CBC: "; + if (out.find(pattern) == std::string_view::npos) return command_result::FAIL; // Parsing +CBC: ,, - sscanf(out.c_str(), "%*s%d,%d,%d", &bcs, &bcl, &voltage); + out = out.substr(pattern.size()); + int pos, value, property = 0; + while ((pos = out.find(',') != std::string::npos)) { + if (std::from_chars(out.data(), out.data() + pos, value).ec == std::errc::invalid_argument) + return command_result::FAIL; + switch (property++) { + case 0: bcs = value; + break; + case 1: bcl = value; + break; + default: + return command_result::FAIL; + } + out = out.substr(pos + 1); + } + if (std::from_chars(out.data(), out.data() + out.size(), voltage).ec == std::errc::invalid_argument) + return command_result::FAIL; return command_result::OK; } command_result get_battery_status_sim7xxx(CommandableIf* t, int& voltage, int &bcs, int &bcl) { - std::string out; + std::string_view out; auto ret = generic_get_string(t, "AT+CBC\r", out); if (ret != command_result::OK) return ret; - if (out.find("+CBC") == std::string::npos) - return command_result::FAIL; // Parsing +CBC: V + constexpr std::string_view pattern = "+CBC: "; + constexpr int num_pos = pattern.size(); + int dot_pos; + if (out.find(pattern) == std::string::npos || + (dot_pos = out.find('.')) == std::string::npos) + return command_result::FAIL; + int volt, fraction; - sscanf(out.c_str(), "+CBC: %d.%dV", &volt, &fraction); + if (std::from_chars(out.data() + num_pos, out.data() + dot_pos, volt).ec == std::errc::invalid_argument) + return command_result::FAIL; + if (std::from_chars(out.data() + dot_pos + 1, out.data() + out.size() - 1, fraction).ec == std::errc::invalid_argument) + return command_result::FAIL; bcl = bcs = -1; // not available for these models voltage = 1000*volt + fraction; return command_result::OK; @@ -145,7 +193,7 @@ command_result set_flow_control(CommandableIf* t, int dce_flow, int dte_flow) command_result get_operator_name(CommandableIf* t, std::string& operator_name) { - std::string out; + std::string_view out; auto ret = generic_get_string(t, "AT+COPS?\r", out, 75000); if (ret != command_result::OK) return ret; @@ -193,19 +241,9 @@ command_result resume_data_mode(CommandableIf* t) command_result set_command_mode(CommandableIf* t) { - std::cout << "Sending +++" << std::endl; - return t->command("+++", [&](uint8_t *data, size_t len) { - std::string response((char*)data, len); - std::cout << response << std::endl; - if (response.find("OK") != std::string::npos) { - return command_result::OK; - } else if (response.find("NO CARRIER") != std::string::npos) { - return command_result::OK; - } else if (response.find("ERROR") != std::string::npos) { - return command_result::FAIL; - } - return command_result::TIMEOUT; - }, 5000); + const auto pass = std::list({"NO CARRIER", "OK"}); + const auto fail = std::list({"ERROR"}); + return generic_command(t, "+++", pass, fail, 5000); } command_result get_imsi(CommandableIf* t, std::string& imsi_number) @@ -239,8 +277,8 @@ command_result sms_character_set(CommandableIf* t) command_result send_sms(CommandableIf* t, const std::string& number, const std::string& message) { auto ret = t->command("AT+CMGS=\"" + number + "\"\r", [&](uint8_t *data, size_t len) { - std::string response((char*)data, len); - std::cout << response << std::endl; + std::string_view response((char*)data, len); + printf("%.*s", static_cast(response.size()), response.data()); if (response.find('>') != std::string::npos) { return command_result::OK; } @@ -259,53 +297,49 @@ command_result set_cmux(CommandableIf* t) command_result read_pin(CommandableIf* t, bool& pin_ok) { - std::cout << "Sending read_pin" << std::endl; - return t->command("AT+CPIN?\r", [&](uint8_t *data, size_t len) { - std::string response((char*)data, len); - std::cout << response << std::endl; - if (response.find("READY") != std::string::npos) { - pin_ok = true; - return command_result::OK; - } else if (response.find("PIN") != std::string::npos || response.find("PUK") != std::string::npos ) { - pin_ok = false; - return command_result::OK; - } else if (response.find("ERROR") != std::string::npos) { - return command_result::FAIL; - } - return command_result::TIMEOUT; - }, 5000); + std::string_view out; + auto ret = generic_get_string(t, "AT+CPIN?\r", out); + if (ret != command_result::OK) + return ret; + if (out.find("+CPIN:") == std::string::npos) + 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; + } + if (out.find("READY") != std::string::npos) { + pin_ok = true; + return command_result::OK; + } + 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) { - std::cout << "Sending set_pin" << std::endl; std::string set_pin_command = "AT+CPIN=" + pin + "\r"; return generic_command_common(t, set_pin_command); } command_result get_signal_quality(CommandableIf* t, int &rssi, int &ber) { - std::cout << "get_signal_quality" << std::endl; - return t->command("AT+CSQ\r", [&](uint8_t *data, size_t len) { - size_t pos = 0; - std::string response((char*)data, len); - while ((pos = response.find('\n')) != std::string::npos) { - std::string token = response.substr(0, pos); - for (auto i = 0; i<2; ++i) // trip trailing \n\r of last two chars - if (pos >= 1 && (token[pos-1] == '\r' || token[pos-1] == '\n')) - token.pop_back(); + printf("%s", __func__ ); + std::string_view out; + auto ret = generic_get_string(t, "AT+CSQ\r", out); + if (ret != command_result::OK) + return ret; - if (token.find("OK") != std::string::npos) { - return command_result::OK; - } else if (token.find("ERROR") != std::string::npos) { - return command_result::FAIL; - } else if (token.find("+CSQ") != std::string::npos) { - sscanf(token.c_str(), "%*s%d,%d", &rssi, &ber); - } - response = response.substr(pos+1); - } - return command_result::TIMEOUT; - }, 500); + constexpr std::string_view pattern = "+CSQ: "; + constexpr int rssi_pos = pattern.size(); + int ber_pos; + if (out.find(pattern) == std::string::npos || + (ber_pos = out.find(',')) == std::string::npos) + return command_result::FAIL; + + if (std::from_chars(out.data() + rssi_pos, out.data() + ber_pos, rssi).ec == std::errc::invalid_argument) + return command_result::FAIL; + if (std::from_chars(out.data() + ber_pos + 1, out.data() + out.size(), ber).ec == std::errc::invalid_argument) + return command_result::FAIL; + return command_result::OK; } } // esp_modem::dce_commands \ No newline at end of file diff --git a/esp_modem/src/esp_modem_dte.cpp b/esp_modem/src/esp_modem_dte.cpp index d2d7bc824..29d4eac43 100644 --- a/esp_modem/src/esp_modem_dte.cpp +++ b/esp_modem/src/esp_modem_dte.cpp @@ -39,18 +39,19 @@ command_result DTE::command(const std::string &command, got_line_cb got_line, ui command_result res = command_result::TIMEOUT; command_term->set_read_cb([&](uint8_t *data, size_t len) { if (!data) { - auto data_to_read = std::min(len, buffer_size - consumed); - data = buffer.get() + consumed; - len = command_term->read(data, data_to_read); + data = buffer.get(); // + consumed; + len = command_term->read(data + consumed, buffer_size - consumed); + } else { + consumed = 0; } - consumed += len; - if (memchr(data, separator, len)) { - res = got_line(data, consumed); + if (memchr(data + consumed, separator, len)) { + res = got_line(data, consumed + len); if (res == command_result::OK || res == command_result::FAIL) { signal.set(GOT_LINE); return true; } } + consumed += len; return false; }); command_term->write((uint8_t *)command.c_str(), command.length()); @@ -107,9 +108,8 @@ void DTE::set_read_cb(std::function f) on_data = std::move(f); 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 - auto data_to_read = std::min(len, buffer_size - consumed); data = buffer.get(); - len = term->read(data, data_to_read); + len = term->read(buffer.get(), buffer_size); } if (on_data) return on_data(data, len); diff --git a/esp_modem/src/esp_modem_primitives_freertos.cpp b/esp_modem/src/esp_modem_primitives_freertos.cpp index 1403abd74..0a809bb53 100644 --- a/esp_modem/src/esp_modem_primitives_freertos.cpp +++ b/esp_modem/src/esp_modem_primitives_freertos.cpp @@ -18,12 +18,8 @@ #include "freertos/event_groups.h" #include "freertos/semphr.h" - - namespace esp_modem { - - void Lock::unlock() { xSemaphoreGiveRecursive(m); } @@ -43,42 +39,66 @@ void Lock::lock() { } -signal_group::signal_group(): event_group(nullptr) +SignalGroup::SignalGroup(): event_group(nullptr) { event_group = xEventGroupCreate(); throw_if_false(event_group != nullptr, "create signal event group failed"); } -void signal_group::set(uint32_t bits) +void SignalGroup::set(uint32_t bits) { xEventGroupSetBits(event_group, bits); } -void signal_group::clear(uint32_t bits) +void SignalGroup::clear(uint32_t bits) { xEventGroupClearBits(event_group, bits); } -bool signal_group::wait(uint32_t flags, uint32_t time_ms) +bool SignalGroup::wait(uint32_t flags, uint32_t time_ms) { EventBits_t bits = xEventGroupWaitBits(event_group, flags, pdTRUE, pdTRUE, pdMS_TO_TICKS(time_ms)); return bits & flags; } -bool signal_group::is_any(uint32_t flags) +bool SignalGroup::is_any(uint32_t flags) { return xEventGroupGetBits(event_group) & flags; } -bool signal_group::wait_any(uint32_t flags, uint32_t time_ms) +bool SignalGroup::wait_any(uint32_t flags, uint32_t time_ms) { EventBits_t bits = xEventGroupWaitBits(event_group, flags, pdFALSE, pdFALSE, pdMS_TO_TICKS(time_ms)); return bits & flags; } -signal_group::~signal_group() +SignalGroup::~SignalGroup() { - if (event_group) vEventGroupDelete(event_group); + if (event_group) + vEventGroupDelete(event_group); +} + +Task::Task(size_t stack_size, size_t priority, void *task_param, TaskFunction_t task_function) + :task_handle(nullptr) +{ + BaseType_t ret = xTaskCreate(task_function, "vfs_task", stack_size, task_param, priority, &task_handle); + throw_if_false(ret == pdTRUE, "create vfs task failed"); +} + +Task::~Task() +{ + if (task_handle) + vTaskDelete(task_handle); +} + +void Task::Delete() +{ + vTaskDelete(nullptr); +} + +void Task::Relinquish() +{ + vTaskDelay(1); } } // namespace esp_modem \ No newline at end of file diff --git a/esp_modem/src/esp_modem_primitives_linux.cpp b/esp_modem/src/esp_modem_primitives_linux.cpp index b09c9bbf1..a40915ff1 100644 --- a/esp_modem/src/esp_modem_primitives_linux.cpp +++ b/esp_modem/src/esp_modem_primitives_linux.cpp @@ -13,35 +13,35 @@ // limitations under the License. #include +#include #include "cxx_include/esp_modem_primitives.hpp" - namespace esp_modem { -struct SignalGroup { +struct SignalGroupInternal { std::condition_variable notify; std::mutex m; uint32_t flags{ 0 }; }; -signal_group::signal_group(): event_group(std::make_unique()) +SignalGroup::SignalGroup(): event_group(std::make_unique()) { } -void signal_group::set(uint32_t bits) +void SignalGroup::set(uint32_t bits) { std::unique_lock lock(event_group->m); event_group->flags |= bits; } -void signal_group::clear(uint32_t bits) +void SignalGroup::clear(uint32_t bits) { std::unique_lock lock(event_group->m); event_group->flags &= ~bits; } -bool signal_group::wait(uint32_t flags, uint32_t time_ms) +bool SignalGroup::wait(uint32_t flags, uint32_t time_ms) { std::unique_lock lock(event_group->m); return event_group->notify.wait_for(lock, std::chrono::milliseconds(time_ms), [&]{ @@ -55,18 +55,35 @@ bool signal_group::wait(uint32_t flags, uint32_t time_ms) // , [&]{return flags&event_group->flags; }); } -bool signal_group::is_any(uint32_t flags) +bool SignalGroup::is_any(uint32_t flags) { std::unique_lock lock(event_group->m); return flags&event_group->flags; } -bool signal_group::wait_any(uint32_t flags, uint32_t time_ms) +bool SignalGroup::wait_any(uint32_t flags, uint32_t time_ms) { std::unique_lock lock(event_group->m); return event_group->notify.wait_for(lock, std::chrono::milliseconds(time_ms), [&]{ return flags&event_group->flags; }); } -signal_group::~signal_group() = default; +SignalGroup::~SignalGroup() = default; + +Task::Task(size_t stack_size, size_t priority, void *task_param, TaskFunction_t task_function) +{ +#warning "Define this for linux" +} + +Task::~Task() +{ + +} + +void Task::Delete() {} + +void Task::Relinquish() +{ + usleep(0); +} } // namespace esp_modem \ No newline at end of file diff --git a/esp_modem/src/esp_modem_term_fs.cpp b/esp_modem/src/esp_modem_term_fs.cpp index 390705b04..9ae2ab8e6 100644 --- a/esp_modem/src/esp_modem_term_fs.cpp +++ b/esp_modem/src/esp_modem_term_fs.cpp @@ -12,15 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include #include "cxx_include/esp_modem_dte.hpp" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" #include "esp_log.h" #include "driver/uart.h" #include "esp_modem_config.h" #include "exception_stub.hpp" -#include "esp_vfs_dev.h" static const char *TAG = "fs_terminal"; @@ -33,68 +31,16 @@ struct uart_resource { int fd; }; -struct vfs_task { - explicit vfs_task(size_t stack_size, size_t priority, void *task_param, TaskFunction_t task_function) : - task_handle(nullptr) { - BaseType_t ret = xTaskCreate(task_function, "vfs_task" - "", stack_size, task_param, priority, &task_handle); - throw_if_false(ret == pdTRUE, "create uart event task failed"); - } - - ~vfs_task() { - if (task_handle) vTaskDelete(task_handle); - } - - TaskHandle_t task_handle; /*!< UART event task handle */ -}; - -uart_resource::~uart_resource() -{ - if (port >= UART_NUM_0 && port < UART_NUM_MAX) { - uart_driver_delete(port); - } - if (fd >= 0) { - close(fd); - } -} - -uart_resource::uart_resource(const esp_modem_dte_config *config) : - port(-1), fd(-1) -{ - /* Config UART */ - uart_config_t uart_config = {}; - uart_config.baud_rate = config->vfs_config.baud_rate; - uart_config.data_bits = UART_DATA_8_BITS; - uart_config.parity = UART_PARITY_DISABLE; - uart_config.stop_bits = UART_STOP_BITS_1; - uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE; - uart_config.source_clk = UART_SCLK_REF_TICK; - - throw_if_esp_fail(uart_param_config(config->vfs_config.port_num, &uart_config), "config uart parameter failed"); - - throw_if_esp_fail(uart_set_pin(config->vfs_config.port_num, config->vfs_config.tx_io_num, config->vfs_config.rx_io_num, - UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE), "config uart gpio failed"); - - throw_if_esp_fail(uart_driver_install(config->vfs_config.port_num, config->vfs_config.rx_buffer_size, config->vfs_config.tx_buffer_size, - 0, nullptr, 0), "install uart driver failed"); - - throw_if_esp_fail(uart_set_rx_timeout(config->vfs_config.port_num, 1), "set rx timeout failed"); - - throw_if_esp_fail(uart_set_rx_full_threshold(config->uart_config.port_num, 64), "config rx full threshold failed"); - - /* mark UART as initialized */ - port = config->vfs_config.port_num; - - fd = open(config->vfs_config.dev_name, O_RDWR); - - throw_if_false(fd >= 0, "Cannot open the fd"); -} class vfs_terminal : public Terminal { public: explicit vfs_terminal(const esp_modem_dte_config *config) : uart(config), signal(), - task_handle(config->vfs_config.task_stack_size, config->vfs_config.task_prio, this, s_task) {} + task_handle(config->vfs_config.task_stack_size, config->vfs_config.task_prio, this, [](void* p){ + auto t = static_cast(p); + t->task(); + Task::Delete(); + }) {} ~vfs_terminal() override = default; @@ -116,22 +62,16 @@ public: } private: - static void s_task(void *task_param) { - auto t = static_cast(task_param); - t->task(); - vTaskDelete(NULL); - } - void task(); - static const size_t TASK_INIT = BIT0; - static const size_t TASK_START = BIT1; - static const size_t TASK_STOP = BIT2; - static const size_t TASK_PARAMS = BIT3; + static const size_t TASK_INIT = SignalGroup::bit0; + static const size_t TASK_START = SignalGroup::bit1; + static const size_t TASK_STOP = SignalGroup::bit2; + static const size_t TASK_PARAMS = SignalGroup::bit3; uart_resource uart; - signal_group signal; - vfs_task task_handle; + SignalGroup signal; + Task task_handle; }; std::unique_ptr create_vfs_terminal(const esp_modem_dte_config *config) { @@ -144,13 +84,15 @@ std::unique_ptr create_vfs_terminal(const esp_modem_dte_config *config void vfs_terminal::task() { std::function on_data_priv = nullptr; - size_t len; +// size_t len; signal.set(TASK_INIT); signal.wait_any(TASK_START | TASK_STOP, portMAX_DELAY); if (signal.is_any(TASK_STOP)) { return; // exits to the static method where the task gets deleted } - esp_vfs_dev_uart_use_driver(uart.port); +// esp_vfs_dev_uart_use_driver(uart.port); + int flags = fcntl(uart.fd, F_GETFL, NULL) | O_NONBLOCK; + fcntl(uart.fd, F_SETFL, flags); while (signal.is_any(TASK_START)) { int s; @@ -163,35 +105,36 @@ void vfs_terminal::task() { FD_SET(uart.fd, &rfds); s = select(uart.fd + 1, &rfds, NULL, NULL, &tv); -// if (signal.is_any(TASK_PARAMS)) { + if (signal.is_any(TASK_PARAMS)) { on_data_priv = on_data; -// signal.clear(TASK_PARAMS); -// } + signal.clear(TASK_PARAMS); + } if (s < 0) { - ESP_LOGE(TAG, "Select failed: errno %d", errno); break; } else if (s == 0) { - ESP_LOGI(TAG, "Timeout has been reached and nothing has been received"); + } else { if (FD_ISSET(uart.fd, &rfds)) { - uart_get_buffered_data_len(uart.port, &len); - if (len && on_data_priv) { - if (on_data_priv(nullptr, len)) { +// uart_get_buffered_data_len(uart.port, &len); + if (on_data_priv) { + if (on_data_priv(nullptr, 0)) { on_data_priv = nullptr; } } } } - vTaskDelay(1); + Task::Relinquish(); } } -int vfs_terminal::read(uint8_t *data, size_t len) { +int vfs_terminal::read(uint8_t *data, size_t len) +{ return ::read(uart.fd, data, len); } -int vfs_terminal::write(uint8_t *data, size_t len) { +int vfs_terminal::write(uint8_t *data, size_t len) +{ return ::write(uart.fd, data, len); } diff --git a/esp_modem/src/esp_modem_term_uart.cpp b/esp_modem/src/esp_modem_term_uart.cpp new file mode 100644 index 000000000..f744189e3 --- /dev/null +++ b/esp_modem/src/esp_modem_term_uart.cpp @@ -0,0 +1,77 @@ +// Copyright 2021 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +#include "cxx_include/esp_modem_dte.hpp" +#include "esp_log.h" +#include "driver/uart.h" +#include "esp_modem_config.h" +#include "exception_stub.hpp" +#include "esp_vfs_dev.h" +#include + + + +static const char *TAG = "uart_terminal"; + +namespace esp_modem::terminal { + +struct uart_resource { + explicit uart_resource(const esp_modem_dte_config *config); + ~uart_resource(); + uart_port_t port; + int fd; +}; + +uart_resource::uart_resource(const esp_modem_dte_config *config) : + port(-1), fd(-1) +{ + /* Config UART */ + uart_config_t uart_config = {}; + uart_config.baud_rate = config->vfs_config.baud_rate; + uart_config.data_bits = UART_DATA_8_BITS; + uart_config.parity = UART_PARITY_DISABLE; + uart_config.stop_bits = UART_STOP_BITS_1; + uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE; + uart_config.source_clk = UART_SCLK_REF_TICK; + + throw_if_esp_fail(uart_param_config(config->vfs_config.port_num, &uart_config), "config uart parameter failed"); + + throw_if_esp_fail(uart_set_pin(config->vfs_config.port_num, config->vfs_config.tx_io_num, config->vfs_config.rx_io_num, + UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE), "config uart gpio failed"); + + throw_if_esp_fail(uart_driver_install(config->vfs_config.port_num, config->vfs_config.rx_buffer_size, config->vfs_config.tx_buffer_size, + 0, nullptr, 0), "install uart driver failed"); + +// throw_if_esp_fail(uart_set_rx_timeout(config->vfs_config.port_num, 1), "set rx timeout failed"); +// +// throw_if_esp_fail(uart_set_rx_full_threshold(config->uart_config.port_num, 64), "config rx full threshold failed"); + + /* mark UART as initialized */ + port = config->vfs_config.port_num; + esp_vfs_dev_uart_use_driver(port); + + fd = open(config->vfs_config.dev_name, O_RDWR); + + throw_if_false(fd >= 0, "Cannot open the fd"); +} + +uart_resource::~uart_resource() { + if (port >= UART_NUM_0 && port < UART_NUM_MAX) { + uart_driver_delete(port); + } + +} + +} // namespace esp_modem::terminal diff --git a/esp_modem/src/esp_modem_uart.cpp b/esp_modem/src/esp_modem_uart.cpp index 9155fbf61..eda885cdd 100644 --- a/esp_modem/src/esp_modem_uart.cpp +++ b/esp_modem/src/esp_modem_uart.cpp @@ -150,7 +150,7 @@ private: static const size_t TASK_PARAMS = BIT3; uart_resource uart; - signal_group signal; + SignalGroup signal; uart_task task_handle; }; diff --git a/esp_modem/test/host_test/main/CMakeLists.txt b/esp_modem/test/host_test/main/CMakeLists.txt index 07e4c5852..4e8231452 100644 --- a/esp_modem/test/host_test/main/CMakeLists.txt +++ b/esp_modem/test/host_test/main/CMakeLists.txt @@ -1,4 +1,4 @@ -idf_component_register(SRCS "test_modem.cpp" +idf_component_register(SRCS "test_modem.cpp" "LoopbackTerm.cpp" INCLUDE_DIRS "$ENV{IDF_PATH}/tools/catch" REQUIRES esp_modem) diff --git a/esp_modem/test/host_test/main/LoopbackTerm.cpp b/esp_modem/test/host_test/main/LoopbackTerm.cpp new file mode 100644 index 000000000..c94155645 --- /dev/null +++ b/esp_modem/test/host_test/main/LoopbackTerm.cpp @@ -0,0 +1,82 @@ +#include +#include +#include +#include "LoopbackTerm.h" + +void LoopbackTerm::start() +{ + status = status_t::STARTED; +} + +void LoopbackTerm::stop() +{ + status = status_t::STOPPED; +} + +int LoopbackTerm::write(uint8_t *data, size_t len) +{ + if (len > 2 && (data[len-1] == '\r' || data[len-1] == '+') ) { // Simple AT responder + std::string command((char*)data, len); + std::string response; + if (command == "+++") { + response = "NO CARRIER\r\n"; + } else if (command == "ATE1\r" || command == "ATE0\r") { + response = "OK\r\n"; + } else if (command == "ATO\r") { + response = "ERROR\r\n"; + } else if (command.find("ATD") != std::string::npos) { + response = "CONNECT\r\n"; + } else if (command.find("AT+CSQ\r") != std::string::npos) { + response = "+CSQ: 123,456\n\r\nOK\r\n"; + } else if (command.find("AT+CBC\r") != std::string::npos) { + response = is_bg96 ? "+CBC: 1,2,123456V\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?\r") != std::string::npos) { + response = pin_ok?"+CPIN: READY\r\nOK\r\n":"+CPIN: SIM PIN\r\nOK\r\n"; + } else if (command.find("AT") != std::string::npos) { + response = "OK\r\n"; + } + if (!response.empty()) { + data_len = response.length(); + loopback_data.resize(data_len); + memcpy(&loopback_data[0], &response[0], data_len); + auto ret = std::async(on_data, nullptr, data_len); + return len; + } + } + if (len > 2 && data[0] == 0xf9) { // Simple CMUX responder + // turn the request into a reply -> implements CMUX loopback + if (data[2] == 0x3f) // SABM command + data[2] = 0x73; + else if (data[2] == 0xef) { // Generic request + data[2] = 0xff; // generic reply + } + } + loopback_data.resize(data_len + len); + memcpy(&loopback_data[data_len], data, len); + data_len += len; + auto ret = std::async(on_data, nullptr, data_len); + return len; +} + +int LoopbackTerm::read(uint8_t *data, size_t len) +{ + size_t read_len = std::min(data_len, len); + if (read_len) { + if (loopback_data.capacity() < len) + loopback_data.reserve(len); + memcpy(data, &loopback_data[0], read_len); + loopback_data.erase(loopback_data.begin(), loopback_data.begin() + read_len); + data_len -= read_len; + } + return read_len; +} + +LoopbackTerm::LoopbackTerm(bool is_bg96): loopback_data(), data_len(0), pin_ok(false), is_bg96(is_bg96) {} + +LoopbackTerm::LoopbackTerm(): loopback_data(), data_len(0), pin_ok(false), is_bg96(false) {} + +LoopbackTerm::~LoopbackTerm() = default; diff --git a/esp_modem/test/host_test/main/LoopbackTerm.h b/esp_modem/test/host_test/main/LoopbackTerm.h new file mode 100644 index 000000000..619f2ea2e --- /dev/null +++ b/esp_modem/test/host_test/main/LoopbackTerm.h @@ -0,0 +1,36 @@ +#ifndef _LOOPBACKTERM_H_ +#define _LOOPBACKTERM_H_ + +#include "cxx_include/esp_modem_api.hpp" +#include "cxx_include/esp_modem_terminal.hpp" + +using namespace esp_modem; + +class LoopbackTerm : public Terminal { +public: + explicit LoopbackTerm(bool is_bg96); + explicit LoopbackTerm(); + + ~LoopbackTerm() override; + + void start() override; + void stop() override; + + int write(uint8_t *data, size_t len) override; + + int read(uint8_t *data, size_t len) override; + +private: + enum class status_t { + STARTED, + STOPPED + }; + status_t status; + SignalGroup signal; + std::vector loopback_data; + size_t data_len; + bool pin_ok; + bool is_bg96; +}; + +#endif //_LOOPBACKTERM_H_ diff --git a/esp_modem/test/host_test/main/test_modem.cpp b/esp_modem/test/host_test/main/test_modem.cpp index da5bc4d1e..f730e04a2 100644 --- a/esp_modem/test/host_test/main/test_modem.cpp +++ b/esp_modem/test/host_test/main/test_modem.cpp @@ -2,82 +2,44 @@ #include #include #include "catch.hpp" -#include "cxx_include/esp_modem_terminal.hpp" #include "cxx_include/esp_modem_api.hpp" +#include "LoopbackTerm.h" using namespace esp_modem; -class LoopbackTerm : public Terminal { -public: - explicit LoopbackTerm(): loopback_data(), data_len(0) {} +TEST_CASE("DCE AT parser", "[esp_modem]") +{ + auto term = std::make_unique(true); + auto dte = std::make_shared(std::move(term)); + CHECK(term == nullptr); - ~LoopbackTerm() override = default; + 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); - void start() override { - status = status_t::STARTED; - } + CHECK(dce->set_command_mode() == command_result::OK); - void stop() override { - status = status_t::STOPPED; - } + int milli_volt, bcl, bcs; + CHECK(dce->get_battery_status(milli_volt, bcl, bcs) == command_result::OK); + CHECK(milli_volt == 123456); + CHECK(bcl == 1); + CHECK(bcs == 2); - int write(uint8_t *data, size_t len) override { - if (len > 2 && (data[len-1] == '\r' || data[len-1] == '+') ) { // Simple AT responder - std::string command((char*)data, len); - std::string response; - if (command == "ATE1\r" || command == "ATE0\r" || command == "+++") { - response = "OK\r\n"; - } else if (command == "ATO\r") { - response = "ERROR\r\n"; - } else if (command.find("ATD") != std::string::npos) { - response = "CONNECT\r\n"; - } else if (command.find("AT") != std::string::npos) { - response = "OK\r\n"; - } - if (!response.empty()) { - data_len = response.length(); - loopback_data.resize(data_len); - memcpy(&loopback_data[0], &response[0], data_len); - auto ret = std::async(on_data, nullptr, data_len); - return len; - } - } - if (len > 2 && data[0] == 0xf9) { // Simple CMUX responder - // turn the request into a reply -> implements CMUX loopback - if (data[2] == 0x3f) // SABM command - data[2] = 0x73; - else if (data[2] == 0xef) // Generic request - data[2] = 0xff; // generic reply - } - loopback_data.resize(data_len + len); - memcpy(&loopback_data[data_len], data, len); - data_len += len; - auto ret = std::async(on_data, nullptr, data_len); - return len; - } + int rssi, ber; + CHECK(dce->get_signal_quality(rssi, ber) == command_result::OK); + CHECK(rssi == 123); + CHECK(ber == 456); - int read(uint8_t *data, size_t len) override { - size_t read_len = std::min(data_len, len); - if (read_len) { - memcpy(data, &loopback_data[0], len); - loopback_data.erase(loopback_data.begin(), loopback_data.begin() + read_len); - data_len -= len; - } - return read_len; - } + bool pin_ok; + CHECK(dce->read_pin(pin_ok) == command_result::OK); + CHECK(pin_ok == false); + CHECK(dce->set_pin("1234") == command_result::OK); + CHECK(dce->read_pin(pin_ok) == command_result::OK); + CHECK(pin_ok == true); +} -private: - enum class status_t { - STARTED, - STOPPED - }; - status_t status; - signal_group signal; - std::vector loopback_data; - size_t data_len; -}; - TEST_CASE("DTE send/receive command", "[esp_modem]") { auto term = std::make_unique(); @@ -127,11 +89,15 @@ TEST_CASE("DCE AT commands", "[esp_modem]") auto dce = create_SIM7600_dce(&dce_config, dte, &netif); CHECK(dce != nullptr); + int milli_volt, bcl, bcs; CHECK(dce->set_echo(false) == command_result::OK); CHECK(dce->set_echo(true) == command_result::OK); + CHECK(dce->get_battery_status(milli_volt, bcl, bcs) == command_result::OK); + CHECK(milli_volt == 123456); CHECK(dce->resume_data_mode() == command_result::FAIL); } + TEST_CASE("DCE modes", "[esp_modem]") { auto term = std::make_unique(); @@ -162,8 +128,9 @@ TEST_CASE("DCE CMUX test", "[esp_modem]") { const auto test_command = "Test\n"; auto ret = dce->command(test_command, [&](uint8_t *data, size_t len) { std::string response((char *) data, len); + std::cout << "Response:" << response << std::endl; CHECK(response == test_command); return command_result::OK; }, 1000); CHECK(ret == command_result::OK); -} \ No newline at end of file +} diff --git a/esp_modem/test/host_test/my.log b/esp_modem/test/host_test/my.log deleted file mode 100644 index 092f4efe3..000000000 --- a/esp_modem/test/host_test/my.log +++ /dev/null @@ -1,31 +0,0 @@ --- Found Git: /usr/bin/git (found version "2.30.2") --- Component directory /home/david/esp/idf/components/aws_iot does not contain a CMakeLists.txt file. No component will be added --- Component directory /home/david/esp/idf/components/micro-ecc does not contain a CMakeLists.txt file. No component will be added --- Component directory /home/david/esp/idf/components/nimble does not contain a CMakeLists.txt file. No component will be added --- ccache will be used for faster recompilation --- The C compiler identification is GNU 10.2.1 --- The CXX compiler identification is GNU 10.2.1 --- The ASM compiler identification is GNU --- Found assembler: /usr/lib64/ccache/cc --- Detecting C compiler ABI info --- Detecting C compiler ABI info - done --- Check for working C compiler: /usr/lib64/ccache/cc - skipped --- Detecting C compile features --- Detecting C compile features - done --- Detecting CXX compiler ABI info --- Detecting CXX compiler ABI info - done --- Check for working CXX compiler: /usr/lib64/ccache/c++ - skipped --- Detecting CXX compile features --- Detecting CXX compile features - done --- Building ESP-IDF components for target linux --- adding linux stuff... --- Found Git: /usr/bin/git (found version "2.30.2") --- Configuring incomplete, errors occurred! -See also "/home/david/repos/esp-idf-contrib/esp_modem/test/host_test/build/CMakeFiles/CMakeOutput.log". -Adding "set-target"'s dependency "fullclean" to list of commands with default set of options. -Executing action: fullclean -Build directory '/home/david/repos/esp-idf-contrib/esp_modem/test/host_test/build' not found. Nothing to clean. -Executing action: set-target -Set Target to: linux, new sdkconfig created. Existing sdkconfig renamed to sdkconfig.old. -Running cmake in directory /home/david/repos/esp-idf-contrib/esp_modem/test/host_test/build -Executing "cmake -G Ninja -DPYTHON_DEPS_CHECKED=1 -DESP_PLATFORM=1 -DIDF_TARGET=linux -DIDF_TARGET=linux -DCCACHE_ENABLE=1 /home/david/repos/esp-idf-contrib/esp_modem/test/host_test"... diff --git a/esp_modem/test/target/main/NetworkDCE.cpp b/esp_modem/test/target/main/NetworkDCE.cpp index bf6692a78..aacca55dd 100644 --- a/esp_modem/test/target/main/NetworkDCE.cpp +++ b/esp_modem/test/target/main/NetworkDCE.cpp @@ -54,10 +54,23 @@ public: { // configure esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG(); + esp_modem_dte_config_t dte_config2 = { + .dte_buffer_size = 512, + .vfs_config = {.port_num = UART_NUM_1, + .dev_name = "/dev/uart/1", + .rx_buffer_size = 1024, + .tx_buffer_size = 1024, + .baud_rate = 115200, + .tx_io_num = 25, + .rx_io_num = 26, + .task_stack_size = 4096, + .task_prio = 5} + }; esp_modem_dce_config dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(""); // create DTE and minimal network DCE - auto uart_dte = create_uart_dte(&dte_config); + auto uart_dte = create_vfs_dte(&dte_config2); +// auto uart_dte = create_uart_dte(&dte_config); dce = NetDCE_Factory::create(&dce_config, uart_dte, netif); return dce == nullptr ? ESP_FAIL : ESP_OK; }