#Precision Drawing and fadeToBlackBy
1 messages · Page 1 of 1 (latest)
//
// ----------------------------------------------------------------------------------------
// Daves PL Precision Draw
// ----------------------------------------------------------------------------------------
//
CRGB LEDPattern::ColorFraction(CRGB colorIn, float fraction)
{
fraction = min(1.0f, fraction);
return CRGB(colorIn).fadeToBlackBy(255 * (1.0f - fraction));
}
void LEDPattern::DrawPixels(float fPos, float count, CRGB color)
{
float availFirstPixel = 1.0f - (fPos - (long)(fPos));
float amtFirstPixel = min(availFirstPixel, count);
float remaining = min(count, _NumLeds-fPos);
int iPos = fPos;
if (remaining > 0.0f)
{
_Leds.data()[iPos++] += ColorFraction(color, amtFirstPixel);
remaining -= amtFirstPixel;
}
// Now draw any full pixels in the middle
while (remaining > 1.0f)
{
_Leds.data()[iPos++] += color;
remaining--;
}
// Draw tail pixel, up to a single full pixel
if (remaining > 0.0f)
{
_Leds.data()[iPos] += ColorFraction(color, remaining);
}
}
//
// ----------------------------------------------------------------------------------------
// My Cylon code
// ----------------------------------------------------------------------------------------
//
void LEDPattern::Cylon()
{
for ( float i = 0; i < _ledsettings._pattern_lead_pixel_size; i++ )
{
float pi = _pattern_position + i - _ledsettings._pattern_lead_pixel_size;
static int pic;
if ((int)pi >= 0 && (int)pi < _NumLeds) { pic = (int)pi; }
DrawPixels(pi, 1, PaletteMode( pic ) );
}
TailMode();
}
//
// ----------------------------------------------------------------------------------------
// My Tailmode function
// ----------------------------------------------------------------------------------------
//
void LEDPattern::TailMode()
{
switch (_ledsettings._pattern_tail)
{
case DUST:
for (int j = 0; j < _NumLeds; j++)
{
if (random(10) == 1)
{
_Leds.data()[j] = _Leds.data()[j].fadeToBlackBy(_ledsettings._pattern_fade_amount);
}
}
break;
case SOLID:
fadeToBlackBy(_Leds.data(), _NumLeds, _ledsettings._pattern_fade_amount);
break;
default:
break;
}
}
the problem im having here is that using TailMode(); somehow f's up the colors in the Cylon pattern :S and i dont see where the error is! (im gonna share 2 videos! 1 how it is supposed to look and 1 with the error with TailMode();
here the colors are correct for the current palette!
realy hard to see it on the video but here you should see that the colors are all messed up and for some reason the leds go ALL white at the middle of the strip and then goes back to the "semi correct" color at the end :S
okay - I'm having a look at this now
@south patio - I'm going to restart what I think you want to happen:
In general... a bar of n pixels slides along, from one end to the other. n might not be integral... and depending on the speed, the position of this bar at any moment might be non-integral.
Further... as it is sliding along, pixels that were turned on will fade out (the "tail") either uniformly (SOLID) or twinkly (DUST)
I think your problems are stemming from the fact this statement of the desired result is ambiguous... and your result is indeed doing something other.
The problem stems from thinking of the strip as having state: the LED settings persist from one step to the next - rather than being calculated afresh. This means each step (each time you call this code to move the bar along) you are really needing to calculate a delta to the strip.
Often in code like this, this is easy - you simply figure out which LEDs are affected, recompute thier values, and then write them. BUT - here there are two gotchas: 1) the code is constantly adding in or fading out the LEDs... and 2) the desired result isn't well defined if the bar position moves by less than the bar length (count passed to DrawPixels)
hmmm
So let's start with DrawPixels - this draws the bar: We could improve your float handling, but that isn't causing the problems. The first thing to note is that this code adds into the LED array. And it does so without a saturating add. If ALL the pixels it is going to touch are black then it will be fine -- but in that case, why add? just set!
On the other hand, if you think it is going to "add in" - then since += is doing a saturating add, it is going to tend to white... if there is any overlap with prior drawn pixels.
so far with you on The first thing to note is that this code adds into the LED array.
And it does so without a saturating add. meaning +=
If you think your method of moving the bar is going to never cause there to be overlapped pixels accept at the fractional ends.... then.... why are you going to this much trouble! Probably easier to just use integral length bars! (since they'd have to move by only an integral amount)
and I'm wrong --- turns out CRGB defined += using a saturating add (just looked it up)
So let's start - do you mean to drag one LED along, leaving a tail? Or will you be dragging a bar of n LEDs along leaving a tail?
dragging a bar of 4
_ledsettings._pattern_lead_pixel_size = 4
and leaving a trail behind
either SOLID or DUST
okay - good, though I'm assuming you mean 4.0f
AND - do you expect the bar to move by less than 4.0f between redraws?
not sure what you mean here?
between steps of redrawing the LED array - between updates - will the position of the bar have moved less than the length of the bar?
between calls to Cylon, does _pattern_position update by less than _pattern_lead_pixel_size?
Also - seems to me that Cylon as a function makes no sense? why is it going to all the work to draw the pixels of the bar one by one when DrawPixel can already draw the whole bar in one go?
bzzzp
that was wrong - okay SO fraction movement it is!
Okay --- so the crux of your problems stems because you have two kinds of operation going on at each step:
- Fade out the existing lights a little
- Draw the bar (n pixels wide at position p)
the first can be done as you are doing it
(except that this is over-work:
_Leds.data()[j] = _Leds.data()[j].fadeToBlackBy(_ledsettings._pattern_fade_amount);
can just be:
_Leds.data()[j].fadeToBlackBy(_ledsettings._pattern_fade_amount);
fadeToBlackBy is a modify in place operation.
)
BUT the second one can't be done with a saturating add!... because that isn't what the operation should be... You want to "draw" it - meaning force the pixels to those colors.... almost.....
The operation you are looking for is max --- in the middle of the strip (the integral positions that are full on the color) you can just set the LEDs, not add to them. But the two fractional ends... that is more tricky: There could be a fading out pixel in that location from a prior run
SO there - you want to set the r g b values to the maximum of the value that is already there, and the computed fractional value for drawing this end of the bar.
CRGB has no operation for that... but it should be easy enough to directly code:
if (remaining > 0.0f)
{
CRGB c = ColorFraction(color, amtFirstPixel);
CRGB& p = _Leds.data()[iPos++];
p.r = max(p.r, c.r);
p.g = max(p.g, c.g);
p.b = max(p.b, c.b);
remaining -= amtFirstPixel;
}
and similarly for the other edge
Lastly - very very important to do the fade first then the bar
gotta run.... I'll check in later
just = - my bad
@coral lance man!! you are awsome!
i've been strugglin with this for 4 days now
when i first implemented the Precision Drawing function i had to rewrite a good portion of my code and then i had to rewrite all of my patterns (and omg i had some problems there) and this was the final problem!
does that mean you have it working now?
ooh yes! 🙂
oh btw while i have you here
someone mentioned that it is bad that i draw "out-of-bounds" on the strip :S
is this true?
the bar is 4... so at the start i start from -4 so that the bar comes in nicely from the start and then goes out numleds + 4
Yes - you MUST NOT manipulate _Leds outside it's bounds EVER.
(was that emphatic enough?)
xD
C/C++ has (in general) no bounds checking on arrays and will happily go right of the edge and clobber other memory
hmmm is there any way i can do this without going out-of-bounds ?
Yes - in DrawPixel we can carefully "prep" in the inputs to keep them in bounds... though it takes a little effort....
I can sketch up some code in a bit (I'm only checking in as I pass by my computer right now.... )
np
hmmm
if ( _ledsettings._pattern_wrap_around == NO && _ledsettings._pattern_active == CYLON )
{
_pattern_position += _pattern_directionFF;
if ( _pattern_position == ( _NumLeds + _ledsettings._pattern_lead_pixel_size ) || _pattern_position == 0 )
{
_pattern_directionFF *= -0.1f;
}
}
this should work but it is not :S
when the bar hits the end it should reverse :S
or can't i do _pattern_directionFF *= -0.1f; with float? :S
First....
void LEDPattern::DrawPixel(int position, CRGB color) {
CRGB& led = _Leds.data()[i];
led.r = max(led.r, c.r);
led.g = max(led.g, c.g);
led.b = max(led.b, c.b);
}
void LEDPattern::DrawBar(float start, float size, CRGB color)
{
if (start < 0.0f) {
// if starting before the array of LEDs, chop off the the start
size -= start;
start = 0.0f;
}
if (start + size > _NumLeds) {
// if ending after the array of LEDs, chop off the end
size = _numLeds - start;
}
if (size <= 0.0f) return; // nothing left to draw
int start_int = std::floor(start);
float start_frac = start - start_int;
if (start_frac > 0.0) {
DrawPixel(start_int, ColorFraction(color, 1.0 - start_frac));
start_int += 1;
}
float end = start + size;
int end_int = std::floor(end);
for (int i = start_int; i <= end_int; ++i) {
DrawPixel(i, color);
}
float end_frac = end - end_int;
if (end_frac > 0.0) {
DrawPixel(end_int, ColorFraction(color, end_frac));
}
}
This is how I'd write the bar drawing. Notice that I've introduced another member function to LEDPattern
You technically don't need std::floor calls in there - just assigning to an int is a floor operation - I was just making it clear
To things about direction code are problems:
_pattern_directionFF *= -0.1f;
not only changes direction... but slows it down by 1/10! I think you wanted:
_pattern_directionFF = - _pattern_directionFF;
//or
_pattern_directionFF *= -1.0f;
Other thing - when fiddling with float, avoid == if you can... you have no idea if it ever will be exactly that value (esp. with increments of 0.1!!!!)
so
if ( _pattern_position >= ( _NumLeds + _ledsettings._pattern_lead_pixel_size ) || _pattern_position <= 0 )
Third bug ... did you want it to bounce off the far edge, or just slide out of sight, then come back? Guessing the first... so:
if ( _pattern_position >= ( _NumLeds - _ledsettings._pattern_lead_pixel_size ) || _pattern_position <= 0 )
(notice the - rather than +)
ooooh
hmmm
well it works
but it is offset :S
it's missing 3 or 4 pixels at the end and it also goes 3 or 4 pixels out on the start :S
hmm so when it hits the end i get
Position : 92.10
and when it hits the start i get
Position : -0.00
hmm
i "fixed" it by doing
if ( _pattern_position >= _NumLeds || _pattern_position <= 4.0f )
dirty hack
because im drawing "out-of-bounds"
are you doing the fade first - then the draw?
Okay.. next step is for you to put the whole project code somewhere where I can read it: GitHub or gist ( if a single file)
I'm still in bed so I'm happy to help in about two hours
It would be good too see the class definition and any other methods it has
@coral lance https://github.com/MythicalForce/Temp
there 🙂
yes
so - why is LEDPattern::Cylon() still looping? Shouldn't just make one call to DrawPixels(_pattern_position, _ledsettings._pattern_lead_pixel_size)?
And I don't understand the static int pic thing at all
None the less - what is the current issue?
flickering on SOLID, right?
also - I'm not sure I understand what _ledsettings._pattern_lead_pixel_size is
the cylon pattern needs to update right? that is why it is looping
isn't the pattern just a bar of n LEDs wide? DrawPixels can already do that
static int pic;
if ((int)pi >= 0 && (int)pi < _NumLeds) { pic = (int)pi; }
this is a fix for the palette! since im still going out of bounds on the pixel draw! the palette can`t go -4 at the start nor more then numleds at the end so to "dirty fix" that i use this so that the palette shows correctly on the strip
also - _pattern_position + i - _ledsettings._pattern_lead_pixel_size --- this just seems wrong: Doesn't the bar start at position _pattern_position and extend for _pattern_led_pixel_size leds?
i bet there are MANY faults to my code! 🙂
but yes the current issue is the flickering on SOLID
I think the first issue is that we don't have a clear, well defined - notion of the the state in LEDPattern means.
LEDSettings& _ledsettings;
std::vector<CRGB> _Leds;
unsigned int _NumLeds;
const unsigned int _MaxLeds;
float _pattern_position; // used be INT
uint16_t _pattern_directionFF;
uint16_t _pattern_spacing;
that's a lot of state.... (and I have no idea what is in _ledsettings)
updating github
check now
settings.h and .cpp should be in there
oh btw
_ledsettings._pattern_lead_pixel_size is the size of the Cylon bar
_ledsettings._pattern_lead_pixel_size = 4
well then im off! hosting my birthday party 🙂
@coral lance ?
sorry - yesterday was a bit of a loss - I didn't sleep the night before and so I spent most of the day like a zombie
So.... the stop gaps in the code to keep things in bounds are sort of a hint that things aren't well construed....
First thought is this:
void LEDPattern::Cylon()
{
TailMode();
auto c = PaletteMode(_pattern_position):
DrawPixels(_pattern_position, _ledsettings._pattern_lead_pixel_size, c);
}
I don't see why Cylon() should be any more than that
Essentially - you're running into problems because of the way the code is structured..... LEDPattern is trying to do way to much... and half it's functionality is oddly split inot LEDSettings
As a consequence, functions like IncrementPatternPosition are really hard to manage.
Also - using sig_ptr for the pattern pointer is really hurting things....
because too much of what it means to be a pattern has to be put into LEDPatten directly - losing any modularity.
SO - let's start with a basic question.... I'm assuming you adapted this from sketch that did a bunch of different patterns. Are you trying to retain that? or just do CYLON?
There is a bunch of work for palette manipulation - but again, are you wanting to make use of that? or just one palette mode?
If your aim is to just do CYLON - I'd go ahead and cut all the other code out. It is very much getting int the way.
If your aim is to support different patterns and palettes and all the generality the code aspires to... it needs some major refactoring... which you can do (it is only ~500 lines of code) -- but don't know if that is the path you want to go down.
- im trying to retain that ( running a bunch of patterns + adding more in the future )
- i want to be able to use all 3 palette modes 🙂
okay - then this code - if you don't want to be constantly trying to debug corner cases... needs some heavy refactoring.
I'd approach it this way:
struct PatternSettings { };
// generic settings for patterns: brightness, direction, tail, wrap, fade...
// these NEVER change except when the user chooses different settings
// so no positions, times, current color, etc.. in here
struct PaletteSettings { }; // same for palette
class LEDStrip {
// I'm just the strip - I know how to draw and tail.. that's all
public:
LEDStrip(int nPixels);
void Clear();
void DrawPixel(int pos, CRGB c);
void DrawBar(float pos, float len, CRGB c);
void Tail();
void Display(); // write to pixel strip, may need NeoPixel instance as arg
private:
// EVERY THING INSIDE HERE IS PRIVATE!!!!
};
class Pattern {
public:
Pattern(int nPixels, const PatternSettings& s);
setSettings(const PatternSettings& s);
virtual update(unsigned long nowMillis) = 0; // update given current time
virtual draw(Palette&, LEDStrip&) = 0; // draw given a palette into a strip
private:
// EVERYTHING ELSE IS PRIVATE
const PatternSettings& settings; // this code can't change them!
}
class Palette {
public:
Palette(int nPixels, PaletteSettings& s);
setSettings(PaletteSettings&);
update(unsigned long nowMillis); // update given current time
CRGB colorForPosition(float pos);
private:
// etc...
};
class Cylon : public Pattern {
...
};
Now your main code ends up something like:
const int nLEDs = 48;
LEDStrip ledStrip(nLEDs);
PaletteSettings paletteSettings;
Palette palette(nLEDs, paletteSettings);
PatternSettings patternSettings;
Cylon patCylon(nLEDs, patternSettings);
Juggle patJuggle(nLEDs, patternSettings);
// etc...
Pattern& currentPattern = patCylon;
void loop() {
auto now = millis();
static unsigned long nextUpdate = 0;
if (now >= nextUpdate) {
nextUpdate += 250; // milliseconds.. or what ever update rate you want
palette.update(now);
currentPattern.update(now);
currentPattern.draw(ledStrip, palette);
ledStrip.tail();
ledStrip.display()
}
}
that's all a bit sketchy... but hopefully you get the idea
the issue is that managing things like wrap around and ping-pong - which are different for each pattern is getting to be a mess in the code
AND the set of variables that hold "the state of the pattern" - are in odd places, named hard things (_pattern_lead_pixel_size makes no sense) - and making the code very hard to debug
i can see that! could be something like barLength or something
it is only used for 2 patterns
I don't even think position make universal sense --- in Theater chase mode, it is really phase
You are in that hard place where it feels like a little bit more tweaking will get this to work... and it probably will ... but finding that tweak is going to be painful..... and the next addition will only get harder....
... BUT, re-writing as I say will take you all day, especially to get the existing patterns to work... and that will be big.... but it will make future coding smooth.
yea maybe i need to re-write the entire thing
i've noticed that if i set each Strip (total of 8 strips in 8 different pins on the ESP32) after some time they will go off-sync.. meaning the Cylon patterns will not run in-sync for some reason :S
i think you have seen my code befor right @coral lance ?
i updated the github with my entire project
@coral lance could my code be done better? im thinking about how i setup each Strip in controller.h and in main.cpp
i feel like im doing it the "heavy way"
im thinking about this
std::vector<LEDSettings> ledsettings { {}, {}, {}, {}, {}, {}, {}, {}, {} };
LEDController<LED_PIN1, LED_AMOUNT_PER_PIN, Config[DEVICE].NUMLED[0]> Strip1{ledsettings[0]};
LEDController<LED_PIN2, LED_AMOUNT_PER_PIN, Config[DEVICE].NUMLED[1]> Strip2{ledsettings[1]};
i do this so that each Strip (on its own pin) has its own settings
but is this fine or can it be done better ?
Well - here's a design choice - are you assuming there is going to be some way for something (user input?) to choose a controller and then adjust it's settings? And you expect to have different settings for each?
If the answer is "not for now but maybe in the future" --- then follow the rule of YAGNI: "You Aren't Gonna Need It" -- and don't code that complexity now.
yes yes and yes
this is my Qt App i made to controll the ESP32 🙂
Aha - had no idea!
okay - the App implies one settings per one strip - and no sharing
yupp
SO -
class LEDController {
public:
...
const LEDSettings& getSettings() const { return _settings; }
void setSettings(const LEDSettings& s) { _settings = s; /* more? */ }
...
private:
LEDSettings _settings;
};
just put one in each LEDController.
It would be nice if we could ensure that LEDController wouldn't go about muchking with the settings directly - but probably isn't worth the cost.
Or - if you want to keep the const LEDSettings& thing in LEDController...
then sure why not the simple, direct:
LEDSettings ledsettings1;
LEDController<LED_PIN1, LED_AMOUNT_PER_PIN, ...> Strip1(ledsettings1);
BUT - the real thing is - why are you templatizing on the led pin, number of leds and "led amount"?
That seems very wrong - it complicates the code significantly - causes a huge explosion in the generated code (I suspect) - and really gains you nothing of significant value (the optimizations at runtime are going to be undetectable)
What are you running this code on?
ESP32-WROOM-32UE
400kB ROM... but still... those templates are buying you nothing - and causing tons of code pain
Ah - wish I had seen that UI before!
i did the template because the num leds needed to be known at compile time so i did it like this
struct DeviceConfig
{
int NUMLED[9];
const char *Device_Name;
const char *Device_Type;
const char *LED_Type;
const char *Wifi_DNS;
};
static constexpr DeviceConfig Config[] =
{
{ {199,4,4,4,4,4,4,4}, "Studio Lights", "ESP32", "STRIP", "studiolights" },
{ {32,32,32,32,32,32,32,32}, "Neon Heaven", "ESP32", "STRIP", "neonheaven" },
{ {28,28,28,28,28,28,28,28}, "Vitrineskap", "ESP32", "STRIP", "vitrineskap" },
{ {70,70,70,70,70,70,70,70}, "The Sun", "ESP32", "STRIP", "thesun" },
{ {19,18,10,10,10,10,19,18}, "Voodoo Mask", "ESP32", "STRIP", "voodoomask" },
{ {4,4,4,4,4,4,4,4}, "Små Sure", "ESP32", "STRIP", "smasure" },
{ {96,96,96,96,96,96,96,96}, "Test Device", "ESP32", "STRIP", "testmachine" },
{ {186,186,186,186,186,186,186,186}, "Computer Desk", "ESP32", "STRIP", "computerdesk" },
{ {72,32,96,32,32,32,32,32}, "HaalandsMarka", "ESP32", "STRIP", "haalandsmarka" },
{ {45,4,4,4,4,4,4,4}, "Røyk Maskin", "ESP32", "STRIP", "smokemachine" }
};
LEDController<LED_PIN1, LED_AMOUNT_PER_PIN, Config[DEVICE].NUMLED[0]> Strip1{ledsettings[0]};
SO - I'd put everything that is in that UI - and only what is in that UI - in a StripSettings struct - and ensure that it is const in any code that is referring to it -
i've made a new project in platformIO! so im ready to re-write the entire code
So - each device has 9 strips, each strip has a different length (maybe) -
Is that DevieConfig code of the GUI - or is it meant to be shared?
i use 9 for "All Strips" soo that all strips pulls settings from 9
the DeviceConfig is just for me to make it easier to change the device im uploading to
These are all ESP32 - so what have Device_Type?
They are all STRIP - why have LED_Type?
this was befor all was ESP32 then i had some ESP32 and some ESP8266! BUT Device_Type is purely information that gets sent to my Qt App so that i can display information about the device im connected to 🙂 that includes LED_Type ! and i have LED_Type because i also have some matrix! and my Qt App checks if the device it is connected to is a Strip or Matrix
i REALY need to go to bed! 01:46 over here! lets talk more tomorrow
im back
so i did the controller.h
#pragma once
#include <FastLED.h>
template <unsigned int PIN_OF_LED, unsigned int NUM_LEDS, unsigned int REAL_NUM_LEDS>
class LEDController
{
public:
CLEDController* controller;
const LEDSettings& getSettings() const { return _settings; }
void setSettings(const LEDSettings& s) { _settings = s; }
LEDController( LEDSettings& s ) : LEDPattern( REAL_NUM_LEDS, s )
{
controller = &FastLED.addLeds<WS2812B, PIN_OF_LED, GRB>( _Leds.data(), NUM_LEDS ).setCorrection( TypicalLEDStrip );
}
private:
LEDSettings _settings;
};
but should i remove the template?
@coral lance
so put everything from LEDSettings and LEDPattern into 1 class ?
That template is buying you nothing.... The code won't be faster and it'll be much bigger. Your compilations will be slower... And the code awkward as everything has to be in headers.
As for settings... It's a big structure... But it is the settings you are gibbering the user.... One structure or two... Which ever makes the coffee feel logical
But perhaps, for names, PatternSettings and PaletteSettings
i fail to see how this is going to work if i trash the template LEDController part
you need to explain more on how this is going to work
just make pin and number of leds (not sure why you have two number of led values) be private const data members, and pass and initialize them in the constructor
OH Cripes - why is FastLED templateted on PIN?
But - you don't need to let that decision infect all your code
CLEDController is not itself templatized
i used the CLEDController* controller; and also two number of led values cause then i could say there is room for 300 leds per controller BUT only use _Numleds
i was trying to figure out a way for the end user via the Qt App to be able to hookup a LED strip (max 300 leds) and then in the software they could write in the numleds and then restart the ESP32 and it would know how many leds there were on the strip connected
and then instead of FastLED.show(); i used Strip1.controller->show(Strip1._Leds.data(), Strip1._NumLeds, 255);
So - pass it in in the construtor... and keep it private!
class LEDController
{
public:
LEDController(CLEDController&);
const LEDSettings& settings() const;
void updateSettings(LEDSettings&);
private:
CLEDController& _cled;
LEDSettings _settings;
};
// later in the .cpp file
LEDController::LEDContrller(CLEDController& c) : _cled(c) { }
// where you build it up:
CLEDController<...> strand1_cled(....);
LEDController strand1(strand1_cled);
okay- realtime reconfigure of strand length... sure
I understand now
but this was as far as i got on it
(I don't know if FastLED will handle that well... or, er, "fast" anymore - if you tell it there could be upto 300 LEDs, but then write patterns to the first 40.... it might be no longer "fast"!)
When the ESP32 in a room (a "device") has multiple strands -- does the user set individual patterns/palettes on each strand? do they all "do the same thing, but at their lengths"? or are they treated as one long strand?
individual patterns/palettes as you see in the UI you can select Strip 1 - 8 and then all then every strand/strip gets the settings from the same place
@coral lance im home 🙂
controller.h
#pragma once
#include <FastLED.h>
#include "settings.h"
class LEDController
{
public:
LEDController(CLEDController& c);
const LEDSettings& settings() const;
void updateSettings(LEDSettings&);
private:
CLEDController& _cled;
LEDSettings _settings;
};
controller.cpp
#include "LEDController/controller.h"
LEDController::LEDController(CLEDController& c) : _cled(c) { }
then in setup()
main.cpp
#if NUM_CHANNELS >= 1
CRGB _leds[96];
CLEDController *strand1_cled = &FastLED.addLeds<WS2812B, LED_PIN1, GRB>( _leds, 96 ).setCorrection( TypicalLEDStrip );
LEDController strand1(*strand1_cled);
#endif
that looks nice
If you want you can replace ( _leds, 96 ) with the ideomatic, if ugly, ( _leds, sizeof(_leds)/sizeof(_leds[0]) )
or
#include <array>
std::array<CRGB, 96> _leds;
...... ( _leds.data(), _leds.size() ) .....
but I'm nit picking
for now i`ll keep it that way
question is
keep this
#pragma once
#include "enum.h"
struct DeviceConfig
{
int NUMLED[9];
const char *Device_Name;
const char *Device_Type;
const char *LED_Type;
const char *Wifi_DNS;
};
static constexpr DeviceConfig Config[] =
{
{ {199,4,4,4,4,4,4,4}, "Studio Lights", "ESP32", "STRIP", "studiolights" },
{ {32,32,32,32,32,32,32,32}, "Neon Heaven", "ESP32", "STRIP", "neonheaven" },
{ {28,28,28,28,28,28,28,28}, "Vitrineskap", "ESP32", "STRIP", "vitrineskap" },
{ {70,70,70,70,70,70,70,70}, "The Sun", "ESP32", "STRIP", "thesun" },
{ {19,18,10,10,10,10,19,18}, "Voodoo Mask", "ESP32", "STRIP", "voodoomask" },
{ {4,4,4,4,4,4,4,4}, "Små Sure", "ESP32", "STRIP", "smasure" },
{ {96,96,96,96,96,96,96,96}, "Test Device", "ESP32", "STRIP", "testmachine" },
{ {186,186,186,186,186,186,186,186}, "Computer Desk", "ESP32", "STRIP", "computerdesk" },
{ {72,32,96,32,32,32,32,32}, "HaalandsMarka", "ESP32", "STRIP", "haalandsmarka" },
{ {45,4,4,4,4,4,4,4}, "Røyk Maskin", "ESP32", "STRIP", "smokemachine" }
};
or do it like this
#if DEVICE == 0 // STUDIOLIGHTS
#define DEVICE_NAME "Studio Lights"
#define DEVICE_TYPE "ESP32"
#define DEVICE_LED_TYPE "STRIP"
#define DEVICE_DNS "studiolights"
#define PIN1_LED_AMOUNT 199
#define PIN2_LED_AMOUNT 4
#define PIN3_LED_AMOUNT 4
#define PIN4_LED_AMOUNT 4
#define PIN5_LED_AMOUNT 4
#define PIN6_LED_AMOUNT 4
#define PIN7_LED_AMOUNT 4
#define PIN8_LED_AMOUNT 4
#endif
#if DEVICE == 1 // NEANHEAVEN
#define DEVICE_NAME "Neon Heaven"
#define DEVICE_TYPE "ESP32"
#define DEVICE_LED_TYPE "STRIP"
#define DEVICE_DNS "neonheaven"
#define PIN1_LED_AMOUNT 32
#define PIN2_LED_AMOUNT 32
#define PIN3_LED_AMOUNT 32
#define PIN4_LED_AMOUNT 32
#define PIN5_LED_AMOUNT 32
#define PIN6_LED_AMOUNT 32
#define PIN7_LED_AMOUNT 32
#define PIN8_LED_AMOUNT 32
#endif
So - this data structure - is used as a datastructure in the Qt programe... but then back in the remote devices, each only needs on line of it --- right?
Is your aim to compile and flash 10 different versions of this program, one per ESP32?
exactly! let me explain
so this is purely for my benefit
when i make changes to the code and i drag in a ESP32 to upload the code to i just change in globals.h this line
#define DEVICE STUDIOLIGHTS // What device to upload to
not to complicate…. but i’d be inclined to have the controller tell them their settings
but keeping your current approach
i guess i’d keep the struct even if it is mostly unused in any given device… it’s not that much static data
true
@coral lance but so far with this code i have no way to actually set "numLeds" via Qt App
#if NUM_CHANNELS >= 1
CRGB _leds[Config[DEVICE].NUMLED[0]];
CLEDController *strand1_cled = &FastLED.addLeds<WS2812B, LED_PIN1, GRB>( _leds, Config[DEVICE].NUMLED[0] ).setCorrection( TypicalLEDStrip );
LEDController strand1(*strand1_cled);
#endif
#if NUM_CHANNELS >= 2
CRGB _leds[Config[DEVICE].NUMLED[1]];
CLEDController *strand2_cled = &FastLED.addLeds<WS2812B, LED_PIN1, GRB>( _leds, Config[DEVICE].NUMLED[1] ).setCorrection( TypicalLEDStrip );
LEDController strand2(*strand2_cled);
#endif
#if NUM_CHANNELS >= 3
CRGB _leds[Config[DEVICE].NUMLED[2]];
CLEDController *strand3_cled = &FastLED.addLeds<WS2812B, LED_PIN1, GRB>( _leds, Config[DEVICE].NUMLED[2] ).setCorrection( TypicalLEDStrip );
LEDController strand3(*strand3_cled);
#endif
#if NUM_CHANNELS >= 4
CRGB _leds[Config[DEVICE].NUMLED[3]];
CLEDController *strand4_cled = &FastLED.addLeds<WS2812B, LED_PIN1, GRB>( _leds, Config[DEVICE].NUMLED[3] ).setCorrection( TypicalLEDStrip );
LEDController strand4(*strand4_cled);
#endif
#if NUM_CHANNELS >= 5
CRGB _leds[Config[DEVICE].NUMLED[4]];
CLEDController *strand5_cled = &FastLED.addLeds<WS2812B, LED_PIN1, GRB>( _leds, Config[DEVICE].NUMLED[4] ).setCorrection( TypicalLEDStrip );
LEDController strand5(*strand5_cled);
#endif
#if NUM_CHANNELS >= 6
CRGB _leds[Config[DEVICE].NUMLED[5]];
CLEDController *strand6_cled = &FastLED.addLeds<WS2812B, LED_PIN1, GRB>( _leds, Config[DEVICE].NUMLED[5] ).setCorrection( TypicalLEDStrip );
LEDController strand6(*strand6_cled);
#endif
#if NUM_CHANNELS >= 7
CRGB _leds[Config[DEVICE].NUMLED[6]];
CLEDController *strand7_cled = &FastLED.addLeds<WS2812B, LED_PIN1, GRB>( _leds, Config[DEVICE].NUMLED[6] ).setCorrection( TypicalLEDStrip );
LEDController strand7(*strand7_cled);
#endif
#if NUM_CHANNELS >= 8
CRGB _leds[Config[DEVICE].NUMLED[7]];
CLEDController *strand8_cled = &FastLED.addLeds<WS2812B, LED_PIN1, GRB>( _leds, Config[DEVICE].NUMLED[7] ).setCorrection( TypicalLEDStrip );
LEDController strand8(*strand8_cled);
#endif
all channels setup
but one thing i did not get
you said something about only calling the pattern once and then let "draw" do the rest
@coral lance should this be in settings class
bool TimeToUpdatePattern();
bool TimeToUpdatePalette();
void UpdateCurrentPalette();
void update();
or controller class?
settings classes model the users choices… and as such, should have modifiable state when running
controllers model active state
but… make each controller self contained
so… i’d have Pattern objects be responsible for their state, and Palette etc..
call each with something like patten.update(now) where Pattern::update(unsigned long)
also.. think of update as only adjusting the state for the passage of time
then, when the led controller decides to draw… pattern.draw(this, palette)
so make a pattern class (that has the update function, draw and so on) and then for each pattern make class cylon : public pattern ?
yes
and, that base class can have utility functions like drawBar
if you want
the idea is to separate the concerns… because the original code had so much being done everywhere it was hard to debug… or add to
yea i can see that now
“I’m a palette. i can give a color for a float between 0.0 and 1.0. i keep track of time and can shift my colors as the settings say. that is all i do. after being set up, my only public interface is color(float), update(unsigned long), and setSetttings(const PaletteSettings&)
sooo should i make "class palette" aswell?
yes
the aim of this kind of “decomposed design” is to separate the design into small units that are self responsible… and have a small public interface as possible, while containing as much of their function privately as possible
it allows you to reason about the internals without having to think about the rest of the program
So this is LEDSettings class so far i mean if i make a Palette and Pattern class what is even left in settings?
#pragma once
#include <Arduino.h>
#include <FastLED.h>
#include "enum.h"
class LEDSettings
{
public:
// Pattern Settings
uint8_t PatternBrightness;
E_Patterns PatternActive;
E_Direction PatternDirection;
uint8_t PatternDelay;
E_Tail PatternTail;
uint8_t PatternFadeAmount;
E_WrapAround PatternWrapAround;
uint8_t PatternBarLength;
// Palette Settings
uint8_t PaletteDelay;
E_Direction PaletteDirection;
E_PaletteMode PaletteMode;
uint8_t PalettePicker;
E_Palette PaletteSelected;
CRGBPalette256 PaletteCurrent;
TBlendType PaletteBlend;
// Update Settings
unsigned long PatternUpdate;
unsigned long PaletteUpdate;
LEDSettings()
{
// Pattern Settings
PatternBrightness = 64;
PatternActive = CYLON;
PatternDirection = FORWARD;
PatternDelay = 127;
PatternTail = DUST;
PatternFadeAmount = 128;
PatternWrapAround = YES;
PatternBarLength = 4;
// Palette Settings
PaletteDelay = 70;
PaletteDirection = FORWARD;
PaletteMode = ONECOLOR;
PalettePicker = 0;
PaletteSelected = palSUNSETREAL;
PaletteBlend = LINEARBLEND;
// Update Settings
PatternUpdate = 0;
PaletteUpdate = 0;
}
// Functions
bool TimeToUpdatePattern();
bool TimeToUpdatePalette();
void UpdateCurrentPalette();
void update();
};
or should the variables stay in settings?
This isn't quite what I was thinking. I'd make PatternSettings and PaletteSettings just be plain old struct with no methods at all. They are just represent the settings that the user set. They can't know about time - or about when to update
struct PatternSettings
{
uint8_t PatternBrightness;
E_Patterns PatternActive;
E_Direction PatternDirection;
uint8_t PatternDelay;
E_Tail PatternTail;
uint8_t PatternFadeAmount;
E_WrapAround PatternWrapAround;
uint8_t PatternBarLength;
};
struct PaletteSettings
{
uint8_t PaletteDelay;
E_Direction PaletteDirection;
E_PaletteMode PaletteMode;
uint8_t PalettePicker;
E_Palette PaletteSelected;
CRGBPalette256 PaletteCurrent;
TBlendType PaletteBlend;
};
so like that
struct PatternSettings {
uint8_t brighness;
E_Patterns pattern;
E_Direction direction;
...
};
struct PaletteSettings {
// similar
};
class Pattern {
public:
void setSettings(const PatternSettings&);
virtual void update(unsigned long);
virtual void draw(LEDController& c, const Palette&) = 0;
private:
PatternSettings settings;
unsigned long time;
};
aaah
but class pattern?
what about the controller then?
class LEDController
{
public:
LEDController(CLEDController& c);
const LEDSettings& settings() const;
void updateSettings(LEDSettings&);
private:
CLEDController& _cled;
LEDSettings _settings;
};
here we have LEDSettings class
class and struct are really exactly the same thing in C++ --- just that default member visibility is private for class, and public for struct ----- but we use them conventionally to mean class are things that manage state and have internal workings --- struct are things that are simple bundles of data
but again
in the controller we have LEDSettings
im confused about that @coral lance
if there is nothing in the LEDSettings why do we use it in controller class?
drop it! That is - I just through two smaller settings structures made sense - especially as palettes and patterns don't need to know about each other settings
on top of that what does LEDController care about the settings at all?
well... seems the tail and fade stuff is PatternSettings...
but I'd be more inclined to put that as utility part of Pattern
so just remove it from the controller?
class Pattern {
public:
void setSettings(const PatternSettings&);
virtual void update(unsigned long);
virtual void draw(LEDController& c, const Palette&) = 0;
protected:
PatternSettings settings;
unsigned long time;
void drawBar(float position, float width, CRGB color);
void applyTailEffect();
};
Seems LEDController will become nice and minimal: "I hold onto a pattern and a palette - and the actual strip of LEDs... when asked to, I can perform the updating operation (passing it on to the pattern and pallette), and if it is time to draw... can coordinate the drawing."
pattern.h
#pragma once
#include "LEDController/controller.h"
#include "palette.h"
class Pattern : public Palette
{
public:
void setSettings(const PatternSettings&);
virtual void update(unsigned long);
virtual void draw(LEDController& c, const Palette&) = 0;
protected:
PatternSettings settings;
unsigned long time;
void drawBar(float position, float width, CRGB color);
void applyTailEffect();
};
palette.h
#include "LEDController/controller.h"
class Palette
{
public:
void setSettings(const PaletteSettings&);
protected:
PaletteSettings settings;
unsigned long time;
};
I suppose LEDController is responsible for accepting a new PatternSettings decoding the E_PATTERN active field - and installing a new instance of the proper Pattern subclass....:
void LEDController::updatePatternSettings(const PatternSettings& s) {
switch (s.active) {
case E_BLINKY: pattern = Blinky(s); break;
case E_CYLON: pattern = Cylon(s); break;
...
default: pattern = AllBlack(s);
}
}
makes you realize that you don't need a setSettings method in Pattern -- you'll always be creating a new one.
Uhm... No --- Either pallette.h nor pattern.h should have to know anything at all about controller.h --- if you are including it - that is a sign that you haven't separated the concerns
yea sorry i see it now
Also - No, Pattern should not be a subclass of Palette --- that is composition through inheritance: You're making a pattern be everything that a palette is, plus being a pattern.
that doesn't separate the concerns much - it mingles them, and allows pattern code to much with palette internals
(going to go work out now - so away for a bit)
without the include of controller.h i get this
that's looking nice and minimal!
yes but it says "identifier "LEDController" is undefined" and "identifier "Palette" is undefined"
i need to have
#include "palette.h"
#include "LEDController/controller.h"
hmmm
include/LEDController/pattern.h:26:21: error: 'LEDController' has not been declared
include/LEDController/controller.h:11:36: error: 'PatternSettings' does not name a type
include/LEDController/pattern.h:26:21: error: 'LEDController' has not been declared
i dont get it why i get that error
@coral lance why is that :S
pattern.h
#pragma once
#include <Arduino.h>
#include <FastLED.h>
#include "enum.h"
#include "palette.h"
#include "LEDController/controller.h"
struct PatternSettings
{
uint8_t PatternBrightness;
E_Patterns PatternActive;
E_Direction PatternDirection;
uint8_t PatternDelay;
E_Tail PatternTail;
uint8_t PatternFadeAmount;
E_WrapAround PatternWrapAround;
uint8_t PatternBarLength;
};
class Pattern
{
public:
void setSettings(const PatternSettings&);
virtual void update(unsigned long);
virtual void draw(LEDController& c, const Palette&) = 0;
protected:
PatternSettings settings;
unsigned long time;
void drawBar(float position, float width, CRGB color);
void applyTailEffect();
};
controller.h
#pragma once
#include <FastLED.h>
#include "settings.h"
#include "pattern.h"
class LEDController
{
public:
LEDController(CLEDController& c);
void updatePatternSettings(const PatternSettings& s);
private:
CLEDController& _cled;
};
because you have the files including each other
you can't do that
So - the problem is that LEDController is trying to be two things:
- a wrapper around CLEDController
- a coordinator of all the parts
SO - the solution is that Pattern shouldn't be handed an LEDController when it draws.. .but just the lower level thing - a CLEDController
because, really - does need anything to draw other than the methods of CLEDController
Probably not - because we don't want Pattern to really know about "all the things"
On my way home from work
back
@coral lance ok now that is fixed
the code so far compiles fine withour errors
so now i can make a pattern
so like this
solidcolor.h
#include "LEDController/pattern.h"
class Solidcolor : public Pattern
{
virtual void Draw()
{
}
};
@coral lance you there m8?
uploaded everything so far to a new git repo
so think of Pattern as having two main parts: given the current state of the pattern draw it. this code should change the object. the other part is what happens to the state on the passage of time… updating the state
excellent i’ll pull that and have a look when back at my system after breakfast
why do you put includes up and over like that? such a structure is usually only used for shared libraries that have public headers distinct from the private ones
huh?
src vs. includes directories
i’d just have everything in src…
minor thing… but wondered why you were following that practice
everytime i make a new project it generates src and include and i've been taught that header files goes into include and cpp in src
hold overs from very old unix systems! like 30+ years
sigh (i’ve been coding professionally for 40+.. i remember those days)
it only makes sense if you are building a shared lib… then the public headers go in their own directory and the internal ones stay with the rest of the code. it helps with the packaging and the clear division of what is public and what is not
oh 😛
i updated the git repo now! 😛 removed everything from include folder and moved it to src 😛
okay - I have the code
so - what's your next step - now that we've thrown it all in the air!
working on setting up the first pattern
#include "pattern.h"
class Solidcolor : public Pattern
{
virtual void Draw()
{
// pattern here
}
virtual void Update()
{
// update what?
}
};
so - a solid color isn't going to - er - update anything, eh?
so perhaps it is going to draw a bar of settings.PatternBarLength at the center of the LED strip
so - do SolidColor
it's a good first step
oh btw
should DrawPixel and ColorFraction functions be in the pattern class?
class Pattern
{
public:
void setSettings(const PatternSettings&);
virtual void update(unsigned long);
virtual void draw(CLEDController& c, const Palette&) = 0;
protected:
PatternSettings settings;
unsigned long time;
void drawBar(float position, float width, CRGB color);
void applyTailEffect();
};
so - drawBar, applyTailEffect, drawPixel are all utility functions that operate on a CLEDController right? I'd put them, along with ColorFraction as free functions explored by draw.h and implemented in draw.cpp
none of those functions requires the state of the Pattern
If I recall, you have the concept that the CLEDController is the physical strip - and may have more LEDs on it that are actually in use... right?
yea in my script i stated that 1 strip could have up to 300 leds per strip per pin(controller)
and then just tell fastLED how many to use
Ah, so if you're only using 44 - then CLEDController thinks there are 44
yea
uhm
about the DrawPixels
it uses
float remaining = min(count, _NumLeds-fPos);
CRGB& p = _Leds.data()[iPos++];
_Leds.data()[iPos++] += color;
CRGB& p = _Leds.data()[iPos];
how do i get access to _NumLeds and _Leds array?
OKAY -
SO - we need to have clear sense of where you're drawing:
Is CLEDController like the NeoPixel Strip object: You can set leds to various values - it keeps an array of colors - and at the end you call .show()?
yea
Ah - so why were you keeping your own array of _Leds?
just how i learned to use FastLED
normaly with FastLED you would just say FastLED.show() at the end to send the data out but with CLEDController you would use
// leds array , number of leds, brightness
Strip1.controller->show(Strip1._Leds.data(), Strip1._NumLeds, 255);
so this is where i would tell the controller HOW many leds i had connected
no need... you can get direct access to CLEDController's array with the member function .leds() -- returns CRBG* --- have a party. And the number of LEDs there... .size()
SO -
oh!
CRGB* leds = controller.leds();
int size= controller.size();
float remaining = min(count, size-fPos);
CRGB& p = leds[iPos++];
leds[iPos++] += color;
CRGB& p = leds[iPos];
code gets easeier... which is a good sign
(BTW - I don't know FastLED at all --- I use AdafruitNeoPixel lib for my work usually.... but I just read through the header file ... and saw what the interface to CLEDController was like)
hmm i think the we need a CLEDController param in the function tho
Yes - that is what I was saying - that those free drawing functions in draw.h/.cpp would all take a CLEDController as the first argument
draw.h
#include <Arduino.h>
#include <FastLED.h>
CRGB ColorFraction(CRGB colorIn, float fraction);
void DrawPixels(float fPos, float count, CRGB color);
draw.cpp
#include "draw.h"
CRGB ColorFraction(CRGB colorIn, float fraction)
{
fraction = min(1.0f, fraction);
return CRGB(colorIn).fadeToBlackBy(255 * (1.0f - fraction));
}
void DrawPixels(CLEDController& controller, float fPos, float count, CRGB color)
{
CRGB* leds = controller.leds();
int size = controller.size();
float availFirstPixel = 1.0f - (fPos - (long)(fPos));
float amtFirstPixel = min(availFirstPixel, count);
float remaining = min(count, size-fPos);
int iPos = fPos;
if (remaining > 0.0f)
{
CRGB c = ColorFraction(color, amtFirstPixel);
CRGB& p = leds[iPos++];
p.r = max(p.r, c.r);
p.g = max(p.g, c.g);
p.b = max(p.b, c.b);
remaining -= amtFirstPixel;
}
// Now draw any full pixels in the middle
while (remaining > 1.0f)
{
leds[iPos++] += color;
remaining--;
}
// Draw tail pixel, up to a single full pixel
if (remaining > 0.0f)
{
CRGB c = ColorFraction(color, remaining);
CRGB& p = leds[iPos];
p.r = max(p.r, c.r);
p.g = max(p.g, c.g);
p.b = max(p.b, c.b);
}
}
so far so good
Question
how would i access Pattern.settings when it is under
protected:
PatternSettings settings;
thinking about this one
CRGB PaletteMode(CLEDController& controller, Pattern& pat, int Index, int Brightness = 127)
{
CRGB* leds = controller.leds();
int size = controller.size();
switch ( _ledsettings._palette_mode )
{
case ONECOLOR: return ColorFromPalette(_ledsettings._palette_current, _ledsettings._palette_picker, Brightness, _ledsettings._palette_blend); break;
case FULLPALETTE: return ColorFromPalette(_ledsettings._palette_current, Index * 255 / size, Brightness, _ledsettings._palette_blend); break;
case FULLPALETTEMOVING: return ColorFromPalette(_ledsettings._palette_current, (_palette_position * 255 / size) - (Index * 255 / size), Brightness, _ledsettings._palette_blend); break;
default: break;
}
return CRGB();
}
all the _ledsettings. are settings i would need to access
@coral lance do i need to make some get functions in the pattern class? 🤔
or maybe i should put that PaletteMode function inside palette.h
so i did this
palette.h
#include "settings.h"
struct PaletteSettings
{
uint8_t PaletteDelay;
E_Direction PaletteDirection;
E_PaletteMode PaletteMode;
uint8_t PalettePicker;
E_Palette PaletteSelected;
CRGBPalette256 PaletteCurrent;
TBlendType PaletteBlend;
unsigned long PalettePosition; // added this here since it is the palettes position or should it be handled somewhere else?
};
class Palette
{
public:
void setSettings(const PaletteSettings&);
CRGB PaletteMode(CLEDController& controller, int Index, int Brightness = 127 ); // Added this here
protected:
PaletteSettings settings;
unsigned long time;
};
palette.cpp
#include "palette.h"
CRGB Palette::PaletteMode(CLEDController& controller, int Index, int Brightness)
{
CRGB* leds = controller.leds();
int size = controller.size();
switch ( settings.PaletteMode )
{
case ONECOLOR: return ColorFromPalette(settings.PaletteCurrent, settings.PalettePicker, Brightness, settings.PaletteBlend); break;
case FULLPALETTE: return ColorFromPalette(settings.PaletteCurrent, Index * 255 / size, Brightness, settings.PaletteBlend); break;
case FULLPALETTEMOVING: return ColorFromPalette(settings.PaletteCurrent, (settings.PalettePosition * 255 / size) - (Index * 255 / size), Brightness, settings.PaletteBlend); break;
default: break;
}
return CRGB();
}
question where would i handle the PatternPosition and PalettePosition ?
PalettePosition in palette class
and
PatternPosition in pattern class
?
yes and yes
@coral lance so the pattern is done
#include "pattern.h"
#include "draw.h"
class Solidcolor : public Pattern
{
virtual void draw(CLEDController& c, const Palette&)
{
CRGB* leds = c.leds();
int size = c.size();
fadeToBlackBy(leds, size, 255);
for(float i = 0; i < size; i++)
{
DrawPixels(i, 1, PaletteMode(c, i));
}
}
virtual void update()
{
}
};
i think! let me know if im missing something or something is wrong
i updated the git repo btw
what do you think?
@coral lance
oh i forgot
did you say drop this or use it?
if ( _ledsettings._pattern_wrap_around == YES && _ledsettings._pattern_direction == FORWARD )
{
_pattern_position += 0.1f;
if ( _ledsettings._pattern_active == THEATHERCHASE )
{
if ( _pattern_position >= 3 )
_pattern_position = 0.0f;
}
else if ( _ledsettings._pattern_active == CYLON )
{
if ( _pattern_position >= (_NumLeds + _ledsettings._pattern_lead_pixel_size) )
_pattern_position = 0.0f;
}
else if ( _ledsettings._pattern_active == MITOSIS )
{
if ( _pattern_position > _NumLeds / 2 - 1 + _ledsettings._pattern_lead_pixel_size )
_pattern_position = 0.0f;
}
else
{
if ( _pattern_position >= _NumLeds )
_pattern_position = 0.0f;
}
you there?
@coral lance ? 😄
sorry - going to be very busy from now on.... (going on tour in March and have to start heavy rehearsal!)
But - the point was to get rid of that code... and let each Pattern subclass, in it's update function do what it thinks it needs. If they want to take into account wrap around and direction - fine... but then each can do it as needed.
for(float i = 0; i < size; i++)
{
DrawPixels(i, 1, PaletteMode(c, i));
}
why that and not
DrawPixels(i, size, ....)
Is it because you need the color to change with each pixel?... we can focus on that
also - what is PaletteMode(....)? Surely you don't want some big free function, passing the CLEDController to it....
why is this not palette.colorForIndex(i)
and nothing more?
If the Palette needs the CLEDController... something is very wrong --- think about it-- should the Palette have the power to draw the LEDs? probably not
If the issue is knowing how long things are..... welll either construct the Palette with the length ... or better yet
palette.color(float(i)/float(size))
CRGB Palette::PaletteMode(CLEDController& controller, int Index, int Brightness)
{
CRGB* leds = controller.leds();
int size = controller.size();
switch ( settings.PaletteMode )
{
case ONECOLOR: return ColorFromPalette(settings.PaletteCurrent, settings.PalettePicker, Brightness, settings.PaletteBlend); break;
case FULLPALETTE: return ColorFromPalette(settings.PaletteCurrent, Index * 255 / size, Brightness, settings.PaletteBlend); break;
case FULLPALETTEMOVING: return ColorFromPalette(settings.PaletteCurrent, (settings.PalettePosition * 255 / size) - (Index * 255 / size), Brightness, settings.PaletteBlend); break;
default: break;
}
return CRGB();
}
huh?
wait so _pattern_position += 0.1f; so this should be in the virtual void update() function?
I don't know .... does every pattern update its position by 0.1 every update cycle?
if every pattern needs to do that - then put it in the base implementation of update -- and besure all the subclasses call the super version in theirs
super version what now? 😐
never heard of that
there we go
hmmm
.pio\build\esp32dev\src\main.cpp.o:(.literal._ZN10Solidcolor4drawER14CLEDController[Solidcolor::draw(CLEDController&)]+0xc): undefined reference to `DrawPixels(float, float, CRGB)'
.pio\build\esp32dev\src\main.cpp.o:(.iram1.28.literal+0x28): undefined reference to `DrawPixels(float, float, CRGB)'
.pio\build\esp32dev\src\main.cpp.o: In function `Solidcolor::draw(CLEDController&)':
C:\Users\Krist\Desktop\MNC 2022/.pio\libdeps\esp32dev\FastLED\src/controller.h:171: undefined reference to `DrawPixels(float, float, CRGB)'
C:\Users\Krist\Desktop\MNC 2022/.pio\libdeps\esp32dev\FastLED\src/controller.h:171: undefined reference to `DrawPixels(float, float, CRGB)'
draw.h
#include <Arduino.h>
#include <FastLED.h>
#include "enum.h"
#include "pattern.h"
CRGB ColorFraction(CRGB colorIn, float fraction);
void DrawPixels(float fPos, float count, CRGB color);
void TailMode();
solidcolor.h
#include "pattern.h"
#include "draw.h"
class Solidcolor : public Pattern
{
public:
virtual void draw(CLEDController& c)
{
CRGB* leds = c.leds();
int size = c.size();
fadeToBlackBy(leds, size, 255);
for(float i = 0; i < size; i++)
{
DrawPixels(i, 1, PaletteMode(c, i));
}
}
};
dont see what im doing wrong here
you haven't implemented DrawPixels
those messages are from the linker
Also - still no idea what PaletteMode is
DrawPixels is implemented
the newest code is on git now - https://github.com/MythicalForce/MNC
@coral lance let me know when you have time again to help 🙂 GL on the tour 🙂
daamn i got it working 🙂
alltho im unsure about 1 thing
but i`ll wait for you to come back 🙂
so what i did this
Solidcolor solidcolor;
TheaterChase theaterchase;
RunningLights runninglights;
Cylon cylon;
void IRAM_ATTR DrawLoopTaskEntry(void *)
{
for (;;)
{
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
TIMERG0.wdt_feed=1;
TIMERG0.wdt_wprotect=0;
//solidcolor.draw(*strand8_cled);
//solidcolor.Palette::ChangePalette();
//theaterchase.draw(*strand8_cled);
//theaterchase.update(*strand8_cled);
//theaterchase.Palette::ChangePalette();
//runninglights.draw(*strand8_cled);
//runninglights.update(*strand8_cled);
//runninglights.Palette::ChangePalette();
cylon.draw(*strand8_cled);
cylon.update(*strand8_cled);
cylon.Palette::ChangePalette();
strand1_cled->show(_leds0, strand1_cled->size(), 255);
strand2_cled->show(_leds1, strand2_cled->size(), 255);
strand3_cled->show(_leds2, strand3_cled->size(), 255);
strand4_cled->show(_leds3, strand4_cled->size(), 255);
strand5_cled->show(_leds4, strand5_cled->size(), 255);
strand6_cled->show(_leds5, strand6_cled->size(), 255);
strand7_cled->show(_leds6, strand7_cled->size(), 255);
strand8_cled->show(_leds7, strand8_cled->size(), 255);
}
}
and it works
but i was supposed to use this
strand1.updatePatternSettings();
strand2.updatePatternSettings();
strand3.updatePatternSettings();
strand4.updatePatternSettings();
strand5.updatePatternSettings();
strand6.updatePatternSettings();
strand7.updatePatternSettings();
strand8.updatePatternSettings();
right?
but
void updatePatternSettings(const PatternSettings& s);
this wants "PatternSettings&"
how do i give it that :S
in main.cpp
controller.cpp
void LEDController::updatePatternSettings(const PatternSettings& s)
{
/*
switch (s.PatternActive)
{
case SOLIDCOLOR: solidcolor(s); break;
default: break;
}
*/
}
keeping it alive
i got all of the patterns up and running 🙂
i've tried to understand and get the updatePatternSettings thing to work but im clueless xD
realy wanna tag you to ask you for the last part xD
@coral lance ? (Sorry for tagging) 🥺 😄
no problem -
I'm around - but sadly very busy now preparing for my tour
(tour is first half of March) - I'm coding (custom electronic instruments!), rehearsing, and (ick) promoting all the time now
If you've got it going -woot.... that's great
if you need more help in the near term... let's rope someone else in here to help out!
well "got it working" in the sence that i call each pattern directly in main.cpp
cylon.draw(*strand8_cled);
cylon.update(*strand8_cled);
cylon.Palette::ChangePalette();
but you wrote this function
controller.cpp
void LEDController::updatePatternSettings(const PatternSettings& s)
{
/*
switch (s.PatternActive)
{
case SOLIDCOLOR: solidcolor(s); break;
default: break;
}
*/
}
well... why not do something like:
// in main.cpp
Pattern* currentPattern = &solidcolor;
...
loop() {
...
currentPattern->draw(...)
currentPattern->update(...)
...
}
...
void LEDController::updatePatternSettings(const PatternSettings& s) {
switch (s.PatternActive) {
case SOLIDCOLOR: currentPattern = &solidcolor; break;
case CYLON: currentPattern = &cylon; break;
...
}
}
but where would i get the "const PatternSettings& s"
how are transmitting the pattern settings from your web app to your devices?
huh, come again?
Where do the pattern settings come from in this system? The user edits them in some desktop app --- how do they get to the devices?
i connect to each device via WebSocket
so you have some code on the devices that receives a PatternSettings from the desktop app.... presumably in some WebSocket message --- when the device receives that - and gets it out of the message, then it calls updatePatternSettings with a reference to it
in the old code i did this
//main.cpp
std::vector<LEDSettings> ledsettings { {}, {}, {}, {}, {}, {}, {}, {}, {} };
#if NUM_CHANNELS >= 1
Strip1.setSettings(ledsettings[8]);
#endif
#if NUM_CHANNELS >= 2
Strip2.setSettings(ledsettings[8]);
#endif
#if NUM_CHANNELS >= 3
Strip3.setSettings(ledsettings[8]);
#endif
#if NUM_CHANNELS >= 4
Strip4.setSettings(ledsettings[8]);
#endif
#if NUM_CHANNELS >= 5
Strip5.setSettings(ledsettings[8]);
#endif
#if NUM_CHANNELS >= 6
Strip6.setSettings(ledsettings[8]);
#endif
#if NUM_CHANNELS >= 7
Strip7.setSettings(ledsettings[8]);
#endif
#if NUM_CHANNELS >= 8
Strip8.setSettings(ledsettings[8]);
#endif
// in websocket.h
if ( inData.containsKey("patternDelay") )
{
ledsettings[selectedstrip]._pattern_delay = (int)inData["patternDelay"];
}
yes
PatternSettings patternSettings;
PaletteSettings paletteSettings;
void handleMessage() {
bool patternSettingsChanged = false;
bool paletteSettingsChanged = false;
...
if (inData.containsKey("patternDelay")) {
patternSettings.delay = (int)inData["patternDelay"];
patternSettingsChanged = true;
}
... // and so on for all parameters
if (patternSettingsChanged) {
LEDController::updatePattern(patternSettings);
}
// ditto for palette
}
hmmm
The other way you could do it is to not bother with the updatePattern() function --- if every pattern is already using the one global PatternSettings object - then they'll see the parameter change next time that they fetch it.
The question is - do they need to notice when it changed, or can they just blindly use the current value of any given parameter?
if the later, there's no need for an update function -- -
for the one parameter that chooses the pattern - that one will of course need a call into LEDController to tell it to change the pattern
so 3 things confusing me here is
- in
main.cpp
Pattern* currentPattern = &solidcolor;
currentPattern->draw();
currentPattern->update();
currentPattern->Palette::ChangePalette();
everyone of these takes/needs a CLEDController so i would need to make it like
currentPattern->draw(*strand1_cled);
currentPattern->draw(*strand2_cled);
currentPattern->draw(*strand3_cled);
currentPattern->draw(*strand4_cled);
currentPattern->draw(*strand5_cled);
currentPattern->draw(*strand6_cled);
currentPattern->draw(*strand7_cled);
currentPattern->draw(*strand8_cled);
currentPattern->update(*strand1_cled);
currentPattern->update(*strand2_cled);
currentPattern->update(*strand3_cled);
currentPattern->update(*strand4_cled);
currentPattern->update(*strand5_cled);
currentPattern->update(*strand6_cled);
currentPattern->update(*strand7_cled);
currentPattern->update(*strand8_cled);
or am i completely wrong?
every strand should have 1 set of PatternSettings and PaletteSettings each
and in websocket.h i have a variable int selectedstrip; so when i select Strip1 (in the Qt Desktop App) it should only update PatternSettings and / or PaletteSettings for strand1 on the ESP32
and so on
you see where im going with this? @coral lance
do different strands have different patterns?
if so - then LEDSettings should have:
Pattern* pattern;
Palette* palette;
PatternSettings patternSettings;
PaletteSettings palleteSettings;
BUT - this means you'll need to heap allocate (and deallocate) the patterns and palettes
Yes
@coral lance so i did it my way (the only way i know how) but i dunno if it is wrong but it works tho so please take a look at it and see if you spot any wrongs or anything else
main.cpp
std::vector<PatternSettings> ledsettings { {}, {}, {}, {}, {}, {}, {}, {}, {} };
void IRAM_ATTR DrawLoopTaskEntry(void *)
{
for (;;)
{
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
TIMERG0.wdt_feed=1;
TIMERG0.wdt_wprotect=0;
strand1.updatePatternSettings(*strand1_cled, ledsettings[0]);
strand2.updatePatternSettings(*strand2_cled, ledsettings[1]);
strand3.updatePatternSettings(*strand3_cled, ledsettings[2]);
strand4.updatePatternSettings(*strand4_cled, ledsettings[3]);
strand5.updatePatternSettings(*strand5_cled, ledsettings[4]);
strand6.updatePatternSettings(*strand6_cled, ledsettings[5]);
strand7.updatePatternSettings(*strand7_cled, ledsettings[6]);
strand8.updatePatternSettings(*strand8_cled, ledsettings[7]);
strand1_cled->show(_leds0, strand1_cled->size(), 255);
strand2_cled->show(_leds1, strand2_cled->size(), 255);
strand3_cled->show(_leds2, strand3_cled->size(), 255);
strand4_cled->show(_leds3, strand4_cled->size(), 255);
strand5_cled->show(_leds4, strand5_cled->size(), 255);
strand6_cled->show(_leds5, strand6_cled->size(), 255);
strand7_cled->show(_leds6, strand7_cled->size(), 255);
strand8_cled->show(_leds7, strand8_cled->size(), 255);
}
}
controller.cpp
#include "controller.h"
#include "pattern/solidcolor.h"
#include "pattern/theaterchase.h"
#include "pattern/runninglights.h"
#include "pattern/cylon.h"
#include "pattern/mitosis.h"
#include "pattern/juggle.h"
#include "pattern/twinkle.h"
#include "pattern/colorstacking.h"
Solidcolor solidcolor;
TheaterChase theaterchase;
Juggle juggle;
RunningLights runninglights;
Cylon cylon;
Mitosis mitosis;
Twinkle twinkle;
ColorStacking colorstacking;
Pattern *currentPattern[] = { &solidcolor, &theaterchase, &juggle, &runninglights, &cylon, &mitosis, &twinkle, &colorstacking };
LEDController::LEDController(CLEDController& c) : _cled(c) { }
void LEDController::updatePatternSettings(CLEDController& c, const PatternSettings& s)
{
currentPattern[s.PatternActive]->setSettings(s);
currentPattern[s.PatternActive]->draw(c);
currentPattern[s.PatternActive]->update(_cled);
currentPattern[s.PatternActive]->Palette::ChangePalette();
}
weird
protected:
PatternSettings patternSettings;
PaletteSettings paletteSettings;
i have these under class Pattern
should i make a settings class? @coral lance
class settings : public Pattern
{
public:
PatternSettings patternSettings;
PaletteSettings paletteSettings;
};
@coral lance you here m8?