Pages: [1] 2   Go Down
Author Topic: Program corruption? Inconsistent and odd failures mid-program  (Read 2306 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 25
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have a moderately complex program driving an LCD (Using the GLCD library, v3)and a few basic peripheral devices via shift registers, running on a Nano. up until yesterday everything had been working perfectly. The setup is pretty straightforward, a few LCD menus and various operations on the peripherals. Yesterday during testing however I found a strange issue, on the main run, if repeated 24 times without restarting the board, one of various strange bugs will occur:

Everything will freeze, on-screen timer shows up but doesn't count, no input will be accepted.
Certain text elements will corrupt, and instead fill the entire character area with full-block characters, no input will be accepted.
Timer will freeze, input will still be accepted but certain portions of the screen will never show text again (Possibly related to textareas with the GLCD library, I'm still trying to determine if this is the case)
Timer will fail to count and timer text will corrupt, showing only full-blocks where numbers should be. Input still works.
The program will entirely reset, and return to its splash screen.

With the exception of the last on that list, neither reseting the board manually nor power cycling it will fix anything. The programs will either fail to work at all or one of many various LCD display bugs will occur. The only way to fix any of them is to upload the program again.


This is all being done via the Arduino IDE, and everything else seems to be working perfectly fine. This is definitely stranger than anything I have encountered, and despite scouring the code I cannot find anything that would cause these bugs. I'm not even convinced that it's the code causing the issue, though, seeing as the bug only pops up after 24 perfectly normal and successful operations. The only thing I can think of at this point is that the code is somehow getting corrupted, but I don't know how that would happen nor how to fix it if it were.

Due to confidentiality reasons I can't post the entire program, but here is the portion where the bug occurs:

Code:
void startrun() { ///

int sec = 0;
int min = 0;
int totalruntime;
int flowcheckint = 500;
int clocktime;
long flowcheckprev = 500;
boolean flow;
long flowtimerprevious = millis();
long runtime = millis();
shiftwrite(pump, HIGH);
Title.CursorToXY(34, 4);
Title.print("Running...");

// Check for button press, if button is pressed, end run.
while(1) {
buttons();
if (select == 1) {
shiftwrite(pump, LOW);
totalruntime = ((millis() - runtime) / 1000);
runminutes = totalruntime / 60;
runseconds = totalruntime % 60;
GLCD.ClearScreen();
runcanceledscreen();
}


// Clock
gText ClockArea;
ClockArea.DefineArea(20, 0, 6, 1, fixednums15x31);
ClockArea.CursorToXY(2,20);
int lastsec;
clocktime = (millis() - runtime) / 1000;
sec = clocktime % 60;
min = clocktime / 60;
if(sec != lastsec) {
ClockArea.Printf("%02d:%02d", min, sec);
lastsec = sec;
}


// Flow buffer check
if (flow == 1 && millis() - flowtimerprevious >= flowbuff && millis() - runtime >= 20000) {
break;}

// Check sensor, print debug, set sensor buffer timer
if (millis() - flowcheckprev >= flowcheckint) {
flow = flowcheck();
flowcheckprev = millis();
if (flow == 0) {
flowtimerprevious = millis();
}
}
}


totalruntime = ((millis() - runtime) / 1000);
runminutes = totalruntime / 60;
runseconds = totalruntime % 60;
shiftwrite(pump, LOW);
GLCD.ClearScreen();
runcompletescreen();}
//\

One idea that I had, but am unsure how to test, is that I may be overfilling the RAM. Is there any way to check that? The sketch size as it currently is is 21KB.

Any ideas?
« Last Edit: July 21, 2011, 11:09:22 am by thehobojoe » Logged

Fort Lauderdale, FL
Offline Offline
Faraday Member
**
Karma: 71
Posts: 6144
Baldengineer
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Due to confidentiality reasons I can't post the entire program, but here is the portion where the bug occurs:
Then your help will be limited.  While it appears the problem occurs here, the fault site isn't always the problem.

Quote
One idea that I had, but am unsure how to test, is that I may be overfilling the RAM. Is there any way to check that? The sketch size as it currently is is 21KB.
The size of the sketch really does not relate to the RAM usage.  An analogy for a PC:  How does used hard drive space correlate to free RAM?  Hint:  It doesn't.

If you search the forum there are some crude methods that can help determine how much RAM is being used.  However, these methods are not perfect.  In general, if you are using strings then you are probably exhausting the RAM.  Constants (including String Constants) are copied to RAM before use.  This is a detail many people miss.

Move all of your strings to PROGMEM.  The sketch size will stay about the same but your RAM usage will drop significantly.

http://www.arduino.cc/en/Reference/PROGMEM
Logged

Capacitor Expert By Day, Enginerd by night.  ||  Personal Blog: www.baldengineer.com  || Electronics Tutorials for Beginners:  www.addohms.c

Offline Offline
Newbie
*
Karma: 0
Posts: 25
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have quite a few string print operations, but it was my understanding that the RAM they used was quickly dropped once they were written. Is there any way to have them go straight to flash and bypass the ram if they aren't declared as variables? i.e. I have many strings printed via "GLCD.print("blah blah");". Does this consume RAM, and if so, is there any way to force it to go to flash without separately declaring the string as a variable?

Does using 'const' use RAM? Should I replace all my const variables with #define?
« Last Edit: July 21, 2011, 01:39:23 pm by thehobojoe » Logged

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 310
Posts: 26620
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
I have many strings printed via "GLCD.print("blah blah");". Does this consume RAM
Yes.
Take a look at PROGMEM
Logged

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Fort Lauderdale, FL
Offline Offline
Faraday Member
**
Karma: 71
Posts: 6144
Baldengineer
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Does using 'const' use RAM? Should I replace all my const variables with #define?
These both use RAM.  You have to look at the PROGMEM link I already posted. 

Quote
my understanding that the RAM they used was quickly dropped once they were written
Based on what?  If there were a memory manager, maybe that would be the case.

The compiler takes constants and copies them to RAM.  That way functions can access those constants.  So methods like "const" (which just makes the complier check to see if you are modifying a variable) or #define (which is a macro that gets replaced at compile time) are effectively the same as using "Blah... blah... blah...". 

The compiler doesn't really have a way to determine when a constant is or is not needed, so it cannot free the memory "automatically."
« Last Edit: July 21, 2011, 02:34:42 pm by James C4S » Logged

Capacitor Expert By Day, Enginerd by night.  ||  Personal Blog: www.baldengineer.com  || Electronics Tutorials for Beginners:  www.addohms.c

Offline Offline
Newbie
*
Karma: 0
Posts: 25
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks for the replies, I'll definitely be looking deeper into PROGMEM. However, based on what I can see from a quick review it looks like it would require very significant rewrite of large portions of my code, so before I take the time to do that I want to be sure that's the source of the problem. Since I'm able to run the program without a single hitch 23 times, until it breaks on the 24th run, could it actually be RAM overflow that is the source? Could there be some accidental memory leak or some other rouge memory hog pulling a little more RAM after each run?

Sorry if these seem like stupid questions, I'm just starting to get a handle of how memory allocation works.
Logged

Global Moderator
Netherlands
Online Online
Shannon Member
*****
Karma: 227
Posts: 14009
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


Is the crash after 24 "steps" repeatable?
It could be that you allocate memory that is not freed again properly (could also be a bug in the libraries BTW)

- http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1213583720 - describes how to test freeRAM  Post #18 (page2)

Quote
The compiler doesn't really have a way to determine when a constant is or is not needed, so it cannot free the memory "automatically."
unless the constant is declared local in a function, that is the only exception I know off
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 310
Posts: 26620
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
it looks like it would require very significant rewrite of large portions of my code,
That shouldn't be the case - the place the strings are output would be about the only place you need to rewrite - the rest is not a "rewrite", just text substitution - the editor and/or the preprocessor can help a great deal here.
Logged

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Offline Offline
Newbie
*
Karma: 0
Posts: 25
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


Is the crash after 24 "steps" repeatable?
It could be that you allocate memory that is not freed again properly (could also be a bug in the libraries BTW)

- http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1213583720 - describes how to test freeRAM  Post #18 (page2)

Quote
The compiler doesn't really have a way to determine when a constant is or is not needed, so it cannot free the memory "automatically."
unless the constant is declared local in a function, that is the only exception I know off

Yes, it's repeatable, it happens on the 24th repetition consistently. Thanks for the link.

Quote
That shouldn't be the case - the place the strings are output would be about the only place you need to rewrite - the rest is not a "rewrite", just text substitution - the editor and/or the preprocessor can help a great deal here.

Well, a major portion of the code is exclusively dealing with an LCD interface, and as you can imagine there are a lot of strings involved with that. It's not insurmountable by any means, it would just require a lot of reworking due to the large number of strings.
Logged

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 310
Posts: 26620
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Strings are still strings whether they're in RAM or progmem - the only things that are different is how you declare them and how you access them.
A few macros could do most of your work for you.
Logged

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Offline Offline
Newbie
*
Karma: 0
Posts: 25
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ok, thanks to the MemoryFree library posted here I have been able to narrow down the problem significantly. During normal run, the program has more than enough free memory, over a KB, but more gets consumed after each repetition of the main run. So, in light of that I have a couple dumb questions. If the same function that has multiple strings gets called multiple times, would they consume more RAM on each pass or simply reuse old RAM?

Also, I have looked further into PROGMEM, and you're right AWOL, it shouldn't take a huge amount of work to implement it, I just didn't fully understand it before. Still, I want to solve this RAM issue, both for the purposes of this program and to understand what I did wrong and how to fix it later. In light of this, I'm going to post the whole thing and hope that you all can see something that I don't.

The most relevant portions are here, the function names should make the process fairly self-explanatory:

Code:
void startmenu() {
menusize = 2;
GLCD.DrawRoundRect(0, 0, 126, 20, 5);
Title.CursorToXY(28,4);
Title.print("Main Menu");
ListMenu.CursorTo(3,4); ListMenu.print("Start Run");
ListMenu.CursorTo(3,5); ListMenu.print("Options");
GLCD.FillRect(0,22,10,50,WHITE);
if (cursor == 1) {
ListMenu.CursorTo(0,4);}
else {
ListMenu.CursorTo(0,5);}
ListMenu.print("->");

while (1) {
buttons();
if (down == 1) {
scrolldown();
startmenu();}
if (up == 1) {
scrollup();
startmenu();}
if (select == 1) {
GLCD.ClearScreen();
switch (cursor) {
case 1: startrun();
case 2:
cursor = 1;
menupos = 1;
optionsmenu();}}
if (bitRead(pinvalues, butdown) == 1 && bitRead(pinvalues, butup) == 1) {
cursor = 1;
menupos = 1;
engmenu();}
}
}


void startrun() {

// Start run variable, set timers, turn on pump
uint8_t sec = 0;
uint8_t min = 0;
int totalruntime;
int flowcheckint = 500;
int clocktime;
long flowcheckprev = 500;
uint8_t flow;
long flowtimerprevious = millis();
long runtime = millis();
shiftwrite(pump, HIGH);
Title.CursorToXY(34, 4);
Title.print("Running...");

// Check for button press, if button is pressed, end run.
while(1) {
buttons();
if (select == 1) {
shiftwrite(pump, LOW);
totalruntime = ((millis() - runtime) / 1000);
runminutes = totalruntime / 60;
runseconds = totalruntime % 60;
GLCD.ClearScreen();
runcanceledscreen();
}


// Clock
gText ClockArea;
ClockArea.DefineArea(20, 0, 6, 1, fixednums15x31);
ClockArea.CursorToXY(2,20);
uint8_t lastsec;
clocktime = (millis() - runtime) / 1000;
sec = clocktime % 60;
min = clocktime / 60;
if(sec != lastsec) {
ClockArea.Printf("%02d:%02d", min, sec);
lastsec = sec;
}


// Flow buffer check
if (flow == 1 && millis() - flowtimerprevious >= flowbuff && millis() - runtime >= 20000) {
break;}

// Check sensor, print debug, set sensor buffer timer
if (millis() - flowcheckprev >= flowcheckint) {
flow = flowcheck();
flowcheckprev = millis();
if (flow == 0) {
flowtimerprevious = millis();
}
}
}
totalruntime = ((millis() - runtime) / 1000);
runminutes = totalruntime / 60;
runseconds = totalruntime % 60;
shiftwrite(pump, LOW);
GLCD.ClearScreen();
runcompletescreen();
}

void runcanceledscreen() {
menusize = 2;
GLCD.DrawRoundRect(0, 0, 126, 20, 5);
Title.CursorToXY(19,4);
Title.print("Run Canceled");
ListMenu.CursorTo(3,4); ListMenu.print("Run time: ");
ListMenu.Printf("%02d:%02d", runminutes, runseconds);
ListMenu.CursorTo(3,5); ListMenu.print("Extract");
ListMenu.CursorTo(3,6); ListMenu.print("Home");
GLCD.FillRect(0,22,10,50,WHITE);
if (cursor == 1) {
ListMenu.CursorTo(0,5);}
else {
ListMenu.CursorTo(0,6);}
ListMenu.print("->");
while (1) {
buttons();
if (up == 1) {
scrollup();
runcanceledscreen();}
if (down == 1) {
scrolldown();
runcanceledscreen();}
if (select == 1) {
GLCD.ClearScreen();
switch (cursor) {
case 1: extract();
case 2: cursor = 1; startmenu();
}
}
}
}


And the entire program is here, if the problem doesn't seem to be contained in the above functions: http://pastebin.com/HXCdchcr

I'm fairly new to coding, so you'll probably find a hundred and one glaring mistakes. Even if they're unrelated, I'd love to hear any advice, I wanna make my stuff as watertight as possible. In the meantime, I'll work on implementing PROGMEM, even though I'm not sure it's entirely necessary at this point. It'll be a good learning experience.  smiley

Thanks a lot for all your help, everyone.
Logged

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 310
Posts: 26620
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I can't see anything obvious, but one thing I'd say is you've got a lot of comments like this:
Code:
597.} //\

Be very, very careful with backslashes in C++ comments.
Backslash is a continuation character, so you think you're writing something like
Code:
// this is my comment \
#define PIN1 12
What the compiler sees is
Code:
// this is my comment #define PIN1 12

Get out of the habit now, before it causes you hair-loss.
Logged

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Offline Offline
Newbie
*
Karma: 0
Posts: 25
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks for the heads up, I was using that for a custom folder close syntax in n++, guess it was a bad choice of characters.  smiley-red


On the subject of PROGMEM, I'm wondering if there's any more straightforward way than string tables, specifically any inline method. Right now I'm pretty much copying posted methods verbatim:

Code:
prog_char options_0[] PROGMEM = "Start extract";
prog_char options_1[] PROGMEM = "Ext. fluid prime";
prog_char options_2[] PROGMEM = "Decon cycle";
prog_char options_3[] PROGMEM = "Rinse Cycle";
prog_char options_4[] PROGMEM = "Ext. size";
prog_char options_5[] PROGMEM = "Beep vol.";
prog_char options_6[] PROGMEM = "Return";

PROGMEM const char *options_table[] = {   
options_0,
options_1,
options_2,
options_3,
options_4,
options_5,
options_6, };

char* readoptions(uint8_t whichstring) {
strcpy_P(buffer, (char*)pgm_read_word(&(options_table[whichstring])));
return buffer;
}


blah blah.print(readoptions(3));

This works fine, it's just a little laborious, and hard to keep track of when you have a lot of unrelated strings.
« Last Edit: July 22, 2011, 12:20:58 pm by thehobojoe » Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 25
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I just did a more comprehensive memory test, something is definitely going wrong. Every single print action(even moving the cursor), repeated or otherwise, consumes more RAM that it never frees up again. I'm by no means an expert on this in any way, but surely that's not the way memory allocation works normally?

This means that if I have ANY strings printing in the entire program(that aren't PROGMEM), they will eventually consume all the RAM if repeated enough times. That's a pretty big problem, but at this point I don't think it's my code that's at fault. Anyone have any ideas for how to fix this? I can migrate all my strings to PROGMEM, but that's not an ideal solution, especially since it obfuscates the code even more, and I'd still like to be able to know how to fix things like this.
Logged

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 310
Posts: 26620
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
his means that if I have ANY strings printing in the entire program(that aren't PROGMEM), they will eventually consume all the RAM if repeated enough times
Well, since this doesn't normally happen, it's your problem.
You're doing something wrong.
Logged

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Pages: [1] 2   Go Up
Jump to: