Skip to content

Commit 0922062

Browse files
committed
fix(modem): Cleanup tcp-client code
1 parent 077f1c4 commit 0922062

File tree

9 files changed

+77
-43
lines changed

9 files changed

+77
-43
lines changed

components/esp_modem/examples/modem_tcp_client/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,18 @@ To enable this mode, please set `EXAMPLE_CUSTOM_TCP_TRANSPORT=y`
2222
This configuration could be used with any network library, which is connecting to a localhost endpoint instead of remote one. This example creates a localhost listener which basically mimics the remote endpoint by forwarding the traffic between the library and the TCP/socket layer of the modem (which is already secure if the TLS is used in the network library)
2323

2424
![with localhost listener](at_client_localhost.png)
25+
26+
### Multi-connection support
27+
28+
This example supports opening multiple TCP connections concurrently when the modem firmware allows it.
29+
30+
- ESP-AT: Multi-connection mode is enabled via `AT+CIPMUX=1`. The example assigns a unique link ID per DCE instance and includes the link ID in `CIPSTART/CIPSEND/CIPRECVDATA` commands.
31+
- BG96/SIM7600: The example uses module-specific multi-connection syntax (for example `QIOPEN/CIPOPEN` with a connection ID) and tracks link IDs internally.
32+
33+
How it works:
34+
- The `sock_dce` layer creates multiple DCE instances over a shared DTE. A lightweight mutex coordinates access to the UART so only one DCE issues AT commands at a time.
35+
- Asynchronous URCs (for example `+IPD`, `+QIURC`, `+CIPRXGET: 1,<cid>`) wake the corresponding DCE which then performs receive operations for its link.
36+
37+
Usage:
38+
- `app_main` starts two DCE tasks to demonstrate concurrent connections. Adjust the number of DCE instances as needed.
39+
- For ESP-AT, ensure your firmware supports `CIPMUX=1` and passive receive (`CIPRECVTYPE`).

