From 6f379678f014deae1a5103cec2bf4f4536a56c6b Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Wed, 5 May 2021 23:56:48 +0200 Subject: [PATCH 1/7] Better fix for "ack timeout 4" client disconnects. After my first attempt at fixing the client disconnects (https://github.com/OttoWinter/AsyncTCP/pull/4) got merged into AsyncTCP, it turned out that there was regression for some devices: the connection stability actually went down instead of up. After a lot of debugging and discussion with @glmnet (some of the results can be found in the above pull request discussion), we came up with an improved fix for the disconnect issues. **Changed:** The code that checks for ACK timeouts has been simplified in such way, that only two timestamps are now used to determine if an ACK timeout has happened: the time of the last sent packet (this was already recorded), and the time of the last received ACK from the client (this has been added). Using these timestamps, there is no more need for a separate field to keep track if we are waiting for an ACK or not (`_pcb_busy`). Therefore, this field was completely removed from the code. While I was at it, I renamed a few variables to make the code easier to read and more consistent. **Results:** I connected Home Assistant plus 8 OTA loggers at the same time, using very verbose logging output. This normally was an easy way to trigger the disconnect errors. It turned out, this solution runs as solid for me, as when disabling the ACK timeout checks completely (using `AsyncClient::setAckTimeout(0)`). --- src/AsyncTCP.cpp | 41 ++++++++++++++++++++--------------------- src/AsyncTCP.h | 6 +++--- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/AsyncTCP.cpp b/src/AsyncTCP.cpp index 471e8fcf..771c0a89 100644 --- a/src/AsyncTCP.cpp +++ b/src/AsyncTCP.cpp @@ -575,11 +575,10 @@ AsyncClient::AsyncClient(tcp_pcb* pcb) , _pb_cb_arg(0) , _timeout_cb(0) , _timeout_cb_arg(0) -, _pcb_busy(0) -, _pcb_sent_at(0) , _ack_pcb(true) -, _rx_last_packet(0) -, _rx_since_timeout(0) +, _tx_last_packet(0) +, _rx_timeout(0) +, _rx_last_ack(0) , _ack_timeout(ASYNC_MAX_ACK_TIME) , _connect_port(0) , prev(NULL) @@ -783,14 +782,12 @@ size_t AsyncClient::add(const char* data, size_t size, uint8_t apiflags) { } bool AsyncClient::send(){ - auto pcb_sent_at_backup = _pcb_sent_at; - _pcb_sent_at = millis(); - _pcb_busy++; + auto backup = _tx_last_packet; + _tx_last_packet = millis(); if (_tcp_output(_pcb, _closed_slot) == ERR_OK) { return true; } - _pcb_sent_at = pcb_sent_at_backup; - _pcb_busy--; + _tx_last_packet = backup; return false; } @@ -870,7 +867,6 @@ int8_t AsyncClient::_connected(void* pcb, int8_t err){ _pcb = reinterpret_cast(pcb); if(_pcb){ _rx_last_packet = millis(); - _pcb_busy = 0; // tcp_recv(_pcb, &_tcp_recv); // tcp_sent(_pcb, &_tcp_sent); // tcp_poll(_pcb, &_tcp_poll, 1); @@ -932,10 +928,10 @@ int8_t AsyncClient::_fin(tcp_pcb* pcb, int8_t err) { int8_t AsyncClient::_sent(tcp_pcb* pcb, uint16_t len) { _rx_last_packet = millis(); + _rx_last_ack = millis(); //log_i("%u", len); - _pcb_busy--; if(_sent_cb) { - _sent_cb(_sent_cb_arg, this, len, (millis() - _pcb_sent_at)); + _sent_cb(_sent_cb_arg, this, len, (millis() - _tx_last_packet)); } return ERR_OK; } @@ -978,15 +974,18 @@ int8_t AsyncClient::_poll(tcp_pcb* pcb){ uint32_t now = millis(); // ACK Timeout - if(_pcb_busy > 0 && _ack_timeout && (now - _pcb_sent_at) >= _ack_timeout){ - _pcb_busy = 0; - log_w("ack timeout %d", pcb->state); - if(_timeout_cb) - _timeout_cb(_timeout_cb_arg, this, (now - _pcb_sent_at)); - return ERR_OK; + if(_ack_timeout){ + uint32_t one_day = 86400000; + bool last_tx_is_after_last_ack = (_rx_last_ack - _tx_last_packet + one_day) < one_day; + if(last_tx_is_after_last_ack && (now - _tx_last_packet) >= _ack_timeout) { + log_w("ack timeout %d", pcb->state); + if(_timeout_cb) + _timeout_cb(_timeout_cb_arg, this, (now - _tx_last_packet)); + return ERR_OK; + } } // RX Timeout - if(_rx_since_timeout && (now - _rx_last_packet) >= (_rx_since_timeout * 1000)){ + if(_rx_timeout && (now - _rx_last_packet) >= (_rx_timeout * 1000)) { log_w("rx timeout %d", pcb->state); _close(); return ERR_OK; @@ -1045,11 +1044,11 @@ size_t AsyncClient::write(const char* data, size_t size, uint8_t apiflags) { } void AsyncClient::setRxTimeout(uint32_t timeout){ - _rx_since_timeout = timeout; + _rx_timeout = timeout; } uint32_t AsyncClient::getRxTimeout(){ - return _rx_since_timeout; + return _rx_timeout; } uint32_t AsyncClient::getAckTimeout(){ diff --git a/src/AsyncTCP.h b/src/AsyncTCP.h index fcd511bb..9786490d 100644 --- a/src/AsyncTCP.h +++ b/src/AsyncTCP.h @@ -160,12 +160,12 @@ class AsyncClient { AcConnectHandler _poll_cb; void* _poll_cb_arg; - uint32_t _pcb_busy; - uint32_t _pcb_sent_at; bool _ack_pcb; + uint32_t _tx_last_packet; uint32_t _rx_ack_len; uint32_t _rx_last_packet; - uint32_t _rx_since_timeout; + uint32_t _rx_timeout; + uint32_t _rx_last_ack; uint32_t _ack_timeout; uint16_t _connect_port; From 18ac673e82bfd2542c0ba12f10615963c13fb5b9 Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Thu, 6 May 2021 00:41:06 +0200 Subject: [PATCH 2/7] Define one_day var as a const. --- src/AsyncTCP.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AsyncTCP.cpp b/src/AsyncTCP.cpp index 771c0a89..59ef1861 100644 --- a/src/AsyncTCP.cpp +++ b/src/AsyncTCP.cpp @@ -975,7 +975,7 @@ int8_t AsyncClient::_poll(tcp_pcb* pcb){ // ACK Timeout if(_ack_timeout){ - uint32_t one_day = 86400000; + const uint32_t one_day = 86400000; bool last_tx_is_after_last_ack = (_rx_last_ack - _tx_last_packet + one_day) < one_day; if(last_tx_is_after_last_ack && (now - _tx_last_packet) >= _ack_timeout) { log_w("ack timeout %d", pcb->state); From 7c767c3f6b36adb0c54bd09f7070b23d46d544d8 Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Sun, 9 May 2021 19:11:18 -0300 Subject: [PATCH 3/7] Bump version to 1.2.2 --- library.json | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/library.json b/library.json index c5e1f447..bf6a4121 100644 --- a/library.json +++ b/library.json @@ -1,18 +1,16 @@ { - "name":"AsyncTCP-esphome", - "description":"Asynchronous TCP Library for ESP32", - "keywords":"async,tcp", - "authors": - { + "name": "AsyncTCP-esphome", + "description": "Asynchronous TCP Library for ESP32", + "keywords": "async,tcp", + "authors": { "name": "Hristo Gochkov", "maintainer": true }, - "repository": - { + "repository": { "type": "git", - "url": "https://github.com/OttoWinter/AsyncTCP.git" + "url": "https://github.com/esphome/AsyncTCP.git" }, - "version": "1.2.1", + "version": "1.2.2", "license": "LGPL-3.0", "frameworks": "arduino", "platforms": "espressif32", From fb0411b699ec9c81f3a7cab72a8574ae380f55dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Tue, 9 Aug 2022 07:34:14 +0200 Subject: [PATCH 4/7] Adapt for LibreTuya compatibility (#3) --- library.json | 2 +- src/AsyncTCP.cpp | 29 ++++++++++++++++++++++++++++- src/AsyncTCP.h | 16 +++++++++++++++- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/library.json b/library.json index bf6a4121..0f1142fa 100644 --- a/library.json +++ b/library.json @@ -13,7 +13,7 @@ "version": "1.2.2", "license": "LGPL-3.0", "frameworks": "arduino", - "platforms": "espressif32", + "platforms": ["espressif32", "libretuya"], "build": { "libCompatMode": 2 } diff --git a/src/AsyncTCP.cpp b/src/AsyncTCP.cpp index 59ef1861..ff82593b 100644 --- a/src/AsyncTCP.cpp +++ b/src/AsyncTCP.cpp @@ -29,7 +29,9 @@ extern "C"{ #include "lwip/dns.h" #include "lwip/err.h" } +#if CONFIG_ASYNC_TCP_USE_WDT #include "esp_task_wdt.h" +#endif /* * TCP/IP Event Task @@ -238,7 +240,7 @@ static bool _start_async_task(){ return false; } if(!_async_service_task_handle){ - customTaskCreateUniversal(_async_service_task, "async_tcp", 8192 * 2, NULL, 3, &_async_service_task_handle, CONFIG_ASYNC_TCP_RUNNING_CORE); + customTaskCreateUniversal(_async_service_task, "async_tcp", CONFIG_ASYNC_TCP_STACK_SIZE, NULL, 3, &_async_service_task_handle, CONFIG_ASYNC_TCP_RUNNING_CORE); if(!_async_service_task_handle){ return false; } @@ -704,8 +706,12 @@ bool AsyncClient::connect(IPAddress ip, uint16_t port){ } ip_addr_t addr; +#if LWIP_IPV4 && LWIP_IPV6 addr.type = IPADDR_TYPE_V4; addr.u_addr.ip4.addr = ip; +#else + addr.addr = ip; +#endif tcp_pcb* pcb = tcp_new_ip_type(IPADDR_TYPE_V4); if (!pcb){ @@ -733,7 +739,11 @@ bool AsyncClient::connect(const char* host, uint16_t port){ err_t err = dns_gethostbyname(host, &addr, (dns_found_callback)&_tcp_dns_found, this); if(err == ERR_OK) { +#if LWIP_IPV4 && LWIP_IPV6 return connect(IPAddress(addr.u_addr.ip4.addr), port); +#else + return connect(IPAddress(addr.addr), port); +#endif } else if(err == ERR_INPROGRESS) { _connect_port = port; return true; @@ -998,8 +1008,13 @@ int8_t AsyncClient::_poll(tcp_pcb* pcb){ } void AsyncClient::_dns_found(struct ip_addr *ipaddr){ +#if LWIP_IPV4 && LWIP_IPV6 if(ipaddr && ipaddr->u_addr.ip4.addr){ connect(IPAddress(ipaddr->u_addr.ip4.addr), _connect_port); +#else + if (ipaddr && ipaddr->addr){ + connect(IPAddress(ipaddr->addr), _connect_port); +#endif } else { if(_error_cb) { _error_cb(_error_cb_arg, this, -55); @@ -1088,7 +1103,11 @@ uint32_t AsyncClient::getRemoteAddress() { if(!_pcb) { return 0; } +#if LWIP_IPV4 && LWIP_IPV6 return _pcb->remote_ip.u_addr.ip4.addr; +#else + return _pcb->remote_ip.addr; +#endif } uint16_t AsyncClient::getRemotePort() { @@ -1102,7 +1121,11 @@ uint32_t AsyncClient::getLocalAddress() { if(!_pcb) { return 0; } +#if LWIP_IPV4 && LWIP_IPV6 return _pcb->local_ip.u_addr.ip4.addr; +#else + return _pcb->local_ip.addr; +#endif } uint16_t AsyncClient::getLocalPort() { @@ -1298,8 +1321,12 @@ void AsyncServer::begin(){ } ip_addr_t local_addr; +#if LWIP_IPV4 && LWIP_IPV6 local_addr.type = IPADDR_TYPE_V4; local_addr.u_addr.ip4.addr = (uint32_t) _addr; +#else + local_addr.addr = (uint32_t) _addr; +#endif err = _tcp_bind(_pcb, &local_addr, _port); if (err != ERR_OK) { diff --git a/src/AsyncTCP.h b/src/AsyncTCP.h index 9786490d..74659db8 100644 --- a/src/AsyncTCP.h +++ b/src/AsyncTCP.h @@ -23,12 +23,22 @@ #define ASYNCTCP_H_ #include "IPAddress.h" -#include "sdkconfig.h" #include + +#ifndef LIBRETUYA +#include "sdkconfig.h" extern "C" { #include "freertos/semphr.h" #include "lwip/pbuf.h" } +#else +extern "C" { + #include + #include +} +#define CONFIG_ASYNC_TCP_RUNNING_CORE -1 //any available core +#define CONFIG_ASYNC_TCP_USE_WDT 0 +#endif //If core is not defined, then we are running in Arduino or PIO #ifndef CONFIG_ASYNC_TCP_RUNNING_CORE @@ -36,6 +46,10 @@ extern "C" { #define CONFIG_ASYNC_TCP_USE_WDT 1 //if enabled, adds between 33us and 200us per event #endif +#ifndef CONFIG_ASYNC_TCP_STACK_SIZE +#define CONFIG_ASYNC_TCP_STACK_SIZE 8192 * 2 +#endif + class AsyncClient; #define ASYNC_MAX_ACK_TIME 5000 From c04346412903c0ccd4eaab565b7089e96b55003e Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 9 Aug 2022 17:34:36 +1200 Subject: [PATCH 5/7] Bump version to 2.0.0 --- library.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.json b/library.json index 0f1142fa..468e29ce 100644 --- a/library.json +++ b/library.json @@ -10,7 +10,7 @@ "type": "git", "url": "https://github.com/esphome/AsyncTCP.git" }, - "version": "1.2.2", + "version": "2.0.0", "license": "LGPL-3.0", "frameworks": "arduino", "platforms": ["espressif32", "libretuya"], From c53368456fc79630b39b37a8464455c92c50b6dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Fri, 1 Sep 2023 04:12:55 +0200 Subject: [PATCH 6/7] Rename LibreTuya to LibreTiny (#4) --- library.json | 2 +- src/AsyncTCP.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library.json b/library.json index 468e29ce..dba64db1 100644 --- a/library.json +++ b/library.json @@ -13,7 +13,7 @@ "version": "2.0.0", "license": "LGPL-3.0", "frameworks": "arduino", - "platforms": ["espressif32", "libretuya"], + "platforms": ["espressif32", "libretiny"], "build": { "libCompatMode": 2 } diff --git a/src/AsyncTCP.h b/src/AsyncTCP.h index 74659db8..1e781b37 100644 --- a/src/AsyncTCP.h +++ b/src/AsyncTCP.h @@ -25,7 +25,7 @@ #include "IPAddress.h" #include -#ifndef LIBRETUYA +#ifndef LIBRETINY #include "sdkconfig.h" extern "C" { #include "freertos/semphr.h" From dc64fedec0c953ba4f52b6bb8bc5ef1a3abdc22d Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 1 Sep 2023 14:18:18 +1200 Subject: [PATCH 7/7] Bump version to 2.0.1 --- library.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.json b/library.json index dba64db1..957bd197 100644 --- a/library.json +++ b/library.json @@ -10,7 +10,7 @@ "type": "git", "url": "https://github.com/esphome/AsyncTCP.git" }, - "version": "2.0.0", + "version": "2.0.1", "license": "LGPL-3.0", "frameworks": "arduino", "platforms": ["espressif32", "libretiny"],