1

I'm trying to shorten the code under "setup" below, where I have over 120 variables that are being read from an external EEPROM. There is a pattern where the variable that stores the position of the EEPROM address is the name of the variable being populated, plus the word "Pos".

byte fanSpeed, nl1Toggle, ledsToggle;
byte fanSpeedPos = 12, nl1TogglePos = 95, ledsTogglePos = 7;

void setup() {
  fanSpeed = readEE(fanSpeedPos);
  nl1Toggle = readEE(nl1TogglePos);
  ledsToggle = readEE(ledsTogglePos);
  //there are over 120 of the above readEE statements in the actual code.
}

uint8_t readEE(uint8_t entry) {
  readEEPROM(AT24C32_ADDRESS, 1, entry);
}

Ideally I would like it to work like this (I'm using pseudo code because I don't know how to do this):

char* variableNames [] = { "fanSpeed", "nl1Toggle", "ledsToggle" };
byte fanSpeedPos = 12, nl1TogglePos = 95, ledsTogglePos = 7;

void setup() {
  for (int i = 1; i < 120; i++;) {
    variableNames[i] = readEE(variableNames[i] + 'Pos');
  }

Is this possible using C++ / Arduino?

  • Copy the EEPROM variable into RAM (ie. into a character array). Then use strcat to append your word to the end of it. Allow enough room for the finished words, and the terminating 0x00 byte. – Nick Gammon Mar 13 '16 at 21:56
3

This can be achieved with a simple preprocessor macro:

#define readEE(VAR) VAR = readEEPROM(AT24C32_ADDRESS, 1, VAR ## Pos)

You can then use:

readEE(foo);

and it will be expanded out to:

foo = readEEPROM(AT24C32_ADDRESS, 1, fooPos);

The ## operator in macros expands out both sides (VAR and Pos) and then concatenates the results together. Since VAR itself is a macro (whatever you pass when you call the macro) it gets replaced with that value, but Pos, because it isn't a macro, just stays as Pos. So you end up with your variable name concatenated with the word Pos.

Note that you can't use that in a loop with variable names in an array - simply because variable names only exist in the source code, not in the compiled code, so you can't do anything with those variable names once the code is compiled. Hence you have to use the preprocessor to do the job.

Yes, it means you still have to write 120 lines of code for all 120 variables, but they are just shortened to readEE(varname);.

However a better solution is to either use Mikael's struct method, or to just use an array and think differently about the concept of names:

uint8_t data[120];
for (int i = 0; i < 120; i++) {
    data[i] = readEEPROM(AT24C32_ADDRESS, 1, i);
);

So now all your variable values are in the data[] array, and you just need to know, in your program, which value is which - and you can do that by defining names for each position as preprocessor macros:

#define FAN_SPEED 12
#define N1_TOGGLE 95
#define LEDS_TOGGLE 7

Then use those macros in place of the numbers:

Serial.println(data[FAN_SPEED]);

Alternatively you could use the macros to "mask" variable names:

#define fanSpeed data[12]
#define n1Toggle data[95]
#define ledsToggle data[7]

There is one thing you might have noticed though: Nothing here shortens your code, it just moves things around a bit. Let's face it - you have 120 variables stored in 120 locations. Whatever you do you're still going to have to have 120 pointers of some form to those variables and their locations. All you can really do is choose the method that provides the most readability so that someone else looking at your code can understand what is going on. Whatever you do you have a long list of variables or variable names to define or set up, which means a lot of typing.

  • The preprocessor solution is really nice - did not think of that :). – Mikael Patel Mar 14 '16 at 13:47
  • Thanks so much for the concise "big picture" overview. I loved learning about all of the methods. I think my first try will be with the preprocessor macro, since that shortens/simplifies the code (which was my main purpose), without changing the way the variables are written to or named. I would have to modify hundreds of lines of code if I changed the way the variables are stored, which I might experiment with if I have time/ambition in the future. – Jerry Mar 14 '16 at 15:12
  • I just added the preprocessor solution to my code, and it worked beautifully without a hitch. It was really a very ideal solution to the problem. Thanks. – Jerry Mar 17 '16 at 16:19
3
variableNames[i] = readEE(variableNames[i] + 'Pos');

Is this possible using C++ / Arduino?

No! Very few programming languages allow that as it requires a lot of run-time information.

Let us instead revisit you issue; Load a large set of variables from EEPROM. What mechanisms do we have in C/C++ and AVR library that could help with this?

Here is a really simple solution that results in a "one-liner"; eeprom_read_block().

struct var_t {
  byte fanSpeed, nl1Toggle, ledsToggle;
};
var_t var;
const void* pos = ...;
eeprom_read_block(&var, pos, sizeof(var));

Obviously var_t and var are not very good names (and you will have to come up with something better) and this is for the internal EEPROM but the same technique can be used for the I2C EEPROM.

Cheers!

BW: What AT24CXXX library are you using? Because this looks strange.

 uint8_t readEE(uint8_t entry) {
   readEEPROM(AT24C32_ADDRESS, 1, entry);
 }

No return statement, etc.

  • Thanks for the answer! This stuff is a little too complex for me, so I have some studying to do before it becomes more clear (same for @Nick Gammon's comment too, which I have to study more about). The AT24CXXX is on a DS3231 RTC board, and I think I had to look through some of the less common DS3231 libraries to find one that would work with the Due (it was easy to find ones that worked with the ATmegas). The code I used is located here: github.com/kriswiner/DS3231RTC. I think I still had to make some modifications to get it to work on the Due, but I don't remember exactly. – Jerry Mar 13 '16 at 22:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.