components/esp_modem/examples/modem_tcp_client/main/command/sock_dce.cpp

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -91,16 +91,18 @@ void DCE::perform_at(uint8_t *data, size_t len)
9191
return;
9292
}
9393
}
94-
ESP_LOG_BUFFER_HEXDUMP(TAG, data, len, ESP_LOG_INFO);
94+
// Trace incoming AT bytes when handling a response; use DEBUG level
95+
ESP_LOG_BUFFER_HEXDUMP(TAG, data, len, ESP_LOG_DEBUG);
9596
switch (at.process_data(state, data, len)) {
9697
case Responder::ret::OK:
97-
ESP_LOGW(TAG, "GIVE data %d", at.link_id);
98+
// Release DTE access for this link after processing data
99+
ESP_LOGD(TAG, "GIVE data %d", at.link_id);
98100
xSemaphoreGive(at.s_dte_mutex);
99101
state = status::IDLE;
100102
signal.set(IDLE);
101103
return;
102104
case Responder::ret::FAIL:
103-
ESP_LOGW(TAG, "GIVE data %d", at.link_id);
105+
ESP_LOGD(TAG, "GIVE data %d", at.link_id);
104106
xSemaphoreGive(at.s_dte_mutex);
105107
state = status::FAILED;
106108
signal.set(IDLE);
@@ -116,13 +118,13 @@ void DCE::perform_at(uint8_t *data, size_t len)
116118
std::string_view response((char *)data, len);
117119
switch (at.check_async_replies(state, response)) {
118120
case Responder::ret::OK:
119-
ESP_LOGW(TAG, "GIVE command %d", at.link_id);
121+
ESP_LOGD(TAG, "GIVE command %d", at.link_id);
120122
xSemaphoreGive(at.s_dte_mutex);
121123
state = status::IDLE;
122124
signal.set(IDLE);
123125
return;
124126
case Responder::ret::FAIL:
125-
ESP_LOGW(TAG, "GIVE command %d", at.link_id);
127+
ESP_LOGD(TAG, "GIVE command %d", at.link_id);
126128
xSemaphoreGive(at.s_dte_mutex);
127129
state = status::FAILED;
128130
signal.set(IDLE);
@@ -169,9 +171,10 @@ bool DCE::at_to_sock()
169171
close_sock();
170172
return false;
171173
}
172-
ESP_LOGI(TAG, "TAKE RECV %d", at.link_id);
174+
// Take DTE mutex before issuing receive on this link
175+
ESP_LOGD(TAG, "TAKE RECV %d", at.link_id);
173176
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
174-
ESP_LOGE(TAG, "TAKE RECV %d", at.link_id);
177+
ESP_LOGD(TAG, "TAKEN RECV %d", at.link_id);
175178
state = status::RECEIVING;
176179
at.start_receiving(at.get_buf_len());
177180
return true;
@@ -190,9 +193,10 @@ bool DCE::sock_to_at()
190193
close_sock();
191194
return false;
192195
}
193-
ESP_LOGI(TAG, "TAKE SEND %d", at.link_id);
196+
// Take DTE mutex before issuing send on this link
197+
ESP_LOGD(TAG, "TAKE SEND %d", at.link_id);
194198
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
195-
ESP_LOGE(TAG, "TAKE SEND %d", at.link_id);
199+
ESP_LOGD(TAG, "TAKEN SEND %d", at.link_id);
196200
state = status::SENDING;
197201
int len = ::recv(sock, at.get_buf(), at.get_buf_len(), 0);
198202
if (len < 0) {
@@ -245,7 +249,7 @@ void DCE::start_listening(int port)
245249
}
246250
int opt = 1;
247251
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
248-
ESP_LOGI(TAG, "Socket created");
252+
ESP_LOGD(TAG, "Socket created");
249253
struct sockaddr_in addr = { };
250254
addr.sin_family = AF_INET;
251255
addr.sin_port = htons(port);
@@ -257,7 +261,7 @@ void DCE::start_listening(int port)
257261
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
258262
return;
259263
}
260-
ESP_LOGI(TAG, "Socket bound, port %d", 1883);
264+
ESP_LOGD(TAG, "Socket bound, port %d", 1883);
261265
err = listen(listen_sock, 1);
262266
if (err != 0) {
263267
ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
@@ -276,9 +280,10 @@ bool DCE::connect(std::string host, int port)
276280
// read_callback(data, len);
277281
// return esp_modem::command_result::TIMEOUT;
278282
// });
279-
ESP_LOGI(TAG, "TAKE CONNECT %d", at.link_id);
283+
// Take DTE mutex before starting connect for this link
284+
ESP_LOGD(TAG, "TAKE CONNECT %d", at.link_id);
280285
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
281-
ESP_LOGE(TAG, "TAKE CONNECT %d", at.link_id);
286+
ESP_LOGD(TAG, "TAKEN CONNECT %d", at.link_id);
282287
if (!at.start_connecting(host, port)) {
283288
ESP_LOGE(TAG, "Unable to start connecting");
284289
dte->on_read(nullptr);

components/esp_modem/examples/modem_tcp_client/main/command/sock_dce.hpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ class Responder {
6464
return total_len;
6565
}
6666

67+
// Unique link identifier used to target multi-connection AT commands
6768
int link_id{s_link_id++};
69+
// Shared mutex guarding DTE access across concurrent DCE instances
6870
static SemaphoreHandle_t s_dte_mutex;
6971
private:
7072
static int s_link_id;
@@ -170,9 +172,10 @@ class DCE : public Module {
170172
return 0;
171173
}
172174
at.clear_offsets();
173-
ESP_LOGI("TAG", "TAKE RECV %d", at.link_id);
175+
// Take DTE mutex before issuing receive on this link
176+
ESP_LOGD("TAG", "TAKE RECV %d", at.link_id);
174177
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
175-
ESP_LOGE("TAG", "TAKE RECV %d", at.link_id);
178+
ESP_LOGD("TAG", "TAKEN RECV %d", at.link_id);
176179
state = status::RECEIVING;
177180
uint64_t data;
178181
read(data_ready_fd, &data, sizeof(data));
@@ -194,9 +197,10 @@ class DCE : public Module {
194197
if (!wait_to_idle(timeout_ms)) {
195198
return -1;
196199
}
197-
ESP_LOGI("TAG", "TAKE SEND %d", at.link_id);
200+
// Take DTE mutex before issuing send on this link
201+
ESP_LOGD("TAG", "TAKE SEND %d", at.link_id);
198202
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
199-
ESP_LOGE("TAG", "TAKE SEND %d", at.link_id);
203+
ESP_LOGD("TAG", "TAKEN SEND %d", at.link_id);
200204
state = status::SENDING;
201205
memcpy(at.get_buf(), buffer, len_to_send);
202206
ESP_LOG_BUFFER_HEXDUMP("dce", at.get_buf(), len, ESP_LOG_VERBOSE);

components/esp_modem/examples/modem_tcp_client/main/generate/sock_dce.cpp

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -91,16 +91,18 @@ void DCE::perform_at(uint8_t *data, size_t len)
9191
return;
9292
}
9393
}
94-
ESP_LOG_BUFFER_HEXDUMP(TAG, data, len, ESP_LOG_INFO);
94+
// Trace incoming AT bytes when handling a response; use DEBUG level
95+
ESP_LOG_BUFFER_HEXDUMP(TAG, data, len, ESP_LOG_DEBUG);
9596
switch (at.process_data(state, data, len)) {
9697
case Responder::ret::OK:
97-
ESP_LOGW(TAG, "GIVE data %d", at.link_id);
98+
// Release DTE access for this link after processing data
99+
ESP_LOGD(TAG, "GIVE data %d", at.link_id);
98100
xSemaphoreGive(at.s_dte_mutex);
99101
state = status::IDLE;
100102
signal.set(IDLE);
101103
return;
102104
case Responder::ret::FAIL:
103-
ESP_LOGW(TAG, "GIVE data %d", at.link_id);
105+
ESP_LOGD(TAG, "GIVE data %d", at.link_id);
104106
xSemaphoreGive(at.s_dte_mutex);
105107
state = status::FAILED;
106108
signal.set(IDLE);
@@ -116,13 +118,13 @@ void DCE::perform_at(uint8_t *data, size_t len)
116118
std::string_view response((char *)data, len);
117119
switch (at.check_async_replies(state, response)) {
118120
case Responder::ret::OK:
119-
ESP_LOGW(TAG, "GIVE command %d", at.link_id);
121+
ESP_LOGD(TAG, "GIVE command %d", at.link_id);
120122
xSemaphoreGive(at.s_dte_mutex);
121123
state = status::IDLE;
122124
signal.set(IDLE);
123125
return;
124126
case Responder::ret::FAIL:
125-
ESP_LOGW(TAG, "GIVE command %d", at.link_id);
127+
ESP_LOGD(TAG, "GIVE command %d", at.link_id);
126128
xSemaphoreGive(at.s_dte_mutex);
127129
state = status::FAILED;
128130
signal.set(IDLE);
@@ -169,9 +171,10 @@ bool DCE::at_to_sock()
169171
close_sock();
170172
return false;
171173
}
172-
ESP_LOGI(TAG, "TAKE RECV %d", at.link_id);
174+
// Take DTE mutex before issuing receive on this link
175+
ESP_LOGD(TAG, "TAKE RECV %d", at.link_id);
173176
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
174-
ESP_LOGE(TAG, "TAKE RECV %d", at.link_id);
177+
ESP_LOGD(TAG, "TAKEN RECV %d", at.link_id);
175178
state = status::RECEIVING;
176179
at.start_receiving(at.get_buf_len());
177180
return true;
@@ -190,9 +193,10 @@ bool DCE::sock_to_at()
190193
close_sock();
191194
return false;
192195
}
193-
ESP_LOGI(TAG, "TAKE SEND %d", at.link_id);
196+
// Take DTE mutex before issuing send on this link
197+
ESP_LOGD(TAG, "TAKE SEND %d", at.link_id);
194198
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
195-
ESP_LOGE(TAG, "TAKE SEND %d", at.link_id);
199+
ESP_LOGD(TAG, "TAKEN SEND %d", at.link_id);
196200
state = status::SENDING;
197201
int len = ::recv(sock, at.get_buf(), at.get_buf_len(), 0);
198202
if (len < 0) {
@@ -245,7 +249,7 @@ void DCE::start_listening(int port)
245249
}
246250
int opt = 1;
247251
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
248-
ESP_LOGI(TAG, "Socket created");
252+
ESP_LOGD(TAG, "Socket created");
249253
struct sockaddr_in addr = { };
250254
addr.sin_family = AF_INET;
251255
addr.sin_port = htons(port);
@@ -257,7 +261,7 @@ void DCE::start_listening(int port)
257261
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
258262
return;
259263
}
260-
ESP_LOGI(TAG, "Socket bound, port %d", 1883);
264+
ESP_LOGD(TAG, "Socket bound, port %d", 1883);
261265
err = listen(listen_sock, 1);
262266
if (err != 0) {
263267
ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
@@ -276,9 +280,10 @@ bool DCE::connect(std::string host, int port)
276280
// read_callback(data, len);
277281
// return esp_modem::command_result::TIMEOUT;
278282
// });
279-
ESP_LOGI(TAG, "TAKE CONNECT %d", at.link_id);
283+
// Take DTE mutex before starting connect for this link
284+
ESP_LOGD(TAG, "TAKE CONNECT %d", at.link_id);
280285
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
281-
ESP_LOGE(TAG, "TAKE CONNECT %d", at.link_id);
286+
ESP_LOGD(TAG, "TAKEN CONNECT %d", at.link_id);
282287
if (!at.start_connecting(host, port)) {
283288
ESP_LOGE(TAG, "Unable to start connecting");
284289
dte->on_read(nullptr);

components/esp_modem/examples/modem_tcp_client/main/generate/sock_dce.hpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ class Responder {
6464
return total_len;
6565
}
6666

67+
// Unique link identifier used to target multi-connection AT commands
6768
int link_id{s_link_id++};
69+
// Shared mutex guarding DTE access across concurrent DCE instances
6870
static SemaphoreHandle_t s_dte_mutex;
6971
private:
7072
static int s_link_id;
@@ -148,9 +150,10 @@ esp_modem::return_type name(ESP_MODEM_COMMAND_PARAMS(__VA_ARGS__));
148150
return 0;
149151
}
150152
at.clear_offsets();
151-
ESP_LOGI("TAG", "TAKE RECV %d", at.link_id);
153+
// Take DTE mutex before issuing receive on this link
154+
ESP_LOGD("TAG", "TAKE RECV %d", at.link_id);
152155
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
153-
ESP_LOGE("TAG", "TAKE RECV %d", at.link_id);
156+
ESP_LOGD("TAG", "TAKEN RECV %d", at.link_id);
154157
state = status::RECEIVING;
155158
uint64_t data;
156159
read(data_ready_fd, &data, sizeof(data));
@@ -173,9 +176,10 @@ esp_modem::return_type name(ESP_MODEM_COMMAND_PARAMS(__VA_ARGS__));
173176
if (!wait_to_idle(timeout_ms)) {
174177
return -1;
175178
}
176-
ESP_LOGI("TAG", "TAKE SEND %d", at.link_id);
179+
// Take DTE mutex before issuing send on this link
180+
ESP_LOGD("TAG", "TAKE SEND %d", at.link_id);
177181
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
178-
ESP_LOGE("TAG", "TAKE SEND %d", at.link_id);
182+
ESP_LOGD("TAG", "TAKEN SEND %d", at.link_id);
179183
state = status::SENDING;
180184
memcpy(at.get_buf(), buffer, len_to_send);
181185
ESP_LOG_BUFFER_HEXDUMP("dce", at.get_buf(), len, ESP_LOG_VERBOSE);

components/esp_modem/examples/modem_tcp_client/main/modem_client.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ extern "C" void app_main(void)
9191
/* Init and register system/core components */
9292
ESP_ERROR_CHECK(esp_netif_init());
9393
ESP_ERROR_CHECK(esp_event_loop_create_default());
94+
// Default to INFO; individual modules use DEBUG for verbose tracing
9495
esp_log_level_set("*", ESP_LOG_INFO);
9596

9697
event_group = xEventGroupCreate();
@@ -179,7 +180,7 @@ static void perform(void* ctx)
179180
while (dce->perform_sock()) {
180181
ESP_LOGV(TAG, "...performing");
181182
}
182-
ESP_LOGE(TAG, "Loop exit.. retrying");
183+
ESP_LOGD(TAG, "Loop exit.. retrying");
183184
// handle disconnections errors
184185
if (!dce->init()) {
185186
ESP_LOGE(TAG, "Failed to reinit network");

components/esp_modem/examples/modem_tcp_client/main/sock_commands_bg96.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
192192
last_pos = (char *)memchr(recv_data + 1 + actual_len, 'O', MIN_MESSAGE);
193193
if (last_pos == nullptr || last_pos[1] != 'K') {
194194
data_to_recv = 0;
195-
ESP_LOGE(TAG, "no OK after data");
195+
ESP_LOGD(TAG, "no OK after data");
196196
return ret::FAIL;
197197
}
198198
}
@@ -278,7 +278,7 @@ Responder::ret Responder::send(std::string_view response)
278278
if (ack < total) {
279279
ESP_LOGD(TAG, "all sending data are not ack (missing %d bytes acked)", (total - ack));
280280
if (total - ack > 64) {
281-
ESP_LOGW(TAG, "Need a pause: missing %d bytes acked", (total - ack));
281+
ESP_LOGD(TAG, "Need a pause: missing %d bytes acked", (total - ack));
282282
return ret::NEED_MORE_TIME;
283283
}
284284
}

components/esp_modem/examples/modem_tcp_client/main/sock_commands_espat.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ command_result net_open(CommandableIf *t)
6464
ESP_LOGE(TAG, "Failed to enable multiple connections mode");
6565
return ret;
6666
}
67-
ESP_LOGI(TAG, "Multiple connections mode enabled");
67+
ESP_LOGD(TAG, "Multiple connections mode enabled");
6868

