#include <WiFi.h>
#include <WebServer.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <DHT.h>
#include <time.h>
// ===== НАСТРОЙКИ =====
const char* ssid = "Wokwi-GUEST";
const char* password = "";
const int LED_PIN = 4;
const int DHT_PIN = 15;
#define DHTTYPE DHT22 // Или DHT11
const uint8_t LCD_ADDR = 0x27; // Адрес I2C LCD (если не работает, попробуйте 0x3F)
const uint8_t LCD_COLS = 16;
const uint8_t LCD_ROWS = 2;
// ===== ОБЪЕКТЫ =====
WebServer server(80);
LiquidCrystal_I2C lcd(LCD_ADDR, LCD_COLS, LCD_ROWS);
DHT dht(DHT_PIN, DHTTYPE);
bool ledState = false;
unsigned long lastUpdate = 0;
const unsigned long UPDATE_INTERVAL = 1000;
// ===== HTML СТРАНИЦА =====
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ESP32 Управление</title>
<style>
body { font-family: system-ui, sans-serif; text-align: center; margin: 40px 10px; background: #f4f4f9; }
h1 { color: #333; }
.btn { padding: 14px 28px; font-size: 18px; margin: 8px; border: none; border-radius: 8px; cursor: pointer; color: #fff; }
.on { background: #2ecc71; }
.off { background: #e74c3c; }
#status { margin-top: 20px; font-size: 18px; font-weight: bold; color: #555; }
</style>
</head>
<body>
<h1>💡 Управление светодиодом</h1>
<button class="btn on" onclick="fetch('/on')">ВКЛ</button>
<button class="btn off" onclick="fetch('/off')">ВЫКЛ</button>
<div id="status">Загрузка...</div>
<script>
function updateStatus() {
fetch('/status').then(r => r.text()).then(t => {
document.getElementById('status').innerText = t === '1' ? '✅ Светодиод: ВКЛ' : '⛔ Светодиод: ВЫКЛ';
});
}
setInterval(updateStatus, 1000);
updateStatus();
</script>
</body>
</html>
)rawliteral";
// ===== ОБРАБОТЧИКИ ЗАПРОСОВ =====
void handleRoot() { server.send(200, "text/html", index_html); }
void handleOn() { ledState = true; digitalWrite(LED_PIN, HIGH); server.send(200, "text/plain", "OK"); }
void handleOff() { ledState = false; digitalWrite(LED_PIN, LOW); server.send(200, "text/plain", "OK"); }
void handleStatus() { server.send(200, "text/plain", ledState ? "1" : "0"); }
// ===== НАСТРОЙКА =====
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
dht.begin();
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0); lcd.print("Connecting...");
// Подключение к Wi-Fi (DHCP автоматически даст IP из вашей подсети 192.168.20.x)
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\n✅ WiFi connected");
Serial.println("🌐 IP: " + WiFi.localIP().toString());
// Синхронизация времени для Новосибирска (UTC+7)
configTime(7 * 3600, 0, "pool.ntp.org", "time.nist.gov");
// Маршруты веб-сервера
server.on("/", handleRoot);
server.on("/on", handleOn);
server.on("/off", handleOff);
server.on("/status", handleStatus);
server.begin();
Serial.println("🚀 HTTP server started");
// Вывод IP на LCD для удобства
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(WiFi.localIP().toString());
lcd.setCursor(0, 1);
lcd.print("Wait sensors...");
}
// ===== ОСНОВНОЙ ЦИКЛ =====
void loop() {
server.handleClient(); // Обработка веб-запросов
if (millis() - lastUpdate > UPDATE_INTERVAL) {
lastUpdate = millis();
// 1. Вывод времени (НСК)
struct tm timeinfo;
lcd.setCursor(0, 0);
if (getLocalTime(&timeinfo)) {
char buf[9];
strftime(buf, sizeof(buf), "%H:%M:%S", &timeinfo);
lcd.print(buf);
lcd.print(" "); // Очистка хвоста строки
} else {
lcd.print("Time sync... ");
}
// 2. Вывод температуры и влажности
float h = dht.readHumidity();
float t = dht.readTemperature();
lcd.setCursor(0, 1);
if (isnan(h) || isnan(t)) {
lcd.print("Sensor error ");
} else {
char buf[17];
snprintf(buf, sizeof(buf), "T:%4.1fC H:%2.0f%% ", t, h);
lcd.print(buf);
}
}
}