// Controller firmware для ESP32-ETH01 V1.4 // Кнопка 1 → IO4, Кнопка 2 → IO2 // Веб-конфигуратор + статический IP + OTA #include #include #include #include #include #include #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 FW_VERSION "1.2" #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"( Barrier Controller

Barrier Controller

IP: )"; html += currentIP(); html += "  ·  v" + String(FW_VERSION) + "
"; 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()); }); server.on("/cmd", HTTP_POST, []() { 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); }