From ceedcfca232e92c33e86becd8b00eef8ba3b44d0 Mon Sep 17 00:00:00 2001 From: Jonathan Dreyer Date: Sun, 22 Jan 2023 19:03:07 +0100 Subject: [PATCH] fix(modem): AT-only example: initial adaptation to BG96 Plus rework a little to support implementation of multiple devices --- .../modem_tcp_client/main/CMakeLists.txt | 8 +- .../modem_tcp_client/main/Kconfig.projbuild | 52 ---- .../modem_tcp_client/main/modem_client.cpp | 8 +- .../main/sock_commands_bg96.cpp | 273 ++++++++++++++++++ ...commands.cpp => sock_commands_sim7600.cpp} | 170 +++++++++++ .../modem_tcp_client/main/sock_dce.cpp | 174 ++--------- .../modem_tcp_client/main/sock_dce.hpp | 20 +- 7 files changed, 482 insertions(+), 223 deletions(-) create mode 100644 components/esp_modem/examples/modem_tcp_client/main/sock_commands_bg96.cpp rename components/esp_modem/examples/modem_tcp_client/main/{sock_commands.cpp => sock_commands_sim7600.cpp} (52%) diff --git a/components/esp_modem/examples/modem_tcp_client/main/CMakeLists.txt b/components/esp_modem/examples/modem_tcp_client/main/CMakeLists.txt index 3a1289c52..b7d3874ab 100644 --- a/components/esp_modem/examples/modem_tcp_client/main/CMakeLists.txt +++ b/components/esp_modem/examples/modem_tcp_client/main/CMakeLists.txt @@ -1,5 +1,11 @@ +if (CONFIG_EXAMPLE_MODEM_DEVICE_BG96) + set(device_srcs sock_commands_bg96.cpp) +elseif(CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600) + set(device_srcs sock_commands_sim7600.cpp) +endif() + idf_component_register(SRCS "modem_client.cpp" "sock_dce.cpp" - "sock_commands.cpp" + "${device_srcs}" INCLUDE_DIRS ".") target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") diff --git a/components/esp_modem/examples/modem_tcp_client/main/Kconfig.projbuild b/components/esp_modem/examples/modem_tcp_client/main/Kconfig.projbuild index 7f423343c..cf8e189a2 100644 --- a/components/esp_modem/examples/modem_tcp_client/main/Kconfig.projbuild +++ b/components/esp_modem/examples/modem_tcp_client/main/Kconfig.projbuild @@ -5,11 +5,6 @@ menu "Example Configuration" default EXAMPLE_MODEM_DEVICE_BG96 help Select modem device connected to the ESP DTE. - config EXAMPLE_MODEM_DEVICE_SIM800 - bool "SIM800" - help - SIMCom SIM800L is a GSM/GPRS module. - It supports Quad-band 850/900/1800/1900MHz. config EXAMPLE_MODEM_DEVICE_BG96 bool "BG96" help @@ -26,53 +21,6 @@ menu "Example Configuration" help Set APN (Access Point Name), a logical name to choose data network - config EXAMPLE_MODEM_PPP_AUTH_USERNAME - string "Set username for authentication" - default "espressif" - depends on !EXAMPLE_MODEM_PPP_AUTH_NONE - help - Set username for PPP Authentication. - - config EXAMPLE_MODEM_PPP_AUTH_PASSWORD - string "Set password for authentication" - default "esp32" - depends on !EXAMPLE_MODEM_PPP_AUTH_NONE - help - Set password for PPP Authentication. - - config EXAMPLE_MODEM_PPP_AUTH_NONE - bool "Skip PPP authentication" - default n - help - Set to true for the PPP client to skip authentication - - config EXAMPLE_SEND_MSG - bool "Short message (SMS)" - default n - help - Select this, the modem will send a short message before power off. - - if EXAMPLE_SEND_MSG - config EXAMPLE_SEND_MSG_PEER_PHONE_NUMBER - string "Peer Phone Number (with area code)" - default "+8610086" - help - Enter the peer phone number that you want to send message to. - endif - - config EXAMPLE_NEED_SIM_PIN - bool "SIM PIN needed" - default n - help - Enable to set SIM PIN before starting the example - - config EXAMPLE_SIM_PIN - string "Set SIM PIN" - default "1234" - depends on EXAMPLE_NEED_SIM_PIN - help - Pin to unlock the SIM - menu "UART Configuration" config EXAMPLE_MODEM_UART_TX_PIN int "TXD Pin Number" diff --git a/components/esp_modem/examples/modem_tcp_client/main/modem_client.cpp b/components/esp_modem/examples/modem_tcp_client/main/modem_client.cpp index d843c4699..bfbde426b 100644 --- a/components/esp_modem/examples/modem_tcp_client/main/modem_client.cpp +++ b/components/esp_modem/examples/modem_tcp_client/main/modem_client.cpp @@ -97,7 +97,7 @@ extern "C" void app_main(void) assert(dte); /* Configure the DCE */ - esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_EXAMPLE_MODEM_APN); + esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("lpwa.vodafone.com"); /* create the DCE and initialize network manually (using AT commands) */ auto dce = sock_dce::create(&dce_config, std::move(dte)); @@ -106,10 +106,10 @@ extern "C" void app_main(void) return; } - dce->init(8883); + dce->init(1883); esp_mqtt_client_config_t mqtt_config = {}; #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) - mqtt_config.broker.address.uri = "mqtts://127.0.0.1"; + mqtt_config.broker.address.uri = "mqtt://127.0.0.1"; mqtt_config.session.message_retransmit_timeout = 10000; #else mqtt_config.uri = "mqtt://127.0.0.1"; @@ -118,7 +118,7 @@ extern "C" void app_main(void) esp_mqtt_client_handle_t mqtt_client = esp_mqtt_client_init(&mqtt_config); esp_mqtt_client_register_event(mqtt_client, static_cast(ESP_EVENT_ANY_ID), mqtt_event_handler, NULL); esp_mqtt_client_start(mqtt_client); - if (!dce->start(BROKER_URL, 8883)) { + if (!dce->start(BROKER_URL, 1883)) { ESP_LOGE(TAG, "Failed to start DCE"); return; } diff --git a/components/esp_modem/examples/modem_tcp_client/main/sock_commands_bg96.cpp b/components/esp_modem/examples/modem_tcp_client/main/sock_commands_bg96.cpp new file mode 100644 index 000000000..3f8c56d18 --- /dev/null +++ b/components/esp_modem/examples/modem_tcp_client/main/sock_commands_bg96.cpp @@ -0,0 +1,273 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "sock_commands.hpp" +#include "cxx_include/esp_modem_command_library_utils.hpp" +#include "sock_dce.hpp" + +static const char *TAG = "sock_commands"; + +namespace sock_commands { + +using namespace esp_modem; + +command_result net_open(CommandableIf *t) +{ + ESP_LOGV(TAG, "%s", __func__ ); + std::string out; + auto ret = dce_commands::generic_get_string(t, "AT+QISTATE?\r", out, 1000); + if (ret != command_result::OK) { + return ret; + } + if (out.find("+QISTATE: 0") != std::string::npos) { + ESP_LOGV(TAG, "%s", out.data() ); + ESP_LOGD(TAG, "Already there"); + return command_result::OK; + } else if (out.empty()) { + return dce_commands::generic_command(t, "AT+QIACT=1\r", "OK", "ERROR", 150000); + } + return command_result::FAIL; +} + +command_result net_close(CommandableIf *t) +{ + ESP_LOGV(TAG, "%s", __func__ ); + return dce_commands::generic_command(t, "AT+QIDEACT=1\r", "OK", "ERROR", 40000); +} + +command_result tcp_open(CommandableIf *t, const std::string &host, int port, int timeout) +{ + ESP_LOGV(TAG, "%s", __func__ ); + std::string ip_open = R"(AT+QIOPEN=1,0,"TCP",")" + host + "\"," + std::to_string(port) + "\r"; + auto ret = dce_commands::generic_command(t, ip_open, "+QIOPEN: 0,0", "ERROR", timeout); + if (ret != command_result::OK) { + ESP_LOGE(TAG, "%s Failed", __func__ ); + return ret; + } + return command_result::OK; +} + +command_result tcp_close(CommandableIf *t) +{ + ESP_LOGV(TAG, "%s", __func__ ); + return dce_commands::generic_command(t, "AT+QICLOSE=0\r", "OK", "ERROR", 10000); +} + +command_result tcp_send(CommandableIf *t, uint8_t *data, size_t len) +{ + ESP_LOGV(TAG, "%s", __func__ ); + assert(0); // Remove when fix done + return command_result::FAIL; +} + +command_result tcp_recv(CommandableIf *t, uint8_t *data, size_t len, size_t &out_len) +{ + ESP_LOGV(TAG, "%s", __func__ ); + assert(0); // Remove when fix done + return command_result::FAIL; +} + +command_result get_ip(CommandableIf *t, std::string &ip) +{ + ESP_LOGV(TAG, "%s", __func__ ); + std::string out; + auto ret = dce_commands::generic_get_string(t, "AT+QIACT?\r", out, 5000); + if (ret != command_result::OK) { + return ret; + } + auto pos = out.find("+QIACT: 1"); + auto property = 0; + while (pos != std::string::npos) { + // Looking for: +QIACT: ,,, + if (property++ == 3) { // ip is after 3rd comma (as a 4rd property of QIACT string) + ip = out.substr(++pos); + // strip quotes if present + auto quote1 = ip.find('"'); + auto quote2 = ip.rfind('"'); + if (quote1 != std::string::npos && quote2 != std::string::npos) { + ip = ip.substr(quote1 + 1, quote2 - 1); + } + return command_result::OK; + } + pos = out.find(',', ++pos); + } + return command_result::FAIL; +} + +} // sock_commands + +namespace sock_dce { + +void Listener::start_sending(size_t len) +{ + data_to_send = len; + send_stat = 0; + send_cmd("AT+QISEND=0," + std::to_string(len) + "\r"); +} + +void Listener::start_receiving(size_t len) +{ + send_cmd("AT+QIRD=0," + std::to_string(size) + "\r"); +} + +bool Listener::start_connecting(std::string host, int port) +{ + send_cmd(R"(AT+QIOPEN=1,0,"TCP",")" + host + "\"," + std::to_string(port) + "\r"); + return true; +} + +Listener::state Listener::recv(uint8_t *data, size_t len) +{ + const size_t MIN_MESSAGE = 6; + const std::string_view head = "+QIRD: "; + auto head_pos = (char *)std::search(data, data + len, head.begin(), head.end()); + if (head_pos == nullptr) { + return state::FAIL; + } + + auto next_nl = (char *)memchr(head_pos + head.size(), '\n', MIN_MESSAGE); + if (next_nl == nullptr) { + return state::FAIL; + } + + size_t actual_len; + if (std::from_chars(head_pos + head.size(), next_nl, actual_len).ec == std::errc::invalid_argument) { + ESP_LOGE(TAG, "cannot convert"); + return state::FAIL; + } + + ESP_LOGD(TAG, "Received: actual len=%d", actual_len); + if (actual_len == 0) { + ESP_LOGD(TAG, "no data received"); + return state::FAIL; + } + + // TODO improve : compare *actual_len* & data size (to be sure that received data is equal to *actual_len*) + if (actual_len > size) { + ESP_LOGE(TAG, "TOO BIG"); + return state::FAIL; + } + ::send(sock, next_nl + 1, actual_len, 0); + + // "OK" after the data + auto last_pos = (char *)memchr(next_nl + 1 + actual_len, 'O', MIN_MESSAGE); + if (last_pos == nullptr || last_pos[1] != 'K') { + return state::FAIL; + } + if ((char *)data + len - last_pos > MIN_MESSAGE) { + // check for async replies after the Recv header + std::string_view response((char *)last_pos + 2 /* OK */, (char *)data + len - last_pos); + check_async_replies(response); + } + return state::OK; +} + + +Listener::state Listener::send(uint8_t *data, size_t len) +{ + if (send_stat == 0) { + if (memchr(data, '>', len) == NULL) { + ESP_LOGE(TAG, "Missed >"); + return state::FAIL; + } + auto written = dte->write(&buffer[0], data_to_send); + if (written != data_to_send) { + ESP_LOGE(TAG, "written %d (%d)...", written, len); + return state::FAIL; + } + data_to_send = 0; + send_stat++; + } + return Listener::state::IN_PROGRESS; +} + +Listener::state Listener::send(std::string_view response) +{ + if (send_stat == 1) { + if (response.find("SEND OK") != std::string::npos) { + send_cmd("AT+QISEND=0,0\r"); + send_stat++; + } else if (response.find("SEND FAIL") != std::string::npos) { + ESP_LOGE(TAG, "Sending buffer full"); + return state::FAIL; + } else if (response.find("ERROR") != std::string::npos) { + ESP_LOGE(TAG, "Failed to sent"); + return state::FAIL; + } + } else if (send_stat == 2) { + constexpr std::string_view head = "+QISEND: "; + if (response.find(head) != std::string::npos) { + // Parsing +QISEND: ,, + size_t head_pos = response.find(head); + response = response.substr(head_pos + head.size()); + int pos, property = 0; + int total = 0, ack = 0, unack = 0; + while ((pos = response.find(',')) != std::string::npos) { + auto next_comma = (char *)memchr(response.data(), ',', response.size()); + + // extract value + size_t value; + if (std::from_chars(response.data(), next_comma, value).ec == std::errc::invalid_argument) { + ESP_LOGE(TAG, "cannot convert"); + return state::FAIL; + } + + switch (property++) { + case 0: total = value; + break; + case 1: ack = value; + break; + default: + return state::FAIL; + } + response = response.substr(pos + 1); + } + if (std::from_chars(response.data(), response.data() + pos, unack).ec == std::errc::invalid_argument) { + return state::FAIL; + } + + // TODO improve : need check *total* & *ack* values, or loop (every 5 sec) with 90s or 120s timeout + if (ack < total) { + ESP_LOGE(TAG, "all sending data are not ack (missing %d bytes acked)", (total - ack)); + } + return state::OK; + } else if (response.find("ERROR") != std::string::npos) { + ESP_LOGE(TAG, "Failed to check sending"); + return state::FAIL; + } + + } + return Listener::state::IN_PROGRESS; +} + +Listener::state Listener::connect(std::string_view response) +{ + if (response.find("+QIOPEN: 0,0") != std::string::npos) { + ESP_LOGI(TAG, "Connected!"); + return state::OK; + } + if (response.find("ERROR") != std::string::npos) { + ESP_LOGE(TAG, "Failed to open"); + return state::FAIL; + } + return Listener::state::IN_PROGRESS; +} + +void Listener::check_async_replies(std::string_view &response) const +{ + ESP_LOGD(TAG, "response %.*s", static_cast(response.size()), response.data()); + if (response.find("+QIURC: \"recv\",0") != std::string::npos) { + uint64_t data_ready = 1; + write(data_ready_fd, &data_ready, sizeof(data_ready)); + ESP_LOGD(TAG, "Got data on modem!"); + } +} + + +} // sock_dce diff --git a/components/esp_modem/examples/modem_tcp_client/main/sock_commands.cpp b/components/esp_modem/examples/modem_tcp_client/main/sock_commands_sim7600.cpp similarity index 52% rename from components/esp_modem/examples/modem_tcp_client/main/sock_commands.cpp rename to components/esp_modem/examples/modem_tcp_client/main/sock_commands_sim7600.cpp index f091cac3b..fa146226f 100644 --- a/components/esp_modem/examples/modem_tcp_client/main/sock_commands.cpp +++ b/components/esp_modem/examples/modem_tcp_client/main/sock_commands_sim7600.cpp @@ -8,6 +8,7 @@ #include #include "sock_commands.hpp" #include "cxx_include/esp_modem_command_library_utils.hpp" +#include "sock_dce.hpp" namespace sock_commands { @@ -181,6 +182,175 @@ command_result set_rx_mode(CommandableIf *term, int mode) return dce_commands::generic_command(term, "AT+CIPRXGET=" + std::to_string(mode) + "\r", "OK", "ERROR", 5000); } +} // sock_commands +namespace sock_dce { + +void Listener::start_sending(size_t len) +{ + data_to_send = len; + send_stat = 0; + send_cmd("AT+CIPSEND=0," + std::to_string(len) + "\r"); +} + +void Listener::start_receiving(size_t len) +{ + send_cmd("AT+CIPRXGET=2,0," + std::to_string(size) + "\r"); +} + +bool Listener::start_connecting(std::string host, int port) +{ + if (esp_modem::dce_commands::generic_command(dte.get(), "AT+CIPRXGET=1\r", "OK", "ERROR", 5000) != esp_modem::command_result::OK) { + return false; + } + send_cmd(R"(AT+CIPOPEN=0,"TCP",")" + host + "\"," + std::to_string(port) + "\r"); + return true; +} + +Listener::state Listener::recv(uint8_t *data, size_t len) +{ + const int MIN_MESSAGE = 6; + size_t actual_len = 0; + auto *recv_data = (char *)data; + if (data_to_recv == 0) { + static constexpr std::string_view head = "+CIPRXGET: 2,0,"; + auto head_pos = std::search(recv_data, recv_data + len, head.begin(), head.end()); + if (head_pos == nullptr) { + return state::FAIL; + } +// state = status::RECEIVING_FAILED; +// signal.set(IDLE); +// return; +// } + if (head_pos - (char *)data > MIN_MESSAGE) { + // check for async replies before the Recv header + std::string_view response((char *)data, head_pos - (char *)data); + check_async_replies(response); + } + + auto next_comma = (char *)memchr(head_pos + head.size(), ',', MIN_MESSAGE); + if (next_comma == nullptr) { + return state::FAIL; + } + if (std::from_chars(head_pos + head.size(), next_comma, actual_len).ec == std::errc::invalid_argument) { + ESP_LOGE(TAG, "cannot convert"); + return state::FAIL; + } + + auto next_nl = (char *)memchr(next_comma, '\n', 8 /* total_len size (~4) + markers */); + if (next_nl == nullptr) { + ESP_LOGE(TAG, "not found"); + return state::FAIL; + } + if (actual_len > size) { + ESP_LOGE(TAG, "TOO BIG"); + return state::FAIL; + } + size_t total_len = 0; + if (std::from_chars(next_comma + 1, next_nl - 1, total_len).ec == std::errc::invalid_argument) { + ESP_LOGE(TAG, "cannot convert"); + return state::FAIL; + } + read_again = (total_len > 0); + recv_data = next_nl + 1; + auto first_data_len = len - (recv_data - (char *)data) /* minus size of the command marker */; + if (actual_len > first_data_len) { + ::send(sock, recv_data, first_data_len, 0); + data_to_recv = actual_len - first_data_len; + return state::IN_PROGRESS; + } + ::send(sock, recv_data, actual_len, 0); + } else if (data_to_recv > len) { // continue sending + ::send(sock, recv_data, len, 0); + data_to_recv -= len; + return state::IN_PROGRESS; + } else if (data_to_recv <= len) { // last read -> looking for "OK" marker + ::send(sock, recv_data, data_to_recv, 0); + actual_len = data_to_recv; + } + + // "OK" after the data + char *last_pos = nullptr; + if (actual_len + 1 + 2 /* OK */ > len) { + last_pos = (char *)memchr(recv_data + 1 + actual_len, 'O', MIN_MESSAGE); + if (last_pos == nullptr || last_pos[1] != 'K') { + data_to_recv = 0; + return state::FAIL; + } + } + if (last_pos != nullptr && (char *)data + len - last_pos > MIN_MESSAGE) { + // check for async replies after the Recv header + std::string_view response((char *)last_pos + 2 /* OK */, (char *)data + len - last_pos - 2); + check_async_replies(response); + } + data_to_recv = 0; + if (read_again) { + uint64_t data_ready = 1; + write(data_ready_fd, &data_ready, sizeof(data_ready)); + } + return state::OK; +} + +Listener::state Listener::send(uint8_t *data, size_t len) +{ + if (send_stat == 0) { + if (memchr(data, '>', len) == NULL) { + ESP_LOGE(TAG, "Missed >"); + return state::FAIL; + } + auto written = dte->write(&buffer[0], data_to_send); + if (written != data_to_send) { + ESP_LOGE(TAG, "written %d (%d)...", written, len); + return state::FAIL; + } + data_to_send = 0; + uint8_t ctrl_z = '\x1A'; + dte->write(&ctrl_z, 1); + send_stat++; + return state::IN_PROGRESS; + } + return Listener::state::IN_PROGRESS; +} + +Listener::state Listener::send(std::string_view response) +{ + if (send_stat == 1) { + if (response.find("+CIPSEND:") != std::string::npos) { + send_stat = 0; + return state::OK; + } + if (response.find("ERROR") != std::string::npos) { + ESP_LOGE(TAG, "Failed to sent"); + send_stat = 0; + return state::FAIL; + } + } + return Listener::state::IN_PROGRESS; +} + +Listener::state Listener::connect(std::string_view response) +{ + if (response.find("+CIPOPEN: 0,0") != std::string::npos) { + ESP_LOGI(TAG, "Connected!"); + return state::OK; + } + if (response.find("ERROR") != std::string::npos) { + ESP_LOGE(TAG, "Failed to open"); + return state::FAIL; + } + return Listener::state::IN_PROGRESS; +} + +void Listener::check_async_replies(std::string_view &response) const +{ + ESP_LOGD(TAG, "response %.*s", static_cast(response.size()), response.data()); + if (response.find("+CIPRXGET: 1") != std::string::npos) { + uint64_t data_ready = 1; + write(data_ready_fd, &data_ready, sizeof(data_ready)); + ESP_LOGD(TAG, "Got data on modem!"); + } } + + +} // sock_dce diff --git a/components/esp_modem/examples/modem_tcp_client/main/sock_dce.cpp b/components/esp_modem/examples/modem_tcp_client/main/sock_dce.cpp index a7d23cdf0..ec189f206 100644 --- a/components/esp_modem/examples/modem_tcp_client/main/sock_dce.cpp +++ b/components/esp_modem/examples/modem_tcp_client/main/sock_dce.cpp @@ -68,9 +68,10 @@ void DCE::forwarding(uint8_t *data, size_t len) signal.set(IDLE); return; case Listener::state::IN_PROGRESS: - return; + break; +// return; } - } else if (state == status::RECEIVING || state == status::RECEIVING_1 ) { + } else if (state == status::RECEIVING) { switch (at.recv(data, len)) { case Listener::state::OK: state = status::IDLE; @@ -81,6 +82,7 @@ void DCE::forwarding(uint8_t *data, size_t len) signal.set(IDLE); return; case Listener::state::IN_PROGRESS: +// break; return; } } @@ -123,6 +125,15 @@ void DCE::close_sock() close(sock); sock = -1; } + const int retries = 5; + int i = 0; + while (net_close() != esp_modem::command_result::OK) { + if (i++ > retries) { + ESP_LOGE(TAG, "Failed to close network"); + return; + } + esp_modem::Task::Delay(1000); + } } bool DCE::at_to_sock() @@ -141,7 +152,7 @@ bool DCE::at_to_sock() return false; } state = status::RECEIVING; - send_cmd("AT+CIPRXGET=2,0," + std::to_string(size) + "\r"); + at.start_receiving(size); return true; } @@ -170,8 +181,7 @@ bool DCE::sock_to_at() return false; } ESP_LOG_BUFFER_HEXDUMP(TAG, &buffer[0], len, ESP_LOG_VERBOSE); - data_to_send = len; - send_cmd("AT+CIPSEND=0," + std::to_string(len) + "\r"); + at.start_sending(len); return true; } @@ -238,30 +248,19 @@ void DCE::init(int port) } -void Listener::check_async_replies(std::string_view &response) const -{ - ESP_LOGD(TAG, "response %.*s", static_cast(response.size()), response.data()); - if (response.find("+CIPRXGET: 1") != std::string::npos) { - uint64_t data_ready = 1; - write(data_ready_fd, &data_ready, sizeof(data_ready)); - ESP_LOGD(TAG, "Got data on modem!"); - } - -} - bool DCE::start(std::string host, int port) { dte->on_read(nullptr); tcp_close(); - if (set_rx_mode(1) != esp_modem::command_result::OK) { - ESP_LOGE(TAG, "Unable to set Rx mode"); - return false; - } dte->on_read([this](uint8_t *data, size_t len) { this->forwarding(data, len); return esp_modem::command_result::TIMEOUT; }); - send_cmd(R"(AT+CIPOPEN=0,"TCP",")" + host + "\"," + std::to_string(port) + "\r"); + if (!at.start_connecting(host, port)) { + ESP_LOGE(TAG, "Unable to start connecting"); + dte->on_read(nullptr); + return false; + } state = status::CONNECTING; return true; } @@ -345,139 +344,6 @@ DECLARE_SOCK_COMMANDS(return_type name(...) ) -Listener::state Listener::recv(uint8_t *data, size_t len) -{ - const int MIN_MESSAGE = 6; - size_t actual_len = 0; - auto *recv_data = (char *)data; - if (data_to_recv == 0) { - static constexpr std::string_view head = "+CIPRXGET: 2,0,"; - auto head_pos = std::search(recv_data, recv_data + len, head.begin(), head.end()); - if (head_pos == nullptr) { - return state::FAIL; - } -// state = status::RECEIVING_FAILED; -// signal.set(IDLE); -// return; -// } - if (head_pos - (char *)data > MIN_MESSAGE) { - // check for async replies before the Recv header - std::string_view response((char *)data, head_pos - (char *)data); - check_async_replies(response); - } - - auto next_comma = (char *)memchr(head_pos + head.size(), ',', MIN_MESSAGE); - if (next_comma == nullptr) { - return state::FAIL; - } - if (std::from_chars(head_pos + head.size(), next_comma, actual_len).ec == std::errc::invalid_argument) { - ESP_LOGE(TAG, "cannot convert"); - return state::FAIL; - } - - auto next_nl = (char *)memchr(next_comma, '\n', 8 /* total_len size (~4) + markers */); - if (next_nl == nullptr) { - ESP_LOGE(TAG, "not found"); - return state::FAIL; - } - if (actual_len > size) { - ESP_LOGE(TAG, "TOO BIG"); - return state::FAIL; - } - size_t total_len = 0; - if (std::from_chars(next_comma + 1, next_nl - 1, total_len).ec == std::errc::invalid_argument) { - ESP_LOGE(TAG, "cannot convert"); - return state::FAIL; - } - read_again = (total_len > 0); - recv_data = next_nl + 1; - auto first_data_len = len - (recv_data - (char *)data) /* minus size of the command marker */; - if (actual_len > first_data_len) { - ::send(sock, recv_data, first_data_len, 0); - data_to_recv = actual_len - first_data_len; - return state::IN_PROGRESS; - } - ::send(sock, recv_data, actual_len, 0); - } else if (data_to_recv > len) { // continue sending - ::send(sock, recv_data, len, 0); - data_to_recv -= len; - return state::IN_PROGRESS; - } else if (data_to_recv <= len) { // last read -> looking for "OK" marker - ::send(sock, recv_data, data_to_recv, 0); - actual_len = data_to_recv; - } - - // "OK" after the data - char *last_pos = nullptr; - if (actual_len + 1 + 2 /* OK */ > len) { - last_pos = (char *)memchr(recv_data + 1 + actual_len, 'O', MIN_MESSAGE); - if (last_pos == nullptr || last_pos[1] != 'K') { - data_to_recv = 0; - return state::FAIL; - } - } - if (last_pos != nullptr && (char *)data + len - last_pos > MIN_MESSAGE) { - // check for async replies after the Recv header - std::string_view response((char *)last_pos + 2 /* OK */, (char *)data + len - last_pos - 2); - check_async_replies(response); - } - data_to_recv = 0; - if (read_again) { - uint64_t data_ready = 1; - write(data_ready_fd, &data_ready, sizeof(data_ready)); - } - return state::OK; -} - -Listener::state Listener::send(uint8_t *data, size_t len) -{ - if (send_stat == 0) { - if (memchr(data, '>', len) == NULL) { - ESP_LOGE(TAG, "Missed >"); - return state::FAIL; - } - auto written = dte->write(&buffer[0], data_to_send); - if (written != data_to_send) { - ESP_LOGE(TAG, "written %d (%d)...", written, len); - return state::FAIL; - } - data_to_send = 0; - uint8_t ctrl_z = '\x1A'; - dte->write(&ctrl_z, 1); - send_stat++; - return state::IN_PROGRESS; - } - return Listener::state::IN_PROGRESS; -} - -Listener::state Listener::send(std::string_view response) -{ - if (send_stat == 1) { - if (response.find("+CIPSEND:") != std::string::npos) { - send_stat = 0; - return state::OK; - } - if (response.find("ERROR") != std::string::npos) { - ESP_LOGE(TAG, "Failed to sent"); - send_stat = 0; - return state::FAIL; - } - } - return Listener::state::IN_PROGRESS; -} - -Listener::state Listener::connect(std::string_view response) -{ - if (response.find("+CIPOPEN: 0,0") != std::string::npos) { - ESP_LOGI(TAG, "Connected!"); - return state::OK; - } - if (response.find("ERROR") != std::string::npos) { - ESP_LOGE(TAG, "Failed to open"); - return state::FAIL; - } - return Listener::state::IN_PROGRESS; -} } // namespace sock_dce diff --git a/components/esp_modem/examples/modem_tcp_client/main/sock_dce.hpp b/components/esp_modem/examples/modem_tcp_client/main/sock_dce.hpp index 643713ce5..9587aa855 100644 --- a/components/esp_modem/examples/modem_tcp_client/main/sock_dce.hpp +++ b/components/esp_modem/examples/modem_tcp_client/main/sock_dce.hpp @@ -28,7 +28,15 @@ public: state send(std::string_view response); state connect(std::string_view response); void check_async_replies(std::string_view &response) const; + + void start_sending(size_t len); + void start_receiving(size_t len); + bool start_connecting(std::string host, int port); private: + void send_cmd(std::string_view command) + { + dte->write((uint8_t *) command.begin(), command.size()); + } std::array &buffer; size_t data_to_recv = 0; bool read_again = false; @@ -67,31 +75,19 @@ private: void forwarding(uint8_t *data, size_t len); -// void check_async_replies(std::string_view &response) const; - - void send_cmd(std::string_view command) - { - dte->write((uint8_t *) command.begin(), command.size()); - } - enum class status { IDLE, CONNECTING, CONNECTION_FAILED, SENDING, - SENDING_1, SENDING_FAILED, RECEIVING, - RECEIVING_1, RECEIVING_FAILED }; status state{status::IDLE}; static constexpr uint8_t IDLE = 1; std::array buffer; Listener at{buffer, sock, data_ready_fd, dte}; - size_t data_to_send = 0; -// size_t data_to_recv = 0; - bool read_again = false; int sock {-1}; int listen_sock {-1}; int data_ready_fd {-1};