#include <WiFi.h>
#include <WebServer.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include "DHTesp.h"
#include "RTClib.h"
#include <LiquidCrystal_I2C.h>
// ====== WiFi конфігурація =====
const char* WIFI_SSID = "Wokwi-GUEST";
const char* WIFI_PASSWORD = "";
const char* API_KEY = "b453666eb3d04c1fa7c608375d88f7fc";
// ====== Сервісні об’єкти ======
WebServer web(80);
RTC_DS1307 clockRTC;
DHTesp dht;
LiquidCrystal_I2C lcd(0x27, 16, 2);
// ====== Піни сенсорів ======
#define PIN_DHT 15
#define PIN_SOIL 34
#define PIN_PH 35
// ====== Історія вимірів ======
const int MAX_SAMPLES = 50;
float tBuf[MAX_SAMPLES];
float hBuf[MAX_SAMPLES];
int soilBuf[MAX_SAMPLES];
float phBuf[MAX_SAMPLES];
int idx = 0;
// ====== Робочі змінні ======
String aiHint = "Awaiting AI forecast...";
String lastStr = "No data";
// ---------------- Веб-сторінка ----------------
void uiRoot() {
String page = R"html(
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Smart Agro System</title>
<script src="https://www.gstatic.com/charts/loader.js"></script>
<script>
google.charts.load('current', {'packages':['corechart']});
google.charts.setOnLoadCallback(refresh);
function refresh(){
fetch('/metrics').then(r=>r.json()).then(j=>{
let opts={width:400,height:200};
function draw(div, name){
let dt=new google.visualization.DataTable();
dt.addColumn('string','t');
dt.addColumn('number',name);
dt.addRows(j.hist.map(x=>[x.time,x[name]]));
new google.visualization.LineChart(document.getElementById(div)).draw(dt,opts);
}
draw('tDiv','temp');
draw('hDiv','hum');
draw('sDiv','soil');
draw('pDiv','ph');
document.getElementById("forecast").innerText=j.forecast;
});
}
function simulateIrrigation(){
fetch('/water').then(r=>r.text()).then(alert);
}
setInterval(refresh,5000);
</script>
</head>
<body>
<h2>🌿 Smart Agro Monitor</h2>
<div id="tDiv"></div>
<div id="hDiv"></div>
<div id="sDiv"></div>
<div id="pDiv"></div>
<h3>AI Assistant:</h3>
<p id="forecast">Loading...</p>
<button onclick="simulateIrrigation()">💧 Water Simulation</button>
</body>
</html>
)html";
web.send(200,"text/html",page);
}
void uiMetrics() {
DynamicJsonDocument doc(2048);
JsonArray arr = doc.createNestedArray("hist");
for(int i=0;i<MAX_SAMPLES;i++){
int j=(idx+i)%MAX_SAMPLES;
if(tBuf[j]==0 && hBuf[j]==0) continue;
JsonObject o=arr.createNestedObject();
o["time"]=String(j);
o["temp"]=tBuf[j];
o["hum"]=hBuf[j];
o["soil"]=soilBuf[j];
o["ph"]=phBuf[j];
}
doc["forecast"]=aiHint;
String out;
serializeJson(doc,out);
web.send(200,"application/json",out);
}
void uiWater() {
int last=(idx-1+MAX_SAMPLES)%MAX_SAMPLES;
soilBuf[last]=min(100,soilBuf[last]+10);
web.send(200,"text/plain","Watering simulated");
}
// ---------------- Виклик GPT ----------------
void askAI(const String& input){
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 an agricultural AI assistant. Respond briefly.";
JsonObject usr = msgs.createNestedObject();
usr["role"] = "user";
usr["content"] = input;
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());
aiHint=resp["choices"][0]["message"]["content"].as<String>();
Serial.println("AI said: "+aiHint);
} else {
Serial.println("AI HTTP error: "+String(code));
}
http.end();
}
// ---------------- Setup ----------------
void setup(){
Serial.begin(115200);
// WiFi
WiFi.begin(WIFI_SSID,WIFI_PASSWORD);
if(WiFi.status()!=WL_CONNECTED){
delay(300);
Serial.print("#");
}
Serial.println(WiFi.localIP());
// сенсори
dht.setup(PIN_DHT,DHTesp::DHT22);
pinMode(PIN_SOIL,INPUT);
pinMode(PIN_PH,INPUT);
clockRTC.begin();
// LCD
lcd.init(); lcd.backlight();
// web endpoints
web.on("/",uiRoot);
web.on("/metrics",uiMetrics);
web.on("/water",uiWater);
web.begin();
}
// ---------------- Loop ----------------
int once=0;
void loop(){
web.handleClient();
TempAndHumidity th = dht.getTempAndHumidity();
float T=th.temperature;
float H=th.humidity;
int soilRaw=analogRead(PIN_SOIL);
int soilPct=map(soilRaw,0,4095,100,0);
int phRaw=analogRead(PIN_PH);
float v=phRaw*(3.3/4095.0);
float pH=7+((2.5-v)/0.18);
DateTime t=clockRTC.now();
String stamp=String(t.hour())+":"+String(t.minute())+":"+String(t.second());
lastStr="["+stamp+"] T:"+String(T,1)+"C H:"+String(H,1)+"% S:"+String(soilPct)+"% pH:"+String(pH,2);
Serial.println(lastStr);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("T:"+String(T,1)+" H:"+String(H,1));
lcd.setCursor(0,1);
lcd.print("Soil:"+String(soilPct)+"% pH:"+String(pH,1));
// збереження
tBuf[idx]=T; hBuf[idx]=H; soilBuf[idx]=soilPct; phBuf[idx]=pH;
idx=(idx+1)%MAX_SAMPLES;
delay(1000);
if(once==0){
askAI("Given this farm data, when to irrigate?\n"+lastStr);
once=1;
}
}