Structs and "not declared in this scope"

Hi,

I'm having trouble using a struct, in order to store data in the eeprom.

I get the error message "'usernames' was not declared in this scope" even though it seems to me it clearly is:

#include <avr/eeprom.h>

struct highscores
{
int num_fails;
String usernames;
} highscores;

char incomingByte;
String mystring;

void store_data() {
eeprom_write_block((const void*)&highscores, (void*)0, sizeof(highscores));
}

void read_data() {
eeprom_read_block((void*)&highscores, (void*)0, sizeof(highscores));
}

void setup()
{
Serial.begin(9600);
read_data();
}

void loop()
{
Serial.println(usernames);

if (Serial.available() > 0) {
// read the incoming byte:
incomingByte = Serial.read();
if (incomingByte != 10 && incomingByte != 13)
{
mystring += incomingByte;
} else {
Serial.print("I received: ");
Serial.println(mystring);
store_data();
}
}

}

I've been searching and found some references that there might be a problem with the arduino environment that causes this, and that it can be fixed by moving the code which references the struct into a different header file, but a) it didn't work when i tried it, and b) it makes the code confusing and unreadable..

is there any way to work around this and get the above code to compile&work?

struct highscores
{
 int num_fails;
 String usernames;
} highscores;

usernames can hold one value. Why is the name plural?

 Serial.println(usernames);

Where is usernames defined? The only occurrence of that name that I can see is as a member of the struct. To print the value in the struct, you need an instance of the struct that has the usernames member valued.

buzh:
I get the error message "'usernames' was not declared in this scope" even though it seems to me it clearly is:

Here are the two parts of your code which contain references to usernames:

struct highscores
{
 int num_fails;
 String usernames;
} highscores;
Serial.println(usernames);

The first section of code defines usernames to be a field in a struct.

The second one uses usernames as though it was a global symbol, which it isn't. Perhaps you meant to print highscores.usernames.

Some more general comments:

Please use copy/paste to post your code in the forum (place the code inside [ code ] [ /code ] tags), rather than the 'copy for forum' feature in the IDE. (I really have no idea why that's still there; it's worse than useless.)

You're using the String class. This isn't related to your problem, but I recommend that you use plain old c-strings (null-terminated char arrays) instead. They're no harder to use and avoid potential issues related to dynamic memory allocation.

Using the same name for more than one thing is a very bad habit to get into. I suggest you rename either the type highscores, or the variable highscores.

Thanks for the reply!

PaulS:
usernames can hold one value. Why is the name plural?

Probably not the best way, but I was intending to use a delimiter, e.g. "Alice:Bob:Charlie" and then process it for display at a later point. Suggestions for a better approach most welcome!

 Serial.println(usernames);

Where is usernames defined? The only occurrence of that name that I can see is as a member of the struct. To print the value in the struct, you need an instance of the struct that has the usernames member valued.

Shouldn't it be populated by the read_data() function? Granted, this code assumes there is already something in the eeprom..

Shouldn't it be populated by the read_data() function? Granted, this code assumes there is already something in the eeprom..

Yes, if there is something in EEPROM, then there will be something in highscores.usernames to print. But, that is not what you are printing.

Thanks, PeterH and PaulS.. this cleared up the matter with the struct, i think..

Still not getting the eeprom read/write to work as I hoped though. It seems filled with garbage, and the garbage doesn't even seem to change after store_data();

Is this related to my use of String? Is there a better way to achieve what I want? (For clarity, my intention is to store a list of names. First one to enter their name is on top of the list, last one is last). I also want to be able to keep more data in the struct and read/write that to eeprom in a generic way.

#include <avr/eeprom.h>

struct
{
 int num_fails;
 String usernames;
} highscores;

char incomingByte;
String mystring;

void store_data() {
    eeprom_write_block((const void*)&highscores, (void*)0, sizeof(highscores));
}

void read_data() {
    eeprom_read_block((void*)&highscores, (void*)0, sizeof(highscores));
}

void setup()  
{
  Serial.begin(9600);
  read_data();
}

void loop()
{
 delay(10);
  if (Serial.available() > 0) {
                 // read the incoming byte:
                 incomingByte = Serial.read();
                 if (incomingByte != 10 && incomingByte != 13)
                  {
                    mystring += incomingByte; 
                  } else {
                 Serial.println(highscores.usernames);
                 Serial.print("I received: ");
                 Serial.println(mystring);
                 highscores.usernames += mystring;
                 store_data();
                 Serial.println("Already stored:");
                 Serial.println(highscores.usernames);
                 mystring="";
                  }
         }
  
}

Is this related to my use of String?

Probably.

Is there a better way to achieve what I want?

Yes. Use strings, instead (NULL terminated array of char). Limit the length of usernames to some reasonable value.

There are EEPROM_readAnything() and EEPROM_writeAnything() functions around. Find and use them.

Is this related to my use of String?

Yes. If you try to write the string object to EEPROM, I rather suspect that you'll only process the string's header, which includes a pointer to the actual text of the string.

When you read the string back into RAM after a power cycle, that text is gone and printing your string will give you whatever is now at the pointed location in memory - likely garbage.

buzh:
Still not getting the eeprom read/write to work as I hoped though. It seems filled with garbage, and the garbage doesn't even seem to change after store_data();

Is this related to my use of String?

Yes.

The String class holds the string content in a memory buffer allocated on the heap. That's not a sensible approach in any case on a system with such limited memory and such limited memory management capability, but it also means that the string content is not held within the memory space of the struct so when you serialise and deserialise the struct you are only saving and reloadnig the pointer to the heap, and not the content.

I recommend you banish the String class from your sketch and try to forget that it ever existed - use 'c' strings (null terminated char arrays) instead. If you store your strings within the struct then they will be stored and reloaded correctly when you store and reload the struct.

By the way, I think your scheme for storing multiple names in the string could be improved. Wouldn't it make more sense for each struct to hold a name and the associated high score, and then define an array of them representing the leaderboard?

Thanks so much for all the help and suggestions, all of you!

I got it working so that I can easily expand the struct with new fields, populate the fields with data and even store and retrieve via eeprom.
So that solves it for me..

Cheers guys!

Code:

#include <avr/eeprom.h>

struct highscores
{
 int place;
 char username[16];
};

struct highscores table[32];

char incomingByte;
char inputstring[16];

int i=0;
int numentries = 0;


void store_data() {
    eeprom_write_block((const void*)&table, (void*)0, sizeof(table));
}

void read_data() {
    eeprom_read_block((void*)&table, (void*)0, sizeof(table));
}

void setup()  
{
  Serial.begin(9600);
  read_data();
  
   for (int j = 0; j < 64; j++) {
     if (table[j].place == j + 1) { //is this a valid entry?
      continue;
     } else {
       numentries = j; // num of valid entries read from eeprom
       break;
     }
   }
  
}

void loop()
{
  delay(10);

 if (Serial.available() > 0) {
      incomingByte = Serial.read();
      inputstring[i] = incomingByte;
      i++;
    }
 
 if (incomingByte == 10 || incomingByte == 13 || i == 15) { // if user hit enter or we have the max lenght
   
   incomingByte = 0; // reset for next round   
   
   if ( i > 0) { // dont do it if blank entry
    while (i < 15) { // fill inputstring with blanks if less than 16 chars
      inputstring[i] = 0;
      i++;
    }

    table[numentries].place = numentries + 1;
    
    for (int j = 0; j < 16; j++) {
      table[numentries].username[j] = inputstring[j];
    }
   }
   
   i = 0; // reset for next round
   
   while (Serial.read() >= 0) {} // flush remaining input, if any
   
   Serial.println("Highscores:");
   for (int j = 0; j <= numentries; j++) {
     Serial.print(table[j].place);
     Serial.print(" : ");
     Serial.println(table[j].username);
   }
   numentries++;
   Serial.println("----");
   store_data();
 }  
}