Very strange problem (caused by memory owerflow)

Hi,

this is the first time I write something in this forum. I'm using the mignon game kit, which is based on arduino.

I am writing a little game. The code has become very big so far. Now I got a very strange problem:

In the middle of my program I use an if-clause on the variable CurLvl:

void Menu() {
  int CurLvl = 1;
  Play_Next_Melody_Tone();
  //Start the Game
  if ( (gamekit.button_pressed(butt_FUNCA)) || (gamekit.button_pressed(butt_FUNCB)) ) {
    if (CurLvl==1) {    
      Load_Level_1_1();  Load_Level_1_2();   
    }
    if (CurLvl==2) {    
      Load_Level_2_1();  Load_Level_2_2();   
    }
  .
  .
  .

So fare it makes no sense, since the variable CurLvl is created right in front of the if-clause. I am just showing this code because it is the last point that works.

Next step: The variable CurLvl must be accessed in other voids, so I want to create it as public. I do this by creating it in front of the setup-void and after the #include's.

int CurLvl = 1;

  .
  .
  .

void Menu() {
  Play_Next_Melody_Tone();
  //Start the Game
  if ( (gamekit.button_pressed(butt_FUNCA)) || (gamekit.button_pressed(butt_FUNCB)) ) {
    if (CurLvl==1) {    
      Load_Level_1_1();  Load_Level_1_2();   
    }
    if (CurLvl==2) {    
      Load_Level_2_1();  Load_Level_2_2();   
    }
  .
  .
  .

Now the program start, draws some pixel and freezes. The variable CurLvl is excessed nowhere else in the program so it should definitely have a value of 1 when the if-clauses are reached. Checking it's value with Serial.print(CurLvl); shows that it is indeed 1.

I'm trying very long now to get around this, but I continuously fail. :cry: I'm going almost crazy :o .

Is there any difference between the creation of a variable in the function and as a public one. Shouldn't both of my versions give the same results?

Thank you very much,
MegaMatze

The homepage of mignon game kit: www.mignongamekit.com

The complete code:

#include <gamekit.h>

long denominator = 172373;
long absolute = 572665;
long multiplier = 1146;
long summand = 568781;

//Tetris melody
int melody[112][2] = {{2,17},{1,12},{1,13},{2,15},{1,13},{1,12} ,{2,10},{1,10},{1,13},{2,17},{1,15},{1,13}, {3,12},{1,13},{2,15},{2,17} ,{2,13},{2,10},{2,10},{2,10} ,{1,0},{2,15},{1,18},{2,22},{1,20},{1,18} ,{3,17},{1,13},{2,17},{1,15},{1,13} ,{2,12},{1,12},{1,13},{2,15},{2,17} ,{2,13},{2,10},{2,10},{2,0}, {2,17},{1,12},{1,13},{2,15},{1,13},{1,12} ,{2,10},{1,10},{1,13},{2,17},{1,15},{1,13}, {3,12},{1,13},{2,15},{2,17} ,{2,13},{2,10},{2,10},{2,0} ,{1,0},{2,15},{1,18},{2,22},{1,20},{1,18} ,{3,17},{1,13},{2,17},{1,15},{1,13} ,{2,12},{1,12},{1,13},{2,15},{2,17} ,{2,13},{2,10},{2,10},{2,0} ,{2,5},{2,5},{2,1},{2,1} ,{2,3},{2,3},{2,-1},{2,-1} ,{2,1},{2,1},{2,-3},{2,-3}, {2,-4},{2,-4},{2,-1},{2,0}, {2,5},{2,5},{2,1},{2,1}, {2,3},{2,3},{2,-1},{2,-1}, {2,1},{2,5},{2,10},{2,10}, {2,9},{2,9},{2,0},{2,0} };
int melody_length = 111;
int melody_position = 0;
int melody_speed = 180;
int sound_volume = 1;

long Timer = 0;
int GamePhase = 1; //1=Selection, 2=Falling Sequence, 3=Death
int Difficulty = 0; //0=easy 3=Hard
int Millis_per_Step = 1200;
int Foods_per_Grow = 1;
int Snake_Length_To_Win = 10;

int Field[10][14];

int pos_x = 1; //Position of upper left pixel
int pos_y = 1;

// {y,x,a} a=0 -> no snake a=10 -> snake
int Snake[30][3];
int Head = 0;
int Ass = 1; //The last snake field at the end
int Direct = 2; //Snake walks (1)south, (2)east, (3)north, (4)west
int Eaten_Food = 0;
int Snake_Length = 2;

int CurLvl = 1;

void setup(){
gamekit.Begin();
Serial.begin(9600);
}

//---------------------------------------------------
// Random Number Generator
//---------------------------------------------------
double generate_rand_double() {
absolute = (multiplierabsolute+summand+(10gamekit.get_systemcounter()%denominator) )%denominator;
return float(absolute)/denominator;
}

//---------------------------------------------------
// Melody
//---------------------------------------------------
void Load_Melody(int No_Of_Melody) {
switch (No_Of_Melody) {
case 1:
break;
case 2:
//do something when var equals 2
break;
}
}
void Play_Next_Melody_Tone() {
play_tone(melody[melody_position][1], melody[melody_position][0]*melody_speed, sound_volume);
melody_position++;
if (melody_position>melody_length) {
melody_position=0;
}
}
void play_frequency(int frequency_, int duration_, int loudness_){
//loudness: 0=no sound, 1=silent, 2=loud
if (loudness_==0) {
//do nothing but delay
delay(duration_);
} else {
if (loudness_==1) {
gamekit.play_tone(frequency_, duration_, 0);
} else {
gamekit.play_tone(frequency_, duration_, 1);
}
}
}
void play_tone(int distance_to_C_, int duration_, int loudness_){
if (distance_to_C_!=0) {
play_frequency(246 * pow(float(2), float(distance_to_C_)/12.0), duration_, loudness_);
} else {
play_frequency(264, duration_, 0);
}
}

void Do_Background_Stuff() {
Play_Next_Melody_Tone();
Read_Keys();
}

//---------------------------------------------------
// The Game
//---------------------------------------------------
void Clear_Screen() {
for (int Counter_y=0; Counter_y<=4; Counter_y++) {
for (int Counter_x=0; Counter_x<=7; Counter_x++) {
gamekit.set_pixel(Counter_y, Counter_x, 0);
}
}
}

void Load_Level_1_1() {
int Field_Level_1[10][14] = { {15,15,15,15,15,15,15,15,15,15,15,15,15,15} , // 0=free 15=wall 17=food
{15,0,0,0,0,0,0,0,0,0,0,0,0,15} ,
{15,0,0,0,0,0,0,17,0,0,0,0,0,15} ,
{15,0,0,0,0,0,0,0,0,0,0,0,0,15} ,
{15,0,0,0,0,0,15,15,0,0,0,0,0,15} ,
{15,0,0,0,0,0,15,15,0,0,0,0,0,15} ,
{15,0,0,17,0,0,0,0,0,17,0,0,0,15} ,
{15,0,0,0,0,0,0,0,0,0,0,0,0,15} ,
{15,0,0,0,0,0,0,0,0,0,0,0,0,15} ,
{15,15,15,15,15,15,15,15,15,15,15,15,15,15} };
for (int Counter_y=0; Counter_y<10; Counter_y++) {
for (int Counter_x=0; Counter_x<14; Counter_x++) {
Field[Counter_y][Counter_x] = Field_Level_1[Counter_y][Counter_x];
}
}
}
void Load_Level_1_2() {
int Snake_Level_1[30][3] = { {3,3,5}, {3,2,5}, {3,1,0}, {2,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0}};
for (int Counter_y=0; Counter_y<30; Counter_y++) {
for (int Counter_x=0; Counter_x<3; Counter_x++) {
Snake[Counter_y][Counter_x] = Snake_Level_1[Counter_y][Counter_x];
}
}
pos_x = 1; //Position of upper left pixel
pos_y = 1;
Head = 0;
Ass = 1; //The last snake field at the end
Direct = 2; //Snake walks (1)south, (2)east, (3)north, (4)west
Eaten_Food = 0;
Snake_Length = 2;
}
void Load_Level_2_1() {
int Field_Level_1[10][14] = { {15,15,15,15,15,15,15,15,15,15,15,15,15,15} , // 0=free 15=wall 17=food
{15,0,0,0,0,0,0,0,0,0,0,0,0,15} ,
{15,0,0,0,0,0,0,17,0,0,0,0,0,15} ,
{15,0,0,0,0,0,0,0,0,0,0,0,0,15} ,
{15,0,0,0,15,15,0,0,15,15,0,0,0,15} ,
{15,0,0,0,15,15,0,0,15,15,0,0,0,15} ,
{15,0,0,17,0,0,0,0,0,17,0,0,0,15} ,
{15,0,0,0,0,0,0,0,0,0,0,0,0,15} ,
{15,0,0,0,0,0,0,0,0,0,0,0,0,15} ,
{15,15,15,15,15,15,15,15,15,15,15,15,15,15} };
for (int Counter_y=0; Counter_y<10; Counter_y++) {
for (int Counter_x=0; Counter_x<14; Counter_x++) {
Field[Counter_y][Counter_x] = Field_Level_1[Counter_y][Counter_x];
}
}
}

void Load_Level_2_1() {
int Field_Level_1[10][14] = { {15,15,15,15,15,15,15,15,15,15,15,15,15,15} , // 0=free 15=wall 17=food
{15,0,0,0,0,0,0,0,0,0,0,0,0,15} ,
{15,0,0,0,0,0,0,17,0,0,0,0,0,15} ,
{15,0,0,0,0,0,0,0,0,0,0,0,0,15} ,
{15,0,0,0,15,15,0,0,15,15,0,0,0,15} ,
{15,0,0,0,15,15,0,0,15,15,0,0,0,15} ,
{15,0,0,17,0,0,0,0,0,17,0,0,0,15} ,
{15,0,0,0,0,0,0,0,0,0,0,0,0,15} ,
{15,0,0,0,0,0,0,0,0,0,0,0,0,15} ,
{15,15,15,15,15,15,15,15,15,15,15,15,15,15} };
for (int Counter_y=0; Counter_y<10; Counter_y++) {
for (int Counter_x=0; Counter_x<14; Counter_x++) {
Field[Counter_y][Counter_x] = Field_Level_1[Counter_y][Counter_x];
}
}
}
void Load_Level_2_2() {
int Snake_Level_1[30][3] = { {3,3,5}, {3,2,5}, {3,1,0}, {2,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0} ,{3,3,0}};
for (int Counter_y=0; Counter_y<30; Counter_y++) {
for (int Counter_x=0; Counter_x<3; Counter_x++) {
Snake[Counter_y][Counter_x] = Snake_Level_1[Counter_y][Counter_x];
}
}
pos_x = 1; //Position of upper left pixel
pos_y = 1;
Head = 0;
Ass = 1; //The last snake field at the end
Direct = 2; //Snake walks (1)south, (2)east, (3)north, (4)west
Eaten_Food = 0;
Snake_Length = 2;
}

void Paint_Picture() {
//Paint Snake
int Snake_Counter=Head;
boolean Done = false;
do {
Field[Snake[Snake_Counter][0]][Snake[Snake_Counter][1]] = Snake[Snake_Counter][2];
if (Snake_Counter==Ass) {
Done = true;
}
Snake_Counter++;
if (Snake_Counter==30) {
Snake_Counter = 0;
}
} while (Done==false);

// for (int Snake_Counter=29; Snake_Counter>=0; Snake_Counter--) {
// field[Snake[Snake_Counter][0]][Snake[Snake_Counter][1]] = Snake[Snake_Counter][2];
// }
Serial.println("");
//Paint field
for (int Counter_y=0; Counter_y<5; Counter_y++) {
for (int Counter_x=0; Counter_x<7; Counter_x++) {
gamekit.set_pixel(Counter_y, Counter_x, Field[pos_y+Counter_y][pos_x+Counter_x]);
}
}
}

void Serial_Field() {
for (int Counter_y=0; Counter_y<10; Counter_y++) {
for (int Counter_x=0; Counter_x<14; Counter_x++) {
Serial.print(Field[Counter_y][Counter_x]); Serial.print(", ");
}
Serial.println("");
}
}

void Move() {
//Determin next int in snake array for next pixel
int Snake_Pos = Head - 1;
if (Snake_Pos < 0) {
Snake_Pos = 29;
}

//write next pixel to snake array
switch (Direct) {
case 1: //walking down
Snake[Snake_Pos][0] = Snake[Head][0] + 1;
Snake[Snake_Pos][1] = Snake[Head][1];
Snake[Snake_Pos][2] = Snake[Head][2];
break;
case 2: //walking right
Snake[Snake_Pos][0] = Snake[Head][0];
Snake[Snake_Pos][1] = Snake[Head][1] + 1;
Snake[Snake_Pos][2] = Snake[Head][2];
break;
case 3: //walking up
Snake[Snake_Pos][0] = Snake[Head][0] - 1;
Snake[Snake_Pos][1] = Snake[Head][1];
Snake[Snake_Pos][2] = Snake[Head][2];
break;
case 4: //walking left
Snake[Snake_Pos][0] = Snake[Head][0];
Snake[Snake_Pos][1] = Snake[Head][1] - 1;
Snake[Snake_Pos][2] = Snake[Head][2];
break;
}
Head = Snake_Pos; //Set new head pixel

//Check collision with food
if (Field[Snake[Head][0]][Snake[Head][1]] == 17) {
Eaten_Food++;
boolean Found = false;
do {
int rand_y = generate_rand_double()*10;
int rand_x = generate_rand_double()*14;

if (Field[rand_y][rand_x]==0) {
Field[rand_y][rand_x] = 17;
Found = true;
Serial.print("New Food at: "); Serial.print(rand_y); Serial.print(", "); Serial.println(rand_x);
}
} while (Found == false);
}

if (Eaten_Food < Foods_per_Grow) {
//Erase Ass and set new Ass pixel
Field[Snake[Ass][0]][Snake[Ass][1]] = 0;
Snake[Ass][2] = 0;
Ass = Ass - 1;
if (Ass<0) {
Ass = 29;
}
} else {
Eaten_Food = 0;
Snake_Length++;
if (Snake_Length > Snake_Length_To_Win) { //Check Win
delay(100);
for (int Counter=1; Counter<31; Counter++) {
play_tone(Counter, 50, sound_volume);
}
boolean GoOn = false;
do {
delay(50);
if (Any_Key_Pressed()==true) {
Clear_Screen();
melody_position = 0;
GamePhase=1;
GoOn=true;
}
} while(GoOn==false);
}
}

//Check collisions with wall
if ( (Field[Snake[Head][0]][Snake[Head][1]] == 15) || (Field[Snake[Head][0]][Snake[Head][1]] == 5) ) {
gamekit.set_pixel(Snake[Head][0]-pos_y, Snake[Head][1]-pos_x, 20);
GamePhase = 3; //Death Phase
delay(100);
for (int Counter=30; Counter>0; Counter--) {
play_tone(Counter, 50, sound_volume);
}
boolean restart = false;
do {
delay(1);
if (Any_Key_Pressed()==true) {
GamePhase=1;
melody_position = 0;
Clear_Screen();
restart=true;
}
} while(restart==false);
}

//Check if visible area should be changed
pos_y = Snake[Head][0]-2;
if (pos_y<0) {
pos_y = 0;
}
if (pos_y>5) {
pos_y = 5;
}
pos_x = Snake[Head][1]-3;
if (pos_x<0) {
pos_x = 0;
}
if (pos_x>7) {
pos_x = 7;
}
}

void Read_Keys() {
if(gamekit.button_pressed(butt_DOWN)) {
Direct = 1;
}
if(gamekit.button_pressed(butt_RIGHT)) {
Direct = 2;
}
if(gamekit.button_pressed(butt_UP)) {
Direct = 3;
}
if(gamekit.button_pressed(butt_LEFT)) {
Direct = 4;
}
if ( (gamekit.button_pressed(butt_FUNCA)) || (gamekit.button_pressed(butt_FUNCB)) ) {
boolean pause = true;
delay(melody_speed*4);
do {
play_tone(1, 50, sound_volume);
for (int Counter=0; Counter <4; Counter++) {
delay(melody_speed);
if ( (gamekit.button_pressed(butt_FUNCA)) || (gamekit.button_pressed(butt_FUNCB)) ) { pause = false; }
}
} while (pause);
}

}

boolean Any_Key_Pressed() {
if ( (gamekit.button_pressed(butt_DOWN)==true) || (gamekit.button_pressed(butt_LEFT)==true) || (gamekit.button_pressed(butt_UP)==true) || (gamekit.button_pressed(butt_RIGHT)==true) || (gamekit.button_pressed(butt_FUNCA)==true) || (gamekit.button_pressed(butt_FUNCB)==true) ) {
return true;
} else {
return false;
}
}

void Menu() {
Play_Next_Melody_Tone();
//Start the Game
if ( (gamekit.button_pressed(butt_FUNCA)) || (gamekit.button_pressed(butt_FUNCB)) ) {
if (CurLvl==1) {
Load_Level_1_1(); Load_Level_1_2();
}
if (CurLvl==2) {
Load_Level_2_1(); Load_Level_2_2();
}
GamePhase = 2;
Paint_Picture();
melody_position = 0;
delay(melody_speed4);
for (int Counter=0; Counter<4; Counter++) {
play_tone(1, 50, sound_volume);
delay(melody_speed
4);
}
}
//Sound Volume
if ( gamekit.button_pressed(butt_RIGHT) ) {
sound_volume++;
if (sound_volume==3) {
sound_volume = 0;
}
}
for (int vol=0; vol<=2; vol++) {
if (sound_volume>=vol) {
gamekit.set_pixel(2, vol, 8);
} else {
gamekit.set_pixel(2, vol, 0);
}
}
//Difficulty
if ( gamekit.button_pressed(butt_DOWN) ) {
Difficulty++;
if (Difficulty > 4) {
Difficulty = 0;
}

switch (Difficulty) {
case 0:
Millis_per_Step = 1200;
melody_speed = 180;
Foods_per_Grow = 1;
Snake_Length_To_Win = 10;
break;
case 1:
Millis_per_Step = 1000;
melody_speed = 160;
Foods_per_Grow = 2;
Snake_Length_To_Win = 12;
break;
case 2:
Millis_per_Step = 800;
melody_speed = 140;
Foods_per_Grow = 2;
Snake_Length_To_Win = 20;
break;
case 3:
Millis_per_Step = 600;
melody_speed = 120;
Foods_per_Grow = 3;
Snake_Length_To_Win = 3;
break;
}
}
for (int dif=0; dif<=3; dif++) {
if (Difficulty>=dif) {
gamekit.set_pixel(4, dif, 8);
} else {
gamekit.set_pixel(4, dif, 0);
}
}
}

void loop(){

// Serial.print("head: ");Serial.print(Head);Serial.print("(");Serial.print(Snake[Head][0]);Serial.print(",");Serial.print(Snake[Head][1]);Serial.print(")");
// Serial.print(" ass: ");Serial.print(Ass);Serial.print("(");Serial.print(Snake[Ass][0]);Serial.print(",");Serial.print(Snake[Ass][1]);Serial.print(")");
// Serial.print(" direction: "); Serial.println(Direct);
if (GamePhase==1) { //Selection
Menu();
}

if (GamePhase==2) { //Main Game
Paint_Picture();

int Millis_For_Next_Tone;
do {
Millis_For_Next_Tone = melody[melody_position][0]melody_speed;
if ( millis()+Millis_For_Next_Tone < Timer+2
melody_speed ) {
Do_Background_Stuff();
} else {
Timer = 0;
}
} while(Timer > millis());
Timer = millis() + Millis_per_Step;

Move();

}

if (GamePhase==3) {

}
}

Have you any idea how much free memory you have?

Looking at the code, you are almost certainly running out of memory.

Some things you could do to reduce memory:

int Field_Level_1[10][14]

It does not appear as though Field_Level_1 (or any of the other Filed_Level_n's) needs to be an int. A byte will hold the required data, and use 1/2 the memory.

Same holds true for Snake_Level_n.

Thank's I'll try thak out!

How much memory does the arduino have?

Is it possible to make use of the EEPROM to save memory?
-> In my simple thinking it goes like follows: Instead of using a variable in memory I save stuff to the "hard disk" and that saves memory.

How much memory does the arduino have?

Depends on which Arduino you have, and which chip it has.

All the gory details are here:

Instead of using a variable in memory I save stuff to the "hard disk" and that saves memory.

Until you try to use the data. Then, it needs to be loaded into memory. Array element access from the "hard disk" won't work.

Not that you have a "hard disk", anyway.

How much memory does the arduino have?

The ATmega328 has 2K of RAM. (it's RAM that is in short supply and hard to tell when you overflow. If you run out of code space you'll get nice warnings.)

The reason that it fails when you make curLvl "Shared" is that this causes the actual memory location to change, probably to a location that is accidentally overwritten by something else.

int melody[112][2]   // 448 bytes.
int Field[10][14]    //280 bytes
int Snake[30][3];   //180 bytes

So that's almost 1K already gone.

int Field_Level_1[10][14] = { {15,15,15,15,15,15,15,15,15,15,15,15,15,15} ,   
                      {15,0,0,0,0,0,0,0,0,0,0,0,0,15} ,
                      {15,0,0,0,0,0,0,17,0,0,0,0,0,15} ,
                      {15,0,0,0,0,0,0,0,0,0,0,0,0,15} ,
                      {15,0,0,0,15,15,0,0,15,15,0,0,0,15} ,
                      {15,0,0,0,15,15,0,0,15,15,0,0,0,15} ,
                      {15,0,0,17,0,0,0,0,0,17,0,0,0,15} ,
                      {15,0,0,0,0,0,0,0,0,0,0,0,0,15} ,
                      {15,0,0,0,0,0,0,0,0,0,0,0,0,15} ,
                      {15,15,15,15,15,15,15,15,15,15,15,15,15,15} };

this does not work the way you would want it to. It ends up using RAM for both the Field_Level_1 local variable AND the array of constants that it is initialized to...

You can probably get much further by simply changing all the "int" arrays to "byte", and perhaps deleting the music (or making it shorter) till it is mostly working...

Instead of using a variable in memory I save stuff to the "hard disk" and that saves memory.

Yes, you can do this. Look at "pgmspace" and "PROGMEM"

So the problem is not sooo strange as I thought it was.

I went through the code and downgraded every variable that used too much memory. And now it works!!!

When I call a function (say X), and in this function I create an array field_1. Then I copy field_1 to field. Isn't the memory used by field_1 freed, when function X is quited and returned to the main code?

Not that you have a "hard disk", anyway.

I dont understand what you meen. Is there another "hard drive" beside the EEPROM?

Anyway, it works :sunglasses: . Thank you very much! The Ardino-thing is really fun!
I'll read "pgmspace" and "PROGMEM". But this technical stuff is not so easy to understand :-/

On my chip it is written ATMEGA328P-PU. So I think I have
32K flash memory - 1K bootloader = 31K. That should should solve my memory problems, I think.

Why is the SRAM used normally? Wouldn't it be better to use flash normally since it is greater?

Why is the SRAM used normally? Wouldn't it be better to use flash normally since it is greater?

SRAM is used to store variables, arrays, etc, things that can change value when your program is running. Flash stores the program, programs don't change once running, so Flash is read only once running.

Lefty