Aktualizace na verzi 3.3.23

This commit is contained in:
2024-12-05 10:21:12 +01:00
parent 2333497adc
commit 7c828c70d8
36 changed files with 1436 additions and 1204 deletions

View File

@ -16,10 +16,7 @@ AsyncWebServer server(80);
class CaptiveRequestHandler : public AsyncWebHandler {
public:
CaptiveRequestHandler() {}
virtual ~CaptiveRequestHandler() {}
bool canHandle(__unused AsyncWebServerRequest* request) {
bool canHandle(__unused AsyncWebServerRequest* request) const override {
return true;
}

View File

@ -1,37 +0,0 @@
#include "mbedtls/md5.h"
#include <Arduino.h>
#include <MD5Builder.h>
void setup() {
Serial.begin(115200);
delay(2000);
const char* data = "Hello World";
{
uint8_t md5[16];
mbedtls_md5_context _ctx;
mbedtls_md5_init(&_ctx);
mbedtls_md5_starts(&_ctx);
mbedtls_md5_update(&_ctx, (const unsigned char*)data, strlen(data));
mbedtls_md5_finish(&_ctx, md5);
char output[33];
for (int i = 0; i < 16; i++) {
sprintf_P(output + (i * 2), PSTR("%02x"), md5[i]);
}
Serial.println(String(output));
}
{
MD5Builder md5;
md5.begin();
md5.add(data, strlen(data);
md5.calculate();
char output[33];
md5.getChars(output);
Serial.println(String(output));
}
}
void loop() {
}

View File

@ -18,14 +18,11 @@ AsyncWebServer server(80);
class CaptiveRequestHandler : public AsyncWebHandler {
public:
CaptiveRequestHandler() {}
virtual ~CaptiveRequestHandler() {}
bool canHandle(__unused AsyncWebServerRequest* request) {
bool canHandle(__unused AsyncWebServerRequest* request) const override {
return true;
}
void handleRequest(AsyncWebServerRequest* request) {
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>");

View File

@ -0,0 +1,84 @@
/**
*
* Connect to AP and run in bash:
*
* > while true; do echo "PING"; sleep 0.1; done | websocat ws://192.168.4.1/ws
*
*/
#include <Arduino.h>
#ifdef ESP8266
#include <ESP8266WiFi.h>
#endif
#ifdef ESP32
#include <WiFi.h>
#endif
#include <ESPAsyncWebServer.h>
#include <list>
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
void onWsEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len) {
if (type == WS_EVT_CONNECT) {
Serial.printf("Client #%" PRIu32 " connected.\n", client->id());
} else if (type == WS_EVT_DISCONNECT) {
Serial.printf("Client #%" PRIu32 " disconnected.\n", client->id());
} else if (type == WS_EVT_ERROR) {
Serial.printf("Client #%" PRIu32 " error.\n", client->id());
} else if (type == WS_EVT_DATA) {
Serial.printf("Client #%" PRIu32 " len: %u\n", client->id(), len);
} else if (type == WS_EVT_PONG) {
Serial.printf("Client #%" PRIu32 " pong.\n", client->id());
} else if (type == WS_EVT_PING) {
Serial.printf("Client #%" PRIu32 " ping.\n", client->id());
}
}
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_AP);
WiFi.softAP("esp-captive");
ws.onEvent(onWsEvent);
server.addHandler(&ws);
server.on("/close_all_ws_clients", HTTP_GET | HTTP_POST, [](AsyncWebServerRequest* request) {
Serial.println("Closing all WebSocket clients...");
ws.closeAll();
request->send(200, "application/json", "{\"status\":\"all clients closed\"}");
});
server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
request->send(200, "text/html", R"rawliteral(
<!DOCTYPE html>
<html>
<head></head>
<script>
let ws = new WebSocket("ws://" + window.location.host + "/ws");
document.addEventListener("DOMContentLoaded", function () {
ws.onopen = function () {
console.log("WebSocket connected");
};
});
function closeAllWsClients() {
fetch("/close_all_ws_clients", {
method : "POST",
});
};
</script>
<body>
<button onclick = "closeAllWsClients()" style = "width: 200px"> ws close all</button><p></p>
</body>
</html>
)rawliteral");
});
server.begin();
}
void loop() {
vTaskDelete(NULL);
}

View File

@ -0,0 +1,127 @@
/**
*
* Connect to AP and run in bash:
*
* > while true; do echo "PING"; sleep 0.1; done | websocat ws://192.168.4.1/ws
*
*/
#include <Arduino.h>
#ifdef ESP8266
#include <ESP8266WiFi.h>
#endif
#ifdef ESP32
#include <WiFi.h>
#endif
#include <ESPAsyncWebServer.h>
#include <list>
size_t msgCount = 0;
size_t window = 100;
std::list<uint32_t> times;
void connect_wifi() {
WiFi.mode(WIFI_AP);
WiFi.softAP("esp-captive");
// Serial.print("Connecting");
// while (WiFi.status() != WL_CONNECTED) {
// delay(500);
// Serial.print(".");
// }
// Serial.println();
// Serial.print("Connected, IP address: ");
// Serial.println(WiFi.localIP());
}
void notFound(AsyncWebServerRequest* request) {
request->send(404, "text/plain", "Not found");
}
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
// initial stack
char* stack_start;
void printStackSize() {
char stack;
Serial.print(F("stack size "));
Serial.print(stack_start - &stack);
Serial.print(F(" | Heap free:"));
Serial.print(ESP.getFreeHeap());
#ifdef ESP8266
Serial.print(F(" frag:"));
Serial.print(ESP.getHeapFragmentation());
Serial.print(F(" maxFreeBlock:"));
Serial.print(ESP.getMaxFreeBlockSize());
#endif
Serial.println();
}
void onWsEventEmpty(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len) {
msgCount++;
Serial.printf("count: %d\n", msgCount);
times.push_back(millis());
while (times.size() > window)
times.pop_front();
if (times.size() == window)
Serial.printf("%f req/s\n", 1000.0 * window / (times.back() - times.front()));
printStackSize();
client->text("PONG");
}
void serve_upload(AsyncWebServerRequest* request, const String& filename, size_t index, uint8_t* data, size_t len, bool final) {
Serial.print("> onUpload ");
Serial.print("index: ");
Serial.print(index);
Serial.print(" len:");
Serial.print(len);
Serial.print(" final:");
Serial.print(final);
Serial.println();
}
void setup() {
char stack;
stack_start = &stack;
Serial.begin(115200);
Serial.println("\n\n\nStart");
Serial.printf("stack_start: %p\n", stack_start);
connect_wifi();
server.onNotFound(notFound);
ws.onEvent(onWsEventEmpty);
server.addHandler(&ws);
server.on("/upload", HTTP_POST, [](AsyncWebServerRequest* request) { request->send(200); }, serve_upload);
server.begin();
Serial.println("Server started");
}
String msg = "";
uint32_t count = 0;
void loop() {
// ws.cleanupClients();
// count += 1;
// // Concatenate some string, and clear it after some time
// static unsigned long millis_string = millis();
// if (millis() - millis_string > 1) {
// millis_string += 100;
// if (count % 100 == 0) {
// // printStackSize();
// // Serial.print(msg);
// msg = String();
// } else {
// msg += 'T';
// }
// }
}

View File

@ -19,7 +19,7 @@
#include <ESPAsyncWebServer.h>
#if ASYNC_JSON_SUPPORT == 1
#if __has_include("ArduinoJson.h")
#include <ArduinoJson.h>
#include <AsyncJson.h>
#include <AsyncMessagePack.h>
@ -27,6 +27,80 @@
#include <LittleFS.h>
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>
)";
const size_t htmlContentLength = strlen_P(htmlContent);
const char* staticContent PROGMEM = R"(
<!DOCTYPE html>
<html>
<head>
<title>Sample HTML</title>
</head>
<body>
<h1>Hello, %IP%</h1>
</body>
</html>
)";
AsyncWebServer server(80);
AsyncEventSource events("/events");
AsyncWebSocket ws("/ws");
@ -111,11 +185,14 @@ void notFound(AsyncWebServerRequest* request) {
request->send(404, "text/plain", "Not found");
}
#if ASYNC_JSON_SUPPORT == 1
#if __has_include("ArduinoJson.h")
AsyncCallbackJsonWebHandler* jsonHandler = new AsyncCallbackJsonWebHandler("/json2");
AsyncCallbackMessagePackWebHandler* msgPackHandler = new AsyncCallbackMessagePackWebHandler("/msgpack2");
#endif
static const char characters[] = "0123456789ABCDEF";
static size_t charactersIndex = 0;
void setup() {
Serial.begin(115200);
@ -134,6 +211,32 @@ void setup() {
WiFi.softAP("esp-captive");
#endif
#ifdef ESP32
LittleFS.begin(true);
#else
LittleFS.begin();
#endif
{
File f = LittleFS.open("/index.txt", "w");
if (f) {
for (size_t c = 0; c < sizeof(characters); c++) {
for (size_t i = 0; i < 1024; i++) {
f.print(characters[c]);
}
}
f.close();
}
}
{
File f = LittleFS.open("/index.html", "w");
if (f) {
f.print(staticContent);
f.close();
}
}
// curl -v -X GET http://192.168.4.1/handler-not-sending-response
server.on("/handler-not-sending-response", HTTP_GET, [](AsyncWebServerRequest* request) {
// handler forgot to send a response to the client => 501 Not Implemented
@ -215,16 +318,18 @@ void setup() {
headerFree.keep("X-Keep-Me");
headerFree.keep("host");
// global middleware
server.addMiddleware(&requestLogger);
server.addMiddlewares({&rateLimit, &cors, &headerFilter});
cors.setOrigin("http://192.168.4.1");
cors.setMethods("POST, GET, OPTIONS, DELETE");
cors.setHeaders("X-Custom-Header");
cors.setAllowCredentials(false);
cors.setMaxAge(600);
#ifndef PERF_TEST
// global middleware
server.addMiddleware(&requestLogger);
server.addMiddlewares({&rateLimit, &cors, &headerFilter});
#endif
// Test CORS preflight request
// curl -v -X OPTIONS -H "origin: http://192.168.4.1" http://192.168.4.1/middleware/cors
server.on("/middleware/cors", HTTP_GET, [](AsyncWebServerRequest* request) {
@ -299,12 +404,103 @@ void setup() {
request->redirect("/");
});
// PERF TEST:
// > brew install autocannon
// > autocannon -c 10 -w 10 -d 20 http://192.168.4.1
server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
request->send(200, "text/plain", "Hello, world");
request->send(200, "text/html", htmlContent);
});
server.on("/file", HTTP_GET, [](AsyncWebServerRequest* request) {
request->send(LittleFS, "/index.html");
// curl -v -X GET http://192.168.4.1/index.txt
server.serveStatic("/index.txt", LittleFS, "/index.txt");
// curl -v -X GET http://192.168.4.1/index-private.txt
server.serveStatic("/index-private.txt", LittleFS, "/index.txt").setAuthentication("admin", "admin");
// ServeStatic static is used to serve static output which never changes over time.
// This special endpoints automatyically adds caching headers.
// If a template processor is used, it must enure that the outputed content will always be the ame over time and never changes.
// Otherwise, do not use serveStatic.
// Example below: IP never changes.
// curl -v -X GET http://192.168.4.1/index-static.html
server.serveStatic("/index-static.html", LittleFS, "/index.html").setTemplateProcessor([](const String& var) -> String {
if (var == "IP") {
// for CI, commented out since H2 board doesn ot support WiFi
// return WiFi.localIP().toString();
// return WiFi.softAPIP().toString();
return "127.0.0..1";
}
return emptyString;
});
// 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 -X GET http://192.168.4.1/index-dynamic.html
server.on("/index-dynamic.html", HTTP_GET, [](AsyncWebServerRequest* request) {
request->send(LittleFS, "/index.html", "text/html", false, [](const String& var) -> String {
if (var == "IP")
return String(random(0, 1000));
return emptyString;
});
});
// Issue #14: assert failed: tcp_update_rcv_ann_wnd (needs help to test fix)
// > curl -v http://192.168.4.1/issue-14
pinMode(4, OUTPUT);
server.on("/issue-14", HTTP_GET, [](AsyncWebServerRequest* request) {
digitalWrite(4, HIGH);
request->send(LittleFS, "/index.txt", "text/pain");
delay(500);
digitalWrite(4, LOW);
});
/*
Chunked encoding test: sends 16k of characters.
curl -N -v -X GET -H "origin: http://192.168.4.1" http://192.168.4.1/chunk
*/
server.on("/chunk", HTTP_HEAD | HTTP_GET, [](AsyncWebServerRequest* request) {
AsyncWebServerResponse* response = request->beginChunkedResponse("text/html", [](uint8_t* buffer, size_t maxLen, size_t index) -> size_t {
if (index >= 16384)
return 0;
memset(buffer, characters[charactersIndex], maxLen);
charactersIndex = (charactersIndex + 1) % sizeof(characters);
return maxLen;
});
request->send(response);
});
// curl -N -v -X GET http://192.168.4.1/chunked.html --output -
// curl -N -v -X GET -H "if-none-match: 4272" http://192.168.4.1/chunked.html --output -
server.on("/chunked.html", HTTP_GET, [](AsyncWebServerRequest* request) {
String len = String(htmlContentLength);
if (request->header(asyncsrv::T_INM) == len) {
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 1024 or maxLen bytes of the remaining content
const int chunkSize = min((size_t)1024, 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, len);
request->send(response);
});
/*
@ -353,14 +549,12 @@ void setup() {
request->send(200, "text/plain", "Hello, POST: " + message);
});
#if ASYNC_JSON_SUPPORT == 1
#if __has_include("ArduinoJson.h")
// JSON
// receives JSON and sends JSON
jsonHandler->onRequest([](AsyncWebServerRequest* request, JsonVariant& json) {
// JsonObject jsonObj = json.as<JsonObject>();
// ...
// sends JSON
// curl -v -X GET 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";
@ -368,11 +562,24 @@ void setup() {
request->send(response);
});
// sends JSON
server.on("/json1", HTTP_GET, [](AsyncWebServerRequest* request) {
// curl -v -X GET 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);
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
jsonHandler->setMethod(HTTP_POST | HTTP_PUT);
jsonHandler->onRequest([](AsyncWebServerRequest* request, JsonVariant& json) {
serializeJson(json, Serial);
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject root = response->getRoot().to<JsonObject>();
root["hello"] = "world";
root["hello"] = json.as<JsonObject>()["name"];
response->setLength();
request->send(response);
});
@ -436,10 +643,69 @@ void setup() {
}
});
// SSS endpoints
// sends a message every 10 ms
//
// go to http://192.168.4.1/sse
// > curl -v -N -H "Accept: text/event-stream" http://192.168.4.1/events
//
// some perf tests:
// launch 16 concurrent workers for 30 seconds
// > 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 Too many messages queued: deleting message
//
// Total: 119 events, 29.75000000000000000000 events / second
// Total: 727 events, 181.75000000000000000000 events / second
// Total: 1386 events, 346.50000000000000000000 events / second
// Total: 1385 events, 346.25000000000000000000 events / second
// Total: 1276 events, 319.00000000000000000000 events / second
// Total: 1411 events, 352.75000000000000000000 events / second
// Total: 1276 events, 319.00000000000000000000 events / second
// Total: 1333 events, 333.25000000000000000000 events / second
// Total: 1250 events, 312.50000000000000000000 events / second
// Total: 1275 events, 318.75000000000000000000 events / second
// Total: 1271 events, 317.75000000000000000000 events / second
// Total: 1271 events, 317.75000000000000000000 events / second
// Total: 1254 events, 313.50000000000000000000 events / second
// Total: 1251 events, 312.75000000000000000000 events / second
// Total: 1254 events, 313.50000000000000000000 events / second
// Total: 1262 events, 315.50000000000000000000 events / second
//
// With AsyncTCP, with 10 workers:
//
// Total: 1875 events, 468.75000000000000000000 events / second
// Total: 1870 events, 467.50000000000000000000 events / second
// Total: 1871 events, 467.75000000000000000000 events / second
// Total: 1875 events, 468.75000000000000000000 events / second
// Total: 1871 events, 467.75000000000000000000 events / second
// Total: 1805 events, 451.25000000000000000000 events / second
// Total: 1803 events, 450.75000000000000000000 events / second
// Total: 1873 events, 468.25000000000000000000 events / second
// Total: 1872 events, 468.00000000000000000000 events / second
// Total: 1805 events, 451.25000000000000000000 events / second
//
// With AsyncTCPSock, with 16 workers: ESP32 CRASH !!!
//
// With AsyncTCPSock, with 10 workers:
//
// Total: 1242 events, 310.50000000000000000000 events / second
// Total: 1242 events, 310.50000000000000000000 events / second
// Total: 1242 events, 310.50000000000000000000 events / second
// Total: 1242 events, 310.50000000000000000000 events / second
// Total: 1181 events, 295.25000000000000000000 events / second
// Total: 1182 events, 295.50000000000000000000 events / second
// Total: 1240 events, 310.00000000000000000000 events / second
// Total: 1181 events, 295.25000000000000000000 events / second
// Total: 1181 events, 295.25000000000000000000 events / second
// Total: 1183 events, 295.75000000000000000000 events / second
//
server.addHandler(&events);
// Run: websocat ws://192.168.4.1/ws
server.addHandler(&ws);
#if ASYNC_JSON_SUPPORT == 1
#if __has_include("ArduinoJson.h")
server.addHandler(jsonHandler);
server.addHandler(msgPackHandler);
#endif
@ -450,7 +716,7 @@ void setup() {
}
uint32_t lastSSE = 0;
uint32_t deltaSSE = 5;
uint32_t deltaSSE = 10;
uint32_t lastWS = 0;
uint32_t deltaWS = 100;
@ -463,9 +729,8 @@ void loop() {
}
if (now - lastWS >= deltaWS) {
ws.printfAll("kp%.4f", (10.0 / 3.0));
// ws.getClients
for (auto& client : ws.getClients()) {
client.text("kp%.4f", (10.0 / 3.0));
client.printf("kp%.4f", (10.0 / 3.0));
}
lastWS = millis();
}

View File

@ -10,11 +10,13 @@
#include <WebServer.h>
#include <WiFi.h>
#endif
#include "StreamConcat.h"
#include "StreamString.h"
#include <StreamString.h>
#include <ESPAsyncWebServer.h>
#include <LittleFS.h>
#include "StreamConcat.h"
DNSServer dnsServer;
AsyncWebServer server(80);

View File

@ -1,40 +0,0 @@
#pragma once
#include <Stream.h>
class StreamString : public Stream {
public:
size_t write(const uint8_t* p, size_t n) override { return _buffer.concat(reinterpret_cast<const char*>(p), n) ? n : 0; }
size_t write(uint8_t c) override { return _buffer.concat(static_cast<char>(c)) ? 1 : 0; }
void flush() override {}
int available() override { return static_cast<int>(_buffer.length()); }
int read() override {
if (_buffer.length() == 0)
return -1;
char c = _buffer[0];
_buffer.remove(0, 1);
return c;
}
#if defined(TARGET_RP2040)
size_t readBytes(char* buffer, size_t length) {
#else
size_t readBytes(char* buffer, size_t length) override {
#endif
if (length > _buffer.length())
length = _buffer.length();
// Don't use _str.ToCharArray() because it inserts a terminator
memcpy(buffer, _buffer.c_str(), length);
_buffer.remove(0, static_cast<unsigned int>(length));
return length;
}
int peek() override { return _buffer.length() > 0 ? _buffer[0] : -1; }
const String& buffer() const { return _buffer; }
private:
String _buffer;
};

View File

@ -1,107 +0,0 @@
#include <DNSServer.h>
#ifdef ESP32
#include <AsyncTCP.h>
#include <WiFi.h>
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#elif defined(TARGET_RP2040)
#include <WebServer.h>
#include <WiFi.h>
#endif
#include "ESPAsyncWebServer.h"
const char appWebPage[] PROGMEM = R"rawliteral(
<body>
<button id="button1" onclick="fetch('/button1');">Relay1</button>
<script>
const evtSource = new EventSource("/events");
button1 = document.getElementById("button1");
evtSource.addEventListener('state', (e) => {
const data = JSON.parse(e.data);
console.log('Event Source data: ', data);
if (data.button1) {
button1.style.backgroundColor = "green";
}
else {
button1.style.backgroundColor = "red";
}
});
</script>
</body>
)rawliteral";
AsyncWebServer server(80);
AsyncEventSource events("/events");
const uint32_t interval = 1000;
const int button1Pin = 4;
uint32_t lastSend = 0;
void prepareJson(String& buffer) {
buffer.reserve(512);
buffer.concat("{\"button1\":");
buffer.concat(digitalRead(button1Pin) == LOW);
buffer.concat(",\"1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij\":");
buffer.concat(random(0, 999999999));
buffer.concat("}");
}
void setup() {
Serial.begin(115200);
#if ARDUINO_USB_CDC_ON_BOOT
Serial.setTxTimeoutMs(0);
delay(100);
#else
while (!Serial)
yield();
#endif
randomSeed(micros());
pinMode(button1Pin, OUTPUT);
digitalWrite(button1Pin, HIGH);
WiFi.softAP("esp-captive");
server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
request->send(200, "text/html", appWebPage);
});
server.on("/button1", HTTP_GET, [](AsyncWebServerRequest* request) {
request->send(200, "text/plain", "OK");
digitalWrite(button1Pin, digitalRead(button1Pin) == LOW ? HIGH : LOW);
String buffer;
prepareJson(buffer);
ESP_LOGI("async_tcp", "Sending from handler...");
events.send(buffer.c_str(), "state", millis());
ESP_LOGI("async_tcp", "Sent from handler!");
});
events.onConnect([](AsyncEventSourceClient* client) {
String buffer;
prepareJson(buffer);
ESP_LOGI("async_tcp", "Sending from onConnect...");
client->send(buffer.c_str(), "state", millis(), 5000);
ESP_LOGI("async_tcp", "Sent from onConnect!");
});
server.addHandler(&events);
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
server.begin();
}
void loop() {
if (millis() - lastSend >= interval) {
String buffer;
prepareJson(buffer);
ESP_LOGI("loop", "Sending...");
events.send(buffer.c_str(), "state", millis());
ESP_LOGI("loop", "Sent!");
lastSend = millis();
}
}