The latest ESP32 Arduino Core is C++20 (or newer).
Another great choice would be Teensy 4.0 / 4.1. Lots of computing horsepower and lots of Flash / SRAM. Currently at C++17, and (like ESP32) the Standard C++ Library is available.
The latest ESP32 Arduino Core is C++20 (or newer).
Another great choice would be Teensy 4.0 / 4.1. Lots of computing horsepower and lots of Flash / SRAM. Currently at C++17, and (like ESP32) the Standard C++ Library is available.
I don't think you're "saving" any memory there with dynamic allocation. The array is initialized and all three "factories" will be created as soon as you instantiate a PinAnimationFactory object. That means all three factories are always in memory. Likewise, each of the three factories always creates all of it's effects. So all of those are in memory at the same time:
IAnimationFactory* effects[5] = {
(new TwinkleEffectFactory())->setColors(lightRed, brightOrange)->setTiming(250, 2000, 750, 2000)->setFillPct(80),
(new TwoColorFactory())->setColors(CRGB::DimGray, brightGold, brightOrange)->setTiming(100, 1000)->setColorSize(2, true),
(new SparkleAnimationFactory())->setColors(darkGold, CRGB::Gray),
(new TwinkleEffectFactory())->setColors(CRGB::DimGray, brightGold)->setTiming(750, 1000, 750, 100)->setFillPct(80),
(new PaletteWaveFactory())->setSpeed(200)->setPalette(palette)
};
So, why not do it statically?
class PinAnimationFactory : public IAnimationFactory {
private:
ThanksgivingAnimationFactory thanksgiving;
NewYearAnimationFactory newYear;
HalloweenAnimationFactory halloween;
IAnimation* create() {
uint8_t val = (!digitalRead(FIRST_PIN) << 2) | (!digitalRead(SECOND_PIN) << 1) | !digitalRead(THIRD_PIN);
Serial.print("Creating Animation: ");
Serial.println(val);
switch (val) {
case 0:
return &thanksgiving;
break;
case 1:
return &newYear;
break;
case 2:
return &halloween;
break;
default:
return nullptr;
}
}
}
Memory could be saved using dynamic allocation if only one type of animation (Thanksgiving or New Year or Halloween) were instantiated at a time (based on the control input pins). But that's not what OP's code is doing. He has all animations with all effects in memory at the same time.
You could check ESP32-S3, S2 or C3. You can have up to 16MB and they cost about 5€. It has less pins maybe, but you can solve that in different ways.
In my option the Arduino boards, in general, are good for educational purposes or quick prototypes with jumpers, but not for something more permanent. The same for the Arduino IDE, when the code starts to grow.
Of course you can put a rocket in Mars with that, but is not best the best option, in my opinion.
Thanks! This is interesting. I will check that out!
Another great choice would be Teensy 4.0 / 4.1. Lots of computing horsepower and lots of Flash / SRAM. Currently at C++17, and (like ESP32) the Standard C++ Library is available.
Thanks! I will check that out too.
OP is wanting this to all work like a big computer.
Yes, I want to have a finished program that I can upload and change animations without a need to recompile and reupload for every occasion/event.
Memory could be saved using dynamic allocation if only one type of animation (Thanksgiving or New Year or Halloween) were instantiated at a time (based on the control input pins). But that's not what OP's code is doing. He has all animations with all effects in memory at the same time.
Yes, only one animation sequence would be playing at a time. I am aware that all three factories and effect factories are always in memory, which is ok. Some effects and especially cross-fade transitions require additional memory, so I can't create them just yet. I only create factory instances that create individual effects with corresponding configurations. Otherwise I will be holding memory that is not in use.
So, why not do it statically?
Well, that's almost how I had it at first, just using functions not memory references. This wasn't elegant however. Program had too many long switch statements and functions all over the place. In some cases even had several pages of code to scroll, really hated that. Now all of them fit on one page.
Well, I am thinking, I can probably utilize pooling here. I can statically allocate 3 CRGB arrays and simply take one that is not in use. At this point of the project I do not think I need more than 3 arrays at a time. 1 for physical LED strip and 2 "virtual" for transition effects. I do not think I have a big issue with memory fragmentation, so not sure how much memory I would gain but that may actually work if I wanted to avoid dynamically allocating CRGB arrays.
Hi! I am experiencing strange behavior with my sketch. Well, I think I have a hunch but not 100% positive. The question is on delete keyword with dynamic memory allocations. I know dynamic memory allocations is not advisable but my project is not going to be how I want it to be without it. So, with that said question comes down to this: why does my memory usage increase if I just use delete keyword with the rest of the code being the same. Here is the simple sketch:
#include "src\core\free_memory.h" class Test { }; Test* test; void setup() { Serial.begin(115200); // prints 8698 Serial.println(freeMemory()); test = new Test(); // prints 7969 if I use delete below, 8698 if I comment out delete Serial.println(freeMemory()); delete test; } void loop() { }Second print prints 7969 if I use delete and 8698 if I do not. So, ~700 bytes have been used for something.
The reason this happens I think is that when compiler sees 'delete' keyword it automatically allocates that memory. I guess delete needs it for something. I would be grateful if someone can point me in the right direction on this.
Also, would free() function help me to get back that memory?
Thank you all!
EDIT: I am using Arduino Mega 2560 for this project
Going back to my original post though. What is that extra memory used for, specifically? Any way to remediate or optimize that? I am ok with it, just trying to understand. If that's how it needs to be in case I want to use dynamic allocation, so be it.
Because the optimizer notices
testhas no side effects so it's removed. Once removed, everything related is removed which includes the overhead of managing a heap.
I would understand if it would loose some memory used by the program itself or some performance degradation because of the overhead. If it does remove test I definitely do not expect to gain ~700 bytes of memory space just because of that. VMT? But I do not have any virtual calls in my example.
PS: this is only related to my original post with an example
Check what that vague src\core\free_memory.h is about. All that (to us) unknown code can be the culprit; we have no idea.
I gave an example in post #6 including output.
You could check ESP32-S3, S2 or C3
I've just read more about it. This is a game changer for me, and looks exactly what I would use. WLED software is even better than what I wanted to create and its open source... just perfect! ![]()
Check what that vague src\core\free_memory.h is about. All that (to us) unknown code can be the culprit; we have no idea.
This is based on the Adafruit memory article at Measuring Memory Usage | Memories of an Arduino | Adafruit Learning System. Essentially comes down to this I think:
extern char *__brkval;
int freeMemory() {
char top;
return &top - __brkval;
}
I gave an example in post #6 including output.
Yep, I saw it and I understand that concept. However this is not exactly my case.
Below code snippet prints 7969
test = new Test();
Serial.println(freeMemory());
delete test;
however this prints 8698
test = new Test();
Serial.println(freeMemory());
The only difference is the presence of delete statement.
You can have that. You can have that simpler if you forget what you learned about the big computers and treat it like a microcontroller.
What is or is not "elegant" is different in the micro world. Lots of big switch statement is what this sort of code ends up looking like. You can work around that and make the code pretty like you are used to in dot net. But that comes at a cost.
I am not sure I got to the point where I am ready to treat microcontroller as such in this particular case. Probably I'll get to this at some point but for now maybe that will be a cost if I go with this - either to upgrade a board or live with not so "elegant" code. However, I may now give up on this project (or put it aside for now) and start looking into WLED with ESP32
This looks exciting.
Would still be curious to know what happens with the delete statement and memory use if anyone knows exactly, otherwise thank you for all of the help. I appreciate it!
Would still be curious to know what happens with the delete statement and memory use if anyone knows exactly
As pointed out near the beginning of this thread, the answer probably lies deep in the compiler's optimization process. You're unlikely to find the exact cause without digging into the detailed assembly code that's emitted. I'd say you'd probably be better off if you stopped worrying about it and just get on with things using a board with sufficient RAM.
Speaking of which, once you leave the AVR board world, you'll have the Standard C++ Library available. Since you like pointers and dynamic allocation so much, that would be a good time to switch over to smart pointers. They greatly reduce the chance of a memory leak which will surely kill you faster than the few hundred bytes involved with your question about delete.
What is that extra memory used for, specifically?
It's the heap. The typical C / C++ algorithm is to extend the heap towards the stack as needed. From your observations that first extension is ~700 bytes.
Your method for measuring free memory is course-grained. It does not give you an exact accounting of free memory. It gives you the space remaining between the heap and stack; the unreserved space.
You're unlikely to find the exact cause without digging into the detailed assembly code that's emitted. I'd say you'd probably be better off if you stopped worrying about it and just get on with things using a board with sufficient RAM.
Alright, I think I can live with that then. Thanks!
Since you like pointers and dynamic allocation so much, that would be a good time to switch over to smart pointers. They greatly reduce the chance of a memory leak which will surely kill you faster than the few hundred bytes involved with your question about
delete.
Its not that I like pointers so much but I could not do what I wanted to do without them. But I've seen people talking about smart pointers and its something I should look into.
Your method for measuring free memory is course-grained. It does not give you an exact accounting of free memory. It gives you the space remaining between the heap and stack; the unreserved space.
Yes, I understand that it measures memory between stack and heap. But what you are saying is that in this case it just kind of reserves memory for heap but its not necessarily being used until you actually allocate with new or malloc().
On microcontollers we measure "elegance" by how it works behind the scenes, not by how it looks in the editor.
Some people may feel that way. However, I do not feel any tension between good code and performant code. You can do classes and constructors and all that and things can still be very fast.
The real issue with this code to me is not that indirection is used, nor classes, but that it is pointless indirection and pointless heap usage. These type of factory classes are code bloat that used to be advocated by folks who are obsessed with design patterns, but I think even most big-computer programming people have realized that they are a distraction at best these days. You can write something that gives you this same behavior without any allocations at all. Something like:
using FactoryFunction = IAnimation*(*)();
FactoryFunction GetAnimation(int index) {
// The following code will be optimized to a jump table, most likely.
// If called with a constant within the file, it will likely be optimized to
// a direct call.
constexpr auto factory = std::make_array(&MakeThanksgiving, &MakeNewYear,...);
return factory[index];
}
I know dynamic memory allocations is not advisable but my project is not going to be how I want it to be without it.
When running on small RAM hardware it is better to manage buffer space than to use dynamic allocation.
Yeah, I can understand the advice to avoid dynamic allocations if you're working on a system that has only 2kB of RAM. I have only been using ucontrollers with >200kB, so the issues are a little less pressing there. Personally I would not recommend starting a new hobby project on a system with such a small amount of available RAM unless there are particular requirements that something like an ESP32-S3 can't handle.
Certainly one thing that I think holds even in larger programs on full size general purpose computers is to avoid dynamic allocations when they are unnecessary and add nothing to the functioning of the program. And that holds in this case.
used to be advocated by folks who are obsessed with design patterns, but I think even most big-computer programming people have realized that they are a distraction at best these days.
You can include the full OOP there.
Actually patterns are there to contain the problems of OOP; code coupling, wild side effects, testing problems among others.
It looks cool, but except for some things like graphical abstractions, OOP is not the best approach nowadays.
Agreed-ish. I think interfaces are a big advantage of OOP, but the usefulness of complicated type hierarchies is far from clear. They should generally be avoided unless a compelling need has been demonstrated. It is not a coincidence that the interface aspect of OOP can be achieved without memory allocation in microcontroller programs.
I would not recommend starting a new hobby project on a system with such a small amount of available RAM
For most project uses, 2K is plenty of RAM when the code and constants reside in flash. Use a Frankenduino for big projects or connect to a PC.
What you might learn on an Uno is how to do with less!
You might surprise yourself.
Hang an SD on an Uno if you need space.
What you might learn on an Uno is how to do with less!
I have enough trouble getting things done without tying my hands behind my back.
But if using low-power devices is your thing, genuinely go for it, and I'm happy to see the advice about how to use such tools.