6969
// Set passive receive mode (1) for better control
7070
for (int i = 0; i < 2; i++) {
@@ -353,14 +353,14 @@ Responder::ret Responder::check_async_replies(status state, std::string_view &re
353353
if (response.find("WIFI CONNECTED") != std::string::npos) {
354354
ESP_LOGI(TAG, "WiFi connected");
355355
} else if (response.find("WIFI DISCONNECTED") != std::string::npos) {
356-
ESP_LOGW(TAG, "WiFi disconnected");
356+
ESP_LOGD(TAG, "WiFi disconnected");
357357
}
358358

359359
// Handle TCP status messages (multiple connections format: <link ID>,CONNECT or <link ID>,CLOSED)
360360
if (response.find("CONNECT") != std::string::npos && state == status::CONNECTING) {
361361
return connect(response);
362362
} else if (response.find("CLOSED") != std::string::npos) {
363-
ESP_LOGW(TAG, "TCP connection closed");
363+
ESP_LOGD(TAG, "TCP connection closed");
364364
return ret::FAIL;
365365
}
366366

components/esp_modem/examples/modem_tcp_client/main/sock_commands_sim7600.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,10 @@ command_result tcp_send(CommandableIf *term, uint8_t *data, size_t len)
8686
return ret;
8787
}
8888
ret = command_result::TIMEOUT;
89-
ESP_LOGW(TAG, "Before setting...");
89+
ESP_LOGD(TAG, "Before setting...");
9090
term->on_read([&ret](uint8_t *cmd_data, size_t cmd_len) {
9191
std::string_view response((char *)cmd_data, cmd_len);
92-
ESP_LOGW(TAG, "CIPSEND response %.*s", static_cast<int>(response.size()), response.data());
92+
ESP_LOGD(TAG, "CIPSEND response %.*s", static_cast<int>(response.size()), response.data());
9393

9494
if (response.find("+CIPSEND:") != std::string::npos) {
9595
ret = command_result::OK;
@@ -98,7 +98,7 @@ command_result tcp_send(CommandableIf *term, uint8_t *data, size_t len)
9898
}
9999
return ret;
100100
});
101-
ESP_LOGW(TAG, "Before writing...");
101+
ESP_LOGD(TAG, "Before writing...");
102102
auto written = term->write(data, len);
103103
if (written != len) {
104104
ESP_LOGE(TAG, "written %d (%d)...", written, len);

0 commit comments

Comments
 (0)