#Precision Drawing and fadeToBlackBy

1 messages · Page 1 of 1 (latest)

south patio
#

So this is my code

#
//
// ----------------------------------------------------------------------------------------
// 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

coral lance
#

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)

south patio
#

hmmm

coral lance
#

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.

south patio
#

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 +=

coral lance
#

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?

south patio
#

dragging a bar of 4

#

_ledsettings._pattern_lead_pixel_size = 4

#

and leaving a trail behind

#

either SOLID or DUST

coral lance
#

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?

south patio
#

not sure what you mean here?

coral lance
#

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?

south patio
#

ooh now i gotcha 🙂

#

position moves by _pattern_position += 0.1f;

coral lance
#

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:

  1. Fade out the existing lights a little
#
  1. 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

south patio
#

hmmmm

#

CRGB c += ColorFraction(color, amtFirstPixel);

coral lance
#

just = - my bad

south patio
#

@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!

coral lance
#

does that mean you have it working now?

south patio
#

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

coral lance
#

Yes - you MUST NOT manipulate _Leds outside it's bounds EVER.

#

(was that emphatic enough?)

south patio
#

xD

coral lance
#

C/C++ has (in general) no bounds checking on arrays and will happily go right of the edge and clobber other memory

south patio
#

hmmm is there any way i can do this without going out-of-bounds ?

coral lance
#

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.... )

south patio
#

np

south patio
#

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

coral lance
#

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 +)

south patio
#

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"

south patio
#

i noticed something now

#

on "SOLID" the 4 pixel bar is "flashing" / "flickering"

coral lance
#

are you doing the fade first - then the draw?

south patio
#

Doing the fade first

#

Then draw

coral lance
#

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

south patio
coral lance
#

It would be good too see the class definition and any other methods it has

south patio
#

there 🙂

south patio
#

you can see it right?

#

@coral lance

coral lance
#

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

south patio
#

the cylon pattern needs to update right? that is why it is looping

coral lance
#

isn't the pattern just a bar of n LEDs wide? DrawPixels can already do that

south patio
#
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

coral lance
#

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?

south patio
#

i bet there are MANY faults to my code! 🙂

#

but yes the current issue is the flickering on SOLID

coral lance
#

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)

south patio
#

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

south patio
#

well then im off! hosting my birthday party 🙂

south patio
#

@coral lance ?

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.

south patio
#
  1. im trying to retain that ( running a bunch of patterns + adding more in the future )
  2. i want to be able to use all 3 palette modes 🙂
coral lance
#

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:

coral lance
#
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

south patio
#

i can see that! could be something like barLength or something

coral lance
#

right - but only for CYLON

#

presumably other patterns do something else with that?

south patio
#

it is only used for 2 patterns

coral lance
#

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.

south patio
#

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

south patio
#

i think you have seen my code befor right @coral lance ?

#

i updated the github with my entire project

south patio
#

@coral lance could my code be done better? im thinking about how i setup each Strip in controller.h and in main.cpp

south patio
#

i feel like im doing it the "heavy way"

south patio
#

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

south patio
#

but is this fine or can it be done better ?

coral lance
#

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.

south patio
#

this is my Qt App i made to controll the ESP32 🙂

coral lance
#

Aha - had no idea!

#

okay - the App implies one settings per one strip - and no sharing

south patio
#

yupp

coral lance
#

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?

south patio
#

ESP32-WROOM-32UE

coral lance
#

400kB ROM... but still... those templates are buying you nothing - and causing tons of code pain

#

Ah - wish I had seen that UI before!

south patio
#

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]};
coral lance
#

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 -

south patio
#

i've made a new project in platformIO! so im ready to re-write the entire code

coral lance
#

So - each device has 9 strips, each strip has a different length (maybe) -

south patio
#

yea

#

8 strips

coral lance
#

Is that DevieConfig code of the GUI - or is it meant to be shared?

south patio
#

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

coral lance
#

These are all ESP32 - so what have Device_Type?

#

They are all STRIP - why have LED_Type?

south patio
# coral lance These are all ESP32 - so what have `Device_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

south patio
#

im back

south patio
#

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

south patio
#

so put everything from LEDSettings and LEDPattern into 1 class ?

coral lance
#

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

south patio
#

i fail to see how this is going to work if i trash the template LEDController part

south patio
#

you need to explain more on how this is going to work

coral lance
#

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

south patio
#

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);

coral lance
#

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

south patio
#

but this was as far as i got on it

coral lance
#

(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"!)

south patio
#

i never realy tested it or anything

#

yea

coral lance
#

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?

south patio
#

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

south patio
#

@coral lance im home 🙂

south patio
#

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
coral lance
#

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

south patio
#

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
coral lance
#

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?

south patio
#

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
coral lance
#

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

south patio
#

true

#

@coral lance but so far with this code i have no way to actually set "numLeds" via Qt App

coral lance
#

yeah….

#

not sure how that could work…. but for another time

south patio
#
  #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?

coral lance
#

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)

south patio
#

so make a pattern class (that has the update function, draw and so on) and then for each pattern make class cylon : public pattern ?

coral lance
#

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

south patio
#

yea i can see that now

coral lance
#

“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&)

south patio
#

sooo should i make "class palette" aswell?

coral lance
#

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

south patio
#

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?

coral lance
#

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

south patio
#
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

coral lance
#
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;
};
south patio
#

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

coral lance
#

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

south patio
#

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?

coral lance
#

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

south patio
#

so just remove it from the controller?

