Skip to content

Commit 536b181

Browse files
committed
Add Physical and Virtual Layers
Server ===== - ModuleAnimations: replace ledsModel by PhysicalLayer, add addPin, addPixels pre (add to LiveScripts) and post, remove leds, nrOfLeds, add rgb sliders, add preset (WIP), add Solid, Lissajous and Lines, remove effects, LiveScripts - Rename Leds.h to PhysicalLayer.h: add leds and nrOfLeds, pixelsToBlend, layerV, addPin, addPixel, effects - Add VirtualLayer.h (as LedsLayer in StarLight) - WIP
1 parent 326b983 commit 536b181

File tree

7 files changed

+13258
-12880
lines changed

7 files changed

+13258
-12880
lines changed

docs/general/gettingstarted.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,4 @@ At the moment MoonBase is the work in progress repo and temporary also contains
9595

9696
<img width="350" alt="Screenshot 2025-04-15 at 15 02 33" src="https://github.com/user-attachments/assets/53bc8b2e-a078-46a5-b926-25d581ec8202" />
9797

98-
* The upstream repo can now process this PR
98+
* The upstream repo (MoonBase or MoonLight) can now process this PR

lib/framework/WWWData.h

Lines changed: 12733 additions & 12731 deletions
Large diffs are not rendered by default.

src/custom/Leds.h

Lines changed: 0 additions & 45 deletions
This file was deleted.

src/custom/ModuleAnimations.h

Lines changed: 47 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,13 @@
1414

1515
#if FT_MOONLIGHT == 1
1616

17-
#include "Module.h"
18-
#include "Leds.h"
19-
2017
#undef TAG
2118
#define TAG "💫"
2219

2320
#include "FastLED.h"
24-
#define MAXLEDS 8192
21+
22+
#include "Module.h"
23+
#include "PhysicalLayer.h"
2524

2625
#if FT_LIVESCRIPT
2726
#include "ESPLiveScript.h" //note: contains declarations AND definitions, therefore can only be included once!
@@ -30,16 +29,17 @@
3029
class ModuleAnimations;
3130
static ModuleAnimations *gAnimations = nullptr;
3231

33-
LedsModel ledsModel;
34-
static void _addPixel(uint16_t x, uint16_t y, uint16_t z) {ledsModel.addPixel(x, y, z);}
32+
PhysicalLayer layerP;
3533

34+
static void _addPin(uint8_t pinNr) {layerP.addPin(pinNr);}
35+
static void _addPixelsPre() {layerP.addPixelsPre();}
36+
static void _addPixel(uint16_t x, uint16_t y, uint16_t z) {layerP.addPixel({x, y, z});}
37+
static void _addPixelsPost() {layerP.addPixelsPost();}
3638

3739
class ModuleAnimations : public Module
3840
{
3941
public:
4042

41-
CRGB leds[MAXLEDS];
42-
uint16_t nrOfLeds = 256;
4343
#if FT_LIVESCRIPT
4444
Parser parser = Parser();
4545
#endif
@@ -89,6 +89,12 @@ class ModuleAnimations : public Module
8989

9090
property = root.add<JsonObject>(); property["name"] = "lightsOn"; property["type"] = "checkbox"; property["default"] = true;
9191
property = root.add<JsonObject>(); property["name"] = "brightness"; property["type"] = "range"; property["min"] = 0; property["max"] = 255; property["default"] = 10;
92+
property = root.add<JsonObject>(); property["name"] = "red"; property["type"] = "range"; property["min"] = 0; property["max"] = 255; property["default"] = 10;
93+
property = root.add<JsonObject>(); property["name"] = "green"; property["type"] = "range"; property["min"] = 0; property["max"] = 255; property["default"] = 10;
94+
property = root.add<JsonObject>(); property["name"] = "blue"; property["type"] = "range"; property["min"] = 0; property["max"] = 255; property["default"] = 10;
95+
property = root.add<JsonObject>(); property["name"] = "preset"; property["type"] = "select"; property["default"] = "solid"; values = property["values"].to<JsonArray>();
96+
values.add("Preset1");
97+
values.add("Preset2");
9298
property = root.add<JsonObject>(); property["name"] = "driverOn"; property["type"] = "checkbox"; property["default"] = true;
9399
property = root.add<JsonObject>(); property["name"] = "pin"; property["type"] = "select"; property["default"] = 2; values = property["values"].to<JsonArray>();
94100
values.add("2");
@@ -97,11 +103,13 @@ class ModuleAnimations : public Module
97103
property = root.add<JsonObject>(); property["name"] = "nodes"; property["type"] = "array"; details = property["n"].to<JsonArray>();
98104
{
99105
property = details.add<JsonObject>(); property["name"] = "animation"; property["type"] = "selectFile"; property["default"] = "Random"; values = property["values"].to<JsonArray>();
106+
values.add("Solid");
100107
values.add("Random");
101108
values.add("Sinelon");
102109
values.add("Rainbow");
103110
values.add("Sinus");
104111
values.add("Lissajous");
112+
values.add("Lines");
105113
//find all the .sc files on FS
106114
File rootFolder = ESPFS.open("/");
107115
walkThroughFiles(rootFolder, [&](File folder, File file) {
@@ -117,7 +125,6 @@ class ModuleAnimations : public Module
117125
values.add("Effect");
118126
values.add("Modifier");
119127
values.add("Driver show");
120-
property = details.add<JsonObject>(); property["name"] = "size"; property["type"] = "number"; property["default"] = 85;
121128
property = details.add<JsonObject>(); property["name"] = "error"; property["type"] = "text"; property["ro"] = true;
122129
property = details.add<JsonObject>(); property["name"] = "controls"; property["type"] = "array"; details = property["n"].to<JsonArray>();
123130
{
@@ -146,40 +153,28 @@ class ModuleAnimations : public Module
146153
}
147154
}
148155

149-
void removeLeds() {
150-
// Turn off all LEDs
151-
for (int i = 0; i < nrOfLeds; i++) {
152-
leds[i] = CRGB::Black;
153-
}
154-
FastLED.show();
155-
156-
// Clear the FastLED configuration
157-
FastLED.clear(true); // Pass 'true' to reset internal FastLED data
158-
ESP_LOGD(TAG, "LEDs removed and FastLED configuration reset.");
159-
}
160-
161156
//implement business logic
162157
void onUpdate(UpdatedItem &updatedItem) override
163158
{
164-
if (equal(updatedItem.name, "pin")) {
159+
if (equal(updatedItem.name, "pin") || equal(updatedItem.name, "red") || equal(updatedItem.name, "green") || equal(updatedItem.name, "blue")) {
165160
ESP_LOGD(TAG, "handle %s = %s -> %s", updatedItem.name, updatedItem.oldValue.c_str(), updatedItem.value.as<String>().c_str());
166161

167-
removeLeds();
168-
169-
//In constructor so before onUpdate ...
170-
switch (updatedItem.value.as<int>()) {
162+
//addLeds twice is temp hack to make rgb sliders work
163+
switch (_state.data["pin"].as<int>()) {
171164
case 2:
172-
FastLED.addLeds<WS2812B, 2, GRB>(leds, 0, nrOfLeds);
165+
FastLED.addLeds<WS2812B, 16, GRB>(layerP.leds, 0, layerP.nrOfLeds).setCorrection(CRGB(_state.data["red"],_state.data["green"],_state.data["blue"]));
166+
FastLED.addLeds<WS2812B, 2, GRB>(layerP.leds, 0, layerP.nrOfLeds).setCorrection(CRGB(_state.data["red"],_state.data["green"],_state.data["blue"]));
173167
break;
174168
case 16:
175-
FastLED.addLeds<WS2812B, 16, GRB>(leds, 0, nrOfLeds);
169+
FastLED.addLeds<WS2812B, 2, GRB>(layerP.leds, 0, layerP.nrOfLeds).setCorrection(CRGB(_state.data["red"],_state.data["green"],_state.data["blue"]));
170+
FastLED.addLeds<WS2812B, 16, GRB>(layerP.leds, 0, layerP.nrOfLeds).setCorrection(CRGB(_state.data["red"],_state.data["green"],_state.data["blue"]));
176171
break;
177172
default:
178-
ESP_LOGD(TAG, "unknown pin %d", updatedItem.value.as<int>());
173+
ESP_LOGD(TAG, "unknown pin %d", _state.data["pin"].as<int>());
179174
}
180175
FastLED.setMaxPowerInMilliWatts(10000); // 5v, 2000mA, to protect usb while developing
181176
FastLED.setBrightness(_state.data["lightsOn"]?_state.data["brightness"]:0);
182-
ESP_LOGD(TAG, "FastLED.addLeds n:%d", nrOfLeds);
177+
ESP_LOGD(TAG, "FastLED.addLeds n:%d", layerP.nrOfLeds);
183178
} else if (equal(updatedItem.name, "lightsOn") || equal(updatedItem.name, "brightness")) {
184179
ESP_LOGD(TAG, "handle %s = %s -> %s", updatedItem.name, updatedItem.oldValue.c_str(), updatedItem.value.as<String>().c_str());
185180
FastLED.setBrightness(_state.data["lightsOn"]?_state.data["brightness"]:0);
@@ -198,86 +193,22 @@ class ModuleAnimations : public Module
198193
ESP_LOGD(TAG, "no handle for %s = %s -> %s", updatedItem.name, updatedItem.oldValue.c_str(), updatedItem.value.as<String>().c_str());
199194
}
200195

201-
//AI generated
202-
void sinusEffect(CRGB* leds, uint16_t numLeds, uint8_t hueOffset = 0, uint8_t brightness = 255, uint16_t speed = 10) {
203-
static uint16_t phase = 0; // Tracks the phase of the sine wave
204-
// ESP_LOGD(TAG, "sinusEffect %d %d %d %d", numLeds, hueOffset, brightness, speed);
205-
206-
for (uint16_t i = 0; i < numLeds; i++) {
207-
// Calculate the sine wave value for the current LED
208-
uint8_t wave = sin8((i * 255 / numLeds) + phase);
209-
210-
// Map the sine wave value to a color hue
211-
uint8_t hue = wave + hueOffset;
212-
213-
// Set the LED color using the calculated hue
214-
leds[i] = CHSV(hue, 255, brightness);
215-
}
216-
217-
// Increment the phase to animate the wave
218-
phase += speed;
219-
}
220-
221196
//run effects
222197
void loop()
223198
{
224199
bool showLeds = false;
225200

226201
for (JsonObject node: _state.data["nodes"].as<JsonArray>()) {
227-
String animation = node["animation"];
228202
//select the right effect
229-
if (animation == "Random") {
230-
fadeToBlackBy(leds, nrOfLeds, 70);
231-
leds[random16(nrOfLeds)] = CRGB(255, random8(), 0);
232-
showLeds = true;
233-
} else if (animation == "Sinelon") {
234-
fadeToBlackBy(leds, nrOfLeds, 20);
235-
uint8_t bpm = 60;
236-
int pos = beatsin16( bpm, 0, 255 );
237-
leds[pos] += CHSV( millis()/50, 255, 255); //= CRGB(255, random8(), 0);
238-
showLeds = true;
239-
} else if (animation == "Rainbow") {
240-
static uint8_t hue = 0;
241-
fill_rainbow(leds, nrOfLeds, hue++, 7);
242-
showLeds = true;
243-
} else if (animation == "Sinus") {
244-
fadeToBlackBy(leds, nrOfLeds, 70);
245-
sinusEffect(leds, nrOfLeds, millis() / 10, 255, 5);
246-
showLeds = true;
247-
} else if (animation == "Lissajous") {
248-
249-
uint8_t xFrequency = 64;// = leds.effectControls.read<uint8_t>();
250-
uint8_t fadeRate = 128;// = leds.effectControls.read<uint8_t>();
251-
uint8_t speed = 128;// = leds.effectControls.read<uint8_t>();
252-
CRGBPalette16 palette = PartyColors_p;
253-
254-
EffectNode leds;
255-
256-
leds.fadeToBlackBy(fadeRate);
257-
uint_fast16_t phase = millis() * speed / 256; // allow user to control rotation speed, speed between 0 and 255!
258-
Coord3D locn = {0,0,0};
259-
for (int i=0; i < 256; i ++) {
260-
//WLEDMM: stick to the original calculations of xlocn and ylocn
261-
locn.x = sin8(phase/2 + (i*xFrequency)/64);
262-
locn.y = cos8(phase/2 + i*2);
263-
locn.x = (leds.size.x < 2) ? 1 : (::map(2*locn.x, 0,511, 0,2*(leds.size.x-1)) +1) /2; // softhack007: "*2 +1" for proper rounding
264-
// leds.setPixelColor((uint8_t)xlocn, (uint8_t)ylocn, leds.color_from_palette(sys->now/100+i, false, PALETTE_SOLID_WRAP, 0));
265-
// leds[locn] = ColorFromPalette(leds.palette, sys->now/100+i);
266-
// leds.setPixelColorPal(locn, millis()/100+i);
267-
leds.setPixelColor(locn, ColorFromPalette(palette, millis()/100+i, 255));
268-
}
269-
showLeds = true;
270-
} else {
271-
//Done by live script (Yves)
272-
}
203+
showLeds |= layerP.effectFrame(node["animation"]);
273204
}
274205

275206
//show connected clients on the led display
276207
// static uint8_t lastConnectedClients = 0;
277208
// if (_socket->getConnectedClients() == 0) lastConnectedClients++;
278209
for (int i = 0; i < _socket->getConnectedClients(); i++) {
279210
// ESP_LOGD(TAG, "socket %d", i);
280-
leds[i] = CRGB(0, 0, 128);
211+
layerP.leds[i] = CRGB(0, 0, 128);
281212
}
282213

283214
// Serial.printf(" %s", animation.c_str());
@@ -370,25 +301,38 @@ class ModuleAnimations : public Module
370301

371302
gAnimations = this;
372303
if (equal(type, "Effect")) {
373-
addExternalVariable("leds", "CRGB *", "", (void *)leds);
304+
addExternalVariable("leds", "CRGB *", "", (void *)layerP.leds);
374305
// addExternalFunction("fadeToBlackBy", "void", "uint8_t", (void *)fadeToBlackBy_static);
375306
uint16_t (*_random16)(uint16_t)=random16; //enforce specific random16 function
376307
addExternalFunction("random16", "uint16_t", "uint16_t", (void *)_random16);
377308
addExternalFunction("sin8", "uint8_t", "uint8_t", (void *)sin8);
309+
scScript += "#define NUM_LEDS " + std::to_string(layerP.nrOfLeds) + "\n"; //NUM_LEDS is used in arrays -> must be define e.g. uint8_t rMapRadius[NUM_LEDS];
310+
// scScript += "void main(){setup();while(2>1){loop();sync();}}";
378311
} else if (equal(type, "Fixture definition")) {
379-
if (findLink("addPixel", externalType::function) == -1)
380-
addExternalFunction("addPixel", "void", "uint16_t,uint16_t,uint16_t", (void *)_addPixel);
312+
addExternalFunction("addPin", "void", "uint8_t", (void *)_addPin);
313+
addExternalFunction("addPixelsPre", "void", "", (void *)_addPixelsPre);
314+
addExternalFunction("addPixel", "void", "uint16_t,uint16_t,uint16_t", (void *)_addPixel);
315+
addExternalFunction("addPixelsPost", "void", "", (void *)_addPixelsPost);
316+
// scScript += "void main(){addPixelsPre();setup();addPixelsPost();}";
381317
}
382-
318+
383319
Executable executable = parser.parseScript(&scScript);
384320
executable.name = animation;
385321
ESP_LOGD(TAG, "parsing %s done\n", animation);
386322
scriptRuntime.addExe(executable); //if already exists, delete it first
387323
ESP_LOGD(TAG, "addExe success %s\n", executable.exeExist?"true":"false");
388324

389-
if (executable.exeExist)
390-
executable.execute("main"); //background task (async - vs sync)
391-
else
325+
if (executable.exeExist) {
326+
if (equal(type, "Fixture definition")) {
327+
executable.execute("main"); //background task (async - vs sync)
328+
// pass = 1;
329+
// liveM->executeTask(liveFixtureID, "c");
330+
// pass = 2;
331+
// liveM->executeTask(liveFixtureID, "c");
332+
}
333+
// if (equal(type, "Effect")) // not working yet!!!
334+
// executable.executeAsTask("main"); //background task (async - vs sync)
335+
} else
392336
ESP_LOGD(TAG, "error %s", executable.error.error_message.c_str());
393337

394338
for (asm_external el: external_links) {

0 commit comments

Comments
 (0)