-
Notifications
You must be signed in to change notification settings - Fork 67
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uno and ProMicro crashes after config upload once a stepper is defined #127
Comments
I am able to reproduce this locally. I did some digging online and found code that can report the available memory and largest available memory block on an Arduino. I added this to the firmware and made a new command called At initial startup here's the memory report:
I went into the modules dialog and re-uploaded the same config. This is the report:
After that any future uploads result in the crash. |
For comparison here's what my Mega shows at startup when I have a bunch of devices and a stepper configured:
And here's what it looks like after uploading the config again:
|
What I found so far:
Sebastian proposed to make a reset after uploading the config. I think this would be the best way to have always a "clean" memory. |
@neilenns , seems that at your ProMicro 107 Bytes getting "lost". ~150 Bytes are required for each stepper if you declare them statically. Could you send me the link for reporting the used memory? I have also somewhere some code from past days, but I have to search it. |
I have an LCD display as well so that might account for the remaining 107 bytes? I pushed two branches for you: Firmware that adds the kMemory command and desktop app that has a GetMemoryStats() method called on |
That's what I found from the past:
#endif /* MEM_CHECK_H_ */ /*
#include <avr/io.h> // RAMEND // Mask to init SRAM and check against // From linker script unsigned int get_mem_free (void) unsigned short get_mem_unused (void)
} /* !!! never call this function !!! */ I will have a look into your branches, thanks! |
Another option, which could be the most sensible in an embedded environment like the Arduino, would be to get completely rid of dynamic allocation (which is known to be something best avoided). Reserve a fixed buffer, large and fully controllable during compilation, and manage it explicitly; after all, all we need is to either incrementally allocate chunks of memory, or free up everything and start from scratch, therefore management would be almost trivial. |
@GioCC I was thinking of something similar after reading around online a bit. Was thinking of looking for a library that would handle the management, but as you point out the needs of the firmware are pretty simple. |
TBH it's something that I had in my bucket list, but (a) there's already very much going on at the moment (Hooray!!! :D) and (b) I won't claim to be able to work 25/8, even during the holidays... but I'm certainly up for it as soon as I manage to find the time. |
Very strangge things are going on. And now after a couple of hours I found out that MemInfo functions mentioned above from Neil can also cause a crash. |
Seems that I got now a stable version for the Uno. I have to test it for the ProMircro too, but unfortenutely I bricked him during my testing. I have to find some time during the weekend. It was only possible to reduce the RAM by every idea I have in mind and we all habe discussed before. Espacielly reduncing the configBuffer size (only the names from the inputs are now stored) was a main driver, but also some other "small" modifications. Another option would be, to init all possible devices dynamically and issue an reset after uploading the config. Allocation of memory seems to working fine. |
@elral about the multiplexers (or any other device, really) you're right, in fact I had to temporarily disable Steppers in order to have at least enough memory to get them working; and the real offender BTW was the flash space (obviously RAM was on the edge too). |
@GioCC yes, the stepper lib needs quite a lot of RAM and Flash. Even with the RAM saving I have implemented and maybe same additional one (I expect only minor savings futhermore like Neil also stated in issue 136) it could be that more devices do not fit anymore into the RAM/Flash of the Uno/ProMicro. And by the way, I got again a worse finger from scrolling with the mouse through the "big" mobiflight.cpp file. ;) |
Unfortunately I think we're already scraping the bottom of the barrel by just trying to optimize individual devices (which is still welcome anyway, where possible). About the length of the Mobiflight.cpp file... glad to hear this has been brought forward! I had the exactly same feeling, and I would have proposed that too sooner or later... Let's open a discussion, by all means, I'm interested. |
Regarding the configBuffer I fully agree, and I already prepared everthing. Parts of it I used for reducing the size as now only the names of the input devices are stored in the configBuffer. It is an easy next step to get rid of the complete configBuffer. Regarding the dynamic memory allocation I am also not sure how to proceed. Allocating does work, but not freeing the memory. A solution could be to issue a reset after config upload (not sure how to do this for the ProMicro), but then it has to be ensured that not too much memory gets allocated by too much devices. Some kind of calculation has to be done while adding each single device, either in the firmware (preferred) or in the connector. If this is not done the Arduino could crash during startup and even the ProMicro can get bricked. |
Sorry, I realized maybe I created some confusion. My aim is not to use dynamic allocation properly meant. I called it "dynamic" as opposed to "fixed preallocated space for a fixed number of devices". As I mentioned somewhere else, the manager would be almost trivial, since we don't have to face complex mechanisms (allocation is only incremental; deletion is global). Correct object instantiation could be done using A few pointers if anyone should require them: |
Probably not the correct place for this, but I'll just also mention another point: the main drawback of the "allocation manager", and currently my main worry about this, is that the GUI would not be able to determine when the user is running out of space and therefore can no longer allocate devices. Currently, the limit warning is based on the known max device numbers; in order to work in the new situation, the GUI should be aware of the actual size in the firmware of the device objects it's requesting (and obviously the buffer size). Another interesting option could be that the firmware returns the amount of remaining free space after each device configuration is sent; this would allow to show a "fuel gauge" that informs the user progressively during configuration. |
Does somebody know what this warning means and how to avoid this? What I have read this "will" cause problems and not "might" cause problems, but i am not sure. I am also wondering if deleting all classes must be in the opposite order as they are initialized to avoid fragmentation of the memory... |
I believe that boils down to none of the classes in the derivation chain ( |
About the order of But don't just take my word, I haven't factually verified this. |
from a high point of view I understood what you are proposing, but it seems that some deeper knowledge is required. I had a look around regarding I am still unsure how to proceed with this issue. Still "waiting" until this concept is running or to implement an interims solution where all classes are defined statically and to reduce the memory as much as possible. |
Ohmm, #include does help ;) /edit 2: And now it works with 2 LCD's :) Checking if still memory is available needs to be implemented but seems that it could be done! |
@elral Not sure to what part(s) you are referring to exactly, but I have the feeling it's something very promising! :D |
@GioCC , I took your execellent proposol regarding Next I declared LCD, 7Segments, Servo and Stepper dynamically while using this new static buffer. And what should I say, the first tests are showing that everything is working fine. I can upload mulitiple times a config on the Uno without crashing him. Unfortenutely I have no devices connected, but I made the same test on the Mega where I have most devices connected. And all are working. The part what is missing is, that before initialisation of a device it has to be checked if sufficient memory in the static buffer is available. But this should be now an easy task. allocateMem.cpp: allocateMem.h: Same for the other devices which are initialized dynamically. I am pretty sure that some optimazation and tests are required, but in principle it seems to work. |
That's great! As I mentioned, it looks pretty straightforward (which means much less chance of bugs or issues), and yet very effective. I'm looking forward to the significant improvements that can be achieved! Bravo Ralf ;) |
Thanks! |
Sounds super cool! Congrats guys for making these first steps - i was really worried in the beginning when i first read about it. That would be very very cool |
Exactly! And I think it can be rather groundbreaking. Think e.g. of getting rid of LCD, Servos and Steppers in one go for a board that does not use them. How much more space you can gain for other stuff (or the opposite, of course)! |
@DocMoebiuz You can trust us... we mean no harm ;) |
This is fascinating, I had no idea "placement new" was a thing! |
I meant i was worried it might become clunky and cumbersome but it looks pretty straight forward and not overly complicated. |
now that you say it explicitly, i am starting to get worried 😂 |
Sorry I meant...: Resistance is futile. 😂😂😂 |
Some more tests shows clearly, that we have to save RAM where ever we can for the Uno and ProMicro. For the Uno I ended up with a configBuffer of 50Bytes (only Input names will be saved) and a "DeviceBuffer" of 120 Bytes to get the Uno stable (multiple config loads w/o crash). Compiling with these sizes results in 1.898 Bytes from 2048 Bytes. If the buffersize will be increased, the first startup still works. But a config upload results in a crash, I think the heap and stack gets overlayed. Looking how a board reset is done, a couple of functions call other functions. And in the end "only" 150 Bytes are left.
The next step will be to setup a new clean branch with all the changes and some more testing. |
I did some more changes, all devices are now initialized dynamically. This will save some more memory which can be used for the new "deviceBuffer" and the ConfigBuffer. This also adapted now for the ProMicro and Uno.
The Uno runs stable with a deviceBuffer of 250Byte and for the ProMicro is a deviceBuffer of 500 Byte defined, but for now untested. These buffersizes can be used for the device (size see above) until the buffer is full. But it would be really nice if somebodey could test it too. In this case i can provide the link to my branch. The branch is not cleaned up and needs some more checks, but runs stable. Debug Info's are transferred to the Connector and will show up in the lower terminal window (I can also provide this branch). |
that's great! have you done the math to check whether the currently defined number of devices for the boards would fit into these buffers? |
I can't promise about how much testing I will be able to do these days, but I would appreciate if you could send me the link to the code to have a go. |
Oh je, All devices (incl. InputShifter) together need (size of deviceBuffer): The branch is not cleared up and has a lot of uncommented debug prints and a lot of changes to reduce memory size which I guess will need some explanations. But I have less time in the moment as I will start my vacation today. |
Thanks, I'm looking forward to having a good look at it. In the meantime, enjoy your holidays! |
A random thought: you could save some more bytes by using the F() macro in the debug prints ( |
This issue might be related to issue #140. |
While working on this issue and creating the pull request, I also touched the part of uploading the config (already changed to write directly to the EEPROM).
The last step I don't really understand. There are two functions available, kActivateConfig (16) and kResetBoard (24). With additional debug prints to the connector it seems, that both functions are called, but I can't figure out in which order (debug prints are sometimes mixed in the timeline and this happens here). I wonder if really both functions are send from the UI and if so, what is the reason? From what I can see in the firmware it should be enough to issue an kActivateConfig (16) which calls the function OnActivateConfig(), kResetBoard (24) is for my understanding not required. Can someone point me to the file from the UI where the config upload is programed? Or does someone know which functions are called? |
If I haven't misread what you need, the config upload is made in
|
When I was working on my custom firmware that fakes out MobiFlight and generates its config on the fly here's the flow I traced when saving a config from the desktop:
|
@elral Really looks like a leftover from a previously used (or different) sequence... in this procedure it actually seems redundant, I imagine as a command (i.e. besides obviously on boot) it could make sense if the config buffer on RAM was somehow thought to be corrupted - but even in that case, not by an upload error, otherwise the EEPROM would have been likely corrupted too. |
Once a stepper is defined the Uno and ProMicro crashes after the next or overnext (depends on how many additional devices are defined) config upload. On the Mega it needs ~16 uploads w/o reset in between, so it will be very seldom.
To reproduce it on the ProMicro/Uno, define a stepper and e.g. a button. Upload this config and add an additional device. Upload this config again. Does the uplaod end w/o error, at least the next coming upload will not be successfull.
The reason is, steppers are initialized dynamically. During config upload all devices are resetted and for dynamically initialized devices the used memory (RAM) is freed. Unfortenutley this does not really work like we also saw for the 7Segements before.
Initializing the steppers statically will increase the required memory up to ~98%, but it seems to work stable until you define one LCD. The LCD device is also intialized dynamically so we are running into the same problem. The remaining RAM might not be sufficient to initialize the LCD statically, but needs some more tests.
The only solutions I can imagine for now is to free up the required RAM by e.g. dispensing the config buffer, reduce the number of devices or reduce the size of the config buffer.
Dispensing the config buffer would mean some (bigger?) changes on the connector side, but is the most effective for freeing up the RAM.
Any other proposels are welcome.
The text was updated successfully, but these errors were encountered: