From 5bf228306349e1ea4591e9f4411817de64019b0b Mon Sep 17 00:00:00 2001 From: Patrick Balsiger Date: Fri, 12 Sep 2025 15:59:21 +0200 Subject: [PATCH] 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 --- examples/neopattern/NeoPattern.cpp | 470 +++++++++++++++++++++++++++++ examples/neopattern/config.h | 31 ++ examples/neopattern/main.cpp | 252 ++++++++++++++++ examples/neopixel/main.cpp | 4 +- include/ApiServer.h | 1 + platformio.ini | 32 ++ src/ApiServer.cpp | 3 + 7 files changed, 791 insertions(+), 2 deletions(-) create mode 100644 examples/neopattern/NeoPattern.cpp create mode 100644 examples/neopattern/config.h create mode 100644 examples/neopattern/main.cpp diff --git a/examples/neopattern/NeoPattern.cpp b/examples/neopattern/NeoPattern.cpp new file mode 100644 index 0000000..fc090b2 --- /dev/null +++ b/examples/neopattern/NeoPattern.cpp @@ -0,0 +1,470 @@ +/** + * Original NeoPattern code by Bill Earl + * https://learn.adafruit.com/multi-tasking-the-arduino-part-3/overview + * + * TODO + * - cleanup the mess + * - fnc table for patterns to replace switch case + * + * Custom modifications by 0x1d: + * - default OnComplete callback that sets pattern to reverse + * - separate animation update from timer; Update now updates directly, UpdateScheduled uses timer + */ +#ifndef __NeoPattern_INCLUDED__ +#define __NeoPattern_INCLUDED__ + +#include + +using namespace std; + +// Pattern types supported: +enum pattern +{ + NONE = 0, + RAINBOW_CYCLE = 1, + THEATER_CHASE = 2, + COLOR_WIPE = 3, + SCANNER = 4, + FADE = 5, + FIRE = 6 +}; +// Patern directions supported: +enum direction +{ + FORWARD, + REVERSE +}; + +// NeoPattern Class - derived from the Adafruit_NeoPixel class +class NeoPattern : public Adafruit_NeoPixel +{ + public: + // Member Variables: + pattern ActivePattern = RAINBOW_CYCLE; // which pattern is running + direction Direction = FORWARD; // direction to run the pattern + + unsigned long Interval = 150; // milliseconds between updates + unsigned long lastUpdate = 0; // last update of position + + uint32_t Color1 = 0; + uint32_t Color2 = 0; // What colors are in use + uint16_t TotalSteps = 32; // total number of steps in the pattern + uint16_t Index; // current step within the pattern + uint16_t completed = 0; + + // FIXME return current NeoPatternState + void (*OnComplete)(int); // Callback on completion of pattern + + uint8_t *frameBuffer; + int bufferSize = 0; + + // Constructor - calls base-class constructor to initialize strip + NeoPattern(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)(int)) + : Adafruit_NeoPixel(pixels, pin, type) + { + frameBuffer = (uint8_t *)malloc(768); + OnComplete = callback; + TotalSteps = numPixels(); + begin(); + } + + NeoPattern(uint16_t pixels, uint8_t pin, uint8_t type) + : Adafruit_NeoPixel(pixels, pin, type) + { + frameBuffer = (uint8_t *)malloc(768); + TotalSteps = numPixels(); + begin(); + } + + void handleStream(uint8_t *data, size_t len) + { + //const uint16_t *data16 = (uint16_t *)data; + bufferSize = len; + memcpy(frameBuffer, data, len); + } + + void drawFrameBuffer(int w, uint8_t *frame, int length) + { + for (int i = 0; i < length; i++) + { + uint8_t r = frame[i]; + uint8_t g = frame[i + 1]; + uint8_t b = frame[i + 2]; + setPixelColor(i, r, g, b); + } + } + + void onCompleteDefault(int pixels) + { + //Serial.println("onCompleteDefault"); + // FIXME no specific code + if (ActivePattern == THEATER_CHASE) + { + return; + } + Reverse(); + //Serial.println("pattern completed"); + } + + // Update the pattern + void Update() + { + switch (ActivePattern) + { + case RAINBOW_CYCLE: + RainbowCycleUpdate(); + break; + case THEATER_CHASE: + TheaterChaseUpdate(); + break; + case COLOR_WIPE: + ColorWipeUpdate(); + break; + case SCANNER: + ScannerUpdate(); + break; + case FADE: + FadeUpdate(); + break; + case FIRE: + Fire(50, 120); + break; + default: + if (bufferSize > 0) + { + drawFrameBuffer(TotalSteps, frameBuffer, bufferSize); + } + break; + } + } + + void UpdateScheduled() + { + if ((millis() - lastUpdate) > Interval) // time to update + { + lastUpdate = millis(); + Update(); + } + } + + // Increment the Index and reset at the end + void Increment() + { + completed = 0; + if (Direction == FORWARD) + { + Index++; + if (Index >= TotalSteps) + { + Index = 0; + completed = 1; + if (OnComplete != NULL) + { + OnComplete(numPixels()); // call the comlpetion callback + } + else + { + onCompleteDefault(numPixels()); + } + } + } + else // Direction == REVERSE + { + --Index; + if (Index <= 0) + { + Index = TotalSteps - 1; + completed = 1; + if (OnComplete != NULL) + { + OnComplete(numPixels()); // call the comlpetion callback + } + else + { + onCompleteDefault(numPixels()); + } + } + } + } + + // Reverse pattern direction + void Reverse() + { + if (Direction == FORWARD) + { + Direction = REVERSE; + Index = TotalSteps - 1; + } + else + { + Direction = FORWARD; + Index = 0; + } + } + + // Initialize for a RainbowCycle + void RainbowCycle(uint8_t interval, direction dir = FORWARD) + { + ActivePattern = RAINBOW_CYCLE; + Interval = interval; + TotalSteps = 255; + Index = 0; + Direction = dir; + } + + // Update the Rainbow Cycle Pattern + void RainbowCycleUpdate() + { + for (int i = 0; i < numPixels(); i++) + { + setPixelColor(i, Wheel(((i * 256 / numPixels()) + Index) & 255)); + } + show(); + Increment(); + } + + // Initialize for a Theater Chase + void TheaterChase(uint32_t color1, uint32_t color2, uint16_t interval, direction dir = FORWARD) + { + ActivePattern = THEATER_CHASE; + Interval = interval; + TotalSteps = numPixels(); + Color1 = color1; + Color2 = color2; + Index = 0; + Direction = dir; + } + + // Update the Theater Chase Pattern + void TheaterChaseUpdate() + { + for (int i = 0; i < numPixels(); i++) + { + if ((i + Index) % 3 == 0) + { + setPixelColor(i, Color1); + } + else + { + setPixelColor(i, Color2); + } + } + show(); + Increment(); + } + + // Initialize for a ColorWipe + void ColorWipe(uint32_t color, uint8_t interval, direction dir = FORWARD) + { + ActivePattern = COLOR_WIPE; + Interval = interval; + TotalSteps = numPixels(); + Color1 = color; + Index = 0; + Direction = dir; + } + + // Update the Color Wipe Pattern + void ColorWipeUpdate() + { + setPixelColor(Index, Color1); + show(); + Increment(); + } + + // Initialize for a SCANNNER + void Scanner(uint32_t color1, uint8_t interval) + { + ActivePattern = SCANNER; + Interval = interval; + TotalSteps = (numPixels() - 1) * 2; + Color1 = color1; + Index = 0; + } + + // Update the Scanner Pattern + void ScannerUpdate() + { + for (int i = 0; i < numPixels(); i++) + { + if (i == Index) // Scan Pixel to the right + { + setPixelColor(i, Color1); + } + else if (i == TotalSteps - Index) // Scan Pixel to the left + { + setPixelColor(i, Color1); + } + else // Fading tail + { + setPixelColor(i, DimColor(getPixelColor(i))); + } + } + show(); + Increment(); + } + + // Initialize for a Fade + void Fade(uint32_t color1, uint32_t color2, uint16_t steps, uint8_t interval, direction dir = FORWARD) + { + ActivePattern = FADE; + Interval = interval; + TotalSteps = steps; + Color1 = color1; + Color2 = color2; + Index = 0; + Direction = dir; + } + + // Update the Fade Pattern + void FadeUpdate() + { + // Calculate linear interpolation between Color1 and Color2 + // Optimise order of operations to minimize truncation error + uint8_t red = ((Red(Color1) * (TotalSteps - Index)) + (Red(Color2) * Index)) / TotalSteps; + uint8_t green = ((Green(Color1) * (TotalSteps - Index)) + (Green(Color2) * Index)) / TotalSteps; + uint8_t blue = ((Blue(Color1) * (TotalSteps - Index)) + (Blue(Color2) * Index)) / TotalSteps; + + ColorSet(Color(red, green, blue)); + show(); + Increment(); + } + + // Calculate 50% dimmed version of a color (used by ScannerUpdate) + uint32_t DimColor(uint32_t color) + { + // Shift R, G and B components one bit to the right + uint32_t dimColor = Color(Red(color) >> 1, Green(color) >> 1, Blue(color) >> 1); + return dimColor; + } + + // Set all pixels to a color (synchronously) + void ColorSet(uint32_t color) + { + for (int i = 0; i < numPixels(); i++) + { + setPixelColor(i, color); + } + show(); + } + + // Returns the Red component of a 32-bit color + uint8_t Red(uint32_t color) + { + return (color >> 16) & 0xFF; + } + + // Returns the Green component of a 32-bit color + uint8_t Green(uint32_t color) + { + return (color >> 8) & 0xFF; + } + + // Returns the Blue component of a 32-bit color + uint8_t Blue(uint32_t color) + { + return color & 0xFF; + } + + // Input a value 0 to 255 to get a color value. + // The colours are a transition r - g - b - back to r. + uint32_t Wheel(uint8_t WheelPos) + { + //if(WheelPos == 0) return Color(0,0,0); + WheelPos = 255 - WheelPos; + if (WheelPos < 85) + { + return Color(255 - WheelPos * 3, 0, WheelPos * 3); + } + else if (WheelPos < 170) + { + WheelPos -= 85; + return Color(0, WheelPos * 3, 255 - WheelPos * 3); + } + else + { + WheelPos -= 170; + return Color(WheelPos * 3, 255 - WheelPos * 3, 0); + } + } + /** + * Effects from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ + */ + void Fire(int Cooling, int Sparking) + { + uint8_t heat[numPixels()]; + int cooldown; + + // Step 1. Cool down every cell a little + for (int i = 0; i < numPixels(); i++) + { + cooldown = random(0, ((Cooling * 10) / numPixels()) + 2); + + if (cooldown > heat[i]) + { + heat[i] = 0; + } + else + { + heat[i] = heat[i] - cooldown; + } + } + + // Step 2. Heat from each cell drifts 'up' and diffuses a little + for (int k = numPixels() - 1; k >= 2; k--) + { + heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3; + } + + // Step 3. Randomly ignite new 'sparks' near the bottom + if (random(255) < Sparking) + { + int y = random(7); + heat[y] = heat[y] + random(160, 255); + //heat[y] = random(160,255); + } + + // Step 4. Convert heat to LED colors + for (int j = 0; j < numPixels(); j++) + { + setPixelHeatColor(j, heat[j]); + } + + showStrip(); + } + + void setPixelHeatColor(int Pixel, uint8_t temperature) + { + // Scale 'heat' down from 0-255 to 0-191 + uint8_t t192 = round((temperature / 255.0) * 191); + + // calculate ramp up from + uint8_t heatramp = t192 & 0x3F; // 0..63 + heatramp <<= 2; // scale up to 0..252 + + // figure out which third of the spectrum we're in: + if (t192 > 0x80) + { // hottest + setPixel(Pixel, 255, 255, heatramp); + } + else if (t192 > 0x40) + { // middle + setPixel(Pixel, 255, heatramp, 0); + } + else + { // coolest + setPixel(Pixel, heatramp, 0, 0); + } + } + + void setPixel(int Pixel, uint8_t red, uint8_t green, uint8_t blue) + { + setPixelColor(Pixel, Color(red, green, blue)); + } + void showStrip() + { + show(); + } +}; + +#endif \ No newline at end of file diff --git a/examples/neopattern/config.h b/examples/neopattern/config.h new file mode 100644 index 0000000..80ebd5c --- /dev/null +++ b/examples/neopattern/config.h @@ -0,0 +1,31 @@ +#ifndef __DEVICE_CONFIG__ +#define __DEVICE_CONFIG__ + +// Scheduler config +#define _TASK_SLEEP_ON_IDLE_RUN +#define _TASK_STD_FUNCTION +#define _TASK_PRIORITY + +// Chip config +#define SPROCKET_TYPE "SPROCKET" +#define SERIAL_BAUD_RATE 115200 +#define STARTUP_DELAY 1000 + +// network config +#define SPROCKET_MODE 1 +#define WIFI_CHANNEL 11 +#define AP_SSID "sprocket" +#define AP_PASSWORD "th3r31sn0sp00n" +#define STATION_SSID "MyAP" +#define STATION_PASSWORD "th3r31sn0sp00n" +#define HOSTNAME "sprocket" +#define CONNECT_TIMEOUT 10000 + +// NeoPixel conig +#define LED_STRIP_PIN D2 +#define LED_STRIP_LENGTH 8 +#define LED_STRIP_BRIGHTNESS 48 +#define LED_STRIP_UPDATE_INTERVAL 200 +#define LED_STRIP_DEFAULT_COLOR 100 + +#endif \ No newline at end of file diff --git a/examples/neopattern/main.cpp b/examples/neopattern/main.cpp new file mode 100644 index 0000000..a9f2b38 --- /dev/null +++ b/examples/neopattern/main.cpp @@ -0,0 +1,252 @@ +#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(); +} \ No newline at end of file diff --git a/examples/neopixel/main.cpp b/examples/neopixel/main.cpp index 064d721..2dc9de1 100644 --- a/examples/neopixel/main.cpp +++ b/examples/neopixel/main.cpp @@ -135,8 +135,8 @@ public: }, std::vector{ ApiServer::ParamSpec{ String("pattern"), false, String("body"), String("string"), patternNamesVector() }, - ApiServer::ParamSpec{ String("interval_ms"), false, String("body"), String("number"), {} }, - ApiServer::ParamSpec{ String("brightness"), false, String("body"), String("number"), {} }, + 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("r"), false, String("body"), String("number"), {} }, ApiServer::ParamSpec{ String("g"), false, String("body"), String("number"), {} }, ApiServer::ParamSpec{ String("b"), false, String("body"), String("number"), {} }, diff --git a/include/ApiServer.h b/include/ApiServer.h index 4856c39..71ca6fe 100644 --- a/include/ApiServer.h +++ b/include/ApiServer.h @@ -26,6 +26,7 @@ public: String location; // "query" | "body" | "path" | "header" String type; // e.g. "string", "number", "boolean" std::vector values; // optional allowed values + String defaultValue; // optional default value (stringified) }; struct EndpointCapability { String uri; diff --git a/platformio.ini b/platformio.ini index 14449c3..e2bd428 100644 --- a/platformio.ini +++ b/platformio.ini @@ -92,3 +92,35 @@ build_src_filter = + + + + +[env:esp01_1m_neopattern] +platform = platformio/espressif8266@^4.2.1 +board = esp01_1m +framework = arduino +upload_speed = 115200 +monitor_speed = 115200 +board_build.partitions = partitions_ota_1M.csv +board_build.flash_mode = dout +board_build.flash_size = 1M +lib_deps = ${common.lib_deps} + adafruit/Adafruit NeoPixel@^1.15.1 +build_flags = -DLED_STRIP_PIN=2 +build_src_filter = + + + + + + + +[env:d1_mini_neopattern] +platform = platformio/espressif8266@^4.2.1 +board = d1_mini +framework = arduino +upload_speed = 115200 +monitor_speed = 115200 +board_build.flash_mode = dio +board_build.flash_size = 4M +lib_deps = ${common.lib_deps} + adafruit/Adafruit NeoPixel@^1.15.1 +build_src_filter = + + + + + + diff --git a/src/ApiServer.cpp b/src/ApiServer.cpp index 60acb94..7b60059 100644 --- a/src/ApiServer.cpp +++ b/src/ApiServer.cpp @@ -365,6 +365,9 @@ void ApiServer::onCapabilitiesRequest(AsyncWebServerRequest *request) { allowed.add(v); } } + if (ps.defaultValue.length() > 0) { + p["default"] = ps.defaultValue; + } } } }