nRF52832 Memory Leak - MotorShield & DC Motors

Please tell us which board you are using.
For CircuitPython issues, ask in the Adafruit CircuitPython forum.

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
sfbob
 
Posts: 14
Joined: Tue Jul 05, 2022 7:56 pm

nRF52832 Memory Leak - MotorShield & DC Motors

Post by sfbob »

I am experiencing consistent, intermittent memory leaks in my sketch.

Hardware stack:
an Adafruit feather nRF52832 and Adafruit Motor Shield connected to four DC motors.

Software stack:
Arduino IDE 1.8.19 running on Ubuntu 20.04 LTS all updates applied, reporting
Board Manager: Adafruit nRF52 ver 1.3.0 with selected board Adafruit Feather nRF52832
Hardware libraries:
Wire 1.0
SPI 1.0
RotaryEncoder 0.8.0
Adafruit_TinuUSB_Arduino 1.7.0
Other Libraries:
Time 1.6.1
Adafruit_Motor_Shield_V2_Library 1.1.0
Adafruit_BusIO 1.12.0

Application:
Sketch is in sub-directory under directory “portable” in the IDE. Code is C++. Code is all functions, no classes. Sketch has muliple .cpp and .h files (too much to post). BLE is not used. Output to Serial Monitor is for console display and debug output for flow, variable values, and memory checks. Output is prepared using snprintf to a single buffer area declared globally. Memory is check based on code at https://github.com/mpflaga/Arduino-MemoryFree referred by https://learn.adafruit.com/memories-of- ... ree-memory .

According to debug output, application is working as expected based on both logic flow and variable values. However there are intermittent memory leaks, but consistently so. One pass through the code will generate a memory leak, the next pass through the same code may not generate a memory leak, then a third pass generates the same leak as the first pass. Code moves motors every minute, memory leak causes code to stop after about 25 minutes (more or less depending on last changes to code).

What I’ve done so far:

Reviewed all the snprintf statements to verify the formatting matches the variable being output.

Moved multiple variables from function scope to global scope to reduce allocating/deallocating memory

Change almost all unsigned integers (regular and long) to signed with exception being those received as output from time functions.

Attempted to #define configUSE_NEWLIB_REENTRANT 1 based on https://nadler.com/embedded/newlibAndFreeRTOS.html - that failed as it was already defined in the hardware directory’s FreeRTOS.h file

Added delay(5) after my Serial.println statements – 5 was arbitrary but I thought SOMETHING might help in case there was something going on in the background I couldn’t see.

One of the most frustrating points in this (mis)adventure was when I changed the code to remove a statement between two memory debug output prints so they were back to back. Upon running there was no memory leak shown on the first memory debug output line and there was a memory leak on the second memory debug output line.

As stated earlier, functionally the code is working as expected based on debug output of flow and variable values. I am an experienced programmer, but this is my first project on an embedded system. I have to believe there is something going on in the background that I’m not familiar or experienced with.

I’m beyond my wit’s end here, and so far tenacity has not been enough.

Can anyone suggest something else to try or somewhere else to look?

User avatar
adafruit_support_mike
 
Posts: 67454
Joined: Thu Feb 11, 2010 2:51 pm

Re: nRF52832 Memory Leak - MotorShield & DC Motors

Post by adafruit_support_mike »

That sounds like you may have a reverse Heisenbug: one that only shows up when you look for bugs. The regular Heisenbug stops existing when you use a debugger. A reverse Heisenbug exists in the debugger and/or debugging code.

Does the system still crash if you remove all the debugging code?

User avatar
sfbob
 
Posts: 14
Joined: Tue Jul 05, 2022 7:56 pm

Re: nRF52832 Memory Leak - MotorShield & DC Motors

Post by sfbob »

Yes system still crashes when I turn off the debugging code.
I don't know where it crashes without the debugging code.
Thought wrapping snprintf formatting strings in F(), but F() type is not what snprintf expects.
Tried wrapping snprintf formatting strings in PSTR(), that does not seem to make a difference.
Still looking for things to try.

User avatar
adafruit_support_mike
 
Posts: 67454
Joined: Thu Feb 11, 2010 2:51 pm

Re: nRF52832 Memory Leak - MotorShield & DC Motors

Post by adafruit_support_mike »

