Pages: [1] 2   Go Down
Author Topic: indexed or enumerated structure  (Read 1149 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 0
Posts: 30
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi,

I'm in the process of building a library that will construct a UART configuration Menu, which stores project settings in the EEPROM.  What I'm doing is working great for me right now, but I would like to modularize this a bit more:

How can I access the data in the structure numerically?  For example settings[3] = "192.168.000.001", instead of settings.ipaddress = "192.168.000.001".  I ask because currently I'm doing all of this through case statements, which has left me repeating code with each case (about 2K worth).  Is there a way that I can do this with an enum?  I still want to keep the structure's variable names, because that works great with interfacing it my actual program.

Code:
struct settings_t
{
  char ssid[33];            //32 is the maximum number of characters in SSID
  char password[17];        //16 is the maximum number of characters in WEP2 Password
  char dhcp;                //Y/N so one char
  char ipaddress[16];       //000.000.000.000 so 15 is the maximum number of characters
  char subnetmask[16];      //000.000.000.000 so 15 is the maximum number characters
  char gateway[16];         //000.000.000.000 so 15 is the maximum number characters
  char timezone[4];         //+12 so 3 is the maximum number of characters
  char daylightsavings;     //Y/N so one char
} settings;

void setup()
{
  delay(100);  // Delay for 100ms to ensure smooth startup
  eeprom_read_block((void*)&settings, (void*)0, sizeof(settings));  // Load the Settings from the EEPROM into memory
  Serial.begin(9600);
}

void SaveSettings()
{
  eeprom_write_block((const void*)&settings, (void*)0, sizeof(settings));
 Serial.print("Settings Saved");
}

Logged

Offline Offline
Edison Member
*
Karma: 33
Posts: 1435
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You could use offsetof() to create a table of offsets.
Logged

UK
Offline Offline
Shannon Member
****
Karma: 223
Posts: 12631
-
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

If you want to access the data structure as a byte array you can take the address of the structure using the & operator and cast it to a byte pointer; you determine the size using the sizeof operator. I assume this would be for the purpose of storing the structure in EEPROM or whatever where you need to treat it as just a set of bytes.
Logged

I only provide help via the forum - please do not contact me for private consultancy.

Offline Offline
Edison Member
*
Karma: 33
Posts: 1435
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

PeterH: offsetof would be a much cleaner way of doing this...
Logged

UK
Offline Offline
Shannon Member
****
Karma: 223
Posts: 12631
-
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

PeterH: offsetof would be a much cleaner way of doing this...

That depends what 'this' is. If 'this' is writing a struct to a byte oriented storage device and then reading it back again, then IMO using address and sizeof operators to locate the memory range containing the struct is a much cleaner way.
Logged

I only provide help via the forum - please do not contact me for private consultancy.

0
Offline Offline
Newbie
*
Karma: 0
Posts: 30
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

If you want to access the data structure as a byte array you can take the address of the structure using the & operator and cast it to a byte pointer; you determine the size using the sizeof operator. I assume this would be for the purpose of storing the structure in EEPROM or whatever where you need to treat it as just a set of bytes.

I'm not trying to write this to EEPROM or read this from EEPROM, my settings are already reading and writing to the EEPROM successfully.  I'm combining all my settings into a structure to simplify the amount of programming/processing required to read/write multiple variables of different types and length to the EEPROM.  What I'm trying to do is write to each variable in the structure by use of a number as if they were stored in a table.

I'm printing a configuration menu and asking the user to enter a number, I want that number to access the respective variable in the structure and allow me to write to it.  I don't to use a case statement to do this.


Code:
void ReadConfigItem(byte index)
{
  PrintMenueItem(index);  // Print the Menu Item
  while(!Serial.available()); // hang program until a byte is received notice the ; after the while()
    byte inputcount = 0;
    char inputchar = '/0';
 
    switch(index)
    {
      case 1:
        do
        {
          delay(100);
          inputchar = Serial.read();
          Serial.print(inputchar);
          settings.ssid[inputcount] = inputchar;
          inputcount++;
        }while(Serial.available() );
        settings.ssid[inputcount] = '\0';  // null terminate the the last known charactor of the string
        break;
      case 2:
        do
        {
          delay(100);
          inputchar = Serial.read();
          Serial.print(inputchar);
          settings.password[inputcount] = inputchar;
          inputcount++;
        }while(Serial.available() );
          settings.password[inputcount] = '\0';  // null terminate the the last known charactor of the string
        break;
      case 3:
          delay(100);
          inputchar = Serial.read();
          Serial.print(inputchar);
          if(inputchar == 'Y' || inputchar == 'y') settings.dhcp = 'Y';
          else settings.dhcp = 'N';
        break;
      case 4:
        do
        {
          delay(100);
          inputchar = Serial.read();
          Serial.print(inputchar);
          settings.ipaddress[inputcount] = inputchar;
          inputcount++;
        }while(Serial.available() );
        settings.ipaddress[inputcount] = '\0';  // null terminate the the last known charactor of the string
        break;
      case 5:
        do
        {
          delay(100);
          inputchar = Serial.read();
          Serial.print(inputchar);
          settings.subnetmask[inputcount] = inputchar;
          inputcount++;
        }while(Serial.available() );
        settings.subnetmask[inputcount] = '\0';  // null terminate the the last known charactor of the string
        break;
      case 6:
        do
        {
          delay(100);
          inputchar = Serial.read();
          Serial.print(inputchar);
          settings.gateway[inputcount] = inputchar;
          inputcount++;
        }while(Serial.available() );
        settings.gateway[inputcount] = '\0';  // null terminate the the last known charactor of the string
        break;
      case 7:
        do
        {
          delay(100);
          inputchar = Serial.read();
          Serial.print(inputchar);
          settings.timezone[inputcount] = inputchar;
          inputcount++;
        }while(Serial.available() );
          settings.timezone[inputcount] = '\0';  // null terminate the the last known charactor of the string
        break;
      case 8:
          delay(100);
          inputchar = Serial.read();
          Serial.print(inputchar);
          settings.daylightsavings = inputchar;
        break; 
    }
}

As you can see its quite cumbersome and is using way more memory than this should.

« Last Edit: July 17, 2013, 05:00:18 pm by bigbro » Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 30
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

PeterH: offsetof would be a much cleaner way of doing this...


Do you perhaps have an example of how to do this?  I have just googled offsetof for c++ but I'm not sure how I'm going to use this because how do I determine the size of the variable.  Is it possible that I can place all of my structured variables in a table somehow?

I tried this, but it didn't compile:

Code:
PROGMEM const char *SettingLookup[] =
{
 settings.ssid,
 settings.password,
 settings.dhcp,
 settings.ipaddress,
 settings.subnetmask,
 settings.gateway,
 settings.timezone
 settings.daylightsavings
};

I'm honestly not sure what to do next.
« Last Edit: July 17, 2013, 04:53:42 pm by bigbro » Logged

Offline Offline
Edison Member
*
Karma: 33
Posts: 1435
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Close, try this:
char *SettingLookup[] =
&settings+offsetof(settings,ssid),
&settings+offsetof(settings,password),
...
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 30
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Close, try this:
char *SettingLookup[] =
&settings+offsetof(settings,ssid),
&settings+offsetof(settings,password),
...

Thanks Keith,  I tried that but I'm getting the compile error "expected type-specifier before 'settings'" on the line "&settings+offsetof(settings,ssid),
"
Logged

UK
Offline Offline
Shannon Member
****
Karma: 223
Posts: 12631
-
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

If the fields you want to access are different sizes then you're heading towards a solution where you need to know the address/offset and size of each parameter and will end up defining a metadata structure for each parameter - it seems to me that in this case a plain old switch statement would actually be a much simpler solution.
Logged

I only provide help via the forum - please do not contact me for private consultancy.

Offline Offline
Edison Member
*
Karma: 33
Posts: 1435
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Sorry, try:
&settings + offfsetof(struct settings_t, ssid)

I just tried it and it compiles.

I did have to define the lookup array as a void*, though that will need to be cast appropriately.
« Last Edit: July 17, 2013, 05:49:14 pm by KeithRB » Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 610
Posts: 49080
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
        do
        {
          delay(100);
          inputchar = Serial.read();
          Serial.print(inputchar);
          settings.ssid[inputcount] = inputchar;
          inputcount++;
        }while(Serial.available() );
Why? If there is no serial data available, this code will wait 100 milliseconds, and then read and print some anyway. You should almost never use a do/while loop to read serial data. Use a while loop, instead, and lose that stupid delay().
Logged

Australia
Offline Offline
Jr. Member
**
Karma: 0
Posts: 99
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Not sure if I'm misinterpreting the problem, but have you considered overloading the subscript operator [] in the struct?

As a struct is just a class with default public methods and member variables, you get all these semantics for free.
Logged

UK
Offline Offline
Shannon Member
****
Karma: 223
Posts: 12631
-
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have a 100ms delay to ensure that the char has reached the buffer (At times it didn't and I read an erroneous character).  I was using a do while because I was looking for a character count and a null character during testing.  This is still at its infancy and the serial read function will most likely become a far more complex function that takes inconsideration both the type of the data and its length.  That bit of code was a quick 1 minute mockup and has nothing to do with this problem

You may not think it is relevant to the problem but PaulS's comment on this piece of code is spot on IMO and what that code does is simply wrong. You should IMO be grateful that PaulS has taken the trouble to review your code and advise you of problems, even if the problems are not relating to your question. (And you would probably be surprised how often we get posts describing issues that turn out to be caused by problems that the poster thought were completely unrelated.)
Logged

I only provide help via the forum - please do not contact me for private consultancy.

0
Offline Offline
Newbie
*
Karma: 0
Posts: 30
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Sorry, try:
&settings + offfsetof(struct settings_t, ssid)

I just tried it and it compiles.

I did have to define the lookup array as a void*, though that will need to be cast appropriately.

Thanks Keith, it compiles but return's gibberish.  

I was hoping you could please tell me what I'm missing here, my pointer programming is a tad rusty.

Code:
struct settings_t
{
  char ssid[33];            //32 is the maximum number of characters in SSID
  char password[17];        //16 is the maximum number of characters in WEP2 Password
  char dhcp;                //Y/N so one char
  char ipaddress[16];       //000.000.000.000 so 15 is the maximum number of characters
  char subnetmask[16];      //000.000.000.000 so 15 is the maximum number characters
  char gateway[16];         //000.000.000.000 so 15 is the maximum number characters
  char timezone[4];         //+12 so 3 is the maximum number of characters
  char daylightsavings;     //Y/N so one char
} settings;

void *SettingLookup[] =
{
  &settings + offsetof(struct settings_t, ssid),
  &settings + offsetof(struct settings_t, password),
  &settings + offsetof(struct settings_t, dhcp),
  &settings + offsetof(struct settings_t, ipaddress),
  &settings + offsetof(struct settings_t, subnetmask),
  &settings + offsetof(struct settings_t, gateway),
  &settings + offsetof(struct settings_t, timezone),
  &settings + offsetof(struct settings_t, daylightsavings),
};

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


  settings.ssid[0] = 'S';
  settings.ssid[1] = 'S';
  settings.ssid[2] = 'I';
  settings.ssid[3] = 'D';

  settings.password[0] = 'P';
  settings.password[1] = 'A';
  settings.password[2] = 'S';
  settings.password[3] = 'S';
  settings.password[4] = 'W';
  settings.password[5] = 'O';
  settings.password[6] = 'R';
  settings.password[7] = 'D';


  settings.dhcp = 'Y';


  settings.ipaddress[0] = 'I';
  settings.ipaddress[1] = 'P';
  settings.ipaddress[2] = 'A';
  settings.ipaddress[3] = 'D';
  settings.ipaddress[4] = 'D';
  settings.ipaddress[5] = 'R';
  settings.ipaddress[6] = 'E';
  settings.ipaddress[7] = 'S';
  settings.ipaddress[8] = 'S';

  settings.subnetmask[0] = 'S';
  settings.subnetmask[1] = 'U';
  settings.subnetmask[2] = 'B';
  settings.subnetmask[3] = 'N';
  settings.subnetmask[4] = 'E';
  settings.subnetmask[5] = 'T';
  settings.subnetmask[6] = 'M';
  settings.subnetmask[7] = 'A';
  settings.subnetmask[8] = 'S';
  settings.subnetmask[9] = 'K';
 
  settings.gateway[0] = 'G';
  settings.gateway[1] = 'A';
  settings.gateway[2] = 'T';
  settings.gateway[3] = 'E';
  settings.gateway[4] = 'W';
  settings.gateway[5] = 'A';
  settings.gateway[6] = 'Y';
 
  settings.timezone[0] = '+';
  settings.timezone[1] = '3';

  settings.daylightsavings = 'Y';

  for (int i = 0; i <= 7; i++)
  {            
    char buffer[256] = {0};  
    strcpy_P(buffer, ((char*)(&SettingLookup[i])));
    Serial.println(buffer);
  }

}

« Last Edit: July 18, 2013, 07:26:55 pm by bigbro » Logged

Pages: [1] 2   Go Up
Jump to: