Since I'm very used to object oriented programming, I would like to use this into my design. My application will have 4 led strips and each led strip will have a 'pattern' running on it. For this I like to create a base class and for each pattern a derived class. So this means I have 4 instances to a pattern. However, the pattern can change dynamically (based on music MIDI input). Each pattern has between 10-16 parameters.
I know dynamic memory allocation is mostly not a good idea in an Arduino. I'm using a Mega which has 8 KB. I found several 'solutions' but neither one I'm sure about. The first is not the solution but the way I should do it when I have 'plenty' of SRAM available (and simplified):
class Pattern
{
public:
virtual void Process();
}
class PatternA: Pattern
{
public:
void Process();
// In example I only use 2 out of 10-16 parameters
uint8_t SetColor(uint8_t colorIndex);
uint8_t SetSpeed(uint8_t speed);
private:
uint8_t _colorIndex;
uint8_t _speed;
}
// In this example I only use 2 out of 24 patterns
class PatternB: Pattern
(like PatternA but with its own implementation)
class LedStrip
{
public:
void SetPattern(Pattern* pattern);
private:
Pattern* _pattern;
}
Other derived classes may have other (named) parameters.
When changing instances, I would get something like this:
LedStrip::SetPattern(Pattern* pattern)
{
delete _pattern;
_pattern = pattern;
}
pattern* pattern = new PatternA(...);
ledStrip1.SetPattern(pattern);
However, since the new pattern may use more memory then the deleted pattern, there will be a memory gap. So this is not suitable for the Arduino to be used I assume.
Solutions:
1. Create all possible patterns beforehand.
Assume I would implement 24 patterns, and create any possible combination, I would have 4 (led strips) * 24 (patterns) * 13.5 (average number of parameters) = 1,296 bytes. This seems a bit much as only 4 are 'active'.
class LedStrip
{
public:
enum EPattern {A, B}
void SetPattern(EPattern pattern);
private:
PatternA* _patternA;
PatternB* _patternB;
Pattern* _pattern;
}
To change pattern:
void LedStrip::SetPattern(EPatternpattern)
{
case EPatternA: _pattern = _patternA; break;
case EPatternB: _pattern = _patternB; break;
}
ledStrip1.SetPattern(EPattern::A);
Of course I could also pass a pattern where the patterns are created globally but this is does not matter for this example. The idea is to create 4 (led strips) * 24 (patterns) instances beforehand and never use delete
. This means out of the 4 * 24 patterns, only 4 are used, 1 per led strip.
Of course this causes a lot of unused memory.
2. New/Delete
I create the 4 correct derived instances and when one pattern (instance) change, I delete the instance and create the new. When I put no (instance) variables in the derived class, this could work, but I have some specific questions regarding this solution:
I make sure all the max. parameters are in the base class and I 'cast' 4 instances of the base class to a derived class. This seems a bit of 'tricky' solution.
class Pattern
{
public:
SetParameter(uint8_t parameter, uint8_t parameterValue);
uint8_t parameter GetParameter(uint8_t parameter);
private:
uint8_t _parameters[16];
}
class PatternA: Pattern
... (does not have any variables)
To change pattern:
delete ledStrip1.pattern;
ledStrip1.pattern = new PatternA();
Question: can I assume the deleted memory of the old pattern will be replaced by the new pattern as the memory usage is equal?
The downside of this way is that each pattern will have the worst-case amount of parameters in each instance, but this is not a problem (I only have 4 led strips).