#include <Arduino.h>
#include <WiFi.h>
#include <WebServer.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <LiquidCrystal_I2C.h>
#include <HX711.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <ESP32Servo.h>
#include <Keypad.h>
// ============ WiFi ============
const char* WIFI_SSID = "Wokwi-GUEST";
const char* WIFI_PASSWORD = "";
const char* API_KEY = "b453666eb3d04c1fa7c608375d88f7fc";
// ============ Об'єкти ============
WebServer web(80);
LiquidCrystal_I2C lcd(0x27, 16, 2);
// --- ваги HX711 ---
const int LOADCELL_DOUT_PIN = 16;
const int LOADCELL_SCK_PIN = 4;
HX711 scale;
// --- температура ---
const int oneWireBus = 2;
OneWire oneWire(oneWireBus);
DallasTemperature sensors(&oneWire);
// --- серво ---
Servo servo;
const int SERVO_PIN = 18;
const int BUZZER = 13;
// --- клавіатура ---
const uint8_t ROWS = 4;
const uint8_t COLS = 3;
char keys[ROWS][COLS] = {
{'1','2','3'},
{'4','5','6'},
{'7','8','9'},
{'*','0','#'}
};
uint8_t colPins[COLS] = {12, 14, 27};
uint8_t rowPins[ROWS] = {26, 25, 33, 32};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
// ============ Дані ============
String correct_password = "1369";
String entered_password = "";
String access_state = "LOCKED";
float weightKg = 0.0;
float tempC = 0.0;
String aiAdvice = "Очікування...";
bool cooling_on = false;
// ============ Mini AI anomaly detector ============
float w1 = 0.5, w2 = -0.5, b = 0.0;
float sigmoid(float x){return 1.0/(1.0+exp(-x));}
float predict(float x1, float x2){return sigmoid(w1*x1+w2*x2+b);} // anomaly score
// ============ Функції ============
// LCD
void updateLCD() {
lcd.clear();
lcd.setCursor(0,0);
lcd.print("T:" + String(tempC,1) + "C W:" + String(weightKg,1));
lcd.setCursor(0,1);
lcd.print(access_state + (cooling_on ? " ❄" : ""));
lcd.setCursor(6,1);
lcd.print(entered_password);
}
// управління вентилятором
void controlCooling() {
if (tempC > 10.0) {
cooling_on = true;
servo.write(180);
} else {
cooling_on = false;
servo.write(90);
}
}
// пароль
void checkPassword() {
if (entered_password == correct_password) {
access_state = "OPEN";
} else {
access_state = "LOCKED";
}
entered_password = "";
}
// ============ Новий UI ============
void uiRoot() {
String page = R"html(
<html><head><meta charset='utf-8'>
<title>SmartCold Storage</title>
<style>
body {
margin: 0;
font-family: 'Segoe UI', sans-serif;
background: linear-gradient(135deg, #0a2d4d, #1b6ca8);
color: #fff;
text-align: center;
}
h2 {
margin-top: 25px;
font-size: 28px;
letter-spacing: 1px;
font-weight: 600;
}
.container {
width: 90%;
max-width: 420px;
margin: auto;
margin-top: 20px;
}
.card {
background: rgba(255,255,255,0.12);
padding: 16px;
margin: 12px 0;
border-radius: 14px;
backdrop-filter: blur(6px);
border: 1px solid rgba(255,255,255,0.25);
transition: 0.3s;
}
.card:hover {
transform: scale(1.03);
background: rgba(255,255,255,0.18);
}
.value {
font-size: 24px;
font-weight: bold;
display: block;
margin-top: 6px;
color: #d7f3ff;
}
footer {
margin-top: 30px;
opacity: 0.7;
font-size: 12px;
}
</style>
<script>
async function refresh(){
const j = await fetch('/metrics').then(r=>r.json());
document.getElementById('temp').innerText = j.temp + ' °C';
document.getElementById('weight').innerText = j.weight + ' кг';
document.getElementById('state').innerText = j.state;
document.getElementById('cool').innerText = j.cool;
document.getElementById('ai').innerText = j.ai;
}
setInterval(refresh, 2000);
</script>
</head><body>
<h2>❄ SmartCold Storage Panel</h2>
<div class='container'>
<div class='card'>
Температура:
<span id='temp' class='value'>--</span>
</div>
<div class='card'>
Вага товару:
<span id='weight' class='value'>--</span>
</div>
<div class='card'>
Стан доступу:
<span id='state' class='value'>--</span>
</div>
<div class='card'>
Охолодження:
<span id='cool' class='value'>--</span>
</div>
<div class='card'>
AI-порада:
<span id='ai' class='value'>--</span>
</div>
</div>
<footer>ESP32 Smart Storage © 2025</footer>
</body></html>
)html";
web.send(200,"text/html", page);
}
void uiMetrics() {
DynamicJsonDocument doc(512);
doc["temp"]=tempC;
doc["weight"]=weightKg;
doc["state"]=access_state;
doc["cool"]=cooling_on?"Увімкнено":"Вимкнено";
doc["ai"]=aiAdvice;
String out; serializeJson(doc,out);
web.send(200,"application/json",out);
}
// ============ Setup ============
void setup() {
Serial.begin(115200);
lcd.init(); lcd.backlight();
lcd.print("Smart Warehouse");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status()!=WL_CONNECTED) { delay(300); Serial.print("."); }
Serial.println(WiFi.localIP());
sensors.begin();
scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
servo.attach(SERVO_PIN,500,2400);
pinMode(BUZZER, OUTPUT);
web.on("/", uiRoot);
web.on("/metrics", uiMetrics);
web.begin();
}
// ============ Loop ============
void loop() {
web.handleClient();
sensors.requestTemperatures();
tempC = sensors.getTempCByIndex(0);
weightKg = scale.get_units(10)/1000.0;
controlCooling();
char key = keypad.getKey();
if (key != NO_KEY) {
if (key == '#') checkPassword();
else if (key == '*') entered_password="";
else entered_password += key;
}
float anomaly = predict(tempC/20.0, weightKg/10.0);
if (anomaly > 0.7) aiAdvice = "⚠️ Перевірте холодильник або товар!";
else aiAdvice = "✅ Все в нормі.";
updateLCD();
}