#include <Arduino.h>
#include "HX711.h"
#include <LiquidCrystal_I2C.h>
#include <WiFi.h>
#include <WebServer.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <OneWire.h>
#include <DallasTemperature.h>
// ====== WiFi ======
const char* WIFI_SSID = "Wokwi-GUEST";
const char* WIFI_PASSWORD = "";
const char* API_KEY = "b453666eb3d04c1fa7c608375d88f7fc";
// ====== Веб-сервер ======
WebServer web(80);
// ====== Ваги/платформа HX711 ======
const int LOADCELL_DOUT_PIN = 16;
const int LOADCELL_SCK_PIN = 4;
HX711 scale;
// ====== LCD ======
LiquidCrystal_I2C lcd(0x27, 16, 2);
// ====== Датчик температури ======
const int oneWireBus = 2;
OneWire oneWire(oneWireBus);
DallasTemperature sensors(&oneWire);
float tempC;
// ====== Дані ======
float objectWeight = 0; // маса вантажу на платформі
String userComment = ""; // додатковий контекст від користувача
String aiAdvice = "Введіть примітку та натисніть 'Отримати пораду'.";
// ---------------- Веб-сторінка ----------------
void uiRoot() {
String page = R"html(
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>📦 Smart Cargo Monitor</title>
<style>
body {
font-family: 'Verdana', Geneva, sans-serif;
background: linear-gradient(to bottom, #e0f7fa, #ffffff);
color:#222;
text-align:center;
margin:0; padding:0;
}
h2 {
color:#006064;
margin-top:25px;
text-shadow: 1px 1px 2px #aaa;
}
.card {
border-radius:15px;
padding:20px;
margin:15px auto;
background: linear-gradient(145deg, #ffffff, #b2ebf2);
width:90%; max-width:400px;
box-shadow: 0 8px 15px rgba(0,0,0,0.15);
transition: transform 0.2s;
}
.card:hover { transform: translateY(-5px); }
button {
padding:12px 24px;
background: linear-gradient(to right, #00796b, #004d40);
border:none;
border-radius:10px;
color:white;
font-size:16px;
font-weight:bold;
transition:0.3s;
}
button:hover {
background: linear-gradient(to right, #004d40, #00796b);
transform: scale(1.05);
cursor:pointer;
}
input, textarea {
width:90%;
padding:12px;
border-radius:10px;
border:1px solid #ccc;
font-size:14px;
margin-top:5px;
margin-bottom:10px;
resize:none;
}
footer {
font-size:12px;
color:#555;
margin:20px 0;
}
</style>
<script>
function refresh(){
fetch('/metrics').then(r=>r.json()).then(j=>{
document.getElementById("weight").innerText=j.weight+" кг";
document.getElementById("temp").innerText=j.temp+" °C";
document.getElementById("ai").innerText=j.ai;
});
}
function ask(){
const text = document.getElementById("comment").value;
fetch('/ask?comment='+encodeURIComponent(text))
.then(r=>r.text()).then(alert);
setTimeout(refresh,3000);
}
setInterval(refresh,4000);
</script>
</head>
<body>
<h2>📦 Smart Cargo Monitor</h2>
<div class="card"><h3>Маса вантажу:</h3><p id="weight">--</p></div>
<div class="card"><h3>Температура навколишнього середовища:</h3><p id="temp">--</p></div>
<div class="card">
<h3>Примітка:</h3>
<textarea id="comment" placeholder="Наприклад: крихкий вантаж, пакунок з продуктами..."></textarea><br>
<button onclick="ask()">Отримати пораду від ШІ</button>
</div>
<div class="card"><h3>AI Рекомендація:</h3><p id="ai">Завантаження...</p></div>
<footer>Smart Cargo Monitor © 2025</footer>
</body>
</html>
)html";
web.send(200,"text/html",page);
}
void uiMetrics() {
DynamicJsonDocument doc(512);
doc["weight"] = objectWeight;
doc["temp"] = tempC;
doc["ai"] = aiAdvice;
String out; serializeJson(doc,out);
web.send(200,"application/json",out);
}
void uiAsk() {
if (web.hasArg("comment")) userComment = web.arg("comment");
else userComment = "";
aiAdvice = "Очікуйте відповідь...";
// Формування запиту до ШІ
DynamicJsonDocument req(1024);
req["model"]="gpt-3.5-turbo";
JsonArray msgs=req.createNestedArray("messages");
JsonObject sys = msgs.createNestedObject();
sys["role"] = "system";
sys["content"] = "You are a logistics assistant. Give short and practical advice for handling cargo safely.";
JsonObject usr = msgs.createNestedObject();
usr["role"] = "user";
usr["content"] = "Маса вантажу: " + String(objectWeight,1) +
" кг, температура: " + String(tempC,1) +
"°C. " + userComment +
". Дай коротку пораду щодо безпечного зберігання чи перевезення вантажу.";
HTTPClient http;
http.begin("https://artificialintelligence.openai.azure.com/openai/deployments/test/chat/completions?api-version=2023-05-15");
http.addHeader("Content-Type","application/json");
http.addHeader("api-key",API_KEY);
String body; serializeJson(req,body);
int code=http.POST(body);
if(code==200){
DynamicJsonDocument resp(2048);
deserializeJson(resp,http.getString());
aiAdvice=resp["choices"][0]["message"]["content"].as<String>();
Serial.println("AI: "+aiAdvice);
} else aiAdvice="Помилка AI ("+String(code)+")";
http.end();
web.send(200,"text/plain","AI запит надіслано!");
}
// ---------------- Setup ----------------
void setup() {
Serial.begin(115200);
// LCD
lcd.init(); lcd.backlight();
lcd.setCursor(0,0); lcd.print("Cargo Monitor...");
// WiFi
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status()!=WL_CONNECTED){ delay(500); Serial.print("."); }
Serial.println(WiFi.localIP());
// HX711
scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
scale.set_scale(); // калібрувати при потребі
// Temperature sensor
sensors.begin();
// Web endpoints
web.on("/",uiRoot);
web.on("/metrics",uiMetrics);
web.on("/ask",uiAsk);
web.begin();
}
// ---------------- Loop ----------------
void loop() {
web.handleClient();
// Оновлення сенсорів
objectWeight = scale.get_units(10) / 1000 * 1.3; // г → кг
sensors.requestTemperatures();
tempC = sensors.getTempCByIndex(0);
// LCD відображення
lcd.clear();
lcd.setCursor(0,0); lcd.print("Weight:" + String(objectWeight,1)+"kg");
lcd.setCursor(0,1); lcd.print("Temp:" + String(tempC,1)+"C");
delay(1500);
}