#include #include #include #include #include "Globals.h" #include "NodeContext.h" #include "NetworkManager.h" #include "ClusterManager.h" #include "ApiServer.h" #include "TaskManager.h" // Include the NeoPattern implementation from this example folder #include "NeoPattern.cpp" #ifndef LED_STRIP_PIN #define LED_STRIP_PIN 2 #endif #ifndef LED_STRIP_LENGTH #define LED_STRIP_LENGTH 8 #endif #ifndef LED_STRIP_BRIGHTNESS #define LED_STRIP_BRIGHTNESS 48 #endif #ifndef LED_STRIP_UPDATE_INTERVAL #define LED_STRIP_UPDATE_INTERVAL 100 #endif #ifndef LED_STRIP_TYPE #define LED_STRIP_TYPE (NEO_GRB + NEO_KHZ800) #endif class NeoPatternService { public: NeoPatternService(NodeContext &ctx, TaskManager &taskMgr, uint16_t numPixels, uint8_t pin, uint8_t type) : ctx(ctx), taskManager(taskMgr), pixels(numPixels, pin, type), updateIntervalMs(LED_STRIP_UPDATE_INTERVAL), brightness(LED_STRIP_BRIGHTNESS) { pixels.setBrightness(brightness); pixels.show(); registerTasks(); setPatternByName("rainbow_cycle"); } void registerApi(ApiServer &api) { api.addEndpoint("/api/neopattern/status", HTTP_GET, [this](AsyncWebServerRequest *request) { JsonDocument doc; doc["pin"] = LED_STRIP_PIN; doc["count"] = pixels.numPixels(); doc["interval_ms"] = updateIntervalMs; doc["brightness"] = brightness; doc["pattern"] = currentPatternName(); doc["total_steps"] = pixels.TotalSteps; doc["color1"] = pixels.Color1; doc["color2"] = pixels.Color2; String json; serializeJson(doc, json); request->send(200, "application/json", json); }); api.addEndpoint("/api/neopattern/patterns", HTTP_GET, [this](AsyncWebServerRequest *request) { JsonDocument doc; JsonArray arr = doc.to(); for (auto &kv : patternSetters) arr.add(kv.first); String json; serializeJson(doc, json); request->send(200, "application/json", json); }); api.addEndpoint("/api/neopattern", HTTP_POST, [this](AsyncWebServerRequest *request) { if (request->hasParam("pattern", true)) { String name = request->getParam("pattern", true)->value(); setPatternByName(name); } if (request->hasParam("interval_ms", true)) { unsigned long v = request->getParam("interval_ms", true)->value().toInt(); if (v < 1) v = 1; updateIntervalMs = v; taskManager.setTaskInterval("neopattern_update", updateIntervalMs); } if (request->hasParam("brightness", true)) { int b = request->getParam("brightness", true)->value().toInt(); if (b < 0) b = 0; if (b > 255) b = 255; setBrightness((uint8_t)b); } // Accept packed color ints or r,g,b triplets if (request->hasParam("color", true)) { pixels.Color1 = (uint32_t)strtoul(request->getParam("color", true)->value().c_str(), nullptr, 0); } if (request->hasParam("color2", true)) { pixels.Color2 = (uint32_t)strtoul(request->getParam("color2", true)->value().c_str(), nullptr, 0); } if (request->hasParam("r", true) || request->hasParam("g", true) || request->hasParam("b", true)) { int r = request->hasParam("r", true) ? request->getParam("r", true)->value().toInt() : 0; int g = request->hasParam("g", true) ? request->getParam("g", true)->value().toInt() : 0; int b = request->hasParam("b", true) ? request->getParam("b", true)->value().toInt() : 0; pixels.Color1 = pixels.Color(r, g, b); } if (request->hasParam("r2", true) || request->hasParam("g2", true) || request->hasParam("b2", true)) { int r = request->hasParam("r2", true) ? request->getParam("r2", true)->value().toInt() : 0; int g = request->hasParam("g2", true) ? request->getParam("g2", true)->value().toInt() : 0; int b = request->hasParam("b2", true) ? request->getParam("b2", true)->value().toInt() : 0; pixels.Color2 = pixels.Color(r, g, b); } if (request->hasParam("total_steps", true)) { pixels.TotalSteps = request->getParam("total_steps", true)->value().toInt(); } if (request->hasParam("direction", true)) { String dir = request->getParam("direction", true)->value(); if (dir.equalsIgnoreCase("forward")) pixels.Direction = FORWARD; else if (dir.equalsIgnoreCase("reverse")) pixels.Direction = REVERSE; } JsonDocument resp; resp["ok"] = true; resp["pattern"] = currentPatternName(); resp["interval_ms"] = updateIntervalMs; resp["brightness"] = brightness; String json; serializeJson(resp, json); request->send(200, "application/json", json); }, std::vector{ ApiServer::ParamSpec{ String("pattern"), false, String("body"), String("string"), patternNamesVector() }, ApiServer::ParamSpec{ String("interval_ms"), false, String("body"), String("number"), {}, String("100") }, ApiServer::ParamSpec{ String("brightness"), false, String("body"), String("number"), {}, String("50") }, ApiServer::ParamSpec{ String("color"), false, String("body"), String("color"), {} }, ApiServer::ParamSpec{ String("color2"), false, String("body"), String("color"), {} }, ApiServer::ParamSpec{ String("r"), false, String("body"), String("number"), {} }, ApiServer::ParamSpec{ String("g"), false, String("body"), String("number"), {} }, ApiServer::ParamSpec{ String("b"), false, String("body"), String("number"), {} }, ApiServer::ParamSpec{ String("r2"), false, String("body"), String("number"), {} }, ApiServer::ParamSpec{ String("g2"), false, String("body"), String("number"), {} }, ApiServer::ParamSpec{ String("b2"), false, String("body"), String("number"), {} }, ApiServer::ParamSpec{ String("total_steps"), false, String("body"), String("number"), {} }, ApiServer::ParamSpec{ String("direction"), false, String("body"), String("string"), { String("forward"), String("reverse") } }, } ); } void setBrightness(uint8_t b) { brightness = b; pixels.setBrightness(brightness); pixels.show(); } private: void registerTasks() { taskManager.registerTask("neopattern_update", updateIntervalMs, [this]() { update(); }); taskManager.registerTask("neopattern_status_print", 10000, [this]() { Serial.printf("[NeoPattern] pattern=%s interval=%lu ms brightness=%u\n", currentPatternName().c_str(), updateIntervalMs, brightness); }); } void registerPatterns() { patternSetters["off"] = [this]() { pixels.ActivePattern = NONE; }; patternSetters["rainbow_cycle"] = [this]() { pixels.RainbowCycle(updateIntervalMs); }; patternSetters["theater_chase"] = [this]() { pixels.TheaterChase(pixels.Color1 ? pixels.Color1 : pixels.Color(127,127,127), pixels.Color2, updateIntervalMs); }; patternSetters["color_wipe"] = [this]() { pixels.ColorWipe(pixels.Color1 ? pixels.Color1 : pixels.Color(255,0,0), updateIntervalMs); }; patternSetters["scanner"] = [this]() { pixels.Scanner(pixels.Color1 ? pixels.Color1 : pixels.Color(0,0,255), updateIntervalMs); }; patternSetters["fade"] = [this]() { pixels.Fade(pixels.Color1, pixels.Color2, pixels.TotalSteps ? pixels.TotalSteps : 32, updateIntervalMs); }; patternSetters["fire"] = [this]() { pixels.ActivePattern = FIRE; pixels.Interval = updateIntervalMs; }; } std::vector patternNamesVector() { if (patternSetters.empty()) registerPatterns(); std::vector v; v.reserve(patternSetters.size()); for (const auto &kv : patternSetters) v.push_back(kv.first); return v; } String currentPatternName() { switch (pixels.ActivePattern) { case NONE: return String("off"); case RAINBOW_CYCLE: return String("rainbow_cycle"); case THEATER_CHASE: return String("theater_chase"); case COLOR_WIPE: return String("color_wipe"); case SCANNER: return String("scanner"); case FADE: return String("fade"); case FIRE: return String("fire"); } return String("off"); } void setPatternByName(const String &name) { if (patternSetters.empty()) registerPatterns(); auto it = patternSetters.find(name); if (it != patternSetters.end()) { pixels.Index = 0; pixels.Direction = FORWARD; it->second(); } } void update() { pixels.Update(); } private: NodeContext &ctx; TaskManager &taskManager; NeoPattern pixels; unsigned long updateIntervalMs; uint8_t brightness; std::map> patternSetters; }; NodeContext ctx({ {"app", "neopattern"}, {"device", "light"}, {"pixels", String(LED_STRIP_LENGTH)}, {"pin", String(LED_STRIP_PIN)} }); NetworkManager network(ctx); TaskManager taskManager(ctx); ClusterManager cluster(ctx, taskManager); ApiServer apiServer(ctx, taskManager, ctx.config.api_server_port); NeoPatternService neoService(ctx, taskManager, LED_STRIP_LENGTH, LED_STRIP_PIN, LED_STRIP_TYPE); void setup() { Serial.begin(115200); network.setupWiFi(); taskManager.initialize(); apiServer.begin(); neoService.registerApi(apiServer); taskManager.printTaskStatus(); } void loop() { taskManager.execute(); yield(); }