#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <WebServer.h>
#define RED_PIN 16
#define GREEN_PIN 17
#define BLUE_PIN 18
#define LDR_PIN 35
// === Підключення Wi-Fi ===
const char* ssid = "Wokwi-GUEST";
const char* password = "";
// === Дані для API ===
String AI_API_KEY = "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";
String AI_URL = "https://openrouter.ai/api/v1/chat/completions";
String AI_MODEL = "google/gemini-2.5-flash-lite-preview-09-2025";
// === Ініціалізація вебсервера ===
WebServer server(80);
bool aiMode = false;
int manualR = 240, manualG = 210, manualB = 180;
unsigned long lastLuxUpdate = 0;
int autoLux = 0;
// === Допоміжні функції ===
void setColor(int r, int g, int b) {
analogWrite(RED_PIN, 255 - r);
analogWrite(GREEN_PIN, 255 - g);
analogWrite(BLUE_PIN, 255 - b);
}
int readLux() {
int sensorValue = analogRead(LDR_PIN);
return map(sensorValue, 0, 4095, 0, 1000);
}
String queryAI(String userPrompt) {
WiFiClientSecure client;
client.setInsecure();
HTTPClient http;
if (!http.begin(client, AI_URL)) {
Serial.println("Помилка ініціалізації HTTP");
return "Error";
}
http.addHeader("Content-Type", "application/json");
http.addHeader("Authorization", "Bearer " + AI_API_KEY);
http.addHeader("HTTP-Referer", "https://wokwi.com");
http.addHeader("X-Title", "Smart Office AI System");
DynamicJsonDocument doc(2048);
doc["model"] = AI_MODEL;
JsonArray msgs = doc.createNestedArray("messages");
JsonObject msg = msgs.createNestedObject();
msg["role"] = "user";
msg["content"] = "Suggest RGB lighting for modern office mood in format 255,200,100. Context: " + userPrompt;
String body;
serializeJson(doc, body);
int code = http.POST(body);
if (code <= 0) {
Serial.printf("HTTP error: %d\n", code);
http.end();
return "Error";
}
String response = http.getString();
http.end();
DynamicJsonDocument res(4096);
deserializeJson(res, response);
String content = res["choices"][0]["message"]["content"].as<String>();
Serial.println("AI рекомендація: " + content);
return content;
}
// === Новий мінімалістичний вебінтерфейс ===
String webPage() {
String modeText = aiMode ? "AI Mode (Automatic)" : "Manual Mode";
String lux = String(readLux());
String html = R"rawliteral(
<!DOCTYPE html>
<html lang="uk">
<head>
<meta charset="UTF-8">
<title>Smart Office Lighting</title>
<style>
body {
font-family: 'Segoe UI', sans-serif;
margin: 0;
padding: 0;
background: #f5f7fa;
color: #333;
}
header {
background: #0078d7;
color: white;
padding: 20px;
text-align: center;
font-size: 1.5em;
letter-spacing: 0.5px;
}
main {
display: flex;
flex-direction: column;
align-items: center;
padding: 25px;
}
.panel {
background: white;
border-radius: 15px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
padding: 25px 35px;
width: 90%;
max-width: 450px;
margin-bottom: 25px;
text-align: center;
}
.panel h2 {
margin-top: 0;
color: #0078d7;
}
.value {
font-size: 1.8em;
margin: 10px 0;
color: #222;
}
button {
background: #0078d7;
color: white;
border: none;
padding: 10px 25px;
border-radius: 10px;
font-size: 15px;
cursor: pointer;
transition: 0.3s;
}
button:hover {
background: #005fa3;
}
input[type=range] {
width: 80%;
margin: 10px;
}
footer {
text-align: center;
padding: 10px;
font-size: 0.9em;
color: #777;
}
</style>
</head>
<body>
<header>Smart Office Lighting System</header>
<main>
<div class="panel">
<h2>Mode</h2>
<div class="value" id="mode">)rawliteral" + modeText + R"rawliteral(</div>
<button onclick="toggleMode()">Switch Mode</button>
</div>
<div class="panel">
<h2>Light Sensor</h2>
<div class="value" id="lux">)rawliteral" + lux + R"rawliteral(</div>
<p>Current brightness (LUX)</p>
</div>
<div class="panel" id="manual" style="display:)rawliteral" + (aiMode ? "none" : "block") + R"rawliteral(; ">
<h2>Manual RGB Control</h2>
<label>R <input type="range" id="r" min="0" max="255"></label><br>
<label>G <input type="range" id="g" min="0" max="255"></label><br>
<label>B <input type="range" id="b" min="0" max="255"></label><br>
<button onclick="sendColor()">Apply</button>
</div>
</main>
<footer>© 2025 Smart Office by ESP32</footer>
<script>
async function sendColor(){
let r=document.getElementById('r').value;
let g=document.getElementById('g').value;
let b=document.getElementById('b').value;
await fetch(`/set?r=${r}&g=${g}&b=${b}`);
}
async function toggleMode(){
await fetch('/toggle');
location.reload();
}
async function updateLux(){
let res = await fetch('/lux');
let t = await res.text();
document.getElementById('lux').innerText=t;
}
setInterval(updateLux, 3000);
</script>
</body>
</html>
)rawliteral";
return html;
}
// === Обробники ===
void handleRoot() { server.send(200, "text/html", webPage()); }
void handleSetColor() {
manualR = server.arg("r").toInt();
manualG = server.arg("g").toInt();
manualB = server.arg("b").toInt();
setColor(manualR, manualG, manualB);
server.send(200, "text/plain", "Color updated");
}
void handleToggleMode() {
aiMode = !aiMode;
server.send(200, "text/plain", aiMode ? "AI mode on" : "Manual mode on");
}
void handleAskAI() {
String text = server.arg("text");
String response = queryAI(text);
server.send(200, "text/plain", response);
}
void handleLux() {
autoLux = readLux();
server.send(200, "text/plain", String(autoLux));
}
void setup() {
Serial.begin(115200);
pinMode(RED_PIN, OUTPUT);
pinMode(GREEN_PIN, OUTPUT);
pinMode(BLUE_PIN, OUTPUT);
pinMode(LDR_PIN, INPUT);
WiFi.begin(ssid, password);
Serial.print("Підключення до Wi-Fi");
while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
Serial.println("\nWi-Fi підключено! IP: " + WiFi.localIP().toString());
server.on("/", handleRoot);
server.on("/set", handleSetColor);
server.on("/toggle", handleToggleMode);
server.on("/ask", handleAskAI);
server.on("/lux", handleLux);
server.begin();
setColor(manualR, manualG, manualB);
}
void loop() {
server.handleClient();
if (aiMode && millis() - lastLuxUpdate > 2000) {
lastLuxUpdate = millis();
int lux = readLux();
float norm = lux / 1000.0;
int r = (int)(255 * (1.0 - norm));
int g = (int)(200 * (1.0 - norm * 0.7));
int b = (int)(120 + 100 * norm);
setColor(r, g, b);
}
}