coral lance
#
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."

south patio
#

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;
};
coral lance
#

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

south patio
#

yea sorry i see it now

coral lance
#

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)

south patio
#

without the include of controller.h i get this

coral lance
#

that's looking nice and minimal!

south patio
#

yes but it says "identifier "LEDController" is undefined" and "identifier "Palette" is undefined"

south patio
#

i need to have

#include "palette.h"
#include "LEDController/controller.h"
south patio
#

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;
};
coral lance
#

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:

#
  1. a wrapper around CLEDController
#
  1. 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"

south patio
#

On my way home from work

south patio
#

back

#

@coral lance ok now that is fixed

#

the code so far compiles fine withour errors

south patio
#

so now i can make a pattern

#

so like this
solidcolor.h

#include "LEDController/pattern.h"

class Solidcolor : public Pattern
{
    virtual void Draw()
    {

    }
};
south patio
#

@coral lance you there m8?

coral lance
#

just got up

#

coffee is brewing

south patio
#

uploaded everything so far to a new git repo

coral lance
#

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

south patio
#

huh?

coral lance
#

src vs. includes directories

#

i’d just have everything in src

#

minor thing… but wondered why you were following that practice

south patio
#

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

coral lance
#

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

south patio
#

oh 😛

south patio
#

i updated the git repo now! 😛 removed everything from include folder and moved it to src 😛

south patio
#

@coral lance brb - need to get some stuff from the shop

#

back

coral lance
#

okay - I have the code

#

so - what's your next step - now that we've thrown it all in the air!

south patio
#

working on setting up the first pattern

#include "pattern.h"

class Solidcolor : public Pattern
{
    virtual void Draw()
    {
        // pattern here
    }

    virtual void Update()
    {
        // update what?
    }
};
coral lance
#

so - a solid color isn't going to - er - update anything, eh?

south patio
#

nothing realy

#

we could do the TheaterChase

coral lance
#

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

south patio
#

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();
};
coral lance
#

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?

south patio
#

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

coral lance
#

Ah, so if you're only using 44 - then CLEDController thinks there are 44

south patio
#

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?

coral lance
#

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()?

south patio
#

yea

coral lance
#

Ah - so why were you keeping your own array of _Leds?

south patio
#

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

coral lance
#

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 -

south patio
#

oh!

coral lance
#
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)

south patio
#

hmm i think the we need a CLEDController param in the function tho

coral lance
#

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

south patio
#

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

coral lance
#

excellent

#

I'm going to go solder for a bit....

south patio
#

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

south patio
#

@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
?

coral lance
#

yes and yes

south patio
#

@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

south patio
#

i updated the git repo btw

south patio
#

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;
        }
south patio
#

you there?

south patio
#

@coral lance ? 😄

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))

south patio
#
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();
}
coral lance
#

see --- the code code Index * 255 /size should be a clue -

#

too much repeated code

south patio
#

huh?

#

wait so _pattern_position += 0.1f; so this should be in the virtual void update() function?

coral lance
#

I don't know .... does every pattern update its position by 0.1 every update cycle?

south patio
#

yea

#

but should it be in the pattern itself or in the class pattern

coral lance
#

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

south patio
#

super version what now? 😐

#

never heard of that

#

there we go

south patio
#

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

coral lance
#

you haven't implemented DrawPixels

#

those messages are from the linker

#

Also - still no idea what PaletteMode is

south patio
#

DrawPixels is implemented

south patio
south patio
#

@coral lance let me know when you have time again to help 🙂 GL on the tour 🙂

south patio
#

daamn i got it working 🙂

#

alltho im unsure about 1 thing

#

but i`ll wait for you to come back 🙂

south patio
#

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

south patio
#

controller.cpp

void LEDController::updatePatternSettings(const PatternSettings& s)
{
  /*
  switch (s.PatternActive)
  {
    case SOLIDCOLOR:  solidcolor(s);  break;

    default: break;
  }
  */
}
south patio
#

keeping it alive

south patio
#

i got all of the patterns up and running 🙂

south patio
#

i've tried to understand and get the updatePatternSettings thing to work but im clueless xD

south patio
#

realy wanna tag you to ask you for the last part xD

#

@coral lance ? (Sorry for tagging) 🥺 😄

coral lance
#

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!

south patio
#

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;
  }
  */
}
coral lance
#

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;
    ...
  }
}
south patio
#

but where would i get the "const PatternSettings& s"

coral lance
#

how are transmitting the pattern settings from your web app to your devices?

south patio
#

huh, come again?

coral lance
#

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?

south patio
#

i connect to each device via WebSocket

coral lance
#

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

south patio
#

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"];
        }
coral lance
#

I see - so the desktop sends over individual parameter changes

#

So...

south patio
#

yes

coral lance
#
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
}
south patio
#

hmmm

coral lance
#

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

south patio
#

so 3 things confusing me here is

  1. 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?

south patio
#

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

south patio
#

you see where im going with this? @coral lance

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

south patio
#

@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();
}
south patio
#

while it works

#

i'm having problems with setting "palette data" :S

south patio
#

weird

#
protected:
  PatternSettings patternSettings;
  PaletteSettings paletteSettings;
#

i have these under class Pattern

south patio
#

also weird

#

palette changing only works for pattern SolidColor

south patio
#

should i make a settings class? @coral lance

class settings : public Pattern
{
public:
  PatternSettings patternSettings;
  PaletteSettings paletteSettings;
};
south patio
#

@coral lance you here m8?