From 4c96de3530b194a2bee9490ea250ae0642465ab9 Mon Sep 17 00:00:00 2001 From: Tolentino Cotesta Date: Wed, 20 Nov 2024 10:45:59 +0100 Subject: [PATCH] AsyncTCP --- examples/csvLogger/csvLogger.ino | 18 +- src/AsyncFsWebServer.cpp | 2 +- src/CaptivePortal.hpp | 2 +- src/ESPAsyncWebServer/src/esp32/AsyncTCP.cpp | 527 +++++++++++++------ src/ESPAsyncWebServer/src/esp32/AsyncTCP.h | 93 +++- src/ESPAsyncWebServer/src/test.zip | Bin 0 -> 64094 bytes src/SetupConfig.hpp | 14 +- 7 files changed, 454 insertions(+), 202 deletions(-) create mode 100644 src/ESPAsyncWebServer/src/test.zip diff --git a/examples/csvLogger/csvLogger.ino b/examples/csvLogger/csvLogger.ino index f2e3bd42..e10db638 100644 --- a/examples/csvLogger/csvLogger.ino +++ b/examples/csvLogger/csvLogger.ino @@ -13,17 +13,6 @@ struct tm ntpTime; const char* basePath = "/csv"; -//////////////////////////////// NTP Time ///////////////////////////////////////// -void getUpdatedtime(const uint32_t timeout) { - uint32_t start = millis(); - do { - time_t now = time(nullptr); - ntpTime = *localtime(&now); - delay(1); - } while (millis() - start < timeout && ntpTime.tm_year <= (1970 - 1900)); -} - - //////////////////////////////// Filesystem ///////////////////////////////////////// bool startFilesystem(){ if (LittleFS.begin()){ @@ -40,7 +29,8 @@ bool startFilesystem(){ //////////////////////////// Append a row to csv file /////////////////////////////////// bool appenRow() { - getUpdatedtime(10); + + getLocalTime(&ntpTime, 10); char filename[24]; snprintf(filename, sizeof(filename), @@ -132,9 +122,9 @@ void setup() { #elif defined(ESP32) configTzTime(MYTZ, "time.google.com", "time.windows.com", "pool.ntp.org"); #endif - // Wait for NTP sync (with timeout) - getUpdatedtime(5000); + getLocalTime(&ntpTime, 5000); + // Create csv logs folder if not exists if (!LittleFS.exists(basePath)) { diff --git a/src/AsyncFsWebServer.cpp b/src/AsyncFsWebServer.cpp index 2cb095ee..21c69928 100644 --- a/src/AsyncFsWebServer.cpp +++ b/src/AsyncFsWebServer.cpp @@ -539,7 +539,7 @@ void AsyncFsWebServer::update_second(AsyncWebServerRequest *request) { void AsyncFsWebServer::update_first(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) { if (!m_contentLen) { - AsyncWebHeader* h = request->getHeader("Content-Length"); + const AsyncWebHeader* h = request->getHeader("Content-Length"); if (h->value().length()) { m_contentLen = h->value().toInt(); log_info("Firmware size: %d", m_contentLen); diff --git a/src/CaptivePortal.hpp b/src/CaptivePortal.hpp index d3c6f71a..2e85cf6b 100644 --- a/src/CaptivePortal.hpp +++ b/src/CaptivePortal.hpp @@ -30,7 +30,7 @@ class CaptiveRequestHandler : public AsyncWebHandler const String targetURL; - bool canHandle(AsyncWebServerRequest *request) override + bool canHandle( AsyncWebServerRequest *request) override { // redirect if not in wifi client mode (through filter) // and request for different host (due to DNS * response) diff --git a/src/ESPAsyncWebServer/src/esp32/AsyncTCP.cpp b/src/ESPAsyncWebServer/src/esp32/AsyncTCP.cpp index 73e5bcec..2345be13 100644 --- a/src/ESPAsyncWebServer/src/esp32/AsyncTCP.cpp +++ b/src/ESPAsyncWebServer/src/esp32/AsyncTCP.cpp @@ -19,8 +19,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifdef ESP32 - #include "Arduino.h" #include "AsyncTCP.h" @@ -31,10 +29,35 @@ extern "C"{ #include "lwip/dns.h" #include "lwip/err.h" } +#if CONFIG_ASYNC_TCP_USE_WDT #include "esp_task_wdt.h" +#endif + +// Required for: +// https://github.com/espressif/arduino-esp32/blob/3.0.3/libraries/Network/src/NetworkInterface.cpp#L37-L47 +#if ESP_IDF_VERSION_MAJOR >= 5 +#include +#endif + +#define TAG "AsyncTCP" -#define CONFIG_ASYNC_TCP_STACK 2*8192 -#define CONFIG_ASYNC_TCP_PRIORITY 3 +// https://github.com/espressif/arduino-esp32/issues/10526 +#ifdef CONFIG_LWIP_TCPIP_CORE_LOCKING +#define TCP_MUTEX_LOCK() \ + if (!sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER)) { \ + LOCK_TCPIP_CORE(); \ + } + +#define TCP_MUTEX_UNLOCK() \ + if (sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER)) { \ + UNLOCK_TCPIP_CORE(); \ + } +#else // CONFIG_LWIP_TCPIP_CORE_LOCKING +#define TCP_MUTEX_LOCK() +#define TCP_MUTEX_UNLOCK() +#endif // CONFIG_LWIP_TCPIP_CORE_LOCKING + +#define INVALID_CLOSED_SLOT -1 /* * TCP/IP Event Task @@ -49,7 +72,7 @@ typedef struct { void *arg; union { struct { - void * pcb; + tcp_pcb * pcb; int8_t err; } connected; struct { @@ -81,7 +104,7 @@ typedef struct { }; } lwip_event_packet_t; -static xQueueHandle _async_queue; +static QueueHandle_t _async_queue; static TaskHandle_t _async_service_task_handle = NULL; @@ -100,7 +123,7 @@ static uint32_t _closed_index = []() { static inline bool _init_async_event_queue(){ if(!_async_queue){ - _async_queue = xQueueCreate(128, sizeof(lwip_event_packet_t *)); + _async_queue = xQueueCreate(CONFIG_ASYNC_TCP_QUEUE_SIZE, sizeof(lwip_event_packet_t *)); if(!_async_queue){ return false; } @@ -218,14 +241,32 @@ static void _stop_async_task(){ } } */ + +static bool customTaskCreateUniversal( + TaskFunction_t pxTaskCode, + const char * const pcName, + const uint32_t usStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask, + const BaseType_t xCoreID) { +#ifndef CONFIG_FREERTOS_UNICORE + if(xCoreID >= 0 && xCoreID < 2) { + return xTaskCreatePinnedToCore(pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask, xCoreID); + } else { +#endif + return xTaskCreate(pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask); +#ifndef CONFIG_FREERTOS_UNICORE + } +#endif +} + static bool _start_async_task(){ if(!_init_async_event_queue()){ return false; } if(!_async_service_task_handle){ - xTaskCreateUniversal(_async_service_task, "async_tcp", - CONFIG_ASYNC_TCP_STACK, NULL, CONFIG_ASYNC_TCP_PRIORITY, - &_async_service_task_handle, CONFIG_ASYNC_TCP_RUNNING_CORE); + customTaskCreateUniversal(_async_service_task, "async_tcp", CONFIG_ASYNC_TCP_STACK_SIZE, NULL, CONFIG_ASYNC_TCP_PRIORITY, &_async_service_task_handle, CONFIG_ASYNC_TCP_RUNNING_CORE); if(!_async_service_task_handle){ return false; } @@ -239,12 +280,10 @@ static bool _start_async_task(){ static int8_t _tcp_clear_events(void * arg) { lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); - if (NULL != e) { - e->event = LWIP_TCP_CLEAR; - e->arg = arg; - if (!_prepend_async_event(&e)) { - free((void*)(e)); - } + e->event = LWIP_TCP_CLEAR; + e->arg = arg; + if (!_prepend_async_event(&e)) { + free((void*)(e)); } return ERR_OK; } @@ -252,14 +291,12 @@ static int8_t _tcp_clear_events(void * arg) { static int8_t _tcp_connected(void * arg, tcp_pcb * pcb, int8_t err) { //ets_printf("+C: 0x%08x\n", pcb); lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); - if (NULL != e) { - e->event = LWIP_TCP_CONNECTED; - e->arg = arg; - e->connected.pcb = pcb; - e->connected.err = err; - if (!_prepend_async_event(&e)) { - free((void*)(e)); - } + e->event = LWIP_TCP_CONNECTED; + e->arg = arg; + e->connected.pcb = pcb; + e->connected.err = err; + if (!_prepend_async_event(&e)) { + free((void*)(e)); } return ERR_OK; } @@ -267,38 +304,34 @@ static int8_t _tcp_connected(void * arg, tcp_pcb * pcb, int8_t err) { static int8_t _tcp_poll(void * arg, struct tcp_pcb * pcb) { //ets_printf("+P: 0x%08x\n", pcb); lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); - if (NULL != e) { - e->event = LWIP_TCP_POLL; - e->arg = arg; - e->poll.pcb = pcb; - if (!_send_async_event(&e)) { - free((void*)(e)); - } + e->event = LWIP_TCP_POLL; + e->arg = arg; + e->poll.pcb = pcb; + if (!_send_async_event(&e)) { + free((void*)(e)); } return ERR_OK; } static int8_t _tcp_recv(void * arg, struct tcp_pcb * pcb, struct pbuf *pb, int8_t err) { lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); - if (NULL != e) { - e->arg = arg; - if(pb){ - //ets_printf("+R: 0x%08x\n", pcb); - e->event = LWIP_TCP_RECV; - e->recv.pcb = pcb; - e->recv.pb = pb; - e->recv.err = err; - } else { - //ets_printf("+F: 0x%08x\n", pcb); - e->event = LWIP_TCP_FIN; - e->fin.pcb = pcb; - e->fin.err = err; - //close the PCB in LwIP thread - AsyncClient::_s_lwip_fin(e->arg, e->fin.pcb, e->fin.err); - } - if (!_send_async_event(&e)) { - free((void*)(e)); - } + e->arg = arg; + if(pb){ + //ets_printf("+R: 0x%08x\n", pcb); + e->event = LWIP_TCP_RECV; + e->recv.pcb = pcb; + e->recv.pb = pb; + e->recv.err = err; + } else { + //ets_printf("+F: 0x%08x\n", pcb); + e->event = LWIP_TCP_FIN; + e->fin.pcb = pcb; + e->fin.err = err; + //close the PCB in LwIP thread + AsyncClient::_s_lwip_fin(e->arg, e->fin.pcb, e->fin.err); + } + if (!_send_async_event(&e)) { + free((void*)(e)); } return ERR_OK; } @@ -306,14 +339,12 @@ static int8_t _tcp_recv(void * arg, struct tcp_pcb * pcb, struct pbuf *pb, int8_ static int8_t _tcp_sent(void * arg, struct tcp_pcb * pcb, uint16_t len) { //ets_printf("+S: 0x%08x\n", pcb); lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); - if (NULL != e) { - e->event = LWIP_TCP_SENT; - e->arg = arg; - e->sent.pcb = pcb; - e->sent.len = len; - if (!_send_async_event(&e)) { - free((void*)(e)); - } + e->event = LWIP_TCP_SENT; + e->arg = arg; + e->sent.pcb = pcb; + e->sent.len = len; + if (!_send_async_event(&e)) { + free((void*)(e)); } return ERR_OK; } @@ -321,44 +352,38 @@ static int8_t _tcp_sent(void * arg, struct tcp_pcb * pcb, uint16_t len) { static void _tcp_error(void * arg, int8_t err) { //ets_printf("+E: 0x%08x\n", arg); lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); - if (NULL != e) { - e->event = LWIP_TCP_ERROR; - e->arg = arg; - e->error.err = err; - if (!_send_async_event(&e)) { - free((void*)(e)); - } + e->event = LWIP_TCP_ERROR; + e->arg = arg; + e->error.err = err; + if (!_send_async_event(&e)) { + free((void*)(e)); } } static void _tcp_dns_found(const char * name, struct ip_addr * ipaddr, void * arg) { lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); //ets_printf("+DNS: name=%s ipaddr=0x%08x arg=%x\n", name, ipaddr, arg); - if (NULL != e) { - e->event = LWIP_TCP_DNS; - e->arg = arg; - e->dns.name = name; - if (ipaddr) { - memcpy(&e->dns.addr, ipaddr, sizeof(struct ip_addr)); - } else { - memset(&e->dns.addr, 0, sizeof(e->dns.addr)); - } - if (!_send_async_event(&e)) { - free((void*)(e)); - } + e->event = LWIP_TCP_DNS; + e->arg = arg; + e->dns.name = name; + if (ipaddr) { + memcpy(&e->dns.addr, ipaddr, sizeof(struct ip_addr)); + } else { + memset(&e->dns.addr, 0, sizeof(e->dns.addr)); + } + if (!_send_async_event(&e)) { + free((void*)(e)); } } //Used to switch out from LwIP thread static int8_t _tcp_accept(void * arg, AsyncClient * client) { lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); - if (NULL != e) { - e->event = LWIP_TCP_ACCEPT; - e->arg = arg; - e->accept.client = client; - if (!_prepend_async_event(&e)) { - free((void*)(e)); - } + e->event = LWIP_TCP_ACCEPT; + e->arg = arg; + e->accept.client = client; + if (!_prepend_async_event(&e)) { + free((void*)(e)); } return ERR_OK; } @@ -397,7 +422,7 @@ typedef struct { static err_t _tcp_output_api(struct tcpip_api_call_data *api_call_msg){ tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; msg->err = ERR_CONN; - if(msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) { + if(msg->closed_slot == INVALID_CLOSED_SLOT || !_closed_slots[msg->closed_slot]) { msg->err = tcp_output(msg->pcb); } return msg->err; @@ -417,7 +442,7 @@ static esp_err_t _tcp_output(tcp_pcb * pcb, int8_t closed_slot) { static err_t _tcp_write_api(struct tcpip_api_call_data *api_call_msg){ tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; msg->err = ERR_CONN; - if(msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) { + if(msg->closed_slot == INVALID_CLOSED_SLOT || !_closed_slots[msg->closed_slot]) { msg->err = tcp_write(msg->pcb, msg->write.data, msg->write.size, msg->write.apiflags); } return msg->err; @@ -440,7 +465,9 @@ static esp_err_t _tcp_write(tcp_pcb * pcb, int8_t closed_slot, const char* data, static err_t _tcp_recved_api(struct tcpip_api_call_data *api_call_msg){ tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; msg->err = ERR_CONN; - if(msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) { + if(msg->closed_slot == INVALID_CLOSED_SLOT || !_closed_slots[msg->closed_slot]) { + // if(msg->closed_slot != INVALID_CLOSED_SLOT && !_closed_slots[msg->closed_slot]) { + // if(msg->closed_slot != INVALID_CLOSED_SLOT) { msg->err = 0; tcp_recved(msg->pcb, msg->received); } @@ -462,7 +489,7 @@ static esp_err_t _tcp_recved(tcp_pcb * pcb, int8_t closed_slot, size_t len) { static err_t _tcp_close_api(struct tcpip_api_call_data *api_call_msg){ tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; msg->err = ERR_CONN; - if(msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) { + if(msg->closed_slot == INVALID_CLOSED_SLOT || !_closed_slots[msg->closed_slot]) { msg->err = tcp_close(msg->pcb); } return msg->err; @@ -482,7 +509,7 @@ static esp_err_t _tcp_close(tcp_pcb * pcb, int8_t closed_slot) { static err_t _tcp_abort_api(struct tcpip_api_call_data *api_call_msg){ tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; msg->err = ERR_CONN; - if(msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) { + if(msg->closed_slot == INVALID_CLOSED_SLOT || !_closed_slots[msg->closed_slot]) { tcp_abort(msg->pcb); } return msg->err; @@ -578,26 +605,27 @@ AsyncClient::AsyncClient(tcp_pcb* pcb) , _pb_cb_arg(0) , _timeout_cb(0) , _timeout_cb_arg(0) -, _pcb_busy(false) -, _pcb_sent_at(0) , _ack_pcb(true) -, _rx_last_packet(0) -, _rx_since_timeout(0) -, _ack_timeout(ASYNC_MAX_ACK_TIME) +, _tx_last_packet(0) +, _rx_timeout(0) +, _rx_last_ack(0) +, _ack_timeout(CONFIG_ASYNC_TCP_MAX_ACK_TIME) , _connect_port(0) , prev(NULL) , next(NULL) { _pcb = pcb; - _closed_slot = -1; + _closed_slot = INVALID_CLOSED_SLOT; if(_pcb){ - _allocate_closed_slot(); _rx_last_packet = millis(); tcp_arg(_pcb, this); tcp_recv(_pcb, &_tcp_recv); tcp_sent(_pcb, &_tcp_sent); tcp_err(_pcb, &_tcp_error); tcp_poll(_pcb, &_tcp_poll, 1); + if(!_allocate_closed_slot()) { + _close(); + } } } @@ -697,9 +725,9 @@ void AsyncClient::onPoll(AcConnectHandler cb, void* arg){ * Main Public Methods * */ -bool AsyncClient::connect(IPAddress ip, uint16_t port){ +bool AsyncClient::_connect(ip_addr_t addr, uint16_t port){ if (_pcb){ - log_w("already connected, state %d", _pcb->state); + log_d("already connected, state %d", _pcb->state); return false; } if(!_start_async_task()){ @@ -707,28 +735,50 @@ bool AsyncClient::connect(IPAddress ip, uint16_t port){ return false; } - ip_addr_t addr; - addr.type = IPADDR_TYPE_V4; - addr.u_addr.ip4.addr = ip; + if(!_allocate_closed_slot()) { + log_e("failed to allocate: closed slot full"); + return false; + } - tcp_pcb* pcb = tcp_new_ip_type(IPADDR_TYPE_V4); + TCP_MUTEX_LOCK(); + tcp_pcb* pcb = tcp_new_ip_type(addr.type); if (!pcb){ + TCP_MUTEX_UNLOCK(); log_e("pcb == NULL"); return false; } - tcp_arg(pcb, this); tcp_err(pcb, &_tcp_error); tcp_recv(pcb, &_tcp_recv); tcp_sent(pcb, &_tcp_sent); tcp_poll(pcb, &_tcp_poll, 1); - //_tcp_connect(pcb, &addr, port,(tcp_connected_fn)&_s_connected); - if (ESP_OK != _tcp_connect(pcb, _closed_slot, &addr, port,(tcp_connected_fn)&_tcp_connected)) { - return false; - } - return true; + TCP_MUTEX_UNLOCK(); + + esp_err_t err =_tcp_connect(pcb, _closed_slot, &addr, port,(tcp_connected_fn)&_tcp_connected); + return err == ESP_OK; } +bool AsyncClient::connect(const IPAddress& ip, uint16_t port){ + ip_addr_t addr; +#if ESP_IDF_VERSION_MAJOR < 5 + addr.u_addr.ip4.addr = ip; + addr.type = IPADDR_TYPE_V4; +#else + ip.to_ip_addr_t(&addr); +#endif + + return _connect(addr, port); +} + +#if LWIP_IPV6 && ESP_IDF_VERSION_MAJOR < 5 +bool AsyncClient::connect(const IPv6Address& ip, uint16_t port){ + auto ipaddr = static_cast(ip); + ip_addr_t addr = IPADDR6_INIT(ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]); + + return _connect(addr, port); +} +#endif + bool AsyncClient::connect(const char* host, uint16_t port){ ip_addr_t addr; @@ -739,12 +789,23 @@ 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 ESP_IDF_VERSION_MAJOR < 5 +#if LWIP_IPV6 + if(addr.type == IPADDR_TYPE_V6) { + return connect(IPv6Address(addr.u_addr.ip6.addr), port); + } return connect(IPAddress(addr.u_addr.ip4.addr), port); +#else + return connect(IPAddress(addr.addr), port); +#endif +#else + return _connect(addr, port); +#endif } else if(err == ERR_INPROGRESS) { _connect_port = port; return true; } - log_e("error: %d", err); + log_d("error: %d", err); return false; } @@ -764,7 +825,7 @@ int8_t AsyncClient::abort(){ } size_t AsyncClient::space(){ - if((_pcb != NULL) && (_pcb->state == 4)){ + if((_pcb != NULL) && (_pcb->state == ESTABLISHED)){ return tcp_sndbuf(_pcb); } return 0; @@ -788,13 +849,12 @@ size_t AsyncClient::add(const char* data, size_t size, uint8_t apiflags) { } bool AsyncClient::send(){ - int8_t err = ERR_OK; - err = _tcp_output(_pcb, _closed_slot); - if(err == ERR_OK){ - _pcb_busy = true; - _pcb_sent_at = millis(); + auto backup = _tx_last_packet; + _tx_last_packet = millis(); + if (_tcp_output(_pcb, _closed_slot) == ERR_OK) { return true; } + _tx_last_packet = backup; return false; } @@ -824,17 +884,19 @@ int8_t AsyncClient::_close(){ //ets_printf("X: 0x%08x\n", (uint32_t)this); int8_t err = ERR_OK; if(_pcb) { - //log_i(""); + TCP_MUTEX_LOCK(); tcp_arg(_pcb, NULL); tcp_sent(_pcb, NULL); tcp_recv(_pcb, NULL); tcp_err(_pcb, NULL); tcp_poll(_pcb, NULL, 0); + TCP_MUTEX_UNLOCK(); _tcp_clear_events(this); err = _tcp_close(_pcb, _closed_slot); if(err != ERR_OK) { err = abort(); } + _free_closed_slot(); _pcb = NULL; if(_discard_cb) { _discard_cb(_discard_cb_arg, this); @@ -843,41 +905,43 @@ int8_t AsyncClient::_close(){ return err; } -void AsyncClient::_allocate_closed_slot(){ +bool AsyncClient::_allocate_closed_slot(){ + if (_closed_slot != INVALID_CLOSED_SLOT) { + return true; + } xSemaphoreTake(_slots_lock, portMAX_DELAY); uint32_t closed_slot_min_index = 0; for (int i = 0; i < _number_of_closed_slots; ++ i) { - if ((_closed_slot == -1 || _closed_slots[i] <= closed_slot_min_index) && _closed_slots[i] != 0) { + if ((_closed_slot == INVALID_CLOSED_SLOT || _closed_slots[i] <= closed_slot_min_index) && _closed_slots[i] != 0) { closed_slot_min_index = _closed_slots[i]; _closed_slot = i; } } - if (_closed_slot != -1) { + if (_closed_slot != INVALID_CLOSED_SLOT) { _closed_slots[_closed_slot] = 0; } xSemaphoreGive(_slots_lock); + return (_closed_slot != INVALID_CLOSED_SLOT); } void AsyncClient::_free_closed_slot(){ - if (_closed_slot != -1) { + xSemaphoreTake(_slots_lock, portMAX_DELAY); + if (_closed_slot != INVALID_CLOSED_SLOT) { _closed_slots[_closed_slot] = _closed_index; - _closed_slot = -1; + _closed_slot = INVALID_CLOSED_SLOT; ++ _closed_index; } + xSemaphoreGive(_slots_lock); } /* * Private Callbacks * */ -int8_t AsyncClient::_connected(void* pcb, int8_t err){ +int8_t AsyncClient::_connected(tcp_pcb* pcb, int8_t err){ _pcb = reinterpret_cast(pcb); if(_pcb){ _rx_last_packet = millis(); - _pcb_busy = false; -// tcp_recv(_pcb, &_tcp_recv); -// tcp_sent(_pcb, &_tcp_sent); -// tcp_poll(_pcb, &_tcp_poll, 1); } if(_connect_cb) { _connect_cb(_connect_cb_arg, this); @@ -887,6 +951,7 @@ int8_t AsyncClient::_connected(void* pcb, int8_t err){ void AsyncClient::_error(int8_t err) { if(_pcb){ + TCP_MUTEX_LOCK(); tcp_arg(_pcb, NULL); if(_pcb->state == LISTEN) { tcp_sent(_pcb, NULL); @@ -894,6 +959,8 @@ void AsyncClient::_error(int8_t err) { tcp_err(_pcb, NULL); tcp_poll(_pcb, NULL, 0); } + TCP_MUTEX_UNLOCK(); + _free_closed_slot(); _pcb = NULL; } if(_error_cb) { @@ -907,7 +974,7 @@ void AsyncClient::_error(int8_t err) { //In LwIP Thread int8_t AsyncClient::_lwip_fin(tcp_pcb* pcb, int8_t err) { if(!_pcb || pcb != _pcb){ - log_e("0x%08x != 0x%08x", (uint32_t)pcb, (uint32_t)_pcb); + log_d("0x%08x != 0x%08x", (uint32_t)pcb, (uint32_t)_pcb); return ERR_OK; } tcp_arg(_pcb, NULL); @@ -935,11 +1002,9 @@ int8_t AsyncClient::_fin(tcp_pcb* pcb, int8_t err) { } int8_t AsyncClient::_sent(tcp_pcb* pcb, uint16_t len) { - _rx_last_packet = millis(); - //log_i("%u", len); - _pcb_busy = false; + _rx_last_ack = _rx_last_packet = millis(); if(_sent_cb) { - _sent_cb(_sent_cb_arg, this, len, (millis() - _pcb_sent_at)); + _sent_cb(_sent_cb_arg, this, len, (_rx_last_packet - _tx_last_packet)); } return ERR_OK; } @@ -963,35 +1028,38 @@ int8_t AsyncClient::_recv(tcp_pcb* pcb, pbuf* pb, int8_t err) { } else if(_pcb) { _tcp_recved(_pcb, _closed_slot, b->len); } - pbuf_free(b); } + pbuf_free(b); } - return ERR_OK; + return ERR_OK; } int8_t AsyncClient::_poll(tcp_pcb* pcb){ if(!_pcb){ - log_w("pcb is NULL"); + // log_d("pcb is NULL"); return ERR_OK; } if(pcb != _pcb){ - log_e("0x%08x != 0x%08x", (uint32_t)pcb, (uint32_t)_pcb); + log_d("0x%08x != 0x%08x", (uint32_t)pcb, (uint32_t)_pcb); return ERR_OK; } uint32_t now = millis(); // ACK Timeout - if(_pcb_busy && _ack_timeout && (now - _pcb_sent_at) >= _ack_timeout){ - _pcb_busy = false; - 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){ + 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_d("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)){ - log_w("rx timeout %d", pcb->state); + if(_rx_timeout && (now - _rx_last_packet) >= (_rx_timeout * 1000)) { + log_d("rx timeout %d", pcb->state); _close(); return ERR_OK; } @@ -1003,8 +1071,19 @@ int8_t AsyncClient::_poll(tcp_pcb* pcb){ } void AsyncClient::_dns_found(struct ip_addr *ipaddr){ - if(ipaddr && ipaddr->u_addr.ip4.addr){ - connect(IPAddress(ipaddr->u_addr.ip4.addr), _connect_port); +#if ESP_IDF_VERSION_MAJOR < 5 + if(ipaddr && IP_IS_V4(ipaddr)){ + connect(IPAddress(ip_addr_get_ip4_u32(ipaddr)), _connect_port); +#if LWIP_IPV6 + } else if(ipaddr && ipaddr->u_addr.ip6.addr){ + connect(IPv6Address(ipaddr->u_addr.ip6.addr), _connect_port); +#endif +#else + if(ipaddr) { + IPAddress ip; + ip.from_ip_addr_t(ipaddr); + connect(ip, _connect_port); +#endif } else { if(_error_cb) { _error_cb(_error_cb_arg, this, -55); @@ -1027,7 +1106,7 @@ bool AsyncClient::free(){ if(!_pcb) { return true; } - if(_pcb->state == 0 || _pcb->state > 4) { + if(_pcb->state == CLOSED || _pcb->state > ESTABLISHED) { return true; } return false; @@ -1049,11 +1128,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(){ @@ -1082,6 +1161,18 @@ bool AsyncClient::getNoDelay(){ return tcp_nagle_disabled(_pcb); } +void AsyncClient::setKeepAlive(uint32_t ms, uint8_t cnt){ + if(ms!=0) { + _pcb->so_options |= SOF_KEEPALIVE; //Turn on TCP Keepalive for the given pcb + // Set the time between keepalive messages in milli-seconds + _pcb->keep_idle = ms; + _pcb->keep_intvl = ms; + _pcb->keep_cnt = cnt; //The number of unanswered probes required to force closure of the socket + } else { + _pcb->so_options &= ~SOF_KEEPALIVE; //Turn off TCP Keepalive for the given pcb + } +} + uint16_t AsyncClient::getMss(){ if(!_pcb) { return 0; @@ -1093,9 +1184,60 @@ 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 } +#if LWIP_IPV6 +ip6_addr_t AsyncClient::getRemoteAddress6() { + if(!_pcb) { + ip6_addr_t nulladdr; + ip6_addr_set_zero(&nulladdr); + return nulladdr; + } + return _pcb->remote_ip.u_addr.ip6; +} + +ip6_addr_t AsyncClient::getLocalAddress6() { + if(!_pcb) { + ip6_addr_t nulladdr; + ip6_addr_set_zero(&nulladdr); + return nulladdr; + } + return _pcb->local_ip.u_addr.ip6; +} +#if ESP_IDF_VERSION_MAJOR < 5 +IPv6Address AsyncClient::remoteIP6() { + return IPv6Address(getRemoteAddress6().addr); +} + +IPv6Address AsyncClient::localIP6() { + return IPv6Address(getLocalAddress6().addr); +} +#else +IPAddress AsyncClient::remoteIP6() { + if (!_pcb) { + return IPAddress(IPType::IPv6); + } + IPAddress ip; + ip.from_ip_addr_t(&(_pcb->remote_ip)); + return ip; +} + +IPAddress AsyncClient::localIP6() { + if (!_pcb) { + return IPAddress(IPType::IPv6); + } + IPAddress ip; + ip.from_ip_addr_t(&(_pcb->local_ip)); + return ip; +} +#endif +#endif + uint16_t AsyncClient::getRemotePort() { if(!_pcb) { return 0; @@ -1107,7 +1249,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() { @@ -1118,7 +1264,16 @@ uint16_t AsyncClient::getLocalPort() { } IPAddress AsyncClient::remoteIP() { +#if ESP_IDF_VERSION_MAJOR < 5 return IPAddress(getRemoteAddress()); +#else + if (!_pcb) { + return IPAddress(); + } + IPAddress ip; + ip.from_ip_addr_t(&(_pcb->remote_ip)); + return ip; +#endif } uint16_t AsyncClient::remotePort() { @@ -1126,9 +1281,19 @@ uint16_t AsyncClient::remotePort() { } IPAddress AsyncClient::localIP() { +#if ESP_IDF_VERSION_MAJOR < 5 return IPAddress(getLocalAddress()); +#else + if (!_pcb) { + return IPAddress(); + } + IPAddress ip; + ip.from_ip_addr_t(&(_pcb->local_ip)); + return ip; +#endif } + uint16_t AsyncClient::localPort() { return getLocalPort(); } @@ -1144,35 +1309,35 @@ bool AsyncClient::connected(){ if (!_pcb) { return false; } - return _pcb->state == 4; + return _pcb->state == ESTABLISHED; } bool AsyncClient::connecting(){ if (!_pcb) { return false; } - return _pcb->state > 0 && _pcb->state < 4; + return _pcb->state > CLOSED && _pcb->state < ESTABLISHED; } bool AsyncClient::disconnecting(){ if (!_pcb) { return false; } - return _pcb->state > 4 && _pcb->state < 10; + return _pcb->state > ESTABLISHED && _pcb->state < TIME_WAIT; } bool AsyncClient::disconnected(){ if (!_pcb) { return true; } - return _pcb->state == 0 || _pcb->state == 10; + return _pcb->state == CLOSED || _pcb->state == TIME_WAIT; } bool AsyncClient::freeable(){ if (!_pcb) { return true; } - return _pcb->state == 0 || _pcb->state > 4; + return _pcb->state == CLOSED || _pcb->state > ESTABLISHED; } bool AsyncClient::canSend(){ @@ -1251,7 +1416,7 @@ void AsyncClient::_s_error(void * arg, int8_t err) { reinterpret_cast(arg)->_error(err); } -int8_t AsyncClient::_s_connected(void * arg, void * pcb, int8_t err){ +int8_t AsyncClient::_s_connected(void * arg, struct tcp_pcb * pcb, int8_t err){ return reinterpret_cast(arg)->_connected(pcb, err); } @@ -1261,6 +1426,13 @@ int8_t AsyncClient::_s_connected(void * arg, void * pcb, int8_t err){ AsyncServer::AsyncServer(IPAddress addr, uint16_t port) : _port(port) +#if ESP_IDF_VERSION_MAJOR < 5 +, _bind4(true) +, _bind6(false) +#else +, _bind4(addr.type() != IPType::IPv6) +, _bind6(addr.type() == IPType::IPv6) +#endif , _addr(addr) , _noDelay(false) , _pcb(0) @@ -1268,9 +1440,27 @@ AsyncServer::AsyncServer(IPAddress addr, uint16_t port) , _connect_cb_arg(0) {} +#if ESP_IDF_VERSION_MAJOR < 5 +AsyncServer::AsyncServer(IPv6Address addr, uint16_t port) +: _port(port) +, _bind4(false) +, _bind6(true) +, _addr6(addr) +, _noDelay(false) +, _pcb(0) +, _connect_cb(0) +, _connect_cb_arg(0) +{} +#endif + AsyncServer::AsyncServer(uint16_t port) : _port(port) +, _bind4(true) +, _bind6(false) , _addr((uint32_t) IPADDR_ANY) +#if ESP_IDF_VERSION_MAJOR < 5 +, _addr6() +#endif , _noDelay(false) , _pcb(0) , _connect_cb(0) @@ -1296,15 +1486,26 @@ void AsyncServer::begin(){ return; } int8_t err; - _pcb = tcp_new_ip_type(IPADDR_TYPE_V4); + TCP_MUTEX_LOCK(); + _pcb = tcp_new_ip_type(_bind4 && _bind6 ? IPADDR_TYPE_ANY : (_bind6 ? IPADDR_TYPE_V6 : IPADDR_TYPE_V4)); + TCP_MUTEX_UNLOCK(); if (!_pcb){ log_e("_pcb == NULL"); return; } ip_addr_t local_addr; - local_addr.type = IPADDR_TYPE_V4; - local_addr.u_addr.ip4.addr = (uint32_t) _addr; +#if ESP_IDF_VERSION_MAJOR < 5 + if (_bind6) { // _bind6 && _bind4 both at the same time is not supported on Arduino 2 in this lib API + local_addr.type = IPADDR_TYPE_V6; + memcpy(local_addr.u_addr.ip6.addr, static_cast(_addr6), sizeof(uint32_t) * 4); + } else { + local_addr.type = IPADDR_TYPE_V4; + local_addr.u_addr.ip4.addr = _addr; + } +#else + _addr.to_ip_addr_t(&local_addr); +#endif err = _tcp_bind(_pcb, &local_addr, _port); if (err != ERR_OK) { @@ -1319,16 +1520,22 @@ void AsyncServer::begin(){ log_e("listen_pcb == NULL"); return; } + TCP_MUTEX_LOCK(); tcp_arg(_pcb, (void*) this); tcp_accept(_pcb, &_s_accept); + TCP_MUTEX_UNLOCK(); } void AsyncServer::end(){ if(_pcb){ + TCP_MUTEX_LOCK(); tcp_arg(_pcb, NULL); tcp_accept(_pcb, NULL); if(tcp_close(_pcb) != ERR_OK){ + TCP_MUTEX_UNLOCK(); _tcp_abort(_pcb, -1); + } else { + TCP_MUTEX_UNLOCK(); } _pcb = NULL; } @@ -1347,7 +1554,7 @@ int8_t AsyncServer::_accept(tcp_pcb* pcb, int8_t err){ if(tcp_close(pcb) != ERR_OK){ tcp_abort(pcb); } - log_e("FAIL"); + log_d("FAIL"); return ERR_OK; } @@ -1379,6 +1586,4 @@ int8_t AsyncServer::_s_accept(void * arg, tcp_pcb * pcb, int8_t err){ int8_t AsyncServer::_s_accepted(void *arg, AsyncClient* client){ return reinterpret_cast(arg)->_accepted(client); -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/ESPAsyncWebServer/src/esp32/AsyncTCP.h b/src/ESPAsyncWebServer/src/esp32/AsyncTCP.h index 4537d8d3..4588b5ad 100644 --- a/src/ESPAsyncWebServer/src/esp32/AsyncTCP.h +++ b/src/ESPAsyncWebServer/src/esp32/AsyncTCP.h @@ -19,18 +19,37 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifdef ESP32 - #ifndef ASYNCTCP_H_ #define ASYNCTCP_H_ +#define ASYNCTCP_VERSION "3.2.14" +#define ASYNCTCP_VERSION_MAJOR 3 +#define ASYNCTCP_VERSION_MINOR 2 +#define ASYNCTCP_VERSION_REVISION 14 +#define ASYNCTCP_FORK_mathieucarbou + #include "IPAddress.h" -#include "sdkconfig.h" +#if ESP_IDF_VERSION_MAJOR < 5 +#include "IPv6Address.h" +#endif #include +#include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" + +#ifndef LIBRETINY +#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 @@ -38,9 +57,24 @@ 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 + +#ifndef CONFIG_ASYNC_TCP_PRIORITY +#define CONFIG_ASYNC_TCP_PRIORITY 10 +#endif + +#ifndef CONFIG_ASYNC_TCP_QUEUE_SIZE +#define CONFIG_ASYNC_TCP_QUEUE_SIZE 64 +#endif + +#ifndef CONFIG_ASYNC_TCP_MAX_ACK_TIME +#define CONFIG_ASYNC_TCP_MAX_ACK_TIME 5000 +#endif + class AsyncClient; -#define ASYNC_MAX_ACK_TIME 5000 #define ASYNC_WRITE_FLAG_COPY 0x01 //will allocate new buffer to hold the data while sending (else will hold reference to the data given) #define ASYNC_WRITE_FLAG_MORE 0x02 //will not send PSH flag, meaning that there should be more data to be sent before the application should react. @@ -67,8 +101,11 @@ class AsyncClient { bool operator!=(const AsyncClient &other) { return !(*this == other); } - bool connect(IPAddress ip, uint16_t port); - bool connect(const char* host, uint16_t port); + bool connect(const IPAddress& ip, uint16_t port); +#if ESP_IDF_VERSION_MAJOR < 5 + bool connect(const IPv6Address& ip, uint16_t port); +#endif + bool connect(const char *host, uint16_t port); void close(bool now = false); void stop(); int8_t abort(); @@ -101,16 +138,29 @@ class AsyncClient { void setNoDelay(bool nodelay); bool getNoDelay(); + void setKeepAlive(uint32_t ms, uint8_t cnt); + uint32_t getRemoteAddress(); uint16_t getRemotePort(); uint32_t getLocalAddress(); uint16_t getLocalPort(); +#if LWIP_IPV6 + ip6_addr_t getRemoteAddress6(); + ip6_addr_t getLocalAddress6(); +#if ESP_IDF_VERSION_MAJOR < 5 + IPv6Address remoteIP6(); + IPv6Address localIP6(); +#else + IPAddress remoteIP6(); + IPAddress localIP6(); +#endif +#endif //compatibility IPAddress remoteIP(); - uint16_t remotePort(); + uint16_t remotePort(); IPAddress localIP(); - uint16_t localPort(); + uint16_t localPort(); void onConnect(AcConnectHandler cb, void* arg = 0); //on successful connect void onDisconnect(AcConnectHandler cb, void* arg = 0); //disconnected @@ -135,13 +185,15 @@ class AsyncClient { static int8_t _s_lwip_fin(void *arg, struct tcp_pcb *tpcb, int8_t err); static void _s_error(void *arg, int8_t err); static int8_t _s_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len); - static int8_t _s_connected(void* arg, void* tpcb, int8_t err); + static int8_t _s_connected(void* arg, struct tcp_pcb *tpcb, int8_t err); static void _s_dns_found(const char *name, struct ip_addr *ipaddr, void *arg); int8_t _recv(tcp_pcb* pcb, pbuf* pb, int8_t err); tcp_pcb * pcb(){ return _pcb; } protected: + bool _connect(ip_addr_t addr, uint16_t port); + tcp_pcb* _pcb; int8_t _closed_slot; @@ -162,19 +214,19 @@ class AsyncClient { AcConnectHandler _poll_cb; void* _poll_cb_arg; - bool _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; int8_t _close(); void _free_closed_slot(); - void _allocate_closed_slot(); - int8_t _connected(void* pcb, int8_t err); + bool _allocate_closed_slot(); + int8_t _connected(tcp_pcb* pcb, int8_t err); void _error(int8_t err); int8_t _poll(tcp_pcb* pcb); int8_t _sent(tcp_pcb* pcb, uint16_t len); @@ -190,6 +242,9 @@ class AsyncClient { class AsyncServer { public: AsyncServer(IPAddress addr, uint16_t port); +#if ESP_IDF_VERSION_MAJOR < 5 + AsyncServer(IPv6Address addr, uint16_t port); +#endif AsyncServer(uint16_t port); ~AsyncServer(); void onClient(AcConnectHandler cb, void* arg); @@ -205,7 +260,12 @@ class AsyncServer { protected: uint16_t _port; + bool _bind4 = false; + bool _bind6 = false; IPAddress _addr; +#if ESP_IDF_VERSION_MAJOR < 5 + IPv6Address _addr6; +#endif bool _noDelay; tcp_pcb* _pcb; AcConnectHandler _connect_cb; @@ -216,7 +276,4 @@ class AsyncServer { }; -#endif /* ASYNCTCP_H_ */ - - -#endif \ No newline at end of file +#endif /* ASYNCTCP_H_ */ \ No newline at end of file diff --git a/src/ESPAsyncWebServer/src/test.zip b/src/ESPAsyncWebServer/src/test.zip new file mode 100644 index 0000000000000000000000000000000000000000..708cccad48b8978a759998203dfe90e776bad9ea GIT binary patch literal 64094 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 literal 0 HcmV?d00001 diff --git a/src/SetupConfig.hpp b/src/SetupConfig.hpp index 451f452e..cb63d68b 100644 --- a/src/SetupConfig.hpp +++ b/src/SetupConfig.hpp @@ -141,14 +141,14 @@ class SetupConfigurator } numOptions++ ; - // If key is present in json, we don't need to create it. - if (doc.containsKey(label)) - return; - - - #if ARDUINOJSON_VERSION_MAJOR > 6 + #if ARDUINOJSON_VERSION_MAJOR > 6 + if (doc["value"]) + return; JsonObject obj = doc[label].to(); #else + // If key is present in json, we don't need to create it + if (doc.containsKey(label)) + return; JsonObject obj = doc.createNestedObject(label); #endif @@ -231,7 +231,7 @@ class SetupConfigurator key += numOptions ; // If key is present and value is the same, we don't need to create/update it. - if (doc.containsKey(key.c_str()) && doc[key] == val) + if (doc[key] == val) return; // if min, max, step != from default, treat this as object in order to set other properties