Доброго ПервоМая друзья.
Делаю мини проектик по мониторингу температуры и вот заткнулся в AJAX. Сделал компилируемые примеры проблемы. ESP32 Core и библиотеки самого свежего розлива. IDE 1.8.13 (никак не соберусь перейти на поновее).
Проблема выглядит так:
это при обновлении 500 мс, при обновлении 1000 - проблема незначительна, при 1500 почти отсутствует, при 2000 ее не дождался.
Тексты примеров
Synk
#include <WiFi.h>
#include <WebServer.h>
const char* ssid = "ssid";
const char* password = "pwd";
WebServer server(80);
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.print("Connecting");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nConnected. IP: " + WiFi.localIP().toString());
server.on("/", handleRoot);
server.on("/millis", handleMillis);
server.begin();
Serial.println("Server started");
}
void loop() {
server.handleClient();
delay(1);
}
void handleRoot() {
String html = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<title>ESP32 Millis Monitor</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { font-family: Arial, sans-serif; text-align: center; margin-top: 30px; background: #f5f5f5; }
.card {
background: white;
max-width: 500px;
margin: 0 auto;
padding: 20px;
border-radius: 15px;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
#millis {
font-size: 4em;
font-weight: bold;
color: #2c3e50;
font-family: monospace;
margin: 20px 0;
}
.control {
background: #ecf0f1;
padding: 15px;
border-radius: 10px;
margin-top: 20px;
}
input {
width: 80%;
padding: 8px;
font-size: 1em;
margin: 10px 0;
}
button {
background: #3498db;
color: white;
border: none;
padding: 8px 16px;
border-radius: 5px;
cursor: pointer;
font-size: 1em;
}
button:hover {
background: #2980b9;
}
.info {
font-size: 0.9em;
color: #7f8c8d;
}
.error {
color: red;
}
</style>
</head>
<body>
<div class="card">
<h2>ESP32 Uptime</h2>
<div id="millis">--- ms</div>
<div class="control">
<label>Интервал обновления: <span id="intervalValue">1000</span> мс</label><br>
<input type="range" id="intervalSlider" min="200" max="5000" step="100" value="1000">
<br>
<button id="applyBtn">Применить</button>
<div class="info">Частота запросов к серверу. Рекомендуемый минимум 500 мс.</div>
</div>
<div class="info" id="status">Статус: работает</div>
</div>
<script>
let updateInterval = null;
let currentIntervalMs = 1000;
async function fetchMillis() {
try {
const response = await fetch('/millis');
if (!response.ok) throw new Error('HTTP error');
const ms = await response.text();
document.getElementById('millis').innerHTML = ms + " ms";
document.getElementById('status').innerHTML = "✓ обновлено " + new Date().toLocaleTimeString();
document.getElementById('status').style.color = "green";
} catch(e) {
console.error("Ошибка:", e);
document.getElementById('status').innerHTML = "❌ ошибка запроса";
document.getElementById('status').style.color = "red";
}
}
function startUpdates(intervalMs) {
if (updateInterval) {
clearInterval(updateInterval);
}
updateInterval = setInterval(fetchMillis, intervalMs);
currentIntervalMs = intervalMs;
document.getElementById('intervalValue').innerText = intervalMs;
// не вызываем fetchMillis сразу, чтобы не дублировать (хотя можно)
fetchMillis(); // сразу покажем текущее значение
}
// Обработчик ползунка
const slider = document.getElementById('intervalSlider');
const applyBtn = document.getElementById('applyBtn');
let pendingInterval = slider.value;
slider.addEventListener('input', function() {
pendingInterval = parseInt(this.value);
document.getElementById('intervalValue').innerText = pendingInterval;
});
applyBtn.addEventListener('click', function() {
const newInterval = pendingInterval;
if (newInterval >= 200) {
startUpdates(newInterval);
document.getElementById('status').innerHTML = "интервал изменён на " + newInterval + " мс";
} else {
alert("Интервал не может быть меньше 200 мс");
}
});
// Запуск по умолчанию
startUpdates(1000);
</script>
</body>
</html>
)rawliteral";
server.send(200, "text/html", html);
}
void handleMillis() {
server.sendHeader("Connection", "close");
server.send(200, "text/plain", String(millis()));
}
Asynk
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
const char* ssid = "ssid";
const char* password = "pwd";
AsyncWebServer server(80);
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.print("Connecting");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nConnected. IP: " + WiFi.localIP().toString());
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
String html = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<title>ESP32 Millis Monitor (Async)</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { font-family: Arial, sans-serif; text-align: center; margin-top: 50px; background: #f0f2f5; }
#millis { font-size: 4em; font-weight: bold; color: #2c3e50; font-family: monospace; margin: 20px 0; }
.card { background: white; max-width: 500px; margin: 0 auto; padding: 20px; border-radius: 15px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
.control { background: #ecf0f1; padding: 15px; border-radius: 10px; margin-top: 20px; }
input { width: 80%; padding: 8px; margin: 10px 0; }
button { background: #3498db; color: white; border: none; padding: 8px 16px; border-radius: 5px; cursor: pointer; }
.info { color: #7f8c8d; margin-top: 15px; }
</style>
</head>
<body>
<div class="card">
<h2>ESP32 Async Web Server</h2>
<div id="millis">--- ms</div>
<div class="control">
<label>Интервал обновления: <span id="intervalValue">1000</span> мс</label><br>
<input type="range" id="intervalSlider" min="100" max="5000" step="100" value="1000">
<br>
<button id="applyBtn">Применить</button>
</div>
<div class="info" id="status">Статус: работает (асинхронно)</div>
</div>
<script>
let currentInterval = null;
async function fetchMillis() {
try {
const resp = await fetch('/millis');
const val = await resp.text();
document.getElementById('millis').innerHTML = val + " ms";
document.getElementById('status').innerHTML = "✓ обновлено " + new Date().toLocaleTimeString();
document.getElementById('status').style.color = "green";
} catch(e) {
document.getElementById('status').innerHTML = "❌ ошибка запроса";
document.getElementById('status').style.color = "red";
}
}
function startUpdates(intervalMs) {
if (currentInterval) clearInterval(currentInterval);
currentInterval = setInterval(fetchMillis, intervalMs);
document.getElementById('intervalValue').innerText = intervalMs;
fetchMillis();
}
const slider = document.getElementById('intervalSlider');
const applyBtn = document.getElementById('applyBtn');
let pendingInterval = slider.value;
slider.addEventListener('input', function() {
pendingInterval = parseInt(this.value);
document.getElementById('intervalValue').innerText = pendingInterval;
});
applyBtn.addEventListener('click', function() {
startUpdates(pendingInterval);
});
startUpdates(1000);
</script>
</body>
</html>
)rawliteral";
request->send(200, "text/html", html);
});
server.on("/millis", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send(200, "text/plain", String(millis()));
});
server.begin();
Serial.println("Async HTTP server started");
}
void loop() {
delay(1);
}
может быть я хочу невозможного - при высокой частоте обновлений стек просто засирается запросами и виснет ? или делаю что то неправильно ?
Аяксу надо связаться с сервером, то-сё, при плохо работающей сети, это может занять целые секунды, а Вы говорите, что в 500мс затыкается.
Вам точно надо мониторить температуру так часто (раз в полсекунды)? Звучит как бред – она так быстро не меняется!
Вы говорите, что если посылать раз в две секунды, проблемы нет. Ну, так сделайте себе 30-кратный запас, посылайте раз в минуту и забудьте о перхоти.
Неужели правда так часто надо? Я вот смотрю на температуру раз в 10 минут! Для чего Вам так часто?
Можно, а зачем
Ну да, и раз в 5 минут достаточно, но хотелось понять предел возможностей. Температура может бегать и реже, а вот часы хотелось каждую секунду обновлять. При настройке железа хочется температуру почаще видеть, потом можно опять уходить на - пореже…
Вцелом главное, что код не подвергнут обструкции, грубых ошибок там нет, и это хорошо.
Спасибо за отклики.
ua6em
01.Май.2026 13:43:14
5
ASYNC
ядро 2.0.14 библиотека 3.6.0 - работает
ядро 3.2.0 библиотека 3.6.0 -
16:36:49.190 -> ���ESP-ROM:esp32s3-20210327
16:36:49.190 -> Build:Mar 27 2021
16:36:49.190 -> rst:0xc (RTC_SW_CPU_RST),boot:0x8 (SPI_FAST_FLASH_BOOT)
16:36:49.190 -> Saved PC:0x4037aaaa
16:36:49.190 -> SPIWP:0xee
16:36:49.228 -> mode:DIO, clock div:1
16:36:49.228 -> load:0x3fce2820,len:0x118c
16:36:49.228 -> load:0x403c8700,len:0x4
16:36:49.228 -> load:0x403c8704,len:0xc20
16:36:49.228 -> load:0x403cb700,len:0x30e0
16:36:49.228 -> entry 0x403c88b8
16:36:49.449 -> Connecting...........
16:36:50.592 -> Connected. IP: 192.168.хх
16:36:50.592 ->
16:36:50.626 -> assert failed: tcp_alloc /IDF/components/lwip/lwip/src/core/tcp.c:1854 (Required to lock TCPIP core functionality!)
16:36:50.626 ->
16:36:50.626 ->
16:36:50.626 -> Backtrace: 0x403766d9:0x3fcaa760 0x4037da05:0x3fcaa780 0x403844be:0x3fcaa7a0 0x420215f7:0x3fcaa8e0 0x4202175d:0x3fcaa900 0x42005b41:0x3fcaa920 0x4200a765:0x3fcaa970 0x42002d6a:0x3fcaa990 0x4200db17:0x3fcaaa00 0x4037e6b2:0x3fcaaa20
16:3 тут ребутится
ядро 3.2.0 библиотека 3.11.0 - не компилируется
In file included from C:\Temp\.arduinoIDE-unsaved202641-12336-1odgzyf.z36b\sketch_may1b\sketch_may1b.ino:3:
c:\ARDUINO\libraries\ESP_Async_WebServer\src/ESPAsyncWebServer.h: In member function 'tcp_state AsyncWebServer::state() const':
c:\ARDUINO\libraries\ESP_Async_WebServer\src/ESPAsyncWebServer.h:1689:49: error: passing 'const AsyncServer' as 'this' argument discards qualifiers [-fpermissive]
1689 | return static_cast<tcp_state>(_server.status());
| ~~~~~~~~~~~~~~^~
In file included from C:\Temp\.arduinoIDE-unsaved202641-12336-1odgzyf.z36b\sketch_may1b\sketch_may1b.ino:2:
c:\ARDUINO\libraries\AsyncTCP\src/AsyncTCP.h:198:13: note: in call to 'uint8_t AsyncServer::status()'
198 | uint8_t status();
| ^~~~~~
Я ставил и сервер и тсп асинк свежие. Могу выложить сюда зипы
ua6em
01.Май.2026 13:58:46
7
я на ESP32S3 проверял, видимо с этим проблема связана
Обновил библиотеку AsyncTcp - на интервале 500мс работает нормально
Но это S3 !!!
Я заказал штук несколько , предвкушаю
ua6em
01.Май.2026 15:15:36
9
накладные расходы где-то 100 миллисекунд, но роутер очень старый, лет 15 ему
BABOS
01.Май.2026 15:26:57
10
ua6em:
ядро 3.2.0
оно не нужно вроде никому, пусть те кто ядра пишет, сам и библиотеки правит!)))
и главное что бы проект собирался на ядре <3, а не компилировался на новом)))
а как начнут править, то и желание писать ядра пропадет, ну или по крайней мере будут делать это аккуратно
v258
01.Май.2026 15:28:53
11
С какой радости? У меня уже давно 3.хх стоит, мне что, назад откатываться?
BABOS
01.Май.2026 15:30:41
12
v258 естественно, потому что в сети куча библиотек которые не будут править под ядро esp32 более 3
вот зачем вам выше ядро ? ради единичной плюшки переходить на новое ядро, и каждый проект править это ад же…
в esp8266 только самое последнее ядро стоит, потому что компилируются старые проекты, но там и так кроме ядра вечные проблемы есть)))
а в esp32 вообще не понятно что внесли… такое что лучше бы они сами откатились назад, и заново писали свои улучшения)))
BABOS
01.Май.2026 15:41:11
13
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
const char* ssid = "ssid";
const char* password = "pwd";
AsyncWebServer server(80);
// Переменные для отслеживания состояния
unsigned long lastRequestTime = 0;
bool requestInProgress = false;
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.print("Connecting");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nConnected. IP: " + WiFi.localIP().toString());
// Настройка CORS для предотвращения проблем с браузером
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Methods", "GET");
DefaultHeaders::Instance().addHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
String html = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<title>ESP32 Millis Monitor (Async)</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { font-family: Arial, sans-serif; text-align: center; margin-top: 50px; background: #f0f2f5; }
#millis { font-size: 4em; font-weight: bold; color: #2c3e50; font-family: monospace; margin: 20px 0; }
.card { background: white; max-width: 500px; margin: 0 auto; padding: 20px; border-radius: 15px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
.control { background: #ecf0f1; padding: 15px; border-radius: 10px; margin-top: 20px; }
input { width: 80%; padding: 8px; margin: 10px 0; }
button { background: #3498db; color: white; border: none; padding: 8px 16px; border-radius: 5px; cursor: pointer; }
.info { color: #7f8c8d; margin-top: 15px; }
.warning { color: #e67e22; font-size: 0.8em; margin-top: 5px; }
</style>
</head>
<body>
<div class="card">
<h2>ESP32 Async Web Server</h2>
<div id="millis">--- ms</div>
<div class="control">
<label>Интервал обновления: <span id="intervalValue">1000</span> мс</label><br>
<input type="range" id="intervalSlider" min="200" max="5000" step="100" value="1000">
<br>
<button id="applyBtn">Применить</button>
<div class="warning">Минимальный интервал: 200 мс для стабильной работы</div>
</div>
<div class="info" id="status">Статус: работает (асинхронно)</div>
<div class="info" id="debug" style="font-size: 0.7em; color: #95a5a6;"></div>
</div>
<script>
let currentInterval = null;
let isRequestPending = false; // Флаг для предотвращения накопления запросов
let failedAttempts = 0;
async function fetchMillis() {
// Пропускаем запрос, если предыдущий ещё не завершён
if (isRequestPending) {
document.getElementById('debug').innerHTML = "Пропущен (предыдущий запрос выполняется)";
return;
}
isRequestPending = true;
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 2000); // Таймаут 2 секунды
const resp = await fetch('/millis', {
signal: controller.signal,
// Не кешировать запросы
cache: 'no-cache'
});
clearTimeout(timeoutId);
const val = await resp.text();
document.getElementById('millis').innerHTML = val + " ms";
document.getElementById('status').innerHTML = "✓ обновлено " + new Date().toLocaleTimeString();
document.getElementById('status').style.color = "green";
document.getElementById('debug').innerHTML = "OK";
failedAttempts = 0;
} catch(e) {
failedAttempts++;
document.getElementById('status').innerHTML = "❌ ошибка запроса (попыток: " + failedAttempts + ")";
document.getElementById('status').style.color = "red";
document.getElementById('debug').innerHTML = "Ошибка: " + e.message;
// Если много ошибок, увеличиваем интервал
if (failedAttempts > 5) {
startUpdates(Math.min(5000, parseInt(document.getElementById('intervalSlider').value) * 2));
document.getElementById('status').innerHTML += " (интервал увеличен)";
}
} finally {
isRequestPending = false;
}
}
function startUpdates(intervalMs) {
if (currentInterval) clearInterval(currentInterval);
currentInterval = setInterval(fetchMillis, intervalMs);
document.getElementById('intervalValue').innerText = intervalMs;
document.getElementById('intervalSlider').value = intervalMs;
isRequestPending = false; // Сбрасываем флаг при смене интервала
fetchMillis();
}
const slider = document.getElementById('intervalSlider');
const applyBtn = document.getElementById('applyBtn');
slider.addEventListener('input', function() {
document.getElementById('intervalValue').innerText = parseInt(this.value);
});
applyBtn.addEventListener('click', function() {
const interval = parseInt(slider.value);
if (interval >= 200) {
startUpdates(interval);
} else {
alert("Минимальный интервал 200 мс");
}
});
startUpdates(1000);
</script>
</body>
</html>
)rawliteral";
request->send(200, "text/html", html);
});
server.on("/millis", HTTP_GET, [](AsyncWebServerRequest *request) {
// Проверка частоты запросов на серверной стороне
unsigned long currentTime = millis();
if (currentTime - lastRequestTime < 50) { // Минимум 50 мс между запросами
request->send(429, "text/plain", "Too Many Requests");
return;
}
lastRequestTime = currentTime;
// Отправляем ответ без лишних заголовков
AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", String(currentTime));
response->addHeader("Cache-Control", "no-cache, no-store, must-revalidate");
response->addHeader("Connection", "close");
request->send(response);
});
// Обработчик для ограничения количества одновременных подключений
server.onNotFound([](AsyncWebServerRequest *request) {
request->send(404, "text/plain", "Not found");
});
server.begin();
Serial.println("Async HTTP server started");
Serial.println("Free heap: " + String(ESP.getFreeHeap()));
}
void loop() {
delay(1);
// Мониторинг свободной памяти каждые 10 секунд
static unsigned long lastMemCheck = 0;
if (millis() - lastMemCheck > 10000) {
Serial.println("Free heap: " + String(ESP.getFreeHeap()));
lastMemCheck = millis();
}
}
может поможет
ua6em
01.Май.2026 15:55:09
14
под ядром > 3
8:53:24.942 -> Free heap: 245072
18:53:34.970 -> Free heap: 245072
18:53:44.944 -> Free heap: 245024
18:53:54.988 -> Free heap: 245072
18:54:04.987 -> Free heap: 245072
18:54:14.969 -> Free heap: 245116
18:54:24.971 -> Free heap: 245068
BABOS
01.Май.2026 16:02:45
15
https://github.com/ESP32Async/ESPAsyncWebServer
хотя может кто и будет поддерживать новое ядро)))
но я подожду пока сообщество все сделает, а потом может и перейду)))
p.s. видел что было написано что это библиотека идет на версии ядра меньше 3, а сейчас этого нет на гитхабе…
хорошая библиотека которая много чего делала автоматически, что в новой версии не знаю
ua6em
01.Май.2026 16:03:54
16
в ядре → 3.x.x мне не нравится долгий коннект, точка - 100мс
entry 0x403c88b8
18:57:34.554 -> Connecting...........
18:57:35.645 -> Connected. IP: 192.168.xx
и для сравнения ядро 2.0.14
19:01:37.682 -> entry 0x403c98d0
19:01:37.691 -> Connecting.
19:01:37.790 -> Connected. IP: 192.168.x.x
Помните задачу про приготовление чая?
v258
01.Май.2026 16:19:08
18
Чтобы было
Пиши сразу правильно, и все будет нормально
Потому что его не развивают. Да и сама 8266 - устаревшее дерьмо мамонта ))
v258
01.Май.2026 16:20:39
19
Открываешь сайт с документацией и смотришь, что они там внесли и чего это касается. И думаешь, коснется ли это тебя. А если коснулось - там же и рекомендации по исправлению даются