The F() macro only works on AVR 8-bit microcontrollers. For nRF52 microcontrollers, declaring data structures as:

Code: Select all

const String
(or whatever) will move them to program memory.

User avatar
sfbob
 
Posts: 14
Joined: Tue Jul 05, 2022 7:56 pm

Re: nRF52832 Memory Leak - MotorShield & DC Motors

Post by sfbob »

Thanks for the clarification that F() works only on AVR 8-bit microcontrollers.
That's dis appointing to hear, I haven't yet found that information on any recommendation to use it.

In the meantime, I've started work in other directions.
One that seem to be working is moving variable definitions from individual files into the sketch global definitions.
So far it appears that moving one int variable in a function very deep in my call stack to a sketch global definition has reduced the consistent leak, but inconsistently. And the size of the reduction has no relation to any math I can come up with based on the size of an int variable.

Regardless, at least I seem to be making some progress.

User avatar
adafruit_support_mike
 
Posts: 67454
Joined: Thu Feb 11, 2010 2:51 pm

Re: nRF52832 Memory Leak - MotorShield & DC Motors

Post by adafruit_support_mike »

Variables declared in a function should live in the stack. Those are more or less immune to memory leaks because stack frames are deleted each time a function returns.

The problem could be related to excessive stack depth though. Each function call needs a return address, space for its input parameters and return value, etc, which would be unrelated to the size of a single variable. Moving a variable out of the function and into the global space would change the amount of space allocated each time that function is called, which would have an effect on the timing of a memory failure.

If you have the option of running the code with a debugger, do that and see if the code branches in ways you don't expect. If stepping through with a debugger isn't an option, use diagnostic print()s to trace the execution path.

User avatar
DillonBailey
 
Posts: 1
Joined: Mon Aug 01, 2022 6:44 am

Re: nRF52832 Memory Leak - MotorShield & DC Motors

Post by DillonBailey »

Would these steps work for you? I really don't know, but you can give it a shot.

User avatar
sfbob
 
Posts: 14
Joined: Tue Jul 05, 2022 7:56 pm

Re: nRF52832 Memory Leak - MotorShield & DC Motors

Post by sfbob »

I moved almost all variables to global scope out of desperation of something to try. You've both confirmed my original feeling that doing that was not necessary, and my guess is that event made matters a bit worse. I'll put them back in the functions where they originally were.

DillonBailey - thanks for the article reference. It is primarily applicable to writing classes. My code is all functions, albeit in multiple files. Functions used in different files are declared in header files that are included where those functions are referenced.

adafruit_support_mike - in your middle paragraph, I had to read the last sentence several times before I realized that this may be the clue I was looking for. I don't have a debugger for the nRF52832, so I have lots of "diagnostic print()s" in the code (probably way too many). All of them in all the functions in all the files use snprintf to format output into the same globally declared variable

Code: Select all

char global_display_buffer_100[101] 
and, as one might expect, many of the messages are of different lengths. It seems that bringing the snprintf buffer to a locally defined variable within each function is the next step I should try.

P.S. I do have an nRF52840 feather, I got my code to compile and run on it, but the motors do not run. The PIN_#A/B values for the nRF52832 are explicit values and not A0...A7 values so I have to figure out the updated values. Once that is done I can run a hardware debugger on the code. I'm looking forward to that.

User avatar
sfbob
 
Posts: 14
Joined: Tue Jul 05, 2022 7:56 pm

Re: nRF52832 Memory Leak - MotorShield & DC Motors

Post by sfbob »

bad news and good news

I did make the change to make the snprintf buffer local to each function, that did not fix the problem.

Bad news is that did not change memory leaks.

Good news is that due to making all these changes I continued to look elsewhere, specifically at the https://learn.adafruit.com/adafruit-mot ... -reference documentation. When changing motors the code sets up the new motor to retrieve the last values for position etc. It also was running AFMS.begin() each time it changed to a new motor. Documentation clearly states "begin() must be called in setup() to initialize the shield".

I changed the code to run AFMS.begin() only once during setup instead of at each change in what motor was being processed, and memory leaks have been resolved.

Thanks for taking the time to review this and my apologies for taking your time on what turned out to be my not using the library code properly.

Locked
Please be positive and constructive with your questions and comments.

Return to “Feather - Adafruit's lightweight platform”