From 2d2f55326766dc79340d906d6a4a386e4291e74e Mon Sep 17 00:00:00 2001 From: Khoi Hoang <57012152+khoih-prog@users.noreply.github.com> Date: Thu, 10 Nov 2022 19:34:09 -0500 Subject: [PATCH] v1.4.1 to add demo sending in chunks #### Release v1.4.1 1. Add examples [Async_AdvancedWebServer_SendChunked](https://github.com/khoih-prog/AsyncWebServer_RP2040W/tree/main/examples/Async_AdvancedWebServer_SendChunked) and [AsyncWebServer_SendChunked](https://github.com/khoih-prog/AsyncWebServer_RP2040W/tree/main/examples/AsyncWebServer_SendChunked) to demo how to use `beginChunkedResponse()` to send large `html` in chunks 2. Use `allman astyle` and add `utils` --- CONTRIBUTING.md | 31 +- changelog.md | 7 + .../AsyncFSWebServer/AsyncFSWebServer.ino | 537 +++++++------- .../AsyncFSWebServer_Complex.ino | 615 ++++++++-------- .../AsyncWebServer_SendChunked.ino | 279 ++++++++ .../Async_AdvancedWebServer.ino | 337 ++++----- .../Async_AdvancedWebServer_Country.ino | 308 ++++---- ...bServer_MemoryIssues_SendArduinoString.ino | 347 ++++----- ...cedWebServer_MemoryIssues_Send_CString.ino | 393 ++++++----- .../Async_AdvancedWebServer_SendChunked.ino | 314 +++++++++ .../Async_AdvancedWebServer_favicon.ino | 328 ++++----- .../Async_AdvancedWebServer_favicon/favicon.h | 6 +- .../Async_HelloServer/Async_HelloServer.ino | 259 +++---- .../Async_HelloServer2/Async_HelloServer2.ino | 303 ++++---- .../Async_HttpBasicAuth.ino | 217 +++--- .../Async_PostServer/Async_PostServer.ino | 261 +++---- .../Async_WebSocketsServer.ino | 315 ++++----- examples/Async_WebSocketsServer/webpage.h | 4 +- .../Async_WebSocketsServer_Xtreme.ino | 401 +++++------ examples/MQTTClient_Auth/MQTTClient_Auth.ino | 269 +++---- examples/MQTTClient_Auth/defines.h | 8 +- .../MQTTClient_Basic/MQTTClient_Basic.ino | 273 ++++---- examples/MQTTClient_Basic/defines.h | 8 +- .../MQTT_ThingStream/MQTT_ThingStream.ino | 323 ++++----- examples/MQTT_ThingStream/defines.h | 8 +- examples/WebClient/WebClient.ino | 189 ++--- examples/WebClient/defines.h | 8 +- .../WebClientRepeating/WebClientRepeating.ino | 201 +++--- examples/WebClientRepeating/defines.h | 8 +- library.json | 4 +- library.properties | 4 +- pics/AsyncWebServer_SendChunked.png | Bin 0 -> 77634 bytes src/AsyncEventSource_RP2040W.cpp | 15 +- src/AsyncEventSource_RP2040W.h | 13 +- src/AsyncFSEditor_RP2040W.cpp | 43 +- src/AsyncFSEditor_RP2040W.h | 13 +- src/AsyncJson_RP2040W.h | 137 ++-- src/AsyncWebAuthentication_RP2040W.cpp | 221 +++--- src/AsyncWebAuthentication_RP2040W.h | 17 +- src/AsyncWebHandlerImpl_RP2040W.h | 20 +- src/AsyncWebHandlers_RP2040W.cpp | 25 +- src/AsyncWebRequest_RP2040W.cpp | 75 +- src/AsyncWebResponseImpl_RP2040W.h | 120 ++-- src/AsyncWebResponses_RP2040W.cpp | 269 ++++--- src/AsyncWebServer_RP2040W.cpp | 36 +- src/AsyncWebServer_RP2040W.h | 97 +-- src/AsyncWebServer_RP2040W_Debug.h | 13 +- src/AsyncWebSocket_RP2040W.cpp | 83 ++- src/AsyncWebSocket_RP2040W.h | 162 ++--- src/AsyncWebSynchronization_RP2040W.h | 29 +- src/Crypto/Hash.cpp | 159 ++--- src/Crypto/Hash.h | 44 +- src/Crypto/bearssl_hash.h | 260 +++---- src/Crypto/md5.h | 112 +-- src/Crypto/sha1.c | 660 +++++++++--------- src/Crypto/sha1.h | 202 +++--- src/StringArray_RP2040W.h | 23 +- src/libb64/cdecode.c | 24 +- src/libb64/cdecode.h | 8 +- src/libb64/cencode.c | 18 +- src/libb64/cencode.h | 8 +- utils/astyle_library.conf | 70 ++ utils/restyle.sh | 6 + 63 files changed, 5267 insertions(+), 4280 deletions(-) create mode 100644 examples/AsyncWebServer_SendChunked/AsyncWebServer_SendChunked.ino create mode 100644 examples/Async_AdvancedWebServer_SendChunked/Async_AdvancedWebServer_SendChunked.ino create mode 100644 pics/AsyncWebServer_SendChunked.png create mode 100644 utils/astyle_library.conf create mode 100644 utils/restyle.sh diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 412dd71..c03a55d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,8 +15,8 @@ If you don't find anything, please [open a new issue](https://github.com/khoih-p Please ensure to specify the following: * Arduino IDE version (e.g. 1.8.19) or Platform.io version -* `RP2040` Core Version (e.g. RP2040 core v2.6.1) -* `RP2040W` Board type (e.g. RASPBERRY_PI_PICO_W) +* `RP2040` Core Version (e.g. RP2040 core v2.6.3) +* `RP2040` Board type (e.g. RASPBERRY_PI_PICO_W) * Contextual information (e.g. what you were trying to achieve) * Simplest possible steps to reproduce * Anything that might be relevant in your opinion, such as: @@ -28,14 +28,13 @@ Please ensure to specify the following: ``` Arduino IDE version: 1.8.19 -RP2040 core v2.6.1 +RP2040 core v2.6.3 RASPBERRY_PI_PICO_W Module OS: Ubuntu 20.04 LTS -Linux xy-Inspiron-3593 5.15.0-50-generic #56~20.04.1-Ubuntu SMP Tue Sep 27 15:51:29 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux +Linux xy-Inspiron-3593 5.15.0-52-generic #58~20.04.1-Ubuntu SMP Thu Oct 13 13:09:46 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux Context: I encountered a crash while using this library - Steps to reproduce: 1. ... 2. ... @@ -43,14 +42,36 @@ Steps to reproduce: 4. ... ``` +### Additional context + +Add any other context about the problem here. + +--- + ### Sending Feature Requests Feel free to post feature requests. It's helpful if you can explain exactly why the feature would be useful. There are usually some outstanding feature requests in the [existing issues list](https://github.com/khoih-prog/AsyncWebServer_RP2040W/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement), feel free to add comments to them. +--- + ### Sending Pull Requests Pull Requests with changes and fixes are also welcome! +Please use the `astyle` to reformat the updated library code as follows (demo for Ubuntu Linux) + +1. Change directory to the library GitHub + +``` +xy@xy-Inspiron-3593:~$ cd Arduino/xy/AsyncWebServer_RP2040W_GitHub/ +xy@xy-Inspiron-3593:~/Arduino/xy/AsyncWebServer_RP2040W_GitHub$ +``` + +2. Issue astyle command + +``` +xy@xy-Inspiron-3593:~/Arduino/xy/AsyncWebServer_RP2040W_GitHub$ bash utils/restyle.sh +``` diff --git a/changelog.md b/changelog.md index 8b9414a..4433130 100644 --- a/changelog.md +++ b/changelog.md @@ -11,6 +11,7 @@ ## Table of Contents * [Changelog](#changelog) + * [Release v1.4.1](#Release-v141) * [Release v1.4.0](#Release-v140) * [Release v1.3.1](#Release-v131) * [Release v1.3.0](#Release-v130) @@ -29,6 +30,12 @@ ## Changelog +#### Release v1.4.1 + +1. Add examples [Async_AdvancedWebServer_SendChunked](https://github.com/khoih-prog/AsyncWebServer_RP2040W/tree/main/examples/Async_AdvancedWebServer_SendChunked) and [AsyncWebServer_SendChunked](https://github.com/khoih-prog/AsyncWebServer_RP2040W/tree/main/examples/AsyncWebServer_SendChunked) to demo how to use `beginChunkedResponse()` to send large `html` in chunks +2. Use `allman astyle` and add `utils` + + #### Release v1.4.0 1. Add `LittleFS` functions such as AsyncFSWebServer diff --git a/examples/AsyncFSWebServer/AsyncFSWebServer.ino b/examples/AsyncFSWebServer/AsyncFSWebServer.ino index ff9c3df..cfde58e 100644 --- a/examples/AsyncFSWebServer/AsyncFSWebServer.ino +++ b/examples/AsyncFSWebServer/AsyncFSWebServer.ino @@ -1,10 +1,10 @@ /**************************************************************************************************************************** AsyncFSWebServer.ino - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -19,7 +19,7 @@ *****************************************************************************************************************************/ #if !( defined(ARDUINO_RASPBERRY_PI_PICO_W) ) - #error For RASPBERRY_PI_PICO_W only + #error For RASPBERRY_PI_PICO_W only #endif #define _RP2040W_AWS_LOGLEVEL_ 4 @@ -54,304 +54,309 @@ const char* http_password = "admin"; void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) { - if (type == WS_EVT_CONNECT) - { - Serial.printf("ws[%s][%u] connect\n", server->url(), client->id()); - client->printf("Hello Client %ld :)", client->id()); - client->ping(); - } - else if (type == WS_EVT_DISCONNECT) - { - Serial.printf("ws[%s][%u] disconnect\n", server->url(), client->id()); - } - else if (type == WS_EVT_ERROR) - { - Serial.printf("ws[%s][%u] error(%u): %s\n", server->url(), client->id(), *((uint16_t*)arg), (char*)data); - } - else if (type == WS_EVT_PONG) - { - Serial.printf("ws[%s][%u] pong[%u]: %s\n", server->url(), client->id(), len, (len) ? (char*)data : ""); - } - else if (type == WS_EVT_DATA) - { - AwsFrameInfo * info = (AwsFrameInfo*)arg; - String msg = ""; - - if (info->final && info->index == 0 && info->len == len) - { - //the whole message is in a single frame and we got all of it's data - Serial.printf("ws[%s][%u] %s-message[%llu]: ", server->url(), client->id(), (info->opcode == WS_TEXT) ? "text" : "binary", info->len); - - if (info->opcode == WS_TEXT) - { - for (size_t i = 0; i < info->len; i++) - { - msg += (char) data[i]; - } - } - else - { - char buff[3]; - - for (size_t i = 0; i < info->len; i++) - { - sprintf(buff, "%02x ", (uint8_t) data[i]); - msg += buff ; - } - } - - Serial.printf("%s\n", msg.c_str()); - - if (info->opcode == WS_TEXT) - client->text("I got your text message"); - else - client->binary("I got your binary message"); - } - else - { - //message is comprised of multiple frames or the frame is split into multiple packets - if (info->index == 0) - { - if (info->num == 0) - Serial.printf("ws[%s][%u] %s-message start\n", server->url(), client->id(), (info->message_opcode == WS_TEXT) ? "text" : "binary"); - - Serial.printf("ws[%s][%u] frame[%u] start[%llu]\n", server->url(), client->id(), info->num, info->len); - } - - Serial.printf("ws[%s][%u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), info->num, (info->message_opcode == WS_TEXT) ? "text" : "binary", info->index, info->index + len); - - if (info->opcode == WS_TEXT) - { - for (size_t i = 0; i < len; i++) - { - msg += (char) data[i]; - } - } - else - { - char buff[3]; - - for (size_t i = 0; i < len; i++) - { - sprintf(buff, "%02x ", (uint8_t) data[i]); - msg += buff ; - } - } - - Serial.printf("%s\n", msg.c_str()); - - if ((info->index + len) == info->len) - { - Serial.printf("ws[%s][%u] frame[%u] end[%llu]\n", server->url(), client->id(), info->num, info->len); - - if (info->final) - { - Serial.printf("ws[%s][%u] %s-message end\n", server->url(), client->id(), (info->message_opcode == WS_TEXT) ? "text" : "binary"); - - if (info->message_opcode == WS_TEXT) - client->text("I got your text message"); - else - client->binary("I got your binary message"); - } - } - } - } + if (type == WS_EVT_CONNECT) + { + Serial.printf("ws[%s][%u] connect\n", server->url(), client->id()); + client->printf("Hello Client %ld :)", client->id()); + client->ping(); + } + else if (type == WS_EVT_DISCONNECT) + { + Serial.printf("ws[%s][%u] disconnect\n", server->url(), client->id()); + } + else if (type == WS_EVT_ERROR) + { + Serial.printf("ws[%s][%u] error(%u): %s\n", server->url(), client->id(), *((uint16_t*)arg), (char*)data); + } + else if (type == WS_EVT_PONG) + { + Serial.printf("ws[%s][%u] pong[%u]: %s\n", server->url(), client->id(), len, (len) ? (char*)data : ""); + } + else if (type == WS_EVT_DATA) + { + AwsFrameInfo * info = (AwsFrameInfo*)arg; + String msg = ""; + + if (info->final && info->index == 0 && info->len == len) + { + //the whole message is in a single frame and we got all of it's data + Serial.printf("ws[%s][%u] %s-message[%llu]: ", server->url(), client->id(), (info->opcode == WS_TEXT) ? "text" : "binary", info->len); + + if (info->opcode == WS_TEXT) + { + for (size_t i = 0; i < info->len; i++) + { + msg += (char) data[i]; + } + } + else + { + char buff[3]; + + for (size_t i = 0; i < info->len; i++) + { + sprintf(buff, "%02x ", (uint8_t) data[i]); + msg += buff ; + } + } + + Serial.printf("%s\n", msg.c_str()); + + if (info->opcode == WS_TEXT) + client->text("I got your text message"); + else + client->binary("I got your binary message"); + } + else + { + //message is comprised of multiple frames or the frame is split into multiple packets + if (info->index == 0) + { + if (info->num == 0) + Serial.printf("ws[%s][%u] %s-message start\n", server->url(), client->id(), (info->message_opcode == WS_TEXT) ? "text" : "binary"); + + Serial.printf("ws[%s][%u] frame[%u] start[%llu]\n", server->url(), client->id(), info->num, info->len); + } + + Serial.printf("ws[%s][%u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), info->num, (info->message_opcode == WS_TEXT) ? "text" : "binary", info->index, info->index + len); + + if (info->opcode == WS_TEXT) + { + for (size_t i = 0; i < len; i++) + { + msg += (char) data[i]; + } + } + else + { + char buff[3]; + + for (size_t i = 0; i < len; i++) + { + sprintf(buff, "%02x ", (uint8_t) data[i]); + msg += buff ; + } + } + + Serial.printf("%s\n", msg.c_str()); + + if ((info->index + len) == info->len) + { + Serial.printf("ws[%s][%u] frame[%u] end[%llu]\n", server->url(), client->id(), info->num, info->len); + + if (info->final) + { + Serial.printf("ws[%s][%u] %s-message end\n", server->url(), client->id(), (info->message_opcode == WS_TEXT) ? "text" : "binary"); + + if (info->message_opcode == WS_TEXT) + client->text("I got your text message"); + else + client->binary("I got your binary message"); + } + } + } + } } //////////////////////////////////////////////////// void initFS() { - // Initialize LittleFS/SPIFFS file-system - if (!LittleFS.begin()) - { - LittleFS.format(); - - if (!LittleFS.begin()) - { - while (true) - { - Serial.println(F("LittleFS failed!")); - - // Stay forever here as useless to go further - delay(5000); - } - } - } + // Initialize LittleFS/SPIFFS file-system + if (!LittleFS.begin()) + { + LittleFS.format(); + + if (!LittleFS.begin()) + { + while (true) + { + Serial.println(F("LittleFS failed!")); + + // Stay forever here as useless to go further + delay(5000); + } + } + } } /////////////////////////////////////////////////// void initWebServer() { - ws.onEvent(onWsEvent); - server.addHandler(&ws); - - events.onConnect([](AsyncEventSourceClient * client) - { - client->send("hello!", NULL, millis(), 1000); - }); - - server.addHandler(&events); - - server.addHandler(new AsyncFSEditor(http_username,http_password)); - - server.serveStatic("/", LittleFS, "/").setDefaultFile("index.htm"); - - server.onNotFound([](AsyncWebServerRequest * request) - { - Serial.printf("NOT_FOUND: "); - if (request->method() == HTTP_GET) - Serial.printf("GET"); - else if (request->method() == HTTP_POST) - Serial.printf("POST"); - else if (request->method() == HTTP_DELETE) - Serial.printf("DELETE"); - else if (request->method() == HTTP_PUT) - Serial.printf("PUT"); - else if (request->method() == HTTP_PATCH) - Serial.printf("PATCH"); - else if (request->method() == HTTP_HEAD) - Serial.printf("HEAD"); - else if (request->method() == HTTP_OPTIONS) - Serial.printf("OPTIONS"); - else - Serial.printf("UNKNOWN"); - - Serial.printf(" http://%s%s\n", request->host().c_str(), request->url().c_str()); - - if (request->contentLength()) - { - Serial.printf("_CONTENT_TYPE: %s\n", request->contentType().c_str()); - Serial.printf("_CONTENT_LENGTH: %u\n", request->contentLength()); - } - - int headers = request->headers(); - int i; - - for (i = 0; i < headers; i++) - { - AsyncWebHeader* h = request->getHeader(i); - Serial.printf("_HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str()); - } - - int params = request->params(); - - for (i = 0; i < params; i++) - { - AsyncWebParameter* p = request->getParam(i); - - if (p->isFile()) - { - Serial.printf("_FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size()); - } - else if (p->isPost()) - { - Serial.printf("_POST[%s]: %s\n", p->name().c_str(), p->value().c_str()); - } - else - { - Serial.printf("_GET[%s]: %s\n", p->name().c_str(), p->value().c_str()); - } - } - - request->send(404); - }); - - server.onFileUpload([](AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t *data, size_t len, bool final) - { - if (!index) - Serial.printf("UploadStart: %s\n", filename.c_str()); - - Serial.printf("%s", (const char*)data); - - if (final) - Serial.printf("UploadEnd: %s (%u)\n", filename.c_str(), index + len); - }); - - server.onRequestBody([](AsyncWebServerRequest * request, uint8_t *data, size_t len, size_t index, size_t total) - { - if (!index) - Serial.printf("BodyStart: %u\n", total); - - Serial.printf("%s", (const char*)data); - - if (index + len == total) - Serial.printf("BodyEnd: %u\n", total); - }); - - server.begin(); + ws.onEvent(onWsEvent); + server.addHandler(&ws); + + events.onConnect([](AsyncEventSourceClient * client) + { + client->send("hello!", NULL, millis(), 1000); + }); + + server.addHandler(&events); + + server.addHandler(new AsyncFSEditor(http_username, http_password)); + + server.serveStatic("/", LittleFS, "/").setDefaultFile("index.htm"); + + server.onNotFound([](AsyncWebServerRequest * request) + { + Serial.printf("NOT_FOUND: "); + + if (request->method() == HTTP_GET) + Serial.printf("GET"); + else if (request->method() == HTTP_POST) + Serial.printf("POST"); + else if (request->method() == HTTP_DELETE) + Serial.printf("DELETE"); + else if (request->method() == HTTP_PUT) + Serial.printf("PUT"); + else if (request->method() == HTTP_PATCH) + Serial.printf("PATCH"); + else if (request->method() == HTTP_HEAD) + Serial.printf("HEAD"); + else if (request->method() == HTTP_OPTIONS) + Serial.printf("OPTIONS"); + else + Serial.printf("UNKNOWN"); + + Serial.printf(" http://%s%s\n", request->host().c_str(), request->url().c_str()); + + if (request->contentLength()) + { + Serial.printf("_CONTENT_TYPE: %s\n", request->contentType().c_str()); + Serial.printf("_CONTENT_LENGTH: %u\n", request->contentLength()); + } + + int headers = request->headers(); + int i; + + for (i = 0; i < headers; i++) + { + AsyncWebHeader* h = request->getHeader(i); + Serial.printf("_HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str()); + } + + int params = request->params(); + + for (i = 0; i < params; i++) + { + AsyncWebParameter* p = request->getParam(i); + + if (p->isFile()) + { + Serial.printf("_FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size()); + } + else if (p->isPost()) + { + Serial.printf("_POST[%s]: %s\n", p->name().c_str(), p->value().c_str()); + } + else + { + Serial.printf("_GET[%s]: %s\n", p->name().c_str(), p->value().c_str()); + } + } + + request->send(404); + }); + + server.onFileUpload([](AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t *data, size_t len, bool final) + { + if (!index) + Serial.printf("UploadStart: %s\n", filename.c_str()); + + Serial.printf("%s", (const char*)data); + + if (final) + Serial.printf("UploadEnd: %s (%u)\n", filename.c_str(), index + len); + }); + + server.onRequestBody([](AsyncWebServerRequest * request, uint8_t *data, size_t len, size_t index, size_t total) + { + if (!index) + Serial.printf("BodyStart: %u\n", total); + + Serial.printf("%s", (const char*)data); + + if (index + len == total) + Serial.printf("BodyEnd: %u\n", total); + }); + + server.begin(); } /////////////////////////////////////////////////// void printWifiStatus() { - // print the SSID of the network you're attached to: - Serial.print("SSID: "); - Serial.println(WiFi.SSID()); - - // print your board's IP address: - IPAddress ip = WiFi.localIP(); - Serial.print("Local IP Address: "); - Serial.println(ip); + // print the SSID of the network you're attached to: + Serial.print("SSID: "); + Serial.println(WiFi.SSID()); + + // print your board's IP address: + IPAddress ip = WiFi.localIP(); + Serial.print("Local IP Address: "); + Serial.println(ip); } void setup() { - Serial.begin(115200); - while (!Serial && millis() < 5000); + Serial.begin(115200); + + while (!Serial && millis() < 5000); + + delay(200); + + Serial.print("\nStart AsyncFSWebServer on "); + Serial.print(BOARD_NAME); + Serial.print(" with "); + Serial.println(SHIELD_TYPE); + Serial.println(ASYNCTCP_RP2040W_VERSION); + Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); + + /////////////////////////////////// + + // check for the WiFi module: + if (WiFi.status() == WL_NO_MODULE) + { + Serial.println("Communication with WiFi module failed!"); - delay(200); + // don't continue + while (true); + } - Serial.print("\nStart AsyncFSWebServer on "); Serial.print(BOARD_NAME); - Serial.print(" with "); Serial.println(SHIELD_TYPE); - Serial.println(ASYNCTCP_RP2040W_VERSION); - Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); + Serial.print(F("Connecting to SSID: ")); + Serial.println(ssid); - /////////////////////////////////// + status = WiFi.begin(ssid, pass); - // check for the WiFi module: - if (WiFi.status() == WL_NO_MODULE) - { - Serial.println("Communication with WiFi module failed!"); - // don't continue - while (true); - } + delay(1000); - Serial.print(F("Connecting to SSID: ")); - Serial.println(ssid); - - status = WiFi.begin(ssid, pass); + // attempt to connect to WiFi network + while ( status != WL_CONNECTED) + { + delay(500); - delay(1000); - - // attempt to connect to WiFi network - while ( status != WL_CONNECTED) - { - delay(500); - - // Connect to WPA/WPA2 network - status = WiFi.status(); - } + // Connect to WPA/WPA2 network + status = WiFi.status(); + } - printWifiStatus(); + printWifiStatus(); - /////////////////////////////////// + /////////////////////////////////// - initFS(); + initFS(); - initWebServer(); + initWebServer(); - Serial.print("AsyncWebServer started @"); - Serial.println(WiFi.localIP()); + Serial.print("AsyncWebServer started @"); + Serial.println(WiFi.localIP()); - Serial.print(F("Open http://")); - Serial.print(WiFi.localIP()); - Serial.println(F("/edit to see the file browser")); + Serial.print(F("Open http://")); + Serial.print(WiFi.localIP()); + Serial.println(F("/edit to see the file browser")); } void loop() { - ws.cleanupClients(); + ws.cleanupClients(); } diff --git a/examples/AsyncFSWebServer_Complex/AsyncFSWebServer_Complex.ino b/examples/AsyncFSWebServer_Complex/AsyncFSWebServer_Complex.ino index e3ea1cc..155748c 100644 --- a/examples/AsyncFSWebServer_Complex/AsyncFSWebServer_Complex.ino +++ b/examples/AsyncFSWebServer_Complex/AsyncFSWebServer_Complex.ino @@ -1,10 +1,10 @@ /**************************************************************************************************************************** AsyncFSWebServer_Complex.ino - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -19,7 +19,7 @@ *****************************************************************************************************************************/ #if !( defined(ARDUINO_RASPBERRY_PI_PICO_W) ) - #error For RASPBERRY_PI_PICO_W only + #error For RASPBERRY_PI_PICO_W only #endif #define _RP2040W_AWS_LOGLEVEL_ 1 @@ -54,130 +54,130 @@ const char* http_password = "admin"; void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) { - if (type == WS_EVT_CONNECT) - { - Serial.printf("ws[%s][%u] connect\n", server->url(), client->id()); - client->printf("Hello Client %ld :)", client->id()); - client->ping(); - } - else if (type == WS_EVT_DISCONNECT) - { - Serial.printf("ws[%s][%u] disconnect\n", server->url(), client->id()); - } - else if (type == WS_EVT_ERROR) - { - Serial.printf("ws[%s][%u] error(%u): %s\n", server->url(), client->id(), *((uint16_t*)arg), (char*)data); - } - else if (type == WS_EVT_PONG) - { - Serial.printf("ws[%s][%u] pong[%u]: %s\n", server->url(), client->id(), len, (len) ? (char*)data : ""); - } - else if (type == WS_EVT_DATA) - { - AwsFrameInfo * info = (AwsFrameInfo*)arg; - String msg = ""; - - if (info->final && info->index == 0 && info->len == len) - { - //the whole message is in a single frame and we got all of it's data - Serial.printf("ws[%s][%u] %s-message[%llu]: ", server->url(), client->id(), (info->opcode == WS_TEXT) ? "text" : "binary", info->len); - - if (info->opcode == WS_TEXT) - { - for (size_t i = 0; i < info->len; i++) - { - msg += (char) data[i]; - } - } - else - { - char buff[3]; - - for (size_t i = 0; i < info->len; i++) - { - sprintf(buff, "%02x ", (uint8_t) data[i]); - msg += buff ; - } - } - - Serial.printf("%s\n", msg.c_str()); - - if (info->opcode == WS_TEXT) - client->text("I got your text message"); - else - client->binary("I got your binary message"); - } - else - { - //message is comprised of multiple frames or the frame is split into multiple packets - if (info->index == 0) - { - if (info->num == 0) - Serial.printf("ws[%s][%u] %s-message start\n", server->url(), client->id(), (info->message_opcode == WS_TEXT) ? "text" : "binary"); - - Serial.printf("ws[%s][%u] frame[%u] start[%llu]\n", server->url(), client->id(), info->num, info->len); - } - - Serial.printf("ws[%s][%u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), info->num, (info->message_opcode == WS_TEXT) ? "text" : "binary", info->index, info->index + len); - - if (info->opcode == WS_TEXT) - { - for (size_t i = 0; i < len; i++) - { - msg += (char) data[i]; - } - } - else - { - char buff[3]; - - for (size_t i = 0; i < len; i++) - { - sprintf(buff, "%02x ", (uint8_t) data[i]); - msg += buff ; - } - } - - Serial.printf("%s\n", msg.c_str()); - - if ((info->index + len) == info->len) - { - Serial.printf("ws[%s][%u] frame[%u] end[%llu]\n", server->url(), client->id(), info->num, info->len); - - if (info->final) - { - Serial.printf("ws[%s][%u] %s-message end\n", server->url(), client->id(), (info->message_opcode == WS_TEXT) ? "text" : "binary"); - - if (info->message_opcode == WS_TEXT) - client->text("I got your text message"); - else - client->binary("I got your binary message"); - } - } - } - } + if (type == WS_EVT_CONNECT) + { + Serial.printf("ws[%s][%u] connect\n", server->url(), client->id()); + client->printf("Hello Client %ld :)", client->id()); + client->ping(); + } + else if (type == WS_EVT_DISCONNECT) + { + Serial.printf("ws[%s][%u] disconnect\n", server->url(), client->id()); + } + else if (type == WS_EVT_ERROR) + { + Serial.printf("ws[%s][%u] error(%u): %s\n", server->url(), client->id(), *((uint16_t*)arg), (char*)data); + } + else if (type == WS_EVT_PONG) + { + Serial.printf("ws[%s][%u] pong[%u]: %s\n", server->url(), client->id(), len, (len) ? (char*)data : ""); + } + else if (type == WS_EVT_DATA) + { + AwsFrameInfo * info = (AwsFrameInfo*)arg; + String msg = ""; + + if (info->final && info->index == 0 && info->len == len) + { + //the whole message is in a single frame and we got all of it's data + Serial.printf("ws[%s][%u] %s-message[%llu]: ", server->url(), client->id(), (info->opcode == WS_TEXT) ? "text" : "binary", info->len); + + if (info->opcode == WS_TEXT) + { + for (size_t i = 0; i < info->len; i++) + { + msg += (char) data[i]; + } + } + else + { + char buff[3]; + + for (size_t i = 0; i < info->len; i++) + { + sprintf(buff, "%02x ", (uint8_t) data[i]); + msg += buff ; + } + } + + Serial.printf("%s\n", msg.c_str()); + + if (info->opcode == WS_TEXT) + client->text("I got your text message"); + else + client->binary("I got your binary message"); + } + else + { + //message is comprised of multiple frames or the frame is split into multiple packets + if (info->index == 0) + { + if (info->num == 0) + Serial.printf("ws[%s][%u] %s-message start\n", server->url(), client->id(), (info->message_opcode == WS_TEXT) ? "text" : "binary"); + + Serial.printf("ws[%s][%u] frame[%u] start[%llu]\n", server->url(), client->id(), info->num, info->len); + } + + Serial.printf("ws[%s][%u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), info->num, (info->message_opcode == WS_TEXT) ? "text" : "binary", info->index, info->index + len); + + if (info->opcode == WS_TEXT) + { + for (size_t i = 0; i < len; i++) + { + msg += (char) data[i]; + } + } + else + { + char buff[3]; + + for (size_t i = 0; i < len; i++) + { + sprintf(buff, "%02x ", (uint8_t) data[i]); + msg += buff ; + } + } + + Serial.printf("%s\n", msg.c_str()); + + if ((info->index + len) == info->len) + { + Serial.printf("ws[%s][%u] frame[%u] end[%llu]\n", server->url(), client->id(), info->num, info->len); + + if (info->final) + { + Serial.printf("ws[%s][%u] %s-message end\n", server->url(), client->id(), (info->message_opcode == WS_TEXT) ? "text" : "binary"); + + if (info->message_opcode == WS_TEXT) + client->text("I got your text message"); + else + client->binary("I got your binary message"); + } + } + } + } } //////////////////////////////////////////////////// void initFS() { - // Initialize LittleFS/SPIFFS file-system - if (!LittleFS.begin()) - { - LittleFS.format(); - - if (!LittleFS.begin()) - { - while (true) - { - Serial.println(F("LittleFS failed!")); - - // Stay forever here as useless to go further - delay(5000); - } - } - } + // Initialize LittleFS/SPIFFS file-system + if (!LittleFS.begin()) + { + LittleFS.format(); + + if (!LittleFS.begin()) + { + while (true) + { + Serial.println(F("LittleFS failed!")); + + // Stay forever here as useless to go further + delay(5000); + } + } + } } /////////////////////////////////////////////////// @@ -185,215 +185,220 @@ void initFS() //format bytes String formatBytes(size_t bytes) { - if (bytes < 1024) - { - return String(bytes) + "B"; - } - else if (bytes < (1024 * 1024)) - { - return String(bytes / 1024.0) + "KB"; - } - else if (bytes < (1024 * 1024 * 1024)) - { - return String(bytes / 1024.0 / 1024.0) + "MB"; - } - else - { - return String(bytes / 1024.0 / 1024.0 / 1024.0) + "GB"; - } + if (bytes < 1024) + { + return String(bytes) + "B"; + } + else if (bytes < (1024 * 1024)) + { + return String(bytes / 1024.0) + "KB"; + } + else if (bytes < (1024 * 1024 * 1024)) + { + return String(bytes / 1024.0 / 1024.0) + "MB"; + } + else + { + return String(bytes / 1024.0 / 1024.0 / 1024.0) + "GB"; + } } /////////////////////////////////////////////////// void listDir() { - Dir dir = LittleFS.openDir("/"); - Serial.println(F("Opening / directory")); - - while (dir.next()) - { - String fileName = dir.fileName(); - size_t fileSize = dir.fileSize(); - Serial.printf("FS File: %s, size: %s\n", fileName.c_str(), formatBytes(fileSize).c_str()); - } - - Serial.println(); + Dir dir = LittleFS.openDir("/"); + Serial.println(F("Opening / directory")); + + while (dir.next()) + { + String fileName = dir.fileName(); + size_t fileSize = dir.fileSize(); + Serial.printf("FS File: %s, size: %s\n", fileName.c_str(), formatBytes(fileSize).c_str()); + } + + Serial.println(); } /////////////////////////////////////////////////// void initWebServer() { - ws.onEvent(onWsEvent); - server.addHandler(&ws); - - events.onConnect([](AsyncEventSourceClient * client) - { - client->send("hello!", NULL, millis(), 1000); - }); - - server.addHandler(&events); - - server.addHandler(new AsyncFSEditor(http_username,http_password)); - - server.serveStatic("/", LittleFS, "/").setDefaultFile("index.htm"); - - server.onNotFound([](AsyncWebServerRequest * request) - { - Serial.printf("NOT_FOUND: "); - if (request->method() == HTTP_GET) - Serial.printf("GET"); - else if (request->method() == HTTP_POST) - Serial.printf("POST"); - else if (request->method() == HTTP_DELETE) - Serial.printf("DELETE"); - else if (request->method() == HTTP_PUT) - Serial.printf("PUT"); - else if (request->method() == HTTP_PATCH) - Serial.printf("PATCH"); - else if (request->method() == HTTP_HEAD) - Serial.printf("HEAD"); - else if (request->method() == HTTP_OPTIONS) - Serial.printf("OPTIONS"); - else - Serial.printf("UNKNOWN"); - - Serial.printf(" http://%s%s\n", request->host().c_str(), request->url().c_str()); - - if (request->contentLength()) - { - Serial.printf("_CONTENT_TYPE: %s\n", request->contentType().c_str()); - Serial.printf("_CONTENT_LENGTH: %u\n", request->contentLength()); - } - - int headers = request->headers(); - int i; - - for (i = 0; i < headers; i++) - { - AsyncWebHeader* h = request->getHeader(i); - Serial.printf("_HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str()); - } - - int params = request->params(); - - for (i = 0; i < params; i++) - { - AsyncWebParameter* p = request->getParam(i); - - if (p->isFile()) - { - Serial.printf("_FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size()); - } - else if (p->isPost()) - { - Serial.printf("_POST[%s]: %s\n", p->name().c_str(), p->value().c_str()); - } - else - { - Serial.printf("_GET[%s]: %s\n", p->name().c_str(), p->value().c_str()); - } - } - - request->send(404); - }); - - server.onFileUpload([](AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t *data, size_t len, bool final) - { - if (!index) - Serial.printf("UploadStart: %s\n", filename.c_str()); - - Serial.printf("%s", (const char*)data); - - if (final) - Serial.printf("UploadEnd: %s (%u)\n", filename.c_str(), index + len); - }); - - server.onRequestBody([](AsyncWebServerRequest * request, uint8_t *data, size_t len, size_t index, size_t total) - { - if (!index) - Serial.printf("BodyStart: %u\n", total); - - Serial.printf("%s", (const char*)data); - - if (index + len == total) - Serial.printf("BodyEnd: %u\n", total); - }); - - server.begin(); + ws.onEvent(onWsEvent); + server.addHandler(&ws); + + events.onConnect([](AsyncEventSourceClient * client) + { + client->send("hello!", NULL, millis(), 1000); + }); + + server.addHandler(&events); + + server.addHandler(new AsyncFSEditor(http_username, http_password)); + + server.serveStatic("/", LittleFS, "/").setDefaultFile("index.htm"); + + server.onNotFound([](AsyncWebServerRequest * request) + { + Serial.printf("NOT_FOUND: "); + + if (request->method() == HTTP_GET) + Serial.printf("GET"); + else if (request->method() == HTTP_POST) + Serial.printf("POST"); + else if (request->method() == HTTP_DELETE) + Serial.printf("DELETE"); + else if (request->method() == HTTP_PUT) + Serial.printf("PUT"); + else if (request->method() == HTTP_PATCH) + Serial.printf("PATCH"); + else if (request->method() == HTTP_HEAD) + Serial.printf("HEAD"); + else if (request->method() == HTTP_OPTIONS) + Serial.printf("OPTIONS"); + else + Serial.printf("UNKNOWN"); + + Serial.printf(" http://%s%s\n", request->host().c_str(), request->url().c_str()); + + if (request->contentLength()) + { + Serial.printf("_CONTENT_TYPE: %s\n", request->contentType().c_str()); + Serial.printf("_CONTENT_LENGTH: %u\n", request->contentLength()); + } + + int headers = request->headers(); + int i; + + for (i = 0; i < headers; i++) + { + AsyncWebHeader* h = request->getHeader(i); + Serial.printf("_HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str()); + } + + int params = request->params(); + + for (i = 0; i < params; i++) + { + AsyncWebParameter* p = request->getParam(i); + + if (p->isFile()) + { + Serial.printf("_FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size()); + } + else if (p->isPost()) + { + Serial.printf("_POST[%s]: %s\n", p->name().c_str(), p->value().c_str()); + } + else + { + Serial.printf("_GET[%s]: %s\n", p->name().c_str(), p->value().c_str()); + } + } + + request->send(404); + }); + + server.onFileUpload([](AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t *data, size_t len, bool final) + { + if (!index) + Serial.printf("UploadStart: %s\n", filename.c_str()); + + Serial.printf("%s", (const char*)data); + + if (final) + Serial.printf("UploadEnd: %s (%u)\n", filename.c_str(), index + len); + }); + + server.onRequestBody([](AsyncWebServerRequest * request, uint8_t *data, size_t len, size_t index, size_t total) + { + if (!index) + Serial.printf("BodyStart: %u\n", total); + + Serial.printf("%s", (const char*)data); + + if (index + len == total) + Serial.printf("BodyEnd: %u\n", total); + }); + + server.begin(); } /////////////////////////////////////////////////// void printWifiStatus() { - // print the SSID of the network you're attached to: - Serial.print("SSID: "); - Serial.println(WiFi.SSID()); - - // print your board's IP address: - IPAddress ip = WiFi.localIP(); - Serial.print("Local IP Address: "); - Serial.println(ip); + // print the SSID of the network you're attached to: + Serial.print("SSID: "); + Serial.println(WiFi.SSID()); + + // print your board's IP address: + IPAddress ip = WiFi.localIP(); + Serial.print("Local IP Address: "); + Serial.println(ip); } /////////////////////////////////////////////////// void setup() { - Serial.begin(115200); - while (!Serial && millis() < 5000); - - delay(200); - - Serial.print("\nStart AsyncFSWebServer_Complex on "); Serial.print(BOARD_NAME); - Serial.print(" with "); Serial.println(SHIELD_TYPE); - Serial.println(ASYNCTCP_RP2040W_VERSION); - Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); - - /////////////////////////////////// - - // check for the WiFi module: - if (WiFi.status() == WL_NO_MODULE) - { - Serial.println("Communication with WiFi module failed!"); - // don't continue - while (true); - } - - Serial.print(F("Connecting to SSID: ")); - Serial.println(ssid); - - status = WiFi.begin(ssid, pass); - - delay(1000); - - // attempt to connect to WiFi network - while ( status != WL_CONNECTED) - { - delay(500); - - // Connect to WPA/WPA2 network - status = WiFi.status(); - } - - printWifiStatus(); - - /////////////////////////////////// - - initFS(); - listDir(); - initWebServer(); - - Serial.print("AsyncWebServer started @"); - Serial.println(WiFi.localIP()); - - Serial.print(F("Open http://")); - Serial.print(WiFi.localIP()); - Serial.println(F("/edit to see the file browser")); + Serial.begin(115200); + + while (!Serial && millis() < 5000); + + delay(200); + + Serial.print("\nStart AsyncFSWebServer_Complex on "); + Serial.print(BOARD_NAME); + Serial.print(" with "); + Serial.println(SHIELD_TYPE); + Serial.println(ASYNCTCP_RP2040W_VERSION); + Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); + + /////////////////////////////////// + + // check for the WiFi module: + if (WiFi.status() == WL_NO_MODULE) + { + Serial.println("Communication with WiFi module failed!"); + + // don't continue + while (true); + } + + Serial.print(F("Connecting to SSID: ")); + Serial.println(ssid); + + status = WiFi.begin(ssid, pass); + + delay(1000); + + // attempt to connect to WiFi network + while ( status != WL_CONNECTED) + { + delay(500); + + // Connect to WPA/WPA2 network + status = WiFi.status(); + } + + printWifiStatus(); + + /////////////////////////////////// + + initFS(); + listDir(); + initWebServer(); + + Serial.print("AsyncWebServer started @"); + Serial.println(WiFi.localIP()); + + Serial.print(F("Open http://")); + Serial.print(WiFi.localIP()); + Serial.println(F("/edit to see the file browser")); } void loop() { - ws.cleanupClients(); + ws.cleanupClients(); } diff --git a/examples/AsyncWebServer_SendChunked/AsyncWebServer_SendChunked.ino b/examples/AsyncWebServer_SendChunked/AsyncWebServer_SendChunked.ino new file mode 100644 index 0000000..94b521f --- /dev/null +++ b/examples/AsyncWebServer_SendChunked/AsyncWebServer_SendChunked.ino @@ -0,0 +1,279 @@ +/**************************************************************************************************************************** + AsyncWebServer_SendChunked.ino + + For RP2040W with CYW43439 WiFi + + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi + + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) + Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W + Licensed under GPLv3 license + *****************************************************************************************************************************/ + +// See the list of country codes in +// https://github.com/earlephilhower/cyw43-driver/blob/02533c10a018c6550e9f66f7699e21356f5e4609/src/cyw43_country.h#L59-L111 +// To modify https://github.com/earlephilhower/arduino-pico/blob/master/variants/rpipicow/picow_init.cpp +// Check https://github.com/khoih-prog/AsyncWebServer_RP2040W/issues/3#issuecomment-1255676644 + +#if !( defined(ARDUINO_RASPBERRY_PI_PICO_W) ) +#error For RASPBERRY_PI_PICO_W only +#endif + +#define _RP2040W_AWS_LOGLEVEL_ 4 + +/////////////////////////////////////////////////////////////////// + +#include + +/////////////////////////////////////////////////////////////////// +#include + +int status = WL_IDLE_STATUS; + +// In bytes +#define STRING_SIZE 50000 + +AsyncWebServer server(80); + +char ssid[] = "your_ssid"; // your network SSID (name) +char pass[] = "12345678"; // your network password (use for WPA, or use as key for WEP), length must be 8+ + +int reqCount = 0; // number of requests received + +#define LED_OFF LOW +#define LED_ON HIGH + +#define BUFFER_SIZE 512 +char temp[BUFFER_SIZE]; + +void createPage(String &pageInput) +{ + static uint32_t pageCount = 0; + static uint32_t maxfreeHeap = 0; + static uint32_t minFreeHeap = 0xFFFFFFFF; + uint32_t curFreeHeap = rp2040.getFreeHeap(); + + if (maxfreeHeap < curFreeHeap) + maxfreeHeap = curFreeHeap; + + if (minFreeHeap > curFreeHeap) + minFreeHeap = curFreeHeap; + + //digitalWrite(LED_BUILTIN, LED_ON); + + int sec = millis() / 1000; + int min = sec / 60; + int hr = min / 60; + int day = hr / 24; + + snprintf(temp, BUFFER_SIZE - 1, + "\ +\ +\ +AsyncWebServer-%s\ +\ +\ +\ +

AsyncWebServer_SendChunked_RP2040W!

\ +

running WiFi on %s

\ +

Uptime: %d d %02d:%02d:%02d, pageCount: %lu

\ +

Heap Free: %lu, Max: %lu, Min: %lu

\ +\ +", BOARD_NAME, BOARD_NAME, day, hr % 24, min % 60, sec % 60, ++pageCount, curFreeHeap, maxfreeHeap, minFreeHeap); + + pageInput = temp; +} + +void handleNotFound(AsyncWebServerRequest *request) +{ + digitalWrite(LED_BUILTIN, LED_ON); + String message = "File Not Found\n\n"; + + message += "URI: "; + message += request->url(); + message += "\nMethod: "; + message += (request->method() == HTTP_GET) ? "GET" : "POST"; + message += "\nArguments: "; + message += request->args(); + message += "\n"; + + for (uint8_t i = 0; i < request->args(); i++) + { + message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; + } + + request->send(404, "text/plain", message); + + digitalWrite(LED_BUILTIN, LED_OFF); +} + +String out; + +void handleRoot(AsyncWebServerRequest *request) +{ + out.reserve(STRING_SIZE); + char temp[70]; + + // clear the String to start over + out = String(); + + digitalWrite(LED_BUILTIN, LED_ON); + + createPage(out); + + out += "\r\n"; + + for (uint16_t lineIndex = 0; lineIndex < 500; lineIndex++) + { + out += ""; + } + + out += "
INDEXDATA
"; + out += String(lineIndex); + out += ""; + out += "RP2040W_AsyncSocketServer_SendChunked_ABCDEFGHIJKLMNOPQRSTUVWXYZ
\r\n"; + + AWS_LOGDEBUG1("Total length to send in chunks =", out.length()); + + AsyncWebServerResponse *response = request->beginChunkedResponse("text/html", [](uint8_t *buffer, size_t maxLen, size_t filledLength) -> size_t + { + size_t len = min(maxLen, out.length() - filledLength); + memcpy(buffer, out.c_str() + filledLength, len); + + AWS_LOGDEBUG1("Bytes sent in chunk =", len); + + return len; + }); + + request->send(response); + + digitalWrite(LED_BUILTIN, LED_OFF); +} + +void printWifiStatus() +{ + // print the SSID of the network you're attached to: + Serial.print("SSID: "); + Serial.println(WiFi.SSID()); + + // print your board's IP address: + IPAddress ip = WiFi.localIP(); + Serial.print("Local IP Address: "); + Serial.println(ip); + + // print your board's country code + // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) + uint32_t myCountryCode = cyw43_arch_get_country_code(); + char countryCode[3] = { 0, 0, 0 }; + + countryCode[0] = myCountryCode & 0xFF; + countryCode[1] = (myCountryCode >> 8) & 0xFF; + + Serial.print("Country code: "); + Serial.println(countryCode); +} + +void setup() +{ + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, LED_OFF); + + Serial.begin(115200); + + while (!Serial && millis() < 5000); + + delay(200); + + Serial.print("\nStart AsyncWebServer_SendChunked on "); + Serial.print(BOARD_NAME); + Serial.print(" with "); + Serial.println(SHIELD_TYPE); + Serial.println(ASYNCTCP_RP2040W_VERSION); + Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); + + /////////////////////////////////// + + // check for the WiFi module: + if (WiFi.status() == WL_NO_MODULE) + { + Serial.println("Communication with WiFi module failed!"); + + // don't continue + while (true); + } + + Serial.print(F("Connecting to SSID: ")); + Serial.println(ssid); + + status = WiFi.begin(ssid, pass); + + delay(1000); + + // attempt to connect to WiFi network + while ( status != WL_CONNECTED) + { + delay(500); + + // Connect to WPA/WPA2 network + status = WiFi.status(); + } + + printWifiStatus(); + + /////////////////////////////////// + + server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) + { + handleRoot(request); + }); + + server.on("/inline", [](AsyncWebServerRequest * request) + { + request->send(200, "text/plain", "This works as well"); + }); + + server.onNotFound(handleNotFound); + + server.begin(); + + Serial.print(F("AsyncWebServer is @ IP : ")); + Serial.println(WiFi.localIP()); +} + +void heartBeatPrint() +{ + static int num = 1; + + Serial.print(F(".")); + + if (num == 80) + { + Serial.println(); + num = 1; + } + else if (num++ % 10 == 0) + { + Serial.print(F(" ")); + } +} + +void check_status() +{ + static unsigned long checkstatus_timeout = 0; + +#define STATUS_CHECK_INTERVAL 10000L + + // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change. + if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) + { + heartBeatPrint(); + checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; + } +} + +void loop() +{ + check_status(); +} diff --git a/examples/Async_AdvancedWebServer/Async_AdvancedWebServer.ino b/examples/Async_AdvancedWebServer/Async_AdvancedWebServer.ino index aef04f5..e8ffdc0 100644 --- a/examples/Async_AdvancedWebServer/Async_AdvancedWebServer.ino +++ b/examples/Async_AdvancedWebServer/Async_AdvancedWebServer.ino @@ -2,9 +2,9 @@ Async_AdvancedWebServer.ino For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -44,7 +44,7 @@ // Check https://github.com/khoih-prog/AsyncWebServer_RP2040W/issues/3#issuecomment-1255676644 #if !( defined(ARDUINO_RASPBERRY_PI_PICO_W) ) - #error For RASPBERRY_PI_PICO_W only + #error For RASPBERRY_PI_PICO_W only #endif #define _RP2040W_AWS_LOGLEVEL_ 1 @@ -74,26 +74,26 @@ char temp[BUFFER_SIZE]; void handleRoot(AsyncWebServerRequest *request) { - static uint32_t pageCount = 0; - static uint32_t maxfreeHeap = 0; - static uint32_t minFreeHeap = 0xFFFFFFFF; - uint32_t curFreeHeap = rp2040.getFreeHeap(); - - if (maxfreeHeap < curFreeHeap) - maxfreeHeap = curFreeHeap; - - if (minFreeHeap > curFreeHeap) - minFreeHeap = curFreeHeap; - - digitalWrite(LED_BUILTIN, LED_ON); - - int sec = millis() / 1000; - int min = sec / 60; - int hr = min / 60; - int day = hr / 24; - - snprintf(temp, BUFFER_SIZE - 1, - "\ + static uint32_t pageCount = 0; + static uint32_t maxfreeHeap = 0; + static uint32_t minFreeHeap = 0xFFFFFFFF; + uint32_t curFreeHeap = rp2040.getFreeHeap(); + + if (maxfreeHeap < curFreeHeap) + maxfreeHeap = curFreeHeap; + + if (minFreeHeap > curFreeHeap) + minFreeHeap = curFreeHeap; + + digitalWrite(LED_BUILTIN, LED_ON); + + int sec = millis() / 1000; + int min = sec / 60; + int hr = min / 60; + int day = hr / 24; + + snprintf(temp, BUFFER_SIZE - 1, + "\ \ \ AsyncWebServer-%s\ @@ -110,185 +110,190 @@ body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Col \ ", BOARD_NAME, BOARD_NAME, day, hr % 24, min % 60, sec % 60, ++pageCount, curFreeHeap, maxfreeHeap, minFreeHeap); - request->send(200, "text/html", temp); + request->send(200, "text/html", temp); - digitalWrite(LED_BUILTIN, LED_OFF); + digitalWrite(LED_BUILTIN, LED_OFF); } void handleNotFound(AsyncWebServerRequest *request) { - digitalWrite(LED_BUILTIN, LED_ON); - String message = "File Not Found\n\n"; - - message += "URI: "; - message += request->url(); - message += "\nMethod: "; - message += (request->method() == HTTP_GET) ? "GET" : "POST"; - message += "\nArguments: "; - message += request->args(); - message += "\n"; - - for (uint8_t i = 0; i < request->args(); i++) - { - message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; - } - - request->send(404, "text/plain", message); - digitalWrite(LED_BUILTIN, LED_OFF); + digitalWrite(LED_BUILTIN, LED_ON); + String message = "File Not Found\n\n"; + + message += "URI: "; + message += request->url(); + message += "\nMethod: "; + message += (request->method() == HTTP_GET) ? "GET" : "POST"; + message += "\nArguments: "; + message += request->args(); + message += "\n"; + + for (uint8_t i = 0; i < request->args(); i++) + { + message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; + } + + request->send(404, "text/plain", message); + digitalWrite(LED_BUILTIN, LED_OFF); } void drawGraph(AsyncWebServerRequest *request) { - String out; + String out; - out.reserve(4000); - char temp[70]; + out.reserve(4000); + char temp[70]; - digitalWrite(LED_BUILTIN, LED_ON); + digitalWrite(LED_BUILTIN, LED_ON); - out += "\n"; - out += "\n"; - out += "\n"; - int y = rand() % 130; + out += "\n"; + out += "\n"; + out += "\n"; + int y = rand() % 130; - for (int x = 10; x < 300; x += 10) - { - int y2 = rand() % 130; - sprintf(temp, "\n", x, 140 - y, x + 10, 140 - y2); - out += temp; - y = y2; - } - - out += "\n\n"; + for (int x = 10; x < 300; x += 10) + { + int y2 = rand() % 130; + sprintf(temp, "\n", x, 140 - y, x + 10, 140 - y2); + out += temp; + y = y2; + } - request->send(200, "image/svg+xml", out); + out += "\n\n"; - digitalWrite(LED_BUILTIN, LED_OFF); + request->send(200, "image/svg+xml", out); + + digitalWrite(LED_BUILTIN, LED_OFF); } void printWifiStatus() { - // print the SSID of the network you're attached to: - Serial.print("SSID: "); - Serial.println(WiFi.SSID()); - - // print your board's IP address: - IPAddress ip = WiFi.localIP(); - Serial.print("Local IP Address: "); - Serial.println(ip); - - // print your board's country code - // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) - uint32_t myCountryCode = cyw43_arch_get_country_code(); - char countryCode[3] = { 0, 0, 0 }; - - countryCode[0] = myCountryCode & 0xFF; - countryCode[1] = (myCountryCode >> 8) & 0xFF; - - Serial.print("Country code: "); Serial.println(countryCode); + // print the SSID of the network you're attached to: + Serial.print("SSID: "); + Serial.println(WiFi.SSID()); + + // print your board's IP address: + IPAddress ip = WiFi.localIP(); + Serial.print("Local IP Address: "); + Serial.println(ip); + + // print your board's country code + // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) + uint32_t myCountryCode = cyw43_arch_get_country_code(); + char countryCode[3] = { 0, 0, 0 }; + + countryCode[0] = myCountryCode & 0xFF; + countryCode[1] = (myCountryCode >> 8) & 0xFF; + + Serial.print("Country code: "); + Serial.println(countryCode); } void setup() { - pinMode(LED_BUILTIN, OUTPUT); - digitalWrite(LED_BUILTIN, LED_OFF); - - Serial.begin(115200); - while (!Serial && millis() < 5000); - - delay(200); - - Serial.print("\nStart Async_AdvancedWebServer on "); Serial.print(BOARD_NAME); - Serial.print(" with "); Serial.println(SHIELD_TYPE); - Serial.println(ASYNCTCP_RP2040W_VERSION); - Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); - - /////////////////////////////////// - - // check for the WiFi module: - if (WiFi.status() == WL_NO_MODULE) - { - Serial.println("Communication with WiFi module failed!"); - // don't continue - while (true); - } - - Serial.print(F("Connecting to SSID: ")); - Serial.println(ssid); - - status = WiFi.begin(ssid, pass); - - delay(1000); - - // attempt to connect to WiFi network - while ( status != WL_CONNECTED) - { - delay(500); - - // Connect to WPA/WPA2 network - status = WiFi.status(); - } - - printWifiStatus(); - - /////////////////////////////////// - - - server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) - { - handleRoot(request); - }); - - server.on("/test.svg", HTTP_GET, [](AsyncWebServerRequest * request) - { - drawGraph(request); - }); - - server.on("/inline", [](AsyncWebServerRequest * request) - { - request->send(200, "text/plain", "This works as well"); - }); - - server.onNotFound(handleNotFound); - - server.begin(); - - Serial.print(F("AsyncWebServer is @ IP : ")); - Serial.println(WiFi.localIP()); + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, LED_OFF); + + Serial.begin(115200); + + while (!Serial && millis() < 5000); + + delay(200); + + Serial.print("\nStart Async_AdvancedWebServer on "); + Serial.print(BOARD_NAME); + Serial.print(" with "); + Serial.println(SHIELD_TYPE); + Serial.println(ASYNCTCP_RP2040W_VERSION); + Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); + + /////////////////////////////////// + + // check for the WiFi module: + if (WiFi.status() == WL_NO_MODULE) + { + Serial.println("Communication with WiFi module failed!"); + + // don't continue + while (true); + } + + Serial.print(F("Connecting to SSID: ")); + Serial.println(ssid); + + status = WiFi.begin(ssid, pass); + + delay(1000); + + // attempt to connect to WiFi network + while ( status != WL_CONNECTED) + { + delay(500); + + // Connect to WPA/WPA2 network + status = WiFi.status(); + } + + printWifiStatus(); + + /////////////////////////////////// + + + server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) + { + handleRoot(request); + }); + + server.on("/test.svg", HTTP_GET, [](AsyncWebServerRequest * request) + { + drawGraph(request); + }); + + server.on("/inline", [](AsyncWebServerRequest * request) + { + request->send(200, "text/plain", "This works as well"); + }); + + server.onNotFound(handleNotFound); + + server.begin(); + + Serial.print(F("AsyncWebServer is @ IP : ")); + Serial.println(WiFi.localIP()); } void heartBeatPrint() { - static int num = 1; - - Serial.print(F(".")); - - if (num == 80) - { - Serial.println(); - num = 1; - } - else if (num++ % 10 == 0) - { - Serial.print(F(" ")); - } + static int num = 1; + + Serial.print(F(".")); + + if (num == 80) + { + Serial.println(); + num = 1; + } + else if (num++ % 10 == 0) + { + Serial.print(F(" ")); + } } void check_status() { - static unsigned long checkstatus_timeout = 0; + static unsigned long checkstatus_timeout = 0; #define STATUS_CHECK_INTERVAL 10000L - // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change. - if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) - { - heartBeatPrint(); - checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; - } + // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change. + if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) + { + heartBeatPrint(); + checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; + } } void loop() { - check_status(); + check_status(); } diff --git a/examples/Async_AdvancedWebServer_Country/Async_AdvancedWebServer_Country.ino b/examples/Async_AdvancedWebServer_Country/Async_AdvancedWebServer_Country.ino index 2575192..17a97c8 100644 --- a/examples/Async_AdvancedWebServer_Country/Async_AdvancedWebServer_Country.ino +++ b/examples/Async_AdvancedWebServer_Country/Async_AdvancedWebServer_Country.ino @@ -2,9 +2,9 @@ Async_AdvancedWebServer.ino For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -44,7 +44,7 @@ // Check https://github.com/khoih-prog/AsyncWebServer_RP2040W/issues/3#issuecomment-1255676644 #if !( defined(ARDUINO_RASPBERRY_PI_PICO_W) ) - #error For RASPBERRY_PI_PICO_W only + #error For RASPBERRY_PI_PICO_W only #endif #define _ASYNCTCP_RP2040W_LOGLEVEL_ 4 @@ -77,15 +77,15 @@ char temp[BUFFER_SIZE]; void handleRoot(AsyncWebServerRequest *request) { - digitalWrite(LED_BUILTIN, LED_ON); + digitalWrite(LED_BUILTIN, LED_ON); - int sec = millis() / 1000; - int min = sec / 60; - int hr = min / 60; - int day = hr / 24; + int sec = millis() / 1000; + int min = sec / 60; + int hr = min / 60; + int day = hr / 24; - snprintf(temp, BUFFER_SIZE - 1, - "\ + snprintf(temp, BUFFER_SIZE - 1, + "\ \ \ AsyncWebServer-%s\ @@ -101,182 +101,188 @@ body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Col \ ", BOARD_NAME, countryCode, BOARD_NAME, day, hr % 24, min % 60, sec % 60); - request->send(200, "text/html", temp); + request->send(200, "text/html", temp); - digitalWrite(LED_BUILTIN, LED_OFF); + digitalWrite(LED_BUILTIN, LED_OFF); } void handleNotFound(AsyncWebServerRequest *request) { - digitalWrite(LED_BUILTIN, LED_ON); - String message = "File Not Found\n\n"; - - message += "URI: "; - message += request->url(); - message += "\nMethod: "; - message += (request->method() == HTTP_GET) ? "GET" : "POST"; - message += "\nArguments: "; - message += request->args(); - message += "\n"; - - for (uint8_t i = 0; i < request->args(); i++) - { - message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; - } - - request->send(404, "text/plain", message); - digitalWrite(LED_BUILTIN, LED_OFF); + digitalWrite(LED_BUILTIN, LED_ON); + String message = "File Not Found\n\n"; + + message += "URI: "; + message += request->url(); + message += "\nMethod: "; + message += (request->method() == HTTP_GET) ? "GET" : "POST"; + message += "\nArguments: "; + message += request->args(); + message += "\n"; + + for (uint8_t i = 0; i < request->args(); i++) + { + message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; + } + + request->send(404, "text/plain", message); + digitalWrite(LED_BUILTIN, LED_OFF); } void drawGraph(AsyncWebServerRequest *request) { - String out; + String out; - out.reserve(4000); - char temp[70]; + out.reserve(4000); + char temp[70]; - digitalWrite(LED_BUILTIN, LED_ON); + digitalWrite(LED_BUILTIN, LED_ON); - out += "\n"; - out += "\n"; - out += "\n"; - int y = rand() % 130; + out += "\n"; + out += "\n"; + out += "\n"; + int y = rand() % 130; - for (int x = 10; x < 300; x += 10) - { - int y2 = rand() % 130; - sprintf(temp, "\n", x, 140 - y, x + 10, 140 - y2); - out += temp; - y = y2; - } - out += "\n\n"; + for (int x = 10; x < 300; x += 10) + { + int y2 = rand() % 130; + sprintf(temp, "\n", x, 140 - y, x + 10, 140 - y2); + out += temp; + y = y2; + } - request->send(200, "image/svg+xml", out); + out += "\n\n"; - digitalWrite(LED_BUILTIN, LED_OFF); + request->send(200, "image/svg+xml", out); + + digitalWrite(LED_BUILTIN, LED_OFF); } void printWifiStatus() { - // print the SSID of the network you're attached to: - Serial.print("SSID: "); - Serial.println(WiFi.SSID()); - - // print your board's IP address: - IPAddress ip = WiFi.localIP(); - Serial.print("Local IP Address: "); - Serial.println(ip); - - // print your board's country code - // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) - uint32_t myCountryCode = cyw43_arch_get_country_code(); - - countryCode[0] = myCountryCode & 0xFF; - countryCode[1] = (myCountryCode >> 8) & 0xFF; - - Serial.print("Country code: "); Serial.println(countryCode); + // print the SSID of the network you're attached to: + Serial.print("SSID: "); + Serial.println(WiFi.SSID()); + + // print your board's IP address: + IPAddress ip = WiFi.localIP(); + Serial.print("Local IP Address: "); + Serial.println(ip); + + // print your board's country code + // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) + uint32_t myCountryCode = cyw43_arch_get_country_code(); + + countryCode[0] = myCountryCode & 0xFF; + countryCode[1] = (myCountryCode >> 8) & 0xFF; + + Serial.print("Country code: "); + Serial.println(countryCode); } void setup() -{ - pinMode(LED_BUILTIN, OUTPUT); - digitalWrite(LED_BUILTIN, LED_OFF); - - Serial.begin(115200); - while (!Serial && millis() < 5000); - - delay(200); - - Serial.print("\nStart Async_AdvancedWebServer_Country on "); Serial.print(BOARD_NAME); - Serial.print(" with "); Serial.println(SHIELD_TYPE); - Serial.println(ASYNCTCP_RP2040W_VERSION); - Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); - - /////////////////////////////////// - - // check for the WiFi module: - if (WiFi.status() == WL_NO_MODULE) - { - Serial.println("Communication with WiFi module failed!"); - // don't continue - while (true); - } - - Serial.print(F("Connecting to SSID: ")); - Serial.println(ssid); - - status = WiFi.begin(ssid, pass); - - delay(1000); - - // attempt to connect to WiFi network - while ( status != WL_CONNECTED) - { - delay(500); - - // Connect to WPA/WPA2 network - status = WiFi.status(); - } - - printWifiStatus(); - - /////////////////////////////////// - - server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) - { - handleRoot(request); - }); - - server.on("/test.svg", HTTP_GET, [](AsyncWebServerRequest * request) - { - drawGraph(request); - }); - - server.on("/inline", [](AsyncWebServerRequest * request) - { - request->send(200, "text/plain", "This works as well"); - }); - - server.onNotFound(handleNotFound); - - server.begin(); - - Serial.print(F("HTTP EthernetWebServer is @ IP : ")); - Serial.println(WiFi.localIP()); +{ + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, LED_OFF); + + Serial.begin(115200); + + while (!Serial && millis() < 5000); + + delay(200); + + Serial.print("\nStart Async_AdvancedWebServer_Country on "); + Serial.print(BOARD_NAME); + Serial.print(" with "); + Serial.println(SHIELD_TYPE); + Serial.println(ASYNCTCP_RP2040W_VERSION); + Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); + + /////////////////////////////////// + + // check for the WiFi module: + if (WiFi.status() == WL_NO_MODULE) + { + Serial.println("Communication with WiFi module failed!"); + + // don't continue + while (true); + } + + Serial.print(F("Connecting to SSID: ")); + Serial.println(ssid); + + status = WiFi.begin(ssid, pass); + + delay(1000); + + // attempt to connect to WiFi network + while ( status != WL_CONNECTED) + { + delay(500); + + // Connect to WPA/WPA2 network + status = WiFi.status(); + } + + printWifiStatus(); + + /////////////////////////////////// + + server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) + { + handleRoot(request); + }); + + server.on("/test.svg", HTTP_GET, [](AsyncWebServerRequest * request) + { + drawGraph(request); + }); + + server.on("/inline", [](AsyncWebServerRequest * request) + { + request->send(200, "text/plain", "This works as well"); + }); + + server.onNotFound(handleNotFound); + + server.begin(); + + Serial.print(F("HTTP EthernetWebServer is @ IP : ")); + Serial.println(WiFi.localIP()); } void heartBeatPrint() { - static int num = 1; - - Serial.print(F(".")); - - if (num == 80) - { - Serial.println(); - num = 1; - } - else if (num++ % 10 == 0) - { - Serial.print(F(" ")); - } + static int num = 1; + + Serial.print(F(".")); + + if (num == 80) + { + Serial.println(); + num = 1; + } + else if (num++ % 10 == 0) + { + Serial.print(F(" ")); + } } void check_status() { - static unsigned long checkstatus_timeout = 0; + static unsigned long checkstatus_timeout = 0; #define STATUS_CHECK_INTERVAL 10000L - // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change. - if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) - { - heartBeatPrint(); - checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; - } + // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change. + if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) + { + heartBeatPrint(); + checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; + } } void loop() { - check_status(); + check_status(); } diff --git a/examples/Async_AdvancedWebServer_MemoryIssues_SendArduinoString/Async_AdvancedWebServer_MemoryIssues_SendArduinoString.ino b/examples/Async_AdvancedWebServer_MemoryIssues_SendArduinoString/Async_AdvancedWebServer_MemoryIssues_SendArduinoString.ino index 8866fa6..791fa76 100644 --- a/examples/Async_AdvancedWebServer_MemoryIssues_SendArduinoString/Async_AdvancedWebServer_MemoryIssues_SendArduinoString.ino +++ b/examples/Async_AdvancedWebServer_MemoryIssues_SendArduinoString/Async_AdvancedWebServer_MemoryIssues_SendArduinoString.ino @@ -2,9 +2,9 @@ Async_AdvancedWebServer_MemoryIssues_SendArduinoString.ino - - Dead simple AsyncWebServer for Portenta_H7 For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -44,7 +44,7 @@ // Check https://github.com/khoih-prog/AsyncWebServer_RP2040W/issues/3#issuecomment-1255676644 #if !( defined(ARDUINO_RASPBERRY_PI_PICO_W) ) - #error For RASPBERRY_PI_PICO_W only + #error For RASPBERRY_PI_PICO_W only #endif #define _RP2040W_AWS_LOGLEVEL_ 1 @@ -78,15 +78,15 @@ char temp[BUFFER_SIZE]; void handleRoot(AsyncWebServerRequest *request) { - digitalWrite(LED_BUILTIN, LED_ON); + digitalWrite(LED_BUILTIN, LED_ON); - int sec = millis() / 1000; - int min = sec / 60; - int hr = min / 60; - int day = hr / 24; + int sec = millis() / 1000; + int min = sec / 60; + int hr = min / 60; + int day = hr / 24; - snprintf(temp, BUFFER_SIZE - 1, - "\ + snprintf(temp, BUFFER_SIZE - 1, + "\ \ \ AsyncWebServer-%s\ @@ -102,229 +102,234 @@ body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Col \ ", BOARD_NAME, BOARD_NAME, day, hr % 24, min % 60, sec % 60); - request->send(200, "text/html", temp); + request->send(200, "text/html", temp); - digitalWrite(LED_BUILTIN, LED_OFF); + digitalWrite(LED_BUILTIN, LED_OFF); } void handleNotFound(AsyncWebServerRequest *request) { - digitalWrite(LED_BUILTIN, LED_ON); - String message = "File Not Found\n\n"; - - message += "URI: "; - message += request->url(); - message += "\nMethod: "; - message += (request->method() == HTTP_GET) ? "GET" : "POST"; - message += "\nArguments: "; - message += request->args(); - message += "\n"; - - for (uint8_t i = 0; i < request->args(); i++) - { - message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; - } - - request->send(404, "text/plain", message); - digitalWrite(LED_BUILTIN, LED_OFF); + digitalWrite(LED_BUILTIN, LED_ON); + String message = "File Not Found\n\n"; + + message += "URI: "; + message += request->url(); + message += "\nMethod: "; + message += (request->method() == HTTP_GET) ? "GET" : "POST"; + message += "\nArguments: "; + message += request->args(); + message += "\n"; + + for (uint8_t i = 0; i < request->args(); i++) + { + message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; + } + + request->send(404, "text/plain", message); + digitalWrite(LED_BUILTIN, LED_OFF); } void PrintHeapData(String hIn) { - // Check https://arduino-pico.readthedocs.io/en/latest/rp2040.html - - static uint32_t maxHeapSize = 0; - - uint32_t usedHeap = rp2040.getUsedHeap(); - uint32_t totalHeap = rp2040.getTotalHeap(); - - // Print and update only when larger heap - if (maxHeapSize < usedHeap) - { - maxHeapSize = usedHeap; - - Serial.print("\nHEAP DATA - "); - Serial.print(hIn); - - Serial.print(" Cur heap: "); - Serial.print(totalHeap); - Serial.print(" Free heap: "); - Serial.print(totalHeap - usedHeap); - Serial.print(" Max heap: "); - Serial.println(usedHeap); - } + // Check https://arduino-pico.readthedocs.io/en/latest/rp2040.html + + static uint32_t maxHeapSize = 0; + + uint32_t usedHeap = rp2040.getUsedHeap(); + uint32_t totalHeap = rp2040.getTotalHeap(); + + // Print and update only when larger heap + if (maxHeapSize < usedHeap) + { + maxHeapSize = usedHeap; + + Serial.print("\nHEAP DATA - "); + Serial.print(hIn); + + Serial.print(" Cur heap: "); + Serial.print(totalHeap); + Serial.print(" Free heap: "); + Serial.print(totalHeap - usedHeap); + Serial.print(" Max heap: "); + Serial.println(usedHeap); + } } void PrintStringSize(String & out) -{ - static uint32_t count = 0; +{ + static uint32_t count = 0; - // Print only when cStr length too large and corrupting memory or every (20 * 5) s - if ( (out.length() >= STRING_SIZE) || (++count > 20) ) - { - Serial.print("\nOut String Length="); - Serial.println(out.length()); + // Print only when cStr length too large and corrupting memory or every (20 * 5) s + if ( (out.length() >= STRING_SIZE) || (++count > 20) ) + { + Serial.print("\nOut String Length="); + Serial.println(out.length()); - count = 0; - } + count = 0; + } } void drawGraph(AsyncWebServerRequest *request) { - String out; + String out; - out.reserve(STRING_SIZE); - char temp[70]; + out.reserve(STRING_SIZE); + char temp[70]; - out += "\n"; - out += "\n"; - out += "\n"; - int y = rand() % 130; + out += "\n"; + out += "\n"; + out += "\n"; + int y = rand() % 130; - for (int x = 10; x < 5000; x += 10) - { - int y2 = rand() % 130; - sprintf(temp, "\n", x, 140 - y, x + 10, 140 - y2); - out += temp; - y = y2; - } - - out += "\n\n"; + for (int x = 10; x < 5000; x += 10) + { + int y2 = rand() % 130; + sprintf(temp, "\n", x, 140 - y, x + 10, 140 - y2); + out += temp; + y = y2; + } - PrintHeapData("Pre Send"); + out += "\n\n"; - PrintStringSize(out); + PrintHeapData("Pre Send"); - request->send(200, "image/svg+xml", out); + PrintStringSize(out); - PrintHeapData("Post Send"); + request->send(200, "image/svg+xml", out); + + PrintHeapData("Post Send"); } void printWifiStatus() { - // print the SSID of the network you're attached to: - Serial.print("SSID: "); - Serial.println(WiFi.SSID()); - - // print your board's IP address: - IPAddress ip = WiFi.localIP(); - Serial.print("Local IP Address: "); - Serial.println(ip); - - // print your board's country code - // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) - uint32_t myCountryCode = cyw43_arch_get_country_code(); - char countryCode[3] = { 0, 0, 0 }; - - countryCode[0] = myCountryCode & 0xFF; - countryCode[1] = (myCountryCode >> 8) & 0xFF; - - Serial.print("Country code: "); Serial.println(countryCode); + // print the SSID of the network you're attached to: + Serial.print("SSID: "); + Serial.println(WiFi.SSID()); + + // print your board's IP address: + IPAddress ip = WiFi.localIP(); + Serial.print("Local IP Address: "); + Serial.println(ip); + + // print your board's country code + // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) + uint32_t myCountryCode = cyw43_arch_get_country_code(); + char countryCode[3] = { 0, 0, 0 }; + + countryCode[0] = myCountryCode & 0xFF; + countryCode[1] = (myCountryCode >> 8) & 0xFF; + + Serial.print("Country code: "); + Serial.println(countryCode); } void setup() { - pinMode(LED_BUILTIN, OUTPUT); - digitalWrite(LED_BUILTIN, LED_OFF); + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, LED_OFF); + + Serial.begin(115200); + + while (!Serial && millis() < 5000); + + delay(200); + + Serial.print("\nStart Async_AdvancedWebServer_MemoryIssues_SendArduinoString on "); + Serial.print(BOARD_NAME); + Serial.print(" with "); + Serial.println(SHIELD_TYPE); + Serial.println(ASYNCTCP_RP2040W_VERSION); + Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); + + /////////////////////////////////// + + // check for the WiFi module: + if (WiFi.status() == WL_NO_MODULE) + { + Serial.println("Communication with WiFi module failed!"); - Serial.begin(115200); - while (!Serial && millis() < 5000); + // don't continue + while (true); + } - delay(200); + Serial.print(F("Connecting to SSID: ")); + Serial.println(ssid); - Serial.print("\nStart Async_AdvancedWebServer_MemoryIssues_SendArduinoString on "); Serial.print(BOARD_NAME); - Serial.print(" with "); Serial.println(SHIELD_TYPE); - Serial.println(ASYNCTCP_RP2040W_VERSION); - Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); + status = WiFi.begin(ssid, pass); - /////////////////////////////////// + delay(1000); - // check for the WiFi module: - if (WiFi.status() == WL_NO_MODULE) - { - Serial.println("Communication with WiFi module failed!"); - // don't continue - while (true); - } + // attempt to connect to WiFi network + while ( status != WL_CONNECTED) + { + delay(500); - Serial.print(F("Connecting to SSID: ")); - Serial.println(ssid); - - status = WiFi.begin(ssid, pass); + // Connect to WPA/WPA2 network + status = WiFi.status(); + } - delay(1000); - - // attempt to connect to WiFi network - while ( status != WL_CONNECTED) - { - delay(500); - - // Connect to WPA/WPA2 network - status = WiFi.status(); - } + printWifiStatus(); - printWifiStatus(); + /////////////////////////////////// - /////////////////////////////////// - - server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) - { - handleRoot(request); - }); + server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) + { + handleRoot(request); + }); - server.on("/test.svg", HTTP_GET, [](AsyncWebServerRequest * request) - { - drawGraph(request); - }); + server.on("/test.svg", HTTP_GET, [](AsyncWebServerRequest * request) + { + drawGraph(request); + }); - server.on("/inline", [](AsyncWebServerRequest * request) - { - request->send(200, "text/plain", "This works as well"); - }); + server.on("/inline", [](AsyncWebServerRequest * request) + { + request->send(200, "text/plain", "This works as well"); + }); - server.onNotFound(handleNotFound); + server.onNotFound(handleNotFound); - server.begin(); + server.begin(); - Serial.print(F("HTTP EthernetWebServer is @ IP : ")); - Serial.println(WiFi.localIP()); + Serial.print(F("HTTP EthernetWebServer is @ IP : ")); + Serial.println(WiFi.localIP()); - PrintHeapData("Pre Create Arduino String"); + PrintHeapData("Pre Create Arduino String"); } void heartBeatPrint() { - static int num = 1; - - Serial.print(F(".")); - - if (num == 80) - { - Serial.println(); - num = 1; - } - else if (num++ % 10 == 0) - { - Serial.print(F(" ")); - } + static int num = 1; + + Serial.print(F(".")); + + if (num == 80) + { + Serial.println(); + num = 1; + } + else if (num++ % 10 == 0) + { + Serial.print(F(" ")); + } } void check_status() { - static unsigned long checkstatus_timeout = 0; + static unsigned long checkstatus_timeout = 0; #define STATUS_CHECK_INTERVAL 10000L - // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change. - if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) - { - heartBeatPrint(); - checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; - } + // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change. + if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) + { + heartBeatPrint(); + checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; + } } void loop() { - check_status(); + check_status(); } diff --git a/examples/Async_AdvancedWebServer_MemoryIssues_Send_CString/Async_AdvancedWebServer_MemoryIssues_Send_CString.ino b/examples/Async_AdvancedWebServer_MemoryIssues_Send_CString/Async_AdvancedWebServer_MemoryIssues_Send_CString.ino index 4fc11bf..3545681 100644 --- a/examples/Async_AdvancedWebServer_MemoryIssues_Send_CString/Async_AdvancedWebServer_MemoryIssues_Send_CString.ino +++ b/examples/Async_AdvancedWebServer_MemoryIssues_Send_CString/Async_AdvancedWebServer_MemoryIssues_Send_CString.ino @@ -2,9 +2,9 @@ Async_AdvancedWebServer_MemoryIssues_Send_CString.ino - Dead simple AsyncWebServer for Portenta_H7 For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -44,7 +44,7 @@ // Check https://github.com/khoih-prog/AsyncWebServer_RP2040W/issues/3#issuecomment-1255676644 #if !( defined(ARDUINO_RASPBERRY_PI_PICO_W) ) - #error For RASPBERRY_PI_PICO_W only + #error For RASPBERRY_PI_PICO_W only #endif #define _RP2040W_AWS_LOGLEVEL_ 1 @@ -79,15 +79,15 @@ char temp[BUFFER_SIZE]; void handleRoot(AsyncWebServerRequest *request) { - digitalWrite(LED_BUILTIN, LED_ON); + digitalWrite(LED_BUILTIN, LED_ON); - int sec = millis() / 1000; - int min = sec / 60; - int hr = min / 60; - int day = hr / 24; + int sec = millis() / 1000; + int min = sec / 60; + int hr = min / 60; + int day = hr / 24; - snprintf(temp, BUFFER_SIZE - 1, - "\ + snprintf(temp, BUFFER_SIZE - 1, + "\ \ \ AsyncWebServer-%s\ @@ -103,234 +103,239 @@ body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Col \ ", BOARD_NAME, BOARD_NAME, day, hr % 24, min % 60, sec % 60); - request->send(200, "text/html", temp); + request->send(200, "text/html", temp); - digitalWrite(LED_BUILTIN, LED_OFF); + digitalWrite(LED_BUILTIN, LED_OFF); } void handleNotFound(AsyncWebServerRequest *request) { - digitalWrite(LED_BUILTIN, LED_ON); - String message = "File Not Found\n\n"; - - message += "URI: "; - message += request->url(); - message += "\nMethod: "; - message += (request->method() == HTTP_GET) ? "GET" : "POST"; - message += "\nArguments: "; - message += request->args(); - message += "\n"; - - for (uint8_t i = 0; i < request->args(); i++) - { - message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; - } - - request->send(404, "text/plain", message); - digitalWrite(LED_BUILTIN, LED_OFF); + digitalWrite(LED_BUILTIN, LED_ON); + String message = "File Not Found\n\n"; + + message += "URI: "; + message += request->url(); + message += "\nMethod: "; + message += (request->method() == HTTP_GET) ? "GET" : "POST"; + message += "\nArguments: "; + message += request->args(); + message += "\n"; + + for (uint8_t i = 0; i < request->args(); i++) + { + message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; + } + + request->send(404, "text/plain", message); + digitalWrite(LED_BUILTIN, LED_OFF); } void PrintHeapData(String hIn) { - // Check https://arduino-pico.readthedocs.io/en/latest/rp2040.html - - static uint32_t maxHeapSize = 0; - - uint32_t usedHeap = rp2040.getUsedHeap(); - uint32_t totalHeap = rp2040.getTotalHeap(); - - // Print and update only when larger heap - if (maxHeapSize < usedHeap) - { - maxHeapSize = usedHeap; - - Serial.print("\nHEAP DATA - "); - Serial.print(hIn); - - Serial.print(" Cur heap: "); - Serial.print(totalHeap); - Serial.print(" Free heap: "); - Serial.print(totalHeap - usedHeap); - Serial.print(" Max heap: "); - Serial.println(usedHeap); - } + // Check https://arduino-pico.readthedocs.io/en/latest/rp2040.html + + static uint32_t maxHeapSize = 0; + + uint32_t usedHeap = rp2040.getUsedHeap(); + uint32_t totalHeap = rp2040.getTotalHeap(); + + // Print and update only when larger heap + if (maxHeapSize < usedHeap) + { + maxHeapSize = usedHeap; + + Serial.print("\nHEAP DATA - "); + Serial.print(hIn); + + Serial.print(" Cur heap: "); + Serial.print(totalHeap); + Serial.print(" Free heap: "); + Serial.print(totalHeap - usedHeap); + Serial.print(" Max heap: "); + Serial.println(usedHeap); + } } void PrintStringSize(const char* cStr) -{ - Serial.print("\nOut String Length="); - Serial.println(strlen(cStr)); +{ + Serial.print("\nOut String Length="); + Serial.println(strlen(cStr)); } -void drawGraph(AsyncWebServerRequest *request) +void drawGraph(AsyncWebServerRequest *request) { - char temp[80]; + char temp[80]; - cStr[0] = '\0'; + cStr[0] = '\0'; - strcat(cStr, "\n"); - strcat(cStr, "\n"); - strcat(cStr, "\n"); - int y = rand() % 130; + strcat(cStr, "\n"); + strcat(cStr, "\n"); + strcat(cStr, "\n"); + int y = rand() % 130; - for (int x = 10; x < 5000; x += 10) - { - int y2 = rand() % 130; - sprintf(temp, "\n", x, 140 - y, x + 10, 140 - y2); - strcat(cStr, temp); - y = y2; - } - - strcat(cStr, "\n\n"); + for (int x = 10; x < 5000; x += 10) + { + int y2 = rand() % 130; + sprintf(temp, "\n", x, 140 - y, x + 10, 140 - y2); + strcat(cStr, temp); + y = y2; + } - PrintHeapData("Pre Send"); + strcat(cStr, "\n\n"); - // Print only when cStr length too large and corrupting memory - if ( (strlen(cStr) >= CSTRING_SIZE)) - { - PrintStringSize(cStr); - } + PrintHeapData("Pre Send"); - request->send(200, "image/svg+xml", cStr, false); + // Print only when cStr length too large and corrupting memory + if ( (strlen(cStr) >= CSTRING_SIZE)) + { + PrintStringSize(cStr); + } - PrintHeapData("Post Send"); + request->send(200, "image/svg+xml", cStr, false); + + PrintHeapData("Post Send"); } void printWifiStatus() { - // print the SSID of the network you're attached to: - Serial.print("SSID: "); - Serial.println(WiFi.SSID()); - - // print your board's IP address: - IPAddress ip = WiFi.localIP(); - Serial.print("Local IP Address: "); - Serial.println(ip); - - // print your board's country code - // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) - uint32_t myCountryCode = cyw43_arch_get_country_code(); - char countryCode[3] = { 0, 0, 0 }; - - countryCode[0] = myCountryCode & 0xFF; - countryCode[1] = (myCountryCode >> 8) & 0xFF; - - Serial.print("Country code: "); Serial.println(countryCode); + // print the SSID of the network you're attached to: + Serial.print("SSID: "); + Serial.println(WiFi.SSID()); + + // print your board's IP address: + IPAddress ip = WiFi.localIP(); + Serial.print("Local IP Address: "); + Serial.println(ip); + + // print your board's country code + // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) + uint32_t myCountryCode = cyw43_arch_get_country_code(); + char countryCode[3] = { 0, 0, 0 }; + + countryCode[0] = myCountryCode & 0xFF; + countryCode[1] = (myCountryCode >> 8) & 0xFF; + + Serial.print("Country code: "); + Serial.println(countryCode); } void setup() { - pinMode(LED_BUILTIN, OUTPUT); - digitalWrite(LED_BUILTIN, LED_OFF); - - Serial.begin(115200); - while (!Serial && millis() < 5000); - - delay(200); - - Serial.print("\nStart Async_AdvancedWebServer_MemoryIssues_Send_CString on "); Serial.print(BOARD_NAME); - Serial.print(" with "); Serial.println(SHIELD_TYPE); - Serial.println(ASYNCTCP_RP2040W_VERSION); - Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); - - cStr = (char *) malloc(CSTRING_SIZE); // make a little larger than required - - if (cStr == NULL) - { - Serial.println("Unable top Allocate RAM"); - - for(;;); - } - - /////////////////////////////////// - - // check for the WiFi module: - if (WiFi.status() == WL_NO_MODULE) - { - Serial.println("Communication with WiFi module failed!"); - // don't continue - while (true); - } - - Serial.print(F("Connecting to SSID: ")); - Serial.println(ssid); - - status = WiFi.begin(ssid, pass); - - delay(1000); - - // attempt to connect to WiFi network - while ( status != WL_CONNECTED) - { - delay(500); - - // Connect to WPA/WPA2 network - status = WiFi.status(); - } - - printWifiStatus(); - - /////////////////////////////////// - - server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) - { - handleRoot(request); - }); - - server.on("/test.svg", HTTP_GET, [](AsyncWebServerRequest * request) - { - drawGraph(request); - }); - - server.on("/inline", [](AsyncWebServerRequest * request) - { - request->send(200, "text/plain", "This works as well"); - }); - - server.onNotFound(handleNotFound); - - server.begin(); - - Serial.print(F("HTTP EthernetWebServer is @ IP : ")); - Serial.println(WiFi.localIP()); - - PrintHeapData("Pre Create Arduino String"); + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, LED_OFF); + + Serial.begin(115200); + + while (!Serial && millis() < 5000); + + delay(200); + + Serial.print("\nStart Async_AdvancedWebServer_MemoryIssues_Send_CString on "); + Serial.print(BOARD_NAME); + Serial.print(" with "); + Serial.println(SHIELD_TYPE); + Serial.println(ASYNCTCP_RP2040W_VERSION); + Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); + + cStr = (char *) malloc(CSTRING_SIZE); // make a little larger than required + + if (cStr == NULL) + { + Serial.println("Unable top Allocate RAM"); + + for (;;); + } + + /////////////////////////////////// + + // check for the WiFi module: + if (WiFi.status() == WL_NO_MODULE) + { + Serial.println("Communication with WiFi module failed!"); + + // don't continue + while (true); + } + + Serial.print(F("Connecting to SSID: ")); + Serial.println(ssid); + + status = WiFi.begin(ssid, pass); + + delay(1000); + + // attempt to connect to WiFi network + while ( status != WL_CONNECTED) + { + delay(500); + + // Connect to WPA/WPA2 network + status = WiFi.status(); + } + + printWifiStatus(); + + /////////////////////////////////// + + server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) + { + handleRoot(request); + }); + + server.on("/test.svg", HTTP_GET, [](AsyncWebServerRequest * request) + { + drawGraph(request); + }); + + server.on("/inline", [](AsyncWebServerRequest * request) + { + request->send(200, "text/plain", "This works as well"); + }); + + server.onNotFound(handleNotFound); + + server.begin(); + + Serial.print(F("HTTP EthernetWebServer is @ IP : ")); + Serial.println(WiFi.localIP()); + + PrintHeapData("Pre Create Arduino String"); } void heartBeatPrint() { - static int num = 1; - - Serial.print(F(".")); - - if (num == 80) - { - //Serial.println(); - PrintStringSize(cStr); - num = 1; - } - else if (num++ % 10 == 0) - { - Serial.print(F(" ")); - } + static int num = 1; + + Serial.print(F(".")); + + if (num == 80) + { + //Serial.println(); + PrintStringSize(cStr); + num = 1; + } + else if (num++ % 10 == 0) + { + Serial.print(F(" ")); + } } void check_status() { - static unsigned long checkstatus_timeout = 0; + static unsigned long checkstatus_timeout = 0; #define STATUS_CHECK_INTERVAL 10000L - // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change. - if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) - { - heartBeatPrint(); - checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; - } + // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change. + if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) + { + heartBeatPrint(); + checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; + } } void loop() { - check_status(); + check_status(); } diff --git a/examples/Async_AdvancedWebServer_SendChunked/Async_AdvancedWebServer_SendChunked.ino b/examples/Async_AdvancedWebServer_SendChunked/Async_AdvancedWebServer_SendChunked.ino new file mode 100644 index 0000000..e169181 --- /dev/null +++ b/examples/Async_AdvancedWebServer_SendChunked/Async_AdvancedWebServer_SendChunked.ino @@ -0,0 +1,314 @@ +/**************************************************************************************************************************** + Async_AdvancedWebServer_SendChunked.ino + + For RP2040W with CYW43439 WiFi + + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi + + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) + Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W + Licensed under GPLv3 license + + Copyright (c) 2015, Majenko Technologies + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + Neither the name of Majenko Technologies nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************************************************************/ + +// See the list of country codes in +// https://github.com/earlephilhower/cyw43-driver/blob/02533c10a018c6550e9f66f7699e21356f5e4609/src/cyw43_country.h#L59-L111 +// To modify https://github.com/earlephilhower/arduino-pico/blob/master/variants/rpipicow/picow_init.cpp +// Check https://github.com/khoih-prog/AsyncWebServer_RP2040W/issues/3#issuecomment-1255676644 + +#if !( defined(ARDUINO_RASPBERRY_PI_PICO_W) ) +#error For RASPBERRY_PI_PICO_W only +#endif + +#define _RP2040W_AWS_LOGLEVEL_ 4 + +/////////////////////////////////////////////////////////////////// + +#include + +/////////////////////////////////////////////////////////////////// +#include + +int status = WL_IDLE_STATUS; + +// In bytes +#define STRING_SIZE 40000 + +AsyncWebServer server(80); + +char ssid[] = "your_ssid"; // your network SSID (name) +char pass[] = "12345678"; // your network password (use for WPA, or use as key for WEP), length must be 8+ + +int reqCount = 0; // number of requests received + +#define LED_OFF LOW +#define LED_ON HIGH + +#define BUFFER_SIZE 512 +char temp[BUFFER_SIZE]; + +void handleRoot(AsyncWebServerRequest *request) +{ + static uint32_t pageCount = 0; + static uint32_t maxfreeHeap = 0; + static uint32_t minFreeHeap = 0xFFFFFFFF; + uint32_t curFreeHeap = rp2040.getFreeHeap(); + + if (maxfreeHeap < curFreeHeap) + maxfreeHeap = curFreeHeap; + + if (minFreeHeap > curFreeHeap) + minFreeHeap = curFreeHeap; + + digitalWrite(LED_BUILTIN, LED_ON); + + int sec = millis() / 1000; + int min = sec / 60; + int hr = min / 60; + int day = hr / 24; + + snprintf(temp, BUFFER_SIZE - 1, + "\ +\ +\ +AsyncWebServer-%s\ +\ +\ +\ +

AsyncWebServer_SendChunked_RP2040W!

\ +

running WiFi on %s

\ +

Uptime: %d d %02d:%02d:%02d, pageCount: %lu

\ +

Heap Free: %lu, Max: %lu, Min: %lu

\ +\ +\ +", BOARD_NAME, BOARD_NAME, day, hr % 24, min % 60, sec % 60, ++pageCount, curFreeHeap, maxfreeHeap, minFreeHeap); + + request->send(200, "text/html", temp); + + digitalWrite(LED_BUILTIN, LED_OFF); +} + +void handleNotFound(AsyncWebServerRequest *request) +{ + digitalWrite(LED_BUILTIN, LED_ON); + String message = "File Not Found\n\n"; + + message += "URI: "; + message += request->url(); + message += "\nMethod: "; + message += (request->method() == HTTP_GET) ? "GET" : "POST"; + message += "\nArguments: "; + message += request->args(); + message += "\n"; + + for (uint8_t i = 0; i < request->args(); i++) + { + message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; + } + + request->send(404, "text/plain", message); + digitalWrite(LED_BUILTIN, LED_OFF); +} + +String out; + +void drawGraph(AsyncWebServerRequest *request) +{ + out.reserve(STRING_SIZE); + char temp[70]; + + out = String(); + + digitalWrite(LED_BUILTIN, LED_ON); + + out += "\n"; + out += "\n"; + out += "\n"; + int y = rand() % 130; + + for (int x = 10; x < 5000; x += 10) + { + int y2 = rand() % 130; + sprintf(temp, "\n", x, 140 - y, x + 10, 140 - y2); + out += temp; + y = y2; + } + + out += "\n\n"; + + AWS_LOGDEBUG1("Total length to send in chunks =", out.length()); + + AsyncWebServerResponse *response = request->beginChunkedResponse("image/svg+xml", [](uint8_t *buffer, size_t maxLen, size_t filledLength) -> size_t + { + size_t len = min(maxLen, out.length() - filledLength); + memcpy(buffer, out.c_str() + filledLength, len); + + AWS_LOGDEBUG1("Bytes sent in chunk =", len); + + return len; + }); + + request->send(response); + + digitalWrite(LED_BUILTIN, LED_OFF); +} + +void printWifiStatus() +{ + // print the SSID of the network you're attached to: + Serial.print("SSID: "); + Serial.println(WiFi.SSID()); + + // print your board's IP address: + IPAddress ip = WiFi.localIP(); + Serial.print("Local IP Address: "); + Serial.println(ip); + + // print your board's country code + // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) + uint32_t myCountryCode = cyw43_arch_get_country_code(); + char countryCode[3] = { 0, 0, 0 }; + + countryCode[0] = myCountryCode & 0xFF; + countryCode[1] = (myCountryCode >> 8) & 0xFF; + + Serial.print("Country code: "); + Serial.println(countryCode); +} + +void setup() +{ + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, LED_OFF); + + Serial.begin(115200); + + while (!Serial && millis() < 5000); + + delay(200); + + Serial.print("\nStart Async_AdvancedWebServer_SendChunked on "); + Serial.print(BOARD_NAME); + Serial.print(" with "); + Serial.println(SHIELD_TYPE); + Serial.println(ASYNCTCP_RP2040W_VERSION); + Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); + + /////////////////////////////////// + + // check for the WiFi module: + if (WiFi.status() == WL_NO_MODULE) + { + Serial.println("Communication with WiFi module failed!"); + + // don't continue + while (true); + } + + Serial.print(F("Connecting to SSID: ")); + Serial.println(ssid); + + status = WiFi.begin(ssid, pass); + + delay(1000); + + // attempt to connect to WiFi network + while ( status != WL_CONNECTED) + { + delay(500); + + // Connect to WPA/WPA2 network + status = WiFi.status(); + } + + printWifiStatus(); + + /////////////////////////////////// + + server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) + { + handleRoot(request); + }); + + server.on("/test.svg", HTTP_GET, [](AsyncWebServerRequest * request) + { + drawGraph(request); + }); + + server.on("/inline", [](AsyncWebServerRequest * request) + { + request->send(200, "text/plain", "This works as well"); + }); + + server.onNotFound(handleNotFound); + + server.begin(); + + Serial.print(F("AsyncWebServer is @ IP : ")); + Serial.println(WiFi.localIP()); +} + +void heartBeatPrint() +{ + static int num = 1; + + Serial.print(F(".")); + + if (num == 80) + { + Serial.println(); + num = 1; + } + else if (num++ % 10 == 0) + { + Serial.print(F(" ")); + } +} + +void check_status() +{ + static unsigned long checkstatus_timeout = 0; + +#define STATUS_CHECK_INTERVAL 10000L + + // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change. + if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) + { + heartBeatPrint(); + checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; + } +} + +void loop() +{ + check_status(); +} diff --git a/examples/Async_AdvancedWebServer_favicon/Async_AdvancedWebServer_favicon.ino b/examples/Async_AdvancedWebServer_favicon/Async_AdvancedWebServer_favicon.ino index 5116620..8d9a4b1 100644 --- a/examples/Async_AdvancedWebServer_favicon/Async_AdvancedWebServer_favicon.ino +++ b/examples/Async_AdvancedWebServer_favicon/Async_AdvancedWebServer_favicon.ino @@ -2,9 +2,9 @@ Async_AdvancedWebServer.ino For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -44,7 +44,7 @@ // Check https://github.com/khoih-prog/AsyncWebServer_RP2040W/issues/3#issuecomment-1255676644 #if !( defined(ARDUINO_RASPBERRY_PI_PICO_W) ) - #error For RASPBERRY_PI_PICO_W only + #error For RASPBERRY_PI_PICO_W only #endif #define _RP2040W_AWS_LOGLEVEL_ 1 @@ -75,15 +75,15 @@ char temp[BUFFER_SIZE]; void handleRoot(AsyncWebServerRequest *request) { - digitalWrite(LED_BUILTIN, LED_ON); + digitalWrite(LED_BUILTIN, LED_ON); - int sec = millis() / 1000; - int min = sec / 60; - int hr = min / 60; - int day = hr / 24; + int sec = millis() / 1000; + int min = sec / 60; + int hr = min / 60; + int day = hr / 24; - snprintf(temp, BUFFER_SIZE - 1, - "\ + snprintf(temp, BUFFER_SIZE - 1, + "\ \ \ AsyncWebServer-%s\ @@ -99,197 +99,203 @@ body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Col \ ", BOARD_NAME, BOARD_NAME, day, hr % 24, min % 60, sec % 60); - request->send(200, "text/html", temp); + request->send(200, "text/html", temp); - digitalWrite(LED_BUILTIN, LED_OFF); + digitalWrite(LED_BUILTIN, LED_OFF); } void drawFavicon(AsyncWebServerRequest *request) { - AsyncWebServerResponse *response = request->beginResponse(200, "image/x-icon", favicon_ico_gz, favicon_ico_gz_len); - - response->addHeader("Content-Encoding", "gzip"); - request->send(response); + AsyncWebServerResponse *response = request->beginResponse(200, "image/x-icon", favicon_ico_gz, favicon_ico_gz_len); + + response->addHeader("Content-Encoding", "gzip"); + request->send(response); } void handleNotFound(AsyncWebServerRequest *request) { - digitalWrite(LED_BUILTIN, LED_ON); - String message = "File Not Found\n\n"; - - message += "URI: "; - message += request->url(); - message += "\nMethod: "; - message += (request->method() == HTTP_GET) ? "GET" : "POST"; - message += "\nArguments: "; - message += request->args(); - message += "\n"; - - for (uint8_t i = 0; i < request->args(); i++) - { - message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; - } - - request->send(404, "text/plain", message); - digitalWrite(LED_BUILTIN, LED_OFF); + digitalWrite(LED_BUILTIN, LED_ON); + String message = "File Not Found\n\n"; + + message += "URI: "; + message += request->url(); + message += "\nMethod: "; + message += (request->method() == HTTP_GET) ? "GET" : "POST"; + message += "\nArguments: "; + message += request->args(); + message += "\n"; + + for (uint8_t i = 0; i < request->args(); i++) + { + message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; + } + + request->send(404, "text/plain", message); + digitalWrite(LED_BUILTIN, LED_OFF); } void drawGraph(AsyncWebServerRequest *request) { - String out; + String out; + + out.reserve(4000); + char temp[70]; - out.reserve(4000); - char temp[70]; + digitalWrite(LED_BUILTIN, LED_ON); - digitalWrite(LED_BUILTIN, LED_ON); + out += "\n"; + out += "\n"; + out += "\n"; + int y = rand() % 130; - out += "\n"; - out += "\n"; - out += "\n"; - int y = rand() % 130; + for (int x = 10; x < 300; x += 10) + { + int y2 = rand() % 130; + sprintf(temp, "\n", x, 140 - y, x + 10, 140 - y2); + out += temp; + y = y2; + } - for (int x = 10; x < 300; x += 10) - { - int y2 = rand() % 130; - sprintf(temp, "\n", x, 140 - y, x + 10, 140 - y2); - out += temp; - y = y2; - } - out += "\n\n"; + out += "\n\n"; - request->send(200, "image/svg+xml", out); + request->send(200, "image/svg+xml", out); - digitalWrite(LED_BUILTIN, LED_OFF); + digitalWrite(LED_BUILTIN, LED_OFF); } void printWifiStatus() { - // print the SSID of the network you're attached to: - Serial.print("SSID: "); - Serial.println(WiFi.SSID()); - - // print your board's IP address: - IPAddress ip = WiFi.localIP(); - Serial.print("Local IP Address: "); - Serial.println(ip); - - // print your board's country code - // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) - uint32_t myCountryCode = cyw43_arch_get_country_code(); - char countryCode[3] = { 0, 0, 0 }; - - countryCode[0] = myCountryCode & 0xFF; - countryCode[1] = (myCountryCode >> 8) & 0xFF; - - Serial.print("Country code: "); Serial.println(countryCode); + // print the SSID of the network you're attached to: + Serial.print("SSID: "); + Serial.println(WiFi.SSID()); + + // print your board's IP address: + IPAddress ip = WiFi.localIP(); + Serial.print("Local IP Address: "); + Serial.println(ip); + + // print your board's country code + // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) + uint32_t myCountryCode = cyw43_arch_get_country_code(); + char countryCode[3] = { 0, 0, 0 }; + + countryCode[0] = myCountryCode & 0xFF; + countryCode[1] = (myCountryCode >> 8) & 0xFF; + + Serial.print("Country code: "); + Serial.println(countryCode); } void setup() { - pinMode(LED_BUILTIN, OUTPUT); - digitalWrite(LED_BUILTIN, LED_OFF); - - Serial.begin(115200); - while (!Serial && millis() < 5000); - - delay(200); - - Serial.print("\nStart Async_AdvancedWebServer_favicon on "); Serial.print(BOARD_NAME); - Serial.print(" with "); Serial.println(SHIELD_TYPE); - Serial.println(ASYNCTCP_RP2040W_VERSION); - Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); - - /////////////////////////////////// - - // check for the WiFi module: - if (WiFi.status() == WL_NO_MODULE) - { - Serial.println("Communication with WiFi module failed!"); - // don't continue - while (true); - } - - Serial.print(F("Connecting to SSID: ")); - Serial.println(ssid); - - status = WiFi.begin(ssid, pass); - - delay(1000); - - // attempt to connect to WiFi network - while ( status != WL_CONNECTED) - { - delay(500); - - // Connect to WPA/WPA2 network - status = WiFi.status(); - } - - printWifiStatus(); - - /////////////////////////////////// - - - server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) - { - handleRoot(request); - }); - - server.on("/test.svg", HTTP_GET, [](AsyncWebServerRequest * request) - { - drawGraph(request); - }); - - server.on("/favicon.ico", HTTP_GET, [](AsyncWebServerRequest * request) - { - drawFavicon(request); - }); - - server.on("/inline", [](AsyncWebServerRequest * request) - { - request->send(200, "text/plain", "This works as well"); - }); - - server.onNotFound(handleNotFound); - - server.begin(); - - Serial.print(F("HTTP EthernetWebServer is @ IP : ")); - Serial.println(WiFi.localIP()); + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, LED_OFF); + + Serial.begin(115200); + + while (!Serial && millis() < 5000); + + delay(200); + + Serial.print("\nStart Async_AdvancedWebServer_favicon on "); + Serial.print(BOARD_NAME); + Serial.print(" with "); + Serial.println(SHIELD_TYPE); + Serial.println(ASYNCTCP_RP2040W_VERSION); + Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); + + /////////////////////////////////// + + // check for the WiFi module: + if (WiFi.status() == WL_NO_MODULE) + { + Serial.println("Communication with WiFi module failed!"); + + // don't continue + while (true); + } + + Serial.print(F("Connecting to SSID: ")); + Serial.println(ssid); + + status = WiFi.begin(ssid, pass); + + delay(1000); + + // attempt to connect to WiFi network + while ( status != WL_CONNECTED) + { + delay(500); + + // Connect to WPA/WPA2 network + status = WiFi.status(); + } + + printWifiStatus(); + + /////////////////////////////////// + + + server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) + { + handleRoot(request); + }); + + server.on("/test.svg", HTTP_GET, [](AsyncWebServerRequest * request) + { + drawGraph(request); + }); + + server.on("/favicon.ico", HTTP_GET, [](AsyncWebServerRequest * request) + { + drawFavicon(request); + }); + + server.on("/inline", [](AsyncWebServerRequest * request) + { + request->send(200, "text/plain", "This works as well"); + }); + + server.onNotFound(handleNotFound); + + server.begin(); + + Serial.print(F("HTTP EthernetWebServer is @ IP : ")); + Serial.println(WiFi.localIP()); } void heartBeatPrint() { - static int num = 1; - - Serial.print(F(".")); - - if (num == 80) - { - Serial.println(); - num = 1; - } - else if (num++ % 10 == 0) - { - Serial.print(F(" ")); - } + static int num = 1; + + Serial.print(F(".")); + + if (num == 80) + { + Serial.println(); + num = 1; + } + else if (num++ % 10 == 0) + { + Serial.print(F(" ")); + } } void check_status() { - static unsigned long checkstatus_timeout = 0; + static unsigned long checkstatus_timeout = 0; #define STATUS_CHECK_INTERVAL 60000L - // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change. - if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) - { - heartBeatPrint(); - checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; - } + // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change. + if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) + { + heartBeatPrint(); + checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; + } } void loop() { - check_status(); + check_status(); } diff --git a/examples/Async_AdvancedWebServer_favicon/favicon.h b/examples/Async_AdvancedWebServer_favicon/favicon.h index 9cc2262..8aca796 100644 --- a/examples/Async_AdvancedWebServer_favicon/favicon.h +++ b/examples/Async_AdvancedWebServer_favicon/favicon.h @@ -16,14 +16,14 @@ /* - Provided from me-no-dev Async WebServer for ESP8266 - https://github.com/me-no-dev/ESPAsyncWebServer + Provided from me-no-dev Async WebServer for ESP8266 + https://github.com/me-no-dev/ESPAsyncWebServer */ //File: favicon.ico.gz, Size: 726 #define favicon_ico_gz_len 726 -const uint8_t favicon_ico_gz[] /*PROGMEM*/ = +const uint8_t favicon_ico_gz[] /*PROGMEM*/ = { 0x1F, 0x8B, 0x08, 0x08, 0x0B, 0x87, 0x90, 0x57, 0x00, 0x03, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6F, 0x6E, 0x2E, 0x69, 0x63, 0x6F, 0x00, 0xCD, 0x53, 0x5F, 0x48, 0x9A, 0x51, 0x14, 0xBF, 0x62, 0x6D, diff --git a/examples/Async_HelloServer/Async_HelloServer.ino b/examples/Async_HelloServer/Async_HelloServer.ino index 21445eb..b7a9f79 100644 --- a/examples/Async_HelloServer/Async_HelloServer.ino +++ b/examples/Async_HelloServer/Async_HelloServer.ino @@ -1,10 +1,10 @@ /**************************************************************************************************************************** Async_HelloServer.h - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -16,7 +16,7 @@ // Check https://github.com/khoih-prog/AsyncWebServer_RP2040W/issues/3#issuecomment-1255676644 #if !( defined(ARDUINO_RASPBERRY_PI_PICO_W) ) - #error For RASPBERRY_PI_PICO_W only + #error For RASPBERRY_PI_PICO_W only #endif #define _RP2040W_AWS_LOGLEVEL_ 1 @@ -44,156 +44,161 @@ char temp[BUFFER_SIZE]; void handleRoot(AsyncWebServerRequest *request) { - digitalWrite(LED_BUILTIN, LED_ON); + digitalWrite(LED_BUILTIN, LED_ON); + + snprintf(temp, BUFFER_SIZE - 1, "Hello from Async_HelloServer on %s\n", BOARD_NAME); - snprintf(temp, BUFFER_SIZE - 1, "Hello from Async_HelloServer on %s\n", BOARD_NAME); + request->send(200, "text/plain", temp); - request->send(200, "text/plain", temp); - - digitalWrite(LED_BUILTIN, LED_OFF); + digitalWrite(LED_BUILTIN, LED_OFF); } void handleNotFound(AsyncWebServerRequest *request) { - digitalWrite(LED_BUILTIN, LED_ON); - - String message = "File Not Found\n\n"; - - message += "URI: "; - //message += server.uri(); - message += request->url(); - message += "\nMethod: "; - message += (request->method() == HTTP_GET) ? "GET" : "POST"; - message += "\nArguments: "; - message += request->args(); - message += "\n"; - - for (uint8_t i = 0; i < request->args(); i++) - { - message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; - } - - request->send(404, "text/plain", message); - digitalWrite(LED_BUILTIN, LED_OFF); + digitalWrite(LED_BUILTIN, LED_ON); + + String message = "File Not Found\n\n"; + + message += "URI: "; + //message += server.uri(); + message += request->url(); + message += "\nMethod: "; + message += (request->method() == HTTP_GET) ? "GET" : "POST"; + message += "\nArguments: "; + message += request->args(); + message += "\n"; + + for (uint8_t i = 0; i < request->args(); i++) + { + message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; + } + + request->send(404, "text/plain", message); + digitalWrite(LED_BUILTIN, LED_OFF); } void printWifiStatus() { - // print the SSID of the network you're attached to: - Serial.print("SSID: "); - Serial.println(WiFi.SSID()); - - // print your board's IP address: - IPAddress ip = WiFi.localIP(); - Serial.print("Local IP Address: "); - Serial.println(ip); - - // print your board's country code - // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) - uint32_t myCountryCode = cyw43_arch_get_country_code(); - char countryCode[3] = { 0, 0, 0 }; - - countryCode[0] = myCountryCode & 0xFF; - countryCode[1] = (myCountryCode >> 8) & 0xFF; - - Serial.print("Country code: "); Serial.println(countryCode); + // print the SSID of the network you're attached to: + Serial.print("SSID: "); + Serial.println(WiFi.SSID()); + + // print your board's IP address: + IPAddress ip = WiFi.localIP(); + Serial.print("Local IP Address: "); + Serial.println(ip); + + // print your board's country code + // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) + uint32_t myCountryCode = cyw43_arch_get_country_code(); + char countryCode[3] = { 0, 0, 0 }; + + countryCode[0] = myCountryCode & 0xFF; + countryCode[1] = (myCountryCode >> 8) & 0xFF; + + Serial.print("Country code: "); + Serial.println(countryCode); } void setup() { - pinMode(LED_BUILTIN, OUTPUT); - digitalWrite(LED_BUILTIN, LED_OFF); - - Serial.begin(115200); - while (!Serial && millis() < 5000); - - delay(200); - - Serial.print("\nStart Async_HelloServer on "); Serial.print(BOARD_NAME); - Serial.print(" with "); Serial.println(SHIELD_TYPE); - Serial.println(ASYNCTCP_RP2040W_VERSION); - Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); - - /////////////////////////////////// - - // check for the WiFi module: - if (WiFi.status() == WL_NO_MODULE) - { - Serial.println("Communication with WiFi module failed!"); - // don't continue - while (true); - } - - Serial.print(F("Connecting to SSID: ")); - Serial.println(ssid); - - status = WiFi.begin(ssid, pass); - - delay(1000); - - // attempt to connect to WiFi network - while ( status != WL_CONNECTED) - { - delay(500); - - // Connect to WPA/WPA2 network - status = WiFi.status(); - } - - printWifiStatus(); - - /////////////////////////////////// - - server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) - { - handleRoot(request); - }); - - server.on("/inline", [](AsyncWebServerRequest * request) - { - request->send(200, "text/plain", "This works as well"); - }); - - server.onNotFound(handleNotFound); - - server.begin(); - - Serial.print(F("HTTP EthernetWebServer is @ IP : ")); - Serial.println(WiFi.localIP()); + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, LED_OFF); + + Serial.begin(115200); + + while (!Serial && millis() < 5000); + + delay(200); + + Serial.print("\nStart Async_HelloServer on "); + Serial.print(BOARD_NAME); + Serial.print(" with "); + Serial.println(SHIELD_TYPE); + Serial.println(ASYNCTCP_RP2040W_VERSION); + Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); + + /////////////////////////////////// + + // check for the WiFi module: + if (WiFi.status() == WL_NO_MODULE) + { + Serial.println("Communication with WiFi module failed!"); + + // don't continue + while (true); + } + + Serial.print(F("Connecting to SSID: ")); + Serial.println(ssid); + + status = WiFi.begin(ssid, pass); + + delay(1000); + + // attempt to connect to WiFi network + while ( status != WL_CONNECTED) + { + delay(500); + + // Connect to WPA/WPA2 network + status = WiFi.status(); + } + + printWifiStatus(); + + /////////////////////////////////// + + server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) + { + handleRoot(request); + }); + + server.on("/inline", [](AsyncWebServerRequest * request) + { + request->send(200, "text/plain", "This works as well"); + }); + + server.onNotFound(handleNotFound); + + server.begin(); + + Serial.print(F("HTTP EthernetWebServer is @ IP : ")); + Serial.println(WiFi.localIP()); } void heartBeatPrint() { - static int num = 1; - - Serial.print(F(".")); - - if (num == 80) - { - Serial.println(); - num = 1; - } - else if (num++ % 10 == 0) - { - Serial.print(F(" ")); - } + static int num = 1; + + Serial.print(F(".")); + + if (num == 80) + { + Serial.println(); + num = 1; + } + else if (num++ % 10 == 0) + { + Serial.print(F(" ")); + } } void check_status() { - static unsigned long checkstatus_timeout = 0; + static unsigned long checkstatus_timeout = 0; #define STATUS_CHECK_INTERVAL 10000L - // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change. - if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) - { - heartBeatPrint(); - checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; - } + // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change. + if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) + { + heartBeatPrint(); + checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; + } } void loop() { - check_status(); + check_status(); } diff --git a/examples/Async_HelloServer2/Async_HelloServer2.ino b/examples/Async_HelloServer2/Async_HelloServer2.ino index 249bfdf..4e293eb 100644 --- a/examples/Async_HelloServer2/Async_HelloServer2.ino +++ b/examples/Async_HelloServer2/Async_HelloServer2.ino @@ -1,10 +1,10 @@ /**************************************************************************************************************************** Async_HelloServer.ino - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -16,7 +16,7 @@ // Check https://github.com/khoih-prog/AsyncWebServer_RP2040W/issues/3#issuecomment-1255676644 #if !( defined(ARDUINO_RASPBERRY_PI_PICO_W) ) - #error For RASPBERRY_PI_PICO_W only + #error For RASPBERRY_PI_PICO_W only #endif #define _RP2040W_AWS_LOGLEVEL_ 1 @@ -46,177 +46,182 @@ char temp[BUFFER_SIZE]; void handleRoot(AsyncWebServerRequest *request) { - digitalWrite(LED_BUILTIN, LED_ON); + digitalWrite(LED_BUILTIN, LED_ON); + + snprintf(temp, BUFFER_SIZE - 1, "Hello from Async_HelloServer2 on %s\n", BOARD_NAME); + request->send(200, "text/plain", temp); - snprintf(temp, BUFFER_SIZE - 1, "Hello from Async_HelloServer2 on %s\n", BOARD_NAME); - request->send(200, "text/plain", temp); - - digitalWrite(LED_BUILTIN, LED_OFF); + digitalWrite(LED_BUILTIN, LED_OFF); } void handleNotFound(AsyncWebServerRequest *request) { - digitalWrite(LED_BUILTIN, LED_ON); - String message = "File Not Found\n\n"; - - message += "URI: "; - //message += server.uri(); - message += request->url(); - message += "\nMethod: "; - message += (request->method() == HTTP_GET) ? "GET" : "POST"; - message += "\nArguments: "; - message += request->args(); - message += "\n"; - - for (uint8_t i = 0; i < request->args(); i++) - { - message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; - } - - request->send(404, "text/plain", message); - digitalWrite(LED_BUILTIN, LED_OFF); + digitalWrite(LED_BUILTIN, LED_ON); + String message = "File Not Found\n\n"; + + message += "URI: "; + //message += server.uri(); + message += request->url(); + message += "\nMethod: "; + message += (request->method() == HTTP_GET) ? "GET" : "POST"; + message += "\nArguments: "; + message += request->args(); + message += "\n"; + + for (uint8_t i = 0; i < request->args(); i++) + { + message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; + } + + request->send(404, "text/plain", message); + digitalWrite(LED_BUILTIN, LED_OFF); } void printWifiStatus() { - // print the SSID of the network you're attached to: - Serial.print("SSID: "); - Serial.println(WiFi.SSID()); - - // print your board's IP address: - IPAddress ip = WiFi.localIP(); - Serial.print("Local IP Address: "); - Serial.println(ip); - - // print your board's country code - // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) - uint32_t myCountryCode = cyw43_arch_get_country_code(); - char countryCode[3] = { 0, 0, 0 }; - - countryCode[0] = myCountryCode & 0xFF; - countryCode[1] = (myCountryCode >> 8) & 0xFF; - - Serial.print("Country code: "); Serial.println(countryCode); + // print the SSID of the network you're attached to: + Serial.print("SSID: "); + Serial.println(WiFi.SSID()); + + // print your board's IP address: + IPAddress ip = WiFi.localIP(); + Serial.print("Local IP Address: "); + Serial.println(ip); + + // print your board's country code + // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) + uint32_t myCountryCode = cyw43_arch_get_country_code(); + char countryCode[3] = { 0, 0, 0 }; + + countryCode[0] = myCountryCode & 0xFF; + countryCode[1] = (myCountryCode >> 8) & 0xFF; + + Serial.print("Country code: "); + Serial.println(countryCode); } void setup() { - pinMode(LED_BUILTIN, OUTPUT); - digitalWrite(LED_BUILTIN, LED_OFF); - - Serial.begin(115200); - while (!Serial && millis() < 5000); - - delay(200); - - Serial.print("\nStart Async_HelloServer2 on "); Serial.print(BOARD_NAME); - Serial.print(" with "); Serial.println(SHIELD_TYPE); - Serial.println(ASYNCTCP_RP2040W_VERSION); - Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); - - /////////////////////////////////// - - // check for the WiFi module: - if (WiFi.status() == WL_NO_MODULE) - { - Serial.println("Communication with WiFi module failed!"); - // don't continue - while (true); - } - - Serial.print(F("Connecting to SSID: ")); - Serial.println(ssid); - - status = WiFi.begin(ssid, pass); - - delay(1000); - - // attempt to connect to WiFi network - while ( status != WL_CONNECTED) - { - delay(500); - - // Connect to WPA/WPA2 network - status = WiFi.status(); - } - - printWifiStatus(); - - /////////////////////////////////// - - server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) - { - handleRoot(request); - }); - - server.on("/inline", [](AsyncWebServerRequest * request) - { - request->send(200, "text/plain", "This works as well"); - }); - - server.on("/gif", [](AsyncWebServerRequest * request) - { - static const uint8_t gif[] = - { - 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x10, 0x00, 0x10, 0x00, 0x80, 0x01, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x2c, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x10, 0x00, 0x00, 0x02, 0x19, 0x8c, 0x8f, 0xa9, 0xcb, 0x9d, - 0x00, 0x5f, 0x74, 0xb4, 0x56, 0xb0, 0xb0, 0xd2, 0xf2, 0x35, 0x1e, 0x4c, - 0x0c, 0x24, 0x5a, 0xe6, 0x89, 0xa6, 0x4d, 0x01, 0x00, 0x3b - }; - - char gif_colored[sizeof(gif)]; - - memcpy(gif_colored, gif, sizeof(gif)); - - // Set the background to a random set of colors - gif_colored[16] = millis() % 256; - gif_colored[17] = millis() % 256; - gif_colored[18] = millis() % 256; - - request->send(200, (char *) "image/gif", gif_colored); - }); - - server.onNotFound(handleNotFound); - - server.begin(); - - Serial.print("HTTP Async_HelloServer2 started @ IP : "); - Serial.println(WiFi.localIP()); + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, LED_OFF); + + Serial.begin(115200); + + while (!Serial && millis() < 5000); + + delay(200); + + Serial.print("\nStart Async_HelloServer2 on "); + Serial.print(BOARD_NAME); + Serial.print(" with "); + Serial.println(SHIELD_TYPE); + Serial.println(ASYNCTCP_RP2040W_VERSION); + Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); + + /////////////////////////////////// + + // check for the WiFi module: + if (WiFi.status() == WL_NO_MODULE) + { + Serial.println("Communication with WiFi module failed!"); + + // don't continue + while (true); + } + + Serial.print(F("Connecting to SSID: ")); + Serial.println(ssid); + + status = WiFi.begin(ssid, pass); + + delay(1000); + + // attempt to connect to WiFi network + while ( status != WL_CONNECTED) + { + delay(500); + + // Connect to WPA/WPA2 network + status = WiFi.status(); + } + + printWifiStatus(); + + /////////////////////////////////// + + server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) + { + handleRoot(request); + }); + + server.on("/inline", [](AsyncWebServerRequest * request) + { + request->send(200, "text/plain", "This works as well"); + }); + + server.on("/gif", [](AsyncWebServerRequest * request) + { + static const uint8_t gif[] = + { + 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x10, 0x00, 0x10, 0x00, 0x80, 0x01, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x2c, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x10, 0x00, 0x00, 0x02, 0x19, 0x8c, 0x8f, 0xa9, 0xcb, 0x9d, + 0x00, 0x5f, 0x74, 0xb4, 0x56, 0xb0, 0xb0, 0xd2, 0xf2, 0x35, 0x1e, 0x4c, + 0x0c, 0x24, 0x5a, 0xe6, 0x89, 0xa6, 0x4d, 0x01, 0x00, 0x3b + }; + + char gif_colored[sizeof(gif)]; + + memcpy(gif_colored, gif, sizeof(gif)); + + // Set the background to a random set of colors + gif_colored[16] = millis() % 256; + gif_colored[17] = millis() % 256; + gif_colored[18] = millis() % 256; + + request->send(200, (char *) "image/gif", gif_colored); + }); + + server.onNotFound(handleNotFound); + + server.begin(); + + Serial.print("HTTP Async_HelloServer2 started @ IP : "); + Serial.println(WiFi.localIP()); } void heartBeatPrint() { - static int num = 1; - - Serial.print(F(".")); - - if (num == 80) - { - Serial.println(); - num = 1; - } - else if (num++ % 10 == 0) - { - Serial.print(F(" ")); - } + static int num = 1; + + Serial.print(F(".")); + + if (num == 80) + { + Serial.println(); + num = 1; + } + else if (num++ % 10 == 0) + { + Serial.print(F(" ")); + } } void check_status() { - static unsigned long checkstatus_timeout = 0; + static unsigned long checkstatus_timeout = 0; #define STATUS_CHECK_INTERVAL 10000L - // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change. - if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) - { - heartBeatPrint(); - checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; - } + // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change. + if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) + { + heartBeatPrint(); + checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; + } } void loop() { - check_status(); + check_status(); } diff --git a/examples/Async_HttpBasicAuth/Async_HttpBasicAuth.ino b/examples/Async_HttpBasicAuth/Async_HttpBasicAuth.ino index e808dad..8a1f1d3 100644 --- a/examples/Async_HttpBasicAuth/Async_HttpBasicAuth.ino +++ b/examples/Async_HttpBasicAuth/Async_HttpBasicAuth.ino @@ -1,10 +1,10 @@ /**************************************************************************************************************************** Async_HttpBasicAuth.ino - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -16,7 +16,7 @@ // Check https://github.com/khoih-prog/AsyncWebServer_RP2040W/issues/3#issuecomment-1255676644 #if !( defined(ARDUINO_RASPBERRY_PI_PICO_W) ) - #error For RASPBERRY_PI_PICO_W only + #error For RASPBERRY_PI_PICO_W only #endif #define _RP2040W_AWS_LOGLEVEL_ 1 @@ -41,125 +41,130 @@ const char* www_password = "rp2040w"; void printWifiStatus() { - // print the SSID of the network you're attached to: - Serial.print("SSID: "); - Serial.println(WiFi.SSID()); - - // print your board's IP address: - IPAddress ip = WiFi.localIP(); - Serial.print("Local IP Address: "); - Serial.println(ip); - - // print your board's country code - // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) - uint32_t myCountryCode = cyw43_arch_get_country_code(); - char countryCode[3] = { 0, 0, 0 }; - - countryCode[0] = myCountryCode & 0xFF; - countryCode[1] = (myCountryCode >> 8) & 0xFF; - - Serial.print("Country code: "); Serial.println(countryCode); + // print the SSID of the network you're attached to: + Serial.print("SSID: "); + Serial.println(WiFi.SSID()); + + // print your board's IP address: + IPAddress ip = WiFi.localIP(); + Serial.print("Local IP Address: "); + Serial.println(ip); + + // print your board's country code + // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) + uint32_t myCountryCode = cyw43_arch_get_country_code(); + char countryCode[3] = { 0, 0, 0 }; + + countryCode[0] = myCountryCode & 0xFF; + countryCode[1] = (myCountryCode >> 8) & 0xFF; + + Serial.print("Country code: "); + Serial.println(countryCode); } void setup() { - Serial.begin(115200); - while (!Serial && millis() < 5000); - - delay(200); - - Serial.print("\nStart Async_HTTPBasicAuth on "); Serial.print(BOARD_NAME); - Serial.print(" with "); Serial.println(SHIELD_TYPE); - Serial.println(ASYNCTCP_RP2040W_VERSION); - Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); - - /////////////////////////////////// - - // check for the WiFi module: - if (WiFi.status() == WL_NO_MODULE) - { - Serial.println("Communication with WiFi module failed!"); - // don't continue - while (true); - } - - Serial.print(F("Connecting to SSID: ")); - Serial.println(ssid); - - status = WiFi.begin(ssid, pass); - - delay(1000); - - // attempt to connect to WiFi network - while ( status != WL_CONNECTED) - { - delay(500); - - // Connect to WPA/WPA2 network - status = WiFi.status(); - } - - printWifiStatus(); - - /////////////////////////////////// - - server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) - { - if (!request->authenticate(www_username, www_password)) - { - return request->requestAuthentication(); - } - - request->send(200, "text/plain", "Login OK"); - }); - - server.begin(); - - Serial.print(F("Async_HttpBasicAuth started @ IP : ")); - Serial.println(WiFi.localIP()); - - Serial.print(F("Open http://")); - Serial.print(WiFi.localIP()); - Serial.println(F("/ in your browser to see it working")); - - Serial.print(F("Login using username = ")); - Serial.print(www_username); - Serial.print(F(" and password = ")); - Serial.println(www_password); + Serial.begin(115200); + + while (!Serial && millis() < 5000); + + delay(200); + + Serial.print("\nStart Async_HTTPBasicAuth on "); + Serial.print(BOARD_NAME); + Serial.print(" with "); + Serial.println(SHIELD_TYPE); + Serial.println(ASYNCTCP_RP2040W_VERSION); + Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); + + /////////////////////////////////// + + // check for the WiFi module: + if (WiFi.status() == WL_NO_MODULE) + { + Serial.println("Communication with WiFi module failed!"); + + // don't continue + while (true); + } + + Serial.print(F("Connecting to SSID: ")); + Serial.println(ssid); + + status = WiFi.begin(ssid, pass); + + delay(1000); + + // attempt to connect to WiFi network + while ( status != WL_CONNECTED) + { + delay(500); + + // Connect to WPA/WPA2 network + status = WiFi.status(); + } + + printWifiStatus(); + + /////////////////////////////////// + + server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) + { + if (!request->authenticate(www_username, www_password)) + { + return request->requestAuthentication(); + } + + request->send(200, "text/plain", "Login OK"); + }); + + server.begin(); + + Serial.print(F("Async_HttpBasicAuth started @ IP : ")); + Serial.println(WiFi.localIP()); + + Serial.print(F("Open http://")); + Serial.print(WiFi.localIP()); + Serial.println(F("/ in your browser to see it working")); + + Serial.print(F("Login using username = ")); + Serial.print(www_username); + Serial.print(F(" and password = ")); + Serial.println(www_password); } void heartBeatPrint() { - static int num = 1; - - Serial.print(F(".")); - - if (num == 80) - { - Serial.println(); - num = 1; - } - else if (num++ % 10 == 0) - { - Serial.print(F(" ")); - } + static int num = 1; + + Serial.print(F(".")); + + if (num == 80) + { + Serial.println(); + num = 1; + } + else if (num++ % 10 == 0) + { + Serial.print(F(" ")); + } } void check_status() { - static unsigned long checkstatus_timeout = 0; + static unsigned long checkstatus_timeout = 0; #define STATUS_CHECK_INTERVAL 10000L - // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change. - if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) - { - heartBeatPrint(); - checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; - } + // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change. + if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) + { + heartBeatPrint(); + checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; + } } void loop() { - check_status(); + check_status(); } diff --git a/examples/Async_PostServer/Async_PostServer.ino b/examples/Async_PostServer/Async_PostServer.ino index 43eb40d..1bfb888 100644 --- a/examples/Async_PostServer/Async_PostServer.ino +++ b/examples/Async_PostServer/Async_PostServer.ino @@ -1,10 +1,10 @@ /**************************************************************************************************************************** Async_PostServer.ino - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -16,7 +16,7 @@ // Check https://github.com/khoih-prog/AsyncWebServer_RP2040W/issues/3#issuecomment-1255676644 #if !( defined(ARDUINO_RASPBERRY_PI_PICO_W) ) - #error For RASPBERRY_PI_PICO_W only + #error For RASPBERRY_PI_PICO_W only #endif #define _RP2040W_AWS_LOGLEVEL_ 1 @@ -60,176 +60,185 @@ body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Col void handleRoot(AsyncWebServerRequest *request) { - request->send(200, "text/html", postForms); + request->send(200, "text/html", postForms); } void handlePlain(AsyncWebServerRequest *request) { - if (request->method() != HTTP_POST) - { - request->send(405, "text/plain", "Method Not Allowed"); - } - else - { - request->send(200, "text/plain", "POST body was:\n" + request->arg("plain")); - } + if (request->method() != HTTP_POST) + { + request->send(405, "text/plain", "Method Not Allowed"); + } + else + { + request->send(200, "text/plain", "POST body was:\n" + request->arg("plain")); + } } void handleForm(AsyncWebServerRequest *request) { - if (request->method() != HTTP_POST) - { - request->send(405, "text/plain", "Method Not Allowed"); - } - else - { - String message = "POST form was:\n"; - for (uint8_t i = 0; i < request->args(); i++) - { - message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; - } - request->send(200, "text/plain", message); - } + if (request->method() != HTTP_POST) + { + request->send(405, "text/plain", "Method Not Allowed"); + } + else + { + String message = "POST form was:\n"; + + for (uint8_t i = 0; i < request->args(); i++) + { + message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; + } + + request->send(200, "text/plain", message); + } } void handleNotFound(AsyncWebServerRequest *request) { - String message = "File Not Found\n\n"; - message += "URI: "; - message += request->url(); - message += "\nMethod: "; - message += (request->method() == HTTP_GET) ? "GET" : "POST"; - message += "\nArguments: "; - message += request->args(); - message += "\n"; - for (uint8_t i = 0; i < request->args(); i++) - { - message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; - } - request->send(404, "text/plain", message); + String message = "File Not Found\n\n"; + message += "URI: "; + message += request->url(); + message += "\nMethod: "; + message += (request->method() == HTTP_GET) ? "GET" : "POST"; + message += "\nArguments: "; + message += request->args(); + message += "\n"; + + for (uint8_t i = 0; i < request->args(); i++) + { + message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; + } + + request->send(404, "text/plain", message); } void printWifiStatus() { - // print the SSID of the network you're attached to: - Serial.print("SSID: "); - Serial.println(WiFi.SSID()); - - // print your board's IP address: - IPAddress ip = WiFi.localIP(); - Serial.print("Local IP Address: "); - Serial.println(ip); - - // print your board's country code - // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) - uint32_t myCountryCode = cyw43_arch_get_country_code(); - char countryCode[3] = { 0, 0, 0 }; - - countryCode[0] = myCountryCode & 0xFF; - countryCode[1] = (myCountryCode >> 8) & 0xFF; - - Serial.print("Country code: "); Serial.println(countryCode); + // print the SSID of the network you're attached to: + Serial.print("SSID: "); + Serial.println(WiFi.SSID()); + + // print your board's IP address: + IPAddress ip = WiFi.localIP(); + Serial.print("Local IP Address: "); + Serial.println(ip); + + // print your board's country code + // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) + uint32_t myCountryCode = cyw43_arch_get_country_code(); + char countryCode[3] = { 0, 0, 0 }; + + countryCode[0] = myCountryCode & 0xFF; + countryCode[1] = (myCountryCode >> 8) & 0xFF; + + Serial.print("Country code: "); + Serial.println(countryCode); } void setup() { - Serial.begin(115200); - while (!Serial && millis() < 5000); + Serial.begin(115200); + + while (!Serial && millis() < 5000); + + delay(200); + + Serial.print("\nStart Async_PostServer on "); + Serial.print(BOARD_NAME); + Serial.print(" with "); + Serial.println(SHIELD_TYPE); + Serial.println(ASYNCTCP_RP2040W_VERSION); + Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); + + /////////////////////////////////// - delay(200); + // check for the WiFi module: + if (WiFi.status() == WL_NO_MODULE) + { + Serial.println("Communication with WiFi module failed!"); - Serial.print("\nStart Async_PostServer on "); Serial.print(BOARD_NAME); - Serial.print(" with "); Serial.println(SHIELD_TYPE); - Serial.println(ASYNCTCP_RP2040W_VERSION); - Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); + // don't continue + while (true); + } - /////////////////////////////////// + Serial.print(F("Connecting to SSID: ")); + Serial.println(ssid); - // check for the WiFi module: - if (WiFi.status() == WL_NO_MODULE) - { - Serial.println("Communication with WiFi module failed!"); - // don't continue - while (true); - } + status = WiFi.begin(ssid, pass); - Serial.print(F("Connecting to SSID: ")); - Serial.println(ssid); + delay(1000); - status = WiFi.begin(ssid, pass); + // attempt to connect to WiFi network + while ( status != WL_CONNECTED) + { + delay(500); - delay(1000); - - // attempt to connect to WiFi network - while ( status != WL_CONNECTED) - { - delay(500); - - // Connect to WPA/WPA2 network - status = WiFi.status(); - } + // Connect to WPA/WPA2 network + status = WiFi.status(); + } - printWifiStatus(); + printWifiStatus(); - /////////////////////////////////// + /////////////////////////////////// - server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) - { - handleRoot(request); - }); + server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) + { + handleRoot(request); + }); - //server.on("/postplain/", handlePlain); - server.on("/postplain/", HTTP_POST, [](AsyncWebServerRequest * request) - { - handlePlain(request); - }); + //server.on("/postplain/", handlePlain); + server.on("/postplain/", HTTP_POST, [](AsyncWebServerRequest * request) + { + handlePlain(request); + }); - //server.on("/postform/", handleForm); - server.on("/postform/", HTTP_POST, [](AsyncWebServerRequest * request) - { - handleForm(request); - }); + //server.on("/postform/", handleForm); + server.on("/postform/", HTTP_POST, [](AsyncWebServerRequest * request) + { + handleForm(request); + }); - server.onNotFound(handleNotFound); + server.onNotFound(handleNotFound); - server.begin(); + server.begin(); - Serial.print(F("HTTP Async_PostServer started @ IP : ")); - Serial.println(WiFi.localIP()); + Serial.print(F("HTTP Async_PostServer started @ IP : ")); + Serial.println(WiFi.localIP()); } void heartBeatPrint() { - static int num = 1; - - Serial.print(F(".")); - - if (num == 80) - { - Serial.println(); - num = 1; - } - else if (num++ % 10 == 0) - { - Serial.print(F(" ")); - } + static int num = 1; + + Serial.print(F(".")); + + if (num == 80) + { + Serial.println(); + num = 1; + } + else if (num++ % 10 == 0) + { + Serial.print(F(" ")); + } } void check_status() { - static unsigned long checkstatus_timeout = 0; + static unsigned long checkstatus_timeout = 0; #define STATUS_CHECK_INTERVAL 10000L - // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change. - if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) - { - heartBeatPrint(); - checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; - } + // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change. + if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) + { + heartBeatPrint(); + checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; + } } void loop() { - check_status(); + check_status(); } diff --git a/examples/Async_WebSocketsServer/Async_WebSocketsServer.ino b/examples/Async_WebSocketsServer/Async_WebSocketsServer.ino index b72770f..8f8f3a2 100644 --- a/examples/Async_WebSocketsServer/Async_WebSocketsServer.ino +++ b/examples/Async_WebSocketsServer/Async_WebSocketsServer.ino @@ -2,16 +2,16 @@ Async_WebSocketsServer.ino For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license *****************************************************************************************************************************/ - #if !( defined(ARDUINO_RASPBERRY_PI_PICO_W) ) - #error For RASPBERRY_PI_PICO_W only +#if !( defined(ARDUINO_RASPBERRY_PI_PICO_W) ) + #error For RASPBERRY_PI_PICO_W only #endif #define _RP2040W_AWS_LOGLEVEL_ 4 @@ -30,170 +30,173 @@ AsyncWebSocket ws("/ws"); void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) { - if (type == WS_EVT_CONNECT) - { - Serial.printf("ws[Server: %s][ClientID: %u] WSClient connected\n", server->url(), client->id()); - client->text("Hello from RP2040W Server"); - } - else if (type == WS_EVT_DISCONNECT) - { - Serial.printf("ws[Server: %s][ClientID: %u] WSClient disconnected\n", server->url(), client->id()); - } - else if (type == WS_EVT_ERROR) - { - //error was received from the other end - Serial.printf("ws[Server: %s][ClientID: %u] error(%u): %s\n", server->url(), client->id(), *((uint16_t*)arg), (char*)data); - } - else if (type == WS_EVT_PONG) - { - //pong message was received (in response to a ping request maybe) - Serial.printf("ws[Server: %s][ClientID: %u] pong[%u]: %s\n", server->url(), client->id(), len, (len) ? (char*)data : ""); - } - else if (type == WS_EVT_DATA) - { - //data packet - AwsFrameInfo * info = (AwsFrameInfo*)arg; - - if (info->final && info->index == 0 && info->len == len) - { - //the whole message is in a single frame and we got all of it's data - Serial.printf("ws[Server: %s][ClientID: %u] %s-message[len: %llu]: ", server->url(), client->id(), - (info->opcode == WS_TEXT) ? "text" : "binary", info->len); - - if (info->opcode == WS_TEXT) - { - data[len] = 0; - Serial.printf("%s\n", (char*)data); - } - else - { - for (size_t i = 0; i < info->len; i++) - { - Serial.printf("%02x ", data[i]); - } - - Serial.printf("\n"); - } - - if (info->opcode == WS_TEXT) - client->text("Got your text message"); - else - client->binary("Got your binary message"); - } - else - { - //message is comprised of multiple frames or the frame is split into multiple packets - if (info->index == 0) - { - if (info->num == 0) - { - Serial.printf("ws[Server: %s][ClientID: %u] %s-message start\n", server->url(), client->id(), - (info->message_opcode == WS_TEXT) ? "text" : "binary"); - } - - Serial.printf("ws[Server: %s][ClientID: %u] frame[%u] start[%llu]\n", server->url(), client->id(), info->num, info->len); - } - - Serial.printf("ws[Server: %s][ClientID: %u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), - info->num, (info->message_opcode == WS_TEXT) ? "text" : "binary", info->index, info->index + len); - - if (info->message_opcode == WS_TEXT) - { - data[len] = 0; - Serial.printf("%s\n", (char*)data); - } - else - { - for (size_t i = 0; i < len; i++) - { - Serial.printf("%02x ", data[i]); - } - - Serial.printf("\n"); - } - - if ((info->index + len) == info->len) - { - Serial.printf("ws[Server: %s][ClientID: %u] frame[%u] end[%llu]\n", server->url(), client->id(), info->num, info->len); - - if (info->final) - { - Serial.printf("ws[Server: %s][ClientID: %u] %s-message end\n", server->url(), client->id(), - (info->message_opcode == WS_TEXT) ? "text" : "binary"); - - if (info->message_opcode == WS_TEXT) - client->text("I got your text message"); - else - client->binary("I got your binary message"); - } - } - } - } + if (type == WS_EVT_CONNECT) + { + Serial.printf("ws[Server: %s][ClientID: %u] WSClient connected\n", server->url(), client->id()); + client->text("Hello from RP2040W Server"); + } + else if (type == WS_EVT_DISCONNECT) + { + Serial.printf("ws[Server: %s][ClientID: %u] WSClient disconnected\n", server->url(), client->id()); + } + else if (type == WS_EVT_ERROR) + { + //error was received from the other end + Serial.printf("ws[Server: %s][ClientID: %u] error(%u): %s\n", server->url(), client->id(), *((uint16_t*)arg), (char*)data); + } + else if (type == WS_EVT_PONG) + { + //pong message was received (in response to a ping request maybe) + Serial.printf("ws[Server: %s][ClientID: %u] pong[%u]: %s\n", server->url(), client->id(), len, (len) ? (char*)data : ""); + } + else if (type == WS_EVT_DATA) + { + //data packet + AwsFrameInfo * info = (AwsFrameInfo*)arg; + + if (info->final && info->index == 0 && info->len == len) + { + //the whole message is in a single frame and we got all of it's data + Serial.printf("ws[Server: %s][ClientID: %u] %s-message[len: %llu]: ", server->url(), client->id(), + (info->opcode == WS_TEXT) ? "text" : "binary", info->len); + + if (info->opcode == WS_TEXT) + { + data[len] = 0; + Serial.printf("%s\n", (char*)data); + } + else + { + for (size_t i = 0; i < info->len; i++) + { + Serial.printf("%02x ", data[i]); + } + + Serial.printf("\n"); + } + + if (info->opcode == WS_TEXT) + client->text("Got your text message"); + else + client->binary("Got your binary message"); + } + else + { + //message is comprised of multiple frames or the frame is split into multiple packets + if (info->index == 0) + { + if (info->num == 0) + { + Serial.printf("ws[Server: %s][ClientID: %u] %s-message start\n", server->url(), client->id(), + (info->message_opcode == WS_TEXT) ? "text" : "binary"); + } + + Serial.printf("ws[Server: %s][ClientID: %u] frame[%u] start[%llu]\n", server->url(), client->id(), info->num, info->len); + } + + Serial.printf("ws[Server: %s][ClientID: %u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), + info->num, (info->message_opcode == WS_TEXT) ? "text" : "binary", info->index, info->index + len); + + if (info->message_opcode == WS_TEXT) + { + data[len] = 0; + Serial.printf("%s\n", (char*)data); + } + else + { + for (size_t i = 0; i < len; i++) + { + Serial.printf("%02x ", data[i]); + } + + Serial.printf("\n"); + } + + if ((info->index + len) == info->len) + { + Serial.printf("ws[Server: %s][ClientID: %u] frame[%u] end[%llu]\n", server->url(), client->id(), info->num, info->len); + + if (info->final) + { + Serial.printf("ws[Server: %s][ClientID: %u] %s-message end\n", server->url(), client->id(), + (info->message_opcode == WS_TEXT) ? "text" : "binary"); + + if (info->message_opcode == WS_TEXT) + client->text("I got your text message"); + else + client->binary("I got your binary message"); + } + } + } + } } void handleRoot(AsyncWebServerRequest *request) { - request->send(200, "text/html", webpageCont); + request->send(200, "text/html", webpageCont); } void printWifiStatus() { - // print the SSID of the network you're attached to: - Serial.print("SSID: "); - Serial.println(WiFi.SSID()); - - // print your board's IP address: - IPAddress ip = WiFi.localIP(); - Serial.print("Local IP Address: "); - Serial.println(ip); + // print the SSID of the network you're attached to: + Serial.print("SSID: "); + Serial.println(WiFi.SSID()); + + // print your board's IP address: + IPAddress ip = WiFi.localIP(); + Serial.print("Local IP Address: "); + Serial.println(ip); } void setup() { - Serial.begin(115200); - while (!Serial && millis() < 5000); - - delay(200); - - Serial.print("\nStarting Async_WebSocketsServer on "); Serial.println(BOARD_NAME); - Serial.println(ASYNCTCP_RP2040W_VERSION); - Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); - - /////////////////////////////////// - - // check for the WiFi module: - if (WiFi.status() == WL_NO_MODULE) - { - Serial.println("Communication with WiFi module failed!"); - // don't continue - while (true); - } - - Serial.print(F("Connecting to SSID: ")); - Serial.println(ssid); - - status = WiFi.begin(ssid, pass); - - delay(1000); - - // attempt to connect to WiFi network - while ( status != WL_CONNECTED) - { - delay(500); - - // Connect to WPA/WPA2 network - status = WiFi.status(); - } - - printWifiStatus(); - - /////////////////////////////////// - - ws.onEvent(onWsEvent); - server.addHandler(&ws); - - server.on("/", handleRoot); - server.begin(); + Serial.begin(115200); + + while (!Serial && millis() < 5000); + + delay(200); + + Serial.print("\nStarting Async_WebSocketsServer on "); + Serial.println(BOARD_NAME); + Serial.println(ASYNCTCP_RP2040W_VERSION); + Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); + + /////////////////////////////////// + + // check for the WiFi module: + if (WiFi.status() == WL_NO_MODULE) + { + Serial.println("Communication with WiFi module failed!"); + + // don't continue + while (true); + } + + Serial.print(F("Connecting to SSID: ")); + Serial.println(ssid); + + status = WiFi.begin(ssid, pass); + + delay(1000); + + // attempt to connect to WiFi network + while ( status != WL_CONNECTED) + { + delay(500); + + // Connect to WPA/WPA2 network + status = WiFi.status(); + } + + printWifiStatus(); + + /////////////////////////////////// + + ws.onEvent(onWsEvent); + server.addHandler(&ws); + + server.on("/", handleRoot); + server.begin(); } void loop() diff --git a/examples/Async_WebSocketsServer/webpage.h b/examples/Async_WebSocketsServer/webpage.h index 0221fed..c014ecd 100644 --- a/examples/Async_WebSocketsServer/webpage.h +++ b/examples/Async_WebSocketsServer/webpage.h @@ -2,8 +2,8 @@ //HTML code for webpage //===================== -const char webpageCont[] PROGMEM = -R"=====( +const char webpageCont[] PROGMEM = + R"=====( RP2040W AsyncSocketServer diff --git a/examples/Async_WebSocketsServer_Xtreme/Async_WebSocketsServer_Xtreme.ino b/examples/Async_WebSocketsServer_Xtreme/Async_WebSocketsServer_Xtreme.ino index 60e7cf7..6dda9ae 100644 --- a/examples/Async_WebSocketsServer_Xtreme/Async_WebSocketsServer_Xtreme.ino +++ b/examples/Async_WebSocketsServer_Xtreme/Async_WebSocketsServer_Xtreme.ino @@ -2,9 +2,9 @@ Async_WebSocketsServer_Xtreme.ino For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -13,8 +13,8 @@ // Modified from code of https://github.com/drmue // Posted in https://github.com/khoih-prog/AsyncWebServer_RP2040W/issues/6 - #if !( defined(ARDUINO_RASPBERRY_PI_PICO_W) ) - #error For RASPBERRY_PI_PICO_W only +#if !( defined(ARDUINO_RASPBERRY_PI_PICO_W) ) + #error For RASPBERRY_PI_PICO_W only #endif #define _RP2040W_AWS_LOGLEVEL_ 4 @@ -37,228 +37,231 @@ AsyncWebSocketClient * globalClient = NULL; void onEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) { - if (type == WS_EVT_CONNECT) - { - Serial.printf("ws[Server: %s][ClientID: %u] WSClient connected\n", server->url(), client->id()); - - globalClient = client; - //client->text("Hello from RP2040W Server"); - } - else if (type == WS_EVT_DISCONNECT) - { - Serial.printf("ws[Server: %s][ClientID: %u] WSClient disconnected\n", server->url(), client->id()); - - globalClient = NULL; - } - else if (type == WS_EVT_ERROR) - { - //error was received from the other end - Serial.printf("ws[Server: %s][ClientID: %u] error(%u): %s\n", server->url(), client->id(), *((uint16_t*)arg), (char*)data); - } - else if (type == WS_EVT_PONG) - { - //pong message was received (in response to a ping request maybe) - Serial.printf("ws[Server: %s][ClientID: %u] pong[%u]: %s\n", server->url(), client->id(), len, (len) ? (char*)data : ""); - } - else if (type == WS_EVT_DATA) - { - //data packet - AwsFrameInfo * info = (AwsFrameInfo*)arg; - - if (info->final && info->index == 0 && info->len == len) - { - //the whole message is in a single frame and we got all of it's data - Serial.printf("ws[Server: %s][ClientID: %u] %s-message[len: %llu]: ", server->url(), client->id(), - (info->opcode == WS_TEXT) ? "text" : "binary", info->len); - - if (info->opcode == WS_TEXT) - { - data[len] = 0; - Serial.printf("%s\n", (char*)data); - } - else - { - for (size_t i = 0; i < info->len; i++) - { - Serial.printf("%02x ", data[i]); - } - - Serial.printf("\n"); - } - - if (info->opcode == WS_TEXT) - client->text("Got your text message"); - else - client->binary("Got your binary message"); - } - else - { - //message is comprised of multiple frames or the frame is split into multiple packets - if (info->index == 0) - { - if (info->num == 0) - { - Serial.printf("ws[Server: %s][ClientID: %u] %s-message start\n", server->url(), client->id(), - (info->message_opcode == WS_TEXT) ? "text" : "binary"); - } - - Serial.printf("ws[Server: %s][ClientID: %u] frame[%u] start[%llu]\n", server->url(), client->id(), info->num, info->len); - } - - Serial.printf("ws[Server: %s][ClientID: %u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), - info->num, (info->message_opcode == WS_TEXT) ? "text" : "binary", info->index, info->index + len); - - if (info->message_opcode == WS_TEXT) - { - data[len] = 0; - Serial.printf("%s\n", (char*)data); - } - else - { - for (size_t i = 0; i < len; i++) - { - Serial.printf("%02x ", data[i]); - } - - Serial.printf("\n"); - } - - if ((info->index + len) == info->len) - { - Serial.printf("ws[Server: %s][ClientID: %u] frame[%u] end[%llu]\n", server->url(), client->id(), info->num, info->len); - - if (info->final) - { - Serial.printf("ws[Server: %s][ClientID: %u] %s-message end\n", server->url(), client->id(), - (info->message_opcode == WS_TEXT) ? "text" : "binary"); - - if (info->message_opcode == WS_TEXT) - client->text("I got your text message"); - else - client->binary("I got your binary message"); - } - } - } - } + if (type == WS_EVT_CONNECT) + { + Serial.printf("ws[Server: %s][ClientID: %u] WSClient connected\n", server->url(), client->id()); + + globalClient = client; + //client->text("Hello from RP2040W Server"); + } + else if (type == WS_EVT_DISCONNECT) + { + Serial.printf("ws[Server: %s][ClientID: %u] WSClient disconnected\n", server->url(), client->id()); + + globalClient = NULL; + } + else if (type == WS_EVT_ERROR) + { + //error was received from the other end + Serial.printf("ws[Server: %s][ClientID: %u] error(%u): %s\n", server->url(), client->id(), *((uint16_t*)arg), (char*)data); + } + else if (type == WS_EVT_PONG) + { + //pong message was received (in response to a ping request maybe) + Serial.printf("ws[Server: %s][ClientID: %u] pong[%u]: %s\n", server->url(), client->id(), len, (len) ? (char*)data : ""); + } + else if (type == WS_EVT_DATA) + { + //data packet + AwsFrameInfo * info = (AwsFrameInfo*)arg; + + if (info->final && info->index == 0 && info->len == len) + { + //the whole message is in a single frame and we got all of it's data + Serial.printf("ws[Server: %s][ClientID: %u] %s-message[len: %llu]: ", server->url(), client->id(), + (info->opcode == WS_TEXT) ? "text" : "binary", info->len); + + if (info->opcode == WS_TEXT) + { + data[len] = 0; + Serial.printf("%s\n", (char*)data); + } + else + { + for (size_t i = 0; i < info->len; i++) + { + Serial.printf("%02x ", data[i]); + } + + Serial.printf("\n"); + } + + if (info->opcode == WS_TEXT) + client->text("Got your text message"); + else + client->binary("Got your binary message"); + } + else + { + //message is comprised of multiple frames or the frame is split into multiple packets + if (info->index == 0) + { + if (info->num == 0) + { + Serial.printf("ws[Server: %s][ClientID: %u] %s-message start\n", server->url(), client->id(), + (info->message_opcode == WS_TEXT) ? "text" : "binary"); + } + + Serial.printf("ws[Server: %s][ClientID: %u] frame[%u] start[%llu]\n", server->url(), client->id(), info->num, info->len); + } + + Serial.printf("ws[Server: %s][ClientID: %u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), + info->num, (info->message_opcode == WS_TEXT) ? "text" : "binary", info->index, info->index + len); + + if (info->message_opcode == WS_TEXT) + { + data[len] = 0; + Serial.printf("%s\n", (char*)data); + } + else + { + for (size_t i = 0; i < len; i++) + { + Serial.printf("%02x ", data[i]); + } + + Serial.printf("\n"); + } + + if ((info->index + len) == info->len) + { + Serial.printf("ws[Server: %s][ClientID: %u] frame[%u] end[%llu]\n", server->url(), client->id(), info->num, info->len); + + if (info->final) + { + Serial.printf("ws[Server: %s][ClientID: %u] %s-message end\n", server->url(), client->id(), + (info->message_opcode == WS_TEXT) ? "text" : "binary"); + + if (info->message_opcode == WS_TEXT) + client->text("I got your text message"); + else + client->binary("I got your binary message"); + } + } + } + } } void handleRoot(AsyncWebServerRequest *request) { - request->send(200, "text/html", webpageCont); + request->send(200, "text/html", webpageCont); } void printWifiStatus() { - // print the SSID of the network you're attached to: - Serial.print("SSID: "); - Serial.println(WiFi.SSID()); - - // print your board's IP address: - IPAddress ip = WiFi.localIP(); - Serial.print("Local IP Address: "); - Serial.println(ip); + // print the SSID of the network you're attached to: + Serial.print("SSID: "); + Serial.println(WiFi.SSID()); + + // print your board's IP address: + IPAddress ip = WiFi.localIP(); + Serial.print("Local IP Address: "); + Serial.println(ip); } void setup() { - Serial.begin(115200); - while (!Serial && millis() < 5000); - - delay(200); - - Serial.print("\nStarting Async_WebSocketsServer_Xtreme on "); Serial.println(BOARD_NAME); - Serial.println(ASYNCTCP_RP2040W_VERSION); - Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); - - /////////////////////////////////// - - // check for the WiFi module: - if (WiFi.status() == WL_NO_MODULE) - { - Serial.println("Communication with WiFi module failed!"); - // don't continue - while (true); - } - - Serial.print(F("Connecting to SSID: ")); - Serial.println(ssid); - - status = WiFi.begin(ssid, pass); - - delay(1000); - - // attempt to connect to WiFi network - while ( status != WL_CONNECTED) - { - delay(500); - - // Connect to WPA/WPA2 network - status = WiFi.status(); - } - - printWifiStatus(); - - /////////////////////////////////// - - ws.onEvent(onEvent); - server.addHandler(&ws); - - server.on("/", handleRoot); - server.begin(); + Serial.begin(115200); + + while (!Serial && millis() < 5000); + + delay(200); + + Serial.print("\nStarting Async_WebSocketsServer_Xtreme on "); + Serial.println(BOARD_NAME); + Serial.println(ASYNCTCP_RP2040W_VERSION); + Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); + + /////////////////////////////////// + + // check for the WiFi module: + if (WiFi.status() == WL_NO_MODULE) + { + Serial.println("Communication with WiFi module failed!"); + + // don't continue + while (true); + } + + Serial.print(F("Connecting to SSID: ")); + Serial.println(ssid); + + status = WiFi.begin(ssid, pass); + + delay(1000); + + // attempt to connect to WiFi network + while ( status != WL_CONNECTED) + { + delay(500); + + // Connect to WPA/WPA2 network + status = WiFi.status(); + } + + printWifiStatus(); + + /////////////////////////////////// + + ws.onEvent(onEvent); + server.addHandler(&ws); + + server.on("/", handleRoot); + server.begin(); } void heartBeatPrint() { - static int num = 1; - - Serial.print(F(".")); - - if (num == 80) - { - Serial.println(); - num = 1; - } - else if (num++ % 10 == 0) - { - Serial.print(F(" ")); - } + static int num = 1; + + Serial.print(F(".")); + + if (num == 80) + { + Serial.println(); + num = 1; + } + else if (num++ % 10 == 0) + { + Serial.print(F(" ")); + } } void sendStatus() { - static unsigned long sendStatus_timeout = 1000; - static unsigned long checkstatus_timeout = 1000; + static unsigned long sendStatus_timeout = 1000; + static unsigned long checkstatus_timeout = 1000; #define STATUS_CHECK_INTERVAL 10000L #define SEND_INTERVAL 30L - // Send status report every SEND_INTERVAL (30) millis - if (millis() > sendStatus_timeout) - { - //if (globalClient != NULL && globalClient->status() == WS_CONNECTED) - // Sending only when gessage_queue not full - if (globalClient != NULL && globalClient->status() == WS_CONNECTED && globalClient->canSend()) - { - String POTvalString = String(rand() % 256); - - JSONtxt = "{\"POT\":\"" + POTvalString + "\"}"; - - globalClient->text(JSONtxt); - } - - sendStatus_timeout = millis() + SEND_INTERVAL; - } - - // Send status report every STATUS_REPORT_INTERVAL (10) seconds - if (millis() > checkstatus_timeout) - { - heartBeatPrint(); - checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; - } + // Send status report every SEND_INTERVAL (30) millis + if (millis() > sendStatus_timeout) + { + //if (globalClient != NULL && globalClient->status() == WS_CONNECTED) + // Sending only when gessage_queue not full + if (globalClient != NULL && globalClient->status() == WS_CONNECTED && globalClient->canSend()) + { + String POTvalString = String(rand() % 256); + + JSONtxt = "{\"POT\":\"" + POTvalString + "\"}"; + + globalClient->text(JSONtxt); + } + + sendStatus_timeout = millis() + SEND_INTERVAL; + } + + // Send status report every STATUS_REPORT_INTERVAL (10) seconds + if (millis() > checkstatus_timeout) + { + heartBeatPrint(); + checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; + } } void loop() { - sendStatus(); + sendStatus(); } diff --git a/examples/MQTTClient_Auth/MQTTClient_Auth.ino b/examples/MQTTClient_Auth/MQTTClient_Auth.ino index 428da21..f5ac284 100644 --- a/examples/MQTTClient_Auth/MQTTClient_Auth.ino +++ b/examples/MQTTClient_Auth/MQTTClient_Auth.ino @@ -1,10 +1,10 @@ /**************************************************************************************************************************** MQTTClient_Auth.ino - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -44,18 +44,18 @@ const char *subTopic = "MQTT_Sub"; // Topic to subcribe to //IPAddress mqttServer(172, 16, 0, 2); -void callback(char* topic, byte* payload, unsigned int length) +void callback(char* topic, byte* payload, unsigned int length) { - Serial.print("Message arrived ["); - Serial.print(topic); - Serial.print("] "); - - for (unsigned int i = 0; i < length; i++) - { - Serial.print((char)payload[i]); - } - - Serial.println(); + Serial.print("Message arrived ["); + Serial.print(topic); + Serial.print("] "); + + for (unsigned int i = 0; i < length; i++) + { + Serial.print((char)payload[i]); + } + + Serial.println(); } WiFiClient wifiClient; @@ -66,137 +66,142 @@ const char *pubData = data.c_str(); void reconnect() { - // Loop until we're reconnected - while (!client.connected()) - { - Serial.print("Attempting MQTT connection to "); - Serial.print(mqttServer); - - // Attempt to connect - if (client.connect("arduino", "try", "try")) - { - Serial.println("...connected"); - - // Once connected, publish an announcement... - client.publish(TOPIC, data.c_str()); - - //Serial.println("Published connection message successfully!"); - //Serial.print("Subcribed to: "); - //Serial.println(subTopic); - - client.subscribe(subTopic); - // for loopback testing - client.subscribe(TOPIC); - } - else - { - Serial.print("...failed, rc="); - Serial.print(client.state()); - Serial.println(" try again in 5 seconds"); - - // Wait 5 seconds before retrying - delay(5000); - } - } + // Loop until we're reconnected + while (!client.connected()) + { + Serial.print("Attempting MQTT connection to "); + Serial.print(mqttServer); + + // Attempt to connect + if (client.connect("arduino", "try", "try")) + { + Serial.println("...connected"); + + // Once connected, publish an announcement... + client.publish(TOPIC, data.c_str()); + + //Serial.println("Published connection message successfully!"); + //Serial.print("Subcribed to: "); + //Serial.println(subTopic); + + client.subscribe(subTopic); + // for loopback testing + client.subscribe(TOPIC); + } + else + { + Serial.print("...failed, rc="); + Serial.print(client.state()); + Serial.println(" try again in 5 seconds"); + + // Wait 5 seconds before retrying + delay(5000); + } + } } void printWifiStatus() { - // print the SSID of the network you're attached to: - Serial.print("SSID: "); - Serial.println(WiFi.SSID()); - - // print your board's IP address: - IPAddress ip = WiFi.localIP(); - Serial.print("Local IP Address: "); - Serial.println(ip); - - // print your board's country code - // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) - uint32_t myCountryCode = cyw43_arch_get_country_code(); - char countryCode[3] = { 0, 0, 0 }; - - countryCode[0] = myCountryCode & 0xFF; - countryCode[1] = (myCountryCode >> 8) & 0xFF; - - Serial.print("Country code: "); Serial.println(countryCode); + // print the SSID of the network you're attached to: + Serial.print("SSID: "); + Serial.println(WiFi.SSID()); + + // print your board's IP address: + IPAddress ip = WiFi.localIP(); + Serial.print("Local IP Address: "); + Serial.println(ip); + + // print your board's country code + // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) + uint32_t myCountryCode = cyw43_arch_get_country_code(); + char countryCode[3] = { 0, 0, 0 }; + + countryCode[0] = myCountryCode & 0xFF; + countryCode[1] = (myCountryCode >> 8) & 0xFF; + + Serial.print("Country code: "); + Serial.println(countryCode); } void setup() { - // Open serial communications and wait for port to open: - Serial.begin(115200); - while (!Serial && millis() < 5000); - - Serial.print("\nStart MQTTClient_Auth on "); Serial.print(BOARD_NAME); - Serial.print(" with "); Serial.println(SHIELD_TYPE); - Serial.println(ASYNCTCP_RP2040W_VERSION); - Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); - - /////////////////////////////////// - - // check for the WiFi module: - if (WiFi.status() == WL_NO_MODULE) - { - Serial.println("Communication with WiFi module failed!"); - // don't continue - while (true); - } - - Serial.print(F("Connecting to SSID: ")); - Serial.println(ssid); - - status = WiFi.begin(ssid, pass); - - delay(1000); - - // attempt to connect to WiFi network - while ( status != WL_CONNECTED) - { - delay(500); - - // Connect to WPA/WPA2 network - status = WiFi.status(); - } - - printWifiStatus(); - - /////////////////////////////////// - - // Note - the default maximum packet size is 128 bytes. If the - // combined length of clientId, username and password exceed this use the - // following to increase the buffer size: - // client.setBufferSize(255); + // Open serial communications and wait for port to open: + Serial.begin(115200); + + while (!Serial && millis() < 5000); + + Serial.print("\nStart MQTTClient_Auth on "); + Serial.print(BOARD_NAME); + Serial.print(" with "); + Serial.println(SHIELD_TYPE); + Serial.println(ASYNCTCP_RP2040W_VERSION); + Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); + + /////////////////////////////////// + + // check for the WiFi module: + if (WiFi.status() == WL_NO_MODULE) + { + Serial.println("Communication with WiFi module failed!"); + + // don't continue + while (true); + } + + Serial.print(F("Connecting to SSID: ")); + Serial.println(ssid); + + status = WiFi.begin(ssid, pass); + + delay(1000); + + // attempt to connect to WiFi network + while ( status != WL_CONNECTED) + { + delay(500); + + // Connect to WPA/WPA2 network + status = WiFi.status(); + } + + printWifiStatus(); + + /////////////////////////////////// + + // Note - the default maximum packet size is 128 bytes. If the + // combined length of clientId, username and password exceed this use the + // following to increase the buffer size: + // client.setBufferSize(255); } #define MQTT_PUBLISH_INTERVAL_MS 5000L unsigned long lastMsg = 0; -void loop() +void loop() { - static unsigned long now; - - if (!client.connected()) - { - reconnect(); - } - - // Sending Data - now = millis(); - - if (now - lastMsg > MQTT_PUBLISH_INTERVAL_MS) - { - lastMsg = now; - - if (!client.publish(TOPIC, pubData)) - { - Serial.println("Message failed to send."); - } - - Serial.print("Message Send : " + String(TOPIC) + " => "); - Serial.println(data); - } - - client.loop(); + static unsigned long now; + + if (!client.connected()) + { + reconnect(); + } + + // Sending Data + now = millis(); + + if (now - lastMsg > MQTT_PUBLISH_INTERVAL_MS) + { + lastMsg = now; + + if (!client.publish(TOPIC, pubData)) + { + Serial.println("Message failed to send."); + } + + Serial.print("Message Send : " + String(TOPIC) + " => "); + Serial.println(data); + } + + client.loop(); } diff --git a/examples/MQTTClient_Auth/defines.h b/examples/MQTTClient_Auth/defines.h index 96c412b..30def16 100644 --- a/examples/MQTTClient_Auth/defines.h +++ b/examples/MQTTClient_Auth/defines.h @@ -1,10 +1,10 @@ /**************************************************************************************************************************** defines.h - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -15,7 +15,7 @@ #define defines_h #if !( defined(ARDUINO_RASPBERRY_PI_PICO_W) ) - #error For RASPBERRY_PI_PICO_W only + #error For RASPBERRY_PI_PICO_W only #endif #define _RP2040W_AWS_LOGLEVEL_ 1 diff --git a/examples/MQTTClient_Basic/MQTTClient_Basic.ino b/examples/MQTTClient_Basic/MQTTClient_Basic.ino index 00015e6..5e6b384 100644 --- a/examples/MQTTClient_Basic/MQTTClient_Basic.ino +++ b/examples/MQTTClient_Basic/MQTTClient_Basic.ino @@ -1,10 +1,10 @@ /**************************************************************************************************************************** MQTTClient_Basic.ino - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -18,7 +18,7 @@ - publishes "hello world" to the topic "outTopic" - subscribes to the topic "inTopic", printing out any messages it receives. NB - it assumes the received payloads are strings not binary - + It will reconnect to the server if the connection is lost using a blocking reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to achieve the same result without blocking the main loop. @@ -43,18 +43,18 @@ const char *ID = "MQTTClient_SSL-Client"; // Name of our device, must be const char *TOPIC = "MQTT_Pub"; // Topic to subcribe to const char *subTopic = "MQTT_Sub"; // Topic to subcribe to -void callback(char* topic, byte* payload, unsigned int length) +void callback(char* topic, byte* payload, unsigned int length) { - Serial.print("Message arrived ["); - Serial.print(topic); - Serial.print("] "); - - for (unsigned int i = 0; i < length; i++) - { - Serial.print((char)payload[i]); - } - - Serial.println(); + Serial.print("Message arrived ["); + Serial.print(topic); + Serial.print("] "); + + for (unsigned int i = 0; i < length; i++) + { + Serial.print((char)payload[i]); + } + + Serial.println(); } WiFiClient wifiClient; @@ -65,138 +65,143 @@ const char *pubData = data.c_str(); void reconnect() { - // Loop until we're reconnected - while (!client.connected()) - { - Serial.print("Attempting MQTT connection to "); - Serial.print(mqttServer); - - // Attempt to connect - if (client.connect(ID, "try", "try")) - { - Serial.println("...connected"); - - // Once connected, publish an announcement... - client.publish(TOPIC, data.c_str()); - - //Serial.println("Published connection message successfully!"); - //Serial.print("Subcribed to: "); - //Serial.println(subTopic); - - client.subscribe(subTopic); - // for loopback testing - client.subscribe(TOPIC); - } - else - { - Serial.print("...failed, rc="); - Serial.print(client.state()); - Serial.println(" try again in 5 seconds"); - - // Wait 5 seconds before retrying - delay(5000); - } - } + // Loop until we're reconnected + while (!client.connected()) + { + Serial.print("Attempting MQTT connection to "); + Serial.print(mqttServer); + + // Attempt to connect + if (client.connect(ID, "try", "try")) + { + Serial.println("...connected"); + + // Once connected, publish an announcement... + client.publish(TOPIC, data.c_str()); + + //Serial.println("Published connection message successfully!"); + //Serial.print("Subcribed to: "); + //Serial.println(subTopic); + + client.subscribe(subTopic); + // for loopback testing + client.subscribe(TOPIC); + } + else + { + Serial.print("...failed, rc="); + Serial.print(client.state()); + Serial.println(" try again in 5 seconds"); + + // Wait 5 seconds before retrying + delay(5000); + } + } } void printWifiStatus() { - // print the SSID of the network you're attached to: - Serial.print("SSID: "); - Serial.println(WiFi.SSID()); - - // print your board's IP address: - IPAddress ip = WiFi.localIP(); - Serial.print("Local IP Address: "); - Serial.println(ip); - - // print your board's country code - // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) - uint32_t myCountryCode = cyw43_arch_get_country_code(); - char countryCode[3] = { 0, 0, 0 }; - - countryCode[0] = myCountryCode & 0xFF; - countryCode[1] = (myCountryCode >> 8) & 0xFF; - - Serial.print("Country code: "); Serial.println(countryCode); + // print the SSID of the network you're attached to: + Serial.print("SSID: "); + Serial.println(WiFi.SSID()); + + // print your board's IP address: + IPAddress ip = WiFi.localIP(); + Serial.print("Local IP Address: "); + Serial.println(ip); + + // print your board's country code + // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) + uint32_t myCountryCode = cyw43_arch_get_country_code(); + char countryCode[3] = { 0, 0, 0 }; + + countryCode[0] = myCountryCode & 0xFF; + countryCode[1] = (myCountryCode >> 8) & 0xFF; + + Serial.print("Country code: "); + Serial.println(countryCode); } void setup() { - // Open serial communications and wait for port to open: - Serial.begin(115200); - while (!Serial && millis() < 5000); - - Serial.print("\nStart MQTTClient_Basic on "); Serial.print(BOARD_NAME); - Serial.print(" with "); Serial.println(SHIELD_TYPE); - Serial.println(ASYNCTCP_RP2040W_VERSION); - Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); - - /////////////////////////////////// - - // check for the WiFi module: - if (WiFi.status() == WL_NO_MODULE) - { - Serial.println("Communication with WiFi module failed!"); - // don't continue - while (true); - } - - Serial.print(F("Connecting to SSID: ")); - Serial.println(ssid); - - status = WiFi.begin(ssid, pass); - - delay(1000); - - // attempt to connect to WiFi network - while ( status != WL_CONNECTED) - { - delay(500); - - // Connect to WPA/WPA2 network - status = WiFi.status(); - } - - printWifiStatus(); - - /////////////////////////////////// - - client.setServer(mqttServer, 1883); - client.setCallback(callback); - - // Allow the hardware to sort itself out - delay(1500); + // Open serial communications and wait for port to open: + Serial.begin(115200); + + while (!Serial && millis() < 5000); + + Serial.print("\nStart MQTTClient_Basic on "); + Serial.print(BOARD_NAME); + Serial.print(" with "); + Serial.println(SHIELD_TYPE); + Serial.println(ASYNCTCP_RP2040W_VERSION); + Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); + + /////////////////////////////////// + + // check for the WiFi module: + if (WiFi.status() == WL_NO_MODULE) + { + Serial.println("Communication with WiFi module failed!"); + + // don't continue + while (true); + } + + Serial.print(F("Connecting to SSID: ")); + Serial.println(ssid); + + status = WiFi.begin(ssid, pass); + + delay(1000); + + // attempt to connect to WiFi network + while ( status != WL_CONNECTED) + { + delay(500); + + // Connect to WPA/WPA2 network + status = WiFi.status(); + } + + printWifiStatus(); + + /////////////////////////////////// + + client.setServer(mqttServer, 1883); + client.setCallback(callback); + + // Allow the hardware to sort itself out + delay(1500); } #define MQTT_PUBLISH_INTERVAL_MS 5000L unsigned long lastMsg = 0; -void loop() +void loop() { - static unsigned long now; - - if (!client.connected()) - { - reconnect(); - } - - // Sending Data - now = millis(); - - if (now - lastMsg > MQTT_PUBLISH_INTERVAL_MS) - { - lastMsg = now; - - if (!client.publish(TOPIC, pubData)) - { - Serial.println("Message failed to send."); - } - - Serial.print("Message Send : " + String(TOPIC) + " => "); - Serial.println(data); - } - - client.loop(); + static unsigned long now; + + if (!client.connected()) + { + reconnect(); + } + + // Sending Data + now = millis(); + + if (now - lastMsg > MQTT_PUBLISH_INTERVAL_MS) + { + lastMsg = now; + + if (!client.publish(TOPIC, pubData)) + { + Serial.println("Message failed to send."); + } + + Serial.print("Message Send : " + String(TOPIC) + " => "); + Serial.println(data); + } + + client.loop(); } diff --git a/examples/MQTTClient_Basic/defines.h b/examples/MQTTClient_Basic/defines.h index 96c412b..30def16 100644 --- a/examples/MQTTClient_Basic/defines.h +++ b/examples/MQTTClient_Basic/defines.h @@ -1,10 +1,10 @@ /**************************************************************************************************************************** defines.h - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -15,7 +15,7 @@ #define defines_h #if !( defined(ARDUINO_RASPBERRY_PI_PICO_W) ) - #error For RASPBERRY_PI_PICO_W only + #error For RASPBERRY_PI_PICO_W only #endif #define _RP2040W_AWS_LOGLEVEL_ 1 diff --git a/examples/MQTT_ThingStream/MQTT_ThingStream.ino b/examples/MQTT_ThingStream/MQTT_ThingStream.ino index 6a5e3d5..8f9a522 100644 --- a/examples/MQTT_ThingStream/MQTT_ThingStream.ino +++ b/examples/MQTT_ThingStream/MQTT_ThingStream.ino @@ -1,10 +1,10 @@ /**************************************************************************************************************************** MQTT_ThingStream.ino - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -46,28 +46,28 @@ const char my_key[] = "FIXME"; #if USING_THINGSTREAM_IO -const char *MQTT_PREFIX_TOPIC = "esp32-sniffer/"; -const char *MQTT_ANNOUNCE_TOPIC = "/status"; -const char *MQTT_CONTROL_TOPIC = "/control"; -const char *MQTT_BLE_TOPIC = "/ble"; + const char *MQTT_PREFIX_TOPIC = "esp32-sniffer/"; + const char *MQTT_ANNOUNCE_TOPIC = "/status"; + const char *MQTT_CONTROL_TOPIC = "/control"; + const char *MQTT_BLE_TOPIC = "/ble"; -// GOT FROM ThingsStream! -const char *MQTT_SERVER = "mqtt.thingstream.io"; -const char *MQTT_USER = "MQTT_USER"; -const char *MQTT_PASS = "MQTT_PASS"; -const char *MQTT_CLIENT_ID = "MQTT_CLIENT_ID"; + // GOT FROM ThingsStream! + const char *MQTT_SERVER = "mqtt.thingstream.io"; + const char *MQTT_USER = "MQTT_USER"; + const char *MQTT_PASS = "MQTT_PASS"; + const char *MQTT_CLIENT_ID = "MQTT_CLIENT_ID"; -String topic = MQTT_PREFIX_TOPIC + String("12345678") + MQTT_BLE_TOPIC; -String subTopic = MQTT_PREFIX_TOPIC + String("12345678") + MQTT_BLE_TOPIC; + String topic = MQTT_PREFIX_TOPIC + String("12345678") + MQTT_BLE_TOPIC; + String subTopic = MQTT_PREFIX_TOPIC + String("12345678") + MQTT_BLE_TOPIC; #else -const char* MQTT_SERVER = "broker.emqx.io"; // Broker address + const char* MQTT_SERVER = "broker.emqx.io"; // Broker address -const char* ID = "MQTT_ThingStream"; // Name of our device, must be unique -String topic = "RP2040W_Pub"; // Topic to publish -String subTopic = "RP2040W_Sub"; // Topic to subcribe to + const char* ID = "MQTT_ThingStream"; // Name of our device, must be unique + String topic = "RP2040W_Pub"; // Topic to publish + String subTopic = "RP2040W_Sub"; // Topic to subcribe to #endif @@ -89,168 +89,173 @@ const char *pubData = data.c_str(); /* Called whenever a payload is received from a subscribed MQTT topic */ -void mqtt_receive_callback(char* topic, byte* payload, unsigned int length) +void mqtt_receive_callback(char* topic, byte* payload, unsigned int length) { - Serial.print("MQTT Message receive ["); - Serial.print(topic); - Serial.print("] "); - - for (unsigned int i = 0; i < length; i++) - { - Serial.print((char)payload[i]); - } - - Serial.println(); + Serial.print("MQTT Message receive ["); + Serial.print(topic); + Serial.print("] "); + + for (unsigned int i = 0; i < length; i++) + { + Serial.print((char)payload[i]); + } + + Serial.println(); } -void reconnect() +void reconnect() { - // Loop until we're reconnected - while (!client.connected()) - { - Serial.print("Attempting MQTT connection to "); - Serial.println(MQTT_SERVER); + // Loop until we're reconnected + while (!client.connected()) + { + Serial.print("Attempting MQTT connection to "); + Serial.println(MQTT_SERVER); - // Attempt to connect + // Attempt to connect #if USING_THINGSTREAM_IO - int connect_status = client.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS, topic.c_str(), 2, false, ""); + int connect_status = client.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS, topic.c_str(), 2, false, ""); #else - int connect_status = client.connect(ID); + int connect_status = client.connect(ID); #endif - if (connect_status) - { - Serial.println("...connected"); - - // Once connected, publish an announcement... - client.publish(topic.c_str(), data.c_str()); - - Serial.println("Published connection message successfully!"); - - Serial.print("Subcribed to: "); - Serial.println(subTopic); - - // This is a workaround to address https://github.com/OPEnSLab-OSU/SSLClient/issues/9 - //ethClientSSL.flush(); - // ... and resubscribe - client.subscribe(subTopic.c_str()); - // for loopback testing - client.subscribe(topic.c_str()); - // This is a workaround to address https://github.com/OPEnSLab-OSU/SSLClient/issues/9 - //ethClientSSL.flush(); - } - else - { - Serial.print("failed, rc="); - Serial.print(client.state()); - Serial.println(" try again in 5 seconds"); - - // Wait 5 seconds before retrying - delay(5000); - } - } + if (connect_status) + { + Serial.println("...connected"); + + // Once connected, publish an announcement... + client.publish(topic.c_str(), data.c_str()); + + Serial.println("Published connection message successfully!"); + + Serial.print("Subcribed to: "); + Serial.println(subTopic); + + // This is a workaround to address https://github.com/OPEnSLab-OSU/SSLClient/issues/9 + //ethClientSSL.flush(); + // ... and resubscribe + client.subscribe(subTopic.c_str()); + // for loopback testing + client.subscribe(topic.c_str()); + // This is a workaround to address https://github.com/OPEnSLab-OSU/SSLClient/issues/9 + //ethClientSSL.flush(); + } + else + { + Serial.print("failed, rc="); + Serial.print(client.state()); + Serial.println(" try again in 5 seconds"); + + // Wait 5 seconds before retrying + delay(5000); + } + } } void printWifiStatus() { - // print the SSID of the network you're attached to: - Serial.print("SSID: "); - Serial.println(WiFi.SSID()); - - // print your board's IP address: - IPAddress ip = WiFi.localIP(); - Serial.print("Local IP Address: "); - Serial.println(ip); - - // print your board's country code - // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) - uint32_t myCountryCode = cyw43_arch_get_country_code(); - char countryCode[3] = { 0, 0, 0 }; - - countryCode[0] = myCountryCode & 0xFF; - countryCode[1] = (myCountryCode >> 8) & 0xFF; - - Serial.print("Country code: "); Serial.println(countryCode); + // print the SSID of the network you're attached to: + Serial.print("SSID: "); + Serial.println(WiFi.SSID()); + + // print your board's IP address: + IPAddress ip = WiFi.localIP(); + Serial.print("Local IP Address: "); + Serial.println(ip); + + // print your board's country code + // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) + uint32_t myCountryCode = cyw43_arch_get_country_code(); + char countryCode[3] = { 0, 0, 0 }; + + countryCode[0] = myCountryCode & 0xFF; + countryCode[1] = (myCountryCode >> 8) & 0xFF; + + Serial.print("Country code: "); + Serial.println(countryCode); } void setup() { - // Open serial communications and wait for port to open: - Serial.begin(115200); - while (!Serial && millis() < 5000); - - Serial.print("\nStart MQTT_ThingStream on "); Serial.print(BOARD_NAME); - Serial.print(" with "); Serial.println(SHIELD_TYPE); - Serial.println(ASYNCTCP_RP2040W_VERSION); - Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); - - /////////////////////////////////// - - // check for the WiFi module: - if (WiFi.status() == WL_NO_MODULE) - { - Serial.println("Communication with WiFi module failed!"); - // don't continue - while (true); - } - - Serial.print(F("Connecting to SSID: ")); - Serial.println(ssid); - - status = WiFi.begin(ssid, pass); - - delay(1000); - - // attempt to connect to WiFi network - while ( status != WL_CONNECTED) - { - delay(500); - - // Connect to WPA/WPA2 network - status = WiFi.status(); - } - - printWifiStatus(); - - /////////////////////////////////// - - // Note - the default maximum packet size is 256 bytes. If the - // combined length of clientId, username and password exceed this use the - // following to increase the buffer size: - //client.setBufferSize(256); - - Serial.println("***************************************"); - Serial.println(topic); - Serial.println("***************************************"); + // Open serial communications and wait for port to open: + Serial.begin(115200); + + while (!Serial && millis() < 5000); + + Serial.print("\nStart MQTT_ThingStream on "); + Serial.print(BOARD_NAME); + Serial.print(" with "); + Serial.println(SHIELD_TYPE); + Serial.println(ASYNCTCP_RP2040W_VERSION); + Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); + + /////////////////////////////////// + + // check for the WiFi module: + if (WiFi.status() == WL_NO_MODULE) + { + Serial.println("Communication with WiFi module failed!"); + + // don't continue + while (true); + } + + Serial.print(F("Connecting to SSID: ")); + Serial.println(ssid); + + status = WiFi.begin(ssid, pass); + + delay(1000); + + // attempt to connect to WiFi network + while ( status != WL_CONNECTED) + { + delay(500); + + // Connect to WPA/WPA2 network + status = WiFi.status(); + } + + printWifiStatus(); + + /////////////////////////////////// + + // Note - the default maximum packet size is 256 bytes. If the + // combined length of clientId, username and password exceed this use the + // following to increase the buffer size: + //client.setBufferSize(256); + + Serial.println("***************************************"); + Serial.println(topic); + Serial.println("***************************************"); } #define MQTT_PUBLISH_INTERVAL_MS 5000L -void loop() +void loop() { - static unsigned long now; - - if (!client.connected()) - { - reconnect(); - } - - // Sending Data - now = millis(); - - if (now - lastMsg > MQTT_PUBLISH_INTERVAL_MS) - { - lastMsg = now; - - if (!client.publish(topic.c_str(), pubData)) - { - Serial.println("Message failed to send."); - } - - Serial.print("MQTT Message Send : " + topic + " => "); - Serial.println(data); - } - - client.loop(); + static unsigned long now; + + if (!client.connected()) + { + reconnect(); + } + + // Sending Data + now = millis(); + + if (now - lastMsg > MQTT_PUBLISH_INTERVAL_MS) + { + lastMsg = now; + + if (!client.publish(topic.c_str(), pubData)) + { + Serial.println("Message failed to send."); + } + + Serial.print("MQTT Message Send : " + topic + " => "); + Serial.println(data); + } + + client.loop(); } diff --git a/examples/MQTT_ThingStream/defines.h b/examples/MQTT_ThingStream/defines.h index 96c412b..30def16 100644 --- a/examples/MQTT_ThingStream/defines.h +++ b/examples/MQTT_ThingStream/defines.h @@ -1,10 +1,10 @@ /**************************************************************************************************************************** defines.h - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -15,7 +15,7 @@ #define defines_h #if !( defined(ARDUINO_RASPBERRY_PI_PICO_W) ) - #error For RASPBERRY_PI_PICO_W only + #error For RASPBERRY_PI_PICO_W only #endif #define _RP2040W_AWS_LOGLEVEL_ 1 diff --git a/examples/WebClient/WebClient.ino b/examples/WebClient/WebClient.ino index 97b83ca..2567934 100644 --- a/examples/WebClient/WebClient.ino +++ b/examples/WebClient/WebClient.ino @@ -1,10 +1,10 @@ /**************************************************************************************************************************** WebClient.h - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -24,105 +24,110 @@ WiFiClient client; void printWifiStatus() { - // print the SSID of the network you're attached to: - Serial.print("SSID: "); - Serial.println(WiFi.SSID()); - - // print your board's IP address: - IPAddress ip = WiFi.localIP(); - Serial.print("Local IP Address: "); - Serial.println(ip); - - // print your board's country code - // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) - uint32_t myCountryCode = cyw43_arch_get_country_code(); - char countryCode[3] = { 0, 0, 0 }; - - countryCode[0] = myCountryCode & 0xFF; - countryCode[1] = (myCountryCode >> 8) & 0xFF; - - Serial.print("Country code: "); Serial.println(countryCode); + // print the SSID of the network you're attached to: + Serial.print("SSID: "); + Serial.println(WiFi.SSID()); + + // print your board's IP address: + IPAddress ip = WiFi.localIP(); + Serial.print("Local IP Address: "); + Serial.println(ip); + + // print your board's country code + // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) + uint32_t myCountryCode = cyw43_arch_get_country_code(); + char countryCode[3] = { 0, 0, 0 }; + + countryCode[0] = myCountryCode & 0xFF; + countryCode[1] = (myCountryCode >> 8) & 0xFF; + + Serial.print("Country code: "); + Serial.println(countryCode); } void setup() { - // Open serial communications and wait for port to open: - Serial.begin(115200); - while (!Serial && millis() < 5000); - - Serial.print("\nStart WebClient on "); Serial.print(BOARD_NAME); - Serial.print(" with "); Serial.println(SHIELD_TYPE); - Serial.println(ASYNCTCP_RP2040W_VERSION); - Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); - - /////////////////////////////////// - - // check for the WiFi module: - if (WiFi.status() == WL_NO_MODULE) - { - Serial.println("Communication with WiFi module failed!"); - // don't continue - while (true); - } - - Serial.print(F("Connecting to SSID: ")); - Serial.println(ssid); - - status = WiFi.begin(ssid, pass); - - delay(1000); - - // attempt to connect to WiFi network - while ( status != WL_CONNECTED) - { - delay(500); - - // Connect to WPA/WPA2 network - status = WiFi.status(); - } - - printWifiStatus(); - - /////////////////////////////////// - - Serial.println(); - Serial.println(F("Starting connection to server...")); - - // if you get a connection, report back via serial - if (client.connect(server, 80)) - { - Serial.println(F("Connected to server")); - // Make a HTTP request - client.println(F("GET /asciilogo.txt HTTP/1.1")); - client.println(F("Host: arduino.tips")); - client.println(F("Connection: close")); - client.println(); - } + // Open serial communications and wait for port to open: + Serial.begin(115200); + + while (!Serial && millis() < 5000); + + Serial.print("\nStart WebClient on "); + Serial.print(BOARD_NAME); + Serial.print(" with "); + Serial.println(SHIELD_TYPE); + Serial.println(ASYNCTCP_RP2040W_VERSION); + Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); + + /////////////////////////////////// + + // check for the WiFi module: + if (WiFi.status() == WL_NO_MODULE) + { + Serial.println("Communication with WiFi module failed!"); + + // don't continue + while (true); + } + + Serial.print(F("Connecting to SSID: ")); + Serial.println(ssid); + + status = WiFi.begin(ssid, pass); + + delay(1000); + + // attempt to connect to WiFi network + while ( status != WL_CONNECTED) + { + delay(500); + + // Connect to WPA/WPA2 network + status = WiFi.status(); + } + + printWifiStatus(); + + /////////////////////////////////// + + Serial.println(); + Serial.println(F("Starting connection to server...")); + + // if you get a connection, report back via serial + if (client.connect(server, 80)) + { + Serial.println(F("Connected to server")); + // Make a HTTP request + client.println(F("GET /asciilogo.txt HTTP/1.1")); + client.println(F("Host: arduino.tips")); + client.println(F("Connection: close")); + client.println(); + } } void printoutData(void) { - // if there are incoming bytes available - // from the server, read them and print them - while (client.available()) - { - char c = client.read(); - Serial.write(c); - } + // if there are incoming bytes available + // from the server, read them and print them + while (client.available()) + { + char c = client.read(); + Serial.write(c); + } } void loop() { - printoutData(); - - // if the server's disconnected, stop the client - if (!client.connected()) - { - Serial.println(); - Serial.println(F("Disconnecting from server...")); - client.stop(); - - // do nothing forevermore - while (true); - } + printoutData(); + + // if the server's disconnected, stop the client + if (!client.connected()) + { + Serial.println(); + Serial.println(F("Disconnecting from server...")); + client.stop(); + + // do nothing forevermore + while (true); + } } diff --git a/examples/WebClient/defines.h b/examples/WebClient/defines.h index 96c412b..30def16 100644 --- a/examples/WebClient/defines.h +++ b/examples/WebClient/defines.h @@ -1,10 +1,10 @@ /**************************************************************************************************************************** defines.h - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -15,7 +15,7 @@ #define defines_h #if !( defined(ARDUINO_RASPBERRY_PI_PICO_W) ) - #error For RASPBERRY_PI_PICO_W only + #error For RASPBERRY_PI_PICO_W only #endif #define _RP2040W_AWS_LOGLEVEL_ 1 diff --git a/examples/WebClientRepeating/WebClientRepeating.ino b/examples/WebClientRepeating/WebClientRepeating.ino index 1480ad4..e8c9302 100644 --- a/examples/WebClientRepeating/WebClientRepeating.ino +++ b/examples/WebClientRepeating/WebClientRepeating.ino @@ -1,10 +1,10 @@ /**************************************************************************************************************************** WebClientRepeating.ino - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -28,111 +28,116 @@ WiFiClient client; // this method makes a HTTP connection to the server void httpRequest() { - Serial.println(); - - // close any connection before send a new request - // this will free the socket on the WiFi shield - client.stop(); - - // if there's a successful connection - if (client.connect(server, 80)) - { - Serial.println(F("Connecting...")); - - // send the HTTP PUT request - client.println(F("GET /asciilogo.txt HTTP/1.1")); - client.println(F("Host: arduino.tips")); - client.println(F("Connection: close")); - client.println(); - - // note the time that the connection was made - lastConnectionTime = millis(); - } - else - { - // if you couldn't make a connection - Serial.println(F("Connection failed")); - } + Serial.println(); + + // close any connection before send a new request + // this will free the socket on the WiFi shield + client.stop(); + + // if there's a successful connection + if (client.connect(server, 80)) + { + Serial.println(F("Connecting...")); + + // send the HTTP PUT request + client.println(F("GET /asciilogo.txt HTTP/1.1")); + client.println(F("Host: arduino.tips")); + client.println(F("Connection: close")); + client.println(); + + // note the time that the connection was made + lastConnectionTime = millis(); + } + else + { + // if you couldn't make a connection + Serial.println(F("Connection failed")); + } } void printWifiStatus() { - // print the SSID of the network you're attached to: - Serial.print("SSID: "); - Serial.println(WiFi.SSID()); - - // print your board's IP address: - IPAddress ip = WiFi.localIP(); - Serial.print("Local IP Address: "); - Serial.println(ip); - - // print your board's country code - // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) - uint32_t myCountryCode = cyw43_arch_get_country_code(); - char countryCode[3] = { 0, 0, 0 }; - - countryCode[0] = myCountryCode & 0xFF; - countryCode[1] = (myCountryCode >> 8) & 0xFF; - - Serial.print("Country code: "); Serial.println(countryCode); + // print the SSID of the network you're attached to: + Serial.print("SSID: "); + Serial.println(WiFi.SSID()); + + // print your board's IP address: + IPAddress ip = WiFi.localIP(); + Serial.print("Local IP Address: "); + Serial.println(ip); + + // print your board's country code + // #define CYW43_COUNTRY(A, B, REV) ((unsigned char)(A) | ((unsigned char)(B) << 8) | ((REV) << 16)) + uint32_t myCountryCode = cyw43_arch_get_country_code(); + char countryCode[3] = { 0, 0, 0 }; + + countryCode[0] = myCountryCode & 0xFF; + countryCode[1] = (myCountryCode >> 8) & 0xFF; + + Serial.print("Country code: "); + Serial.println(countryCode); } void setup() { - // Open serial communications and wait for port to open: - Serial.begin(115200); - while (!Serial && millis() < 5000); - - Serial.print("\nStart WebClientRepeating on "); Serial.print(BOARD_NAME); - Serial.print(" with "); Serial.println(SHIELD_TYPE); - Serial.println(ASYNCTCP_RP2040W_VERSION); - Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); - - /////////////////////////////////// - - // check for the WiFi module: - if (WiFi.status() == WL_NO_MODULE) - { - Serial.println("Communication with WiFi module failed!"); - // don't continue - while (true); - } - - Serial.print(F("Connecting to SSID: ")); - Serial.println(ssid); - - status = WiFi.begin(ssid, pass); - - delay(1000); - - // attempt to connect to WiFi network - while ( status != WL_CONNECTED) - { - delay(500); - - // Connect to WPA/WPA2 network - status = WiFi.status(); - } - - printWifiStatus(); - - /////////////////////////////////// + // Open serial communications and wait for port to open: + Serial.begin(115200); + + while (!Serial && millis() < 5000); + + Serial.print("\nStart WebClientRepeating on "); + Serial.print(BOARD_NAME); + Serial.print(" with "); + Serial.println(SHIELD_TYPE); + Serial.println(ASYNCTCP_RP2040W_VERSION); + Serial.println(ASYNC_WEBSERVER_RP2040W_VERSION); + + /////////////////////////////////// + + // check for the WiFi module: + if (WiFi.status() == WL_NO_MODULE) + { + Serial.println("Communication with WiFi module failed!"); + + // don't continue + while (true); + } + + Serial.print(F("Connecting to SSID: ")); + Serial.println(ssid); + + status = WiFi.begin(ssid, pass); + + delay(1000); + + // attempt to connect to WiFi network + while ( status != WL_CONNECTED) + { + delay(500); + + // Connect to WPA/WPA2 network + status = WiFi.status(); + } + + printWifiStatus(); + + /////////////////////////////////// } void loop() { - // if there's incoming data from the net connection send it out the serial port - // this is for debugging purposes only - while (client.available()) - { - char c = client.read(); - Serial.write(c); - } - - // if 10 seconds have passed since your last connection, - // then connect again and send data - if (millis() - lastConnectionTime > postingInterval) - { - httpRequest(); - } + // if there's incoming data from the net connection send it out the serial port + // this is for debugging purposes only + while (client.available()) + { + char c = client.read(); + Serial.write(c); + } + + // if 10 seconds have passed since your last connection, + // then connect again and send data + if (millis() - lastConnectionTime > postingInterval) + { + httpRequest(); + } } diff --git a/examples/WebClientRepeating/defines.h b/examples/WebClientRepeating/defines.h index 96c412b..30def16 100644 --- a/examples/WebClientRepeating/defines.h +++ b/examples/WebClientRepeating/defines.h @@ -1,10 +1,10 @@ /**************************************************************************************************************************** defines.h - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -15,7 +15,7 @@ #define defines_h #if !( defined(ARDUINO_RASPBERRY_PI_PICO_W) ) - #error For RASPBERRY_PI_PICO_W only + #error For RASPBERRY_PI_PICO_W only #endif #define _RP2040W_AWS_LOGLEVEL_ 1 diff --git a/library.json b/library.json index 9f1d4ca..da60b94 100644 --- a/library.json +++ b/library.json @@ -1,8 +1,8 @@ { "name": "AsyncWebServer_RP2040W", - "version": "1.4.0", + "version": "1.4.1", "keywords": "http, async, websocket, webserver, async-webserver, async-tcp, async-udp, async-websocket, async-http, ssl, tls, rp2040, rp2040w, raspberry-pi-pico-w, cyw43439, wifi", - "description": "Asynchronous WebServer Library for RASPBERRY_PI_PICO_W using CYW43439 WiFi with arduino-pico core. This library, which is relied on AsyncTCP_RP2040W, is part of a series of advanced Async libraries for RP2040W, such as AsyncTCP_RP2040W, AsyncUDP_RP2040W, AsyncWebServer_RP2040W, AsyncHTTPRequest_RP2040W, AsyncHTTPSRequest_RP2040W, etc. Now can display programmed WiFi country-code and support using CString to save heap to send very large data", + "description": "Asynchronous WebServer Library for RASPBERRY_PI_PICO_W using CYW43439 WiFi with arduino-pico core. This library, which is relied on AsyncTCP_RP2040W, is part of a series of advanced Async libraries for RP2040W, such as AsyncTCP_RP2040W, AsyncUDP_RP2040W, AsyncWebServer_RP2040W, AsyncHTTPRequest_RP2040W, AsyncHTTPSRequest_RP2040W, etc. Now can display programmed WiFi country-code, support using CString to save heap to send very large data and examples to demo how to use beginChunkedResponse() to send large html in chunks", "authors": [ { diff --git a/library.properties b/library.properties index fd8cfb6..b1591e3 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=AsyncWebServer_RP2040W -version=1.4.0 +version=1.4.1 author=Hristo Gochkov,Khoi Hoang maintainer=Khoi Hoang sentence=Asynchronous WebServer Library for RASPBERRY_PI_PICO_W using CYW43439 WiFi with arduino-pico core. -paragraph=This library, which is relied on AsyncTCP_RP2040W, is part of a series of advanced Async libraries for RP2040W, such as AsyncTCP_RP2040W, AsyncUDP_RP2040W, AsyncWebServer_RP2040W, AsyncHTTPRequest_RP2040W, AsyncHTTPSRequest_RP2040W, etc. Now can display programmed WiFi country-code and support using CString to save heap to send very large data +paragraph=This library, which is relied on AsyncTCP_RP2040W, is part of a series of advanced Async libraries for RP2040W, such as AsyncTCP_RP2040W, AsyncUDP_RP2040W, AsyncWebServer_RP2040W, AsyncHTTPRequest_RP2040W, AsyncHTTPSRequest_RP2040W, etc. Now can display programmed WiFi country-code, support using CString to save heap to send very large data and examples to demo how to use beginChunkedResponse() to send large html in chunks category=Communication url=https://github.com/khoih-prog/AsyncWebServer_RP2040W architectures=rp2040 diff --git a/pics/AsyncWebServer_SendChunked.png b/pics/AsyncWebServer_SendChunked.png new file mode 100644 index 0000000000000000000000000000000000000000..f8a7e4a1124b9ec9aefc62aae04f0549c65a5e88 GIT binary patch literal 77634 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

NKDTLje-yr?$A1^WR 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 z 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 literal 0 HcmV?d00001 diff --git a/src/AsyncEventSource_RP2040W.cpp b/src/AsyncEventSource_RP2040W.cpp index ba1570f..62a3111 100644 --- a/src/AsyncEventSource_RP2040W.cpp +++ b/src/AsyncEventSource_RP2040W.cpp @@ -1,16 +1,16 @@ /**************************************************************************************************************************** AsyncEventSource_RP2040W.cpp - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license - - Version: 1.4.0 - + + Version: 1.4.1 + Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 13/08/2022 Initial coding for RP2040W with CYW43439 WiFi @@ -24,6 +24,7 @@ 1.3.0 K Hoang 10/10/2022 Fix crash when using AsyncWebSockets server 1.3.1 K Hoang 10/10/2022 Improve robustness of AsyncWebSockets server 1.4.0 K Hoang 20/10/2022 Add LittleFS functions such as AsyncFSWebServer + 1.4.1 K Hoang 10/11/2022 Add examples to demo how to use beginChunkedResponse() to send in chunks *****************************************************************************************************************************/ #if !defined(_RP2040W_AWS_LOGLEVEL_) @@ -491,7 +492,7 @@ size_t AsyncEventSource::avgPacketsWaiting() const for (const auto &c : _clients) { - if (c->connected()) + if (c->connected()) { aql += c->packetsWaiting(); ++nConnectedClients; diff --git a/src/AsyncEventSource_RP2040W.h b/src/AsyncEventSource_RP2040W.h index 0c65d3b..47daf61 100644 --- a/src/AsyncEventSource_RP2040W.h +++ b/src/AsyncEventSource_RP2040W.h @@ -1,16 +1,16 @@ /************************************************************************************************************* AsyncEventSource_RP2040W.h - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license - - Version: 1.4.0 - + + Version: 1.4.1 + Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 13/08/2022 Initial coding for RP2040W with CYW43439 WiFi @@ -24,6 +24,7 @@ 1.3.0 K Hoang 10/10/2022 Fix crash when using AsyncWebSockets server 1.3.1 K Hoang 10/10/2022 Improve robustness of AsyncWebSockets server 1.4.0 K Hoang 20/10/2022 Add LittleFS functions such as AsyncFSWebServer + 1.4.1 K Hoang 10/11/2022 Add examples to demo how to use beginChunkedResponse() to send in chunks *****************************************************************************************************************************/ #pragma once diff --git a/src/AsyncFSEditor_RP2040W.cpp b/src/AsyncFSEditor_RP2040W.cpp index e610bb3..c803801 100644 --- a/src/AsyncFSEditor_RP2040W.cpp +++ b/src/AsyncFSEditor_RP2040W.cpp @@ -1,16 +1,16 @@ /**************************************************************************************************************************** AsyncFSEditor_RP2040W.cpp - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license - - Version: 1.4.0 - + + Version: 1.4.1 + Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 13/08/2022 Initial coding for RP2040W with CYW43439 WiFi @@ -24,6 +24,7 @@ 1.3.0 K Hoang 10/10/2022 Fix crash when using AsyncWebSockets server 1.3.1 K Hoang 10/10/2022 Improve robustness of AsyncWebSockets server 1.4.0 K Hoang 20/10/2022 Add LittleFS functions such as AsyncFSWebServer + 1.4.1 K Hoang 10/11/2022 Add examples to demo how to use beginChunkedResponse() to send in chunks *****************************************************************************************************************************/ #include "AsyncFSEditor_RP2040W.h" @@ -328,25 +329,30 @@ static bool matchWild(const char *pattern, const char *testee) { if (( *pattern == '?' ) || (*pattern == *testee)) { - pattern++; testee++; + pattern++; + testee++; continue; } if (*pattern == '*') { - nxPat = pattern++; nxTst = testee; + nxPat = pattern++; + nxTst = testee; continue; } if (nxPat) { - pattern = nxPat + 1; testee = ++nxTst; + pattern = nxPat + 1; + testee = ++nxTst; continue; } return false; } - while (*pattern == '*') { + + while (*pattern == '*') + { pattern++; } @@ -376,7 +382,7 @@ static bool addExclude(const char *item) if (!e->item) { free(e); - + return false; } @@ -395,7 +401,8 @@ static void loadExcludeList(FS& fs, const char *filename) if (&fs) { - AWS_LOGDEBUG0("loadExcludeList: FS * = "); AWS_LOGDEBUG0( ( (uint32_t) &fs, HEX ) ); + AWS_LOGDEBUG0("loadExcludeList: FS * = "); + AWS_LOGDEBUG0( ( (uint32_t) &fs, HEX ) ); } else { @@ -451,7 +458,7 @@ static void loadExcludeList(FS& fs, const char *filename) if (!addExclude(linebuf)) { excludeFile.close(); - + return; } } @@ -466,7 +473,8 @@ static bool isExcluded(FS& fs, const char *filename) { if (&fs) { - AWS_LOGDEBUG0("isExcluded: FS * = "); AWS_LOGDEBUG0( ( (uint32_t) &fs, HEX ) ); + AWS_LOGDEBUG0("isExcluded: FS * = "); + AWS_LOGDEBUG0( ( (uint32_t) &fs, HEX ) ); } else { @@ -506,7 +514,8 @@ AsyncFSEditor::AsyncFSEditor(const String& username, const String& password, con { if (&fs) { - AWS_LOGDEBUG0("AsyncFSEditor: FS * = "); AWS_LOGDEBUG0( ( (uint32_t) &fs, HEX ) ); + AWS_LOGDEBUG0("AsyncFSEditor: FS * = "); + AWS_LOGDEBUG0( ( (uint32_t) &fs, HEX ) ); } else { @@ -537,7 +546,7 @@ bool AsyncFSEditor::canHandle(AsyncWebServerRequest *request) if (request->_tempFile.isDirectory()) { request->_tempFile.close(); - + return false; } } @@ -554,7 +563,7 @@ bool AsyncFSEditor::canHandle(AsyncWebServerRequest *request) if (request->_tempFile.isDirectory()) { request->_tempFile.close(); - + return false; } } diff --git a/src/AsyncFSEditor_RP2040W.h b/src/AsyncFSEditor_RP2040W.h index b62e19a..bcdd705 100644 --- a/src/AsyncFSEditor_RP2040W.h +++ b/src/AsyncFSEditor_RP2040W.h @@ -1,16 +1,16 @@ /**************************************************************************************************************************** AsyncFSEditor_RP2040W.h - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license - - Version: 1.4.0 - + + Version: 1.4.1 + Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 13/08/2022 Initial coding for RP2040W with CYW43439 WiFi @@ -24,6 +24,7 @@ 1.3.0 K Hoang 10/10/2022 Fix crash when using AsyncWebSockets server 1.3.1 K Hoang 10/10/2022 Improve robustness of AsyncWebSockets server 1.4.0 K Hoang 20/10/2022 Add LittleFS functions such as AsyncFSWebServer + 1.4.1 K Hoang 10/11/2022 Add examples to demo how to use beginChunkedResponse() to send in chunks *****************************************************************************************************************************/ #ifndef RP2040W_ASYNC_FSEDITOR_H_ diff --git a/src/AsyncJson_RP2040W.h b/src/AsyncJson_RP2040W.h index c479318..dec15ae 100644 --- a/src/AsyncJson_RP2040W.h +++ b/src/AsyncJson_RP2040W.h @@ -1,16 +1,16 @@ /**************************************************************************************************************************** AsyncJson_RP2040W.h - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license - - Version: 1.4.0 - + + Version: 1.4.1 + Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 13/08/2022 Initial coding for RP2040W with CYW43439 WiFi @@ -24,6 +24,7 @@ 1.3.0 K Hoang 10/10/2022 Fix crash when using AsyncWebSockets server 1.3.1 K Hoang 10/10/2022 Improve robustness of AsyncWebSockets server 1.4.0 K Hoang 20/10/2022 Add LittleFS functions such as AsyncFSWebServer + 1.4.1 K Hoang 10/11/2022 Add examples to demo how to use beginChunkedResponse() to send in chunks *****************************************************************************************************************************/ /* Async Response to use with ArduinoJson and AsyncWebServer @@ -56,7 +57,7 @@ // ... }); server.addHandler(handler); - + */ #pragma once @@ -147,22 +148,23 @@ class AsyncJsonResponse: public AsyncAbstractResponse ///////////////////////////////////////////////// #ifdef ARDUINOJSON_5_COMPATIBILITY - AsyncJsonResponse(bool isArray = false): _isValid {false} + AsyncJsonResponse(bool isArray = false): _isValid {false} { _code = 200; _contentType = JSON_MIMETYPE; - + if (isArray) _root = _jsonBuffer.createArray(); else _root = _jsonBuffer.createObject(); } #else - AsyncJsonResponse(bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : _jsonBuffer(maxJsonBufferSize), _isValid {false} + AsyncJsonResponse(bool isArray = false, + size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : _jsonBuffer(maxJsonBufferSize), _isValid {false} { _code = 200; _contentType = JSON_MIMETYPE; - + if (isArray) _root = _jsonBuffer.createNestedArray(); else @@ -175,22 +177,22 @@ class AsyncJsonResponse: public AsyncAbstractResponse ~AsyncJsonResponse() {} ///////////////////////////////////////////////// - - inline JsonVariant & getRoot() + + inline JsonVariant & getRoot() { return _root; } ///////////////////////////////////////////////// - - inline bool _sourceValid() const + + inline bool _sourceValid() const { return _isValid; } ///////////////////////////////////////////////// - - size_t setLength() + + size_t setLength() { #ifdef ARDUINOJSON_5_COMPATIBILITY @@ -199,24 +201,24 @@ class AsyncJsonResponse: public AsyncAbstractResponse _contentLength = measureJson(_root); #endif - if (_contentLength) + if (_contentLength) { _isValid = true; } - + return _contentLength; } ///////////////////////////////////////////////// - inline size_t getSize() + inline size_t getSize() { return _jsonBuffer.size(); } ///////////////////////////////////////////////// - size_t _fillBuffer(uint8_t *data, size_t len) + size_t _fillBuffer(uint8_t *data, size_t len) { ChunkPrint dest(data, _sentLength, len); @@ -229,24 +231,25 @@ class AsyncJsonResponse: public AsyncAbstractResponse } ///////////////////////////////////////////////// - + }; ///////////////////////////////////////////////// ///////////////////////////////////////////////// -class PrettyAsyncJsonResponse: public AsyncJsonResponse +class PrettyAsyncJsonResponse: public AsyncJsonResponse { public: #ifdef ARDUINOJSON_5_COMPATIBILITY PrettyAsyncJsonResponse (bool isArray = false) : AsyncJsonResponse {isArray} {} #else - PrettyAsyncJsonResponse (bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : AsyncJsonResponse {isArray, maxJsonBufferSize} {} + PrettyAsyncJsonResponse (bool isArray = false, + size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : AsyncJsonResponse {isArray, maxJsonBufferSize} {} #endif ///////////////////////////////////////////////// - size_t setLength () + size_t setLength () { #ifdef ARDUINOJSON_5_COMPATIBILITY _contentLength = _root.measurePrettyLength (); @@ -254,20 +257,20 @@ class PrettyAsyncJsonResponse: public AsyncJsonResponse _contentLength = measureJsonPretty(_root); #endif - if (_contentLength) + if (_contentLength) { _isValid = true; } - + return _contentLength; } ///////////////////////////////////////////////// - - size_t _fillBuffer (uint8_t *data, size_t len) + + size_t _fillBuffer (uint8_t *data, size_t len) { ChunkPrint dest (data, _sentLength, len); - + #ifdef ARDUINOJSON_5_COMPATIBILITY _root.prettyPrintTo (dest); #else @@ -285,7 +288,7 @@ typedef std::function A ///////////////////////////////////////////////// ///////////////////////////////////////////////// -class AsyncCallbackJsonWebHandler: public AsyncWebHandler +class AsyncCallbackJsonWebHandler: public AsyncWebHandler { private: protected: @@ -293,49 +296,51 @@ class AsyncCallbackJsonWebHandler: public AsyncWebHandler WebRequestMethodComposite _method; ArJsonRequestHandlerFunction _onRequest; size_t _contentLength; - + #ifndef ARDUINOJSON_5_COMPATIBILITY const size_t maxJsonBufferSize; #endif size_t _maxContentLength; - + public: ///////////////////////////////////////////////// - + #ifdef ARDUINOJSON_5_COMPATIBILITY AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest) : _uri(uri), _method(HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {} #else - AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) - : _uri(uri), _method(HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), _maxContentLength(16384) {} + AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest, + size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) + : _uri(uri), _method(HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), + _maxContentLength(16384) {} #endif ///////////////////////////////////////////////// - inline void setMethod(WebRequestMethodComposite method) + inline void setMethod(WebRequestMethodComposite method) { _method = method; } ///////////////////////////////////////////////// - - inline void setMaxContentLength(int maxContentLength) + + inline void setMaxContentLength(int maxContentLength) { _maxContentLength = maxContentLength; } ///////////////////////////////////////////////// - - inline void onRequest(ArJsonRequestHandlerFunction fn) + + inline void onRequest(ArJsonRequestHandlerFunction fn) { _onRequest = fn; } ///////////////////////////////////////////////// - virtual bool canHandle(AsyncWebServerRequest *request) override final + virtual bool canHandle(AsyncWebServerRequest *request) override final { if (!_onRequest) return false; @@ -350,68 +355,70 @@ class AsyncCallbackJsonWebHandler: public AsyncWebHandler return false; request->addInterestingHeader("ANY"); - + return true; } ///////////////////////////////////////////////// - virtual void handleRequest(AsyncWebServerRequest *request) override final + virtual void handleRequest(AsyncWebServerRequest *request) override final { - if (_onRequest) + if (_onRequest) { - if (request->_tempObject != NULL) + if (request->_tempObject != NULL) { #ifdef ARDUINOJSON_5_COMPATIBILITY DynamicJsonBuffer jsonBuffer; JsonVariant json = jsonBuffer.parse((uint8_t*)(request->_tempObject)); - - if (json.success()) + + if (json.success()) { #else DynamicJsonDocument jsonBuffer(this->maxJsonBufferSize); DeserializationError error = deserializeJson(jsonBuffer, (uint8_t*)(request->_tempObject)); - - if (!error) + + if (!error) { JsonVariant json = jsonBuffer.as(); #endif _onRequest(request, json); - + return; } } - + request->send(_contentLength > _maxContentLength ? 413 : 400); - } - else + } + 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 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 + + virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, + size_t total) override final { - if (_onRequest) + if (_onRequest) { _contentLength = total; - - if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) + + if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) { request->_tempObject = malloc(total); } - - if (request->_tempObject != NULL) + + if (request->_tempObject != NULL) { memcpy((uint8_t*)(request->_tempObject) + index, data, len); } @@ -419,8 +426,8 @@ class AsyncCallbackJsonWebHandler: public AsyncWebHandler } ///////////////////////////////////////////////// - - virtual bool isRequestHandlerTrivial() override final + + virtual bool isRequestHandlerTrivial() override final { return _onRequest ? false : true; } diff --git a/src/AsyncWebAuthentication_RP2040W.cpp b/src/AsyncWebAuthentication_RP2040W.cpp index f88c41d..7ec6119 100644 --- a/src/AsyncWebAuthentication_RP2040W.cpp +++ b/src/AsyncWebAuthentication_RP2040W.cpp @@ -1,16 +1,16 @@ /**************************************************************************************************************************** AsyncWebAuthentication_RP2040W.cpp - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license - - Version: 1.4.0 - + + Version: 1.4.1 + Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 13/08/2022 Initial coding for RP2040W with CYW43439 WiFi @@ -24,6 +24,7 @@ 1.3.0 K Hoang 10/10/2022 Fix crash when using AsyncWebSockets server 1.3.1 K Hoang 10/10/2022 Improve robustness of AsyncWebSockets server 1.4.0 K Hoang 20/10/2022 Add LittleFS functions such as AsyncFSWebServer + 1.4.1 K Hoang 10/11/2022 Add examples to demo how to use beginChunkedResponse() to send in chunks *****************************************************************************************************************************/ #if !defined(_RP2040W_AWS_LOGLEVEL_) @@ -44,69 +45,69 @@ // Basic Auth hash = base64("username:password") -bool checkBasicAuthentication(const char * hash, const char * username, const char * password) +bool checkBasicAuthentication(const char * hash, const char * username, const char * password) { if (username == NULL || password == NULL || hash == NULL) { AWS_LOGDEBUG("checkBasicAuthentication: Fail: NULL username/password/hash"); - + return false; } size_t toencodeLen = strlen(username) + strlen(password) + 1; size_t encodedLen = base64_encode_expected_len(toencodeLen); - + if (strlen(hash) != encodedLen) { AWS_LOGDEBUG3("checkBasicAuthentication: Fail: strlen(hash) = ", strlen(hash), " != encodedLen = ", encodedLen ); - + return false; } char *toencode = new char[toencodeLen + 1]; - - if (toencode == NULL) + + if (toencode == NULL) { AWS_LOGDEBUG("checkBasicAuthentication: NULL toencode"); - + return false; } - + char *encoded = new char[base64_encode_expected_len(toencodeLen) + 1]; - - if (encoded == NULL) + + if (encoded == NULL) { AWS_LOGDEBUG("checkBasicAuthentication: NULL encoded"); - + delete[] toencode; - + return false; } - + sprintf(toencode, "%s:%s", username, password); - - if (base64_encode_chars(toencode, toencodeLen, encoded) > 0 && memcmp(hash, encoded, encodedLen) == 0) + + if (base64_encode_chars(toencode, toencodeLen, encoded) > 0 && memcmp(hash, encoded, encodedLen) == 0) { AWS_LOGDEBUG("checkBasicAuthentication: OK"); - + delete[] toencode; delete[] encoded; - + return true; } - + AWS_LOGDEBUG("checkBasicAuthentication: Failed"); - + delete[] toencode; delete[] encoded; - + return false; } ///////////////////////////////////////////////// -static bool getMD5(uint8_t * data, uint16_t len, char * output) -{ +static bool getMD5(uint8_t * data, uint16_t len, char * output) +{ //33 bytes or more // For RP2040W @@ -114,142 +115,142 @@ static bool getMD5(uint8_t * data, uint16_t len, char * output) uint8_t i; uint8_t * _buf = (uint8_t*) malloc(16); - + if (_buf == NULL) { AWS_LOGDEBUG("getMD5: Can malloc _buf"); - + return false; } - + memset(_buf, 0x00, 16); // For RP2040W br_md5_init(&_ctx); br_md5_update(&_ctx, data, len); br_md5_out(&_ctx, _buf); - - for (i = 0; i < 16; i++) + + for (i = 0; i < 16; i++) { sprintf(output + (i * 2), "%02x", _buf[i]); } - + free(_buf); - + AWS_LOGDEBUG("getMD5: Success"); - + return true; } ///////////////////////////////////////////////// -static String genRandomMD5() +static String genRandomMD5() { // For RP2040W uint32_t r = rand(); char * out = (char*) malloc(33); - + if (out == NULL || !getMD5((uint8_t*)(&r), 4, out)) return ""; - + String res = String(out); free(out); - + AWS_LOGDEBUG1("genRandomMD5: res = ", res); - + return res; } ///////////////////////////////////////////////// -static String stringMD5(const String& in) +static String stringMD5(const String& in) { char * out = (char*) malloc(33); - + if (out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out)) return ""; - + String res = String(out); free(out); - + AWS_LOGDEBUG1("stringMD5: res = ", res); - + return res; } ///////////////////////////////////////////////// -String generateDigestHash(const char * username, const char * password, const char * realm) +String generateDigestHash(const char * username, const char * password, const char * realm) { - if (username == NULL || password == NULL || realm == NULL) + if (username == NULL || password == NULL || realm == NULL) { return ""; } - + char * out = (char*) malloc(33); String res = String(username); - + res.concat(":"); res.concat(realm); res.concat(":"); - + String in = res; - + in.concat(password); - + if (out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out)) return ""; - + res.concat(out); free(out); - + AWS_LOGDEBUG1("generateDigestHash: res = ", res); - + return res; } ///////////////////////////////////////////////// -String requestDigestAuthentication(const char * realm) +String requestDigestAuthentication(const char * realm) { String header = "realm=\""; - + if (realm == NULL) header.concat("asyncesp"); else header.concat(realm); - + header.concat( "\", qop=\"auth\", nonce=\""); header.concat(genRandomMD5()); header.concat("\", opaque=\""); header.concat(genRandomMD5()); header.concat("\""); - + AWS_LOGDEBUG1("requestDigestAuthentication: header = ", header); - + return header; } ///////////////////////////////////////////////// -bool checkDigestAuthentication(const char * header, const char * method, const char * username, const char * password, - const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri) +bool checkDigestAuthentication(const char * header, const char * method, const char * username, const char * password, + const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri) { - if (username == NULL || password == NULL || header == NULL || method == NULL) + if (username == NULL || password == NULL || header == NULL || method == NULL) { AWS_LOGDEBUG("AUTH FAIL: missing required fields"); - + return false; } String myHeader = String(header); int nextBreak = myHeader.indexOf(","); - - if (nextBreak < 0) + + if (nextBreak < 0) { AWS_LOGDEBUG("AUTH FAIL: no variables"); - + return false; } @@ -263,98 +264,98 @@ bool checkDigestAuthentication(const char * header, const char * method, const c String myCnonce = String(); myHeader += ", "; - - do + + do { String avLine = myHeader.substring(0, nextBreak); - + avLine.trim(); myHeader = myHeader.substring(nextBreak + 1); nextBreak = myHeader.indexOf(","); int eqSign = avLine.indexOf("="); - - if (eqSign < 0) + + if (eqSign < 0) { AWS_LOGDEBUG("AUTH FAIL: no = sign"); - + return false; } - + String varName = avLine.substring(0, eqSign); avLine = avLine.substring(eqSign + 1); - - if (avLine.startsWith("\"")) + + if (avLine.startsWith("\"")) { avLine = avLine.substring(1, avLine.length() - 1); } - if (varName.equals("username")) + if (varName.equals("username")) { - if (!avLine.equals(username)) + if (!avLine.equals(username)) { AWS_LOGDEBUG("AUTH FAIL: username"); - + return false; } - + myUsername = avLine; - } - else if (varName.equals("realm")) + } + else if (varName.equals("realm")) { - if (realm != NULL && !avLine.equals(realm)) + if (realm != NULL && !avLine.equals(realm)) { AWS_LOGDEBUG("AUTH FAIL: realm"); - + return false; } - + myRealm = avLine; - } - else if (varName.equals("nonce")) + } + else if (varName.equals("nonce")) { - if (nonce != NULL && !avLine.equals(nonce)) + if (nonce != NULL && !avLine.equals(nonce)) { AWS_LOGDEBUG("AUTH FAIL: nonce"); - + return false; } - + myNonce = avLine; - } - else if (varName.equals("opaque")) + } + else if (varName.equals("opaque")) { - if (opaque != NULL && !avLine.equals(opaque)) + if (opaque != NULL && !avLine.equals(opaque)) { AWS_LOGDEBUG("AUTH FAIL: opaque"); - + return false; } - } - else if (varName.equals("uri")) + } + else if (varName.equals("uri")) { - if (uri != NULL && !avLine.equals(uri)) + if (uri != NULL && !avLine.equals(uri)) { AWS_LOGDEBUG("AUTH FAIL: uri"); - + return false; } - + myUri = avLine; - } - else if (varName.equals("response")) + } + else if (varName.equals("response")) { myResponse = avLine; - } - else if (varName.equals("qop")) + } + else if (varName.equals("qop")) { myQop = avLine; - } - else if (varName.equals("nc")) + } + else if (varName.equals("nc")) { myNc = avLine; } - else if (varName.equals("cnonce")) + else if (varName.equals("cnonce")) { myCnonce = avLine; } @@ -364,14 +365,14 @@ bool checkDigestAuthentication(const char * header, const char * method, const c String ha2 = String(method) + ":" + myUri; String response = ha1 + ":" + myNonce + ":" + myNc + ":" + myCnonce + ":" + myQop + ":" + stringMD5(ha2); - if (myResponse.equals(stringMD5(response))) + if (myResponse.equals(stringMD5(response))) { AWS_LOGDEBUG("AUTH SUCCESS"); - + return true; } AWS_LOGDEBUG("AUTH FAIL: password"); - + return false; } diff --git a/src/AsyncWebAuthentication_RP2040W.h b/src/AsyncWebAuthentication_RP2040W.h index 1352ca1..ae05953 100644 --- a/src/AsyncWebAuthentication_RP2040W.h +++ b/src/AsyncWebAuthentication_RP2040W.h @@ -1,16 +1,16 @@ /**************************************************************************************************************************** AsyncWebAuthentication_RP2040W.h - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license - - Version: 1.4.0 - + + Version: 1.4.1 + Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 13/08/2022 Initial coding for RP2040W with CYW43439 WiFi @@ -24,6 +24,7 @@ 1.3.0 K Hoang 10/10/2022 Fix crash when using AsyncWebSockets server 1.3.1 K Hoang 10/10/2022 Improve robustness of AsyncWebSockets server 1.4.0 K Hoang 20/10/2022 Add LittleFS functions such as AsyncFSWebServer + 1.4.1 K Hoang 10/11/2022 Add examples to demo how to use beginChunkedResponse() to send in chunks *****************************************************************************************************************************/ #pragma once @@ -40,8 +41,8 @@ bool checkBasicAuthentication(const char * header, const char * username, const String requestDigestAuthentication(const char * realm); -bool checkDigestAuthentication(const char * header, const char * method, const char * username, const char * password, - const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri); +bool checkDigestAuthentication(const char * header, const char * method, const char * username, const char * password, + const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri); //for storing hashed versions on the device that can be authenticated against String generateDigestHash(const char * username, const char * password, const char * realm); diff --git a/src/AsyncWebHandlerImpl_RP2040W.h b/src/AsyncWebHandlerImpl_RP2040W.h index 4a3552c..fc9db72 100644 --- a/src/AsyncWebHandlerImpl_RP2040W.h +++ b/src/AsyncWebHandlerImpl_RP2040W.h @@ -1,16 +1,16 @@ /**************************************************************************************************************************** AsyncWebHandlerImpl_RP2040W.h - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license - - Version: 1.4.0 - + + Version: 1.4.1 + Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 13/08/2022 Initial coding for RP2040W with CYW43439 WiFi @@ -24,6 +24,7 @@ 1.3.0 K Hoang 10/10/2022 Fix crash when using AsyncWebSockets server 1.3.1 K Hoang 10/10/2022 Improve robustness of AsyncWebSockets server 1.4.0 K Hoang 20/10/2022 Add LittleFS functions such as AsyncFSWebServer + 1.4.1 K Hoang 10/11/2022 Add examples to demo how to use beginChunkedResponse() to send in chunks *****************************************************************************************************************************/ #pragma once @@ -98,7 +99,8 @@ class AsyncCallbackWebHandler: public AsyncWebHandler bool _isRegex; public: - AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), _isRegex(false) {} + AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), + _isRegex(false) {} ///////////////////////////////////////////////// @@ -147,6 +149,7 @@ class AsyncCallbackWebHandler: public AsyncWebHandler return false; #ifdef ASYNCWEBSERVER_REGEX + if (_isRegex) { std::regex pattern(_uri.c_str()); @@ -196,7 +199,8 @@ class AsyncCallbackWebHandler: public AsyncWebHandler ///////////////////////////////////////////////// - virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final + virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, + size_t total) override final { if (_onBody) _onBody(request, data, len, index, total); diff --git a/src/AsyncWebHandlers_RP2040W.cpp b/src/AsyncWebHandlers_RP2040W.cpp index b9f235b..a8db41e 100644 --- a/src/AsyncWebHandlers_RP2040W.cpp +++ b/src/AsyncWebHandlers_RP2040W.cpp @@ -1,16 +1,16 @@ /**************************************************************************************************************************** AsyncWebHandlers_RP2040W.cpp - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license - - Version: 1.4.0 - + + Version: 1.4.1 + Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 13/08/2022 Initial coding for RP2040W with CYW43439 WiFi @@ -24,6 +24,7 @@ 1.3.0 K Hoang 10/10/2022 Fix crash when using AsyncWebSockets server 1.3.1 K Hoang 10/10/2022 Improve robustness of AsyncWebSockets server 1.4.0 K Hoang 20/10/2022 Add LittleFS functions such as AsyncFSWebServer + 1.4.1 K Hoang 10/11/2022 Add examples to demo how to use beginChunkedResponse() to send in chunks *****************************************************************************************************************************/ #if !defined(_RP2040W_AWS_LOGLEVEL_) @@ -38,7 +39,8 @@ ///////////////////////////////////////////////// AsyncStaticWebHandler::AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control) - : _fs(fs), _uri(uri), _path(path), _default_file("index.htm"), _cache_control(cache_control), _last_modified(""), _callback(nullptr) + : _fs(fs), _uri(uri), _path(path), _default_file("index.htm"), _cache_control(cache_control), _last_modified(""), + _callback(nullptr) { // Ensure leading '/' if (_uri.length() == 0 || _uri[0] != '/') @@ -69,7 +71,7 @@ AsyncStaticWebHandler::AsyncStaticWebHandler(const char* uri, FS& fs, const char AsyncStaticWebHandler& AsyncStaticWebHandler::setIsDir(bool isDir) { _isDir = isDir; - + return *this; } @@ -134,12 +136,13 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified() bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request) { - if ( request->method() != HTTP_GET || !request->url().startsWith(_uri) || !request->isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP) ) + if ( request->method() != HTTP_GET || !request->url().startsWith(_uri) + || !request->isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP) ) { return false; } - if (_getFile(request)) + if (_getFile(request)) { // We interested in "If-Modified-Since" header to check if file was modified @@ -274,7 +277,7 @@ void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request) { request->_tempFile.close(); AsyncWebServerResponse * response = new AsyncBasicResponse(304); // Not modified - + response->addHeader("Cache-Control", _cache_control); response->addHeader("ETag", etag); request->send(response); diff --git a/src/AsyncWebRequest_RP2040W.cpp b/src/AsyncWebRequest_RP2040W.cpp index 3c42ca4..73f6d74 100644 --- a/src/AsyncWebRequest_RP2040W.cpp +++ b/src/AsyncWebRequest_RP2040W.cpp @@ -1,16 +1,16 @@ /**************************************************************************************************************************** AsyncWebRequest_RP2040W.cpp - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license - - Version: 1.4.0 - + + Version: 1.4.1 + Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 13/08/2022 Initial coding for RP2040W with CYW43439 WiFi @@ -24,6 +24,7 @@ 1.3.0 K Hoang 10/10/2022 Fix crash when using AsyncWebSockets server 1.3.1 K Hoang 10/10/2022 Improve robustness of AsyncWebSockets server 1.4.0 K Hoang 20/10/2022 Add LittleFS functions such as AsyncFSWebServer + 1.4.1 K Hoang 10/11/2022 Add examples to demo how to use beginChunkedResponse() to send in chunks *****************************************************************************************************************************/ #if !defined(_RP2040W_AWS_LOGLEVEL_) @@ -252,7 +253,8 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len) if (_handler) _handler->handleRequest(this); - else send(501); + else + send(501); } } @@ -854,7 +856,8 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) { if (data == '-' && (_contentLength - _parsedLength - 4) != 0) { - AWS_LOGDEBUG1("ERROR: The parser at the end of the POST but expecting more bytes =", (_contentLength - _parsedLength - 4)); + AWS_LOGDEBUG1("ERROR: The parser at the end of the POST but expecting more bytes =", + (_contentLength - _parsedLength - 4)); _contentLength = _parsedLength + 4;//lets close the request gracefully } @@ -1102,15 +1105,17 @@ AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, const St } ///////////////////////////////////////////////// -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, const String& contentType, const String& content) +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, const String& contentType, + const String& content) { return new AsyncBasicResponse(code, contentType, content); } ///////////////////////////////////////////////// // KH add for favicon -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, const String& contentType, const uint8_t * content, size_t len, - AwsTemplateProcessor callback) +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, const String& contentType, + const uint8_t * content, size_t len, + AwsTemplateProcessor callback) { return new AsyncProgmemResponse(code, contentType, content, len, callback); } @@ -1118,7 +1123,7 @@ AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, const St ///////////////////////////////////////////////// AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(FS &fs, const String& path, const String& contentType, - bool download, AwsTemplateProcessor callback) + bool download, AwsTemplateProcessor callback) { if (fs.exists(path) || (!download && fs.exists(path + ".gz"))) return new AsyncFileResponse(fs, path, contentType, download, callback); @@ -1126,8 +1131,9 @@ AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(FS &fs, const Stri return NULL; } -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(File content, const String& path, const String& contentType, - bool download, AwsTemplateProcessor callback) +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(File content, const String& path, + const String& contentType, + bool download, AwsTemplateProcessor callback) { if (content == true) return new AsyncFileResponse(content, path, contentType, download, callback); @@ -1137,23 +1143,26 @@ AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(File content, cons ///////////////////////////////////////////////// -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback) +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(Stream &stream, const String& contentType, size_t len, + AwsTemplateProcessor callback) { return new AsyncStreamResponse(stream, contentType, len, callback); } ///////////////////////////////////////////////// -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, - AwsTemplateProcessor templateCallback) +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(const String& contentType, size_t len, + AwsResponseFiller callback, + AwsTemplateProcessor templateCallback) { return new AsyncCallbackResponse(contentType, len, callback, templateCallback); } ///////////////////////////////////////////////// -AsyncWebServerResponse * AsyncWebServerRequest::beginChunkedResponse(const String& contentType, AwsResponseFiller callback, - AwsTemplateProcessor templateCallback) +AsyncWebServerResponse * AsyncWebServerRequest::beginChunkedResponse(const String& contentType, + AwsResponseFiller callback, + AwsTemplateProcessor templateCallback) { if (_version) return new AsyncChunkedResponse(contentType, callback, templateCallback); @@ -1170,8 +1179,9 @@ AsyncResponseStream * AsyncWebServerRequest::beginResponseStream(const String& c ///////////////////////////////////////////////// -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse_P(int code, const String& contentType, const uint8_t * content, - size_t len, AwsTemplateProcessor callback) +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse_P(int code, const String& contentType, + const uint8_t * content, + size_t len, AwsTemplateProcessor callback) { return new AsyncProgmemResponse(code, contentType, content, len, callback); } @@ -1179,7 +1189,7 @@ AsyncWebServerResponse * AsyncWebServerRequest::beginResponse_P(int code, const ///////////////////////////////////////////////// AsyncWebServerResponse * AsyncWebServerRequest::beginResponse_P(int code, const String& contentType, PGM_P content, - AwsTemplateProcessor callback) + AwsTemplateProcessor callback) { return beginResponse_P(code, contentType, (const uint8_t *)content, strlen_P(content), callback); } @@ -1240,7 +1250,7 @@ void AsyncWebServerRequest::send(Stream &stream, const String& contentType, size ///////////////////////////////////////////////// -void AsyncWebServerRequest::send(const String& contentType, size_t len, AwsResponseFiller callback, +void AsyncWebServerRequest::send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) { send(beginResponse(contentType, len, callback, templateCallback)); @@ -1248,7 +1258,7 @@ void AsyncWebServerRequest::send(const String& contentType, size_t len, AwsRespo ///////////////////////////////////////////////// -void AsyncWebServerRequest::sendChunked(const String& contentType, AwsResponseFiller callback, +void AsyncWebServerRequest::sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) { send(beginChunkedResponse(contentType, callback, templateCallback)); @@ -1265,7 +1275,8 @@ void AsyncWebServerRequest::redirect(const String& url) ///////////////////////////////////////////////// -bool AsyncWebServerRequest::authenticate(const char * username, const char * password, const char * realm, bool passwordIsHash) +bool AsyncWebServerRequest::authenticate(const char * username, const char * password, const char * realm, + bool passwordIsHash) { AWS_LOGDEBUG1("AsyncWebServerRequest::authenticate: auth-len =", _authorization.length()); @@ -1275,7 +1286,8 @@ bool AsyncWebServerRequest::authenticate(const char * username, const char * pas { AWS_LOGDEBUG("AsyncWebServerRequest::authenticate: _isDigest"); - return checkDigestAuthentication(_authorization.c_str(), methodToString(), username, password, realm, passwordIsHash, NULL, NULL, NULL); + return checkDigestAuthentication(_authorization.c_str(), methodToString(), username, password, realm, passwordIsHash, + NULL, NULL, NULL); } else if (!passwordIsHash) { @@ -1321,7 +1333,8 @@ bool AsyncWebServerRequest::authenticate(const char * hash) String realm = hStr.substring(0, separator); hStr = hStr.substring(separator + 1); - return checkDigestAuthentication(_authorization.c_str(), methodToString(), username.c_str(), hStr.c_str(), realm.c_str(), true, NULL, NULL, NULL); + return checkDigestAuthentication(_authorization.c_str(), methodToString(), username.c_str(), hStr.c_str(), + realm.c_str(), true, NULL, NULL, NULL); } return (_authorization.equals(hash)); @@ -1502,14 +1515,19 @@ const char *AsyncWebServerRequest::requestedConnTypeToString() const { case RCT_NOT_USED: return "RCT_NOT_USED"; + case RCT_DEFAULT: return "RCT_DEFAULT"; + case RCT_HTTP: return "RCT_HTTP"; + case RCT_WS: return "RCT_WS"; + case RCT_EVENT: return "RCT_EVENT"; + default: return "ERROR"; } @@ -1517,7 +1535,8 @@ const char *AsyncWebServerRequest::requestedConnTypeToString() const ///////////////////////////////////////////////// -bool AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2, RequestedConnectionType erct3) +bool AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2, + RequestedConnectionType erct3) { bool res = false; diff --git a/src/AsyncWebResponseImpl_RP2040W.h b/src/AsyncWebResponseImpl_RP2040W.h index 40b9ccc..ecc74fa 100644 --- a/src/AsyncWebResponseImpl_RP2040W.h +++ b/src/AsyncWebResponseImpl_RP2040W.h @@ -1,16 +1,16 @@ /**************************************************************************************************************************** AsyncWebResponseImpl_RP2040W.h - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license - - Version: 1.4.0 - + + Version: 1.4.1 + Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 13/08/2022 Initial coding for RP2040W with CYW43439 WiFi @@ -24,6 +24,7 @@ 1.3.0 K Hoang 10/10/2022 Fix crash when using AsyncWebSockets server 1.3.1 K Hoang 10/10/2022 Improve robustness of AsyncWebSockets server 1.4.0 K Hoang 20/10/2022 Add LittleFS functions such as AsyncFSWebServer + 1.4.1 K Hoang 10/11/2022 Add examples to demo how to use beginChunkedResponse() to send in chunks *****************************************************************************************************************************/ #pragma once @@ -47,30 +48,30 @@ // It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max. -class AsyncBasicResponse: public AsyncWebServerResponse +class AsyncBasicResponse: public AsyncWebServerResponse { private: String _content; - + char *_contentCstr; // RSMOD String _partialHeader; - + public: AsyncBasicResponse(int code, const String& contentType = String(), const String& content = String()); - + AsyncBasicResponse(int code, const String& contentType, const char *content = nullptr); // RSMOD - + // KH add for favicon //AsyncBasicResponse(int code, const String& contentType, const uint8_t * content, size_t len); ////// - + void _respond(AsyncWebServerRequest *request); - + size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); ///////////////////////////////////////////////// - - inline bool _sourceValid() const + + inline bool _sourceValid() const { return true; } @@ -80,7 +81,7 @@ class AsyncBasicResponse: public AsyncWebServerResponse ///////////////////////////////////////////////// -class AsyncAbstractResponse: public AsyncWebServerResponse +class AsyncAbstractResponse: public AsyncWebServerResponse { private: String _head; @@ -91,31 +92,31 @@ class AsyncAbstractResponse: public AsyncWebServerResponse std::vector _cache; size_t _readDataFromCacheOrContent(uint8_t* data, const size_t len); size_t _fillBufferAndProcessTemplates(uint8_t* buf, size_t maxLen); - + protected: AwsTemplateProcessor _callback; - + public: AsyncAbstractResponse(AwsTemplateProcessor callback = nullptr); void _respond(AsyncWebServerRequest *request); size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); ///////////////////////////////////////////////// - - inline bool _sourceValid() const + + inline bool _sourceValid() const { return false; } ///////////////////////////////////////////////// - - virtual size_t _fillBuffer(uint8_t *buf __attribute__((unused)), size_t maxLen __attribute__((unused))) + + virtual size_t _fillBuffer(uint8_t *buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { return 0; } ///////////////////////////////////////////////// - + }; ///////////////////////////////////////////////// @@ -130,7 +131,8 @@ class AsyncProgmemResponse: public AsyncAbstractResponse size_t _readLength; public: - AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback = nullptr); + AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, + AwsTemplateProcessor callback = nullptr); ///////////////////////////////////////////////// @@ -154,94 +156,96 @@ class AsyncProgmemResponse: public AsyncAbstractResponse ///////////////////////////////////////////////// -class AsyncFileResponse: public AsyncAbstractResponse +class AsyncFileResponse: public AsyncAbstractResponse { private: File _content; String _path; void _setContentType(const String& path); - + public: - AsyncFileResponse(FS &fs, const String& path, const String& contentType=String(), bool download=false, - AwsTemplateProcessor callback=nullptr); - AsyncFileResponse(File content, const String& path, const String& contentType=String(), bool download=false, - AwsTemplateProcessor callback=nullptr); - + AsyncFileResponse(FS &fs, const String& path, const String& contentType = String(), bool download = false, + AwsTemplateProcessor callback = nullptr); + AsyncFileResponse(File content, const String& path, const String& contentType = String(), bool download = false, + AwsTemplateProcessor callback = nullptr); + ~AsyncFileResponse(); - - inline bool _sourceValid() const - { - return !!(_content); + + inline bool _sourceValid() const + { + return !!(_content); } - + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; }; ///////////////////////////////////////////////// -class AsyncStreamResponse: public AsyncAbstractResponse +class AsyncStreamResponse: public AsyncAbstractResponse { private: Stream *_content; - + public: AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr); ///////////////////////////////////////////////// - - inline bool _sourceValid() const + + inline bool _sourceValid() const { return !!(_content); } ///////////////////////////////////////////////// - + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; }; ///////////////////////////////////////////////// -class AsyncCallbackResponse: public AsyncAbstractResponse +class AsyncCallbackResponse: public AsyncAbstractResponse { private: AwsResponseFiller _content; size_t _filledLength; - + public: - AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); + AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, + AwsTemplateProcessor templateCallback = nullptr); ///////////////////////////////////////////////// - - inline bool _sourceValid() const + + inline bool _sourceValid() const { return !!(_content); } ///////////////////////////////////////////////// - + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; }; ///////////////////////////////////////////////// -class AsyncChunkedResponse: public AsyncAbstractResponse +class AsyncChunkedResponse: public AsyncAbstractResponse { private: AwsResponseFiller _content; size_t _filledLength; - + public: - AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); + AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, + AwsTemplateProcessor templateCallback = nullptr); ///////////////////////////////////////////////// - - inline bool _sourceValid() const + + inline bool _sourceValid() const { return !!(_content); } ///////////////////////////////////////////////// - + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; }; @@ -251,24 +255,24 @@ class cbuf; ///////////////////////////////////////////////// -class AsyncResponseStream: public AsyncAbstractResponse, public Print +class AsyncResponseStream: public AsyncAbstractResponse, public Print { private: cbuf *_content; - + public: AsyncResponseStream(const String& contentType, size_t bufferSize); ~AsyncResponseStream(); ///////////////////////////////////////////////// - - inline bool _sourceValid() const + + inline bool _sourceValid() const { return (_state < RESPONSE_END); } ///////////////////////////////////////////////// - + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; size_t write(const uint8_t *data, size_t len); size_t write(uint8_t data); diff --git a/src/AsyncWebResponses_RP2040W.cpp b/src/AsyncWebResponses_RP2040W.cpp index ce45a63..c51e349 100644 --- a/src/AsyncWebResponses_RP2040W.cpp +++ b/src/AsyncWebResponses_RP2040W.cpp @@ -1,16 +1,16 @@ /**************************************************************************************************************************** AsyncWebResponses_RP2040W.cpp - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license - - Version: 1.4.0 - + + Version: 1.4.1 + Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 13/08/2022 Initial coding for RP2040W with CYW43439 WiFi @@ -24,6 +24,7 @@ 1.3.0 K Hoang 10/10/2022 Fix crash when using AsyncWebSockets server 1.3.1 K Hoang 10/10/2022 Improve robustness of AsyncWebSockets server 1.4.0 K Hoang 20/10/2022 Add LittleFS functions such as AsyncFSWebServer + 1.4.1 K Hoang 10/11/2022 Add examples to demo how to use beginChunkedResponse() to send in chunks *****************************************************************************************************************************/ #if !defined(_RP2040W_AWS_LOGLEVEL_) @@ -60,47 +61,128 @@ const char* AsyncWebServerResponse::_responseCodeToString(int code) { switch (code) { - case 100: return "Continue"; - case 101: return "Switching Protocols"; - case 200: return "OK"; - case 201: return "Created"; - case 202: return "Accepted"; - case 203: return "Non-Authoritative Information"; - case 204: return "No Content"; - case 205: return "Reset Content"; - case 206: return "Partial Content"; - case 300: return "Multiple Choices"; - case 301: return "Moved Permanently"; - case 302: return "Found"; - case 303: return "See Other"; - case 304: return "Not Modified"; - case 305: return "Use Proxy"; - case 307: return "Temporary Redirect"; - case 400: return "Bad Request"; - case 401: return "Unauthorized"; - case 402: return "Payment Required"; - case 403: return "Forbidden"; - case 404: return "Not Found"; - case 405: return "Method Not Allowed"; - case 406: return "Not Acceptable"; - case 407: return "Proxy Authentication Required"; - case 408: return "Request Time-out"; - case 409: return "Conflict"; - case 410: return "Gone"; - case 411: return "Length Required"; - case 412: return "Precondition Failed"; - case 413: return "Request Entity Too Large"; - case 414: return "Request-URI Too Large"; - case 415: return "Unsupported Media Type"; - case 416: return "Requested range not satisfiable"; - case 417: return "Expectation Failed"; - case 500: return "Internal Server Error"; - case 501: return "Not Implemented"; - case 502: return "Bad Gateway"; - case 503: return "Service Unavailable"; - case 504: return "Gateway Time-out"; - case 505: return "HTTP Version not supported"; - default: return ""; + case 100: + return "Continue"; + + case 101: + return "Switching Protocols"; + + case 200: + return "OK"; + + case 201: + return "Created"; + + case 202: + return "Accepted"; + + case 203: + return "Non-Authoritative Information"; + + case 204: + return "No Content"; + + case 205: + return "Reset Content"; + + case 206: + return "Partial Content"; + + case 300: + return "Multiple Choices"; + + case 301: + return "Moved Permanently"; + + case 302: + return "Found"; + + case 303: + return "See Other"; + + case 304: + return "Not Modified"; + + case 305: + return "Use Proxy"; + + case 307: + return "Temporary Redirect"; + + case 400: + return "Bad Request"; + + case 401: + return "Unauthorized"; + + case 402: + return "Payment Required"; + + case 403: + return "Forbidden"; + + case 404: + return "Not Found"; + + case 405: + return "Method Not Allowed"; + + case 406: + return "Not Acceptable"; + + case 407: + return "Proxy Authentication Required"; + + case 408: + return "Request Time-out"; + + case 409: + return "Conflict"; + + case 410: + return "Gone"; + + case 411: + return "Length Required"; + + case 412: + return "Precondition Failed"; + + case 413: + return "Request Entity Too Large"; + + case 414: + return "Request-URI Too Large"; + + case 415: + return "Unsupported Media Type"; + + case 416: + return "Requested range not satisfiable"; + + case 417: + return "Expectation Failed"; + + case 500: + return "Internal Server Error"; + + case 501: + return "Not Implemented"; + + case 502: + return "Bad Gateway"; + + case 503: + return "Service Unavailable"; + + case 504: + return "Gateway Time-out"; + + case 505: + return "HTTP Version not supported"; + + default: + return ""; } } @@ -259,7 +341,8 @@ size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest *request, size_t len, Fake Progmem Response * */ -AsyncProgmemResponse::AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) +AsyncProgmemResponse::AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, + AwsTemplateProcessor callback): AsyncAbstractResponse(callback) { _code = code; _content = content; @@ -384,7 +467,8 @@ void AsyncBasicResponse::_respond(AsyncWebServerRequest *request) if (_contentCstr) { - _content = String(_contentCstr); // short _contentCstr - so just send as Arduino String - not much of a penalty - fall into below + _content = String( + _contentCstr); // short _contentCstr - so just send as Arduino String - not much of a penalty - fall into below } out += _content; @@ -588,7 +672,8 @@ size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint } } - AWS_LOGDEBUG3("AsyncBasicResponse::_ack : Post_ack, _contentLength =", _contentLength, ", _contentCstr =", _contentCstr); + AWS_LOGDEBUG3("AsyncBasicResponse::_ack : Post_ack, _contentLength =", _contentLength, ", _contentCstr =", + _contentCstr); return 0; } @@ -713,9 +798,9 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u buf[outLen++] = '\r'; buf[outLen++] = '\n'; - + outLen += readLen; - + buf[outLen++] = '\r'; buf[outLen++] = '\n'; } @@ -743,7 +828,8 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u _writtenLength += request->client()->write((const char*)buf, outLen); } - if (_chunked) { + if (_chunked) + { _sentLength += readLen; } else @@ -809,10 +895,12 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size // Search for template placeholders uint8_t* pTemplateStart = data; - while ((pTemplateStart < &data[len]) && (pTemplateStart = (uint8_t*) memchr(pTemplateStart, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart + 1))) + while ((pTemplateStart < &data[len]) + && (pTemplateStart = (uint8_t*) memchr(pTemplateStart, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart + 1))) { // data[0] ... data[len - 1] - uint8_t* pTemplateEnd = (pTemplateStart < &data[len - 1]) ? (uint8_t*) memchr(pTemplateStart + 1, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart) : nullptr; + uint8_t* pTemplateEnd = (pTemplateStart < &data[len - 1]) ? (uint8_t*) memchr(pTemplateStart + 1, TEMPLATE_PLACEHOLDER, + &data[len - 1] - pTemplateStart) : nullptr; // temporary buffer to hold parameter name uint8_t buf[TEMPLATE_PARAM_NAME_LENGTH + 1]; @@ -844,7 +932,7 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size // closing placeholder not found, check if it's in the remaining file data memcpy(buf, pTemplateStart + 1, &data[len - 1] - pTemplateStart); const size_t readFromCacheOrContent = _readDataFromCacheOrContent(buf + (&data[len - 1] - pTemplateStart), - TEMPLATE_PARAM_NAME_LENGTH + 2 - (&data[len - 1] - pTemplateStart + 1)); + TEMPLATE_PARAM_NAME_LENGTH + 2 - (&data[len - 1] - pTemplateStart + 1)); if (readFromCacheOrContent) { @@ -887,7 +975,8 @@ 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)) + if ((pTemplateEnd + 1 < pTemplateStart + numBytesCopied) + && (originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1) < len)) { _cache.insert(_cache.begin(), &data[originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1)], &data[len]); @@ -947,25 +1036,44 @@ AsyncFileResponse::~AsyncFileResponse() 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 = "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"; - else if (path.endsWith(".jpg")) _contentType = "image/jpeg"; - else if (path.endsWith(".ico")) _contentType = "image/x-icon"; - else if (path.endsWith(".svg")) _contentType = "image/svg+xml"; - else if (path.endsWith(".eot")) _contentType = "font/eot"; - else if (path.endsWith(".woff")) _contentType = "font/woff"; - else if (path.endsWith(".woff2")) _contentType = "font/woff2"; - else if (path.endsWith(".ttf")) _contentType = "font/ttf"; - else if (path.endsWith(".xml")) _contentType = "text/xml"; - else if (path.endsWith(".pdf")) _contentType = "application/pdf"; - else if (path.endsWith(".zip")) _contentType = "application/zip"; - else if (path.endsWith(".gz")) _contentType = "application/x-gzip"; - else _contentType = "text/plain"; + 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 = "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"; + else if (path.endsWith(".jpg")) + _contentType = "image/jpeg"; + else if (path.endsWith(".ico")) + _contentType = "image/x-icon"; + else if (path.endsWith(".svg")) + _contentType = "image/svg+xml"; + else if (path.endsWith(".eot")) + _contentType = "font/eot"; + else if (path.endsWith(".woff")) + _contentType = "font/woff"; + else if (path.endsWith(".woff2")) + _contentType = "font/woff2"; + else if (path.endsWith(".ttf")) + _contentType = "font/ttf"; + else if (path.endsWith(".xml")) + _contentType = "text/xml"; + else if (path.endsWith(".pdf")) + _contentType = "application/pdf"; + else if (path.endsWith(".zip")) + _contentType = "application/zip"; + else if (path.endsWith(".gz")) + _contentType = "application/x-gzip"; + else + _contentType = "text/plain"; } ///////////////////////////////////////////////// @@ -1063,7 +1171,8 @@ size_t AsyncFileResponse::_fillBuffer(uint8_t *data, size_t len) Stream Response * */ -AsyncStreamResponse::AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) +AsyncStreamResponse::AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, + AwsTemplateProcessor callback): AsyncAbstractResponse(callback) { _code = 200; _content = &stream; @@ -1092,7 +1201,8 @@ size_t AsyncStreamResponse::_fillBuffer(uint8_t *data, size_t len) Callback Response * */ -AsyncCallbackResponse::AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) +AsyncCallbackResponse::AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, + AwsTemplateProcessor templateCallback) : AsyncAbstractResponse(templateCallback) { _code = 200; @@ -1127,7 +1237,8 @@ size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len) Chunked Response * */ -AsyncChunkedResponse::AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor processorCallback): AsyncAbstractResponse(processorCallback) +AsyncChunkedResponse::AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, + AwsTemplateProcessor processorCallback): AsyncAbstractResponse(processorCallback) { _code = 200; _content = callback; diff --git a/src/AsyncWebServer_RP2040W.cpp b/src/AsyncWebServer_RP2040W.cpp index 4c42e7e..aa66f37 100644 --- a/src/AsyncWebServer_RP2040W.cpp +++ b/src/AsyncWebServer_RP2040W.cpp @@ -1,16 +1,16 @@ /**************************************************************************************************************************** AsyncWebServer_RP2040W.cpp - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license - - Version: 1.4.0 - + + Version: 1.4.1 + Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 13/08/2022 Initial coding for RP2040W with CYW43439 WiFi @@ -24,6 +24,7 @@ 1.3.0 K Hoang 10/10/2022 Fix crash when using AsyncWebSockets server 1.3.1 K Hoang 10/10/2022 Improve robustness of AsyncWebSockets server 1.4.0 K Hoang 20/10/2022 Add LittleFS functions such as AsyncFSWebServer + 1.4.1 K Hoang 10/11/2022 Add examples to demo how to use beginChunkedResponse() to send in chunks *****************************************************************************************************************************/ #if !defined(_RP2040W_AWS_LOGLEVEL_) @@ -63,7 +64,7 @@ AsyncWebServer::AsyncWebServer(uint16_t port) //c->setRxTimeout(3); c->setRxTimeout(0); ////// - + AsyncWebServerRequest *r = new AsyncWebServerRequest((AsyncWebServer*)s, c); if (r == NULL) @@ -114,7 +115,7 @@ AsyncWebRewrite& AsyncWebServer::rewrite(const char* from, const char* to) AsyncWebHandler& AsyncWebServer::addHandler(AsyncWebHandler* handler) { _handlers.add(handler); - + return *handler; } @@ -159,7 +160,7 @@ void AsyncWebServer::beginSecure(const char *cert, const char *key, const char * ///////////////////////////////////////////////// void AsyncWebServer::_handleDisconnect(AsyncWebServerRequest *request) -{ +{ delete request; } @@ -186,19 +187,20 @@ void AsyncWebServer::_attachHandler(AsyncWebServerRequest *request) if (h->filter(request) && h->canHandle(request)) { request->setHandler(h); - + return; } } request->addInterestingHeader("ANY"); - + request->setHandler(NULL); } ///////////////////////////////////////////////// -AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, +AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, + ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody) { AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); @@ -215,7 +217,8 @@ AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodCom ///////////////////////////////////////////////// -AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, +AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, + ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload) { AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); @@ -230,7 +233,8 @@ AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodCom ///////////////////////////////////////////////// -AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest) +AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, + ArRequestHandlerFunction onRequest) { AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); handler->setUri(uri); @@ -256,12 +260,12 @@ AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, ArRequestHandlerFun ///////////////////////////////////////////////// // KH Test add serveStatic -AsyncStaticWebHandler& AsyncWebServer::serveStatic(const char* uri, fs::FS& fs, const char* path, +AsyncStaticWebHandler& AsyncWebServer::serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control) { AsyncStaticWebHandler* handler = new AsyncStaticWebHandler(uri, fs, path, cache_control); addHandler(handler); - + return *handler; } ////// diff --git a/src/AsyncWebServer_RP2040W.h b/src/AsyncWebServer_RP2040W.h index ba8ab99..aa8cd55 100644 --- a/src/AsyncWebServer_RP2040W.h +++ b/src/AsyncWebServer_RP2040W.h @@ -1,16 +1,16 @@ /**************************************************************************************************************************** AsyncWebServer_RP2040W.h - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license - - Version: 1.4.0 - + + Version: 1.4.1 + Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 13/08/2022 Initial coding for RP2040W with CYW43439 WiFi @@ -24,8 +24,9 @@ 1.3.0 K Hoang 10/10/2022 Fix crash when using AsyncWebSockets server 1.3.1 K Hoang 10/10/2022 Improve robustness of AsyncWebSockets server 1.4.0 K Hoang 20/10/2022 Add LittleFS functions such as AsyncFSWebServer + 1.4.1 K Hoang 10/11/2022 Add examples to demo how to use beginChunkedResponse() to send in chunks *****************************************************************************************************************************/ - + #ifndef _RP2040W_ASYNC_WEBSERVER_H_ #define _RP2040W_ASYNC_WEBSERVER_H_ @@ -39,13 +40,13 @@ ///////////////////////////////////////////////// -#define ASYNC_WEBSERVER_RP2040W_VERSION "AsyncWebServer_RP2040W v1.4.0" +#define ASYNC_WEBSERVER_RP2040W_VERSION "AsyncWebServer_RP2040W v1.4.1" #define ASYNC_WEBSERVER_RP2040W_VERSION_MAJOR 1 #define ASYNC_WEBSERVER_RP2040W_VERSION_MINOR 4 -#define ASYNC_WEBSERVER_RP2040W_VERSION_PATCH 0 +#define ASYNC_WEBSERVER_RP2040W_VERSION_PATCH 1 -#define ASYNC_WEBSERVER_RP2040W_VERSION_INT 1004000 +#define ASYNC_WEBSERVER_RP2040W_VERSION_INT 1004001 ///////////////////////////////////////////////// @@ -90,17 +91,17 @@ class AsyncResponseStream; ///////////////////////////////////////////////// #ifndef WEBSERVER_H - typedef enum - { - HTTP_GET = 0b00000001, - HTTP_POST = 0b00000010, - HTTP_DELETE = 0b00000100, - HTTP_PUT = 0b00001000, - HTTP_PATCH = 0b00010000, - HTTP_HEAD = 0b00100000, - HTTP_OPTIONS = 0b01000000, - HTTP_ANY = 0b01111111, - } WebRequestMethod; +typedef enum +{ + HTTP_GET = 0b00000001, + HTTP_POST = 0b00000010, + HTTP_DELETE = 0b00000100, + HTTP_PUT = 0b00001000, + HTTP_PATCH = 0b00010000, + HTTP_HEAD = 0b00100000, + 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 @@ -439,28 +440,38 @@ class AsyncWebServerRequest AwsTemplateProcessor callback = nullptr); ////// - void send(FS &fs, const String& path, const String& contentType = String(), bool download = false, + void send(FS &fs, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); - void send(File content, const String& path, const String& contentType = String(), bool download = false, + void send(File content, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); - + void send(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr); - void send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); - void sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); - void send_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback = nullptr); + void send(const String& contentType, size_t len, AwsResponseFiller callback, + AwsTemplateProcessor templateCallback = nullptr); + void sendChunked(const String& contentType, AwsResponseFiller callback, + AwsTemplateProcessor templateCallback = nullptr); + void send_P(int code, const String& contentType, const uint8_t * content, size_t len, + AwsTemplateProcessor callback = nullptr); void send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback = nullptr); AsyncWebServerResponse *beginResponse(int code, const String& contentType = String(), const String& content = String()); - AsyncWebServerResponse *beginResponse(FS &fs, const String& path, const String& contentType = String(), bool download = false, - AwsTemplateProcessor callback = nullptr); - AsyncWebServerResponse *beginResponse(File content, const String& path, const String& contentType = String(), bool download = false, + AsyncWebServerResponse *beginResponse(FS &fs, const String& path, const String& contentType = String(), + bool download = false, + AwsTemplateProcessor callback = nullptr); + AsyncWebServerResponse *beginResponse(File content, const String& path, const String& contentType = String(), + bool download = false, AwsTemplateProcessor callback = nullptr); - AsyncWebServerResponse *beginResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr); - AsyncWebServerResponse *beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); - AsyncWebServerResponse *beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); + AsyncWebServerResponse *beginResponse(Stream &stream, const String& contentType, size_t len, + AwsTemplateProcessor callback = nullptr); + AsyncWebServerResponse *beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, + AwsTemplateProcessor templateCallback = nullptr); + AsyncWebServerResponse *beginChunkedResponse(const String& contentType, AwsResponseFiller callback, + AwsTemplateProcessor templateCallback = nullptr); AsyncResponseStream *beginResponseStream(const String& contentType, size_t bufferSize = 1460); - AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback = nullptr); - AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback = nullptr); + AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, const uint8_t * content, size_t len, + AwsTemplateProcessor callback = nullptr); + AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, PGM_P content, + AwsTemplateProcessor callback = nullptr); size_t headers() const; // get header count bool hasHeader(const String& name) const; // check if header exists @@ -553,7 +564,7 @@ class AsyncWebRewrite inline AsyncWebRewrite& setFilter(ArRequestFilterFunction fn) { _filter = fn; - + return *this; } @@ -614,7 +625,7 @@ class AsyncWebHandler inline AsyncWebHandler& setFilter(ArRequestFilterFunction fn) { _filter = fn; - + return *this; } @@ -649,7 +660,8 @@ class AsyncWebHandler ///////////////////////////////////////////////// virtual void handleRequest(AsyncWebServerRequest *request __attribute__((unused))) {} - virtual void handleUpload(AsyncWebServerRequest *request __attribute__((unused)), const String& filename __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)), @@ -713,8 +725,10 @@ class AsyncWebServerResponse * */ typedef std::function ArRequestHandlerFunction; -typedef std::function ArUploadHandlerFunction; -typedef std::function ArBodyHandlerFunction; +typedef std::function +ArUploadHandlerFunction; +typedef std::function +ArBodyHandlerFunction; ///////////////////////////////////////////////// @@ -761,7 +775,8 @@ class AsyncWebServer void onFileUpload(ArUploadHandlerFunction fn); //handle file uploads - void onRequestBody(ArBodyHandlerFunction fn); //handle posts with plain body content (JSON often transmitted this way as a request) + void onRequestBody(ArBodyHandlerFunction + fn); //handle posts with plain body content (JSON often transmitted this way as a request) void reset(); //remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody diff --git a/src/AsyncWebServer_RP2040W_Debug.h b/src/AsyncWebServer_RP2040W_Debug.h index e7c0575..407379e 100644 --- a/src/AsyncWebServer_RP2040W_Debug.h +++ b/src/AsyncWebServer_RP2040W_Debug.h @@ -1,16 +1,16 @@ /**************************************************************************************************************************** AsyncWebServer_RP2040W_Debug.h - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license - - Version: 1.4.0 - + + Version: 1.4.1 + Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 13/08/2022 Initial coding for RP2040W with CYW43439 WiFi @@ -24,6 +24,7 @@ 1.3.0 K Hoang 10/10/2022 Fix crash when using AsyncWebSockets server 1.3.1 K Hoang 10/10/2022 Improve robustness of AsyncWebSockets server 1.4.0 K Hoang 20/10/2022 Add LittleFS functions such as AsyncFSWebServer + 1.4.1 K Hoang 10/11/2022 Add examples to demo how to use beginChunkedResponse() to send in chunks *****************************************************************************************************************************/ #pragma once diff --git a/src/AsyncWebSocket_RP2040W.cpp b/src/AsyncWebSocket_RP2040W.cpp index 7ea4c5d..c554154 100644 --- a/src/AsyncWebSocket_RP2040W.cpp +++ b/src/AsyncWebSocket_RP2040W.cpp @@ -1,16 +1,16 @@ /**************************************************************************************************************************** AsyncWebSocket_RP2040W.cpp - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license - - Version: 1.4.0 - + + Version: 1.4.1 + Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 13/08/2022 Initial coding for RP2040W with CYW43439 WiFi @@ -24,6 +24,7 @@ 1.3.0 K Hoang 10/10/2022 Fix crash when using AsyncWebSockets server 1.3.1 K Hoang 10/10/2022 Improve robustness of AsyncWebSockets server 1.4.0 K Hoang 20/10/2022 Add LittleFS functions such as AsyncFSWebServer + 1.4.1 K Hoang 10/11/2022 Add examples to demo how to use beginChunkedResponse() to send in chunks *****************************************************************************************************************************/ #include "Arduino.h" @@ -127,7 +128,7 @@ size_t webSocketSendFrame(AsyncClient *client, bool final, uint8_t opcode, bool AWS_LOGDEBUG1("Error adding header, bytes =", headLen); free(buf); - + return 0; } @@ -154,7 +155,7 @@ size_t webSocketSendFrame(AsyncClient *client, bool final, uint8_t opcode, bool if (!client->send()) { AWS_LOGDEBUG1("Error sending frame: bytes =", headLen + len); - + return 0; } @@ -300,7 +301,7 @@ class AsyncWebSocketControl public: ///////////////////////////////////////////////// - + AsyncWebSocketControl(uint8_t opcode, uint8_t *data = NULL, size_t len = 0, bool mask = false) : _opcode(opcode), _len(len), _mask(len && mask), _finished(false) { @@ -357,7 +358,7 @@ class AsyncWebSocketControl size_t send(AsyncClient *client) { _finished = true; - + return webSocketSendFrame(client, true, _opcode & 0x0F, _mask, _data, _len); } }; @@ -489,7 +490,7 @@ AsyncWebSocketMultiMessage::AsyncWebSocketMultiMessage(AsyncWebSocketMessageBuff { _WSbuffer = buffer; (*_WSbuffer)++; - + _data = buffer->get(); _len = buffer->length(); _status = WS_MSG_SENDING; @@ -543,14 +544,14 @@ size_t AsyncWebSocketMultiMessage::send(AsyncClient *client) if (_sent == _len) { _status = WS_MSG_SENT; - + return 0; } if (_sent > _len) { _status = WS_MSG_ERROR; - + AWS_LOGDEBUG3("WS_MSG_ERROR: _sent =", _sent, ", _len =", _len); return 0; @@ -582,6 +583,7 @@ size_t AsyncWebSocketMultiMessage::send(AsyncClient *client) _sent -= (toSend - sent); _ack -= (toSend - sent); } + AWS_LOGDEBUG3("Send OK: _sent = ", _sent, "= sent =", sent); return sent; @@ -603,7 +605,7 @@ AsyncWebSocketClient::AsyncWebSocketClient(AsyncWebServerRequest *request, Async { delete c; })) -, _messageQueue(LinkedList([](AsyncWebSocketMessage *m) +, _messageQueue(LinkedList([](AsyncWebSocketMessage *m) { delete m; })) @@ -616,7 +618,7 @@ AsyncWebSocketClient::AsyncWebSocketClient(AsyncWebServerRequest *request, Async _pstate = 0; _lastMessageTime = millis(); _keepAlivePeriod = 0; - + _client->setRxTimeout(0); _client->onError([](void *r, AsyncClient * c, int8_t error) @@ -692,6 +694,7 @@ void AsyncWebSocketClient::_onAck(size_t len, uint32_t time) return; } + _controlQueue.remove(head); } } @@ -709,10 +712,12 @@ void AsyncWebSocketClient::_onAck(size_t len, uint32_t time) void AsyncWebSocketClient::_onPoll() { - if (_client->canSend() && (!_controlQueue.isEmpty() || !_messageQueue.isEmpty())) { + if (_client->canSend() && (!_controlQueue.isEmpty() || !_messageQueue.isEmpty())) + { _runQueue(); } - else if (_keepAlivePeriod > 0 && _controlQueue.isEmpty() && _messageQueue.isEmpty() && (millis() - _lastMessageTime) >= _keepAlivePeriod) + else if (_keepAlivePeriod > 0 && _controlQueue.isEmpty() && _messageQueue.isEmpty() + && (millis() - _lastMessageTime) >= _keepAlivePeriod) { ping((uint8_t *)AWSC_PING_PAYLOAD, AWSC_PING_PAYLOAD_LEN); } @@ -758,7 +763,7 @@ void AsyncWebSocketClient::_queueMessage(AsyncWebSocketMessage *dataMessage) if (_status != WS_CONNECTED) { delete dataMessage; - + return; } @@ -766,10 +771,10 @@ void AsyncWebSocketClient::_queueMessage(AsyncWebSocketMessage *dataMessage) { AWS_LOGERROR("ERROR: Large MsQ"); delete dataMessage; - + // KH, fix _messageQueue overflowed by discarding all in the queue _messageQueue.free(); - + delay(20); ////// } @@ -878,28 +883,28 @@ void AsyncWebSocketClient::_onData(void *pbuf, size_t plen) 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 -= 2; if (_pinfo.len == 126) { _pinfo.len = fdata[3] | (uint16_t)(fdata[2]) << 8; - + data += 2; plen -= 2; } - else if (_pinfo.len == 127) + 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; } @@ -907,7 +912,7 @@ void AsyncWebSocketClient::_onData(void *pbuf, size_t plen) if (_pinfo.masked) { memcpy(_pinfo.mask, data, 4); - + data += 4; plen -= 4; } @@ -928,7 +933,7 @@ void AsyncWebSocketClient::_onData(void *pbuf, size_t plen) if (_pinfo.index == 0) { - if (_pinfo.opcode) + if (_pinfo.opcode) { _pinfo.message_opcode = _pinfo.opcode; _pinfo.num = 0; @@ -979,8 +984,9 @@ void AsyncWebSocketClient::_onData(void *pbuf, size_t plen) 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 + else if (_pinfo.opcode < 8) + { + //continuation or text/binary frame _server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, data, datalen); } } @@ -1013,7 +1019,7 @@ size_t AsyncWebSocketClient::printf(const char *format, ...) if (!temp) { va_end(arg); - + return 0; } @@ -1028,7 +1034,7 @@ size_t AsyncWebSocketClient::printf(const char *format, ...) if (!buffer) { delete[] temp; - + return 0; } @@ -1045,7 +1051,7 @@ size_t AsyncWebSocketClient::printf(const char *format, ...) } delete[] temp; - + return len; } @@ -1184,7 +1190,8 @@ AsyncWebSocket::~AsyncWebSocket() {} ///////////////////////////////////////////////// -void AsyncWebSocket::_handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) +void AsyncWebSocket::_handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, + size_t len) { if (_eventHandler != NULL) { @@ -1423,7 +1430,7 @@ size_t AsyncWebSocket::printf(uint32_t id, const char *format, ...) va_start(arg, format); size_t len = c->printf(format, arg); va_end(arg); - + return len; } @@ -1459,7 +1466,7 @@ size_t AsyncWebSocket::printfAll(const char *format, ...) va_end(arg); textAll(buffer); - + return len; } @@ -1617,7 +1624,7 @@ void AsyncWebSocket::handleRequest(AsyncWebServerRequest * request) return; } - if ((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())) + if ((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())) { return request->requestAuthentication(); } @@ -1717,7 +1724,7 @@ AsyncWebSocketResponse::AsyncWebSocketResponse(const String & key, AsyncWebSocke if (hash == NULL) { _state = RESPONSE_FAILED; - + return; } @@ -1759,7 +1766,7 @@ void AsyncWebSocketResponse::_respond(AsyncWebServerRequest * request) if (_state == RESPONSE_FAILED) { request->client()->close(true); - + return; } diff --git a/src/AsyncWebSocket_RP2040W.h b/src/AsyncWebSocket_RP2040W.h index 646cf39..1fa5414 100644 --- a/src/AsyncWebSocket_RP2040W.h +++ b/src/AsyncWebSocket_RP2040W.h @@ -1,16 +1,16 @@ /**************************************************************************************************************************** AsyncWebSocket_RP2040W.h - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license - - Version: 1.4.0 - + + Version: 1.4.1 + Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 13/08/2022 Initial coding for RP2040W with CYW43439 WiFi @@ -24,10 +24,11 @@ 1.3.0 K Hoang 10/10/2022 Fix crash when using AsyncWebSockets server 1.3.1 K Hoang 10/10/2022 Improve robustness of AsyncWebSockets server 1.4.0 K Hoang 20/10/2022 Add LittleFS functions such as AsyncFSWebServer + 1.4.1 K Hoang 10/11/2022 Add examples to demo how to use beginChunkedResponse() to send in chunks *****************************************************************************************************************************/ #pragma once - + #ifndef RP2040W_ASYNCWEBSOCKET_H_ #define RP2040W_ASYNCWEBSOCKET_H_ @@ -117,7 +118,7 @@ typedef enum ///////////////////////////////////////////////// -class AsyncWebSocketMessageBuffer +class AsyncWebSocketMessageBuffer { private: uint8_t * _data; @@ -134,67 +135,67 @@ class AsyncWebSocketMessageBuffer ~AsyncWebSocketMessageBuffer(); ///////////////////////////////////////////////// - - void operator ++(int i) + + void operator ++(int i) { RP2040W_AWS_UNUSED(i); _count++; } ///////////////////////////////////////////////// - - void operator --(int i) + + void operator --(int i) { RP2040W_AWS_UNUSED(i); - - if (_count > 0) + + if (_count > 0) { _count--; } } ///////////////////////////////////////////////// - + bool reserve(size_t size); ///////////////////////////////////////////////// - - inline void lock() + + inline void lock() { _lock = true; } ///////////////////////////////////////////////// - - inline void unlock() + + inline void unlock() { _lock = false; } ///////////////////////////////////////////////// - - inline uint8_t * get() + + inline uint8_t * get() { return _data; } ///////////////////////////////////////////////// - - inline size_t length() + + inline size_t length() { return _len; } ///////////////////////////////////////////////// - - inline uint32_t count() + + inline uint32_t count() { return _count; } ///////////////////////////////////////////////// - - inline bool canDelete() + + inline bool canDelete() { return (!_count && !_lock); } @@ -207,35 +208,35 @@ class AsyncWebSocketMessageBuffer ///////////////////////////////////////////////// -class AsyncWebSocketMessage +class AsyncWebSocketMessage { protected: uint8_t _opcode; bool _mask; AwsMessageStatus _status; - + public: AsyncWebSocketMessage(): _opcode(WS_TEXT), _mask(false), _status(WS_MSG_ERROR) {} virtual ~AsyncWebSocketMessage() {} virtual void ack(size_t len __attribute__((unused)), uint32_t time __attribute__((unused))) {} ///////////////////////////////////////////////// - - virtual size_t send(AsyncClient *client __attribute__((unused))) + + virtual size_t send(AsyncClient *client __attribute__((unused))) { return 0; } ///////////////////////////////////////////////// - - virtual bool finished() + + virtual bool finished() { return _status != WS_MSG_SENDING; } ///////////////////////////////////////////////// - - virtual bool betweenFrames() const + + virtual bool betweenFrames() const { return false; } @@ -243,7 +244,7 @@ class AsyncWebSocketMessage ///////////////////////////////////////////////// -class AsyncWebSocketBasicMessage: public AsyncWebSocketMessage +class AsyncWebSocketBasicMessage: public AsyncWebSocketMessage { private: size_t _len; @@ -251,28 +252,28 @@ class AsyncWebSocketBasicMessage: public AsyncWebSocketMessage size_t _ack; size_t _acked; uint8_t * _data; - + public: AsyncWebSocketBasicMessage(const char * data, size_t len, uint8_t opcode = WS_TEXT, bool mask = false); AsyncWebSocketBasicMessage(uint8_t opcode = WS_TEXT, bool mask = false); virtual ~AsyncWebSocketBasicMessage() override; ///////////////////////////////////////////////// - - virtual bool betweenFrames() const override + + virtual bool betweenFrames() const override { return _acked == _ack; } ///////////////////////////////////////////////// - + virtual void ack(size_t len, uint32_t time) override; virtual size_t send(AsyncClient *client) override; }; ///////////////////////////////////////////////// -class AsyncWebSocketMultiMessage: public AsyncWebSocketMessage +class AsyncWebSocketMultiMessage: public AsyncWebSocketMessage { private: uint8_t * _data; @@ -281,27 +282,27 @@ class AsyncWebSocketMultiMessage: public AsyncWebSocketMessage size_t _ack; size_t _acked; AsyncWebSocketMessageBuffer * _WSbuffer; - + public: AsyncWebSocketMultiMessage(AsyncWebSocketMessageBuffer * buffer, uint8_t opcode = WS_TEXT, bool mask = false); virtual ~AsyncWebSocketMultiMessage() override; ///////////////////////////////////////////////// - - virtual bool betweenFrames() const override + + virtual bool betweenFrames() const override { return _acked == _ack; } ///////////////////////////////////////////////// - + virtual void ack(size_t len, uint32_t time) override ; virtual size_t send(AsyncClient *client) override ; }; ///////////////////////////////////////////////// -class AsyncWebSocketClient +class AsyncWebSocketClient { private: AsyncClient *_client; @@ -331,35 +332,35 @@ class AsyncWebSocketClient ///////////////////////////////////////////////// //client id increments for the given server - inline uint32_t id() + inline uint32_t id() { return _clientId; } ///////////////////////////////////////////////// - - inline AwsClientStatus status() + + inline AwsClientStatus status() { return _status; } ///////////////////////////////////////////////// - - inline AsyncClient* client() + + inline AsyncClient* client() { return _client; } ///////////////////////////////////////////////// - - inline AsyncWebSocket *server() + + inline AsyncWebSocket *server() { return _server; } ///////////////////////////////////////////////// - - inline AwsFrameInfo const &pinfo() const + + inline AwsFrameInfo const &pinfo() const { return _pinfo; } @@ -376,14 +377,14 @@ class AsyncWebSocketClient ///////////////////////////////////////////////// //set auto-ping period in seconds. disabled if zero (default) - inline void keepAlivePeriod(uint16_t seconds) + inline void keepAlivePeriod(uint16_t seconds) { _keepAlivePeriod = seconds * 1000; } ///////////////////////////////////////////////// - - inline uint16_t keepAlivePeriod() + + inline uint16_t keepAlivePeriod() { return (uint16_t)(_keepAlivePeriod / 1000); } @@ -391,13 +392,13 @@ class AsyncWebSocketClient ///////////////////////////////////////////////// //data packets - void message(AsyncWebSocketMessage *message) + void message(AsyncWebSocketMessage *message) { _queueMessage(message); } ///////////////////////////////////////////////// - + bool queueIsFull(); size_t printf(const char *format, ...) __attribute__ ((format (printf, 2, 3))); @@ -418,7 +419,7 @@ class AsyncWebSocketClient ///////////////////////////////////////////////// - inline bool canSend() + inline bool canSend() { return _messageQueue.length() < WS_MAX_QUEUED_MESSAGES; } @@ -436,16 +437,17 @@ class AsyncWebSocketClient ///////////////////////////////////////////////// -typedef std::function AwsEventHandler; +typedef std::function +AwsEventHandler; ///////////////////////////////////////////////// //WebServer Handler implementation that plays the role of a socket server -class AsyncWebSocket: public AsyncWebHandler +class AsyncWebSocket: public AsyncWebHandler { public: typedef LinkedList AsyncWebSocketClientLinkedList; - + private: String _url; AsyncWebSocketClientLinkedList _clients; @@ -459,28 +461,28 @@ class AsyncWebSocket: public AsyncWebHandler ~AsyncWebSocket(); ///////////////////////////////////////////////// - - inline const char * url() const + + inline const char * url() const { return _url.c_str(); } ///////////////////////////////////////////////// - - inline void enable(bool e) + + inline void enable(bool e) { _enabled = e; } ///////////////////////////////////////////////// - - inline bool enabled() const + + inline bool enabled() const { return _enabled; } ///////////////////////////////////////////////// - + bool availableForWriteAll(); bool availableForWrite(uint32_t id); @@ -488,8 +490,8 @@ class AsyncWebSocket: public AsyncWebHandler AsyncWebSocketClient * client(uint32_t id); ///////////////////////////////////////////////// - - inline bool hasClient(uint32_t id) + + inline bool hasClient(uint32_t id) { return client(id) != NULL; } @@ -538,7 +540,7 @@ class AsyncWebSocket: public AsyncWebHandler ///////////////////////////////////////////////// //event listener - inline void onEvent(AwsEventHandler handler) + inline void onEvent(AwsEventHandler handler) { _eventHandler = handler; } @@ -546,13 +548,13 @@ class AsyncWebSocket: public AsyncWebHandler ///////////////////////////////////////////////// //system callbacks (do not call) - inline uint32_t _getNextId() + inline uint32_t _getNextId() { return _cNextId++; } ///////////////////////////////////////////////// - + void _addClient(AsyncWebSocketClient * client); void _handleDisconnect(AsyncWebSocketClient * client); void _handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len); @@ -571,20 +573,20 @@ class AsyncWebSocket: public AsyncWebHandler ///////////////////////////////////////////////// //WebServer response to authenticate the socket and detach the tcp client from the web server request -class AsyncWebSocketResponse: public AsyncWebServerResponse +class AsyncWebSocketResponse: public AsyncWebServerResponse { private: String _content; AsyncWebSocket *_server; - + public: AsyncWebSocketResponse(const String& key, AsyncWebSocket *server); void _respond(AsyncWebServerRequest *request); size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); ///////////////////////////////////////////////// - - inline bool _sourceValid() const + + inline bool _sourceValid() const { return true; } diff --git a/src/AsyncWebSynchronization_RP2040W.h b/src/AsyncWebSynchronization_RP2040W.h index 7af8a85..6193dc8 100644 --- a/src/AsyncWebSynchronization_RP2040W.h +++ b/src/AsyncWebSynchronization_RP2040W.h @@ -1,16 +1,16 @@ /**************************************************************************************************************************** AsyncWebSynchronization_RP2040W.h - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license - - Version: 1.4.0 - + + Version: 1.4.1 + Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 13/08/2022 Initial coding for RP2040W with CYW43439 WiFi @@ -24,6 +24,7 @@ 1.3.0 K Hoang 10/10/2022 Fix crash when using AsyncWebSockets server 1.3.1 K Hoang 10/10/2022 Improve robustness of AsyncWebSockets server 1.4.0 K Hoang 20/10/2022 Add LittleFS functions such as AsyncFSWebServer + 1.4.1 K Hoang 10/11/2022 Add examples to demo how to use beginChunkedResponse() to send in chunks *****************************************************************************************************************************/ #pragma once @@ -44,7 +45,7 @@ class AsyncWebLock ///////////////////////////////////////////////// - inline bool lock() const + inline bool lock() const { return false; } @@ -62,14 +63,14 @@ class AsyncWebLockGuard public: ///////////////////////////////////////////////// - - AsyncWebLockGuard(const AsyncWebLock &l) + + AsyncWebLockGuard(const AsyncWebLock &l) { - if (l.lock()) + if (l.lock()) { _lock = &l; - } - else + } + else { _lock = NULL; } @@ -77,9 +78,9 @@ class AsyncWebLockGuard ///////////////////////////////////////////////// - ~AsyncWebLockGuard() + ~AsyncWebLockGuard() { - if (_lock) + if (_lock) { _lock->unlock(); } diff --git a/src/Crypto/Hash.cpp b/src/Crypto/Hash.cpp index 77a61e6..65257da 100644 --- a/src/Crypto/Hash.cpp +++ b/src/Crypto/Hash.cpp @@ -1,26 +1,26 @@ /** - * @file Hash.cpp - * @date 20.05.2015 - * @author Markus Sattler - * - * Copyright (c) 2015 Markus Sattler. All rights reserved. - * This file is part of the esp8266 core for Arduino environment. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ + @file Hash.cpp + @date 20.05.2015 + @author Markus Sattler + + Copyright (c) 2015 Markus Sattler. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ #include #include "bearssl_hash.h" @@ -28,81 +28,82 @@ #include "Hash.h" /** - * create a sha1 hash from data - * @param data uint8_t * - * @param size uint32_t - * @param hash uint8_t[HASH_BUFFER_SIZE] - */ -void sha1(const uint8_t* data, uint32_t size, uint8_t hash[HASH_BUFFER_SIZE]) + create a sha1 hash from data + @param data uint8_t + @param size uint32_t + @param hash uint8_t[HASH_BUFFER_SIZE] +*/ +void sha1(const uint8_t* data, uint32_t size, uint8_t hash[HASH_BUFFER_SIZE]) { - br_sha1_context ctx; - - AWS_LOGDEBUG0("DATA:"); - for(uint16_t i = 0; i < size; i++) - { - AWS_LOGDEBUG0(data[i]); - } - - AWS_LOGDEBUG0("\nDATA:"); - - for(uint16_t i = 0; i < size; i++) - { - AWS_LOGDEBUG0((char) data[i]); - - } - - AWS_LOGDEBUG0("\n"); - - br_sha1_init(&ctx); - br_sha1_update(&ctx, data, size); - br_sha1_out(&ctx, hash); - - AWS_LOGDEBUG0("SHA1:"); - - for(uint16_t i = 0; i < HASH_BUFFER_SIZE; i++) - { - AWS_LOGDEBUG0(hash[i]); - } - - AWS_LOGDEBUG0("\n"); + br_sha1_context ctx; + + AWS_LOGDEBUG0("DATA:"); + + for (uint16_t i = 0; i < size; i++) + { + AWS_LOGDEBUG0(data[i]); + } + + AWS_LOGDEBUG0("\nDATA:"); + + for (uint16_t i = 0; i < size; i++) + { + AWS_LOGDEBUG0((char) data[i]); + + } + + AWS_LOGDEBUG0("\n"); + + br_sha1_init(&ctx); + br_sha1_update(&ctx, data, size); + br_sha1_out(&ctx, hash); + + AWS_LOGDEBUG0("SHA1:"); + + for (uint16_t i = 0; i < HASH_BUFFER_SIZE; i++) + { + AWS_LOGDEBUG0(hash[i]); + } + + AWS_LOGDEBUG0("\n"); } -void sha1(const char* data, uint32_t size, uint8_t hash[HASH_BUFFER_SIZE]) +void sha1(const char* data, uint32_t size, uint8_t hash[HASH_BUFFER_SIZE]) { - sha1((const uint8_t *) data, size, hash); + sha1((const uint8_t *) data, size, hash); } -void sha1(const String& data, uint8_t hash[HASH_BUFFER_SIZE]) +void sha1(const String& data, uint8_t hash[HASH_BUFFER_SIZE]) { - sha1(data.c_str(), data.length(), hash); + sha1(data.c_str(), data.length(), hash); } -String sha1(const uint8_t* data, uint32_t size) +String sha1(const uint8_t* data, uint32_t size) { - uint8_t hash[HASH_BUFFER_SIZE]; - - String hashStr((const char*)nullptr); - hashStr.reserve(HASH_BUFFER_SIZE * 2 + 1); + uint8_t hash[HASH_BUFFER_SIZE]; + + String hashStr((const char*)nullptr); + hashStr.reserve(HASH_BUFFER_SIZE * 2 + 1); - sha1(&data[0], size, &hash[0]); + sha1(&data[0], size, &hash[0]); - for(uint16_t i = 0; i < HASH_BUFFER_SIZE; i++) - { - char hex[3]; - snprintf(hex, sizeof(hex), "%02x", hash[i]); - hashStr += hex; - } + for (uint16_t i = 0; i < HASH_BUFFER_SIZE; i++) + { + char hex[3]; + snprintf(hex, sizeof(hex), "%02x", hash[i]); + hashStr += hex; + } - return hashStr; + return hashStr; } -String sha1(const char* data, uint32_t size) +String sha1(const char* data, uint32_t size) { - return sha1((const uint8_t*) data, size); + return sha1((const uint8_t*) data, size); } -String sha1(const String& data) +String sha1(const String& data) { - return sha1(data.c_str(), data.length()); + return sha1(data.c_str(), data.length()); } diff --git a/src/Crypto/Hash.h b/src/Crypto/Hash.h index c272230..9d1cc44 100644 --- a/src/Crypto/Hash.h +++ b/src/Crypto/Hash.h @@ -1,26 +1,26 @@ /** - * @file Hash.h - * @date 20.05.2015 - * @author Markus Sattler - * - * Copyright (c) 2015 Markus Sattler. All rights reserved. - * This file is part of the esp8266 core for Arduino environment. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ + @file Hash.h + @date 20.05.2015 + @author Markus Sattler + + Copyright (c) 2015 Markus Sattler. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ #pragma once diff --git a/src/Crypto/bearssl_hash.h b/src/Crypto/bearssl_hash.h index d8e4405..987be56 100644 --- a/src/Crypto/bearssl_hash.h +++ b/src/Crypto/bearssl_hash.h @@ -1,26 +1,26 @@ /* - * Copyright (c) 2016 Thomas Pornin - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ + Copyright (c) 2016 Thomas Pornin + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ #pragma once @@ -224,7 +224,7 @@ extern "C" { */ typedef struct br_hash_class_ br_hash_class; -struct br_hash_class_ +struct br_hash_class_ { /** \brief Size (in bytes) of the context structure appropriate for @@ -392,7 +392,7 @@ extern const br_hash_class br_md5_vtable; First field is a pointer to the vtable; it is set by the initialisation function. Other fields are not supposed to be accessed by user code. */ -typedef struct +typedef struct { /** \brief Pointer to vtable for this context. @@ -488,7 +488,7 @@ extern const br_hash_class br_sha1_vtable; First field is a pointer to the vtable; it is set by the initialisation function. Other fields are not supposed to be accessed by user code. */ -typedef struct +typedef struct { /** \brief Pointer to vtable for this context. @@ -584,7 +584,7 @@ extern const br_hash_class br_sha224_vtable; First field is a pointer to the vtable; it is set by the initialisation function. Other fields are not supposed to be accessed by user code. */ -typedef struct +typedef struct { /** \brief Pointer to vtable for this context. @@ -675,22 +675,22 @@ void br_sha224_set_state(br_sha224_context *ctx, const void *stb, uint64_t count extern const br_hash_class br_sha256_vtable; #ifdef BR_DOXYGEN_IGNORE +/** + \brief SHA-256 context. + + First field is a pointer to the vtable; it is set by the initialisation + function. Other fields are not supposed to be accessed by user code. +*/ +typedef struct +{ /** - \brief SHA-256 context. - - First field is a pointer to the vtable; it is set by the initialisation - function. Other fields are not supposed to be accessed by user code. + \brief Pointer to vtable for this context. */ - typedef struct - { - /** - \brief Pointer to vtable for this context. - */ - const br_hash_class *vtable; - } br_sha256_context; + const br_hash_class *vtable; +} br_sha256_context; #else - typedef br_sha224_context br_sha256_context; +typedef br_sha224_context br_sha256_context; #endif /** @@ -704,20 +704,20 @@ extern const br_hash_class br_sha256_vtable; void br_sha256_init(br_sha256_context *ctx); #ifdef BR_DOXYGEN_IGNORE - /** - \brief Inject some data bytes in a running SHA-256 computation. - - The provided context is updated with some data bytes. If the number - of bytes (`len`) is zero, then the data pointer (`data`) is ignored - and may be `NULL`, and this function does nothing. - - \param ctx pointer to the context structure. - \param data pointer to the injected data. - \param len injected data length (in bytes). - */ - void br_sha256_update(br_sha256_context *ctx, const void *data, size_t len); +/** + \brief Inject some data bytes in a running SHA-256 computation. + + The provided context is updated with some data bytes. If the number + of bytes (`len`) is zero, then the data pointer (`data`) is ignored + and may be `NULL`, and this function does nothing. + + \param ctx pointer to the context structure. + \param data pointer to the injected data. + \param len injected data length (in bytes). +*/ +void br_sha256_update(br_sha256_context *ctx, const void *data, size_t len); #else - #define br_sha256_update br_sha224_update +#define br_sha256_update br_sha224_update #endif /** @@ -735,36 +735,36 @@ void br_sha256_init(br_sha256_context *ctx); void br_sha256_out(const br_sha256_context *ctx, void *out); #ifdef BR_DOXYGEN_IGNORE - /** - \brief Save SHA-256 running state. - - The running state for SHA-256 (output of the last internal block - processing) is written in the buffer pointed to by `out`. The - number of bytes injected since the last initialisation or reset - call is returned. The context is not modified. - - \param ctx pointer to the context structure. - \param out destination buffer for the running state. - \return the injected total byte length. - */ - uint64_t br_sha256_state(const br_sha256_context *ctx, void *out); +/** + \brief Save SHA-256 running state. + + The running state for SHA-256 (output of the last internal block + processing) is written in the buffer pointed to by `out`. The + number of bytes injected since the last initialisation or reset + call is returned. The context is not modified. + + \param ctx pointer to the context structure. + \param out destination buffer for the running state. + \return the injected total byte length. +*/ +uint64_t br_sha256_state(const br_sha256_context *ctx, void *out); #else - #define br_sha256_state br_sha224_state +#define br_sha256_state br_sha224_state #endif #ifdef BR_DOXYGEN_IGNORE - /** - \brief Restore SHA-256 running state. - - The running state for SHA-256 is set to the provided values. - - \param ctx pointer to the context structure. - \param stb source buffer for the running state. - \param count the injected total byte length. - */ - void br_sha256_set_state(br_sha256_context *ctx, const void *stb, uint64_t count); +/** + \brief Restore SHA-256 running state. + + The running state for SHA-256 is set to the provided values. + + \param ctx pointer to the context structure. + \param stb source buffer for the running state. + \param count the injected total byte length. +*/ +void br_sha256_set_state(br_sha256_context *ctx, const void *stb, uint64_t count); #else - #define br_sha256_set_state br_sha224_set_state +#define br_sha256_set_state br_sha224_set_state #endif /** @@ -788,7 +788,7 @@ extern const br_hash_class br_sha384_vtable; First field is a pointer to the vtable; it is set by the initialisation function. Other fields are not supposed to be accessed by user code. */ -typedef struct +typedef struct { /** \brief Pointer to vtable for this context. @@ -880,21 +880,21 @@ void br_sha384_set_state(br_sha384_context *ctx, extern const br_hash_class br_sha512_vtable; #ifdef BR_DOXYGEN_IGNORE +/** + \brief SHA-512 context. + + First field is a pointer to the vtable; it is set by the initialisation + function. Other fields are not supposed to be accessed by user code. +*/ +typedef struct +{ /** - \brief SHA-512 context. - - First field is a pointer to the vtable; it is set by the initialisation - function. Other fields are not supposed to be accessed by user code. + \brief Pointer to vtable for this context. */ - typedef struct - { - /** - \brief Pointer to vtable for this context. - */ - const br_hash_class *vtable; - } br_sha512_context; + const br_hash_class *vtable; +} br_sha512_context; #else - typedef br_sha384_context br_sha512_context; +typedef br_sha384_context br_sha512_context; #endif /** @@ -908,20 +908,20 @@ extern const br_hash_class br_sha512_vtable; void br_sha512_init(br_sha512_context *ctx); #ifdef BR_DOXYGEN_IGNORE - /** - \brief Inject some data bytes in a running SHA-512 computation. - - The provided context is updated with some data bytes. If the number - of bytes (`len`) is zero, then the data pointer (`data`) is ignored - and may be `NULL`, and this function does nothing. - - \param ctx pointer to the context structure. - \param data pointer to the injected data. - \param len injected data length (in bytes). - */ - void br_sha512_update(br_sha512_context *ctx, const void *data, size_t len); +/** + \brief Inject some data bytes in a running SHA-512 computation. + + The provided context is updated with some data bytes. If the number + of bytes (`len`) is zero, then the data pointer (`data`) is ignored + and may be `NULL`, and this function does nothing. + + \param ctx pointer to the context structure. + \param data pointer to the injected data. + \param len injected data length (in bytes). +*/ +void br_sha512_update(br_sha512_context *ctx, const void *data, size_t len); #else - #define br_sha512_update br_sha384_update +#define br_sha512_update br_sha384_update #endif /** @@ -939,36 +939,36 @@ void br_sha512_init(br_sha512_context *ctx); void br_sha512_out(const br_sha512_context *ctx, void *out); #ifdef BR_DOXYGEN_IGNORE - /** - \brief Save SHA-512 running state. - - The running state for SHA-512 (output of the last internal block - processing) is written in the buffer pointed to by `out`. The - number of bytes injected since the last initialisation or reset - call is returned. The context is not modified. - - \param ctx pointer to the context structure. - \param out destination buffer for the running state. - \return the injected total byte length. - */ - uint64_t br_sha512_state(const br_sha512_context *ctx, void *out); +/** + \brief Save SHA-512 running state. + + The running state for SHA-512 (output of the last internal block + processing) is written in the buffer pointed to by `out`. The + number of bytes injected since the last initialisation or reset + call is returned. The context is not modified. + + \param ctx pointer to the context structure. + \param out destination buffer for the running state. + \return the injected total byte length. +*/ +uint64_t br_sha512_state(const br_sha512_context *ctx, void *out); #else - #define br_sha512_state br_sha384_state +#define br_sha512_state br_sha384_state #endif #ifdef BR_DOXYGEN_IGNORE - /** - \brief Restore SHA-512 running state. - - The running state for SHA-512 is set to the provided values. - - \param ctx pointer to the context structure. - \param stb source buffer for the running state. - \param count the injected total byte length. - */ - void br_sha512_set_state(br_sha512_context *ctx, const void *stb, uint64_t count); +/** + \brief Restore SHA-512 running state. + + The running state for SHA-512 is set to the provided values. + + \param ctx pointer to the context structure. + \param stb source buffer for the running state. + \param count the injected total byte length. +*/ +void br_sha512_set_state(br_sha512_context *ctx, const void *stb, uint64_t count); #else - #define br_sha512_set_state br_sha384_set_state +#define br_sha512_set_state br_sha384_set_state #endif /* @@ -1002,7 +1002,7 @@ extern const br_hash_class br_md5sha1_vtable; First field is a pointer to the vtable; it is set by the initialisation function. Other fields are not supposed to be accessed by user code. */ -typedef struct +typedef struct { /** \brief Pointer to vtable for this context. @@ -1085,7 +1085,7 @@ void br_md5sha1_set_state(br_md5sha1_context *ctx, The `br_hash_compat_context` type is a type which is large enough to serve as context for all standard hash functions defined above. */ -typedef union +typedef union { const br_hash_class *vtable; br_md5_context md5; @@ -1116,7 +1116,7 @@ typedef union Instead, it is configured with the vtables of the hash functions it should run. Structure fields are not supposed to be accessed directly. */ -typedef struct +typedef struct { #ifndef BR_DOXYGEN_IGNORE unsigned char buf[128]; diff --git a/src/Crypto/md5.h b/src/Crypto/md5.h index 61fc7ef..774c686 100644 --- a/src/Crypto/md5.h +++ b/src/Crypto/md5.h @@ -1,37 +1,37 @@ /** - * \file md5.h - * - * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine - * - * Copyright (C) 2009 Paul Bakker - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * + \file md5.h + + Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + + Copyright (C) 2009 Paul Bakker + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. + notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. * * Neither the names of PolarSSL or XySSL nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ #pragma once @@ -41,13 +41,13 @@ #include "AsyncWebServer_RP2040W_Debug.h" /** - * \brief MD5 context structure - */ + \brief MD5 context structure +*/ typedef struct { - unsigned long total[2]; /*!< number of bytes processed */ - unsigned long state[4]; /*!< intermediate digest state */ - unsigned char buffer[64]; /*!< data block being processed */ + unsigned long total[2]; /*!< number of bytes processed */ + unsigned long state[4]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ } md5_context; @@ -56,36 +56,36 @@ extern "C" { #endif /** - * \brief MD5 context setup - * - * \param ctx context to be initialized - */ + \brief MD5 context setup + + \param ctx context to be initialized +*/ void md5_starts( md5_context *ctx ); /** - * \brief MD5 process buffer - * - * \param ctx MD5 context - * \param input buffer holding the data - * \param ilen length of the input data - */ + \brief MD5 process buffer + + \param ctx MD5 context + \param input buffer holding the data + \param ilen length of the input data +*/ void md5_update( md5_context *ctx, const unsigned char *input, int ilen ); /** - * \brief MD5 final digest - * - * \param ctx MD5 context - * \param output MD5 checksum result - */ + \brief MD5 final digest + + \param ctx MD5 context + \param output MD5 checksum result +*/ void md5_finish( md5_context *ctx, unsigned char output[16] ); /** - * \brief Output = MD5( input buffer ) - * - * \param input buffer holding the data - * \param ilen length of the input data - * \param output MD5 checksum result - */ + \brief Output = MD5( input buffer ) + + \param input buffer holding the data + \param ilen length of the input data + \param output MD5 checksum result +*/ void md5( unsigned char *input, int ilen, unsigned char output[16] ); #ifdef __cplusplus diff --git a/src/Crypto/sha1.c b/src/Crypto/sha1.c index 4e470f3..0bd892d 100644 --- a/src/Crypto/sha1.c +++ b/src/Crypto/sha1.c @@ -1,330 +1,330 @@ -/* - * FIPS-180-1 compliant SHA-1 implementation - * - * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine - * - * Copyright (C) 2009 Paul Bakker - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the names of PolarSSL or XySSL nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * The SHA-1 standard was published by NIST in 1993. - * - * http://www.itl.nist.gov/fipspubs/fip180-1.htm - */ - -#include "sha1.h" - -#include - -/* - * 32-bit integer manipulation macros (big endian) - */ -#ifndef GET_ULONG_BE -#define GET_ULONG_BE(n,b,i) \ -{ \ - (n) = ( (unsigned long) (b)[(i) ] << 24 ) \ - | ( (unsigned long) (b)[(i) + 1] << 16 ) \ - | ( (unsigned long) (b)[(i) + 2] << 8 ) \ - | ( (unsigned long) (b)[(i) + 3] ); \ -} -#endif - -#ifndef PUT_ULONG_BE -#define PUT_ULONG_BE(n,b,i) \ -{ \ - (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ - (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ - (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ - (b)[(i) + 3] = (unsigned char) ( (n) ); \ -} -#endif - -/* - * SHA-1 context setup - */ -void sha1_starts( sha1_context *ctx ) -{ - ctx->total[0] = 0; - ctx->total[1] = 0; - - ctx->state[0] = 0x67452301; - ctx->state[1] = 0xEFCDAB89; - ctx->state[2] = 0x98BADCFE; - ctx->state[3] = 0x10325476; - ctx->state[4] = 0xC3D2E1F0; -} - -static void sha1_process( sha1_context *ctx, const unsigned char data[64] ) -{ - unsigned long temp, W[16], A, B, C, D, E; - - GET_ULONG_BE( W[ 0], data, 0 ); - GET_ULONG_BE( W[ 1], data, 4 ); - GET_ULONG_BE( W[ 2], data, 8 ); - GET_ULONG_BE( W[ 3], data, 12 ); - GET_ULONG_BE( W[ 4], data, 16 ); - GET_ULONG_BE( W[ 5], data, 20 ); - GET_ULONG_BE( W[ 6], data, 24 ); - GET_ULONG_BE( W[ 7], data, 28 ); - GET_ULONG_BE( W[ 8], data, 32 ); - GET_ULONG_BE( W[ 9], data, 36 ); - GET_ULONG_BE( W[10], data, 40 ); - GET_ULONG_BE( W[11], data, 44 ); - GET_ULONG_BE( W[12], data, 48 ); - GET_ULONG_BE( W[13], data, 52 ); - GET_ULONG_BE( W[14], data, 56 ); - GET_ULONG_BE( W[15], data, 60 ); - -#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) - -#define R(t) \ -( \ - temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ \ - W[(t - 14) & 0x0F] ^ W[ t & 0x0F], \ - ( W[t & 0x0F] = S(temp,1) ) \ -) - -#define P(a,b,c,d,e,x) \ -{ \ - e += S(a,5) + F(b,c,d) + K + x; b = S(b,30); \ -} - - A = ctx->state[0]; - B = ctx->state[1]; - C = ctx->state[2]; - D = ctx->state[3]; - E = ctx->state[4]; - -#define F(x,y,z) (z ^ (x & (y ^ z))) -#define K 0x5A827999 - - P( A, B, C, D, E, W[0] ); - P( E, A, B, C, D, W[1] ); - P( D, E, A, B, C, W[2] ); - P( C, D, E, A, B, W[3] ); - P( B, C, D, E, A, W[4] ); - P( A, B, C, D, E, W[5] ); - P( E, A, B, C, D, W[6] ); - P( D, E, A, B, C, W[7] ); - P( C, D, E, A, B, W[8] ); - P( B, C, D, E, A, W[9] ); - P( A, B, C, D, E, W[10] ); - P( E, A, B, C, D, W[11] ); - P( D, E, A, B, C, W[12] ); - P( C, D, E, A, B, W[13] ); - P( B, C, D, E, A, W[14] ); - P( A, B, C, D, E, W[15] ); - P( E, A, B, C, D, R(16) ); - P( D, E, A, B, C, R(17) ); - P( C, D, E, A, B, R(18) ); - P( B, C, D, E, A, R(19) ); - -#undef K -#undef F - -#define F(x,y,z) (x ^ y ^ z) -#define K 0x6ED9EBA1 - - P( A, B, C, D, E, R(20) ); - P( E, A, B, C, D, R(21) ); - P( D, E, A, B, C, R(22) ); - P( C, D, E, A, B, R(23) ); - P( B, C, D, E, A, R(24) ); - P( A, B, C, D, E, R(25) ); - P( E, A, B, C, D, R(26) ); - P( D, E, A, B, C, R(27) ); - P( C, D, E, A, B, R(28) ); - P( B, C, D, E, A, R(29) ); - P( A, B, C, D, E, R(30) ); - P( E, A, B, C, D, R(31) ); - P( D, E, A, B, C, R(32) ); - P( C, D, E, A, B, R(33) ); - P( B, C, D, E, A, R(34) ); - P( A, B, C, D, E, R(35) ); - P( E, A, B, C, D, R(36) ); - P( D, E, A, B, C, R(37) ); - P( C, D, E, A, B, R(38) ); - P( B, C, D, E, A, R(39) ); - -#undef K -#undef F - -#define F(x,y,z) ((x & y) | (z & (x | y))) -#define K 0x8F1BBCDC - - P( A, B, C, D, E, R(40) ); - P( E, A, B, C, D, R(41) ); - P( D, E, A, B, C, R(42) ); - P( C, D, E, A, B, R(43) ); - P( B, C, D, E, A, R(44) ); - P( A, B, C, D, E, R(45) ); - P( E, A, B, C, D, R(46) ); - P( D, E, A, B, C, R(47) ); - P( C, D, E, A, B, R(48) ); - P( B, C, D, E, A, R(49) ); - P( A, B, C, D, E, R(50) ); - P( E, A, B, C, D, R(51) ); - P( D, E, A, B, C, R(52) ); - P( C, D, E, A, B, R(53) ); - P( B, C, D, E, A, R(54) ); - P( A, B, C, D, E, R(55) ); - P( E, A, B, C, D, R(56) ); - P( D, E, A, B, C, R(57) ); - P( C, D, E, A, B, R(58) ); - P( B, C, D, E, A, R(59) ); - -#undef K -#undef F - -#define F(x,y,z) (x ^ y ^ z) -#define K 0xCA62C1D6 - - P( A, B, C, D, E, R(60) ); - P( E, A, B, C, D, R(61) ); - P( D, E, A, B, C, R(62) ); - P( C, D, E, A, B, R(63) ); - P( B, C, D, E, A, R(64) ); - P( A, B, C, D, E, R(65) ); - P( E, A, B, C, D, R(66) ); - P( D, E, A, B, C, R(67) ); - P( C, D, E, A, B, R(68) ); - P( B, C, D, E, A, R(69) ); - P( A, B, C, D, E, R(70) ); - P( E, A, B, C, D, R(71) ); - P( D, E, A, B, C, R(72) ); - P( C, D, E, A, B, R(73) ); - P( B, C, D, E, A, R(74) ); - P( A, B, C, D, E, R(75) ); - P( E, A, B, C, D, R(76) ); - P( D, E, A, B, C, R(77) ); - P( C, D, E, A, B, R(78) ); - P( B, C, D, E, A, R(79) ); - -#undef K -#undef F - - ctx->state[0] += A; - ctx->state[1] += B; - ctx->state[2] += C; - ctx->state[3] += D; - ctx->state[4] += E; -} - -/* - * SHA-1 process buffer - */ -void sha1_update( sha1_context *ctx, const unsigned char *input, int ilen ) -{ - int fill; - unsigned long left; - - if( ilen <= 0 ) - return; - - left = ctx->total[0] & 0x3F; - fill = 64 - left; - - ctx->total[0] += ilen; - ctx->total[0] &= 0xFFFFFFFF; - - if( ctx->total[0] < (unsigned long) ilen ) - ctx->total[1]++; - - if( left && ilen >= fill ) - { - memcpy( (void *) (ctx->buffer + left), - input, fill ); - sha1_process( ctx, ctx->buffer ); - input += fill; - ilen -= fill; - left = 0; - } - - while( ilen >= 64 ) - { - sha1_process( ctx, input ); - input += 64; - ilen -= 64; - } - - if( ilen > 0 ) - { - memcpy( (void *) (ctx->buffer + left), - input, ilen ); - } -} - -static const unsigned char sha1_padding[64] = -{ - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -/* - * SHA-1 final digest - */ -void sha1_finish( sha1_context *ctx, unsigned char output[SHA1_BUFFER_SIZE] ) -{ - unsigned long last, padn; - unsigned long high, low; - unsigned char msglen[8]; - - high = ( ctx->total[0] >> 29 ) - | ( ctx->total[1] << 3 ); - low = ( ctx->total[0] << 3 ); - - PUT_ULONG_BE( high, msglen, 0 ); - PUT_ULONG_BE( low, msglen, 4 ); - - last = ctx->total[0] & 0x3F; - padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); - - sha1_update( ctx, sha1_padding, padn ); - sha1_update( ctx, msglen, 8 ); - - PUT_ULONG_BE( ctx->state[0], output, 0 ); - PUT_ULONG_BE( ctx->state[1], output, 4 ); - PUT_ULONG_BE( ctx->state[2], output, 8 ); - PUT_ULONG_BE( ctx->state[3], output, 12 ); - PUT_ULONG_BE( ctx->state[4], output, 16 ); -} - -/* - * output = SHA-1( input buffer ) - */ -void sha1( unsigned char *input, int ilen, unsigned char output[SHA1_BUFFER_SIZE] ) -{ - sha1_context ctx; - - sha1_starts( &ctx ); - sha1_update( &ctx, input, ilen ); - sha1_finish( &ctx, output ); -} +/* + FIPS-180-1 compliant SHA-1 implementation + + Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + + Copyright (C) 2009 Paul Bakker + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * * Neither the names of PolarSSL or XySSL nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/* + The SHA-1 standard was published by NIST in 1993. + + http://www.itl.nist.gov/fipspubs/fip180-1.htm +*/ + +#include "sha1.h" + +#include + +/* + 32-bit integer manipulation macros (big endian) +*/ +#ifndef GET_ULONG_BE +#define GET_ULONG_BE(n,b,i) \ +{ \ + (n) = ( (unsigned long) (b)[(i) ] << 24 ) \ + | ( (unsigned long) (b)[(i) + 1] << 16 ) \ + | ( (unsigned long) (b)[(i) + 2] << 8 ) \ + | ( (unsigned long) (b)[(i) + 3] ); \ +} +#endif + +#ifndef PUT_ULONG_BE +#define PUT_ULONG_BE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) ); \ +} +#endif + +/* + SHA-1 context setup +*/ +void sha1_starts( sha1_context *ctx ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; +} + +static void sha1_process( sha1_context *ctx, const unsigned char data[64] ) +{ + unsigned long temp, W[16], A, B, C, D, E; + + GET_ULONG_BE( W[ 0], data, 0 ); + GET_ULONG_BE( W[ 1], data, 4 ); + GET_ULONG_BE( W[ 2], data, 8 ); + GET_ULONG_BE( W[ 3], data, 12 ); + GET_ULONG_BE( W[ 4], data, 16 ); + GET_ULONG_BE( W[ 5], data, 20 ); + GET_ULONG_BE( W[ 6], data, 24 ); + GET_ULONG_BE( W[ 7], data, 28 ); + GET_ULONG_BE( W[ 8], data, 32 ); + GET_ULONG_BE( W[ 9], data, 36 ); + GET_ULONG_BE( W[10], data, 40 ); + GET_ULONG_BE( W[11], data, 44 ); + GET_ULONG_BE( W[12], data, 48 ); + GET_ULONG_BE( W[13], data, 52 ); + GET_ULONG_BE( W[14], data, 56 ); + GET_ULONG_BE( W[15], data, 60 ); + +#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + +#define R(t) \ +( \ + temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ \ + W[(t - 14) & 0x0F] ^ W[ t & 0x0F], \ + ( W[t & 0x0F] = S(temp,1) ) \ +) + +#define P(a,b,c,d,e,x) \ +{ \ + e += S(a,5) + F(b,c,d) + K + x; b = S(b,30); \ +} + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + +#define F(x,y,z) (z ^ (x & (y ^ z))) +#define K 0x5A827999 + + P( A, B, C, D, E, W[0] ); + P( E, A, B, C, D, W[1] ); + P( D, E, A, B, C, W[2] ); + P( C, D, E, A, B, W[3] ); + P( B, C, D, E, A, W[4] ); + P( A, B, C, D, E, W[5] ); + P( E, A, B, C, D, W[6] ); + P( D, E, A, B, C, W[7] ); + P( C, D, E, A, B, W[8] ); + P( B, C, D, E, A, W[9] ); + P( A, B, C, D, E, W[10] ); + P( E, A, B, C, D, W[11] ); + P( D, E, A, B, C, W[12] ); + P( C, D, E, A, B, W[13] ); + P( B, C, D, E, A, W[14] ); + P( A, B, C, D, E, W[15] ); + P( E, A, B, C, D, R(16) ); + P( D, E, A, B, C, R(17) ); + P( C, D, E, A, B, R(18) ); + P( B, C, D, E, A, R(19) ); + +#undef K +#undef F + +#define F(x,y,z) (x ^ y ^ z) +#define K 0x6ED9EBA1 + + P( A, B, C, D, E, R(20) ); + P( E, A, B, C, D, R(21) ); + P( D, E, A, B, C, R(22) ); + P( C, D, E, A, B, R(23) ); + P( B, C, D, E, A, R(24) ); + P( A, B, C, D, E, R(25) ); + P( E, A, B, C, D, R(26) ); + P( D, E, A, B, C, R(27) ); + P( C, D, E, A, B, R(28) ); + P( B, C, D, E, A, R(29) ); + P( A, B, C, D, E, R(30) ); + P( E, A, B, C, D, R(31) ); + P( D, E, A, B, C, R(32) ); + P( C, D, E, A, B, R(33) ); + P( B, C, D, E, A, R(34) ); + P( A, B, C, D, E, R(35) ); + P( E, A, B, C, D, R(36) ); + P( D, E, A, B, C, R(37) ); + P( C, D, E, A, B, R(38) ); + P( B, C, D, E, A, R(39) ); + +#undef K +#undef F + +#define F(x,y,z) ((x & y) | (z & (x | y))) +#define K 0x8F1BBCDC + + P( A, B, C, D, E, R(40) ); + P( E, A, B, C, D, R(41) ); + P( D, E, A, B, C, R(42) ); + P( C, D, E, A, B, R(43) ); + P( B, C, D, E, A, R(44) ); + P( A, B, C, D, E, R(45) ); + P( E, A, B, C, D, R(46) ); + P( D, E, A, B, C, R(47) ); + P( C, D, E, A, B, R(48) ); + P( B, C, D, E, A, R(49) ); + P( A, B, C, D, E, R(50) ); + P( E, A, B, C, D, R(51) ); + P( D, E, A, B, C, R(52) ); + P( C, D, E, A, B, R(53) ); + P( B, C, D, E, A, R(54) ); + P( A, B, C, D, E, R(55) ); + P( E, A, B, C, D, R(56) ); + P( D, E, A, B, C, R(57) ); + P( C, D, E, A, B, R(58) ); + P( B, C, D, E, A, R(59) ); + +#undef K +#undef F + +#define F(x,y,z) (x ^ y ^ z) +#define K 0xCA62C1D6 + + P( A, B, C, D, E, R(60) ); + P( E, A, B, C, D, R(61) ); + P( D, E, A, B, C, R(62) ); + P( C, D, E, A, B, R(63) ); + P( B, C, D, E, A, R(64) ); + P( A, B, C, D, E, R(65) ); + P( E, A, B, C, D, R(66) ); + P( D, E, A, B, C, R(67) ); + P( C, D, E, A, B, R(68) ); + P( B, C, D, E, A, R(69) ); + P( A, B, C, D, E, R(70) ); + P( E, A, B, C, D, R(71) ); + P( D, E, A, B, C, R(72) ); + P( C, D, E, A, B, R(73) ); + P( B, C, D, E, A, R(74) ); + P( A, B, C, D, E, R(75) ); + P( E, A, B, C, D, R(76) ); + P( D, E, A, B, C, R(77) ); + P( C, D, E, A, B, R(78) ); + P( B, C, D, E, A, R(79) ); + +#undef K +#undef F + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; +} + +/* + SHA-1 process buffer +*/ +void sha1_update( sha1_context *ctx, const unsigned char *input, int ilen ) +{ + int fill; + unsigned long left; + + if ( ilen <= 0 ) + return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if ( ctx->total[0] < (unsigned long) ilen ) + ctx->total[1]++; + + if ( left && ilen >= fill ) + { + memcpy( (void *) (ctx->buffer + left), + input, fill ); + sha1_process( ctx, ctx->buffer ); + input += fill; + ilen -= fill; + left = 0; + } + + while ( ilen >= 64 ) + { + sha1_process( ctx, input ); + input += 64; + ilen -= 64; + } + + if ( ilen > 0 ) + { + memcpy( (void *) (ctx->buffer + left), + input, ilen ); + } +} + +static const unsigned char sha1_padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + SHA-1 final digest +*/ +void sha1_finish( sha1_context *ctx, unsigned char output[SHA1_BUFFER_SIZE] ) +{ + unsigned long last, padn; + unsigned long high, low; + unsigned char msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_ULONG_BE( high, msglen, 0 ); + PUT_ULONG_BE( low, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + sha1_update( ctx, sha1_padding, padn ); + sha1_update( ctx, msglen, 8 ); + + PUT_ULONG_BE( ctx->state[0], output, 0 ); + PUT_ULONG_BE( ctx->state[1], output, 4 ); + PUT_ULONG_BE( ctx->state[2], output, 8 ); + PUT_ULONG_BE( ctx->state[3], output, 12 ); + PUT_ULONG_BE( ctx->state[4], output, 16 ); +} + +/* + output = SHA-1( input buffer ) +*/ +void sha1( unsigned char *input, int ilen, unsigned char output[SHA1_BUFFER_SIZE] ) +{ + sha1_context ctx; + + sha1_starts( &ctx ); + sha1_update( &ctx, input, ilen ); + sha1_finish( &ctx, output ); +} diff --git a/src/Crypto/sha1.h b/src/Crypto/sha1.h index 6dc1fef..eb2f121 100644 --- a/src/Crypto/sha1.h +++ b/src/Crypto/sha1.h @@ -1,101 +1,101 @@ -/** - * \file sha1.h - * - * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine - * - * Copyright (C) 2009 Paul Bakker - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the names of PolarSSL or XySSL nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#ifndef LWIP_INCLUDED_POLARSSL_SHA1_H -#define LWIP_INCLUDED_POLARSSL_SHA1_H - -#include "AsyncWebServer_RP2040W_Debug.h" - -#ifdef SHA1_BUFFER_SIZE - #undef SHA1_BUFFER_SIZE -#endif - -#define SHA1_BUFFER_SIZE 20 - -/** - * \brief SHA-1 context structure - */ -typedef struct -{ - unsigned long total[2]; /*!< number of bytes processed */ - unsigned long state[5]; /*!< intermediate digest state */ - unsigned char buffer[64]; /*!< data block being processed */ -} -sha1_context; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief SHA-1 context setup - * - * \param ctx context to be initialized - */ -void sha1_starts( sha1_context *ctx ); - -/** - * \brief SHA-1 process buffer - * - * \param ctx SHA-1 context - * \param input buffer holding the data - * \param ilen length of the input data - */ -void sha1_update( sha1_context *ctx, const unsigned char *input, int ilen ); - -/** - * \brief SHA-1 final digest - * - * \param ctx SHA-1 context - * \param output SHA-1 checksum result - */ -void sha1_finish( sha1_context *ctx, unsigned char output[SHA1_BUFFER_SIZE] ); - -/** - * \brief Output = SHA-1( input buffer ) - * - * \param input buffer holding the data - * \param ilen length of the input data - * \param output SHA-1 checksum result - */ -void sha1( unsigned char *input, int ilen, unsigned char output[SHA1_BUFFER_SIZE] ); - -#ifdef __cplusplus -} -#endif - -#endif /* LWIP_INCLUDED_POLARSSL_SHA1_H */ +/** + \file sha1.h + + Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + + Copyright (C) 2009 Paul Bakker + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * * Neither the names of PolarSSL or XySSL nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#ifndef LWIP_INCLUDED_POLARSSL_SHA1_H +#define LWIP_INCLUDED_POLARSSL_SHA1_H + +#include "AsyncWebServer_RP2040W_Debug.h" + +#ifdef SHA1_BUFFER_SIZE + #undef SHA1_BUFFER_SIZE +#endif + +#define SHA1_BUFFER_SIZE 20 + +/** + \brief SHA-1 context structure +*/ +typedef struct +{ + unsigned long total[2]; /*!< number of bytes processed */ + unsigned long state[5]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ +} +sha1_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + \brief SHA-1 context setup + + \param ctx context to be initialized +*/ +void sha1_starts( sha1_context *ctx ); + +/** + \brief SHA-1 process buffer + + \param ctx SHA-1 context + \param input buffer holding the data + \param ilen length of the input data +*/ +void sha1_update( sha1_context *ctx, const unsigned char *input, int ilen ); + +/** + \brief SHA-1 final digest + + \param ctx SHA-1 context + \param output SHA-1 checksum result +*/ +void sha1_finish( sha1_context *ctx, unsigned char output[SHA1_BUFFER_SIZE] ); + +/** + \brief Output = SHA-1( input buffer ) + + \param input buffer holding the data + \param ilen length of the input data + \param output SHA-1 checksum result +*/ +void sha1( unsigned char *input, int ilen, unsigned char output[SHA1_BUFFER_SIZE] ); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_INCLUDED_POLARSSL_SHA1_H */ diff --git a/src/StringArray_RP2040W.h b/src/StringArray_RP2040W.h index d55b977..b588ca4 100644 --- a/src/StringArray_RP2040W.h +++ b/src/StringArray_RP2040W.h @@ -1,16 +1,16 @@ /**************************************************************************************************************************** StringArray_RP2040W.h - + For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license - - Version: 1.4.0 - + + Version: 1.4.1 + Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 13/08/2022 Initial coding for RP2040W with CYW43439 WiFi @@ -24,6 +24,7 @@ 1.3.0 K Hoang 10/10/2022 Fix crash when using AsyncWebSockets server 1.3.1 K Hoang 10/10/2022 Improve robustness of AsyncWebSockets server 1.4.0 K Hoang 20/10/2022 Add LittleFS functions such as AsyncFSWebServer + 1.4.1 K Hoang 10/11/2022 Add examples to demo how to use beginChunkedResponse() to send in chunks *****************************************************************************************************************************/ #pragma once @@ -89,7 +90,7 @@ class LinkedList inline Iterator& operator ++() { _node = _node->next; - + return *this; } @@ -135,7 +136,7 @@ class LinkedList ///////////////////////////////////////////////// LinkedList(OnRemove onRemove) : _root(nullptr), _onRemove(onRemove) {} - + ///////////////////////////////////////////////// ~LinkedList() {} @@ -211,7 +212,7 @@ class LinkedList it = it->next; } - + return i; } @@ -259,7 +260,7 @@ class LinkedList } delete it; - + return true; } @@ -296,7 +297,7 @@ class LinkedList } delete it; - + return true; } diff --git a/src/libb64/cdecode.c b/src/libb64/cdecode.c index 2379a01..724ebe5 100644 --- a/src/libb64/cdecode.c +++ b/src/libb64/cdecode.c @@ -5,9 +5,9 @@ For details, see http://sourceforge.net/projects/libb64 For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -18,7 +18,8 @@ int base64_decode_value(int value_in) { static const char decoding[] = - { 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, -1, 0, 1, 2, + { + 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 @@ -65,8 +66,8 @@ int base64_decode_block(const char* code_in, const int length_in, char* plaintex } while (fragment < 0); *plainchar = (fragment & 0x03f) << 2; - - // fall through + + // fall through case step_b: do @@ -83,8 +84,8 @@ int base64_decode_block(const char* code_in, const int length_in, char* plaintex *plainchar++ |= (fragment & 0x030) >> 4; *plainchar = (fragment & 0x00f) << 4; - - // fall through + + // fall through case step_c: do @@ -101,8 +102,8 @@ int base64_decode_block(const char* code_in, const int length_in, char* plaintex *plainchar++ |= (fragment & 0x03c) >> 2; *plainchar = (fragment & 0x003) << 6; - - // fall through + + // fall through case step_d: do @@ -118,7 +119,7 @@ int base64_decode_block(const char* code_in, const int length_in, char* plaintex } while (fragment < 0); *plainchar++ |= (fragment & 0x03f); - + // fall through } } @@ -127,7 +128,8 @@ int base64_decode_block(const char* code_in, const int length_in, char* plaintex return plainchar - plaintext_out; } -int base64_decode_chars(const char* code_in, const int length_in, char* plaintext_out) { +int base64_decode_chars(const char* code_in, const int length_in, char* plaintext_out) +{ base64_decodestate _state; base64_init_decodestate(&_state); diff --git a/src/libb64/cdecode.h b/src/libb64/cdecode.h index b8645f6..8256d31 100644 --- a/src/libb64/cdecode.h +++ b/src/libb64/cdecode.h @@ -5,9 +5,9 @@ For details, see http://sourceforge.net/projects/libb64 For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -26,12 +26,12 @@ extern "C" { #endif -typedef enum +typedef enum { step_a, step_b, step_c, step_d } base64_decodestep; -typedef struct +typedef struct { base64_decodestep step; char plainchar; diff --git a/src/libb64/cencode.c b/src/libb64/cencode.c index 310ba38..239e78f 100644 --- a/src/libb64/cencode.c +++ b/src/libb64/cencode.c @@ -5,9 +5,9 @@ For details, see http://sourceforge.net/projects/libb64 For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -60,8 +60,8 @@ int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, result = (fragment & 0x0fc) >> 2; *codechar++ = base64_encode_value(result); result = (fragment & 0x003) << 4; - - // fall through + + // fall through case step_B: if (plainchar == plaintextend) @@ -75,9 +75,9 @@ int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, result |= (fragment & 0x0f0) >> 4; *codechar++ = base64_encode_value(result); result = (fragment & 0x00f) << 2; - - // fall through - + + // fall through + case step_C: if (plainchar == plaintextend) { @@ -99,7 +99,7 @@ int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, *codechar++ = '\n'; state_in->stepcount = 0; } - + // fall through } } @@ -119,10 +119,12 @@ int base64_encode_blockend(char* code_out, base64_encodestate* state_in) *codechar++ = '='; *codechar++ = '='; break; + case step_C: *codechar++ = base64_encode_value(state_in->result); *codechar++ = '='; break; + case step_A: break; } diff --git a/src/libb64/cencode.h b/src/libb64/cencode.h index 4ac6a8f..9fbbd9b 100644 --- a/src/libb64/cencode.h +++ b/src/libb64/cencode.h @@ -5,9 +5,9 @@ For details, see http://sourceforge.net/projects/libb64 For RP2040W with CYW43439 WiFi - + AsyncWebServer_RP2040W is a library for the RP2040W with CYW43439 WiFi - + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_RP2040W Licensed under GPLv3 license @@ -26,12 +26,12 @@ extern "C" { #endif -typedef enum +typedef enum { step_A, step_B, step_C } base64_encodestep; -typedef struct +typedef struct { base64_encodestep step; char result; diff --git a/utils/astyle_library.conf b/utils/astyle_library.conf new file mode 100644 index 0000000..8a73bc2 --- /dev/null +++ b/utils/astyle_library.conf @@ -0,0 +1,70 @@ +# Code formatting rules for Arduino libraries, modified from for KH libraries: +# +# https://github.com/arduino/Arduino/blob/master/build/shared/examples_formatter.conf +# + +# astyle --style=allman -s2 -t2 -C -S -xW -Y -M120 -f -p -xg -H -xb -c --xC120 -xL *.h *.cpp *.ino + +--mode=c +--lineend=linux +--style=allman + +# -r or -R +#--recursive + +# -c => Converts tabs into spaces +convert-tabs + +# -s2 => 2 spaces indentation +--indent=spaces=2 + +# -t2 => tab =2 spaces +#--indent=tab=2 + +# -C +--indent-classes + +# -S +--indent-switches + +# -xW +--indent-preproc-block + +# -Y => indent classes, switches (and cases), comments starting at column 1 +--indent-col1-comments + +# -M120 => maximum of 120 spaces to indent a continuation line +--max-continuation-indent=120 + +# -xC120 => max‑code‑length will break a line if the code exceeds # characters +--max-code-length=120 + +# -f => +--break-blocks + +# -p => put a space around operators +--pad-oper + +# -xg => Insert space padding after commas +--pad-comma + +# -H => put a space after if/for/while +pad-header + +# -xb => Break one line headers (e.g. if/for/while) +--break-one-line-headers + +# -c => Converts tabs into spaces +#--convert-tabs + +# if you like one-liners, keep them +#keep-one-line-statements + +# -xV +--attach-closing-while + +#unpad-paren + +# -xp +remove-comment-prefix + diff --git a/utils/restyle.sh b/utils/restyle.sh new file mode 100644 index 0000000..bcd846f --- /dev/null +++ b/utils/restyle.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +for dir in . ; do + find $dir -type f \( -name "*.c" -o -name "*.h" -o -name "*.cpp" -o -name "*.ino" \) -exec astyle --suffix=none --options=./utils/astyle_library.conf \{\} \; +done +