Files
spore/examples/neopattern/main.cpp
Patrick Balsiger 5bf2283063 feat: Add NeoPattern example and update API server functionality
- Add new NeoPattern example with pattern-based LED control
- Update ApiServer with enhanced functionality
- Modify neopixel example
- Update platformio configuration
2025-09-12 16:05:38 +02:00

252 lines
10 KiB
C++

#include <Arduino.h>
#include <functional>
#include <map>
#include <vector>
#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<JsonArray>();
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>{
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<String> patternNamesVector() {
if (patternSetters.empty()) registerPatterns();
std::vector<String> 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<String, std::function<void()>> 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();
}