diff --git a/libraries/AsyncTCP/library.json b/libraries/AsyncTCP/library.json index 0644061e4..b4a0e22dc 100644 --- a/libraries/AsyncTCP/library.json +++ b/libraries/AsyncTCP/library.json @@ -12,8 +12,11 @@ "type": "git", "url": "https://github.com/me-no-dev/AsyncTCP.git" }, - "version": "1.0.0", + "version": "1.0.3", "license": "LGPL-3.0", "frameworks": "arduino", - "platforms": ["espressif32", "espressif32_stage"] + "platforms": "espressif32", + "build": { + "libCompatMode": 2 + } } diff --git a/libraries/AsyncTCP/library.properties b/libraries/AsyncTCP/library.properties index d9e8e47d9..14c39f0cb 100644 --- a/libraries/AsyncTCP/library.properties +++ b/libraries/AsyncTCP/library.properties @@ -1,5 +1,5 @@ name=AsyncTCP -version=1.0.0 +version=1.0.3 author=Me-No-Dev maintainer=Me-No-Dev sentence=Async TCP Library for ESP32 diff --git a/libraries/AsyncTCP/src/AsyncTCP.cpp b/libraries/AsyncTCP/src/AsyncTCP.cpp index ba1929b81..f06ccccb7 100644 --- a/libraries/AsyncTCP/src/AsyncTCP.cpp +++ b/libraries/AsyncTCP/src/AsyncTCP.cpp @@ -29,12 +29,18 @@ extern "C"{ #include "lwip/dns.h" } +#if CONFIG_FREERTOS_UNICORE +#define ASYNCTCP_RUNNING_CORE 0 +#else +#define ASYNCTCP_RUNNING_CORE 1 +#endif + /* * TCP/IP Event Task * */ typedef enum { - LWIP_TCP_SENT, LWIP_TCP_RECV, LWIP_TCP_ERROR, LWIP_TCP_POLL + LWIP_TCP_SENT, LWIP_TCP_RECV, LWIP_TCP_ERROR, LWIP_TCP_POLL, LWIP_TCP_CLEAR } lwip_event_t; typedef struct { @@ -74,13 +80,77 @@ typedef struct { static xQueueHandle _async_queue; static TaskHandle_t _async_service_task_handle = NULL; -static void _handle_async_event(lwip_event_packet_t * e){ +static inline bool _init_async_event_queue(){ + if(!_async_queue){ + _async_queue = xQueueCreate(32, sizeof(lwip_event_packet_t *)); + if(!_async_queue){ + return false; + } + } + return true; +} + +static inline bool _send_async_event(lwip_event_packet_t ** e){ + return _async_queue && xQueueSend(_async_queue, e, portMAX_DELAY) == pdPASS; +} + +static inline bool _prepend_async_event(lwip_event_packet_t ** e){ + return _async_queue && xQueueSendToFront(_async_queue, e, portMAX_DELAY) == pdPASS; +} + +static inline bool _get_async_event(lwip_event_packet_t ** e){ + return _async_queue && xQueueReceive(_async_queue, e, portMAX_DELAY) == pdPASS; +} + +static bool _remove_events_with_arg(void * arg){ + lwip_event_packet_t * first_packet = NULL; + lwip_event_packet_t * packet = NULL; + + if(!_async_queue){ + return false; + } + //figure out which is the first packet so we can keep the order + while(!first_packet){ + if(xQueueReceive(_async_queue, &first_packet, 0) != pdPASS){ + return false; + } + //discard packet if matching + if((int)first_packet->arg == (int)arg){ + //ets_printf("X: 0x%08x\n", (uint32_t)first_packet->arg); + free(first_packet); + first_packet = NULL; + //return first packet to the back of the queue + } else if(xQueueSend(_async_queue, &first_packet, portMAX_DELAY) != pdPASS){ + return false; + } + } - if(e->event == LWIP_TCP_RECV){ + while(xQueuePeek(_async_queue, &packet, 0) == pdPASS && packet != first_packet){ + if(xQueueReceive(_async_queue, &packet, 0) != pdPASS){ + return false; + } + if((int)packet->arg == (int)arg){ + //ets_printf("X: 0x%08x\n", (uint32_t)packet->arg); + free(packet); + packet = NULL; + } else if(xQueueSend(_async_queue, &packet, portMAX_DELAY) != pdPASS){ + return false; + } + } + return true; +} + +static void _handle_async_event(lwip_event_packet_t * e){ + if(e->event == LWIP_TCP_CLEAR){ + _remove_events_with_arg(e->arg); + } else if(e->event == LWIP_TCP_RECV){ + //ets_printf("%c: 0x%08x 0x%08x\n", e->recv.pb?'R':'D', e->arg, e->recv.pcb); AsyncClient::_s_recv(e->arg, e->recv.pcb, e->recv.pb, e->recv.err); } else if(e->event == LWIP_TCP_SENT){ + //ets_printf("S: 0x%08x 0x%08x\n", e->arg, e->sent.pcb); AsyncClient::_s_sent(e->arg, e->sent.pcb, e->sent.len); } else if(e->event == LWIP_TCP_POLL){ + //ets_printf("P: 0x%08x 0x%08x\n", e->arg, e->poll.pcb); AsyncClient::_s_poll(e->arg, e->poll.pcb); } else if(e->event == LWIP_TCP_ERROR){ AsyncClient::_s_error(e->arg, e->error.err); @@ -91,11 +161,8 @@ static void _handle_async_event(lwip_event_packet_t * e){ static void _async_service_task(void *pvParameters){ lwip_event_packet_t * packet = NULL; for (;;) { - if(xQueueReceive(_async_queue, &packet, 0) == pdTRUE){ - //dispatch packet + if(_get_async_event(&packet)){ _handle_async_event(packet); - } else { - vTaskDelay(1); } } vTaskDelete(NULL); @@ -110,14 +177,11 @@ static void _stop_async_task(){ } */ static bool _start_async_task(){ - if(!_async_queue){ - _async_queue = xQueueCreate(32, sizeof(lwip_event_packet_t *)); - if(!_async_queue){ - return false; - } + if(!_init_async_event_queue()){ + return false; } if(!_async_service_task_handle){ - xTaskCreatePinnedToCore(_async_service_task, "async_tcp", 8192, NULL, 3, &_async_service_task_handle, 1); + xTaskCreatePinnedToCore(_async_service_task, "async_tcp", 8192, NULL, 3, &_async_service_task_handle, ASYNCTCP_RUNNING_CORE); if(!_async_service_task_handle){ return false; } @@ -129,60 +193,58 @@ static bool _start_async_task(){ * LwIP Callbacks * */ -static int8_t _tcp_poll(void * arg, struct tcp_pcb * pcb) { - if(!_async_queue){ - return ERR_OK; +static int8_t _tcp_clear_events(void * arg) { + lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); + e->event = LWIP_TCP_CLEAR; + e->arg = arg; + if (!_prepend_async_event(&e)) { + free((void*)(e)); } + return ERR_OK; +} + +static int8_t _tcp_poll(void * arg, struct tcp_pcb * pcb) { lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); e->event = LWIP_TCP_POLL; e->arg = arg; e->poll.pcb = pcb; - if (xQueueSend(_async_queue, &e, portMAX_DELAY) != pdPASS) { + 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) { - if(!_async_queue){ - return ERR_OK; - } lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); e->event = LWIP_TCP_RECV; e->arg = arg; e->recv.pcb = pcb; e->recv.pb = pb; e->recv.err = err; - if (xQueueSend(_async_queue, &e, portMAX_DELAY) != pdPASS) { + if (!_send_async_event(&e)) { free((void*)(e)); } return ERR_OK; } static int8_t _tcp_sent(void * arg, struct tcp_pcb * pcb, uint16_t len) { - if(!_async_queue){ - return ERR_OK; - } lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); e->event = LWIP_TCP_SENT; e->arg = arg; e->sent.pcb = pcb; e->sent.len = len; - if (xQueueSend(_async_queue, &e, portMAX_DELAY) != pdPASS) { + if (!_send_async_event(&e)) { free((void*)(e)); } return ERR_OK; } static void _tcp_error(void * arg, int8_t err) { - if(!_async_queue){ - return; - } lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); e->event = LWIP_TCP_ERROR; e->arg = arg; e->error.err = err; - if (xQueueSend(_async_queue, &e, portMAX_DELAY) != pdPASS) { + if (!_send_async_event(&e)) { free((void*)(e)); } } @@ -194,7 +256,7 @@ static void _tcp_error(void * arg, int8_t err) { #include "lwip/priv/tcpip_priv.h" typedef struct { - struct tcpip_api_call call; + struct tcpip_api_call_data call; tcp_pcb * pcb; int8_t err; union { @@ -217,20 +279,24 @@ typedef struct { }; } tcp_api_call_t; -static err_t _tcp_output_api(struct tcpip_api_call *api_call_msg){ +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 = tcp_output(msg->pcb); + if(msg->pcb){ + msg->err = tcp_output(msg->pcb); + } else { + msg->err = 0; + } return msg->err; } static esp_err_t _tcp_output(tcp_pcb * pcb) { tcp_api_call_t msg; msg.pcb = pcb; - tcpip_api_call(_tcp_output_api, (struct tcpip_api_call*)&msg); + tcpip_api_call(_tcp_output_api, (struct tcpip_api_call_data*)&msg); return msg.err; } -static err_t _tcp_write_api(struct tcpip_api_call *api_call_msg){ +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 = tcp_write(msg->pcb, msg->write.data, msg->write.size, msg->write.apiflags); return msg->err; @@ -242,11 +308,11 @@ static esp_err_t _tcp_write(tcp_pcb * pcb, const char* data, size_t size, uint8_ msg.write.data = data; msg.write.size = size; msg.write.apiflags = apiflags; - tcpip_api_call(_tcp_write_api, (struct tcpip_api_call*)&msg); + tcpip_api_call(_tcp_write_api, (struct tcpip_api_call_data*)&msg); return msg.err; } -static err_t _tcp_recved_api(struct tcpip_api_call *api_call_msg){ +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 = 0; tcp_recved(msg->pcb, msg->received); @@ -257,11 +323,11 @@ static esp_err_t _tcp_recved(tcp_pcb * pcb, size_t len) { tcp_api_call_t msg; msg.pcb = pcb; msg.received = len; - tcpip_api_call(_tcp_recved_api, (struct tcpip_api_call*)&msg); + tcpip_api_call(_tcp_recved_api, (struct tcpip_api_call_data*)&msg); return msg.err; } -static err_t _tcp_connect_api(struct tcpip_api_call *api_call_msg){ +static err_t _tcp_connect_api(struct tcpip_api_call_data *api_call_msg){ tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; msg->err = tcp_connect(msg->pcb, msg->connect.addr, msg->connect.port, msg->connect.cb); return msg->err; @@ -273,11 +339,11 @@ static esp_err_t _tcp_connect(tcp_pcb * pcb, ip_addr_t * addr, uint16_t port, tc msg.connect.addr = addr; msg.connect.port = port; msg.connect.cb = cb; - tcpip_api_call(_tcp_connect_api, (struct tcpip_api_call*)&msg); + tcpip_api_call(_tcp_connect_api, (struct tcpip_api_call_data*)&msg); return msg.err; } -static err_t _tcp_close_api(struct tcpip_api_call *api_call_msg){ +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 = tcp_close(msg->pcb); return msg->err; @@ -286,11 +352,12 @@ static err_t _tcp_close_api(struct tcpip_api_call *api_call_msg){ static esp_err_t _tcp_close(tcp_pcb * pcb) { tcp_api_call_t msg; msg.pcb = pcb; - tcpip_api_call(_tcp_close_api, (struct tcpip_api_call*)&msg); + //ets_printf("close 0x%08x\n", (uint32_t)pcb); + tcpip_api_call(_tcp_close_api, (struct tcpip_api_call_data*)&msg); return msg.err; } -static err_t _tcp_abort_api(struct tcpip_api_call *api_call_msg){ +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 = 0; tcp_abort(msg->pcb); @@ -300,11 +367,12 @@ static err_t _tcp_abort_api(struct tcpip_api_call *api_call_msg){ static esp_err_t _tcp_abort(tcp_pcb * pcb) { tcp_api_call_t msg; msg.pcb = pcb; - tcpip_api_call(_tcp_abort_api, (struct tcpip_api_call*)&msg); + //ets_printf("abort 0x%08x\n", (uint32_t)pcb); + tcpip_api_call(_tcp_abort_api, (struct tcpip_api_call_data*)&msg); return msg.err; } -static err_t _tcp_bind_api(struct tcpip_api_call *api_call_msg){ +static err_t _tcp_bind_api(struct tcpip_api_call_data *api_call_msg){ tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; msg->err = tcp_bind(msg->pcb, msg->bind.addr, msg->bind.port); return msg->err; @@ -315,11 +383,11 @@ static esp_err_t _tcp_bind(tcp_pcb * pcb, ip_addr_t * addr, uint16_t port) { msg.pcb = pcb; msg.bind.addr = addr; msg.bind.port = port; - tcpip_api_call(_tcp_bind_api, (struct tcpip_api_call*)&msg); + tcpip_api_call(_tcp_bind_api, (struct tcpip_api_call_data*)&msg); return msg.err; } -static err_t _tcp_listen_api(struct tcpip_api_call *api_call_msg){ +static err_t _tcp_listen_api(struct tcpip_api_call_data *api_call_msg){ tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; msg->err = 0; msg->pcb = tcp_listen_with_backlog(msg->pcb, msg->backlog); @@ -330,7 +398,7 @@ static tcp_pcb * _tcp_listen_with_backlog(tcp_pcb * pcb, uint8_t backlog) { tcp_api_call_t msg; msg.pcb = pcb; msg.backlog = backlog?backlog:0xFF; - tcpip_api_call(_tcp_listen_api, (struct tcpip_api_call*)&msg); + tcpip_api_call(_tcp_listen_api, (struct tcpip_api_call_data*)&msg); return msg.pcb; } #define _tcp_listen(p) _tcp_listen_with_backlog(p, 0xFF); @@ -352,6 +420,8 @@ AsyncClient::AsyncClient(tcp_pcb* pcb) , _error_cb_arg(0) , _recv_cb(0) , _recv_cb_arg(0) +, _pb_cb(0) +, _pb_cb_arg(0) , _timeout_cb(0) , _timeout_cb_arg(0) , _pcb_busy(false) @@ -366,6 +436,8 @@ AsyncClient::AsyncClient(tcp_pcb* pcb) , next(NULL) , _in_lwip_thread(false) { + //ets_printf("+: 0x%08x\n", (uint32_t)this); + _pcb = pcb; if(_pcb){ _rx_last_packet = millis(); @@ -374,12 +446,15 @@ AsyncClient::AsyncClient(tcp_pcb* pcb) tcp_sent(_pcb, &_tcp_sent); tcp_err(_pcb, &_tcp_error); tcp_poll(_pcb, &_tcp_poll, 1); + //ets_printf("accept 0x%08x\n", (uint32_t)_pcb); } } AsyncClient::~AsyncClient(){ if(_pcb) _close(); + + //ets_printf("-: 0x%08x\n", (uint32_t)this); } bool AsyncClient::connect(IPAddress ip, uint16_t port){ @@ -445,6 +520,7 @@ int8_t AsyncClient::_connected(void* pcb, int8_t err){ } int8_t AsyncClient::_close(){ + //ets_printf("X: 0x%08x\n", (uint32_t)this); int8_t err = ERR_OK; if(_pcb) { //log_i(""); @@ -453,6 +529,7 @@ int8_t AsyncClient::_close(){ tcp_recv(_pcb, NULL); tcp_err(_pcb, NULL); tcp_poll(_pcb, NULL, 0); + _tcp_clear_events(this); if(_in_lwip_thread){ err = tcp_close(_pcb); } else { @@ -484,6 +561,7 @@ void AsyncClient::_error(int8_t err) { } int8_t AsyncClient::_sent(tcp_pcb* pcb, uint16_t len) { + _in_lwip_thread = false; _rx_last_packet = millis(); //log_i("%u", len); _pcb_busy = false; @@ -493,6 +571,14 @@ int8_t AsyncClient::_sent(tcp_pcb* pcb, uint16_t len) { } int8_t AsyncClient::_recv(tcp_pcb* pcb, pbuf* pb, int8_t err) { + if(!_pcb || pcb != _pcb){ + log_e("0x%08x != 0x%08x", (uint32_t)pcb, (uint32_t)_pcb); + if(pb){ + pbuf_free(pb); + } + return ERR_OK; + } + _in_lwip_thread = false; if(pb == NULL){ return _close(); } @@ -504,20 +590,25 @@ int8_t AsyncClient::_recv(tcp_pcb* pcb, pbuf* pb, int8_t err) { //Serial.write((const uint8_t *)pb->payload, pb->len); _ack_pcb = true; pbuf *b = pb; - if(_recv_cb) - _recv_cb(_recv_cb_arg, this, b->payload, b->len); - if(!_ack_pcb) - _rx_ack_len += b->len; - else - _tcp_recved(pcb, b->len); pb = b->next; b->next = NULL; - pbuf_free(b); + if(_pb_cb){ + _pb_cb(_pb_cb_arg, this, b); + } else { + if(_recv_cb) + _recv_cb(_recv_cb_arg, this, b->payload, b->len); + if(!_ack_pcb) + _rx_ack_len += b->len; + else + _tcp_recved(pcb, b->len); + pbuf_free(b); + } } return ERR_OK; } int8_t AsyncClient::_poll(tcp_pcb* pcb){ + _in_lwip_thread = false; // Close requested if(_close_pcb){ _close_pcb = false; @@ -546,7 +637,7 @@ int8_t AsyncClient::_poll(tcp_pcb* pcb){ return ERR_OK; } -void AsyncClient::_dns_found(ip_addr_t *ipaddr){ +void AsyncClient::_dns_found(struct ip_addr *ipaddr){ _in_lwip_thread = true; if(ipaddr){ connect(IPAddress(ipaddr->u_addr.ip4.addr), _connect_port); @@ -591,10 +682,12 @@ int8_t AsyncClient::abort(){ } void AsyncClient::close(bool now){ - if(_in_lwip_thread){ - tcp_recved(_pcb, _rx_ack_len); - } else { - _tcp_recved(_pcb, _rx_ack_len); + if(_pcb){ + if(_in_lwip_thread){ + tcp_recved(_pcb, _rx_ack_len); + } else { + _tcp_recved(_pcb, _rx_ack_len); + } } if(now) _close(); @@ -814,6 +907,14 @@ bool AsyncClient::canSend(){ return space() > 0; } +void AsyncClient::ackPacket(struct pbuf * pb){ + if(!pb){ + return; + } + _tcp_recved(_pcb, pb->len); + pbuf_free(pb); +} + // Callback Setters @@ -842,6 +943,11 @@ void AsyncClient::onData(AcDataHandler cb, void* arg){ _recv_cb_arg = arg; } +void AsyncClient::onPacket(AcPacketHandler cb, void* arg){ + _pb_cb = cb; + _pb_cb_arg = arg; +} + void AsyncClient::onTimeout(AcTimeoutHandler cb, void* arg){ _timeout_cb = cb; _timeout_cb_arg = arg; @@ -853,31 +959,58 @@ void AsyncClient::onPoll(AcConnectHandler cb, void* arg){ } -void AsyncClient::_s_dns_found(const char * name, ip_addr_t * ipaddr, void * arg){ - reinterpret_cast(arg)->_dns_found(ipaddr); +void AsyncClient::_s_dns_found(const char * name, struct ip_addr * ipaddr, void * arg){ + if(arg){ + reinterpret_cast(arg)->_dns_found(ipaddr); + } else { + log_e("Bad Arg: 0x%08x", arg); + } } int8_t AsyncClient::_s_poll(void * arg, struct tcp_pcb * pcb) { - reinterpret_cast(arg)->_poll(pcb); + if(arg && pcb){ + reinterpret_cast(arg)->_poll(pcb); + } else { + log_e("Bad Args: 0x%08x 0x%08x", arg, pcb); + } return ERR_OK; } int8_t AsyncClient::_s_recv(void * arg, struct tcp_pcb * pcb, struct pbuf *pb, int8_t err) { - reinterpret_cast(arg)->_recv(pcb, pb, err); + if(arg && pcb){ + reinterpret_cast(arg)->_recv(pcb, pb, err); + } else { + if(pb){ + pbuf_free(pb); + } + log_e("Bad Args: 0x%08x 0x%08x", arg, pcb); + } return ERR_OK; } int8_t AsyncClient::_s_sent(void * arg, struct tcp_pcb * pcb, uint16_t len) { - reinterpret_cast(arg)->_sent(pcb, len); + if(arg && pcb){ + reinterpret_cast(arg)->_sent(pcb, len); + } else { + log_e("Bad Args: 0x%08x 0x%08x", arg, pcb); + } return ERR_OK; } void AsyncClient::_s_error(void * arg, int8_t err) { - reinterpret_cast(arg)->_error(err); + if(arg){ + reinterpret_cast(arg)->_error(err); + } else { + log_e("Bad Arg: 0x%08x", arg); + } } int8_t AsyncClient::_s_connected(void * arg, void * pcb, int8_t err){ - reinterpret_cast(arg)->_connected(pcb, err); + if(arg && pcb){ + reinterpret_cast(arg)->_connected(pcb, err); + } else { + log_e("Bad Args: 0x%08x 0x%08x", arg, pcb); + } return ERR_OK; } @@ -1032,9 +1165,9 @@ void AsyncServer::end(){ tcp_arg(_pcb, NULL); tcp_accept(_pcb, NULL); if(_in_lwip_thread){ - tcp_abort(_pcb); + tcp_close(_pcb); } else { - _tcp_abort(_pcb); + _tcp_close(_pcb); } _pcb = NULL; } diff --git a/libraries/AsyncTCP/src/AsyncTCP.h b/libraries/AsyncTCP/src/AsyncTCP.h index 1c24ac924..6cd0fca24 100644 --- a/libraries/AsyncTCP/src/AsyncTCP.h +++ b/libraries/AsyncTCP/src/AsyncTCP.h @@ -25,7 +25,8 @@ #include "IPAddress.h" #include extern "C" { -#include "freertos/semphr.h" + #include "freertos/semphr.h" + #include "lwip/pbuf.h" } class AsyncClient; @@ -38,11 +39,11 @@ typedef std::function AcConnectHandler; typedef std::function AcAckHandler; typedef std::function AcErrorHandler; typedef std::function AcDataHandler; +typedef std::function AcPacketHandler; typedef std::function AcTimeoutHandler; struct tcp_pcb; -struct pbuf; -struct _ip_addr; +struct ip_addr; class AsyncClient { protected: @@ -58,6 +59,8 @@ class AsyncClient { void* _error_cb_arg; AcDataHandler _recv_cb; void* _recv_cb_arg; + AcPacketHandler _pb_cb; + void* _pb_cb_arg; AcTimeoutHandler _timeout_cb; void* _timeout_cb_arg; AcConnectHandler _poll_cb; @@ -78,7 +81,7 @@ class AsyncClient { void _error(int8_t err); int8_t _poll(tcp_pcb* pcb); int8_t _sent(tcp_pcb* pcb, uint16_t len); - void _dns_found(struct _ip_addr *ipaddr); + void _dns_found(struct ip_addr *ipaddr); public: @@ -105,13 +108,13 @@ class AsyncClient { bool canSend();//ack is not pending size_t space(); - size_t add(const char* data, size_t size, uint8_t apiflags=0);//add for sending + size_t add(const char* data, size_t size, uint8_t apiflags=ASYNC_WRITE_FLAG_COPY);//add for sending bool send();//send all data added with the method above size_t ack(size_t len); //ack data that you have not acked using the method below void ackLater(){ _ack_pcb = false; } //will not ack the current packet. Call from onData size_t write(const char* data); - size_t write(const char* data, size_t size, uint8_t apiflags=0); //only when canSend() == true + size_t write(const char* data, size_t size, uint8_t apiflags=ASYNC_WRITE_FLAG_COPY); //only when canSend() == true uint8_t state(); bool connecting(); @@ -141,10 +144,13 @@ class AsyncClient { void onDisconnect(AcConnectHandler cb, void* arg = 0); //disconnected void onAck(AcAckHandler cb, void* arg = 0); //ack received void onError(AcErrorHandler cb, void* arg = 0); //unsuccessful connect or error - void onData(AcDataHandler cb, void* arg = 0); //data received + void onData(AcDataHandler cb, void* arg = 0); //data received (called if onPacket is not used) + void onPacket(AcPacketHandler cb, void* arg = 0); //data received void onTimeout(AcTimeoutHandler cb, void* arg = 0); //ack timeout void onPoll(AcConnectHandler cb, void* arg = 0); //every 125ms when connected + void ackPacket(struct pbuf * pb); + const char * errorToString(int8_t error); const char * stateToString(); @@ -155,7 +161,7 @@ class AsyncClient { 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 void _s_dns_found(const char *name, struct _ip_addr *ipaddr, void *arg); + static void _s_dns_found(const char *name, struct ip_addr *ipaddr, void *arg); bool _in_lwip_thread; }; diff --git a/libraries/ESPAsyncWebServer/README.md b/libraries/ESPAsyncWebServer/README.md index b58a90217..7ec5424a4 100644 --- a/libraries/ESPAsyncWebServer/README.md +++ b/libraries/ESPAsyncWebServer/README.md @@ -12,6 +12,8 @@ To use this library you might need to have the latest git versions of [ESP32](ht ## Table of contents - [ESPAsyncWebServer ](#espasyncwebserver-) + - [Installation](#installation) + - [Using PlatformIO](#using-platformio) - [Why should you care](#why-should-you-care) - [Important things to remember](#important-things-to-remember) - [Principles of operation](#principles-of-operation) @@ -28,6 +30,7 @@ To use this library you might need to have the latest git versions of [ESP32](ht - [GET, POST and FILE parameters](#get-post-and-file-parameters) - [FILE Upload handling](#file-upload-handling) - [Body data handling](#body-data-handling) + - [JSON body handling with ArduinoJson](#json-body-handling-with-arduinojson) - [Responses](#responses) - [Redirect to another URL](#redirect-to-another-url) - [Basic response with HTTP Code](#basic-response-with-http-code) @@ -78,6 +81,33 @@ To use this library you might need to have the latest git versions of [ESP32](ht - [Setup global and class functions as request handlers](#setup-global-and-class-functions-as-request-handlers) - [Methods for controlling websocket connections](#methods-for-controlling-websocket-connections) - [Adding default headers to all responses](#adding-default-headers) + +## Installation + +### Using PlatformIO + +[PlatformIO](http://platformio.org) is an open source ecosystem for IoT development with cross platform build system, library manager and full support for Espressif ESP8266/ESP32 development. It works on the popular host OS: Mac OS X, Windows, Linux 32/64, Linux ARM (like Raspberry Pi, BeagleBone, CubieBoard). + +1. Install [PlatformIO IDE](http://platformio.org/platformio-ide) +2. Create new project using "PlatformIO Home > New Project" +3. Update dev/platform to staging version: + - [Instruction for Espressif 8266](http://docs.platformio.org/en/latest/platforms/espressif8266.html#using-arduino-framework-with-staging-version) + - [Instruction for Espressif 32](http://docs.platformio.org/en/latest/platforms/espressif32.html#using-arduino-framework-with-staging-version) + 4. Add "ESP Async WebServer" to project using [Project Configuration File `platformio.ini`](http://docs.platformio.org/page/projectconf.html) and [lib_deps](http://docs.platformio.org/page/projectconf/section_env_library.html#lib-deps) option: +```ini +[env:myboard] +platform = espressif... +board = ... +framework = arduino + +# using the latest stable version +lib_deps = ESP Async WebServer + +# or using GIT Url (the latest development version) +lib_deps = https://github.com/me-no-dev/ESPAsyncWebServer.git +``` + 5. Happy coding with PlatformIO! + ## Why should you care - Using asynchronous network means that you can handle more than one connection at the same time - You are called once the request is ready and parsed @@ -284,6 +314,20 @@ void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_ } } ``` +If needed, the `_tempObject` field on the request can be used to store a pointer to temporary data (e.g. from the body) associated with the request. If assigned, the pointer will automatically be freed along with the request. + +### JSON body handling with ArduinoJson +Endpoints which consume JSON can use a special handler to get ready to use JSON data in the request callback: +```cpp +#include "AsyncJson.h" +#include "ArduinoJson.h" + +AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/rest/endpoint", [](AsyncWebServerRequest *request, JsonVariant &json) { + JsonObject& jsonObj = json.as(); + // ... +}); +server.addHandler(handler); +``` ## Responses ### Redirect to another URL @@ -688,7 +732,7 @@ This way of sending Json is great for when the result is below 4KB #include "ArduinoJson.h" -AsyncResponseStream *response = request->beginResponseStream("text/json"); +AsyncResponseStream *response = request->beginResponseStream("application/json"); DynamicJsonBuffer jsonBuffer; JsonObject &root = jsonBuffer.createObject(); root["heap"] = ESP.getFreeHeap(); @@ -942,7 +986,7 @@ ws.printfAll(arguments...); //printf_P to a client ws.printf_P((uint32_t)client_id, PSTR(format), arguments...); //printfAll_P to all clients -ws.printf_P(PSTR(format), arguments...); +ws.printfAll_P(PSTR(format), arguments...); //send text to a client ws.text((uint32_t)client_id, (char*)text); ws.text((uint32_t)client_id, (uint8_t*)text, (size_t)len); @@ -1097,7 +1141,7 @@ server.on("/scan", HTTP_GET, [](AsyncWebServerRequest *request){ } } json += "]"; - request->send(200, "text/json", json); + request->send(200, "application/json", json); json = String(); }); ``` diff --git a/libraries/ESPAsyncWebServer/espressif8266_library.json b/libraries/ESPAsyncWebServer/espressif8266_library.json deleted file mode 100644 index fee6410ef..000000000 --- a/libraries/ESPAsyncWebServer/espressif8266_library.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name":"ESPAsyncWebServer", - "description":"Asynchronous HTTP and WebSocket Server Library for ESP8266", - "keywords":"http,async,websocket,webserver", - "authors": - { - "name": "Hristo Gochkov", - "maintainer": true - }, - "repository": - { - "type": "git", - "url": "https://github.com/me-no-dev/ESPAsyncWebServer.git" - }, - "version": "1.1.0", - "license": "LGPL-3.0", - "frameworks": "arduino", - "platforms": ["espressif8266", "espressif8266_stage"], - "dependencies": - { - "name": "ESPAsyncTCP" - } -} diff --git a/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino b/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino index 651c55774..b350dc325 100644 --- a/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino +++ b/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino @@ -6,7 +6,7 @@ #include #include #include -#include + // SKETCH BEGIN AsyncWebServer server(80); AsyncWebSocket ws("/ws"); diff --git a/libraries/ESPAsyncWebServer/examples/simple_server/simple_server.ino b/libraries/ESPAsyncWebServer/examples/simple_server/simple_server.ino new file mode 100644 index 000000000..6fee6f7c9 --- /dev/null +++ b/libraries/ESPAsyncWebServer/examples/simple_server/simple_server.ino @@ -0,0 +1,72 @@ +// +// A simple server implementation showing how to: +// * serve static messages +// * read GET and POST parameters +// * handle missing pages / 404s +// + +#include +#include +#include +#include +#include + +AsyncWebServer server(80); + +const char* ssid = "YOUR_SSID"; +const char* password = "YOUR_PASSWORD"; + +const char* PARAM_MESSAGE = "message"; + +void notFound(AsyncWebServerRequest *request) { + request->send(404, "text/plain", "Not found"); +} + +void setup() { + + Serial.begin(115200); + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + if (WiFi.waitForConnectResult() != WL_CONNECTED) { + Serial.printf("WiFi Failed!\n"); + return; + } + + Serial.print("IP Address: "); + Serial.println(WiFi.localIP()); + Serial.print("Hostname: "); + Serial.println(WiFi.hostname()); + + server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ + request->send(200, "text/plain", "Hello, world"); + }); + + // Send a GET request to /get?message= + server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) { + String message; + if (request->hasParam(PARAM_MESSAGE)) { + message = request->getParam(PARAM_MESSAGE)->value(); + } else { + message = "No message sent"; + } + request->send(200, "text/plain", "Hello, GET: " + message); + }); + + // Send a POST request to /post with a form field message set to + server.on("/post", HTTP_POST, [](AsyncWebServerRequest *request){ + String message; + if (request->hasParam(PARAM_MESSAGE, true)) { + message = request->getParam(PARAM_MESSAGE, true)->value(); + } else { + message = "No message sent"; + } + request->send(200, "text/plain", "Hello, POST: " + message); + }); + + server.onNotFound(notFound); + + server.begin(); +} + +void loop() { +} \ No newline at end of file diff --git a/libraries/ESPAsyncWebServer/espressif32_library.json b/libraries/ESPAsyncWebServer/library.json similarity index 53% rename from libraries/ESPAsyncWebServer/espressif32_library.json rename to libraries/ESPAsyncWebServer/library.json index baea13973..a2f961a91 100644 --- a/libraries/ESPAsyncWebServer/espressif32_library.json +++ b/libraries/ESPAsyncWebServer/library.json @@ -1,6 +1,6 @@ { - "name":"AsyncWebServer", - "description":"Asynchronous HTTP and WebSocket Server Library for ESP32", + "name":"ESP Async WebServer", + "description":"Asynchronous HTTP and WebSocket Server Library for ESP8266 and ESP32", "keywords":"http,async,websocket,webserver", "authors": { @@ -12,12 +12,18 @@ "type": "git", "url": "https://github.com/me-no-dev/ESPAsyncWebServer.git" }, - "version": "1.1.0", + "version": "1.2.0", "license": "LGPL-3.0", "frameworks": "arduino", - "platforms": ["espressif32", "espressif32_stage"], - "dependencies": - { - "name": "AsyncTCP" - } + "platforms": ["espressif8266", "espressif32"], + "dependencies": [ + { + "name": "ESPAsyncTCP", + "platforms": "espressif8266" + }, + { + "name": "AsyncTCP", + "platforms": "espressif32" + } + ] } diff --git a/libraries/ESPAsyncWebServer/library.properties b/libraries/ESPAsyncWebServer/library.properties index 174bb2f5d..5c144dc3b 100644 --- a/libraries/ESPAsyncWebServer/library.properties +++ b/libraries/ESPAsyncWebServer/library.properties @@ -1,5 +1,5 @@ name=ESP Async WebServer -version=1.1.0 +version=1.2.0 author=Me-No-Dev maintainer=Me-No-Dev sentence=Async Web Server for ESP8266 and ESP31B diff --git a/libraries/ESPAsyncWebServer/src/AsyncJson.h b/libraries/ESPAsyncWebServer/src/AsyncJson.h index 4d37062dc..8412159bc 100644 --- a/libraries/ESPAsyncWebServer/src/AsyncJson.h +++ b/libraries/ESPAsyncWebServer/src/AsyncJson.h @@ -1,9 +1,9 @@ -// ESPasyncJson.h +// AsyncJson.h /* - Async Response to use with arduinoJson and asyncwebserver + Async Response to use with ArduinoJson and AsyncWebServer Written by Andrew Melvin (SticilFace) with help from me-no-dev and BBlanchon. - example of callback in use + Example of callback in use server.on("/json", HTTP_ANY, [](AsyncWebServerRequest * request) { @@ -17,11 +17,27 @@ request->send(response); }); + -------------------- + + Async Request to use with ArduinoJson and AsyncWebServer + Written by Arsène von Wyss (avonwyss) + + Example + + AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/rest/endpoint"); + handler->onRequest([](AsyncWebServerRequest *request, JsonVariant &json) { + JsonObject& jsonObj = json.as(); + // ... + }); + server.addHandler(handler); + */ #ifndef ASYNC_JSON_H_ #define ASYNC_JSON_H_ #include +constexpr char* JSON_MIMETYPE = "application/json"; + /* * Json Response * */ @@ -57,7 +73,7 @@ class AsyncJsonResponse: public AsyncAbstractResponse { public: AsyncJsonResponse(bool isArray=false): _isValid{false} { _code = 200; - _contentType = "text/json"; + _contentType = JSON_MIMETYPE; if(isArray) _root = _jsonBuffer.createArray(); else @@ -80,4 +96,68 @@ class AsyncJsonResponse: public AsyncAbstractResponse { return len; } }; + +typedef std::function ArJsonRequestHandlerFunction; + +class AsyncCallbackJsonWebHandler: public AsyncWebHandler { +private: +protected: + const String _uri; + WebRequestMethodComposite _method; + ArJsonRequestHandlerFunction _onRequest; + int _contentLength; + int _maxContentLength; +public: + AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest) : _uri(uri), _method(HTTP_POST|HTTP_PUT|HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {} + void setMethod(WebRequestMethodComposite method){ _method = method; } + void setMaxContentLength(int maxContentLength){ _maxContentLength = maxContentLength; } + void onRequest(ArJsonRequestHandlerFunction fn){ _onRequest = fn; } + + virtual bool canHandle(AsyncWebServerRequest *request) override final{ + if(!_onRequest) + return false; + + if(!(_method & request->method())) + return false; + + if(_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri+"/"))) + return false; + + if (!request->contentType().equalsIgnoreCase(JSON_MIMETYPE)) + return false; + + request->addInterestingHeader("ANY"); + return true; + } + + virtual void handleRequest(AsyncWebServerRequest *request) override final { + if(_onRequest) { + if (request->_tempObject != NULL) { + DynamicJsonBuffer jsonBuffer; + JsonVariant json = jsonBuffer.parse((uint8_t*)(request->_tempObject)); + if (json.success()) { + _onRequest(request, json); + return; + } + } + request->send(_contentLength > _maxContentLength ? 413 : 400); + } else { + request->send(500); + } + } + virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final { + } + virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final { + if (_onRequest) { + _contentLength = total; + if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) { + request->_tempObject = malloc(total); + } + if (request->_tempObject != NULL) { + memcpy((uint8_t*)(request->_tempObject) + index, data, len); + } + } + } + virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;} +}; #endif diff --git a/libraries/ESPAsyncWebServer/src/AsyncWebSocket.cpp b/libraries/ESPAsyncWebServer/src/AsyncWebSocket.cpp index 7eeadac44..f507aa7e0 100644 --- a/libraries/ESPAsyncWebServer/src/AsyncWebSocket.cpp +++ b/libraries/ESPAsyncWebServer/src/AsyncWebSocket.cpp @@ -146,7 +146,7 @@ AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(uint8_t * data, size_t return; } - _data = new uint8_t[size + 1]; + _data = new uint8_t[_len + 1]; if (_data) { memcpy(_data, data, _len); @@ -224,10 +224,10 @@ bool AsyncWebSocketMessageBuffer::reserve(size_t size) _data = nullptr; } - _data = new uint8_t[size]; + _data = new uint8_t[_len + 1]; if (_data) { - _data[_len] = 0; + _data[_len] = 0; return true; } else { return false; @@ -337,14 +337,31 @@ AsyncWebSocketBasicMessage::~AsyncWebSocketBasicMessage() { _status = WS_MSG_SENT; return 0; } - size_t window = webSocketSendFrameWindow(client); + if(_sent > _len){ + _status = WS_MSG_ERROR; + return 0; + } + size_t toSend = _len - _sent; - if(window < toSend) toSend = window; - bool final = ((toSend + _sent) == _len); - size_t sent = webSocketSendFrame(client, final, (_sent == 0)?_opcode:(int)WS_CONTINUATION, _mask, (uint8_t*)(_data+_sent), toSend); - _sent += sent; - uint8_t headLen = ((sent < 126)?2:4)+(_mask*4); - _ack += sent + headLen; + size_t window = webSocketSendFrameWindow(client); + + if(window < toSend) { + toSend = window; + } + + _sent += toSend; + _ack += toSend + ((toSend < 126)?2:4) + (_mask * 4); + + bool final = (_sent == _len); + uint8_t* dPtr = (uint8_t*)(_data + (_sent - toSend)); + uint8_t opCode = (toSend && _sent == toSend)?_opcode:(uint8_t)WS_CONTINUATION; + + size_t sent = webSocketSendFrame(client, final, opCode, _mask, dPtr, toSend); + _status = WS_MSG_SENDING; + if(toSend && sent != toSend){ + _sent -= (toSend - sent); + _ack -= (toSend - sent); + } return sent; } @@ -384,6 +401,7 @@ AsyncWebSocketMultiMessage::AsyncWebSocketMultiMessage(AsyncWebSocketMessageBuff _data = buffer->get(); _len = buffer->length(); _status = WS_MSG_SENDING; + //ets_printf("M: %u\n", _len); } else { _status = WS_MSG_ERROR; } @@ -399,9 +417,10 @@ AsyncWebSocketMultiMessage::~AsyncWebSocketMultiMessage() { void AsyncWebSocketMultiMessage::ack(size_t len, uint32_t time) { _acked += len; - if(_sent == _len && _acked == _ack){ + if(_sent >= _len && _acked >= _ack){ _status = WS_MSG_SENT; } + //ets_printf("A: %u\n", len); } size_t AsyncWebSocketMultiMessage::send(AsyncClient *client) { if(_status != WS_MSG_SENDING) @@ -410,18 +429,39 @@ AsyncWebSocketMultiMessage::~AsyncWebSocketMultiMessage() { return 0; } if(_sent == _len){ - if(_acked == _ack) - _status = WS_MSG_SENT; + _status = WS_MSG_SENT; return 0; } - size_t window = webSocketSendFrameWindow(client); + if(_sent > _len){ + _status = WS_MSG_ERROR; + //ets_printf("E: %u > %u\n", _sent, _len); + return 0; + } + size_t toSend = _len - _sent; - if(window < toSend) toSend = window; - bool final = ((toSend + _sent) == _len); - size_t sent = webSocketSendFrame(client, final, (_sent == 0)?_opcode:(int)WS_CONTINUATION, _mask, (uint8_t*)(_data+_sent), toSend); - _sent += sent; - uint8_t headLen = ((sent < 126)?2:4)+(_mask*4); - _ack += sent + headLen; + size_t window = webSocketSendFrameWindow(client); + + if(window < toSend) { + toSend = window; + } + + _sent += toSend; + _ack += toSend + ((toSend < 126)?2:4) + (_mask * 4); + + //ets_printf("W: %u %u\n", _sent - toSend, toSend); + + bool final = (_sent == _len); + uint8_t* dPtr = (uint8_t*)(_data + (_sent - toSend)); + uint8_t opCode = (toSend && _sent == toSend)?_opcode:(uint8_t)WS_CONTINUATION; + + size_t sent = webSocketSendFrame(client, final, opCode, _mask, dPtr, toSend); + _status = WS_MSG_SENDING; + if(toSend && sent != toSend){ + //ets_printf("E: %u != %u\n", toSend, sent); + _sent -= (toSend - sent); + _ack -= (toSend - sent); + } + //ets_printf("S: %u %u\n", _sent, sent); return sent; } @@ -511,7 +551,12 @@ void AsyncWebSocketClient::_queueMessage(AsyncWebSocketMessage *dataMessage){ delete dataMessage; return; } - _messageQueue.add(dataMessage); + if(_messageQueue.length() > WS_MAX_QUEUED_MESSAGES){ + ets_printf("ERROR: Too many messages queued\n"); + delete dataMessage; + } else { + _messageQueue.add(dataMessage); + } if(_client->canSend()) _runQueue(); } @@ -565,83 +610,93 @@ void AsyncWebSocketClient::_onDisconnect(){ _server->_handleDisconnect(this); } -void AsyncWebSocketClient::_onData(void *buf, size_t plen){ +void AsyncWebSocketClient::_onData(void *pbuf, size_t plen){ _lastMessageTime = millis(); - uint8_t *fdata = (uint8_t*)buf; - uint8_t * data = fdata; - if(!_pstate){ - _pinfo.index = 0; - _pinfo.final = (fdata[0] & 0x80) != 0; - _pinfo.opcode = fdata[0] & 0x0F; - _pinfo.masked = (fdata[1] & 0x80) != 0; - _pinfo.len = fdata[1] & 0x7F; - data += 2; - plen = plen - 2; - if(_pinfo.len == 126){ - _pinfo.len = fdata[3] | (uint16_t)(fdata[2]) << 8; + uint8_t *data = (uint8_t*)pbuf; + while(plen > 0){ + if(!_pstate){ + const uint8_t *fdata = data; + _pinfo.index = 0; + _pinfo.final = (fdata[0] & 0x80) != 0; + _pinfo.opcode = fdata[0] & 0x0F; + _pinfo.masked = (fdata[1] & 0x80) != 0; + _pinfo.len = fdata[1] & 0x7F; data += 2; - plen = plen - 2; - } else if(_pinfo.len == 127){ - _pinfo.len = fdata[9] | (uint16_t)(fdata[8]) << 8 | (uint32_t)(fdata[7]) << 16 | (uint32_t)(fdata[6]) << 24 | (uint64_t)(fdata[5]) << 32 | (uint64_t)(fdata[4]) << 40 | (uint64_t)(fdata[3]) << 48 | (uint64_t)(fdata[2]) << 56; - data += 8; - plen = plen - 8; - } + plen -= 2; + if(_pinfo.len == 126){ + _pinfo.len = fdata[3] | (uint16_t)(fdata[2]) << 8; + data += 2; + plen -= 2; + } else if(_pinfo.len == 127){ + _pinfo.len = fdata[9] | (uint16_t)(fdata[8]) << 8 | (uint32_t)(fdata[7]) << 16 | (uint32_t)(fdata[6]) << 24 | (uint64_t)(fdata[5]) << 32 | (uint64_t)(fdata[4]) << 40 | (uint64_t)(fdata[3]) << 48 | (uint64_t)(fdata[2]) << 56; + data += 8; + plen -= 8; + } - if(_pinfo.masked){ - memcpy(_pinfo.mask, data, 4); - data += 4; - plen = plen - 4; - size_t i; - for(i=0;i_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, (uint8_t*)data, plen); - - _pinfo.index += plen; - } else if((plen + _pinfo.index) == _pinfo.len){ - _pstate = 0; - if(_pinfo.opcode == WS_DISCONNECT){ - if(plen){ - uint16_t reasonCode = (uint16_t)(data[0] << 8) + data[1]; - char * reasonString = (char*)(data+2); - if(reasonCode > 1001){ - _server->_handleEvent(this, WS_EVT_ERROR, (void *)&reasonCode, (uint8_t*)reasonString, strlen(reasonString)); - } + if((datalen + _pinfo.index) < _pinfo.len){ + _pstate = 1; + + if(_pinfo.index == 0){ + if(_pinfo.opcode){ + _pinfo.message_opcode = _pinfo.opcode; + _pinfo.num = 0; + } else _pinfo.num += 1; } - if(_status == WS_DISCONNECTING){ - _status = WS_DISCONNECTED; - _client->close(true); - } else { - _status = WS_DISCONNECTING; - _queueControl(new AsyncWebSocketControl(WS_DISCONNECT, data, plen)); + _server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, (uint8_t*)data, datalen); + + _pinfo.index += datalen; + } else if((datalen + _pinfo.index) == _pinfo.len){ + _pstate = 0; + if(_pinfo.opcode == WS_DISCONNECT){ + if(datalen){ + uint16_t reasonCode = (uint16_t)(data[0] << 8) + data[1]; + char * reasonString = (char*)(data+2); + if(reasonCode > 1001){ + _server->_handleEvent(this, WS_EVT_ERROR, (void *)&reasonCode, (uint8_t*)reasonString, strlen(reasonString)); + } + } + if(_status == WS_DISCONNECTING){ + _status = WS_DISCONNECTED; + _client->close(true); + } else { + _status = WS_DISCONNECTING; + _queueControl(new AsyncWebSocketControl(WS_DISCONNECT, data, datalen)); + } + } else if(_pinfo.opcode == WS_PING){ + _queueControl(new AsyncWebSocketControl(WS_PONG, data, datalen)); + } else if(_pinfo.opcode == WS_PONG){ + if(datalen != AWSC_PING_PAYLOAD_LEN || memcmp(AWSC_PING_PAYLOAD, data, AWSC_PING_PAYLOAD_LEN) != 0) + _server->_handleEvent(this, WS_EVT_PONG, NULL, data, datalen); + } else if(_pinfo.opcode < 8){//continuation or text/binary frame + _server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, data, datalen); } - } else if(_pinfo.opcode == WS_PING){ - _queueControl(new AsyncWebSocketControl(WS_PONG, data, plen)); - } else if(_pinfo.opcode == WS_PONG){ - if(plen != AWSC_PING_PAYLOAD_LEN || memcmp(AWSC_PING_PAYLOAD, data, AWSC_PING_PAYLOAD_LEN) != 0) - _server->_handleEvent(this, WS_EVT_PONG, NULL, (uint8_t*)data, plen); - } else if(_pinfo.opcode < 8){//continuation or text/binary frame - _server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, (uint8_t*)data, plen); + } else { + //os_printf("frame error: len: %u, index: %llu, total: %llu\n", datalen, _pinfo.index, _pinfo.len); + //what should we do? + break; } - } else { - //os_printf("frame error: len: %u, index: %llu, total: %llu\n", plen, _pinfo.index, _pinfo.len); - //what should we do? + + // restore byte as _handleEvent may have added a null terminator i.e., data[len] = 0; + if (datalen > 0) + data[datalen] = datalast; + + data += datalen; + plen -= datalen; } } @@ -873,8 +928,9 @@ void AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer * buffer){ if (!buffer) return; buffer->lock(); for(const auto& c: _clients){ - if(c->status() == WS_CONNECTED) - c->text(buffer); + if(c->status() == WS_CONNECTED){ + c->text(buffer); + } } buffer->unlock(); _cleanBuffers(); @@ -946,7 +1002,7 @@ size_t AsyncWebSocket::printfAll(const char *format, ...) { va_end(arg); delete[] temp; - AsyncWebSocketMessageBuffer * buffer = makeBuffer(len + 1); + AsyncWebSocketMessageBuffer * buffer = makeBuffer(len); if (!buffer) { return 0; } @@ -1140,9 +1196,10 @@ AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(uint8_t * data, size_t void AsyncWebSocket::_cleanBuffers() { - for(const auto& c: _buffers){ - if(c->canDelete()) - _buffers.remove(c); + for(AsyncWebSocketMessageBuffer * c: _buffers){ + if(c && c->canDelete()){ + _buffers.remove(c); + } } } diff --git a/libraries/ESPAsyncWebServer/src/AsyncWebSocket.h b/libraries/ESPAsyncWebServer/src/AsyncWebSocket.h index 5242ba3c1..06b942a9f 100644 --- a/libraries/ESPAsyncWebServer/src/AsyncWebSocket.h +++ b/libraries/ESPAsyncWebServer/src/AsyncWebSocket.h @@ -24,8 +24,10 @@ #include #ifdef ESP32 #include +#define WS_MAX_QUEUED_MESSAGES 32 #else #include +#define WS_MAX_QUEUED_MESSAGES 8 #endif #include @@ -35,13 +37,26 @@ class AsyncWebSocketClient; class AsyncWebSocketControl; typedef struct { + /** Message type as defined by enum AwsFrameType. + * Note: Applications will only see WS_TEXT and WS_BINARY. + * All other types are handled by the library. */ uint8_t message_opcode; + /** Frame number of a fragmented message. */ uint32_t num; + /** Is this the last frame in a fragmented message ?*/ uint8_t final; + /** Is this frame masked? */ uint8_t masked; + /** Message type as defined by enum AwsFrameType. + * This value is the same as message_opcode for non-fragmented + * messages, but may also be WS_CONTINUATION in a fragmented message. */ uint8_t opcode; + /** Length of the current frame. + * This equals the total length of the message if num == 0 && final == true */ uint64_t len; + /** Mask key */ uint8_t mask[4]; + /** Offset of the data inside the current frame. */ uint64_t index; } AwsFrameInfo; @@ -154,6 +169,8 @@ class AsyncWebSocketClient { uint32_t id(){ return _clientId; } AwsClientStatus status(){ return _status; } AsyncClient* client(){ return _client; } + AsyncWebSocket *server(){ return _server; } + AwsFrameInfo const &pinfo() const { return _pinfo; } IPAddress remoteIP(); uint16_t remotePort(); @@ -199,7 +216,7 @@ class AsyncWebSocketClient { void _onPoll(); void _onTimeout(uint32_t time); void _onDisconnect(); - void _onData(void *buf, size_t plen); + void _onData(void *pbuf, size_t plen); }; typedef std::function AwsEventHandler; diff --git a/libraries/ESPAsyncWebServer/src/ESPAsyncWebServer.h b/libraries/ESPAsyncWebServer/src/ESPAsyncWebServer.h index 447eb34e1..de6f0cd1a 100644 --- a/libraries/ESPAsyncWebServer/src/ESPAsyncWebServer.h +++ b/libraries/ESPAsyncWebServer/src/ESPAsyncWebServer.h @@ -51,6 +51,7 @@ class AsyncStaticWebHandler; class AsyncCallbackWebHandler; class AsyncResponseStream; +#ifndef WEBSERVER_H typedef enum { HTTP_GET = 0b00000001, HTTP_POST = 0b00000010, @@ -61,6 +62,11 @@ typedef enum { HTTP_OPTIONS = 0b01000000, HTTP_ANY = 0b01111111, } WebRequestMethod; +#endif + +//if this value is returned when asked for data, packet will not be sent and you will be asked for data again +#define RESPONSE_TRY_AGAIN 0xFFFFFFFF + typedef uint8_t WebRequestMethodComposite; typedef std::function ArDisconnectHandler; @@ -130,6 +136,7 @@ class AsyncWebServerRequest { AsyncWebServerResponse* _response; StringArray _interestingHeaders; ArDisconnectHandler _onDisconnectfn; + String _temp; uint8_t _parseState; @@ -204,6 +211,7 @@ class AsyncWebServerRequest { RequestedConnectionType requestedConnType() const { return _reqconntype; } bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED); void onDisconnect (ArDisconnectHandler fn); + //hash is the string representation of: // base64(user:pass) for basic or // user:realm:md5(user:realm:pass) for digest @@ -323,6 +331,7 @@ class AsyncWebHandler { virtual void handleRequest(AsyncWebServerRequest *request __attribute__((unused))){} virtual void handleUpload(AsyncWebServerRequest *request __attribute__((unused)), const String& filename __attribute__((unused)), size_t index __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), bool final __attribute__((unused))){} virtual void handleBody(AsyncWebServerRequest *request __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), size_t index __attribute__((unused)), size_t total __attribute__((unused))){} + virtual bool isRequestHandlerTrivial(){return true;} }; /* diff --git a/libraries/ESPAsyncWebServer/src/SPIFFSEditor.cpp b/libraries/ESPAsyncWebServer/src/SPIFFSEditor.cpp index a385a9743..a84fa87dd 100644 --- a/libraries/ESPAsyncWebServer/src/SPIFFSEditor.cpp +++ b/libraries/ESPAsyncWebServer/src/SPIFFSEditor.cpp @@ -477,7 +477,7 @@ void SPIFFSEditor::handleRequest(AsyncWebServerRequest *request){ dir.close(); #endif output += "]"; - request->send(200, "text/json", output); + request->send(200, "application/json", output); output = String(); } else if(request->hasParam("edit") || request->hasParam("download")){ diff --git a/libraries/ESPAsyncWebServer/src/SPIFFSEditor.h b/libraries/ESPAsyncWebServer/src/SPIFFSEditor.h index 115a3df4e..3586429e1 100644 --- a/libraries/ESPAsyncWebServer/src/SPIFFSEditor.h +++ b/libraries/ESPAsyncWebServer/src/SPIFFSEditor.h @@ -18,6 +18,7 @@ class SPIFFSEditor: public AsyncWebHandler { virtual bool canHandle(AsyncWebServerRequest *request) override final; virtual void handleRequest(AsyncWebServerRequest *request) override final; virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final; + virtual bool isRequestHandlerTrivial() override final {return false;} }; #endif diff --git a/libraries/ESPAsyncWebServer/src/WebHandlerImpl.h b/libraries/ESPAsyncWebServer/src/WebHandlerImpl.h index b3c84e31f..b2da6fe50 100644 --- a/libraries/ESPAsyncWebServer/src/WebHandlerImpl.h +++ b/libraries/ESPAsyncWebServer/src/WebHandlerImpl.h @@ -104,6 +104,7 @@ class AsyncCallbackWebHandler: public AsyncWebHandler { if(_onBody) _onBody(request, data, len, index, total); } + virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;} }; #endif /* ASYNCWEBSERVERHANDLERIMPL_H_ */ diff --git a/libraries/ESPAsyncWebServer/src/WebRequest.cpp b/libraries/ESPAsyncWebServer/src/WebRequest.cpp index 91b5caf6a..dbcb92766 100644 --- a/libraries/ESPAsyncWebServer/src/WebRequest.cpp +++ b/libraries/ESPAsyncWebServer/src/WebRequest.cpp @@ -127,12 +127,18 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len){ } } } else if(_parseState == PARSE_REQ_BODY){ + // A handler should be already attached at this point in _parseLine function. + // If handler does nothing (_onRequest is NULL), we don't need to really parse the body. + const bool needParse = _handler && !_handler->isRequestHandlerTrivial(); if(_isMultipart){ - size_t i; - for(i=0; ihandleBody(this, (uint8_t*)buf, len, _parsedLength, _contentLength); _parsedLength += len; - } else { + } else if(needParse) { size_t i; for(i=0; i _cache; size_t _readDataFromCacheOrContent(uint8_t* data, const size_t len); size_t _fillBufferAndProcessTemplates(uint8_t* buf, size_t maxLen); @@ -55,7 +59,10 @@ class AsyncAbstractResponse: public AsyncWebServerResponse { virtual size_t _fillBuffer(uint8_t *buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { return 0; } }; +#ifndef TEMPLATE_PLACEHOLDER #define TEMPLATE_PLACEHOLDER '%' +#endif + #define TEMPLATE_PARAM_NAME_LENGTH 32 class AsyncFileResponse: public AsyncAbstractResponse { using File = fs::File; diff --git a/libraries/ESPAsyncWebServer/src/WebResponses.cpp b/libraries/ESPAsyncWebServer/src/WebResponses.cpp index 67b5bfdb1..cca6b5324 100644 --- a/libraries/ESPAsyncWebServer/src/WebResponses.cpp +++ b/libraries/ESPAsyncWebServer/src/WebResponses.cpp @@ -302,9 +302,7 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u } if(headLen){ - //TODO: memcpy should be faster? - sprintf((char*)buf, "%s", _head.c_str()); - _head = String(); + memcpy(buf, _head.c_str(), _head.length()); } size_t readLen = 0; @@ -313,6 +311,10 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u // HTTP 1.1 allows leading zeros in chunk length. Or spaces may be added. // See RFC2616 sections 2, 3.6.1. readLen = _fillBufferAndProcessTemplates(buf+headLen+6, outLen - 8); + if(readLen == RESPONSE_TRY_AGAIN){ + free(buf); + return 0; + } outLen = sprintf((char*)buf+headLen, "%x", readLen) + headLen; while(outLen < headLen + 4) buf[outLen++] = ' '; buf[outLen++] = '\r'; @@ -321,16 +323,27 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u buf[outLen++] = '\r'; buf[outLen++] = '\n'; } else { - outLen = _fillBufferAndProcessTemplates(buf+headLen, outLen) + headLen; + readLen = _fillBufferAndProcessTemplates(buf+headLen, outLen); + if(readLen == RESPONSE_TRY_AGAIN){ + free(buf); + return 0; + } + outLen = readLen + headLen; } - if(outLen) - _writtenLength += request->client()->write((const char*)buf, outLen); + if(headLen){ + _head = String(); + } - if(_chunked) - _sentLength += readLen; - else - _sentLength += outLen - headLen; + if(outLen){ + _writtenLength += request->client()->write((const char*)buf, outLen); + } + + if(_chunked){ + _sentLength += readLen; + } else { + _sentLength += outLen - headLen; + } free(buf); @@ -378,8 +391,6 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size // temporary buffer to hold parameter name uint8_t buf[TEMPLATE_PARAM_NAME_LENGTH + 1]; String paramName; - // cache position to insert remainder of template parameter value - std::vector::iterator i = _cache.end(); // If closing placeholder is found: if(pTemplateEnd) { // prepare argument to callback @@ -403,16 +414,14 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size // prepare argument to callback *pTemplateEnd = 0; paramName = String(reinterpret_cast(buf)); - // Copy remaining read-ahead data into cache (when std::vector::insert returning iterator will be available, these 3 lines can be simplified into 1) - const size_t pos = _cache.size(); - _cache.insert(_cache.end(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent); - i = _cache.begin() + pos; + // Copy remaining read-ahead data into cache + _cache.insert(_cache.begin(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent); pTemplateEnd = &data[len - 1]; } else // closing placeholder not found in file data, store found percent symbol as is and advance to the next position { // but first, store read file data in cache - _cache.insert(_cache.end(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent); + _cache.insert(_cache.begin(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent); ++pTemplateStart; } } @@ -434,9 +443,7 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size // make room for param value // 1. move extra data to cache if parameter value is longer than placeholder AND if there is no room to store if((pTemplateEnd + 1 < pTemplateStart + numBytesCopied) && (originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1) < len)) { - size_t pos = i - _cache.begin(); - _cache.insert(i, &data[originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1)], &data[len]); - i = _cache.begin() + pos; + _cache.insert(_cache.begin(), &data[originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1)], &data[len]); //2. parameter value is longer than placeholder text, push the data after placeholder which not saved into cache further to the end memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[originalLen] - pTemplateStart - numBytesCopied); } else if(pTemplateEnd + 1 != pTemplateStart + numBytesCopied) @@ -447,7 +454,7 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size memcpy(pTemplateStart, pvstr, numBytesCopied); // If result is longer than buffer, copy the remainder into cache (this could happen only if placeholder text itself did not fit entirely in buffer) if(numBytesCopied < pvlen) { - _cache.insert(i, pvstr + numBytesCopied, pvstr + pvlen); + _cache.insert(_cache.begin(), pvstr + numBytesCopied, pvstr + pvlen); } else if(pTemplateStart + numBytesCopied < pTemplateEnd + 1) { // result is copied fully; if result is shorter than placeholder text... // there is some free room, fill it from cache const size_t roomFreed = pTemplateEnd + 1 - pTemplateStart - numBytesCopied; @@ -476,7 +483,7 @@ void AsyncFileResponse::_setContentType(const String& path){ if (path.endsWith(".html")) _contentType = "text/html"; else if (path.endsWith(".htm")) _contentType = "text/html"; else if (path.endsWith(".css")) _contentType = "text/css"; - else if (path.endsWith(".json")) _contentType = "text/json"; + else if (path.endsWith(".json")) _contentType = "application/json"; else if (path.endsWith(".js")) _contentType = "application/javascript"; else if (path.endsWith(".png")) _contentType = "image/png"; else if (path.endsWith(".gif")) _contentType = "image/gif"; @@ -599,7 +606,9 @@ AsyncCallbackResponse::AsyncCallbackResponse(const String& contentType, size_t l size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len){ size_t ret = _content(data, len, _filledLength); - _filledLength += ret; + if(ret != RESPONSE_TRY_AGAIN){ + _filledLength += ret; + } return ret; } @@ -619,7 +628,9 @@ AsyncChunkedResponse::AsyncChunkedResponse(const String& contentType, AwsRespons size_t AsyncChunkedResponse::_fillBuffer(uint8_t *data, size_t len){ size_t ret = _content(data, len, _filledLength); - _filledLength += ret; + if(ret != RESPONSE_TRY_AGAIN){ + _filledLength += ret; + } return ret; } diff --git a/libraries/ESPAsyncWebServer/src/WebServer.cpp b/libraries/ESPAsyncWebServer/src/WebServer.cpp index 28d2055e4..e6cd0c6c0 100644 --- a/libraries/ESPAsyncWebServer/src/WebServer.cpp +++ b/libraries/ESPAsyncWebServer/src/WebServer.cpp @@ -79,6 +79,7 @@ bool AsyncWebServer::removeHandler(AsyncWebHandler *handler){ } void AsyncWebServer::begin(){ + _server.setNoDelay(true); _server.begin(); } diff --git a/src/asyncwebserver.cpp b/src/asyncwebserver.cpp index 5bef5552e..409bddbe9 100644 --- a/src/asyncwebserver.cpp +++ b/src/asyncwebserver.cpp @@ -60,8 +60,9 @@ bool can_process_serial = true; extern bool deleteRecursive(String path); -extern void CloseSerialUpload (bool iserror, String & filename); -extern bool sendLine2Serial (String & line); +extern bool sendLine2Serial (String & line, int32_t linenb, int32_t * newlinenb); +extern void CloseSerialUpload (bool iserror, String & filename , int32_t linenb); +extern bool purge_serial(); extern long id_connection; const uint8_t PAGE_404 [] PROGMEM = "\n\nRedirecting... \n\n\n
Unknown page : $QUERY$- you will be redirected...\n

\nif not redirected, click here\n

\n\n\n\n
\n\n\n\n"; @@ -1092,6 +1093,7 @@ void SDFile_serial_upload (AsyncWebServerRequest *request, String filename, size LOG ("Uploading: ") LOG (filename) LOG ("\n") + static int32_t lineNb =-1; static String current_line; static bool is_comment = false; static String current_filename; @@ -1107,74 +1109,65 @@ void SDFile_serial_upload (AsyncWebServerRequest *request, String filename, size //Upload start //************** if (!index) { - LOG ("Starting\n") + LOG("Upload Start\r\n") + String command = "M29"; + String resetcmd = "M110 N0"; + if (CONFIG::GetFirmwareTarget() == SMOOTHIEWARE)resetcmd = "N0 M110"; + lineNb=1; + //close any ongoing upload and get current line number + if(!sendLine2Serial (command,1, &lineNb)){ + //it can failed for repetier + if ( ( CONFIG::GetFirmwareTarget() == REPETIER4DV) || (CONFIG::GetFirmwareTarget() == REPETIER) ) { + if(!sendLine2Serial (command,-1, NULL)){ + LOG("Start Upload failed") + web_interface->_upload_status= UPLOAD_STATUS_FAILED; + return; + } + } else { + LOG("Start Upload failed") + web_interface->_upload_status= UPLOAD_STATUS_FAILED; + return; + } + } + //Mount SD card + command = "M21"; + if(!sendLine2Serial (command,-1, NULL)){ + LOG("Mounting SD failed") + web_interface->_upload_status= UPLOAD_STATUS_FAILED; + return; + } + //Reset line numbering + if(!sendLine2Serial (resetcmd,-1, NULL)){ + LOG("Reset Numbering failed") + web_interface->_upload_status= UPLOAD_STATUS_FAILED; + return; + } + lineNb=1; //need to lock serial out to avoid garbage in file (web_interface->blockserial) = true; - current_line = ""; - //init flags - is_comment = false; + current_line =""; current_filename = filename; - web_interface->_upload_status = UPLOAD_STATUS_ONGOING; + is_comment = false; + String response; ESPCOM::println (F ("Uploading..."), PRINTER_PIPE); + //Clear all serial ESPCOM::flush (DEFAULT_PRINTER_PIPE); - LOG ("Clear Serial\r\n"); - if(ESPCOM::available(DEFAULT_PRINTER_PIPE)) { - ESPCOM::bridge(); - CONFIG::wait(1); - } - //command to pritnter to start print - String command = "M28 " + filename; - LOG (command); - LOG ("\r\n"); - ESPCOM::println (command, DEFAULT_PRINTER_PIPE); - ESPCOM::flush (DEFAULT_PRINTER_PIPE); - CONFIG::wait (500); - uint32_t timeout = millis(); - bool done = false; - while (!done) { //time out is 2000ms - CONFIG::wdtFeed(); - //if there is something in serial buffer - size_t len = ESPCOM::available(DEFAULT_PRINTER_PIPE); - //get size of buffer - if (len > 0) { - CONFIG::wdtFeed(); - uint8_t * sbuf = (uint8_t *)malloc(len+1); - if(!sbuf){ - web_interface->_upload_status = UPLOAD_STATUS_CANCELLED; - ESPCOM::println (F ("SD upload rejected"), PRINTER_PIPE); - LOG ("SD upload rejected\r\n"); - request->client()->abort(); - return ; - } - //read buffer - ESPCOM::readBytes (DEFAULT_PRINTER_PIPE, sbuf, len); - //convert buffer to zero end array - sbuf[len] = '\0'; - //use string because easier to handle - response = (const char*) sbuf; - LOG (response); - //if there is a wait it means purge is done - if (response.indexOf ("wait") > -1) { - LOG ("Exit start writing\r\n"); - done = true; - free(sbuf); - break; - } - //it is first command if it is failed no need to continue - //and resend command won't help - if (response.indexOf ("Resend") > -1 || response.indexOf ("failed") > -1) { - web_interface->blockserial = false; - LOG ("Error start writing\r\n"); - web_interface->_upload_status = UPLOAD_STATUS_FAILED; - request->client()->abort(); - free(sbuf); - return; - } - free(sbuf); - } - if ( (millis() - timeout) > SERIAL_CHECK_TIMEOUT) { - done = true; - } + purge_serial(); + //besure nothing left again + purge_serial(); + command = "M28 " + current_filename; + //send start upload + //no correction allowed because it means reset numbering was failed + if (sendLine2Serial(command, lineNb, NULL)){ + CONFIG::wait(1200); + //additional purge, in case it is slow to answer + purge_serial(); + web_interface->_upload_status= UPLOAD_STATUS_ONGOING; + LOG("Creation Ok\r\n") + + } else { + web_interface->_upload_status= UPLOAD_STATUS_FAILED; + LOG("Creation failed\r\n"); } } //Upload write @@ -1196,10 +1189,10 @@ void SDFile_serial_upload (AsyncWebServerRequest *request, String filename, size if (current_line.length() < 126) { //do we have something in buffer ? if (current_line.length() > 0 ) { - current_line += "\r\n"; - if (!sendLine2Serial (current_line) ) { - LOG ("Error over buffer\n") - CloseSerialUpload (true, current_filename); + lineNb++; + if (!sendLine2Serial (current_line, lineNb, NULL) ) { + LOG ("Error sending line\n") + CloseSerialUpload (true, current_filename,lineNb); request->client()->abort(); return; } @@ -1212,7 +1205,8 @@ void SDFile_serial_upload (AsyncWebServerRequest *request, String filename, size } else { //error buffer overload LOG ("Error over buffer\n") - CloseSerialUpload (true, current_filename); + lineNb++; + CloseSerialUpload (true, current_filename, lineNb); request->client()->abort(); return; } @@ -1221,7 +1215,8 @@ void SDFile_serial_upload (AsyncWebServerRequest *request, String filename, size current_line += char (data[pos]); //copy current char to buffer to send/resend } else { LOG ("Error over buffer\n") - CloseSerialUpload (true, current_filename); + lineNb++; + CloseSerialUpload (true, current_filename, lineNb); request->client()->abort(); return; } @@ -1238,16 +1233,18 @@ void SDFile_serial_upload (AsyncWebServerRequest *request, String filename, size LOG ("Final is reached\n") //if last part does not have '\n' if (current_line.length() > 0) { - current_line += "\r\n"; - if (!sendLine2Serial (current_line) ) { + lineNb++; + if (!sendLine2Serial (current_line, lineNb, NULL) ) { LOG ("Error sending buffer\n") - CloseSerialUpload (true, current_filename); + lineNb++; + CloseSerialUpload (true, current_filename, lineNb); request->client()->abort(); return; } } LOG ("Upload finished "); - CloseSerialUpload (false, current_filename); + lineNb++; + CloseSerialUpload (false, current_filename, lineNb); } LOG ("Exit fn\n") } diff --git a/src/config.h b/src/config.h index b2b158603..5a2f13e0c 100644 --- a/src/config.h +++ b/src/config.h @@ -19,7 +19,7 @@ */ //version and sources location -#define FW_VERSION "2.0.0.c24" +#define FW_VERSION "2.0.0.c25" #define REPOSITORY "https://github.com/luc-github/ESP3D" //Customize ESP3D ////////////////////////////////////////////////////////////////////////