Files
esp_jardin/src/web_server.cpp
T
2026-05-23 16:49:31 +02:00

149 lines
5.2 KiB
C++

#include "web_server.h"
#include "config.h"
#include <LittleFS.h>
#include <ESPAsyncWebServer.h>
#include <ArduinoJson.h>
static AsyncWebServer _server(80);
static AsyncWebSocket _ws("/ws");
// ── Construction JSON temps réel ─────────────────────────────────────
static String _buildJsonSondes() {
JsonDocument doc;
JsonArray arr = doc["sondes"].to<JsonArray>();
if (xSemaphoreTake(xSondesMutex, pdMS_TO_TICKS(20)) == pdTRUE) {
for (uint8_t i = 0; i < NB_SONDES; i++) {
JsonObject s = arr.add<JsonObject>();
s["nom"] = sondesConfig[i].nom;
s["erreur"] = sondesEtat[i].erreur;
if (!sondesEtat[i].erreur) {
s["temp"] = String(sondesEtat[i].tempActuelle, 1);
} else {
s["temp"] = nullptr;
}
}
xSemaphoreGive(xSondesMutex);
}
doc["uptime"] = (millis() - netStatus.uptimeDemarrage) / 1000;
doc["rssi"] = netStatus.rssi;
String out;
serializeJson(doc, out);
return out;
}
// ── Construction JSON historique ─────────────────────────────────────
static String _buildJsonHistory() {
JsonDocument doc;
JsonArray arr = doc.to<JsonArray>();
if (xSemaphoreTake(xHistMutex, pdMS_TO_TICKS(50)) == pdTRUE) {
for (uint16_t i = 0; i < HIST_TAILLE; i++) {
uint16_t idx = (histIdx + i) % HIST_TAILLE;
if (historique[idx].timestamp == 0) continue;
JsonObject pt = arr.add<JsonObject>();
pt["ts"] = historique[idx].timestamp;
JsonArray t = pt["t"].to<JsonArray>();
for (uint8_t j = 0; j < NB_SONDES; j++) {
if (isnan(historique[idx].temps[j])) {
t.add(nullptr);
} else {
t.add(String(historique[idx].temps[j], 1));
}
}
}
xSemaphoreGive(xHistMutex);
}
String out;
serializeJson(doc, out);
return out;
}
static void _onWsEvent(AsyncWebSocket* server, AsyncWebSocketClient* client,
AwsEventType type, void* arg, uint8_t* data, size_t len) {
if (type == WS_EVT_CONNECT) {
Serial.printf("[WS] Client #%u connecté\n", client->id());
} else if (type == WS_EVT_DISCONNECT) {
Serial.printf("[WS] Client #%u déconnecté\n", client->id());
}
}
void web_server_init() {
bool fsOk = LittleFS.begin();
if (!fsOk) {
Serial.println("[FS] Erreur montage LittleFS — interface web indisponible, API REST active");
} else {
Serial.println("[FS] LittleFS monté");
}
_ws.onEvent(_onWsEvent);
_server.addHandler(&_ws);
// GET /api/status
_server.on("/api/status", HTTP_GET, [](AsyncWebServerRequest* req) {
JsonDocument doc;
doc["rssi"] = netStatus.rssi;
doc["uptime"] = (millis() - netStatus.uptimeDemarrage) / 1000;
doc["ramLibre"] = ESP.getFreeHeap();
doc["modeAP"] = netStatus.modeAP;
doc["mqttConnecte"] = netStatus.mqttConnecte;
String out;
serializeJson(doc, out);
req->send(200, "application/json", out);
});
// GET /api/temperatures
_server.on("/api/temperatures", HTTP_GET, [](AsyncWebServerRequest* req) {
JsonDocument doc;
if (xSemaphoreTake(xSondesMutex, pdMS_TO_TICKS(20)) == pdTRUE) {
for (uint8_t i = 0; i < NB_SONDES; i++) {
String key = "sonde_" + String(i + 1);
if (!sondesEtat[i].erreur) {
doc[key] = String(sondesEtat[i].tempActuelle, 1);
} else {
doc[key] = nullptr;
}
}
xSemaphoreGive(xSondesMutex);
}
doc["unit"] = "C";
String out;
serializeJson(doc, out);
req->send(200, "application/json", out);
});
// GET /api/history
_server.on("/api/history", HTTP_GET, [](AsyncWebServerRequest* req) {
req->send(200, "application/json", _buildJsonHistory());
});
// POST /api/config — body: {"intervalleMs":10000,"mqttBroker":"10.0.0.3","mqttPort":1883}
_server.on("/api/config", HTTP_POST,
[](AsyncWebServerRequest* req) {},
nullptr,
[](AsyncWebServerRequest* req, uint8_t* data, size_t len, size_t, size_t) {
JsonDocument doc;
DeserializationError err = deserializeJson(doc, data, len);
if (err) {
req->send(400, "application/json", "{\"erreur\":\"JSON invalide\"}");
return;
}
if (doc["intervalleMs"].is<uint32_t>()) {
Serial.printf("[CONFIG] intervalleMs: %u\n", (uint32_t)doc["intervalleMs"]);
}
req->send(200, "application/json", "{\"ok\":true}");
}
);
if (fsOk) {
_server.serveStatic("/", LittleFS, "/").setDefaultFile("index.html");
}
_server.begin();
Serial.println("[HTTP] Serveur web démarré sur port 80");
}
void web_server_notify_clients() {
if (_ws.count() == 0) return;
_ws.cleanupClients();
_ws.textAll(_buildJsonSondes());
}