forked from espressif/esp-protocols
		
	fix(modem): AT-only example: support MQTT over TLS on BG96
This commit is contained in:
		| @@ -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("lpwa.vodafone.com"); | ||||
|     esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_EXAMPLE_MODEM_APN); | ||||
|  | ||||
|     /* 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(1883); | ||||
|     dce->init_sock(8883); | ||||
|     esp_mqtt_client_config_t mqtt_config = {}; | ||||
| #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) | ||||
|     mqtt_config.broker.address.uri = "mqtt://127.0.0.1"; | ||||
|     mqtt_config.broker.address.uri = "mqtts://127.0.0.1"; | ||||
|     mqtt_config.session.message_retransmit_timeout = 10000; | ||||
| #else | ||||
|     mqtt_config.uri = "mqtt://127.0.0.1"; | ||||
| @@ -118,13 +118,13 @@ 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_mqtt_event_id_t>(ESP_EVENT_ANY_ID), mqtt_event_handler, NULL); | ||||
|     esp_mqtt_client_start(mqtt_client); | ||||
|     if (!dce->start(BROKER_URL, 1883)) { | ||||
|     if (!dce->start(BROKER_URL, 8883)) { | ||||
|         ESP_LOGE(TAG, "Failed to start DCE"); | ||||
|         return; | ||||
|     } | ||||
|     while (1) { | ||||
|         while (dce->perform()) { | ||||
|             ESP_LOGD(TAG, "...performing"); | ||||
|         while (dce->perform_sock()) { | ||||
|             ESP_LOGV(TAG, "...performing"); | ||||
|         } | ||||
|         ESP_LOGE(TAG, "Loop exit.. retrying"); | ||||
|         // handle disconnections errors | ||||
|   | ||||
| @@ -28,7 +28,7 @@ command_result net_open(CommandableIf *t) | ||||
|     if (out.find("+QISTATE: 0") != std::string::npos) { | ||||
|         ESP_LOGV(TAG, "%s", out.data() ); | ||||
|         ESP_LOGD(TAG, "Already there"); | ||||
|         return command_result::OK; | ||||
|         return command_result::FAIL; | ||||
|     } else if (out.empty()) { | ||||
|         return dce_commands::generic_command(t, "AT+QIACT=1\r", "OK", "ERROR", 150000); | ||||
|     } | ||||
| @@ -38,6 +38,8 @@ command_result net_open(CommandableIf *t) | ||||
| command_result net_close(CommandableIf *t) | ||||
| { | ||||
|     ESP_LOGV(TAG, "%s", __func__ ); | ||||
|     dce_commands::generic_command(t, "AT+QICLOSE=0\r", "OK", "ERROR", 10000); | ||||
|     esp_modem::Task::Delay(1000); | ||||
|     return dce_commands::generic_command(t, "AT+QIDEACT=1\r", "OK", "ERROR", 40000); | ||||
| } | ||||
|  | ||||
| @@ -104,103 +106,131 @@ command_result get_ip(CommandableIf *t, std::string &ip) | ||||
|  | ||||
| namespace sock_dce { | ||||
|  | ||||
| void Listener::start_sending(size_t len) | ||||
| void Responder::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) | ||||
| void Responder::start_receiving(size_t len) | ||||
| { | ||||
|     send_cmd("AT+QIRD=0," + std::to_string(size) + "\r"); | ||||
|     send_cmd("AT+QIRD=0," + std::to_string(len) + "\r"); | ||||
| } | ||||
|  | ||||
| bool Listener::start_connecting(std::string host, int port) | ||||
| bool Responder::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) | ||||
| Responder::ret Responder::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; | ||||
|     } | ||||
|     const int MIN_MESSAGE = 6; | ||||
|     size_t actual_len = 0; | ||||
|     auto *recv_data = (char *)data; | ||||
|     if (data_to_recv == 0) { | ||||
|         const std::string_view head = "+QIRD: "; | ||||
|         auto head_pos = std::search(recv_data, recv_data + len, head.begin(), head.end()); | ||||
|         if (head_pos == nullptr) { | ||||
|             return ret::FAIL; | ||||
|         } | ||||
|  | ||||
|     auto next_nl = (char *)memchr(head_pos + head.size(), '\n', MIN_MESSAGE); | ||||
|     if (next_nl == nullptr) { | ||||
|         return state::FAIL; | ||||
|     } | ||||
|         auto next_nl = (char *)memchr(head_pos + head.size(), '\n', MIN_MESSAGE); | ||||
|         if (next_nl == nullptr) { | ||||
|             return ret::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; | ||||
|     } | ||||
|         if (std::from_chars(head_pos + head.size(), next_nl, actual_len).ec == std::errc::invalid_argument) { | ||||
|             ESP_LOGE(TAG, "cannot convert"); | ||||
|             return ret::FAIL; | ||||
|         } | ||||
|  | ||||
|     ESP_LOGD(TAG, "Received: actual len=%d", actual_len); | ||||
|     if (actual_len == 0) { | ||||
|         ESP_LOGD(TAG, "no data received"); | ||||
|         return state::FAIL; | ||||
|     } | ||||
|         ESP_LOGD(TAG, "Received: actual len=%d", actual_len); | ||||
|         if (actual_len == 0) { | ||||
|             ESP_LOGD(TAG, "no data received"); | ||||
|             return ret::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; | ||||
|         if (actual_len > buffer_size) { | ||||
|             ESP_LOGE(TAG, "TOO BIG"); | ||||
|             return ret::FAIL; | ||||
|         } | ||||
|  | ||||
|         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 ret::NEED_MORE_DATA; | ||||
|         } | ||||
|         ::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 ret::NEED_MORE_DATA; | ||||
|     } 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; | ||||
|     } | ||||
|     ::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; | ||||
|     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 ret::FAIL; | ||||
|         } | ||||
|     } | ||||
|     if ((char *)data + len - last_pos > MIN_MESSAGE) { | ||||
|     if (last_pos != nullptr && (char *)data + len - last_pos - 2 > 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); | ||||
|         check_async_replies(status::RECEIVING, response); | ||||
|     } | ||||
|     return state::OK; | ||||
|     // check if some other data? | ||||
|     start_receiving(0); | ||||
|     data_to_recv = 0; | ||||
|     return ret::OK; | ||||
| } | ||||
|  | ||||
|  | ||||
| Listener::state Listener::send(uint8_t *data, size_t len) | ||||
| Responder::ret Responder::send(uint8_t *data, size_t len) | ||||
| { | ||||
|     if (send_stat == 0) { | ||||
|     if (send_stat < 3) { | ||||
|         if (memchr(data, '>', len) == NULL) { | ||||
|             if (send_stat++ < 2) { | ||||
|                 return Responder::ret::NEED_MORE_DATA; | ||||
|             } | ||||
|             ESP_LOGE(TAG, "Missed >"); | ||||
|             return state::FAIL; | ||||
|             return ret::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; | ||||
|             return ret::FAIL; | ||||
|         } | ||||
|         data_to_send = 0; | ||||
|         send_stat++; | ||||
|         send_stat = 3; | ||||
|     } | ||||
|     return Listener::state::IN_PROGRESS; | ||||
|     return Responder::ret::IN_PROGRESS; | ||||
| } | ||||
|  | ||||
| Listener::state Listener::send(std::string_view response) | ||||
| Responder::ret Responder::send(std::string_view response) | ||||
| { | ||||
|     if (send_stat == 1) { | ||||
|     if (send_stat == 3) { | ||||
|         if (response.find("SEND OK") != std::string::npos) { | ||||
|             send_cmd("AT+QISEND=0,0\r"); | ||||
|             send_stat++; | ||||
|             return ret::IN_PROGRESS; | ||||
|         } else if (response.find("SEND FAIL") != std::string::npos) { | ||||
|             ESP_LOGE(TAG, "Sending buffer full"); | ||||
|             return state::FAIL; | ||||
|             return ret::FAIL; | ||||
|         } else if (response.find("ERROR") != std::string::npos) { | ||||
|             ESP_LOGE(TAG, "Failed to sent"); | ||||
|             return state::FAIL; | ||||
|             return ret::FAIL; | ||||
|         } | ||||
|     } else if (send_stat == 2) { | ||||
|     } else if (send_stat == 4) { | ||||
|         constexpr std::string_view head = "+QISEND: "; | ||||
|         if (response.find(head) != std::string::npos) { | ||||
|             // Parsing +QISEND: <total_send_length>,<ackedbytes>,<unackedbytes> | ||||
| @@ -215,7 +245,7 @@ Listener::state Listener::send(std::string_view response) | ||||
|                 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; | ||||
|                     return ret::FAIL; | ||||
|                 } | ||||
|  | ||||
|                 switch (property++) { | ||||
| @@ -224,49 +254,94 @@ Listener::state Listener::send(std::string_view response) | ||||
|                 case 1: ack = value; | ||||
|                     break; | ||||
|                 default: | ||||
|                     return state::FAIL; | ||||
|                     return ret::FAIL; | ||||
|                 } | ||||
|                 response = response.substr(pos + 1); | ||||
|             } | ||||
|             if (std::from_chars(response.data(), response.data() + pos, unack).ec == std::errc::invalid_argument) { | ||||
|                 return state::FAIL; | ||||
|                 return ret::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)); | ||||
|                 ESP_LOGD(TAG, "all sending data are not ack (missing %d bytes acked)", (total - ack)); | ||||
|                 if (total - ack > 64) { | ||||
|                     ESP_LOGW(TAG, "Need a pause: missing %d bytes acked", (total - ack)); | ||||
|                     return ret::NEED_MORE_TIME; | ||||
|                 } | ||||
|             } | ||||
|             return state::OK; | ||||
|             send_stat = 0; | ||||
|             return ret::OK; | ||||
|         } else if (response.find("ERROR") != std::string::npos) { | ||||
|             ESP_LOGE(TAG, "Failed to check sending"); | ||||
|             return state::FAIL; | ||||
|             return ret::FAIL; | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     return Listener::state::IN_PROGRESS; | ||||
|     return Responder::ret::IN_PROGRESS; | ||||
| } | ||||
|  | ||||
| Listener::state Listener::connect(std::string_view response) | ||||
| Responder::ret Responder::connect(std::string_view response) | ||||
| { | ||||
|     if (response.find("+QIOPEN: 0,0") != std::string::npos) { | ||||
|         ESP_LOGI(TAG, "Connected!"); | ||||
|         return state::OK; | ||||
|         return ret::OK; | ||||
|     } | ||||
|     if (response.find("ERROR") != std::string::npos) { | ||||
|         ESP_LOGE(TAG, "Failed to open"); | ||||
|         return state::FAIL; | ||||
|         return ret::FAIL; | ||||
|     } | ||||
|     return Listener::state::IN_PROGRESS; | ||||
|     return Responder::ret::IN_PROGRESS; | ||||
| } | ||||
|  | ||||
| void Listener::check_async_replies(std::string_view &response) const | ||||
| Responder::ret Responder::check_async_replies(status state, std::string_view &response) | ||||
| { | ||||
|     ESP_LOGD(TAG, "response %.*s", static_cast<int>(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!"); | ||||
|     } else if (response.find("+QIRD: ") != std::string::npos) { | ||||
|         static constexpr std::string_view head = "+QIRD: "; | ||||
|         size_t head_pos = response.find(head); | ||||
|         // Parsing +QIURC: <total_receive_length>,<have_read_length>,<unread_length> | ||||
|         response = response.substr(head_pos + head.size()); | ||||
|         int next_cr = response.find('\r'); | ||||
|         if (next_cr != std::string::npos) { | ||||
|             response = response.substr(next_cr - 2, next_cr); | ||||
|             if (response.find(",0") != std::string::npos) { | ||||
|                 ESP_LOGV(TAG, "Receiving done"); | ||||
|             } else { | ||||
|                 uint64_t data_ready = 1; | ||||
|                 write(data_ready_fd, &data_ready, sizeof(data_ready)); | ||||
|                 ESP_LOGD(TAG, "Got data on modem!"); | ||||
|             } | ||||
|         } | ||||
|     } else if (response.find("+QIURC: \"closed\",0") != std::string::npos) { | ||||
|         return ret::FAIL; | ||||
|     } | ||||
|     if (state == status::SENDING) { | ||||
|         return send(response); | ||||
|     } else if (state == status::CONNECTING) { | ||||
|         return connect(response); | ||||
|     } | ||||
|     return ret::IN_PROGRESS; | ||||
| } | ||||
|  | ||||
| Responder::ret Responder::process_data(status state, uint8_t *data, size_t len) | ||||
| { | ||||
|     if (state == status::SENDING) { | ||||
|         return send(data, len); | ||||
|     } | ||||
|     if (state == status::RECEIVING) { | ||||
|         return recv(data, len); | ||||
|     } | ||||
|     return Responder::ret::IN_PROGRESS; | ||||
| } | ||||
|  | ||||
| status Responder::pending() | ||||
| { | ||||
|     send_cmd("AT+QISEND=0,0\r"); | ||||
|     return status::SENDING; | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -6,14 +6,15 @@ | ||||
|  | ||||
| #include <charconv> | ||||
| #include <cstring> | ||||
| #include <sys/socket.h> | ||||
| #include "sock_commands.hpp" | ||||
| #include "cxx_include/esp_modem_command_library_utils.hpp" | ||||
| #include "sock_dce.hpp" | ||||
|  | ||||
| namespace sock_commands { | ||||
|  | ||||
| static const char *TAG = "sock_commands"; | ||||
|  | ||||
| namespace sock_commands { | ||||
|  | ||||
| using namespace esp_modem; | ||||
|  | ||||
| command_result net_open(CommandableIf *term) | ||||
| @@ -27,12 +28,17 @@ command_result net_open(CommandableIf *term) | ||||
|     ESP_LOGV(TAG, "%s", response.data() ); | ||||
|     if (response.find("+NETOPEN: 1") != std::string::npos) { | ||||
|         ESP_LOGD(TAG, "Already there"); | ||||
|         return command_result::OK; | ||||
|         ret = command_result::OK; | ||||
|     } else if (response.find("+NETOPEN: 0") != std::string::npos) { | ||||
|         ESP_LOGD(TAG, "Need to setup"); | ||||
|         return dce_commands::generic_command(term, "AT+NETOPEN\r", "+NETOPEN: 1", "+NETOPEN: 0", 10000); | ||||
|         ret = dce_commands::generic_command(term, "AT+NETOPEN\r", "+NETOPEN: 1", "+NETOPEN: 0", 10000); | ||||
|     } else { | ||||
|         return command_result::FAIL; | ||||
|     } | ||||
|     return command_result::FAIL; | ||||
|     if (ret != command_result::OK) { | ||||
|         return ret; | ||||
|     } | ||||
|     return dce_commands::generic_command(term, "AT+CIPRXGET=1\r", "OK", "ERROR", 5000); | ||||
| } | ||||
|  | ||||
| command_result net_close(CommandableIf *term) | ||||
| @@ -186,28 +192,25 @@ command_result set_rx_mode(CommandableIf *term, int mode) | ||||
|  | ||||
| namespace sock_dce { | ||||
|  | ||||
| void Listener::start_sending(size_t len) | ||||
| void Responder::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) | ||||
| void Responder::start_receiving(size_t len) | ||||
| { | ||||
|     send_cmd("AT+CIPRXGET=2,0," + std::to_string(size) + "\r"); | ||||
|     send_cmd("AT+CIPRXGET=2,0," + std::to_string(len) + "\r"); | ||||
| } | ||||
|  | ||||
| bool Listener::start_connecting(std::string host, int port) | ||||
| bool Responder::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) | ||||
| Responder::ret Responder::recv(uint8_t *data, size_t len) | ||||
| { | ||||
|     const int MIN_MESSAGE = 6; | ||||
|     size_t actual_len = 0; | ||||
| @@ -216,40 +219,37 @@ Listener::state Listener::recv(uint8_t *data, size_t len) | ||||
|         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; | ||||
|             return ret::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); | ||||
|             check_async_replies(status::RECEIVING, response); | ||||
|         } | ||||
|  | ||||
|         auto next_comma = (char *)memchr(head_pos + head.size(), ',', MIN_MESSAGE); | ||||
|         if (next_comma == nullptr)  { | ||||
|             return state::FAIL; | ||||
|             return ret::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; | ||||
|             return ret::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; | ||||
|             return ret::FAIL; | ||||
|         } | ||||
|         if (actual_len > size) { | ||||
|         if (actual_len > buffer_size) { | ||||
|             ESP_LOGE(TAG, "TOO BIG"); | ||||
|             return state::FAIL; | ||||
|             return ret::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; | ||||
|             return ret::FAIL; | ||||
|         } | ||||
|         read_again = (total_len > 0); | ||||
|         recv_data = next_nl + 1; | ||||
| @@ -257,13 +257,13 @@ Listener::state Listener::recv(uint8_t *data, size_t len) | ||||
|         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; | ||||
|             return ret::NEED_MORE_DATA; | ||||
|         } | ||||
|         ::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; | ||||
|         return ret::NEED_MORE_DATA; | ||||
|     } 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; | ||||
| @@ -275,73 +275,73 @@ Listener::state Listener::recv(uint8_t *data, size_t 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; | ||||
|             return ret::FAIL; | ||||
|         } | ||||
|     } | ||||
|     if (last_pos != nullptr && (char *)data + len - last_pos > MIN_MESSAGE) { | ||||
|     if (last_pos != nullptr && (char *)data + len - last_pos - 2 > 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); | ||||
|         check_async_replies(status::RECEIVING, 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; | ||||
|     return ret::OK; | ||||
| } | ||||
|  | ||||
| Listener::state Listener::send(uint8_t *data, size_t len) | ||||
| Responder::ret Responder::send(uint8_t *data, size_t len) | ||||
| { | ||||
|     if (send_stat == 0) { | ||||
|         if (memchr(data, '>', len) == NULL) { | ||||
|             ESP_LOGE(TAG, "Missed >"); | ||||
|             return state::FAIL; | ||||
|             return ret::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; | ||||
|             return ret::FAIL; | ||||
|         } | ||||
|         data_to_send = 0; | ||||
|         uint8_t ctrl_z = '\x1A'; | ||||
|         dte->write(&ctrl_z, 1); | ||||
|         send_stat++; | ||||
|         return state::IN_PROGRESS; | ||||
|         return ret::IN_PROGRESS; | ||||
|     } | ||||
|     return Listener::state::IN_PROGRESS; | ||||
|     return Responder::ret::IN_PROGRESS; | ||||
| } | ||||
|  | ||||
| Listener::state Listener::send(std::string_view response) | ||||
| Responder::ret Responder::send(std::string_view response) | ||||
| { | ||||
|     if (send_stat == 1) { | ||||
|         if (response.find("+CIPSEND:") != std::string::npos) { | ||||
|             send_stat = 0; | ||||
|             return state::OK; | ||||
|             return ret::OK; | ||||
|         } | ||||
|         if (response.find("ERROR") != std::string::npos) { | ||||
|             ESP_LOGE(TAG, "Failed to sent"); | ||||
|             send_stat = 0; | ||||
|             return state::FAIL; | ||||
|             return ret::FAIL; | ||||
|         } | ||||
|     } | ||||
|     return Listener::state::IN_PROGRESS; | ||||
|     return Responder::ret::IN_PROGRESS; | ||||
| } | ||||
|  | ||||
| Listener::state Listener::connect(std::string_view response) | ||||
| Responder::ret Responder::connect(std::string_view response) | ||||
| { | ||||
|     if (response.find("+CIPOPEN: 0,0") != std::string::npos) { | ||||
|         ESP_LOGI(TAG, "Connected!"); | ||||
|         return state::OK; | ||||
|         return ret::OK; | ||||
|     } | ||||
|     if (response.find("ERROR") != std::string::npos) { | ||||
|         ESP_LOGE(TAG, "Failed to open"); | ||||
|         return state::FAIL; | ||||
|         return ret::FAIL; | ||||
|     } | ||||
|     return Listener::state::IN_PROGRESS; | ||||
|     return Responder::ret::IN_PROGRESS; | ||||
| } | ||||
|  | ||||
| void Listener::check_async_replies(std::string_view &response) const | ||||
| Responder::ret Responder::check_async_replies(status state, std::string_view &response) | ||||
| { | ||||
|     ESP_LOGD(TAG, "response %.*s", static_cast<int>(response.size()), response.data()); | ||||
|     if (response.find("+CIPRXGET: 1") != std::string::npos) { | ||||
| @@ -349,8 +349,30 @@ void Listener::check_async_replies(std::string_view &response) const | ||||
|         write(data_ready_fd, &data_ready, sizeof(data_ready)); | ||||
|         ESP_LOGD(TAG, "Got data on modem!"); | ||||
|     } | ||||
|     if (state == status::SENDING) { | ||||
|         return send(response); | ||||
|     } else if (state == status::CONNECTING) { | ||||
|         return connect(response); | ||||
|     } | ||||
|     return ret::IN_PROGRESS; | ||||
|  | ||||
| } | ||||
|  | ||||
| Responder::ret Responder::process_data(status state, uint8_t *data, size_t len) | ||||
| { | ||||
|     if (state == status::SENDING) { | ||||
|         return send(data, len); | ||||
|     } | ||||
|     if (state == status::RECEIVING) { | ||||
|         return recv(data, len); | ||||
|     } | ||||
|     return Responder::ret::IN_PROGRESS; | ||||
| } | ||||
|  | ||||
| status Responder::pending() | ||||
| { | ||||
|     return status::PENDING; | ||||
| } | ||||
|  | ||||
|  | ||||
| } // sock_dce | ||||
|   | ||||
| @@ -16,7 +16,7 @@ namespace sock_dce { | ||||
| constexpr auto const *TAG = "sock_dce"; | ||||
|  | ||||
|  | ||||
| bool DCE::perform() | ||||
| bool DCE::perform_sock() | ||||
| { | ||||
|     if (listen_sock == -1) { | ||||
|         ESP_LOGE(TAG, "Listening socket not ready"); | ||||
| @@ -32,13 +32,18 @@ bool DCE::perform() | ||||
|         .tv_sec = 0, | ||||
|         .tv_usec = 500000, | ||||
|     }; | ||||
|     if (state == status::PENDING) { | ||||
|         vTaskDelay(pdMS_TO_TICKS(500)); | ||||
|         state = at.pending(); | ||||
|         return true; | ||||
|     } | ||||
|     fd_set fdset; | ||||
|     FD_ZERO(&fdset); | ||||
|     FD_SET(sock, &fdset); | ||||
|     FD_SET(data_ready_fd, &fdset); | ||||
|     int s = select(std::max(sock, data_ready_fd) + 1, &fdset, nullptr, nullptr, &tv); | ||||
|     if (s == 0) { | ||||
|         ESP_LOGD(TAG, "perform select timeout..."); | ||||
|         ESP_LOGV(TAG, "perform select timeout..."); | ||||
|         return true; | ||||
|     } else if (s < 0) { | ||||
|         ESP_LOGE(TAG,  "select error %d", errno); | ||||
| @@ -54,68 +59,42 @@ bool DCE::perform() | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| void DCE::forwarding(uint8_t *data, size_t len) | ||||
| void DCE::perform_at(uint8_t *data, size_t len) | ||||
| { | ||||
|     ESP_LOG_BUFFER_HEXDUMP(TAG, data, len, ESP_LOG_DEBUG); | ||||
|     if (state == status::SENDING) { | ||||
|         switch (at.send(data, len)) { | ||||
|         case Listener::state::OK: | ||||
|             state = status::IDLE; | ||||
|             signal.set(IDLE); | ||||
|             return; | ||||
|         case Listener::state::FAIL: | ||||
|             state = status::SENDING_FAILED; | ||||
|             signal.set(IDLE); | ||||
|             return; | ||||
|         case Listener::state::IN_PROGRESS: | ||||
|             break; | ||||
| //            return; | ||||
|         } | ||||
|     } else if (state == status::RECEIVING) { | ||||
|         switch (at.recv(data, len)) { | ||||
|         case Listener::state::OK: | ||||
|             state = status::IDLE; | ||||
|             signal.set(IDLE); | ||||
|             return; | ||||
|         case Listener::state::FAIL: | ||||
|             state = status::RECEIVING_FAILED; | ||||
|             signal.set(IDLE); | ||||
|             return; | ||||
|         case Listener::state::IN_PROGRESS: | ||||
| //            break; | ||||
|             return; | ||||
|         } | ||||
|     ESP_LOG_BUFFER_HEXDUMP(TAG, data, len, ESP_LOG_VERBOSE); | ||||
|     switch (at.process_data(state, data, len)) { | ||||
|     case Responder::ret::OK: | ||||
|         state = status::IDLE; | ||||
|         signal.set(IDLE); | ||||
|         return; | ||||
|     case Responder::ret::FAIL: | ||||
|         state = status::FAILED; | ||||
|         signal.set(IDLE); | ||||
|         return; | ||||
|     case Responder::ret::NEED_MORE_DATA: | ||||
|         return; | ||||
|     case Responder::ret::IN_PROGRESS: | ||||
|         break; | ||||
|     case Responder::ret::NEED_MORE_TIME: | ||||
|         state = status::PENDING; | ||||
|         return; | ||||
|     } | ||||
|     std::string_view response((char *)data, len); | ||||
|     at.check_async_replies(response); | ||||
|     // Notification about Data Ready could come any time | ||||
|     if (state == status::SENDING) { | ||||
|         switch (at.send(response)) { | ||||
|         case Listener::state::OK: | ||||
|             state = status::IDLE; | ||||
|             signal.set(IDLE); | ||||
|             return; | ||||
|         case Listener::state::FAIL: | ||||
|             state = status::SENDING_FAILED; | ||||
|             signal.set(IDLE); | ||||
|             return; | ||||
|         case Listener::state::IN_PROGRESS: | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     if (state == status::CONNECTING) { | ||||
|         switch (at.connect(response)) { | ||||
|         case Listener::state::OK: | ||||
|             state = status::IDLE; | ||||
|             signal.set(IDLE); | ||||
|             return; | ||||
|         case Listener::state::FAIL: | ||||
|             state = status::CONNECTION_FAILED; | ||||
|             signal.set(IDLE); | ||||
|             return; | ||||
|         case Listener::state::IN_PROGRESS: | ||||
|             break; | ||||
|         } | ||||
|     switch (at.check_async_replies(state, response)) { | ||||
|     case Responder::ret::OK: | ||||
|         state = status::IDLE; | ||||
|         signal.set(IDLE); | ||||
|         return; | ||||
|     case Responder::ret::FAIL: | ||||
|         state = status::FAILED; | ||||
|         signal.set(IDLE); | ||||
|         return; | ||||
|     case Responder::ret::NEED_MORE_TIME: | ||||
|         state = status::PENDING; | ||||
|         return; | ||||
|     case Responder::ret::NEED_MORE_DATA: | ||||
|     case Responder::ret::IN_PROGRESS: | ||||
|         break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -125,6 +104,7 @@ void DCE::close_sock() | ||||
|         close(sock); | ||||
|         sock = -1; | ||||
|     } | ||||
|     dte->on_read(nullptr); | ||||
|     const int retries = 5; | ||||
|     int i = 0; | ||||
|     while (net_close() != esp_modem::command_result::OK) { | ||||
| @@ -152,7 +132,7 @@ bool DCE::at_to_sock() | ||||
|         return false; | ||||
|     } | ||||
|     state = status::RECEIVING; | ||||
|     at.start_receiving(size); | ||||
|     at.start_receiving(at.get_buf_len()); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @@ -170,7 +150,7 @@ bool DCE::sock_to_at() | ||||
|         return false; | ||||
|     } | ||||
|     state = status::SENDING; | ||||
|     int len = ::recv(sock, &buffer[0], size, 0); | ||||
|     int len = ::recv(sock, at.get_buf(), at.get_buf_len(), 0); | ||||
|     if (len < 0) { | ||||
|         ESP_LOGE(TAG,  "read error %d", errno); | ||||
|         close_sock(); | ||||
| @@ -180,7 +160,7 @@ bool DCE::sock_to_at() | ||||
|         close_sock(); | ||||
|         return false; | ||||
|     } | ||||
|     ESP_LOG_BUFFER_HEXDUMP(TAG, &buffer[0], len, ESP_LOG_VERBOSE); | ||||
|     ESP_LOG_BUFFER_HEXDUMP(TAG, at.get_buf(), len, ESP_LOG_VERBOSE); | ||||
|     at.start_sending(len); | ||||
|     return true; | ||||
| } | ||||
| @@ -212,7 +192,7 @@ bool DCE::accept_sock() | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| void DCE::init(int port) | ||||
| void DCE::init_sock(int port) | ||||
| { | ||||
|     esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT(); | ||||
|     esp_vfs_eventfd_register(&config); | ||||
| @@ -228,7 +208,7 @@ void DCE::init(int port) | ||||
|     int opt = 1; | ||||
|     setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); | ||||
|     ESP_LOGI(TAG, "Socket created"); | ||||
|     struct sockaddr_in addr = {  }; | ||||
|     struct sockaddr_in addr = { }; | ||||
|     addr.sin_family = AF_INET; | ||||
|     addr.sin_port = htons(port); | ||||
|     addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | ||||
| @@ -253,7 +233,7 @@ bool DCE::start(std::string host, int port) | ||||
|     dte->on_read(nullptr); | ||||
|     tcp_close(); | ||||
|     dte->on_read([this](uint8_t *data, size_t len) { | ||||
|         this->forwarding(data, len); | ||||
|         this->perform_at(data, len); | ||||
|         return esp_modem::command_result::TIMEOUT; | ||||
|     }); | ||||
|     if (!at.start_connecting(host, port)) { | ||||
| @@ -267,6 +247,7 @@ bool DCE::start(std::string host, int port) | ||||
|  | ||||
| bool DCE::init_network() | ||||
| { | ||||
|     dte->on_read(nullptr); | ||||
|     const int retries = 5; | ||||
|     int i = 0; | ||||
|     while (sync() != esp_modem::command_result::OK) { | ||||
| @@ -292,6 +273,7 @@ bool DCE::init_network() | ||||
|             ESP_LOGE(TAG, "Failed to open network"); | ||||
|             return false; | ||||
|         } | ||||
|         net_close(); | ||||
|         esp_modem::Task::Delay(1000); | ||||
|     } | ||||
|     ESP_LOGD(TAG, "Network opened"); | ||||
| @@ -341,9 +323,4 @@ DECLARE_SOCK_COMMANDS(return_type name(...) ) | ||||
|  | ||||
| #undef ESP_MODEM_DECLARE_DCE_COMMAND | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| } // namespace sock_dce | ||||
|   | ||||
| @@ -14,30 +14,50 @@ | ||||
|  | ||||
| namespace sock_dce { | ||||
|  | ||||
| static constexpr size_t size = 512; | ||||
|  | ||||
| class Listener { | ||||
| enum class status { | ||||
|     IDLE, | ||||
|     CONNECTING, | ||||
|     SENDING, | ||||
|     RECEIVING, | ||||
|     FAILED, | ||||
|     PENDING | ||||
| }; | ||||
|  | ||||
| class Responder { | ||||
| public: | ||||
|     enum class state { | ||||
|         OK, FAIL, IN_PROGRESS | ||||
|     enum class ret { | ||||
|         OK, FAIL, IN_PROGRESS, NEED_MORE_DATA, NEED_MORE_TIME | ||||
|     }; | ||||
|     Listener(std::array<uint8_t, size> &b, int &s, int &ready_fd, std::shared_ptr<esp_modem::DTE> &dte_arg): | ||||
|         buffer(b), sock(s), data_ready_fd(ready_fd), dte(dte_arg) {} | ||||
|     state recv(uint8_t *data, size_t len); | ||||
|     state send(uint8_t *data, size_t len); | ||||
|     state send(std::string_view response); | ||||
|     state connect(std::string_view response); | ||||
|     void check_async_replies(std::string_view &response) const; | ||||
|     Responder(int &s, int &ready_fd, std::shared_ptr<esp_modem::DTE> &dte_arg): | ||||
|         sock(s), data_ready_fd(ready_fd), dte(dte_arg) {} | ||||
|     ret process_data(status state, uint8_t *data, size_t len); | ||||
|     ret check_async_replies(status state, std::string_view &response); | ||||
|  | ||||
|     void start_sending(size_t len); | ||||
|     void start_receiving(size_t len); | ||||
|     bool start_connecting(std::string host, int port); | ||||
|     status pending(); | ||||
|     uint8_t *get_buf() | ||||
|     { | ||||
|         return &buffer[0]; | ||||
|     } | ||||
|     size_t get_buf_len() | ||||
|     { | ||||
|         return buffer_size; | ||||
|     } | ||||
| private: | ||||
|     static constexpr size_t buffer_size = 512; | ||||
|  | ||||
|     ret recv(uint8_t *data, size_t len); | ||||
|     ret send(uint8_t *data, size_t len); | ||||
|     ret send(std::string_view response); | ||||
|     ret connect(std::string_view response); | ||||
|     void send_cmd(std::string_view command) | ||||
|     { | ||||
|         dte->write((uint8_t *) command.begin(), command.size()); | ||||
|     } | ||||
|     std::array<uint8_t, size> &buffer; | ||||
|     std::array<uint8_t, buffer_size> buffer; | ||||
|     size_t data_to_recv = 0; | ||||
|     bool read_again = false; | ||||
|     int &sock; | ||||
| @@ -61,9 +81,9 @@ esp_modem::return_type name(__VA_ARGS__); | ||||
|     bool init_network(); | ||||
|     bool start(std::string host, int port); | ||||
|  | ||||
|     void init(int port); | ||||
|     void init_sock(int port); | ||||
|  | ||||
|     bool perform(); | ||||
|     bool perform_sock(); | ||||
|  | ||||
| private: | ||||
|     esp_modem::SignalGroup signal; | ||||
| @@ -73,21 +93,11 @@ private: | ||||
|     bool sock_to_at(); | ||||
|     bool at_to_sock(); | ||||
|  | ||||
|     void forwarding(uint8_t *data, size_t len); | ||||
|     void perform_at(uint8_t *data, size_t len); | ||||
|  | ||||
|     enum class status { | ||||
|         IDLE, | ||||
|         CONNECTING, | ||||
|         CONNECTION_FAILED, | ||||
|         SENDING, | ||||
|         SENDING_FAILED, | ||||
|         RECEIVING, | ||||
|         RECEIVING_FAILED | ||||
|     }; | ||||
|     status state{status::IDLE}; | ||||
|     static constexpr uint8_t IDLE = 1; | ||||
|     std::array<uint8_t, size> buffer; | ||||
|     Listener at{buffer, sock, data_ready_fd, dte}; | ||||
|     Responder at{sock, data_ready_fd, dte}; | ||||
|     int sock {-1}; | ||||
|     int listen_sock {-1}; | ||||
|     int data_ready_fd {-1}; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user