// Barrier Node firmware для ESP32-ETH01 V1.4 // Управление реле → IO4 // HTTP API: GET /open (заголовок X-Token) // Веб-морда + OTA обновление #include #include #include #include #include #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, LOW); // LOW = включить (NC размыкается) delay(500); digitalWrite(RELAY_PIN, HIGH); // HIGH = выключить } 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"( Barrier Node

)"; html += cfg_name; html += R"(

IP: )"; html += currentIP(); html += "
"; if (msg.startsWith("❌")) html += "
" + msg + "
"; else if (msg.length() > 0) html += "
" + msg + "
"; // Управление html += R"(

Управление

)"; // Настройки html += R"(

Устройство


Сеть WiFi


IP этого устройства

)"; // OTA html += R"(

Обновление прошивки

)"; return html; } void setupRoutes() { server.on("/", HTTP_GET, []() { server.send(200, "text/html", buildPage()); }); // Открытие через браузер (POST) server.on("/open", HTTP_POST, []() { triggerRelay(); server.send(200, "text/html", buildPage("✅ Команда выполнена")); }); // Открытие от контроллера (GET + токен) 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); digitalWrite(RELAY_PIN, HIGH); // HIGH = реле выключено (инвертированная логика) pinMode(RELAY_PIN, OUTPUT); 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); }