feat: barrier_controller и barrier_node прошивки v1.0

This commit is contained in:
Aleksei
2026-06-15 10:53:00 +00:00
parent 135b946bf2
commit 6192da8ec5
3 changed files with 654 additions and 2 deletions
+27 -2
View File
@@ -1,3 +1,28 @@
# barrier-controller # Barrier Controller — ESP32-ETH01 V1.4
ESP32-ETH01 barrier controller firmware Прошивки для управления шлагбаумами DoorHan через ESP32-ETH01.
## Устройства
### barrier_controller — пульт управления
- Кнопка 1 → IO4 (шлагбаум 1)
- Кнопка 2 → IO2 (шлагбаум 2)
- Веб-морда с настройками и OTA
- ETH (основной) + WiFi (резервный)
- AP режим при первом запуске: `Barrier-Setup` / `barrier123`
### barrier_node — узел на шлагбауме
- Реле → IO4 (импульс 500мс)
- HTTP API: `GET /open` с заголовком `X-Token`
- Веб-морда с настройками и OTA
- AP режим при первом запуске: `Barrier-Node-Setup` / `barrier123`
## Сеть
- Контроллер: `192.168.15.20` (по умолчанию)
- Узел 1: `192.168.15.10`
- Узел 2: `192.168.15.11`
## Прошивка
Arduino IDE, Board: ESP32 Dev Module, esp32 by Espressif 3.3.10+
При прошивке: IO0 на GND, отключить/подключить питание при `Connecting...`
+330
View File
@@ -0,0 +1,330 @@
// Controller firmware для ESP32-ETH01 V1.4
// Кнопка 1 → IO4, Кнопка 2 → IO2
// Веб-конфигуратор + статический IP + OTA
#include <WiFi.h>
#include <HTTPClient.h>
#include <ETH.h>
#include <Preferences.h>
#include <WebServer.h>
#include <Update.h>
#define BTN1_PIN 4
#define BTN2_PIN 2
#define ETH_POWER_PIN 16
#define ETH_MDC_PIN 23
#define ETH_MDIO_PIN 18
#define ETH_CLK_MODE ETH_CLOCK_GPIO17_OUT
#define AP_SSID "Barrier-Setup"
#define AP_PASS "barrier123"
Preferences prefs;
WebServer server(80);
bool ethConnected = false;
bool wifiConnected = false;
bool staticIpApplied = false;
String cfg_ssid = "";
String cfg_pass = "";
String cfg_ip1 = "192.168.15.10";
String cfg_ip2 = "192.168.15.11";
String cfg_token = "barrier_token_2026";
String cfg_self_ip = ""; // пусто = DHCP
String cfg_gateway = "";
String cfg_subnet = "255.255.255.0";
unsigned long lastPress1 = 0;
unsigned long lastPress2 = 0;
const unsigned long DEBOUNCE = 1500;
void WiFiEvent(WiFiEvent_t event) {
switch (event) {
case ARDUINO_EVENT_ETH_CONNECTED:
Serial.println("ETH: кабель подключён");
break;
case ARDUINO_EVENT_ETH_GOT_IP:
if (cfg_self_ip.length() > 0 && !staticIpApplied) {
staticIpApplied = true;
IPAddress ip, gw, sn, dns;
ip.fromString(cfg_self_ip);
gw.fromString(cfg_gateway.length() > 0 ? cfg_gateway : "192.168.1.1");
sn.fromString(cfg_subnet);
dns.fromString(cfg_gateway.length() > 0 ? cfg_gateway : "8.8.8.8");
ETH.config(ip, gw, sn, dns);
Serial.println("ETH статический IP: " + cfg_self_ip);
} else if (cfg_self_ip.length() == 0) {
Serial.print("ETH IP (DHCP): ");
Serial.println(ETH.localIP());
}
ethConnected = true;
break;
case ARDUINO_EVENT_ETH_DISCONNECTED:
Serial.println("ETH: отключён");
ethConnected = false;
break;
default: break;
}
}
void loadConfig() {
prefs.begin("cfg", true);
cfg_ssid = prefs.getString("ssid", "");
cfg_pass = prefs.getString("pass", "");
cfg_ip1 = prefs.getString("ip1", "192.168.15.10");
cfg_ip2 = prefs.getString("ip2", "192.168.15.11");
cfg_token = prefs.getString("token", "barrier_token_2026");
cfg_self_ip = prefs.getString("self_ip", "");
cfg_gateway = prefs.getString("gateway", "");
cfg_subnet = prefs.getString("subnet", "255.255.255.0");
prefs.end();
}
void saveConfig() {
prefs.begin("cfg", false);
prefs.putString("ssid", cfg_ssid);
prefs.putString("pass", cfg_pass);
prefs.putString("ip1", cfg_ip1);
prefs.putString("ip2", cfg_ip2);
prefs.putString("token", cfg_token);
prefs.putString("self_ip", cfg_self_ip);
prefs.putString("gateway", cfg_gateway);
prefs.putString("subnet", cfg_subnet);
prefs.end();
}
void sendCommand(String ip) {
if (!ethConnected && !wifiConnected) {
Serial.println("Нет сети!");
return;
}
HTTPClient http;
http.begin("http://" + ip + "/open");
http.addHeader("X-Token", cfg_token);
int code = http.GET();
Serial.println("Ответ от " + ip + ": " + String(code));
http.end();
}
String currentIP() {
if (ethConnected) return ETH.localIP().toString();
if (wifiConnected) return WiFi.localIP().toString();
return "192.168.4.1 (AP)";
}
String buildPage(String msg = "") {
String html = R"(<!DOCTYPE html><html><head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Barrier Controller</title>
<style>
body{font-family:sans-serif;max-width:500px;margin:20px auto;padding:0 16px;background:#f0f2f5}
h2{color:#222;margin-bottom:2px}
.sub{font-size:12px;color:#888;margin-bottom:16px}
.card{background:#fff;border-radius:12px;padding:16px;margin-bottom:14px;box-shadow:0 1px 3px rgba(0,0,0,.08)}
h3{margin:0 0 12px;font-size:14px;color:#555;text-transform:uppercase;letter-spacing:.5px}
label{display:block;margin-top:10px;font-size:13px;color:#555}
input[type=text],input[type=password]{width:100%;box-sizing:border-box;padding:9px;border:1px solid #ddd;border-radius:7px;font-size:14px;margin-top:3px}
input[type=text]:focus,input[type=password]:focus{outline:none;border-color:#2196F3}
.hint{font-size:11px;color:#aaa;margin-top:3px}
.btn{display:inline-block;padding:10px 0;border:none;border-radius:8px;cursor:pointer;font-size:14px;font-weight:500}
.btn-primary{background:#2196F3;color:#fff;width:100%;margin-top:12px}
.btn-open{background:#4CAF50;color:#fff;width:49%}
.btn-ota{background:#ff5722;color:#fff;width:100%;margin-top:8px}
.row{display:flex;gap:2%}
.msg{background:#e8f5e9;color:#2e7d32;padding:10px 14px;border-radius:8px;margin-bottom:12px;font-size:14px}
.err{background:#ffebee;color:#c62828;padding:10px 14px;border-radius:8px;margin-bottom:12px;font-size:14px}
.sep{border:none;border-top:1px solid #f0f0f0;margin:14px 0}
</style></head><body>
<h2>Barrier Controller</h2>
<div class='sub'>IP: )";
html += currentIP();
html += "</div>";
if (msg.startsWith(""))
html += "<div class='err'>" + msg + "</div>";
else if (msg.length() > 0)
html += "<div class='msg'>" + msg + "</div>";
// Управление
html += R"(<div class='card'>
<h3>Управление</h3>
<div class='row'>
<form action='/cmd' method='get' style='width:49%'>
<input type='hidden' name='b' value='1'>
<button class='btn btn-open' style='width:100%'> Шлагбаум 1</button>
</form>
<form action='/cmd' method='get' style='width:49%'>
<input type='hidden' name='b' value='2'>
<button class='btn btn-open' style='width:100%'> Шлагбаум 2</button>
</form>
</div>
</div>)";
// Настройки шлагбаумов
html += R"(<div class='card'>
<form action='/save' method='post'>
<h3>Шлагбаумы</h3>
<label>IP шлагбаума 1</label>
<input type='text' name='ip1' value=')" + cfg_ip1 + R"('>
<label>IP шлагбаума 2</label>
<input type='text' name='ip2' value=')" + cfg_ip2 + R"('>
<label>API токен</label>
<input type='text' name='token' value=')" + cfg_token + R"('>
<hr class='sep'>
<h3>Сеть WiFi</h3>
<label>SSID</label>
<input type='text' name='ssid' value=')" + cfg_ssid + R"('>
<label>Пароль</label>
<input type='password' name='pass' placeholder='оставь пустым не менять'>
<hr class='sep'>
<h3>IP этого устройства</h3>
<label>Статический IP <span class='hint'>(пусто = DHCP)</span></label>
<input type='text' name='self_ip' value=')" + cfg_self_ip + R"(' placeholder='например 192.168.1.50'>
<label>Шлюз (gateway)</label>
<input type='text' name='gateway' value=')" + cfg_gateway + R"(' placeholder='например 192.168.1.1'>
<label>Маска подсети</label>
<input type='text' name='subnet' value=')" + cfg_subnet + R"('>
<button class='btn btn-primary' type='submit'>💾 Сохранить и перезагрузить</button>
</form>
</div>)";
// OTA
html += R"(<div class='card'>
<h3>Обновление прошивки</h3>
<form action='/update' method='post' enctype='multipart/form-data'>
<input type='file' name='firmware' accept='.bin' style='margin-bottom:8px;font-size:13px'>
<button class='btn btn-ota' type='submit'> Загрузить .bin файл</button>
</form>
</div>
</body></html>)";
return html;
}
void setupRoutes() {
server.on("/", HTTP_GET, []() {
server.send(200, "text/html", buildPage());
});
server.on("/cmd", HTTP_GET, []() {
String b = server.arg("b");
if (b == "1") { sendCommand(cfg_ip1); server.send(200, "text/html", buildPage("✅ Команда → шлагбаум 1")); }
else if (b == "2") { sendCommand(cfg_ip2); server.send(200, "text/html", buildPage("✅ Команда → шлагбаум 2")); }
else server.send(400, "text/plain", "bad request");
});
server.on("/save", HTTP_POST, []() {
cfg_ip1 = server.arg("ip1");
cfg_ip2 = server.arg("ip2");
cfg_token = server.arg("token");
cfg_ssid = server.arg("ssid");
cfg_self_ip = server.arg("self_ip");
cfg_gateway = server.arg("gateway");
cfg_subnet = server.arg("subnet");
String np = server.arg("pass");
if (np.length() > 0) cfg_pass = np;
saveConfig();
server.send(200, "text/html", buildPage("✅ Сохранено. Перезагрузка..."));
delay(1500);
ESP.restart();
});
server.on("/update", HTTP_POST,
[]() {
server.send(200, "text/html",
Update.hasError()
? buildPage("❌ Ошибка обновления")
: buildPage("✅ Обновление OK! Перезагрузка..."));
delay(1000);
ESP.restart();
},
[]() {
HTTPUpload& upload = server.upload();
if (upload.status == UPLOAD_FILE_START) {
Serial.printf("OTA: %s\n", upload.filename.c_str());
if (!Update.begin(UPDATE_SIZE_UNKNOWN)) Update.printError(Serial);
} else if (upload.status == UPLOAD_FILE_WRITE) {
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) Update.printError(Serial);
} else if (upload.status == UPLOAD_FILE_END) {
if (Update.end(true)) Serial.printf("OTA OK: %u байт\n", upload.totalSize);
else Update.printError(Serial);
}
}
);
// HTTP API (для вызова из других систем)
server.on("/open", HTTP_GET, []() {
if (server.header("X-Token") != cfg_token) {
server.send(401, "text/plain", "unauthorized");
return;
}
String b = server.arg("b");
if (b == "2") sendCommand(cfg_ip2);
else sendCommand(cfg_ip1);
server.send(200, "text/plain", "ok");
});
}
void setup() {
Serial.begin(115200);
pinMode(BTN1_PIN, INPUT_PULLUP);
pinMode(BTN2_PIN, INPUT_PULLUP);
loadConfig();
WiFi.onEvent(WiFiEvent);
ETH.begin(ETH_PHY_LAN8720, 1, ETH_MDC_PIN, ETH_MDIO_PIN, ETH_POWER_PIN, ETH_CLK_MODE);
Serial.println("Жду ETH 5 сек...");
delay(5000);
if (!ethConnected && cfg_ssid.length() > 0) {
Serial.println("ETH нет — пробую WiFi: " + cfg_ssid);
WiFi.begin(cfg_ssid.c_str(), cfg_pass.c_str());
int tries = 0;
while (WiFi.status() != WL_CONNECTED && tries < 20) {
delay(500); tries++;
}
if (WiFi.status() == WL_CONNECTED) {
wifiConnected = true;
Serial.println("WiFi IP: " + WiFi.localIP().toString());
}
}
if (!ethConnected && !wifiConnected) {
Serial.println("Нет сети — AP: " + String(AP_SSID));
WiFi.softAP(AP_SSID, AP_PASS);
Serial.println("AP IP: " + WiFi.softAPIP().toString());
}
const char* headers[] = {"X-Token"};
server.collectHeaders(headers, 1);
setupRoutes();
server.begin();
Serial.println("Веб-сервер запущен на " + currentIP());
}
void loop() {
server.handleClient();
unsigned long now = millis();
if (digitalRead(BTN1_PIN) == LOW && now - lastPress1 > DEBOUNCE) {
lastPress1 = now;
Serial.println("Кнопка 1");
sendCommand(cfg_ip1);
}
if (digitalRead(BTN2_PIN) == LOW && now - lastPress2 > DEBOUNCE) {
lastPress2 = now;
Serial.println("Кнопка 2");
sendCommand(cfg_ip2);
}
delay(10);
}
+297
View File
@@ -0,0 +1,297 @@
// Barrier Node firmware для ESP32-ETH01 V1.4
// Управление реле → IO4
// HTTP API: GET /open (заголовок X-Token)
// Веб-морда + OTA обновление
#include <WiFi.h>
#include <ETH.h>
#include <Preferences.h>
#include <WebServer.h>
#include <Update.h>
#define RELAY_PIN 4
#define ETH_POWER_PIN 16
#define ETH_MDC_PIN 23
#define ETH_MDIO_PIN 18
#define ETH_CLK_MODE ETH_CLOCK_GPIO17_OUT
#define AP_SSID "Barrier-Node-Setup"
#define AP_PASS "barrier123"
Preferences prefs;
WebServer server(80);
bool ethConnected = false;
bool wifiConnected = false;
bool staticIpApplied = false;
String cfg_ssid = "";
String cfg_pass = "";
String cfg_token = "barrier_token_2026";
String cfg_self_ip = "";
String cfg_gateway = "";
String cfg_subnet = "255.255.255.0";
String cfg_name = "Шлагбаум 1"; // имя для идентификации
void WiFiEvent(WiFiEvent_t event) {
switch (event) {
case ARDUINO_EVENT_ETH_CONNECTED:
Serial.println("ETH: кабель подключён");
break;
case ARDUINO_EVENT_ETH_GOT_IP:
if (cfg_self_ip.length() > 0 && !staticIpApplied) {
staticIpApplied = true;
IPAddress ip, gw, sn, dns;
ip.fromString(cfg_self_ip);
gw.fromString(cfg_gateway.length() > 0 ? cfg_gateway : "192.168.1.1");
sn.fromString(cfg_subnet);
dns.fromString(cfg_gateway.length() > 0 ? cfg_gateway : "8.8.8.8");
ETH.config(ip, gw, sn, dns);
Serial.println("ETH статический IP: " + cfg_self_ip);
} else if (cfg_self_ip.length() == 0) {
Serial.print("ETH IP (DHCP): ");
Serial.println(ETH.localIP());
}
ethConnected = true;
break;
case ARDUINO_EVENT_ETH_DISCONNECTED:
Serial.println("ETH: отключён");
ethConnected = false;
break;
default: break;
}
}
void loadConfig() {
prefs.begin("cfg", true);
cfg_ssid = prefs.getString("ssid", "");
cfg_pass = prefs.getString("pass", "");
cfg_token = prefs.getString("token", "barrier_token_2026");
cfg_self_ip = prefs.getString("self_ip", "");
cfg_gateway = prefs.getString("gateway", "");
cfg_subnet = prefs.getString("subnet", "255.255.255.0");
cfg_name = prefs.getString("name", "Шлагбаум 1");
prefs.end();
}
void saveConfig() {
prefs.begin("cfg", false);
prefs.putString("ssid", cfg_ssid);
prefs.putString("pass", cfg_pass);
prefs.putString("token", cfg_token);
prefs.putString("self_ip", cfg_self_ip);
prefs.putString("gateway", cfg_gateway);
prefs.putString("subnet", cfg_subnet);
prefs.putString("name", cfg_name);
prefs.end();
}
void triggerRelay() {
Serial.println("Реле: импульс 500мс");
digitalWrite(RELAY_PIN, HIGH);
delay(500);
digitalWrite(RELAY_PIN, LOW);
}
String currentIP() {
if (ethConnected) return ETH.localIP().toString();
if (wifiConnected) return WiFi.localIP().toString();
return "192.168.4.1 (AP)";
}
String buildPage(String msg = "") {
String html = R"(<!DOCTYPE html><html><head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Barrier Node</title>
<style>
body{font-family:sans-serif;max-width:500px;margin:20px auto;padding:0 16px;background:#f0f2f5}
h2{color:#222;margin-bottom:2px}
.sub{font-size:12px;color:#888;margin-bottom:16px}
.card{background:#fff;border-radius:12px;padding:16px;margin-bottom:14px;box-shadow:0 1px 3px rgba(0,0,0,.08)}
h3{margin:0 0 12px;font-size:14px;color:#555;text-transform:uppercase;letter-spacing:.5px}
label{display:block;margin-top:10px;font-size:13px;color:#555}
input[type=text],input[type=password]{width:100%;box-sizing:border-box;padding:9px;border:1px solid #ddd;border-radius:7px;font-size:14px;margin-top:3px}
.hint{font-size:11px;color:#aaa;margin-top:3px}
.btn{display:inline-block;padding:10px 0;border:none;border-radius:8px;cursor:pointer;font-size:14px;font-weight:500}
.btn-primary{background:#2196F3;color:#fff;width:100%;margin-top:12px}
.btn-open{background:#4CAF50;color:#fff;width:100%}
.btn-ota{background:#ff5722;color:#fff;width:100%;margin-top:8px}
.msg{background:#e8f5e9;color:#2e7d32;padding:10px 14px;border-radius:8px;margin-bottom:12px;font-size:14px}
.err{background:#ffebee;color:#c62828;padding:10px 14px;border-radius:8px;margin-bottom:12px;font-size:14px}
.sep{border:none;border-top:1px solid #f0f0f0;margin:14px 0}
</style></head><body>
<h2>)";
html += cfg_name;
html += R"(</h2>
<div class='sub'>IP: )";
html += currentIP();
html += "</div>";
if (msg.startsWith(""))
html += "<div class='err'>" + msg + "</div>";
else if (msg.length() > 0)
html += "<div class='msg'>" + msg + "</div>";
// Управление
html += R"(<div class='card'>
<h3>Управление</h3>
<form action='/open' method='get'>
<button class='btn btn-open'> Открыть шлагбаум</button>
</form>
</div>)";
// Настройки
html += R"(<div class='card'>
<form action='/save' method='post'>
<h3>Устройство</h3>
<label>Название</label>
<input type='text' name='name' value=')" + cfg_name + R"('>
<label>API токен</label>
<input type='text' name='token' value=')" + cfg_token + R"('>
<hr class='sep'>
<h3>Сеть WiFi</h3>
<label>SSID</label>
<input type='text' name='ssid' value=')" + cfg_ssid + R"('>
<label>Пароль</label>
<input type='password' name='pass' placeholder='оставь пустым не менять'>
<hr class='sep'>
<h3>IP этого устройства</h3>
<label>Статический IP <span class='hint'>(пусто = DHCP)</span></label>
<input type='text' name='self_ip' value=')" + cfg_self_ip + R"(' placeholder='например 192.168.15.10'>
<label>Шлюз (gateway)</label>
<input type='text' name='gateway' value=')" + cfg_gateway + R"(' placeholder='например 192.168.15.1'>
<label>Маска подсети</label>
<input type='text' name='subnet' value=')" + cfg_subnet + R"('>
<button class='btn btn-primary' type='submit'>💾 Сохранить и перезагрузить</button>
</form>
</div>)";
// OTA
html += R"(<div class='card'>
<h3>Обновление прошивки</h3>
<form action='/update' method='post' enctype='multipart/form-data'>
<input type='file' name='firmware' accept='.bin' style='margin-bottom:8px;font-size:13px'>
<button class='btn btn-ota' type='submit'> Загрузить .bin файл</button>
</form>
</div>
</body></html>)";
return html;
}
void setupRoutes() {
server.on("/", HTTP_GET, []() {
server.send(200, "text/html", buildPage());
});
// Открытие через браузер (без токена)
server.on("/open", HTTP_GET, []() {
String token = server.header("X-Token");
// Если запрос из браузера (нет токена) — разрешаем
// Если запрос от контроллера — проверяем токен
if (token.length() > 0 && token != cfg_token) {
server.send(401, "text/plain", "unauthorized");
return;
}
triggerRelay();
if (token.length() > 0) {
server.send(200, "text/plain", "ok");
} else {
server.send(200, "text/html", buildPage("✅ Команда выполнена"));
}
});
server.on("/save", HTTP_POST, []() {
cfg_name = server.arg("name");
cfg_token = server.arg("token");
cfg_ssid = server.arg("ssid");
cfg_self_ip = server.arg("self_ip");
cfg_gateway = server.arg("gateway");
cfg_subnet = server.arg("subnet");
String np = server.arg("pass");
if (np.length() > 0) cfg_pass = np;
saveConfig();
server.send(200, "text/html", buildPage("✅ Сохранено. Перезагрузка..."));
delay(1500);
ESP.restart();
});
server.on("/update", HTTP_POST,
[]() {
server.send(200, "text/html",
Update.hasError()
? buildPage("❌ Ошибка обновления")
: buildPage("✅ Обновление OK! Перезагрузка..."));
delay(1000);
ESP.restart();
},
[]() {
HTTPUpload& upload = server.upload();
if (upload.status == UPLOAD_FILE_START) {
Serial.printf("OTA: %s\n", upload.filename.c_str());
if (!Update.begin(UPDATE_SIZE_UNKNOWN)) Update.printError(Serial);
} else if (upload.status == UPLOAD_FILE_WRITE) {
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) Update.printError(Serial);
} else if (upload.status == UPLOAD_FILE_END) {
if (Update.end(true)) Serial.printf("OTA OK: %u байт\n", upload.totalSize);
else Update.printError(Serial);
}
}
);
// Статус
server.on("/status", HTTP_GET, []() {
String json = "{\"name\":\"" + cfg_name + "\",\"ip\":\"" + currentIP() + "\",\"eth\":" + (ethConnected ? "true" : "false") + "}";
server.send(200, "application/json", json);
});
}
void setup() {
Serial.begin(115200);
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, LOW); // реле выключено при старте
loadConfig();
WiFi.onEvent(WiFiEvent);
ETH.begin(ETH_PHY_LAN8720, 1, ETH_MDC_PIN, ETH_MDIO_PIN, ETH_POWER_PIN, ETH_CLK_MODE);
Serial.println("Жду ETH 5 сек...");
delay(5000);
if (!ethConnected && cfg_ssid.length() > 0) {
Serial.println("ETH нет — пробую WiFi: " + cfg_ssid);
WiFi.begin(cfg_ssid.c_str(), cfg_pass.c_str());
int tries = 0;
while (WiFi.status() != WL_CONNECTED && tries < 20) {
delay(500); tries++;
}
if (WiFi.status() == WL_CONNECTED) {
wifiConnected = true;
Serial.println("WiFi IP: " + WiFi.localIP().toString());
}
}
if (!ethConnected && !wifiConnected) {
Serial.println("Нет сети — AP: " + String(AP_SSID));
WiFi.softAP(AP_SSID, AP_PASS);
Serial.println("AP IP: " + WiFi.softAPIP().toString());
}
const char* headers[] = {"X-Token"};
server.collectHeaders(headers, 1);
setupRoutes();
server.begin();
Serial.println("Веб-сервер запущен на " + currentIP());
}
void loop() {
server.handleClient();
delay(10);
}