chore: import local project into Gitea
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
#include <DNSServer.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
// Shows how to use AsyncResponseStream.
|
||||
// The internal buffer will be allocated and data appended to it,
|
||||
// until the response is sent, then this buffer is read and committed on the network.
|
||||
//
|
||||
// curl -v http://192.168.4.1/
|
||||
//
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
AsyncResponseStream *response = request->beginResponseStream("plain/text", 40 * 1024);
|
||||
for (int i = 0; i < 32 * 1024; i++) {
|
||||
response->write('a');
|
||||
}
|
||||
request->send(response);
|
||||
});
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Shows how to trigger an async client request from a browser request and send the client response back to the browser through websocket
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
#define WIFI_SSID "IoT"
|
||||
#define WIFI_PASSWORD ""
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
static AsyncWebSocketMessageHandler wsHandler;
|
||||
static AsyncWebSocket ws("/ws", wsHandler.eventHandler());
|
||||
|
||||
static const char *htmlContent PROGMEM = R"(
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>WebSocket Tunnel Example</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>WebSocket Tunnel Example</h1>
|
||||
<div><input type="text" id="url" value="http://www.google.com" /></div>
|
||||
<div><button onclick='fetch()'>Fetch</button></div>
|
||||
<div><pre id="response"></pre></div>
|
||||
<script>
|
||||
var ws = new WebSocket('/ws');
|
||||
ws.binaryType = "arraybuffer";
|
||||
ws.onopen = function() {
|
||||
console.log("WebSocket connected");
|
||||
};
|
||||
ws.onmessage = function(event) {
|
||||
let uint8array = new Uint8Array(event.data);
|
||||
let string = new TextDecoder().decode(uint8array);
|
||||
console.log("WebSocket message: " + string);
|
||||
document.getElementById("response").innerText += string;
|
||||
};
|
||||
ws.onclose = function() {
|
||||
console.log("WebSocket closed");
|
||||
};
|
||||
ws.onerror = function(error) {
|
||||
console.log("WebSocket error: " + error);
|
||||
};
|
||||
function fetch() {
|
||||
document.getElementById("response").innerText = "";
|
||||
var url = document.getElementById("url").value;
|
||||
ws.send(url);
|
||||
console.log("WebSocket sent: " + url);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
static const size_t htmlContentLength = strlen_P(htmlContent);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
}
|
||||
Serial.println("Connected to WiFi!");
|
||||
Serial.println(WiFi.localIP());
|
||||
#endif
|
||||
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
request->send(200, "text/html", (const uint8_t *)htmlContent, htmlContentLength);
|
||||
});
|
||||
|
||||
wsHandler.onMessage([](AsyncWebSocket *server, AsyncWebSocketClient *wsClient, const uint8_t *data, size_t len) {
|
||||
String url;
|
||||
String host;
|
||||
String port;
|
||||
String path;
|
||||
|
||||
url.concat((const char *)data, len);
|
||||
|
||||
if (!url.startsWith("http://")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!url.endsWith("/")) {
|
||||
url += "/";
|
||||
}
|
||||
|
||||
// Parse the URL to extract the host and port
|
||||
int start = url.indexOf("://") + 3;
|
||||
int end = url.indexOf("/", start);
|
||||
if (end == -1) {
|
||||
end = url.length();
|
||||
}
|
||||
String hostPort = url.substring(start, end);
|
||||
int colonIndex = hostPort.indexOf(":");
|
||||
if (colonIndex != -1) {
|
||||
host = hostPort.substring(0, colonIndex);
|
||||
port = hostPort.substring(colonIndex + 1);
|
||||
} else {
|
||||
host = hostPort;
|
||||
port = "80"; // Default HTTP port
|
||||
}
|
||||
path = url.substring(end);
|
||||
|
||||
Serial.printf("Host: %s\n", host.c_str());
|
||||
Serial.printf("Port: %s\n", port.c_str());
|
||||
Serial.printf("Path: %s\n", path.c_str());
|
||||
|
||||
// Ensure client does not get deleted while the websocket holds a reference to it
|
||||
std::shared_ptr<AsyncClient> *safeAsyncClient = new std::shared_ptr<AsyncClient>(std::make_shared<AsyncClient>());
|
||||
AsyncClient *asyncClient = safeAsyncClient->get();
|
||||
|
||||
asyncClient->onDisconnect([safeAsyncClient](void *arg, AsyncClient *client) {
|
||||
Serial.printf("Tunnel disconnected!\n");
|
||||
delete safeAsyncClient;
|
||||
});
|
||||
|
||||
// register a callback when an error occurs
|
||||
// note: onDisconnect also called on error
|
||||
asyncClient->onError([](void *arg, AsyncClient *client, int8_t error) {
|
||||
Serial.printf("Tunnel error: %s\n", client->errorToString(error));
|
||||
});
|
||||
|
||||
// register a callback when data arrives, to accumulate it
|
||||
asyncClient->onPacket(
|
||||
[safeAsyncClient](void *arg, AsyncClient *, struct pbuf *pb) {
|
||||
std::shared_ptr<AsyncClient> safeAsyncClientRef = *safeAsyncClient; // add a reference
|
||||
AsyncWebSocketClient *wsClient = (AsyncWebSocketClient *)arg;
|
||||
Serial.printf("Tunnel received %u bytes\n", pb->len);
|
||||
AsyncWebSocketSharedBuffer wsBuffer =
|
||||
AsyncWebSocketSharedBuffer(new std::vector<uint8_t>((uint8_t *)pb->payload, (uint8_t *)pb->payload + pb->len), [=](std::vector<uint8_t> *bufptr) {
|
||||
delete bufptr;
|
||||
Serial.printf("ACK %u bytes\n", pb->len);
|
||||
safeAsyncClientRef->ackPacket(pb);
|
||||
});
|
||||
Serial.printf("Tunnel sending %u bytes\n", wsBuffer->size());
|
||||
Serial.printf("%.*s\n", (int)wsBuffer->size(), wsBuffer->data());
|
||||
wsClient->binary(std::move(wsBuffer));
|
||||
},
|
||||
wsClient
|
||||
);
|
||||
|
||||
asyncClient->onConnect([=](void *arg, AsyncClient *client) {
|
||||
Serial.printf("Tunnel connected!\n");
|
||||
|
||||
client->write("GET ");
|
||||
client->write(path.c_str());
|
||||
client->write(" HTTP/1.1\r\n");
|
||||
client->write("Host: ");
|
||||
client->write(host.c_str());
|
||||
client->write(":");
|
||||
client->write(port.c_str());
|
||||
client->write("\r\n");
|
||||
client->write("User-Agent: ESP32\r\n");
|
||||
client->write("Accept: */*\r\n");
|
||||
client->write("Connection: close\r\n");
|
||||
client->write("\r\n");
|
||||
});
|
||||
|
||||
Serial.printf("Fetching: http://%s:%s%s\n", host.c_str(), port.c_str(), path.c_str());
|
||||
|
||||
if (!asyncClient->connect(host.c_str(), port.toInt())) {
|
||||
Serial.printf("Failed to open tunnel!\n");
|
||||
delete safeAsyncClient;
|
||||
}
|
||||
});
|
||||
|
||||
wsHandler.onConnect([](AsyncWebSocket *server, AsyncWebSocketClient *client) {
|
||||
Serial.printf("Client %" PRIu32 " connected\n", client->id());
|
||||
client->binary("WebSocket connected!");
|
||||
});
|
||||
|
||||
wsHandler.onDisconnect([](AsyncWebSocket *server, uint32_t clientId) {
|
||||
Serial.printf("Client %" PRIu32 " disconnected\n", clientId);
|
||||
});
|
||||
|
||||
server.addHandler(&ws);
|
||||
server.begin();
|
||||
Serial.println("Server started!");
|
||||
}
|
||||
|
||||
static uint32_t lastHeap = 0;
|
||||
|
||||
void loop() {
|
||||
ws.cleanupClients(2);
|
||||
|
||||
#ifdef ESP32
|
||||
uint32_t now = millis();
|
||||
if (now - lastHeap >= 2000) {
|
||||
Serial.printf("Uptime: %3lu s, Free heap: %" PRIu32 "\n", millis() / 1000, ESP.getFreeHeap());
|
||||
lastHeap = now;
|
||||
}
|
||||
#endif
|
||||
|
||||
delay(500);
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Authentication and authorization middlewares
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
// basicAuth
|
||||
static AsyncAuthenticationMiddleware basicAuth;
|
||||
static AsyncAuthenticationMiddleware basicAuthHash;
|
||||
|
||||
// simple digest authentication
|
||||
static AsyncAuthenticationMiddleware digestAuth;
|
||||
static AsyncAuthenticationMiddleware digestAuthHash;
|
||||
|
||||
// complex authentication which adds request attributes for the next middlewares and handler
|
||||
static AsyncMiddlewareFunction complexAuth([](AsyncWebServerRequest *request, ArMiddlewareNext next) {
|
||||
if (!request->authenticate("user", "password")) {
|
||||
return request->requestAuthentication();
|
||||
}
|
||||
|
||||
// add attributes to the request for the next middlewares and handler
|
||||
request->setAttribute("user", "Mathieu");
|
||||
request->setAttribute("role", "staff");
|
||||
if (request->hasParam("token")) {
|
||||
request->setAttribute("token", request->getParam("token")->value().c_str());
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
static AsyncAuthorizationMiddleware authz([](AsyncWebServerRequest *request) {
|
||||
return request->getAttribute("token") == "123";
|
||||
});
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
// basic authentication
|
||||
basicAuth.setUsername("admin");
|
||||
basicAuth.setPassword("admin");
|
||||
basicAuth.setRealm("MyApp");
|
||||
basicAuth.setAuthFailureMessage("Authentication failed");
|
||||
basicAuth.setAuthType(AsyncAuthType::AUTH_BASIC);
|
||||
basicAuth.generateHash(); // precompute hash (optional but recommended)
|
||||
|
||||
// basic authentication with hash
|
||||
basicAuthHash.setUsername("admin");
|
||||
basicAuthHash.setPasswordHash("YWRtaW46YWRtaW4="); // BASE64(admin:admin)
|
||||
basicAuthHash.setRealm("MyApp");
|
||||
basicAuthHash.setAuthFailureMessage("Authentication failed");
|
||||
basicAuthHash.setAuthType(AsyncAuthType::AUTH_BASIC);
|
||||
|
||||
// digest authentication
|
||||
digestAuth.setUsername("admin");
|
||||
digestAuth.setPassword("admin");
|
||||
digestAuth.setRealm("MyApp");
|
||||
digestAuth.setAuthFailureMessage("Authentication failed");
|
||||
digestAuth.setAuthType(AsyncAuthType::AUTH_DIGEST);
|
||||
digestAuth.generateHash(); // precompute hash (optional but recommended)
|
||||
|
||||
// digest authentication with hash
|
||||
digestAuthHash.setUsername("admin");
|
||||
digestAuthHash.setPasswordHash("f499b71f9a36d838b79268e145e132f7"); // MD5(user:realm:pass)
|
||||
digestAuthHash.setRealm("MyApp");
|
||||
digestAuthHash.setAuthFailureMessage("Authentication failed");
|
||||
digestAuthHash.setAuthType(AsyncAuthType::AUTH_DIGEST);
|
||||
|
||||
// basic authentication method
|
||||
// curl -v -u admin:admin http://192.168.4.1/auth-basic
|
||||
server
|
||||
.on(
|
||||
"/auth-basic", HTTP_GET,
|
||||
[](AsyncWebServerRequest *request) {
|
||||
request->send(200, "text/plain", "Hello, world!");
|
||||
}
|
||||
)
|
||||
.addMiddleware(&basicAuth);
|
||||
|
||||
// basic authentication method with hash
|
||||
// curl -v -u admin:admin http://192.168.4.1/auth-basic-hash
|
||||
server
|
||||
.on(
|
||||
"/auth-basic-hash", HTTP_GET,
|
||||
[](AsyncWebServerRequest *request) {
|
||||
request->send(200, "text/plain", "Hello, world!");
|
||||
}
|
||||
)
|
||||
.addMiddleware(&basicAuthHash);
|
||||
|
||||
// digest authentication
|
||||
// curl -v -u admin:admin --digest http://192.168.4.1/auth-digest
|
||||
server
|
||||
.on(
|
||||
"/auth-digest", HTTP_GET,
|
||||
[](AsyncWebServerRequest *request) {
|
||||
request->send(200, "text/plain", "Hello, world!");
|
||||
}
|
||||
)
|
||||
.addMiddleware(&digestAuth);
|
||||
|
||||
// digest authentication with hash
|
||||
// curl -v -u admin:admin --digest http://192.168.4.1/auth-digest-hash
|
||||
server
|
||||
.on(
|
||||
"/auth-digest-hash", HTTP_GET,
|
||||
[](AsyncWebServerRequest *request) {
|
||||
request->send(200, "text/plain", "Hello, world!");
|
||||
}
|
||||
)
|
||||
.addMiddleware(&digestAuthHash);
|
||||
|
||||
// test digest auth custom authorization middleware
|
||||
// curl -v --digest -u user:password http://192.168.4.1/auth-custom?token=123 => OK
|
||||
// curl -v --digest -u user:password http://192.168.4.1/auth-custom?token=456 => 403
|
||||
// curl -v --digest -u user:FAILED http://192.168.4.1/auth-custom?token=456 => 401
|
||||
server
|
||||
.on(
|
||||
"/auth-custom", HTTP_GET,
|
||||
[](AsyncWebServerRequest *request) {
|
||||
String buffer = "Hello ";
|
||||
buffer.concat(request->getAttribute("user"));
|
||||
buffer.concat(" with role: ");
|
||||
buffer.concat(request->getAttribute("role"));
|
||||
request->send(200, "text/plain", buffer);
|
||||
}
|
||||
)
|
||||
.addMiddlewares({&complexAuth, &authz});
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
// not needed
|
||||
void loop() {
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// How to use CORS middleware
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
static AsyncCorsMiddleware cors;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
cors.setOrigin("http://192.168.4.1");
|
||||
cors.setMethods("POST, GET, OPTIONS, DELETE");
|
||||
cors.setHeaders("X-Custom-Header");
|
||||
cors.setAllowCredentials(false);
|
||||
cors.setMaxAge(600);
|
||||
|
||||
server.addMiddleware(&cors);
|
||||
|
||||
// Test CORS preflight request
|
||||
// curl -v -X OPTIONS -H "origin: http://192.168.4.1" http://192.168.4.1/cors
|
||||
//
|
||||
// Test CORS request
|
||||
// curl -v -H "origin: http://192.168.4.1" http://192.168.4.1/cors
|
||||
//
|
||||
// Test non-CORS request
|
||||
// curl -v http://192.168.4.1/cors
|
||||
//
|
||||
server.on("/cors", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
request->send(200, "text/plain", "Hello, world!");
|
||||
});
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
// not needed
|
||||
void loop() {
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
#include <DNSServer.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
#include "ESPAsyncWebServer.h"
|
||||
|
||||
static DNSServer dnsServer;
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
class CaptiveRequestHandler : public AsyncWebHandler {
|
||||
public:
|
||||
bool canHandle(__unused AsyncWebServerRequest *request) const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
void handleRequest(AsyncWebServerRequest *request) {
|
||||
AsyncResponseStream *response = request->beginResponseStream("text/html");
|
||||
response->print("<!DOCTYPE html><html><head><title>Captive Portal</title></head><body>");
|
||||
response->print("<p>This is our captive portal front page.</p>");
|
||||
response->printf("<p>You were trying to reach: http://%s%s</p>", request->host().c_str(), request->url().c_str());
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
response->printf("<p>Try opening <a href='http://%s'>this link</a> instead</p>", WiFi.softAPIP().toString().c_str());
|
||||
#endif
|
||||
response->print("</body></html>");
|
||||
request->send(response);
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println("Configuring access point...");
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
if (!WiFi.softAP("esp-captive")) {
|
||||
Serial.println("Soft AP creation failed.");
|
||||
while (1);
|
||||
}
|
||||
|
||||
dnsServer.start(53, "*", WiFi.softAPIP());
|
||||
#endif
|
||||
|
||||
server.addHandler(new CaptiveRequestHandler()).setFilter(ON_AP_FILTER); // only when requested from AP
|
||||
// more handlers...
|
||||
server.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
dnsServer.processNextRequest();
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Shows how to catch all requests and send a 404 Not Found response
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
static const char *htmlContent PROGMEM = R"(
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Sample HTML</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello, World!</h1>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
|
||||
static const size_t htmlContentLength = strlen_P(htmlContent);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
// curl -v http://192.168.4.1/
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
// need to cast to uint8_t*
|
||||
// if you do not, the const char* will be copied in a temporary String buffer
|
||||
request->send(200, "text/html", (uint8_t *)htmlContent, htmlContentLength);
|
||||
});
|
||||
|
||||
// catch any request, and send a 404 Not Found response
|
||||
// except for /game_log which is handled by onRequestBody
|
||||
//
|
||||
// curl -v http://192.168.4.1/foo
|
||||
//
|
||||
server.onNotFound([](AsyncWebServerRequest *request) {
|
||||
if (request->url() == "/game_log") {
|
||||
return; // response object already created by onRequestBody
|
||||
}
|
||||
|
||||
request->send(404, "text/plain", "Not found");
|
||||
});
|
||||
|
||||
// See: https://github.com/ESP32Async/ESPAsyncWebServer/issues/6
|
||||
// catch any POST request and send a 200 OK response
|
||||
//
|
||||
// curl -v -X POST http://192.168.4.1/game_log -H "Content-Type: application/json" -d '{"game": "test"}'
|
||||
//
|
||||
server.onRequestBody([](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
|
||||
if (request->url() == "/game_log") {
|
||||
request->send(200, "application/json", "{\"status\":\"OK\"}");
|
||||
}
|
||||
// note that there is no else here: the goal is only to prepare a response based on some body content
|
||||
// onNotFound will always be called after this, and will not override the response object if `/game_log` is requested
|
||||
});
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
// not needed
|
||||
void loop() {
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Chunk response with caching example
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
static const char *htmlContent PROGMEM = R"(
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Sample HTML</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello, World!</h1>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
|
||||
static const size_t htmlContentLength = strlen_P(htmlContent);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
// first time: serves the file and cache headers
|
||||
// curl -N -v http://192.168.4.1/ --output -
|
||||
//
|
||||
// secodn time: serves 304
|
||||
// curl -N -v -H "if-none-match: 4272" http://192.168.4.1/ --output -
|
||||
//
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
String etag = String(htmlContentLength);
|
||||
|
||||
if (request->header(asyncsrv::T_INM) == etag) {
|
||||
request->send(304);
|
||||
return;
|
||||
}
|
||||
|
||||
AsyncWebServerResponse *response = request->beginChunkedResponse("text/html", [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t {
|
||||
Serial.printf("%u / %u\n", index, htmlContentLength);
|
||||
|
||||
// finished ?
|
||||
if (htmlContentLength <= index) {
|
||||
Serial.println("finished");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// serve a maximum of 256 or maxLen bytes of the remaining content
|
||||
// this small number is specifically chosen to demonstrate the chunking
|
||||
// DO NOT USE SUCH SMALL NUMBER IN PRODUCTION
|
||||
// Reducing the chunk size will increase the response time, thus reducing the server's capacity in processing concurrent requests
|
||||
const int chunkSize = min((size_t)256, min(maxLen, htmlContentLength - index));
|
||||
Serial.printf("sending: %u\n", chunkSize);
|
||||
|
||||
memcpy(buffer, htmlContent + index, chunkSize);
|
||||
|
||||
return chunkSize;
|
||||
});
|
||||
|
||||
response->addHeader(asyncsrv::T_Cache_Control, "public,max-age=60");
|
||||
response->addHeader(asyncsrv::T_ETag, etag);
|
||||
|
||||
request->send(response);
|
||||
});
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Shows how to wait in a chunk response for incoming data
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
#if __has_include("ArduinoJson.h")
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <AsyncMessagePack.h>
|
||||
#endif
|
||||
|
||||
static const char *htmlContent PROGMEM = R"(
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Sample HTML</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello, World!</h1>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
|
||||
static const size_t htmlContentLength = strlen_P(htmlContent);
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
static AsyncLoggingMiddleware requestLogger;
|
||||
|
||||
static String triggerUART;
|
||||
static int key = -1;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
// adds some internal request logging for debugging
|
||||
requestLogger.setEnabled(true);
|
||||
requestLogger.setOutput(Serial);
|
||||
|
||||
server.addMiddleware(&requestLogger);
|
||||
|
||||
#if __has_include("ArduinoJson.h")
|
||||
|
||||
//
|
||||
// HOW TO RUN THIS EXAMPLE:
|
||||
//
|
||||
// 1. Trigger a request that will be blocked for a long time:
|
||||
// > time curl -v -X POST http://192.168.4.1/api -H "Content-Type: application/json" -d '{"input": "Please type a key to continue in Serial console..."}' --output -
|
||||
//
|
||||
// 2. While waiting, in another terminal, run some concurrent requests:
|
||||
// > time curl -v http://192.168.4.1/
|
||||
//
|
||||
// 3. Type a key in the Serial console to continue the processing within 30 seconds.
|
||||
// This should unblock the first request.
|
||||
//
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
// need to cast to uint8_t*
|
||||
// if you do not, the const char* will be copied in a temporary String buffer
|
||||
request->send(200, "text/html", (uint8_t *)htmlContent, htmlContentLength);
|
||||
});
|
||||
|
||||
server.on(
|
||||
"/api", HTTP_POST,
|
||||
[](AsyncWebServerRequest *request) {
|
||||
// request parsing has finished
|
||||
String *data = (String *)request->_tempObject;
|
||||
|
||||
if (!data) {
|
||||
request->send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
// no data ?
|
||||
if (!data->length()) {
|
||||
delete data;
|
||||
request->_tempObject = nullptr;
|
||||
request->send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
JsonDocument doc;
|
||||
|
||||
// deserialize and check for errors
|
||||
if (deserializeJson(doc, *data)) {
|
||||
delete data;
|
||||
request->_tempObject = nullptr;
|
||||
request->send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
delete data;
|
||||
request->_tempObject = nullptr;
|
||||
|
||||
// start UART com: UART will send the data to the Serial console and wait for the key press
|
||||
triggerUART = doc["input"].as<const char *>();
|
||||
key = -1;
|
||||
|
||||
AsyncWebServerResponse *response = request->beginChunkedResponse("text/plain", [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t {
|
||||
// still waiting for UARY ?
|
||||
if (triggerUART.length() && key == -1) {
|
||||
return RESPONSE_TRY_AGAIN;
|
||||
}
|
||||
|
||||
// finished ?
|
||||
if (!triggerUART.length() && key == -1) {
|
||||
return 0; // 0 means we are done
|
||||
}
|
||||
|
||||
// async_ws_log_d("UART answered!");
|
||||
|
||||
String answer = "You typed: ";
|
||||
answer.concat((char)key);
|
||||
|
||||
// note: I did not check for maxLen, but you should (see ChunkResponse.ino)
|
||||
memcpy(buffer, answer.c_str(), answer.length());
|
||||
|
||||
// finish!
|
||||
triggerUART = emptyString;
|
||||
key = -1;
|
||||
|
||||
return answer.length();
|
||||
});
|
||||
|
||||
request->send(response);
|
||||
},
|
||||
NULL, // upload handler is not used so it should be NULL
|
||||
[](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
|
||||
// async_ws_log_d("Body: index: %u, len: %u, total: %u", index, len, total);
|
||||
|
||||
if (!index) {
|
||||
// async_ws_log_d("Start body parsing");
|
||||
request->_tempObject = new String();
|
||||
// cast request->_tempObject pointer to String and reserve total size
|
||||
((String *)request->_tempObject)->reserve(total);
|
||||
// set timeout 30s
|
||||
request->client()->setRxTimeout(30);
|
||||
}
|
||||
|
||||
// async_ws_log_d("Append body data");
|
||||
((String *)request->_tempObject)->concat((const char *)data, len);
|
||||
}
|
||||
);
|
||||
|
||||
#endif
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (triggerUART.length() && key == -1) {
|
||||
Serial.println(triggerUART);
|
||||
// async_ws_log_d("Waiting for UART input...");
|
||||
while (!Serial.available()) {
|
||||
delay(100);
|
||||
}
|
||||
key = Serial.read();
|
||||
Serial.flush();
|
||||
// async_ws_log_d("UART input: %c", key);
|
||||
triggerUART = emptyString;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// https://github.com/ESP32Async/ESPAsyncWebServer/discussions/23
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
request->send(200, "text/plain", "Hello, world");
|
||||
});
|
||||
|
||||
server.begin();
|
||||
Serial.println("begin() - run: curl -v http://192.168.4.1/ => should succeed");
|
||||
delay(10000);
|
||||
|
||||
Serial.println("end()");
|
||||
server.end();
|
||||
server.begin();
|
||||
Serial.println("begin() - run: curl -v http://192.168.4.1/ => should succeed");
|
||||
}
|
||||
|
||||
// not needed
|
||||
void loop() {
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Shows how to use setFilter to route requests to different handlers based on WiFi mode
|
||||
//
|
||||
|
||||
#include <DNSServer.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
#include "ESPAsyncWebServer.h"
|
||||
|
||||
static DNSServer dnsServer;
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
class CaptiveRequestHandler : public AsyncWebHandler {
|
||||
public:
|
||||
bool canHandle(__unused AsyncWebServerRequest *request) const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
void handleRequest(AsyncWebServerRequest *request) override {
|
||||
AsyncResponseStream *response = request->beginResponseStream("text/html");
|
||||
response->print("<!DOCTYPE html><html><head><title>Captive Portal</title></head><body>");
|
||||
response->print("<p>This is out captive portal front page.</p>");
|
||||
response->printf("<p>You were trying to reach: http://%s%s</p>", request->host().c_str(), request->url().c_str());
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
response->printf("<p>Try opening <a href='http://%s'>this link</a> instead</p>", WiFi.softAPIP().toString().c_str());
|
||||
#endif
|
||||
response->print("</body></html>");
|
||||
request->send(response);
|
||||
}
|
||||
};
|
||||
|
||||
bool hit1 = false;
|
||||
bool hit2 = false;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
server
|
||||
.on(
|
||||
"/", HTTP_GET,
|
||||
[](AsyncWebServerRequest *request) {
|
||||
Serial.println("Captive portal request...");
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
Serial.println("WiFi.localIP(): " + WiFi.localIP().toString());
|
||||
#endif
|
||||
Serial.println("request->client()->localIP(): " + request->client()->localIP().toString());
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
Serial.println("WiFi.type(): " + String((int)WiFi.localIP().type()));
|
||||
#endif
|
||||
Serial.println("request->client()->type(): " + String((int)request->client()->localIP().type()));
|
||||
#endif
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
Serial.println(WiFi.localIP() == request->client()->localIP() ? "should be: ON_STA_FILTER" : "should be: ON_AP_FILTER");
|
||||
Serial.println(WiFi.localIP() == request->client()->localIP());
|
||||
Serial.println(WiFi.localIP().toString() == request->client()->localIP().toString());
|
||||
#endif
|
||||
request->send(200, "text/plain", "This is the captive portal");
|
||||
hit1 = true;
|
||||
}
|
||||
)
|
||||
.setFilter(ON_AP_FILTER);
|
||||
|
||||
server
|
||||
.on(
|
||||
"/", HTTP_GET,
|
||||
[](AsyncWebServerRequest *request) {
|
||||
Serial.println("Website request...");
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
Serial.println("WiFi.localIP(): " + WiFi.localIP().toString());
|
||||
#endif
|
||||
Serial.println("request->client()->localIP(): " + request->client()->localIP().toString());
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
Serial.println("WiFi.type(): " + String((int)WiFi.localIP().type()));
|
||||
#endif
|
||||
Serial.println("request->client()->type(): " + String((int)request->client()->localIP().type()));
|
||||
#endif
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
Serial.println(WiFi.localIP() == request->client()->localIP() ? "should be: ON_STA_FILTER" : "should be: ON_AP_FILTER");
|
||||
Serial.println(WiFi.localIP() == request->client()->localIP());
|
||||
Serial.println(WiFi.localIP().toString() == request->client()->localIP().toString());
|
||||
#endif
|
||||
request->send(200, "text/plain", "This is the website");
|
||||
hit2 = true;
|
||||
}
|
||||
)
|
||||
.setFilter(ON_STA_FILTER);
|
||||
|
||||
// assert(WiFi.softAP("esp-captive-portal"));
|
||||
// dnsServer.start(53, "*", WiFi.softAPIP());
|
||||
// server.begin();
|
||||
// Serial.println("Captive portal started!");
|
||||
|
||||
// while (!hit1) {
|
||||
// dnsServer.processNextRequest();
|
||||
// yield();
|
||||
// }
|
||||
// delay(1000); // Wait for the client to process the response
|
||||
|
||||
// Serial.println("Captive portal opened, stopping it and connecting to WiFi...");
|
||||
// dnsServer.stop();
|
||||
// WiFi.softAPdisconnect();
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.persistent(false);
|
||||
WiFi.begin("IoT");
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
}
|
||||
Serial.println("Connected to WiFi with IP address: " + WiFi.localIP().toString());
|
||||
#endif
|
||||
|
||||
server.begin();
|
||||
|
||||
// while (!hit2) {
|
||||
// delay(10);
|
||||
// }
|
||||
// delay(1000); // Wait for the client to process the response
|
||||
// ESP.restart();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Shows how to serve a large HTML page from flash memory without copying it to heap in a temporary buffer
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
static const char *htmlContent PROGMEM = R"(
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Sample HTML</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello, World!</h1>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
|
||||
static const size_t htmlContentLength = strlen_P(htmlContent);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
// curl -v http://192.168.4.1/
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
// need to cast to uint8_t*
|
||||
// if you do not, the const char* will be copied in a temporary String buffer
|
||||
request->send(200, "text/html", (uint8_t *)htmlContent, htmlContentLength);
|
||||
});
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
// not needed
|
||||
void loop() {
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Show how to manipulate headers in the request / response
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
// request logger
|
||||
static AsyncLoggingMiddleware requestLogger;
|
||||
|
||||
// filter out specific headers from the incoming request
|
||||
static AsyncHeaderFilterMiddleware headerFilter;
|
||||
|
||||
// remove all headers from the incoming request except the ones provided in the constructor
|
||||
AsyncHeaderFreeMiddleware headerFree;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
requestLogger.setEnabled(true);
|
||||
requestLogger.setOutput(Serial);
|
||||
|
||||
headerFilter.filter("X-Remove-Me");
|
||||
|
||||
headerFree.keep("X-Keep-Me");
|
||||
headerFree.keep("host");
|
||||
|
||||
server.addMiddlewares({&requestLogger, &headerFilter});
|
||||
|
||||
// x-remove-me header will be removed
|
||||
//
|
||||
// curl -v -H "X-Header: Foo" -H "x-remove-me: value" http://192.168.4.1/remove
|
||||
//
|
||||
server.on("/remove", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
// print all headers
|
||||
for (size_t i = 0; i < request->headers(); i++) {
|
||||
const AsyncWebHeader *h = request->getHeader(i);
|
||||
Serial.printf("Header[%s]: %s\n", h->name().c_str(), h->value().c_str());
|
||||
}
|
||||
request->send(200, "text/plain", "Hello, world!");
|
||||
});
|
||||
|
||||
// Only headers x-keep-me and host will be kept
|
||||
//
|
||||
// curl -v -H "x-keep-me: value" -H "x-remove-me: value" http://192.168.4.1/keep
|
||||
//
|
||||
server
|
||||
.on(
|
||||
"/keep", HTTP_GET,
|
||||
[](AsyncWebServerRequest *request) {
|
||||
// print all headers
|
||||
for (size_t i = 0; i < request->headers(); i++) {
|
||||
const AsyncWebHeader *h = request->getHeader(i);
|
||||
Serial.printf("Header[%s]: %s\n", h->name().c_str(), h->value().c_str());
|
||||
}
|
||||
request->send(200, "text/plain", "Hello, world!");
|
||||
}
|
||||
)
|
||||
.addMiddleware(&headerFree);
|
||||
|
||||
// curl -v http://192.168.4.1/
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", "Hello, world!");
|
||||
response->addHeader(AsyncWebHeader::parse("X-Test-1: value1"));
|
||||
response->addHeader(AsyncWebHeader::parse("X-Test-2:value2"));
|
||||
response->addHeader(AsyncWebHeader::parse("X-Test-3:"));
|
||||
response->addHeader(AsyncWebHeader::parse("X-Test-4: "));
|
||||
response->addHeader(AsyncWebHeader::parse(""));
|
||||
response->addHeader(AsyncWebHeader::parse(":"));
|
||||
request->send(response);
|
||||
/**
|
||||
< HTTP/1.1 200 OK
|
||||
< connection: close
|
||||
< X-Test-1: value1
|
||||
< X-Test-2: value2
|
||||
< X-Test-3:
|
||||
< X-Test-4:
|
||||
< accept-ranges: none
|
||||
< content-length: 13
|
||||
< content-type: text/plain
|
||||
*/
|
||||
});
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
// not needed
|
||||
void loop() {
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Query and send headers
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
//
|
||||
// curl -v http://192.168.4.1
|
||||
//
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
//List all collected headers
|
||||
int headers = request->headers();
|
||||
int i;
|
||||
for (i = 0; i < headers; i++) {
|
||||
const AsyncWebHeader *h = request->getHeader(i);
|
||||
Serial.printf("HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str());
|
||||
}
|
||||
|
||||
AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", "Hello World!");
|
||||
|
||||
//Add header to the response
|
||||
response->addHeader("Server", "ESP Async Web Server");
|
||||
|
||||
//Add multiple headers with the same name
|
||||
response->addHeader("Set-Cookie", "sessionId=38afes7a8", false);
|
||||
response->addHeader("Set-Cookie", "id=a3fWa; Max-Age=2592000", false);
|
||||
response->addHeader("Set-Cookie", "qwerty=219ffwef9w0f; Domain=example.com", false);
|
||||
|
||||
//Remove specific header
|
||||
response->removeHeader("Set-Cookie", "sessionId=38afes7a8");
|
||||
|
||||
//Remove all headers with the same name
|
||||
response->removeHeader("Set-Cookie");
|
||||
|
||||
request->send(response);
|
||||
});
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
//Sleep in the loop task to not keep the CPU busy
|
||||
delay(1000);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Shows how to send and receive Json data
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
#if __has_include("ArduinoJson.h")
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <AsyncMessagePack.h>
|
||||
#endif
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
#if __has_include("ArduinoJson.h")
|
||||
static AsyncCallbackJsonWebHandler *handler = new AsyncCallbackJsonWebHandler("/json2");
|
||||
#endif
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
#if __has_include("ArduinoJson.h")
|
||||
//
|
||||
// sends JSON using AsyncJsonResponse
|
||||
//
|
||||
// curl -v http://192.168.4.1/json1
|
||||
//
|
||||
server.on("/json1", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
AsyncJsonResponse *response = new AsyncJsonResponse();
|
||||
JsonObject root = response->getRoot().to<JsonObject>();
|
||||
root["hello"] = "world";
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
});
|
||||
|
||||
// Send JSON using AsyncResponseStream
|
||||
//
|
||||
// curl -v http://192.168.4.1/json2
|
||||
//
|
||||
server.on("/json2", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
AsyncResponseStream *response = request->beginResponseStream("application/json");
|
||||
JsonDocument doc;
|
||||
JsonObject root = doc.to<JsonObject>();
|
||||
root["foo"] = "bar";
|
||||
serializeJson(root, *response);
|
||||
Serial.println();
|
||||
request->send(response);
|
||||
});
|
||||
|
||||
// curl -v -X POST -H 'Content-Type: application/json' -d '{"name":"You"}' http://192.168.4.1/json2
|
||||
// curl -v -X PUT -H 'Content-Type: application/json' -d '{"name":"You"}' http://192.168.4.1/json2
|
||||
//
|
||||
// edge cases:
|
||||
//
|
||||
// curl -v -X POST -H "Content-Type: application/json" -d "1234" -H "Content-Length: 5" http://192.168.4.1/json2 => rx timeout
|
||||
// curl -v -X POST -H "Content-Type: application/json" -d "1234" -H "Content-Length: 2" http://192.168.4.1/json2 => 12
|
||||
// curl -v -X POST -H "Content-Type: application/json" -d "1234" -H "Content-Length: 4" http://192.168.4.1/json2 => 1234
|
||||
// curl -v -X POST -H "Content-Type: application/json" -d "1234" -H "Content-Length: 10" http://192.168.4.1/json2 => rx timeout
|
||||
// curl -v -X POST -H "Content-Type: application/json" -d "12345678" -H "Content-Length: 8" http://192.168.4.1/json2 => 12345678
|
||||
// curl -v -X POST -H "Content-Type: application/json" -d "123456789" -H "Content-Length: 8" http://192.168.4.1/json2 => 12345678
|
||||
// curl -v -X POST -H "Content-Type: application/json" -d "123456789" -H "Content-Length: 9" http://192.168.4.1/json2 => 413: Content length exceeds maximum allowed
|
||||
handler->setMaxContentLength(8);
|
||||
handler->setMethod(HTTP_POST | HTTP_PUT);
|
||||
handler->onRequest([](AsyncWebServerRequest *request, JsonVariant &json) {
|
||||
serializeJson(json, Serial);
|
||||
Serial.println();
|
||||
AsyncJsonResponse *response = new AsyncJsonResponse();
|
||||
JsonObject root = response->getRoot().to<JsonObject>();
|
||||
root["hello"] = json.as<JsonObject>()["name"];
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
});
|
||||
|
||||
server.addHandler(handler);
|
||||
#endif
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
// not needed
|
||||
void loop() {
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Show how to log the incoming request and response as a curl-like syntax
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
static AsyncLoggingMiddleware requestLogger;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
requestLogger.setEnabled(true);
|
||||
requestLogger.setOutput(Serial);
|
||||
|
||||
server.addMiddleware(&requestLogger);
|
||||
|
||||
// curl -v -H "X-Header:Foo" http://192.168.4.1/
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
request->send(200, "text/plain", "Hello, world!");
|
||||
});
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
// not needed
|
||||
void loop() {
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Shows how to send and receive Message Pack data
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
#if __has_include("ArduinoJson.h")
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <AsyncMessagePack.h>
|
||||
#endif
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
#if __has_include("ArduinoJson.h")
|
||||
static AsyncCallbackMessagePackWebHandler *handler = new AsyncCallbackMessagePackWebHandler("/msgpack2");
|
||||
#endif
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
#if __has_include("ArduinoJson.h")
|
||||
//
|
||||
// sends MessagePack using AsyncMessagePackResponse
|
||||
//
|
||||
// curl -v http://192.168.4.1/msgpack1
|
||||
//
|
||||
server.on("/msgpack1", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
AsyncMessagePackResponse *response = new AsyncMessagePackResponse();
|
||||
JsonObject root = response->getRoot().to<JsonObject>();
|
||||
root["hello"] = "world";
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
});
|
||||
|
||||
// Send MessagePack using AsyncResponseStream
|
||||
//
|
||||
// curl -v http://192.168.4.1/msgpack2
|
||||
//
|
||||
server.on("/msgpack2", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
AsyncResponseStream *response = request->beginResponseStream("application/msgpack");
|
||||
JsonDocument doc;
|
||||
JsonObject root = doc.to<JsonObject>();
|
||||
root["foo"] = "bar";
|
||||
serializeMsgPack(root, *response);
|
||||
request->send(response);
|
||||
});
|
||||
|
||||
handler->setMethod(HTTP_POST | HTTP_PUT);
|
||||
handler->onRequest([](AsyncWebServerRequest *request, JsonVariant &json) {
|
||||
serializeJson(json, Serial);
|
||||
AsyncMessagePackResponse *response = new AsyncMessagePackResponse();
|
||||
JsonObject root = response->getRoot().to<JsonObject>();
|
||||
root["hello"] = json.as<JsonObject>()["name"];
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
});
|
||||
|
||||
server.addHandler(handler);
|
||||
#endif
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
// not needed
|
||||
void loop() {
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Show how to sue Middleware
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
// New middleware classes can be created!
|
||||
class MyMiddleware : public AsyncMiddleware {
|
||||
public:
|
||||
void run(AsyncWebServerRequest *request, ArMiddlewareNext next) override {
|
||||
Serial.printf("Before handler: %s %s\n", request->methodToString(), request->url().c_str());
|
||||
next(); // continue middleware chain
|
||||
Serial.printf("After handler: response code=%d\n", request->getResponse()->code());
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
// add a global middleware to the server
|
||||
server.addMiddleware(new MyMiddleware());
|
||||
|
||||
// Test with:
|
||||
//
|
||||
// - curl -v http://192.168.4.1/ => 200 OK
|
||||
// - curl -v http://192.168.4.1/?user=anon => 403 Forbidden
|
||||
// - curl -v http://192.168.4.1/?user=foo => 200 OK
|
||||
// - curl -v http://192.168.4.1/?user=error => 400 ERROR
|
||||
//
|
||||
AsyncCallbackWebHandler &handler = server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
Serial.printf("In Handler: %s %s\n", request->methodToString(), request->url().c_str());
|
||||
request->send(200, "text/plain", "Hello, world!");
|
||||
});
|
||||
|
||||
// add a middleware to this handler only to send 403 if the user is anon
|
||||
handler.addMiddleware([](AsyncWebServerRequest *request, ArMiddlewareNext next) {
|
||||
Serial.println("Checking user=anon");
|
||||
if (request->hasParam("user") && request->getParam("user")->value() == "anon") {
|
||||
request->send(403, "text/plain", "Forbidden");
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
// add a middleware to this handler that will replace the previously created response by another one
|
||||
handler.addMiddleware([](AsyncWebServerRequest *request, ArMiddlewareNext next) {
|
||||
next();
|
||||
Serial.println("Checking user=error");
|
||||
if (request->hasParam("user") && request->getParam("user")->value() == "error") {
|
||||
request->send(400, "text/plain", "ERROR");
|
||||
}
|
||||
});
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
// not needed
|
||||
void loop() {
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Query parameters and body parameters
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
static const char *htmlContent PROGMEM = R"(
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>POST Request with Multiple Parameters</title>
|
||||
</head>
|
||||
<body>
|
||||
<form action="http://192.168.4.1" method="POST">
|
||||
<label for="who">Who?</label>
|
||||
<input type="text" id="who" name="who" value="Carl"><br>
|
||||
<label for="g0">g0:</label>
|
||||
<input type="text" id="g0" name="g0" value="1"><br>
|
||||
<label for="a0">a0:</label>
|
||||
<input type="text" id="a0" name="a0" value="2"><br>
|
||||
<label for="n0">n0:</label>
|
||||
<input type="text" id="n0" name="n0" value="3"><br>
|
||||
<label for="t10">t10:</label>
|
||||
<input type="text" id="t10" name="t10" value="3"><br>
|
||||
<label for="t20">t20:</label>
|
||||
<input type="text" id="t20" name="t20" value="4"><br>
|
||||
<label for="t30">t30:</label>
|
||||
<input type="text" id="t30" name="t30" value="5"><br>
|
||||
<label for="t40">t40:</label>
|
||||
<input type="text" id="t40" name="t40" value="6"><br>
|
||||
<label for="t50">t50:</label>
|
||||
<input type="text" id="t50" name="t50" value="7"><br>
|
||||
<label for="g1">g1:</label>
|
||||
<input type="text" id="g1" name="g1" value="2"><br>
|
||||
<label for="a1">a1:</label>
|
||||
<input type="text" id="a1" name="a1" value="2"><br>
|
||||
<label for="n1">n1:</label>
|
||||
<input type="text" id="n1" name="n1" value="3"><br>
|
||||
<label for="t11">t11:</label>
|
||||
<input type="text" id="t11" name="t11" value="13"><br>
|
||||
<label for="t21">t21:</label>
|
||||
<input type="text" id="t21" name="t21" value="14"><br>
|
||||
<label for="t31">t31:</label>
|
||||
<input type="text" id="t31" name="t31" value="15"><br>
|
||||
<label for="t41">t41:</label>
|
||||
<input type="text" id="t41" name="t41" value="16"><br>
|
||||
<label for="t51">t51:</label>
|
||||
<input type="text" id="t51" name="t51" value="17"><br>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
|
||||
static const size_t htmlContentLength = strlen_P(htmlContent);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
// Get query parameters
|
||||
//
|
||||
// curl -v http://192.168.4.1/?who=Bob
|
||||
//
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
if (request->hasParam("who")) {
|
||||
Serial.printf("Who? %s\n", request->getParam("who")->value().c_str());
|
||||
}
|
||||
|
||||
request->send(200, "text/html", (uint8_t *)htmlContent, htmlContentLength);
|
||||
});
|
||||
|
||||
// Get form body parameters
|
||||
//
|
||||
// curl -v -H "Content-Type: application/x-www-form-urlencoded" -d "who=Carl" -d "param=value" http://192.168.4.1/
|
||||
//
|
||||
server.on("/", HTTP_POST, [](AsyncWebServerRequest *request) {
|
||||
// display params
|
||||
size_t count = request->params();
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
const AsyncWebParameter *p = request->getParam(i);
|
||||
Serial.printf("PARAM[%u]: %s = %s\n", i, p->name().c_str(), p->value().c_str());
|
||||
}
|
||||
|
||||
// get who param
|
||||
String who;
|
||||
if (request->hasParam("who", true)) {
|
||||
who = request->getParam("who", true)->value();
|
||||
} else {
|
||||
who = "No message sent";
|
||||
}
|
||||
request->send(200, "text/plain", "Hello " + who + "!");
|
||||
});
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
// not needed
|
||||
void loop() {
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// - Download ESP32 partition by name and/or type and/or subtype
|
||||
// - Support encrypted and non-encrypted partitions
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <LittleFS.h>
|
||||
|
||||
#ifndef ESP32
|
||||
// this example is only for the ESP32
|
||||
void setup() {}
|
||||
void loop() {}
|
||||
#else
|
||||
|
||||
#include <esp_partition.h>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
LittleFS.begin(true);
|
||||
|
||||
// To upload the FS partition, run:
|
||||
// > pio run -e arduino-3 -t buildfs
|
||||
// > pio run -e arduino-3 -t uploadfs
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// - Download the partition named "spiffs": http://192.168.4.1/partition?label=spiffs
|
||||
// - Download the partition named "spiffs" with type "data": http://192.168.4.1/partition?label=spiffs&type=1
|
||||
// - Download the partition named "spiffs" with type "data" and subtype "spiffs": http://192.168.4.1/partition?label=spiffs&type=1&subtype=130
|
||||
// - Download the partition with subtype "nvs": http://192.168.4.1/partition?type=1&subtype=2
|
||||
//
|
||||
// "type" and "subtype" IDs can be found in esp_partition.h header file.
|
||||
//
|
||||
// Add "&raw=false" parameter to download the partition unencrypted (for encrypted partitions).
|
||||
// By default, the raw partition is downloaded, so if a partition is encrypted, the encrypted data will be downloaded.
|
||||
//
|
||||
// To browse a downloaded LittleFS partition, you can use https://tniessen.github.io/littlefs-disk-img-viewer/ (block size is 4096)
|
||||
//
|
||||
server.on("/partition", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
const AsyncWebParameter *pLabel = request->getParam("label");
|
||||
const AsyncWebParameter *pType = request->getParam("type");
|
||||
const AsyncWebParameter *pSubtype = request->getParam("subtype");
|
||||
const AsyncWebParameter *pRaw = request->getParam("raw");
|
||||
|
||||
if (!pLabel && !pType && !pSubtype) {
|
||||
request->send(400, "text/plain", "Bad request: missing parameter");
|
||||
return;
|
||||
}
|
||||
|
||||
esp_partition_type_t type = ESP_PARTITION_TYPE_ANY;
|
||||
esp_partition_subtype_t subtype = ESP_PARTITION_SUBTYPE_ANY;
|
||||
const char *label = nullptr;
|
||||
bool raw = true;
|
||||
|
||||
if (pLabel) {
|
||||
label = pLabel->value().c_str();
|
||||
}
|
||||
|
||||
if (pType) {
|
||||
type = (esp_partition_type_t)pType->value().toInt();
|
||||
}
|
||||
|
||||
if (pSubtype) {
|
||||
subtype = (esp_partition_subtype_t)pSubtype->value().toInt();
|
||||
}
|
||||
|
||||
if (pRaw && pRaw->value() == "false") {
|
||||
raw = false;
|
||||
}
|
||||
|
||||
const esp_partition_t *partition = esp_partition_find_first(type, subtype, label);
|
||||
|
||||
if (!partition) {
|
||||
request->send(404, "text/plain", "Partition not found");
|
||||
return;
|
||||
}
|
||||
|
||||
AsyncWebServerResponse *response =
|
||||
request->beginChunkedResponse("application/octet-stream", [partition, raw](uint8_t *buffer, size_t maxLen, size_t index) -> size_t {
|
||||
const size_t remaining = partition->size - index;
|
||||
if (!remaining) {
|
||||
return 0;
|
||||
}
|
||||
const size_t len = std::min(maxLen, remaining);
|
||||
if (raw && esp_partition_read_raw(partition, index, buffer, len) == ESP_OK) {
|
||||
return len;
|
||||
}
|
||||
if (!raw && esp_partition_read(partition, index, buffer, len) == ESP_OK) {
|
||||
return len;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
response->addHeader("Content-Disposition", "attachment; filename=" + String(partition->label) + ".bin");
|
||||
response->setContentLength(partition->size);
|
||||
|
||||
request->send(response);
|
||||
});
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
delay(100);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,243 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Perf tests
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
static const char *htmlContent PROGMEM = R"(
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Sample HTML</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello, World!</h1>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
|
||||
static const size_t htmlContentLength = strlen_P(htmlContent);
|
||||
static constexpr char characters[] = "0123456789ABCDEF";
|
||||
static size_t charactersIndex = 0;
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
static AsyncEventSource events("/events");
|
||||
|
||||
static volatile size_t requests = 0;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
// Pauses in the request parsing phase
|
||||
//
|
||||
// autocannon -c 32 -w 32 -a 96 -t 30 --renderStatusCodes -m POST -H "Content-Type: application/json" -b '{"foo": "bar"}' http://192.168.4.1/delay
|
||||
//
|
||||
// curl -v -X POST -H "Content-Type: application/json" -d '{"game": "test"}' http://192.168.4.1/delay
|
||||
//
|
||||
server.onNotFound([](AsyncWebServerRequest *request) {
|
||||
requests = requests + 1;
|
||||
if (request->url() == "/delay") {
|
||||
request->send(200, "application/json", "{\"status\":\"OK\"}");
|
||||
} else {
|
||||
request->send(404, "text/plain", "Not found");
|
||||
}
|
||||
});
|
||||
server.onRequestBody([](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
|
||||
if (request->url() == "/delay") {
|
||||
delay(3000);
|
||||
}
|
||||
});
|
||||
|
||||
// HTTP endpoint
|
||||
//
|
||||
// > brew install autocannon
|
||||
// > autocannon -c 10 -w 10 -d 20 http://192.168.4.1
|
||||
// > autocannon -c 16 -w 16 -d 20 http://192.168.4.1
|
||||
//
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
// need to cast to uint8_t*
|
||||
// if you do not, the const char* will be copied in a temporary String buffer
|
||||
requests = requests + 1;
|
||||
request->send(200, "text/html", (uint8_t *)htmlContent, htmlContentLength);
|
||||
});
|
||||
|
||||
// IMPORTANT - DO NOT WRITE SUCH CODE IN PRODUCTON !
|
||||
//
|
||||
// This example simulates the slowdown that can happen when:
|
||||
// - downloading a huge file from sdcard
|
||||
// - doing some file listing on SDCard because it is horribly slow to get a file listing with file stats on SDCard.
|
||||
// So in both cases, ESP would deadlock or TWDT would trigger.
|
||||
//
|
||||
// This example simulats that by slowing down the chunk callback:
|
||||
// - d=2000 is the delay in ms in the callback
|
||||
// - l=10000 is the length of the response
|
||||
//
|
||||
// time curl -N -v -G -d 'd=2000' -d 'l=10000' http://192.168.4.1/slow.html --output -
|
||||
//
|
||||
server.on("/slow.html", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
requests = requests + 1;
|
||||
uint32_t d = request->getParam("d")->value().toInt();
|
||||
uint32_t l = request->getParam("l")->value().toInt();
|
||||
Serial.printf("d = %" PRIu32 ", l = %" PRIu32 "\n", d, l);
|
||||
AsyncWebServerResponse *response = request->beginChunkedResponse("text/html", [d, l](uint8_t *buffer, size_t maxLen, size_t index) -> size_t {
|
||||
Serial.printf("%u\n", index);
|
||||
// finished ?
|
||||
if (index >= l) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// slow down the task to simulate some heavy processing, like SD card reading
|
||||
delay(d);
|
||||
|
||||
memset(buffer, characters[charactersIndex], 256);
|
||||
charactersIndex = (charactersIndex + 1) % sizeof(characters);
|
||||
return 256;
|
||||
});
|
||||
|
||||
request->send(response);
|
||||
});
|
||||
|
||||
// SSS endpoint
|
||||
//
|
||||
// launch 16 concurrent workers for 30 seconds
|
||||
// > for i in {1..10}; do ( count=$(gtimeout 30 curl -s -N -H "Accept: text/event-stream" http://192.168.4.1/events 2>&1 | grep -c "^data:"); echo "Total: $count events, $(echo "$count / 4" | bc -l) events / second" ) & done;
|
||||
// > for i in {1..16}; do ( count=$(gtimeout 30 curl -s -N -H "Accept: text/event-stream" http://192.168.4.1/events 2>&1 | grep -c "^data:"); echo "Total: $count events, $(echo "$count / 4" | bc -l) events / second" ) & done;
|
||||
//
|
||||
// With AsyncTCP, with 16 workers: a lot of "Event message queue overflow: discard message", no crash
|
||||
//
|
||||
// Total: 1711 events, 427.75 events / second
|
||||
// Total: 1711 events, 427.75 events / second
|
||||
// Total: 1626 events, 406.50 events / second
|
||||
// Total: 1562 events, 390.50 events / second
|
||||
// Total: 1706 events, 426.50 events / second
|
||||
// Total: 1659 events, 414.75 events / second
|
||||
// Total: 1624 events, 406.00 events / second
|
||||
// Total: 1706 events, 426.50 events / second
|
||||
// Total: 1487 events, 371.75 events / second
|
||||
// Total: 1573 events, 393.25 events / second
|
||||
// Total: 1569 events, 392.25 events / second
|
||||
// Total: 1559 events, 389.75 events / second
|
||||
// Total: 1560 events, 390.00 events / second
|
||||
// Total: 1562 events, 390.50 events / second
|
||||
// Total: 1626 events, 406.50 events / second
|
||||
//
|
||||
// With AsyncTCP, with 10 workers:
|
||||
//
|
||||
// Total: 2038 events, 509.50 events / second
|
||||
// Total: 2120 events, 530.00 events / second
|
||||
// Total: 2119 events, 529.75 events / second
|
||||
// Total: 2038 events, 509.50 events / second
|
||||
// Total: 2037 events, 509.25 events / second
|
||||
// Total: 2119 events, 529.75 events / second
|
||||
// Total: 2119 events, 529.75 events / second
|
||||
// Total: 2120 events, 530.00 events / second
|
||||
// Total: 2038 events, 509.50 events / second
|
||||
// Total: 2038 events, 509.50 events / second
|
||||
//
|
||||
// With AsyncTCPSock, with 16 workers: ESP32 CRASH !!!
|
||||
//
|
||||
// With AsyncTCPSock, with 10 workers:
|
||||
//
|
||||
// Total: 1242 events, 310.50 events / second
|
||||
// Total: 1242 events, 310.50 events / second
|
||||
// Total: 1242 events, 310.50 events / second
|
||||
// Total: 1242 events, 310.50 events / second
|
||||
// Total: 1181 events, 295.25 events / second
|
||||
// Total: 1182 events, 295.50 events / second
|
||||
// Total: 1240 events, 310.00 events / second
|
||||
// Total: 1181 events, 295.25 events / second
|
||||
// Total: 1181 events, 295.25 events / second
|
||||
// Total: 1183 events, 295.75 events / second
|
||||
//
|
||||
server.addHandler(&events);
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
static uint32_t lastSSE = 0;
|
||||
static uint32_t deltaSSE = 10;
|
||||
|
||||
static uint32_t lastHeap = 0;
|
||||
|
||||
void loop() {
|
||||
uint32_t now = millis();
|
||||
if (now - lastSSE >= deltaSSE) {
|
||||
events.send(String("ping-") + now, "heartbeat", now);
|
||||
lastSSE = millis();
|
||||
}
|
||||
|
||||
#ifdef ESP32
|
||||
if (now - lastHeap >= 2000) {
|
||||
Serial.printf("Uptime: %3lu s, requests: %3u, Free heap: %" PRIu32 "\n", millis() / 1000, requests, ESP.getFreeHeap());
|
||||
lastHeap = now;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Show how to rate limit the server or some endpoints
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
static AsyncRateLimitMiddleware rateLimit;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
// maximum 5 requests per 10 seconds
|
||||
rateLimit.setMaxRequests(5);
|
||||
rateLimit.setWindowSize(10);
|
||||
|
||||
// run quickly several times:
|
||||
//
|
||||
// curl -v http://192.168.4.1/
|
||||
//
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
request->send(200, "text/plain", "Hello, world!");
|
||||
});
|
||||
|
||||
// run quickly several times:
|
||||
//
|
||||
// curl -v http://192.168.4.1/rate-limited
|
||||
//
|
||||
server
|
||||
.on(
|
||||
"/rate-limited", HTTP_GET,
|
||||
[](AsyncWebServerRequest *request) {
|
||||
request->send(200, "text/plain", "Hello, world!");
|
||||
}
|
||||
)
|
||||
.addMiddleware(&rateLimit); // only rate limit this endpoint, but could be applied globally to the server
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
// not needed
|
||||
void loop() {
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Shows how to redirect
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
// curl -v http://192.168.4.1/
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
request->redirect("/index.txt");
|
||||
});
|
||||
|
||||
// curl -v http://192.168.4.1/index.txt
|
||||
server.on("/index.txt", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
request->send(200, "text/plain", "Hello, world!");
|
||||
});
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
// not needed
|
||||
void loop() {
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Shows how to use request continuation to pause a request for a long processing task, and be able to resume it later.
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
// request handler that is saved from the paused request to communicate with Serial
|
||||
static String message;
|
||||
static AsyncWebServerRequestPtr serialRequest;
|
||||
|
||||
// request handler that is saved from the paused request to communicate with GPIO
|
||||
static uint8_t pin = 35;
|
||||
static AsyncWebServerRequestPtr gpioRequest;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
// Post a message that will be sent to the Serial console, and pause the request until the user types a key
|
||||
//
|
||||
// curl -v -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "question=Name%3F%20" http://192.168.4.1/serial
|
||||
//
|
||||
// curl output should show "Answer: [y/n]" as the response
|
||||
server.on("/serial", HTTP_POST, [](AsyncWebServerRequest *request) {
|
||||
message = request->getParam("question", true)->value();
|
||||
serialRequest = request->pause();
|
||||
});
|
||||
|
||||
// Wait for a GPIO to be high
|
||||
//
|
||||
// curl -v http://192.168.4.1/gpio
|
||||
//
|
||||
// curl output should show "GPIO is high!" as the response
|
||||
server.on("/gpio", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
gpioRequest = request->pause();
|
||||
});
|
||||
|
||||
pinMode(pin, INPUT);
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
delay(500);
|
||||
|
||||
// Check for a high voltage on the RX1 pin
|
||||
if (digitalRead(pin) == HIGH) {
|
||||
if (auto request = gpioRequest.lock()) {
|
||||
request->send(200, "text/plain", "GPIO is high!");
|
||||
}
|
||||
}
|
||||
|
||||
// check for an incoming message from the Serial console
|
||||
if (message.length()) {
|
||||
Serial.printf("%s", message.c_str());
|
||||
// drops buffer
|
||||
while (Serial.available()) {
|
||||
Serial.read();
|
||||
}
|
||||
Serial.setTimeout(10000);
|
||||
String response = Serial.readStringUntil('\n'); // waits for a key to be pressed
|
||||
Serial.println();
|
||||
message = emptyString;
|
||||
if (auto request = serialRequest.lock()) {
|
||||
request->send(200, "text/plain", "Answer: " + response);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Shows how to use request continuation to pause a request for a long processing task, and be able to resume it later.
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
// ===============================================================
|
||||
// The code below is used to simulate some long running operations
|
||||
// ===============================================================
|
||||
|
||||
typedef struct {
|
||||
size_t id;
|
||||
AsyncWebServerRequestPtr requestPtr;
|
||||
uint8_t data;
|
||||
} LongRunningOperation;
|
||||
|
||||
static std::list<std::unique_ptr<LongRunningOperation>> longRunningOperations;
|
||||
static size_t longRunningOperationsCount = 0;
|
||||
#ifdef ESP32
|
||||
static std::mutex longRunningOperationsMutex;
|
||||
#endif
|
||||
|
||||
static void startLongRunningOperation(AsyncWebServerRequestPtr &&requestPtr) {
|
||||
#ifdef ESP32
|
||||
std::lock_guard<std::mutex> lock(longRunningOperationsMutex);
|
||||
#endif
|
||||
|
||||
// LongRunningOperation *op = new LongRunningOperation();
|
||||
std::unique_ptr<LongRunningOperation> op(new LongRunningOperation());
|
||||
op->id = ++longRunningOperationsCount;
|
||||
op->data = 10;
|
||||
|
||||
// you need to hold the AsyncWebServerRequestPtr returned by pause();
|
||||
// This object is authorized to leave the scope of the request handler.
|
||||
op->requestPtr = std::move(requestPtr);
|
||||
|
||||
Serial.printf("[%u] Start long running operation for %" PRIu8 " seconds...\n", op->id, op->data);
|
||||
longRunningOperations.push_back(std::move(op));
|
||||
}
|
||||
|
||||
static bool processLongRunningOperation(LongRunningOperation *op) {
|
||||
// request was deleted ?
|
||||
if (op->requestPtr.expired()) {
|
||||
Serial.printf("[%u] Request was deleted - stopping long running operation\n", op->id);
|
||||
return true; // operation finished
|
||||
}
|
||||
|
||||
// processing the operation
|
||||
Serial.printf("[%u] Long running operation processing... %" PRIu8 " seconds left\n", op->id, op->data);
|
||||
|
||||
// check if we have finished ?
|
||||
op->data--;
|
||||
if (op->data) {
|
||||
// not finished yet
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to get access to the request pointer if it is still exist.
|
||||
// If there has been a disconnection during that time, the pointer won't be valid anymore
|
||||
if (auto request = op->requestPtr.lock()) {
|
||||
Serial.printf("[%u] Long running operation finished! Sending back response...\n", op->id);
|
||||
request->send(200, "text/plain", String(op->id) + " ");
|
||||
|
||||
} else {
|
||||
Serial.printf("[%u] Long running operation finished, but request was deleted!\n", op->id);
|
||||
}
|
||||
|
||||
return true; // operation finished
|
||||
}
|
||||
|
||||
/// ==========================================================
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
// Add a middleware to see how pausing a request affects the middleware chain
|
||||
server.addMiddleware([](AsyncWebServerRequest *request, ArMiddlewareNext next) {
|
||||
Serial.printf("Middleware chain start\n");
|
||||
|
||||
// continue to the next middleware, and at the end the request handler
|
||||
next();
|
||||
|
||||
// we can check the request pause state after the handler was executed
|
||||
if (request->isPaused()) {
|
||||
Serial.printf("Request was paused!\n");
|
||||
}
|
||||
|
||||
Serial.printf("Middleware chain ends\n");
|
||||
});
|
||||
|
||||
// HOW TO RUN THIS EXAMPLE:
|
||||
//
|
||||
// 1. Open several terminals to trigger some requests concurrently that will be paused with:
|
||||
// > time curl -v http://192.168.4.1/
|
||||
//
|
||||
// 2. Look at the output of the Serial console to see how the middleware chain is executed
|
||||
// and to see the long running operations being processed and resume the requests.
|
||||
//
|
||||
// 3. You can try close your curl command to cancel the request and check that the request is deleted.
|
||||
// Note: in case the network is disconnected, the request will be deleted.
|
||||
//
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
// Print a message in case the request is disconnected (network disconnection, client close, etc.)
|
||||
request->onDisconnect([]() {
|
||||
Serial.printf("Request was disconnected!\n");
|
||||
});
|
||||
|
||||
// Instruct ESPAsyncWebServer to pause the request and get a AsyncWebServerRequestPtr to be able to access the request later.
|
||||
// The AsyncWebServerRequestPtr is the ONLY object authorized to leave the scope of the request handler.
|
||||
// The Middleware chain will continue to run until the end after this handler exit, but the request will be paused and will not
|
||||
// be sent to the client until send() is called later.
|
||||
Serial.printf("Pausing request...\n");
|
||||
AsyncWebServerRequestPtr requestPtr = request->pause();
|
||||
|
||||
// start our long operation...
|
||||
startLongRunningOperation(std::move(requestPtr));
|
||||
});
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
static uint32_t lastTime = 0;
|
||||
|
||||
void loop() {
|
||||
if (millis() - lastTime >= 1000) {
|
||||
|
||||
#ifdef ESP32
|
||||
Serial.printf("Free heap: %" PRIu32 "\n", ESP.getFreeHeap());
|
||||
std::lock_guard<std::mutex> lock(longRunningOperationsMutex);
|
||||
#endif
|
||||
|
||||
// process all long running operations
|
||||
longRunningOperations.remove_if([](const std::unique_ptr<LongRunningOperation> &op) {
|
||||
return processLongRunningOperation(op.get());
|
||||
});
|
||||
|
||||
lastTime = millis();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Make sure resumable downloads can be implemented (HEAD request / response and Range header)
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
/*
|
||||
❯ curl -I -X HEAD http://192.168.4.1/download
|
||||
HTTP/1.1 200 OK
|
||||
Content-Length: 1024
|
||||
Content-Type: application/octet-stream
|
||||
Connection: close
|
||||
Accept-Ranges: bytes
|
||||
*/
|
||||
// Ref: https://github.com/mathieucarbou/ESPAsyncWebServer/pull/80
|
||||
server.on("/download", HTTP_HEAD | HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
if (request->method() == HTTP_HEAD) {
|
||||
AsyncWebServerResponse *response = request->beginResponse(200, "application/octet-stream");
|
||||
response->addHeader(asyncsrv::T_Accept_Ranges, "bytes");
|
||||
response->addHeader(asyncsrv::T_Content_Length, 10);
|
||||
response->setContentLength(1024); // make sure we can overrides previously set content length
|
||||
response->addHeader(asyncsrv::T_Content_Type, "foo");
|
||||
response->setContentType("application/octet-stream"); // make sure we can overrides previously set content type
|
||||
// ...
|
||||
request->send(response);
|
||||
} else {
|
||||
// ...
|
||||
}
|
||||
});
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Shows how to rewrite URLs
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
// curl -v http://192.168.4.1/index.txt
|
||||
server.on("/index.txt", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
request->send(200, "text/plain", "Hello, world!");
|
||||
});
|
||||
|
||||
// curl -v http://192.168.4.1/index.txt
|
||||
server.on("/index.html", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
request->send(200, "text/html", "<h1>Hello, world!</h1>");
|
||||
});
|
||||
|
||||
// curl -v http://192.168.4.1/
|
||||
server.rewrite("/", "/index.html");
|
||||
server.rewrite("/index.txt", "/index.html"); // will hide the .txt file
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
// not needed
|
||||
void loop() {
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// SSE example
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
static const char *htmlContent PROGMEM = R"(
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Server-Sent Events</title>
|
||||
<script>
|
||||
if (!!window.EventSource) {
|
||||
var source = new EventSource('/events');
|
||||
source.addEventListener('open', function(e) {
|
||||
console.log("Events Connected");
|
||||
}, false);
|
||||
source.addEventListener('error', function(e) {
|
||||
if (e.target.readyState != EventSource.OPEN) {
|
||||
console.log("Events Disconnected");
|
||||
}
|
||||
}, false);
|
||||
source.addEventListener('message', function(e) {
|
||||
console.log("message", e.data);
|
||||
}, false);
|
||||
source.addEventListener('heartbeat', function(e) {
|
||||
console.log("heartbeat", e.data);
|
||||
}, false);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Open your browser console!</h1>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
|
||||
static const size_t htmlContentLength = strlen_P(htmlContent);
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
static AsyncEventSource events("/events");
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
// curl -v http://192.168.4.1/
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
// need to cast to uint8_t*
|
||||
// if you do not, the const char* will be copied in a temporary String buffer
|
||||
request->send(200, "text/html", (uint8_t *)htmlContent, htmlContentLength);
|
||||
});
|
||||
|
||||
events.onConnect([](AsyncEventSourceClient *client) {
|
||||
Serial.printf("SSE Client connected! ID: %" PRIu32 "\n", client->lastId());
|
||||
client->send("hello!", NULL, millis(), 1000);
|
||||
});
|
||||
|
||||
events.onDisconnect([](AsyncEventSourceClient *client) {
|
||||
Serial.printf("SSE Client disconnected! ID: %" PRIu32 "\n", client->lastId());
|
||||
});
|
||||
|
||||
server.addHandler(&events);
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
static uint32_t lastSSE = 0;
|
||||
static uint32_t deltaSSE = 3000;
|
||||
|
||||
static uint32_t lastHeap = 0;
|
||||
|
||||
void loop() {
|
||||
uint32_t now = millis();
|
||||
if (now - lastSSE >= deltaSSE) {
|
||||
events.send(String("ping-") + now, "heartbeat", now);
|
||||
lastSSE = millis();
|
||||
}
|
||||
|
||||
#ifdef ESP32
|
||||
if (now - lastHeap >= 2000) {
|
||||
Serial.printf("Free heap: %" PRIu32 "\n", ESP.getFreeHeap());
|
||||
lastHeap = now;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// SSE example
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
static const char *htmlContent PROGMEM = R"(
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Server-Sent Events</title>
|
||||
<script>
|
||||
if (!!window.EventSource) {
|
||||
var source = new EventSource('/events');
|
||||
source.onopen = function(e) {
|
||||
console.log("Events Connected");
|
||||
};
|
||||
source.onerror = function(e) {
|
||||
if (e.target.readyState != EventSource.OPEN) {
|
||||
console.log("Events Disconnected");
|
||||
}
|
||||
// Uncomment below to prevent the client from proactively establishing a new connection.
|
||||
// source.close();
|
||||
};
|
||||
source.onmessage = function(e) {
|
||||
console.log("Message: " + e.data);
|
||||
};
|
||||
source.addEventListener('heartbeat', function(e) {
|
||||
console.log("Heartbeat", e.data);
|
||||
}, false);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Open your browser console!</h1>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
|
||||
static const size_t htmlContentLength = strlen_P(htmlContent);
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
static AsyncEventSource events("/events");
|
||||
|
||||
static volatile size_t connectionCount = 0;
|
||||
static volatile uint32_t timestampConnected = 0;
|
||||
static constexpr uint32_t timeoutClose = 15000;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
// curl -v http://192.168.4.1/
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
// need to cast to uint8_t*
|
||||
// if you do not, the const char* will be copied in a temporary String buffer
|
||||
request->send(200, "text/html", (uint8_t *)htmlContent, htmlContentLength);
|
||||
});
|
||||
|
||||
events.onConnect([](AsyncEventSourceClient *client) {
|
||||
/**
|
||||
* @brief: Purpose for a test case: count() function
|
||||
* Task watchdog shall be triggered due to a self-deadlock by mutex handling of the AsyncEventSource.
|
||||
*
|
||||
* E (61642) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
|
||||
* E (61642) task_wdt: - async_tcp (CPU 0/1)
|
||||
*
|
||||
* Resolve: using recursive_mutex insteads of mutex.
|
||||
*/
|
||||
connectionCount = events.count();
|
||||
|
||||
timestampConnected = millis();
|
||||
Serial.printf("SSE Client connected! ID: %" PRIu32 "\n", client->lastId());
|
||||
client->send("hello!", NULL, millis(), 1000);
|
||||
Serial.printf("Number of connected clients: %u\n", connectionCount);
|
||||
});
|
||||
|
||||
events.onDisconnect([](AsyncEventSourceClient *client) {
|
||||
connectionCount = events.count();
|
||||
Serial.printf("SSE Client disconnected! ID: %" PRIu32 "\n", client->lastId());
|
||||
Serial.printf("Number of connected clients: %u\n", connectionCount);
|
||||
});
|
||||
|
||||
server.addHandler(&events);
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
static constexpr uint32_t deltaSSE = 3000;
|
||||
static uint32_t lastSSE = 0;
|
||||
static uint32_t lastHeap = 0;
|
||||
|
||||
void loop() {
|
||||
uint32_t now = millis();
|
||||
if (connectionCount > 0) {
|
||||
if (now - lastSSE >= deltaSSE) {
|
||||
events.send(String("ping-") + now, "heartbeat", now);
|
||||
lastSSE = millis();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief: Purpose for a test case: close() function
|
||||
* Task watchdog shall be triggered due to a self-deadlock by mutex handling of the AsyncEventSource.
|
||||
*
|
||||
* E (61642) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
|
||||
* E (61642) task_wdt: - async_tcp (CPU 0/1)
|
||||
*
|
||||
* Resolve: using recursive_mutex insteads of mutex.
|
||||
*/
|
||||
if (now - timestampConnected >= timeoutClose) {
|
||||
Serial.printf("SSE Clients close\n");
|
||||
events.close();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ESP32
|
||||
if (now - lastHeap >= 2000) {
|
||||
Serial.printf("Free heap: %" PRIu32 "\n", ESP.getFreeHeap());
|
||||
lastHeap = now;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Server state example
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
static AsyncWebServer server1(80);
|
||||
static AsyncWebServer server2(80);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
// server state returns one of the tcp_state enum values:
|
||||
// enum tcp_state {
|
||||
// CLOSED = 0,
|
||||
// LISTEN = 1,
|
||||
// SYN_SENT = 2,
|
||||
// SYN_RCVD = 3,
|
||||
// ESTABLISHED = 4,
|
||||
// FIN_WAIT_1 = 5,
|
||||
// FIN_WAIT_2 = 6,
|
||||
// CLOSE_WAIT = 7,
|
||||
// CLOSING = 8,
|
||||
// LAST_ACK = 9,
|
||||
// TIME_WAIT = 10
|
||||
// };
|
||||
|
||||
assert(server1.state() == tcp_state::CLOSED);
|
||||
assert(server2.state() == tcp_state::CLOSED);
|
||||
|
||||
server1.begin();
|
||||
|
||||
assert(server1.state() == tcp_state::LISTEN);
|
||||
assert(server2.state() == tcp_state::CLOSED);
|
||||
|
||||
server2.begin();
|
||||
|
||||
assert(server1.state() == tcp_state::LISTEN);
|
||||
assert(server2.state() == tcp_state::CLOSED);
|
||||
|
||||
Serial.println("Done!");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Authentication and authorization middlewares
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
static AsyncAuthenticationMiddleware basicAuth;
|
||||
static AsyncLoggingMiddleware logging;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
// basic authentication
|
||||
basicAuth.setUsername("admin");
|
||||
basicAuth.setPassword("admin");
|
||||
basicAuth.setRealm("MyApp");
|
||||
basicAuth.setAuthFailureMessage("Authentication failed");
|
||||
basicAuth.setAuthType(AsyncAuthType::AUTH_BASIC);
|
||||
basicAuth.generateHash(); // precompute hash (optional but recommended)
|
||||
|
||||
// logging middleware
|
||||
logging.setEnabled(true);
|
||||
logging.setOutput(Serial);
|
||||
|
||||
// we apply auth middleware to the server globally
|
||||
server.addMiddleware(&basicAuth);
|
||||
|
||||
// protected endpoint: requires basic authentication
|
||||
// curl -v -u admin:admin http://192.168.4.1/
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
request->send(200, "text/plain", "Hello, world!");
|
||||
});
|
||||
|
||||
// we skip all global middleware from the catchall handler
|
||||
server.catchAllHandler().skipServerMiddlewares();
|
||||
// we apply a specific middleware to the catchall handler only to log requests without a handler defined
|
||||
server.catchAllHandler().addMiddleware(&logging);
|
||||
|
||||
// standard 404 handler: will display the request in the console i na curl-like style
|
||||
// curl -v -H "Foo: Bar" http://192.168.4.1/foo
|
||||
server.onNotFound([](AsyncWebServerRequest *request) {
|
||||
request->send(404, "text/plain", "Not found");
|
||||
});
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
// not needed
|
||||
void loop() {
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Simulate a slow response in a chunk response (like file download from SD Card)
|
||||
// poll events will be throttled.
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
static const char *htmlContent PROGMEM = R"(
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Sample HTML</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello, World!</h1>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
|
||||
static const size_t htmlContentLength = strlen_P(htmlContent);
|
||||
static constexpr char characters[] = "0123456789ABCDEF";
|
||||
static size_t charactersIndex = 0;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
// curl -v http://192.168.4.1/
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
// need to cast to uint8_t*
|
||||
// if you do not, the const char* will be copied in a temporary String buffer
|
||||
request->send(200, "text/html", (uint8_t *)htmlContent, htmlContentLength);
|
||||
});
|
||||
|
||||
// IMPORTANT - DO NOT WRITE SUCH CODE IN PRODUCTON !
|
||||
//
|
||||
// This example simulates the slowdown that can happen when:
|
||||
// - downloading a huge file from sdcard
|
||||
// - doing some file listing on SDCard because it is horribly slow to get a file listing with file stats on SDCard.
|
||||
// So in both cases, ESP would deadlock or TWDT would trigger.
|
||||
//
|
||||
// This example simulats that by slowing down the chunk callback:
|
||||
// - d=2000 is the delay in ms in the callback
|
||||
// - l=10000 is the length of the response
|
||||
//
|
||||
// time curl -N -v -G -d 'd=2000' -d 'l=10000' http://192.168.4.1/slow.html --output -
|
||||
//
|
||||
server.on("/slow.html", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
uint32_t d = request->getParam("d")->value().toInt();
|
||||
uint32_t l = request->getParam("l")->value().toInt();
|
||||
Serial.printf("d = %" PRIu32 ", l = %" PRIu32 "\n", d, l);
|
||||
AsyncWebServerResponse *response = request->beginChunkedResponse("text/html", [d, l](uint8_t *buffer, size_t maxLen, size_t index) -> size_t {
|
||||
Serial.printf("%u\n", index);
|
||||
// finished ?
|
||||
if (index >= l) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// slow down the task to simulate some heavy processing, like SD card reading
|
||||
delay(d);
|
||||
|
||||
memset(buffer, characters[charactersIndex], 256);
|
||||
charactersIndex = (charactersIndex + 1) % sizeof(characters);
|
||||
return 256;
|
||||
});
|
||||
|
||||
request->send(response);
|
||||
});
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
static uint32_t lastHeap = 0;
|
||||
|
||||
void loop() {
|
||||
#ifdef ESP32
|
||||
uint32_t now = millis();
|
||||
if (now - lastHeap >= 2000) {
|
||||
Serial.printf("Free heap: %" PRIu32 "\n", ESP.getFreeHeap());
|
||||
lastHeap = now;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Shows how to serve a static file
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <LittleFS.h>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
static const char *htmlContent PROGMEM = R"(
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Sample HTML</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello, World!</h1>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
|
||||
static const size_t htmlContentLength = strlen_P(htmlContent);
|
||||
|
||||
// sample_html_gz.h
|
||||
static const uint8_t index2_html_gz[] = {
|
||||
0x1f, 0x8b, 0x08, 0x08, 0x13, 0x45, 0x92, 0x68, 0x00, 0x03, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x32, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x00, 0xed, 0x98, 0xcb,
|
||||
0x6e, 0xdc, 0x30, 0x0c, 0x45, 0xf7, 0xf3, 0x15, 0xcc, 0xde, 0xb0, 0x91, 0xbd, 0xe1, 0x4d, 0x1f, 0x48, 0x81, 0xbc, 0x8a, 0x26, 0x2d, 0xba, 0xe4, 0x48,
|
||||
0x8c, 0x87, 0x81, 0x1e, 0x0e, 0x25, 0x19, 0xc8, 0xdf, 0x97, 0xb2, 0x67, 0x82, 0x6c, 0xfa, 0x07, 0x32, 0x0c, 0x58, 0xa6, 0xa8, 0xcb, 0x4b, 0xe9, 0xac,
|
||||
0x34, 0x5e, 0x7d, 0x7d, 0xf8, 0xf2, 0xf4, 0xf7, 0xf1, 0x1b, 0x9c, 0xb2, 0x77, 0xd3, 0x61, 0xbc, 0x7c, 0x08, 0xed, 0x74, 0x00, 0x7d, 0xc6, 0xcc, 0xd9,
|
||||
0xd1, 0xf4, 0x0b, 0xfd, 0xe2, 0x08, 0x6e, 0x9e, 0xee, 0x6e, 0xc7, 0x61, 0x0f, 0x1d, 0xc6, 0x61, 0x4f, 0x1b, 0x8f, 0xd1, 0xbe, 0x9f, 0xb3, 0x4f, 0xd7,
|
||||
0xd3, 0x0d, 0x39, 0x17, 0x3b, 0xf8, 0x13, 0xc5, 0xd9, 0x2b, 0xcd, 0xb9, 0x3e, 0x4f, 0x2d, 0xd3, 0x6d, 0x14, 0xf2, 0xc0, 0x4b, 0x2a, 0x1e, 0x6c, 0x74,
|
||||
0x51, 0x20, 0x71, 0x06, 0xf4, 0x94, 0x3b, 0x30, 0x31, 0x24, 0x32, 0x99, 0x72, 0x11, 0x40, 0xcb, 0x0b, 0x27, 0xc3, 0x61, 0x06, 0x72, 0x9c, 0x7b, 0x78,
|
||||
0x94, 0xc8, 0x01, 0xa8, 0x70, 0xf2, 0xd1, 0x76, 0xb0, 0x14, 0x29, 0x09, 0xf0, 0x12, 0xd8, 0xe4, 0xe5, 0x14, 0x83, 0x29, 0xa9, 0x83, 0x22, 0x01, 0xcf,
|
||||
0x35, 0x4c, 0x91, 0xa4, 0x89, 0x1e, 0x53, 0xc2, 0x4e, 0xb3, 0xc1, 0xb2, 0xc9, 0x1a, 0xcf, 0xea, 0x50, 0xe3, 0xaf, 0x25, 0xe5, 0x08, 0x68, 0xf6, 0x41,
|
||||
0x0f, 0x3f, 0x55, 0xee, 0xad, 0x10, 0x14, 0xe7, 0xd0, 0x9b, 0x28, 0x0b, 0xc9, 0x26, 0x8d, 0x62, 0x0a, 0x04, 0x32, 0x90, 0xa3, 0xe8, 0xfb, 0x79, 0xbe,
|
||||
0x83, 0x95, 0x1c, 0xbc, 0x90, 0x78, 0x0a, 0x55, 0x79, 0x97, 0xfc, 0xf8, 0xef, 0xe1, 0x37, 0xaf, 0xe8, 0xb5, 0x56, 0x22, 0x5b, 0x53, 0xb5, 0xdd, 0x92,
|
||||
0xb7, 0xa6, 0x76, 0x65, 0x63, 0x8a, 0x4f, 0x18, 0x6a, 0xf7, 0x73, 0xad, 0xbc, 0x4f, 0x07, 0xd6, 0x95, 0xcf, 0xb9, 0x3a, 0xde, 0x05, 0x75, 0xe0, 0x50,
|
||||
0xbb, 0x83, 0x15, 0x85, 0xf5, 0x33, 0x0b, 0xae, 0x6c, 0xb1, 0x26, 0xe3, 0xb9, 0x9b, 0x1e, 0xee, 0xab, 0x2f, 0x78, 0x41, 0xc3, 0x8e, 0x13, 0xf7, 0x5b,
|
||||
0x81, 0x1f, 0x21, 0xd3, 0x4c, 0xba, 0xa3, 0xc5, 0x54, 0xe7, 0x9f, 0x37, 0xb9, 0xb8, 0x2c, 0x6c, 0x98, 0x74, 0xe5, 0xf7, 0x92, 0x0c, 0xa9, 0xeb, 0x32,
|
||||
0x33, 0xea, 0x51, 0x78, 0xfe, 0x38, 0x17, 0x38, 0xf2, 0x91, 0x82, 0xd5, 0xce, 0x56, 0x5e, 0x49, 0x44, 0xb7, 0x31, 0x8a, 0x61, 0x70, 0x14, 0x37, 0x7d,
|
||||
0x8b, 0x0b, 0x1f, 0xd5, 0x50, 0xed, 0xa8, 0x03, 0xb6, 0x17, 0x83, 0x49, 0xcf, 0xd9, 0x16, 0xae, 0x91, 0xcd, 0x78, 0x3f, 0x0e, 0x4b, 0xc3, 0xa0, 0x61,
|
||||
0xd0, 0x30, 0x68, 0x18, 0x34, 0x0c, 0x1a, 0x06, 0x0d, 0x83, 0x86, 0x41, 0xc3, 0xa0, 0x61, 0xd0, 0x30, 0x68, 0x18, 0x34, 0x0c, 0x1a, 0x06, 0xff, 0xc1,
|
||||
0x60, 0x1c, 0xf6, 0xab, 0x85, 0x71, 0xd8, 0xee, 0x25, 0xfe, 0x01, 0xa0, 0xec, 0x78, 0xfe, 0xae, 0x10, 0x00, 0x00
|
||||
};
|
||||
|
||||
static const size_t index2_html_gz_len = sizeof(index2_html_gz);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
#ifdef ESP32
|
||||
LittleFS.begin(true);
|
||||
#else
|
||||
LittleFS.begin();
|
||||
#endif
|
||||
|
||||
{
|
||||
File f = LittleFS.open("/index.html", "w");
|
||||
assert(f);
|
||||
f.print(htmlContent);
|
||||
f.close();
|
||||
}
|
||||
|
||||
{
|
||||
File f = LittleFS.open("/index2.html.gz", "w");
|
||||
assert(f);
|
||||
f.write(index2_html_gz, index2_html_gz_len);
|
||||
f.close();
|
||||
}
|
||||
|
||||
LittleFS.mkdir("/files");
|
||||
|
||||
{
|
||||
File f = LittleFS.open("/files/a.txt", "w");
|
||||
assert(f);
|
||||
f.print("Hello from a.txt");
|
||||
f.close();
|
||||
}
|
||||
|
||||
{
|
||||
File f = LittleFS.open("/files/b.txt", "w");
|
||||
assert(f);
|
||||
f.print("Hello from b.txt");
|
||||
f.close();
|
||||
}
|
||||
|
||||
// curl -v http://192.168.4.1/
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
request->redirect("/index.html");
|
||||
});
|
||||
|
||||
// curl -v http://192.168.4.1/index.html
|
||||
server.serveStatic("/index.html", LittleFS, "/index.html");
|
||||
|
||||
// curl -v http://192.168.4.1/index2.html | gunzip -c
|
||||
server.serveStatic("/index2.html", LittleFS, "/index2.html");
|
||||
|
||||
// Example to serve a directory content
|
||||
// curl -v http://192.168.4.1/base/ => serves a.txt
|
||||
// curl -v http://192.168.4.1/base/a.txt => serves a.txt
|
||||
// curl -v http://192.168.4.1/base/b.txt => serves b.txt
|
||||
server.serveStatic("/base", LittleFS, "/files").setDefaultFile("a.txt");
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
// not needed
|
||||
void loop() {
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Shows how to serve a static and dynamic template
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <LittleFS.h>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
static const char *htmlContent PROGMEM = R"(
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Hello, %USER%</h1>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
|
||||
static const size_t htmlContentLength = strlen_P(htmlContent);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
#ifdef ESP32
|
||||
LittleFS.begin(true);
|
||||
#else
|
||||
LittleFS.begin();
|
||||
#endif
|
||||
|
||||
{
|
||||
File f = LittleFS.open("/template.html", "w");
|
||||
assert(f);
|
||||
f.print(htmlContent);
|
||||
f.close();
|
||||
}
|
||||
|
||||
// Serve the static template file
|
||||
//
|
||||
// curl -v http://192.168.4.1/template.html
|
||||
server.serveStatic("/template.html", LittleFS, "/template.html");
|
||||
|
||||
// Serve the static template with a template processor
|
||||
//
|
||||
// ServeStatic static is used to serve static output which never changes over time.
|
||||
// This special endpoints automatically adds caching headers.
|
||||
// If a template processor is used, it must ensure that the outputted content will always be the same over time and never changes.
|
||||
// Otherwise, do not use serveStatic.
|
||||
// Example below: IP never changes.
|
||||
//
|
||||
// curl -v http://192.168.4.1/index.html
|
||||
server.serveStatic("/index.html", LittleFS, "/template.html").setTemplateProcessor([](const String &var) -> String {
|
||||
if (var == "USER") {
|
||||
return "Bob";
|
||||
}
|
||||
return emptyString;
|
||||
});
|
||||
|
||||
// Serve a template with dynamic content
|
||||
//
|
||||
// to serve a template with dynamic content (output changes over time), use normal
|
||||
// Example below: content changes over tinme do not use serveStatic.
|
||||
//
|
||||
// curl -v http://192.168.4.1/dynamic.html
|
||||
server.on("/dynamic.html", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
request->send(LittleFS, "/template.html", "text/html", false, [](const String &var) -> String {
|
||||
if (var == "USER") {
|
||||
return String("Bob ") + millis();
|
||||
}
|
||||
return emptyString;
|
||||
});
|
||||
});
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
// not needed
|
||||
void loop() {
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// Demo text, binary and file upload
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <StreamString.h>
|
||||
#include <LittleFS.h>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
if (!LittleFS.begin()) {
|
||||
LittleFS.format();
|
||||
LittleFS.begin();
|
||||
}
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
// 1. Generate a Lorem_ipsum.txt file of about 20KB of text
|
||||
//
|
||||
// 3. Run: curl -v -F "data=@Lorem_ipsum.txt" http://192.168.4.1/upload/text
|
||||
//
|
||||
server.on(
|
||||
"/upload/text", HTTP_POST,
|
||||
[](AsyncWebServerRequest *request) {
|
||||
if (!request->_tempObject) {
|
||||
return request->send(400, "text/plain", "Nothing uploaded");
|
||||
}
|
||||
StreamString *buffer = reinterpret_cast<StreamString *>(request->_tempObject);
|
||||
Serial.printf("Text uploaded:\n%s\n", buffer->c_str());
|
||||
delete buffer;
|
||||
request->_tempObject = nullptr;
|
||||
request->send(200, "text/plain", "OK");
|
||||
},
|
||||
[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
|
||||
Serial.printf("Upload[%s]: start=%u, len=%u, final=%d\n", filename.c_str(), index, len, final);
|
||||
|
||||
if (!index) {
|
||||
// first pass
|
||||
StreamString *buffer = new StreamString();
|
||||
size_t size = std::max(4094l, request->header("Content-Length").toInt());
|
||||
Serial.printf("Allocating string buffer of %u bytes\n", size);
|
||||
if (!buffer->reserve(size)) {
|
||||
delete buffer;
|
||||
request->abort();
|
||||
}
|
||||
request->_tempObject = buffer;
|
||||
}
|
||||
|
||||
if (len) {
|
||||
reinterpret_cast<StreamString *>(request->_tempObject)->write(data, len);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 1. Generate a Lorem_ipsum.txt file of about 20KB of text
|
||||
//
|
||||
// 3. Run: curl -v -F "data=@Lorem_ipsum.txt" http://192.168.4.1/upload/file
|
||||
//
|
||||
server.on(
|
||||
"/upload/file", HTTP_POST,
|
||||
[](AsyncWebServerRequest *request) {
|
||||
if (request->getResponse()) {
|
||||
// 400 File not available for writing
|
||||
return;
|
||||
}
|
||||
|
||||
if (!LittleFS.exists("/my_file.txt")) {
|
||||
return request->send(400, "text/plain", "Nothing uploaded");
|
||||
}
|
||||
|
||||
// sends back the uploaded file
|
||||
request->send(LittleFS, "/my_file.txt", "text/plain");
|
||||
},
|
||||
[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
|
||||
Serial.printf("Upload[%s]: start=%u, len=%u, final=%d\n", filename.c_str(), index, len, final);
|
||||
|
||||
if (!index) {
|
||||
request->_tempFile = LittleFS.open("/my_file.txt", "w");
|
||||
|
||||
if (!request->_tempFile) {
|
||||
request->send(400, "text/plain", "File not available for writing");
|
||||
}
|
||||
}
|
||||
if (len) {
|
||||
request->_tempFile.write(data, len);
|
||||
}
|
||||
if (final) {
|
||||
request->_tempFile.close();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
//
|
||||
// Upload a binary file: curl -v -F "data=@file.mp3" http://192.168.4.1/upload/binary
|
||||
//
|
||||
server.on(
|
||||
"/upload/binary", HTTP_POST,
|
||||
[](AsyncWebServerRequest *request) {
|
||||
// response already set ?
|
||||
if (request->getResponse()) {
|
||||
// 400 No Content-Length
|
||||
return;
|
||||
}
|
||||
|
||||
// nothing uploaded ?
|
||||
if (!request->_tempObject) {
|
||||
return request->send(400, "text/plain", "Nothing uploaded");
|
||||
}
|
||||
|
||||
uint8_t *buffer = reinterpret_cast<uint8_t *>(request->_tempObject);
|
||||
// process the buffer
|
||||
|
||||
delete buffer;
|
||||
request->_tempObject = nullptr;
|
||||
|
||||
request->send(200, "text/plain", "OK");
|
||||
},
|
||||
[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
|
||||
Serial.printf("Upload[%s]: start=%u, len=%u, final=%d\n", filename.c_str(), index, len, final);
|
||||
|
||||
// first pass ?
|
||||
if (!index) {
|
||||
size_t size = request->header("Content-Length").toInt();
|
||||
if (!size) {
|
||||
request->send(400, "text/plain", "No Content-Length");
|
||||
} else {
|
||||
Serial.printf("Allocating buffer of %u bytes\n", size);
|
||||
uint8_t *buffer = new (std::nothrow) uint8_t[size];
|
||||
if (!buffer) {
|
||||
// not enough memory
|
||||
request->abort();
|
||||
} else {
|
||||
request->_tempObject = buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (len) {
|
||||
memcpy(reinterpret_cast<uint8_t *>(request->_tempObject) + index, data, len);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
// not needed
|
||||
void loop() {
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// WebSocket example
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
static AsyncWebSocket ws("/ws");
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
//
|
||||
// Run in terminal 1: websocat ws://192.168.4.1/ws => should stream data
|
||||
// Run in terminal 2: websocat ws://192.168.4.1/ws => should stream data
|
||||
// Run in terminal 3: websocat ws://192.168.4.1/ws => should fail:
|
||||
//
|
||||
// To send a message to the WebSocket server:
|
||||
//
|
||||
// echo "Hello!" | websocat ws://192.168.4.1/ws
|
||||
//
|
||||
ws.onEvent([](AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
|
||||
(void)len;
|
||||
|
||||
if (type == WS_EVT_CONNECT) {
|
||||
ws.textAll("new client connected");
|
||||
Serial.println("ws connect");
|
||||
client->setCloseClientOnQueueFull(false);
|
||||
client->ping();
|
||||
|
||||
} else if (type == WS_EVT_DISCONNECT) {
|
||||
ws.textAll("client disconnected");
|
||||
Serial.println("ws disconnect");
|
||||
|
||||
} else if (type == WS_EVT_ERROR) {
|
||||
Serial.println("ws error");
|
||||
|
||||
} else if (type == WS_EVT_PONG) {
|
||||
Serial.println("ws pong");
|
||||
|
||||
} else if (type == WS_EVT_DATA) {
|
||||
AwsFrameInfo *info = (AwsFrameInfo *)arg;
|
||||
Serial.printf("index: %" PRIu64 ", len: %" PRIu64 ", final: %" PRIu8 ", opcode: %" PRIu8 "\n", info->index, info->len, info->final, info->opcode);
|
||||
String msg = "";
|
||||
if (info->final && info->index == 0 && info->len == len) {
|
||||
if (info->opcode == WS_TEXT) {
|
||||
data[len] = 0;
|
||||
Serial.printf("ws text: %s\n", (char *)data);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// shows how to prevent a third WS client to connect
|
||||
server.addHandler(&ws).addMiddleware([](AsyncWebServerRequest *request, ArMiddlewareNext next) {
|
||||
// ws.count() is the current count of WS clients: this one is trying to upgrade its HTTP connection
|
||||
if (ws.count() > 1) {
|
||||
// if we have 2 clients or more, prevent the next one to connect
|
||||
request->send(503, "text/plain", "Server is busy");
|
||||
} else {
|
||||
// process next middleware and at the end the handler
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
server.addHandler(&ws);
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
static uint32_t lastWS = 0;
|
||||
static uint32_t deltaWS = 100;
|
||||
|
||||
static uint32_t lastHeap = 0;
|
||||
|
||||
void loop() {
|
||||
uint32_t now = millis();
|
||||
|
||||
if (now - lastWS >= deltaWS) {
|
||||
ws.printfAll("kp%.4f", (10.0 / 3.0));
|
||||
lastWS = millis();
|
||||
}
|
||||
|
||||
if (now - lastHeap >= 2000) {
|
||||
Serial.printf("Connected clients: %u / %u total\n", ws.count(), ws.getClients().size());
|
||||
|
||||
// this can be called to also set a soft limit on the number of connected clients
|
||||
ws.cleanupClients(2); // no more than 2 clients
|
||||
|
||||
#ifdef ESP32
|
||||
Serial.printf("Free heap: %" PRIu32 "\n", ESP.getFreeHeap());
|
||||
#endif
|
||||
lastHeap = now;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
//
|
||||
// WebSocket example using the easy to use AsyncWebSocketMessageHandler handler that only supports unfragmented messages
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
// create an easy-to-use handler
|
||||
static AsyncWebSocketMessageHandler wsHandler;
|
||||
|
||||
// add it to the websocket server
|
||||
static AsyncWebSocket ws("/ws", wsHandler.eventHandler());
|
||||
|
||||
// alternatively you can do as usual:
|
||||
//
|
||||
// static AsyncWebSocket ws("/ws");
|
||||
// ws.onEvent(wsHandler.eventHandler());
|
||||
|
||||
static const char *htmlContent PROGMEM = R"(
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>WebSocket</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>WebSocket Example</h1>
|
||||
<>Open your browser console!</p>
|
||||
<input type="text" id="message" placeholder="Type a message">
|
||||
<button onclick='sendMessage()'>Send</button>
|
||||
<script>
|
||||
var ws = new WebSocket('ws://192.168.4.1/ws');
|
||||
ws.onopen = function() {
|
||||
console.log("WebSocket connected");
|
||||
};
|
||||
ws.onmessage = function(event) {
|
||||
console.log("WebSocket message: " + event.data);
|
||||
};
|
||||
ws.onclose = function() {
|
||||
console.log("WebSocket closed");
|
||||
};
|
||||
ws.onerror = function(error) {
|
||||
console.log("WebSocket error: " + error);
|
||||
};
|
||||
function sendMessage() {
|
||||
var message = document.getElementById("message").value;
|
||||
ws.send(message);
|
||||
console.log("WebSocket sent: " + message);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
static const size_t htmlContentLength = strlen_P(htmlContent);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
// serves root html page
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
request->send(200, "text/html", (const uint8_t *)htmlContent, htmlContentLength);
|
||||
});
|
||||
|
||||
wsHandler.onConnect([](AsyncWebSocket *server, AsyncWebSocketClient *client) {
|
||||
Serial.printf("Client %" PRIu32 " connected\n", client->id());
|
||||
server->textAll("New client: " + String(client->id()));
|
||||
});
|
||||
|
||||
wsHandler.onDisconnect([](AsyncWebSocket *server, uint32_t clientId) {
|
||||
Serial.printf("Client %" PRIu32 " disconnected\n", clientId);
|
||||
server->textAll("Client " + String(clientId) + " disconnected");
|
||||
});
|
||||
|
||||
wsHandler.onError([](AsyncWebSocket *server, AsyncWebSocketClient *client, uint16_t errorCode, const char *reason, size_t len) {
|
||||
Serial.printf("Client %" PRIu32 " error: %" PRIu16 ": %s\n", client->id(), errorCode, reason);
|
||||
});
|
||||
|
||||
wsHandler.onMessage([](AsyncWebSocket *server, AsyncWebSocketClient *client, const uint8_t *data, size_t len) {
|
||||
Serial.printf("Client %" PRIu32 " data: %s\n", client->id(), (const char *)data);
|
||||
});
|
||||
|
||||
wsHandler.onFragment([](AsyncWebSocket *server, AsyncWebSocketClient *client, const AwsFrameInfo *frameInfo, const uint8_t *data, size_t len) {
|
||||
Serial.printf("Client %" PRIu32 " fragment %" PRIu32 ": %s\n", client->id(), frameInfo->num, (const char *)data);
|
||||
});
|
||||
|
||||
server.addHandler(&ws);
|
||||
server.begin();
|
||||
}
|
||||
|
||||
static uint32_t lastWS = 0;
|
||||
static uint32_t deltaWS = 2000;
|
||||
|
||||
void loop() {
|
||||
uint32_t now = millis();
|
||||
|
||||
if (now - lastWS >= deltaWS) {
|
||||
ws.cleanupClients();
|
||||
ws.printfAll("now: %" PRIu32 "\n", now);
|
||||
lastWS = millis();
|
||||
#ifdef ESP32
|
||||
Serial.printf("Free heap: %" PRIu32 "\n", ESP.getFreeHeap());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user