Go Down

Topic: indexed or enumerated structure (Read 1 time) previous topic - next topic

bigbro

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: [Select]

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");
}


KeithRB

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

PeterH

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 only provide help via the forum - please do not contact me for private consultancy.

KeithRB

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

PeterH


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.
I only provide help via the forum - please do not contact me for private consultancy.

bigbro

#5
Jul 17, 2013, 11:40 pm Last Edit: Jul 18, 2013, 12:00 am by bigbro Reason: 1

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: [Select]

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.


bigbro

#6
Jul 17, 2013, 11:42 pm Last Edit: Jul 17, 2013, 11:53 pm by bigbro Reason: 1

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: [Select]

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.

KeithRB

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

bigbro


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),
"

PeterH

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.
I only provide help via the forum - please do not contact me for private consultancy.

KeithRB

#10
Jul 18, 2013, 12:47 am Last Edit: Jul 18, 2013, 12:49 am by KeithRB Reason: 1
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.

PaulS

Code: [Select]
        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().

crimony

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.

PeterH


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.)
I only provide help via the forum - please do not contact me for private consultancy.

bigbro

#14
Jul 19, 2013, 01:51 am Last Edit: Jul 19, 2013, 02:26 am by bigbro Reason: 1

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: [Select]

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);
 }

}



Go Up