120 lines
4.3 KiB
C++
120 lines
4.3 KiB
C++
// =====================================================================
|
|
// WebServerManager.cpp
|
|
// =====================================================================
|
|
#include "WebServerManager.h"
|
|
#include <ArduinoJson.h>
|
|
|
|
WebServerManager::WebServerManager(ZoneManager &zoneManager)
|
|
: _zoneManager(zoneManager), _server(80), _ws("/ws") {
|
|
}
|
|
|
|
void WebServerManager::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 connected from %s\n", client->id(),
|
|
client->remoteIP().toString().c_str());
|
|
} else if (type == WS_EVT_DISCONNECT) {
|
|
Serial.printf("[WS] client #%u disconnected\n", client->id());
|
|
}
|
|
}
|
|
|
|
void WebServerManager::setupRoutes() {
|
|
_ws.onEvent(onWsEvent);
|
|
_server.addHandler(&_ws);
|
|
|
|
// GET /zones -> current zone definitions + occupancy
|
|
_server.on("/zones", HTTP_GET, [this](AsyncWebServerRequest *request) {
|
|
request->send(200, "application/json", _zoneManager.zonesToJson());
|
|
});
|
|
|
|
// POST /updateZones -> replace zone definitions (partial updates
|
|
// allowed; see ZoneManager::zonesFromJson)
|
|
_server.on(
|
|
"/updateZones", HTTP_POST, [](AsyncWebServerRequest *request) {},
|
|
nullptr,
|
|
[this](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
|
|
if (_zoneManager.zonesFromJson(data, len)) {
|
|
request->send(200, "application/json",
|
|
"{\"status\":\"success\",\"message\":\"Zones updated\"}");
|
|
} else {
|
|
request->send(400, "application/json",
|
|
"{\"status\":\"error\",\"message\":\"Invalid JSON\"}");
|
|
}
|
|
});
|
|
|
|
// Basic health check, handy when bringing up a new node in the
|
|
// future multi-radar setup.
|
|
_server.on("/status", HTTP_GET, [](AsyncWebServerRequest *request) {
|
|
StaticJsonDocument<128> doc;
|
|
doc["status"] = "ok";
|
|
doc["uptime_ms"] = millis();
|
|
String out;
|
|
serializeJson(doc, out);
|
|
request->send(200, "application/json", out);
|
|
});
|
|
}
|
|
|
|
void WebServerManager::begin(const char *ssid, const char *password) {
|
|
Serial.print("Connecting to ");
|
|
Serial.println(ssid);
|
|
WiFi.begin(ssid, password);
|
|
|
|
// Matches the original project's blocking connect-on-boot
|
|
// behavior. This only runs once at startup, not in the main
|
|
// loop, so it doesn't violate the "no long delay() in the
|
|
// acquisition path" requirement — radar acquisition hasn't
|
|
// started yet at this point in setup().
|
|
while (WiFi.status() != WL_CONNECTED) {
|
|
delay(500);
|
|
Serial.print(".");
|
|
}
|
|
Serial.println();
|
|
Serial.print("WiFi connected, IP: ");
|
|
Serial.println(WiFi.localIP());
|
|
|
|
setupRoutes();
|
|
_server.begin();
|
|
Serial.println("HTTP server started.");
|
|
}
|
|
|
|
void WebServerManager::loop() {
|
|
if (WiFi.status() != WL_CONNECTED) {
|
|
// Non-blocking reconnect kick. We don't retry-loop with
|
|
// delay() here (unlike begin()) so radar acquisition in the
|
|
// main loop is never stalled by a flaky network.
|
|
static uint32_t lastAttempt = 0;
|
|
uint32_t now = millis();
|
|
if (now - lastAttempt > 5000) {
|
|
Serial.println("WiFi disconnected, retrying...");
|
|
WiFi.reconnect();
|
|
lastAttempt = now;
|
|
}
|
|
}
|
|
_ws.cleanupClients();
|
|
}
|
|
|
|
void WebServerManager::broadcastFrame(const RadarFrame &frame) {
|
|
if (_ws.count() == 0) return; // skip serialization work if nobody's listening
|
|
|
|
StaticJsonDocument<512> doc;
|
|
doc["sensor"] = "LD2453";
|
|
doc["node_id"] = frame.node_id;
|
|
doc["sequence"] = frame.sequence_number;
|
|
doc["timestamp"] = frame.timestamp_ms;
|
|
|
|
JsonArray targets = doc.createNestedArray("targets");
|
|
for (uint8_t i = 0; i < frame.targetCount && i < 3; i++) {
|
|
const RadarTarget &t = frame.targets[i];
|
|
JsonObject jt = targets.createNestedObject();
|
|
jt["id"] = i + 1; // radar slot index, NOT a persistent person ID
|
|
jt["valid"] = t.valid;
|
|
jt["x"] = t.x_mm;
|
|
jt["y"] = t.y_mm;
|
|
jt["speed"] = t.speed;
|
|
}
|
|
|
|
String out;
|
|
serializeJson(doc, out);
|
|
_ws.textAll(out);
|
|
}
|