#pragma once #include #include #include #include "web_ui.h" // --------------------------------------------------------------- // Der Async-Webserver läuft in einem eigenen Task. Damit es keine // Race-Conditions mit loop() gibt, ändern die Callbacks NIE direkt // settings/stepper, sondern legen nur Befehle in eine Queue. // loop() arbeitet die Queue ab (handleWebCommands in main.cpp). // --------------------------------------------------------------- enum WebCmdType : uint8_t { WC_TIMER, WC_SPEED, WC_RUN, WC_MAXTEMP, WC_ACCEL, WC_WINDOW }; struct WebCmd { WebCmdType type; int32_t v[4]; }; static AsyncWebServer webServer(80); static AsyncWebSocket webSocket("/ws"); static QueueHandle_t webQueue = nullptr; static void onWsEvent(AsyncWebSocket *, AsyncWebSocketClient *, AwsEventType type, void *arg, uint8_t *data, size_t len) { if (type != WS_EVT_DATA) return; AwsFrameInfo *info = (AwsFrameInfo *)arg; if (!(info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT)) return; JsonDocument doc; if (deserializeJson(doc, (const char *)data, len)) return; // ungültiges JSON ignorieren const char *c = doc["cmd"] | ""; WebCmd cmd = {}; if (!strcmp(c, "timer")) { cmd.type = WC_TIMER; cmd.v[0] = doc["v"] | 0; } else if (!strcmp(c, "speed")) { cmd.type = WC_SPEED; cmd.v[0] = doc["v"] | 0; } else if (!strcmp(c, "run")) { cmd.type = WC_RUN; cmd.v[0] = doc["v"] | 0; } else if (!strcmp(c, "maxTemp")) { cmd.type = WC_MAXTEMP; cmd.v[0] = doc["v"] | 0; } else if (!strcmp(c, "accel")) { cmd.type = WC_ACCEL; cmd.v[0] = doc["v"] | 0; } else if (!strcmp(c, "window")) { cmd.type = WC_WINDOW; for (int i = 0; i < 4; i++) cmd.v[i] = doc["v"][i] | 0; } else return; xQueueSend(webQueue, &cmd, 0); } inline void initWebServer() { webQueue = xQueueCreate(8, sizeof(WebCmd)); webSocket.onEvent(onWsEvent); webServer.addHandler(&webSocket); webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(200, "text/html", INDEX_HTML); }); webServer.begin(); Serial.println("[Web] Server gestartet"); }